Merge remote-tracking branches 'asoc/topic/rt5645', 'asoc/topic/rt5670', 'asoc/topic...
authorMark Brown <broonie@kernel.org>
Mon, 8 Dec 2014 13:12:09 +0000 (13:12 +0000)
committerMark Brown <broonie@kernel.org>
Mon, 8 Dec 2014 13:12:09 +0000 (13:12 +0000)
1001 files changed:
Documentation/devicetree/bindings/ata/sata_rcar.txt
Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
Documentation/devicetree/bindings/pci/pci.txt
Documentation/devicetree/bindings/pinctrl/img,tz1090-pdc-pinctrl.txt
Documentation/devicetree/bindings/pinctrl/img,tz1090-pinctrl.txt
Documentation/devicetree/bindings/pinctrl/lantiq,falcon-pinumx.txt
Documentation/devicetree/bindings/pinctrl/lantiq,xway-pinumx.txt
Documentation/devicetree/bindings/pinctrl/nvidia,tegra20-pinmux.txt
Documentation/devicetree/bindings/pinctrl/pinctrl-sirf.txt
Documentation/devicetree/bindings/pinctrl/pinctrl_spear.txt
Documentation/devicetree/bindings/pinctrl/qcom,apq8064-pinctrl.txt
Documentation/devicetree/bindings/pinctrl/qcom,apq8084-pinctrl.txt
Documentation/devicetree/bindings/pinctrl/qcom,ipq8064-pinctrl.txt
Documentation/devicetree/bindings/pinctrl/qcom,msm8960-pinctrl.txt
Documentation/devicetree/bindings/pinctrl/qcom,msm8974-pinctrl.txt
Documentation/devicetree/bindings/sound/arndale.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt
Documentation/devicetree/bindings/sound/eukrea-tlv320.txt
Documentation/devicetree/bindings/sound/fsl,esai.txt
Documentation/devicetree/bindings/sound/fsl,spdif.txt
Documentation/devicetree/bindings/sound/fsl-sai.txt
Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt
Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt
Documentation/devicetree/bindings/sound/imx-audmux.txt
Documentation/devicetree/bindings/sound/max98090.txt
Documentation/devicetree/bindings/sound/renesas,rsnd.txt
Documentation/devicetree/bindings/sound/rt5631.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/rt5677.txt
Documentation/devicetree/bindings/sound/samsung-i2s.txt
Documentation/devicetree/bindings/thermal/rcar-thermal.txt
Documentation/devicetree/bindings/vendor-prefixes.txt
Documentation/filesystems/overlayfs.txt
Documentation/input/elantech.txt
Documentation/kernel-parameters.txt
Documentation/networking/ip-sysctl.txt
Documentation/networking/timestamping.txt
Documentation/video4linux/vivid.txt
MAINTAINERS
Makefile
arch/arm/Kconfig.debug
arch/arm/boot/compressed/head.S
arch/arm/boot/dts/am335x-evm.dts
arch/arm/boot/dts/am437x-gp-evm.dts
arch/arm/boot/dts/am437x-sk-evm.dts
arch/arm/boot/dts/am43x-epos-evm.dts
arch/arm/boot/dts/exynos5250-snow.dts
arch/arm/boot/dts/exynos5250.dtsi
arch/arm/boot/dts/r8a7740.dtsi
arch/arm/boot/dts/r8a7790.dtsi
arch/arm/boot/dts/sama5d31.dtsi
arch/arm/boot/dts/sama5d33.dtsi
arch/arm/boot/dts/sama5d34.dtsi
arch/arm/boot/dts/sama5d35.dtsi
arch/arm/boot/dts/sama5d36.dtsi
arch/arm/boot/dts/sama5d3xcm.dtsi
arch/arm/boot/dts/sun6i-a31.dtsi
arch/arm/boot/dts/tegra114-dalmore.dts
arch/arm/boot/dts/tegra114-roth.dts
arch/arm/boot/dts/tegra114-tn7.dts
arch/arm/boot/dts/tegra114.dtsi
arch/arm/boot/dts/tegra124-jetson-tk1.dts
arch/arm/boot/dts/tegra124-nyan-big.dts
arch/arm/boot/dts/tegra124-venice2.dts
arch/arm/boot/dts/tegra124.dtsi
arch/arm/boot/dts/tegra20-harmony.dts
arch/arm/boot/dts/tegra20-iris-512.dts
arch/arm/boot/dts/tegra20-medcom-wide.dts
arch/arm/boot/dts/tegra20-paz00.dts
arch/arm/boot/dts/tegra20-seaboard.dts
arch/arm/boot/dts/tegra20-tamonten.dtsi
arch/arm/boot/dts/tegra20-trimslice.dts
arch/arm/boot/dts/tegra20-ventana.dts
arch/arm/boot/dts/tegra20-whistler.dts
arch/arm/boot/dts/tegra20.dtsi
arch/arm/boot/dts/tegra30-apalis-eval.dts
arch/arm/boot/dts/tegra30-beaver.dts
arch/arm/boot/dts/tegra30-cardhu.dtsi
arch/arm/boot/dts/tegra30-colibri-eval-v3.dts
arch/arm/boot/dts/tegra30.dtsi
arch/arm/boot/dts/vf610-cosmic.dts
arch/arm/boot/dts/zynq-parallella.dts
arch/arm/common/edma.c
arch/arm/configs/exynos_defconfig
arch/arm/configs/imx_v4_v5_defconfig
arch/arm/configs/imx_v6_v7_defconfig
arch/arm/configs/multi_v7_defconfig
arch/arm/configs/omap2plus_defconfig
arch/arm/configs/socfpga_defconfig
arch/arm/include/asm/thread_info.h
arch/arm/kernel/traps.c
arch/arm/kvm/mmu.c
arch/arm/mach-imx/clk-vf610.c
arch/arm/mach-ixp4xx/include/mach/io.h
arch/arm/mach-mvebu/board-v7.c
arch/arm/mach-mvebu/coherency.c
arch/arm/mach-omap2/omap_device.c
arch/arm/mach-pxa/include/mach/addr-map.h
arch/arm/mach-pxa/spitz.c
arch/arm/mach-shmobile/clock-r8a7740.c
arch/arm/mach-shmobile/clock-r8a7790.c
arch/arm/mach-shmobile/setup-sh73a0.c
arch/arm/mach-tegra/irq.c
arch/arm/mm/Kconfig
arch/arm/mm/proc-v7.S
arch/arm/mm/proc-xscale.S
arch/arm/plat-orion/gpio.c
arch/arm64/boot/dts/apm-storm.dtsi
arch/arm64/configs/defconfig
arch/arm64/include/asm/memory.h
arch/arm64/include/asm/unistd32.h
arch/arm64/kernel/efi-entry.S
arch/arm64/kernel/insn.c
arch/arm64/kernel/psci.c
arch/arm64/kvm/sys_regs.c
arch/arm64/lib/clear_user.S
arch/arm64/mm/mmu.c
arch/ia64/kvm/kvm-ia64.c
arch/m68k/include/asm/unistd.h
arch/m68k/include/uapi/asm/unistd.h
arch/m68k/kernel/syscalltable.S
arch/mips/Kconfig
arch/mips/Makefile
arch/mips/cavium-octeon/octeon-irq.c
arch/mips/include/asm/asmmacro-32.h
arch/mips/include/asm/asmmacro.h
arch/mips/include/asm/fpregdef.h
arch/mips/include/asm/fpu.h
arch/mips/include/asm/jump_label.h
arch/mips/include/asm/mach-loongson/cpu-feature-overrides.h
arch/mips/include/asm/mipsregs.h
arch/mips/include/asm/r4kcache.h
arch/mips/include/asm/uaccess.h
arch/mips/include/uapi/asm/unistd.h
arch/mips/kernel/bmips_vec.S
arch/mips/kernel/branch.c
arch/mips/kernel/cps-vec.S
arch/mips/kernel/cpu-probe.c
arch/mips/kernel/genex.S
arch/mips/kernel/jump_label.c
arch/mips/kernel/r2300_fpu.S
arch/mips/kernel/r2300_switch.S
arch/mips/kernel/r4k_fpu.S
arch/mips/kernel/r4k_switch.S
arch/mips/kernel/r6000_fpu.S
arch/mips/kernel/rtlx.c
arch/mips/kernel/scall32-o32.S
arch/mips/kernel/scall64-64.S
arch/mips/kernel/scall64-n32.S
arch/mips/kernel/scall64-o32.S
arch/mips/kernel/setup.c
arch/mips/kernel/signal.c
arch/mips/lib/memcpy.S
arch/mips/lib/r3k_dump_tlb.c
arch/mips/lib/strnlen_user.S
arch/mips/loongson/common/Makefile
arch/mips/loongson/loongson-3/numa.c
arch/mips/math-emu/cp1emu.c
arch/mips/mm/tlb-r4k.c
arch/mips/mm/tlbex.c
arch/mips/mti-sead3/sead3-leds.c
arch/mips/netlogic/xlp/Makefile
arch/mips/oprofile/backtrace.c
arch/mips/pci/msi-xlp.c
arch/mips/sgi-ip27/ip27-memory.c
arch/parisc/include/asm/uaccess.h
arch/parisc/include/uapi/asm/bitsperlong.h
arch/parisc/include/uapi/asm/msgbuf.h
arch/parisc/include/uapi/asm/sembuf.h
arch/parisc/include/uapi/asm/shmbuf.h
arch/parisc/include/uapi/asm/signal.h
arch/parisc/include/uapi/asm/unistd.h
arch/parisc/kernel/syscall_table.S
arch/powerpc/include/asm/fadump.h
arch/powerpc/include/asm/pci-bridge.h
arch/powerpc/kernel/eeh_sysfs.c
arch/powerpc/kernel/entry_64.S
arch/powerpc/kernel/fadump.c
arch/powerpc/kernel/pci_64.c
arch/powerpc/kernel/vdso32/getcpu.S
arch/powerpc/mm/init_32.c
arch/powerpc/platforms/powernv/opal-hmi.c
arch/powerpc/platforms/powernv/opal-lpc.c
arch/powerpc/platforms/powernv/pci-ioda.c
arch/powerpc/platforms/powernv/pci.c
arch/powerpc/platforms/pseries/dlpar.c
arch/powerpc/platforms/pseries/lpar.c
arch/powerpc/platforms/pseries/msi.c
arch/powerpc/sysdev/fsl_msi.c
arch/powerpc/xmon/xmon.c
arch/s390/configs/default_defconfig
arch/s390/configs/gcov_defconfig
arch/s390/configs/performance_defconfig
arch/s390/configs/zfcpdump_defconfig
arch/s390/defconfig
arch/s390/kernel/ftrace.c
arch/s390/kernel/nmi.c
arch/s390/kernel/vdso32/clock_gettime.S
arch/s390/kernel/vdso32/gettimeofday.S
arch/s390/kernel/vdso64/clock_gettime.S
arch/s390/kernel/vdso64/gettimeofday.S
arch/s390/kernel/vtime.c
arch/sparc/include/asm/atomic_32.h
arch/sparc/include/asm/cmpxchg_32.h
arch/sparc/include/asm/dma-mapping.h
arch/sparc/include/uapi/asm/swab.h
arch/sparc/kernel/pci_schizo.c
arch/sparc/kernel/smp_64.c
arch/sparc/lib/atomic32.c
arch/x86/Kconfig
arch/x86/boot/compressed/Makefile
arch/x86/boot/compressed/head_32.S
arch/x86/boot/compressed/head_64.S
arch/x86/boot/compressed/misc.c
arch/x86/boot/compressed/mkpiggy.c
arch/x86/include/asm/page_32_types.h
arch/x86/include/asm/page_64_types.h
arch/x86/include/asm/platform_sst_audio.h
arch/x86/include/asm/smp.h
arch/x86/include/asm/thread_info.h
arch/x86/include/asm/traps.h
arch/x86/kernel/cpu/common.c
arch/x86/kernel/cpu/microcode/amd_early.c
arch/x86/kernel/cpu/microcode/core.c
arch/x86/kernel/cpu/microcode/core_early.c
arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c
arch/x86/kernel/dumpstack_64.c
arch/x86/kernel/entry_64.S
arch/x86/kernel/ptrace.c
arch/x86/kernel/smpboot.c
arch/x86/kernel/traps.c
arch/x86/kvm/emulate.c
arch/x86/kvm/mmu.c
arch/x86/lib/csum-wrappers_64.c
arch/x86/mm/init_64.c
arch/x86/tools/calc_run_size.pl [new file with mode: 0644]
arch/x86/xen/smp.c
arch/xtensa/Kconfig
arch/xtensa/boot/dts/lx200mx.dts [new file with mode: 0644]
arch/xtensa/configs/generic_kc705_defconfig [new file with mode: 0644]
arch/xtensa/configs/smp_lx200_defconfig [new file with mode: 0644]
arch/xtensa/include/asm/pgtable.h
arch/xtensa/include/uapi/asm/unistd.h
block/bio-integrity.c
block/blk-merge.c
block/blk-mq.c
block/ioprio.c
block/scsi_ioctl.c
drivers/acpi/blacklist.c
drivers/acpi/device_pm.c
drivers/acpi/video.c
drivers/ata/ahci.c
drivers/ata/libahci.c
drivers/ata/sata_fsl.c
drivers/ata/sata_rcar.c
drivers/atm/solos-pci.c
drivers/base/Kconfig
drivers/base/core.c
drivers/base/power/domain.c
drivers/base/regmap/Kconfig
drivers/base/regmap/Makefile
drivers/base/regmap/regmap-ac97.c [new file with mode: 0644]
drivers/block/rbd.c
drivers/block/zram/zram_drv.c
drivers/char/hw_random/pseries-rng.c
drivers/char/virtio_console.c
drivers/clk/at91/clk-usb.c
drivers/clk/clk-divider.c
drivers/clk/pxa/clk-pxa27x.c
drivers/clk/qcom/mmcc-apq8084.c
drivers/clk/rockchip/clk.c
drivers/clocksource/sun4i_timer.c
drivers/cpufreq/cpufreq-dt.c
drivers/cpufreq/cpufreq.c
drivers/crypto/caam/key_gen.c
drivers/crypto/qat/qat_common/adf_accel_devices.h
drivers/crypto/qat/qat_common/adf_transport.c
drivers/crypto/qat/qat_common/qat_algs.c
drivers/crypto/qat/qat_common/qat_crypto.c
drivers/crypto/qat/qat_dh895xcc/adf_admin.c
drivers/crypto/qat/qat_dh895xcc/adf_drv.c
drivers/crypto/qat/qat_dh895xcc/adf_isr.c
drivers/dma/edma.c
drivers/dma/pl330.c
drivers/dma/sun6i-dma.c
drivers/firewire/core-cdev.c
drivers/gpu/drm/exynos/exynos_drm_drv.c
drivers/gpu/drm/exynos/exynos_drm_g2d.c
drivers/gpu/drm/i915/i915_dma.c
drivers/gpu/drm/i915/i915_drv.c
drivers/gpu/drm/i915/i915_gem_gtt.c
drivers/gpu/drm/i915/i915_gem_tiling.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_dp.c
drivers/gpu/drm/i915/intel_lvds.c
drivers/gpu/drm/i915/intel_panel.c
drivers/gpu/drm/i915/intel_pm.c
drivers/gpu/drm/nouveau/core/engine/device/nvc0.c
drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c
drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c
drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c
drivers/gpu/drm/nouveau/core/subdev/fb/gk20a.c
drivers/gpu/drm/nouveau/nouveau_drm.c
drivers/gpu/drm/nouveau/nouveau_fence.c
drivers/gpu/drm/nouveau/nouveau_fence.h
drivers/gpu/drm/nouveau/nv50_display.c
drivers/gpu/drm/radeon/atom.c
drivers/gpu/drm/radeon/atom.h
drivers/gpu/drm/radeon/atombios_dp.c
drivers/gpu/drm/radeon/atombios_i2c.c
drivers/gpu/drm/radeon/cik.c
drivers/gpu/drm/radeon/cik_sdma.c
drivers/gpu/drm/radeon/evergreen.c
drivers/gpu/drm/radeon/r100.c
drivers/gpu/drm/radeon/r600_dma.c
drivers/gpu/drm/radeon/r600_dpm.c
drivers/gpu/drm/radeon/radeon_connectors.c
drivers/gpu/drm/radeon/radeon_cs.c
drivers/gpu/drm/radeon/radeon_device.c
drivers/gpu/drm/radeon/radeon_encoders.c
drivers/gpu/drm/radeon/radeon_irq_kms.c
drivers/gpu/drm/radeon/radeon_kms.c
drivers/gpu/drm/radeon/radeon_object.c
drivers/gpu/drm/radeon/rs600.c
drivers/gpu/drm/radeon/rs690.c
drivers/gpu/drm/radeon/rv515.c
drivers/gpu/drm/radeon/si.c
drivers/gpu/drm/tegra/dc.c
drivers/hid/hid-core.c
drivers/hid/hid-ids.h
drivers/hid/usbhid/hid-quirks.c
drivers/hwmon/fam15h_power.c
drivers/hwmon/g762.c
drivers/hwmon/ibmpowernv.c
drivers/hwmon/pwm-fan.c
drivers/i2c/algos/i2c-algo-bit.c
drivers/i2c/algos/i2c-algo-pca.c
drivers/i2c/algos/i2c-algo-pcf.c
drivers/i2c/algos/i2c-algo-pcf.h
drivers/i2c/busses/i2c-ali1535.c
drivers/i2c/busses/i2c-ali15x3.c
drivers/i2c/busses/i2c-amd756-s4882.c
drivers/i2c/busses/i2c-amd756.c
drivers/i2c/busses/i2c-at91.c
drivers/i2c/busses/i2c-au1550.c
drivers/i2c/busses/i2c-cadence.c
drivers/i2c/busses/i2c-cpm.c
drivers/i2c/busses/i2c-davinci.c
drivers/i2c/busses/i2c-designware-core.c
drivers/i2c/busses/i2c-designware-core.h
drivers/i2c/busses/i2c-designware-pcidrv.c
drivers/i2c/busses/i2c-designware-platdrv.c
drivers/i2c/busses/i2c-eg20t.c
drivers/i2c/busses/i2c-elektor.c
drivers/i2c/busses/i2c-hydra.c
drivers/i2c/busses/i2c-i801.c
drivers/i2c/busses/i2c-imx.c
drivers/i2c/busses/i2c-iop3xx.h
drivers/i2c/busses/i2c-isch.c
drivers/i2c/busses/i2c-ismt.c
drivers/i2c/busses/i2c-nforce2-s4985.c
drivers/i2c/busses/i2c-nforce2.c
drivers/i2c/busses/i2c-omap.c
drivers/i2c/busses/i2c-parport-light.c
drivers/i2c/busses/i2c-parport.c
drivers/i2c/busses/i2c-parport.h
drivers/i2c/busses/i2c-pasemi.c
drivers/i2c/busses/i2c-pca-isa.c
drivers/i2c/busses/i2c-piix4.c
drivers/i2c/busses/i2c-pmcmsp.c
drivers/i2c/busses/i2c-powermac.c
drivers/i2c/busses/i2c-s3c2410.c
drivers/i2c/busses/i2c-sh_mobile.c
drivers/i2c/busses/i2c-sibyte.c
drivers/i2c/busses/i2c-simtec.c
drivers/i2c/busses/i2c-sis5595.c
drivers/i2c/busses/i2c-sis630.c
drivers/i2c/busses/i2c-sis96x.c
drivers/i2c/busses/i2c-taos-evm.c
drivers/i2c/busses/i2c-via.c
drivers/i2c/busses/i2c-viapro.c
drivers/i2c/busses/i2c-xiic.c
drivers/i2c/busses/scx200_acb.c
drivers/i2c/i2c-boardinfo.c
drivers/i2c/i2c-core.c
drivers/i2c/i2c-core.h
drivers/i2c/i2c-dev.c
drivers/i2c/i2c-smbus.c
drivers/i2c/i2c-stub.c
drivers/iio/accel/bmc150-accel.c
drivers/iio/accel/kxcjk-1013.c
drivers/iio/adc/men_z188_adc.c
drivers/iio/gyro/bmg160.c
drivers/iio/light/tsl4531.c
drivers/iio/proximity/as3935.c
drivers/infiniband/ulp/isert/ib_isert.c
drivers/infiniband/ulp/srpt/ib_srpt.c
drivers/input/evdev.c
drivers/input/joystick/xpad.c
drivers/input/misc/twl4030-pwrbutton.c
drivers/input/mouse/alps.c
drivers/input/mouse/elantech.c
drivers/input/mouse/synaptics.c
drivers/irqchip/irq-armada-370-xp.c
drivers/irqchip/irq-atmel-aic-common.c
drivers/irqchip/irq-bcm7120-l2.c
drivers/irqchip/irq-brcmstb-l2.c
drivers/md/dm-bufio.c
drivers/md/dm-raid.c
drivers/md/dm-stripe.c
drivers/md/dm-thin.c
drivers/md/md.c
drivers/md/persistent-data/dm-btree-internal.h
drivers/md/persistent-data/dm-btree-spine.c
drivers/md/persistent-data/dm-btree.c
drivers/media/dvb-core/dvb_frontend.c
drivers/media/dvb-frontends/ds3000.c
drivers/media/dvb-frontends/sp2.c
drivers/media/dvb-frontends/tc90522.c
drivers/media/i2c/smiapp/smiapp-core.c
drivers/media/pci/cx23885/cx23885-core.c
drivers/media/pci/solo6x10/solo6x10-core.c
drivers/media/platform/vivid/vivid-core.c
drivers/media/rc/imon.c
drivers/media/rc/ir-hix5hd2.c
drivers/media/rc/ir-rc5-decoder.c
drivers/media/rc/ir-rc6-decoder.c
drivers/media/rc/rc-ir-raw.c
drivers/media/rc/rc-main.c
drivers/media/usb/s2255/s2255drv.c
drivers/mfd/max77693.c
drivers/mfd/rtsx_pcr.c
drivers/mfd/stmpe.h
drivers/mfd/twl4030-power.c
drivers/mfd/viperboard.c
drivers/mmc/core/host.c
drivers/net/bonding/bond_main.c
drivers/net/bonding/bond_netlink.c
drivers/net/can/dev.c
drivers/net/can/m_can/Kconfig
drivers/net/can/m_can/m_can.c
drivers/net/can/rcar_can.c
drivers/net/can/sja1000/kvaser_pci.c
drivers/net/can/usb/ems_usb.c
drivers/net/can/usb/esd_usb2.c
drivers/net/can/usb/gs_usb.c
drivers/net/can/xilinx_can.c
drivers/net/dsa/bcm_sf2.c
drivers/net/ethernet/apm/xgene/xgene_enet_hw.c
drivers/net/ethernet/apm/xgene/xgene_enet_hw.h
drivers/net/ethernet/apm/xgene/xgene_enet_main.c
drivers/net/ethernet/apm/xgene/xgene_enet_main.h
drivers/net/ethernet/apm/xgene/xgene_enet_sgmac.c
drivers/net/ethernet/apm/xgene/xgene_enet_xgmac.c
drivers/net/ethernet/broadcom/bcmsysport.c
drivers/net/ethernet/broadcom/genet/bcmgenet.c
drivers/net/ethernet/broadcom/genet/bcmgenet.h
drivers/net/ethernet/broadcom/genet/bcmmii.c
drivers/net/ethernet/broadcom/tg3.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
drivers/net/ethernet/chelsio/cxgb4/sge.c
drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
drivers/net/ethernet/chelsio/cxgb4/t4_regs.h
drivers/net/ethernet/chelsio/cxgb4vf/adapter.h
drivers/net/ethernet/chelsio/cxgb4vf/sge.c
drivers/net/ethernet/chelsio/cxgb4vf/t4vf_common.h
drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c
drivers/net/ethernet/cisco/enic/enic_main.c
drivers/net/ethernet/emulex/benet/be_main.c
drivers/net/ethernet/freescale/fec_main.c
drivers/net/ethernet/intel/igb/igb_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
drivers/net/ethernet/marvell/mv643xx_eth.c
drivers/net/ethernet/marvell/mvpp2.c
drivers/net/ethernet/mellanox/mlx4/en_netdev.c
drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
drivers/net/ethernet/mellanox/mlx5/core/eq.c
drivers/net/ethernet/mellanox/mlx5/core/main.c
drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
drivers/net/ethernet/qualcomm/Kconfig
drivers/net/ethernet/renesas/sh_eth.c
drivers/net/ethernet/renesas/sh_eth.h
drivers/net/ethernet/sfc/ef10.c
drivers/net/ethernet/smsc/smc91x.c
drivers/net/ethernet/smsc/smsc911x.c
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
drivers/net/ethernet/sun/sunhme.c
drivers/net/ethernet/ti/cpsw.c
drivers/net/ethernet/ti/cpsw_ale.c
drivers/net/ethernet/ti/cpts.c
drivers/net/ieee802154/fakehard.c
drivers/net/macvtap.c
drivers/net/phy/dp83640.c
drivers/net/phy/phy.c
drivers/net/ppp/ppp_generic.c
drivers/net/ppp/pptp.c
drivers/net/tun.c
drivers/net/usb/asix_devices.c
drivers/net/usb/qmi_wwan.c
drivers/net/virtio_net.c
drivers/net/vxlan.c
drivers/net/wireless/ath/ath9k/ar9003_phy.c
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/b43/phy_common.c
drivers/net/wireless/brcm80211/brcmfmac/of.c
drivers/net/wireless/brcm80211/brcmfmac/pcie.c
drivers/net/wireless/brcm80211/brcmfmac/usb.c
drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
drivers/net/wireless/iwlwifi/iwl-fw.h
drivers/net/wireless/iwlwifi/mvm/fw.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/ops.c
drivers/net/wireless/iwlwifi/mvm/scan.c
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/rt2x00/rt2x00queue.c
drivers/net/wireless/rtlwifi/pci.c
drivers/net/wireless/rtlwifi/rtl8192se/hw.c
drivers/net/wireless/rtlwifi/rtl8192se/phy.c
drivers/net/wireless/rtlwifi/rtl8192se/sw.c
drivers/net/wireless/rtlwifi/rtl8821ae/hw.c
drivers/net/xen-netback/xenbus.c
drivers/net/xen-netfront.c
drivers/of/address.c
drivers/of/base.c
drivers/of/dynamic.c
drivers/of/fdt.c
drivers/of/selftest.c
drivers/of/testcase-data/tests-phandle.dtsi
drivers/pci/access.c
drivers/pci/host/pci-tegra.c
drivers/pci/host/pci-xgene.c
drivers/pci/msi.c
drivers/pci/pci.h
drivers/pci/probe.c
drivers/phy/phy-omap-usb2.c
drivers/pinctrl/pinctrl-baytrail.c
drivers/platform/x86/Kconfig
drivers/platform/x86/acer-wmi.c
drivers/platform/x86/asus-nb-wmi.c
drivers/platform/x86/hp_accel.c
drivers/platform/x86/ideapad-laptop.c
drivers/platform/x86/samsung-laptop.c
drivers/platform/x86/toshiba_acpi.c
drivers/power/ab8500_fg.c
drivers/power/bq2415x_charger.c
drivers/power/charger-manager.c
drivers/power/power_supply_core.c
drivers/regulator/max1586.c
drivers/regulator/max77686.c
drivers/regulator/max77693.c
drivers/regulator/max77802.c
drivers/regulator/max8660.c
drivers/regulator/of_regulator.c
drivers/regulator/s2mpa01.c
drivers/s390/kvm/virtio_ccw.c
drivers/scsi/bnx2fc/bnx2fc_els.c
drivers/scsi/bnx2fc/bnx2fc_fcoe.c
drivers/scsi/bnx2fc/bnx2fc_io.c
drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
drivers/scsi/cxgbi/libcxgbi.c
drivers/scsi/device_handler/scsi_dh_alua.c
drivers/scsi/megaraid/megaraid_sas_base.c
drivers/scsi/scsi_devinfo.c
drivers/scsi/scsi_error.c
drivers/scsi/ufs/ufshcd-pltfrm.c
drivers/scsi/ufs/ufshcd.c
drivers/scsi/ufs/ufshcd.h
drivers/soc/versatile/soc-realview.c
drivers/spi/spi-dw.c
drivers/spi/spi-fsl-dspi.c
drivers/spi/spi-pxa2xx.c
drivers/spi/spi-sirf.c
drivers/spi/spi.c
drivers/staging/iio/meter/ade7758.h
drivers/staging/iio/meter/ade7758_core.c
drivers/staging/iio/meter/ade7758_ring.c
drivers/staging/rtl8188eu/core/rtw_cmd.c
drivers/staging/rtl8188eu/core/rtw_mlme_ext.c
drivers/staging/rtl8188eu/core/rtw_wlan_util.c
drivers/staging/rtl8188eu/os_dep/usb_intf.c
drivers/target/iscsi/iscsi_target.c
drivers/target/target_core_pr.c
drivers/target/target_core_transport.c
drivers/thermal/cpu_cooling.c
drivers/thermal/imx_thermal.c
drivers/thermal/int340x_thermal/int3403_thermal.c
drivers/thermal/of-thermal.c
drivers/thermal/samsung/exynos_thermal_common.c
drivers/thermal/samsung/exynos_thermal_common.h
drivers/thermal/samsung/exynos_tmu.c
drivers/thermal/samsung/exynos_tmu.h
drivers/thermal/samsung/exynos_tmu_data.c
drivers/thermal/samsung/exynos_tmu_data.h
drivers/thermal/st/st_thermal.c
drivers/thermal/thermal_core.c
drivers/tty/n_tty.c
drivers/tty/serial/8250/8250_mtk.c
drivers/tty/serial/of_serial.c
drivers/tty/serial/serial_core.c
drivers/tty/tty_io.c
drivers/tty/vt/consolemap.c
drivers/usb/class/cdc-acm.c
drivers/usb/class/cdc-acm.h
drivers/usb/core/hcd.c
drivers/usb/core/hub.c
drivers/usb/core/quirks.c
drivers/usb/dwc2/gadget.c
drivers/usb/dwc3/ep0.c
drivers/usb/host/Kconfig
drivers/usb/host/hwa-hc.c
drivers/usb/host/xhci-hub.c
drivers/usb/host/xhci-pci.c
drivers/usb/host/xhci-plat.c
drivers/usb/host/xhci-ring.c
drivers/usb/host/xhci.c
drivers/usb/host/xhci.h
drivers/usb/serial/cp210x.c
drivers/usb/serial/ftdi_sio.c
drivers/usb/serial/ftdi_sio_ids.h
drivers/usb/serial/keyspan.c
drivers/usb/serial/kobil_sct.c
drivers/usb/serial/opticon.c
drivers/usb/serial/ssu100.c
drivers/usb/storage/initializers.c
drivers/usb/storage/realtek_cr.c
drivers/usb/storage/transport.c
drivers/usb/storage/unusual_uas.h
drivers/vhost/scsi.c
drivers/watchdog/s3c2410_wdt.c
fs/Makefile
fs/aio.c
fs/btrfs/compression.c
fs/btrfs/compression.h
fs/btrfs/ctree.c
fs/btrfs/file-item.c
fs/btrfs/locking.c
fs/btrfs/locking.h
fs/btrfs/lzo.c
fs/btrfs/zlib.c
fs/ceph/caps.c
fs/dcache.c
fs/fat/namei_vfat.c
fs/isofs/inode.c
fs/jbd2/journal.c
fs/nfs/blocklayout/blocklayout.c
fs/nfs/blocklayout/rpc_pipefs.c
fs/nfs/delegation.c
fs/nfs/delegation.h
fs/nfs/dir.c
fs/nfs/direct.c
fs/nfs/filelayout/filelayout.c
fs/nfs/inode.c
fs/nfs/netns.h
fs/nfs/nfs4proc.c
fs/nfs/write.c
fs/nfsd/nfs4callback.c
fs/nfsd/nfsd.h
fs/notify/fsnotify.c
fs/notify/fsnotify.h
fs/notify/inode_mark.c
fs/notify/mark.c
fs/notify/vfsmount_mark.c
fs/ocfs2/cluster/tcp.c
fs/overlayfs/Kconfig
fs/overlayfs/Makefile
fs/overlayfs/dir.c
fs/overlayfs/inode.c
fs/overlayfs/readdir.c
fs/overlayfs/super.c
fs/xfs/xfs_bmap_util.c
fs/xfs/xfs_itable.c
fs/xfs/xfs_itable.h
include/dt-bindings/clock/qcom,mmcc-apq8084.h
include/dt-bindings/clock/vf610-clock.h
include/dt-bindings/pinctrl/dra.h
include/linux/bitops.h
include/linux/bootmem.h
include/linux/can/dev.h
include/linux/clk-provider.h
include/linux/cma.h
include/linux/iio/events.h
include/linux/inetdevice.h
include/linux/kernel_stat.h
include/linux/kvm_host.h
include/linux/mfd/arizona/core.h
include/linux/mfd/davinci_voicecodec.h
include/linux/mfd/max77693-private.h
include/linux/mmzone.h
include/linux/nfs_xdr.h
include/linux/of.h
include/linux/page-isolation.h
include/linux/pci-acpi.h
include/linux/pci.h
include/linux/percpu-refcount.h
include/linux/platform_data/asoc-s3c.h
include/linux/pm_domain.h
include/linux/power/charger-manager.h
include/linux/power_supply.h
include/linux/regmap.h
include/linux/ring_buffer.h
include/linux/socket.h
include/net/9p/transport.h
include/net/inet_common.h
include/net/netfilter/nf_tables.h
include/net/udp_tunnel.h
include/net/vxlan.h
include/sound/pcm.h
include/sound/rcar_snd.h
include/sound/rt5645.h
include/sound/rt5677.h
include/sound/soc-dai.h
include/sound/soc-dapm.h
include/sound/soc-dpcm.h
include/sound/soc.h
include/trace/events/asoc.h
include/uapi/linux/Kbuild
include/uapi/linux/if_bridge.h
include/uapi/sound/asound.h
init/main.c
ipc/sem.c
kernel/audit.c
kernel/audit_tree.c
kernel/events/core.c
kernel/events/uprobes.c
kernel/panic.c
kernel/power/suspend.c
kernel/sched/core.c
kernel/sched/deadline.c
kernel/sched/fair.c
kernel/sched/idle_task.c
kernel/sched/rt.c
kernel/sched/sched.h
kernel/sched/stop_task.c
kernel/time/posix-cpu-timers.c
kernel/trace/ring_buffer.c
kernel/trace/trace.c
lib/Makefile
lib/genalloc.c
lib/rhashtable.c
lib/show_mem.c
mm/bootmem.c
mm/cma.c
mm/compaction.c
mm/frontswap.c
mm/internal.h
mm/iov_iter.c
mm/memory.c
mm/memory_hotplug.c
mm/mmap.c
mm/nobootmem.c
mm/page_alloc.c
mm/page_isolation.c
mm/rmap.c
mm/slab.c
mm/slab_common.c
mm/truncate.c
mm/vmpressure.c
net/bridge/br_multicast.c
net/bridge/br_netlink.c
net/bridge/netfilter/nft_reject_bridge.c
net/ceph/auth_x.c
net/ceph/crypto.c
net/ceph/messenger.c
net/ceph/osd_client.c
net/core/rtnetlink.c
net/core/skbuff.c
net/dcb/dcbnl.c
net/dsa/slave.c
net/ipv4/af_inet.c
net/ipv4/fib_rules.c
net/ipv4/fou.c
net/ipv4/geneve.c
net/ipv4/igmp.c
net/ipv4/ip_sockglue.c
net/ipv4/ip_vti.c
net/ipv4/netfilter/nft_masq_ipv4.c
net/ipv4/ping.c
net/ipv4/tcp.c
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv6/ip6_gre.c
net/ipv6/ip6_offload.c
net/ipv6/ip6_tunnel.c
net/ipv6/ip6_udp_tunnel.c
net/ipv6/ip6_vti.c
net/ipv6/ip6mr.c
net/ipv6/mcast.c
net/ipv6/netfilter/nft_masq_ipv6.c
net/ipv6/sit.c
net/ipv6/tcp_ipv6.c
net/ipx/af_ipx.c
net/mac80211/aes_ccm.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/mesh.c
net/mac80211/mlme.c
net/mac80211/rc80211_minstrel_ht.c
net/mac80211/rx.c
net/mac80211/spectmgmt.c
net/netfilter/ipset/ip_set_core.c
net/netfilter/ipvs/ip_vs_xmit.c
net/netfilter/nf_tables_api.c
net/netfilter/nfnetlink.c
net/netfilter/nft_compat.c
net/netlink/af_netlink.c
net/openvswitch/actions.c
net/openvswitch/datapath.c
net/openvswitch/flow_netlink.c
net/packet/af_packet.c
net/sctp/auth.c
net/sctp/sm_make_chunk.c
net/sunrpc/auth_gss/auth_gss.c
net/sunrpc/svcsock.c
security/keys/internal.h
security/keys/keyctl.c
security/keys/keyring.c
security/keys/request_key.c
security/keys/request_key_auth.c
security/selinux/hooks.c
sound/core/pcm.c
sound/core/pcm_misc.c
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_priv.h
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_realtek.c
sound/soc/Makefile
sound/soc/atmel/Kconfig
sound/soc/atmel/Makefile
sound/soc/atmel/atmel_ssc_dai.c
sound/soc/atmel/snd-soc-afeb9260.c [deleted file]
sound/soc/au1x/ac97c.c
sound/soc/au1x/psc-ac97.c
sound/soc/blackfin/bf5xx-ac97.c
sound/soc/blackfin/bf5xx-ad1980.c
sound/soc/cirrus/Kconfig
sound/soc/cirrus/ep93xx-ac97.c
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ab8500-codec.c
sound/soc/codecs/ac97.c
sound/soc/codecs/ad193x.c
sound/soc/codecs/ad1980.c
sound/soc/codecs/ad1980.h [deleted file]
sound/soc/codecs/adau1373.c
sound/soc/codecs/adau1761.c
sound/soc/codecs/adau1781.c
sound/soc/codecs/adau17x1.c
sound/soc/codecs/adav80x.c
sound/soc/codecs/ak4535.c
sound/soc/codecs/ak4641.c
sound/soc/codecs/ak4642.c
sound/soc/codecs/ak4671.c
sound/soc/codecs/alc5623.c
sound/soc/codecs/alc5632.c
sound/soc/codecs/arizona.c
sound/soc/codecs/cq93vc.c
sound/soc/codecs/cs4265.c
sound/soc/codecs/cs4271-i2c.c [new file with mode: 0644]
sound/soc/codecs/cs4271-spi.c [new file with mode: 0644]
sound/soc/codecs/cs4271.c
sound/soc/codecs/cs4271.h [new file with mode: 0644]
sound/soc/codecs/cs42l51-i2c.c
sound/soc/codecs/cs42l51.c
sound/soc/codecs/cs42l51.h
sound/soc/codecs/cs42l73.c
sound/soc/codecs/es8328-i2c.c
sound/soc/codecs/hdmi.c
sound/soc/codecs/lm49453.c
sound/soc/codecs/max98088.c
sound/soc/codecs/max98090.c
sound/soc/codecs/max98090.h
sound/soc/codecs/max98095.c
sound/soc/codecs/max9850.c
sound/soc/codecs/rt286.c
sound/soc/codecs/rt5631.c
sound/soc/codecs/rt5645.c
sound/soc/codecs/rt5645.h
sound/soc/codecs/rt5670.c
sound/soc/codecs/rt5670.h
sound/soc/codecs/rt5677-spi.c [new file with mode: 0644]
sound/soc/codecs/rt5677-spi.h [new file with mode: 0644]
sound/soc/codecs/rt5677.c
sound/soc/codecs/rt5677.h
sound/soc/codecs/sgtl5000.c
sound/soc/codecs/sgtl5000.h
sound/soc/codecs/sigmadsp.c
sound/soc/codecs/stac9766.c
sound/soc/codecs/tlv320aic31xx.c
sound/soc/codecs/wm5102.c
sound/soc/codecs/wm8731.c
sound/soc/codecs/wm8903.c
sound/soc/codecs/wm8958-dsp2.c
sound/soc/codecs/wm8962.c
sound/soc/codecs/wm8994.c
sound/soc/codecs/wm8994.h
sound/soc/codecs/wm9705.c
sound/soc/codecs/wm9712.c
sound/soc/codecs/wm9713.c
sound/soc/codecs/wm_adsp.c
sound/soc/davinci/davinci-mcasp.c
sound/soc/davinci/davinci-mcasp.h
sound/soc/dwc/designware_i2s.c
sound/soc/fsl/eukrea-tlv320.c
sound/soc/fsl/fsl-asoc-card.c
sound/soc/fsl/fsl_asrc.c
sound/soc/fsl/fsl_esai.c
sound/soc/fsl/fsl_ssi.c
sound/soc/fsl/imx-sgtl5000.c
sound/soc/fsl/imx-spdif.c
sound/soc/fsl/imx-ssi.c
sound/soc/fsl/imx-wm8962.c
sound/soc/fsl/mpc5200_dma.c
sound/soc/fsl/mpc5200_psc_ac97.c
sound/soc/generic/simple-card.c
sound/soc/intel/Kconfig
sound/soc/intel/Makefile
sound/soc/intel/broadwell.c
sound/soc/intel/bytcr_dpcm_rt5640.c [new file with mode: 0644]
sound/soc/intel/cht_bsw_rt5672.c [new file with mode: 0644]
sound/soc/intel/haswell.c
sound/soc/intel/sst-atom-controls.c
sound/soc/intel/sst-atom-controls.h
sound/soc/intel/sst-baytrail-dsp.c
sound/soc/intel/sst-dsp-priv.h
sound/soc/intel/sst-dsp.c
sound/soc/intel/sst-dsp.h
sound/soc/intel/sst-firmware.c
sound/soc/intel/sst-haswell-dsp.c
sound/soc/intel/sst-haswell-ipc.c
sound/soc/intel/sst-haswell-ipc.h
sound/soc/intel/sst-haswell-pcm.c
sound/soc/intel/sst-mfld-platform-compress.c
sound/soc/intel/sst-mfld-platform-pcm.c
sound/soc/intel/sst-mfld-platform.h
sound/soc/intel/sst/Makefile [new file with mode: 0644]
sound/soc/intel/sst/sst.c [new file with mode: 0644]
sound/soc/intel/sst/sst.h [new file with mode: 0644]
sound/soc/intel/sst/sst_acpi.c [new file with mode: 0644]
sound/soc/intel/sst/sst_drv_interface.c [new file with mode: 0644]
sound/soc/intel/sst/sst_ipc.c [new file with mode: 0644]
sound/soc/intel/sst/sst_loader.c [new file with mode: 0644]
sound/soc/intel/sst/sst_pci.c [new file with mode: 0644]
sound/soc/intel/sst/sst_pvt.c [new file with mode: 0644]
sound/soc/intel/sst/sst_stream.c [new file with mode: 0644]
sound/soc/jz4740/qi_lb60.c
sound/soc/mxs/mxs-saif.c
sound/soc/mxs/mxs-sgtl5000.c
sound/soc/nuc900/nuc900-ac97.c
sound/soc/omap/Kconfig
sound/soc/omap/mcbsp.c
sound/soc/pxa/mioa701_wm9713.c
sound/soc/pxa/pxa-ssp.c
sound/soc/pxa/pxa2xx-ac97.c
sound/soc/pxa/spitz.c
sound/soc/rockchip/Kconfig
sound/soc/rockchip/rockchip_i2s.c
sound/soc/samsung/Kconfig
sound/soc/samsung/Makefile
sound/soc/samsung/ac97.c
sound/soc/samsung/arndale_rt5631.c [new file with mode: 0644]
sound/soc/samsung/i2s-regs.h
sound/soc/samsung/i2s.c
sound/soc/samsung/odroidx2_max98090.c
sound/soc/samsung/snow.c
sound/soc/sh/fsi.c
sound/soc/sh/hac.c
sound/soc/sh/rcar/adg.c
sound/soc/sh/rcar/core.c
sound/soc/sh/rcar/dvc.c
sound/soc/sh/rcar/gen.c
sound/soc/sh/rcar/rsnd.h
sound/soc/sh/rcar/src.c
sound/soc/sh/rcar/ssi.c
sound/soc/soc-ac97.c [new file with mode: 0644]
sound/soc/soc-cache.c
sound/soc/soc-compress.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/soc-jack.c
sound/soc/soc-ops.c [new file with mode: 0644]
sound/soc/soc-pcm.c
sound/soc/tegra/tegra20_ac97.c
sound/soc/txx9/txx9aclc-ac97.c
sound/soc/txx9/txx9aclc.c
sound/soc/ux500/mop500.c
sound/usb/card.c
sound/usb/mixer.c
sound/usb/mixer_quirks.c
sound/usb/quirks.c
tools/testing/selftests/ftrace/ftracetest
tools/testing/selftests/net/psock_fanout.c
virt/kvm/arm/vgic.c
virt/kvm/kvm_main.c

index 1e61113..80ae87a 100644 (file)
@@ -3,8 +3,10 @@
 Required properties:
 - compatible           : should contain one of the following:
                          - "renesas,sata-r8a7779" for R-Car H1
-                         - "renesas,sata-r8a7790" for R-Car H2
-                         - "renesas,sata-r8a7791" for R-Car M2
+                         - "renesas,sata-r8a7790-es1" for R-Car H2 ES1
+                         - "renesas,sata-r8a7790" for R-Car H2 other than ES1
+                         - "renesas,sata-r8a7791" for R-Car M2-W
+                         - "renesas,sata-r8a7793" for R-Car M2-N
 - reg                  : address and length of the SATA registers;
 - interrupts           : must consist of one interrupt specifier.
 
index ce6a1a0..8a3c408 100644 (file)
@@ -30,10 +30,6 @@ should only be used when a device has multiple interrupt parents.
   Example:
        interrupts-extended = <&intc1 5 1>, <&intc2 1 0>;
 
-A device node may contain either "interrupts" or "interrupts-extended", but not
-both. If both properties are present, then the operating system should log an
-error and use only the data in "interrupts".
-
 2) Interrupt controller nodes
 -----------------------------
 
index 41aeed3..f8fbe9a 100644 (file)
@@ -7,3 +7,14 @@ And for the interrupt mapping part:
 
 Open Firmware Recommended Practice: Interrupt Mapping
 http://www.openfirmware.org/1275/practice/imap/imap0_9d.pdf
+
+Additionally to the properties specified in the above standards a host bridge
+driver implementation may support the following properties:
+
+- linux,pci-domain:
+   If present this property assigns a fixed PCI domain number to a host bridge,
+   otherwise an unstable (across boots) unique number will be assigned.
+   It is required to either not set this property at all or set it for all
+   host bridges in the system, otherwise potentially conflicting domain numbers
+   may be assigned to root buses behind different host bridges.  The domain
+   number for each host bridge in the system must be unique.
index a186181..51b943c 100644 (file)
@@ -9,7 +9,7 @@ Please refer to pinctrl-bindings.txt in this directory for details of the
 common pinctrl bindings used by client devices, including the meaning of the
 phrase "pin configuration node".
 
-TZ1090-PDC's pin configuration nodes act as a container for an abitrary number
+TZ1090-PDC's pin configuration nodes act as a container for an arbitrary number
 of subnodes. Each of these subnodes represents some desired configuration for a
 pin, a group, or a list of pins or groups. This configuration can include the
 mux function to select on those pin(s)/group(s), and various pin configuration
index 4b27c99..49d0e60 100644 (file)
@@ -9,7 +9,7 @@ Please refer to pinctrl-bindings.txt in this directory for details of the
 common pinctrl bindings used by client devices, including the meaning of the
 phrase "pin configuration node".
 
-TZ1090's pin configuration nodes act as a container for an abitrary number of
+TZ1090's pin configuration nodes act as a container for an arbitrary number of
 subnodes. Each of these subnodes represents some desired configuration for a
 pin, a group, or a list of pins or groups. This configuration can include the
 mux function to select on those pin(s)/group(s), and various pin configuration
index daa7689..ac4da9f 100644 (file)
@@ -9,7 +9,7 @@ Please refer to pinctrl-bindings.txt in this directory for details of the
 common pinctrl bindings used by client devices, including the meaning of the
 phrase "pin configuration node".
 
-Lantiq's pin configuration nodes act as a container for an abitrary number of
+Lantiq's pin configuration nodes act as a container for an arbitrary number of
 subnodes. Each of these subnodes represents some desired configuration for a
 pin, a group, or a list of pins or groups. This configuration can include the
 mux function to select on those group(s), and two pin configuration parameters:
index b5469db..e89b467 100644 (file)
@@ -9,7 +9,7 @@ Please refer to pinctrl-bindings.txt in this directory for details of the
 common pinctrl bindings used by client devices, including the meaning of the
 phrase "pin configuration node".
 
-Lantiq's pin configuration nodes act as a container for an abitrary number of
+Lantiq's pin configuration nodes act as a container for an arbitrary number of
 subnodes. Each of these subnodes represents some desired configuration for a
 pin, a group, or a list of pins or groups. This configuration can include the
 mux function to select on those group(s), and two pin configuration parameters:
index 61e73cd..3c8ce28 100644 (file)
@@ -9,7 +9,7 @@ Please refer to pinctrl-bindings.txt in this directory for details of the
 common pinctrl bindings used by client devices, including the meaning of the
 phrase "pin configuration node".
 
-Tegra's pin configuration nodes act as a container for an abitrary number of
+Tegra's pin configuration nodes act as a container for an arbitrary number of
 subnodes. Each of these subnodes represents some desired configuration for a
 pin, a group, or a list of pins or groups. This configuration can include the
 mux function to select on those pin(s)/group(s), and various pin configuration
index c596a6a..5f55be5 100644 (file)
@@ -13,7 +13,7 @@ Optional properties:
 Please refer to pinctrl-bindings.txt in this directory for details of the common
 pinctrl bindings used by client devices.
 
-SiRFprimaII's pinmux nodes act as a container for an abitrary number of subnodes.
+SiRFprimaII's pinmux nodes act as a container for an arbitrary number of subnodes.
 Each of these subnodes represents some desired configuration for a group of pins.
 
 Required subnode-properties:
index b4480d5..4586155 100644 (file)
@@ -32,7 +32,7 @@ Required properties:
 Please refer to pinctrl-bindings.txt in this directory for details of the common
 pinctrl bindings used by client devices.
 
-SPEAr's pinmux nodes act as a container for an abitrary number of subnodes. Each
+SPEAr's pinmux nodes act as a container for an arbitrary number of subnodes. Each
 of these subnodes represents muxing for a pin, a group, or a list of pins or
 groups.
 
index 2fb90b3..a7bde64 100644 (file)
@@ -18,7 +18,7 @@ Please refer to pinctrl-bindings.txt in this directory for details of the
 common pinctrl bindings used by client devices, including the meaning of the
 phrase "pin configuration node".
 
-Qualcomm's pin configuration nodes act as a container for an abitrary number of
+Qualcomm's pin configuration nodes act as a container for an arbitrary number of
 subnodes. Each of these subnodes represents some desired configuration for a
 pin, a group, or a list of pins or groups. This configuration can include the
 mux function to select on those pin(s)/group(s), and various pin configuration
index ffafa19..c4ea61a 100644 (file)
@@ -47,7 +47,7 @@ Please refer to pinctrl-bindings.txt in this directory for details of the
 common pinctrl bindings used by client devices, including the meaning of the
 phrase "pin configuration node".
 
-The pin configuration nodes act as a container for an abitrary number of
+The pin configuration nodes act as a container for an arbitrary number of
 subnodes. Each of these subnodes represents some desired configuration for a
 pin, a group, or a list of pins or groups. This configuration can include the
 mux function to select on those pin(s)/group(s), and various pin configuration
index e33e4dc..6e88e91 100644 (file)
@@ -18,7 +18,7 @@ Please refer to pinctrl-bindings.txt in this directory for details of the
 common pinctrl bindings used by client devices, including the meaning of the
 phrase "pin configuration node".
 
-Qualcomm's pin configuration nodes act as a container for an abitrary number of
+Qualcomm's pin configuration nodes act as a container for an arbitrary number of
 subnodes. Each of these subnodes represents some desired configuration for a
 pin, a group, or a list of pins or groups. This configuration can include the
 mux function to select on those pin(s)/group(s), and various pin configuration
index 93b7de9..eb8d8aa 100644 (file)
@@ -47,7 +47,7 @@ Please refer to pinctrl-bindings.txt in this directory for details of the
 common pinctrl bindings used by client devices, including the meaning of the
 phrase "pin configuration node".
 
-The pin configuration nodes act as a container for an abitrary number of
+The pin configuration nodes act as a container for an arbitrary number of
 subnodes. Each of these subnodes represents some desired configuration for a
 pin, a group, or a list of pins or groups. This configuration can include the
 mux function to select on those pin(s)/group(s), and various pin configuration
index d2ea80d..e4d6a9d 100644 (file)
@@ -18,7 +18,7 @@ Please refer to pinctrl-bindings.txt in this directory for details of the
 common pinctrl bindings used by client devices, including the meaning of the
 phrase "pin configuration node".
 
-Qualcomm's pin configuration nodes act as a container for an abitrary number of
+Qualcomm's pin configuration nodes act as a container for an arbitrary number of
 subnodes. Each of these subnodes represents some desired configuration for a
 pin, a group, or a list of pins or groups. This configuration can include the
 mux function to select on those pin(s)/group(s), and various pin configuration
diff --git a/Documentation/devicetree/bindings/sound/arndale.txt b/Documentation/devicetree/bindings/sound/arndale.txt
new file mode 100644 (file)
index 0000000..0e76946
--- /dev/null
@@ -0,0 +1,24 @@
+Audio Binding for Arndale boards
+
+Required properties:
+- compatible : Can be the following,
+                       "samsung,arndale-rt5631"
+
+- samsung,audio-cpu: The phandle of the Samsung I2S controller
+- samsung,audio-codec: The phandle of the audio codec
+
+Optional:
+- samsung,model: The name of the sound-card
+
+Arndale Boards has many audio daughter cards, one of them is
+rt5631/alc5631. Below example shows audio bindings for rt5631/
+alc5631 based codec.
+
+Example:
+
+sound {
+               compatible = "samsung,arndale-rt5631";
+
+               samsung,audio-cpu = <&i2s0>
+               samsung,audio-codec = <&rt5631>;
+};
index 60ca079..46bc982 100644 (file)
@@ -32,7 +32,7 @@ Optional properties:
 - rx-num-evt : FIFO levels.
 - sram-size-playback : size of sram to be allocated during playback
 - sram-size-capture  : size of sram to be allocated during capture
-- interrupts : Interrupt numbers for McASP, currently not used by the driver
+- interrupts : Interrupt numbers for McASP
 - interrupt-names : Known interrupt names are "tx" and "rx"
 - pinctrl-0: Should specify pin control group used for this controller.
 - pinctrl-names: Should contain only one value - "default", for more details
index 0d7985c..6dfa88c 100644 (file)
@@ -1,11 +1,16 @@
 Audio complex for Eukrea boards with tlv320aic23 codec.
 
 Required properties:
-- compatible : "eukrea,asoc-tlv320"
-- eukrea,model : The user-visible name of this sound complex.
-- ssi-controller : The phandle of the SSI controller.
-- fsl,mux-int-port : The internal port of the i.MX audio muxer (AUDMUX).
-- fsl,mux-ext-port : The external port of the i.MX audio muxer.
+
+  - compatible         : "eukrea,asoc-tlv320"
+
+  - eukrea,model       : The user-visible name of this sound complex.
+
+  - ssi-controller     : The phandle of the SSI controller.
+
+  - fsl,mux-int-port   : The internal port of the i.MX audio muxer (AUDMUX).
+
+  - fsl,mux-ext-port   : The external port of the i.MX audio muxer.
 
 Note: The AUDMUX port numbering should start at 1, which is consistent with
 hardware manual.
index 52f5b6b..d3b6b5f 100644 (file)
@@ -7,37 +7,39 @@ other DSPs. It has up to six transmitters and four receivers.
 
 Required properties:
 
-  - compatible : Compatible list, must contain "fsl,imx35-esai" or
-                "fsl,vf610-esai"
+  - compatible         : Compatible list, must contain "fsl,imx35-esai" or
+                         "fsl,vf610-esai"
 
-  - reg : Offset and length of the register set for the device.
+  - reg                        : Offset and length of the register set for the device.
 
-  - interrupts : Contains the spdif interrupt.
+  - interrupts         : Contains the spdif interrupt.
 
-  - dmas : Generic dma devicetree binding as described in
-  Documentation/devicetree/bindings/dma/dma.txt.
+  - dmas               : Generic dma devicetree binding as described in
+                         Documentation/devicetree/bindings/dma/dma.txt.
 
-  - dma-names : Two dmas have to be defined, "tx" and "rx".
+  - dma-names          : Two dmas have to be defined, "tx" and "rx".
 
-  - clocks: Contains an entry for each entry in clock-names.
+  - clocks             : Contains an entry for each entry in clock-names.
 
-  - clock-names : Includes the following entries:
-       "core"          The core clock used to access registers
-       "extal"         The esai baud clock for esai controller used to derive
-                       HCK, SCK and FS.
-       "fsys"          The system clock derived from ahb clock used to derive
-                       HCK, SCK and FS.
+  - clock-names                : Includes the following entries:
+       "core"            The core clock used to access registers
+       "extal"           The esai baud clock for esai controller used to
+                         derive HCK, SCK and FS.
+       "fsys"            The system clock derived from ahb clock used to
+                         derive HCK, SCK and FS.
 
-  - fsl,fifo-depth: The number of elements in the transmit and receive FIFOs.
-    This number is the maximum allowed value for TFCR[TFWM] or RFCR[RFWM].
+  - fsl,fifo-depth     : The number of elements in the transmit and receive
+                         FIFOs. This number is the maximum allowed value for
+                         TFCR[TFWM] or RFCR[RFWM].
 
   - fsl,esai-synchronous: This is a boolean property. If present, indicating
-    that ESAI would work in the synchronous mode, which means all the settings
-    for Receiving would be duplicated from Transmition related registers.
+                         that ESAI would work in the synchronous mode, which
+                         means all the settings for Receiving would be
+                         duplicated from Transmition related registers.
 
-  - big-endian : If this property is absent, the native endian mode will
-    be in use as default, or the big endian mode will be in use for all the
-    device registers.
+  - big-endian         : If this property is absent, the native endian mode
+                         will be in use as default, or the big endian mode
+                         will be in use for all the device registers.
 
 Example:
 
index 3e9e82c..b5ee32e 100644 (file)
@@ -6,32 +6,31 @@ a fibre cable.
 
 Required properties:
 
-  - compatible : Compatible list, must contain "fsl,imx35-spdif".
+  - compatible         : Compatible list, must contain "fsl,imx35-spdif".
 
-  - reg : Offset and length of the register set for the device.
+  - reg                        : Offset and length of the register set for the device.
 
-  - interrupts : Contains the spdif interrupt.
+  - interrupts         : Contains the spdif interrupt.
 
-  - dmas : Generic dma devicetree binding as described in
-  Documentation/devicetree/bindings/dma/dma.txt.
+  - dmas               : Generic dma devicetree binding as described in
+                         Documentation/devicetree/bindings/dma/dma.txt.
 
-  - dma-names : Two dmas have to be defined, "tx" and "rx".
+  - dma-names          : Two dmas have to be defined, "tx" and "rx".
 
-  - clocks : Contains an entry for each entry in clock-names.
+  - clocks             : Contains an entry for each entry in clock-names.
 
-  - clock-names : Includes the following entries:
-       "core"          The core clock of spdif controller
-       "rxtx<0-7>"     Clock source list for tx and rx clock.
-                       This clock list should be identical to
-                       the source list connecting to the spdif
-                       clock mux in "SPDIF Transceiver Clock
-                       Diagram" of SoC reference manual. It
-                       can also be referred to TxClk_Source
-                       bit of register SPDIF_STC.
+  - clock-names                : Includes the following entries:
+       "core"            The core clock of spdif controller.
+       "rxtx<0-7>"       Clock source list for tx and rx clock.
+                         This clock list should be identical to the source
+                         list connecting to the spdif clock mux in "SPDIF
+                         Transceiver Clock Diagram" of SoC reference manual.
+                         It can also be referred to TxClk_Source bit of
+                         register SPDIF_STC.
 
-   - big-endian : If this property is absent, the native endian mode will
-   be in use as default, or the big endian mode will be in use for all the
-   device registers.
+   - big-endian                : If this property is absent, the native endian mode
+                         will be in use as default, or the big endian mode
+                         will be in use for all the device registers.
 
 Example:
 
index 4956b14..044e5d7 100644 (file)
@@ -5,32 +5,48 @@ which provides a synchronous audio interface that supports fullduplex
 serial interfaces with frame synchronization such as I2S, AC97, TDM, and
 codec/DSP interfaces.
 
-
 Required properties:
-- compatible: Compatible list, contains "fsl,vf610-sai" or "fsl,imx6sx-sai".
-- reg: Offset and length of the register set for the device.
-- clocks: Must contain an entry for each entry in clock-names.
-- clock-names : Must include the "bus" for register access and "mclk1" "mclk2"
-  "mclk3" for bit clock and frame clock providing.
-- dmas : Generic dma devicetree binding as described in
-  Documentation/devicetree/bindings/dma/dma.txt.
-- dma-names : Two dmas have to be defined, "tx" and "rx".
-- pinctrl-names: Must contain a "default" entry.
-- pinctrl-NNN: One property must exist for each entry in pinctrl-names.
-  See ../pinctrl/pinctrl-bindings.txt for details of the property values.
-- big-endian: Boolean property, required if all the FTM_PWM registers
-  are big-endian rather than little-endian.
-- lsb-first: Configures whether the LSB or the MSB is transmitted first for
-  the fifo data. If this property is absent, the MSB is transmitted first as
-  default, or the LSB is transmitted first.
-- fsl,sai-synchronous-rx: This is a boolean property. If present, indicating
-  that SAI will work in the synchronous mode (sync Tx with Rx) which means
-  both the transimitter and receiver will send and receive data by following
-  receiver's bit clocks and frame sync clocks.
-- fsl,sai-asynchronous: This is a boolean property. If present, indicating
-  that SAI will work in the asynchronous mode, which means both transimitter
-  and receiver will send and receive data by following their own bit clocks
-  and frame sync clocks separately.
+
+  - compatible         : Compatible list, contains "fsl,vf610-sai" or
+                         "fsl,imx6sx-sai".
+
+  - reg                        : Offset and length of the register set for the device.
+
+  - clocks             : Must contain an entry for each entry in clock-names.
+
+  - clock-names                : Must include the "bus" for register access and
+                         "mclk1", "mclk2", "mclk3" for bit clock and frame
+                         clock providing.
+  - dmas               : Generic dma devicetree binding as described in
+                         Documentation/devicetree/bindings/dma/dma.txt.
+
+  - dma-names          : Two dmas have to be defined, "tx" and "rx".
+
+  - pinctrl-names      : Must contain a "default" entry.
+
+  - pinctrl-NNN                : One property must exist for each entry in
+                         pinctrl-names. See ../pinctrl/pinctrl-bindings.txt
+                         for details of the property values.
+
+  - big-endian         : Boolean property, required if all the FTM_PWM
+                         registers are big-endian rather than little-endian.
+
+  - lsb-first          : Configures whether the LSB or the MSB is transmitted
+                         first for the fifo data. If this property is absent,
+                         the MSB is transmitted first as default, or the LSB
+                         is transmitted first.
+
+  - fsl,sai-synchronous-rx: This is a boolean property. If present, indicating
+                         that SAI will work in the synchronous mode (sync Tx
+                         with Rx) which means both the transimitter and the
+                         receiver will send and receive data by following
+                         receiver's bit clocks and frame sync clocks.
+
+  - fsl,sai-asynchronous: This is a boolean property. If present, indicating
+                         that SAI will work in the asynchronous mode, which
+                         means both transimitter and receiver will send and
+                         receive data by following their own bit clocks and
+                         frame sync clocks separately.
 
 Note:
 - If both fsl,sai-asynchronous and fsl,sai-synchronous-rx are absent, the
index e4acdd8..2f89db8 100644 (file)
@@ -1,33 +1,40 @@
 Freescale i.MX audio complex with SGTL5000 codec
 
 Required properties:
-- compatible : "fsl,imx-audio-sgtl5000"
-- model : The user-visible name of this sound complex
-- ssi-controller : The phandle of the i.MX SSI controller
-- audio-codec : The phandle of the SGTL5000 audio codec
-- audio-routing : A list of the connections between audio components.
-  Each entry is a pair of strings, the first being the connection's sink,
-  the second being the connection's source. Valid names could be power
-  supplies, SGTL5000 pins, and the jacks on the board:
-
-  Power supplies:
-   * Mic Bias
-
-  SGTL5000 pins:
-   * MIC_IN
-   * LINE_IN
-   * HP_OUT
-   * LINE_OUT
-
-  Board connectors:
-   * Mic Jack
-   * Line In Jack
-   * Headphone Jack
-   * Line Out Jack
-   * Ext Spk
-
-- mux-int-port : The internal port of the i.MX audio muxer (AUDMUX)
-- mux-ext-port : The external port of the i.MX audio muxer
+
+  - compatible         : "fsl,imx-audio-sgtl5000"
+
+  - model              : The user-visible name of this sound complex
+
+  - ssi-controller     : The phandle of the i.MX SSI controller
+
+  - audio-codec                : The phandle of the SGTL5000 audio codec
+
+  - audio-routing      : A list of the connections between audio components.
+                         Each entry is a pair of strings, the first being the
+                         connection's sink, the second being the connection's
+                         source. Valid names could be power supplies, SGTL5000
+                         pins, and the jacks on the board:
+
+                         Power supplies:
+                          * Mic Bias
+
+                         SGTL5000 pins:
+                          * MIC_IN
+                          * LINE_IN
+                          * HP_OUT
+                          * LINE_OUT
+
+                         Board connectors:
+                          * Mic Jack
+                          * Line In Jack
+                          * Headphone Jack
+                          * Line Out Jack
+                          * Ext Spk
+
+  - mux-int-port       : The internal port of the i.MX audio muxer (AUDMUX)
+
+  - mux-ext-port       : The external port of the i.MX audio muxer
 
 Note: The AUDMUX port numbering should start at 1, which is consistent with
 hardware manual.
index 7d13479..da84a44 100644 (file)
@@ -2,23 +2,25 @@ Freescale i.MX audio complex with S/PDIF transceiver
 
 Required properties:
 
-  - compatible : "fsl,imx-audio-spdif"
+  - compatible         : "fsl,imx-audio-spdif"
 
-  - model : The user-visible name of this sound complex
+  - model              : The user-visible name of this sound complex
 
-  - spdif-controller : The phandle of the i.MX S/PDIF controller
+  - spdif-controller   : The phandle of the i.MX S/PDIF controller
 
 
 Optional properties:
 
-  - spdif-out : This is a boolean property. If present, the transmitting
-    function of S/PDIF will be enabled, indicating there's a physical
-    S/PDIF out connector/jack on the board or it's connecting to some
-    other IP block, such as an HDMI encoder/display-controller.
+  - spdif-out          : This is a boolean property. If present, the
+                         transmitting function of S/PDIF will be enabled,
+                         indicating there's a physical S/PDIF out connector
+                         or jack on the board or it's connecting to some
+                         other IP block, such as an HDMI encoder or
+                         display-controller.
 
-  - spdif-in : This is a boolean property. If present, the receiving
-    function of S/PDIF will be enabled, indicating there's a physical
-    S/PDIF in connector/jack on the board.
+  - spdif-in           : This is a boolean property. If present, the receiving
+                         function of S/PDIF will be enabled, indicating there
+                         is a physical S/PDIF in connector/jack on the board.
 
 * Note: At least one of these two properties should be set in the DT binding.
 
index f49450a..acea71b 100644 (file)
@@ -1,25 +1,32 @@
 Freescale i.MX audio complex with WM8962 codec
 
 Required properties:
-- compatible : "fsl,imx-audio-wm8962"
-- model : The user-visible name of this sound complex
-- ssi-controller : The phandle of the i.MX SSI controller
-- audio-codec : The phandle of the WM8962 audio codec
-- audio-routing : A list of the connections between audio components.
-  Each entry is a pair of strings, the first being the connection's sink,
-  the second being the connection's source. Valid names could be power
-  supplies, WM8962 pins, and the jacks on the board:
-
-  Power supplies:
-   * Mic Bias
-
-  Board connectors:
-   * Mic Jack
-   * Headphone Jack
-   * Ext Spk
-
-- mux-int-port : The internal port of the i.MX audio muxer (AUDMUX)
-- mux-ext-port : The external port of the i.MX audio muxer
+
+  - compatible         : "fsl,imx-audio-wm8962"
+
+  - model              : The user-visible name of this sound complex
+
+  - ssi-controller     : The phandle of the i.MX SSI controller
+
+  - audio-codec                : The phandle of the WM8962 audio codec
+
+  - audio-routing      : A list of the connections between audio components.
+                         Each entry is a pair of strings, the first being the
+                         connection's sink, the second being the connection's
+                         source. Valid names could be power supplies, WM8962
+                         pins, and the jacks on the board:
+
+                         Power supplies:
+                          * Mic Bias
+
+                         Board connectors:
+                          * Mic Jack
+                          * Headphone Jack
+                          * Ext Spk
+
+  - mux-int-port       : The internal port of the i.MX audio muxer (AUDMUX)
+
+  - mux-ext-port       : The external port of the i.MX audio muxer
 
 Note: The AUDMUX port numbering should start at 1, which is consistent with
 hardware manual.
index f88a00e..b30a737 100644 (file)
@@ -1,18 +1,24 @@
 Freescale Digital Audio Mux (AUDMUX) device
 
 Required properties:
-- compatible : "fsl,imx21-audmux" for AUDMUX version firstly used on i.MX21,
-  or "fsl,imx31-audmux" for the version firstly used on i.MX31.
-- reg : Should contain AUDMUX registers location and length
+
+  - compatible         : "fsl,imx21-audmux" for AUDMUX version firstly used
+                         on i.MX21, or "fsl,imx31-audmux" for the version
+                         firstly used on i.MX31.
+
+  - reg                        : Should contain AUDMUX registers location and length.
 
 An initial configuration can be setup using child nodes.
 
 Required properties of optional child nodes:
-- fsl,audmux-port : Integer of the audmux port that is configured by this
-  child node.
-- fsl,port-config : List of configuration options for the specific port. For
-  imx31-audmux and above, it is a list of tuples <ptcr pdcr>. For
-  imx21-audmux it is a list of pcr values.
+
+  - fsl,audmux-port    : Integer of the audmux port that is configured by this
+                         child node.
+
+  - fsl,port-config    : List of configuration options for the specific port.
+                         For imx31-audmux and above, it is a list of tuples
+                         <ptcr pdcr>. For imx21-audmux it is a list of pcr
+                         values.
 
 Example:
 
index c454e67..aa802a2 100644 (file)
@@ -16,6 +16,8 @@ Optional properties:
 
 - clock-names: Should be "mclk"
 
+- maxim,dmic-freq: Frequency at which to clock DMIC
+
 Pins on the device (for linking into audio routes):
 
   * MIC1
index aa697ab..2dd690b 100644 (file)
@@ -1,8 +1,12 @@
 Renesas R-Car sound
 
 Required properties:
-- compatible                   : "renesas,rcar_sound-gen1" if generation1
+- compatible                   : "renesas,rcar_sound-<soctype>", fallbacks
+                                 "renesas,rcar_sound-gen1" if generation1, and
                                  "renesas,rcar_sound-gen2" if generation2
+                                 Examples with soctypes are:
+                                   - "renesas,rcar_sound-r8a7790" (R-Car H2)
+                                   - "renesas,rcar_sound-r8a7791" (R-Car M2-W)
 - reg                          : Should contain the register physical address.
                                  required register is
                                   SRU/ADG/SSI      if generation1
@@ -35,9 +39,9 @@ DAI subnode properties:
 
 Example:
 
-rcar_sound: rcar_sound@0xffd90000 {
+rcar_sound: rcar_sound@ec500000 {
        #sound-dai-cells = <1>;
-       compatible = "renesas,rcar_sound-gen2";
+       compatible = "renesas,rcar_sound-r8a7791", "renesas,rcar_sound-gen2";
        reg =   <0 0xec500000 0 0x1000>, /* SCU */
                <0 0xec5a0000 0 0x100>,  /* ADG */
                <0 0xec540000 0 0x1000>, /* SSIU */
diff --git a/Documentation/devicetree/bindings/sound/rt5631.txt b/Documentation/devicetree/bindings/sound/rt5631.txt
new file mode 100644 (file)
index 0000000..92b986c
--- /dev/null
@@ -0,0 +1,48 @@
+ALC5631/RT5631 audio CODEC
+
+This device supports I2C only.
+
+Required properties:
+
+  - compatible : "realtek,alc5631" or "realtek,rt5631"
+
+  - reg : the I2C address of the device.
+
+Pins on the device (for linking into audio routes):
+
+  * SPK_OUT_R_P
+  * SPK_OUT_R_N
+  * SPK_OUT_L_P
+  * SPK_OUT_L_N
+  * HP_OUT_L
+  * HP_OUT_R
+  * AUX_OUT2_LP
+  * AUX_OUT2_RN
+  * AUX_OUT1_LP
+  * AUX_OUT1_RN
+  * AUX_IN_L_JD
+  * AUX_IN_R_JD
+  * MONO_IN_P
+  * MONO_IN_N
+  * MIC1_P
+  * MIC1_N
+  * MIC2_P
+  * MIC2_N
+  * MONO_OUT_P
+  * MONO_OUT_N
+  * MICBIAS1
+  * MICBIAS2
+
+Example:
+
+alc5631: alc5631@1a {
+       compatible = "realtek,alc5631";
+       reg = <0x1a>;
+};
+
+or
+
+rt5631: rt5631@1a {
+       compatible = "realtek,rt5631";
+       reg = <0x1a>;
+};
index 0701b83..740ff77 100644 (file)
@@ -27,6 +27,21 @@ Optional properties:
   Boolean. Indicate MIC1/2 input and LOUT1/2/3 outputs are differential,
   rather than single-ended.
 
+- realtek,gpio-config
+  Array of six 8bit elements that configures GPIO.
+    0 - floating (reset value)
+    1 - pull down
+    2 - pull up
+
+- realtek,jd1-gpio
+  Configures GPIO Mic Jack detection 1.
+  Select 0 ~ 3 as OFF, GPIO1, GPIO2 and GPIO3 respectively.
+
+- realtek,jd2-gpio
+- realtek,jd3-gpio
+  Configures GPIO Mic Jack detection 2 and 3.
+  Select 0 ~ 3 as OFF, GPIO4, GPIO5 and GPIO6 respectively.
+
 Pins on the device (for linking into audio routes):
 
   * IN1P
@@ -56,4 +71,6 @@ rt5677 {
        realtek,pow-ldo2-gpio =
                <&gpio TEGRA_GPIO(V, 3) GPIO_ACTIVE_HIGH>;
        realtek,in1-differential = "true";
+       realtek,gpio-config = /bits/ 8  <0 0 0 0 0 2>;   /* pull up GPIO6 */
+       realtek,jd2-gpio = <3>;  /* Enables Jack detection for GPIO6 */
 };
index 7386d44..d188296 100644 (file)
@@ -6,10 +6,17 @@ Required SoC Specific Properties:
    - samsung,s3c6410-i2s: for 8/16/24bit stereo I2S.
    - samsung,s5pv210-i2s: for 8/16/24bit multichannel(5.1) I2S with
      secondary fifo, s/w reset control and internal mux for root clk src.
-   - samsung,exynos5420-i2s: for 8/16/24bit multichannel(7.1) I2S with
-     secondary fifo, s/w reset control, internal mux for root clk src and
-     TDM support. TDM (Time division multiplexing) is to allow transfer of
-     multiple channel audio data on single data line.
+   - samsung,exynos5420-i2s: for 8/16/24bit multichannel(5.1) I2S for
+     playback, sterio channel capture, secondary fifo using internal
+     or external dma, s/w reset control, internal mux for root clk src
+     and 7.1 channel TDM support for playback. TDM (Time division multiplexing)
+     is to allow transfer of multiple channel audio data on single data line.
+   - samsung,exynos7-i2s: with all the available features of exynos5 i2s,
+     exynos7 I2S has 7.1 channel TDM support for capture, secondary fifo
+     with only external dma and more no.of root clk sampling frequencies.
+   - samsung,exynos7-i2s1: I2S1 on previous samsung platforms supports
+     stereo channels. exynos7 i2s1 upgraded to 5.1 multichannel with
+     slightly modified bit offsets.
 
 - reg: physical base address of the controller and length of memory mapped
   region.
index 0ef00be..43404b1 100644 (file)
@@ -7,7 +7,10 @@ Required properties:
                            - "renesas,thermal-r8a73a4" (R-Mobile AP6)
                            - "renesas,thermal-r8a7779" (R-Car H1)
                            - "renesas,thermal-r8a7790" (R-Car H2)
-                           - "renesas,thermal-r8a7791" (R-Car M2)
+                           - "renesas,thermal-r8a7791" (R-Car M2-W)
+                           - "renesas,thermal-r8a7792" (R-Car V2H)
+                           - "renesas,thermal-r8a7793" (R-Car M2-N)
+                           - "renesas,thermal-r8a7794" (R-Car E2)
 - reg                  : Address range of the thermal registers.
                          The 1st reg will be recognized as common register
                          if it has "interrupts".
index 723999d..a344ec2 100644 (file)
@@ -34,6 +34,7 @@ chipidea      Chipidea, Inc
 chrp   Common Hardware Reference Platform
 chunghwa       Chunghwa Picture Tubes Ltd.
 cirrus Cirrus Logic, Inc.
+cnm    Chips&Media, Inc.
 cortina        Cortina Systems, Inc.
 crystalfontz   Crystalfontz America, Inc.
 dallas Maxim Integrated Products (formerly Dallas Semiconductor)
@@ -92,6 +93,7 @@ maxim Maxim Integrated Products
 mediatek       MediaTek Inc.
 micrel Micrel Inc.
 microchip      Microchip Technology Inc.
+micron Micron Technology Inc.
 mitsubishi     Mitsubishi Electric Corporation
 mosaixtech     Mosaix Technologies, Inc.
 moxa   Moxa
@@ -127,6 +129,7 @@ renesas     Renesas Electronics Corporation
 ricoh  Ricoh Co. Ltd.
 rockchip       Fuzhou Rockchip Electronics Co., Ltd
 samsung        Samsung Semiconductor
+sandisk        Sandisk Corporation
 sbs    Smart Battery System
 schindler      Schindler
 seagate        Seagate Technology PLC
@@ -138,7 +141,7 @@ silergy     Silergy Corp.
 sirf   SiRF Technology, Inc.
 sitronix       Sitronix Technology Corporation
 smsc   Standard Microsystems Corporation
-snps   Synopsys, Inc.
+snps   Synopsys, Inc.
 solidrun       SolidRun
 sony   Sony Corporation
 spansion       Spansion Inc.
index 530850a..a27c950 100644 (file)
@@ -64,7 +64,7 @@ is formed.
 At mount time, the two directories given as mount options "lowerdir" and
 "upperdir" are combined into a merged directory:
 
-  mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper,\
+  mount -t overlay overlay -olowerdir=/lower,upperdir=/upper,\
 workdir=/work /merged
 
 The "workdir" needs to be an empty directory on the same filesystem
index e1ae127..1ec0db7 100644 (file)
@@ -38,22 +38,38 @@ Contents
         7.2.1 Status packet
         7.2.2 Head packet
         7.2.3 Motion packet
+ 8. Trackpoint (for Hardware version 3 and 4)
+    8.1 Registers
+    8.2 Native relative mode 6 byte packet format
+        8.2.1 Status Packet
 
 
 
 1. Introduction
    ~~~~~~~~~~~~
 
-Currently the Linux Elantech touchpad driver is aware of two different
-hardware versions unimaginatively called version 1 and version 2. Version 1
-is found in "older" laptops and uses 4 bytes per packet. Version 2 seems to
-be introduced with the EeePC and uses 6 bytes per packet, and provides
-additional features such as position of two fingers, and width of the touch.
+Currently the Linux Elantech touchpad driver is aware of four different
+hardware versions unimaginatively called version 1,version 2, version 3
+and version 4. Version 1 is found in "older" laptops and uses 4 bytes per
+packet. Version 2 seems to be introduced with the EeePC and uses 6 bytes
+per packet, and provides additional features such as position of two fingers,
+and width of the touch.  Hardware version 3 uses 6 bytes per packet (and
+for 2 fingers the concatenation of two 6 bytes packets) and allows tracking
+of up to 3 fingers. Hardware version 4 uses 6 bytes per packet, and can
+combine a status packet with multiple head or motion packets. Hardware version
+4 allows tracking up to 5 fingers.
+
+Some Hardware version 3 and version 4 also have a trackpoint which uses a
+separate packet format. It is also 6 bytes per packet.
 
 The driver tries to support both hardware versions and should be compatible
 with the Xorg Synaptics touchpad driver and its graphical configuration
 utilities.
 
+Note that a mouse button is also associated with either the touchpad or the
+trackpoint when a trackpoint is available.  Disabling the Touchpad in xorg
+(TouchPadOff=0) will also disable the buttons associated with the touchpad.
+
 Additionally the operation of the touchpad can be altered by adjusting the
 contents of some of its internal registers. These registers are represented
 by the driver as sysfs entries under /sys/bus/serio/drivers/psmouse/serio?
@@ -78,7 +94,7 @@ completeness sake.
 2. Extra knobs
    ~~~~~~~~~~~
 
-Currently the Linux Elantech touchpad driver provides two extra knobs under
+Currently the Linux Elantech touchpad driver provides three extra knobs under
 /sys/bus/serio/drivers/psmouse/serio? for the user.
 
 * debug
@@ -112,6 +128,20 @@ Currently the Linux Elantech touchpad driver provides two extra knobs under
    data consistency checking can be done. For now checking is disabled by
    default. Currently even turning it on will do nothing.
 
+* crc_enabled
+
+   Sets crc_enabled to 0/1. The name "crc_enabled" is the official name of
+   this integrity check, even though it is not an actual cyclic redundancy
+   check.
+
+   Depending on the state of crc_enabled, certain basic data integrity
+   verification is done by the driver on hardware version 3 and 4. The
+   driver will reject any packet that appears corrupted. Using this knob,
+   The state of crc_enabled can be altered with this knob.
+
+   Reading the crc_enabled value will show the active value. Echoing
+   "0" or "1" to this file will set the state to "0" or "1".
+
 /////////////////////////////////////////////////////////////////////////////
 
 3. Differentiating hardware versions
@@ -746,3 +776,42 @@ byte 5:
 
         byte 0 ~ 2 for one finger
         byte 3 ~ 5 for another
+
+
+8. Trackpoint (for Hardware version 3 and 4)
+   =========================================
+8.1 Registers
+    ~~~~~~~~~
+No special registers have been identified.
+
+8.2 Native relative mode 6 byte packet format
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+8.2.1 Status Packet
+      ~~~~~~~~~~~~~
+
+byte 0:
+   bit   7   6   5   4   3   2   1   0
+         0   0  sx  sy   0   M   R   L
+byte 1:
+   bit   7   6   5   4   3   2   1   0
+       ~sx   0   0   0   0   0   0   0
+byte 2:
+   bit   7   6   5   4   3   2   1   0
+       ~sy   0   0   0   0   0   0   0
+byte 3:
+   bit   7   6   5   4   3   2   1   0
+         0   0 ~sy ~sx   0   1   1   0
+byte 4:
+   bit   7   6   5   4   3   2   1   0
+        x7  x6  x5  x4  x3  x2  x1  x0
+byte 5:
+   bit   7   6   5   4   3   2   1   0
+        y7  y6  y5  y4  y3  y2  y1  y0
+
+
+         x and y are written in two's complement spread
+             over 9 bits with sx/sy the relative top bit and
+             x7..x0 and y7..y0 the lower bits.
+        ~sx is the inverse of sx, ~sy is the inverse of sy.
+         The sign of y is opposite to what the input driver
+             expects for a relative movement
index 4c81a86..479f332 100644 (file)
@@ -3621,7 +3621,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
 
        usb-storage.delay_use=
                        [UMS] The delay in seconds before a new device is
-                       scanned for Logical Units (default 5).
+                       scanned for Logical Units (default 1).
 
        usb-storage.quirks=
                        [UMS] A list of quirks entries to supplement or
index 0307e28..a476b08 100644 (file)
@@ -56,6 +56,13 @@ ip_forward_use_pmtu - BOOLEAN
        0 - disabled
        1 - enabled
 
+fwmark_reflect - BOOLEAN
+       Controls the fwmark of kernel-generated IPv4 reply packets that are not
+       associated with a socket for example, TCP RSTs or ICMP echo replies).
+       If unset, these packets have a fwmark of zero. If set, they have the
+       fwmark of the packet they are replying to.
+       Default: 0
+
 route/max_size - INTEGER
        Maximum number of routes allowed in the kernel.  Increase
        this when using large numbers of interfaces and/or routes.
@@ -1201,6 +1208,13 @@ conf/all/forwarding - BOOLEAN
 proxy_ndp - BOOLEAN
        Do proxy ndp.
 
+fwmark_reflect - BOOLEAN
+       Controls the fwmark of kernel-generated IPv6 reply packets that are not
+       associated with a socket for example, TCP RSTs or ICMPv6 echo replies).
+       If unset, these packets have a fwmark of zero. If set, they have the
+       fwmark of the packet they are replying to.
+       Default: 0
+
 conf/interface/*:
        Change special settings per interface.
 
index 412f45c..1d6d02d 100644 (file)
@@ -136,7 +136,7 @@ SOF_TIMESTAMPING_OPT_ID:
 
   This option is implemented only for transmit timestamps. There, the
   timestamp is always looped along with a struct sock_extended_err.
-  The option modifies field ee_info to pass an id that is unique
+  The option modifies field ee_data to pass an id that is unique
   among all possibly concurrently outstanding timestamp requests for
   that socket. In practice, it is a monotonically increasing u32
   (that wraps).
index eeb11a2..e5a940e 100644 (file)
@@ -221,12 +221,11 @@ ccs_out_mode: specify the allowed video output crop/compose/scaling combination
                       key, not quality.
 
 multiplanar: select whether each device instance supports multi-planar formats,
-       and thus the V4L2 multi-planar API. By default the first device instance
-       is single-planar, the second multi-planar, and it keeps alternating.
+       and thus the V4L2 multi-planar API. By default device instances are
+       single-planar.
 
        This module option can override that for each instance. Values are:
 
-               0: use alternating single and multi-planar devices.
                1: this is a single-planar instance.
                2: this is a multi-planar instance.
 
@@ -975,9 +974,8 @@ is set, then the alpha component is only used for the color red and set to
 0 otherwise.
 
 The driver has to be configured to support the multiplanar formats. By default
-the first driver instance is single-planar, the second is multi-planar, and it
-keeps alternating. This can be changed by setting the multiplanar module option,
-see section 1 for more details on that option.
+the driver instances are single-planar. This can be changed by setting the
+multiplanar module option, see section 1 for more details on that option.
 
 If the driver instance is using the multiplanar formats/API, then the first
 single planar format (YUYV) and the multiplanar NV16M and NV61M formats the
@@ -1021,7 +1019,7 @@ the output overlay for the video output, turn on video looping and capture
 to see the blended framebuffer overlay that's being written to by the second
 instance. This setup would require the following commands:
 
-       $ sudo modprobe vivid n_devs=2 node_types=0x10101,0x1 multiplanar=1,1
+       $ sudo modprobe vivid n_devs=2 node_types=0x10101,0x1
        $ v4l2-ctl -d1 --find-fb
        /dev/fb1 is the framebuffer associated with base address 0x12800000
        $ sudo v4l2-ctl -d2 --set-fbuf fb=1
index 3c64271..c721042 100644 (file)
@@ -1543,6 +1543,7 @@ F:        arch/arm/mach-pxa/include/mach/z2.h
 
 ARM/ZYNQ ARCHITECTURE
 M:     Michal Simek <michal.simek@xilinx.com>
+R:     Sören Brinkmann <soren.brinkmann@xilinx.com>
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 W:     http://wiki.xilinx.com
 T:     git git://git.xilinx.com/linux-xlnx.git
@@ -1827,7 +1828,7 @@ F:        include/net/ax25.h
 F:     net/ax25/
 
 AZ6007 DVB DRIVER
-M:     Mauro Carvalho Chehab <m.chehab@samsung.com>
+M:     Mauro Carvalho Chehab <mchehab@osg.samsung.com>
 L:     linux-media@vger.kernel.org
 W:     http://linuxtv.org
 T:     git git://linuxtv.org/media_tree.git
@@ -2071,8 +2072,9 @@ F:        drivers/clocksource/bcm_kona_timer.c
 
 BROADCOM BCM2835 ARM ARCHITECTURE
 M:     Stephen Warren <swarren@wwwdotorg.org>
+M:     Lee Jones <lee@kernel.org>
 L:     linux-rpi-kernel@lists.infradead.org (moderated for non-subscribers)
-T:     git git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-rpi.git
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/rpi/linux-rpi.git
 S:     Maintained
 N:     bcm2835
 
@@ -2196,7 +2198,7 @@ F:        Documentation/filesystems/btrfs.txt
 F:     fs/btrfs/
 
 BTTV VIDEO4LINUX DRIVER
-M:     Mauro Carvalho Chehab <m.chehab@samsung.com>
+M:     Mauro Carvalho Chehab <mchehab@osg.samsung.com>
 L:     linux-media@vger.kernel.org
 W:     http://linuxtv.org
 T:     git git://linuxtv.org/media_tree.git
@@ -2717,7 +2719,7 @@ F:        drivers/media/common/cx2341x*
 F:     include/media/cx2341x*
 
 CX88 VIDEO4LINUX DRIVER
-M:     Mauro Carvalho Chehab <m.chehab@samsung.com>
+M:     Mauro Carvalho Chehab <mchehab@osg.samsung.com>
 L:     linux-media@vger.kernel.org
 W:     http://linuxtv.org
 T:     git git://linuxtv.org/media_tree.git
@@ -2742,6 +2744,13 @@ W:       http://www.chelsio.com
 S:     Supported
 F:     drivers/net/ethernet/chelsio/cxgb3/
 
+CXGB3 ISCSI DRIVER (CXGB3I)
+M:      Karen Xie <kxie@chelsio.com>
+L:      linux-scsi@vger.kernel.org
+W:      http://www.chelsio.com
+S:      Supported
+F:      drivers/scsi/cxgbi/cxgb3i
+
 CXGB3 IWARP RNIC DRIVER (IW_CXGB3)
 M:     Steve Wise <swise@chelsio.com>
 L:     linux-rdma@vger.kernel.org
@@ -2756,6 +2765,13 @@ W:       http://www.chelsio.com
 S:     Supported
 F:     drivers/net/ethernet/chelsio/cxgb4/
 
+CXGB4 ISCSI DRIVER (CXGB4I)
+M:      Karen Xie <kxie@chelsio.com>
+L:      linux-scsi@vger.kernel.org
+W:      http://www.chelsio.com
+S:      Supported
+F:      drivers/scsi/cxgbi/cxgb4i
+
 CXGB4 IWARP RNIC DRIVER (IW_CXGB4)
 M:     Steve Wise <swise@chelsio.com>
 L:     linux-rdma@vger.kernel.org
@@ -3386,7 +3402,7 @@ F:        fs/ecryptfs/
 EDAC-CORE
 M:     Doug Thompson <dougthompson@xmission.com>
 M:     Borislav Petkov <bp@alien8.de>
-M:     Mauro Carvalho Chehab <m.chehab@samsung.com>
+M:     Mauro Carvalho Chehab <mchehab@osg.samsung.com>
 L:     linux-edac@vger.kernel.org
 W:     bluesmoke.sourceforge.net
 S:     Supported
@@ -3435,7 +3451,7 @@ S:        Maintained
 F:     drivers/edac/e7xxx_edac.c
 
 EDAC-GHES
-M:     Mauro Carvalho Chehab <m.chehab@samsung.com>
+M:     Mauro Carvalho Chehab <mchehab@osg.samsung.com>
 L:     linux-edac@vger.kernel.org
 W:     bluesmoke.sourceforge.net
 S:     Maintained
@@ -3463,21 +3479,21 @@ S:      Maintained
 F:     drivers/edac/i5000_edac.c
 
 EDAC-I5400
-M:     Mauro Carvalho Chehab <m.chehab@samsung.com>
+M:     Mauro Carvalho Chehab <mchehab@osg.samsung.com>
 L:     linux-edac@vger.kernel.org
 W:     bluesmoke.sourceforge.net
 S:     Maintained
 F:     drivers/edac/i5400_edac.c
 
 EDAC-I7300
-M:     Mauro Carvalho Chehab <m.chehab@samsung.com>
+M:     Mauro Carvalho Chehab <mchehab@osg.samsung.com>
 L:     linux-edac@vger.kernel.org
 W:     bluesmoke.sourceforge.net
 S:     Maintained
 F:     drivers/edac/i7300_edac.c
 
 EDAC-I7CORE
-M:     Mauro Carvalho Chehab <m.chehab@samsung.com>
+M:     Mauro Carvalho Chehab <mchehab@osg.samsung.com>
 L:     linux-edac@vger.kernel.org
 W:     bluesmoke.sourceforge.net
 S:     Maintained
@@ -3520,7 +3536,7 @@ S:        Maintained
 F:     drivers/edac/r82600_edac.c
 
 EDAC-SBRIDGE
-M:     Mauro Carvalho Chehab <m.chehab@samsung.com>
+M:     Mauro Carvalho Chehab <mchehab@osg.samsung.com>
 L:     linux-edac@vger.kernel.org
 W:     bluesmoke.sourceforge.net
 S:     Maintained
@@ -3580,7 +3596,7 @@ S:        Maintained
 F:     drivers/net/ethernet/ibm/ehea/
 
 EM28XX VIDEO4LINUX DRIVER
-M:     Mauro Carvalho Chehab <m.chehab@samsung.com>
+M:     Mauro Carvalho Chehab <mchehab@osg.samsung.com>
 L:     linux-media@vger.kernel.org
 W:     http://linuxtv.org
 T:     git git://linuxtv.org/media_tree.git
@@ -4714,6 +4730,7 @@ L:        linux-iio@vger.kernel.org
 S:     Maintained
 F:     drivers/iio/
 F:     drivers/staging/iio/
+F:     include/linux/iio/
 
 IKANOS/ADI EAGLE ADSL USB DRIVER
 M:     Matthieu Castet <castet.matthieu@free.fr>
@@ -5945,7 +5962,7 @@ S:        Maintained
 F:     drivers/media/radio/radio-maxiradio*
 
 MEDIA INPUT INFRASTRUCTURE (V4L/DVB)
-M:     Mauro Carvalho Chehab <m.chehab@samsung.com>
+M:     Mauro Carvalho Chehab <mchehab@osg.samsung.com>
 P:     LinuxTV.org Project
 L:     linux-media@vger.kernel.org
 W:     http://linuxtv.org
@@ -6594,6 +6611,23 @@ T:       git git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap.git
 S:     Maintained
 F:     arch/arm/*omap*/
 F:     drivers/i2c/busses/i2c-omap.c
+F:     drivers/irqchip/irq-omap-intc.c
+F:     drivers/mfd/*omap*.c
+F:     drivers/mfd/menelaus.c
+F:     drivers/mfd/palmas.c
+F:     drivers/mfd/tps65217.c
+F:     drivers/mfd/tps65218.c
+F:     drivers/mfd/tps65910.c
+F:     drivers/mfd/twl-core.[ch]
+F:     drivers/mfd/twl4030*.c
+F:     drivers/mfd/twl6030*.c
+F:     drivers/mfd/twl6040*.c
+F:     drivers/regulator/palmas-regulator*.c
+F:     drivers/regulator/pbias-regulator.c
+F:     drivers/regulator/tps65217-regulator.c
+F:     drivers/regulator/tps65218-regulator.c
+F:     drivers/regulator/tps65910-regulator.c
+F:     drivers/regulator/twl-regulator.c
 F:     include/linux/i2c-omap.h
 
 OMAP DEVICE TREE SUPPORT
@@ -6604,6 +6638,9 @@ L:        devicetree@vger.kernel.org
 S:     Maintained
 F:     arch/arm/boot/dts/*omap*
 F:     arch/arm/boot/dts/*am3*
+F:     arch/arm/boot/dts/*am4*
+F:     arch/arm/boot/dts/*am5*
+F:     arch/arm/boot/dts/*dra7*
 
 OMAP CLOCK FRAMEWORK SUPPORT
 M:     Paul Walmsley <paul@pwsan.com>
@@ -6851,11 +6888,12 @@ F:      drivers/scsi/osd/
 F:     include/scsi/osd_*
 F:     fs/exofs/
 
-OVERLAYFS FILESYSTEM
+OVERLAY FILESYSTEM
 M:     Miklos Szeredi <miklos@szeredi.hu>
-L:     linux-fsdevel@vger.kernel.org
+L:     linux-unionfs@vger.kernel.org
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs.git
 S:     Supported
-F:     fs/overlayfs/*
+F:     fs/overlayfs/
 F:     Documentation/filesystems/overlayfs.txt
 
 P54 WIRELESS DRIVER
@@ -7179,6 +7217,7 @@ F:        drivers/crypto/picoxcell*
 
 PIN CONTROL SUBSYSTEM
 M:     Linus Walleij <linus.walleij@linaro.org>
+L:     linux-gpio@vger.kernel.org
 S:     Maintained
 F:     drivers/pinctrl/
 F:     include/linux/pinctrl/
@@ -7974,7 +8013,7 @@ S:        Odd Fixes
 F:     drivers/media/i2c/saa6588*
 
 SAA7134 VIDEO4LINUX DRIVER
-M:     Mauro Carvalho Chehab <m.chehab@samsung.com>
+M:     Mauro Carvalho Chehab <mchehab@osg.samsung.com>
 L:     linux-media@vger.kernel.org
 W:     http://linuxtv.org
 T:     git git://linuxtv.org/media_tree.git
@@ -8432,7 +8471,7 @@ S:        Maintained
 F:     drivers/media/radio/si4713/radio-usb-si4713.c
 
 SIANO DVB DRIVER
-M:     Mauro Carvalho Chehab <m.chehab@samsung.com>
+M:     Mauro Carvalho Chehab <mchehab@osg.samsung.com>
 L:     linux-media@vger.kernel.org
 W:     http://linuxtv.org
 T:     git git://linuxtv.org/media_tree.git
@@ -8483,7 +8522,6 @@ F:        arch/arm/mach-s3c24xx/bast-irq.c
 TI DAVINCI MACHINE SUPPORT
 M:     Sekhar Nori <nsekhar@ti.com>
 M:     Kevin Hilman <khilman@deeprootsystems.com>
-L:     davinci-linux-open-source@linux.davincidsp.com (moderated for non-subscribers)
 T:     git git://gitorious.org/linux-davinci/linux-davinci.git
 Q:     http://patchwork.kernel.org/project/linux-davinci/list/
 S:     Supported
@@ -8493,7 +8531,6 @@ F:        drivers/i2c/busses/i2c-davinci.c
 TI DAVINCI SERIES MEDIA DRIVER
 M:     Lad, Prabhakar <prabhakar.csengg@gmail.com>
 L:     linux-media@vger.kernel.org
-L:     davinci-linux-open-source@linux.davincidsp.com (moderated for non-subscribers)
 W:     http://linuxtv.org/
 Q:     http://patchwork.linuxtv.org/project/linux-media/list/
 T:     git git://linuxtv.org/mhadli/v4l-dvb-davinci_devices.git
@@ -8645,7 +8682,9 @@ S:        Maintained
 F:     drivers/leds/leds-net48xx.c
 
 SOFTLOGIC 6x10 MPEG CODEC
-M:     Ismael Luceno <ismael.luceno@corp.bluecherry.net>
+M:     Bluecherry Maintainers <maintainers@bluecherrydvr.com>
+M:     Andrey Utkin <andrey.utkin@corp.bluecherry.net>
+M:     Andrey Utkin <andrey.krieger.utkin@gmail.com>
 L:     linux-media@vger.kernel.org
 S:     Supported
 F:     drivers/media/pci/solo6x10/
@@ -9119,7 +9158,7 @@ S:        Maintained
 F:     drivers/media/i2c/tda9840*
 
 TEA5761 TUNER DRIVER
-M:     Mauro Carvalho Chehab <m.chehab@samsung.com>
+M:     Mauro Carvalho Chehab <mchehab@osg.samsung.com>
 L:     linux-media@vger.kernel.org
 W:     http://linuxtv.org
 T:     git git://linuxtv.org/media_tree.git
@@ -9127,7 +9166,7 @@ S:        Odd fixes
 F:     drivers/media/tuners/tea5761.*
 
 TEA5767 TUNER DRIVER
-M:     Mauro Carvalho Chehab <m.chehab@samsung.com>
+M:     Mauro Carvalho Chehab <mchehab@osg.samsung.com>
 L:     linux-media@vger.kernel.org
 W:     http://linuxtv.org
 T:     git git://linuxtv.org/media_tree.git
@@ -9439,7 +9478,7 @@ F:        include/linux/shmem_fs.h
 F:     mm/shmem.c
 
 TM6000 VIDEO4LINUX DRIVER
-M:     Mauro Carvalho Chehab <m.chehab@samsung.com>
+M:     Mauro Carvalho Chehab <mchehab@osg.samsung.com>
 L:     linux-media@vger.kernel.org
 W:     http://linuxtv.org
 T:     git git://linuxtv.org/media_tree.git
@@ -9703,11 +9742,6 @@ S:       Maintained
 F:     Documentation/hid/hiddev.txt
 F:     drivers/hid/usbhid/
 
-USB/IP DRIVERS
-L:     linux-usb@vger.kernel.org
-S:     Orphan
-F:     drivers/staging/usbip/
-
 USB ISP116X DRIVER
 M:     Olav Kongas <ok@artecdesign.ee>
 L:     linux-usb@vger.kernel.org
@@ -10265,7 +10299,7 @@ S:      Maintained
 F:     arch/x86/kernel/cpu/mcheck/*
 
 XC2028/3028 TUNER DRIVER
-M:     Mauro Carvalho Chehab <m.chehab@samsung.com>
+M:     Mauro Carvalho Chehab <mchehab@osg.samsung.com>
 L:     linux-media@vger.kernel.org
 W:     http://linuxtv.org
 T:     git git://linuxtv.org/media_tree.git
index ffc1ce2..fd80c6e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 3
 PATCHLEVEL = 18
 SUBLEVEL = 0
-EXTRAVERSION = -rc3
+EXTRAVERSION =
 NAME = Diseased Newt
 
 # *DOCUMENTATION*
@@ -297,7 +297,7 @@ CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
 
 HOSTCC       = gcc
 HOSTCXX      = g++
-HOSTCFLAGS   = -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer
+HOSTCFLAGS   = -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu89
 HOSTCXXFLAGS = -O2
 
 ifeq ($(shell $(HOSTCC) -v 2>&1 | grep -c "clang version"), 1)
@@ -401,7 +401,8 @@ KBUILD_CPPFLAGS := -D__KERNEL__
 KBUILD_CFLAGS   := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
                   -fno-strict-aliasing -fno-common \
                   -Werror-implicit-function-declaration \
-                  -Wno-format-security
+                  -Wno-format-security \
+                  -std=gnu89
 
 KBUILD_AFLAGS_KERNEL :=
 KBUILD_CFLAGS_KERNEL :=
index 03dc4c1..d8f6a2e 100644 (file)
@@ -1187,7 +1187,7 @@ config DEBUG_UART_VIRT
        default 0xf1c28000 if DEBUG_SUNXI_UART0
        default 0xf1c28400 if DEBUG_SUNXI_UART1
        default 0xf1f02800 if DEBUG_SUNXI_R_UART
-       default 0xf2100000 if DEBUG_PXA_UART1
+       default 0xf6200000 if DEBUG_PXA_UART1
        default 0xf4090000 if ARCH_LPC32XX
        default 0xf4200000 if ARCH_GEMINI
        default 0xf7000000 if DEBUG_S3C24XX_UART && (DEBUG_S3C_UART0 || \
index 413fd94..68be901 100644 (file)
@@ -397,8 +397,7 @@ dtb_check_done:
                add     sp, sp, r6
 #endif
 
-               tst     r4, #1
-               bleq    cache_clean_flush
+               bl      cache_clean_flush
 
                adr     r0, BSYM(restart)
                add     r0, r0, r6
@@ -1047,6 +1046,8 @@ cache_clean_flush:
                b       call_cache_fn
 
 __armv4_mpu_cache_flush:
+               tst     r4, #1
+               movne   pc, lr
                mov     r2, #1
                mov     r3, #0
                mcr     p15, 0, ip, c7, c6, 0   @ invalidate D cache
@@ -1064,6 +1065,8 @@ __armv4_mpu_cache_flush:
                mov     pc, lr
                
 __fa526_cache_flush:
+               tst     r4, #1
+               movne   pc, lr
                mov     r1, #0
                mcr     p15, 0, r1, c7, c14, 0  @ clean and invalidate D cache
                mcr     p15, 0, r1, c7, c5, 0   @ flush I cache
@@ -1072,13 +1075,16 @@ __fa526_cache_flush:
 
 __armv6_mmu_cache_flush:
                mov     r1, #0
-               mcr     p15, 0, r1, c7, c14, 0  @ clean+invalidate D
+               tst     r4, #1
+               mcreq   p15, 0, r1, c7, c14, 0  @ clean+invalidate D
                mcr     p15, 0, r1, c7, c5, 0   @ invalidate I+BTB
-               mcr     p15, 0, r1, c7, c15, 0  @ clean+invalidate unified
+               mcreq   p15, 0, r1, c7, c15, 0  @ clean+invalidate unified
                mcr     p15, 0, r1, c7, c10, 4  @ drain WB
                mov     pc, lr
 
 __armv7_mmu_cache_flush:
+               tst     r4, #1
+               bne     iflush
                mrc     p15, 0, r10, c0, c1, 5  @ read ID_MMFR1
                tst     r10, #0xf << 16         @ hierarchical cache (ARMv7)
                mov     r10, #0
@@ -1139,6 +1145,8 @@ iflush:
                mov     pc, lr
 
 __armv5tej_mmu_cache_flush:
+               tst     r4, #1
+               movne   pc, lr
 1:             mrc     p15, 0, r15, c7, c14, 3 @ test,clean,invalidate D cache
                bne     1b
                mcr     p15, 0, r0, c7, c5, 0   @ flush I cache
@@ -1146,6 +1154,8 @@ __armv5tej_mmu_cache_flush:
                mov     pc, lr
 
 __armv4_mmu_cache_flush:
+               tst     r4, #1
+               movne   pc, lr
                mov     r2, #64*1024            @ default: 32K dcache size (*2)
                mov     r11, #32                @ default: 32 byte line size
                mrc     p15, 0, r3, c0, c0, 1   @ read cache type
@@ -1179,6 +1189,8 @@ no_cache_id:
 
 __armv3_mmu_cache_flush:
 __armv3_mpu_cache_flush:
+               tst     r4, #1
+               movne   pc, lr
                mov     r1, #0
                mcr     p15, 0, r1, c7, c0, 0   @ invalidate whole cache v3
                mov     pc, lr
index e2156a5..c4b968f 100644 (file)
                        reg = <0x00060000 0x00020000>;
                };
                partition@4 {
-                       label = "NAND.u-boot-spl";
+                       label = "NAND.u-boot-spl-os";
                        reg = <0x00080000 0x00040000>;
                };
                partition@5 {
index e7ac47f..a521ac0 100644 (file)
                dcdc3: regulator-dcdc3 {
                        compatible = "ti,tps65218-dcdc3";
                        regulator-name = "vdcdc3";
-                       regulator-min-microvolt = <1350000>;
-                       regulator-max-microvolt = <1350000>;
+                       regulator-min-microvolt = <1500000>;
+                       regulator-max-microvolt = <1500000>;
                        regulator-boot-on;
                        regulator-always-on;
                };
index 859ff3d..87aa4f3 100644 (file)
                dcdc3: regulator-dcdc3 {
                        compatible = "ti,tps65218-dcdc3";
                        regulator-name = "vdds_ddr";
-                       regulator-min-microvolt = <1350000>;
-                       regulator-max-microvolt = <1350000>;
+                       regulator-min-microvolt = <1500000>;
+                       regulator-max-microvolt = <1500000>;
                        regulator-boot-on;
                        regulator-always-on;
                };
index ac3e485..f7e9bba 100644 (file)
                dcdc3: regulator-dcdc3 {
                        compatible = "ti,tps65218-dcdc3";
                        regulator-name = "vdcdc3";
-                       regulator-min-microvolt = <1350000>;
-                       regulator-max-microvolt = <1350000>;
+                       regulator-min-microvolt = <1500000>;
+                       regulator-max-microvolt = <1500000>;
                        regulator-boot-on;
                        regulator-always-on;
                };
index e51fcef..60429ad 100644 (file)
        num-cs = <1>;
 };
 
+&usbdrd_dwc3 {
+       dr_mode = "host";
+};
+
 #include "cros-ec-keyboard.dtsi"
index f21b9aa..d55c1a2 100644 (file)
                #size-cells = <1>;
                ranges;
 
-               dwc3 {
+               usbdrd_dwc3: dwc3 {
                        compatible = "synopsys,dwc3";
                        reg = <0x12000000 0x10000>;
                        interrupts = <0 72 0>;
index d46c213..eed697a 100644 (file)
                        clocks = <&cpg_clocks R8A7740_CLK_S>,
                                 <&cpg_clocks R8A7740_CLK_S>, <&sub_clk>,
                                 <&cpg_clocks R8A7740_CLK_B>,
-                                <&sub_clk>, <&sub_clk>,
+                                <&cpg_clocks R8A7740_CLK_HPP>, <&sub_clk>,
                                 <&cpg_clocks R8A7740_CLK_B>;
                        #clock-cells = <1>;
                        renesas,clock-indices = <
index d0e1773..e20affe 100644 (file)
                        #clock-cells = <0>;
                        clock-output-names = "sd2";
                };
-               sd3_clk: sd3_clk@e615007c {
+               sd3_clk: sd3_clk@e615026c {
                        compatible = "renesas,r8a7790-div6-clock", "renesas,cpg-div6-clock";
-                       reg = <0 0xe615007c 0 4>;
+                       reg = <0 0xe615026c 0 4>;
                        clocks = <&pll1_div2_clk>;
                        #clock-cells = <0>;
                        clock-output-names = "sd3";
index 7997dc9..883878b 100644 (file)
@@ -12,5 +12,5 @@
 #include "sama5d3_uart.dtsi"
 
 / {
-       compatible = "atmel,samad31", "atmel,sama5d3", "atmel,sama5";
+       compatible = "atmel,sama5d31", "atmel,sama5d3", "atmel,sama5";
 };
index 39f8322..4b4434a 100644 (file)
@@ -10,5 +10,5 @@
 #include "sama5d3_gmac.dtsi"
 
 / {
-       compatible = "atmel,samad33", "atmel,sama5d3", "atmel,sama5";
+       compatible = "atmel,sama5d33", "atmel,sama5d3", "atmel,sama5";
 };
index 89cda2c..aa01573 100644 (file)
@@ -12,5 +12,5 @@
 #include "sama5d3_mci2.dtsi"
 
 / {
-       compatible = "atmel,samad34", "atmel,sama5d3", "atmel,sama5";
+       compatible = "atmel,sama5d34", "atmel,sama5d3", "atmel,sama5";
 };
index d20cd71..16c39f4 100644 (file)
@@ -14,5 +14,5 @@
 #include "sama5d3_tcb1.dtsi"
 
 / {
-       compatible = "atmel,samad35", "atmel,sama5d3", "atmel,sama5";
+       compatible = "atmel,sama5d35", "atmel,sama5d3", "atmel,sama5";
 };
index db58cad..e85139e 100644 (file)
@@ -16,5 +16,5 @@
 #include "sama5d3_uart.dtsi"
 
 / {
-       compatible = "atmel,samad36", "atmel,sama5d3", "atmel,sama5";
+       compatible = "atmel,sama5d36", "atmel,sama5d3", "atmel,sama5";
 };
index 962dc28..cfcd200 100644 (file)
@@ -8,7 +8,7 @@
  */
 
 / {
-       compatible = "atmel,samad3xcm", "atmel,sama5d3", "atmel,sama5";
+       compatible = "atmel,sama5d3xcm", "atmel,sama5d3", "atmel,sama5";
 
        chosen {
                bootargs = "console=ttyS0,115200 rootfstype=ubifs ubi.mtd=5 root=ubi0:rootfs";
index 543f895..2e652e2 100644 (file)
                        clocks = <&ahb1_gates 6>;
                        resets = <&ahb1_rst 6>;
                        #dma-cells = <1>;
+
+                       /* DMA controller requires AHB1 clocked from PLL6 */
+                       assigned-clocks = <&ahb1_mux>;
+                       assigned-clock-parents = <&pll6>;
                };
 
                mmc0: mmc@01c0f000 {
index 5c21d21..8b7aa0d 100644 (file)
@@ -15,6 +15,7 @@
        aliases {
                rtc0 = "/i2c@7000d000/tps65913@58";
                rtc1 = "/rtc@7000e000";
+               serial0 = &uartd;
        };
 
        memory {
index c7c6825..38acf78 100644 (file)
                linux,initrd-end = <0x82800000>;
        };
 
+       aliases {
+               serial0 = &uartd;
+       };
+
        firmware {
                trusted-foundations {
                        compatible = "tlm,trusted-foundations";
                                                regulator-name = "vddio-sdmmc3";
                                                regulator-min-microvolt = <1800000>;
                                                regulator-max-microvolt = <3300000>;
-                                               regulator-always-on;
-                                               regulator-boot-on;
                                        };
 
                                        ldousb {
        sdhci@78000400 {
                status = "okay";
                bus-width = <4>;
-               vmmc-supply = <&vddio_sdmmc3>;
+               vqmmc-supply = <&vddio_sdmmc3>;
                cd-gpios = <&gpio TEGRA_GPIO(V, 2) GPIO_ACTIVE_LOW>;
                power-gpios = <&gpio TEGRA_GPIO(H, 0) GPIO_ACTIVE_HIGH>;
        };
        sdhci@78000600 {
                status = "okay";
                bus-width = <8>;
-               vmmc-supply = <&vdd_1v8>;
                non-removable;
        };
 
index 9636621..f91c2c9 100644 (file)
                linux,initrd-end = <0x82800000>;
        };
 
+       aliases {
+               serial0 = &uartd;
+       };
+
        firmware {
                trusted-foundations {
                        compatible = "tlm,trusted-foundations";
        sdhci@78000600 {
                status = "okay";
                bus-width = <8>;
-               vmmc-supply = <&vdd_1v8>;
                non-removable;
        };
 
index 2ca9c18..222f3b3 100644 (file)
@@ -9,13 +9,6 @@
        compatible = "nvidia,tegra114";
        interrupt-parent = <&gic>;
 
-       aliases {
-               serial0 = &uarta;
-               serial1 = &uartb;
-               serial2 = &uartc;
-               serial3 = &uartd;
-       };
-
        host1x@50000000 {
                compatible = "nvidia,tegra114-host1x", "simple-bus";
                reg = <0x50000000 0x00028000>;
index 029c9a0..51b373f 100644 (file)
@@ -10,6 +10,7 @@
        aliases {
                rtc0 = "/i2c@0,7000d000/pmic@40";
                rtc1 = "/rtc@0,7000e000";
+               serial0 = &uartd;
        };
 
        memory {
index 7d0784c..53181d3 100644 (file)
@@ -10,6 +10,7 @@
        aliases {
                rtc0 = "/i2c@0,7000d000/pmic@40";
                rtc1 = "/rtc@0,7000e000";
+               serial0 = &uarta;
        };
 
        memory {
index 1300885..5c3f781 100644 (file)
@@ -10,6 +10,7 @@
        aliases {
                rtc0 = "/i2c@0,7000d000/pmic@40";
                rtc1 = "/rtc@0,7000e000";
+               serial0 = &uarta;
        };
 
        memory {
index 478c555..df2b06b 100644 (file)
         * the APB DMA based serial driver, the comptible is
         * "nvidia,tegra124-hsuart", "nvidia,tegra30-hsuart".
         */
-       serial@0,70006000 {
+       uarta: serial@0,70006000 {
                compatible = "nvidia,tegra124-uart", "nvidia,tegra20-uart";
                reg = <0x0 0x70006000 0x0 0x40>;
                reg-shift = <2>;
                status = "disabled";
        };
 
-       serial@0,70006040 {
+       uartb: serial@0,70006040 {
                compatible = "nvidia,tegra124-uart", "nvidia,tegra20-uart";
                reg = <0x0 0x70006040 0x0 0x40>;
                reg-shift = <2>;
                status = "disabled";
        };
 
-       serial@0,70006200 {
+       uartc: serial@0,70006200 {
                compatible = "nvidia,tegra124-uart", "nvidia,tegra20-uart";
                reg = <0x0 0x70006200 0x0 0x40>;
                reg-shift = <2>;
                status = "disabled";
        };
 
-       serial@0,70006300 {
+       uartd: serial@0,70006300 {
                compatible = "nvidia,tegra124-uart", "nvidia,tegra20-uart";
                reg = <0x0 0x70006300 0x0 0x40>;
                reg-shift = <2>;
index a37279a..b926a07 100644 (file)
@@ -10,6 +10,7 @@
        aliases {
                rtc0 = "/i2c@7000d000/tps6586x@34";
                rtc1 = "/rtc@7000e000";
+               serial0 = &uartd;
        };
 
        memory {
index 8cfb83f..1dd7d7b 100644 (file)
@@ -6,6 +6,11 @@
        model = "Toradex Colibri T20 512MB on Iris";
        compatible = "toradex,iris", "toradex,colibri_t20-512", "nvidia,tegra20";
 
+       aliases {
+               serial0 = &uarta;
+               serial1 = &uartd;
+       };
+
        host1x@50000000 {
                hdmi@54280000 {
                        status = "okay";
index 1b7c56b..9b87526 100644 (file)
@@ -6,6 +6,10 @@
        model = "Avionic Design Medcom-Wide board";
        compatible = "ad,medcom-wide", "ad,tamonten", "nvidia,tegra20";
 
+       aliases {
+               serial0 = &uartd;
+       };
+
        pwm@7000a000 {
                status = "okay";
        };
index d4438e3..ed7e100 100644 (file)
@@ -10,6 +10,8 @@
        aliases {
                rtc0 = "/i2c@7000d000/tps6586x@34";
                rtc1 = "/rtc@7000e000";
+               serial0 = &uarta;
+               serial1 = &uartc;
        };
 
        memory {
index a1d4bf9..ea282c7 100644 (file)
@@ -10,6 +10,7 @@
        aliases {
                rtc0 = "/i2c@7000d000/tps6586x@34";
                rtc1 = "/rtc@7000e000";
+               serial0 = &uartd;
        };
 
        memory {
index 80e7d38..13d4e61 100644 (file)
@@ -7,6 +7,7 @@
        aliases {
                rtc0 = "/i2c@7000d000/tps6586x@34";
                rtc1 = "/rtc@7000e000";
+               serial0 = &uartd;
        };
 
        memory {
index 5ad8797..d99af4e 100644 (file)
@@ -10,6 +10,7 @@
        aliases {
                rtc0 = "/i2c@7000c500/rtc@56";
                rtc1 = "/rtc@7000e000";
+               serial0 = &uarta;
        };
 
        memory {
index ca8484c..04c58e9 100644 (file)
@@ -10,6 +10,7 @@
        aliases {
                rtc0 = "/i2c@7000d000/tps6586x@34";
                rtc1 = "/rtc@7000e000";
+               serial0 = &uartd;
        };
 
        memory {
index 1843725..340d811 100644 (file)
@@ -10,6 +10,7 @@
        aliases {
                rtc0 = "/i2c@7000d000/max8907@3c";
                rtc1 = "/rtc@7000e000";
+               serial0 = &uarta;
        };
 
        memory {
index 3b374c4..8acf5d8 100644 (file)
@@ -9,14 +9,6 @@
        compatible = "nvidia,tegra20";
        interrupt-parent = <&intc>;
 
-       aliases {
-               serial0 = &uarta;
-               serial1 = &uartb;
-               serial2 = &uartc;
-               serial3 = &uartd;
-               serial4 = &uarte;
-       };
-
        host1x@50000000 {
                compatible = "nvidia,tegra20-host1x", "simple-bus";
                reg = <0x50000000 0x00024000>;
index 45d40f0..6236bde 100644 (file)
                rtc0 = "/i2c@7000c000/rtc@68";
                rtc1 = "/i2c@7000d000/tps65911@2d";
                rtc2 = "/rtc@7000e000";
+               serial0 = &uarta;
+               serial1 = &uartb;
+               serial2 = &uartc;
+               serial3 = &uartd;
        };
 
        pcie-controller@00003000 {
index cee8f22..6b157ee 100644 (file)
@@ -9,6 +9,7 @@
        aliases {
                rtc0 = "/i2c@7000d000/tps65911@2d";
                rtc1 = "/rtc@7000e000";
+               serial0 = &uarta;
        };
 
        memory {
index 2063795..a1b682e 100644 (file)
@@ -30,6 +30,8 @@
        aliases {
                rtc0 = "/i2c@7000d000/tps65911@2d";
                rtc1 = "/rtc@7000e000";
+               serial0 = &uarta;
+               serial1 = &uartc;
        };
 
        memory {
index 7793abd..4d3ddc5 100644 (file)
@@ -10,6 +10,9 @@
                rtc0 = "/i2c@7000c000/rtc@68";
                rtc1 = "/i2c@7000d000/tps65911@2d";
                rtc2 = "/rtc@7000e000";
+               serial0 = &uarta;
+               serial1 = &uartb;
+               serial2 = &uartd;
        };
 
        host1x@50000000 {
index aa6ccea..b270b9e 100644 (file)
@@ -9,14 +9,6 @@
        compatible = "nvidia,tegra30";
        interrupt-parent = <&intc>;
 
-       aliases {
-               serial0 = &uarta;
-               serial1 = &uartb;
-               serial2 = &uartc;
-               serial3 = &uartd;
-               serial4 = &uarte;
-       };
-
        pcie-controller@00003000 {
                compatible = "nvidia,tegra30-pcie";
                device_type = "pci";
index 3fd1b74..de1b453 100644 (file)
 
 };
 
+&esdhc1 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_esdhc1>;
+       bus-width = <4>;
+       status = "okay";
+};
+
 &fec1 {
        phy-mode = "rmii";
        pinctrl-names = "default";
 
 &iomuxc {
        vf610-cosmic {
+               pinctrl_esdhc1: esdhc1grp {
+                       fsl,pins = <
+                               VF610_PAD_PTA24__ESDHC1_CLK     0x31ef
+                               VF610_PAD_PTA25__ESDHC1_CMD     0x31ef
+                               VF610_PAD_PTA26__ESDHC1_DAT0    0x31ef
+                               VF610_PAD_PTA27__ESDHC1_DAT1    0x31ef
+                               VF610_PAD_PTA28__ESDHC1_DATA2   0x31ef
+                               VF610_PAD_PTA29__ESDHC1_DAT3    0x31ef
+                               VF610_PAD_PTB28__GPIO_98        0x219d
+                       >;
+               };
+
                pinctrl_fec1: fec1grp {
                        fsl,pins = <
                                VF610_PAD_PTC9__ENET_RMII1_MDC          0x30d2
index e1f51ca..0429bbd 100644 (file)
        };
 };
 
+&clkc {
+       fclk-enable = <0xf>;
+};
+
 &gem0 {
        status = "okay";
        phy-mode = "rgmii-id";
index d86771a..72041f0 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/io.h>
 #include <linux/slab.h>
 #include <linux/edma.h>
+#include <linux/dma-mapping.h>
 #include <linux/of_address.h>
 #include <linux/of_device.h>
 #include <linux/of_dma.h>
@@ -1623,6 +1624,11 @@ static int edma_probe(struct platform_device *pdev)
        struct device_node      *node = pdev->dev.of_node;
        struct device           *dev = &pdev->dev;
        int                     ret;
+       struct platform_device_info edma_dev_info = {
+               .name = "edma-dma-engine",
+               .dma_mask = DMA_BIT_MASK(32),
+               .parent = &pdev->dev,
+       };
 
        if (node) {
                /* Check if this is a second instance registered */
@@ -1793,6 +1799,9 @@ static int edma_probe(struct platform_device *pdev)
                        edma_write_array(j, EDMA_QRAE, i, 0x0);
                }
                arch_num_cc++;
+
+               edma_dev_info.id = j;
+               platform_device_register_full(&edma_dev_info);
        }
 
        return 0;
index 72058b8..e21ef83 100644 (file)
@@ -142,11 +142,13 @@ CONFIG_MMC_DW_IDMAC=y
 CONFIG_MMC_DW_EXYNOS=y
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_MAX77686=y
+CONFIG_RTC_DRV_MAX77802=y
 CONFIG_RTC_DRV_S5M=y
 CONFIG_RTC_DRV_S3C=y
 CONFIG_DMADEVICES=y
 CONFIG_PL330_DMA=y
 CONFIG_COMMON_CLK_MAX77686=y
+CONFIG_COMMON_CLK_MAX77802=y
 CONFIG_COMMON_CLK_S2MPS11=y
 CONFIG_EXYNOS_IOMMU=y
 CONFIG_IIO=y
index e688741..e6b0007 100644 (file)
@@ -97,6 +97,7 @@ CONFIG_SERIAL_IMX_CONSOLE=y
 # CONFIG_HW_RANDOM is not set
 CONFIG_I2C_CHARDEV=y
 CONFIG_I2C_IMX=y
+CONFIG_SPI=y
 CONFIG_SPI_IMX=y
 CONFIG_SPI_SPIDEV=y
 CONFIG_GPIO_SYSFS=y
index 8fca6e2..6790f1b 100644 (file)
@@ -158,6 +158,7 @@ CONFIG_I2C_CHARDEV=y
 CONFIG_I2C_ALGOPCF=m
 CONFIG_I2C_ALGOPCA=m
 CONFIG_I2C_IMX=y
+CONFIG_SPI=y
 CONFIG_SPI_IMX=y
 CONFIG_GPIO_SYSFS=y
 CONFIG_GPIO_MC9S08DZ60=y
index f1dc7fc..9d7a32f 100644 (file)
@@ -217,6 +217,7 @@ CONFIG_I2C_CADENCE=y
 CONFIG_I2C_DESIGNWARE_PLATFORM=y
 CONFIG_I2C_EXYNOS5=y
 CONFIG_I2C_MV64XXX=y
+CONFIG_I2C_S3C2410=y
 CONFIG_I2C_SIRF=y
 CONFIG_I2C_TEGRA=y
 CONFIG_I2C_ST=y
@@ -235,6 +236,7 @@ CONFIG_SPI_TEGRA20_SLINK=y
 CONFIG_SPI_XILINX=y
 CONFIG_PINCTRL_AS3722=y
 CONFIG_PINCTRL_PALMAS=y
+CONFIG_PINCTRL_APQ8084=y
 CONFIG_GPIO_SYSFS=y
 CONFIG_GPIO_GENERIC_PLATFORM=y
 CONFIG_GPIO_DWAPB=y
@@ -411,6 +413,7 @@ CONFIG_NVEC_POWER=y
 CONFIG_NVEC_PAZ00=y
 CONFIG_QCOM_GSBI=y
 CONFIG_COMMON_CLK_QCOM=y
+CONFIG_APQ_MMCC_8084=y
 CONFIG_MSM_GCC_8660=y
 CONFIG_MSM_MMCC_8960=y
 CONFIG_MSM_MMCC_8974=y
index 16e719c..b3f8667 100644 (file)
@@ -86,7 +86,6 @@ CONFIG_IP_PNP_DHCP=y
 CONFIG_IP_PNP_BOOTP=y
 CONFIG_IP_PNP_RARP=y
 # CONFIG_INET_LRO is not set
-CONFIG_IPV6=y
 CONFIG_NETFILTER=y
 CONFIG_CAN=m
 CONFIG_CAN_C_CAN=m
@@ -112,6 +111,7 @@ CONFIG_MTD_OOPS=y
 CONFIG_MTD_CFI=y
 CONFIG_MTD_CFI_INTELEXT=y
 CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_ECC_BCH=y
 CONFIG_MTD_NAND_OMAP2=y
 CONFIG_MTD_ONENAND=y
 CONFIG_MTD_ONENAND_VERIFY_WRITE=y
@@ -317,7 +317,7 @@ CONFIG_EXT4_FS=y
 CONFIG_FANOTIFY=y
 CONFIG_QUOTA=y
 CONFIG_QFMT_V2=y
-CONFIG_AUTOFS4_FS=y
+CONFIG_AUTOFS4_FS=m
 CONFIG_MSDOS_FS=y
 CONFIG_VFAT_FS=y
 CONFIG_TMPFS=y
index d7a5855..a2956c3 100644 (file)
@@ -1,5 +1,6 @@
-CONFIG_EXPERIMENTAL=y
 CONFIG_SYSVIPC=y
+CONFIG_FHANDLE=y
+CONFIG_HIGH_RES_TIMERS=y
 CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
 CONFIG_LOG_BUF_SHIFT=14
@@ -11,23 +12,17 @@ CONFIG_PROFILING=y
 CONFIG_OPROFILE=y
 CONFIG_MODULES=y
 CONFIG_MODULE_UNLOAD=y
-CONFIG_HOTPLUG=y
 # CONFIG_LBDAF is not set
 # CONFIG_BLK_DEV_BSG is not set
 # CONFIG_IOSCHED_DEADLINE is not set
 # CONFIG_IOSCHED_CFQ is not set
 CONFIG_ARCH_SOCFPGA=y
-CONFIG_MACH_SOCFPGA_CYCLONE5=y
 CONFIG_ARM_THUMBEE=y
-# CONFIG_ARCH_VEXPRESS_CORTEX_A5_A9_ERRATA is not set
-# CONFIG_CACHE_L2X0 is not set
-CONFIG_HIGH_RES_TIMERS=y
 CONFIG_SMP=y
 CONFIG_NR_CPUS=2
 CONFIG_AEABI=y
 CONFIG_ZBOOT_ROM_TEXT=0x0
 CONFIG_ZBOOT_ROM_BSS=0x0
-CONFIG_CMDLINE=""
 CONFIG_VFP=y
 CONFIG_NEON=y
 CONFIG_NET=y
@@ -41,38 +36,30 @@ CONFIG_IP_PNP=y
 CONFIG_IP_PNP_DHCP=y
 CONFIG_IP_PNP_BOOTP=y
 CONFIG_IP_PNP_RARP=y
+CONFIG_IPV6=y
+CONFIG_NETWORK_PHY_TIMESTAMPING=y
+CONFIG_VLAN_8021Q=y
+CONFIG_VLAN_8021Q_GVRP=y
 CONFIG_CAN=y
-CONFIG_CAN_RAW=y
-CONFIG_CAN_BCM=y
-CONFIG_CAN_GW=y
-CONFIG_CAN_DEV=y
-CONFIG_CAN_CALC_BITTIMING=y
 CONFIG_CAN_C_CAN=y
 CONFIG_CAN_C_CAN_PLATFORM=y
 CONFIG_CAN_DEBUG_DEVICES=y
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 CONFIG_DEVTMPFS=y
-CONFIG_PROC_DEVICETREE=y
+CONFIG_DEVTMPFS_MOUNT=y
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_RAM_COUNT=2
 CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_SRAM=y
 CONFIG_SCSI=y
 # CONFIG_SCSI_PROC_FS is not set
 CONFIG_BLK_DEV_SD=y
 # CONFIG_SCSI_LOWLEVEL is not set
 CONFIG_NETDEVICES=y
 CONFIG_STMMAC_ETH=y
+CONFIG_DWMAC_SOCFPGA=y
 CONFIG_MICREL_PHY=y
-# CONFIG_STMMAC_PHY_ID_ZERO_WORKAROUND is not set
 CONFIG_INPUT_EVDEV=y
-CONFIG_DWMAC_SOCFPGA=y
-CONFIG_PPS=y
-CONFIG_NETWORK_PHY_TIMESTAMPING=y
-CONFIG_PTP_1588_CLOCK=y
-CONFIG_VLAN_8021Q=y
-CONFIG_VLAN_8021Q_GVRP=y
-CONFIG_GARP=y
-CONFIG_IPV6=y
 # CONFIG_SERIO_SERPORT is not set
 CONFIG_SERIO_AMBAKMI=y
 CONFIG_LEGACY_PTY_COUNT=16
@@ -81,45 +68,43 @@ CONFIG_SERIAL_8250_CONSOLE=y
 CONFIG_SERIAL_8250_NR_UARTS=2
 CONFIG_SERIAL_8250_RUNTIME_UARTS=2
 CONFIG_SERIAL_8250_DW=y
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_DESIGNWARE_PLATFORM=y
 CONFIG_GPIOLIB=y
 CONFIG_GPIO_SYSFS=y
 CONFIG_GPIO_DWAPB=y
-# CONFIG_RTC_HCTOSYS is not set
+CONFIG_PMBUS=y
+CONFIG_SENSORS_LTC2978=y
+CONFIG_SENSORS_LTC2978_REGULATOR=y
 CONFIG_WATCHDOG=y
 CONFIG_DW_WATCHDOG=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_USB=y
+CONFIG_USB_DWC2=y
+CONFIG_USB_DWC2_HOST=y
+CONFIG_MMC=y
+CONFIG_MMC_DW=y
 CONFIG_EXT2_FS=y
 CONFIG_EXT2_FS_XATTR=y
 CONFIG_EXT2_FS_POSIX_ACL=y
 CONFIG_EXT3_FS=y
-CONFIG_NFS_FS=y
-CONFIG_ROOT_NFS=y
-# CONFIG_DNOTIFY is not set
-# CONFIG_INOTIFY_USER is not set
-CONFIG_FHANDLE=y
+CONFIG_EXT4_FS=y
 CONFIG_VFAT_FS=y
 CONFIG_NTFS_FS=y
 CONFIG_NTFS_RW=y
 CONFIG_TMPFS=y
-CONFIG_JFFS2_FS=y
+CONFIG_CONFIGFS_FS=y
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO=y
 CONFIG_MAGIC_SYSRQ=y
 CONFIG_DETECT_HUNG_TASK=y
 # CONFIG_SCHED_DEBUG is not set
-CONFIG_DEBUG_INFO=y
 CONFIG_ENABLE_DEFAULT_TRACERS=y
 CONFIG_DEBUG_USER=y
 CONFIG_XZ_DEC=y
-CONFIG_I2C=y
-CONFIG_I2C_DESIGNWARE_CORE=y
-CONFIG_I2C_DESIGNWARE_PLATFORM=y
-CONFIG_I2C_CHARDEV=y
-CONFIG_MMC=y
-CONFIG_MMC_DW=y
-CONFIG_PM=y
-CONFIG_SUSPEND=y
-CONFIG_MMC_UNSAFE_RESUME=y
-CONFIG_USB=y
-CONFIG_USB_DWC2=y
-CONFIG_USB_DWC2_HOST=y
-CONFIG_USB_DWC2_PLATFORM=y
index fc44d37..ce73ab6 100644 (file)
@@ -44,16 +44,6 @@ struct cpu_context_save {
        __u32   extra[2];               /* Xscale 'acc' register, etc */
 };
 
-struct arm_restart_block {
-       union {
-               /* For user cache flushing */
-               struct {
-                       unsigned long start;
-                       unsigned long end;
-               } cache;
-       };
-};
-
 /*
  * low level task data that entry.S needs immediate access to.
  * __switch_to() assumes cpu_context follows immediately after cpu_domain.
@@ -79,7 +69,6 @@ struct thread_info {
        unsigned long           thumbee_state;  /* ThumbEE Handler Base register */
 #endif
        struct restart_block    restart_block;
-       struct arm_restart_block        arm_restart_block;
 };
 
 #define INIT_THREAD_INFO(tsk)                                          \
index 0c8b108..9f5d818 100644 (file)
@@ -533,8 +533,6 @@ static int bad_syscall(int n, struct pt_regs *regs)
        return regs->ARM_r0;
 }
 
-static long do_cache_op_restart(struct restart_block *);
-
 static inline int
 __do_cache_op(unsigned long start, unsigned long end)
 {
@@ -543,24 +541,8 @@ __do_cache_op(unsigned long start, unsigned long end)
        do {
                unsigned long chunk = min(PAGE_SIZE, end - start);
 
-               if (signal_pending(current)) {
-                       struct thread_info *ti = current_thread_info();
-
-                       ti->restart_block = (struct restart_block) {
-                               .fn     = do_cache_op_restart,
-                       };
-
-                       ti->arm_restart_block = (struct arm_restart_block) {
-                               {
-                                       .cache = {
-                                               .start  = start,
-                                               .end    = end,
-                                       },
-                               },
-                       };
-
-                       return -ERESTART_RESTARTBLOCK;
-               }
+               if (fatal_signal_pending(current))
+                       return 0;
 
                ret = flush_cache_user_range(start, start + chunk);
                if (ret)
@@ -573,15 +555,6 @@ __do_cache_op(unsigned long start, unsigned long end)
        return 0;
 }
 
-static long do_cache_op_restart(struct restart_block *unused)
-{
-       struct arm_restart_block *restart_block;
-
-       restart_block = &current_thread_info()->arm_restart_block;
-       return __do_cache_op(restart_block->cache.start,
-                            restart_block->cache.end);
-}
-
 static inline int
 do_cache_op(unsigned long start, unsigned long end, int flags)
 {
index 57a403a..8664ff1 100644 (file)
@@ -197,7 +197,8 @@ static void unmap_range(struct kvm *kvm, pgd_t *pgdp,
        pgd = pgdp + pgd_index(addr);
        do {
                next = kvm_pgd_addr_end(addr, end);
-               unmap_puds(kvm, pgd, addr, next);
+               if (!pgd_none(*pgd))
+                       unmap_puds(kvm, pgd, addr, next);
        } while (pgd++, addr = next, addr != end);
 }
 
@@ -834,6 +835,11 @@ static bool kvm_is_write_fault(struct kvm_vcpu *vcpu)
        return kvm_vcpu_dabt_iswrite(vcpu);
 }
 
+static bool kvm_is_device_pfn(unsigned long pfn)
+{
+       return !pfn_valid(pfn);
+}
+
 static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
                          struct kvm_memory_slot *memslot, unsigned long hva,
                          unsigned long fault_status)
@@ -904,7 +910,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
        if (is_error_pfn(pfn))
                return -EFAULT;
 
-       if (kvm_is_mmio_pfn(pfn))
+       if (kvm_is_device_pfn(pfn))
                mem_type = PAGE_S2_DEVICE;
 
        spin_lock(&kvm->mmu_lock);
index a178184..4096372 100644 (file)
 #define PFD_PLL1_BASE          (anatop_base + 0x2b0)
 #define PFD_PLL2_BASE          (anatop_base + 0x100)
 #define PFD_PLL3_BASE          (anatop_base + 0xf0)
+#define PLL1_CTRL              (anatop_base + 0x270)
+#define PLL2_CTRL              (anatop_base + 0x30)
 #define PLL3_CTRL              (anatop_base + 0x10)
+#define PLL4_CTRL              (anatop_base + 0x70)
+#define PLL5_CTRL              (anatop_base + 0xe0)
+#define PLL6_CTRL              (anatop_base + 0xa0)
 #define PLL7_CTRL              (anatop_base + 0x20)
+#define ANA_MISC1              (anatop_base + 0x160)
 
 static void __iomem *anatop_base;
 static void __iomem *ccm_base;
@@ -67,25 +73,34 @@ static void __iomem *ccm_base;
 /* sources for multiplexer clocks, this is used multiple times */
 static const char *fast_sels[] = { "firc", "fxosc", };
 static const char *slow_sels[] = { "sirc_32k", "sxosc", };
-static const char *pll1_sels[] = { "pll1_main", "pll1_pfd1", "pll1_pfd2", "pll1_pfd3", "pll1_pfd4", };
-static const char *pll2_sels[] = { "pll2_main", "pll2_pfd1", "pll2_pfd2", "pll2_pfd3", "pll2_pfd4", };
-static const char *sys_sels[]  = { "fast_clk_sel", "slow_clk_sel", "pll2_pfd_sel", "pll2_main", "pll1_pfd_sel", "pll3_main", };
+static const char *pll1_sels[] = { "pll1_sys", "pll1_pfd1", "pll1_pfd2", "pll1_pfd3", "pll1_pfd4", };
+static const char *pll2_sels[] = { "pll2_bus", "pll2_pfd1", "pll2_pfd2", "pll2_pfd3", "pll2_pfd4", };
+static const char *pll_bypass_src_sels[] = { "fast_clk_sel", "lvds1_in", };
+static const char *pll1_bypass_sels[] = { "pll1", "pll1_bypass_src", };
+static const char *pll2_bypass_sels[] = { "pll2", "pll2_bypass_src", };
+static const char *pll3_bypass_sels[] = { "pll3", "pll3_bypass_src", };
+static const char *pll4_bypass_sels[] = { "pll4", "pll4_bypass_src", };
+static const char *pll5_bypass_sels[] = { "pll5", "pll5_bypass_src", };
+static const char *pll6_bypass_sels[] = { "pll6", "pll6_bypass_src", };
+static const char *pll7_bypass_sels[] = { "pll7", "pll7_bypass_src", };
+static const char *sys_sels[]  = { "fast_clk_sel", "slow_clk_sel", "pll2_pfd_sel", "pll2_bus", "pll1_pfd_sel", "pll3_usb_otg", };
 static const char *ddr_sels[]  = { "pll2_pfd2", "sys_sel", };
 static const char *rmii_sels[] = { "enet_ext", "audio_ext", "enet_50m", "enet_25m", };
 static const char *enet_ts_sels[]      = { "enet_ext", "fxosc", "audio_ext", "usb", "enet_ts", "enet_25m", "enet_50m", };
-static const char *esai_sels[] = { "audio_ext", "mlb", "spdif_rx", "pll4_main_div", };
-static const char *sai_sels[]  = { "audio_ext", "mlb", "spdif_rx", "pll4_main_div", };
+static const char *esai_sels[] = { "audio_ext", "mlb", "spdif_rx", "pll4_audio_div", };
+static const char *sai_sels[]  = { "audio_ext", "mlb", "spdif_rx", "pll4_audio_div", };
 static const char *nfc_sels[]  = { "platform_bus", "pll1_pfd1", "pll3_pfd1", "pll3_pfd3", };
-static const char *qspi_sels[] = { "pll3_main", "pll3_pfd4", "pll2_pfd4", "pll1_pfd4", };
-static const char *esdhc_sels[]        = { "pll3_main", "pll3_pfd3", "pll1_pfd3", "platform_bus", };
-static const char *dcu_sels[]  = { "pll1_pfd2", "pll3_main", };
+static const char *qspi_sels[] = { "pll3_usb_otg", "pll3_pfd4", "pll2_pfd4", "pll1_pfd4", };
+static const char *esdhc_sels[]        = { "pll3_usb_otg", "pll3_pfd3", "pll1_pfd3", "platform_bus", };
+static const char *dcu_sels[]  = { "pll1_pfd2", "pll3_usb_otg", };
 static const char *gpu_sels[]  = { "pll2_pfd2", "pll3_pfd2", };
-static const char *vadc_sels[] = { "pll6_main_div", "pll3_main_div", "pll3_main", };
+static const char *vadc_sels[] = { "pll6_video_div", "pll3_usb_otg_div", "pll3_usb_otg", };
 /* FTM counter clock source, not module clock */
 static const char *ftm_ext_sels[]      = {"sirc_128k", "sxosc", "fxosc_half", "audio_ext", };
 static const char *ftm_fix_sels[]      = { "sxosc", "ipg_bus", };
 
-static struct clk_div_table pll4_main_div_table[] = {
+
+static struct clk_div_table pll4_audio_div_table[] = {
        { .val = 0, .div = 1 },
        { .val = 1, .div = 2 },
        { .val = 2, .div = 6 },
@@ -120,6 +135,9 @@ static void __init vf610_clocks_init(struct device_node *ccm_node)
        clk[VF610_CLK_AUDIO_EXT] = imx_obtain_fixed_clock("audio_ext", 0);
        clk[VF610_CLK_ENET_EXT] = imx_obtain_fixed_clock("enet_ext", 0);
 
+       /* Clock source from external clock via LVDs PAD */
+       clk[VF610_CLK_ANACLK1] = imx_obtain_fixed_clock("anaclk1", 0);
+
        clk[VF610_CLK_FXOSC_HALF] = imx_clk_fixed_factor("fxosc_half", "fxosc", 1, 2);
 
        np = of_find_compatible_node(NULL, NULL, "fsl,vf610-anatop");
@@ -133,31 +151,63 @@ static void __init vf610_clocks_init(struct device_node *ccm_node)
        clk[VF610_CLK_SLOW_CLK_SEL] = imx_clk_mux("slow_clk_sel", CCM_CCSR, 4, 1, slow_sels, ARRAY_SIZE(slow_sels));
        clk[VF610_CLK_FASK_CLK_SEL] = imx_clk_mux("fast_clk_sel", CCM_CCSR, 5, 1, fast_sels, ARRAY_SIZE(fast_sels));
 
-       clk[VF610_CLK_PLL1_MAIN] = imx_clk_fixed_factor("pll1_main", "fast_clk_sel", 22, 1);
-       clk[VF610_CLK_PLL1_PFD1] = imx_clk_pfd("pll1_pfd1", "pll1_main", PFD_PLL1_BASE, 0);
-       clk[VF610_CLK_PLL1_PFD2] = imx_clk_pfd("pll1_pfd2", "pll1_main", PFD_PLL1_BASE, 1);
-       clk[VF610_CLK_PLL1_PFD3] = imx_clk_pfd("pll1_pfd3", "pll1_main", PFD_PLL1_BASE, 2);
-       clk[VF610_CLK_PLL1_PFD4] = imx_clk_pfd("pll1_pfd4", "pll1_main", PFD_PLL1_BASE, 3);
-
-       clk[VF610_CLK_PLL2_MAIN] = imx_clk_fixed_factor("pll2_main", "fast_clk_sel", 22, 1);
-       clk[VF610_CLK_PLL2_PFD1] = imx_clk_pfd("pll2_pfd1", "pll2_main", PFD_PLL2_BASE, 0);
-       clk[VF610_CLK_PLL2_PFD2] = imx_clk_pfd("pll2_pfd2", "pll2_main", PFD_PLL2_BASE, 1);
-       clk[VF610_CLK_PLL2_PFD3] = imx_clk_pfd("pll2_pfd3", "pll2_main", PFD_PLL2_BASE, 2);
-       clk[VF610_CLK_PLL2_PFD4] = imx_clk_pfd("pll2_pfd4", "pll2_main", PFD_PLL2_BASE, 3);
-
-       clk[VF610_CLK_PLL3_MAIN] = imx_clk_fixed_factor("pll3_main", "fast_clk_sel", 20, 1);
-       clk[VF610_CLK_PLL3_PFD1] = imx_clk_pfd("pll3_pfd1", "pll3_main", PFD_PLL3_BASE, 0);
-       clk[VF610_CLK_PLL3_PFD2] = imx_clk_pfd("pll3_pfd2", "pll3_main", PFD_PLL3_BASE, 1);
-       clk[VF610_CLK_PLL3_PFD3] = imx_clk_pfd("pll3_pfd3", "pll3_main", PFD_PLL3_BASE, 2);
-       clk[VF610_CLK_PLL3_PFD4] = imx_clk_pfd("pll3_pfd4", "pll3_main", PFD_PLL3_BASE, 3);
-
-       clk[VF610_CLK_PLL4_MAIN] = imx_clk_fixed_factor("pll4_main", "fast_clk_sel", 25, 1);
-       /* Enet pll: fixed 50Mhz */
-       clk[VF610_CLK_PLL5_MAIN] = imx_clk_fixed_factor("pll5_main", "fast_clk_sel", 125, 6);
-       /* pll6: default 960Mhz */
-       clk[VF610_CLK_PLL6_MAIN] = imx_clk_fixed_factor("pll6_main", "fast_clk_sel", 40, 1);
-       /* pll7: USB1 PLL at 480MHz */
-       clk[VF610_CLK_PLL7_MAIN] = imx_clk_pllv3(IMX_PLLV3_USB, "pll7_main", "fast_clk_sel", PLL7_CTRL, 0x2);
+       clk[VF610_CLK_PLL1_BYPASS_SRC] = imx_clk_mux("pll1_bypass_src", PLL1_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
+       clk[VF610_CLK_PLL2_BYPASS_SRC] = imx_clk_mux("pll2_bypass_src", PLL2_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
+       clk[VF610_CLK_PLL3_BYPASS_SRC] = imx_clk_mux("pll3_bypass_src", PLL3_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
+       clk[VF610_CLK_PLL4_BYPASS_SRC] = imx_clk_mux("pll4_bypass_src", PLL4_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
+       clk[VF610_CLK_PLL5_BYPASS_SRC] = imx_clk_mux("pll5_bypass_src", PLL5_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
+       clk[VF610_CLK_PLL6_BYPASS_SRC] = imx_clk_mux("pll6_bypass_src", PLL6_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
+       clk[VF610_CLK_PLL7_BYPASS_SRC] = imx_clk_mux("pll7_bypass_src", PLL7_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
+
+       clk[VF610_CLK_PLL1] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll1", "pll1_bypass_src", PLL1_CTRL, 0x1);
+       clk[VF610_CLK_PLL2] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2", "pll2_bypass_src", PLL2_CTRL, 0x1);
+       clk[VF610_CLK_PLL3] = imx_clk_pllv3(IMX_PLLV3_USB,     "pll3", "pll3_bypass_src", PLL3_CTRL, 0x1);
+       clk[VF610_CLK_PLL4] = imx_clk_pllv3(IMX_PLLV3_AV,      "pll4", "pll4_bypass_src", PLL4_CTRL, 0x7f);
+       clk[VF610_CLK_PLL5] = imx_clk_pllv3(IMX_PLLV3_ENET,    "pll5", "pll5_bypass_src", PLL5_CTRL, 0x3);
+       clk[VF610_CLK_PLL6] = imx_clk_pllv3(IMX_PLLV3_AV,      "pll6", "pll6_bypass_src", PLL6_CTRL, 0x7f);
+       clk[VF610_CLK_PLL7] = imx_clk_pllv3(IMX_PLLV3_USB,     "pll7", "pll7_bypass_src", PLL7_CTRL, 0x1);
+
+       clk[VF610_PLL1_BYPASS] = imx_clk_mux_flags("pll1_bypass", PLL1_CTRL, 16, 1, pll1_bypass_sels, ARRAY_SIZE(pll1_bypass_sels), CLK_SET_RATE_PARENT);
+       clk[VF610_PLL2_BYPASS] = imx_clk_mux_flags("pll2_bypass", PLL2_CTRL, 16, 1, pll2_bypass_sels, ARRAY_SIZE(pll2_bypass_sels), CLK_SET_RATE_PARENT);
+       clk[VF610_PLL3_BYPASS] = imx_clk_mux_flags("pll3_bypass", PLL3_CTRL, 16, 1, pll3_bypass_sels, ARRAY_SIZE(pll3_bypass_sels), CLK_SET_RATE_PARENT);
+       clk[VF610_PLL4_BYPASS] = imx_clk_mux_flags("pll4_bypass", PLL4_CTRL, 16, 1, pll4_bypass_sels, ARRAY_SIZE(pll4_bypass_sels), CLK_SET_RATE_PARENT);
+       clk[VF610_PLL5_BYPASS] = imx_clk_mux_flags("pll5_bypass", PLL5_CTRL, 16, 1, pll5_bypass_sels, ARRAY_SIZE(pll5_bypass_sels), CLK_SET_RATE_PARENT);
+       clk[VF610_PLL6_BYPASS] = imx_clk_mux_flags("pll6_bypass", PLL6_CTRL, 16, 1, pll6_bypass_sels, ARRAY_SIZE(pll6_bypass_sels), CLK_SET_RATE_PARENT);
+       clk[VF610_PLL7_BYPASS] = imx_clk_mux_flags("pll7_bypass", PLL7_CTRL, 16, 1, pll7_bypass_sels, ARRAY_SIZE(pll7_bypass_sels), CLK_SET_RATE_PARENT);
+
+       /* Do not bypass PLLs initially */
+       clk_set_parent(clk[VF610_PLL1_BYPASS], clk[VF610_CLK_PLL1]);
+       clk_set_parent(clk[VF610_PLL2_BYPASS], clk[VF610_CLK_PLL2]);
+       clk_set_parent(clk[VF610_PLL3_BYPASS], clk[VF610_CLK_PLL3]);
+       clk_set_parent(clk[VF610_PLL4_BYPASS], clk[VF610_CLK_PLL4]);
+       clk_set_parent(clk[VF610_PLL5_BYPASS], clk[VF610_CLK_PLL5]);
+       clk_set_parent(clk[VF610_PLL6_BYPASS], clk[VF610_CLK_PLL6]);
+       clk_set_parent(clk[VF610_PLL7_BYPASS], clk[VF610_CLK_PLL7]);
+
+       clk[VF610_CLK_PLL1_SYS]      = imx_clk_gate("pll1_sys",      "pll1_bypass", PLL1_CTRL, 13);
+       clk[VF610_CLK_PLL2_BUS]      = imx_clk_gate("pll2_bus",      "pll2_bypass", PLL2_CTRL, 13);
+       clk[VF610_CLK_PLL3_USB_OTG]  = imx_clk_gate("pll3_usb_otg",  "pll3_bypass", PLL3_CTRL, 13);
+       clk[VF610_CLK_PLL4_AUDIO]    = imx_clk_gate("pll4_audio",    "pll4_bypass", PLL4_CTRL, 13);
+       clk[VF610_CLK_PLL5_ENET]     = imx_clk_gate("pll5_enet",     "pll5_bypass", PLL5_CTRL, 13);
+       clk[VF610_CLK_PLL6_VIDEO]    = imx_clk_gate("pll6_video",    "pll6_bypass", PLL6_CTRL, 13);
+       clk[VF610_CLK_PLL7_USB_HOST] = imx_clk_gate("pll7_usb_host", "pll7_bypass", PLL7_CTRL, 13);
+
+       clk[VF610_CLK_LVDS1_IN]  = imx_clk_gate_exclusive("lvds1_in", "anaclk1", ANA_MISC1, 12, BIT(10));
+
+       clk[VF610_CLK_PLL1_PFD1] = imx_clk_pfd("pll1_pfd1", "pll1_sys", PFD_PLL1_BASE, 0);
+       clk[VF610_CLK_PLL1_PFD2] = imx_clk_pfd("pll1_pfd2", "pll1_sys", PFD_PLL1_BASE, 1);
+       clk[VF610_CLK_PLL1_PFD3] = imx_clk_pfd("pll1_pfd3", "pll1_sys", PFD_PLL1_BASE, 2);
+       clk[VF610_CLK_PLL1_PFD4] = imx_clk_pfd("pll1_pfd4", "pll1_sys", PFD_PLL1_BASE, 3);
+
+       clk[VF610_CLK_PLL2_PFD1] = imx_clk_pfd("pll2_pfd1", "pll2_bus", PFD_PLL2_BASE, 0);
+       clk[VF610_CLK_PLL2_PFD2] = imx_clk_pfd("pll2_pfd2", "pll2_bus", PFD_PLL2_BASE, 1);
+       clk[VF610_CLK_PLL2_PFD3] = imx_clk_pfd("pll2_pfd3", "pll2_bus", PFD_PLL2_BASE, 2);
+       clk[VF610_CLK_PLL2_PFD4] = imx_clk_pfd("pll2_pfd4", "pll2_bus", PFD_PLL2_BASE, 3);
+
+       clk[VF610_CLK_PLL3_PFD1] = imx_clk_pfd("pll3_pfd1", "pll3_usb_otg", PFD_PLL3_BASE, 0);
+       clk[VF610_CLK_PLL3_PFD2] = imx_clk_pfd("pll3_pfd2", "pll3_usb_otg", PFD_PLL3_BASE, 1);
+       clk[VF610_CLK_PLL3_PFD3] = imx_clk_pfd("pll3_pfd3", "pll3_usb_otg", PFD_PLL3_BASE, 2);
+       clk[VF610_CLK_PLL3_PFD4] = imx_clk_pfd("pll3_pfd4", "pll3_usb_otg", PFD_PLL3_BASE, 3);
 
        clk[VF610_CLK_PLL1_PFD_SEL] = imx_clk_mux("pll1_pfd_sel", CCM_CCSR, 16, 3, pll1_sels, 5);
        clk[VF610_CLK_PLL2_PFD_SEL] = imx_clk_mux("pll2_pfd_sel", CCM_CCSR, 19, 3, pll2_sels, 5);
@@ -167,12 +217,12 @@ static void __init vf610_clocks_init(struct device_node *ccm_node)
        clk[VF610_CLK_PLATFORM_BUS] = imx_clk_divider("platform_bus", "sys_bus", CCM_CACRR, 3, 3);
        clk[VF610_CLK_IPG_BUS] = imx_clk_divider("ipg_bus", "platform_bus", CCM_CACRR, 11, 2);
 
-       clk[VF610_CLK_PLL3_MAIN_DIV] = imx_clk_divider("pll3_main_div", "pll3_main", CCM_CACRR, 20, 1);
-       clk[VF610_CLK_PLL4_MAIN_DIV] = clk_register_divider_table(NULL, "pll4_main_div", "pll4_main", 0, CCM_CACRR, 6, 3, 0, pll4_main_div_table, &imx_ccm_lock);
-       clk[VF610_CLK_PLL6_MAIN_DIV] = imx_clk_divider("pll6_main_div", "pll6_main", CCM_CACRR, 21, 1);
+       clk[VF610_CLK_PLL3_MAIN_DIV] = imx_clk_divider("pll3_usb_otg_div", "pll3_usb_otg", CCM_CACRR, 20, 1);
+       clk[VF610_CLK_PLL4_MAIN_DIV] = clk_register_divider_table(NULL, "pll4_audio_div", "pll4_audio", 0, CCM_CACRR, 6, 3, 0, pll4_audio_div_table, &imx_ccm_lock);
+       clk[VF610_CLK_PLL6_MAIN_DIV] = imx_clk_divider("pll6_video_div", "pll6_video", CCM_CACRR, 21, 1);
 
-       clk[VF610_CLK_USBPHY0] = imx_clk_gate("usbphy0", "pll3_main", PLL3_CTRL, 6);
-       clk[VF610_CLK_USBPHY1] = imx_clk_gate("usbphy1", "pll7_main", PLL7_CTRL, 6);
+       clk[VF610_CLK_USBPHY0] = imx_clk_gate("usbphy0", "pll3_usb_otg", PLL3_CTRL, 6);
+       clk[VF610_CLK_USBPHY1] = imx_clk_gate("usbphy1", "pll7_usb_host", PLL7_CTRL, 6);
 
        clk[VF610_CLK_USBC0] = imx_clk_gate2("usbc0", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(4));
        clk[VF610_CLK_USBC1] = imx_clk_gate2("usbc1", "ipg_bus", CCM_CCGR7, CCM_CCGRx_CGn(4));
@@ -191,8 +241,8 @@ static void __init vf610_clocks_init(struct device_node *ccm_node)
        clk[VF610_CLK_QSPI1_X1_DIV] = imx_clk_divider("qspi1_x1", "qspi1_x2", CCM_CSCDR3, 11, 1);
        clk[VF610_CLK_QSPI1] = imx_clk_gate2("qspi1", "qspi1_x1", CCM_CCGR8, CCM_CCGRx_CGn(4));
 
-       clk[VF610_CLK_ENET_50M] = imx_clk_fixed_factor("enet_50m", "pll5_main", 1, 10);
-       clk[VF610_CLK_ENET_25M] = imx_clk_fixed_factor("enet_25m", "pll5_main", 1, 20);
+       clk[VF610_CLK_ENET_50M] = imx_clk_fixed_factor("enet_50m", "pll5_enet", 1, 10);
+       clk[VF610_CLK_ENET_25M] = imx_clk_fixed_factor("enet_25m", "pll5_enet", 1, 20);
        clk[VF610_CLK_ENET_SEL] = imx_clk_mux("enet_sel", CCM_CSCMR2, 4, 2, rmii_sels, 4);
        clk[VF610_CLK_ENET_TS_SEL] = imx_clk_mux("enet_ts_sel", CCM_CSCMR2, 0, 3, enet_ts_sels, 7);
        clk[VF610_CLK_ENET] = imx_clk_gate("enet", "enet_sel", CCM_CSCDR1, 24);
index 559c69a..7d11979 100644 (file)
@@ -76,7 +76,7 @@ static inline void __indirect_writeb(u8 value, volatile void __iomem *p)
        u32 n, byte_enables, data;
 
        if (!is_pci_memory(addr)) {
-               __raw_writeb(value, addr);
+               __raw_writeb(value, p);
                return;
        }
 
@@ -141,7 +141,7 @@ static inline unsigned char __indirect_readb(const volatile void __iomem *p)
        u32 n, byte_enables, data;
 
        if (!is_pci_memory(addr))
-               return __raw_readb(addr);
+               return __raw_readb(p);
 
        n = addr % 4;
        byte_enables = (0xf & ~BIT(n)) << IXP4XX_PCI_NP_CBE_BESL;
index 6478626..d0d39f1 100644 (file)
@@ -188,7 +188,7 @@ static void __init thermal_quirk(void)
 
 static void __init mvebu_dt_init(void)
 {
-       if (of_machine_is_compatible("plathome,openblocks-ax3-4"))
+       if (of_machine_is_compatible("marvell,armadaxp"))
                i2c_quirk();
        if (of_machine_is_compatible("marvell,a375-db")) {
                external_abort_quirk();
index 2bdc323..044b511 100644 (file)
@@ -400,6 +400,8 @@ int __init coherency_init(void)
                 type == COHERENCY_FABRIC_TYPE_ARMADA_380)
                armada_375_380_coherency_init(np);
 
+       of_node_put(np);
+
        return 0;
 }
 
index d22c30d..8c58b71 100644 (file)
@@ -917,6 +917,10 @@ static int __init omap_device_late_idle(struct device *dev, void *data)
 static int __init omap_device_late_init(void)
 {
        bus_for_each_dev(&platform_bus_type, NULL, NULL, omap_device_late_idle);
+
+       WARN(!of_have_populated_dt(),
+               "legacy booting deprecated, please update to boot with .dts\n");
+
        return 0;
 }
 omap_late_initcall_sync(omap_device_late_init);
index bbf9df3..d28fe29 100644 (file)
 #define DMEMC_VIRT             IOMEM(0xf6100000)
 #define DMEMC_SIZE             0x00100000
 
+/*
+ * Reserved space for low level debug virtual addresses within
+ * 0xf6200000..0xf6201000
+ */
+
 /*
  * Internal Memory Controller (PXA27x and later)
  */
index 840c3a4..962a7f3 100644 (file)
@@ -923,6 +923,14 @@ static void __init spitz_i2c_init(void)
 static inline void spitz_i2c_init(void) {}
 #endif
 
+/******************************************************************************
+ * Audio devices
+ ******************************************************************************/
+static inline void spitz_audio_init(void)
+{
+       platform_device_register_simple("spitz-audio", -1, NULL, 0);
+}
+
 /******************************************************************************
  * Machine init
  ******************************************************************************/
@@ -970,6 +978,7 @@ static void __init spitz_init(void)
        spitz_nor_init();
        spitz_nand_init();
        spitz_i2c_init();
+       spitz_audio_init();
 }
 
 static void __init spitz_fixup(struct tag *tags, char **cmdline)
index 0794f04..19df9cb 100644 (file)
@@ -455,7 +455,7 @@ enum {
        MSTP128, MSTP127, MSTP125,
        MSTP116, MSTP111, MSTP100, MSTP117,
 
-       MSTP230,
+       MSTP230, MSTP229,
        MSTP222,
        MSTP218, MSTP217, MSTP216, MSTP214,
        MSTP207, MSTP206, MSTP204, MSTP203, MSTP202, MSTP201, MSTP200,
@@ -474,11 +474,12 @@ static struct clk mstp_clks[MSTP_NR] = {
        [MSTP127] = SH_CLK_MSTP32(&div4_clks[DIV4_S],   SMSTPCR1, 27, 0), /* CEU20 */
        [MSTP125] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR1, 25, 0), /* TMU0 */
        [MSTP117] = SH_CLK_MSTP32(&div4_clks[DIV4_B],   SMSTPCR1, 17, 0), /* LCDC1 */
-       [MSTP116] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR1, 16, 0), /* IIC0 */
+       [MSTP116] = SH_CLK_MSTP32(&div4_clks[DIV4_HPP], SMSTPCR1, 16, 0), /* IIC0 */
        [MSTP111] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR1, 11, 0), /* TMU1 */
        [MSTP100] = SH_CLK_MSTP32(&div4_clks[DIV4_B],   SMSTPCR1,  0, 0), /* LCDC0 */
 
        [MSTP230] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2, 30, 0), /* SCIFA6 */
+       [MSTP229] = SH_CLK_MSTP32(&div4_clks[DIV4_HP],  SMSTPCR2, 29, 0), /* INTCA */
        [MSTP222] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2, 22, 0), /* SCIFA7 */
        [MSTP218] = SH_CLK_MSTP32(&div4_clks[DIV4_HP],  SMSTPCR2, 18, 0), /* DMAC1 */
        [MSTP217] = SH_CLK_MSTP32(&div4_clks[DIV4_HP],  SMSTPCR2, 17, 0), /* DMAC2 */
@@ -575,6 +576,10 @@ static struct clk_lookup lookups[] = {
        CLKDEV_DEV_ID("sh-dma-engine.0",        &mstp_clks[MSTP218]),
        CLKDEV_DEV_ID("sh-sci.7",               &mstp_clks[MSTP222]),
        CLKDEV_DEV_ID("e6cd0000.serial",        &mstp_clks[MSTP222]),
+       CLKDEV_DEV_ID("renesas_intc_irqpin.0",  &mstp_clks[MSTP229]),
+       CLKDEV_DEV_ID("renesas_intc_irqpin.1",  &mstp_clks[MSTP229]),
+       CLKDEV_DEV_ID("renesas_intc_irqpin.2",  &mstp_clks[MSTP229]),
+       CLKDEV_DEV_ID("renesas_intc_irqpin.3",  &mstp_clks[MSTP229]),
        CLKDEV_DEV_ID("sh-sci.6",               &mstp_clks[MSTP230]),
        CLKDEV_DEV_ID("e6cc0000.serial",        &mstp_clks[MSTP230]),
 
index 126ddaf..f622652 100644 (file)
@@ -68,7 +68,7 @@
 
 #define SDCKCR         0xE6150074
 #define SD2CKCR                0xE6150078
-#define SD3CKCR                0xE615007C
+#define SD3CKCR                0xE615026C
 #define MMC0CKCR       0xE6150240
 #define MMC1CKCR       0xE6150244
 #define SSPCKCR                0xE6150248
index b7bd8e5..328657d 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/of_platform.h>
 #include <linux/delay.h>
 #include <linux/input.h>
+#include <linux/i2c/i2c-sh_mobile.h>
 #include <linux/io.h>
 #include <linux/serial_sci.h>
 #include <linux/sh_dma.h>
@@ -192,11 +193,18 @@ static struct resource i2c4_resources[] = {
        },
 };
 
+static struct i2c_sh_mobile_platform_data i2c_platform_data = {
+       .clks_per_count = 2,
+};
+
 static struct platform_device i2c0_device = {
        .name           = "i2c-sh_mobile",
        .id             = 0,
        .resource       = i2c0_resources,
        .num_resources  = ARRAY_SIZE(i2c0_resources),
+       .dev            = {
+               .platform_data  = &i2c_platform_data,
+       },
 };
 
 static struct platform_device i2c1_device = {
@@ -204,6 +212,9 @@ static struct platform_device i2c1_device = {
        .id             = 1,
        .resource       = i2c1_resources,
        .num_resources  = ARRAY_SIZE(i2c1_resources),
+       .dev            = {
+               .platform_data  = &i2c_platform_data,
+       },
 };
 
 static struct platform_device i2c2_device = {
@@ -211,6 +222,9 @@ static struct platform_device i2c2_device = {
        .id             = 2,
        .resource       = i2c2_resources,
        .num_resources  = ARRAY_SIZE(i2c2_resources),
+       .dev            = {
+               .platform_data  = &i2c_platform_data,
+       },
 };
 
 static struct platform_device i2c3_device = {
@@ -218,6 +232,9 @@ static struct platform_device i2c3_device = {
        .id             = 3,
        .resource       = i2c3_resources,
        .num_resources  = ARRAY_SIZE(i2c3_resources),
+       .dev            = {
+               .platform_data  = &i2c_platform_data,
+       },
 };
 
 static struct platform_device i2c4_device = {
@@ -225,6 +242,9 @@ static struct platform_device i2c4_device = {
        .id             = 4,
        .resource       = i2c4_resources,
        .num_resources  = ARRAY_SIZE(i2c4_resources),
+       .dev            = {
+               .platform_data  = &i2c_platform_data,
+       },
 };
 
 static const struct sh_dmae_slave_config sh73a0_dmae_slaves[] = {
index da7be13..ab95f53 100644 (file)
@@ -99,42 +99,42 @@ static inline void tegra_irq_write_mask(unsigned int irq, unsigned long reg)
 
 static void tegra_mask(struct irq_data *d)
 {
-       if (d->irq < FIRST_LEGACY_IRQ)
+       if (d->hwirq < FIRST_LEGACY_IRQ)
                return;
 
-       tegra_irq_write_mask(d->irq, ICTLR_CPU_IER_CLR);
+       tegra_irq_write_mask(d->hwirq, ICTLR_CPU_IER_CLR);
 }
 
 static void tegra_unmask(struct irq_data *d)
 {
-       if (d->irq < FIRST_LEGACY_IRQ)
+       if (d->hwirq < FIRST_LEGACY_IRQ)
                return;
 
-       tegra_irq_write_mask(d->irq, ICTLR_CPU_IER_SET);
+       tegra_irq_write_mask(d->hwirq, ICTLR_CPU_IER_SET);
 }
 
 static void tegra_ack(struct irq_data *d)
 {
-       if (d->irq < FIRST_LEGACY_IRQ)
+       if (d->hwirq < FIRST_LEGACY_IRQ)
                return;
 
-       tegra_irq_write_mask(d->irq, ICTLR_CPU_IEP_FIR_CLR);
+       tegra_irq_write_mask(d->hwirq, ICTLR_CPU_IEP_FIR_CLR);
 }
 
 static void tegra_eoi(struct irq_data *d)
 {
-       if (d->irq < FIRST_LEGACY_IRQ)
+       if (d->hwirq < FIRST_LEGACY_IRQ)
                return;
 
-       tegra_irq_write_mask(d->irq, ICTLR_CPU_IEP_FIR_CLR);
+       tegra_irq_write_mask(d->hwirq, ICTLR_CPU_IEP_FIR_CLR);
 }
 
 static int tegra_retrigger(struct irq_data *d)
 {
-       if (d->irq < FIRST_LEGACY_IRQ)
+       if (d->hwirq < FIRST_LEGACY_IRQ)
                return 0;
 
-       tegra_irq_write_mask(d->irq, ICTLR_CPU_IEP_FIR_SET);
+       tegra_irq_write_mask(d->hwirq, ICTLR_CPU_IEP_FIR_SET);
 
        return 1;
 }
@@ -142,7 +142,7 @@ static int tegra_retrigger(struct irq_data *d)
 #ifdef CONFIG_PM_SLEEP
 static int tegra_set_wake(struct irq_data *d, unsigned int enable)
 {
-       u32 irq = d->irq;
+       u32 irq = d->hwirq;
        u32 index, mask;
 
        if (irq < FIRST_LEGACY_IRQ ||
index ae69809..7eb94e6 100644 (file)
@@ -798,6 +798,7 @@ config NEED_KUSER_HELPERS
 
 config KUSER_HELPERS
        bool "Enable kuser helpers in vector page" if !NEED_KUSER_HELPERS
+       depends on MMU
        default y
        help
          Warning: disabling this option may break user programs.
index b3a9478..22ac2a6 100644 (file)
@@ -270,7 +270,6 @@ __v7_pj4b_setup:
 /* Auxiliary Debug Modes Control 1 Register */
 #define PJ4B_STATIC_BP (1 << 2) /* Enable Static BP */
 #define PJ4B_INTER_PARITY (1 << 8) /* Disable Internal Parity Handling */
-#define PJ4B_BCK_OFF_STREX (1 << 5) /* Enable the back off of STREX instr */
 #define PJ4B_CLEAN_LINE (1 << 16) /* Disable data transfer for clean line */
 
 /* Auxiliary Debug Modes Control 2 Register */
@@ -293,7 +292,6 @@ __v7_pj4b_setup:
        /* Auxiliary Debug Modes Control 1 Register */
        mrc     p15, 1, r0, c15, c1, 1
        orr     r0, r0, #PJ4B_CLEAN_LINE
-       orr     r0, r0, #PJ4B_BCK_OFF_STREX
        orr     r0, r0, #PJ4B_INTER_PARITY
        bic     r0, r0, #PJ4B_STATIC_BP
        mcr     p15, 1, r0, c15, c1, 1
index 23259f1..afa2b3c 100644 (file)
@@ -535,7 +535,7 @@ ENTRY(cpu_xscale_do_suspend)
        mrc     p15, 0, r5, c15, c1, 0  @ CP access reg
        mrc     p15, 0, r6, c13, c0, 0  @ PID
        mrc     p15, 0, r7, c3, c0, 0   @ domain ID
-       mrc     p15, 0, r8, c1, c1, 0   @ auxiliary control reg
+       mrc     p15, 0, r8, c1, c0, 1   @ auxiliary control reg
        mrc     p15, 0, r9, c1, c0, 0   @ control reg
        bic     r4, r4, #2              @ clear frequency change bit
        stmia   r0, {r4 - r9}           @ store cp regs
@@ -552,7 +552,7 @@ ENTRY(cpu_xscale_do_resume)
        mcr     p15, 0, r6, c13, c0, 0  @ PID
        mcr     p15, 0, r7, c3, c0, 0   @ domain ID
        mcr     p15, 0, r1, c2, c0, 0   @ translation table base addr
-       mcr     p15, 0, r8, c1, c1, 0   @ auxiliary control reg
+       mcr     p15, 0, r8, c1, c0, 1   @ auxiliary control reg
        mov     r0, r9                  @ control register
        b       cpu_resume_mmu
 ENDPROC(cpu_xscale_do_resume)
index b61a3bc..e048f61 100644 (file)
@@ -497,6 +497,34 @@ static void orion_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
 #define orion_gpio_dbg_show NULL
 #endif
 
+static void orion_gpio_unmask_irq(struct irq_data *d)
+{
+       struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+       struct irq_chip_type *ct = irq_data_get_chip_type(d);
+       u32 reg_val;
+       u32 mask = d->mask;
+
+       irq_gc_lock(gc);
+       reg_val = irq_reg_readl(gc->reg_base + ct->regs.mask);
+       reg_val |= mask;
+       irq_reg_writel(reg_val, gc->reg_base + ct->regs.mask);
+       irq_gc_unlock(gc);
+}
+
+static void orion_gpio_mask_irq(struct irq_data *d)
+{
+       struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+       struct irq_chip_type *ct = irq_data_get_chip_type(d);
+       u32 mask = d->mask;
+       u32 reg_val;
+
+       irq_gc_lock(gc);
+       reg_val = irq_reg_readl(gc->reg_base + ct->regs.mask);
+       reg_val &= ~mask;
+       irq_reg_writel(reg_val, gc->reg_base + ct->regs.mask);
+       irq_gc_unlock(gc);
+}
+
 void __init orion_gpio_init(struct device_node *np,
                            int gpio_base, int ngpio,
                            void __iomem *base, int mask_offset,
@@ -565,8 +593,8 @@ void __init orion_gpio_init(struct device_node *np,
        ct = gc->chip_types;
        ct->regs.mask = ochip->mask_offset + GPIO_LEVEL_MASK_OFF;
        ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW;
-       ct->chip.irq_mask = irq_gc_mask_clr_bit;
-       ct->chip.irq_unmask = irq_gc_mask_set_bit;
+       ct->chip.irq_mask = orion_gpio_mask_irq;
+       ct->chip.irq_unmask = orion_gpio_unmask_irq;
        ct->chip.irq_set_type = gpio_irq_set_type;
        ct->chip.name = ochip->chip.label;
 
@@ -575,8 +603,8 @@ void __init orion_gpio_init(struct device_node *np,
        ct->regs.ack = GPIO_EDGE_CAUSE_OFF;
        ct->type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
        ct->chip.irq_ack = irq_gc_ack_clr_bit;
-       ct->chip.irq_mask = irq_gc_mask_clr_bit;
-       ct->chip.irq_unmask = irq_gc_mask_set_bit;
+       ct->chip.irq_mask = orion_gpio_mask_irq;
+       ct->chip.irq_unmask = orion_gpio_unmask_irq;
        ct->chip.irq_set_type = gpio_irq_set_type;
        ct->handler = handle_edge_irq;
        ct->chip.name = ochip->chip.label;
index 295c72d..f1ad9c2 100644 (file)
                        compatible = "apm,xgene-enet";
                        status = "disabled";
                        reg = <0x0 0x17020000 0x0 0xd100>,
-                             <0x0 0X17030000 0x0 0X400>,
+                             <0x0 0X17030000 0x0 0Xc300>,
                              <0x0 0X10000000 0x0 0X200>;
                        reg-names = "enet_csr", "ring_csr", "ring_cmd";
                        interrupts = <0x0 0x3c 0x4>;
                sgenet0: ethernet@1f210000 {
                        compatible = "apm,xgene-enet";
                        status = "disabled";
-                       reg = <0x0 0x1f210000 0x0 0x10000>,
-                             <0x0 0x1f200000 0x0 0X10000>,
-                             <0x0 0x1B000000 0x0 0X20000>;
+                       reg = <0x0 0x1f210000 0x0 0xd100>,
+                             <0x0 0x1f200000 0x0 0Xc300>,
+                             <0x0 0x1B000000 0x0 0X200>;
                        reg-names = "enet_csr", "ring_csr", "ring_cmd";
                        interrupts = <0x0 0xA0 0x4>;
                        dma-coherent;
                        compatible = "apm,xgene-enet";
                        status = "disabled";
                        reg = <0x0 0x1f610000 0x0 0xd100>,
-                             <0x0 0x1f600000 0x0 0X400>,
+                             <0x0 0x1f600000 0x0 0Xc300>,
                              <0x0 0x18000000 0x0 0X200>;
                        reg-names = "enet_csr", "ring_csr", "ring_cmd";
                        interrupts = <0x0 0x60 0x4>;
index 4ce602c..dd301be 100644 (file)
@@ -35,6 +35,9 @@ CONFIG_MODULE_UNLOAD=y
 CONFIG_ARCH_THUNDER=y
 CONFIG_ARCH_VEXPRESS=y
 CONFIG_ARCH_XGENE=y
+CONFIG_PCI=y
+CONFIG_PCI_MSI=y
+CONFIG_PCI_XGENE=y
 CONFIG_SMP=y
 CONFIG_PREEMPT=y
 CONFIG_KSM=y
@@ -52,6 +55,7 @@ CONFIG_IP_PNP_DHCP=y
 CONFIG_IP_PNP_BOOTP=y
 # CONFIG_INET_LRO is not set
 # CONFIG_IPV6 is not set
+CONFIG_BPF_JIT=y
 # CONFIG_WIRELESS is not set
 CONFIG_NET_9P=y
 CONFIG_NET_9P_VIRTIO=y
@@ -65,16 +69,17 @@ CONFIG_VIRTIO_BLK=y
 CONFIG_BLK_DEV_SD=y
 # CONFIG_SCSI_LOWLEVEL is not set
 CONFIG_ATA=y
+CONFIG_SATA_AHCI=y
+CONFIG_SATA_AHCI_PLATFORM=y
 CONFIG_AHCI_XGENE=y
-CONFIG_PHY_XGENE=y
 CONFIG_PATA_PLATFORM=y
 CONFIG_PATA_OF_PLATFORM=y
 CONFIG_NETDEVICES=y
 CONFIG_TUN=y
 CONFIG_VIRTIO_NET=y
+CONFIG_NET_XGENE=y
 CONFIG_SMC91X=y
 CONFIG_SMSC911X=y
-CONFIG_NET_XGENE=y
 # CONFIG_WLAN is not set
 CONFIG_INPUT_EVDEV=y
 # CONFIG_SERIO_SERPORT is not set
@@ -87,6 +92,11 @@ CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
 CONFIG_SERIAL_OF_PLATFORM=y
 CONFIG_VIRTIO_CONSOLE=y
 # CONFIG_HW_RANDOM is not set
+# CONFIG_HMC_DRV is not set
+CONFIG_SPI=y
+CONFIG_SPI_PL022=y
+CONFIG_GPIO_PL061=y
+CONFIG_GPIO_XGENE=y
 # CONFIG_HWMON is not set
 CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
@@ -97,13 +107,25 @@ CONFIG_LOGO=y
 # CONFIG_LOGO_LINUX_MONO is not set
 # CONFIG_LOGO_LINUX_VGA16 is not set
 CONFIG_USB=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_HCD_PLATFORM=y
 CONFIG_USB_ISP1760_HCD=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_OHCI_HCD_PLATFORM=y
 CONFIG_USB_STORAGE=y
+CONFIG_USB_ULPI=y
 CONFIG_MMC=y
 CONFIG_MMC_ARMMMCI=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SPI=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_EFI=y
+CONFIG_RTC_DRV_XGENE=y
 CONFIG_VIRTIO_BALLOON=y
 CONFIG_VIRTIO_MMIO=y
 # CONFIG_IOMMU_SUPPORT is not set
+CONFIG_PHY_XGENE=y
 CONFIG_EXT2_FS=y
 CONFIG_EXT3_FS=y
 # CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
index ccc7087..a62cd07 100644 (file)
@@ -142,7 +142,7 @@ static inline void *phys_to_virt(phys_addr_t x)
  *  virt_to_page(k)    convert a _valid_ virtual address to struct page *
  *  virt_addr_valid(k) indicates whether a virtual address is valid
  */
-#define ARCH_PFN_OFFSET                PHYS_PFN_OFFSET
+#define ARCH_PFN_OFFSET                ((unsigned long)PHYS_PFN_OFFSET)
 
 #define virt_to_page(kaddr)    pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
 #define        virt_addr_valid(kaddr)  pfn_valid(__pa(kaddr) >> PAGE_SHIFT)
index da1f06b..9dfdac4 100644 (file)
@@ -792,3 +792,5 @@ __SYSCALL(__NR_renameat2, sys_renameat2)
 __SYSCALL(__NR_getrandom, sys_getrandom)
 #define __NR_memfd_create 385
 __SYSCALL(__NR_memfd_create, sys_memfd_create)
+#define __NR_bpf 386
+__SYSCALL(__NR_bpf, sys_bpf)
index 619b1dd..d18a449 100644 (file)
@@ -54,18 +54,17 @@ ENTRY(efi_stub_entry)
        b.eq    efi_load_fail
 
        /*
-        * efi_entry() will have relocated the kernel image if necessary
-        * and we return here with device tree address in x0 and the kernel
-        * entry point stored at *image_addr. Save those values in registers
-        * which are callee preserved.
+        * efi_entry() will have copied the kernel image if necessary and we
+        * return here with device tree address in x0 and the kernel entry
+        * point stored at *image_addr. Save those values in registers which
+        * are callee preserved.
         */
        mov     x20, x0         // DTB address
        ldr     x0, [sp, #16]   // relocated _text address
        mov     x21, x0
 
        /*
-        * Flush dcache covering current runtime addresses
-        * of kernel text/data. Then flush all of icache.
+        * Calculate size of the kernel Image (same for original and copy).
         */
        adrp    x1, _text
        add     x1, x1, #:lo12:_text
@@ -73,9 +72,24 @@ ENTRY(efi_stub_entry)
        add     x2, x2, #:lo12:_edata
        sub     x1, x2, x1
 
+       /*
+        * Flush the copied Image to the PoC, and ensure it is not shadowed by
+        * stale icache entries from before relocation.
+        */
        bl      __flush_dcache_area
        ic      ialluis
 
+       /*
+        * Ensure that the rest of this function (in the original Image) is
+        * visible when the caches are disabled. The I-cache can't have stale
+        * entries for the VA range of the current image, so no maintenance is
+        * necessary.
+        */
+       adr     x0, efi_stub_entry
+       adr     x1, efi_stub_entry_end
+       sub     x1, x1, x0
+       bl      __flush_dcache_area
+
        /* Turn off Dcache and MMU */
        mrs     x0, CurrentEL
        cmp     x0, #CurrentEL_EL2
@@ -105,4 +119,5 @@ efi_load_fail:
        ldp     x29, x30, [sp], #32
        ret
 
+efi_stub_entry_end:
 ENDPROC(efi_stub_entry)
index e007714..8cd27fe 100644 (file)
@@ -163,9 +163,10 @@ static int __kprobes aarch64_insn_patch_text_cb(void *arg)
                 * which ends with "dsb; isb" pair guaranteeing global
                 * visibility.
                 */
-               atomic_set(&pp->cpu_count, -1);
+               /* Notify other processors with an additional increment. */
+               atomic_inc(&pp->cpu_count);
        } else {
-               while (atomic_read(&pp->cpu_count) != -1)
+               while (atomic_read(&pp->cpu_count) <= num_online_cpus())
                        cpu_relax();
                isb();
        }
index 866c1c8..663da77 100644 (file)
@@ -528,7 +528,7 @@ static int __maybe_unused cpu_psci_cpu_suspend(unsigned long index)
        if (WARN_ON_ONCE(!index))
                return -EINVAL;
 
-       if (state->type == PSCI_POWER_STATE_TYPE_STANDBY)
+       if (state[index - 1].type == PSCI_POWER_STATE_TYPE_STANDBY)
                ret = psci_ops.cpu_suspend(state[index - 1], 0);
        else
                ret = __cpu_suspend(index, psci_suspend_finisher);
index 4cc3b71..3d7c2df 100644 (file)
@@ -424,6 +424,11 @@ static const struct sys_reg_desc sys_reg_descs[] = {
        /* VBAR_EL1 */
        { Op0(0b11), Op1(0b000), CRn(0b1100), CRm(0b0000), Op2(0b000),
          NULL, reset_val, VBAR_EL1, 0 },
+
+       /* ICC_SRE_EL1 */
+       { Op0(0b11), Op1(0b000), CRn(0b1100), CRm(0b1100), Op2(0b101),
+         trap_raz_wi },
+
        /* CONTEXTIDR_EL1 */
        { Op0(0b11), Op1(0b000), CRn(0b1101), CRm(0b0000), Op2(0b001),
          access_vm_reg, reset_val, CONTEXTIDR_EL1, 0 },
@@ -690,6 +695,10 @@ static const struct sys_reg_desc cp15_regs[] = {
        { Op1( 0), CRn(10), CRm( 2), Op2( 1), access_vm_reg, NULL, c10_NMRR },
        { Op1( 0), CRn(10), CRm( 3), Op2( 0), access_vm_reg, NULL, c10_AMAIR0 },
        { Op1( 0), CRn(10), CRm( 3), Op2( 1), access_vm_reg, NULL, c10_AMAIR1 },
+
+       /* ICC_SRE */
+       { Op1( 0), CRn(12), CRm(12), Op2( 5), trap_raz_wi },
+
        { Op1( 0), CRn(13), CRm( 0), Op2( 1), access_vm_reg, NULL, c13_CID },
 };
 
index 6e0ed93..c17967f 100644 (file)
@@ -46,7 +46,7 @@ USER(9f, strh wzr, [x0], #2   )
        sub     x1, x1, #2
 4:     adds    x1, x1, #1
        b.mi    5f
-       strb    wzr, [x0]
+USER(9f, strb  wzr, [x0]       )
 5:     mov     x0, #0
        ret
 ENDPROC(__clear_user)
index 0bf90d2..f4f8b50 100644 (file)
@@ -202,7 +202,7 @@ static void __init alloc_init_pmd(pud_t *pud, unsigned long addr,
 }
 
 static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr,
-                                 unsigned long end, unsigned long phys,
+                                 unsigned long end, phys_addr_t phys,
                                  int map_io)
 {
        pud_t *pud;
index ec6b9ac..dbe46f4 100644 (file)
@@ -1563,7 +1563,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
 
        for (i = 0; i < npages; i++) {
                pfn = gfn_to_pfn(kvm, base_gfn + i);
-               if (!kvm_is_mmio_pfn(pfn)) {
+               if (!kvm_is_reserved_pfn(pfn)) {
                        kvm_set_pmt_entry(kvm, base_gfn + i,
                                        pfn << PAGE_SHIFT,
                                _PAGE_AR_RWX | _PAGE_MA_WB);
index 4ef7a54..75e75d7 100644 (file)
@@ -4,7 +4,7 @@
 #include <uapi/asm/unistd.h>
 
 
-#define NR_syscalls            354
+#define NR_syscalls            355
 
 #define __ARCH_WANT_OLD_READDIR
 #define __ARCH_WANT_OLD_STAT
index b419c6b..2c1bec9 100644 (file)
 #define __NR_renameat2         351
 #define __NR_getrandom         352
 #define __NR_memfd_create      353
+#define __NR_bpf               354
 
 #endif /* _UAPI_ASM_M68K_UNISTD_H_ */
index 05b46c2..2ca219e 100644 (file)
@@ -374,4 +374,5 @@ ENTRY(sys_call_table)
        .long sys_renameat2
        .long sys_getrandom
        .long sys_memfd_create
+       .long sys_bpf
 
index f43aa53..9536ef9 100644 (file)
@@ -2101,9 +2101,17 @@ config 64BIT_PHYS_ADDR
 config ARCH_PHYS_ADDR_T_64BIT
        def_bool 64BIT_PHYS_ADDR
 
+choice
+       prompt "SmartMIPS or microMIPS ASE support"
+
+config CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS
+       bool "None"
+       help
+         Select this if you want neither microMIPS nor SmartMIPS support
+
 config CPU_HAS_SMARTMIPS
        depends on SYS_SUPPORTS_SMARTMIPS
-       bool "Support for the SmartMIPS ASE"
+       bool "SmartMIPS"
        help
          SmartMIPS is a extension of the MIPS32 architecture aimed at
          increased security at both hardware and software level for
@@ -2115,11 +2123,13 @@ config CPU_HAS_SMARTMIPS
 
 config CPU_MICROMIPS
        depends on SYS_SUPPORTS_MICROMIPS
-       bool "Build kernel using microMIPS ISA"
+       bool "microMIPS"
        help
          When this option is enabled the kernel will be built using the
          microMIPS ISA
 
+endchoice
+
 config CPU_HAS_MSA
        bool "Support for the MIPS SIMD Architecture (EXPERIMENTAL)"
        depends on CPU_SUPPORTS_MSA
index 23cb948..5807647 100644 (file)
@@ -93,6 +93,15 @@ LDFLAGS_vmlinux                      += -G 0 -static -n -nostdlib
 KBUILD_AFLAGS_MODULE           += -mlong-calls
 KBUILD_CFLAGS_MODULE           += -mlong-calls
 
+#
+# pass -msoft-float to GAS if it supports it.  However on newer binutils
+# (specifically newer than 2.24.51.20140728) we then also need to explicitly
+# set ".set hardfloat" in all files which manipulate floating point registers.
+#
+ifneq ($(call as-option,-Wa$(comma)-msoft-float,),)
+       cflags-y                += -DGAS_HAS_SET_HARDFLOAT -Wa,-msoft-float
+endif
+
 cflags-y += -ffreestanding
 
 #
index 7417340..2bc4aa9 100644 (file)
@@ -809,6 +809,7 @@ static struct irq_chip octeon_irq_chip_ciu_gpio_v2 = {
        .irq_set_type = octeon_irq_ciu_gpio_set_type,
 #ifdef CONFIG_SMP
        .irq_set_affinity = octeon_irq_ciu_set_affinity_v2,
+       .irq_cpu_offline = octeon_irq_cpu_offline_ciu,
 #endif
        .flags = IRQCHIP_SET_TYPE_MASKED,
 };
@@ -823,6 +824,7 @@ static struct irq_chip octeon_irq_chip_ciu_gpio = {
        .irq_set_type = octeon_irq_ciu_gpio_set_type,
 #ifdef CONFIG_SMP
        .irq_set_affinity = octeon_irq_ciu_set_affinity,
+       .irq_cpu_offline = octeon_irq_cpu_offline_ciu,
 #endif
        .flags = IRQCHIP_SET_TYPE_MASKED,
 };
index e38c281..cdac7b3 100644 (file)
@@ -13,6 +13,8 @@
 #include <asm/mipsregs.h>
 
        .macro  fpu_save_single thread tmp=t0
+       .set push
+       SET_HARDFLOAT
        cfc1    \tmp,  fcr31
        swc1    $f0,  THREAD_FPR0_LS64(\thread)
        swc1    $f1,  THREAD_FPR1_LS64(\thread)
        swc1    $f30, THREAD_FPR30_LS64(\thread)
        swc1    $f31, THREAD_FPR31_LS64(\thread)
        sw      \tmp, THREAD_FCR31(\thread)
+       .set pop
        .endm
 
        .macro  fpu_restore_single thread tmp=t0
+       .set push
+       SET_HARDFLOAT
        lw      \tmp, THREAD_FCR31(\thread)
        lwc1    $f0,  THREAD_FPR0_LS64(\thread)
        lwc1    $f1,  THREAD_FPR1_LS64(\thread)
@@ -84,6 +89,7 @@
        lwc1    $f30, THREAD_FPR30_LS64(\thread)
        lwc1    $f31, THREAD_FPR31_LS64(\thread)
        ctc1    \tmp, fcr31
+       .set pop
        .endm
 
        .macro  cpu_save_nonscratch thread
index cd9a98b..6caf876 100644 (file)
@@ -57,6 +57,8 @@
 #endif /* CONFIG_CPU_MIPSR2 */
 
        .macro  fpu_save_16even thread tmp=t0
+       .set    push
+       SET_HARDFLOAT
        cfc1    \tmp, fcr31
        sdc1    $f0,  THREAD_FPR0_LS64(\thread)
        sdc1    $f2,  THREAD_FPR2_LS64(\thread)
        sdc1    $f28, THREAD_FPR28_LS64(\thread)
        sdc1    $f30, THREAD_FPR30_LS64(\thread)
        sw      \tmp, THREAD_FCR31(\thread)
+       .set    pop
        .endm
 
        .macro  fpu_save_16odd thread
        .set    push
        .set    mips64r2
+       SET_HARDFLOAT
        sdc1    $f1,  THREAD_FPR1_LS64(\thread)
        sdc1    $f3,  THREAD_FPR3_LS64(\thread)
        sdc1    $f5,  THREAD_FPR5_LS64(\thread)
        .endm
 
        .macro  fpu_restore_16even thread tmp=t0
+       .set    push
+       SET_HARDFLOAT
        lw      \tmp, THREAD_FCR31(\thread)
        ldc1    $f0,  THREAD_FPR0_LS64(\thread)
        ldc1    $f2,  THREAD_FPR2_LS64(\thread)
        .macro  fpu_restore_16odd thread
        .set    push
        .set    mips64r2
+       SET_HARDFLOAT
        ldc1    $f1,  THREAD_FPR1_LS64(\thread)
        ldc1    $f3,  THREAD_FPR3_LS64(\thread)
        ldc1    $f5,  THREAD_FPR5_LS64(\thread)
        .macro  cfcmsa  rd, cs
        .set    push
        .set    noat
+       SET_HARDFLOAT
        .insn
        .word   CFC_MSA_INSN | (\cs << 11)
        move    \rd, $1
        .macro  ctcmsa  cd, rs
        .set    push
        .set    noat
+       SET_HARDFLOAT
        move    $1, \rs
        .word   CTC_MSA_INSN | (\cd << 6)
        .set    pop
        .macro  ld_d    wd, off, base
        .set    push
        .set    noat
+       SET_HARDFLOAT
        add     $1, \base, \off
        .word   LDD_MSA_INSN | (\wd << 6)
        .set    pop
        .macro  st_d    wd, off, base
        .set    push
        .set    noat
+       SET_HARDFLOAT
        add     $1, \base, \off
        .word   STD_MSA_INSN | (\wd << 6)
        .set    pop
        .macro  copy_u_w        rd, ws, n
        .set    push
        .set    noat
+       SET_HARDFLOAT
        .insn
        .word   COPY_UW_MSA_INSN | (\n << 16) | (\ws << 11)
        /* move triggers an assembler bug... */
        .macro  copy_u_d        rd, ws, n
        .set    push
        .set    noat
+       SET_HARDFLOAT
        .insn
        .word   COPY_UD_MSA_INSN | (\n << 16) | (\ws << 11)
        /* move triggers an assembler bug... */
        .macro  insert_w        wd, n, rs
        .set    push
        .set    noat
+       SET_HARDFLOAT
        /* move triggers an assembler bug... */
        or      $1, \rs, zero
        .word   INSERT_W_MSA_INSN | (\n << 16) | (\wd << 6)
        .macro  insert_d        wd, n, rs
        .set    push
        .set    noat
+       SET_HARDFLOAT
        /* move triggers an assembler bug... */
        or      $1, \rs, zero
        .word   INSERT_D_MSA_INSN | (\n << 16) | (\wd << 6)
        st_d    31, THREAD_FPR31, \thread
        .set    push
        .set    noat
+       SET_HARDFLOAT
        cfcmsa  $1, MSA_CSR
        sw      $1, THREAD_MSA_CSR(\thread)
        .set    pop
        .macro  msa_restore_all thread
        .set    push
        .set    noat
+       SET_HARDFLOAT
        lw      $1, THREAD_MSA_CSR(\thread)
        ctcmsa  MSA_CSR, $1
        .set    pop
        .macro  msa_init_all_upper
        .set    push
        .set    noat
+       SET_HARDFLOAT
        not     $1, zero
        msa_init_upper  0
        .set    pop
index 429481f..f184ba0 100644 (file)
 
 #include <asm/sgidefs.h>
 
+/*
+ * starting with binutils 2.24.51.20140729, MIPS binutils warn about mixing
+ * hardfloat and softfloat object files.  The kernel build uses soft-float by
+ * default, so we also need to pass -msoft-float along to GAS if it supports it.
+ * But this in turn causes assembler errors in files which access hardfloat
+ * registers.  We detect if GAS supports "-msoft-float" in the Makefile and
+ * explicitly put ".set hardfloat" where floating point registers are touched.
+ */
+#ifdef GAS_HAS_SET_HARDFLOAT
+#define SET_HARDFLOAT .set hardfloat
+#else
+#define SET_HARDFLOAT
+#endif
+
 #if _MIPS_SIM == _MIPS_SIM_ABI32
 
 /*
index 4d0aeda..dd56241 100644 (file)
@@ -145,8 +145,8 @@ static inline void lose_fpu(int save)
        if (is_msa_enabled()) {
                if (save) {
                        save_msa(current);
-                       asm volatile("cfc1 %0, $31"
-                               : "=r"(current->thread.fpu.fcr31));
+                       current->thread.fpu.fcr31 =
+                                       read_32bit_cp1_register(CP1_STATUS);
                }
                disable_msa();
                clear_thread_flag(TIF_USEDMSA);
index e194f95..fdbff44 100644 (file)
 #define WORD_INSN ".word"
 #endif
 
+#ifdef CONFIG_CPU_MICROMIPS
+#define NOP_INSN "nop32"
+#else
+#define NOP_INSN "nop"
+#endif
+
 static __always_inline bool arch_static_branch(struct static_key *key)
 {
-       asm_volatile_goto("1:\tnop\n\t"
+       asm_volatile_goto("1:\t" NOP_INSN "\n\t"
                "nop\n\t"
                ".pushsection __jump_table,  \"aw\"\n\t"
                WORD_INSN " 1b, %l[l_yes], %0\n\t"
index 7d28f95..6d69332 100644 (file)
 #define cpu_has_mcheck         0
 #define cpu_has_mdmx           0
 #define cpu_has_mips16         0
-#define cpu_has_mips32r1       0
 #define cpu_has_mips32r2       0
 #define cpu_has_mips3d         0
-#define cpu_has_mips64r1       0
 #define cpu_has_mips64r2       0
 #define cpu_has_mipsmt         0
 #define cpu_has_prefetch       0
index cf3b580..22a135a 100644 (file)
 #define MIPS_CONF6_SYND                (_ULCAST_(1) << 13)
 /* proAptiv FTLB on/off bit */
 #define MIPS_CONF6_FTLBEN      (_ULCAST_(1) << 15)
+/* FTLB probability bits */
+#define MIPS_CONF6_FTLBP_SHIFT (16)
 
 #define MIPS_CONF7_WII         (_ULCAST_(1) << 31)
 
@@ -1324,7 +1326,7 @@ do {                                                                      \
 /*
  * Macros to access the floating point coprocessor control registers
  */
-#define read_32bit_cp1_register(source)                                        \
+#define _read_32bit_cp1_register(source, gas_hardfloat)                        \
 ({                                                                     \
        int __res;                                                      \
                                                                        \
@@ -1334,12 +1336,21 @@ do {                                                                    \
        "       # gas fails to assemble cfc1 for some archs,    \n"     \
        "       # like Octeon.                                  \n"     \
        "       .set    mips1                                   \n"     \
+       "       "STR(gas_hardfloat)"                            \n"     \
        "       cfc1    %0,"STR(source)"                        \n"     \
        "       .set    pop                                     \n"     \
        : "=r" (__res));                                                \
        __res;                                                          \
 })
 
+#ifdef GAS_HAS_SET_HARDFLOAT
+#define read_32bit_cp1_register(source)                                        \
+       _read_32bit_cp1_register(source, .set hardfloat)
+#else
+#define read_32bit_cp1_register(source)                                        \
+       _read_32bit_cp1_register(source, )
+#endif
+
 #ifdef HAVE_AS_DSP
 #define rddsp(mask)                                                    \
 ({                                                                     \
index 4520adc..cd6e0af 100644 (file)
@@ -257,7 +257,11 @@ static inline void protected_flush_icache_line(unsigned long addr)
  */
 static inline void protected_writeback_dcache_line(unsigned long addr)
 {
+#ifdef CONFIG_EVA
+       protected_cachee_op(Hit_Writeback_Inv_D, addr);
+#else
        protected_cache_op(Hit_Writeback_Inv_D, addr);
+#endif
 }
 
 static inline void protected_writeback_scache_line(unsigned long addr)
index a109510..22a5624 100644 (file)
@@ -301,7 +301,8 @@ do {                                                                        \
                        __get_kernel_common((x), size, __gu_ptr);       \
                else                                                    \
                        __get_user_common((x), size, __gu_ptr);         \
-       }                                                               \
+       } else                                                          \
+               (x) = 0;                                                \
                                                                        \
        __gu_err;                                                       \
 })
@@ -316,6 +317,7 @@ do {                                                                        \
        "       .insn                                           \n"     \
        "       .section .fixup,\"ax\"                          \n"     \
        "3:     li      %0, %4                                  \n"     \
+       "       move    %1, $0                                  \n"     \
        "       j       2b                                      \n"     \
        "       .previous                                       \n"     \
        "       .section __ex_table,\"a\"                       \n"     \
@@ -630,6 +632,7 @@ do {                                                                        \
        "       .insn                                           \n"     \
        "       .section .fixup,\"ax\"                          \n"     \
        "3:     li      %0, %4                                  \n"     \
+       "       move    %1, $0                                  \n"     \
        "       j       2b                                      \n"     \
        "       .previous                                       \n"     \
        "       .section __ex_table,\"a\"                       \n"     \
@@ -773,10 +776,11 @@ extern void __put_user_unaligned_unknown(void);
        "jal\t" #destination "\n\t"
 #endif
 
-#ifndef CONFIG_CPU_DADDI_WORKAROUNDS
-#define DADDI_SCRATCH "$0"
-#else
+#if defined(CONFIG_CPU_DADDI_WORKAROUNDS) || (defined(CONFIG_EVA) &&   \
+                                             defined(CONFIG_CPU_HAS_PREFETCH))
 #define DADDI_SCRATCH "$3"
+#else
+#define DADDI_SCRATCH "$0"
 #endif
 
 extern size_t __copy_user(void *__to, const void *__from, size_t __n);
@@ -1418,7 +1422,7 @@ static inline long __strnlen_user(const char __user *s, long n)
 }
 
 /*
- * strlen_user: - Get the size of a string in user space.
+ * strnlen_user: - Get the size of a string in user space.
  * @str: The string to measure.
  *
  * Context: User context only. This function may sleep.
@@ -1427,9 +1431,7 @@ static inline long __strnlen_user(const char __user *s, long n)
  *
  * Returns the size of the string INCLUDING the terminating NUL.
  * On exception, returns 0.
- *
- * If there is a limit on the length of a valid string, you may wish to
- * consider using strnlen_user() instead.
+ * If the string is too long, returns a value greater than @n.
  */
 static inline long strnlen_user(const char __user *s, long n)
 {
index fdb4923..d001bb1 100644 (file)
 #define __NR_seccomp                   (__NR_Linux + 352)
 #define __NR_getrandom                 (__NR_Linux + 353)
 #define __NR_memfd_create              (__NR_Linux + 354)
+#define __NR_bpf                       (__NR_Linux + 355)
 
 /*
  * Offset of the last Linux o32 flavoured syscall
  */
-#define __NR_Linux_syscalls            354
+#define __NR_Linux_syscalls            355
 
 #endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */
 
 #define __NR_O32_Linux                 4000
-#define __NR_O32_Linux_syscalls                354
+#define __NR_O32_Linux_syscalls                355
 
 #if _MIPS_SIM == _MIPS_SIM_ABI64
 
 #define __NR_seccomp                   (__NR_Linux + 312)
 #define __NR_getrandom                 (__NR_Linux + 313)
 #define __NR_memfd_create              (__NR_Linux + 314)
+#define __NR_bpf                       (__NR_Linux + 315)
 
 /*
  * Offset of the last Linux 64-bit flavoured syscall
  */
-#define __NR_Linux_syscalls            314
+#define __NR_Linux_syscalls            315
 
 #endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */
 
 #define __NR_64_Linux                  5000
-#define __NR_64_Linux_syscalls         314
+#define __NR_64_Linux_syscalls         315
 
 #if _MIPS_SIM == _MIPS_SIM_NABI32
 
 #define __NR_seccomp                   (__NR_Linux + 316)
 #define __NR_getrandom                 (__NR_Linux + 317)
 #define __NR_memfd_create              (__NR_Linux + 318)
+#define __NR_bpf                       (__NR_Linux + 319)
 
 /*
  * Offset of the last N32 flavoured syscall
  */
-#define __NR_Linux_syscalls            318
+#define __NR_Linux_syscalls            319
 
 #endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */
 
 #define __NR_N32_Linux                 6000
-#define __NR_N32_Linux_syscalls                318
+#define __NR_N32_Linux_syscalls                319
 
 #endif /* _UAPI_ASM_UNISTD_H */
index 290c23b..8649507 100644 (file)
@@ -208,7 +208,6 @@ bmips_reset_nmi_vec_end:
 END(bmips_reset_nmi_vec)
 
        .set    pop
-       .previous
 
 /***********************************************************************
  * CPU1 warm restart vector (used for second and subsequent boots).
@@ -281,5 +280,3 @@ LEAF(bmips_enable_xks01)
        jr      ra
 
 END(bmips_enable_xks01)
-
-       .previous
index 7b2df22..4d7d99d 100644 (file)
@@ -144,7 +144,7 @@ int __mm_isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
                case mm_bc1t_op:
                        preempt_disable();
                        if (is_fpu_owner())
-                               asm volatile("cfc1\t%0,$31" : "=r" (fcr31));
+                               fcr31 = read_32bit_cp1_register(CP1_STATUS);
                        else
                                fcr31 = current->thread.fpu.fcr31;
                        preempt_enable();
@@ -562,11 +562,7 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
        case cop1_op:
                preempt_disable();
                if (is_fpu_owner())
-                       asm volatile(
-                               ".set push\n"
-                               "\t.set mips1\n"
-                               "\tcfc1\t%0,$31\n"
-                               "\t.set pop" : "=r" (fcr31));
+                       fcr31 = read_32bit_cp1_register(CP1_STATUS);
                else
                        fcr31 = current->thread.fpu.fcr31;
                preempt_enable();
index e6e97d2..0384b05 100644 (file)
@@ -229,6 +229,7 @@ LEAF(mips_cps_core_init)
         nop
 
        .set    push
+       .set    mips32r2
        .set    mt
 
        /* Only allow 1 TC per VPE to execute... */
@@ -345,6 +346,7 @@ LEAF(mips_cps_boot_vpes)
         nop
 
        .set    push
+       .set    mips32r2
        .set    mt
 
 1:     /* Enter VPE configuration state */
index 94c4a0c..dc49cf3 100644 (file)
@@ -193,6 +193,32 @@ static void set_isa(struct cpuinfo_mips *c, unsigned int isa)
 static char unknown_isa[] = KERN_ERR \
        "Unsupported ISA type, c0.config0: %d.";
 
+static unsigned int calculate_ftlb_probability(struct cpuinfo_mips *c)
+{
+
+       unsigned int probability = c->tlbsize / c->tlbsizevtlb;
+
+       /*
+        * 0 = All TLBWR instructions go to FTLB
+        * 1 = 15:1: For every 16 TBLWR instructions, 15 go to the
+        * FTLB and 1 goes to the VTLB.
+        * 2 = 7:1: As above with 7:1 ratio.
+        * 3 = 3:1: As above with 3:1 ratio.
+        *
+        * Use the linear midpoint as the probability threshold.
+        */
+       if (probability >= 12)
+               return 1;
+       else if (probability >= 6)
+               return 2;
+       else
+               /*
+                * So FTLB is less than 4 times bigger than VTLB.
+                * A 3:1 ratio can still be useful though.
+                */
+               return 3;
+}
+
 static void set_ftlb_enable(struct cpuinfo_mips *c, int enable)
 {
        unsigned int config6;
@@ -203,9 +229,14 @@ static void set_ftlb_enable(struct cpuinfo_mips *c, int enable)
        case CPU_P5600:
                /* proAptiv & related cores use Config6 to enable the FTLB */
                config6 = read_c0_config6();
+               /* Clear the old probability value */
+               config6 &= ~(3 << MIPS_CONF6_FTLBP_SHIFT);
                if (enable)
                        /* Enable FTLB */
-                       write_c0_config6(config6 | MIPS_CONF6_FTLBEN);
+                       write_c0_config6(config6 |
+                                        (calculate_ftlb_probability(c)
+                                         << MIPS_CONF6_FTLBP_SHIFT)
+                                        | MIPS_CONF6_FTLBEN);
                else
                        /* Disable FTLB */
                        write_c0_config6(config6 &  ~MIPS_CONF6_FTLBEN);
@@ -757,31 +788,34 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
                        c->cputype = CPU_LOONGSON2;
                        __cpu_name[cpu] = "ICT Loongson-2";
                        set_elf_platform(cpu, "loongson2e");
+                       set_isa(c, MIPS_CPU_ISA_III);
                        break;
                case PRID_REV_LOONGSON2F:
                        c->cputype = CPU_LOONGSON2;
                        __cpu_name[cpu] = "ICT Loongson-2";
                        set_elf_platform(cpu, "loongson2f");
+                       set_isa(c, MIPS_CPU_ISA_III);
                        break;
                case PRID_REV_LOONGSON3A:
                        c->cputype = CPU_LOONGSON3;
-                       c->writecombine = _CACHE_UNCACHED_ACCELERATED;
                        __cpu_name[cpu] = "ICT Loongson-3";
                        set_elf_platform(cpu, "loongson3a");
+                       set_isa(c, MIPS_CPU_ISA_M64R1);
                        break;
                case PRID_REV_LOONGSON3B_R1:
                case PRID_REV_LOONGSON3B_R2:
                        c->cputype = CPU_LOONGSON3;
                        __cpu_name[cpu] = "ICT Loongson-3";
                        set_elf_platform(cpu, "loongson3b");
+                       set_isa(c, MIPS_CPU_ISA_M64R1);
                        break;
                }
 
-               set_isa(c, MIPS_CPU_ISA_III);
                c->options = R4K_OPTS |
                             MIPS_CPU_FPU | MIPS_CPU_LLSC |
                             MIPS_CPU_32FPR;
                c->tlbsize = 64;
+               c->writecombine = _CACHE_UNCACHED_ACCELERATED;
                break;
        case PRID_IMP_LOONGSON_32:  /* Loongson-1 */
                decode_configs(c);
index ac35e12..a5e26dd 100644 (file)
@@ -358,6 +358,7 @@ NESTED(nmi_handler, PT_SIZE, sp)
        .set    push
        /* gas fails to assemble cfc1 for some archs (octeon).*/ \
        .set    mips1
+       SET_HARDFLOAT
        cfc1    a1, fcr31
        li      a2, ~(0x3f << 12)
        and     a2, a1
index 6001610..dda800e 100644 (file)
 
 #ifdef HAVE_JUMP_LABEL
 
-#define J_RANGE_MASK ((1ul << 28) - 1)
+/*
+ * Define parameters for the standard MIPS and the microMIPS jump
+ * instruction encoding respectively:
+ *
+ * - the ISA bit of the target, either 0 or 1 respectively,
+ *
+ * - the amount the jump target address is shifted right to fit in the
+ *   immediate field of the machine instruction, either 2 or 1,
+ *
+ * - the mask determining the size of the jump region relative to the
+ *   delay-slot instruction, either 256MB or 128MB,
+ *
+ * - the jump target alignment, either 4 or 2 bytes.
+ */
+#define J_ISA_BIT      IS_ENABLED(CONFIG_CPU_MICROMIPS)
+#define J_RANGE_SHIFT  (2 - J_ISA_BIT)
+#define J_RANGE_MASK   ((1ul << (26 + J_RANGE_SHIFT)) - 1)
+#define J_ALIGN_MASK   ((1ul << J_RANGE_SHIFT) - 1)
 
 void arch_jump_label_transform(struct jump_entry *e,
                               enum jump_label_type type)
 {
+       union mips_instruction *insn_p;
        union mips_instruction insn;
-       union mips_instruction *insn_p =
-               (union mips_instruction *)(unsigned long)e->code;
 
-       /* Jump only works within a 256MB aligned region. */
-       BUG_ON((e->target & ~J_RANGE_MASK) != (e->code & ~J_RANGE_MASK));
+       insn_p = (union mips_instruction *)msk_isa16_mode(e->code);
+
+       /* Jump only works within an aligned region its delay slot is in. */
+       BUG_ON((e->target & ~J_RANGE_MASK) != ((e->code + 4) & ~J_RANGE_MASK));
 
-       /* Target must have 4 byte alignment. */
-       BUG_ON((e->target & 3) != 0);
+       /* Target must have the right alignment and ISA must be preserved. */
+       BUG_ON((e->target & J_ALIGN_MASK) != J_ISA_BIT);
 
        if (type == JUMP_LABEL_ENABLE) {
-               insn.j_format.opcode = j_op;
-               insn.j_format.target = (e->target & J_RANGE_MASK) >> 2;
+               insn.j_format.opcode = J_ISA_BIT ? mm_j32_op : j_op;
+               insn.j_format.target = e->target >> J_RANGE_SHIFT;
        } else {
                insn.word = 0; /* nop */
        }
 
        get_online_cpus();
        mutex_lock(&text_mutex);
-       *insn_p = insn;
+       if (IS_ENABLED(CONFIG_CPU_MICROMIPS)) {
+               insn_p->halfword[0] = insn.word >> 16;
+               insn_p->halfword[1] = insn.word;
+       } else
+               *insn_p = insn;
 
        flush_icache_range((unsigned long)insn_p,
                           (unsigned long)insn_p + sizeof(*insn_p));
index f31063d..5ce3b74 100644 (file)
@@ -28,6 +28,8 @@
        .set    mips1
        /* Save floating point context */
 LEAF(_save_fp_context)
+       .set    push
+       SET_HARDFLOAT
        li      v0, 0                                   # assume success
        cfc1    t1,fcr31
        EX(swc1 $f0,(SC_FPREGS+0)(a0))
@@ -65,6 +67,7 @@ LEAF(_save_fp_context)
        EX(sw   t1,(SC_FPC_CSR)(a0))
        cfc1    t0,$0                           # implementation/version
        jr      ra
+       .set    pop
        .set    nomacro
         EX(sw  t0,(SC_FPC_EIR)(a0))
        .set    macro
@@ -80,6 +83,8 @@ LEAF(_save_fp_context)
  * stack frame which might have been changed by the user.
  */
 LEAF(_restore_fp_context)
+       .set    push
+       SET_HARDFLOAT
        li      v0, 0                                   # assume success
        EX(lw t0,(SC_FPC_CSR)(a0))
        EX(lwc1 $f0,(SC_FPREGS+0)(a0))
@@ -116,6 +121,7 @@ LEAF(_restore_fp_context)
        EX(lwc1 $f31,(SC_FPREGS+248)(a0))
        jr      ra
         ctc1   t0,fcr31
+       .set    pop
        END(_restore_fp_context)
        .set    reorder
 
index 20b7b04..435ea65 100644 (file)
@@ -120,6 +120,9 @@ LEAF(_restore_fp)
 
 #define FPU_DEFAULT  0x00000000
 
+       .set push
+       SET_HARDFLOAT
+
 LEAF(_init_fpu)
        mfc0    t0, CP0_STATUS
        li      t1, ST0_CU1
@@ -165,3 +168,5 @@ LEAF(_init_fpu)
        mtc1    t0, $f31
        jr      ra
        END(_init_fpu)
+
+       .set pop
index 8352523..6c160c6 100644 (file)
 #include <asm/asm-offsets.h>
 #include <asm/regdef.h>
 
+/* preprocessor replaces the fp in ".set fp=64" with $30 otherwise */
+#undef fp
+
        .macro  EX insn, reg, src
        .set    push
+       SET_HARDFLOAT
        .set    nomacro
 .ex\@: \insn   \reg, \src
        .set    pop
        .set    arch=r4000
 
 LEAF(_save_fp_context)
+       .set    push
+       SET_HARDFLOAT
        cfc1    t1, fcr31
+       .set    pop
 
 #if defined(CONFIG_64BIT) || defined(CONFIG_CPU_MIPS32_R2)
        .set    push
+       SET_HARDFLOAT
 #ifdef CONFIG_CPU_MIPS32_R2
-       .set    mips64r2
+       .set    mips32r2
+       .set    fp=64
        mfc0    t0, CP0_STATUS
        sll     t0, t0, 5
        bgez    t0, 1f                  # skip storing odd if FR=0
@@ -64,6 +73,8 @@ LEAF(_save_fp_context)
 1:     .set    pop
 #endif
 
+       .set push
+       SET_HARDFLOAT
        /* Store the 16 even double precision registers */
        EX      sdc1 $f0, SC_FPREGS+0(a0)
        EX      sdc1 $f2, SC_FPREGS+16(a0)
@@ -84,11 +95,14 @@ LEAF(_save_fp_context)
        EX      sw t1, SC_FPC_CSR(a0)
        jr      ra
         li     v0, 0                                   # success
+       .set pop
        END(_save_fp_context)
 
 #ifdef CONFIG_MIPS32_COMPAT
        /* Save 32-bit process floating point context */
 LEAF(_save_fp_context32)
+       .set push
+       SET_HARDFLOAT
        cfc1    t1, fcr31
 
        mfc0    t0, CP0_STATUS
@@ -134,6 +148,7 @@ LEAF(_save_fp_context32)
        EX      sw t1, SC32_FPC_CSR(a0)
        cfc1    t0, $0                          # implementation/version
        EX      sw t0, SC32_FPC_EIR(a0)
+       .set pop
 
        jr      ra
         li     v0, 0                                   # success
@@ -150,8 +165,10 @@ LEAF(_restore_fp_context)
 
 #if defined(CONFIG_64BIT) || defined(CONFIG_CPU_MIPS32_R2)
        .set    push
+       SET_HARDFLOAT
 #ifdef CONFIG_CPU_MIPS32_R2
-       .set    mips64r2
+       .set    mips32r2
+       .set    fp=64
        mfc0    t0, CP0_STATUS
        sll     t0, t0, 5
        bgez    t0, 1f                  # skip loading odd if FR=0
@@ -175,6 +192,8 @@ LEAF(_restore_fp_context)
        EX      ldc1 $f31, SC_FPREGS+248(a0)
 1:     .set pop
 #endif
+       .set push
+       SET_HARDFLOAT
        EX      ldc1 $f0, SC_FPREGS+0(a0)
        EX      ldc1 $f2, SC_FPREGS+16(a0)
        EX      ldc1 $f4, SC_FPREGS+32(a0)
@@ -192,6 +211,7 @@ LEAF(_restore_fp_context)
        EX      ldc1 $f28, SC_FPREGS+224(a0)
        EX      ldc1 $f30, SC_FPREGS+240(a0)
        ctc1    t1, fcr31
+       .set pop
        jr      ra
         li     v0, 0                                   # success
        END(_restore_fp_context)
@@ -199,6 +219,8 @@ LEAF(_restore_fp_context)
 #ifdef CONFIG_MIPS32_COMPAT
 LEAF(_restore_fp_context32)
        /* Restore an o32 sigcontext.  */
+       .set push
+       SET_HARDFLOAT
        EX      lw t1, SC32_FPC_CSR(a0)
 
        mfc0    t0, CP0_STATUS
@@ -242,6 +264,7 @@ LEAF(_restore_fp_context32)
        ctc1    t1, fcr31
        jr      ra
         li     v0, 0                                   # success
+       .set pop
        END(_restore_fp_context32)
 #endif
 
index 4c4ec18..64591e6 100644 (file)
@@ -22,6 +22,9 @@
 
 #include <asm/asmmacro.h>
 
+/* preprocessor replaces the fp in ".set fp=64" with $30 otherwise */
+#undef fp
+
 /*
  * Offset to the current process status flags, the first 32 bytes of the
  * stack are not used.
        bgtz    a3, 1f
 
        /* Save 128b MSA vector context + scalar FP control & status. */
+       .set push
+       SET_HARDFLOAT
        cfc1    t1, fcr31
        msa_save_all    a0
+       .set pop        /* SET_HARDFLOAT */
+
        sw      t1, THREAD_FCR31(a0)
        b       2f
 
@@ -161,6 +168,9 @@ LEAF(_init_msa_upper)
 
 #define FPU_DEFAULT  0x00000000
 
+       .set push
+       SET_HARDFLOAT
+
 LEAF(_init_fpu)
        mfc0    t0, CP0_STATUS
        li      t1, ST0_CU1
@@ -232,7 +242,8 @@ LEAF(_init_fpu)
 
 #ifdef CONFIG_CPU_MIPS32_R2
        .set    push
-       .set    mips64r2
+       .set    mips32r2
+       .set    fp=64
        sll     t0, t0, 5                       # is Status.FR set?
        bgez    t0, 1f                          # no: skip setting upper 32b
 
@@ -291,3 +302,5 @@ LEAF(_init_fpu)
 #endif
        jr      ra
        END(_init_fpu)
+
+       .set pop        /* SET_HARDFLOAT */
index da0fbe4..4707738 100644 (file)
@@ -18,6 +18,9 @@
 
        .set    noreorder
        .set    mips2
+       .set    push
+       SET_HARDFLOAT
+
        /* Save floating point context */
        LEAF(_save_fp_context)
        mfc0    t0,CP0_STATUS
@@ -85,3 +88,5 @@
 1:     jr      ra
         nop
        END(_restore_fp_context)
+
+       .set pop        /* SET_HARDFLOAT */
index 31b1b76..c5c4fd5 100644 (file)
@@ -94,12 +94,12 @@ int rtlx_open(int index, int can_sleep)
        int ret = 0;
 
        if (index >= RTLX_CHANNELS) {
-               pr_debug(KERN_DEBUG "rtlx_open index out of range\n");
+               pr_debug("rtlx_open index out of range\n");
                return -ENOSYS;
        }
 
        if (atomic_inc_return(&channel_wqs[index].in_open) > 1) {
-               pr_debug(KERN_DEBUG "rtlx_open channel %d already opened\n", index);
+               pr_debug("rtlx_open channel %d already opened\n", index);
                ret = -EBUSY;
                goto out_fail;
        }
index 744cd10..00cad10 100644 (file)
@@ -579,3 +579,4 @@ EXPORT(sys_call_table)
        PTR     sys_seccomp
        PTR     sys_getrandom
        PTR     sys_memfd_create
+       PTR     sys_bpf                         /* 4355 */
index 002b1bc..5251565 100644 (file)
@@ -434,4 +434,5 @@ EXPORT(sys_call_table)
        PTR     sys_seccomp
        PTR     sys_getrandom
        PTR     sys_memfd_create
+       PTR     sys_bpf                         /* 5315 */
        .size   sys_call_table,.-sys_call_table
index ca6cbbe..77e7439 100644 (file)
@@ -427,4 +427,5 @@ EXPORT(sysn32_call_table)
        PTR     sys_seccomp
        PTR     sys_getrandom
        PTR     sys_memfd_create
+       PTR     sys_bpf
        .size   sysn32_call_table,.-sysn32_call_table
index 9e10d11..6f8db9f 100644 (file)
@@ -564,4 +564,5 @@ EXPORT(sys32_call_table)
        PTR     sys_seccomp
        PTR     sys_getrandom
        PTR     sys_memfd_create
+       PTR     sys_bpf                         /* 4355 */
        .size   sys32_call_table,.-sys32_call_table
index b3b8f0d..f3b635f 100644 (file)
@@ -485,7 +485,7 @@ static void __init bootmem_init(void)
  * NOTE: historically plat_mem_setup did the entire platform initialization.
  *      This was rather impractical because it meant plat_mem_setup had to
  * get away without any kind of memory allocator.  To keep old code from
- * breaking plat_setup was just renamed to plat_setup and a second platform
+ * breaking plat_setup was just renamed to plat_mem_setup and a second platform
  * initialization hook for anything else was introduced.
  */
 
@@ -493,7 +493,7 @@ static int usermem __initdata;
 
 static int __init early_parse_mem(char *p)
 {
-       unsigned long start, size;
+       phys_t start, size;
 
        /*
         * If a user specifies memory size, we
@@ -683,7 +683,8 @@ static void __init arch_mem_init(char **cmdline_p)
        dma_contiguous_reserve(PFN_PHYS(max_low_pfn));
        /* Tell bootmem about cma reserved memblock section */
        for_each_memblock(reserved, reg)
-               reserve_bootmem(reg->base, reg->size, BOOTMEM_DEFAULT);
+               if (reg->size != 0)
+                       reserve_bootmem(reg->base, reg->size, BOOTMEM_DEFAULT);
 }
 
 static void __init resource_init(void)
index 1d57605..16f1e4f 100644 (file)
@@ -658,13 +658,13 @@ static int signal_setup(void)
                save_fp_context = _save_fp_context;
                restore_fp_context = _restore_fp_context;
        } else {
-               save_fp_context = copy_fp_from_sigcontext;
-               restore_fp_context = copy_fp_to_sigcontext;
+               save_fp_context = copy_fp_to_sigcontext;
+               restore_fp_context = copy_fp_from_sigcontext;
        }
 #endif /* CONFIG_SMP */
 #else
-       save_fp_context = copy_fp_from_sigcontext;;
-       restore_fp_context = copy_fp_to_sigcontext;
+       save_fp_context = copy_fp_to_sigcontext;
+       restore_fp_context = copy_fp_from_sigcontext;
 #endif
 
        return 0;
index c17ef80..5d3238a 100644 (file)
        STOREB(t0, NBYTES-2(dst), .Ls_exc_p1\@)
 .Ldone\@:
        jr      ra
+        nop
        .if __memcpy == 1
        END(memcpy)
        .set __memcpy, 0
index 91615c2..1ef365a 100644 (file)
@@ -34,7 +34,7 @@ static void dump_tlb(int first, int last)
                entrylo0 = read_c0_entrylo0();
 
                /* Unused entries have a virtual address of KSEG0.  */
-               if ((entryhi & 0xffffe000) != 0x80000000
+               if ((entryhi & 0xfffff000) != 0x80000000
                    && (entryhi & 0xfc0) == asid) {
                        /*
                         * Only print entries in use
@@ -43,7 +43,7 @@ static void dump_tlb(int first, int last)
 
                        printk("va=%08lx asid=%08lx"
                               "  [pa=%06lx n=%d d=%d v=%d g=%d]",
-                              (entryhi & 0xffffe000),
+                              (entryhi & 0xfffff000),
                               entryhi & 0xfc0,
                               entrylo0 & PAGE_MASK,
                               (entrylo0 & (1 << 11)) ? 1 : 0,
index f3af699..7d12c0d 100644 (file)
@@ -40,9 +40,11 @@ FEXPORT(__strnlen_\func\()_nocheck_asm)
 .else
        EX(lbe, t0, (v0), .Lfault\@)
 .endif
-       PTR_ADDIU       v0, 1
+       .set            noreorder
        bnez            t0, 1b
-1:     PTR_SUBU        v0, a0
+1:      PTR_ADDIU      v0, 1
+       .set            reorder
+       PTR_SUBU        v0, a0
        jr              ra
        END(__strnlen_\func\()_asm)
 
index 0bb9cc9..d87e033 100644 (file)
@@ -11,7 +11,8 @@ obj-$(CONFIG_PCI) += pci.o
 # Serial port support
 #
 obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
-obj-$(CONFIG_SERIAL_8250) += serial.o
+loongson-serial-$(CONFIG_SERIAL_8250) := serial.o
+obj-y += $(loongson-serial-m) $(loongson-serial-y)
 obj-$(CONFIG_LOONGSON_UART_BASE) += uart_base.o
 obj-$(CONFIG_LOONGSON_MC146818) += rtc.o
 
index 37ed184..42323bc 100644 (file)
@@ -33,6 +33,7 @@
 
 static struct node_data prealloc__node_data[MAX_NUMNODES];
 unsigned char __node_distances[MAX_NUMNODES][MAX_NUMNODES];
+EXPORT_SYMBOL(__node_distances);
 struct node_data *__node_data[MAX_NUMNODES];
 EXPORT_SYMBOL(__node_data);
 
index 51a0fde..cac529a 100644 (file)
@@ -584,11 +584,7 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
                if (insn.i_format.rs == bc_op) {
                        preempt_disable();
                        if (is_fpu_owner())
-                               asm volatile(
-                                       ".set push\n"
-                                       "\t.set mips1\n"
-                                       "\tcfc1\t%0,$31\n"
-                                       "\t.set pop" : "=r" (fcr31));
+                               fcr31 = read_32bit_cp1_register(CP1_STATUS);
                        else
                                fcr31 = current->thread.fpu.fcr31;
                        preempt_enable();
index fa6ebd4..c3917e2 100644 (file)
@@ -299,6 +299,7 @@ void __update_tlb(struct vm_area_struct * vma, unsigned long address, pte_t pte)
 
        local_irq_save(flags);
 
+       htw_stop();
        pid = read_c0_entryhi() & ASID_MASK;
        address &= (PAGE_MASK << 1);
        write_c0_entryhi(address | pid);
@@ -346,6 +347,7 @@ void __update_tlb(struct vm_area_struct * vma, unsigned long address, pte_t pte)
                        tlb_write_indexed();
        }
        tlbw_use_hazard();
+       htw_start();
        flush_itlb_vm(vma);
        local_irq_restore(flags);
 }
@@ -422,6 +424,7 @@ __init int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1,
 
        local_irq_save(flags);
        /* Save old context and create impossible VPN2 value */
+       htw_stop();
        old_ctx = read_c0_entryhi();
        old_pagemask = read_c0_pagemask();
        wired = read_c0_wired();
@@ -443,6 +446,7 @@ __init int add_temporary_entry(unsigned long entrylo0, unsigned long entrylo1,
 
        write_c0_entryhi(old_ctx);
        write_c0_pagemask(old_pagemask);
+       htw_start();
 out:
        local_irq_restore(flags);
        return ret;
index b5f228e..e3328a9 100644 (file)
@@ -1872,8 +1872,16 @@ build_r4000_tlbchange_handler_head(u32 **p, struct uasm_label **l,
        uasm_l_smp_pgtable_change(l, *p);
 #endif
        iPTE_LW(p, wr.r1, wr.r2); /* get even pte */
-       if (!m4kc_tlbp_war())
+       if (!m4kc_tlbp_war()) {
                build_tlb_probe_entry(p);
+               if (cpu_has_htw) {
+                       /* race condition happens, leaving */
+                       uasm_i_ehb(p);
+                       uasm_i_mfc0(p, wr.r3, C0_INDEX);
+                       uasm_il_bltz(p, r, wr.r3, label_leave);
+                       uasm_i_nop(p);
+               }
+       }
        return wr;
 }
 
index 20102a6..c427c57 100644 (file)
@@ -5,7 +5,7 @@
  *
  * Copyright (C) 2012 MIPS Technologies, Inc.  All rights reserved.
  */
-#include <linux/module.h>
+#include <linux/init.h>
 #include <linux/leds.h>
 #include <linux/platform_device.h>
 
@@ -76,8 +76,4 @@ static int __init led_init(void)
        return platform_device_register(&fled_device);
 }
 
-module_init(led_init);
-
-MODULE_AUTHOR("Chris Dearman <chris@mips.com>");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("LED probe driver for SEAD-3");
+device_initcall(led_init);
index be358a8..6b43af0 100644 (file)
@@ -1,6 +1,10 @@
 obj-y                          += setup.o nlm_hal.o cop2-ex.o dt.o
 obj-$(CONFIG_SMP)              += wakeup.o
-obj-$(CONFIG_USB)              += usb-init.o
-obj-$(CONFIG_USB)              += usb-init-xlp2.o
-obj-$(CONFIG_SATA_AHCI)                += ahci-init.o
-obj-$(CONFIG_SATA_AHCI)                += ahci-init-xlp2.o
+ifdef CONFIG_USB
+obj-y                          += usb-init.o
+obj-y                          += usb-init-xlp2.o
+endif
+ifdef CONFIG_SATA_AHCI
+obj-y                          += ahci-init.o
+obj-y                          += ahci-init-xlp2.o
+endif
index 6854ed5..83a1dfd 100644 (file)
@@ -92,7 +92,7 @@ static inline int unwind_user_frame(struct stackframe *old_frame,
                                /* This marks the end of the previous function,
                                   which means we overran. */
                                break;
-                       stack_size = (unsigned) stack_adjustment;
+                       stack_size = (unsigned long) stack_adjustment;
                } else if (is_ra_save_ins(&ip)) {
                        int ra_slot = ip.i_format.simmediate;
                        if (ra_slot < 0)
index fa374fe..f7ac3ed 100644 (file)
@@ -443,10 +443,8 @@ static int xlp_setup_msix(uint64_t lnkbase, int node, int link,
        msg.data = 0xc00 | msixvec;
 
        ret = irq_set_msi_desc(xirq, desc);
-       if (ret < 0) {
-               destroy_irq(xirq);
+       if (ret < 0)
                return ret;
-       }
 
        write_msi_msg(xirq, &msg);
        return 0;
index a95c00f..a304bcc 100644 (file)
@@ -107,6 +107,7 @@ static void router_recurse(klrou_t *router_a, klrou_t *router_b, int depth)
 }
 
 unsigned char __node_distances[MAX_COMPACT_NODES][MAX_COMPACT_NODES];
+EXPORT_SYMBOL(__node_distances);
 
 static int __init compute_node_distance(nasid_t nasid_a, nasid_t nasid_b)
 {
index 4006964..a5cb070 100644 (file)
@@ -9,6 +9,8 @@
 #include <asm/errno.h>
 #include <asm-generic/uaccess-unaligned.h>
 
+#include <linux/bug.h>
+
 #define VERIFY_READ 0
 #define VERIFY_WRITE 1
 
  * that put_user is the same as __put_user, etc.
  */
 
-extern int __get_kernel_bad(void);
-extern int __get_user_bad(void);
-extern int __put_kernel_bad(void);
-extern int __put_user_bad(void);
-
 static inline long access_ok(int type, const void __user * addr,
                unsigned long size)
 {
@@ -43,8 +40,8 @@ static inline long access_ok(int type, const void __user * addr,
 #define get_user __get_user
 
 #if !defined(CONFIG_64BIT)
-#define LDD_KERNEL(ptr)                __get_kernel_bad();
-#define LDD_USER(ptr)          __get_user_bad();
+#define LDD_KERNEL(ptr)                BUILD_BUG()
+#define LDD_USER(ptr)          BUILD_BUG()
 #define STD_KERNEL(x, ptr)     __put_kernel_asm64(x,ptr)
 #define STD_USER(x, ptr)       __put_user_asm64(x,ptr)
 #define ASM_WORD_INSN          ".word\t"
@@ -94,7 +91,7 @@ struct exception_data {
            case 2: __get_kernel_asm("ldh",ptr); break; \
            case 4: __get_kernel_asm("ldw",ptr); break; \
            case 8: LDD_KERNEL(ptr); break;             \
-           default: __get_kernel_bad(); break;         \
+           default: BUILD_BUG(); break;                \
            }                                           \
        }                                               \
        else {                                          \
@@ -103,7 +100,7 @@ struct exception_data {
            case 2: __get_user_asm("ldh",ptr); break;   \
            case 4: __get_user_asm("ldw",ptr); break;   \
            case 8: LDD_USER(ptr);  break;              \
-           default: __get_user_bad(); break;           \
+           default: BUILD_BUG(); break;                \
            }                                           \
        }                                               \
                                                        \
@@ -136,7 +133,7 @@ struct exception_data {
            case 2: __put_kernel_asm("sth",__x,ptr); break;     \
            case 4: __put_kernel_asm("stw",__x,ptr); break;     \
            case 8: STD_KERNEL(__x,ptr); break;                 \
-           default: __put_kernel_bad(); break;                 \
+           default: BUILD_BUG(); break;                        \
            }                                                   \
        }                                                       \
        else {                                                  \
@@ -145,7 +142,7 @@ struct exception_data {
            case 2: __put_user_asm("sth",__x,ptr); break;       \
            case 4: __put_user_asm("stw",__x,ptr); break;       \
            case 8: STD_USER(__x,ptr); break;                   \
-           default: __put_user_bad(); break;                   \
+           default: BUILD_BUG(); break;                        \
            }                                                   \
        }                                                       \
                                                                \
index 75196b4..e0a23c7 100644 (file)
@@ -1,13 +1,7 @@
 #ifndef __ASM_PARISC_BITSPERLONG_H
 #define __ASM_PARISC_BITSPERLONG_H
 
-/*
- * using CONFIG_* outside of __KERNEL__ is wrong,
- * __LP64__ was also removed from headers, so what
- * is the right approach on parisc?
- *     -arnd
- */
-#if (defined(__KERNEL__) && defined(CONFIG_64BIT)) || defined (__LP64__)
+#if defined(__LP64__)
 #define __BITS_PER_LONG 64
 #define SHIFT_PER_LONG 6
 #else
index fe88f26..3421389 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef _PARISC_MSGBUF_H
 #define _PARISC_MSGBUF_H
 
+#include <asm/bitsperlong.h>
+
 /* 
  * The msqid64_ds structure for parisc architecture, copied from sparc.
  * Note extra padding because this structure is passed back and forth
 
 struct msqid64_ds {
        struct ipc64_perm msg_perm;
-#ifndef CONFIG_64BIT
+#if __BITS_PER_LONG != 64
        unsigned int   __pad1;
 #endif
        __kernel_time_t msg_stime;      /* last msgsnd time */
-#ifndef CONFIG_64BIT
+#if __BITS_PER_LONG != 64
        unsigned int   __pad2;
 #endif
        __kernel_time_t msg_rtime;      /* last msgrcv time */
-#ifndef CONFIG_64BIT
+#if __BITS_PER_LONG != 64
        unsigned int   __pad3;
 #endif
        __kernel_time_t msg_ctime;      /* last change time */
index 1e59ffd..f01d89e 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef _PARISC_SEMBUF_H
 #define _PARISC_SEMBUF_H
 
+#include <asm/bitsperlong.h>
+
 /* 
  * The semid64_ds structure for parisc architecture.
  * Note extra padding because this structure is passed back and forth
 
 struct semid64_ds {
        struct ipc64_perm sem_perm;             /* permissions .. see ipc.h */
-#ifndef CONFIG_64BIT
+#if __BITS_PER_LONG != 64
        unsigned int    __pad1;
 #endif
        __kernel_time_t sem_otime;              /* last semop time */
-#ifndef CONFIG_64BIT
+#if __BITS_PER_LONG != 64
        unsigned int    __pad2;
 #endif
        __kernel_time_t sem_ctime;              /* last change time */
index 0a3eada..8496c38 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef _PARISC_SHMBUF_H
 #define _PARISC_SHMBUF_H
 
+#include <asm/bitsperlong.h>
+
 /* 
  * The shmid64_ds structure for parisc architecture.
  * Note extra padding because this structure is passed back and forth
 
 struct shmid64_ds {
        struct ipc64_perm       shm_perm;       /* operation perms */
-#ifndef CONFIG_64BIT
+#if __BITS_PER_LONG != 64
        unsigned int            __pad1;
 #endif
        __kernel_time_t         shm_atime;      /* last attach time */
-#ifndef CONFIG_64BIT
+#if __BITS_PER_LONG != 64
        unsigned int            __pad2;
 #endif
        __kernel_time_t         shm_dtime;      /* last detach time */
-#ifndef CONFIG_64BIT
+#if __BITS_PER_LONG != 64
        unsigned int            __pad3;
 #endif
        __kernel_time_t         shm_ctime;      /* last change time */
-#ifndef CONFIG_64BIT
+#if __BITS_PER_LONG != 64
        unsigned int            __pad4;
 #endif
        size_t                  shm_segsz;      /* size of segment (bytes) */
@@ -36,23 +38,16 @@ struct shmid64_ds {
        unsigned int            __unused2;
 };
 
-#ifdef CONFIG_64BIT
-/* The 'unsigned int' (formerly 'unsigned long') data types below will
- * ensure that a 32-bit app calling shmctl(*,IPC_INFO,*) will work on
- * a wide kernel, but if some of these values are meant to contain pointers
- * they may need to be 'long long' instead. -PB XXX FIXME
- */
-#endif
 struct shminfo64 {
-       unsigned int    shmmax;
-       unsigned int    shmmin;
-       unsigned int    shmmni;
-       unsigned int    shmseg;
-       unsigned int    shmall;
-       unsigned int    __unused1;
-       unsigned int    __unused2;
-       unsigned int    __unused3;
-       unsigned int    __unused4;
+       unsigned long   shmmax;
+       unsigned long   shmmin;
+       unsigned long   shmmni;
+       unsigned long   shmseg;
+       unsigned long   shmall;
+       unsigned long   __unused1;
+       unsigned long   __unused2;
+       unsigned long   __unused3;
+       unsigned long   __unused4;
 };
 
 #endif /* _PARISC_SHMBUF_H */
index 10df707..e26043b 100644 (file)
@@ -85,7 +85,7 @@
 struct siginfo;
 
 /* Type of a signal handler.  */
-#ifdef CONFIG_64BIT
+#if defined(__LP64__)
 /* function pointers on 64-bit parisc are pointers to little structs and the
  * compiler doesn't support code which changes or tests the address of
  * the function in the little struct.  This is really ugly -PB
index 8667f18..5f5c037 100644 (file)
 #define __NR_seccomp           (__NR_Linux + 338)
 #define __NR_getrandom         (__NR_Linux + 339)
 #define __NR_memfd_create      (__NR_Linux + 340)
+#define __NR_bpf               (__NR_Linux + 341)
 
-#define __NR_Linux_syscalls    (__NR_memfd_create + 1)
+#define __NR_Linux_syscalls    (__NR_bpf + 1)
 
 
 #define __IGNORE_select                /* newselect */
index b563d9c..fe4f0b8 100644 (file)
        ENTRY_COMP(msgsnd)
        ENTRY_COMP(msgrcv)
        ENTRY_SAME(msgget)              /* 190 */
-       ENTRY_SAME(msgctl)
-       ENTRY_SAME(shmat)
+       ENTRY_COMP(msgctl)
+       ENTRY_COMP(shmat)
        ENTRY_SAME(shmdt)
        ENTRY_SAME(shmget)
-       ENTRY_SAME(shmctl)              /* 195 */
+       ENTRY_COMP(shmctl)              /* 195 */
        ENTRY_SAME(ni_syscall)          /* streams1 */
        ENTRY_SAME(ni_syscall)          /* streams2 */
        ENTRY_SAME(lstat64)
        ENTRY_SAME(epoll_ctl)           /* 225 */
        ENTRY_SAME(epoll_wait)
        ENTRY_SAME(remap_file_pages)
-       ENTRY_SAME(semtimedop)
+       ENTRY_COMP(semtimedop)
        ENTRY_COMP(mq_open)
        ENTRY_SAME(mq_unlink)           /* 230 */
        ENTRY_COMP(mq_timedsend)
        ENTRY_SAME(seccomp)
        ENTRY_SAME(getrandom)
        ENTRY_SAME(memfd_create)        /* 340 */
+       ENTRY_SAME(bpf)
 
        /* Nothing yet */
 
index a677456..493e72f 100644 (file)
 #define CPU_UNKNOWN            (~((u32)0))
 
 /* Utility macros */
-#define SKIP_TO_NEXT_CPU(reg_entry)                    \
-({                                                     \
-       while (reg_entry->reg_id != REG_ID("CPUEND"))   \
-               reg_entry++;                            \
-       reg_entry++;                                    \
+#define SKIP_TO_NEXT_CPU(reg_entry)                                    \
+({                                                                     \
+       while (be64_to_cpu(reg_entry->reg_id) != REG_ID("CPUEND"))      \
+               reg_entry++;                                            \
+       reg_entry++;                                                    \
 })
 
 /* Kernel Dump section info */
 struct fadump_section {
-       u32     request_flag;
-       u16     source_data_type;
-       u16     error_flags;
-       u64     source_address;
-       u64     source_len;
-       u64     bytes_dumped;
-       u64     destination_address;
+       __be32  request_flag;
+       __be16  source_data_type;
+       __be16  error_flags;
+       __be64  source_address;
+       __be64  source_len;
+       __be64  bytes_dumped;
+       __be64  destination_address;
 };
 
 /* ibm,configure-kernel-dump header. */
 struct fadump_section_header {
-       u32     dump_format_version;
-       u16     dump_num_sections;
-       u16     dump_status_flag;
-       u32     offset_first_dump_section;
+       __be32  dump_format_version;
+       __be16  dump_num_sections;
+       __be16  dump_status_flag;
+       __be32  offset_first_dump_section;
 
        /* Fields for disk dump option. */
-       u32     dd_block_size;
-       u64     dd_block_offset;
-       u64     dd_num_blocks;
-       u32     dd_offset_disk_path;
+       __be32  dd_block_size;
+       __be64  dd_block_offset;
+       __be64  dd_num_blocks;
+       __be32  dd_offset_disk_path;
 
        /* Maximum time allowed to prevent an automatic dump-reboot. */
-       u32     max_time_auto;
+       __be32  max_time_auto;
 };
 
 /*
@@ -174,15 +174,15 @@ static inline u64 str_to_u64(const char *str)
 
 /* Register save area header. */
 struct fadump_reg_save_area_header {
-       u64             magic_number;
-       u32             version;
-       u32             num_cpu_offset;
+       __be64          magic_number;
+       __be32          version;
+       __be32          num_cpu_offset;
 };
 
 /* Register entry. */
 struct fadump_reg_entry {
-       u64             reg_id;
-       u64             reg_value;
+       __be64          reg_id;
+       __be64          reg_value;
 };
 
 /* fadump crash info structure */
index 4ca90a3..725247b 100644 (file)
@@ -159,8 +159,6 @@ struct pci_dn {
 
        int     pci_ext_config_space;   /* for pci devices */
 
-       bool    force_32bit_msi;
-
        struct  pci_dev *pcidev;        /* back-pointer to the pci device */
 #ifdef CONFIG_EEH
        struct eeh_dev *edev;           /* eeh device */
index f19b1e5..1ceecdd 100644 (file)
@@ -65,7 +65,7 @@ static ssize_t eeh_pe_state_show(struct device *dev,
                return -ENODEV;
 
        state = eeh_ops->get_state(edev->pe, NULL);
-       return sprintf(buf, "%0x08x %0x08x\n",
+       return sprintf(buf, "0x%08x 0x%08x\n",
                       state, edev->pe->state);
 }
 
index 5bbd1bc..0905c8d 100644 (file)
@@ -659,7 +659,13 @@ _GLOBAL(ret_from_except_lite)
 3:
 #endif
        bl      save_nvgprs
+       /*
+        * Use a non volatile GPR to save and restore our thread_info flags
+        * across the call to restore_interrupts.
+        */
+       mr      r30,r4
        bl      restore_interrupts
+       mr      r4,r30
        addi    r3,r1,STACK_FRAME_OVERHEAD
        bl      do_notify_resume
        b       ret_from_except
index 742694c..26d091a 100644 (file)
@@ -58,7 +58,7 @@ int __init early_init_dt_scan_fw_dump(unsigned long node,
        const __be32 *sections;
        int i, num_sections;
        int size;
-       const int *token;
+       const __be32 *token;
 
        if (depth != 1 || strcmp(uname, "rtas") != 0)
                return 0;
@@ -72,7 +72,7 @@ int __init early_init_dt_scan_fw_dump(unsigned long node,
                return 1;
 
        fw_dump.fadump_supported = 1;
-       fw_dump.ibm_configure_kernel_dump = *token;
+       fw_dump.ibm_configure_kernel_dump = be32_to_cpu(*token);
 
        /*
         * The 'ibm,kernel-dump' rtas node is present only if there is
@@ -147,11 +147,11 @@ static unsigned long init_fadump_mem_struct(struct fadump_mem_struct *fdm,
        memset(fdm, 0, sizeof(struct fadump_mem_struct));
        addr = addr & PAGE_MASK;
 
-       fdm->header.dump_format_version = 0x00000001;
-       fdm->header.dump_num_sections = 3;
+       fdm->header.dump_format_version = cpu_to_be32(0x00000001);
+       fdm->header.dump_num_sections = cpu_to_be16(3);
        fdm->header.dump_status_flag = 0;
        fdm->header.offset_first_dump_section =
-               (u32)offsetof(struct fadump_mem_struct, cpu_state_data);
+               cpu_to_be32((u32)offsetof(struct fadump_mem_struct, cpu_state_data));
 
        /*
         * Fields for disk dump option.
@@ -167,27 +167,27 @@ static unsigned long init_fadump_mem_struct(struct fadump_mem_struct *fdm,
 
        /* Kernel dump sections */
        /* cpu state data section. */
-       fdm->cpu_state_data.request_flag = FADUMP_REQUEST_FLAG;
-       fdm->cpu_state_data.source_data_type = FADUMP_CPU_STATE_DATA;
+       fdm->cpu_state_data.request_flag = cpu_to_be32(FADUMP_REQUEST_FLAG);
+       fdm->cpu_state_data.source_data_type = cpu_to_be16(FADUMP_CPU_STATE_DATA);
        fdm->cpu_state_data.source_address = 0;
-       fdm->cpu_state_data.source_len = fw_dump.cpu_state_data_size;
-       fdm->cpu_state_data.destination_address = addr;
+       fdm->cpu_state_data.source_len = cpu_to_be64(fw_dump.cpu_state_data_size);
+       fdm->cpu_state_data.destination_address = cpu_to_be64(addr);
        addr += fw_dump.cpu_state_data_size;
 
        /* hpte region section */
-       fdm->hpte_region.request_flag = FADUMP_REQUEST_FLAG;
-       fdm->hpte_region.source_data_type = FADUMP_HPTE_REGION;
+       fdm->hpte_region.request_flag = cpu_to_be32(FADUMP_REQUEST_FLAG);
+       fdm->hpte_region.source_data_type = cpu_to_be16(FADUMP_HPTE_REGION);
        fdm->hpte_region.source_address = 0;
-       fdm->hpte_region.source_len = fw_dump.hpte_region_size;
-       fdm->hpte_region.destination_address = addr;
+       fdm->hpte_region.source_len = cpu_to_be64(fw_dump.hpte_region_size);
+       fdm->hpte_region.destination_address = cpu_to_be64(addr);
        addr += fw_dump.hpte_region_size;
 
        /* RMA region section */
-       fdm->rmr_region.request_flag = FADUMP_REQUEST_FLAG;
-       fdm->rmr_region.source_data_type = FADUMP_REAL_MODE_REGION;
-       fdm->rmr_region.source_address = RMA_START;
-       fdm->rmr_region.source_len = fw_dump.boot_memory_size;
-       fdm->rmr_region.destination_address = addr;
+       fdm->rmr_region.request_flag = cpu_to_be32(FADUMP_REQUEST_FLAG);
+       fdm->rmr_region.source_data_type = cpu_to_be16(FADUMP_REAL_MODE_REGION);
+       fdm->rmr_region.source_address = cpu_to_be64(RMA_START);
+       fdm->rmr_region.source_len = cpu_to_be64(fw_dump.boot_memory_size);
+       fdm->rmr_region.destination_address = cpu_to_be64(addr);
        addr += fw_dump.boot_memory_size;
 
        return addr;
@@ -272,7 +272,7 @@ int __init fadump_reserve_mem(void)
         * first kernel.
         */
        if (fdm_active)
-               fw_dump.boot_memory_size = fdm_active->rmr_region.source_len;
+               fw_dump.boot_memory_size = be64_to_cpu(fdm_active->rmr_region.source_len);
        else
                fw_dump.boot_memory_size = fadump_calculate_reserve_size();
 
@@ -314,8 +314,8 @@ int __init fadump_reserve_mem(void)
                                (unsigned long)(base >> 20));
 
                fw_dump.fadumphdr_addr =
-                               fdm_active->rmr_region.destination_address +
-                               fdm_active->rmr_region.source_len;
+                               be64_to_cpu(fdm_active->rmr_region.destination_address) +
+                               be64_to_cpu(fdm_active->rmr_region.source_len);
                pr_debug("fadumphdr_addr = %p\n",
                                (void *) fw_dump.fadumphdr_addr);
        } else {
@@ -472,9 +472,9 @@ fadump_read_registers(struct fadump_reg_entry *reg_entry, struct pt_regs *regs)
 {
        memset(regs, 0, sizeof(struct pt_regs));
 
-       while (reg_entry->reg_id != REG_ID("CPUEND")) {
-               fadump_set_regval(regs, reg_entry->reg_id,
-                                       reg_entry->reg_value);
+       while (be64_to_cpu(reg_entry->reg_id) != REG_ID("CPUEND")) {
+               fadump_set_regval(regs, be64_to_cpu(reg_entry->reg_id),
+                                       be64_to_cpu(reg_entry->reg_value));
                reg_entry++;
        }
        reg_entry++;
@@ -603,20 +603,20 @@ static int __init fadump_build_cpu_notes(const struct fadump_mem_struct *fdm)
        if (!fdm->cpu_state_data.bytes_dumped)
                return -EINVAL;
 
-       addr = fdm->cpu_state_data.destination_address;
+       addr = be64_to_cpu(fdm->cpu_state_data.destination_address);
        vaddr = __va(addr);
 
        reg_header = vaddr;
-       if (reg_header->magic_number != REGSAVE_AREA_MAGIC) {
+       if (be64_to_cpu(reg_header->magic_number) != REGSAVE_AREA_MAGIC) {
                printk(KERN_ERR "Unable to read register save area.\n");
                return -ENOENT;
        }
        pr_debug("--------CPU State Data------------\n");
-       pr_debug("Magic Number: %llx\n", reg_header->magic_number);
-       pr_debug("NumCpuOffset: %x\n", reg_header->num_cpu_offset);
+       pr_debug("Magic Number: %llx\n", be64_to_cpu(reg_header->magic_number));
+       pr_debug("NumCpuOffset: %x\n", be32_to_cpu(reg_header->num_cpu_offset));
 
-       vaddr += reg_header->num_cpu_offset;
-       num_cpus = *((u32 *)(vaddr));
+       vaddr += be32_to_cpu(reg_header->num_cpu_offset);
+       num_cpus = be32_to_cpu(*((__be32 *)(vaddr)));
        pr_debug("NumCpus     : %u\n", num_cpus);
        vaddr += sizeof(u32);
        reg_entry = (struct fadump_reg_entry *)vaddr;
@@ -639,13 +639,13 @@ static int __init fadump_build_cpu_notes(const struct fadump_mem_struct *fdm)
                fdh = __va(fw_dump.fadumphdr_addr);
 
        for (i = 0; i < num_cpus; i++) {
-               if (reg_entry->reg_id != REG_ID("CPUSTRT")) {
+               if (be64_to_cpu(reg_entry->reg_id) != REG_ID("CPUSTRT")) {
                        printk(KERN_ERR "Unable to read CPU state data\n");
                        rc = -ENOENT;
                        goto error_out;
                }
                /* Lower 4 bytes of reg_value contains logical cpu id */
-               cpu = reg_entry->reg_value & FADUMP_CPU_ID_MASK;
+               cpu = be64_to_cpu(reg_entry->reg_value) & FADUMP_CPU_ID_MASK;
                if (fdh && !cpumask_test_cpu(cpu, &fdh->cpu_online_mask)) {
                        SKIP_TO_NEXT_CPU(reg_entry);
                        continue;
@@ -692,7 +692,7 @@ static int __init process_fadump(const struct fadump_mem_struct *fdm_active)
                return -EINVAL;
 
        /* Check if the dump data is valid. */
-       if ((fdm_active->header.dump_status_flag == FADUMP_ERROR_FLAG) ||
+       if ((be16_to_cpu(fdm_active->header.dump_status_flag) == FADUMP_ERROR_FLAG) ||
                        (fdm_active->cpu_state_data.error_flags != 0) ||
                        (fdm_active->rmr_region.error_flags != 0)) {
                printk(KERN_ERR "Dump taken by platform is not valid\n");
@@ -828,7 +828,7 @@ static void fadump_setup_crash_memory_ranges(void)
 static inline unsigned long fadump_relocate(unsigned long paddr)
 {
        if (paddr > RMA_START && paddr < fw_dump.boot_memory_size)
-               return fdm.rmr_region.destination_address + paddr;
+               return be64_to_cpu(fdm.rmr_region.destination_address) + paddr;
        else
                return paddr;
 }
@@ -902,7 +902,7 @@ static int fadump_create_elfcore_headers(char *bufp)
                         * to the specified destination_address. Hence set
                         * the correct offset.
                         */
-                       phdr->p_offset = fdm.rmr_region.destination_address;
+                       phdr->p_offset = be64_to_cpu(fdm.rmr_region.destination_address);
                }
 
                phdr->p_paddr = mbase;
@@ -951,7 +951,7 @@ static void register_fadump(void)
 
        fadump_setup_crash_memory_ranges();
 
-       addr = fdm.rmr_region.destination_address + fdm.rmr_region.source_len;
+       addr = be64_to_cpu(fdm.rmr_region.destination_address) + be64_to_cpu(fdm.rmr_region.source_len);
        /* Initialize fadump crash info header. */
        addr = init_fadump_header(addr);
        vaddr = __va(addr);
@@ -1023,7 +1023,7 @@ void fadump_cleanup(void)
        /* Invalidate the registration only if dump is active. */
        if (fw_dump.dump_active) {
                init_fadump_mem_struct(&fdm,
-                       fdm_active->cpu_state_data.destination_address);
+                       be64_to_cpu(fdm_active->cpu_state_data.destination_address));
                fadump_invalidate_dump(&fdm);
        }
 }
@@ -1063,7 +1063,7 @@ static void fadump_invalidate_release_mem(void)
                return;
        }
 
-       destination_address = fdm_active->cpu_state_data.destination_address;
+       destination_address = be64_to_cpu(fdm_active->cpu_state_data.destination_address);
        fadump_cleanup();
        mutex_unlock(&fadump_mutex);
 
@@ -1183,31 +1183,31 @@ static int fadump_region_show(struct seq_file *m, void *private)
        seq_printf(m,
                        "CPU : [%#016llx-%#016llx] %#llx bytes, "
                        "Dumped: %#llx\n",
-                       fdm_ptr->cpu_state_data.destination_address,
-                       fdm_ptr->cpu_state_data.destination_address +
-                       fdm_ptr->cpu_state_data.source_len - 1,
-                       fdm_ptr->cpu_state_data.source_len,
-                       fdm_ptr->cpu_state_data.bytes_dumped);
+                       be64_to_cpu(fdm_ptr->cpu_state_data.destination_address),
+                       be64_to_cpu(fdm_ptr->cpu_state_data.destination_address) +
+                       be64_to_cpu(fdm_ptr->cpu_state_data.source_len) - 1,
+                       be64_to_cpu(fdm_ptr->cpu_state_data.source_len),
+                       be64_to_cpu(fdm_ptr->cpu_state_data.bytes_dumped));
        seq_printf(m,
                        "HPTE: [%#016llx-%#016llx] %#llx bytes, "
                        "Dumped: %#llx\n",
-                       fdm_ptr->hpte_region.destination_address,
-                       fdm_ptr->hpte_region.destination_address +
-                       fdm_ptr->hpte_region.source_len - 1,
-                       fdm_ptr->hpte_region.source_len,
-                       fdm_ptr->hpte_region.bytes_dumped);
+                       be64_to_cpu(fdm_ptr->hpte_region.destination_address),
+                       be64_to_cpu(fdm_ptr->hpte_region.destination_address) +
+                       be64_to_cpu(fdm_ptr->hpte_region.source_len) - 1,
+                       be64_to_cpu(fdm_ptr->hpte_region.source_len),
+                       be64_to_cpu(fdm_ptr->hpte_region.bytes_dumped));
        seq_printf(m,
                        "DUMP: [%#016llx-%#016llx] %#llx bytes, "
                        "Dumped: %#llx\n",
-                       fdm_ptr->rmr_region.destination_address,
-                       fdm_ptr->rmr_region.destination_address +
-                       fdm_ptr->rmr_region.source_len - 1,
-                       fdm_ptr->rmr_region.source_len,
-                       fdm_ptr->rmr_region.bytes_dumped);
+                       be64_to_cpu(fdm_ptr->rmr_region.destination_address),
+                       be64_to_cpu(fdm_ptr->rmr_region.destination_address) +
+                       be64_to_cpu(fdm_ptr->rmr_region.source_len) - 1,
+                       be64_to_cpu(fdm_ptr->rmr_region.source_len),
+                       be64_to_cpu(fdm_ptr->rmr_region.bytes_dumped));
 
        if (!fdm_active ||
                (fw_dump.reserve_dump_area_start ==
-               fdm_ptr->cpu_state_data.destination_address))
+               be64_to_cpu(fdm_ptr->cpu_state_data.destination_address)))
                goto out;
 
        /* Dump is active. Show reserved memory region. */
@@ -1215,10 +1215,10 @@ static int fadump_region_show(struct seq_file *m, void *private)
                        "    : [%#016llx-%#016llx] %#llx bytes, "
                        "Dumped: %#llx\n",
                        (unsigned long long)fw_dump.reserve_dump_area_start,
-                       fdm_ptr->cpu_state_data.destination_address - 1,
-                       fdm_ptr->cpu_state_data.destination_address -
+                       be64_to_cpu(fdm_ptr->cpu_state_data.destination_address) - 1,
+                       be64_to_cpu(fdm_ptr->cpu_state_data.destination_address) -
                        fw_dump.reserve_dump_area_start,
-                       fdm_ptr->cpu_state_data.destination_address -
+                       be64_to_cpu(fdm_ptr->cpu_state_data.destination_address) -
                        fw_dump.reserve_dump_area_start);
 out:
        if (fdm_active)
index 155013d..b15194e 100644 (file)
@@ -266,13 +266,3 @@ int pcibus_to_node(struct pci_bus *bus)
 }
 EXPORT_SYMBOL(pcibus_to_node);
 #endif
-
-static void quirk_radeon_32bit_msi(struct pci_dev *dev)
-{
-       struct pci_dn *pdn = pci_get_pdn(dev);
-
-       if (pdn)
-               pdn->force_32bit_msi = true;
-}
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x68f2, quirk_radeon_32bit_msi);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0xaa68, quirk_radeon_32bit_msi);
index 23eb9a9..c62be60 100644 (file)
@@ -30,8 +30,8 @@
 V_FUNCTION_BEGIN(__kernel_getcpu)
   .cfi_startproc
        mfspr   r5,SPRN_SPRG_VDSO_READ
-       cmpdi   cr0,r3,0
-       cmpdi   cr1,r4,0
+       cmpwi   cr0,r3,0
+       cmpwi   cr1,r4,0
        clrlwi  r6,r5,16
        rlwinm  r7,r5,16,31-15,31-0
        beq     cr0,1f
index cad68ff..415a51b 100644 (file)
@@ -103,7 +103,7 @@ unsigned long __max_low_memory = MAX_LOW_MEM;
 /*
  * Check for command-line options that affect what MMU_init will do.
  */
-void MMU_setup(void)
+void __init MMU_setup(void)
 {
        /* Check for nobats option (used in mapin_ram). */
        if (strstr(boot_command_line, "nobats")) {
index 5e1ed15..b322bfb 100644 (file)
@@ -57,7 +57,7 @@ static void print_hmi_event_info(struct OpalHMIEvent *hmi_evt)
        };
 
        /* Print things out */
-       if (hmi_evt->version != OpalHMIEvt_V1) {
+       if (hmi_evt->version < OpalHMIEvt_V1) {
                pr_err("HMI Interrupt, Unknown event version %d !\n",
                        hmi_evt->version);
                return;
index ad4b31d..e4169d6 100644 (file)
@@ -216,14 +216,54 @@ static ssize_t lpc_debug_read(struct file *filp, char __user *ubuf,
                                   &data, len);
                if (rc)
                        return -ENXIO;
+
+               /*
+                * Now there is some trickery with the data returned by OPAL
+                * as it's the desired data right justified in a 32-bit BE
+                * word.
+                *
+                * This is a very bad interface and I'm to blame for it :-(
+                *
+                * So we can't just apply a 32-bit swap to what comes from OPAL,
+                * because user space expects the *bytes* to be in their proper
+                * respective positions (ie, LPC position).
+                *
+                * So what we really want to do here is to shift data right
+                * appropriately on a LE kernel.
+                *
+                * IE. If the LPC transaction has bytes B0, B1, B2 and B3 in that
+                * order, we have in memory written to by OPAL at the "data"
+                * pointer:
+                *
+                *               Bytes:      OPAL "data"   LE "data"
+                *   32-bit:   B0 B1 B2 B3   B0B1B2B3      B3B2B1B0
+                *   16-bit:   B0 B1         0000B0B1      B1B00000
+                *    8-bit:   B0            000000B0      B0000000
+                *
+                * So a BE kernel will have the leftmost of the above in the MSB
+                * and rightmost in the LSB and can just then "cast" the u32 "data"
+                * down to the appropriate quantity and write it.
+                *
+                * However, an LE kernel can't. It doesn't need to swap because a
+                * load from data followed by a store to user are going to preserve
+                * the byte ordering which is the wire byte order which is what the
+                * user wants, but in order to "crop" to the right size, we need to
+                * shift right first.
+                */
                switch(len) {
                case 4:
                        rc = __put_user((u32)data, (u32 __user *)ubuf);
                        break;
                case 2:
+#ifdef __LITTLE_ENDIAN__
+                       data >>= 16;
+#endif
                        rc = __put_user((u16)data, (u16 __user *)ubuf);
                        break;
                default:
+#ifdef __LITTLE_ENDIAN__
+                       data >>= 24;
+#endif
                        rc = __put_user((u8)data, (u8 __user *)ubuf);
                        break;
                }
@@ -263,12 +303,31 @@ static ssize_t lpc_debug_write(struct file *filp, const char __user *ubuf,
                        else if (todo > 1 && (pos & 1) == 0)
                                len = 2;
                }
+
+               /*
+                * Similarly to the read case, we have some trickery here but
+                * it's different to handle. We need to pass the value to OPAL in
+                * a register whose layout depends on the access size. We want
+                * to reproduce the memory layout of the user, however we aren't
+                * doing a load from user and a store to another memory location
+                * which would achieve that. Here we pass the value to OPAL via
+                * a register which is expected to contain the "BE" interpretation
+                * of the byte sequence. IE: for a 32-bit access, byte 0 should be
+                * in the MSB. So here we *do* need to byteswap on LE.
+                *
+                *           User bytes:    LE "data"  OPAL "data"
+                *  32-bit:  B0 B1 B2 B3    B3B2B1B0   B0B1B2B3
+                *  16-bit:  B0 B1          0000B1B0   0000B0B1
+                *   8-bit:  B0             000000B0   000000B0
+                */
                switch(len) {
                case 4:
                        rc = __get_user(data, (u32 __user *)ubuf);
+                       data = cpu_to_be32(data);
                        break;
                case 2:
                        rc = __get_user(data, (u16 __user *)ubuf);
+                       data = cpu_to_be16(data);
                        break;
                default:
                        rc = __get_user(data, (u8 __user *)ubuf);
index 468a0f2..3ba435e 100644 (file)
@@ -1509,7 +1509,6 @@ static int pnv_pci_ioda_msi_setup(struct pnv_phb *phb, struct pci_dev *dev,
                                  unsigned int is_64, struct msi_msg *msg)
 {
        struct pnv_ioda_pe *pe = pnv_ioda_get_pe(dev);
-       struct pci_dn *pdn = pci_get_pdn(dev);
        unsigned int xive_num = hwirq - phb->msi_base;
        __be32 data;
        int rc;
@@ -1523,7 +1522,7 @@ static int pnv_pci_ioda_msi_setup(struct pnv_phb *phb, struct pci_dev *dev,
                return -ENXIO;
 
        /* Force 32-bit MSI on some broken devices */
-       if (pdn && pdn->force_32bit_msi)
+       if (dev->no_64bit_msi)
                is_64 = 0;
 
        /* Assign XIVE to PE */
@@ -1997,7 +1996,7 @@ static void __init pnv_pci_init_ioda_phb(struct device_node *np,
        if (is_kdump_kernel()) {
                pr_info("  Issue PHB reset ...\n");
                ioda_eeh_phb_reset(hose, EEH_RESET_FUNDAMENTAL);
-               ioda_eeh_phb_reset(hose, OPAL_DEASSERT_RESET);
+               ioda_eeh_phb_reset(hose, EEH_RESET_DEACTIVATE);
        }
 
        /* Configure M64 window */
index b2187d0..4b20f2c 100644 (file)
@@ -50,7 +50,6 @@ static int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
 {
        struct pci_controller *hose = pci_bus_to_host(pdev->bus);
        struct pnv_phb *phb = hose->private_data;
-       struct pci_dn *pdn = pci_get_pdn(pdev);
        struct msi_desc *entry;
        struct msi_msg msg;
        int hwirq;
@@ -60,7 +59,7 @@ static int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
        if (WARN_ON(!phb) || !phb->msi_bmp.bitmap)
                return -ENODEV;
 
-       if (pdn && pdn->force_32bit_msi && !phb->msi32_support)
+       if (pdev->no_64bit_msi && !phb->msi32_support)
                return -ENODEV;
 
        list_for_each_entry(entry, &pdev->msi_list, list) {
index 6ad83bd..c22bb1b 100644 (file)
@@ -382,7 +382,7 @@ static int dlpar_online_cpu(struct device_node *dn)
                        BUG_ON(get_cpu_current_state(cpu)
                                        != CPU_STATE_OFFLINE);
                        cpu_maps_update_done();
-                       rc = cpu_up(cpu);
+                       rc = device_online(get_cpu_device(cpu));
                        if (rc)
                                goto out;
                        cpu_maps_update_begin();
@@ -467,7 +467,7 @@ static int dlpar_offline_cpu(struct device_node *dn)
                        if (get_cpu_current_state(cpu) == CPU_STATE_ONLINE) {
                                set_preferred_offline_state(cpu, CPU_STATE_OFFLINE);
                                cpu_maps_update_done();
-                               rc = cpu_down(cpu);
+                               rc = device_offline(get_cpu_device(cpu));
                                if (rc)
                                        goto out;
                                cpu_maps_update_begin();
index 8c509d5..f6880d2 100644 (file)
@@ -43,6 +43,7 @@
 #include <asm/trace.h>
 #include <asm/firmware.h>
 #include <asm/plpar_wrappers.h>
+#include <asm/fadump.h>
 
 #include "pseries.h"
 
@@ -247,8 +248,17 @@ static void pSeries_lpar_hptab_clear(void)
        }
 
 #ifdef __LITTLE_ENDIAN__
-       /* Reset exceptions to big endian */
-       if (firmware_has_feature(FW_FEATURE_SET_MODE)) {
+       /*
+        * Reset exceptions to big endian.
+        *
+        * FIXME this is a hack for kexec, we need to reset the exception
+        * endian before starting the new kernel and this is a convenient place
+        * to do it.
+        *
+        * This is also called on boot when a fadump happens. In that case we
+        * must not change the exception endian mode.
+        */
+       if (firmware_has_feature(FW_FEATURE_SET_MODE) && !is_fadump_active()) {
                long rc;
 
                rc = pseries_big_endian_exceptions();
index 8ab5add..8b909e9 100644 (file)
@@ -420,7 +420,7 @@ static int rtas_setup_msi_irqs(struct pci_dev *pdev, int nvec_in, int type)
         */
 again:
        if (type == PCI_CAP_ID_MSI) {
-               if (pdn->force_32bit_msi) {
+               if (pdev->no_64bit_msi) {
                        rc = rtas_change_msi(pdn, RTAS_CHANGE_32MSI_FN, nvec);
                        if (rc < 0) {
                                /*
index de40b48..da08ed0 100644 (file)
@@ -361,7 +361,7 @@ static int fsl_msi_setup_hwirq(struct fsl_msi *msi, struct platform_device *dev,
        cascade_data->virq = virt_msir;
        msi->cascade_array[irq_index] = cascade_data;
 
-       ret = request_irq(virt_msir, fsl_msi_cascade, 0,
+       ret = request_irq(virt_msir, fsl_msi_cascade, IRQF_NO_THREAD,
                          "fsl-msi-cascade", cascade_data);
        if (ret) {
                dev_err(&dev->dev, "failed to request_irq(%d), ret = %d\n",
index b988b5a..c8efbb3 100644 (file)
@@ -293,10 +293,10 @@ static inline void disable_surveillance(void)
        args.token = rtas_token("set-indicator");
        if (args.token == RTAS_UNKNOWN_SERVICE)
                return;
-       args.nargs = 3;
-       args.nret = 1;
+       args.nargs = cpu_to_be32(3);
+       args.nret = cpu_to_be32(1);
        args.rets = &args.args[3];
-       args.args[0] = SURVEILLANCE_TOKEN;
+       args.args[0] = cpu_to_be32(SURVEILLANCE_TOKEN);
        args.args[1] = 0;
        args.args[2] = 0;
        enter_rtas(__pa(&args));
index 9d94fdd..9432d0f 100644 (file)
@@ -35,7 +35,6 @@ CONFIG_MODULE_UNLOAD=y
 CONFIG_MODULE_FORCE_UNLOAD=y
 CONFIG_MODVERSIONS=y
 CONFIG_MODULE_SRCVERSION_ALL=y
-CONFIG_BLK_DEV_INTEGRITY=y
 CONFIG_BLK_DEV_THROTTLING=y
 CONFIG_PARTITION_ADVANCED=y
 CONFIG_IBM_PARTITION=y
@@ -245,6 +244,7 @@ CONFIG_NF_TABLES_IPV4=m
 CONFIG_NFT_CHAIN_ROUTE_IPV4=m
 CONFIG_NFT_CHAIN_NAT_IPV4=m
 CONFIG_NF_TABLES_ARP=m
+CONFIG_NF_NAT_IPV4=m
 CONFIG_IP_NF_IPTABLES=m
 CONFIG_IP_NF_MATCH_AH=m
 CONFIG_IP_NF_MATCH_ECN=m
@@ -252,11 +252,6 @@ CONFIG_IP_NF_MATCH_RPFILTER=m
 CONFIG_IP_NF_MATCH_TTL=m
 CONFIG_IP_NF_FILTER=m
 CONFIG_IP_NF_TARGET_REJECT=m
-CONFIG_IP_NF_TARGET_ULOG=m
-CONFIG_NF_NAT_IPV4=m
-CONFIG_IP_NF_TARGET_MASQUERADE=m
-CONFIG_IP_NF_TARGET_NETMAP=m
-CONFIG_IP_NF_TARGET_REDIRECT=m
 CONFIG_IP_NF_MANGLE=m
 CONFIG_IP_NF_TARGET_CLUSTERIP=m
 CONFIG_IP_NF_TARGET_ECN=m
@@ -270,6 +265,7 @@ CONFIG_NF_CONNTRACK_IPV6=m
 CONFIG_NF_TABLES_IPV6=m
 CONFIG_NFT_CHAIN_ROUTE_IPV6=m
 CONFIG_NFT_CHAIN_NAT_IPV6=m
+CONFIG_NF_NAT_IPV6=m
 CONFIG_IP6_NF_IPTABLES=m
 CONFIG_IP6_NF_MATCH_AH=m
 CONFIG_IP6_NF_MATCH_EUI64=m
@@ -286,9 +282,6 @@ CONFIG_IP6_NF_TARGET_REJECT=m
 CONFIG_IP6_NF_MANGLE=m
 CONFIG_IP6_NF_RAW=m
 CONFIG_IP6_NF_SECURITY=m
-CONFIG_NF_NAT_IPV6=m
-CONFIG_IP6_NF_TARGET_MASQUERADE=m
-CONFIG_IP6_NF_TARGET_NPT=m
 CONFIG_NF_TABLES_BRIDGE=m
 CONFIG_NET_SCTPPROBE=m
 CONFIG_RDS=m
@@ -374,14 +367,13 @@ CONFIG_BLK_DEV_SR=m
 CONFIG_CHR_DEV_SG=y
 CONFIG_CHR_DEV_SCH=m
 CONFIG_SCSI_ENCLOSURE=m
-CONFIG_SCSI_MULTI_LUN=y
 CONFIG_SCSI_CONSTANTS=y
 CONFIG_SCSI_LOGGING=y
 CONFIG_SCSI_SPI_ATTRS=m
+CONFIG_SCSI_FC_ATTRS=y
 CONFIG_SCSI_SAS_LIBSAS=m
 CONFIG_SCSI_SRP_ATTRS=m
 CONFIG_ISCSI_TCP=m
-CONFIG_LIBFCOE=m
 CONFIG_SCSI_DEBUG=m
 CONFIG_ZFCP=y
 CONFIG_SCSI_VIRTIO=m
@@ -427,7 +419,6 @@ CONFIG_VIRTIO_NET=m
 CONFIG_NLMON=m
 CONFIG_VHOST_NET=m
 # CONFIG_NET_VENDOR_ARC is not set
-# CONFIG_NET_CADENCE is not set
 # CONFIG_NET_VENDOR_CHELSIO is not set
 # CONFIG_NET_VENDOR_INTEL is not set
 # CONFIG_NET_VENDOR_MARVELL is not set
@@ -481,14 +472,14 @@ CONFIG_JFS_FS=m
 CONFIG_JFS_POSIX_ACL=y
 CONFIG_JFS_SECURITY=y
 CONFIG_JFS_STATISTICS=y
-CONFIG_XFS_FS=m
+CONFIG_XFS_FS=y
 CONFIG_XFS_QUOTA=y
 CONFIG_XFS_POSIX_ACL=y
 CONFIG_XFS_RT=y
 CONFIG_XFS_DEBUG=y
 CONFIG_GFS2_FS=m
 CONFIG_OCFS2_FS=m
-CONFIG_BTRFS_FS=m
+CONFIG_BTRFS_FS=y
 CONFIG_BTRFS_FS_POSIX_ACL=y
 CONFIG_NILFS2_FS=m
 CONFIG_FANOTIFY=y
@@ -574,7 +565,6 @@ CONFIG_DEBUG_SHIRQ=y
 CONFIG_DETECT_HUNG_TASK=y
 CONFIG_TIMER_STATS=y
 CONFIG_DEBUG_RT_MUTEXES=y
-CONFIG_RT_MUTEX_TESTER=y
 CONFIG_DEBUG_WW_MUTEX_SLOWPATH=y
 CONFIG_PROVE_LOCKING=y
 CONFIG_LOCK_STAT=y
@@ -600,8 +590,13 @@ CONFIG_FAULT_INJECTION_DEBUG_FS=y
 CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
 CONFIG_LATENCYTOP=y
 CONFIG_DEBUG_STRICT_USER_COPY_CHECKS=y
+CONFIG_IRQSOFF_TRACER=y
+CONFIG_PREEMPT_TRACER=y
+CONFIG_SCHED_TRACER=y
+CONFIG_FTRACE_SYSCALLS=y
+CONFIG_STACK_TRACER=y
 CONFIG_BLK_DEV_IO_TRACE=y
-# CONFIG_KPROBE_EVENT is not set
+CONFIG_UPROBE_EVENT=y
 CONFIG_LKDTM=m
 CONFIG_TEST_LIST_SORT=y
 CONFIG_KPROBES_SANITY_TEST=y
@@ -609,7 +604,10 @@ CONFIG_RBTREE_TEST=y
 CONFIG_INTERVAL_TREE_TEST=m
 CONFIG_PERCPU_TEST=m
 CONFIG_ATOMIC64_SELFTEST=y
+CONFIG_TEST_STRING_HELPERS=y
+CONFIG_TEST_KSTRTOX=y
 CONFIG_DMA_API_DEBUG=y
+CONFIG_TEST_BPF=m
 # CONFIG_STRICT_DEVMEM is not set
 CONFIG_S390_PTDUMP=y
 CONFIG_ENCRYPTED_KEYS=m
@@ -673,12 +671,6 @@ CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=m
 CONFIG_X509_CERTIFICATE_PARSER=m
 CONFIG_CRC7=m
 CONFIG_CRC8=m
-CONFIG_XZ_DEC_X86=y
-CONFIG_XZ_DEC_POWERPC=y
-CONFIG_XZ_DEC_IA64=y
-CONFIG_XZ_DEC_ARM=y
-CONFIG_XZ_DEC_ARMTHUMB=y
-CONFIG_XZ_DEC_SPARC=y
 CONFIG_CORDIC=m
 CONFIG_CMM=m
 CONFIG_APPLDATA_BASE=y
index 90f514b..219dca6 100644 (file)
@@ -35,7 +35,6 @@ CONFIG_MODULE_UNLOAD=y
 CONFIG_MODULE_FORCE_UNLOAD=y
 CONFIG_MODVERSIONS=y
 CONFIG_MODULE_SRCVERSION_ALL=y
-CONFIG_BLK_DEV_INTEGRITY=y
 CONFIG_BLK_DEV_THROTTLING=y
 CONFIG_PARTITION_ADVANCED=y
 CONFIG_IBM_PARTITION=y
@@ -243,6 +242,7 @@ CONFIG_NF_TABLES_IPV4=m
 CONFIG_NFT_CHAIN_ROUTE_IPV4=m
 CONFIG_NFT_CHAIN_NAT_IPV4=m
 CONFIG_NF_TABLES_ARP=m
+CONFIG_NF_NAT_IPV4=m
 CONFIG_IP_NF_IPTABLES=m
 CONFIG_IP_NF_MATCH_AH=m
 CONFIG_IP_NF_MATCH_ECN=m
@@ -250,11 +250,6 @@ CONFIG_IP_NF_MATCH_RPFILTER=m
 CONFIG_IP_NF_MATCH_TTL=m
 CONFIG_IP_NF_FILTER=m
 CONFIG_IP_NF_TARGET_REJECT=m
-CONFIG_IP_NF_TARGET_ULOG=m
-CONFIG_NF_NAT_IPV4=m
-CONFIG_IP_NF_TARGET_MASQUERADE=m
-CONFIG_IP_NF_TARGET_NETMAP=m
-CONFIG_IP_NF_TARGET_REDIRECT=m
 CONFIG_IP_NF_MANGLE=m
 CONFIG_IP_NF_TARGET_CLUSTERIP=m
 CONFIG_IP_NF_TARGET_ECN=m
@@ -268,6 +263,7 @@ CONFIG_NF_CONNTRACK_IPV6=m
 CONFIG_NF_TABLES_IPV6=m
 CONFIG_NFT_CHAIN_ROUTE_IPV6=m
 CONFIG_NFT_CHAIN_NAT_IPV6=m
+CONFIG_NF_NAT_IPV6=m
 CONFIG_IP6_NF_IPTABLES=m
 CONFIG_IP6_NF_MATCH_AH=m
 CONFIG_IP6_NF_MATCH_EUI64=m
@@ -284,9 +280,6 @@ CONFIG_IP6_NF_TARGET_REJECT=m
 CONFIG_IP6_NF_MANGLE=m
 CONFIG_IP6_NF_RAW=m
 CONFIG_IP6_NF_SECURITY=m
-CONFIG_NF_NAT_IPV6=m
-CONFIG_IP6_NF_TARGET_MASQUERADE=m
-CONFIG_IP6_NF_TARGET_NPT=m
 CONFIG_NF_TABLES_BRIDGE=m
 CONFIG_NET_SCTPPROBE=m
 CONFIG_RDS=m
@@ -371,14 +364,13 @@ CONFIG_BLK_DEV_SR=m
 CONFIG_CHR_DEV_SG=y
 CONFIG_CHR_DEV_SCH=m
 CONFIG_SCSI_ENCLOSURE=m
-CONFIG_SCSI_MULTI_LUN=y
 CONFIG_SCSI_CONSTANTS=y
 CONFIG_SCSI_LOGGING=y
 CONFIG_SCSI_SPI_ATTRS=m
+CONFIG_SCSI_FC_ATTRS=y
 CONFIG_SCSI_SAS_LIBSAS=m
 CONFIG_SCSI_SRP_ATTRS=m
 CONFIG_ISCSI_TCP=m
-CONFIG_LIBFCOE=m
 CONFIG_SCSI_DEBUG=m
 CONFIG_ZFCP=y
 CONFIG_SCSI_VIRTIO=m
@@ -424,7 +416,6 @@ CONFIG_VIRTIO_NET=m
 CONFIG_NLMON=m
 CONFIG_VHOST_NET=m
 # CONFIG_NET_VENDOR_ARC is not set
-# CONFIG_NET_CADENCE is not set
 # CONFIG_NET_VENDOR_CHELSIO is not set
 # CONFIG_NET_VENDOR_INTEL is not set
 # CONFIG_NET_VENDOR_MARVELL is not set
@@ -478,13 +469,13 @@ CONFIG_JFS_FS=m
 CONFIG_JFS_POSIX_ACL=y
 CONFIG_JFS_SECURITY=y
 CONFIG_JFS_STATISTICS=y
-CONFIG_XFS_FS=m
+CONFIG_XFS_FS=y
 CONFIG_XFS_QUOTA=y
 CONFIG_XFS_POSIX_ACL=y
 CONFIG_XFS_RT=y
 CONFIG_GFS2_FS=m
 CONFIG_OCFS2_FS=m
-CONFIG_BTRFS_FS=m
+CONFIG_BTRFS_FS=y
 CONFIG_BTRFS_FS_POSIX_ACL=y
 CONFIG_NILFS2_FS=m
 CONFIG_FANOTIFY=y
@@ -626,12 +617,6 @@ CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=m
 CONFIG_X509_CERTIFICATE_PARSER=m
 CONFIG_CRC7=m
 CONFIG_CRC8=m
-CONFIG_XZ_DEC_X86=y
-CONFIG_XZ_DEC_POWERPC=y
-CONFIG_XZ_DEC_IA64=y
-CONFIG_XZ_DEC_ARM=y
-CONFIG_XZ_DEC_ARMTHUMB=y
-CONFIG_XZ_DEC_SPARC=y
 CONFIG_CORDIC=m
 CONFIG_CMM=m
 CONFIG_APPLDATA_BASE=y
index 13559d3..822c2f2 100644 (file)
@@ -33,7 +33,6 @@ CONFIG_MODULE_UNLOAD=y
 CONFIG_MODULE_FORCE_UNLOAD=y
 CONFIG_MODVERSIONS=y
 CONFIG_MODULE_SRCVERSION_ALL=y
-CONFIG_BLK_DEV_INTEGRITY=y
 CONFIG_BLK_DEV_THROTTLING=y
 CONFIG_PARTITION_ADVANCED=y
 CONFIG_IBM_PARTITION=y
@@ -241,6 +240,7 @@ CONFIG_NF_TABLES_IPV4=m
 CONFIG_NFT_CHAIN_ROUTE_IPV4=m
 CONFIG_NFT_CHAIN_NAT_IPV4=m
 CONFIG_NF_TABLES_ARP=m
+CONFIG_NF_NAT_IPV4=m
 CONFIG_IP_NF_IPTABLES=m
 CONFIG_IP_NF_MATCH_AH=m
 CONFIG_IP_NF_MATCH_ECN=m
@@ -248,11 +248,6 @@ CONFIG_IP_NF_MATCH_RPFILTER=m
 CONFIG_IP_NF_MATCH_TTL=m
 CONFIG_IP_NF_FILTER=m
 CONFIG_IP_NF_TARGET_REJECT=m
-CONFIG_IP_NF_TARGET_ULOG=m
-CONFIG_NF_NAT_IPV4=m
-CONFIG_IP_NF_TARGET_MASQUERADE=m
-CONFIG_IP_NF_TARGET_NETMAP=m
-CONFIG_IP_NF_TARGET_REDIRECT=m
 CONFIG_IP_NF_MANGLE=m
 CONFIG_IP_NF_TARGET_CLUSTERIP=m
 CONFIG_IP_NF_TARGET_ECN=m
@@ -266,6 +261,7 @@ CONFIG_NF_CONNTRACK_IPV6=m
 CONFIG_NF_TABLES_IPV6=m
 CONFIG_NFT_CHAIN_ROUTE_IPV6=m
 CONFIG_NFT_CHAIN_NAT_IPV6=m
+CONFIG_NF_NAT_IPV6=m
 CONFIG_IP6_NF_IPTABLES=m
 CONFIG_IP6_NF_MATCH_AH=m
 CONFIG_IP6_NF_MATCH_EUI64=m
@@ -282,9 +278,6 @@ CONFIG_IP6_NF_TARGET_REJECT=m
 CONFIG_IP6_NF_MANGLE=m
 CONFIG_IP6_NF_RAW=m
 CONFIG_IP6_NF_SECURITY=m
-CONFIG_NF_NAT_IPV6=m
-CONFIG_IP6_NF_TARGET_MASQUERADE=m
-CONFIG_IP6_NF_TARGET_NPT=m
 CONFIG_NF_TABLES_BRIDGE=m
 CONFIG_NET_SCTPPROBE=m
 CONFIG_RDS=m
@@ -369,14 +362,13 @@ CONFIG_BLK_DEV_SR=m
 CONFIG_CHR_DEV_SG=y
 CONFIG_CHR_DEV_SCH=m
 CONFIG_SCSI_ENCLOSURE=m
-CONFIG_SCSI_MULTI_LUN=y
 CONFIG_SCSI_CONSTANTS=y
 CONFIG_SCSI_LOGGING=y
 CONFIG_SCSI_SPI_ATTRS=m
+CONFIG_SCSI_FC_ATTRS=y
 CONFIG_SCSI_SAS_LIBSAS=m
 CONFIG_SCSI_SRP_ATTRS=m
 CONFIG_ISCSI_TCP=m
-CONFIG_LIBFCOE=m
 CONFIG_SCSI_DEBUG=m
 CONFIG_ZFCP=y
 CONFIG_SCSI_VIRTIO=m
@@ -422,7 +414,6 @@ CONFIG_VIRTIO_NET=m
 CONFIG_NLMON=m
 CONFIG_VHOST_NET=m
 # CONFIG_NET_VENDOR_ARC is not set
-# CONFIG_NET_CADENCE is not set
 # CONFIG_NET_VENDOR_CHELSIO is not set
 # CONFIG_NET_VENDOR_INTEL is not set
 # CONFIG_NET_VENDOR_MARVELL is not set
@@ -476,13 +467,13 @@ CONFIG_JFS_FS=m
 CONFIG_JFS_POSIX_ACL=y
 CONFIG_JFS_SECURITY=y
 CONFIG_JFS_STATISTICS=y
-CONFIG_XFS_FS=m
+CONFIG_XFS_FS=y
 CONFIG_XFS_QUOTA=y
 CONFIG_XFS_POSIX_ACL=y
 CONFIG_XFS_RT=y
 CONFIG_GFS2_FS=m
 CONFIG_OCFS2_FS=m
-CONFIG_BTRFS_FS=m
+CONFIG_BTRFS_FS=y
 CONFIG_BTRFS_FS_POSIX_ACL=y
 CONFIG_NILFS2_FS=m
 CONFIG_FANOTIFY=y
@@ -550,8 +541,11 @@ CONFIG_TIMER_STATS=y
 CONFIG_RCU_TORTURE_TEST=m
 CONFIG_RCU_CPU_STALL_TIMEOUT=60
 CONFIG_LATENCYTOP=y
+CONFIG_SCHED_TRACER=y
+CONFIG_FTRACE_SYSCALLS=y
+CONFIG_STACK_TRACER=y
 CONFIG_BLK_DEV_IO_TRACE=y
-# CONFIG_KPROBE_EVENT is not set
+CONFIG_UPROBE_EVENT=y
 CONFIG_LKDTM=m
 CONFIG_PERCPU_TEST=m
 CONFIG_ATOMIC64_SELFTEST=y
@@ -618,12 +612,6 @@ CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=m
 CONFIG_X509_CERTIFICATE_PARSER=m
 CONFIG_CRC7=m
 CONFIG_CRC8=m
-CONFIG_XZ_DEC_X86=y
-CONFIG_XZ_DEC_POWERPC=y
-CONFIG_XZ_DEC_IA64=y
-CONFIG_XZ_DEC_ARM=y
-CONFIG_XZ_DEC_ARMTHUMB=y
-CONFIG_XZ_DEC_SPARC=y
 CONFIG_CORDIC=m
 CONFIG_CMM=m
 CONFIG_APPLDATA_BASE=y
index e376789..9d63051 100644 (file)
@@ -22,8 +22,8 @@ CONFIG_HZ_100=y
 CONFIG_CRASH_DUMP=y
 # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
 # CONFIG_SECCOMP is not set
-# CONFIG_IUCV is not set
 CONFIG_NET=y
+# CONFIG_IUCV is not set
 CONFIG_ATM=y
 CONFIG_ATM_LANE=y
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
@@ -36,9 +36,9 @@ CONFIG_ENCLOSURE_SERVICES=y
 CONFIG_SCSI=y
 CONFIG_BLK_DEV_SD=y
 CONFIG_SCSI_ENCLOSURE=y
-CONFIG_SCSI_MULTI_LUN=y
 CONFIG_SCSI_CONSTANTS=y
 CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_FC_ATTRS=y
 CONFIG_SCSI_SRP_ATTRS=y
 CONFIG_ZFCP=y
 # CONFIG_INPUT_MOUSEDEV_PSAUX is not set
@@ -75,12 +75,6 @@ CONFIG_DEBUG_KERNEL=y
 CONFIG_RCU_CPU_STALL_TIMEOUT=60
 # CONFIG_FTRACE is not set
 # CONFIG_STRICT_DEVMEM is not set
-CONFIG_XZ_DEC_X86=y
-CONFIG_XZ_DEC_POWERPC=y
-CONFIG_XZ_DEC_IA64=y
-CONFIG_XZ_DEC_ARM=y
-CONFIG_XZ_DEC_ARMTHUMB=y
-CONFIG_XZ_DEC_SPARC=y
 # CONFIG_PFAULT is not set
 # CONFIG_S390_HYPFS_FS is not set
 # CONFIG_VIRTUALIZATION is not set
index fab35a8..785c5f2 100644 (file)
@@ -92,10 +92,10 @@ CONFIG_CHR_DEV_ST=y
 CONFIG_BLK_DEV_SR=y
 CONFIG_BLK_DEV_SR_VENDOR=y
 CONFIG_CHR_DEV_SG=y
-CONFIG_SCSI_MULTI_LUN=y
 CONFIG_SCSI_CONSTANTS=y
 CONFIG_SCSI_LOGGING=y
 CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_SCSI_FC_ATTRS=y
 CONFIG_ZFCP=y
 CONFIG_SCSI_VIRTIO=y
 CONFIG_NETDEVICES=y
@@ -164,14 +164,13 @@ CONFIG_CRYPTO_CMAC=m
 CONFIG_CRYPTO_XCBC=m
 CONFIG_CRYPTO_VMAC=m
 CONFIG_CRYPTO_CRC32=m
-CONFIG_CRYPTO_CRCT10DIF=m
 CONFIG_CRYPTO_MD4=m
 CONFIG_CRYPTO_MICHAEL_MIC=m
 CONFIG_CRYPTO_RMD128=m
 CONFIG_CRYPTO_RMD160=m
 CONFIG_CRYPTO_RMD256=m
 CONFIG_CRYPTO_RMD320=m
-CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA256=y
 CONFIG_CRYPTO_SHA512=m
 CONFIG_CRYPTO_TGR192=m
 CONFIG_CRYPTO_WP512=m
index 51d14fe..ca1cabb 100644 (file)
@@ -121,6 +121,8 @@ unsigned long __kprobes prepare_ftrace_return(unsigned long parent,
 {
        struct ftrace_graph_ent trace;
 
+       if (unlikely(ftrace_graph_is_dead()))
+               goto out;
        if (unlikely(atomic_read(&current->tracing_graph_pause)))
                goto out;
        ip = (ip & PSW_ADDR_INSN) - MCOUNT_INSN_SIZE;
index dd1c24c..3f51cf4 100644 (file)
@@ -54,12 +54,8 @@ void s390_handle_mcck(void)
         */
        local_irq_save(flags);
        local_mcck_disable();
-       /*
-        * Ummm... Does this make sense at all? Copying the percpu struct
-        * and then zapping it one statement later?
-        */
-       memcpy(&mcck, this_cpu_ptr(&cpu_mcck), sizeof(mcck));
-       memset(&mcck, 0, sizeof(struct mcck_struct));
+       mcck = *this_cpu_ptr(&cpu_mcck);
+       memset(this_cpu_ptr(&cpu_mcck), 0, sizeof(mcck));
        clear_cpu_flag(CIF_MCCK_PENDING);
        local_mcck_enable();
        local_irq_restore(flags);
index 48c2206..5eec9af 100644 (file)
@@ -19,6 +19,7 @@
        .type  __kernel_clock_gettime,@function
 __kernel_clock_gettime:
        .cfi_startproc
+       ahi     %r15,-16
        basr    %r5,0
 0:     al      %r5,21f-0b(%r5)                 /* get &_vdso_data */
        chi     %r2,__CLOCK_REALTIME_COARSE
@@ -34,8 +35,8 @@ __kernel_clock_gettime:
 1:     l       %r4,__VDSO_UPD_COUNT+4(%r5)     /* load update counter */
        tml     %r4,0x0001                      /* pending update ? loop */
        jnz     1b
-       stcke   24(%r15)                        /* Store TOD clock */
-       lm      %r0,%r1,25(%r15)
+       stcke   0(%r15)                         /* Store TOD clock */
+       lm      %r0,%r1,1(%r15)
        s       %r0,__VDSO_XTIME_STAMP(%r5)     /* TOD - cycle_last */
        sl      %r1,__VDSO_XTIME_STAMP+4(%r5)
        brc     3,2f
@@ -70,6 +71,7 @@ __kernel_clock_gettime:
 8:     st      %r2,0(%r3)                      /* store tp->tv_sec */
        st      %r1,4(%r3)                      /* store tp->tv_nsec */
        lhi     %r2,0
+       ahi     %r15,16
        br      %r14
 
        /* CLOCK_MONOTONIC_COARSE */
@@ -96,8 +98,8 @@ __kernel_clock_gettime:
 11:    l       %r4,__VDSO_UPD_COUNT+4(%r5)     /* load update counter */
        tml     %r4,0x0001                      /* pending update ? loop */
        jnz     11b
-       stcke   24(%r15)                        /* Store TOD clock */
-       lm      %r0,%r1,25(%r15)
+       stcke   0(%r15)                         /* Store TOD clock */
+       lm      %r0,%r1,1(%r15)
        s       %r0,__VDSO_XTIME_STAMP(%r5)     /* TOD - cycle_last */
        sl      %r1,__VDSO_XTIME_STAMP+4(%r5)
        brc     3,12f
@@ -132,11 +134,13 @@ __kernel_clock_gettime:
 17:    st      %r2,0(%r3)                      /* store tp->tv_sec */
        st      %r1,4(%r3)                      /* store tp->tv_nsec */
        lhi     %r2,0
+       ahi     %r15,16
        br      %r14
 
        /* Fallback to system call */
 19:    lhi     %r1,__NR_clock_gettime
        svc     0
+       ahi     %r15,16
        br      %r14
 
 20:    .long   1000000000
index 60def5f..719de61 100644 (file)
@@ -19,6 +19,7 @@
        .type  __kernel_gettimeofday,@function
 __kernel_gettimeofday:
        .cfi_startproc
+       ahi     %r15,-16
        basr    %r5,0
 0:     al      %r5,13f-0b(%r5)                 /* get &_vdso_data */
 1:     ltr     %r3,%r3                         /* check if tz is NULL */
@@ -29,30 +30,30 @@ __kernel_gettimeofday:
        l       %r4,__VDSO_UPD_COUNT+4(%r5)     /* load update counter */
        tml     %r4,0x0001                      /* pending update ? loop */
        jnz     1b
-       stcke   24(%r15)                        /* Store TOD clock */
-       lm      %r0,%r1,25(%r15)
+       stcke   0(%r15)                         /* Store TOD clock */
+       lm      %r0,%r1,1(%r15)
        s       %r0,__VDSO_XTIME_STAMP(%r5)     /* TOD - cycle_last */
        sl      %r1,__VDSO_XTIME_STAMP+4(%r5)
        brc     3,3f
        ahi     %r0,-1
 3:     ms      %r0,__VDSO_TK_MULT(%r5)         /*  * tk->mult */
-       st      %r0,24(%r15)
+       st      %r0,0(%r15)
        l       %r0,__VDSO_TK_MULT(%r5)
        ltr     %r1,%r1
        mr      %r0,%r0
        jnm     4f
        a       %r0,__VDSO_TK_MULT(%r5)
-4:     al      %r0,24(%r15)
+4:     al      %r0,0(%r15)
        al      %r0,__VDSO_XTIME_NSEC(%r5)      /*  + xtime */
        al      %r1,__VDSO_XTIME_NSEC+4(%r5)
        brc     12,5f
        ahi     %r0,1
-5:     mvc     24(4,%r15),__VDSO_XTIME_SEC+4(%r5)
+5:     mvc     0(4,%r15),__VDSO_XTIME_SEC+4(%r5)
        cl      %r4,__VDSO_UPD_COUNT+4(%r5)     /* check update counter */
        jne     1b
        l       %r4,__VDSO_TK_SHIFT(%r5)        /* Timekeeper shift */
        srdl    %r0,0(%r4)                      /*  >> tk->shift */
-       l       %r4,24(%r15)                    /* get tv_sec from stack */
+       l       %r4,0(%r15)                     /* get tv_sec from stack */
        basr    %r5,0
 6:     ltr     %r0,%r0
        jnz     7f
@@ -71,6 +72,7 @@ __kernel_gettimeofday:
 9:     srl     %r0,6
        st      %r0,4(%r2)                      /* store tv->tv_usec */
 10:    slr     %r2,%r2
+       ahi     %r15,16
        br      %r14
 11:    .long   1000000000
 12:    .long   274877907
index 9d9761f..7699e73 100644 (file)
@@ -19,6 +19,7 @@
        .type  __kernel_clock_gettime,@function
 __kernel_clock_gettime:
        .cfi_startproc
+       aghi    %r15,-16
        larl    %r5,_vdso_data
        cghi    %r2,__CLOCK_REALTIME_COARSE
        je      4f
@@ -37,10 +38,10 @@ __kernel_clock_gettime:
 0:     lg      %r4,__VDSO_UPD_COUNT(%r5)       /* load update counter */
        tmll    %r4,0x0001                      /* pending update ? loop */
        jnz     0b
-       stcke   48(%r15)                        /* Store TOD clock */
+       stcke   0(%r15)                         /* Store TOD clock */
        lgf     %r2,__VDSO_TK_SHIFT(%r5)        /* Timekeeper shift */
        lg      %r0,__VDSO_WTOM_SEC(%r5)
-       lg      %r1,49(%r15)
+       lg      %r1,1(%r15)
        sg      %r1,__VDSO_XTIME_STAMP(%r5)     /* TOD - cycle_last */
        msgf    %r1,__VDSO_TK_MULT(%r5)         /*  * tk->mult */
        alg     %r1,__VDSO_WTOM_NSEC(%r5)
@@ -56,6 +57,7 @@ __kernel_clock_gettime:
 2:     stg     %r0,0(%r3)                      /* store tp->tv_sec */
        stg     %r1,8(%r3)                      /* store tp->tv_nsec */
        lghi    %r2,0
+       aghi    %r15,16
        br      %r14
 
        /* CLOCK_MONOTONIC_COARSE */
@@ -82,9 +84,9 @@ __kernel_clock_gettime:
 5:     lg      %r4,__VDSO_UPD_COUNT(%r5)       /* load update counter */
        tmll    %r4,0x0001                      /* pending update ? loop */
        jnz     5b
-       stcke   48(%r15)                        /* Store TOD clock */
+       stcke   0(%r15)                         /* Store TOD clock */
        lgf     %r2,__VDSO_TK_SHIFT(%r5)        /* Timekeeper shift */
-       lg      %r1,49(%r15)
+       lg      %r1,1(%r15)
        sg      %r1,__VDSO_XTIME_STAMP(%r5)     /* TOD - cycle_last */
        msgf    %r1,__VDSO_TK_MULT(%r5)         /*  * tk->mult */
        alg     %r1,__VDSO_XTIME_NSEC(%r5)      /*  + tk->xtime_nsec */
@@ -101,6 +103,7 @@ __kernel_clock_gettime:
 7:     stg     %r0,0(%r3)                      /* store tp->tv_sec */
        stg     %r1,8(%r3)                      /* store tp->tv_nsec */
        lghi    %r2,0
+       aghi    %r15,16
        br      %r14
 
        /* CLOCK_THREAD_CPUTIME_ID for this thread */
@@ -134,11 +137,13 @@ __kernel_clock_gettime:
        slgr    %r4,%r0                         /* r4 = tv_nsec */
        stg     %r4,8(%r3)
        lghi    %r2,0
+       aghi    %r15,16
        br      %r14
 
        /* Fallback to system call */
 12:    lghi    %r1,__NR_clock_gettime
        svc     0
+       aghi    %r15,16
        br      %r14
 
 13:    .quad   1000000000
index 7a34499..6ce4670 100644 (file)
@@ -19,6 +19,7 @@
        .type  __kernel_gettimeofday,@function
 __kernel_gettimeofday:
        .cfi_startproc
+       aghi    %r15,-16
        larl    %r5,_vdso_data
 0:     ltgr    %r3,%r3                         /* check if tz is NULL */
        je      1f
@@ -28,8 +29,8 @@ __kernel_gettimeofday:
        lg      %r4,__VDSO_UPD_COUNT(%r5)       /* load update counter */
        tmll    %r4,0x0001                      /* pending update ? loop */
        jnz     0b
-       stcke   48(%r15)                        /* Store TOD clock */
-       lg      %r1,49(%r15)
+       stcke   0(%r15)                         /* Store TOD clock */
+       lg      %r1,1(%r15)
        sg      %r1,__VDSO_XTIME_STAMP(%r5)     /* TOD - cycle_last */
        msgf    %r1,__VDSO_TK_MULT(%r5)         /*  * tk->mult */
        alg     %r1,__VDSO_XTIME_NSEC(%r5)      /*  + tk->xtime_nsec */
@@ -50,6 +51,7 @@ __kernel_gettimeofday:
        srlg    %r0,%r0,6
        stg     %r0,8(%r2)                      /* store tv->tv_usec */
 4:     lghi    %r2,0
+       aghi    %r15,16
        br      %r14
 5:     .quad   1000000000
        .long   274877907
index 416f2a3..7f0089d 100644 (file)
@@ -66,7 +66,11 @@ static int do_account_vtime(struct task_struct *tsk, int hardirq_offset)
        clock = S390_lowcore.last_update_clock;
        asm volatile(
                "       stpt    %0\n"   /* Store current cpu timer value */
+#ifdef CONFIG_HAVE_MARCH_Z9_109_FEATURES
+               "       stckf   %1"     /* Store current tod clock value */
+#else
                "       stck    %1"     /* Store current tod clock value */
+#endif
                : "=m" (S390_lowcore.last_update_timer),
                  "=m" (S390_lowcore.last_update_clock));
        S390_lowcore.system_timer += timer - S390_lowcore.last_update_timer;
index 765c177..0e69b7e 100644 (file)
@@ -22,7 +22,7 @@
 
 int atomic_add_return(int, atomic_t *);
 int atomic_cmpxchg(atomic_t *, int, int);
-#define atomic_xchg(v, new) (xchg(&((v)->counter), new))
+int atomic_xchg(atomic_t *, int);
 int __atomic_add_unless(atomic_t *, int, int);
 void atomic_set(atomic_t *, int);
 
index 32c29a1..d38b52d 100644 (file)
 #ifndef __ARCH_SPARC_CMPXCHG__
 #define __ARCH_SPARC_CMPXCHG__
 
-static inline unsigned long xchg_u32(__volatile__ unsigned long *m, unsigned long val)
-{
-       __asm__ __volatile__("swap [%2], %0"
-                            : "=&r" (val)
-                            : "0" (val), "r" (m)
-                            : "memory");
-       return val;
-}
-
+unsigned long __xchg_u32(volatile u32 *m, u32 new);
 void __xchg_called_with_bad_pointer(void);
 
 static inline unsigned long __xchg(unsigned long x, __volatile__ void * ptr, int size)
 {
        switch (size) {
        case 4:
-               return xchg_u32(ptr, x);
+               return __xchg_u32(ptr, x);
        }
        __xchg_called_with_bad_pointer();
        return x;
index 5b1b52a..7e064c6 100644 (file)
@@ -12,6 +12,14 @@ int dma_supported(struct device *dev, u64 mask);
 #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
 #define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
 
+static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
+                                 enum dma_data_direction dir)
+{
+       /* Since dma_{alloc,free}_noncoherent() allocated coherent memory, this
+        * routine can be a nop.
+        */
+}
+
 extern struct dma_map_ops *dma_ops;
 extern struct dma_map_ops *leon_dma_ops;
 extern struct dma_map_ops pci32_dma_ops;
index a34ad07..4c7c12d 100644 (file)
@@ -9,9 +9,9 @@ static inline __u16 __arch_swab16p(const __u16 *addr)
 {
        __u16 ret;
 
-       __asm__ __volatile__ ("lduha [%1] %2, %0"
+       __asm__ __volatile__ ("lduha [%2] %3, %0"
                              : "=r" (ret)
-                             : "r" (addr), "i" (ASI_PL));
+                             : "m" (*addr), "r" (addr), "i" (ASI_PL));
        return ret;
 }
 #define __arch_swab16p __arch_swab16p
@@ -20,9 +20,9 @@ static inline __u32 __arch_swab32p(const __u32 *addr)
 {
        __u32 ret;
 
-       __asm__ __volatile__ ("lduwa [%1] %2, %0"
+       __asm__ __volatile__ ("lduwa [%2] %3, %0"
                              : "=r" (ret)
-                             : "r" (addr), "i" (ASI_PL));
+                             : "m" (*addr), "r" (addr), "i" (ASI_PL));
        return ret;
 }
 #define __arch_swab32p __arch_swab32p
@@ -31,9 +31,9 @@ static inline __u64 __arch_swab64p(const __u64 *addr)
 {
        __u64 ret;
 
-       __asm__ __volatile__ ("ldxa [%1] %2, %0"
+       __asm__ __volatile__ ("ldxa [%2] %3, %0"
                              : "=r" (ret)
-                             : "r" (addr), "i" (ASI_PL));
+                             : "m" (*addr), "r" (addr), "i" (ASI_PL));
        return ret;
 }
 #define __arch_swab64p __arch_swab64p
index 8f76f23..f9c6813 100644 (file)
@@ -581,7 +581,7 @@ static irqreturn_t schizo_pcierr_intr_other(struct pci_pbm_info *pbm)
 {
        unsigned long csr_reg, csr, csr_error_bits;
        irqreturn_t ret = IRQ_NONE;
-       u16 stat;
+       u32 stat;
 
        csr_reg = pbm->pbm_regs + SCHIZO_PCI_CTRL;
        csr = upa_readq(csr_reg);
@@ -617,7 +617,7 @@ static irqreturn_t schizo_pcierr_intr_other(struct pci_pbm_info *pbm)
                               pbm->name);
                ret = IRQ_HANDLED;
        }
-       pci_read_config_word(pbm->pci_bus->self, PCI_STATUS, &stat);
+       pbm->pci_ops->read(pbm->pci_bus, 0, PCI_STATUS, 2, &stat);
        if (stat & (PCI_STATUS_PARITY |
                    PCI_STATUS_SIG_TARGET_ABORT |
                    PCI_STATUS_REC_TARGET_ABORT |
@@ -625,7 +625,7 @@ static irqreturn_t schizo_pcierr_intr_other(struct pci_pbm_info *pbm)
                    PCI_STATUS_SIG_SYSTEM_ERROR)) {
                printk("%s: PCI bus error, PCI_STATUS[%04x]\n",
                       pbm->name, stat);
-               pci_write_config_word(pbm->pci_bus->self, PCI_STATUS, 0xffff);
+               pbm->pci_ops->write(pbm->pci_bus, 0, PCI_STATUS, 2, 0xffff);
                ret = IRQ_HANDLED;
        }
        return ret;
index 302c476..da6f1a7 100644 (file)
@@ -816,13 +816,17 @@ void arch_send_call_function_single_ipi(int cpu)
 void __irq_entry smp_call_function_client(int irq, struct pt_regs *regs)
 {
        clear_softint(1 << irq);
+       irq_enter();
        generic_smp_call_function_interrupt();
+       irq_exit();
 }
 
 void __irq_entry smp_call_function_single_client(int irq, struct pt_regs *regs)
 {
        clear_softint(1 << irq);
+       irq_enter();
        generic_smp_call_function_single_interrupt();
+       irq_exit();
 }
 
 static void tsb_sync(void *info)
index a7c418a..71cd65a 100644 (file)
@@ -45,6 +45,19 @@ ATOMIC_OP(add, +=)
 
 #undef ATOMIC_OP
 
+int atomic_xchg(atomic_t *v, int new)
+{
+       int ret;
+       unsigned long flags;
+
+       spin_lock_irqsave(ATOMIC_HASH(v), flags);
+       ret = v->counter;
+       v->counter = new;
+       spin_unlock_irqrestore(ATOMIC_HASH(v), flags);
+       return ret;
+}
+EXPORT_SYMBOL(atomic_xchg);
+
 int atomic_cmpxchg(atomic_t *v, int old, int new)
 {
        int ret;
@@ -137,3 +150,17 @@ unsigned long __cmpxchg_u32(volatile u32 *ptr, u32 old, u32 new)
        return (unsigned long)prev;
 }
 EXPORT_SYMBOL(__cmpxchg_u32);
+
+unsigned long __xchg_u32(volatile u32 *ptr, u32 new)
+{
+       unsigned long flags;
+       u32 prev;
+
+       spin_lock_irqsave(ATOMIC_HASH(ptr), flags);
+       prev = *ptr;
+       *ptr = new;
+       spin_unlock_irqrestore(ATOMIC_HASH(ptr), flags);
+
+       return (unsigned long)prev;
+}
+EXPORT_SYMBOL(__xchg_u32);
index ded8a67..41a503c 100644 (file)
@@ -144,7 +144,7 @@ config INSTRUCTION_DECODER
 
 config PERF_EVENTS_INTEL_UNCORE
        def_bool y
-       depends on PERF_EVENTS && SUP_SUP_INTEL && PCI
+       depends on PERF_EVENTS && CPU_SUP_INTEL && PCI
 
 config OUTPUT_FORMAT
        string
index 704f58a..45abc36 100644 (file)
@@ -76,8 +76,10 @@ suffix-$(CONFIG_KERNEL_XZ)   := xz
 suffix-$(CONFIG_KERNEL_LZO)    := lzo
 suffix-$(CONFIG_KERNEL_LZ4)    := lz4
 
+RUN_SIZE = $(shell $(OBJDUMP) -h vmlinux | \
+            perl $(srctree)/arch/x86/tools/calc_run_size.pl)
 quiet_cmd_mkpiggy = MKPIGGY $@
-      cmd_mkpiggy = $(obj)/mkpiggy $< > $@ || ( rm -f $@ ; false )
+      cmd_mkpiggy = $(obj)/mkpiggy $< $(RUN_SIZE) > $@ || ( rm -f $@ ; false )
 
 targets += piggy.S
 $(obj)/piggy.S: $(obj)/vmlinux.bin.$(suffix-y) $(obj)/mkpiggy FORCE
index cbed140..1d7fbbc 100644 (file)
@@ -207,7 +207,8 @@ relocated:
  * Do the decompression, and jump to the new kernel..
  */
                                /* push arguments for decompress_kernel: */
-       pushl   $z_output_len   /* decompressed length */
+       pushl   $z_run_size     /* size of kernel with .bss and .brk */
+       pushl   $z_output_len   /* decompressed length, end of relocs */
        leal    z_extract_offset_negative(%ebx), %ebp
        pushl   %ebp            /* output address */
        pushl   $z_input_len    /* input_len */
@@ -217,7 +218,7 @@ relocated:
        pushl   %eax            /* heap area */
        pushl   %esi            /* real mode pointer */
        call    decompress_kernel /* returns kernel location in %eax */
-       addl    $24, %esp
+       addl    $28, %esp
 
 /*
  * Jump to the decompressed kernel.
index 2884e0c..6b1766c 100644 (file)
@@ -402,13 +402,16 @@ relocated:
  * Do the decompression, and jump to the new kernel..
  */
        pushq   %rsi                    /* Save the real mode argument */
+       movq    $z_run_size, %r9        /* size of kernel with .bss and .brk */
+       pushq   %r9
        movq    %rsi, %rdi              /* real mode address */
        leaq    boot_heap(%rip), %rsi   /* malloc area for uncompression */
        leaq    input_data(%rip), %rdx  /* input_data */
        movl    $z_input_len, %ecx      /* input_len */
        movq    %rbp, %r8               /* output target address */
-       movq    $z_output_len, %r9      /* decompressed length */
+       movq    $z_output_len, %r9      /* decompressed length, end of relocs */
        call    decompress_kernel       /* returns kernel location in %rax */
+       popq    %r9
        popq    %rsi
 
 /*
index 57ab74d..30dd59a 100644 (file)
@@ -358,7 +358,8 @@ asmlinkage __visible void *decompress_kernel(void *rmode, memptr heap,
                                  unsigned char *input_data,
                                  unsigned long input_len,
                                  unsigned char *output,
-                                 unsigned long output_len)
+                                 unsigned long output_len,
+                                 unsigned long run_size)
 {
        real_mode = rmode;
 
@@ -381,8 +382,14 @@ asmlinkage __visible void *decompress_kernel(void *rmode, memptr heap,
        free_mem_ptr     = heap;        /* Heap */
        free_mem_end_ptr = heap + BOOT_HEAP_SIZE;
 
-       output = choose_kernel_location(input_data, input_len,
-                                       output, output_len);
+       /*
+        * The memory hole needed for the kernel is the larger of either
+        * the entire decompressed kernel plus relocation table, or the
+        * entire decompressed kernel plus .bss and .brk sections.
+        */
+       output = choose_kernel_location(input_data, input_len, output,
+                                       output_len > run_size ? output_len
+                                                             : run_size);
 
        /* Validate memory location choices. */
        if ((unsigned long)output & (MIN_KERNEL_ALIGN - 1))
index b669ab6..d8222f2 100644 (file)
@@ -36,11 +36,13 @@ int main(int argc, char *argv[])
        uint32_t olen;
        long ilen;
        unsigned long offs;
+       unsigned long run_size;
        FILE *f = NULL;
        int retval = 1;
 
-       if (argc < 2) {
-               fprintf(stderr, "Usage: %s compressed_file\n", argv[0]);
+       if (argc < 3) {
+               fprintf(stderr, "Usage: %s compressed_file run_size\n",
+                               argv[0]);
                goto bail;
        }
 
@@ -74,6 +76,7 @@ int main(int argc, char *argv[])
        offs += olen >> 12;     /* Add 8 bytes for each 32K block */
        offs += 64*1024 + 128;  /* Add 64K + 128 bytes slack */
        offs = (offs+4095) & ~4095; /* Round to a 4K boundary */
+       run_size = atoi(argv[2]);
 
        printf(".section \".rodata..compressed\",\"a\",@progbits\n");
        printf(".globl z_input_len\n");
@@ -85,6 +88,8 @@ int main(int argc, char *argv[])
        /* z_extract_offset_negative allows simplification of head_32.S */
        printf(".globl z_extract_offset_negative\n");
        printf("z_extract_offset_negative = -0x%lx\n", offs);
+       printf(".globl z_run_size\n");
+       printf("z_run_size = %lu\n", run_size);
 
        printf(".globl input_data, input_data_end\n");
        printf("input_data:\n");
index f48b17d..3a52ee0 100644 (file)
@@ -20,7 +20,6 @@
 #define THREAD_SIZE_ORDER      1
 #define THREAD_SIZE            (PAGE_SIZE << THREAD_SIZE_ORDER)
 
-#define STACKFAULT_STACK 0
 #define DOUBLEFAULT_STACK 1
 #define NMI_STACK 0
 #define DEBUG_STACK 0
index 6782051..75450b2 100644 (file)
 #define IRQ_STACK_ORDER 2
 #define IRQ_STACK_SIZE (PAGE_SIZE << IRQ_STACK_ORDER)
 
-#define STACKFAULT_STACK 1
-#define DOUBLEFAULT_STACK 2
-#define NMI_STACK 3
-#define DEBUG_STACK 4
-#define MCE_STACK 5
-#define N_EXCEPTION_STACKS 5  /* hw limit: 7 */
+#define DOUBLEFAULT_STACK 1
+#define NMI_STACK 2
+#define DEBUG_STACK 3
+#define MCE_STACK 4
+#define N_EXCEPTION_STACKS 4  /* hw limit: 7 */
 
 #define PUD_PAGE_SIZE          (_AC(1, UL) << PUD_SHIFT)
 #define PUD_PAGE_MASK          (~(PUD_PAGE_SIZE-1))
index 0a4e140..7249e6d 100644 (file)
@@ -16,6 +16,9 @@
 
 #include <linux/sfi.h>
 
+#define MAX_NUM_STREAMS_MRFLD  25
+#define MAX_NUM_STREAMS        MAX_NUM_STREAMS_MRFLD
+
 enum sst_audio_task_id_mrfld {
        SST_TASK_ID_NONE = 0,
        SST_TASK_ID_SBA = 1,
@@ -73,6 +76,65 @@ struct sst_platform_data {
        unsigned int strm_map_size;
 };
 
+struct sst_info {
+       u32 iram_start;
+       u32 iram_end;
+       bool iram_use;
+       u32 dram_start;
+       u32 dram_end;
+       bool dram_use;
+       u32 imr_start;
+       u32 imr_end;
+       bool imr_use;
+       u32 mailbox_start;
+       bool use_elf;
+       bool lpe_viewpt_rqd;
+       unsigned int max_streams;
+       u32 dma_max_len;
+       u8 num_probes;
+};
+
+struct sst_lib_dnld_info {
+       unsigned int mod_base;
+       unsigned int mod_end;
+       unsigned int mod_table_offset;
+       unsigned int mod_table_size;
+       bool mod_ddr_dnld;
+};
+
+struct sst_res_info {
+       unsigned int shim_offset;
+       unsigned int shim_size;
+       unsigned int shim_phy_addr;
+       unsigned int ssp0_offset;
+       unsigned int ssp0_size;
+       unsigned int dma0_offset;
+       unsigned int dma0_size;
+       unsigned int dma1_offset;
+       unsigned int dma1_size;
+       unsigned int iram_offset;
+       unsigned int iram_size;
+       unsigned int dram_offset;
+       unsigned int dram_size;
+       unsigned int mbox_offset;
+       unsigned int mbox_size;
+       unsigned int acpi_lpe_res_index;
+       unsigned int acpi_ddr_index;
+       unsigned int acpi_ipc_irq_index;
+};
+
+struct sst_ipc_info {
+       int ipc_offset;
+       unsigned int mbox_recv_off;
+};
+
+struct sst_platform_info {
+       const struct sst_info *probe_data;
+       const struct sst_ipc_info *ipc_info;
+       const struct sst_res_info *res_info;
+       const struct sst_lib_dnld_info *lib_info;
+       const char *platform;
+};
 int add_sst_platform_device(void);
 #endif
 
index 8cd27e0..8cd1cc3 100644 (file)
@@ -150,6 +150,7 @@ static inline void arch_send_call_function_ipi_mask(const struct cpumask *mask)
 }
 
 void cpu_disable_common(void);
+void cpu_die_common(unsigned int cpu);
 void native_smp_prepare_boot_cpu(void);
 void native_smp_prepare_cpus(unsigned int max_cpus);
 void native_smp_cpus_done(unsigned int max_cpus);
index 8540538..547e344 100644 (file)
@@ -141,7 +141,7 @@ struct thread_info {
 /* Only used for 64 bit */
 #define _TIF_DO_NOTIFY_MASK                                            \
        (_TIF_SIGPENDING | _TIF_MCE_NOTIFY | _TIF_NOTIFY_RESUME |       \
-        _TIF_USER_RETURN_NOTIFY)
+        _TIF_USER_RETURN_NOTIFY | _TIF_UPROBE)
 
 /* flags to check in __switch_to() */
 #define _TIF_WORK_CTXSW                                                        \
index bc8352e..707adc6 100644 (file)
@@ -39,6 +39,7 @@ asmlinkage void simd_coprocessor_error(void);
 
 #ifdef CONFIG_TRACING
 asmlinkage void trace_page_fault(void);
+#define trace_stack_segment stack_segment
 #define trace_divide_error divide_error
 #define trace_bounds bounds
 #define trace_invalid_op invalid_op
index 4b4f78c..cfa9b5b 100644 (file)
@@ -146,6 +146,8 @@ EXPORT_PER_CPU_SYMBOL_GPL(gdt_page);
 
 static int __init x86_xsave_setup(char *s)
 {
+       if (strlen(s))
+               return 0;
        setup_clear_cpu_cap(X86_FEATURE_XSAVE);
        setup_clear_cpu_cap(X86_FEATURE_XSAVEOPT);
        setup_clear_cpu_cap(X86_FEATURE_XSAVES);
index 7aa1acc..0667447 100644 (file)
@@ -108,12 +108,13 @@ static size_t compute_container_size(u8 *data, u32 total_size)
  * load_microcode_amd() to save equivalent cpu table and microcode patches in
  * kernel heap memory.
  */
-static void apply_ucode_in_initrd(void *ucode, size_t size)
+static void apply_ucode_in_initrd(void *ucode, size_t size, bool save_patch)
 {
        struct equiv_cpu_entry *eq;
        size_t *cont_sz;
        u32 *header;
        u8  *data, **cont;
+       u8 (*patch)[PATCH_MAX_SIZE];
        u16 eq_id = 0;
        int offset, left;
        u32 rev, eax, ebx, ecx, edx;
@@ -123,10 +124,12 @@ static void apply_ucode_in_initrd(void *ucode, size_t size)
        new_rev = (u32 *)__pa_nodebug(&ucode_new_rev);
        cont_sz = (size_t *)__pa_nodebug(&container_size);
        cont    = (u8 **)__pa_nodebug(&container);
+       patch   = (u8 (*)[PATCH_MAX_SIZE])__pa_nodebug(&amd_ucode_patch);
 #else
        new_rev = &ucode_new_rev;
        cont_sz = &container_size;
        cont    = &container;
+       patch   = &amd_ucode_patch;
 #endif
 
        data   = ucode;
@@ -213,9 +216,9 @@ static void apply_ucode_in_initrd(void *ucode, size_t size)
                                rev = mc->hdr.patch_id;
                                *new_rev = rev;
 
-                               /* save ucode patch */
-                               memcpy(amd_ucode_patch, mc,
-                                      min_t(u32, header[1], PATCH_MAX_SIZE));
+                               if (save_patch)
+                                       memcpy(patch, mc,
+                                              min_t(u32, header[1], PATCH_MAX_SIZE));
                        }
                }
 
@@ -246,7 +249,7 @@ void __init load_ucode_amd_bsp(void)
        *data = cp.data;
        *size = cp.size;
 
-       apply_ucode_in_initrd(cp.data, cp.size);
+       apply_ucode_in_initrd(cp.data, cp.size, true);
 }
 
 #ifdef CONFIG_X86_32
@@ -263,7 +266,7 @@ void load_ucode_amd_ap(void)
        size_t *usize;
        void **ucode;
 
-       mc = (struct microcode_amd *)__pa(amd_ucode_patch);
+       mc = (struct microcode_amd *)__pa_nodebug(amd_ucode_patch);
        if (mc->hdr.patch_id && mc->hdr.processor_rev_id) {
                __apply_microcode_amd(mc);
                return;
@@ -275,7 +278,7 @@ void load_ucode_amd_ap(void)
        if (!*ucode || !*usize)
                return;
 
-       apply_ucode_in_initrd(*ucode, *usize);
+       apply_ucode_in_initrd(*ucode, *usize, false);
 }
 
 static void __init collect_cpu_sig_on_bsp(void *arg)
@@ -339,7 +342,7 @@ void load_ucode_amd_ap(void)
                 * AP has a different equivalence ID than BSP, looks like
                 * mixed-steppings silicon so go through the ucode blob anew.
                 */
-               apply_ucode_in_initrd(ucode_cpio.data, ucode_cpio.size);
+               apply_ucode_in_initrd(ucode_cpio.data, ucode_cpio.size, false);
        }
 }
 #endif
@@ -347,7 +350,9 @@ void load_ucode_amd_ap(void)
 int __init save_microcode_in_initrd_amd(void)
 {
        unsigned long cont;
+       int retval = 0;
        enum ucode_state ret;
+       u8 *cont_va;
        u32 eax;
 
        if (!container)
@@ -355,13 +360,15 @@ int __init save_microcode_in_initrd_amd(void)
 
 #ifdef CONFIG_X86_32
        get_bsp_sig();
-       cont = (unsigned long)container;
+       cont    = (unsigned long)container;
+       cont_va = __va(container);
 #else
        /*
         * We need the physical address of the container for both bitness since
         * boot_params.hdr.ramdisk_image is a physical address.
         */
-       cont = __pa(container);
+       cont    = __pa(container);
+       cont_va = container;
 #endif
 
        /*
@@ -372,6 +379,8 @@ int __init save_microcode_in_initrd_amd(void)
        if (relocated_ramdisk)
                container = (u8 *)(__va(relocated_ramdisk) +
                             (cont - boot_params.hdr.ramdisk_image));
+       else
+               container = cont_va;
 
        if (ucode_new_rev)
                pr_info("microcode: updated early to new patch_level=0x%08x\n",
@@ -382,7 +391,7 @@ int __init save_microcode_in_initrd_amd(void)
 
        ret = load_microcode_amd(eax, container, container_size);
        if (ret != UCODE_OK)
-               return -EINVAL;
+               retval = -EINVAL;
 
        /*
         * This will be freed any msec now, stash patches for the current
@@ -391,5 +400,5 @@ int __init save_microcode_in_initrd_amd(void)
        container = NULL;
        container_size = 0;
 
-       return 0;
+       return retval;
 }
index dd9d619..08fe6e8 100644 (file)
@@ -465,6 +465,16 @@ static void mc_bp_resume(void)
 
        if (uci->valid && uci->mc)
                microcode_ops->apply_microcode(cpu);
+#ifdef CONFIG_X86_64
+       else if (!uci->mc)
+               /*
+                * We might resume and not have applied late microcode but still
+                * have a newer patch stashed from the early loader. We don't
+                * have it in uci->mc so we have to load it the same way we're
+                * applying patches early on the APs.
+                */
+               load_ucode_ap();
+#endif
 }
 
 static struct syscore_ops mc_syscore_ops = {
index 5f28a64..2c017f2 100644 (file)
@@ -124,7 +124,7 @@ void __init load_ucode_bsp(void)
 static bool check_loader_disabled_ap(void)
 {
 #ifdef CONFIG_X86_32
-       return __pa_nodebug(dis_ucode_ldr);
+       return *((bool *)__pa_nodebug(&dis_ucode_ldr));
 #else
        return dis_ucode_ldr;
 #endif
index adf138e..f9ed429 100644 (file)
@@ -486,14 +486,17 @@ static struct attribute_group snbep_uncore_qpi_format_group = {
        .attrs = snbep_uncore_qpi_formats_attr,
 };
 
-#define SNBEP_UNCORE_MSR_OPS_COMMON_INIT()                     \
-       .init_box       = snbep_uncore_msr_init_box,            \
+#define __SNBEP_UNCORE_MSR_OPS_COMMON_INIT()                   \
        .disable_box    = snbep_uncore_msr_disable_box,         \
        .enable_box     = snbep_uncore_msr_enable_box,          \
        .disable_event  = snbep_uncore_msr_disable_event,       \
        .enable_event   = snbep_uncore_msr_enable_event,        \
        .read_counter   = uncore_msr_read_counter
 
+#define SNBEP_UNCORE_MSR_OPS_COMMON_INIT()                     \
+       __SNBEP_UNCORE_MSR_OPS_COMMON_INIT(),                   \
+       .init_box       = snbep_uncore_msr_init_box             \
+
 static struct intel_uncore_ops snbep_uncore_msr_ops = {
        SNBEP_UNCORE_MSR_OPS_COMMON_INIT(),
 };
@@ -1919,6 +1922,30 @@ static struct intel_uncore_type hswep_uncore_cbox = {
        .format_group           = &hswep_uncore_cbox_format_group,
 };
 
+/*
+ * Write SBOX Initialization register bit by bit to avoid spurious #GPs
+ */
+static void hswep_uncore_sbox_msr_init_box(struct intel_uncore_box *box)
+{
+       unsigned msr = uncore_msr_box_ctl(box);
+
+       if (msr) {
+               u64 init = SNBEP_PMON_BOX_CTL_INT;
+               u64 flags = 0;
+               int i;
+
+               for_each_set_bit(i, (unsigned long *)&init, 64) {
+                       flags |= (1ULL << i);
+                       wrmsrl(msr, flags);
+               }
+       }
+}
+
+static struct intel_uncore_ops hswep_uncore_sbox_msr_ops = {
+       __SNBEP_UNCORE_MSR_OPS_COMMON_INIT(),
+       .init_box               = hswep_uncore_sbox_msr_init_box
+};
+
 static struct attribute *hswep_uncore_sbox_formats_attr[] = {
        &format_attr_event.attr,
        &format_attr_umask.attr,
@@ -1944,7 +1971,7 @@ static struct intel_uncore_type hswep_uncore_sbox = {
        .event_mask             = HSWEP_S_MSR_PMON_RAW_EVENT_MASK,
        .box_ctl                = HSWEP_S0_MSR_PMON_BOX_CTL,
        .msr_offset             = HSWEP_SBOX_MSR_OFFSET,
-       .ops                    = &snbep_uncore_msr_ops,
+       .ops                    = &hswep_uncore_sbox_msr_ops,
        .format_group           = &hswep_uncore_sbox_format_group,
 };
 
@@ -2025,13 +2052,27 @@ static struct intel_uncore_type hswep_uncore_imc = {
        SNBEP_UNCORE_PCI_COMMON_INIT(),
 };
 
+static unsigned hswep_uncore_irp_ctrs[] = {0xa0, 0xa8, 0xb0, 0xb8};
+
+static u64 hswep_uncore_irp_read_counter(struct intel_uncore_box *box, struct perf_event *event)
+{
+       struct pci_dev *pdev = box->pci_dev;
+       struct hw_perf_event *hwc = &event->hw;
+       u64 count = 0;
+
+       pci_read_config_dword(pdev, hswep_uncore_irp_ctrs[hwc->idx], (u32 *)&count);
+       pci_read_config_dword(pdev, hswep_uncore_irp_ctrs[hwc->idx] + 4, (u32 *)&count + 1);
+
+       return count;
+}
+
 static struct intel_uncore_ops hswep_uncore_irp_ops = {
        .init_box       = snbep_uncore_pci_init_box,
        .disable_box    = snbep_uncore_pci_disable_box,
        .enable_box     = snbep_uncore_pci_enable_box,
        .disable_event  = ivbep_uncore_irp_disable_event,
        .enable_event   = ivbep_uncore_irp_enable_event,
-       .read_counter   = ivbep_uncore_irp_read_counter,
+       .read_counter   = hswep_uncore_irp_read_counter,
 };
 
 static struct intel_uncore_type hswep_uncore_irp = {
index 1abcb50..ff86f19 100644 (file)
@@ -24,7 +24,6 @@ static char x86_stack_ids[][8] = {
                [ DEBUG_STACK-1                 ]       = "#DB",
                [ NMI_STACK-1                   ]       = "NMI",
                [ DOUBLEFAULT_STACK-1           ]       = "#DF",
-               [ STACKFAULT_STACK-1            ]       = "#SS",
                [ MCE_STACK-1                   ]       = "#MC",
 #if DEBUG_STKSZ > EXCEPTION_STKSZ
                [ N_EXCEPTION_STACKS ...
index df088bb..c0226ab 100644 (file)
@@ -828,9 +828,15 @@ ENTRY(native_iret)
        jnz native_irq_return_ldt
 #endif
 
+.global native_irq_return_iret
 native_irq_return_iret:
+       /*
+        * This may fault.  Non-paranoid faults on return to userspace are
+        * handled by fixup_bad_iret.  These include #SS, #GP, and #NP.
+        * Double-faults due to espfix64 are handled in do_double_fault.
+        * Other faults here are fatal.
+        */
        iretq
-       _ASM_EXTABLE(native_irq_return_iret, bad_iret)
 
 #ifdef CONFIG_X86_ESPFIX64
 native_irq_return_ldt:
@@ -858,25 +864,6 @@ native_irq_return_ldt:
        jmp native_irq_return_iret
 #endif
 
-       .section .fixup,"ax"
-bad_iret:
-       /*
-        * The iret traps when the %cs or %ss being restored is bogus.
-        * We've lost the original trap vector and error code.
-        * #GPF is the most likely one to get for an invalid selector.
-        * So pretend we completed the iret and took the #GPF in user mode.
-        *
-        * We are now running with the kernel GS after exception recovery.
-        * But error_entry expects us to have user GS to match the user %cs,
-        * so swap back.
-        */
-       pushq $0
-
-       SWAPGS
-       jmp general_protection
-
-       .previous
-
        /* edi: workmask, edx: work */
 retint_careful:
        CFI_RESTORE_STATE
@@ -922,37 +909,6 @@ ENTRY(retint_kernel)
        CFI_ENDPROC
 END(common_interrupt)
 
-       /*
-        * If IRET takes a fault on the espfix stack, then we
-        * end up promoting it to a doublefault.  In that case,
-        * modify the stack to make it look like we just entered
-        * the #GP handler from user space, similar to bad_iret.
-        */
-#ifdef CONFIG_X86_ESPFIX64
-       ALIGN
-__do_double_fault:
-       XCPT_FRAME 1 RDI+8
-       movq RSP(%rdi),%rax             /* Trap on the espfix stack? */
-       sarq $PGDIR_SHIFT,%rax
-       cmpl $ESPFIX_PGD_ENTRY,%eax
-       jne do_double_fault             /* No, just deliver the fault */
-       cmpl $__KERNEL_CS,CS(%rdi)
-       jne do_double_fault
-       movq RIP(%rdi),%rax
-       cmpq $native_irq_return_iret,%rax
-       jne do_double_fault             /* This shouldn't happen... */
-       movq PER_CPU_VAR(kernel_stack),%rax
-       subq $(6*8-KERNEL_STACK_OFFSET),%rax    /* Reset to original stack */
-       movq %rax,RSP(%rdi)
-       movq $0,(%rax)                  /* Missing (lost) #GP error code */
-       movq $general_protection,RIP(%rdi)
-       retq
-       CFI_ENDPROC
-END(__do_double_fault)
-#else
-# define __do_double_fault do_double_fault
-#endif
-
 /*
  * APIC interrupts.
  */
@@ -1124,7 +1080,7 @@ idtentry overflow do_overflow has_error_code=0
 idtentry bounds do_bounds has_error_code=0
 idtentry invalid_op do_invalid_op has_error_code=0
 idtentry device_not_available do_device_not_available has_error_code=0
-idtentry double_fault __do_double_fault has_error_code=1 paranoid=1
+idtentry double_fault do_double_fault has_error_code=1 paranoid=1
 idtentry coprocessor_segment_overrun do_coprocessor_segment_overrun has_error_code=0
 idtentry invalid_TSS do_invalid_TSS has_error_code=1
 idtentry segment_not_present do_segment_not_present has_error_code=1
@@ -1289,7 +1245,7 @@ apicinterrupt3 HYPERVISOR_CALLBACK_VECTOR \
 
 idtentry debug do_debug has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK
 idtentry int3 do_int3 has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK
-idtentry stack_segment do_stack_segment has_error_code=1 paranoid=1
+idtentry stack_segment do_stack_segment has_error_code=1
 #ifdef CONFIG_XEN
 idtentry xen_debug do_debug has_error_code=0
 idtentry xen_int3 do_int3 has_error_code=0
@@ -1399,17 +1355,16 @@ error_sti:
 
 /*
  * There are two places in the kernel that can potentially fault with
- * usergs. Handle them here. The exception handlers after iret run with
- * kernel gs again, so don't set the user space flag. B stepping K8s
- * sometimes report an truncated RIP for IRET exceptions returning to
- * compat mode. Check for these here too.
+ * usergs. Handle them here.  B stepping K8s sometimes report a
+ * truncated RIP for IRET exceptions returning to compat mode. Check
+ * for these here too.
  */
 error_kernelspace:
        CFI_REL_OFFSET rcx, RCX+8
        incl %ebx
        leaq native_irq_return_iret(%rip),%rcx
        cmpq %rcx,RIP+8(%rsp)
-       je error_swapgs
+       je error_bad_iret
        movl %ecx,%eax  /* zero extend */
        cmpq %rax,RIP+8(%rsp)
        je bstep_iret
@@ -1420,7 +1375,15 @@ error_kernelspace:
 bstep_iret:
        /* Fix truncated RIP */
        movq %rcx,RIP+8(%rsp)
-       jmp error_swapgs
+       /* fall through */
+
+error_bad_iret:
+       SWAPGS
+       mov %rsp,%rdi
+       call fixup_bad_iret
+       mov %rax,%rsp
+       decl %ebx       /* Return to usergs */
+       jmp error_sti
        CFI_ENDPROC
 END(error_entry)
 
index 749b0e4..e510618 100644 (file)
@@ -1484,7 +1484,7 @@ unsigned long syscall_trace_enter_phase1(struct pt_regs *regs, u32 arch)
         */
        if (work & _TIF_NOHZ) {
                user_exit();
-               work &= ~TIF_NOHZ;
+               work &= ~_TIF_NOHZ;
        }
 
 #ifdef CONFIG_SECCOMP
index 4d2128a..668d8f2 100644 (file)
@@ -1303,10 +1303,14 @@ static void __ref remove_cpu_from_maps(int cpu)
        numa_remove_cpu(cpu);
 }
 
+static DEFINE_PER_CPU(struct completion, die_complete);
+
 void cpu_disable_common(void)
 {
        int cpu = smp_processor_id();
 
+       init_completion(&per_cpu(die_complete, smp_processor_id()));
+
        remove_siblinginfo(cpu);
 
        /* It's now safe to remove this processor from the online map */
@@ -1316,8 +1320,6 @@ void cpu_disable_common(void)
        fixup_irqs();
 }
 
-static DEFINE_PER_CPU(struct completion, die_complete);
-
 int native_cpu_disable(void)
 {
        int ret;
@@ -1327,16 +1329,21 @@ int native_cpu_disable(void)
                return ret;
 
        clear_local_APIC();
-       init_completion(&per_cpu(die_complete, smp_processor_id()));
        cpu_disable_common();
 
        return 0;
 }
 
+void cpu_die_common(unsigned int cpu)
+{
+       wait_for_completion_timeout(&per_cpu(die_complete, cpu), HZ);
+}
+
 void native_cpu_die(unsigned int cpu)
 {
        /* We don't do anything here: idle task is faking death itself. */
-       wait_for_completion_timeout(&per_cpu(die_complete, cpu), HZ);
+
+       cpu_die_common(cpu);
 
        /* They ack this in play_dead() by setting CPU_DEAD */
        if (per_cpu(cpu_state, cpu) == CPU_DEAD) {
index 0d0e922..de801f2 100644 (file)
@@ -233,32 +233,40 @@ DO_ERROR(X86_TRAP_UD,     SIGILL,  "invalid opcode",              invalid_op)
 DO_ERROR(X86_TRAP_OLD_MF, SIGFPE,  "coprocessor segment overrun",coprocessor_segment_overrun)
 DO_ERROR(X86_TRAP_TS,     SIGSEGV, "invalid TSS",              invalid_TSS)
 DO_ERROR(X86_TRAP_NP,     SIGBUS,  "segment not present",      segment_not_present)
-#ifdef CONFIG_X86_32
 DO_ERROR(X86_TRAP_SS,     SIGBUS,  "stack segment",            stack_segment)
-#endif
 DO_ERROR(X86_TRAP_AC,     SIGBUS,  "alignment check",          alignment_check)
 
 #ifdef CONFIG_X86_64
 /* Runs on IST stack */
-dotraplinkage void do_stack_segment(struct pt_regs *regs, long error_code)
-{
-       enum ctx_state prev_state;
-
-       prev_state = exception_enter();
-       if (notify_die(DIE_TRAP, "stack segment", regs, error_code,
-                      X86_TRAP_SS, SIGBUS) != NOTIFY_STOP) {
-               preempt_conditional_sti(regs);
-               do_trap(X86_TRAP_SS, SIGBUS, "stack segment", regs, error_code, NULL);
-               preempt_conditional_cli(regs);
-       }
-       exception_exit(prev_state);
-}
-
 dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code)
 {
        static const char str[] = "double fault";
        struct task_struct *tsk = current;
 
+#ifdef CONFIG_X86_ESPFIX64
+       extern unsigned char native_irq_return_iret[];
+
+       /*
+        * If IRET takes a non-IST fault on the espfix64 stack, then we
+        * end up promoting it to a doublefault.  In that case, modify
+        * the stack to make it look like we just entered the #GP
+        * handler from user space, similar to bad_iret.
+        */
+       if (((long)regs->sp >> PGDIR_SHIFT) == ESPFIX_PGD_ENTRY &&
+               regs->cs == __KERNEL_CS &&
+               regs->ip == (unsigned long)native_irq_return_iret)
+       {
+               struct pt_regs *normal_regs = task_pt_regs(current);
+
+               /* Fake a #GP(0) from userspace. */
+               memmove(&normal_regs->ip, (void *)regs->sp, 5*8);
+               normal_regs->orig_ax = 0;  /* Missing (lost) #GP error code */
+               regs->ip = (unsigned long)general_protection;
+               regs->sp = (unsigned long)&normal_regs->orig_ax;
+               return;
+       }
+#endif
+
        exception_enter();
        /* Return not checked because double check cannot be ignored */
        notify_die(DIE_TRAP, str, regs, error_code, X86_TRAP_DF, SIGSEGV);
@@ -399,6 +407,35 @@ asmlinkage __visible struct pt_regs *sync_regs(struct pt_regs *eregs)
        return regs;
 }
 NOKPROBE_SYMBOL(sync_regs);
+
+struct bad_iret_stack {
+       void *error_entry_ret;
+       struct pt_regs regs;
+};
+
+asmlinkage __visible
+struct bad_iret_stack *fixup_bad_iret(struct bad_iret_stack *s)
+{
+       /*
+        * This is called from entry_64.S early in handling a fault
+        * caused by a bad iret to user mode.  To handle the fault
+        * correctly, we want move our stack frame to task_pt_regs
+        * and we want to pretend that the exception came from the
+        * iret target.
+        */
+       struct bad_iret_stack *new_stack =
+               container_of(task_pt_regs(current),
+                            struct bad_iret_stack, regs);
+
+       /* Copy the IRET target to the new stack. */
+       memmove(&new_stack->regs.ip, (void *)s->regs.sp, 5*8);
+
+       /* Copy the remainder of the stack from the current stack. */
+       memmove(new_stack, s, offsetof(struct bad_iret_stack, regs.ip));
+
+       BUG_ON(!user_mode_vm(&new_stack->regs));
+       return new_stack;
+}
 #endif
 
 /*
@@ -778,7 +815,7 @@ void __init trap_init(void)
        set_intr_gate(X86_TRAP_OLD_MF, coprocessor_segment_overrun);
        set_intr_gate(X86_TRAP_TS, invalid_TSS);
        set_intr_gate(X86_TRAP_NP, segment_not_present);
-       set_intr_gate_ist(X86_TRAP_SS, &stack_segment, STACKFAULT_STACK);
+       set_intr_gate(X86_TRAP_SS, stack_segment);
        set_intr_gate(X86_TRAP_GP, general_protection);
        set_intr_gate(X86_TRAP_SPURIOUS, spurious_interrupt_bug);
        set_intr_gate(X86_TRAP_MF, coprocessor_error);
index 5edf088..9f8a2fa 100644 (file)
@@ -4287,6 +4287,7 @@ static int decode_operand(struct x86_emulate_ctxt *ctxt, struct operand *op,
                fetch_register_operand(op);
                break;
        case OpCL:
+               op->type = OP_IMM;
                op->bytes = 1;
                op->val = reg_read(ctxt, VCPU_REGS_RCX) & 0xff;
                break;
@@ -4294,6 +4295,7 @@ static int decode_operand(struct x86_emulate_ctxt *ctxt, struct operand *op,
                rc = decode_imm(ctxt, op, 1, true);
                break;
        case OpOne:
+               op->type = OP_IMM;
                op->bytes = 1;
                op->val = 1;
                break;
@@ -4352,21 +4354,27 @@ static int decode_operand(struct x86_emulate_ctxt *ctxt, struct operand *op,
                ctxt->memop.bytes = ctxt->op_bytes + 2;
                goto mem_common;
        case OpES:
+               op->type = OP_IMM;
                op->val = VCPU_SREG_ES;
                break;
        case OpCS:
+               op->type = OP_IMM;
                op->val = VCPU_SREG_CS;
                break;
        case OpSS:
+               op->type = OP_IMM;
                op->val = VCPU_SREG_SS;
                break;
        case OpDS:
+               op->type = OP_IMM;
                op->val = VCPU_SREG_DS;
                break;
        case OpFS:
+               op->type = OP_IMM;
                op->val = VCPU_SREG_FS;
                break;
        case OpGS:
+               op->type = OP_IMM;
                op->val = VCPU_SREG_GS;
                break;
        case OpImplicit:
index ac1c4de..978f402 100644 (file)
@@ -630,7 +630,7 @@ static int mmu_spte_clear_track_bits(u64 *sptep)
         * kvm mmu, before reclaiming the page, we should
         * unmap it from mmu first.
         */
-       WARN_ON(!kvm_is_mmio_pfn(pfn) && !page_count(pfn_to_page(pfn)));
+       WARN_ON(!kvm_is_reserved_pfn(pfn) && !page_count(pfn_to_page(pfn)));
 
        if (!shadow_accessed_mask || old_spte & shadow_accessed_mask)
                kvm_set_pfn_accessed(pfn);
@@ -2461,7 +2461,7 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep,
                spte |= PT_PAGE_SIZE_MASK;
        if (tdp_enabled)
                spte |= kvm_x86_ops->get_mt_mask(vcpu, gfn,
-                       kvm_is_mmio_pfn(pfn));
+                       kvm_is_reserved_pfn(pfn));
 
        if (host_writable)
                spte |= SPTE_HOST_WRITEABLE;
@@ -2737,7 +2737,7 @@ static void transparent_hugepage_adjust(struct kvm_vcpu *vcpu,
         * PT_PAGE_TABLE_LEVEL and there would be no adjustment done
         * here.
         */
-       if (!is_error_noslot_pfn(pfn) && !kvm_is_mmio_pfn(pfn) &&
+       if (!is_error_noslot_pfn(pfn) && !kvm_is_reserved_pfn(pfn) &&
            level == PT_PAGE_TABLE_LEVEL &&
            PageTransCompound(pfn_to_page(pfn)) &&
            !has_wrprotected_page(vcpu->kvm, gfn, PT_DIRECTORY_LEVEL)) {
index 7609e0e..1318f75 100644 (file)
@@ -41,9 +41,8 @@ csum_partial_copy_from_user(const void __user *src, void *dst,
                while (((unsigned long)src & 6) && len >= 2) {
                        __u16 val16;
 
-                       *errp = __get_user(val16, (const __u16 __user *)src);
-                       if (*errp)
-                               return isum;
+                       if (__get_user(val16, (const __u16 __user *)src))
+                               goto out_err;
 
                        *(__u16 *)dst = val16;
                        isum = (__force __wsum)add32_with_carry(
index 4cb8763..4e5dfec 100644 (file)
@@ -1123,7 +1123,7 @@ void mark_rodata_ro(void)
        unsigned long end = (unsigned long) &__end_rodata_hpage_align;
        unsigned long text_end = PFN_ALIGN(&__stop___ex_table);
        unsigned long rodata_end = PFN_ALIGN(&__end_rodata);
-       unsigned long all_end = PFN_ALIGN(&_end);
+       unsigned long all_end;
 
        printk(KERN_INFO "Write protecting the kernel read-only data: %luk\n",
               (end - start) >> 10);
@@ -1134,7 +1134,16 @@ void mark_rodata_ro(void)
        /*
         * The rodata/data/bss/brk section (but not the kernel text!)
         * should also be not-executable.
+        *
+        * We align all_end to PMD_SIZE because the existing mapping
+        * is a full PMD. If we would align _brk_end to PAGE_SIZE we
+        * split the PMD and the reminder between _brk_end and the end
+        * of the PMD will remain mapped executable.
+        *
+        * Any PMD which was setup after the one which covers _brk_end
+        * has been zapped already via cleanup_highmem().
         */
+       all_end = roundup((unsigned long)_brk_end, PMD_SIZE);
        set_memory_nx(rodata_start, (all_end - rodata_start) >> PAGE_SHIFT);
 
        rodata_test();
diff --git a/arch/x86/tools/calc_run_size.pl b/arch/x86/tools/calc_run_size.pl
new file mode 100644 (file)
index 0000000..23210ba
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/perl
+#
+# Calculate the amount of space needed to run the kernel, including room for
+# the .bss and .brk sections.
+#
+# Usage:
+# objdump -h a.out | perl calc_run_size.pl
+use strict;
+
+my $mem_size = 0;
+my $file_offset = 0;
+
+my $sections=" *[0-9]+ \.(?:bss|brk) +";
+while (<>) {
+       if (/^$sections([0-9a-f]+) +(?:[0-9a-f]+ +){2}([0-9a-f]+)/) {
+               my $size = hex($1);
+               my $offset = hex($2);
+               $mem_size += $size;
+               if ($file_offset == 0) {
+                       $file_offset = $offset;
+               } elsif ($file_offset != $offset) {
+                       # BFD linker shows the same file offset in ELF.
+                       # Gold linker shows them as consecutive.
+                       next if ($file_offset + $mem_size == $offset + $size);
+
+                       printf STDERR "file_offset: 0x%lx\n", $file_offset;
+                       printf STDERR "mem_size: 0x%lx\n", $mem_size;
+                       printf STDERR "offset: 0x%lx\n", $offset;
+                       printf STDERR "size: 0x%lx\n", $size;
+
+                       die ".bss and .brk are non-contiguous\n";
+               }
+       }
+}
+
+if ($file_offset == 0) {
+       die "Never found .bss or .brk file offset\n";
+}
+printf("%d\n", $mem_size + $file_offset);
index 8650cdb..4c071ae 100644 (file)
@@ -510,6 +510,9 @@ static void xen_cpu_die(unsigned int cpu)
                current->state = TASK_UNINTERRUPTIBLE;
                schedule_timeout(HZ/10);
        }
+
+       cpu_die_common(cpu);
+
        xen_smp_intr_free(cpu);
        xen_uninit_lock_cpu(cpu);
        xen_teardown_timer(cpu);
index 49c6c3d..81f57e8 100644 (file)
@@ -319,8 +319,8 @@ config XTENSA_PLATFORM_S6105
 
 config XTENSA_PLATFORM_XTFPGA
        bool "XTFPGA"
+       select ETHOC if ETHERNET
        select SERIAL_CONSOLE
-       select ETHOC
        select XTENSA_CALIBRATE_CCOUNT
        help
          XTFPGA is the name of Tensilica board family (LX60, LX110, LX200, ML605).
@@ -367,7 +367,7 @@ config BUILTIN_DTB
 config BLK_DEV_SIMDISK
        tristate "Host file-based simulated block device support"
        default n
-       depends on XTENSA_PLATFORM_ISS
+       depends on XTENSA_PLATFORM_ISS && BLOCK
        help
          Create block devices that map to files in the host file system.
          Device binding to host file may be changed at runtime via proc
diff --git a/arch/xtensa/boot/dts/lx200mx.dts b/arch/xtensa/boot/dts/lx200mx.dts
new file mode 100644 (file)
index 0000000..249822b
--- /dev/null
@@ -0,0 +1,16 @@
+/dts-v1/;
+/include/ "xtfpga.dtsi"
+/include/ "xtfpga-flash-16m.dtsi"
+
+/ {
+       compatible = "cdns,xtensa-lx200";
+       memory@0 {
+               device_type = "memory";
+               reg = <0x00000000 0x06000000>;
+       };
+       pic: pic {
+               compatible = "cdns,xtensa-mx";
+               #interrupt-cells = <2>;
+               interrupt-controller;
+       };
+};
diff --git a/arch/xtensa/configs/generic_kc705_defconfig b/arch/xtensa/configs/generic_kc705_defconfig
new file mode 100644 (file)
index 0000000..f4b7b38
--- /dev/null
@@ -0,0 +1,131 @@
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_FHANDLE=y
+CONFIG_IRQ_DOMAIN_DEBUG=y
+CONFIG_NO_HZ_IDLE=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IRQ_TIME_ACCOUNTING=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_MEMCG=y
+CONFIG_NAMESPACES=y
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_EXPERT=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_XTENSA_VARIANT_DC233C=y
+CONFIG_XTENSA_UNALIGNED_USER=y
+CONFIG_PREEMPT=y
+CONFIG_HIGHMEM=y
+# CONFIG_PCI is not set
+CONFIG_XTENSA_PLATFORM_XTFPGA=y
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="earlycon=uart8250,mmio32,0xfd050020,115200n8 console=ttyS0,115200n8 ip=dhcp root=/dev/nfs rw debug"
+CONFIG_USE_OF=y
+CONFIG_BUILTIN_DTB="kc705"
+# CONFIG_COMPACTION is not set
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+# CONFIG_IPV6 is not set
+CONFIG_NETFILTER=y
+# CONFIG_WIRELESS is not set
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+# CONFIG_STANDALONE is not set
+CONFIG_MTD=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_JEDECPROBE=y
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_MTD_CFI_STAA=y
+CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_MTD_UBI=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_NETDEVICES=y
+# CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_SAMSUNG is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+# CONFIG_NET_VENDOR_STMICRO is not set
+# CONFIG_NET_VENDOR_VIA is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+CONFIG_MARVELL_PHY=y
+# CONFIG_WLAN is not set
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+CONFIG_SERIAL_8250=y
+# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_HW_RANDOM=y
+# CONFIG_HWMON is not set
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_NOWAYOUT=y
+CONFIG_SOFT_WATCHDOG=y
+# CONFIG_VGA_CONSOLE is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS=y
+CONFIG_FANOTIFY=y
+CONFIG_VFAT_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_UBIFS_FS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_SWAP=y
+CONFIG_ROOT_NFS=y
+CONFIG_SUNRPC_DEBUG=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_INFO=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_LOCKUP_DETECTOR=y
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_DEBUG_RT_MUTEXES=y
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_ATOMIC_SLEEP=y
+CONFIG_STACKTRACE=y
+CONFIG_RCU_TRACE=y
+# CONFIG_FTRACE is not set
+CONFIG_LD_NO_RELAX=y
+# CONFIG_S32C1I_SELFTEST is not set
+CONFIG_CRYPTO_ANSI_CPRNG=y
diff --git a/arch/xtensa/configs/smp_lx200_defconfig b/arch/xtensa/configs/smp_lx200_defconfig
new file mode 100644 (file)
index 0000000..22eeacb
--- /dev/null
@@ -0,0 +1,135 @@
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_FHANDLE=y
+CONFIG_IRQ_DOMAIN_DEBUG=y
+CONFIG_NO_HZ_IDLE=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IRQ_TIME_ACCOUNTING=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_MEMCG=y
+CONFIG_NAMESPACES=y
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_EXPERT=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_XTENSA_VARIANT_CUSTOM=y
+CONFIG_XTENSA_VARIANT_CUSTOM_NAME="test_mmuhifi_c3"
+CONFIG_XTENSA_UNALIGNED_USER=y
+CONFIG_PREEMPT=y
+CONFIG_HAVE_SMP=y
+CONFIG_SMP=y
+CONFIG_HOTPLUG_CPU=y
+# CONFIG_INITIALIZE_XTENSA_MMU_INSIDE_VMLINUX is not set
+# CONFIG_PCI is not set
+CONFIG_XTENSA_PLATFORM_XTFPGA=y
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="earlycon=uart8250,mmio32,0xfd050020,115200n8 console=ttyS0,115200n8 ip=dhcp root=/dev/nfs rw debug"
+CONFIG_USE_OF=y
+CONFIG_BUILTIN_DTB="lx200mx"
+# CONFIG_COMPACTION is not set
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+# CONFIG_IPV6 is not set
+CONFIG_NETFILTER=y
+# CONFIG_WIRELESS is not set
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+# CONFIG_STANDALONE is not set
+CONFIG_MTD=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_JEDECPROBE=y
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_MTD_CFI_STAA=y
+CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_MTD_UBI=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_NETDEVICES=y
+# CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_SAMSUNG is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+# CONFIG_NET_VENDOR_STMICRO is not set
+# CONFIG_NET_VENDOR_VIA is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+CONFIG_MARVELL_PHY=y
+# CONFIG_WLAN is not set
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+CONFIG_SERIAL_8250=y
+# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_HW_RANDOM=y
+# CONFIG_HWMON is not set
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_NOWAYOUT=y
+CONFIG_SOFT_WATCHDOG=y
+# CONFIG_VGA_CONSOLE is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS=y
+CONFIG_FANOTIFY=y
+CONFIG_VFAT_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_UBIFS_FS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_SWAP=y
+CONFIG_ROOT_NFS=y
+CONFIG_SUNRPC_DEBUG=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_INFO=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_VM=y
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_DEBUG_RT_MUTEXES=y
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_ATOMIC_SLEEP=y
+CONFIG_STACKTRACE=y
+CONFIG_RCU_TRACE=y
+# CONFIG_FTRACE is not set
+CONFIG_LD_NO_RELAX=y
+# CONFIG_S32C1I_SELFTEST is not set
+CONFIG_CRYPTO_ANSI_CPRNG=y
index b2173e5..0383aed 100644 (file)
@@ -277,6 +277,8 @@ static inline pte_t pte_mkwrite(pte_t pte)
 static inline pte_t pte_mkspecial(pte_t pte)
        { return pte; }
 
+#define pgprot_noncached(prot) (__pgprot(pgprot_val(prot) & ~_PAGE_CA_MASK))
+
 /*
  * Conversion functions: convert a page and protection to a page entry,
  * and a page entry and page directory to the page they refer to.
index 8883fc8..db5bb72 100644 (file)
@@ -384,7 +384,8 @@ __SYSCALL(174, sys_chroot, 1)
 #define __NR_pivot_root                        175
 __SYSCALL(175, sys_pivot_root, 2)
 #define __NR_umount                            176
-__SYSCALL(176, sys_umount, 2)
+__SYSCALL(176, sys_oldumount, 1)
+#define __ARCH_WANT_SYS_OLDUMOUNT
 #define __NR_swapoff                           177
 __SYSCALL(177, sys_swapoff, 1)
 #define __NR_sync                              178
@@ -742,7 +743,14 @@ __SYSCALL(335, sys_sched_getattr, 3)
 #define __NR_renameat2                         336
 __SYSCALL(336, sys_renameat2, 5)
 
-#define __NR_syscall_count                     337
+#define __NR_seccomp                           337
+__SYSCALL(337, sys_seccomp, 3)
+#define __NR_getrandom                         338
+__SYSCALL(338, sys_getrandom, 3)
+#define __NR_memfd_create                      339
+__SYSCALL(339, sys_memfd_create, 2)
+
+#define __NR_syscall_count                     340
 
 /*
  * sysxtensa syscall handler
index 0984232..5cbd5d9 100644 (file)
@@ -216,9 +216,10 @@ static int bio_integrity_process(struct bio *bio,
 {
        struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev);
        struct blk_integrity_iter iter;
-       struct bio_vec *bv;
+       struct bvec_iter bviter;
+       struct bio_vec bv;
        struct bio_integrity_payload *bip = bio_integrity(bio);
-       unsigned int i, ret = 0;
+       unsigned int ret = 0;
        void *prot_buf = page_address(bip->bip_vec->bv_page) +
                bip->bip_vec->bv_offset;
 
@@ -227,11 +228,11 @@ static int bio_integrity_process(struct bio *bio,
        iter.seed = bip_get_seed(bip);
        iter.prot_buf = prot_buf;
 
-       bio_for_each_segment_all(bv, bio, i) {
-               void *kaddr = kmap_atomic(bv->bv_page);
+       bio_for_each_segment(bv, bio, bviter) {
+               void *kaddr = kmap_atomic(bv.bv_page);
 
-               iter.data_buf = kaddr + bv->bv_offset;
-               iter.data_size = bv->bv_len;
+               iter.data_buf = kaddr + bv.bv_offset;
+               iter.data_size = bv.bv_len;
 
                ret = proc_fn(&iter);
                if (ret) {
index b3ac40a..89b97b5 100644 (file)
@@ -97,19 +97,22 @@ void blk_recalc_rq_segments(struct request *rq)
 
 void blk_recount_segments(struct request_queue *q, struct bio *bio)
 {
-       bool no_sg_merge = !!test_bit(QUEUE_FLAG_NO_SG_MERGE,
-                       &q->queue_flags);
-       bool merge_not_need = bio->bi_vcnt < queue_max_segments(q);
+       unsigned short seg_cnt;
+
+       /* estimate segment number by bi_vcnt for non-cloned bio */
+       if (bio_flagged(bio, BIO_CLONED))
+               seg_cnt = bio_segments(bio);
+       else
+               seg_cnt = bio->bi_vcnt;
 
-       if (no_sg_merge && !bio_flagged(bio, BIO_CLONED) &&
-                       merge_not_need)
-               bio->bi_phys_segments = bio->bi_vcnt;
+       if (test_bit(QUEUE_FLAG_NO_SG_MERGE, &q->queue_flags) &&
+                       (seg_cnt < queue_max_segments(q)))
+               bio->bi_phys_segments = seg_cnt;
        else {
                struct bio *nxt = bio->bi_next;
 
                bio->bi_next = NULL;
-               bio->bi_phys_segments = __blk_recalc_rq_segments(q, bio,
-                               no_sg_merge && merge_not_need);
+               bio->bi_phys_segments = __blk_recalc_rq_segments(q, bio, false);
                bio->bi_next = nxt;
        }
 
index 68929ba..1d016fc 100644 (file)
@@ -107,11 +107,7 @@ static void blk_mq_usage_counter_release(struct percpu_ref *ref)
        wake_up_all(&q->mq_freeze_wq);
 }
 
-/*
- * Guarantee no request is in use, so we can change any data structure of
- * the queue afterward.
- */
-void blk_mq_freeze_queue(struct request_queue *q)
+static void blk_mq_freeze_queue_start(struct request_queue *q)
 {
        bool freeze;
 
@@ -123,9 +119,23 @@ void blk_mq_freeze_queue(struct request_queue *q)
                percpu_ref_kill(&q->mq_usage_counter);
                blk_mq_run_queues(q, false);
        }
+}
+
+static void blk_mq_freeze_queue_wait(struct request_queue *q)
+{
        wait_event(q->mq_freeze_wq, percpu_ref_is_zero(&q->mq_usage_counter));
 }
 
+/*
+ * Guarantee no request is in use, so we can change any data structure of
+ * the queue afterward.
+ */
+void blk_mq_freeze_queue(struct request_queue *q)
+{
+       blk_mq_freeze_queue_start(q);
+       blk_mq_freeze_queue_wait(q);
+}
+
 static void blk_mq_unfreeze_queue(struct request_queue *q)
 {
        bool wake;
@@ -1921,7 +1931,7 @@ void blk_mq_free_queue(struct request_queue *q)
 /* Basically redo blk_mq_init_queue with queue frozen */
 static void blk_mq_queue_reinit(struct request_queue *q)
 {
-       blk_mq_freeze_queue(q);
+       WARN_ON_ONCE(!q->mq_freeze_depth);
 
        blk_mq_sysfs_unregister(q);
 
@@ -1936,8 +1946,6 @@ static void blk_mq_queue_reinit(struct request_queue *q)
        blk_mq_map_swqueue(q);
 
        blk_mq_sysfs_register(q);
-
-       blk_mq_unfreeze_queue(q);
 }
 
 static int blk_mq_queue_reinit_notify(struct notifier_block *nb,
@@ -1956,8 +1964,25 @@ static int blk_mq_queue_reinit_notify(struct notifier_block *nb,
                return NOTIFY_OK;
 
        mutex_lock(&all_q_mutex);
+
+       /*
+        * We need to freeze and reinit all existing queues.  Freezing
+        * involves synchronous wait for an RCU grace period and doing it
+        * one by one may take a long time.  Start freezing all queues in
+        * one swoop and then wait for the completions so that freezing can
+        * take place in parallel.
+        */
+       list_for_each_entry(q, &all_q_list, all_q_node)
+               blk_mq_freeze_queue_start(q);
+       list_for_each_entry(q, &all_q_list, all_q_node)
+               blk_mq_freeze_queue_wait(q);
+
        list_for_each_entry(q, &all_q_list, all_q_node)
                blk_mq_queue_reinit(q);
+
+       list_for_each_entry(q, &all_q_list, all_q_node)
+               blk_mq_unfreeze_queue(q);
+
        mutex_unlock(&all_q_mutex);
        return NOTIFY_OK;
 }
index e50170c..31666c9 100644 (file)
@@ -157,14 +157,16 @@ out:
 
 int ioprio_best(unsigned short aprio, unsigned short bprio)
 {
-       unsigned short aclass = IOPRIO_PRIO_CLASS(aprio);
-       unsigned short bclass = IOPRIO_PRIO_CLASS(bprio);
+       unsigned short aclass;
+       unsigned short bclass;
 
-       if (aclass == IOPRIO_CLASS_NONE)
-               aclass = IOPRIO_CLASS_BE;
-       if (bclass == IOPRIO_CLASS_NONE)
-               bclass = IOPRIO_CLASS_BE;
+       if (!ioprio_valid(aprio))
+               aprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, IOPRIO_NORM);
+       if (!ioprio_valid(bprio))
+               bprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, IOPRIO_NORM);
 
+       aclass = IOPRIO_PRIO_CLASS(aprio);
+       bclass = IOPRIO_PRIO_CLASS(bprio);
        if (aclass == bclass)
                return min(aprio, bprio);
        if (aclass > bclass)
index 1e053d9..b0c2a61 100644 (file)
@@ -458,7 +458,7 @@ int sg_scsi_ioctl(struct request_queue *q, struct gendisk *disk, fmode_t mode,
        rq = blk_get_request(q, in_len ? WRITE : READ, __GFP_WAIT);
        if (IS_ERR(rq)) {
                err = PTR_ERR(rq);
-               goto error;
+               goto error_free_buffer;
        }
        blk_rq_set_block_pc(rq);
 
@@ -531,9 +531,11 @@ int sg_scsi_ioctl(struct request_queue *q, struct gendisk *disk, fmode_t mode,
        }
        
 error:
+       blk_put_request(rq);
+
+error_free_buffer:
        kfree(buffer);
-       if (rq)
-               blk_put_request(rq);
+
        return err;
 }
 EXPORT_SYMBOL_GPL(sg_scsi_ioctl);
index ed122e1..7556e7c 100644 (file)
@@ -290,6 +290,14 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
                    DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3446"),
                },
        },
+       {
+       .callback = dmi_disable_osi_win8,
+       .ident = "Dell Vostro 3546",
+       .matches = {
+                   DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                   DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3546"),
+               },
+       },
 
        /*
         * BIOS invocation of _OSI(Linux) is almost always a BIOS bug.
index 143ec6e..7db1931 100644 (file)
@@ -878,7 +878,7 @@ int acpi_dev_suspend_late(struct device *dev)
                return 0;
 
        target_state = acpi_target_system_state();
-       wakeup = device_may_wakeup(dev);
+       wakeup = device_may_wakeup(dev) && acpi_device_can_wakeup(adev);
        error = acpi_device_wakeup(adev, target_state, wakeup);
        if (wakeup && error)
                return error;
index 807a88a..9d75ead 100644 (file)
@@ -1164,7 +1164,8 @@ static bool acpi_video_device_in_dod(struct acpi_video_device *device)
                return true;
 
        for (i = 0; i < video->attached_count; i++) {
-               if (video->attached_array[i].bind_info == device)
+               if ((video->attached_array[i].value.int_val & 0xfff) ==
+                   (device->device_id & 0xfff))
                        return true;
        }
 
index 5f039f1..49f1e68 100644 (file)
@@ -60,6 +60,7 @@ enum board_ids {
        /* board IDs by feature in alphabetical order */
        board_ahci,
        board_ahci_ign_iferr,
+       board_ahci_nomsi,
        board_ahci_noncq,
        board_ahci_nosntf,
        board_ahci_yes_fbs,
@@ -121,6 +122,13 @@ static const struct ata_port_info ahci_port_info[] = {
                .udma_mask      = ATA_UDMA6,
                .port_ops       = &ahci_ops,
        },
+       [board_ahci_nomsi] = {
+               AHCI_HFLAGS     (AHCI_HFLAG_NO_MSI),
+               .flags          = AHCI_FLAG_COMMON,
+               .pio_mask       = ATA_PIO4,
+               .udma_mask      = ATA_UDMA6,
+               .port_ops       = &ahci_ops,
+       },
        [board_ahci_noncq] = {
                AHCI_HFLAGS     (AHCI_HFLAG_NO_NCQ),
                .flags          = AHCI_FLAG_COMMON,
@@ -313,6 +321,14 @@ static const struct pci_device_id ahci_pci_tbl[] = {
        { PCI_VDEVICE(INTEL, 0x8c87), board_ahci }, /* 9 Series RAID */
        { PCI_VDEVICE(INTEL, 0x8c8e), board_ahci }, /* 9 Series RAID */
        { PCI_VDEVICE(INTEL, 0x8c8f), board_ahci }, /* 9 Series RAID */
+       { PCI_VDEVICE(INTEL, 0x9d03), board_ahci }, /* Sunrise Point-LP AHCI */
+       { PCI_VDEVICE(INTEL, 0x9d05), board_ahci }, /* Sunrise Point-LP RAID */
+       { PCI_VDEVICE(INTEL, 0x9d07), board_ahci }, /* Sunrise Point-LP RAID */
+       { PCI_VDEVICE(INTEL, 0xa103), board_ahci }, /* Sunrise Point-H AHCI */
+       { PCI_VDEVICE(INTEL, 0xa103), board_ahci }, /* Sunrise Point-H RAID */
+       { PCI_VDEVICE(INTEL, 0xa105), board_ahci }, /* Sunrise Point-H RAID */
+       { PCI_VDEVICE(INTEL, 0xa107), board_ahci }, /* Sunrise Point-H RAID */
+       { PCI_VDEVICE(INTEL, 0xa10f), board_ahci }, /* Sunrise Point-H RAID */
 
        /* JMicron 360/1/3/5/6, match class to avoid IDE function */
        { PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
@@ -475,10 +491,11 @@ static const struct pci_device_id ahci_pci_tbl[] = {
        { PCI_VDEVICE(ASMEDIA, 0x0612), board_ahci },   /* ASM1062 */
 
        /*
-        * Samsung SSDs found on some macbooks.  NCQ times out.
-        * https://bugzilla.kernel.org/show_bug.cgi?id=60731
+        * Samsung SSDs found on some macbooks.  NCQ times out if MSI is
+        * enabled.  https://bugzilla.kernel.org/show_bug.cgi?id=60731
         */
-       { PCI_VDEVICE(SAMSUNG, 0x1600), board_ahci_noncq },
+       { PCI_VDEVICE(SAMSUNG, 0x1600), board_ahci_nomsi },
+       { PCI_VDEVICE(SAMSUNG, 0xa800), board_ahci_nomsi },
 
        /* Enmotus */
        { PCI_DEVICE(0x1c44, 0x8000), board_ahci },
@@ -514,12 +531,9 @@ MODULE_PARM_DESC(marvell_enable, "Marvell SATA via AHCI (1 = enabled)");
 static void ahci_pci_save_initial_config(struct pci_dev *pdev,
                                         struct ahci_host_priv *hpriv)
 {
-       unsigned int force_port_map = 0;
-       unsigned int mask_port_map = 0;
-
        if (pdev->vendor == PCI_VENDOR_ID_JMICRON && pdev->device == 0x2361) {
                dev_info(&pdev->dev, "JMB361 has only one port\n");
-               force_port_map = 1;
+               hpriv->force_port_map = 1;
        }
 
        /*
@@ -529,9 +543,9 @@ static void ahci_pci_save_initial_config(struct pci_dev *pdev,
         */
        if (hpriv->flags & AHCI_HFLAG_MV_PATA) {
                if (pdev->device == 0x6121)
-                       mask_port_map = 0x3;
+                       hpriv->mask_port_map = 0x3;
                else
-                       mask_port_map = 0xf;
+                       hpriv->mask_port_map = 0xf;
                dev_info(&pdev->dev,
                          "Disabling your PATA port. Use the boot option 'ahci.marvell_enable=0' to avoid this.\n");
        }
index 5eb61c9..97683e4 100644 (file)
@@ -1778,16 +1778,15 @@ static void ahci_handle_port_interrupt(struct ata_port *ap,
        }
 }
 
-static void ahci_update_intr_status(struct ata_port *ap)
+static void ahci_port_intr(struct ata_port *ap)
 {
        void __iomem *port_mmio = ahci_port_base(ap);
-       struct ahci_port_priv *pp = ap->private_data;
        u32 status;
 
        status = readl(port_mmio + PORT_IRQ_STAT);
        writel(status, port_mmio + PORT_IRQ_STAT);
 
-       atomic_or(status, &pp->intr_status);
+       ahci_handle_port_interrupt(ap, port_mmio, status);
 }
 
 static irqreturn_t ahci_port_thread_fn(int irq, void *dev_instance)
@@ -1808,34 +1807,6 @@ static irqreturn_t ahci_port_thread_fn(int irq, void *dev_instance)
        return IRQ_HANDLED;
 }
 
-irqreturn_t ahci_thread_fn(int irq, void *dev_instance)
-{
-       struct ata_host *host = dev_instance;
-       struct ahci_host_priv *hpriv = host->private_data;
-       u32 irq_masked = hpriv->port_map;
-       unsigned int i;
-
-       for (i = 0; i < host->n_ports; i++) {
-               struct ata_port *ap;
-
-               if (!(irq_masked & (1 << i)))
-                       continue;
-
-               ap = host->ports[i];
-               if (ap) {
-                       ahci_port_thread_fn(irq, ap);
-                       VPRINTK("port %u\n", i);
-               } else {
-                       VPRINTK("port %u (no irq)\n", i);
-                       if (ata_ratelimit())
-                               dev_warn(host->dev,
-                                        "interrupt on disabled port %u\n", i);
-               }
-       }
-
-       return IRQ_HANDLED;
-}
-
 static irqreturn_t ahci_multi_irqs_intr(int irq, void *dev_instance)
 {
        struct ata_port *ap = dev_instance;
@@ -1875,6 +1846,8 @@ static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance)
 
        irq_masked = irq_stat & hpriv->port_map;
 
+       spin_lock(&host->lock);
+
        for (i = 0; i < host->n_ports; i++) {
                struct ata_port *ap;
 
@@ -1883,7 +1856,7 @@ static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance)
 
                ap = host->ports[i];
                if (ap) {
-                       ahci_update_intr_status(ap);
+                       ahci_port_intr(ap);
                        VPRINTK("port %u\n", i);
                } else {
                        VPRINTK("port %u (no irq)\n", i);
@@ -1906,9 +1879,11 @@ static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance)
         */
        writel(irq_stat, mmio + HOST_IRQ_STAT);
 
+       spin_unlock(&host->lock);
+
        VPRINTK("EXIT\n");
 
-       return handled ? IRQ_WAKE_THREAD : IRQ_NONE;
+       return IRQ_RETVAL(handled);
 }
 
 unsigned int ahci_qc_issue(struct ata_queued_cmd *qc)
@@ -2320,8 +2295,13 @@ static int ahci_port_start(struct ata_port *ap)
         */
        pp->intr_mask = DEF_PORT_IRQ;
 
-       spin_lock_init(&pp->lock);
-       ap->lock = &pp->lock;
+       /*
+        * Switch to per-port locking in case each port has its own MSI vector.
+        */
+       if ((hpriv->flags & AHCI_HFLAG_MULTI_MSI)) {
+               spin_lock_init(&pp->lock);
+               ap->lock = &pp->lock;
+       }
 
        ap->private_data = pp;
 
@@ -2482,31 +2462,6 @@ out_free_irqs:
        return rc;
 }
 
-static int ahci_host_activate_single_irq(struct ata_host *host, int irq,
-                                        struct scsi_host_template *sht)
-{
-       int i, rc;
-
-       rc = ata_host_start(host);
-       if (rc)
-               return rc;
-
-       rc = devm_request_threaded_irq(host->dev, irq, ahci_single_irq_intr,
-                                      ahci_thread_fn, IRQF_SHARED,
-                                      dev_driver_string(host->dev), host);
-       if (rc)
-               return rc;
-
-       for (i = 0; i < host->n_ports; i++)
-               ata_port_desc(host->ports[i], "irq %d", irq);
-
-       rc = ata_host_register(host, sht);
-       if (rc)
-               devm_free_irq(host->dev, irq, host);
-
-       return rc;
-}
-
 /**
  *     ahci_host_activate - start AHCI host, request IRQs and register it
  *     @host: target ATA host
@@ -2532,7 +2487,8 @@ int ahci_host_activate(struct ata_host *host, int irq,
        if (hpriv->flags & AHCI_HFLAG_MULTI_MSI)
                rc = ahci_host_activate_multi_irqs(host, irq, sht);
        else
-               rc = ahci_host_activate_single_irq(host, irq, sht);
+               rc = ata_host_activate(host, irq, ahci_single_irq_intr,
+                                      IRQF_SHARED, sht);
        return rc;
 }
 EXPORT_SYMBOL_GPL(ahci_host_activate);
index 07bc7e4..6507159 100644 (file)
@@ -1488,7 +1488,7 @@ static int sata_fsl_probe(struct platform_device *ofdev)
        host_priv->csr_base = csr_base;
 
        irq = irq_of_parse_and_map(ofdev->dev.of_node, 0);
-       if (irq < 0) {
+       if (!irq) {
                dev_err(&ofdev->dev, "invalid irq from platform\n");
                goto error_exit_with_cleanup;
        }
index 61eb6d7..ea1fbc1 100644 (file)
 enum sata_rcar_type {
        RCAR_GEN1_SATA,
        RCAR_GEN2_SATA,
+       RCAR_R8A7790_ES1_SATA,
 };
 
 struct sata_rcar_priv {
@@ -763,6 +764,9 @@ static void sata_rcar_setup_port(struct ata_host *host)
        ap->udma_mask   = ATA_UDMA6;
        ap->flags       |= ATA_FLAG_SATA;
 
+       if (priv->type == RCAR_R8A7790_ES1_SATA)
+               ap->flags       |= ATA_FLAG_NO_DIPM;
+
        ioaddr->cmd_addr = base + SDATA_REG;
        ioaddr->ctl_addr = base + SSDEVCON_REG;
        ioaddr->scr_addr = base + SCRSSTS_REG;
@@ -792,6 +796,7 @@ static void sata_rcar_init_controller(struct ata_host *host)
                sata_rcar_gen1_phy_init(priv);
                break;
        case RCAR_GEN2_SATA:
+       case RCAR_R8A7790_ES1_SATA:
                sata_rcar_gen2_phy_init(priv);
                break;
        default:
@@ -837,10 +842,18 @@ static struct of_device_id sata_rcar_match[] = {
                .compatible = "renesas,sata-r8a7790",
                .data = (void *)RCAR_GEN2_SATA
        },
+       {
+               .compatible = "renesas,sata-r8a7790-es1",
+               .data = (void *)RCAR_R8A7790_ES1_SATA
+       },
        {
                .compatible = "renesas,sata-r8a7791",
                .data = (void *)RCAR_GEN2_SATA
        },
+       {
+               .compatible = "renesas,sata-r8a7793",
+               .data = (void *)RCAR_GEN2_SATA
+       },
        { },
 };
 MODULE_DEVICE_TABLE(of, sata_rcar_match);
@@ -849,7 +862,9 @@ static const struct platform_device_id sata_rcar_id_table[] = {
        { "sata_rcar", RCAR_GEN1_SATA }, /* Deprecated by "sata-r8a7779" */
        { "sata-r8a7779", RCAR_GEN1_SATA },
        { "sata-r8a7790", RCAR_GEN2_SATA },
+       { "sata-r8a7790-es1", RCAR_R8A7790_ES1_SATA },
        { "sata-r8a7791", RCAR_GEN2_SATA },
+       { "sata-r8a7793", RCAR_GEN2_SATA },
        { },
 };
 MODULE_DEVICE_TABLE(platform, sata_rcar_id_table);
index 7652e8d..21b0bc6 100644 (file)
@@ -1225,11 +1225,13 @@ static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id)
        card->config_regs = pci_iomap(dev, 0, CONFIG_RAM_SIZE);
        if (!card->config_regs) {
                dev_warn(&dev->dev, "Failed to ioremap config registers\n");
+               err = -ENOMEM;
                goto out_release_regions;
        }
        card->buffers = pci_iomap(dev, 1, DATA_RAM_SIZE);
        if (!card->buffers) {
                dev_warn(&dev->dev, "Failed to ioremap data buffers\n");
+               err = -ENOMEM;
                goto out_unmap_config;
        }
 
index 61a33f4..df04227 100644 (file)
@@ -171,20 +171,23 @@ config WANT_DEV_COREDUMP
          Drivers should "select" this option if they desire to use the
          device coredump mechanism.
 
-config DISABLE_DEV_COREDUMP
-       bool "Disable device coredump" if EXPERT
+config ALLOW_DEV_COREDUMP
+       bool "Allow device coredump" if EXPERT
+       default y
        help
-         Disable the device coredump mechanism despite drivers wanting to
-         use it; this allows for more sensitive systems or systems that
-         don't want to ever access the information to not have the code,
-         nor keep any data.
+         This option controls if the device coredump mechanism is available or
+         not; if disabled, the mechanism will be omitted even if drivers that
+         can use it are enabled.
+         Say 'N' for more sensitive systems or systems that don't want
+         to ever access the information to not have the code, nor keep any
+         data.
 
-         If unsure, say N.
+         If unsure, say Y.
 
 config DEV_COREDUMP
        bool
        default y if WANT_DEV_COREDUMP
-       depends on !DISABLE_DEV_COREDUMP
+       depends on ALLOW_DEV_COREDUMP
 
 config DEBUG_DRIVER
        bool "Driver Core verbose debug messages"
index 14d1629..842d047 100644 (file)
@@ -724,12 +724,12 @@ class_dir_create_and_add(struct class *class, struct kobject *parent_kobj)
        return &dir->kobj;
 }
 
+static DEFINE_MUTEX(gdp_mutex);
 
 static struct kobject *get_device_parent(struct device *dev,
                                         struct device *parent)
 {
        if (dev->class) {
-               static DEFINE_MUTEX(gdp_mutex);
                struct kobject *kobj = NULL;
                struct kobject *parent_kobj;
                struct kobject *k;
@@ -793,7 +793,9 @@ static void cleanup_glue_dir(struct device *dev, struct kobject *glue_dir)
            glue_dir->kset != &dev->class->p->glue_dirs)
                return;
 
+       mutex_lock(&gdp_mutex);
        kobject_put(glue_dir);
+       mutex_unlock(&gdp_mutex);
 }
 
 static void cleanup_device_parent(struct device *dev)
index 40bc2f4..fb83d4a 100644 (file)
@@ -361,9 +361,19 @@ static int __pm_genpd_save_device(struct pm_domain_data *pdd,
        struct device *dev = pdd->dev;
        int ret = 0;
 
-       if (gpd_data->need_restore)
+       if (gpd_data->need_restore > 0)
                return 0;
 
+       /*
+        * If the value of the need_restore flag is still unknown at this point,
+        * we trust that pm_genpd_poweroff() has verified that the device is
+        * already runtime PM suspended.
+        */
+       if (gpd_data->need_restore < 0) {
+               gpd_data->need_restore = 1;
+               return 0;
+       }
+
        mutex_unlock(&genpd->lock);
 
        genpd_start_dev(genpd, dev);
@@ -373,7 +383,7 @@ static int __pm_genpd_save_device(struct pm_domain_data *pdd,
        mutex_lock(&genpd->lock);
 
        if (!ret)
-               gpd_data->need_restore = true;
+               gpd_data->need_restore = 1;
 
        return ret;
 }
@@ -389,12 +399,17 @@ static void __pm_genpd_restore_device(struct pm_domain_data *pdd,
 {
        struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
        struct device *dev = pdd->dev;
-       bool need_restore = gpd_data->need_restore;
+       int need_restore = gpd_data->need_restore;
 
-       gpd_data->need_restore = false;
+       gpd_data->need_restore = 0;
        mutex_unlock(&genpd->lock);
 
        genpd_start_dev(genpd, dev);
+
+       /*
+        * Call genpd_restore_dev() for recently added devices too (need_restore
+        * is negative then).
+        */
        if (need_restore)
                genpd_restore_dev(genpd, dev);
 
@@ -603,6 +618,7 @@ static void genpd_power_off_work_fn(struct work_struct *work)
 static int pm_genpd_runtime_suspend(struct device *dev)
 {
        struct generic_pm_domain *genpd;
+       struct generic_pm_domain_data *gpd_data;
        bool (*stop_ok)(struct device *__dev);
        int ret;
 
@@ -628,6 +644,16 @@ static int pm_genpd_runtime_suspend(struct device *dev)
                return 0;
 
        mutex_lock(&genpd->lock);
+
+       /*
+        * If we have an unknown state of the need_restore flag, it means none
+        * of the runtime PM callbacks has been invoked yet. Let's update the
+        * flag to reflect that the current state is active.
+        */
+       gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
+       if (gpd_data->need_restore < 0)
+               gpd_data->need_restore = 0;
+
        genpd->in_progress++;
        pm_genpd_poweroff(genpd);
        genpd->in_progress--;
@@ -1437,12 +1463,12 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
        spin_unlock_irq(&dev->power.lock);
 
        if (genpd->attach_dev)
-               genpd->attach_dev(dev);
+               genpd->attach_dev(genpd, dev);
 
        mutex_lock(&gpd_data->lock);
        gpd_data->base.dev = dev;
        list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
-       gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF;
+       gpd_data->need_restore = -1;
        gpd_data->td.constraint_changed = true;
        gpd_data->td.effective_constraint_ns = -1;
        mutex_unlock(&gpd_data->lock);
@@ -1499,7 +1525,7 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
        genpd->max_off_time_changed = true;
 
        if (genpd->detach_dev)
-               genpd->detach_dev(dev);
+               genpd->detach_dev(genpd, dev);
 
        spin_lock_irq(&dev->power.lock);
 
@@ -1546,7 +1572,7 @@ void pm_genpd_dev_need_restore(struct device *dev, bool val)
 
        psd = dev_to_psd(dev);
        if (psd && psd->domain_data)
-               to_gpd_data(psd->domain_data)->need_restore = val;
+               to_gpd_data(psd->domain_data)->need_restore = val ? 1 : 0;
 
        spin_unlock_irqrestore(&dev->power.lock, flags);
 }
index 8a3f51f..db9d00c 100644 (file)
@@ -3,12 +3,15 @@
 # subsystems should select the appropriate symbols.
 
 config REGMAP
-       default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_MMIO || REGMAP_IRQ)
+       default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ)
        select LZO_COMPRESS
        select LZO_DECOMPRESS
        select IRQ_DOMAIN if REGMAP_IRQ
        bool
 
+config REGMAP_AC97
+       tristate
+
 config REGMAP_I2C
        tristate
        depends on I2C
index a7c670b..0a53365 100644 (file)
@@ -1,6 +1,7 @@
 obj-$(CONFIG_REGMAP) += regmap.o regcache.o
 obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.o
 obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
+obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o
 obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
 obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
 obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o
diff --git a/drivers/base/regmap/regmap-ac97.c b/drivers/base/regmap/regmap-ac97.c
new file mode 100644 (file)
index 0000000..e4c45d2
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Register map access API - AC'97 support
+ *
+ * Copyright 2013 Linaro Ltd.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <sound/ac97_codec.h>
+
+bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case AC97_RESET:
+       case AC97_POWERDOWN:
+       case AC97_INT_PAGING:
+       case AC97_EXTENDED_ID:
+       case AC97_EXTENDED_STATUS:
+       case AC97_EXTENDED_MID:
+       case AC97_EXTENDED_MSTATUS:
+       case AC97_GPIO_STATUS:
+       case AC97_MISC_AFE:
+       case AC97_VENDOR_ID1:
+       case AC97_VENDOR_ID2:
+       case AC97_CODEC_CLASS_REV:
+       case AC97_PCI_SVID:
+       case AC97_PCI_SID:
+       case AC97_FUNC_SELECT:
+       case AC97_FUNC_INFO:
+       case AC97_SENSE_INFO:
+               return true;
+       default:
+               return false;
+       }
+}
+EXPORT_SYMBOL_GPL(regmap_ac97_default_volatile);
+
+static int regmap_ac97_reg_read(void *context, unsigned int reg,
+       unsigned int *val)
+{
+       struct snd_ac97 *ac97 = context;
+
+       *val = ac97->bus->ops->read(ac97, reg);
+
+       return 0;
+}
+
+static int regmap_ac97_reg_write(void *context, unsigned int reg,
+       unsigned int val)
+{
+       struct snd_ac97 *ac97 = context;
+
+       ac97->bus->ops->write(ac97, reg, val);
+
+       return 0;
+}
+
+static const struct regmap_bus ac97_regmap_bus = {
+               .reg_write = regmap_ac97_reg_write,
+               .reg_read = regmap_ac97_reg_read,
+};
+
+/**
+ * regmap_init_ac97(): Initialise AC'97 register map
+ *
+ * @ac97: Device that will be interacted with
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer to
+ * a struct regmap.
+ */
+struct regmap *regmap_init_ac97(struct snd_ac97 *ac97,
+                               const struct regmap_config *config)
+{
+       return regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config);
+}
+EXPORT_SYMBOL_GPL(regmap_init_ac97);
+
+/**
+ * devm_regmap_init_ac97(): Initialise AC'97 register map
+ *
+ * @ac97: Device that will be interacted with
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct regmap.  The regmap will be automatically freed by the
+ * device management code.
+ */
+struct regmap *devm_regmap_init_ac97(struct snd_ac97 *ac97,
+                                    const struct regmap_config *config)
+{
+       return devm_regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config);
+}
+EXPORT_SYMBOL_GPL(devm_regmap_init_ac97);
+
+MODULE_LICENSE("GPL v2");
index 0a54c58..27b71a0 100644 (file)
@@ -342,7 +342,6 @@ struct rbd_device {
 
        struct list_head        rq_queue;       /* incoming rq queue */
        spinlock_t              lock;           /* queue, flags, open_count */
-       struct workqueue_struct *rq_wq;
        struct work_struct      rq_work;
 
        struct rbd_image_header header;
@@ -402,6 +401,8 @@ static struct kmem_cache    *rbd_segment_name_cache;
 static int rbd_major;
 static DEFINE_IDA(rbd_dev_id_ida);
 
+static struct workqueue_struct *rbd_wq;
+
 /*
  * Default to false for now, as single-major requires >= 0.75 version of
  * userspace rbd utility.
@@ -3452,7 +3453,7 @@ static void rbd_request_fn(struct request_queue *q)
        }
 
        if (queued)
-               queue_work(rbd_dev->rq_wq, &rbd_dev->rq_work);
+               queue_work(rbd_wq, &rbd_dev->rq_work);
 }
 
 /*
@@ -3532,7 +3533,7 @@ static int rbd_obj_read_sync(struct rbd_device *rbd_dev,
        page_count = (u32) calc_pages_for(offset, length);
        pages = ceph_alloc_page_vector(page_count, GFP_KERNEL);
        if (IS_ERR(pages))
-               ret = PTR_ERR(pages);
+               return PTR_ERR(pages);
 
        ret = -ENOMEM;
        obj_request = rbd_obj_request_create(object_name, offset, length,
@@ -5242,16 +5243,9 @@ static int rbd_dev_device_setup(struct rbd_device *rbd_dev)
        set_capacity(rbd_dev->disk, rbd_dev->mapping.size / SECTOR_SIZE);
        set_disk_ro(rbd_dev->disk, rbd_dev->mapping.read_only);
 
-       rbd_dev->rq_wq = alloc_workqueue("%s", WQ_MEM_RECLAIM, 0,
-                                        rbd_dev->disk->disk_name);
-       if (!rbd_dev->rq_wq) {
-               ret = -ENOMEM;
-               goto err_out_mapping;
-       }
-
        ret = rbd_bus_add_dev(rbd_dev);
        if (ret)
-               goto err_out_workqueue;
+               goto err_out_mapping;
 
        /* Everything's ready.  Announce the disk to the world. */
 
@@ -5263,9 +5257,6 @@ static int rbd_dev_device_setup(struct rbd_device *rbd_dev)
 
        return ret;
 
-err_out_workqueue:
-       destroy_workqueue(rbd_dev->rq_wq);
-       rbd_dev->rq_wq = NULL;
 err_out_mapping:
        rbd_dev_mapping_clear(rbd_dev);
 err_out_disk:
@@ -5512,7 +5503,6 @@ static void rbd_dev_device_release(struct device *dev)
 {
        struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
 
-       destroy_workqueue(rbd_dev->rq_wq);
        rbd_free_disk(rbd_dev);
        clear_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags);
        rbd_dev_mapping_clear(rbd_dev);
@@ -5716,11 +5706,21 @@ static int __init rbd_init(void)
        if (rc)
                return rc;
 
+       /*
+        * The number of active work items is limited by the number of
+        * rbd devices, so leave @max_active at default.
+        */
+       rbd_wq = alloc_workqueue(RBD_DRV_NAME, WQ_MEM_RECLAIM, 0);
+       if (!rbd_wq) {
+               rc = -ENOMEM;
+               goto err_out_slab;
+       }
+
        if (single_major) {
                rbd_major = register_blkdev(0, RBD_DRV_NAME);
                if (rbd_major < 0) {
                        rc = rbd_major;
-                       goto err_out_slab;
+                       goto err_out_wq;
                }
        }
 
@@ -5738,6 +5738,8 @@ static int __init rbd_init(void)
 err_out_blkdev:
        if (single_major)
                unregister_blkdev(rbd_major, RBD_DRV_NAME);
+err_out_wq:
+       destroy_workqueue(rbd_wq);
 err_out_slab:
        rbd_slab_exit();
        return rc;
@@ -5749,6 +5751,7 @@ static void __exit rbd_exit(void)
        rbd_sysfs_cleanup();
        if (single_major)
                unregister_blkdev(rbd_major, RBD_DRV_NAME);
+       destroy_workqueue(rbd_wq);
        rbd_slab_exit();
 }
 
index 2ad0b5b..3920ee4 100644 (file)
@@ -560,7 +560,8 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
        }
 
        if (page_zero_filled(uncmem)) {
-               kunmap_atomic(user_mem);
+               if (user_mem)
+                       kunmap_atomic(user_mem);
                /* Free memory associated with this sector now. */
                bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value);
                zram_free_page(zram, index);
index 6226aa0..bcf86f9 100644 (file)
 #include <asm/vio.h>
 
 
-static int pseries_rng_data_read(struct hwrng *rng, u32 *data)
+static int pseries_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
 {
+       u64 buffer[PLPAR_HCALL_BUFSIZE];
+       size_t size = max < 8 ? max : 8;
        int rc;
 
-       rc = plpar_hcall(H_RANDOM, (unsigned long *)data);
+       rc = plpar_hcall(H_RANDOM, (unsigned long *)buffer);
        if (rc != H_SUCCESS) {
                pr_err_ratelimited("H_RANDOM call failed %d\n", rc);
                return -EIO;
        }
+       memcpy(data, buffer, size);
 
        /* The hypervisor interface returns 64 bits */
-       return 8;
+       return size;
 }
 
 /**
@@ -55,7 +58,7 @@ static unsigned long pseries_rng_get_desired_dma(struct vio_dev *vdev)
 
 static struct hwrng pseries_rng = {
        .name           = KBUILD_MODNAME,
-       .data_read      = pseries_rng_data_read,
+       .read           = pseries_rng_read,
 };
 
 static int __init pseries_rng_probe(struct vio_dev *dev,
index bfa6400..cf7a561 100644 (file)
@@ -1449,8 +1449,6 @@ static int add_port(struct ports_device *portdev, u32 id)
        spin_lock_init(&port->outvq_lock);
        init_waitqueue_head(&port->waitqueue);
 
-       virtio_device_ready(portdev->vdev);
-
        /* Fill the in_vq with buffers so the host can send us data. */
        nr_added_bufs = fill_queue(port->in_vq, &port->inbuf_lock);
        if (!nr_added_bufs) {
@@ -2026,6 +2024,8 @@ static int virtcons_probe(struct virtio_device *vdev)
        spin_lock_init(&portdev->ports_lock);
        INIT_LIST_HEAD(&portdev->ports);
 
+       virtio_device_ready(portdev->vdev);
+
        if (multiport) {
                unsigned int nr_added_bufs;
 
index 24b5b02..a23ac0c 100644 (file)
@@ -52,29 +52,26 @@ static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk_hw *hw,
 
        tmp = pmc_read(pmc, AT91_PMC_USB);
        usbdiv = (tmp & AT91_PMC_OHCIUSBDIV) >> SAM9X5_USB_DIV_SHIFT;
-       return parent_rate / (usbdiv + 1);
+
+       return DIV_ROUND_CLOSEST(parent_rate, (usbdiv + 1));
 }
 
 static long at91sam9x5_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
                                          unsigned long *parent_rate)
 {
        unsigned long div;
-       unsigned long bestrate;
-       unsigned long tmp;
+
+       if (!rate)
+               return -EINVAL;
 
        if (rate >= *parent_rate)
                return *parent_rate;
 
-       div = *parent_rate / rate;
-       if (div >= SAM9X5_USB_MAX_DIV)
-               return *parent_rate / (SAM9X5_USB_MAX_DIV + 1);
-
-       bestrate = *parent_rate / div;
-       tmp = *parent_rate / (div + 1);
-       if (bestrate - rate > rate - tmp)
-               bestrate = tmp;
+       div = DIV_ROUND_CLOSEST(*parent_rate, rate);
+       if (div > SAM9X5_USB_MAX_DIV + 1)
+               div = SAM9X5_USB_MAX_DIV + 1;
 
-       return bestrate;
+       return DIV_ROUND_CLOSEST(*parent_rate, div);
 }
 
 static int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index)
@@ -106,9 +103,13 @@ static int at91sam9x5_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
        u32 tmp;
        struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
        struct at91_pmc *pmc = usb->pmc;
-       unsigned long div = parent_rate / rate;
+       unsigned long div;
+
+       if (!rate)
+               return -EINVAL;
 
-       if (parent_rate % rate || div < 1 || div >= SAM9X5_USB_MAX_DIV)
+       div = DIV_ROUND_CLOSEST(parent_rate, rate);
+       if (div > SAM9X5_USB_MAX_DIV + 1 || !div)
                return -EINVAL;
 
        tmp = pmc_read(pmc, AT91_PMC_USB) & ~AT91_PMC_OHCIUSBDIV;
@@ -253,7 +254,7 @@ static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
 
                tmp_parent_rate = rate * usb->divisors[i];
                tmp_parent_rate = __clk_round_rate(parent, tmp_parent_rate);
-               tmprate = tmp_parent_rate / usb->divisors[i];
+               tmprate = DIV_ROUND_CLOSEST(tmp_parent_rate, usb->divisors[i]);
                if (tmprate < rate)
                        tmpdiff = rate - tmprate;
                else
@@ -281,10 +282,10 @@ static int at91rm9200_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
        struct at91_pmc *pmc = usb->pmc;
        unsigned long div;
 
-       if (!rate || parent_rate % rate)
+       if (!rate)
                return -EINVAL;
 
-       div = parent_rate / rate;
+       div = DIV_ROUND_CLOSEST(parent_rate, rate);
 
        for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) {
                if (usb->divisors[i] == div) {
index 18a9de2..c0a842b 100644 (file)
@@ -263,6 +263,14 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
        if (!rate)
                rate = 1;
 
+       /* if read only, just return current value */
+       if (divider->flags & CLK_DIVIDER_READ_ONLY) {
+               bestdiv = readl(divider->reg) >> divider->shift;
+               bestdiv &= div_mask(divider);
+               bestdiv = _get_div(divider, bestdiv);
+               return bestdiv;
+       }
+
        maxdiv = _get_maxdiv(divider);
 
        if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) {
@@ -361,11 +369,6 @@ const struct clk_ops clk_divider_ops = {
 };
 EXPORT_SYMBOL_GPL(clk_divider_ops);
 
-const struct clk_ops clk_divider_ro_ops = {
-       .recalc_rate = clk_divider_recalc_rate,
-};
-EXPORT_SYMBOL_GPL(clk_divider_ro_ops);
-
 static struct clk *_register_divider(struct device *dev, const char *name,
                const char *parent_name, unsigned long flags,
                void __iomem *reg, u8 shift, u8 width,
@@ -391,10 +394,7 @@ static struct clk *_register_divider(struct device *dev, const char *name,
        }
 
        init.name = name;
-       if (clk_divider_flags & CLK_DIVIDER_READ_ONLY)
-               init.ops = &clk_divider_ro_ops;
-       else
-               init.ops = &clk_divider_ops;
+       init.ops = &clk_divider_ops;
        init.flags = flags | CLK_IS_BASIC;
        init.parent_names = (parent_name ? &parent_name: NULL);
        init.num_parents = (parent_name ? 1 : 0);
index b345cc7..88b9fe1 100644 (file)
@@ -322,7 +322,7 @@ static unsigned long clk_pxa27x_memory_get_rate(struct clk_hw *hw,
        unsigned long ccsr = CCSR;
 
        osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
-       a = cccr & CCCR_A_BIT;
+       a = cccr & (1 << CCCR_A_BIT);
        l  = ccsr & CCSR_L_MASK;
 
        if (osc_forced || a)
@@ -341,7 +341,7 @@ static u8 clk_pxa27x_memory_get_parent(struct clk_hw *hw)
        unsigned long ccsr = CCSR;
 
        osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
-       a = cccr & CCCR_A_BIT;
+       a = cccr & (1 << CCCR_A_BIT);
        if (osc_forced)
                return PXA_MEM_13Mhz;
        if (a)
index dab988a..157139a 100644 (file)
@@ -3122,7 +3122,7 @@ static struct clk_regmap *mmcc_apq8084_clocks[] = {
        [ESC1_CLK_SRC] = &esc1_clk_src.clkr,
        [HDMI_CLK_SRC] = &hdmi_clk_src.clkr,
        [VSYNC_CLK_SRC] = &vsync_clk_src.clkr,
-       [RBCPR_CLK_SRC] = &rbcpr_clk_src.clkr,
+       [MMSS_RBCPR_CLK_SRC] = &rbcpr_clk_src.clkr,
        [RBBMTIMER_CLK_SRC] = &rbbmtimer_clk_src.clkr,
        [MAPLE_CLK_SRC] = &maple_clk_src.clkr,
        [VDP_CLK_SRC] = &vdp_clk_src.clkr,
index 1e68bff..880a266 100644 (file)
@@ -90,9 +90,7 @@ static struct clk *rockchip_clk_register_branch(const char *name,
                div->width = div_width;
                div->lock = lock;
                div->table = div_table;
-               div_ops = (div_flags & CLK_DIVIDER_READ_ONLY)
-                                               ? &clk_divider_ro_ops
-                                               : &clk_divider_ops;
+               div_ops = &clk_divider_ops;
        }
 
        clk = clk_register_composite(NULL, name, parent_names, num_parents,
index efb17c3..f4a9c00 100644 (file)
@@ -182,6 +182,12 @@ static void __init sun4i_timer_init(struct device_node *node)
        /* Make sure timer is stopped before playing with interrupts */
        sun4i_clkevt_time_stop(0);
 
+       sun4i_clockevent.cpumask = cpu_possible_mask;
+       sun4i_clockevent.irq = irq;
+
+       clockevents_config_and_register(&sun4i_clockevent, rate,
+                                       TIMER_SYNC_TICKS, 0xffffffff);
+
        ret = setup_irq(irq, &sun4i_timer_irq);
        if (ret)
                pr_warn("failed to setup irq %d\n", irq);
@@ -189,12 +195,6 @@ static void __init sun4i_timer_init(struct device_node *node)
        /* Enable timer0 interrupt */
        val = readl(timer_base + TIMER_IRQ_EN_REG);
        writel(val | TIMER_IRQ_EN(0), timer_base + TIMER_IRQ_EN_REG);
-
-       sun4i_clockevent.cpumask = cpu_possible_mask;
-       sun4i_clockevent.irq = irq;
-
-       clockevents_config_and_register(&sun4i_clockevent, rate,
-                                       TIMER_SYNC_TICKS, 0xffffffff);
 }
 CLOCKSOURCE_OF_DECLARE(sun4i, "allwinner,sun4i-a10-timer",
                       sun4i_timer_init);
index 23aaf40..f657c57 100644 (file)
@@ -166,8 +166,8 @@ try_again:
                if (ret == -EPROBE_DEFER)
                        dev_dbg(cpu_dev, "cpu%d clock not ready, retry\n", cpu);
                else
-                       dev_err(cpu_dev, "failed to get cpu%d clock: %d\n", ret,
-                               cpu);
+                       dev_err(cpu_dev, "failed to get cpu%d clock: %d\n", cpu,
+                               ret);
        } else {
                *cdev = cpu_dev;
                *creg = cpu_reg;
index 644b54e..4473eba 100644 (file)
@@ -1022,7 +1022,8 @@ static struct cpufreq_policy *cpufreq_policy_restore(unsigned int cpu)
 
        read_unlock_irqrestore(&cpufreq_driver_lock, flags);
 
-       policy->governor = NULL;
+       if (policy)
+               policy->governor = NULL;
 
        return policy;
 }
index 871703c..e1eaf4f 100644 (file)
@@ -48,23 +48,29 @@ int gen_split_key(struct device *jrdev, u8 *key_out, int split_key_len,
        u32 *desc;
        struct split_key_result result;
        dma_addr_t dma_addr_in, dma_addr_out;
-       int ret = 0;
+       int ret = -ENOMEM;
 
        desc = kmalloc(CAAM_CMD_SZ * 6 + CAAM_PTR_SZ * 2, GFP_KERNEL | GFP_DMA);
        if (!desc) {
                dev_err(jrdev, "unable to allocate key input memory\n");
-               return -ENOMEM;
+               return ret;
        }
 
-       init_job_desc(desc, 0);
-
        dma_addr_in = dma_map_single(jrdev, (void *)key_in, keylen,
                                     DMA_TO_DEVICE);
        if (dma_mapping_error(jrdev, dma_addr_in)) {
                dev_err(jrdev, "unable to map key input memory\n");
-               kfree(desc);
-               return -ENOMEM;
+               goto out_free;
        }
+
+       dma_addr_out = dma_map_single(jrdev, key_out, split_key_pad_len,
+                                     DMA_FROM_DEVICE);
+       if (dma_mapping_error(jrdev, dma_addr_out)) {
+               dev_err(jrdev, "unable to map key output memory\n");
+               goto out_unmap_in;
+       }
+
+       init_job_desc(desc, 0);
        append_key(desc, dma_addr_in, keylen, CLASS_2 | KEY_DEST_CLASS_REG);
 
        /* Sets MDHA up into an HMAC-INIT */
@@ -81,13 +87,6 @@ int gen_split_key(struct device *jrdev, u8 *key_out, int split_key_len,
         * FIFO_STORE with the explicit split-key content store
         * (0x26 output type)
         */
-       dma_addr_out = dma_map_single(jrdev, key_out, split_key_pad_len,
-                                     DMA_FROM_DEVICE);
-       if (dma_mapping_error(jrdev, dma_addr_out)) {
-               dev_err(jrdev, "unable to map key output memory\n");
-               kfree(desc);
-               return -ENOMEM;
-       }
        append_fifo_store(desc, dma_addr_out, split_key_len,
                          LDST_CLASS_2_CCB | FIFOST_TYPE_SPLIT_KEK);
 
@@ -115,10 +114,10 @@ int gen_split_key(struct device *jrdev, u8 *key_out, int split_key_len,
 
        dma_unmap_single(jrdev, dma_addr_out, split_key_pad_len,
                         DMA_FROM_DEVICE);
+out_unmap_in:
        dma_unmap_single(jrdev, dma_addr_in, keylen, DMA_TO_DEVICE);
-
+out_free:
        kfree(desc);
-
        return ret;
 }
 EXPORT_SYMBOL(gen_split_key);
index 9282381..fe7b3f0 100644 (file)
@@ -198,8 +198,7 @@ struct adf_accel_dev {
        struct dentry *debugfs_dir;
        struct list_head list;
        struct module *owner;
-       uint8_t accel_id;
-       uint8_t numa_node;
        struct adf_accel_pci accel_pci_dev;
+       uint8_t accel_id;
 } __packed;
 #endif
index 5f3fa45..9dd2cb7 100644 (file)
@@ -419,9 +419,10 @@ static int adf_init_bank(struct adf_accel_dev *accel_dev,
                WRITE_CSR_RING_BASE(csr_addr, bank_num, i, 0);
                ring = &bank->rings[i];
                if (hw_data->tx_rings_mask & (1 << i)) {
-                       ring->inflights = kzalloc_node(sizeof(atomic_t),
-                                                      GFP_KERNEL,
-                                                      accel_dev->numa_node);
+                       ring->inflights =
+                               kzalloc_node(sizeof(atomic_t),
+                                            GFP_KERNEL,
+                                            dev_to_node(&GET_DEV(accel_dev)));
                        if (!ring->inflights)
                                goto err;
                } else {
@@ -469,13 +470,14 @@ int adf_init_etr_data(struct adf_accel_dev *accel_dev)
        int i, ret;
 
        etr_data = kzalloc_node(sizeof(*etr_data), GFP_KERNEL,
-                               accel_dev->numa_node);
+                               dev_to_node(&GET_DEV(accel_dev)));
        if (!etr_data)
                return -ENOMEM;
 
        num_banks = GET_MAX_BANKS(accel_dev);
        size = num_banks * sizeof(struct adf_etr_bank_data);
-       etr_data->banks = kzalloc_node(size, GFP_KERNEL, accel_dev->numa_node);
+       etr_data->banks = kzalloc_node(size, GFP_KERNEL,
+                                      dev_to_node(&GET_DEV(accel_dev)));
        if (!etr_data->banks) {
                ret = -ENOMEM;
                goto err_bank;
index f2e2f15..9e9619c 100644 (file)
@@ -596,7 +596,8 @@ static int qat_alg_sgl_to_bufl(struct qat_crypto_instance *inst,
        if (unlikely(!n))
                return -EINVAL;
 
-       bufl = kmalloc_node(sz, GFP_ATOMIC, inst->accel_dev->numa_node);
+       bufl = kmalloc_node(sz, GFP_ATOMIC,
+                           dev_to_node(&GET_DEV(inst->accel_dev)));
        if (unlikely(!bufl))
                return -ENOMEM;
 
@@ -605,6 +606,8 @@ static int qat_alg_sgl_to_bufl(struct qat_crypto_instance *inst,
                goto err;
 
        for_each_sg(assoc, sg, assoc_n, i) {
+               if (!sg->length)
+                       continue;
                bufl->bufers[bufs].addr = dma_map_single(dev,
                                                         sg_virt(sg),
                                                         sg->length,
@@ -640,7 +643,7 @@ static int qat_alg_sgl_to_bufl(struct qat_crypto_instance *inst,
                struct qat_alg_buf *bufers;
 
                buflout = kmalloc_node(sz, GFP_ATOMIC,
-                                      inst->accel_dev->numa_node);
+                                      dev_to_node(&GET_DEV(inst->accel_dev)));
                if (unlikely(!buflout))
                        goto err;
                bloutp = dma_map_single(dev, buflout, sz, DMA_TO_DEVICE);
index 0d59bcb..828f2a6 100644 (file)
@@ -109,12 +109,14 @@ struct qat_crypto_instance *qat_crypto_get_instance_node(int node)
 
        list_for_each(itr, adf_devmgr_get_head()) {
                accel_dev = list_entry(itr, struct adf_accel_dev, list);
-               if (accel_dev->numa_node == node && adf_dev_started(accel_dev))
+               if ((node == dev_to_node(&GET_DEV(accel_dev)) ||
+                       dev_to_node(&GET_DEV(accel_dev)) < 0)
+                               && adf_dev_started(accel_dev))
                        break;
                accel_dev = NULL;
        }
        if (!accel_dev) {
-               pr_err("QAT: Could not find device on give node\n");
+               pr_err("QAT: Could not find device on node %d\n", node);
                accel_dev = adf_devmgr_get_first();
        }
        if (!accel_dev || !adf_dev_started(accel_dev))
@@ -164,7 +166,7 @@ static int qat_crypto_create_instances(struct adf_accel_dev *accel_dev)
 
        for (i = 0; i < num_inst; i++) {
                inst = kzalloc_node(sizeof(*inst), GFP_KERNEL,
-                                   accel_dev->numa_node);
+                                   dev_to_node(&GET_DEV(accel_dev)));
                if (!inst)
                        goto err;
 
index 978d6c5..53c491b 100644 (file)
@@ -108,7 +108,7 @@ int adf_init_admin_comms(struct adf_accel_dev *accel_dev)
        uint64_t reg_val;
 
        admin = kzalloc_node(sizeof(*accel_dev->admin), GFP_KERNEL,
-                            accel_dev->numa_node);
+                            dev_to_node(&GET_DEV(accel_dev)));
        if (!admin)
                return -ENOMEM;
        admin->virt_addr = dma_zalloc_coherent(&GET_DEV(accel_dev), PAGE_SIZE,
index 0d0435a..948f66b 100644 (file)
@@ -119,21 +119,6 @@ static void adf_cleanup_accel(struct adf_accel_dev *accel_dev)
        kfree(accel_dev);
 }
 
-static uint8_t adf_get_dev_node_id(struct pci_dev *pdev)
-{
-       unsigned int bus_per_cpu = 0;
-       struct cpuinfo_x86 *c = &cpu_data(num_online_cpus() - 1);
-
-       if (!c->phys_proc_id)
-               return 0;
-
-       bus_per_cpu = 256 / (c->phys_proc_id + 1);
-
-       if (bus_per_cpu != 0)
-               return pdev->bus->number / bus_per_cpu;
-       return 0;
-}
-
 static int qat_dev_start(struct adf_accel_dev *accel_dev)
 {
        int cpus = num_online_cpus();
@@ -235,7 +220,6 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        void __iomem *pmisc_bar_addr = NULL;
        char name[ADF_DEVICE_NAME_LENGTH];
        unsigned int i, bar_nr;
-       uint8_t node;
        int ret;
 
        switch (ent->device) {
@@ -246,12 +230,19 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                return -ENODEV;
        }
 
-       node = adf_get_dev_node_id(pdev);
-       accel_dev = kzalloc_node(sizeof(*accel_dev), GFP_KERNEL, node);
+       if (num_possible_nodes() > 1 && dev_to_node(&pdev->dev) < 0) {
+               /* If the accelerator is connected to a node with no memory
+                * there is no point in using the accelerator since the remote
+                * memory transaction will be very slow. */
+               dev_err(&pdev->dev, "Invalid NUMA configuration.\n");
+               return -EINVAL;
+       }
+
+       accel_dev = kzalloc_node(sizeof(*accel_dev), GFP_KERNEL,
+                                dev_to_node(&pdev->dev));
        if (!accel_dev)
                return -ENOMEM;
 
-       accel_dev->numa_node = node;
        INIT_LIST_HEAD(&accel_dev->crypto_list);
 
        /* Add accel device to accel table.
@@ -264,7 +255,8 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        accel_dev->owner = THIS_MODULE;
        /* Allocate and configure device configuration structure */
-       hw_data = kzalloc_node(sizeof(*hw_data), GFP_KERNEL, node);
+       hw_data = kzalloc_node(sizeof(*hw_data), GFP_KERNEL,
+                              dev_to_node(&pdev->dev));
        if (!hw_data) {
                ret = -ENOMEM;
                goto out_err;
index 67ec61e..d96ee21 100644 (file)
@@ -168,7 +168,7 @@ static int adf_isr_alloc_msix_entry_table(struct adf_accel_dev *accel_dev)
        uint32_t msix_num_entries = hw_data->num_banks + 1;
 
        entries = kzalloc_node(msix_num_entries * sizeof(*entries),
-                              GFP_KERNEL, accel_dev->numa_node);
+                              GFP_KERNEL, dev_to_node(&GET_DEV(accel_dev)));
        if (!entries)
                return -ENOMEM;
 
index 123f578..4cfaaa5 100644 (file)
@@ -1107,52 +1107,14 @@ bool edma_filter_fn(struct dma_chan *chan, void *param)
 }
 EXPORT_SYMBOL(edma_filter_fn);
 
-static struct platform_device *pdev0, *pdev1;
-
-static const struct platform_device_info edma_dev_info0 = {
-       .name = "edma-dma-engine",
-       .id = 0,
-       .dma_mask = DMA_BIT_MASK(32),
-};
-
-static const struct platform_device_info edma_dev_info1 = {
-       .name = "edma-dma-engine",
-       .id = 1,
-       .dma_mask = DMA_BIT_MASK(32),
-};
-
 static int edma_init(void)
 {
-       int ret = platform_driver_register(&edma_driver);
-
-       if (ret == 0) {
-               pdev0 = platform_device_register_full(&edma_dev_info0);
-               if (IS_ERR(pdev0)) {
-                       platform_driver_unregister(&edma_driver);
-                       ret = PTR_ERR(pdev0);
-                       goto out;
-               }
-       }
-
-       if (!of_have_populated_dt() && EDMA_CTLRS == 2) {
-               pdev1 = platform_device_register_full(&edma_dev_info1);
-               if (IS_ERR(pdev1)) {
-                       platform_driver_unregister(&edma_driver);
-                       platform_device_unregister(pdev0);
-                       ret = PTR_ERR(pdev1);
-               }
-       }
-
-out:
-       return ret;
+       return platform_driver_register(&edma_driver);
 }
 subsys_initcall(edma_init);
 
 static void __exit edma_exit(void)
 {
-       platform_device_unregister(pdev0);
-       if (pdev1)
-               platform_device_unregister(pdev1);
        platform_driver_unregister(&edma_driver);
 }
 module_exit(edma_exit);
index 4839bfa..19a9974 100644 (file)
@@ -271,7 +271,7 @@ struct pl330_config {
 #define DMAC_MODE_NS   (1 << 0)
        unsigned int    mode;
        unsigned int    data_bus_width:10; /* In number of bits */
-       unsigned int    data_buf_dep:10;
+       unsigned int    data_buf_dep:11;
        unsigned int    num_chan:4;
        unsigned int    num_peri:6;
        u32             peri_ns;
@@ -2336,7 +2336,7 @@ static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len)
        int burst_len;
 
        burst_len = pl330->pcfg.data_bus_width / 8;
-       burst_len *= pl330->pcfg.data_buf_dep;
+       burst_len *= pl330->pcfg.data_buf_dep / pl330->pcfg.num_chan;
        burst_len >>= desc->rqcfg.brst_size;
 
        /* src/dst_burst_len can't be more than 16 */
@@ -2459,16 +2459,25 @@ pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst,
        /* Select max possible burst size */
        burst = pl330->pcfg.data_bus_width / 8;
 
-       while (burst > 1) {
-               if (!(len % burst))
-                       break;
+       /*
+        * Make sure we use a burst size that aligns with all the memcpy
+        * parameters because our DMA programming algorithm doesn't cope with
+        * transfers which straddle an entry in the DMA device's MFIFO.
+        */
+       while ((src | dst | len) & (burst - 1))
                burst /= 2;
-       }
 
        desc->rqcfg.brst_size = 0;
        while (burst != (1 << desc->rqcfg.brst_size))
                desc->rqcfg.brst_size++;
 
+       /*
+        * If burst size is smaller than bus width then make sure we only
+        * transfer one at a time to avoid a burst stradling an MFIFO entry.
+        */
+       if (desc->rqcfg.brst_size * 8 < pl330->pcfg.data_bus_width)
+               desc->rqcfg.brst_len = 1;
+
        desc->rqcfg.brst_len = get_burst_len(desc, len);
 
        desc->txd.flags = flags;
@@ -2732,7 +2741,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
 
 
        dev_info(&adev->dev,
-               "Loaded driver for PL330 DMAC-%d\n", adev->periphid);
+               "Loaded driver for PL330 DMAC-%x\n", adev->periphid);
        dev_info(&adev->dev,
                "\tDBUFF-%ux%ubytes Num_Chans-%u Num_Peri-%u Num_Events-%u\n",
                pcfg->data_buf_dep, pcfg->data_bus_width / 8, pcfg->num_chan,
index 3aa10b3..91292f5 100644 (file)
@@ -230,30 +230,25 @@ static inline void sun6i_dma_dump_chan_regs(struct sun6i_dma_dev *sdev,
                readl(pchan->base + DMA_CHAN_CUR_PARA));
 }
 
-static inline int convert_burst(u32 maxburst, u8 *burst)
+static inline s8 convert_burst(u32 maxburst)
 {
        switch (maxburst) {
        case 1:
-               *burst = 0;
-               break;
+               return 0;
        case 8:
-               *burst = 2;
-               break;
+               return 2;
        default:
                return -EINVAL;
        }
-
-       return 0;
 }
 
-static inline int convert_buswidth(enum dma_slave_buswidth addr_width, u8 *width)
+static inline s8 convert_buswidth(enum dma_slave_buswidth addr_width)
 {
        if ((addr_width < DMA_SLAVE_BUSWIDTH_1_BYTE) ||
            (addr_width > DMA_SLAVE_BUSWIDTH_4_BYTES))
                return -EINVAL;
 
-       *width = addr_width >> 1;
-       return 0;
+       return addr_width >> 1;
 }
 
 static void *sun6i_dma_lli_add(struct sun6i_dma_lli *prev,
@@ -284,26 +279,25 @@ static inline int sun6i_dma_cfg_lli(struct sun6i_dma_lli *lli,
                                    struct dma_slave_config *config)
 {
        u8 src_width, dst_width, src_burst, dst_burst;
-       int ret;
 
        if (!config)
                return -EINVAL;
 
-       ret = convert_burst(config->src_maxburst, &src_burst);
-       if (ret)
-               return ret;
+       src_burst = convert_burst(config->src_maxburst);
+       if (src_burst)
+               return src_burst;
 
-       ret = convert_burst(config->dst_maxburst, &dst_burst);
-       if (ret)
-               return ret;
+       dst_burst = convert_burst(config->dst_maxburst);
+       if (dst_burst)
+               return dst_burst;
 
-       ret = convert_buswidth(config->src_addr_width, &src_width);
-       if (ret)
-               return ret;
+       src_width = convert_buswidth(config->src_addr_width);
+       if (src_width)
+               return src_width;
 
-       ret = convert_buswidth(config->dst_addr_width, &dst_width);
-       if (ret)
-               return ret;
+       dst_width = convert_buswidth(config->dst_addr_width);
+       if (dst_width)
+               return dst_width;
 
        lli->cfg = DMA_CHAN_CFG_SRC_BURST(src_burst) |
                DMA_CHAN_CFG_SRC_WIDTH(src_width) |
@@ -542,11 +536,10 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy(
 {
        struct sun6i_dma_dev *sdev = to_sun6i_dma_dev(chan->device);
        struct sun6i_vchan *vchan = to_sun6i_vchan(chan);
-       struct dma_slave_config *sconfig = &vchan->cfg;
        struct sun6i_dma_lli *v_lli;
        struct sun6i_desc *txd;
        dma_addr_t p_lli;
-       int ret;
+       s8 burst, width;
 
        dev_dbg(chan2dev(chan),
                "%s; chan: %d, dest: %pad, src: %pad, len: %zu. flags: 0x%08lx\n",
@@ -565,14 +558,21 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy(
                goto err_txd_free;
        }
 
-       ret = sun6i_dma_cfg_lli(v_lli, src, dest, len, sconfig);
-       if (ret)
-               goto err_dma_free;
+       v_lli->src = src;
+       v_lli->dst = dest;
+       v_lli->len = len;
+       v_lli->para = NORMAL_WAIT;
 
+       burst = convert_burst(8);
+       width = convert_buswidth(DMA_SLAVE_BUSWIDTH_4_BYTES);
        v_lli->cfg |= DMA_CHAN_CFG_SRC_DRQ(DRQ_SDRAM) |
                DMA_CHAN_CFG_DST_DRQ(DRQ_SDRAM) |
                DMA_CHAN_CFG_DST_LINEAR_MODE |
-               DMA_CHAN_CFG_SRC_LINEAR_MODE;
+               DMA_CHAN_CFG_SRC_LINEAR_MODE |
+               DMA_CHAN_CFG_SRC_BURST(burst) |
+               DMA_CHAN_CFG_SRC_WIDTH(width) |
+               DMA_CHAN_CFG_DST_BURST(burst) |
+               DMA_CHAN_CFG_DST_WIDTH(width);
 
        sun6i_dma_lli_add(NULL, v_lli, p_lli, txd);
 
@@ -580,8 +580,6 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy(
 
        return vchan_tx_prep(&vchan->vc, &txd->vd, flags);
 
-err_dma_free:
-       dma_pool_free(sdev->pool, v_lli, p_lli);
 err_txd_free:
        kfree(txd);
        return NULL;
@@ -915,6 +913,7 @@ static int sun6i_dma_probe(struct platform_device *pdev)
        sdc->slave.device_prep_dma_memcpy       = sun6i_dma_prep_dma_memcpy;
        sdc->slave.device_control               = sun6i_dma_control;
        sdc->slave.chancnt                      = NR_MAX_VCHANS;
+       sdc->slave.copy_align                   = 4;
 
        sdc->slave.dev = &pdev->dev;
 
index 5d997a3..2a3973a 100644 (file)
@@ -1637,8 +1637,7 @@ static int dispatch_ioctl(struct client *client,
            _IOC_SIZE(cmd) > sizeof(buffer))
                return -ENOTTY;
 
-       if (_IOC_DIR(cmd) == _IOC_READ)
-               memset(&buffer, 0, _IOC_SIZE(cmd));
+       memset(&buffer, 0, sizeof(buffer));
 
        if (_IOC_DIR(cmd) & _IOC_WRITE)
                if (copy_from_user(&buffer, arg, _IOC_SIZE(cmd)))
index c57466e..e5c4c6c 100644 (file)
@@ -495,6 +495,12 @@ static struct component_match *exynos_drm_match_add(struct device *dev)
 
        mutex_lock(&drm_component_lock);
 
+       /* Do not retry to probe if there is no any kms driver regitered. */
+       if (list_empty(&drm_component_list)) {
+               mutex_unlock(&drm_component_lock);
+               return ERR_PTR(-ENODEV);
+       }
+
        list_for_each_entry(cdev, &drm_component_list, list) {
                /*
                 * Add components to master only in case that crtc and
@@ -585,10 +591,21 @@ static int exynos_drm_platform_probe(struct platform_device *pdev)
                goto err_unregister_mixer_drv;
 #endif
 
+       match = exynos_drm_match_add(&pdev->dev);
+       if (IS_ERR(match)) {
+               ret = PTR_ERR(match);
+               goto err_unregister_hdmi_drv;
+       }
+
+       ret = component_master_add_with_match(&pdev->dev, &exynos_drm_ops,
+                                               match);
+       if (ret < 0)
+               goto err_unregister_hdmi_drv;
+
 #ifdef CONFIG_DRM_EXYNOS_G2D
        ret = platform_driver_register(&g2d_driver);
        if (ret < 0)
-               goto err_unregister_hdmi_drv;
+               goto err_del_component_master;
 #endif
 
 #ifdef CONFIG_DRM_EXYNOS_FIMC
@@ -619,23 +636,9 @@ static int exynos_drm_platform_probe(struct platform_device *pdev)
                goto err_unregister_ipp_drv;
 #endif
 
-       match = exynos_drm_match_add(&pdev->dev);
-       if (IS_ERR(match)) {
-               ret = PTR_ERR(match);
-               goto err_unregister_resources;
-       }
-
-       ret = component_master_add_with_match(&pdev->dev, &exynos_drm_ops,
-                                               match);
-       if (ret < 0)
-               goto err_unregister_resources;
-
        return ret;
 
-err_unregister_resources:
-
 #ifdef CONFIG_DRM_EXYNOS_IPP
-       exynos_platform_device_ipp_unregister();
 err_unregister_ipp_drv:
        platform_driver_unregister(&ipp_driver);
 err_unregister_gsc_drv:
@@ -658,9 +661,11 @@ err_unregister_g2d_drv:
 
 #ifdef CONFIG_DRM_EXYNOS_G2D
        platform_driver_unregister(&g2d_driver);
-err_unregister_hdmi_drv:
+err_del_component_master:
 #endif
+       component_master_del(&pdev->dev, &exynos_drm_ops);
 
+err_unregister_hdmi_drv:
 #ifdef CONFIG_DRM_EXYNOS_HDMI
        platform_driver_unregister(&hdmi_driver);
 err_unregister_mixer_drv:
@@ -741,6 +746,18 @@ static int exynos_drm_init(void)
 {
        int ret;
 
+       /*
+        * Register device object only in case of Exynos SoC.
+        *
+        * Below codes resolves temporarily infinite loop issue incurred
+        * by Exynos drm driver when using multi-platform kernel.
+        * So these codes will be replaced with more generic way later.
+        */
+       if (!of_machine_is_compatible("samsung,exynos3") &&
+                       !of_machine_is_compatible("samsung,exynos4") &&
+                       !of_machine_is_compatible("samsung,exynos5"))
+               return -ENODEV;
+
        exynos_drm_pdev = platform_device_register_simple("exynos-drm", -1,
                                                                NULL, 0);
        if (IS_ERR(exynos_drm_pdev))
index df7a77d..6ff8599 100644 (file)
@@ -302,9 +302,12 @@ static void g2d_fini_cmdlist(struct g2d_data *g2d)
        struct exynos_drm_subdrv *subdrv = &g2d->subdrv;
 
        kfree(g2d->cmdlist_node);
-       dma_free_attrs(subdrv->drm_dev->dev, G2D_CMDLIST_POOL_SIZE,
-                       g2d->cmdlist_pool_virt,
-                       g2d->cmdlist_pool, &g2d->cmdlist_dma_attrs);
+
+       if (g2d->cmdlist_pool_virt && g2d->cmdlist_pool) {
+               dma_free_attrs(subdrv->drm_dev->dev, G2D_CMDLIST_POOL_SIZE,
+                               g2d->cmdlist_pool_virt,
+                               g2d->cmdlist_pool, &g2d->cmdlist_dma_attrs);
+       }
 }
 
 static struct g2d_cmdlist_node *g2d_get_cmdlist(struct g2d_data *g2d)
index 1403b01..318ade9 100644 (file)
@@ -1670,15 +1670,17 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
                goto out_regs;
 
        if (drm_core_check_feature(dev, DRIVER_MODESET)) {
-               ret = i915_kick_out_vgacon(dev_priv);
+               /* WARNING: Apparently we must kick fbdev drivers before vgacon,
+                * otherwise the vga fbdev driver falls over. */
+               ret = i915_kick_out_firmware_fb(dev_priv);
                if (ret) {
-                       DRM_ERROR("failed to remove conflicting VGA console\n");
+                       DRM_ERROR("failed to remove conflicting framebuffer drivers\n");
                        goto out_gtt;
                }
 
-               ret = i915_kick_out_firmware_fb(dev_priv);
+               ret = i915_kick_out_vgacon(dev_priv);
                if (ret) {
-                       DRM_ERROR("failed to remove conflicting framebuffer drivers\n");
+                       DRM_ERROR("failed to remove conflicting VGA console\n");
                        goto out_gtt;
                }
        }
index 055d5e7..2318b4c 100644 (file)
@@ -986,6 +986,15 @@ static int i915_pm_freeze(struct device *dev)
        return i915_drm_freeze(drm_dev);
 }
 
+static int i915_pm_freeze_late(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+       struct drm_i915_private *dev_priv = drm_dev->dev_private;
+
+       return intel_suspend_complete(dev_priv);
+}
+
 static int i915_pm_thaw_early(struct device *dev)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
@@ -1570,6 +1579,7 @@ static const struct dev_pm_ops i915_pm_ops = {
        .resume_early = i915_pm_resume_early,
        .resume = i915_pm_resume,
        .freeze = i915_pm_freeze,
+       .freeze_late = i915_pm_freeze_late,
        .thaw_early = i915_pm_thaw_early,
        .thaw = i915_pm_thaw,
        .poweroff = i915_pm_poweroff,
index b672b84..728938f 100644 (file)
@@ -1902,6 +1902,22 @@ static void bdw_setup_private_ppat(struct drm_i915_private *dev_priv)
              GEN8_PPAT(6, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(2)) |
              GEN8_PPAT(7, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(3));
 
+       if (!USES_PPGTT(dev_priv->dev))
+               /* Spec: "For GGTT, there is NO pat_sel[2:0] from the entry,
+                * so RTL will always use the value corresponding to
+                * pat_sel = 000".
+                * So let's disable cache for GGTT to avoid screen corruptions.
+                * MOCS still can be used though.
+                * - System agent ggtt writes (i.e. cpu gtt mmaps) already work
+                * before this patch, i.e. the same uncached + snooping access
+                * like on gen6/7 seems to be in effect.
+                * - So this just fixes blitter/render access. Again it looks
+                * like it's not just uncached access, but uncached + snooping.
+                * So we can still hold onto all our assumptions wrt cpu
+                * clflushing on LLC machines.
+                */
+               pat = GEN8_PPAT(0, GEN8_PPAT_UC);
+
        /* XXX: spec defines this as 2 distinct registers. It's unclear if a 64b
         * write would work. */
        I915_WRITE(GEN8_PRIVATE_PAT, pat);
index 2cefb59..2b1eaa2 100644 (file)
@@ -364,22 +364,9 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
                 * has to also include the unfenced register the GPU uses
                 * whilst executing a fenced command for an untiled object.
                 */
-
-               obj->map_and_fenceable =
-                       !i915_gem_obj_ggtt_bound(obj) ||
-                       (i915_gem_obj_ggtt_offset(obj) +
-                        obj->base.size <= dev_priv->gtt.mappable_end &&
-                        i915_gem_object_fence_ok(obj, args->tiling_mode));
-
-               /* Rebind if we need a change of alignment */
-               if (!obj->map_and_fenceable) {
-                       u32 unfenced_align =
-                               i915_gem_get_gtt_alignment(dev, obj->base.size,
-                                                           args->tiling_mode,
-                                                           false);
-                       if (i915_gem_obj_ggtt_offset(obj) & (unfenced_align - 1))
-                               ret = i915_gem_object_ggtt_unbind(obj);
-               }
+               if (obj->map_and_fenceable &&
+                   !i915_gem_object_fence_ok(obj, args->tiling_mode))
+                       ret = i915_gem_object_ggtt_unbind(obj);
 
                if (ret == 0) {
                        obj->fence_dirty =
index f0a1a56..9cb5c95 100644 (file)
@@ -4325,7 +4325,6 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
                ironlake_fdi_disable(crtc);
 
                ironlake_disable_pch_transcoder(dev_priv, pipe);
-               intel_set_pch_fifo_underrun_reporting(dev, pipe, true);
 
                if (HAS_PCH_CPT(dev)) {
                        /* disable TRANS_DP_CTL */
@@ -4389,7 +4388,6 @@ static void haswell_crtc_disable(struct drm_crtc *crtc)
 
        if (intel_crtc->config.has_pch_encoder) {
                lpt_disable_pch_transcoder(dev_priv);
-               intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, true);
                intel_ddi_fdi_disable(crtc);
        }
 
@@ -9408,6 +9406,10 @@ static bool page_flip_finished(struct intel_crtc *crtc)
        struct drm_device *dev = crtc->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
 
+       if (i915_reset_in_progress(&dev_priv->gpu_error) ||
+           crtc->reset_counter != atomic_read(&dev_priv->gpu_error.reset_counter))
+               return true;
+
        /*
         * The relevant registers doen't exist on pre-ctg.
         * As the flip done interrupt doesn't trigger for mmio
index 5ad45bf..4bcd917 100644 (file)
@@ -4450,6 +4450,7 @@ static void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder)
         * vdd might still be enabled do to the delayed vdd off.
         * Make sure vdd is actually turned off here.
         */
+       cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
        pps_lock(intel_dp);
        edp_panel_vdd_off_sync(intel_dp);
        pps_unlock(intel_dp);
index a6bd142..c0bbf21 100644 (file)
@@ -899,6 +899,17 @@ void intel_lvds_init(struct drm_device *dev)
        int pipe;
        u8 pin;
 
+       /*
+        * Unlock registers and just leave them unlocked. Do this before
+        * checking quirk lists to avoid bogus WARNINGs.
+        */
+       if (HAS_PCH_SPLIT(dev)) {
+               I915_WRITE(PCH_PP_CONTROL,
+                          I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS);
+       } else {
+               I915_WRITE(PP_CONTROL,
+                          I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS);
+       }
        if (!intel_lvds_supported(dev))
                return;
 
@@ -1097,17 +1108,6 @@ out:
        lvds_encoder->a3_power = I915_READ(lvds_encoder->reg) &
                                 LVDS_A3_POWER_MASK;
 
-       /*
-        * Unlock registers and just
-        * leave them unlocked
-        */
-       if (HAS_PCH_SPLIT(dev)) {
-               I915_WRITE(PCH_PP_CONTROL,
-                          I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS);
-       } else {
-               I915_WRITE(PP_CONTROL,
-                          I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS);
-       }
        lvds_connector->lid_notifier.notifier_call = intel_lid_notify;
        if (acpi_lid_notifier_register(&lvds_connector->lid_notifier)) {
                DRM_DEBUG_KMS("lid notifier registration failed\n");
index 0e018cb..41b3be2 100644 (file)
@@ -1098,12 +1098,25 @@ static u32 get_backlight_min_vbt(struct intel_connector *connector)
        struct drm_device *dev = connector->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_panel *panel = &connector->panel;
+       int min;
 
        WARN_ON(panel->backlight.max == 0);
 
+       /*
+        * XXX: If the vbt value is 255, it makes min equal to max, which leads
+        * to problems. There are such machines out there. Either our
+        * interpretation is wrong or the vbt has bogus data. Or both. Safeguard
+        * against this by letting the minimum be at most (arbitrarily chosen)
+        * 25% of the max.
+        */
+       min = clamp_t(int, dev_priv->vbt.backlight.min_brightness, 0, 64);
+       if (min != dev_priv->vbt.backlight.min_brightness) {
+               DRM_DEBUG_KMS("clamping VBT min backlight %d/255 to %d/255\n",
+                             dev_priv->vbt.backlight.min_brightness, min);
+       }
+
        /* vbt value is a coefficient in range [0..255] */
-       return scale(dev_priv->vbt.backlight.min_brightness, 0, 255,
-                    0, panel->backlight.max);
+       return scale(min, 0, 255, 0, panel->backlight.max);
 }
 
 static int bdw_setup_backlight(struct intel_connector *connector)
index c27b614..ad2fd60 100644 (file)
@@ -5469,11 +5469,6 @@ static void gen6_init_clock_gating(struct drm_device *dev)
        I915_WRITE(_3D_CHICKEN,
                   _MASKED_BIT_ENABLE(_3D_CHICKEN_HIZ_PLANE_DISABLE_MSAA_4X_SNB));
 
-       /* WaSetupGtModeTdRowDispatch:snb */
-       if (IS_SNB_GT1(dev))
-               I915_WRITE(GEN6_GT_MODE,
-                          _MASKED_BIT_ENABLE(GEN6_TD_FOUR_ROW_DISPATCH_DISABLE));
-
        /* WaDisable_RenderCache_OperationalFlush:snb */
        I915_WRITE(CACHE_MODE_0, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE));
 
index cd05677..72a40f9 100644 (file)
@@ -218,7 +218,6 @@ nvc0_identify(struct nouveau_device *device)
                device->oclass[NVDEV_ENGINE_BSP    ] = &nvc0_bsp_oclass;
                device->oclass[NVDEV_ENGINE_PPP    ] = &nvc0_ppp_oclass;
                device->oclass[NVDEV_ENGINE_COPY0  ] = &nvc0_copy0_oclass;
-               device->oclass[NVDEV_ENGINE_COPY1  ] = &nvc0_copy1_oclass;
                device->oclass[NVDEV_ENGINE_DISP   ] =  nva3_disp_oclass;
                device->oclass[NVDEV_ENGINE_PERFMON] = &nvc0_perfmon_oclass;
                break;
index 5ae6a43..1931057 100644 (file)
@@ -551,8 +551,8 @@ nv04_fifo_intr(struct nouveau_subdev *subdev)
                        }
 
                        if (status & 0x40000000) {
-                               nouveau_fifo_uevent(&priv->base);
                                nv_wr32(priv, 0x002100, 0x40000000);
+                               nouveau_fifo_uevent(&priv->base);
                                status &= ~0x40000000;
                        }
                }
index 1fe1f8f..074d434 100644 (file)
@@ -740,6 +740,8 @@ nvc0_fifo_intr_engine_unit(struct nvc0_fifo_priv *priv, int engn)
        u32 inte = nv_rd32(priv, 0x002628);
        u32 unkn;
 
+       nv_wr32(priv, 0x0025a8 + (engn * 0x04), intr);
+
        for (unkn = 0; unkn < 8; unkn++) {
                u32 ints = (intr >> (unkn * 0x04)) & inte;
                if (ints & 0x1) {
@@ -751,8 +753,6 @@ nvc0_fifo_intr_engine_unit(struct nvc0_fifo_priv *priv, int engn)
                        nv_mask(priv, 0x002628, ints, 0);
                }
        }
-
-       nv_wr32(priv, 0x0025a8 + (engn * 0x04), intr);
 }
 
 static void
index d2f0fd3..f8734eb 100644 (file)
@@ -952,8 +952,8 @@ nve0_fifo_intr(struct nouveau_subdev *subdev)
        }
 
        if (stat & 0x80000000) {
-               nve0_fifo_intr_engine(priv);
                nv_wr32(priv, 0x002100, 0x80000000);
+               nve0_fifo_intr_engine(priv);
                stat &= ~0x80000000;
        }
 
index a16024a..fde42e4 100644 (file)
@@ -26,6 +26,20 @@ struct gk20a_fb_priv {
        struct nouveau_fb base;
 };
 
+static int
+gk20a_fb_init(struct nouveau_object *object)
+{
+       struct gk20a_fb_priv *priv = (void *)object;
+       int ret;
+
+       ret = nouveau_fb_init(&priv->base);
+       if (ret)
+               return ret;
+
+       nv_mask(priv, 0x100c80, 0x00000001, 0x00000000); /* 128KiB lpg */
+       return 0;
+}
+
 static int
 gk20a_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
             struct nouveau_oclass *oclass, void *data, u32 size,
@@ -48,7 +62,7 @@ gk20a_fb_oclass = &(struct nouveau_fb_impl) {
        .base.ofuncs = &(struct nouveau_ofuncs) {
                .ctor = gk20a_fb_ctor,
                .dtor = _nouveau_fb_dtor,
-               .init = _nouveau_fb_init,
+               .init = gk20a_fb_init,
                .fini = _nouveau_fb_fini,
        },
        .memtype = nvc0_fb_memtype_valid,
index 5723807..62b97c4 100644 (file)
@@ -629,7 +629,6 @@ int nouveau_pmops_suspend(struct device *dev)
 
        pci_save_state(pdev);
        pci_disable_device(pdev);
-       pci_ignore_hotplug(pdev);
        pci_set_power_state(pdev, PCI_D3hot);
        return 0;
 }
@@ -933,6 +932,7 @@ static int nouveau_pmops_runtime_suspend(struct device *dev)
        ret = nouveau_do_suspend(drm_dev, true);
        pci_save_state(pdev);
        pci_disable_device(pdev);
+       pci_ignore_hotplug(pdev);
        pci_set_power_state(pdev, PCI_D3cold);
        drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF;
        return ret;
index 515cd9a..f32a434 100644 (file)
@@ -52,20 +52,24 @@ nouveau_fctx(struct nouveau_fence *fence)
        return container_of(fence->base.lock, struct nouveau_fence_chan, lock);
 }
 
-static void
+static int
 nouveau_fence_signal(struct nouveau_fence *fence)
 {
+       int drop = 0;
+
        fence_signal_locked(&fence->base);
        list_del(&fence->head);
+       rcu_assign_pointer(fence->channel, NULL);
 
        if (test_bit(FENCE_FLAG_USER_BITS, &fence->base.flags)) {
                struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
 
                if (!--fctx->notify_ref)
-                       nvif_notify_put(&fctx->notify);
+                       drop = 1;
        }
 
        fence_put(&fence->base);
+       return drop;
 }
 
 static struct nouveau_fence *
@@ -88,16 +92,23 @@ nouveau_fence_context_del(struct nouveau_fence_chan *fctx)
 {
        struct nouveau_fence *fence;
 
-       nvif_notify_fini(&fctx->notify);
-
        spin_lock_irq(&fctx->lock);
        while (!list_empty(&fctx->pending)) {
                fence = list_entry(fctx->pending.next, typeof(*fence), head);
 
-               nouveau_fence_signal(fence);
-               fence->channel = NULL;
+               if (nouveau_fence_signal(fence))
+                       nvif_notify_put(&fctx->notify);
        }
        spin_unlock_irq(&fctx->lock);
+
+       nvif_notify_fini(&fctx->notify);
+       fctx->dead = 1;
+
+       /*
+        * Ensure that all accesses to fence->channel complete before freeing
+        * the channel.
+        */
+       synchronize_rcu();
 }
 
 static void
@@ -112,21 +123,23 @@ nouveau_fence_context_free(struct nouveau_fence_chan *fctx)
        kref_put(&fctx->fence_ref, nouveau_fence_context_put);
 }
 
-static void
+static int
 nouveau_fence_update(struct nouveau_channel *chan, struct nouveau_fence_chan *fctx)
 {
        struct nouveau_fence *fence;
-
+       int drop = 0;
        u32 seq = fctx->read(chan);
 
        while (!list_empty(&fctx->pending)) {
                fence = list_entry(fctx->pending.next, typeof(*fence), head);
 
                if ((int)(seq - fence->base.seqno) < 0)
-                       return;
+                       break;
 
-               nouveau_fence_signal(fence);
+               drop |= nouveau_fence_signal(fence);
        }
+
+       return drop;
 }
 
 static int
@@ -135,18 +148,21 @@ nouveau_fence_wait_uevent_handler(struct nvif_notify *notify)
        struct nouveau_fence_chan *fctx =
                container_of(notify, typeof(*fctx), notify);
        unsigned long flags;
+       int ret = NVIF_NOTIFY_KEEP;
 
        spin_lock_irqsave(&fctx->lock, flags);
        if (!list_empty(&fctx->pending)) {
                struct nouveau_fence *fence;
+               struct nouveau_channel *chan;
 
                fence = list_entry(fctx->pending.next, typeof(*fence), head);
-               nouveau_fence_update(fence->channel, fctx);
+               chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock));
+               if (nouveau_fence_update(fence->channel, fctx))
+                       ret = NVIF_NOTIFY_DROP;
        }
        spin_unlock_irqrestore(&fctx->lock, flags);
 
-       /* Always return keep here. NVIF refcount is handled with nouveau_fence_update */
-       return NVIF_NOTIFY_KEEP;
+       return ret;
 }
 
 void
@@ -262,7 +278,10 @@ nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan)
        if (!ret) {
                fence_get(&fence->base);
                spin_lock_irq(&fctx->lock);
-               nouveau_fence_update(chan, fctx);
+
+               if (nouveau_fence_update(chan, fctx))
+                       nvif_notify_put(&fctx->notify);
+
                list_add_tail(&fence->head, &fctx->pending);
                spin_unlock_irq(&fctx->lock);
        }
@@ -276,13 +295,16 @@ nouveau_fence_done(struct nouveau_fence *fence)
        if (fence->base.ops == &nouveau_fence_ops_legacy ||
            fence->base.ops == &nouveau_fence_ops_uevent) {
                struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
+               struct nouveau_channel *chan;
                unsigned long flags;
 
                if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags))
                        return true;
 
                spin_lock_irqsave(&fctx->lock, flags);
-               nouveau_fence_update(fence->channel, fctx);
+               chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock));
+               if (chan && nouveau_fence_update(chan, fctx))
+                       nvif_notify_put(&fctx->notify);
                spin_unlock_irqrestore(&fctx->lock, flags);
        }
        return fence_is_signaled(&fence->base);
@@ -387,12 +409,18 @@ nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan, bool e
 
        if (fence && (!exclusive || !fobj || !fobj->shared_count)) {
                struct nouveau_channel *prev = NULL;
+               bool must_wait = true;
 
                f = nouveau_local_fence(fence, chan->drm);
-               if (f)
-                       prev = f->channel;
+               if (f) {
+                       rcu_read_lock();
+                       prev = rcu_dereference(f->channel);
+                       if (prev && (prev == chan || fctx->sync(f, prev, chan) == 0))
+                               must_wait = false;
+                       rcu_read_unlock();
+               }
 
-               if (!prev || (prev != chan && (ret = fctx->sync(f, prev, chan))))
+               if (must_wait)
                        ret = fence_wait(fence, intr);
 
                return ret;
@@ -403,19 +431,22 @@ nouveau_fence_sync(struct nouveau_bo *nvbo, struct nouveau_channel *chan, bool e
 
        for (i = 0; i < fobj->shared_count && !ret; ++i) {
                struct nouveau_channel *prev = NULL;
+               bool must_wait = true;
 
                fence = rcu_dereference_protected(fobj->shared[i],
                                                reservation_object_held(resv));
 
                f = nouveau_local_fence(fence, chan->drm);
-               if (f)
-                       prev = f->channel;
+               if (f) {
+                       rcu_read_lock();
+                       prev = rcu_dereference(f->channel);
+                       if (prev && (prev == chan || fctx->sync(f, prev, chan) == 0))
+                               must_wait = false;
+                       rcu_read_unlock();
+               }
 
-               if (!prev || (prev != chan && (ret = fctx->sync(f, prev, chan))))
+               if (must_wait)
                        ret = fence_wait(fence, intr);
-
-               if (ret)
-                       break;
        }
 
        return ret;
@@ -463,7 +494,7 @@ static const char *nouveau_fence_get_timeline_name(struct fence *f)
        struct nouveau_fence *fence = from_fence(f);
        struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
 
-       return fence->channel ? fctx->name : "dead channel";
+       return !fctx->dead ? fctx->name : "dead channel";
 }
 
 /*
@@ -476,9 +507,16 @@ static bool nouveau_fence_is_signaled(struct fence *f)
 {
        struct nouveau_fence *fence = from_fence(f);
        struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
-       struct nouveau_channel *chan = fence->channel;
+       struct nouveau_channel *chan;
+       bool ret = false;
+
+       rcu_read_lock();
+       chan = rcu_dereference(fence->channel);
+       if (chan)
+               ret = (int)(fctx->read(chan) - fence->base.seqno) >= 0;
+       rcu_read_unlock();
 
-       return (int)(fctx->read(chan) - fence->base.seqno) >= 0;
+       return ret;
 }
 
 static bool nouveau_fence_no_signaling(struct fence *f)
index 943b0b1..96e461c 100644 (file)
@@ -14,7 +14,7 @@ struct nouveau_fence {
 
        bool sysmem;
 
-       struct nouveau_channel *channel;
+       struct nouveau_channel __rcu *channel;
        unsigned long timeout;
 };
 
@@ -47,7 +47,7 @@ struct nouveau_fence_chan {
        char name[32];
 
        struct nvif_notify notify;
-       int notify_ref;
+       int notify_ref, dead;
 };
 
 struct nouveau_fence_priv {
index ae873d1..eb8b367 100644 (file)
@@ -790,6 +790,22 @@ nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update)
        return 0;
 }
 
+static int
+nv50_crtc_set_raster_vblank_dmi(struct nouveau_crtc *nv_crtc, u32 usec)
+{
+       struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev);
+       u32 *push;
+
+       push = evo_wait(mast, 8);
+       if (!push)
+               return -ENOMEM;
+
+       evo_mthd(push, 0x0828 + (nv_crtc->index * 0x400), 1);
+       evo_data(push, usec);
+       evo_kick(push, mast);
+       return 0;
+}
+
 static int
 nv50_crtc_set_color_vibrance(struct nouveau_crtc *nv_crtc, bool update)
 {
@@ -1104,14 +1120,14 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode,
                        evo_mthd(push, 0x0804 + (nv_crtc->index * 0x400), 2);
                        evo_data(push, 0x00800000 | mode->clock);
                        evo_data(push, (ilace == 2) ? 2 : 0);
-                       evo_mthd(push, 0x0810 + (nv_crtc->index * 0x400), 8);
+                       evo_mthd(push, 0x0810 + (nv_crtc->index * 0x400), 6);
                        evo_data(push, 0x00000000);
                        evo_data(push, (vactive << 16) | hactive);
                        evo_data(push, ( vsynce << 16) | hsynce);
                        evo_data(push, (vblanke << 16) | hblanke);
                        evo_data(push, (vblanks << 16) | hblanks);
                        evo_data(push, (vblan2e << 16) | vblan2s);
-                       evo_data(push, vblankus);
+                       evo_mthd(push, 0x082c + (nv_crtc->index * 0x400), 1);
                        evo_data(push, 0x00000000);
                        evo_mthd(push, 0x0900 + (nv_crtc->index * 0x400), 2);
                        evo_data(push, 0x00000311);
@@ -1141,6 +1157,11 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode,
        nv_connector = nouveau_crtc_connector_get(nv_crtc);
        nv50_crtc_set_dither(nv_crtc, false);
        nv50_crtc_set_scale(nv_crtc, false);
+
+       /* G94 only accepts this after setting scale */
+       if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA)
+               nv50_crtc_set_raster_vblank_dmi(nv_crtc, vblankus);
+
        nv50_crtc_set_color_vibrance(nv_crtc, false);
        nv50_crtc_set_image(nv_crtc, crtc->primary->fb, x, y, false);
        return 0;
index 15da7ef..ec1593a 100644 (file)
@@ -1217,7 +1217,7 @@ free:
        return ret;
 }
 
-int atom_execute_table(struct atom_context *ctx, int index, uint32_t * params)
+int atom_execute_table_scratch_unlocked(struct atom_context *ctx, int index, uint32_t * params)
 {
        int r;
 
@@ -1238,6 +1238,15 @@ int atom_execute_table(struct atom_context *ctx, int index, uint32_t * params)
        return r;
 }
 
+int atom_execute_table(struct atom_context *ctx, int index, uint32_t * params)
+{
+       int r;
+       mutex_lock(&ctx->scratch_mutex);
+       r = atom_execute_table_scratch_unlocked(ctx, index, params);
+       mutex_unlock(&ctx->scratch_mutex);
+       return r;
+}
+
 static int atom_iio_len[] = { 1, 2, 3, 3, 3, 3, 4, 4, 4, 3 };
 
 static void atom_index_iio(struct atom_context *ctx, int base)
index feba6b8..6d014dd 100644 (file)
@@ -125,6 +125,7 @@ struct card_info {
 struct atom_context {
        struct card_info *card;
        struct mutex mutex;
+       struct mutex scratch_mutex;
        void *bios;
        uint32_t cmd_table, data_table;
        uint16_t *iio;
@@ -145,6 +146,7 @@ extern int atom_debug;
 
 struct atom_context *atom_parse(struct card_info *, void *);
 int atom_execute_table(struct atom_context *, int, uint32_t *);
+int atom_execute_table_scratch_unlocked(struct atom_context *, int, uint32_t *);
 int atom_asic_init(struct atom_context *);
 void atom_destroy(struct atom_context *);
 bool atom_parse_data_header(struct atom_context *ctx, int index, uint16_t *size,
index 95d5d4a..11ba9d2 100644 (file)
@@ -100,6 +100,7 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan,
        memset(&args, 0, sizeof(args));
 
        mutex_lock(&chan->mutex);
+       mutex_lock(&rdev->mode_info.atom_context->scratch_mutex);
 
        base = (unsigned char *)(rdev->mode_info.atom_context->scratch + 1);
 
@@ -113,7 +114,7 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan,
        if (ASIC_IS_DCE4(rdev))
                args.v2.ucHPD_ID = chan->rec.hpd;
 
-       atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+       atom_execute_table_scratch_unlocked(rdev->mode_info.atom_context, index, (uint32_t *)&args);
 
        *ack = args.v1.ucReplyStatus;
 
@@ -147,6 +148,7 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan,
 
        r = recv_bytes;
 done:
+       mutex_unlock(&rdev->mode_info.atom_context->scratch_mutex);
        mutex_unlock(&chan->mutex);
 
        return r;
index 9c570fb..4157780 100644 (file)
@@ -48,6 +48,7 @@ static int radeon_process_i2c_ch(struct radeon_i2c_chan *chan,
        memset(&args, 0, sizeof(args));
 
        mutex_lock(&chan->mutex);
+       mutex_lock(&rdev->mode_info.atom_context->scratch_mutex);
 
        base = (unsigned char *)rdev->mode_info.atom_context->scratch;
 
@@ -82,7 +83,7 @@ static int radeon_process_i2c_ch(struct radeon_i2c_chan *chan,
        args.ucSlaveAddr = slave_addr << 1;
        args.ucLineNumber = chan->rec.i2c_id;
 
-       atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+       atom_execute_table_scratch_unlocked(rdev->mode_info.atom_context, index, (uint32_t *)&args);
 
        /* error */
        if (args.ucStatus != HW_ASSISTED_I2C_STATUS_SUCCESS) {
@@ -95,6 +96,7 @@ static int radeon_process_i2c_ch(struct radeon_i2c_chan *chan,
                radeon_atom_copy_swap(buf, base, num, false);
 
 done:
+       mutex_unlock(&rdev->mode_info.atom_context->scratch_mutex);
        mutex_unlock(&chan->mutex);
 
        return r;
index 377afa5..89c01fa 100644 (file)
@@ -4313,8 +4313,8 @@ static int cik_cp_gfx_start(struct radeon_device *rdev)
        /* init the CE partitions.  CE only used for gfx on CIK */
        radeon_ring_write(ring, PACKET3(PACKET3_SET_BASE, 2));
        radeon_ring_write(ring, PACKET3_BASE_INDEX(CE_PARTITION_BASE));
-       radeon_ring_write(ring, 0xc000);
-       radeon_ring_write(ring, 0xc000);
+       radeon_ring_write(ring, 0x8000);
+       radeon_ring_write(ring, 0x8000);
 
        /* setup clear context state */
        radeon_ring_write(ring, PACKET3(PACKET3_PREAMBLE_CNTL, 0));
@@ -9447,6 +9447,9 @@ void dce8_bandwidth_update(struct radeon_device *rdev)
        u32 num_heads = 0, lb_size;
        int i;
 
+       if (!rdev->mode_info.mode_config_initialized)
+               return;
+
        radeon_update_display_priority(rdev);
 
        for (i = 0; i < rdev->num_crtc; i++) {
index 4e8432d..d748963 100644 (file)
@@ -667,17 +667,20 @@ int cik_sdma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
 {
        struct radeon_ib ib;
        unsigned i;
+       unsigned index;
        int r;
-       void __iomem *ptr = (void *)rdev->vram_scratch.ptr;
        u32 tmp = 0;
+       u64 gpu_addr;
 
-       if (!ptr) {
-               DRM_ERROR("invalid vram scratch pointer\n");
-               return -EINVAL;
-       }
+       if (ring->idx == R600_RING_TYPE_DMA_INDEX)
+               index = R600_WB_DMA_RING_TEST_OFFSET;
+       else
+               index = CAYMAN_WB_DMA1_RING_TEST_OFFSET;
+
+       gpu_addr = rdev->wb.gpu_addr + index;
 
        tmp = 0xCAFEDEAD;
-       writel(tmp, ptr);
+       rdev->wb.wb[index/4] = cpu_to_le32(tmp);
 
        r = radeon_ib_get(rdev, ring->idx, &ib, NULL, 256);
        if (r) {
@@ -686,8 +689,8 @@ int cik_sdma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
        }
 
        ib.ptr[0] = SDMA_PACKET(SDMA_OPCODE_WRITE, SDMA_WRITE_SUB_OPCODE_LINEAR, 0);
-       ib.ptr[1] = rdev->vram_scratch.gpu_addr & 0xfffffffc;
-       ib.ptr[2] = upper_32_bits(rdev->vram_scratch.gpu_addr);
+       ib.ptr[1] = lower_32_bits(gpu_addr);
+       ib.ptr[2] = upper_32_bits(gpu_addr);
        ib.ptr[3] = 1;
        ib.ptr[4] = 0xDEADBEEF;
        ib.length_dw = 5;
@@ -704,7 +707,7 @@ int cik_sdma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
                return r;
        }
        for (i = 0; i < rdev->usec_timeout; i++) {
-               tmp = readl(ptr);
+               tmp = le32_to_cpu(rdev->wb.wb[index/4]);
                if (tmp == 0xDEADBEEF)
                        break;
                DRM_UDELAY(1);
index f37d39d..85995b4 100644 (file)
@@ -2345,6 +2345,9 @@ void evergreen_bandwidth_update(struct radeon_device *rdev)
        u32 num_heads = 0, lb_size;
        int i;
 
+       if (!rdev->mode_info.mode_config_initialized)
+               return;
+
        radeon_update_display_priority(rdev);
 
        for (i = 0; i < rdev->num_crtc; i++) {
@@ -2552,6 +2555,7 @@ void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *sav
                                        WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 1);
                                        tmp |= EVERGREEN_CRTC_BLANK_DATA_EN;
                                        WREG32(EVERGREEN_CRTC_BLANK_CONTROL + crtc_offsets[i], tmp);
+                                       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 0);
                                }
                        } else {
                                tmp = RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i]);
index 10f8be0..b53b31a 100644 (file)
@@ -3207,6 +3207,9 @@ void r100_bandwidth_update(struct radeon_device *rdev)
        uint32_t pixel_bytes1 = 0;
        uint32_t pixel_bytes2 = 0;
 
+       if (!rdev->mode_info.mode_config_initialized)
+               return;
+
        radeon_update_display_priority(rdev);
 
        if (rdev->mode_info.crtcs[0]->base.enabled) {
index aabc343..cf0df45 100644 (file)
@@ -338,17 +338,17 @@ int r600_dma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
 {
        struct radeon_ib ib;
        unsigned i;
+       unsigned index;
        int r;
-       void __iomem *ptr = (void *)rdev->vram_scratch.ptr;
        u32 tmp = 0;
+       u64 gpu_addr;
 
-       if (!ptr) {
-               DRM_ERROR("invalid vram scratch pointer\n");
-               return -EINVAL;
-       }
+       if (ring->idx == R600_RING_TYPE_DMA_INDEX)
+               index = R600_WB_DMA_RING_TEST_OFFSET;
+       else
+               index = CAYMAN_WB_DMA1_RING_TEST_OFFSET;
 
-       tmp = 0xCAFEDEAD;
-       writel(tmp, ptr);
+       gpu_addr = rdev->wb.gpu_addr + index;
 
        r = radeon_ib_get(rdev, ring->idx, &ib, NULL, 256);
        if (r) {
@@ -357,8 +357,8 @@ int r600_dma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
        }
 
        ib.ptr[0] = DMA_PACKET(DMA_PACKET_WRITE, 0, 0, 1);
-       ib.ptr[1] = rdev->vram_scratch.gpu_addr & 0xfffffffc;
-       ib.ptr[2] = upper_32_bits(rdev->vram_scratch.gpu_addr) & 0xff;
+       ib.ptr[1] = lower_32_bits(gpu_addr);
+       ib.ptr[2] = upper_32_bits(gpu_addr) & 0xff;
        ib.ptr[3] = 0xDEADBEEF;
        ib.length_dw = 4;
 
@@ -374,7 +374,7 @@ int r600_dma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
                return r;
        }
        for (i = 0; i < rdev->usec_timeout; i++) {
-               tmp = readl(ptr);
+               tmp = le32_to_cpu(rdev->wb.wb[index/4]);
                if (tmp == 0xDEADBEEF)
                        break;
                DRM_UDELAY(1);
index f6309bd..b5c73df 100644 (file)
@@ -1256,7 +1256,7 @@ int r600_parse_extended_power_table(struct radeon_device *rdev)
                                        (mode_info->atom_context->bios + data_offset +
                                         le16_to_cpu(ext_hdr->usPowerTuneTableOffset));
                                rdev->pm.dpm.dyn_state.cac_tdp_table->maximum_power_delivery_limit =
-                                       ppt->usMaximumPowerDeliveryLimit;
+                                       le16_to_cpu(ppt->usMaximumPowerDeliveryLimit);
                                pt = &ppt->power_tune_table;
                        } else {
                                ATOM_PPLIB_POWERTUNE_Table *ppt = (ATOM_PPLIB_POWERTUNE_Table *)
index 300c4b3..26baa9c 100644 (file)
@@ -322,6 +322,12 @@ static void radeon_connector_get_edid(struct drm_connector *connector)
        }
 
        if (!radeon_connector->edid) {
+               /* don't fetch the edid from the vbios if ddc fails and runpm is
+                * enabled so we report disconnected.
+                */
+               if ((rdev->flags & RADEON_IS_PX) && (radeon_runtime_pm != 0))
+                       return;
+
                if (rdev->is_atom_bios) {
                        /* some laptops provide a hardcoded edid in rom for LCDs */
                        if (((connector->connector_type == DRM_MODE_CONNECTOR_LVDS) ||
@@ -826,6 +832,8 @@ static int radeon_lvds_mode_valid(struct drm_connector *connector,
 static enum drm_connector_status
 radeon_lvds_detect(struct drm_connector *connector, bool force)
 {
+       struct drm_device *dev = connector->dev;
+       struct radeon_device *rdev = dev->dev_private;
        struct radeon_connector *radeon_connector = to_radeon_connector(connector);
        struct drm_encoder *encoder = radeon_best_single_encoder(connector);
        enum drm_connector_status ret = connector_status_disconnected;
@@ -842,7 +850,11 @@ radeon_lvds_detect(struct drm_connector *connector, bool force)
                /* check if panel is valid */
                if (native_mode->hdisplay >= 320 && native_mode->vdisplay >= 240)
                        ret = connector_status_connected;
-
+               /* don't fetch the edid from the vbios if ddc fails and runpm is
+                * enabled so we report disconnected.
+                */
+               if ((rdev->flags & RADEON_IS_PX) && (radeon_runtime_pm != 0))
+                       ret = connector_status_disconnected;
        }
 
        /* check for edid as well */
@@ -1589,6 +1601,11 @@ radeon_dp_detect(struct drm_connector *connector, bool force)
                        /* check if panel is valid */
                        if (native_mode->hdisplay >= 320 && native_mode->vdisplay >= 240)
                                ret = connector_status_connected;
+                       /* don't fetch the edid from the vbios if ddc fails and runpm is
+                        * enabled so we report disconnected.
+                        */
+                       if ((rdev->flags & RADEON_IS_PX) && (radeon_runtime_pm != 0))
+                               ret = connector_status_disconnected;
                }
                /* eDP is always DP */
                radeon_dig_connector->dp_sink_type = CONNECTOR_OBJECT_ID_DISPLAYPORT;
index a3e7aed..6f377de 100644 (file)
@@ -251,22 +251,19 @@ static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority
 
 static int radeon_cs_sync_rings(struct radeon_cs_parser *p)
 {
-       int i, r = 0;
+       struct radeon_cs_reloc *reloc;
+       int r;
 
-       for (i = 0; i < p->nrelocs; i++) {
+       list_for_each_entry(reloc, &p->validated, tv.head) {
                struct reservation_object *resv;
 
-               if (!p->relocs[i].robj)
-                       continue;
-
-               resv = p->relocs[i].robj->tbo.resv;
+               resv = reloc->robj->tbo.resv;
                r = radeon_semaphore_sync_resv(p->rdev, p->ib.semaphore, resv,
-                                              p->relocs[i].tv.shared);
-
+                                              reloc->tv.shared);
                if (r)
-                       break;
+                       return r;
        }
-       return r;
+       return 0;
 }
 
 /* XXX: note that this is called from the legacy UMS CS ioctl as well */
index ea26769..995a8b1 100644 (file)
@@ -952,6 +952,7 @@ int radeon_atombios_init(struct radeon_device *rdev)
        }
 
        mutex_init(&rdev->mode_info.atom_context->mutex);
+       mutex_init(&rdev->mode_info.atom_context->scratch_mutex);
        radeon_atom_initialize_bios_scratch_regs(rdev->ddev);
        atom_allocate_fb_scratch(rdev->mode_info.atom_context);
        return 0;
index 9a19e52..6b670b0 100644 (file)
@@ -179,6 +179,9 @@ static void radeon_encoder_add_backlight(struct radeon_encoder *radeon_encoder,
                    (rdev->pdev->subsystem_vendor == 0x1734) &&
                    (rdev->pdev->subsystem_device == 0x1107))
                        use_bl = false;
+               /* disable native backlight control on older asics */
+               else if (rdev->family < CHIP_R600)
+                       use_bl = false;
                else
                        use_bl = true;
        }
index 7784911..00fc597 100644 (file)
@@ -185,6 +185,16 @@ static bool radeon_msi_ok(struct radeon_device *rdev)
        if (rdev->flags & RADEON_IS_AGP)
                return false;
 
+       /*
+        * Older chips have a HW limitation, they can only generate 40 bits
+        * of address for "64-bit" MSIs which breaks on some platforms, notably
+        * IBM POWER servers, so we limit them
+        */
+       if (rdev->family < CHIP_BONAIRE) {
+               dev_info(rdev->dev, "radeon: MSI limited to 32-bit\n");
+               rdev->pdev->no_64bit_msi = 1;
+       }
+
        /* force MSI on */
        if (radeon_msi == 1)
                return true;
index 8309b11..0358676 100644 (file)
@@ -795,6 +795,8 @@ int radeon_get_vblank_timestamp_kms(struct drm_device *dev, int crtc,
 
        /* Get associated drm_crtc: */
        drmcrtc = &rdev->mode_info.crtcs[crtc]->base;
+       if (!drmcrtc)
+               return -EINVAL;
 
        /* Helper routine in DRM core does all the work: */
        return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc, max_error,
index 99a960a..4c0d786 100644 (file)
@@ -213,6 +213,13 @@ int radeon_bo_create(struct radeon_device *rdev,
        if (!(rdev->flags & RADEON_IS_PCIE))
                bo->flags &= ~(RADEON_GEM_GTT_WC | RADEON_GEM_GTT_UC);
 
+#ifdef CONFIG_X86_32
+       /* XXX: Write-combined CPU mappings of GTT seem broken on 32-bit
+        * See https://bugs.freedesktop.org/show_bug.cgi?id=84627
+        */
+       bo->flags &= ~RADEON_GEM_GTT_WC;
+#endif
+
        radeon_ttm_placement_from_domain(bo, domain);
        /* Kernel allocation are uninterruptible */
        down_read(&rdev->pm.mclk_lock);
index 5f6db46..9acb1c3 100644 (file)
@@ -879,6 +879,9 @@ void rs600_bandwidth_update(struct radeon_device *rdev)
        u32 d1mode_priority_a_cnt, d2mode_priority_a_cnt;
        /* FIXME: implement full support */
 
+       if (!rdev->mode_info.mode_config_initialized)
+               return;
+
        radeon_update_display_priority(rdev);
 
        if (rdev->mode_info.crtcs[0]->base.enabled)
index 3462b64..0a2d36e 100644 (file)
@@ -579,6 +579,9 @@ void rs690_bandwidth_update(struct radeon_device *rdev)
        u32 d1mode_priority_a_cnt, d1mode_priority_b_cnt;
        u32 d2mode_priority_a_cnt, d2mode_priority_b_cnt;
 
+       if (!rdev->mode_info.mode_config_initialized)
+               return;
+
        radeon_update_display_priority(rdev);
 
        if (rdev->mode_info.crtcs[0]->base.enabled)
index 8a477bf..c55d653 100644 (file)
@@ -1277,6 +1277,9 @@ void rv515_bandwidth_update(struct radeon_device *rdev)
        struct drm_display_mode *mode0 = NULL;
        struct drm_display_mode *mode1 = NULL;
 
+       if (!rdev->mode_info.mode_config_initialized)
+               return;
+
        radeon_update_display_priority(rdev);
 
        if (rdev->mode_info.crtcs[0]->base.enabled)
index eeea5b6..7d5083d 100644 (file)
@@ -2384,6 +2384,9 @@ void dce6_bandwidth_update(struct radeon_device *rdev)
        u32 num_heads = 0, lb_size;
        int i;
 
+       if (!rdev->mode_info.mode_config_initialized)
+               return;
+
        radeon_update_display_priority(rdev);
 
        for (i = 0; i < rdev->num_crtc; i++) {
index 6553fd2..054a79f 100644 (file)
@@ -736,7 +736,6 @@ static const struct drm_crtc_funcs tegra_crtc_funcs = {
 
 static void tegra_crtc_disable(struct drm_crtc *crtc)
 {
-       struct tegra_dc *dc = to_tegra_dc(crtc);
        struct drm_device *drm = crtc->dev;
        struct drm_plane *plane;
 
@@ -752,7 +751,7 @@ static void tegra_crtc_disable(struct drm_crtc *crtc)
                }
        }
 
-       drm_vblank_off(drm, dc->pipe);
+       drm_crtc_vblank_off(crtc);
 }
 
 static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc,
@@ -841,8 +840,6 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc,
        u32 value;
        int err;
 
-       drm_vblank_pre_modeset(crtc->dev, dc->pipe);
-
        err = tegra_crtc_setup_clk(crtc, mode);
        if (err) {
                dev_err(dc->dev, "failed to setup clock for CRTC: %d\n", err);
@@ -896,6 +893,8 @@ static void tegra_crtc_prepare(struct drm_crtc *crtc)
        unsigned int syncpt;
        unsigned long value;
 
+       drm_crtc_vblank_off(crtc);
+
        /* hardware initialization */
        reset_control_deassert(dc->rst);
        usleep_range(10000, 20000);
@@ -943,7 +942,7 @@ static void tegra_crtc_commit(struct drm_crtc *crtc)
        value = GENERAL_ACT_REQ | WIN_A_ACT_REQ;
        tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
 
-       drm_vblank_post_modeset(crtc->dev, dc->pipe);
+       drm_crtc_vblank_on(crtc);
 }
 
 static void tegra_crtc_load_lut(struct drm_crtc *crtc)
index 73bd9e2..3402033 100644 (file)
@@ -1659,6 +1659,7 @@ void hid_disconnect(struct hid_device *hdev)
                hdev->hiddev_disconnect(hdev);
        if (hdev->claimed & HID_CLAIMED_HIDRAW)
                hidraw_disconnect(hdev);
+       hdev->claimed = 0;
 }
 EXPORT_SYMBOL_GPL(hid_disconnect);
 
index e23ab8b..7c86373 100644 (file)
 #define USB_VENDOR_ID_ELAN             0x04f3
 #define USB_DEVICE_ID_ELAN_TOUCHSCREEN 0x0089
 #define USB_DEVICE_ID_ELAN_TOUCHSCREEN_009B    0x009b
+#define USB_DEVICE_ID_ELAN_TOUCHSCREEN_0103    0x0103
 #define USB_DEVICE_ID_ELAN_TOUCHSCREEN_016F    0x016f
 
 #define USB_VENDOR_ID_ELECOM           0x056e
index 5014bb5..552671e 100644 (file)
@@ -72,6 +72,7 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN, HID_QUIRK_ALWAYS_POLL },
        { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_009B, HID_QUIRK_ALWAYS_POLL },
+       { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_0103, HID_QUIRK_ALWAYS_POLL },
        { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_016F, HID_QUIRK_ALWAYS_POLL },
        { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
index fcdbde4..3057dfc 100644 (file)
@@ -234,7 +234,7 @@ static const struct pci_device_id fam15h_power_id_table[] = {
        { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
        { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F4) },
        { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F4) },
-       { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F3) },
+       { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F4) },
        {}
 };
 MODULE_DEVICE_TABLE(pci, fam15h_power_id_table);
index 6aac695..9b55e67 100644 (file)
@@ -1084,10 +1084,8 @@ static int g762_probe(struct i2c_client *client, const struct i2c_device_id *id)
        if (ret)
                goto clock_dis;
 
-       data->hwmon_dev = devm_hwmon_device_register_with_groups(dev,
-                                                                client->name,
-                                                                data,
-                                                                g762_groups);
+       data->hwmon_dev = hwmon_device_register_with_groups(dev, client->name,
+                                                           data, g762_groups);
        if (IS_ERR(data->hwmon_dev)) {
                ret = PTR_ERR(data->hwmon_dev);
                goto clock_dis;
index d2bf2c9..6a30eee 100644 (file)
@@ -181,7 +181,7 @@ static int __init populate_attr_groups(struct platform_device *pdev)
 
        opal = of_find_node_by_path("/ibm,opal/sensors");
        if (!opal) {
-               dev_err(&pdev->dev, "Opal node 'sensors' not found\n");
+               dev_dbg(&pdev->dev, "Opal node 'sensors' not found\n");
                return -ENODEV;
        }
 
@@ -335,7 +335,9 @@ static int __init ibmpowernv_init(void)
 
        err = platform_driver_probe(&ibmpowernv_driver, ibmpowernv_probe);
        if (err) {
-               pr_err("Platfrom driver probe failed\n");
+               if (err != -ENODEV)
+                       pr_err("Platform driver probe failed (%d)\n", err);
+
                goto exit_device_del;
        }
 
index 823c877..1991d90 100644 (file)
@@ -161,10 +161,17 @@ static int pwm_fan_suspend(struct device *dev)
 static int pwm_fan_resume(struct device *dev)
 {
        struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
+       unsigned long duty;
+       int ret;
 
-       if (ctx->pwm_value)
-               return pwm_enable(ctx->pwm);
-       return 0;
+       if (ctx->pwm_value == 0)
+               return 0;
+
+       duty = DIV_ROUND_UP(ctx->pwm_value * (ctx->pwm->period - 1), MAX_PWM);
+       ret = pwm_config(ctx->pwm, duty, ctx->pwm->period);
+       if (ret)
+               return ret;
+       return pwm_enable(ctx->pwm);
 }
 #endif
 
index 65ef966..899bede 100644 (file)
     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 Street, Fifth Floor, Boston,
-    MA 02110-1301 USA.
  * ------------------------------------------------------------------------- */
 
 /* With some changes from Frodo Looijaard <frodol@dds.nl>, Kyösti Mälkki
index 8b10f88..580dbf0 100644 (file)
  *  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 Street, Fifth Floor, Boston,
- *  MA 02110-1301 USA.
  */
 
 #include <linux/kernel.h>
index 3437009..270d84b 100644 (file)
  *  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 Street, Fifth Floor, Boston,
- *  MA 02110-1301 USA.
- *
  * With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and
  * Frodo Looijaard <frodol@dds.nl>, and also from Martin Bailey
  * <mbailey@littlefeet-inc.com>
index 1ec703e..262ee80 100644 (file)
     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 Street, Fifth Floor, Boston,
-    MA 02110-1301 USA.                                                 */
+    GNU General Public License for more details.                       */
 /* --------------------------------------------------------------------        */
 
 /* With some changes from Frodo Looijaard <frodol@dds.nl> */
index 451e305..4f2d788 100644 (file)
  *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
 /*
index 2fa21ce..45c5c48 100644 (file)
     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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
 /*
index 41fc683..65e3240 100644 (file)
  * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
  
 /*
index a16f728..6c7113d 100644 (file)
     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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
 /*
index 917d545..e05a672 100644 (file)
@@ -434,7 +434,7 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev)
                }
        }
 
-       ret = wait_for_completion_io_timeout(&dev->cmd_complete,
+       ret = wait_for_completion_timeout(&dev->cmd_complete,
                                             dev->adapter.timeout);
        if (ret == 0) {
                dev_err(dev->dev, "controller timed out\n");
index 8762458..6f8c075 100644 (file)
  * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
 #include <linux/delay.h>
index 63f3f03..c604f4c 100644 (file)
 #define CDNS_I2C_DIVA_MAX      4
 #define CDNS_I2C_DIVB_MAX      64
 
+#define CDNS_I2C_TIMEOUT_MAX   0xFF
+
 #define cdns_i2c_readreg(offset)       readl_relaxed(id->membase + offset)
 #define cdns_i2c_writereg(val, offset) writel_relaxed(val, id->membase + offset)
 
@@ -852,6 +854,15 @@ static int cdns_i2c_probe(struct platform_device *pdev)
                goto err_clk_dis;
        }
 
+       /*
+        * Cadence I2C controller has a bug wherein it generates
+        * invalid read transaction after HW timeout in master receiver mode.
+        * HW timeout is not used by this driver and the interrupt is disabled.
+        * But the feature itself cannot be disabled. Hence maximum value
+        * is written to this register to reduce the chances of error.
+        */
+       cdns_i2c_writereg(CDNS_I2C_TIMEOUT_MAX, CDNS_I2C_TIME_OUT_OFFSET);
+
        dev_info(&pdev->dev, "%u kHz mmio %08lx irq %d\n",
                 id->i2c_clk / 1000, (unsigned long)r_mem->start, id->irq);
 
index f3b89a4..5bdbc71 100644 (file)
  *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/kernel.h>
index 4d96147..01f0cd8 100644 (file)
  * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
  * ----------------------------------------------------------------------------
  *
  */
@@ -411,11 +407,9 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
        if (dev->cmd_err & DAVINCI_I2C_STR_NACK) {
                if (msg->flags & I2C_M_IGNORE_NAK)
                        return msg->len;
-               if (stop) {
-                       w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG);
-                       w |= DAVINCI_I2C_MDR_STP;
-                       davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w);
-               }
+               w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG);
+               w |= DAVINCI_I2C_MDR_STP;
+               davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w);
                return -EREMOTEIO;
        }
        return -EIO;
index 3c20e4b..23628b7 100644 (file)
  * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
  * ----------------------------------------------------------------------------
  *
  */
@@ -363,7 +359,7 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
        }
 
        /* Configure Tx/Rx FIFO threshold levels */
-       dw_writel(dev, dev->tx_fifo_depth - 1, DW_IC_TX_TL);
+       dw_writel(dev, dev->tx_fifo_depth / 2, DW_IC_TX_TL);
        dw_writel(dev, 0, DW_IC_RX_TL);
 
        /* configure the i2c master */
index d66b6cb..5a410ef 100644 (file)
  * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
  * ----------------------------------------------------------------------------
  *
  */
index d31d313..acb40f9 100644 (file)
  * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
  * ----------------------------------------------------------------------------
  *
  */
index a743115..373dd4d 100644 (file)
  * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
  * ----------------------------------------------------------------------------
  *
  */
index a44ea13..76e699f 100644 (file)
@@ -9,10 +9,6 @@
  * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
  */
 
 #include <linux/module.h>
index 4854970..92e8c0c 100644 (file)
     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., 675 Mass Ave, Cambridge, MA 02139, USA.               */
+    GNU General Public License for more details.                            */
 /* ------------------------------------------------------------------------- */
 
 /* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
index 14d2b76..b7864cf 100644 (file)
     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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
 #include <linux/kernel.h>
index 7cfc183..6ab4f1c 100644 (file)
     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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
 /*
index c48e46a..e9fb7cf 100644 (file)
  *     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., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
- *     USA.
- *
  * Author:
  *     Darius Augulis, Teltonika Inc.
  *
index 097e270..2d6929c 100644 (file)
     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., 675 Mass Ave, Cambridge, MA 02139, USA.                */
+    GNU General Public License for more details.                            */
 /* ------------------------------------------------------------------------- */
 
 
index cf99dbf..113293d 100644 (file)
     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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
 /*
index 3f6ecbf..f2b0ff0 100644 (file)
  * 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.
  * The full GNU General Public License is included in this distribution
  * in the file called LICENSE.GPL.
  *
index b170bdf..88eda09 100644 (file)
  * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 /*
index ee3a76c..70b3c91 100644 (file)
     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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
 /*
index 0dffb0e..277a228 100644 (file)
  * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/module.h>
@@ -926,14 +922,12 @@ omap_i2c_isr_thread(int this_irq, void *dev_id)
                if (stat & OMAP_I2C_STAT_NACK) {
                        err |= OMAP_I2C_STAT_NACK;
                        omap_i2c_ack_stat(dev, OMAP_I2C_STAT_NACK);
-                       break;
                }
 
                if (stat & OMAP_I2C_STAT_AL) {
                        dev_err(dev->dev, "Arbitration lost\n");
                        err |= OMAP_I2C_STAT_AL;
                        omap_i2c_ack_stat(dev, OMAP_I2C_STAT_AL);
-                       break;
                }
 
                /*
@@ -958,11 +952,13 @@ omap_i2c_isr_thread(int this_irq, void *dev_id)
                        if (dev->fifo_size)
                                num_bytes = dev->buf_len;
 
-                       omap_i2c_receive_data(dev, num_bytes, true);
-
-                       if (dev->errata & I2C_OMAP_ERRATA_I207)
+                       if (dev->errata & I2C_OMAP_ERRATA_I207) {
                                i2c_omap_errata_i207(dev, stat);
+                               num_bytes = (omap_i2c_read_reg(dev,
+                                       OMAP_I2C_BUFSTAT_REG) >> 8) & 0x3F;
+                       }
 
+                       omap_i2c_receive_data(dev, num_bytes, true);
                        omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RDR);
                        continue;
                }
index 62f55fe..d1f625f 100644 (file)
    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., 675 Mass Ave, Cambridge, MA 02139, USA.
  * ------------------------------------------------------------------------ */
 
 #include <linux/kernel.h>
index a27aae2..a1fac5a 100644 (file)
    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., 675 Mass Ave, Cambridge, MA 02139, USA.
  * ------------------------------------------------------------------------ */
 
 #include <linux/kernel.h>
index e572f3a..4e12945 100644 (file)
    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., 675 Mass Ave, Cambridge, MA 02139, USA.
  * ------------------------------------------------------------------------ */
 
 #define PORT_DATA      0
index 7a9dce4..df1dbc9 100644 (file)
  * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
 #include <linux/module.h>
index 323f061..e0eb4ca 100644 (file)
  *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/kernel.h>
index a6f54ba..67cbec6 100644 (file)
     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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
 /*
index 8564768..177834e 100644 (file)
  *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *  You should have received a copy of the  GNU General Public License along
- *  with this program; if not, write  to the Free Software Foundation, Inc.,
- *  675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 #include <linux/kernel.h>
index 01e9677..60a53c1 100644 (file)
     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., 675 Mass Ave, Cambridge, MA 02139, USA.
-
 */
 
 #include <linux/module.h>
index e3b0337..6524477 100644 (file)
  * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
 #include <linux/kernel.h>
index 8b5e79c..4855188 100644 (file)
  * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 #include <linux/kernel.h>
index 0fe505d..2b6219d 100644 (file)
  * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
 #include <linux/kernel.h>
index 964e5c6..15ac839 100644 (file)
  * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
 #include <linux/kernel.h>
index ac9bc33..7d58a40 100644 (file)
     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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
 /* Note: we assume there can only be one SIS5595 with one SMBus interface */
index c636673..1e6805b 100644 (file)
     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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
 /*
index 8dc2fc5..44b9044 100644 (file)
     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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
 /*
index 10855a0..4c7fc2d 100644 (file)
  * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
 #include <linux/delay.h>
index f4a1ed7..59b1d23 100644 (file)
     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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
 #include <linux/kernel.h>
index 6841200..0ee2646 100644 (file)
     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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
 /*
index ade9223..cc65ea0 100644 (file)
  * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
  *
  * This code was implemented by Mocean Laboratories AB when porting linux
  * to the automotive development board Russellville. The copyright holder
index ff3f574..5153354 100644 (file)
     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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
index f24cc64..90e3229 100644 (file)
  * 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 Street, Fifth Floor, Boston,
- * MA 02110-1301 USA.
  */
 
 #include <linux/kernel.h>
index 2f90ac6..f43b4e1 100644 (file)
     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 Street, Fifth Floor, Boston,
-    MA 02110-1301 USA.                                                      */
+    GNU General Public License for more details.                            */
 /* ------------------------------------------------------------------------- */
 
 /* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi>.
@@ -670,6 +665,9 @@ static int i2c_device_remove(struct device *dev)
                status = driver->remove(client);
        }
 
+       if (dev->of_node)
+               irq_dispose_mapping(client->irq);
+
        dev_pm_domain_detach(&client->dev, true);
        return status;
 }
index 18a8fd2..17700bf 100644 (file)
  * 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 Street, Fifth Floor, Boston,
- * MA 02110-1301 USA.
  */
 
 #include <linux/rwsem.h>
index 80b47e8..71c7a39 100644 (file)
     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 Street, Fifth Floor, Boston,
-    MA 02110-1301 USA.
 */
 
 /* Note that this is a complete rewrite of Simon Vogl's i2c-dev module.
index fc99f0d..9ebf9cb 100644 (file)
  * 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 Street, Fifth Floor, Boston,
- * MA 02110-1301 USA.
  */
 
 #include <linux/kernel.h>
index d241aa2..af2a94e 100644 (file)
     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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
 #define DEBUG 1
index 22c096c..513bd6d 100644 (file)
@@ -44,6 +44,9 @@
 
 #define BMC150_ACCEL_REG_INT_STATUS_2          0x0B
 #define BMC150_ACCEL_ANY_MOTION_MASK           0x07
+#define BMC150_ACCEL_ANY_MOTION_BIT_X          BIT(0)
+#define BMC150_ACCEL_ANY_MOTION_BIT_Y          BIT(1)
+#define BMC150_ACCEL_ANY_MOTION_BIT_Z          BIT(2)
 #define BMC150_ACCEL_ANY_MOTION_BIT_SIGN       BIT(3)
 
 #define BMC150_ACCEL_REG_PMU_LPW               0x11
@@ -92,9 +95,9 @@
 #define BMC150_ACCEL_SLOPE_THRES_MASK          0xFF
 
 /* Slope duration in terms of number of samples */
-#define BMC150_ACCEL_DEF_SLOPE_DURATION        2
+#define BMC150_ACCEL_DEF_SLOPE_DURATION                1
 /* in terms of multiples of g's/LSB, based on range */
-#define BMC150_ACCEL_DEF_SLOPE_THRESHOLD       5
+#define BMC150_ACCEL_DEF_SLOPE_THRESHOLD       1
 
 #define BMC150_ACCEL_REG_XOUT_L                0x02
 
@@ -536,6 +539,9 @@ static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on)
        if (ret < 0) {
                dev_err(&data->client->dev,
                        "Failed: bmc150_accel_set_power_state for %d\n", on);
+               if (on)
+                       pm_runtime_put_noidle(&data->client->dev);
+
                return ret;
        }
 
@@ -811,6 +817,7 @@ static int bmc150_accel_write_event_config(struct iio_dev *indio_dev,
 
        ret =  bmc150_accel_setup_any_motion_interrupt(data, state);
        if (ret < 0) {
+               bmc150_accel_set_power_state(data, false);
                mutex_unlock(&data->mutex);
                return ret;
        }
@@ -846,7 +853,7 @@ static const struct attribute_group bmc150_accel_attrs_group = {
 
 static const struct iio_event_spec bmc150_accel_event = {
                .type = IIO_EV_TYPE_ROC,
-               .dir = IIO_EV_DIR_RISING | IIO_EV_DIR_FALLING,
+               .dir = IIO_EV_DIR_EITHER,
                .mask_separate = BIT(IIO_EV_INFO_VALUE) |
                                 BIT(IIO_EV_INFO_ENABLE) |
                                 BIT(IIO_EV_INFO_PERIOD)
@@ -1054,6 +1061,7 @@ static int bmc150_accel_data_rdy_trigger_set_state(struct iio_trigger *trig,
        else
                ret = bmc150_accel_setup_new_data_interrupt(data, state);
        if (ret < 0) {
+               bmc150_accel_set_power_state(data, false);
                mutex_unlock(&data->mutex);
                return ret;
        }
@@ -1092,12 +1100,26 @@ static irqreturn_t bmc150_accel_event_handler(int irq, void *private)
        else
                dir = IIO_EV_DIR_RISING;
 
-       if (ret & BMC150_ACCEL_ANY_MOTION_MASK)
+       if (ret & BMC150_ACCEL_ANY_MOTION_BIT_X)
+               iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL,
+                                                       0,
+                                                       IIO_MOD_X,
+                                                       IIO_EV_TYPE_ROC,
+                                                       dir),
+                                                       data->timestamp);
+       if (ret & BMC150_ACCEL_ANY_MOTION_BIT_Y)
                iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL,
                                                        0,
-                                                       IIO_MOD_X_OR_Y_OR_Z,
+                                                       IIO_MOD_Y,
                                                        IIO_EV_TYPE_ROC,
-                                                       IIO_EV_DIR_EITHER),
+                                                       dir),
+                                                       data->timestamp);
+       if (ret & BMC150_ACCEL_ANY_MOTION_BIT_Z)
+               iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL,
+                                                       0,
+                                                       IIO_MOD_Z,
+                                                       IIO_EV_TYPE_ROC,
+                                                       dir),
                                                        data->timestamp);
 ack_intr_status:
        if (!data->dready_trigger_on)
@@ -1354,10 +1376,14 @@ static int bmc150_accel_runtime_suspend(struct device *dev)
 {
        struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
        struct bmc150_accel_data *data = iio_priv(indio_dev);
+       int ret;
 
        dev_dbg(&data->client->dev,  __func__);
+       ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_SUSPEND, 0);
+       if (ret < 0)
+               return -EAGAIN;
 
-       return bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_SUSPEND, 0);
+       return 0;
 }
 
 static int bmc150_accel_runtime_resume(struct device *dev)
index 98909a9..320aa72 100644 (file)
@@ -269,6 +269,8 @@ static int kxcjk1013_set_range(struct kxcjk1013_data *data, int range_index)
                return ret;
        }
 
+       ret &= ~(KXCJK1013_REG_CTRL1_BIT_GSEL0 |
+                KXCJK1013_REG_CTRL1_BIT_GSEL1);
        ret |= (KXCJK1013_scale_table[range_index].gsel_0 << 3);
        ret |= (KXCJK1013_scale_table[range_index].gsel_1 << 4);
 
@@ -894,7 +896,7 @@ static const struct attribute_group kxcjk1013_attrs_group = {
 
 static const struct iio_event_spec kxcjk1013_event = {
                .type = IIO_EV_TYPE_THRESH,
-               .dir = IIO_EV_DIR_RISING | IIO_EV_DIR_FALLING,
+               .dir = IIO_EV_DIR_EITHER,
                .mask_separate = BIT(IIO_EV_INFO_VALUE) |
                                 BIT(IIO_EV_INFO_ENABLE) |
                                 BIT(IIO_EV_INFO_PERIOD)
index b58d630..d095efe 100644 (file)
@@ -152,6 +152,7 @@ static void men_z188_remove(struct mcb_device *dev)
 
 static const struct mcb_device_id men_z188_ids[] = {
        { .device = 0xbc },
+       { }
 };
 MODULE_DEVICE_TABLE(mcb, men_z188_ids);
 
index 1f967e0..d2fa526 100644 (file)
@@ -67,6 +67,9 @@
 #define BMG160_REG_INT_EN_0            0x15
 #define BMG160_DATA_ENABLE_INT         BIT(7)
 
+#define BMG160_REG_INT_EN_1            0x16
+#define BMG160_INT1_BIT_OD             BIT(1)
+
 #define BMG160_REG_XOUT_L              0x02
 #define BMG160_AXIS_TO_REG(axis)       (BMG160_REG_XOUT_L + (axis * 2))
 
@@ -82,6 +85,9 @@
 
 #define BMG160_REG_INT_STATUS_2        0x0B
 #define BMG160_ANY_MOTION_MASK         0x07
+#define BMG160_ANY_MOTION_BIT_X                BIT(0)
+#define BMG160_ANY_MOTION_BIT_Y                BIT(1)
+#define BMG160_ANY_MOTION_BIT_Z                BIT(2)
 
 #define BMG160_REG_TEMP                0x08
 #define BMG160_TEMP_CENTER_VAL         23
@@ -222,6 +228,19 @@ static int bmg160_chip_init(struct bmg160_data *data)
        data->slope_thres = ret;
 
        /* Set default interrupt mode */
+       ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_EN_1);
+       if (ret < 0) {
+               dev_err(&data->client->dev, "Error reading reg_int_en_1\n");
+               return ret;
+       }
+       ret &= ~BMG160_INT1_BIT_OD;
+       ret = i2c_smbus_write_byte_data(data->client,
+                                       BMG160_REG_INT_EN_1, ret);
+       if (ret < 0) {
+               dev_err(&data->client->dev, "Error writing reg_int_en_1\n");
+               return ret;
+       }
+
        ret = i2c_smbus_write_byte_data(data->client,
                                        BMG160_REG_INT_RST_LATCH,
                                        BMG160_INT_MODE_LATCH_INT |
@@ -250,6 +269,9 @@ static int bmg160_set_power_state(struct bmg160_data *data, bool on)
        if (ret < 0) {
                dev_err(&data->client->dev,
                        "Failed: bmg160_set_power_state for %d\n", on);
+               if (on)
+                       pm_runtime_put_noidle(&data->client->dev);
+
                return ret;
        }
 #endif
@@ -705,6 +727,7 @@ static int bmg160_write_event_config(struct iio_dev *indio_dev,
 
        ret =  bmg160_setup_any_motion_interrupt(data, state);
        if (ret < 0) {
+               bmg160_set_power_state(data, false);
                mutex_unlock(&data->mutex);
                return ret;
        }
@@ -743,7 +766,7 @@ static const struct attribute_group bmg160_attrs_group = {
 
 static const struct iio_event_spec bmg160_event = {
                .type = IIO_EV_TYPE_ROC,
-               .dir = IIO_EV_DIR_RISING | IIO_EV_DIR_FALLING,
+               .dir = IIO_EV_DIR_EITHER,
                .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
                                       BIT(IIO_EV_INFO_ENABLE)
 };
@@ -871,6 +894,7 @@ static int bmg160_data_rdy_trigger_set_state(struct iio_trigger *trig,
        else
                ret = bmg160_setup_new_data_interrupt(data, state);
        if (ret < 0) {
+               bmg160_set_power_state(data, false);
                mutex_unlock(&data->mutex);
                return ret;
        }
@@ -908,10 +932,24 @@ static irqreturn_t bmg160_event_handler(int irq, void *private)
        else
                dir = IIO_EV_DIR_FALLING;
 
-       if (ret & BMG160_ANY_MOTION_MASK)
+       if (ret & BMG160_ANY_MOTION_BIT_X)
                iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
                                                        0,
-                                                       IIO_MOD_X_OR_Y_OR_Z,
+                                                       IIO_MOD_X,
+                                                       IIO_EV_TYPE_ROC,
+                                                       dir),
+                                                       data->timestamp);
+       if (ret & BMG160_ANY_MOTION_BIT_Y)
+               iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
+                                                       0,
+                                                       IIO_MOD_Y,
+                                                       IIO_EV_TYPE_ROC,
+                                                       dir),
+                                                       data->timestamp);
+       if (ret & BMG160_ANY_MOTION_BIT_Z)
+               iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
+                                                       0,
+                                                       IIO_MOD_Z,
                                                        IIO_EV_TYPE_ROC,
                                                        dir),
                                                        data->timestamp);
@@ -1169,8 +1207,15 @@ static int bmg160_runtime_suspend(struct device *dev)
 {
        struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
        struct bmg160_data *data = iio_priv(indio_dev);
+       int ret;
+
+       ret = bmg160_set_mode(data, BMG160_MODE_SUSPEND);
+       if (ret < 0) {
+               dev_err(&data->client->dev, "set mode failed\n");
+               return -EAGAIN;
+       }
 
-       return bmg160_set_mode(data, BMG160_MODE_SUSPEND);
+       return 0;
 }
 
 static int bmg160_runtime_resume(struct device *dev)
index a15006e..0763b86 100644 (file)
@@ -230,9 +230,12 @@ static int tsl4531_resume(struct device *dev)
        return i2c_smbus_write_byte_data(to_i2c_client(dev), TSL4531_CONTROL,
                TSL4531_MODE_NORMAL);
 }
-#endif
 
 static SIMPLE_DEV_PM_OPS(tsl4531_pm_ops, tsl4531_suspend, tsl4531_resume);
+#define TSL4531_PM_OPS (&tsl4531_pm_ops)
+#else
+#define TSL4531_PM_OPS NULL
+#endif
 
 static const struct i2c_device_id tsl4531_id[] = {
        { "tsl4531", 0 },
@@ -243,7 +246,7 @@ MODULE_DEVICE_TABLE(i2c, tsl4531_id);
 static struct i2c_driver tsl4531_driver = {
        .driver = {
                .name   = TSL4531_DRV_NAME,
-               .pm     = &tsl4531_pm_ops,
+               .pm     = TSL4531_PM_OPS,
                .owner  = THIS_MODULE,
        },
        .probe  = tsl4531_probe,
index 5e780ef..8349cc0 100644 (file)
@@ -330,7 +330,7 @@ static int as3935_probe(struct spi_device *spi)
                return -EINVAL;
        }
 
-       indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(st));
+       indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
        if (!indio_dev)
                return -ENOMEM;
 
index 3effa93..10641b7 100644 (file)
@@ -115,9 +115,12 @@ isert_conn_setup_qp(struct isert_conn *isert_conn, struct rdma_cm_id *cma_id,
        attr.cap.max_recv_wr = ISERT_QP_MAX_RECV_DTOS;
        /*
         * FIXME: Use devattr.max_sge - 2 for max_send_sge as
-        * work-around for RDMA_READ..
+        * work-around for RDMA_READs with ConnectX-2.
+        *
+        * Also, still make sure to have at least two SGEs for
+        * outgoing control PDU responses.
         */
-       attr.cap.max_send_sge = device->dev_attr.max_sge - 2;
+       attr.cap.max_send_sge = max(2, device->dev_attr.max_sge - 2);
        isert_conn->max_sge = attr.cap.max_send_sge;
 
        attr.cap.max_recv_sge = 1;
@@ -225,12 +228,16 @@ isert_create_device_ib_res(struct isert_device *device)
        struct isert_cq_desc *cq_desc;
        struct ib_device_attr *dev_attr;
        int ret = 0, i, j;
+       int max_rx_cqe, max_tx_cqe;
 
        dev_attr = &device->dev_attr;
        ret = isert_query_device(ib_dev, dev_attr);
        if (ret)
                return ret;
 
+       max_rx_cqe = min(ISER_MAX_RX_CQ_LEN, dev_attr->max_cqe);
+       max_tx_cqe = min(ISER_MAX_TX_CQ_LEN, dev_attr->max_cqe);
+
        /* asign function handlers */
        if (dev_attr->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS &&
            dev_attr->device_cap_flags & IB_DEVICE_SIGNATURE_HANDOVER) {
@@ -272,7 +279,7 @@ isert_create_device_ib_res(struct isert_device *device)
                                                isert_cq_rx_callback,
                                                isert_cq_event_callback,
                                                (void *)&cq_desc[i],
-                                               ISER_MAX_RX_CQ_LEN, i);
+                                               max_rx_cqe, i);
                if (IS_ERR(device->dev_rx_cq[i])) {
                        ret = PTR_ERR(device->dev_rx_cq[i]);
                        device->dev_rx_cq[i] = NULL;
@@ -284,7 +291,7 @@ isert_create_device_ib_res(struct isert_device *device)
                                                isert_cq_tx_callback,
                                                isert_cq_event_callback,
                                                (void *)&cq_desc[i],
-                                               ISER_MAX_TX_CQ_LEN, i);
+                                               max_tx_cqe, i);
                if (IS_ERR(device->dev_tx_cq[i])) {
                        ret = PTR_ERR(device->dev_tx_cq[i]);
                        device->dev_tx_cq[i] = NULL;
@@ -803,14 +810,25 @@ wake_up:
        complete(&isert_conn->conn_wait);
 }
 
-static void
+static int
 isert_disconnected_handler(struct rdma_cm_id *cma_id, bool disconnect)
 {
-       struct isert_conn *isert_conn = (struct isert_conn *)cma_id->context;
+       struct isert_conn *isert_conn;
+
+       if (!cma_id->qp) {
+               struct isert_np *isert_np = cma_id->context;
+
+               isert_np->np_cm_id = NULL;
+               return -1;
+       }
+
+       isert_conn = (struct isert_conn *)cma_id->context;
 
        isert_conn->disconnect = disconnect;
        INIT_WORK(&isert_conn->conn_logout_work, isert_disconnect_work);
        schedule_work(&isert_conn->conn_logout_work);
+
+       return 0;
 }
 
 static int
@@ -825,6 +843,9 @@ isert_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
        switch (event->event) {
        case RDMA_CM_EVENT_CONNECT_REQUEST:
                ret = isert_connect_request(cma_id, event);
+               if (ret)
+                       pr_err("isert_cma_handler failed RDMA_CM_EVENT: 0x%08x %d\n",
+                               event->event, ret);
                break;
        case RDMA_CM_EVENT_ESTABLISHED:
                isert_connected_handler(cma_id);
@@ -834,7 +855,7 @@ isert_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
        case RDMA_CM_EVENT_DEVICE_REMOVAL: /* FALLTHRU */
                disconnect = true;
        case RDMA_CM_EVENT_TIMEWAIT_EXIT:  /* FALLTHRU */
-               isert_disconnected_handler(cma_id, disconnect);
+               ret = isert_disconnected_handler(cma_id, disconnect);
                break;
        case RDMA_CM_EVENT_CONNECT_ERROR:
        default:
@@ -842,12 +863,6 @@ isert_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
                break;
        }
 
-       if (ret != 0) {
-               pr_err("isert_cma_handler failed RDMA_CM_EVENT: 0x%08x %d\n",
-                      event->event, ret);
-               dump_stack();
-       }
-
        return ret;
 }
 
@@ -3190,7 +3205,8 @@ isert_free_np(struct iscsi_np *np)
 {
        struct isert_np *isert_np = (struct isert_np *)np->np_context;
 
-       rdma_destroy_id(isert_np->np_cm_id);
+       if (isert_np->np_cm_id)
+               rdma_destroy_id(isert_np->np_cm_id);
 
        np->np_context = NULL;
        kfree(isert_np);
index 7206547..dc82968 100644 (file)
@@ -2092,6 +2092,7 @@ static int srpt_create_ch_ib(struct srpt_rdma_ch *ch)
        if (!qp_init)
                goto out;
 
+retry:
        ch->cq = ib_create_cq(sdev->device, srpt_completion, NULL, ch,
                              ch->rq_size + srp_sq_size, 0);
        if (IS_ERR(ch->cq)) {
@@ -2115,6 +2116,13 @@ static int srpt_create_ch_ib(struct srpt_rdma_ch *ch)
        ch->qp = ib_create_qp(sdev->pd, qp_init);
        if (IS_ERR(ch->qp)) {
                ret = PTR_ERR(ch->qp);
+               if (ret == -ENOMEM) {
+                       srp_sq_size /= 2;
+                       if (srp_sq_size >= MIN_SRPT_SQ_SIZE) {
+                               ib_destroy_cq(ch->cq);
+                               goto retry;
+                       }
+               }
                printk(KERN_ERR "failed to create_qp ret= %d\n", ret);
                goto err_destroy_cq;
        }
index bc20348..8afa28e 100644 (file)
@@ -421,7 +421,7 @@ static int evdev_open(struct inode *inode, struct file *file)
 
  err_free_client:
        evdev_detach_client(evdev, client);
-       kfree(client);
+       kvfree(client);
        return error;
 }
 
index 2ed7905..fc55f0d 100644 (file)
@@ -1179,9 +1179,19 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
                }
 
                ep_irq_in = &intf->cur_altsetting->endpoint[1].desc;
-               usb_fill_bulk_urb(xpad->bulk_out, udev,
-                               usb_sndbulkpipe(udev, ep_irq_in->bEndpointAddress),
-                               xpad->bdata, XPAD_PKT_LEN, xpad_bulk_out, xpad);
+               if (usb_endpoint_is_bulk_out(ep_irq_in)) {
+                       usb_fill_bulk_urb(xpad->bulk_out, udev,
+                                         usb_sndbulkpipe(udev,
+                                                         ep_irq_in->bEndpointAddress),
+                                         xpad->bdata, XPAD_PKT_LEN,
+                                         xpad_bulk_out, xpad);
+               } else {
+                       usb_fill_int_urb(xpad->bulk_out, udev,
+                                        usb_sndintpipe(udev,
+                                                       ep_irq_in->bEndpointAddress),
+                                        xpad->bdata, XPAD_PKT_LEN,
+                                        xpad_bulk_out, xpad, 0);
+               }
 
                /*
                 * Submit the int URB immediately rather than waiting for open
index fb3b63b..8400a1a 100644 (file)
@@ -85,6 +85,7 @@ static int twl4030_pwrbutton_probe(struct platform_device *pdev)
        }
 
        platform_set_drvdata(pdev, pwr);
+       device_init_wakeup(&pdev->dev, true);
 
        return 0;
 }
index 2b0ae8c..d125a01 100644 (file)
@@ -1156,7 +1156,13 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
 {
        struct alps_data *priv = psmouse->private;
 
-       if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */
+       /*
+        * Check if we are dealing with a bare PS/2 packet, presumably from
+        * a device connected to the external PS/2 port. Because bare PS/2
+        * protocol does not have enough constant bits to self-synchronize
+        * properly we only do this if the device is fully synchronized.
+        */
+       if (!psmouse->out_of_sync_cnt && (psmouse->packet[0] & 0xc8) == 0x08) {
                if (psmouse->pktcnt == 3) {
                        alps_report_bare_ps2_packet(psmouse, psmouse->packet,
                                                    true);
@@ -1180,12 +1186,27 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
        }
 
        /* Bytes 2 - pktsize should have 0 in the highest bit */
-       if ((priv->proto_version < ALPS_PROTO_V5) &&
+       if (priv->proto_version < ALPS_PROTO_V5 &&
            psmouse->pktcnt >= 2 && psmouse->pktcnt <= psmouse->pktsize &&
            (psmouse->packet[psmouse->pktcnt - 1] & 0x80)) {
                psmouse_dbg(psmouse, "refusing packet[%i] = %x\n",
                            psmouse->pktcnt - 1,
                            psmouse->packet[psmouse->pktcnt - 1]);
+
+               if (priv->proto_version == ALPS_PROTO_V3 &&
+                   psmouse->pktcnt == psmouse->pktsize) {
+                       /*
+                        * Some Dell boxes, such as Latitude E6440 or E7440
+                        * with closed lid, quite often smash last byte of
+                        * otherwise valid packet with 0xff. Given that the
+                        * next packet is very likely to be valid let's
+                        * report PSMOUSE_FULL_PACKET but not process data,
+                        * rather than reporting PSMOUSE_BAD_DATA and
+                        * filling the logs.
+                        */
+                       return PSMOUSE_FULL_PACKET;
+               }
+
                return PSMOUSE_BAD_DATA;
        }
 
@@ -2389,6 +2410,9 @@ int alps_init(struct psmouse *psmouse)
        /* We are having trouble resyncing ALPS touchpads so disable it for now */
        psmouse->resync_time = 0;
 
+       /* Allow 2 invalid packets without resetting device */
+       psmouse->resetafter = psmouse->pktsize * 2;
+
        return 0;
 
 init_fail:
index 06fc6e7..f2b9780 100644 (file)
@@ -428,14 +428,6 @@ static void elantech_report_trackpoint(struct psmouse *psmouse,
        int x, y;
        u32 t;
 
-       if (dev_WARN_ONCE(&psmouse->ps2dev.serio->dev,
-                         !tp_dev,
-                         psmouse_fmt("Unexpected trackpoint message\n"))) {
-               if (etd->debug == 1)
-                       elantech_packet_dump(psmouse);
-               return;
-       }
-
        t = get_unaligned_le32(&packet[0]);
 
        switch (t & ~7U) {
@@ -563,6 +555,7 @@ static void elantech_input_sync_v4(struct psmouse *psmouse)
        } else {
                input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
                input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
+               input_report_key(dev, BTN_MIDDLE, packet[0] & 0x04);
        }
 
        input_mt_report_pointer_emulation(dev, true);
@@ -792,6 +785,9 @@ static int elantech_packet_check_v4(struct psmouse *psmouse)
        unsigned char packet_type = packet[3] & 0x03;
        bool sanity_check;
 
+       if (etd->tp_dev && (packet[3] & 0x0f) == 0x06)
+               return PACKET_TRACKPOINT;
+
        /*
         * Sanity check based on the constant bits of a packet.
         * The constant bits change depending on the value of
@@ -877,10 +873,19 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
 
        case 4:
                packet_type = elantech_packet_check_v4(psmouse);
-               if (packet_type == PACKET_UNKNOWN)
+               switch (packet_type) {
+               case PACKET_UNKNOWN:
                        return PSMOUSE_BAD_DATA;
 
-               elantech_report_absolute_v4(psmouse, packet_type);
+               case PACKET_TRACKPOINT:
+                       elantech_report_trackpoint(psmouse, packet_type);
+                       break;
+
+               default:
+                       elantech_report_absolute_v4(psmouse, packet_type);
+                       break;
+               }
+
                break;
        }
 
@@ -1119,6 +1124,22 @@ static void elantech_set_buttonpad_prop(struct psmouse *psmouse)
        }
 }
 
+/*
+ * Some hw_version 4 models do have a middle button
+ */
+static const struct dmi_system_id elantech_dmi_has_middle_button[] = {
+#if defined(CONFIG_DMI) && defined(CONFIG_X86)
+       {
+               /* Fujitsu H730 has a middle button */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H730"),
+               },
+       },
+#endif
+       { }
+};
+
 /*
  * Set the appropriate event bits for the input subsystem
  */
@@ -1138,6 +1159,8 @@ static int elantech_set_input_params(struct psmouse *psmouse)
        __clear_bit(EV_REL, dev->evbit);
 
        __set_bit(BTN_LEFT, dev->keybit);
+       if (dmi_check_system(elantech_dmi_has_middle_button))
+               __set_bit(BTN_MIDDLE, dev->keybit);
        __set_bit(BTN_RIGHT, dev->keybit);
 
        __set_bit(BTN_TOUCH, dev->keybit);
@@ -1299,6 +1322,7 @@ ELANTECH_INT_ATTR(reg_25, 0x25);
 ELANTECH_INT_ATTR(reg_26, 0x26);
 ELANTECH_INT_ATTR(debug, 0);
 ELANTECH_INT_ATTR(paritycheck, 0);
+ELANTECH_INT_ATTR(crc_enabled, 0);
 
 static struct attribute *elantech_attrs[] = {
        &psmouse_attr_reg_07.dattr.attr,
@@ -1313,6 +1337,7 @@ static struct attribute *elantech_attrs[] = {
        &psmouse_attr_reg_26.dattr.attr,
        &psmouse_attr_debug.dattr.attr,
        &psmouse_attr_paritycheck.dattr.attr,
+       &psmouse_attr_crc_enabled.dattr.attr,
        NULL
 };
 
@@ -1438,6 +1463,22 @@ static int elantech_reconnect(struct psmouse *psmouse)
        return 0;
 }
 
+/*
+ * Some hw_version 4 models do not work with crc_disabled
+ */
+static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = {
+#if defined(CONFIG_DMI) && defined(CONFIG_X86)
+       {
+               /* Fujitsu H730 does not work with crc_enabled == 0 */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H730"),
+               },
+       },
+#endif
+       { }
+};
+
 /*
  * Some hw_version 3 models go into error state when we try to set
  * bit 3 and/or bit 1 of r10.
@@ -1513,7 +1554,8 @@ static int elantech_set_properties(struct elantech_data *etd)
         * The signatures of v3 and v4 packets change depending on the
         * value of this hardware flag.
         */
-       etd->crc_enabled = ((etd->fw_version & 0x4000) == 0x4000);
+       etd->crc_enabled = (etd->fw_version & 0x4000) == 0x4000 ||
+                          dmi_check_system(elantech_dmi_force_crc_enabled);
 
        /* Enable real hardware resolution on hw_version 3 ? */
        etd->set_hw_resolution = !dmi_check_system(no_hw_res_dmi_table);
index 9031a0a..f947292 100644 (file)
@@ -135,14 +135,18 @@ static const struct min_max_quirk min_max_pnpid_table[] = {
                1232, 5710, 1156, 4696
        },
        {
-               (const char * const []){"LEN0034", "LEN0036", "LEN2002",
-                                       "LEN2004", NULL},
+               (const char * const []){"LEN0034", "LEN0036", "LEN0039",
+                                       "LEN2002", "LEN2004", NULL},
                1024, 5112, 2024, 4832
        },
        {
                (const char * const []){"LEN2001", NULL},
                1024, 5022, 2508, 4832
        },
+       {
+               (const char * const []){"LEN2006", NULL},
+               1264, 5675, 1171, 4688
+       },
        { }
 };
 
@@ -163,6 +167,7 @@ static const char * const topbuttonpad_pnp_ids[] = {
        "LEN0036", /* T440 */
        "LEN0037",
        "LEN0038",
+       "LEN0039", /* T440s */
        "LEN0041",
        "LEN0042", /* Yoga */
        "LEN0045",
index 3e238cd..6a2e168 100644 (file)
@@ -43,6 +43,7 @@
 #define ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS    (0x34)
 #define ARMADA_370_XP_INT_SOURCE_CTL(irq)      (0x100 + irq*4)
 #define ARMADA_370_XP_INT_SOURCE_CPU_MASK      0xF
+#define ARMADA_370_XP_INT_IRQ_FIQ_MASK(cpuid)  ((BIT(0) | BIT(8)) << cpuid)
 
 #define ARMADA_370_XP_CPU_INTACK_OFFS          (0x44)
 #define ARMADA_375_PPI_CAUSE                   (0x10)
@@ -406,19 +407,29 @@ static void armada_370_xp_mpic_handle_cascade_irq(unsigned int irq,
                                                  struct irq_desc *desc)
 {
        struct irq_chip *chip = irq_get_chip(irq);
-       unsigned long irqmap, irqn;
+       unsigned long irqmap, irqn, irqsrc, cpuid;
        unsigned int cascade_irq;
 
        chained_irq_enter(chip, desc);
 
        irqmap = readl_relaxed(per_cpu_int_base + ARMADA_375_PPI_CAUSE);
-
-       if (irqmap & BIT(0)) {
-               armada_370_xp_handle_msi_irq(NULL, true);
-               irqmap &= ~BIT(0);
-       }
+       cpuid = cpu_logical_map(smp_processor_id());
 
        for_each_set_bit(irqn, &irqmap, BITS_PER_LONG) {
+               irqsrc = readl_relaxed(main_int_base +
+                                      ARMADA_370_XP_INT_SOURCE_CTL(irqn));
+
+               /* Check if the interrupt is not masked on current CPU.
+                * Test IRQ (0-1) and FIQ (8-9) mask bits.
+                */
+               if (!(irqsrc & ARMADA_370_XP_INT_IRQ_FIQ_MASK(cpuid)))
+                       continue;
+
+               if (irqn == 1) {
+                       armada_370_xp_handle_msi_irq(NULL, true);
+                       continue;
+               }
+
                cascade_irq = irq_find_mapping(armada_370_xp_mpic_domain, irqn);
                generic_handle_irq(cascade_irq);
        }
index 6ae3cde..cc4f9d8 100644 (file)
@@ -217,8 +217,9 @@ struct irq_domain *__init aic_common_of_init(struct device_node *node,
        }
 
        ret = irq_alloc_domain_generic_chips(domain, 32, 1, name,
-                                            handle_level_irq, 0, 0,
-                                            IRQCHIP_SKIP_SET_WAKE);
+                                            handle_fasteoi_irq,
+                                            IRQ_NOREQUEST | IRQ_NOPROBE |
+                                            IRQ_NOAUTOEN, 0, 0);
        if (ret)
                goto err_domain_remove;
 
@@ -230,7 +231,6 @@ struct irq_domain *__init aic_common_of_init(struct device_node *node,
                gc->unused = 0;
                gc->wake_enabled = ~0;
                gc->chip_types[0].type = IRQ_TYPE_SENSE_MASK;
-               gc->chip_types[0].handler = handle_fasteoi_irq;
                gc->chip_types[0].chip.irq_eoi = irq_gc_eoi;
                gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake;
                gc->chip_types[0].chip.irq_shutdown = aic_common_shutdown;
index b9f4fb8..5fb38a2 100644 (file)
@@ -101,9 +101,9 @@ static int bcm7120_l2_intc_init_one(struct device_node *dn,
        int parent_irq;
 
        parent_irq = irq_of_parse_and_map(dn, irq);
-       if (parent_irq < 0) {
+       if (!parent_irq) {
                pr_err("failed to map interrupt %d\n", irq);
-               return parent_irq;
+               return -EINVAL;
        }
 
        data->irq_map_mask |= be32_to_cpup(map_mask + irq);
index c15c840..14691a4 100644 (file)
@@ -135,9 +135,9 @@ int __init brcmstb_l2_intc_of_init(struct device_node *np,
        __raw_writel(0xffffffff, data->base + CPU_CLEAR);
 
        data->parent_irq = irq_of_parse_and_map(np, 0);
-       if (data->parent_irq < 0) {
+       if (!data->parent_irq) {
                pr_err("failed to find parent interrupt\n");
-               ret = data->parent_irq;
+               ret = -EINVAL;
                goto out_unmap;
        }
 
index 825ca1f..afe7971 100644 (file)
@@ -1434,9 +1434,9 @@ static void drop_buffers(struct dm_bufio_client *c)
 
 /*
  * Test if the buffer is unused and too old, and commit it.
- * At if noio is set, we must not do any I/O because we hold
- * dm_bufio_clients_lock and we would risk deadlock if the I/O gets rerouted to
- * different bufio client.
+ * And if GFP_NOFS is used, we must not do any I/O because we hold
+ * dm_bufio_clients_lock and we would risk deadlock if the I/O gets
+ * rerouted to different bufio client.
  */
 static int __cleanup_old_buffer(struct dm_buffer *b, gfp_t gfp,
                                unsigned long max_jiffies)
@@ -1444,7 +1444,7 @@ static int __cleanup_old_buffer(struct dm_buffer *b, gfp_t gfp,
        if (jiffies - b->last_accessed < max_jiffies)
                return 0;
 
-       if (!(gfp & __GFP_IO)) {
+       if (!(gfp & __GFP_FS)) {
                if (test_bit(B_READING, &b->state) ||
                    test_bit(B_WRITING, &b->state) ||
                    test_bit(B_DIRTY, &b->state))
@@ -1486,7 +1486,7 @@ dm_bufio_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
        unsigned long freed;
 
        c = container_of(shrink, struct dm_bufio_client, shrinker);
-       if (sc->gfp_mask & __GFP_IO)
+       if (sc->gfp_mask & __GFP_FS)
                dm_bufio_lock(c);
        else if (!dm_bufio_trylock(c))
                return SHRINK_STOP;
@@ -1503,7 +1503,7 @@ dm_bufio_shrink_count(struct shrinker *shrink, struct shrink_control *sc)
        unsigned long count;
 
        c = container_of(shrink, struct dm_bufio_client, shrinker);
-       if (sc->gfp_mask & __GFP_IO)
+       if (sc->gfp_mask & __GFP_FS)
                dm_bufio_lock(c);
        else if (!dm_bufio_trylock(c))
                return 0;
index 4857fa4..07c0fa0 100644 (file)
@@ -789,8 +789,7 @@ struct dm_raid_superblock {
        __le32 layout;
        __le32 stripe_sectors;
 
-       __u8 pad[452];          /* Round struct to 512 bytes. */
-                               /* Always set to 0 when writing. */
+       /* Remainder of a logical block is zero-filled when writing (see super_sync()). */
 } __packed;
 
 static int read_disk_sb(struct md_rdev *rdev, int size)
@@ -827,7 +826,7 @@ static void super_sync(struct mddev *mddev, struct md_rdev *rdev)
                    test_bit(Faulty, &(rs->dev[i].rdev.flags)))
                        failed_devices |= (1ULL << i);
 
-       memset(sb, 0, sizeof(*sb));
+       memset(sb + 1, 0, rdev->sb_size - sizeof(*sb));
 
        sb->magic = cpu_to_le32(DM_RAID_MAGIC);
        sb->features = cpu_to_le32(0);  /* No features yet */
@@ -862,7 +861,11 @@ static int super_load(struct md_rdev *rdev, struct md_rdev *refdev)
        uint64_t events_sb, events_refsb;
 
        rdev->sb_start = 0;
-       rdev->sb_size = sizeof(*sb);
+       rdev->sb_size = bdev_logical_block_size(rdev->meta_bdev);
+       if (rdev->sb_size < sizeof(*sb) || rdev->sb_size > PAGE_SIZE) {
+               DMERR("superblock size of a logical block is no longer valid");
+               return -EINVAL;
+       }
 
        ret = read_disk_sb(rdev, rdev->sb_size);
        if (ret)
@@ -1169,8 +1172,12 @@ static void configure_discard_support(struct dm_target *ti, struct raid_set *rs)
        raid456 = (rs->md.level == 4 || rs->md.level == 5 || rs->md.level == 6);
 
        for (i = 0; i < rs->md.raid_disks; i++) {
-               struct request_queue *q = bdev_get_queue(rs->dev[i].rdev.bdev);
+               struct request_queue *q;
+
+               if (!rs->dev[i].rdev.bdev)
+                       continue;
 
+               q = bdev_get_queue(rs->dev[i].rdev.bdev);
                if (!q || !blk_queue_discard(q))
                        return;
 
index d1600d2..f8b37d4 100644 (file)
@@ -159,8 +159,10 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
                sc->stripes_shift = __ffs(stripes);
 
        r = dm_set_target_max_io_len(ti, chunk_size);
-       if (r)
+       if (r) {
+               kfree(sc);
                return r;
+       }
 
        ti->num_flush_bios = stripes;
        ti->num_discard_bios = stripes;
index 4843801..0f86d80 100644 (file)
@@ -1936,6 +1936,14 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio)
                return DM_MAPIO_SUBMITTED;
        }
 
+       /*
+        * We must hold the virtual cell before doing the lookup, otherwise
+        * there's a race with discard.
+        */
+       build_virtual_key(tc->td, block, &key);
+       if (dm_bio_detain(tc->pool->prison, &key, bio, &cell1, &cell_result))
+               return DM_MAPIO_SUBMITTED;
+
        r = dm_thin_find_block(td, block, 0, &result);
 
        /*
@@ -1959,13 +1967,10 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio)
                         * shared flag will be set in their case.
                         */
                        thin_defer_bio(tc, bio);
+                       cell_defer_no_holder_no_free(tc, &cell1);
                        return DM_MAPIO_SUBMITTED;
                }
 
-               build_virtual_key(tc->td, block, &key);
-               if (dm_bio_detain(tc->pool->prison, &key, bio, &cell1, &cell_result))
-                       return DM_MAPIO_SUBMITTED;
-
                build_data_key(tc->td, result.block, &key);
                if (dm_bio_detain(tc->pool->prison, &key, bio, &cell2, &cell_result)) {
                        cell_defer_no_holder_no_free(tc, &cell1);
@@ -1986,6 +1991,7 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio)
                         * of doing so.
                         */
                        handle_unserviceable_bio(tc->pool, bio);
+                       cell_defer_no_holder_no_free(tc, &cell1);
                        return DM_MAPIO_SUBMITTED;
                }
                /* fall through */
@@ -1996,6 +2002,7 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio)
                 * provide the hint to load the metadata into cache.
                 */
                thin_defer_bio(tc, bio);
+               cell_defer_no_holder_no_free(tc, &cell1);
                return DM_MAPIO_SUBMITTED;
 
        default:
@@ -2005,6 +2012,7 @@ static int thin_bio_map(struct dm_target *ti, struct bio *bio)
                 * pool is switched to fail-io mode.
                 */
                bio_io_error(bio);
+               cell_defer_no_holder_no_free(tc, &cell1);
                return DM_MAPIO_SUBMITTED;
        }
 }
index 4dfa15d..9233c71 100644 (file)
@@ -5121,6 +5121,7 @@ static int md_set_readonly(struct mddev *mddev, struct block_device *bdev)
                printk("md: %s still in use.\n",mdname(mddev));
                if (did_freeze) {
                        clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
+                       set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
                        md_wakeup_thread(mddev->thread);
                }
                err = -EBUSY;
@@ -5135,6 +5136,8 @@ static int md_set_readonly(struct mddev *mddev, struct block_device *bdev)
                mddev->ro = 1;
                set_disk_ro(mddev->gendisk, 1);
                clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
+               set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
+               md_wakeup_thread(mddev->thread);
                sysfs_notify_dirent_safe(mddev->sysfs_state);
                err = 0;
        }
@@ -5178,6 +5181,7 @@ static int do_md_stop(struct mddev *mddev, int mode,
                mutex_unlock(&mddev->open_mutex);
                if (did_freeze) {
                        clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
+                       set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
                        md_wakeup_thread(mddev->thread);
                }
                return -EBUSY;
index 37d367b..bf2b80d 100644 (file)
@@ -42,6 +42,12 @@ struct btree_node {
 } __packed;
 
 
+/*
+ * Locks a block using the btree node validator.
+ */
+int bn_read_lock(struct dm_btree_info *info, dm_block_t b,
+                struct dm_block **result);
+
 void inc_children(struct dm_transaction_manager *tm, struct btree_node *n,
                  struct dm_btree_value_type *vt);
 
index cf9fd67..1b5e13e 100644 (file)
@@ -92,7 +92,7 @@ struct dm_block_validator btree_node_validator = {
 
 /*----------------------------------------------------------------*/
 
-static int bn_read_lock(struct dm_btree_info *info, dm_block_t b,
+int bn_read_lock(struct dm_btree_info *info, dm_block_t b,
                 struct dm_block **result)
 {
        return dm_tm_read_lock(info->tm, b, &btree_node_validator, result);
index 416060c..200ac12 100644 (file)
@@ -847,22 +847,26 @@ EXPORT_SYMBOL_GPL(dm_btree_find_lowest_key);
  * FIXME: We shouldn't use a recursive algorithm when we have limited stack
  * space.  Also this only works for single level trees.
  */
-static int walk_node(struct ro_spine *s, dm_block_t block,
+static int walk_node(struct dm_btree_info *info, dm_block_t block,
                     int (*fn)(void *context, uint64_t *keys, void *leaf),
                     void *context)
 {
        int r;
        unsigned i, nr;
+       struct dm_block *node;
        struct btree_node *n;
        uint64_t keys;
 
-       r = ro_step(s, block);
-       n = ro_node(s);
+       r = bn_read_lock(info, block, &node);
+       if (r)
+               return r;
+
+       n = dm_block_data(node);
 
        nr = le32_to_cpu(n->header.nr_entries);
        for (i = 0; i < nr; i++) {
                if (le32_to_cpu(n->header.flags) & INTERNAL_NODE) {
-                       r = walk_node(s, value64(n, i), fn, context);
+                       r = walk_node(info, value64(n, i), fn, context);
                        if (r)
                                goto out;
                } else {
@@ -874,7 +878,7 @@ static int walk_node(struct ro_spine *s, dm_block_t block,
        }
 
 out:
-       ro_pop(s);
+       dm_tm_unlock(info->tm, node);
        return r;
 }
 
@@ -882,15 +886,7 @@ int dm_btree_walk(struct dm_btree_info *info, dm_block_t root,
                  int (*fn)(void *context, uint64_t *keys, void *leaf),
                  void *context)
 {
-       int r;
-       struct ro_spine spine;
-
        BUG_ON(info->levels > 1);
-
-       init_ro_spine(&spine, info);
-       r = walk_node(&spine, root, fn, context);
-       exit_ro_spine(&spine);
-
-       return r;
+       return walk_node(info, root, fn, context);
 }
 EXPORT_SYMBOL_GPL(dm_btree_walk);
index b8579ee..2cf3057 100644 (file)
@@ -962,6 +962,11 @@ static int dvb_frontend_clear_cache(struct dvb_frontend *fe)
        case SYS_ATSC:
                c->modulation = VSB_8;
                break;
+       case SYS_ISDBS:
+               c->symbol_rate = 28860000;
+               c->rolloff = ROLLOFF_35;
+               c->bandwidth_hz = c->symbol_rate / 100 * 135;
+               break;
        default:
                c->modulation = QAM_AUTO;
                break;
@@ -2072,6 +2077,7 @@ static int dtv_set_frontend(struct dvb_frontend *fe)
                break;
        case SYS_DVBS:
        case SYS_TURBO:
+       case SYS_ISDBS:
                rolloff = 135;
                break;
        case SYS_DVBS2:
index 335daef..9d0d034 100644 (file)
@@ -864,6 +864,13 @@ struct dvb_frontend *ds3000_attach(const struct ds3000_config *config,
        memcpy(&state->frontend.ops, &ds3000_ops,
                        sizeof(struct dvb_frontend_ops));
        state->frontend.demodulator_priv = state;
+
+       /*
+        * Some devices like T480 starts with voltage on. Be sure
+        * to turn voltage off during init, as this can otherwise
+        * interfere with Unicable SCR systems.
+        */
+       ds3000_set_voltage(&state->frontend, SEC_VOLTAGE_OFF);
        return &state->frontend;
 
 error3:
index 9b684d5..15bf431 100644 (file)
@@ -266,7 +266,7 @@ int sp2_ci_poll_slot_status(struct dvb_ca_en50221 *en50221,
        return s->status;
 }
 
-int sp2_init(struct sp2 *s)
+static int sp2_init(struct sp2 *s)
 {
        int ret = 0;
        u8 buf;
@@ -348,7 +348,7 @@ err:
        return ret;
 }
 
-int sp2_exit(struct i2c_client *client)
+static int sp2_exit(struct i2c_client *client)
 {
        struct sp2 *s;
 
index d9905fb..b35d65c 100644 (file)
@@ -216,32 +216,30 @@ static int tc90522s_get_frontend(struct dvb_frontend *fe)
        c->delivery_system = SYS_ISDBS;
 
        layers = 0;
-       ret = reg_read(state, 0xe8, val, 3);
+       ret = reg_read(state, 0xe6, val, 5);
        if (ret == 0) {
-               int slots;
                u8 v;
 
+               c->stream_id = val[0] << 8 | val[1];
+
                /* high/single layer */
-               v = (val[0] & 0x70) >> 4;
+               v = (val[2] & 0x70) >> 4;
                c->modulation = (v == 7) ? PSK_8 : QPSK;
                c->fec_inner = fec_conv_sat[v];
                c->layer[0].fec = c->fec_inner;
                c->layer[0].modulation = c->modulation;
-               c->layer[0].segment_count = val[1] & 0x3f; /* slots */
+               c->layer[0].segment_count = val[3] & 0x3f; /* slots */
 
                /* low layer */
-               v = (val[0] & 0x07);
+               v = (val[2] & 0x07);
                c->layer[1].fec = fec_conv_sat[v];
                if (v == 0)  /* no low layer */
                        c->layer[1].segment_count = 0;
                else
-                       c->layer[1].segment_count = val[2] & 0x3f; /* slots */
+                       c->layer[1].segment_count = val[4] & 0x3f; /* slots */
                /* actually, BPSK if v==1, but not defined in fe_modulation_t */
                c->layer[1].modulation = QPSK;
                layers = (v > 0) ? 2 : 1;
-
-               slots =  c->layer[0].segment_count +  c->layer[1].segment_count;
-               c->symbol_rate = 28860000 * slots / 48;
        }
 
        /* statistics */
@@ -363,7 +361,7 @@ static int tc90522t_get_frontend(struct dvb_frontend *fe)
                u8 v;
 
                c->isdbt_partial_reception = val[0] & 0x01;
-               c->isdbt_sb_mode = (val[0] & 0xc0) == 0x01;
+               c->isdbt_sb_mode = (val[0] & 0xc0) == 0x40;
 
                /* layer A */
                v = (val[2] & 0x78) >> 3;
index 932ed9b..b10aaed 100644 (file)
@@ -2190,7 +2190,7 @@ static int smiapp_set_selection(struct v4l2_subdev *subdev,
                ret = smiapp_set_compose(subdev, fh, sel);
                break;
        default:
-               BUG();
+               ret = -EINVAL;
        }
 
        mutex_unlock(&sensor->mutex);
index 331edda..3bd386c 100644 (file)
@@ -1078,7 +1078,7 @@ static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist,
        for (line = 0; line < lines; line++) {
                while (offset && offset >= sg_dma_len(sg)) {
                        offset -= sg_dma_len(sg);
-                       sg++;
+                       sg = sg_next(sg);
                }
 
                if (lpi && line > 0 && !(line % lpi))
@@ -1101,14 +1101,14 @@ static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist,
                        *(rp++) = cpu_to_le32(0); /* bits 63-32 */
                        todo -= (sg_dma_len(sg)-offset);
                        offset = 0;
-                       sg++;
+                       sg = sg_next(sg);
                        while (todo > sg_dma_len(sg)) {
                                *(rp++) = cpu_to_le32(RISC_WRITE|
                                                    sg_dma_len(sg));
                                *(rp++) = cpu_to_le32(sg_dma_address(sg));
                                *(rp++) = cpu_to_le32(0); /* bits 63-32 */
                                todo -= sg_dma_len(sg);
-                               sg++;
+                               sg = sg_next(sg);
                        }
                        *(rp++) = cpu_to_le32(RISC_WRITE|RISC_EOL|todo);
                        *(rp++) = cpu_to_le32(sg_dma_address(sg));
index 172583d..8cbe6b4 100644 (file)
@@ -105,11 +105,8 @@ static irqreturn_t solo_isr(int irq, void *data)
        if (!status)
                return IRQ_NONE;
 
-       if (status & ~solo_dev->irq_mask) {
-               solo_reg_write(solo_dev, SOLO_IRQ_STAT,
-                              status & ~solo_dev->irq_mask);
-               status &= solo_dev->irq_mask;
-       }
+       /* Acknowledge all interrupts immediately */
+       solo_reg_write(solo_dev, SOLO_IRQ_STAT, status);
 
        if (status & SOLO_IRQ_PCI_ERR)
                solo_p2m_error_isr(solo_dev);
@@ -132,9 +129,6 @@ static irqreturn_t solo_isr(int irq, void *data)
        if (status & SOLO_IRQ_G723)
                solo_g723_isr(solo_dev);
 
-       /* Clear all interrupts handled */
-       solo_reg_write(solo_dev, SOLO_IRQ_STAT, status);
-
        return IRQ_HANDLED;
 }
 
index 2c61a62..686c3c2 100644 (file)
@@ -100,11 +100,9 @@ MODULE_PARM_DESC(ccs_out_mode, " output crop/compose/scale mode:\n"
                           "\t\t    bit 0=crop, 1=compose, 2=scale,\n"
                           "\t\t    -1=user-controlled (default)");
 
-static unsigned multiplanar[VIVID_MAX_DEVS];
+static unsigned multiplanar[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 1 };
 module_param_array(multiplanar, uint, NULL, 0444);
-MODULE_PARM_DESC(multiplanar, " 0 (default) is alternating single and multiplanar devices,\n"
-                             "\t\t    1 is single planar devices,\n"
-                             "\t\t    2 is multiplanar devices");
+MODULE_PARM_DESC(multiplanar, " 1 (default) creates a single planar device, 2 creates a multiplanar device.");
 
 /* Default: video + vbi-cap (raw and sliced) + radio rx + radio tx + sdr + vbi-out + vid-out */
 static unsigned node_types[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 0x1d3d };
@@ -669,10 +667,7 @@ static int __init vivid_create_instance(int inst)
        /* start detecting feature set */
 
        /* do we use single- or multi-planar? */
-       if (multiplanar[inst] == 0)
-               dev->multiplanar = inst & 1;
-       else
-               dev->multiplanar = multiplanar[inst] > 1;
+       dev->multiplanar = multiplanar[inst] > 1;
        v4l2_info(&dev->v4l2_dev, "using %splanar format API\n",
                        dev->multiplanar ? "multi" : "single ");
 
index b8837dd..65f80b8 100644 (file)
@@ -1678,7 +1678,8 @@ static void imon_incoming_packet(struct imon_context *ictx,
                if (press_type == 0)
                        rc_keyup(ictx->rdev);
                else {
-                       if (ictx->rc_type == RC_BIT_RC6_MCE)
+                       if (ictx->rc_type == RC_BIT_RC6_MCE ||
+                           ictx->rc_type == RC_BIT_OTHER)
                                rc_keydown(ictx->rdev,
                                           ictx->rc_type == RC_BIT_RC6_MCE ? RC_TYPE_RC6_MCE : RC_TYPE_OTHER,
                                           ictx->rc_scancode, ictx->rc_toggle);
index 08bbd4f..b0df629 100644 (file)
@@ -297,7 +297,7 @@ static int hix5hd2_ir_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 static int hix5hd2_ir_suspend(struct device *dev)
 {
        struct hix5hd2_ir_priv *priv = dev_get_drvdata(dev);
index 2ef7639..84fa6e9 100644 (file)
@@ -53,7 +53,7 @@ static int ir_rc5_decode(struct rc_dev *dev, struct ir_raw_event ev)
        u32 scancode;
        enum rc_type protocol;
 
-       if (!(dev->enabled_protocols & (RC_BIT_RC5 | RC_BIT_RC5X)))
+       if (!(dev->enabled_protocols & (RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ)))
                return 0;
 
        if (!is_timing_event(ev)) {
index f1f098e..d16bc67 100644 (file)
@@ -259,8 +259,8 @@ again:
                        case 32:
                                if ((scancode & RC6_6A_LCC_MASK) == RC6_6A_MCE_CC) {
                                        protocol = RC_TYPE_RC6_MCE;
-                                       scancode &= ~RC6_6A_MCE_TOGGLE_MASK;
                                        toggle = !!(scancode & RC6_6A_MCE_TOGGLE_MASK);
+                                       scancode &= ~RC6_6A_MCE_TOGGLE_MASK;
                                } else {
                                        protocol = RC_BIT_RC6_6A_32;
                                        toggle = 0;
index e8fff2a..b732ac6 100644 (file)
@@ -262,7 +262,6 @@ int ir_raw_event_register(struct rc_dev *dev)
                return -ENOMEM;
 
        dev->raw->dev = dev;
-       dev->enabled_protocols = ~0;
        dev->change_protocol = change_protocol;
        rc = kfifo_alloc(&dev->raw->kfifo,
                         sizeof(struct ir_raw_event) * MAX_IR_EVENT_SIZE,
index a7991c7..8d3b74c 100644 (file)
@@ -1421,6 +1421,8 @@ int rc_register_device(struct rc_dev *dev)
 
        if (dev->change_protocol) {
                u64 rc_type = (1 << rc_map->rc_type);
+               if (dev->driver_type == RC_DRIVER_IR_RAW)
+                       rc_type |= RC_BIT_LIRC;
                rc = dev->change_protocol(dev, &rc_type);
                if (rc < 0)
                        goto out_raw;
index ccc0009..1c0dbf4 100644 (file)
@@ -632,7 +632,7 @@ static void s2255_fillbuff(struct s2255_vc *vc,
                        break;
                case V4L2_PIX_FMT_JPEG:
                case V4L2_PIX_FMT_MJPEG:
-                       buf->vb.v4l2_buf.length = jpgsize;
+                       vb2_set_plane_payload(&buf->vb, 0, jpgsize);
                        memcpy(vbuf, tmpbuf, jpgsize);
                        break;
                case V4L2_PIX_FMT_YUV422P:
index cf008f4..711773e 100644 (file)
@@ -240,7 +240,7 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
                goto err_irq_charger;
        }
 
-       ret = regmap_add_irq_chip(max77693->regmap, max77693->irq,
+       ret = regmap_add_irq_chip(max77693->regmap_muic, max77693->irq,
                                IRQF_ONESHOT | IRQF_SHARED |
                                IRQF_TRIGGER_FALLING, 0,
                                &max77693_muic_irq_chip,
@@ -250,6 +250,17 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
                goto err_irq_muic;
        }
 
+       /* Unmask interrupts from all blocks in interrupt source register */
+       ret = regmap_update_bits(max77693->regmap,
+                               MAX77693_PMIC_REG_INTSRC_MASK,
+                               SRC_IRQ_ALL, (unsigned int)~SRC_IRQ_ALL);
+       if (ret < 0) {
+               dev_err(max77693->dev,
+                       "Could not unmask interrupts in INTSRC: %d\n",
+                       ret);
+               goto err_intsrc;
+       }
+
        pm_runtime_set_active(max77693->dev);
 
        ret = mfd_add_devices(max77693->dev, -1, max77693_devs,
@@ -261,6 +272,7 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
 
 err_mfd:
        mfd_remove_devices(max77693->dev);
+err_intsrc:
        regmap_del_irq_chip(max77693->irq, max77693->irq_data_muic);
 err_irq_muic:
        regmap_del_irq_chip(max77693->irq, max77693->irq_data_charger);
index f2643c2..30f7ca8 100644 (file)
@@ -947,6 +947,7 @@ static void rtsx_pci_idle_work(struct work_struct *work)
        mutex_unlock(&pcr->pcr_mutex);
 }
 
+#ifdef CONFIG_PM
 static void rtsx_pci_power_off(struct rtsx_pcr *pcr, u8 pm_state)
 {
        if (pcr->ops->turn_off_led)
@@ -961,6 +962,7 @@ static void rtsx_pci_power_off(struct rtsx_pcr *pcr, u8 pm_state)
        if (pcr->ops->force_power_down)
                pcr->ops->force_power_down(pcr, pm_state);
 }
+#endif
 
 static int rtsx_pci_init_hw(struct rtsx_pcr *pcr)
 {
index 2d045f2..bee0abf 100644 (file)
@@ -269,7 +269,7 @@ int stmpe_remove(struct stmpe *stmpe);
 #define STMPE24XX_REG_CHIP_ID          0x80
 #define STMPE24XX_REG_IEGPIOR_LSB      0x18
 #define STMPE24XX_REG_ISGPIOR_MSB      0x19
-#define STMPE24XX_REG_GPMR_LSB         0xA5
+#define STMPE24XX_REG_GPMR_LSB         0xA4
 #define STMPE24XX_REG_GPSR_LSB         0x85
 #define STMPE24XX_REG_GPCR_LSB         0x88
 #define STMPE24XX_REG_GPDR_LSB         0x8B
index cf92a6d..50f9091 100644 (file)
@@ -44,6 +44,15 @@ static u8 twl4030_start_script_address = 0x2b;
 #define PWR_DEVSLP             BIT(1)
 #define PWR_DEVOFF             BIT(0)
 
+/* Register bits for CFG_P1_TRANSITION (also for P2 and P3) */
+#define STARTON_SWBUG          BIT(7)  /* Start on watchdog */
+#define STARTON_VBUS           BIT(5)  /* Start on VBUS */
+#define STARTON_VBAT           BIT(4)  /* Start on battery insert */
+#define STARTON_RTC            BIT(3)  /* Start on RTC */
+#define STARTON_USB            BIT(2)  /* Start on USB host */
+#define STARTON_CHG            BIT(1)  /* Start on charger */
+#define STARTON_PWON           BIT(0)  /* Start on PWRON button */
+
 #define SEQ_OFFSYNC            (1 << 0)
 
 #define PHY_TO_OFF_PM_MASTER(p)                (p - 0x36)
@@ -606,6 +615,44 @@ twl4030_power_configure_resources(const struct twl4030_power_data *pdata)
        return 0;
 }
 
+static int twl4030_starton_mask_and_set(u8 bitmask, u8 bitvalues)
+{
+       u8 regs[3] = { TWL4030_PM_MASTER_CFG_P1_TRANSITION,
+                      TWL4030_PM_MASTER_CFG_P2_TRANSITION,
+                      TWL4030_PM_MASTER_CFG_P3_TRANSITION, };
+       u8 val;
+       int i, err;
+
+       err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1,
+                              TWL4030_PM_MASTER_PROTECT_KEY);
+       if (err)
+               goto relock;
+       err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER,
+                              TWL4030_PM_MASTER_KEY_CFG2,
+                              TWL4030_PM_MASTER_PROTECT_KEY);
+       if (err)
+               goto relock;
+
+       for (i = 0; i < sizeof(regs); i++) {
+               err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER,
+                                     &val, regs[i]);
+               if (err)
+                       break;
+               val = (~bitmask & val) | (bitmask & bitvalues);
+               err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER,
+                                      val, regs[i]);
+               if (err)
+                       break;
+       }
+
+       if (err)
+               pr_err("TWL4030 Register access failed: %i\n", err);
+
+relock:
+       return twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0,
+                               TWL4030_PM_MASTER_PROTECT_KEY);
+}
+
 /*
  * In master mode, start the power off sequence.
  * After a successful execution, TWL shuts down the power to the SoC
@@ -615,6 +662,11 @@ void twl4030_power_off(void)
 {
        int err;
 
+       /* Disable start on charger or VBUS as it can break poweroff */
+       err = twl4030_starton_mask_and_set(STARTON_VBUS | STARTON_CHG, 0);
+       if (err)
+               pr_err("TWL4030 Unable to configure start-up\n");
+
        err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, PWR_DEVOFF,
                               TWL4030_PM_MASTER_P1_SW_EVENTS);
        if (err)
index e00f534..3c2b8f9 100644 (file)
@@ -93,8 +93,9 @@ static int vprbrd_probe(struct usb_interface *interface,
                 version >> 8, version & 0xff,
                 vb->usb_dev->bus->busnum, vb->usb_dev->devnum);
 
-       ret = mfd_add_devices(&interface->dev, -1, vprbrd_devs,
-                               ARRAY_SIZE(vprbrd_devs), NULL, 0, NULL);
+       ret = mfd_add_devices(&interface->dev, PLATFORM_DEVID_AUTO,
+                               vprbrd_devs, ARRAY_SIZE(vprbrd_devs), NULL, 0,
+                               NULL);
        if (ret != 0) {
                dev_err(&interface->dev, "Failed to add mfd devices to core.");
                goto error;
index 03c53b7..270d58a 100644 (file)
@@ -311,7 +311,8 @@ int mmc_of_parse(struct mmc_host *host)
        struct device_node *np;
        u32 bus_width;
        int len, ret;
-       bool cap_invert, gpio_invert;
+       bool cd_cap_invert, cd_gpio_invert = false;
+       bool ro_cap_invert, ro_gpio_invert = false;
 
        if (!host->parent || !host->parent->of_node)
                return 0;
@@ -359,16 +360,13 @@ int mmc_of_parse(struct mmc_host *host)
        if (of_find_property(np, "non-removable", &len)) {
                host->caps |= MMC_CAP_NONREMOVABLE;
        } else {
-               if (of_property_read_bool(np, "cd-inverted"))
-                       cap_invert = true;
-               else
-                       cap_invert = false;
+               cd_cap_invert = of_property_read_bool(np, "cd-inverted");
 
                if (of_find_property(np, "broken-cd", &len))
                        host->caps |= MMC_CAP_NEEDS_POLL;
 
                ret = mmc_gpiod_request_cd(host, "cd", 0, true,
-                                          0, &gpio_invert);
+                                          0, &cd_gpio_invert);
                if (ret) {
                        if (ret == -EPROBE_DEFER)
                                return ret;
@@ -391,17 +389,14 @@ int mmc_of_parse(struct mmc_host *host)
                 * both inverted, the end result is that the CD line is
                 * not inverted.
                 */
-               if (cap_invert ^ gpio_invert)
+               if (cd_cap_invert ^ cd_gpio_invert)
                        host->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
        }
 
        /* Parse Write Protection */
-       if (of_property_read_bool(np, "wp-inverted"))
-               cap_invert = true;
-       else
-               cap_invert = false;
+       ro_cap_invert = of_property_read_bool(np, "wp-inverted");
 
-       ret = mmc_gpiod_request_ro(host, "wp", 0, false, 0, &gpio_invert);
+       ret = mmc_gpiod_request_ro(host, "wp", 0, false, 0, &ro_gpio_invert);
        if (ret) {
                if (ret == -EPROBE_DEFER)
                        goto out;
@@ -414,7 +409,7 @@ int mmc_of_parse(struct mmc_host *host)
                dev_info(host->parent, "Got WP GPIO\n");
 
        /* See the comment on CD inversion above */
-       if (cap_invert ^ gpio_invert)
+       if (ro_cap_invert ^ ro_gpio_invert)
                host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
 
        if (of_find_property(np, "cap-sd-highspeed", &len))
index c9ac06c..a5115fb 100644 (file)
@@ -2471,7 +2471,8 @@ static void bond_loadbalance_arp_mon(struct work_struct *work)
                        bond_slave_state_change(bond);
                        if (BOND_MODE(bond) == BOND_MODE_XOR)
                                bond_update_slave_arr(bond, NULL);
-               } else if (do_failover) {
+               }
+               if (do_failover) {
                        block_netpoll_tx();
                        bond_select_active_slave(bond);
                        unblock_netpoll_tx();
index c13d83e..45f09a6 100644 (file)
@@ -225,7 +225,12 @@ static int bond_changelink(struct net_device *bond_dev,
 
                bond_option_arp_ip_targets_clear(bond);
                nla_for_each_nested(attr, data[IFLA_BOND_ARP_IP_TARGET], rem) {
-                       __be32 target = nla_get_be32(attr);
+                       __be32 target;
+
+                       if (nla_len(attr) < sizeof(target))
+                               return -EINVAL;
+
+                       target = nla_get_be32(attr);
 
                        bond_opt_initval(&newval, (__force u64)target);
                        err = __bond_opt_set(bond, BOND_OPT_ARP_TARGETS,
index 02492d2..2cfe501 100644 (file)
@@ -110,7 +110,7 @@ static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt,
        long rate;
        u64 v64;
 
-       /* Use CIA recommended sample points */
+       /* Use CiA recommended sample points */
        if (bt->sample_point) {
                sampl_pt = bt->sample_point;
        } else {
@@ -382,7 +382,7 @@ void can_free_echo_skb(struct net_device *dev, unsigned int idx)
        BUG_ON(idx >= priv->echo_skb_max);
 
        if (priv->echo_skb[idx]) {
-               kfree_skb(priv->echo_skb[idx]);
+               dev_kfree_skb_any(priv->echo_skb[idx]);
                priv->echo_skb[idx] = NULL;
        }
 }
index fca5482..04f20dd 100644 (file)
@@ -1,4 +1,5 @@
 config CAN_M_CAN
+       depends on HAS_IOMEM
        tristate "Bosch M_CAN devices"
        ---help---
          Say Y here if you want to support for Bosch M_CAN controller.
index 10d571e..d7bc462 100644 (file)
@@ -105,14 +105,36 @@ enum m_can_mram_cfg {
        MRAM_CFG_NUM,
 };
 
+/* Fast Bit Timing & Prescaler Register (FBTP) */
+#define FBTR_FBRP_MASK         0x1f
+#define FBTR_FBRP_SHIFT                16
+#define FBTR_FTSEG1_SHIFT      8
+#define FBTR_FTSEG1_MASK       (0xf << FBTR_FTSEG1_SHIFT)
+#define FBTR_FTSEG2_SHIFT      4
+#define FBTR_FTSEG2_MASK       (0x7 << FBTR_FTSEG2_SHIFT)
+#define FBTR_FSJW_SHIFT                0
+#define FBTR_FSJW_MASK         0x3
+
 /* Test Register (TEST) */
 #define TEST_LBCK      BIT(4)
 
 /* CC Control Register(CCCR) */
-#define CCCR_TEST      BIT(7)
-#define CCCR_MON       BIT(5)
-#define CCCR_CCE       BIT(1)
-#define CCCR_INIT      BIT(0)
+#define CCCR_TEST              BIT(7)
+#define CCCR_CMR_MASK          0x3
+#define CCCR_CMR_SHIFT         10
+#define CCCR_CMR_CANFD         0x1
+#define CCCR_CMR_CANFD_BRS     0x2
+#define CCCR_CMR_CAN           0x3
+#define CCCR_CME_MASK          0x3
+#define CCCR_CME_SHIFT         8
+#define CCCR_CME_CAN           0
+#define CCCR_CME_CANFD         0x1
+#define CCCR_CME_CANFD_BRS     0x2
+#define CCCR_TEST              BIT(7)
+#define CCCR_MON               BIT(5)
+#define CCCR_CCE               BIT(1)
+#define CCCR_INIT              BIT(0)
+#define CCCR_CANFD             0x10
 
 /* Bit Timing & Prescaler Register (BTP) */
 #define BTR_BRP_MASK           0x3ff
@@ -204,6 +226,7 @@ enum m_can_mram_cfg {
 
 /* Rx Buffer / FIFO Element Size Configuration (RXESC) */
 #define M_CAN_RXESC_8BYTES     0x0
+#define M_CAN_RXESC_64BYTES    0x777
 
 /* Tx Buffer Configuration(TXBC) */
 #define TXBC_NDTB_OFF          16
@@ -211,6 +234,7 @@ enum m_can_mram_cfg {
 
 /* Tx Buffer Element Size Configuration(TXESC) */
 #define TXESC_TBDS_8BYTES      0x0
+#define TXESC_TBDS_64BYTES     0x7
 
 /* Tx Event FIFO Con.guration (TXEFC) */
 #define TXEFC_EFS_OFF          16
@@ -219,11 +243,11 @@ enum m_can_mram_cfg {
 /* Message RAM Configuration (in bytes) */
 #define SIDF_ELEMENT_SIZE      4
 #define XIDF_ELEMENT_SIZE      8
-#define RXF0_ELEMENT_SIZE      16
-#define RXF1_ELEMENT_SIZE      16
+#define RXF0_ELEMENT_SIZE      72
+#define RXF1_ELEMENT_SIZE      72
 #define RXB_ELEMENT_SIZE       16
 #define TXE_ELEMENT_SIZE       8
-#define TXB_ELEMENT_SIZE       16
+#define TXB_ELEMENT_SIZE       72
 
 /* Message RAM Elements */
 #define M_CAN_FIFO_ID          0x0
@@ -231,11 +255,17 @@ enum m_can_mram_cfg {
 #define M_CAN_FIFO_DATA(n)     (0x8 + ((n) << 2))
 
 /* Rx Buffer Element */
+/* R0 */
 #define RX_BUF_ESI             BIT(31)
 #define RX_BUF_XTD             BIT(30)
 #define RX_BUF_RTR             BIT(29)
+/* R1 */
+#define RX_BUF_ANMF            BIT(31)
+#define RX_BUF_EDL             BIT(21)
+#define RX_BUF_BRS             BIT(20)
 
 /* Tx Buffer Element */
+/* R0 */
 #define TX_BUF_XTD             BIT(30)
 #define TX_BUF_RTR             BIT(29)
 
@@ -296,6 +326,7 @@ static inline void m_can_config_endisable(const struct m_can_priv *priv,
        if (enable) {
                /* enable m_can configuration */
                m_can_write(priv, M_CAN_CCCR, cccr | CCCR_INIT);
+               udelay(5);
                /* CCCR.CCE can only be set/reset while CCCR.INIT = '1' */
                m_can_write(priv, M_CAN_CCCR, cccr | CCCR_INIT | CCCR_CCE);
        } else {
@@ -326,41 +357,67 @@ static inline void m_can_disable_all_interrupts(const struct m_can_priv *priv)
        m_can_write(priv, M_CAN_ILE, 0x0);
 }
 
-static void m_can_read_fifo(const struct net_device *dev, struct can_frame *cf,
-                           u32 rxfs)
+static void m_can_read_fifo(struct net_device *dev, u32 rxfs)
 {
+       struct net_device_stats *stats = &dev->stats;
        struct m_can_priv *priv = netdev_priv(dev);
-       u32 id, fgi;
+       struct canfd_frame *cf;
+       struct sk_buff *skb;
+       u32 id, fgi, dlc;
+       int i;
 
        /* calculate the fifo get index for where to read data */
        fgi = (rxfs & RXFS_FGI_MASK) >> RXFS_FGI_OFF;
+       dlc = m_can_fifo_read(priv, fgi, M_CAN_FIFO_DLC);
+       if (dlc & RX_BUF_EDL)
+               skb = alloc_canfd_skb(dev, &cf);
+       else
+               skb = alloc_can_skb(dev, (struct can_frame **)&cf);
+       if (!skb) {
+               stats->rx_dropped++;
+               return;
+       }
+
+       if (dlc & RX_BUF_EDL)
+               cf->len = can_dlc2len((dlc >> 16) & 0x0F);
+       else
+               cf->len = get_can_dlc((dlc >> 16) & 0x0F);
+
        id = m_can_fifo_read(priv, fgi, M_CAN_FIFO_ID);
        if (id & RX_BUF_XTD)
                cf->can_id = (id & CAN_EFF_MASK) | CAN_EFF_FLAG;
        else
                cf->can_id = (id >> 18) & CAN_SFF_MASK;
 
-       if (id & RX_BUF_RTR) {
+       if (id & RX_BUF_ESI) {
+               cf->flags |= CANFD_ESI;
+               netdev_dbg(dev, "ESI Error\n");
+       }
+
+       if (!(dlc & RX_BUF_EDL) && (id & RX_BUF_RTR)) {
                cf->can_id |= CAN_RTR_FLAG;
        } else {
-               id = m_can_fifo_read(priv, fgi, M_CAN_FIFO_DLC);
-               cf->can_dlc = get_can_dlc((id >> 16) & 0x0F);
-               *(u32 *)(cf->data + 0) = m_can_fifo_read(priv, fgi,
-                                                        M_CAN_FIFO_DATA(0));
-               *(u32 *)(cf->data + 4) = m_can_fifo_read(priv, fgi,
-                                                        M_CAN_FIFO_DATA(1));
+               if (dlc & RX_BUF_BRS)
+                       cf->flags |= CANFD_BRS;
+
+               for (i = 0; i < cf->len; i += 4)
+                       *(u32 *)(cf->data + i) =
+                               m_can_fifo_read(priv, fgi,
+                                               M_CAN_FIFO_DATA(i / 4));
        }
 
        /* acknowledge rx fifo 0 */
        m_can_write(priv, M_CAN_RXF0A, fgi);
+
+       stats->rx_packets++;
+       stats->rx_bytes += cf->len;
+
+       netif_receive_skb(skb);
 }
 
 static int m_can_do_rx_poll(struct net_device *dev, int quota)
 {
        struct m_can_priv *priv = netdev_priv(dev);
-       struct net_device_stats *stats = &dev->stats;
-       struct sk_buff *skb;
-       struct can_frame *frame;
        u32 pkts = 0;
        u32 rxfs;
 
@@ -374,18 +431,7 @@ static int m_can_do_rx_poll(struct net_device *dev, int quota)
                if (rxfs & RXFS_RFL)
                        netdev_warn(dev, "Rx FIFO 0 Message Lost\n");
 
-               skb = alloc_can_skb(dev, &frame);
-               if (!skb) {
-                       stats->rx_dropped++;
-                       return pkts;
-               }
-
-               m_can_read_fifo(dev, frame, rxfs);
-
-               stats->rx_packets++;
-               stats->rx_bytes += frame->can_dlc;
-
-               netif_receive_skb(skb);
+               m_can_read_fifo(dev, rxfs);
 
                quota--;
                pkts++;
@@ -481,11 +527,23 @@ static int m_can_handle_lec_err(struct net_device *dev,
        return 1;
 }
 
+static int __m_can_get_berr_counter(const struct net_device *dev,
+                                   struct can_berr_counter *bec)
+{
+       struct m_can_priv *priv = netdev_priv(dev);
+       unsigned int ecr;
+
+       ecr = m_can_read(priv, M_CAN_ECR);
+       bec->rxerr = (ecr & ECR_REC_MASK) >> ECR_REC_SHIFT;
+       bec->txerr = ecr & ECR_TEC_MASK;
+
+       return 0;
+}
+
 static int m_can_get_berr_counter(const struct net_device *dev,
                                  struct can_berr_counter *bec)
 {
        struct m_can_priv *priv = netdev_priv(dev);
-       unsigned int ecr;
        int err;
 
        err = clk_prepare_enable(priv->hclk);
@@ -498,9 +556,7 @@ static int m_can_get_berr_counter(const struct net_device *dev,
                return err;
        }
 
-       ecr = m_can_read(priv, M_CAN_ECR);
-       bec->rxerr = (ecr & ECR_REC_MASK) >> ECR_REC_SHIFT;
-       bec->txerr = ecr & ECR_TEC_MASK;
+       __m_can_get_berr_counter(dev, bec);
 
        clk_disable_unprepare(priv->cclk);
        clk_disable_unprepare(priv->hclk);
@@ -544,7 +600,7 @@ static int m_can_handle_state_change(struct net_device *dev,
        if (unlikely(!skb))
                return 0;
 
-       m_can_get_berr_counter(dev, &bec);
+       __m_can_get_berr_counter(dev, &bec);
 
        switch (new_state) {
        case CAN_STATE_ERROR_ACTIVE:
@@ -596,14 +652,14 @@ static int m_can_handle_state_errors(struct net_device *dev, u32 psr)
 
        if ((psr & PSR_EP) &&
            (priv->can.state != CAN_STATE_ERROR_PASSIVE)) {
-               netdev_dbg(dev, "entered error warning state\n");
+               netdev_dbg(dev, "entered error passive state\n");
                work_done += m_can_handle_state_change(dev,
                                                       CAN_STATE_ERROR_PASSIVE);
        }
 
        if ((psr & PSR_BO) &&
            (priv->can.state != CAN_STATE_BUS_OFF)) {
-               netdev_dbg(dev, "entered error warning state\n");
+               netdev_dbg(dev, "entered error bus off state\n");
                work_done += m_can_handle_state_change(dev,
                                                       CAN_STATE_BUS_OFF);
        }
@@ -615,7 +671,7 @@ static void m_can_handle_other_err(struct net_device *dev, u32 irqstatus)
 {
        if (irqstatus & IR_WDI)
                netdev_err(dev, "Message RAM Watchdog event due to missing READY\n");
-       if (irqstatus & IR_BEU)
+       if (irqstatus & IR_ELO)
                netdev_err(dev, "Error Logging Overflow\n");
        if (irqstatus & IR_BEU)
                netdev_err(dev, "Bit Error Uncorrected\n");
@@ -733,10 +789,23 @@ static const struct can_bittiming_const m_can_bittiming_const = {
        .brp_inc = 1,
 };
 
+static const struct can_bittiming_const m_can_data_bittiming_const = {
+       .name = KBUILD_MODNAME,
+       .tseg1_min = 2,         /* Time segment 1 = prop_seg + phase_seg1 */
+       .tseg1_max = 16,
+       .tseg2_min = 1,         /* Time segment 2 = phase_seg2 */
+       .tseg2_max = 8,
+       .sjw_max = 4,
+       .brp_min = 1,
+       .brp_max = 32,
+       .brp_inc = 1,
+};
+
 static int m_can_set_bittiming(struct net_device *dev)
 {
        struct m_can_priv *priv = netdev_priv(dev);
        const struct can_bittiming *bt = &priv->can.bittiming;
+       const struct can_bittiming *dbt = &priv->can.data_bittiming;
        u16 brp, sjw, tseg1, tseg2;
        u32 reg_btp;
 
@@ -747,7 +816,17 @@ static int m_can_set_bittiming(struct net_device *dev)
        reg_btp = (brp << BTR_BRP_SHIFT) | (sjw << BTR_SJW_SHIFT) |
                        (tseg1 << BTR_TSEG1_SHIFT) | (tseg2 << BTR_TSEG2_SHIFT);
        m_can_write(priv, M_CAN_BTP, reg_btp);
-       netdev_dbg(dev, "setting BTP 0x%x\n", reg_btp);
+
+       if (priv->can.ctrlmode & CAN_CTRLMODE_FD) {
+               brp = dbt->brp - 1;
+               sjw = dbt->sjw - 1;
+               tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1;
+               tseg2 = dbt->phase_seg2 - 1;
+               reg_btp = (brp << FBTR_FBRP_SHIFT) | (sjw << FBTR_FSJW_SHIFT) |
+                               (tseg1 << FBTR_FTSEG1_SHIFT) |
+                               (tseg2 << FBTR_FTSEG2_SHIFT);
+               m_can_write(priv, M_CAN_FBTP, reg_btp);
+       }
 
        return 0;
 }
@@ -767,8 +846,8 @@ static void m_can_chip_config(struct net_device *dev)
 
        m_can_config_endisable(priv, true);
 
-       /* RX Buffer/FIFO Element Size 8 bytes data field */
-       m_can_write(priv, M_CAN_RXESC, M_CAN_RXESC_8BYTES);
+       /* RX Buffer/FIFO Element Size 64 bytes data field */
+       m_can_write(priv, M_CAN_RXESC, M_CAN_RXESC_64BYTES);
 
        /* Accept Non-matching Frames Into FIFO 0 */
        m_can_write(priv, M_CAN_GFC, 0x0);
@@ -777,8 +856,8 @@ static void m_can_chip_config(struct net_device *dev)
        m_can_write(priv, M_CAN_TXBC, (1 << TXBC_NDTB_OFF) |
                    priv->mcfg[MRAM_TXB].off);
 
-       /* only support 8 bytes firstly */
-       m_can_write(priv, M_CAN_TXESC, TXESC_TBDS_8BYTES);
+       /* support 64 bytes payload */
+       m_can_write(priv, M_CAN_TXESC, TXESC_TBDS_64BYTES);
 
        m_can_write(priv, M_CAN_TXEFC, (1 << TXEFC_EFS_OFF) |
                    priv->mcfg[MRAM_TXE].off);
@@ -793,7 +872,8 @@ static void m_can_chip_config(struct net_device *dev)
                    RXFC_FWM_1 | priv->mcfg[MRAM_RXF1].off);
 
        cccr = m_can_read(priv, M_CAN_CCCR);
-       cccr &= ~(CCCR_TEST | CCCR_MON);
+       cccr &= ~(CCCR_TEST | CCCR_MON | (CCCR_CMR_MASK << CCCR_CMR_SHIFT) |
+               (CCCR_CME_MASK << CCCR_CME_SHIFT));
        test = m_can_read(priv, M_CAN_TEST);
        test &= ~TEST_LBCK;
 
@@ -805,6 +885,9 @@ static void m_can_chip_config(struct net_device *dev)
                test |= TEST_LBCK;
        }
 
+       if (priv->can.ctrlmode & CAN_CTRLMODE_FD)
+               cccr |= CCCR_CME_CANFD_BRS << CCCR_CME_SHIFT;
+
        m_can_write(priv, M_CAN_CCCR, cccr);
        m_can_write(priv, M_CAN_TEST, test);
 
@@ -869,11 +952,13 @@ static struct net_device *alloc_m_can_dev(void)
 
        priv->dev = dev;
        priv->can.bittiming_const = &m_can_bittiming_const;
+       priv->can.data_bittiming_const = &m_can_data_bittiming_const;
        priv->can.do_set_mode = m_can_set_mode;
        priv->can.do_get_berr_counter = m_can_get_berr_counter;
        priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
                                        CAN_CTRLMODE_LISTENONLY |
-                                       CAN_CTRLMODE_BERR_REPORTING;
+                                       CAN_CTRLMODE_BERR_REPORTING |
+                                       CAN_CTRLMODE_FD;
 
        return dev;
 }
@@ -956,8 +1041,9 @@ static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
                                    struct net_device *dev)
 {
        struct m_can_priv *priv = netdev_priv(dev);
-       struct can_frame *cf = (struct can_frame *)skb->data;
-       u32 id;
+       struct canfd_frame *cf = (struct canfd_frame *)skb->data;
+       u32 id, cccr;
+       int i;
 
        if (can_dropped_invalid_skb(dev, skb))
                return NETDEV_TX_OK;
@@ -976,11 +1062,28 @@ static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
 
        /* message ram configuration */
        m_can_fifo_write(priv, 0, M_CAN_FIFO_ID, id);
-       m_can_fifo_write(priv, 0, M_CAN_FIFO_DLC, cf->can_dlc << 16);
-       m_can_fifo_write(priv, 0, M_CAN_FIFO_DATA(0), *(u32 *)(cf->data + 0));
-       m_can_fifo_write(priv, 0, M_CAN_FIFO_DATA(1), *(u32 *)(cf->data + 4));
+       m_can_fifo_write(priv, 0, M_CAN_FIFO_DLC, can_len2dlc(cf->len) << 16);
+
+       for (i = 0; i < cf->len; i += 4)
+               m_can_fifo_write(priv, 0, M_CAN_FIFO_DATA(i / 4),
+                                *(u32 *)(cf->data + i));
+
        can_put_echo_skb(skb, dev, 0);
 
+       if (priv->can.ctrlmode & CAN_CTRLMODE_FD) {
+               cccr = m_can_read(priv, M_CAN_CCCR);
+               cccr &= ~(CCCR_CMR_MASK << CCCR_CMR_SHIFT);
+               if (can_is_canfd_skb(skb)) {
+                       if (cf->flags & CANFD_BRS)
+                               cccr |= CCCR_CMR_CANFD_BRS << CCCR_CMR_SHIFT;
+                       else
+                               cccr |= CCCR_CMR_CANFD << CCCR_CMR_SHIFT;
+               } else {
+                       cccr |= CCCR_CMR_CAN << CCCR_CMR_SHIFT;
+               }
+               m_can_write(priv, M_CAN_CCCR, cccr);
+       }
+
        /* enable first TX buffer to start transfer  */
        m_can_write(priv, M_CAN_TXBTIE, 0x1);
        m_can_write(priv, M_CAN_TXBAR, 0x1);
@@ -992,6 +1095,7 @@ static const struct net_device_ops m_can_netdev_ops = {
        .ndo_open = m_can_open,
        .ndo_stop = m_can_close,
        .ndo_start_xmit = m_can_start_xmit,
+       .ndo_change_mtu = can_change_mtu,
 };
 
 static int register_m_can_dev(struct net_device *dev)
@@ -1009,7 +1113,7 @@ static int m_can_of_parse_mram(struct platform_device *pdev,
        struct resource *res;
        void __iomem *addr;
        u32 out_val[MRAM_CFG_LEN];
-       int ret;
+       int i, start, end, ret;
 
        /* message ram could be shared */
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");
@@ -1060,6 +1164,15 @@ static int m_can_of_parse_mram(struct platform_device *pdev,
                priv->mcfg[MRAM_TXE].off, priv->mcfg[MRAM_TXE].num,
                priv->mcfg[MRAM_TXB].off, priv->mcfg[MRAM_TXB].num);
 
+       /* initialize the entire Message RAM in use to avoid possible
+        * ECC/parity checksum errors when reading an uninitialized buffer
+        */
+       start = priv->mcfg[MRAM_SIDF].off;
+       end = priv->mcfg[MRAM_TXB].off +
+               priv->mcfg[MRAM_TXB].num * TXB_ELEMENT_SIZE;
+       for (i = start; i < end; i += 4)
+               writel(0x0, priv->mram_base + i);
+
        return 0;
 }
 
index 1abe133..9718248 100644 (file)
@@ -628,6 +628,7 @@ static const struct net_device_ops rcar_can_netdev_ops = {
        .ndo_open = rcar_can_open,
        .ndo_stop = rcar_can_close,
        .ndo_start_xmit = rcar_can_start_xmit,
+       .ndo_change_mtu = can_change_mtu,
 };
 
 static void rcar_can_rx_pkt(struct rcar_can_priv *priv)
index 8ff3424..15c00fa 100644 (file)
@@ -214,7 +214,7 @@ static int kvaser_pci_add_chan(struct pci_dev *pdev, int channel,
        struct net_device *dev;
        struct sja1000_priv *priv;
        struct kvaser_pci *board;
-       int err, init_step;
+       int err;
 
        dev = alloc_sja1000dev(sizeof(struct kvaser_pci));
        if (dev == NULL)
@@ -235,7 +235,6 @@ static int kvaser_pci_add_chan(struct pci_dev *pdev, int channel,
        if (channel == 0) {
                board->xilinx_ver =
                        ioread8(board->res_addr + XILINX_VERINT) >> 4;
-               init_step = 2;
 
                /* Assert PTADR# - we're in passive mode so the other bits are
                   not important */
@@ -264,8 +263,6 @@ static int kvaser_pci_add_chan(struct pci_dev *pdev, int channel,
        priv->irq_flags = IRQF_SHARED;
        dev->irq = pdev->irq;
 
-       init_step = 4;
-
        dev_info(&pdev->dev, "reg_base=%p conf_addr=%p irq=%d\n",
                 priv->reg_base, board->conf_addr, dev->irq);
 
index 00f2534..29d3f09 100644 (file)
@@ -434,10 +434,9 @@ static void ems_usb_read_bulk_callback(struct urb *urb)
        if (urb->actual_length > CPC_HEADER_SIZE) {
                struct ems_cpc_msg *msg;
                u8 *ibuf = urb->transfer_buffer;
-               u8 msg_count, again, start;
+               u8 msg_count, start;
 
                msg_count = ibuf[0] & ~0x80;
-               again = ibuf[0] & 0x80;
 
                start = CPC_HEADER_SIZE;
 
index b7c9e8b..c063a54 100644 (file)
@@ -464,7 +464,6 @@ static void esd_usb2_write_bulk_callback(struct urb *urb)
 {
        struct esd_tx_urb_context *context = urb->context;
        struct esd_usb2_net_priv *priv;
-       struct esd_usb2 *dev;
        struct net_device *netdev;
        size_t size = sizeof(struct esd_usb2_msg);
 
@@ -472,7 +471,6 @@ static void esd_usb2_write_bulk_callback(struct urb *urb)
 
        priv = context->priv;
        netdev = priv->netdev;
-       dev = priv->usb2;
 
        /* free up our allocated buffer */
        usb_free_coherent(urb->dev, size,
@@ -1143,6 +1141,7 @@ static void esd_usb2_disconnect(struct usb_interface *intf)
                        }
                }
                unlink_all_urbs(dev);
+               kfree(dev);
        }
 }
 
index 04b0f84..009acc8 100644 (file)
@@ -718,6 +718,7 @@ static const struct net_device_ops gs_usb_netdev_ops = {
        .ndo_open = gs_can_open,
        .ndo_stop = gs_can_close,
        .ndo_start_xmit = gs_can_start_xmit,
+       .ndo_change_mtu = can_change_mtu,
 };
 
 static struct gs_can *gs_make_candev(unsigned int channel, struct usb_interface *intf)
index 5e8b560..8a998e3 100644 (file)
@@ -300,7 +300,8 @@ static int xcan_set_bittiming(struct net_device *ndev)
 static int xcan_chip_start(struct net_device *ndev)
 {
        struct xcan_priv *priv = netdev_priv(ndev);
-       u32 err, reg_msr, reg_sr_mask;
+       u32 reg_msr, reg_sr_mask;
+       int err;
        unsigned long timeout;
 
        /* Check if it is in reset mode */
@@ -961,6 +962,7 @@ static const struct net_device_ops xcan_netdev_ops = {
        .ndo_open       = xcan_open,
        .ndo_stop       = xcan_close,
        .ndo_start_xmit = xcan_start_xmit,
+       .ndo_change_mtu = can_change_mtu,
 };
 
 /**
index b962596..4f4c2a7 100644 (file)
@@ -377,6 +377,29 @@ static irqreturn_t bcm_sf2_switch_1_isr(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+static int bcm_sf2_sw_rst(struct bcm_sf2_priv *priv)
+{
+       unsigned int timeout = 1000;
+       u32 reg;
+
+       reg = core_readl(priv, CORE_WATCHDOG_CTRL);
+       reg |= SOFTWARE_RESET | EN_CHIP_RST | EN_SW_RESET;
+       core_writel(priv, reg, CORE_WATCHDOG_CTRL);
+
+       do {
+               reg = core_readl(priv, CORE_WATCHDOG_CTRL);
+               if (!(reg & SOFTWARE_RESET))
+                       break;
+
+               usleep_range(1000, 2000);
+       } while (timeout-- > 0);
+
+       if (timeout == 0)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
 static int bcm_sf2_sw_setup(struct dsa_switch *ds)
 {
        const char *reg_names[BCM_SF2_REGS_NUM] = BCM_SF2_REGS_NAME;
@@ -404,11 +427,18 @@ static int bcm_sf2_sw_setup(struct dsa_switch *ds)
                *base = of_iomap(dn, i);
                if (*base == NULL) {
                        pr_err("unable to find register: %s\n", reg_names[i]);
-                       return -ENODEV;
+                       ret = -ENOMEM;
+                       goto out_unmap;
                }
                base++;
        }
 
+       ret = bcm_sf2_sw_rst(priv);
+       if (ret) {
+               pr_err("unable to software reset switch: %d\n", ret);
+               goto out_unmap;
+       }
+
        /* Disable all interrupts and request them */
        intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET);
        intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR);
@@ -484,7 +514,8 @@ out_free_irq0:
 out_unmap:
        base = &priv->core;
        for (i = 0; i < BCM_SF2_REGS_NUM; i++) {
-               iounmap(*base);
+               if (*base)
+                       iounmap(*base);
                base++;
        }
        return ret;
@@ -733,29 +764,6 @@ static int bcm_sf2_sw_suspend(struct dsa_switch *ds)
        return 0;
 }
 
-static int bcm_sf2_sw_rst(struct bcm_sf2_priv *priv)
-{
-       unsigned int timeout = 1000;
-       u32 reg;
-
-       reg = core_readl(priv, CORE_WATCHDOG_CTRL);
-       reg |= SOFTWARE_RESET | EN_CHIP_RST | EN_SW_RESET;
-       core_writel(priv, reg, CORE_WATCHDOG_CTRL);
-
-       do {
-               reg = core_readl(priv, CORE_WATCHDOG_CTRL);
-               if (!(reg & SOFTWARE_RESET))
-                       break;
-
-               usleep_range(1000, 2000);
-       } while (timeout-- > 0);
-
-       if (timeout == 0)
-               return -ETIMEDOUT;
-
-       return 0;
-}
-
 static int bcm_sf2_sw_resume(struct dsa_switch *ds)
 {
        struct bcm_sf2_priv *priv = ds_to_priv(ds);
index 63ea194..7ba83ff 100644 (file)
@@ -575,10 +575,24 @@ static void xgene_gmac_tx_disable(struct xgene_enet_pdata *pdata)
        xgene_enet_wr_mcx_mac(pdata, MAC_CONFIG_1_ADDR, data & ~TX_EN);
 }
 
-static void xgene_enet_reset(struct xgene_enet_pdata *pdata)
+bool xgene_ring_mgr_init(struct xgene_enet_pdata *p)
+{
+       if (!ioread32(p->ring_csr_addr + CLKEN_ADDR))
+               return false;
+
+       if (ioread32(p->ring_csr_addr + SRST_ADDR))
+               return false;
+
+       return true;
+}
+
+static int xgene_enet_reset(struct xgene_enet_pdata *pdata)
 {
        u32 val;
 
+       if (!xgene_ring_mgr_init(pdata))
+               return -ENODEV;
+
        clk_prepare_enable(pdata->clk);
        clk_disable_unprepare(pdata->clk);
        clk_prepare_enable(pdata->clk);
@@ -590,6 +604,8 @@ static void xgene_enet_reset(struct xgene_enet_pdata *pdata)
        val |= SCAN_AUTO_INCR;
        MGMT_CLOCK_SEL_SET(&val, 1);
        xgene_enet_wr_mcx_mac(pdata, MII_MGMT_CONFIG_ADDR, val);
+
+       return 0;
 }
 
 static void xgene_gport_shutdown(struct xgene_enet_pdata *pdata)
index 3855858..ec45f32 100644 (file)
@@ -104,6 +104,9 @@ enum xgene_enet_rm {
 #define BLOCK_ETH_MAC_OFFSET           0x0000
 #define BLOCK_ETH_MAC_CSR_OFFSET       0x2800
 
+#define CLKEN_ADDR                     0xc208
+#define SRST_ADDR                      0xc200
+
 #define MAC_ADDR_REG_OFFSET            0x00
 #define MAC_COMMAND_REG_OFFSET         0x04
 #define MAC_WRITE_REG_OFFSET           0x08
@@ -318,6 +321,7 @@ void xgene_enet_parse_error(struct xgene_enet_desc_ring *ring,
 
 int xgene_enet_mdio_config(struct xgene_enet_pdata *pdata);
 void xgene_enet_mdio_remove(struct xgene_enet_pdata *pdata);
+bool xgene_ring_mgr_init(struct xgene_enet_pdata *p);
 
 extern struct xgene_mac_ops xgene_gmac_ops;
 extern struct xgene_port_ops xgene_gport_ops;
index 3c208cc..1236696 100644 (file)
@@ -639,9 +639,9 @@ static int xgene_enet_create_desc_rings(struct net_device *ndev)
        struct device *dev = ndev_to_dev(ndev);
        struct xgene_enet_desc_ring *rx_ring, *tx_ring, *cp_ring;
        struct xgene_enet_desc_ring *buf_pool = NULL;
-       u8 cpu_bufnum = 0, eth_bufnum = 0;
-       u8 bp_bufnum = 0x20;
-       u16 ring_id, ring_num = 0;
+       u8 cpu_bufnum = 0, eth_bufnum = START_ETH_BUFNUM;
+       u8 bp_bufnum = START_BP_BUFNUM;
+       u16 ring_id, ring_num = START_RING_NUM;
        int ret;
 
        /* allocate rx descriptor ring */
@@ -852,7 +852,9 @@ static int xgene_enet_init_hw(struct xgene_enet_pdata *pdata)
        u16 dst_ring_num;
        int ret;
 
-       pdata->port_ops->reset(pdata);
+       ret = pdata->port_ops->reset(pdata);
+       if (ret)
+               return ret;
 
        ret = xgene_enet_create_desc_rings(ndev);
        if (ret) {
@@ -954,6 +956,7 @@ static int xgene_enet_probe(struct platform_device *pdev)
 
        return ret;
 err:
+       unregister_netdev(ndev);
        free_netdev(ndev);
        return ret;
 }
index 874e5a0..f9958fa 100644 (file)
@@ -38,6 +38,9 @@
 #define SKB_BUFFER_SIZE                (XGENE_ENET_MAX_MTU - NET_IP_ALIGN)
 #define NUM_PKT_BUF    64
 #define NUM_BUFPOOL    32
+#define START_ETH_BUFNUM       2
+#define START_BP_BUFNUM                0x22
+#define START_RING_NUM         8
 
 #define PHY_POLL_LINK_ON       (10 * HZ)
 #define PHY_POLL_LINK_OFF      (PHY_POLL_LINK_ON / 5)
@@ -83,7 +86,7 @@ struct xgene_mac_ops {
 };
 
 struct xgene_port_ops {
-       void (*reset)(struct xgene_enet_pdata *pdata);
+       int (*reset)(struct xgene_enet_pdata *pdata);
        void (*cle_bypass)(struct xgene_enet_pdata *pdata,
                           u32 dst_ring_num, u16 bufpool_id);
        void (*shutdown)(struct xgene_enet_pdata *pdata);
index c22f326..f5d4f68 100644 (file)
@@ -311,14 +311,19 @@ static void xgene_sgmac_tx_disable(struct xgene_enet_pdata *p)
        xgene_sgmac_rxtx(p, TX_EN, false);
 }
 
-static void xgene_enet_reset(struct xgene_enet_pdata *p)
+static int xgene_enet_reset(struct xgene_enet_pdata *p)
 {
+       if (!xgene_ring_mgr_init(p))
+               return -ENODEV;
+
        clk_prepare_enable(p->clk);
        clk_disable_unprepare(p->clk);
        clk_prepare_enable(p->clk);
 
        xgene_enet_ecc_init(p);
        xgene_enet_config_ring_if_assoc(p);
+
+       return 0;
 }
 
 static void xgene_enet_cle_bypass(struct xgene_enet_pdata *p,
index 67d0720..a18a9d1 100644 (file)
@@ -252,14 +252,19 @@ static void xgene_xgmac_tx_disable(struct xgene_enet_pdata *pdata)
        xgene_enet_wr_mac(pdata, AXGMAC_CONFIG_1, data & ~HSTTFEN);
 }
 
-static void xgene_enet_reset(struct xgene_enet_pdata *pdata)
+static int xgene_enet_reset(struct xgene_enet_pdata *pdata)
 {
+       if (!xgene_ring_mgr_init(pdata))
+               return -ENODEV;
+
        clk_prepare_enable(pdata->clk);
        clk_disable_unprepare(pdata->clk);
        clk_prepare_enable(pdata->clk);
 
        xgene_enet_ecc_init(pdata);
        xgene_enet_config_ring_if_assoc(pdata);
+
+       return 0;
 }
 
 static void xgene_enet_xgcle_bypass(struct xgene_enet_pdata *pdata,
index 3a6778a..531bb7c 100644 (file)
@@ -1110,7 +1110,8 @@ static int bcm_sysport_init_tx_ring(struct bcm_sysport_priv *priv,
        /* We just need one DMA descriptor which is DMA-able, since writing to
         * the port will allocate a new descriptor in its internal linked-list
         */
-       p = dma_zalloc_coherent(kdev, 1, &ring->desc_dma, GFP_KERNEL);
+       p = dma_zalloc_coherent(kdev, sizeof(struct dma_desc), &ring->desc_dma,
+                               GFP_KERNEL);
        if (!p) {
                netif_err(priv, hw, priv->netdev, "DMA alloc failed\n");
                return -ENOMEM;
@@ -1174,6 +1175,13 @@ static void bcm_sysport_fini_tx_ring(struct bcm_sysport_priv *priv,
        if (!(reg & TDMA_DISABLED))
                netdev_warn(priv->netdev, "TDMA not stopped!\n");
 
+       /* ring->cbs is the last part in bcm_sysport_init_tx_ring which could
+        * fail, so by checking this pointer we know whether the TX ring was
+        * fully initialized or not.
+        */
+       if (!ring->cbs)
+               return;
+
        napi_disable(&ring->napi);
        netif_napi_del(&ring->napi);
 
@@ -1183,7 +1191,8 @@ static void bcm_sysport_fini_tx_ring(struct bcm_sysport_priv *priv,
        ring->cbs = NULL;
 
        if (ring->desc_dma) {
-               dma_free_coherent(kdev, 1, ring->desc_cpu, ring->desc_dma);
+               dma_free_coherent(kdev, sizeof(struct dma_desc),
+                                 ring->desc_cpu, ring->desc_dma);
                ring->desc_dma = 0;
        }
        ring->size = 0;
index fdc9ec0..da1a250 100644 (file)
@@ -2140,6 +2140,12 @@ static int bcmgenet_open(struct net_device *dev)
                goto err_irq0;
        }
 
+       /* Re-configure the port multiplexer towards the PHY device */
+       bcmgenet_mii_config(priv->dev, false);
+
+       phy_connect_direct(dev, priv->phydev, bcmgenet_mii_setup,
+                          priv->phy_interface);
+
        bcmgenet_netif_start(dev);
 
        return 0;
@@ -2184,6 +2190,9 @@ static int bcmgenet_close(struct net_device *dev)
 
        bcmgenet_netif_stop(dev);
 
+       /* Really kill the PHY state machine and disconnect from it */
+       phy_disconnect(priv->phydev);
+
        /* Disable MAC receive */
        umac_enable_set(priv, CMD_RX_EN, false);
 
@@ -2685,7 +2694,7 @@ static int bcmgenet_resume(struct device *d)
 
        phy_init_hw(priv->phydev);
        /* Speed settings must be restored */
-       bcmgenet_mii_config(priv->dev);
+       bcmgenet_mii_config(priv->dev, false);
 
        /* disable ethernet MAC while updating its registers */
        umac_enable_set(priv, CMD_TX_EN | CMD_RX_EN, false);
index dbf524e..31b2da5 100644 (file)
@@ -617,9 +617,10 @@ GENET_IO_MACRO(rbuf, GENET_RBUF_OFF);
 
 /* MDIO routines */
 int bcmgenet_mii_init(struct net_device *dev);
-int bcmgenet_mii_config(struct net_device *dev);
+int bcmgenet_mii_config(struct net_device *dev, bool init);
 void bcmgenet_mii_exit(struct net_device *dev);
 void bcmgenet_mii_reset(struct net_device *dev);
+void bcmgenet_mii_setup(struct net_device *dev);
 
 /* Wake-on-LAN routines */
 void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol);
index 9ff799a..933cd7e 100644 (file)
@@ -77,7 +77,7 @@ static int bcmgenet_mii_write(struct mii_bus *bus, int phy_id,
 /* setup netdev link state when PHY link status change and
  * update UMAC and RGMII block when link up
  */
-static void bcmgenet_mii_setup(struct net_device *dev)
+void bcmgenet_mii_setup(struct net_device *dev)
 {
        struct bcmgenet_priv *priv = netdev_priv(dev);
        struct phy_device *phydev = priv->phydev;
@@ -211,7 +211,7 @@ static void bcmgenet_moca_phy_setup(struct bcmgenet_priv *priv)
        bcmgenet_sys_writel(priv, reg, SYS_PORT_CTRL);
 }
 
-int bcmgenet_mii_config(struct net_device *dev)
+int bcmgenet_mii_config(struct net_device *dev, bool init)
 {
        struct bcmgenet_priv *priv = netdev_priv(dev);
        struct phy_device *phydev = priv->phydev;
@@ -298,7 +298,8 @@ int bcmgenet_mii_config(struct net_device *dev)
                return -EINVAL;
        }
 
-       dev_info(kdev, "configuring instance for %s\n", phy_name);
+       if (init)
+               dev_info(kdev, "configuring instance for %s\n", phy_name);
 
        return 0;
 }
@@ -350,7 +351,7 @@ static int bcmgenet_mii_probe(struct net_device *dev)
         * PHY speed which is needed for bcmgenet_mii_config() to configure
         * things appropriately.
         */
-       ret = bcmgenet_mii_config(dev);
+       ret = bcmgenet_mii_config(dev, true);
        if (ret) {
                phy_disconnect(priv->phydev);
                return ret;
index dbb41c1..77f8f83 100644 (file)
@@ -8563,7 +8563,8 @@ static int tg3_init_rings(struct tg3 *tp)
                if (tnapi->rx_rcb)
                        memset(tnapi->rx_rcb, 0, TG3_RX_RCB_RING_BYTES(tp));
 
-               if (tg3_rx_prodring_alloc(tp, &tnapi->prodring)) {
+               if (tnapi->prodring.rx_std &&
+                   tg3_rx_prodring_alloc(tp, &tnapi->prodring)) {
                        tg3_free_rings(tp);
                        return -ENOMEM;
                }
index 6fe300e..4fe3360 100644 (file)
@@ -79,8 +79,9 @@ static void cxgb4_dcb_cleanup_apps(struct net_device *dev)
                app.protocol = dcb->app_priority[i].protocolid;
 
                if (dcb->dcb_version == FW_PORT_DCB_VER_IEEE) {
+                       app.priority = dcb->app_priority[i].user_prio_map;
                        app.selector = dcb->app_priority[i].sel_field + 1;
-                       err = dcb_ieee_setapp(dev, &app);
+                       err = dcb_ieee_delapp(dev, &app);
                } else {
                        app.selector = !!(dcb->app_priority[i].sel_field);
                        err = dcb_setapp(dev, &app);
@@ -122,7 +123,11 @@ void cxgb4_dcb_state_fsm(struct net_device *dev,
                case CXGB4_DCB_INPUT_FW_ENABLED: {
                        /* we're going to use Firmware DCB */
                        dcb->state = CXGB4_DCB_STATE_FW_INCOMPLETE;
-                       dcb->supported = CXGB4_DCBX_FW_SUPPORT;
+                       dcb->supported = DCB_CAP_DCBX_LLD_MANAGED;
+                       if (dcb->dcb_version == FW_PORT_DCB_VER_IEEE)
+                               dcb->supported |= DCB_CAP_DCBX_VER_IEEE;
+                       else
+                               dcb->supported |= DCB_CAP_DCBX_VER_CEE;
                        break;
                }
 
@@ -436,14 +441,17 @@ static void cxgb4_getpgtccfg(struct net_device *dev, int tc,
        *up_tc_map = (1 << tc);
 
        /* prio_type is link strict */
-       *prio_type = 0x2;
+       if (*pgid != 0xF)
+               *prio_type = 0x2;
 }
 
 static void cxgb4_getpgtccfg_tx(struct net_device *dev, int tc,
                                u8 *prio_type, u8 *pgid, u8 *bw_per,
                                u8 *up_tc_map)
 {
-       return cxgb4_getpgtccfg(dev, tc, prio_type, pgid, bw_per, up_tc_map, 1);
+       /* tc 0 is written at MSB position */
+       return cxgb4_getpgtccfg(dev, (7 - tc), prio_type, pgid, bw_per,
+                               up_tc_map, 1);
 }
 
 
@@ -451,7 +459,9 @@ static void cxgb4_getpgtccfg_rx(struct net_device *dev, int tc,
                                u8 *prio_type, u8 *pgid, u8 *bw_per,
                                u8 *up_tc_map)
 {
-       return cxgb4_getpgtccfg(dev, tc, prio_type, pgid, bw_per, up_tc_map, 0);
+       /* tc 0 is written at MSB position */
+       return cxgb4_getpgtccfg(dev, (7 - tc), prio_type, pgid, bw_per,
+                               up_tc_map, 0);
 }
 
 static void cxgb4_setpgtccfg_tx(struct net_device *dev, int tc,
@@ -461,6 +471,7 @@ static void cxgb4_setpgtccfg_tx(struct net_device *dev, int tc,
        struct fw_port_cmd pcmd;
        struct port_info *pi = netdev2pinfo(dev);
        struct adapter *adap = pi->adapter;
+       int fw_tc = 7 - tc;
        u32 _pgid;
        int err;
 
@@ -479,8 +490,8 @@ static void cxgb4_setpgtccfg_tx(struct net_device *dev, int tc,
        }
 
        _pgid = be32_to_cpu(pcmd.u.dcb.pgid.pgid);
-       _pgid &= ~(0xF << (tc * 4));
-       _pgid |= pgid << (tc * 4);
+       _pgid &= ~(0xF << (fw_tc * 4));
+       _pgid |= pgid << (fw_tc * 4);
        pcmd.u.dcb.pgid.pgid = cpu_to_be32(_pgid);
 
        INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id);
@@ -593,7 +604,7 @@ static void cxgb4_getpfccfg(struct net_device *dev, int priority, u8 *pfccfg)
            priority >= CXGB4_MAX_PRIORITY)
                *pfccfg = 0;
        else
-               *pfccfg = (pi->dcb.pfcen >> priority) & 1;
+               *pfccfg = (pi->dcb.pfcen >> (7 - priority)) & 1;
 }
 
 /* Enable/disable Priority Pause Frames for the specified Traffic Class
@@ -618,9 +629,9 @@ static void cxgb4_setpfccfg(struct net_device *dev, int priority, u8 pfccfg)
        pcmd.u.dcb.pfc.pfcen = pi->dcb.pfcen;
 
        if (pfccfg)
-               pcmd.u.dcb.pfc.pfcen |= (1 << priority);
+               pcmd.u.dcb.pfc.pfcen |= (1 << (7 - priority));
        else
-               pcmd.u.dcb.pfc.pfcen &= (~(1 << priority));
+               pcmd.u.dcb.pfc.pfcen &= (~(1 << (7 - priority)));
 
        err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
        if (err != FW_PORT_DCB_CFG_SUCCESS) {
@@ -1071,7 +1082,7 @@ static int cxgb4_cee_peer_getpg(struct net_device *dev, struct cee_pg *pg)
        pgid = be32_to_cpu(pcmd.u.dcb.pgid.pgid);
 
        for (i = 0; i < CXGB4_MAX_PRIORITY; i++)
-               pg->prio_pg[i] = (pgid >> (i * 4)) & 0xF;
+               pg->prio_pg[7 - i] = (pgid >> (i * 4)) & 0xF;
 
        INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id);
        pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE;
index 8520d55..279873c 100644 (file)
@@ -2442,9 +2442,13 @@ static unsigned int from_fw_linkcaps(unsigned int type, unsigned int caps)
                     SUPPORTED_10000baseKR_Full | SUPPORTED_1000baseKX_Full |
                     SUPPORTED_10000baseKX4_Full;
        else if (type == FW_PORT_TYPE_FIBER_XFI ||
-                type == FW_PORT_TYPE_FIBER_XAUI || type == FW_PORT_TYPE_SFP)
+                type == FW_PORT_TYPE_FIBER_XAUI || type == FW_PORT_TYPE_SFP) {
                v |= SUPPORTED_FIBRE;
-       else if (type == FW_PORT_TYPE_BP40_BA)
+               if (caps & FW_PORT_CAP_SPEED_1G)
+                       v |= SUPPORTED_1000baseT_Full;
+               if (caps & FW_PORT_CAP_SPEED_10G)
+                       v |= SUPPORTED_10000baseT_Full;
+       } else if (type == FW_PORT_TYPE_BP40_BA)
                v |= SUPPORTED_40000baseSR4_Full;
 
        if (caps & FW_PORT_CAP_ANEG)
index 5e1b314..39f2b13 100644 (file)
@@ -2914,7 +2914,8 @@ static int t4_sge_init_hard(struct adapter *adap)
 int t4_sge_init(struct adapter *adap)
 {
        struct sge *s = &adap->sge;
-       u32 sge_control, sge_conm_ctrl;
+       u32 sge_control, sge_control2, sge_conm_ctrl;
+       unsigned int ingpadboundary, ingpackboundary;
        int ret, egress_threshold;
 
        /*
@@ -2924,8 +2925,31 @@ int t4_sge_init(struct adapter *adap)
        sge_control = t4_read_reg(adap, SGE_CONTROL);
        s->pktshift = PKTSHIFT_GET(sge_control);
        s->stat_len = (sge_control & EGRSTATUSPAGESIZE_MASK) ? 128 : 64;
-       s->fl_align = 1 << (INGPADBOUNDARY_GET(sge_control) +
-                           X_INGPADBOUNDARY_SHIFT);
+
+       /* T4 uses a single control field to specify both the PCIe Padding and
+        * Packing Boundary.  T5 introduced the ability to specify these
+        * separately.  The actual Ingress Packet Data alignment boundary
+        * within Packed Buffer Mode is the maximum of these two
+        * specifications.
+        */
+       ingpadboundary = 1 << (INGPADBOUNDARY_GET(sge_control) +
+                              X_INGPADBOUNDARY_SHIFT);
+       if (is_t4(adap->params.chip)) {
+               s->fl_align = ingpadboundary;
+       } else {
+               /* T5 has a different interpretation of one of the PCIe Packing
+                * Boundary values.
+                */
+               sge_control2 = t4_read_reg(adap, SGE_CONTROL2_A);
+               ingpackboundary = INGPACKBOUNDARY_G(sge_control2);
+               if (ingpackboundary == INGPACKBOUNDARY_16B_X)
+                       ingpackboundary = 16;
+               else
+                       ingpackboundary = 1 << (ingpackboundary +
+                                               INGPACKBOUNDARY_SHIFT_X);
+
+               s->fl_align = max(ingpadboundary, ingpackboundary);
+       }
 
        if (adap->flags & USING_SOFT_PARAMS)
                ret = t4_sge_init_soft(adap);
index a9d9d74..163a2a1 100644 (file)
@@ -3129,12 +3129,51 @@ int t4_fixup_host_params(struct adapter *adap, unsigned int page_size,
                     HOSTPAGESIZEPF6(sge_hps) |
                     HOSTPAGESIZEPF7(sge_hps));
 
-       t4_set_reg_field(adap, SGE_CONTROL,
-                        INGPADBOUNDARY_MASK |
-                        EGRSTATUSPAGESIZE_MASK,
-                        INGPADBOUNDARY(fl_align_log - 5) |
-                        EGRSTATUSPAGESIZE(stat_len != 64));
-
+       if (is_t4(adap->params.chip)) {
+               t4_set_reg_field(adap, SGE_CONTROL,
+                                INGPADBOUNDARY_MASK |
+                                EGRSTATUSPAGESIZE_MASK,
+                                INGPADBOUNDARY(fl_align_log - 5) |
+                                EGRSTATUSPAGESIZE(stat_len != 64));
+       } else {
+               /* T5 introduced the separation of the Free List Padding and
+                * Packing Boundaries.  Thus, we can select a smaller Padding
+                * Boundary to avoid uselessly chewing up PCIe Link and Memory
+                * Bandwidth, and use a Packing Boundary which is large enough
+                * to avoid false sharing between CPUs, etc.
+                *
+                * For the PCI Link, the smaller the Padding Boundary the
+                * better.  For the Memory Controller, a smaller Padding
+                * Boundary is better until we cross under the Memory Line
+                * Size (the minimum unit of transfer to/from Memory).  If we
+                * have a Padding Boundary which is smaller than the Memory
+                * Line Size, that'll involve a Read-Modify-Write cycle on the
+                * Memory Controller which is never good.  For T5 the smallest
+                * Padding Boundary which we can select is 32 bytes which is
+                * larger than any known Memory Controller Line Size so we'll
+                * use that.
+                *
+                * T5 has a different interpretation of the "0" value for the
+                * Packing Boundary.  This corresponds to 16 bytes instead of
+                * the expected 32 bytes.  We never have a Packing Boundary
+                * less than 32 bytes so we can't use that special value but
+                * on the other hand, if we wanted 32 bytes, the best we can
+                * really do is 64 bytes.
+               */
+               if (fl_align <= 32) {
+                       fl_align = 64;
+                       fl_align_log = 6;
+               }
+               t4_set_reg_field(adap, SGE_CONTROL,
+                                INGPADBOUNDARY_MASK |
+                                EGRSTATUSPAGESIZE_MASK,
+                                INGPADBOUNDARY(INGPCIEBOUNDARY_32B_X) |
+                                EGRSTATUSPAGESIZE(stat_len != 64));
+               t4_set_reg_field(adap, SGE_CONTROL2_A,
+                                INGPACKBOUNDARY_V(INGPACKBOUNDARY_M),
+                                INGPACKBOUNDARY_V(fl_align_log -
+                                                INGPACKBOUNDARY_SHIFT_X));
+       }
        /*
         * Adjust various SGE Free List Host Buffer Sizes.
         *
index a1024db..8d2de10 100644 (file)
@@ -95,6 +95,7 @@
 #define X_INGPADBOUNDARY_SHIFT 5
 
 #define SGE_CONTROL 0x1008
+#define SGE_CONTROL2_A         0x1124
 #define  DCASYSTYPE             0x00080000U
 #define  RXPKTCPLMODE_MASK      0x00040000U
 #define  RXPKTCPLMODE_SHIFT     18
 #define  PKTSHIFT_SHIFT         10
 #define  PKTSHIFT(x)            ((x) << PKTSHIFT_SHIFT)
 #define  PKTSHIFT_GET(x)       (((x) & PKTSHIFT_MASK) >> PKTSHIFT_SHIFT)
+#define  INGPCIEBOUNDARY_32B_X 0
 #define  INGPCIEBOUNDARY_MASK   0x00000380U
 #define  INGPCIEBOUNDARY_SHIFT  7
 #define  INGPCIEBOUNDARY(x)     ((x) << INGPCIEBOUNDARY_SHIFT)
 #define  INGPADBOUNDARY(x)      ((x) << INGPADBOUNDARY_SHIFT)
 #define  INGPADBOUNDARY_GET(x) (((x) & INGPADBOUNDARY_MASK) \
                                 >> INGPADBOUNDARY_SHIFT)
+#define  INGPACKBOUNDARY_16B_X 0
+#define  INGPACKBOUNDARY_SHIFT_X 5
+
+#define  INGPACKBOUNDARY_S     16
+#define  INGPACKBOUNDARY_M     0x7U
+#define  INGPACKBOUNDARY_V(x)  ((x) << INGPACKBOUNDARY_S)
+#define  INGPACKBOUNDARY_G(x)  (((x) >> INGPACKBOUNDARY_S) \
+                                & INGPACKBOUNDARY_M)
 #define  EGRPCIEBOUNDARY_MASK   0x0000000eU
 #define  EGRPCIEBOUNDARY_SHIFT  1
 #define  EGRPCIEBOUNDARY(x)     ((x) << EGRPCIEBOUNDARY_SHIFT)
index 68eaa9c..3d06e77 100644 (file)
@@ -299,6 +299,14 @@ struct sge {
        u16 timer_val[SGE_NTIMERS];     /* interrupt holdoff timer array */
        u8 counter_val[SGE_NCOUNTERS];  /* interrupt RX threshold array */
 
+       /* Decoded Adapter Parameters.
+        */
+       u32 fl_pg_order;                /* large page allocation size */
+       u32 stat_len;                   /* length of status page at ring end */
+       u32 pktshift;                   /* padding between CPL & packet data */
+       u32 fl_align;                   /* response queue message alignment */
+       u32 fl_starve_thres;            /* Free List starvation threshold */
+
        /*
         * Reverse maps from Absolute Queue IDs to associated queue pointers.
         * The absolute Queue IDs are in a compact range which start at a
index 85036e6..fdd078d 100644 (file)
 #include "../cxgb4/t4fw_api.h"
 #include "../cxgb4/t4_msg.h"
 
-/*
- * Decoded Adapter Parameters.
- */
-static u32 FL_PG_ORDER;                /* large page allocation size */
-static u32 STAT_LEN;           /* length of status page at ring end */
-static u32 PKTSHIFT;           /* padding between CPL and packet data */
-static u32 FL_ALIGN;           /* response queue message alignment */
-
 /*
  * Constants ...
  */
@@ -101,12 +93,6 @@ enum {
        TX_QCHECK_PERIOD = (HZ / 2),
        MAX_TIMER_TX_RECLAIM = 100,
 
-       /*
-        * An FL with <= FL_STARVE_THRES buffers is starving and a periodic
-        * timer will attempt to refill it.
-        */
-       FL_STARVE_THRES = 4,
-
        /*
         * Suspend an Ethernet TX queue with fewer available descriptors than
         * this.  We always want to have room for a maximum sized packet:
@@ -264,15 +250,19 @@ static inline unsigned int fl_cap(const struct sge_fl *fl)
 
 /**
  *     fl_starving - return whether a Free List is starving.
+ *     @adapter: pointer to the adapter
  *     @fl: the Free List
  *
  *     Tests specified Free List to see whether the number of buffers
  *     available to the hardware has falled below our "starvation"
  *     threshold.
  */
-static inline bool fl_starving(const struct sge_fl *fl)
+static inline bool fl_starving(const struct adapter *adapter,
+                              const struct sge_fl *fl)
 {
-       return fl->avail - fl->pend_cred <= FL_STARVE_THRES;
+       const struct sge *s = &adapter->sge;
+
+       return fl->avail - fl->pend_cred <= s->fl_starve_thres;
 }
 
 /**
@@ -457,13 +447,16 @@ static inline void reclaim_completed_tx(struct adapter *adapter,
 
 /**
  *     get_buf_size - return the size of an RX Free List buffer.
+ *     @adapter: pointer to the associated adapter
  *     @sdesc: pointer to the software buffer descriptor
  */
-static inline int get_buf_size(const struct rx_sw_desc *sdesc)
+static inline int get_buf_size(const struct adapter *adapter,
+                              const struct rx_sw_desc *sdesc)
 {
-       return FL_PG_ORDER > 0 && (sdesc->dma_addr & RX_LARGE_BUF)
-               ? (PAGE_SIZE << FL_PG_ORDER)
-               : PAGE_SIZE;
+       const struct sge *s = &adapter->sge;
+
+       return (s->fl_pg_order > 0 && (sdesc->dma_addr & RX_LARGE_BUF)
+               ? (PAGE_SIZE << s->fl_pg_order) : PAGE_SIZE);
 }
 
 /**
@@ -483,7 +476,8 @@ static void free_rx_bufs(struct adapter *adapter, struct sge_fl *fl, int n)
 
                if (is_buf_mapped(sdesc))
                        dma_unmap_page(adapter->pdev_dev, get_buf_addr(sdesc),
-                                      get_buf_size(sdesc), PCI_DMA_FROMDEVICE);
+                                      get_buf_size(adapter, sdesc),
+                                      PCI_DMA_FROMDEVICE);
                put_page(sdesc->page);
                sdesc->page = NULL;
                if (++fl->cidx == fl->size)
@@ -511,7 +505,8 @@ static void unmap_rx_buf(struct adapter *adapter, struct sge_fl *fl)
 
        if (is_buf_mapped(sdesc))
                dma_unmap_page(adapter->pdev_dev, get_buf_addr(sdesc),
-                              get_buf_size(sdesc), PCI_DMA_FROMDEVICE);
+                              get_buf_size(adapter, sdesc),
+                              PCI_DMA_FROMDEVICE);
        sdesc->page = NULL;
        if (++fl->cidx == fl->size)
                fl->cidx = 0;
@@ -589,6 +584,7 @@ static inline void poison_buf(struct page *page, size_t sz)
 static unsigned int refill_fl(struct adapter *adapter, struct sge_fl *fl,
                              int n, gfp_t gfp)
 {
+       struct sge *s = &adapter->sge;
        struct page *page;
        dma_addr_t dma_addr;
        unsigned int cred = fl->avail;
@@ -608,12 +604,12 @@ static unsigned int refill_fl(struct adapter *adapter, struct sge_fl *fl,
         * If we don't support large pages, drop directly into the small page
         * allocation code.
         */
-       if (FL_PG_ORDER == 0)
+       if (s->fl_pg_order == 0)
                goto alloc_small_pages;
 
        while (n) {
                page = alloc_pages(gfp | __GFP_COMP | __GFP_NOWARN,
-                                  FL_PG_ORDER);
+                                  s->fl_pg_order);
                if (unlikely(!page)) {
                        /*
                         * We've failed inour attempt to allocate a "large
@@ -623,10 +619,10 @@ static unsigned int refill_fl(struct adapter *adapter, struct sge_fl *fl,
                        fl->large_alloc_failed++;
                        break;
                }
-               poison_buf(page, PAGE_SIZE << FL_PG_ORDER);
+               poison_buf(page, PAGE_SIZE << s->fl_pg_order);
 
                dma_addr = dma_map_page(adapter->pdev_dev, page, 0,
-                                       PAGE_SIZE << FL_PG_ORDER,
+                                       PAGE_SIZE << s->fl_pg_order,
                                        PCI_DMA_FROMDEVICE);
                if (unlikely(dma_mapping_error(adapter->pdev_dev, dma_addr))) {
                        /*
@@ -637,7 +633,7 @@ static unsigned int refill_fl(struct adapter *adapter, struct sge_fl *fl,
                         * because DMA mapping resources are typically
                         * critical resources once they become scarse.
                         */
-                       __free_pages(page, FL_PG_ORDER);
+                       __free_pages(page, s->fl_pg_order);
                        goto out;
                }
                dma_addr |= RX_LARGE_BUF;
@@ -693,7 +689,7 @@ out:
        fl->pend_cred += cred;
        ring_fl_db(adapter, fl);
 
-       if (unlikely(fl_starving(fl))) {
+       if (unlikely(fl_starving(adapter, fl))) {
                smp_wmb();
                set_bit(fl->cntxt_id, adapter->sge.starving_fl);
        }
@@ -1468,6 +1464,8 @@ static void t4vf_pktgl_free(const struct pkt_gl *gl)
 static void do_gro(struct sge_eth_rxq *rxq, const struct pkt_gl *gl,
                   const struct cpl_rx_pkt *pkt)
 {
+       struct adapter *adapter = rxq->rspq.adapter;
+       struct sge *s = &adapter->sge;
        int ret;
        struct sk_buff *skb;
 
@@ -1478,8 +1476,8 @@ static void do_gro(struct sge_eth_rxq *rxq, const struct pkt_gl *gl,
                return;
        }
 
-       copy_frags(skb, gl, PKTSHIFT);
-       skb->len = gl->tot_len - PKTSHIFT;
+       copy_frags(skb, gl, s->pktshift);
+       skb->len = gl->tot_len - s->pktshift;
        skb->data_len = skb->len;
        skb->truesize += skb->data_len;
        skb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -1516,6 +1514,8 @@ int t4vf_ethrx_handler(struct sge_rspq *rspq, const __be64 *rsp,
        bool csum_ok = pkt->csum_calc && !pkt->err_vec &&
                       (rspq->netdev->features & NETIF_F_RXCSUM);
        struct sge_eth_rxq *rxq = container_of(rspq, struct sge_eth_rxq, rspq);
+       struct adapter *adapter = rspq->adapter;
+       struct sge *s = &adapter->sge;
 
        /*
         * If this is a good TCP packet and we have Generic Receive Offload
@@ -1537,7 +1537,7 @@ int t4vf_ethrx_handler(struct sge_rspq *rspq, const __be64 *rsp,
                rxq->stats.rx_drops++;
                return 0;
        }
-       __skb_pull(skb, PKTSHIFT);
+       __skb_pull(skb, s->pktshift);
        skb->protocol = eth_type_trans(skb, rspq->netdev);
        skb_record_rx_queue(skb, rspq->idx);
        rxq->stats.pkts++;
@@ -1648,6 +1648,8 @@ static inline void rspq_next(struct sge_rspq *rspq)
 static int process_responses(struct sge_rspq *rspq, int budget)
 {
        struct sge_eth_rxq *rxq = container_of(rspq, struct sge_eth_rxq, rspq);
+       struct adapter *adapter = rspq->adapter;
+       struct sge *s = &adapter->sge;
        int budget_left = budget;
 
        while (likely(budget_left)) {
@@ -1697,7 +1699,7 @@ static int process_responses(struct sge_rspq *rspq, int budget)
                                BUG_ON(frag >= MAX_SKB_FRAGS);
                                BUG_ON(rxq->fl.avail == 0);
                                sdesc = &rxq->fl.sdesc[rxq->fl.cidx];
-                               bufsz = get_buf_size(sdesc);
+                               bufsz = get_buf_size(adapter, sdesc);
                                fp->page = sdesc->page;
                                fp->offset = rspq->offset;
                                fp->size = min(bufsz, len);
@@ -1726,7 +1728,7 @@ static int process_responses(struct sge_rspq *rspq, int budget)
                         */
                        ret = rspq->handler(rspq, rspq->cur_desc, &gl);
                        if (likely(ret == 0))
-                               rspq->offset += ALIGN(fp->size, FL_ALIGN);
+                               rspq->offset += ALIGN(fp->size, s->fl_align);
                        else
                                restore_rx_bufs(&gl, &rxq->fl, frag);
                } else if (likely(rsp_type == RSP_TYPE_CPL)) {
@@ -1963,7 +1965,7 @@ static void sge_rx_timer_cb(unsigned long data)
                         * schedule napi but the FL is no longer starving.
                         * No biggie.
                         */
-                       if (fl_starving(fl)) {
+                       if (fl_starving(adapter, fl)) {
                                struct sge_eth_rxq *rxq;
 
                                rxq = container_of(fl, struct sge_eth_rxq, fl);
@@ -2047,6 +2049,7 @@ int t4vf_sge_alloc_rxq(struct adapter *adapter, struct sge_rspq *rspq,
                       int intr_dest,
                       struct sge_fl *fl, rspq_handler_t hnd)
 {
+       struct sge *s = &adapter->sge;
        struct port_info *pi = netdev_priv(dev);
        struct fw_iq_cmd cmd, rpl;
        int ret, iqandst, flsz = 0;
@@ -2117,7 +2120,7 @@ int t4vf_sge_alloc_rxq(struct adapter *adapter, struct sge_rspq *rspq,
                fl->size = roundup(fl->size, FL_PER_EQ_UNIT);
                fl->desc = alloc_ring(adapter->pdev_dev, fl->size,
                                      sizeof(__be64), sizeof(struct rx_sw_desc),
-                                     &fl->addr, &fl->sdesc, STAT_LEN);
+                                     &fl->addr, &fl->sdesc, s->stat_len);
                if (!fl->desc) {
                        ret = -ENOMEM;
                        goto err;
@@ -2129,7 +2132,7 @@ int t4vf_sge_alloc_rxq(struct adapter *adapter, struct sge_rspq *rspq,
                 * free list ring) in Egress Queue Units.
                 */
                flsz = (fl->size / FL_PER_EQ_UNIT +
-                       STAT_LEN / EQ_UNIT);
+                       s->stat_len / EQ_UNIT);
 
                /*
                 * Fill in all the relevant firmware Ingress Queue Command
@@ -2217,6 +2220,7 @@ int t4vf_sge_alloc_eth_txq(struct adapter *adapter, struct sge_eth_txq *txq,
                           struct net_device *dev, struct netdev_queue *devq,
                           unsigned int iqid)
 {
+       struct sge *s = &adapter->sge;
        int ret, nentries;
        struct fw_eq_eth_cmd cmd, rpl;
        struct port_info *pi = netdev_priv(dev);
@@ -2225,7 +2229,7 @@ int t4vf_sge_alloc_eth_txq(struct adapter *adapter, struct sge_eth_txq *txq,
         * Calculate the size of the hardware TX Queue (including the Status
         * Page on the end of the TX Queue) in units of TX Descriptors.
         */
-       nentries = txq->q.size + STAT_LEN / sizeof(struct tx_desc);
+       nentries = txq->q.size + s->stat_len / sizeof(struct tx_desc);
 
        /*
         * Allocate the hardware ring for the TX ring (with space for its
@@ -2234,7 +2238,7 @@ int t4vf_sge_alloc_eth_txq(struct adapter *adapter, struct sge_eth_txq *txq,
        txq->q.desc = alloc_ring(adapter->pdev_dev, txq->q.size,
                                 sizeof(struct tx_desc),
                                 sizeof(struct tx_sw_desc),
-                                &txq->q.phys_addr, &txq->q.sdesc, STAT_LEN);
+                                &txq->q.phys_addr, &txq->q.sdesc, s->stat_len);
        if (!txq->q.desc)
                return -ENOMEM;
 
@@ -2307,8 +2311,10 @@ int t4vf_sge_alloc_eth_txq(struct adapter *adapter, struct sge_eth_txq *txq,
  */
 static void free_txq(struct adapter *adapter, struct sge_txq *tq)
 {
+       struct sge *s = &adapter->sge;
+
        dma_free_coherent(adapter->pdev_dev,
-                         tq->size * sizeof(*tq->desc) + STAT_LEN,
+                         tq->size * sizeof(*tq->desc) + s->stat_len,
                          tq->desc, tq->phys_addr);
        tq->cntxt_id = 0;
        tq->sdesc = NULL;
@@ -2322,6 +2328,7 @@ static void free_txq(struct adapter *adapter, struct sge_txq *tq)
 static void free_rspq_fl(struct adapter *adapter, struct sge_rspq *rspq,
                         struct sge_fl *fl)
 {
+       struct sge *s = &adapter->sge;
        unsigned int flid = fl ? fl->cntxt_id : 0xffff;
 
        t4vf_iq_free(adapter, FW_IQ_TYPE_FL_INT_CAP,
@@ -2337,7 +2344,7 @@ static void free_rspq_fl(struct adapter *adapter, struct sge_rspq *rspq,
        if (fl) {
                free_rx_bufs(adapter, fl, fl->avail);
                dma_free_coherent(adapter->pdev_dev,
-                                 fl->size * sizeof(*fl->desc) + STAT_LEN,
+                                 fl->size * sizeof(*fl->desc) + s->stat_len,
                                  fl->desc, fl->addr);
                kfree(fl->sdesc);
                fl->sdesc = NULL;
@@ -2423,6 +2430,7 @@ int t4vf_sge_init(struct adapter *adapter)
        u32 fl0 = sge_params->sge_fl_buffer_size[0];
        u32 fl1 = sge_params->sge_fl_buffer_size[1];
        struct sge *s = &adapter->sge;
+       unsigned int ingpadboundary, ingpackboundary;
 
        /*
         * Start by vetting the basic SGE parameters which have been set up by
@@ -2443,12 +2451,48 @@ int t4vf_sge_init(struct adapter *adapter)
         * Now translate the adapter parameters into our internal forms.
         */
        if (fl1)
-               FL_PG_ORDER = ilog2(fl1) - PAGE_SHIFT;
-       STAT_LEN = ((sge_params->sge_control & EGRSTATUSPAGESIZE_MASK)
-                   ? 128 : 64);
-       PKTSHIFT = PKTSHIFT_GET(sge_params->sge_control);
-       FL_ALIGN = 1 << (INGPADBOUNDARY_GET(sge_params->sge_control) +
-                        SGE_INGPADBOUNDARY_SHIFT);
+               s->fl_pg_order = ilog2(fl1) - PAGE_SHIFT;
+       s->stat_len = ((sge_params->sge_control & EGRSTATUSPAGESIZE_MASK)
+                       ? 128 : 64);
+       s->pktshift = PKTSHIFT_GET(sge_params->sge_control);
+
+       /* T4 uses a single control field to specify both the PCIe Padding and
+        * Packing Boundary.  T5 introduced the ability to specify these
+        * separately.  The actual Ingress Packet Data alignment boundary
+        * within Packed Buffer Mode is the maximum of these two
+        * specifications.  (Note that it makes no real practical sense to
+        * have the Pading Boudary be larger than the Packing Boundary but you
+        * could set the chip up that way and, in fact, legacy T4 code would
+        * end doing this because it would initialize the Padding Boundary and
+        * leave the Packing Boundary initialized to 0 (16 bytes).)
+        */
+       ingpadboundary = 1 << (INGPADBOUNDARY_GET(sge_params->sge_control) +
+                              X_INGPADBOUNDARY_SHIFT);
+       if (is_t4(adapter->params.chip)) {
+               s->fl_align = ingpadboundary;
+       } else {
+               /* T5 has a different interpretation of one of the PCIe Packing
+                * Boundary values.
+                */
+               ingpackboundary = INGPACKBOUNDARY_G(sge_params->sge_control2);
+               if (ingpackboundary == INGPACKBOUNDARY_16B_X)
+                       ingpackboundary = 16;
+               else
+                       ingpackboundary = 1 << (ingpackboundary +
+                                               INGPACKBOUNDARY_SHIFT_X);
+
+               s->fl_align = max(ingpadboundary, ingpackboundary);
+       }
+
+       /* A FL with <= fl_starve_thres buffers is starving and a periodic
+        * timer will attempt to refill it.  This needs to be larger than the
+        * SGE's Egress Congestion Threshold.  If it isn't, then we can get
+        * stuck waiting for new packets while the SGE is waiting for us to
+        * give it more Free List entries.  (Note that the SGE's Egress
+        * Congestion Threshold is in units of 2 Free List pointers.)
+        */
+       s->fl_starve_thres
+               = EGRTHRESHOLD_GET(sge_params->sge_congestion_control)*2 + 1;
 
        /*
         * Set up tasklet timers.
index 95df61d..4b6a6d1 100644 (file)
@@ -134,11 +134,13 @@ struct dev_params {
  */
 struct sge_params {
        u32 sge_control;                /* padding, boundaries, lengths, etc. */
+       u32 sge_control2;               /* T5: more of the same */
        u32 sge_host_page_size;         /* RDMA page sizes */
        u32 sge_queues_per_page;        /* RDMA queues/page */
        u32 sge_user_mode_limits;       /* limits for BAR2 user mode accesses */
        u32 sge_fl_buffer_size[16];     /* free list buffer sizes */
        u32 sge_ingress_rx_threshold;   /* RX counter interrupt threshold[4] */
+       u32 sge_congestion_control;     /* congestion thresholds, etc. */
        u32 sge_timer_value_0_and_1;    /* interrupt coalescing timer values */
        u32 sge_timer_value_2_and_3;
        u32 sge_timer_value_4_and_5;
index e984fdc..1e896b9 100644 (file)
@@ -468,12 +468,38 @@ int t4vf_get_sge_params(struct adapter *adapter)
        sge_params->sge_timer_value_2_and_3 = vals[5];
        sge_params->sge_timer_value_4_and_5 = vals[6];
 
+       /* T4 uses a single control field to specify both the PCIe Padding and
+        * Packing Boundary.  T5 introduced the ability to specify these
+        * separately with the Padding Boundary in SGE_CONTROL and and Packing
+        * Boundary in SGE_CONTROL2.  So for T5 and later we need to grab
+        * SGE_CONTROL in order to determine how ingress packet data will be
+        * laid out in Packed Buffer Mode.  Unfortunately, older versions of
+        * the firmware won't let us retrieve SGE_CONTROL2 so if we get a
+        * failure grabbing it we throw an error since we can't figure out the
+        * right value.
+        */
+       if (!is_t4(adapter->params.chip)) {
+               params[0] = (FW_PARAMS_MNEM(FW_PARAMS_MNEM_REG) |
+                            FW_PARAMS_PARAM_XYZ(SGE_CONTROL2_A));
+               v = t4vf_query_params(adapter, 1, params, vals);
+               if (v != FW_SUCCESS) {
+                       dev_err(adapter->pdev_dev,
+                               "Unable to get SGE Control2; "
+                               "probably old firmware.\n");
+                       return v;
+               }
+               sge_params->sge_control2 = vals[0];
+       }
+
        params[0] = (FW_PARAMS_MNEM(FW_PARAMS_MNEM_REG) |
                     FW_PARAMS_PARAM_XYZ(SGE_INGRESS_RX_THRESHOLD));
-       v = t4vf_query_params(adapter, 1, params, vals);
+       params[1] = (FW_PARAMS_MNEM(FW_PARAMS_MNEM_REG) |
+                    FW_PARAMS_PARAM_XYZ(SGE_CONM_CTRL));
+       v = t4vf_query_params(adapter, 2, params, vals);
        if (v)
                return v;
        sge_params->sge_ingress_rx_threshold = vals[0];
+       sge_params->sge_congestion_control = vals[1];
 
        return 0;
 }
index 180e53f..73cf165 100644 (file)
@@ -940,18 +940,8 @@ static int enic_rq_alloc_buf(struct vnic_rq *rq)
        struct vnic_rq_buf *buf = rq->to_use;
 
        if (buf->os_buf) {
-               buf = buf->next;
-               rq->to_use = buf;
-               rq->ring.desc_avail--;
-               if ((buf->index & VNIC_RQ_RETURN_RATE) == 0) {
-                       /* Adding write memory barrier prevents compiler and/or
-                        * CPU reordering, thus avoiding descriptor posting
-                        * before descriptor is initialized. Otherwise, hardware
-                        * can read stale descriptor fields.
-                        */
-                       wmb();
-                       iowrite32(buf->index, &rq->ctrl->posted_index);
-               }
+               enic_queue_rq_desc(rq, buf->os_buf, os_buf_index, buf->dma_addr,
+                                  buf->len);
 
                return 0;
        }
@@ -1037,7 +1027,10 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq,
                                enic->rq_truncated_pkts++;
                }
 
+               pci_unmap_single(enic->pdev, buf->dma_addr, buf->len,
+                                PCI_DMA_FROMDEVICE);
                dev_kfree_skb_any(skb);
+               buf->os_buf = NULL;
 
                return;
        }
@@ -1088,7 +1081,10 @@ static void enic_rq_indicate_buf(struct vnic_rq *rq,
                /* Buffer overflow
                 */
 
+               pci_unmap_single(enic->pdev, buf->dma_addr, buf->len,
+                                PCI_DMA_FROMDEVICE);
                dev_kfree_skb_any(skb);
+               buf->os_buf = NULL;
        }
 }
 
index 9a18e79..597c463 100644 (file)
@@ -4309,11 +4309,16 @@ static int be_ndo_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh)
                return -EOPNOTSUPP;
 
        br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
+       if (!br_spec)
+               return -EINVAL;
 
        nla_for_each_nested(attr, br_spec, rem) {
                if (nla_type(attr) != IFLA_BRIDGE_MODE)
                        continue;
 
+               if (nla_len(attr) < sizeof(mode))
+                       return -EINVAL;
+
                mode = nla_get_u16(attr);
                if (mode != BRIDGE_MODE_VEPA && mode != BRIDGE_MODE_VEB)
                        return -EINVAL;
@@ -4421,6 +4426,11 @@ static void be_del_vxlan_port(struct net_device *netdev, sa_family_t sa_family,
                 "Disabled VxLAN offloads for UDP port %d\n",
                 be16_to_cpu(port));
 }
+
+static bool be_gso_check(struct sk_buff *skb, struct net_device *dev)
+{
+       return vxlan_gso_check(skb);
+}
 #endif
 
 static const struct net_device_ops be_netdev_ops = {
@@ -4450,6 +4460,7 @@ static const struct net_device_ops be_netdev_ops = {
 #ifdef CONFIG_BE2NET_VXLAN
        .ndo_add_vxlan_port     = be_add_vxlan_port,
        .ndo_del_vxlan_port     = be_del_vxlan_port,
+       .ndo_gso_check          = be_gso_check,
 #endif
 };
 
index 50a851d..3dca494 100644 (file)
@@ -298,6 +298,16 @@ static void *swap_buffer(void *bufaddr, int len)
        return bufaddr;
 }
 
+static void swap_buffer2(void *dst_buf, void *src_buf, int len)
+{
+       int i;
+       unsigned int *src = src_buf;
+       unsigned int *dst = dst_buf;
+
+       for (i = 0; i < len; i += 4, src++, dst++)
+               *dst = swab32p(src);
+}
+
 static void fec_dump(struct net_device *ndev)
 {
        struct fec_enet_private *fep = netdev_priv(ndev);
@@ -1307,7 +1317,7 @@ fec_enet_new_rxbdp(struct net_device *ndev, struct bufdesc *bdp, struct sk_buff
 }
 
 static bool fec_enet_copybreak(struct net_device *ndev, struct sk_buff **skb,
-                              struct bufdesc *bdp, u32 length)
+                              struct bufdesc *bdp, u32 length, bool swap)
 {
        struct  fec_enet_private *fep = netdev_priv(ndev);
        struct sk_buff *new_skb;
@@ -1322,7 +1332,10 @@ static bool fec_enet_copybreak(struct net_device *ndev, struct sk_buff **skb,
        dma_sync_single_for_cpu(&fep->pdev->dev, bdp->cbd_bufaddr,
                                FEC_ENET_RX_FRSIZE - fep->rx_align,
                                DMA_FROM_DEVICE);
-       memcpy(new_skb->data, (*skb)->data, length);
+       if (!swap)
+               memcpy(new_skb->data, (*skb)->data, length);
+       else
+               swap_buffer2(new_skb->data, (*skb)->data, length);
        *skb = new_skb;
 
        return true;
@@ -1352,6 +1365,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
        u16     vlan_tag;
        int     index = 0;
        bool    is_copybreak;
+       bool    need_swap = id_entry->driver_data & FEC_QUIRK_SWAP_FRAME;
 
 #ifdef CONFIG_M532x
        flush_cache_all();
@@ -1415,7 +1429,8 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
                 * include that when passing upstream as it messes up
                 * bridging applications.
                 */
-               is_copybreak = fec_enet_copybreak(ndev, &skb, bdp, pkt_len - 4);
+               is_copybreak = fec_enet_copybreak(ndev, &skb, bdp, pkt_len - 4,
+                                                 need_swap);
                if (!is_copybreak) {
                        skb_new = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE);
                        if (unlikely(!skb_new)) {
@@ -1430,7 +1445,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id)
                prefetch(skb->data - NET_IP_ALIGN);
                skb_put(skb, pkt_len - 4);
                data = skb->data;
-               if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
+               if (!is_copybreak && need_swap)
                        swap_buffer(data, pkt_len);
 
                /* Extract the enhanced buffer descriptor */
@@ -3343,12 +3358,11 @@ static int __maybe_unused fec_suspend(struct device *dev)
                netif_device_detach(ndev);
                netif_tx_unlock_bh(ndev);
                fec_stop(ndev);
+               fec_enet_clk_enable(ndev, false);
+               pinctrl_pm_select_sleep_state(&fep->pdev->dev);
        }
        rtnl_unlock();
 
-       fec_enet_clk_enable(ndev, false);
-       pinctrl_pm_select_sleep_state(&fep->pdev->dev);
-
        if (fep->reg_phy)
                regulator_disable(fep->reg_phy);
 
@@ -3367,13 +3381,14 @@ static int __maybe_unused fec_resume(struct device *dev)
                        return ret;
        }
 
-       pinctrl_pm_select_default_state(&fep->pdev->dev);
-       ret = fec_enet_clk_enable(ndev, true);
-       if (ret)
-               goto failed_clk;
-
        rtnl_lock();
        if (netif_running(ndev)) {
+               pinctrl_pm_select_default_state(&fep->pdev->dev);
+               ret = fec_enet_clk_enable(ndev, true);
+               if (ret) {
+                       rtnl_unlock();
+                       goto failed_clk;
+               }
                fec_restart(ndev);
                netif_tx_lock_bh(ndev);
                netif_device_attach(ndev);
index a2d72a8..487cd9c 100644 (file)
@@ -1012,7 +1012,8 @@ static void igb_free_q_vector(struct igb_adapter *adapter, int v_idx)
        /* igb_get_stats64() might access the rings on this vector,
         * we must wait a grace period before freeing it.
         */
-       kfree_rcu(q_vector, rcu);
+       if (q_vector)
+               kfree_rcu(q_vector, rcu);
 }
 
 /**
@@ -1792,8 +1793,10 @@ void igb_down(struct igb_adapter *adapter)
        adapter->flags &= ~IGB_FLAG_NEED_LINK_UPDATE;
 
        for (i = 0; i < adapter->num_q_vectors; i++) {
-               napi_synchronize(&(adapter->q_vector[i]->napi));
-               napi_disable(&(adapter->q_vector[i]->napi));
+               if (adapter->q_vector[i]) {
+                       napi_synchronize(&adapter->q_vector[i]->napi);
+                       napi_disable(&adapter->q_vector[i]->napi);
+               }
        }
 
 
@@ -3717,7 +3720,8 @@ static void igb_free_all_tx_resources(struct igb_adapter *adapter)
        int i;
 
        for (i = 0; i < adapter->num_tx_queues; i++)
-               igb_free_tx_resources(adapter->tx_ring[i]);
+               if (adapter->tx_ring[i])
+                       igb_free_tx_resources(adapter->tx_ring[i]);
 }
 
 void igb_unmap_and_free_tx_resource(struct igb_ring *ring,
@@ -3782,7 +3786,8 @@ static void igb_clean_all_tx_rings(struct igb_adapter *adapter)
        int i;
 
        for (i = 0; i < adapter->num_tx_queues; i++)
-               igb_clean_tx_ring(adapter->tx_ring[i]);
+               if (adapter->tx_ring[i])
+                       igb_clean_tx_ring(adapter->tx_ring[i]);
 }
 
 /**
@@ -3819,7 +3824,8 @@ static void igb_free_all_rx_resources(struct igb_adapter *adapter)
        int i;
 
        for (i = 0; i < adapter->num_rx_queues; i++)
-               igb_free_rx_resources(adapter->rx_ring[i]);
+               if (adapter->rx_ring[i])
+                       igb_free_rx_resources(adapter->rx_ring[i]);
 }
 
 /**
@@ -3874,7 +3880,8 @@ static void igb_clean_all_rx_rings(struct igb_adapter *adapter)
        int i;
 
        for (i = 0; i < adapter->num_rx_queues; i++)
-               igb_clean_rx_ring(adapter->rx_ring[i]);
+               if (adapter->rx_ring[i])
+                       igb_clean_rx_ring(adapter->rx_ring[i]);
 }
 
 /**
@@ -7404,6 +7411,8 @@ static int igb_resume(struct device *dev)
        pci_restore_state(pdev);
        pci_save_state(pdev);
 
+       if (!pci_device_is_present(pdev))
+               return -ENODEV;
        err = pci_enable_device_mem(pdev);
        if (err) {
                dev_err(&pdev->dev,
index d2df4e3..cc51554 100644 (file)
@@ -3936,8 +3936,8 @@ void ixgbe_set_rx_mode(struct net_device *netdev)
                 * if SR-IOV and VMDQ are disabled - otherwise ensure
                 * that hardware VLAN filters remain enabled.
                 */
-               if (!(adapter->flags & (IXGBE_FLAG_VMDQ_ENABLED |
-                                       IXGBE_FLAG_SRIOV_ENABLED)))
+               if (adapter->flags & (IXGBE_FLAG_VMDQ_ENABLED |
+                                     IXGBE_FLAG_SRIOV_ENABLED))
                        vlnctrl |= (IXGBE_VLNCTRL_VFE | IXGBE_VLNCTRL_CFIEN);
        } else {
                if (netdev->flags & IFF_ALLMULTI) {
@@ -7669,6 +7669,8 @@ static int ixgbe_ndo_bridge_setlink(struct net_device *dev,
                return -EOPNOTSUPP;
 
        br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
+       if (!br_spec)
+               return -EINVAL;
 
        nla_for_each_nested(attr, br_spec, rem) {
                __u16 mode;
@@ -7677,6 +7679,9 @@ static int ixgbe_ndo_bridge_setlink(struct net_device *dev,
                if (nla_type(attr) != IFLA_BRIDGE_MODE)
                        continue;
 
+               if (nla_len(attr) < sizeof(mode))
+                       return -EINVAL;
+
                mode = nla_get_u16(attr);
                if (mode == BRIDGE_MODE_VEPA) {
                        reg = 0;
@@ -7979,6 +7984,7 @@ static int ixgbe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        int i, err, pci_using_dac, expected_gts;
        unsigned int indices = MAX_TX_QUEUES;
        u8 part_str[IXGBE_PBANUM_LENGTH];
+       bool disable_dev = false;
 #ifdef IXGBE_FCOE
        u16 device_caps;
 #endif
@@ -8369,13 +8375,14 @@ err_sw_init:
        iounmap(adapter->io_addr);
        kfree(adapter->mac_table);
 err_ioremap:
+       disable_dev = !test_and_set_bit(__IXGBE_DISABLED, &adapter->state);
        free_netdev(netdev);
 err_alloc_etherdev:
        pci_release_selected_regions(pdev,
                                     pci_select_bars(pdev, IORESOURCE_MEM));
 err_pci_reg:
 err_dma:
-       if (!adapter || !test_and_set_bit(__IXGBE_DISABLED, &adapter->state))
+       if (!adapter || disable_dev)
                pci_disable_device(pdev);
        return err;
 }
@@ -8393,6 +8400,7 @@ static void ixgbe_remove(struct pci_dev *pdev)
 {
        struct ixgbe_adapter *adapter = pci_get_drvdata(pdev);
        struct net_device *netdev = adapter->netdev;
+       bool disable_dev;
 
        ixgbe_dbg_adapter_exit(adapter);
 
@@ -8442,11 +8450,12 @@ static void ixgbe_remove(struct pci_dev *pdev)
        e_dev_info("complete\n");
 
        kfree(adapter->mac_table);
+       disable_dev = !test_and_set_bit(__IXGBE_DISABLED, &adapter->state);
        free_netdev(netdev);
 
        pci_disable_pcie_error_reporting(pdev);
 
-       if (!test_and_set_bit(__IXGBE_DISABLED, &adapter->state))
+       if (disable_dev)
                pci_disable_device(pdev);
 }
 
index d47b19f..28b81ae 100644 (file)
@@ -635,7 +635,6 @@ s32 ixgbe_check_phy_link_tnx(struct ixgbe_hw *hw, ixgbe_link_speed *speed,
  **/
 s32 ixgbe_setup_phy_link_tnx(struct ixgbe_hw *hw)
 {
-       s32 status;
        u16 autoneg_reg = IXGBE_MII_AUTONEG_REG;
        bool autoneg = false;
        ixgbe_link_speed speed;
@@ -700,8 +699,7 @@ s32 ixgbe_setup_phy_link_tnx(struct ixgbe_hw *hw)
 
        hw->phy.ops.write_reg(hw, MDIO_CTRL1,
                              MDIO_MMD_AN, autoneg_reg);
-
-       return status;
+       return 0;
 }
 
 /**
index b151a94..d44560d 100644 (file)
@@ -1047,7 +1047,6 @@ static int txq_reclaim(struct tx_queue *txq, int budget, int force)
                int tx_index;
                struct tx_desc *desc;
                u32 cmd_sts;
-               struct sk_buff *skb;
 
                tx_index = txq->tx_used_desc;
                desc = &txq->tx_desc_area[tx_index];
@@ -1066,19 +1065,22 @@ static int txq_reclaim(struct tx_queue *txq, int budget, int force)
                reclaimed++;
                txq->tx_desc_count--;
 
-               skb = NULL;
-               if (cmd_sts & TX_LAST_DESC)
-                       skb = __skb_dequeue(&txq->tx_skb);
+               if (!IS_TSO_HEADER(txq, desc->buf_ptr))
+                       dma_unmap_single(mp->dev->dev.parent, desc->buf_ptr,
+                                        desc->byte_cnt, DMA_TO_DEVICE);
+
+               if (cmd_sts & TX_ENABLE_INTERRUPT) {
+                       struct sk_buff *skb = __skb_dequeue(&txq->tx_skb);
+
+                       if (!WARN_ON(!skb))
+                               dev_kfree_skb(skb);
+               }
 
                if (cmd_sts & ERROR_SUMMARY) {
                        netdev_info(mp->dev, "tx error\n");
                        mp->dev->stats.tx_errors++;
                }
 
-               if (!IS_TSO_HEADER(txq, desc->buf_ptr))
-                       dma_unmap_single(mp->dev->dev.parent, desc->buf_ptr,
-                                        desc->byte_cnt, DMA_TO_DEVICE);
-               dev_kfree_skb(skb);
        }
 
        __netif_tx_unlock_bh(nq);
index ece83f1..fdf3e38 100644 (file)
@@ -1692,6 +1692,7 @@ static int mvpp2_prs_vlan_add(struct mvpp2 *priv, unsigned short tpid, int ai,
 {
        struct mvpp2_prs_entry *pe;
        int tid_aux, tid;
+       int ret = 0;
 
        pe = mvpp2_prs_vlan_find(priv, tpid, ai);
 
@@ -1723,8 +1724,10 @@ static int mvpp2_prs_vlan_add(struct mvpp2 *priv, unsigned short tpid, int ai,
                                break;
                }
 
-               if (tid <= tid_aux)
-                       return -EINVAL;
+               if (tid <= tid_aux) {
+                       ret = -EINVAL;
+                       goto error;
+               }
 
                memset(pe, 0 , sizeof(struct mvpp2_prs_entry));
                mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VLAN);
@@ -1756,9 +1759,10 @@ static int mvpp2_prs_vlan_add(struct mvpp2 *priv, unsigned short tpid, int ai,
 
        mvpp2_prs_hw_write(priv, pe);
 
+error:
        kfree(pe);
 
-       return 0;
+       return ret;
 }
 
 /* Get first free double vlan ai number */
@@ -1821,7 +1825,7 @@ static int mvpp2_prs_double_vlan_add(struct mvpp2 *priv, unsigned short tpid1,
                                     unsigned int port_map)
 {
        struct mvpp2_prs_entry *pe;
-       int tid_aux, tid, ai;
+       int tid_aux, tid, ai, ret = 0;
 
        pe = mvpp2_prs_double_vlan_find(priv, tpid1, tpid2);
 
@@ -1838,8 +1842,10 @@ static int mvpp2_prs_double_vlan_add(struct mvpp2 *priv, unsigned short tpid1,
 
                /* Set ai value for new double vlan entry */
                ai = mvpp2_prs_double_vlan_ai_free_get(priv);
-               if (ai < 0)
-                       return ai;
+               if (ai < 0) {
+                       ret = ai;
+                       goto error;
+               }
 
                /* Get first single/triple vlan tid */
                for (tid_aux = MVPP2_PE_FIRST_FREE_TID;
@@ -1859,8 +1865,10 @@ static int mvpp2_prs_double_vlan_add(struct mvpp2 *priv, unsigned short tpid1,
                                break;
                }
 
-               if (tid >= tid_aux)
-                       return -ERANGE;
+               if (tid >= tid_aux) {
+                       ret = -ERANGE;
+                       goto error;
+               }
 
                memset(pe, 0, sizeof(struct mvpp2_prs_entry));
                mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VLAN);
@@ -1887,8 +1895,9 @@ static int mvpp2_prs_double_vlan_add(struct mvpp2 *priv, unsigned short tpid1,
        mvpp2_prs_tcam_port_map_set(pe, port_map);
        mvpp2_prs_hw_write(priv, pe);
 
+error:
        kfree(pe);
-       return 0;
+       return ret;
 }
 
 /* IPv4 header parsing for fragmentation and L4 offset */
index f3032fe..4d69e38 100644 (file)
@@ -1693,7 +1693,7 @@ int mlx4_en_start_port(struct net_device *dev)
        mlx4_set_stats_bitmap(mdev->dev, &priv->stats_bitmap);
 
 #ifdef CONFIG_MLX4_EN_VXLAN
-       if (priv->mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_VXLAN_OFFLOADS)
+       if (priv->mdev->dev->caps.tunnel_offload_mode == MLX4_TUNNEL_OFFLOAD_MODE_VXLAN)
                vxlan_get_rx_port(dev);
 #endif
        priv->port_up = true;
@@ -2281,8 +2281,16 @@ static void mlx4_en_add_vxlan_offloads(struct work_struct *work)
        ret = mlx4_SET_PORT_VXLAN(priv->mdev->dev, priv->port,
                                  VXLAN_STEER_BY_OUTER_MAC, 1);
 out:
-       if (ret)
+       if (ret) {
                en_err(priv, "failed setting L2 tunnel configuration ret %d\n", ret);
+               return;
+       }
+
+       /* set offloads */
+       priv->dev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
+                                     NETIF_F_TSO | NETIF_F_GSO_UDP_TUNNEL;
+       priv->dev->hw_features |= NETIF_F_GSO_UDP_TUNNEL;
+       priv->dev->features    |= NETIF_F_GSO_UDP_TUNNEL;
 }
 
 static void mlx4_en_del_vxlan_offloads(struct work_struct *work)
@@ -2290,6 +2298,11 @@ static void mlx4_en_del_vxlan_offloads(struct work_struct *work)
        int ret;
        struct mlx4_en_priv *priv = container_of(work, struct mlx4_en_priv,
                                                 vxlan_del_task);
+       /* unset offloads */
+       priv->dev->hw_enc_features &= ~(NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
+                                     NETIF_F_TSO | NETIF_F_GSO_UDP_TUNNEL);
+       priv->dev->hw_features &= ~NETIF_F_GSO_UDP_TUNNEL;
+       priv->dev->features    &= ~NETIF_F_GSO_UDP_TUNNEL;
 
        ret = mlx4_SET_PORT_VXLAN(priv->mdev->dev, priv->port,
                                  VXLAN_STEER_BY_OUTER_MAC, 0);
@@ -2342,6 +2355,11 @@ static void mlx4_en_del_vxlan_port(struct  net_device *dev,
 
        queue_work(priv->mdev->workqueue, &priv->vxlan_del_task);
 }
+
+static bool mlx4_en_gso_check(struct sk_buff *skb, struct net_device *dev)
+{
+       return vxlan_gso_check(skb);
+}
 #endif
 
 static const struct net_device_ops mlx4_netdev_ops = {
@@ -2373,6 +2391,7 @@ static const struct net_device_ops mlx4_netdev_ops = {
 #ifdef CONFIG_MLX4_EN_VXLAN
        .ndo_add_vxlan_port     = mlx4_en_add_vxlan_port,
        .ndo_del_vxlan_port     = mlx4_en_del_vxlan_port,
+       .ndo_gso_check          = mlx4_en_gso_check,
 #endif
 };
 
@@ -2403,6 +2422,11 @@ static const struct net_device_ops mlx4_netdev_ops_master = {
        .ndo_rx_flow_steer      = mlx4_en_filter_rfs,
 #endif
        .ndo_get_phys_port_id   = mlx4_en_get_phys_port_id,
+#ifdef CONFIG_MLX4_EN_VXLAN
+       .ndo_add_vxlan_port     = mlx4_en_add_vxlan_port,
+       .ndo_del_vxlan_port     = mlx4_en_del_vxlan_port,
+       .ndo_gso_check          = mlx4_en_gso_check,
+#endif
 };
 
 int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
@@ -2568,13 +2592,6 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
        if (mdev->dev->caps.steering_mode != MLX4_STEERING_MODE_A0)
                dev->priv_flags |= IFF_UNICAST_FLT;
 
-       if (mdev->dev->caps.tunnel_offload_mode == MLX4_TUNNEL_OFFLOAD_MODE_VXLAN) {
-               dev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
-                                       NETIF_F_TSO | NETIF_F_GSO_UDP_TUNNEL;
-               dev->hw_features |= NETIF_F_GSO_UDP_TUNNEL;
-               dev->features    |= NETIF_F_GSO_UDP_TUNNEL;
-       }
-
        mdev->pndev[port] = dev;
 
        netif_carrier_off(dev);
index 5d2498d..cd5cf6d 100644 (file)
@@ -1546,7 +1546,7 @@ static int qp_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd,
 
        switch (op) {
        case RES_OP_RESERVE:
-               count = get_param_l(&in_param);
+               count = get_param_l(&in_param) & 0xffffff;
                align = get_param_h(&in_param);
                err = mlx4_grant_resource(dev, slave, RES_QP, count, 0);
                if (err)
index a278238..ad2c96a 100644 (file)
@@ -374,15 +374,14 @@ int mlx5_create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, u8 vecidx,
        snprintf(eq->name, MLX5_MAX_EQ_NAME, "%s@pci:%s",
                 name, pci_name(dev->pdev));
        eq->eqn = out.eq_number;
+       eq->irqn = vecidx;
+       eq->dev = dev;
+       eq->doorbell = uar->map + MLX5_EQ_DOORBEL_OFFSET;
        err = request_irq(table->msix_arr[vecidx].vector, mlx5_msix_handler, 0,
                          eq->name, eq);
        if (err)
                goto err_eq;
 
-       eq->irqn = vecidx;
-       eq->dev = dev;
-       eq->doorbell = uar->map + MLX5_EQ_DOORBEL_OFFSET;
-
        err = mlx5_debug_eq_add(dev, eq);
        if (err)
                goto err_irq;
index 3d8e8e4..71b10b2 100644 (file)
@@ -864,14 +864,14 @@ static int init_one(struct pci_dev *pdev,
        dev->profile = &profile[prof_sel];
        dev->event = mlx5_core_event;
 
+       INIT_LIST_HEAD(&priv->ctx_list);
+       spin_lock_init(&priv->ctx_lock);
        err = mlx5_dev_init(dev, pdev);
        if (err) {
                dev_err(&pdev->dev, "mlx5_dev_init failed %d\n", err);
                goto out;
        }
 
-       INIT_LIST_HEAD(&priv->ctx_list);
-       spin_lock_init(&priv->ctx_lock);
        err = mlx5_register_device(dev);
        if (err) {
                dev_err(&pdev->dev, "mlx5_register_device failed %d\n", err);
index 0b2a1cc..6130375 100644 (file)
@@ -2762,7 +2762,8 @@ netxen_fw_poll_work(struct work_struct *work)
        if (test_bit(__NX_RESETTING, &adapter->state))
                goto reschedule;
 
-       if (test_bit(__NX_DEV_UP, &adapter->state)) {
+       if (test_bit(__NX_DEV_UP, &adapter->state) &&
+           !(adapter->capabilities & NX_FW_CAPABILITY_LINK_NOTIFICATION)) {
                if (!adapter->has_link_events) {
 
                        netxen_nic_handle_phy_intr(adapter);
index f5e29f7..a913b3a 100644 (file)
@@ -503,6 +503,11 @@ static void qlcnic_del_vxlan_port(struct net_device *netdev,
 
        adapter->flags |= QLCNIC_DEL_VXLAN_PORT;
 }
+
+static bool qlcnic_gso_check(struct sk_buff *skb, struct net_device *dev)
+{
+       return vxlan_gso_check(skb);
+}
 #endif
 
 static const struct net_device_ops qlcnic_netdev_ops = {
@@ -526,6 +531,7 @@ static const struct net_device_ops qlcnic_netdev_ops = {
 #ifdef CONFIG_QLCNIC_VXLAN
        .ndo_add_vxlan_port     = qlcnic_add_vxlan_port,
        .ndo_del_vxlan_port     = qlcnic_del_vxlan_port,
+       .ndo_gso_check          = qlcnic_gso_check,
 #endif
 #ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller = qlcnic_poll_controller,
index f3a4714..9a49f42 100644 (file)
@@ -5,7 +5,6 @@
 config NET_VENDOR_QUALCOMM
        bool "Qualcomm devices"
        default y
-       depends on SPI_MASTER && OF_GPIO
        ---help---
          If you have a network (Ethernet) card belonging to this class, say Y
          and read the Ethernet-HOWTO, available from
@@ -20,7 +19,7 @@ if NET_VENDOR_QUALCOMM
 
 config QCA7000
        tristate "Qualcomm Atheros QCA7000 support"
-       depends on SPI_MASTER && OF_GPIO
+       depends on SPI_MASTER && OF
        ---help---
          This SPI protocol driver supports the Qualcomm Atheros QCA7000.
 
index 60e9c2c..b5db6b3 100644 (file)
@@ -917,21 +917,13 @@ static int sh_eth_reset(struct net_device *ndev)
        return ret;
 }
 
-#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
 static void sh_eth_set_receive_align(struct sk_buff *skb)
 {
-       int reserve;
+       uintptr_t reserve = (uintptr_t)skb->data & (SH_ETH_RX_ALIGN - 1);
 
-       reserve = SH4_SKB_RX_ALIGN - ((u32)skb->data & (SH4_SKB_RX_ALIGN - 1));
        if (reserve)
-               skb_reserve(skb, reserve);
+               skb_reserve(skb, SH_ETH_RX_ALIGN - reserve);
 }
-#else
-static void sh_eth_set_receive_align(struct sk_buff *skb)
-{
-       skb_reserve(skb, SH2_SH3_SKB_RX_ALIGN);
-}
-#endif
 
 
 /* CPU <-> EDMAC endian convert */
@@ -1119,6 +1111,7 @@ static void sh_eth_ring_format(struct net_device *ndev)
        struct sh_eth_txdesc *txdesc = NULL;
        int rx_ringsize = sizeof(*rxdesc) * mdp->num_rx_ring;
        int tx_ringsize = sizeof(*txdesc) * mdp->num_tx_ring;
+       int skbuff_size = mdp->rx_buf_sz + SH_ETH_RX_ALIGN - 1;
 
        mdp->cur_rx = 0;
        mdp->cur_tx = 0;
@@ -1131,21 +1124,21 @@ static void sh_eth_ring_format(struct net_device *ndev)
        for (i = 0; i < mdp->num_rx_ring; i++) {
                /* skb */
                mdp->rx_skbuff[i] = NULL;
-               skb = netdev_alloc_skb(ndev, mdp->rx_buf_sz);
+               skb = netdev_alloc_skb(ndev, skbuff_size);
                mdp->rx_skbuff[i] = skb;
                if (skb == NULL)
                        break;
-               dma_map_single(&ndev->dev, skb->data, mdp->rx_buf_sz,
-                              DMA_FROM_DEVICE);
                sh_eth_set_receive_align(skb);
 
                /* RX descriptor */
                rxdesc = &mdp->rx_ring[i];
+               /* The size of the buffer is a multiple of 16 bytes. */
+               rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 16);
+               dma_map_single(&ndev->dev, skb->data, rxdesc->buffer_length,
+                              DMA_FROM_DEVICE);
                rxdesc->addr = virt_to_phys(PTR_ALIGN(skb->data, 4));
                rxdesc->status = cpu_to_edmac(mdp, RD_RACT | RD_RFP);
 
-               /* The size of the buffer is 16 byte boundary. */
-               rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 16);
                /* Rx descriptor address set */
                if (i == 0) {
                        sh_eth_write(ndev, mdp->rx_desc_dma, RDLAR);
@@ -1397,6 +1390,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
        struct sk_buff *skb;
        u16 pkt_len = 0;
        u32 desc_status;
+       int skbuff_size = mdp->rx_buf_sz + SH_ETH_RX_ALIGN - 1;
 
        rxdesc = &mdp->rx_ring[entry];
        while (!(rxdesc->status & cpu_to_edmac(mdp, RD_RACT))) {
@@ -1448,7 +1442,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
                        if (mdp->cd->rpadir)
                                skb_reserve(skb, NET_IP_ALIGN);
                        dma_sync_single_for_cpu(&ndev->dev, rxdesc->addr,
-                                               mdp->rx_buf_sz,
+                                               ALIGN(mdp->rx_buf_sz, 16),
                                                DMA_FROM_DEVICE);
                        skb_put(skb, pkt_len);
                        skb->protocol = eth_type_trans(skb, ndev);
@@ -1468,13 +1462,13 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota)
                rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 16);
 
                if (mdp->rx_skbuff[entry] == NULL) {
-                       skb = netdev_alloc_skb(ndev, mdp->rx_buf_sz);
+                       skb = netdev_alloc_skb(ndev, skbuff_size);
                        mdp->rx_skbuff[entry] = skb;
                        if (skb == NULL)
                                break;  /* Better luck next round. */
-                       dma_map_single(&ndev->dev, skb->data, mdp->rx_buf_sz,
-                                      DMA_FROM_DEVICE);
                        sh_eth_set_receive_align(skb);
+                       dma_map_single(&ndev->dev, skb->data,
+                                      rxdesc->buffer_length, DMA_FROM_DEVICE);
 
                        skb_checksum_none_assert(skb);
                        rxdesc->addr = virt_to_phys(PTR_ALIGN(skb->data, 4));
@@ -2042,6 +2036,8 @@ static int sh_eth_open(struct net_device *ndev)
        if (ret)
                goto out_free_irq;
 
+       mdp->is_opened = 1;
+
        return ret;
 
 out_free_irq:
@@ -2131,6 +2127,36 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev)
        return NETDEV_TX_OK;
 }
 
+static struct net_device_stats *sh_eth_get_stats(struct net_device *ndev)
+{
+       struct sh_eth_private *mdp = netdev_priv(ndev);
+
+       if (sh_eth_is_rz_fast_ether(mdp))
+               return &ndev->stats;
+
+       if (!mdp->is_opened)
+               return &ndev->stats;
+
+       ndev->stats.tx_dropped += sh_eth_read(ndev, TROCR);
+       sh_eth_write(ndev, 0, TROCR);   /* (write clear) */
+       ndev->stats.collisions += sh_eth_read(ndev, CDCR);
+       sh_eth_write(ndev, 0, CDCR);    /* (write clear) */
+       ndev->stats.tx_carrier_errors += sh_eth_read(ndev, LCCR);
+       sh_eth_write(ndev, 0, LCCR);    /* (write clear) */
+
+       if (sh_eth_is_gether(mdp)) {
+               ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CERCR);
+               sh_eth_write(ndev, 0, CERCR);   /* (write clear) */
+               ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CEECR);
+               sh_eth_write(ndev, 0, CEECR);   /* (write clear) */
+       } else {
+               ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CNDCR);
+               sh_eth_write(ndev, 0, CNDCR);   /* (write clear) */
+       }
+
+       return &ndev->stats;
+}
+
 /* device close function */
 static int sh_eth_close(struct net_device *ndev)
 {
@@ -2145,6 +2171,7 @@ static int sh_eth_close(struct net_device *ndev)
        sh_eth_write(ndev, 0, EDTRR);
        sh_eth_write(ndev, 0, EDRRR);
 
+       sh_eth_get_stats(ndev);
        /* PHY Disconnect */
        if (mdp->phydev) {
                phy_stop(mdp->phydev);
@@ -2163,36 +2190,9 @@ static int sh_eth_close(struct net_device *ndev)
 
        pm_runtime_put_sync(&mdp->pdev->dev);
 
-       return 0;
-}
-
-static struct net_device_stats *sh_eth_get_stats(struct net_device *ndev)
-{
-       struct sh_eth_private *mdp = netdev_priv(ndev);
-
-       if (sh_eth_is_rz_fast_ether(mdp))
-               return &ndev->stats;
+       mdp->is_opened = 0;
 
-       pm_runtime_get_sync(&mdp->pdev->dev);
-
-       ndev->stats.tx_dropped += sh_eth_read(ndev, TROCR);
-       sh_eth_write(ndev, 0, TROCR);   /* (write clear) */
-       ndev->stats.collisions += sh_eth_read(ndev, CDCR);
-       sh_eth_write(ndev, 0, CDCR);    /* (write clear) */
-       ndev->stats.tx_carrier_errors += sh_eth_read(ndev, LCCR);
-       sh_eth_write(ndev, 0, LCCR);    /* (write clear) */
-       if (sh_eth_is_gether(mdp)) {
-               ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CERCR);
-               sh_eth_write(ndev, 0, CERCR);   /* (write clear) */
-               ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CEECR);
-               sh_eth_write(ndev, 0, CEECR);   /* (write clear) */
-       } else {
-               ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CNDCR);
-               sh_eth_write(ndev, 0, CNDCR);   /* (write clear) */
-       }
-       pm_runtime_put_sync(&mdp->pdev->dev);
-
-       return &ndev->stats;
+       return 0;
 }
 
 /* ioctl to device function */
index b37c427..22301bf 100644 (file)
@@ -162,9 +162,9 @@ enum {
 
 /* Driver's parameters */
 #if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
-#define SH4_SKB_RX_ALIGN       32
+#define SH_ETH_RX_ALIGN                32
 #else
-#define SH2_SH3_SKB_RX_ALIGN   2
+#define SH_ETH_RX_ALIGN                2
 #endif
 
 /* Register's bits
@@ -522,6 +522,7 @@ struct sh_eth_private {
 
        unsigned no_ether_link:1;
        unsigned ether_link_active_low:1;
+       unsigned is_opened:1;
 };
 
 static inline void sh_eth_soft_swap(char *src, int len)
index 002d4cd..a77f05c 100644 (file)
@@ -180,7 +180,8 @@ static int efx_ef10_probe(struct efx_nic *efx)
                      EFX_MAX_CHANNELS,
                      resource_size(&efx->pci_dev->resource[EFX_MEM_BAR]) /
                      (EFX_VI_PAGE_SIZE * EFX_TXQ_TYPES));
-       BUG_ON(efx->max_channels == 0);
+       if (WARN_ON(efx->max_channels == 0))
+               return -EIO;
 
        nic_data = kzalloc(sizeof(*nic_data), GFP_KERNEL);
        if (!nic_data)
index 2c62208..6cc3cf6 100644 (file)
@@ -2243,9 +2243,10 @@ static int smc_drv_probe(struct platform_device *pdev)
        const struct of_device_id *match = NULL;
        struct smc_local *lp;
        struct net_device *ndev;
-       struct resource *res, *ires;
+       struct resource *res;
        unsigned int __iomem *addr;
        unsigned long irq_flags = SMC_IRQ_FLAGS;
+       unsigned long irq_resflags;
        int ret;
 
        ndev = alloc_etherdev(sizeof(struct smc_local));
@@ -2337,16 +2338,19 @@ static int smc_drv_probe(struct platform_device *pdev)
                goto out_free_netdev;
        }
 
-       ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-       if (!ires) {
+       ndev->irq = platform_get_irq(pdev, 0);
+       if (ndev->irq <= 0) {
                ret = -ENODEV;
                goto out_release_io;
        }
-
-       ndev->irq = ires->start;
-
-       if (irq_flags == -1 || ires->flags & IRQF_TRIGGER_MASK)
-               irq_flags = ires->flags & IRQF_TRIGGER_MASK;
+       /*
+        * If this platform does not specify any special irqflags, or if
+        * the resource supplies a trigger, override the irqflags with
+        * the trigger flags from the resource.
+        */
+       irq_resflags = irqd_get_trigger_type(irq_get_irq_data(ndev->irq));
+       if (irq_flags == -1 || irq_resflags & IRQF_TRIGGER_MASK)
+               irq_flags = irq_resflags & IRQF_TRIGGER_MASK;
 
        ret = smc_request_attrib(pdev, ndev);
        if (ret)
index affb29d..77ed745 100644 (file)
@@ -1342,6 +1342,42 @@ static void smsc911x_rx_multicast_update_workaround(struct smsc911x_data *pdata)
        spin_unlock(&pdata->mac_lock);
 }
 
+static int smsc911x_phy_general_power_up(struct smsc911x_data *pdata)
+{
+       int rc = 0;
+
+       if (!pdata->phy_dev)
+               return rc;
+
+       /* If the internal PHY is in General Power-Down mode, all, except the
+        * management interface, is powered-down and stays in that condition as
+        * long as Phy register bit 0.11 is HIGH.
+        *
+        * In that case, clear the bit 0.11, so the PHY powers up and we can
+        * access to the phy registers.
+        */
+       rc = phy_read(pdata->phy_dev, MII_BMCR);
+       if (rc < 0) {
+               SMSC_WARN(pdata, drv, "Failed reading PHY control reg");
+               return rc;
+       }
+
+       /* If the PHY general power-down bit is not set is not necessary to
+        * disable the general power down-mode.
+        */
+       if (rc & BMCR_PDOWN) {
+               rc = phy_write(pdata->phy_dev, MII_BMCR, rc & ~BMCR_PDOWN);
+               if (rc < 0) {
+                       SMSC_WARN(pdata, drv, "Failed writing PHY control reg");
+                       return rc;
+               }
+
+               usleep_range(1000, 1500);
+       }
+
+       return 0;
+}
+
 static int smsc911x_phy_disable_energy_detect(struct smsc911x_data *pdata)
 {
        int rc = 0;
@@ -1356,12 +1392,8 @@ static int smsc911x_phy_disable_energy_detect(struct smsc911x_data *pdata)
                return rc;
        }
 
-       /*
-        * If energy is detected the PHY is already awake so is not necessary
-        * to disable the energy detect power-down mode.
-        */
-       if ((rc & MII_LAN83C185_EDPWRDOWN) &&
-           !(rc & MII_LAN83C185_ENERGYON)) {
+       /* Only disable if energy detect mode is already enabled */
+       if (rc & MII_LAN83C185_EDPWRDOWN) {
                /* Disable energy detect mode for this SMSC Transceivers */
                rc = phy_write(pdata->phy_dev, MII_LAN83C185_CTRL_STATUS,
                               rc & (~MII_LAN83C185_EDPWRDOWN));
@@ -1370,8 +1402,8 @@ static int smsc911x_phy_disable_energy_detect(struct smsc911x_data *pdata)
                        SMSC_WARN(pdata, drv, "Failed writing PHY control reg");
                        return rc;
                }
-
-               mdelay(1);
+               /* Allow PHY to wakeup */
+               mdelay(2);
        }
 
        return 0;
@@ -1393,7 +1425,6 @@ static int smsc911x_phy_enable_energy_detect(struct smsc911x_data *pdata)
 
        /* Only enable if energy detect mode is already disabled */
        if (!(rc & MII_LAN83C185_EDPWRDOWN)) {
-               mdelay(100);
                /* Enable energy detect mode for this SMSC Transceivers */
                rc = phy_write(pdata->phy_dev, MII_LAN83C185_CTRL_STATUS,
                               rc | MII_LAN83C185_EDPWRDOWN);
@@ -1402,8 +1433,6 @@ static int smsc911x_phy_enable_energy_detect(struct smsc911x_data *pdata)
                        SMSC_WARN(pdata, drv, "Failed writing PHY control reg");
                        return rc;
                }
-
-               mdelay(1);
        }
        return 0;
 }
@@ -1414,6 +1443,16 @@ static int smsc911x_soft_reset(struct smsc911x_data *pdata)
        unsigned int temp;
        int ret;
 
+       /*
+        * Make sure to power-up the PHY chip before doing a reset, otherwise
+        * the reset fails.
+        */
+       ret = smsc911x_phy_general_power_up(pdata);
+       if (ret) {
+               SMSC_WARN(pdata, drv, "Failed to power-up the PHY chip");
+               return ret;
+       }
+
        /*
         * LAN9210/LAN9211/LAN9220/LAN9221 chips have an internal PHY that
         * are initialized in a Energy Detect Power-Down mode that prevents
index 6f77a46..18c46bb 100644 (file)
@@ -276,6 +276,7 @@ static void stmmac_eee_ctrl_timer(unsigned long arg)
 bool stmmac_eee_init(struct stmmac_priv *priv)
 {
        char *phy_bus_name = priv->plat->phy_bus_name;
+       unsigned long flags;
        bool ret = false;
 
        /* Using PCS we cannot dial with the phy registers at this stage
@@ -300,6 +301,7 @@ bool stmmac_eee_init(struct stmmac_priv *priv)
                         * changed).
                         * In that case the driver disable own timers.
                         */
+                       spin_lock_irqsave(&priv->lock, flags);
                        if (priv->eee_active) {
                                pr_debug("stmmac: disable EEE\n");
                                del_timer_sync(&priv->eee_ctrl_timer);
@@ -307,9 +309,11 @@ bool stmmac_eee_init(struct stmmac_priv *priv)
                                                             tx_lpi_timer);
                        }
                        priv->eee_active = 0;
+                       spin_unlock_irqrestore(&priv->lock, flags);
                        goto out;
                }
                /* Activate the EEE and start timers */
+               spin_lock_irqsave(&priv->lock, flags);
                if (!priv->eee_active) {
                        priv->eee_active = 1;
                        init_timer(&priv->eee_ctrl_timer);
@@ -325,9 +329,10 @@ bool stmmac_eee_init(struct stmmac_priv *priv)
                /* Set HW EEE according to the speed */
                priv->hw->mac->set_eee_pls(priv->hw, priv->phydev->link);
 
-               pr_debug("stmmac: Energy-Efficient Ethernet initialized\n");
-
                ret = true;
+               spin_unlock_irqrestore(&priv->lock, flags);
+
+               pr_debug("stmmac: Energy-Efficient Ethernet initialized\n");
        }
 out:
        return ret;
@@ -760,12 +765,12 @@ static void stmmac_adjust_link(struct net_device *dev)
        if (new_state && netif_msg_link(priv))
                phy_print_status(phydev);
 
+       spin_unlock_irqrestore(&priv->lock, flags);
+
        /* At this stage, it could be needed to setup the EEE or adjust some
         * MAC related HW registers.
         */
        priv->eee_enabled = stmmac_eee_init(priv);
-
-       spin_unlock_irqrestore(&priv->lock, flags);
 }
 
 /**
@@ -959,12 +964,12 @@ static void stmmac_clear_descriptors(struct stmmac_priv *priv)
 }
 
 static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p,
-                                 int i)
+                                 int i, gfp_t flags)
 {
        struct sk_buff *skb;
 
        skb = __netdev_alloc_skb(priv->dev, priv->dma_buf_sz + NET_IP_ALIGN,
-                                GFP_KERNEL);
+                                flags);
        if (!skb) {
                pr_err("%s: Rx init fails; skb is NULL\n", __func__);
                return -ENOMEM;
@@ -1006,7 +1011,7 @@ static void stmmac_free_rx_buffers(struct stmmac_priv *priv, int i)
  * and allocates the socket buffers. It suppors the chained and ring
  * modes.
  */
-static int init_dma_desc_rings(struct net_device *dev)
+static int init_dma_desc_rings(struct net_device *dev, gfp_t flags)
 {
        int i;
        struct stmmac_priv *priv = netdev_priv(dev);
@@ -1041,7 +1046,7 @@ static int init_dma_desc_rings(struct net_device *dev)
                else
                        p = priv->dma_rx + i;
 
-               ret = stmmac_init_rx_buffers(priv, p, i);
+               ret = stmmac_init_rx_buffers(priv, p, i, flags);
                if (ret)
                        goto err_init_rx_buffers;
 
@@ -1647,11 +1652,6 @@ static int stmmac_hw_setup(struct net_device *dev)
        struct stmmac_priv *priv = netdev_priv(dev);
        int ret;
 
-       ret = init_dma_desc_rings(dev);
-       if (ret < 0) {
-               pr_err("%s: DMA descriptors initialization failed\n", __func__);
-               return ret;
-       }
        /* DMA initialization and SW reset */
        ret = stmmac_init_dma_engine(priv);
        if (ret < 0) {
@@ -1705,10 +1705,6 @@ static int stmmac_hw_setup(struct net_device *dev)
        }
        priv->tx_lpi_timer = STMMAC_DEFAULT_TWT_LS;
 
-       priv->eee_enabled = stmmac_eee_init(priv);
-
-       stmmac_init_tx_coalesce(priv);
-
        if ((priv->use_riwt) && (priv->hw->dma->rx_watchdog)) {
                priv->rx_riwt = MAX_DMA_RIWT;
                priv->hw->dma->rx_watchdog(priv->ioaddr, MAX_DMA_RIWT);
@@ -1761,12 +1757,20 @@ static int stmmac_open(struct net_device *dev)
                goto dma_desc_error;
        }
 
+       ret = init_dma_desc_rings(dev, GFP_KERNEL);
+       if (ret < 0) {
+               pr_err("%s: DMA descriptors initialization failed\n", __func__);
+               goto init_error;
+       }
+
        ret = stmmac_hw_setup(dev);
        if (ret < 0) {
                pr_err("%s: Hw setup failed\n", __func__);
                goto init_error;
        }
 
+       stmmac_init_tx_coalesce(priv);
+
        if (priv->phydev)
                phy_start(priv->phydev);
 
@@ -1894,7 +1898,10 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
        unsigned int nopaged_len = skb_headlen(skb);
        unsigned int enh_desc = priv->plat->enh_desc;
 
+       spin_lock(&priv->tx_lock);
+
        if (unlikely(stmmac_tx_avail(priv) < nfrags + 1)) {
+               spin_unlock(&priv->tx_lock);
                if (!netif_queue_stopped(dev)) {
                        netif_stop_queue(dev);
                        /* This is a hard error, log it. */
@@ -1903,8 +1910,6 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
                return NETDEV_TX_BUSY;
        }
 
-       spin_lock(&priv->tx_lock);
-
        if (priv->tx_path_in_lpi_mode)
                stmmac_disable_eee_mode(priv);
 
@@ -2025,6 +2030,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
        return NETDEV_TX_OK;
 
 dma_map_err:
+       spin_unlock(&priv->tx_lock);
        dev_err(priv->device, "Tx dma map failed\n");
        dev_kfree_skb(skb);
        priv->dev->stats.tx_dropped++;
@@ -2281,9 +2287,7 @@ static void stmmac_set_rx_mode(struct net_device *dev)
 {
        struct stmmac_priv *priv = netdev_priv(dev);
 
-       spin_lock(&priv->lock);
        priv->hw->mac->set_filter(priv->hw, dev);
-       spin_unlock(&priv->lock);
 }
 
 /**
@@ -2950,7 +2954,7 @@ int stmmac_suspend(struct net_device *ndev)
                stmmac_set_mac(priv->ioaddr, false);
                pinctrl_pm_select_sleep_state(priv->device);
                /* Disable clock in case of PWM is off */
-               clk_disable_unprepare(priv->stmmac_clk);
+               clk_disable(priv->stmmac_clk);
        }
        spin_unlock_irqrestore(&priv->lock, flags);
 
@@ -2982,7 +2986,7 @@ int stmmac_resume(struct net_device *ndev)
        } else {
                pinctrl_pm_select_default_state(priv->device);
                /* enable the clk prevously disabled */
-               clk_prepare_enable(priv->stmmac_clk);
+               clk_enable(priv->stmmac_clk);
                /* reset the phy so that it's ready */
                if (priv->mii)
                        stmmac_mdio_reset(priv->mii);
@@ -2990,7 +2994,9 @@ int stmmac_resume(struct net_device *ndev)
 
        netif_device_attach(ndev);
 
+       init_dma_desc_rings(ndev, GFP_ATOMIC);
        stmmac_hw_setup(ndev);
+       stmmac_init_tx_coalesce(priv);
 
        napi_enable(&priv->napi);
 
index db56fa7..58a1a0a 100644 (file)
@@ -177,12 +177,6 @@ static int stmmac_probe_config_dt(struct platform_device *pdev,
         */
        plat->maxmtu = JUMBO_LEN;
 
-       /* Set default value for multicast hash bins */
-       plat->multicast_filter_bins = HASH_TABLE_SIZE;
-
-       /* Set default value for unicast filter entries */
-       plat->unicast_filter_entries = 1;
-
        /*
         * Currently only the properties needed on SPEAr600
         * are provided. All other properties should be added
@@ -270,16 +264,23 @@ static int stmmac_pltfr_probe(struct platform_device *pdev)
                return PTR_ERR(addr);
 
        plat_dat = dev_get_platdata(&pdev->dev);
-       if (pdev->dev.of_node) {
-               if (!plat_dat)
-                       plat_dat = devm_kzalloc(&pdev->dev,
+
+       if (!plat_dat)
+               plat_dat = devm_kzalloc(&pdev->dev,
                                        sizeof(struct plat_stmmacenet_data),
                                        GFP_KERNEL);
-               if (!plat_dat) {
-                       pr_err("%s: ERROR: no memory", __func__);
-                       return  -ENOMEM;
-               }
+       if (!plat_dat) {
+               pr_err("%s: ERROR: no memory", __func__);
+               return  -ENOMEM;
+       }
+
+       /* Set default value for multicast hash bins */
+       plat_dat->multicast_filter_bins = HASH_TABLE_SIZE;
 
+       /* Set default value for unicast filter entries */
+       plat_dat->unicast_filter_entries = 1;
+
+       if (pdev->dev.of_node) {
                ret = stmmac_probe_config_dt(pdev, plat_dat, &mac);
                if (ret) {
                        pr_err("%s: main dt probe failed", __func__);
index 72c8525..9c01480 100644 (file)
@@ -1262,6 +1262,7 @@ static void happy_meal_init_rings(struct happy_meal *hp)
        HMD(("init rxring, "));
        for (i = 0; i < RX_RING_SIZE; i++) {
                struct sk_buff *skb;
+               u32 mapping;
 
                skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC);
                if (!skb) {
@@ -1272,10 +1273,16 @@ static void happy_meal_init_rings(struct happy_meal *hp)
 
                /* Because we reserve afterwards. */
                skb_put(skb, (ETH_FRAME_LEN + RX_OFFSET + 4));
+               mapping = dma_map_single(hp->dma_dev, skb->data, RX_BUF_ALLOC_SIZE,
+                                        DMA_FROM_DEVICE);
+               if (dma_mapping_error(hp->dma_dev, mapping)) {
+                       dev_kfree_skb_any(skb);
+                       hme_write_rxd(hp, &hb->happy_meal_rxd[i], 0, 0);
+                       continue;
+               }
                hme_write_rxd(hp, &hb->happy_meal_rxd[i],
                              (RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16)),
-                             dma_map_single(hp->dma_dev, skb->data, RX_BUF_ALLOC_SIZE,
-                                            DMA_FROM_DEVICE));
+                             mapping);
                skb_reserve(skb, RX_OFFSET);
        }
 
@@ -2020,6 +2027,7 @@ static void happy_meal_rx(struct happy_meal *hp, struct net_device *dev)
                skb = hp->rx_skbs[elem];
                if (len > RX_COPY_THRESHOLD) {
                        struct sk_buff *new_skb;
+                       u32 mapping;
 
                        /* Now refill the entry, if we can. */
                        new_skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC);
@@ -2027,13 +2035,21 @@ static void happy_meal_rx(struct happy_meal *hp, struct net_device *dev)
                                drops++;
                                goto drop_it;
                        }
+                       skb_put(new_skb, (ETH_FRAME_LEN + RX_OFFSET + 4));
+                       mapping = dma_map_single(hp->dma_dev, new_skb->data,
+                                                RX_BUF_ALLOC_SIZE,
+                                                DMA_FROM_DEVICE);
+                       if (unlikely(dma_mapping_error(hp->dma_dev, mapping))) {
+                               dev_kfree_skb_any(new_skb);
+                               drops++;
+                               goto drop_it;
+                       }
+
                        dma_unmap_single(hp->dma_dev, dma_addr, RX_BUF_ALLOC_SIZE, DMA_FROM_DEVICE);
                        hp->rx_skbs[elem] = new_skb;
-                       skb_put(new_skb, (ETH_FRAME_LEN + RX_OFFSET + 4));
                        hme_write_rxd(hp, this,
                                      (RXFLAG_OWN|((RX_BUF_ALLOC_SIZE-RX_OFFSET)<<16)),
-                                     dma_map_single(hp->dma_dev, new_skb->data, RX_BUF_ALLOC_SIZE,
-                                                    DMA_FROM_DEVICE));
+                                     mapping);
                        skb_reserve(new_skb, RX_OFFSET);
 
                        /* Trim the original skb for the netif. */
@@ -2248,6 +2264,25 @@ static void happy_meal_tx_timeout(struct net_device *dev)
        netif_wake_queue(dev);
 }
 
+static void unmap_partial_tx_skb(struct happy_meal *hp, u32 first_mapping,
+                                u32 first_len, u32 first_entry, u32 entry)
+{
+       struct happy_meal_txd *txbase = &hp->happy_block->happy_meal_txd[0];
+
+       dma_unmap_single(hp->dma_dev, first_mapping, first_len, DMA_TO_DEVICE);
+
+       first_entry = NEXT_TX(first_entry);
+       while (first_entry != entry) {
+               struct happy_meal_txd *this = &txbase[first_entry];
+               u32 addr, len;
+
+               addr = hme_read_desc32(hp, &this->tx_addr);
+               len = hme_read_desc32(hp, &this->tx_flags);
+               len &= TXFLAG_SIZE;
+               dma_unmap_page(hp->dma_dev, addr, len, DMA_TO_DEVICE);
+       }
+}
+
 static netdev_tx_t happy_meal_start_xmit(struct sk_buff *skb,
                                         struct net_device *dev)
 {
@@ -2284,6 +2319,8 @@ static netdev_tx_t happy_meal_start_xmit(struct sk_buff *skb,
 
                len = skb->len;
                mapping = dma_map_single(hp->dma_dev, skb->data, len, DMA_TO_DEVICE);
+               if (unlikely(dma_mapping_error(hp->dma_dev, mapping)))
+                       goto out_dma_error;
                tx_flags |= (TXFLAG_SOP | TXFLAG_EOP);
                hme_write_txd(hp, &hp->happy_block->happy_meal_txd[entry],
                              (tx_flags | (len & TXFLAG_SIZE)),
@@ -2299,6 +2336,8 @@ static netdev_tx_t happy_meal_start_xmit(struct sk_buff *skb,
                first_len = skb_headlen(skb);
                first_mapping = dma_map_single(hp->dma_dev, skb->data, first_len,
                                               DMA_TO_DEVICE);
+               if (unlikely(dma_mapping_error(hp->dma_dev, first_mapping)))
+                       goto out_dma_error;
                entry = NEXT_TX(entry);
 
                for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {
@@ -2308,6 +2347,11 @@ static netdev_tx_t happy_meal_start_xmit(struct sk_buff *skb,
                        len = skb_frag_size(this_frag);
                        mapping = skb_frag_dma_map(hp->dma_dev, this_frag,
                                                   0, len, DMA_TO_DEVICE);
+                       if (unlikely(dma_mapping_error(hp->dma_dev, mapping))) {
+                               unmap_partial_tx_skb(hp, first_mapping, first_len,
+                                                    first_entry, entry);
+                               goto out_dma_error;
+                       }
                        this_txflags = tx_flags;
                        if (frag == skb_shinfo(skb)->nr_frags - 1)
                                this_txflags |= TXFLAG_EOP;
@@ -2333,6 +2377,14 @@ static netdev_tx_t happy_meal_start_xmit(struct sk_buff *skb,
 
        tx_add_log(hp, TXLOG_ACTION_TXMIT, 0);
        return NETDEV_TX_OK;
+
+out_dma_error:
+       hp->tx_skbs[hp->tx_new] = NULL;
+       spin_unlock_irq(&hp->happy_lock);
+
+       dev_kfree_skb_any(skb);
+       dev->stats.tx_dropped++;
+       return NETDEV_TX_OK;
 }
 
 static struct net_device_stats *happy_meal_get_stats(struct net_device *dev)
index d879448..c560f9a 100644 (file)
@@ -129,9 +129,9 @@ do {                                                                \
 #define CPSW_VLAN_AWARE                BIT(1)
 #define CPSW_ALE_VLAN_AWARE    1
 
-#define CPSW_FIFO_NORMAL_MODE          (0 << 15)
-#define CPSW_FIFO_DUAL_MAC_MODE                (1 << 15)
-#define CPSW_FIFO_RATE_LIMIT_MODE      (2 << 15)
+#define CPSW_FIFO_NORMAL_MODE          (0 << 16)
+#define CPSW_FIFO_DUAL_MAC_MODE                (1 << 16)
+#define CPSW_FIFO_RATE_LIMIT_MODE      (2 << 16)
 
 #define CPSW_INTPACEEN         (0x3f << 16)
 #define CPSW_INTPRESCALE_MASK  (0x7FF << 0)
index 3ae8387..097ebe7 100644 (file)
@@ -785,7 +785,6 @@ int cpsw_ale_destroy(struct cpsw_ale *ale)
 {
        if (!ale)
                return -EINVAL;
-       cpsw_ale_stop(ale);
        cpsw_ale_control_set(ale, 0, ALE_ENABLE, 0);
        kfree(ale);
        return 0;
index ab92f67..4a4388b 100644 (file)
@@ -264,7 +264,7 @@ static int cpts_match(struct sk_buff *skb, unsigned int ptp_class,
 
        switch (ptp_class & PTP_CLASS_PMASK) {
        case PTP_CLASS_IPV4:
-               offset += ETH_HLEN + IPV4_HLEN(data) + UDP_HLEN;
+               offset += ETH_HLEN + IPV4_HLEN(data + offset) + UDP_HLEN;
                break;
        case PTP_CLASS_IPV6:
                offset += ETH_HLEN + IP6_HLEN + UDP_HLEN;
index 9ce854f..6cbc56a 100644 (file)
@@ -377,17 +377,20 @@ static int ieee802154fake_probe(struct platform_device *pdev)
 
        err = wpan_phy_register(phy);
        if (err)
-               goto out;
+               goto err_phy_reg;
 
        err = register_netdev(dev);
-       if (err < 0)
-               goto out;
+       if (err)
+               goto err_netdev_reg;
 
        dev_info(&pdev->dev, "Added ieee802154 HardMAC hardware\n");
        return 0;
 
-out:
-       unregister_netdev(dev);
+err_netdev_reg:
+       wpan_phy_unregister(phy);
+err_phy_reg:
+       free_netdev(dev);
+       wpan_phy_free(phy);
        return err;
 }
 
index 6f226de..880cc09 100644 (file)
@@ -629,6 +629,8 @@ static void macvtap_skb_to_vnet_hdr(const struct sk_buff *skb,
        if (skb->ip_summed == CHECKSUM_PARTIAL) {
                vnet_hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
                vnet_hdr->csum_start = skb_checksum_start_offset(skb);
+               if (vlan_tx_tag_present(skb))
+                       vnet_hdr->csum_start += VLAN_HLEN;
                vnet_hdr->csum_offset = skb->csum_offset;
        } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
                vnet_hdr->flags = VIRTIO_NET_HDR_F_DATA_VALID;
index 2954052..e22e602 100644 (file)
@@ -791,7 +791,7 @@ static int match(struct sk_buff *skb, unsigned int type, struct rxts *rxts)
 
        switch (type & PTP_CLASS_PMASK) {
        case PTP_CLASS_IPV4:
-               offset += ETH_HLEN + IPV4_HLEN(data) + UDP_HLEN;
+               offset += ETH_HLEN + IPV4_HLEN(data + offset) + UDP_HLEN;
                break;
        case PTP_CLASS_IPV6:
                offset += ETH_HLEN + IP6_HLEN + UDP_HLEN;
@@ -934,7 +934,7 @@ static int is_sync(struct sk_buff *skb, int type)
 
        switch (type & PTP_CLASS_PMASK) {
        case PTP_CLASS_IPV4:
-               offset += ETH_HLEN + IPV4_HLEN(data) + UDP_HLEN;
+               offset += ETH_HLEN + IPV4_HLEN(data + offset) + UDP_HLEN;
                break;
        case PTP_CLASS_IPV6:
                offset += ETH_HLEN + IP6_HLEN + UDP_HLEN;
index 1dfffdc..767cd11 100644 (file)
@@ -352,6 +352,7 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
 {
        struct mii_ioctl_data *mii_data = if_mii(ifr);
        u16 val = mii_data->val_in;
+       bool change_autoneg = false;
 
        switch (cmd) {
        case SIOCGMIIPHY:
@@ -367,22 +368,29 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
                if (mii_data->phy_id == phydev->addr) {
                        switch (mii_data->reg_num) {
                        case MII_BMCR:
-                               if ((val & (BMCR_RESET | BMCR_ANENABLE)) == 0)
+                               if ((val & (BMCR_RESET | BMCR_ANENABLE)) == 0) {
+                                       if (phydev->autoneg == AUTONEG_ENABLE)
+                                               change_autoneg = true;
                                        phydev->autoneg = AUTONEG_DISABLE;
-                               else
+                                       if (val & BMCR_FULLDPLX)
+                                               phydev->duplex = DUPLEX_FULL;
+                                       else
+                                               phydev->duplex = DUPLEX_HALF;
+                                       if (val & BMCR_SPEED1000)
+                                               phydev->speed = SPEED_1000;
+                                       else if (val & BMCR_SPEED100)
+                                               phydev->speed = SPEED_100;
+                                       else phydev->speed = SPEED_10;
+                               }
+                               else {
+                                       if (phydev->autoneg == AUTONEG_DISABLE)
+                                               change_autoneg = true;
                                        phydev->autoneg = AUTONEG_ENABLE;
-                               if (!phydev->autoneg && (val & BMCR_FULLDPLX))
-                                       phydev->duplex = DUPLEX_FULL;
-                               else
-                                       phydev->duplex = DUPLEX_HALF;
-                               if (!phydev->autoneg && (val & BMCR_SPEED1000))
-                                       phydev->speed = SPEED_1000;
-                               else if (!phydev->autoneg &&
-                                        (val & BMCR_SPEED100))
-                                       phydev->speed = SPEED_100;
+                               }
                                break;
                        case MII_ADVERTISE:
-                               phydev->advertising = val;
+                               phydev->advertising = mii_adv_to_ethtool_adv_t(val);
+                               change_autoneg = true;
                                break;
                        default:
                                /* do nothing */
@@ -396,6 +404,10 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
                if (mii_data->reg_num == MII_BMCR &&
                    val & BMCR_RESET)
                        return phy_init_hw(phydev);
+
+               if (change_autoneg)
+                       return phy_start_aneg(phydev);
+
                return 0;
 
        case SIOCSHWTSTAMP:
index 68c3a3f..794a473 100644 (file)
@@ -755,23 +755,23 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 
                err = get_filter(argp, &code);
                if (err >= 0) {
+                       struct bpf_prog *pass_filter = NULL;
                        struct sock_fprog_kern fprog = {
                                .len = err,
                                .filter = code,
                        };
 
-                       ppp_lock(ppp);
-                       if (ppp->pass_filter) {
-                               bpf_prog_destroy(ppp->pass_filter);
-                               ppp->pass_filter = NULL;
+                       err = 0;
+                       if (fprog.filter)
+                               err = bpf_prog_create(&pass_filter, &fprog);
+                       if (!err) {
+                               ppp_lock(ppp);
+                               if (ppp->pass_filter)
+                                       bpf_prog_destroy(ppp->pass_filter);
+                               ppp->pass_filter = pass_filter;
+                               ppp_unlock(ppp);
                        }
-                       if (fprog.filter != NULL)
-                               err = bpf_prog_create(&ppp->pass_filter,
-                                                     &fprog);
-                       else
-                               err = 0;
                        kfree(code);
-                       ppp_unlock(ppp);
                }
                break;
        }
@@ -781,23 +781,23 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 
                err = get_filter(argp, &code);
                if (err >= 0) {
+                       struct bpf_prog *active_filter = NULL;
                        struct sock_fprog_kern fprog = {
                                .len = err,
                                .filter = code,
                        };
 
-                       ppp_lock(ppp);
-                       if (ppp->active_filter) {
-                               bpf_prog_destroy(ppp->active_filter);
-                               ppp->active_filter = NULL;
+                       err = 0;
+                       if (fprog.filter)
+                               err = bpf_prog_create(&active_filter, &fprog);
+                       if (!err) {
+                               ppp_lock(ppp);
+                               if (ppp->active_filter)
+                                       bpf_prog_destroy(ppp->active_filter);
+                               ppp->active_filter = active_filter;
+                               ppp_unlock(ppp);
                        }
-                       if (fprog.filter != NULL)
-                               err = bpf_prog_create(&ppp->active_filter,
-                                                     &fprog);
-                       else
-                               err = 0;
                        kfree(code);
-                       ppp_unlock(ppp);
                }
                break;
        }
index 1aff970..1dc628f 100644 (file)
@@ -506,7 +506,9 @@ static int pptp_getname(struct socket *sock, struct sockaddr *uaddr,
        int len = sizeof(struct sockaddr_pppox);
        struct sockaddr_pppox sp;
 
-       sp.sa_family      = AF_PPPOX;
+       memset(&sp.sa_addr, 0, sizeof(sp.sa_addr));
+
+       sp.sa_family    = AF_PPPOX;
        sp.sa_protocol  = PX_PROTO_PPTP;
        sp.sa_addr.pptp = pppox_sk(sock->sk)->proto.pptp.src_addr;
 
index 7302398..9dd3746 100644 (file)
@@ -1235,12 +1235,20 @@ static ssize_t tun_put_user(struct tun_struct *tun,
        struct tun_pi pi = { 0, skb->protocol };
        ssize_t total = 0;
        int vlan_offset = 0, copied;
+       int vlan_hlen = 0;
+       int vnet_hdr_sz = 0;
+
+       if (vlan_tx_tag_present(skb))
+               vlan_hlen = VLAN_HLEN;
+
+       if (tun->flags & TUN_VNET_HDR)
+               vnet_hdr_sz = tun->vnet_hdr_sz;
 
        if (!(tun->flags & TUN_NO_PI)) {
                if ((len -= sizeof(pi)) < 0)
                        return -EINVAL;
 
-               if (len < skb->len) {
+               if (len < skb->len + vlan_hlen + vnet_hdr_sz) {
                        /* Packet will be striped */
                        pi.flags |= TUN_PKT_STRIP;
                }
@@ -1250,9 +1258,9 @@ static ssize_t tun_put_user(struct tun_struct *tun,
                total += sizeof(pi);
        }
 
-       if (tun->flags & TUN_VNET_HDR) {
+       if (vnet_hdr_sz) {
                struct virtio_net_hdr gso = { 0 }; /* no info leak */
-               if ((len -= tun->vnet_hdr_sz) < 0)
+               if ((len -= vnet_hdr_sz) < 0)
                        return -EINVAL;
 
                if (skb_is_gso(skb)) {
@@ -1284,7 +1292,8 @@ static ssize_t tun_put_user(struct tun_struct *tun,
 
                if (skb->ip_summed == CHECKSUM_PARTIAL) {
                        gso.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
-                       gso.csum_start = skb_checksum_start_offset(skb);
+                       gso.csum_start = skb_checksum_start_offset(skb) +
+                                        vlan_hlen;
                        gso.csum_offset = skb->csum_offset;
                } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
                        gso.flags = VIRTIO_NET_HDR_F_DATA_VALID;
@@ -1293,14 +1302,13 @@ static ssize_t tun_put_user(struct tun_struct *tun,
                if (unlikely(memcpy_toiovecend(iv, (void *)&gso, total,
                                               sizeof(gso))))
                        return -EFAULT;
-               total += tun->vnet_hdr_sz;
+               total += vnet_hdr_sz;
        }
 
        copied = total;
-       total += skb->len;
-       if (!vlan_tx_tag_present(skb)) {
-               len = min_t(int, skb->len, len);
-       } else {
+       len = min_t(int, skb->len + vlan_hlen, len);
+       total += skb->len + vlan_hlen;
+       if (vlan_hlen) {
                int copy, ret;
                struct {
                        __be16 h_vlan_proto;
@@ -1311,8 +1319,6 @@ static ssize_t tun_put_user(struct tun_struct *tun,
                veth.h_vlan_TCI = htons(vlan_tx_tag_get(skb));
 
                vlan_offset = offsetof(struct vlan_ethhdr, h_vlan_proto);
-               len = min_t(int, skb->len + VLAN_HLEN, len);
-               total += VLAN_HLEN;
 
                copy = min_t(int, vlan_offset, len);
                ret = skb_copy_datagram_const_iovec(skb, 0, iv, copied, copy);
index 2c05f6c..816d511 100644 (file)
@@ -465,19 +465,7 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
                return ret;
        }
 
-       ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL);
-       if (ret < 0)
-               return ret;
-
-       msleep(150);
-
-       ret = asix_sw_reset(dev, AX_SWRESET_CLEAR);
-       if (ret < 0)
-               return ret;
-
-       msleep(150);
-
-       ret = asix_sw_reset(dev, embd_phy ? AX_SWRESET_IPRL : AX_SWRESET_PRTE);
+       ax88772_reset(dev);
 
        /* Read PHYID register *AFTER* the PHY was reset properly */
        phyid = asix_get_phyid(dev);
index 22756db..b8a82b8 100644 (file)
@@ -780,6 +780,7 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x413c, 0x81a4, 8)},    /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */
        {QMI_FIXED_INTF(0x413c, 0x81a8, 8)},    /* Dell Wireless 5808 Gobi(TM) 4G LTE Mobile Broadband Card */
        {QMI_FIXED_INTF(0x413c, 0x81a9, 8)},    /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */
+       {QMI_FIXED_INTF(0x03f0, 0x581d, 4)},    /* HP lt4112 LTE/HSPA+ Gobi 4G Module (Huawei me906e) */
 
        /* 4. Gobi 1000 devices */
        {QMI_GOBI1K_DEVICE(0x05c6, 0x9212)},    /* Acer Gobi Modem Device */
index ec2a8b4..b0bc8ea 100644 (file)
@@ -1673,6 +1673,40 @@ static const struct attribute_group virtio_net_mrg_rx_group = {
 };
 #endif
 
+static bool virtnet_fail_on_feature(struct virtio_device *vdev,
+                                   unsigned int fbit,
+                                   const char *fname, const char *dname)
+{
+       if (!virtio_has_feature(vdev, fbit))
+               return false;
+
+       dev_err(&vdev->dev, "device advertises feature %s but not %s",
+               fname, dname);
+
+       return true;
+}
+
+#define VIRTNET_FAIL_ON(vdev, fbit, dbit)                      \
+       virtnet_fail_on_feature(vdev, fbit, #fbit, dbit)
+
+static bool virtnet_validate_features(struct virtio_device *vdev)
+{
+       if (!virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ) &&
+           (VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_CTRL_RX,
+                            "VIRTIO_NET_F_CTRL_VQ") ||
+            VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_CTRL_VLAN,
+                            "VIRTIO_NET_F_CTRL_VQ") ||
+            VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE,
+                            "VIRTIO_NET_F_CTRL_VQ") ||
+            VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_MQ, "VIRTIO_NET_F_CTRL_VQ") ||
+            VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR,
+                            "VIRTIO_NET_F_CTRL_VQ"))) {
+               return false;
+       }
+
+       return true;
+}
+
 static int virtnet_probe(struct virtio_device *vdev)
 {
        int i, err;
@@ -1680,6 +1714,9 @@ static int virtnet_probe(struct virtio_device *vdev)
        struct virtnet_info *vi;
        u16 max_queue_pairs;
 
+       if (!virtnet_validate_features(vdev))
+               return -EINVAL;
+
        /* Find if host supports multiqueue virtio_net device */
        err = virtio_cread_feature(vdev, VIRTIO_NET_F_MQ,
                                   struct virtio_net_config,
index ca30982..be4649a 100644 (file)
 
 #define VXLAN_FLAGS 0x08000000 /* struct vxlanhdr.vx_flags required value. */
 
-/* VXLAN protocol header */
-struct vxlanhdr {
-       __be32 vx_flags;
-       __be32 vx_vni;
-};
-
 /* UDP port for VXLAN traffic.
  * The IANA assigned port is 4789, but the Linux default is 8472
  * for compatibility with early adopters.
@@ -275,13 +269,15 @@ static inline struct vxlan_rdst *first_remote_rtnl(struct vxlan_fdb *fdb)
        return list_first_entry(&fdb->remotes, struct vxlan_rdst, list);
 }
 
-/* Find VXLAN socket based on network namespace and UDP port */
-static struct vxlan_sock *vxlan_find_sock(struct net *net, __be16 port)
+/* Find VXLAN socket based on network namespace, address family and UDP port */
+static struct vxlan_sock *vxlan_find_sock(struct net *net,
+                                         sa_family_t family, __be16 port)
 {
        struct vxlan_sock *vs;
 
        hlist_for_each_entry_rcu(vs, vs_head(net, port), hlist) {
-               if (inet_sk(vs->sock->sk)->inet_sport == port)
+               if (inet_sk(vs->sock->sk)->inet_sport == port &&
+                   inet_sk(vs->sock->sk)->sk.sk_family == family)
                        return vs;
        }
        return NULL;
@@ -300,11 +296,12 @@ static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs, u32 id)
 }
 
 /* Look up VNI in a per net namespace table */
-static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id, __be16 port)
+static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id,
+                                       sa_family_t family, __be16 port)
 {
        struct vxlan_sock *vs;
 
-       vs = vxlan_find_sock(net, port);
+       vs = vxlan_find_sock(net, family, port);
        if (!vs)
                return NULL;
 
@@ -621,6 +618,8 @@ static int vxlan_gro_complete(struct sk_buff *skb, int nhoff)
        int vxlan_len  = sizeof(struct vxlanhdr) + sizeof(struct ethhdr);
        int err = -ENOSYS;
 
+       udp_tunnel_gro_complete(skb, nhoff);
+
        eh = (struct ethhdr *)(skb->data + nhoff + sizeof(struct vxlanhdr));
        type = eh->h_proto;
 
@@ -1771,7 +1770,8 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                        struct vxlan_dev *dst_vxlan;
 
                        ip_rt_put(rt);
-                       dst_vxlan = vxlan_find_vni(vxlan->net, vni, dst_port);
+                       dst_vxlan = vxlan_find_vni(vxlan->net, vni,
+                                                  dst->sa.sa_family, dst_port);
                        if (!dst_vxlan)
                                goto tx_error;
                        vxlan_encap_bypass(skb, vxlan, dst_vxlan);
@@ -1825,7 +1825,8 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                        struct vxlan_dev *dst_vxlan;
 
                        dst_release(ndst);
-                       dst_vxlan = vxlan_find_vni(vxlan->net, vni, dst_port);
+                       dst_vxlan = vxlan_find_vni(vxlan->net, vni,
+                                                  dst->sa.sa_family, dst_port);
                        if (!dst_vxlan)
                                goto tx_error;
                        vxlan_encap_bypass(skb, vxlan, dst_vxlan);
@@ -1985,13 +1986,15 @@ static int vxlan_init(struct net_device *dev)
        struct vxlan_dev *vxlan = netdev_priv(dev);
        struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id);
        struct vxlan_sock *vs;
+       bool ipv6 = vxlan->flags & VXLAN_F_IPV6;
 
        dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
        if (!dev->tstats)
                return -ENOMEM;
 
        spin_lock(&vn->sock_lock);
-       vs = vxlan_find_sock(vxlan->net, vxlan->dst_port);
+       vs = vxlan_find_sock(vxlan->net, ipv6 ? AF_INET6 : AF_INET,
+                            vxlan->dst_port);
        if (vs) {
                /* If we have a socket with same port already, reuse it */
                atomic_inc(&vs->refcnt);
@@ -2303,9 +2306,9 @@ static struct socket *vxlan_create_sock(struct net *net, bool ipv6,
        if (ipv6) {
                udp_conf.family = AF_INET6;
                udp_conf.use_udp6_tx_checksums =
-                   !!(flags & VXLAN_F_UDP_ZERO_CSUM6_TX);
+                   !(flags & VXLAN_F_UDP_ZERO_CSUM6_TX);
                udp_conf.use_udp6_rx_checksums =
-                   !!(flags & VXLAN_F_UDP_ZERO_CSUM6_RX);
+                   !(flags & VXLAN_F_UDP_ZERO_CSUM6_RX);
        } else {
                udp_conf.family = AF_INET;
                udp_conf.local_ip.s_addr = INADDR_ANY;
@@ -2382,6 +2385,7 @@ struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port,
 {
        struct vxlan_net *vn = net_generic(net, vxlan_net_id);
        struct vxlan_sock *vs;
+       bool ipv6 = flags & VXLAN_F_IPV6;
 
        vs = vxlan_socket_create(net, port, rcv, data, flags);
        if (!IS_ERR(vs))
@@ -2391,7 +2395,7 @@ struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port,
                return vs;
 
        spin_lock(&vn->sock_lock);
-       vs = vxlan_find_sock(net, port);
+       vs = vxlan_find_sock(net, ipv6 ? AF_INET6 : AF_INET, port);
        if (vs) {
                if (vs->rcv == rcv)
                        atomic_inc(&vs->refcnt);
@@ -2550,7 +2554,8 @@ static int vxlan_newlink(struct net *net, struct net_device *dev,
            nla_get_u8(data[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]))
                vxlan->flags |= VXLAN_F_UDP_ZERO_CSUM6_RX;
 
-       if (vxlan_find_vni(net, vni, vxlan->dst_port)) {
+       if (vxlan_find_vni(net, vni, use_ipv6 ? AF_INET6 : AF_INET,
+                          vxlan->dst_port)) {
                pr_info("duplicate VNI %u\n", vni);
                return -EEXIST;
        }
index 697c4ae..1e8ea5e 100644 (file)
@@ -664,6 +664,19 @@ static void ar9003_hw_override_ini(struct ath_hw *ah)
                ah->enabled_cals |= TX_CL_CAL;
        else
                ah->enabled_cals &= ~TX_CL_CAL;
+
+       if (AR_SREV_9340(ah) || AR_SREV_9531(ah) || AR_SREV_9550(ah)) {
+               if (ah->is_clk_25mhz) {
+                       REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x17c << 1);
+                       REG_WRITE(ah, AR_SLP32_MODE, 0x0010f3d7);
+                       REG_WRITE(ah, AR_SLP32_INC, 0x0001e7ae);
+               } else {
+                       REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x261 << 1);
+                       REG_WRITE(ah, AR_SLP32_MODE, 0x0010f400);
+                       REG_WRITE(ah, AR_SLP32_INC, 0x0001e800);
+               }
+               udelay(100);
+       }
 }
 
 static void ar9003_hw_prog_ini(struct ath_hw *ah,
index 8be4b14..2ad6057 100644 (file)
@@ -861,19 +861,6 @@ static void ath9k_hw_init_pll(struct ath_hw *ah,
        udelay(RTC_PLL_SETTLE_DELAY);
 
        REG_WRITE(ah, AR_RTC_SLEEP_CLK, AR_RTC_FORCE_DERIVED_CLK);
-
-       if (AR_SREV_9340(ah) || AR_SREV_9550(ah)) {
-               if (ah->is_clk_25mhz) {
-                       REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x17c << 1);
-                       REG_WRITE(ah, AR_SLP32_MODE, 0x0010f3d7);
-                       REG_WRITE(ah,  AR_SLP32_INC, 0x0001e7ae);
-               } else {
-                       REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x261 << 1);
-                       REG_WRITE(ah, AR_SLP32_MODE, 0x0010f400);
-                       REG_WRITE(ah,  AR_SLP32_INC, 0x0001e800);
-               }
-               udelay(100);
-       }
 }
 
 static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah,
index 30c66df..4f18a6b 100644 (file)
@@ -974,9 +974,8 @@ void ath9k_calculate_iter_data(struct ath_softc *sc,
        struct ath_vif *avp;
 
        /*
-        * Pick the MAC address of the first interface as the new hardware
-        * MAC address. The hardware will use it together with the BSSID mask
-        * when matching addresses.
+        * The hardware will use primary station addr together with the
+        * BSSID mask when matching addresses.
         */
        memset(iter_data, 0, sizeof(*iter_data));
        memset(&iter_data->mask, 0xff, ETH_ALEN);
@@ -1205,6 +1204,8 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
                list_add_tail(&avp->list, &avp->chanctx->vifs);
        }
 
+       ath9k_calculate_summary_state(sc, avp->chanctx);
+
        ath9k_assign_hw_queues(hw, vif);
 
        an->sc = sc;
@@ -1274,6 +1275,8 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
 
        ath_tx_node_cleanup(sc, &avp->mcast_node);
 
+       ath9k_calculate_summary_state(sc, avp->chanctx);
+
        mutex_unlock(&sc->mutex);
 }
 
index 1dfc682..ee27b06 100644 (file)
@@ -300,9 +300,7 @@ void b43_phy_write(struct b43_wldev *dev, u16 reg, u16 value)
 
 void b43_phy_copy(struct b43_wldev *dev, u16 destreg, u16 srcreg)
 {
-       assert_mac_suspended(dev);
-       dev->phy.ops->phy_write(dev, destreg,
-               dev->phy.ops->phy_read(dev, srcreg));
+       b43_phy_write(dev, destreg, b43_phy_read(dev, srcreg));
 }
 
 void b43_phy_mask(struct b43_wldev *dev, u16 offset, u16 mask)
index f05f527..927bffd 100644 (file)
@@ -40,8 +40,8 @@ void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev)
                return;
 
        irq = irq_of_parse_and_map(np, 0);
-       if (irq < 0) {
-               brcmf_err("interrupt could not be mapped: err=%d\n", irq);
+       if (!irq) {
+               brcmf_err("interrupt could not be mapped\n");
                devm_kfree(dev, sdiodev->pdata);
                return;
        }
index 8c0632e..16fef33 100644 (file)
 #include <linux/pci.h>
 #include <linux/vmalloc.h>
 #include <linux/delay.h>
-#include <linux/unaligned/access_ok.h>
 #include <linux/interrupt.h>
 #include <linux/bcma/bcma.h>
 #include <linux/sched.h>
+#include <asm/unaligned.h>
 
 #include <soc.h>
 #include <chipcommon.h>
index dc13591..875d114 100644 (file)
@@ -669,10 +669,12 @@ static int brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd,
                goto finalize;
        }
 
-       if (!brcmf_usb_ioctl_resp_wait(devinfo))
+       if (!brcmf_usb_ioctl_resp_wait(devinfo)) {
+               usb_kill_urb(devinfo->ctl_urb);
                ret = -ETIMEDOUT;
-       else
+       } else {
                memcpy(buffer, tmpbuf, buflen);
+       }
 
 finalize:
        kfree(tmpbuf);
index 28fa25b..39b45c0 100644 (file)
@@ -299,6 +299,7 @@ static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
        primary_offset = ch->center_freq1 - ch->chan->center_freq;
        switch (ch->width) {
        case NL80211_CHAN_WIDTH_20:
+       case NL80211_CHAN_WIDTH_20_NOHT:
                ch_inf.bw = BRCMU_CHAN_BW_20;
                WARN_ON(primary_offset != 0);
                break;
@@ -323,6 +324,10 @@ static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
                                ch_inf.sb = BRCMU_CHAN_SB_LU;
                }
                break;
+       case NL80211_CHAN_WIDTH_80P80:
+       case NL80211_CHAN_WIDTH_160:
+       case NL80211_CHAN_WIDTH_5:
+       case NL80211_CHAN_WIDTH_10:
        default:
                WARN_ON_ONCE(1);
        }
@@ -333,6 +338,7 @@ static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
        case IEEE80211_BAND_5GHZ:
                ch_inf.band = BRCMU_CHAN_BAND_5G;
                break;
+       case IEEE80211_BAND_60GHZ:
        default:
                WARN_ON_ONCE(1);
        }
index 4f6e668..b894a84 100644 (file)
@@ -155,6 +155,7 @@ enum iwl_ucode_tlv_api {
  * @IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT: supports Quiet Period requests
  * @IWL_UCODE_TLV_CAPA_DQA_SUPPORT: supports dynamic queue allocation (DQA),
  *     which also implies support for the scheduler configuration command
+ * @IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT: supports Hot Spot Command
  */
 enum iwl_ucode_tlv_capa {
        IWL_UCODE_TLV_CAPA_D0I3_SUPPORT                 = BIT(0),
@@ -163,6 +164,7 @@ enum iwl_ucode_tlv_capa {
        IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT       = BIT(10),
        IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT         = BIT(11),
        IWL_UCODE_TLV_CAPA_DQA_SUPPORT                  = BIT(12),
+       IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT              = BIT(18),
 };
 
 /* The default calibrate table size if not specified by firmware file */
index e0d9f19..eb03943 100644 (file)
@@ -284,7 +284,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
 
        lockdep_assert_held(&mvm->mutex);
 
-       if (WARN_ON_ONCE(mvm->init_ucode_complete))
+       if (WARN_ON_ONCE(mvm->init_ucode_complete || mvm->calibrating))
                return 0;
 
        iwl_init_notification_wait(&mvm->notif_wait,
@@ -334,6 +334,8 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
                goto out;
        }
 
+       mvm->calibrating = true;
+
        /* Send TX valid antennas before triggering calibrations */
        ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant);
        if (ret)
@@ -358,11 +360,17 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
                        MVM_UCODE_CALIB_TIMEOUT);
        if (!ret)
                mvm->init_ucode_complete = true;
+
+       if (ret && iwl_mvm_is_radio_killed(mvm)) {
+               IWL_DEBUG_RF_KILL(mvm, "RFKILL while calibrating.\n");
+               ret = 1;
+       }
        goto out;
 
 error:
        iwl_remove_notification(&mvm->notif_wait, &calib_wait);
 out:
+       mvm->calibrating = false;
        if (iwlmvm_mod_params.init_dbg && !mvm->nvm_data) {
                /* we want to debug INIT and we have no NVM - fake */
                mvm->nvm_data = kzalloc(sizeof(struct iwl_nvm_data) +
index 585fe5b..b6d2683 100644 (file)
@@ -788,6 +788,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
 
        mvm->scan_status = IWL_MVM_SCAN_NONE;
        mvm->ps_disabled = false;
+       mvm->calibrating = false;
 
        /* just in case one was running */
        ieee80211_remain_on_channel_expired(mvm->hw);
@@ -2447,9 +2448,15 @@ static int iwl_mvm_roc(struct ieee80211_hw *hw,
 
        switch (vif->type) {
        case NL80211_IFTYPE_STATION:
-               /* Use aux roc framework (HS20) */
-               ret = iwl_mvm_send_aux_roc_cmd(mvm, channel,
-                                              vif, duration);
+               if (mvm->fw->ucode_capa.capa[0] &
+                   IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT) {
+                       /* Use aux roc framework (HS20) */
+                       ret = iwl_mvm_send_aux_roc_cmd(mvm, channel,
+                                                      vif, duration);
+                       goto out_unlock;
+               }
+               IWL_ERR(mvm, "hotspot not supported\n");
+               ret = -EINVAL;
                goto out_unlock;
        case NL80211_IFTYPE_P2P_DEVICE:
                /* handle below */
index b153ced..845429c 100644 (file)
@@ -548,6 +548,7 @@ struct iwl_mvm {
        enum iwl_ucode_type cur_ucode;
        bool ucode_loaded;
        bool init_ucode_complete;
+       bool calibrating;
        u32 error_event_table;
        u32 log_event_table;
        u32 umac_error_event_table;
index 48cb25a..5b719ee 100644 (file)
@@ -424,6 +424,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        }
        mvm->sf_state = SF_UNINIT;
        mvm->low_latency_agg_frame_limit = 6;
+       mvm->cur_ucode = IWL_UCODE_INIT;
 
        mutex_init(&mvm->mutex);
        mutex_init(&mvm->d0i3_suspend_mutex);
@@ -752,6 +753,7 @@ void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state)
 static bool iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
 {
        struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+       bool calibrating = ACCESS_ONCE(mvm->calibrating);
 
        if (state)
                set_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status);
@@ -760,7 +762,15 @@ static bool iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
 
        wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm));
 
-       return state && mvm->cur_ucode != IWL_UCODE_INIT;
+       /* iwl_run_init_mvm_ucode is waiting for results, abort it */
+       if (calibrating)
+               iwl_abort_notification_waits(&mvm->notif_wait);
+
+       /*
+        * Stop the device if we run OPERATIONAL firmware or if we are in the
+        * middle of the calibrations.
+        */
+       return state && (mvm->cur_ucode != IWL_UCODE_INIT || calibrating);
 }
 
 static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)
index b280d5d..7554f70 100644 (file)
@@ -602,16 +602,6 @@ static int iwl_mvm_cancel_regular_scan(struct iwl_mvm *mvm)
                                               SCAN_COMPLETE_NOTIFICATION };
        int ret;
 
-       if (mvm->scan_status == IWL_MVM_SCAN_NONE)
-               return 0;
-
-       if (iwl_mvm_is_radio_killed(mvm)) {
-               ieee80211_scan_completed(mvm->hw, true);
-               iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
-               mvm->scan_status = IWL_MVM_SCAN_NONE;
-               return 0;
-       }
-
        iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_abort,
                                   scan_abort_notif,
                                   ARRAY_SIZE(scan_abort_notif),
@@ -1400,6 +1390,16 @@ int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm,
 
 int iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
 {
+       if (mvm->scan_status == IWL_MVM_SCAN_NONE)
+               return 0;
+
+       if (iwl_mvm_is_radio_killed(mvm)) {
+               ieee80211_scan_completed(mvm->hw, true);
+               iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
+               mvm->scan_status = IWL_MVM_SCAN_NONE;
+               return 0;
+       }
+
        if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
                return iwl_mvm_scan_offload_stop(mvm, true);
        return iwl_mvm_cancel_regular_scan(mvm);
index 3781b02..dd2f3f8 100644 (file)
@@ -915,7 +915,8 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
         * restart. So don't process again if the device is
         * already dead.
         */
-       if (test_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
+       if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
+               IWL_DEBUG_INFO(trans, "DEVICE_ENABLED bit was set and is now cleared\n");
                iwl_pcie_tx_stop(trans);
                iwl_pcie_rx_stop(trans);
 
@@ -945,7 +946,6 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
        /* clear all status bits */
        clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
        clear_bit(STATUS_INT_ENABLED, &trans->status);
-       clear_bit(STATUS_DEVICE_ENABLED, &trans->status);
        clear_bit(STATUS_TPOWER_PMI, &trans->status);
        clear_bit(STATUS_RFKILL, &trans->status);
 
@@ -1894,8 +1894,7 @@ static u32 iwl_trans_pcie_dump_prph(struct iwl_trans *trans,
                int reg;
                __le32 *val;
 
-               prph_len += sizeof(*data) + sizeof(*prph) +
-                       num_bytes_in_chunk;
+               prph_len += sizeof(**data) + sizeof(*prph) + num_bytes_in_chunk;
 
                (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH);
                (*data)->len = cpu_to_le32(sizeof(*prph) +
index babbdc1..c9ad4cf 100644 (file)
@@ -1987,7 +1987,7 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
        if (err != 0) {
                printk(KERN_DEBUG "mac80211_hwsim: device_bind_driver failed (%d)\n",
                       err);
-               goto failed_hw;
+               goto failed_bind;
        }
 
        skb_queue_head_init(&data->pending);
@@ -2183,6 +2183,8 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
        return idx;
 
 failed_hw:
+       device_release_driver(data->dev);
+failed_bind:
        device_unregister(data->dev);
 failed_drvdata:
        ieee80211_free_hw(hw);
index 8e68f87..66ff364 100644 (file)
@@ -158,55 +158,29 @@ void rt2x00queue_align_frame(struct sk_buff *skb)
        skb_trim(skb, frame_length);
 }
 
-void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int header_length)
+/*
+ * H/W needs L2 padding between the header and the paylod if header size
+ * is not 4 bytes aligned.
+ */
+void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int hdr_len)
 {
-       unsigned int payload_length = skb->len - header_length;
-       unsigned int header_align = ALIGN_SIZE(skb, 0);
-       unsigned int payload_align = ALIGN_SIZE(skb, header_length);
-       unsigned int l2pad = payload_length ? L2PAD_SIZE(header_length) : 0;
+       unsigned int l2pad = (skb->len > hdr_len) ? L2PAD_SIZE(hdr_len) : 0;
 
-       /*
-        * Adjust the header alignment if the payload needs to be moved more
-        * than the header.
-        */
-       if (payload_align > header_align)
-               header_align += 4;
-
-       /* There is nothing to do if no alignment is needed */
-       if (!header_align)
+       if (!l2pad)
                return;
 
-       /* Reserve the amount of space needed in front of the frame */
-       skb_push(skb, header_align);
-
-       /*
-        * Move the header.
-        */
-       memmove(skb->data, skb->data + header_align, header_length);
-
-       /* Move the payload, if present and if required */
-       if (payload_length && payload_align)
-               memmove(skb->data + header_length + l2pad,
-                       skb->data + header_length + l2pad + payload_align,
-                       payload_length);
-
-       /* Trim the skb to the correct size */
-       skb_trim(skb, header_length + l2pad + payload_length);
+       skb_push(skb, l2pad);
+       memmove(skb->data, skb->data + l2pad, hdr_len);
 }
 
-void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length)
+void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int hdr_len)
 {
-       /*
-        * L2 padding is only present if the skb contains more than just the
-        * IEEE 802.11 header.
-        */
-       unsigned int l2pad = (skb->len > header_length) ?
-                               L2PAD_SIZE(header_length) : 0;
+       unsigned int l2pad = (skb->len > hdr_len) ? L2PAD_SIZE(hdr_len) : 0;
 
        if (!l2pad)
                return;
 
-       memmove(skb->data + l2pad, skb->data, header_length);
+       memmove(skb->data + l2pad, skb->data, hdr_len);
        skb_pull(skb, l2pad);
 }
 
index 25daa87..846a2e6 100644 (file)
@@ -842,7 +842,8 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
                        break;
                }
                /* handle command packet here */
-               if (rtlpriv->cfg->ops->rx_command_packet(hw, stats, skb)) {
+               if (rtlpriv->cfg->ops->rx_command_packet &&
+                   rtlpriv->cfg->ops->rx_command_packet(hw, stats, skb)) {
                                dev_kfree_skb_any(skb);
                                goto end;
                }
@@ -1127,9 +1128,14 @@ static void _rtl_pci_prepare_bcn_tasklet(struct ieee80211_hw *hw)
 
        __skb_queue_tail(&ring->queue, pskb);
 
-       rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, true, HW_DESC_OWN,
-                                   &temp_one);
-
+       if (rtlpriv->use_new_trx_flow) {
+               temp_one = 4;
+               rtlpriv->cfg->ops->set_desc(hw, (u8 *)pbuffer_desc, true,
+                                           HW_DESC_OWN, (u8 *)&temp_one);
+       } else {
+               rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, true, HW_DESC_OWN,
+                                           &temp_one);
+       }
        return;
 }
 
@@ -1370,9 +1376,9 @@ static void _rtl_pci_free_tx_ring(struct ieee80211_hw *hw,
        ring->desc = NULL;
        if (rtlpriv->use_new_trx_flow) {
                pci_free_consistent(rtlpci->pdev,
-                                   sizeof(*ring->desc) * ring->entries,
+                                   sizeof(*ring->buffer_desc) * ring->entries,
                                    ring->buffer_desc, ring->buffer_desc_dma);
-               ring->desc = NULL;
+               ring->buffer_desc = NULL;
        }
 }
 
@@ -1543,7 +1549,6 @@ int rtl_pci_reset_trx_ring(struct ieee80211_hw *hw)
                                                         true,
                                                         HW_DESC_TXBUFF_ADDR),
                                                 skb->len, PCI_DMA_TODEVICE);
-                               ring->idx = (ring->idx + 1) % ring->entries;
                                kfree_skb(skb);
                                ring->idx = (ring->idx + 1) % ring->entries;
                        }
@@ -2244,6 +2249,16 @@ int rtl_pci_probe(struct pci_dev *pdev,
        /*like read eeprom and so on */
        rtlpriv->cfg->ops->read_eeprom_info(hw);
 
+       if (rtlpriv->cfg->ops->init_sw_vars(hw)) {
+               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Can't init_sw_vars\n");
+               err = -ENODEV;
+               goto fail3;
+       }
+       rtlpriv->cfg->ops->init_sw_leds(hw);
+
+       /*aspm */
+       rtl_pci_init_aspm(hw);
+
        /* Init mac80211 sw */
        err = rtl_init_core(hw);
        if (err) {
@@ -2259,16 +2274,6 @@ int rtl_pci_probe(struct pci_dev *pdev,
                goto fail3;
        }
 
-       if (rtlpriv->cfg->ops->init_sw_vars(hw)) {
-               RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Can't init_sw_vars\n");
-               err = -ENODEV;
-               goto fail3;
-       }
-       rtlpriv->cfg->ops->init_sw_leds(hw);
-
-       /*aspm */
-       rtl_pci_init_aspm(hw);
-
        err = ieee80211_register_hw(hw);
        if (err) {
                RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
index 00e0670..5761d5b 100644 (file)
@@ -1201,6 +1201,9 @@ static int _rtl92se_set_media_status(struct ieee80211_hw *hw,
 
        }
 
+       if (type != NL80211_IFTYPE_AP &&
+           rtlpriv->mac80211.link_state < MAC80211_LINKED)
+               bt_msr = rtl_read_byte(rtlpriv, MSR) & ~MSR_LINK_MASK;
        rtl_write_byte(rtlpriv, (MSR), bt_msr);
 
        temp = rtl_read_dword(rtlpriv, TCR);
@@ -1262,6 +1265,7 @@ void rtl92se_enable_interrupt(struct ieee80211_hw *hw)
        rtl_write_dword(rtlpriv, INTA_MASK, rtlpci->irq_mask[0]);
        /* Support Bit 32-37(Assign as Bit 0-5) interrupt setting now */
        rtl_write_dword(rtlpriv, INTA_MASK + 4, rtlpci->irq_mask[1] & 0x3F);
+       rtlpci->irq_enabled = true;
 }
 
 void rtl92se_disable_interrupt(struct ieee80211_hw *hw)
@@ -1276,8 +1280,7 @@ void rtl92se_disable_interrupt(struct ieee80211_hw *hw)
        rtlpci = rtl_pcidev(rtl_pcipriv(hw));
        rtl_write_dword(rtlpriv, INTA_MASK, 0);
        rtl_write_dword(rtlpriv, INTA_MASK + 4, 0);
-
-       synchronize_irq(rtlpci->pdev->irq);
+       rtlpci->irq_enabled = false;
 }
 
 static u8 _rtl92s_set_sysclk(struct ieee80211_hw *hw, u8 data)
index 77c5b5f..4b4612f 100644 (file)
@@ -399,6 +399,8 @@ static bool _rtl92s_phy_sw_chnl_step_by_step(struct ieee80211_hw *hw,
                case 2:
                        currentcmd = &postcommoncmd[*step];
                        break;
+               default:
+                       return true;
                }
 
                if (currentcmd->cmdid == CMDID_END) {
index aadba29..fb00386 100644 (file)
@@ -236,6 +236,19 @@ static void rtl92s_deinit_sw_vars(struct ieee80211_hw *hw)
        }
 }
 
+static bool rtl92se_is_tx_desc_closed(struct ieee80211_hw *hw, u8 hw_queue,
+                                     u16 index)
+{
+       struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+       struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[hw_queue];
+       u8 *entry = (u8 *)(&ring->desc[ring->idx]);
+       u8 own = (u8)rtl92se_get_desc(entry, true, HW_DESC_OWN);
+
+       if (own)
+               return false;
+       return true;
+}
+
 static struct rtl_hal_ops rtl8192se_hal_ops = {
        .init_sw_vars = rtl92s_init_sw_vars,
        .deinit_sw_vars = rtl92s_deinit_sw_vars,
@@ -269,6 +282,7 @@ static struct rtl_hal_ops rtl8192se_hal_ops = {
        .led_control = rtl92se_led_control,
        .set_desc = rtl92se_set_desc,
        .get_desc = rtl92se_get_desc,
+       .is_tx_desc_closed = rtl92se_is_tx_desc_closed,
        .tx_polling = rtl92se_tx_polling,
        .enable_hw_sec = rtl92se_enable_hw_security_config,
        .set_key = rtl92se_set_key,
@@ -306,6 +320,8 @@ static struct rtl_hal_cfg rtl92se_hal_cfg = {
        .maps[MAC_RCR_ACRC32] = RCR_ACRC32,
        .maps[MAC_RCR_ACF] = RCR_ACF,
        .maps[MAC_RCR_AAP] = RCR_AAP,
+       .maps[MAC_HIMR] = INTA_MASK,
+       .maps[MAC_HIMRE] = INTA_MASK + 4,
 
        .maps[EFUSE_TEST] = REG_EFUSE_TEST,
        .maps[EFUSE_CTRL] = REG_EFUSE_CTRL,
index 310d316..8ec8200 100644 (file)
@@ -3672,8 +3672,9 @@ static void rtl8821ae_update_hal_rate_mask(struct ieee80211_hw *hw,
                mac->opmode == NL80211_IFTYPE_ADHOC)
                macid = sta->aid + 1;
        if (wirelessmode == WIRELESS_MODE_N_5G ||
-           wirelessmode == WIRELESS_MODE_AC_5G)
-               ratr_bitmap = sta->supp_rates[NL80211_BAND_5GHZ];
+           wirelessmode == WIRELESS_MODE_AC_5G ||
+           wirelessmode == WIRELESS_MODE_A)
+               ratr_bitmap = sta->supp_rates[NL80211_BAND_5GHZ] << 4;
        else
                ratr_bitmap = sta->supp_rates[NL80211_BAND_2GHZ];
 
index 4e56a27..fab0d4b 100644 (file)
@@ -39,7 +39,7 @@ struct backend_info {
 static int connect_rings(struct backend_info *be, struct xenvif_queue *queue);
 static void connect(struct backend_info *be);
 static int read_xenbus_vif_flags(struct backend_info *be);
-static void backend_create_xenvif(struct backend_info *be);
+static int backend_create_xenvif(struct backend_info *be);
 static void unregister_hotplug_status_watch(struct backend_info *be);
 static void set_backend_state(struct backend_info *be,
                              enum xenbus_state state);
@@ -352,7 +352,9 @@ static int netback_probe(struct xenbus_device *dev,
        be->state = XenbusStateInitWait;
 
        /* This kicks hotplug scripts, so do it immediately. */
-       backend_create_xenvif(be);
+       err = backend_create_xenvif(be);
+       if (err)
+               goto fail;
 
        return 0;
 
@@ -397,19 +399,19 @@ static int netback_uevent(struct xenbus_device *xdev,
 }
 
 
-static void backend_create_xenvif(struct backend_info *be)
+static int backend_create_xenvif(struct backend_info *be)
 {
        int err;
        long handle;
        struct xenbus_device *dev = be->dev;
 
        if (be->vif != NULL)
-               return;
+               return 0;
 
        err = xenbus_scanf(XBT_NIL, dev->nodename, "handle", "%li", &handle);
        if (err != 1) {
                xenbus_dev_fatal(dev, err, "reading handle");
-               return;
+               return (err < 0) ? err : -EINVAL;
        }
 
        be->vif = xenvif_alloc(&dev->dev, dev->otherend_id, handle);
@@ -417,10 +419,11 @@ static void backend_create_xenvif(struct backend_info *be)
                err = PTR_ERR(be->vif);
                be->vif = NULL;
                xenbus_dev_fatal(dev, err, "creating interface");
-               return;
+               return err;
        }
 
        kobject_uevent(&dev->dev.kobj, KOBJ_ONLINE);
+       return 0;
 }
 
 static void backend_disconnect(struct backend_info *be)
index cca8713..ece8d18 100644 (file)
@@ -496,9 +496,6 @@ static void xennet_make_frags(struct sk_buff *skb, struct netfront_queue *queue,
                len = skb_frag_size(frag);
                offset = frag->page_offset;
 
-               /* Data must not cross a page boundary. */
-               BUG_ON(len + offset > PAGE_SIZE<<compound_order(page));
-
                /* Skip unused frames from start of page */
                page += offset >> PAGE_SHIFT;
                offset &= ~PAGE_MASK;
@@ -506,8 +503,6 @@ static void xennet_make_frags(struct sk_buff *skb, struct netfront_queue *queue,
                while (len > 0) {
                        unsigned long bytes;
 
-                       BUG_ON(offset >= PAGE_SIZE);
-
                        bytes = PAGE_SIZE - offset;
                        if (bytes > len)
                                bytes = len;
index afdb782..06af494 100644 (file)
@@ -450,6 +450,21 @@ static struct of_bus *of_match_bus(struct device_node *np)
        return NULL;
 }
 
+static int of_empty_ranges_quirk(void)
+{
+       if (IS_ENABLED(CONFIG_PPC)) {
+               /* To save cycles, we cache the result */
+               static int quirk_state = -1;
+
+               if (quirk_state < 0)
+                       quirk_state =
+                               of_machine_is_compatible("Power Macintosh") ||
+                               of_machine_is_compatible("MacRISC");
+               return quirk_state;
+       }
+       return false;
+}
+
 static int of_translate_one(struct device_node *parent, struct of_bus *bus,
                            struct of_bus *pbus, __be32 *addr,
                            int na, int ns, int pna, const char *rprop)
@@ -475,12 +490,10 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus,
         * This code is only enabled on powerpc. --gcl
         */
        ranges = of_get_property(parent, rprop, &rlen);
-#if !defined(CONFIG_PPC)
-       if (ranges == NULL) {
+       if (ranges == NULL && !of_empty_ranges_quirk()) {
                pr_err("OF: no ranges; cannot translate\n");
                return 1;
        }
-#endif /* !defined(CONFIG_PPC) */
        if (ranges == NULL || rlen == 0) {
                offset = of_read_number(addr, na);
                memset(addr, 0, pna * 4);
index 2305dc0..3823edf 100644 (file)
@@ -1279,52 +1279,6 @@ int of_property_read_string(struct device_node *np, const char *propname,
 }
 EXPORT_SYMBOL_GPL(of_property_read_string);
 
-/**
- * of_property_read_string_index - Find and read a string from a multiple
- * strings property.
- * @np:                device node from which the property value is to be read.
- * @propname:  name of the property to be searched.
- * @index:     index of the string in the list of strings
- * @out_string:        pointer to null terminated return string, modified only if
- *             return value is 0.
- *
- * Search for a property in a device tree node and retrieve a null
- * terminated string value (pointer to data, not a copy) in the list of strings
- * contained in that property.
- * Returns 0 on success, -EINVAL if the property does not exist, -ENODATA if
- * property does not have a value, and -EILSEQ if the string is not
- * null-terminated within the length of the property data.
- *
- * The out_string pointer is modified only if a valid string can be decoded.
- */
-int of_property_read_string_index(struct device_node *np, const char *propname,
-                                 int index, const char **output)
-{
-       struct property *prop = of_find_property(np, propname, NULL);
-       int i = 0;
-       size_t l = 0, total = 0;
-       const char *p;
-
-       if (!prop)
-               return -EINVAL;
-       if (!prop->value)
-               return -ENODATA;
-       if (strnlen(prop->value, prop->length) >= prop->length)
-               return -EILSEQ;
-
-       p = prop->value;
-
-       for (i = 0; total < prop->length; total += l, p += l) {
-               l = strlen(p) + 1;
-               if (i++ == index) {
-                       *output = p;
-                       return 0;
-               }
-       }
-       return -ENODATA;
-}
-EXPORT_SYMBOL_GPL(of_property_read_string_index);
-
 /**
  * of_property_match_string() - Find string in a list and return index
  * @np: pointer to node containing string list property
@@ -1351,7 +1305,7 @@ int of_property_match_string(struct device_node *np, const char *propname,
        end = p + prop->length;
 
        for (i = 0; p < end; i++, p += l) {
-               l = strlen(p) + 1;
+               l = strnlen(p, end - p) + 1;
                if (p + l > end)
                        return -EILSEQ;
                pr_debug("comparing %s with %s\n", string, p);
@@ -1363,39 +1317,41 @@ int of_property_match_string(struct device_node *np, const char *propname,
 EXPORT_SYMBOL_GPL(of_property_match_string);
 
 /**
- * of_property_count_strings - Find and return the number of strings from a
- * multiple strings property.
+ * of_property_read_string_util() - Utility helper for parsing string properties
  * @np:                device node from which the property value is to be read.
  * @propname:  name of the property to be searched.
+ * @out_strs:  output array of string pointers.
+ * @sz:                number of array elements to read.
+ * @skip:      Number of strings to skip over at beginning of list.
  *
- * Search for a property in a device tree node and retrieve the number of null
- * terminated string contain in it. Returns the number of strings on
- * success, -EINVAL if the property does not exist, -ENODATA if property
- * does not have a value, and -EILSEQ if the string is not null-terminated
- * within the length of the property data.
+ * Don't call this function directly. It is a utility helper for the
+ * of_property_read_string*() family of functions.
  */
-int of_property_count_strings(struct device_node *np, const char *propname)
+int of_property_read_string_helper(struct device_node *np, const char *propname,
+                                  const char **out_strs, size_t sz, int skip)
 {
        struct property *prop = of_find_property(np, propname, NULL);
-       int i = 0;
-       size_t l = 0, total = 0;
-       const char *p;
+       int l = 0, i = 0;
+       const char *p, *end;
 
        if (!prop)
                return -EINVAL;
        if (!prop->value)
                return -ENODATA;
-       if (strnlen(prop->value, prop->length) >= prop->length)
-               return -EILSEQ;
-
        p = prop->value;
+       end = p + prop->length;
 
-       for (i = 0; total < prop->length; total += l, p += l, i++)
-               l = strlen(p) + 1;
-
-       return i;
+       for (i = 0; p < end && (!out_strs || i < skip + sz); i++, p += l) {
+               l = strnlen(p, end - p) + 1;
+               if (p + l > end)
+                       return -EILSEQ;
+               if (out_strs && i >= skip)
+                       *out_strs++ = p;
+       }
+       i -= skip;
+       return i <= 0 ? -ENODATA : i;
 }
-EXPORT_SYMBOL_GPL(of_property_count_strings);
+EXPORT_SYMBOL_GPL(of_property_read_string_helper);
 
 void of_print_phandle_args(const char *msg, const struct of_phandle_args *args)
 {
index f297891..d499417 100644 (file)
@@ -247,7 +247,7 @@ void of_node_release(struct kobject *kobj)
  * @allocflags:        Allocation flags (typically pass GFP_KERNEL)
  *
  * Copy a property by dynamically allocating the memory of both the
- * property stucture and the property name & contents. The property's
+ * property structure and the property name & contents. The property's
  * flags have the OF_DYNAMIC bit set so that we can differentiate between
  * dynamically allocated properties and not.
  * Returns the newly allocated property or NULL on out of memory error.
index d1ffca8..d134710 100644 (file)
@@ -773,7 +773,7 @@ int __init early_init_dt_scan_chosen_serial(void)
        if (offset < 0)
                return -ENODEV;
 
-       while (match->compatible) {
+       while (match->compatible[0]) {
                unsigned long addr;
                if (fdt_node_check_compatible(fdt, offset, match->compatible)) {
                        match++;
@@ -964,8 +964,6 @@ void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size)
 int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base,
                                        phys_addr_t size, bool nomap)
 {
-       if (memblock_is_region_reserved(base, size))
-               return -EBUSY;
        if (nomap)
                return memblock_remove(base, size);
        return memblock_reserve(base, size);
index 7800127..e2d79af 100644 (file)
@@ -339,8 +339,9 @@ static void __init of_selftest_parse_phandle_with_args(void)
        selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
 }
 
-static void __init of_selftest_property_match_string(void)
+static void __init of_selftest_property_string(void)
 {
+       const char *strings[4];
        struct device_node *np;
        int rc;
 
@@ -357,13 +358,66 @@ static void __init of_selftest_property_match_string(void)
        rc = of_property_match_string(np, "phandle-list-names", "third");
        selftest(rc == 2, "third expected:0 got:%i\n", rc);
        rc = of_property_match_string(np, "phandle-list-names", "fourth");
-       selftest(rc == -ENODATA, "unmatched string; rc=%i", rc);
+       selftest(rc == -ENODATA, "unmatched string; rc=%i\n", rc);
        rc = of_property_match_string(np, "missing-property", "blah");
-       selftest(rc == -EINVAL, "missing property; rc=%i", rc);
+       selftest(rc == -EINVAL, "missing property; rc=%i\n", rc);
        rc = of_property_match_string(np, "empty-property", "blah");
-       selftest(rc == -ENODATA, "empty property; rc=%i", rc);
+       selftest(rc == -ENODATA, "empty property; rc=%i\n", rc);
        rc = of_property_match_string(np, "unterminated-string", "blah");
-       selftest(rc == -EILSEQ, "unterminated string; rc=%i", rc);
+       selftest(rc == -EILSEQ, "unterminated string; rc=%i\n", rc);
+
+       /* of_property_count_strings() tests */
+       rc = of_property_count_strings(np, "string-property");
+       selftest(rc == 1, "Incorrect string count; rc=%i\n", rc);
+       rc = of_property_count_strings(np, "phandle-list-names");
+       selftest(rc == 3, "Incorrect string count; rc=%i\n", rc);
+       rc = of_property_count_strings(np, "unterminated-string");
+       selftest(rc == -EILSEQ, "unterminated string; rc=%i\n", rc);
+       rc = of_property_count_strings(np, "unterminated-string-list");
+       selftest(rc == -EILSEQ, "unterminated string array; rc=%i\n", rc);
+
+       /* of_property_read_string_index() tests */
+       rc = of_property_read_string_index(np, "string-property", 0, strings);
+       selftest(rc == 0 && !strcmp(strings[0], "foobar"), "of_property_read_string_index() failure; rc=%i\n", rc);
+       strings[0] = NULL;
+       rc = of_property_read_string_index(np, "string-property", 1, strings);
+       selftest(rc == -ENODATA && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc);
+       rc = of_property_read_string_index(np, "phandle-list-names", 0, strings);
+       selftest(rc == 0 && !strcmp(strings[0], "first"), "of_property_read_string_index() failure; rc=%i\n", rc);
+       rc = of_property_read_string_index(np, "phandle-list-names", 1, strings);
+       selftest(rc == 0 && !strcmp(strings[0], "second"), "of_property_read_string_index() failure; rc=%i\n", rc);
+       rc = of_property_read_string_index(np, "phandle-list-names", 2, strings);
+       selftest(rc == 0 && !strcmp(strings[0], "third"), "of_property_read_string_index() failure; rc=%i\n", rc);
+       strings[0] = NULL;
+       rc = of_property_read_string_index(np, "phandle-list-names", 3, strings);
+       selftest(rc == -ENODATA && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc);
+       strings[0] = NULL;
+       rc = of_property_read_string_index(np, "unterminated-string", 0, strings);
+       selftest(rc == -EILSEQ && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc);
+       rc = of_property_read_string_index(np, "unterminated-string-list", 0, strings);
+       selftest(rc == 0 && !strcmp(strings[0], "first"), "of_property_read_string_index() failure; rc=%i\n", rc);
+       strings[0] = NULL;
+       rc = of_property_read_string_index(np, "unterminated-string-list", 2, strings); /* should fail */
+       selftest(rc == -EILSEQ && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc);
+       strings[1] = NULL;
+
+       /* of_property_read_string_array() tests */
+       rc = of_property_read_string_array(np, "string-property", strings, 4);
+       selftest(rc == 1, "Incorrect string count; rc=%i\n", rc);
+       rc = of_property_read_string_array(np, "phandle-list-names", strings, 4);
+       selftest(rc == 3, "Incorrect string count; rc=%i\n", rc);
+       rc = of_property_read_string_array(np, "unterminated-string", strings, 4);
+       selftest(rc == -EILSEQ, "unterminated string; rc=%i\n", rc);
+       /* -- An incorrectly formed string should cause a failure */
+       rc = of_property_read_string_array(np, "unterminated-string-list", strings, 4);
+       selftest(rc == -EILSEQ, "unterminated string array; rc=%i\n", rc);
+       /* -- parsing the correctly formed strings should still work: */
+       strings[2] = NULL;
+       rc = of_property_read_string_array(np, "unterminated-string-list", strings, 2);
+       selftest(rc == 2 && strings[2] == NULL, "of_property_read_string_array() failure; rc=%i\n", rc);
+       strings[1] = NULL;
+       rc = of_property_read_string_array(np, "phandle-list-names", strings, 1);
+       selftest(rc == 1 && strings[1] == NULL, "Overwrote end of string array; rc=%i, str='%s'\n", rc, strings[1]);
 }
 
 #define propcmp(p1, p2) (((p1)->length == (p2)->length) && \
@@ -842,10 +896,14 @@ static void selftest_data_remove(void)
                return;
        }
 
-       while (last_node_index >= 0) {
+       while (last_node_index-- > 0) {
                if (nodes[last_node_index]) {
                        np = of_find_node_by_path(nodes[last_node_index]->full_name);
-                       if (strcmp(np->full_name, "/aliases") != 0) {
+                       if (np == nodes[last_node_index]) {
+                               if (of_aliases == np) {
+                                       of_node_put(of_aliases);
+                                       of_aliases = NULL;
+                               }
                                detach_node_and_children(np);
                        } else {
                                for_each_property_of_node(np, prop) {
@@ -854,7 +912,6 @@ static void selftest_data_remove(void)
                                }
                        }
                }
-               last_node_index--;
        }
 }
 
@@ -867,6 +924,8 @@ static int __init of_selftest(void)
        res = selftest_data_add();
        if (res)
                return res;
+       if (!of_aliases)
+               of_aliases = of_find_node_by_path("/aliases");
 
        np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a");
        if (!np) {
@@ -881,7 +940,7 @@ static int __init of_selftest(void)
        of_selftest_find_node_by_name();
        of_selftest_dynamic();
        of_selftest_parse_phandle_with_args();
-       of_selftest_property_match_string();
+       of_selftest_property_string();
        of_selftest_property_copy();
        of_selftest_changeset();
        of_selftest_parse_interrupts();
index ce0fe08..5b1527e 100644 (file)
@@ -39,7 +39,9 @@
                                phandle-list-bad-args = <&provider2 1 0>,
                                                        <&provider3 0>;
                                empty-property;
+                               string-property = "foobar";
                                unterminated-string = [40 41 42 43];
+                               unterminated-string-list = "first", "second", [40 41 42 43];
                        };
                };
        };
index d292d7c..49dd766 100644 (file)
@@ -444,7 +444,7 @@ static inline int pcie_cap_version(const struct pci_dev *dev)
        return pcie_caps_reg(dev) & PCI_EXP_FLAGS_VERS;
 }
 
-static inline bool pcie_cap_has_lnkctl(const struct pci_dev *dev)
+bool pcie_cap_has_lnkctl(const struct pci_dev *dev)
 {
        int type = pci_pcie_type(dev);
 
index 3d43874..19bb19c 100644 (file)
@@ -276,6 +276,7 @@ struct tegra_pcie {
 
        struct resource all;
        struct resource io;
+       struct resource pio;
        struct resource mem;
        struct resource prefetch;
        struct resource busn;
@@ -658,7 +659,6 @@ static int tegra_pcie_setup(int nr, struct pci_sys_data *sys)
 {
        struct tegra_pcie *pcie = sys_to_pcie(sys);
        int err;
-       phys_addr_t io_start;
 
        err = devm_request_resource(pcie->dev, &pcie->all, &pcie->mem);
        if (err < 0)
@@ -668,14 +668,12 @@ static int tegra_pcie_setup(int nr, struct pci_sys_data *sys)
        if (err)
                return err;
 
-       io_start = pci_pio_to_address(pcie->io.start);
-
        pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset);
        pci_add_resource_offset(&sys->resources, &pcie->prefetch,
                                sys->mem_offset);
        pci_add_resource(&sys->resources, &pcie->busn);
 
-       pci_ioremap_io(nr * SZ_64K, io_start);
+       pci_ioremap_io(pcie->pio.start, pcie->io.start);
 
        return 1;
 }
@@ -786,7 +784,6 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg)
 static void tegra_pcie_setup_translations(struct tegra_pcie *pcie)
 {
        u32 fpci_bar, size, axi_address;
-       phys_addr_t io_start = pci_pio_to_address(pcie->io.start);
 
        /* Bar 0: type 1 extended configuration space */
        fpci_bar = 0xfe100000;
@@ -799,7 +796,7 @@ static void tegra_pcie_setup_translations(struct tegra_pcie *pcie)
        /* Bar 1: downstream IO bar */
        fpci_bar = 0xfdfc0000;
        size = resource_size(&pcie->io);
-       axi_address = io_start;
+       axi_address = pcie->io.start;
        afi_writel(pcie, axi_address, AFI_AXI_BAR1_START);
        afi_writel(pcie, size >> 12, AFI_AXI_BAR1_SZ);
        afi_writel(pcie, fpci_bar, AFI_FPCI_BAR1);
@@ -1690,8 +1687,23 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
 
                switch (res.flags & IORESOURCE_TYPE_BITS) {
                case IORESOURCE_IO:
-                       memcpy(&pcie->io, &res, sizeof(res));
-                       pcie->io.name = np->full_name;
+                       memcpy(&pcie->pio, &res, sizeof(res));
+                       pcie->pio.name = np->full_name;
+
+                       /*
+                        * The Tegra PCIe host bridge uses this to program the
+                        * mapping of the I/O space to the physical address,
+                        * so we override the .start and .end fields here that
+                        * of_pci_range_to_resource() converted to I/O space.
+                        * We also set the IORESOURCE_MEM type to clarify that
+                        * the resource is in the physical memory space.
+                        */
+                       pcie->io.start = range.cpu_addr;
+                       pcie->io.end = range.cpu_addr + range.size - 1;
+                       pcie->io.flags = IORESOURCE_MEM;
+                       pcie->io.name = "I/O";
+
+                       memcpy(&res, &pcie->io, sizeof(res));
                        break;
 
                case IORESOURCE_MEM:
index 9ecabfa..2988fe1 100644 (file)
@@ -631,10 +631,15 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
        if (ret)
                return ret;
 
-       bus = pci_scan_root_bus(&pdev->dev, 0, &xgene_pcie_ops, port, &res);
+       bus = pci_create_root_bus(&pdev->dev, 0,
+                                       &xgene_pcie_ops, port, &res);
        if (!bus)
                return -ENOMEM;
 
+       pci_scan_child_bus(bus);
+       pci_assign_unassigned_bus_resources(bus);
+       pci_bus_add_devices(bus);
+
        platform_set_drvdata(pdev, port);
        return 0;
 }
index 9fab30a..084587d 100644 (file)
@@ -590,6 +590,20 @@ static struct msi_desc *msi_setup_entry(struct pci_dev *dev)
        return entry;
 }
 
+static int msi_verify_entries(struct pci_dev *dev)
+{
+       struct msi_desc *entry;
+
+       list_for_each_entry(entry, &dev->msi_list, list) {
+               if (!dev->no_64bit_msi || !entry->msg.address_hi)
+                       continue;
+               dev_err(&dev->dev, "Device has broken 64-bit MSI but arch"
+                       " tried to assign one above 4G\n");
+               return -EIO;
+       }
+       return 0;
+}
+
 /**
  * msi_capability_init - configure device's MSI capability structure
  * @dev: pointer to the pci_dev data structure of MSI device function
@@ -627,6 +641,13 @@ static int msi_capability_init(struct pci_dev *dev, int nvec)
                return ret;
        }
 
+       ret = msi_verify_entries(dev);
+       if (ret) {
+               msi_mask_irq(entry, mask, ~mask);
+               free_msi_irqs(dev);
+               return ret;
+       }
+
        ret = populate_msi_sysfs(dev);
        if (ret) {
                msi_mask_irq(entry, mask, ~mask);
@@ -739,6 +760,11 @@ static int msix_capability_init(struct pci_dev *dev,
        if (ret)
                goto out_avail;
 
+       /* Check if all MSI entries honor device restrictions */
+       ret = msi_verify_entries(dev);
+       if (ret)
+               goto out_free;
+
        /*
         * Some devices require MSI-X to be enabled before we can touch the
         * MSI-X registers.  We need to mask all the vectors to prevent
index 0601890..4a3902d 100644 (file)
@@ -6,6 +6,8 @@
 
 extern const unsigned char pcie_link_speed[];
 
+bool pcie_cap_has_lnkctl(const struct pci_dev *dev);
+
 /* Functions internal to the PCI core code */
 
 int pci_create_sysfs_dev_files(struct pci_dev *pdev);
index 5ed9930..c8ca98c 100644 (file)
@@ -407,15 +407,16 @@ static void pci_read_bridge_mmio_pref(struct pci_bus *child)
 {
        struct pci_dev *dev = child->self;
        u16 mem_base_lo, mem_limit_lo;
-       unsigned long base, limit;
+       u64 base64, limit64;
+       dma_addr_t base, limit;
        struct pci_bus_region region;
        struct resource *res;
 
        res = child->resource[2];
        pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo);
        pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo);
-       base = ((unsigned long) mem_base_lo & PCI_PREF_RANGE_MASK) << 16;
-       limit = ((unsigned long) mem_limit_lo & PCI_PREF_RANGE_MASK) << 16;
+       base64 = (mem_base_lo & PCI_PREF_RANGE_MASK) << 16;
+       limit64 = (mem_limit_lo & PCI_PREF_RANGE_MASK) << 16;
 
        if ((mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) {
                u32 mem_base_hi, mem_limit_hi;
@@ -429,17 +430,20 @@ static void pci_read_bridge_mmio_pref(struct pci_bus *child)
                 * this, just assume they are not being used.
                 */
                if (mem_base_hi <= mem_limit_hi) {
-#if BITS_PER_LONG == 64
-                       base |= ((unsigned long) mem_base_hi) << 32;
-                       limit |= ((unsigned long) mem_limit_hi) << 32;
-#else
-                       if (mem_base_hi || mem_limit_hi) {
-                               dev_err(&dev->dev, "can't handle 64-bit address space for bridge\n");
-                               return;
-                       }
-#endif
+                       base64 |= (u64) mem_base_hi << 32;
+                       limit64 |= (u64) mem_limit_hi << 32;
                }
        }
+
+       base = (dma_addr_t) base64;
+       limit = (dma_addr_t) limit64;
+
+       if (base != base64) {
+               dev_err(&dev->dev, "can't handle bridge window above 4GB (bus address %#010llx)\n",
+                       (unsigned long long) base64);
+               return;
+       }
+
        if (base <= limit) {
                res->flags = (mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) |
                                         IORESOURCE_MEM | IORESOURCE_PREFETCH;
@@ -1323,7 +1327,7 @@ static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
                        ~hpp->pci_exp_devctl_and, hpp->pci_exp_devctl_or);
 
        /* Initialize Link Control Register */
-       if (dev->subordinate)
+       if (pcie_cap_has_lnkctl(dev))
                pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL,
                        ~hpp->pci_exp_lnkctl_and, hpp->pci_exp_lnkctl_or);
 
index 8c84298..f091576 100644 (file)
@@ -258,14 +258,16 @@ static int omap_usb2_probe(struct platform_device *pdev)
        otg->phy                = &phy->phy;
 
        platform_set_drvdata(pdev, phy);
+       pm_runtime_enable(phy->dev);
 
        generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL);
-       if (IS_ERR(generic_phy))
+       if (IS_ERR(generic_phy)) {
+               pm_runtime_disable(phy->dev);
                return PTR_ERR(generic_phy);
+       }
 
        phy_set_drvdata(generic_phy, phy);
 
-       pm_runtime_enable(phy->dev);
        phy_provider = devm_of_phy_provider_register(phy->dev,
                        of_phy_simple_xlate);
        if (IS_ERR(phy_provider)) {
index e12e5b0..9dc3814 100644 (file)
@@ -227,10 +227,14 @@ static int byt_irq_type(struct irq_data *d, unsigned type)
        spin_lock_irqsave(&vg->lock, flags);
        value = readl(reg);
 
+       WARN(value & BYT_DIRECT_IRQ_EN,
+               "Bad pad config for io mode, force direct_irq_en bit clearing");
+
        /* For level trigges the BYT_TRIG_POS and BYT_TRIG_NEG bits
         * are used to indicate high and low level triggering
         */
-       value &= ~(BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL);
+       value &= ~(BYT_DIRECT_IRQ_EN | BYT_TRIG_POS | BYT_TRIG_NEG |
+                  BYT_TRIG_LVL);
 
        switch (type) {
        case IRQ_TYPE_LEVEL_HIGH:
@@ -318,7 +322,7 @@ static int byt_gpio_direction_output(struct gpio_chip *chip,
                "Potential Error: Setting GPIO with direct_irq_en to output");
 
        reg_val = readl(reg) | BYT_DIR_MASK;
-       reg_val &= ~BYT_OUTPUT_EN;
+       reg_val &= ~(BYT_OUTPUT_EN | BYT_INPUT_EN);
 
        if (value)
                writel(reg_val | BYT_LEVEL, reg);
index 4dcfb71..a2eabe6 100644 (file)
@@ -202,6 +202,7 @@ config TC1100_WMI
 config HP_ACCEL
        tristate "HP laptop accelerometer"
        depends on INPUT && ACPI
+       depends on SERIO_I8042
        select SENSORS_LIS3LV02D
        select NEW_LEDS
        select LEDS_CLASS
index 96a0b75..26c4fd1 100644 (file)
@@ -579,6 +579,17 @@ static const struct dmi_system_id video_vendor_dmi_table[] __initconst = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5741"),
                },
        },
+       {
+               /*
+                * Note no video_set_backlight_video_vendor, we must use the
+                * acer interface, as there is no native backlight interface.
+                */
+               .ident = "Acer KAV80",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "KAV80"),
+               },
+       },
        {}
 };
 
index 3a4951f..c1a6cd6 100644 (file)
@@ -180,6 +180,15 @@ static const struct dmi_system_id asus_quirks[] = {
                },
                .driver_data = &quirk_asus_wapf4,
        },
+       {
+               .callback = dmi_matched,
+               .ident = "ASUSTeK COMPUTER INC. X550VB",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X550VB"),
+               },
+               .driver_data = &quirk_asus_wapf4,
+       },
        {
                .callback = dmi_matched,
                .ident = "ASUSTeK COMPUTER INC. X55A",
index 13e14ec..6bec745 100644 (file)
@@ -37,6 +37,8 @@
 #include <linux/leds.h>
 #include <linux/atomic.h>
 #include <linux/acpi.h>
+#include <linux/i8042.h>
+#include <linux/serio.h>
 #include "../../misc/lis3lv02d/lis3lv02d.h"
 
 #define DRIVER_NAME     "hp_accel"
@@ -73,6 +75,13 @@ static inline void delayed_sysfs_set(struct led_classdev *led_cdev,
 
 /* HP-specific accelerometer driver ------------------------------------ */
 
+/* e0 25, e0 26, e0 27, e0 28 are scan codes that the accelerometer with acpi id
+ * HPQ6000 sends through the keyboard bus */
+#define ACCEL_1 0x25
+#define ACCEL_2 0x26
+#define ACCEL_3 0x27
+#define ACCEL_4 0x28
+
 /* For automatic insertion of the module */
 static const struct acpi_device_id lis3lv02d_device_ids[] = {
        {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */
@@ -294,6 +303,35 @@ static void lis3lv02d_enum_resources(struct acpi_device *device)
                printk(KERN_DEBUG DRIVER_NAME ": Error getting resources\n");
 }
 
+static bool hp_accel_i8042_filter(unsigned char data, unsigned char str,
+                                 struct serio *port)
+{
+       static bool extended;
+
+       if (str & I8042_STR_AUXDATA)
+               return false;
+
+       if (data == 0xe0) {
+               extended = true;
+               return true;
+       } else if (unlikely(extended)) {
+               extended = false;
+
+               switch (data) {
+               case ACCEL_1:
+               case ACCEL_2:
+               case ACCEL_3:
+               case ACCEL_4:
+                       return true;
+               default:
+                       serio_interrupt(port, 0xe0, 0);
+                       return false;
+               }
+       }
+
+       return false;
+}
+
 static int lis3lv02d_add(struct acpi_device *device)
 {
        int ret;
@@ -326,6 +364,11 @@ static int lis3lv02d_add(struct acpi_device *device)
        if (ret)
                return ret;
 
+       /* filter to remove HPQ6000 accelerometer data
+        * from keyboard bus stream */
+       if (strstr(dev_name(&device->dev), "HPQ6000"))
+               i8042_install_filter(hp_accel_i8042_filter);
+
        INIT_WORK(&hpled_led.work, delayed_set_status_worker);
        ret = led_classdev_register(NULL, &hpled_led.led_classdev);
        if (ret) {
@@ -343,6 +386,7 @@ static int lis3lv02d_remove(struct acpi_device *device)
        if (!device)
                return -EINVAL;
 
+       i8042_remove_filter(hp_accel_i8042_filter);
        lis3lv02d_joystick_disable(&lis3_dev);
        lis3lv02d_poweroff(&lis3_dev);
 
index 02152de..ed494f3 100644 (file)
@@ -837,6 +837,13 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {
                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 2"),
                },
        },
+       {
+               .ident = "Lenovo Yoga 3 Pro 1370",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+                       DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 3 Pro-1370"),
+               },
+       },
        {}
 };
 
index 5a59665..ff765d8 100644 (file)
@@ -1559,6 +1559,16 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = {
                },
         .driver_data = &samsung_broken_acpi_video,
        },
+       {
+        .callback = samsung_dmi_matched,
+        .ident = "NC210",
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+               DMI_MATCH(DMI_PRODUCT_NAME, "NC210/NC110"),
+               DMI_MATCH(DMI_BOARD_NAME, "NC210/NC110"),
+               },
+        .driver_data = &samsung_broken_acpi_video,
+       },
        {
         .callback = samsung_dmi_matched,
         .ident = "730U3E/740U3E",
index ef3a190..ab6151f 100644 (file)
@@ -240,6 +240,12 @@ static const struct dmi_system_id toshiba_alt_keymap_dmi[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "Qosmio X75-A"),
                },
        },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "TECRA A50-A"),
+               },
+       },
        {}
 };
 
index 217da4b..99a78d3 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/time.h>
+#include <linux/time64.h>
 #include <linux/of.h>
 #include <linux/completion.h>
 #include <linux/mfd/core.h>
@@ -108,7 +109,7 @@ enum ab8500_fg_calibration_state {
 struct ab8500_fg_avg_cap {
        int avg;
        int samples[NBR_AVG_SAMPLES];
-       __kernel_time_t time_stamps[NBR_AVG_SAMPLES];
+       time64_t time_stamps[NBR_AVG_SAMPLES];
        int pos;
        int nbr_samples;
        int sum;
@@ -386,15 +387,15 @@ static int ab8500_fg_is_low_curr(struct ab8500_fg *di, int curr)
  */
 static int ab8500_fg_add_cap_sample(struct ab8500_fg *di, int sample)
 {
-       struct timespec ts;
+       struct timespec64 ts64;
        struct ab8500_fg_avg_cap *avg = &di->avg_cap;
 
-       getnstimeofday(&ts);
+       getnstimeofday64(&ts64);
 
        do {
                avg->sum += sample - avg->samples[avg->pos];
                avg->samples[avg->pos] = sample;
-               avg->time_stamps[avg->pos] = ts.tv_sec;
+               avg->time_stamps[avg->pos] = ts64.tv_sec;
                avg->pos++;
 
                if (avg->pos == NBR_AVG_SAMPLES)
@@ -407,7 +408,7 @@ static int ab8500_fg_add_cap_sample(struct ab8500_fg *di, int sample)
                 * Check the time stamp for each sample. If too old,
                 * replace with latest sample
                 */
-       } while (ts.tv_sec - VALID_CAPACITY_SEC > avg->time_stamps[avg->pos]);
+       } while (ts64.tv_sec - VALID_CAPACITY_SEC > avg->time_stamps[avg->pos]);
 
        avg->avg = avg->sum / avg->nbr_samples;
 
@@ -446,14 +447,14 @@ static void ab8500_fg_clear_cap_samples(struct ab8500_fg *di)
 static void ab8500_fg_fill_cap_sample(struct ab8500_fg *di, int sample)
 {
        int i;
-       struct timespec ts;
+       struct timespec64 ts64;
        struct ab8500_fg_avg_cap *avg = &di->avg_cap;
 
-       getnstimeofday(&ts);
+       getnstimeofday64(&ts64);
 
        for (i = 0; i < NBR_AVG_SAMPLES; i++) {
                avg->samples[i] = sample;
-               avg->time_stamps[i] = ts.tv_sec;
+               avg->time_stamps[i] = ts64.tv_sec;
        }
 
        avg->pos = 0;
index e384844..1f49986 100644 (file)
@@ -1579,8 +1579,15 @@ static int bq2415x_probe(struct i2c_client *client,
        if (np) {
                bq->notify_psy = power_supply_get_by_phandle(np, "ti,usb-charger-detection");
 
-               if (!bq->notify_psy)
-                       return -EPROBE_DEFER;
+               if (IS_ERR(bq->notify_psy)) {
+                       dev_info(&client->dev,
+                               "no 'ti,usb-charger-detection' property (err=%ld)\n",
+                               PTR_ERR(bq->notify_psy));
+                       bq->notify_psy = NULL;
+               } else if (!bq->notify_psy) {
+                       ret = -EPROBE_DEFER;
+                       goto error_2;
+               }
        }
        else if (pdata->notify_device)
                bq->notify_psy = power_supply_get_by_name(pdata->notify_device);
@@ -1602,27 +1609,27 @@ static int bq2415x_probe(struct i2c_client *client,
                ret = of_property_read_u32(np, "ti,current-limit",
                                &bq->init_data.current_limit);
                if (ret)
-                       return ret;
+                       goto error_2;
                ret = of_property_read_u32(np, "ti,weak-battery-voltage",
                                &bq->init_data.weak_battery_voltage);
                if (ret)
-                       return ret;
+                       goto error_2;
                ret = of_property_read_u32(np, "ti,battery-regulation-voltage",
                                &bq->init_data.battery_regulation_voltage);
                if (ret)
-                       return ret;
+                       goto error_2;
                ret = of_property_read_u32(np, "ti,charge-current",
                                &bq->init_data.charge_current);
                if (ret)
-                       return ret;
+                       goto error_2;
                ret = of_property_read_u32(np, "ti,termination-current",
                                &bq->init_data.termination_current);
                if (ret)
-                       return ret;
+                       goto error_2;
                ret = of_property_read_u32(np, "ti,resistor-sense",
                                &bq->init_data.resistor_sense);
                if (ret)
-                       return ret;
+                       goto error_2;
        } else {
                memcpy(&bq->init_data, pdata, sizeof(bq->init_data));
        }
index 7098a1c..ef8094a 100644 (file)
@@ -97,6 +97,7 @@ static struct charger_global_desc *g_desc; /* init with setup_charger_manager */
 static bool is_batt_present(struct charger_manager *cm)
 {
        union power_supply_propval val;
+       struct power_supply *psy;
        bool present = false;
        int i, ret;
 
@@ -107,16 +108,27 @@ static bool is_batt_present(struct charger_manager *cm)
        case CM_NO_BATTERY:
                break;
        case CM_FUEL_GAUGE:
-               ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+               psy = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
+               if (!psy)
+                       break;
+
+               ret = psy->get_property(psy,
                                POWER_SUPPLY_PROP_PRESENT, &val);
                if (ret == 0 && val.intval)
                        present = true;
                break;
        case CM_CHARGER_STAT:
-               for (i = 0; cm->charger_stat[i]; i++) {
-                       ret = cm->charger_stat[i]->get_property(
-                                       cm->charger_stat[i],
-                                       POWER_SUPPLY_PROP_PRESENT, &val);
+               for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
+                       psy = power_supply_get_by_name(
+                                       cm->desc->psy_charger_stat[i]);
+                       if (!psy) {
+                               dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
+                                       cm->desc->psy_charger_stat[i]);
+                               continue;
+                       }
+
+                       ret = psy->get_property(psy, POWER_SUPPLY_PROP_PRESENT,
+                                       &val);
                        if (ret == 0 && val.intval) {
                                present = true;
                                break;
@@ -139,14 +151,20 @@ static bool is_batt_present(struct charger_manager *cm)
 static bool is_ext_pwr_online(struct charger_manager *cm)
 {
        union power_supply_propval val;
+       struct power_supply *psy;
        bool online = false;
        int i, ret;
 
        /* If at least one of them has one, it's yes. */
-       for (i = 0; cm->charger_stat[i]; i++) {
-               ret = cm->charger_stat[i]->get_property(
-                               cm->charger_stat[i],
-                               POWER_SUPPLY_PROP_ONLINE, &val);
+       for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
+               psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]);
+               if (!psy) {
+                       dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
+                                       cm->desc->psy_charger_stat[i]);
+                       continue;
+               }
+
+               ret = psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &val);
                if (ret == 0 && val.intval) {
                        online = true;
                        break;
@@ -167,12 +185,14 @@ static bool is_ext_pwr_online(struct charger_manager *cm)
 static int get_batt_uV(struct charger_manager *cm, int *uV)
 {
        union power_supply_propval val;
+       struct power_supply *fuel_gauge;
        int ret;
 
-       if (!cm->fuel_gauge)
+       fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
+       if (!fuel_gauge)
                return -ENODEV;
 
-       ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+       ret = fuel_gauge->get_property(fuel_gauge,
                                POWER_SUPPLY_PROP_VOLTAGE_NOW, &val);
        if (ret)
                return ret;
@@ -189,6 +209,7 @@ static bool is_charging(struct charger_manager *cm)
 {
        int i, ret;
        bool charging = false;
+       struct power_supply *psy;
        union power_supply_propval val;
 
        /* If there is no battery, it cannot be charged */
@@ -196,17 +217,22 @@ static bool is_charging(struct charger_manager *cm)
                return false;
 
        /* If at least one of the charger is charging, return yes */
-       for (i = 0; cm->charger_stat[i]; i++) {
+       for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
                /* 1. The charger sholuld not be DISABLED */
                if (cm->emergency_stop)
                        continue;
                if (!cm->charger_enabled)
                        continue;
 
+               psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]);
+               if (!psy) {
+                       dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
+                                       cm->desc->psy_charger_stat[i]);
+                       continue;
+               }
+
                /* 2. The charger should be online (ext-power) */
-               ret = cm->charger_stat[i]->get_property(
-                               cm->charger_stat[i],
-                               POWER_SUPPLY_PROP_ONLINE, &val);
+               ret = psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &val);
                if (ret) {
                        dev_warn(cm->dev, "Cannot read ONLINE value from %s\n",
                                 cm->desc->psy_charger_stat[i]);
@@ -219,9 +245,7 @@ static bool is_charging(struct charger_manager *cm)
                 * 3. The charger should not be FULL, DISCHARGING,
                 * or NOT_CHARGING.
                 */
-               ret = cm->charger_stat[i]->get_property(
-                               cm->charger_stat[i],
-                               POWER_SUPPLY_PROP_STATUS, &val);
+               ret = psy->get_property(psy, POWER_SUPPLY_PROP_STATUS, &val);
                if (ret) {
                        dev_warn(cm->dev, "Cannot read STATUS value from %s\n",
                                 cm->desc->psy_charger_stat[i]);
@@ -248,6 +272,7 @@ static bool is_full_charged(struct charger_manager *cm)
 {
        struct charger_desc *desc = cm->desc;
        union power_supply_propval val;
+       struct power_supply *fuel_gauge;
        int ret = 0;
        int uV;
 
@@ -255,11 +280,15 @@ static bool is_full_charged(struct charger_manager *cm)
        if (!is_batt_present(cm))
                return false;
 
-       if (cm->fuel_gauge && desc->fullbatt_full_capacity > 0) {
+       fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
+       if (!fuel_gauge)
+               return false;
+
+       if (desc->fullbatt_full_capacity > 0) {
                val.intval = 0;
 
                /* Not full if capacity of fuel gauge isn't full */
-               ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+               ret = fuel_gauge->get_property(fuel_gauge,
                                POWER_SUPPLY_PROP_CHARGE_FULL, &val);
                if (!ret && val.intval > desc->fullbatt_full_capacity)
                        return true;
@@ -273,10 +302,10 @@ static bool is_full_charged(struct charger_manager *cm)
        }
 
        /* Full, if the capacity is more than fullbatt_soc */
-       if (cm->fuel_gauge && desc->fullbatt_soc > 0) {
+       if (desc->fullbatt_soc > 0) {
                val.intval = 0;
 
-               ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+               ret = fuel_gauge->get_property(fuel_gauge,
                                POWER_SUPPLY_PROP_CAPACITY, &val);
                if (!ret && val.intval >= desc->fullbatt_soc)
                        return true;
@@ -551,6 +580,20 @@ static int check_charging_duration(struct charger_manager *cm)
        return ret;
 }
 
+static int cm_get_battery_temperature_by_psy(struct charger_manager *cm,
+                                       int *temp)
+{
+       struct power_supply *fuel_gauge;
+
+       fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
+       if (!fuel_gauge)
+               return -ENODEV;
+
+       return fuel_gauge->get_property(fuel_gauge,
+                               POWER_SUPPLY_PROP_TEMP,
+                               (union power_supply_propval *)temp);
+}
+
 static int cm_get_battery_temperature(struct charger_manager *cm,
                                        int *temp)
 {
@@ -560,15 +603,18 @@ static int cm_get_battery_temperature(struct charger_manager *cm,
                return -ENODEV;
 
 #ifdef CONFIG_THERMAL
-       ret = thermal_zone_get_temp(cm->tzd_batt, (unsigned long *)temp);
-       if (!ret)
-               /* Calibrate temperature unit */
-               *temp /= 100;
-#else
-       ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
-                               POWER_SUPPLY_PROP_TEMP,
-                               (union power_supply_propval *)temp);
+       if (cm->tzd_batt) {
+               ret = thermal_zone_get_temp(cm->tzd_batt, (unsigned long *)temp);
+               if (!ret)
+                       /* Calibrate temperature unit */
+                       *temp /= 100;
+       } else
 #endif
+       {
+               /* if-else continued from CONFIG_THERMAL */
+               ret = cm_get_battery_temperature_by_psy(cm, temp);
+       }
+
        return ret;
 }
 
@@ -827,6 +873,7 @@ static int charger_get_property(struct power_supply *psy,
        struct charger_manager *cm = container_of(psy,
                        struct charger_manager, charger_psy);
        struct charger_desc *desc = cm->desc;
+       struct power_supply *fuel_gauge;
        int ret = 0;
        int uV;
 
@@ -857,14 +904,20 @@ static int charger_get_property(struct power_supply *psy,
                ret = get_batt_uV(cm, &val->intval);
                break;
        case POWER_SUPPLY_PROP_CURRENT_NOW:
-               ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+               fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
+               if (!fuel_gauge) {
+                       ret = -ENODEV;
+                       break;
+               }
+               ret = fuel_gauge->get_property(fuel_gauge,
                                POWER_SUPPLY_PROP_CURRENT_NOW, val);
                break;
        case POWER_SUPPLY_PROP_TEMP:
        case POWER_SUPPLY_PROP_TEMP_AMBIENT:
                return cm_get_battery_temperature(cm, &val->intval);
        case POWER_SUPPLY_PROP_CAPACITY:
-               if (!cm->fuel_gauge) {
+               fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
+               if (!fuel_gauge) {
                        ret = -ENODEV;
                        break;
                }
@@ -875,7 +928,7 @@ static int charger_get_property(struct power_supply *psy,
                        break;
                }
 
-               ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+               ret = fuel_gauge->get_property(fuel_gauge,
                                        POWER_SUPPLY_PROP_CAPACITY, val);
                if (ret)
                        break;
@@ -924,7 +977,14 @@ static int charger_get_property(struct power_supply *psy,
                break;
        case POWER_SUPPLY_PROP_CHARGE_NOW:
                if (is_charging(cm)) {
-                       ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+                       fuel_gauge = power_supply_get_by_name(
+                                       cm->desc->psy_fuel_gauge);
+                       if (!fuel_gauge) {
+                               ret = -ENODEV;
+                               break;
+                       }
+
+                       ret = fuel_gauge->get_property(fuel_gauge,
                                                POWER_SUPPLY_PROP_CHARGE_NOW,
                                                val);
                        if (ret) {
@@ -970,6 +1030,7 @@ static struct power_supply psy_default = {
        .properties = default_charger_props,
        .num_properties = ARRAY_SIZE(default_charger_props),
        .get_property = charger_get_property,
+       .no_thermal = true,
 };
 
 /**
@@ -1485,14 +1546,15 @@ err:
        return ret;
 }
 
-static int cm_init_thermal_data(struct charger_manager *cm)
+static int cm_init_thermal_data(struct charger_manager *cm,
+               struct power_supply *fuel_gauge)
 {
        struct charger_desc *desc = cm->desc;
        union power_supply_propval val;
        int ret;
 
        /* Verify whether fuel gauge provides battery temperature */
-       ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+       ret = fuel_gauge->get_property(fuel_gauge,
                                        POWER_SUPPLY_PROP_TEMP, &val);
 
        if (!ret) {
@@ -1502,8 +1564,6 @@ static int cm_init_thermal_data(struct charger_manager *cm)
                cm->desc->measure_battery_temp = true;
        }
 #ifdef CONFIG_THERMAL
-       cm->tzd_batt = cm->fuel_gauge->tzd;
-
        if (ret && desc->thermal_zone) {
                cm->tzd_batt =
                        thermal_zone_get_zone_by_name(desc->thermal_zone);
@@ -1666,6 +1726,7 @@ static int charger_manager_probe(struct platform_device *pdev)
        int ret = 0, i = 0;
        int j = 0;
        union power_supply_propval val;
+       struct power_supply *fuel_gauge;
 
        if (g_desc && !rtc_dev && g_desc->rtc_name) {
                rtc_dev = rtc_class_open(g_desc->rtc_name);
@@ -1729,23 +1790,20 @@ static int charger_manager_probe(struct platform_device *pdev)
        while (desc->psy_charger_stat[i])
                i++;
 
-       cm->charger_stat = devm_kzalloc(&pdev->dev,
-                               sizeof(struct power_supply *) * i, GFP_KERNEL);
-       if (!cm->charger_stat)
-               return -ENOMEM;
-
+       /* Check if charger's supplies are present at probe */
        for (i = 0; desc->psy_charger_stat[i]; i++) {
-               cm->charger_stat[i] = power_supply_get_by_name(
-                                       desc->psy_charger_stat[i]);
-               if (!cm->charger_stat[i]) {
+               struct power_supply *psy;
+
+               psy = power_supply_get_by_name(desc->psy_charger_stat[i]);
+               if (!psy) {
                        dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
                                desc->psy_charger_stat[i]);
                        return -ENODEV;
                }
        }
 
-       cm->fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge);
-       if (!cm->fuel_gauge) {
+       fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge);
+       if (!fuel_gauge) {
                dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
                        desc->psy_fuel_gauge);
                return -ENODEV;
@@ -1788,13 +1846,13 @@ static int charger_manager_probe(struct platform_device *pdev)
        cm->charger_psy.num_properties = psy_default.num_properties;
 
        /* Find which optional psy-properties are available */
-       if (!cm->fuel_gauge->get_property(cm->fuel_gauge,
+       if (!fuel_gauge->get_property(fuel_gauge,
                                          POWER_SUPPLY_PROP_CHARGE_NOW, &val)) {
                cm->charger_psy.properties[cm->charger_psy.num_properties] =
                                POWER_SUPPLY_PROP_CHARGE_NOW;
                cm->charger_psy.num_properties++;
        }
-       if (!cm->fuel_gauge->get_property(cm->fuel_gauge,
+       if (!fuel_gauge->get_property(fuel_gauge,
                                          POWER_SUPPLY_PROP_CURRENT_NOW,
                                          &val)) {
                cm->charger_psy.properties[cm->charger_psy.num_properties] =
@@ -1802,7 +1860,7 @@ static int charger_manager_probe(struct platform_device *pdev)
                cm->charger_psy.num_properties++;
        }
 
-       ret = cm_init_thermal_data(cm);
+       ret = cm_init_thermal_data(cm, fuel_gauge);
        if (ret) {
                dev_err(&pdev->dev, "Failed to initialize thermal data\n");
                cm->desc->measure_battery_temp = false;
@@ -2066,8 +2124,8 @@ static bool find_power_supply(struct charger_manager *cm,
        int i;
        bool found = false;
 
-       for (i = 0; cm->charger_stat[i]; i++) {
-               if (psy == cm->charger_stat[i]) {
+       for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
+               if (!strcmp(psy->name, cm->desc->psy_charger_stat[i])) {
                        found = true;
                        break;
                }
index 6cb7fe5..694e8cd 100644 (file)
@@ -417,6 +417,9 @@ static int psy_register_thermal(struct power_supply *psy)
 {
        int i;
 
+       if (psy->no_thermal)
+               return 0;
+
        /* Register battery zone device psy reports temperature */
        for (i = 0; i < psy->num_properties; i++) {
                if (psy->properties[i] == POWER_SUPPLY_PROP_TEMP) {
index 86db310..d2a8c64 100644 (file)
@@ -163,7 +163,7 @@ static int of_get_max1586_platform_data(struct device *dev,
                                 struct max1586_platform_data *pdata)
 {
        struct max1586_subdev_data *sub;
-       struct of_regulator_match rmatch[ARRAY_SIZE(max1586_reg)];
+       struct of_regulator_match rmatch[ARRAY_SIZE(max1586_reg)] = { };
        struct device_node *np = dev->of_node;
        int i, matched;
 
index ef1af2d..f69320e 100644 (file)
@@ -395,7 +395,7 @@ static int max77686_pmic_dt_parse_pdata(struct platform_device *pdev,
        struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
        struct device_node *pmic_np, *regulators_np;
        struct max77686_regulator_data *rdata;
-       struct of_regulator_match rmatch;
+       struct of_regulator_match rmatch = { };
        unsigned int i;
 
        pmic_np = iodev->dev->of_node;
index c67ff05..d158f71 100644 (file)
@@ -227,7 +227,7 @@ static int max77693_pmic_probe(struct platform_device *pdev)
        struct max77693_dev *iodev = dev_get_drvdata(pdev->dev.parent);
        struct max77693_regulator_data *rdata = NULL;
        int num_rdata, i;
-       struct regulator_config config;
+       struct regulator_config config = { };
 
        num_rdata = max77693_pmic_init_rdata(&pdev->dev, &rdata);
        if (!rdata || num_rdata <= 0) {
index d89792b..45fa240 100644 (file)
@@ -454,7 +454,7 @@ static int max77802_pmic_dt_parse_pdata(struct platform_device *pdev,
        struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
        struct device_node *pmic_np, *regulators_np;
        struct max77686_regulator_data *rdata;
-       struct of_regulator_match rmatch;
+       struct of_regulator_match rmatch = { };
        unsigned int i;
 
        pmic_np = iodev->dev->of_node;
index 2fc4111..7eee2ca 100644 (file)
@@ -335,7 +335,7 @@ static int max8660_pdata_from_dt(struct device *dev,
        int matched, i;
        struct device_node *np;
        struct max8660_subdev_data *sub;
-       struct of_regulator_match rmatch[ARRAY_SIZE(max8660_reg)];
+       struct of_regulator_match rmatch[ARRAY_SIZE(max8660_reg)] = { };
 
        np = of_get_child_by_name(dev->of_node, "regulators");
        if (!np) {
index 7a51814..5a1d4af 100644 (file)
@@ -211,7 +211,8 @@ struct regulator_init_data *regulator_of_get_init_data(struct device *dev,
                search = dev->of_node;
 
        if (!search) {
-               dev_err(dev, "Failed to find regulator container node\n");
+               dev_dbg(dev, "Failed to find regulator container node '%s'\n",
+                       desc->regulators_node);
                return NULL;
        }
 
index 4acefa6..7633b9b 100644 (file)
@@ -341,7 +341,7 @@ static int s2mpa01_pmic_probe(struct platform_device *pdev)
 {
        struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
        struct sec_platform_data *pdata = dev_get_platdata(iodev->dev);
-       struct of_regulator_match rdata[S2MPA01_REGULATOR_MAX];
+       struct of_regulator_match rdata[S2MPA01_REGULATOR_MAX] = { };
        struct device_node *reg_np = NULL;
        struct regulator_config config = { };
        struct s2mpa01_info *s2mpa01;
index 6cbe6ef..bda52f1 100644 (file)
@@ -888,7 +888,6 @@ static void virtio_ccw_int_handler(struct ccw_device *cdev,
        struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev);
        int i;
        struct virtqueue *vq;
-       struct virtio_driver *drv;
 
        if (!vcdev)
                return;
index ca75c7c..ef355c1 100644 (file)
@@ -480,9 +480,7 @@ void bnx2fc_rec_compl(struct bnx2fc_els_cb_arg *cb_arg)
                        bnx2fc_initiate_cleanup(orig_io_req);
                        /* Post a new IO req with the same sc_cmd */
                        BNX2FC_IO_DBG(rec_req, "Post IO request again\n");
-                       spin_unlock_bh(&tgt->tgt_lock);
                        rc = bnx2fc_post_io_req(tgt, new_io_req);
-                       spin_lock_bh(&tgt->tgt_lock);
                        if (!rc)
                                goto free_frame;
                        BNX2FC_IO_DBG(rec_req, "REC: io post err\n");
index 79e5c94..72533c5 100644 (file)
@@ -412,6 +412,7 @@ static int bnx2fc_rcv(struct sk_buff *skb, struct net_device *dev,
        struct fc_frame_header *fh;
        struct fcoe_rcv_info *fr;
        struct fcoe_percpu_s *bg;
+       struct sk_buff *tmp_skb;
        unsigned short oxid;
 
        interface = container_of(ptype, struct bnx2fc_interface,
@@ -424,6 +425,12 @@ static int bnx2fc_rcv(struct sk_buff *skb, struct net_device *dev,
                goto err;
        }
 
+       tmp_skb = skb_share_check(skb, GFP_ATOMIC);
+       if (!tmp_skb)
+               goto err;
+
+       skb = tmp_skb;
+
        if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) {
                printk(KERN_ERR PFX "bnx2fc_rcv: Wrong FC type frame\n");
                goto err;
index 0679782..5b99844 100644 (file)
@@ -1894,18 +1894,24 @@ int bnx2fc_queuecommand(struct Scsi_Host *host,
                        goto exit_qcmd;
                }
        }
+
+       spin_lock_bh(&tgt->tgt_lock);
+
        io_req = bnx2fc_cmd_alloc(tgt);
        if (!io_req) {
                rc = SCSI_MLQUEUE_HOST_BUSY;
-               goto exit_qcmd;
+               goto exit_qcmd_tgtlock;
        }
        io_req->sc_cmd = sc_cmd;
 
        if (bnx2fc_post_io_req(tgt, io_req)) {
                printk(KERN_ERR PFX "Unable to post io_req\n");
                rc = SCSI_MLQUEUE_HOST_BUSY;
-               goto exit_qcmd;
+               goto exit_qcmd_tgtlock;
        }
+
+exit_qcmd_tgtlock:
+       spin_unlock_bh(&tgt->tgt_lock);
 exit_qcmd:
        return rc;
 }
@@ -2020,6 +2026,8 @@ int bnx2fc_post_io_req(struct bnx2fc_rport *tgt,
        int task_idx, index;
        u16 xid;
 
+       /* bnx2fc_post_io_req() is called with the tgt_lock held */
+
        /* Initialize rest of io_req fields */
        io_req->cmd_type = BNX2FC_SCSI_CMD;
        io_req->port = port;
@@ -2047,9 +2055,7 @@ int bnx2fc_post_io_req(struct bnx2fc_rport *tgt,
        /* Build buffer descriptor list for firmware from sg list */
        if (bnx2fc_build_bd_list_from_sg(io_req)) {
                printk(KERN_ERR PFX "BD list creation failed\n");
-               spin_lock_bh(&tgt->tgt_lock);
                kref_put(&io_req->refcount, bnx2fc_cmd_release);
-               spin_unlock_bh(&tgt->tgt_lock);
                return -EAGAIN;
        }
 
@@ -2061,19 +2067,15 @@ int bnx2fc_post_io_req(struct bnx2fc_rport *tgt,
        task = &(task_page[index]);
        bnx2fc_init_task(io_req, task);
 
-       spin_lock_bh(&tgt->tgt_lock);
-
        if (tgt->flush_in_prog) {
                printk(KERN_ERR PFX "Flush in progress..Host Busy\n");
                kref_put(&io_req->refcount, bnx2fc_cmd_release);
-               spin_unlock_bh(&tgt->tgt_lock);
                return -EAGAIN;
        }
 
        if (!test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)) {
                printk(KERN_ERR PFX "Session not ready...post_io\n");
                kref_put(&io_req->refcount, bnx2fc_cmd_release);
-               spin_unlock_bh(&tgt->tgt_lock);
                return -EAGAIN;
        }
 
@@ -2091,6 +2093,5 @@ int bnx2fc_post_io_req(struct bnx2fc_rport *tgt,
 
        /* Ring doorbell */
        bnx2fc_ring_doorbell(tgt);
-       spin_unlock_bh(&tgt->tgt_lock);
        return 0;
 }
index 3e0a0d3..1508125 100644 (file)
@@ -828,6 +828,8 @@ static void do_act_open_rpl(struct cxgbi_device *cdev, struct sk_buff *skb)
        if (status == CPL_ERR_RTX_NEG_ADVICE)
                goto rel_skb;
 
+       module_put(THIS_MODULE);
+
        if (status && status != CPL_ERR_TCAM_FULL &&
            status != CPL_ERR_CONN_EXIST &&
            status != CPL_ERR_ARP_MISS)
@@ -936,20 +938,23 @@ static void do_abort_req_rss(struct cxgbi_device *cdev, struct sk_buff *skb)
        cxgbi_sock_get(csk);
        spin_lock_bh(&csk->lock);
 
-       if (!cxgbi_sock_flag(csk, CTPF_ABORT_REQ_RCVD)) {
-               cxgbi_sock_set_flag(csk, CTPF_ABORT_REQ_RCVD);
-               cxgbi_sock_set_state(csk, CTP_ABORTING);
-               goto done;
+       cxgbi_sock_clear_flag(csk, CTPF_ABORT_REQ_RCVD);
+
+       if (!cxgbi_sock_flag(csk, CTPF_TX_DATA_SENT)) {
+               send_tx_flowc_wr(csk);
+               cxgbi_sock_set_flag(csk, CTPF_TX_DATA_SENT);
        }
 
-       cxgbi_sock_clear_flag(csk, CTPF_ABORT_REQ_RCVD);
+       cxgbi_sock_set_flag(csk, CTPF_ABORT_REQ_RCVD);
+       cxgbi_sock_set_state(csk, CTP_ABORTING);
+
        send_abort_rpl(csk, rst_status);
 
        if (!cxgbi_sock_flag(csk, CTPF_ABORT_RPL_PENDING)) {
                csk->err = abort_status_to_errno(csk, req->status, &rst_status);
                cxgbi_sock_closed(csk);
        }
-done:
+
        spin_unlock_bh(&csk->lock);
        cxgbi_sock_put(csk);
 rel_skb:
index 674d498..7da59c3 100644 (file)
@@ -816,7 +816,7 @@ static void cxgbi_inform_iscsi_conn_closing(struct cxgbi_sock *csk)
                read_lock_bh(&csk->callback_lock);
                if (csk->user_data)
                        iscsi_conn_failure(csk->user_data,
-                                       ISCSI_ERR_CONN_FAILED);
+                                       ISCSI_ERR_TCP_CONN_CLOSE);
                read_unlock_bh(&csk->callback_lock);
        }
 }
@@ -905,18 +905,16 @@ void cxgbi_sock_rcv_abort_rpl(struct cxgbi_sock *csk)
 {
        cxgbi_sock_get(csk);
        spin_lock_bh(&csk->lock);
+
+       cxgbi_sock_set_flag(csk, CTPF_ABORT_RPL_RCVD);
        if (cxgbi_sock_flag(csk, CTPF_ABORT_RPL_PENDING)) {
-               if (!cxgbi_sock_flag(csk, CTPF_ABORT_RPL_RCVD))
-                       cxgbi_sock_set_flag(csk, CTPF_ABORT_RPL_RCVD);
-               else {
-                       cxgbi_sock_clear_flag(csk, CTPF_ABORT_RPL_RCVD);
-                       cxgbi_sock_clear_flag(csk, CTPF_ABORT_RPL_PENDING);
-                       if (cxgbi_sock_flag(csk, CTPF_ABORT_REQ_RCVD))
-                               pr_err("csk 0x%p,%u,0x%lx,%u,ABT_RPL_RSS.\n",
-                                       csk, csk->state, csk->flags, csk->tid);
-                       cxgbi_sock_closed(csk);
-               }
+               cxgbi_sock_clear_flag(csk, CTPF_ABORT_RPL_PENDING);
+               if (cxgbi_sock_flag(csk, CTPF_ABORT_REQ_RCVD))
+                       pr_err("csk 0x%p,%u,0x%lx,%u,ABT_RPL_RSS.\n",
+                              csk, csk->state, csk->flags, csk->tid);
+               cxgbi_sock_closed(csk);
        }
+
        spin_unlock_bh(&csk->lock);
        cxgbi_sock_put(csk);
 }
index e99507e..fd78bdc 100644 (file)
@@ -474,6 +474,13 @@ static int alua_check_sense(struct scsi_device *sdev,
                         * LUN Not Ready -- Offline
                         */
                        return SUCCESS;
+               if (sdev->allow_restart &&
+                   sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x02)
+                       /*
+                        * if the device is not started, we need to wake
+                        * the error handler to start the motor
+                        */
+                       return FAILED;
                break;
        case UNIT_ATTENTION:
                if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00)
index f6a69a3..5640ad1 100644 (file)
@@ -4453,7 +4453,7 @@ static int megasas_init_fw(struct megasas_instance *instance)
                        instance->msixentry[i].entry = i;
                i = pci_enable_msix_range(instance->pdev, instance->msixentry,
                                          1, instance->msix_vectors);
-               if (i)
+               if (i > 0)
                        instance->msix_vectors = i;
                else
                        instance->msix_vectors = 0;
index 49014a1..c1d04d4 100644 (file)
@@ -202,6 +202,7 @@ static struct {
        {"IOMEGA", "Io20S         *F", NULL, BLIST_KEY},
        {"INSITE", "Floptical   F*8I", NULL, BLIST_KEY},
        {"INSITE", "I325VM", NULL, BLIST_KEY},
+       {"Intel", "Multi-Flex", NULL, BLIST_NO_RSOC},
        {"iRiver", "iFP Mass Driver", NULL, BLIST_NOT_LOCKABLE | BLIST_INQUIRY_36},
        {"LASOUND", "CDX7405", "3.10", BLIST_MAX5LUN | BLIST_SINGLELUN},
        {"MATSHITA", "PD-1", NULL, BLIST_FORCELUN | BLIST_SINGLELUN},
index 9a6f846..bc5ff6f 100644 (file)
@@ -459,14 +459,6 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
        if (! scsi_command_normalize_sense(scmd, &sshdr))
                return FAILED;  /* no valid sense data */
 
-       if (scmd->cmnd[0] == TEST_UNIT_READY && scmd->scsi_done != scsi_eh_done)
-               /*
-                * nasty: for mid-layer issued TURs, we need to return the
-                * actual sense data without any recovery attempt.  For eh
-                * issued ones, we need to try to recover and interpret
-                */
-               return SUCCESS;
-
        scsi_report_sense(sdev, &sshdr);
 
        if (scsi_sense_is_deferred(&sshdr))
@@ -482,6 +474,14 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
                /* handler does not care. Drop down to default handling */
        }
 
+       if (scmd->cmnd[0] == TEST_UNIT_READY && scmd->scsi_done != scsi_eh_done)
+               /*
+                * nasty: for mid-layer issued TURs, we need to return the
+                * actual sense data without any recovery attempt.  For eh
+                * issued ones, we need to try to recover and interpret
+                */
+               return SUCCESS;
+
        /*
         * Previous logic looked for FILEMARK, EOM or ILI which are
         * mainly associated with tapes and returned SUCCESS.
@@ -2001,8 +2001,10 @@ static void scsi_restart_operations(struct Scsi_Host *shost)
         * is no point trying to lock the door of an off-line device.
         */
        shost_for_each_device(sdev, shost) {
-               if (scsi_device_online(sdev) && sdev->locked)
+               if (scsi_device_online(sdev) && sdev->was_reset && sdev->locked) {
                        scsi_eh_lock_door(sdev);
+                       sdev->was_reset = 0;
+               }
        }
 
        /*
index 8adf067..1c3467b 100644 (file)
@@ -102,7 +102,6 @@ static int ufshcd_parse_clock_info(struct ufs_hba *hba)
        clkfreq = devm_kzalloc(dev, sz * sizeof(*clkfreq),
                        GFP_KERNEL);
        if (!clkfreq) {
-               dev_err(dev, "%s: no memory\n", "freq-table-hz");
                ret = -ENOMEM;
                goto out;
        }
@@ -112,19 +111,19 @@ static int ufshcd_parse_clock_info(struct ufs_hba *hba)
        if (ret && (ret != -EINVAL)) {
                dev_err(dev, "%s: error reading array %d\n",
                                "freq-table-hz", ret);
-               goto free_clkfreq;
+               return ret;
        }
 
        for (i = 0; i < sz; i += 2) {
                ret = of_property_read_string_index(np,
                                "clock-names", i/2, (const char **)&name);
                if (ret)
-                       goto free_clkfreq;
+                       goto out;
 
                clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL);
                if (!clki) {
                        ret = -ENOMEM;
-                       goto free_clkfreq;
+                       goto out;
                }
 
                clki->min_freq = clkfreq[i];
@@ -134,8 +133,6 @@ static int ufshcd_parse_clock_info(struct ufs_hba *hba)
                                clki->min_freq, clki->max_freq, clki->name);
                list_add_tail(&clki->list, &hba->clk_list_head);
        }
-free_clkfreq:
-       kfree(clkfreq);
 out:
        return ret;
 }
@@ -162,10 +159,8 @@ static int ufshcd_populate_vreg(struct device *dev, const char *name,
        }
 
        vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
-       if (!vreg) {
-               dev_err(dev, "No memory for %s regulator\n", name);
-               goto out;
-       }
+       if (!vreg)
+               return -ENOMEM;
 
        vreg->name = kstrdup(name, GFP_KERNEL);
 
index 497c38a..605ca60 100644 (file)
@@ -744,6 +744,8 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba)
        if (!ufshcd_is_clkgating_allowed(hba))
                return;
        device_remove_file(hba->dev, &hba->clk_gating.delay_attr);
+       cancel_work_sync(&hba->clk_gating.ungate_work);
+       cancel_delayed_work_sync(&hba->clk_gating.gate_work);
 }
 
 /* Must be called with host lock acquired */
@@ -2246,6 +2248,22 @@ static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba)
        return ret;
 }
 
+ /**
+ * ufshcd_init_pwr_info - setting the POR (power on reset)
+ * values in hba power info
+ * @hba: per-adapter instance
+ */
+static void ufshcd_init_pwr_info(struct ufs_hba *hba)
+{
+       hba->pwr_info.gear_rx = UFS_PWM_G1;
+       hba->pwr_info.gear_tx = UFS_PWM_G1;
+       hba->pwr_info.lane_rx = 1;
+       hba->pwr_info.lane_tx = 1;
+       hba->pwr_info.pwr_rx = SLOWAUTO_MODE;
+       hba->pwr_info.pwr_tx = SLOWAUTO_MODE;
+       hba->pwr_info.hs_rate = 0;
+}
+
 /**
  * ufshcd_get_max_pwr_mode - reads the max power mode negotiated with device
  * @hba: per-adapter instance
@@ -2844,8 +2862,13 @@ static void ufshcd_slave_destroy(struct scsi_device *sdev)
        hba = shost_priv(sdev->host);
        scsi_deactivate_tcq(sdev, hba->nutrs);
        /* Drop the reference as it won't be needed anymore */
-       if (ufshcd_scsi_to_upiu_lun(sdev->lun) == UFS_UPIU_UFS_DEVICE_WLUN)
+       if (ufshcd_scsi_to_upiu_lun(sdev->lun) == UFS_UPIU_UFS_DEVICE_WLUN) {
+               unsigned long flags;
+
+               spin_lock_irqsave(hba->host->host_lock, flags);
                hba->sdev_ufs_device = NULL;
+               spin_unlock_irqrestore(hba->host->host_lock, flags);
+       }
 }
 
 /**
@@ -4062,6 +4085,8 @@ static void ufshcd_init_icc_levels(struct ufs_hba *hba)
 static int ufshcd_scsi_add_wlus(struct ufs_hba *hba)
 {
        int ret = 0;
+       struct scsi_device *sdev_rpmb;
+       struct scsi_device *sdev_boot;
 
        hba->sdev_ufs_device = __scsi_add_device(hba->host, 0, 0,
                ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_UFS_DEVICE_WLUN), NULL);
@@ -4070,56 +4095,33 @@ static int ufshcd_scsi_add_wlus(struct ufs_hba *hba)
                hba->sdev_ufs_device = NULL;
                goto out;
        }
+       scsi_device_put(hba->sdev_ufs_device);
 
-       hba->sdev_boot = __scsi_add_device(hba->host, 0, 0,
+       sdev_boot = __scsi_add_device(hba->host, 0, 0,
                ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_BOOT_WLUN), NULL);
-       if (IS_ERR(hba->sdev_boot)) {
-               ret = PTR_ERR(hba->sdev_boot);
-               hba->sdev_boot = NULL;
+       if (IS_ERR(sdev_boot)) {
+               ret = PTR_ERR(sdev_boot);
                goto remove_sdev_ufs_device;
        }
+       scsi_device_put(sdev_boot);
 
-       hba->sdev_rpmb = __scsi_add_device(hba->host, 0, 0,
+       sdev_rpmb = __scsi_add_device(hba->host, 0, 0,
                ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_RPMB_WLUN), NULL);
-       if (IS_ERR(hba->sdev_rpmb)) {
-               ret = PTR_ERR(hba->sdev_rpmb);
-               hba->sdev_rpmb = NULL;
+       if (IS_ERR(sdev_rpmb)) {
+               ret = PTR_ERR(sdev_rpmb);
                goto remove_sdev_boot;
        }
+       scsi_device_put(sdev_rpmb);
        goto out;
 
 remove_sdev_boot:
-       scsi_remove_device(hba->sdev_boot);
+       scsi_remove_device(sdev_boot);
 remove_sdev_ufs_device:
        scsi_remove_device(hba->sdev_ufs_device);
 out:
        return ret;
 }
 
-/**
- * ufshcd_scsi_remove_wlus - Removes the W-LUs which were added by
- *                          ufshcd_scsi_add_wlus()
- * @hba: per-adapter instance
- *
- */
-static void ufshcd_scsi_remove_wlus(struct ufs_hba *hba)
-{
-       if (hba->sdev_ufs_device) {
-               scsi_remove_device(hba->sdev_ufs_device);
-               hba->sdev_ufs_device = NULL;
-       }
-
-       if (hba->sdev_boot) {
-               scsi_remove_device(hba->sdev_boot);
-               hba->sdev_boot = NULL;
-       }
-
-       if (hba->sdev_rpmb) {
-               scsi_remove_device(hba->sdev_rpmb);
-               hba->sdev_rpmb = NULL;
-       }
-}
-
 /**
  * ufshcd_probe_hba - probe hba to detect device and initialize
  * @hba: per-adapter instance
@@ -4134,6 +4136,8 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
        if (ret)
                goto out;
 
+       ufshcd_init_pwr_info(hba);
+
        /* UniPro link is active now */
        ufshcd_set_link_active(hba);
 
@@ -4264,12 +4268,18 @@ static int ufshcd_config_vreg_load(struct device *dev, struct ufs_vreg *vreg,
 static inline int ufshcd_config_vreg_lpm(struct ufs_hba *hba,
                                         struct ufs_vreg *vreg)
 {
+       if (!vreg)
+               return 0;
+
        return ufshcd_config_vreg_load(hba->dev, vreg, UFS_VREG_LPM_LOAD_UA);
 }
 
 static inline int ufshcd_config_vreg_hpm(struct ufs_hba *hba,
                                         struct ufs_vreg *vreg)
 {
+       if (!vreg)
+               return 0;
+
        return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA);
 }
 
@@ -4471,7 +4481,7 @@ out:
                        if (!IS_ERR_OR_NULL(clki->clk) && clki->enabled)
                                clk_disable_unprepare(clki->clk);
                }
-       } else if (!ret && on) {
+       } else if (on) {
                spin_lock_irqsave(hba->host->host_lock, flags);
                hba->clk_gating.state = CLKS_ON;
                spin_unlock_irqrestore(hba->host->host_lock, flags);
@@ -4675,11 +4685,25 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba,
 {
        unsigned char cmd[6] = { START_STOP };
        struct scsi_sense_hdr sshdr;
-       struct scsi_device *sdp = hba->sdev_ufs_device;
+       struct scsi_device *sdp;
+       unsigned long flags;
        int ret;
 
-       if (!sdp || !scsi_device_online(sdp))
-               return -ENODEV;
+       spin_lock_irqsave(hba->host->host_lock, flags);
+       sdp = hba->sdev_ufs_device;
+       if (sdp) {
+               ret = scsi_device_get(sdp);
+               if (!ret && !scsi_device_online(sdp)) {
+                       ret = -ENODEV;
+                       scsi_device_put(sdp);
+               }
+       } else {
+               ret = -ENODEV;
+       }
+       spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+       if (ret)
+               return ret;
 
        /*
         * If scsi commands fail, the scsi mid-layer schedules scsi error-
@@ -4718,6 +4742,7 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba,
        if (!ret)
                hba->curr_dev_pwr_mode = pwr_mode;
 out:
+       scsi_device_put(sdp);
        hba->host->eh_noresume = 0;
        return ret;
 }
@@ -5087,7 +5112,7 @@ int ufshcd_system_suspend(struct ufs_hba *hba)
        int ret = 0;
 
        if (!hba || !hba->is_powered)
-               goto out;
+               return 0;
 
        if (pm_runtime_suspended(hba->dev)) {
                if (hba->rpm_lvl == hba->spm_lvl)
@@ -5231,7 +5256,6 @@ EXPORT_SYMBOL(ufshcd_shutdown);
 void ufshcd_remove(struct ufs_hba *hba)
 {
        scsi_remove_host(hba->host);
-       ufshcd_scsi_remove_wlus(hba);
        /* disable interrupts */
        ufshcd_disable_intr(hba, hba->intr_mask);
        ufshcd_hba_stop(hba);
index 58ecdff..4a574aa 100644 (file)
@@ -392,8 +392,6 @@ struct ufs_hba {
         * "UFS device" W-LU.
         */
        struct scsi_device *sdev_ufs_device;
-       struct scsi_device *sdev_rpmb;
-       struct scsi_device *sdev_boot;
 
        enum ufs_dev_pwr_mode curr_dev_pwr_mode;
        enum uic_link_state uic_link_state;
index cea8ea3..1a07bf5 100644 (file)
@@ -26,6 +26,7 @@ static const struct of_device_id realview_soc_of_match[] = {
        { .compatible = "arm,realview-pb11mp-soc", },
        { .compatible = "arm,realview-pba8-soc", },
        { .compatible = "arm,realview-pbx-soc", },
+       { }
 };
 
 static u32 realview_coreid;
index 72e12ba..d0d5542 100644 (file)
@@ -376,9 +376,6 @@ static void pump_transfers(unsigned long data)
        chip = dws->cur_chip;
        spi = message->spi;
 
-       if (unlikely(!chip->clk_div))
-               chip->clk_div = dws->max_freq / chip->speed_hz;
-
        if (message->state == ERROR_STATE) {
                message->status = -EIO;
                goto early_exit;
@@ -419,7 +416,7 @@ static void pump_transfers(unsigned long data)
        if (transfer->speed_hz) {
                speed = chip->speed_hz;
 
-               if (transfer->speed_hz != speed) {
+               if ((transfer->speed_hz != speed) || (!chip->clk_div)) {
                        speed = transfer->speed_hz;
 
                        /* clk_div doesn't support odd number */
@@ -581,7 +578,6 @@ static int dw_spi_setup(struct spi_device *spi)
                dev_err(&spi->dev, "No max speed HZ parameter\n");
                return -EINVAL;
        }
-       chip->speed_hz = spi->max_speed_hz;
 
        chip->tmode = 0; /* Tx & Rx */
        /* Default SPI mode is SCPOL = 0, SCPH = 0 */
index 4482160..831ceb4 100644 (file)
@@ -46,7 +46,7 @@
 
 #define SPI_TCR                        0x08
 
-#define SPI_CTAR(x)            (0x0c + (x * 4))
+#define SPI_CTAR(x)            (0x0c + (((x) & 0x3) * 4))
 #define SPI_CTAR_FMSZ(x)       (((x) & 0x0000000f) << 27)
 #define SPI_CTAR_CPOL(x)       ((x) << 26)
 #define SPI_CTAR_CPHA(x)       ((x) << 25)
@@ -70,7 +70,7 @@
 
 #define SPI_PUSHR              0x34
 #define SPI_PUSHR_CONT         (1 << 31)
-#define SPI_PUSHR_CTAS(x)      (((x) & 0x00000007) << 28)
+#define SPI_PUSHR_CTAS(x)      (((x) & 0x00000003) << 28)
 #define SPI_PUSHR_EOQ          (1 << 27)
 #define SPI_PUSHR_CTCNT        (1 << 26)
 #define SPI_PUSHR_PCS(x)       (((1 << x) & 0x0000003f) << 16)
index d8a105f..9e9e0f9 100644 (file)
@@ -1274,7 +1274,9 @@ static int pxa2xx_spi_suspend(struct device *dev)
        if (status != 0)
                return status;
        write_SSCR0(0, drv_data->ioaddr);
-       clk_disable_unprepare(ssp->clk);
+
+       if (!pm_runtime_suspended(dev))
+               clk_disable_unprepare(ssp->clk);
 
        return 0;
 }
@@ -1288,7 +1290,8 @@ static int pxa2xx_spi_resume(struct device *dev)
        pxa2xx_spi_dma_resume(drv_data);
 
        /* Enable the SSP clock */
-       clk_prepare_enable(ssp->clk);
+       if (!pm_runtime_suspended(dev))
+               clk_prepare_enable(ssp->clk);
 
        /* Restore LPSS private register bits */
        lpss_ssp_setup(drv_data);
index 39e2c0a..f63de78 100644 (file)
@@ -562,9 +562,9 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
 
        sspi->word_width = DIV_ROUND_UP(bits_per_word, 8);
        txfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
-                                          sspi->word_width;
+                                          (sspi->word_width >> 1);
        rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
-                                          sspi->word_width;
+                                          (sspi->word_width >> 1);
 
        if (!(spi->mode & SPI_CS_HIGH))
                regval |= SIRFSOC_SPI_CS_IDLE_STAT;
index ebcb33d..50f20f2 100644 (file)
@@ -615,13 +615,13 @@ static int spi_map_buf(struct spi_master *master, struct device *dev,
                                sg_free_table(sgt);
                                return -ENOMEM;
                        }
-                       sg_buf = page_address(vm_page) +
-                               ((size_t)buf & ~PAGE_MASK);
+                       sg_set_page(&sgt->sgl[i], vm_page,
+                                   min, offset_in_page(buf));
                } else {
                        sg_buf = buf;
+                       sg_set_buf(&sgt->sgl[i], sg_buf, min);
                }
 
-               sg_set_buf(&sgt->sgl[i], sg_buf, min);
 
                buf += min;
                len -= min;
index 0731820..e8c98cf 100644 (file)
@@ -119,7 +119,6 @@ struct ade7758_state {
        u8                      *tx;
        u8                      *rx;
        struct mutex            buf_lock;
-       const struct iio_chan_spec *ade7758_ring_channels;
        struct spi_transfer     ring_xfer[4];
        struct spi_message      ring_msg;
        /*
index abc6006..fb373b8 100644 (file)
@@ -634,9 +634,6 @@ static const struct iio_chan_spec ade7758_channels[] = {
                .type = IIO_VOLTAGE,
                .indexed = 1,
                .channel = 0,
-               .extend_name = "raw",
-               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
-               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
                .address = AD7758_WT(AD7758_PHASE_A, AD7758_VOLTAGE),
                .scan_index = 0,
                .scan_type = {
@@ -648,9 +645,6 @@ static const struct iio_chan_spec ade7758_channels[] = {
                .type = IIO_CURRENT,
                .indexed = 1,
                .channel = 0,
-               .extend_name = "raw",
-               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
-               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
                .address = AD7758_WT(AD7758_PHASE_A, AD7758_CURRENT),
                .scan_index = 1,
                .scan_type = {
@@ -662,9 +656,7 @@ static const struct iio_chan_spec ade7758_channels[] = {
                .type = IIO_POWER,
                .indexed = 1,
                .channel = 0,
-               .extend_name = "apparent_raw",
-               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
-               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+               .extend_name = "apparent",
                .address = AD7758_WT(AD7758_PHASE_A, AD7758_APP_PWR),
                .scan_index = 2,
                .scan_type = {
@@ -676,9 +668,7 @@ static const struct iio_chan_spec ade7758_channels[] = {
                .type = IIO_POWER,
                .indexed = 1,
                .channel = 0,
-               .extend_name = "active_raw",
-               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
-               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+               .extend_name = "active",
                .address = AD7758_WT(AD7758_PHASE_A, AD7758_ACT_PWR),
                .scan_index = 3,
                .scan_type = {
@@ -690,9 +680,7 @@ static const struct iio_chan_spec ade7758_channels[] = {
                .type = IIO_POWER,
                .indexed = 1,
                .channel = 0,
-               .extend_name = "reactive_raw",
-               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
-               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+               .extend_name = "reactive",
                .address = AD7758_WT(AD7758_PHASE_A, AD7758_REACT_PWR),
                .scan_index = 4,
                .scan_type = {
@@ -704,9 +692,6 @@ static const struct iio_chan_spec ade7758_channels[] = {
                .type = IIO_VOLTAGE,
                .indexed = 1,
                .channel = 1,
-               .extend_name = "raw",
-               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
-               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
                .address = AD7758_WT(AD7758_PHASE_B, AD7758_VOLTAGE),
                .scan_index = 5,
                .scan_type = {
@@ -718,9 +703,6 @@ static const struct iio_chan_spec ade7758_channels[] = {
                .type = IIO_CURRENT,
                .indexed = 1,
                .channel = 1,
-               .extend_name = "raw",
-               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
-               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
                .address = AD7758_WT(AD7758_PHASE_B, AD7758_CURRENT),
                .scan_index = 6,
                .scan_type = {
@@ -732,9 +714,7 @@ static const struct iio_chan_spec ade7758_channels[] = {
                .type = IIO_POWER,
                .indexed = 1,
                .channel = 1,
-               .extend_name = "apparent_raw",
-               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
-               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+               .extend_name = "apparent",
                .address = AD7758_WT(AD7758_PHASE_B, AD7758_APP_PWR),
                .scan_index = 7,
                .scan_type = {
@@ -746,9 +726,7 @@ static const struct iio_chan_spec ade7758_channels[] = {
                .type = IIO_POWER,
                .indexed = 1,
                .channel = 1,
-               .extend_name = "active_raw",
-               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
-               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+               .extend_name = "active",
                .address = AD7758_WT(AD7758_PHASE_B, AD7758_ACT_PWR),
                .scan_index = 8,
                .scan_type = {
@@ -760,9 +738,7 @@ static const struct iio_chan_spec ade7758_channels[] = {
                .type = IIO_POWER,
                .indexed = 1,
                .channel = 1,
-               .extend_name = "reactive_raw",
-               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
-               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+               .extend_name = "reactive",
                .address = AD7758_WT(AD7758_PHASE_B, AD7758_REACT_PWR),
                .scan_index = 9,
                .scan_type = {
@@ -774,9 +750,6 @@ static const struct iio_chan_spec ade7758_channels[] = {
                .type = IIO_VOLTAGE,
                .indexed = 1,
                .channel = 2,
-               .extend_name = "raw",
-               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
-               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
                .address = AD7758_WT(AD7758_PHASE_C, AD7758_VOLTAGE),
                .scan_index = 10,
                .scan_type = {
@@ -788,9 +761,6 @@ static const struct iio_chan_spec ade7758_channels[] = {
                .type = IIO_CURRENT,
                .indexed = 1,
                .channel = 2,
-               .extend_name = "raw",
-               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
-               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
                .address = AD7758_WT(AD7758_PHASE_C, AD7758_CURRENT),
                .scan_index = 11,
                .scan_type = {
@@ -802,9 +772,7 @@ static const struct iio_chan_spec ade7758_channels[] = {
                .type = IIO_POWER,
                .indexed = 1,
                .channel = 2,
-               .extend_name = "apparent_raw",
-               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
-               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+               .extend_name = "apparent",
                .address = AD7758_WT(AD7758_PHASE_C, AD7758_APP_PWR),
                .scan_index = 12,
                .scan_type = {
@@ -816,9 +784,7 @@ static const struct iio_chan_spec ade7758_channels[] = {
                .type = IIO_POWER,
                .indexed = 1,
                .channel = 2,
-               .extend_name = "active_raw",
-               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
-               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+               .extend_name = "active",
                .address = AD7758_WT(AD7758_PHASE_C, AD7758_ACT_PWR),
                .scan_index = 13,
                .scan_type = {
@@ -830,9 +796,7 @@ static const struct iio_chan_spec ade7758_channels[] = {
                .type = IIO_POWER,
                .indexed = 1,
                .channel = 2,
-               .extend_name = "reactive_raw",
-               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
-               .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+               .extend_name = "reactive",
                .address = AD7758_WT(AD7758_PHASE_C, AD7758_REACT_PWR),
                .scan_index = 14,
                .scan_type = {
@@ -873,13 +837,14 @@ static int ade7758_probe(struct spi_device *spi)
                goto error_free_rx;
        }
        st->us = spi;
-       st->ade7758_ring_channels = &ade7758_channels[0];
        mutex_init(&st->buf_lock);
 
        indio_dev->name = spi->dev.driver->name;
        indio_dev->dev.parent = &spi->dev;
        indio_dev->info = &ade7758_info;
        indio_dev->modes = INDIO_DIRECT_MODE;
+       indio_dev->channels = ade7758_channels;
+       indio_dev->num_channels = ARRAY_SIZE(ade7758_channels);
 
        ret = ade7758_configure_ring(indio_dev);
        if (ret)
index c0accf8..6e90064 100644 (file)
@@ -85,17 +85,16 @@ static irqreturn_t ade7758_trigger_handler(int irq, void *p)
  **/
 static int ade7758_ring_preenable(struct iio_dev *indio_dev)
 {
-       struct ade7758_state *st = iio_priv(indio_dev);
        unsigned channel;
 
-       if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength))
+       if (bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength))
                return -EINVAL;
 
        channel = find_first_bit(indio_dev->active_scan_mask,
                                 indio_dev->masklength);
 
        ade7758_write_waveform_type(&indio_dev->dev,
-               st->ade7758_ring_channels[channel].address);
+               indio_dev->channels[channel].address);
 
        return 0;
 }
index 9935e66..eddef9c 100644 (file)
@@ -275,11 +275,11 @@ u8 rtw_sitesurvey_cmd(struct adapter  *padapter, struct ndis_802_11_ssid *ssid,
        if (check_fwstate(pmlmepriv, _FW_LINKED) == true)
                rtw_lps_ctrl_wk_cmd(padapter, LPS_CTRL_SCAN, 1);
 
-       ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+       ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
        if (ph2c == NULL)
                return _FAIL;
 
-       psurveyPara = kzalloc(sizeof(struct sitesurvey_parm), GFP_KERNEL);
+       psurveyPara = kzalloc(sizeof(struct sitesurvey_parm), GFP_ATOMIC);
        if (psurveyPara == NULL) {
                kfree(ph2c);
                return _FAIL;
@@ -405,7 +405,7 @@ u8 rtw_joinbss_cmd(struct adapter  *padapter, struct wlan_network *pnetwork)
        else
                RT_TRACE(_module_rtl871x_cmd_c_, _drv_notice_, ("+Join cmd: SSid =[%s]\n", pmlmepriv->assoc_ssid.Ssid));
 
-       pcmd = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+       pcmd = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
        if (pcmd == NULL) {
                res = _FAIL;
                RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_, ("rtw_joinbss_cmd: memory allocate for cmd_obj fail!!!\n"));
@@ -755,13 +755,13 @@ u8 rtw_dynamic_chk_wk_cmd(struct adapter *padapter)
        u8      res = _SUCCESS;
 
 
-       ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+       ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
        if (ph2c == NULL) {
                res = _FAIL;
                goto exit;
        }
 
-       pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_KERNEL);
+       pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_ATOMIC);
        if (pdrvextra_cmd_parm == NULL) {
                kfree(ph2c);
                res = _FAIL;
@@ -967,13 +967,13 @@ u8 rtw_lps_ctrl_wk_cmd(struct adapter *padapter, u8 lps_ctrl_type, u8 enqueue)
        u8      res = _SUCCESS;
 
        if (enqueue) {
-               ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+               ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
                if (ph2c == NULL) {
                        res = _FAIL;
                        goto exit;
                }
 
-               pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_KERNEL);
+               pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_ATOMIC);
                if (pdrvextra_cmd_parm == NULL) {
                        kfree(ph2c);
                        res = _FAIL;
@@ -1010,13 +1010,13 @@ u8 rtw_rpt_timer_cfg_cmd(struct adapter *padapter, u16 min_time)
 
        u8      res = _SUCCESS;
 
-       ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+       ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
        if (ph2c == NULL) {
                res = _FAIL;
                goto exit;
        }
 
-       pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_KERNEL);
+       pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_ATOMIC);
        if (pdrvextra_cmd_parm == NULL) {
                kfree(ph2c);
                res = _FAIL;
@@ -1088,13 +1088,13 @@ u8 rtw_ps_cmd(struct adapter *padapter)
 
        u8      res = _SUCCESS;
 
-       ppscmd = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+       ppscmd = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
        if (ppscmd == NULL) {
                res = _FAIL;
                goto exit;
        }
 
-       pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_KERNEL);
+       pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_ATOMIC);
        if (pdrvextra_cmd_parm == NULL) {
                kfree(ppscmd);
                res = _FAIL;
index 5ba5099..70b1bc3 100644 (file)
@@ -4241,12 +4241,12 @@ void report_survey_event(struct adapter *padapter,
        pcmdpriv = &padapter->cmdpriv;
 
 
-       pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+       pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
        if (pcmd_obj == NULL)
                return;
 
        cmdsz = (sizeof(struct survey_event) + sizeof(struct C2HEvent_Header));
-       pevtcmd = kzalloc(cmdsz, GFP_KERNEL);
+       pevtcmd = kzalloc(cmdsz, GFP_ATOMIC);
        if (pevtcmd == NULL) {
                kfree(pcmd_obj);
                return;
@@ -4339,12 +4339,12 @@ void report_join_res(struct adapter *padapter, int res)
        struct mlme_ext_info    *pmlmeinfo = &(pmlmeext->mlmext_info);
        struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
 
-       pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+       pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
        if (pcmd_obj == NULL)
                return;
 
        cmdsz = (sizeof(struct joinbss_event) + sizeof(struct C2HEvent_Header));
-       pevtcmd = kzalloc(cmdsz, GFP_KERNEL);
+       pevtcmd = kzalloc(cmdsz, GFP_ATOMIC);
        if (pevtcmd == NULL) {
                kfree(pcmd_obj);
                return;
@@ -4854,11 +4854,11 @@ void survey_timer_hdl(void *function_context)
                        pmlmeext->scan_abort = false;/* reset */
                }
 
-               ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+               ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
                if (ph2c == NULL)
                        goto exit_survey_timer_hdl;
 
-               psurveyPara = kzalloc(sizeof(struct sitesurvey_parm), GFP_KERNEL);
+               psurveyPara = kzalloc(sizeof(struct sitesurvey_parm), GFP_ATOMIC);
                if (psurveyPara == NULL) {
                        kfree(ph2c);
                        goto exit_survey_timer_hdl;
index 33ccbbb..d300369 100644 (file)
@@ -935,7 +935,7 @@ int rtw_check_bcn_info(struct adapter  *Adapter, u8 *pframe, u32 packet_len)
                return true;
        }
 
-       bssid = kzalloc(sizeof(struct wlan_bssid_ex), GFP_KERNEL);
+       bssid = kzalloc(sizeof(struct wlan_bssid_ex), GFP_ATOMIC);
 
        subtype = GetFrameSubType(pframe) >> 4;
 
index 407a318..2f87150 100644 (file)
@@ -47,6 +47,7 @@ static struct usb_device_id rtw_usb_id_tbl[] = {
        {USB_DEVICE(0x07b8, 0x8179)}, /* Abocom - Abocom */
        {USB_DEVICE(0x2001, 0x330F)}, /* DLink DWA-125 REV D1 */
        {USB_DEVICE(0x2001, 0x3310)}, /* Dlink DWA-123 REV D1 */
+       {USB_DEVICE(0x2001, 0x3311)}, /* DLink GO-USB-N150 REV B1 */
        {USB_DEVICE(0x0df6, 0x0076)}, /* Sitecom N150 v2 */
        {}      /* Terminating entry */
 };
index b19e432..73e58d2 100644 (file)
@@ -3491,7 +3491,7 @@ iscsit_build_sendtargets_response(struct iscsi_cmd *cmd,
                                len = sprintf(buf, "TargetAddress="
                                        "%s:%hu,%hu",
                                        inaddr_any ? conn->local_ip : np->np_ip,
-                                       inaddr_any ? conn->local_port : np->np_port,
+                                       np->np_port,
                                        tpg->tpgt);
                                len += 1;
 
index 8c60a1a..9f93b82 100644 (file)
@@ -2738,7 +2738,8 @@ core_scsi3_pro_preempt(struct se_cmd *cmd, int type, int scope, u64 res_key,
        struct t10_pr_registration *pr_reg, *pr_reg_tmp, *pr_reg_n, *pr_res_holder;
        struct t10_reservation *pr_tmpl = &dev->t10_pr;
        u32 pr_res_mapped_lun = 0;
-       int all_reg = 0, calling_it_nexus = 0, released_regs = 0;
+       int all_reg = 0, calling_it_nexus = 0;
+       bool sa_res_key_unmatched = sa_res_key != 0;
        int prh_type = 0, prh_scope = 0;
 
        if (!se_sess)
@@ -2813,6 +2814,7 @@ core_scsi3_pro_preempt(struct se_cmd *cmd, int type, int scope, u64 res_key,
                        if (!all_reg) {
                                if (pr_reg->pr_res_key != sa_res_key)
                                        continue;
+                               sa_res_key_unmatched = false;
 
                                calling_it_nexus = (pr_reg_n == pr_reg) ? 1 : 0;
                                pr_reg_nacl = pr_reg->pr_reg_nacl;
@@ -2820,7 +2822,6 @@ core_scsi3_pro_preempt(struct se_cmd *cmd, int type, int scope, u64 res_key,
                                __core_scsi3_free_registration(dev, pr_reg,
                                        (preempt_type == PREEMPT_AND_ABORT) ? &preempt_and_abort_list :
                                                NULL, calling_it_nexus);
-                               released_regs++;
                        } else {
                                /*
                                 * Case for any existing all registrants type
@@ -2838,6 +2839,7 @@ core_scsi3_pro_preempt(struct se_cmd *cmd, int type, int scope, u64 res_key,
                                if ((sa_res_key) &&
                                     (pr_reg->pr_res_key != sa_res_key))
                                        continue;
+                               sa_res_key_unmatched = false;
 
                                calling_it_nexus = (pr_reg_n == pr_reg) ? 1 : 0;
                                if (calling_it_nexus)
@@ -2848,7 +2850,6 @@ core_scsi3_pro_preempt(struct se_cmd *cmd, int type, int scope, u64 res_key,
                                __core_scsi3_free_registration(dev, pr_reg,
                                        (preempt_type == PREEMPT_AND_ABORT) ? &preempt_and_abort_list :
                                                NULL, 0);
-                               released_regs++;
                        }
                        if (!calling_it_nexus)
                                core_scsi3_ua_allocate(pr_reg_nacl,
@@ -2863,7 +2864,7 @@ core_scsi3_pro_preempt(struct se_cmd *cmd, int type, int scope, u64 res_key,
                 * registered reservation key, then the device server shall
                 * complete the command with RESERVATION CONFLICT status.
                 */
-               if (!released_regs) {
+               if (sa_res_key_unmatched) {
                        spin_unlock(&dev->dev_reservation_lock);
                        core_scsi3_put_pr_reg(pr_reg_n);
                        return TCM_RESERVATION_CONFLICT;
index 9ea0d5f..be877bf 100644 (file)
@@ -2292,7 +2292,7 @@ transport_generic_new_cmd(struct se_cmd *cmd)
         * and let it call back once the write buffers are ready.
         */
        target_add_to_state_list(cmd);
-       if (cmd->data_direction != DMA_TO_DEVICE) {
+       if (cmd->data_direction != DMA_TO_DEVICE || cmd->data_length == 0) {
                target_execute_cmd(cmd);
                return 0;
        }
index 1ab0018..ad09e51 100644 (file)
@@ -50,15 +50,14 @@ struct cpufreq_cooling_device {
        unsigned int cpufreq_state;
        unsigned int cpufreq_val;
        struct cpumask allowed_cpus;
+       struct list_head node;
 };
 static DEFINE_IDR(cpufreq_idr);
 static DEFINE_MUTEX(cooling_cpufreq_lock);
 
 static unsigned int cpufreq_dev_count;
 
-/* notify_table passes value to the CPUFREQ_ADJUST callback function. */
-#define NOTIFY_INVALID NULL
-static struct cpufreq_cooling_device *notify_device;
+static LIST_HEAD(cpufreq_dev_list);
 
 /**
  * get_idr - function to get a unique id.
@@ -287,15 +286,12 @@ static int cpufreq_apply_cooling(struct cpufreq_cooling_device *cpufreq_device,
 
        cpufreq_device->cpufreq_state = cooling_state;
        cpufreq_device->cpufreq_val = clip_freq;
-       notify_device = cpufreq_device;
 
        for_each_cpu(cpuid, mask) {
                if (is_cpufreq_valid(cpuid))
                        cpufreq_update_policy(cpuid);
        }
 
-       notify_device = NOTIFY_INVALID;
-
        return 0;
 }
 
@@ -316,21 +312,28 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb,
 {
        struct cpufreq_policy *policy = data;
        unsigned long max_freq = 0;
+       struct cpufreq_cooling_device *cpufreq_dev;
 
-       if (event != CPUFREQ_ADJUST || notify_device == NOTIFY_INVALID)
+       if (event != CPUFREQ_ADJUST)
                return 0;
 
-       if (cpumask_test_cpu(policy->cpu, &notify_device->allowed_cpus))
-               max_freq = notify_device->cpufreq_val;
-       else
-               return 0;
+       mutex_lock(&cooling_cpufreq_lock);
+       list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) {
+               if (!cpumask_test_cpu(policy->cpu,
+                                       &cpufreq_dev->allowed_cpus))
+                       continue;
+
+               if (!cpufreq_dev->cpufreq_val)
+                       cpufreq_dev->cpufreq_val = get_cpu_frequency(
+                                       cpumask_any(&cpufreq_dev->allowed_cpus),
+                                       cpufreq_dev->cpufreq_state);
 
-       /* Never exceed user_policy.max */
-       if (max_freq > policy->user_policy.max)
-               max_freq = policy->user_policy.max;
+               max_freq = cpufreq_dev->cpufreq_val;
 
-       if (policy->max != max_freq)
-               cpufreq_verify_within_limits(policy, 0, max_freq);
+               if (policy->max != max_freq)
+                       cpufreq_verify_within_limits(policy, 0, max_freq);
+       }
+       mutex_unlock(&cooling_cpufreq_lock);
 
        return 0;
 }
@@ -486,6 +489,7 @@ __cpufreq_cooling_register(struct device_node *np,
                cpufreq_register_notifier(&thermal_cpufreq_notifier_block,
                                          CPUFREQ_POLICY_NOTIFIER);
        cpufreq_dev_count++;
+       list_add(&cpufreq_dev->node, &cpufreq_dev_list);
 
        mutex_unlock(&cooling_cpufreq_lock);
 
@@ -549,6 +553,7 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
 
        cpufreq_dev = cdev->devdata;
        mutex_lock(&cooling_cpufreq_lock);
+       list_del(&cpufreq_dev->node);
        cpufreq_dev_count--;
 
        /* Unregister the notifier for the last cpufreq cooling device */
index 461bf3d..5a1f107 100644 (file)
@@ -459,6 +459,10 @@ static int imx_thermal_probe(struct platform_device *pdev)
        int measure_freq;
        int ret;
 
+       if (!cpufreq_get_current_driver()) {
+               dev_dbg(&pdev->dev, "no cpufreq driver!");
+               return -EPROBE_DEFER;
+       }
        data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
@@ -521,6 +525,30 @@ static int imx_thermal_probe(struct platform_device *pdev)
                return ret;
        }
 
+       data->thermal_clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(data->thermal_clk)) {
+               ret = PTR_ERR(data->thermal_clk);
+               if (ret != -EPROBE_DEFER)
+                       dev_err(&pdev->dev,
+                               "failed to get thermal clk: %d\n", ret);
+               cpufreq_cooling_unregister(data->cdev);
+               return ret;
+       }
+
+       /*
+        * Thermal sensor needs clk on to get correct value, normally
+        * we should enable its clk before taking measurement and disable
+        * clk after measurement is done, but if alarm function is enabled,
+        * hardware will auto measure the temperature periodically, so we
+        * need to keep the clk always on for alarm function.
+        */
+       ret = clk_prepare_enable(data->thermal_clk);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to enable thermal clk: %d\n", ret);
+               cpufreq_cooling_unregister(data->cdev);
+               return ret;
+       }
+
        data->tz = thermal_zone_device_register("imx_thermal_zone",
                                                IMX_TRIP_NUM,
                                                BIT(IMX_TRIP_PASSIVE), data,
@@ -531,26 +559,11 @@ static int imx_thermal_probe(struct platform_device *pdev)
                ret = PTR_ERR(data->tz);
                dev_err(&pdev->dev,
                        "failed to register thermal zone device %d\n", ret);
+               clk_disable_unprepare(data->thermal_clk);
                cpufreq_cooling_unregister(data->cdev);
                return ret;
        }
 
-       data->thermal_clk = devm_clk_get(&pdev->dev, NULL);
-       if (IS_ERR(data->thermal_clk)) {
-               dev_warn(&pdev->dev, "failed to get thermal clk!\n");
-       } else {
-               /*
-                * Thermal sensor needs clk on to get correct value, normally
-                * we should enable its clk before taking measurement and disable
-                * clk after measurement is done, but if alarm function is enabled,
-                * hardware will auto measure the temperature periodically, so we
-                * need to keep the clk always on for alarm function.
-                */
-               ret = clk_prepare_enable(data->thermal_clk);
-               if (ret)
-                       dev_warn(&pdev->dev, "failed to enable thermal clk: %d\n", ret);
-       }
-
        /* Enable measurements at ~ 10 Hz */
        regmap_write(map, TEMPSENSE1 + REG_CLR, TEMPSENSE1_MEASURE_FREQ);
        measure_freq = DIV_ROUND_UP(32768, 10); /* 10 Hz */
index d20dba9..6e9fb62 100644 (file)
@@ -92,7 +92,13 @@ static int sys_get_trip_hyst(struct thermal_zone_device *tzone,
        if (ACPI_FAILURE(status))
                return -EIO;
 
-       *temp = DECI_KELVIN_TO_MILLI_CELSIUS(hyst, KELVIN_OFFSET);
+       /*
+        * Thermal hysteresis represents a temperature difference.
+        * Kelvin and Celsius have same degree size. So the
+        * conversion here between tenths of degree Kelvin unit
+        * and Milli-Celsius unit is just to multiply 100.
+        */
+       *temp = hyst * 100;
 
        return 0;
 }
index f8eb625..62143ba 100644 (file)
@@ -387,15 +387,18 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id,
                                int (*get_trend)(void *, long *))
 {
        struct device_node *np, *child, *sensor_np;
+       struct thermal_zone_device *tzd = ERR_PTR(-ENODEV);
 
        np = of_find_node_by_name(NULL, "thermal-zones");
        if (!np)
                return ERR_PTR(-ENODEV);
 
-       if (!dev || !dev->of_node)
+       if (!dev || !dev->of_node) {
+               of_node_put(np);
                return ERR_PTR(-EINVAL);
+       }
 
-       sensor_np = dev->of_node;
+       sensor_np = of_node_get(dev->of_node);
 
        for_each_child_of_node(np, child) {
                struct of_phandle_args sensor_specs;
@@ -422,16 +425,21 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id,
                }
 
                if (sensor_specs.np == sensor_np && id == sensor_id) {
-                       of_node_put(np);
-                       return thermal_zone_of_add_sensor(child, sensor_np,
-                                                         data,
-                                                         get_temp,
-                                                         get_trend);
+                       tzd = thermal_zone_of_add_sensor(child, sensor_np,
+                                                        data,
+                                                        get_temp,
+                                                        get_trend);
+                       of_node_put(sensor_specs.np);
+                       of_node_put(child);
+                       goto exit;
                }
+               of_node_put(sensor_specs.np);
        }
+exit:
+       of_node_put(sensor_np);
        of_node_put(np);
 
-       return ERR_PTR(-ENODEV);
+       return tzd;
 }
 EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_register);
 
@@ -623,6 +631,7 @@ static int thermal_of_populate_trip(struct device_node *np,
 
        /* Required for cooling map matching */
        trip->np = np;
+       of_node_get(np);
 
        return 0;
 }
@@ -730,9 +739,14 @@ finish:
        return tz;
 
 free_tbps:
+       for (i = 0; i < tz->num_tbps; i++)
+               of_node_put(tz->tbps[i].cooling_device);
        kfree(tz->tbps);
 free_trips:
+       for (i = 0; i < tz->ntrips; i++)
+               of_node_put(tz->trips[i].np);
        kfree(tz->trips);
+       of_node_put(gchild);
 free_tz:
        kfree(tz);
        of_node_put(child);
@@ -742,7 +756,13 @@ free_tz:
 
 static inline void of_thermal_free_zone(struct __thermal_zone *tz)
 {
+       int i;
+
+       for (i = 0; i < tz->num_tbps; i++)
+               of_node_put(tz->tbps[i].cooling_device);
        kfree(tz->tbps);
+       for (i = 0; i < tz->ntrips; i++)
+               of_node_put(tz->trips[i].np);
        kfree(tz->trips);
        kfree(tz);
 }
@@ -814,10 +834,13 @@ int __init of_parse_thermal_zones(void)
                        /* attempting to build remaining zones still */
                }
        }
+       of_node_put(np);
 
        return 0;
 
 exit_free:
+       of_node_put(child);
+       of_node_put(np);
        of_thermal_free_zone(tz);
 
        /* no memory available, so free what we have built */
@@ -859,4 +882,5 @@ void of_thermal_destroy_zones(void)
                kfree(zone->ops);
                of_thermal_free_zone(zone->devdata);
        }
+       of_node_put(np);
 }
index 3f5ad25..b6be572 100644 (file)
@@ -417,13 +417,10 @@ void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf)
 
        th_zone = sensor_conf->pzone_data;
 
-       if (th_zone->therm_dev)
-               thermal_zone_device_unregister(th_zone->therm_dev);
+       thermal_zone_device_unregister(th_zone->therm_dev);
 
-       for (i = 0; i < th_zone->cool_dev_size; i++) {
-               if (th_zone->cool_dev[i])
-                       cpufreq_cooling_unregister(th_zone->cool_dev[i]);
-       }
+       for (i = 0; i < th_zone->cool_dev_size; ++i)
+               cpufreq_cooling_unregister(th_zone->cool_dev[i]);
 
        dev_info(sensor_conf->dev,
                "Exynos: Kernel Thermal management unregistered\n");
index 3eb2ed9..158f5aa 100644 (file)
@@ -27,7 +27,7 @@
 #define SENSOR_NAME_LEN        16
 #define MAX_TRIP_COUNT 8
 #define MAX_COOLING_DEVICE 4
-#define MAX_THRESHOLD_LEVS 5
+#define MAX_TRIMINFO_CTRL_REG  2
 
 #define ACTIVE_INTERVAL 500
 #define IDLE_INTERVAL 10000
index acbff14..49c0924 100644 (file)
@@ -77,16 +77,6 @@ static int temp_to_code(struct exynos_tmu_data *data, u8 temp)
        struct exynos_tmu_platform_data *pdata = data->pdata;
        int temp_code;
 
-       if (pdata->cal_mode == HW_MODE)
-               return temp;
-
-       if (data->soc == SOC_ARCH_EXYNOS4210)
-               /* temp should range between 25 and 125 */
-               if (temp < 25 || temp > 125) {
-                       temp_code = -EINVAL;
-                       goto out;
-               }
-
        switch (pdata->cal_type) {
        case TYPE_TWO_POINT_TRIMMING:
                temp_code = (temp - pdata->first_point_trim) *
@@ -101,7 +91,7 @@ static int temp_to_code(struct exynos_tmu_data *data, u8 temp)
                temp_code = temp + pdata->default_temp_offset;
                break;
        }
-out:
+
        return temp_code;
 }
 
@@ -114,16 +104,6 @@ static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code)
        struct exynos_tmu_platform_data *pdata = data->pdata;
        int temp;
 
-       if (pdata->cal_mode == HW_MODE)
-               return temp_code;
-
-       if (data->soc == SOC_ARCH_EXYNOS4210)
-               /* temp_code should range between 75 and 175 */
-               if (temp_code < 75 || temp_code > 175) {
-                       temp = -ENODATA;
-                       goto out;
-               }
-
        switch (pdata->cal_type) {
        case TYPE_TWO_POINT_TRIMMING:
                temp = (temp_code - data->temp_error1) *
@@ -138,18 +118,35 @@ static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code)
                temp = temp_code - pdata->default_temp_offset;
                break;
        }
-out:
+
        return temp;
 }
 
+static void exynos_tmu_clear_irqs(struct exynos_tmu_data *data)
+{
+       const struct exynos_tmu_registers *reg = data->pdata->registers;
+       unsigned int val_irq;
+
+       val_irq = readl(data->base + reg->tmu_intstat);
+       /*
+        * Clear the interrupts.  Please note that the documentation for
+        * Exynos3250, Exynos4412, Exynos5250 and Exynos5260 incorrectly
+        * states that INTCLEAR register has a different placing of bits
+        * responsible for FALL IRQs than INTSTAT register.  Exynos5420
+        * and Exynos5440 documentation is correct (Exynos4210 doesn't
+        * support FALL IRQs at all).
+        */
+       writel(val_irq, data->base + reg->tmu_intclear);
+}
+
 static int exynos_tmu_initialize(struct platform_device *pdev)
 {
        struct exynos_tmu_data *data = platform_get_drvdata(pdev);
        struct exynos_tmu_platform_data *pdata = data->pdata;
        const struct exynos_tmu_registers *reg = pdata->registers;
-       unsigned int status, trim_info = 0, con;
+       unsigned int status, trim_info = 0, con, ctrl;
        unsigned int rising_threshold = 0, falling_threshold = 0;
-       int ret = 0, threshold_code, i, trigger_levs = 0;
+       int ret = 0, threshold_code, i;
 
        mutex_lock(&data->lock);
        clk_enable(data->clk);
@@ -164,11 +161,17 @@ static int exynos_tmu_initialize(struct platform_device *pdev)
                }
        }
 
-       if (TMU_SUPPORTS(pdata, TRIM_RELOAD))
-               __raw_writel(1, data->base + reg->triminfo_ctrl);
-
-       if (pdata->cal_mode == HW_MODE)
-               goto skip_calib_data;
+       if (TMU_SUPPORTS(pdata, TRIM_RELOAD)) {
+               for (i = 0; i < reg->triminfo_ctrl_count; i++) {
+                       if (pdata->triminfo_reload[i]) {
+                               ctrl = readl(data->base +
+                                               reg->triminfo_ctrl[i]);
+                               ctrl |= pdata->triminfo_reload[i];
+                               writel(ctrl, data->base +
+                                               reg->triminfo_ctrl[i]);
+                       }
+               }
+       }
 
        /* Save trimming info in order to perform calibration */
        if (data->soc == SOC_ARCH_EXYNOS5440) {
@@ -197,7 +200,7 @@ static int exynos_tmu_initialize(struct platform_device *pdev)
                        trim_info = readl(data->base + reg->triminfo_data);
        }
        data->temp_error1 = trim_info & EXYNOS_TMU_TEMP_MASK;
-       data->temp_error2 = ((trim_info >> reg->triminfo_85_shift) &
+       data->temp_error2 = ((trim_info >> EXYNOS_TRIMINFO_85_SHIFT) &
                                EXYNOS_TMU_TEMP_MASK);
 
        if (!data->temp_error1 ||
@@ -207,67 +210,33 @@ static int exynos_tmu_initialize(struct platform_device *pdev)
 
        if (!data->temp_error2)
                data->temp_error2 =
-                       (pdata->efuse_value >> reg->triminfo_85_shift) &
+                       (pdata->efuse_value >> EXYNOS_TRIMINFO_85_SHIFT) &
                        EXYNOS_TMU_TEMP_MASK;
 
-skip_calib_data:
-       if (pdata->max_trigger_level > MAX_THRESHOLD_LEVS) {
-               dev_err(&pdev->dev, "Invalid max trigger level\n");
-               ret = -EINVAL;
-               goto out;
-       }
-
-       for (i = 0; i < pdata->max_trigger_level; i++) {
-               if (!pdata->trigger_levels[i])
-                       continue;
-
-               if ((pdata->trigger_type[i] == HW_TRIP) &&
-               (!pdata->trigger_levels[pdata->max_trigger_level - 1])) {
-                       dev_err(&pdev->dev, "Invalid hw trigger level\n");
-                       ret = -EINVAL;
-                       goto out;
-               }
-
-               /* Count trigger levels except the HW trip*/
-               if (!(pdata->trigger_type[i] == HW_TRIP))
-                       trigger_levs++;
-       }
-
        rising_threshold = readl(data->base + reg->threshold_th0);
 
        if (data->soc == SOC_ARCH_EXYNOS4210) {
                /* Write temperature code for threshold */
                threshold_code = temp_to_code(data, pdata->threshold);
-               if (threshold_code < 0) {
-                       ret = threshold_code;
-                       goto out;
-               }
                writeb(threshold_code,
                        data->base + reg->threshold_temp);
-               for (i = 0; i < trigger_levs; i++)
+               for (i = 0; i < pdata->non_hw_trigger_levels; i++)
                        writeb(pdata->trigger_levels[i], data->base +
                        reg->threshold_th0 + i * sizeof(reg->threshold_th0));
 
-               writel(reg->intclr_rise_mask, data->base + reg->tmu_intclear);
+               exynos_tmu_clear_irqs(data);
        } else {
                /* Write temperature code for rising and falling threshold */
-               for (i = 0;
-               i < trigger_levs && i < EXYNOS_MAX_TRIGGER_PER_REG; i++) {
+               for (i = 0; i < pdata->non_hw_trigger_levels; i++) {
                        threshold_code = temp_to_code(data,
                                                pdata->trigger_levels[i]);
-                       if (threshold_code < 0) {
-                               ret = threshold_code;
-                               goto out;
-                       }
                        rising_threshold &= ~(0xff << 8 * i);
                        rising_threshold |= threshold_code << 8 * i;
                        if (pdata->threshold_falling) {
                                threshold_code = temp_to_code(data,
                                                pdata->trigger_levels[i] -
                                                pdata->threshold_falling);
-                               if (threshold_code > 0)
-                                       falling_threshold |=
-                                               threshold_code << 8 * i;
+                               falling_threshold |= threshold_code << 8 * i;
                        }
                }
 
@@ -276,9 +245,7 @@ skip_calib_data:
                writel(falling_threshold,
                                data->base + reg->threshold_th1);
 
-               writel((reg->intclr_rise_mask << reg->intclr_rise_shift) |
-                       (reg->intclr_fall_mask << reg->intclr_fall_shift),
-                               data->base + reg->tmu_intclear);
+               exynos_tmu_clear_irqs(data);
 
                /* if last threshold limit is also present */
                i = pdata->max_trigger_level - 1;
@@ -286,10 +253,6 @@ skip_calib_data:
                                (pdata->trigger_type[i] == HW_TRIP)) {
                        threshold_code = temp_to_code(data,
                                                pdata->trigger_levels[i]);
-                       if (threshold_code < 0) {
-                               ret = threshold_code;
-                               goto out;
-                       }
                        if (i == EXYNOS_MAX_TRIGGER_PER_REG - 1) {
                                /* 1-4 level to be assigned in th0 reg */
                                rising_threshold &= ~(0xff << 8 * i);
@@ -325,7 +288,7 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on)
        struct exynos_tmu_data *data = platform_get_drvdata(pdev);
        struct exynos_tmu_platform_data *pdata = data->pdata;
        const struct exynos_tmu_registers *reg = pdata->registers;
-       unsigned int con, interrupt_en, cal_val;
+       unsigned int con, interrupt_en;
 
        mutex_lock(&data->lock);
        clk_enable(data->clk);
@@ -335,15 +298,11 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on)
        if (pdata->test_mux)
                con |= (pdata->test_mux << reg->test_mux_addr_shift);
 
-       if (pdata->reference_voltage) {
-               con &= ~(reg->buf_vref_sel_mask << reg->buf_vref_sel_shift);
-               con |= pdata->reference_voltage << reg->buf_vref_sel_shift;
-       }
+       con &= ~(EXYNOS_TMU_REF_VOLTAGE_MASK << EXYNOS_TMU_REF_VOLTAGE_SHIFT);
+       con |= pdata->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT;
 
-       if (pdata->gain) {
-               con &= ~(reg->buf_slope_sel_mask << reg->buf_slope_sel_shift);
-               con |= (pdata->gain << reg->buf_slope_sel_shift);
-       }
+       con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT);
+       con |= (pdata->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT);
 
        if (pdata->noise_cancel_mode) {
                con &= ~(reg->therm_trip_mode_mask <<
@@ -351,29 +310,8 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on)
                con |= (pdata->noise_cancel_mode << reg->therm_trip_mode_shift);
        }
 
-       if (pdata->cal_mode == HW_MODE) {
-               con &= ~(reg->calib_mode_mask << reg->calib_mode_shift);
-               cal_val = 0;
-               switch (pdata->cal_type) {
-               case TYPE_TWO_POINT_TRIMMING:
-                       cal_val = 3;
-                       break;
-               case TYPE_ONE_POINT_TRIMMING_85:
-                       cal_val = 2;
-                       break;
-               case TYPE_ONE_POINT_TRIMMING_25:
-                       cal_val = 1;
-                       break;
-               case TYPE_NONE:
-                       break;
-               default:
-                       dev_err(&pdev->dev, "Invalid calibration type, using none\n");
-               }
-               con |= cal_val << reg->calib_mode_shift;
-       }
-
        if (on) {
-               con |= (1 << reg->core_en_shift);
+               con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT);
                interrupt_en =
                        pdata->trigger_enable[3] << reg->inten_rise3_shift |
                        pdata->trigger_enable[2] << reg->inten_rise2_shift |
@@ -383,7 +321,7 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on)
                        interrupt_en |=
                                interrupt_en << reg->inten_fall0_shift;
        } else {
-               con &= ~(1 << reg->core_en_shift);
+               con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT);
                interrupt_en = 0; /* Disable all interrupts */
        }
        writel(interrupt_en, data->base + reg->tmu_inten);
@@ -404,8 +342,16 @@ static int exynos_tmu_read(struct exynos_tmu_data *data)
        clk_enable(data->clk);
 
        temp_code = readb(data->base + reg->tmu_cur_temp);
-       temp = code_to_temp(data, temp_code);
 
+       if (data->soc == SOC_ARCH_EXYNOS4210)
+               /* temp_code should range between 75 and 175 */
+               if (temp_code < 75 || temp_code > 175) {
+                       temp = -ENODATA;
+                       goto out;
+               }
+
+       temp = code_to_temp(data, temp_code);
+out:
        clk_disable(data->clk);
        mutex_unlock(&data->lock);
 
@@ -465,7 +411,7 @@ static void exynos_tmu_work(struct work_struct *work)
                        struct exynos_tmu_data, irq_work);
        struct exynos_tmu_platform_data *pdata = data->pdata;
        const struct exynos_tmu_registers *reg = pdata->registers;
-       unsigned int val_irq, val_type;
+       unsigned int val_type;
 
        if (!IS_ERR(data->clk_sec))
                clk_enable(data->clk_sec);
@@ -483,9 +429,7 @@ static void exynos_tmu_work(struct work_struct *work)
        clk_enable(data->clk);
 
        /* TODO: take action based on particular interrupt */
-       val_irq = readl(data->base + reg->tmu_intstat);
-       /* clear the interrupts */
-       writel(val_irq, data->base + reg->tmu_intclear);
+       exynos_tmu_clear_irqs(data);
 
        clk_disable(data->clk);
        mutex_unlock(&data->lock);
index 1b4a644..c58c766 100644 (file)
@@ -34,11 +34,6 @@ enum calibration_type {
        TYPE_NONE,
 };
 
-enum calibration_mode {
-       SW_MODE,
-       HW_MODE,
-};
-
 enum soc_type {
        SOC_ARCH_EXYNOS3250 = 1,
        SOC_ARCH_EXYNOS4210,
@@ -82,46 +77,19 @@ enum soc_type {
  * bitfields. The register validity, offsets and bitfield values may vary
  * slightly across different exynos SOC's.
  * @triminfo_data: register containing 2 pont trimming data
- * @triminfo_25_shift: shift bit of the 25 C trim value in triminfo_data reg.
- * @triminfo_85_shift: shift bit of the 85 C trim value in triminfo_data reg.
  * @triminfo_ctrl: trim info controller register.
- * @triminfo_reload_shift: shift of triminfo reload enable bit in triminfo_ctrl
-       reg.
+ * @triminfo_ctrl_count: the number of trim info controller register.
  * @tmu_ctrl: TMU main controller register.
  * @test_mux_addr_shift: shift bits of test mux address.
- * @buf_vref_sel_shift: shift bits of reference voltage in tmu_ctrl register.
- * @buf_vref_sel_mask: mask bits of reference voltage in tmu_ctrl register.
  * @therm_trip_mode_shift: shift bits of tripping mode in tmu_ctrl register.
  * @therm_trip_mode_mask: mask bits of tripping mode in tmu_ctrl register.
  * @therm_trip_en_shift: shift bits of tripping enable in tmu_ctrl register.
- * @buf_slope_sel_shift: shift bits of amplifier gain value in tmu_ctrl
-       register.
- * @buf_slope_sel_mask: mask bits of amplifier gain value in tmu_ctrl register.
- * @calib_mode_shift: shift bits of calibration mode value in tmu_ctrl
-       register.
- * @calib_mode_mask: mask bits of calibration mode value in tmu_ctrl
-       register.
- * @therm_trip_tq_en_shift: shift bits of thermal trip enable by TQ pin in
-       tmu_ctrl register.
- * @core_en_shift: shift bits of TMU core enable bit in tmu_ctrl register.
  * @tmu_status: register drescribing the TMU status.
  * @tmu_cur_temp: register containing the current temperature of the TMU.
- * @tmu_cur_temp_shift: shift bits of current temp value in tmu_cur_temp
-       register.
  * @threshold_temp: register containing the base threshold level.
  * @threshold_th0: Register containing first set of rising levels.
- * @threshold_th0_l0_shift: shift bits of level0 threshold temperature.
- * @threshold_th0_l1_shift: shift bits of level1 threshold temperature.
- * @threshold_th0_l2_shift: shift bits of level2 threshold temperature.
- * @threshold_th0_l3_shift: shift bits of level3 threshold temperature.
  * @threshold_th1: Register containing second set of rising levels.
- * @threshold_th1_l0_shift: shift bits of level0 threshold temperature.
- * @threshold_th1_l1_shift: shift bits of level1 threshold temperature.
- * @threshold_th1_l2_shift: shift bits of level2 threshold temperature.
- * @threshold_th1_l3_shift: shift bits of level3 threshold temperature.
  * @threshold_th2: Register containing third set of rising levels.
- * @threshold_th2_l0_shift: shift bits of level0 threshold temperature.
- * @threshold_th3: Register containing fourth set of rising levels.
  * @threshold_th3_l0_shift: shift bits of level0 threshold temperature.
  * @tmu_inten: register containing the different threshold interrupt
        enable bits.
@@ -130,68 +98,35 @@ enum soc_type {
  * @inten_rise2_shift: shift bits of rising 2 interrupt bits.
  * @inten_rise3_shift: shift bits of rising 3 interrupt bits.
  * @inten_fall0_shift: shift bits of falling 0 interrupt bits.
- * @inten_fall1_shift: shift bits of falling 1 interrupt bits.
- * @inten_fall2_shift: shift bits of falling 2 interrupt bits.
- * @inten_fall3_shift: shift bits of falling 3 interrupt bits.
  * @tmu_intstat: Register containing the interrupt status values.
  * @tmu_intclear: Register for clearing the raised interrupt status.
- * @intclr_fall_shift: shift bits for interrupt clear fall 0
- * @intclr_rise_shift: shift bits of all rising interrupt bits.
- * @intclr_rise_mask: mask bits of all rising interrupt bits.
- * @intclr_fall_mask: mask bits of all rising interrupt bits.
  * @emul_con: TMU emulation controller register.
  * @emul_temp_shift: shift bits of emulation temperature.
  * @emul_time_shift: shift bits of emulation time.
- * @emul_time_mask: mask bits of emulation time.
  * @tmu_irqstatus: register to find which TMU generated interrupts.
  * @tmu_pmin: register to get/set the Pmin value.
  */
 struct exynos_tmu_registers {
        u32     triminfo_data;
-       u32     triminfo_25_shift;
-       u32     triminfo_85_shift;
 
-       u32     triminfo_ctrl;
-       u32     triminfo_ctrl1;
-       u32     triminfo_reload_shift;
+       u32     triminfo_ctrl[MAX_TRIMINFO_CTRL_REG];
+       u32     triminfo_ctrl_count;
 
        u32     tmu_ctrl;
        u32     test_mux_addr_shift;
-       u32     buf_vref_sel_shift;
-       u32     buf_vref_sel_mask;
        u32     therm_trip_mode_shift;
        u32     therm_trip_mode_mask;
        u32     therm_trip_en_shift;
-       u32     buf_slope_sel_shift;
-       u32     buf_slope_sel_mask;
-       u32     calib_mode_shift;
-       u32     calib_mode_mask;
-       u32     therm_trip_tq_en_shift;
-       u32     core_en_shift;
 
        u32     tmu_status;
 
        u32     tmu_cur_temp;
-       u32     tmu_cur_temp_shift;
 
        u32     threshold_temp;
 
        u32     threshold_th0;
-       u32     threshold_th0_l0_shift;
-       u32     threshold_th0_l1_shift;
-       u32     threshold_th0_l2_shift;
-       u32     threshold_th0_l3_shift;
-
        u32     threshold_th1;
-       u32     threshold_th1_l0_shift;
-       u32     threshold_th1_l1_shift;
-       u32     threshold_th1_l2_shift;
-       u32     threshold_th1_l3_shift;
-
        u32     threshold_th2;
-       u32     threshold_th2_l0_shift;
-
-       u32     threshold_th3;
        u32     threshold_th3_l0_shift;
 
        u32     tmu_inten;
@@ -200,22 +135,14 @@ struct exynos_tmu_registers {
        u32     inten_rise2_shift;
        u32     inten_rise3_shift;
        u32     inten_fall0_shift;
-       u32     inten_fall1_shift;
-       u32     inten_fall2_shift;
-       u32     inten_fall3_shift;
 
        u32     tmu_intstat;
 
        u32     tmu_intclear;
-       u32     intclr_fall_shift;
-       u32     intclr_rise_shift;
-       u32     intclr_fall_mask;
-       u32     intclr_rise_mask;
 
        u32     emul_con;
        u32     emul_temp_shift;
        u32     emul_time_shift;
-       u32     emul_time_mask;
 
        u32     tmu_irqstatus;
        u32     tmu_pmin;
@@ -250,11 +177,12 @@ struct exynos_tmu_registers {
  *     1 = enable trigger_level[] interrupt,
  *     0 = disable trigger_level[] interrupt
  * @max_trigger_level: max trigger level supported by the TMU
+ * @non_hw_trigger_levels: number of defined non-hardware trigger levels
  * @gain: gain of amplifier in the positive-TC generator block
- *     0 <= gain <= 15
+ *     0 < gain <= 15
  * @reference_voltage: reference voltage of amplifier
  *     in the positive-TC generator block
- *     0 <= reference_voltage <= 31
+ *     0 < reference_voltage <= 31
  * @noise_cancel_mode: noise cancellation mode
  *     000, 100, 101, 110 and 111 can be different modes
  * @type: determines the type of SOC
@@ -265,8 +193,8 @@ struct exynos_tmu_registers {
  * @second_point_trim: temp value of the second point trimming
  * @default_temp_offset: default temperature offset in case of no trimming
  * @test_mux; information if SoC supports test MUX
+ * @triminfo_reload: reload value to read TRIMINFO register
  * @cal_type: calibration type for temperature
- * @cal_mode: calibration mode for temperature
  * @freq_clip_table: Table representing frequency reduction percentage.
  * @freq_tab_count: Count of the above table as frequency reduction may
  *     applicable to only some of the trigger levels.
@@ -284,6 +212,7 @@ struct exynos_tmu_platform_data {
        enum trigger_type trigger_type[MAX_TRIP_COUNT];
        bool trigger_enable[MAX_TRIP_COUNT];
        u8 max_trigger_level;
+       u8 non_hw_trigger_levels;
        u8 gain;
        u8 reference_voltage;
        u8 noise_cancel_mode;
@@ -295,9 +224,9 @@ struct exynos_tmu_platform_data {
        u8 second_point_trim;
        u8 default_temp_offset;
        u8 test_mux;
+       u8 triminfo_reload[MAX_TRIMINFO_CTRL_REG];
 
        enum calibration_type cal_type;
-       enum calibration_mode cal_mode;
        enum soc_type type;
        struct freq_clip_table freq_tab[4];
        unsigned int freq_tab_count;
index aa8e0de..1724f6c 100644 (file)
 #if defined(CONFIG_CPU_EXYNOS4210)
 static const struct exynos_tmu_registers exynos4210_tmu_registers = {
        .triminfo_data = EXYNOS_TMU_REG_TRIMINFO,
-       .triminfo_25_shift = EXYNOS_TRIMINFO_25_SHIFT,
-       .triminfo_85_shift = EXYNOS_TRIMINFO_85_SHIFT,
        .tmu_ctrl = EXYNOS_TMU_REG_CONTROL,
-       .buf_vref_sel_shift = EXYNOS_TMU_REF_VOLTAGE_SHIFT,
-       .buf_vref_sel_mask = EXYNOS_TMU_REF_VOLTAGE_MASK,
-       .buf_slope_sel_shift = EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT,
-       .buf_slope_sel_mask = EXYNOS_TMU_BUF_SLOPE_SEL_MASK,
-       .core_en_shift = EXYNOS_TMU_CORE_EN_SHIFT,
        .tmu_status = EXYNOS_TMU_REG_STATUS,
        .tmu_cur_temp = EXYNOS_TMU_REG_CURRENT_TEMP,
        .threshold_temp = EXYNOS4210_TMU_REG_THRESHOLD_TEMP,
@@ -46,7 +39,6 @@ static const struct exynos_tmu_registers exynos4210_tmu_registers = {
        .inten_rise3_shift = EXYNOS_TMU_INTEN_RISE3_SHIFT,
        .tmu_intstat = EXYNOS_TMU_REG_INTSTAT,
        .tmu_intclear = EXYNOS_TMU_REG_INTCLEAR,
-       .intclr_rise_mask = EXYNOS4210_TMU_TRIG_LEVEL_MASK,
 };
 
 struct exynos_tmu_init_data const exynos4210_default_tmu_data = {
@@ -64,6 +56,7 @@ struct exynos_tmu_init_data const exynos4210_default_tmu_data = {
                .trigger_type[1] = THROTTLE_ACTIVE,
                .trigger_type[2] = SW_TRIP,
                .max_trigger_level = 4,
+               .non_hw_trigger_levels = 3,
                .gain = 15,
                .reference_voltage = 7,
                .cal_type = TYPE_ONE_POINT_TRIMMING,
@@ -93,18 +86,14 @@ struct exynos_tmu_init_data const exynos4210_default_tmu_data = {
 #if defined(CONFIG_SOC_EXYNOS3250)
 static const struct exynos_tmu_registers exynos3250_tmu_registers = {
        .triminfo_data = EXYNOS_TMU_REG_TRIMINFO,
-       .triminfo_25_shift = EXYNOS_TRIMINFO_25_SHIFT,
-       .triminfo_85_shift = EXYNOS_TRIMINFO_85_SHIFT,
+       .triminfo_ctrl[0] = EXYNOS_TMU_TRIMINFO_CON1,
+       .triminfo_ctrl[1] = EXYNOS_TMU_TRIMINFO_CON2,
+       .triminfo_ctrl_count = 2,
        .tmu_ctrl = EXYNOS_TMU_REG_CONTROL,
        .test_mux_addr_shift = EXYNOS4412_MUX_ADDR_SHIFT,
-       .buf_vref_sel_shift = EXYNOS_TMU_REF_VOLTAGE_SHIFT,
-       .buf_vref_sel_mask = EXYNOS_TMU_REF_VOLTAGE_MASK,
        .therm_trip_mode_shift = EXYNOS_TMU_TRIP_MODE_SHIFT,
        .therm_trip_mode_mask = EXYNOS_TMU_TRIP_MODE_MASK,
        .therm_trip_en_shift = EXYNOS_TMU_THERM_TRIP_EN_SHIFT,
-       .buf_slope_sel_shift = EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT,
-       .buf_slope_sel_mask = EXYNOS_TMU_BUF_SLOPE_SEL_MASK,
-       .core_en_shift = EXYNOS_TMU_CORE_EN_SHIFT,
        .tmu_status = EXYNOS_TMU_REG_STATUS,
        .tmu_cur_temp = EXYNOS_TMU_REG_CURRENT_TEMP,
        .threshold_th0 = EXYNOS_THD_TEMP_RISE,
@@ -116,14 +105,9 @@ static const struct exynos_tmu_registers exynos3250_tmu_registers = {
        .inten_fall0_shift = EXYNOS_TMU_INTEN_FALL0_SHIFT,
        .tmu_intstat = EXYNOS_TMU_REG_INTSTAT,
        .tmu_intclear = EXYNOS_TMU_REG_INTCLEAR,
-       .intclr_fall_shift = EXYNOS_TMU_CLEAR_FALL_INT_SHIFT,
-       .intclr_rise_shift = EXYNOS_TMU_RISE_INT_SHIFT,
-       .intclr_rise_mask = EXYNOS_TMU_RISE_INT_MASK,
-       .intclr_fall_mask = EXYNOS_TMU_FALL_INT_MASK,
        .emul_con = EXYNOS_EMUL_CON,
        .emul_temp_shift = EXYNOS_EMUL_DATA_SHIFT,
        .emul_time_shift = EXYNOS_EMUL_TIME_SHIFT,
-       .emul_time_mask = EXYNOS_EMUL_TIME_MASK,
 };
 
 #define EXYNOS3250_TMU_DATA \
@@ -141,6 +125,7 @@ static const struct exynos_tmu_registers exynos3250_tmu_registers = {
        .trigger_type[2] = SW_TRIP, \
        .trigger_type[3] = HW_TRIP, \
        .max_trigger_level = 4, \
+       .non_hw_trigger_levels = 3, \
        .gain = 8, \
        .reference_voltage = 16, \
        .noise_cancel_mode = 4, \
@@ -160,8 +145,10 @@ static const struct exynos_tmu_registers exynos3250_tmu_registers = {
                .temp_level = 95, \
        }, \
        .freq_tab_count = 2, \
+       .triminfo_reload[0] = EXYNOS_TRIMINFO_RELOAD_ENABLE, \
+       .triminfo_reload[1] = EXYNOS_TRIMINFO_RELOAD_ENABLE, \
        .registers = &exynos3250_tmu_registers, \
-       .features = (TMU_SUPPORT_EMULATION | \
+       .features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_TRIM_RELOAD | \
                        TMU_SUPPORT_FALLING_TRIP | TMU_SUPPORT_READY_STATUS | \
                        TMU_SUPPORT_EMUL_TIME)
 #endif
@@ -182,20 +169,13 @@ struct exynos_tmu_init_data const exynos3250_default_tmu_data = {
 #if defined(CONFIG_SOC_EXYNOS4412) || defined(CONFIG_SOC_EXYNOS5250)
 static const struct exynos_tmu_registers exynos4412_tmu_registers = {
        .triminfo_data = EXYNOS_TMU_REG_TRIMINFO,
-       .triminfo_25_shift = EXYNOS_TRIMINFO_25_SHIFT,
-       .triminfo_85_shift = EXYNOS_TRIMINFO_85_SHIFT,
-       .triminfo_ctrl = EXYNOS_TMU_TRIMINFO_CON,
-       .triminfo_reload_shift = EXYNOS_TRIMINFO_RELOAD_SHIFT,
+       .triminfo_ctrl[0] = EXYNOS_TMU_TRIMINFO_CON2,
+       .triminfo_ctrl_count = 1,
        .tmu_ctrl = EXYNOS_TMU_REG_CONTROL,
        .test_mux_addr_shift = EXYNOS4412_MUX_ADDR_SHIFT,
-       .buf_vref_sel_shift = EXYNOS_TMU_REF_VOLTAGE_SHIFT,
-       .buf_vref_sel_mask = EXYNOS_TMU_REF_VOLTAGE_MASK,
        .therm_trip_mode_shift = EXYNOS_TMU_TRIP_MODE_SHIFT,
        .therm_trip_mode_mask = EXYNOS_TMU_TRIP_MODE_MASK,
        .therm_trip_en_shift = EXYNOS_TMU_THERM_TRIP_EN_SHIFT,
-       .buf_slope_sel_shift = EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT,
-       .buf_slope_sel_mask = EXYNOS_TMU_BUF_SLOPE_SEL_MASK,
-       .core_en_shift = EXYNOS_TMU_CORE_EN_SHIFT,
        .tmu_status = EXYNOS_TMU_REG_STATUS,
        .tmu_cur_temp = EXYNOS_TMU_REG_CURRENT_TEMP,
        .threshold_th0 = EXYNOS_THD_TEMP_RISE,
@@ -208,14 +188,9 @@ static const struct exynos_tmu_registers exynos4412_tmu_registers = {
        .inten_fall0_shift = EXYNOS_TMU_INTEN_FALL0_SHIFT,
        .tmu_intstat = EXYNOS_TMU_REG_INTSTAT,
        .tmu_intclear = EXYNOS_TMU_REG_INTCLEAR,
-       .intclr_fall_shift = EXYNOS_TMU_CLEAR_FALL_INT_SHIFT,
-       .intclr_rise_shift = EXYNOS_TMU_RISE_INT_SHIFT,
-       .intclr_rise_mask = EXYNOS_TMU_RISE_INT_MASK,
-       .intclr_fall_mask = EXYNOS_TMU_FALL_INT_MASK,
        .emul_con = EXYNOS_EMUL_CON,
        .emul_temp_shift = EXYNOS_EMUL_DATA_SHIFT,
        .emul_time_shift = EXYNOS_EMUL_TIME_SHIFT,
-       .emul_time_mask = EXYNOS_EMUL_TIME_MASK,
 };
 
 #define EXYNOS4412_TMU_DATA \
@@ -233,6 +208,7 @@ static const struct exynos_tmu_registers exynos4412_tmu_registers = {
        .trigger_type[2] = SW_TRIP, \
        .trigger_type[3] = HW_TRIP, \
        .max_trigger_level = 4, \
+       .non_hw_trigger_levels = 3, \
        .gain = 8, \
        .reference_voltage = 16, \
        .noise_cancel_mode = 4, \
@@ -252,6 +228,7 @@ static const struct exynos_tmu_registers exynos4412_tmu_registers = {
                .temp_level = 95, \
        }, \
        .freq_tab_count = 2, \
+       .triminfo_reload[0] = EXYNOS_TRIMINFO_RELOAD_ENABLE, \
        .registers = &exynos4412_tmu_registers, \
        .features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_TRIM_RELOAD | \
                        TMU_SUPPORT_FALLING_TRIP | TMU_SUPPORT_READY_STATUS | \
@@ -286,18 +263,10 @@ struct exynos_tmu_init_data const exynos5250_default_tmu_data = {
 #if defined(CONFIG_SOC_EXYNOS5260)
 static const struct exynos_tmu_registers exynos5260_tmu_registers = {
        .triminfo_data = EXYNOS_TMU_REG_TRIMINFO,
-       .triminfo_25_shift = EXYNOS_TRIMINFO_25_SHIFT,
-       .triminfo_85_shift = EXYNOS_TRIMINFO_85_SHIFT,
        .tmu_ctrl = EXYNOS_TMU_REG_CONTROL,
-       .tmu_ctrl = EXYNOS_TMU_REG_CONTROL1,
-       .buf_vref_sel_shift = EXYNOS_TMU_REF_VOLTAGE_SHIFT,
-       .buf_vref_sel_mask = EXYNOS_TMU_REF_VOLTAGE_MASK,
        .therm_trip_mode_shift = EXYNOS_TMU_TRIP_MODE_SHIFT,
        .therm_trip_mode_mask = EXYNOS_TMU_TRIP_MODE_MASK,
        .therm_trip_en_shift = EXYNOS_TMU_THERM_TRIP_EN_SHIFT,
-       .buf_slope_sel_shift = EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT,
-       .buf_slope_sel_mask = EXYNOS_TMU_BUF_SLOPE_SEL_MASK,
-       .core_en_shift = EXYNOS_TMU_CORE_EN_SHIFT,
        .tmu_status = EXYNOS_TMU_REG_STATUS,
        .tmu_cur_temp = EXYNOS_TMU_REG_CURRENT_TEMP,
        .threshold_th0 = EXYNOS_THD_TEMP_RISE,
@@ -310,14 +279,9 @@ static const struct exynos_tmu_registers exynos5260_tmu_registers = {
        .inten_fall0_shift = EXYNOS_TMU_INTEN_FALL0_SHIFT,
        .tmu_intstat = EXYNOS5260_TMU_REG_INTSTAT,
        .tmu_intclear = EXYNOS5260_TMU_REG_INTCLEAR,
-       .intclr_fall_shift = EXYNOS5420_TMU_CLEAR_FALL_INT_SHIFT,
-       .intclr_rise_shift = EXYNOS_TMU_RISE_INT_SHIFT,
-       .intclr_rise_mask = EXYNOS5260_TMU_RISE_INT_MASK,
-       .intclr_fall_mask = EXYNOS5260_TMU_FALL_INT_MASK,
        .emul_con = EXYNOS5260_EMUL_CON,
        .emul_temp_shift = EXYNOS_EMUL_DATA_SHIFT,
        .emul_time_shift = EXYNOS_EMUL_TIME_SHIFT,
-       .emul_time_mask = EXYNOS_EMUL_TIME_MASK,
 };
 
 #define __EXYNOS5260_TMU_DATA  \
@@ -335,6 +299,7 @@ static const struct exynos_tmu_registers exynos5260_tmu_registers = {
        .trigger_type[2] = SW_TRIP, \
        .trigger_type[3] = HW_TRIP, \
        .max_trigger_level = 4, \
+       .non_hw_trigger_levels = 3, \
        .gain = 8, \
        .reference_voltage = 16, \
        .noise_cancel_mode = 4, \
@@ -359,9 +324,8 @@ static const struct exynos_tmu_registers exynos5260_tmu_registers = {
 #define EXYNOS5260_TMU_DATA \
        __EXYNOS5260_TMU_DATA \
        .type = SOC_ARCH_EXYNOS5260, \
-       .features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_TRIM_RELOAD | \
-                       TMU_SUPPORT_FALLING_TRIP | TMU_SUPPORT_READY_STATUS | \
-                       TMU_SUPPORT_EMUL_TIME)
+       .features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_FALLING_TRIP | \
+                       TMU_SUPPORT_READY_STATUS | TMU_SUPPORT_EMUL_TIME)
 
 struct exynos_tmu_init_data const exynos5260_default_tmu_data = {
        .tmu_data = {
@@ -378,17 +342,10 @@ struct exynos_tmu_init_data const exynos5260_default_tmu_data = {
 #if defined(CONFIG_SOC_EXYNOS5420)
 static const struct exynos_tmu_registers exynos5420_tmu_registers = {
        .triminfo_data = EXYNOS_TMU_REG_TRIMINFO,
-       .triminfo_25_shift = EXYNOS_TRIMINFO_25_SHIFT,
-       .triminfo_85_shift = EXYNOS_TRIMINFO_85_SHIFT,
        .tmu_ctrl = EXYNOS_TMU_REG_CONTROL,
-       .buf_vref_sel_shift = EXYNOS_TMU_REF_VOLTAGE_SHIFT,
-       .buf_vref_sel_mask = EXYNOS_TMU_REF_VOLTAGE_MASK,
        .therm_trip_mode_shift = EXYNOS_TMU_TRIP_MODE_SHIFT,
        .therm_trip_mode_mask = EXYNOS_TMU_TRIP_MODE_MASK,
        .therm_trip_en_shift = EXYNOS_TMU_THERM_TRIP_EN_SHIFT,
-       .buf_slope_sel_shift = EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT,
-       .buf_slope_sel_mask = EXYNOS_TMU_BUF_SLOPE_SEL_MASK,
-       .core_en_shift = EXYNOS_TMU_CORE_EN_SHIFT,
        .tmu_status = EXYNOS_TMU_REG_STATUS,
        .tmu_cur_temp = EXYNOS_TMU_REG_CURRENT_TEMP,
        .threshold_th0 = EXYNOS_THD_TEMP_RISE,
@@ -402,14 +359,9 @@ static const struct exynos_tmu_registers exynos5420_tmu_registers = {
        .inten_fall0_shift = EXYNOS_TMU_INTEN_FALL0_SHIFT,
        .tmu_intstat = EXYNOS_TMU_REG_INTSTAT,
        .tmu_intclear = EXYNOS_TMU_REG_INTCLEAR,
-       .intclr_fall_shift = EXYNOS5420_TMU_CLEAR_FALL_INT_SHIFT,
-       .intclr_rise_shift = EXYNOS_TMU_RISE_INT_SHIFT,
-       .intclr_rise_mask = EXYNOS_TMU_RISE_INT_MASK,
-       .intclr_fall_mask = EXYNOS_TMU_FALL_INT_MASK,
        .emul_con = EXYNOS_EMUL_CON,
        .emul_temp_shift = EXYNOS_EMUL_DATA_SHIFT,
        .emul_time_shift = EXYNOS_EMUL_TIME_SHIFT,
-       .emul_time_mask = EXYNOS_EMUL_TIME_MASK,
 };
 
 #define __EXYNOS5420_TMU_DATA  \
@@ -427,6 +379,7 @@ static const struct exynos_tmu_registers exynos5420_tmu_registers = {
        .trigger_type[2] = SW_TRIP, \
        .trigger_type[3] = HW_TRIP, \
        .max_trigger_level = 4, \
+       .non_hw_trigger_levels = 3, \
        .gain = 8, \
        .reference_voltage = 16, \
        .noise_cancel_mode = 4, \
@@ -451,16 +404,15 @@ static const struct exynos_tmu_registers exynos5420_tmu_registers = {
 #define EXYNOS5420_TMU_DATA \
        __EXYNOS5420_TMU_DATA \
        .type = SOC_ARCH_EXYNOS5250, \
-       .features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_TRIM_RELOAD | \
-                       TMU_SUPPORT_FALLING_TRIP | TMU_SUPPORT_READY_STATUS | \
-                       TMU_SUPPORT_EMUL_TIME)
+       .features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_FALLING_TRIP | \
+                       TMU_SUPPORT_READY_STATUS | TMU_SUPPORT_EMUL_TIME)
 
 #define EXYNOS5420_TMU_DATA_SHARED \
        __EXYNOS5420_TMU_DATA \
        .type = SOC_ARCH_EXYNOS5420_TRIMINFO, \
-       .features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_TRIM_RELOAD | \
-                       TMU_SUPPORT_FALLING_TRIP | TMU_SUPPORT_READY_STATUS | \
-                       TMU_SUPPORT_EMUL_TIME | TMU_SUPPORT_ADDRESS_MULTIPLE)
+       .features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_FALLING_TRIP | \
+                       TMU_SUPPORT_READY_STATUS | TMU_SUPPORT_EMUL_TIME | \
+                       TMU_SUPPORT_ADDRESS_MULTIPLE)
 
 struct exynos_tmu_init_data const exynos5420_default_tmu_data = {
        .tmu_data = {
@@ -477,19 +429,10 @@ struct exynos_tmu_init_data const exynos5420_default_tmu_data = {
 #if defined(CONFIG_SOC_EXYNOS5440)
 static const struct exynos_tmu_registers exynos5440_tmu_registers = {
        .triminfo_data = EXYNOS5440_TMU_S0_7_TRIM,
-       .triminfo_25_shift = EXYNOS_TRIMINFO_25_SHIFT,
-       .triminfo_85_shift = EXYNOS_TRIMINFO_85_SHIFT,
        .tmu_ctrl = EXYNOS5440_TMU_S0_7_CTRL,
-       .buf_vref_sel_shift = EXYNOS_TMU_REF_VOLTAGE_SHIFT,
-       .buf_vref_sel_mask = EXYNOS_TMU_REF_VOLTAGE_MASK,
        .therm_trip_mode_shift = EXYNOS_TMU_TRIP_MODE_SHIFT,
        .therm_trip_mode_mask = EXYNOS_TMU_TRIP_MODE_MASK,
        .therm_trip_en_shift = EXYNOS_TMU_THERM_TRIP_EN_SHIFT,
-       .buf_slope_sel_shift = EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT,
-       .buf_slope_sel_mask = EXYNOS_TMU_BUF_SLOPE_SEL_MASK,
-       .calib_mode_shift = EXYNOS_TMU_CALIB_MODE_SHIFT,
-       .calib_mode_mask = EXYNOS_TMU_CALIB_MODE_MASK,
-       .core_en_shift = EXYNOS_TMU_CORE_EN_SHIFT,
        .tmu_status = EXYNOS5440_TMU_S0_7_STATUS,
        .tmu_cur_temp = EXYNOS5440_TMU_S0_7_TEMP,
        .threshold_th0 = EXYNOS5440_TMU_S0_7_TH0,
@@ -504,10 +447,6 @@ static const struct exynos_tmu_registers exynos5440_tmu_registers = {
        .inten_fall0_shift = EXYNOS5440_TMU_INTEN_FALL0_SHIFT,
        .tmu_intstat = EXYNOS5440_TMU_S0_7_IRQ,
        .tmu_intclear = EXYNOS5440_TMU_S0_7_IRQ,
-       .intclr_fall_shift = EXYNOS5440_TMU_CLEAR_FALL_INT_SHIFT,
-       .intclr_rise_shift = EXYNOS5440_TMU_RISE_INT_SHIFT,
-       .intclr_rise_mask = EXYNOS5440_TMU_RISE_INT_MASK,
-       .intclr_fall_mask = EXYNOS5440_TMU_FALL_INT_MASK,
        .tmu_irqstatus = EXYNOS5440_TMU_IRQ_STATUS,
        .emul_con = EXYNOS5440_TMU_S0_7_DEBUG,
        .emul_temp_shift = EXYNOS_EMUL_DATA_SHIFT,
@@ -521,11 +460,11 @@ static const struct exynos_tmu_registers exynos5440_tmu_registers = {
        .trigger_type[0] = SW_TRIP, \
        .trigger_type[4] = HW_TRIP, \
        .max_trigger_level = 5, \
+       .non_hw_trigger_levels = 1, \
        .gain = 5, \
        .reference_voltage = 16, \
        .noise_cancel_mode = 4, \
        .cal_type = TYPE_ONE_POINT_TRIMMING, \
-       .cal_mode = 0, \
        .efuse_value = 0x5b2d, \
        .min_efuse_value = 16, \
        .max_efuse_value = 76, \
index f0979e5..63de598 100644 (file)
 #define EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT 8
 #define EXYNOS_TMU_CORE_EN_SHIFT       0
 
+/* Exynos3250 specific registers */
+#define EXYNOS_TMU_TRIMINFO_CON1       0x10
+
 /* Exynos4210 specific registers */
 #define EXYNOS4210_TMU_REG_THRESHOLD_TEMP      0x44
 #define EXYNOS4210_TMU_REG_TRIG_LEVEL0 0x50
-#define EXYNOS4210_TMU_REG_TRIG_LEVEL1 0x54
-#define EXYNOS4210_TMU_REG_TRIG_LEVEL2 0x58
-#define EXYNOS4210_TMU_REG_TRIG_LEVEL3 0x5C
-#define EXYNOS4210_TMU_REG_PAST_TEMP0  0x60
-#define EXYNOS4210_TMU_REG_PAST_TEMP1  0x64
-#define EXYNOS4210_TMU_REG_PAST_TEMP2  0x68
-#define EXYNOS4210_TMU_REG_PAST_TEMP3  0x6C
-
-#define EXYNOS4210_TMU_TRIG_LEVEL0_MASK        0x1
-#define EXYNOS4210_TMU_TRIG_LEVEL1_MASK        0x10
-#define EXYNOS4210_TMU_TRIG_LEVEL2_MASK        0x100
-#define EXYNOS4210_TMU_TRIG_LEVEL3_MASK        0x1000
-#define EXYNOS4210_TMU_TRIG_LEVEL_MASK 0x1111
-#define EXYNOS4210_TMU_INTCLEAR_VAL    0x1111
-
-/* Exynos5250 and Exynos4412 specific registers */
-#define EXYNOS_TMU_TRIMINFO_CON        0x14
+
+/* Exynos5250, Exynos4412, Exynos3250 specific registers */
+#define EXYNOS_TMU_TRIMINFO_CON2       0x14
 #define EXYNOS_THD_TEMP_RISE           0x50
 #define EXYNOS_THD_TEMP_FALL           0x54
 #define EXYNOS_EMUL_CON                0x80
 
-#define EXYNOS_TRIMINFO_RELOAD_SHIFT   1
+#define EXYNOS_TRIMINFO_RELOAD_ENABLE  1
 #define EXYNOS_TRIMINFO_25_SHIFT       0
 #define EXYNOS_TRIMINFO_85_SHIFT       8
-#define EXYNOS_TMU_RISE_INT_MASK       0x111
-#define EXYNOS_TMU_RISE_INT_SHIFT      0
-#define EXYNOS_TMU_FALL_INT_MASK       0x111
-#define EXYNOS_TMU_CLEAR_RISE_INT      0x111
-#define EXYNOS_TMU_CLEAR_FALL_INT      (0x111 << 12)
-#define EXYNOS_TMU_CLEAR_FALL_INT_SHIFT        12
-#define EXYNOS5420_TMU_CLEAR_FALL_INT_SHIFT    16
-#define EXYNOS5440_TMU_CLEAR_FALL_INT_SHIFT    4
 #define EXYNOS_TMU_TRIP_MODE_SHIFT     13
 #define EXYNOS_TMU_TRIP_MODE_MASK      0x7
 #define EXYNOS_TMU_THERM_TRIP_EN_SHIFT 12
-#define EXYNOS_TMU_CALIB_MODE_SHIFT    4
-#define EXYNOS_TMU_CALIB_MODE_MASK     0x3
 
 #define EXYNOS_TMU_INTEN_RISE0_SHIFT   0
 #define EXYNOS_TMU_INTEN_RISE1_SHIFT   4
 #define EXYNOS_TMU_INTEN_RISE2_SHIFT   8
 #define EXYNOS_TMU_INTEN_RISE3_SHIFT   12
 #define EXYNOS_TMU_INTEN_FALL0_SHIFT   16
-#define EXYNOS_TMU_INTEN_FALL1_SHIFT   20
-#define EXYNOS_TMU_INTEN_FALL2_SHIFT   24
-#define EXYNOS_TMU_INTEN_FALL3_SHIFT   28
 
 #define EXYNOS_EMUL_TIME       0x57F0
 #define EXYNOS_EMUL_TIME_MASK  0xffff
 #define EXYNOS_MAX_TRIGGER_PER_REG     4
 
 /* Exynos5260 specific */
-#define EXYNOS_TMU_REG_CONTROL1                        0x24
 #define EXYNOS5260_TMU_REG_INTEN               0xC0
 #define EXYNOS5260_TMU_REG_INTSTAT             0xC4
 #define EXYNOS5260_TMU_REG_INTCLEAR            0xC8
-#define EXYNOS5260_TMU_CLEAR_RISE_INT          0x1111
-#define EXYNOS5260_TMU_CLEAR_FALL_INT          (0x1111 << 16)
-#define EXYNOS5260_TMU_RISE_INT_MASK           0x1111
-#define EXYNOS5260_TMU_FALL_INT_MASK           0x1111
 #define EXYNOS5260_EMUL_CON                    0x100
 
 /* Exynos4412 specific */
 #define EXYNOS5440_TMU_S0_7_TH0                        0x110
 #define EXYNOS5440_TMU_S0_7_TH1                        0x130
 #define EXYNOS5440_TMU_S0_7_TH2                        0x150
-#define EXYNOS5440_TMU_S0_7_EVTEN              0x1F0
 #define EXYNOS5440_TMU_S0_7_IRQEN              0x210
 #define EXYNOS5440_TMU_S0_7_IRQ                        0x230
 /* exynos5440 common registers */
 #define EXYNOS5440_TMU_IRQ_STATUS              0x000
 #define EXYNOS5440_TMU_PMIN                    0x004
-#define EXYNOS5440_TMU_TEMP                    0x008
 
-#define EXYNOS5440_TMU_RISE_INT_MASK           0xf
-#define EXYNOS5440_TMU_RISE_INT_SHIFT          0
-#define EXYNOS5440_TMU_FALL_INT_MASK           0xf
 #define EXYNOS5440_TMU_INTEN_RISE0_SHIFT       0
 #define EXYNOS5440_TMU_INTEN_RISE1_SHIFT       1
 #define EXYNOS5440_TMU_INTEN_RISE2_SHIFT       2
 #define EXYNOS5440_TMU_INTEN_RISE3_SHIFT       3
 #define EXYNOS5440_TMU_INTEN_FALL0_SHIFT       4
-#define EXYNOS5440_TMU_INTEN_FALL1_SHIFT       5
-#define EXYNOS5440_TMU_INTEN_FALL2_SHIFT       6
-#define EXYNOS5440_TMU_INTEN_FALL3_SHIFT       7
-#define EXYNOS5440_TMU_TH_RISE0_SHIFT          0
-#define EXYNOS5440_TMU_TH_RISE1_SHIFT          8
-#define EXYNOS5440_TMU_TH_RISE2_SHIFT          16
-#define EXYNOS5440_TMU_TH_RISE3_SHIFT          24
 #define EXYNOS5440_TMU_TH_RISE4_SHIFT          24
 #define EXYNOS5440_EFUSE_SWAP_OFFSET           8
 
index 90163b3..d1ec580 100644 (file)
@@ -275,6 +275,7 @@ int st_thermal_unregister(struct platform_device *pdev)
 }
 EXPORT_SYMBOL_GPL(st_thermal_unregister);
 
+#ifdef CONFIG_PM_SLEEP
 static int st_thermal_suspend(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
@@ -305,6 +306,8 @@ static int st_thermal_resume(struct device *dev)
 
        return 0;
 }
+#endif
+
 SIMPLE_DEV_PM_OPS(st_thermal_pm_ops, st_thermal_suspend, st_thermal_resume);
 EXPORT_SYMBOL_GPL(st_thermal_pm_ops);
 
index 9bf10aa..43b9070 100644 (file)
@@ -1575,8 +1575,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
 
        thermal_zone_device_update(tz);
 
-       if (!result)
-               return tz;
+       return tz;
 
 unregister:
        release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
index 89c4cee..2e900a9 100644 (file)
@@ -2413,12 +2413,17 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file,
 
        poll_wait(file, &tty->read_wait, wait);
        poll_wait(file, &tty->write_wait, wait);
+       if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
+               mask |= POLLHUP;
        if (input_available_p(tty, 1))
                mask |= POLLIN | POLLRDNORM;
+       else if (mask & POLLHUP) {
+               tty_flush_to_ldisc(tty);
+               if (input_available_p(tty, 1))
+                       mask |= POLLIN | POLLRDNORM;
+       }
        if (tty->packet && tty->link->ctrl_status)
                mask |= POLLPRI | POLLIN | POLLRDNORM;
-       if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
-               mask |= POLLHUP;
        if (tty_hung_up_p(file))
                mask |= POLLHUP;
        if (!(mask & (POLLHUP | POLLIN | POLLRDNORM))) {
index 8f37d57..de7aae5 100644 (file)
@@ -81,7 +81,7 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios,
                /* Set to highest baudrate supported */
                if (baud >= 1152000)
                        baud = 921600;
-               quot = DIV_ROUND_CLOSEST(port->uartclk, 256 * baud);
+               quot = (port->uartclk / (256 * baud)) + 1;
        }
 
        /*
index 8bc2563..bf35505 100644 (file)
@@ -158,7 +158,7 @@ static int of_platform_serial_probe(struct platform_device *ofdev)
        if (of_find_property(ofdev->dev.of_node, "used-by-rtas", NULL))
                return -EBUSY;
 
-       info = kmalloc(sizeof(*info), GFP_KERNEL);
+       info = kzalloc(sizeof(*info), GFP_KERNEL);
        if (info == NULL)
                return -ENOMEM;
 
@@ -240,32 +240,6 @@ static int of_platform_serial_remove(struct platform_device *ofdev)
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
-static int of_serial_suspend(struct device *dev)
-{
-       struct of_serial_info *info = dev_get_drvdata(dev);
-
-       serial8250_suspend_port(info->line);
-       if (info->clk)
-               clk_disable_unprepare(info->clk);
-
-       return 0;
-}
-
-static int of_serial_resume(struct device *dev)
-{
-       struct of_serial_info *info = dev_get_drvdata(dev);
-
-       if (info->clk)
-               clk_prepare_enable(info->clk);
-
-       serial8250_resume_port(info->line);
-
-       return 0;
-}
-#endif
-static SIMPLE_DEV_PM_OPS(of_serial_pm_ops, of_serial_suspend, of_serial_resume);
-
 /*
  * A few common types, add more as needed.
  */
@@ -297,7 +271,6 @@ static struct platform_driver of_platform_serial_driver = {
                .name = "of_serial",
                .owner = THIS_MODULE,
                .of_match_table = of_platform_serial_table,
-               .pm = &of_serial_pm_ops,
        },
        .probe = of_platform_serial_probe,
        .remove = of_platform_serial_remove,
index df3a8c7..eaeb9a0 100644 (file)
@@ -363,7 +363,7 @@ uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,
                 * The spd_hi, spd_vhi, spd_shi, spd_warp kludge...
                 * Die! Die! Die!
                 */
-               if (baud == 38400)
+               if (try == 0 && baud == 38400)
                        baud = altbaud;
 
                /*
index 16a2c02..0508a1d 100644 (file)
@@ -1709,6 +1709,8 @@ int tty_release(struct inode *inode, struct file *filp)
        int     pty_master, tty_closing, o_tty_closing, do_sleep;
        int     idx;
        char    buf[64];
+       long    timeout = 0;
+       int     once = 1;
 
        if (tty_paranoia_check(tty, inode, __func__))
                return 0;
@@ -1789,11 +1791,18 @@ int tty_release(struct inode *inode, struct file *filp)
                if (!do_sleep)
                        break;
 
-               printk(KERN_WARNING "%s: %s: read/write wait queue active!\n",
-                               __func__, tty_name(tty, buf));
+               if (once) {
+                       once = 0;
+                       printk(KERN_WARNING "%s: %s: read/write wait queue active!\n",
+                              __func__, tty_name(tty, buf));
+               }
                tty_unlock_pair(tty, o_tty);
                mutex_unlock(&tty_mutex);
-               schedule();
+               schedule_timeout_killable(timeout);
+               if (timeout < 120 * HZ)
+                       timeout = 2 * timeout + 1;
+               else
+                       timeout = MAX_SCHEDULE_TIMEOUT;
        }
 
        /*
index 610b720..59b25e0 100644 (file)
@@ -539,6 +539,12 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list)
 
        /* Save original vc_unipagdir_loc in case we allocate a new one */
        p = *vc->vc_uni_pagedir_loc;
+
+       if (!p) {
+               err = -EINVAL;
+
+               goto out_unlock;
+       }
        
        if (p->refcount > 1) {
                int j, k;
@@ -623,6 +629,7 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list)
                set_inverse_transl(vc, p, i); /* Update inverse translations */
        set_inverse_trans_unicode(vc, p);
 
+out_unlock:
        console_unlock();
        return err;
 }
index e934e19..077d58a 100644 (file)
@@ -60,6 +60,9 @@ static struct acm *acm_table[ACM_TTY_MINORS];
 
 static DEFINE_MUTEX(acm_table_lock);
 
+static void acm_tty_set_termios(struct tty_struct *tty,
+                               struct ktermios *termios_old);
+
 /*
  * acm_table accessors
  */
@@ -145,8 +148,15 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value,
 /* devices aren't required to support these requests.
  * the cdc acm descriptor tells whether they do...
  */
-#define acm_set_control(acm, control) \
-       acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
+static inline int acm_set_control(struct acm *acm, int control)
+{
+       if (acm->quirks & QUIRK_CONTROL_LINE_STATE)
+               return -EOPNOTSUPP;
+
+       return acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE,
+                       control, NULL, 0);
+}
+
 #define acm_set_line(acm, line) \
        acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
 #define acm_send_break(acm, ms) \
@@ -554,6 +564,8 @@ static int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
                goto error_submit_urb;
        }
 
+       acm_tty_set_termios(tty, NULL);
+
        /*
         * Unthrottle device in case the TTY was closed while throttled.
         */
@@ -980,11 +992,12 @@ static void acm_tty_set_termios(struct tty_struct *tty,
        /* FIXME: Needs to clear unsupported bits in the termios */
        acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
 
-       if (!newline.dwDTERate) {
+       if (C_BAUD(tty) == B0) {
                newline.dwDTERate = acm->line.dwDTERate;
                newctrl &= ~ACM_CTRL_DTR;
-       } else
+       } else if (termios_old && (termios_old->c_cflag & CBAUD) == B0) {
                newctrl |=  ACM_CTRL_DTR;
+       }
 
        if (newctrl != acm->ctrlout)
                acm_set_control(acm, acm->ctrlout = newctrl);
@@ -1314,6 +1327,7 @@ made_compressed_probe:
        tty_port_init(&acm->port);
        acm->port.ops = &acm_port_ops;
        init_usb_anchor(&acm->delayed);
+       acm->quirks = quirks;
 
        buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
        if (!buf) {
@@ -1681,6 +1695,9 @@ static const struct usb_device_id acm_ids[] = {
        { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
        .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
        },
+       { USB_DEVICE(0x20df, 0x0001), /* Simtec Electronics Entropy Key */
+       .driver_info = QUIRK_CONTROL_LINE_STATE, },
+       { USB_DEVICE(0x2184, 0x001c) }, /* GW Instek AFG-2225 */
        { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
        },
        /* Motorola H24 HSPA module: */
index fc75651..d3251eb 100644 (file)
@@ -121,6 +121,7 @@ struct acm {
        unsigned int throttle_req:1;                    /* throttle requested */
        u8 bInterval;
        struct usb_anchor delayed;                      /* writes queued for a device about to be woken */
+       unsigned long quirks;
 };
 
 #define CDC_DATA_INTERFACE_TYPE        0x0a
@@ -132,3 +133,4 @@ struct acm {
 #define NOT_A_MODEM                    BIT(3)
 #define NO_DATA_INTERFACE              BIT(4)
 #define IGNORE_DEVICE                  BIT(5)
+#define QUIRK_CONTROL_LINE_STATE       BIT(6)
index b84fb14..a6efb41 100644 (file)
@@ -2060,6 +2060,8 @@ int usb_alloc_streams(struct usb_interface *interface,
                return -EINVAL;
        if (dev->speed != USB_SPEED_SUPER)
                return -EINVAL;
+       if (dev->state < USB_STATE_CONFIGURED)
+               return -ENODEV;
 
        for (i = 0; i < num_eps; i++) {
                /* Streams only apply to bulk endpoints. */
index 11e80ac..b649fef 100644 (file)
@@ -4468,9 +4468,6 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        if (retval)
                goto fail;
 
-       if (hcd->usb_phy && !hdev->parent)
-               usb_phy_notify_connect(hcd->usb_phy, udev->speed);
-
        /*
         * Some superspeed devices have finished the link training process
         * and attached to a superspeed hub port, but the device descriptor
@@ -4627,8 +4624,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
 
        /* Disconnect any existing devices under this port */
        if (udev) {
-               if (hcd->usb_phy && !hdev->parent &&
-                               !(portstatus & USB_PORT_STAT_CONNECTION))
+               if (hcd->usb_phy && !hdev->parent)
                        usb_phy_notify_disconnect(hcd->usb_phy, udev->speed);
                usb_disconnect(&port_dev->child);
        }
@@ -4783,6 +4779,10 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
                                port_dev->child = NULL;
                                spin_unlock_irq(&device_state_lock);
                                mutex_unlock(&usb_port_peer_mutex);
+                       } else {
+                               if (hcd->usb_phy && !hdev->parent)
+                                       usb_phy_notify_connect(hcd->usb_phy,
+                                                       udev->speed);
                        }
                }
 
index 5ae883d..96fafed 100644 (file)
@@ -44,6 +44,9 @@ static const struct usb_device_id usb_quirk_list[] = {
        /* Creative SB Audigy 2 NX */
        { USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME },
 
+       /* Microsoft Wireless Laser Mouse 6000 Receiver */
+       { USB_DEVICE(0x045e, 0x00e1), .driver_info = USB_QUIRK_RESET_RESUME },
+
        /* Microsoft LifeCam-VX700 v2.0 */
        { USB_DEVICE(0x045e, 0x0770), .driver_info = USB_QUIRK_RESET_RESUME },
 
@@ -97,6 +100,12 @@ static const struct usb_device_id usb_quirk_list[] = {
        { USB_DEVICE(0x04f3, 0x0089), .driver_info =
                        USB_QUIRK_DEVICE_QUALIFIER },
 
+       { USB_DEVICE(0x04f3, 0x009b), .driver_info =
+                       USB_QUIRK_DEVICE_QUALIFIER },
+
+       { USB_DEVICE(0x04f3, 0x016f), .driver_info =
+                       USB_QUIRK_DEVICE_QUALIFIER },
+
        /* Roland SC-8820 */
        { USB_DEVICE(0x0582, 0x0007), .driver_info = USB_QUIRK_RESET_RESUME },
 
index eee8709..8b5c079 100644 (file)
@@ -2327,7 +2327,7 @@ irq_retry:
 
                u32 usb_status = readl(hsotg->regs + GOTGCTL);
 
-               dev_info(hsotg->dev, "%s: USBRst\n", __func__);
+               dev_dbg(hsotg->dev, "%s: USBRst\n", __func__);
                dev_dbg(hsotg->dev, "GNPTXSTS=%08x\n",
                        readl(hsotg->regs + GNPTXSTS));
 
index 711b230..df38e7e 100644 (file)
@@ -791,6 +791,10 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
 
        trb = dwc->ep0_trb;
 
+       r = next_request(&ep0->request_list);
+       if (!r)
+               return;
+
        status = DWC3_TRB_SIZE_TRBSTS(trb->size);
        if (status == DWC3_TRBSTS_SETUP_PENDING) {
                dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
@@ -801,10 +805,6 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
                return;
        }
 
-       r = next_request(&ep0->request_list);
-       if (!r)
-               return;
-
        ur = &r->request;
 
        length = trb->size & DWC3_TRB_SIZE_MASK;
index a8a30b1..a3ca137 100644 (file)
@@ -234,7 +234,7 @@ config USB_EHCI_SH
 
 config USB_EHCI_EXYNOS
        tristate "EHCI support for Samsung S5P/EXYNOS SoC Series"
-       depends on PLAT_S5P || ARCH_EXYNOS
+       depends on ARCH_S5PV210 || ARCH_EXYNOS
        help
        Enable support for the Samsung Exynos SOC's on-chip EHCI controller.
 
@@ -550,7 +550,7 @@ config USB_OHCI_SH
 
 config USB_OHCI_EXYNOS
        tristate "OHCI support for Samsung S5P/EXYNOS SoC Series"
-       depends on PLAT_S5P || ARCH_EXYNOS
+       depends on ARCH_S5PV210 || ARCH_EXYNOS
        help
         Enable support for the Samsung Exynos SOC's on-chip OHCI controller.
 
index d0d8fad..1db0626 100644 (file)
@@ -607,7 +607,7 @@ found:
        wa->wa_descr = wa_descr = (struct usb_wa_descriptor *) hdr;
        if (le16_to_cpu(wa_descr->bcdWAVersion) > 0x0100)
                dev_warn(dev, "Wire Adapter v%d.%d newer than groked v1.0\n",
-                        le16_to_cpu(wa_descr->bcdWAVersion) & 0xff00 >> 8,
+                        (le16_to_cpu(wa_descr->bcdWAVersion) & 0xff00) >> 8,
                         le16_to_cpu(wa_descr->bcdWAVersion) & 0x00ff);
        result = 0;
 error:
index 696160d..388cfd8 100644 (file)
@@ -22,7 +22,6 @@
 
 
 #include <linux/slab.h>
-#include <linux/device.h>
 #include <asm/unaligned.h>
 
 #include "xhci.h"
@@ -1149,9 +1148,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
                 * including the USB 3.0 roothub, but only if CONFIG_PM_RUNTIME
                 * is enabled, so also enable remote wake here.
                 */
-               if (hcd->self.root_hub->do_remote_wakeup
-                               && device_may_wakeup(hcd->self.controller)) {
-
+               if (hcd->self.root_hub->do_remote_wakeup) {
                        if (t1 & PORT_CONNECT) {
                                t2 |= PORT_WKOC_E | PORT_WKDISC_E;
                                t2 &= ~PORT_WKCONN_E;
index 280dde9..142b601 100644 (file)
@@ -127,20 +127,6 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
                xhci->quirks |= XHCI_SPURIOUS_REBOOT;
                xhci->quirks |= XHCI_AVOID_BEI;
        }
-       if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
-           (pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI ||
-            pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI)) {
-               /* Workaround for occasional spurious wakeups from S5 (or
-                * any other sleep) on Haswell machines with LPT and LPT-LP
-                * with the new Intel BIOS
-                */
-               /* Limit the quirk to only known vendors, as this triggers
-                * yet another BIOS bug on some other machines
-                * https://bugzilla.kernel.org/show_bug.cgi?id=66171
-                */
-               if (pdev->subsystem_vendor == PCI_VENDOR_ID_HP)
-                       xhci->quirks |= XHCI_SPURIOUS_WAKEUP;
-       }
        if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
                pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI) {
                xhci->quirks |= XHCI_SPURIOUS_REBOOT;
@@ -162,6 +148,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
                        pdev->device == 0x3432)
                xhci->quirks |= XHCI_BROKEN_STREAMS;
 
+       if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA &&
+                       pdev->device == 0x1042)
+               xhci->quirks |= XHCI_BROKEN_STREAMS;
+
        if (xhci->quirks & XHCI_RESET_ON_RESUME)
                xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
                                "QUIRK: Resetting on resume");
@@ -291,7 +281,7 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
        if (xhci->quirks & XHCI_COMP_MODE_QUIRK)
                pdev->no_d3cold = true;
 
-       return xhci_suspend(xhci);
+       return xhci_suspend(xhci, do_wakeup);
 }
 
 static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
index 3d78b0c..646300c 100644 (file)
@@ -204,7 +204,15 @@ static int xhci_plat_suspend(struct device *dev)
        struct usb_hcd  *hcd = dev_get_drvdata(dev);
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
 
-       return xhci_suspend(xhci);
+       /*
+        * xhci_suspend() needs `do_wakeup` to know whether host is allowed
+        * to do wakeup during suspend. Since xhci_plat_suspend is currently
+        * only designed for system suspend, device_may_wakeup() is enough
+        * to dertermine whether host is allowed to do wakeup. Need to
+        * reconsider this when xhci_plat_suspend enlarges its scope, e.g.,
+        * also applies to runtime suspend.
+        */
+       return xhci_suspend(xhci, device_may_wakeup(dev));
 }
 
 static int xhci_plat_resume(struct device *dev)
index bc6fcbc..06433ae 100644 (file)
@@ -1067,9 +1067,8 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id,
                                false);
                xhci_ring_cmd_db(xhci);
        } else {
-               /* Clear our internal halted state and restart the ring(s) */
+               /* Clear our internal halted state */
                xhci->devs[slot_id]->eps[ep_index].ep_state &= ~EP_HALTED;
-               ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
        }
 }
 
@@ -1823,22 +1822,13 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
                ep->stopped_td = td;
                return 0;
        } else {
-               if (trb_comp_code == COMP_STALL) {
-                       /* The transfer is completed from the driver's
-                        * perspective, but we need to issue a set dequeue
-                        * command for this stalled endpoint to move the dequeue
-                        * pointer past the TD.  We can't do that here because
-                        * the halt condition must be cleared first.  Let the
-                        * USB class driver clear the stall later.
-                        */
-                       ep->stopped_td = td;
-                       ep->stopped_stream = ep_ring->stream_id;
-               } else if (xhci_requires_manual_halt_cleanup(xhci,
-                                       ep_ctx, trb_comp_code)) {
-                       /* Other types of errors halt the endpoint, but the
-                        * class driver doesn't call usb_reset_endpoint() unless
-                        * the error is -EPIPE.  Clear the halted status in the
-                        * xHCI hardware manually.
+               if (trb_comp_code == COMP_STALL ||
+                   xhci_requires_manual_halt_cleanup(xhci, ep_ctx,
+                                                     trb_comp_code)) {
+                       /* Issue a reset endpoint command to clear the host side
+                        * halt, followed by a set dequeue command to move the
+                        * dequeue pointer past the TD.
+                        * The class driver clears the device side halt later.
                         */
                        xhci_cleanup_halted_endpoint(xhci,
                                        slot_id, ep_index, ep_ring->stream_id,
@@ -1958,9 +1948,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
                else
                        td->urb->actual_length = 0;
 
-               xhci_cleanup_halted_endpoint(xhci,
-                       slot_id, ep_index, 0, td, event_trb);
-               return finish_td(xhci, td, event_trb, event, ep, status, true);
+               return finish_td(xhci, td, event_trb, event, ep, status, false);
        }
        /*
         * Did we transfer any data, despite the errors that might have
@@ -2519,17 +2507,8 @@ cleanup:
                if (ret) {
                        urb = td->urb;
                        urb_priv = urb->hcpriv;
-                       /* Leave the TD around for the reset endpoint function
-                        * to use(but only if it's not a control endpoint,
-                        * since we already queued the Set TR dequeue pointer
-                        * command for stalled control endpoints).
-                        */
-                       if (usb_endpoint_xfer_control(&urb->ep->desc) ||
-                               (trb_comp_code != COMP_STALL &&
-                                       trb_comp_code != COMP_BABBLE))
-                               xhci_urb_free_priv(xhci, urb_priv);
-                       else
-                               kfree(urb_priv);
+
+                       xhci_urb_free_priv(xhci, urb_priv);
 
                        usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb);
                        if ((urb->actual_length != urb->transfer_buffer_length &&
index 2a5d45b..033b46c 100644 (file)
@@ -35,6 +35,8 @@
 #define DRIVER_AUTHOR "Sarah Sharp"
 #define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver"
 
+#define        PORT_WAKE_BITS  (PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E)
+
 /* Some 0.95 hardware can't handle the chain bit on a Link TRB being cleared */
 static int link_quirk;
 module_param(link_quirk, int, S_IRUGO | S_IWUSR);
@@ -851,13 +853,47 @@ static void xhci_clear_command_ring(struct xhci_hcd *xhci)
        xhci_set_cmd_ring_deq(xhci);
 }
 
+static void xhci_disable_port_wake_on_bits(struct xhci_hcd *xhci)
+{
+       int port_index;
+       __le32 __iomem **port_array;
+       unsigned long flags;
+       u32 t1, t2;
+
+       spin_lock_irqsave(&xhci->lock, flags);
+
+       /* disble usb3 ports Wake bits*/
+       port_index = xhci->num_usb3_ports;
+       port_array = xhci->usb3_ports;
+       while (port_index--) {
+               t1 = readl(port_array[port_index]);
+               t1 = xhci_port_state_to_neutral(t1);
+               t2 = t1 & ~PORT_WAKE_BITS;
+               if (t1 != t2)
+                       writel(t2, port_array[port_index]);
+       }
+
+       /* disble usb2 ports Wake bits*/
+       port_index = xhci->num_usb2_ports;
+       port_array = xhci->usb2_ports;
+       while (port_index--) {
+               t1 = readl(port_array[port_index]);
+               t1 = xhci_port_state_to_neutral(t1);
+               t2 = t1 & ~PORT_WAKE_BITS;
+               if (t1 != t2)
+                       writel(t2, port_array[port_index]);
+       }
+
+       spin_unlock_irqrestore(&xhci->lock, flags);
+}
+
 /*
  * Stop HC (not bus-specific)
  *
  * This is called when the machine transition into S3/S4 mode.
  *
  */
-int xhci_suspend(struct xhci_hcd *xhci)
+int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
 {
        int                     rc = 0;
        unsigned int            delay = XHCI_MAX_HALT_USEC;
@@ -868,6 +904,10 @@ int xhci_suspend(struct xhci_hcd *xhci)
                        xhci->shared_hcd->state != HC_STATE_SUSPENDED)
                return -EINVAL;
 
+       /* Clear root port wake on bits if wakeup not allowed. */
+       if (!do_wakeup)
+               xhci_disable_port_wake_on_bits(xhci);
+
        /* Don't poll the roothubs on bus suspend. */
        xhci_dbg(xhci, "%s: stopping port polling.\n", __func__);
        clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
@@ -2912,68 +2952,33 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci,
        }
 }
 
-/* Deal with stalled endpoints.  The core should have sent the control message
- * to clear the halt condition.  However, we need to make the xHCI hardware
- * reset its sequence number, since a device will expect a sequence number of
- * zero after the halt condition is cleared.
+/* Called when clearing halted device. The core should have sent the control
+ * message to clear the device halt condition. The host side of the halt should
+ * already be cleared with a reset endpoint command issued when the STALL tx
+ * event was received.
+ *
  * Context: in_interrupt
  */
+
 void xhci_endpoint_reset(struct usb_hcd *hcd,
                struct usb_host_endpoint *ep)
 {
        struct xhci_hcd *xhci;
-       struct usb_device *udev;
-       unsigned int ep_index;
-       unsigned long flags;
-       int ret;
-       struct xhci_virt_ep *virt_ep;
-       struct xhci_command *command;
 
        xhci = hcd_to_xhci(hcd);
-       udev = (struct usb_device *) ep->hcpriv;
-       /* Called with a root hub endpoint (or an endpoint that wasn't added
-        * with xhci_add_endpoint()
-        */
-       if (!ep->hcpriv)
-               return;
-       ep_index = xhci_get_endpoint_index(&ep->desc);
-       virt_ep = &xhci->devs[udev->slot_id]->eps[ep_index];
-       if (!virt_ep->stopped_td) {
-               xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep,
-                       "Endpoint 0x%x not halted, refusing to reset.",
-                       ep->desc.bEndpointAddress);
-               return;
-       }
-       if (usb_endpoint_xfer_control(&ep->desc)) {
-               xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep,
-                               "Control endpoint stall already handled.");
-               return;
-       }
 
-       command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC);
-       if (!command)
-               return;
-
-       xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep,
-                       "Queueing reset endpoint command");
-       spin_lock_irqsave(&xhci->lock, flags);
-       ret = xhci_queue_reset_ep(xhci, command, udev->slot_id, ep_index);
        /*
-        * Can't change the ring dequeue pointer until it's transitioned to the
-        * stopped state, which is only upon a successful reset endpoint
-        * command.  Better hope that last command worked!
+        * We might need to implement the config ep cmd in xhci 4.8.1 note:
+        * The Reset Endpoint Command may only be issued to endpoints in the
+        * Halted state. If software wishes reset the Data Toggle or Sequence
+        * Number of an endpoint that isn't in the Halted state, then software
+        * may issue a Configure Endpoint Command with the Drop and Add bits set
+        * for the target endpoint. that is in the Stopped state.
         */
-       if (!ret) {
-               xhci_cleanup_stalled_ring(xhci, udev, ep_index);
-               kfree(virt_ep->stopped_td);
-               xhci_ring_cmd_db(xhci);
-       }
-       virt_ep->stopped_td = NULL;
-       virt_ep->stopped_stream = 0;
-       spin_unlock_irqrestore(&xhci->lock, flags);
 
-       if (ret)
-               xhci_warn(xhci, "FIXME allocate a new ring segment\n");
+       /* For now just print debug to follow the situation */
+       xhci_dbg(xhci, "Endpoint 0x%x ep reset callback called\n",
+                ep->desc.bEndpointAddress);
 }
 
 static int xhci_check_streams_endpoint(struct xhci_hcd *xhci,
index df76d64..d745715 100644 (file)
@@ -1746,7 +1746,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks);
 void xhci_init_driver(struct hc_driver *drv, int (*setup_fn)(struct usb_hcd *));
 
 #ifdef CONFIG_PM
-int xhci_suspend(struct xhci_hcd *xhci);
+int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup);
 int xhci_resume(struct xhci_hcd *xhci, bool hibernated);
 #else
 #define        xhci_suspend    NULL
index cfd009d..6c4eb3c 100644 (file)
@@ -120,6 +120,7 @@ static const struct usb_device_id id_table[] = {
        { USB_DEVICE(0x10C4, 0x85F8) }, /* Virtenio Preon32 */
        { USB_DEVICE(0x10C4, 0x8664) }, /* AC-Services CAN-IF */
        { USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */
+       { USB_DEVICE(0x10C4, 0x8875) }, /* CEL MeshConnect USB Stick */
        { USB_DEVICE(0x10C4, 0x88A4) }, /* MMB Networks ZigBee USB Device */
        { USB_DEVICE(0x10C4, 0x88A5) }, /* Planet Innovation Ingeni ZigBee USB Device */
        { USB_DEVICE(0x10C4, 0x8946) }, /* Ketra N1 Wireless Interface */
index 0dad8ce..1ebb351 100644 (file)
@@ -470,6 +470,39 @@ static const struct usb_device_id id_table_combined[] = {
        { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FD_PID) },
        { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FE_PID) },
        { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FF_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_4701_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9300_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9301_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9302_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9303_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9304_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9305_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9306_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9307_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9308_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9309_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930A_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930B_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930C_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930D_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930E_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930F_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9310_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9311_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9312_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9313_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9314_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9315_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9316_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9317_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9318_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9319_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931A_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931B_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931C_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931D_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931E_PID) },
+       { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931F_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_PERLE_ULTRAPORT_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_PIEGROUP_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_TNC_X_PID) },
index 6786b70..e52409c 100644 (file)
 #define BAYER_CONTOUR_CABLE_PID        0x6001
 
 /*
- * The following are the values for the Matrix Orbital FTDI Range
- * Anything in this range will use an FT232RL.
+ * Matrix Orbital Intelligent USB displays.
+ * http://www.matrixorbital.com
  */
 #define MTXORB_VID                     0x1B3D
 #define MTXORB_FTDI_RANGE_0100_PID     0x0100
 #define MTXORB_FTDI_RANGE_01FD_PID     0x01FD
 #define MTXORB_FTDI_RANGE_01FE_PID     0x01FE
 #define MTXORB_FTDI_RANGE_01FF_PID     0x01FF
-
-
+#define MTXORB_FTDI_RANGE_4701_PID     0x4701
+#define MTXORB_FTDI_RANGE_9300_PID     0x9300
+#define MTXORB_FTDI_RANGE_9301_PID     0x9301
+#define MTXORB_FTDI_RANGE_9302_PID     0x9302
+#define MTXORB_FTDI_RANGE_9303_PID     0x9303
+#define MTXORB_FTDI_RANGE_9304_PID     0x9304
+#define MTXORB_FTDI_RANGE_9305_PID     0x9305
+#define MTXORB_FTDI_RANGE_9306_PID     0x9306
+#define MTXORB_FTDI_RANGE_9307_PID     0x9307
+#define MTXORB_FTDI_RANGE_9308_PID     0x9308
+#define MTXORB_FTDI_RANGE_9309_PID     0x9309
+#define MTXORB_FTDI_RANGE_930A_PID     0x930A
+#define MTXORB_FTDI_RANGE_930B_PID     0x930B
+#define MTXORB_FTDI_RANGE_930C_PID     0x930C
+#define MTXORB_FTDI_RANGE_930D_PID     0x930D
+#define MTXORB_FTDI_RANGE_930E_PID     0x930E
+#define MTXORB_FTDI_RANGE_930F_PID     0x930F
+#define MTXORB_FTDI_RANGE_9310_PID     0x9310
+#define MTXORB_FTDI_RANGE_9311_PID     0x9311
+#define MTXORB_FTDI_RANGE_9312_PID     0x9312
+#define MTXORB_FTDI_RANGE_9313_PID     0x9313
+#define MTXORB_FTDI_RANGE_9314_PID     0x9314
+#define MTXORB_FTDI_RANGE_9315_PID     0x9315
+#define MTXORB_FTDI_RANGE_9316_PID     0x9316
+#define MTXORB_FTDI_RANGE_9317_PID     0x9317
+#define MTXORB_FTDI_RANGE_9318_PID     0x9318
+#define MTXORB_FTDI_RANGE_9319_PID     0x9319
+#define MTXORB_FTDI_RANGE_931A_PID     0x931A
+#define MTXORB_FTDI_RANGE_931B_PID     0x931B
+#define MTXORB_FTDI_RANGE_931C_PID     0x931C
+#define MTXORB_FTDI_RANGE_931D_PID     0x931D
+#define MTXORB_FTDI_RANGE_931E_PID     0x931E
+#define MTXORB_FTDI_RANGE_931F_PID     0x931F
 
 /*
  * The Mobility Lab (TML)
index 93cb7ce..077c714 100644 (file)
@@ -311,24 +311,30 @@ static void       usa26_indat_callback(struct urb *urb)
                if ((data[0] & 0x80) == 0) {
                        /* no errors on individual bytes, only
                           possible overrun err */
-                       if (data[0] & RXERROR_OVERRUN)
-                               err = TTY_OVERRUN;
-                       else
-                               err = 0;
+                       if (data[0] & RXERROR_OVERRUN) {
+                               tty_insert_flip_char(&port->port, 0,
+                                                               TTY_OVERRUN);
+                       }
                        for (i = 1; i < urb->actual_length ; ++i)
-                               tty_insert_flip_char(&port->port, data[i], err);
+                               tty_insert_flip_char(&port->port, data[i],
+                                                               TTY_NORMAL);
                } else {
                        /* some bytes had errors, every byte has status */
                        dev_dbg(&port->dev, "%s - RX error!!!!\n", __func__);
                        for (i = 0; i + 1 < urb->actual_length; i += 2) {
-                               int stat = data[i], flag = 0;
-                               if (stat & RXERROR_OVERRUN)
-                                       flag |= TTY_OVERRUN;
-                               if (stat & RXERROR_FRAMING)
-                                       flag |= TTY_FRAME;
-                               if (stat & RXERROR_PARITY)
-                                       flag |= TTY_PARITY;
+                               int stat = data[i];
+                               int flag = TTY_NORMAL;
+
+                               if (stat & RXERROR_OVERRUN) {
+                                       tty_insert_flip_char(&port->port, 0,
+                                                               TTY_OVERRUN);
+                               }
                                /* XXX should handle break (0x10) */
+                               if (stat & RXERROR_PARITY)
+                                       flag = TTY_PARITY;
+                               else if (stat & RXERROR_FRAMING)
+                                       flag = TTY_FRAME;
+
                                tty_insert_flip_char(&port->port, data[i+1],
                                                flag);
                        }
@@ -649,14 +655,19 @@ static void       usa49_indat_callback(struct urb *urb)
                } else {
                        /* some bytes had errors, every byte has status */
                        for (i = 0; i + 1 < urb->actual_length; i += 2) {
-                               int stat = data[i], flag = 0;
-                               if (stat & RXERROR_OVERRUN)
-                                       flag |= TTY_OVERRUN;
-                               if (stat & RXERROR_FRAMING)
-                                       flag |= TTY_FRAME;
-                               if (stat & RXERROR_PARITY)
-                                       flag |= TTY_PARITY;
+                               int stat = data[i];
+                               int flag = TTY_NORMAL;
+
+                               if (stat & RXERROR_OVERRUN) {
+                                       tty_insert_flip_char(&port->port, 0,
+                                                               TTY_OVERRUN);
+                               }
                                /* XXX should handle break (0x10) */
+                               if (stat & RXERROR_PARITY)
+                                       flag = TTY_PARITY;
+                               else if (stat & RXERROR_FRAMING)
+                                       flag = TTY_FRAME;
+
                                tty_insert_flip_char(&port->port, data[i+1],
                                                flag);
                        }
@@ -713,15 +724,19 @@ static void usa49wg_indat_callback(struct urb *urb)
                         */
                        for (x = 0; x + 1 < len &&
                                    i + 1 < urb->actual_length; x += 2) {
-                               int stat = data[i], flag = 0;
+                               int stat = data[i];
+                               int flag = TTY_NORMAL;
 
-                               if (stat & RXERROR_OVERRUN)
-                                       flag |= TTY_OVERRUN;
-                               if (stat & RXERROR_FRAMING)
-                                       flag |= TTY_FRAME;
-                               if (stat & RXERROR_PARITY)
-                                       flag |= TTY_PARITY;
+                               if (stat & RXERROR_OVERRUN) {
+                                       tty_insert_flip_char(&port->port, 0,
+                                                               TTY_OVERRUN);
+                               }
                                /* XXX should handle break (0x10) */
+                               if (stat & RXERROR_PARITY)
+                                       flag = TTY_PARITY;
+                               else if (stat & RXERROR_FRAMING)
+                                       flag = TTY_FRAME;
+
                                tty_insert_flip_char(&port->port, data[i+1],
                                                     flag);
                                i += 2;
@@ -773,25 +788,31 @@ static void usa90_indat_callback(struct urb *urb)
                        if ((data[0] & 0x80) == 0) {
                                /* no errors on individual bytes, only
                                   possible overrun err*/
-                               if (data[0] & RXERROR_OVERRUN)
-                                       err = TTY_OVERRUN;
-                               else
-                                       err = 0;
+                               if (data[0] & RXERROR_OVERRUN) {
+                                       tty_insert_flip_char(&port->port, 0,
+                                                               TTY_OVERRUN);
+                               }
                                for (i = 1; i < urb->actual_length ; ++i)
                                        tty_insert_flip_char(&port->port,
-                                                       data[i], err);
+                                                       data[i], TTY_NORMAL);
                        }  else {
                        /* some bytes had errors, every byte has status */
                                dev_dbg(&port->dev, "%s - RX error!!!!\n", __func__);
                                for (i = 0; i + 1 < urb->actual_length; i += 2) {
-                                       int stat = data[i], flag = 0;
-                                       if (stat & RXERROR_OVERRUN)
-                                               flag |= TTY_OVERRUN;
-                                       if (stat & RXERROR_FRAMING)
-                                               flag |= TTY_FRAME;
-                                       if (stat & RXERROR_PARITY)
-                                               flag |= TTY_PARITY;
+                                       int stat = data[i];
+                                       int flag = TTY_NORMAL;
+
+                                       if (stat & RXERROR_OVERRUN) {
+                                               tty_insert_flip_char(
+                                                               &port->port, 0,
+                                                               TTY_OVERRUN);
+                                       }
                                        /* XXX should handle break (0x10) */
+                                       if (stat & RXERROR_PARITY)
+                                               flag = TTY_PARITY;
+                                       else if (stat & RXERROR_FRAMING)
+                                               flag = TTY_FRAME;
+
                                        tty_insert_flip_char(&port->port,
                                                        data[i+1], flag);
                                }
index 3d2bd65..02c420a 100644 (file)
@@ -335,7 +335,8 @@ static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port,
                        port->interrupt_out_urb->transfer_buffer_length = length;
 
                        priv->cur_pos = priv->cur_pos + length;
-                       result = usb_submit_urb(port->interrupt_out_urb, GFP_NOIO);
+                       result = usb_submit_urb(port->interrupt_out_urb,
+                                       GFP_ATOMIC);
                        dev_dbg(&port->dev, "%s - Send write URB returns: %i\n", __func__, result);
                        todo = priv->filled - priv->cur_pos;
 
@@ -350,7 +351,7 @@ static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port,
                if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
                        priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) {
                        result = usb_submit_urb(port->interrupt_in_urb,
-                                                               GFP_NOIO);
+                                       GFP_ATOMIC);
                        dev_dbg(&port->dev, "%s - Send read URB returns: %i\n", __func__, result);
                }
        }
index 4856fb7..4b7bfb3 100644 (file)
@@ -215,7 +215,7 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port,
 
        /* The connected devices do not have a bulk write endpoint,
         * to transmit data to de barcode device the control endpoint is used */
-       dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
+       dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
        if (!dr) {
                count = -ENOMEM;
                goto error_no_dr;
index a7fe664..70a098d 100644 (file)
@@ -490,10 +490,9 @@ static void ssu100_update_lsr(struct usb_serial_port *port, u8 lsr,
                        if (*tty_flag == TTY_NORMAL)
                                *tty_flag = TTY_FRAME;
                }
-               if (lsr & UART_LSR_OE){
+               if (lsr & UART_LSR_OE) {
                        port->icount.overrun++;
-                       if (*tty_flag == TTY_NORMAL)
-                               *tty_flag = TTY_OVERRUN;
+                       tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
                }
        }
 
@@ -511,12 +510,8 @@ static void ssu100_process_read_urb(struct urb *urb)
        if ((len >= 4) &&
            (packet[0] == 0x1b) && (packet[1] == 0x1b) &&
            ((packet[2] == 0x00) || (packet[2] == 0x01))) {
-               if (packet[2] == 0x00) {
+               if (packet[2] == 0x00)
                        ssu100_update_lsr(port, packet[3], &flag);
-                       if (flag == TTY_OVERRUN)
-                               tty_insert_flip_char(&port->port, 0,
-                                               TTY_OVERRUN);
-               }
                if (packet[2] == 0x01)
                        ssu100_update_msr(port, packet[3]);
 
index 4bc2fc9..73f125e 100644 (file)
@@ -52,7 +52,7 @@ int usb_stor_euscsi_init(struct us_data *us)
        us->iobuf[0] = 0x1;
        result = usb_stor_control_msg(us, us->send_ctrl_pipe,
                        0x0C, USB_RECIP_INTERFACE | USB_TYPE_VENDOR,
-                       0x01, 0x0, us->iobuf, 0x1, USB_CTRL_SET_TIMEOUT);
+                       0x01, 0x0, us->iobuf, 0x1, 5 * HZ);
        usb_stor_dbg(us, "-- result is %d\n", result);
 
        return 0;
@@ -100,7 +100,7 @@ int usb_stor_huawei_e220_init(struct us_data *us)
        result = usb_stor_control_msg(us, us->send_ctrl_pipe,
                                      USB_REQ_SET_FEATURE,
                                      USB_TYPE_STANDARD | USB_RECIP_DEVICE,
-                                     0x01, 0x0, NULL, 0x0, 1000);
+                                     0x01, 0x0, NULL, 0x0, 1 * HZ);
        usb_stor_dbg(us, "Huawei mode set result is %d\n", result);
        return 0;
 }
index 8591d89..27e4a58 100644 (file)
@@ -626,6 +626,7 @@ static int config_autodelink_after_power_on(struct us_data *us)
        return 0;
 }
 
+#ifdef CONFIG_PM
 static int config_autodelink_before_power_down(struct us_data *us)
 {
        struct rts51x_chip *chip = (struct rts51x_chip *)(us->extra);
@@ -716,6 +717,7 @@ static void fw5895_init(struct us_data *us)
                }
        }
 }
+#endif
 
 #ifdef CONFIG_REALTEK_AUTOPM
 static void fw5895_set_mmc_wp(struct us_data *us)
index 22c7d43..b1d815e 100644 (file)
@@ -1118,6 +1118,31 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
                 */
                if (result == USB_STOR_XFER_LONG)
                        fake_sense = 1;
+
+               /*
+                * Sometimes a device will mistakenly skip the data phase
+                * and go directly to the status phase without sending a
+                * zero-length packet.  If we get a 13-byte response here,
+                * check whether it really is a CSW.
+                */
+               if (result == USB_STOR_XFER_SHORT &&
+                               srb->sc_data_direction == DMA_FROM_DEVICE &&
+                               transfer_length - scsi_get_resid(srb) ==
+                                       US_BULK_CS_WRAP_LEN) {
+                       struct scatterlist *sg = NULL;
+                       unsigned int offset = 0;
+
+                       if (usb_stor_access_xfer_buf((unsigned char *) bcs,
+                                       US_BULK_CS_WRAP_LEN, srb, &sg,
+                                       &offset, FROM_XFER_BUF) ==
+                                               US_BULK_CS_WRAP_LEN &&
+                                       bcs->Signature ==
+                                               cpu_to_le32(US_BULK_CS_SIGN)) {
+                               usb_stor_dbg(us, "Device skipped data phase\n");
+                               scsi_set_resid(srb, transfer_length);
+                               goto skipped_data_phase;
+                       }
+               }
        }
 
        /* See flow chart on pg 15 of the Bulk Only Transport spec for
@@ -1153,6 +1178,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
        if (result != USB_STOR_XFER_GOOD)
                return USB_STOR_TRANSPORT_ERROR;
 
+ skipped_data_phase:
        /* check bulk status */
        residue = le32_to_cpu(bcs->Residue);
        usb_stor_dbg(us, "Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n",
index 8511b54..18a283d 100644 (file)
@@ -54,6 +54,20 @@ UNUSUAL_DEV(0x0bc2, 0x3312, 0x0000, 0x9999,
                USB_SC_DEVICE, USB_PR_DEVICE, NULL,
                US_FL_NO_ATA_1X),
 
+/* Reported-by: Hans de Goede <hdegoede@redhat.com> */
+UNUSUAL_DEV(0x0bc2, 0x3320, 0x0000, 0x9999,
+               "Seagate",
+               "Expansion Desk",
+               USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+               US_FL_NO_ATA_1X),
+
+/* Reported-by: Bogdan Mihalcea <bogdan.mihalcea@infim.ro> */
+UNUSUAL_DEV(0x0bc2, 0xa003, 0x0000, 0x9999,
+               "Seagate",
+               "Backup Plus",
+               USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+               US_FL_NO_ATA_1X),
+
 /* https://bbs.archlinux.org/viewtopic.php?id=183190 */
 UNUSUAL_DEV(0x0bc2, 0xab20, 0x0000, 0x9999,
                "Seagate",
@@ -61,6 +75,13 @@ UNUSUAL_DEV(0x0bc2, 0xab20, 0x0000, 0x9999,
                USB_SC_DEVICE, USB_PR_DEVICE, NULL,
                US_FL_NO_ATA_1X),
 
+/* https://bbs.archlinux.org/viewtopic.php?id=183190 */
+UNUSUAL_DEV(0x0bc2, 0xab21, 0x0000, 0x9999,
+               "Seagate",
+               "Backup+ BK",
+               USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+               US_FL_NO_ATA_1X),
+
 /* Reported-by: Claudio Bizzarri <claudio.bizzarri@gmail.com> */
 UNUSUAL_DEV(0x152d, 0x0567, 0x0000, 0x9999,
                "JMicron",
@@ -75,3 +96,17 @@ UNUSUAL_DEV(0x174c, 0x5106, 0x0000, 0x9999,
                "ASM1051",
                USB_SC_DEVICE, USB_PR_DEVICE, NULL,
                US_FL_IGNORE_UAS),
+
+/* Reported-by: Hans de Goede <hdegoede@redhat.com> */
+UNUSUAL_DEV(0x2109, 0x0711, 0x0000, 0x9999,
+               "VIA",
+               "VL711",
+               USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+               US_FL_NO_ATA_1X),
+
+/* Reported-by: Hans de Goede <hdegoede@redhat.com> */
+UNUSUAL_DEV(0x4971, 0x1012, 0x0000, 0x9999,
+               "Hitachi",
+               "External HDD",
+               USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+               US_FL_IGNORE_UAS),
index 69906ca..a17f118 100644 (file)
@@ -1312,6 +1312,7 @@ static int
 vhost_scsi_set_endpoint(struct vhost_scsi *vs,
                        struct vhost_scsi_target *t)
 {
+       struct se_portal_group *se_tpg;
        struct tcm_vhost_tport *tv_tport;
        struct tcm_vhost_tpg *tpg;
        struct tcm_vhost_tpg **vs_tpg;
@@ -1359,6 +1360,21 @@ vhost_scsi_set_endpoint(struct vhost_scsi *vs,
                                ret = -EEXIST;
                                goto out;
                        }
+                       /*
+                        * In order to ensure individual vhost-scsi configfs
+                        * groups cannot be removed while in use by vhost ioctl,
+                        * go ahead and take an explicit se_tpg->tpg_group.cg_item
+                        * dependency now.
+                        */
+                       se_tpg = &tpg->se_tpg;
+                       ret = configfs_depend_item(se_tpg->se_tpg_tfo->tf_subsys,
+                                                  &se_tpg->tpg_group.cg_item);
+                       if (ret) {
+                               pr_warn("configfs_depend_item() failed: %d\n", ret);
+                               kfree(vs_tpg);
+                               mutex_unlock(&tpg->tv_tpg_mutex);
+                               goto out;
+                       }
                        tpg->tv_tpg_vhost_count++;
                        tpg->vhost_scsi = vs;
                        vs_tpg[tpg->tport_tpgt] = tpg;
@@ -1401,6 +1417,7 @@ static int
 vhost_scsi_clear_endpoint(struct vhost_scsi *vs,
                          struct vhost_scsi_target *t)
 {
+       struct se_portal_group *se_tpg;
        struct tcm_vhost_tport *tv_tport;
        struct tcm_vhost_tpg *tpg;
        struct vhost_virtqueue *vq;
@@ -1449,6 +1466,13 @@ vhost_scsi_clear_endpoint(struct vhost_scsi *vs,
                vs->vs_tpg[target] = NULL;
                match = true;
                mutex_unlock(&tpg->tv_tpg_mutex);
+               /*
+                * Release se_tpg->tpg_group.cg_item configfs dependency now
+                * to allow vhost-scsi WWPN se_tpg->tpg_group shutdown to occur.
+                */
+               se_tpg = &tpg->se_tpg;
+               configfs_undepend_item(se_tpg->se_tpg_tfo->tf_subsys,
+                                      &se_tpg->tpg_group.cg_item);
        }
        if (match) {
                for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) {
index 8532c3e..1626dc6 100644 (file)
@@ -161,7 +161,7 @@ static const struct s3c2410_wdt_variant drv_data_exynos5420 = {
 static const struct s3c2410_wdt_variant drv_data_exynos7 = {
        .disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET,
        .mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET,
-       .mask_bit = 0,
+       .mask_bit = 23,
        .rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
        .rst_stat_bit = 23,     /* A57 WDTRESET */
        .quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT,
index 34a1b9d..da0bbb4 100644 (file)
@@ -104,7 +104,7 @@ obj-$(CONFIG_QNX6FS_FS)             += qnx6/
 obj-$(CONFIG_AUTOFS4_FS)       += autofs4/
 obj-$(CONFIG_ADFS_FS)          += adfs/
 obj-$(CONFIG_FUSE_FS)          += fuse/
-obj-$(CONFIG_OVERLAYFS_FS)     += overlayfs/
+obj-$(CONFIG_OVERLAY_FS)       += overlayfs/
 obj-$(CONFIG_UDF_FS)           += udf/
 obj-$(CONFIG_SUN_OPENPROMFS)   += openpromfs/
 obj-$(CONFIG_OMFS_FS)          += omfs/
index 84a7510..14b9315 100644 (file)
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -165,6 +165,15 @@ static struct vfsmount *aio_mnt;
 static const struct file_operations aio_ring_fops;
 static const struct address_space_operations aio_ctx_aops;
 
+/* Backing dev info for aio fs.
+ * -no dirty page accounting or writeback happens
+ */
+static struct backing_dev_info aio_fs_backing_dev_info = {
+       .name           = "aiofs",
+       .state          = 0,
+       .capabilities   = BDI_CAP_NO_ACCT_AND_WRITEBACK | BDI_CAP_MAP_COPY,
+};
+
 static struct file *aio_private_file(struct kioctx *ctx, loff_t nr_pages)
 {
        struct qstr this = QSTR_INIT("[aio]", 5);
@@ -176,6 +185,7 @@ static struct file *aio_private_file(struct kioctx *ctx, loff_t nr_pages)
 
        inode->i_mapping->a_ops = &aio_ctx_aops;
        inode->i_mapping->private_data = ctx;
+       inode->i_mapping->backing_dev_info = &aio_fs_backing_dev_info;
        inode->i_size = PAGE_SIZE * nr_pages;
 
        path.dentry = d_alloc_pseudo(aio_mnt->mnt_sb, &this);
@@ -220,6 +230,9 @@ static int __init aio_setup(void)
        if (IS_ERR(aio_mnt))
                panic("Failed to create aio fs mount.");
 
+       if (bdi_init(&aio_fs_backing_dev_info))
+               panic("Failed to init aio fs backing dev info.");
+
        kiocb_cachep = KMEM_CACHE(kiocb, SLAB_HWCACHE_ALIGN|SLAB_PANIC);
        kioctx_cachep = KMEM_CACHE(kioctx,SLAB_HWCACHE_ALIGN|SLAB_PANIC);
 
@@ -281,11 +294,6 @@ static const struct file_operations aio_ring_fops = {
        .mmap = aio_ring_mmap,
 };
 
-static int aio_set_page_dirty(struct page *page)
-{
-       return 0;
-}
-
 #if IS_ENABLED(CONFIG_MIGRATION)
 static int aio_migratepage(struct address_space *mapping, struct page *new,
                        struct page *old, enum migrate_mode mode)
@@ -357,7 +365,7 @@ out:
 #endif
 
 static const struct address_space_operations aio_ctx_aops = {
-       .set_page_dirty = aio_set_page_dirty,
+       .set_page_dirty = __set_page_dirty_no_writeback,
 #if IS_ENABLED(CONFIG_MIGRATION)
        .migratepage    = aio_migratepage,
 #endif
@@ -412,7 +420,6 @@ static int aio_setup_ring(struct kioctx *ctx)
                pr_debug("pid(%d) page[%d]->count=%d\n",
                         current->pid, i, page_count(page));
                SetPageUptodate(page);
-               SetPageDirty(page);
                unlock_page(page);
 
                ctx->ring_pages[i] = page;
index d3220d3..dcd9be3 100644 (file)
@@ -1011,8 +1011,6 @@ int btrfs_decompress_buf2page(char *buf, unsigned long buf_start,
                bytes = min(bytes, working_bytes);
                kaddr = kmap_atomic(page_out);
                memcpy(kaddr + *pg_offset, buf + buf_offset, bytes);
-               if (*pg_index == (vcnt - 1) && *pg_offset == 0)
-                       memset(kaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
                kunmap_atomic(kaddr);
                flush_dcache_page(page_out);
 
@@ -1054,3 +1052,34 @@ int btrfs_decompress_buf2page(char *buf, unsigned long buf_start,
 
        return 1;
 }
+
+/*
+ * When uncompressing data, we need to make sure and zero any parts of
+ * the biovec that were not filled in by the decompression code.  pg_index
+ * and pg_offset indicate the last page and the last offset of that page
+ * that have been filled in.  This will zero everything remaining in the
+ * biovec.
+ */
+void btrfs_clear_biovec_end(struct bio_vec *bvec, int vcnt,
+                                  unsigned long pg_index,
+                                  unsigned long pg_offset)
+{
+       while (pg_index < vcnt) {
+               struct page *page = bvec[pg_index].bv_page;
+               unsigned long off = bvec[pg_index].bv_offset;
+               unsigned long len = bvec[pg_index].bv_len;
+
+               if (pg_offset < off)
+                       pg_offset = off;
+               if (pg_offset < off + len) {
+                       unsigned long bytes = off + len - pg_offset;
+                       char *kaddr;
+
+                       kaddr = kmap_atomic(page);
+                       memset(kaddr + pg_offset, 0, bytes);
+                       kunmap_atomic(kaddr);
+               }
+               pg_index++;
+               pg_offset = 0;
+       }
+}
index 0c803b4..d181f70 100644 (file)
@@ -45,7 +45,9 @@ int btrfs_submit_compressed_write(struct inode *inode, u64 start,
                                  unsigned long nr_pages);
 int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
                                 int mirror_num, unsigned long bio_flags);
-
+void btrfs_clear_biovec_end(struct bio_vec *bvec, int vcnt,
+                                  unsigned long pg_index,
+                                  unsigned long pg_offset);
 struct btrfs_compress_op {
        struct list_head *(*alloc_workspace)(void);
 
index 19bc616..150822e 100644 (file)
@@ -80,13 +80,6 @@ noinline void btrfs_clear_path_blocking(struct btrfs_path *p,
 {
        int i;
 
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-       /* lockdep really cares that we take all of these spinlocks
-        * in the right order.  If any of the locks in the path are not
-        * currently blocking, it is going to complain.  So, make really
-        * really sure by forcing the path to blocking before we clear
-        * the path blocking.
-        */
        if (held) {
                btrfs_set_lock_blocking_rw(held, held_rw);
                if (held_rw == BTRFS_WRITE_LOCK)
@@ -95,7 +88,6 @@ noinline void btrfs_clear_path_blocking(struct btrfs_path *p,
                        held_rw = BTRFS_READ_LOCK_BLOCKING;
        }
        btrfs_set_path_blocking(p);
-#endif
 
        for (i = BTRFS_MAX_LEVEL - 1; i >= 0; i--) {
                if (p->nodes[i] && p->locks[i]) {
@@ -107,10 +99,8 @@ noinline void btrfs_clear_path_blocking(struct btrfs_path *p,
                }
        }
 
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
        if (held)
                btrfs_clear_lock_blocking_rw(held, held_rw);
-#endif
 }
 
 /* this also releases the path */
@@ -2893,7 +2883,7 @@ cow_done:
                                        }
                                        p->locks[level] = BTRFS_WRITE_LOCK;
                                } else {
-                                       err = btrfs_try_tree_read_lock(b);
+                                       err = btrfs_tree_read_lock_atomic(b);
                                        if (!err) {
                                                btrfs_set_path_blocking(p);
                                                btrfs_tree_read_lock(b);
@@ -3025,7 +3015,7 @@ again:
                        }
 
                        level = btrfs_header_level(b);
-                       err = btrfs_try_tree_read_lock(b);
+                       err = btrfs_tree_read_lock_atomic(b);
                        if (!err) {
                                btrfs_set_path_blocking(p);
                                btrfs_tree_read_lock(b);
index 783a943..84a2d18 100644 (file)
@@ -413,7 +413,7 @@ int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
        ret = 0;
 fail:
        while (ret < 0 && !list_empty(&tmplist)) {
-               sums = list_entry(&tmplist, struct btrfs_ordered_sum, list);
+               sums = list_entry(tmplist.next, struct btrfs_ordered_sum, list);
                list_del(&sums->list);
                kfree(sums);
        }
index 5665d21..f8229ef 100644 (file)
@@ -127,6 +127,26 @@ again:
        atomic_inc(&eb->spinning_readers);
 }
 
+/*
+ * take a spinning read lock.
+ * returns 1 if we get the read lock and 0 if we don't
+ * this won't wait for blocking writers
+ */
+int btrfs_tree_read_lock_atomic(struct extent_buffer *eb)
+{
+       if (atomic_read(&eb->blocking_writers))
+               return 0;
+
+       read_lock(&eb->lock);
+       if (atomic_read(&eb->blocking_writers)) {
+               read_unlock(&eb->lock);
+               return 0;
+       }
+       atomic_inc(&eb->read_locks);
+       atomic_inc(&eb->spinning_readers);
+       return 1;
+}
+
 /*
  * returns 1 if we get the read lock and 0 if we don't
  * this won't wait for blocking writers
@@ -158,9 +178,7 @@ int btrfs_try_tree_write_lock(struct extent_buffer *eb)
            atomic_read(&eb->blocking_readers))
                return 0;
 
-       if (!write_trylock(&eb->lock))
-               return 0;
-
+       write_lock(&eb->lock);
        if (atomic_read(&eb->blocking_writers) ||
            atomic_read(&eb->blocking_readers)) {
                write_unlock(&eb->lock);
index b81e0e9..c44a9d5 100644 (file)
@@ -35,6 +35,8 @@ void btrfs_clear_lock_blocking_rw(struct extent_buffer *eb, int rw);
 void btrfs_assert_tree_locked(struct extent_buffer *eb);
 int btrfs_try_tree_read_lock(struct extent_buffer *eb);
 int btrfs_try_tree_write_lock(struct extent_buffer *eb);
+int btrfs_tree_read_lock_atomic(struct extent_buffer *eb);
+
 
 static inline void btrfs_tree_unlock_rw(struct extent_buffer *eb, int rw)
 {
index 78285f3..617553c 100644 (file)
@@ -373,6 +373,8 @@ cont:
        }
 done:
        kunmap(pages_in[page_in_index]);
+       if (!ret)
+               btrfs_clear_biovec_end(bvec, vcnt, page_out_index, pg_offset);
        return ret;
 }
 
@@ -410,10 +412,23 @@ static int lzo_decompress(struct list_head *ws, unsigned char *data_in,
                goto out;
        }
 
+       /*
+        * the caller is already checking against PAGE_SIZE, but lets
+        * move this check closer to the memcpy/memset
+        */
+       destlen = min_t(unsigned long, destlen, PAGE_SIZE);
        bytes = min_t(unsigned long, destlen, out_len - start_byte);
 
        kaddr = kmap_atomic(dest_page);
        memcpy(kaddr, workspace->buf + start_byte, bytes);
+
+       /*
+        * btrfs_getblock is doing a zero on the tail of the page too,
+        * but this will cover anything missing from the decompressed
+        * data.
+        */
+       if (bytes < destlen)
+               memset(kaddr+bytes, 0, destlen-bytes);
        kunmap_atomic(kaddr);
 out:
        return ret;
index 759fa4e..fb22fd8 100644 (file)
@@ -299,6 +299,8 @@ done:
        zlib_inflateEnd(&workspace->strm);
        if (data_in)
                kunmap(pages_in[page_in_index]);
+       if (!ret)
+               btrfs_clear_biovec_end(bvec, vcnt, page_out_index, pg_offset);
        return ret;
 }
 
@@ -310,10 +312,14 @@ static int zlib_decompress(struct list_head *ws, unsigned char *data_in,
        struct workspace *workspace = list_entry(ws, struct workspace, list);
        int ret = 0;
        int wbits = MAX_WBITS;
-       unsigned long bytes_left = destlen;
+       unsigned long bytes_left;
        unsigned long total_out = 0;
+       unsigned long pg_offset = 0;
        char *kaddr;
 
+       destlen = min_t(unsigned long, destlen, PAGE_SIZE);
+       bytes_left = destlen;
+
        workspace->strm.next_in = data_in;
        workspace->strm.avail_in = srclen;
        workspace->strm.total_in = 0;
@@ -341,7 +347,6 @@ static int zlib_decompress(struct list_head *ws, unsigned char *data_in,
                unsigned long buf_start;
                unsigned long buf_offset;
                unsigned long bytes;
-               unsigned long pg_offset = 0;
 
                ret = zlib_inflate(&workspace->strm, Z_NO_FLUSH);
                if (ret != Z_OK && ret != Z_STREAM_END)
@@ -384,6 +389,17 @@ next:
                ret = 0;
 
        zlib_inflateEnd(&workspace->strm);
+
+       /*
+        * this should only happen if zlib returned fewer bytes than we
+        * expected.  btrfs_get_block is responsible for zeroing from the
+        * end of the inline extent (destlen) to the end of the page
+        */
+       if (pg_offset < destlen) {
+               kaddr = kmap_atomic(dest_page);
+               memset(kaddr + pg_offset, 0, destlen - pg_offset);
+               kunmap_atomic(kaddr);
+       }
        return ret;
 }
 
index 659f2ea..cefca66 100644 (file)
@@ -2638,7 +2638,7 @@ static void handle_cap_flush_ack(struct inode *inode, u64 flush_tid,
 
        for (i = 0; i < CEPH_CAP_BITS; i++)
                if ((dirty & (1 << i)) &&
-                   flush_tid == ci->i_cap_flush_tid[i])
+                   (u16)flush_tid == ci->i_cap_flush_tid[i])
                        cleaned |= 1 << i;
 
        dout("handle_cap_flush_ack inode %p mds%d seq %d on %s cleaned %s,"
index 3ffef7f..5bc72b0 100644 (file)
@@ -778,6 +778,7 @@ restart:
                        struct dentry *parent = lock_parent(dentry);
                        if (likely(!dentry->d_lockref.count)) {
                                __dentry_kill(dentry);
+                               dput(parent);
                                goto restart;
                        }
                        if (parent)
index 6df8d3d..b8b92c2 100644 (file)
@@ -736,7 +736,12 @@ static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry,
        }
 
        alias = d_find_alias(inode);
-       if (alias && !vfat_d_anon_disconn(alias)) {
+       /*
+        * Checking "alias->d_parent == dentry->d_parent" to make sure
+        * FS is not corrupted (especially double linked dir).
+        */
+       if (alias && alias->d_parent == dentry->d_parent &&
+           !vfat_d_anon_disconn(alias)) {
                /*
                 * This inode has non anonymous-DCACHE_DISCONNECTED
                 * dentry. This means, the user did ->lookup() by an
@@ -755,12 +760,9 @@ static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry,
 
 out:
        mutex_unlock(&MSDOS_SB(sb)->s_lock);
-       dentry->d_time = dentry->d_parent->d_inode->i_version;
-       dentry = d_splice_alias(inode, dentry);
-       if (dentry)
-               dentry->d_time = dentry->d_parent->d_inode->i_version;
-       return dentry;
-
+       if (!inode)
+               dentry->d_time = dir->i_version;
+       return d_splice_alias(inode, dentry);
 error:
        mutex_unlock(&MSDOS_SB(sb)->s_lock);
        return ERR_PTR(err);
@@ -793,7 +795,6 @@ static int vfat_create(struct inode *dir, struct dentry *dentry, umode_t mode,
        inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
        /* timestamp is already written, so mark_inode_dirty() is unneeded. */
 
-       dentry->d_time = dentry->d_parent->d_inode->i_version;
        d_instantiate(dentry, inode);
 out:
        mutex_unlock(&MSDOS_SB(sb)->s_lock);
@@ -824,6 +825,7 @@ static int vfat_rmdir(struct inode *dir, struct dentry *dentry)
        clear_nlink(inode);
        inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
        fat_detach(inode);
+       dentry->d_time = dir->i_version;
 out:
        mutex_unlock(&MSDOS_SB(sb)->s_lock);
 
@@ -849,6 +851,7 @@ static int vfat_unlink(struct inode *dir, struct dentry *dentry)
        clear_nlink(inode);
        inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
        fat_detach(inode);
+       dentry->d_time = dir->i_version;
 out:
        mutex_unlock(&MSDOS_SB(sb)->s_lock);
 
@@ -889,7 +892,6 @@ static int vfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
        inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
        /* timestamp is already written, so mark_inode_dirty() is unneeded. */
 
-       dentry->d_time = dentry->d_parent->d_inode->i_version;
        d_instantiate(dentry, inode);
 
        mutex_unlock(&MSDOS_SB(sb)->s_lock);
index fe839b9..d67a16f 100644 (file)
@@ -170,27 +170,6 @@ struct iso9660_options{
        s32 sbsector;
 };
 
-/*
- * Compute the hash for the isofs name corresponding to the dentry.
- */
-static int
-isofs_hash_common(struct qstr *qstr, int ms)
-{
-       const char *name;
-       int len;
-
-       len = qstr->len;
-       name = qstr->name;
-       if (ms) {
-               while (len && name[len-1] == '.')
-                       len--;
-       }
-
-       qstr->hash = full_name_hash(name, len);
-
-       return 0;
-}
-
 /*
  * Compute the hash for the isofs name corresponding to the dentry.
  */
@@ -263,6 +242,27 @@ isofs_dentry_cmpi(const struct dentry *parent, const struct dentry *dentry,
 }
 
 #ifdef CONFIG_JOLIET
+/*
+ * Compute the hash for the isofs name corresponding to the dentry.
+ */
+static int
+isofs_hash_common(struct qstr *qstr, int ms)
+{
+       const char *name;
+       int len;
+
+       len = qstr->len;
+       name = qstr->name;
+       if (ms) {
+               while (len && name[len-1] == '.')
+                       len--;
+       }
+
+       qstr->hash = full_name_hash(name, len);
+
+       return 0;
+}
+
 static int
 isofs_hash_ms(const struct dentry *dentry, struct qstr *qstr)
 {
index e4dc747..1df94fa 100644 (file)
@@ -1853,13 +1853,12 @@ int jbd2_journal_set_features (journal_t *journal, unsigned long compat,
                                journal->j_chksum_driver = NULL;
                                return 0;
                        }
-               }
 
-               /* Precompute checksum seed for all metadata */
-               if (jbd2_journal_has_csum_v2or3(journal))
+                       /* Precompute checksum seed for all metadata */
                        journal->j_csum_seed = jbd2_chksum(journal, ~0,
                                                           sb->s_uuid,
                                                           sizeof(sb->s_uuid));
+               }
        }
 
        /* If enabling v1 checksums, downgrade superblock */
index 5228f20..4f46f7a 100644 (file)
@@ -378,7 +378,7 @@ bl_write_pagelist(struct nfs_pgio_header *header, int sync)
        loff_t offset = header->args.offset;
        size_t count = header->args.count;
        struct page **pages = header->args.pages;
-       int pg_index = pg_index = header->args.pgbase >> PAGE_CACHE_SHIFT;
+       int pg_index = header->args.pgbase >> PAGE_CACHE_SHIFT;
        unsigned int pg_len;
        struct blk_plug plug;
        int i;
index e966c02..acbf9ca 100644 (file)
@@ -65,17 +65,18 @@ bl_resolve_deviceid(struct nfs_server *server, struct pnfs_block_volume *b,
 
        dprintk("%s CREATING PIPEFS MESSAGE\n", __func__);
 
+       mutex_lock(&nn->bl_mutex);
        bl_pipe_msg.bl_wq = &nn->bl_wq;
 
        b->simple.len += 4;     /* single volume */
        if (b->simple.len > PAGE_SIZE)
-               return -EIO;
+               goto out_unlock;
 
        memset(msg, 0, sizeof(*msg));
        msg->len = sizeof(*bl_msg) + b->simple.len;
        msg->data = kzalloc(msg->len, gfp_mask);
        if (!msg->data)
-               goto out;
+               goto out_free_data;
 
        bl_msg = msg->data;
        bl_msg->type = BL_DEVICE_MOUNT,
@@ -87,7 +88,7 @@ bl_resolve_deviceid(struct nfs_server *server, struct pnfs_block_volume *b,
        rc = rpc_queue_upcall(nn->bl_device_pipe, msg);
        if (rc < 0) {
                remove_wait_queue(&nn->bl_wq, &wq);
-               goto out;
+               goto out_free_data;
        }
 
        set_current_state(TASK_UNINTERRUPTIBLE);
@@ -97,12 +98,14 @@ bl_resolve_deviceid(struct nfs_server *server, struct pnfs_block_volume *b,
        if (reply->status != BL_DEVICE_REQUEST_PROC) {
                printk(KERN_WARNING "%s failed to decode device: %d\n",
                        __func__, reply->status);
-               goto out;
+               goto out_free_data;
        }
 
        dev = MKDEV(reply->major, reply->minor);
-out:
+out_free_data:
        kfree(msg->data);
+out_unlock:
+       mutex_unlock(&nn->bl_mutex);
        return dev;
 }
 
@@ -232,6 +235,7 @@ static int nfs4blocklayout_net_init(struct net *net)
        struct nfs_net *nn = net_generic(net, nfs_net_id);
        struct dentry *dentry;
 
+       mutex_init(&nn->bl_mutex);
        init_waitqueue_head(&nn->bl_wq);
        nn->bl_device_pipe = rpc_mkpipe_data(&bl_upcall_ops, 0);
        if (IS_ERR(nn->bl_device_pipe))
index 5853f53..7f3f606 100644 (file)
@@ -125,6 +125,8 @@ again:
                        continue;
                if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
                        continue;
+               if (!nfs4_valid_open_stateid(state))
+                       continue;
                if (!nfs4_stateid_match(&state->stateid, stateid))
                        continue;
                get_nfs_open_context(ctx);
@@ -193,7 +195,11 @@ static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *
 {
        int res = 0;
 
-       res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync);
+       if (!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
+               res = nfs4_proc_delegreturn(inode,
+                               delegation->cred,
+                               &delegation->stateid,
+                               issync);
        nfs_free_delegation(delegation);
        return res;
 }
@@ -380,11 +386,13 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation
 {
        struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
        struct nfs_inode *nfsi = NFS_I(inode);
-       int err;
+       int err = 0;
 
        if (delegation == NULL)
                return 0;
        do {
+               if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
+                       break;
                err = nfs_delegation_claim_opens(inode, &delegation->stateid);
                if (!issync || err != -EAGAIN)
                        break;
@@ -605,10 +613,23 @@ static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *cl
        rcu_read_unlock();
 }
 
+static void nfs_revoke_delegation(struct inode *inode)
+{
+       struct nfs_delegation *delegation;
+       rcu_read_lock();
+       delegation = rcu_dereference(NFS_I(inode)->delegation);
+       if (delegation != NULL) {
+               set_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
+               nfs_mark_return_delegation(NFS_SERVER(inode), delegation);
+       }
+       rcu_read_unlock();
+}
+
 void nfs_remove_bad_delegation(struct inode *inode)
 {
        struct nfs_delegation *delegation;
 
+       nfs_revoke_delegation(inode);
        delegation = nfs_inode_detach_delegation(inode);
        if (delegation) {
                nfs_inode_find_state_and_recover(inode, &delegation->stateid);
index 5c1cce3..e3c20a3 100644 (file)
@@ -31,6 +31,7 @@ enum {
        NFS_DELEGATION_RETURN_IF_CLOSED,
        NFS_DELEGATION_REFERENCED,
        NFS_DELEGATION_RETURNING,
+       NFS_DELEGATION_REVOKED,
 };
 
 int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
index 06e8cfc..6e62155 100644 (file)
@@ -1527,6 +1527,7 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
                case -ENOENT:
                        d_drop(dentry);
                        d_add(dentry, NULL);
+                       nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
                        break;
                case -EISDIR:
                case -ENOTDIR:
index 20cffc8..10bf072 100644 (file)
@@ -266,6 +266,7 @@ static void nfs_direct_req_free(struct kref *kref)
 {
        struct nfs_direct_req *dreq = container_of(kref, struct nfs_direct_req, kref);
 
+       nfs_free_pnfs_ds_cinfo(&dreq->ds_cinfo);
        if (dreq->l_ctx != NULL)
                nfs_put_lock_context(dreq->l_ctx);
        if (dreq->ctx != NULL)
index 46fab1c..7afb52f 100644 (file)
@@ -145,9 +145,6 @@ static int filelayout_async_handle_error(struct rpc_task *task,
        case -NFS4ERR_DELEG_REVOKED:
        case -NFS4ERR_ADMIN_REVOKED:
        case -NFS4ERR_BAD_STATEID:
-               if (state == NULL)
-                       break;
-               nfs_remove_bad_delegation(state->inode);
        case -NFS4ERR_OPENMODE:
                if (state == NULL)
                        break;
index 6388a59..00689a8 100644 (file)
@@ -626,7 +626,7 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
 {
        struct inode *inode = dentry->d_inode;
        int need_atime = NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATIME;
-       int err;
+       int err = 0;
 
        trace_nfs_getattr_enter(inode);
        /* Flush out writes to the server in order to update c/mtime.  */
index ef221fb..f0e06e4 100644 (file)
@@ -19,6 +19,7 @@ struct nfs_net {
        struct rpc_pipe *bl_device_pipe;
        struct bl_dev_msg bl_mount_reply;
        wait_queue_head_t bl_wq;
+       struct mutex bl_mutex;
        struct list_head nfs_client_list;
        struct list_head nfs_volume_list;
 #if IS_ENABLED(CONFIG_NFS_V4)
index 405bd95..69dc20a 100644 (file)
@@ -370,11 +370,6 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc
                case -NFS4ERR_DELEG_REVOKED:
                case -NFS4ERR_ADMIN_REVOKED:
                case -NFS4ERR_BAD_STATEID:
-                       if (inode != NULL && nfs4_have_delegation(inode, FMODE_READ)) {
-                               nfs_remove_bad_delegation(inode);
-                               exception->retry = 1;
-                               break;
-                       }
                        if (state == NULL)
                                break;
                        ret = nfs4_schedule_stateid_recovery(server, state);
@@ -1654,7 +1649,7 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct
                        nfs_inode_find_state_and_recover(state->inode,
                                        stateid);
                        nfs4_schedule_stateid_recovery(server, state);
-                       return 0;
+                       return -EAGAIN;
                case -NFS4ERR_DELAY:
                case -NFS4ERR_GRACE:
                        set_bit(NFS_DELEGATED_STATE, &state->flags);
@@ -2109,46 +2104,60 @@ static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *sta
        return ret;
 }
 
+static void nfs_finish_clear_delegation_stateid(struct nfs4_state *state)
+{
+       nfs_remove_bad_delegation(state->inode);
+       write_seqlock(&state->seqlock);
+       nfs4_stateid_copy(&state->stateid, &state->open_stateid);
+       write_sequnlock(&state->seqlock);
+       clear_bit(NFS_DELEGATED_STATE, &state->flags);
+}
+
+static void nfs40_clear_delegation_stateid(struct nfs4_state *state)
+{
+       if (rcu_access_pointer(NFS_I(state->inode)->delegation) != NULL)
+               nfs_finish_clear_delegation_stateid(state);
+}
+
+static int nfs40_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state)
+{
+       /* NFSv4.0 doesn't allow for delegation recovery on open expire */
+       nfs40_clear_delegation_stateid(state);
+       return nfs4_open_expired(sp, state);
+}
+
 #if defined(CONFIG_NFS_V4_1)
-static void nfs41_clear_delegation_stateid(struct nfs4_state *state)
+static void nfs41_check_delegation_stateid(struct nfs4_state *state)
 {
        struct nfs_server *server = NFS_SERVER(state->inode);
-       nfs4_stateid *stateid = &state->stateid;
+       nfs4_stateid stateid;
        struct nfs_delegation *delegation;
-       struct rpc_cred *cred = NULL;
-       int status = -NFS4ERR_BAD_STATEID;
-
-       /* If a state reset has been done, test_stateid is unneeded */
-       if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
-               return;
+       struct rpc_cred *cred;
+       int status;
 
        /* Get the delegation credential for use by test/free_stateid */
        rcu_read_lock();
        delegation = rcu_dereference(NFS_I(state->inode)->delegation);
-       if (delegation != NULL &&
-           nfs4_stateid_match(&delegation->stateid, stateid)) {
-               cred = get_rpccred(delegation->cred);
-               rcu_read_unlock();
-               status = nfs41_test_stateid(server, stateid, cred);
-               trace_nfs4_test_delegation_stateid(state, NULL, status);
-       } else
+       if (delegation == NULL) {
                rcu_read_unlock();
+               return;
+       }
+
+       nfs4_stateid_copy(&stateid, &delegation->stateid);
+       cred = get_rpccred(delegation->cred);
+       rcu_read_unlock();
+       status = nfs41_test_stateid(server, &stateid, cred);
+       trace_nfs4_test_delegation_stateid(state, NULL, status);
 
        if (status != NFS_OK) {
                /* Free the stateid unless the server explicitly
                 * informs us the stateid is unrecognized. */
                if (status != -NFS4ERR_BAD_STATEID)
-                       nfs41_free_stateid(server, stateid, cred);
-               nfs_remove_bad_delegation(state->inode);
-
-               write_seqlock(&state->seqlock);
-               nfs4_stateid_copy(&state->stateid, &state->open_stateid);
-               write_sequnlock(&state->seqlock);
-               clear_bit(NFS_DELEGATED_STATE, &state->flags);
+                       nfs41_free_stateid(server, &stateid, cred);
+               nfs_finish_clear_delegation_stateid(state);
        }
 
-       if (cred != NULL)
-               put_rpccred(cred);
+       put_rpccred(cred);
 }
 
 /**
@@ -2192,7 +2201,7 @@ static int nfs41_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *st
 {
        int status;
 
-       nfs41_clear_delegation_stateid(state);
+       nfs41_check_delegation_stateid(state);
        status = nfs41_check_open_stateid(state);
        if (status != NFS_OK)
                status = nfs4_open_expired(sp, state);
@@ -2231,19 +2240,8 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata,
        seq = raw_seqcount_begin(&sp->so_reclaim_seqcount);
 
        ret = _nfs4_proc_open(opendata);
-       if (ret != 0) {
-               if (ret == -ENOENT) {
-                       dentry = opendata->dentry;
-                       if (dentry->d_inode)
-                               d_delete(dentry);
-                       else if (d_unhashed(dentry))
-                               d_add(dentry, NULL);
-
-                       nfs_set_verifier(dentry,
-                                        nfs_save_change_attribute(opendata->dir->d_inode));
-               }
+       if (ret != 0)
                goto out;
-       }
 
        state = nfs4_opendata_to_nfs4_state(opendata);
        ret = PTR_ERR(state);
@@ -4841,9 +4839,6 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
                case -NFS4ERR_DELEG_REVOKED:
                case -NFS4ERR_ADMIN_REVOKED:
                case -NFS4ERR_BAD_STATEID:
-                       if (state == NULL)
-                               break;
-                       nfs_remove_bad_delegation(state->inode);
                case -NFS4ERR_OPENMODE:
                        if (state == NULL)
                                break;
@@ -8341,7 +8336,7 @@ static const struct nfs4_state_recovery_ops nfs41_reboot_recovery_ops = {
 static const struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops = {
        .owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE,
        .state_flag_bit = NFS_STATE_RECLAIM_NOGRACE,
-       .recover_open   = nfs4_open_expired,
+       .recover_open   = nfs40_open_expired,
        .recover_lock   = nfs4_lock_expired,
        .establish_clid = nfs4_init_clientid,
 };
@@ -8408,8 +8403,7 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
                | NFS_CAP_CHANGE_ATTR
                | NFS_CAP_POSIX_LOCK
                | NFS_CAP_STATEID_NFSV41
-               | NFS_CAP_ATOMIC_OPEN_V1
-               | NFS_CAP_SEEK,
+               | NFS_CAP_ATOMIC_OPEN_V1,
        .init_client = nfs41_init_client,
        .shutdown_client = nfs41_shutdown_client,
        .match_stateid = nfs41_match_stateid,
@@ -8431,7 +8425,8 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = {
                | NFS_CAP_CHANGE_ATTR
                | NFS_CAP_POSIX_LOCK
                | NFS_CAP_STATEID_NFSV41
-               | NFS_CAP_ATOMIC_OPEN_V1,
+               | NFS_CAP_ATOMIC_OPEN_V1
+               | NFS_CAP_SEEK,
        .init_client = nfs41_init_client,
        .shutdown_client = nfs41_shutdown_client,
        .match_stateid = nfs41_match_stateid,
index 1249384..f83b02d 100644 (file)
@@ -715,8 +715,6 @@ static void nfs_inode_remove_request(struct nfs_page *req)
 
        if (test_and_clear_bit(PG_INODE_REF, &req->wb_flags))
                nfs_release_request(req);
-       else
-               WARN_ON_ONCE(1);
 }
 
 static void
index ed2b115..7cbdf1b 100644 (file)
@@ -774,8 +774,12 @@ static bool nfsd41_cb_get_slot(struct nfs4_client *clp, struct rpc_task *task)
 {
        if (test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) {
                rpc_sleep_on(&clp->cl_cb_waitq, task, NULL);
-               dprintk("%s slot is busy\n", __func__);
-               return false;
+               /* Race breaker */
+               if (test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) {
+                       dprintk("%s slot is busy\n", __func__);
+                       return false;
+               }
+               rpc_wake_up_queued_task(&clp->cl_cb_waitq, task);
        }
        return true;
 }
index 747f3b9..33a46a8 100644 (file)
@@ -335,12 +335,15 @@ void              nfsd_lockd_shutdown(void);
        (NFSD4_SUPPORTED_ATTRS_WORD2 | FATTR4_WORD2_SUPPATTR_EXCLCREAT)
 
 #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
-#define NFSD4_2_SUPPORTED_ATTRS_WORD2 \
-       (NFSD4_1_SUPPORTED_ATTRS_WORD2 | FATTR4_WORD2_SECURITY_LABEL)
+#define NFSD4_2_SECURITY_ATTRS         FATTR4_WORD2_SECURITY_LABEL
 #else
-#define NFSD4_2_SUPPORTED_ATTRS_WORD2 0
+#define NFSD4_2_SECURITY_ATTRS         0
 #endif
 
+#define NFSD4_2_SUPPORTED_ATTRS_WORD2 \
+       (NFSD4_1_SUPPORTED_ATTRS_WORD2 | \
+       NFSD4_2_SECURITY_ATTRS)
+
 static inline u32 nfsd_suppattrs0(u32 minorversion)
 {
        return minorversion ? NFSD4_1_SUPPORTED_ATTRS_WORD0
index 9d3e9c5..89326ac 100644 (file)
@@ -229,8 +229,16 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
                                              &fsnotify_mark_srcu);
        }
 
+       /*
+        * We need to merge inode & vfsmount mark lists so that inode mark
+        * ignore masks are properly reflected for mount mark notifications.
+        * That's why this traversal is so complicated...
+        */
        while (inode_node || vfsmount_node) {
-               inode_group = vfsmount_group = NULL;
+               inode_group = NULL;
+               inode_mark = NULL;
+               vfsmount_group = NULL;
+               vfsmount_mark = NULL;
 
                if (inode_node) {
                        inode_mark = hlist_entry(srcu_dereference(inode_node, &fsnotify_mark_srcu),
@@ -244,21 +252,19 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
                        vfsmount_group = vfsmount_mark->group;
                }
 
-               if (inode_group > vfsmount_group) {
-                       /* handle inode */
-                       ret = send_to_group(to_tell, inode_mark, NULL, mask,
-                                           data, data_is, cookie, file_name);
-                       /* we didn't use the vfsmount_mark */
-                       vfsmount_group = NULL;
-               } else if (vfsmount_group > inode_group) {
-                       ret = send_to_group(to_tell, NULL, vfsmount_mark, mask,
-                                           data, data_is, cookie, file_name);
-                       inode_group = NULL;
-               } else {
-                       ret = send_to_group(to_tell, inode_mark, vfsmount_mark,
-                                           mask, data, data_is, cookie,
-                                           file_name);
+               if (inode_group && vfsmount_group) {
+                       int cmp = fsnotify_compare_groups(inode_group,
+                                                         vfsmount_group);
+                       if (cmp > 0) {
+                               inode_group = NULL;
+                               inode_mark = NULL;
+                       } else if (cmp < 0) {
+                               vfsmount_group = NULL;
+                               vfsmount_mark = NULL;
+                       }
                }
+               ret = send_to_group(to_tell, inode_mark, vfsmount_mark, mask,
+                                   data, data_is, cookie, file_name);
 
                if (ret && (mask & ALL_FSNOTIFY_PERM_EVENTS))
                        goto out;
index 9c0898c..3b68b0a 100644 (file)
@@ -12,6 +12,10 @@ extern void fsnotify_flush_notify(struct fsnotify_group *group);
 /* protects reads of inode and vfsmount marks list */
 extern struct srcu_struct fsnotify_mark_srcu;
 
+/* compare two groups for sorting of marks lists */
+extern int fsnotify_compare_groups(struct fsnotify_group *a,
+                                  struct fsnotify_group *b);
+
 extern void fsnotify_set_inode_mark_mask_locked(struct fsnotify_mark *fsn_mark,
                                                __u32 mask);
 /* add a mark to an inode */
index e849714..dfbf544 100644 (file)
@@ -194,6 +194,7 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
 {
        struct fsnotify_mark *lmark, *last = NULL;
        int ret = 0;
+       int cmp;
 
        mark->flags |= FSNOTIFY_MARK_FLAG_INODE;
 
@@ -219,11 +220,8 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
                        goto out;
                }
 
-               if (mark->group->priority < lmark->group->priority)
-                       continue;
-
-               if ((mark->group->priority == lmark->group->priority) &&
-                   (mark->group < lmark->group))
+               cmp = fsnotify_compare_groups(lmark->group, mark->group);
+               if (cmp < 0)
                        continue;
 
                hlist_add_before_rcu(&mark->i.i_list, &lmark->i.i_list);
index d90deaa..34c38fa 100644 (file)
@@ -209,6 +209,42 @@ void fsnotify_set_mark_ignored_mask_locked(struct fsnotify_mark *mark, __u32 mas
        mark->ignored_mask = mask;
 }
 
+/*
+ * Sorting function for lists of fsnotify marks.
+ *
+ * Fanotify supports different notification classes (reflected as priority of
+ * notification group). Events shall be passed to notification groups in
+ * decreasing priority order. To achieve this marks in notification lists for
+ * inodes and vfsmounts are sorted so that priorities of corresponding groups
+ * are descending.
+ *
+ * Furthermore correct handling of the ignore mask requires processing inode
+ * and vfsmount marks of each group together. Using the group address as
+ * further sort criterion provides a unique sorting order and thus we can
+ * merge inode and vfsmount lists of marks in linear time and find groups
+ * present in both lists.
+ *
+ * A return value of 1 signifies that b has priority over a.
+ * A return value of 0 signifies that the two marks have to be handled together.
+ * A return value of -1 signifies that a has priority over b.
+ */
+int fsnotify_compare_groups(struct fsnotify_group *a, struct fsnotify_group *b)
+{
+       if (a == b)
+               return 0;
+       if (!a)
+               return 1;
+       if (!b)
+               return -1;
+       if (a->priority < b->priority)
+               return 1;
+       if (a->priority > b->priority)
+               return -1;
+       if (a < b)
+               return 1;
+       return -1;
+}
+
 /*
  * Attach an initialized mark to a given group and fs object.
  * These marks may be used for the fsnotify backend to determine which
index ac851e8..faefa72 100644 (file)
@@ -153,6 +153,7 @@ int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark,
        struct mount *m = real_mount(mnt);
        struct fsnotify_mark *lmark, *last = NULL;
        int ret = 0;
+       int cmp;
 
        mark->flags |= FSNOTIFY_MARK_FLAG_VFSMOUNT;
 
@@ -178,11 +179,8 @@ int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark,
                        goto out;
                }
 
-               if (mark->group->priority < lmark->group->priority)
-                       continue;
-
-               if ((mark->group->priority == lmark->group->priority) &&
-                   (mark->group < lmark->group))
+               cmp = fsnotify_compare_groups(lmark->group, mark->group);
+               if (cmp < 0)
                        continue;
 
                hlist_add_before_rcu(&mark->m.m_list, &lmark->m.m_list);
index 97de0fb..a960440 100644 (file)
@@ -925,7 +925,7 @@ static int o2net_send_tcp_msg(struct socket *sock, struct kvec *vec,
                              size_t veclen, size_t total)
 {
        int ret;
-       struct msghdr msg;
+       struct msghdr msg = {.msg_flags = 0,};
 
        if (sock == NULL) {
                ret = -EINVAL;
index e601259..3435581 100644 (file)
@@ -1,4 +1,4 @@
-config OVERLAYFS_FS
+config OVERLAY_FS
        tristate "Overlay filesystem support"
        help
          An overlay filesystem combines two filesystems - an 'upper' filesystem
index 8f91889..900daed 100644 (file)
@@ -2,6 +2,6 @@
 # Makefile for the overlay filesystem.
 #
 
-obj-$(CONFIG_OVERLAYFS_FS) += overlayfs.o
+obj-$(CONFIG_OVERLAY_FS) += overlay.o
 
-overlayfs-objs := super.o inode.o dir.o readdir.o copy_up.o
+overlay-objs := super.o inode.o dir.o readdir.o copy_up.o
index 15cd91a..8ffc4b9 100644 (file)
@@ -284,8 +284,7 @@ out:
        return ERR_PTR(err);
 }
 
-static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry,
-                                               enum ovl_path_type type)
+static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry)
 {
        int err;
        struct dentry *ret = NULL;
@@ -294,8 +293,17 @@ static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry,
        err = ovl_check_empty_dir(dentry, &list);
        if (err)
                ret = ERR_PTR(err);
-       else if (type == OVL_PATH_MERGE)
-               ret = ovl_clear_empty(dentry, &list);
+       else {
+               /*
+                * If no upperdentry then skip clearing whiteouts.
+                *
+                * Can race with copy-up, since we don't hold the upperdir
+                * mutex.  Doesn't matter, since copy-up can't create a
+                * non-empty directory from an empty one.
+                */
+               if (ovl_dentry_upper(dentry))
+                       ret = ovl_clear_empty(dentry, &list);
+       }
 
        ovl_cache_free(&list);
 
@@ -487,8 +495,7 @@ out:
        return err;
 }
 
-static int ovl_remove_and_whiteout(struct dentry *dentry,
-                                  enum ovl_path_type type, bool is_dir)
+static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir)
 {
        struct dentry *workdir = ovl_workdir(dentry);
        struct inode *wdir = workdir->d_inode;
@@ -500,7 +507,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry,
        int err;
 
        if (is_dir) {
-               opaquedir = ovl_check_empty_and_clear(dentry, type);
+               opaquedir = ovl_check_empty_and_clear(dentry);
                err = PTR_ERR(opaquedir);
                if (IS_ERR(opaquedir))
                        goto out;
@@ -515,9 +522,10 @@ static int ovl_remove_and_whiteout(struct dentry *dentry,
        if (IS_ERR(whiteout))
                goto out_unlock;
 
-       if (type == OVL_PATH_LOWER) {
+       upper = ovl_dentry_upper(dentry);
+       if (!upper) {
                upper = lookup_one_len(dentry->d_name.name, upperdir,
-                                          dentry->d_name.len);
+                                      dentry->d_name.len);
                err = PTR_ERR(upper);
                if (IS_ERR(upper))
                        goto kill_whiteout;
@@ -529,7 +537,6 @@ static int ovl_remove_and_whiteout(struct dentry *dentry,
        } else {
                int flags = 0;
 
-               upper = ovl_dentry_upper(dentry);
                if (opaquedir)
                        upper = opaquedir;
                err = -ESTALE;
@@ -648,7 +655,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
                cap_raise(override_cred->cap_effective, CAP_CHOWN);
                old_cred = override_creds(override_cred);
 
-               err = ovl_remove_and_whiteout(dentry, type, is_dir);
+               err = ovl_remove_and_whiteout(dentry, is_dir);
 
                revert_creds(old_cred);
                put_cred(override_cred);
@@ -781,7 +788,7 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
        }
 
        if (overwrite && (new_type == OVL_PATH_LOWER || new_type == OVL_PATH_MERGE) && new_is_dir) {
-               opaquedir = ovl_check_empty_and_clear(new, new_type);
+               opaquedir = ovl_check_empty_and_clear(new);
                err = PTR_ERR(opaquedir);
                if (IS_ERR(opaquedir)) {
                        opaquedir = NULL;
index af2d18c..07d74b2 100644 (file)
@@ -235,26 +235,36 @@ out:
        return err;
 }
 
+static bool ovl_need_xattr_filter(struct dentry *dentry,
+                                 enum ovl_path_type type)
+{
+       return type == OVL_PATH_UPPER && S_ISDIR(dentry->d_inode->i_mode);
+}
+
 ssize_t ovl_getxattr(struct dentry *dentry, const char *name,
                     void *value, size_t size)
 {
-       if (ovl_path_type(dentry->d_parent) == OVL_PATH_MERGE &&
-           ovl_is_private_xattr(name))
+       struct path realpath;
+       enum ovl_path_type type = ovl_path_real(dentry, &realpath);
+
+       if (ovl_need_xattr_filter(dentry, type) && ovl_is_private_xattr(name))
                return -ENODATA;
 
-       return vfs_getxattr(ovl_dentry_real(dentry), name, value, size);
+       return vfs_getxattr(realpath.dentry, name, value, size);
 }
 
 ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
 {
+       struct path realpath;
+       enum ovl_path_type type = ovl_path_real(dentry, &realpath);
        ssize_t res;
        int off;
 
-       res = vfs_listxattr(ovl_dentry_real(dentry), list, size);
+       res = vfs_listxattr(realpath.dentry, list, size);
        if (res <= 0 || size == 0)
                return res;
 
-       if (ovl_path_type(dentry->d_parent) != OVL_PATH_MERGE)
+       if (!ovl_need_xattr_filter(dentry, type))
                return res;
 
        /* filter out private xattrs */
@@ -279,17 +289,16 @@ int ovl_removexattr(struct dentry *dentry, const char *name)
 {
        int err;
        struct path realpath;
-       enum ovl_path_type type;
+       enum ovl_path_type type = ovl_path_real(dentry, &realpath);
 
        err = ovl_want_write(dentry);
        if (err)
                goto out;
 
-       if (ovl_path_type(dentry->d_parent) == OVL_PATH_MERGE &&
-           ovl_is_private_xattr(name))
+       err = -ENODATA;
+       if (ovl_need_xattr_filter(dentry, type) && ovl_is_private_xattr(name))
                goto out_drop_write;
 
-       type = ovl_path_real(dentry, &realpath);
        if (type == OVL_PATH_LOWER) {
                err = vfs_getxattr(realpath.dentry, name, NULL, 0);
                if (err < 0)
index 4e9d7c1..ab1e3dc 100644 (file)
@@ -168,7 +168,7 @@ static void ovl_cache_put(struct ovl_dir_file *od, struct dentry *dentry)
 {
        struct ovl_dir_cache *cache = od->cache;
 
-       list_del(&od->cursor.l_node);
+       list_del_init(&od->cursor.l_node);
        WARN_ON(cache->refcount <= 0);
        cache->refcount--;
        if (!cache->refcount) {
@@ -274,11 +274,11 @@ static int ovl_dir_mark_whiteouts(struct dentry *dir,
        return 0;
 }
 
-static inline int ovl_dir_read_merged(struct path *upperpath,
-                                     struct path *lowerpath,
-                                     struct list_head *list)
+static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list)
 {
        int err;
+       struct path lowerpath;
+       struct path upperpath;
        struct ovl_readdir_data rdd = {
                .ctx.actor = ovl_fill_merge,
                .list = list,
@@ -286,25 +286,28 @@ static inline int ovl_dir_read_merged(struct path *upperpath,
                .is_merge = false,
        };
 
-       if (upperpath->dentry) {
-               err = ovl_dir_read(upperpath, &rdd);
+       ovl_path_lower(dentry, &lowerpath);
+       ovl_path_upper(dentry, &upperpath);
+
+       if (upperpath.dentry) {
+               err = ovl_dir_read(&upperpath, &rdd);
                if (err)
                        goto out;
 
-               if (lowerpath->dentry) {
-                       err = ovl_dir_mark_whiteouts(upperpath->dentry, &rdd);
+               if (lowerpath.dentry) {
+                       err = ovl_dir_mark_whiteouts(upperpath.dentry, &rdd);
                        if (err)
                                goto out;
                }
        }
-       if (lowerpath->dentry) {
+       if (lowerpath.dentry) {
                /*
                 * Insert lowerpath entries before upperpath ones, this allows
                 * offsets to be reasonably constant
                 */
                list_add(&rdd.middle, rdd.list);
                rdd.is_merge = true;
-               err = ovl_dir_read(lowerpath, &rdd);
+               err = ovl_dir_read(&lowerpath, &rdd);
                list_del(&rdd.middle);
        }
 out:
@@ -329,8 +332,6 @@ static void ovl_seek_cursor(struct ovl_dir_file *od, loff_t pos)
 static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry)
 {
        int res;
-       struct path lowerpath;
-       struct path upperpath;
        struct ovl_dir_cache *cache;
 
        cache = ovl_dir_cache(dentry);
@@ -347,10 +348,7 @@ static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry)
        cache->refcount = 1;
        INIT_LIST_HEAD(&cache->entries);
 
-       ovl_path_lower(dentry, &lowerpath);
-       ovl_path_upper(dentry, &upperpath);
-
-       res = ovl_dir_read_merged(&upperpath, &lowerpath, &cache->entries);
+       res = ovl_dir_read_merged(dentry, &cache->entries);
        if (res) {
                ovl_cache_free(&cache->entries);
                kfree(cache);
@@ -452,10 +450,10 @@ static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end,
        /*
         * Need to check if we started out being a lower dir, but got copied up
         */
-       if (!od->is_upper && ovl_path_type(dentry) == OVL_PATH_MERGE) {
+       if (!od->is_upper && ovl_path_type(dentry) != OVL_PATH_LOWER) {
                struct inode *inode = file_inode(file);
 
-               realfile =lockless_dereference(od->upperfile);
+               realfile = lockless_dereference(od->upperfile);
                if (!realfile) {
                        struct path upperpath;
 
@@ -538,14 +536,9 @@ const struct file_operations ovl_dir_operations = {
 int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list)
 {
        int err;
-       struct path lowerpath;
-       struct path upperpath;
        struct ovl_cache_entry *p;
 
-       ovl_path_upper(dentry, &upperpath);
-       ovl_path_lower(dentry, &lowerpath);
-
-       err = ovl_dir_read_merged(&upperpath, &lowerpath, list);
+       err = ovl_dir_read_merged(dentry, list);
        if (err)
                return err;
 
index 08b704c..f16d318 100644 (file)
@@ -24,7 +24,7 @@ MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
 MODULE_DESCRIPTION("Overlay filesystem");
 MODULE_LICENSE("GPL");
 
-#define OVERLAYFS_SUPER_MAGIC 0x794c764f
+#define OVERLAYFS_SUPER_MAGIC 0x794c7630
 
 struct ovl_config {
        char *lowerdir;
@@ -84,12 +84,7 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry)
 
 static struct dentry *ovl_upperdentry_dereference(struct ovl_entry *oe)
 {
-       struct dentry *upperdentry = ACCESS_ONCE(oe->__upperdentry);
-       /*
-        * Make sure to order reads to upperdentry wrt ovl_dentry_update()
-        */
-       smp_read_barrier_depends();
-       return upperdentry;
+       return lockless_dereference(oe->__upperdentry);
 }
 
 void ovl_path_upper(struct dentry *dentry, struct path *path)
@@ -462,11 +457,34 @@ static const match_table_t ovl_tokens = {
        {OPT_ERR,                       NULL}
 };
 
+static char *ovl_next_opt(char **s)
+{
+       char *sbegin = *s;
+       char *p;
+
+       if (sbegin == NULL)
+               return NULL;
+
+       for (p = sbegin; *p; p++) {
+               if (*p == '\\') {
+                       p++;
+                       if (!*p)
+                               break;
+               } else if (*p == ',') {
+                       *p = '\0';
+                       *s = p + 1;
+                       return sbegin;
+               }
+       }
+       *s = NULL;
+       return sbegin;
+}
+
 static int ovl_parse_opt(char *opt, struct ovl_config *config)
 {
        char *p;
 
-       while ((p = strsep(&opt, ",")) != NULL) {
+       while ((p = ovl_next_opt(&opt)) != NULL) {
                int token;
                substring_t args[MAX_OPT_ARGS];
 
@@ -554,15 +572,34 @@ out_dput:
        goto out_unlock;
 }
 
+static void ovl_unescape(char *s)
+{
+       char *d = s;
+
+       for (;; s++, d++) {
+               if (*s == '\\')
+                       s++;
+               *d = *s;
+               if (!*s)
+                       break;
+       }
+}
+
 static int ovl_mount_dir(const char *name, struct path *path)
 {
        int err;
+       char *tmp = kstrdup(name, GFP_KERNEL);
+
+       if (!tmp)
+               return -ENOMEM;
 
-       err = kern_path(name, LOOKUP_FOLLOW, path);
+       ovl_unescape(tmp);
+       err = kern_path(tmp, LOOKUP_FOLLOW, path);
        if (err) {
-               pr_err("overlayfs: failed to resolve '%s': %i\n", name, err);
+               pr_err("overlayfs: failed to resolve '%s': %i\n", tmp, err);
                err = -EINVAL;
        }
+       kfree(tmp);
        return err;
 }
 
@@ -776,11 +813,11 @@ static struct dentry *ovl_mount(struct file_system_type *fs_type, int flags,
 
 static struct file_system_type ovl_fs_type = {
        .owner          = THIS_MODULE,
-       .name           = "overlayfs",
+       .name           = "overlay",
        .mount          = ovl_mount,
        .kill_sb        = kill_anon_super,
 };
-MODULE_ALIAS_FS("overlayfs");
+MODULE_ALIAS_FS("overlay");
 
 static int __init ovl_init(void)
 {
index 92e8f99..2810026 100644 (file)
@@ -1338,7 +1338,10 @@ xfs_free_file_space(
        goto out;
 }
 
-
+/*
+ * Preallocate and zero a range of a file. This mechanism has the allocation
+ * semantics of fallocate and in addition converts data in the range to zeroes.
+ */
 int
 xfs_zero_file_space(
        struct xfs_inode        *ip,
@@ -1346,65 +1349,30 @@ xfs_zero_file_space(
        xfs_off_t               len)
 {
        struct xfs_mount        *mp = ip->i_mount;
-       uint                    granularity;
-       xfs_off_t               start_boundary;
-       xfs_off_t               end_boundary;
+       uint                    blksize;
        int                     error;
 
        trace_xfs_zero_file_space(ip);
 
-       granularity = max_t(uint, 1 << mp->m_sb.sb_blocklog, PAGE_CACHE_SIZE);
+       blksize = 1 << mp->m_sb.sb_blocklog;
 
        /*
-        * Round the range of extents we are going to convert inwards.  If the
-        * offset is aligned, then it doesn't get changed so we zero from the
-        * start of the block offset points to.
+        * Punch a hole and prealloc the range. We use hole punch rather than
+        * unwritten extent conversion for two reasons:
+        *
+        * 1.) Hole punch handles partial block zeroing for us.
+        *
+        * 2.) If prealloc returns ENOSPC, the file range is still zero-valued
+        * by virtue of the hole punch.
         */
-       start_boundary = round_up(offset, granularity);
-       end_boundary = round_down(offset + len, granularity);
-
-       ASSERT(start_boundary >= offset);
-       ASSERT(end_boundary <= offset + len);
-
-       if (start_boundary < end_boundary - 1) {
-               /*
-                * Writeback the range to ensure any inode size updates due to
-                * appending writes make it to disk (otherwise we could just
-                * punch out the delalloc blocks).
-                */
-               error = filemap_write_and_wait_range(VFS_I(ip)->i_mapping,
-                               start_boundary, end_boundary - 1);
-               if (error)
-                       goto out;
-               truncate_pagecache_range(VFS_I(ip), start_boundary,
-                                        end_boundary - 1);
-
-               /* convert the blocks */
-               error = xfs_alloc_file_space(ip, start_boundary,
-                                       end_boundary - start_boundary - 1,
-                                       XFS_BMAPI_PREALLOC | XFS_BMAPI_CONVERT);
-               if (error)
-                       goto out;
-
-               /* We've handled the interior of the range, now for the edges */
-               if (start_boundary != offset) {
-                       error = xfs_iozero(ip, offset, start_boundary - offset);
-                       if (error)
-                               goto out;
-               }
-
-               if (end_boundary != offset + len)
-                       error = xfs_iozero(ip, end_boundary,
-                                          offset + len - end_boundary);
-
-       } else {
-               /*
-                * It's either a sub-granularity range or the range spanned lies
-                * partially across two adjacent blocks.
-                */
-               error = xfs_iozero(ip, offset, len);
-       }
+       error = xfs_free_file_space(ip, offset, len);
+       if (error)
+               goto out;
 
+       error = xfs_alloc_file_space(ip, round_down(offset, blksize),
+                                    round_up(offset + len, blksize) -
+                                    round_down(offset, blksize),
+                                    XFS_BMAPI_PREALLOC);
 out:
        return error;
 
index f1deb96..894924a 100644 (file)
@@ -236,8 +236,10 @@ xfs_bulkstat_grab_ichunk(
        XFS_WANT_CORRUPTED_RETURN(stat == 1);
 
        /* Check if the record contains the inode in request */
-       if (irec->ir_startino + XFS_INODES_PER_CHUNK <= agino)
-               return -EINVAL;
+       if (irec->ir_startino + XFS_INODES_PER_CHUNK <= agino) {
+               *icount = 0;
+               return 0;
+       }
 
        idx = agino - irec->ir_startino + 1;
        if (idx < XFS_INODES_PER_CHUNK &&
@@ -262,75 +264,76 @@ xfs_bulkstat_grab_ichunk(
 
 #define XFS_BULKSTAT_UBLEFT(ubleft)    ((ubleft) >= statstruct_size)
 
+struct xfs_bulkstat_agichunk {
+       char            __user **ac_ubuffer;/* pointer into user's buffer */
+       int             ac_ubleft;      /* bytes left in user's buffer */
+       int             ac_ubelem;      /* spaces used in user's buffer */
+};
+
 /*
  * Process inodes in chunk with a pointer to a formatter function
  * that will iget the inode and fill in the appropriate structure.
  */
-int
+static int
 xfs_bulkstat_ag_ichunk(
        struct xfs_mount                *mp,
        xfs_agnumber_t                  agno,
        struct xfs_inobt_rec_incore     *irbp,
        bulkstat_one_pf                 formatter,
        size_t                          statstruct_size,
-       struct xfs_bulkstat_agichunk    *acp)
+       struct xfs_bulkstat_agichunk    *acp,
+       xfs_agino_t                     *last_agino)
 {
-       xfs_ino_t                       lastino = acp->ac_lastino;
        char                            __user **ubufp = acp->ac_ubuffer;
-       int                             ubleft = acp->ac_ubleft;
-       int                             ubelem = acp->ac_ubelem;
-       int                             chunkidx, clustidx;
+       int                             chunkidx;
        int                             error = 0;
-       xfs_agino_t                     agino;
+       xfs_agino_t                     agino = irbp->ir_startino;
 
-       for (agino = irbp->ir_startino, chunkidx = clustidx = 0;
-            XFS_BULKSTAT_UBLEFT(ubleft) &&
-            irbp->ir_freecount < XFS_INODES_PER_CHUNK;
-            chunkidx++, clustidx++, agino++) {
-               int             fmterror;       /* bulkstat formatter result */
+       for (chunkidx = 0; chunkidx < XFS_INODES_PER_CHUNK;
+            chunkidx++, agino++) {
+               int             fmterror;
                int             ubused;
-               xfs_ino_t       ino = XFS_AGINO_TO_INO(mp, agno, agino);
 
-               ASSERT(chunkidx < XFS_INODES_PER_CHUNK);
+               /* inode won't fit in buffer, we are done */
+               if (acp->ac_ubleft < statstruct_size)
+                       break;
 
                /* Skip if this inode is free */
-               if (XFS_INOBT_MASK(chunkidx) & irbp->ir_free) {
-                       lastino = ino;
+               if (XFS_INOBT_MASK(chunkidx) & irbp->ir_free)
                        continue;
-               }
-
-               /*
-                * Count used inodes as free so we can tell when the
-                * chunk is used up.
-                */
-               irbp->ir_freecount++;
 
                /* Get the inode and fill in a single buffer */
                ubused = statstruct_size;
-               error = formatter(mp, ino, *ubufp, ubleft, &ubused, &fmterror);
-               if (fmterror == BULKSTAT_RV_NOTHING) {
-                       if (error && error != -ENOENT && error != -EINVAL) {
-                               ubleft = 0;
-                               break;
-                       }
-                       lastino = ino;
-                       continue;
-               }
-               if (fmterror == BULKSTAT_RV_GIVEUP) {
-                       ubleft = 0;
+               error = formatter(mp, XFS_AGINO_TO_INO(mp, agno, agino),
+                                 *ubufp, acp->ac_ubleft, &ubused, &fmterror);
+
+               if (fmterror == BULKSTAT_RV_GIVEUP ||
+                   (error && error != -ENOENT && error != -EINVAL)) {
+                       acp->ac_ubleft = 0;
                        ASSERT(error);
                        break;
                }
-               if (*ubufp)
-                       *ubufp += ubused;
-               ubleft -= ubused;
-               ubelem++;
-               lastino = ino;
+
+               /* be careful not to leak error if at end of chunk */
+               if (fmterror == BULKSTAT_RV_NOTHING || error) {
+                       error = 0;
+                       continue;
+               }
+
+               *ubufp += ubused;
+               acp->ac_ubleft -= ubused;
+               acp->ac_ubelem++;
        }
 
-       acp->ac_lastino = lastino;
-       acp->ac_ubleft = ubleft;
-       acp->ac_ubelem = ubelem;
+       /*
+        * Post-update *last_agino. At this point, agino will always point one
+        * inode past the last inode we processed successfully. Hence we
+        * substract that inode when setting the *last_agino cursor so that we
+        * return the correct cookie to userspace. On the next bulkstat call,
+        * the inode under the lastino cookie will be skipped as we have already
+        * processed it here.
+        */
+       *last_agino = agino - 1;
 
        return error;
 }
@@ -353,45 +356,33 @@ xfs_bulkstat(
        xfs_agino_t             agino;  /* inode # in allocation group */
        xfs_agnumber_t          agno;   /* allocation group number */
        xfs_btree_cur_t         *cur;   /* btree cursor for ialloc btree */
-       int                     end_of_ag; /* set if we've seen the ag end */
-       int                     error;  /* error code */
-       int                     fmterror;/* bulkstat formatter result */
-       int                     i;      /* loop index */
-       int                     icount; /* count of inodes good in irbuf */
        size_t                  irbsize; /* size of irec buffer in bytes */
-       xfs_ino_t               ino;    /* inode number (filesystem) */
-       xfs_inobt_rec_incore_t  *irbp;  /* current irec buffer pointer */
        xfs_inobt_rec_incore_t  *irbuf; /* start of irec buffer */
-       xfs_inobt_rec_incore_t  *irbufend; /* end of good irec buffer entries */
-       xfs_ino_t               lastino; /* last inode number returned */
        int                     nirbuf; /* size of irbuf */
-       int                     rval;   /* return value error code */
-       int                     tmp;    /* result value from btree calls */
        int                     ubcount; /* size of user's buffer */
-       int                     ubleft; /* bytes left in user's buffer */
-       char                    __user *ubufp;  /* pointer into user's buffer */
-       int                     ubelem; /* spaces used in user's buffer */
+       struct xfs_bulkstat_agichunk ac;
+       int                     error = 0;
 
        /*
         * Get the last inode value, see if there's nothing to do.
         */
-       ino = (xfs_ino_t)*lastinop;
-       lastino = ino;
-       agno = XFS_INO_TO_AGNO(mp, ino);
-       agino = XFS_INO_TO_AGINO(mp, ino);
+       agno = XFS_INO_TO_AGNO(mp, *lastinop);
+       agino = XFS_INO_TO_AGINO(mp, *lastinop);
        if (agno >= mp->m_sb.sb_agcount ||
-           ino != XFS_AGINO_TO_INO(mp, agno, agino)) {
+           *lastinop != XFS_AGINO_TO_INO(mp, agno, agino)) {
                *done = 1;
                *ubcountp = 0;
                return 0;
        }
 
        ubcount = *ubcountp; /* statstruct's */
-       ubleft = ubcount * statstruct_size; /* bytes */
-       *ubcountp = ubelem = 0;
+       ac.ac_ubuffer = &ubuffer;
+       ac.ac_ubleft = ubcount * statstruct_size; /* bytes */;
+       ac.ac_ubelem = 0;
+
+       *ubcountp = 0;
        *done = 0;
-       fmterror = 0;
-       ubufp = ubuffer;
+
        irbuf = kmem_zalloc_greedy(&irbsize, PAGE_SIZE, PAGE_SIZE * 4);
        if (!irbuf)
                return -ENOMEM;
@@ -402,9 +393,13 @@ xfs_bulkstat(
         * Loop over the allocation groups, starting from the last
         * inode returned; 0 means start of the allocation group.
         */
-       rval = 0;
-       while (XFS_BULKSTAT_UBLEFT(ubleft) && agno < mp->m_sb.sb_agcount) {
-               cond_resched();
+       while (agno < mp->m_sb.sb_agcount) {
+               struct xfs_inobt_rec_incore     *irbp = irbuf;
+               struct xfs_inobt_rec_incore     *irbufend = irbuf + nirbuf;
+               bool                            end_of_ag = false;
+               int                             icount = 0;
+               int                             stat;
+
                error = xfs_ialloc_read_agi(mp, NULL, agno, &agbp);
                if (error)
                        break;
@@ -414,10 +409,6 @@ xfs_bulkstat(
                 */
                cur = xfs_inobt_init_cursor(mp, NULL, agbp, agno,
                                            XFS_BTNUM_INO);
-               irbp = irbuf;
-               irbufend = irbuf + nirbuf;
-               end_of_ag = 0;
-               icount = 0;
                if (agino > 0) {
                        /*
                         * In the middle of an allocation group, we need to get
@@ -427,22 +418,23 @@ xfs_bulkstat(
 
                        error = xfs_bulkstat_grab_ichunk(cur, agino, &icount, &r);
                        if (error)
-                               break;
+                               goto del_cursor;
                        if (icount) {
                                irbp->ir_startino = r.ir_startino;
                                irbp->ir_freecount = r.ir_freecount;
                                irbp->ir_free = r.ir_free;
                                irbp++;
-                               agino = r.ir_startino + XFS_INODES_PER_CHUNK;
                        }
                        /* Increment to the next record */
-                       error = xfs_btree_increment(cur, 0, &tmp);
+                       error = xfs_btree_increment(cur, 0, &stat);
                } else {
                        /* Start of ag.  Lookup the first inode chunk */
-                       error = xfs_inobt_lookup(cur, 0, XFS_LOOKUP_GE, &tmp);
+                       error = xfs_inobt_lookup(cur, 0, XFS_LOOKUP_GE, &stat);
+               }
+               if (error || stat == 0) {
+                       end_of_ag = true;
+                       goto del_cursor;
                }
-               if (error)
-                       break;
 
                /*
                 * Loop through inode btree records in this ag,
@@ -451,10 +443,10 @@ xfs_bulkstat(
                while (irbp < irbufend && icount < ubcount) {
                        struct xfs_inobt_rec_incore     r;
 
-                       error = xfs_inobt_get_rec(cur, &r, &i);
-                       if (error || i == 0) {
-                               end_of_ag = 1;
-                               break;
+                       error = xfs_inobt_get_rec(cur, &r, &stat);
+                       if (error || stat == 0) {
+                               end_of_ag = true;
+                               goto del_cursor;
                        }
 
                        /*
@@ -469,77 +461,79 @@ xfs_bulkstat(
                                irbp++;
                                icount += XFS_INODES_PER_CHUNK - r.ir_freecount;
                        }
-                       /*
-                        * Set agino to after this chunk and bump the cursor.
-                        */
-                       agino = r.ir_startino + XFS_INODES_PER_CHUNK;
-                       error = xfs_btree_increment(cur, 0, &tmp);
+                       error = xfs_btree_increment(cur, 0, &stat);
+                       if (error || stat == 0) {
+                               end_of_ag = true;
+                               goto del_cursor;
+                       }
                        cond_resched();
                }
+
                /*
-                * Drop the btree buffers and the agi buffer.
-                * We can't hold any of the locks these represent
-                * when calling iget.
+                * Drop the btree buffers and the agi buffer as we can't hold any
+                * of the locks these represent when calling iget. If there is a
+                * pending error, then we are done.
                 */
+del_cursor:
                xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
                xfs_buf_relse(agbp);
+               if (error)
+                       break;
                /*
-                * Now format all the good inodes into the user's buffer.
+                * Now format all the good inodes into the user's buffer. The
+                * call to xfs_bulkstat_ag_ichunk() sets up the agino pointer
+                * for the next loop iteration.
                 */
                irbufend = irbp;
                for (irbp = irbuf;
-                    irbp < irbufend && XFS_BULKSTAT_UBLEFT(ubleft); irbp++) {
-                       struct xfs_bulkstat_agichunk ac;
-
-                       ac.ac_lastino = lastino;
-                       ac.ac_ubuffer = &ubuffer;
-                       ac.ac_ubleft = ubleft;
-                       ac.ac_ubelem = ubelem;
+                    irbp < irbufend && ac.ac_ubleft >= statstruct_size;
+                    irbp++) {
                        error = xfs_bulkstat_ag_ichunk(mp, agno, irbp,
-                                       formatter, statstruct_size, &ac);
+                                       formatter, statstruct_size, &ac,
+                                       &agino);
                        if (error)
-                               rval = error;
-
-                       lastino = ac.ac_lastino;
-                       ubleft = ac.ac_ubleft;
-                       ubelem = ac.ac_ubelem;
+                               break;
 
                        cond_resched();
                }
+
                /*
-                * Set up for the next loop iteration.
+                * If we've run out of space or had a formatting error, we
+                * are now done
                 */
-               if (XFS_BULKSTAT_UBLEFT(ubleft)) {
-                       if (end_of_ag) {
-                               agno++;
-                               agino = 0;
-                       } else
-                               agino = XFS_INO_TO_AGINO(mp, lastino);
-               } else
+               if (ac.ac_ubleft < statstruct_size || error)
                        break;
+
+               if (end_of_ag) {
+                       agno++;
+                       agino = 0;
+               }
        }
        /*
         * Done, we're either out of filesystem or space to put the data.
         */
        kmem_free(irbuf);
-       *ubcountp = ubelem;
+       *ubcountp = ac.ac_ubelem;
+
        /*
-        * Found some inodes, return them now and return the error next time.
+        * We found some inodes, so clear the error status and return them.
+        * The lastino pointer will point directly at the inode that triggered
+        * any error that occurred, so on the next call the error will be
+        * triggered again and propagated to userspace as there will be no
+        * formatted inodes in the buffer.
         */
-       if (ubelem)
-               rval = 0;
-       if (agno >= mp->m_sb.sb_agcount) {
-               /*
-                * If we ran out of filesystem, mark lastino as off
-                * the end of the filesystem, so the next call
-                * will return immediately.
-                */
-               *lastinop = (xfs_ino_t)XFS_AGINO_TO_INO(mp, agno, 0);
+       if (ac.ac_ubelem)
+               error = 0;
+
+       /*
+        * If we ran out of filesystem, lastino will point off the end of
+        * the filesystem so the next call will return immediately.
+        */
+       *lastinop = XFS_AGINO_TO_INO(mp, agno, agino);
+       if (agno >= mp->m_sb.sb_agcount)
                *done = 1;
-       } else
-               *lastinop = (xfs_ino_t)lastino;
 
-       return rval;
+       return error;
 }
 
 int
index aaed080..6ea8b39 100644 (file)
@@ -30,22 +30,6 @@ typedef int (*bulkstat_one_pf)(struct xfs_mount      *mp,
                               int              *ubused,
                               int              *stat);
 
-struct xfs_bulkstat_agichunk {
-       xfs_ino_t       ac_lastino;     /* last inode returned */
-       char            __user **ac_ubuffer;/* pointer into user's buffer */
-       int             ac_ubleft;      /* bytes left in user's buffer */
-       int             ac_ubelem;      /* spaces used in user's buffer */
-};
-
-int
-xfs_bulkstat_ag_ichunk(
-       struct xfs_mount                *mp,
-       xfs_agnumber_t                  agno,
-       struct xfs_inobt_rec_incore     *irbp,
-       bulkstat_one_pf                 formatter,
-       size_t                          statstruct_size,
-       struct xfs_bulkstat_agichunk    *acp);
-
 /*
  * Values for stat return value.
  */
index a929f86..d72b5b3 100644 (file)
@@ -60,7 +60,7 @@
 #define ESC1_CLK_SRC                   43
 #define HDMI_CLK_SRC                   44
 #define VSYNC_CLK_SRC                  45
-#define RBCPR_CLK_SRC                  46
+#define MMSS_RBCPR_CLK_SRC             46
 #define RBBMTIMER_CLK_SRC              47
 #define MAPLE_CLK_SRC                  48
 #define VDP_CLK_SRC                    49
index d6b56b2..801c0ac 100644 (file)
 #define VF610_CLK_FASK_CLK_SEL         8
 #define VF610_CLK_AUDIO_EXT            9
 #define VF610_CLK_ENET_EXT             10
-#define VF610_CLK_PLL1_MAIN            11
+#define VF610_CLK_PLL1_SYS             11
 #define VF610_CLK_PLL1_PFD1            12
 #define VF610_CLK_PLL1_PFD2            13
 #define VF610_CLK_PLL1_PFD3            14
 #define VF610_CLK_PLL1_PFD4            15
-#define VF610_CLK_PLL2_MAIN            16
+#define VF610_CLK_PLL2_BUS             16
 #define VF610_CLK_PLL2_PFD1            17
 #define VF610_CLK_PLL2_PFD2            18
 #define VF610_CLK_PLL2_PFD3            19
 #define VF610_CLK_PLL2_PFD4            20
-#define VF610_CLK_PLL3_MAIN            21
+#define VF610_CLK_PLL3_USB_OTG         21
 #define VF610_CLK_PLL3_PFD1            22
 #define VF610_CLK_PLL3_PFD2            23
 #define VF610_CLK_PLL3_PFD3            24
 #define VF610_CLK_PLL3_PFD4            25
-#define VF610_CLK_PLL4_MAIN            26
-#define VF610_CLK_PLL5_MAIN            27
-#define VF610_CLK_PLL6_MAIN            28
+#define VF610_CLK_PLL4_AUDIO           26
+#define VF610_CLK_PLL5_ENET            27
+#define VF610_CLK_PLL6_VIDEO           28
 #define VF610_CLK_PLL3_MAIN_DIV                29
 #define VF610_CLK_PLL4_MAIN_DIV                30
 #define VF610_CLK_PLL6_MAIN_DIV                31
 #define VF610_CLK_DMAMUX3              153
 #define VF610_CLK_FLEXCAN0_EN          154
 #define VF610_CLK_FLEXCAN1_EN          155
-#define VF610_CLK_PLL7_MAIN            156
+#define VF610_CLK_PLL7_USB_HOST                156
 #define VF610_CLK_USBPHY0              157
 #define VF610_CLK_USBPHY1              158
-#define VF610_CLK_END                  159
+#define VF610_CLK_LVDS1_IN             159
+#define VF610_CLK_ANACLK1              160
+#define VF610_CLK_PLL1_BYPASS_SRC      161
+#define VF610_CLK_PLL2_BYPASS_SRC      162
+#define VF610_CLK_PLL3_BYPASS_SRC      163
+#define VF610_CLK_PLL4_BYPASS_SRC      164
+#define VF610_CLK_PLL5_BYPASS_SRC      165
+#define VF610_CLK_PLL6_BYPASS_SRC      166
+#define VF610_CLK_PLL7_BYPASS_SRC      167
+#define VF610_CLK_PLL1                 168
+#define VF610_CLK_PLL2                 169
+#define VF610_CLK_PLL3                 170
+#define VF610_CLK_PLL4                 171
+#define VF610_CLK_PLL5                 172
+#define VF610_CLK_PLL6                 173
+#define VF610_CLK_PLL7                 174
+#define VF610_PLL1_BYPASS              175
+#define VF610_PLL2_BYPASS              176
+#define VF610_PLL3_BYPASS              177
+#define VF610_PLL4_BYPASS              178
+#define VF610_PLL5_BYPASS              179
+#define VF610_PLL6_BYPASS              180
+#define VF610_PLL7_BYPASS              181
+#define VF610_CLK_END                  182
 
 #endif /* __DT_BINDINGS_CLOCK_VF610_H */
index 3d33794..7448edf 100644 (file)
@@ -40,8 +40,8 @@
 
 /* Active pin states */
 #define PIN_OUTPUT             (0 | PULL_DIS)
-#define PIN_OUTPUT_PULLUP      (PIN_OUTPUT | PULL_ENA | PULL_UP)
-#define PIN_OUTPUT_PULLDOWN    (PIN_OUTPUT | PULL_ENA)
+#define PIN_OUTPUT_PULLUP      (PULL_UP)
+#define PIN_OUTPUT_PULLDOWN    (0)
 #define PIN_INPUT              (INPUT_EN | PULL_DIS)
 #define PIN_INPUT_SLEW         (INPUT_EN | SLEWCONTROL)
 #define PIN_INPUT_PULLUP       (PULL_ENA | INPUT_EN | PULL_UP)
index be5fd38..5d858e0 100644 (file)
  * position @h. For example
  * GENMASK_ULL(39, 21) gives us the 64bit vector 0x000000ffffe00000.
  */
-#define GENMASK(h, l)          (((U32_C(1) << ((h) - (l) + 1)) - 1) << (l))
-#define GENMASK_ULL(h, l)      (((U64_C(1) << ((h) - (l) + 1)) - 1) << (l))
+#define GENMASK(h, l) \
+       (((~0UL) << (l)) & (~0UL >> (BITS_PER_LONG - 1 - (h))))
+
+#define GENMASK_ULL(h, l) \
+       (((~0ULL) << (l)) & (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h))))
 
 extern unsigned int __sw_hweight8(unsigned int w);
 extern unsigned int __sw_hweight16(unsigned int w);
index 4e2bd4c..0995c2d 100644 (file)
@@ -46,6 +46,7 @@ extern unsigned long init_bootmem_node(pg_data_t *pgdat,
 extern unsigned long init_bootmem(unsigned long addr, unsigned long memend);
 
 extern unsigned long free_all_bootmem(void);
+extern void reset_node_managed_pages(pg_data_t *pgdat);
 extern void reset_all_zones_managed_pages(void);
 
 extern void free_bootmem_node(pg_data_t *pgdat,
index 6992afc..b37ea95 100644 (file)
@@ -99,6 +99,12 @@ inval_skb:
        return 1;
 }
 
+static inline bool can_is_canfd_skb(const struct sk_buff *skb)
+{
+       /* the CAN specific type of skb is identified by its data length */
+       return skb->len == CANFD_MTU;
+}
+
 /* get data length from can_dlc with sanitized can_dlc */
 u8 can_dlc2len(u8 can_dlc);
 
index be21af1..2839c63 100644 (file)
@@ -352,7 +352,6 @@ struct clk_divider {
 #define CLK_DIVIDER_READ_ONLY          BIT(5)
 
 extern const struct clk_ops clk_divider_ops;
-extern const struct clk_ops clk_divider_ro_ops;
 struct clk *clk_register_divider(struct device *dev, const char *name,
                const char *parent_name, unsigned long flags,
                void __iomem *reg, u8 shift, u8 width,
index 0430ed0..a93438b 100644 (file)
@@ -18,12 +18,12 @@ struct cma;
 extern phys_addr_t cma_get_base(struct cma *cma);
 extern unsigned long cma_get_size(struct cma *cma);
 
-extern int __init cma_declare_contiguous(phys_addr_t size,
-                       phys_addr_t base, phys_addr_t limit,
+extern int __init cma_declare_contiguous(phys_addr_t base,
+                       phys_addr_t size, phys_addr_t limit,
                        phys_addr_t alignment, unsigned int order_per_bit,
                        bool fixed, struct cma **res_cma);
-extern int cma_init_reserved_mem(phys_addr_t size,
-                                       phys_addr_t base, int order_per_bit,
+extern int cma_init_reserved_mem(phys_addr_t base,
+                                       phys_addr_t size, int order_per_bit,
                                        struct cma **res_cma);
 extern struct page *cma_alloc(struct cma *cma, int count, unsigned int align);
 extern bool cma_release(struct cma *cma, struct page *pages, int count);
index 8bbd7bc..03fa332 100644 (file)
@@ -72,7 +72,7 @@ struct iio_event_data {
 
 #define IIO_EVENT_CODE_EXTRACT_TYPE(mask) ((mask >> 56) & 0xFF)
 
-#define IIO_EVENT_CODE_EXTRACT_DIR(mask) ((mask >> 48) & 0xCF)
+#define IIO_EVENT_CODE_EXTRACT_DIR(mask) ((mask >> 48) & 0x7F)
 
 #define IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(mask) ((mask >> 32) & 0xFF)
 
index 0068708..0a21fbe 100644 (file)
@@ -242,7 +242,7 @@ static inline void in_dev_put(struct in_device *idev)
 static __inline__ __be32 inet_make_mask(int logmask)
 {
        if (logmask)
-               return htonl(~((1<<(32-logmask))-1));
+               return htonl(~((1U<<(32-logmask))-1));
        return 0;
 }
 
index 8422b4e..b9376cd 100644 (file)
@@ -77,11 +77,6 @@ static inline unsigned int kstat_cpu_irqs_sum(unsigned int cpu)
        return kstat_cpu(cpu).irqs_sum;
 }
 
-/*
- * Lock/unlock the current runqueue - to extract task statistics:
- */
-extern unsigned long long task_delta_exec(struct task_struct *);
-
 extern void account_user_time(struct task_struct *, cputime_t, cputime_t);
 extern void account_system_time(struct task_struct *, int, cputime_t, cputime_t);
 extern void account_steal_time(cputime_t);
index ea53b04..a6059bd 100644 (file)
@@ -703,7 +703,7 @@ void kvm_arch_sync_events(struct kvm *kvm);
 int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu);
 void kvm_vcpu_kick(struct kvm_vcpu *vcpu);
 
-bool kvm_is_mmio_pfn(pfn_t pfn);
+bool kvm_is_reserved_pfn(pfn_t pfn);
 
 struct kvm_irq_ack_notifier {
        struct hlist_node link;
index f34723f..910e3aa 100644 (file)
@@ -141,6 +141,7 @@ struct arizona {
 
        uint16_t dac_comp_coeff;
        uint8_t dac_comp_enabled;
+       struct mutex dac_comp_lock;
 };
 
 int arizona_clk32k_enable(struct arizona *arizona);
index cb01496..8e1cdbe 100644 (file)
@@ -99,12 +99,6 @@ struct davinci_vcif {
        dma_addr_t dma_rx_addr;
 };
 
-struct cq93vc {
-       struct platform_device *pdev;
-       struct snd_soc_codec *codec;
-       u32 sysclk;
-};
-
 struct davinci_vc;
 
 struct davinci_vc {
@@ -122,7 +116,6 @@ struct davinci_vc {
 
        /* Client devices */
        struct davinci_vcif davinci_vcif;
-       struct cq93vc cq93vc;
 };
 
 #endif
index fc17d56..582e67f 100644 (file)
@@ -330,6 +330,13 @@ enum max77693_irq_source {
        MAX77693_IRQ_GROUP_NR,
 };
 
+#define SRC_IRQ_CHARGER                        BIT(0)
+#define SRC_IRQ_TOP                    BIT(1)
+#define SRC_IRQ_FLASH                  BIT(2)
+#define SRC_IRQ_MUIC                   BIT(3)
+#define SRC_IRQ_ALL                    (SRC_IRQ_CHARGER | SRC_IRQ_TOP \
+                                               | SRC_IRQ_FLASH | SRC_IRQ_MUIC)
+
 #define LED_IRQ_FLED2_OPEN             BIT(0)
 #define LED_IRQ_FLED2_SHORT            BIT(1)
 #define LED_IRQ_FLED1_OPEN             BIT(2)
index 48bf12e..ffe66e3 100644 (file)
@@ -431,6 +431,15 @@ struct zone {
         */
        int                     nr_migrate_reserve_block;
 
+#ifdef CONFIG_MEMORY_ISOLATION
+       /*
+        * Number of isolated pageblock. It is used to solve incorrect
+        * freepage counting problem due to racy retrieving migratetype
+        * of pageblock. Protected by zone->lock.
+        */
+       unsigned long           nr_isolate_pageblock;
+#endif
+
 #ifdef CONFIG_MEMORY_HOTPLUG
        /* see spanned/present_pages for more description */
        seqlock_t               span_seqlock;
index 983876f..47ebb4f 100644 (file)
@@ -1224,11 +1224,22 @@ struct nfs41_free_stateid_res {
        unsigned int                    status;
 };
 
+static inline void
+nfs_free_pnfs_ds_cinfo(struct pnfs_ds_commit_info *cinfo)
+{
+       kfree(cinfo->buckets);
+}
+
 #else
 
 struct pnfs_ds_commit_info {
 };
 
+static inline void
+nfs_free_pnfs_ds_cinfo(struct pnfs_ds_commit_info *cinfo)
+{
+}
+
 #endif /* CONFIG_NFS_V4_1 */
 
 #ifdef CONFIG_NFS_V4_2
index 6545e7a..29f0adc 100644 (file)
@@ -267,14 +267,12 @@ extern int of_property_read_u64(const struct device_node *np,
 extern int of_property_read_string(struct device_node *np,
                                   const char *propname,
                                   const char **out_string);
-extern int of_property_read_string_index(struct device_node *np,
-                                        const char *propname,
-                                        int index, const char **output);
 extern int of_property_match_string(struct device_node *np,
                                    const char *propname,
                                    const char *string);
-extern int of_property_count_strings(struct device_node *np,
-                                    const char *propname);
+extern int of_property_read_string_helper(struct device_node *np,
+                                             const char *propname,
+                                             const char **out_strs, size_t sz, int index);
 extern int of_device_is_compatible(const struct device_node *device,
                                   const char *);
 extern int of_device_is_available(const struct device_node *device);
@@ -486,15 +484,9 @@ static inline int of_property_read_string(struct device_node *np,
        return -ENOSYS;
 }
 
-static inline int of_property_read_string_index(struct device_node *np,
-                                               const char *propname, int index,
-                                               const char **out_string)
-{
-       return -ENOSYS;
-}
-
-static inline int of_property_count_strings(struct device_node *np,
-                                           const char *propname)
+static inline int of_property_read_string_helper(struct device_node *np,
+                                                const char *propname,
+                                                const char **out_strs, size_t sz, int index)
 {
        return -ENOSYS;
 }
@@ -667,6 +659,70 @@ static inline int of_property_count_u64_elems(const struct device_node *np,
        return of_property_count_elems_of_size(np, propname, sizeof(u64));
 }
 
+/**
+ * of_property_read_string_array() - Read an array of strings from a multiple
+ * strings property.
+ * @np:                device node from which the property value is to be read.
+ * @propname:  name of the property to be searched.
+ * @out_strs:  output array of string pointers.
+ * @sz:                number of array elements to read.
+ *
+ * Search for a property in a device tree node and retrieve a list of
+ * terminated string values (pointer to data, not a copy) in that property.
+ *
+ * If @out_strs is NULL, the number of strings in the property is returned.
+ */
+static inline int of_property_read_string_array(struct device_node *np,
+                                               const char *propname, const char **out_strs,
+                                               size_t sz)
+{
+       return of_property_read_string_helper(np, propname, out_strs, sz, 0);
+}
+
+/**
+ * of_property_count_strings() - Find and return the number of strings from a
+ * multiple strings property.
+ * @np:                device node from which the property value is to be read.
+ * @propname:  name of the property to be searched.
+ *
+ * Search for a property in a device tree node and retrieve the number of null
+ * terminated string contain in it. Returns the number of strings on
+ * success, -EINVAL if the property does not exist, -ENODATA if property
+ * does not have a value, and -EILSEQ if the string is not null-terminated
+ * within the length of the property data.
+ */
+static inline int of_property_count_strings(struct device_node *np,
+                                           const char *propname)
+{
+       return of_property_read_string_helper(np, propname, NULL, 0, 0);
+}
+
+/**
+ * of_property_read_string_index() - Find and read a string from a multiple
+ * strings property.
+ * @np:                device node from which the property value is to be read.
+ * @propname:  name of the property to be searched.
+ * @index:     index of the string in the list of strings
+ * @out_string:        pointer to null terminated return string, modified only if
+ *             return value is 0.
+ *
+ * Search for a property in a device tree node and retrieve a null
+ * terminated string value (pointer to data, not a copy) in the list of strings
+ * contained in that property.
+ * Returns 0 on success, -EINVAL if the property does not exist, -ENODATA if
+ * property does not have a value, and -EILSEQ if the string is not
+ * null-terminated within the length of the property data.
+ *
+ * The out_string pointer is modified only if a valid string can be decoded.
+ */
+static inline int of_property_read_string_index(struct device_node *np,
+                                               const char *propname,
+                                               int index, const char **output)
+{
+       int rc = of_property_read_string_helper(np, propname, output, 1, index);
+       return rc < 0 ? rc : 0;
+}
+
 /**
  * of_property_read_bool - Findfrom a property
  * @np:                device node from which the property value is to be read.
index 3fff8e7..2dc1e16 100644 (file)
@@ -2,6 +2,10 @@
 #define __LINUX_PAGEISOLATION_H
 
 #ifdef CONFIG_MEMORY_ISOLATION
+static inline bool has_isolate_pageblock(struct zone *zone)
+{
+       return zone->nr_isolate_pageblock;
+}
 static inline bool is_migrate_isolate_page(struct page *page)
 {
        return get_pageblock_migratetype(page) == MIGRATE_ISOLATE;
@@ -11,6 +15,10 @@ static inline bool is_migrate_isolate(int migratetype)
        return migratetype == MIGRATE_ISOLATE;
 }
 #else
+static inline bool has_isolate_pageblock(struct zone *zone)
+{
+       return false;
+}
 static inline bool is_migrate_isolate_page(struct page *page)
 {
        return false;
index 64dacb7..24c7728 100644 (file)
@@ -41,8 +41,13 @@ static inline acpi_handle acpi_pci_get_bridge_handle(struct pci_bus *pbus)
 
        if (pci_is_root_bus(pbus))
                dev = pbus->bridge;
-       else
+       else {
+               /* If pbus is a virtual bus, there is no bridge to it */
+               if (!pbus->self)
+                       return NULL;
+
                dev = &pbus->self->dev;
+       }
 
        return ACPI_HANDLE(dev);
 }
index 5be8db4..4c8ac5f 100644 (file)
@@ -331,6 +331,7 @@ struct pci_dev {
        unsigned int    is_added:1;
        unsigned int    is_busmaster:1; /* device is busmaster */
        unsigned int    no_msi:1;       /* device may not use msi */
+       unsigned int    no_64bit_msi:1; /* device may only use 32-bit MSIs */
        unsigned int    block_cfg_access:1;     /* config space access is blocked */
        unsigned int    broken_parity_status:1; /* Device generates false positive parity */
        unsigned int    irq_reroute_variant:2;  /* device needs IRQ rerouting variant */
index d5c89e0..51ce60c 100644 (file)
@@ -133,7 +133,13 @@ static inline bool __ref_is_percpu(struct percpu_ref *ref,
        /* paired with smp_store_release() in percpu_ref_reinit() */
        smp_read_barrier_depends();
 
-       if (unlikely(percpu_ptr & __PERCPU_REF_ATOMIC))
+       /*
+        * Theoretically, the following could test just ATOMIC; however,
+        * then we'd have to mask off DEAD separately as DEAD may be
+        * visible without ATOMIC if we race with percpu_ref_kill().  DEAD
+        * implies ATOMIC anyway.  Test them together.
+        */
+       if (unlikely(percpu_ptr & __PERCPU_REF_ATOMIC_DEAD))
                return false;
 
        *percpu_countp = (unsigned long __percpu *)percpu_ptr;
index a6591c6..5e0bc77 100644 (file)
@@ -27,6 +27,7 @@ struct samsung_i2s {
 #define QUIRK_NO_MUXPSR                (1 << 2)
 #define QUIRK_NEED_RSTCLR      (1 << 3)
 #define QUIRK_SUPPORTS_TDM     (1 << 4)
+#define QUIRK_SUPPORTS_IDMA    (1 << 5)
        /* Quirks of the I2S controller */
        u32 quirks;
        dma_addr_t idma_addr;
index 73e938b..2e0e06d 100644 (file)
@@ -72,8 +72,10 @@ struct generic_pm_domain {
        bool max_off_time_changed;
        bool cached_power_down_ok;
        struct gpd_cpuidle_data *cpuidle_data;
-       void (*attach_dev)(struct device *dev);
-       void (*detach_dev)(struct device *dev);
+       int (*attach_dev)(struct generic_pm_domain *domain,
+                         struct device *dev);
+       void (*detach_dev)(struct generic_pm_domain *domain,
+                          struct device *dev);
 };
 
 static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
@@ -104,7 +106,7 @@ struct generic_pm_domain_data {
        struct notifier_block nb;
        struct mutex lock;
        unsigned int refcount;
-       bool need_restore;
+       int need_restore;
 };
 
 #ifdef CONFIG_PM_GENERIC_DOMAINS
index 07e7945..e97fc65 100644 (file)
@@ -253,9 +253,6 @@ struct charger_manager {
        struct device *dev;
        struct charger_desc *desc;
 
-       struct power_supply *fuel_gauge;
-       struct power_supply **charger_stat;
-
 #ifdef CONFIG_THERMAL
        struct thermal_zone_device *tzd_batt;
 #endif
index 3ed0496..096dbce 100644 (file)
@@ -200,6 +200,12 @@ struct power_supply {
        void (*external_power_changed)(struct power_supply *psy);
        void (*set_charged)(struct power_supply *psy);
 
+       /*
+        * Set if thermal zone should not be created for this power supply.
+        * For example for virtual supplies forwarding calls to actual
+        * sensors or other supplies.
+        */
+       bool no_thermal;
        /* For APM emulation, think legacy userspace. */
        int use_for_apm;
 
index c5ed83f..4419b99 100644 (file)
@@ -27,6 +27,7 @@ struct spmi_device;
 struct regmap;
 struct regmap_range_cfg;
 struct regmap_field;
+struct snd_ac97;
 
 /* An enum of all the supported cache types */
 enum regcache_type {
@@ -340,6 +341,8 @@ struct regmap *regmap_init_spmi_ext(struct spmi_device *dev,
 struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id,
                                    void __iomem *regs,
                                    const struct regmap_config *config);
+struct regmap *regmap_init_ac97(struct snd_ac97 *ac97,
+                               const struct regmap_config *config);
 
 struct regmap *devm_regmap_init(struct device *dev,
                                const struct regmap_bus *bus,
@@ -356,6 +359,10 @@ struct regmap *devm_regmap_init_spmi_ext(struct spmi_device *dev,
 struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id,
                                         void __iomem *regs,
                                         const struct regmap_config *config);
+struct regmap *devm_regmap_init_ac97(struct snd_ac97 *ac97,
+                                    const struct regmap_config *config);
+
+bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
 
 /**
  * regmap_init_mmio(): Initialise register map
index 49a4d6f..e2c13cd 100644 (file)
@@ -97,7 +97,7 @@ __ring_buffer_alloc(unsigned long size, unsigned flags, struct lock_class_key *k
        __ring_buffer_alloc((size), (flags), &__key);   \
 })
 
-int ring_buffer_wait(struct ring_buffer *buffer, int cpu);
+int ring_buffer_wait(struct ring_buffer *buffer, int cpu, bool full);
 int ring_buffer_poll_wait(struct ring_buffer *buffer, int cpu,
                          struct file *filp, poll_table *poll_table);
 
index ec538fc..bb9b836 100644 (file)
@@ -256,7 +256,7 @@ struct ucred {
 #define MSG_EOF         MSG_FIN
 
 #define MSG_FASTOPEN   0x20000000      /* Send data in TCP SYN */
-#define MSG_CMSG_CLOEXEC 0x40000000    /* Set close_on_exit for file
+#define MSG_CMSG_CLOEXEC 0x40000000    /* Set close_on_exec for file
                                           descriptor received through
                                           SCM_RIGHTS */
 #if defined(CONFIG_COMPAT)
index d9fa68f..2a25dec 100644 (file)
@@ -34,7 +34,6 @@
  * @list: used to maintain a list of currently available transports
  * @name: the human-readable name of the transport
  * @maxsize: transport provided maximum packet size
- * @pref: Preferences of this transport
  * @def: set if this transport should be considered the default
  * @create: member function to create a new connection on this transport
  * @close: member function to discard a connection on this transport
index fe7994c..b2828a0 100644 (file)
@@ -37,6 +37,8 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
 int inet_ctl_sock_create(struct sock **sk, unsigned short family,
                         unsigned short type, unsigned char protocol,
                         struct net *net);
+int inet_recv_error(struct sock *sk, struct msghdr *msg, int len,
+                   int *addr_len);
 
 static inline void inet_ctl_sock_destroy(struct sock *sk)
 {
index 845c596..3ae969e 100644 (file)
@@ -396,14 +396,12 @@ struct nft_rule {
 /**
  *     struct nft_trans - nf_tables object update in transaction
  *
- *     @rcu_head: rcu head to defer release of transaction data
  *     @list: used internally
  *     @msg_type: message type
  *     @ctx: transaction context
  *     @data: internal information related to the transaction
  */
 struct nft_trans {
-       struct rcu_head                 rcu_head;
        struct list_head                list;
        int                             msg_type;
        struct nft_ctx                  ctx;
index a47790b..2a50a70 100644 (file)
@@ -100,6 +100,15 @@ static inline struct sk_buff *udp_tunnel_handle_offloads(struct sk_buff *skb,
        return iptunnel_handle_offloads(skb, udp_csum, type);
 }
 
+static inline void udp_tunnel_gro_complete(struct sk_buff *skb, int nhoff)
+{
+       struct udphdr *uh;
+
+       uh = (struct udphdr *)(skb->data + nhoff - sizeof(struct udphdr));
+       skb_shinfo(skb)->gso_type |= uh->check ?
+                               SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;
+}
+
 static inline void udp_tunnel_encap_enable(struct socket *sock)
 {
 #if IS_ENABLED(CONFIG_IPV6)
index d5f59f3..57cccd0 100644 (file)
@@ -8,6 +8,12 @@
 #define VNI_HASH_BITS  10
 #define VNI_HASH_SIZE  (1<<VNI_HASH_BITS)
 
+/* VXLAN protocol header */
+struct vxlanhdr {
+       __be32 vx_flags;
+       __be32 vx_vni;
+};
+
 struct vxlan_sock;
 typedef void (vxlan_rcv_t)(struct vxlan_sock *vh, struct sk_buff *skb, __be32 key);
 
@@ -45,6 +51,18 @@ int vxlan_xmit_skb(struct vxlan_sock *vs,
                   __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df,
                   __be16 src_port, __be16 dst_port, __be32 vni, bool xnet);
 
+static inline bool vxlan_gso_check(struct sk_buff *skb)
+{
+       if ((skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL) &&
+           (skb->inner_protocol_type != ENCAP_TYPE_ETHER ||
+            skb->inner_protocol != htons(ETH_P_TEB) ||
+            (skb_inner_mac_header(skb) - skb_transport_header(skb) !=
+             sizeof(struct udphdr) + sizeof(struct vxlanhdr))))
+               return false;
+
+       return true;
+}
+
 /* IP header + UDP + VXLAN + Ethernet header */
 #define VXLAN_HEADROOM (20 + 8 + 8 + 14)
 /* IPv6 header + UDP + VXLAN + Ethernet header */
index e862497..8bb00a2 100644 (file)
@@ -184,6 +184,8 @@ struct snd_pcm_ops {
 #define SNDRV_PCM_FMTBIT_DSD_U8                _SNDRV_PCM_FMTBIT(DSD_U8)
 #define SNDRV_PCM_FMTBIT_DSD_U16_LE    _SNDRV_PCM_FMTBIT(DSD_U16_LE)
 #define SNDRV_PCM_FMTBIT_DSD_U32_LE    _SNDRV_PCM_FMTBIT(DSD_U32_LE)
+#define SNDRV_PCM_FMTBIT_DSD_U16_BE    _SNDRV_PCM_FMTBIT(DSD_U16_BE)
+#define SNDRV_PCM_FMTBIT_DSD_U32_BE    _SNDRV_PCM_FMTBIT(DSD_U32_BE)
 
 #ifdef SNDRV_LITTLE_ENDIAN
 #define SNDRV_PCM_FMTBIT_S16           SNDRV_PCM_FMTBIT_S16_LE
index d76412b..83284ca 100644 (file)
 #define RSND_SSI_CLK_PIN_SHARE         (1 << 31)
 #define RSND_SSI_NO_BUSIF              (1 << 30) /* SSI+DMA without BUSIF */
 
-#define RSND_SSI(_dma_id, _pio_irq, _flags)            \
-{ .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags }
+#define RSND_SSI(_dma_id, _irq, _flags)                \
+{ .dma_id = _dma_id, .irq = _irq, .flags = _flags }
 #define RSND_SSI_UNUSED \
-{ .dma_id = -1, .pio_irq = -1, .flags = 0 }
+{ .dma_id = -1, .irq = -1, .flags = 0 }
 
 struct rsnd_ssi_platform_info {
        int dma_id;
-       int pio_irq;
+       int irq;
        u32 flags;
 };
 
index a535271..120d961 100644 (file)
@@ -23,6 +23,10 @@ struct rt5645_platform_data {
 
        unsigned int hp_det_gpio;
        bool gpio_hp_det_active_high;
+
+       /* true if codec's jd function is used */
+       bool en_jd_func;
+       unsigned int jd_mode;
 };
 
 #endif
index 082670e..d9eb7d8 100644 (file)
@@ -27,6 +27,16 @@ struct rt5677_platform_data {
        bool lout3_diff;
        /* DMIC2 clock source selection */
        enum rt5677_dmic2_clk dmic2_clk_pin;
+
+       /* configures GPIO, 0 - floating, 1 - pulldown, 2 - pullup */
+       u8 gpio_config[6];
+
+       /* jd1 can select 0 ~ 3 as OFF, GPIO1, GPIO2 and GPIO3 respectively */
+       unsigned int jd1_gpio;
+       /* jd2 and jd3 can select 0 ~ 3 as
+               OFF, GPIO4, GPIO5 and GPIO6 respectively */
+       unsigned int jd2_gpio;
+       unsigned int jd3_gpio;
 };
 
 #endif
index e8b3080..2df96b1 100644 (file)
@@ -206,7 +206,6 @@ struct snd_soc_dai_driver {
        /* DAI description */
        const char *name;
        unsigned int id;
-       int ac97_control;
        unsigned int base;
 
        /* DAI driver callbacks */
@@ -216,6 +215,8 @@ struct snd_soc_dai_driver {
        int (*resume)(struct snd_soc_dai *dai);
        /* compress dai */
        bool compress_dai;
+       /* DAI is also used for the control bus */
+       bool bus_control;
 
        /* ops */
        const struct snd_soc_dai_ops *ops;
@@ -241,7 +242,6 @@ struct snd_soc_dai {
        const char *name;
        int id;
        struct device *dev;
-       void *ac97_pdata;       /* platform_data for the ac97 codec */
 
        /* driver ops */
        struct snd_soc_dai_driver *driver;
@@ -268,7 +268,6 @@ struct snd_soc_dai {
        unsigned int sample_bits;
 
        /* parent platform/codec */
-       struct snd_soc_platform *platform;
        struct snd_soc_codec *codec;
        struct snd_soc_component *component;
 
@@ -276,8 +275,6 @@ struct snd_soc_dai {
        unsigned int tx_mask;
        unsigned int rx_mask;
 
-       struct snd_soc_card *card;
-
        struct list_head list;
 };
 
index 3a4d7da..89823cf 100644 (file)
@@ -435,7 +435,7 @@ void snd_soc_dapm_auto_nc_pins(struct snd_soc_card *card);
 unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol);
 
 /* Mostly internal - should not normally be used */
-void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm);
+void dapm_mark_endpoints_dirty(struct snd_soc_card *card);
 
 /* dapm path query */
 int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
@@ -508,9 +508,9 @@ struct snd_soc_dapm_path {
 
        /* status */
        u32 connect:1;  /* source and sink widgets are connected */
-       u32 walked:1;   /* path has been walked */
        u32 walking:1;  /* path is in the process of being walked */
        u32 weak:1;     /* path ignored for power management */
+       u32 is_supply:1;        /* At least one of the connected widgets is a supply */
 
        int (*connected)(struct snd_soc_dapm_widget *source,
                         struct snd_soc_dapm_widget *sink);
@@ -544,11 +544,13 @@ struct snd_soc_dapm_widget {
        unsigned char active:1;                 /* active stream on DAC, ADC's */
        unsigned char connected:1;              /* connected codec pin */
        unsigned char new:1;                    /* cnew complete */
-       unsigned char ext:1;                    /* has external widgets */
        unsigned char force:1;                  /* force state */
        unsigned char ignore_suspend:1;         /* kept enabled over suspend */
        unsigned char new_power:1;              /* power from this run */
        unsigned char power_checked:1;          /* power checked this run */
+       unsigned char is_supply:1;              /* Widget is a supply type widget */
+       unsigned char is_sink:1;                /* Widget is a sink type widget */
+       unsigned char is_source:1;              /* Widget is a source type widget */
        int subseq;                             /* sort within widget type */
 
        int (*power_check)(struct snd_soc_dapm_widget *w);
@@ -567,6 +569,7 @@ struct snd_soc_dapm_widget {
        struct list_head sinks;
 
        /* used during DAPM updates */
+       struct list_head work_list;
        struct list_head power_list;
        struct list_head dirty;
        int inputs;
index 2883a7a..98f2ade 100644 (file)
@@ -102,6 +102,8 @@ struct snd_soc_dpcm_runtime {
        /* state and update */
        enum snd_soc_dpcm_update runtime_update;
        enum snd_soc_dpcm_state state;
+
+       int trigger_pending; /* trigger cmd + 1 if pending, 0 if not */
 };
 
 /* can this BE stop and free */
index 7ba7130..b4fca9a 100644 (file)
        {.reg = xreg, .rreg = xreg, .shift = shift_left, \
        .rshift = shift_right, .max = xmax, .platform_max = xmax, \
        .invert = xinvert, .autodisable = xautodisable})
+#define SOC_DOUBLE_S_VALUE(xreg, shift_left, shift_right, xmin, xmax, xsign_bit, xinvert, xautodisable) \
+       ((unsigned long)&(struct soc_mixer_control) \
+       {.reg = xreg, .rreg = xreg, .shift = shift_left, \
+       .rshift = shift_right, .min = xmin, .max = xmax, .platform_max = xmax, \
+       .sign_bit = xsign_bit, .invert = xinvert, .autodisable = xautodisable})
 #define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, xautodisable) \
        SOC_DOUBLE_VALUE(xreg, xshift, xshift, xmax, xinvert, xautodisable)
 #define SOC_SINGLE_VALUE_EXT(xreg, xmax, xinvert) \
        .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
                  SNDRV_CTL_ELEM_ACCESS_READWRITE, \
        .tlv.p  = (tlv_array), \
-       .info   = snd_soc_info_volsw_s8, .get = snd_soc_get_volsw_s8, \
-       .put    = snd_soc_put_volsw_s8, \
-       .private_value = (unsigned long)&(struct soc_mixer_control) \
-               {.reg = xreg, .min = xmin, .max = xmax, \
-                .platform_max = xmax} }
+       .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
+       .put = snd_soc_put_volsw, \
+       .private_value = SOC_DOUBLE_S_VALUE(xreg, 0, 8, xmin, xmax, 7, 0, 0) }
 #define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xitems, xtexts) \
 {      .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
        .items = xitems, .texts = xtexts, \
@@ -366,8 +369,6 @@ struct snd_soc_jack_gpio;
 
 typedef int (*hw_write_t)(void *,const char* ,int);
 
-extern struct snd_ac97_bus_ops *soc_ac97_ops;
-
 enum snd_soc_pcm_subclass {
        SND_SOC_PCM_CLASS_PCM   = 0,
        SND_SOC_PCM_CLASS_BE    = 1,
@@ -409,13 +410,9 @@ int devm_snd_soc_register_component(struct device *dev,
                         const struct snd_soc_component_driver *cmpnt_drv,
                         struct snd_soc_dai_driver *dai_drv, int num_dai);
 void snd_soc_unregister_component(struct device *dev);
-int snd_soc_cache_sync(struct snd_soc_codec *codec);
 int snd_soc_cache_init(struct snd_soc_codec *codec);
 int snd_soc_cache_exit(struct snd_soc_codec *codec);
-int snd_soc_cache_write(struct snd_soc_codec *codec,
-                       unsigned int reg, unsigned int value);
-int snd_soc_cache_read(struct snd_soc_codec *codec,
-                      unsigned int reg, unsigned int *value);
+
 int snd_soc_platform_read(struct snd_soc_platform *platform,
                                        unsigned int reg);
 int snd_soc_platform_write(struct snd_soc_platform *platform,
@@ -500,14 +497,28 @@ int snd_soc_update_bits_locked(struct snd_soc_codec *codec,
 int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned int reg,
                                unsigned int mask, unsigned int value);
 
-int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
-       struct snd_ac97_bus_ops *ops, int num);
-void snd_soc_free_ac97_codec(struct snd_soc_codec *codec);
+#ifdef CONFIG_SND_SOC_AC97_BUS
+struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec);
+void snd_soc_free_ac97_codec(struct snd_ac97 *ac97);
 
 int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops);
 int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
                struct platform_device *pdev);
 
+extern struct snd_ac97_bus_ops *soc_ac97_ops;
+#else
+static inline int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
+       struct platform_device *pdev)
+{
+       return 0;
+}
+
+static inline int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops)
+{
+       return 0;
+}
+#endif
+
 /*
  *Controls
  */
@@ -545,12 +556,6 @@ int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol);
 int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol);
-int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_info *uinfo);
-int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol);
-int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol);
 int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_info *uinfo);
 int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
@@ -780,24 +785,18 @@ struct snd_soc_codec {
        struct device *dev;
        const struct snd_soc_codec_driver *driver;
 
-       struct mutex mutex;
        struct list_head list;
        struct list_head card_list;
 
        /* runtime */
-       struct snd_ac97 *ac97;  /* for ad-hoc ac97 devices */
        unsigned int cache_bypass:1; /* Suppress access to the cache */
        unsigned int suspended:1; /* Codec is in suspend PM state */
-       unsigned int ac97_registered:1; /* Codec has been AC97 registered */
-       unsigned int ac97_created:1; /* Codec has been created by SoC */
        unsigned int cache_init:1; /* codec cache has been initialized */
-       u32 cache_sync; /* Cache needs to be synced to hardware */
 
        /* codec IO */
        void *control_data; /* codec control (i2c/3wire) data */
        hw_write_t hw_write;
        void *reg_cache;
-       struct mutex cache_rw_mutex;
 
        /* component */
        struct snd_soc_component component;
@@ -860,8 +859,6 @@ struct snd_soc_platform_driver {
 
        int (*probe)(struct snd_soc_platform *);
        int (*remove)(struct snd_soc_platform *);
-       int (*suspend)(struct snd_soc_dai *dai);
-       int (*resume)(struct snd_soc_dai *dai);
        struct snd_soc_component_driver component_driver;
 
        /* pcm creation and destruction */
@@ -886,7 +883,7 @@ struct snd_soc_platform_driver {
 
 struct snd_soc_dai_link_component {
        const char *name;
-       const struct device_node *of_node;
+       struct device_node *of_node;
        const char *dai_name;
 };
 
@@ -894,8 +891,6 @@ struct snd_soc_platform {
        struct device *dev;
        const struct snd_soc_platform_driver *driver;
 
-       unsigned int suspended:1; /* platform is suspended */
-
        struct list_head list;
 
        struct snd_soc_component component;
@@ -990,7 +985,7 @@ struct snd_soc_codec_conf {
         * DT/OF node, but not both.
         */
        const char *dev_name;
-       const struct device_node *of_node;
+       struct device_node *of_node;
 
        /*
         * optional map of kcontrol, widget and path name prefixes that are
@@ -1007,7 +1002,7 @@ struct snd_soc_aux_dev {
         * DT/OF node, but not both.
         */
        const char *codec_name;
-       const struct device_node *codec_of_node;
+       struct device_node *codec_of_node;
 
        /* codec/machine specific init - e.g. add machine controls */
        int (*init)(struct snd_soc_component *component);
@@ -1264,6 +1259,17 @@ unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg);
 int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg,
        unsigned int val);
 
+/**
+ * snd_soc_cache_sync() - Sync the register cache with the hardware
+ * @codec: CODEC to sync
+ *
+ * Note: This function will call regcache_sync()
+ */
+static inline int snd_soc_cache_sync(struct snd_soc_codec *codec)
+{
+       return regcache_sync(codec->component.regmap);
+}
+
 /* component IO */
 int snd_soc_component_read(struct snd_soc_component *component,
        unsigned int reg, unsigned int *val);
@@ -1277,6 +1283,45 @@ void snd_soc_component_async_complete(struct snd_soc_component *component);
 int snd_soc_component_test_bits(struct snd_soc_component *component,
        unsigned int reg, unsigned int mask, unsigned int value);
 
+#ifdef CONFIG_REGMAP
+
+void snd_soc_component_init_regmap(struct snd_soc_component *component,
+       struct regmap *regmap);
+void snd_soc_component_exit_regmap(struct snd_soc_component *component);
+
+/**
+ * snd_soc_codec_init_regmap() - Initialize regmap instance for the CODEC
+ * @codec: The CODEC for which to initialize the regmap instance
+ * @regmap: The regmap instance that should be used by the CODEC
+ *
+ * This function allows deferred assignment of the regmap instance that is
+ * associated with the CODEC. Only use this if the regmap instance is not yet
+ * ready when the CODEC is registered. The function must also be called before
+ * the first IO attempt of the CODEC.
+ */
+static inline void snd_soc_codec_init_regmap(struct snd_soc_codec *codec,
+       struct regmap *regmap)
+{
+       snd_soc_component_init_regmap(&codec->component, regmap);
+}
+
+/**
+ * snd_soc_codec_exit_regmap() - De-initialize regmap instance for the CODEC
+ * @codec: The CODEC for which to de-initialize the regmap instance
+ *
+ * Calls regmap_exit() on the regmap instance associated to the CODEC and
+ * removes the regmap instance from the CODEC.
+ *
+ * This function should only be used if snd_soc_codec_init_regmap() was used to
+ * initialize the regmap instance.
+ */
+static inline void snd_soc_codec_exit_regmap(struct snd_soc_codec *codec)
+{
+       snd_soc_component_exit_regmap(&codec->component);
+}
+
+#endif
+
 /* device driver data */
 
 static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card,
@@ -1451,6 +1496,9 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
                                     struct device_node **framemaster);
 int snd_soc_of_get_dai_name(struct device_node *of_node,
                            const char **dai_name);
+int snd_soc_of_get_dai_link_codecs(struct device *dev,
+                                  struct device_node *of_node,
+                                  struct snd_soc_dai_link *dai_link);
 
 #include <sound/soc-dai.h>
 
index b04ee7e..88cf39d 100644 (file)
@@ -288,31 +288,6 @@ TRACE_EVENT(snd_soc_jack_notify,
        TP_printk("jack=%s %x", __get_str(name), (int)__entry->val)
 );
 
-TRACE_EVENT(snd_soc_cache_sync,
-
-       TP_PROTO(struct snd_soc_codec *codec, const char *type,
-                const char *status),
-
-       TP_ARGS(codec, type, status),
-
-       TP_STRUCT__entry(
-               __string(       name,           codec->component.name)
-               __string(       status,         status          )
-               __string(       type,           type            )
-               __field(        int,            id              )
-       ),
-
-       TP_fast_assign(
-               __assign_str(name, codec->component.name);
-               __assign_str(status, status);
-               __assign_str(type, type);
-               __entry->id = codec->component.id;
-       ),
-
-       TP_printk("codec=%s.%d type=%s status=%s", __get_str(name),
-                 (int)__entry->id, __get_str(type), __get_str(status))
-);
-
 #endif /* _TRACE_ASOC_H */
 
 /* This part must be outside protection */
index b70237e..8523f9b 100644 (file)
@@ -125,6 +125,7 @@ header-y += filter.h
 header-y += firewire-cdev.h
 header-y += firewire-constants.h
 header-y += flat.h
+header-y += fou.h
 header-y += fs.h
 header-y += fsl_hypervisor.h
 header-y += fuse.h
@@ -141,6 +142,7 @@ header-y += hid.h
 header-y += hiddev.h
 header-y += hidraw.h
 header-y += hpet.h
+header-y += hsr_netlink.h
 header-y += hyperv.h
 header-y += hysdn_if.h
 header-y += i2c-dev.h
@@ -251,6 +253,7 @@ header-y += mii.h
 header-y += minix_fs.h
 header-y += mman.h
 header-y += mmtimer.h
+header-y += mpls.h
 header-y += mqueue.h
 header-y += mroute.h
 header-y += mroute6.h
@@ -424,6 +427,7 @@ header-y += virtio_net.h
 header-y += virtio_pci.h
 header-y += virtio_ring.h
 header-y += virtio_rng.h
+header-y += vm_sockets.h
 header-y += vt.h
 header-y += wait.h
 header-y += wanrouter.h
index 39f621a..da17e45 100644 (file)
@@ -15,6 +15,7 @@
 
 #include <linux/types.h>
 #include <linux/if_ether.h>
+#include <linux/in6.h>
 
 #define SYSFS_BRIDGE_ATTR      "bridge"
 #define SYSFS_BRIDGE_FDB       "brforward"
index 6ee5867..941d32f 100644 (file)
@@ -220,7 +220,9 @@ typedef int __bitwise snd_pcm_format_t;
 #define        SNDRV_PCM_FORMAT_DSD_U8         ((__force snd_pcm_format_t) 48) /* DSD, 1-byte samples DSD (x8) */
 #define        SNDRV_PCM_FORMAT_DSD_U16_LE     ((__force snd_pcm_format_t) 49) /* DSD, 2-byte samples DSD (x16), little endian */
 #define        SNDRV_PCM_FORMAT_DSD_U32_LE     ((__force snd_pcm_format_t) 50) /* DSD, 4-byte samples DSD (x32), little endian */
-#define        SNDRV_PCM_FORMAT_LAST           SNDRV_PCM_FORMAT_DSD_U32_LE
+#define        SNDRV_PCM_FORMAT_DSD_U16_BE     ((__force snd_pcm_format_t) 51) /* DSD, 2-byte samples DSD (x16), big endian */
+#define        SNDRV_PCM_FORMAT_DSD_U32_BE     ((__force snd_pcm_format_t) 52) /* DSD, 4-byte samples DSD (x32), big endian */
+#define        SNDRV_PCM_FORMAT_LAST           SNDRV_PCM_FORMAT_DSD_U32_BE
 
 #ifdef SNDRV_LITTLE_ENDIAN
 #define        SNDRV_PCM_FORMAT_S16            SNDRV_PCM_FORMAT_S16_LE
index 800a0da..321d0ce 100644 (file)
@@ -544,7 +544,7 @@ asmlinkage __visible void __init start_kernel(void)
                                  static_command_line, __start___param,
                                  __stop___param - __start___param,
                                  -1, -1, &unknown_bootoption);
-       if (after_dashes)
+       if (!IS_ERR_OR_NULL(after_dashes))
                parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,
                           set_init_arg);
 
index 454f6c6..53c3310 100644 (file)
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -507,13 +507,6 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
                return retval;
        }
 
-       id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni);
-       if (id < 0) {
-               ipc_rcu_putref(sma, sem_rcu_free);
-               return id;
-       }
-       ns->used_sems += nsems;
-
        sma->sem_base = (struct sem *) &sma[1];
 
        for (i = 0; i < nsems; i++) {
@@ -528,6 +521,14 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
        INIT_LIST_HEAD(&sma->list_id);
        sma->sem_nsems = nsems;
        sma->sem_ctime = get_seconds();
+
+       id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni);
+       if (id < 0) {
+               ipc_rcu_putref(sma, sem_rcu_free);
+               return id;
+       }
+       ns->used_sems += nsems;
+
        sem_unlock(sma, -1);
        rcu_read_unlock();
 
index 80983df..cebb11d 100644 (file)
@@ -739,7 +739,7 @@ static void audit_log_feature_change(int which, u32 old_feature, u32 new_feature
 
        ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_FEATURE_CHANGE);
        audit_log_task_info(ab, current);
-       audit_log_format(ab, "feature=%s old=%u new=%u old_lock=%u new_lock=%u res=%d",
+       audit_log_format(ab, " feature=%s old=%u new=%u old_lock=%u new_lock=%u res=%d",
                         audit_feature_names[which], !!old_feature, !!new_feature,
                         !!old_lock, !!new_lock, res);
        audit_log_end(ab);
index e242e3a..80f29e0 100644 (file)
@@ -154,6 +154,7 @@ static struct audit_chunk *alloc_chunk(int count)
                chunk->owners[i].index = i;
        }
        fsnotify_init_mark(&chunk->mark, audit_tree_destroy_watch);
+       chunk->mark.mask = FS_IN_IGNORED;
        return chunk;
 }
 
index 2b02c9f..1cd5eef 100644 (file)
@@ -1562,8 +1562,10 @@ static void perf_remove_from_context(struct perf_event *event, bool detach_group
 
        if (!task) {
                /*
-                * Per cpu events are removed via an smp call and
-                * the removal is always successful.
+                * Per cpu events are removed via an smp call. The removal can
+                * fail if the CPU is currently offline, but in that case we
+                * already called __perf_remove_from_context from
+                * perf_event_exit_cpu.
                 */
                cpu_function_call(event->cpu, __perf_remove_from_context, &re);
                return;
@@ -8117,7 +8119,7 @@ static void perf_pmu_rotate_stop(struct pmu *pmu)
 
 static void __perf_event_exit_context(void *__info)
 {
-       struct remove_event re = { .detach_group = false };
+       struct remove_event re = { .detach_group = true };
        struct perf_event_context *ctx = __info;
 
        perf_pmu_rotate_stop(ctx->pmu);
index 1d0af8a..ed8f2cd 100644 (file)
@@ -1640,7 +1640,6 @@ bool uprobe_deny_signal(void)
                if (__fatal_signal_pending(t) || arch_uprobe_xol_was_trapped(t)) {
                        utask->state = UTASK_SSTEP_TRAPPED;
                        set_tsk_thread_flag(t, TIF_UPROBE);
-                       set_tsk_thread_flag(t, TIF_NOTIFY_RESUME);
                }
        }
 
index d09dc5c..cf80672 100644 (file)
@@ -244,6 +244,7 @@ static const struct tnt tnts[] = {
  *  'I' - Working around severe firmware bug.
  *  'O' - Out-of-tree module has been loaded.
  *  'E' - Unsigned module has been loaded.
+ *  'L' - A soft lockup has previously occurred.
  *
  *     The string is overwritten by the next call to print_tainted().
  */
index 4ca9a33..c347e3c 100644 (file)
@@ -146,7 +146,7 @@ static int platform_suspend_prepare(suspend_state_t state)
 
 static int platform_suspend_prepare_late(suspend_state_t state)
 {
-       return state == PM_SUSPEND_FREEZE && freeze_ops->prepare ?
+       return state == PM_SUSPEND_FREEZE && freeze_ops && freeze_ops->prepare ?
                freeze_ops->prepare() : 0;
 }
 
@@ -164,7 +164,7 @@ static void platform_resume_noirq(suspend_state_t state)
 
 static void platform_resume_early(suspend_state_t state)
 {
-       if (state == PM_SUSPEND_FREEZE && freeze_ops->restore)
+       if (state == PM_SUSPEND_FREEZE && freeze_ops && freeze_ops->restore)
                freeze_ops->restore();
 }
 
index 240157c..89e7283 100644 (file)
@@ -2474,44 +2474,6 @@ DEFINE_PER_CPU(struct kernel_cpustat, kernel_cpustat);
 EXPORT_PER_CPU_SYMBOL(kstat);
 EXPORT_PER_CPU_SYMBOL(kernel_cpustat);
 
-/*
- * Return any ns on the sched_clock that have not yet been accounted in
- * @p in case that task is currently running.
- *
- * Called with task_rq_lock() held on @rq.
- */
-static u64 do_task_delta_exec(struct task_struct *p, struct rq *rq)
-{
-       u64 ns = 0;
-
-       /*
-        * Must be ->curr _and_ ->on_rq.  If dequeued, we would
-        * project cycles that may never be accounted to this
-        * thread, breaking clock_gettime().
-        */
-       if (task_current(rq, p) && task_on_rq_queued(p)) {
-               update_rq_clock(rq);
-               ns = rq_clock_task(rq) - p->se.exec_start;
-               if ((s64)ns < 0)
-                       ns = 0;
-       }
-
-       return ns;
-}
-
-unsigned long long task_delta_exec(struct task_struct *p)
-{
-       unsigned long flags;
-       struct rq *rq;
-       u64 ns = 0;
-
-       rq = task_rq_lock(p, &flags);
-       ns = do_task_delta_exec(p, rq);
-       task_rq_unlock(rq, p, &flags);
-
-       return ns;
-}
-
 /*
  * Return accounted runtime for the task.
  * In case the task is currently running, return the runtime plus current's
@@ -2521,7 +2483,7 @@ unsigned long long task_sched_runtime(struct task_struct *p)
 {
        unsigned long flags;
        struct rq *rq;
-       u64 ns = 0;
+       u64 ns;
 
 #if defined(CONFIG_64BIT) && defined(CONFIG_SMP)
        /*
@@ -2540,7 +2502,16 @@ unsigned long long task_sched_runtime(struct task_struct *p)
 #endif
 
        rq = task_rq_lock(p, &flags);
-       ns = p->se.sum_exec_runtime + do_task_delta_exec(p, rq);
+       /*
+        * Must be ->curr _and_ ->on_rq.  If dequeued, we would
+        * project cycles that may never be accounted to this
+        * thread, breaking clock_gettime().
+        */
+       if (task_current(rq, p) && task_on_rq_queued(p)) {
+               update_rq_clock(rq);
+               p->sched_class->update_curr(rq);
+       }
+       ns = p->se.sum_exec_runtime;
        task_rq_unlock(rq, p, &flags);
 
        return ns;
@@ -2903,10 +2874,14 @@ asmlinkage __visible void __sched schedule_user(void)
         * or we have been woken up remotely but the IPI has not yet arrived,
         * we haven't yet exited the RCU idle mode. Do it here manually until
         * we find a better solution.
+        *
+        * NB: There are buggy callers of this function.  Ideally we
+        * should warn if prev_state != IN_USER, but that will trigger
+        * too frequently to make sense yet.
         */
-       user_exit();
+       enum ctx_state prev_state = exception_enter();
        schedule();
-       user_enter();
+       exception_exit(prev_state);
 }
 #endif
 
@@ -6368,6 +6343,10 @@ static void sched_init_numa(void)
                if (!sched_debug())
                        break;
        }
+
+       if (!level)
+               return;
+
        /*
         * 'level' contains the number of unique distances, excluding the
         * identity distance node_distance(i,i).
@@ -7444,8 +7423,12 @@ void sched_move_task(struct task_struct *tsk)
        if (unlikely(running))
                put_prev_task(rq, tsk);
 
-       tg = container_of(task_css_check(tsk, cpu_cgrp_id,
-                               lockdep_is_held(&tsk->sighand->siglock)),
+       /*
+        * All callers are synchronized by task_rq_lock(); we do not use RCU
+        * which is pointless here. Thus, we pass "true" to task_css_check()
+        * to prevent lockdep warnings.
+        */
+       tg = container_of(task_css_check(tsk, cpu_cgrp_id, true),
                          struct task_group, css);
        tg = autogroup_task_group(tsk, tg);
        tsk->sched_task_group = tg;
index 5285332..28fa9d9 100644 (file)
@@ -1701,4 +1701,6 @@ const struct sched_class dl_sched_class = {
        .prio_changed           = prio_changed_dl,
        .switched_from          = switched_from_dl,
        .switched_to            = switched_to_dl,
+
+       .update_curr            = update_curr_dl,
 };
index 34baa60..ef2b104 100644 (file)
@@ -726,6 +726,11 @@ static void update_curr(struct cfs_rq *cfs_rq)
        account_cfs_rq_runtime(cfs_rq, delta_exec);
 }
 
+static void update_curr_fair(struct rq *rq)
+{
+       update_curr(cfs_rq_of(&rq->curr->se));
+}
+
 static inline void
 update_stats_wait_start(struct cfs_rq *cfs_rq, struct sched_entity *se)
 {
@@ -1179,6 +1184,13 @@ static void task_numa_compare(struct task_numa_env *env,
                cur = NULL;
        raw_spin_unlock_irq(&dst_rq->lock);
 
+       /*
+        * Because we have preemption enabled we can get migrated around and
+        * end try selecting ourselves (current == env->p) as a swap candidate.
+        */
+       if (cur == env->p)
+               goto unlock;
+
        /*
         * "imp" is the fault differential for the source task between the
         * source and destination node. Calculate the total differential for
@@ -7949,6 +7961,8 @@ const struct sched_class fair_sched_class = {
 
        .get_rr_interval        = get_rr_interval_fair,
 
+       .update_curr            = update_curr_fair,
+
 #ifdef CONFIG_FAIR_GROUP_SCHED
        .task_move_group        = task_move_group_fair,
 #endif
index 67ad4e7..c65dac8 100644 (file)
@@ -75,6 +75,10 @@ static unsigned int get_rr_interval_idle(struct rq *rq, struct task_struct *task
        return 0;
 }
 
+static void update_curr_idle(struct rq *rq)
+{
+}
+
 /*
  * Simple, special scheduling class for the per-CPU idle tasks:
  */
@@ -101,4 +105,5 @@ const struct sched_class idle_sched_class = {
 
        .prio_changed           = prio_changed_idle,
        .switched_to            = switched_to_idle,
+       .update_curr            = update_curr_idle,
 };
index d024e6c..20bca39 100644 (file)
@@ -2128,6 +2128,8 @@ const struct sched_class rt_sched_class = {
 
        .prio_changed           = prio_changed_rt,
        .switched_to            = switched_to_rt,
+
+       .update_curr            = update_curr_rt,
 };
 
 #ifdef CONFIG_SCHED_DEBUG
index 24156c8..2df8ef0 100644 (file)
@@ -1135,6 +1135,8 @@ struct sched_class {
        unsigned int (*get_rr_interval) (struct rq *rq,
                                         struct task_struct *task);
 
+       void (*update_curr) (struct rq *rq);
+
 #ifdef CONFIG_FAIR_GROUP_SCHED
        void (*task_move_group) (struct task_struct *p, int on_rq);
 #endif
index 67426e5..79ffec4 100644 (file)
@@ -102,6 +102,10 @@ get_rr_interval_stop(struct rq *rq, struct task_struct *task)
        return 0;
 }
 
+static void update_curr_stop(struct rq *rq)
+{
+}
+
 /*
  * Simple, special scheduling class for the per-CPU stop tasks:
  */
@@ -128,4 +132,5 @@ const struct sched_class stop_sched_class = {
 
        .prio_changed           = prio_changed_stop,
        .switched_to            = switched_to_stop,
+       .update_curr            = update_curr_stop,
 };
index 492b986..a16b678 100644 (file)
@@ -553,7 +553,7 @@ static int cpu_timer_sample_group(const clockid_t which_clock,
                *sample = cputime_to_expires(cputime.utime);
                break;
        case CPUCLOCK_SCHED:
-               *sample = cputime.sum_exec_runtime + task_delta_exec(p);
+               *sample = cputime.sum_exec_runtime;
                break;
        }
        return 0;
index 2d75c94..a56e07c 100644 (file)
@@ -538,16 +538,18 @@ static void rb_wake_up_waiters(struct irq_work *work)
  * ring_buffer_wait - wait for input to the ring buffer
  * @buffer: buffer to wait on
  * @cpu: the cpu buffer to wait on
+ * @full: wait until a full page is available, if @cpu != RING_BUFFER_ALL_CPUS
  *
  * If @cpu == RING_BUFFER_ALL_CPUS then the task will wake up as soon
  * as data is added to any of the @buffer's cpu buffers. Otherwise
  * it will wait for data to be added to a specific cpu buffer.
  */
-int ring_buffer_wait(struct ring_buffer *buffer, int cpu)
+int ring_buffer_wait(struct ring_buffer *buffer, int cpu, bool full)
 {
-       struct ring_buffer_per_cpu *cpu_buffer;
+       struct ring_buffer_per_cpu *uninitialized_var(cpu_buffer);
        DEFINE_WAIT(wait);
        struct rb_irq_work *work;
+       int ret = 0;
 
        /*
         * Depending on what the caller is waiting for, either any
@@ -564,36 +566,61 @@ int ring_buffer_wait(struct ring_buffer *buffer, int cpu)
        }
 
 
-       prepare_to_wait(&work->waiters, &wait, TASK_INTERRUPTIBLE);
+       while (true) {
+               prepare_to_wait(&work->waiters, &wait, TASK_INTERRUPTIBLE);
 
-       /*
-        * The events can happen in critical sections where
-        * checking a work queue can cause deadlocks.
-        * After adding a task to the queue, this flag is set
-        * only to notify events to try to wake up the queue
-        * using irq_work.
-        *
-        * We don't clear it even if the buffer is no longer
-        * empty. The flag only causes the next event to run
-        * irq_work to do the work queue wake up. The worse
-        * that can happen if we race with !trace_empty() is that
-        * an event will cause an irq_work to try to wake up
-        * an empty queue.
-        *
-        * There's no reason to protect this flag either, as
-        * the work queue and irq_work logic will do the necessary
-        * synchronization for the wake ups. The only thing
-        * that is necessary is that the wake up happens after
-        * a task has been queued. It's OK for spurious wake ups.
-        */
-       work->waiters_pending = true;
+               /*
+                * The events can happen in critical sections where
+                * checking a work queue can cause deadlocks.
+                * After adding a task to the queue, this flag is set
+                * only to notify events to try to wake up the queue
+                * using irq_work.
+                *
+                * We don't clear it even if the buffer is no longer
+                * empty. The flag only causes the next event to run
+                * irq_work to do the work queue wake up. The worse
+                * that can happen if we race with !trace_empty() is that
+                * an event will cause an irq_work to try to wake up
+                * an empty queue.
+                *
+                * There's no reason to protect this flag either, as
+                * the work queue and irq_work logic will do the necessary
+                * synchronization for the wake ups. The only thing
+                * that is necessary is that the wake up happens after
+                * a task has been queued. It's OK for spurious wake ups.
+                */
+               work->waiters_pending = true;
+
+               if (signal_pending(current)) {
+                       ret = -EINTR;
+                       break;
+               }
+
+               if (cpu == RING_BUFFER_ALL_CPUS && !ring_buffer_empty(buffer))
+                       break;
+
+               if (cpu != RING_BUFFER_ALL_CPUS &&
+                   !ring_buffer_empty_cpu(buffer, cpu)) {
+                       unsigned long flags;
+                       bool pagebusy;
+
+                       if (!full)
+                               break;
+
+                       raw_spin_lock_irqsave(&cpu_buffer->reader_lock, flags);
+                       pagebusy = cpu_buffer->reader_page == cpu_buffer->commit_page;
+                       raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
+
+                       if (!pagebusy)
+                               break;
+               }
 
-       if ((cpu == RING_BUFFER_ALL_CPUS && ring_buffer_empty(buffer)) ||
-           (cpu != RING_BUFFER_ALL_CPUS && ring_buffer_empty_cpu(buffer, cpu)))
                schedule();
+       }
 
        finish_wait(&work->waiters, &wait);
-       return 0;
+
+       return ret;
 }
 
 /**
index 8a52839..92f4a6c 100644 (file)
@@ -1076,13 +1076,14 @@ update_max_tr_single(struct trace_array *tr, struct task_struct *tsk, int cpu)
 }
 #endif /* CONFIG_TRACER_MAX_TRACE */
 
-static int wait_on_pipe(struct trace_iterator *iter)
+static int wait_on_pipe(struct trace_iterator *iter, bool full)
 {
        /* Iterators are static, they should be filled or empty */
        if (trace_buffer_iter(iter, iter->cpu_file))
                return 0;
 
-       return ring_buffer_wait(iter->trace_buffer->buffer, iter->cpu_file);
+       return ring_buffer_wait(iter->trace_buffer->buffer, iter->cpu_file,
+                               full);
 }
 
 #ifdef CONFIG_FTRACE_STARTUP_TEST
@@ -4434,15 +4435,12 @@ static int tracing_wait_pipe(struct file *filp)
 
                mutex_unlock(&iter->mutex);
 
-               ret = wait_on_pipe(iter);
+               ret = wait_on_pipe(iter, false);
 
                mutex_lock(&iter->mutex);
 
                if (ret)
                        return ret;
-
-               if (signal_pending(current))
-                       return -EINTR;
        }
 
        return 1;
@@ -5372,16 +5370,12 @@ tracing_buffers_read(struct file *filp, char __user *ubuf,
                                goto out_unlock;
                        }
                        mutex_unlock(&trace_types_lock);
-                       ret = wait_on_pipe(iter);
+                       ret = wait_on_pipe(iter, false);
                        mutex_lock(&trace_types_lock);
                        if (ret) {
                                size = ret;
                                goto out_unlock;
                        }
-                       if (signal_pending(current)) {
-                               size = -EINTR;
-                               goto out_unlock;
-                       }
                        goto again;
                }
                size = 0;
@@ -5500,7 +5494,7 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos,
        };
        struct buffer_ref *ref;
        int entries, size, i;
-       ssize_t ret;
+       ssize_t ret = 0;
 
        mutex_lock(&trace_types_lock);
 
@@ -5538,13 +5532,16 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos,
                int r;
 
                ref = kzalloc(sizeof(*ref), GFP_KERNEL);
-               if (!ref)
+               if (!ref) {
+                       ret = -ENOMEM;
                        break;
+               }
 
                ref->ref = 1;
                ref->buffer = iter->trace_buffer->buffer;
                ref->page = ring_buffer_alloc_read_page(ref->buffer, iter->cpu_file);
                if (!ref->page) {
+                       ret = -ENOMEM;
                        kfree(ref);
                        break;
                }
@@ -5582,19 +5579,19 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos,
 
        /* did we read anything? */
        if (!spd.nr_pages) {
+               if (ret)
+                       goto out;
+
                if ((file->f_flags & O_NONBLOCK) || (flags & SPLICE_F_NONBLOCK)) {
                        ret = -EAGAIN;
                        goto out;
                }
                mutex_unlock(&trace_types_lock);
-               ret = wait_on_pipe(iter);
+               ret = wait_on_pipe(iter, true);
                mutex_lock(&trace_types_lock);
                if (ret)
                        goto out;
-               if (signal_pending(current)) {
-                       ret = -EINTR;
-                       goto out;
-               }
+
                goto again;
        }
 
index 7512dc9..0211d2b 100644 (file)
@@ -10,7 +10,7 @@ endif
 lib-y := ctype.o string.o vsprintf.o cmdline.o \
         rbtree.o radix-tree.o dump_stack.o timerqueue.o\
         idr.o int_sqrt.o extable.o \
-        sha1.o md5.o irq_regs.o reciprocal_div.o argv_split.o \
+        sha1.o md5.o irq_regs.o argv_split.o \
         proportions.o flex_proportions.o ratelimit.o show_mem.o \
         is_single_threaded.o plist.o decompress.o kobject_uevent.o \
         earlycpio.o
@@ -26,7 +26,7 @@ obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
         bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \
         gcd.o lcm.o list_sort.o uuid.o flex_array.o iovec.o clz_ctz.o \
         bsearch.o find_last_bit.o find_next_bit.o llist.o memweight.o kfifo.o \
-        percpu-refcount.o percpu_ida.o hash.o rhashtable.o
+        percpu-refcount.o percpu_ida.o hash.o rhashtable.o reciprocal_div.o
 obj-y += string_helpers.o
 obj-$(CONFIG_TEST_STRING_HELPERS) += test-string_helpers.o
 obj-y += kstrtox.o
index cce4dd6..2e65d20 100644 (file)
@@ -598,6 +598,7 @@ struct gen_pool *devm_gen_pool_create(struct device *dev, int min_alloc_order,
 
        return pool;
 }
+EXPORT_SYMBOL(devm_gen_pool_create);
 
 /**
  * dev_get_gen_pool - Obtain the gen_pool (if any) for a device
index 081be3b..624a0b7 100644 (file)
@@ -230,7 +230,7 @@ int rhashtable_expand(struct rhashtable *ht, gfp_t flags)
        ht->shift++;
 
        /* For each new bucket, search the corresponding old bucket
-        * for the rst entry that hashes to the new bucket, and
+        * for the first entry that hashes to the new bucket, and
         * link the new bucket to that entry. Since all the entries
         * which will end up in the new bucket appear in the same
         * old bucket, this constructs an entirely valid new hash
@@ -248,8 +248,8 @@ int rhashtable_expand(struct rhashtable *ht, gfp_t flags)
        }
 
        /* Publish the new table pointer. Lookups may now traverse
-        * the new table, but they will not benet from any
-        * additional efciency until later steps unzip the buckets.
+        * the new table, but they will not benefit from any
+        * additional efficiency until later steps unzip the buckets.
         */
        rcu_assign_pointer(ht->tbl, new_tbl);
 
@@ -306,14 +306,14 @@ int rhashtable_shrink(struct rhashtable *ht, gfp_t flags)
 
        ht->shift--;
 
-       /* Link each bucket in the new table to the rst bucket
+       /* Link each bucket in the new table to the first bucket
         * in the old table that contains entries which will hash
         * to the new bucket.
         */
        for (i = 0; i < ntbl->size; i++) {
                ntbl->buckets[i] = tbl->buckets[i];
 
-               /* Link each bucket in the new table to the rst bucket
+               /* Link each bucket in the new table to the first bucket
                 * in the old table that contains entries which will hash
                 * to the new bucket.
                 */
index 0922579..5e25627 100644 (file)
@@ -28,7 +28,7 @@ void show_mem(unsigned int filter)
                                continue;
 
                        total += zone->present_pages;
-                       reserved = zone->present_pages - zone->managed_pages;
+                       reserved += zone->present_pages - zone->managed_pages;
 
                        if (is_highmem_idx(zoneid))
                                highmem += zone->present_pages;
index 8a000ce..477be69 100644 (file)
@@ -243,13 +243,10 @@ static unsigned long __init free_all_bootmem_core(bootmem_data_t *bdata)
 
 static int reset_managed_pages_done __initdata;
 
-static inline void __init reset_node_managed_pages(pg_data_t *pgdat)
+void reset_node_managed_pages(pg_data_t *pgdat)
 {
        struct zone *z;
 
-       if (reset_managed_pages_done)
-               return;
-
        for (z = pgdat->node_zones; z < pgdat->node_zones + MAX_NR_ZONES; z++)
                z->managed_pages = 0;
 }
@@ -258,8 +255,12 @@ void __init reset_all_zones_managed_pages(void)
 {
        struct pglist_data *pgdat;
 
+       if (reset_managed_pages_done)
+               return;
+
        for_each_online_pgdat(pgdat)
                reset_node_managed_pages(pgdat);
+
        reset_managed_pages_done = 1;
 }
 
index 963bc4a..fde706e 100644 (file)
--- a/mm/cma.c
+++ b/mm/cma.c
@@ -124,6 +124,7 @@ static int __init cma_activate_area(struct cma *cma)
 
 err:
        kfree(cma->bitmap);
+       cma->count = 0;
        return -EINVAL;
 }
 
@@ -217,9 +218,8 @@ int __init cma_declare_contiguous(phys_addr_t base,
        phys_addr_t highmem_start = __pa(high_memory);
        int ret = 0;
 
-       pr_debug("%s(size %lx, base %08lx, limit %08lx alignment %08lx)\n",
-               __func__, (unsigned long)size, (unsigned long)base,
-               (unsigned long)limit, (unsigned long)alignment);
+       pr_debug("%s(size %pa, base %pa, limit %pa alignment %pa)\n",
+               __func__, &size, &base, &limit, &alignment);
 
        if (cma_area_count == ARRAY_SIZE(cma_areas)) {
                pr_err("Not enough slots for CMA reserved regions!\n");
@@ -244,52 +244,72 @@ int __init cma_declare_contiguous(phys_addr_t base,
        size = ALIGN(size, alignment);
        limit &= ~(alignment - 1);
 
+       if (!base)
+               fixed = false;
+
        /* size should be aligned with order_per_bit */
        if (!IS_ALIGNED(size >> PAGE_SHIFT, 1 << order_per_bit))
                return -EINVAL;
 
        /*
-        * adjust limit to avoid crossing low/high memory boundary for
-        * automatically allocated regions
+        * If allocating at a fixed base the request region must not cross the
+        * low/high memory boundary.
         */
-       if (((limit == 0 || limit > memblock_end) &&
-            (memblock_end - size < highmem_start &&
-             memblock_end > highmem_start)) ||
-           (!fixed && limit > highmem_start && limit - size < highmem_start)) {
-               limit = highmem_start;
-       }
-
-       if (fixed && base < highmem_start && base+size > highmem_start) {
+       if (fixed && base < highmem_start && base + size > highmem_start) {
                ret = -EINVAL;
-               pr_err("Region at %08lx defined on low/high memory boundary (%08lx)\n",
-                       (unsigned long)base, (unsigned long)highmem_start);
+               pr_err("Region at %pa defined on low/high memory boundary (%pa)\n",
+                       &base, &highmem_start);
                goto err;
        }
 
+       /*
+        * If the limit is unspecified or above the memblock end, its effective
+        * value will be the memblock end. Set it explicitly to simplify further
+        * checks.
+        */
+       if (limit == 0 || limit > memblock_end)
+               limit = memblock_end;
+
        /* Reserve memory */
-       if (base && fixed) {
+       if (fixed) {
                if (memblock_is_region_reserved(base, size) ||
                    memblock_reserve(base, size) < 0) {
                        ret = -EBUSY;
                        goto err;
                }
        } else {
-               phys_addr_t addr = memblock_alloc_range(size, alignment, base,
-                                                       limit);
+               phys_addr_t addr = 0;
+
+               /*
+                * All pages in the reserved area must come from the same zone.
+                * If the requested region crosses the low/high memory boundary,
+                * try allocating from high memory first and fall back to low
+                * memory in case of failure.
+                */
+               if (base < highmem_start && limit > highmem_start) {
+                       addr = memblock_alloc_range(size, alignment,
+                                                   highmem_start, limit);
+                       limit = highmem_start;
+               }
+
                if (!addr) {
-                       ret = -ENOMEM;
-                       goto err;
-               } else {
-                       base = addr;
+                       addr = memblock_alloc_range(size, alignment, base,
+                                                   limit);
+                       if (!addr) {
+                               ret = -ENOMEM;
+                               goto err;
+                       }
                }
+
+               base = addr;
        }
 
        ret = cma_init_reserved_mem(base, size, order_per_bit, res_cma);
        if (ret)
                goto err;
 
-       pr_info("Reserved %ld MiB at %08lx\n", (unsigned long)size / SZ_1M,
-               (unsigned long)base);
+       pr_info("Reserved %ld MiB at %pa\n", (unsigned long)size / SZ_1M,
+               &base);
        return 0;
 
 err:
index ec74cf0..f9792ba 100644 (file)
@@ -479,6 +479,16 @@ isolate_freepages_range(struct compact_control *cc,
 
                block_end_pfn = min(block_end_pfn, end_pfn);
 
+               /*
+                * pfn could pass the block_end_pfn if isolated freepage
+                * is more than pageblock order. In this case, we adjust
+                * scanning range to right one.
+                */
+               if (pfn >= block_end_pfn) {
+                       block_end_pfn = ALIGN(pfn + 1, pageblock_nr_pages);
+                       block_end_pfn = min(block_end_pfn, end_pfn);
+               }
+
                if (!pageblock_pfn_to_page(pfn, block_end_pfn, cc->zone))
                        break;
 
@@ -1029,8 +1039,12 @@ static isolate_migrate_t isolate_migratepages(struct zone *zone,
        }
 
        acct_isolated(zone, cc);
-       /* Record where migration scanner will be restarted */
-       cc->migrate_pfn = low_pfn;
+       /*
+        * Record where migration scanner will be restarted. If we end up in
+        * the same pageblock as the free scanner, make the scanners fully
+        * meet so that compact_finished() terminates compaction.
+        */
+       cc->migrate_pfn = (end_pfn <= cc->free_pfn) ? low_pfn : cc->free_pfn;
 
        return cc->nr_migratepages ? ISOLATE_SUCCESS : ISOLATE_NONE;
 }
index c30eec5..f2a3571 100644 (file)
@@ -244,8 +244,10 @@ int __frontswap_store(struct page *page)
                  the (older) page from frontswap
                 */
                inc_frontswap_failed_stores();
-               if (dup)
+               if (dup) {
                        __frontswap_clear(sis, offset);
+                       frontswap_ops->invalidate_page(type, offset);
+               }
        }
        if (frontswap_writethrough_enabled)
                /* report failure so swap also writes to swap device */
index 8293040..a4f90ba 100644 (file)
@@ -108,6 +108,31 @@ extern pmd_t *mm_find_pmd(struct mm_struct *mm, unsigned long address);
 /*
  * in mm/page_alloc.c
  */
+
+/*
+ * Locate the struct page for both the matching buddy in our
+ * pair (buddy1) and the combined O(n+1) page they form (page).
+ *
+ * 1) Any buddy B1 will have an order O twin B2 which satisfies
+ * the following equation:
+ *     B2 = B1 ^ (1 << O)
+ * For example, if the starting buddy (buddy2) is #8 its order
+ * 1 buddy is #10:
+ *     B2 = 8 ^ (1 << 1) = 8 ^ 2 = 10
+ *
+ * 2) Any buddy B will have an order O+1 parent P which
+ * satisfies the following equation:
+ *     P = B & ~(1 << O)
+ *
+ * Assumption: *_mem_map is contiguous at least up to MAX_ORDER
+ */
+static inline unsigned long
+__find_buddy_index(unsigned long page_idx, unsigned int order)
+{
+       return page_idx ^ (1 << order);
+}
+
+extern int __isolate_free_page(struct page *page, unsigned int order);
 extern void __free_pages_bootmem(struct page *page, unsigned int order);
 extern void prep_compound_page(struct page *page, unsigned long order);
 #ifdef CONFIG_MEMORY_FAILURE
index eafcf60..e34a3cb 100644 (file)
@@ -911,9 +911,9 @@ size_t iov_iter_single_seg_count(const struct iov_iter *i)
        if (i->nr_segs == 1)
                return i->count;
        else if (i->type & ITER_BVEC)
-               return min(i->count, i->iov->iov_len - i->iov_offset);
-       else
                return min(i->count, i->bvec->bv_len - i->iov_offset);
+       else
+               return min(i->count, i->iov->iov_len - i->iov_offset);
 }
 EXPORT_SYMBOL(iov_iter_single_seg_count);
 
index 3e50383..d5f2ae9 100644 (file)
@@ -815,20 +815,20 @@ copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
                if (!pte_file(pte)) {
                        swp_entry_t entry = pte_to_swp_entry(pte);
 
-                       if (swap_duplicate(entry) < 0)
-                               return entry.val;
-
-                       /* make sure dst_mm is on swapoff's mmlist. */
-                       if (unlikely(list_empty(&dst_mm->mmlist))) {
-                               spin_lock(&mmlist_lock);
-                               if (list_empty(&dst_mm->mmlist))
-                                       list_add(&dst_mm->mmlist,
-                                                &src_mm->mmlist);
-                               spin_unlock(&mmlist_lock);
-                       }
-                       if (likely(!non_swap_entry(entry)))
+                       if (likely(!non_swap_entry(entry))) {
+                               if (swap_duplicate(entry) < 0)
+                                       return entry.val;
+
+                               /* make sure dst_mm is on swapoff's mmlist. */
+                               if (unlikely(list_empty(&dst_mm->mmlist))) {
+                                       spin_lock(&mmlist_lock);
+                                       if (list_empty(&dst_mm->mmlist))
+                                               list_add(&dst_mm->mmlist,
+                                                        &src_mm->mmlist);
+                                       spin_unlock(&mmlist_lock);
+                               }
                                rss[MM_SWAPENTS]++;
-                       else if (is_migration_entry(entry)) {
+                       else if (is_migration_entry(entry)) {
                                page = migration_entry_to_page(entry);
 
                                if (PageAnon(page))
index 252e1db..1bf4807 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/stop_machine.h>
 #include <linux/hugetlb.h>
 #include <linux/memblock.h>
+#include <linux/bootmem.h>
 
 #include <asm/tlbflush.h>
 
@@ -1066,6 +1067,16 @@ out:
 }
 #endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
 
+static void reset_node_present_pages(pg_data_t *pgdat)
+{
+       struct zone *z;
+
+       for (z = pgdat->node_zones; z < pgdat->node_zones + MAX_NR_ZONES; z++)
+               z->present_pages = 0;
+
+       pgdat->node_present_pages = 0;
+}
+
 /* we are OK calling __meminit stuff here - we have CONFIG_MEMORY_HOTPLUG */
 static pg_data_t __ref *hotadd_new_pgdat(int nid, u64 start)
 {
@@ -1096,6 +1107,21 @@ static pg_data_t __ref *hotadd_new_pgdat(int nid, u64 start)
        build_all_zonelists(pgdat, NULL);
        mutex_unlock(&zonelists_mutex);
 
+       /*
+        * zone->managed_pages is set to an approximate value in
+        * free_area_init_core(), which will cause
+        * /sys/device/system/node/nodeX/meminfo has wrong data.
+        * So reset it to 0 before any memory is onlined.
+        */
+       reset_node_managed_pages(pgdat);
+
+       /*
+        * When memory is hot-added, all the memory is in offline state. So
+        * clear all zones' present_pages because they will be updated in
+        * online_pages() and offline_pages().
+        */
+       reset_node_present_pages(pgdat);
+
        return pgdat;
 }
 
index 87e82b3..ae91989 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -776,8 +776,11 @@ again:                     remove_next = 1 + (end > next->vm_end);
                 * shrinking vma had, to cover any anon pages imported.
                 */
                if (exporter && exporter->anon_vma && !importer->anon_vma) {
-                       if (anon_vma_clone(importer, exporter))
-                               return -ENOMEM;
+                       int error;
+
+                       error = anon_vma_clone(importer, exporter);
+                       if (error)
+                               return error;
                        importer->anon_vma = exporter->anon_vma;
                }
        }
@@ -2469,7 +2472,8 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
        if (err)
                goto out_free_vma;
 
-       if (anon_vma_clone(new, vma))
+       err = anon_vma_clone(new, vma);
+       if (err)
                goto out_free_mpol;
 
        if (new->vm_file)
index 7c7ab32..90b5046 100644 (file)
@@ -145,12 +145,10 @@ static unsigned long __init free_low_memory_core_early(void)
 
 static int reset_managed_pages_done __initdata;
 
-static inline void __init reset_node_managed_pages(pg_data_t *pgdat)
+void reset_node_managed_pages(pg_data_t *pgdat)
 {
        struct zone *z;
 
-       if (reset_managed_pages_done)
-               return;
        for (z = pgdat->node_zones; z < pgdat->node_zones + MAX_NR_ZONES; z++)
                z->managed_pages = 0;
 }
@@ -159,8 +157,12 @@ void __init reset_all_zones_managed_pages(void)
 {
        struct pglist_data *pgdat;
 
+       if (reset_managed_pages_done)
+               return;
+
        for_each_online_pgdat(pgdat)
                reset_node_managed_pages(pgdat);
+
        reset_managed_pages_done = 1;
 }
 
index 9cd36b8..616a2c9 100644 (file)
@@ -466,29 +466,6 @@ static inline void rmv_page_order(struct page *page)
        set_page_private(page, 0);
 }
 
-/*
- * Locate the struct page for both the matching buddy in our
- * pair (buddy1) and the combined O(n+1) page they form (page).
- *
- * 1) Any buddy B1 will have an order O twin B2 which satisfies
- * the following equation:
- *     B2 = B1 ^ (1 << O)
- * For example, if the starting buddy (buddy2) is #8 its order
- * 1 buddy is #10:
- *     B2 = 8 ^ (1 << 1) = 8 ^ 2 = 10
- *
- * 2) Any buddy B will have an order O+1 parent P which
- * satisfies the following equation:
- *     P = B & ~(1 << O)
- *
- * Assumption: *_mem_map is contiguous at least up to MAX_ORDER
- */
-static inline unsigned long
-__find_buddy_index(unsigned long page_idx, unsigned int order)
-{
-       return page_idx ^ (1 << order);
-}
-
 /*
  * This function checks whether a page is free && is the buddy
  * we can do coalesce a page and its buddy if
@@ -569,6 +546,7 @@ static inline void __free_one_page(struct page *page,
        unsigned long combined_idx;
        unsigned long uninitialized_var(buddy_idx);
        struct page *buddy;
+       int max_order = MAX_ORDER;
 
        VM_BUG_ON(!zone_is_initialized(zone));
 
@@ -577,13 +555,24 @@ static inline void __free_one_page(struct page *page,
                        return;
 
        VM_BUG_ON(migratetype == -1);
+       if (is_migrate_isolate(migratetype)) {
+               /*
+                * We restrict max order of merging to prevent merge
+                * between freepages on isolate pageblock and normal
+                * pageblock. Without this, pageblock isolation
+                * could cause incorrect freepage accounting.
+                */
+               max_order = min(MAX_ORDER, pageblock_order + 1);
+       } else {
+               __mod_zone_freepage_state(zone, 1 << order, migratetype);
+       }
 
-       page_idx = pfn & ((1 << MAX_ORDER) - 1);
+       page_idx = pfn & ((1 << max_order) - 1);
 
        VM_BUG_ON_PAGE(page_idx & ((1 << order) - 1), page);
        VM_BUG_ON_PAGE(bad_range(zone, page), page);
 
-       while (order < MAX_ORDER-1) {
+       while (order < max_order - 1) {
                buddy_idx = __find_buddy_index(page_idx, order);
                buddy = page + (buddy_idx - page_idx);
                if (!page_is_buddy(page, buddy, order))
@@ -594,9 +583,11 @@ static inline void __free_one_page(struct page *page,
                 */
                if (page_is_guard(buddy)) {
                        clear_page_guard_flag(buddy);
-                       set_page_private(page, 0);
-                       __mod_zone_freepage_state(zone, 1 << order,
-                                                 migratetype);
+                       set_page_private(buddy, 0);
+                       if (!is_migrate_isolate(migratetype)) {
+                               __mod_zone_freepage_state(zone, 1 << order,
+                                                         migratetype);
+                       }
                } else {
                        list_del(&buddy->lru);
                        zone->free_area[order].nr_free--;
@@ -715,14 +706,12 @@ static void free_pcppages_bulk(struct zone *zone, int count,
                        /* must delete as __free_one_page list manipulates */
                        list_del(&page->lru);
                        mt = get_freepage_migratetype(page);
+                       if (unlikely(has_isolate_pageblock(zone)))
+                               mt = get_pageblock_migratetype(page);
+
                        /* MIGRATE_MOVABLE list may include MIGRATE_RESERVEs */
                        __free_one_page(page, page_to_pfn(page), zone, 0, mt);
                        trace_mm_page_pcpu_drain(page, 0, mt);
-                       if (likely(!is_migrate_isolate_page(page))) {
-                               __mod_zone_page_state(zone, NR_FREE_PAGES, 1);
-                               if (is_migrate_cma(mt))
-                                       __mod_zone_page_state(zone, NR_FREE_CMA_PAGES, 1);
-                       }
                } while (--to_free && --batch_free && !list_empty(list));
        }
        spin_unlock(&zone->lock);
@@ -739,9 +728,11 @@ static void free_one_page(struct zone *zone,
        if (nr_scanned)
                __mod_zone_page_state(zone, NR_PAGES_SCANNED, -nr_scanned);
 
+       if (unlikely(has_isolate_pageblock(zone) ||
+               is_migrate_isolate(migratetype))) {
+               migratetype = get_pfnblock_migratetype(page, pfn);
+       }
        __free_one_page(page, pfn, zone, order, migratetype);
-       if (unlikely(!is_migrate_isolate(migratetype)))
-               __mod_zone_freepage_state(zone, 1 << order, migratetype);
        spin_unlock(&zone->lock);
 }
 
@@ -1484,7 +1475,7 @@ void split_page(struct page *page, unsigned int order)
 }
 EXPORT_SYMBOL_GPL(split_page);
 
-static int __isolate_free_page(struct page *page, unsigned int order)
+int __isolate_free_page(struct page *page, unsigned int order)
 {
        unsigned long watermark;
        struct zone *zone;
@@ -6408,13 +6399,12 @@ int alloc_contig_range(unsigned long start, unsigned long end,
 
        /* Make sure the range is really isolated. */
        if (test_pages_isolated(outer_start, end, false)) {
-               pr_warn("alloc_contig_range test_pages_isolated(%lx, %lx) failed\n",
-                      outer_start, end);
+               pr_info("%s: [%lx, %lx) PFNs busy\n",
+                       __func__, outer_start, end);
                ret = -EBUSY;
                goto done;
        }
 
-
        /* Grab isolated pages from freelists. */
        outer_end = isolate_freepages_range(&cc, outer_start, end);
        if (!outer_end) {
index d1473b2..c8778f7 100644 (file)
@@ -60,6 +60,7 @@ out:
                int migratetype = get_pageblock_migratetype(page);
 
                set_pageblock_migratetype(page, MIGRATE_ISOLATE);
+               zone->nr_isolate_pageblock++;
                nr_pages = move_freepages_block(zone, page, MIGRATE_ISOLATE);
 
                __mod_zone_freepage_state(zone, -nr_pages, migratetype);
@@ -75,16 +76,54 @@ void unset_migratetype_isolate(struct page *page, unsigned migratetype)
 {
        struct zone *zone;
        unsigned long flags, nr_pages;
+       struct page *isolated_page = NULL;
+       unsigned int order;
+       unsigned long page_idx, buddy_idx;
+       struct page *buddy;
 
        zone = page_zone(page);
        spin_lock_irqsave(&zone->lock, flags);
        if (get_pageblock_migratetype(page) != MIGRATE_ISOLATE)
                goto out;
-       nr_pages = move_freepages_block(zone, page, migratetype);
-       __mod_zone_freepage_state(zone, nr_pages, migratetype);
+
+       /*
+        * Because freepage with more than pageblock_order on isolated
+        * pageblock is restricted to merge due to freepage counting problem,
+        * it is possible that there is free buddy page.
+        * move_freepages_block() doesn't care of merge so we need other
+        * approach in order to merge them. Isolation and free will make
+        * these pages to be merged.
+        */
+       if (PageBuddy(page)) {
+               order = page_order(page);
+               if (order >= pageblock_order) {
+                       page_idx = page_to_pfn(page) & ((1 << MAX_ORDER) - 1);
+                       buddy_idx = __find_buddy_index(page_idx, order);
+                       buddy = page + (buddy_idx - page_idx);
+
+                       if (!is_migrate_isolate_page(buddy)) {
+                               __isolate_free_page(page, order);
+                               set_page_refcounted(page);
+                               isolated_page = page;
+                       }
+               }
+       }
+
+       /*
+        * If we isolate freepage with more than pageblock_order, there
+        * should be no freepage in the range, so we could avoid costly
+        * pageblock scanning for freepage moving.
+        */
+       if (!isolated_page) {
+               nr_pages = move_freepages_block(zone, page, migratetype);
+               __mod_zone_freepage_state(zone, nr_pages, migratetype);
+       }
        set_pageblock_migratetype(page, migratetype);
+       zone->nr_isolate_pageblock--;
 out:
        spin_unlock_irqrestore(&zone->lock, flags);
+       if (isolated_page)
+               __free_pages(isolated_page, order);
 }
 
 static inline struct page *
index 19886fb..3e4c721 100644 (file)
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -274,6 +274,7 @@ int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma)
 {
        struct anon_vma_chain *avc;
        struct anon_vma *anon_vma;
+       int error;
 
        /* Don't bother if the parent process has no anon_vma here. */
        if (!pvma->anon_vma)
@@ -283,8 +284,9 @@ int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma)
         * First, attach the new VMA to the parent VMA's anon_vmas,
         * so rmap can find non-COWed pages in child processes.
         */
-       if (anon_vma_clone(vma, pvma))
-               return -ENOMEM;
+       error = anon_vma_clone(vma, pvma);
+       if (error)
+               return error;
 
        /* Then add our own anon_vma. */
        anon_vma = anon_vma_alloc();
index eb2b2ea..f34e053 100644 (file)
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -3076,7 +3076,7 @@ static void *____cache_alloc_node(struct kmem_cache *cachep, gfp_t flags,
        void *obj;
        int x;
 
-       VM_BUG_ON(nodeid > num_online_nodes());
+       VM_BUG_ON(nodeid < 0 || nodeid >= MAX_NUMNODES);
        n = get_node(cachep, nodeid);
        BUG_ON(!n);
 
index 4069442..dcdab81 100644 (file)
@@ -259,6 +259,10 @@ struct kmem_cache *find_mergeable(size_t size, size_t align,
                if (s->size - size >= sizeof(void *))
                        continue;
 
+               if (IS_ENABLED(CONFIG_SLAB) && align &&
+                       (align > s->align || s->align % align))
+                       continue;
+
                return s;
        }
        return NULL;
index 261eaf6..f1e4d60 100644 (file)
@@ -715,8 +715,9 @@ EXPORT_SYMBOL(truncate_pagecache);
  * necessary) to @newsize. It will be typically be called from the filesystem's
  * setattr function when ATTR_SIZE is passed in.
  *
- * Must be called with inode_mutex held and before all filesystem specific
- * block truncation has been performed.
+ * Must be called with a lock serializing truncates and writes (generally
+ * i_mutex but e.g. xfs uses a different lock) and before all filesystem
+ * specific block truncation has been performed.
  */
 void truncate_setsize(struct inode *inode, loff_t newsize)
 {
@@ -755,7 +756,6 @@ void pagecache_isize_extended(struct inode *inode, loff_t from, loff_t to)
        struct page *page;
        pgoff_t index;
 
-       WARN_ON(!mutex_is_locked(&inode->i_mutex));
        WARN_ON(to > inode->i_size);
 
        if (from >= to || bsize == PAGE_CACHE_SIZE)
index d4042e7..c5afd57 100644 (file)
@@ -165,6 +165,7 @@ static void vmpressure_work_fn(struct work_struct *work)
        unsigned long scanned;
        unsigned long reclaimed;
 
+       spin_lock(&vmpr->sr_lock);
        /*
         * Several contexts might be calling vmpressure(), so it is
         * possible that the work was rescheduled again before the old
@@ -173,11 +174,12 @@ static void vmpressure_work_fn(struct work_struct *work)
         * here. No need for any locks here since we don't care if
         * vmpr->reclaimed is in sync.
         */
-       if (!vmpr->scanned)
+       scanned = vmpr->scanned;
+       if (!scanned) {
+               spin_unlock(&vmpr->sr_lock);
                return;
+       }
 
-       spin_lock(&vmpr->sr_lock);
-       scanned = vmpr->scanned;
        reclaimed = vmpr->reclaimed;
        vmpr->scanned = 0;
        vmpr->reclaimed = 0;
index 648d79c..c465876 100644 (file)
@@ -813,10 +813,9 @@ static void __br_multicast_send_query(struct net_bridge *br,
                return;
 
        if (port) {
-               __skb_push(skb, sizeof(struct ethhdr));
                skb->dev = port->dev;
                NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
-                       dev_queue_xmit);
+                       br_dev_queue_push_xmit);
        } else {
                br_multicast_select_own_querier(br, ip, skb);
                netif_rx(skb);
index 2ff9706..e5ec470 100644 (file)
@@ -280,6 +280,7 @@ static const struct nla_policy br_port_policy[IFLA_BRPORT_MAX + 1] = {
        [IFLA_BRPORT_MODE]      = { .type = NLA_U8 },
        [IFLA_BRPORT_GUARD]     = { .type = NLA_U8 },
        [IFLA_BRPORT_PROTECT]   = { .type = NLA_U8 },
+       [IFLA_BRPORT_FAST_LEAVE]= { .type = NLA_U8 },
        [IFLA_BRPORT_LEARNING]  = { .type = NLA_U8 },
        [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NLA_U8 },
 };
index 654c901..48da2c5 100644 (file)
@@ -18,6 +18,7 @@
 #include <net/netfilter/ipv6/nf_reject.h>
 #include <linux/ip.h>
 #include <net/ip.h>
+#include <net/ip6_checksum.h>
 #include <linux/netfilter_bridge.h>
 #include "../br_private.h"
 
index de6662b..7e38b72 100644 (file)
@@ -149,6 +149,7 @@ static int process_one_ticket(struct ceph_auth_client *ac,
        struct ceph_crypto_key old_key;
        void *ticket_buf = NULL;
        void *tp, *tpend;
+       void **ptp;
        struct ceph_timespec new_validity;
        struct ceph_crypto_key new_session_key;
        struct ceph_buffer *new_ticket_blob;
@@ -208,25 +209,19 @@ static int process_one_ticket(struct ceph_auth_client *ac,
                        goto out;
                }
                tp = ticket_buf;
-               dlen = ceph_decode_32(&tp);
+               ptp = &tp;
+               tpend = *ptp + dlen;
        } else {
                /* unencrypted */
-               ceph_decode_32_safe(p, end, dlen, bad);
-               ticket_buf = kmalloc(dlen, GFP_NOFS);
-               if (!ticket_buf) {
-                       ret = -ENOMEM;
-                       goto out;
-               }
-               tp = ticket_buf;
-               ceph_decode_need(p, end, dlen, bad);
-               ceph_decode_copy(p, ticket_buf, dlen);
+               ptp = p;
+               tpend = end;
        }
-       tpend = tp + dlen;
+       ceph_decode_32_safe(ptp, tpend, dlen, bad);
        dout(" ticket blob is %d bytes\n", dlen);
-       ceph_decode_need(&tp, tpend, 1 + sizeof(u64), bad);
-       blob_struct_v = ceph_decode_8(&tp);
-       new_secret_id = ceph_decode_64(&tp);
-       ret = ceph_decode_buffer(&new_ticket_blob, &tp, tpend);
+       ceph_decode_need(ptp, tpend, 1 + sizeof(u64), bad);
+       blob_struct_v = ceph_decode_8(ptp);
+       new_secret_id = ceph_decode_64(ptp);
+       ret = ceph_decode_buffer(&new_ticket_blob, ptp, tpend);
        if (ret)
                goto out;
 
index 62fc5e7..790fe89 100644 (file)
@@ -90,11 +90,82 @@ static struct crypto_blkcipher *ceph_crypto_alloc_cipher(void)
 
 static const u8 *aes_iv = (u8 *)CEPH_AES_IV;
 
+/*
+ * Should be used for buffers allocated with ceph_kvmalloc().
+ * Currently these are encrypt out-buffer (ceph_buffer) and decrypt
+ * in-buffer (msg front).
+ *
+ * Dispose of @sgt with teardown_sgtable().
+ *
+ * @prealloc_sg is to avoid memory allocation inside sg_alloc_table()
+ * in cases where a single sg is sufficient.  No attempt to reduce the
+ * number of sgs by squeezing physically contiguous pages together is
+ * made though, for simplicity.
+ */
+static int setup_sgtable(struct sg_table *sgt, struct scatterlist *prealloc_sg,
+                        const void *buf, unsigned int buf_len)
+{
+       struct scatterlist *sg;
+       const bool is_vmalloc = is_vmalloc_addr(buf);
+       unsigned int off = offset_in_page(buf);
+       unsigned int chunk_cnt = 1;
+       unsigned int chunk_len = PAGE_ALIGN(off + buf_len);
+       int i;
+       int ret;
+
+       if (buf_len == 0) {
+               memset(sgt, 0, sizeof(*sgt));
+               return -EINVAL;
+       }
+
+       if (is_vmalloc) {
+               chunk_cnt = chunk_len >> PAGE_SHIFT;
+               chunk_len = PAGE_SIZE;
+       }
+
+       if (chunk_cnt > 1) {
+               ret = sg_alloc_table(sgt, chunk_cnt, GFP_NOFS);
+               if (ret)
+                       return ret;
+       } else {
+               WARN_ON(chunk_cnt != 1);
+               sg_init_table(prealloc_sg, 1);
+               sgt->sgl = prealloc_sg;
+               sgt->nents = sgt->orig_nents = 1;
+       }
+
+       for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+               struct page *page;
+               unsigned int len = min(chunk_len - off, buf_len);
+
+               if (is_vmalloc)
+                       page = vmalloc_to_page(buf);
+               else
+                       page = virt_to_page(buf);
+
+               sg_set_page(sg, page, len, off);
+
+               off = 0;
+               buf += len;
+               buf_len -= len;
+       }
+       WARN_ON(buf_len != 0);
+
+       return 0;
+}
+
+static void teardown_sgtable(struct sg_table *sgt)
+{
+       if (sgt->orig_nents > 1)
+               sg_free_table(sgt);
+}
+
 static int ceph_aes_encrypt(const void *key, int key_len,
                            void *dst, size_t *dst_len,
                            const void *src, size_t src_len)
 {
-       struct scatterlist sg_in[2], sg_out[1];
+       struct scatterlist sg_in[2], prealloc_sg;
+       struct sg_table sg_out;
        struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
        struct blkcipher_desc desc = { .tfm = tfm, .flags = 0 };
        int ret;
@@ -110,16 +181,18 @@ static int ceph_aes_encrypt(const void *key, int key_len,
 
        *dst_len = src_len + zero_padding;
 
-       crypto_blkcipher_setkey((void *)tfm, key, key_len);
        sg_init_table(sg_in, 2);
        sg_set_buf(&sg_in[0], src, src_len);
        sg_set_buf(&sg_in[1], pad, zero_padding);
-       sg_init_table(sg_out, 1);
-       sg_set_buf(sg_out, dst, *dst_len);
+       ret = setup_sgtable(&sg_out, &prealloc_sg, dst, *dst_len);
+       if (ret)
+               goto out_tfm;
+
+       crypto_blkcipher_setkey((void *)tfm, key, key_len);
        iv = crypto_blkcipher_crt(tfm)->iv;
        ivsize = crypto_blkcipher_ivsize(tfm);
-
        memcpy(iv, aes_iv, ivsize);
+
        /*
        print_hex_dump(KERN_ERR, "enc key: ", DUMP_PREFIX_NONE, 16, 1,
                       key, key_len, 1);
@@ -128,16 +201,22 @@ static int ceph_aes_encrypt(const void *key, int key_len,
        print_hex_dump(KERN_ERR, "enc pad: ", DUMP_PREFIX_NONE, 16, 1,
                        pad, zero_padding, 1);
        */
-       ret = crypto_blkcipher_encrypt(&desc, sg_out, sg_in,
+       ret = crypto_blkcipher_encrypt(&desc, sg_out.sgl, sg_in,
                                     src_len + zero_padding);
-       crypto_free_blkcipher(tfm);
-       if (ret < 0)
+       if (ret < 0) {
                pr_err("ceph_aes_crypt failed %d\n", ret);
+               goto out_sg;
+       }
        /*
        print_hex_dump(KERN_ERR, "enc out: ", DUMP_PREFIX_NONE, 16, 1,
                       dst, *dst_len, 1);
        */
-       return 0;
+
+out_sg:
+       teardown_sgtable(&sg_out);
+out_tfm:
+       crypto_free_blkcipher(tfm);
+       return ret;
 }
 
 static int ceph_aes_encrypt2(const void *key, int key_len, void *dst,
@@ -145,7 +224,8 @@ static int ceph_aes_encrypt2(const void *key, int key_len, void *dst,
                             const void *src1, size_t src1_len,
                             const void *src2, size_t src2_len)
 {
-       struct scatterlist sg_in[3], sg_out[1];
+       struct scatterlist sg_in[3], prealloc_sg;
+       struct sg_table sg_out;
        struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
        struct blkcipher_desc desc = { .tfm = tfm, .flags = 0 };
        int ret;
@@ -161,17 +241,19 @@ static int ceph_aes_encrypt2(const void *key, int key_len, void *dst,
 
        *dst_len = src1_len + src2_len + zero_padding;
 
-       crypto_blkcipher_setkey((void *)tfm, key, key_len);
        sg_init_table(sg_in, 3);
        sg_set_buf(&sg_in[0], src1, src1_len);
        sg_set_buf(&sg_in[1], src2, src2_len);
        sg_set_buf(&sg_in[2], pad, zero_padding);
-       sg_init_table(sg_out, 1);
-       sg_set_buf(sg_out, dst, *dst_len);
+       ret = setup_sgtable(&sg_out, &prealloc_sg, dst, *dst_len);
+       if (ret)
+               goto out_tfm;
+
+       crypto_blkcipher_setkey((void *)tfm, key, key_len);
        iv = crypto_blkcipher_crt(tfm)->iv;
        ivsize = crypto_blkcipher_ivsize(tfm);
-
        memcpy(iv, aes_iv, ivsize);
+
        /*
        print_hex_dump(KERN_ERR, "enc  key: ", DUMP_PREFIX_NONE, 16, 1,
                       key, key_len, 1);
@@ -182,23 +264,30 @@ static int ceph_aes_encrypt2(const void *key, int key_len, void *dst,
        print_hex_dump(KERN_ERR, "enc  pad: ", DUMP_PREFIX_NONE, 16, 1,
                        pad, zero_padding, 1);
        */
-       ret = crypto_blkcipher_encrypt(&desc, sg_out, sg_in,
+       ret = crypto_blkcipher_encrypt(&desc, sg_out.sgl, sg_in,
                                     src1_len + src2_len + zero_padding);
-       crypto_free_blkcipher(tfm);
-       if (ret < 0)
+       if (ret < 0) {
                pr_err("ceph_aes_crypt2 failed %d\n", ret);
+               goto out_sg;
+       }
        /*
        print_hex_dump(KERN_ERR, "enc  out: ", DUMP_PREFIX_NONE, 16, 1,
                       dst, *dst_len, 1);
        */
-       return 0;
+
+out_sg:
+       teardown_sgtable(&sg_out);
+out_tfm:
+       crypto_free_blkcipher(tfm);
+       return ret;
 }
 
 static int ceph_aes_decrypt(const void *key, int key_len,
                            void *dst, size_t *dst_len,
                            const void *src, size_t src_len)
 {
-       struct scatterlist sg_in[1], sg_out[2];
+       struct sg_table sg_in;
+       struct scatterlist sg_out[2], prealloc_sg;
        struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
        struct blkcipher_desc desc = { .tfm = tfm };
        char pad[16];
@@ -210,16 +299,16 @@ static int ceph_aes_decrypt(const void *key, int key_len,
        if (IS_ERR(tfm))
                return PTR_ERR(tfm);
 
-       crypto_blkcipher_setkey((void *)tfm, key, key_len);
-       sg_init_table(sg_in, 1);
        sg_init_table(sg_out, 2);
-       sg_set_buf(sg_in, src, src_len);
        sg_set_buf(&sg_out[0], dst, *dst_len);
        sg_set_buf(&sg_out[1], pad, sizeof(pad));
+       ret = setup_sgtable(&sg_in, &prealloc_sg, src, src_len);
+       if (ret)
+               goto out_tfm;
 
+       crypto_blkcipher_setkey((void *)tfm, key, key_len);
        iv = crypto_blkcipher_crt(tfm)->iv;
        ivsize = crypto_blkcipher_ivsize(tfm);
-
        memcpy(iv, aes_iv, ivsize);
 
        /*
@@ -228,12 +317,10 @@ static int ceph_aes_decrypt(const void *key, int key_len,
        print_hex_dump(KERN_ERR, "dec  in: ", DUMP_PREFIX_NONE, 16, 1,
                       src, src_len, 1);
        */
-
-       ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, src_len);
-       crypto_free_blkcipher(tfm);
+       ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in.sgl, src_len);
        if (ret < 0) {
                pr_err("ceph_aes_decrypt failed %d\n", ret);
-               return ret;
+               goto out_sg;
        }
 
        if (src_len <= *dst_len)
@@ -251,7 +338,12 @@ static int ceph_aes_decrypt(const void *key, int key_len,
        print_hex_dump(KERN_ERR, "dec out: ", DUMP_PREFIX_NONE, 16, 1,
                       dst, *dst_len, 1);
        */
-       return 0;
+
+out_sg:
+       teardown_sgtable(&sg_in);
+out_tfm:
+       crypto_free_blkcipher(tfm);
+       return ret;
 }
 
 static int ceph_aes_decrypt2(const void *key, int key_len,
@@ -259,7 +351,8 @@ static int ceph_aes_decrypt2(const void *key, int key_len,
                             void *dst2, size_t *dst2_len,
                             const void *src, size_t src_len)
 {
-       struct scatterlist sg_in[1], sg_out[3];
+       struct sg_table sg_in;
+       struct scatterlist sg_out[3], prealloc_sg;
        struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
        struct blkcipher_desc desc = { .tfm = tfm };
        char pad[16];
@@ -271,17 +364,17 @@ static int ceph_aes_decrypt2(const void *key, int key_len,
        if (IS_ERR(tfm))
                return PTR_ERR(tfm);
 
-       sg_init_table(sg_in, 1);
-       sg_set_buf(sg_in, src, src_len);
        sg_init_table(sg_out, 3);
        sg_set_buf(&sg_out[0], dst1, *dst1_len);
        sg_set_buf(&sg_out[1], dst2, *dst2_len);
        sg_set_buf(&sg_out[2], pad, sizeof(pad));
+       ret = setup_sgtable(&sg_in, &prealloc_sg, src, src_len);
+       if (ret)
+               goto out_tfm;
 
        crypto_blkcipher_setkey((void *)tfm, key, key_len);
        iv = crypto_blkcipher_crt(tfm)->iv;
        ivsize = crypto_blkcipher_ivsize(tfm);
-
        memcpy(iv, aes_iv, ivsize);
 
        /*
@@ -290,12 +383,10 @@ static int ceph_aes_decrypt2(const void *key, int key_len,
        print_hex_dump(KERN_ERR, "dec   in: ", DUMP_PREFIX_NONE, 16, 1,
                       src, src_len, 1);
        */
-
-       ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, src_len);
-       crypto_free_blkcipher(tfm);
+       ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in.sgl, src_len);
        if (ret < 0) {
                pr_err("ceph_aes_decrypt failed %d\n", ret);
-               return ret;
+               goto out_sg;
        }
 
        if (src_len <= *dst1_len)
@@ -325,7 +416,11 @@ static int ceph_aes_decrypt2(const void *key, int key_len,
                       dst2, *dst2_len, 1);
        */
 
-       return 0;
+out_sg:
+       teardown_sgtable(&sg_in);
+out_tfm:
+       crypto_free_blkcipher(tfm);
+       return ret;
 }
 
 
index 559c9f6..8d1653c 100644 (file)
@@ -484,7 +484,7 @@ static int ceph_tcp_connect(struct ceph_connection *con)
                               IPPROTO_TCP, &sock);
        if (ret)
                return ret;
-       sock->sk->sk_allocation = GFP_NOFS;
+       sock->sk->sk_allocation = GFP_NOFS | __GFP_MEMALLOC;
 
 #ifdef CONFIG_LOCKDEP
        lockdep_set_class(&sock->sk->sk_lock, &socket_class);
@@ -509,6 +509,9 @@ static int ceph_tcp_connect(struct ceph_connection *con)
 
                return ret;
        }
+
+       sk_set_memalloc(sock->sk);
+
        con->sock = sock;
        return 0;
 }
@@ -2769,8 +2772,11 @@ static void con_work(struct work_struct *work)
 {
        struct ceph_connection *con = container_of(work, struct ceph_connection,
                                                   work.work);
+       unsigned long pflags = current->flags;
        bool fault;
 
+       current->flags |= PF_MEMALLOC;
+
        mutex_lock(&con->mutex);
        while (true) {
                int ret;
@@ -2824,6 +2830,8 @@ static void con_work(struct work_struct *work)
                con_fault_finish(con);
 
        con->ops->put(con);
+
+       tsk_restore_flags(current, pflags, PF_MEMALLOC);
 }
 
 /*
index f3fc54e..6f16428 100644 (file)
@@ -1007,8 +1007,8 @@ static void put_osd(struct ceph_osd *osd)
 static void __remove_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd)
 {
        dout("__remove_osd %p\n", osd);
-       BUG_ON(!list_empty(&osd->o_requests));
-       BUG_ON(!list_empty(&osd->o_linger_requests));
+       WARN_ON(!list_empty(&osd->o_requests));
+       WARN_ON(!list_empty(&osd->o_linger_requests));
 
        rb_erase(&osd->o_node, &osdc->osds);
        list_del_init(&osd->o_osd_lru);
@@ -1254,6 +1254,8 @@ static void __unregister_linger_request(struct ceph_osd_client *osdc,
                if (list_empty(&req->r_osd_item))
                        req->r_osd = NULL;
        }
+
+       list_del_init(&req->r_req_lru_item); /* can be on notarget */
        ceph_osdc_put_request(req);
 }
 
@@ -1395,6 +1397,7 @@ static int __map_request(struct ceph_osd_client *osdc,
        if (req->r_osd) {
                __cancel_request(req);
                list_del_init(&req->r_osd_item);
+               list_del_init(&req->r_linger_osd_item);
                req->r_osd = NULL;
        }
 
index a688268..76321ea 100644 (file)
@@ -1498,6 +1498,7 @@ static int do_setlink(const struct sk_buff *skb,
                        goto errout;
                }
                if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) {
+                       put_net(net);
                        err = -EPERM;
                        goto errout;
                }
@@ -2685,13 +2686,20 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb)
        int idx = 0;
        u32 portid = NETLINK_CB(cb->skb).portid;
        u32 seq = cb->nlh->nlmsg_seq;
-       struct nlattr *extfilt;
        u32 filter_mask = 0;
 
-       extfilt = nlmsg_find_attr(cb->nlh, sizeof(struct ifinfomsg),
-                                 IFLA_EXT_MASK);
-       if (extfilt)
-               filter_mask = nla_get_u32(extfilt);
+       if (nlmsg_len(cb->nlh) > sizeof(struct ifinfomsg)) {
+               struct nlattr *extfilt;
+
+               extfilt = nlmsg_find_attr(cb->nlh, sizeof(struct ifinfomsg),
+                                         IFLA_EXT_MASK);
+               if (extfilt) {
+                       if (nla_len(extfilt) < sizeof(filter_mask))
+                               return -EINVAL;
+
+                       filter_mask = nla_get_u32(extfilt);
+               }
+       }
 
        rcu_read_lock();
        for_each_netdev_rcu(net, dev) {
@@ -2798,6 +2806,9 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh)
        if (br_spec) {
                nla_for_each_nested(attr, br_spec, rem) {
                        if (nla_type(attr) == IFLA_BRIDGE_FLAGS) {
+                               if (nla_len(attr) < sizeof(flags))
+                                       return -EINVAL;
+
                                have_flags = true;
                                flags = nla_get_u16(attr);
                                break;
@@ -2868,6 +2879,9 @@ static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh)
        if (br_spec) {
                nla_for_each_nested(attr, br_spec, rem) {
                        if (nla_type(attr) == IFLA_BRIDGE_FLAGS) {
+                               if (nla_len(attr) < sizeof(flags))
+                                       return -EINVAL;
+
                                have_flags = true;
                                flags = nla_get_u16(attr);
                                break;
index c16615b..32e31c2 100644 (file)
@@ -552,20 +552,13 @@ static void kfree_skbmem(struct sk_buff *skb)
        case SKB_FCLONE_CLONE:
                fclones = container_of(skb, struct sk_buff_fclones, skb2);
 
-               /* Warning : We must perform the atomic_dec_and_test() before
-                * setting skb->fclone back to SKB_FCLONE_FREE, otherwise
-                * skb_clone() could set clone_ref to 2 before our decrement.
-                * Anyway, if we are going to free the structure, no need to
-                * rewrite skb->fclone.
+               /* The clone portion is available for
+                * fast-cloning again.
                 */
-               if (atomic_dec_and_test(&fclones->fclone_ref)) {
+               skb->fclone = SKB_FCLONE_FREE;
+
+               if (atomic_dec_and_test(&fclones->fclone_ref))
                        kmem_cache_free(skbuff_fclone_cache, fclones);
-               } else {
-                       /* The clone portion is available for
-                        * fast-cloning again.
-                        */
-                       skb->fclone = SKB_FCLONE_FREE;
-               }
                break;
        }
 }
@@ -887,11 +880,7 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
        if (skb->fclone == SKB_FCLONE_ORIG &&
            n->fclone == SKB_FCLONE_FREE) {
                n->fclone = SKB_FCLONE_CLONE;
-               /* As our fastclone was free, clone_ref must be 1 at this point.
-                * We could use atomic_inc() here, but it is faster
-                * to set the final value.
-                */
-               atomic_set(&fclones->fclone_ref, 2);
+               atomic_inc(&fclones->fclone_ref);
        } else {
                if (skb_pfmemalloc(skb))
                        gfp_mask |= __GFP_MEMALLOC;
index ca11d28..93ea801 100644 (file)
@@ -1080,13 +1080,13 @@ static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev)
        if (!app)
                return -EMSGSIZE;
 
-       spin_lock(&dcb_lock);
+       spin_lock_bh(&dcb_lock);
        list_for_each_entry(itr, &dcb_app_list, list) {
                if (itr->ifindex == netdev->ifindex) {
                        err = nla_put(skb, DCB_ATTR_IEEE_APP, sizeof(itr->app),
                                         &itr->app);
                        if (err) {
-                               spin_unlock(&dcb_lock);
+                               spin_unlock_bh(&dcb_lock);
                                return -EMSGSIZE;
                        }
                }
@@ -1097,7 +1097,7 @@ static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev)
        else
                dcbx = -EOPNOTSUPP;
 
-       spin_unlock(&dcb_lock);
+       spin_unlock_bh(&dcb_lock);
        nla_nest_end(skb, app);
 
        /* get peer info if available */
@@ -1234,7 +1234,7 @@ static int dcbnl_cee_fill(struct sk_buff *skb, struct net_device *netdev)
        }
 
        /* local app */
-       spin_lock(&dcb_lock);
+       spin_lock_bh(&dcb_lock);
        app = nla_nest_start(skb, DCB_ATTR_CEE_APP_TABLE);
        if (!app)
                goto dcb_unlock;
@@ -1271,7 +1271,7 @@ static int dcbnl_cee_fill(struct sk_buff *skb, struct net_device *netdev)
        else
                dcbx = -EOPNOTSUPP;
 
-       spin_unlock(&dcb_lock);
+       spin_unlock_bh(&dcb_lock);
 
        /* features flags */
        if (ops->getfeatcfg) {
@@ -1326,7 +1326,7 @@ static int dcbnl_cee_fill(struct sk_buff *skb, struct net_device *netdev)
        return 0;
 
 dcb_unlock:
-       spin_unlock(&dcb_lock);
+       spin_unlock_bh(&dcb_lock);
 nla_put_failure:
        return err;
 }
@@ -1762,10 +1762,10 @@ u8 dcb_getapp(struct net_device *dev, struct dcb_app *app)
        struct dcb_app_type *itr;
        u8 prio = 0;
 
-       spin_lock(&dcb_lock);
+       spin_lock_bh(&dcb_lock);
        if ((itr = dcb_app_lookup(app, dev->ifindex, 0)))
                prio = itr->app.priority;
-       spin_unlock(&dcb_lock);
+       spin_unlock_bh(&dcb_lock);
 
        return prio;
 }
@@ -1789,7 +1789,7 @@ int dcb_setapp(struct net_device *dev, struct dcb_app *new)
        if (dev->dcbnl_ops->getdcbx)
                event.dcbx = dev->dcbnl_ops->getdcbx(dev);
 
-       spin_lock(&dcb_lock);
+       spin_lock_bh(&dcb_lock);
        /* Search for existing match and replace */
        if ((itr = dcb_app_lookup(new, dev->ifindex, 0))) {
                if (new->priority)
@@ -1804,7 +1804,7 @@ int dcb_setapp(struct net_device *dev, struct dcb_app *new)
        if (new->priority)
                err = dcb_app_add(new, dev->ifindex);
 out:
-       spin_unlock(&dcb_lock);
+       spin_unlock_bh(&dcb_lock);
        if (!err)
                call_dcbevent_notifiers(DCB_APP_EVENT, &event);
        return err;
@@ -1823,10 +1823,10 @@ u8 dcb_ieee_getapp_mask(struct net_device *dev, struct dcb_app *app)
        struct dcb_app_type *itr;
        u8 prio = 0;
 
-       spin_lock(&dcb_lock);
+       spin_lock_bh(&dcb_lock);
        if ((itr = dcb_app_lookup(app, dev->ifindex, 0)))
                prio |= 1 << itr->app.priority;
-       spin_unlock(&dcb_lock);
+       spin_unlock_bh(&dcb_lock);
 
        return prio;
 }
@@ -1850,7 +1850,7 @@ int dcb_ieee_setapp(struct net_device *dev, struct dcb_app *new)
        if (dev->dcbnl_ops->getdcbx)
                event.dcbx = dev->dcbnl_ops->getdcbx(dev);
 
-       spin_lock(&dcb_lock);
+       spin_lock_bh(&dcb_lock);
        /* Search for existing match and abort if found */
        if (dcb_app_lookup(new, dev->ifindex, new->priority)) {
                err = -EEXIST;
@@ -1859,7 +1859,7 @@ int dcb_ieee_setapp(struct net_device *dev, struct dcb_app *new)
 
        err = dcb_app_add(new, dev->ifindex);
 out:
-       spin_unlock(&dcb_lock);
+       spin_unlock_bh(&dcb_lock);
        if (!err)
                call_dcbevent_notifiers(DCB_APP_EVENT, &event);
        return err;
@@ -1882,7 +1882,7 @@ int dcb_ieee_delapp(struct net_device *dev, struct dcb_app *del)
        if (dev->dcbnl_ops->getdcbx)
                event.dcbx = dev->dcbnl_ops->getdcbx(dev);
 
-       spin_lock(&dcb_lock);
+       spin_lock_bh(&dcb_lock);
        /* Search for existing match and remove it. */
        if ((itr = dcb_app_lookup(del, dev->ifindex, del->priority))) {
                list_del(&itr->list);
@@ -1890,7 +1890,7 @@ int dcb_ieee_delapp(struct net_device *dev, struct dcb_app *del)
                err = 0;
        }
 
-       spin_unlock(&dcb_lock);
+       spin_unlock_bh(&dcb_lock);
        if (!err)
                call_dcbevent_notifiers(DCB_APP_EVENT, &event);
        return err;
@@ -1902,12 +1902,12 @@ static void dcb_flushapp(void)
        struct dcb_app_type *app;
        struct dcb_app_type *tmp;
 
-       spin_lock(&dcb_lock);
+       spin_lock_bh(&dcb_lock);
        list_for_each_entry_safe(app, tmp, &dcb_app_list, list) {
                list_del(&app->list);
                kfree(app);
        }
-       spin_unlock(&dcb_lock);
+       spin_unlock_bh(&dcb_lock);
 }
 
 static int __init dcbnl_init(void)
index 6d18174..ab03e00 100644 (file)
@@ -489,11 +489,14 @@ static void dsa_slave_phy_setup(struct dsa_slave_priv *p,
        /* We could not connect to a designated PHY, so use the switch internal
         * MDIO bus instead
         */
-       if (!p->phy)
+       if (!p->phy) {
                p->phy = ds->slave_mii_bus->phy_map[p->port];
-       else
+               phy_connect_direct(slave_dev, p->phy, dsa_slave_adjust_link,
+                                  p->phy_interface);
+       } else {
                pr_info("attached PHY at address %d [%s]\n",
                        p->phy->addr, p->phy->drv->name);
+       }
 }
 
 int dsa_slave_suspend(struct net_device *slave_dev)
index 8b7fe5b..e67da4e 100644 (file)
@@ -1386,6 +1386,17 @@ out:
        return pp;
 }
 
+int inet_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
+{
+       if (sk->sk_family == AF_INET)
+               return ip_recv_error(sk, msg, len, addr_len);
+#if IS_ENABLED(CONFIG_IPV6)
+       if (sk->sk_family == AF_INET6)
+               return pingv6_ops.ipv6_recv_error(sk, msg, len, addr_len);
+#endif
+       return -EINVAL;
+}
+
 static int inet_gro_complete(struct sk_buff *skb, int nhoff)
 {
        __be16 newlen = htons(skb->len - nhoff);
index f2e1573..8f7bd56 100644 (file)
@@ -62,6 +62,10 @@ int __fib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res)
        else
                res->tclassid = 0;
 #endif
+
+       if (err == -ESRCH)
+               err = -ENETUNREACH;
+
        return err;
 }
 EXPORT_SYMBOL_GPL(__fib_lookup);
index 32e7892..606c520 100644 (file)
@@ -133,6 +133,8 @@ static int fou_gro_complete(struct sk_buff *skb, int nhoff)
        int err = -ENOSYS;
        const struct net_offload **offloads;
 
+       udp_tunnel_gro_complete(skb, nhoff);
+
        rcu_read_lock();
        offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
        ops = rcu_dereference(offloads[proto]);
index 065cd94..dedb21e 100644 (file)
@@ -144,6 +144,8 @@ int geneve_xmit_skb(struct geneve_sock *gs, struct rtable *rt,
        gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
        geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
 
+       skb_set_inner_protocol(skb, htons(ETH_P_TEB));
+
        return udp_tunnel_xmit_skb(gs->sock, rt, skb, src, dst,
                                   tos, ttl, df, src_port, dst_port, xnet);
 }
@@ -364,6 +366,7 @@ late_initcall(geneve_init_module);
 static void __exit geneve_cleanup_module(void)
 {
        destroy_workqueue(geneve_wq);
+       unregister_pernet_subsys(&geneve_net_ops);
 }
 module_exit(geneve_cleanup_module);
 
index fb70e3e..bb15d0e 100644 (file)
@@ -318,9 +318,7 @@ igmp_scount(struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted)
        return scount;
 }
 
-#define igmp_skb_size(skb) (*(unsigned int *)((skb)->cb))
-
-static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size)
+static struct sk_buff *igmpv3_newpack(struct net_device *dev, unsigned int mtu)
 {
        struct sk_buff *skb;
        struct rtable *rt;
@@ -330,6 +328,7 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size)
        struct flowi4 fl4;
        int hlen = LL_RESERVED_SPACE(dev);
        int tlen = dev->needed_tailroom;
+       unsigned int size = mtu;
 
        while (1) {
                skb = alloc_skb(size + hlen + tlen,
@@ -341,7 +340,6 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size)
                        return NULL;
        }
        skb->priority = TC_PRIO_CONTROL;
-       igmp_skb_size(skb) = size;
 
        rt = ip_route_output_ports(net, &fl4, NULL, IGMPV3_ALL_MCR, 0,
                                   0, 0,
@@ -354,6 +352,8 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size)
        skb_dst_set(skb, &rt->dst);
        skb->dev = dev;
 
+       skb->reserved_tailroom = skb_end_offset(skb) -
+                                min(mtu, skb_end_offset(skb));
        skb_reserve(skb, hlen);
 
        skb_reset_network_header(skb);
@@ -423,8 +423,7 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ip_mc_list *pmc,
        return skb;
 }
 
-#define AVAILABLE(skb) ((skb) ? ((skb)->dev ? igmp_skb_size(skb) - (skb)->len : \
-       skb_tailroom(skb)) : 0)
+#define AVAILABLE(skb) ((skb) ? skb_availroom(skb) : 0)
 
 static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
        int type, int gdeleted, int sdeleted)
index c373a9a..9daf217 100644 (file)
@@ -195,7 +195,7 @@ int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc,
        for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
                if (!CMSG_OK(msg, cmsg))
                        return -EINVAL;
-#if defined(CONFIG_IPV6)
+#if IS_ENABLED(CONFIG_IPV6)
                if (allow_ipv6 &&
                    cmsg->cmsg_level == SOL_IPV6 &&
                    cmsg->cmsg_type == IPV6_PKTINFO) {
index 3e86101..1a7e979 100644 (file)
@@ -528,6 +528,7 @@ static struct rtnl_link_ops vti_link_ops __read_mostly = {
        .validate       = vti_tunnel_validate,
        .newlink        = vti_newlink,
        .changelink     = vti_changelink,
+       .dellink        = ip_tunnel_dellink,
        .get_size       = vti_get_size,
        .fill_info      = vti_fill_info,
 };
index c1023c4..665de06 100644 (file)
@@ -24,6 +24,7 @@ static void nft_masq_ipv4_eval(const struct nft_expr *expr,
        struct nf_nat_range range;
        unsigned int verdict;
 
+       memset(&range, 0, sizeof(range));
        range.flags = priv->flags;
 
        verdict = nf_nat_masquerade_ipv4(pkt->skb, pkt->ops->hooknum,
index 57f7c98..5d740cc 100644 (file)
@@ -217,6 +217,8 @@ static struct sock *ping_lookup(struct net *net, struct sk_buff *skb, u16 ident)
                                             &ipv6_hdr(skb)->daddr))
                                continue;
 #endif
+               } else {
+                       continue;
                }
 
                if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif)
@@ -853,16 +855,8 @@ int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
        if (flags & MSG_OOB)
                goto out;
 
-       if (flags & MSG_ERRQUEUE) {
-               if (family == AF_INET) {
-                       return ip_recv_error(sk, msg, len, addr_len);
-#if IS_ENABLED(CONFIG_IPV6)
-               } else if (family == AF_INET6) {
-                       return pingv6_ops.ipv6_recv_error(sk, msg, len,
-                                                         addr_len);
-#endif
-               }
-       }
+       if (flags & MSG_ERRQUEUE)
+               return inet_recv_error(sk, msg, len, addr_len);
 
        skb = skb_recv_datagram(sk, flags, noblock, &err);
        if (!skb)
index 39ec0c3..38c2bcb 100644 (file)
@@ -1598,7 +1598,7 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
        u32 urg_hole = 0;
 
        if (unlikely(flags & MSG_ERRQUEUE))
-               return ip_recv_error(sk, msg, len, addr_len);
+               return inet_recv_error(sk, msg, len, addr_len);
 
        if (sk_can_busy_loop(sk) && skb_queue_empty(&sk->sk_receive_queue) &&
            (sk->sk_state == TCP_ESTABLISHED))
index a12b455..d107ee2 100644 (file)
@@ -2315,6 +2315,35 @@ static inline bool tcp_packet_delayed(const struct tcp_sock *tp)
 
 /* Undo procedures. */
 
+/* We can clear retrans_stamp when there are no retransmissions in the
+ * window. It would seem that it is trivially available for us in
+ * tp->retrans_out, however, that kind of assumptions doesn't consider
+ * what will happen if errors occur when sending retransmission for the
+ * second time. ...It could the that such segment has only
+ * TCPCB_EVER_RETRANS set at the present time. It seems that checking
+ * the head skb is enough except for some reneging corner cases that
+ * are not worth the effort.
+ *
+ * Main reason for all this complexity is the fact that connection dying
+ * time now depends on the validity of the retrans_stamp, in particular,
+ * that successive retransmissions of a segment must not advance
+ * retrans_stamp under any conditions.
+ */
+static bool tcp_any_retrans_done(const struct sock *sk)
+{
+       const struct tcp_sock *tp = tcp_sk(sk);
+       struct sk_buff *skb;
+
+       if (tp->retrans_out)
+               return true;
+
+       skb = tcp_write_queue_head(sk);
+       if (unlikely(skb && TCP_SKB_CB(skb)->sacked & TCPCB_EVER_RETRANS))
+               return true;
+
+       return false;
+}
+
 #if FASTRETRANS_DEBUG > 1
 static void DBGUNDO(struct sock *sk, const char *msg)
 {
@@ -2410,6 +2439,8 @@ static bool tcp_try_undo_recovery(struct sock *sk)
                 * is ACKed. For Reno it is MUST to prevent false
                 * fast retransmits (RFC2582). SACK TCP is safe. */
                tcp_moderate_cwnd(tp);
+               if (!tcp_any_retrans_done(sk))
+                       tp->retrans_stamp = 0;
                return true;
        }
        tcp_set_ca_state(sk, TCP_CA_Open);
@@ -2430,35 +2461,6 @@ static bool tcp_try_undo_dsack(struct sock *sk)
        return false;
 }
 
-/* We can clear retrans_stamp when there are no retransmissions in the
- * window. It would seem that it is trivially available for us in
- * tp->retrans_out, however, that kind of assumptions doesn't consider
- * what will happen if errors occur when sending retransmission for the
- * second time. ...It could the that such segment has only
- * TCPCB_EVER_RETRANS set at the present time. It seems that checking
- * the head skb is enough except for some reneging corner cases that
- * are not worth the effort.
- *
- * Main reason for all this complexity is the fact that connection dying
- * time now depends on the validity of the retrans_stamp, in particular,
- * that successive retransmissions of a segment must not advance
- * retrans_stamp under any conditions.
- */
-static bool tcp_any_retrans_done(const struct sock *sk)
-{
-       const struct tcp_sock *tp = tcp_sk(sk);
-       struct sk_buff *skb;
-
-       if (tp->retrans_out)
-               return true;
-
-       skb = tcp_write_queue_head(sk);
-       if (unlikely(skb && TCP_SKB_CB(skb)->sacked & TCPCB_EVER_RETRANS))
-               return true;
-
-       return false;
-}
-
 /* Undo during loss recovery after partial ACK or using F-RTO. */
 static bool tcp_try_undo_loss(struct sock *sk, bool frto_undo)
 {
@@ -5229,7 +5231,7 @@ slow_path:
        if (len < (th->doff << 2) || tcp_checksum_complete_user(sk, skb))
                goto csum_error;
 
-       if (!th->ack && !th->rst)
+       if (!th->ack && !th->rst && !th->syn)
                goto discard;
 
        /*
@@ -5648,7 +5650,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
                        goto discard;
        }
 
-       if (!th->ack && !th->rst)
+       if (!th->ack && !th->rst && !th->syn)
                goto discard;
 
        if (!tcp_validate_incoming(sk, skb, th, 0))
index 9c7d762..147be20 100644 (file)
@@ -598,7 +598,10 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
        if (th->rst)
                return;
 
-       if (skb_rtable(skb)->rt_type != RTN_LOCAL)
+       /* If sk not NULL, it means we did a successful lookup and incoming
+        * route had to be correct. prequeue might have dropped our dst.
+        */
+       if (!sk && skb_rtable(skb)->rt_type != RTN_LOCAL)
                return;
 
        /* Swap the send and the receive. */
index 12c3c8e..0e32d2e 100644 (file)
@@ -502,11 +502,11 @@ static int ip6gre_rcv(struct sk_buff *skb)
 
                skb->protocol = gre_proto;
                /* WCCP version 1 and 2 protocol decoding.
-                * - Change protocol to IP
+                * - Change protocol to IPv6
                 * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
                 */
                if (flags == 0 && gre_proto == htons(ETH_P_WCCP)) {
-                       skb->protocol = htons(ETH_P_IP);
+                       skb->protocol = htons(ETH_P_IPV6);
                        if ((*(h + offset) & 0xF0) != 0x40)
                                offset += 4;
                }
@@ -961,8 +961,6 @@ static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu)
        else
                dev->flags &= ~IFF_POINTOPOINT;
 
-       dev->iflink = p->link;
-
        /* Precalculate GRE options length */
        if (t->parms.o_flags&(GRE_CSUM|GRE_KEY|GRE_SEQ)) {
                if (t->parms.o_flags&GRE_CSUM)
@@ -1272,6 +1270,7 @@ static int ip6gre_tunnel_init(struct net_device *dev)
                u64_stats_init(&ip6gre_tunnel_stats->syncp);
        }
 
+       dev->iflink = tunnel->parms.link;
 
        return 0;
 }
@@ -1481,6 +1480,8 @@ static int ip6gre_tap_init(struct net_device *dev)
        if (!dev->tstats)
                return -ENOMEM;
 
+       dev->iflink = tunnel->parms.link;
+
        return 0;
 }
 
index a071563..01e12d0 100644 (file)
@@ -69,7 +69,8 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
        int nhoff;
 
        if (unlikely(skb_shinfo(skb)->gso_type &
-                    ~(SKB_GSO_UDP |
+                    ~(SKB_GSO_TCPV4 |
+                      SKB_GSO_UDP |
                       SKB_GSO_DODGY |
                       SKB_GSO_TCP_ECN |
                       SKB_GSO_GRE |
index 9409887..9cb94cf 100644 (file)
@@ -272,9 +272,6 @@ static int ip6_tnl_create2(struct net_device *dev)
        int err;
 
        t = netdev_priv(dev);
-       err = ip6_tnl_dev_init(dev);
-       if (err < 0)
-               goto out;
 
        err = register_netdevice(dev);
        if (err < 0)
@@ -1462,6 +1459,7 @@ ip6_tnl_change_mtu(struct net_device *dev, int new_mtu)
 
 
 static const struct net_device_ops ip6_tnl_netdev_ops = {
+       .ndo_init       = ip6_tnl_dev_init,
        .ndo_uninit     = ip6_tnl_dev_uninit,
        .ndo_start_xmit = ip6_tnl_xmit,
        .ndo_do_ioctl   = ip6_tnl_ioctl,
@@ -1546,16 +1544,10 @@ static int __net_init ip6_fb_tnl_dev_init(struct net_device *dev)
        struct ip6_tnl *t = netdev_priv(dev);
        struct net *net = dev_net(dev);
        struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
-       int err = ip6_tnl_dev_init_gen(dev);
-
-       if (err)
-               return err;
 
        t->parms.proto = IPPROTO_IPV6;
        dev_hold(dev);
 
-       ip6_tnl_link_config(t);
-
        rcu_assign_pointer(ip6n->tnls_wc[0], t);
        return 0;
 }
index b04ed72..8db6c98 100644 (file)
@@ -79,15 +79,13 @@ int udp_tunnel6_xmit_skb(struct socket *sock, struct dst_entry *dst,
        uh->source = src_port;
 
        uh->len = htons(skb->len);
-       uh->check = 0;
 
        memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
        IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED
                            | IPSKB_REROUTED);
        skb_dst_set(skb, dst);
 
-       udp6_set_csum(udp_get_no_check6_tx(sk), skb, &inet6_sk(sk)->saddr,
-                     &sk->sk_v6_daddr, skb->len);
+       udp6_set_csum(udp_get_no_check6_tx(sk), skb, saddr, daddr, skb->len);
 
        __skb_push(skb, sizeof(*ip6h));
        skb_reset_network_header(skb);
index d440bb5..bcda14d 100644 (file)
@@ -172,10 +172,6 @@ static int vti6_tnl_create2(struct net_device *dev)
        struct vti6_net *ip6n = net_generic(net, vti6_net_id);
        int err;
 
-       err = vti6_dev_init(dev);
-       if (err < 0)
-               goto out;
-
        err = register_netdevice(dev);
        if (err < 0)
                goto out;
@@ -783,6 +779,7 @@ static int vti6_change_mtu(struct net_device *dev, int new_mtu)
 }
 
 static const struct net_device_ops vti6_netdev_ops = {
+       .ndo_init       = vti6_dev_init,
        .ndo_uninit     = vti6_dev_uninit,
        .ndo_start_xmit = vti6_tnl_xmit,
        .ndo_do_ioctl   = vti6_ioctl,
@@ -852,16 +849,10 @@ static int __net_init vti6_fb_tnl_dev_init(struct net_device *dev)
        struct ip6_tnl *t = netdev_priv(dev);
        struct net *net = dev_net(dev);
        struct vti6_net *ip6n = net_generic(net, vti6_net_id);
-       int err = vti6_dev_init_gen(dev);
-
-       if (err)
-               return err;
 
        t->parms.proto = IPPROTO_IPV6;
        dev_hold(dev);
 
-       vti6_link_config(t);
-
        rcu_assign_pointer(ip6n->tnls_wc[0], t);
        return 0;
 }
@@ -914,6 +905,15 @@ static int vti6_newlink(struct net *src_net, struct net_device *dev,
        return vti6_tnl_create2(dev);
 }
 
+static void vti6_dellink(struct net_device *dev, struct list_head *head)
+{
+       struct net *net = dev_net(dev);
+       struct vti6_net *ip6n = net_generic(net, vti6_net_id);
+
+       if (dev != ip6n->fb_tnl_dev)
+               unregister_netdevice_queue(dev, head);
+}
+
 static int vti6_changelink(struct net_device *dev, struct nlattr *tb[],
                           struct nlattr *data[])
 {
@@ -989,6 +989,7 @@ static struct rtnl_link_ops vti6_link_ops __read_mostly = {
        .setup          = vti6_dev_setup,
        .validate       = vti6_validate,
        .newlink        = vti6_newlink,
+       .dellink        = vti6_dellink,
        .changelink     = vti6_changelink,
        .get_size       = vti6_get_size,
        .fill_info      = vti6_fill_info,
@@ -1029,6 +1030,7 @@ static int __net_init vti6_init_net(struct net *net)
        if (!ip6n->fb_tnl_dev)
                goto err_alloc_dev;
        dev_net_set(ip6n->fb_tnl_dev, net);
+       ip6n->fb_tnl_dev->rtnl_link_ops = &vti6_link_ops;
 
        err = vti6_fb_tnl_dev_init(ip6n->fb_tnl_dev);
        if (err < 0)
index 0171f08..1a01d79 100644 (file)
@@ -1439,6 +1439,10 @@ reg_pernet_fail:
 
 void ip6_mr_cleanup(void)
 {
+       rtnl_unregister(RTNL_FAMILY_IP6MR, RTM_GETROUTE);
+#ifdef CONFIG_IPV6_PIMSM_V2
+       inet6_del_protocol(&pim6_protocol, IPPROTO_PIM);
+#endif
        unregister_netdevice_notifier(&ip6_mr_notifier);
        unregister_pernet_subsys(&ip6mr_net_ops);
        kmem_cache_destroy(mrt_cachep);
index 9648de2..ed2c4e4 100644 (file)
@@ -1550,7 +1550,7 @@ static void ip6_mc_hdr(struct sock *sk, struct sk_buff *skb,
        hdr->daddr = *daddr;
 }
 
-static struct sk_buff *mld_newpack(struct inet6_dev *idev, int size)
+static struct sk_buff *mld_newpack(struct inet6_dev *idev, unsigned int mtu)
 {
        struct net_device *dev = idev->dev;
        struct net *net = dev_net(dev);
@@ -1561,13 +1561,13 @@ static struct sk_buff *mld_newpack(struct inet6_dev *idev, int size)
        const struct in6_addr *saddr;
        int hlen = LL_RESERVED_SPACE(dev);
        int tlen = dev->needed_tailroom;
+       unsigned int size = mtu + hlen + tlen;
        int err;
        u8 ra[8] = { IPPROTO_ICMPV6, 0,
                     IPV6_TLV_ROUTERALERT, 2, 0, 0,
                     IPV6_TLV_PADN, 0 };
 
        /* we assume size > sizeof(ra) here */
-       size += hlen + tlen;
        /* limit our allocations to order-0 page */
        size = min_t(int, size, SKB_MAX_ORDER(0, 0));
        skb = sock_alloc_send_skb(sk, size, 1, &err);
@@ -1576,6 +1576,8 @@ static struct sk_buff *mld_newpack(struct inet6_dev *idev, int size)
                return NULL;
 
        skb->priority = TC_PRIO_CONTROL;
+       skb->reserved_tailroom = skb_end_offset(skb) -
+                                min(mtu, skb_end_offset(skb));
        skb_reserve(skb, hlen);
 
        if (__ipv6_get_lladdr(idev, &addr_buf, IFA_F_TENTATIVE)) {
@@ -1690,8 +1692,7 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc,
        return skb;
 }
 
-#define AVAILABLE(skb) ((skb) ? ((skb)->dev ? (skb)->dev->mtu - (skb)->len : \
-       skb_tailroom(skb)) : 0)
+#define AVAILABLE(skb) ((skb) ? skb_availroom(skb) : 0)
 
 static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,
        int type, int gdeleted, int sdeleted, int crsend)
index 8a7ac68..529c119 100644 (file)
@@ -25,6 +25,7 @@ static void nft_masq_ipv6_eval(const struct nft_expr *expr,
        struct nf_nat_range range;
        unsigned int verdict;
 
+       memset(&range, 0, sizeof(range));
        range.flags = priv->flags;
 
        verdict = nf_nat_masquerade_ipv6(pkt->skb, &range, pkt->out);
index 58e5b47..a24557a 100644 (file)
@@ -195,10 +195,8 @@ static int ipip6_tunnel_create(struct net_device *dev)
        struct sit_net *sitn = net_generic(net, sit_net_id);
        int err;
 
-       err = ipip6_tunnel_init(dev);
-       if (err < 0)
-               goto out;
-       ipip6_tunnel_clone_6rd(dev, sitn);
+       memcpy(dev->dev_addr, &t->parms.iph.saddr, 4);
+       memcpy(dev->broadcast, &t->parms.iph.daddr, 4);
 
        if ((__force u16)t->parms.i_flags & SIT_ISATAP)
                dev->priv_flags |= IFF_ISATAP;
@@ -207,7 +205,8 @@ static int ipip6_tunnel_create(struct net_device *dev)
        if (err < 0)
                goto out;
 
-       strcpy(t->parms.name, dev->name);
+       ipip6_tunnel_clone_6rd(dev, sitn);
+
        dev->rtnl_link_ops = &sit_link_ops;
 
        dev_hold(dev);
@@ -1330,6 +1329,7 @@ static int ipip6_tunnel_change_mtu(struct net_device *dev, int new_mtu)
 }
 
 static const struct net_device_ops ipip6_netdev_ops = {
+       .ndo_init       = ipip6_tunnel_init,
        .ndo_uninit     = ipip6_tunnel_uninit,
        .ndo_start_xmit = sit_tunnel_xmit,
        .ndo_do_ioctl   = ipip6_tunnel_ioctl,
@@ -1378,9 +1378,7 @@ static int ipip6_tunnel_init(struct net_device *dev)
 
        tunnel->dev = dev;
        tunnel->net = dev_net(dev);
-
-       memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4);
-       memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
+       strcpy(tunnel->parms.name, dev->name);
 
        ipip6_tunnel_bind_dev(dev);
        dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
@@ -1405,7 +1403,6 @@ static int __net_init ipip6_fb_tunnel_init(struct net_device *dev)
 
        tunnel->dev = dev;
        tunnel->net = dev_net(dev);
-       strcpy(tunnel->parms.name, dev->name);
 
        iph->version            = 4;
        iph->protocol           = IPPROTO_IPV6;
index ace29b6..dc495ae 100644 (file)
@@ -903,7 +903,10 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb)
        if (th->rst)
                return;
 
-       if (!ipv6_unicast_destination(skb))
+       /* If sk not NULL, it means we did a successful lookup and incoming
+        * route had to be correct. prequeue might have dropped our dst.
+        */
+       if (!sk && !ipv6_unicast_destination(skb))
                return;
 
 #ifdef CONFIG_TCP_MD5SIG
index 91729b8..1b095ca 100644 (file)
@@ -1764,6 +1764,7 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock,
        struct ipxhdr *ipx = NULL;
        struct sk_buff *skb;
        int copied, rc;
+       bool locked = true;
 
        lock_sock(sk);
        /* put the autobinding in */
@@ -1790,6 +1791,8 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock,
        if (sock_flag(sk, SOCK_ZAPPED))
                goto out;
 
+       release_sock(sk);
+       locked = false;
        skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
                                flags & MSG_DONTWAIT, &rc);
        if (!skb) {
@@ -1826,7 +1829,8 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock,
 out_free:
        skb_free_datagram(sk, skb);
 out:
-       release_sock(sk);
+       if (locked)
+               release_sock(sk);
        return rc;
 }
 
index ec24378..09d9caa 100644 (file)
@@ -53,6 +53,9 @@ int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
                __aligned(__alignof__(struct aead_request));
        struct aead_request *aead_req = (void *) aead_req_data;
 
+       if (data_len == 0)
+               return -EINVAL;
+
        memset(aead_req, 0, sizeof(aead_req_data));
 
        sg_init_one(&pt, data, data_len);
index 56b5357..509bc15 100644 (file)
@@ -805,7 +805,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 
        memset(&params, 0, sizeof(params));
        memset(&csa_ie, 0, sizeof(csa_ie));
-       err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon,
+       err = ieee80211_parse_ch_switch_ie(sdata, elems,
                                           ifibss->chandef.chan->band,
                                           sta_flags, ifibss->bssid, &csa_ie);
        /* can't switch to destination channel, fail */
index c2aaec4..8c68da3 100644 (file)
@@ -1642,7 +1642,6 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
  * ieee80211_parse_ch_switch_ie - parses channel switch IEs
  * @sdata: the sdata of the interface which has received the frame
  * @elems: parsed 802.11 elements received with the frame
- * @beacon: indicates if the frame was a beacon or probe response
  * @current_band: indicates the current band
  * @sta_flags: contains information about own capabilities and restrictions
  *     to decide which channel switch announcements can be accepted. Only the
@@ -1656,7 +1655,7 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
  * Return: 0 on success, <0 on error and >0 if there is nothing to parse.
  */
 int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
-                                struct ieee802_11_elems *elems, bool beacon,
+                                struct ieee802_11_elems *elems,
                                 enum ieee80211_band current_band,
                                 u32 sta_flags, u8 *bssid,
                                 struct ieee80211_csa_ie *csa_ie);
index af23722..653f5eb 100644 (file)
@@ -766,10 +766,12 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
        int i, flushed;
        struct ps_data *ps;
        struct cfg80211_chan_def chandef;
+       bool cancel_scan;
 
        clear_bit(SDATA_STATE_RUNNING, &sdata->state);
 
-       if (rcu_access_pointer(local->scan_sdata) == sdata)
+       cancel_scan = rcu_access_pointer(local->scan_sdata) == sdata;
+       if (cancel_scan)
                ieee80211_scan_cancel(local);
 
        /*
@@ -898,6 +900,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
                list_del(&sdata->u.vlan.list);
                mutex_unlock(&local->mtx);
                RCU_INIT_POINTER(sdata->vif.chanctx_conf, NULL);
+               /* see comment in the default case below */
+               ieee80211_free_keys(sdata, true);
                /* no need to tell driver */
                break;
        case NL80211_IFTYPE_MONITOR:
@@ -923,17 +927,16 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
                /*
                 * When we get here, the interface is marked down.
                 * Free the remaining keys, if there are any
-                * (shouldn't be, except maybe in WDS mode?)
+                * (which can happen in AP mode if userspace sets
+                * keys before the interface is operating, and maybe
+                * also in WDS mode)
                 *
                 * Force the key freeing to always synchronize_net()
                 * to wait for the RX path in case it is using this
-                * interface enqueuing frames at this very time on
+                * interface enqueuing frames at this very time on
                 * another CPU.
                 */
                ieee80211_free_keys(sdata, true);
-
-               /* fall through */
-       case NL80211_IFTYPE_AP:
                skb_queue_purge(&sdata->skb_queue);
        }
 
@@ -991,6 +994,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_recalc_ps(local, -1);
 
+       if (cancel_scan)
+               flush_delayed_work(&local->scan_work);
+
        if (local->open_count == 0) {
                ieee80211_stop_device(local);
 
index e9f99c1..0c8b2a7 100644 (file)
@@ -874,7 +874,7 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
 
        memset(&params, 0, sizeof(params));
        memset(&csa_ie, 0, sizeof(csa_ie));
-       err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, band,
+       err = ieee80211_parse_ch_switch_ie(sdata, elems, band,
                                           sta_flags, sdata->vif.addr,
                                           &csa_ie);
        if (err < 0)
index 2de8870..93af0f1 100644 (file)
@@ -1072,7 +1072,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 
        current_band = cbss->channel->band;
        memset(&csa_ie, 0, sizeof(csa_ie));
-       res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band,
+       res = ieee80211_parse_ch_switch_ie(sdata, elems, current_band,
                                           ifmgd->flags,
                                           ifmgd->associated->bssid, &csa_ie);
        if (res < 0)
@@ -1168,7 +1168,8 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work);
        else
                mod_timer(&ifmgd->chswitch_timer,
-                         TU_TO_EXP_TIME(csa_ie.count * cbss->beacon_interval));
+                         TU_TO_EXP_TIME((csa_ie.count - 1) *
+                                        cbss->beacon_interval));
 }
 
 static bool
index df90ce2..408fd8a 100644 (file)
@@ -252,19 +252,16 @@ minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u8 index,
        cur_thr = mi->groups[cur_group].rates[cur_idx].cur_tp;
        cur_prob = mi->groups[cur_group].rates[cur_idx].probability;
 
-       tmp_group = tp_list[j - 1] / MCS_GROUP_RATES;
-       tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES;
-       tmp_thr = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
-       tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability;
-
-       while (j > 0 && (cur_thr > tmp_thr ||
-             (cur_thr == tmp_thr && cur_prob > tmp_prob))) {
-               j--;
+       do {
                tmp_group = tp_list[j - 1] / MCS_GROUP_RATES;
                tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES;
                tmp_thr = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
                tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability;
-       }
+               if (cur_thr < tmp_thr ||
+                   (cur_thr == tmp_thr && cur_prob <= tmp_prob))
+                       break;
+               j--;
+       } while (j > 0);
 
        if (j < MAX_THR_RATES - 1) {
                memmove(&tp_list[j + 1], &tp_list[j], (sizeof(*tp_list) *
index b04ca40..a37f9af 100644 (file)
@@ -1678,11 +1678,14 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
        sc = le16_to_cpu(hdr->seq_ctrl);
        frag = sc & IEEE80211_SCTL_FRAG;
 
-       if (likely((!ieee80211_has_morefrags(fc) && frag == 0) ||
-                  is_multicast_ether_addr(hdr->addr1))) {
-               /* not fragmented */
+       if (likely(!ieee80211_has_morefrags(fc) && frag == 0))
+               goto out;
+
+       if (is_multicast_ether_addr(hdr->addr1)) {
+               rx->local->dot11MulticastReceivedFrameCount++;
                goto out;
        }
+
        I802_DEBUG_INC(rx->local->rx_handlers_fragments);
 
        if (skb_linearize(rx->skb))
@@ -1775,10 +1778,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
  out:
        if (rx->sta)
                rx->sta->rx_packets++;
-       if (is_multicast_ether_addr(hdr->addr1))
-               rx->local->dot11MulticastReceivedFrameCount++;
-       else
-               ieee80211_led_rx(rx->local);
+       ieee80211_led_rx(rx->local);
        return RX_CONTINUE;
 }
 
index 6ab0090..efeba56 100644 (file)
@@ -22,7 +22,7 @@
 #include "wme.h"
 
 int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
-                                struct ieee802_11_elems *elems, bool beacon,
+                                struct ieee802_11_elems *elems,
                                 enum ieee80211_band current_band,
                                 u32 sta_flags, u8 *bssid,
                                 struct ieee80211_csa_ie *csa_ie)
@@ -91,19 +91,13 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
                return -EINVAL;
        }
 
-       if (!beacon && sec_chan_offs) {
+       if (sec_chan_offs) {
                secondary_channel_offset = sec_chan_offs->sec_chan_offs;
-       } else if (beacon && ht_oper) {
-               secondary_channel_offset =
-                       ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
        } else if (!(sta_flags & IEEE80211_STA_DISABLE_HT)) {
-               /* If it's not a beacon, HT is enabled and the IE not present,
-                * it's 20 MHz, 802.11-2012 8.5.2.6:
-                *      This element [the Secondary Channel Offset Element] is
-                *      present when switching to a 40 MHz channel. It may be
-                *      present when switching to a 20 MHz channel (in which
-                *      case the secondary channel offset is set to SCN).
-                */
+               /* If the secondary channel offset IE is not present,
+                * we can't know what's the post-CSA offset, so the
+                * best we can do is use 20MHz.
+               */
                secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
        }
 
index 86f9d76..d259da3 100644 (file)
@@ -1863,6 +1863,12 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
        if (*op < IP_SET_OP_VERSION) {
                /* Check the version at the beginning of operations */
                struct ip_set_req_version *req_version = data;
+
+               if (*len < sizeof(struct ip_set_req_version)) {
+                       ret = -EINVAL;
+                       goto done;
+               }
+
                if (req_version->version != IPSET_PROTOCOL) {
                        ret = -EPROTO;
                        goto done;
index 437a366..bd90bf8 100644 (file)
@@ -846,6 +846,8 @@ ip_vs_prepare_tunneled_skb(struct sk_buff *skb, int skb_af,
                new_skb = skb_realloc_headroom(skb, max_headroom);
                if (!new_skb)
                        goto error;
+               if (skb->sk)
+                       skb_set_owner_w(new_skb, skb->sk);
                consume_skb(skb);
                skb = new_skb;
        }
index 11ab4b0..66e8425 100644 (file)
@@ -3484,13 +3484,8 @@ static void nft_chain_commit_update(struct nft_trans *trans)
        }
 }
 
-/* Schedule objects for release via rcu to make sure no packets are accesing
- * removed rules.
- */
-static void nf_tables_commit_release_rcu(struct rcu_head *rt)
+static void nf_tables_commit_release(struct nft_trans *trans)
 {
-       struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head);
-
        switch (trans->msg_type) {
        case NFT_MSG_DELTABLE:
                nf_tables_table_destroy(&trans->ctx);
@@ -3612,10 +3607,11 @@ static int nf_tables_commit(struct sk_buff *skb)
                }
        }
 
+       synchronize_rcu();
+
        list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
                list_del(&trans->list);
-               trans->ctx.nla = NULL;
-               call_rcu(&trans->rcu_head, nf_tables_commit_release_rcu);
+               nf_tables_commit_release(trans);
        }
 
        nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN);
@@ -3623,13 +3619,8 @@ static int nf_tables_commit(struct sk_buff *skb)
        return 0;
 }
 
-/* Schedule objects for release via rcu to make sure no packets are accesing
- * aborted rules.
- */
-static void nf_tables_abort_release_rcu(struct rcu_head *rt)
+static void nf_tables_abort_release(struct nft_trans *trans)
 {
-       struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head);
-
        switch (trans->msg_type) {
        case NFT_MSG_NEWTABLE:
                nf_tables_table_destroy(&trans->ctx);
@@ -3725,11 +3716,12 @@ static int nf_tables_abort(struct sk_buff *skb)
                }
        }
 
+       synchronize_rcu();
+
        list_for_each_entry_safe_reverse(trans, next,
                                         &net->nft.commit_list, list) {
                list_del(&trans->list);
-               trans->ctx.nla = NULL;
-               call_rcu(&trans->rcu_head, nf_tables_abort_release_rcu);
+               nf_tables_abort_release(trans);
        }
 
        return 0;
index 6c5a915..13c2e17 100644 (file)
@@ -47,6 +47,8 @@ static const int nfnl_group2type[NFNLGRP_MAX+1] = {
        [NFNLGRP_CONNTRACK_EXP_NEW]     = NFNL_SUBSYS_CTNETLINK_EXP,
        [NFNLGRP_CONNTRACK_EXP_UPDATE]  = NFNL_SUBSYS_CTNETLINK_EXP,
        [NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP,
+       [NFNLGRP_NFTABLES]              = NFNL_SUBSYS_NFTABLES,
+       [NFNLGRP_ACCT_QUOTA]            = NFNL_SUBSYS_ACCT,
 };
 
 void nfnl_lock(__u8 subsys_id)
@@ -464,7 +466,12 @@ static void nfnetlink_rcv(struct sk_buff *skb)
 static int nfnetlink_bind(int group)
 {
        const struct nfnetlink_subsystem *ss;
-       int type = nfnl_group2type[group];
+       int type;
+
+       if (group <= NFNLGRP_NONE || group > NFNLGRP_MAX)
+               return -EINVAL;
+
+       type = nfnl_group2type[group];
 
        rcu_read_lock();
        ss = nfnetlink_get_subsys(type);
@@ -514,6 +521,9 @@ static int __init nfnetlink_init(void)
 {
        int i;
 
+       for (i = NFNLGRP_NONE + 1; i <= NFNLGRP_MAX; i++)
+               BUG_ON(nfnl_group2type[i] == NFNL_SUBSYS_NONE);
+
        for (i=0; i<NFNL_SUBSYS_COUNT; i++)
                mutex_init(&table[i].mutex);
 
index 9d6d6f6..265e190 100644 (file)
 #include <linux/netfilter_ipv6/ip6_tables.h>
 #include <net/netfilter/nf_tables.h>
 
-static const struct {
-       const char      *name;
-       u8              type;
-} table_to_chaintype[] = {
-       { "filter",     NFT_CHAIN_T_DEFAULT },
-       { "raw",        NFT_CHAIN_T_DEFAULT },
-       { "security",   NFT_CHAIN_T_DEFAULT },
-       { "mangle",     NFT_CHAIN_T_ROUTE },
-       { "nat",        NFT_CHAIN_T_NAT },
-       { },
-};
-
-static int nft_compat_table_to_chaintype(const char *table)
-{
-       int i;
-
-       for (i = 0; table_to_chaintype[i].name != NULL; i++) {
-               if (strcmp(table_to_chaintype[i].name, table) == 0)
-                       return table_to_chaintype[i].type;
-       }
-
-       return -1;
-}
-
 static int nft_compat_chain_validate_dependency(const char *tablename,
                                                const struct nft_chain *chain)
 {
-       enum nft_chain_type type;
        const struct nft_base_chain *basechain;
 
        if (!tablename || !(chain->flags & NFT_BASE_CHAIN))
                return 0;
 
-       type = nft_compat_table_to_chaintype(tablename);
-       if (type < 0)
-               return -EINVAL;
-
        basechain = nft_base_chain(chain);
-       if (basechain->type->type != type)
+       if (strcmp(tablename, "nat") == 0 &&
+           basechain->type->type != NFT_CHAIN_T_NAT)
                return -EINVAL;
 
        return 0;
@@ -117,7 +89,7 @@ nft_target_set_tgchk_param(struct xt_tgchk_param *par,
                           struct xt_target *target, void *info,
                           union nft_entry *entry, u8 proto, bool inv)
 {
-       par->net        = &init_net;
+       par->net        = ctx->net;
        par->table      = ctx->table->name;
        switch (ctx->afi->family) {
        case AF_INET:
@@ -324,7 +296,7 @@ nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx,
                          struct xt_match *match, void *info,
                          union nft_entry *entry, u8 proto, bool inv)
 {
-       par->net        = &init_net;
+       par->net        = ctx->net;
        par->table      = ctx->table->name;
        switch (ctx->afi->family) {
        case AF_INET:
@@ -374,7 +346,7 @@ nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
        union nft_entry e = {};
        int ret;
 
-       ret = nft_compat_chain_validate_dependency(match->name, ctx->chain);
+       ret = nft_compat_chain_validate_dependency(match->table, ctx->chain);
        if (ret < 0)
                goto err;
 
@@ -448,7 +420,7 @@ static int nft_match_validate(const struct nft_ctx *ctx,
                if (!(hook_mask & match->hooks))
                        return -EINVAL;
 
-               ret = nft_compat_chain_validate_dependency(match->name,
+               ret = nft_compat_chain_validate_dependency(match->table,
                                                           ctx->chain);
                if (ret < 0)
                        return ret;
index f1de72d..0007b81 100644 (file)
@@ -1440,7 +1440,7 @@ static void netlink_unbind(int group, long unsigned int groups,
                return;
 
        for (undo = 0; undo < group; undo++)
-               if (test_bit(group, &groups))
+               if (test_bit(undo, &groups))
                        nlk->netlink_unbind(undo);
 }
 
@@ -1492,7 +1492,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr,
                        netlink_insert(sk, net, nladdr->nl_pid) :
                        netlink_autobind(sock);
                if (err) {
-                       netlink_unbind(nlk->ngroups - 1, groups, nlk);
+                       netlink_unbind(nlk->ngroups, groups, nlk);
                        return err;
                }
        }
@@ -2509,6 +2509,7 @@ __netlink_kernel_create(struct net *net, int unit, struct module *module,
                nl_table[unit].module = module;
                if (cfg) {
                        nl_table[unit].bind = cfg->bind;
+                       nl_table[unit].unbind = cfg->unbind;
                        nl_table[unit].flags = cfg->flags;
                        if (cfg->compare)
                                nl_table[unit].compare = cfg->compare;
index 006886d..8c4229b 100644 (file)
@@ -246,11 +246,11 @@ static void update_ipv6_checksum(struct sk_buff *skb, u8 l4_proto,
 {
        int transport_len = skb->len - skb_transport_offset(skb);
 
-       if (l4_proto == IPPROTO_TCP) {
+       if (l4_proto == NEXTHDR_TCP) {
                if (likely(transport_len >= sizeof(struct tcphdr)))
                        inet_proto_csum_replace16(&tcp_hdr(skb)->check, skb,
                                                  addr, new_addr, 1);
-       } else if (l4_proto == IPPROTO_UDP) {
+       } else if (l4_proto == NEXTHDR_UDP) {
                if (likely(transport_len >= sizeof(struct udphdr))) {
                        struct udphdr *uh = udp_hdr(skb);
 
@@ -261,6 +261,10 @@ static void update_ipv6_checksum(struct sk_buff *skb, u8 l4_proto,
                                        uh->check = CSUM_MANGLED_0;
                        }
                }
+       } else if (l4_proto == NEXTHDR_ICMP) {
+               if (likely(transport_len >= sizeof(struct icmp6hdr)))
+                       inet_proto_csum_replace16(&icmp6_hdr(skb)->icmp6_cksum,
+                                                 skb, addr, new_addr, 1);
        }
 }
 
@@ -722,8 +726,6 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
 
                case OVS_ACTION_ATTR_SAMPLE:
                        err = sample(dp, skb, key, a);
-                       if (unlikely(err)) /* skb already freed. */
-                               return err;
                        break;
                }
 
index e6d7255..f9e556b 100644 (file)
@@ -1265,7 +1265,7 @@ static size_t ovs_dp_cmd_msg_size(void)
        return msgsize;
 }
 
-/* Called with ovs_mutex or RCU read lock. */
+/* Called with ovs_mutex. */
 static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb,
                                u32 portid, u32 seq, u32 flags, u8 cmd)
 {
@@ -1555,7 +1555,7 @@ static int ovs_dp_cmd_get(struct sk_buff *skb, struct genl_info *info)
        if (!reply)
                return -ENOMEM;
 
-       rcu_read_lock();
+       ovs_lock();
        dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs);
        if (IS_ERR(dp)) {
                err = PTR_ERR(dp);
@@ -1564,12 +1564,12 @@ static int ovs_dp_cmd_get(struct sk_buff *skb, struct genl_info *info)
        err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid,
                                   info->snd_seq, 0, OVS_DP_CMD_NEW);
        BUG_ON(err < 0);
-       rcu_read_unlock();
+       ovs_unlock();
 
        return genlmsg_reply(reply, info);
 
 err_unlock_free:
-       rcu_read_unlock();
+       ovs_unlock();
        kfree_skb(reply);
        return err;
 }
@@ -1581,8 +1581,8 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
        int skip = cb->args[0];
        int i = 0;
 
-       rcu_read_lock();
-       list_for_each_entry_rcu(dp, &ovs_net->dps, list_node) {
+       ovs_lock();
+       list_for_each_entry(dp, &ovs_net->dps, list_node) {
                if (i >= skip &&
                    ovs_dp_cmd_fill_info(dp, skb, NETLINK_CB(cb->skb).portid,
                                         cb->nlh->nlmsg_seq, NLM_F_MULTI,
@@ -1590,7 +1590,7 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
                        break;
                i++;
        }
-       rcu_read_unlock();
+       ovs_unlock();
 
        cb->args[0] = i;
 
index 939bcb3..089b195 100644 (file)
@@ -145,7 +145,7 @@ static bool match_validate(const struct sw_flow_match *match,
        if (match->key->eth.type == htons(ETH_P_ARP)
                        || match->key->eth.type == htons(ETH_P_RARP)) {
                key_expected |= 1 << OVS_KEY_ATTR_ARP;
-               if (match->mask && (match->mask->key.eth.type == htons(0xffff)))
+               if (match->mask && (match->mask->key.tp.src == htons(0xff)))
                        mask_allowed |= 1 << OVS_KEY_ATTR_ARP;
        }
 
@@ -689,6 +689,13 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
                                ipv6_key->ipv6_frag, OVS_FRAG_TYPE_MAX);
                        return -EINVAL;
                }
+
+               if (!is_mask && ipv6_key->ipv6_label & htonl(0xFFF00000)) {
+                       OVS_NLERR("IPv6 flow label %x is out of range (max=%x).\n",
+                                 ntohl(ipv6_key->ipv6_label), (1 << 20) - 1);
+                       return -EINVAL;
+               }
+
                SW_FLOW_KEY_PUT(match, ipv6.label,
                                ipv6_key->ipv6_label, is_mask);
                SW_FLOW_KEY_PUT(match, ip.proto,
index 87d20f4..07c04a8 100644 (file)
@@ -378,7 +378,7 @@ static void unregister_prot_hook(struct sock *sk, bool sync)
                __unregister_prot_hook(sk, sync);
 }
 
-static inline __pure struct page *pgv_to_page(void *addr)
+static inline struct page * __pure pgv_to_page(void *addr)
 {
        if (is_vmalloc_addr(addr))
                return vmalloc_to_page(addr);
index 0e85291..fb7976a 100644 (file)
@@ -862,8 +862,6 @@ int sctp_auth_set_key(struct sctp_endpoint *ep,
                list_add(&cur_key->key_list, sh_keys);
 
        cur_key->key = key;
-       sctp_auth_key_hold(key);
-
        return 0;
 nomem:
        if (!replace)
index ab734be..9f32741 100644 (file)
@@ -2609,6 +2609,9 @@ do_addr_param:
                addr_param = param.v + sizeof(sctp_addip_param_t);
 
                af = sctp_get_af_specific(param_type2af(param.p->type));
+               if (af == NULL)
+                       break;
+
                af->from_addr_param(&addr, addr_param,
                                    htons(asoc->peer.port), 0);
 
index afb292c..53ed8d3 100644 (file)
@@ -1353,6 +1353,7 @@ gss_stringify_acceptor(struct rpc_cred *cred)
        char *string = NULL;
        struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
        struct gss_cl_ctx *ctx;
+       unsigned int len;
        struct xdr_netobj *acceptor;
 
        rcu_read_lock();
@@ -1360,15 +1361,39 @@ gss_stringify_acceptor(struct rpc_cred *cred)
        if (!ctx)
                goto out;
 
-       acceptor = &ctx->gc_acceptor;
+       len = ctx->gc_acceptor.len;
+       rcu_read_unlock();
 
        /* no point if there's no string */
-       if (!acceptor->len)
-               goto out;
-
-       string = kmalloc(acceptor->len + 1, GFP_KERNEL);
+       if (!len)
+               return NULL;
+realloc:
+       string = kmalloc(len + 1, GFP_KERNEL);
        if (!string)
+               return NULL;
+
+       rcu_read_lock();
+       ctx = rcu_dereference(gss_cred->gc_ctx);
+
+       /* did the ctx disappear or was it replaced by one with no acceptor? */
+       if (!ctx || !ctx->gc_acceptor.len) {
+               kfree(string);
+               string = NULL;
                goto out;
+       }
+
+       acceptor = &ctx->gc_acceptor;
+
+       /*
+        * Did we find a new acceptor that's longer than the original? Allocate
+        * a longer buffer and try again.
+        */
+       if (len < acceptor->len) {
+               len = acceptor->len;
+               rcu_read_unlock();
+               kfree(string);
+               goto realloc;
+       }
 
        memcpy(string, acceptor->data, acceptor->len);
        string[acceptor->len] = '\0';
index 3f959c6..f9c052d 100644 (file)
@@ -1019,17 +1019,12 @@ static int receive_cb_reply(struct svc_sock *svsk, struct svc_rqst *rqstp)
        xid = *p++;
        calldir = *p;
 
-       if (bc_xprt)
-               req = xprt_lookup_rqst(bc_xprt, xid);
-
-       if (!req) {
-               printk(KERN_NOTICE
-                       "%s: Got unrecognized reply: "
-                       "calldir 0x%x xpt_bc_xprt %p xid %08x\n",
-                       __func__, ntohl(calldir),
-                       bc_xprt, ntohl(xid));
+       if (!bc_xprt)
                return -EAGAIN;
-       }
+       spin_lock_bh(&bc_xprt->transport_lock);
+       req = xprt_lookup_rqst(bc_xprt, xid);
+       if (!req)
+               goto unlock_notfound;
 
        memcpy(&req->rq_private_buf, &req->rq_rcv_buf, sizeof(struct xdr_buf));
        /*
@@ -1040,11 +1035,21 @@ static int receive_cb_reply(struct svc_sock *svsk, struct svc_rqst *rqstp)
        dst = &req->rq_private_buf.head[0];
        src = &rqstp->rq_arg.head[0];
        if (dst->iov_len < src->iov_len)
-               return -EAGAIN; /* whatever; just giving up. */
+               goto unlock_eagain; /* whatever; just giving up. */
        memcpy(dst->iov_base, src->iov_base, src->iov_len);
        xprt_complete_rqst(req->rq_task, rqstp->rq_arg.len);
        rqstp->rq_arg.len = 0;
+       spin_unlock_bh(&bc_xprt->transport_lock);
        return 0;
+unlock_notfound:
+       printk(KERN_NOTICE
+               "%s: Got unrecognized reply: "
+               "calldir 0x%x xpt_bc_xprt %p xid %08x\n",
+               __func__, ntohl(calldir),
+               bc_xprt, ntohl(xid));
+unlock_eagain:
+       spin_unlock_bh(&bc_xprt->transport_lock);
+       return -EAGAIN;
 }
 
 static int copy_pages_to_kvecs(struct kvec *vec, struct page **pages, int len)
index b8960c4..200e378 100644 (file)
@@ -117,6 +117,7 @@ struct keyring_search_context {
 #define KEYRING_SEARCH_NO_UPDATE_TIME  0x0004  /* Don't update times */
 #define KEYRING_SEARCH_NO_CHECK_PERM   0x0008  /* Don't check permissions */
 #define KEYRING_SEARCH_DETECT_TOO_DEEP 0x0010  /* Give an error on excessive depth */
+#define KEYRING_SEARCH_SKIP_EXPIRED    0x0020  /* Ignore expired keys (intention to replace) */
 
        int (*iterator)(const void *object, void *iterator_data);
 
index eff88a5..4743d71 100644 (file)
@@ -26,6 +26,8 @@
 #include <asm/uaccess.h>
 #include "internal.h"
 
+#define KEY_MAX_DESC_SIZE 4096
+
 static int key_get_type_from_user(char *type,
                                  const char __user *_type,
                                  unsigned len)
@@ -78,7 +80,7 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type,
 
        description = NULL;
        if (_description) {
-               description = strndup_user(_description, PAGE_SIZE);
+               description = strndup_user(_description, KEY_MAX_DESC_SIZE);
                if (IS_ERR(description)) {
                        ret = PTR_ERR(description);
                        goto error;
@@ -177,7 +179,7 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type,
                goto error;
 
        /* pull the description into kernel space */
-       description = strndup_user(_description, PAGE_SIZE);
+       description = strndup_user(_description, KEY_MAX_DESC_SIZE);
        if (IS_ERR(description)) {
                ret = PTR_ERR(description);
                goto error;
@@ -287,7 +289,7 @@ long keyctl_join_session_keyring(const char __user *_name)
        /* fetch the name from userspace */
        name = NULL;
        if (_name) {
-               name = strndup_user(_name, PAGE_SIZE);
+               name = strndup_user(_name, KEY_MAX_DESC_SIZE);
                if (IS_ERR(name)) {
                        ret = PTR_ERR(name);
                        goto error;
@@ -562,8 +564,9 @@ long keyctl_describe_key(key_serial_t keyid,
 {
        struct key *key, *instkey;
        key_ref_t key_ref;
-       char *tmpbuf;
+       char *infobuf;
        long ret;
+       int desclen, infolen;
 
        key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_NEED_VIEW);
        if (IS_ERR(key_ref)) {
@@ -586,38 +589,31 @@ long keyctl_describe_key(key_serial_t keyid,
        }
 
 okay:
-       /* calculate how much description we're going to return */
-       ret = -ENOMEM;
-       tmpbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
-       if (!tmpbuf)
-               goto error2;
-
        key = key_ref_to_ptr(key_ref);
+       desclen = strlen(key->description);
 
-       ret = snprintf(tmpbuf, PAGE_SIZE - 1,
-                      "%s;%d;%d;%08x;%s",
-                      key->type->name,
-                      from_kuid_munged(current_user_ns(), key->uid),
-                      from_kgid_munged(current_user_ns(), key->gid),
-                      key->perm,
-                      key->description ?: "");
-
-       /* include a NUL char at the end of the data */
-       if (ret > PAGE_SIZE - 1)
-               ret = PAGE_SIZE - 1;
-       tmpbuf[ret] = 0;
-       ret++;
+       /* calculate how much information we're going to return */
+       ret = -ENOMEM;
+       infobuf = kasprintf(GFP_KERNEL,
+                           "%s;%d;%d;%08x;",
+                           key->type->name,
+                           from_kuid_munged(current_user_ns(), key->uid),
+                           from_kgid_munged(current_user_ns(), key->gid),
+                           key->perm);
+       if (!infobuf)
+               goto error2;
+       infolen = strlen(infobuf);
+       ret = infolen + desclen + 1;
 
        /* consider returning the data */
-       if (buffer && buflen > 0) {
-               if (buflen > ret)
-                       buflen = ret;
-
-               if (copy_to_user(buffer, tmpbuf, buflen) != 0)
+       if (buffer && buflen >= ret) {
+               if (copy_to_user(buffer, infobuf, infolen) != 0 ||
+                   copy_to_user(buffer + infolen, key->description,
+                                desclen + 1) != 0)
                        ret = -EFAULT;
        }
 
-       kfree(tmpbuf);
+       kfree(infobuf);
 error2:
        key_ref_put(key_ref);
 error:
@@ -649,7 +645,7 @@ long keyctl_keyring_search(key_serial_t ringid,
        if (ret < 0)
                goto error;
 
-       description = strndup_user(_description, PAGE_SIZE);
+       description = strndup_user(_description, KEY_MAX_DESC_SIZE);
        if (IS_ERR(description)) {
                ret = PTR_ERR(description);
                goto error;
index 8177010..e72548b 100644 (file)
@@ -546,7 +546,8 @@ static int keyring_search_iterator(const void *object, void *iterator_data)
                }
 
                if (key->expiry && ctx->now.tv_sec >= key->expiry) {
-                       ctx->result = ERR_PTR(-EKEYEXPIRED);
+                       if (!(ctx->flags & KEYRING_SEARCH_SKIP_EXPIRED))
+                               ctx->result = ERR_PTR(-EKEYEXPIRED);
                        kleave(" = %d [expire]", ctx->skipped_ret);
                        goto skipped;
                }
@@ -628,6 +629,10 @@ static bool search_nested_keyrings(struct key *keyring,
               ctx->index_key.type->name,
               ctx->index_key.description);
 
+#define STATE_CHECKS (KEYRING_SEARCH_NO_STATE_CHECK | KEYRING_SEARCH_DO_STATE_CHECK)
+       BUG_ON((ctx->flags & STATE_CHECKS) == 0 ||
+              (ctx->flags & STATE_CHECKS) == STATE_CHECKS);
+
        if (ctx->index_key.description)
                ctx->index_key.desc_len = strlen(ctx->index_key.description);
 
@@ -637,7 +642,6 @@ static bool search_nested_keyrings(struct key *keyring,
        if (ctx->match_data.lookup_type == KEYRING_SEARCH_LOOKUP_ITERATE ||
            keyring_compare_object(keyring, &ctx->index_key)) {
                ctx->skipped_ret = 2;
-               ctx->flags |= KEYRING_SEARCH_DO_STATE_CHECK;
                switch (ctx->iterator(keyring_key_to_ptr(keyring), ctx)) {
                case 1:
                        goto found;
@@ -649,8 +653,6 @@ static bool search_nested_keyrings(struct key *keyring,
        }
 
        ctx->skipped_ret = 0;
-       if (ctx->flags & KEYRING_SEARCH_NO_STATE_CHECK)
-               ctx->flags &= ~KEYRING_SEARCH_DO_STATE_CHECK;
 
        /* Start processing a new keyring */
 descend_to_keyring:
index bb4337c..0c7aea4 100644 (file)
@@ -516,6 +516,8 @@ struct key *request_key_and_link(struct key_type *type,
                .match_data.cmp         = key_default_cmp,
                .match_data.raw_data    = description,
                .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
+               .flags                  = (KEYRING_SEARCH_DO_STATE_CHECK |
+                                          KEYRING_SEARCH_SKIP_EXPIRED),
        };
        struct key *key;
        key_ref_t key_ref;
index 6639e2c..5d672f7 100644 (file)
@@ -249,6 +249,7 @@ struct key *key_get_instantiation_authkey(key_serial_t target_id)
                .match_data.cmp         = key_default_cmp,
                .match_data.raw_data    = description,
                .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
+               .flags                  = KEYRING_SEARCH_DO_STATE_CHECK,
        };
        struct key *authkey;
        key_ref_t authkey_ref;
index e663141..c603b20 100644 (file)
@@ -4725,9 +4725,10 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
        err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm);
        if (err) {
                if (err == -EINVAL) {
-                       WARN_ONCE(1, "selinux_nlmsg_perm: unrecognized netlink message:"
-                                 " protocol=%hu nlmsg_type=%hu sclass=%hu\n",
-                                 sk->sk_protocol, nlh->nlmsg_type, sksec->sclass);
+                       printk(KERN_WARNING
+                              "SELinux: unrecognized netlink message:"
+                              " protocol=%hu nlmsg_type=%hu sclass=%hu\n",
+                              sk->sk_protocol, nlh->nlmsg_type, sksec->sclass);
                        if (!selinux_enforcing || security_get_allow_unknown())
                                err = 0;
                }
index 42ded99..c6ff94a 100644 (file)
@@ -216,6 +216,8 @@ static char *snd_pcm_format_names[] = {
        FORMAT(DSD_U8),
        FORMAT(DSD_U16_LE),
        FORMAT(DSD_U32_LE),
+       FORMAT(DSD_U16_BE),
+       FORMAT(DSD_U32_BE),
 };
 
 const char *snd_pcm_format_name(snd_pcm_format_t format)
index ae7a0fe..ebe8444 100644 (file)
@@ -152,6 +152,14 @@ static struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = {
                .width = 32, .phys = 32, .le = 1, .signd = 0,
                .silence = { 0x69, 0x69, 0x69, 0x69 },
        },
+       [SNDRV_PCM_FORMAT_DSD_U16_BE] = {
+               .width = 16, .phys = 16, .le = 0, .signd = 0,
+               .silence = { 0x69, 0x69 },
+       },
+       [SNDRV_PCM_FORMAT_DSD_U32_BE] = {
+               .width = 32, .phys = 32, .le = 0, .signd = 0,
+               .silence = { 0x69, 0x69, 0x69, 0x69 },
+       },
        /* FIXME: the following three formats are not defined properly yet */
        [SNDRV_PCM_FORMAT_MPEG] = {
                .le = -1, .signd = -1,
index 9ab1e63..48b6c5a 100644 (file)
@@ -219,6 +219,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
                         "{Intel, LPT_LP},"
                         "{Intel, WPT_LP},"
                         "{Intel, SPT},"
+                        "{Intel, SPT_LP},"
                         "{Intel, HPT},"
                         "{Intel, PBG},"
                         "{Intel, SCH},"
@@ -297,7 +298,8 @@ enum {
 
 /* quirks for ATI/AMD HDMI */
 #define AZX_DCAPS_PRESET_ATI_HDMI \
-       (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB)
+       (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB|\
+        AZX_DCAPS_NO_MSI64)
 
 /* quirks for Nvidia */
 #define AZX_DCAPS_PRESET_NVIDIA \
@@ -1485,6 +1487,7 @@ static int azx_first_init(struct azx *chip)
        struct snd_card *card = chip->card;
        int err;
        unsigned short gcap;
+       unsigned int dma_bits = 64;
 
 #if BITS_PER_LONG != 64
        /* Fix up base address on ULI M5461 */
@@ -1508,9 +1511,14 @@ static int azx_first_init(struct azx *chip)
                return -ENXIO;
        }
 
-       if (chip->msi)
+       if (chip->msi) {
+               if (chip->driver_caps & AZX_DCAPS_NO_MSI64) {
+                       dev_dbg(card->dev, "Disabling 64bit MSI\n");
+                       pci->no_64bit_msi = true;
+               }
                if (pci_enable_msi(pci) < 0)
                        chip->msi = 0;
+       }
 
        if (azx_acquire_irq(chip, 0) < 0)
                return -EBUSY;
@@ -1521,9 +1529,14 @@ static int azx_first_init(struct azx *chip)
        gcap = azx_readw(chip, GCAP);
        dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap);
 
+       /* AMD devices support 40 or 48bit DMA, take the safe one */
+       if (chip->pci->vendor == PCI_VENDOR_ID_AMD)
+               dma_bits = 40;
+
        /* disable SB600 64bit support for safety */
        if (chip->pci->vendor == PCI_VENDOR_ID_ATI) {
                struct pci_dev *p_smbus;
+               dma_bits = 40;
                p_smbus = pci_get_device(PCI_VENDOR_ID_ATI,
                                         PCI_DEVICE_ID_ATI_SBX00_SMBUS,
                                         NULL);
@@ -1553,9 +1566,11 @@ static int azx_first_init(struct azx *chip)
        }
 
        /* allow 64bit DMA address if supported by H/W */
-       if ((gcap & AZX_GCAP_64OK) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64)))
-               pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(64));
-       else {
+       if (!(gcap & AZX_GCAP_64OK))
+               dma_bits = 32;
+       if (!pci_set_dma_mask(pci, DMA_BIT_MASK(dma_bits))) {
+               pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(dma_bits));
+       } else {
                pci_set_dma_mask(pci, DMA_BIT_MASK(32));
                pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32));
        }
@@ -2004,6 +2019,9 @@ static const struct pci_device_id azx_ids[] = {
        /* Sunrise Point */
        { PCI_DEVICE(0x8086, 0xa170),
          .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+       /* Sunrise Point-LP */
+       { PCI_DEVICE(0x8086, 0x9d70),
+         .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
        /* Haswell */
        { PCI_DEVICE(0x8086, 0x0a0c),
          .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL },
index 949cd43..5016014 100644 (file)
@@ -171,6 +171,7 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
 #define AZX_DCAPS_PM_RUNTIME   (1 << 26)       /* runtime PM support */
 #define AZX_DCAPS_I915_POWERWELL (1 << 27)     /* HSW i915 powerwell support */
 #define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28)  /* CORBRP clears itself after reset */
+#define AZX_DCAPS_NO_MSI64      (1 << 29)      /* Stick to 32-bit MSIs */
 
 /* HD Audio class code */
 #define PCI_CLASS_MULTIMEDIA_HD_AUDIO  0x0403
index 71e4bad..e9ebc7b 100644 (file)
@@ -43,6 +43,7 @@ struct conexant_spec {
        unsigned int num_eapds;
        hda_nid_t eapds[4];
        bool dynamic_eapd;
+       hda_nid_t mute_led_eapd;
 
        unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
 
@@ -163,6 +164,17 @@ static void cx_auto_vmaster_hook(void *private_data, int enabled)
        cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled);
 }
 
+/* turn on/off EAPD according to Master switch (inversely!) for mute LED */
+static void cx_auto_vmaster_hook_mute_led(void *private_data, int enabled)
+{
+       struct hda_codec *codec = private_data;
+       struct conexant_spec *spec = codec->spec;
+
+       snd_hda_codec_write(codec, spec->mute_led_eapd, 0,
+                           AC_VERB_SET_EAPD_BTLENABLE,
+                           enabled ? 0x00 : 0x02);
+}
+
 static int cx_auto_build_controls(struct hda_codec *codec)
 {
        int err;
@@ -223,6 +235,7 @@ enum {
        CXT_FIXUP_TOSHIBA_P105,
        CXT_FIXUP_HP_530,
        CXT_FIXUP_CAP_MIX_AMP_5047,
+       CXT_FIXUP_MUTE_LED_EAPD,
 };
 
 /* for hda_fixup_thinkpad_acpi() */
@@ -557,6 +570,18 @@ static void cxt_fixup_olpc_xo(struct hda_codec *codec,
        }
 }
 
+static void cxt_fixup_mute_led_eapd(struct hda_codec *codec,
+                                   const struct hda_fixup *fix, int action)
+{
+       struct conexant_spec *spec = codec->spec;
+
+       if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+               spec->mute_led_eapd = 0x1b;
+               spec->dynamic_eapd = 1;
+               spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook_mute_led;
+       }
+}
+
 /*
  * Fix max input level on mixer widget to 0dB
  * (originally it has 0x2b steps with 0dB offset 0x14)
@@ -705,6 +730,10 @@ static const struct hda_fixup cxt_fixups[] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = cxt_fixup_cap_mix_amp_5047,
        },
+       [CXT_FIXUP_MUTE_LED_EAPD] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = cxt_fixup_mute_led_eapd,
+       },
 };
 
 static const struct snd_pci_quirk cxt5045_fixups[] = {
@@ -762,6 +791,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
        SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520", CXT_PINCFG_LENOVO_TP410),
        SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT_PINCFG_LENOVO_TP410),
        SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT_PINCFG_LENOVO_TP410),
+       SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo IdeaPad Z560", CXT_FIXUP_MUTE_LED_EAPD),
        SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC),
        SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC),
        SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC),
@@ -780,6 +810,7 @@ static const struct hda_model_fixup cxt5066_fixup_models[] = {
        { .id = CXT_PINCFG_LEMOTE_A1004, .name = "lemote-a1004" },
        { .id = CXT_PINCFG_LEMOTE_A1205, .name = "lemote-a1205" },
        { .id = CXT_FIXUP_OLPC_XO, .name = "olpc-xo" },
+       { .id = CXT_FIXUP_MUTE_LED_EAPD, .name = "mute-led-eapd" },
        {}
 };
 
index c9cf248..b118a5b 100644 (file)
@@ -288,21 +288,91 @@ static void alc880_unsol_event(struct hda_codec *codec, unsigned int res)
        snd_hda_jack_unsol_event(codec, res >> 2);
 }
 
-/* additional initialization for ALC888 variants */
-static void alc888_coef_init(struct hda_codec *codec)
+/* Change EAPD to verb control */
+static void alc_fill_eapd_coef(struct hda_codec *codec)
 {
-       if (alc_get_coef0(codec) == 0x20)
-               /* alc888S-VC */
-               alc_write_coef_idx(codec, 7, 0x830);
-        else
-                /* alc888-VB */
-               alc_write_coef_idx(codec, 7, 0x3030);
+       int coef;
+
+       coef = alc_get_coef0(codec);
+
+       switch (codec->vendor_id) {
+       case 0x10ec0262:
+               alc_update_coef_idx(codec, 0x7, 0, 1<<5);
+               break;
+       case 0x10ec0267:
+       case 0x10ec0268:
+               alc_update_coef_idx(codec, 0x7, 0, 1<<13);
+               break;
+       case 0x10ec0269:
+               if ((coef & 0x00f0) == 0x0010)
+                       alc_update_coef_idx(codec, 0xd, 0, 1<<14);
+               if ((coef & 0x00f0) == 0x0020)
+                       alc_update_coef_idx(codec, 0x4, 1<<15, 0);
+               if ((coef & 0x00f0) == 0x0030)
+                       alc_update_coef_idx(codec, 0x10, 1<<9, 0);
+               break;
+       case 0x10ec0280:
+       case 0x10ec0284:
+       case 0x10ec0290:
+       case 0x10ec0292:
+               alc_update_coef_idx(codec, 0x4, 1<<15, 0);
+               break;
+       case 0x10ec0233:
+       case 0x10ec0255:
+       case 0x10ec0282:
+       case 0x10ec0283:
+       case 0x10ec0286:
+       case 0x10ec0288:
+               alc_update_coef_idx(codec, 0x10, 1<<9, 0);
+               break;
+       case 0x10ec0285:
+       case 0x10ec0293:
+               alc_update_coef_idx(codec, 0xa, 1<<13, 0);
+               break;
+       case 0x10ec0662:
+               if ((coef & 0x00f0) == 0x0030)
+                       alc_update_coef_idx(codec, 0x4, 1<<10, 0); /* EAPD Ctrl */
+               break;
+       case 0x10ec0272:
+       case 0x10ec0273:
+       case 0x10ec0663:
+       case 0x10ec0665:
+       case 0x10ec0670:
+       case 0x10ec0671:
+       case 0x10ec0672:
+               alc_update_coef_idx(codec, 0xd, 0, 1<<14); /* EAPD Ctrl */
+               break;
+       case 0x10ec0668:
+               alc_update_coef_idx(codec, 0x7, 3<<13, 0);
+               break;
+       case 0x10ec0867:
+               alc_update_coef_idx(codec, 0x4, 1<<10, 0);
+               break;
+       case 0x10ec0888:
+               if ((coef & 0x00f0) == 0x0020 || (coef & 0x00f0) == 0x0030)
+                       alc_update_coef_idx(codec, 0x7, 1<<5, 0);
+               break;
+       case 0x10ec0892:
+               alc_update_coef_idx(codec, 0x7, 1<<5, 0);
+               break;
+       case 0x10ec0899:
+       case 0x10ec0900:
+               alc_update_coef_idx(codec, 0x7, 1<<1, 0);
+               break;
+       }
 }
 
-/* additional initialization for ALC889 variants */
-static void alc889_coef_init(struct hda_codec *codec)
+/* additional initialization for ALC888 variants */
+static void alc888_coef_init(struct hda_codec *codec)
 {
-       alc_update_coef_idx(codec, 7, 0, 0x2010);
+       switch (alc_get_coef0(codec) & 0x00f0) {
+       /* alc888-VA */
+       case 0x00:
+       /* alc888-VB */
+       case 0x10:
+               alc_update_coef_idx(codec, 7, 0, 0x2030); /* Turn EAPD to High */
+               break;
+       }
 }
 
 /* turn on/off EAPD control (only if available) */
@@ -343,6 +413,7 @@ static void alc_eapd_shutup(struct hda_codec *codec)
 /* generic EAPD initialization */
 static void alc_auto_init_amp(struct hda_codec *codec, int type)
 {
+       alc_fill_eapd_coef(codec);
        alc_auto_setup_eapd(codec, true);
        switch (type) {
        case ALC_INIT_GPIO1:
@@ -359,25 +430,15 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type)
                case 0x10ec0260:
                        alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x2010);
                        break;
-               case 0x10ec0262:
                case 0x10ec0880:
                case 0x10ec0882:
                case 0x10ec0883:
                case 0x10ec0885:
-               case 0x10ec0887:
-               /*case 0x10ec0889:*/ /* this causes an SPDIF problem */
-               case 0x10ec0900:
-                       alc889_coef_init(codec);
+                       alc_update_coef_idx(codec, 7, 0, 0x2030);
                        break;
                case 0x10ec0888:
                        alc888_coef_init(codec);
                        break;
-#if 0 /* XXX: This may cause the silent output on speaker on some machines */
-               case 0x10ec0267:
-               case 0x10ec0268:
-                       alc_update_coef_idx(codec, 7, 0, 0x3000);
-                       break;
-#endif /* XXX */
                }
                break;
        }
@@ -1710,7 +1771,7 @@ static void alc889_fixup_coef(struct hda_codec *codec,
 {
        if (action != HDA_FIXUP_ACT_INIT)
                return;
-       alc889_coef_init(codec);
+       alc_update_coef_idx(codec, 7, 0, 0x2030);
 }
 
 /* toggle speaker-output according to the hp-jack state */
@@ -3350,6 +3411,27 @@ static void alc269_fixup_hp_gpio_mic1_led(struct hda_codec *codec,
        }
 }
 
+static void alc280_fixup_hp_gpio4(struct hda_codec *codec,
+                                  const struct hda_fixup *fix, int action)
+{
+       /* Like hp_gpio_mic1_led, but also needs GPIO4 low to enable headphone amp */
+       struct alc_spec *spec = codec->spec;
+       static const struct hda_verb gpio_init[] = {
+               { 0x01, AC_VERB_SET_GPIO_MASK, 0x18 },
+               { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x18 },
+               {}
+       };
+
+       if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+               spec->gen.vmaster_mute.hook = alc269_fixup_hp_gpio_mute_hook;
+               spec->gen.cap_sync_hook = alc269_fixup_hp_cap_mic_mute_hook;
+               spec->gpio_led = 0;
+               spec->cap_mute_led_nid = 0x18;
+               snd_hda_add_verbs(codec, gpio_init);
+               codec->power_filter = led_power_filter;
+       }
+}
+
 static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec,
                                const struct hda_fixup *fix, int action)
 {
@@ -4217,6 +4299,7 @@ enum {
        ALC283_FIXUP_BXBT2807_MIC,
        ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED,
        ALC282_FIXUP_ASPIRE_V5_PINS,
+       ALC280_FIXUP_HP_GPIO4,
 };
 
 static const struct hda_fixup alc269_fixups[] = {
@@ -4437,6 +4520,8 @@ static const struct hda_fixup alc269_fixups[] = {
        [ALC269_FIXUP_HEADSET_MODE] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc_fixup_headset_mode,
+               .chained = true,
+               .chain_id = ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED
        },
        [ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC] = {
                .type = HDA_FIXUP_FUNC,
@@ -4626,6 +4711,8 @@ static const struct hda_fixup alc269_fixups[] = {
        [ALC255_FIXUP_HEADSET_MODE] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc_fixup_headset_mode_alc255,
+               .chained = true,
+               .chain_id = ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED
        },
        [ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC] = {
                .type = HDA_FIXUP_FUNC,
@@ -4661,8 +4748,6 @@ static const struct hda_fixup alc269_fixups[] = {
        [ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc_fixup_dell_wmi,
-               .chained_before = true,
-               .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
        },
        [ALC282_FIXUP_ASPIRE_V5_PINS] = {
                .type = HDA_FIXUP_PINS,
@@ -4680,7 +4765,10 @@ static const struct hda_fixup alc269_fixups[] = {
                        { },
                },
        },
-
+       [ALC280_FIXUP_HP_GPIO4] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc280_fixup_hp_gpio4,
+       },
 };
 
 static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -4697,13 +4785,13 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1028, 0x05f4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x05f5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x05f6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
-       SND_PCI_QUIRK(0x1028, 0x0610, "Dell", ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED),
        SND_PCI_QUIRK(0x1028, 0x0615, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
        SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
-       SND_PCI_QUIRK(0x1028, 0x061f, "Dell", ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED),
        SND_PCI_QUIRK(0x1028, 0x0638, "Dell Inspiron 5439", ALC290_FIXUP_MONO_SPEAKERS_HSJACK),
        SND_PCI_QUIRK(0x1028, 0x064a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x064b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+       SND_PCI_QUIRK(0x1028, 0x06d9, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+       SND_PCI_QUIRK(0x1028, 0x06da, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
@@ -4728,21 +4816,15 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x22cf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x22dc, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x22fb, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
-       SND_PCI_QUIRK(0x103c, 0x8004, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        /* ALC290 */
        SND_PCI_QUIRK(0x103c, 0x221b, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x2221, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x2225, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
-       SND_PCI_QUIRK(0x103c, 0x2246, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
-       SND_PCI_QUIRK(0x103c, 0x2247, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
-       SND_PCI_QUIRK(0x103c, 0x2248, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
-       SND_PCI_QUIRK(0x103c, 0x2249, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x2253, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x2254, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x2255, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x2256, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x2257, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
-       SND_PCI_QUIRK(0x103c, 0x2258, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x2259, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x225a, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x2260, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
@@ -4751,7 +4833,6 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x2265, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x2272, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x2273, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
-       SND_PCI_QUIRK(0x103c, 0x2277, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x2278, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
        SND_PCI_QUIRK(0x103c, 0x227f, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x2282, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
@@ -4804,7 +4885,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x17aa, 0x220e, "Thinkpad T440p", ALC292_FIXUP_TPT440_DOCK),
        SND_PCI_QUIRK(0x17aa, 0x2210, "Thinkpad T540p", ALC292_FIXUP_TPT440_DOCK),
        SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad T440", ALC292_FIXUP_TPT440_DOCK),
-       SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+       SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad X240", ALC292_FIXUP_TPT440_DOCK),
        SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP),
        SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
@@ -4984,6 +5065,19 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
                {0x17, 0x40000000},
                {0x1d, 0x40700001},
                {0x21, 0x02211040}),
+       SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC280_FIXUP_HP_GPIO4,
+               {0x12, 0x90a60130},
+               {0x13, 0x40000000},
+               {0x14, 0x90170110},
+               {0x15, 0x0421101f},
+               {0x16, 0x411111f0},
+               {0x17, 0x411111f0},
+               {0x18, 0x411111f0},
+               {0x19, 0x411111f0},
+               {0x1a, 0x04a11020},
+               {0x1b, 0x411111f0},
+               {0x1d, 0x40748605},
+               {0x1e, 0x411111f0}),
        SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED,
                {0x12, 0x90a60140},
                {0x13, 0x40000000},
@@ -5194,9 +5288,6 @@ static void alc269_fill_coef(struct hda_codec *codec)
                }
        }
 
-       /* Class D */
-       alc_update_coef_idx(codec, 0xd, 0, 1<<14);
-
        /* HP */
        alc_update_coef_idx(codec, 0x4, 0, 1<<11);
 }
@@ -5651,6 +5742,35 @@ static void alc662_fixup_led_gpio1(struct hda_codec *codec,
        }
 }
 
+static struct coef_fw alc668_coefs[] = {
+       WRITE_COEF(0x01, 0xbebe), WRITE_COEF(0x02, 0xaaaa), WRITE_COEF(0x03,    0x0),
+       WRITE_COEF(0x04, 0x0180), WRITE_COEF(0x06,    0x0), WRITE_COEF(0x07, 0x0f80),
+       WRITE_COEF(0x08, 0x0031), WRITE_COEF(0x0a, 0x0060), WRITE_COEF(0x0b,    0x0),
+       WRITE_COEF(0x0c, 0x7cf7), WRITE_COEF(0x0d, 0x1080), WRITE_COEF(0x0e, 0x7f7f),
+       WRITE_COEF(0x0f, 0xcccc), WRITE_COEF(0x10, 0xddcc), WRITE_COEF(0x11, 0x0001),
+       WRITE_COEF(0x13,    0x0), WRITE_COEF(0x14, 0x2aa0), WRITE_COEF(0x17, 0xa940),
+       WRITE_COEF(0x19,    0x0), WRITE_COEF(0x1a,    0x0), WRITE_COEF(0x1b,    0x0),
+       WRITE_COEF(0x1c,    0x0), WRITE_COEF(0x1d,    0x0), WRITE_COEF(0x1e, 0x7418),
+       WRITE_COEF(0x1f, 0x0804), WRITE_COEF(0x20, 0x4200), WRITE_COEF(0x21, 0x0468),
+       WRITE_COEF(0x22, 0x8ccc), WRITE_COEF(0x23, 0x0250), WRITE_COEF(0x24, 0x7418),
+       WRITE_COEF(0x27,    0x0), WRITE_COEF(0x28, 0x8ccc), WRITE_COEF(0x2a, 0xff00),
+       WRITE_COEF(0x2b, 0x8000), WRITE_COEF(0xa7, 0xff00), WRITE_COEF(0xa8, 0x8000),
+       WRITE_COEF(0xaa, 0x2e17), WRITE_COEF(0xab, 0xa0c0), WRITE_COEF(0xac,    0x0),
+       WRITE_COEF(0xad,    0x0), WRITE_COEF(0xae, 0x2ac6), WRITE_COEF(0xaf, 0xa480),
+       WRITE_COEF(0xb0,    0x0), WRITE_COEF(0xb1,    0x0), WRITE_COEF(0xb2,    0x0),
+       WRITE_COEF(0xb3,    0x0), WRITE_COEF(0xb4,    0x0), WRITE_COEF(0xb5, 0x1040),
+       WRITE_COEF(0xb6, 0xd697), WRITE_COEF(0xb7, 0x902b), WRITE_COEF(0xb8, 0xd697),
+       WRITE_COEF(0xb9, 0x902b), WRITE_COEF(0xba, 0xb8ba), WRITE_COEF(0xbb, 0xaaab),
+       WRITE_COEF(0xbc, 0xaaaf), WRITE_COEF(0xbd, 0x6aaa), WRITE_COEF(0xbe, 0x1c02),
+       WRITE_COEF(0xc0, 0x00ff), WRITE_COEF(0xc1, 0x0fa6),
+       {}
+};
+
+static void alc668_restore_default_value(struct hda_codec *codec)
+{
+       alc_process_coef_fw(codec, alc668_coefs);
+}
+
 enum {
        ALC662_FIXUP_ASPIRE,
        ALC662_FIXUP_LED_GPIO1,
@@ -6077,29 +6197,6 @@ static const struct snd_hda_pin_quirk alc662_pin_fixup_tbl[] = {
        {}
 };
 
-static void alc662_fill_coef(struct hda_codec *codec)
-{
-       int coef;
-
-       coef = alc_get_coef0(codec);
-
-       switch (codec->vendor_id) {
-       case 0x10ec0662:
-               if ((coef & 0x00f0) == 0x0030)
-                       alc_update_coef_idx(codec, 0x4, 1<<10, 0); /* EAPD Ctrl */
-               break;
-       case 0x10ec0272:
-       case 0x10ec0273:
-       case 0x10ec0663:
-       case 0x10ec0665:
-       case 0x10ec0670:
-       case 0x10ec0671:
-       case 0x10ec0672:
-               alc_update_coef_idx(codec, 0xd, 0, 1<<14); /* EAPD Ctrl */
-               break;
-       }
-}
-
 /*
  */
 static int patch_alc662(struct hda_codec *codec)
@@ -6118,8 +6215,11 @@ static int patch_alc662(struct hda_codec *codec)
 
        alc_fix_pll_init(codec, 0x20, 0x04, 15);
 
-       spec->init_hook = alc662_fill_coef;
-       alc662_fill_coef(codec);
+       switch (codec->vendor_id) {
+       case 0x10ec0668:
+               spec->init_hook = alc668_restore_default_value;
+               break;
+       }
 
        snd_hda_pick_fixup(codec, alc662_fixup_models,
                       alc662_fixup_tbl, alc662_fixups);
index d88edfc..865e090 100644 (file)
@@ -1,10 +1,14 @@
 snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o
-snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o
+snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o soc-ops.o
 
 ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),)
 snd-soc-core-objs += soc-generic-dmaengine-pcm.o
 endif
 
+ifneq ($(CONFIG_SND_SOC_AC97_BUS),)
+snd-soc-core-objs += soc-ac97.o
+endif
+
 obj-$(CONFIG_SND_SOC)  += snd-soc-core.o
 obj-$(CONFIG_SND_SOC)  += codecs/
 obj-$(CONFIG_SND_SOC)  += generic/
index 27e3fc4..fb38783 100644 (file)
@@ -52,12 +52,3 @@ config SND_AT91_SOC_SAM9X5_WM8731
        help
          Say Y if you want to add support for audio SoC on an
          at91sam9x5 based board that is using WM8731 codec.
-
-config SND_AT91_SOC_AFEB9260
-       tristate "SoC Audio support for AFEB9260 board"
-       depends on ARCH_AT91 && ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC
-       select SND_ATMEL_SOC_PDC
-       select SND_ATMEL_SOC_SSC
-       select SND_SOC_TLV320AIC23_I2C
-       help
-         Say Y here to support sound on AFEB9260 board.
index 5baabc8..466a821 100644 (file)
@@ -17,4 +17,3 @@ snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o
 obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
 obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o
 obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o
-obj-$(CONFIG_SND_AT91_SOC_AFEB9260) += snd-soc-afeb9260.o
index f403f39..b1cc2a4 100644 (file)
@@ -310,7 +310,10 @@ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
                 * transmit and receive, so if a value has already
                 * been set, it must match this value.
                 */
-               if (ssc_p->cmr_div == 0)
+               if (ssc_p->dir_mask !=
+                       (SSC_DIR_MASK_PLAYBACK | SSC_DIR_MASK_CAPTURE))
+                       ssc_p->cmr_div = div;
+               else if (ssc_p->cmr_div == 0)
                        ssc_p->cmr_div = div;
                else
                        if (div != ssc_p->cmr_div)
diff --git a/sound/soc/atmel/snd-soc-afeb9260.c b/sound/soc/atmel/snd-soc-afeb9260.c
deleted file mode 100644 (file)
index 9579799..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * afeb9260.c  --  SoC audio for AFEB9260
- *
- * Copyright (C) 2009 Sergey Lapin <slapin@ossfans.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-
-#include <linux/atmel-ssc.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <mach/hardware.h>
-#include <linux/gpio.h>
-
-#include "../codecs/tlv320aic23.h"
-#include "atmel-pcm.h"
-#include "atmel_ssc_dai.h"
-
-#define CODEC_CLOCK    12000000
-
-static int afeb9260_hw_params(struct snd_pcm_substream *substream,
-                        struct snd_pcm_hw_params *params)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       int err;
-
-       /* Set the codec system clock for DAC and ADC */
-       err =
-           snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, SND_SOC_CLOCK_IN);
-
-       if (err < 0) {
-               printk(KERN_ERR "can't set codec system clock\n");
-               return err;
-       }
-
-       return err;
-}
-
-static struct snd_soc_ops afeb9260_ops = {
-       .hw_params = afeb9260_hw_params,
-};
-
-static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
-       SND_SOC_DAPM_HP("Headphone Jack", NULL),
-       SND_SOC_DAPM_LINE("Line In", NULL),
-       SND_SOC_DAPM_MIC("Mic Jack", NULL),
-};
-
-static const struct snd_soc_dapm_route afeb9260_audio_map[] = {
-       {"Headphone Jack", NULL, "LHPOUT"},
-       {"Headphone Jack", NULL, "RHPOUT"},
-
-       {"LLINEIN", NULL, "Line In"},
-       {"RLINEIN", NULL, "Line In"},
-
-       {"MICIN", NULL, "Mic Jack"},
-};
-
-
-/* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link afeb9260_dai = {
-       .name = "TLV320AIC23",
-       .stream_name = "AIC23",
-       .cpu_dai_name = "atmel-ssc-dai.0",
-       .codec_dai_name = "tlv320aic23-hifi",
-       .platform_name = "atmel_pcm-audio",
-       .codec_name = "tlv320aic23-codec.0-001a",
-       .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
-                  SND_SOC_DAIFMT_CBM_CFM,
-       .ops = &afeb9260_ops,
-};
-
-/* Audio machine driver */
-static struct snd_soc_card snd_soc_machine_afeb9260 = {
-       .name = "AFEB9260",
-       .owner = THIS_MODULE,
-       .dai_link = &afeb9260_dai,
-       .num_links = 1,
-
-       .dapm_widgets = tlv320aic23_dapm_widgets,
-       .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets),
-       .dapm_routes = afeb9260_audio_map,
-       .num_dapm_routes = ARRAY_SIZE(afeb9260_audio_map),
-};
-
-static struct platform_device *afeb9260_snd_device;
-
-static int __init afeb9260_soc_init(void)
-{
-       int err;
-       struct device *dev;
-
-       if (!(machine_is_afeb9260()))
-               return -ENODEV;
-
-
-       afeb9260_snd_device = platform_device_alloc("soc-audio", -1);
-       if (!afeb9260_snd_device) {
-               printk(KERN_ERR "ASoC: Platform device allocation failed\n");
-               return -ENOMEM;
-       }
-
-       platform_set_drvdata(afeb9260_snd_device, &snd_soc_machine_afeb9260);
-       err = platform_device_add(afeb9260_snd_device);
-       if (err)
-               goto err1;
-
-       dev = &afeb9260_snd_device->dev;
-
-       return 0;
-err1:
-       platform_device_put(afeb9260_snd_device);
-       return err;
-}
-
-static void __exit afeb9260_soc_exit(void)
-{
-       platform_device_unregister(afeb9260_snd_device);
-}
-
-module_init(afeb9260_soc_init);
-module_exit(afeb9260_soc_exit);
-
-MODULE_AUTHOR("Sergey Lapin <slapin@ossfans.org>");
-MODULE_DESCRIPTION("ALSA SoC for AFEB9260");
-MODULE_LICENSE("GPL");
-
index c8a2de1..5159a50 100644 (file)
@@ -205,7 +205,7 @@ static int au1xac97c_dai_probe(struct snd_soc_dai *dai)
 
 static struct snd_soc_dai_driver au1xac97c_dai_driver = {
        .name                   = "alchemy-ac97c",
-       .ac97_control           = 1,
+       .bus_control            = true,
        .probe                  = au1xac97c_dai_probe,
        .playback = {
                .rates          = AC97_RATES,
index 84f31e1..c6daec9 100644 (file)
@@ -343,7 +343,7 @@ static const struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
 };
 
 static const struct snd_soc_dai_driver au1xpsc_ac97_dai_template = {
-       .ac97_control           = 1,
+       .bus_control            = true,
        .probe                  = au1xpsc_ac97_probe,
        .playback = {
                .rates          = AC97_RATES,
index e82eb37..6bf21a6 100644 (file)
@@ -260,7 +260,7 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
 #endif
 
 static struct snd_soc_dai_driver bfin_ac97_dai = {
-       .ac97_control = 1,
+       .bus_control = true,
        .suspend = bf5xx_ac97_suspend,
        .resume = bf5xx_ac97_resume,
        .playback = {
index 3450e8f..0fa81a5 100644 (file)
@@ -46,8 +46,6 @@
 #include <linux/gpio.h>
 #include <asm/portmux.h>
 
-#include "../codecs/ad1980.h"
-
 #include "bf5xx-ac97.h"
 
 static struct snd_soc_card bf5xx_board;
index 5477c54..7b7fbcd 100644 (file)
@@ -36,7 +36,8 @@ config SND_EP93XX_SOC_EDB93XX
        tristate "SoC Audio support for Cirrus Logic EDB93xx boards"
        depends on SND_EP93XX_SOC && (MACH_EDB9301 || MACH_EDB9302 || MACH_EDB9302A || MACH_EDB9307A || MACH_EDB9315A)
        select SND_EP93XX_SOC_I2S
-       select SND_SOC_CS4271
+       select SND_SOC_CS4271_I2C if I2C
+       select SND_SOC_CS4271_SPI if SPI_MASTER
        help
          Say Y or M here if you want to add support for I2S audio on the
          Cirrus Logic EDB93xx boards.
index f30dadf..6b8a366 100644 (file)
@@ -338,7 +338,7 @@ static const struct snd_soc_dai_ops ep93xx_ac97_dai_ops = {
 static struct snd_soc_dai_driver ep93xx_ac97_dai = {
        .name           = "ep93xx-ac97",
        .id             = 0,
-       .ac97_control   = 1,
+       .bus_control    = true,
        .probe          = ep93xx_ac97_dai_probe,
        .playback       = {
                .stream_name    = "AC97 Playback",
index a68d173..cf7d335 100644 (file)
@@ -50,7 +50,8 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_CS42L73 if I2C
        select SND_SOC_CS4265 if I2C
        select SND_SOC_CS4270 if I2C
-       select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI
+       select SND_SOC_CS4271_I2C if I2C
+       select SND_SOC_CS4271_SPI if SPI_MASTER
        select SND_SOC_CS42XX8_I2C if I2C
        select SND_SOC_CX20442 if TTY
        select SND_SOC_DA7210 if I2C
@@ -85,7 +86,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_RT5645 if I2C
        select SND_SOC_RT5651 if I2C
        select SND_SOC_RT5670 if I2C
-       select SND_SOC_RT5677 if I2C
+       select SND_SOC_RT5677 if I2C && SPI_MASTER
        select SND_SOC_SGTL5000 if I2C
        select SND_SOC_SI476X if MFD_SI476X_CORE
        select SND_SOC_SIRF_AUDIO_CODEC
@@ -223,6 +224,7 @@ config SND_SOC_AD193X_I2C
        select SND_SOC_AD193X
 
 config SND_SOC_AD1980
+       select REGMAP_AC97
        tristate
 
 config SND_SOC_AD73311
@@ -336,7 +338,8 @@ config SND_SOC_CS42L51
        tristate
 
 config SND_SOC_CS42L51_I2C
-       tristate
+       tristate "Cirrus Logic CS42L51 CODEC (I2C)"
+       depends on I2C
        select SND_SOC_CS42L51
 
 config SND_SOC_CS42L52
@@ -370,8 +373,19 @@ config SND_SOC_CS4270_VD33_ERRATA
        depends on SND_SOC_CS4270
 
 config SND_SOC_CS4271
-       tristate "Cirrus Logic CS4271 CODEC"
-       depends on SND_SOC_I2C_AND_SPI
+       tristate
+
+config SND_SOC_CS4271_I2C
+       tristate "Cirrus Logic CS4271 CODEC (I2C)"
+       depends on I2C
+       select SND_SOC_CS4271
+       select REGMAP_I2C
+
+config SND_SOC_CS4271_SPI
+       tristate "Cirrus Logic CS4271 CODEC (SPI)"
+       depends on SPI_MASTER
+       select SND_SOC_CS4271
+       select REGMAP_SPI
 
 config SND_SOC_CS42XX8
        tristate
@@ -487,7 +501,8 @@ config SND_SOC_RT286
        depends on I2C
 
 config SND_SOC_RT5631
-       tristate
+       tristate "Realtek ALC5631/RT5631 CODEC"
+       depends on I2C
 
 config SND_SOC_RT5640
        tristate
@@ -504,6 +519,10 @@ config SND_SOC_RT5670
 config SND_SOC_RT5677
        tristate
 
+config SND_SOC_RT5677_SPI
+       tristate
+       default SND_SOC_RT5677
+
 #Freescale sgtl5000 codec
 config SND_SOC_SGTL5000
        tristate "Freescale SGTL5000 CODEC"
index 5dce451..a6f4fba 100644 (file)
@@ -41,6 +41,8 @@ snd-soc-cs42l73-objs := cs42l73.o
 snd-soc-cs4265-objs := cs4265.o
 snd-soc-cs4270-objs := cs4270.o
 snd-soc-cs4271-objs := cs4271.o
+snd-soc-cs4271-i2c-objs := cs4271-i2c.o
+snd-soc-cs4271-spi-objs := cs4271-spi.o
 snd-soc-cs42xx8-objs := cs42xx8.o
 snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
 snd-soc-cx20442-objs := cx20442.o
@@ -80,6 +82,7 @@ snd-soc-rt5645-objs := rt5645.o
 snd-soc-rt5651-objs := rt5651.o
 snd-soc-rt5670-objs := rt5670.o
 snd-soc-rt5677-objs := rt5677.o
+snd-soc-rt5677-spi-objs := rt5677-spi.o
 snd-soc-sgtl5000-objs := sgtl5000.o
 snd-soc-alc5623-objs := alc5623.o
 snd-soc-alc5632-objs := alc5632.o
@@ -217,6 +220,8 @@ obj-$(CONFIG_SND_SOC_CS42L73)       += snd-soc-cs42l73.o
 obj-$(CONFIG_SND_SOC_CS4265)   += snd-soc-cs4265.o
 obj-$(CONFIG_SND_SOC_CS4270)   += snd-soc-cs4270.o
 obj-$(CONFIG_SND_SOC_CS4271)   += snd-soc-cs4271.o
+obj-$(CONFIG_SND_SOC_CS4271_I2C)       += snd-soc-cs4271-i2c.o
+obj-$(CONFIG_SND_SOC_CS4271_SPI)       += snd-soc-cs4271-spi.o
 obj-$(CONFIG_SND_SOC_CS42XX8)  += snd-soc-cs42xx8.o
 obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
 obj-$(CONFIG_SND_SOC_CX20442)  += snd-soc-cx20442.o
@@ -256,6 +261,7 @@ obj-$(CONFIG_SND_SOC_RT5645)        += snd-soc-rt5645.o
 obj-$(CONFIG_SND_SOC_RT5651)   += snd-soc-rt5651.o
 obj-$(CONFIG_SND_SOC_RT5670)   += snd-soc-rt5670.o
 obj-$(CONFIG_SND_SOC_RT5677)   += snd-soc-rt5677.o
+obj-$(CONFIG_SND_SOC_RT5677_SPI)       += snd-soc-rt5677-spi.o
 obj-$(CONFIG_SND_SOC_SGTL5000)  += snd-soc-sgtl5000.o
 obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o
 obj-$(CONFIG_SND_SOC_SIGMADSP_I2C)     += snd-soc-sigmadsp-i2c.o
index fd43827..7dfbc99 100644 (file)
@@ -126,13 +126,13 @@ struct ab8500_codec_drvdata_dbg {
 /* Private data for AB8500 device-driver */
 struct ab8500_codec_drvdata {
        struct regmap *regmap;
+       struct mutex ctrl_lock;
 
        /* Sidetone */
        long *sid_fir_values;
        enum sid_state sid_status;
 
        /* ANC */
-       struct mutex anc_lock;
        long *anc_fir_values;
        long *anc_iir_values;
        enum anc_state anc_status;
@@ -1129,9 +1129,9 @@ static int sid_status_control_get(struct snd_kcontrol *kcontrol,
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&drvdata->ctrl_lock);
        ucontrol->value.integer.value[0] = drvdata->sid_status;
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&drvdata->ctrl_lock);
 
        return 0;
 }
@@ -1154,7 +1154,7 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol,
                return -EIO;
        }
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&drvdata->ctrl_lock);
 
        sidconf = snd_soc_read(codec, AB8500_SIDFIRCONF);
        if (((sidconf & BIT(AB8500_SIDFIRCONF_FIRSIDBUSY)) != 0)) {
@@ -1185,7 +1185,7 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol,
        drvdata->sid_status = SID_FIR_CONFIGURED;
 
 out:
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&drvdata->ctrl_lock);
 
        dev_dbg(codec->dev, "%s: Exit\n", __func__);
 
@@ -1198,9 +1198,9 @@ static int anc_status_control_get(struct snd_kcontrol *kcontrol,
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&drvdata->ctrl_lock);
        ucontrol->value.integer.value[0] = drvdata->anc_status;
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&drvdata->ctrl_lock);
 
        return 0;
 }
@@ -1217,7 +1217,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
 
        dev_dbg(dev, "%s: Enter.\n", __func__);
 
-       mutex_lock(&drvdata->anc_lock);
+       mutex_lock(&drvdata->ctrl_lock);
 
        req = ucontrol->value.integer.value[0];
        if (req >= ARRAY_SIZE(enum_anc_state)) {
@@ -1244,9 +1244,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
        }
        snd_soc_dapm_sync(&codec->dapm);
 
-       mutex_lock(&codec->mutex);
        anc_configure(codec, apply_fir, apply_iir);
-       mutex_unlock(&codec->mutex);
 
        if (apply_fir) {
                if (drvdata->anc_status == ANC_IIR_CONFIGURED)
@@ -1265,7 +1263,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
        snd_soc_dapm_sync(&codec->dapm);
 
 cleanup:
-       mutex_unlock(&drvdata->anc_lock);
+       mutex_unlock(&drvdata->ctrl_lock);
 
        if (status < 0)
                dev_err(dev, "%s: Unable to configure ANC! (status = %d)\n",
@@ -1294,14 +1292,15 @@ static int filter_control_get(struct snd_kcontrol *kcontrol,
                        struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct ab8500_codec_drvdata *drvdata = snd_soc_codec_get_drvdata(codec);
        struct filter_control *fc =
                        (struct filter_control *)kcontrol->private_value;
        unsigned int i;
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&drvdata->ctrl_lock);
        for (i = 0; i < fc->count; i++)
                ucontrol->value.integer.value[i] = fc->value[i];
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&drvdata->ctrl_lock);
 
        return 0;
 }
@@ -1310,14 +1309,15 @@ static int filter_control_put(struct snd_kcontrol *kcontrol,
                struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct ab8500_codec_drvdata *drvdata = snd_soc_codec_get_drvdata(codec);
        struct filter_control *fc =
                        (struct filter_control *)kcontrol->private_value;
        unsigned int i;
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&drvdata->ctrl_lock);
        for (i = 0; i < fc->count; i++)
                fc->value[i] = ucontrol->value.integer.value[i];
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&drvdata->ctrl_lock);
 
        return 0;
 }
@@ -2545,7 +2545,7 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec)
 
        (void)snd_soc_dapm_disable_pin(&codec->dapm, "ANC Configure Input");
 
-       mutex_init(&drvdata->anc_lock);
+       mutex_init(&drvdata->ctrl_lock);
 
        return status;
 }
index bd9b183..c6e5a31 100644 (file)
@@ -37,10 +37,11 @@ static int ac97_prepare(struct snd_pcm_substream *substream,
                        struct snd_soc_dai *dai)
 {
        struct snd_soc_codec *codec = dai->codec;
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
 
        int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
                  AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
-       return snd_ac97_set_rate(codec->ac97, reg, substream->runtime->rate);
+       return snd_ac97_set_rate(ac97, reg, substream->runtime->rate);
 }
 
 #define STD_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
@@ -53,7 +54,6 @@ static const struct snd_soc_dai_ops ac97_dai_ops = {
 
 static struct snd_soc_dai_driver ac97_dai = {
        .name = "ac97-hifi",
-       .ac97_control = 1,
        .playback = {
                .stream_name = "AC97 Playback",
                .channels_min = 1,
@@ -71,6 +71,7 @@ static struct snd_soc_dai_driver ac97_dai = {
 
 static int ac97_soc_probe(struct snd_soc_codec *codec)
 {
+       struct snd_ac97 *ac97;
        struct snd_ac97_bus *ac97_bus;
        struct snd_ac97_template ac97_template;
        int ret;
@@ -82,24 +83,31 @@ static int ac97_soc_probe(struct snd_soc_codec *codec)
                return ret;
 
        memset(&ac97_template, 0, sizeof(struct snd_ac97_template));
-       ret = snd_ac97_mixer(ac97_bus, &ac97_template, &codec->ac97);
+       ret = snd_ac97_mixer(ac97_bus, &ac97_template, &ac97);
        if (ret < 0)
                return ret;
 
+       snd_soc_codec_set_drvdata(codec, ac97);
+
        return 0;
 }
 
 #ifdef CONFIG_PM
 static int ac97_soc_suspend(struct snd_soc_codec *codec)
 {
-       snd_ac97_suspend(codec->ac97);
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+       snd_ac97_suspend(ac97);
 
        return 0;
 }
 
 static int ac97_soc_resume(struct snd_soc_codec *codec)
 {
-       snd_ac97_resume(codec->ac97);
+
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+       snd_ac97_resume(ac97);
 
        return 0;
 }
index 6844d0b..387530b 100644 (file)
@@ -72,11 +72,13 @@ static const struct snd_kcontrol_new ad193x_snd_controls[] = {
 };
 
 static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = {
-       SND_SOC_DAPM_DAC("DAC", "Playback", AD193X_DAC_CTRL0, 0, 1),
+       SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_PGA("DAC Output", AD193X_DAC_CTRL0, 0, 1, NULL, 0),
        SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
        SND_SOC_DAPM_SUPPLY("PLL_PWR", AD193X_PLL_CLK_CTRL0, 0, 1, NULL, 0),
        SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0),
        SND_SOC_DAPM_SUPPLY("SYSCLK", AD193X_PLL_CLK_CTRL0, 7, 0, NULL, 0),
+       SND_SOC_DAPM_VMID("VMID"),
        SND_SOC_DAPM_OUTPUT("DAC1OUT"),
        SND_SOC_DAPM_OUTPUT("DAC2OUT"),
        SND_SOC_DAPM_OUTPUT("DAC3OUT"),
@@ -87,13 +89,15 @@ static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = {
 
 static const struct snd_soc_dapm_route audio_paths[] = {
        { "DAC", NULL, "SYSCLK" },
+       { "DAC Output", NULL, "DAC" },
+       { "DAC Output", NULL, "VMID" },
        { "ADC", NULL, "SYSCLK" },
        { "DAC", NULL, "ADC_PWR" },
        { "ADC", NULL, "ADC_PWR" },
-       { "DAC1OUT", NULL, "DAC" },
-       { "DAC2OUT", NULL, "DAC" },
-       { "DAC3OUT", NULL, "DAC" },
-       { "DAC4OUT", NULL, "DAC" },
+       { "DAC1OUT", NULL, "DAC Output" },
+       { "DAC2OUT", NULL, "DAC Output" },
+       { "DAC3OUT", NULL, "DAC Output" },
+       { "DAC4OUT", NULL, "DAC Output" },
        { "ADC", NULL, "ADC1IN" },
        { "ADC", NULL, "ADC2IN" },
        { "SYSCLK", NULL, "PLL_PWR" },
index 304d300..2860eef 100644 (file)
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/device.h>
+#include <linux/regmap.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/ac97_codec.h>
 #include <sound/initval.h>
 #include <sound/soc.h>
 
-#include "ad1980.h"
+static const struct reg_default ad1980_reg_defaults[] = {
+       { 0x02, 0x8000 },
+       { 0x04, 0x8000 },
+       { 0x06, 0x8000 },
+       { 0x0c, 0x8008 },
+       { 0x0e, 0x8008 },
+       { 0x10, 0x8808 },
+       { 0x12, 0x8808 },
+       { 0x16, 0x8808 },
+       { 0x18, 0x8808 },
+       { 0x1a, 0x0000 },
+       { 0x1c, 0x8000 },
+       { 0x20, 0x0000 },
+       { 0x28, 0x03c7 },
+       { 0x2c, 0xbb80 },
+       { 0x2e, 0xbb80 },
+       { 0x30, 0xbb80 },
+       { 0x32, 0xbb80 },
+       { 0x36, 0x8080 },
+       { 0x38, 0x8080 },
+       { 0x3a, 0x2000 },
+       { 0x60, 0x0000 },
+       { 0x62, 0x0000 },
+       { 0x72, 0x0000 },
+       { 0x74, 0x1001 },
+       { 0x76, 0x0000 },
+};
 
-/*
- * AD1980 register cache
- */
-static const u16 ad1980_reg[] = {
-       0x0090, 0x8000, 0x8000, 0x8000, /* 0 - 6  */
-       0x0000, 0x0000, 0x8008, 0x8008, /* 8 - e  */
-       0x8808, 0x8808, 0x0000, 0x8808, /* 10 - 16 */
-       0x8808, 0x0000, 0x8000, 0x0000, /* 18 - 1e */
-       0x0000, 0x0000, 0x0000, 0x0000, /* 20 - 26 */
-       0x03c7, 0x0000, 0xbb80, 0xbb80, /* 28 - 2e */
-       0xbb80, 0xbb80, 0x0000, 0x8080, /* 30 - 36 */
-       0x8080, 0x2000, 0x0000, 0x0000, /* 38 - 3e */
-       0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
-       0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
-       0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
-       0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
-       0x8080, 0x0000, 0x0000, 0x0000, /* 60 - 66 */
-       0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
-       0x0000, 0x0000, 0x1001, 0x0000, /* 70 - 76 */
-       0x0000, 0x0000, 0x4144, 0x5370  /* 78 - 7e */
+static bool ad1980_readable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case AC97_RESET ... AC97_MASTER_MONO:
+       case AC97_PHONE ... AC97_CD:
+       case AC97_AUX ... AC97_GENERAL_PURPOSE:
+       case AC97_POWERDOWN ... AC97_PCM_LR_ADC_RATE:
+       case AC97_SPDIF:
+       case AC97_CODEC_CLASS_REV:
+       case AC97_PCI_SVID:
+       case AC97_AD_CODEC_CFG:
+       case AC97_AD_JACK_SPDIF:
+       case AC97_AD_SERIAL_CFG:
+       case AC97_VENDOR_ID1:
+       case AC97_VENDOR_ID2:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool ad1980_writeable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case AC97_VENDOR_ID1:
+       case AC97_VENDOR_ID2:
+               return false;
+       default:
+               return ad1980_readable_reg(dev, reg);
+       }
+}
+
+static const struct regmap_config ad1980_regmap_config = {
+       .reg_bits = 16,
+       .reg_stride = 2,
+       .val_bits = 16,
+       .max_register = 0x7e,
+       .cache_type = REGCACHE_RBTREE,
+
+       .volatile_reg = regmap_ac97_default_volatile,
+       .readable_reg = ad1980_readable_reg,
+       .writeable_reg = ad1980_writeable_reg,
+
+       .reg_defaults = ad1980_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(ad1980_reg_defaults),
 };
 
 static const char *ad1980_rec_sel[] = {"Mic", "CD", "NC", "AUX", "Line",
@@ -134,45 +186,8 @@ static const struct snd_soc_dapm_route ad1980_dapm_routes[] = {
        { "HP_OUT_R", NULL, "Playback" },
 };
 
-static unsigned int ac97_read(struct snd_soc_codec *codec,
-       unsigned int reg)
-{
-       u16 *cache = codec->reg_cache;
-
-       switch (reg) {
-       case AC97_RESET:
-       case AC97_INT_PAGING:
-       case AC97_POWERDOWN:
-       case AC97_EXTENDED_STATUS:
-       case AC97_VENDOR_ID1:
-       case AC97_VENDOR_ID2:
-               return soc_ac97_ops->read(codec->ac97, reg);
-       default:
-               reg = reg >> 1;
-
-               if (reg >= ARRAY_SIZE(ad1980_reg))
-                       return -EINVAL;
-
-               return cache[reg];
-       }
-}
-
-static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
-       unsigned int val)
-{
-       u16 *cache = codec->reg_cache;
-
-       soc_ac97_ops->write(codec->ac97, reg, val);
-       reg = reg >> 1;
-       if (reg < ARRAY_SIZE(ad1980_reg))
-               cache[reg] = val;
-
-       return 0;
-}
-
 static struct snd_soc_dai_driver ad1980_dai = {
        .name = "ad1980-hifi",
-       .ac97_control = 1,
        .playback = {
                .stream_name = "Playback",
                .channels_min = 2,
@@ -189,108 +204,115 @@ static struct snd_soc_dai_driver ad1980_dai = {
 
 static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
 {
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
        unsigned int retry_cnt = 0;
 
        do {
                if (try_warm && soc_ac97_ops->warm_reset) {
-                       soc_ac97_ops->warm_reset(codec->ac97);
-                       if (ac97_read(codec, AC97_RESET) == 0x0090)
+                       soc_ac97_ops->warm_reset(ac97);
+                       if (snd_soc_read(codec, AC97_RESET) == 0x0090)
                                return 1;
                }
 
-               soc_ac97_ops->reset(codec->ac97);
+               soc_ac97_ops->reset(ac97);
                /*
                 * Set bit 16slot in register 74h, then every slot will has only
                 * 16 bits. This command is sent out in 20bit mode, in which
                 * case the first nibble of data is eaten by the addr. (Tag is
                 * always 16 bit)
                 */
-               ac97_write(codec, AC97_AD_SERIAL_CFG, 0x9900);
+               snd_soc_write(codec, AC97_AD_SERIAL_CFG, 0x9900);
 
-               if (ac97_read(codec, AC97_RESET)  == 0x0090)
+               if (snd_soc_read(codec, AC97_RESET)  == 0x0090)
                        return 0;
        } while (retry_cnt++ < 10);
 
-       printk(KERN_ERR "AD1980 AC97 reset failed\n");
+       dev_err(codec->dev, "Failed to reset: AC97 link error\n");
+
        return -EIO;
 }
 
 static int ad1980_soc_probe(struct snd_soc_codec *codec)
 {
+       struct snd_ac97 *ac97;
+       struct regmap *regmap;
        int ret;
        u16 vendor_id2;
        u16 ext_status;
 
-       printk(KERN_INFO "AD1980 SoC Audio Codec\n");
-
-       ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
-       if (ret < 0) {
-               printk(KERN_ERR "ad1980: failed to register AC97 codec\n");
+       ac97 = snd_soc_new_ac97_codec(codec);
+       if (IS_ERR(ac97)) {
+               ret = PTR_ERR(ac97);
+               dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
                return ret;
        }
 
+       regmap = regmap_init_ac97(ac97, &ad1980_regmap_config);
+       if (IS_ERR(regmap)) {
+               ret = PTR_ERR(regmap);
+               goto err_free_ac97;
+       }
+
+       snd_soc_codec_init_regmap(codec, regmap);
+       snd_soc_codec_set_drvdata(codec, ac97);
+
        ret = ad1980_reset(codec, 0);
-       if (ret < 0) {
-               printk(KERN_ERR "Failed to reset AD1980: AC97 link error\n");
+       if (ret < 0)
                goto reset_err;
-       }
 
        /* Read out vendor ID to make sure it is ad1980 */
-       if (ac97_read(codec, AC97_VENDOR_ID1) != 0x4144) {
+       if (snd_soc_read(codec, AC97_VENDOR_ID1) != 0x4144) {
                ret = -ENODEV;
                goto reset_err;
        }
 
-       vendor_id2 = ac97_read(codec, AC97_VENDOR_ID2);
+       vendor_id2 = snd_soc_read(codec, AC97_VENDOR_ID2);
 
        if (vendor_id2 != 0x5370) {
                if (vendor_id2 != 0x5374) {
                        ret = -ENODEV;
                        goto reset_err;
                } else {
-                       printk(KERN_WARNING "ad1980: "
-                               "Found AD1981 - only 2/2 IN/OUT Channels "
-                               "supported\n");
+                       dev_warn(codec->dev,
+                               "Found AD1981 - only 2/2 IN/OUT Channels supported\n");
                }
        }
 
        /* unmute captures and playbacks volume */
-       ac97_write(codec, AC97_MASTER, 0x0000);
-       ac97_write(codec, AC97_PCM, 0x0000);
-       ac97_write(codec, AC97_REC_GAIN, 0x0000);
-       ac97_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
-       ac97_write(codec, AC97_SURROUND_MASTER, 0x0000);
+       snd_soc_write(codec, AC97_MASTER, 0x0000);
+       snd_soc_write(codec, AC97_PCM, 0x0000);
+       snd_soc_write(codec, AC97_REC_GAIN, 0x0000);
+       snd_soc_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
+       snd_soc_write(codec, AC97_SURROUND_MASTER, 0x0000);
 
        /*power on LFE/CENTER/Surround DACs*/
-       ext_status = ac97_read(codec, AC97_EXTENDED_STATUS);
-       ac97_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
-
-       snd_soc_add_codec_controls(codec, ad1980_snd_ac97_controls,
-                               ARRAY_SIZE(ad1980_snd_ac97_controls));
+       ext_status = snd_soc_read(codec, AC97_EXTENDED_STATUS);
+       snd_soc_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
 
        return 0;
 
 reset_err:
-       snd_soc_free_ac97_codec(codec);
+       snd_soc_codec_exit_regmap(codec);
+err_free_ac97:
+       snd_soc_free_ac97_codec(ac97);
        return ret;
 }
 
 static int ad1980_soc_remove(struct snd_soc_codec *codec)
 {
-       snd_soc_free_ac97_codec(codec);
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+       snd_soc_codec_exit_regmap(codec);
+       snd_soc_free_ac97_codec(ac97);
        return 0;
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_ad1980 = {
        .probe =        ad1980_soc_probe,
        .remove =       ad1980_soc_remove,
-       .reg_cache_size = ARRAY_SIZE(ad1980_reg),
-       .reg_word_size = sizeof(u16),
-       .reg_cache_default = ad1980_reg,
-       .reg_cache_step = 2,
-       .write = ac97_write,
-       .read = ac97_read,
 
+       .controls = ad1980_snd_ac97_controls,
+       .num_controls = ARRAY_SIZE(ad1980_snd_ac97_controls),
        .dapm_widgets = ad1980_dapm_widgets,
        .num_dapm_widgets = ARRAY_SIZE(ad1980_dapm_widgets),
        .dapm_routes = ad1980_dapm_routes,
diff --git a/sound/soc/codecs/ad1980.h b/sound/soc/codecs/ad1980.h
deleted file mode 100644 (file)
index eb0af44..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * ad1980.h  --  ad1980 Soc Audio driver
- *
- * WARNING:
- *
- * Because Analog Devices Inc. discontinued the ad1980 sound chip since
- * Sep. 2009, this ad1980 driver is not maintained, tested and supported
- * by ADI now.
- */
-
-#ifndef _AD1980_H
-#define _AD1980_H
-/* Bit definition of Power-Down Control/Status Register */
-#define ADC            0x0001
-#define DAC            0x0002
-#define ANL            0x0004
-#define REF            0x0008
-#define PR0            0x0100
-#define PR1            0x0200
-#define PR2            0x0400
-#define PR3            0x0800
-#define PR4            0x1000
-#define PR5            0x2000
-#define PR6            0x4000
-
-#endif
index 7c784ad..783dcb5 100644 (file)
@@ -551,7 +551,7 @@ static const struct snd_kcontrol_new adau1373_drc_controls[] = {
 static int adau1373_pll_event(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
 {
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
        unsigned int pll_id = w->name[3] - '1';
        unsigned int val;
@@ -823,7 +823,7 @@ static const struct snd_soc_dapm_widget adau1373_dapm_widgets[] = {
 static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
        struct snd_soc_dapm_widget *sink)
 {
-       struct snd_soc_codec *codec = source->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
        struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
        unsigned int dai;
        const char *clk;
@@ -844,7 +844,7 @@ static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
 static int adau1373_check_src(struct snd_soc_dapm_widget *source,
        struct snd_soc_dapm_widget *sink)
 {
-       struct snd_soc_codec *codec = source->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
        struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
        unsigned int dai;
 
index 91f6028..16093dc 100644 (file)
@@ -255,7 +255,8 @@ static const struct snd_kcontrol_new adau1761_input_mux_control =
 static int adau1761_dejitter_fixup(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
 {
-       struct adau *adau = snd_soc_codec_get_drvdata(w->codec);
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct adau *adau = snd_soc_codec_get_drvdata(codec);
 
        /* After any power changes have been made the dejitter circuit
         * has to be reinitialized. */
index e9fc00f..aa6a37c 100644 (file)
@@ -174,7 +174,7 @@ static const struct snd_kcontrol_new adau1781_mono_mixer_controls[] = {
 static int adau1781_dejitter_fixup(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
 {
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct adau *adau = snd_soc_codec_get_drvdata(codec);
 
        /* After any power changes have been made the dejitter circuit
index 3e16c1c..427ad77 100644 (file)
@@ -61,7 +61,8 @@ static const struct snd_kcontrol_new adau17x1_controls[] = {
 static int adau17x1_pll_event(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
 {
-       struct adau *adau = snd_soc_codec_get_drvdata(w->codec);
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct adau *adau = snd_soc_codec_get_drvdata(codec);
        int ret;
 
        if (SND_SOC_DAPM_EVENT_ON(event)) {
index ce3cdca..b67480f 100644 (file)
@@ -212,7 +212,7 @@ static const struct snd_soc_dapm_widget adav80x_dapm_widgets[] = {
 static int adav80x_dapm_sysclk_check(struct snd_soc_dapm_widget *source,
                         struct snd_soc_dapm_widget *sink)
 {
-       struct snd_soc_codec *codec = source->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
        struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
        const char *clk;
 
@@ -236,7 +236,7 @@ static int adav80x_dapm_sysclk_check(struct snd_soc_dapm_widget *source,
 static int adav80x_dapm_pll_check(struct snd_soc_dapm_widget *source,
                         struct snd_soc_dapm_widget *sink)
 {
-       struct snd_soc_codec *codec = source->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
        struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
 
        return adav80x->pll_src == ADAV80X_PLL_SRC_XTAL;
index 30e2978..9130d91 100644 (file)
@@ -373,33 +373,9 @@ static struct snd_soc_dai_driver ak4535_dai = {
        .ops = &ak4535_dai_ops,
 };
 
-static int ak4535_suspend(struct snd_soc_codec *codec)
-{
-       ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static int ak4535_resume(struct snd_soc_codec *codec)
 {
        snd_soc_cache_sync(codec);
-       ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-
-static int ak4535_probe(struct snd_soc_codec *codec)
-{
-       /* power on device */
-       ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       snd_soc_add_codec_controls(codec, ak4535_snd_controls,
-                               ARRAY_SIZE(ak4535_snd_controls));
-       return 0;
-}
-
-/* power down chip */
-static int ak4535_remove(struct snd_soc_codec *codec)
-{
-       ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF);
        return 0;
 }
 
@@ -416,11 +392,12 @@ static const struct regmap_config ak4535_regmap = {
 };
 
 static struct snd_soc_codec_driver soc_codec_dev_ak4535 = {
-       .probe =        ak4535_probe,
-       .remove =       ak4535_remove,
-       .suspend =      ak4535_suspend,
        .resume =       ak4535_resume,
        .set_bias_level = ak4535_set_bias_level,
+       .suspend_bias_off = true,
+
+       .controls = ak4535_snd_controls,
+       .num_controls = ARRAY_SIZE(ak4535_snd_controls),
        .dapm_widgets = ak4535_dapm_widgets,
        .num_dapm_widgets = ARRAY_SIZE(ak4535_dapm_widgets),
        .dapm_routes = ak4535_audio_map,
index 7afe8f4..70861c7 100644 (file)
@@ -505,39 +505,7 @@ static struct snd_soc_dai_driver ak4641_dai[] = {
 },
 };
 
-static int ak4641_suspend(struct snd_soc_codec *codec)
-{
-       ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int ak4641_resume(struct snd_soc_codec *codec)
-{
-       ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-
-static int ak4641_probe(struct snd_soc_codec *codec)
-{
-       /* power on device */
-       ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
-static int ak4641_remove(struct snd_soc_codec *codec)
-{
-       ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
-
 static struct snd_soc_codec_driver soc_codec_dev_ak4641 = {
-       .probe                  = ak4641_probe,
-       .remove                 = ak4641_remove,
-       .suspend                = ak4641_suspend,
-       .resume                 = ak4641_resume,
        .controls               = ak4641_snd_controls,
        .num_controls           = ARRAY_SIZE(ak4641_snd_controls),
        .dapm_widgets           = ak4641_dapm_widgets,
@@ -545,6 +513,7 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4641 = {
        .dapm_routes            = ak4641_audio_map,
        .num_dapm_routes        = ARRAY_SIZE(ak4641_audio_map),
        .set_bias_level         = ak4641_set_bias_level,
+       .suspend_bias_off       = true,
 };
 
 static const struct regmap_config ak4641_regmap = {
index 0417125..dde8b49 100644 (file)
@@ -491,23 +491,7 @@ static int ak4642_resume(struct snd_soc_codec *codec)
        return 0;
 }
 
-
-static int ak4642_probe(struct snd_soc_codec *codec)
-{
-       ak4642_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
-static int ak4642_remove(struct snd_soc_codec *codec)
-{
-       ak4642_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
-       .probe                  = ak4642_probe,
-       .remove                 = ak4642_remove,
        .resume                 = ak4642_resume,
        .set_bias_level         = ak4642_set_bias_level,
        .controls               = ak4642_snd_controls,
index 998fa0c..686cacb 100644 (file)
@@ -611,20 +611,7 @@ static struct snd_soc_dai_driver ak4671_dai = {
        .ops = &ak4671_dai_ops,
 };
 
-static int ak4671_probe(struct snd_soc_codec *codec)
-{
-       return ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-}
-
-static int ak4671_remove(struct snd_soc_codec *codec)
-{
-       ak4671_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_ak4671 = {
-       .probe = ak4671_probe,
-       .remove = ak4671_remove,
        .set_bias_level = ak4671_set_bias_level,
        .controls = ak4671_snd_controls,
        .num_controls = ARRAY_SIZE(ak4671_snd_controls),
index 9d0755a..bdf8c5a 100644 (file)
@@ -866,7 +866,6 @@ static int alc5623_suspend(struct snd_soc_codec *codec)
 {
        struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
 
-       alc5623_set_bias_level(codec, SND_SOC_BIAS_OFF);
        regcache_cache_only(alc5623->regmap, true);
 
        return 0;
@@ -887,15 +886,6 @@ static int alc5623_resume(struct snd_soc_codec *codec)
                return ret;
        }
 
-       alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       /* charge alc5623 caps */
-       if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) {
-               alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-               codec->dapm.bias_level = SND_SOC_BIAS_ON;
-               alc5623_set_bias_level(codec, codec->dapm.bias_level);
-       }
-
        return 0;
 }
 
@@ -906,9 +896,6 @@ static int alc5623_probe(struct snd_soc_codec *codec)
 
        alc5623_reset(codec);
 
-       /* power on device */
-       alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        if (alc5623->add_ctrl) {
                snd_soc_write(codec, ALC5623_ADD_CTRL_REG,
                                alc5623->add_ctrl);
@@ -964,19 +951,12 @@ static int alc5623_probe(struct snd_soc_codec *codec)
        return 0;
 }
 
-/* power down chip */
-static int alc5623_remove(struct snd_soc_codec *codec)
-{
-       alc5623_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_device_alc5623 = {
        .probe = alc5623_probe,
-       .remove = alc5623_remove,
        .suspend = alc5623_suspend,
        .resume = alc5623_resume,
        .set_bias_level = alc5623_set_bias_level,
+       .suspend_bias_off = true,
 };
 
 static const struct regmap_config alc5623_regmap = {
index 85942ca..d1fdbc2 100644 (file)
@@ -1038,23 +1038,15 @@ static struct snd_soc_dai_driver alc5632_dai = {
 };
 
 #ifdef CONFIG_PM
-static int alc5632_suspend(struct snd_soc_codec *codec)
-{
-       alc5632_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static int alc5632_resume(struct snd_soc_codec *codec)
 {
        struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec);
 
        regcache_sync(alc5632->regmap);
 
-       alc5632_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
        return 0;
 }
 #else
-#define        alc5632_suspend NULL
 #define        alc5632_resume  NULL
 #endif
 
@@ -1062,9 +1054,6 @@ static int alc5632_probe(struct snd_soc_codec *codec)
 {
        struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec);
 
-       /* power on device  */
-       alc5632_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        switch (alc5632->id) {
        case 0x5c:
                snd_soc_add_codec_controls(codec, alc5632_vol_snd_controls,
@@ -1077,19 +1066,12 @@ static int alc5632_probe(struct snd_soc_codec *codec)
        return 0;
 }
 
-/* power down chip */
-static int alc5632_remove(struct snd_soc_codec *codec)
-{
-       alc5632_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_device_alc5632 = {
        .probe = alc5632_probe,
-       .remove = alc5632_remove,
-       .suspend = alc5632_suspend,
        .resume = alc5632_resume,
        .set_bias_level = alc5632_set_bias_level,
+       .suspend_bias_off = true,
+
        .controls = alc5632_snd_controls,
        .num_controls = ARRAY_SIZE(alc5632_snd_controls),
        .dapm_widgets = alc5632_dapm_widgets,
index 0c05e7a..9550d74 100644 (file)
 #define ARIZONA_FLL_MIN_OUTDIV 2
 #define ARIZONA_FLL_MAX_OUTDIV 7
 
+#define ARIZONA_FMT_DSP_MODE_A          0
+#define ARIZONA_FMT_DSP_MODE_B          1
+#define ARIZONA_FMT_I2S_MODE            2
+#define ARIZONA_FMT_LEFT_JUSTIFIED_MODE 3
+
 #define arizona_fll_err(_fll, fmt, ...) \
        dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
 #define arizona_fll_warn(_fll, fmt, ...) \
@@ -648,7 +653,7 @@ SOC_ENUM_SINGLE_DECL(arizona_in_hpf_cut_enum,
 EXPORT_SYMBOL_GPL(arizona_in_hpf_cut_enum);
 
 static const char * const arizona_in_dmic_osr_text[] = {
-       "1.536MHz", "3.072MHz", "6.144MHz",
+       "1.536MHz", "3.072MHz", "6.144MHz", "768kHz",
 };
 
 const struct soc_enum arizona_in_dmic_osr[] = {
@@ -946,10 +951,26 @@ static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
        case SND_SOC_DAIFMT_DSP_A:
-               mode = 0;
+               mode = ARIZONA_FMT_DSP_MODE_A;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               if ((fmt & SND_SOC_DAIFMT_MASTER_MASK)
+                               != SND_SOC_DAIFMT_CBM_CFM) {
+                       arizona_aif_err(dai, "DSP_B not valid in slave mode\n");
+                       return -EINVAL;
+               }
+               mode = ARIZONA_FMT_DSP_MODE_B;
                break;
        case SND_SOC_DAIFMT_I2S:
-               mode = 2;
+               mode = ARIZONA_FMT_I2S_MODE;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               if ((fmt & SND_SOC_DAIFMT_MASTER_MASK)
+                               != SND_SOC_DAIFMT_CBM_CFM) {
+                       arizona_aif_err(dai, "LEFT_J not valid in slave mode\n");
+                       return -EINVAL;
+               }
+               mode = ARIZONA_FMT_LEFT_JUSTIFIED_MODE;
                break;
        default:
                arizona_aif_err(dai, "Unsupported DAI format %d\n",
@@ -1164,13 +1185,13 @@ static void arizona_wm5102_set_dac_comp(struct snd_soc_codec *codec,
                { 0x80, 0x0 },
        };
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&arizona->dac_comp_lock);
 
        dac_comp[1].def = arizona->dac_comp_coeff;
        if (rate >= 176400)
                dac_comp[2].def = arizona->dac_comp_enabled;
 
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&arizona->dac_comp_lock);
 
        regmap_multi_reg_write(arizona->regmap,
                               dac_comp,
@@ -1298,7 +1319,8 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
 
        /* Force multiple of 2 channels for I2S mode */
        val = snd_soc_read(codec, base + ARIZONA_AIF_FORMAT);
-       if ((channels & 1) && (val & ARIZONA_AIF1_FMT_MASK)) {
+       val &= ARIZONA_AIF1_FMT_MASK;
+       if ((channels & 1) && (val == ARIZONA_FMT_I2S_MODE)) {
                arizona_aif_dbg(dai, "Forcing stereo mode\n");
                bclk_target /= channels;
                bclk_target *= channels + 1;
index 537327c..8d638e8 100644 (file)
@@ -62,14 +62,10 @@ static int cq93vc_mute(struct snd_soc_dai *dai, int mute)
 static int cq93vc_set_dai_sysclk(struct snd_soc_dai *codec_dai,
                                 int clk_id, unsigned int freq, int dir)
 {
-       struct snd_soc_codec *codec = codec_dai->codec;
-       struct davinci_vc *davinci_vc = codec->dev->platform_data;
-
        switch (freq) {
        case 22579200:
        case 27000000:
        case 33868800:
-               davinci_vc->cq93vc.sysclk = freq;
                return 0;
        }
 
@@ -126,32 +122,6 @@ static struct snd_soc_dai_driver cq93vc_dai = {
        .ops = &cq93vc_dai_ops,
 };
 
-static int cq93vc_resume(struct snd_soc_codec *codec)
-{
-       cq93vc_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
-static int cq93vc_probe(struct snd_soc_codec *codec)
-{
-       struct davinci_vc *davinci_vc = codec->dev->platform_data;
-
-       davinci_vc->cq93vc.codec = codec;
-
-       /* Off, with power on */
-       cq93vc_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
-static int cq93vc_remove(struct snd_soc_codec *codec)
-{
-       cq93vc_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
 static struct regmap *cq93vc_get_regmap(struct device *dev)
 {
        struct davinci_vc *davinci_vc = dev->platform_data;
@@ -161,9 +131,6 @@ static struct regmap *cq93vc_get_regmap(struct device *dev)
 
 static struct snd_soc_codec_driver soc_codec_dev_cq93vc = {
        .set_bias_level = cq93vc_set_bias_level,
-       .probe = cq93vc_probe,
-       .remove = cq93vc_remove,
-       .resume = cq93vc_resume,
        .get_regmap = cq93vc_get_regmap,
        .controls = cq93vc_snd_controls,
        .num_controls = ARRAY_SIZE(cq93vc_snd_controls),
index 4fdd47d..ce60868 100644 (file)
@@ -32,7 +32,6 @@
 #include "cs4265.h"
 
 struct cs4265_private {
-       struct device *dev;
        struct regmap *regmap;
        struct gpio_desc *reset_gpio;
        u8 format;
@@ -598,7 +597,6 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client,
                               GFP_KERNEL);
        if (cs4265 == NULL)
                return -ENOMEM;
-       cs4265->dev = &i2c_client->dev;
 
        cs4265->regmap = devm_regmap_init_i2c(i2c_client, &cs4265_regmap);
        if (IS_ERR(cs4265->regmap)) {
diff --git a/sound/soc/codecs/cs4271-i2c.c b/sound/soc/codecs/cs4271-i2c.c
new file mode 100644 (file)
index 0000000..b264da0
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * CS4271 I2C audio driver
+ *
+ * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "cs4271.h"
+
+static int cs4271_i2c_probe(struct i2c_client *client,
+                            const struct i2c_device_id *id)
+{
+       struct regmap_config config;
+
+       config = cs4271_regmap_config;
+       config.reg_bits = 8;
+       config.val_bits = 8;
+
+       return cs4271_probe(&client->dev,
+                           devm_regmap_init_i2c(client, &config));
+}
+
+static int cs4271_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       return 0;
+}
+
+static const struct i2c_device_id cs4271_i2c_id[] = {
+       { "cs4271", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id);
+
+static struct i2c_driver cs4271_i2c_driver = {
+       .driver = {
+               .name = "cs4271",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(cs4271_dt_ids),
+       },
+       .probe = cs4271_i2c_probe,
+       .remove = cs4271_i2c_remove,
+       .id_table = cs4271_i2c_id,
+};
+module_i2c_driver(cs4271_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS4271 I2C Driver");
+MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs4271-spi.c b/sound/soc/codecs/cs4271-spi.c
new file mode 100644 (file)
index 0000000..acd49d8
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * CS4271 SPI audio driver
+ *
+ * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "cs4271.h"
+
+static int cs4271_spi_probe(struct spi_device *spi)
+{
+       struct regmap_config config;
+
+       config = cs4271_regmap_config;
+       config.reg_bits = 16;
+       config.val_bits = 8;
+       config.read_flag_mask = 0x21;
+       config.write_flag_mask = 0x20;
+
+       return cs4271_probe(&spi->dev, devm_regmap_init_spi(spi, &config));
+}
+
+static int cs4271_spi_remove(struct spi_device *spi)
+{
+       snd_soc_unregister_codec(&spi->dev);
+       return 0;
+}
+
+static struct spi_driver cs4271_spi_driver = {
+       .driver = {
+               .name   = "cs4271",
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(cs4271_dt_ids),
+       },
+       .probe          = cs4271_spi_probe,
+       .remove         = cs4271_spi_remove,
+};
+module_spi_driver(cs4271_spi_driver);
+
+MODULE_DESCRIPTION("ASoC CS4271 SPI Driver");
+MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
+MODULE_LICENSE("GPL");
index 93cec52..79a4efc 100644 (file)
@@ -23,8 +23,6 @@
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/gpio.h>
-#include <linux/i2c.h>
-#include <linux/spi/spi.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/of_gpio.h>
@@ -32,6 +30,7 @@
 #include <sound/soc.h>
 #include <sound/tlv.h>
 #include <sound/cs4271.h>
+#include "cs4271.h"
 
 #define CS4271_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
                            SNDRV_PCM_FMTBIT_S24_LE | \
@@ -527,14 +526,15 @@ static int cs4271_soc_resume(struct snd_soc_codec *codec)
 #endif /* CONFIG_PM */
 
 #ifdef CONFIG_OF
-static const struct of_device_id cs4271_dt_ids[] = {
+const struct of_device_id cs4271_dt_ids[] = {
        { .compatible = "cirrus,cs4271", },
        { }
 };
 MODULE_DEVICE_TABLE(of, cs4271_dt_ids);
+EXPORT_SYMBOL_GPL(cs4271_dt_ids);
 #endif
 
-static int cs4271_probe(struct snd_soc_codec *codec)
+static int cs4271_codec_probe(struct snd_soc_codec *codec)
 {
        struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
        struct cs4271_platform_data *cs4271plat = codec->dev->platform_data;
@@ -587,7 +587,7 @@ static int cs4271_probe(struct snd_soc_codec *codec)
        return 0;
 }
 
-static int cs4271_remove(struct snd_soc_codec *codec)
+static int cs4271_codec_remove(struct snd_soc_codec *codec)
 {
        struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
 
@@ -599,8 +599,8 @@ static int cs4271_remove(struct snd_soc_codec *codec)
 };
 
 static struct snd_soc_codec_driver soc_codec_dev_cs4271 = {
-       .probe                  = cs4271_probe,
-       .remove                 = cs4271_remove,
+       .probe                  = cs4271_codec_probe,
+       .remove                 = cs4271_codec_remove,
        .suspend                = cs4271_soc_suspend,
        .resume                 = cs4271_soc_resume,
 
@@ -642,14 +642,8 @@ static int cs4271_common_probe(struct device *dev,
        return 0;
 }
 
-#if defined(CONFIG_SPI_MASTER)
-
-static const struct regmap_config cs4271_spi_regmap = {
-       .reg_bits = 16,
-       .val_bits = 8,
+const struct regmap_config cs4271_regmap_config = {
        .max_register = CS4271_LASTREG,
-       .read_flag_mask = 0x21,
-       .write_flag_mask = 0x20,
 
        .reg_defaults = cs4271_reg_defaults,
        .num_reg_defaults = ARRAY_SIZE(cs4271_reg_defaults),
@@ -657,140 +651,27 @@ static const struct regmap_config cs4271_spi_regmap = {
 
        .volatile_reg = cs4271_volatile_reg,
 };
+EXPORT_SYMBOL_GPL(cs4271_regmap_config);
 
-static int cs4271_spi_probe(struct spi_device *spi)
+int cs4271_probe(struct device *dev, struct regmap *regmap)
 {
        struct cs4271_private *cs4271;
        int ret;
 
-       ret = cs4271_common_probe(&spi->dev, &cs4271);
-       if (ret < 0)
-               return ret;
-
-       spi_set_drvdata(spi, cs4271);
-       cs4271->regmap = devm_regmap_init_spi(spi, &cs4271_spi_regmap);
-       if (IS_ERR(cs4271->regmap))
-               return PTR_ERR(cs4271->regmap);
-
-       return snd_soc_register_codec(&spi->dev, &soc_codec_dev_cs4271,
-               &cs4271_dai, 1);
-}
-
-static int cs4271_spi_remove(struct spi_device *spi)
-{
-       snd_soc_unregister_codec(&spi->dev);
-       return 0;
-}
-
-static struct spi_driver cs4271_spi_driver = {
-       .driver = {
-               .name   = "cs4271",
-               .owner  = THIS_MODULE,
-               .of_match_table = of_match_ptr(cs4271_dt_ids),
-       },
-       .probe          = cs4271_spi_probe,
-       .remove         = cs4271_spi_remove,
-};
-#endif /* defined(CONFIG_SPI_MASTER) */
-
-#if IS_ENABLED(CONFIG_I2C)
-static const struct i2c_device_id cs4271_i2c_id[] = {
-       {"cs4271", 0},
-       {}
-};
-MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id);
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
 
-static const struct regmap_config cs4271_i2c_regmap = {
-       .reg_bits = 8,
-       .val_bits = 8,
-       .max_register = CS4271_LASTREG,
-
-       .reg_defaults = cs4271_reg_defaults,
-       .num_reg_defaults = ARRAY_SIZE(cs4271_reg_defaults),
-       .cache_type = REGCACHE_RBTREE,
-
-       .volatile_reg = cs4271_volatile_reg,
-};
-
-static int cs4271_i2c_probe(struct i2c_client *client,
-                           const struct i2c_device_id *id)
-{
-       struct cs4271_private *cs4271;
-       int ret;
-
-       ret = cs4271_common_probe(&client->dev, &cs4271);
+       ret = cs4271_common_probe(dev, &cs4271);
        if (ret < 0)
                return ret;
 
-       i2c_set_clientdata(client, cs4271);
-       cs4271->regmap = devm_regmap_init_i2c(client, &cs4271_i2c_regmap);
-       if (IS_ERR(cs4271->regmap))
-               return PTR_ERR(cs4271->regmap);
+       dev_set_drvdata(dev, cs4271);
+       cs4271->regmap = regmap;
 
-       return snd_soc_register_codec(&client->dev, &soc_codec_dev_cs4271,
-               &cs4271_dai, 1);
-}
-
-static int cs4271_i2c_remove(struct i2c_client *client)
-{
-       snd_soc_unregister_codec(&client->dev);
-       return 0;
-}
-
-static struct i2c_driver cs4271_i2c_driver = {
-       .driver = {
-               .name   = "cs4271",
-               .owner  = THIS_MODULE,
-               .of_match_table = of_match_ptr(cs4271_dt_ids),
-       },
-       .id_table       = cs4271_i2c_id,
-       .probe          = cs4271_i2c_probe,
-       .remove         = cs4271_i2c_remove,
-};
-#endif /* IS_ENABLED(CONFIG_I2C) */
-
-/*
- * We only register our serial bus driver here without
- * assignment to particular chip. So if any of the below
- * fails, there is some problem with I2C or SPI subsystem.
- * In most cases this module will be compiled with support
- * of only one serial bus.
- */
-static int __init cs4271_modinit(void)
-{
-       int ret;
-
-#if IS_ENABLED(CONFIG_I2C)
-       ret = i2c_add_driver(&cs4271_i2c_driver);
-       if (ret) {
-               pr_err("Failed to register CS4271 I2C driver: %d\n", ret);
-               return ret;
-       }
-#endif
-
-#if defined(CONFIG_SPI_MASTER)
-       ret = spi_register_driver(&cs4271_spi_driver);
-       if (ret) {
-               pr_err("Failed to register CS4271 SPI driver: %d\n", ret);
-               return ret;
-       }
-#endif
-
-       return 0;
-}
-module_init(cs4271_modinit);
-
-static void __exit cs4271_modexit(void)
-{
-#if defined(CONFIG_SPI_MASTER)
-       spi_unregister_driver(&cs4271_spi_driver);
-#endif
-
-#if IS_ENABLED(CONFIG_I2C)
-       i2c_del_driver(&cs4271_i2c_driver);
-#endif
+       return snd_soc_register_codec(dev, &soc_codec_dev_cs4271, &cs4271_dai,
+                                     1);
 }
-module_exit(cs4271_modexit);
+EXPORT_SYMBOL_GPL(cs4271_probe);
 
 MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
 MODULE_DESCRIPTION("Cirrus Logic CS4271 ALSA SoC Codec Driver");
diff --git a/sound/soc/codecs/cs4271.h b/sound/soc/codecs/cs4271.h
new file mode 100644 (file)
index 0000000..9adad8e
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef _CS4271_PRIV_H
+#define _CS4271_PRIV_H
+
+#include <linux/regmap.h>
+
+extern const struct of_device_id cs4271_dt_ids[];
+extern const struct regmap_config cs4271_regmap_config;
+
+int cs4271_probe(struct device *dev, struct regmap *regmap);
+
+#endif
index cee51ae..c40428f 100644 (file)
@@ -46,6 +46,7 @@ static struct i2c_driver cs42l51_i2c_driver = {
        .driver = {
                .name = "cs42l51",
                .owner = THIS_MODULE,
+               .of_match_table = cs42l51_of_match,
        },
        .probe = cs42l51_i2c_probe,
        .remove = cs42l51_i2c_remove,
index 09488d9..b395152 100644 (file)
@@ -153,15 +153,17 @@ static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
 static int cs42l51_pdn_event(struct snd_soc_dapm_widget *w,
                struct snd_kcontrol *kcontrol, int event)
 {
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
        switch (event) {
        case SND_SOC_DAPM_PRE_PMD:
-               snd_soc_update_bits(w->codec, CS42L51_POWER_CTL1,
+               snd_soc_update_bits(codec, CS42L51_POWER_CTL1,
                                    CS42L51_POWER_CTL1_PDN,
                                    CS42L51_POWER_CTL1_PDN);
                break;
        default:
        case SND_SOC_DAPM_POST_PMD:
-               snd_soc_update_bits(w->codec, CS42L51_POWER_CTL1,
+               snd_soc_update_bits(codec, CS42L51_POWER_CTL1,
                                    CS42L51_POWER_CTL1_PDN, 0);
                break;
        }
@@ -558,11 +560,13 @@ error:
 }
 EXPORT_SYMBOL_GPL(cs42l51_probe);
 
-static const struct of_device_id cs42l51_of_match[] = {
+const struct of_device_id cs42l51_of_match[] = {
        { .compatible = "cirrus,cs42l51", },
        { }
 };
 MODULE_DEVICE_TABLE(of, cs42l51_of_match);
+EXPORT_SYMBOL_GPL(cs42l51_of_match);
+
 MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
 MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver");
 MODULE_LICENSE("GPL");
index 8c55bf3..0ca8054 100644 (file)
@@ -22,6 +22,7 @@ struct device;
 
 extern const struct regmap_config cs42l51_regmap;
 int cs42l51_probe(struct device *dev, struct regmap *regmap);
+extern const struct of_device_id cs42l51_of_match[];
 
 #define CS42L51_CHIP_ID                        0x1B
 #define CS42L51_CHIP_REV_A             0x00
index 2f8b946..7c55537 100644 (file)
@@ -584,7 +584,7 @@ static const struct snd_kcontrol_new cs42l73_snd_controls[] = {
 static int cs42l73_spklo_spk_amp_event(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
 {
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
        switch (event) {
        case SND_SOC_DAPM_POST_PMD:
@@ -600,7 +600,7 @@ static int cs42l73_spklo_spk_amp_event(struct snd_soc_dapm_widget *w,
 static int cs42l73_ear_amp_event(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
 {
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
        switch (event) {
        case SND_SOC_DAPM_POST_PMD:
@@ -618,7 +618,7 @@ static int cs42l73_ear_amp_event(struct snd_soc_dapm_widget *w,
 static int cs42l73_hp_amp_event(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
 {
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
        switch (event) {
        case SND_SOC_DAPM_POST_PMD:
index aae410d..2d05b5d 100644 (file)
@@ -19,7 +19,7 @@
 #include "es8328.h"
 
 static const struct i2c_device_id es8328_id[] = {
-       { "everest,es8328", 0 },
+       { "es8328", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, es8328_id);
index 1087fd5..1391ad5 100644 (file)
@@ -47,6 +47,7 @@ static struct snd_soc_dai_driver hdmi_codec_dai = {
                        SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
                .formats = SNDRV_PCM_FMTBIT_S16_LE |
                        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+               .sig_bits = 24,
        },
        .capture = {
                .stream_name = "Capture",
@@ -75,6 +76,7 @@ static struct snd_soc_codec_driver hdmi_codec = {
        .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
        .dapm_routes = hdmi_routes,
        .num_dapm_routes = ARRAY_SIZE(hdmi_routes),
+       .ignore_pmdown_time = true,
 };
 
 static int hdmi_codec_probe(struct platform_device *pdev)
index c1ae576..c4dfde9 100644 (file)
@@ -1395,15 +1395,7 @@ static struct snd_soc_dai_driver lm49453_dai[] = {
        },
 };
 
-/* power down chip */
-static int lm49453_remove(struct snd_soc_codec *codec)
-{
-       lm49453_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_lm49453 = {
-       .remove = lm49453_remove,
        .set_bias_level = lm49453_set_bias_level,
        .controls = lm49453_snd_controls,
        .num_controls = ARRAY_SIZE(lm49453_snd_controls),
index 2cd3e54..805b3f8 100644 (file)
@@ -875,7 +875,7 @@ static const struct snd_kcontrol_new max98088_right_ADC_mixer_controls[] = {
 static int max98088_mic_event(struct snd_soc_dapm_widget *w,
                             struct snd_kcontrol *kcontrol, int event)
 {
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
 
        switch (event) {
@@ -905,7 +905,7 @@ static int max98088_mic_event(struct snd_soc_dapm_widget *w,
 static int max98088_line_pga(struct snd_soc_dapm_widget *w,
                             int event, int line, u8 channel)
 {
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
        u8 *state;
 
@@ -1887,25 +1887,6 @@ static void max98088_handle_pdata(struct snd_soc_codec *codec)
                max98088_handle_eq_pdata(codec);
 }
 
-#ifdef CONFIG_PM
-static int max98088_suspend(struct snd_soc_codec *codec)
-{
-       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
-static int max98088_resume(struct snd_soc_codec *codec)
-{
-       max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-#else
-#define max98088_suspend NULL
-#define max98088_resume NULL
-#endif
-
 static int max98088_probe(struct snd_soc_codec *codec)
 {
        struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
@@ -1946,9 +1927,6 @@ static int max98088_probe(struct snd_soc_codec *codec)
 
        snd_soc_write(codec, M98088_REG_51_PWR_SYS, M98088_PWRSV);
 
-       /* initialize registers cache to hardware default */
-       max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        snd_soc_write(codec, M98088_REG_0F_IRQ_ENABLE, 0x00);
 
        snd_soc_write(codec, M98088_REG_22_MIX_DAC,
@@ -1974,7 +1952,6 @@ static int max98088_remove(struct snd_soc_codec *codec)
 {
        struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
 
-       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
        kfree(max98088->eq_texts);
 
        return 0;
@@ -1983,9 +1960,9 @@ static int max98088_remove(struct snd_soc_codec *codec)
 static struct snd_soc_codec_driver soc_codec_dev_max98088 = {
        .probe   = max98088_probe,
        .remove  = max98088_remove,
-       .suspend = max98088_suspend,
-       .resume  = max98088_resume,
        .set_bias_level = max98088_set_bias_level,
+       .suspend_bias_off = true,
+
        .controls = max98088_snd_controls,
        .num_controls = ARRAY_SIZE(max98088_snd_controls),
        .dapm_widgets = max98088_dapm_widgets,
index d519294..151f718 100644 (file)
@@ -806,7 +806,7 @@ static const struct snd_kcontrol_new max98091_snd_controls[] = {
 static int max98090_micinput_event(struct snd_soc_dapm_widget *w,
                                 struct snd_kcontrol *kcontrol, int event)
 {
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
 
        unsigned int val = snd_soc_read(codec, w->reg);
@@ -1311,6 +1311,10 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
        {"MIC1 Input", NULL, "MIC1"},
        {"MIC2 Input", NULL, "MIC2"},
 
+       {"DMICL", NULL, "DMICL_ENA"},
+       {"DMICL", NULL, "DMICR_ENA"},
+       {"DMICR", NULL, "DMICL_ENA"},
+       {"DMICR", NULL, "DMICR_ENA"},
        {"DMICL", NULL, "AHPF"},
        {"DMICR", NULL, "AHPF"},
 
@@ -1368,8 +1372,6 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
        {"DMIC Mux", "ADC", "ADCR"},
        {"DMIC Mux", "DMIC", "DMICL"},
        {"DMIC Mux", "DMIC", "DMICR"},
-       {"DMIC Mux", "DMIC", "DMICL_ENA"},
-       {"DMIC Mux", "DMIC", "DMICR_ENA"},
 
        {"LBENL Mux", "Normal", "DMIC Mux"},
        {"LBENL Mux", "Loopback", "LTENL Mux"},
@@ -1395,8 +1397,8 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
        {"STENL Mux", "Sidetone Left", "DMICL"},
        {"STENR Mux", "Sidetone Right", "ADCR"},
        {"STENR Mux", "Sidetone Right", "DMICR"},
-       {"DACL", "NULL", "STENL Mux"},
-       {"DACR", "NULL", "STENL Mux"},
+       {"DACL", NULL, "STENL Mux"},
+       {"DACR", NULL, "STENR Mux"},
 
        {"AIFINL", NULL, "SHDN"},
        {"AIFINR", NULL, "SHDN"},
@@ -1826,27 +1828,155 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
-static const int comp_pclk_rates[] = {
-       11289600, 12288000, 12000000, 13000000, 19200000
-};
-
-static const int dmic_micclk[] = {
-       2, 2, 2, 2, 4, 2
-};
+static const int dmic_divisors[] = { 2, 3, 4, 5, 6, 8 };
 
 static const int comp_lrclk_rates[] = {
        8000, 16000, 32000, 44100, 48000, 96000
 };
 
-static const int dmic_comp[6][6] = {
-       {7, 8, 3, 3, 3, 3},
-       {7, 8, 3, 3, 3, 3},
-       {7, 8, 3, 3, 3, 3},
-       {7, 8, 3, 1, 1, 1},
-       {7, 8, 3, 1, 2, 2},
-       {7, 8, 3, 3, 3, 3}
+struct dmic_table {
+       int pclk;
+       struct {
+               int freq;
+               int comp[6]; /* One each for 8, 16, 32, 44.1, 48, and 96 kHz */
+       } settings[6]; /* One for each dmic divisor. */
 };
 
+static const struct dmic_table dmic_table[] = { /* One for each pclk freq. */
+       {
+               .pclk = 11289600,
+               .settings = {
+                       { .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } },
+                       { .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } },
+                       { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+                       { .freq = 0, .comp = { 7, 8, 6, 6, 6, 6 } },
+                       { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+                       { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+               },
+       },
+       {
+               .pclk = 12000000,
+               .settings = {
+                       { .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } },
+                       { .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } },
+                       { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+                       { .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } },
+                       { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+                       { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+               }
+       },
+       {
+               .pclk = 12288000,
+               .settings = {
+                       { .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } },
+                       { .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } },
+                       { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+                       { .freq = 0, .comp = { 7, 8, 6, 6, 6, 6 } },
+                       { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+                       { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+               }
+       },
+       {
+               .pclk = 13000000,
+               .settings = {
+                       { .freq = 2, .comp = { 7, 8, 1, 1, 1, 1 } },
+                       { .freq = 1, .comp = { 7, 8, 0, 0, 0, 0 } },
+                       { .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } },
+                       { .freq = 0, .comp = { 7, 8, 4, 4, 5, 5 } },
+                       { .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } },
+                       { .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } },
+               }
+       },
+       {
+               .pclk = 19200000,
+               .settings = {
+                       { .freq = 2, .comp = { 0, 0, 0, 0, 0, 0 } },
+                       { .freq = 1, .comp = { 7, 8, 1, 1, 1, 1 } },
+                       { .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } },
+                       { .freq = 0, .comp = { 7, 8, 2, 2, 3, 3 } },
+                       { .freq = 0, .comp = { 7, 8, 1, 1, 2, 2 } },
+                       { .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } },
+               }
+       },
+};
+
+static int max98090_find_divisor(int target_freq, int pclk)
+{
+       int current_diff = INT_MAX;
+       int test_diff = INT_MAX;
+       int divisor_index = 0;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(dmic_divisors); i++) {
+               test_diff = abs(target_freq - (pclk / dmic_divisors[i]));
+               if (test_diff < current_diff) {
+                       current_diff = test_diff;
+                       divisor_index = i;
+               }
+       }
+
+       return divisor_index;
+}
+
+static int max98090_find_closest_pclk(int pclk)
+{
+       int m1;
+       int m2;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(dmic_table); i++) {
+               if (pclk == dmic_table[i].pclk)
+                       return i;
+               if (pclk < dmic_table[i].pclk) {
+                       if (i == 0)
+                               return i;
+                       m1 = pclk - dmic_table[i-1].pclk;
+                       m2 = dmic_table[i].pclk - pclk;
+                       if (m1 < m2)
+                               return i - 1;
+                       else
+                               return i;
+               }
+       }
+
+       return -EINVAL;
+}
+
+static int max98090_configure_dmic(struct max98090_priv *max98090,
+                                  int target_dmic_clk, int pclk, int fs)
+{
+       int micclk_index;
+       int pclk_index;
+       int dmic_freq;
+       int dmic_comp;
+       int i;
+
+       pclk_index = max98090_find_closest_pclk(pclk);
+       if (pclk_index < 0)
+               return pclk_index;
+
+       micclk_index = max98090_find_divisor(target_dmic_clk, pclk);
+
+       for (i = 0; i < ARRAY_SIZE(comp_lrclk_rates) - 1; i++) {
+               if (fs <= (comp_lrclk_rates[i] + comp_lrclk_rates[i+1]) / 2)
+                       break;
+       }
+
+       dmic_freq = dmic_table[pclk_index].settings[micclk_index].freq;
+       dmic_comp = dmic_table[pclk_index].settings[micclk_index].comp[i];
+
+       regmap_update_bits(max98090->regmap, M98090_REG_DIGITAL_MIC_ENABLE,
+                          M98090_MICCLK_MASK,
+                          micclk_index << M98090_MICCLK_SHIFT);
+
+       regmap_update_bits(max98090->regmap, M98090_REG_DIGITAL_MIC_CONFIG,
+                          M98090_DMIC_COMP_MASK | M98090_DMIC_FREQ_MASK,
+                          dmic_comp << M98090_DMIC_COMP_SHIFT |
+                          dmic_freq << M98090_DMIC_FREQ_SHIFT);
+
+       return 0;
+}
+
 static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
                                   struct snd_pcm_hw_params *params,
                                   struct snd_soc_dai *dai)
@@ -1854,7 +1984,6 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_codec *codec = dai->codec;
        struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
        struct max98090_cdata *cdata;
-       int i, j;
 
        cdata = &max98090->dai[0];
        max98090->bclk = snd_soc_params_to_bclk(params);
@@ -1893,27 +2022,8 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
                snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG,
                        M98090_DHF_MASK, M98090_DHF_MASK);
 
-       /* Check for supported PCLK to LRCLK ratios */
-       for (j = 0; j < ARRAY_SIZE(comp_pclk_rates); j++) {
-               if (comp_pclk_rates[j] == max98090->sysclk) {
-                       break;
-               }
-       }
-
-       for (i = 0; i < ARRAY_SIZE(comp_lrclk_rates) - 1; i++) {
-               if (max98090->lrclk <= (comp_lrclk_rates[i] +
-                       comp_lrclk_rates[i + 1]) / 2) {
-                       break;
-               }
-       }
-
-       snd_soc_update_bits(codec, M98090_REG_DIGITAL_MIC_ENABLE,
-                       M98090_MICCLK_MASK,
-                       dmic_micclk[j] << M98090_MICCLK_SHIFT);
-
-       snd_soc_update_bits(codec, M98090_REG_DIGITAL_MIC_CONFIG,
-                       M98090_DMIC_COMP_MASK,
-                       dmic_comp[j][i] << M98090_DMIC_COMP_SHIFT);
+       max98090_configure_dmic(max98090, max98090->dmic_freq, max98090->pclk,
+                               max98090->lrclk);
 
        return 0;
 }
@@ -1941,15 +2051,18 @@ static int max98090_dai_set_sysclk(struct snd_soc_dai *dai,
         *               0x02 (when master clk is 20MHz to 40MHz)..
         *               0x03 (when master clk is 40MHz to 60MHz)..
         */
-       if ((freq >= 10000000) && (freq < 20000000)) {
+       if ((freq >= 10000000) && (freq <= 20000000)) {
                snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
                        M98090_PSCLK_DIV1);
-       } else if ((freq >= 20000000) && (freq < 40000000)) {
+               max98090->pclk = freq;
+       } else if ((freq > 20000000) && (freq <= 40000000)) {
                snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
                        M98090_PSCLK_DIV2);
-       } else if ((freq >= 40000000) && (freq < 60000000)) {
+               max98090->pclk = freq >> 1;
+       } else if ((freq > 40000000) && (freq <= 60000000)) {
                snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
                        M98090_PSCLK_DIV4);
+               max98090->pclk = freq >> 2;
        } else {
                dev_err(codec->dev, "Invalid master clock frequency\n");
                return -EINVAL;
@@ -2324,6 +2437,7 @@ static int max98090_probe(struct snd_soc_codec *codec)
        /* Initialize private data */
 
        max98090->sysclk = (unsigned)-1;
+       max98090->pclk = (unsigned)-1;
        max98090->master = false;
 
        cdata = &max98090->dai[0];
@@ -2463,6 +2577,11 @@ static int max98090_i2c_probe(struct i2c_client *i2c,
        i2c_set_clientdata(i2c, max98090);
        max98090->pdata = i2c->dev.platform_data;
 
+       ret = of_property_read_u32(i2c->dev.of_node, "maxim,dmic-freq",
+                                  &max98090->dmic_freq);
+       if (ret < 0)
+               max98090->dmic_freq = MAX98090_DEFAULT_DMIC_FREQ;
+
        max98090->regmap = devm_regmap_init_i2c(i2c, &max98090_regmap);
        if (IS_ERR(max98090->regmap)) {
                ret = PTR_ERR(max98090->regmap);
index a5f6bad..21ff743 100644 (file)
 #ifndef _MAX98090_H
 #define _MAX98090_H
 
+/*
+ * The default operating frequency for a DMIC attached to the codec.
+ * This can be overridden by a device tree property.
+ */
+#define MAX98090_DEFAULT_DMIC_FREQ             2500000
+
 /*
  * MAX98090 Register Definitions
  */
@@ -1518,8 +1524,10 @@ struct max98090_priv {
        struct max98090_pdata *pdata;
        struct clk *mclk;
        unsigned int sysclk;
+       unsigned int pclk;
        unsigned int bclk;
        unsigned int lrclk;
+       u32 dmic_freq;
        struct max98090_cdata dai[1];
        int jack_state;
        struct delayed_work jack_work;
index 0ee6797..8fba0c3 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/clk.h>
+#include <linux/mutex.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -57,6 +58,7 @@ struct max98095_priv {
        unsigned int mic2pre;
        struct snd_soc_jack *headphone_jack;
        struct snd_soc_jack *mic_jack;
+       struct mutex lock;
 };
 
 static const struct reg_default max98095_reg_def[] = {
@@ -864,7 +866,7 @@ static const struct snd_kcontrol_new max98095_right_ADC_mixer_controls[] = {
 static int max98095_mic_event(struct snd_soc_dapm_widget *w,
                             struct snd_kcontrol *kcontrol, int event)
 {
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
 
        switch (event) {
@@ -894,7 +896,7 @@ static int max98095_mic_event(struct snd_soc_dapm_widget *w,
 static int max98095_line_pga(struct snd_soc_dapm_widget *w,
                             int event, u8 channel)
 {
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
        u8 *state;
 
@@ -942,7 +944,7 @@ static int max98095_pga_in2_event(struct snd_soc_dapm_widget *w,
 static int max98095_lineout_event(struct snd_soc_dapm_widget *w,
                             struct snd_kcontrol *kcontrol, int event)
 {
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 
        switch (event) {
        case SND_SOC_DAPM_POST_PMU:
@@ -1803,7 +1805,7 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
        regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL);
        snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, 0);
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&max98095->lock);
        snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG);
        m98095_eq_band(codec, channel, 0, coef_set->band1);
        m98095_eq_band(codec, channel, 1, coef_set->band2);
@@ -1811,7 +1813,7 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
        m98095_eq_band(codec, channel, 3, coef_set->band4);
        m98095_eq_band(codec, channel, 4, coef_set->band5);
        snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, 0);
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&max98095->lock);
 
        /* Restore the original on/off state */
        snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, regsave);
@@ -1957,12 +1959,12 @@ static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol,
        regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL);
        snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, 0);
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&max98095->lock);
        snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG);
        m98095_biquad_band(codec, channel, 0, coef_set->band1);
        m98095_biquad_band(codec, channel, 1, coef_set->band2);
        snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, 0);
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&max98095->lock);
 
        /* Restore the original on/off state */
        snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, regsave);
@@ -2317,9 +2319,6 @@ static int max98095_probe(struct snd_soc_codec *codec)
 
        snd_soc_write(codec, M98095_097_PWR_SYS, M98095_PWRSV);
 
-       /* initialize registers cache to hardware default */
-       max98095_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        snd_soc_write(codec, M98095_048_MIX_DAC_LR,
                M98095_DAI1L_TO_DACL|M98095_DAI1R_TO_DACR);
 
@@ -2359,8 +2358,6 @@ static int max98095_remove(struct snd_soc_codec *codec)
        struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
        struct i2c_client *client = to_i2c_client(codec->dev);
 
-       max98095_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
        if (max98095->headphone_jack || max98095->mic_jack)
                max98095_jack_detect_disable(codec);
 
@@ -2395,6 +2392,8 @@ static int max98095_i2c_probe(struct i2c_client *i2c,
        if (max98095 == NULL)
                return -ENOMEM;
 
+       mutex_init(&max98095->lock);
+
        max98095->regmap = devm_regmap_init_i2c(i2c, &max98095_regmap);
        if (IS_ERR(max98095->regmap)) {
                ret = PTR_ERR(max98095->regmap);
index 4fdf5aa..10f8e47 100644 (file)
@@ -291,25 +291,6 @@ static struct snd_soc_dai_driver max9850_dai = {
        .ops = &max9850_dai_ops,
 };
 
-#ifdef CONFIG_PM
-static int max9850_suspend(struct snd_soc_codec *codec)
-{
-       max9850_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
-static int max9850_resume(struct snd_soc_codec *codec)
-{
-       max9850_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-#else
-#define max9850_suspend NULL
-#define max9850_resume NULL
-#endif
-
 static int max9850_probe(struct snd_soc_codec *codec)
 {
        /* enable zero-detect */
@@ -324,9 +305,8 @@ static int max9850_probe(struct snd_soc_codec *codec)
 
 static struct snd_soc_codec_driver soc_codec_dev_max9850 = {
        .probe =        max9850_probe,
-       .suspend =      max9850_suspend,
-       .resume =       max9850_resume,
        .set_bias_level = max9850_set_bias_level,
+       .suspend_bias_off = true,
 
        .controls = max9850_controls,
        .num_controls = ARRAY_SIZE(max9850_controls),
index 4aa555c..2cd4fe4 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
+#include <linux/dmi.h>
 #include <linux/acpi.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 
 struct rt286_priv {
        struct regmap *regmap;
+       struct snd_soc_codec *codec;
        struct rt286_platform_data pdata;
        struct i2c_client *i2c;
        struct snd_soc_jack *jack;
        struct delayed_work jack_detect_work;
        int sys_clk;
+       int clk_id;
        struct reg_default *index_cache;
 };
 
@@ -188,7 +191,7 @@ static int rt286_hw_write(void *context, unsigned int reg, unsigned int value)
        u8 data[4];
        int ret, i;
 
-       /*handle index registers*/
+       /* handle index registers */
        if (reg <= 0xff) {
                rt286_hw_write(client, RT286_COEF_INDEX, reg);
                for (i = 0; i < INDEX_CACHE_SIZE; i++) {
@@ -231,7 +234,7 @@ static int rt286_hw_read(void *context, unsigned int reg, unsigned int *value)
        __be32 be_reg;
        unsigned int index, vid, buf = 0x0;
 
-       /*handle index registers*/
+       /* handle index registers */
        if (reg <= 0xff) {
                rt286_hw_write(client, RT286_COEF_INDEX, reg);
                reg = RT286_PROC_COEF;
@@ -298,7 +301,6 @@ static int rt286_support_power_controls[] = {
 static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
 {
        unsigned int val, buf;
-       int i;
 
        *hp = false;
        *mic = false;
@@ -309,67 +311,44 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
                if (*hp) {
                        /* power on HV,VERF */
                        regmap_update_bits(rt286->regmap,
-                               RT286_POWER_CTRL1, 0x1001, 0x0);
+                               RT286_DC_GAIN, 0x200, 0x200);
+
+                       snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
+                                                       "HV");
+                       snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
+                                                       "VREF");
                        /* power LDO1 */
-                       regmap_update_bits(rt286->regmap,
-                               RT286_POWER_CTRL2, 0x4, 0x4);
-                       regmap_write(rt286->regmap, RT286_SET_MIC1, 0x24);
-                       regmap_read(rt286->regmap, RT286_CBJ_CTRL2, &val);
+                       snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
+                                                       "LDO1");
+                       snd_soc_dapm_sync(&rt286->codec->dapm);
 
-                       msleep(200);
-                       i = 40;
-                       while (((val & 0x0800) == 0) && (i > 0)) {
-                               regmap_read(rt286->regmap,
-                                       RT286_CBJ_CTRL2, &val);
-                               i--;
-                               msleep(20);
-                       }
+                       regmap_write(rt286->regmap, RT286_SET_MIC1, 0x24);
+                       msleep(50);
 
-                       if (0x0400 == (val & 0x0700)) {
-                               *mic = false;
+                       regmap_update_bits(rt286->regmap,
+                               RT286_CBJ_CTRL1, 0xfcc0, 0xd400);
+                       msleep(300);
+                       regmap_read(rt286->regmap, RT286_CBJ_CTRL2, &val);
 
-                               regmap_write(rt286->regmap,
-                                       RT286_SET_MIC1, 0x20);
-                               /* power off HV,VERF */
-                               regmap_update_bits(rt286->regmap,
-                                       RT286_POWER_CTRL1, 0x1001, 0x1001);
-                               regmap_update_bits(rt286->regmap,
-                                       RT286_A_BIAS_CTRL3, 0xc000, 0x0000);
-                               regmap_update_bits(rt286->regmap,
-                                       RT286_CBJ_CTRL1, 0x0030, 0x0000);
-                               regmap_update_bits(rt286->regmap,
-                                       RT286_A_BIAS_CTRL2, 0xc000, 0x0000);
-                       } else if ((0x0200 == (val & 0x0700)) ||
-                               (0x0100 == (val & 0x0700))) {
+                       if (0x0070 == (val & 0x0070)) {
                                *mic = true;
-                               regmap_update_bits(rt286->regmap,
-                                       RT286_A_BIAS_CTRL3, 0xc000, 0x8000);
-                               regmap_update_bits(rt286->regmap,
-                                       RT286_CBJ_CTRL1, 0x0030, 0x0020);
-                               regmap_update_bits(rt286->regmap,
-                                       RT286_A_BIAS_CTRL2, 0xc000, 0x8000);
                        } else {
-                               *mic = false;
+                               regmap_update_bits(rt286->regmap,
+                                       RT286_CBJ_CTRL1, 0xfcc0, 0xe400);
+                               msleep(300);
+                               regmap_read(rt286->regmap,
+                                       RT286_CBJ_CTRL2, &val);
+                               if (0x0070 == (val & 0x0070))
+                                       *mic = true;
+                               else
+                                       *mic = false;
                        }
-
-                       regmap_update_bits(rt286->regmap,
-                                               RT286_MISC_CTRL1,
-                                               0x0060, 0x0000);
-               } else {
-                       regmap_update_bits(rt286->regmap,
-                                               RT286_MISC_CTRL1,
-                                               0x0060, 0x0020);
                        regmap_update_bits(rt286->regmap,
-                                               RT286_A_BIAS_CTRL3,
-                                               0xc000, 0x8000);
-                       regmap_update_bits(rt286->regmap,
-                                               RT286_CBJ_CTRL1,
-                                               0x0030, 0x0020);
-                       regmap_update_bits(rt286->regmap,
-                                               RT286_A_BIAS_CTRL2,
-                                               0xc000, 0x8000);
+                               RT286_DC_GAIN, 0x200, 0x0);
 
+               } else {
                        *mic = false;
+                       regmap_write(rt286->regmap, RT286_SET_MIC1, 0x20);
                }
        } else {
                regmap_read(rt286->regmap, RT286_GET_HP_SENSE, &buf);
@@ -378,6 +357,12 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
                *mic = buf & 0x80000000;
        }
 
+       snd_soc_dapm_disable_pin(&rt286->codec->dapm, "HV");
+       snd_soc_dapm_disable_pin(&rt286->codec->dapm, "VREF");
+       if (!*hp)
+               snd_soc_dapm_disable_pin(&rt286->codec->dapm, "LDO1");
+       snd_soc_dapm_sync(&rt286->codec->dapm);
+
        return 0;
 }
 
@@ -415,6 +400,17 @@ int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
 }
 EXPORT_SYMBOL_GPL(rt286_mic_detect);
 
+static int is_mclk_mode(struct snd_soc_dapm_widget *source,
+                        struct snd_soc_dapm_widget *sink)
+{
+       struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(source->codec);
+
+       if (rt286->clk_id == RT286_SCLK_S_MCLK)
+               return 1;
+       else
+               return 0;
+}
+
 static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6350, 50, 0);
 static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
 
@@ -568,7 +564,84 @@ static int rt286_adc_event(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
+static int rt286_vref_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               snd_soc_update_bits(codec,
+                       RT286_CBJ_CTRL1, 0x0400, 0x0000);
+               mdelay(50);
+               break;
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt286_ldo2_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               snd_soc_update_bits(codec, RT286_POWER_CTRL2, 0x38, 0x08);
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_update_bits(codec, RT286_POWER_CTRL2, 0x38, 0x30);
+               break;
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt286_mic1_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               snd_soc_update_bits(codec,
+                       RT286_A_BIAS_CTRL3, 0xc000, 0x8000);
+               snd_soc_update_bits(codec,
+                       RT286_A_BIAS_CTRL2, 0xc000, 0x8000);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               snd_soc_update_bits(codec,
+                       RT286_A_BIAS_CTRL3, 0xc000, 0x0000);
+               snd_soc_update_bits(codec,
+                       RT286_A_BIAS_CTRL2, 0xc000, 0x0000);
+               break;
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
 static const struct snd_soc_dapm_widget rt286_dapm_widgets[] = {
+       SND_SOC_DAPM_SUPPLY_S("HV", 1, RT286_POWER_CTRL1,
+               12, 1, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("VREF", RT286_POWER_CTRL1,
+               0, 1, rt286_vref_event, SND_SOC_DAPM_PRE_PMU),
+       SND_SOC_DAPM_SUPPLY_S("LDO1", 1, RT286_POWER_CTRL2,
+               2, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("LDO2", 2, RT286_POWER_CTRL1,
+               13, 1, rt286_ldo2_event, SND_SOC_DAPM_PRE_PMD |
+               SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_SUPPLY("MCLK MODE", RT286_PLL_CTRL1,
+               5, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("MIC1 Input Buffer", SND_SOC_NOPM,
+               0, 0, rt286_mic1_event, SND_SOC_DAPM_PRE_PMU |
+               SND_SOC_DAPM_POST_PMD),
+
        /* Input Lines */
        SND_SOC_DAPM_INPUT("DMIC1 Pin"),
        SND_SOC_DAPM_INPUT("DMIC2 Pin"),
@@ -642,6 +715,25 @@ static const struct snd_soc_dapm_widget rt286_dapm_widgets[] = {
 };
 
 static const struct snd_soc_dapm_route rt286_dapm_routes[] = {
+       {"ADC 0", NULL, "MCLK MODE", is_mclk_mode},
+       {"ADC 1", NULL, "MCLK MODE", is_mclk_mode},
+       {"Front", NULL, "MCLK MODE", is_mclk_mode},
+       {"Surround", NULL, "MCLK MODE", is_mclk_mode},
+
+       {"HP Power", NULL, "LDO1"},
+       {"HP Power", NULL, "LDO2"},
+
+       {"MIC1", NULL, "LDO1"},
+       {"MIC1", NULL, "LDO2"},
+       {"MIC1", NULL, "HV"},
+       {"MIC1", NULL, "VREF"},
+       {"MIC1", NULL, "MIC1 Input Buffer"},
+
+       {"SPO", NULL, "LDO1"},
+       {"SPO", NULL, "LDO2"},
+       {"SPO", NULL, "HV"},
+       {"SPO", NULL, "VREF"},
+
        {"DMIC1", NULL, "DMIC1 Pin"},
        {"DMIC2", NULL, "DMIC2 Pin"},
        {"DMIC1", NULL, "DMIC Receiver"},
@@ -880,6 +972,7 @@ static int rt286_set_dai_sysclk(struct snd_soc_dai *dai,
        }
 
        rt286->sys_clk = freq;
+       rt286->clk_id = clk_id;
 
        return 0;
 }
@@ -915,13 +1008,18 @@ static int rt286_set_bias_level(struct snd_soc_codec *codec,
 
        case SND_SOC_BIAS_ON:
                mdelay(10);
+               snd_soc_update_bits(codec,
+                       RT286_CBJ_CTRL1, 0x0400, 0x0400);
+               snd_soc_update_bits(codec,
+                       RT286_DC_GAIN, 0x200, 0x0);
+
                break;
 
        case SND_SOC_BIAS_STANDBY:
                snd_soc_write(codec,
                        RT286_SET_AUDIO_POWER, AC_PWRST_D3);
                snd_soc_update_bits(codec,
-                       RT286_DC_GAIN, 0x200, 0x0);
+                       RT286_CBJ_CTRL1, 0x0400, 0x0000);
                break;
 
        default:
@@ -962,6 +1060,7 @@ static int rt286_probe(struct snd_soc_codec *codec)
 {
        struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
 
+       rt286->codec = codec;
        codec->dapm.bias_level = SND_SOC_BIAS_OFF;
 
        if (rt286->i2c->irq) {
@@ -1107,6 +1206,16 @@ static const struct acpi_device_id rt286_acpi_match[] = {
 };
 MODULE_DEVICE_TABLE(acpi, rt286_acpi_match);
 
+static struct dmi_system_id force_combo_jack_table[] = {
+       {
+               .ident = "Intel Wilson Beach",
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_NAME, "Wilson Beach SDS")
+               }
+       },
+       { }
+};
+
 static int rt286_i2c_probe(struct i2c_client *i2c,
                           const struct i2c_device_id *id)
 {
@@ -1142,6 +1251,9 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
        if (pdata)
                rt286->pdata = *pdata;
 
+       if (dmi_check_system(force_combo_jack_table))
+               rt286->pdata.cbj_en = true;
+
        regmap_write(rt286->regmap, RT286_SET_AUDIO_POWER, AC_PWRST_D3);
 
        for (i = 0; i < RT286_POWER_REG_LEN; i++)
@@ -1152,7 +1264,6 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
        if (!rt286->pdata.cbj_en) {
                regmap_write(rt286->regmap, RT286_CBJ_CTRL2, 0x0000);
                regmap_write(rt286->regmap, RT286_MIC1_DET_CTRL, 0x0816);
-               regmap_write(rt286->regmap, RT286_MISC_CTRL1, 0x0000);
                regmap_update_bits(rt286->regmap,
                                        RT286_CBJ_CTRL1, 0xf000, 0xb000);
        } else {
@@ -1169,10 +1280,12 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
 
        mdelay(10);
 
-       /*Power down LDO2*/
-       regmap_update_bits(rt286->regmap, RT286_POWER_CTRL2, 0x8, 0x0);
+       regmap_write(rt286->regmap, RT286_MISC_CTRL1, 0x0000);
+       /* Power down LDO, VREF */
+       regmap_update_bits(rt286->regmap, RT286_POWER_CTRL2, 0xc, 0x0);
+       regmap_update_bits(rt286->regmap, RT286_POWER_CTRL1, 0x1001, 0x1001);
 
-       /*Set depop parameter*/
+       /* Set depop parameter */
        regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL2, 0x403a, 0x401a);
        regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL3, 0xf777, 0x4737);
        regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL4, 0x00ff, 0x003f);
index 1ba27db..6d7b7ca 100644 (file)
@@ -1612,29 +1612,6 @@ static int rt5631_probe(struct snd_soc_codec *codec)
        return 0;
 }
 
-static int rt5631_remove(struct snd_soc_codec *codec)
-{
-       rt5631_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-#ifdef CONFIG_PM
-static int rt5631_suspend(struct snd_soc_codec *codec)
-{
-       rt5631_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int rt5631_resume(struct snd_soc_codec *codec)
-{
-       rt5631_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-#else
-#define rt5631_suspend NULL
-#define rt5631_resume NULL
-#endif
-
 #define RT5631_STEREO_RATES SNDRV_PCM_RATE_8000_96000
 #define RT5631_FORMAT  (SNDRV_PCM_FMTBIT_S16_LE | \
                        SNDRV_PCM_FMTBIT_S20_3LE | \
@@ -1672,10 +1649,8 @@ static struct snd_soc_dai_driver rt5631_dai[] = {
 
 static struct snd_soc_codec_driver soc_codec_dev_rt5631 = {
        .probe = rt5631_probe,
-       .remove = rt5631_remove,
-       .suspend = rt5631_suspend,
-       .resume = rt5631_resume,
        .set_bias_level = rt5631_set_bias_level,
+       .suspend_bias_off = true,
        .controls = rt5631_snd_controls,
        .num_controls = ARRAY_SIZE(rt5631_snd_controls),
        .dapm_widgets = rt5631_dapm_widgets,
@@ -1686,10 +1661,20 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5631 = {
 
 static const struct i2c_device_id rt5631_i2c_id[] = {
        { "rt5631", 0 },
+       { "alc5631", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, rt5631_i2c_id);
 
+#ifdef CONFIG_OF
+static struct of_device_id rt5631_i2c_dt_ids[] = {
+       { .compatible = "realtek,rt5631"},
+       { .compatible = "realtek,alc5631"},
+       { }
+};
+MODULE_DEVICE_TABLE(of, rt5631_i2c_dt_ids);
+#endif
+
 static const struct regmap_config rt5631_regmap_config = {
        .reg_bits = 8,
        .val_bits = 16,
@@ -1734,6 +1719,7 @@ static struct i2c_driver rt5631_i2c_driver = {
        .driver = {
                .name = "rt5631",
                .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(rt5631_i2c_dt_ids),
        },
        .probe = rt5631_i2c_probe,
        .remove   = rt5631_i2c_remove,
index 3fb83bf..a7789a8 100644 (file)
@@ -139,6 +139,7 @@ static const struct reg_default rt5645_reg[] = {
        { 0x76, 0x000a },
        { 0x77, 0x0c00 },
        { 0x78, 0x0000 },
+       { 0x79, 0x0123 },
        { 0x80, 0x0000 },
        { 0x81, 0x0000 },
        { 0x82, 0x0000 },
@@ -334,6 +335,7 @@ static bool rt5645_readable_register(struct device *dev, unsigned int reg)
        case RT5645_DMIC_CTRL2:
        case RT5645_TDM_CTRL_1:
        case RT5645_TDM_CTRL_2:
+       case RT5645_TDM_CTRL_3:
        case RT5645_GLB_CLK:
        case RT5645_PLL_CTRL1:
        case RT5645_PLL_CTRL2:
@@ -552,6 +554,53 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
                return 0;
 }
 
+static int is_using_asrc(struct snd_soc_dapm_widget *source,
+                        struct snd_soc_dapm_widget *sink)
+{
+       unsigned int reg, shift, val;
+
+       switch (source->shift) {
+       case 0:
+               reg = RT5645_ASRC_3;
+               shift = 0;
+               break;
+       case 1:
+               reg = RT5645_ASRC_3;
+               shift = 4;
+               break;
+       case 3:
+               reg = RT5645_ASRC_2;
+               shift = 0;
+               break;
+       case 8:
+               reg = RT5645_ASRC_2;
+               shift = 4;
+               break;
+       case 9:
+               reg = RT5645_ASRC_2;
+               shift = 8;
+               break;
+       case 10:
+               reg = RT5645_ASRC_2;
+               shift = 12;
+               break;
+       default:
+               return 0;
+       }
+
+       val = (snd_soc_read(source->codec, reg) >> shift) & 0xf;
+       switch (val) {
+       case 1:
+       case 2:
+       case 3:
+       case 4:
+               return 1;
+       default:
+               return 0;
+       }
+
+}
+
 /* Digital Mixer */
 static const struct snd_kcontrol_new rt5645_sto1_adc_l_mix[] = {
        SOC_DAPM_SINGLE("ADC1 Switch", RT5645_STO1_ADC_MIXER,
@@ -1244,6 +1293,30 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
        SND_SOC_DAPM_SUPPLY("Mic Det Power", RT5645_PWR_VOL,
                RT5645_PWR_MIC_DET_BIT, 0, NULL, 0),
 
+       /* ASRC */
+       SND_SOC_DAPM_SUPPLY_S("I2S1 ASRC", 1, RT5645_ASRC_1,
+                             11, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5645_ASRC_1,
+                             12, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DAC STO ASRC", 1, RT5645_ASRC_1,
+                             10, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DAC MONO L ASRC", 1, RT5645_ASRC_1,
+                             9, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DAC MONO R ASRC", 1, RT5645_ASRC_1,
+                             8, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DMIC STO1 ASRC", 1, RT5645_ASRC_1,
+                             7, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DMIC MONO L ASRC", 1, RT5645_ASRC_1,
+                             5, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DMIC MONO R ASRC", 1, RT5645_ASRC_1,
+                             4, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5645_ASRC_1,
+                             3, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("ADC MONO L ASRC", 1, RT5645_ASRC_1,
+                             1, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("ADC MONO R ASRC", 1, RT5645_ASRC_1,
+                             0, 0, NULL, 0),
+
        /* Input Side */
        /* micbias */
        SND_SOC_DAPM_MICBIAS("micbias1", RT5645_PWR_ANLG2,
@@ -1502,6 +1575,17 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
 };
 
 static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
+       { "adc stereo1 filter", NULL, "ADC STO1 ASRC", is_using_asrc },
+       { "adc stereo2 filter", NULL, "ADC STO2 ASRC", is_using_asrc },
+       { "adc mono left filter", NULL, "ADC MONO L ASRC", is_using_asrc },
+       { "adc mono right filter", NULL, "ADC MONO R ASRC", is_using_asrc },
+       { "dac mono left filter", NULL, "DAC MONO L ASRC", is_using_asrc },
+       { "dac mono right filter", NULL, "DAC MONO R ASRC", is_using_asrc },
+       { "dac stereo1 filter", NULL, "DAC STO ASRC", is_using_asrc },
+
+       { "I2S1", NULL, "I2S1 ASRC" },
+       { "I2S2", NULL, "I2S2 ASRC" },
+
        { "IN1P", NULL, "LDO2" },
        { "IN2P", NULL, "LDO2" },
 
@@ -1548,12 +1632,15 @@ static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
 
        { "Stereo1 DMIC Mux", "DMIC1", "DMIC1" },
        { "Stereo1 DMIC Mux", "DMIC2", "DMIC2" },
+       { "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC" },
 
        { "Mono DMIC L Mux", "DMIC1", "DMIC L1" },
        { "Mono DMIC L Mux", "DMIC2", "DMIC L2" },
+       { "Mono DMIC L Mux", NULL, "DMIC MONO L ASRC" },
 
        { "Mono DMIC R Mux", "DMIC1", "DMIC R1" },
        { "Mono DMIC R Mux", "DMIC2", "DMIC R2" },
+       { "Mono DMIC R Mux", NULL, "DMIC MONO R ASRC" },
 
        { "Stereo1 ADC L2 Mux", "DMIC", "Stereo1 DMIC Mux" },
        { "Stereo1 ADC L2 Mux", "DAC MIX", "DAC MIXL" },
@@ -2027,8 +2114,11 @@ static int rt5645_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
        struct snd_soc_codec *codec = dai->codec;
        unsigned int val = 0;
 
-       if (rx_mask || tx_mask)
+       if (rx_mask || tx_mask) {
                val |= (1 << 14);
+               snd_soc_update_bits(codec, RT5645_BASS_BACK,
+                       RT5645_G_BB_BST_MASK, RT5645_G_BB_BST_25DB);
+       }
 
        switch (slots) {
        case 4:
@@ -2069,8 +2159,8 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
                        enum snd_soc_bias_level level)
 {
        switch (level) {
-       case SND_SOC_BIAS_STANDBY:
-               if (SND_SOC_BIAS_OFF == codec->dapm.bias_level) {
+       case SND_SOC_BIAS_PREPARE:
+               if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) {
                        snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
                                RT5645_PWR_VREF1 | RT5645_PWR_MB |
                                RT5645_PWR_BG | RT5645_PWR_VREF2,
@@ -2085,15 +2175,24 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
                }
                break;
 
+       case SND_SOC_BIAS_STANDBY:
+               snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+                       RT5645_PWR_VREF1 | RT5645_PWR_MB |
+                       RT5645_PWR_BG | RT5645_PWR_VREF2,
+                       RT5645_PWR_VREF1 | RT5645_PWR_MB |
+                       RT5645_PWR_BG | RT5645_PWR_VREF2);
+               snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+                       RT5645_PWR_FV1 | RT5645_PWR_FV2,
+                       RT5645_PWR_FV1 | RT5645_PWR_FV2);
+               break;
+
        case SND_SOC_BIAS_OFF:
                snd_soc_write(codec, RT5645_DEPOP_M2, 0x1100);
                snd_soc_write(codec, RT5645_GEN_CTRL1, 0x0128);
-               snd_soc_write(codec, RT5645_PWR_DIG1, 0x0000);
-               snd_soc_write(codec, RT5645_PWR_DIG2, 0x0000);
-               snd_soc_write(codec, RT5645_PWR_VOL, 0x0000);
-               snd_soc_write(codec, RT5645_PWR_MIXER, 0x0000);
-               snd_soc_write(codec, RT5645_PWR_ANLG1, 0x0000);
-               snd_soc_write(codec, RT5645_PWR_ANLG2, 0x0000);
+               snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+                               RT5645_PWR_VREF1 | RT5645_PWR_MB |
+                               RT5645_PWR_BG | RT5645_PWR_VREF2 |
+                               RT5645_PWR_FV1 | RT5645_PWR_FV2, 0x0);
                break;
 
        default:
@@ -2104,8 +2203,7 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
-static int rt5645_jack_detect(struct snd_soc_codec *codec,
-       struct snd_soc_jack *jack)
+static int rt5645_jack_detect(struct snd_soc_codec *codec)
 {
        struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
        int gpio_state, jack_type = 0;
@@ -2143,34 +2241,44 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec,
 
                snd_soc_dapm_disable_pin(&codec->dapm, "micbias1");
                snd_soc_dapm_disable_pin(&codec->dapm, "micbias2");
-               snd_soc_dapm_disable_pin(&codec->dapm, "LDO2");
+               if (rt5645->pdata.jd_mode == 0)
+                       snd_soc_dapm_disable_pin(&codec->dapm, "LDO2");
                snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power");
                snd_soc_dapm_sync(&codec->dapm);
        }
 
-       snd_soc_jack_report(rt5645->jack, jack_type, SND_JACK_HEADSET);
-
+       snd_soc_jack_report(rt5645->hp_jack, jack_type, SND_JACK_HEADPHONE);
+       snd_soc_jack_report(rt5645->mic_jack, jack_type, SND_JACK_MICROPHONE);
        return 0;
 }
 
 int rt5645_set_jack_detect(struct snd_soc_codec *codec,
-       struct snd_soc_jack *jack)
+       struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack)
 {
        struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
 
-       rt5645->jack = jack;
-
-       rt5645_jack_detect(codec, rt5645->jack);
+       rt5645->hp_jack = hp_jack;
+       rt5645->mic_jack = mic_jack;
+       rt5645_jack_detect(codec);
 
        return 0;
 }
 EXPORT_SYMBOL_GPL(rt5645_set_jack_detect);
 
+static void rt5645_jack_detect_work(struct work_struct *work)
+{
+       struct rt5645_priv *rt5645 =
+               container_of(work, struct rt5645_priv, jack_detect_work.work);
+
+       rt5645_jack_detect(rt5645->codec);
+}
+
 static irqreturn_t rt5645_irq(int irq, void *data)
 {
        struct rt5645_priv *rt5645 = data;
 
-       rt5645_jack_detect(rt5645->codec, rt5645->jack);
+       queue_delayed_work(system_power_efficient_wq,
+                          &rt5645->jack_detect_work, msecs_to_jiffies(250));
 
        return IRQ_HANDLED;
 }
@@ -2185,6 +2293,13 @@ static int rt5645_probe(struct snd_soc_codec *codec)
 
        snd_soc_update_bits(codec, RT5645_CHARGE_PUMP, 0x0300, 0x0200);
 
+       /* for JD function */
+       if (rt5645->pdata.en_jd_func) {
+               snd_soc_dapm_force_enable_pin(&codec->dapm, "JD Power");
+               snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2");
+               snd_soc_dapm_sync(&codec->dapm);
+       }
+
        return 0;
 }
 
@@ -2418,6 +2533,51 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
 
        }
 
+       if (rt5645->pdata.en_jd_func) {
+               regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3,
+                       RT5645_IRQ_CLK_GATE_CTRL | RT5645_MICINDET_MANU,
+                       RT5645_IRQ_CLK_GATE_CTRL | RT5645_MICINDET_MANU);
+               regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1,
+                       RT5645_CBJ_BST1_EN, RT5645_CBJ_BST1_EN);
+               regmap_update_bits(rt5645->regmap, RT5645_JD_CTRL3,
+                       RT5645_JD_CBJ_EN | RT5645_JD_CBJ_POL,
+                       RT5645_JD_CBJ_EN | RT5645_JD_CBJ_POL);
+               regmap_update_bits(rt5645->regmap, RT5645_MICBIAS,
+                       RT5645_IRQ_CLK_INT, RT5645_IRQ_CLK_INT);
+       }
+
+       if (rt5645->pdata.jd_mode) {
+               regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
+                                  RT5645_IRQ_JD_1_1_EN, RT5645_IRQ_JD_1_1_EN);
+               regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3,
+                                  RT5645_JD_PSV_MODE, RT5645_JD_PSV_MODE);
+               regmap_update_bits(rt5645->regmap, RT5645_HPO_MIXER,
+                                  RT5645_IRQ_PSV_MODE, RT5645_IRQ_PSV_MODE);
+               regmap_update_bits(rt5645->regmap, RT5645_MICBIAS,
+                                  RT5645_MIC2_OVCD_EN, RT5645_MIC2_OVCD_EN);
+               regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+                                  RT5645_GP1_PIN_IRQ, RT5645_GP1_PIN_IRQ);
+               switch (rt5645->pdata.jd_mode) {
+               case 1:
+                       regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1,
+                                          RT5645_JD1_MODE_MASK,
+                                          RT5645_JD1_MODE_0);
+                       break;
+               case 2:
+                       regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1,
+                                          RT5645_JD1_MODE_MASK,
+                                          RT5645_JD1_MODE_1);
+                       break;
+               case 3:
+                       regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1,
+                                          RT5645_JD1_MODE_MASK,
+                                          RT5645_JD1_MODE_2);
+                       break;
+               default:
+                       break;
+               }
+       }
+
        if (rt5645->i2c->irq) {
                ret = request_threaded_irq(rt5645->i2c->irq, NULL, rt5645_irq,
                        IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
@@ -2436,6 +2596,8 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
                        dev_err(&i2c->dev, "Fail gpio_direction hp_det_gpio\n");
        }
 
+       INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work);
+
        return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5645,
                                      rt5645_dai, ARRAY_SIZE(rt5645_dai));
 }
@@ -2447,6 +2609,8 @@ static int rt5645_i2c_remove(struct i2c_client *i2c)
        if (i2c->irq)
                free_irq(i2c->irq, rt5645);
 
+       cancel_delayed_work_sync(&rt5645->jack_detect_work);
+
        if (gpio_is_valid(rt5645->pdata.hp_det_gpio))
                gpio_free(rt5645->pdata.hp_det_gpio);
 
index 50c62c5..a815e36 100644 (file)
 #define RT5645_M_DAC1_HM_SFT                   14
 #define RT5645_M_HPVOL_HM                      (0x1 << 13)
 #define RT5645_M_HPVOL_HM_SFT                  13
+#define RT5645_IRQ_PSV_MODE                    (0x1 << 12)
 
 /* SPK Left Mixer Control (0x46) */
 #define RT5645_G_RM_L_SM_L_MASK                        (0x3 << 14)
 #define RT5645_PWR_CLK25M_SFT                  4
 #define RT5645_PWR_CLK25M_PD                   (0x0 << 4)
 #define RT5645_PWR_CLK25M_PU                   (0x1 << 4)
+#define RT5645_IRQ_CLK_MCLK                    (0x0 << 3)
+#define RT5645_IRQ_CLK_INT                     (0x1 << 3)
+#define RT5645_JD1_MODE_MASK                   (0x3 << 0)
+#define RT5645_JD1_MODE_0                      (0x0 << 0)
+#define RT5645_JD1_MODE_1                      (0x1 << 0)
+#define RT5645_JD1_MODE_2                      (0x2 << 0)
 
 /* VAD Control 4 (0x9d) */
 #define RT5645_VAD_SEL_MASK                    (0x3 << 8)
 #define RT5645_OT_P_SFT                                10
 #define RT5645_OT_P_NOR                                (0x0 << 10)
 #define RT5645_OT_P_INV                                (0x1 << 10)
+#define RT5645_IRQ_JD_1_1_EN                   (0x1 << 9)
 
 /* IRQ Control 2 (0xbe) */
 #define RT5645_IRQ_MB1_OC_MASK                 (0x1 << 15)
 #define RT5645_M_BB_HPF_R_SFT                  6
 #define RT5645_G_BB_BST_MASK                   (0x3f)
 #define RT5645_G_BB_BST_SFT                    0
+#define RT5645_G_BB_BST_25DB                   0x14
 
 /* MP3 Plus Control 1 (0xd0) */
 #define RT5645_M_MP3_L_MASK                    (0x1 << 15)
@@ -2116,6 +2125,10 @@ enum {
 #define RT5645_RXDP2_SEL_ADC                   (0x1 << 3)
 #define RT5645_RXDP2_SEL_SFT                   (3)
 
+/* General Control3 (0xfc) */
+#define RT5645_JD_PSV_MODE                     (0x1 << 12)
+#define RT5645_IRQ_CLK_GATE_CTRL               (0x1 << 11)
+#define RT5645_MICINDET_MANU                   (0x1 << 7)
 
 /* Vendor ID (0xfd) */
 #define RT5645_VER_C                           0x2
@@ -2167,7 +2180,9 @@ struct rt5645_priv {
        struct rt5645_platform_data pdata;
        struct regmap *regmap;
        struct i2c_client *i2c;
-       struct snd_soc_jack *jack;
+       struct snd_soc_jack *hp_jack;
+       struct snd_soc_jack *mic_jack;
+       struct delayed_work jack_detect_work;
 
        int sysclk;
        int sysclk_src;
@@ -2181,6 +2196,6 @@ struct rt5645_priv {
 };
 
 int rt5645_set_jack_detect(struct snd_soc_codec *codec,
-       struct snd_soc_jack *jack);
+       struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack);
 
 #endif /* __RT5645_H__ */
index ba9d9b4..8a0833d 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
+#include <linux/acpi.h>
 #include <linux/spi/spi.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -100,18 +101,18 @@ static const struct reg_default rt5670_reg[] = {
        { 0x4c, 0x5380 },
        { 0x4f, 0x0073 },
        { 0x52, 0x00d3 },
-       { 0x53, 0xf0f0 },
+       { 0x53, 0xf000 },
        { 0x61, 0x0000 },
        { 0x62, 0x0001 },
        { 0x63, 0x00c3 },
        { 0x64, 0x0000 },
-       { 0x65, 0x0000 },
+       { 0x65, 0x0001 },
        { 0x66, 0x0000 },
        { 0x6f, 0x8000 },
        { 0x70, 0x8000 },
        { 0x71, 0x8000 },
        { 0x72, 0x8000 },
-       { 0x73, 0x1110 },
+       { 0x73, 0x7770 },
        { 0x74, 0x0e00 },
        { 0x75, 0x1505 },
        { 0x76, 0x0015 },
@@ -125,21 +126,21 @@ static const struct reg_default rt5670_reg[] = {
        { 0x83, 0x0000 },
        { 0x84, 0x0000 },
        { 0x85, 0x0000 },
-       { 0x86, 0x0008 },
+       { 0x86, 0x0004 },
        { 0x87, 0x0000 },
        { 0x88, 0x0000 },
        { 0x89, 0x0000 },
        { 0x8a, 0x0000 },
        { 0x8b, 0x0000 },
-       { 0x8c, 0x0007 },
+       { 0x8c, 0x0003 },
        { 0x8d, 0x0000 },
        { 0x8e, 0x0004 },
        { 0x8f, 0x1100 },
        { 0x90, 0x0646 },
        { 0x91, 0x0c06 },
        { 0x93, 0x0000 },
-       { 0x94, 0x0000 },
-       { 0x95, 0x0000 },
+       { 0x94, 0x1270 },
+       { 0x95, 0x1000 },
        { 0x97, 0x0000 },
        { 0x98, 0x0000 },
        { 0x99, 0x0000 },
@@ -150,11 +151,11 @@ static const struct reg_default rt5670_reg[] = {
        { 0x9e, 0x0400 },
        { 0xae, 0x7000 },
        { 0xaf, 0x0000 },
-       { 0xb0, 0x6000 },
+       { 0xb0, 0x7000 },
        { 0xb1, 0x0000 },
        { 0xb2, 0x0000 },
        { 0xb3, 0x001f },
-       { 0xb4, 0x2206 },
+       { 0xb4, 0x220c },
        { 0xb5, 0x1f00 },
        { 0xb6, 0x0000 },
        { 0xb7, 0x0000 },
@@ -171,25 +172,25 @@ static const struct reg_default rt5670_reg[] = {
        { 0xcf, 0x1813 },
        { 0xd0, 0x0690 },
        { 0xd1, 0x1c17 },
-       { 0xd3, 0xb320 },
+       { 0xd3, 0xa220 },
        { 0xd4, 0x0000 },
        { 0xd6, 0x0400 },
        { 0xd9, 0x0809 },
        { 0xda, 0x0000 },
        { 0xdb, 0x0001 },
        { 0xdc, 0x0049 },
-       { 0xdd, 0x0009 },
+       { 0xdd, 0x0024 },
        { 0xe6, 0x8000 },
        { 0xe7, 0x0000 },
-       { 0xec, 0xb300 },
+       { 0xec, 0xa200 },
        { 0xed, 0x0000 },
-       { 0xee, 0xb300 },
+       { 0xee, 0xa200 },
        { 0xef, 0x0000 },
        { 0xf8, 0x0000 },
        { 0xf9, 0x0000 },
        { 0xfa, 0x8010 },
        { 0xfb, 0x0033 },
-       { 0xfc, 0x0080 },
+       { 0xfc, 0x0100 },
 };
 
 static bool rt5670_volatile_register(struct device *dev, unsigned int reg)
@@ -575,6 +576,18 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
 
 }
 
+static int can_use_asrc(struct snd_soc_dapm_widget *source,
+                        struct snd_soc_dapm_widget *sink)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+       struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+
+       if (rt5670->sysclk > rt5670->lrck[RT5670_AIF1] * 384)
+               return 1;
+
+       return 0;
+}
+
 /* Digital Mixer */
 static const struct snd_kcontrol_new rt5670_sto1_adc_l_mix[] = {
        SOC_DAPM_SINGLE("ADC1 Switch", RT5670_STO1_ADC_MIXER,
@@ -1281,6 +1294,14 @@ static const struct snd_soc_dapm_widget rt5670_dapm_widgets[] = {
                              9, 0, NULL, 0),
        SND_SOC_DAPM_SUPPLY_S("DAC MONO R ASRC", 1, RT5670_ASRC_1,
                              8, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DMIC STO1 ASRC", 1, RT5670_ASRC_1,
+                             7, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DMIC STO2 ASRC", 1, RT5670_ASRC_1,
+                             6, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DMIC MONO L ASRC", 1, RT5670_ASRC_1,
+                             5, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DMIC MONO R ASRC", 1, RT5670_ASRC_1,
+                             4, 0, NULL, 0),
        SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5670_ASRC_1,
                              3, 0, NULL, 0),
        SND_SOC_DAPM_SUPPLY_S("ADC STO2 ASRC", 1, RT5670_ASRC_1,
@@ -1595,29 +1616,40 @@ static const struct snd_soc_dapm_widget rt5670_dapm_widgets[] = {
        /* PDM */
        SND_SOC_DAPM_SUPPLY("PDM1 Power", RT5670_PWR_DIG2,
                RT5670_PWR_PDM1_BIT, 0, NULL, 0),
-       SND_SOC_DAPM_SUPPLY("PDM2 Power", RT5670_PWR_DIG2,
-               RT5670_PWR_PDM2_BIT, 0, NULL, 0),
 
        SND_SOC_DAPM_MUX("PDM1 L Mux", RT5670_PDM_OUT_CTRL,
                         RT5670_M_PDM1_L_SFT, 1, &rt5670_pdm1_l_mux),
        SND_SOC_DAPM_MUX("PDM1 R Mux", RT5670_PDM_OUT_CTRL,
                         RT5670_M_PDM1_R_SFT, 1, &rt5670_pdm1_r_mux),
-       SND_SOC_DAPM_MUX("PDM2 L Mux", RT5670_PDM_OUT_CTRL,
-                        RT5670_M_PDM2_L_SFT, 1, &rt5670_pdm2_l_mux),
-       SND_SOC_DAPM_MUX("PDM2 R Mux", RT5670_PDM_OUT_CTRL,
-                        RT5670_M_PDM2_R_SFT, 1, &rt5670_pdm2_r_mux),
 
        /* Output Lines */
        SND_SOC_DAPM_OUTPUT("HPOL"),
        SND_SOC_DAPM_OUTPUT("HPOR"),
        SND_SOC_DAPM_OUTPUT("LOUTL"),
        SND_SOC_DAPM_OUTPUT("LOUTR"),
+};
+
+static const struct snd_soc_dapm_widget rt5670_specific_dapm_widgets[] = {
+       SND_SOC_DAPM_SUPPLY("PDM2 Power", RT5670_PWR_DIG2,
+               RT5670_PWR_PDM2_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_MUX("PDM2 L Mux", RT5670_PDM_OUT_CTRL,
+                        RT5670_M_PDM2_L_SFT, 1, &rt5670_pdm2_l_mux),
+       SND_SOC_DAPM_MUX("PDM2 R Mux", RT5670_PDM_OUT_CTRL,
+                        RT5670_M_PDM2_R_SFT, 1, &rt5670_pdm2_r_mux),
        SND_SOC_DAPM_OUTPUT("PDM1L"),
        SND_SOC_DAPM_OUTPUT("PDM1R"),
        SND_SOC_DAPM_OUTPUT("PDM2L"),
        SND_SOC_DAPM_OUTPUT("PDM2R"),
 };
 
+static const struct snd_soc_dapm_widget rt5672_specific_dapm_widgets[] = {
+       SND_SOC_DAPM_PGA("SPO Amp", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_OUTPUT("SPOLP"),
+       SND_SOC_DAPM_OUTPUT("SPOLN"),
+       SND_SOC_DAPM_OUTPUT("SPORP"),
+       SND_SOC_DAPM_OUTPUT("SPORN"),
+};
+
 static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
        { "ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc },
        { "ADC Stereo2 Filter", NULL, "ADC STO2 ASRC", is_using_asrc },
@@ -1626,9 +1658,13 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
        { "DAC Mono Left Filter", NULL, "DAC MONO L ASRC", is_using_asrc },
        { "DAC Mono Right Filter", NULL, "DAC MONO R ASRC", is_using_asrc },
        { "DAC Stereo1 Filter", NULL, "DAC STO ASRC", is_using_asrc },
+       { "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC", can_use_asrc },
+       { "Stereo2 DMIC Mux", NULL, "DMIC STO2 ASRC", can_use_asrc },
+       { "Mono DMIC L Mux", NULL, "DMIC MONO L ASRC", can_use_asrc },
+       { "Mono DMIC R Mux", NULL, "DMIC MONO R ASRC", can_use_asrc },
 
-       { "I2S1", NULL, "I2S1 ASRC" },
-       { "I2S2", NULL, "I2S2 ASRC" },
+       { "I2S1", NULL, "I2S1 ASRC", can_use_asrc},
+       { "I2S2", NULL, "I2S2 ASRC", can_use_asrc},
 
        { "DMIC1", NULL, "DMIC L1" },
        { "DMIC1", NULL, "DMIC R1" },
@@ -1877,6 +1913,10 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
        { "DAC1 MIXR", "DAC1 Switch", "DAC1 R Mux" },
        { "DAC1 MIXR", NULL, "DAC Stereo1 Filter" },
 
+       { "DAC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll },
+       { "DAC Mono Left Filter", NULL, "PLL1", is_sys_clk_from_pll },
+       { "DAC Mono Right Filter", NULL, "PLL1", is_sys_clk_from_pll },
+
        { "DAC MIX", NULL, "DAC1 MIXL" },
        { "DAC MIX", NULL, "DAC1 MIXR" },
 
@@ -1926,14 +1966,10 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
 
        { "DAC L1", NULL, "DAC L1 Power" },
        { "DAC L1", NULL, "Stereo DAC MIXL" },
-       { "DAC L1", NULL, "PLL1", is_sys_clk_from_pll },
        { "DAC R1", NULL, "DAC R1 Power" },
        { "DAC R1", NULL, "Stereo DAC MIXR" },
-       { "DAC R1", NULL, "PLL1", is_sys_clk_from_pll },
        { "DAC L2", NULL, "Mono DAC MIXL" },
-       { "DAC L2", NULL, "PLL1", is_sys_clk_from_pll },
        { "DAC R2", NULL, "Mono DAC MIXR" },
-       { "DAC R2", NULL, "PLL1", is_sys_clk_from_pll },
 
        { "OUT MIXL", "BST1 Switch", "BST1" },
        { "OUT MIXL", "INL Switch", "INL VOL" },
@@ -1970,12 +2006,6 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
        { "PDM1 R Mux", "Stereo DAC", "Stereo DAC MIXR" },
        { "PDM1 R Mux", "Mono DAC", "Mono DAC MIXR" },
        { "PDM1 R Mux", NULL, "PDM1 Power" },
-       { "PDM2 L Mux", "Stereo DAC", "Stereo DAC MIXL" },
-       { "PDM2 L Mux", "Mono DAC", "Mono DAC MIXL" },
-       { "PDM2 L Mux", NULL, "PDM2 Power" },
-       { "PDM2 R Mux", "Stereo DAC", "Stereo DAC MIXR" },
-       { "PDM2 R Mux", "Mono DAC", "Mono DAC MIXR" },
-       { "PDM2 R Mux", NULL, "PDM2 Power" },
 
        { "HP Amp", NULL, "HPO MIX" },
        { "HP Amp", NULL, "Mic Det Power" },
@@ -1993,13 +2023,30 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
        { "LOUTR", NULL, "LOUT R Playback" },
        { "LOUTL", NULL, "Improve HP Amp Drv" },
        { "LOUTR", NULL, "Improve HP Amp Drv" },
+};
 
+static const struct snd_soc_dapm_route rt5670_specific_dapm_routes[] = {
+       { "PDM2 L Mux", "Stereo DAC", "Stereo DAC MIXL" },
+       { "PDM2 L Mux", "Mono DAC", "Mono DAC MIXL" },
+       { "PDM2 L Mux", NULL, "PDM2 Power" },
+       { "PDM2 R Mux", "Stereo DAC", "Stereo DAC MIXR" },
+       { "PDM2 R Mux", "Mono DAC", "Mono DAC MIXR" },
+       { "PDM2 R Mux", NULL, "PDM2 Power" },
        { "PDM1L", NULL, "PDM1 L Mux" },
        { "PDM1R", NULL, "PDM1 R Mux" },
        { "PDM2L", NULL, "PDM2 L Mux" },
        { "PDM2R", NULL, "PDM2 R Mux" },
 };
 
+static const struct snd_soc_dapm_route rt5672_specific_dapm_routes[] = {
+       { "SPO Amp", NULL, "PDM1 L Mux" },
+       { "SPO Amp", NULL, "PDM1 R Mux" },
+       { "SPOLP", NULL, "SPO Amp" },
+       { "SPOLN", NULL, "SPO Amp" },
+       { "SPORP", NULL, "SPO Amp" },
+       { "SPORN", NULL, "SPO Amp" },
+};
+
 static int rt5670_hw_params(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 {
@@ -2287,6 +2334,8 @@ static int rt5670_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
 static int rt5670_set_bias_level(struct snd_soc_codec *codec,
                        enum snd_soc_bias_level level)
 {
+       struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+
        switch (level) {
        case SND_SOC_BIAS_PREPARE:
                if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) {
@@ -2308,16 +2357,27 @@ static int rt5670_set_bias_level(struct snd_soc_codec *codec,
                }
                break;
        case SND_SOC_BIAS_STANDBY:
-               snd_soc_write(codec, RT5670_PWR_DIG1, 0x0000);
-               snd_soc_write(codec, RT5670_PWR_DIG2, 0x0001);
-               snd_soc_write(codec, RT5670_PWR_VOL, 0x0000);
-               snd_soc_write(codec, RT5670_PWR_MIXER, 0x0001);
-               snd_soc_write(codec, RT5670_PWR_ANLG1, 0x2800);
-               snd_soc_write(codec, RT5670_PWR_ANLG2, 0x0004);
-               snd_soc_update_bits(codec, RT5670_DIG_MISC, 0x1, 0x0);
+               snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
+                               RT5670_PWR_VREF1 | RT5670_PWR_VREF2 |
+                               RT5670_PWR_FV1 | RT5670_PWR_FV2, 0);
                snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
                                RT5670_LDO_SEL_MASK, 0x1);
                break;
+       case SND_SOC_BIAS_OFF:
+               if (rt5670->pdata.jd_mode)
+                       snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
+                               RT5670_PWR_VREF1 | RT5670_PWR_MB |
+                               RT5670_PWR_BG | RT5670_PWR_VREF2 |
+                               RT5670_PWR_FV1 | RT5670_PWR_FV2,
+                               RT5670_PWR_MB | RT5670_PWR_BG);
+               else
+                       snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
+                               RT5670_PWR_VREF1 | RT5670_PWR_MB |
+                               RT5670_PWR_BG | RT5670_PWR_VREF2 |
+                               RT5670_PWR_FV1 | RT5670_PWR_FV2, 0);
+
+               snd_soc_update_bits(codec, RT5670_DIG_MISC, 0x1, 0x0);
+               break;
 
        default:
                break;
@@ -2331,6 +2391,29 @@ static int rt5670_probe(struct snd_soc_codec *codec)
 {
        struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
 
+       switch (snd_soc_read(codec, RT5670_RESET) & RT5670_ID_MASK) {
+       case RT5670_ID_5670:
+       case RT5670_ID_5671:
+               snd_soc_dapm_new_controls(&codec->dapm,
+                       rt5670_specific_dapm_widgets,
+                       ARRAY_SIZE(rt5670_specific_dapm_widgets));
+               snd_soc_dapm_add_routes(&codec->dapm,
+                       rt5670_specific_dapm_routes,
+                       ARRAY_SIZE(rt5670_specific_dapm_routes));
+               break;
+       case RT5670_ID_5672:
+               snd_soc_dapm_new_controls(&codec->dapm,
+                       rt5672_specific_dapm_widgets,
+                       ARRAY_SIZE(rt5672_specific_dapm_widgets));
+               snd_soc_dapm_add_routes(&codec->dapm,
+                       rt5672_specific_dapm_routes,
+                       ARRAY_SIZE(rt5672_specific_dapm_routes));
+               break;
+       default:
+               dev_err(codec->dev,
+                       "The driver is for RT5670 RT5671 or RT5672 only\n");
+               return -ENODEV;
+       }
        rt5670->codec = codec;
 
        return 0;
@@ -2452,10 +2535,20 @@ static const struct regmap_config rt5670_regmap = {
 
 static const struct i2c_device_id rt5670_i2c_id[] = {
        { "rt5670", 0 },
+       { "rt5671", 0 },
+       { "rt5672", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id);
 
+#ifdef CONFIG_ACPI
+static struct acpi_device_id rt5670_acpi_match[] = {
+       { "10EC5670", 0},
+       { },
+};
+MODULE_DEVICE_TABLE(acpi, rt5670_acpi_match);
+#endif
+
 static int rt5670_i2c_probe(struct i2c_client *i2c,
                    const struct i2c_device_id *id)
 {
@@ -2644,6 +2737,7 @@ static struct i2c_driver rt5670_i2c_driver = {
        .driver = {
                .name = "rt5670",
                .owner = THIS_MODULE,
+               .acpi_match_table = ACPI_PTR(rt5670_acpi_match),
        },
        .probe = rt5670_i2c_probe,
        .remove   = rt5670_i2c_remove,
index a0b5c85..d11b9c2 100644 (file)
 #define RT5670_R_VOL_MASK                      (0x3f)
 #define RT5670_R_VOL_SFT                       0
 
+/* SW Reset & Device ID (0x00) */
+#define RT5670_ID_MASK                         (0x3 << 1)
+#define RT5670_ID_5670                         (0x0 << 1)
+#define RT5670_ID_5672                         (0x1 << 1)
+#define RT5670_ID_5671                         (0x2 << 1)
+
 /* Combo Jack Control 1 (0x0a) */
 #define RT5670_CBJ_BST1_MASK                   (0xf << 12)
 #define RT5670_CBJ_BST1_SFT                    (12)
diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c
new file mode 100644 (file)
index 0000000..ef6348c
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * rt5677-spi.c  --  RT5677 ALSA SoC audio codec driver
+ *
+ * Copyright 2013 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/spi/spi.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_qos.h>
+#include <linux/sysfs.h>
+#include <linux/clk.h>
+#include <linux/firmware.h>
+
+#include "rt5677-spi.h"
+
+static struct spi_device *g_spi;
+
+/**
+ * rt5677_spi_write - Write data to SPI.
+ * @txbuf: Data Buffer for writing.
+ * @len: Data length.
+ *
+ *
+ * Returns true for success.
+ */
+int rt5677_spi_write(u8 *txbuf, size_t len)
+{
+       int status;
+
+       status = spi_write(g_spi, txbuf, len);
+
+       if (status)
+               dev_err(&g_spi->dev, "rt5677_spi_write error %d\n", status);
+
+       return status;
+}
+EXPORT_SYMBOL_GPL(rt5677_spi_write);
+
+/**
+ * rt5677_spi_burst_write - Write data to SPI by rt5677 dsp memory address.
+ * @addr: Start address.
+ * @txbuf: Data Buffer for writng.
+ * @len: Data length, it must be a multiple of 8.
+ *
+ *
+ * Returns true for success.
+ */
+int rt5677_spi_burst_write(u32 addr, const struct firmware *fw)
+{
+       u8 spi_cmd = RT5677_SPI_CMD_BURST_WRITE;
+       u8 *write_buf;
+       unsigned int i, end, offset = 0;
+
+       write_buf = kmalloc(RT5677_SPI_BUF_LEN + 6, GFP_KERNEL);
+
+       if (write_buf == NULL)
+               return -ENOMEM;
+
+       while (offset < fw->size) {
+               if (offset + RT5677_SPI_BUF_LEN <= fw->size)
+                       end = RT5677_SPI_BUF_LEN;
+               else
+                       end = fw->size % RT5677_SPI_BUF_LEN;
+
+               write_buf[0] = spi_cmd;
+               write_buf[1] = ((addr + offset) & 0xff000000) >> 24;
+               write_buf[2] = ((addr + offset) & 0x00ff0000) >> 16;
+               write_buf[3] = ((addr + offset) & 0x0000ff00) >> 8;
+               write_buf[4] = ((addr + offset) & 0x000000ff) >> 0;
+
+               for (i = 0; i < end; i += 8) {
+                       write_buf[i + 12] = fw->data[offset + i + 0];
+                       write_buf[i + 11] = fw->data[offset + i + 1];
+                       write_buf[i + 10] = fw->data[offset + i + 2];
+                       write_buf[i +  9] = fw->data[offset + i + 3];
+                       write_buf[i +  8] = fw->data[offset + i + 4];
+                       write_buf[i +  7] = fw->data[offset + i + 5];
+                       write_buf[i +  6] = fw->data[offset + i + 6];
+                       write_buf[i +  5] = fw->data[offset + i + 7];
+               }
+
+               write_buf[end + 5] = spi_cmd;
+
+               rt5677_spi_write(write_buf, end + 6);
+
+               offset += RT5677_SPI_BUF_LEN;
+       }
+
+       kfree(write_buf);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rt5677_spi_burst_write);
+
+static int rt5677_spi_probe(struct spi_device *spi)
+{
+       g_spi = spi;
+       return 0;
+}
+
+static struct spi_driver rt5677_spi_driver = {
+       .driver = {
+               .name = "rt5677",
+               .owner = THIS_MODULE,
+       },
+       .probe = rt5677_spi_probe,
+};
+module_spi_driver(rt5677_spi_driver);
+
+MODULE_DESCRIPTION("ASoC RT5677 SPI driver");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5677-spi.h b/sound/soc/codecs/rt5677-spi.h
new file mode 100644 (file)
index 0000000..ec41b2b
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * rt5677-spi.h  --  RT5677 ALSA SoC audio codec driver
+ *
+ * Copyright 2013 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __RT5677_SPI_H__
+#define __RT5677_SPI_H__
+
+#define RT5677_SPI_BUF_LEN 240
+#define RT5677_SPI_CMD_BURST_WRITE 0x05
+
+int rt5677_spi_write(u8 *txbuf, size_t len);
+int rt5677_spi_burst_write(u32 addr, const struct firmware *fw);
+
+#endif /* __RT5677_SPI_H__ */
index 16aa4d9..81fe146 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
+#include <linux/firmware.h>
 #include <linux/gpio.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -31,6 +32,7 @@
 
 #include "rl6231.h"
 #include "rt5677.h"
+#include "rt5677-spi.h"
 
 #define RT5677_DEVICE_ID 0x6327
 
@@ -53,12 +55,13 @@ static const struct regmap_range_cfg rt5677_ranges[] = {
 };
 
 static const struct reg_default init_list[] = {
+       {RT5677_ASRC_12,        0x0018},
        {RT5677_PR_BASE + 0x3d, 0x364d},
-       {RT5677_PR_BASE + 0x17, 0x4fc0},
-       {RT5677_PR_BASE + 0x13, 0x0312},
-       {RT5677_PR_BASE + 0x1e, 0x0000},
-       {RT5677_PR_BASE + 0x12, 0x0eaa},
-       {RT5677_PR_BASE + 0x14, 0x018a},
+       {RT5677_PR_BASE + 0x17, 0x4fc0},
+       {RT5677_PR_BASE + 0x13, 0x0312},
+       {RT5677_PR_BASE + 0x1e, 0x0000},
+       {RT5677_PR_BASE + 0x12, 0x0eaa},
+       {RT5677_PR_BASE + 0x14, 0x018a},
 };
 #define RT5677_INIT_REG_LEN ARRAY_SIZE(init_list)
 
@@ -171,7 +174,7 @@ static const struct reg_default rt5677_reg[] = {
        {RT5677_ASRC_9                  , 0x0000},
        {RT5677_ASRC_10                 , 0x0000},
        {RT5677_ASRC_11                 , 0x0000},
-       {RT5677_ASRC_12                 , 0x0008},
+       {RT5677_ASRC_12                 , 0x0018},
        {RT5677_ASRC_13                 , 0x0000},
        {RT5677_ASRC_14                 , 0x0000},
        {RT5677_ASRC_15                 , 0x0000},
@@ -537,10 +540,232 @@ static bool rt5677_readable_register(struct device *dev, unsigned int reg)
        }
 }
 
+/**
+ * rt5677_dsp_mode_i2c_write_addr - Write value to address on DSP mode.
+ * @rt5677: Private Data.
+ * @addr: Address index.
+ * @value: Address data.
+ *
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int rt5677_dsp_mode_i2c_write_addr(struct rt5677_priv *rt5677,
+               unsigned int addr, unsigned int value, unsigned int opcode)
+{
+       struct snd_soc_codec *codec = rt5677->codec;
+       int ret;
+
+       mutex_lock(&rt5677->dsp_cmd_lock);
+
+       ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB,
+               addr >> 16);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set addr msb value: %d\n", ret);
+               goto err;
+       }
+
+       ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB,
+               addr & 0xffff);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set addr lsb value: %d\n", ret);
+               goto err;
+       }
+
+       ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB,
+               value >> 16);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set data msb value: %d\n", ret);
+               goto err;
+       }
+
+       ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB,
+               value & 0xffff);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set data lsb value: %d\n", ret);
+               goto err;
+       }
+
+       ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE,
+               opcode);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set op code value: %d\n", ret);
+               goto err;
+       }
+
+err:
+       mutex_unlock(&rt5677->dsp_cmd_lock);
+
+       return ret;
+}
+
+/**
+ * rt5677_dsp_mode_i2c_read_addr - Read value from address on DSP mode.
+ * rt5677: Private Data.
+ * @addr: Address index.
+ * @value: Address data.
+ *
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int rt5677_dsp_mode_i2c_read_addr(
+       struct rt5677_priv *rt5677, unsigned int addr, unsigned int *value)
+{
+       struct snd_soc_codec *codec = rt5677->codec;
+       int ret;
+       unsigned int msb, lsb;
+
+       mutex_lock(&rt5677->dsp_cmd_lock);
+
+       ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB,
+               addr >> 16);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set addr msb value: %d\n", ret);
+               goto err;
+       }
+
+       ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB,
+               addr & 0xffff);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set addr lsb value: %d\n", ret);
+               goto err;
+       }
+
+       ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE,
+               0x0002);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set op code value: %d\n", ret);
+               goto err;
+       }
+
+       regmap_read(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB, &msb);
+       regmap_read(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB, &lsb);
+       *value = (msb << 16) | lsb;
+
+err:
+       mutex_unlock(&rt5677->dsp_cmd_lock);
+
+       return ret;
+}
+
+/**
+ * rt5677_dsp_mode_i2c_write - Write register on DSP mode.
+ * rt5677: Private Data.
+ * @reg: Register index.
+ * @value: Register data.
+ *
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int rt5677_dsp_mode_i2c_write(struct rt5677_priv *rt5677,
+               unsigned int reg, unsigned int value)
+{
+       return rt5677_dsp_mode_i2c_write_addr(rt5677, 0x18020000 + reg * 2,
+               value, 0x0001);
+}
+
+/**
+ * rt5677_dsp_mode_i2c_read - Read register on DSP mode.
+ * @codec: SoC audio codec device.
+ * @reg: Register index.
+ * @value: Register data.
+ *
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int rt5677_dsp_mode_i2c_read(
+       struct rt5677_priv *rt5677, unsigned int reg, unsigned int *value)
+{
+       int ret = rt5677_dsp_mode_i2c_read_addr(rt5677, 0x18020000 + reg * 2,
+               value);
+
+       *value &= 0xffff;
+
+       return ret;
+}
+
+static void rt5677_set_dsp_mode(struct snd_soc_codec *codec, bool on)
+{
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+       if (on) {
+               regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x2, 0x2);
+               rt5677->is_dsp_mode = true;
+       } else {
+               regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x2, 0x0);
+               rt5677->is_dsp_mode = false;
+       }
+}
+
+static int rt5677_set_dsp_vad(struct snd_soc_codec *codec, bool on)
+{
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+       static bool activity;
+       int ret;
+
+       if (on && !activity) {
+               activity = true;
+
+               regcache_cache_only(rt5677->regmap, false);
+               regcache_cache_bypass(rt5677->regmap, true);
+
+               regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x1, 0x1);
+               regmap_update_bits(rt5677->regmap,
+                       RT5677_PR_BASE + RT5677_BIAS_CUR4, 0x0f00, 0x0f00);
+               regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
+                       RT5677_LDO1_SEL_MASK, 0x0);
+               regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
+                       RT5677_PWR_LDO1, RT5677_PWR_LDO1);
+               regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
+                       RT5677_MCLK_SRC_MASK, RT5677_MCLK2_SRC);
+               regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2,
+                       RT5677_PLL2_PR_SRC_MASK | RT5677_DSP_CLK_SRC_MASK,
+                       RT5677_PLL2_PR_SRC_MCLK2 | RT5677_DSP_CLK_SRC_BYPASS);
+               regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x07ff);
+               regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x07fd);
+               rt5677_set_dsp_mode(codec, true);
+
+               ret = request_firmware(&rt5677->fw1, RT5677_FIRMWARE1,
+                       codec->dev);
+               if (ret == 0) {
+                       rt5677_spi_burst_write(0x50000000, rt5677->fw1);
+                       release_firmware(rt5677->fw1);
+               }
+
+               ret = request_firmware(&rt5677->fw2, RT5677_FIRMWARE2,
+                       codec->dev);
+               if (ret == 0) {
+                       rt5677_spi_burst_write(0x60000000, rt5677->fw2);
+                       release_firmware(rt5677->fw2);
+               }
+
+               regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x1, 0x0);
+
+               regcache_cache_bypass(rt5677->regmap, false);
+               regcache_cache_only(rt5677->regmap, true);
+       } else if (!on && activity) {
+               activity = false;
+
+               regcache_cache_only(rt5677->regmap, false);
+               regcache_cache_bypass(rt5677->regmap, true);
+
+               regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x1, 0x1);
+               rt5677_set_dsp_mode(codec, false);
+               regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x0001);
+
+               regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
+
+               regcache_cache_bypass(rt5677->regmap, false);
+               regcache_mark_dirty(rt5677->regmap);
+               regcache_sync(rt5677->regmap);
+       }
+
+       return 0;
+}
+
 static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
-static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6525, 75, 0);
 static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
-static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0);
 static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
 static const DECLARE_TLV_DB_SCALE(st_vol_tlv, -4650, 150, 0);
 
@@ -556,6 +781,31 @@ static unsigned int bst_tlv[] = {
        8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
 };
 
+static int rt5677_dsp_vad_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+       ucontrol->value.integer.value[0] = rt5677->dsp_vad_en;
+
+       return 0;
+}
+
+static int rt5677_dsp_vad_put(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+       rt5677->dsp_vad_en = !!ucontrol->value.integer.value[0];
+
+       if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+               rt5677_set_dsp_vad(codec, rt5677->dsp_vad_en);
+
+       return 0;
+}
+
 static const struct snd_kcontrol_new rt5677_snd_controls[] = {
        /* OUTPUT Control */
        SOC_SINGLE("OUT1 Playback Switch", RT5677_LOUT1,
@@ -567,13 +817,13 @@ static const struct snd_kcontrol_new rt5677_snd_controls[] = {
 
        /* DAC Digital Volume */
        SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5677_DAC1_DIG_VOL,
-               RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 175, 0, dac_vol_tlv),
+               RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv),
        SOC_DOUBLE_TLV("DAC2 Playback Volume", RT5677_DAC2_DIG_VOL,
-               RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 175, 0, dac_vol_tlv),
+               RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv),
        SOC_DOUBLE_TLV("DAC3 Playback Volume", RT5677_DAC3_DIG_VOL,
-               RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 175, 0, dac_vol_tlv),
+               RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv),
        SOC_DOUBLE_TLV("DAC4 Playback Volume", RT5677_DAC4_DIG_VOL,
-               RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 175, 0, dac_vol_tlv),
+               RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv),
 
        /* IN1/IN2 Control */
        SOC_SINGLE_TLV("IN1 Boost", RT5677_IN1, RT5677_BST_SFT1, 8, 0, bst_tlv),
@@ -592,19 +842,19 @@ static const struct snd_kcontrol_new rt5677_snd_controls[] = {
                RT5677_L_MUTE_SFT, RT5677_R_MUTE_SFT, 1, 1),
 
        SOC_DOUBLE_TLV("ADC1 Capture Volume", RT5677_STO1_ADC_DIG_VOL,
-               RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 127, 0,
+               RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 63, 0,
                adc_vol_tlv),
        SOC_DOUBLE_TLV("ADC2 Capture Volume", RT5677_STO2_ADC_DIG_VOL,
-               RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 127, 0,
+               RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 63, 0,
                adc_vol_tlv),
        SOC_DOUBLE_TLV("ADC3 Capture Volume", RT5677_STO3_ADC_DIG_VOL,
-               RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 127, 0,
+               RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 63, 0,
                adc_vol_tlv),
        SOC_DOUBLE_TLV("ADC4 Capture Volume", RT5677_STO4_ADC_DIG_VOL,
-               RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 127, 0,
+               RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 63, 0,
                adc_vol_tlv),
        SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5677_MONO_ADC_DIG_VOL,
-               RT5677_MONO_ADC_L_VOL_SFT, RT5677_MONO_ADC_R_VOL_SFT, 127, 0,
+               RT5677_MONO_ADC_L_VOL_SFT, RT5677_MONO_ADC_R_VOL_SFT, 63, 0,
                adc_vol_tlv),
 
        /* Sidetone Control */
@@ -627,6 +877,9 @@ static const struct snd_kcontrol_new rt5677_snd_controls[] = {
        SOC_DOUBLE_TLV("Mono ADC Boost Volume", RT5677_ADC_BST_CTRL2,
                RT5677_MONO_ADC_L_BST_SFT, RT5677_MONO_ADC_R_BST_SFT, 3, 0,
                adc_bst_tlv),
+
+       SOC_SINGLE_EXT("DSP VAD Switch", SND_SOC_NOPM, 0, 1, 0,
+               rt5677_dsp_vad_get, rt5677_dsp_vad_put),
 };
 
 /**
@@ -1086,7 +1339,7 @@ static SOC_ENUM_SINGLE_DECL(
 static const struct snd_kcontrol_new rt5677_ib45_bypass_src_mux =
        SOC_DAPM_ENUM("IB45 Bypass Source", rt5677_ib45_bypass_src_enum);
 
-/* Stereo ADC Source 2 */ /* MX-27 MX26  MX25 [11:10] */
+/* Stereo ADC Source 2 */ /* MX-27 MX26 MX25 [11:10] */
 static const char * const rt5677_stereo_adc2_src[] = {
        "DD MIX1", "DMIC", "Stereo DAC MIX"
 };
@@ -1171,7 +1424,7 @@ static SOC_ENUM_SINGLE_DECL(
 static const struct snd_kcontrol_new rt5677_sto2_adc_lr_mux =
        SOC_DAPM_ENUM("Stereo2 ADC LR Source", rt5677_stereo2_adc_lr_enum);
 
-/* Stereo1 ADC Source 1 */ /* MX-27 MX26  MX25 [13:12] */
+/* Stereo1 ADC Source 1 */ /* MX-27 MX26 MX25 [13:12] */
 static const char * const rt5677_stereo_adc1_src[] = {
        "DD MIX1", "ADC1/2", "Stereo DAC MIX"
 };
@@ -1443,7 +1696,7 @@ static SOC_ENUM_SINGLE_DECL(
 static const struct snd_kcontrol_new rt5677_pdm2_r_mux =
        SOC_DAPM_ENUM("PDM2 Source", rt5677_pdm2_r_enum);
 
-/* TDM IF1/2 SLB ADC1 Data Selection */ /* MX-3C MX-41 [5:4] MX-08 [1:0]*/
+/* TDM IF1/2 SLB ADC1 Data Selection */ /* MX-3C MX-41 [5:4] MX-08 [1:0] */
 static const char * const rt5677_if12_adc1_src[] = {
        "STO1 ADC MIX", "OB01", "VAD ADC"
 };
@@ -1521,7 +1774,7 @@ static SOC_ENUM_SINGLE_DECL(
 static const struct snd_kcontrol_new rt5677_slb_adc3_mux =
        SOC_DAPM_ENUM("SLB ADC3 Source", rt5677_slb_adc3_enum);
 
-/* TDM IF1/2 SLB ADC4 Data Selection */ /* MX-3C MX-41 [11:10]  MX-08 [7:6] */
+/* TDM IF1/2 SLB ADC4 Data Selection */ /* MX-3C MX-41 [11:10] MX-08 [7:6] */
 static const char * const rt5677_if12_adc4_src[] = {
        "STO4 ADC MIX", "OB67", "OB01"
 };
@@ -1547,7 +1800,7 @@ static SOC_ENUM_SINGLE_DECL(
 static const struct snd_kcontrol_new rt5677_slb_adc4_mux =
        SOC_DAPM_ENUM("SLB ADC4 Source", rt5677_slb_adc4_enum);
 
-/* Interface3/4 ADC Data Input */ /* MX-2F [3:0] MX-30 [7:4]*/
+/* Interface3/4 ADC Data Input */ /* MX-2F [3:0] MX-30 [7:4] */
 static const char * const rt5677_if34_adc_src[] = {
        "STO1 ADC MIX", "STO2 ADC MIX", "STO3 ADC MIX", "STO4 ADC MIX",
        "MONO ADC MIX", "OB01", "OB23", "VAD ADC"
@@ -1567,6 +1820,213 @@ static SOC_ENUM_SINGLE_DECL(
 static const struct snd_kcontrol_new rt5677_if4_adc_mux =
        SOC_DAPM_ENUM("IF4 ADC Source", rt5677_if4_adc_enum);
 
+/* TDM IF1/2 ADC Data Selection */ /* MX-3B MX-40 [7:6][5:4][3:2][1:0] */
+static const char * const rt5677_if12_adc_swap_src[] = {
+       "L/R", "R/L", "L/L", "R/R"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_adc1_swap_enum, RT5677_TDM1_CTRL1,
+       RT5677_IF1_ADC1_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc1_swap_mux =
+       SOC_DAPM_ENUM("IF1 ADC1 Swap Source", rt5677_if1_adc1_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_adc2_swap_enum, RT5677_TDM1_CTRL1,
+       RT5677_IF1_ADC2_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc2_swap_mux =
+       SOC_DAPM_ENUM("IF1 ADC2 Swap Source", rt5677_if1_adc2_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_adc3_swap_enum, RT5677_TDM1_CTRL1,
+       RT5677_IF1_ADC3_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc3_swap_mux =
+       SOC_DAPM_ENUM("IF1 ADC3 Swap Source", rt5677_if1_adc3_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_adc4_swap_enum, RT5677_TDM1_CTRL1,
+       RT5677_IF1_ADC4_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc4_swap_mux =
+       SOC_DAPM_ENUM("IF1 ADC4 Swap Source", rt5677_if1_adc4_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_adc1_swap_enum, RT5677_TDM2_CTRL1,
+       RT5677_IF1_ADC2_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc1_swap_mux =
+       SOC_DAPM_ENUM("IF1 ADC2 Swap Source", rt5677_if2_adc1_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_adc2_swap_enum, RT5677_TDM2_CTRL1,
+       RT5677_IF2_ADC2_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc2_swap_mux =
+       SOC_DAPM_ENUM("IF2 ADC2 Swap Source", rt5677_if2_adc2_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_adc3_swap_enum, RT5677_TDM2_CTRL1,
+       RT5677_IF2_ADC3_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc3_swap_mux =
+       SOC_DAPM_ENUM("IF2 ADC3 Swap Source", rt5677_if2_adc3_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_adc4_swap_enum, RT5677_TDM2_CTRL1,
+       RT5677_IF2_ADC4_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc4_swap_mux =
+       SOC_DAPM_ENUM("IF2 ADC4 Swap Source", rt5677_if2_adc4_swap_enum);
+
+/* TDM IF1 ADC Data Selection */ /* MX-3C [2:0] */
+static const char * const rt5677_if1_adc_tdm_swap_src[] = {
+       "1/2/3/4", "2/1/3/4", "2/3/1/4", "4/1/2/3", "1/3/2/4", "1/4/2/3",
+       "3/1/2/4", "3/4/1/2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_adc_tdm_swap_enum, RT5677_TDM1_CTRL2,
+       RT5677_IF1_ADC_CTRL_SFT, rt5677_if1_adc_tdm_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc_tdm_swap_mux =
+       SOC_DAPM_ENUM("IF1 ADC TDM Swap Source", rt5677_if1_adc_tdm_swap_enum);
+
+/* TDM IF2 ADC Data Selection */ /* MX-41[2:0] */
+static const char * const rt5677_if2_adc_tdm_swap_src[] = {
+       "1/2/3/4", "2/1/3/4", "3/1/2/4", "4/1/2/3", "1/3/2/4", "1/4/2/3",
+       "2/3/1/4", "3/4/1/2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_adc_tdm_swap_enum, RT5677_TDM2_CTRL2,
+       RT5677_IF2_ADC_CTRL_SFT, rt5677_if2_adc_tdm_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc_tdm_swap_mux =
+       SOC_DAPM_ENUM("IF2 ADC TDM Swap Source", rt5677_if2_adc_tdm_swap_enum);
+
+/* TDM IF1/2 DAC Data Selection */ /* MX-3E[14:12][10:8][6:4][2:0]
+                                       MX-3F[14:12][10:8][6:4][2:0]
+                                       MX-43[14:12][10:8][6:4][2:0]
+                                       MX-44[14:12][10:8][6:4][2:0] */
+static const char * const rt5677_if12_dac_tdm_sel_src[] = {
+       "Slot0", "Slot1", "Slot2", "Slot3", "Slot4", "Slot5", "Slot6", "Slot7"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_dac0_tdm_sel_enum, RT5677_TDM1_CTRL4,
+       RT5677_IF1_DAC0_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac0_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF1 DAC0 TDM Source", rt5677_if1_dac0_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_dac1_tdm_sel_enum, RT5677_TDM1_CTRL4,
+       RT5677_IF1_DAC1_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac1_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF1 DAC1 TDM Source", rt5677_if1_dac1_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_dac2_tdm_sel_enum, RT5677_TDM1_CTRL4,
+       RT5677_IF1_DAC2_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac2_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF1 DAC2 TDM Source", rt5677_if1_dac2_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_dac3_tdm_sel_enum, RT5677_TDM1_CTRL4,
+       RT5677_IF1_DAC3_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac3_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF1 DAC3 TDM Source", rt5677_if1_dac3_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_dac4_tdm_sel_enum, RT5677_TDM1_CTRL5,
+       RT5677_IF1_DAC4_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac4_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF1 DAC4 TDM Source", rt5677_if1_dac4_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_dac5_tdm_sel_enum, RT5677_TDM1_CTRL5,
+       RT5677_IF1_DAC5_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac5_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF1 DAC5 TDM Source", rt5677_if1_dac5_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_dac6_tdm_sel_enum, RT5677_TDM1_CTRL5,
+       RT5677_IF1_DAC6_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac6_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF1 DAC6 TDM Source", rt5677_if1_dac6_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_dac7_tdm_sel_enum, RT5677_TDM1_CTRL5,
+       RT5677_IF1_DAC7_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac7_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF1 DAC7 TDM Source", rt5677_if1_dac7_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_dac0_tdm_sel_enum, RT5677_TDM2_CTRL4,
+       RT5677_IF2_DAC0_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac0_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF2 DAC0 TDM Source", rt5677_if2_dac0_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_dac1_tdm_sel_enum, RT5677_TDM2_CTRL4,
+       RT5677_IF2_DAC1_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac1_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF2 DAC1 TDM Source", rt5677_if2_dac1_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_dac2_tdm_sel_enum, RT5677_TDM2_CTRL4,
+       RT5677_IF2_DAC2_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac2_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF2 DAC2 TDM Source", rt5677_if2_dac2_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_dac3_tdm_sel_enum, RT5677_TDM2_CTRL4,
+       RT5677_IF2_DAC3_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac3_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF2 DAC3 TDM Source", rt5677_if2_dac3_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_dac4_tdm_sel_enum, RT5677_TDM2_CTRL5,
+       RT5677_IF2_DAC4_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac4_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF2 DAC4 TDM Source", rt5677_if2_dac4_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_dac5_tdm_sel_enum, RT5677_TDM2_CTRL5,
+       RT5677_IF2_DAC5_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac5_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF2 DAC5 TDM Source", rt5677_if2_dac5_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_dac6_tdm_sel_enum, RT5677_TDM2_CTRL5,
+       RT5677_IF2_DAC6_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac6_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF2 DAC6 TDM Source", rt5677_if2_dac6_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_dac7_tdm_sel_enum, RT5677_TDM2_CTRL5,
+       RT5677_IF2_DAC7_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac7_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF2 DAC7 TDM Source", rt5677_if2_dac7_tdm_sel_enum);
+
 static int rt5677_bst1_event(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
 {
@@ -1678,6 +2138,77 @@ static int rt5677_set_micbias1_event(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
+static int rt5677_if1_adc_tdm_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+       unsigned int value;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               regmap_read(rt5677->regmap, RT5677_TDM1_CTRL2, &value);
+               if (value & RT5677_IF1_ADC_CTRL_MASK)
+                       regmap_update_bits(rt5677->regmap, RT5677_TDM1_CTRL1,
+                               RT5677_IF1_ADC_MODE_MASK,
+                               RT5677_IF1_ADC_MODE_TDM);
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt5677_if2_adc_tdm_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+       unsigned int value;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               regmap_read(rt5677->regmap, RT5677_TDM2_CTRL2, &value);
+               if (value & RT5677_IF2_ADC_CTRL_MASK)
+                       regmap_update_bits(rt5677->regmap, RT5677_TDM2_CTRL1,
+                               RT5677_IF2_ADC_MODE_MASK,
+                               RT5677_IF2_ADC_MODE_TDM);
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt5677_vref_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               if (codec->dapm.bias_level != SND_SOC_BIAS_ON &&
+                       !rt5677->is_vref_slow) {
+                       mdelay(20);
+                       regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
+                               RT5677_PWR_FV1 | RT5677_PWR_FV2,
+                               RT5677_PWR_FV1 | RT5677_PWR_FV2);
+                       rt5677->is_vref_slow = true;
+               }
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
 static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
        SND_SOC_DAPM_SUPPLY("PLL1", RT5677_PWR_ANLG2, RT5677_PWR_PLL1_BIT,
                0, rt5677_set_pll1_event, SND_SOC_DAPM_POST_PMU),
@@ -1837,10 +2368,8 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
        SND_SOC_DAPM_PGA("Stereo4 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
        SND_SOC_DAPM_PGA("Sto2 ADC LR MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
        SND_SOC_DAPM_PGA("Mono ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("IF1_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("IF1_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("IF1_ADC3", SND_SOC_NOPM, 0, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("IF1_ADC4", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
 
        /* DSP */
        SND_SOC_DAPM_MUX("IB9 Mux", SND_SOC_NOPM, 0, 0,
@@ -1963,6 +2492,17 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
                        &rt5677_if1_adc3_mux),
        SND_SOC_DAPM_MUX("IF1 ADC4 Mux", SND_SOC_NOPM, 0, 0,
                        &rt5677_if1_adc4_mux),
+       SND_SOC_DAPM_MUX("IF1 ADC1 Swap Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_adc1_swap_mux),
+       SND_SOC_DAPM_MUX("IF1 ADC2 Swap Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_adc2_swap_mux),
+       SND_SOC_DAPM_MUX("IF1 ADC3 Swap Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_adc3_swap_mux),
+       SND_SOC_DAPM_MUX("IF1 ADC4 Swap Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_adc4_swap_mux),
+       SND_SOC_DAPM_MUX_E("IF1 ADC TDM Swap Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_adc_tdm_swap_mux, rt5677_if1_adc_tdm_event,
+                       SND_SOC_DAPM_PRE_PMU),
        SND_SOC_DAPM_MUX("IF2 ADC1 Mux", SND_SOC_NOPM, 0, 0,
                        &rt5677_if2_adc1_mux),
        SND_SOC_DAPM_MUX("IF2 ADC2 Mux", SND_SOC_NOPM, 0, 0,
@@ -1971,6 +2511,17 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
                        &rt5677_if2_adc3_mux),
        SND_SOC_DAPM_MUX("IF2 ADC4 Mux", SND_SOC_NOPM, 0, 0,
                        &rt5677_if2_adc4_mux),
+       SND_SOC_DAPM_MUX("IF2 ADC1 Swap Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_adc1_swap_mux),
+       SND_SOC_DAPM_MUX("IF2 ADC2 Swap Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_adc2_swap_mux),
+       SND_SOC_DAPM_MUX("IF2 ADC3 Swap Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_adc3_swap_mux),
+       SND_SOC_DAPM_MUX("IF2 ADC4 Swap Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_adc4_swap_mux),
+       SND_SOC_DAPM_MUX_E("IF2 ADC TDM Swap Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_adc_tdm_swap_mux, rt5677_if2_adc_tdm_event,
+                       SND_SOC_DAPM_PRE_PMU),
        SND_SOC_DAPM_MUX("IF3 ADC Mux", SND_SOC_NOPM, 0, 0,
                        &rt5677_if3_adc_mux),
        SND_SOC_DAPM_MUX("IF4 ADC Mux", SND_SOC_NOPM, 0, 0,
@@ -1984,6 +2535,40 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
        SND_SOC_DAPM_MUX("SLB ADC4 Mux", SND_SOC_NOPM, 0, 0,
                        &rt5677_slb_adc4_mux),
 
+       SND_SOC_DAPM_MUX("IF1 DAC0 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_dac0_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF1 DAC1 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_dac1_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF1 DAC2 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_dac2_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF1 DAC3 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_dac3_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF1 DAC4 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_dac4_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF1 DAC5 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_dac5_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF1 DAC6 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_dac6_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF1 DAC7 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_dac7_tdm_sel_mux),
+
+       SND_SOC_DAPM_MUX("IF2 DAC0 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_dac0_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF2 DAC1 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_dac1_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF2 DAC2 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_dac2_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF2 DAC3 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_dac3_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF2 DAC4 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_dac4_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF2 DAC5 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_dac5_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF2 DAC6 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_dac6_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF2 DAC7 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_dac7_tdm_sel_mux),
+
        /* Audio Interface */
        SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
        SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
@@ -2022,7 +2607,7 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
                rt5677_ob_7_mix, ARRAY_SIZE(rt5677_ob_7_mix)),
 
        /* Output Side */
-       /* DAC mixer before sound effect  */
+       /* DAC mixer before sound effect */
        SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0,
                rt5677_dac_l_mix, ARRAY_SIZE(rt5677_dac_l_mix)),
        SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0,
@@ -2109,13 +2694,20 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
        SND_SOC_DAPM_MUX("PDM2 R Mux", RT5677_PDM_OUT_CTRL, RT5677_M_PDM2_R_SFT,
                1, &rt5677_pdm2_r_mux),
 
-       SND_SOC_DAPM_PGA_S("LOUT1 amp", 1, RT5677_PWR_ANLG1, RT5677_PWR_LO1_BIT,
+       SND_SOC_DAPM_PGA_S("LOUT1 amp", 0, RT5677_PWR_ANLG1, RT5677_PWR_LO1_BIT,
                0, NULL, 0),
-       SND_SOC_DAPM_PGA_S("LOUT2 amp", 1, RT5677_PWR_ANLG1, RT5677_PWR_LO2_BIT,
+       SND_SOC_DAPM_PGA_S("LOUT2 amp", 0, RT5677_PWR_ANLG1, RT5677_PWR_LO2_BIT,
                0, NULL, 0),
-       SND_SOC_DAPM_PGA_S("LOUT3 amp", 1, RT5677_PWR_ANLG1, RT5677_PWR_LO3_BIT,
+       SND_SOC_DAPM_PGA_S("LOUT3 amp", 0, RT5677_PWR_ANLG1, RT5677_PWR_LO3_BIT,
                0, NULL, 0),
 
+       SND_SOC_DAPM_PGA_S("LOUT1 vref", 1, SND_SOC_NOPM, 0, 0,
+               rt5677_vref_event, SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_PGA_S("LOUT2 vref", 1, SND_SOC_NOPM, 0, 0,
+               rt5677_vref_event, SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_PGA_S("LOUT3 vref", 1, SND_SOC_NOPM, 0, 0,
+               rt5677_vref_event, SND_SOC_DAPM_POST_PMU),
+
        /* Output Lines */
        SND_SOC_DAPM_OUTPUT("LOUT1"),
        SND_SOC_DAPM_OUTPUT("LOUT2"),
@@ -2124,6 +2716,8 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
        SND_SOC_DAPM_OUTPUT("PDM1R"),
        SND_SOC_DAPM_OUTPUT("PDM2L"),
        SND_SOC_DAPM_OUTPUT("PDM2R"),
+
+       SND_SOC_DAPM_POST("vref", rt5677_vref_event),
 };
 
 static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
@@ -2354,11 +2948,42 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
        { "IF1 ADC4 Mux", "OB67", "OB67" },
        { "IF1 ADC4 Mux", "OB01", "OB01 Bypass Mux" },
 
+       { "IF1 ADC1 Swap Mux", "L/R", "IF1 ADC1 Mux" },
+       { "IF1 ADC1 Swap Mux", "R/L", "IF1 ADC1 Mux" },
+       { "IF1 ADC1 Swap Mux", "L/L", "IF1 ADC1 Mux" },
+       { "IF1 ADC1 Swap Mux", "R/R", "IF1 ADC1 Mux" },
+
+       { "IF1 ADC2 Swap Mux", "L/R", "IF1 ADC2 Mux" },
+       { "IF1 ADC2 Swap Mux", "R/L", "IF1 ADC2 Mux" },
+       { "IF1 ADC2 Swap Mux", "L/L", "IF1 ADC2 Mux" },
+       { "IF1 ADC2 Swap Mux", "R/R", "IF1 ADC2 Mux" },
+
+       { "IF1 ADC3 Swap Mux", "L/R", "IF1 ADC3 Mux" },
+       { "IF1 ADC3 Swap Mux", "R/L", "IF1 ADC3 Mux" },
+       { "IF1 ADC3 Swap Mux", "L/L", "IF1 ADC3 Mux" },
+       { "IF1 ADC3 Swap Mux", "R/R", "IF1 ADC3 Mux" },
+
+       { "IF1 ADC4 Swap Mux", "L/R", "IF1 ADC4 Mux" },
+       { "IF1 ADC4 Swap Mux", "R/L", "IF1 ADC4 Mux" },
+       { "IF1 ADC4 Swap Mux", "L/L", "IF1 ADC4 Mux" },
+       { "IF1 ADC4 Swap Mux", "R/R", "IF1 ADC4 Mux" },
+
+       { "IF1 ADC", NULL, "IF1 ADC1 Swap Mux" },
+       { "IF1 ADC", NULL, "IF1 ADC2 Swap Mux" },
+       { "IF1 ADC", NULL, "IF1 ADC3 Swap Mux" },
+       { "IF1 ADC", NULL, "IF1 ADC4 Swap Mux" },
+
+       { "IF1 ADC TDM Swap Mux", "1/2/3/4", "IF1 ADC" },
+       { "IF1 ADC TDM Swap Mux", "2/1/3/4", "IF1 ADC" },
+       { "IF1 ADC TDM Swap Mux", "2/3/1/4", "IF1 ADC" },
+       { "IF1 ADC TDM Swap Mux", "4/1/2/3", "IF1 ADC" },
+       { "IF1 ADC TDM Swap Mux", "1/3/2/4", "IF1 ADC" },
+       { "IF1 ADC TDM Swap Mux", "1/4/2/3", "IF1 ADC" },
+       { "IF1 ADC TDM Swap Mux", "3/1/2/4", "IF1 ADC" },
+       { "IF1 ADC TDM Swap Mux", "3/4/1/2", "IF1 ADC" },
+
        { "AIF1TX", NULL, "I2S1" },
-       { "AIF1TX", NULL, "IF1 ADC1 Mux" },
-       { "AIF1TX", NULL, "IF1 ADC2 Mux" },
-       { "AIF1TX", NULL, "IF1 ADC3 Mux" },
-       { "AIF1TX", NULL, "IF1 ADC4 Mux" },
+       { "AIF1TX", NULL, "IF1 ADC TDM Swap Mux" },
 
        { "IF2 ADC1 Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" },
        { "IF2 ADC1 Mux", "OB01", "OB01 Bypass Mux" },
@@ -2375,11 +3000,42 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
        { "IF2 ADC4 Mux", "OB67", "OB67" },
        { "IF2 ADC4 Mux", "OB01", "OB01 Bypass Mux" },
 
+       { "IF2 ADC1 Swap Mux", "L/R", "IF2 ADC1 Mux" },
+       { "IF2 ADC1 Swap Mux", "R/L", "IF2 ADC1 Mux" },
+       { "IF2 ADC1 Swap Mux", "L/L", "IF2 ADC1 Mux" },
+       { "IF2 ADC1 Swap Mux", "R/R", "IF2 ADC1 Mux" },
+
+       { "IF2 ADC2 Swap Mux", "L/R", "IF2 ADC2 Mux" },
+       { "IF2 ADC2 Swap Mux", "R/L", "IF2 ADC2 Mux" },
+       { "IF2 ADC2 Swap Mux", "L/L", "IF2 ADC2 Mux" },
+       { "IF2 ADC2 Swap Mux", "R/R", "IF2 ADC2 Mux" },
+
+       { "IF2 ADC3 Swap Mux", "L/R", "IF2 ADC3 Mux" },
+       { "IF2 ADC3 Swap Mux", "R/L", "IF2 ADC3 Mux" },
+       { "IF2 ADC3 Swap Mux", "L/L", "IF2 ADC3 Mux" },
+       { "IF2 ADC3 Swap Mux", "R/R", "IF2 ADC3 Mux" },
+
+       { "IF2 ADC4 Swap Mux", "L/R", "IF2 ADC4 Mux" },
+       { "IF2 ADC4 Swap Mux", "R/L", "IF2 ADC4 Mux" },
+       { "IF2 ADC4 Swap Mux", "L/L", "IF2 ADC4 Mux" },
+       { "IF2 ADC4 Swap Mux", "R/R", "IF2 ADC4 Mux" },
+
+       { "IF2 ADC", NULL, "IF2 ADC1 Swap Mux" },
+       { "IF2 ADC", NULL, "IF2 ADC2 Swap Mux" },
+       { "IF2 ADC", NULL, "IF2 ADC3 Swap Mux" },
+       { "IF2 ADC", NULL, "IF2 ADC4 Swap Mux" },
+
+       { "IF2 ADC TDM Swap Mux", "1/2/3/4", "IF2 ADC" },
+       { "IF2 ADC TDM Swap Mux", "2/1/3/4", "IF2 ADC" },
+       { "IF2 ADC TDM Swap Mux", "3/1/2/4", "IF2 ADC" },
+       { "IF2 ADC TDM Swap Mux", "4/1/2/3", "IF2 ADC" },
+       { "IF2 ADC TDM Swap Mux", "1/3/2/4", "IF2 ADC" },
+       { "IF2 ADC TDM Swap Mux", "1/4/2/3", "IF2 ADC" },
+       { "IF2 ADC TDM Swap Mux", "2/3/1/4", "IF2 ADC" },
+       { "IF2 ADC TDM Swap Mux", "3/4/1/2", "IF2 ADC" },
+
        { "AIF2TX", NULL, "I2S2" },
-       { "AIF2TX", NULL, "IF2 ADC1 Mux" },
-       { "AIF2TX", NULL, "IF2 ADC2 Mux" },
-       { "AIF2TX", NULL, "IF2 ADC3 Mux" },
-       { "AIF2TX", NULL, "IF2 ADC4 Mux" },
+       { "AIF2TX", NULL, "IF2 ADC TDM Swap Mux" },
 
        { "IF3 ADC Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" },
        { "IF3 ADC Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" },
@@ -2569,14 +3225,86 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
        { "IF1 DAC6", NULL, "I2S1" },
        { "IF1 DAC7", NULL, "I2S1" },
 
-       { "IF1 DAC01", NULL, "IF1 DAC0" },
-       { "IF1 DAC01", NULL, "IF1 DAC1" },
-       { "IF1 DAC23", NULL, "IF1 DAC2" },
-       { "IF1 DAC23", NULL, "IF1 DAC3" },
-       { "IF1 DAC45", NULL, "IF1 DAC4" },
-       { "IF1 DAC45", NULL, "IF1 DAC5" },
-       { "IF1 DAC67", NULL, "IF1 DAC6" },
-       { "IF1 DAC67", NULL, "IF1 DAC7" },
+       { "IF1 DAC0 Mux", "Slot0", "IF1 DAC0" },
+       { "IF1 DAC0 Mux", "Slot1", "IF1 DAC1" },
+       { "IF1 DAC0 Mux", "Slot2", "IF1 DAC2" },
+       { "IF1 DAC0 Mux", "Slot3", "IF1 DAC3" },
+       { "IF1 DAC0 Mux", "Slot4", "IF1 DAC4" },
+       { "IF1 DAC0 Mux", "Slot5", "IF1 DAC5" },
+       { "IF1 DAC0 Mux", "Slot6", "IF1 DAC6" },
+       { "IF1 DAC0 Mux", "Slot7", "IF1 DAC7" },
+
+       { "IF1 DAC1 Mux", "Slot0", "IF1 DAC0" },
+       { "IF1 DAC1 Mux", "Slot1", "IF1 DAC1" },
+       { "IF1 DAC1 Mux", "Slot2", "IF1 DAC2" },
+       { "IF1 DAC1 Mux", "Slot3", "IF1 DAC3" },
+       { "IF1 DAC1 Mux", "Slot4", "IF1 DAC4" },
+       { "IF1 DAC1 Mux", "Slot5", "IF1 DAC5" },
+       { "IF1 DAC1 Mux", "Slot6", "IF1 DAC6" },
+       { "IF1 DAC1 Mux", "Slot7", "IF1 DAC7" },
+
+       { "IF1 DAC2 Mux", "Slot0", "IF1 DAC0" },
+       { "IF1 DAC2 Mux", "Slot1", "IF1 DAC1" },
+       { "IF1 DAC2 Mux", "Slot2", "IF1 DAC2" },
+       { "IF1 DAC2 Mux", "Slot3", "IF1 DAC3" },
+       { "IF1 DAC2 Mux", "Slot4", "IF1 DAC4" },
+       { "IF1 DAC2 Mux", "Slot5", "IF1 DAC5" },
+       { "IF1 DAC2 Mux", "Slot6", "IF1 DAC6" },
+       { "IF1 DAC2 Mux", "Slot7", "IF1 DAC7" },
+
+       { "IF1 DAC3 Mux", "Slot0", "IF1 DAC0" },
+       { "IF1 DAC3 Mux", "Slot1", "IF1 DAC1" },
+       { "IF1 DAC3 Mux", "Slot2", "IF1 DAC2" },
+       { "IF1 DAC3 Mux", "Slot3", "IF1 DAC3" },
+       { "IF1 DAC3 Mux", "Slot4", "IF1 DAC4" },
+       { "IF1 DAC3 Mux", "Slot5", "IF1 DAC5" },
+       { "IF1 DAC3 Mux", "Slot6", "IF1 DAC6" },
+       { "IF1 DAC3 Mux", "Slot7", "IF1 DAC7" },
+
+       { "IF1 DAC4 Mux", "Slot0", "IF1 DAC0" },
+       { "IF1 DAC4 Mux", "Slot1", "IF1 DAC1" },
+       { "IF1 DAC4 Mux", "Slot2", "IF1 DAC2" },
+       { "IF1 DAC4 Mux", "Slot3", "IF1 DAC3" },
+       { "IF1 DAC4 Mux", "Slot4", "IF1 DAC4" },
+       { "IF1 DAC4 Mux", "Slot5", "IF1 DAC5" },
+       { "IF1 DAC4 Mux", "Slot6", "IF1 DAC6" },
+       { "IF1 DAC4 Mux", "Slot7", "IF1 DAC7" },
+
+       { "IF1 DAC5 Mux", "Slot0", "IF1 DAC0" },
+       { "IF1 DAC5 Mux", "Slot1", "IF1 DAC1" },
+       { "IF1 DAC5 Mux", "Slot2", "IF1 DAC2" },
+       { "IF1 DAC5 Mux", "Slot3", "IF1 DAC3" },
+       { "IF1 DAC5 Mux", "Slot4", "IF1 DAC4" },
+       { "IF1 DAC5 Mux", "Slot5", "IF1 DAC5" },
+       { "IF1 DAC5 Mux", "Slot6", "IF1 DAC6" },
+       { "IF1 DAC5 Mux", "Slot7", "IF1 DAC7" },
+
+       { "IF1 DAC6 Mux", "Slot0", "IF1 DAC0" },
+       { "IF1 DAC6 Mux", "Slot1", "IF1 DAC1" },
+       { "IF1 DAC6 Mux", "Slot2", "IF1 DAC2" },
+       { "IF1 DAC6 Mux", "Slot3", "IF1 DAC3" },
+       { "IF1 DAC6 Mux", "Slot4", "IF1 DAC4" },
+       { "IF1 DAC6 Mux", "Slot5", "IF1 DAC5" },
+       { "IF1 DAC6 Mux", "Slot6", "IF1 DAC6" },
+       { "IF1 DAC6 Mux", "Slot7", "IF1 DAC7" },
+
+       { "IF1 DAC7 Mux", "Slot0", "IF1 DAC0" },
+       { "IF1 DAC7 Mux", "Slot1", "IF1 DAC1" },
+       { "IF1 DAC7 Mux", "Slot2", "IF1 DAC2" },
+       { "IF1 DAC7 Mux", "Slot3", "IF1 DAC3" },
+       { "IF1 DAC7 Mux", "Slot4", "IF1 DAC4" },
+       { "IF1 DAC7 Mux", "Slot5", "IF1 DAC5" },
+       { "IF1 DAC7 Mux", "Slot6", "IF1 DAC6" },
+       { "IF1 DAC7 Mux", "Slot7", "IF1 DAC7" },
+
+       { "IF1 DAC01", NULL, "IF1 DAC0 Mux" },
+       { "IF1 DAC01", NULL, "IF1 DAC1 Mux" },
+       { "IF1 DAC23", NULL, "IF1 DAC2 Mux" },
+       { "IF1 DAC23", NULL, "IF1 DAC3 Mux" },
+       { "IF1 DAC45", NULL, "IF1 DAC4 Mux" },
+       { "IF1 DAC45", NULL, "IF1 DAC5 Mux" },
+       { "IF1 DAC67", NULL, "IF1 DAC6 Mux" },
+       { "IF1 DAC67", NULL, "IF1 DAC7 Mux" },
 
        { "IF2 DAC0", NULL, "AIF2RX" },
        { "IF2 DAC1", NULL, "AIF2RX" },
@@ -2595,14 +3323,86 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
        { "IF2 DAC6", NULL, "I2S2" },
        { "IF2 DAC7", NULL, "I2S2" },
 
-       { "IF2 DAC01", NULL, "IF2 DAC0" },
-       { "IF2 DAC01", NULL, "IF2 DAC1" },
-       { "IF2 DAC23", NULL, "IF2 DAC2" },
-       { "IF2 DAC23", NULL, "IF2 DAC3" },
-       { "IF2 DAC45", NULL, "IF2 DAC4" },
-       { "IF2 DAC45", NULL, "IF2 DAC5" },
-       { "IF2 DAC67", NULL, "IF2 DAC6" },
-       { "IF2 DAC67", NULL, "IF2 DAC7" },
+       { "IF2 DAC0 Mux", "Slot0", "IF2 DAC0" },
+       { "IF2 DAC0 Mux", "Slot1", "IF2 DAC1" },
+       { "IF2 DAC0 Mux", "Slot2", "IF2 DAC2" },
+       { "IF2 DAC0 Mux", "Slot3", "IF2 DAC3" },
+       { "IF2 DAC0 Mux", "Slot4", "IF2 DAC4" },
+       { "IF2 DAC0 Mux", "Slot5", "IF2 DAC5" },
+       { "IF2 DAC0 Mux", "Slot6", "IF2 DAC6" },
+       { "IF2 DAC0 Mux", "Slot7", "IF2 DAC7" },
+
+       { "IF2 DAC1 Mux", "Slot0", "IF2 DAC0" },
+       { "IF2 DAC1 Mux", "Slot1", "IF2 DAC1" },
+       { "IF2 DAC1 Mux", "Slot2", "IF2 DAC2" },
+       { "IF2 DAC1 Mux", "Slot3", "IF2 DAC3" },
+       { "IF2 DAC1 Mux", "Slot4", "IF2 DAC4" },
+       { "IF2 DAC1 Mux", "Slot5", "IF2 DAC5" },
+       { "IF2 DAC1 Mux", "Slot6", "IF2 DAC6" },
+       { "IF2 DAC1 Mux", "Slot7", "IF2 DAC7" },
+
+       { "IF2 DAC2 Mux", "Slot0", "IF2 DAC0" },
+       { "IF2 DAC2 Mux", "Slot1", "IF2 DAC1" },
+       { "IF2 DAC2 Mux", "Slot2", "IF2 DAC2" },
+       { "IF2 DAC2 Mux", "Slot3", "IF2 DAC3" },
+       { "IF2 DAC2 Mux", "Slot4", "IF2 DAC4" },
+       { "IF2 DAC2 Mux", "Slot5", "IF2 DAC5" },
+       { "IF2 DAC2 Mux", "Slot6", "IF2 DAC6" },
+       { "IF2 DAC2 Mux", "Slot7", "IF2 DAC7" },
+
+       { "IF2 DAC3 Mux", "Slot0", "IF2 DAC0" },
+       { "IF2 DAC3 Mux", "Slot1", "IF2 DAC1" },
+       { "IF2 DAC3 Mux", "Slot2", "IF2 DAC2" },
+       { "IF2 DAC3 Mux", "Slot3", "IF2 DAC3" },
+       { "IF2 DAC3 Mux", "Slot4", "IF2 DAC4" },
+       { "IF2 DAC3 Mux", "Slot5", "IF2 DAC5" },
+       { "IF2 DAC3 Mux", "Slot6", "IF2 DAC6" },
+       { "IF2 DAC3 Mux", "Slot7", "IF2 DAC7" },
+
+       { "IF2 DAC4 Mux", "Slot0", "IF2 DAC0" },
+       { "IF2 DAC4 Mux", "Slot1", "IF2 DAC1" },
+       { "IF2 DAC4 Mux", "Slot2", "IF2 DAC2" },
+       { "IF2 DAC4 Mux", "Slot3", "IF2 DAC3" },
+       { "IF2 DAC4 Mux", "Slot4", "IF2 DAC4" },
+       { "IF2 DAC4 Mux", "Slot5", "IF2 DAC5" },
+       { "IF2 DAC4 Mux", "Slot6", "IF2 DAC6" },
+       { "IF2 DAC4 Mux", "Slot7", "IF2 DAC7" },
+
+       { "IF2 DAC5 Mux", "Slot0", "IF2 DAC0" },
+       { "IF2 DAC5 Mux", "Slot1", "IF2 DAC1" },
+       { "IF2 DAC5 Mux", "Slot2", "IF2 DAC2" },
+       { "IF2 DAC5 Mux", "Slot3", "IF2 DAC3" },
+       { "IF2 DAC5 Mux", "Slot4", "IF2 DAC4" },
+       { "IF2 DAC5 Mux", "Slot5", "IF2 DAC5" },
+       { "IF2 DAC5 Mux", "Slot6", "IF2 DAC6" },
+       { "IF2 DAC5 Mux", "Slot7", "IF2 DAC7" },
+
+       { "IF2 DAC6 Mux", "Slot0", "IF2 DAC0" },
+       { "IF2 DAC6 Mux", "Slot1", "IF2 DAC1" },
+       { "IF2 DAC6 Mux", "Slot2", "IF2 DAC2" },
+       { "IF2 DAC6 Mux", "Slot3", "IF2 DAC3" },
+       { "IF2 DAC6 Mux", "Slot4", "IF2 DAC4" },
+       { "IF2 DAC6 Mux", "Slot5", "IF2 DAC5" },
+       { "IF2 DAC6 Mux", "Slot6", "IF2 DAC6" },
+       { "IF2 DAC6 Mux", "Slot7", "IF2 DAC7" },
+
+       { "IF2 DAC7 Mux", "Slot0", "IF2 DAC0" },
+       { "IF2 DAC7 Mux", "Slot1", "IF2 DAC1" },
+       { "IF2 DAC7 Mux", "Slot2", "IF2 DAC2" },
+       { "IF2 DAC7 Mux", "Slot3", "IF2 DAC3" },
+       { "IF2 DAC7 Mux", "Slot4", "IF2 DAC4" },
+       { "IF2 DAC7 Mux", "Slot5", "IF2 DAC5" },
+       { "IF2 DAC7 Mux", "Slot6", "IF2 DAC6" },
+       { "IF2 DAC7 Mux", "Slot7", "IF2 DAC7" },
+
+       { "IF2 DAC01", NULL, "IF2 DAC0 Mux" },
+       { "IF2 DAC01", NULL, "IF2 DAC1 Mux" },
+       { "IF2 DAC23", NULL, "IF2 DAC2 Mux" },
+       { "IF2 DAC23", NULL, "IF2 DAC3 Mux" },
+       { "IF2 DAC45", NULL, "IF2 DAC4 Mux" },
+       { "IF2 DAC45", NULL, "IF2 DAC5 Mux" },
+       { "IF2 DAC67", NULL, "IF2 DAC6 Mux" },
+       { "IF2 DAC67", NULL, "IF2 DAC7 Mux" },
 
        { "IF3 DAC", NULL, "AIF3RX" },
        { "IF3 DAC", NULL, "I2S3" },
@@ -2806,9 +3606,13 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
        { "LOUT2 amp", NULL, "DAC 2" },
        { "LOUT3 amp", NULL, "DAC 3" },
 
-       { "LOUT1", NULL, "LOUT1 amp" },
-       { "LOUT2", NULL, "LOUT2 amp" },
-       { "LOUT3", NULL, "LOUT3 amp" },
+       { "LOUT1 vref", NULL, "LOUT1 amp" },
+       { "LOUT2 vref", NULL, "LOUT2 amp" },
+       { "LOUT3 vref", NULL, "LOUT3 amp" },
+
+       { "LOUT1", NULL, "LOUT1 vref" },
+       { "LOUT2", NULL, "LOUT2 vref" },
+       { "LOUT3", NULL, "LOUT3 vref" },
 
        { "PDM1L", NULL, "PDM1 L Mux" },
        { "PDM1R", NULL, "PDM1 R Mux" },
@@ -2837,7 +3641,8 @@ static int rt5677_hw_params(struct snd_pcm_substream *substream,
        rt5677->lrck[dai->id] = params_rate(params);
        pre_div = rl6231_get_clk_info(rt5677->sysclk, rt5677->lrck[dai->id]);
        if (pre_div < 0) {
-               dev_err(codec->dev, "Unsupported clock setting\n");
+               dev_err(codec->dev, "Unsupported clock setting: sysclk=%dHz lrck=%dHz\n",
+                       rt5677->sysclk, rt5677->lrck[dai->id]);
                return -EINVAL;
        }
        frame_size = snd_soc_params_to_frame_size(params);
@@ -3181,6 +3986,8 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec,
 
        case SND_SOC_BIAS_PREPARE:
                if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
+                       rt5677_set_dsp_vad(codec, false);
+
                        regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
                                RT5677_LDO1_SEL_MASK | RT5677_LDO2_SEL_MASK,
                                0x0055);
@@ -3188,14 +3995,12 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec,
                                RT5677_PR_BASE + RT5677_BIAS_CUR4,
                                0x0f00, 0x0f00);
                        regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
+                               RT5677_PWR_FV1 | RT5677_PWR_FV2 |
                                RT5677_PWR_VREF1 | RT5677_PWR_MB |
                                RT5677_PWR_BG | RT5677_PWR_VREF2,
                                RT5677_PWR_VREF1 | RT5677_PWR_MB |
                                RT5677_PWR_BG | RT5677_PWR_VREF2);
-                       mdelay(20);
-                       regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
-                               RT5677_PWR_FV1 | RT5677_PWR_FV2,
-                               RT5677_PWR_FV1 | RT5677_PWR_FV2);
+                       rt5677->is_vref_slow = false;
                        regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
                                RT5677_PWR_CORE, RT5677_PWR_CORE);
                        regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC,
@@ -3214,6 +4019,9 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec,
                regmap_write(rt5677->regmap, RT5677_PWR_ANLG2, 0x0000);
                regmap_update_bits(rt5677->regmap,
                        RT5677_PR_BASE + RT5677_BIAS_CUR4, 0x0f00, 0x0000);
+
+               if (rt5677->dsp_vad_en)
+                       rt5677_set_dsp_vad(codec, true);
                break;
 
        default:
@@ -3309,6 +4117,78 @@ static int rt5677_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
        return 0;
 }
 
+/** Configures the gpio as
+ *   0 - floating
+ *   1 - pull down
+ *   2 - pull up
+ */
+static void rt5677_gpio_config(struct rt5677_priv *rt5677, unsigned offset,
+               int value)
+{
+       int shift;
+
+       switch (offset) {
+       case RT5677_GPIO1 ... RT5677_GPIO2:
+               shift = 2 * (1 - offset);
+               regmap_update_bits(rt5677->regmap,
+                       RT5677_PR_BASE + RT5677_DIG_IN_PIN_ST_CTRL2,
+                       0x3 << shift,
+                       (value & 0x3) << shift);
+               break;
+
+       case RT5677_GPIO3 ... RT5677_GPIO6:
+               shift = 2 * (9 - offset);
+               regmap_update_bits(rt5677->regmap,
+                       RT5677_PR_BASE + RT5677_DIG_IN_PIN_ST_CTRL3,
+                       0x3 << shift,
+                       (value & 0x3) << shift);
+               break;
+
+       default:
+               break;
+       }
+}
+
+static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+       struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
+       struct regmap_irq_chip_data *data = rt5677->irq_data;
+       int irq;
+
+       if (offset >= RT5677_GPIO1 && offset <= RT5677_GPIO3) {
+               if ((rt5677->pdata.jd1_gpio == 1 && offset == RT5677_GPIO1) ||
+                       (rt5677->pdata.jd1_gpio == 2 &&
+                               offset == RT5677_GPIO2) ||
+                       (rt5677->pdata.jd1_gpio == 3 &&
+                               offset == RT5677_GPIO3)) {
+                       irq = RT5677_IRQ_JD1;
+               } else {
+                       return -ENXIO;
+               }
+       }
+
+       if (offset >= RT5677_GPIO4 && offset <= RT5677_GPIO6) {
+               if ((rt5677->pdata.jd2_gpio == 1 && offset == RT5677_GPIO4) ||
+                       (rt5677->pdata.jd2_gpio == 2 &&
+                               offset == RT5677_GPIO5) ||
+                       (rt5677->pdata.jd2_gpio == 3 &&
+                               offset == RT5677_GPIO6)) {
+                       irq = RT5677_IRQ_JD2;
+               } else if ((rt5677->pdata.jd3_gpio == 1 &&
+                               offset == RT5677_GPIO4) ||
+                       (rt5677->pdata.jd3_gpio == 2 &&
+                               offset == RT5677_GPIO5) ||
+                       (rt5677->pdata.jd3_gpio == 3 &&
+                               offset == RT5677_GPIO6)) {
+                       irq = RT5677_IRQ_JD3;
+               } else {
+                       return -ENXIO;
+               }
+       }
+
+       return regmap_irq_get_virq(data, irq);
+}
+
 static struct gpio_chip rt5677_template_chip = {
        .label                  = "rt5677",
        .owner                  = THIS_MODULE,
@@ -3316,6 +4196,7 @@ static struct gpio_chip rt5677_template_chip = {
        .set                    = rt5677_gpio_set,
        .direction_input        = rt5677_gpio_direction_in,
        .get                    = rt5677_gpio_get,
+       .to_irq                 = rt5677_to_irq,
        .can_sleep              = 1,
 };
 
@@ -3341,6 +4222,11 @@ static void rt5677_free_gpio(struct i2c_client *i2c)
        gpiochip_remove(&rt5677->gpio_chip);
 }
 #else
+static void rt5677_gpio_config(struct rt5677_priv *rt5677, unsigned offset,
+               int value)
+{
+}
+
 static void rt5677_init_gpio(struct i2c_client *i2c)
 {
 }
@@ -3353,6 +4239,7 @@ static void rt5677_free_gpio(struct i2c_client *i2c)
 static int rt5677_probe(struct snd_soc_codec *codec)
 {
        struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+       int i;
 
        rt5677->codec = codec;
 
@@ -3371,6 +4258,37 @@ static int rt5677_probe(struct snd_soc_codec *codec)
        regmap_write(rt5677->regmap, RT5677_DIG_MISC, 0x0020);
        regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x0c00);
 
+       for (i = 0; i < RT5677_GPIO_NUM; i++)
+               rt5677_gpio_config(rt5677, i, rt5677->pdata.gpio_config[i]);
+
+       if (rt5677->irq_data) {
+               regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, 0x8000,
+                       0x8000);
+               regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x0018,
+                       0x0008);
+
+               if (rt5677->pdata.jd1_gpio)
+                       regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
+                               RT5677_SEL_GPIO_JD1_MASK,
+                               rt5677->pdata.jd1_gpio <<
+                               RT5677_SEL_GPIO_JD1_SFT);
+
+               if (rt5677->pdata.jd2_gpio)
+                       regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
+                               RT5677_SEL_GPIO_JD2_MASK,
+                               rt5677->pdata.jd2_gpio <<
+                               RT5677_SEL_GPIO_JD2_SFT);
+
+               if (rt5677->pdata.jd3_gpio)
+                       regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
+                               RT5677_SEL_GPIO_JD3_MASK,
+                               rt5677->pdata.jd3_gpio <<
+                               RT5677_SEL_GPIO_JD3_SFT);
+       }
+
+       mutex_init(&rt5677->dsp_cmd_lock);
+       mutex_init(&rt5677->dsp_pri_lock);
+
        return 0;
 }
 
@@ -3390,8 +4308,11 @@ static int rt5677_suspend(struct snd_soc_codec *codec)
 {
        struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
 
-       regcache_cache_only(rt5677->regmap, true);
-       regcache_mark_dirty(rt5677->regmap);
+       if (!rt5677->dsp_vad_en) {
+               regcache_cache_only(rt5677->regmap, true);
+               regcache_mark_dirty(rt5677->regmap);
+       }
+
        if (gpio_is_valid(rt5677->pow_ldo2))
                gpio_set_value_cansleep(rt5677->pow_ldo2, 0);
 
@@ -3406,8 +4327,11 @@ static int rt5677_resume(struct snd_soc_codec *codec)
                gpio_set_value_cansleep(rt5677->pow_ldo2, 1);
                msleep(10);
        }
-       regcache_cache_only(rt5677->regmap, false);
-       regcache_sync(rt5677->regmap);
+
+       if (!rt5677->dsp_vad_en) {
+               regcache_cache_only(rt5677->regmap, false);
+               regcache_sync(rt5677->regmap);
+       }
 
        return 0;
 }
@@ -3416,6 +4340,51 @@ static int rt5677_resume(struct snd_soc_codec *codec)
 #define rt5677_resume NULL
 #endif
 
+static int rt5677_read(void *context, unsigned int reg, unsigned int *val)
+{
+       struct i2c_client *client = context;
+       struct rt5677_priv *rt5677 = i2c_get_clientdata(client);
+
+       if (rt5677->is_dsp_mode) {
+               if (reg > 0xff) {
+                       mutex_lock(&rt5677->dsp_pri_lock);
+                       rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX,
+                               reg & 0xff);
+                       rt5677_dsp_mode_i2c_read(rt5677, RT5677_PRIV_DATA, val);
+                       mutex_unlock(&rt5677->dsp_pri_lock);
+               } else {
+                       rt5677_dsp_mode_i2c_read(rt5677, reg, val);
+               }
+       } else {
+               regmap_read(rt5677->regmap_physical, reg, val);
+       }
+
+       return 0;
+}
+
+static int rt5677_write(void *context, unsigned int reg, unsigned int val)
+{
+       struct i2c_client *client = context;
+       struct rt5677_priv *rt5677 = i2c_get_clientdata(client);
+
+       if (rt5677->is_dsp_mode) {
+               if (reg > 0xff) {
+                       mutex_lock(&rt5677->dsp_pri_lock);
+                       rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX,
+                               reg & 0xff);
+                       rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_DATA,
+                               val);
+                       mutex_unlock(&rt5677->dsp_pri_lock);
+               } else {
+                       rt5677_dsp_mode_i2c_write(rt5677, reg, val);
+               }
+       } else {
+               regmap_write(rt5677->regmap_physical, reg, val);
+       }
+
+       return 0;
+}
+
 #define RT5677_STEREO_RATES SNDRV_PCM_RATE_8000_96000
 #define RT5677_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
                        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
@@ -3541,6 +4510,20 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5677 = {
        .num_dapm_routes = ARRAY_SIZE(rt5677_dapm_routes),
 };
 
+static const struct regmap_config rt5677_regmap_physical = {
+       .name = "physical",
+       .reg_bits = 8,
+       .val_bits = 16,
+
+       .max_register = RT5677_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5677_ranges) *
+                                               RT5677_PR_SPACING),
+       .readable_reg = rt5677_readable_register,
+
+       .cache_type = REGCACHE_NONE,
+       .ranges = rt5677_ranges,
+       .num_ranges = ARRAY_SIZE(rt5677_ranges),
+};
+
 static const struct regmap_config rt5677_regmap = {
        .reg_bits = 8,
        .val_bits = 16,
@@ -3550,6 +4533,8 @@ static const struct regmap_config rt5677_regmap = {
 
        .volatile_reg = rt5677_volatile_register,
        .readable_reg = rt5677_readable_register,
+       .reg_read = rt5677_read,
+       .reg_write = rt5677_write,
 
        .cache_type = REGCACHE_RBTREE,
        .reg_defaults = rt5677_reg,
@@ -3590,9 +4575,77 @@ static int rt5677_parse_dt(struct rt5677_priv *rt5677, struct device_node *np)
                        (rt5677->pow_ldo2 != -ENOENT))
                return rt5677->pow_ldo2;
 
+       of_property_read_u8_array(np, "realtek,gpio-config",
+               rt5677->pdata.gpio_config, RT5677_GPIO_NUM);
+
+       of_property_read_u32(np, "realtek,jd1-gpio", &rt5677->pdata.jd1_gpio);
+       of_property_read_u32(np, "realtek,jd2-gpio", &rt5677->pdata.jd2_gpio);
+       of_property_read_u32(np, "realtek,jd3-gpio", &rt5677->pdata.jd3_gpio);
+
+       return 0;
+}
+
+static struct regmap_irq rt5677_irqs[] = {
+       [RT5677_IRQ_JD1] = {
+               .reg_offset = 0,
+               .mask = RT5677_EN_IRQ_GPIO_JD1,
+       },
+       [RT5677_IRQ_JD2] = {
+               .reg_offset = 0,
+               .mask = RT5677_EN_IRQ_GPIO_JD2,
+       },
+       [RT5677_IRQ_JD3] = {
+               .reg_offset = 0,
+               .mask = RT5677_EN_IRQ_GPIO_JD3,
+       },
+};
+
+static struct regmap_irq_chip rt5677_irq_chip = {
+       .name = "rt5677",
+       .irqs = rt5677_irqs,
+       .num_irqs = ARRAY_SIZE(rt5677_irqs),
+
+       .num_regs = 1,
+       .status_base = RT5677_IRQ_CTRL1,
+       .mask_base = RT5677_IRQ_CTRL1,
+       .mask_invert = 1,
+};
+
+static int rt5677_init_irq(struct i2c_client *i2c)
+{
+       int ret;
+       struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
+
+       if (!rt5677->pdata.jd1_gpio &&
+               !rt5677->pdata.jd2_gpio &&
+               !rt5677->pdata.jd3_gpio)
+               return 0;
+
+       if (!i2c->irq) {
+               dev_err(&i2c->dev, "No interrupt specified\n");
+               return -EINVAL;
+       }
+
+       ret = regmap_add_irq_chip(rt5677->regmap, i2c->irq,
+               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 0,
+               &rt5677_irq_chip, &rt5677->irq_data);
+
+       if (ret != 0) {
+               dev_err(&i2c->dev, "Failed to register IRQ chip: %d\n", ret);
+               return ret;
+       }
+
        return 0;
 }
 
+static void rt5677_free_irq(struct i2c_client *i2c)
+{
+       struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
+
+       if (rt5677->irq_data)
+               regmap_del_irq_chip(i2c->irq, rt5677->irq_data);
+}
+
 static int rt5677_i2c_probe(struct i2c_client *i2c,
                    const struct i2c_device_id *id)
 {
@@ -3638,7 +4691,16 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
                msleep(10);
        }
 
-       rt5677->regmap = devm_regmap_init_i2c(i2c, &rt5677_regmap);
+       rt5677->regmap_physical = devm_regmap_init_i2c(i2c,
+                                       &rt5677_regmap_physical);
+       if (IS_ERR(rt5677->regmap_physical)) {
+               ret = PTR_ERR(rt5677->regmap_physical);
+               dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+                       ret);
+               return ret;
+       }
+
+       rt5677->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt5677_regmap);
        if (IS_ERR(rt5677->regmap)) {
                ret = PTR_ERR(rt5677->regmap);
                dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
@@ -3690,6 +4752,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
        }
 
        rt5677_init_gpio(i2c);
+       rt5677_init_irq(i2c);
 
        return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5677,
                                      rt5677_dai, ARRAY_SIZE(rt5677_dai));
@@ -3698,6 +4761,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
 static int rt5677_i2c_remove(struct i2c_client *i2c)
 {
        snd_soc_unregister_codec(&i2c->dev);
+       rt5677_free_irq(i2c);
        rt5677_free_gpio(i2c);
 
        return 0;
index d4eb6d5..c0a625f 100644 (file)
@@ -13,6 +13,7 @@
 #define __RT5677_H__
 
 #include <sound/rt5677.h>
+#include <linux/gpio/driver.h>
 
 /* Info */
 #define RT5677_RESET                           0x00
 #define RT5677_R_MUTE_SFT                      7
 #define RT5677_VOL_R_MUTE                      (0x1 << 6)
 #define RT5677_VOL_R_SFT                       6
-#define RT5677_L_VOL_MASK                      (0x3f << 8)
-#define RT5677_L_VOL_SFT                       8
-#define RT5677_R_VOL_MASK                      (0x3f)
-#define RT5677_R_VOL_SFT                       0
+#define RT5677_L_VOL_MASK                      (0x7f << 9)
+#define RT5677_L_VOL_SFT                       9
+#define RT5677_R_VOL_MASK                      (0x7f << 1)
+#define RT5677_R_VOL_SFT                       1
 
 /* LOUT1 Control (0x01) */
 #define RT5677_LOUT1_L_MUTE                    (0x1 << 15)
 #define RT5677_SEL_DAC2_R_SRC_SFT              0
 
 /* Stereo1 ADC Digital Volume Control (0x1c) */
-#define RT5677_STO1_ADC_L_VOL_MASK             (0x7f << 8)
-#define RT5677_STO1_ADC_L_VOL_SFT              8
-#define RT5677_STO1_ADC_R_VOL_MASK             (0x7f)
-#define RT5677_STO1_ADC_R_VOL_SFT              0
+#define RT5677_STO1_ADC_L_VOL_MASK             (0x3f << 9)
+#define RT5677_STO1_ADC_L_VOL_SFT              9
+#define RT5677_STO1_ADC_R_VOL_MASK             (0x3f << 1)
+#define RT5677_STO1_ADC_R_VOL_SFT              1
 
 /* Mono ADC Digital Volume Control (0x1d) */
-#define RT5677_MONO_ADC_L_VOL_MASK             (0x7f << 8)
-#define RT5677_MONO_ADC_L_VOL_SFT              8
-#define RT5677_MONO_ADC_R_VOL_MASK             (0x7f)
-#define RT5677_MONO_ADC_R_VOL_SFT              0
+#define RT5677_MONO_ADC_L_VOL_MASK             (0x3f << 9)
+#define RT5677_MONO_ADC_L_VOL_SFT              9
+#define RT5677_MONO_ADC_R_VOL_MASK             (0x3f << 1)
+#define RT5677_MONO_ADC_R_VOL_SFT              1
 
 /* Stereo 1/2 ADC Boost Gain Control (0x1e) */
 #define RT5677_STO1_ADC_L_BST_MASK             (0x3 << 14)
 #define RT5677_PDM2_I2C_EXE                    (0x1 << 1)
 #define RT5677_PDM2_I2C_BUSY                   (0x1 << 0)
 
-/* MX3C TDM1 control 1 (0x3c) */
+/* TDM1 control 1 (0x3b) */
+#define RT5677_IF1_ADC_MODE_MASK               (0x1 << 12)
+#define RT5677_IF1_ADC_MODE_SFT                        12
+#define RT5677_IF1_ADC_MODE_I2S                        (0x0 << 12)
+#define RT5677_IF1_ADC_MODE_TDM                        (0x1 << 12)
+#define RT5677_IF1_ADC1_SWAP_MASK              (0x3 << 6)
+#define RT5677_IF1_ADC1_SWAP_SFT               6
+#define RT5677_IF1_ADC2_SWAP_MASK              (0x3 << 4)
+#define RT5677_IF1_ADC2_SWAP_SFT               4
+#define RT5677_IF1_ADC3_SWAP_MASK              (0x3 << 2)
+#define RT5677_IF1_ADC3_SWAP_SFT               2
+#define RT5677_IF1_ADC4_SWAP_MASK              (0x3 << 0)
+#define RT5677_IF1_ADC4_SWAP_SFT               0
+
+/* TDM1 control 2 (0x3c) */
 #define RT5677_IF1_ADC4_MASK                   (0x3 << 10)
 #define RT5677_IF1_ADC4_SFT                    10
 #define RT5677_IF1_ADC3_MASK                   (0x3 << 8)
 #define RT5677_IF1_ADC2_SFT                    6
 #define RT5677_IF1_ADC1_MASK                   (0x3 << 4)
 #define RT5677_IF1_ADC1_SFT                    4
-
-/* MX41 TDM2 control 1 (0x41) */
+#define RT5677_IF1_ADC_CTRL_MASK               (0x7 << 0)
+#define RT5677_IF1_ADC_CTRL_SFT                        0
+
+/* TDM1 control 4 (0x3e) */
+#define RT5677_IF1_DAC0_MASK                   (0x7 << 12)
+#define RT5677_IF1_DAC0_SFT                    12
+#define RT5677_IF1_DAC1_MASK                   (0x7 << 8)
+#define RT5677_IF1_DAC1_SFT                    8
+#define RT5677_IF1_DAC2_MASK                   (0x7 << 4)
+#define RT5677_IF1_DAC2_SFT                    4
+#define RT5677_IF1_DAC3_MASK                   (0x7 << 0)
+#define RT5677_IF1_DAC3_SFT                    0
+
+/* TDM1 control 5 (0x3f) */
+#define RT5677_IF1_DAC4_MASK                   (0x7 << 12)
+#define RT5677_IF1_DAC4_SFT                    12
+#define RT5677_IF1_DAC5_MASK                   (0x7 << 8)
+#define RT5677_IF1_DAC5_SFT                    8
+#define RT5677_IF1_DAC6_MASK                   (0x7 << 4)
+#define RT5677_IF1_DAC6_SFT                    4
+#define RT5677_IF1_DAC7_MASK                   (0x7 << 0)
+#define RT5677_IF1_DAC7_SFT                    0
+
+/* TDM2 control 1 (0x40) */
+#define RT5677_IF2_ADC_MODE_MASK               (0x1 << 12)
+#define RT5677_IF2_ADC_MODE_SFT                        12
+#define RT5677_IF2_ADC_MODE_I2S                        (0x0 << 12)
+#define RT5677_IF2_ADC_MODE_TDM                        (0x1 << 12)
+#define RT5677_IF2_ADC1_SWAP_MASK              (0x3 << 6)
+#define RT5677_IF2_ADC1_SWAP_SFT               6
+#define RT5677_IF2_ADC2_SWAP_MASK              (0x3 << 4)
+#define RT5677_IF2_ADC2_SWAP_SFT               4
+#define RT5677_IF2_ADC3_SWAP_MASK              (0x3 << 2)
+#define RT5677_IF2_ADC3_SWAP_SFT               2
+#define RT5677_IF2_ADC4_SWAP_MASK              (0x3 << 0)
+#define RT5677_IF2_ADC4_SWAP_SFT               0
+
+/* TDM2 control 2 (0x41) */
 #define RT5677_IF2_ADC4_MASK                   (0x3 << 10)
 #define RT5677_IF2_ADC4_SFT                    10
 #define RT5677_IF2_ADC3_MASK                   (0x3 << 8)
 #define RT5677_IF2_ADC2_SFT                    6
 #define RT5677_IF2_ADC1_MASK                   (0x3 << 4)
 #define RT5677_IF2_ADC1_SFT                    4
+#define RT5677_IF2_ADC_CTRL_MASK               (0x7 << 0)
+#define RT5677_IF2_ADC_CTRL_SFT                        0
+
+/* TDM2 control 4 (0x43) */
+#define RT5677_IF2_DAC0_MASK                   (0x7 << 12)
+#define RT5677_IF2_DAC0_SFT                    12
+#define RT5677_IF2_DAC1_MASK                   (0x7 << 8)
+#define RT5677_IF2_DAC1_SFT                    8
+#define RT5677_IF2_DAC2_MASK                   (0x7 << 4)
+#define RT5677_IF2_DAC2_SFT                    4
+#define RT5677_IF2_DAC3_MASK                   (0x7 << 0)
+#define RT5677_IF2_DAC3_SFT                    0
+
+/* TDM2 control 5 (0x44) */
+#define RT5677_IF2_DAC4_MASK                   (0x7 << 12)
+#define RT5677_IF2_DAC4_SFT                    12
+#define RT5677_IF2_DAC5_MASK                   (0x7 << 8)
+#define RT5677_IF2_DAC5_SFT                    8
+#define RT5677_IF2_DAC6_MASK                   (0x7 << 4)
+#define RT5677_IF2_DAC6_SFT                    4
+#define RT5677_IF2_DAC7_MASK                   (0x7 << 0)
+#define RT5677_IF2_DAC7_SFT                    0
 
 /* Digital Microphone Control 1 (0x50) */
 #define RT5677_DMIC_1_EN_MASK                  (0x1 << 15)
 #define RT5677_SEL_SRC_IB01                    (0x1 << 0)
 #define RT5677_SEL_SRC_IB01_SFT                        0
 
+/* Jack Detect Control 1 (0xb5) */
+#define RT5677_SEL_GPIO_JD1_MASK               (0x3 << 14)
+#define RT5677_SEL_GPIO_JD1_SFT                        14
+#define RT5677_SEL_GPIO_JD2_MASK               (0x3 << 12)
+#define RT5677_SEL_GPIO_JD2_SFT                        12
+#define RT5677_SEL_GPIO_JD3_MASK               (0x3 << 10)
+#define RT5677_SEL_GPIO_JD3_SFT                        10
+
+/* IRQ Control 1 (0xbd) */
+#define RT5677_STA_GPIO_JD1                    (0x1 << 15)
+#define RT5677_STA_GPIO_JD1_SFT                        15
+#define RT5677_EN_IRQ_GPIO_JD1                 (0x1 << 14)
+#define RT5677_EN_IRQ_GPIO_JD1_SFT             14
+#define RT5677_EN_GPIO_JD1_STICKY              (0x1 << 13)
+#define RT5677_EN_GPIO_JD1_STICKY_SFT          13
+#define RT5677_INV_GPIO_JD1                    (0x1 << 12)
+#define RT5677_INV_GPIO_JD1_SFT                        12
+#define RT5677_STA_GPIO_JD2                    (0x1 << 11)
+#define RT5677_STA_GPIO_JD2_SFT                        11
+#define RT5677_EN_IRQ_GPIO_JD2                 (0x1 << 10)
+#define RT5677_EN_IRQ_GPIO_JD2_SFT             10
+#define RT5677_EN_GPIO_JD2_STICKY              (0x1 << 9)
+#define RT5677_EN_GPIO_JD2_STICKY_SFT          9
+#define RT5677_INV_GPIO_JD2                    (0x1 << 8)
+#define RT5677_INV_GPIO_JD2_SFT                        8
+#define RT5677_STA_MICBIAS1_OVCD               (0x1 << 7)
+#define RT5677_STA_MICBIAS1_OVCD_SFT           7
+#define RT5677_EN_IRQ_MICBIAS1_OVCD            (0x1 << 6)
+#define RT5677_EN_IRQ_MICBIAS1_OVCD_SFT                6
+#define RT5677_EN_MICBIAS1_OVCD_STICKY         (0x1 << 5)
+#define RT5677_EN_MICBIAS1_OVCD_STICKY_SFT     5
+#define RT5677_INV_MICBIAS1_OVCD               (0x1 << 4)
+#define RT5677_INV_MICBIAS1_OVCD_SFT           4
+#define RT5677_STA_GPIO_JD3                    (0x1 << 3)
+#define RT5677_STA_GPIO_JD3_SFT                        3
+#define RT5677_EN_IRQ_GPIO_JD3                 (0x1 << 2)
+#define RT5677_EN_IRQ_GPIO_JD3_SFT             2
+#define RT5677_EN_GPIO_JD3_STICKY              (0x1 << 1)
+#define RT5677_EN_GPIO_JD3_STICKY_SFT          1
+#define RT5677_INV_GPIO_JD3                    (0x1 << 0)
+#define RT5677_INV_GPIO_JD3_SFT                        0
+
 /* GPIO status (0xbf) */
 #define RT5677_GPIO6_STATUS_MASK               (0x1 << 5)
 #define RT5677_GPIO6_STATUS_SFT                        5
 #define RT5677_GPIO5_FUNC_GPIO                 (0x0 << 9)
 #define RT5677_GPIO5_FUNC_DMIC                 (0x1 << 9)
 
+#define RT5677_FIRMWARE1       "rt5677_dsp_fw1.bin"
+#define RT5677_FIRMWARE2       "rt5677_dsp_fw2.bin"
+
 /* System Clock Source */
 enum {
        RT5677_SCLK_S_MCLK,
@@ -1541,10 +1659,18 @@ enum {
        RT5677_GPIO_NUM,
 };
 
+enum {
+       RT5677_IRQ_JD1,
+       RT5677_IRQ_JD2,
+       RT5677_IRQ_JD3,
+};
+
 struct rt5677_priv {
        struct snd_soc_codec *codec;
        struct rt5677_platform_data pdata;
-       struct regmap *regmap;
+       struct regmap *regmap, *regmap_physical;
+       const struct firmware *fw1, *fw2;
+       struct mutex dsp_cmd_lock, dsp_pri_lock;
 
        int sysclk;
        int sysclk_src;
@@ -1558,6 +1684,10 @@ struct rt5677_priv {
 #ifdef CONFIG_GPIOLIB
        struct gpio_chip gpio_chip;
 #endif
+       bool dsp_vad_en;
+       struct regmap_irq_chip_data *irq_data;
+       bool is_dsp_mode;
+       bool is_vref_slow;
 };
 
 #endif /* __RT5677_H__ */
index 47d6ca0..29cf7ce 100644 (file)
@@ -1312,8 +1312,7 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
 
        /* enable small pop, introduce 400ms delay in turning off */
        snd_soc_update_bits(codec, SGTL5000_CHIP_REF_CTRL,
-                               SGTL5000_SMALL_POP,
-                               SGTL5000_SMALL_POP);
+                               SGTL5000_SMALL_POP, 1);
 
        /* disable short cut detector */
        snd_soc_write(codec, SGTL5000_CHIP_SHORT_CTRL, 0);
index 2f8c889..bd7a344 100644 (file)
 #define SGTL5000_BIAS_CTRL_MASK                        0x000e
 #define SGTL5000_BIAS_CTRL_SHIFT               1
 #define SGTL5000_BIAS_CTRL_WIDTH               3
-#define SGTL5000_SMALL_POP                     0x0001
+#define SGTL5000_SMALL_POP                     0
 
 /*
  * SGTL5000_CHIP_MIC_CTRL
index f2de7e0..81a38dd 100644 (file)
@@ -159,6 +159,13 @@ int _process_sigma_firmware(struct device *dev,
                goto done;
        }
 
+       if (ssfw_head->version != 1) {
+               dev_err(dev,
+                       "Failed to load firmware: Invalid version %d. Supported firmware versions: 1\n",
+                       ssfw_head->version);
+               goto done;
+       }
+
        crc = crc32(0, fw->data + sizeof(*ssfw_head),
                        fw->size - sizeof(*ssfw_head));
        pr_debug("%s: crc=%x\n", __func__, crc);
index 53b810d..f37a79e 100644 (file)
@@ -139,18 +139,19 @@ static const struct snd_kcontrol_new stac9766_snd_ac97_controls[] = {
 static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
                               unsigned int val)
 {
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
        u16 *cache = codec->reg_cache;
 
        if (reg > AC97_STAC_PAGE0) {
                stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
-               soc_ac97_ops->write(codec->ac97, reg, val);
+               soc_ac97_ops->write(ac97, reg, val);
                stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
                return 0;
        }
        if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
                return -EIO;
 
-       soc_ac97_ops->write(codec->ac97, reg, val);
+       soc_ac97_ops->write(ac97, reg, val);
        cache[reg / 2] = val;
        return 0;
 }
@@ -158,11 +159,12 @@ static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
 static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
                                       unsigned int reg)
 {
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
        u16 val = 0, *cache = codec->reg_cache;
 
        if (reg > AC97_STAC_PAGE0) {
                stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
-               val = soc_ac97_ops->read(codec->ac97, reg - AC97_STAC_PAGE0);
+               val = soc_ac97_ops->read(ac97, reg - AC97_STAC_PAGE0);
                stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
                return val;
        }
@@ -173,7 +175,7 @@ static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
                reg == AC97_INT_PAGING || reg == AC97_VENDOR_ID1 ||
                reg == AC97_VENDOR_ID2) {
 
-               val = soc_ac97_ops->read(codec->ac97, reg);
+               val = soc_ac97_ops->read(ac97, reg);
                return val;
        }
        return cache[reg / 2];
@@ -240,15 +242,17 @@ static int stac9766_set_bias_level(struct snd_soc_codec *codec,
 
 static int stac9766_reset(struct snd_soc_codec *codec, int try_warm)
 {
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
        if (try_warm && soc_ac97_ops->warm_reset) {
-               soc_ac97_ops->warm_reset(codec->ac97);
+               soc_ac97_ops->warm_reset(ac97);
                if (stac9766_ac97_read(codec, 0) == stac9766_reg[0])
                        return 1;
        }
 
-       soc_ac97_ops->reset(codec->ac97);
+       soc_ac97_ops->reset(ac97);
        if (soc_ac97_ops->warm_reset)
-               soc_ac97_ops->warm_reset(codec->ac97);
+               soc_ac97_ops->warm_reset(ac97);
        if (stac9766_ac97_read(codec, 0) != stac9766_reg[0])
                return -EIO;
        return 0;
@@ -262,6 +266,7 @@ static int stac9766_codec_suspend(struct snd_soc_codec *codec)
 
 static int stac9766_codec_resume(struct snd_soc_codec *codec)
 {
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
        u16 id, reset;
 
        reset = 0;
@@ -271,8 +276,8 @@ reset:
                printk(KERN_ERR "stac9766 failed to resume");
                return -EIO;
        }
-       codec->ac97->bus->ops->warm_reset(codec->ac97);
-       id = soc_ac97_ops->read(codec->ac97, AC97_VENDOR_ID2);
+       ac97->bus->ops->warm_reset(ac97);
+       id = soc_ac97_ops->read(ac97, AC97_VENDOR_ID2);
        if (id != 0x4c13) {
                stac9766_reset(codec, 0);
                reset++;
@@ -294,7 +299,6 @@ static const struct snd_soc_dai_ops stac9766_dai_ops_digital = {
 static struct snd_soc_dai_driver stac9766_dai[] = {
 {
        .name = "stac9766-hifi-analog",
-       .ac97_control = 1,
 
        /* stream cababilities */
        .playback = {
@@ -316,7 +320,6 @@ static struct snd_soc_dai_driver stac9766_dai[] = {
 },
 {
        .name = "stac9766-hifi-IEC958",
-       .ac97_control = 1,
 
        /* stream cababilities */
        .playback = {
@@ -334,11 +337,14 @@ static struct snd_soc_dai_driver stac9766_dai[] = {
 
 static int stac9766_codec_probe(struct snd_soc_codec *codec)
 {
+       struct snd_ac97 *ac97;
        int ret = 0;
 
-       ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
-       if (ret < 0)
-               goto codec_err;
+       ac97 = snd_soc_new_ac97_codec(codec);
+       if (IS_ERR(ac97))
+               return PTR_ERR(ac97);
+
+       snd_soc_codec_set_drvdata(codec, ac97);
 
        /* do a cold reset for the controller and then try
         * a warm reset followed by an optional cold reset for codec */
@@ -357,13 +363,15 @@ static int stac9766_codec_probe(struct snd_soc_codec *codec)
        return 0;
 
 codec_err:
-       snd_soc_free_ac97_codec(codec);
+       snd_soc_free_ac97_codec(ac97);
        return ret;
 }
 
 static int stac9766_codec_remove(struct snd_soc_codec *codec)
 {
-       snd_soc_free_ac97_codec(codec);
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+       snd_soc_free_ac97_codec(ac97);
        return 0;
 }
 
index 145fe5b..93de5dd 100644 (file)
@@ -911,12 +911,13 @@ static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai,
        }
        aic31xx->p_div = i;
 
-       for (i = 0; aic31xx_divs[i].mclk_p != freq/aic31xx->p_div; i++) {
-               if (i == ARRAY_SIZE(aic31xx_divs)) {
-                       dev_err(aic31xx->dev, "%s: Unsupported frequency %d\n",
-                               __func__, freq);
-                       return -EINVAL;
-               }
+       for (i = 0; i < ARRAY_SIZE(aic31xx_divs) &&
+                    aic31xx_divs[i].mclk_p != freq/aic31xx->p_div; i++)
+               ;
+       if (i == ARRAY_SIZE(aic31xx_divs)) {
+               dev_err(aic31xx->dev, "%s: Unsupported frequency %d\n",
+                       __func__, freq);
+               return -EINVAL;
        }
 
        /* set clock on MCLK, BCLK, or GPIO1 as PLL input */
index f602349..d78fb8d 100644 (file)
@@ -619,10 +619,10 @@ static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol,
        struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
        uint16_t data;
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&arizona->dac_comp_lock);
        data = cpu_to_be16(arizona->dac_comp_coeff);
        memcpy(ucontrol->value.bytes.data, &data, sizeof(data));
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&arizona->dac_comp_lock);
 
        return 0;
 }
@@ -633,11 +633,11 @@ static int wm5102_out_comp_coeff_put(struct snd_kcontrol *kcontrol,
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&arizona->dac_comp_lock);
        memcpy(&arizona->dac_comp_coeff, ucontrol->value.bytes.data,
               sizeof(arizona->dac_comp_coeff));
        arizona->dac_comp_coeff = be16_to_cpu(arizona->dac_comp_coeff);
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&arizona->dac_comp_lock);
 
        return 0;
 }
@@ -648,9 +648,9 @@ static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol,
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&arizona->dac_comp_lock);
        ucontrol->value.integer.value[0] = arizona->dac_comp_enabled;
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&arizona->dac_comp_lock);
 
        return 0;
 }
@@ -661,9 +661,9 @@ static int wm5102_out_comp_switch_put(struct snd_kcontrol *kcontrol,
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&arizona->dac_comp_lock);
        arizona->dac_comp_enabled = ucontrol->value.integer.value[0];
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&arizona->dac_comp_lock);
 
        return 0;
 }
@@ -1900,6 +1900,8 @@ static int wm5102_probe(struct platform_device *pdev)
                return -ENOMEM;
        platform_set_drvdata(pdev, wm5102);
 
+       mutex_init(&arizona->dac_comp_lock);
+
        wm5102->core.arizona = arizona;
        wm5102->core.num_inputs = 6;
 
index eebb328..5dae9a6 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/spi/spi.h>
 #include <linux/of_device.h>
+#include <linux/mutex.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -50,6 +51,8 @@ struct wm8731_priv {
        int sysclk_type;
        int playback_fs;
        bool deemph;
+
+       struct mutex lock;
 };
 
 
@@ -138,7 +141,7 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol,
        if (deemph > 1)
                return -EINVAL;
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&wm8731->lock);
        if (wm8731->deemph != deemph) {
                wm8731->deemph = deemph;
 
@@ -146,7 +149,7 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol,
 
                ret = 1;
        }
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&wm8731->lock);
 
        return ret;
 }
@@ -685,6 +688,8 @@ static int wm8731_spi_probe(struct spi_device *spi)
        if (wm8731 == NULL)
                return -ENOMEM;
 
+       mutex_init(&wm8731->lock);
+
        wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap);
        if (IS_ERR(wm8731->regmap)) {
                ret = PTR_ERR(wm8731->regmap);
index c038b3e..ffbe6df 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/regmap.h>
 #include <linux/slab.h>
 #include <linux/irq.h>
+#include <linux/mutex.h>
 #include <sound/core.h>
 #include <sound/jack.h>
 #include <sound/pcm.h>
@@ -123,6 +124,7 @@ struct wm8903_priv {
        int sysclk;
        int irq;
 
+       struct mutex lock;
        int fs;
        int deemph;
 
@@ -457,7 +459,7 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol,
        if (deemph > 1)
                return -EINVAL;
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&wm8903->lock);
        if (wm8903->deemph != deemph) {
                wm8903->deemph = deemph;
 
@@ -465,7 +467,7 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol,
 
                ret = 1;
        }
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&wm8903->lock);
 
        return ret;
 }
@@ -2023,6 +2025,8 @@ static int wm8903_i2c_probe(struct i2c_client *i2c,
                              GFP_KERNEL);
        if (wm8903 == NULL)
                return -ENOMEM;
+
+       mutex_init(&wm8903->lock);
        wm8903->dev = &i2c->dev;
 
        wm8903->regmap = devm_regmap_init_i2c(i2c, &wm8903_regmap);
index 0dada7f..3cbc82b 100644 (file)
@@ -867,9 +867,9 @@ static void wm8958_enh_eq_loaded(const struct firmware *fw, void *context)
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
        if (fw && (wm8958_dsp2_fw(codec, "ENH_EQ", fw, true) == 0)) {
-               mutex_lock(&codec->mutex);
+               mutex_lock(&wm8994->fw_lock);
                wm8994->enh_eq = fw;
-               mutex_unlock(&codec->mutex);
+               mutex_unlock(&wm8994->fw_lock);
        }
 }
 
@@ -879,9 +879,9 @@ static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context)
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
        if (fw && (wm8958_dsp2_fw(codec, "MBC+VSS", fw, true) == 0)) {
-               mutex_lock(&codec->mutex);
+               mutex_lock(&wm8994->fw_lock);
                wm8994->mbc_vss = fw;
-               mutex_unlock(&codec->mutex);
+               mutex_unlock(&wm8994->fw_lock);
        }
 }
 
@@ -891,9 +891,9 @@ static void wm8958_mbc_loaded(const struct firmware *fw, void *context)
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
        if (fw && (wm8958_dsp2_fw(codec, "MBC", fw, true) == 0)) {
-               mutex_lock(&codec->mutex);
+               mutex_lock(&wm8994->fw_lock);
                wm8994->mbc = fw;
-               mutex_unlock(&codec->mutex);
+               mutex_unlock(&wm8994->fw_lock);
        }
 }
 
index 9077411..61ca4a7 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 #include <linux/workqueue.h>
+#include <linux/mutex.h>
 #include <sound/core.h>
 #include <sound/jack.h>
 #include <sound/pcm.h>
@@ -67,6 +68,7 @@ struct wm8962_priv {
        int fll_fref;
        int fll_fout;
 
+       struct mutex dsp2_ena_lock;
        u16 dsp2_ena;
 
        struct delayed_work mic_work;
@@ -1570,7 +1572,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol,
        int dsp2_running = snd_soc_read(codec, WM8962_DSP2_POWER_MANAGEMENT) &
                WM8962_DSP2_ENA;
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&wm8962->dsp2_ena_lock);
 
        if (ucontrol->value.integer.value[0])
                wm8962->dsp2_ena |= 1 << shift;
@@ -1590,7 +1592,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol,
        }
 
 out:
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&wm8962->dsp2_ena_lock);
 
        return ret;
 }
@@ -3557,6 +3559,8 @@ static int wm8962_i2c_probe(struct i2c_client *i2c,
        if (wm8962 == NULL)
                return -ENOMEM;
 
+       mutex_init(&wm8962->dsp2_ena_lock);
+
        i2c_set_clientdata(i2c, wm8962);
 
        INIT_DELAYED_WORK(&wm8962->mic_work, wm8962_mic_work);
index 1fcb9f3..dbca6e0 100644 (file)
@@ -4457,6 +4457,8 @@ static int wm8994_probe(struct platform_device *pdev)
                return -ENOMEM;
        platform_set_drvdata(pdev, wm8994);
 
+       mutex_init(&wm8994->fw_lock);
+
        wm8994->wm8994 = dev_get_drvdata(pdev->dev.parent);
 
        pm_runtime_enable(&pdev->dev);
index 6536f8d..dd73387 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/firmware.h>
 #include <linux/completion.h>
 #include <linux/workqueue.h>
+#include <linux/mutex.h>
 
 #include "wm_hubs.h"
 
@@ -156,6 +157,7 @@ struct wm8994_priv {
        unsigned int aif1clk_disable:1;
        unsigned int aif2clk_disable:1;
 
+       struct mutex fw_lock;
        int dsp_active;
        const struct firmware *cur_fw;
        const struct firmware *mbc;
index c0b7f45..d3a800f 100644 (file)
@@ -203,13 +203,14 @@ static const struct snd_soc_dapm_route wm9705_audio_map[] = {
 /* We use a register cache to enhance read performance. */
 static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg)
 {
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
        u16 *cache = codec->reg_cache;
 
        switch (reg) {
        case AC97_RESET:
        case AC97_VENDOR_ID1:
        case AC97_VENDOR_ID2:
-               return soc_ac97_ops->read(codec->ac97, reg);
+               return soc_ac97_ops->read(ac97, reg);
        default:
                reg = reg >> 1;
 
@@ -223,9 +224,10 @@ static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg)
 static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
        unsigned int val)
 {
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
        u16 *cache = codec->reg_cache;
 
-       soc_ac97_ops->write(codec->ac97, reg, val);
+       soc_ac97_ops->write(ac97, reg, val);
        reg = reg >> 1;
        if (reg < (ARRAY_SIZE(wm9705_reg)))
                cache[reg] = val;
@@ -263,7 +265,6 @@ static const struct snd_soc_dai_ops wm9705_dai_ops = {
 static struct snd_soc_dai_driver wm9705_dai[] = {
        {
                .name = "wm9705-hifi",
-               .ac97_control = 1,
                .playback = {
                        .stream_name = "HiFi Playback",
                        .channels_min = 1,
@@ -294,36 +295,41 @@ static struct snd_soc_dai_driver wm9705_dai[] = {
 
 static int wm9705_reset(struct snd_soc_codec *codec)
 {
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
        if (soc_ac97_ops->reset) {
-               soc_ac97_ops->reset(codec->ac97);
+               soc_ac97_ops->reset(ac97);
                if (ac97_read(codec, 0) == wm9705_reg[0])
                        return 0; /* Success */
        }
 
+       dev_err(codec->dev, "Failed to reset: AC97 link error\n");
+
        return -EIO;
 }
 
 #ifdef CONFIG_PM
 static int wm9705_soc_suspend(struct snd_soc_codec *codec)
 {
-       soc_ac97_ops->write(codec->ac97, AC97_POWERDOWN, 0xffff);
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+       soc_ac97_ops->write(ac97, AC97_POWERDOWN, 0xffff);
 
        return 0;
 }
 
 static int wm9705_soc_resume(struct snd_soc_codec *codec)
 {
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
        int i, ret;
        u16 *cache = codec->reg_cache;
 
        ret = wm9705_reset(codec);
-       if (ret < 0) {
-               printk(KERN_ERR "could not reset AC97 codec\n");
+       if (ret < 0)
                return ret;
-       }
 
        for (i = 2; i < ARRAY_SIZE(wm9705_reg) << 1; i += 2) {
-               soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
+               soc_ac97_ops->write(ac97, i, cache[i>>1]);
        }
 
        return 0;
@@ -335,31 +341,34 @@ static int wm9705_soc_resume(struct snd_soc_codec *codec)
 
 static int wm9705_soc_probe(struct snd_soc_codec *codec)
 {
+       struct snd_ac97 *ac97;
        int ret = 0;
 
-       ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
-       if (ret < 0) {
-               printk(KERN_ERR "wm9705: failed to register AC97 codec\n");
+       ac97 = snd_soc_new_ac97_codec(codec);
+       if (IS_ERR(ac97)) {
+               ret = PTR_ERR(ac97);
+               dev_err(codec->dev, "Failed to register AC97 codec\n");
                return ret;
        }
 
+       snd_soc_codec_set_drvdata(codec, ac97);
+
        ret = wm9705_reset(codec);
        if (ret)
                goto reset_err;
 
-       snd_soc_add_codec_controls(codec, wm9705_snd_ac97_controls,
-                               ARRAY_SIZE(wm9705_snd_ac97_controls));
-
        return 0;
 
 reset_err:
-       snd_soc_free_ac97_codec(codec);
+       snd_soc_free_ac97_codec(ac97);
        return ret;
 }
 
 static int wm9705_soc_remove(struct snd_soc_codec *codec)
 {
-       snd_soc_free_ac97_codec(codec);
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+       snd_soc_free_ac97_codec(ac97);
        return 0;
 }
 
@@ -374,6 +383,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9705 = {
        .reg_word_size = sizeof(u16),
        .reg_cache_step = 2,
        .reg_cache_default = wm9705_reg,
+
+       .controls = wm9705_snd_ac97_controls,
+       .num_controls = ARRAY_SIZE(wm9705_snd_ac97_controls),
        .dapm_widgets = wm9705_dapm_widgets,
        .num_dapm_widgets = ARRAY_SIZE(wm9705_dapm_widgets),
        .dapm_routes = wm9705_audio_map,
index c5eb746..52a211b 100644 (file)
 #include <sound/tlv.h>
 #include "wm9712.h"
 
+struct wm9712_priv {
+       struct snd_ac97 *ac97;
+       unsigned int hp_mixer[2];
+       struct mutex lock;
+};
+
 static unsigned int ac97_read(struct snd_soc_codec *codec,
        unsigned int reg);
 static int ac97_write(struct snd_soc_codec *codec,
@@ -48,12 +54,10 @@ static const u16 wm9712_reg[] = {
        0x0000, 0x0000, 0x0000, 0x0000, /* 6e */
        0x0000, 0x0000, 0x0000, 0x0006, /* 76 */
        0x0001, 0x0000, 0x574d, 0x4c12, /* 7e */
-       0x0000, 0x0000 /* virtual hp mixers */
 };
 
-/* virtual HP mixers regs */
-#define HPL_MIXER      0x80
-#define HPR_MIXER      0x82
+#define HPL_MIXER      0x0
+#define HPR_MIXER      0x1
 
 static const char *wm9712_alc_select[] = {"None", "Left", "Right", "Stereo"};
 static const char *wm9712_alc_mux[] = {"Stereo", "Left", "Right", "None"};
@@ -157,75 +161,108 @@ SOC_SINGLE_TLV("Mic 2 Volume", AC97_MIC, 0, 31, 1, main_tlv),
 SOC_SINGLE_TLV("Mic Boost Volume", AC97_MIC, 7, 1, 0, boost_tlv),
 };
 
+static const unsigned int wm9712_mixer_mute_regs[] = {
+       AC97_VIDEO,
+       AC97_PCM,
+       AC97_LINE,
+       AC97_PHONE,
+       AC97_CD,
+       AC97_PC_BEEP,
+};
+
 /* We have to create a fake left and right HP mixers because
  * the codec only has a single control that is shared by both channels.
  * This makes it impossible to determine the audio path.
  */
-static int mixer_event(struct snd_soc_dapm_widget *w,
-       struct snd_kcontrol *k, int event)
+static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
 {
-       u16 l, r, beep, line, phone, mic, pcm, aux;
-
-       l = ac97_read(w->codec, HPL_MIXER);
-       r = ac97_read(w->codec, HPR_MIXER);
-       beep = ac97_read(w->codec, AC97_PC_BEEP);
-       mic = ac97_read(w->codec, AC97_VIDEO);
-       phone = ac97_read(w->codec, AC97_PHONE);
-       line = ac97_read(w->codec, AC97_LINE);
-       pcm = ac97_read(w->codec, AC97_PCM);
-       aux = ac97_read(w->codec, AC97_CD);
-
-       if (l & 0x1 || r & 0x1)
-               ac97_write(w->codec, AC97_VIDEO, mic & 0x7fff);
+       struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+       struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+       unsigned int val = ucontrol->value.enumerated.item[0];
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int mixer, mask, shift, old;
+       struct snd_soc_dapm_update update;
+       bool change;
+
+       mixer = mc->shift >> 8;
+       shift = mc->shift & 0xff;
+       mask = 1 << shift;
+
+       mutex_lock(&wm9712->lock);
+       old = wm9712->hp_mixer[mixer];
+       if (ucontrol->value.enumerated.item[0])
+               wm9712->hp_mixer[mixer] |= mask;
        else
-               ac97_write(w->codec, AC97_VIDEO, mic | 0x8000);
+               wm9712->hp_mixer[mixer] &= ~mask;
+
+       change = old != wm9712->hp_mixer[mixer];
+       if (change) {
+               update.kcontrol = kcontrol;
+               update.reg = wm9712_mixer_mute_regs[shift];
+               update.mask = 0x8000;
+               if ((wm9712->hp_mixer[0] & mask) ||
+                   (wm9712->hp_mixer[1] & mask))
+                       update.val = 0x0;
+               else
+                       update.val = 0x8000;
+
+               snd_soc_dapm_mixer_update_power(dapm, kcontrol, val,
+                       &update);
+       }
 
-       if (l & 0x2 || r & 0x2)
-               ac97_write(w->codec, AC97_PCM, pcm & 0x7fff);
-       else
-               ac97_write(w->codec, AC97_PCM, pcm | 0x8000);
+       mutex_unlock(&wm9712->lock);
 
-       if (l & 0x4 || r & 0x4)
-               ac97_write(w->codec, AC97_LINE, line & 0x7fff);
-       else
-               ac97_write(w->codec, AC97_LINE, line | 0x8000);
+       return change;
+}
 
-       if (l & 0x8 || r & 0x8)
-               ac97_write(w->codec, AC97_PHONE, phone & 0x7fff);
-       else
-               ac97_write(w->codec, AC97_PHONE, phone | 0x8000);
+static int wm9712_hp_mixer_get(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+       struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int shift, mixer;
 
-       if (l & 0x10 || r & 0x10)
-               ac97_write(w->codec, AC97_CD, aux & 0x7fff);
-       else
-               ac97_write(w->codec, AC97_CD, aux | 0x8000);
+       mixer = mc->shift >> 8;
+       shift = mc->shift & 0xff;
 
-       if (l & 0x20 || r & 0x20)
-               ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff);
-       else
-               ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000);
+       ucontrol->value.enumerated.item[0] =
+               (wm9712->hp_mixer[mixer] >> shift) & 1;
 
        return 0;
 }
 
+#define WM9712_HP_MIXER_CTRL(xname, xmixer, xshift) { \
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_volsw, \
+       .get = wm9712_hp_mixer_get, .put = wm9712_hp_mixer_put, \
+       .private_value = SOC_SINGLE_VALUE(SND_SOC_NOPM, \
+               (xmixer << 8) | xshift, 1, 0, 0) \
+}
+
 /* Left Headphone Mixers */
 static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = {
-       SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPL_MIXER, 5, 1, 0),
-       SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 4, 1, 0),
-       SOC_DAPM_SINGLE("Phone Bypass Switch", HPL_MIXER, 3, 1, 0),
-       SOC_DAPM_SINGLE("Line Bypass Switch", HPL_MIXER, 2, 1, 0),
-       SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 1, 1, 0),
-       SOC_DAPM_SINGLE("Mic Sidetone Switch", HPL_MIXER, 0, 1, 0),
+       WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPL_MIXER, 5),
+       WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 4),
+       WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPL_MIXER, 3),
+       WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPL_MIXER, 2),
+       WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 1),
+       WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPL_MIXER, 0),
 };
 
 /* Right Headphone Mixers */
 static const struct snd_kcontrol_new wm9712_hpr_mixer_controls[] = {
-       SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPR_MIXER, 5, 1, 0),
-       SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 4, 1, 0),
-       SOC_DAPM_SINGLE("Phone Bypass Switch", HPR_MIXER, 3, 1, 0),
-       SOC_DAPM_SINGLE("Line Bypass Switch", HPR_MIXER, 2, 1, 0),
-       SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 1, 1, 0),
-       SOC_DAPM_SINGLE("Mic Sidetone Switch", HPR_MIXER, 0, 1, 0),
+       WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPR_MIXER, 5),
+       WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 4),
+       WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPR_MIXER, 3),
+       WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPR_MIXER, 2),
+       WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 1),
+       WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPR_MIXER, 0),
 };
 
 /* Speaker Mixer */
@@ -299,12 +336,10 @@ SND_SOC_DAPM_MUX("Right Mic Select Source", SND_SOC_NOPM, 0, 0,
 SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0,
        &wm9712_diff_sel_controls),
 SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
-SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_INT_PAGING, 9, 1,
-       &wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls),
-       mixer_event, SND_SOC_DAPM_POST_REG),
-SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_INT_PAGING, 8, 1,
-       &wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls),
-        mixer_event, SND_SOC_DAPM_POST_REG),
+SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_INT_PAGING, 9, 1,
+       &wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls)),
+SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_INT_PAGING, 8, 1,
+       &wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls)),
 SND_SOC_DAPM_MIXER("Phone Mixer", AC97_INT_PAGING, 6, 1,
        &wm9712_phone_mixer_controls[0], ARRAY_SIZE(wm9712_phone_mixer_controls)),
 SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_INT_PAGING, 7, 1,
@@ -450,12 +485,13 @@ static const struct snd_soc_dapm_route wm9712_audio_map[] = {
 static unsigned int ac97_read(struct snd_soc_codec *codec,
        unsigned int reg)
 {
+       struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
        u16 *cache = codec->reg_cache;
 
        if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
                reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
                reg == AC97_REC_GAIN)
-               return soc_ac97_ops->read(codec->ac97, reg);
+               return soc_ac97_ops->read(wm9712->ac97, reg);
        else {
                reg = reg >> 1;
 
@@ -469,10 +505,10 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
 static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
        unsigned int val)
 {
+       struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
        u16 *cache = codec->reg_cache;
 
-       if (reg < 0x7c)
-               soc_ac97_ops->write(codec->ac97, reg, val);
+       soc_ac97_ops->write(wm9712->ac97, reg, val);
        reg = reg >> 1;
        if (reg < (ARRAY_SIZE(wm9712_reg)))
                cache[reg] = val;
@@ -532,7 +568,6 @@ static const struct snd_soc_dai_ops wm9712_dai_ops_aux = {
 static struct snd_soc_dai_driver wm9712_dai[] = {
 {
        .name = "wm9712-hifi",
-       .ac97_control = 1,
        .playback = {
                .stream_name = "HiFi Playback",
                .channels_min = 1,
@@ -581,21 +616,23 @@ static int wm9712_set_bias_level(struct snd_soc_codec *codec,
 
 static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
 {
+       struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+
        if (try_warm && soc_ac97_ops->warm_reset) {
-               soc_ac97_ops->warm_reset(codec->ac97);
+               soc_ac97_ops->warm_reset(wm9712->ac97);
                if (ac97_read(codec, 0) == wm9712_reg[0])
                        return 1;
        }
 
-       soc_ac97_ops->reset(codec->ac97);
+       soc_ac97_ops->reset(wm9712->ac97);
        if (soc_ac97_ops->warm_reset)
-               soc_ac97_ops->warm_reset(codec->ac97);
+               soc_ac97_ops->warm_reset(wm9712->ac97);
        if (ac97_read(codec, 0) != wm9712_reg[0])
                goto err;
        return 0;
 
 err:
-       printk(KERN_ERR "WM9712 AC97 reset failed\n");
+       dev_err(codec->dev, "Failed to reset: AC97 link error\n");
        return -EIO;
 }
 
@@ -607,14 +644,13 @@ static int wm9712_soc_suspend(struct snd_soc_codec *codec)
 
 static int wm9712_soc_resume(struct snd_soc_codec *codec)
 {
+       struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
        int i, ret;
        u16 *cache = codec->reg_cache;
 
        ret = wm9712_reset(codec, 1);
-       if (ret < 0) {
-               printk(KERN_ERR "could not reset AC97 codec\n");
+       if (ret < 0)
                return ret;
-       }
 
        wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
@@ -624,7 +660,7 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec)
                        if (i == AC97_INT_PAGING || i == AC97_POWERDOWN ||
                            (i > 0x58 && i != 0x5c))
                                continue;
-                       soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
+                       soc_ac97_ops->write(wm9712->ac97, i, cache[i>>1]);
                }
        }
 
@@ -633,37 +669,37 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec)
 
 static int wm9712_soc_probe(struct snd_soc_codec *codec)
 {
+       struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
        int ret = 0;
 
-       ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
-       if (ret < 0) {
-               printk(KERN_ERR "wm9712: failed to register AC97 codec\n");
+       wm9712->ac97 = snd_soc_new_ac97_codec(codec);
+       if (IS_ERR(wm9712->ac97)) {
+               ret = PTR_ERR(wm9712->ac97);
+               dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
                return ret;
        }
 
        ret = wm9712_reset(codec, 0);
-       if (ret < 0) {
-               printk(KERN_ERR "Failed to reset WM9712: AC97 link error\n");
+       if (ret < 0)
                goto reset_err;
-       }
 
        /* set alc mux to none */
        ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000);
 
        wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       snd_soc_add_codec_controls(codec, wm9712_snd_ac97_controls,
-                               ARRAY_SIZE(wm9712_snd_ac97_controls));
 
        return 0;
 
 reset_err:
-       snd_soc_free_ac97_codec(codec);
+       snd_soc_free_ac97_codec(wm9712->ac97);
        return ret;
 }
 
 static int wm9712_soc_remove(struct snd_soc_codec *codec)
 {
-       snd_soc_free_ac97_codec(codec);
+       struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+
+       snd_soc_free_ac97_codec(wm9712->ac97);
        return 0;
 }
 
@@ -679,6 +715,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = {
        .reg_word_size = sizeof(u16),
        .reg_cache_step = 2,
        .reg_cache_default = wm9712_reg,
+
+       .controls = wm9712_snd_ac97_controls,
+       .num_controls = ARRAY_SIZE(wm9712_snd_ac97_controls),
        .dapm_widgets = wm9712_dapm_widgets,
        .num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets),
        .dapm_routes = wm9712_audio_map,
@@ -687,6 +726,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = {
 
 static int wm9712_probe(struct platform_device *pdev)
 {
+       struct wm9712_priv *wm9712;
+
+       wm9712 = devm_kzalloc(&pdev->dev, sizeof(*wm9712), GFP_KERNEL);
+       if (wm9712 == NULL)
+               return -ENOMEM;
+
+       mutex_init(&wm9712->lock);
+
+       platform_set_drvdata(pdev, wm9712);
+
        return snd_soc_register_codec(&pdev->dev,
                        &soc_codec_dev_wm9712, wm9712_dai, ARRAY_SIZE(wm9712_dai));
 }
index bddee30..6c95d98 100644 (file)
 #include "wm9713.h"
 
 struct wm9713_priv {
+       struct snd_ac97 *ac97;
        u32 pll_in; /* PLL input frequency */
+       unsigned int hp_mixer[2];
+       struct mutex lock;
 };
 
 static unsigned int ac97_read(struct snd_soc_codec *codec,
@@ -59,13 +62,10 @@ static const u16 wm9713_reg[] = {
        0x0000, 0x0000, 0x0000, 0x0000,
        0x0000, 0x0000, 0x0000, 0x0006,
        0x0001, 0x0000, 0x574d, 0x4c13,
-       0x0000, 0x0000, 0x0000
 };
 
-/* virtual HP mixers regs */
-#define HPL_MIXER      0x80
-#define HPR_MIXER      0x82
-#define MICB_MUX       0x82
+#define HPL_MIXER 0
+#define HPR_MIXER 1
 
 static const char *wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"};
 static const char *wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"};
@@ -110,7 +110,7 @@ SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 10, 8, wm9713_dac_inv), /* dac invert 2 15 */
 SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, wm9713_bass), /* bass control 16 */
 SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9713_ng_type), /* noise gate type 17 */
 SOC_ENUM_SINGLE(AC97_3D_CONTROL, 12, 3, wm9713_mic_select), /* mic selection 18 */
-SOC_ENUM_SINGLE(MICB_MUX, 0, 2, wm9713_micb_select), /* mic selection 19 */
+SOC_ENUM_SINGLE_VIRT(2, wm9713_micb_select), /* mic selection 19 */
 };
 
 static const DECLARE_TLV_DB_SCALE(out_tlv, -4650, 150, 0);
@@ -234,6 +234,14 @@ static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
+static const unsigned int wm9713_mixer_mute_regs[] = {
+       AC97_PC_BEEP,
+       AC97_MASTER_TONE,
+       AC97_PHONE,
+       AC97_REC_SEL,
+       AC97_PCM,
+       AC97_AUX,
+};
 
 /* We have to create a fake left and right HP mixers because
  * the codec only has a single control that is shared by both channels.
@@ -241,73 +249,95 @@ static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w,
  * register map, thus we add a new (virtual) register to help determine the
  * audio route within the device.
  */
-static int mixer_event(struct snd_soc_dapm_widget *w,
-       struct snd_kcontrol *kcontrol, int event)
+static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
 {
-       u16 l, r, beep, tone, phone, rec, pcm, aux;
-
-       l = ac97_read(w->codec, HPL_MIXER);
-       r = ac97_read(w->codec, HPR_MIXER);
-       beep = ac97_read(w->codec, AC97_PC_BEEP);
-       tone = ac97_read(w->codec, AC97_MASTER_TONE);
-       phone = ac97_read(w->codec, AC97_PHONE);
-       rec = ac97_read(w->codec, AC97_REC_SEL);
-       pcm = ac97_read(w->codec, AC97_PCM);
-       aux = ac97_read(w->codec, AC97_AUX);
-
-       if (event & SND_SOC_DAPM_PRE_REG)
-               return 0;
-       if ((l & 0x1) || (r & 0x1))
-               ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff);
+       struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+       struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+       unsigned int val = ucontrol->value.enumerated.item[0];
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int mixer, mask, shift, old;
+       struct snd_soc_dapm_update update;
+       bool change;
+
+       mixer = mc->shift >> 8;
+       shift = mc->shift & 0xff;
+       mask = (1 << shift);
+
+       mutex_lock(&wm9713->lock);
+       old = wm9713->hp_mixer[mixer];
+       if (ucontrol->value.enumerated.item[0])
+               wm9713->hp_mixer[mixer] |= mask;
        else
-               ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000);
+               wm9713->hp_mixer[mixer] &= ~mask;
+
+       change = old != wm9713->hp_mixer[mixer];
+       if (change) {
+               update.kcontrol = kcontrol;
+               update.reg = wm9713_mixer_mute_regs[shift];
+               update.mask = 0x8000;
+               if ((wm9713->hp_mixer[0] & mask) ||
+                   (wm9713->hp_mixer[1] & mask))
+                       update.val = 0x0;
+               else
+                       update.val = 0x8000;
+
+               snd_soc_dapm_mixer_update_power(dapm, kcontrol, val,
+                       &update);
+       }
 
-       if ((l & 0x2) || (r & 0x2))
-               ac97_write(w->codec, AC97_MASTER_TONE, tone & 0x7fff);
-       else
-               ac97_write(w->codec, AC97_MASTER_TONE, tone | 0x8000);
+       mutex_unlock(&wm9713->lock);
 
-       if ((l & 0x4) || (r & 0x4))
-               ac97_write(w->codec, AC97_PHONE, phone & 0x7fff);
-       else
-               ac97_write(w->codec, AC97_PHONE, phone | 0x8000);
+       return change;
+}
 
-       if ((l & 0x8) || (r & 0x8))
-               ac97_write(w->codec, AC97_REC_SEL, rec & 0x7fff);
-       else
-               ac97_write(w->codec, AC97_REC_SEL, rec | 0x8000);
+static int wm9713_hp_mixer_get(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+       struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int mixer, shift;
 
-       if ((l & 0x10) || (r & 0x10))
-               ac97_write(w->codec, AC97_PCM, pcm & 0x7fff);
-       else
-               ac97_write(w->codec, AC97_PCM, pcm | 0x8000);
+       mixer = mc->shift >> 8;
+       shift = mc->shift & 0xff;
 
-       if ((l & 0x20) || (r & 0x20))
-               ac97_write(w->codec, AC97_AUX, aux & 0x7fff);
-       else
-               ac97_write(w->codec, AC97_AUX, aux | 0x8000);
+       ucontrol->value.enumerated.item[0] =
+               (wm9713->hp_mixer[mixer] >> shift) & 1;
 
        return 0;
 }
 
+#define WM9713_HP_MIXER_CTRL(xname, xmixer, xshift) { \
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_volsw, \
+       .get = wm9713_hp_mixer_get, .put = wm9713_hp_mixer_put, \
+       .private_value = SOC_DOUBLE_VALUE(SND_SOC_NOPM, \
+               xshift, xmixer, 1, 0, 0) \
+}
+
 /* Left Headphone Mixers */
 static const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = {
-SOC_DAPM_SINGLE("Beep Playback Switch", HPL_MIXER, 5, 1, 0),
-SOC_DAPM_SINGLE("Voice Playback Switch", HPL_MIXER, 4, 1, 0),
-SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 3, 1, 0),
-SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 2, 1, 0),
-SOC_DAPM_SINGLE("MonoIn Playback Switch", HPL_MIXER, 1, 1, 0),
-SOC_DAPM_SINGLE("Bypass Playback Switch", HPL_MIXER, 0, 1, 0),
+WM9713_HP_MIXER_CTRL("Beep Playback Switch", HPL_MIXER, 5),
+WM9713_HP_MIXER_CTRL("Voice Playback Switch", HPL_MIXER, 4),
+WM9713_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 3),
+WM9713_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 2),
+WM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPL_MIXER, 1),
+WM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPL_MIXER, 0),
 };
 
 /* Right Headphone Mixers */
 static const struct snd_kcontrol_new wm9713_hpr_mixer_controls[] = {
-SOC_DAPM_SINGLE("Beep Playback Switch", HPR_MIXER, 5, 1, 0),
-SOC_DAPM_SINGLE("Voice Playback Switch", HPR_MIXER, 4, 1, 0),
-SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 3, 1, 0),
-SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 2, 1, 0),
-SOC_DAPM_SINGLE("MonoIn Playback Switch", HPR_MIXER, 1, 1, 0),
-SOC_DAPM_SINGLE("Bypass Playback Switch", HPR_MIXER, 0, 1, 0),
+WM9713_HP_MIXER_CTRL("Beep Playback Switch", HPR_MIXER, 5),
+WM9713_HP_MIXER_CTRL("Voice Playback Switch", HPR_MIXER, 4),
+WM9713_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 3),
+WM9713_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 2),
+WM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPR_MIXER, 1),
+WM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPR_MIXER, 0),
 };
 
 /* headphone capture mux */
@@ -429,12 +459,10 @@ SND_SOC_DAPM_MUX("Mic A Source", SND_SOC_NOPM, 0, 0,
        &wm9713_mic_sel_mux_controls),
 SND_SOC_DAPM_MUX("Mic B Source", SND_SOC_NOPM, 0, 0,
        &wm9713_micb_sel_mux_controls),
-SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_EXTENDED_MID, 3, 1,
-       &wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls),
-       mixer_event, SND_SOC_DAPM_POST_REG),
-SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_EXTENDED_MID, 2, 1,
-       &wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls),
-       mixer_event, SND_SOC_DAPM_POST_REG),
+SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_EXTENDED_MID, 3, 1,
+       &wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls)),
+SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_EXTENDED_MID, 2, 1,
+       &wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls)),
 SND_SOC_DAPM_MIXER("Mono Mixer", AC97_EXTENDED_MID, 0, 1,
        &wm9713_mono_mixer_controls[0], ARRAY_SIZE(wm9713_mono_mixer_controls)),
 SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_EXTENDED_MID, 1, 1,
@@ -647,12 +675,13 @@ static const struct snd_soc_dapm_route wm9713_audio_map[] = {
 static unsigned int ac97_read(struct snd_soc_codec *codec,
        unsigned int reg)
 {
+       struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
        u16 *cache = codec->reg_cache;
 
        if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
                reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
                reg == AC97_CD)
-               return soc_ac97_ops->read(codec->ac97, reg);
+               return soc_ac97_ops->read(wm9713->ac97, reg);
        else {
                reg = reg >> 1;
 
@@ -666,9 +695,10 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
 static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
        unsigned int val)
 {
+       struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+
        u16 *cache = codec->reg_cache;
-       if (reg < 0x7c)
-               soc_ac97_ops->write(codec->ac97, reg, val);
+       soc_ac97_ops->write(wm9713->ac97, reg, val);
        reg = reg >> 1;
        if (reg < (ARRAY_SIZE(wm9713_reg)))
                cache[reg] = val;
@@ -689,7 +719,8 @@ struct _pll_div {
  * to allow rounding later */
 #define FIXED_PLL_SIZE ((1 << 22) * 10)
 
-static void pll_factors(struct _pll_div *pll_div, unsigned int source)
+static void pll_factors(struct snd_soc_codec *codec,
+       struct _pll_div *pll_div, unsigned int source)
 {
        u64 Kpart;
        unsigned int K, Ndiv, Nmod, target;
@@ -724,7 +755,7 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int source)
 
        Ndiv = target / source;
        if ((Ndiv < 5) || (Ndiv > 12))
-               printk(KERN_WARNING
+               dev_warn(codec->dev,
                        "WM9713 PLL N value %u out of recommended range!\n",
                        Ndiv);
 
@@ -768,7 +799,7 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
                return 0;
        }
 
-       pll_factors(&pll_div, freq_in);
+       pll_factors(codec, &pll_div, freq_in);
 
        if (pll_div.k == 0) {
                reg = (pll_div.n << 12) | (pll_div.lf << 11) |
@@ -1049,7 +1080,6 @@ static const struct snd_soc_dai_ops wm9713_dai_ops_voice = {
 static struct snd_soc_dai_driver wm9713_dai[] = {
 {
        .name = "wm9713-hifi",
-       .ac97_control = 1,
        .playback = {
                .stream_name = "HiFi Playback",
                .channels_min = 1,
@@ -1095,17 +1125,22 @@ static struct snd_soc_dai_driver wm9713_dai[] = {
 
 int wm9713_reset(struct snd_soc_codec *codec, int try_warm)
 {
+       struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+
        if (try_warm && soc_ac97_ops->warm_reset) {
-               soc_ac97_ops->warm_reset(codec->ac97);
+               soc_ac97_ops->warm_reset(wm9713->ac97);
                if (ac97_read(codec, 0) == wm9713_reg[0])
                        return 1;
        }
 
-       soc_ac97_ops->reset(codec->ac97);
+       soc_ac97_ops->reset(wm9713->ac97);
        if (soc_ac97_ops->warm_reset)
-               soc_ac97_ops->warm_reset(codec->ac97);
-       if (ac97_read(codec, 0) != wm9713_reg[0])
+               soc_ac97_ops->warm_reset(wm9713->ac97);
+       if (ac97_read(codec, 0) != wm9713_reg[0]) {
+               dev_err(codec->dev, "Failed to reset: AC97 link error\n");
                return -EIO;
+       }
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(wm9713_reset);
@@ -1163,10 +1198,8 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
        u16 *cache = codec->reg_cache;
 
        ret = wm9713_reset(codec, 1);
-       if (ret < 0) {
-               printk(KERN_ERR "could not reset AC97 codec\n");
+       if (ret < 0)
                return ret;
-       }
 
        wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
@@ -1180,7 +1213,7 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
                        if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID ||
                                i == AC97_EXTENDED_MSTATUS || i > 0x66)
                                continue;
-                       soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
+                       soc_ac97_ops->write(wm9713->ac97, i, cache[i>>1]);
                }
        }
 
@@ -1189,26 +1222,19 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
 
 static int wm9713_soc_probe(struct snd_soc_codec *codec)
 {
-       struct wm9713_priv *wm9713;
+       struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
        int ret = 0, reg;
 
-       wm9713 = kzalloc(sizeof(struct wm9713_priv), GFP_KERNEL);
-       if (wm9713 == NULL)
-               return -ENOMEM;
-       snd_soc_codec_set_drvdata(codec, wm9713);
-
-       ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
-       if (ret < 0)
-               goto codec_err;
+       wm9713->ac97 = snd_soc_new_ac97_codec(codec);
+       if (IS_ERR(wm9713->ac97))
+               return PTR_ERR(wm9713->ac97);
 
        /* do a cold reset for the controller and then try
         * a warm reset followed by an optional cold reset for codec */
        wm9713_reset(codec, 0);
        ret = wm9713_reset(codec, 1);
-       if (ret < 0) {
-               printk(KERN_ERR "Failed to reset WM9713: AC97 link error\n");
+       if (ret < 0)
                goto reset_err;
-       }
 
        wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
@@ -1216,23 +1242,18 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec)
        reg = ac97_read(codec, AC97_CD) & 0x7fff;
        ac97_write(codec, AC97_CD, reg);
 
-       snd_soc_add_codec_controls(codec, wm9713_snd_ac97_controls,
-                               ARRAY_SIZE(wm9713_snd_ac97_controls));
-
        return 0;
 
 reset_err:
-       snd_soc_free_ac97_codec(codec);
-codec_err:
-       kfree(wm9713);
+       snd_soc_free_ac97_codec(wm9713->ac97);
        return ret;
 }
 
 static int wm9713_soc_remove(struct snd_soc_codec *codec)
 {
        struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
-       snd_soc_free_ac97_codec(codec);
-       kfree(wm9713);
+
+       snd_soc_free_ac97_codec(wm9713->ac97);
        return 0;
 }
 
@@ -1248,6 +1269,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = {
        .reg_word_size = sizeof(u16),
        .reg_cache_step = 2,
        .reg_cache_default = wm9713_reg,
+
+       .controls = wm9713_snd_ac97_controls,
+       .num_controls = ARRAY_SIZE(wm9713_snd_ac97_controls),
        .dapm_widgets = wm9713_dapm_widgets,
        .num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets),
        .dapm_routes = wm9713_audio_map,
@@ -1256,6 +1280,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = {
 
 static int wm9713_probe(struct platform_device *pdev)
 {
+       struct wm9713_priv *wm9713;
+
+       wm9713 = devm_kzalloc(&pdev->dev, sizeof(*wm9713), GFP_KERNEL);
+       if (wm9713 == NULL)
+               return -ENOMEM;
+
+       mutex_init(&wm9713->lock);
+
+       platform_set_drvdata(pdev, wm9713);
+
        return snd_soc_register_codec(&pdev->dev,
                        &soc_codec_dev_wm9713, wm9713_dai, ARRAY_SIZE(wm9713_dai));
 }
index f412a99..720d6e8 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
+#include <linux/vmalloc.h>
 #include <linux/workqueue.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -169,11 +170,12 @@ static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len,
        if (buf == NULL)
                return NULL;
 
-       buf->buf = kmemdup(src, len, GFP_KERNEL | GFP_DMA);
+       buf->buf = vmalloc(len);
        if (!buf->buf) {
-               kfree(buf);
+               vfree(buf);
                return NULL;
        }
+       memcpy(buf->buf, src, len);
 
        if (list)
                list_add_tail(&buf->list, list);
@@ -188,7 +190,7 @@ static void wm_adsp_buf_free(struct list_head *list)
                                                           struct wm_adsp_buf,
                                                           list);
                list_del(&buf->list);
-               kfree(buf->buf);
+               vfree(buf->buf);
                kfree(buf);
        }
 }
@@ -684,38 +686,24 @@ static int wm_adsp_load(struct wm_adsp *dsp)
                }
 
                if (reg) {
-                       size_t to_write = PAGE_SIZE;
-                       size_t remain = le32_to_cpu(region->len);
-                       const u8 *data = region->data;
-
-                       while (remain > 0) {
-                               if (remain < PAGE_SIZE)
-                                       to_write = remain;
-
-                               buf = wm_adsp_buf_alloc(data,
-                                                       to_write,
-                                                       &buf_list);
-                               if (!buf) {
-                                       adsp_err(dsp, "Out of memory\n");
-                                       ret = -ENOMEM;
-                                       goto out_fw;
-                               }
-
-                               ret = regmap_raw_write_async(regmap, reg,
-                                                            buf->buf,
-                                                            to_write);
-                               if (ret != 0) {
-                                       adsp_err(dsp,
-                                               "%s.%d: Failed to write %zd bytes at %d in %s: %d\n",
-                                               file, regions,
-                                               to_write, offset,
-                                               region_name, ret);
-                                       goto out_fw;
-                               }
+                       buf = wm_adsp_buf_alloc(region->data,
+                                               le32_to_cpu(region->len),
+                                               &buf_list);
+                       if (!buf) {
+                               adsp_err(dsp, "Out of memory\n");
+                               ret = -ENOMEM;
+                               goto out_fw;
+                       }
 
-                               data += to_write;
-                               reg += to_write / 2;
-                               remain -= to_write;
+                       ret = regmap_raw_write_async(regmap, reg, buf->buf,
+                                                    le32_to_cpu(region->len));
+                       if (ret != 0) {
+                               adsp_err(dsp,
+                                       "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
+                                       file, regions,
+                                       le32_to_cpu(region->len), offset,
+                                       region_name, ret);
+                               goto out_fw;
                        }
                }
 
@@ -1065,8 +1053,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
                                  be32_to_cpu(adsp1_alg[i].zm));
 
                        region = kzalloc(sizeof(*region), GFP_KERNEL);
-                       if (!region)
-                               return -ENOMEM;
+                       if (!region) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
                        region->type = WMFW_ADSP1_DM;
                        region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
                        region->base = be32_to_cpu(adsp1_alg[i].dm);
@@ -1083,8 +1073,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
                        }
 
                        region = kzalloc(sizeof(*region), GFP_KERNEL);
-                       if (!region)
-                               return -ENOMEM;
+                       if (!region) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
                        region->type = WMFW_ADSP1_ZM;
                        region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
                        region->base = be32_to_cpu(adsp1_alg[i].zm);
@@ -1113,8 +1105,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
                                  be32_to_cpu(adsp2_alg[i].zm));
 
                        region = kzalloc(sizeof(*region), GFP_KERNEL);
-                       if (!region)
-                               return -ENOMEM;
+                       if (!region) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
                        region->type = WMFW_ADSP2_XM;
                        region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
                        region->base = be32_to_cpu(adsp2_alg[i].xm);
@@ -1131,8 +1125,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
                        }
 
                        region = kzalloc(sizeof(*region), GFP_KERNEL);
-                       if (!region)
-                               return -ENOMEM;
+                       if (!region) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
                        region->type = WMFW_ADSP2_YM;
                        region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
                        region->base = be32_to_cpu(adsp2_alg[i].ym);
@@ -1149,8 +1145,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
                        }
 
                        region = kzalloc(sizeof(*region), GFP_KERNEL);
-                       if (!region)
-                               return -ENOMEM;
+                       if (!region) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
                        region->type = WMFW_ADSP2_ZM;
                        region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
                        region->base = be32_to_cpu(adsp2_alg[i].zm);
@@ -1355,6 +1353,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
                          file, blocks, pos - firmware->size);
 
 out_fw:
+       regmap_async_complete(regmap);
        release_firmware(firmware);
        wm_adsp_buf_free(&buf_list);
 out:
@@ -1594,13 +1593,6 @@ static void wm_adsp2_boot_work(struct work_struct *work)
        if (ret != 0)
                goto err;
 
-       ret = regmap_update_bits_async(dsp->regmap,
-                                      dsp->base + ADSP2_CONTROL,
-                                      ADSP2_CORE_ENA,
-                                      ADSP2_CORE_ENA);
-       if (ret != 0)
-               goto err;
-
        dsp->running = true;
 
        return;
@@ -1650,8 +1642,8 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
 
                ret = regmap_update_bits(dsp->regmap,
                                         dsp->base + ADSP2_CONTROL,
-                                        ADSP2_START,
-                                        ADSP2_START);
+                                        ADSP2_CORE_ENA | ADSP2_START,
+                                        ADSP2_CORE_ENA | ADSP2_START);
                if (ret != 0)
                        goto err;
                break;
index 0eed9b1..0dab382 100644 (file)
@@ -70,6 +70,7 @@ struct davinci_mcasp {
        void __iomem *base;
        u32 fifo_base;
        struct device *dev;
+       struct snd_pcm_substream *substreams[2];
 
        /* McASP specific data */
        int     tdm_slots;
@@ -80,6 +81,7 @@ struct davinci_mcasp {
        u8      bclk_div;
        u16     bclk_lrclk_ratio;
        int     streams;
+       u32     irq_request[2];
 
        int     sysclk_freq;
        bool    bclk_master;
@@ -90,6 +92,9 @@ struct davinci_mcasp {
 
        bool    dat_port;
 
+       /* Used for comstraint setting on the second stream */
+       u32     channels;
+
 #ifdef CONFIG_PM_SLEEP
        struct davinci_mcasp_context context;
 #endif
@@ -154,9 +159,16 @@ static bool mcasp_is_synchronous(struct davinci_mcasp *mcasp)
 
 static void mcasp_start_rx(struct davinci_mcasp *mcasp)
 {
+       if (mcasp->rxnumevt) {  /* enable FIFO */
+               u32 reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+
+               mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+               mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
+       }
+
+       /* Start clocks */
        mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXHCLKRST);
        mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXCLKRST);
-
        /*
         * When ASYNC == 0 the transmit and receive sections operate
         * synchronously from the transmit clock and frame sync. We need to make
@@ -167,74 +179,69 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp)
                mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
        }
 
+       /* Activate serializer(s) */
        mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSERCLR);
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_RXBUF_REG, 0);
-
-       mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
-       mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_RXBUF_REG, 0);
-
+       /* Release RX state machine */
        mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
+       /* Release Frame Sync generator */
        mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
-
        if (mcasp_is_synchronous(mcasp))
                mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
+
+       /* enable receive IRQs */
+       mcasp_set_bits(mcasp, DAVINCI_MCASP_EVTCTLR_REG,
+                      mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]);
 }
 
 static void mcasp_start_tx(struct davinci_mcasp *mcasp)
 {
-       u8 offset = 0, i;
        u32 cnt;
 
+       if (mcasp->txnumevt) {  /* enable FIFO */
+               u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+
+               mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+               mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
+       }
+
+       /* Start clocks */
        mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
        mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
+       /* Activate serializer(s) */
        mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR);
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0);
 
-       mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST);
-       mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0);
-       for (i = 0; i < mcasp->num_serializer; i++) {
-               if (mcasp->serial_dir[i] == TX_MODE) {
-                       offset = i;
-                       break;
-               }
-       }
-
-       /* wait for TX ready */
+       /* wait for XDATA to be cleared */
        cnt = 0;
-       while (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(offset)) &
-                TXSTATE) && (cnt < 100000))
+       while (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) &
+                ~XRDATA) && (cnt < 100000))
                cnt++;
 
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0);
+       /* Release TX state machine */
+       mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST);
+       /* Release Frame Sync generator */
+       mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
+
+       /* enable transmit IRQs */
+       mcasp_set_bits(mcasp, DAVINCI_MCASP_EVTCTLX_REG,
+                      mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]);
 }
 
 static void davinci_mcasp_start(struct davinci_mcasp *mcasp, int stream)
 {
-       u32 reg;
-
        mcasp->streams++;
 
-       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               if (mcasp->txnumevt) {  /* enable FIFO */
-                       reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
-                       mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
-                       mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
-               }
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
                mcasp_start_tx(mcasp);
-       } else {
-               if (mcasp->rxnumevt) {  /* enable FIFO */
-                       reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
-                       mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
-                       mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
-               }
+       else
                mcasp_start_rx(mcasp);
-       }
 }
 
 static void mcasp_stop_rx(struct davinci_mcasp *mcasp)
 {
+       /* disable IRQ sources */
+       mcasp_clr_bits(mcasp, DAVINCI_MCASP_EVTCTLR_REG,
+                      mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]);
+
        /*
         * In synchronous mode stop the TX clocks if no other stream is
         * running
@@ -244,12 +251,22 @@ static void mcasp_stop_rx(struct davinci_mcasp *mcasp)
 
        mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, 0);
        mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
+
+       if (mcasp->rxnumevt) {  /* disable FIFO */
+               u32 reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+
+               mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+       }
 }
 
 static void mcasp_stop_tx(struct davinci_mcasp *mcasp)
 {
        u32 val = 0;
 
+       /* disable IRQ sources */
+       mcasp_clr_bits(mcasp, DAVINCI_MCASP_EVTCTLX_REG,
+                      mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]);
+
        /*
         * In synchronous mode keep TX clocks running if the capture stream is
         * still running.
@@ -259,27 +276,92 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp)
 
        mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, val);
        mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
+
+       if (mcasp->txnumevt) {  /* disable FIFO */
+               u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+
+               mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+       }
 }
 
 static void davinci_mcasp_stop(struct davinci_mcasp *mcasp, int stream)
 {
-       u32 reg;
-
        mcasp->streams--;
 
-       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               if (mcasp->txnumevt) {  /* disable FIFO */
-                       reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
-                       mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
-               }
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
                mcasp_stop_tx(mcasp);
-       } else {
-               if (mcasp->rxnumevt) {  /* disable FIFO */
-                       reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
-                       mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
-               }
+       else
                mcasp_stop_rx(mcasp);
+}
+
+static irqreturn_t davinci_mcasp_tx_irq_handler(int irq, void *data)
+{
+       struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data;
+       struct snd_pcm_substream *substream;
+       u32 irq_mask = mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK];
+       u32 handled_mask = 0;
+       u32 stat;
+
+       stat = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG);
+       if (stat & XUNDRN & irq_mask) {
+               dev_warn(mcasp->dev, "Transmit buffer underflow\n");
+               handled_mask |= XUNDRN;
+
+               substream = mcasp->substreams[SNDRV_PCM_STREAM_PLAYBACK];
+               if (substream) {
+                       snd_pcm_stream_lock_irq(substream);
+                       if (snd_pcm_running(substream))
+                               snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+                       snd_pcm_stream_unlock_irq(substream);
+               }
        }
+
+       if (!handled_mask)
+               dev_warn(mcasp->dev, "unhandled tx event. txstat: 0x%08x\n",
+                        stat);
+
+       if (stat & XRERR)
+               handled_mask |= XRERR;
+
+       /* Ack the handled event only */
+       mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, handled_mask);
+
+       return IRQ_RETVAL(handled_mask);
+}
+
+static irqreturn_t davinci_mcasp_rx_irq_handler(int irq, void *data)
+{
+       struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data;
+       struct snd_pcm_substream *substream;
+       u32 irq_mask = mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE];
+       u32 handled_mask = 0;
+       u32 stat;
+
+       stat = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG);
+       if (stat & ROVRN & irq_mask) {
+               dev_warn(mcasp->dev, "Receive buffer overflow\n");
+               handled_mask |= ROVRN;
+
+               substream = mcasp->substreams[SNDRV_PCM_STREAM_CAPTURE];
+               if (substream) {
+                       snd_pcm_stream_lock_irq(substream);
+                       if (snd_pcm_running(substream))
+                               snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+                       snd_pcm_stream_unlock_irq(substream);
+               }
+       }
+
+       if (!handled_mask)
+               dev_warn(mcasp->dev, "unhandled rx event. rxstat: 0x%08x\n",
+                        stat);
+
+       if (stat & XRERR)
+               handled_mask |= XRERR;
+
+       /* Ack the handled event only */
+       mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, handled_mask);
+
+       return IRQ_RETVAL(handled_mask);
 }
 
 static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
@@ -500,8 +582,17 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
         * both left and right channels), so it has to be divided by number of
         * tdm-slots (for I2S - divided by 2).
         */
-       if (mcasp->bclk_lrclk_ratio)
-               word_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots;
+       if (mcasp->bclk_lrclk_ratio) {
+               u32 slot_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots;
+
+               /*
+                * When we have more bclk then it is needed for the data, we
+                * need to use the rotation to move the received samples to have
+                * correct alignment.
+                */
+               rx_rotate = (slot_length - word_length) / 4;
+               word_length = slot_length;
+       }
 
        /* mapping of the XSSZ bit-field as described in the datasheet */
        fmt = (word_length >> 1) - 1;
@@ -635,19 +726,29 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream,
        return 0;
 }
 
-static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream)
+static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream,
+                             int channels)
 {
        int i, active_slots;
+       int total_slots;
+       int active_serializers;
        u32 mask = 0;
        u32 busel = 0;
 
-       if ((mcasp->tdm_slots < 2) || (mcasp->tdm_slots > 32)) {
-               dev_err(mcasp->dev, "tdm slot %d not supported\n",
-                       mcasp->tdm_slots);
-               return -EINVAL;
-       }
+       total_slots = mcasp->tdm_slots;
+
+       /*
+        * If more than one serializer is needed, then use them with
+        * their specified tdm_slots count. Otherwise, one serializer
+        * can cope with the transaction using as many slots as channels
+        * in the stream, requires channels symmetry
+        */
+       active_serializers = (channels + total_slots - 1) / total_slots;
+       if (active_serializers == 1)
+               active_slots = channels;
+       else
+               active_slots = total_slots;
 
-       active_slots = (mcasp->tdm_slots > 31) ? 32 : mcasp->tdm_slots;
        for (i = 0; i < active_slots; i++)
                mask |= (1 << i);
 
@@ -659,12 +760,12 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream)
        mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask);
        mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD);
        mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
-                      FSXMOD(mcasp->tdm_slots), FSXMOD(0x1FF));
+                      FSXMOD(total_slots), FSXMOD(0x1FF));
 
        mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask);
        mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD);
        mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG,
-                      FSRMOD(mcasp->tdm_slots), FSRMOD(0x1FF));
+                      FSRMOD(total_slots), FSRMOD(0x1FF));
 
        return 0;
 }
@@ -778,7 +879,8 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
        if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
                ret = mcasp_dit_hw_param(mcasp, params_rate(params));
        else
-               ret = mcasp_i2s_hw_param(mcasp, substream->stream);
+               ret = mcasp_i2s_hw_param(mcasp, substream->stream,
+                                        channels);
 
        if (ret)
                return ret;
@@ -826,6 +928,9 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
 
        davinci_config_channel_size(mcasp, word_length);
 
+       if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE)
+               mcasp->channels = channels;
+
        return 0;
 }
 
@@ -854,7 +959,65 @@ static int davinci_mcasp_trigger(struct snd_pcm_substream *substream,
        return ret;
 }
 
+static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
+                                struct snd_soc_dai *cpu_dai)
+{
+       struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
+       u32 max_channels = 0;
+       int i, dir;
+
+       mcasp->substreams[substream->stream] = substream;
+
+       if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
+               return 0;
+
+       /*
+        * Limit the maximum allowed channels for the first stream:
+        * number of serializers for the direction * tdm slots per serializer
+        */
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               dir = TX_MODE;
+       else
+               dir = RX_MODE;
+
+       for (i = 0; i < mcasp->num_serializer; i++) {
+               if (mcasp->serial_dir[i] == dir)
+                       max_channels++;
+       }
+       max_channels *= mcasp->tdm_slots;
+       /*
+        * If the already active stream has less channels than the calculated
+        * limnit based on the seirializers * tdm_slots, we need to use that as
+        * a constraint for the second stream.
+        * Otherwise (first stream or less allowed channels) we use the
+        * calculated constraint.
+        */
+       if (mcasp->channels && mcasp->channels < max_channels)
+               max_channels = mcasp->channels;
+
+       snd_pcm_hw_constraint_minmax(substream->runtime,
+                                    SNDRV_PCM_HW_PARAM_CHANNELS,
+                                    2, max_channels);
+       return 0;
+}
+
+static void davinci_mcasp_shutdown(struct snd_pcm_substream *substream,
+                                  struct snd_soc_dai *cpu_dai)
+{
+       struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
+
+       mcasp->substreams[substream->stream] = NULL;
+
+       if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
+               return;
+
+       if (!cpu_dai->active)
+               mcasp->channels = 0;
+}
+
 static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
+       .startup        = davinci_mcasp_startup,
+       .shutdown       = davinci_mcasp_shutdown,
        .trigger        = davinci_mcasp_trigger,
        .hw_params      = davinci_mcasp_hw_params,
        .set_fmt        = davinci_mcasp_set_dai_fmt,
@@ -971,6 +1134,7 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
                },
                .ops            = &davinci_mcasp_dai_ops,
 
+               .symmetric_samplebits   = 1,
        },
        {
                .name           = "davinci-mcasp.1",
@@ -1194,6 +1358,8 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
        struct resource *mem, *ioarea, *res, *dat;
        struct davinci_mcasp_pdata *pdata;
        struct davinci_mcasp *mcasp;
+       char *irq_name;
+       int irq;
        int ret;
 
        if (!pdev->dev.platform_data && !pdev->dev.of_node) {
@@ -1235,6 +1401,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
        ret = pm_runtime_get_sync(&pdev->dev);
        if (IS_ERR_VALUE(ret)) {
                dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
+               pm_runtime_disable(&pdev->dev);
                return ret;
        }
 
@@ -1246,7 +1413,21 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
        }
 
        mcasp->op_mode = pdata->op_mode;
-       mcasp->tdm_slots = pdata->tdm_slots;
+       /* sanity check for tdm slots parameter */
+       if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) {
+               if (pdata->tdm_slots < 2) {
+                       dev_err(&pdev->dev, "invalid tdm slots: %d\n",
+                               pdata->tdm_slots);
+                       mcasp->tdm_slots = 2;
+               } else if (pdata->tdm_slots > 32) {
+                       dev_err(&pdev->dev, "invalid tdm slots: %d\n",
+                               pdata->tdm_slots);
+                       mcasp->tdm_slots = 32;
+               } else {
+                       mcasp->tdm_slots = pdata->tdm_slots;
+               }
+       }
+
        mcasp->num_serializer = pdata->num_serializer;
 #ifdef CONFIG_PM_SLEEP
        mcasp->context.xrsr_regs = devm_kzalloc(&pdev->dev,
@@ -1260,6 +1441,36 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
 
        mcasp->dev = &pdev->dev;
 
+       irq = platform_get_irq_byname(pdev, "rx");
+       if (irq >= 0) {
+               irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_rx\n",
+                                         dev_name(&pdev->dev));
+               ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+                                               davinci_mcasp_rx_irq_handler,
+                                               IRQF_ONESHOT, irq_name, mcasp);
+               if (ret) {
+                       dev_err(&pdev->dev, "RX IRQ request failed\n");
+                       goto err;
+               }
+
+               mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE] = ROVRN;
+       }
+
+       irq = platform_get_irq_byname(pdev, "tx");
+       if (irq >= 0) {
+               irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_tx\n",
+                                         dev_name(&pdev->dev));
+               ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+                                               davinci_mcasp_tx_irq_handler,
+                                               IRQF_ONESHOT, irq_name, mcasp);
+               if (ret) {
+                       dev_err(&pdev->dev, "TX IRQ request failed\n");
+                       goto err;
+               }
+
+               mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK] = XUNDRN;
+       }
+
        dat = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat");
        if (dat)
                mcasp->dat_port = true;
index 98fbc45..79dc511 100644 (file)
 #define TXSMRST                BIT(11) /* Transmitter State Machine Reset */
 #define TXFSRST                BIT(12) /* Frame Sync Generator Reset */
 
+/*
+ * DAVINCI_MCASP_TXSTAT_REG - Transmitter Status Register Bits
+ * DAVINCI_MCASP_RXSTAT_REG - Receiver Status Register Bits
+ */
+#define XRERR          BIT(8) /* Transmit/Receive error */
+#define XRDATA         BIT(5) /* Transmit/Receive data ready */
+
 /*
  * DAVINCI_MCASP_AMUTE_REG -  Mute Control Register Bits
  */
  */
 #define TXDATADMADIS   BIT(0)
 
+/*
+ * DAVINCI_MCASP_EVTCTLR_REG - Receiver Interrupt Control Register Bits
+ */
+#define ROVRN          BIT(0)
+
+/*
+ * DAVINCI_MCASP_EVTCTLX_REG - Transmitter Interrupt Control Register Bits
+ */
+#define XUNDRN         BIT(0)
+
 /*
  * DAVINCI_MCASP_W[R]FIFOCTL - Write/Read FIFO Control Register bits
  */
index e961388..08f0229 100644 (file)
@@ -338,31 +338,34 @@ static int dw_i2s_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               dev_err(&pdev->dev, "no i2s resource defined\n");
-               return -ENODEV;
-       }
-
-       if (!devm_request_mem_region(&pdev->dev, res->start,
-                               resource_size(res), pdev->name)) {
-               dev_err(&pdev->dev, "i2s region already claimed\n");
-               return -EBUSY;
-       }
-
        dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
        if (!dev) {
                dev_warn(&pdev->dev, "kzalloc fail\n");
                return -ENOMEM;
        }
 
-       dev->i2s_base = devm_ioremap(&pdev->dev, res->start,
-                       resource_size(res));
-       if (!dev->i2s_base) {
-               dev_err(&pdev->dev, "ioremap fail for i2s_region\n");
+       dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL);
+       if (!dw_i2s_dai) {
+               dev_err(&pdev->dev, "mem allocation failed for dai driver\n");
                return -ENOMEM;
        }
 
+       dw_i2s_dai->ops = &dw_i2s_dai_ops;
+       dw_i2s_dai->suspend = dw_i2s_suspend;
+       dw_i2s_dai->resume = dw_i2s_resume;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "no i2s resource defined\n");
+               return -ENODEV;
+       }
+
+       dev->i2s_base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(dev->i2s_base)) {
+               dev_err(&pdev->dev, "ioremap fail for i2s_region\n");
+               return PTR_ERR(dev->i2s_base);
+       }
+
        cap = pdata->cap;
        dev->capability = cap;
        dev->i2s_clk_cfg = pdata->i2s_clk_cfg;
@@ -388,13 +391,6 @@ static int dw_i2s_probe(struct platform_device *pdev)
        if (ret < 0)
                goto err_clk_put;
 
-       dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL);
-       if (!dw_i2s_dai) {
-               dev_err(&pdev->dev, "mem allocation failed for dai driver\n");
-               ret = -ENOMEM;
-               goto err_clk_disable;
-       }
-
        if (cap & DWC_I2S_PLAY) {
                dev_dbg(&pdev->dev, " designware: play supported\n");
                dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM;
@@ -411,10 +407,6 @@ static int dw_i2s_probe(struct platform_device *pdev)
                dw_i2s_dai->capture.rates = pdata->snd_rates;
        }
 
-       dw_i2s_dai->ops = &dw_i2s_dai_ops;
-       dw_i2s_dai->suspend = dw_i2s_suspend;
-       dw_i2s_dai->resume = dw_i2s_resume;
-
        dev->dev = &pdev->dev;
        dev_set_drvdata(&pdev->dev, dev);
        ret = snd_soc_register_component(&pdev->dev, &dw_i2s_component,
index eb093d5..b175b01 100644 (file)
@@ -105,7 +105,7 @@ static int eukrea_tlv320_probe(struct platform_device *pdev)
        int ret;
        int int_port = 0, ext_port;
        struct device_node *np = pdev->dev.of_node;
-       struct device_node *ssi_np, *codec_np;
+       struct device_node *ssi_np = NULL, *codec_np = NULL;
 
        eukrea_tlv320.dev = &pdev->dev;
        if (np) {
@@ -217,8 +217,7 @@ static int eukrea_tlv320_probe(struct platform_device *pdev)
 err:
        if (ret)
                dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
-       if (np)
-               of_node_put(ssi_np);
+       of_node_put(ssi_np);
 
        return ret;
 }
index 007c772..3f6959c 100644 (file)
@@ -51,6 +51,7 @@ struct codec_priv {
  * @sysclk_freq[2]: SYSCLK rates for set_sysclk()
  * @sysclk_dir[2]: SYSCLK directions for set_sysclk()
  * @sysclk_id[2]: SYSCLK ids for set_sysclk()
+ * @slot_width: Slot width of each frame
  *
  * Note: [1] for tx and [0] for rx
  */
@@ -58,6 +59,7 @@ struct cpu_priv {
        unsigned long sysclk_freq[2];
        u32 sysclk_dir[2];
        u32 sysclk_id[2];
+       u32 slot_width;
 };
 
 /**
@@ -125,7 +127,12 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
        priv->sample_rate = params_rate(params);
        priv->sample_format = params_format(params);
 
-       if (priv->card.set_bias_level)
+       /*
+        * If codec-dai is DAI Master and all configurations are already in the
+        * set_bias_level(), bypass the remaining settings in hw_params().
+        * Note: (dai_fmt & CBM_CFM) includes CBM_CFM and CBM_CFS.
+        */
+       if (priv->card.set_bias_level && priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM)
                return 0;
 
        /* Specific configurations of DAIs starts from here */
@@ -137,6 +144,15 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
                return ret;
        }
 
+       if (cpu_priv->slot_width) {
+               ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2,
+                                              cpu_priv->slot_width);
+               if (ret) {
+                       dev_err(dev, "failed to set TDM slot for cpu dai\n");
+                       return ret;
+               }
+       }
+
        return 0;
 }
 
@@ -448,6 +464,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
                priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv.mclk_freq;
                priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT;
                priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT;
+               priv->cpu_priv.slot_width = 32;
                priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
        } else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) {
                priv->codec_priv.mclk_id = SGTL5000_SYSCLK;
index ed866e9..9deabdd 100644 (file)
@@ -684,12 +684,38 @@ static bool fsl_asrc_writeable_reg(struct device *dev, unsigned int reg)
        }
 }
 
+static struct reg_default fsl_asrc_reg[] = {
+       { REG_ASRCTR, 0x0000 }, { REG_ASRIER, 0x0000 },
+       { REG_ASRCNCR, 0x0000 }, { REG_ASRCFG, 0x0000 },
+       { REG_ASRCSR, 0x0000 }, { REG_ASRCDR1, 0x0000 },
+       { REG_ASRCDR2, 0x0000 }, { REG_ASRSTR, 0x0000 },
+       { REG_ASRRA, 0x0000 }, { REG_ASRRB, 0x0000 },
+       { REG_ASRRC, 0x0000 }, { REG_ASRPM1, 0x0000 },
+       { REG_ASRPM2, 0x0000 }, { REG_ASRPM3, 0x0000 },
+       { REG_ASRPM4, 0x0000 }, { REG_ASRPM5, 0x0000 },
+       { REG_ASRTFR1, 0x0000 }, { REG_ASRCCR, 0x0000 },
+       { REG_ASRDIA, 0x0000 }, { REG_ASRDOA, 0x0000 },
+       { REG_ASRDIB, 0x0000 }, { REG_ASRDOB, 0x0000 },
+       { REG_ASRDIC, 0x0000 }, { REG_ASRDOC, 0x0000 },
+       { REG_ASRIDRHA, 0x0000 }, { REG_ASRIDRLA, 0x0000 },
+       { REG_ASRIDRHB, 0x0000 }, { REG_ASRIDRLB, 0x0000 },
+       { REG_ASRIDRHC, 0x0000 }, { REG_ASRIDRLC, 0x0000 },
+       { REG_ASR76K, 0x0A47 }, { REG_ASR56K, 0x0DF3 },
+       { REG_ASRMCRA, 0x0000 }, { REG_ASRFSTA, 0x0000 },
+       { REG_ASRMCRB, 0x0000 }, { REG_ASRFSTB, 0x0000 },
+       { REG_ASRMCRC, 0x0000 }, { REG_ASRFSTC, 0x0000 },
+       { REG_ASRMCR1A, 0x0000 }, { REG_ASRMCR1B, 0x0000 },
+       { REG_ASRMCR1C, 0x0000 },
+};
+
 static const struct regmap_config fsl_asrc_regmap_config = {
        .reg_bits = 32,
        .reg_stride = 4,
        .val_bits = 32,
 
        .max_register = REG_ASRMCR1C,
+       .reg_defaults = fsl_asrc_reg,
+       .num_reg_defaults = ARRAY_SIZE(fsl_asrc_reg),
        .readable_reg = fsl_asrc_readable_reg,
        .volatile_reg = fsl_asrc_volatile_reg,
        .writeable_reg = fsl_asrc_writeable_reg,
index a645e29..ca319d5 100644 (file)
@@ -513,10 +513,15 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
        u32 width = snd_pcm_format_width(params_format(params));
        u32 channels = params_channels(params);
        u32 pins = DIV_ROUND_UP(channels, esai_priv->slots);
+       u32 slot_width = width;
        u32 bclk, mask, val;
        int ret;
 
-       bclk = params_rate(params) * esai_priv->slot_width * esai_priv->slots;
+       /* Override slot_width if being specifially set */
+       if (esai_priv->slot_width)
+               slot_width = esai_priv->slot_width;
+
+       bclk = params_rate(params) * slot_width * esai_priv->slots;
 
        ret = fsl_esai_set_bclk(dai, tx, bclk);
        if (ret)
@@ -538,7 +543,7 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
        regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), mask, val);
 
        mask = ESAI_xCR_xSWS_MASK | (tx ? ESAI_xCR_PADC : 0);
-       val = ESAI_xCR_xSWS(esai_priv->slot_width, width) | (tx ? ESAI_xCR_PADC : 0);
+       val = ESAI_xCR_xSWS(slot_width, width) | (tx ? ESAI_xCR_PADC : 0);
 
        regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val);
 
@@ -780,9 +785,6 @@ static int fsl_esai_probe(struct platform_device *pdev)
                return ret;
        }
 
-       /* Set a default slot size */
-       esai_priv->slot_width = 32;
-
        /* Set a default slot number */
        esai_priv->slots = 2;
 
index e695517..b6b0d25 100644 (file)
@@ -67,8 +67,6 @@
 /**
  * FSLSSI_I2S_FORMATS: audio formats supported by the SSI
  *
- * This driver currently only supports the SSI running in I2S slave mode.
- *
  * The SSI has a limitation in that the samples must be in the same byte
  * order as the host CPU.  This is because when multiple bytes are written
  * to the STX register, the bytes and bits must be written in the same
@@ -1099,7 +1097,7 @@ static const struct snd_soc_component_driver fsl_ssi_component = {
 };
 
 static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
-       .ac97_control = 1,
+       .bus_control = true,
        .playback = {
                .stream_name = "AC97 Playback",
                .channels_min = 2,
@@ -1363,7 +1361,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
                return PTR_ERR(ssi_private->regs);
        }
 
-       ssi_private->irq = irq_of_parse_and_map(np, 0);
+       ssi_private->irq = platform_get_irq(pdev, 0);
        if (!ssi_private->irq) {
                dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
                return -ENXIO;
@@ -1389,7 +1387,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
        if (ssi_private->soc->imx) {
                ret = fsl_ssi_imx_probe(pdev, ssi_private, iomem);
                if (ret)
-                       goto error_irqmap;
+                       return ret;
        }
 
        ret = snd_soc_register_component(&pdev->dev, &fsl_ssi_component,
@@ -1412,7 +1410,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
 
        ret = fsl_ssi_debugfs_create(&ssi_private->dbg_stats, &pdev->dev);
        if (ret)
-               goto error_asoc_register;
+               goto error_irq;
 
        /*
         * If codec-handle property is missing from SSI node, we assume
@@ -1460,10 +1458,6 @@ error_asoc_register:
        if (ssi_private->soc->imx)
                fsl_ssi_imx_clean(pdev, ssi_private);
 
-error_irqmap:
-       if (ssi_private->use_dma)
-               irq_dispose_mapping(ssi_private->irq);
-
        return ret;
 }
 
@@ -1480,9 +1474,6 @@ static int fsl_ssi_remove(struct platform_device *pdev)
        if (ssi_private->soc->imx)
                fsl_ssi_imx_clean(pdev, ssi_private);
 
-       if (ssi_private->use_dma)
-               irq_dispose_mapping(ssi_private->irq);
-
        return 0;
 }
 
index 1cb22dd..1dab963 100644 (file)
@@ -175,10 +175,8 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
 fail:
        if (data && !IS_ERR(data->codec_clk))
                clk_put(data->codec_clk);
-       if (ssi_np)
-               of_node_put(ssi_np);
-       if (codec_np)
-               of_node_put(codec_np);
+       of_node_put(ssi_np);
+       of_node_put(codec_np);
 
        return ret;
 }
index e1dc401..0c9068e 100644 (file)
@@ -74,8 +74,7 @@ static int imx_spdif_audio_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, data);
 
 end:
-       if (spdif_np)
-               of_node_put(spdif_np);
+       of_node_put(spdif_np);
 
        return ret;
 }
index ab2fdd7..60b0a5b 100644 (file)
@@ -382,7 +382,7 @@ static struct snd_soc_dai_driver imx_ssi_dai = {
 
 static struct snd_soc_dai_driver imx_ac97_dai = {
        .probe = imx_ssi_dai_probe,
-       .ac97_control = 1,
+       .bus_control = true,
        .playback = {
                .stream_name = "AC97 Playback",
                .channels_min = 2,
index 3a3d17c..48179ff 100644 (file)
@@ -281,10 +281,8 @@ static int imx_wm8962_probe(struct platform_device *pdev)
 clk_fail:
        clk_disable_unprepare(data->codec_clk);
 fail:
-       if (ssi_np)
-               of_node_put(ssi_np);
-       if (codec_np)
-               of_node_put(codec_np);
+       of_node_put(ssi_np);
+       of_node_put(codec_np);
 
        return ret;
 }
index f2b5d75..0b82e20 100644 (file)
@@ -327,9 +327,6 @@ static int psc_dma_new(struct snd_soc_pcm_runtime *rtd)
                        goto capture_alloc_err;
        }
 
-       if (rtd->codec->ac97)
-               rtd->codec->ac97->private_data = psc_dma;
-
        return 0;
 
  capture_alloc_err:
index 24eafa2..c6ed6ba 100644 (file)
@@ -237,7 +237,7 @@ static const struct snd_soc_dai_ops psc_ac97_digital_ops = {
 static struct snd_soc_dai_driver psc_ac97_dai[] = {
 {
        .name = "mpc5200-psc-ac97.0",
-       .ac97_control = 1,
+       .bus_control = true,
        .probe  = psc_ac97_probe,
        .playback = {
                .stream_name    = "AC97 Playback",
@@ -257,7 +257,7 @@ static struct snd_soc_dai_driver psc_ac97_dai[] = {
 },
 {
        .name = "mpc5200-psc-ac97.1",
-       .ac97_control = 1,
+       .bus_control = true,
        .playback = {
                .stream_name    = "AC97 SPDIF",
                .channels_min   = 1,
@@ -282,7 +282,6 @@ static const struct snd_soc_component_driver psc_ac97_component = {
 static int psc_ac97_of_probe(struct platform_device *op)
 {
        int rc;
-       struct snd_ac97 ac97;
        struct mpc52xx_psc __iomem *regs;
 
        rc = mpc5200_audio_dma_create(op);
@@ -304,7 +303,6 @@ static int psc_ac97_of_probe(struct platform_device *op)
 
        psc_dma = dev_get_drvdata(&op->dev);
        regs = psc_dma->psc_regs;
-       ac97.private_data = psc_dma;
 
        psc_dma->imr = 0;
        out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr);
index d1b7293..3e3fec4 100644 (file)
@@ -29,7 +29,9 @@ struct simple_card_data {
        } *dai_props;
        unsigned int mclk_fs;
        int gpio_hp_det;
+       int gpio_hp_det_invert;
        int gpio_mic_det;
+       int gpio_mic_det_invert;
        struct snd_soc_dai_link dai_link[];     /* dynamically allocated */
 };
 
@@ -148,6 +150,7 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
                                      simple_card_hp_jack_pins);
 
                simple_card_hp_jack_gpio.gpio = priv->gpio_hp_det;
+               simple_card_hp_jack_gpio.invert = priv->gpio_hp_det_invert;
                snd_soc_jack_add_gpios(&simple_card_hp_jack, 1,
                                       &simple_card_hp_jack_gpio);
        }
@@ -159,6 +162,7 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
                                      ARRAY_SIZE(simple_card_mic_jack_pins),
                                      simple_card_mic_jack_pins);
                simple_card_mic_jack_gpio.gpio = priv->gpio_mic_det;
+               simple_card_mic_jack_gpio.invert = priv->gpio_mic_det_invert;
                snd_soc_jack_add_gpios(&simple_card_mic_jack, 1,
                                       &simple_card_mic_jack_gpio);
        }
@@ -226,6 +230,53 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
        return 0;
 }
 
+static int asoc_simple_card_parse_daifmt(struct device_node *node,
+                                        struct simple_card_data *priv,
+                                        struct device_node *cpu,
+                                        struct device_node *codec,
+                                        char *prefix, int idx)
+{
+       struct device *dev = simple_priv_to_dev(priv);
+       struct device_node *bitclkmaster = NULL;
+       struct device_node *framemaster = NULL;
+       struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx);
+       struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai;
+       struct asoc_simple_dai *codec_dai = &dai_props->codec_dai;
+       unsigned int daifmt;
+
+       daifmt = snd_soc_of_parse_daifmt(node, prefix,
+                                        &bitclkmaster, &framemaster);
+       daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
+
+       if (strlen(prefix) && !bitclkmaster && !framemaster) {
+               /*
+                * No dai-link level and master setting was not found from
+                * sound node level, revert back to legacy DT parsing and
+                * take the settings from codec node.
+                */
+               dev_dbg(dev, "Revert to legacy daifmt parsing\n");
+
+               cpu_dai->fmt = codec_dai->fmt =
+                       snd_soc_of_parse_daifmt(codec, NULL, NULL, NULL) |
+                       (daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK);
+       } else {
+               if (codec == bitclkmaster)
+                       daifmt |= (codec == framemaster) ?
+                               SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS;
+               else
+                       daifmt |= (codec == framemaster) ?
+                               SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
+
+               cpu_dai->fmt    = daifmt;
+               codec_dai->fmt  = daifmt;
+       }
+
+       of_node_put(bitclkmaster);
+       of_node_put(framemaster);
+
+       return 0;
+}
+
 static int asoc_simple_card_dai_link_of(struct device_node *node,
                                        struct simple_card_data *priv,
                                        int idx,
@@ -234,10 +285,8 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
        struct device *dev = simple_priv_to_dev(priv);
        struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
        struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx);
-       struct device_node *np = NULL;
-       struct device_node *bitclkmaster = NULL;
-       struct device_node *framemaster = NULL;
-       unsigned int daifmt;
+       struct device_node *cpu = NULL;
+       struct device_node *codec = NULL;
        char *name;
        char prop[128];
        char *prefix = "";
@@ -247,85 +296,36 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
        if (is_top_level_node)
                prefix = "simple-audio-card,";
 
-       daifmt = snd_soc_of_parse_daifmt(node, prefix,
-                                        &bitclkmaster, &framemaster);
-       daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
-
        snprintf(prop, sizeof(prop), "%scpu", prefix);
-       np = of_get_child_by_name(node, prop);
-       if (!np) {
+       cpu = of_get_child_by_name(node, prop);
+
+       snprintf(prop, sizeof(prop), "%scodec", prefix);
+       codec = of_get_child_by_name(node, prop);
+
+       if (!cpu || !codec) {
                ret = -EINVAL;
                dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
                goto dai_link_of_err;
        }
 
-       ret = asoc_simple_card_sub_parse_of(np, &dai_props->cpu_dai,
+       ret = asoc_simple_card_parse_daifmt(node, priv,
+                                           cpu, codec, prefix, idx);
+       if (ret < 0)
+               goto dai_link_of_err;
+
+       ret = asoc_simple_card_sub_parse_of(cpu, &dai_props->cpu_dai,
                                            &dai_link->cpu_of_node,
                                            &dai_link->cpu_dai_name,
                                            &cpu_args);
        if (ret < 0)
                goto dai_link_of_err;
 
-       dai_props->cpu_dai.fmt = daifmt;
-       switch (((np == bitclkmaster) << 4) | (np == framemaster)) {
-       case 0x11:
-               dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBS_CFS;
-               break;
-       case 0x10:
-               dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBS_CFM;
-               break;
-       case 0x01:
-               dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBM_CFS;
-               break;
-       default:
-               dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBM_CFM;
-               break;
-       }
-
-       of_node_put(np);
-       snprintf(prop, sizeof(prop), "%scodec", prefix);
-       np = of_get_child_by_name(node, prop);
-       if (!np) {
-               ret = -EINVAL;
-               dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
-               goto dai_link_of_err;
-       }
-
-       ret = asoc_simple_card_sub_parse_of(np, &dai_props->codec_dai,
+       ret = asoc_simple_card_sub_parse_of(codec, &dai_props->codec_dai,
                                            &dai_link->codec_of_node,
                                            &dai_link->codec_dai_name, NULL);
        if (ret < 0)
                goto dai_link_of_err;
 
-       if (strlen(prefix) && !bitclkmaster && !framemaster) {
-               /*
-                * No DAI link level and master setting was found
-                * from sound node level, revert back to legacy DT
-                * parsing and take the settings from codec node.
-                */
-               dev_dbg(dev, "%s: Revert to legacy daifmt parsing\n",
-                       __func__);
-               dai_props->cpu_dai.fmt = dai_props->codec_dai.fmt =
-                       snd_soc_of_parse_daifmt(np, NULL, NULL, NULL) |
-                       (daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK);
-       } else {
-               dai_props->codec_dai.fmt = daifmt;
-               switch (((np == bitclkmaster) << 4) | (np == framemaster)) {
-               case 0x11:
-                       dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBM_CFM;
-                       break;
-               case 0x10:
-                       dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBM_CFS;
-                       break;
-               case 0x01:
-                       dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBS_CFM;
-                       break;
-               default:
-                       dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBS_CFS;
-                       break;
-               }
-       }
-
        if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) {
                ret = -EINVAL;
                goto dai_link_of_err;
@@ -368,12 +368,9 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
                dai_link->cpu_dai_name = NULL;
 
 dai_link_of_err:
-       if (np)
-               of_node_put(np);
-       if (bitclkmaster)
-               of_node_put(bitclkmaster);
-       if (framemaster)
-               of_node_put(framemaster);
+       of_node_put(cpu);
+       of_node_put(codec);
+
        return ret;
 }
 
@@ -381,6 +378,7 @@ static int asoc_simple_card_parse_of(struct device_node *node,
                                     struct simple_card_data *priv)
 {
        struct device *dev = simple_priv_to_dev(priv);
+       enum of_gpio_flags flags;
        u32 val;
        int ret;
 
@@ -436,13 +434,15 @@ static int asoc_simple_card_parse_of(struct device_node *node,
                        return ret;
        }
 
-       priv->gpio_hp_det = of_get_named_gpio(node,
-                               "simple-audio-card,hp-det-gpio", 0);
+       priv->gpio_hp_det = of_get_named_gpio_flags(node,
+                               "simple-audio-card,hp-det-gpio", 0, &flags);
+       priv->gpio_hp_det_invert = !!(flags & OF_GPIO_ACTIVE_LOW);
        if (priv->gpio_hp_det == -EPROBE_DEFER)
                return -EPROBE_DEFER;
 
-       priv->gpio_mic_det = of_get_named_gpio(node,
-                               "simple-audio-card,mic-det-gpio", 0);
+       priv->gpio_mic_det = of_get_named_gpio_flags(node,
+                               "simple-audio-card,mic-det-gpio", 0, &flags);
+       priv->gpio_mic_det_invert = !!(flags & OF_GPIO_ACTIVE_LOW);
        if (priv->gpio_mic_det == -EPROBE_DEFER)
                return -EPROBE_DEFER;
 
@@ -457,18 +457,13 @@ static int asoc_simple_card_unref(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
        struct snd_soc_dai_link *dai_link;
-       struct device_node *np;
        int num_links;
 
        for (num_links = 0, dai_link = card->dai_link;
             num_links < card->num_links;
             num_links++, dai_link++) {
-               np = (struct device_node *) dai_link->cpu_of_node;
-               if (np)
-                       of_node_put(np);
-               np = (struct device_node *) dai_link->codec_of_node;
-               if (np)
-                       of_node_put(np);
+               of_node_put(dai_link->cpu_of_node);
+               of_node_put(dai_link->codec_of_node);
        }
        return 0;
 }
index f5b4a9c..e989ecf 100644 (file)
@@ -3,6 +3,7 @@ config SND_MFLD_MACHINE
        depends on INTEL_SCU_IPC
        select SND_SOC_SN95031
        select SND_SST_MFLD_PLATFORM
+       select SND_SST_IPC_PCI
        help
           This adds support for ASoC machine driver for Intel(R) MID Medfield platform
           used as alsa device in audio substem in Intel(R) MID devices
@@ -12,10 +13,23 @@ config SND_MFLD_MACHINE
 config SND_SST_MFLD_PLATFORM
        tristate
 
+config SND_SST_IPC
+       tristate
+
+config SND_SST_IPC_PCI
+       tristate
+       select SND_SST_IPC
+
+config SND_SST_IPC_ACPI
+       tristate
+       select SND_SST_IPC
+       depends on ACPI
+
 config SND_SOC_INTEL_SST
        tristate "ASoC support for Intel(R) Smart Sound Technology"
        select SND_SOC_INTEL_SST_ACPI if ACPI
        depends on (X86 || COMPILE_TEST)
+       depends on DW_DMAC_CORE
        help
           This adds support for Intel(R) Smart Sound Technology (SST).
           Say Y if you have such a device
@@ -32,7 +46,8 @@ config SND_SOC_INTEL_BAYTRAIL
 
 config SND_SOC_INTEL_HASWELL_MACH
        tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint"
-       depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C
+       depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C && \\
+                  I2C_DESIGNWARE_PLATFORM
        select SND_SOC_INTEL_HASWELL
        select SND_SOC_RT5640
        help
@@ -61,7 +76,8 @@ config SND_SOC_INTEL_BYT_MAX98090_MACH
 
 config SND_SOC_INTEL_BROADWELL_MACH
        tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint"
-       depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && DW_DMAC
+       depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && DW_DMAC && \\
+                  I2C_DESIGNWARE_PLATFORM
        select SND_SOC_INTEL_HASWELL
        select SND_COMPRESS_OFFLOAD
        select SND_SOC_RT286
@@ -70,3 +86,27 @@ config SND_SOC_INTEL_BROADWELL_MACH
          Ultrabook platforms.
          Say Y if you have such a device
          If unsure select "N".
+
+config SND_SOC_INTEL_BYTCR_RT5640_MACH
+       tristate "ASoC Audio DSP Support for MID BYT Platform"
+       depends on X86
+       select SND_SOC_RT5640
+       select SND_SST_MFLD_PLATFORM
+       select SND_SST_IPC_ACPI
+       help
+         This adds support for ASoC machine driver for Intel(R) MID Baytrail platform
+          used as alsa device in audio substem in Intel(R) MID devices
+          Say Y if you have such a device
+          If unsure select "N".
+
+config SND_SOC_INTEL_CHT_BSW_RT5672_MACH
+        tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5672 codec"
+        depends on X86_INTEL_LPSS
+        select SND_SOC_RT5670
+        select SND_SST_MFLD_PLATFORM
+        select SND_SST_IPC_ACPI
+        help
+          This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
+          platforms with RT5672 audio codec.
+          Say Y if you have such a device
+          If unsure select "N".
index f841786..e928ec3 100644 (file)
@@ -26,8 +26,15 @@ snd-soc-sst-haswell-objs := haswell.o
 snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o
 snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o
 snd-soc-sst-broadwell-objs := broadwell.o
+snd-soc-sst-bytcr-dpcm-rt5640-objs := bytcr_dpcm_rt5640.o
+snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
 
 obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
 obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o
 obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o
 obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o
+obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-dpcm-rt5640.o
+obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
+
+# DSP driver
+obj-$(CONFIG_SND_SST_IPC) += sst/
index 0e550f1..c256764 100644 (file)
@@ -19,6 +19,7 @@
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
+#include <sound/jack.h>
 #include <sound/pcm_params.h>
 
 #include "sst-dsp.h"
 
 #include "../codecs/rt286.h"
 
+static struct snd_soc_jack broadwell_headset;
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin broadwell_headset_pins[] = {
+       {
+               .pin = "Mic Jack",
+               .mask = SND_JACK_MICROPHONE,
+       },
+       {
+               .pin = "Headphone Jack",
+               .mask = SND_JACK_HEADPHONE,
+       },
+};
+
+static const struct snd_kcontrol_new broadwell_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Speaker"),
+       SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+};
+
 static const struct snd_soc_dapm_widget broadwell_widgets[] = {
-       SND_SOC_DAPM_HP("Headphones", NULL),
+       SND_SOC_DAPM_HP("Headphone Jack", NULL),
        SND_SOC_DAPM_SPK("Speaker", NULL),
        SND_SOC_DAPM_MIC("Mic Jack", NULL),
        SND_SOC_DAPM_MIC("DMIC1", NULL),
@@ -42,7 +61,7 @@ static const struct snd_soc_dapm_route broadwell_rt286_map[] = {
        {"Speaker", NULL, "SPOL"},
 
        /* HP jack connectors - unknown if we have jack deteck */
-       {"Headphones", NULL, "HPO Pin"},
+       {"Headphone Jack", NULL, "HPO Pin"},
 
        /* other jacks */
        {"MIC1", NULL, "Mic Jack"},
@@ -57,6 +76,27 @@ static const struct snd_soc_dapm_route broadwell_rt286_map[] = {
        {"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
 };
 
+static int broadwell_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_codec *codec = rtd->codec;
+       int ret = 0;
+       ret = snd_soc_jack_new(codec, "Headset",
+               SND_JACK_HEADSET | SND_JACK_BTN_0, &broadwell_headset);
+
+       if (ret)
+               return ret;
+
+       ret = snd_soc_jack_add_pins(&broadwell_headset,
+               ARRAY_SIZE(broadwell_headset_pins),
+               broadwell_headset_pins);
+       if (ret)
+               return ret;
+
+       rt286_mic_detect(codec, &broadwell_headset);
+       return 0;
+}
+
+
 static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
                        struct snd_pcm_hw_params *params)
 {
@@ -116,7 +156,7 @@ static int broadwell_rtd_init(struct snd_soc_pcm_runtime *rtd)
        }
 
        /* always connected - check HP for jack detect */
-       snd_soc_dapm_enable_pin(dapm, "Headphones");
+       snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
        snd_soc_dapm_enable_pin(dapm, "Speaker");
        snd_soc_dapm_enable_pin(dapm, "Mic Jack");
        snd_soc_dapm_enable_pin(dapm, "Line Jack");
@@ -131,7 +171,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
        /* Front End DAI links */
        {
                .name = "System PCM",
-               .stream_name = "System Playback",
+               .stream_name = "System Playback/Capture",
                .cpu_dai_name = "System Pin",
                .platform_name = "haswell-pcm-audio",
                .dynamic = 1,
@@ -140,6 +180,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
                .init = broadwell_rtd_init,
                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
                .dpcm_playback = 1,
+               .dpcm_capture = 1,
        },
        {
                .name = "Offload0",
@@ -174,18 +215,6 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
                .dpcm_capture = 1,
        },
-       {
-               .name = "Capture PCM",
-               .stream_name = "Capture",
-               .cpu_dai_name = "Capture Pin",
-               .platform_name = "haswell-pcm-audio",
-               .dynamic = 1,
-               .codec_name = "snd-soc-dummy",
-               .codec_dai_name = "snd-soc-dummy-dai",
-               .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
-               .dpcm_capture = 1,
-       },
-
        /* Back End DAI links */
        {
                /* SSP0 - Codec */
@@ -196,6 +225,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
                .no_pcm = 1,
                .codec_name = "i2c-INT343A:00",
                .codec_dai_name = "rt286-aif1",
+               .init = broadwell_rt286_codec_init,
                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
                        SND_SOC_DAIFMT_CBS_CFS,
                .ignore_suspend = 1,
@@ -213,6 +243,8 @@ static struct snd_soc_card broadwell_rt286 = {
        .owner = THIS_MODULE,
        .dai_link = broadwell_rt286_dais,
        .num_links = ARRAY_SIZE(broadwell_rt286_dais),
+       .controls = broadwell_controls,
+       .num_controls = ARRAY_SIZE(broadwell_controls),
        .dapm_widgets = broadwell_widgets,
        .num_dapm_widgets = ARRAY_SIZE(broadwell_widgets),
        .dapm_routes = broadwell_rt286_map,
diff --git a/sound/soc/intel/bytcr_dpcm_rt5640.c b/sound/soc/intel/bytcr_dpcm_rt5640.c
new file mode 100644 (file)
index 0000000..f5d0fc1
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ *  byt_cr_dpcm_rt5640.c - ASoc Machine driver for Intel Byt CR platform
+ *
+ *  Copyright (C) 2014 Intel Corp
+ *  Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../codecs/rt5640.h"
+#include "sst-atom-controls.h"
+
+static const struct snd_soc_dapm_widget byt_dapm_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone", NULL),
+       SND_SOC_DAPM_MIC("Headset Mic", NULL),
+       SND_SOC_DAPM_MIC("Int Mic", NULL),
+       SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route byt_audio_map[] = {
+       {"IN2P", NULL, "Headset Mic"},
+       {"IN2N", NULL, "Headset Mic"},
+       {"Headset Mic", NULL, "MICBIAS1"},
+       {"IN1P", NULL, "MICBIAS1"},
+       {"LDO2", NULL, "Int Mic"},
+       {"Headphone", NULL, "HPOL"},
+       {"Headphone", NULL, "HPOR"},
+       {"Ext Spk", NULL, "SPOLP"},
+       {"Ext Spk", NULL, "SPOLN"},
+       {"Ext Spk", NULL, "SPORP"},
+       {"Ext Spk", NULL, "SPORN"},
+
+       {"AIF1 Playback", NULL, "ssp2 Tx"},
+       {"ssp2 Tx", NULL, "codec_out0"},
+       {"ssp2 Tx", NULL, "codec_out1"},
+       {"codec_in0", NULL, "ssp2 Rx"},
+       {"codec_in1", NULL, "ssp2 Rx"},
+       {"ssp2 Rx", NULL, "AIF1 Capture"},
+};
+
+static const struct snd_kcontrol_new byt_mc_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Headphone"),
+       SOC_DAPM_PIN_SWITCH("Headset Mic"),
+       SOC_DAPM_PIN_SWITCH("Int Mic"),
+       SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static int byt_aif1_hw_params(struct snd_pcm_substream *substream,
+                                       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       int ret;
+
+       snd_soc_dai_set_bclk_ratio(codec_dai, 50);
+
+       ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1,
+                                    params_rate(params) * 512,
+                                    SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               dev_err(rtd->dev, "can't set codec clock %d\n", ret);
+               return ret;
+       }
+
+       ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1,
+                                 params_rate(params) * 50,
+                                 params_rate(params) * 512);
+       if (ret < 0) {
+               dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static const struct snd_soc_pcm_stream byt_dai_params = {
+       .formats = SNDRV_PCM_FMTBIT_S24_LE,
+       .rate_min = 48000,
+       .rate_max = 48000,
+       .channels_min = 2,
+       .channels_max = 2,
+};
+
+static int byt_codec_fixup(struct snd_soc_pcm_runtime *rtd,
+                           struct snd_pcm_hw_params *params)
+{
+       struct snd_interval *rate = hw_param_interval(params,
+                       SNDRV_PCM_HW_PARAM_RATE);
+       struct snd_interval *channels = hw_param_interval(params,
+                                               SNDRV_PCM_HW_PARAM_CHANNELS);
+
+       /* The DSP will covert the FE rate to 48k, stereo, 24bits */
+       rate->min = rate->max = 48000;
+       channels->min = channels->max = 2;
+
+       /* set SSP2 to 24-bit */
+       snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
+                                   SNDRV_PCM_HW_PARAM_FIRST_MASK],
+                                   SNDRV_PCM_FORMAT_S24_LE);
+       return 0;
+}
+
+static unsigned int rates_48000[] = {
+       48000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_48000 = {
+       .count = ARRAY_SIZE(rates_48000),
+       .list  = rates_48000,
+};
+
+static int byt_aif1_startup(struct snd_pcm_substream *substream)
+{
+       return snd_pcm_hw_constraint_list(substream->runtime, 0,
+                       SNDRV_PCM_HW_PARAM_RATE,
+                       &constraints_48000);
+}
+
+static struct snd_soc_ops byt_aif1_ops = {
+       .startup = byt_aif1_startup,
+};
+
+static struct snd_soc_ops byt_be_ssp2_ops = {
+       .hw_params = byt_aif1_hw_params,
+};
+
+static struct snd_soc_dai_link byt_dailink[] = {
+       [MERR_DPCM_AUDIO] = {
+               .name = "Baytrail Audio Port",
+               .stream_name = "Baytrail Audio",
+               .cpu_dai_name = "media-cpu-dai",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .codec_name = "snd-soc-dummy",
+               .platform_name = "sst-mfld-platform",
+               .ignore_suspend = 1,
+               .dynamic = 1,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+               .ops = &byt_aif1_ops,
+       },
+       [MERR_DPCM_COMPR] = {
+               .name = "Baytrail Compressed Port",
+               .stream_name = "Baytrail Compress",
+               .cpu_dai_name = "compress-cpu-dai",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .codec_name = "snd-soc-dummy",
+               .platform_name = "sst-mfld-platform",
+       },
+               /* back ends */
+       {
+               .name = "SSP2-Codec",
+               .be_id = 1,
+               .cpu_dai_name = "ssp2-port",
+               .platform_name = "sst-mfld-platform",
+               .no_pcm = 1,
+               .codec_dai_name = "rt5640-aif1",
+               .codec_name = "i2c-10EC5640:00",
+               .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+                                               | SND_SOC_DAIFMT_CBS_CFS,
+               .be_hw_params_fixup = byt_codec_fixup,
+               .ignore_suspend = 1,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+               .ops = &byt_be_ssp2_ops,
+       },
+};
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_byt = {
+       .name = "baytrailcraudio",
+       .dai_link = byt_dailink,
+       .num_links = ARRAY_SIZE(byt_dailink),
+       .dapm_widgets = byt_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(byt_dapm_widgets),
+       .dapm_routes = byt_audio_map,
+       .num_dapm_routes = ARRAY_SIZE(byt_audio_map),
+       .controls = byt_mc_controls,
+       .num_controls = ARRAY_SIZE(byt_mc_controls),
+};
+
+static int snd_byt_mc_probe(struct platform_device *pdev)
+{
+       int ret_val = 0;
+
+       /* register the soc card */
+       snd_soc_card_byt.dev = &pdev->dev;
+
+       ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_byt);
+       if (ret_val) {
+               dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n", ret_val);
+               return ret_val;
+       }
+       platform_set_drvdata(pdev, &snd_soc_card_byt);
+       return ret_val;
+}
+
+static struct platform_driver snd_byt_mc_driver = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "bytt100_rt5640",
+               .pm = &snd_soc_pm_ops,
+       },
+       .probe = snd_byt_mc_probe,
+};
+
+module_platform_driver(snd_byt_mc_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
+MODULE_AUTHOR("Subhransu S. Prusty <subhransu.s.prusty@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bytrt5640-audio");
diff --git a/sound/soc/intel/cht_bsw_rt5672.c b/sound/soc/intel/cht_bsw_rt5672.c
new file mode 100644 (file)
index 0000000..9b8b561
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ *  cht_bsw_rt5672.c - ASoc Machine driver for Intel Cherryview-based platforms
+ *                     Cherrytrail and Braswell, with RT5672 codec.
+ *
+ *  Copyright (C) 2014 Intel Corp
+ *  Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
+ *          Mengdong Lin <mengdong.lin@intel.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../codecs/rt5670.h"
+#include "sst-atom-controls.h"
+
+/* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */
+#define CHT_PLAT_CLK_3_HZ      19200000
+#define CHT_CODEC_DAI  "rt5670-aif1"
+
+static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
+{
+       int i;
+
+       for (i = 0; i < card->num_rtd; i++) {
+               struct snd_soc_pcm_runtime *rtd;
+
+               rtd = card->rtd + i;
+               if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
+                            strlen(CHT_CODEC_DAI)))
+                       return rtd->codec_dai;
+       }
+       return NULL;
+}
+
+static int platform_clock_control(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *k, int  event)
+{
+       struct snd_soc_dapm_context *dapm = w->dapm;
+       struct snd_soc_card *card = dapm->card;
+       struct snd_soc_dai *codec_dai;
+
+       codec_dai = cht_get_codec_dai(card);
+       if (!codec_dai) {
+               dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");
+               return -EIO;
+       }
+
+       if (!SND_SOC_DAPM_EVENT_OFF(event))
+               return 0;
+
+       /* Set codec sysclk source to its internal clock because codec PLL will
+        * be off when idle and MCLK will also be off by ACPI when codec is
+        * runtime suspended. Codec needs clock for jack detection and button
+        * press.
+        */
+       snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK,
+                              0, SND_SOC_CLOCK_IN);
+
+       return 0;
+}
+
+static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone", NULL),
+       SND_SOC_DAPM_MIC("Headset Mic", NULL),
+       SND_SOC_DAPM_MIC("Int Mic", NULL),
+       SND_SOC_DAPM_SPK("Ext Spk", NULL),
+       SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+                       platform_clock_control, SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route cht_audio_map[] = {
+       {"IN1P", NULL, "Headset Mic"},
+       {"IN1N", NULL, "Headset Mic"},
+       {"DMIC L1", NULL, "Int Mic"},
+       {"DMIC R1", NULL, "Int Mic"},
+       {"Headphone", NULL, "HPOL"},
+       {"Headphone", NULL, "HPOR"},
+       {"Ext Spk", NULL, "SPOLP"},
+       {"Ext Spk", NULL, "SPOLN"},
+       {"Ext Spk", NULL, "SPORP"},
+       {"Ext Spk", NULL, "SPORN"},
+       {"AIF1 Playback", NULL, "ssp2 Tx"},
+       {"ssp2 Tx", NULL, "codec_out0"},
+       {"ssp2 Tx", NULL, "codec_out1"},
+       {"codec_in0", NULL, "ssp2 Rx"},
+       {"codec_in1", NULL, "ssp2 Rx"},
+       {"ssp2 Rx", NULL, "AIF1 Capture"},
+       {"Headphone", NULL, "Platform Clock"},
+       {"Headset Mic", NULL, "Platform Clock"},
+       {"Int Mic", NULL, "Platform Clock"},
+       {"Ext Spk", NULL, "Platform Clock"},
+};
+
+static const struct snd_kcontrol_new cht_mc_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Headphone"),
+       SOC_DAPM_PIN_SWITCH("Headset Mic"),
+       SOC_DAPM_PIN_SWITCH("Int Mic"),
+       SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
+                                       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       int ret;
+
+       /* set codec PLL source to the 19.2MHz platform clock (MCLK) */
+       ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK,
+                                 CHT_PLAT_CLK_3_HZ, params_rate(params) * 512);
+       if (ret < 0) {
+               dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+               return ret;
+       }
+
+       /* set codec sysclk source to PLL */
+       ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_PLL1,
+                                    params_rate(params) * 512,
+                                    SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
+               return ret;
+       }
+       return 0;
+}
+
+static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+       int ret;
+       struct snd_soc_dai *codec_dai = runtime->codec_dai;
+
+       /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
+       ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24);
+       if (ret < 0) {
+               dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
+                           struct snd_pcm_hw_params *params)
+{
+       struct snd_interval *rate = hw_param_interval(params,
+                       SNDRV_PCM_HW_PARAM_RATE);
+       struct snd_interval *channels = hw_param_interval(params,
+                                               SNDRV_PCM_HW_PARAM_CHANNELS);
+
+       /* The DSP will covert the FE rate to 48k, stereo, 24bits */
+       rate->min = rate->max = 48000;
+       channels->min = channels->max = 2;
+
+       /* set SSP2 to 24-bit */
+       snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
+                                   SNDRV_PCM_HW_PARAM_FIRST_MASK],
+                                   SNDRV_PCM_FORMAT_S24_LE);
+       return 0;
+}
+
+static unsigned int rates_48000[] = {
+       48000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_48000 = {
+       .count = ARRAY_SIZE(rates_48000),
+       .list  = rates_48000,
+};
+
+static int cht_aif1_startup(struct snd_pcm_substream *substream)
+{
+       return snd_pcm_hw_constraint_list(substream->runtime, 0,
+                       SNDRV_PCM_HW_PARAM_RATE,
+                       &constraints_48000);
+}
+
+static struct snd_soc_ops cht_aif1_ops = {
+       .startup = cht_aif1_startup,
+};
+
+static struct snd_soc_ops cht_be_ssp2_ops = {
+       .hw_params = cht_aif1_hw_params,
+};
+
+static struct snd_soc_dai_link cht_dailink[] = {
+       /* Front End DAI links */
+       [MERR_DPCM_AUDIO] = {
+               .name = "Audio Port",
+               .stream_name = "Audio",
+               .cpu_dai_name = "media-cpu-dai",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .codec_name = "snd-soc-dummy",
+               .platform_name = "sst-mfld-platform",
+               .ignore_suspend = 1,
+               .dynamic = 1,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+               .ops = &cht_aif1_ops,
+       },
+       [MERR_DPCM_COMPR] = {
+               .name = "Compressed Port",
+               .stream_name = "Compress",
+               .cpu_dai_name = "compress-cpu-dai",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .codec_name = "snd-soc-dummy",
+               .platform_name = "sst-mfld-platform",
+       },
+
+       /* Back End DAI links */
+       {
+               /* SSP2 - Codec */
+               .name = "SSP2-Codec",
+               .be_id = 1,
+               .cpu_dai_name = "ssp2-port",
+               .platform_name = "sst-mfld-platform",
+               .no_pcm = 1,
+               .codec_dai_name = "rt5670-aif1",
+               .codec_name = "i2c-10EC5670:00",
+               .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF
+                                       | SND_SOC_DAIFMT_CBS_CFS,
+               .init = cht_codec_init,
+               .be_hw_params_fixup = cht_codec_fixup,
+               .ignore_suspend = 1,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+               .ops = &cht_be_ssp2_ops,
+       },
+};
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_cht = {
+       .name = "cherrytrailcraudio",
+       .dai_link = cht_dailink,
+       .num_links = ARRAY_SIZE(cht_dailink),
+       .dapm_widgets = cht_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
+       .dapm_routes = cht_audio_map,
+       .num_dapm_routes = ARRAY_SIZE(cht_audio_map),
+       .controls = cht_mc_controls,
+       .num_controls = ARRAY_SIZE(cht_mc_controls),
+};
+
+static int snd_cht_mc_probe(struct platform_device *pdev)
+{
+       int ret_val = 0;
+
+       /* register the soc card */
+       snd_soc_card_cht.dev = &pdev->dev;
+       ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
+       if (ret_val) {
+               dev_err(&pdev->dev,
+                       "snd_soc_register_card failed %d\n", ret_val);
+               return ret_val;
+       }
+       platform_set_drvdata(pdev, &snd_soc_card_cht);
+       return ret_val;
+}
+
+static struct platform_driver snd_cht_mc_driver = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "cht-bsw-rt5672",
+               .pm = &snd_soc_pm_ops,
+       },
+       .probe = snd_cht_mc_probe,
+};
+
+module_platform_driver(snd_cht_mc_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
+MODULE_AUTHOR("Subhransu S. Prusty, Mengdong Lin");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cht-bsw-rt5672");
index 3981982..cb8a482 100644 (file)
@@ -109,7 +109,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = {
        /* Front End DAI links */
        {
                .name = "System",
-               .stream_name = "System Playback",
+               .stream_name = "System Playback/Capture",
                .cpu_dai_name = "System Pin",
                .platform_name = "haswell-pcm-audio",
                .dynamic = 1,
@@ -118,6 +118,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = {
                .init = haswell_rtd_init,
                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
                .dpcm_playback = 1,
+               .dpcm_capture = 1,
        },
        {
                .name = "Offload0",
@@ -152,17 +153,6 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = {
                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
                .dpcm_capture = 1,
        },
-       {
-               .name = "Capture",
-               .stream_name = "Capture",
-               .cpu_dai_name = "Capture Pin",
-               .platform_name = "haswell-pcm-audio",
-               .dynamic = 1,
-               .codec_name = "snd-soc-dummy",
-               .codec_dai_name = "snd-soc-dummy-dai",
-               .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
-               .dpcm_capture = 1,
-       },
 
        /* Back End DAI links */
        {
index 7104a34..90aa5c0 100644 (file)
@@ -15,6 +15,9 @@
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  *  General Public License for more details.
  *
+ *  In the dpcm driver modelling when a particular FE/BE/Mixer/Pipe is active
+ *  we forward the settings and parameters, rest we keep the values  in
+ *  driver and forward when DAPM enables them
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  */
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -81,6 +84,183 @@ static int sst_fill_and_send_cmd(struct sst_data *drv,
        return ret;
 }
 
+/**
+ * tx map value is a bitfield where each bit represents a FW channel
+ *
+ *                     3 2 1 0         # 0 = codec0, 1 = codec1
+ *                     RLRLRLRL        # 3, 4 = reserved
+ *
+ * e.g. slot 0 rx map =        00001100b -> data from slot 0 goes into codec_in1 L,R
+ */
+static u8 sst_ssp_tx_map[SST_MAX_TDM_SLOTS] = {
+       0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default rx map */
+};
+
+/**
+ * rx map value is a bitfield where each bit represents a slot
+ *
+ *                       76543210      # 0 = slot 0, 1 = slot 1
+ *
+ * e.g. codec1_0 tx map = 00000101b -> data from codec_out1_0 goes into slot 0, 2
+ */
+static u8 sst_ssp_rx_map[SST_MAX_TDM_SLOTS] = {
+       0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default tx map */
+};
+
+/**
+ * NOTE: this is invoked with lock held
+ */
+static int sst_send_slot_map(struct sst_data *drv)
+{
+       struct sst_param_sba_ssp_slot_map cmd;
+
+       SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+       cmd.header.command_id = SBA_SET_SSP_SLOT_MAP;
+       cmd.header.length = sizeof(struct sst_param_sba_ssp_slot_map)
+                               - sizeof(struct sst_dsp_header);
+
+       cmd.param_id = SBA_SET_SSP_SLOT_MAP;
+       cmd.param_len = sizeof(cmd.rx_slot_map) + sizeof(cmd.tx_slot_map)
+                                       + sizeof(cmd.ssp_index);
+       cmd.ssp_index = SSP_CODEC;
+
+       memcpy(cmd.rx_slot_map, &sst_ssp_tx_map[0], sizeof(cmd.rx_slot_map));
+       memcpy(cmd.tx_slot_map, &sst_ssp_rx_map[0], sizeof(cmd.tx_slot_map));
+
+       return sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,
+                       SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd,
+                             sizeof(cmd.header) + cmd.header.length);
+}
+
+int sst_slot_enum_info(struct snd_kcontrol *kcontrol,
+                      struct snd_ctl_elem_info *uinfo)
+{
+       struct sst_enum *e = (struct sst_enum *)kcontrol->private_value;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = e->max;
+
+       if (uinfo->value.enumerated.item > e->max - 1)
+               uinfo->value.enumerated.item = e->max - 1;
+       strcpy(uinfo->value.enumerated.name,
+               e->texts[uinfo->value.enumerated.item]);
+
+       return 0;
+}
+
+/**
+ * sst_slot_get - get the status of the interleaver/deinterleaver control
+ *
+ * Searches the map where the control status is stored, and gets the
+ * channel/slot which is currently set for this enumerated control. Since it is
+ * an enumerated control, there is only one possible value.
+ */
+static int sst_slot_get(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct sst_enum *e = (void *)kcontrol->private_value;
+       struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+       struct sst_data *drv = snd_soc_component_get_drvdata(c);
+       unsigned int ctl_no = e->reg;
+       unsigned int is_tx = e->tx;
+       unsigned int val, mux;
+       u8 *map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map;
+
+       mutex_lock(&drv->lock);
+       val = 1 << ctl_no;
+       /* search which slot/channel has this bit set - there should be only one */
+       for (mux = e->max; mux > 0;  mux--)
+               if (map[mux - 1] & val)
+                       break;
+
+       ucontrol->value.enumerated.item[0] = mux;
+       mutex_unlock(&drv->lock);
+
+       dev_dbg(c->dev, "%s - %s map = %#x\n",
+                       is_tx ? "tx channel" : "rx slot",
+                        e->texts[mux], mux ? map[mux - 1] : -1);
+       return 0;
+}
+
+/* sst_check_and_send_slot_map - helper for checking power state and sending
+ * slot map cmd
+ *
+ * called with lock held
+ */
+static int sst_check_and_send_slot_map(struct sst_data *drv, struct snd_kcontrol *kcontrol)
+{
+       struct sst_enum *e = (void *)kcontrol->private_value;
+       int ret = 0;
+
+       if (e->w && e->w->power)
+               ret = sst_send_slot_map(drv);
+       else
+               dev_err(&drv->pdev->dev, "Slot control: %s doesn't have DAPM widget!!!\n",
+                               kcontrol->id.name);
+       return ret;
+}
+
+/**
+ * sst_slot_put - set the status of interleaver/deinterleaver control
+ *
+ * (de)interleaver controls are defined in opposite sense to be user-friendly
+ *
+ * Instead of the enum value being the value written to the register, it is the
+ * register address; and the kcontrol number (register num) is the value written
+ * to the register. This is so that there can be only one value for each
+ * slot/channel since there is only one control for each slot/channel.
+ *
+ * This means that whenever an enum is set, we need to clear the bit
+ * for that kcontrol_no for all the interleaver OR deinterleaver registers
+ */
+static int sst_slot_put(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+       struct sst_data *drv = snd_soc_component_get_drvdata(c);
+       struct sst_enum *e = (void *)kcontrol->private_value;
+       int i, ret = 0;
+       unsigned int ctl_no = e->reg;
+       unsigned int is_tx = e->tx;
+       unsigned int slot_channel_no;
+       unsigned int val, mux;
+       u8 *map;
+
+       map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map;
+
+       val = 1 << ctl_no;
+       mux = ucontrol->value.enumerated.item[0];
+       if (mux > e->max - 1)
+               return -EINVAL;
+
+       mutex_lock(&drv->lock);
+       /* first clear all registers of this bit */
+       for (i = 0; i < e->max; i++)
+               map[i] &= ~val;
+
+       if (mux == 0) {
+               /* kctl set to 'none' and we reset the bits so send IPC */
+               ret = sst_check_and_send_slot_map(drv, kcontrol);
+
+               mutex_unlock(&drv->lock);
+               return ret;
+       }
+
+       /* offset by one to take "None" into account */
+       slot_channel_no = mux - 1;
+       map[slot_channel_no] |= val;
+
+       dev_dbg(c->dev, "%s %s map = %#x\n",
+                       is_tx ? "tx channel" : "rx slot",
+                       e->texts[mux], map[slot_channel_no]);
+
+       ret = sst_check_and_send_slot_map(drv, kcontrol);
+
+       mutex_unlock(&drv->lock);
+       return ret;
+}
+
 static int sst_send_algo_cmd(struct sst_data *drv,
                              struct sst_algo_control *bc)
 {
@@ -104,6 +284,34 @@ static int sst_send_algo_cmd(struct sst_data *drv,
        return ret;
 }
 
+/**
+ * sst_find_and_send_pipe_algo - send all the algo parameters for a pipe
+ *
+ * The algos which are in each pipeline are sent to the firmware one by one
+ *
+ * Called with lock held
+ */
+static int sst_find_and_send_pipe_algo(struct sst_data *drv,
+                                       const char *pipe, struct sst_ids *ids)
+{
+       int ret = 0;
+       struct sst_algo_control *bc;
+       struct sst_module *algo = NULL;
+
+       dev_dbg(&drv->pdev->dev, "Enter: widget=%s\n", pipe);
+
+       list_for_each_entry(algo, &ids->algo_list, node) {
+               bc = (void *)algo->kctl->private_value;
+
+               dev_dbg(&drv->pdev->dev, "Found algo control name=%s pipe=%s\n",
+                               algo->kctl->id.name, pipe);
+               ret = sst_send_algo_cmd(drv, bc);
+               if (ret)
+                       return ret;
+       }
+       return ret;
+}
+
 static int sst_algo_bytes_ctl_info(struct snd_kcontrol *kcontrol,
                            struct snd_ctl_elem_info *uinfo)
 {
@@ -162,6 +370,743 @@ static int sst_algo_control_set(struct snd_kcontrol *kcontrol,
        return ret;
 }
 
+static int sst_gain_ctl_info(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_info *uinfo)
+{
+       struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = mc->stereo ? 2 : 1;
+       uinfo->value.integer.min = mc->min;
+       uinfo->value.integer.max = mc->max;
+
+       return 0;
+}
+
+/**
+ * sst_send_gain_cmd - send the gain algorithm IPC to the FW
+ * @gv:                the stored value of gain (also contains rampduration)
+ * @mute:      flag that indicates whether this was called from the
+ *             digital_mute callback or directly. If called from the
+ *             digital_mute callback, module will be muted/unmuted based on this
+ *             flag. The flag is always 0 if called directly.
+ *
+ * Called with sst_data.lock held
+ *
+ * The user-set gain value is sent only if the user-controllable 'mute' control
+ * is OFF (indicated by gv->mute). Otherwise, the mute value (MIN value) is
+ * sent.
+ */
+static int sst_send_gain_cmd(struct sst_data *drv, struct sst_gain_value *gv,
+                             u16 task_id, u16 loc_id, u16 module_id, int mute)
+{
+       struct sst_cmd_set_gain_dual cmd;
+
+       dev_dbg(&drv->pdev->dev, "Enter\n");
+
+       cmd.header.command_id = MMX_SET_GAIN;
+       SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+       cmd.gain_cell_num = 1;
+
+       if (mute || gv->mute) {
+               cmd.cell_gains[0].cell_gain_left = SST_GAIN_MIN_VALUE;
+               cmd.cell_gains[0].cell_gain_right = SST_GAIN_MIN_VALUE;
+       } else {
+               cmd.cell_gains[0].cell_gain_left = gv->l_gain;
+               cmd.cell_gains[0].cell_gain_right = gv->r_gain;
+       }
+
+       SST_FILL_DESTINATION(2, cmd.cell_gains[0].dest,
+                            loc_id, module_id);
+       cmd.cell_gains[0].gain_time_constant = gv->ramp_duration;
+
+       cmd.header.length = sizeof(struct sst_cmd_set_gain_dual)
+                               - sizeof(struct sst_dsp_header);
+
+       /* we are with lock held, so call the unlocked api  to send */
+       return sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,
+                               SST_FLAG_BLOCKED, task_id, 0, &cmd,
+                             sizeof(cmd.header) + cmd.header.length);
+}
+
+static int sst_gain_get(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
+       struct sst_gain_value *gv = mc->gain_val;
+
+       switch (mc->type) {
+       case SST_GAIN_TLV:
+               ucontrol->value.integer.value[0] = gv->l_gain;
+               ucontrol->value.integer.value[1] = gv->r_gain;
+               break;
+
+       case SST_GAIN_MUTE:
+               ucontrol->value.integer.value[0] = gv->mute ? 1 : 0;
+               break;
+
+       case SST_GAIN_RAMP_DURATION:
+               ucontrol->value.integer.value[0] = gv->ramp_duration;
+               break;
+
+       default:
+               dev_err(component->dev, "Invalid Input- gain type:%d\n",
+                               mc->type);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int sst_gain_put(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       int ret = 0;
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);
+       struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
+       struct sst_gain_value *gv = mc->gain_val;
+
+       mutex_lock(&drv->lock);
+
+       switch (mc->type) {
+       case SST_GAIN_TLV:
+               gv->l_gain = ucontrol->value.integer.value[0];
+               gv->r_gain = ucontrol->value.integer.value[1];
+               dev_dbg(cmpnt->dev, "%s: Volume %d, %d\n",
+                               mc->pname, gv->l_gain, gv->r_gain);
+               break;
+
+       case SST_GAIN_MUTE:
+               gv->mute = !!ucontrol->value.integer.value[0];
+               dev_dbg(cmpnt->dev, "%s: Mute %d\n", mc->pname, gv->mute);
+               break;
+
+       case SST_GAIN_RAMP_DURATION:
+               gv->ramp_duration = ucontrol->value.integer.value[0];
+               dev_dbg(cmpnt->dev, "%s: Ramp Delay%d\n",
+                                       mc->pname, gv->ramp_duration);
+               break;
+
+       default:
+               mutex_unlock(&drv->lock);
+               dev_err(cmpnt->dev, "Invalid Input- gain type:%d\n",
+                               mc->type);
+               return -EINVAL;
+       }
+
+       if (mc->w && mc->w->power)
+               ret = sst_send_gain_cmd(drv, gv, mc->task_id,
+                       mc->pipe_id | mc->instance_id, mc->module_id, 0);
+       mutex_unlock(&drv->lock);
+
+       return ret;
+}
+
+static int sst_set_pipe_gain(struct sst_ids *ids,
+                               struct sst_data *drv, int mute);
+
+static int sst_send_pipe_module_params(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol)
+{
+       struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+       struct sst_data *drv = snd_soc_component_get_drvdata(c);
+       struct sst_ids *ids = w->priv;
+
+       mutex_lock(&drv->lock);
+       sst_find_and_send_pipe_algo(drv, w->name, ids);
+       sst_set_pipe_gain(ids, drv, 0);
+       mutex_unlock(&drv->lock);
+
+       return 0;
+}
+
+static int sst_generic_modules_event(struct snd_soc_dapm_widget *w,
+                                    struct snd_kcontrol *k, int event)
+{
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               return sst_send_pipe_module_params(w, k);
+       return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(sst_gain_tlv_common, SST_GAIN_MIN_VALUE * 10, 10, 0);
+
+/* Look up table to convert MIXER SW bit regs to SWM inputs */
+static const uint swm_mixer_input_ids[SST_SWM_INPUT_COUNT] = {
+       [SST_IP_CODEC0]         = SST_SWM_IN_CODEC0,
+       [SST_IP_CODEC1]         = SST_SWM_IN_CODEC1,
+       [SST_IP_LOOP0]          = SST_SWM_IN_SPROT_LOOP,
+       [SST_IP_LOOP1]          = SST_SWM_IN_MEDIA_LOOP1,
+       [SST_IP_LOOP2]          = SST_SWM_IN_MEDIA_LOOP2,
+       [SST_IP_PCM0]           = SST_SWM_IN_PCM0,
+       [SST_IP_PCM1]           = SST_SWM_IN_PCM1,
+       [SST_IP_MEDIA0]         = SST_SWM_IN_MEDIA0,
+       [SST_IP_MEDIA1]         = SST_SWM_IN_MEDIA1,
+       [SST_IP_MEDIA2]         = SST_SWM_IN_MEDIA2,
+       [SST_IP_MEDIA3]         = SST_SWM_IN_MEDIA3,
+};
+
+/**
+ * fill_swm_input - fill in the SWM input ids given the register
+ *
+ * The register value is a bit-field inicated which mixer inputs are ON. Use the
+ * lookup table to get the input-id and fill it in the structure.
+ */
+static int fill_swm_input(struct snd_soc_component *cmpnt,
+               struct swm_input_ids *swm_input, unsigned int reg)
+{
+       uint i, is_set, nb_inputs = 0;
+       u16 input_loc_id;
+
+       dev_dbg(cmpnt->dev, "reg: %#x\n", reg);
+       for (i = 0; i < SST_SWM_INPUT_COUNT; i++) {
+               is_set = reg & BIT(i);
+               if (!is_set)
+                       continue;
+
+               input_loc_id = swm_mixer_input_ids[i];
+               SST_FILL_DESTINATION(2, swm_input->input_id,
+                                    input_loc_id, SST_DEFAULT_MODULE_ID);
+               nb_inputs++;
+               swm_input++;
+               dev_dbg(cmpnt->dev, "input id: %#x, nb_inputs: %d\n",
+                               input_loc_id, nb_inputs);
+
+               if (nb_inputs == SST_CMD_SWM_MAX_INPUTS) {
+                       dev_warn(cmpnt->dev, "SET_SWM cmd max inputs reached");
+                       break;
+               }
+       }
+       return nb_inputs;
+}
+
+
+/**
+ * called with lock held
+ */
+static int sst_set_pipe_gain(struct sst_ids *ids,
+                       struct sst_data *drv, int mute)
+{
+       int ret = 0;
+       struct sst_gain_mixer_control *mc;
+       struct sst_gain_value *gv;
+       struct sst_module *gain = NULL;
+
+       list_for_each_entry(gain, &ids->gain_list, node) {
+               struct snd_kcontrol *kctl = gain->kctl;
+
+               dev_dbg(&drv->pdev->dev, "control name=%s\n", kctl->id.name);
+               mc = (void *)kctl->private_value;
+               gv = mc->gain_val;
+
+               ret = sst_send_gain_cmd(drv, gv, mc->task_id,
+                       mc->pipe_id | mc->instance_id, mc->module_id, mute);
+               if (ret)
+                       return ret;
+       }
+       return ret;
+}
+
+static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *k, int event)
+{
+       struct sst_cmd_set_swm cmd;
+       struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+       struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);
+       struct sst_ids *ids = w->priv;
+       bool set_mixer = false;
+       struct soc_mixer_control *mc;
+       int val = 0;
+       int i = 0;
+
+       dev_dbg(cmpnt->dev, "widget = %s\n", w->name);
+       /*
+        * Identify which mixer input is on and send the bitmap of the
+        * inputs as an IPC to the DSP.
+        */
+       for (i = 0; i < w->num_kcontrols; i++) {
+               if (dapm_kcontrol_get_value(w->kcontrols[i])) {
+                       mc = (struct soc_mixer_control *)(w->kcontrols[i])->private_value;
+                       val |= 1 << mc->shift;
+               }
+       }
+       dev_dbg(cmpnt->dev, "val = %#x\n", val);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+       case SND_SOC_DAPM_POST_PMD:
+               set_mixer = true;
+               break;
+       case SND_SOC_DAPM_POST_REG:
+               if (w->power)
+                       set_mixer = true;
+               break;
+       default:
+               set_mixer = false;
+       }
+
+       if (set_mixer == false)
+               return 0;
+
+       if (SND_SOC_DAPM_EVENT_ON(event) ||
+           event == SND_SOC_DAPM_POST_REG)
+               cmd.switch_state = SST_SWM_ON;
+       else
+               cmd.switch_state = SST_SWM_OFF;
+
+       SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+       /* MMX_SET_SWM == SBA_SET_SWM */
+       cmd.header.command_id = SBA_SET_SWM;
+
+       SST_FILL_DESTINATION(2, cmd.output_id,
+                            ids->location_id, SST_DEFAULT_MODULE_ID);
+       cmd.nb_inputs = fill_swm_input(cmpnt, &cmd.input[0], val);
+       cmd.header.length = offsetof(struct sst_cmd_set_swm, input)
+                               - sizeof(struct sst_dsp_header)
+                               + (cmd.nb_inputs * sizeof(cmd.input[0]));
+
+       return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+                             ids->task_id, 0, &cmd,
+                             sizeof(cmd.header) + cmd.header.length);
+}
+
+/* SBA mixers - 16 inputs */
+#define SST_SBA_DECLARE_MIX_CONTROLS(kctl_name)                                                        \
+       static const struct snd_kcontrol_new kctl_name[] = {                                    \
+               SOC_DAPM_SINGLE("codec_in0 Switch", SND_SOC_NOPM, SST_IP_CODEC0, 1, 0),         \
+               SOC_DAPM_SINGLE("codec_in1 Switch", SND_SOC_NOPM, SST_IP_CODEC1, 1, 0),         \
+               SOC_DAPM_SINGLE("sprot_loop_in Switch", SND_SOC_NOPM, SST_IP_LOOP0, 1, 0),      \
+               SOC_DAPM_SINGLE("media_loop1_in Switch", SND_SOC_NOPM, SST_IP_LOOP1, 1, 0),     \
+               SOC_DAPM_SINGLE("media_loop2_in Switch", SND_SOC_NOPM, SST_IP_LOOP2, 1, 0),     \
+               SOC_DAPM_SINGLE("pcm0_in Switch", SND_SOC_NOPM, SST_IP_PCM0, 1, 0),             \
+               SOC_DAPM_SINGLE("pcm1_in Switch", SND_SOC_NOPM, SST_IP_PCM1, 1, 0),             \
+       }
+
+#define SST_SBA_MIXER_GRAPH_MAP(mix_name)                      \
+       { mix_name, "codec_in0 Switch", "codec_in0" },          \
+       { mix_name, "codec_in1 Switch", "codec_in1" },          \
+       { mix_name, "sprot_loop_in Switch",     "sprot_loop_in" },      \
+       { mix_name, "media_loop1_in Switch",    "media_loop1_in" },     \
+       { mix_name, "media_loop2_in Switch",    "media_loop2_in" },     \
+       { mix_name, "pcm0_in Switch",           "pcm0_in" },            \
+       { mix_name, "pcm1_in Switch",           "pcm1_in" }
+
+#define SST_MMX_DECLARE_MIX_CONTROLS(kctl_name)                                                \
+       static const struct snd_kcontrol_new kctl_name[] = {                            \
+               SOC_DAPM_SINGLE("media0_in Switch", SND_SOC_NOPM, SST_IP_MEDIA0, 1, 0), \
+               SOC_DAPM_SINGLE("media1_in Switch", SND_SOC_NOPM, SST_IP_MEDIA1, 1, 0), \
+               SOC_DAPM_SINGLE("media2_in Switch", SND_SOC_NOPM, SST_IP_MEDIA2, 1, 0), \
+               SOC_DAPM_SINGLE("media3_in Switch", SND_SOC_NOPM, SST_IP_MEDIA3, 1, 0), \
+       }
+
+SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media0_controls);
+SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media1_controls);
+
+/* 18 SBA mixers */
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm0_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm1_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm2_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_sprot_l0_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l1_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l2_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_voip_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec0_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec1_controls);
+
+/*
+ * sst_handle_vb_timer - Start/Stop the DSP scheduler
+ *
+ * The DSP expects first cmd to be SBA_VB_START, so at first startup send
+ * that.
+ * DSP expects last cmd to be SBA_VB_IDLE, so at last shutdown send that.
+ *
+ * Do refcount internally so that we send command only at first start
+ * and last end. Since SST driver does its own ref count, invoke sst's
+ * power ops always!
+ */
+int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable)
+{
+       int ret = 0;
+       struct sst_cmd_generic cmd;
+       struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
+       static int timer_usage;
+
+       if (enable)
+               cmd.header.command_id = SBA_VB_START;
+       else
+               cmd.header.command_id = SBA_IDLE;
+       dev_dbg(dai->dev, "enable=%u, usage=%d\n", enable, timer_usage);
+
+       SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+       cmd.header.length = 0;
+
+       if (enable) {
+               ret = sst->ops->power(sst->dev, true);
+               if (ret < 0)
+                       return ret;
+       }
+
+       mutex_lock(&drv->lock);
+       if (enable)
+               timer_usage++;
+       else
+               timer_usage--;
+
+       /*
+        * Send the command only if this call is the first enable or last
+        * disable
+        */
+       if ((enable && (timer_usage == 1)) ||
+           (!enable && (timer_usage == 0))) {
+               ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD,
+                               SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd,
+                               sizeof(cmd.header) + cmd.header.length);
+               if (ret && enable) {
+                       timer_usage--;
+                       enable  = false;
+               }
+       }
+       mutex_unlock(&drv->lock);
+
+       if (!enable)
+               sst->ops->power(sst->dev, false);
+       return ret;
+}
+
+/**
+ * sst_ssp_config - contains SSP configuration for media UC
+ */
+static const struct sst_ssp_config sst_ssp_configs = {
+       .ssp_id = SSP_CODEC,
+       .bits_per_slot = 24,
+       .slots = 4,
+       .ssp_mode = SSP_MODE_MASTER,
+       .pcm_mode = SSP_PCM_MODE_NETWORK,
+       .duplex = SSP_DUPLEX,
+       .ssp_protocol = SSP_MODE_PCM,
+       .fs_width = 1,
+       .fs_frequency = SSP_FS_48_KHZ,
+       .active_slot_map = 0xF,
+       .start_delay = 0,
+};
+
+int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable)
+{
+       struct sst_cmd_sba_hw_set_ssp cmd;
+       struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
+       const struct sst_ssp_config *config;
+
+       dev_info(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id);
+
+       SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+       cmd.header.command_id = SBA_HW_SET_SSP;
+       cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp)
+                               - sizeof(struct sst_dsp_header);
+
+       config = &sst_ssp_configs;
+       dev_dbg(dai->dev, "ssp_id: %u\n", config->ssp_id);
+
+       if (enable)
+               cmd.switch_state = SST_SWITCH_ON;
+       else
+               cmd.switch_state = SST_SWITCH_OFF;
+
+       cmd.selection = config->ssp_id;
+       cmd.nb_bits_per_slots = config->bits_per_slot;
+       cmd.nb_slots = config->slots;
+       cmd.mode = config->ssp_mode | (config->pcm_mode << 1);
+       cmd.duplex = config->duplex;
+       cmd.active_tx_slot_map = config->active_slot_map;
+       cmd.active_rx_slot_map = config->active_slot_map;
+       cmd.frame_sync_frequency = config->fs_frequency;
+       cmd.frame_sync_polarity = SSP_FS_ACTIVE_HIGH;
+       cmd.data_polarity = 1;
+       cmd.frame_sync_width = config->fs_width;
+       cmd.ssp_protocol = config->ssp_protocol;
+       cmd.start_delay = config->start_delay;
+       cmd.reserved1 = cmd.reserved2 = 0xFF;
+
+       return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+                               SST_TASK_SBA, 0, &cmd,
+                               sizeof(cmd.header) + cmd.header.length);
+}
+
+static int sst_set_be_modules(struct snd_soc_dapm_widget *w,
+                        struct snd_kcontrol *k, int event)
+{
+       int ret = 0;
+       struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+       struct sst_data *drv = snd_soc_component_get_drvdata(c);
+
+       dev_dbg(c->dev, "Enter: widget=%s\n", w->name);
+
+       if (SND_SOC_DAPM_EVENT_ON(event)) {
+               ret = sst_send_slot_map(drv);
+               if (ret)
+                       return ret;
+               ret = sst_send_pipe_module_params(w, k);
+       }
+       return ret;
+}
+
+static int sst_set_media_path(struct snd_soc_dapm_widget *w,
+                             struct snd_kcontrol *k, int event)
+{
+       int ret = 0;
+       struct sst_cmd_set_media_path cmd;
+       struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+       struct sst_data *drv = snd_soc_component_get_drvdata(c);
+       struct sst_ids *ids = w->priv;
+
+       dev_dbg(c->dev, "widget=%s\n", w->name);
+       dev_dbg(c->dev, "task=%u, location=%#x\n",
+                               ids->task_id, ids->location_id);
+
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               cmd.switch_state = SST_PATH_ON;
+       else
+               cmd.switch_state = SST_PATH_OFF;
+
+       SST_FILL_DESTINATION(2, cmd.header.dst,
+                            ids->location_id, SST_DEFAULT_MODULE_ID);
+
+       /* MMX_SET_MEDIA_PATH == SBA_SET_MEDIA_PATH */
+       cmd.header.command_id = MMX_SET_MEDIA_PATH;
+       cmd.header.length = sizeof(struct sst_cmd_set_media_path)
+                               - sizeof(struct sst_dsp_header);
+
+       ret = sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+                             ids->task_id, 0, &cmd,
+                             sizeof(cmd.header) + cmd.header.length);
+       if (ret)
+               return ret;
+
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               ret = sst_send_pipe_module_params(w, k);
+       return ret;
+}
+
+static int sst_set_media_loop(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *k, int event)
+{
+       int ret = 0;
+       struct sst_cmd_sba_set_media_loop_map cmd;
+       struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+       struct sst_data *drv = snd_soc_component_get_drvdata(c);
+       struct sst_ids *ids = w->priv;
+
+       dev_dbg(c->dev, "Enter:widget=%s\n", w->name);
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               cmd.switch_state = SST_SWITCH_ON;
+       else
+               cmd.switch_state = SST_SWITCH_OFF;
+
+       SST_FILL_DESTINATION(2, cmd.header.dst,
+                            ids->location_id, SST_DEFAULT_MODULE_ID);
+
+       cmd.header.command_id = SBA_SET_MEDIA_LOOP_MAP;
+       cmd.header.length = sizeof(struct sst_cmd_sba_set_media_loop_map)
+                                - sizeof(struct sst_dsp_header);
+       cmd.param.part.cfg.rate = 2; /* 48khz */
+
+       cmd.param.part.cfg.format = ids->format; /* stereo/Mono */
+       cmd.param.part.cfg.s_length = 1; /* 24bit left justified */
+       cmd.map = 0; /* Algo sequence: Gain - DRP - FIR - IIR */
+
+       ret = sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+                             SST_TASK_SBA, 0, &cmd,
+                             sizeof(cmd.header) + cmd.header.length);
+       if (ret)
+               return ret;
+
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               ret = sst_send_pipe_module_params(w, k);
+       return ret;
+}
+
+static const struct snd_soc_dapm_widget sst_dapm_widgets[] = {
+       SST_AIF_IN("codec_in0", sst_set_be_modules),
+       SST_AIF_IN("codec_in1", sst_set_be_modules),
+       SST_AIF_OUT("codec_out0", sst_set_be_modules),
+       SST_AIF_OUT("codec_out1", sst_set_be_modules),
+
+       /* Media Paths */
+       /* MediaX IN paths are set via ALLOC, so no SET_MEDIA_PATH command */
+       SST_PATH_INPUT("media0_in", SST_TASK_MMX, SST_SWM_IN_MEDIA0, sst_generic_modules_event),
+       SST_PATH_INPUT("media1_in", SST_TASK_MMX, SST_SWM_IN_MEDIA1, NULL),
+       SST_PATH_INPUT("media2_in", SST_TASK_MMX, SST_SWM_IN_MEDIA2, sst_set_media_path),
+       SST_PATH_INPUT("media3_in", SST_TASK_MMX, SST_SWM_IN_MEDIA3, NULL),
+       SST_PATH_OUTPUT("media0_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA0, sst_set_media_path),
+       SST_PATH_OUTPUT("media1_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA1, sst_set_media_path),
+
+       /* SBA PCM Paths */
+       SST_PATH_INPUT("pcm0_in", SST_TASK_SBA, SST_SWM_IN_PCM0, sst_set_media_path),
+       SST_PATH_INPUT("pcm1_in", SST_TASK_SBA, SST_SWM_IN_PCM1, sst_set_media_path),
+       SST_PATH_OUTPUT("pcm0_out", SST_TASK_SBA, SST_SWM_OUT_PCM0, sst_set_media_path),
+       SST_PATH_OUTPUT("pcm1_out", SST_TASK_SBA, SST_SWM_OUT_PCM1, sst_set_media_path),
+       SST_PATH_OUTPUT("pcm2_out", SST_TASK_SBA, SST_SWM_OUT_PCM2, sst_set_media_path),
+
+       /* SBA Loops */
+       SST_PATH_INPUT("sprot_loop_in", SST_TASK_SBA, SST_SWM_IN_SPROT_LOOP, NULL),
+       SST_PATH_INPUT("media_loop1_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP1, NULL),
+       SST_PATH_INPUT("media_loop2_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP2, NULL),
+       SST_PATH_MEDIA_LOOP_OUTPUT("sprot_loop_out", SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, SST_FMT_MONO, sst_set_media_loop),
+       SST_PATH_MEDIA_LOOP_OUTPUT("media_loop1_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, SST_FMT_MONO, sst_set_media_loop),
+       SST_PATH_MEDIA_LOOP_OUTPUT("media_loop2_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, SST_FMT_STEREO, sst_set_media_loop),
+
+       /* Media Mixers */
+       SST_SWM_MIXER("media0_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA0,
+                     sst_mix_media0_controls, sst_swm_mixer_event),
+       SST_SWM_MIXER("media1_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA1,
+                     sst_mix_media1_controls, sst_swm_mixer_event),
+
+       /* SBA PCM mixers */
+       SST_SWM_MIXER("pcm0_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM0,
+                     sst_mix_pcm0_controls, sst_swm_mixer_event),
+       SST_SWM_MIXER("pcm1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM1,
+                     sst_mix_pcm1_controls, sst_swm_mixer_event),
+       SST_SWM_MIXER("pcm2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM2,
+                     sst_mix_pcm2_controls, sst_swm_mixer_event),
+
+       /* SBA Loop mixers */
+       SST_SWM_MIXER("sprot_loop_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP,
+                     sst_mix_sprot_l0_controls, sst_swm_mixer_event),
+       SST_SWM_MIXER("media_loop1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1,
+                     sst_mix_media_l1_controls, sst_swm_mixer_event),
+       SST_SWM_MIXER("media_loop2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2,
+                     sst_mix_media_l2_controls, sst_swm_mixer_event),
+
+       /* SBA Backend mixers */
+       SST_SWM_MIXER("codec_out0 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC0,
+                     sst_mix_codec0_controls, sst_swm_mixer_event),
+       SST_SWM_MIXER("codec_out1 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC1,
+                     sst_mix_codec1_controls, sst_swm_mixer_event),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+       {"media0_in", NULL, "Compress Playback"},
+       {"media1_in", NULL, "Headset Playback"},
+       {"media2_in", NULL, "pcm0_out"},
+
+       {"media0_out mix 0", "media0_in Switch", "media0_in"},
+       {"media0_out mix 0", "media1_in Switch", "media1_in"},
+       {"media0_out mix 0", "media2_in Switch", "media2_in"},
+       {"media0_out mix 0", "media3_in Switch", "media3_in"},
+       {"media1_out mix 0", "media0_in Switch", "media0_in"},
+       {"media1_out mix 0", "media1_in Switch", "media1_in"},
+       {"media1_out mix 0", "media2_in Switch", "media2_in"},
+       {"media1_out mix 0", "media3_in Switch", "media3_in"},
+
+       {"media0_out", NULL, "media0_out mix 0"},
+       {"media1_out", NULL, "media1_out mix 0"},
+       {"pcm0_in", NULL, "media0_out"},
+       {"pcm1_in", NULL, "media1_out"},
+
+       {"Headset Capture", NULL, "pcm1_out"},
+       {"Headset Capture", NULL, "pcm2_out"},
+       {"pcm0_out", NULL, "pcm0_out mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("pcm0_out mix 0"),
+       {"pcm1_out", NULL, "pcm1_out mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("pcm1_out mix 0"),
+       {"pcm2_out", NULL, "pcm2_out mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("pcm2_out mix 0"),
+
+       {"media_loop1_in", NULL, "media_loop1_out"},
+       {"media_loop1_out", NULL, "media_loop1_out mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("media_loop1_out mix 0"),
+       {"media_loop2_in", NULL, "media_loop2_out"},
+       {"media_loop2_out", NULL, "media_loop2_out mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("media_loop2_out mix 0"),
+       {"sprot_loop_in", NULL, "sprot_loop_out"},
+       {"sprot_loop_out", NULL, "sprot_loop_out mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("sprot_loop_out mix 0"),
+
+       {"codec_out0", NULL, "codec_out0 mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("codec_out0 mix 0"),
+       {"codec_out1", NULL, "codec_out1 mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("codec_out1 mix 0"),
+
+};
+static const char * const slot_names[] = {
+       "none",
+       "slot 0", "slot 1", "slot 2", "slot 3",
+       "slot 4", "slot 5", "slot 6", "slot 7", /* not supported by FW */
+};
+
+static const char * const channel_names[] = {
+       "none",
+       "codec_out0_0", "codec_out0_1", "codec_out1_0", "codec_out1_1",
+       "codec_out2_0", "codec_out2_1", "codec_out3_0", "codec_out3_1", /* not supported by FW */
+};
+
+#define SST_INTERLEAVER(xpname, slot_name, slotno) \
+       SST_SSP_SLOT_CTL(xpname, "tx interleaver", slot_name, slotno, true, \
+                        channel_names, sst_slot_get, sst_slot_put)
+
+#define SST_DEINTERLEAVER(xpname, channel_name, channel_no) \
+       SST_SSP_SLOT_CTL(xpname, "rx deinterleaver", channel_name, channel_no, false, \
+                        slot_names, sst_slot_get, sst_slot_put)
+
+static const struct snd_kcontrol_new sst_slot_controls[] = {
+       SST_INTERLEAVER("codec_out", "slot 0", 0),
+       SST_INTERLEAVER("codec_out", "slot 1", 1),
+       SST_INTERLEAVER("codec_out", "slot 2", 2),
+       SST_INTERLEAVER("codec_out", "slot 3", 3),
+       SST_DEINTERLEAVER("codec_in", "codec_in0_0", 0),
+       SST_DEINTERLEAVER("codec_in", "codec_in0_1", 1),
+       SST_DEINTERLEAVER("codec_in", "codec_in1_0", 2),
+       SST_DEINTERLEAVER("codec_in", "codec_in1_1", 3),
+};
+
+/* Gain helper with min/max set */
+#define SST_GAIN(name, path_id, task_id, instance, gain_var)                           \
+       SST_GAIN_KCONTROLS(name, "Gain", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE,        \
+               SST_GAIN_TC_MIN, SST_GAIN_TC_MAX,                                       \
+               sst_gain_get, sst_gain_put,                                             \
+               SST_MODULE_ID_GAIN_CELL, path_id, instance, task_id,                    \
+               sst_gain_tlv_common, gain_var)
+
+#define SST_VOLUME(name, path_id, task_id, instance, gain_var)                         \
+       SST_GAIN_KCONTROLS(name, "Volume", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE,      \
+               SST_GAIN_TC_MIN, SST_GAIN_TC_MAX,                                       \
+               sst_gain_get, sst_gain_put,                                             \
+               SST_MODULE_ID_VOLUME, path_id, instance, task_id,                       \
+               sst_gain_tlv_common, gain_var)
+
+static struct sst_gain_value sst_gains[];
+
+static const struct snd_kcontrol_new sst_gain_controls[] = {
+       SST_GAIN("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[0]),
+       SST_GAIN("media1_in", SST_PATH_INDEX_MEDIA1_IN, SST_TASK_MMX, 0, &sst_gains[1]),
+       SST_GAIN("media2_in", SST_PATH_INDEX_MEDIA2_IN, SST_TASK_MMX, 0, &sst_gains[2]),
+       SST_GAIN("media3_in", SST_PATH_INDEX_MEDIA3_IN, SST_TASK_MMX, 0, &sst_gains[3]),
+
+       SST_GAIN("pcm0_in", SST_PATH_INDEX_PCM0_IN, SST_TASK_SBA, 0, &sst_gains[4]),
+       SST_GAIN("pcm1_in", SST_PATH_INDEX_PCM1_IN, SST_TASK_SBA, 0, &sst_gains[5]),
+       SST_GAIN("pcm1_out", SST_PATH_INDEX_PCM1_OUT, SST_TASK_SBA, 0, &sst_gains[6]),
+       SST_GAIN("pcm2_out", SST_PATH_INDEX_PCM2_OUT, SST_TASK_SBA, 0, &sst_gains[7]),
+
+       SST_GAIN("codec_in0", SST_PATH_INDEX_CODEC_IN0, SST_TASK_SBA, 0, &sst_gains[8]),
+       SST_GAIN("codec_in1", SST_PATH_INDEX_CODEC_IN1, SST_TASK_SBA, 0, &sst_gains[9]),
+       SST_GAIN("codec_out0", SST_PATH_INDEX_CODEC_OUT0, SST_TASK_SBA, 0, &sst_gains[10]),
+       SST_GAIN("codec_out1", SST_PATH_INDEX_CODEC_OUT1, SST_TASK_SBA, 0, &sst_gains[11]),
+       SST_GAIN("media_loop1_out", SST_PATH_INDEX_MEDIA_LOOP1_OUT, SST_TASK_SBA, 0, &sst_gains[12]),
+       SST_GAIN("media_loop2_out", SST_PATH_INDEX_MEDIA_LOOP2_OUT, SST_TASK_SBA, 0, &sst_gains[13]),
+       SST_GAIN("sprot_loop_out", SST_PATH_INDEX_SPROT_LOOP_OUT, SST_TASK_SBA, 0, &sst_gains[14]),
+       SST_VOLUME("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[15]),
+};
+
+#define SST_GAIN_NUM_CONTROLS 3
+/* the SST_GAIN macro above will create three alsa controls for each
+ * instance invoked, gain, mute and ramp duration, which use the same gain
+ * cell sst_gain to keep track of data
+ * To calculate number of gain cell instances we need to device by 3 in
+ * below caulcation for gain cell memory.
+ * This gets rid of static number and issues while adding new controls
+ */
+static struct sst_gain_value sst_gains[ARRAY_SIZE(sst_gain_controls)/SST_GAIN_NUM_CONTROLS];
+
 static const struct snd_kcontrol_new sst_algo_controls[] = {
        SST_ALGO_KCONTROL_BYTES("media_loop1_out", "fir", 272, SST_MODULE_ID_FIR_24,
                 SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR),
@@ -198,21 +1143,280 @@ static int sst_algo_control_init(struct device *dev)
        return 0;
 }
 
-int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform)
+static bool is_sst_dapm_widget(struct snd_soc_dapm_widget *w)
+{
+       switch (w->id) {
+       case snd_soc_dapm_pga:
+       case snd_soc_dapm_aif_in:
+       case snd_soc_dapm_aif_out:
+       case snd_soc_dapm_input:
+       case snd_soc_dapm_output:
+       case snd_soc_dapm_mixer:
+               return true;
+       default:
+               return false;
+       }
+}
+
+/**
+ * sst_send_pipe_gains - send gains for the front-end DAIs
+ *
+ * The gains in the pipes connected to the front-ends are muted/unmuted
+ * automatically via the digital_mute() DAPM callback. This function sends the
+ * gains for the front-end pipes.
+ */
+int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute)
+{
+       struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
+       struct snd_soc_dapm_widget *w;
+       struct snd_soc_dapm_path *p = NULL;
+
+       dev_dbg(dai->dev, "enter, dai-name=%s dir=%d\n", dai->name, stream);
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               dev_dbg(dai->dev, "Stream name=%s\n",
+                               dai->playback_widget->name);
+               w = dai->playback_widget;
+               list_for_each_entry(p, &w->sinks, list_source) {
+                       if (p->connected && !p->connected(w, p->sink))
+                               continue;
+
+                       if (p->connect && p->sink->power &&
+                                       is_sst_dapm_widget(p->sink)) {
+                               struct sst_ids *ids = p->sink->priv;
+
+                               dev_dbg(dai->dev, "send gains for widget=%s\n",
+                                               p->sink->name);
+                               mutex_lock(&drv->lock);
+                               sst_set_pipe_gain(ids, drv, mute);
+                               mutex_unlock(&drv->lock);
+                       }
+               }
+       } else {
+               dev_dbg(dai->dev, "Stream name=%s\n",
+                               dai->capture_widget->name);
+               w = dai->capture_widget;
+               list_for_each_entry(p, &w->sources, list_sink) {
+                       if (p->connected && !p->connected(w, p->sink))
+                               continue;
+
+                       if (p->connect &&  p->source->power &&
+                                       is_sst_dapm_widget(p->source)) {
+                               struct sst_ids *ids = p->source->priv;
+
+                               dev_dbg(dai->dev, "send gain for widget=%s\n",
+                                               p->source->name);
+                               mutex_lock(&drv->lock);
+                               sst_set_pipe_gain(ids, drv, mute);
+                               mutex_unlock(&drv->lock);
+                       }
+               }
+       }
+       return 0;
+}
+
+/**
+ * sst_fill_module_list - populate the list of modules/gains for a pipe
+ *
+ *
+ * Fills the widget pointer in the kcontrol private data, and also fills the
+ * kcontrol pointer in the widget private data.
+ *
+ * Widget pointer is used to send the algo/gain in the .put() handler if the
+ * widget is powerd on.
+ *
+ * Kcontrol pointer is used to send the algo/gain in the widget power ON/OFF
+ * event handler. Each widget (pipe) has multiple algos stored in the algo_list.
+ */
+static int sst_fill_module_list(struct snd_kcontrol *kctl,
+        struct snd_soc_dapm_widget *w, int type)
 {
+       struct sst_module *module = NULL;
+       struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+       struct sst_ids *ids = w->priv;
        int ret = 0;
+
+       module = devm_kzalloc(c->dev, sizeof(*module), GFP_KERNEL);
+       if (!module)
+               return -ENOMEM;
+
+       if (type == SST_MODULE_GAIN) {
+               struct sst_gain_mixer_control *mc = (void *)kctl->private_value;
+
+               mc->w = w;
+               module->kctl = kctl;
+               list_add_tail(&module->node, &ids->gain_list);
+       } else if (type == SST_MODULE_ALGO) {
+               struct sst_algo_control *bc = (void *)kctl->private_value;
+
+               bc->w = w;
+               module->kctl = kctl;
+               list_add_tail(&module->node, &ids->algo_list);
+       } else {
+               dev_err(c->dev, "invoked for unknown type %d module %s",
+                               type, kctl->id.name);
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+/**
+ * sst_fill_widget_module_info - fill list of gains/algos for the pipe
+ * @widget:    pipe modelled as a DAPM widget
+ *
+ * Fill the list of gains/algos for the widget by looking at all the card
+ * controls and comparing the name of the widget with the first part of control
+ * name. First part of control name contains the pipe name (widget name).
+ */
+static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w,
+       struct snd_soc_platform *platform)
+{
+       struct snd_kcontrol *kctl;
+       int index, ret = 0;
+       struct snd_card *card = platform->component.card->snd_card;
+       char *idx;
+
+       down_read(&card->controls_rwsem);
+
+       list_for_each_entry(kctl, &card->controls, list) {
+               idx = strstr(kctl->id.name, " ");
+               if (idx == NULL)
+                       continue;
+               index  = strlen(kctl->id.name) - strlen(idx);
+
+               if (strstr(kctl->id.name, "Volume") &&
+                   !strncmp(kctl->id.name, w->name, index))
+                       ret = sst_fill_module_list(kctl, w, SST_MODULE_GAIN);
+
+               else if (strstr(kctl->id.name, "params") &&
+                        !strncmp(kctl->id.name, w->name, index))
+                       ret = sst_fill_module_list(kctl, w, SST_MODULE_ALGO);
+
+               else if (strstr(kctl->id.name, "Switch") &&
+                        !strncmp(kctl->id.name, w->name, index) &&
+                        strstr(kctl->id.name, "Gain")) {
+                       struct sst_gain_mixer_control *mc =
+                                               (void *)kctl->private_value;
+
+                       mc->w = w;
+
+               } else if (strstr(kctl->id.name, "interleaver") &&
+                        !strncmp(kctl->id.name, w->name, index)) {
+                       struct sst_enum *e = (void *)kctl->private_value;
+
+                       e->w = w;
+
+               } else if (strstr(kctl->id.name, "deinterleaver") &&
+                        !strncmp(kctl->id.name, w->name, index)) {
+
+                       struct sst_enum *e = (void *)kctl->private_value;
+
+                       e->w = w;
+               }
+
+               if (ret < 0) {
+                       up_read(&card->controls_rwsem);
+                       return ret;
+               }
+       }
+
+       up_read(&card->controls_rwsem);
+       return 0;
+}
+
+/**
+ * sst_fill_linked_widgets - fill the parent pointer for the linked widget
+ */
+static void sst_fill_linked_widgets(struct snd_soc_platform *platform,
+                                               struct sst_ids *ids)
+{
+       struct snd_soc_dapm_widget *w;
+       unsigned int len = strlen(ids->parent_wname);
+
+       list_for_each_entry(w, &platform->component.card->widgets, list) {
+               if (!strncmp(ids->parent_wname, w->name, len)) {
+                       ids->parent_w = w;
+                       break;
+               }
+       }
+}
+
+/**
+ * sst_map_modules_to_pipe - fill algo/gains list for all pipes
+ */
+static int sst_map_modules_to_pipe(struct snd_soc_platform *platform)
+{
+       struct snd_soc_dapm_widget *w;
+       int ret = 0;
+
+       list_for_each_entry(w, &platform->component.card->widgets, list) {
+               if (is_sst_dapm_widget(w) && (w->priv)) {
+                       struct sst_ids *ids = w->priv;
+
+                       dev_dbg(platform->dev, "widget type=%d name=%s\n",
+                                       w->id, w->name);
+                       INIT_LIST_HEAD(&ids->algo_list);
+                       INIT_LIST_HEAD(&ids->gain_list);
+                       ret = sst_fill_widget_module_info(w, platform);
+
+                       if (ret < 0)
+                               return ret;
+
+                       /* fill linked widgets */
+                       if (ids->parent_wname !=  NULL)
+                               sst_fill_linked_widgets(platform, ids);
+               }
+       }
+       return 0;
+}
+
+int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform)
+{
+       int i, ret = 0;
+       struct snd_soc_dapm_context *dapm =
+                       snd_soc_component_get_dapm(&platform->component);
        struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
+       unsigned int gains = ARRAY_SIZE(sst_gain_controls)/3;
 
        drv->byte_stream = devm_kzalloc(platform->dev,
                                        SST_MAX_BIN_BYTES, GFP_KERNEL);
        if (!drv->byte_stream)
                return -ENOMEM;
 
-       /*Initialize algo control params*/
+       snd_soc_dapm_new_controls(dapm, sst_dapm_widgets,
+                       ARRAY_SIZE(sst_dapm_widgets));
+       snd_soc_dapm_add_routes(dapm, intercon,
+                       ARRAY_SIZE(intercon));
+       snd_soc_dapm_new_widgets(dapm->card);
+
+       for (i = 0; i < gains; i++) {
+               sst_gains[i].mute = SST_GAIN_MUTE_DEFAULT;
+               sst_gains[i].l_gain = SST_GAIN_VOLUME_DEFAULT;
+               sst_gains[i].r_gain = SST_GAIN_VOLUME_DEFAULT;
+               sst_gains[i].ramp_duration = SST_GAIN_RAMP_DURATION_DEFAULT;
+       }
+
+       ret = snd_soc_add_platform_controls(platform, sst_gain_controls,
+                       ARRAY_SIZE(sst_gain_controls));
+       if (ret)
+               return ret;
+
+       /* Initialize algo control params */
        ret = sst_algo_control_init(platform->dev);
        if (ret)
                return ret;
        ret = snd_soc_add_platform_controls(platform, sst_algo_controls,
                        ARRAY_SIZE(sst_algo_controls));
+       if (ret)
+               return ret;
+
+       ret = snd_soc_add_platform_controls(platform, sst_slot_controls,
+                       ARRAY_SIZE(sst_slot_controls));
+       if (ret)
+               return ret;
+
+       ret = sst_map_modules_to_pipe(platform);
+
        return ret;
 }
index a73e894..dfebfdd 100644 (file)
@@ -23,6 +23,9 @@
 #ifndef __SST_ATOM_CONTROLS_H__
 #define __SST_ATOM_CONTROLS_H__
 
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
 enum {
        MERR_DPCM_AUDIO = 0,
        MERR_DPCM_COMPR,
@@ -360,16 +363,416 @@ struct sst_dsp_header {
 struct sst_cmd_generic {
        struct sst_dsp_header header;
 } __packed;
+
+struct swm_input_ids {
+       struct sst_destination_id input_id;
+} __packed;
+
+struct sst_cmd_set_swm {
+       struct sst_dsp_header header;
+       struct sst_destination_id output_id;
+       u16    switch_state;
+       u16    nb_inputs;
+       struct swm_input_ids input[SST_CMD_SWM_MAX_INPUTS];
+} __packed;
+
+struct sst_cmd_set_media_path {
+       struct sst_dsp_header header;
+       u16    switch_state;
+} __packed;
+
+struct pcm_cfg {
+               u8 s_length:2;
+               u8 rate:3;
+               u8 format:3;
+} __packed;
+
+struct sst_cmd_set_speech_path {
+       struct sst_dsp_header header;
+       u16    switch_state;
+       struct {
+               u16 rsvd:8;
+               struct pcm_cfg cfg;
+       } config;
+} __packed;
+
+struct gain_cell {
+       struct sst_destination_id dest;
+       s16 cell_gain_left;
+       s16 cell_gain_right;
+       u16 gain_time_constant;
+} __packed;
+
+#define NUM_GAIN_CELLS 1
+struct sst_cmd_set_gain_dual {
+       struct sst_dsp_header header;
+       u16    gain_cell_num;
+       struct gain_cell cell_gains[NUM_GAIN_CELLS];
+} __packed;
 struct sst_cmd_set_params {
        struct sst_destination_id dst;
        u16 command_id;
        char params[0];
 } __packed;
+
+
+struct sst_cmd_sba_vb_start {
+       struct sst_dsp_header header;
+} __packed;
+
+union sba_media_loop_params {
+       struct {
+               u16 rsvd:8;
+               struct pcm_cfg cfg;
+       } part;
+       u16 full;
+} __packed;
+
+struct sst_cmd_sba_set_media_loop_map {
+       struct  sst_dsp_header header;
+       u16     switch_state;
+       union   sba_media_loop_params param;
+       u16     map;
+} __packed;
+
+struct sst_cmd_tone_stop {
+       struct  sst_dsp_header header;
+       u16     switch_state;
+} __packed;
+
+enum sst_ssp_mode {
+       SSP_MODE_MASTER = 0,
+       SSP_MODE_SLAVE = 1,
+};
+
+enum sst_ssp_pcm_mode {
+       SSP_PCM_MODE_NORMAL = 0,
+       SSP_PCM_MODE_NETWORK = 1,
+};
+
+enum sst_ssp_duplex {
+       SSP_DUPLEX = 0,
+       SSP_RX = 1,
+       SSP_TX = 2,
+};
+
+enum sst_ssp_fs_frequency {
+       SSP_FS_8_KHZ = 0,
+       SSP_FS_16_KHZ = 1,
+       SSP_FS_44_1_KHZ = 2,
+       SSP_FS_48_KHZ = 3,
+};
+
+enum sst_ssp_fs_polarity {
+       SSP_FS_ACTIVE_LOW = 0,
+       SSP_FS_ACTIVE_HIGH = 1,
+};
+
+enum sst_ssp_protocol {
+       SSP_MODE_PCM = 0,
+       SSP_MODE_I2S = 1,
+};
+
+enum sst_ssp_port_id {
+       SSP_MODEM = 0,
+       SSP_BT = 1,
+       SSP_FM = 2,
+       SSP_CODEC = 3,
+};
+
+struct sst_cmd_sba_hw_set_ssp {
+       struct sst_dsp_header header;
+       u16 selection;                  /* 0:SSP0(def), 1:SSP1, 2:SSP2 */
+
+       u16 switch_state;
+
+       u16 nb_bits_per_slots:6;        /* 0-32 bits, 24 (def) */
+       u16 nb_slots:4;                 /* 0-8: slots per frame  */
+       u16 mode:3;                     /* 0:Master, 1: Slave  */
+       u16 duplex:3;
+
+       u16 active_tx_slot_map:8;       /* Bit map, 0:off, 1:on */
+       u16 reserved1:8;
+
+       u16 active_rx_slot_map:8;       /* Bit map 0: Off, 1:On */
+       u16 reserved2:8;
+
+       u16 frame_sync_frequency;
+
+       u16 frame_sync_polarity:8;
+       u16 data_polarity:8;
+
+       u16 frame_sync_width;           /* 1 to N clocks */
+       u16 ssp_protocol:8;
+       u16 start_delay:8;              /* Start delay in terms of clock ticks */
+} __packed;
+
+#define SST_MAX_TDM_SLOTS 8
+
+struct sst_param_sba_ssp_slot_map {
+       struct sst_dsp_header header;
+
+       u16 param_id;
+       u16 param_len;
+       u16 ssp_index;
+
+       u8 rx_slot_map[SST_MAX_TDM_SLOTS];
+       u8 tx_slot_map[SST_MAX_TDM_SLOTS];
+} __packed;
+
+enum {
+       SST_PROBE_EXTRACTOR = 0,
+       SST_PROBE_INJECTOR = 1,
+};
+
+/**** widget defines *****/
+
+#define SST_MODULE_GAIN 1
+#define SST_MODULE_ALGO 2
+
+#define SST_FMT_MONO 0
+#define SST_FMT_STEREO 3
+
+/* physical SSP numbers */
+enum {
+       SST_SSP0 = 0,
+       SST_SSP1,
+       SST_SSP2,
+       SST_SSP_LAST = SST_SSP2,
+};
+
+#define SST_NUM_SSPS           (SST_SSP_LAST + 1)      /* physical SSPs */
+#define SST_MAX_SSP_MUX                2                       /* single SSP muxed between pipes */
+#define SST_MAX_SSP_DOMAINS    2                       /* domains present in each pipe */
+
+struct sst_module {
+       struct snd_kcontrol *kctl;
+       struct list_head node;
+};
+
+struct sst_ssp_config {
+       u8 ssp_id;
+       u8 bits_per_slot;
+       u8 slots;
+       u8 ssp_mode;
+       u8 pcm_mode;
+       u8 duplex;
+       u8 ssp_protocol;
+       u8 fs_frequency;
+       u8 active_slot_map;
+       u8 start_delay;
+       u16 fs_width;
+};
+
+struct sst_ssp_cfg {
+       const u8 ssp_number;
+       const int *mux_shift;
+       const int (*domain_shift)[SST_MAX_SSP_MUX];
+       const struct sst_ssp_config (*ssp_config)[SST_MAX_SSP_MUX][SST_MAX_SSP_DOMAINS];
+};
+
+struct sst_ids {
+       u16 location_id;
+       u16 module_id;
+       u8  task_id;
+       u8  format;
+       u8  reg;
+       const char *parent_wname;
+       struct snd_soc_dapm_widget *parent_w;
+       struct list_head algo_list;
+       struct list_head gain_list;
+       const struct sst_pcm_format *pcm_fmt;
+};
+
+
+#define SST_AIF_IN(wname, wevent)                                                      \
+{      .id = snd_soc_dapm_aif_in, .name = wname, .sname = NULL,                        \
+       .reg = SND_SOC_NOPM, .shift = 0,                                        \
+       .on_val = 1, .off_val = 0,                                                      \
+       .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD,   \
+       .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 }            \
+}
+
+#define SST_AIF_OUT(wname, wevent)                                                     \
+{      .id = snd_soc_dapm_aif_out, .name = wname, .sname = NULL,                       \
+       .reg = SND_SOC_NOPM, .shift = 0,                                                \
+       .on_val = 1, .off_val = 0,                                                      \
+       .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD,   \
+       .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 }            \
+}
+
+#define SST_INPUT(wname, wevent)                                                       \
+{      .id = snd_soc_dapm_input, .name = wname, .sname = NULL,                         \
+       .reg = SND_SOC_NOPM, .shift = 0,                                                \
+       .on_val = 1, .off_val = 0,                                                      \
+       .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD,   \
+       .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 }            \
+}
+
+#define SST_OUTPUT(wname, wevent)                                                      \
+{      .id = snd_soc_dapm_output, .name = wname, .sname = NULL,                        \
+       .reg = SND_SOC_NOPM, .shift = 0,                                                \
+       .on_val = 1, .off_val = 0,                                                      \
+       .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD,   \
+       .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 }            \
+}
+
+#define SST_DAPM_OUTPUT(wname, wloc_id, wtask_id, wformat, wevent)                      \
+{      .id = snd_soc_dapm_output, .name = wname, .sname = NULL,                        \
+       .reg = SND_SOC_NOPM, .shift = 0,                                                \
+       .on_val = 1, .off_val = 0,                                                      \
+       .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD,   \
+       .priv = (void *)&(struct sst_ids) { .location_id = wloc_id, .task_id = wtask_id,\
+                                               .pcm_fmt = wformat, }                   \
+}
+
+#define SST_PATH(wname, wtask, wloc_id, wevent, wflags)                                        \
+{      .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0,         \
+       .kcontrol_news = NULL, .num_kcontrols = 0,                              \
+       .on_val = 1, .off_val = 0,                                                      \
+       .event = wevent, .event_flags = wflags,                                         \
+       .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, } \
+}
+
+#define SST_LINKED_PATH(wname, wtask, wloc_id, linked_wname, wevent, wflags)           \
+{      .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0,         \
+       .kcontrol_news = NULL, .num_kcontrols = 0,                              \
+       .on_val = 1, .off_val = 0,                                                      \
+       .event = wevent, .event_flags = wflags,                                         \
+       .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id,   \
+                                       .parent_wname = linked_wname}                   \
+}
+
+#define SST_PATH_MEDIA_LOOP(wname, wtask, wloc_id, wformat, wevent, wflags)             \
+{      .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0,         \
+       .kcontrol_news = NULL, .num_kcontrols = 0,                         \
+       .event = wevent, .event_flags = wflags,                                         \
+       .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id,   \
+                                           .format = wformat,}                         \
+}
+
+/* output is triggered before input */
+#define SST_PATH_INPUT(name, task_id, loc_id, event)                                   \
+       SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
+
+#define SST_PATH_LINKED_INPUT(name, task_id, loc_id, linked_wname, event)              \
+       SST_LINKED_PATH(name, task_id, loc_id, linked_wname, event,                     \
+                                       SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
+
+#define SST_PATH_OUTPUT(name, task_id, loc_id, event)                                  \
+       SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
+
+#define SST_PATH_LINKED_OUTPUT(name, task_id, loc_id, linked_wname, event)             \
+       SST_LINKED_PATH(name, task_id, loc_id, linked_wname, event,                     \
+                                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
+
+#define SST_PATH_MEDIA_LOOP_OUTPUT(name, task_id, loc_id, format, event)               \
+       SST_PATH_MEDIA_LOOP(name, task_id, loc_id, format, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
+
+
+#define SST_SWM_MIXER(wname, wreg, wtask, wloc_id, wcontrols, wevent)                  \
+{      .id = snd_soc_dapm_mixer, .name = wname, .reg = SND_SOC_NOPM, .shift = 0,       \
+       .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols),\
+       .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD |  \
+                                       SND_SOC_DAPM_POST_REG,                          \
+       .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id,   \
+                                           .reg = wreg }                               \
+}
+
+enum sst_gain_kcontrol_type {
+       SST_GAIN_TLV,
+       SST_GAIN_MUTE,
+       SST_GAIN_RAMP_DURATION,
+};
+
+struct sst_gain_mixer_control {
+       bool stereo;
+       enum sst_gain_kcontrol_type type;
+       struct sst_gain_value *gain_val;
+       int max;
+       int min;
+       u16 instance_id;
+       u16 module_id;
+       u16 pipe_id;
+       u16 task_id;
+       char pname[44];
+       struct snd_soc_dapm_widget *w;
+};
+
+struct sst_gain_value {
+       u16 ramp_duration;
+       s16 l_gain;
+       s16 r_gain;
+       bool mute;
+};
+#define SST_GAIN_VOLUME_DEFAULT                (-1440)
+#define SST_GAIN_RAMP_DURATION_DEFAULT 5 /* timeconstant */
+#define SST_GAIN_MUTE_DEFAULT          true
+
+#define SST_GAIN_KCONTROL_TLV(xname, xhandler_get, xhandler_put, \
+                             xmod, xpipe, xinstance, xtask, tlv_array, xgain_val, \
+                             xmin, xmax, xpname) \
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+                 SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+       .tlv.p = (tlv_array), \
+       .info = sst_gain_ctl_info,\
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = (unsigned long)&(struct sst_gain_mixer_control) \
+       { .stereo = true, .max = xmax, .min = xmin, .type = SST_GAIN_TLV, \
+         .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\
+         .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname}
+
+#define SST_GAIN_KCONTROL_INT(xname, xhandler_get, xhandler_put, \
+                             xmod, xpipe, xinstance, xtask, xtype, xgain_val, \
+                             xmin, xmax, xpname) \
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = sst_gain_ctl_info, \
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = (unsigned long)&(struct sst_gain_mixer_control) \
+       { .stereo = false, .max = xmax, .min = xmin, .type = xtype, \
+         .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\
+         .instance_id = xinstance, .gain_val = xgain_val, .pname =  xpname}
+
+#define SST_GAIN_KCONTROL_BOOL(xname, xhandler_get, xhandler_put,\
+                              xmod, xpipe, xinstance, xtask, xgain_val, xpname) \
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_bool_ext, \
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = (unsigned long)&(struct sst_gain_mixer_control) \
+       { .stereo = false, .type = SST_GAIN_MUTE, \
+         .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\
+         .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname}
 #define SST_CONTROL_NAME(xpname, xmname, xinstance, xtype) \
        xpname " " xmname " " #xinstance " " xtype
 
 #define SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, xtype, xsubmodule) \
        xpname " " xmname " " #xinstance " " xtype " " xsubmodule
+
+/*
+ * 3 Controls for each Gain module
+ * e.g.        - pcm0_in Gain 0 Volume
+ *     - pcm0_in Gain 0 Ramp Delay
+ *     - pcm0_in Gain 0 Switch
+ */
+#define SST_GAIN_KCONTROLS(xpname, xmname, xmin_gain, xmax_gain, xmin_tc, xmax_tc, \
+                          xhandler_get, xhandler_put, \
+                          xmod, xpipe, xinstance, xtask, tlv_array, xgain_val) \
+       { SST_GAIN_KCONTROL_INT(SST_CONTROL_NAME(xpname, xmname, xinstance, "Ramp Delay"), \
+               xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, SST_GAIN_RAMP_DURATION, \
+               xgain_val, xmin_tc, xmax_tc, xpname) }, \
+       { SST_GAIN_KCONTROL_BOOL(SST_CONTROL_NAME(xpname, xmname, xinstance, "Switch"), \
+               xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, \
+               xgain_val, xpname) } ,\
+       { SST_GAIN_KCONTROL_TLV(SST_CONTROL_NAME(xpname, xmname, xinstance, "Volume"), \
+               xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, tlv_array, \
+               xgain_val, xmin_gain, xmax_gain, xpname) }
+
+#define SST_GAIN_TC_MIN                5
+#define SST_GAIN_TC_MAX                5000
+#define SST_GAIN_MIN_VALUE     -1440 /* in 0.1 DB units */
+#define SST_GAIN_MAX_VALUE     360
+
 enum sst_algo_kcontrol_type {
        SST_ALGO_PARAMS,
        SST_ALGO_BYPASS,
@@ -439,4 +842,29 @@ struct sst_enum {
        struct snd_soc_dapm_widget *w;
 };
 
+/* only 4 slots/channels supported atm */
+#define SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts) \
+       (struct sst_enum){ .reg = s_ch_no, .tx = is_tx, .max = 4+1, .texts = xtexts, }
+
+#define SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name) \
+       xpname " " xmname " " s_ch_name
+
+#define SST_SSP_SLOT_CTL(xpname, xmname, s_ch_name, s_ch_no, is_tx, xtexts, xget, xput) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+       .name = SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name), \
+       .info = sst_slot_enum_info, \
+       .get = xget, .put = xput, \
+       .private_value = (unsigned long)&SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts), \
+}
+
+#define SST_MUX_CTL_NAME(xpname, xinstance) \
+       xpname " " #xinstance
+
+#define SST_SSP_MUX_ENUM(xreg, xshift, xtexts) \
+       (struct soc_enum) SOC_ENUM_DOUBLE(xreg, xshift, xshift, ARRAY_SIZE(xtexts), xtexts)
+
+#define SST_SSP_MUX_CTL(xpname, xinstance, xreg, xshift, xtexts) \
+       SOC_DAPM_ENUM(SST_MUX_CTL_NAME(xpname, xinstance), \
+                         SST_SSP_MUX_ENUM(xreg, xshift, xtexts))
+
 #endif
index fc58876..5a9e567 100644 (file)
@@ -67,17 +67,12 @@ static int sst_byt_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
 {
        struct dma_block_info *block;
        struct sst_module *mod;
-       struct sst_module_data block_data;
        struct sst_module_template template;
        int count;
 
        memset(&template, 0, sizeof(template));
        template.id = module->type;
        template.entry = module->entry_point;
-       template.p.type = SST_MEM_DRAM;
-       template.p.data_type = SST_DATA_P;
-       template.s.type = SST_MEM_DRAM;
-       template.s.data_type = SST_DATA_S;
 
        mod = sst_module_new(fw, &template, NULL);
        if (mod == NULL)
@@ -94,19 +89,19 @@ static int sst_byt_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
 
                switch (block->type) {
                case SST_BYT_IRAM:
-                       block_data.offset = block->ram_offset +
+                       mod->offset = block->ram_offset +
                                            dsp->addr.iram_offset;
-                       block_data.type = SST_MEM_IRAM;
+                       mod->type = SST_MEM_IRAM;
                        break;
                case SST_BYT_DRAM:
-                       block_data.offset = block->ram_offset +
+                       mod->offset = block->ram_offset +
                                            dsp->addr.dram_offset;
-                       block_data.type = SST_MEM_DRAM;
+                       mod->type = SST_MEM_DRAM;
                        break;
                case SST_BYT_CACHE:
-                       block_data.offset = block->ram_offset +
+                       mod->offset = block->ram_offset +
                                            (dsp->addr.fw_ext - dsp->addr.lpe);
-                       block_data.type = SST_MEM_CACHE;
+                       mod->type = SST_MEM_CACHE;
                        break;
                default:
                        dev_err(dsp->dev, "wrong ram type 0x%x in block0x%x\n",
@@ -114,11 +109,10 @@ static int sst_byt_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
                        return -EINVAL;
                }
 
-               block_data.size = block->size;
-               block_data.data_type = SST_DATA_M;
-               block_data.data = (void *)block + sizeof(*block);
+               mod->size = block->size;
+               mod->data = (void *)block + sizeof(*block);
 
-               sst_module_insert_fixed_block(mod, &block_data);
+               sst_module_alloc_blocks(mod);
 
                block = (void *)block + sizeof(*block) + block->size;
        }
index ffb308b..b9da030 100644 (file)
@@ -26,6 +26,9 @@ struct sst_mem_block;
 struct sst_module;
 struct sst_fw;
 
+/* do we need to remove or keep */
+#define DSP_DRAM_ADDR_OFFSET           0x400000
+
 /*
  * DSP Operations exported by platform Audio DSP driver.
  */
@@ -33,6 +36,9 @@ struct sst_ops {
        /* DSP core boot / reset */
        void (*boot)(struct sst_dsp *);
        void (*reset)(struct sst_dsp *);
+       int (*wake)(struct sst_dsp *);
+       void (*sleep)(struct sst_dsp *);
+       void (*stall)(struct sst_dsp *);
 
        /* Shim IO */
        void (*write)(void __iomem *addr, u32 offset, u32 value);
@@ -67,6 +73,8 @@ struct sst_addr {
        u32 shim_offset;
        u32 iram_offset;
        u32 dram_offset;
+       u32 dsp_iram_offset;
+       u32 dsp_dram_offset;
        void __iomem *lpe;
        void __iomem *shim;
        void __iomem *pci_cfg;
@@ -83,15 +91,6 @@ struct sst_mailbox {
        size_t out_size;
 };
 
-/*
- * Audio DSP Firmware data types.
- */
-enum sst_data_type {
-       SST_DATA_M      = 0, /* module block data */
-       SST_DATA_P      = 1, /* peristant data (text, data) */
-       SST_DATA_S      = 2, /* scratch data (usually buffers) */
-};
-
 /*
  * Audio DSP memory block types.
  */
@@ -124,23 +123,6 @@ struct sst_fw {
        void *private;                  /* core doesn't touch this */
 };
 
-/*
- * Audio DSP Generic Module data.
- *
- * This is used to dsecribe any sections of persistent (text and data) and
- * scratch (buffers) of module data in ADSP memory space.
- */
-struct sst_module_data {
-
-       enum sst_mem_type type;         /* destination memory type */
-       enum sst_data_type data_type;   /* type of module data */
-
-       u32 size;               /* size in bytes */
-       int32_t offset;         /* offset in FW file */
-       u32 data_offset;        /* offset in ADSP memory space */
-       void *data;             /* module data */
-};
-
 /*
  * Audio DSP Generic Module Template.
  *
@@ -150,15 +132,52 @@ struct sst_module_data {
 struct sst_module_template {
        u32 id;
        u32 entry;                      /* entry point */
-       struct sst_module_data s;       /* scratch data */
-       struct sst_module_data p;       /* peristant data */
+       u32 scratch_size;
+       u32 persistent_size;
+};
+
+/*
+ * Block Allocator - Used to allocate blocks of DSP memory.
+ */
+struct sst_block_allocator {
+       u32 id;
+       u32 offset;
+       int size;
+       enum sst_mem_type type;
+};
+
+/*
+ * Runtime Module Instance - A module object can be instanciated multiple
+ * times within the DSP FW.
+ */
+struct sst_module_runtime {
+       struct sst_dsp *dsp;
+       int id;
+       struct sst_module *module;      /* parent module we belong too */
+
+       u32 persistent_offset;          /* private memory offset */
+       void *private;
+
+       struct list_head list;
+       struct list_head block_list;    /* list of blocks used */
+};
+
+/*
+ * Runtime Module Context - The runtime context must be manually stored by the
+ * driver prior to enter S3 and restored after leaving S3. This should really be
+ * part of the memory context saved by the enter D3 message IPC ???
+ */
+struct sst_module_runtime_context {
+       dma_addr_t dma_buffer;
+       u32 *buffer;
 };
 
 /*
  * Audio DSP Generic Module.
  *
  * Each Firmware file can consist of 1..N modules. A module can span multiple
- * ADSP memory blocks. The simplest FW will be a file with 1 module.
+ * ADSP memory blocks. The simplest FW will be a file with 1 module. A module
+ * can be instanciated multiple times in the DSP.
  */
 struct sst_module {
        struct sst_dsp *dsp;
@@ -167,10 +186,13 @@ struct sst_module {
        /* module configuration */
        u32 id;
        u32 entry;                      /* module entry point */
-       u32 offset;                     /* module offset in firmware file */
+       s32 offset;                     /* module offset in firmware file */
        u32 size;                       /* module size */
-       struct sst_module_data s;       /* scratch data */
-       struct sst_module_data p;       /* peristant data */
+       u32 scratch_size;               /* global scratch memory required */
+       u32 persistent_size;            /* private memory required */
+       enum sst_mem_type type;         /* destination memory type */
+       u32 data_offset;                /* offset in ADSP memory space */
+       void *data;                     /* module data */
 
        /* runtime */
        u32 usage_count;                /* can be unloaded if count == 0 */
@@ -180,6 +202,7 @@ struct sst_module {
        struct list_head block_list;    /* Module list of blocks in use */
        struct list_head list;          /* DSP list of modules */
        struct list_head list_fw;       /* FW list of modules */
+       struct list_head runtime_list;  /* list of runtime module objects*/
 };
 
 /*
@@ -208,7 +231,6 @@ struct sst_mem_block {
        struct sst_block_ops *ops;      /* block operations, if any */
 
        /* block status */
-       enum sst_data_type data_type;   /* data type held in this block */
        u32 bytes_used;                 /* bytes in use by modules */
        void *private;                  /* generic core does not touch this */
        int users;                      /* number of modules using this block */
@@ -253,6 +275,11 @@ struct sst_dsp {
        struct list_head module_list;
        struct list_head fw_list;
 
+       /* scratch buffer */
+       struct list_head scratch_block_list;
+       u32 scratch_offset;
+       u32 scratch_size;
+
        /* platform data */
        struct sst_pdata *pdata;
 
@@ -290,18 +317,33 @@ void sst_fw_unload(struct sst_fw *sst_fw);
 /* Create/Free firmware modules */
 struct sst_module *sst_module_new(struct sst_fw *sst_fw,
        struct sst_module_template *template, void *private);
-void sst_module_free(struct sst_module *sst_module);
-int sst_module_insert(struct sst_module *sst_module);
-int sst_module_remove(struct sst_module *sst_module);
-int sst_module_insert_fixed_block(struct sst_module *module,
-       struct sst_module_data *data);
+void sst_module_free(struct sst_module *module);
 struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id);
-
-/* allocate/free pesistent/scratch memory regions managed by drv */
-struct sst_module *sst_mem_block_alloc_scratch(struct sst_dsp *dsp);
-void sst_mem_block_free_scratch(struct sst_dsp *dsp,
-       struct sst_module *scratch);
-int sst_block_module_remove(struct sst_module *module);
+int sst_module_alloc_blocks(struct sst_module *module);
+int sst_module_free_blocks(struct sst_module *module);
+
+/* Create/Free firmware module runtime instances */
+struct sst_module_runtime *sst_module_runtime_new(struct sst_module *module,
+       int id, void *private);
+void sst_module_runtime_free(struct sst_module_runtime *runtime);
+struct sst_module_runtime *sst_module_runtime_get_from_id(
+       struct sst_module *module, u32 id);
+int sst_module_runtime_alloc_blocks(struct sst_module_runtime *runtime,
+       int offset);
+int sst_module_runtime_free_blocks(struct sst_module_runtime *runtime);
+int sst_module_runtime_save(struct sst_module_runtime *runtime,
+       struct sst_module_runtime_context *context);
+int sst_module_runtime_restore(struct sst_module_runtime *runtime,
+       struct sst_module_runtime_context *context);
+
+/* generic block allocation */
+int sst_alloc_blocks(struct sst_dsp *dsp, struct sst_block_allocator *ba,
+       struct list_head *block_list);
+int sst_free_blocks(struct sst_dsp *dsp, struct list_head *block_list);
+
+/* scratch allocation */
+int sst_block_alloc_scratch(struct sst_dsp *dsp);
+void sst_block_free_scratch(struct sst_dsp *dsp);
 
 /* Register the DSPs memory blocks - would be nice to read from ACPI */
 struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
@@ -309,4 +351,10 @@ struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
        void *private);
 void sst_mem_block_unregister_all(struct sst_dsp *dsp);
 
+/* Create/Free DMA resources */
+int sst_dma_new(struct sst_dsp *sst);
+void sst_dma_free(struct sst_dma *dma);
+
+u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset,
+       enum sst_mem_type type);
 #endif
index cd23060..86e4108 100644 (file)
@@ -245,6 +245,29 @@ int sst_dsp_boot(struct sst_dsp *sst)
 }
 EXPORT_SYMBOL_GPL(sst_dsp_boot);
 
+int sst_dsp_wake(struct sst_dsp *sst)
+{
+       if (sst->ops->wake)
+               return sst->ops->wake(sst);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_wake);
+
+void sst_dsp_sleep(struct sst_dsp *sst)
+{
+       if (sst->ops->sleep)
+               sst->ops->sleep(sst);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_sleep);
+
+void sst_dsp_stall(struct sst_dsp *sst)
+{
+       if (sst->ops->stall)
+               sst->ops->stall(sst);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_stall);
+
 void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg)
 {
        sst_dsp_shim_write_unlocked(dsp, SST_IPCX, msg | SST_IPCX_BUSY);
@@ -352,6 +375,7 @@ struct sst_dsp *sst_dsp_new(struct device *dev,
        INIT_LIST_HEAD(&sst->free_block_list);
        INIT_LIST_HEAD(&sst->module_list);
        INIT_LIST_HEAD(&sst->fw_list);
+       INIT_LIST_HEAD(&sst->scratch_block_list);
 
        /* Initialise SST Audio DSP */
        if (sst->ops->init) {
@@ -366,6 +390,10 @@ struct sst_dsp *sst_dsp_new(struct device *dev,
        if (err)
                goto irq_err;
 
+       err = sst_dma_new(sst);
+       if (err)
+               dev_warn(dev, "sst_dma_new failed %d\n", err);
+
        return sst;
 
 irq_err:
@@ -381,6 +409,9 @@ void sst_dsp_free(struct sst_dsp *sst)
        free_irq(sst->irq, sst);
        if (sst->ops->free)
                sst->ops->free(sst);
+
+       if (sst->dma)
+               sst_dma_free(sst->dma);
 }
 EXPORT_SYMBOL_GPL(sst_dsp_free);
 
index 3165dfa..f291e32 100644 (file)
@@ -30,6 +30,9 @@
 #define SST_DMA_TYPE_DW                1
 #define SST_DMA_TYPE_MID       2
 
+/* autosuspend delay 5s*/
+#define SST_RUNTIME_SUSPEND_DELAY      (5 * 1000)
+
 /* SST Shim register map
  * The register naming can differ between products. Some products also
  * contain extra functionality.
 #define SST_VDRTCTL3           0xaC
 
 /* VDRTCTL0 */
-#define SST_VDRTCL0_APLLSE_MASK                1
-#define SST_VDRTCL0_DSRAMPGE_SHIFT     16
-#define SST_VDRTCL0_DSRAMPGE_MASK      (0xffff << SST_VDRTCL0_DSRAMPGE_SHIFT)
-#define SST_VDRTCL0_ISRAMPGE_SHIFT     6
+#define SST_VDRTCL0_D3PGD              (1 << 0)
+#define SST_VDRTCL0_D3SRAMPGD          (1 << 1)
+#define SST_VDRTCL0_DSRAMPGE_SHIFT     12
+#define SST_VDRTCL0_DSRAMPGE_MASK      (0xfffff << SST_VDRTCL0_DSRAMPGE_SHIFT)
+#define SST_VDRTCL0_ISRAMPGE_SHIFT     2
 #define SST_VDRTCL0_ISRAMPGE_MASK      (0x3ff << SST_VDRTCL0_ISRAMPGE_SHIFT)
 
+/* VDRTCTL2 */
+#define SST_VDRTCL2_DCLCGE             (1 << 1)
+#define SST_VDRTCL2_DTCGE              (1 << 10)
+#define SST_VDRTCL2_APLLSE_MASK                (1 << 31)
+
 /* PMCS */
 #define SST_PMCS               0x84
 #define SST_PMCS_PS_MASK       0x3
@@ -245,6 +254,17 @@ void sst_memcpy_fromio_32(struct sst_dsp *sst,
 /* DSP reset & boot */
 void sst_dsp_reset(struct sst_dsp *sst);
 int sst_dsp_boot(struct sst_dsp *sst);
+int sst_dsp_wake(struct sst_dsp *sst);
+void sst_dsp_sleep(struct sst_dsp *sst);
+void sst_dsp_stall(struct sst_dsp *sst);
+
+/* DMA */
+int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id);
+void sst_dsp_dma_put_channel(struct sst_dsp *dsp);
+int sst_dsp_dma_copyfrom(struct sst_dsp *sst, dma_addr_t dest_addr,
+       dma_addr_t src_addr, size_t size);
+int sst_dsp_dma_copyto(struct sst_dsp *sst, dma_addr_t dest_addr,
+       dma_addr_t src_addr, size_t size);
 
 /* Msg IO */
 void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg);
index 3bb43da..4a5bde9 100644 (file)
 #include <linux/dma-mapping.h>
 #include <linux/dmaengine.h>
 #include <linux/pci.h>
+#include <linux/acpi.h>
+
+/* supported DMA engine drivers */
+#include <linux/platform_data/dma-dw.h>
+#include <linux/dma/dw.h>
 
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include "sst-dsp.h"
 #include "sst-dsp-priv.h"
 
-static void block_module_remove(struct sst_module *module);
+#define SST_DMA_RESOURCES      2
+#define SST_DSP_DMA_MAX_BURST  0x3
+#define SST_HSW_BLOCK_ANY      0xffffffff
+
+#define SST_HSW_MASK_DMA_ADDR_DSP 0xfff00000
+
+struct sst_dma {
+       struct sst_dsp *sst;
+
+       struct dw_dma_chip *chip;
+
+       struct dma_async_tx_descriptor *desc;
+       struct dma_chan *ch;
+};
+
+static inline void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes)
+{
+       /* __iowrite32_copy use 32bit size values so divide by 4 */
+       __iowrite32_copy((void *)dest, src, bytes/4);
+}
+
+static void sst_dma_transfer_complete(void *arg)
+{
+       struct sst_dsp *sst = (struct sst_dsp *)arg;
+
+       dev_dbg(sst->dev, "DMA: callback\n");
+}
+
+static int sst_dsp_dma_copy(struct sst_dsp *sst, dma_addr_t dest_addr,
+       dma_addr_t src_addr, size_t size)
+{
+       struct dma_async_tx_descriptor *desc;
+       struct sst_dma *dma = sst->dma;
+
+       if (dma->ch == NULL) {
+               dev_err(sst->dev, "error: no DMA channel\n");
+               return -ENODEV;
+       }
+
+       dev_dbg(sst->dev, "DMA: src: 0x%lx dest 0x%lx size %zu\n",
+               (unsigned long)src_addr, (unsigned long)dest_addr, size);
+
+       desc = dma->ch->device->device_prep_dma_memcpy(dma->ch, dest_addr,
+               src_addr, size, DMA_CTRL_ACK);
+       if (!desc){
+               dev_err(sst->dev, "error: dma prep memcpy failed\n");
+               return -EINVAL;
+       }
+
+       desc->callback = sst_dma_transfer_complete;
+       desc->callback_param = sst;
+
+       desc->tx_submit(desc);
+       dma_wait_for_async_tx(desc);
+
+       return 0;
+}
+
+/* copy to DSP */
+int sst_dsp_dma_copyto(struct sst_dsp *sst, dma_addr_t dest_addr,
+       dma_addr_t src_addr, size_t size)
+{
+       return sst_dsp_dma_copy(sst, dest_addr | SST_HSW_MASK_DMA_ADDR_DSP,
+                       src_addr, size);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_dma_copyto);
+
+/* copy from DSP */
+int sst_dsp_dma_copyfrom(struct sst_dsp *sst, dma_addr_t dest_addr,
+       dma_addr_t src_addr, size_t size)
+{
+       return sst_dsp_dma_copy(sst, dest_addr,
+               src_addr | SST_HSW_MASK_DMA_ADDR_DSP, size);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_dma_copyfrom);
+
+/* remove module from memory - callers hold locks */
+static void block_list_remove(struct sst_dsp *dsp,
+       struct list_head *block_list)
+{
+       struct sst_mem_block *block, *tmp;
+       int err;
+
+       /* disable each block  */
+       list_for_each_entry(block, block_list, module_list) {
+
+               if (block->ops && block->ops->disable) {
+                       err = block->ops->disable(block);
+                       if (err < 0)
+                               dev_err(dsp->dev,
+                                       "error: cant disable block %d:%d\n",
+                                       block->type, block->index);
+               }
+       }
+
+       /* mark each block as free */
+       list_for_each_entry_safe(block, tmp, block_list, module_list) {
+               list_del(&block->module_list);
+               list_move(&block->list, &dsp->free_block_list);
+               dev_dbg(dsp->dev, "block freed %d:%d at offset 0x%x\n",
+                       block->type, block->index, block->offset);
+       }
+}
+
+/* prepare the memory block to receive data from host - callers hold locks */
+static int block_list_prepare(struct sst_dsp *dsp,
+       struct list_head *block_list)
+{
+       struct sst_mem_block *block;
+       int ret = 0;
+
+       /* enable each block so that's it'e ready for data */
+       list_for_each_entry(block, block_list, module_list) {
+
+               if (block->ops && block->ops->enable && !block->users) {
+                       ret = block->ops->enable(block);
+                       if (ret < 0) {
+                               dev_err(dsp->dev,
+                                       "error: cant disable block %d:%d\n",
+                                       block->type, block->index);
+                               goto err;
+                       }
+               }
+       }
+       return ret;
+
+err:
+       list_for_each_entry(block, block_list, module_list) {
+               if (block->ops && block->ops->disable)
+                       block->ops->disable(block);
+       }
+       return ret;
+}
+
+static struct dw_dma_platform_data dw_pdata = {
+       .is_private = 1,
+       .chan_allocation_order = CHAN_ALLOCATION_ASCENDING,
+       .chan_priority = CHAN_PRIORITY_ASCENDING,
+};
+
+static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem,
+       int irq)
+{
+       struct dw_dma_chip *chip;
+       int err;
+
+       chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return ERR_PTR(-ENOMEM);
+
+       chip->irq = irq;
+       chip->regs = devm_ioremap_resource(dev, mem);
+       if (IS_ERR(chip->regs))
+               return ERR_CAST(chip->regs);
+
+       err = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(31));
+       if (err)
+               return ERR_PTR(err);
+
+       chip->dev = dev;
+       err = dw_dma_probe(chip, &dw_pdata);
+       if (err)
+               return ERR_PTR(err);
+
+       return chip;
+}
+
+static void dw_remove(struct dw_dma_chip *chip)
+{
+       dw_dma_remove(chip);
+}
+
+static bool dma_chan_filter(struct dma_chan *chan, void *param)
+{
+       struct sst_dsp *dsp = (struct sst_dsp *)param;
+
+       return chan->device->dev == dsp->dma_dev;
+}
 
-static void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes)
+int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id)
 {
-       u32 i;
+       struct sst_dma *dma = dsp->dma;
+       struct dma_slave_config slave;
+       dma_cap_mask_t mask;
+       int ret;
+
+       /* The Intel MID DMA engine driver needs the slave config set but
+        * Synopsis DMA engine driver safely ignores the slave config */
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+       dma_cap_set(DMA_MEMCPY, mask);
+
+       dma->ch = dma_request_channel(mask, dma_chan_filter, dsp);
+       if (dma->ch == NULL) {
+               dev_err(dsp->dev, "error: DMA request channel failed\n");
+               return -EIO;
+       }
+
+       memset(&slave, 0, sizeof(slave));
+       slave.direction = DMA_MEM_TO_DEV;
+       slave.src_addr_width =
+               slave.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       slave.src_maxburst = slave.dst_maxburst = SST_DSP_DMA_MAX_BURST;
+
+       ret = dmaengine_slave_config(dma->ch, &slave);
+       if (ret) {
+               dev_err(dsp->dev, "error: unable to set DMA slave config %d\n",
+                       ret);
+               dma_release_channel(dma->ch);
+               dma->ch = NULL;
+       }
 
-       /* copy one 32 bit word at a time as 64 bit access is not supported */
-       for (i = 0; i < bytes; i += 4)
-               memcpy_toio(dest + i, src + i, 4);
+       return ret;
 }
+EXPORT_SYMBOL_GPL(sst_dsp_dma_get_channel);
+
+void sst_dsp_dma_put_channel(struct sst_dsp *dsp)
+{
+       struct sst_dma *dma = dsp->dma;
+
+       if (!dma->ch)
+               return;
+
+       dma_release_channel(dma->ch);
+       dma->ch = NULL;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_dma_put_channel);
+
+int sst_dma_new(struct sst_dsp *sst)
+{
+       struct sst_pdata *sst_pdata = sst->pdata;
+       struct sst_dma *dma;
+       struct resource mem;
+       const char *dma_dev_name;
+       int ret = 0;
+
+       /* configure the correct platform data for whatever DMA engine
+       * is attached to the ADSP IP. */
+       switch (sst->pdata->dma_engine) {
+       case SST_DMA_TYPE_DW:
+               dma_dev_name = "dw_dmac";
+               break;
+       case SST_DMA_TYPE_MID:
+               dma_dev_name = "Intel MID DMA";
+               break;
+       default:
+               dev_err(sst->dev, "error: invalid DMA engine %d\n",
+                       sst->pdata->dma_engine);
+               return -EINVAL;
+       }
+
+       dma = devm_kzalloc(sst->dev, sizeof(struct sst_dma), GFP_KERNEL);
+       if (!dma)
+               return -ENOMEM;
+
+       dma->sst = sst;
+
+       memset(&mem, 0, sizeof(mem));
+
+       mem.start = sst->addr.lpe_base + sst_pdata->dma_base;
+       mem.end   = sst->addr.lpe_base + sst_pdata->dma_base + sst_pdata->dma_size - 1;
+       mem.flags = IORESOURCE_MEM;
+
+       /* now register DMA engine device */
+       dma->chip = dw_probe(sst->dma_dev, &mem, sst_pdata->irq);
+       if (IS_ERR(dma->chip)) {
+               dev_err(sst->dev, "error: DMA device register failed\n");
+               ret = PTR_ERR(dma->chip);
+               goto err_dma_dev;
+       }
+
+       sst->dma = dma;
+       sst->fw_use_dma = true;
+       return 0;
+
+err_dma_dev:
+       devm_kfree(sst->dev, dma);
+       return ret;
+}
+EXPORT_SYMBOL(sst_dma_new);
+
+void sst_dma_free(struct sst_dma *dma)
+{
+
+       if (dma == NULL)
+               return;
+
+       if (dma->ch)
+               dma_release_channel(dma->ch);
+
+       if (dma->chip)
+               dw_remove(dma->chip);
+
+}
+EXPORT_SYMBOL(sst_dma_free);
 
 /* create new generic firmware object */
 struct sst_fw *sst_fw_new(struct sst_dsp *dsp, 
@@ -71,6 +361,12 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
        /* copy FW data to DMA-able memory */
        memcpy((void *)sst_fw->dma_buf, (void *)fw->data, fw->size);
 
+       if (dsp->fw_use_dma) {
+               err = sst_dsp_dma_get_channel(dsp, 0);
+               if (err < 0)
+                       goto chan_err;
+       }
+
        /* call core specific FW paser to load FW data into DSP */
        err = dsp->ops->parse_fw(sst_fw);
        if (err < 0) {
@@ -78,6 +374,9 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
                goto parse_err;
        }
 
+       if (dsp->fw_use_dma)
+               sst_dsp_dma_put_channel(dsp);
+
        mutex_lock(&dsp->mutex);
        list_add(&sst_fw->list, &dsp->fw_list);
        mutex_unlock(&dsp->mutex);
@@ -85,9 +384,13 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
        return sst_fw;
 
 parse_err:
-       dma_free_coherent(dsp->dev, sst_fw->size,
+       if (dsp->fw_use_dma)
+               sst_dsp_dma_put_channel(dsp);
+chan_err:
+       dma_free_coherent(dsp->dma_dev, sst_fw->size,
                                sst_fw->dma_buf,
                                sst_fw->dmable_fw_paddr);
+       sst_fw->dma_buf = NULL;
        kfree(sst_fw);
        return NULL;
 }
@@ -111,21 +414,37 @@ EXPORT_SYMBOL_GPL(sst_fw_reload);
 
 void sst_fw_unload(struct sst_fw *sst_fw)
 {
-        struct sst_dsp *dsp = sst_fw->dsp;
-        struct sst_module *module, *tmp;
+       struct sst_dsp *dsp = sst_fw->dsp;
+       struct sst_module *module, *mtmp;
+       struct sst_module_runtime *runtime, *rtmp;
+
+       dev_dbg(dsp->dev, "unloading firmware\n");
+
+       mutex_lock(&dsp->mutex);
+
+       /* check module by module */
+       list_for_each_entry_safe(module, mtmp, &dsp->module_list, list) {
+               if (module->sst_fw == sst_fw) {
+
+                       /* remove runtime modules */
+                       list_for_each_entry_safe(runtime, rtmp, &module->runtime_list, list) {
 
-        dev_dbg(dsp->dev, "unloading firmware\n");
+                               block_list_remove(dsp, &runtime->block_list);
+                               list_del(&runtime->list);
+                               kfree(runtime);
+                       }
+
+                       /* now remove the module */
+                       block_list_remove(dsp, &module->block_list);
+                       list_del(&module->list);
+                       kfree(module);
+               }
+       }
 
-        mutex_lock(&dsp->mutex);
-        list_for_each_entry_safe(module, tmp, &dsp->module_list, list) {
-                if (module->sst_fw == sst_fw) {
-                        block_module_remove(module);
-                        list_del(&module->list);
-                        kfree(module);
-                }
-        }
+       /* remove all scratch blocks */
+       block_list_remove(dsp, &dsp->scratch_block_list);
 
-        mutex_unlock(&dsp->mutex);
+       mutex_unlock(&dsp->mutex);
 }
 EXPORT_SYMBOL_GPL(sst_fw_unload);
 
@@ -138,7 +457,8 @@ void sst_fw_free(struct sst_fw *sst_fw)
        list_del(&sst_fw->list);
        mutex_unlock(&dsp->mutex);
 
-       dma_free_coherent(dsp->dma_dev, sst_fw->size, sst_fw->dma_buf,
+       if (sst_fw->dma_buf)
+               dma_free_coherent(dsp->dma_dev, sst_fw->size, sst_fw->dma_buf,
                        sst_fw->dmable_fw_paddr);
        kfree(sst_fw);
 }
@@ -175,11 +495,11 @@ struct sst_module *sst_module_new(struct sst_fw *sst_fw,
        sst_module->id = template->id;
        sst_module->dsp = dsp;
        sst_module->sst_fw = sst_fw;
-
-       memcpy(&sst_module->s, &template->s, sizeof(struct sst_module_data));
-       memcpy(&sst_module->p, &template->p, sizeof(struct sst_module_data));
+       sst_module->scratch_size = template->scratch_size;
+       sst_module->persistent_size = template->persistent_size;
 
        INIT_LIST_HEAD(&sst_module->block_list);
+       INIT_LIST_HEAD(&sst_module->runtime_list);
 
        mutex_lock(&dsp->mutex);
        list_add(&sst_module->list, &dsp->module_list);
@@ -202,73 +522,122 @@ void sst_module_free(struct sst_module *sst_module)
 }
 EXPORT_SYMBOL_GPL(sst_module_free);
 
-static struct sst_mem_block *find_block(struct sst_dsp *dsp, int type,
-       u32 offset)
+struct sst_module_runtime *sst_module_runtime_new(struct sst_module *module,
+       int id, void *private)
+{
+       struct sst_dsp *dsp = module->dsp;
+       struct sst_module_runtime *runtime;
+
+       runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
+       if (runtime == NULL)
+               return NULL;
+
+       runtime->id = id;
+       runtime->dsp = dsp;
+       runtime->module = module;
+       INIT_LIST_HEAD(&runtime->block_list);
+
+       mutex_lock(&dsp->mutex);
+       list_add(&runtime->list, &module->runtime_list);
+       mutex_unlock(&dsp->mutex);
+
+       return runtime;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_new);
+
+void sst_module_runtime_free(struct sst_module_runtime *runtime)
+{
+       struct sst_dsp *dsp = runtime->dsp;
+
+       mutex_lock(&dsp->mutex);
+       list_del(&runtime->list);
+       mutex_unlock(&dsp->mutex);
+
+       kfree(runtime);
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_free);
+
+static struct sst_mem_block *find_block(struct sst_dsp *dsp,
+       struct sst_block_allocator *ba)
 {
        struct sst_mem_block *block;
 
        list_for_each_entry(block, &dsp->free_block_list, list) {
-               if (block->type == type && block->offset == offset)
+               if (block->type == ba->type && block->offset == ba->offset)
                        return block;
        }
 
        return NULL;
 }
 
-static int block_alloc_contiguous(struct sst_module *module,
-       struct sst_module_data *data, u32 offset, int size)
+/* Block allocator must be on block boundary */
+static int block_alloc_contiguous(struct sst_dsp *dsp,
+       struct sst_block_allocator *ba, struct list_head *block_list)
 {
        struct list_head tmp = LIST_HEAD_INIT(tmp);
-       struct sst_dsp *dsp = module->dsp;
        struct sst_mem_block *block;
+       u32 block_start = SST_HSW_BLOCK_ANY;
+       int size = ba->size, offset = ba->offset;
+
+       while (ba->size > 0) {
 
-       while (size > 0) {
-               block = find_block(dsp, data->type, offset);
+               block = find_block(dsp, ba);
                if (!block) {
                        list_splice(&tmp, &dsp->free_block_list);
+
+                       ba->size = size;
+                       ba->offset = offset;
                        return -ENOMEM;
                }
 
                list_move_tail(&block->list, &tmp);
-               offset += block->size;
-               size -= block->size;
+               ba->offset += block->size;
+               ba->size -= block->size;
        }
+       ba->size = size;
+       ba->offset = offset;
+
+       list_for_each_entry(block, &tmp, list) {
+
+               if (block->offset < block_start)
+                       block_start = block->offset;
 
-       list_for_each_entry(block, &tmp, list)
-               list_add(&block->module_list, &module->block_list);
+               list_add(&block->module_list, block_list);
+
+               dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n",
+                       block->type, block->index, block->offset);
+       }
 
        list_splice(&tmp, &dsp->used_block_list);
        return 0;
 }
 
-/* allocate free DSP blocks for module data - callers hold locks */
-static int block_alloc(struct sst_module *module,
-       struct sst_module_data *data)
+/* allocate first free DSP blocks for data - callers hold locks */
+static int block_alloc(struct sst_dsp *dsp, struct sst_block_allocator *ba,
+       struct list_head *block_list)
 {
-       struct sst_dsp *dsp = module->dsp;
        struct sst_mem_block *block, *tmp;
        int ret = 0;
 
-       if (data->size == 0)
+       if (ba->size == 0)
                return 0;
 
        /* find first free whole blocks that can hold module */
        list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
 
                /* ignore blocks with wrong type */
-               if (block->type != data->type)
+               if (block->type != ba->type)
                        continue;
 
-               if (data->size > block->size)
+               if (ba->size > block->size)
                        continue;
 
-               data->offset = block->offset;
-               block->data_type = data->data_type;
-               block->bytes_used = data->size % block->size;
-               list_add(&block->module_list, &module->block_list);
+               ba->offset = block->offset;
+               block->bytes_used = ba->size % block->size;
+               list_add(&block->module_list, block_list);
                list_move(&block->list, &dsp->used_block_list);
-               dev_dbg(dsp->dev, " *module %d added block %d:%d\n",
-                       module->id, block->type, block->index);
+               dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n",
+                       block->type, block->index, block->offset);
                return 0;
        }
 
@@ -276,15 +645,19 @@ static int block_alloc(struct sst_module *module,
        list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
 
                /* ignore blocks with wrong type */
-               if (block->type != data->type)
+               if (block->type != ba->type)
                        continue;
 
                /* do we span > 1 blocks */
-               if (data->size > block->size) {
-                       ret = block_alloc_contiguous(module, data,
-                               block->offset, data->size);
+               if (ba->size > block->size) {
+
+                       /* align ba to block boundary */
+                       ba->offset = block->offset;
+
+                       ret = block_alloc_contiguous(dsp, ba, block_list);
                        if (ret == 0)
                                return ret;
+
                }
        }
 
@@ -292,93 +665,74 @@ static int block_alloc(struct sst_module *module,
        return -ENOMEM;
 }
 
-/* remove module from memory - callers hold locks */
-static void block_module_remove(struct sst_module *module)
+int sst_alloc_blocks(struct sst_dsp *dsp, struct sst_block_allocator *ba,
+       struct list_head *block_list)
 {
-       struct sst_mem_block *block, *tmp;
-       struct sst_dsp *dsp = module->dsp;
-       int err;
+       int ret;
 
-       /* disable each block  */
-       list_for_each_entry(block, &module->block_list, module_list) {
+       dev_dbg(dsp->dev, "block request 0x%x bytes at offset 0x%x type %d\n",
+               ba->size, ba->offset, ba->type);
 
-               if (block->ops && block->ops->disable) {
-                       err = block->ops->disable(block);
-                       if (err < 0)
-                               dev_err(dsp->dev,
-                                       "error: cant disable block %d:%d\n",
-                                       block->type, block->index);
-               }
-       }
+       mutex_lock(&dsp->mutex);
 
-       /* mark each block as free */
-       list_for_each_entry_safe(block, tmp, &module->block_list, module_list) {
-               list_del(&block->module_list);
-               list_move(&block->list, &dsp->free_block_list);
+       ret = block_alloc(dsp, ba, block_list);
+       if (ret < 0) {
+               dev_err(dsp->dev, "error: can't alloc blocks %d\n", ret);
+               goto out;
        }
-}
-
-/* prepare the memory block to receive data from host - callers hold locks */
-static int block_module_prepare(struct sst_module *module)
-{
-       struct sst_mem_block *block;
-       int ret = 0;
 
-       /* enable each block so that's it'e ready for module P/S data */
-       list_for_each_entry(block, &module->block_list, module_list) {
+       /* prepare DSP blocks for module usage */
+       ret = block_list_prepare(dsp, block_list);
+       if (ret < 0)
+               dev_err(dsp->dev, "error: prepare failed\n");
 
-               if (block->ops && block->ops->enable) {
-                       ret = block->ops->enable(block);
-                       if (ret < 0) {
-                               dev_err(module->dsp->dev,
-                                       "error: cant disable block %d:%d\n",
-                                       block->type, block->index);
-                               goto err;
-                       }
-               }
-       }
+out:
+       mutex_unlock(&dsp->mutex);
        return ret;
+}
+EXPORT_SYMBOL_GPL(sst_alloc_blocks);
 
-err:
-       list_for_each_entry(block, &module->block_list, module_list) {
-               if (block->ops && block->ops->disable)
-                       block->ops->disable(block);
-       }
-       return ret;
+int sst_free_blocks(struct sst_dsp *dsp, struct list_head *block_list)
+{
+       mutex_lock(&dsp->mutex);
+       block_list_remove(dsp, block_list);
+       mutex_unlock(&dsp->mutex);
+       return 0;
 }
+EXPORT_SYMBOL_GPL(sst_free_blocks);
 
 /* allocate memory blocks for static module addresses - callers hold locks */
-static int block_alloc_fixed(struct sst_module *module,
-       struct sst_module_data *data)
+static int block_alloc_fixed(struct sst_dsp *dsp, struct sst_block_allocator *ba,
+       struct list_head *block_list)
 {
-       struct sst_dsp *dsp = module->dsp;
        struct sst_mem_block *block, *tmp;
-       u32 end = data->offset + data->size, block_end;
+       u32 end = ba->offset + ba->size, block_end;
        int err;
 
        /* only IRAM/DRAM blocks are managed */
-       if (data->type != SST_MEM_IRAM && data->type != SST_MEM_DRAM)
+       if (ba->type != SST_MEM_IRAM && ba->type != SST_MEM_DRAM)
                return 0;
 
        /* are blocks already attached to this module */
-       list_for_each_entry_safe(block, tmp, &module->block_list, module_list) {
+       list_for_each_entry_safe(block, tmp, block_list, module_list) {
 
-               /* force compacting mem blocks of the same data_type */
-               if (block->data_type != data->data_type)
+               /* ignore blocks with wrong type */
+               if (block->type != ba->type)
                        continue;
 
                block_end = block->offset + block->size;
 
                /* find block that holds section */
-               if (data->offset >= block->offset && end < block_end)
+               if (ba->offset >= block->offset && end <= block_end)
                        return 0;
 
                /* does block span more than 1 section */
-               if (data->offset >= block->offset && data->offset < block_end) {
+               if (ba->offset >= block->offset && ba->offset < block_end) {
 
-                       err = block_alloc_contiguous(module, data,
-                               block->offset + block->size,
-                               data->size - block->size);
+                       /* align ba to block boundary */
+                       ba->size -= block_end - ba->offset;
+                       ba->offset = block_end;
+                       err = block_alloc_contiguous(dsp, ba, block_list);
                        if (err < 0)
                                return -ENOMEM;
 
@@ -391,82 +745,270 @@ static int block_alloc_fixed(struct sst_module *module,
        list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
                block_end = block->offset + block->size;
 
+               /* ignore blocks with wrong type */
+               if (block->type != ba->type)
+                       continue;
+
                /* find block that holds section */
-               if (data->offset >= block->offset && end < block_end) {
+               if (ba->offset >= block->offset && end <= block_end) {
 
                        /* add block */
-                       block->data_type = data->data_type;
                        list_move(&block->list, &dsp->used_block_list);
-                       list_add(&block->module_list, &module->block_list);
+                       list_add(&block->module_list, block_list);
+                       dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n",
+                               block->type, block->index, block->offset);
                        return 0;
                }
 
                /* does block span more than 1 section */
-               if (data->offset >= block->offset && data->offset < block_end) {
+               if (ba->offset >= block->offset && ba->offset < block_end) {
 
-                       err = block_alloc_contiguous(module, data,
-                               block->offset, data->size);
+                       /* align ba to block boundary */
+                       ba->offset = block->offset;
+
+                       err = block_alloc_contiguous(dsp, ba, block_list);
                        if (err < 0)
                                return -ENOMEM;
 
                        return 0;
                }
-
        }
 
        return -ENOMEM;
 }
 
 /* Load fixed module data into DSP memory blocks */
-int sst_module_insert_fixed_block(struct sst_module *module,
-       struct sst_module_data *data)
+int sst_module_alloc_blocks(struct sst_module *module)
 {
        struct sst_dsp *dsp = module->dsp;
+       struct sst_fw *sst_fw = module->sst_fw;
+       struct sst_block_allocator ba;
        int ret;
 
+       ba.size = module->size;
+       ba.type = module->type;
+       ba.offset = module->offset;
+
+       dev_dbg(dsp->dev, "block request 0x%x bytes at offset 0x%x type %d\n",
+               ba.size, ba.offset, ba.type);
+
        mutex_lock(&dsp->mutex);
 
        /* alloc blocks that includes this section */
-       ret = block_alloc_fixed(module, data);
+       ret = block_alloc_fixed(dsp, &ba, &module->block_list);
        if (ret < 0) {
                dev_err(dsp->dev,
                        "error: no free blocks for section at offset 0x%x size 0x%x\n",
-                       data->offset, data->size);
+                       module->offset, module->size);
                mutex_unlock(&dsp->mutex);
                return -ENOMEM;
        }
 
        /* prepare DSP blocks for module copy */
-       ret = block_module_prepare(module);
+       ret = block_list_prepare(dsp, &module->block_list);
        if (ret < 0) {
                dev_err(dsp->dev, "error: fw module prepare failed\n");
                goto err;
        }
 
        /* copy partial module data to blocks */
-       sst_memcpy32(dsp->addr.lpe + data->offset, data->data, data->size);
+       if (dsp->fw_use_dma) {
+               ret = sst_dsp_dma_copyto(dsp,
+                       dsp->addr.lpe_base + module->offset,
+                       sst_fw->dmable_fw_paddr + module->data_offset,
+                       module->size);
+               if (ret < 0) {
+                       dev_err(dsp->dev, "error: module copy failed\n");
+                       goto err;
+               }
+       } else
+               sst_memcpy32(dsp->addr.lpe + module->offset, module->data,
+                       module->size);
 
        mutex_unlock(&dsp->mutex);
        return ret;
 
 err:
-       block_module_remove(module);
+       block_list_remove(dsp, &module->block_list);
        mutex_unlock(&dsp->mutex);
        return ret;
 }
-EXPORT_SYMBOL_GPL(sst_module_insert_fixed_block);
+EXPORT_SYMBOL_GPL(sst_module_alloc_blocks);
 
 /* Unload entire module from DSP memory */
-int sst_block_module_remove(struct sst_module *module)
+int sst_module_free_blocks(struct sst_module *module)
 {
        struct sst_dsp *dsp = module->dsp;
 
        mutex_lock(&dsp->mutex);
-       block_module_remove(module);
+       block_list_remove(dsp, &module->block_list);
        mutex_unlock(&dsp->mutex);
        return 0;
 }
-EXPORT_SYMBOL_GPL(sst_block_module_remove);
+EXPORT_SYMBOL_GPL(sst_module_free_blocks);
+
+int sst_module_runtime_alloc_blocks(struct sst_module_runtime *runtime,
+       int offset)
+{
+       struct sst_dsp *dsp = runtime->dsp;
+       struct sst_module *module = runtime->module;
+       struct sst_block_allocator ba;
+       int ret;
+
+       if (module->persistent_size == 0)
+               return 0;
+
+       ba.size = module->persistent_size;
+       ba.type = SST_MEM_DRAM;
+
+       mutex_lock(&dsp->mutex);
+
+       /* do we need to allocate at a fixed address ? */
+       if (offset != 0) {
+
+               ba.offset = offset;
+
+               dev_dbg(dsp->dev, "persistent fixed block request 0x%x bytes type %d offset 0x%x\n",
+                       ba.size, ba.type, ba.offset);
+
+               /* alloc blocks that includes this section */
+               ret = block_alloc_fixed(dsp, &ba, &runtime->block_list);
+
+       } else {
+               dev_dbg(dsp->dev, "persistent block request 0x%x bytes type %d\n",
+                       ba.size, ba.type);
+
+               /* alloc blocks that includes this section */
+               ret = block_alloc(dsp, &ba, &runtime->block_list);
+       }
+       if (ret < 0) {
+               dev_err(dsp->dev,
+               "error: no free blocks for runtime module size 0x%x\n",
+                       module->persistent_size);
+               mutex_unlock(&dsp->mutex);
+               return -ENOMEM;
+       }
+       runtime->persistent_offset = ba.offset;
+
+       /* prepare DSP blocks for module copy */
+       ret = block_list_prepare(dsp, &runtime->block_list);
+       if (ret < 0) {
+               dev_err(dsp->dev, "error: runtime block prepare failed\n");
+               goto err;
+       }
+
+       mutex_unlock(&dsp->mutex);
+       return ret;
+
+err:
+       block_list_remove(dsp, &module->block_list);
+       mutex_unlock(&dsp->mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_alloc_blocks);
+
+int sst_module_runtime_free_blocks(struct sst_module_runtime *runtime)
+{
+       struct sst_dsp *dsp = runtime->dsp;
+
+       mutex_lock(&dsp->mutex);
+       block_list_remove(dsp, &runtime->block_list);
+       mutex_unlock(&dsp->mutex);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_free_blocks);
+
+int sst_module_runtime_save(struct sst_module_runtime *runtime,
+       struct sst_module_runtime_context *context)
+{
+       struct sst_dsp *dsp = runtime->dsp;
+       struct sst_module *module = runtime->module;
+       int ret = 0;
+
+       dev_dbg(dsp->dev, "saving runtime %d memory at 0x%x size 0x%x\n",
+               runtime->id, runtime->persistent_offset,
+               module->persistent_size);
+
+       context->buffer = dma_alloc_coherent(dsp->dma_dev,
+               module->persistent_size,
+               &context->dma_buffer, GFP_DMA | GFP_KERNEL);
+       if (!context->buffer) {
+               dev_err(dsp->dev, "error: DMA context alloc failed\n");
+               return -ENOMEM;
+       }
+
+       mutex_lock(&dsp->mutex);
+
+       if (dsp->fw_use_dma) {
+
+               ret = sst_dsp_dma_get_channel(dsp, 0);
+               if (ret < 0)
+                       goto err;
+
+               ret = sst_dsp_dma_copyfrom(dsp, context->dma_buffer,
+                       dsp->addr.lpe_base + runtime->persistent_offset,
+                       module->persistent_size);
+               sst_dsp_dma_put_channel(dsp);
+               if (ret < 0) {
+                       dev_err(dsp->dev, "error: context copy failed\n");
+                       goto err;
+               }
+       } else
+               sst_memcpy32(context->buffer, dsp->addr.lpe +
+                       runtime->persistent_offset,
+                       module->persistent_size);
+
+err:
+       mutex_unlock(&dsp->mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_save);
+
+int sst_module_runtime_restore(struct sst_module_runtime *runtime,
+       struct sst_module_runtime_context *context)
+{
+       struct sst_dsp *dsp = runtime->dsp;
+       struct sst_module *module = runtime->module;
+       int ret = 0;
+
+       dev_dbg(dsp->dev, "restoring runtime %d memory at 0x%x size 0x%x\n",
+               runtime->id, runtime->persistent_offset,
+               module->persistent_size);
+
+       mutex_lock(&dsp->mutex);
+
+       if (!context->buffer) {
+               dev_info(dsp->dev, "no context buffer need to restore!\n");
+               goto err;
+       }
+
+       if (dsp->fw_use_dma) {
+
+               ret = sst_dsp_dma_get_channel(dsp, 0);
+               if (ret < 0)
+                       goto err;
+
+               ret = sst_dsp_dma_copyto(dsp,
+                       dsp->addr.lpe_base + runtime->persistent_offset,
+                       context->dma_buffer, module->persistent_size);
+               sst_dsp_dma_put_channel(dsp);
+               if (ret < 0) {
+                       dev_err(dsp->dev, "error: module copy failed\n");
+                       goto err;
+               }
+       } else
+               sst_memcpy32(dsp->addr.lpe + runtime->persistent_offset,
+                       context->buffer, module->persistent_size);
+
+       dma_free_coherent(dsp->dma_dev, module->persistent_size,
+                               context->buffer, context->dma_buffer);
+       context->buffer = NULL;
+
+err:
+       mutex_unlock(&dsp->mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_restore);
 
 /* register a DSP memory block for use with FW based modules */
 struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
@@ -519,80 +1061,84 @@ void sst_mem_block_unregister_all(struct sst_dsp *dsp)
 EXPORT_SYMBOL_GPL(sst_mem_block_unregister_all);
 
 /* allocate scratch buffer blocks */
-struct sst_module *sst_mem_block_alloc_scratch(struct sst_dsp *dsp)
+int sst_block_alloc_scratch(struct sst_dsp *dsp)
 {
-       struct sst_module *sst_module, *scratch;
-       struct sst_mem_block *block, *tmp;
-       u32 block_size;
-       int ret = 0;
-
-       scratch = kzalloc(sizeof(struct sst_module), GFP_KERNEL);
-       if (scratch == NULL)
-               return NULL;
+       struct sst_module *module;
+       struct sst_block_allocator ba;
+       int ret;
 
        mutex_lock(&dsp->mutex);
 
        /* calculate required scratch size */
-       list_for_each_entry(sst_module, &dsp->module_list, list) {
-               if (scratch->s.size < sst_module->s.size)
-                       scratch->s.size = sst_module->s.size;
+       dsp->scratch_size = 0;
+       list_for_each_entry(module, &dsp->module_list, list) {
+               dev_dbg(dsp->dev, "module %d scratch req 0x%x bytes\n",
+                       module->id, module->scratch_size);
+               if (dsp->scratch_size < module->scratch_size)
+                       dsp->scratch_size = module->scratch_size;
        }
 
-       dev_dbg(dsp->dev, "scratch buffer required is %d bytes\n",
-               scratch->s.size);
-
-       /* init scratch module */
-       scratch->dsp = dsp;
-       scratch->s.type = SST_MEM_DRAM;
-       scratch->s.data_type = SST_DATA_S;
-       INIT_LIST_HEAD(&scratch->block_list);
+       dev_dbg(dsp->dev, "scratch buffer required is 0x%x bytes\n",
+               dsp->scratch_size);
 
-       /* check free blocks before looking at used blocks for space */
-       if (!list_empty(&dsp->free_block_list))
-               block = list_first_entry(&dsp->free_block_list,
-                       struct sst_mem_block, list);
-       else
-               block = list_first_entry(&dsp->used_block_list,
-                       struct sst_mem_block, list);
-       block_size = block->size;
+       if (dsp->scratch_size == 0) {
+               dev_info(dsp->dev, "no modules need scratch buffer\n");
+               mutex_unlock(&dsp->mutex);
+               return 0;
+       }
 
        /* allocate blocks for module scratch buffers */
        dev_dbg(dsp->dev, "allocating scratch blocks\n");
-       ret = block_alloc(scratch, &scratch->s);
+
+       ba.size = dsp->scratch_size;
+       ba.type = SST_MEM_DRAM;
+
+       /* do we need to allocate at fixed offset */
+       if (dsp->scratch_offset != 0) {
+
+               dev_dbg(dsp->dev, "block request 0x%x bytes type %d at 0x%x\n",
+                       ba.size, ba.type, ba.offset);
+
+               ba.offset = dsp->scratch_offset;
+
+               /* alloc blocks that includes this section */
+               ret = block_alloc_fixed(dsp, &ba, &dsp->scratch_block_list);
+
+       } else {
+               dev_dbg(dsp->dev, "block request 0x%x bytes type %d\n",
+                       ba.size, ba.type);
+
+               ba.offset = 0;
+               ret = block_alloc(dsp, &ba, &dsp->scratch_block_list);
+       }
        if (ret < 0) {
                dev_err(dsp->dev, "error: can't alloc scratch blocks\n");
-               goto err;
+               mutex_unlock(&dsp->mutex);
+               return ret;
        }
 
-       /* assign the same offset of scratch to each module */
-       list_for_each_entry(sst_module, &dsp->module_list, list)
-               sst_module->s.offset = scratch->s.offset;
-
-       mutex_unlock(&dsp->mutex);
-       return scratch;
+       ret = block_list_prepare(dsp, &dsp->scratch_block_list);
+       if (ret < 0) {
+               dev_err(dsp->dev, "error: scratch block prepare failed\n");
+               mutex_unlock(&dsp->mutex);
+               return ret;
+       }
 
-err:
-       list_for_each_entry_safe(block, tmp, &scratch->block_list, module_list)
-               list_del(&block->module_list);
+       /* assign the same offset of scratch to each module */
+       dsp->scratch_offset = ba.offset;
        mutex_unlock(&dsp->mutex);
-       return NULL;
+       return dsp->scratch_size;
 }
-EXPORT_SYMBOL_GPL(sst_mem_block_alloc_scratch);
+EXPORT_SYMBOL_GPL(sst_block_alloc_scratch);
 
 /* free all scratch blocks */
-void sst_mem_block_free_scratch(struct sst_dsp *dsp,
-       struct sst_module *scratch)
+void sst_block_free_scratch(struct sst_dsp *dsp)
 {
-       struct sst_mem_block *block, *tmp;
-
        mutex_lock(&dsp->mutex);
-
-       list_for_each_entry_safe(block, tmp, &scratch->block_list, module_list)
-               list_del(&block->module_list);
-
+       block_list_remove(dsp, &dsp->scratch_block_list);
        mutex_unlock(&dsp->mutex);
 }
-EXPORT_SYMBOL_GPL(sst_mem_block_free_scratch);
+EXPORT_SYMBOL_GPL(sst_block_free_scratch);
 
 /* get a module from it's unique ID */
 struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id)
@@ -612,3 +1158,40 @@ struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id)
        return NULL;
 }
 EXPORT_SYMBOL_GPL(sst_module_get_from_id);
+
+struct sst_module_runtime *sst_module_runtime_get_from_id(
+       struct sst_module *module, u32 id)
+{
+       struct sst_module_runtime *runtime;
+       struct sst_dsp *dsp = module->dsp;
+
+       mutex_lock(&dsp->mutex);
+
+       list_for_each_entry(runtime, &module->runtime_list, list) {
+               if (runtime->id == id) {
+                       mutex_unlock(&dsp->mutex);
+                       return runtime;
+               }
+       }
+
+       mutex_unlock(&dsp->mutex);
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_get_from_id);
+
+/* returns block address in DSP address space */
+u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset,
+       enum sst_mem_type type)
+{
+       switch (type) {
+       case SST_MEM_IRAM:
+               return offset - dsp->addr.iram_offset +
+                       dsp->addr.dsp_iram_offset;
+       case SST_MEM_DRAM:
+               return offset - dsp->addr.dram_offset +
+                       dsp->addr.dsp_dram_offset;
+       default:
+               return 0;
+       }
+}
+EXPORT_SYMBOL_GPL(sst_dsp_get_offset);
index 4b6c163..57039b0 100644 (file)
 #define SST_LP_SHIM_OFFSET     0xE7000
 #define SST_WPT_IRAM_OFFSET    0xA0000
 #define SST_LP_IRAM_OFFSET     0x80000
+#define SST_WPT_DSP_DRAM_OFFSET        0x400000
+#define SST_WPT_DSP_IRAM_OFFSET        0x00000
+#define SST_LPT_DSP_DRAM_OFFSET        0x400000
+#define SST_LPT_DSP_IRAM_OFFSET        0x00000
 
 #define SST_SHIM_PM_REG                0x84
 
@@ -86,9 +90,8 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
 {
        struct dma_block_info *block;
        struct sst_module *mod;
-       struct sst_module_data block_data;
        struct sst_module_template template;
-       int count;
+       int count, ret;
        void __iomem *ram;
 
        /* TODO: allowed module types need to be configurable */
@@ -109,13 +112,9 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
 
        memset(&template, 0, sizeof(template));
        template.id = module->type;
-       template.entry = module->entry_point;
-       template.p.size = module->info.persistent_size;
-       template.p.type = SST_MEM_DRAM;
-       template.p.data_type = SST_DATA_P;
-       template.s.size = module->info.scratch_size;
-       template.s.type = SST_MEM_DRAM;
-       template.s.data_type = SST_DATA_S;
+       template.entry = module->entry_point - 4;
+       template.persistent_size = module->info.persistent_size;
+       template.scratch_size = module->info.scratch_size;
 
        mod = sst_module_new(fw, &template, NULL);
        if (mod == NULL)
@@ -135,14 +134,14 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
                switch (block->type) {
                case SST_HSW_IRAM:
                        ram = dsp->addr.lpe;
-                       block_data.offset =
+                       mod->offset =
                                block->ram_offset + dsp->addr.iram_offset;
-                       block_data.type = SST_MEM_IRAM;
+                       mod->type = SST_MEM_IRAM;
                        break;
                case SST_HSW_DRAM:
                        ram = dsp->addr.lpe;
-                       block_data.offset = block->ram_offset;
-                       block_data.type = SST_MEM_DRAM;
+                       mod->offset = block->ram_offset;
+                       mod->type = SST_MEM_DRAM;
                        break;
                default:
                        dev_err(dsp->dev, "error: bad type 0x%x for block 0x%x\n",
@@ -151,30 +150,34 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
                        return -EINVAL;
                }
 
-               block_data.size = block->size;
-               block_data.data_type = SST_DATA_M;
-               block_data.data = (void *)block + sizeof(*block);
-               block_data.data_offset = block_data.data - fw->dma_buf;
+               mod->size = block->size;
+               mod->data = (void *)block + sizeof(*block);
+               mod->data_offset = mod->data - fw->dma_buf;
 
-               dev_dbg(dsp->dev, "copy firmware block %d type 0x%x "
+               dev_dbg(dsp->dev, "module block %d type 0x%x "
                        "size 0x%x ==> ram %p offset 0x%x\n",
-                       count, block->type, block->size, ram,
+                       count, mod->type, block->size, ram,
                        block->ram_offset);
 
-               sst_module_insert_fixed_block(mod, &block_data);
+               ret = sst_module_alloc_blocks(mod);
+               if (ret < 0) {
+                       dev_err(dsp->dev, "error: could not allocate blocks for module %d\n",
+                               count);
+                       sst_module_free(mod);
+                       return ret;
+               }
 
                block = (void *)block + sizeof(*block) + block->size;
        }
+
        return 0;
 }
 
 static int hsw_parse_fw_image(struct sst_fw *sst_fw)
 {
        struct fw_header *header;
-       struct sst_module *scratch;
        struct fw_module_header *module;
        struct sst_dsp *dsp = sst_fw->dsp;
-       struct sst_hsw *hsw = sst_fw->private;
        int ret, count;
 
        /* Read the header information from the data pointer */
@@ -204,12 +207,8 @@ static int hsw_parse_fw_image(struct sst_fw *sst_fw)
                module = (void *)module + sizeof(*module) + module->mod_size;
        }
 
-       /* allocate persistent/scratch mem regions */
-       scratch = sst_mem_block_alloc_scratch(dsp);
-       if (scratch == NULL)
-               return -ENOMEM;
-
-       sst_hsw_set_scratch_module(hsw, scratch);
+       /* allocate scratch mem regions */
+       sst_block_alloc_scratch(dsp);
 
        return 0;
 }
@@ -248,8 +247,94 @@ static irqreturn_t hsw_irq(int irq, void *context)
        return ret;
 }
 
-static void hsw_boot(struct sst_dsp *sst)
+static void hsw_set_dsp_D3(struct sst_dsp *sst)
+{
+       u32 val;
+       u32 reg;
+
+       /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE);
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       /* enable power gating and switch off DRAM & IRAM blocks */
+       val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+       val |= SST_VDRTCL0_DSRAMPGE_MASK |
+               SST_VDRTCL0_ISRAMPGE_MASK;
+       val &= ~(SST_VDRTCL0_D3PGD | SST_VDRTCL0_D3SRAMPGD);
+       writel(val, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+       /* switch off audio PLL */
+       val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       val |= SST_VDRTCL2_APLLSE_MASK;
+       writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       /* disable MCLK(clkctl.smos = 0) */
+       sst_dsp_shim_update_bits_unlocked(sst, SST_CLKCTL,
+               SST_CLKCTL_MASK, 0);
+
+       /* Set D3 state, delay 50 us */
+       val = readl(sst->addr.pci_cfg + SST_PMCS);
+       val |= SST_PMCS_PS_MASK;
+       writel(val, sst->addr.pci_cfg + SST_PMCS);
+       udelay(50);
+
+       /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE;
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       udelay(50);
+
+}
+
+static void hsw_reset(struct sst_dsp *sst)
 {
+       /* put DSP into reset and stall */
+       sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
+               SST_CSR_RST | SST_CSR_STALL,
+               SST_CSR_RST | SST_CSR_STALL);
+
+       /* keep in reset for 10ms */
+       mdelay(10);
+
+       /* take DSP out of reset and keep stalled for FW loading */
+       sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
+               SST_CSR_RST | SST_CSR_STALL, SST_CSR_STALL);
+}
+
+static int hsw_set_dsp_D0(struct sst_dsp *sst)
+{
+       int tries = 10;
+       u32 reg;
+
+       /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE);
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       /* Disable D3PG (VDRTCTL0.D3PGD = 1) */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+       reg |= SST_VDRTCL0_D3PGD;
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+       /* Set D0 state */
+       reg = readl(sst->addr.pci_cfg + SST_PMCS);
+       reg &= ~SST_PMCS_PS_MASK;
+       writel(reg, sst->addr.pci_cfg + SST_PMCS);
+
+       /* check that ADSP shim is enabled */
+       while (tries--) {
+               reg = readl(sst->addr.pci_cfg + SST_PMCS) & SST_PMCS_PS_MASK;
+               if (reg == 0)
+                       goto finish;
+
+               msleep(1);
+       }
+
+       return -ENODEV;
+
+finish:
        /* select SSP1 19.2MHz base clock, SSP clock 0, turn off Low Power Clock */
        sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
                SST_CSR_S1IOCS | SST_CSR_SBCS1 | SST_CSR_LPCS, 0x0);
@@ -264,34 +349,96 @@ static void hsw_boot(struct sst_dsp *sst)
                SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0,
                SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0);
 
+       /* Stall and reset core, set CSR */
+       hsw_reset(sst);
+
+       /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE;
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       udelay(50);
+
+       /* switch on audio PLL */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       reg &= ~SST_VDRTCL2_APLLSE_MASK;
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       /* set default power gating control, enable power gating control for all blocks. that is,
+       can't be accessed, please enable each block before accessing. */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+       reg |= SST_VDRTCL0_DSRAMPGE_MASK | SST_VDRTCL0_ISRAMPGE_MASK;
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+
        /* disable DMA finish function for SSP0 & SSP1 */
        sst_dsp_shim_update_bits_unlocked(sst, SST_CSR2, SST_CSR2_SDFD_SSP1,
                SST_CSR2_SDFD_SSP1);
 
-       /* enable DMA engine 0,1 all channels to access host memory */
-       sst_dsp_shim_update_bits_unlocked(sst, SST_HMDC,
-               SST_HMDC_HDDA1(0xff) | SST_HMDC_HDDA0(0xff),
-               SST_HMDC_HDDA1(0xff) | SST_HMDC_HDDA0(0xff));
+       /* set on-demond mode on engine 0,1 for all channels */
+       sst_dsp_shim_update_bits(sst, SST_HMDC,
+                       SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH,
+                       SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH);
+
+       /* Enable Interrupt from both sides */
+       sst_dsp_shim_update_bits(sst, SST_IMRX, (SST_IMRX_BUSY | SST_IMRX_DONE),
+                                0x0);
+       sst_dsp_shim_update_bits(sst, SST_IMRD, (SST_IMRD_DONE | SST_IMRD_BUSY |
+                               SST_IMRD_SSP0 | SST_IMRD_DMAC), 0x0);
+
+       /* clear IPC registers */
+       sst_dsp_shim_write(sst, SST_IPCX, 0x0);
+       sst_dsp_shim_write(sst, SST_IPCD, 0x0);
+       sst_dsp_shim_write(sst, 0x80, 0x6);
+       sst_dsp_shim_write(sst, 0xe0, 0x300a);
+
+       return 0;
+}
 
-       /* disable all clock gating */
-       writel(0x0, sst->addr.pci_cfg + SST_VDRTCTL2);
+static void hsw_boot(struct sst_dsp *sst)
+{
+       /* set oportunistic mode on engine 0,1 for all channels */
+       sst_dsp_shim_update_bits(sst, SST_HMDC,
+                       SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH, 0);
 
        /* set DSP to RUN */
        sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, SST_CSR_STALL, 0x0);
 }
 
-static void hsw_reset(struct sst_dsp *sst)
+static void hsw_stall(struct sst_dsp *sst)
+{
+       /* stall DSP */
+       sst_dsp_shim_update_bits(sst, SST_CSR,
+               SST_CSR_24MHZ_LPCS | SST_CSR_STALL,
+               SST_CSR_STALL | SST_CSR_24MHZ_LPCS);
+}
+
+static void hsw_sleep(struct sst_dsp *sst)
 {
+       dev_dbg(sst->dev, "HSW_PM dsp runtime suspend\n");
+
        /* put DSP into reset and stall */
-       sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
-               SST_CSR_RST | SST_CSR_STALL, SST_CSR_RST | SST_CSR_STALL);
+       sst_dsp_shim_update_bits(sst, SST_CSR,
+               SST_CSR_24MHZ_LPCS | SST_CSR_RST | SST_CSR_STALL,
+               SST_CSR_RST | SST_CSR_STALL | SST_CSR_24MHZ_LPCS);
 
-       /* keep in reset for 10ms */
-       mdelay(10);
+       hsw_set_dsp_D3(sst);
+       dev_dbg(sst->dev, "HSW_PM dsp runtime suspend exit\n");
+}
 
-       /* take DSP out of reset and keep stalled for FW loading */
-       sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
-               SST_CSR_RST | SST_CSR_STALL, SST_CSR_STALL);
+static int hsw_wake(struct sst_dsp *sst)
+{
+       int ret;
+
+       dev_dbg(sst->dev, "HSW_PM dsp runtime resume\n");
+
+       ret = hsw_set_dsp_D0(sst);
+       if (ret < 0)
+               return ret;
+
+       dev_dbg(sst->dev, "HSW_PM dsp runtime resume exit\n");
+
+       return 0;
 }
 
 struct sst_adsp_memregion {
@@ -396,6 +543,11 @@ static int hsw_block_enable(struct sst_mem_block *block)
        dev_dbg(block->dsp->dev, " enabled block %d:%d at offset 0x%x\n",
                block->type, block->index, block->offset);
 
+       /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+       val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       val &= ~SST_VDRTCL2_DCLCGE;
+       writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
        val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
        bit = hsw_block_get_bit(block);
        writel(val & ~bit, sst->addr.pci_cfg + SST_VDRTCTL0);
@@ -403,6 +555,13 @@ static int hsw_block_enable(struct sst_mem_block *block)
        /* wait 18 DSP clock ticks */
        udelay(10);
 
+       /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+       val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       val |= SST_VDRTCL2_DCLCGE;
+       writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       udelay(50);
+
        /*add a dummy read before the SRAM block is written, otherwise the writing may miss bytes sometimes.*/
        sst_mem_block_dummy_read(block);
        return 0;
@@ -420,10 +579,26 @@ static int hsw_block_disable(struct sst_mem_block *block)
        dev_dbg(block->dsp->dev, " disabled block %d:%d at offset 0x%x\n",
                block->type, block->index, block->offset);
 
+       /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+       val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       val &= ~SST_VDRTCL2_DCLCGE;
+       writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+
        val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
        bit = hsw_block_get_bit(block);
        writel(val | bit, sst->addr.pci_cfg + SST_VDRTCTL0);
 
+       /* wait 18 DSP clock ticks */
+       udelay(10);
+
+       /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+       val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       val |= SST_VDRTCL2_DCLCGE;
+       writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       udelay(50);
+
        return 0;
 }
 
@@ -432,27 +607,6 @@ static struct sst_block_ops sst_hsw_ops = {
        .disable = hsw_block_disable,
 };
 
-static int hsw_enable_shim(struct sst_dsp *sst)
-{
-       int tries = 10;
-       u32 reg;
-
-       /* enable shim */
-       reg = readl(sst->addr.pci_cfg + SST_SHIM_PM_REG);
-       writel(reg & ~0x3, sst->addr.pci_cfg + SST_SHIM_PM_REG);
-
-       /* check that ADSP shim is enabled */
-       while (tries--) {
-               reg = sst_dsp_shim_read_unlocked(sst, SST_CSR);
-               if (reg != 0xffffffff)
-                       return 0;
-
-               msleep(1);
-       }
-
-       return -ENODEV;
-}
-
 static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
 {
        const struct sst_adsp_memregion *region;
@@ -467,12 +621,16 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
                region = lp_region;
                region_count = ARRAY_SIZE(lp_region);
                sst->addr.iram_offset = SST_LP_IRAM_OFFSET;
+               sst->addr.dsp_iram_offset = SST_LPT_DSP_IRAM_OFFSET;
+               sst->addr.dsp_dram_offset = SST_LPT_DSP_DRAM_OFFSET;
                sst->addr.shim_offset = SST_LP_SHIM_OFFSET;
                break;
        case SST_DEV_ID_WILDCAT_POINT:
                region = wpt_region;
                region_count = ARRAY_SIZE(wpt_region);
                sst->addr.iram_offset = SST_WPT_IRAM_OFFSET;
+               sst->addr.dsp_iram_offset = SST_WPT_DSP_IRAM_OFFSET;
+               sst->addr.dsp_dram_offset = SST_WPT_DSP_DRAM_OFFSET;
                sst->addr.shim_offset = SST_WPT_SHIM_OFFSET;
                break;
        default:
@@ -487,7 +645,7 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
        }
 
        /* enable the DSP SHIM */
-       ret = hsw_enable_shim(sst);
+       ret = hsw_set_dsp_D0(sst);
        if (ret < 0) {
                dev_err(dev, "error: failed to set DSP D0 and reset SHIM\n");
                return ret;
@@ -497,10 +655,6 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
        if (ret)
                return ret;
 
-       /* Enable Interrupt from both sides */
-       sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX, 0x3, 0x0);
-       sst_dsp_shim_update_bits_unlocked(sst, SST_IMRD,
-               (0x3 | 0x1 << 16 | 0x3 << 21), 0x0);
 
        /* register DSP memory blocks - ideally we should get this from ACPI */
        for (i = 0; i < region_count; i++) {
@@ -532,6 +686,9 @@ static void hsw_free(struct sst_dsp *sst)
 struct sst_ops haswell_ops = {
        .reset = hsw_reset,
        .boot = hsw_boot,
+       .stall = hsw_stall,
+       .wake = hsw_wake,
+       .sleep = hsw_sleep,
        .write = sst_shim32_write,
        .read = sst_shim32_read,
        .write64 = sst_shim32_write64,
index b629151..3f8c482 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/firmware.h>
 #include <linux/dma-mapping.h>
 #include <linux/debugfs.h>
+#include <linux/pm_runtime.h>
 
 #include "sst-haswell-ipc.h"
 #include "sst-dsp.h"
@@ -276,6 +277,7 @@ struct sst_hsw {
        struct sst_hsw_ipc_fw_version version;
        struct sst_module *scratch;
        bool fw_done;
+       struct sst_fw *sst_fw;
 
        /* stream */
        struct list_head stream_list;
@@ -289,6 +291,8 @@ struct sst_hsw {
 
        /* DX */
        struct sst_hsw_ipc_dx_reply dx;
+       void *dx_context;
+       dma_addr_t dx_context_paddr;
 
        /* boot */
        wait_queue_head_t boot_wait;
@@ -1038,14 +1042,9 @@ int sst_hsw_stream_set_volume(struct sst_hsw *hsw,
 
        trace_ipc_request("set stream volume", stream->reply.stream_hw_id);
 
-       if (channel > 1)
+       if (channel >= 2 && channel != SST_HSW_CHANNELS_ALL)
                return -EINVAL;
 
-       if (stream->mute[channel]) {
-               stream->mute_volume[channel] = volume;
-               return 0;
-       }
-
        header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) |
                IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE);
        header |= (stream->reply.stream_hw_id << IPC_STR_ID_SHIFT);
@@ -1053,9 +1052,28 @@ int sst_hsw_stream_set_volume(struct sst_hsw *hsw,
        header |= (stage_id << IPC_STG_ID_SHIFT);
 
        req = &stream->vol_req;
-       req->channel = channel;
        req->target_volume = volume;
 
+       /* set both at same time ? */
+       if (channel == SST_HSW_CHANNELS_ALL) {
+               if (hsw->mute[0] && hsw->mute[1]) {
+                       hsw->mute_volume[0] = hsw->mute_volume[1] = volume;
+                       return 0;
+               } else if (hsw->mute[0])
+                       req->channel = 1;
+               else if (hsw->mute[1])
+                       req->channel = 0;
+               else
+                       req->channel = SST_HSW_CHANNELS_ALL;
+       } else {
+               /* set only 1 channel */
+               if (hsw->mute[channel]) {
+                       hsw->mute_volume[channel] = volume;
+                       return 0;
+               }
+               req->channel = channel;
+       }
+
        ret = ipc_tx_message_wait(hsw, header, req, sizeof(*req), NULL, 0);
        if (ret < 0) {
                dev_err(hsw->dev, "error: set stream volume failed\n");
@@ -1134,8 +1152,11 @@ int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
 
        trace_ipc_request("set mixer volume", volume);
 
+       if (channel >= 2 && channel != SST_HSW_CHANNELS_ALL)
+               return -EINVAL;
+
        /* set both at same time ? */
-       if (channel == 2) {
+       if (channel == SST_HSW_CHANNELS_ALL) {
                if (hsw->mute[0] && hsw->mute[1]) {
                        hsw->mute_volume[0] = hsw->mute_volume[1] = volume;
                        return 0;
@@ -1144,7 +1165,7 @@ int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
                else if (hsw->mute[1])
                        req.channel = 0;
                else
-                       req.channel = 0xffffffff;
+                       req.channel = SST_HSW_CHANNELS_ALL;
        } else {
                /* set only 1 channel */
                if (hsw->mute[channel]) {
@@ -1256,10 +1277,6 @@ int sst_hsw_stream_set_channels(struct sst_hsw *hsw,
                return -EINVAL;
        }
 
-       /* stereo is only supported atm */
-       if (channels != 2)
-               return -EINVAL;
-
        stream->request.format.ch_num = channels;
        return 0;
 }
@@ -1355,10 +1372,11 @@ int sst_hsw_stream_buffer(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
 }
 
 int sst_hsw_stream_set_module_info(struct sst_hsw *hsw,
-       struct sst_hsw_stream *stream, enum sst_hsw_module_id module_id,
-       u32 entry_point)
+       struct sst_hsw_stream *stream, struct sst_module_runtime *runtime)
 {
        struct sst_hsw_module_map *map = &stream->request.map;
+       struct sst_dsp *dsp = sst_hsw_get_dsp(hsw);
+       struct sst_module *module = runtime->module;
 
        if (stream->commited) {
                dev_err(hsw->dev, "error: stream committed for set module\n");
@@ -1367,36 +1385,25 @@ int sst_hsw_stream_set_module_info(struct sst_hsw *hsw,
 
        /* only support initial module atm */
        map->module_entries_count = 1;
-       map->module_entries[0].module_id = module_id;
-       map->module_entries[0].entry_point = entry_point;
-
-       return 0;
-}
-
-int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw,
-       struct sst_hsw_stream *stream, u32 offset, u32 size)
-{
-       if (stream->commited) {
-               dev_err(hsw->dev, "error: stream committed for set pmem\n");
-               return -EINVAL;
-       }
-
-       stream->request.persistent_mem.offset = offset;
-       stream->request.persistent_mem.size = size;
-
-       return 0;
-}
-
-int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw,
-       struct sst_hsw_stream *stream, u32 offset, u32 size)
-{
-       if (stream->commited) {
-               dev_err(hsw->dev, "error: stream committed for set smem\n");
-               return -EINVAL;
-       }
-
-       stream->request.scratch_mem.offset = offset;
-       stream->request.scratch_mem.size = size;
+       map->module_entries[0].module_id = module->id;
+       map->module_entries[0].entry_point = module->entry;
+
+       stream->request.persistent_mem.offset =
+               sst_dsp_get_offset(dsp, runtime->persistent_offset, SST_MEM_DRAM);
+       stream->request.persistent_mem.size = module->persistent_size;
+
+       stream->request.scratch_mem.offset =
+               sst_dsp_get_offset(dsp, dsp->scratch_offset, SST_MEM_DRAM);
+       stream->request.scratch_mem.size = dsp->scratch_size;
+
+       dev_dbg(hsw->dev, "module %d runtime %d using:\n", module->id,
+               runtime->id);
+       dev_dbg(hsw->dev, " persistent offset 0x%x bytes 0x%x\n",
+               stream->request.persistent_mem.offset,
+               stream->request.persistent_mem.size);
+       dev_dbg(hsw->dev, " scratch offset 0x%x bytes 0x%x\n",
+               stream->request.scratch_mem.offset,
+               stream->request.scratch_mem.size);
 
        return 0;
 }
@@ -1630,6 +1637,10 @@ int sst_hsw_device_set_config(struct sst_hsw *hsw,
        config.clock_frequency = mclk;
        config.mode = mode;
        config.clock_divider = clock_divider;
+       if (mode == SST_HSW_DEVICE_TDM_CLOCK_MASTER)
+               config.channels = 4;
+       else
+               config.channels = 2;
 
        trace_hsw_device_config_req(&config);
 
@@ -1673,34 +1684,283 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw,
        dev_dbg(hsw->dev, "ipc: got %d entry numbers for state %d\n",
                dx->entries_no, state);
 
-       memcpy(&hsw->dx, dx, sizeof(*dx));
-       return 0;
+       return ret;
 }
 
-/* Used to save state into hsw->dx_reply */
-int sst_hsw_dx_get_state(struct sst_hsw *hsw, u32 item,
-       u32 *offset, u32 *size, u32 *source)
+struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw,
+       int mod_id, int offset)
 {
-       struct sst_hsw_ipc_dx_memory_item *dx_mem;
-       struct sst_hsw_ipc_dx_reply *dx_reply;
-       int entry_no;
+       struct sst_dsp *dsp = hsw->dsp;
+       struct sst_module *module;
+       struct sst_module_runtime *runtime;
+       int err;
 
-       dx_reply = &hsw->dx;
-       entry_no = dx_reply->entries_no;
+       module = sst_module_get_from_id(dsp, mod_id);
+       if (module == NULL) {
+               dev_err(dsp->dev, "error: failed to get module %d for pcm\n",
+                       mod_id);
+               return NULL;
+       }
+
+       runtime = sst_module_runtime_new(module, mod_id, NULL);
+       if (runtime == NULL) {
+               dev_err(dsp->dev, "error: failed to create module %d runtime\n",
+                       mod_id);
+               return NULL;
+       }
+
+       err = sst_module_runtime_alloc_blocks(runtime, offset);
+       if (err < 0) {
+               dev_err(dsp->dev, "error: failed to alloc blocks for module %d runtime\n",
+                       mod_id);
+               sst_module_runtime_free(runtime);
+               return NULL;
+       }
+
+       dev_dbg(dsp->dev, "runtime id %d created for module %d\n", runtime->id,
+               mod_id);
+       return runtime;
+}
+
+void sst_hsw_runtime_module_free(struct sst_module_runtime *runtime)
+{
+       sst_module_runtime_free_blocks(runtime);
+       sst_module_runtime_free(runtime);
+}
+
+#ifdef CONFIG_PM
+static int sst_hsw_dx_state_dump(struct sst_hsw *hsw)
+{
+       struct sst_dsp *sst = hsw->dsp;
+       u32 item, offset, size;
+       int ret = 0;
 
-       trace_ipc_request("PM get Dx state", entry_no);
+       trace_ipc_request("PM state dump. Items #", SST_HSW_MAX_DX_REGIONS);
 
-       if (item >= entry_no)
+       if (hsw->dx.entries_no > SST_HSW_MAX_DX_REGIONS) {
+               dev_err(hsw->dev,
+                       "error: number of FW context regions greater than %d\n",
+                       SST_HSW_MAX_DX_REGIONS);
+               memset(&hsw->dx, 0, sizeof(hsw->dx));
                return -EINVAL;
+       }
+
+       ret = sst_dsp_dma_get_channel(sst, 0);
+       if (ret < 0) {
+               dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
+               return ret;
+       }
+
+       /* set on-demond mode on engine 0 channel 3 */
+       sst_dsp_shim_update_bits(sst, SST_HMDC,
+                       SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH,
+                       SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH);
+
+       for (item = 0; item < hsw->dx.entries_no; item++) {
+               if (hsw->dx.mem_info[item].source == SST_HSW_DX_TYPE_MEMORY_DUMP
+                       && hsw->dx.mem_info[item].offset > DSP_DRAM_ADDR_OFFSET
+                       && hsw->dx.mem_info[item].offset <
+                       DSP_DRAM_ADDR_OFFSET + SST_HSW_DX_CONTEXT_SIZE) {
+
+                       offset = hsw->dx.mem_info[item].offset
+                                       - DSP_DRAM_ADDR_OFFSET;
+                       size = (hsw->dx.mem_info[item].size + 3) & (~3);
+
+                       ret = sst_dsp_dma_copyfrom(sst, hsw->dx_context_paddr + offset,
+                               sst->addr.lpe_base + offset, size);
+                       if (ret < 0) {
+                               dev_err(hsw->dev,
+                                       "error: FW context dump failed\n");
+                               memset(&hsw->dx, 0, sizeof(hsw->dx));
+                               goto out;
+                       }
+               }
+       }
+
+out:
+       sst_dsp_dma_put_channel(sst);
+       return ret;
+}
+
+static int sst_hsw_dx_state_restore(struct sst_hsw *hsw)
+{
+       struct sst_dsp *sst = hsw->dsp;
+       u32 item, offset, size;
+       int ret;
+
+       for (item = 0; item < hsw->dx.entries_no; item++) {
+               if (hsw->dx.mem_info[item].source == SST_HSW_DX_TYPE_MEMORY_DUMP
+                       && hsw->dx.mem_info[item].offset > DSP_DRAM_ADDR_OFFSET
+                       && hsw->dx.mem_info[item].offset <
+                       DSP_DRAM_ADDR_OFFSET + SST_HSW_DX_CONTEXT_SIZE) {
+
+                       offset = hsw->dx.mem_info[item].offset
+                                       - DSP_DRAM_ADDR_OFFSET;
+                       size = (hsw->dx.mem_info[item].size + 3) & (~3);
+
+                       ret = sst_dsp_dma_copyto(sst, sst->addr.lpe_base + offset,
+                               hsw->dx_context_paddr + offset, size);
+                       if (ret < 0) {
+                               dev_err(hsw->dev,
+                                       "error: FW context restore failed\n");
+                               return ret;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static void sst_hsw_drop_all(struct sst_hsw *hsw)
+{
+       struct ipc_message *msg, *tmp;
+       unsigned long flags;
+       int tx_drop_cnt = 0, rx_drop_cnt = 0;
 
-       dx_mem = &dx_reply->mem_info[item];
-       *offset = dx_mem->offset;
-       *size = dx_mem->size;
-       *source = dx_mem->source;
+       /* drop all TX and Rx messages before we stall + reset DSP */
+       spin_lock_irqsave(&hsw->dsp->spinlock, flags);
+
+       list_for_each_entry_safe(msg, tmp, &hsw->tx_list, list) {
+               list_move(&msg->list, &hsw->empty_list);
+               tx_drop_cnt++;
+       }
+
+       list_for_each_entry_safe(msg, tmp, &hsw->rx_list, list) {
+               list_move(&msg->list, &hsw->empty_list);
+               rx_drop_cnt++;
+       }
+
+       spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
+
+       if (tx_drop_cnt || rx_drop_cnt)
+               dev_err(hsw->dev, "dropped IPC msg RX=%d, TX=%d\n",
+                       tx_drop_cnt, rx_drop_cnt);
+}
+
+int sst_hsw_dsp_load(struct sst_hsw *hsw)
+{
+       struct sst_dsp *dsp = hsw->dsp;
+       int ret;
+
+       dev_dbg(hsw->dev, "loading audio DSP....");
+
+       ret = sst_dsp_wake(dsp);
+       if (ret < 0) {
+               dev_err(hsw->dev, "error: failed to wake audio DSP\n");
+               return -ENODEV;
+       }
+
+       ret = sst_dsp_dma_get_channel(dsp, 0);
+       if (ret < 0) {
+               dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
+               return ret;
+       }
+
+       ret = sst_fw_reload(hsw->sst_fw);
+       if (ret < 0) {
+               dev_err(hsw->dev, "error: SST FW reload failed\n");
+               sst_dsp_dma_put_channel(dsp);
+               return -ENOMEM;
+       }
 
+       sst_dsp_dma_put_channel(dsp);
        return 0;
 }
 
+static int sst_hsw_dsp_restore(struct sst_hsw *hsw)
+{
+       struct sst_dsp *dsp = hsw->dsp;
+       int ret;
+
+       dev_dbg(hsw->dev, "restoring audio DSP....");
+
+       ret = sst_dsp_dma_get_channel(dsp, 0);
+       if (ret < 0) {
+               dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
+               return ret;
+       }
+
+       ret = sst_hsw_dx_state_restore(hsw);
+       if (ret < 0) {
+               dev_err(hsw->dev, "error: SST FW context restore failed\n");
+               sst_dsp_dma_put_channel(dsp);
+               return -ENOMEM;
+       }
+       sst_dsp_dma_put_channel(dsp);
+
+       /* wait for DSP boot completion */
+       sst_dsp_boot(dsp);
+
+       return ret;
+}
+
+int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw)
+{
+       int ret;
+
+       dev_dbg(hsw->dev, "audio dsp runtime suspend\n");
+
+       ret = sst_hsw_dx_set_state(hsw, SST_HSW_DX_STATE_D3, &hsw->dx);
+       if (ret < 0)
+               return ret;
+
+       sst_dsp_stall(hsw->dsp);
+
+       ret = sst_hsw_dx_state_dump(hsw);
+       if (ret < 0)
+               return ret;
+
+       sst_hsw_drop_all(hsw);
+
+       return 0;
+}
+
+int sst_hsw_dsp_runtime_sleep(struct sst_hsw *hsw)
+{
+       sst_fw_unload(hsw->sst_fw);
+       sst_block_free_scratch(hsw->dsp);
+
+       hsw->boot_complete = false;
+
+       sst_dsp_sleep(hsw->dsp);
+
+       return 0;
+}
+
+int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw)
+{
+       struct device *dev = hsw->dev;
+       int ret;
+
+       dev_dbg(dev, "audio dsp runtime resume\n");
+
+       if (hsw->boot_complete)
+               return 1; /* tell caller no action is required */
+
+       ret = sst_hsw_dsp_restore(hsw);
+       if (ret < 0)
+               dev_err(dev, "error: audio DSP boot failure\n");
+
+       ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete,
+               msecs_to_jiffies(IPC_BOOT_MSECS));
+       if (ret == 0) {
+               dev_err(hsw->dev, "error: audio DSP boot timeout IPCD 0x%x IPCX 0x%x\n",
+                       sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCD),
+                       sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX));
+               return -EIO;
+       }
+
+       /* Set ADSP SSP port settings */
+       ret = sst_hsw_device_set_config(hsw, SST_HSW_DEVICE_SSP_0,
+                                       SST_HSW_DEVICE_MCLK_FREQ_24_MHZ,
+                                       SST_HSW_DEVICE_CLOCK_MASTER, 9);
+       if (ret < 0)
+               dev_err(dev, "error: SSP re-initialization failed\n");
+
+       return ret;
+}
+#endif
+
 static int msg_empty_list_init(struct sst_hsw *hsw)
 {
        int i;
@@ -1718,12 +1978,6 @@ static int msg_empty_list_init(struct sst_hsw *hsw)
        return 0;
 }
 
-void sst_hsw_set_scratch_module(struct sst_hsw *hsw,
-       struct sst_module *scratch)
-{
-       hsw->scratch = scratch;
-}
-
 struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw)
 {
        return hsw->dsp;
@@ -1738,7 +1992,6 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
 {
        struct sst_hsw_ipc_fw_version version;
        struct sst_hsw *hsw;
-       struct sst_fw *hsw_sst_fw;
        int ret;
 
        dev_dbg(dev, "initialising Audio DSP IPC\n");
@@ -1780,12 +2033,19 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
                goto dsp_err;
        }
 
+       /* allocate DMA buffer for context storage */
+       hsw->dx_context = dma_alloc_coherent(hsw->dsp->dma_dev,
+               SST_HSW_DX_CONTEXT_SIZE, &hsw->dx_context_paddr, GFP_KERNEL);
+       if (hsw->dx_context == NULL) {
+               ret = -ENOMEM;
+               goto dma_err;
+       }
+
        /* keep the DSP in reset state for base FW loading */
        sst_dsp_reset(hsw->dsp);
 
-       hsw_sst_fw = sst_fw_new(hsw->dsp, pdata->fw, hsw);
-
-       if (hsw_sst_fw == NULL) {
+       hsw->sst_fw = sst_fw_new(hsw->dsp, pdata->fw, hsw);
+       if (hsw->sst_fw == NULL) {
                ret = -ENODEV;
                dev_err(dev, "error: failed to load firmware\n");
                goto fw_err;
@@ -1797,7 +2057,9 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
                msecs_to_jiffies(IPC_BOOT_MSECS));
        if (ret == 0) {
                ret = -EIO;
-               dev_err(hsw->dev, "error: ADSP boot timeout\n");
+               dev_err(hsw->dev, "error: audio DSP boot timeout IPCD 0x%x IPCX 0x%x\n",
+                       sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCD),
+                       sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX));
                goto boot_err;
        }
 
@@ -1816,8 +2078,11 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
 
 boot_err:
        sst_dsp_reset(hsw->dsp);
-       sst_fw_free(hsw_sst_fw);
+       sst_fw_free(hsw->sst_fw);
 fw_err:
+       dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE,
+                       hsw->dx_context, hsw->dx_context_paddr);
+dma_err:
        sst_dsp_free(hsw->dsp);
 dsp_err:
        kthread_stop(hsw->tx_thread);
@@ -1834,6 +2099,8 @@ void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata)
 
        sst_dsp_reset(hsw->dsp);
        sst_fw_free_all(hsw->dsp);
+       dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE,
+                       hsw->dx_context, hsw->dx_context_paddr);
        sst_dsp_free(hsw->dsp);
        kfree(hsw->scratch);
        kthread_stop(hsw->tx_thread);
index 2ac194a..138e894 100644 (file)
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
 
-#define SST_HSW_NO_CHANNELS            2
+#define SST_HSW_NO_CHANNELS            4
 #define SST_HSW_MAX_DX_REGIONS         14
+#define SST_HSW_DX_CONTEXT_SIZE        (640 * 1024)
+#define SST_HSW_CHANNELS_ALL           0xffffffff
 
 #define SST_HSW_FW_LOG_CONFIG_DWORDS   12
 #define SST_HSW_GLOBAL_LOG             15
@@ -40,6 +42,7 @@ struct sst_hsw_stream;
 struct sst_hsw_log_stream;
 struct sst_pdata;
 struct sst_module;
+struct sst_module_runtime;
 extern struct sst_ops haswell_ops;
 
 /* Stream Allocate Path ID */
@@ -84,6 +87,7 @@ enum sst_hsw_device_mclk {
 enum sst_hsw_device_mode {
        SST_HSW_DEVICE_CLOCK_SLAVE   = 0,
        SST_HSW_DEVICE_CLOCK_MASTER  = 1,
+       SST_HSW_DEVICE_TDM_CLOCK_MASTER = 2,
 };
 
 /* DX Power State */
@@ -295,7 +299,8 @@ struct sst_hsw_ipc_device_config_req {
        u32 clock_frequency;
        u32 mode;
        u16 clock_divider;
-       u16 reserved;
+       u8 channels;
+       u8 reserved;
 } __attribute__((packed));
 
 /* Audio Data formats */
@@ -430,8 +435,7 @@ int sst_hsw_stream_set_map_config(struct sst_hsw *hsw,
 int sst_hsw_stream_set_style(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
        enum sst_hsw_interleaving style);
 int sst_hsw_stream_set_module_info(struct sst_hsw *hsw,
-       struct sst_hsw_stream *stream, enum sst_hsw_module_id module_id,
-       u32 entry_point);
+       struct sst_hsw_stream *stream, struct sst_module_runtime *runtime);
 int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw,
        struct sst_hsw_stream *stream, u32 offset, u32 size);
 int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw,
@@ -484,7 +488,16 @@ int sst_hsw_dx_get_state(struct sst_hsw *hsw, u32 item,
 int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata);
 void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata);
 struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw);
-void sst_hsw_set_scratch_module(struct sst_hsw *hsw,
-       struct sst_module *scratch);
+
+/* runtime module management */
+struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw,
+       int mod_id, int offset);
+void sst_hsw_runtime_module_free(struct sst_module_runtime *runtime);
+
+/* PM */
+int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw);
+int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw);
+int sst_hsw_dsp_load(struct sst_hsw *hsw);
+int sst_hsw_dsp_runtime_sleep(struct sst_hsw *hsw);
 
 #endif
index 4df867c..0180b38 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
+#include <linux/pm_runtime.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include <sound/core.h>
@@ -73,6 +74,13 @@ static const u32 volume_map[] = {
 #define HSW_PCM_PERIODS_MAX    64
 #define HSW_PCM_PERIODS_MIN    2
 
+#define HSW_PCM_DAI_ID_SYSTEM  0
+#define HSW_PCM_DAI_ID_OFFLOAD0        1
+#define HSW_PCM_DAI_ID_OFFLOAD1        2
+#define HSW_PCM_DAI_ID_LOOPBACK        3
+#define HSW_PCM_DAI_ID_CAPTURE 4
+
+
 static const struct snd_pcm_hardware hsw_pcm_hardware = {
        .info                   = SNDRV_PCM_INFO_MMAP |
                                  SNDRV_PCM_INFO_MMAP_VALID |
@@ -89,22 +97,39 @@ static const struct snd_pcm_hardware hsw_pcm_hardware = {
        .buffer_bytes_max       = HSW_PCM_PERIODS_MAX * PAGE_SIZE,
 };
 
+struct hsw_pcm_module_map {
+       int dai_id;
+       enum sst_hsw_module_id mod_id;
+};
+
 /* private data for each PCM DSP stream */
 struct hsw_pcm_data {
        int dai_id;
        struct sst_hsw_stream *stream;
+       struct sst_module_runtime *runtime;
+       struct sst_module_runtime_context context;
+       struct snd_pcm *hsw_pcm;
        u32 volume[2];
        struct snd_pcm_substream *substream;
        struct snd_compr_stream *cstream;
        unsigned int wpos;
        struct mutex mutex;
        bool allocated;
+       int persistent_offset;
+};
+
+enum hsw_pm_state {
+       HSW_PM_STATE_D3 = 0,
+       HSW_PM_STATE_D0 = 1,
 };
 
 /* private data for the driver */
 struct hsw_priv_data {
        /* runtime DSP */
        struct sst_hsw *hsw;
+       struct device *dev;
+       enum hsw_pm_state pm_state;
+       struct snd_soc_card *soc_card;
 
        /* page tables */
        struct snd_dma_buffer dmab[HSW_PCM_COUNT][2];
@@ -138,21 +163,25 @@ static inline unsigned int hsw_ipc_to_mixer(u32 value)
 static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
-       struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
+       struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
+       struct hsw_priv_data *pdata =
+               snd_soc_platform_get_drvdata(platform);
        struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg];
        struct sst_hsw *hsw = pdata->hsw;
        u32 volume;
 
        mutex_lock(&pcm_data->mutex);
+       pm_runtime_get_sync(pdata->dev);
 
        if (!pcm_data->stream) {
                pcm_data->volume[0] =
                        hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
                pcm_data->volume[1] =
                        hsw_mixer_to_ipc(ucontrol->value.integer.value[1]);
+               pm_runtime_mark_last_busy(pdata->dev);
+               pm_runtime_put_autosuspend(pdata->dev);
                mutex_unlock(&pcm_data->mutex);
                return 0;
        }
@@ -160,7 +189,8 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
        if (ucontrol->value.integer.value[0] ==
                ucontrol->value.integer.value[1]) {
                volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
-               sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 2, volume);
+               /* apply volume value to all channels */
+               sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, SST_HSW_CHANNELS_ALL, volume);
        } else {
                volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
                sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 0, volume);
@@ -168,6 +198,8 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
                sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 1, volume);
        }
 
+       pm_runtime_mark_last_busy(pdata->dev);
+       pm_runtime_put_autosuspend(pdata->dev);
        mutex_unlock(&pcm_data->mutex);
        return 0;
 }
@@ -175,21 +207,25 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
 static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
-       struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
+       struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
+       struct hsw_priv_data *pdata =
+               snd_soc_platform_get_drvdata(platform);
        struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg];
        struct sst_hsw *hsw = pdata->hsw;
        u32 volume;
 
        mutex_lock(&pcm_data->mutex);
+       pm_runtime_get_sync(pdata->dev);
 
        if (!pcm_data->stream) {
                ucontrol->value.integer.value[0] =
                        hsw_ipc_to_mixer(pcm_data->volume[0]);
                ucontrol->value.integer.value[1] =
                        hsw_ipc_to_mixer(pcm_data->volume[1]);
+               pm_runtime_mark_last_busy(pdata->dev);
+               pm_runtime_put_autosuspend(pdata->dev);
                mutex_unlock(&pcm_data->mutex);
                return 0;
        }
@@ -198,6 +234,9 @@ static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
        ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume);
        sst_hsw_stream_get_volume(hsw, pcm_data->stream, 0, 1, &volume);
        ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume);
+
+       pm_runtime_mark_last_busy(pdata->dev);
+       pm_runtime_put_autosuspend(pdata->dev);
        mutex_unlock(&pcm_data->mutex);
 
        return 0;
@@ -206,16 +245,18 @@ static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
 static int hsw_volume_put(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
-       struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
+       struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+       struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
        struct sst_hsw *hsw = pdata->hsw;
        u32 volume;
 
+       pm_runtime_get_sync(pdata->dev);
+
        if (ucontrol->value.integer.value[0] ==
                ucontrol->value.integer.value[1]) {
 
                volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
-               sst_hsw_mixer_set_volume(hsw, 0, 2, volume);
+               sst_hsw_mixer_set_volume(hsw, 0, SST_HSW_CHANNELS_ALL, volume);
 
        } else {
                volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
@@ -225,23 +266,28 @@ static int hsw_volume_put(struct snd_kcontrol *kcontrol,
                sst_hsw_mixer_set_volume(hsw, 0, 1, volume);
        }
 
+       pm_runtime_mark_last_busy(pdata->dev);
+       pm_runtime_put_autosuspend(pdata->dev);
        return 0;
 }
 
 static int hsw_volume_get(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
-       struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
+       struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+       struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
        struct sst_hsw *hsw = pdata->hsw;
        unsigned int volume = 0;
 
+       pm_runtime_get_sync(pdata->dev);
        sst_hsw_mixer_get_volume(hsw, 0, 0, &volume);
        ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume);
 
        sst_hsw_mixer_get_volume(hsw, 0, 1, &volume);
        ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume);
 
+       pm_runtime_mark_last_busy(pdata->dev);
+       pm_runtime_put_autosuspend(pdata->dev);
        return 0;
 }
 
@@ -252,23 +298,19 @@ static const DECLARE_TLV_DB_SCALE(hsw_vol_tlv, -9000, 300, 1);
 static const struct snd_kcontrol_new hsw_volume_controls[] = {
        /* Global DSP volume */
        SOC_DOUBLE_EXT_TLV("Master Playback Volume", 0, 0, 8,
-               ARRAY_SIZE(volume_map) -1, 0,
+               ARRAY_SIZE(volume_map) - 1, 0,
                hsw_volume_get, hsw_volume_put, hsw_vol_tlv),
        /* Offload 0 volume */
        SOC_DOUBLE_EXT_TLV("Media0 Playback Volume", 1, 0, 8,
-               ARRAY_SIZE(volume_map), 0,
+               ARRAY_SIZE(volume_map) - 1, 0,
                hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
        /* Offload 1 volume */
        SOC_DOUBLE_EXT_TLV("Media1 Playback Volume", 2, 0, 8,
-               ARRAY_SIZE(volume_map), 0,
-               hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
-       /* Loopback volume */
-       SOC_DOUBLE_EXT_TLV("Loopback Capture Volume", 3, 0, 8,
-               ARRAY_SIZE(volume_map), 0,
+               ARRAY_SIZE(volume_map) - 1, 0,
                hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
        /* Mic Capture volume */
-       SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8,
-               ARRAY_SIZE(volume_map), 0,
+       SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 0, 0, 8,
+               ARRAY_SIZE(volume_map) - 1, 0,
                hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
 };
 
@@ -354,8 +396,14 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
        /* DSP stream type depends on DAI ID */
        switch (rtd->cpu_dai->id) {
        case 0:
-               stream_type = SST_HSW_STREAM_TYPE_SYSTEM;
-               module_id = SST_HSW_MODULE_PCM_SYSTEM;
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       stream_type = SST_HSW_STREAM_TYPE_SYSTEM;
+                       module_id = SST_HSW_MODULE_PCM_SYSTEM;
+               }
+               else {
+                       stream_type = SST_HSW_STREAM_TYPE_CAPTURE;
+                       module_id = SST_HSW_MODULE_PCM_CAPTURE;
+               }
                break;
        case 1:
        case 2:
@@ -368,10 +416,6 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
                path_id = SST_HSW_STREAM_PATH_SSP0_OUT;
                module_id = SST_HSW_MODULE_PCM_REFERENCE;
                break;
-       case 4:
-               stream_type = SST_HSW_STREAM_TYPE_CAPTURE;
-               module_id = SST_HSW_MODULE_PCM_CAPTURE;
-               break;
        default:
                dev_err(rtd->dev, "error: invalid DAI ID %d\n",
                        rtd->cpu_dai->id);
@@ -421,13 +465,7 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
                return ret;
        }
 
-       /* we only support stereo atm */
        channels = params_channels(params);
-       if (channels != 2) {
-               dev_err(rtd->dev, "error: invalid channels %d\n", channels);
-               return -EINVAL;
-       }
-
        map = create_channel_map(SST_HSW_CHANNEL_CONFIG_STEREO);
        sst_hsw_stream_set_map_config(hsw, pcm_data->stream,
                        map, SST_HSW_CHANNEL_CONFIG_STEREO);
@@ -478,35 +516,23 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       /* we use hardcoded memory offsets atm, will be updated for new FW */
-       if (stream_type == SST_HSW_STREAM_TYPE_CAPTURE) {
-               sst_hsw_stream_set_module_info(hsw, pcm_data->stream,
-                       SST_HSW_MODULE_PCM_CAPTURE, module_data->entry);
-               sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream,
-                       0x449400, 0x4000);
-               sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream,
-                       0x400000, 0);
-       } else { /* stream_type == SST_HSW_STREAM_TYPE_SYSTEM */
-               sst_hsw_stream_set_module_info(hsw, pcm_data->stream,
-                       SST_HSW_MODULE_PCM_SYSTEM, module_data->entry);
-
-               sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream,
-                       module_data->offset, module_data->size);
-               sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream,
-                       0x44d400, 0x3800);
-
-               sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream,
-                       module_data->offset, module_data->size);
-               sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream,
-                       0x400000, 0);
-       }
+       sst_hsw_stream_set_module_info(hsw, pcm_data->stream,
+               pcm_data->runtime);
 
        ret = sst_hsw_stream_commit(hsw, pcm_data->stream);
        if (ret < 0) {
                dev_err(rtd->dev, "error: failed to commit stream %d\n", ret);
                return ret;
        }
-       pcm_data->allocated = true;
+
+       if (!pcm_data->allocated) {
+               /* Set previous saved volume */
+               sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
+                               0, pcm_data->volume[0]);
+               sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
+                               1, pcm_data->volume[1]);
+               pcm_data->allocated = true;
+       }
 
        ret = sst_hsw_stream_pause(hsw, pcm_data->stream, 1);
        if (ret < 0)
@@ -558,7 +584,7 @@ static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data)
        pos = frames_to_bytes(runtime,
                (runtime->control->appl_ptr % runtime->buffer_size));
 
-       dev_dbg(rtd->dev, "PCM: App pointer %d bytes\n", pos);
+       dev_vdbg(rtd->dev, "PCM: App pointer %d bytes\n", pos);
 
        /* let alsa know we have play a period */
        snd_pcm_period_elapsed(substream);
@@ -580,7 +606,7 @@ static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_pcm_substream *substream)
        offset = bytes_to_frames(runtime, position);
        ppos = sst_hsw_get_dsp_presentation_position(hsw, pcm_data->stream);
 
-       dev_dbg(rtd->dev, "PCM: DMA pointer %du bytes, pos %llu\n",
+       dev_vdbg(rtd->dev, "PCM: DMA pointer %du bytes, pos %llu\n",
                position, ppos);
        return offset;
 }
@@ -596,6 +622,7 @@ static int hsw_pcm_open(struct snd_pcm_substream *substream)
        pcm_data = &pdata->pcm[rtd->cpu_dai->id];
 
        mutex_lock(&pcm_data->mutex);
+       pm_runtime_get_sync(pdata->dev);
 
        snd_soc_pcm_set_drvdata(rtd, pcm_data);
        pcm_data->substream = substream;
@@ -606,16 +633,12 @@ static int hsw_pcm_open(struct snd_pcm_substream *substream)
                hsw_notify_pointer, pcm_data);
        if (pcm_data->stream == NULL) {
                dev_err(rtd->dev, "error: failed to create stream\n");
+               pm_runtime_mark_last_busy(pdata->dev);
+               pm_runtime_put_autosuspend(pdata->dev);
                mutex_unlock(&pcm_data->mutex);
                return -EINVAL;
        }
 
-       /* Set previous saved volume */
-       sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
-                       0, pcm_data->volume[0]);
-       sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
-                       1, pcm_data->volume[1]);
-
        mutex_unlock(&pcm_data->mutex);
        return 0;
 }
@@ -645,6 +668,8 @@ static int hsw_pcm_close(struct snd_pcm_substream *substream)
        pcm_data->stream = NULL;
 
 out:
+       pm_runtime_mark_last_busy(pdata->dev);
+       pm_runtime_put_autosuspend(pdata->dev);
        mutex_unlock(&pcm_data->mutex);
        return ret;
 }
@@ -660,6 +685,56 @@ static struct snd_pcm_ops hsw_pcm_ops = {
        .page           = snd_pcm_sgbuf_ops_page,
 };
 
+/* static mappings between PCMs and modules - may be dynamic in future */
+static struct hsw_pcm_module_map mod_map[] = {
+       {HSW_PCM_DAI_ID_SYSTEM, SST_HSW_MODULE_PCM_SYSTEM},
+       {HSW_PCM_DAI_ID_OFFLOAD0, SST_HSW_MODULE_PCM},
+       {HSW_PCM_DAI_ID_OFFLOAD1, SST_HSW_MODULE_PCM},
+       {HSW_PCM_DAI_ID_LOOPBACK, SST_HSW_MODULE_PCM_REFERENCE},
+       {HSW_PCM_DAI_ID_CAPTURE, SST_HSW_MODULE_PCM_CAPTURE},
+};
+
+static int hsw_pcm_create_modules(struct hsw_priv_data *pdata)
+{
+       struct sst_hsw *hsw = pdata->hsw;
+       struct hsw_pcm_data *pcm_data;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
+               pcm_data = &pdata->pcm[i];
+
+               /* create new runtime module, use same offset if recreated */
+               pcm_data->runtime = sst_hsw_runtime_module_create(hsw,
+                       mod_map[i].mod_id, pcm_data->persistent_offset);
+               if (pcm_data->runtime == NULL)
+                       goto err;
+               pcm_data->persistent_offset =
+                       pcm_data->runtime->persistent_offset;
+       }
+
+       return 0;
+
+err:
+       for (--i; i >= 0; i--) {
+               pcm_data = &pdata->pcm[i];
+               sst_hsw_runtime_module_free(pcm_data->runtime);
+       }
+
+       return -ENODEV;
+}
+
+static void hsw_pcm_free_modules(struct hsw_priv_data *pdata)
+{
+       struct hsw_pcm_data *pcm_data;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
+               pcm_data = &pdata->pcm[i];
+
+               sst_hsw_runtime_module_free(pcm_data->runtime);
+       }
+}
+
 static void hsw_pcm_free(struct snd_pcm *pcm)
 {
        snd_pcm_lib_preallocate_free_for_all(pcm);
@@ -670,6 +745,7 @@ static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
        struct snd_pcm *pcm = rtd->pcm;
        struct snd_soc_platform *platform = rtd->platform;
        struct sst_pdata *pdata = dev_get_platdata(platform->dev);
+       struct hsw_priv_data *priv_data = dev_get_drvdata(platform->dev);
        struct device *dev = pdata->dma_dev;
        int ret = 0;
 
@@ -686,6 +762,7 @@ static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
                        return ret;
                }
        }
+       priv_data->pcm[rtd->cpu_dai->id].hsw_pcm = pcm;
 
        return ret;
 }
@@ -696,6 +773,7 @@ static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
 static struct snd_soc_dai_driver hsw_dais[] = {
        {
                .name  = "System Pin",
+               .id = HSW_PCM_DAI_ID_SYSTEM,
                .playback = {
                        .stream_name = "System Playback",
                        .channels_min = 2,
@@ -703,10 +781,18 @@ static struct snd_soc_dai_driver hsw_dais[] = {
                        .rates = SNDRV_PCM_RATE_48000,
                        .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
                },
+               .capture = {
+                       .stream_name = "Analog Capture",
+                       .channels_min = 2,
+                       .channels_max = 4,
+                       .rates = SNDRV_PCM_RATE_48000,
+                       .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
+               },
        },
        {
                /* PCM */
                .name  = "Offload0 Pin",
+               .id = HSW_PCM_DAI_ID_OFFLOAD0,
                .playback = {
                        .stream_name = "Offload0 Playback",
                        .channels_min = 2,
@@ -718,6 +804,7 @@ static struct snd_soc_dai_driver hsw_dais[] = {
        {
                /* PCM */
                .name  = "Offload1 Pin",
+               .id = HSW_PCM_DAI_ID_OFFLOAD1,
                .playback = {
                        .stream_name = "Offload1 Playback",
                        .channels_min = 2,
@@ -728,6 +815,7 @@ static struct snd_soc_dai_driver hsw_dais[] = {
        },
        {
                .name  = "Loopback Pin",
+               .id = HSW_PCM_DAI_ID_LOOPBACK,
                .capture = {
                        .stream_name = "Loopback Capture",
                        .channels_min = 2,
@@ -736,16 +824,6 @@ static struct snd_soc_dai_driver hsw_dais[] = {
                        .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
                },
        },
-       {
-               .name  = "Capture Pin",
-               .capture = {
-                       .stream_name = "Analog Capture",
-                       .channels_min = 2,
-                       .channels_max = 2,
-                       .rates = SNDRV_PCM_RATE_48000,
-                       .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
-               },
-       },
 };
 
 static const struct snd_soc_dapm_widget widgets[] = {
@@ -776,9 +854,20 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform)
 {
        struct hsw_priv_data *priv_data = snd_soc_platform_get_drvdata(platform);
        struct sst_pdata *pdata = dev_get_platdata(platform->dev);
-       struct device *dma_dev = pdata->dma_dev;
+       struct device *dma_dev, *dev;
        int i, ret = 0;
 
+       if (!pdata)
+               return -ENODEV;
+
+       dev = platform->dev;
+       dma_dev = pdata->dma_dev;
+
+       priv_data->hsw = pdata->dsp;
+       priv_data->dev = platform->dev;
+       priv_data->pm_state = HSW_PM_STATE_D0;
+       priv_data->soc_card = platform->component.card;
+
        /* allocate DSP buffer page tables */
        for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
 
@@ -801,6 +890,16 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform)
                }
        }
 
+       /* allocate runtime modules */
+       hsw_pcm_create_modules(priv_data);
+
+       /* enable runtime PM with auto suspend */
+       pm_runtime_set_autosuspend_delay(platform->dev,
+               SST_RUNTIME_SUSPEND_DELAY);
+       pm_runtime_use_autosuspend(platform->dev);
+       pm_runtime_enable(platform->dev);
+       pm_runtime_idle(platform->dev);
+
        return 0;
 
 err:
@@ -819,6 +918,9 @@ static int hsw_pcm_remove(struct snd_soc_platform *platform)
                snd_soc_platform_get_drvdata(platform);
        int i;
 
+       pm_runtime_disable(platform->dev);
+       hsw_pcm_free_modules(priv_data);
+
        for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
                if (hsw_dais[i].playback.channels_min)
                        snd_dma_free_pages(&priv_data->dmab[i][0]);
@@ -896,10 +998,181 @@ static int hsw_pcm_dev_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM_RUNTIME
+
+static int hsw_pcm_runtime_idle(struct device *dev)
+{
+       return 0;
+}
+
+static int hsw_pcm_runtime_suspend(struct device *dev)
+{
+       struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+       struct sst_hsw *hsw = pdata->hsw;
+
+       if (pdata->pm_state == HSW_PM_STATE_D3)
+               return 0;
+
+       sst_hsw_dsp_runtime_suspend(hsw);
+       sst_hsw_dsp_runtime_sleep(hsw);
+       pdata->pm_state = HSW_PM_STATE_D3;
+
+       return 0;
+}
+
+static int hsw_pcm_runtime_resume(struct device *dev)
+{
+       struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+       struct sst_hsw *hsw = pdata->hsw;
+       int ret;
+
+       if (pdata->pm_state == HSW_PM_STATE_D0)
+               return 0;
+
+       ret = sst_hsw_dsp_load(hsw);
+       if (ret < 0) {
+               dev_err(dev, "failed to reload %d\n", ret);
+               return ret;
+       }
+
+       ret = hsw_pcm_create_modules(pdata);
+       if (ret < 0) {
+               dev_err(dev, "failed to create modules %d\n", ret);
+               return ret;
+       }
+
+       ret = sst_hsw_dsp_runtime_resume(hsw);
+       if (ret < 0)
+               return ret;
+       else if (ret == 1) /* no action required */
+               return 0;
+
+       pdata->pm_state = HSW_PM_STATE_D0;
+       return ret;
+}
+
+#else
+#define hsw_pcm_runtime_idle           NULL
+#define hsw_pcm_runtime_suspend                NULL
+#define hsw_pcm_runtime_resume         NULL
+#endif
+
+#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_RUNTIME)
+
+static void hsw_pcm_complete(struct device *dev)
+{
+       struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+       struct sst_hsw *hsw = pdata->hsw;
+       struct hsw_pcm_data *pcm_data;
+       int i, err;
+
+       if (pdata->pm_state == HSW_PM_STATE_D0)
+               return;
+
+       err = sst_hsw_dsp_load(hsw);
+       if (err < 0) {
+               dev_err(dev, "failed to reload %d\n", err);
+               return;
+       }
+
+       err = hsw_pcm_create_modules(pdata);
+       if (err < 0) {
+               dev_err(dev, "failed to create modules %d\n", err);
+               return;
+       }
+
+       for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) {
+               pcm_data = &pdata->pcm[i];
+
+               if (!pcm_data->substream)
+                       continue;
+
+               err = sst_module_runtime_restore(pcm_data->runtime,
+                       &pcm_data->context);
+               if (err < 0)
+                       dev_err(dev, "failed to restore context for PCM %d\n", i);
+       }
+
+       snd_soc_resume(pdata->soc_card->dev);
+
+       err = sst_hsw_dsp_runtime_resume(hsw);
+       if (err < 0)
+               return;
+       else if (err == 1) /* no action required */
+               return;
+
+       pdata->pm_state = HSW_PM_STATE_D0;
+       return;
+}
+
+static int hsw_pcm_prepare(struct device *dev)
+{
+       struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+       struct sst_hsw *hsw = pdata->hsw;
+       struct hsw_pcm_data *pcm_data;
+       int i, err;
+
+       if (pdata->pm_state == HSW_PM_STATE_D3)
+               return 0;
+       /* suspend all active streams */
+       for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) {
+               pcm_data = &pdata->pcm[i];
+
+               if (!pcm_data->substream)
+                       continue;
+               dev_dbg(dev, "suspending pcm %d\n", i);
+               snd_pcm_suspend_all(pcm_data->hsw_pcm);
+
+               /* We need to wait until the DSP FW stops the streams */
+               msleep(2);
+       }
+
+       snd_soc_suspend(pdata->soc_card->dev);
+       snd_soc_poweroff(pdata->soc_card->dev);
+
+       /* enter D3 state and stall */
+       sst_hsw_dsp_runtime_suspend(hsw);
+
+       /* preserve persistent memory */
+       for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) {
+               pcm_data = &pdata->pcm[i];
+
+               if (!pcm_data->substream)
+                       continue;
+
+               dev_dbg(dev, "saving context pcm %d\n", i);
+               err = sst_module_runtime_save(pcm_data->runtime,
+                       &pcm_data->context);
+               if (err < 0)
+                       dev_err(dev, "failed to save context for PCM %d\n", i);
+       }
+
+       /* put the DSP to sleep */
+       sst_hsw_dsp_runtime_sleep(hsw);
+       pdata->pm_state = HSW_PM_STATE_D3;
+
+       return 0;
+}
+
+#else
+#define hsw_pcm_prepare                NULL
+#define hsw_pcm_complete       NULL
+#endif
+
+static const struct dev_pm_ops hsw_pcm_pm = {
+       .runtime_idle = hsw_pcm_runtime_idle,
+       .runtime_suspend = hsw_pcm_runtime_suspend,
+       .runtime_resume = hsw_pcm_runtime_resume,
+       .prepare = hsw_pcm_prepare,
+       .complete = hsw_pcm_complete,
+};
+
 static struct platform_driver hsw_pcm_driver = {
        .driver = {
                .name = "haswell-pcm-audio",
                .owner = THIS_MODULE,
+               .pm = &hsw_pcm_pm,
+
        },
 
        .probe = hsw_pcm_dev_probe,
index 5946777..3951689 100644 (file)
@@ -67,8 +67,11 @@ static int sst_platform_compr_open(struct snd_compr_stream *cstream)
                goto out_ops;
        }
        stream->compr_ops = sst->compr_ops;
-
        stream->id = 0;
+
+       /* Turn on LPE */
+       sst->compr_ops->power(sst->dev, true);
+
        sst_set_stream_status(stream, SST_PLATFORM_INIT);
        runtime->private_data = stream;
        return 0;
@@ -83,6 +86,9 @@ static int sst_platform_compr_free(struct snd_compr_stream *cstream)
        int ret_val = 0, str_id;
 
        stream = cstream->runtime->private_data;
+       /* Turn off LPE */
+       sst->compr_ops->power(sst->dev, false);
+
        /*need to check*/
        str_id = stream->id;
        if (str_id)
index aa9b600..6032f18 100644 (file)
@@ -101,35 +101,11 @@ static struct sst_dev_stream_map dpcm_strm_map[] = {
        {MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_PCM1_OUT, SST_TASK_ID_MEDIA, 0},
 };
 
-/* MFLD - MSIC */
-static struct snd_soc_dai_driver sst_platform_dai[] = {
+static int sst_media_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
 {
-       .name = "Headset-cpu-dai",
-       .id = 0,
-       .playback = {
-               .channels_min = SST_STEREO,
-               .channels_max = SST_STEREO,
-               .rates = SNDRV_PCM_RATE_48000,
-               .formats = SNDRV_PCM_FMTBIT_S24_LE,
-       },
-       .capture = {
-               .channels_min = 1,
-               .channels_max = 5,
-               .rates = SNDRV_PCM_RATE_48000,
-               .formats = SNDRV_PCM_FMTBIT_S24_LE,
-       },
-},
-{
-       .name = "Compress-cpu-dai",
-       .compress_dai = 1,
-       .playback = {
-               .channels_min = SST_STEREO,
-               .channels_max = SST_STEREO,
-               .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,
-       },
-},
-};
+
+       return sst_send_pipe_gains(dai, stream, mute);
+}
 
 /* helper functions */
 void sst_set_stream_status(struct sst_runtime_stream *stream,
@@ -451,12 +427,133 @@ static int sst_media_hw_free(struct snd_pcm_substream *substream,
        return snd_pcm_lib_free_pages(substream);
 }
 
+static int sst_enable_ssp(struct snd_pcm_substream *substream,
+                       struct snd_soc_dai *dai)
+{
+       int ret = 0;
+
+       if (!dai->active) {
+               ret = sst_handle_vb_timer(dai, true);
+               if (ret)
+                       return ret;
+               ret = send_ssp_cmd(dai, dai->name, 1);
+       }
+       return ret;
+}
+
+static void sst_disable_ssp(struct snd_pcm_substream *substream,
+                       struct snd_soc_dai *dai)
+{
+       if (!dai->active) {
+               send_ssp_cmd(dai, dai->name, 0);
+               sst_handle_vb_timer(dai, false);
+       }
+}
+
 static struct snd_soc_dai_ops sst_media_dai_ops = {
        .startup = sst_media_open,
        .shutdown = sst_media_close,
        .prepare = sst_media_prepare,
        .hw_params = sst_media_hw_params,
        .hw_free = sst_media_hw_free,
+       .mute_stream = sst_media_digital_mute,
+};
+
+static struct snd_soc_dai_ops sst_compr_dai_ops = {
+       .mute_stream = sst_media_digital_mute,
+};
+
+static struct snd_soc_dai_ops sst_be_dai_ops = {
+       .startup = sst_enable_ssp,
+       .shutdown = sst_disable_ssp,
+};
+
+static struct snd_soc_dai_driver sst_platform_dai[] = {
+{
+       .name = "media-cpu-dai",
+       .ops = &sst_media_dai_ops,
+       .playback = {
+               .stream_name = "Headset Playback",
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "Headset Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+},
+{
+       .name = "compress-cpu-dai",
+       .compress_dai = 1,
+       .ops = &sst_compr_dai_ops,
+       .playback = {
+               .stream_name = "Compress Playback",
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+},
+/* BE CPU  Dais */
+{
+       .name = "ssp0-port",
+       .ops = &sst_be_dai_ops,
+       .playback = {
+               .stream_name = "ssp0 Tx",
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "ssp0 Rx",
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+},
+{
+       .name = "ssp1-port",
+       .ops = &sst_be_dai_ops,
+       .playback = {
+               .stream_name = "ssp1 Tx",
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "ssp1 Rx",
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+},
+{
+       .name = "ssp2-port",
+       .ops = &sst_be_dai_ops,
+       .playback = {
+               .stream_name = "ssp2 Tx",
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "ssp2 Rx",
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+},
 };
 
 static int sst_platform_open(struct snd_pcm_substream *substream)
@@ -609,6 +706,7 @@ static int sst_platform_probe(struct platform_device *pdev)
        pdata->pdev_strm_map = dpcm_strm_map;
        pdata->strm_map_size = ARRAY_SIZE(dpcm_strm_map);
        drv->pdata = pdata;
+       drv->pdev = pdev;
        mutex_init(&drv->lock);
        dev_set_drvdata(&pdev->dev, drv);
 
index 19f83ec..79c8d12 100644 (file)
@@ -117,6 +117,7 @@ struct compress_sst_ops {
        int (*get_codec_caps)(struct snd_compr_codec_caps *codec);
        int (*set_metadata)(struct device *dev, unsigned int str_id,
                        struct snd_compr_metadata *mdata);
+       int (*power)(struct device *dev, bool state);
 };
 
 struct sst_ops {
@@ -153,6 +154,10 @@ struct sst_device {
 struct sst_data;
 
 int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform);
+int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute);
+int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable);
+int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable);
+
 void sst_set_stream_status(struct sst_runtime_stream *stream, int state);
 int sst_fill_stream_params(void *substream, const struct sst_data *ctx,
                           struct snd_sst_params *str_params, bool is_compress);
diff --git a/sound/soc/intel/sst/Makefile b/sound/soc/intel/sst/Makefile
new file mode 100644 (file)
index 0000000..fd21726
--- /dev/null
@@ -0,0 +1,7 @@
+snd-intel-sst-core-objs := sst.o sst_ipc.o sst_stream.o sst_drv_interface.o sst_loader.o sst_pvt.o
+snd-intel-sst-pci-objs += sst_pci.o
+snd-intel-sst-acpi-objs += sst_acpi.o
+
+obj-$(CONFIG_SND_SST_IPC) += snd-intel-sst-core.o
+obj-$(CONFIG_SND_SST_IPC_PCI) += snd-intel-sst-pci.o
+obj-$(CONFIG_SND_SST_IPC_ACPI) += snd-intel-sst-acpi.o
diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c
new file mode 100644 (file)
index 0000000..8a8d56a
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+ *  sst.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14      Intel Corp
+ *  Authors:   Vinod Koul <vinod.koul@intel.com>
+ *             Harsha Priya <priya.harsha@intel.com>
+ *             Dharageswari R <dharageswari.r@intel.com>
+ *             KP Jeeja <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/async.h>
+#include <linux/acpi.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine Driver");
+MODULE_LICENSE("GPL v2");
+
+static inline bool sst_is_process_reply(u32 msg_id)
+{
+       return ((msg_id & PROCESS_MSG) ? true : false);
+}
+
+static inline bool sst_validate_mailbox_size(unsigned int size)
+{
+       return ((size <= SST_MAILBOX_SIZE) ? true : false);
+}
+
+static irqreturn_t intel_sst_interrupt_mrfld(int irq, void *context)
+{
+       union interrupt_reg_mrfld isr;
+       union ipc_header_mrfld header;
+       union sst_imr_reg_mrfld imr;
+       struct ipc_post *msg = NULL;
+       unsigned int size = 0;
+       struct intel_sst_drv *drv = (struct intel_sst_drv *) context;
+       irqreturn_t retval = IRQ_HANDLED;
+
+       /* Interrupt arrived, check src */
+       isr.full = sst_shim_read64(drv->shim, SST_ISRX);
+
+       if (isr.part.done_interrupt) {
+               /* Clear done bit */
+               spin_lock(&drv->ipc_spin_lock);
+               header.full = sst_shim_read64(drv->shim,
+                                       drv->ipc_reg.ipcx);
+               header.p.header_high.part.done = 0;
+               sst_shim_write64(drv->shim, drv->ipc_reg.ipcx, header.full);
+
+               /* write 1 to clear status register */;
+               isr.part.done_interrupt = 1;
+               sst_shim_write64(drv->shim, SST_ISRX, isr.full);
+               spin_unlock(&drv->ipc_spin_lock);
+
+               /* we can send more messages to DSP so trigger work */
+               queue_work(drv->post_msg_wq, &drv->ipc_post_msg_wq);
+               retval = IRQ_HANDLED;
+       }
+
+       if (isr.part.busy_interrupt) {
+               /* message from dsp so copy that */
+               spin_lock(&drv->ipc_spin_lock);
+               imr.full = sst_shim_read64(drv->shim, SST_IMRX);
+               imr.part.busy_interrupt = 1;
+               sst_shim_write64(drv->shim, SST_IMRX, imr.full);
+               spin_unlock(&drv->ipc_spin_lock);
+               header.full =  sst_shim_read64(drv->shim, drv->ipc_reg.ipcd);
+
+               if (sst_create_ipc_msg(&msg, header.p.header_high.part.large)) {
+                       drv->ops->clear_interrupt(drv);
+                       return IRQ_HANDLED;
+               }
+
+               if (header.p.header_high.part.large) {
+                       size = header.p.header_low_payload;
+                       if (sst_validate_mailbox_size(size)) {
+                               memcpy_fromio(msg->mailbox_data,
+                                       drv->mailbox + drv->mailbox_recv_offset, size);
+                       } else {
+                               dev_err(drv->dev,
+                                       "Mailbox not copied, payload size is: %u\n", size);
+                               header.p.header_low_payload = 0;
+                       }
+               }
+
+               msg->mrfld_header = header;
+               msg->is_process_reply =
+                       sst_is_process_reply(header.p.header_high.part.msg_id);
+               spin_lock(&drv->rx_msg_lock);
+               list_add_tail(&msg->node, &drv->rx_list);
+               spin_unlock(&drv->rx_msg_lock);
+               drv->ops->clear_interrupt(drv);
+               retval = IRQ_WAKE_THREAD;
+       }
+       return retval;
+}
+
+static irqreturn_t intel_sst_irq_thread_mrfld(int irq, void *context)
+{
+       struct intel_sst_drv *drv = (struct intel_sst_drv *) context;
+       struct ipc_post *__msg, *msg = NULL;
+       unsigned long irq_flags;
+
+       spin_lock_irqsave(&drv->rx_msg_lock, irq_flags);
+       if (list_empty(&drv->rx_list)) {
+               spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
+               return IRQ_HANDLED;
+       }
+
+       list_for_each_entry_safe(msg, __msg, &drv->rx_list, node) {
+               list_del(&msg->node);
+               spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
+               if (msg->is_process_reply)
+                       drv->ops->process_message(msg);
+               else
+                       drv->ops->process_reply(drv, msg);
+
+               if (msg->is_large)
+                       kfree(msg->mailbox_data);
+               kfree(msg);
+               spin_lock_irqsave(&drv->rx_msg_lock, irq_flags);
+       }
+       spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
+       return IRQ_HANDLED;
+}
+
+static int sst_save_dsp_context_v2(struct intel_sst_drv *sst)
+{
+       int ret = 0;
+
+       ret = sst_prepare_and_post_msg(sst, SST_TASK_ID_MEDIA, IPC_CMD,
+                       IPC_PREP_D3, PIPE_RSVD, 0, NULL, NULL,
+                       true, true, false, true);
+
+       if (ret < 0) {
+               dev_err(sst->dev, "not suspending FW!!, Err: %d\n", ret);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+
+static struct intel_sst_ops mrfld_ops = {
+       .interrupt = intel_sst_interrupt_mrfld,
+       .irq_thread = intel_sst_irq_thread_mrfld,
+       .clear_interrupt = intel_sst_clear_intr_mrfld,
+       .start = sst_start_mrfld,
+       .reset = intel_sst_reset_dsp_mrfld,
+       .post_message = sst_post_message_mrfld,
+       .process_reply = sst_process_reply_mrfld,
+       .save_dsp_context =  sst_save_dsp_context_v2,
+       .alloc_stream = sst_alloc_stream_mrfld,
+       .post_download = sst_post_download_mrfld,
+};
+
+int sst_driver_ops(struct intel_sst_drv *sst)
+{
+
+       switch (sst->dev_id) {
+       case SST_MRFLD_PCI_ID:
+       case SST_BYT_ACPI_ID:
+       case SST_CHV_ACPI_ID:
+               sst->tstamp = SST_TIME_STAMP_MRFLD;
+               sst->ops = &mrfld_ops;
+               return 0;
+
+       default:
+               dev_err(sst->dev,
+                       "SST Driver capablities missing for dev_id: %x", sst->dev_id);
+               return -EINVAL;
+       };
+}
+
+void sst_process_pending_msg(struct work_struct *work)
+{
+       struct intel_sst_drv *ctx = container_of(work,
+                       struct intel_sst_drv, ipc_post_msg_wq);
+
+       ctx->ops->post_message(ctx, NULL, false);
+}
+
+static int sst_workqueue_init(struct intel_sst_drv *ctx)
+{
+       INIT_LIST_HEAD(&ctx->memcpy_list);
+       INIT_LIST_HEAD(&ctx->rx_list);
+       INIT_LIST_HEAD(&ctx->ipc_dispatch_list);
+       INIT_LIST_HEAD(&ctx->block_list);
+       INIT_WORK(&ctx->ipc_post_msg_wq, sst_process_pending_msg);
+       init_waitqueue_head(&ctx->wait_queue);
+
+       ctx->post_msg_wq =
+               create_singlethread_workqueue("sst_post_msg_wq");
+       if (!ctx->post_msg_wq)
+               return -EBUSY;
+       return 0;
+}
+
+static void sst_init_locks(struct intel_sst_drv *ctx)
+{
+       mutex_init(&ctx->sst_lock);
+       spin_lock_init(&ctx->rx_msg_lock);
+       spin_lock_init(&ctx->ipc_spin_lock);
+       spin_lock_init(&ctx->block_lock);
+}
+
+int sst_alloc_drv_context(struct intel_sst_drv **ctx,
+               struct device *dev, unsigned int dev_id)
+{
+       *ctx = devm_kzalloc(dev, sizeof(struct intel_sst_drv), GFP_KERNEL);
+       if (!(*ctx))
+               return -ENOMEM;
+
+       (*ctx)->dev = dev;
+       (*ctx)->dev_id = dev_id;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(sst_alloc_drv_context);
+
+int sst_context_init(struct intel_sst_drv *ctx)
+{
+       int ret = 0, i;
+
+       if (!ctx->pdata)
+               return -EINVAL;
+
+       if (!ctx->pdata->probe_data)
+               return -EINVAL;
+
+       memcpy(&ctx->info, ctx->pdata->probe_data, sizeof(ctx->info));
+
+       ret = sst_driver_ops(ctx);
+       if (ret != 0)
+               return -EINVAL;
+
+       sst_init_locks(ctx);
+       sst_set_fw_state_locked(ctx, SST_RESET);
+
+       /* pvt_id 0 reserved for async messages */
+       ctx->pvt_id = 1;
+       ctx->stream_cnt = 0;
+       ctx->fw_in_mem = NULL;
+       /* we use memcpy, so set to 0 */
+       ctx->use_dma = 0;
+       ctx->use_lli = 0;
+
+       if (sst_workqueue_init(ctx))
+               return -EINVAL;
+
+       ctx->mailbox_recv_offset = ctx->pdata->ipc_info->mbox_recv_off;
+       ctx->ipc_reg.ipcx = SST_IPCX + ctx->pdata->ipc_info->ipc_offset;
+       ctx->ipc_reg.ipcd = SST_IPCD + ctx->pdata->ipc_info->ipc_offset;
+
+       dev_info(ctx->dev, "Got drv data max stream %d\n",
+                               ctx->info.max_streams);
+
+       for (i = 1; i <= ctx->info.max_streams; i++) {
+               struct stream_info *stream = &ctx->streams[i];
+
+               memset(stream, 0, sizeof(*stream));
+               stream->pipe_id = PIPE_RSVD;
+               mutex_init(&stream->lock);
+       }
+
+       /* Register the ISR */
+       ret = devm_request_threaded_irq(ctx->dev, ctx->irq_num, ctx->ops->interrupt,
+                                       ctx->ops->irq_thread, 0, SST_DRV_NAME,
+                                       ctx);
+       if (ret)
+               goto do_free_mem;
+
+       dev_dbg(ctx->dev, "Registered IRQ %#x\n", ctx->irq_num);
+
+       /* default intr are unmasked so set this as masked */
+       sst_shim_write64(ctx->shim, SST_IMRX, 0xFFFF0038);
+
+       ctx->qos = devm_kzalloc(ctx->dev,
+               sizeof(struct pm_qos_request), GFP_KERNEL);
+       if (!ctx->qos) {
+               ret = -ENOMEM;
+               goto do_free_mem;
+       }
+       pm_qos_add_request(ctx->qos, PM_QOS_CPU_DMA_LATENCY,
+                               PM_QOS_DEFAULT_VALUE);
+
+       dev_dbg(ctx->dev, "Requesting FW %s now...\n", ctx->firmware_name);
+       ret = request_firmware_nowait(THIS_MODULE, true, ctx->firmware_name,
+                                     ctx->dev, GFP_KERNEL, ctx, sst_firmware_load_cb);
+       if (ret) {
+               dev_err(ctx->dev, "Firmware download failed:%d\n", ret);
+               goto do_free_mem;
+       }
+       sst_register(ctx->dev);
+       return 0;
+
+do_free_mem:
+       destroy_workqueue(ctx->post_msg_wq);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(sst_context_init);
+
+void sst_context_cleanup(struct intel_sst_drv *ctx)
+{
+       pm_runtime_get_noresume(ctx->dev);
+       pm_runtime_disable(ctx->dev);
+       sst_unregister(ctx->dev);
+       sst_set_fw_state_locked(ctx, SST_SHUTDOWN);
+       flush_scheduled_work();
+       destroy_workqueue(ctx->post_msg_wq);
+       pm_qos_remove_request(ctx->qos);
+       kfree(ctx->fw_sg_list.src);
+       kfree(ctx->fw_sg_list.dst);
+       ctx->fw_sg_list.list_len = 0;
+       kfree(ctx->fw_in_mem);
+       ctx->fw_in_mem = NULL;
+       sst_memcpy_free_resources(ctx);
+       ctx = NULL;
+}
+EXPORT_SYMBOL_GPL(sst_context_cleanup);
+
+static inline void sst_save_shim64(struct intel_sst_drv *ctx,
+                           void __iomem *shim,
+                           struct sst_shim_regs64 *shim_regs)
+{
+       unsigned long irq_flags;
+
+       spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags);
+
+       shim_regs->imrx = sst_shim_read64(shim, SST_IMRX),
+
+       spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags);
+}
+
+static inline void sst_restore_shim64(struct intel_sst_drv *ctx,
+                                     void __iomem *shim,
+                                     struct sst_shim_regs64 *shim_regs)
+{
+       unsigned long irq_flags;
+
+       /*
+        * we only need to restore IMRX for this case, rest will be
+        * initialize by FW or driver when firmware is loaded
+        */
+       spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags);
+       sst_shim_write64(shim, SST_IMRX, shim_regs->imrx),
+       spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags);
+}
+
+void sst_configure_runtime_pm(struct intel_sst_drv *ctx)
+{
+       pm_runtime_set_autosuspend_delay(ctx->dev, SST_SUSPEND_DELAY);
+       pm_runtime_use_autosuspend(ctx->dev);
+       /*
+        * For acpi devices, the actual physical device state is
+        * initially active. So change the state to active before
+        * enabling the pm
+        */
+       pm_runtime_enable(ctx->dev);
+
+       if (acpi_disabled)
+               pm_runtime_set_active(ctx->dev);
+       else
+               pm_runtime_put_noidle(ctx->dev);
+
+       sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64);
+}
+EXPORT_SYMBOL_GPL(sst_configure_runtime_pm);
+
+static int intel_sst_runtime_suspend(struct device *dev)
+{
+       int ret = 0;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       if (ctx->sst_state == SST_RESET) {
+               dev_dbg(dev, "LPE is already in RESET state, No action\n");
+               return 0;
+       }
+       /* save fw context */
+       if (ctx->ops->save_dsp_context(ctx))
+               return -EBUSY;
+
+       /* Move the SST state to Reset */
+       sst_set_fw_state_locked(ctx, SST_RESET);
+
+       synchronize_irq(ctx->irq_num);
+       flush_workqueue(ctx->post_msg_wq);
+
+       /* save the shim registers because PMC doesn't save state */
+       sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64);
+
+       return ret;
+}
+
+static int intel_sst_runtime_resume(struct device *dev)
+{
+       int ret = 0;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       if (ctx->sst_state == SST_RESET) {
+               ret = sst_load_fw(ctx);
+               if (ret) {
+                       dev_err(dev, "FW download fail %d\n", ret);
+                       sst_set_fw_state_locked(ctx, SST_RESET);
+               }
+       }
+       return ret;
+}
+
+const struct dev_pm_ops intel_sst_pm = {
+       .runtime_suspend = intel_sst_runtime_suspend,
+       .runtime_resume = intel_sst_runtime_resume,
+};
+EXPORT_SYMBOL_GPL(intel_sst_pm);
diff --git a/sound/soc/intel/sst/sst.h b/sound/soc/intel/sst/sst.h
new file mode 100644 (file)
index 0000000..7f4bbfc
--- /dev/null
@@ -0,0 +1,546 @@
+/*
+ *  sst.h - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14 Intel Corporation
+ *  Authors:   Vinod Koul <vinod.koul@intel.com>
+ *             Harsha Priya <priya.harsha@intel.com>
+ *             Dharageswari R <dharageswari.r@intel.com>
+ *             KP Jeeja <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  Common private declarations for SST
+ */
+#ifndef __SST_H__
+#define __SST_H__
+
+#include <linux/firmware.h>
+
+/* driver names */
+#define SST_DRV_NAME "intel_sst_driver"
+#define SST_MRFLD_PCI_ID 0x119A
+#define SST_BYT_ACPI_ID        0x80860F28
+#define SST_CHV_ACPI_ID        0x808622A8
+
+#define SST_SUSPEND_DELAY 2000
+#define FW_CONTEXT_MEM (64*1024)
+#define SST_ICCM_BOUNDARY 4
+#define SST_CONFIG_SSP_SIGN 0x7ffe8001
+
+#define MRFLD_FW_VIRTUAL_BASE 0xC0000000
+#define MRFLD_FW_DDR_BASE_OFFSET 0x0
+#define MRFLD_FW_FEATURE_BASE_OFFSET 0x4
+#define MRFLD_FW_BSS_RESET_BIT 0
+
+extern const struct dev_pm_ops intel_sst_pm;
+enum sst_states {
+       SST_FW_LOADING = 1,
+       SST_FW_RUNNING,
+       SST_RESET,
+       SST_SHUTDOWN,
+};
+
+enum sst_algo_ops {
+       SST_SET_ALGO = 0,
+       SST_GET_ALGO = 1,
+};
+
+#define SST_BLOCK_TIMEOUT      1000
+
+#define FW_SIGNATURE_SIZE      4
+
+/* stream states */
+enum sst_stream_states {
+       STREAM_UN_INIT  = 0,    /* Freed/Not used stream */
+       STREAM_RUNNING  = 1,    /* Running */
+       STREAM_PAUSED   = 2,    /* Paused stream */
+       STREAM_DECODE   = 3,    /* stream is in decoding only state */
+       STREAM_INIT     = 4,    /* stream init, waiting for data */
+       STREAM_RESET    = 5,    /* force reset on recovery */
+};
+
+enum sst_ram_type {
+       SST_IRAM        = 1,
+       SST_DRAM        = 2,
+       SST_DDR = 5,
+       SST_CUSTOM_INFO = 7,    /* consists of FW binary information */
+};
+
+/* SST shim registers to structure mapping */
+union interrupt_reg {
+       struct {
+               u64 done_interrupt:1;
+               u64 busy_interrupt:1;
+               u64 rsvd:62;
+       } part;
+       u64 full;
+};
+
+union sst_pisr_reg {
+       struct {
+               u32 pssp0:1;
+               u32 pssp1:1;
+               u32 rsvd0:3;
+               u32 dmac:1;
+               u32 rsvd1:26;
+       } part;
+       u32 full;
+};
+
+union sst_pimr_reg {
+       struct {
+               u32 ssp0:1;
+               u32 ssp1:1;
+               u32 rsvd0:3;
+               u32 dmac:1;
+               u32 rsvd1:10;
+               u32 ssp0_sc:1;
+               u32 ssp1_sc:1;
+               u32 rsvd2:3;
+               u32 dmac_sc:1;
+               u32 rsvd3:10;
+       } part;
+       u32 full;
+};
+
+union config_status_reg_mrfld {
+       struct {
+               u64 lpe_reset:1;
+               u64 lpe_reset_vector:1;
+               u64 runstall:1;
+               u64 pwaitmode:1;
+               u64 clk_sel:3;
+               u64 rsvd2:1;
+               u64 sst_clk:3;
+               u64 xt_snoop:1;
+               u64 rsvd3:4;
+               u64 clk_sel1:6;
+               u64 clk_enable:3;
+               u64 rsvd4:6;
+               u64 slim0baseclk:1;
+               u64 rsvd:32;
+       } part;
+       u64 full;
+};
+
+union interrupt_reg_mrfld {
+       struct {
+               u64 done_interrupt:1;
+               u64 busy_interrupt:1;
+               u64 rsvd:62;
+       } part;
+       u64 full;
+};
+
+union sst_imr_reg_mrfld {
+       struct {
+               u64 done_interrupt:1;
+               u64 busy_interrupt:1;
+               u64 rsvd:62;
+       } part;
+       u64 full;
+};
+
+/**
+ * struct sst_block - This structure is used to block a user/fw data call to another
+ * fw/user call
+ *
+ * @condition: condition for blocking check
+ * @ret_code: ret code when block is released
+ * @data: data ptr
+ * @size: size of data
+ * @on: block condition
+ * @msg_id: msg_id = msgid in mfld/ctp, mrfld = NULL
+ * @drv_id: str_id in mfld/ctp, = drv_id in mrfld
+ * @node: list head node
+ */
+struct sst_block {
+       bool    condition;
+       int     ret_code;
+       void    *data;
+       u32     size;
+       bool    on;
+       u32     msg_id;
+       u32     drv_id;
+       struct list_head node;
+};
+
+/**
+ * struct stream_info - structure that holds the stream information
+ *
+ * @status : stream current state
+ * @prev : stream prev state
+ * @ops : stream operation pb/cp/drm...
+ * @bufs: stream buffer list
+ * @lock : stream mutex for protecting state
+ * @pcm_substream : PCM substream
+ * @period_elapsed : PCM period elapsed callback
+ * @sfreq : stream sampling freq
+ * @str_type : stream type
+ * @cumm_bytes : cummulative bytes decoded
+ * @str_type : stream type
+ * @src : stream source
+ */
+struct stream_info {
+       unsigned int            status;
+       unsigned int            prev;
+       unsigned int            ops;
+       struct mutex            lock;
+
+       void                    *pcm_substream;
+       void (*period_elapsed)(void *pcm_substream);
+
+       unsigned int            sfreq;
+       u32                     cumm_bytes;
+
+       void                    *compr_cb_param;
+       void (*compr_cb)(void *compr_cb_param);
+
+       void                    *drain_cb_param;
+       void (*drain_notify)(void *drain_cb_param);
+
+       unsigned int            num_ch;
+       unsigned int            pipe_id;
+       unsigned int            str_id;
+       unsigned int            task_id;
+};
+
+#define SST_FW_SIGN "$SST"
+#define SST_FW_LIB_SIGN "$LIB"
+
+/**
+ * struct sst_fw_header - FW file headers
+ *
+ * @signature : FW signature
+ * @file_size: size of fw image
+ * @modules : # of modules
+ * @file_format : version of header format
+ * @reserved : reserved fields
+ */
+struct sst_fw_header {
+       unsigned char signature[FW_SIGNATURE_SIZE];
+       u32 file_size;
+       u32 modules;
+       u32 file_format;
+       u32 reserved[4];
+};
+
+/**
+ * struct fw_module_header - module header in FW
+ *
+ * @signature: module signature
+ * @mod_size: size of module
+ * @blocks: block count
+ * @type: block type
+ * @entry_point: module netry point
+ */
+struct fw_module_header {
+       unsigned char signature[FW_SIGNATURE_SIZE];
+       u32 mod_size;
+       u32 blocks;
+       u32 type;
+       u32 entry_point;
+};
+
+/**
+ * struct fw_block_info - block header for FW
+ *
+ * @type: block ram type I/D
+ * @size: size of block
+ * @ram_offset: offset in ram
+ */
+struct fw_block_info {
+       enum sst_ram_type       type;
+       u32                     size;
+       u32                     ram_offset;
+       u32                     rsvd;
+};
+
+struct sst_runtime_param {
+       struct snd_sst_runtime_params param;
+};
+
+struct sst_sg_list {
+       struct scatterlist *src;
+       struct scatterlist *dst;
+       int list_len;
+       unsigned int sg_idx;
+};
+
+struct sst_memcpy_list {
+       struct list_head memcpylist;
+       void *dstn;
+       const void *src;
+       u32 size;
+       bool is_io;
+};
+
+/*Firmware Module Information*/
+enum sst_lib_dwnld_status {
+       SST_LIB_NOT_FOUND = 0,
+       SST_LIB_FOUND,
+       SST_LIB_DOWNLOADED,
+};
+
+struct sst_module_info {
+       const char *name; /*Library name*/
+       u32     id; /*Module ID*/
+       u32     entry_pt; /*Module entry point*/
+       u8      status; /*module status*/
+       u8      rsvd1;
+       u16     rsvd2;
+};
+
+/*
+ * Structure for managing the Library Region(1.5MB)
+ * in DDR in Merrifield
+ */
+struct sst_mem_mgr {
+       phys_addr_t current_base;
+       int avail;
+       unsigned int count;
+};
+
+struct sst_ipc_reg {
+       int ipcx;
+       int ipcd;
+};
+
+struct sst_shim_regs64 {
+       u64 csr;
+       u64 pisr;
+       u64 pimr;
+       u64 isrx;
+       u64 isrd;
+       u64 imrx;
+       u64 imrd;
+       u64 ipcx;
+       u64 ipcd;
+       u64 isrsc;
+       u64 isrlpesc;
+       u64 imrsc;
+       u64 imrlpesc;
+       u64 ipcsc;
+       u64 ipclpesc;
+       u64 clkctl;
+       u64 csr2;
+};
+
+/**
+ * struct intel_sst_drv - driver ops
+ *
+ * @sst_state : current sst device state
+ * @dev_id : device identifier, pci_id for pci devices and acpi_id for acpi
+ *          devices
+ * @shim : SST shim pointer
+ * @mailbox : SST mailbox pointer
+ * @iram : SST IRAM pointer
+ * @dram : SST DRAM pointer
+ * @pdata : SST info passed as a part of pci platform data
+ * @shim_phy_add : SST shim phy addr
+ * @shim_regs64: Struct to save shim registers
+ * @ipc_dispatch_list : ipc messages dispatched
+ * @rx_list : to copy the process_reply/process_msg from DSP
+ * @ipc_post_msg_wq : wq to post IPC messages context
+ * @mad_ops : MAD driver operations registered
+ * @mad_wq : MAD driver wq
+ * @post_msg_wq : wq to post IPC messages
+ * @streams : sst stream contexts
+ * @list_lock : sst driver list lock (deprecated)
+ * @ipc_spin_lock : spin lock to handle audio shim access and ipc queue
+ * @block_lock : spin lock to add block to block_list and assign pvt_id
+ * @rx_msg_lock : spin lock to handle the rx messages from the DSP
+ * @scard_ops : sst card ops
+ * @pci : sst pci device struture
+ * @dev : pointer to current device struct
+ * @sst_lock : sst device lock
+ * @pvt_id : sst private id
+ * @stream_cnt : total sst active stream count
+ * @pb_streams : total active pb streams
+ * @cp_streams : total active cp streams
+ * @audio_start : audio status
+ * @qos                : PM Qos struct
+ * firmware_name : Firmware / Library name
+ */
+struct intel_sst_drv {
+       int                     sst_state;
+       int                     irq_num;
+       unsigned int            dev_id;
+       void __iomem            *ddr;
+       void __iomem            *shim;
+       void __iomem            *mailbox;
+       void __iomem            *iram;
+       void __iomem            *dram;
+       unsigned int            mailbox_add;
+       unsigned int            iram_base;
+       unsigned int            dram_base;
+       unsigned int            shim_phy_add;
+       unsigned int            iram_end;
+       unsigned int            dram_end;
+       unsigned int            ddr_end;
+       unsigned int            ddr_base;
+       unsigned int            mailbox_recv_offset;
+       struct sst_shim_regs64  *shim_regs64;
+       struct list_head        block_list;
+       struct list_head        ipc_dispatch_list;
+       struct sst_platform_info *pdata;
+       struct list_head        rx_list;
+       struct work_struct      ipc_post_msg_wq;
+       wait_queue_head_t       wait_queue;
+       struct workqueue_struct *post_msg_wq;
+       unsigned int            tstamp;
+       /* str_id 0 is not used */
+       struct stream_info      streams[MAX_NUM_STREAMS+1];
+       spinlock_t              ipc_spin_lock;
+       spinlock_t              block_lock;
+       spinlock_t              rx_msg_lock;
+       struct pci_dev          *pci;
+       struct device           *dev;
+       volatile long unsigned          pvt_id;
+       struct mutex            sst_lock;
+       unsigned int            stream_cnt;
+       unsigned int            csr_value;
+       void                    *fw_in_mem;
+       struct sst_sg_list      fw_sg_list, library_list;
+       struct intel_sst_ops    *ops;
+       struct sst_info         info;
+       struct pm_qos_request   *qos;
+       unsigned int            use_dma;
+       unsigned int            use_lli;
+       atomic_t                fw_clear_context;
+       bool                    lib_dwnld_reqd;
+       struct list_head        memcpy_list;
+       struct sst_ipc_reg      ipc_reg;
+       struct sst_mem_mgr      lib_mem_mgr;
+       /*
+        * Holder for firmware name. Due to async call it needs to be
+        * persistent till worker thread gets called
+        */
+       char firmware_name[20];
+};
+
+/* misc definitions */
+#define FW_DWNL_ID 0x01
+
+struct intel_sst_ops {
+       irqreturn_t (*interrupt)(int, void *);
+       irqreturn_t (*irq_thread)(int, void *);
+       void (*clear_interrupt)(struct intel_sst_drv *ctx);
+       int (*start)(struct intel_sst_drv *ctx);
+       int (*reset)(struct intel_sst_drv *ctx);
+       void (*process_reply)(struct intel_sst_drv *ctx, struct ipc_post *msg);
+       int (*post_message)(struct intel_sst_drv *ctx,
+                       struct ipc_post *msg, bool sync);
+       void (*process_message)(struct ipc_post *msg);
+       void (*set_bypass)(bool set);
+       int (*save_dsp_context)(struct intel_sst_drv *sst);
+       void (*restore_dsp_context)(void);
+       int (*alloc_stream)(struct intel_sst_drv *ctx, void *params);
+       void (*post_download)(struct intel_sst_drv *sst);
+};
+
+int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id);
+int sst_send_byte_stream_mrfld(struct intel_sst_drv *ctx,
+                       struct snd_sst_bytes_v2 *sbytes);
+int sst_set_stream_param(int str_id, struct snd_sst_params *str_param);
+int sst_set_metadata(int str_id, char *params);
+int sst_get_stream(struct intel_sst_drv *sst_drv_ctx,
+               struct snd_sst_params *str_param);
+int sst_get_stream_allocated(struct intel_sst_drv *ctx,
+               struct snd_sst_params *str_param,
+               struct snd_sst_lib_download **lib_dnld);
+int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx,
+               int str_id, bool partial_drain);
+int sst_post_message_mrfld(struct intel_sst_drv *ctx,
+               struct ipc_post *msg, bool sync);
+void sst_process_reply_mrfld(struct intel_sst_drv *ctx, struct ipc_post *msg);
+int sst_start_mrfld(struct intel_sst_drv *ctx);
+int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *ctx);
+void intel_sst_clear_intr_mrfld(struct intel_sst_drv *ctx);
+
+int sst_load_fw(struct intel_sst_drv *ctx);
+int sst_load_library(struct snd_sst_lib_download *lib, u8 ops);
+void sst_post_download_mrfld(struct intel_sst_drv *ctx);
+int sst_get_block_stream(struct intel_sst_drv *sst_drv_ctx);
+void sst_memcpy_free_resources(struct intel_sst_drv *ctx);
+
+int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx,
+                               struct sst_block *block);
+int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx,
+                       struct sst_block *block);
+int sst_create_ipc_msg(struct ipc_post **arg, bool large);
+int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id);
+void sst_clean_stream(struct stream_info *stream);
+int intel_sst_register_compress(struct intel_sst_drv *sst);
+int intel_sst_remove_compress(struct intel_sst_drv *sst);
+void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id);
+int sst_send_sync_msg(int ipc, int str_id);
+int sst_get_num_channel(struct snd_sst_params *str_param);
+int sst_get_sfreq(struct snd_sst_params *str_param);
+int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params);
+void sst_restore_fw_context(void);
+struct sst_block *sst_create_block(struct intel_sst_drv *ctx,
+                               u32 msg_id, u32 drv_id);
+int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large,
+               struct intel_sst_drv *sst_drv_ctx, struct sst_block **block,
+               u32 msg_id, u32 drv_id);
+int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed);
+int sst_wake_up_block(struct intel_sst_drv *ctx, int result,
+               u32 drv_id, u32 ipc, void *data, u32 size);
+int sst_request_firmware_async(struct intel_sst_drv *ctx);
+int sst_driver_ops(struct intel_sst_drv *sst);
+struct sst_platform_info *sst_get_acpi_driver_data(const char *hid);
+void sst_firmware_load_cb(const struct firmware *fw, void *context);
+int sst_prepare_and_post_msg(struct intel_sst_drv *sst,
+               int task_id, int ipc_msg, int cmd_id, int pipe_id,
+               size_t mbox_data_len, const void *mbox_data, void **data,
+               bool large, bool fill_dsp, bool sync, bool response);
+
+void sst_process_pending_msg(struct work_struct *work);
+int sst_assign_pvt_id(struct intel_sst_drv *sst_drv_ctx);
+void sst_init_stream(struct stream_info *stream,
+               int codec, int sst_id, int ops, u8 slot);
+int sst_validate_strid(struct intel_sst_drv *sst_drv_ctx, int str_id);
+struct stream_info *get_stream_info(struct intel_sst_drv *sst_drv_ctx,
+               int str_id);
+int get_stream_id_mrfld(struct intel_sst_drv *sst_drv_ctx,
+               u32 pipe_id);
+u32 relocate_imr_addr_mrfld(u32 base_addr);
+void sst_add_to_dispatch_list_and_post(struct intel_sst_drv *sst,
+                                       struct ipc_post *msg);
+int sst_pm_runtime_put(struct intel_sst_drv *sst_drv);
+int sst_shim_write(void __iomem *addr, int offset, int value);
+u32 sst_shim_read(void __iomem *addr, int offset);
+u64 sst_reg_read64(void __iomem *addr, int offset);
+int sst_shim_write64(void __iomem *addr, int offset, u64 value);
+u64 sst_shim_read64(void __iomem *addr, int offset);
+void sst_set_fw_state_locked(
+               struct intel_sst_drv *sst_drv_ctx, int sst_state);
+void sst_fill_header_mrfld(union ipc_header_mrfld *header,
+                               int msg, int task_id, int large, int drv_id);
+void sst_fill_header_dsp(struct ipc_dsp_hdr *dsp, int msg,
+                                       int pipe_id, int len);
+
+int sst_register(struct device *);
+int sst_unregister(struct device *);
+
+int sst_alloc_drv_context(struct intel_sst_drv **ctx,
+               struct device *dev, unsigned int dev_id);
+int sst_context_init(struct intel_sst_drv *ctx);
+void sst_context_cleanup(struct intel_sst_drv *ctx);
+void sst_configure_runtime_pm(struct intel_sst_drv *ctx);
+#endif
diff --git a/sound/soc/intel/sst/sst_acpi.c b/sound/soc/intel/sst/sst_acpi.c
new file mode 100644 (file)
index 0000000..31124aa
--- /dev/null
@@ -0,0 +1,383 @@
+/*
+ * sst_acpi.c - SST (LPE) driver init file for ACPI enumeration.
+ *
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ *  Authors:   Ramesh Babu K V <Ramesh.Babu@intel.com>
+ *  Authors:   Omair Mohammed Abdullah <omair.m.abdullah@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/acpi.h>
+#include <asm/platform_sst_audio.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <acpi/acbuffer.h>
+#include <acpi/platform/acenv.h>
+#include <acpi/platform/aclinux.h>
+#include <acpi/actypes.h>
+#include <acpi/acpi_bus.h>
+#include "../sst-mfld-platform.h"
+#include "../sst-dsp.h"
+#include "sst.h"
+
+struct sst_machines {
+       char codec_id[32];
+       char board[32];
+       char machine[32];
+       void (*machine_quirk)(void);
+       char firmware[32];
+       struct sst_platform_info *pdata;
+
+};
+
+/* LPE viewpoint addresses */
+#define SST_BYT_IRAM_PHY_START 0xff2c0000
+#define SST_BYT_IRAM_PHY_END   0xff2d4000
+#define SST_BYT_DRAM_PHY_START 0xff300000
+#define SST_BYT_DRAM_PHY_END   0xff320000
+#define SST_BYT_IMR_VIRT_START 0xc0000000 /* virtual addr in LPE */
+#define SST_BYT_IMR_VIRT_END   0xc01fffff
+#define SST_BYT_SHIM_PHY_ADDR  0xff340000
+#define SST_BYT_MBOX_PHY_ADDR  0xff344000
+#define SST_BYT_DMA0_PHY_ADDR  0xff298000
+#define SST_BYT_DMA1_PHY_ADDR  0xff29c000
+#define SST_BYT_SSP0_PHY_ADDR  0xff2a0000
+#define SST_BYT_SSP2_PHY_ADDR  0xff2a2000
+
+#define BYT_FW_MOD_TABLE_OFFSET        0x80000
+#define BYT_FW_MOD_TABLE_SIZE  0x100
+#define BYT_FW_MOD_OFFSET      (BYT_FW_MOD_TABLE_OFFSET + BYT_FW_MOD_TABLE_SIZE)
+
+static const struct sst_info byt_fwparse_info = {
+       .use_elf        = false,
+       .max_streams    = 25,
+       .iram_start     = SST_BYT_IRAM_PHY_START,
+       .iram_end       = SST_BYT_IRAM_PHY_END,
+       .iram_use       = true,
+       .dram_start     = SST_BYT_DRAM_PHY_START,
+       .dram_end       = SST_BYT_DRAM_PHY_END,
+       .dram_use       = true,
+       .imr_start      = SST_BYT_IMR_VIRT_START,
+       .imr_end        = SST_BYT_IMR_VIRT_END,
+       .imr_use        = true,
+       .mailbox_start  = SST_BYT_MBOX_PHY_ADDR,
+       .num_probes     = 0,
+       .lpe_viewpt_rqd  = true,
+};
+
+static const struct sst_ipc_info byt_ipc_info = {
+       .ipc_offset = 0,
+       .mbox_recv_off = 0x400,
+};
+
+static const struct sst_lib_dnld_info  byt_lib_dnld_info = {
+       .mod_base           = SST_BYT_IMR_VIRT_START,
+       .mod_end            = SST_BYT_IMR_VIRT_END,
+       .mod_table_offset   = BYT_FW_MOD_TABLE_OFFSET,
+       .mod_table_size     = BYT_FW_MOD_TABLE_SIZE,
+       .mod_ddr_dnld       = false,
+};
+
+static const struct sst_res_info byt_rvp_res_info = {
+       .shim_offset = 0x140000,
+       .shim_size = 0x000100,
+       .shim_phy_addr = SST_BYT_SHIM_PHY_ADDR,
+       .ssp0_offset = 0xa0000,
+       .ssp0_size = 0x1000,
+       .dma0_offset = 0x98000,
+       .dma0_size = 0x4000,
+       .dma1_offset = 0x9c000,
+       .dma1_size = 0x4000,
+       .iram_offset = 0x0c0000,
+       .iram_size = 0x14000,
+       .dram_offset = 0x100000,
+       .dram_size = 0x28000,
+       .mbox_offset = 0x144000,
+       .mbox_size = 0x1000,
+       .acpi_lpe_res_index = 0,
+       .acpi_ddr_index = 2,
+       .acpi_ipc_irq_index = 5,
+};
+
+static struct sst_platform_info byt_rvp_platform_data = {
+       .probe_data = &byt_fwparse_info,
+       .ipc_info = &byt_ipc_info,
+       .lib_info = &byt_lib_dnld_info,
+       .res_info = &byt_rvp_res_info,
+       .platform = "sst-mfld-platform",
+};
+
+/* Cherryview (Cherrytrail and Braswell) uses same mrfld dpcm fw as Baytrail,
+ * so pdata is same as Baytrail.
+ */
+static struct sst_platform_info chv_platform_data = {
+       .probe_data = &byt_fwparse_info,
+       .ipc_info = &byt_ipc_info,
+       .lib_info = &byt_lib_dnld_info,
+       .res_info = &byt_rvp_res_info,
+       .platform = "sst-mfld-platform",
+};
+
+static int sst_platform_get_resources(struct intel_sst_drv *ctx)
+{
+       struct resource *rsrc;
+       struct platform_device *pdev = to_platform_device(ctx->dev);
+
+       /* All ACPI resource request here */
+       /* Get Shim addr */
+       rsrc = platform_get_resource(pdev, IORESOURCE_MEM,
+                                       ctx->pdata->res_info->acpi_lpe_res_index);
+       if (!rsrc) {
+               dev_err(ctx->dev, "Invalid SHIM base from IFWI");
+               return -EIO;
+       }
+       dev_info(ctx->dev, "LPE base: %#x size:%#x", (unsigned int) rsrc->start,
+                                       (unsigned int)resource_size(rsrc));
+
+       ctx->iram_base = rsrc->start + ctx->pdata->res_info->iram_offset;
+       ctx->iram_end =  ctx->iram_base + ctx->pdata->res_info->iram_size - 1;
+       dev_info(ctx->dev, "IRAM base: %#x", ctx->iram_base);
+       ctx->iram = devm_ioremap_nocache(ctx->dev, ctx->iram_base,
+                                        ctx->pdata->res_info->iram_size);
+       if (!ctx->iram) {
+               dev_err(ctx->dev, "unable to map IRAM");
+               return -EIO;
+       }
+
+       ctx->dram_base = rsrc->start + ctx->pdata->res_info->dram_offset;
+       ctx->dram_end = ctx->dram_base + ctx->pdata->res_info->dram_size - 1;
+       dev_info(ctx->dev, "DRAM base: %#x", ctx->dram_base);
+       ctx->dram = devm_ioremap_nocache(ctx->dev, ctx->dram_base,
+                                        ctx->pdata->res_info->dram_size);
+       if (!ctx->dram) {
+               dev_err(ctx->dev, "unable to map DRAM");
+               return -EIO;
+       }
+
+       ctx->shim_phy_add = rsrc->start + ctx->pdata->res_info->shim_offset;
+       dev_info(ctx->dev, "SHIM base: %#x", ctx->shim_phy_add);
+       ctx->shim = devm_ioremap_nocache(ctx->dev, ctx->shim_phy_add,
+                                       ctx->pdata->res_info->shim_size);
+       if (!ctx->shim) {
+               dev_err(ctx->dev, "unable to map SHIM");
+               return -EIO;
+       }
+
+       /* reassign physical address to LPE viewpoint address */
+       ctx->shim_phy_add = ctx->pdata->res_info->shim_phy_addr;
+
+       /* Get mailbox addr */
+       ctx->mailbox_add = rsrc->start + ctx->pdata->res_info->mbox_offset;
+       dev_info(ctx->dev, "Mailbox base: %#x", ctx->mailbox_add);
+       ctx->mailbox = devm_ioremap_nocache(ctx->dev, ctx->mailbox_add,
+                                           ctx->pdata->res_info->mbox_size);
+       if (!ctx->mailbox) {
+               dev_err(ctx->dev, "unable to map mailbox");
+               return -EIO;
+       }
+
+       /* reassign physical address to LPE viewpoint address */
+       ctx->mailbox_add = ctx->info.mailbox_start;
+
+       rsrc = platform_get_resource(pdev, IORESOURCE_MEM,
+                                       ctx->pdata->res_info->acpi_ddr_index);
+       if (!rsrc) {
+               dev_err(ctx->dev, "Invalid DDR base from IFWI");
+               return -EIO;
+       }
+       ctx->ddr_base = rsrc->start;
+       ctx->ddr_end = rsrc->end;
+       dev_info(ctx->dev, "DDR base: %#x", ctx->ddr_base);
+       ctx->ddr = devm_ioremap_nocache(ctx->dev, ctx->ddr_base,
+                                       resource_size(rsrc));
+       if (!ctx->ddr) {
+               dev_err(ctx->dev, "unable to map DDR");
+               return -EIO;
+       }
+
+       /* Find the IRQ */
+       ctx->irq_num = platform_get_irq(pdev,
+                               ctx->pdata->res_info->acpi_ipc_irq_index);
+       return 0;
+}
+
+static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level,
+                                      void *context, void **ret)
+{
+       *(bool *)context = true;
+       return AE_OK;
+}
+
+static struct sst_machines *sst_acpi_find_machine(
+       struct sst_machines *machines)
+{
+       struct sst_machines *mach;
+       bool found = false;
+
+       for (mach = machines; mach->codec_id; mach++)
+               if (ACPI_SUCCESS(acpi_get_devices(mach->codec_id,
+                                                 sst_acpi_mach_match,
+                                                 &found, NULL)) && found)
+                       return mach;
+
+       return NULL;
+}
+
+int sst_acpi_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       int ret = 0;
+       struct intel_sst_drv *ctx;
+       const struct acpi_device_id *id;
+       struct sst_machines *mach;
+       struct platform_device *mdev;
+       struct platform_device *plat_dev;
+       unsigned int dev_id;
+
+       id = acpi_match_device(dev->driver->acpi_match_table, dev);
+       if (!id)
+               return -ENODEV;
+       dev_dbg(dev, "for %s", id->id);
+
+       mach = (struct sst_machines *)id->driver_data;
+       mach = sst_acpi_find_machine(mach);
+       if (mach == NULL) {
+               dev_err(dev, "No matching machine driver found\n");
+               return -ENODEV;
+       }
+
+       ret = kstrtouint(id->id, 16, &dev_id);
+       if (ret < 0) {
+               dev_err(dev, "Unique device id conversion error: %d\n", ret);
+               return ret;
+       }
+
+       dev_dbg(dev, "ACPI device id: %x\n", dev_id);
+
+       plat_dev = platform_device_register_data(dev, mach->pdata->platform, -1, NULL, 0);
+       if (plat_dev == NULL) {
+               dev_err(dev, "Failed to create machine device: %s\n", mach->pdata->platform);
+               return -ENODEV;
+       }
+
+       /* Create platform device for sst machine driver */
+       mdev = platform_device_register_data(dev, mach->machine, -1, NULL, 0);
+       if (mdev == NULL) {
+               dev_err(dev, "Failed to create machine device: %s\n", mach->machine);
+               return -ENODEV;
+       }
+
+       ret = sst_alloc_drv_context(&ctx, dev, dev_id);
+       if (ret < 0)
+               return ret;
+
+       /* Fill sst platform data */
+       ctx->pdata = mach->pdata;
+       strcpy(ctx->firmware_name, mach->firmware);
+
+       ret = sst_platform_get_resources(ctx);
+       if (ret)
+               return ret;
+
+       ret = sst_context_init(ctx);
+       if (ret < 0)
+               return ret;
+
+       /* need to save shim registers in BYT */
+       ctx->shim_regs64 = devm_kzalloc(ctx->dev, sizeof(*ctx->shim_regs64),
+                                       GFP_KERNEL);
+       if (!ctx->shim_regs64) {
+               return -ENOMEM;
+               goto do_sst_cleanup;
+       }
+
+       sst_configure_runtime_pm(ctx);
+       platform_set_drvdata(pdev, ctx);
+       return ret;
+
+do_sst_cleanup:
+       sst_context_cleanup(ctx);
+       platform_set_drvdata(pdev, NULL);
+       dev_err(ctx->dev, "failed with %d\n", ret);
+       return ret;
+}
+
+/**
+* intel_sst_remove - remove function
+*
+* @pdev:       platform device structure
+*
+* This function is called by OS when a device is unloaded
+* This frees the interrupt etc
+*/
+int sst_acpi_remove(struct platform_device *pdev)
+{
+       struct intel_sst_drv *ctx;
+
+       ctx = platform_get_drvdata(pdev);
+       sst_context_cleanup(ctx);
+       platform_set_drvdata(pdev, NULL);
+       return 0;
+}
+
+static struct sst_machines sst_acpi_bytcr[] = {
+       {"10EC5640", "T100", "bytt100_rt5640", NULL, "fw_sst_0f28.bin",
+                                               &byt_rvp_platform_data },
+       {},
+};
+
+/* Cherryview-based platforms: CherryTrail and Braswell */
+static struct sst_machines sst_acpi_chv[] = {
+       {"10EC5670", "cht-bsw", "cht-bsw-rt5672", NULL, "fw_sst_22a8.bin",
+                                               &chv_platform_data },
+       {},
+};
+
+static const struct acpi_device_id sst_acpi_ids[] = {
+       { "80860F28", (unsigned long)&sst_acpi_bytcr},
+       { "808622A8", (unsigned long) &sst_acpi_chv},
+       { },
+};
+
+MODULE_DEVICE_TABLE(acpi, sst_acpi_ids);
+
+static struct platform_driver sst_acpi_driver = {
+       .driver = {
+               .name                   = "intel_sst_acpi",
+               .owner                  = THIS_MODULE,
+               .acpi_match_table       = ACPI_PTR(sst_acpi_ids),
+               .pm                     = &intel_sst_pm,
+       },
+       .probe  = sst_acpi_probe,
+       .remove = sst_acpi_remove,
+};
+
+module_platform_driver(sst_acpi_driver);
+
+MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine ACPI Driver");
+MODULE_AUTHOR("Ramesh Babu K V");
+MODULE_AUTHOR("Omair Mohammed Abdullah");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("sst");
diff --git a/sound/soc/intel/sst/sst_drv_interface.c b/sound/soc/intel/sst/sst_drv_interface.c
new file mode 100644 (file)
index 0000000..5f75ef3
--- /dev/null
@@ -0,0 +1,686 @@
+/*
+ *  sst_drv_interface.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14 Intel Corp
+ *  Authors:   Vinod Koul <vinod.koul@intel.com>
+ *             Harsha Priya <priya.harsha@intel.com>
+ *             Dharageswari R <dharageswari.r@intel.com)
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/math64.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+
+
+#define NUM_CODEC 2
+#define MIN_FRAGMENT 2
+#define MAX_FRAGMENT 4
+#define MIN_FRAGMENT_SIZE (50 * 1024)
+#define MAX_FRAGMENT_SIZE (1024 * 1024)
+#define SST_GET_BYTES_PER_SAMPLE(pcm_wd_sz)  (((pcm_wd_sz + 15) >> 4) << 1)
+
+int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id)
+{
+       struct stream_info *stream;
+       int ret = 0;
+
+       stream = get_stream_info(ctx, str_id);
+       if (stream) {
+               /* str_id is valid, so stream is alloacted */
+               ret = sst_free_stream(ctx, str_id);
+               if (ret)
+                       sst_clean_stream(&ctx->streams[str_id]);
+               return ret;
+       } else {
+               dev_err(ctx->dev, "we tried to free stream context %d which was freed!!!\n", str_id);
+       }
+       return ret;
+}
+
+int sst_get_stream_allocated(struct intel_sst_drv *ctx,
+       struct snd_sst_params *str_param,
+       struct snd_sst_lib_download **lib_dnld)
+{
+       int retval;
+
+       retval = ctx->ops->alloc_stream(ctx, str_param);
+       if (retval > 0)
+               dev_dbg(ctx->dev, "Stream allocated %d\n", retval);
+       return retval;
+
+}
+
+/*
+ * sst_get_sfreq - this function returns the frequency of the stream
+ *
+ * @str_param : stream params
+ */
+int sst_get_sfreq(struct snd_sst_params *str_param)
+{
+       switch (str_param->codec) {
+       case SST_CODEC_TYPE_PCM:
+               return str_param->sparams.uc.pcm_params.sfreq;
+       case SST_CODEC_TYPE_AAC:
+               return str_param->sparams.uc.aac_params.externalsr;
+       case SST_CODEC_TYPE_MP3:
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+/*
+ * sst_get_num_channel - get number of channels for the stream
+ *
+ * @str_param : stream params
+ */
+int sst_get_num_channel(struct snd_sst_params *str_param)
+{
+       switch (str_param->codec) {
+       case SST_CODEC_TYPE_PCM:
+               return str_param->sparams.uc.pcm_params.num_chan;
+       case SST_CODEC_TYPE_MP3:
+               return str_param->sparams.uc.mp3_params.num_chan;
+       case SST_CODEC_TYPE_AAC:
+               return str_param->sparams.uc.aac_params.num_chan;
+       default:
+               return -EINVAL;
+       }
+}
+
+/*
+ * sst_get_stream - this function prepares for stream allocation
+ *
+ * @str_param : stream param
+ */
+int sst_get_stream(struct intel_sst_drv *ctx,
+                       struct snd_sst_params *str_param)
+{
+       int retval;
+       struct stream_info *str_info;
+
+       /* stream is not allocated, we are allocating */
+       retval = ctx->ops->alloc_stream(ctx, str_param);
+       if (retval <= 0) {
+               return -EIO;
+       }
+       /* store sampling freq */
+       str_info = &ctx->streams[retval];
+       str_info->sfreq = sst_get_sfreq(str_param);
+
+       return retval;
+}
+
+static int sst_power_control(struct device *dev, bool state)
+{
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       dev_dbg(ctx->dev, "state:%d", state);
+       if (state == true)
+               return pm_runtime_get_sync(dev);
+       else
+               return sst_pm_runtime_put(ctx);
+}
+
+/*
+ * sst_open_pcm_stream - Open PCM interface
+ *
+ * @str_param: parameters of pcm stream
+ *
+ * This function is called by MID sound card driver to open
+ * a new pcm interface
+ */
+static int sst_open_pcm_stream(struct device *dev,
+               struct snd_sst_params *str_param)
+{
+       int retval;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       if (!str_param)
+               return -EINVAL;
+
+       retval = sst_get_stream(ctx, str_param);
+       if (retval > 0)
+               ctx->stream_cnt++;
+       else
+               dev_err(ctx->dev, "sst_get_stream returned err %d\n", retval);
+
+       return retval;
+}
+
+static int sst_cdev_open(struct device *dev,
+               struct snd_sst_params *str_params, struct sst_compress_cb *cb)
+{
+       int str_id, retval;
+       struct stream_info *stream;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       retval = pm_runtime_get_sync(ctx->dev);
+       if (retval < 0)
+               return retval;
+
+       str_id = sst_get_stream(ctx, str_params);
+       if (str_id > 0) {
+               dev_dbg(dev, "stream allocated in sst_cdev_open %d\n", str_id);
+               stream = &ctx->streams[str_id];
+               stream->compr_cb = cb->compr_cb;
+               stream->compr_cb_param = cb->param;
+               stream->drain_notify = cb->drain_notify;
+               stream->drain_cb_param = cb->drain_cb_param;
+       } else {
+               dev_err(dev, "stream encountered error during alloc %d\n", str_id);
+               str_id = -EINVAL;
+               sst_pm_runtime_put(ctx);
+       }
+       return str_id;
+}
+
+static int sst_cdev_close(struct device *dev, unsigned int str_id)
+{
+       int retval;
+       struct stream_info *stream;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       stream = get_stream_info(ctx, str_id);
+       if (!stream) {
+               dev_err(dev, "stream info is NULL for str %d!!!\n", str_id);
+               return -EINVAL;
+       }
+
+       if (stream->status == STREAM_RESET) {
+               dev_dbg(dev, "stream in reset state...\n");
+               stream->status = STREAM_UN_INIT;
+
+               retval = 0;
+               goto put;
+       }
+
+       retval = sst_free_stream(ctx, str_id);
+put:
+       stream->compr_cb_param = NULL;
+       stream->compr_cb = NULL;
+
+       if (retval)
+               dev_err(dev, "free stream returned err %d\n", retval);
+
+       dev_dbg(dev, "End\n");
+       return retval;
+
+}
+
+static int sst_cdev_ack(struct device *dev, unsigned int str_id,
+               unsigned long bytes)
+{
+       struct stream_info *stream;
+       struct snd_sst_tstamp fw_tstamp = {0,};
+       int offset;
+       void __iomem *addr;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       stream = get_stream_info(ctx, str_id);
+       if (!stream)
+               return -EINVAL;
+
+       /* update bytes sent */
+       stream->cumm_bytes += bytes;
+       dev_dbg(dev, "bytes copied %d inc by %ld\n", stream->cumm_bytes, bytes);
+
+       memcpy_fromio(&fw_tstamp,
+               ((void *)(ctx->mailbox + ctx->tstamp)
+               +(str_id * sizeof(fw_tstamp))),
+               sizeof(fw_tstamp));
+
+       fw_tstamp.bytes_copied = stream->cumm_bytes;
+       dev_dbg(dev, "bytes sent to fw %llu inc by %ld\n",
+                       fw_tstamp.bytes_copied, bytes);
+
+       addr =  ((void *)(ctx->mailbox + ctx->tstamp)) +
+                       (str_id * sizeof(fw_tstamp));
+       offset =  offsetof(struct snd_sst_tstamp, bytes_copied);
+       sst_shim_write(addr, offset, fw_tstamp.bytes_copied);
+       return 0;
+}
+
+static int sst_cdev_set_metadata(struct device *dev,
+               unsigned int str_id, struct snd_compr_metadata *metadata)
+{
+       int retval = 0;
+       struct stream_info *str_info;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "set metadata for stream %d\n", str_id);
+
+       str_info = get_stream_info(ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+
+       dev_dbg(dev, "pipe id = %d\n", str_info->pipe_id);
+       retval = sst_prepare_and_post_msg(ctx, str_info->task_id, IPC_CMD,
+                       IPC_IA_SET_STREAM_PARAMS_MRFLD, str_info->pipe_id,
+                       sizeof(*metadata), metadata, NULL,
+                       true, true, true, false);
+
+       return retval;
+}
+
+static int sst_cdev_stream_pause(struct device *dev, unsigned int str_id)
+{
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       return sst_pause_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_pause_release(struct device *dev,
+               unsigned int str_id)
+{
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       return sst_resume_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_start(struct device *dev, unsigned int str_id)
+{
+       struct stream_info *str_info;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       str_info = get_stream_info(ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       str_info->prev = str_info->status;
+       str_info->status = STREAM_RUNNING;
+       return sst_start_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_drop(struct device *dev, unsigned int str_id)
+{
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       return sst_drop_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_drain(struct device *dev, unsigned int str_id)
+{
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       return sst_drain_stream(ctx, str_id, false);
+}
+
+static int sst_cdev_stream_partial_drain(struct device *dev,
+               unsigned int str_id)
+{
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       return sst_drain_stream(ctx, str_id, true);
+}
+
+static int sst_cdev_tstamp(struct device *dev, unsigned int str_id,
+               struct snd_compr_tstamp *tstamp)
+{
+       struct snd_sst_tstamp fw_tstamp = {0,};
+       struct stream_info *stream;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       memcpy_fromio(&fw_tstamp,
+               ((void *)(ctx->mailbox + ctx->tstamp)
+               +(str_id * sizeof(fw_tstamp))),
+               sizeof(fw_tstamp));
+
+       stream = get_stream_info(ctx, str_id);
+       if (!stream)
+               return -EINVAL;
+       dev_dbg(dev, "rb_counter %llu in bytes\n", fw_tstamp.ring_buffer_counter);
+
+       tstamp->copied_total = fw_tstamp.ring_buffer_counter;
+       tstamp->pcm_frames = fw_tstamp.frames_decoded;
+       tstamp->pcm_io_frames = div_u64(fw_tstamp.hardware_counter,
+                       (u64)((stream->num_ch) * SST_GET_BYTES_PER_SAMPLE(24)));
+       tstamp->sampling_rate = fw_tstamp.sampling_frequency;
+
+       dev_dbg(dev, "PCM  = %u\n", tstamp->pcm_io_frames);
+       dev_dbg(dev, "Ptr Query on strid = %d  copied_total %d, decodec %d\n",
+               str_id, tstamp->copied_total, tstamp->pcm_frames);
+       dev_dbg(dev, "rendered %d\n", tstamp->pcm_io_frames);
+
+       return 0;
+}
+
+static int sst_cdev_caps(struct snd_compr_caps *caps)
+{
+       caps->num_codecs = NUM_CODEC;
+       caps->min_fragment_size = MIN_FRAGMENT_SIZE;  /* 50KB */
+       caps->max_fragment_size = MAX_FRAGMENT_SIZE;  /* 1024KB */
+       caps->min_fragments = MIN_FRAGMENT;
+       caps->max_fragments = MAX_FRAGMENT;
+       caps->codecs[0] = SND_AUDIOCODEC_MP3;
+       caps->codecs[1] = SND_AUDIOCODEC_AAC;
+       return 0;
+}
+
+static struct snd_compr_codec_caps caps_mp3 = {
+       .num_descriptors = 1,
+       .descriptor[0].max_ch = 2,
+       .descriptor[0].sample_rates[0] = 48000,
+       .descriptor[0].sample_rates[1] = 44100,
+       .descriptor[0].sample_rates[2] = 32000,
+       .descriptor[0].sample_rates[3] = 16000,
+       .descriptor[0].sample_rates[4] = 8000,
+       .descriptor[0].num_sample_rates = 5,
+       .descriptor[0].bit_rate[0] = 320,
+       .descriptor[0].bit_rate[1] = 192,
+       .descriptor[0].num_bitrates = 2,
+       .descriptor[0].profiles = 0,
+       .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO,
+       .descriptor[0].formats = 0,
+};
+
+static struct snd_compr_codec_caps caps_aac = {
+       .num_descriptors = 2,
+       .descriptor[1].max_ch = 2,
+       .descriptor[0].sample_rates[0] = 48000,
+       .descriptor[0].sample_rates[1] = 44100,
+       .descriptor[0].sample_rates[2] = 32000,
+       .descriptor[0].sample_rates[3] = 16000,
+       .descriptor[0].sample_rates[4] = 8000,
+       .descriptor[0].num_sample_rates = 5,
+       .descriptor[1].bit_rate[0] = 320,
+       .descriptor[1].bit_rate[1] = 192,
+       .descriptor[1].num_bitrates = 2,
+       .descriptor[1].profiles = 0,
+       .descriptor[1].modes = 0,
+       .descriptor[1].formats =
+                       (SND_AUDIOSTREAMFORMAT_MP4ADTS |
+                               SND_AUDIOSTREAMFORMAT_RAW),
+};
+
+static int sst_cdev_codec_caps(struct snd_compr_codec_caps *codec)
+{
+       if (codec->codec == SND_AUDIOCODEC_MP3)
+               *codec = caps_mp3;
+       else if (codec->codec == SND_AUDIOCODEC_AAC)
+               *codec = caps_aac;
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id)
+{
+       struct stream_info *stream;
+
+       dev_dbg(ctx->dev, "fragment elapsed from firmware for str_id %d\n",
+                       str_id);
+       stream = &ctx->streams[str_id];
+       if (stream->compr_cb)
+               stream->compr_cb(stream->compr_cb_param);
+}
+
+/*
+ * sst_close_pcm_stream - Close PCM interface
+ *
+ * @str_id: stream id to be closed
+ *
+ * This function is called by MID sound card driver to close
+ * an existing pcm interface
+ */
+static int sst_close_pcm_stream(struct device *dev, unsigned int str_id)
+{
+       struct stream_info *stream;
+       int retval = 0;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       stream = get_stream_info(ctx, str_id);
+       if (!stream) {
+               dev_err(ctx->dev, "stream info is NULL for str %d!!!\n", str_id);
+               return -EINVAL;
+       }
+
+       if (stream->status == STREAM_RESET) {
+               /* silently fail here as we have cleaned the stream earlier */
+               dev_dbg(ctx->dev, "stream in reset state...\n");
+
+               retval = 0;
+               goto put;
+       }
+
+       retval = free_stream_context(ctx, str_id);
+put:
+       stream->pcm_substream = NULL;
+       stream->status = STREAM_UN_INIT;
+       stream->period_elapsed = NULL;
+       ctx->stream_cnt--;
+
+       if (retval)
+               dev_err(ctx->dev, "free stream returned err %d\n", retval);
+
+       dev_dbg(ctx->dev, "Exit\n");
+       return 0;
+}
+
+static inline int sst_calc_tstamp(struct intel_sst_drv *ctx,
+               struct pcm_stream_info *info,
+               struct snd_pcm_substream *substream,
+               struct snd_sst_tstamp *fw_tstamp)
+{
+       size_t delay_bytes, delay_frames;
+       size_t buffer_sz;
+       u32 pointer_bytes, pointer_samples;
+
+       dev_dbg(ctx->dev, "mrfld ring_buffer_counter %llu in bytes\n",
+                       fw_tstamp->ring_buffer_counter);
+       dev_dbg(ctx->dev, "mrfld hardware_counter %llu in bytes\n",
+                        fw_tstamp->hardware_counter);
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               delay_bytes = (size_t) (fw_tstamp->ring_buffer_counter -
+                                       fw_tstamp->hardware_counter);
+       else
+               delay_bytes = (size_t) (fw_tstamp->hardware_counter -
+                                       fw_tstamp->ring_buffer_counter);
+       delay_frames = bytes_to_frames(substream->runtime, delay_bytes);
+       buffer_sz = snd_pcm_lib_buffer_bytes(substream);
+       div_u64_rem(fw_tstamp->ring_buffer_counter, buffer_sz, &pointer_bytes);
+       pointer_samples = bytes_to_samples(substream->runtime, pointer_bytes);
+
+       dev_dbg(ctx->dev, "pcm delay %zu in bytes\n", delay_bytes);
+
+       info->buffer_ptr = pointer_samples / substream->runtime->channels;
+
+       info->pcm_delay = delay_frames / substream->runtime->channels;
+       dev_dbg(ctx->dev, "buffer ptr %llu pcm_delay rep: %llu\n",
+                       info->buffer_ptr, info->pcm_delay);
+       return 0;
+}
+
+static int sst_read_timestamp(struct device *dev, struct pcm_stream_info *info)
+{
+       struct stream_info *stream;
+       struct snd_pcm_substream *substream;
+       struct snd_sst_tstamp fw_tstamp;
+       unsigned int str_id;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       str_id = info->str_id;
+       stream = get_stream_info(ctx, str_id);
+       if (!stream)
+               return -EINVAL;
+
+       if (!stream->pcm_substream)
+               return -EINVAL;
+       substream = stream->pcm_substream;
+
+       memcpy_fromio(&fw_tstamp,
+               ((void *)(ctx->mailbox + ctx->tstamp)
+                       + (str_id * sizeof(fw_tstamp))),
+               sizeof(fw_tstamp));
+       return sst_calc_tstamp(ctx, info, substream, &fw_tstamp);
+}
+
+static int sst_stream_start(struct device *dev, int str_id)
+{
+       struct stream_info *str_info;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       if (ctx->sst_state != SST_FW_RUNNING)
+               return 0;
+       str_info = get_stream_info(ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       str_info->prev = str_info->status;
+       str_info->status = STREAM_RUNNING;
+       sst_start_stream(ctx, str_id);
+
+       return 0;
+}
+
+static int sst_stream_drop(struct device *dev, int str_id)
+{
+       struct stream_info *str_info;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       if (ctx->sst_state != SST_FW_RUNNING)
+               return 0;
+
+       str_info = get_stream_info(ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       str_info->prev = STREAM_UN_INIT;
+       str_info->status = STREAM_INIT;
+       return sst_drop_stream(ctx, str_id);
+}
+
+static int sst_stream_init(struct device *dev, struct pcm_stream_info *str_info)
+{
+       int str_id = 0;
+       struct stream_info *stream;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       str_id = str_info->str_id;
+
+       if (ctx->sst_state != SST_FW_RUNNING)
+               return 0;
+
+       stream = get_stream_info(ctx, str_id);
+       if (!stream)
+               return -EINVAL;
+
+       dev_dbg(ctx->dev, "setting the period ptrs\n");
+       stream->pcm_substream = str_info->arg;
+       stream->period_elapsed = str_info->period_elapsed;
+       stream->sfreq = str_info->sfreq;
+       stream->prev = stream->status;
+       stream->status = STREAM_INIT;
+       dev_dbg(ctx->dev,
+               "pcm_substream %p, period_elapsed %p, sfreq %d, status %d\n",
+               stream->pcm_substream, stream->period_elapsed,
+               stream->sfreq, stream->status);
+
+       return 0;
+}
+
+/*
+ * sst_set_byte_stream - Set generic params
+ *
+ * @cmd: control cmd to be set
+ * @arg: command argument
+ *
+ * This function is called by MID sound card driver to configure
+ * SST runtime params.
+ */
+static int sst_send_byte_stream(struct device *dev,
+               struct snd_sst_bytes_v2 *bytes)
+{
+       int ret_val = 0;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       if (NULL == bytes)
+               return -EINVAL;
+       ret_val = pm_runtime_get_sync(ctx->dev);
+       if (ret_val < 0)
+               return ret_val;
+
+       ret_val = sst_send_byte_stream_mrfld(ctx, bytes);
+       sst_pm_runtime_put(ctx);
+
+       return ret_val;
+}
+
+static struct sst_ops pcm_ops = {
+       .open = sst_open_pcm_stream,
+       .stream_init = sst_stream_init,
+       .stream_start = sst_stream_start,
+       .stream_drop = sst_stream_drop,
+       .stream_read_tstamp = sst_read_timestamp,
+       .send_byte_stream = sst_send_byte_stream,
+       .close = sst_close_pcm_stream,
+       .power = sst_power_control,
+};
+
+static struct compress_sst_ops compr_ops = {
+       .open = sst_cdev_open,
+       .close = sst_cdev_close,
+       .stream_pause = sst_cdev_stream_pause,
+       .stream_pause_release = sst_cdev_stream_pause_release,
+       .stream_start = sst_cdev_stream_start,
+       .stream_drop = sst_cdev_stream_drop,
+       .stream_drain = sst_cdev_stream_drain,
+       .stream_partial_drain = sst_cdev_stream_partial_drain,
+       .tstamp = sst_cdev_tstamp,
+       .ack = sst_cdev_ack,
+       .get_caps = sst_cdev_caps,
+       .get_codec_caps = sst_cdev_codec_caps,
+       .set_metadata = sst_cdev_set_metadata,
+       .power = sst_power_control,
+};
+
+static struct sst_device sst_dsp_device = {
+       .name = "Intel(R) SST LPE",
+       .dev = NULL,
+       .ops = &pcm_ops,
+       .compr_ops = &compr_ops,
+};
+
+/*
+ * sst_register - function to register DSP
+ *
+ * This functions registers DSP with the platform driver
+ */
+int sst_register(struct device *dev)
+{
+       int ret_val;
+
+       sst_dsp_device.dev = dev;
+       ret_val = sst_register_dsp(&sst_dsp_device);
+       if (ret_val)
+               dev_err(dev, "Unable to register DSP with platform driver\n");
+
+       return ret_val;
+}
+
+int sst_unregister(struct device *dev)
+{
+       return sst_unregister_dsp(&sst_dsp_device);
+}
diff --git a/sound/soc/intel/sst/sst_ipc.c b/sound/soc/intel/sst/sst_ipc.c
new file mode 100644 (file)
index 0000000..484e609
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ *  sst_ipc.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14 Intel Corporation
+ *  Authors:   Vinod Koul <vinod.koul@intel.com>
+ *             Harsha Priya <priya.harsha@intel.com>
+ *             Dharageswari R <dharageswari.r@intel.com>
+ *             KP Jeeja <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/pci.h>
+#include <linux/firmware.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/intel-mid.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+struct sst_block *sst_create_block(struct intel_sst_drv *ctx,
+                                       u32 msg_id, u32 drv_id)
+{
+       struct sst_block *msg = NULL;
+
+       dev_dbg(ctx->dev, "Enter\n");
+       msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+       if (!msg)
+               return NULL;
+       msg->condition = false;
+       msg->on = true;
+       msg->msg_id = msg_id;
+       msg->drv_id = drv_id;
+       spin_lock_bh(&ctx->block_lock);
+       list_add_tail(&msg->node, &ctx->block_list);
+       spin_unlock_bh(&ctx->block_lock);
+
+       return msg;
+}
+
+/*
+ * while handling the interrupts, we need to check for message status and
+ * then if we are blocking for a message
+ *
+ * here we are unblocking the blocked ones, this is based on id we have
+ * passed and search that for block threads.
+ * We will not find block in two cases
+ *  a) when its small message and block in not there, so silently ignore
+ *  them
+ *  b) when we are actually not able to find the block (bug perhaps)
+ *
+ *  Since we have bit of small messages we can spam kernel log with err
+ *  print on above so need to keep as debug prints which should be enabled
+ *  via dynamic debug while debugging IPC issues
+ */
+int sst_wake_up_block(struct intel_sst_drv *ctx, int result,
+               u32 drv_id, u32 ipc, void *data, u32 size)
+{
+       struct sst_block *block = NULL;
+
+       dev_dbg(ctx->dev, "Enter\n");
+
+       spin_lock_bh(&ctx->block_lock);
+       list_for_each_entry(block, &ctx->block_list, node) {
+               dev_dbg(ctx->dev, "Block ipc %d, drv_id %d\n", block->msg_id,
+                                                       block->drv_id);
+               if (block->msg_id == ipc && block->drv_id == drv_id) {
+                       dev_dbg(ctx->dev, "free up the block\n");
+                       block->ret_code = result;
+                       block->data = data;
+                       block->size = size;
+                       block->condition = true;
+                       spin_unlock_bh(&ctx->block_lock);
+                       wake_up(&ctx->wait_queue);
+                       return 0;
+               }
+       }
+       spin_unlock_bh(&ctx->block_lock);
+       dev_dbg(ctx->dev,
+               "Block not found or a response received for a short msg for ipc %d, drv_id %d\n",
+               ipc, drv_id);
+       return -EINVAL;
+}
+
+int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed)
+{
+       struct sst_block *block = NULL, *__block;
+
+       dev_dbg(ctx->dev, "Enter\n");
+       spin_lock_bh(&ctx->block_lock);
+       list_for_each_entry_safe(block, __block, &ctx->block_list, node) {
+               if (block == freed) {
+                       pr_debug("pvt_id freed --> %d\n", freed->drv_id);
+                       /* toggle the index position of pvt_id */
+                       list_del(&freed->node);
+                       spin_unlock_bh(&ctx->block_lock);
+                       kfree(freed->data);
+                       freed->data = NULL;
+                       kfree(freed);
+                       return 0;
+               }
+       }
+       spin_unlock_bh(&ctx->block_lock);
+       dev_err(ctx->dev, "block is already freed!!!\n");
+       return -EINVAL;
+}
+
+int sst_post_message_mrfld(struct intel_sst_drv *sst_drv_ctx,
+               struct ipc_post *ipc_msg, bool sync)
+{
+       struct ipc_post *msg = ipc_msg;
+       union ipc_header_mrfld header;
+       unsigned int loop_count = 0;
+       int retval = 0;
+       unsigned long irq_flags;
+
+       dev_dbg(sst_drv_ctx->dev, "Enter: sync: %d\n", sync);
+       spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+       header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX);
+       if (sync) {
+               while (header.p.header_high.part.busy) {
+                       if (loop_count > 25) {
+                               dev_err(sst_drv_ctx->dev,
+                                       "sst: Busy wait failed, cant send this msg\n");
+                               retval = -EBUSY;
+                               goto out;
+                       }
+                       cpu_relax();
+                       loop_count++;
+                       header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX);
+               }
+       } else {
+               if (list_empty(&sst_drv_ctx->ipc_dispatch_list)) {
+                       /* queue is empty, nothing to send */
+                       spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+                       dev_dbg(sst_drv_ctx->dev,
+                                       "Empty msg queue... NO Action\n");
+                       return 0;
+               }
+
+               if (header.p.header_high.part.busy) {
+                       spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+                       dev_dbg(sst_drv_ctx->dev, "Busy not free... post later\n");
+                       return 0;
+               }
+
+               /* copy msg from list */
+               msg = list_entry(sst_drv_ctx->ipc_dispatch_list.next,
+                               struct ipc_post, node);
+               list_del(&msg->node);
+       }
+       dev_dbg(sst_drv_ctx->dev, "sst: Post message: header = %x\n",
+                               msg->mrfld_header.p.header_high.full);
+       dev_dbg(sst_drv_ctx->dev, "sst: size = 0x%x\n",
+                       msg->mrfld_header.p.header_low_payload);
+
+       if (msg->mrfld_header.p.header_high.part.large)
+               memcpy_toio(sst_drv_ctx->mailbox + SST_MAILBOX_SEND,
+                       msg->mailbox_data,
+                       msg->mrfld_header.p.header_low_payload);
+
+       sst_shim_write64(sst_drv_ctx->shim, SST_IPCX, msg->mrfld_header.full);
+
+out:
+       spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+       kfree(msg->mailbox_data);
+       kfree(msg);
+       return retval;
+}
+
+void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx)
+{
+       union interrupt_reg_mrfld isr;
+       union interrupt_reg_mrfld imr;
+       union ipc_header_mrfld clear_ipc;
+       unsigned long irq_flags;
+
+       spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+       imr.full = sst_shim_read64(sst_drv_ctx->shim, SST_IMRX);
+       isr.full = sst_shim_read64(sst_drv_ctx->shim, SST_ISRX);
+
+       /* write 1 to clear*/
+       isr.part.busy_interrupt = 1;
+       sst_shim_write64(sst_drv_ctx->shim, SST_ISRX, isr.full);
+
+       /* Set IA done bit */
+       clear_ipc.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCD);
+
+       clear_ipc.p.header_high.part.busy = 0;
+       clear_ipc.p.header_high.part.done = 1;
+       clear_ipc.p.header_low_payload = IPC_ACK_SUCCESS;
+       sst_shim_write64(sst_drv_ctx->shim, SST_IPCD, clear_ipc.full);
+       /* un mask busy interrupt */
+       imr.part.busy_interrupt = 0;
+       sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, imr.full);
+       spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+}
+
+
+/*
+ * process_fw_init - process the FW init msg
+ *
+ * @msg: IPC message mailbox data from FW
+ *
+ * This function processes the FW init msg from FW
+ * marks FW state and prints debug info of loaded FW
+ */
+static void process_fw_init(struct intel_sst_drv *sst_drv_ctx,
+                       void *msg)
+{
+       struct ipc_header_fw_init *init =
+               (struct ipc_header_fw_init *)msg;
+       int retval = 0;
+
+       dev_dbg(sst_drv_ctx->dev, "*** FW Init msg came***\n");
+       if (init->result) {
+               sst_set_fw_state_locked(sst_drv_ctx, SST_RESET);
+               dev_err(sst_drv_ctx->dev, "FW Init failed, Error %x\n",
+                               init->result);
+               retval = init->result;
+               goto ret;
+       }
+
+ret:
+       sst_wake_up_block(sst_drv_ctx, retval, FW_DWNL_ID, 0 , NULL, 0);
+}
+
+static void process_fw_async_msg(struct intel_sst_drv *sst_drv_ctx,
+                       struct ipc_post *msg)
+{
+       u32 msg_id;
+       int str_id;
+       u32 data_size, i;
+       void *data_offset;
+       struct stream_info *stream;
+       union ipc_header_high msg_high;
+       u32 msg_low, pipe_id;
+
+       msg_high = msg->mrfld_header.p.header_high;
+       msg_low = msg->mrfld_header.p.header_low_payload;
+       msg_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->cmd_id;
+       data_offset = (msg->mailbox_data + sizeof(struct ipc_dsp_hdr));
+       data_size =  msg_low - (sizeof(struct ipc_dsp_hdr));
+
+       switch (msg_id) {
+       case IPC_SST_PERIOD_ELAPSED_MRFLD:
+               pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
+               str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
+               if (str_id > 0) {
+                       dev_dbg(sst_drv_ctx->dev,
+                               "Period elapsed rcvd for pipe id 0x%x\n",
+                               pipe_id);
+                       stream = &sst_drv_ctx->streams[str_id];
+                       if (stream->period_elapsed)
+                               stream->period_elapsed(stream->pcm_substream);
+                       if (stream->compr_cb)
+                               stream->compr_cb(stream->compr_cb_param);
+               }
+               break;
+
+       case IPC_IA_DRAIN_STREAM_MRFLD:
+               pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
+               str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
+               if (str_id > 0) {
+                       stream = &sst_drv_ctx->streams[str_id];
+                       if (stream->drain_notify)
+                               stream->drain_notify(stream->drain_cb_param);
+               }
+               break;
+
+       case IPC_IA_FW_ASYNC_ERR_MRFLD:
+               dev_err(sst_drv_ctx->dev, "FW sent async error msg:\n");
+               for (i = 0; i < (data_size/4); i++)
+                       print_hex_dump(KERN_DEBUG, NULL, DUMP_PREFIX_NONE,
+                                       16, 4, data_offset, data_size, false);
+               break;
+
+       case IPC_IA_FW_INIT_CMPLT_MRFLD:
+               process_fw_init(sst_drv_ctx, data_offset);
+               break;
+
+       case IPC_IA_BUF_UNDER_RUN_MRFLD:
+               pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
+               str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
+               if (str_id > 0)
+                       dev_err(sst_drv_ctx->dev,
+                               "Buffer under-run for pipe:%#x str_id:%d\n",
+                               pipe_id, str_id);
+               break;
+
+       default:
+               dev_err(sst_drv_ctx->dev,
+                       "Unrecognized async msg from FW msg_id %#x\n", msg_id);
+       }
+}
+
+void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx,
+               struct ipc_post *msg)
+{
+       unsigned int drv_id;
+       void *data;
+       union ipc_header_high msg_high;
+       u32 msg_low;
+       struct ipc_dsp_hdr *dsp_hdr;
+       unsigned int cmd_id;
+
+       msg_high = msg->mrfld_header.p.header_high;
+       msg_low = msg->mrfld_header.p.header_low_payload;
+
+       dev_dbg(sst_drv_ctx->dev, "IPC process message header %x payload %x\n",
+                       msg->mrfld_header.p.header_high.full,
+                       msg->mrfld_header.p.header_low_payload);
+
+       drv_id = msg_high.part.drv_id;
+
+       /* Check for async messages first */
+       if (drv_id == SST_ASYNC_DRV_ID) {
+               /*FW sent async large message*/
+               process_fw_async_msg(sst_drv_ctx, msg);
+               return;
+       }
+
+       /* FW sent short error response for an IPC */
+       if (msg_high.part.result && drv_id && !msg_high.part.large) {
+               /* 32-bit FW error code in msg_low */
+               dev_err(sst_drv_ctx->dev, "FW sent error response 0x%x", msg_low);
+               sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
+                       msg_high.part.drv_id,
+                       msg_high.part.msg_id, NULL, 0);
+               return;
+       }
+
+       /*
+        * Process all valid responses
+        * if it is a large message, the payload contains the size to
+        * copy from mailbox
+        **/
+       if (msg_high.part.large) {
+               data = kzalloc(msg_low, GFP_KERNEL);
+               if (!data)
+                       return;
+               memcpy(data, (void *) msg->mailbox_data, msg_low);
+               /* Copy command id so that we can use to put sst to reset */
+               dsp_hdr = (struct ipc_dsp_hdr *)data;
+               cmd_id = dsp_hdr->cmd_id;
+               dev_dbg(sst_drv_ctx->dev, "cmd_id %d\n", dsp_hdr->cmd_id);
+               if (sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
+                               msg_high.part.drv_id,
+                               msg_high.part.msg_id, data, msg_low))
+                       kfree(data);
+       } else {
+               sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
+                               msg_high.part.drv_id,
+                               msg_high.part.msg_id, NULL, 0);
+       }
+
+}
diff --git a/sound/soc/intel/sst/sst_loader.c b/sound/soc/intel/sst/sst_loader.c
new file mode 100644 (file)
index 0000000..b580f96
--- /dev/null
@@ -0,0 +1,456 @@
+/*
+ *  sst_dsp.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14      Intel Corp
+ *  Authors:   Vinod Koul <vinod.koul@intel.com>
+ *             Harsha Priya <priya.harsha@intel.com>
+ *             Dharageswari R <dharageswari.r@intel.com>
+ *             KP Jeeja <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This file contains all dsp controlling functions like firmware download,
+ * setting/resetting dsp cores, etc
+ */
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+#include <linux/dmaengine.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+static inline void memcpy32_toio(void __iomem *dst, const void *src, int count)
+{
+       /* __iowrite32_copy uses 32-bit count values so divide by 4 for
+        * right count in words
+        */
+       __iowrite32_copy(dst, src, count/4);
+}
+
+/**
+ * intel_sst_reset_dsp_mrfld - Resetting SST DSP
+ *
+ * This resets DSP in case of MRFLD platfroms
+ */
+int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *sst_drv_ctx)
+{
+       union config_status_reg_mrfld csr;
+
+       dev_dbg(sst_drv_ctx->dev, "sst: Resetting the DSP in mrfld\n");
+       csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+
+       dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+       csr.full |= 0x7;
+       sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+       csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+
+       dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+       csr.full &= ~(0x1);
+       sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+
+       csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+       dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+       return 0;
+}
+
+/**
+ * sst_start_merrifield - Start the SST DSP processor
+ *
+ * This starts the DSP in MERRIFIELD platfroms
+ */
+int sst_start_mrfld(struct intel_sst_drv *sst_drv_ctx)
+{
+       union config_status_reg_mrfld csr;
+
+       dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP in mrfld LALALALA\n");
+       csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+       dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+       csr.full |= 0x7;
+       sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+
+       csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+       dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+       csr.part.xt_snoop = 1;
+       csr.full &= ~(0x5);
+       sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+
+       csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+       dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP_merrifield:%llx\n",
+                       csr.full);
+       return 0;
+}
+
+static int sst_validate_fw_image(struct intel_sst_drv *ctx, unsigned long size,
+               struct fw_module_header **module, u32 *num_modules)
+{
+       struct sst_fw_header *header;
+       const void *sst_fw_in_mem = ctx->fw_in_mem;
+
+       dev_dbg(ctx->dev, "Enter\n");
+
+       /* Read the header information from the data pointer */
+       header = (struct sst_fw_header *)sst_fw_in_mem;
+       dev_dbg(ctx->dev,
+               "header sign=%s size=%x modules=%x fmt=%x size=%zx\n",
+               header->signature, header->file_size, header->modules,
+               header->file_format, sizeof(*header));
+
+       /* verify FW */
+       if ((strncmp(header->signature, SST_FW_SIGN, 4) != 0) ||
+               (size != header->file_size + sizeof(*header))) {
+               /* Invalid FW signature */
+               dev_err(ctx->dev, "InvalidFW sign/filesize mismatch\n");
+               return -EINVAL;
+       }
+       *num_modules = header->modules;
+       *module = (void *)sst_fw_in_mem + sizeof(*header);
+
+       return 0;
+}
+
+/*
+ * sst_fill_memcpy_list - Fill the memcpy list
+ *
+ * @memcpy_list: List to be filled
+ * @destn: Destination addr to be filled in the list
+ * @src: Source addr to be filled in the list
+ * @size: Size to be filled in the list
+ *
+ * Adds the node to the list after required fields
+ * are populated in the node
+ */
+static int sst_fill_memcpy_list(struct list_head *memcpy_list,
+                       void *destn, const void *src, u32 size, bool is_io)
+{
+       struct sst_memcpy_list *listnode;
+
+       listnode = kzalloc(sizeof(*listnode), GFP_KERNEL);
+       if (listnode == NULL)
+               return -ENOMEM;
+       listnode->dstn = destn;
+       listnode->src = src;
+       listnode->size = size;
+       listnode->is_io = is_io;
+       list_add_tail(&listnode->memcpylist, memcpy_list);
+
+       return 0;
+}
+
+/**
+ * sst_parse_module_memcpy - Parse audio FW modules and populate the memcpy list
+ *
+ * @sst_drv_ctx                : driver context
+ * @module             : FW module header
+ * @memcpy_list        : Pointer to the list to be populated
+ * Create the memcpy list as the number of block to be copied
+ * returns error or 0 if module sizes are proper
+ */
+static int sst_parse_module_memcpy(struct intel_sst_drv *sst_drv_ctx,
+               struct fw_module_header *module, struct list_head *memcpy_list)
+{
+       struct fw_block_info *block;
+       u32 count;
+       int ret_val = 0;
+       void __iomem *ram_iomem;
+
+       dev_dbg(sst_drv_ctx->dev, "module sign %s size %x blocks %x type %x\n",
+                       module->signature, module->mod_size,
+                       module->blocks, module->type);
+       dev_dbg(sst_drv_ctx->dev, "module entrypoint 0x%x\n", module->entry_point);
+
+       block = (void *)module + sizeof(*module);
+
+       for (count = 0; count < module->blocks; count++) {
+               if (block->size <= 0) {
+                       dev_err(sst_drv_ctx->dev, "block size invalid\n");
+                       return -EINVAL;
+               }
+               switch (block->type) {
+               case SST_IRAM:
+                       ram_iomem = sst_drv_ctx->iram;
+                       break;
+               case SST_DRAM:
+                       ram_iomem = sst_drv_ctx->dram;
+                       break;
+               case SST_DDR:
+                       ram_iomem = sst_drv_ctx->ddr;
+                       break;
+               case SST_CUSTOM_INFO:
+                       block = (void *)block + sizeof(*block) + block->size;
+                       continue;
+               default:
+                       dev_err(sst_drv_ctx->dev, "wrong ram type0x%x in block0x%x\n",
+                                       block->type, count);
+                       return -EINVAL;
+               }
+
+               ret_val = sst_fill_memcpy_list(memcpy_list,
+                               ram_iomem + block->ram_offset,
+                               (void *)block + sizeof(*block), block->size, 1);
+               if (ret_val)
+                       return ret_val;
+
+               block = (void *)block + sizeof(*block) + block->size;
+       }
+       return 0;
+}
+
+/**
+ * sst_parse_fw_memcpy - parse the firmware image & populate the list for memcpy
+ *
+ * @ctx                        : pointer to drv context
+ * @size               : size of the firmware
+ * @fw_list            : pointer to list_head to be populated
+ * This function parses the FW image and saves the parsed image in the list
+ * for memcpy
+ */
+static int sst_parse_fw_memcpy(struct intel_sst_drv *ctx, unsigned long size,
+                               struct list_head *fw_list)
+{
+       struct fw_module_header *module;
+       u32 count, num_modules;
+       int ret_val;
+
+       ret_val = sst_validate_fw_image(ctx, size, &module, &num_modules);
+       if (ret_val)
+               return ret_val;
+
+       for (count = 0; count < num_modules; count++) {
+               ret_val = sst_parse_module_memcpy(ctx, module, fw_list);
+               if (ret_val)
+                       return ret_val;
+               module = (void *)module + sizeof(*module) + module->mod_size;
+       }
+
+       return 0;
+}
+
+/**
+ * sst_do_memcpy - function initiates the memcpy
+ *
+ * @memcpy_list: Pter to memcpy list on which the memcpy needs to be initiated
+ *
+ * Triggers the memcpy
+ */
+static void sst_do_memcpy(struct list_head *memcpy_list)
+{
+       struct sst_memcpy_list *listnode;
+
+       list_for_each_entry(listnode, memcpy_list, memcpylist) {
+               if (listnode->is_io == true)
+                       memcpy32_toio((void __iomem *)listnode->dstn,
+                                       listnode->src, listnode->size);
+               else
+                       memcpy(listnode->dstn, listnode->src, listnode->size);
+       }
+}
+
+void sst_memcpy_free_resources(struct intel_sst_drv *sst_drv_ctx)
+{
+       struct sst_memcpy_list *listnode, *tmplistnode;
+
+       /* Free the list */
+       if (!list_empty(&sst_drv_ctx->memcpy_list)) {
+               list_for_each_entry_safe(listnode, tmplistnode,
+                               &sst_drv_ctx->memcpy_list, memcpylist) {
+                       list_del(&listnode->memcpylist);
+                       kfree(listnode);
+               }
+       }
+}
+
+static int sst_cache_and_parse_fw(struct intel_sst_drv *sst,
+               const struct firmware *fw)
+{
+       int retval = 0;
+
+       sst->fw_in_mem = kzalloc(fw->size, GFP_KERNEL);
+       if (!sst->fw_in_mem) {
+               retval = -ENOMEM;
+               goto end_release;
+       }
+       dev_dbg(sst->dev, "copied fw to %p", sst->fw_in_mem);
+       dev_dbg(sst->dev, "phys: %lx", (unsigned long)virt_to_phys(sst->fw_in_mem));
+       memcpy(sst->fw_in_mem, fw->data, fw->size);
+       retval = sst_parse_fw_memcpy(sst, fw->size, &sst->memcpy_list);
+       if (retval) {
+               dev_err(sst->dev, "Failed to parse fw\n");
+               kfree(sst->fw_in_mem);
+               sst->fw_in_mem = NULL;
+       }
+
+end_release:
+       release_firmware(fw);
+       return retval;
+
+}
+
+void sst_firmware_load_cb(const struct firmware *fw, void *context)
+{
+       struct intel_sst_drv *ctx = context;
+
+       dev_dbg(ctx->dev, "Enter\n");
+
+       if (fw == NULL) {
+               dev_err(ctx->dev, "request fw failed\n");
+               return;
+       }
+
+       mutex_lock(&ctx->sst_lock);
+
+       if (ctx->sst_state != SST_RESET ||
+                       ctx->fw_in_mem != NULL) {
+               if (fw != NULL)
+                       release_firmware(fw);
+               mutex_unlock(&ctx->sst_lock);
+               return;
+       }
+
+       dev_dbg(ctx->dev, "Request Fw completed\n");
+       sst_cache_and_parse_fw(ctx, fw);
+       mutex_unlock(&ctx->sst_lock);
+}
+
+/*
+ * sst_request_fw - requests audio fw from kernel and saves a copy
+ *
+ * This function requests the SST FW from the kernel, parses it and
+ * saves a copy in the driver context
+ */
+static int sst_request_fw(struct intel_sst_drv *sst)
+{
+       int retval = 0;
+       const struct firmware *fw;
+
+       retval = request_firmware(&fw, sst->firmware_name, sst->dev);
+       if (fw == NULL) {
+               dev_err(sst->dev, "fw is returning as null\n");
+               return -EINVAL;
+       }
+       if (retval) {
+               dev_err(sst->dev, "request fw failed %d\n", retval);
+               return retval;
+       }
+       mutex_lock(&sst->sst_lock);
+       retval = sst_cache_and_parse_fw(sst, fw);
+       mutex_unlock(&sst->sst_lock);
+
+       return retval;
+}
+
+/*
+ * Writing the DDR physical base to DCCM offset
+ * so that FW can use it to setup TLB
+ */
+static void sst_dccm_config_write(void __iomem *dram_base,
+               unsigned int ddr_base)
+{
+       void __iomem *addr;
+       u32 bss_reset = 0;
+
+       addr = (void __iomem *)(dram_base + MRFLD_FW_DDR_BASE_OFFSET);
+       memcpy32_toio(addr, (void *)&ddr_base, sizeof(u32));
+       bss_reset |= (1 << MRFLD_FW_BSS_RESET_BIT);
+       addr = (void __iomem *)(dram_base + MRFLD_FW_FEATURE_BASE_OFFSET);
+       memcpy32_toio(addr, &bss_reset, sizeof(u32));
+
+}
+
+void sst_post_download_mrfld(struct intel_sst_drv *ctx)
+{
+       sst_dccm_config_write(ctx->dram, ctx->ddr_base);
+       dev_dbg(ctx->dev, "config written to DCCM\n");
+}
+
+/**
+ * sst_load_fw - function to load FW into DSP
+ * Transfers the FW to DSP using dma/memcpy
+ */
+int sst_load_fw(struct intel_sst_drv *sst_drv_ctx)
+{
+       int ret_val = 0;
+       struct sst_block *block;
+
+       dev_dbg(sst_drv_ctx->dev, "sst_load_fw\n");
+
+       if (sst_drv_ctx->sst_state !=  SST_RESET ||
+                       sst_drv_ctx->sst_state == SST_SHUTDOWN)
+               return -EAGAIN;
+
+       if (!sst_drv_ctx->fw_in_mem) {
+               dev_dbg(sst_drv_ctx->dev, "sst: FW not in memory retry to download\n");
+               ret_val = sst_request_fw(sst_drv_ctx);
+               if (ret_val)
+                       return ret_val;
+       }
+
+       BUG_ON(!sst_drv_ctx->fw_in_mem);
+       block = sst_create_block(sst_drv_ctx, 0, FW_DWNL_ID);
+       if (block == NULL)
+               return -ENOMEM;
+
+       /* Prevent C-states beyond C6 */
+       pm_qos_update_request(sst_drv_ctx->qos, 0);
+
+       sst_drv_ctx->sst_state = SST_FW_LOADING;
+
+       ret_val = sst_drv_ctx->ops->reset(sst_drv_ctx);
+       if (ret_val)
+               goto restore;
+
+       sst_do_memcpy(&sst_drv_ctx->memcpy_list);
+
+       /* Write the DRAM/DCCM config before enabling FW */
+       if (sst_drv_ctx->ops->post_download)
+               sst_drv_ctx->ops->post_download(sst_drv_ctx);
+
+       /* bring sst out of reset */
+       ret_val = sst_drv_ctx->ops->start(sst_drv_ctx);
+       if (ret_val)
+               goto restore;
+
+       ret_val = sst_wait_timeout(sst_drv_ctx, block);
+       if (ret_val) {
+               dev_err(sst_drv_ctx->dev, "fw download failed %d\n" , ret_val);
+               /* FW download failed due to timeout */
+               ret_val = -EBUSY;
+
+       }
+
+
+restore:
+       /* Re-enable Deeper C-states beyond C6 */
+       pm_qos_update_request(sst_drv_ctx->qos, PM_QOS_DEFAULT_VALUE);
+       sst_free_block(sst_drv_ctx, block);
+       dev_dbg(sst_drv_ctx->dev, "fw load successful!!!\n");
+
+       if (sst_drv_ctx->ops->restore_dsp_context)
+               sst_drv_ctx->ops->restore_dsp_context();
+       sst_drv_ctx->sst_state = SST_FW_RUNNING;
+       return ret_val;
+}
+
diff --git a/sound/soc/intel/sst/sst_pci.c b/sound/soc/intel/sst/sst_pci.c
new file mode 100644 (file)
index 0000000..3a0b3bf
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ *  sst_pci.c - SST (LPE) driver init file for pci enumeration.
+ *
+ *  Copyright (C) 2008-14      Intel Corp
+ *  Authors:   Vinod Koul <vinod.koul@intel.com>
+ *             Harsha Priya <priya.harsha@intel.com>
+ *             Dharageswari R <dharageswari.r@intel.com>
+ *             KP Jeeja <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+
+static int sst_platform_get_resources(struct intel_sst_drv *ctx)
+{
+       int ddr_base, ret = 0;
+       struct pci_dev *pci = ctx->pci;
+
+       ret = pci_request_regions(pci, SST_DRV_NAME);
+       if (ret)
+               return ret;
+
+       /* map registers */
+       /* DDR base */
+       if (ctx->dev_id == SST_MRFLD_PCI_ID) {
+               ctx->ddr_base = pci_resource_start(pci, 0);
+               /* check that the relocated IMR base matches with FW Binary */
+               ddr_base = relocate_imr_addr_mrfld(ctx->ddr_base);
+               if (!ctx->pdata->lib_info) {
+                       dev_err(ctx->dev, "lib_info pointer NULL\n");
+                       ret = -EINVAL;
+                       goto do_release_regions;
+               }
+               if (ddr_base != ctx->pdata->lib_info->mod_base) {
+                       dev_err(ctx->dev,
+                                       "FW LSP DDR BASE does not match with IFWI\n");
+                       ret = -EINVAL;
+                       goto do_release_regions;
+               }
+               ctx->ddr_end = pci_resource_end(pci, 0);
+
+               ctx->ddr = pcim_iomap(pci, 0,
+                                       pci_resource_len(pci, 0));
+               if (!ctx->ddr) {
+                       ret = -EINVAL;
+                       goto do_release_regions;
+               }
+               dev_dbg(ctx->dev, "sst: DDR Ptr %p\n", ctx->ddr);
+       } else {
+               ctx->ddr = NULL;
+       }
+       /* SHIM */
+       ctx->shim_phy_add = pci_resource_start(pci, 1);
+       ctx->shim = pcim_iomap(pci, 1, pci_resource_len(pci, 1));
+       if (!ctx->shim) {
+               ret = -EINVAL;
+               goto do_release_regions;
+       }
+       dev_dbg(ctx->dev, "SST Shim Ptr %p\n", ctx->shim);
+
+       /* Shared SRAM */
+       ctx->mailbox_add = pci_resource_start(pci, 2);
+       ctx->mailbox = pcim_iomap(pci, 2, pci_resource_len(pci, 2));
+       if (!ctx->mailbox) {
+               ret = -EINVAL;
+               goto do_release_regions;
+       }
+       dev_dbg(ctx->dev, "SRAM Ptr %p\n", ctx->mailbox);
+
+       /* IRAM */
+       ctx->iram_end = pci_resource_end(pci, 3);
+       ctx->iram_base = pci_resource_start(pci, 3);
+       ctx->iram = pcim_iomap(pci, 3, pci_resource_len(pci, 3));
+       if (!ctx->iram) {
+               ret = -EINVAL;
+               goto do_release_regions;
+       }
+       dev_dbg(ctx->dev, "IRAM Ptr %p\n", ctx->iram);
+
+       /* DRAM */
+       ctx->dram_end = pci_resource_end(pci, 4);
+       ctx->dram_base = pci_resource_start(pci, 4);
+       ctx->dram = pcim_iomap(pci, 4, pci_resource_len(pci, 4));
+       if (!ctx->dram) {
+               ret = -EINVAL;
+               goto do_release_regions;
+       }
+       dev_dbg(ctx->dev, "DRAM Ptr %p\n", ctx->dram);
+do_release_regions:
+       pci_release_regions(pci);
+       return 0;
+}
+
+/*
+ * intel_sst_probe - PCI probe function
+ *
+ * @pci:       PCI device structure
+ * @pci_id: PCI device ID structure
+ *
+ */
+static int intel_sst_probe(struct pci_dev *pci,
+                       const struct pci_device_id *pci_id)
+{
+       int ret = 0;
+       struct intel_sst_drv *sst_drv_ctx;
+       struct sst_platform_info *sst_pdata = pci->dev.platform_data;
+
+       dev_dbg(&pci->dev, "Probe for DID %x\n", pci->device);
+       ret = sst_alloc_drv_context(&sst_drv_ctx, &pci->dev, pci->device);
+       if (ret < 0)
+               return ret;
+
+       sst_drv_ctx->pdata = sst_pdata;
+       sst_drv_ctx->irq_num = pci->irq;
+       snprintf(sst_drv_ctx->firmware_name, sizeof(sst_drv_ctx->firmware_name),
+                       "%s%04x%s", "fw_sst_",
+                       sst_drv_ctx->dev_id, ".bin");
+
+       ret = sst_context_init(sst_drv_ctx);
+       if (ret < 0)
+               return ret;
+
+       /* Init the device */
+       ret = pcim_enable_device(pci);
+       if (ret) {
+               dev_err(sst_drv_ctx->dev,
+                       "device can't be enabled. Returned err: %d\n", ret);
+               goto do_free_drv_ctx;
+       }
+       sst_drv_ctx->pci = pci_dev_get(pci);
+       ret = sst_platform_get_resources(sst_drv_ctx);
+       if (ret < 0)
+               goto do_free_drv_ctx;
+
+       pci_set_drvdata(pci, sst_drv_ctx);
+       sst_configure_runtime_pm(sst_drv_ctx);
+
+       return ret;
+
+do_free_drv_ctx:
+       sst_context_cleanup(sst_drv_ctx);
+       dev_err(sst_drv_ctx->dev, "Probe failed with %d\n", ret);
+       return ret;
+}
+
+/**
+ * intel_sst_remove - PCI remove function
+ *
+ * @pci:       PCI device structure
+ *
+ * This function is called by OS when a device is unloaded
+ * This frees the interrupt etc
+ */
+static void intel_sst_remove(struct pci_dev *pci)
+{
+       struct intel_sst_drv *sst_drv_ctx = pci_get_drvdata(pci);
+
+       sst_context_cleanup(sst_drv_ctx);
+       pci_dev_put(sst_drv_ctx->pci);
+       pci_release_regions(pci);
+       pci_set_drvdata(pci, NULL);
+}
+
+/* PCI Routines */
+static struct pci_device_id intel_sst_ids[] = {
+       { PCI_VDEVICE(INTEL, SST_MRFLD_PCI_ID), 0},
+       { 0, }
+};
+
+static struct pci_driver sst_driver = {
+       .name = SST_DRV_NAME,
+       .id_table = intel_sst_ids,
+       .probe = intel_sst_probe,
+       .remove = intel_sst_remove,
+#ifdef CONFIG_PM
+       .driver = {
+               .pm = &intel_sst_pm,
+       },
+#endif
+};
+
+module_pci_driver(sst_driver);
+
+MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine PCI Driver");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_AUTHOR("Dharageswari R <dharageswari.r@intel.com>");
+MODULE_AUTHOR("KP Jeeja <jeeja.kp@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("sst");
diff --git a/sound/soc/intel/sst/sst_pvt.c b/sound/soc/intel/sst/sst_pvt.c
new file mode 100644 (file)
index 0000000..4b77208
--- /dev/null
@@ -0,0 +1,449 @@
+/*
+ *  sst_pvt.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14      Intel Corp
+ *  Authors:   Vinod Koul <vinod.koul@intel.com>
+ *             Harsha Priya <priya.harsha@intel.com>
+ *             Dharageswari R <dharageswari.r@intel.com>
+ *             KP Jeeja <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/kobject.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <sound/asound.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+int sst_shim_write(void __iomem *addr, int offset, int value)
+{
+       writel(value, addr + offset);
+       return 0;
+}
+
+u32 sst_shim_read(void __iomem *addr, int offset)
+{
+       return readl(addr + offset);
+}
+
+u64 sst_reg_read64(void __iomem *addr, int offset)
+{
+       u64 val = 0;
+
+       memcpy_fromio(&val, addr + offset, sizeof(val));
+
+       return val;
+}
+
+int sst_shim_write64(void __iomem *addr, int offset, u64 value)
+{
+       memcpy_toio(addr + offset, &value, sizeof(value));
+       return 0;
+}
+
+u64 sst_shim_read64(void __iomem *addr, int offset)
+{
+       u64 val = 0;
+
+       memcpy_fromio(&val, addr + offset, sizeof(val));
+       return val;
+}
+
+void sst_set_fw_state_locked(
+               struct intel_sst_drv *sst_drv_ctx, int sst_state)
+{
+       mutex_lock(&sst_drv_ctx->sst_lock);
+       sst_drv_ctx->sst_state = sst_state;
+       mutex_unlock(&sst_drv_ctx->sst_lock);
+}
+
+/*
+ * sst_wait_interruptible - wait on event
+ *
+ * @sst_drv_ctx: Driver context
+ * @block: Driver block to wait on
+ *
+ * This function waits without a timeout (and is interruptable) for a
+ * given block event
+ */
+int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx,
+                               struct sst_block *block)
+{
+       int retval = 0;
+
+       if (!wait_event_interruptible(sst_drv_ctx->wait_queue,
+                               block->condition)) {
+               /* event wake */
+               if (block->ret_code < 0) {
+                       dev_err(sst_drv_ctx->dev,
+                               "stream failed %d\n", block->ret_code);
+                       retval = -EBUSY;
+               } else {
+                       dev_dbg(sst_drv_ctx->dev, "event up\n");
+                       retval = 0;
+               }
+       } else {
+               dev_err(sst_drv_ctx->dev, "signal interrupted\n");
+               retval = -EINTR;
+       }
+       return retval;
+
+}
+
+unsigned long long read_shim_data(struct intel_sst_drv *sst, int addr)
+{
+       unsigned long long val = 0;
+
+       switch (sst->dev_id) {
+       case SST_MRFLD_PCI_ID:
+       case SST_BYT_ACPI_ID:
+               val = sst_shim_read64(sst->shim, addr);
+               break;
+       }
+       return val;
+}
+
+void write_shim_data(struct intel_sst_drv *sst, int addr,
+                               unsigned long long data)
+{
+       switch (sst->dev_id) {
+       case SST_MRFLD_PCI_ID:
+       case SST_BYT_ACPI_ID:
+               sst_shim_write64(sst->shim, addr, (u64) data);
+               break;
+       }
+}
+
+/*
+ * sst_wait_timeout - wait on event for timeout
+ *
+ * @sst_drv_ctx: Driver context
+ * @block: Driver block to wait on
+ *
+ * This function waits with a timeout value (and is not interruptible) on a
+ * given block event
+ */
+int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx, struct sst_block *block)
+{
+       int retval = 0;
+
+       /*
+        * NOTE:
+        * Observed that FW processes the alloc msg and replies even
+        * before the alloc thread has finished execution
+        */
+       dev_dbg(sst_drv_ctx->dev,
+               "waiting for condition %x ipc %d drv_id %d\n",
+               block->condition, block->msg_id, block->drv_id);
+       if (wait_event_timeout(sst_drv_ctx->wait_queue,
+                               block->condition,
+                               msecs_to_jiffies(SST_BLOCK_TIMEOUT))) {
+               /* event wake */
+               dev_dbg(sst_drv_ctx->dev, "Event wake %x\n",
+                               block->condition);
+               dev_dbg(sst_drv_ctx->dev, "message ret: %d\n",
+                               block->ret_code);
+               retval = -block->ret_code;
+       } else {
+               block->on = false;
+               dev_err(sst_drv_ctx->dev,
+                       "Wait timed-out condition:%#x, msg_id:%#x fw_state %#x\n",
+                       block->condition, block->msg_id, sst_drv_ctx->sst_state);
+               sst_drv_ctx->sst_state = SST_RESET;
+
+               retval = -EBUSY;
+       }
+       return retval;
+}
+
+/*
+ * sst_create_ipc_msg - create a IPC message
+ *
+ * @arg: ipc message
+ * @large: large or short message
+ *
+ * this function allocates structures to send a large or short
+ * message to the firmware
+ */
+int sst_create_ipc_msg(struct ipc_post **arg, bool large)
+{
+       struct ipc_post *msg;
+
+       msg = kzalloc(sizeof(struct ipc_post), GFP_ATOMIC);
+       if (!msg)
+               return -ENOMEM;
+       if (large) {
+               msg->mailbox_data = kzalloc(SST_MAILBOX_SIZE, GFP_ATOMIC);
+               if (!msg->mailbox_data) {
+                       kfree(msg);
+                       return -ENOMEM;
+               }
+       } else {
+               msg->mailbox_data = NULL;
+       }
+       msg->is_large = large;
+       *arg = msg;
+       return 0;
+}
+
+/*
+ * sst_create_block_and_ipc_msg - Creates IPC message and sst block
+ * @arg: passed to sst_create_ipc_message API
+ * @large: large or short message
+ * @sst_drv_ctx: sst driver context
+ * @block: return block allocated
+ * @msg_id: IPC
+ * @drv_id: stream id or private id
+ */
+int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large,
+               struct intel_sst_drv *sst_drv_ctx, struct sst_block **block,
+               u32 msg_id, u32 drv_id)
+{
+       int retval = 0;
+
+       retval = sst_create_ipc_msg(arg, large);
+       if (retval)
+               return retval;
+       *block = sst_create_block(sst_drv_ctx, msg_id, drv_id);
+       if (*block == NULL) {
+               kfree(*arg);
+               return -ENOMEM;
+       }
+       return retval;
+}
+
+/*
+ * sst_clean_stream - clean the stream context
+ *
+ * @stream: stream structure
+ *
+ * this function resets the stream contexts
+ * should be called in free
+ */
+void sst_clean_stream(struct stream_info *stream)
+{
+       stream->status = STREAM_UN_INIT;
+       stream->prev = STREAM_UN_INIT;
+       mutex_lock(&stream->lock);
+       stream->cumm_bytes = 0;
+       mutex_unlock(&stream->lock);
+}
+
+int sst_prepare_and_post_msg(struct intel_sst_drv *sst,
+               int task_id, int ipc_msg, int cmd_id, int pipe_id,
+               size_t mbox_data_len, const void *mbox_data, void **data,
+               bool large, bool fill_dsp, bool sync, bool response)
+{
+       struct ipc_post *msg = NULL;
+       struct ipc_dsp_hdr dsp_hdr;
+       struct sst_block *block;
+       int ret = 0, pvt_id;
+
+       pvt_id = sst_assign_pvt_id(sst);
+       if (pvt_id < 0)
+               return pvt_id;
+
+       if (response)
+               ret = sst_create_block_and_ipc_msg(
+                               &msg, large, sst, &block, ipc_msg, pvt_id);
+       else
+               ret = sst_create_ipc_msg(&msg, large);
+
+       if (ret < 0) {
+               test_and_clear_bit(pvt_id, &sst->pvt_id);
+               return -ENOMEM;
+       }
+
+       dev_dbg(sst->dev, "pvt_id = %d, pipe id = %d, task = %d ipc_msg: %d\n",
+                pvt_id, pipe_id, task_id, ipc_msg);
+       sst_fill_header_mrfld(&msg->mrfld_header, ipc_msg,
+                                       task_id, large, pvt_id);
+       msg->mrfld_header.p.header_low_payload = sizeof(dsp_hdr) + mbox_data_len;
+       msg->mrfld_header.p.header_high.part.res_rqd = !sync;
+       dev_dbg(sst->dev, "header:%x\n",
+                       msg->mrfld_header.p.header_high.full);
+       dev_dbg(sst->dev, "response rqd: %x",
+                       msg->mrfld_header.p.header_high.part.res_rqd);
+       dev_dbg(sst->dev, "msg->mrfld_header.p.header_low_payload:%d",
+                       msg->mrfld_header.p.header_low_payload);
+       if (fill_dsp) {
+               sst_fill_header_dsp(&dsp_hdr, cmd_id, pipe_id, mbox_data_len);
+               memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr));
+               if (mbox_data_len) {
+                       memcpy(msg->mailbox_data + sizeof(dsp_hdr),
+                                       mbox_data, mbox_data_len);
+               }
+       }
+
+       if (sync)
+               sst->ops->post_message(sst, msg, true);
+       else
+               sst_add_to_dispatch_list_and_post(sst, msg);
+
+       if (response) {
+               ret = sst_wait_timeout(sst, block);
+               if (ret < 0) {
+                       goto out;
+               } else if(block->data) {
+                       if (!data)
+                               goto out;
+                       *data = kzalloc(block->size, GFP_KERNEL);
+                       if (!(*data)) {
+                               ret = -ENOMEM;
+                               goto out;
+                       } else
+                               memcpy(data, (void *) block->data, block->size);
+               }
+       }
+out:
+       if (response)
+               sst_free_block(sst, block);
+       test_and_clear_bit(pvt_id, &sst->pvt_id);
+       return ret;
+}
+
+int sst_pm_runtime_put(struct intel_sst_drv *sst_drv)
+{
+       int ret;
+
+       pm_runtime_mark_last_busy(sst_drv->dev);
+       ret = pm_runtime_put_autosuspend(sst_drv->dev);
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
+void sst_fill_header_mrfld(union ipc_header_mrfld *header,
+                               int msg, int task_id, int large, int drv_id)
+{
+       header->full = 0;
+       header->p.header_high.part.msg_id = msg;
+       header->p.header_high.part.task_id = task_id;
+       header->p.header_high.part.large = large;
+       header->p.header_high.part.drv_id = drv_id;
+       header->p.header_high.part.done = 0;
+       header->p.header_high.part.busy = 1;
+       header->p.header_high.part.res_rqd = 1;
+}
+
+void sst_fill_header_dsp(struct ipc_dsp_hdr *dsp, int msg,
+                                       int pipe_id, int len)
+{
+       dsp->cmd_id = msg;
+       dsp->mod_index_id = 0xff;
+       dsp->pipe_id = pipe_id;
+       dsp->length = len;
+       dsp->mod_id = 0;
+}
+
+#define SST_MAX_BLOCKS 15
+/*
+ * sst_assign_pvt_id - assign a pvt id for stream
+ *
+ * @sst_drv_ctx : driver context
+ *
+ * this function assigns a private id for calls that dont have stream
+ * context yet, should be called with lock held
+ * uses bits for the id, and finds first free bits and assigns that
+ */
+int sst_assign_pvt_id(struct intel_sst_drv *drv)
+{
+       int local;
+
+       spin_lock(&drv->block_lock);
+       /* find first zero index from lsb */
+       local = ffz(drv->pvt_id);
+       dev_dbg(drv->dev, "pvt_id assigned --> %d\n", local);
+       if (local >= SST_MAX_BLOCKS){
+               spin_unlock(&drv->block_lock);
+               dev_err(drv->dev, "PVT _ID error: no free id blocks ");
+               return -EINVAL;
+       }
+       /* toggle the index */
+       change_bit(local, &drv->pvt_id);
+       spin_unlock(&drv->block_lock);
+       return local;
+}
+
+void sst_init_stream(struct stream_info *stream,
+               int codec, int sst_id, int ops, u8 slot)
+{
+       stream->status = STREAM_INIT;
+       stream->prev = STREAM_UN_INIT;
+       stream->ops = ops;
+}
+
+int sst_validate_strid(
+               struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+       if (str_id <= 0 || str_id > sst_drv_ctx->info.max_streams) {
+               dev_err(sst_drv_ctx->dev,
+                       "SST ERR: invalid stream id : %d, max %d\n",
+                       str_id, sst_drv_ctx->info.max_streams);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+struct stream_info *get_stream_info(
+               struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+       if (sst_validate_strid(sst_drv_ctx, str_id))
+               return NULL;
+       return &sst_drv_ctx->streams[str_id];
+}
+
+int get_stream_id_mrfld(struct intel_sst_drv *sst_drv_ctx,
+               u32 pipe_id)
+{
+       int i;
+
+       for (i = 1; i <= sst_drv_ctx->info.max_streams; i++)
+               if (pipe_id == sst_drv_ctx->streams[i].pipe_id)
+                       return i;
+
+       dev_dbg(sst_drv_ctx->dev, "no such pipe_id(%u)", pipe_id);
+       return -1;
+}
+
+u32 relocate_imr_addr_mrfld(u32 base_addr)
+{
+       /* Get the difference from 512MB aligned base addr */
+       /* relocate the base */
+       base_addr = MRFLD_FW_VIRTUAL_BASE + (base_addr % (512 * 1024 * 1024));
+       return base_addr;
+}
+EXPORT_SYMBOL_GPL(relocate_imr_addr_mrfld);
+
+void sst_add_to_dispatch_list_and_post(struct intel_sst_drv *sst,
+                                               struct ipc_post *msg)
+{
+       unsigned long irq_flags;
+
+       spin_lock_irqsave(&sst->ipc_spin_lock, irq_flags);
+       list_add_tail(&msg->node, &sst->ipc_dispatch_list);
+       spin_unlock_irqrestore(&sst->ipc_spin_lock, irq_flags);
+       sst->ops->post_message(sst, NULL, false);
+}
diff --git a/sound/soc/intel/sst/sst_stream.c b/sound/soc/intel/sst/sst_stream.c
new file mode 100644 (file)
index 0000000..dae2a41
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+ *  sst_stream.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14 Intel Corp
+ *  Authors:   Vinod Koul <vinod.koul@intel.com>
+ *             Harsha Priya <priya.harsha@intel.com>
+ *             Dharageswari R <dharageswari.r@intel.com>
+ *             KP Jeeja <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/pci.h>
+#include <linux/firmware.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params)
+{
+       struct snd_sst_alloc_mrfld alloc_param;
+       struct snd_sst_params *str_params;
+       struct snd_sst_tstamp fw_tstamp;
+       struct stream_info *str_info;
+       struct snd_sst_alloc_response *response;
+       unsigned int str_id, pipe_id, task_id;
+       int i, num_ch, ret = 0;
+       void *data = NULL;
+
+       dev_dbg(sst_drv_ctx->dev, "Enter\n");
+       BUG_ON(!params);
+
+       str_params = (struct snd_sst_params *)params;
+       memset(&alloc_param, 0, sizeof(alloc_param));
+       alloc_param.operation = str_params->ops;
+       alloc_param.codec_type = str_params->codec;
+       alloc_param.sg_count = str_params->aparams.sg_count;
+       alloc_param.ring_buf_info[0].addr =
+               str_params->aparams.ring_buf_info[0].addr;
+       alloc_param.ring_buf_info[0].size =
+               str_params->aparams.ring_buf_info[0].size;
+       alloc_param.frag_size = str_params->aparams.frag_size;
+
+       memcpy(&alloc_param.codec_params, &str_params->sparams,
+                       sizeof(struct snd_sst_stream_params));
+
+       /*
+        * fill channel map params for multichannel support.
+        * Ideally channel map should be received from upper layers
+        * for multichannel support.
+        * Currently hardcoding as per FW reqm.
+        */
+       num_ch = sst_get_num_channel(str_params);
+       for (i = 0; i < 8; i++) {
+               if (i < num_ch)
+                       alloc_param.codec_params.uc.pcm_params.channel_map[i] = i;
+               else
+                       alloc_param.codec_params.uc.pcm_params.channel_map[i] = 0xFF;
+       }
+
+       str_id = str_params->stream_id;
+       str_info = get_stream_info(sst_drv_ctx, str_id);
+       if (str_info == NULL) {
+               dev_err(sst_drv_ctx->dev, "get stream info returned null\n");
+               return -EINVAL;
+       }
+
+       pipe_id = str_params->device_type;
+       task_id = str_params->task;
+       sst_drv_ctx->streams[str_id].pipe_id = pipe_id;
+       sst_drv_ctx->streams[str_id].task_id = task_id;
+       sst_drv_ctx->streams[str_id].num_ch = num_ch;
+
+       if (sst_drv_ctx->info.lpe_viewpt_rqd)
+               alloc_param.ts = sst_drv_ctx->info.mailbox_start +
+                       sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp));
+       else
+               alloc_param.ts = sst_drv_ctx->mailbox_add +
+                       sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp));
+
+       dev_dbg(sst_drv_ctx->dev, "alloc tstamp location = 0x%x\n",
+                       alloc_param.ts);
+       dev_dbg(sst_drv_ctx->dev, "assigned pipe id 0x%x to task %d\n",
+                       pipe_id, task_id);
+
+       /* allocate device type context */
+       sst_init_stream(&sst_drv_ctx->streams[str_id], alloc_param.codec_type,
+                       str_id, alloc_param.operation, 0);
+
+       dev_info(sst_drv_ctx->dev, "Alloc for str %d pipe %#x\n",
+                       str_id, pipe_id);
+       ret = sst_prepare_and_post_msg(sst_drv_ctx, task_id, IPC_CMD,
+                       IPC_IA_ALLOC_STREAM_MRFLD, pipe_id, sizeof(alloc_param),
+                       &alloc_param, data, true, true, false, true);
+
+       if (ret < 0) {
+               dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);
+               /* alloc failed, so reset the state to uninit */
+               str_info->status = STREAM_UN_INIT;
+               str_id = ret;
+       } else if (data) {
+               response = (struct snd_sst_alloc_response *)data;
+               ret = response->str_type.result;
+               if (!ret)
+                       goto out;
+               dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);
+               if (ret == SST_ERR_STREAM_IN_USE) {
+                       dev_err(sst_drv_ctx->dev,
+                               "FW not in clean state, send free for:%d\n", str_id);
+                       sst_free_stream(sst_drv_ctx, str_id);
+               }
+               str_id = -ret;
+       }
+out:
+       kfree(data);
+       return str_id;
+}
+
+/**
+* sst_start_stream - Send msg for a starting stream
+* @str_id:      stream ID
+*
+* This function is called by any function which wants to start
+* a stream.
+*/
+int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+       int retval = 0;
+       struct stream_info *str_info;
+       u16 data = 0;
+
+       dev_dbg(sst_drv_ctx->dev, "sst_start_stream for %d\n", str_id);
+       str_info = get_stream_info(sst_drv_ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       if (str_info->status != STREAM_RUNNING)
+               return -EBADRQC;
+
+       retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
+                       IPC_CMD, IPC_IA_START_STREAM_MRFLD, str_info->pipe_id,
+                       sizeof(u16), &data, NULL, true, true, true, false);
+
+       return retval;
+}
+
+int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx,
+               struct snd_sst_bytes_v2 *bytes)
+{      struct ipc_post *msg = NULL;
+       u32 length;
+       int pvt_id, ret = 0;
+       struct sst_block *block = NULL;
+
+       dev_dbg(sst_drv_ctx->dev,
+               "type:%u ipc_msg:%u block:%u task_id:%u pipe: %#x length:%#x\n",
+               bytes->type, bytes->ipc_msg, bytes->block, bytes->task_id,
+               bytes->pipe_id, bytes->len);
+
+       if (sst_create_ipc_msg(&msg, true))
+               return -ENOMEM;
+
+       pvt_id = sst_assign_pvt_id(sst_drv_ctx);
+       sst_fill_header_mrfld(&msg->mrfld_header, bytes->ipc_msg,
+                       bytes->task_id, 1, pvt_id);
+       msg->mrfld_header.p.header_high.part.res_rqd = bytes->block;
+       length = bytes->len;
+       msg->mrfld_header.p.header_low_payload = length;
+       dev_dbg(sst_drv_ctx->dev, "length is %d\n", length);
+       memcpy(msg->mailbox_data, &bytes->bytes, bytes->len);
+       if (bytes->block) {
+               block = sst_create_block(sst_drv_ctx, bytes->ipc_msg, pvt_id);
+               if (block == NULL) {
+                       kfree(msg);
+                       ret = -ENOMEM;
+                       goto out;
+               }
+       }
+
+       sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg);
+       dev_dbg(sst_drv_ctx->dev, "msg->mrfld_header.p.header_low_payload:%d",
+                       msg->mrfld_header.p.header_low_payload);
+
+       if (bytes->block) {
+               ret = sst_wait_timeout(sst_drv_ctx, block);
+               if (ret) {
+                       dev_err(sst_drv_ctx->dev, "fw returned err %d\n", ret);
+                       sst_free_block(sst_drv_ctx, block);
+                       goto out;
+               }
+       }
+       if (bytes->type == SND_SST_BYTES_GET) {
+               /*
+                * copy the reply and send back
+                * we need to update only sz and payload
+                */
+               if (bytes->block) {
+                       unsigned char *r = block->data;
+
+                       dev_dbg(sst_drv_ctx->dev, "read back %d bytes",
+                                       bytes->len);
+                       memcpy(bytes->bytes, r, bytes->len);
+               }
+       }
+       if (bytes->block)
+               sst_free_block(sst_drv_ctx, block);
+out:
+       test_and_clear_bit(pvt_id, &sst_drv_ctx->pvt_id);
+       return 0;
+}
+
+/*
+ * sst_pause_stream - Send msg for a pausing stream
+ * @str_id:     stream ID
+ *
+ * This function is called by any function which wants to pause
+ * an already running stream.
+ */
+int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+       int retval = 0;
+       struct stream_info *str_info;
+
+       dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_pause_stream for %d\n", str_id);
+       str_info = get_stream_info(sst_drv_ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       if (str_info->status == STREAM_PAUSED)
+               return 0;
+       if (str_info->status == STREAM_RUNNING ||
+               str_info->status == STREAM_INIT) {
+               if (str_info->prev == STREAM_UN_INIT)
+                       return -EBADRQC;
+
+               retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
+                               IPC_IA_PAUSE_STREAM_MRFLD, str_info->pipe_id,
+                               0, NULL, NULL, true, true, false, true);
+
+               if (retval == 0) {
+                       str_info->prev = str_info->status;
+                       str_info->status = STREAM_PAUSED;
+               } else if (retval == SST_ERR_INVALID_STREAM_ID) {
+                       retval = -EINVAL;
+                       mutex_lock(&sst_drv_ctx->sst_lock);
+                       sst_clean_stream(str_info);
+                       mutex_unlock(&sst_drv_ctx->sst_lock);
+               }
+       } else {
+               retval = -EBADRQC;
+               dev_dbg(sst_drv_ctx->dev, "SST DBG:BADRQC for stream\n ");
+       }
+
+       return retval;
+}
+
+/**
+ * sst_resume_stream - Send msg for resuming stream
+ * @str_id:            stream ID
+ *
+ * This function is called by any function which wants to resume
+ * an already paused stream.
+ */
+int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+       int retval = 0;
+       struct stream_info *str_info;
+
+       dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_resume_stream for %d\n", str_id);
+       str_info = get_stream_info(sst_drv_ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       if (str_info->status == STREAM_RUNNING)
+                       return 0;
+       if (str_info->status == STREAM_PAUSED) {
+               retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
+                               IPC_CMD, IPC_IA_RESUME_STREAM_MRFLD,
+                               str_info->pipe_id, 0, NULL, NULL,
+                               true, true, false, true);
+
+               if (!retval) {
+                       if (str_info->prev == STREAM_RUNNING)
+                               str_info->status = STREAM_RUNNING;
+                       else
+                               str_info->status = STREAM_INIT;
+                       str_info->prev = STREAM_PAUSED;
+               } else if (retval == -SST_ERR_INVALID_STREAM_ID) {
+                       retval = -EINVAL;
+                       mutex_lock(&sst_drv_ctx->sst_lock);
+                       sst_clean_stream(str_info);
+                       mutex_unlock(&sst_drv_ctx->sst_lock);
+               }
+       } else {
+               retval = -EBADRQC;
+               dev_err(sst_drv_ctx->dev, "SST ERR: BADQRC for stream\n");
+       }
+
+       return retval;
+}
+
+
+/**
+ * sst_drop_stream - Send msg for stopping stream
+ * @str_id:            stream ID
+ *
+ * This function is called by any function which wants to stop
+ * a stream.
+ */
+int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+       int retval = 0;
+       struct stream_info *str_info;
+
+       dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_drop_stream for %d\n", str_id);
+       str_info = get_stream_info(sst_drv_ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+
+       if (str_info->status != STREAM_UN_INIT) {
+               str_info->prev = STREAM_UN_INIT;
+               str_info->status = STREAM_INIT;
+               str_info->cumm_bytes = 0;
+               retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
+                               IPC_CMD, IPC_IA_DROP_STREAM_MRFLD,
+                               str_info->pipe_id, 0, NULL, NULL,
+                               true, true, true, false);
+       } else {
+               retval = -EBADRQC;
+               dev_dbg(sst_drv_ctx->dev, "BADQRC for stream, state %x\n",
+                               str_info->status);
+       }
+       return retval;
+}
+
+/**
+* sst_drain_stream - Send msg for draining stream
+* @str_id:             stream ID
+*
+* This function is called by any function which wants to drain
+* a stream.
+*/
+int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx,
+                       int str_id, bool partial_drain)
+{
+       int retval = 0;
+       struct stream_info *str_info;
+
+       dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_drain_stream for %d\n", str_id);
+       str_info = get_stream_info(sst_drv_ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       if (str_info->status != STREAM_RUNNING &&
+               str_info->status != STREAM_INIT &&
+               str_info->status != STREAM_PAUSED) {
+                       dev_err(sst_drv_ctx->dev, "SST ERR: BADQRC for stream = %d\n",
+                                      str_info->status);
+                       return -EBADRQC;
+       }
+
+       retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
+                       IPC_IA_DRAIN_STREAM_MRFLD, str_info->pipe_id,
+                       sizeof(u8), &partial_drain, NULL, true, true, false, false);
+       /*
+        * with new non blocked drain implementation in core we dont need to
+        * wait for respsonse, and need to only invoke callback for drain
+        * complete
+        */
+
+       return retval;
+}
+
+/**
+ * sst_free_stream - Frees a stream
+ * @str_id:            stream ID
+ *
+ * This function is called by any function which wants to free
+ * a stream.
+ */
+int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+       int retval = 0;
+       struct stream_info *str_info;
+       struct intel_sst_ops *ops;
+
+       dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_free_stream for %d\n", str_id);
+
+       mutex_lock(&sst_drv_ctx->sst_lock);
+       if (sst_drv_ctx->sst_state == SST_RESET) {
+               mutex_unlock(&sst_drv_ctx->sst_lock);
+               return -ENODEV;
+       }
+       mutex_unlock(&sst_drv_ctx->sst_lock);
+       str_info = get_stream_info(sst_drv_ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       ops = sst_drv_ctx->ops;
+
+       mutex_lock(&str_info->lock);
+       if (str_info->status != STREAM_UN_INIT) {
+               str_info->prev =  str_info->status;
+               str_info->status = STREAM_UN_INIT;
+               mutex_unlock(&str_info->lock);
+
+               dev_info(sst_drv_ctx->dev, "Free for str %d pipe %#x\n",
+                               str_id, str_info->pipe_id);
+               retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
+                               IPC_IA_FREE_STREAM_MRFLD, str_info->pipe_id, 0,
+                               NULL, NULL, true, true, false, true);
+
+               dev_dbg(sst_drv_ctx->dev, "sst: wait for free returned %d\n",
+                               retval);
+               mutex_lock(&sst_drv_ctx->sst_lock);
+               sst_clean_stream(str_info);
+               mutex_unlock(&sst_drv_ctx->sst_lock);
+               dev_dbg(sst_drv_ctx->dev, "SST DBG:Stream freed\n");
+       } else {
+               mutex_unlock(&str_info->lock);
+               retval = -EBADRQC;
+               dev_dbg(sst_drv_ctx->dev, "SST DBG:BADQRC for stream\n");
+       }
+
+       return retval;
+}
index 5cb91f9..0fb7d2a 100644 (file)
@@ -77,25 +77,18 @@ static int qi_lb60_probe(struct platform_device *pdev)
 {
        struct qi_lb60 *qi_lb60;
        struct snd_soc_card *card = &qi_lb60_card;
-       int ret;
 
        qi_lb60 = devm_kzalloc(&pdev->dev, sizeof(*qi_lb60), GFP_KERNEL);
        if (!qi_lb60)
                return -ENOMEM;
 
-       qi_lb60->snd_gpio = devm_gpiod_get(&pdev->dev, "snd");
+       qi_lb60->snd_gpio = devm_gpiod_get(&pdev->dev, "snd", GPIOD_OUT_LOW);
        if (IS_ERR(qi_lb60->snd_gpio))
                return PTR_ERR(qi_lb60->snd_gpio);
-       ret = gpiod_direction_output(qi_lb60->snd_gpio, 0);
-       if (ret)
-               return ret;
 
-       qi_lb60->amp_gpio = devm_gpiod_get(&pdev->dev, "amp");
+       qi_lb60->amp_gpio = devm_gpiod_get(&pdev->dev, "amp", GPIOD_OUT_LOW);
        if (IS_ERR(qi_lb60->amp_gpio))
                return PTR_ERR(qi_lb60->amp_gpio);
-       ret = gpiod_direction_output(qi_lb60->amp_gpio, 0);
-       if (ret)
-               return ret;
 
        card->dev = &pdev->dev;
 
index 231d7e7..83b2fea 100644 (file)
@@ -773,7 +773,7 @@ static int mxs_saif_probe(struct platform_device *pdev)
 
        saif->dev = &pdev->dev;
        ret = devm_request_irq(&pdev->dev, saif->irq, mxs_saif_irq, 0,
-                              "mxs-saif", saif);
+                              dev_name(&pdev->dev), saif);
        if (ret) {
                dev_err(&pdev->dev, "failed to request irq\n");
                return ret;
index 61822cc..3bba6cf 100644 (file)
@@ -49,13 +49,6 @@ static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream,
                break;
        }
 
-       /* Sgtl5000 sysclk should be >= 8MHz and <= 27M */
-       if (mclk < 8000000 || mclk > 27000000) {
-               dev_err(codec_dai->dev, "Invalid mclk frequency: %u.%03uMHz\n",
-                       mclk / 1000000, mclk / 1000 % 1000);
-               return -EINVAL;
-       }
-
        /* Set SGTL5000's SYSCLK (provided by SAIF MCLK) */
        ret = snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, mclk, 0);
        if (ret) {
index f2f6794..dff443e 100644 (file)
@@ -298,7 +298,7 @@ static const struct snd_soc_dai_ops nuc900_ac97_dai_ops = {
 static struct snd_soc_dai_driver nuc900_ac97_dai = {
        .probe                  = nuc900_ac97_probe,
        .remove                 = nuc900_ac97_remove,
-       .ac97_control           = 1,
+       .bus_control            = true,
        .playback = {
                .rates          = SNDRV_PCM_RATE_8000_48000,
                .formats        = SNDRV_PCM_FMTBIT_S16_LE,
index d44463a..2738b19 100644 (file)
@@ -25,15 +25,15 @@ config SND_OMAP_SOC_N810
          Say Y if you want to add support for SoC audio on Nokia N810.
 
 config SND_OMAP_SOC_RX51
-       tristate "SoC Audio support for Nokia RX-51"
-       depends on SND_OMAP_SOC && ARM && (MACH_NOKIA_RX51 || COMPILE_TEST) && I2C
+       tristate "SoC Audio support for Nokia N900 (RX-51)"
+       depends on SND_OMAP_SOC && ARM && I2C
        select SND_OMAP_SOC_MCBSP
        select SND_SOC_TLV320AIC3X
        select SND_SOC_TPA6130A2
        depends on GPIOLIB
        help
-         Say Y if you want to add support for SoC audio on Nokia RX-51
-         hardware. This is also known as Nokia N900 product.
+         Say Y if you want to add support for SoC audio on Nokia N900
+         cellphone.
 
 config SND_OMAP_SOC_AMS_DELTA
        tristate "SoC Audio support for Amstrad E3 (Delta) videophone"
index 86c7538..68a1252 100644 (file)
@@ -621,8 +621,7 @@ void omap_mcbsp_free(struct omap_mcbsp *mcbsp)
        mcbsp->reg_cache = NULL;
        spin_unlock(&mcbsp->lock);
 
-       if (reg_cache)
-               kfree(reg_cache);
+       kfree(reg_cache);
 }
 
 /*
index 595eee3..a6b2be2 100644 (file)
@@ -127,15 +127,12 @@ static const struct snd_soc_dapm_route audio_map[] = {
 static int mioa701_wm9713_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_codec *codec = rtd->codec;
-       unsigned short reg;
 
        /* Prepare GPIO8 for rear speaker amplifier */
-       reg = codec->driver->read(codec, AC97_GPIO_CFG);
-       codec->driver->write(codec, AC97_GPIO_CFG, reg | 0x0100);
+       snd_soc_update_bits(codec, AC97_GPIO_CFG, 0x100, 0x100);
 
        /* Prepare MIC input */
-       reg = codec->driver->read(codec, AC97_3D_CONTROL);
-       codec->driver->write(codec, AC97_3D_CONTROL, reg | 0xc000);
+       snd_soc_update_bits(codec, AC97_3D_CONTROL, 0xc000, 0xc000);
 
        return 0;
 }
index a8e0974..cbba063 100644 (file)
@@ -97,7 +97,7 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream,
        int ret = 0;
 
        if (!cpu_dai->active) {
-               clk_enable(ssp->clk);
+               clk_prepare_enable(ssp->clk);
                pxa_ssp_disable(ssp);
        }
 
@@ -121,7 +121,7 @@ static void pxa_ssp_shutdown(struct snd_pcm_substream *substream,
 
        if (!cpu_dai->active) {
                pxa_ssp_disable(ssp);
-               clk_disable(ssp->clk);
+               clk_disable_unprepare(ssp->clk);
        }
 
        kfree(snd_soc_dai_get_dma_data(cpu_dai, substream));
@@ -136,7 +136,7 @@ static int pxa_ssp_suspend(struct snd_soc_dai *cpu_dai)
        struct ssp_device *ssp = priv->ssp;
 
        if (!cpu_dai->active)
-               clk_enable(ssp->clk);
+               clk_prepare_enable(ssp->clk);
 
        priv->cr0 = __raw_readl(ssp->mmio_base + SSCR0);
        priv->cr1 = __raw_readl(ssp->mmio_base + SSCR1);
@@ -144,7 +144,7 @@ static int pxa_ssp_suspend(struct snd_soc_dai *cpu_dai)
        priv->psp = __raw_readl(ssp->mmio_base + SSPSP);
 
        pxa_ssp_disable(ssp);
-       clk_disable(ssp->clk);
+       clk_disable_unprepare(ssp->clk);
        return 0;
 }
 
@@ -154,7 +154,7 @@ static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai)
        struct ssp_device *ssp = priv->ssp;
        uint32_t sssr = SSSR_ROR | SSSR_TUR | SSSR_BCE;
 
-       clk_enable(ssp->clk);
+       clk_prepare_enable(ssp->clk);
 
        __raw_writel(sssr, ssp->mmio_base + SSSR);
        __raw_writel(priv->cr0 & ~SSCR0_SSE, ssp->mmio_base + SSCR0);
@@ -165,7 +165,7 @@ static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai)
        if (cpu_dai->active)
                pxa_ssp_enable(ssp);
        else
-               clk_disable(ssp->clk);
+               clk_disable_unprepare(ssp->clk);
 
        return 0;
 }
@@ -256,11 +256,11 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
        /* The SSP clock must be disabled when changing SSP clock mode
         * on PXA2xx.  On PXA3xx it must be enabled when doing so. */
        if (ssp->type != PXA3xx_SSP)
-               clk_disable(ssp->clk);
+               clk_disable_unprepare(ssp->clk);
        val = pxa_ssp_read_reg(ssp, SSCR0) | sscr0;
        pxa_ssp_write_reg(ssp, SSCR0, val);
        if (ssp->type != PXA3xx_SSP)
-               clk_enable(ssp->clk);
+               clk_prepare_enable(ssp->clk);
 
        return 0;
 }
index ae956e3..73ca282 100644 (file)
@@ -157,7 +157,7 @@ static const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = {
 static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
 {
        .name = "pxa2xx-ac97",
-       .ac97_control = 1,
+       .bus_control = true,
        .playback = {
                .stream_name = "AC97 Playback",
                .channels_min = 2,
@@ -174,7 +174,7 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
 },
 {
        .name = "pxa2xx-ac97-aux",
-       .ac97_control = 1,
+       .bus_control = true,
        .playback = {
                .stream_name = "AC97 Aux Playback",
                .channels_min = 1,
@@ -191,7 +191,7 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
 },
 {
        .name = "pxa2xx-ac97-mic",
-       .ac97_control = 1,
+       .bus_control = true,
        .capture = {
                .stream_name = "AC97 Mic Capture",
                .channels_min = 1,
index 1373b01..d7d5fb2 100644 (file)
@@ -305,19 +305,15 @@ static struct snd_soc_card snd_soc_spitz = {
        .num_dapm_routes = ARRAY_SIZE(spitz_audio_map),
 };
 
-static struct platform_device *spitz_snd_device;
-
-static int __init spitz_init(void)
+static int spitz_probe(struct platform_device *pdev)
 {
+       struct snd_soc_card *card = &snd_soc_spitz;
        int ret;
 
-       if (!(machine_is_spitz() || machine_is_borzoi() || machine_is_akita()))
-               return -ENODEV;
-
-       if (machine_is_borzoi() || machine_is_spitz())
-               spitz_mic_gpio = SPITZ_GPIO_MIC_BIAS;
-       else
+       if (machine_is_akita())
                spitz_mic_gpio = AKITA_GPIO_MIC_BIAS;
+       else
+               spitz_mic_gpio = SPITZ_GPIO_MIC_BIAS;
 
        ret = gpio_request(spitz_mic_gpio, "MIC GPIO");
        if (ret)
@@ -327,37 +323,45 @@ static int __init spitz_init(void)
        if (ret)
                goto err2;
 
-       spitz_snd_device = platform_device_alloc("soc-audio", -1);
-       if (!spitz_snd_device) {
-               ret = -ENOMEM;
+       card->dev = &pdev->dev;
+
+       ret = snd_soc_register_card(card);
+       if (ret) {
+               dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+                       ret);
                goto err2;
        }
 
-       platform_set_drvdata(spitz_snd_device, &snd_soc_spitz);
-
-       ret = platform_device_add(spitz_snd_device);
-       if (ret)
-               goto err3;
-
        return 0;
 
-err3:
-       platform_device_put(spitz_snd_device);
 err2:
        gpio_free(spitz_mic_gpio);
 err1:
        return ret;
 }
 
-static void __exit spitz_exit(void)
+static int spitz_remove(struct platform_device *pdev)
 {
-       platform_device_unregister(spitz_snd_device);
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+       snd_soc_unregister_card(card);
        gpio_free(spitz_mic_gpio);
+       return 0;
 }
 
-module_init(spitz_init);
-module_exit(spitz_exit);
+static struct platform_driver spitz_driver = {
+       .driver         = {
+               .name   = "spitz-audio",
+               .owner  = THIS_MODULE,
+               .pm     = &snd_soc_pm_ops,
+       },
+       .probe          = spitz_probe,
+       .remove         = spitz_remove,
+};
+
+module_platform_driver(spitz_driver);
 
 MODULE_AUTHOR("Richard Purdie");
 MODULE_DESCRIPTION("ALSA SoC Spitz");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:spitz-audio");
index 78fc159..e181826 100644 (file)
@@ -1,11 +1,16 @@
 config SND_SOC_ROCKCHIP
        tristate "ASoC support for Rockchip"
        depends on COMPILE_TEST || ARCH_ROCKCHIP
-       select SND_SOC_GENERIC_DMAENGINE_PCM
        help
          Say Y or M if you want to add support for codecs attached to
          the Rockchip SoCs' Audio interfaces. You will also need to
          select the audio interfaces to support below.
 
 config SND_SOC_ROCKCHIP_I2S
-       tristate
+       tristate "Rockchip I2S Device Driver"
+       depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP
+       select SND_SOC_GENERIC_DMAENGINE_PCM
+       help
+         Say Y or M if you want to add support for I2S driver for
+         Rockchip I2S device. The device supports upto maximum of
+         8 channels each for play and record.
index f373e37..c74ba37 100644 (file)
@@ -154,8 +154,10 @@ static void rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on)
                        while (val) {
                                regmap_read(i2s->regmap, I2S_CLR, &val);
                                retry--;
-                               if (!retry)
+                               if (!retry) {
                                        dev_warn(i2s->dev, "fail to clear\n");
+                                       break;
+                               }
                        }
                }
        }
index 55a3869..fc67f97 100644 (file)
@@ -1,6 +1,6 @@
 config SND_SOC_SAMSUNG
        tristate "ASoC support for Samsung"
-       depends on PLAT_SAMSUNG
+       depends on (PLAT_SAMSUNG || ARCH_EXYNOS)
        depends on S3C64XX_PL080 || !ARCH_S3C64XX
        depends on S3C24XX_DMAC || !ARCH_S3C24XX
        select SND_SOC_GENERIC_DMAENGINE_PCM
@@ -239,3 +239,9 @@ config SND_SOC_ODROIDX2
        select SND_SAMSUNG_I2S
        help
          Say Y here to enable audio support for the Odroid-X2/U3.
+
+config SND_SOC_ARNDALE_RT5631_ALC5631
+        tristate "Audio support for RT5631(ALC5631) on Arndale Board"
+        depends on SND_SOC_SAMSUNG
+        select SND_SAMSUNG_I2S
+        select SND_SOC_RT5631
index 91505dd..31e3dba 100644 (file)
@@ -45,6 +45,7 @@ snd-soc-lowland-objs := lowland.o
 snd-soc-littlemill-objs := littlemill.o
 snd-soc-bells-objs := bells.o
 snd-soc-odroidx2-max98090-objs := odroidx2_max98090.o
+snd-soc-arndale-rt5631-objs := arndale_rt5631.o
 
 obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o
 obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
@@ -71,3 +72,4 @@ obj-$(CONFIG_SND_SOC_LOWLAND) += snd-soc-lowland.o
 obj-$(CONFIG_SND_SOC_LITTLEMILL) += snd-soc-littlemill.o
 obj-$(CONFIG_SND_SOC_BELLS) += snd-soc-bells.o
 obj-$(CONFIG_SND_SOC_ODROIDX2) += snd-soc-odroidx2-max98090.o
+obj-$(CONFIG_SND_SOC_ARNDALE_RT5631_ALC5631) += snd-soc-arndale-rt5631.o
index e161511..7952a62 100644 (file)
@@ -288,7 +288,7 @@ static int s3c_ac97_mic_dai_probe(struct snd_soc_dai *dai)
 static struct snd_soc_dai_driver s3c_ac97_dai[] = {
        [S3C_AC97_DAI_PCM] = {
                .name = "samsung-ac97",
-               .ac97_control = 1,
+               .bus_control = true,
                .playback = {
                        .stream_name = "AC97 Playback",
                        .channels_min = 2,
@@ -306,7 +306,7 @@ static struct snd_soc_dai_driver s3c_ac97_dai[] = {
        },
        [S3C_AC97_DAI_MIC] = {
                .name = "samsung-ac97-mic",
-               .ac97_control = 1,
+               .bus_control = true,
                .capture = {
                        .stream_name = "AC97 Mic Capture",
                        .channels_min = 1,
diff --git a/sound/soc/samsung/arndale_rt5631.c b/sound/soc/samsung/arndale_rt5631.c
new file mode 100644 (file)
index 0000000..1e2b61c
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ *  arndale_rt5631.c
+ *
+ *  Copyright (c) 2014, Insignal Co., Ltd.
+ *
+ *  Author: Claude <claude@insginal.co.kr>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include "i2s.h"
+
+static int arndale_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       int rfs, ret;
+       unsigned long rclk;
+
+       rfs = 256;
+
+       rclk = params_rate(params) * rfs;
+
+       ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,
+                                       0, SND_SOC_CLOCK_OUT);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0,
+                                       0, SND_SOC_CLOCK_OUT);
+
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0, rclk, SND_SOC_CLOCK_OUT);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static struct snd_soc_ops arndale_ops = {
+       .hw_params = arndale_hw_params,
+};
+
+static struct snd_soc_dai_link arndale_rt5631_dai[] = {
+       {
+               .name = "RT5631 HiFi",
+               .stream_name = "Primary",
+               .codec_dai_name = "rt5631-hifi",
+               .dai_fmt = SND_SOC_DAIFMT_I2S
+                       | SND_SOC_DAIFMT_NB_NF
+                       | SND_SOC_DAIFMT_CBS_CFS,
+               .ops = &arndale_ops,
+       },
+};
+
+static struct snd_soc_card arndale_rt5631 = {
+       .name = "Arndale RT5631",
+       .dai_link = arndale_rt5631_dai,
+       .num_links = ARRAY_SIZE(arndale_rt5631_dai),
+};
+
+static int arndale_audio_probe(struct platform_device *pdev)
+{
+       int n, ret;
+       struct device_node *np = pdev->dev.of_node;
+       struct snd_soc_card *card = &arndale_rt5631;
+
+       card->dev = &pdev->dev;
+
+       for (n = 0; np && n < ARRAY_SIZE(arndale_rt5631_dai); n++) {
+               if (!arndale_rt5631_dai[n].cpu_dai_name) {
+                       arndale_rt5631_dai[n].cpu_of_node = of_parse_phandle(np,
+                                       "samsung,audio-cpu", n);
+
+                       if (!arndale_rt5631_dai[n].cpu_of_node) {
+                               dev_err(&pdev->dev,
+                               "Property 'samsung,audio-cpu' missing or invalid\n");
+                               return -EINVAL;
+                       }
+               }
+               if (!arndale_rt5631_dai[n].platform_name)
+                       arndale_rt5631_dai[n].platform_of_node =
+                                       arndale_rt5631_dai[n].cpu_of_node;
+
+               arndale_rt5631_dai[n].codec_name = NULL;
+               arndale_rt5631_dai[n].codec_of_node = of_parse_phandle(np,
+                                       "samsung,audio-codec", n);
+               if (!arndale_rt5631_dai[0].codec_of_node) {
+                       dev_err(&pdev->dev,
+                       "Property 'samsung,audio-codec' missing or invalid\n");
+                       return -EINVAL;
+               }
+       }
+
+       ret = devm_snd_soc_register_card(card->dev, card);
+
+       if (ret)
+               dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret);
+
+       return ret;
+}
+
+static int arndale_audio_remove(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+       snd_soc_unregister_card(card);
+
+       return 0;
+}
+
+static const struct of_device_id samsung_arndale_rt5631_of_match[] __maybe_unused = {
+       { .compatible = "samsung,arndale-rt5631", },
+       { .compatible = "samsung,arndale-alc5631", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, samsung_arndale_rt5631_of_match);
+
+static struct platform_driver arndale_audio_driver = {
+       .driver = {
+               .name   = "arndale-audio",
+               .owner  = THIS_MODULE,
+               .pm = &snd_soc_pm_ops,
+               .of_match_table = of_match_ptr(samsung_arndale_rt5631_of_match),
+       },
+       .probe = arndale_audio_probe,
+       .remove = arndale_audio_remove,
+};
+
+module_platform_driver(arndale_audio_driver);
+
+MODULE_AUTHOR("Claude <claude@insignal.co.kr>");
+MODULE_DESCRIPTION("ALSA SoC Driver for Arndale Board");
+MODULE_LICENSE("GPL");
index 821a502..9170c31 100644 (file)
@@ -33,8 +33,9 @@
 #define I2SLVL3ADDR    0x3c
 #define I2SSTR1                0x40
 #define I2SVER         0x44
-#define I2SFIC2                0x48
+#define I2SFIC1                0x48
 #define I2STDM         0x4c
+#define I2SFSTA                0x50
 
 #define CON_RSTCLR             (1 << 31)
 #define CON_FRXOFSTATUS                (1 << 26)
@@ -93,8 +94,6 @@
 #define MOD_BLC_24BIT          (2 << 13)
 #define MOD_BLC_MASK           (3 << 13)
 
-#define MOD_IMS_SYSMUX         (1 << 10)
-#define MOD_SLAVE              (1 << 11)
 #define MOD_TXONLY             (0 << 8)
 #define MOD_RXONLY             (1 << 8)
 #define MOD_TXRX               (2 << 8)
 #define EXYNOS5420_MOD_BCLK_256FS      8
 #define EXYNOS5420_MOD_BCLK_MASK       0xf
 
-#define MOD_CDCLKCON           (1 << 12)
+#define EXYNOS7_MOD_RCLK_64FS  4
+#define EXYNOS7_MOD_RCLK_128FS 5
+#define EXYNOS7_MOD_RCLK_96FS  6
+#define EXYNOS7_MOD_RCLK_192FS 7
 
 #define PSR_PSREN              (1 << 15)
 
index 9d51347..c7aafcd 100644 (file)
@@ -36,9 +36,24 @@ enum samsung_dai_type {
        TYPE_SEC,
 };
 
+struct samsung_i2s_variant_regs {
+       unsigned int    bfs_off;
+       unsigned int    rfs_off;
+       unsigned int    sdf_off;
+       unsigned int    txr_off;
+       unsigned int    rclksrc_off;
+       unsigned int    mss_off;
+       unsigned int    cdclkcon_off;
+       unsigned int    lrp_off;
+       unsigned int    bfs_mask;
+       unsigned int    rfs_mask;
+       unsigned int    ftx0cnt_off;
+};
+
 struct samsung_i2s_dai_data {
        int dai_type;
        u32 quirks;
+       const struct samsung_i2s_variant_regs *i2s_variant_regs;
 };
 
 struct i2s_dai {
@@ -81,6 +96,7 @@ struct i2s_dai {
        u32     suspend_i2scon;
        u32     suspend_i2spsr;
        unsigned long gpios[7]; /* i2s gpio line numbers */
+       const struct samsung_i2s_variant_regs *variant_regs;
 };
 
 /* Lock for cross i/f checks */
@@ -95,7 +111,8 @@ static inline bool is_secondary(struct i2s_dai *i2s)
 /* If operating in SoC-Slave mode */
 static inline bool is_slave(struct i2s_dai *i2s)
 {
-       return (readl(i2s->addr + I2SMOD) & MOD_SLAVE) ? true : false;
+       u32 mod = readl(i2s->addr + I2SMOD);
+       return (mod & (1 << i2s->variant_regs->mss_off)) ? true : false;
 }
 
 /* If this interface of the controller is transmitting data */
@@ -200,14 +217,14 @@ static inline bool is_manager(struct i2s_dai *i2s)
 static inline unsigned get_rfs(struct i2s_dai *i2s)
 {
        u32 rfs;
-
-       if (i2s->quirks & QUIRK_SUPPORTS_TDM)
-               rfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_RCLK_SHIFT;
-       else
-               rfs = (readl(i2s->addr + I2SMOD) >> MOD_RCLK_SHIFT);
-       rfs &= MOD_RCLK_MASK;
+       rfs = readl(i2s->addr + I2SMOD) >> i2s->variant_regs->rfs_off;
+       rfs &= i2s->variant_regs->rfs_mask;
 
        switch (rfs) {
+       case 7: return 192;
+       case 6: return 96;
+       case 5: return 128;
+       case 4: return 64;
        case 3: return 768;
        case 2: return 384;
        case 1: return 512;
@@ -219,15 +236,23 @@ static inline unsigned get_rfs(struct i2s_dai *i2s)
 static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs)
 {
        u32 mod = readl(i2s->addr + I2SMOD);
-       int rfs_shift;
+       int rfs_shift = i2s->variant_regs->rfs_off;
 
-       if (i2s->quirks & QUIRK_SUPPORTS_TDM)
-               rfs_shift = EXYNOS5420_MOD_RCLK_SHIFT;
-       else
-               rfs_shift = MOD_RCLK_SHIFT;
-       mod &= ~(MOD_RCLK_MASK << rfs_shift);
+       mod &= ~(i2s->variant_regs->rfs_mask << rfs_shift);
 
        switch (rfs) {
+       case 192:
+               mod |= (EXYNOS7_MOD_RCLK_192FS << rfs_shift);
+               break;
+       case 96:
+               mod |= (EXYNOS7_MOD_RCLK_96FS << rfs_shift);
+               break;
+       case 128:
+               mod |= (EXYNOS7_MOD_RCLK_128FS << rfs_shift);
+               break;
+       case 64:
+               mod |= (EXYNOS7_MOD_RCLK_64FS << rfs_shift);
+               break;
        case 768:
                mod |= (MOD_RCLK_768FS << rfs_shift);
                break;
@@ -249,14 +274,8 @@ static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs)
 static inline unsigned get_bfs(struct i2s_dai *i2s)
 {
        u32 bfs;
-
-       if (i2s->quirks & QUIRK_SUPPORTS_TDM) {
-               bfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_BCLK_SHIFT;
-               bfs &= EXYNOS5420_MOD_BCLK_MASK;
-       } else {
-               bfs =  readl(i2s->addr + I2SMOD) >> MOD_BCLK_SHIFT;
-               bfs &= MOD_BCLK_MASK;
-       }
+       bfs = readl(i2s->addr + I2SMOD) >> i2s->variant_regs->bfs_off;
+       bfs &= i2s->variant_regs->bfs_mask;
 
        switch (bfs) {
        case 8: return 256;
@@ -275,16 +294,8 @@ static inline unsigned get_bfs(struct i2s_dai *i2s)
 static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs)
 {
        u32 mod = readl(i2s->addr + I2SMOD);
-       int bfs_shift;
        int tdm = i2s->quirks & QUIRK_SUPPORTS_TDM;
-
-       if (i2s->quirks & QUIRK_SUPPORTS_TDM) {
-               bfs_shift = EXYNOS5420_MOD_BCLK_SHIFT;
-               mod &= ~(EXYNOS5420_MOD_BCLK_MASK << bfs_shift);
-       } else {
-               bfs_shift = MOD_BCLK_SHIFT;
-               mod &= ~(MOD_BCLK_MASK << bfs_shift);
-       }
+       int bfs_shift = i2s->variant_regs->bfs_off;
 
        /* Non-TDM I2S controllers do not support BCLK > 48 * FS */
        if (!tdm && bfs > 48) {
@@ -292,6 +303,8 @@ static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs)
                return;
        }
 
+       mod &= ~(i2s->variant_regs->bfs_mask << bfs_shift);
+
        switch (bfs) {
        case 48:
                mod |= (MOD_BCLK_48FS << bfs_shift);
@@ -346,8 +359,9 @@ static inline int get_blc(struct i2s_dai *i2s)
 static void i2s_txctrl(struct i2s_dai *i2s, int on)
 {
        void __iomem *addr = i2s->addr;
+       int txr_off = i2s->variant_regs->txr_off;
        u32 con = readl(addr + I2SCON);
-       u32 mod = readl(addr + I2SMOD) & ~MOD_MASK;
+       u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off);
 
        if (on) {
                con |= CON_ACTIVE;
@@ -362,9 +376,9 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on)
                }
 
                if (any_rx_active(i2s))
-                       mod |= MOD_TXRX;
+                       mod |= 2 << txr_off;
                else
-                       mod |= MOD_TXONLY;
+                       mod |= 0 << txr_off;
        } else {
                if (is_secondary(i2s)) {
                        con |=  CON_TXSDMA_PAUSE;
@@ -382,7 +396,7 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on)
                con |=  CON_TXCH_PAUSE;
 
                if (any_rx_active(i2s))
-                       mod |= MOD_RXONLY;
+                       mod |= 1 << txr_off;
                else
                        con &= ~CON_ACTIVE;
        }
@@ -395,23 +409,24 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on)
 static void i2s_rxctrl(struct i2s_dai *i2s, int on)
 {
        void __iomem *addr = i2s->addr;
+       int txr_off = i2s->variant_regs->txr_off;
        u32 con = readl(addr + I2SCON);
-       u32 mod = readl(addr + I2SMOD) & ~MOD_MASK;
+       u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off);
 
        if (on) {
                con |= CON_RXDMA_ACTIVE | CON_ACTIVE;
                con &= ~(CON_RXDMA_PAUSE | CON_RXCH_PAUSE);
 
                if (any_tx_active(i2s))
-                       mod |= MOD_TXRX;
+                       mod |= 2 << txr_off;
                else
-                       mod |= MOD_RXONLY;
+                       mod |= 1 << txr_off;
        } else {
                con |=  CON_RXDMA_PAUSE | CON_RXCH_PAUSE;
                con &= ~CON_RXDMA_ACTIVE;
 
                if (any_tx_active(i2s))
-                       mod |= MOD_TXONLY;
+                       mod |= 0 << txr_off;
                else
                        con &= ~CON_ACTIVE;
        }
@@ -451,6 +466,9 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
        struct i2s_dai *i2s = to_info(dai);
        struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
        u32 mod = readl(i2s->addr + I2SMOD);
+       const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs;
+       unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off;
+       unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off;
 
        switch (clk_id) {
        case SAMSUNG_I2S_OPCLK:
@@ -465,18 +483,18 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
                if ((rfs && other && other->rfs && (other->rfs != rfs)) ||
                                (any_active(i2s) &&
                                (((dir == SND_SOC_CLOCK_IN)
-                                       && !(mod & MOD_CDCLKCON)) ||
+                                       && !(mod & cdcon_mask)) ||
                                ((dir == SND_SOC_CLOCK_OUT)
-                                       && (mod & MOD_CDCLKCON))))) {
+                                       && (mod & cdcon_mask))))) {
                        dev_err(&i2s->pdev->dev,
                                "%s:%d Other DAI busy\n", __func__, __LINE__);
                        return -EAGAIN;
                }
 
                if (dir == SND_SOC_CLOCK_IN)
-                       mod |= MOD_CDCLKCON;
+                       mod |= 1 << i2s_regs->cdclkcon_off;
                else
-                       mod &= ~MOD_CDCLKCON;
+                       mod &= ~(1 << i2s_regs->cdclkcon_off);
 
                i2s->rfs = rfs;
                break;
@@ -491,8 +509,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
 
                if (!any_active(i2s)) {
                        if (i2s->op_clk && !IS_ERR(i2s->op_clk)) {
-                               if ((clk_id && !(mod & MOD_IMS_SYSMUX)) ||
-                                       (!clk_id && (mod & MOD_IMS_SYSMUX))) {
+                               if ((clk_id && !(mod & rsrc_mask)) ||
+                                       (!clk_id && (mod & rsrc_mask))) {
                                        clk_disable_unprepare(i2s->op_clk);
                                        clk_put(i2s->op_clk);
                                } else {
@@ -520,8 +538,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
                                other->op_clk = i2s->op_clk;
                                other->rclk_srcrate = i2s->rclk_srcrate;
                        }
-               } else if ((!clk_id && (mod & MOD_IMS_SYSMUX))
-                               || (clk_id && !(mod & MOD_IMS_SYSMUX))) {
+               } else if ((!clk_id && (mod & rsrc_mask))
+                               || (clk_id && !(mod & rsrc_mask))) {
                        dev_err(&i2s->pdev->dev,
                                "%s:%d Other DAI busy\n", __func__, __LINE__);
                        return -EAGAIN;
@@ -533,11 +551,11 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
                }
 
                if (clk_id == 0)
-                       mod &= ~MOD_IMS_SYSMUX;
+                       mod &= ~(1 << i2s_regs->rclksrc_off);
                else
-                       mod |= MOD_IMS_SYSMUX;
-               break;
+                       mod |= 1 << i2s_regs->rclksrc_off;
 
+               break;
        default:
                dev_err(&i2s->pdev->dev, "We don't serve that!\n");
                return -EINVAL;
@@ -553,16 +571,12 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
 {
        struct i2s_dai *i2s = to_info(dai);
        u32 mod = readl(i2s->addr + I2SMOD);
-       int lrp_shift, sdf_shift, sdf_mask, lrp_rlow;
+       int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave;
        u32 tmp = 0;
 
-       if (i2s->quirks & QUIRK_SUPPORTS_TDM) {
-               lrp_shift = EXYNOS5420_MOD_LRP_SHIFT;
-               sdf_shift = EXYNOS5420_MOD_SDF_SHIFT;
-       } else {
-               lrp_shift = MOD_LRP_SHIFT;
-               sdf_shift = MOD_SDF_SHIFT;
-       }
+       lrp_shift = i2s->variant_regs->lrp_off;
+       sdf_shift = i2s->variant_regs->sdf_off;
+       mod_slave = 1 << i2s->variant_regs->mss_off;
 
        sdf_mask = MOD_SDF_MASK << sdf_shift;
        lrp_rlow = MOD_LR_RLOW << lrp_shift;
@@ -605,7 +619,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
 
        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
        case SND_SOC_DAIFMT_CBM_CFM:
-               tmp |= MOD_SLAVE;
+               tmp |= mod_slave;
                break;
        case SND_SOC_DAIFMT_CBS_CFS:
                /* Set default source clock in Master mode */
@@ -623,13 +637,13 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
         * channel.
         */
        if (any_active(i2s) &&
-               ((mod & (sdf_mask | lrp_rlow | MOD_SLAVE)) != tmp)) {
+               ((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) {
                dev_err(&i2s->pdev->dev,
                                "%s:%d Other DAI busy\n", __func__, __LINE__);
                return -EAGAIN;
        }
 
-       mod &= ~(sdf_mask | lrp_rlow | MOD_SLAVE);
+       mod &= ~(sdf_mask | lrp_rlow | mod_slave);
        mod |= tmp;
        writel(mod, i2s->addr + I2SMOD);
 
@@ -751,6 +765,7 @@ static void i2s_shutdown(struct snd_pcm_substream *substream,
        struct i2s_dai *i2s = to_info(dai);
        struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
        unsigned long flags;
+       const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs;
 
        spin_lock_irqsave(&lock, flags);
 
@@ -761,7 +776,7 @@ static void i2s_shutdown(struct snd_pcm_substream *substream,
                other->mode |= DAI_MANAGER;
        } else {
                u32 mod = readl(i2s->addr + I2SMOD);
-               i2s->cdclk_out = !(mod & MOD_CDCLKCON);
+               i2s->cdclk_out = !(mod & (1 << i2s_regs->cdclkcon_off));
                if (other)
                        other->cdclk_out = i2s->cdclk_out;
        }
@@ -914,13 +929,14 @@ i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
        struct i2s_dai *i2s = to_info(dai);
        u32 reg = readl(i2s->addr + I2SFIC);
        snd_pcm_sframes_t delay;
+       const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs;
 
        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
                delay = FIC_RXCOUNT(reg);
        else if (is_secondary(i2s))
                delay = FICS_TXCOUNT(readl(i2s->addr + I2SFICS));
        else
-               delay = FIC_TXCOUNT(reg);
+               delay = (reg >> i2s_regs->ftx0cnt_off) & 0x7f;
 
        return delay;
 }
@@ -956,6 +972,7 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
 {
        struct i2s_dai *i2s = to_info(dai);
        struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
+       int ret;
 
        if (other && other->clk) { /* If this is probe on secondary */
                samsung_asoc_init_dma_data(dai, &other->sec_dai->dma_playback,
@@ -973,9 +990,14 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
        if (IS_ERR(i2s->clk)) {
                dev_err(&i2s->pdev->dev, "failed to get i2s_clock\n");
                iounmap(i2s->addr);
-               return -ENOENT;
+               return PTR_ERR(i2s->clk);
+       }
+
+       ret = clk_prepare_enable(i2s->clk);
+       if (ret != 0) {
+               dev_err(&i2s->pdev->dev, "failed to enable clock: %d\n", ret);
+               return ret;
        }
-       clk_prepare_enable(i2s->clk);
 
        samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture);
 
@@ -987,7 +1009,7 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
        if (i2s->quirks & QUIRK_NEED_RSTCLR)
                writel(CON_RSTCLR, i2s->addr + I2SCON);
 
-       if (i2s->quirks & QUIRK_SEC_DAI)
+       if (i2s->quirks & QUIRK_SUPPORTS_IDMA)
                idma_reg_addr_init(i2s->addr,
                                        i2s->sec_dai->idma_playback.dma_addr);
 
@@ -1199,10 +1221,9 @@ static int samsung_i2s_probe(struct platform_device *pdev)
                quirks = i2s_dai_data->quirks;
                if (of_property_read_u32(np, "samsung,idma-addr",
                                         &idma_addr)) {
-                       if (quirks & QUIRK_SEC_DAI) {
-                               dev_err(&pdev->dev, "idma address is not"\
+                       if (quirks & QUIRK_SUPPORTS_IDMA) {
+                               dev_info(&pdev->dev, "idma address is not"\
                                                "specified");
-                               return -EINVAL;
                        }
                }
        }
@@ -1228,6 +1249,7 @@ static int samsung_i2s_probe(struct platform_device *pdev)
        pri_dai->dma_capture.dma_size = 4;
        pri_dai->base = regs_base;
        pri_dai->quirks = quirks;
+       pri_dai->variant_regs = i2s_dai_data->i2s_variant_regs;
 
        if (quirks & QUIRK_PRI_6CHAN)
                pri_dai->i2s_dai_drv.playback.channels_max = 6;
@@ -1302,20 +1324,93 @@ static int samsung_i2s_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct samsung_i2s_variant_regs i2sv3_regs = {
+       .bfs_off = 1,
+       .rfs_off = 3,
+       .sdf_off = 5,
+       .txr_off = 8,
+       .rclksrc_off = 10,
+       .mss_off = 11,
+       .cdclkcon_off = 12,
+       .lrp_off = 7,
+       .bfs_mask = 0x3,
+       .rfs_mask = 0x3,
+       .ftx0cnt_off = 8,
+};
+
+static const struct samsung_i2s_variant_regs i2sv6_regs = {
+       .bfs_off = 0,
+       .rfs_off = 4,
+       .sdf_off = 6,
+       .txr_off = 8,
+       .rclksrc_off = 10,
+       .mss_off = 11,
+       .cdclkcon_off = 12,
+       .lrp_off = 15,
+       .bfs_mask = 0xf,
+       .rfs_mask = 0x3,
+       .ftx0cnt_off = 8,
+};
+
+static const struct samsung_i2s_variant_regs i2sv7_regs = {
+       .bfs_off = 0,
+       .rfs_off = 4,
+       .sdf_off = 7,
+       .txr_off = 9,
+       .rclksrc_off = 11,
+       .mss_off = 12,
+       .cdclkcon_off = 22,
+       .lrp_off = 15,
+       .bfs_mask = 0xf,
+       .rfs_mask = 0x7,
+       .ftx0cnt_off = 0,
+};
+
+static const struct samsung_i2s_variant_regs i2sv5_i2s1_regs = {
+       .bfs_off = 0,
+       .rfs_off = 3,
+       .sdf_off = 6,
+       .txr_off = 8,
+       .rclksrc_off = 10,
+       .mss_off = 11,
+       .cdclkcon_off = 12,
+       .lrp_off = 15,
+       .bfs_mask = 0x7,
+       .rfs_mask = 0x7,
+       .ftx0cnt_off = 8,
+};
+
 static const struct samsung_i2s_dai_data i2sv3_dai_type = {
        .dai_type = TYPE_PRI,
        .quirks = QUIRK_NO_MUXPSR,
+       .i2s_variant_regs = &i2sv3_regs,
 };
 
 static const struct samsung_i2s_dai_data i2sv5_dai_type = {
        .dai_type = TYPE_PRI,
-       .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR,
+       .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
+                       QUIRK_SUPPORTS_IDMA,
+       .i2s_variant_regs = &i2sv3_regs,
 };
 
 static const struct samsung_i2s_dai_data i2sv6_dai_type = {
+       .dai_type = TYPE_PRI,
+       .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
+                       QUIRK_SUPPORTS_TDM | QUIRK_SUPPORTS_IDMA,
+       .i2s_variant_regs = &i2sv6_regs,
+};
+
+static const struct samsung_i2s_dai_data i2sv7_dai_type = {
        .dai_type = TYPE_PRI,
        .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
                        QUIRK_SUPPORTS_TDM,
+       .i2s_variant_regs = &i2sv7_regs,
+};
+
+static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 = {
+       .dai_type = TYPE_PRI,
+       .quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR,
+       .i2s_variant_regs = &i2sv5_i2s1_regs,
 };
 
 static const struct samsung_i2s_dai_data samsung_dai_type_pri = {
@@ -1329,10 +1424,13 @@ static const struct samsung_i2s_dai_data samsung_dai_type_sec = {
 static struct platform_device_id samsung_i2s_driver_ids[] = {
        {
                .name           = "samsung-i2s",
-               .driver_data    = (kernel_ulong_t)&samsung_dai_type_pri,
+               .driver_data    = (kernel_ulong_t)&i2sv3_dai_type,
        }, {
                .name           = "samsung-i2s-sec",
                .driver_data    = (kernel_ulong_t)&samsung_dai_type_sec,
+       }, {
+               .name           = "samsung-i2sv4",
+               .driver_data    = (kernel_ulong_t)&i2sv5_dai_type,
        },
        {},
 };
@@ -1349,6 +1447,12 @@ static const struct of_device_id exynos_i2s_match[] = {
        }, {
                .compatible = "samsung,exynos5420-i2s",
                .data = &i2sv6_dai_type,
+       }, {
+               .compatible = "samsung,exynos7-i2s",
+               .data = &i2sv7_dai_type,
+       }, {
+               .compatible = "samsung,exynos7-i2s1",
+               .data = &i2sv5_dai_type_i2s1,
        },
        {},
 };
index 3c8f604..d7640e7 100644 (file)
@@ -153,8 +153,8 @@ static int odroidx2_audio_remove(struct platform_device *pdev)
 
        snd_soc_unregister_card(card);
 
-       of_node_put((struct device_node *)odroidx2_dai[0].cpu_of_node);
-       of_node_put((struct device_node *)odroidx2_dai[0].codec_of_node);
+       of_node_put(odroidx2_dai[0].cpu_of_node);
+       of_node_put(odroidx2_dai[0].codec_of_node);
 
        return 0;
 }
index 0acf5d0..72118a7 100644 (file)
@@ -110,6 +110,7 @@ static const struct of_device_id snow_of_match[] = {
        { .compatible = "google,snow-audio-max98095", },
        {},
 };
+MODULE_DEVICE_TABLE(of, snow_of_match);
 
 static struct platform_driver snow_driver = {
        .driver = {
index 66fddec..8869971 100644 (file)
@@ -842,12 +842,9 @@ static int fsi_clk_disable(struct device *dev,
                return -EINVAL;
 
        if (1 == clock->count--) {
-               if (clock->xck)
-                       clk_disable(clock->xck);
-               if (clock->ick)
-                       clk_disable(clock->ick);
-               if (clock->div)
-                       clk_disable(clock->div);
+               clk_disable(clock->xck);
+               clk_disable(clock->ick);
+               clk_disable(clock->div);
        }
 
        return 0;
@@ -1711,8 +1708,7 @@ static const struct snd_soc_dai_ops fsi_dai_ops = {
 static struct snd_pcm_hardware fsi_pcm_hardware = {
        .info =         SNDRV_PCM_INFO_INTERLEAVED      |
                        SNDRV_PCM_INFO_MMAP             |
-                       SNDRV_PCM_INFO_MMAP_VALID       |
-                       SNDRV_PCM_INFO_PAUSE,
+                       SNDRV_PCM_INFO_MMAP_VALID,
        .buffer_bytes_max       = 64 * 1024,
        .period_bytes_min       = 32,
        .period_bytes_max       = 8192,
index 0af2e4d..d5f567e 100644 (file)
@@ -272,7 +272,7 @@ static const struct snd_soc_dai_ops hac_dai_ops = {
 static struct snd_soc_dai_driver sh4_hac_dai[] = {
 {
        .name                   = "hac-dai.0",
-       .ac97_control           = 1,
+       .bus_control            = true,
        .playback = {
                .rates          = AC97_RATES,
                .formats        = AC97_FMTS,
index fc41a0e..14d1a71 100644 (file)
@@ -430,7 +430,7 @@ int rsnd_adg_probe(struct platform_device *pdev,
        adg->clk[CLKI]  = devm_clk_get(dev, "clk_i");
 
        for_each_rsnd_clk(clk, adg, i)
-               dev_dbg(dev, "clk %d : %p\n", i, clk);
+               dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
 
        rsnd_adg_ssi_clk_init(priv, adg);
 
index 1922ec5..75308bb 100644 (file)
@@ -349,7 +349,7 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
                                                     dma_name);
        if (!dma->chan) {
                dev_err(dev, "can't get dma channel\n");
-               return -EIO;
+               goto rsnd_dma_channel_err;
        }
 
        ret = dmaengine_slave_config(dma->chan, &cfg);
@@ -363,8 +363,15 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
 
 rsnd_dma_init_err:
        rsnd_dma_quit(priv, dma);
+rsnd_dma_channel_err:
 
-       return ret;
+       /*
+        * DMA failed. try to PIO mode
+        * see
+        *      rsnd_ssi_fallback()
+        *      rsnd_rdai_continuance_probe()
+        */
+       return -EAGAIN;
 }
 
 void  rsnd_dma_quit(struct rsnd_priv *priv,
@@ -409,9 +416,16 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod)
 ({                                                             \
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);         \
        struct device *dev = rsnd_priv_to_dev(priv);            \
-       dev_dbg(dev, "%s [%d] %s\n",                            \
-               rsnd_mod_name(mod), rsnd_mod_id(mod), #func);   \
-       (mod)->ops->func(mod, rdai);                            \
+       u32 mask = 1 << __rsnd_mod_shift_##func;                        \
+       u32 call = __rsnd_mod_call_##func << __rsnd_mod_shift_##func;   \
+       int ret = 0;                                                    \
+       if ((mod->status & mask) == call) {                             \
+               dev_dbg(dev, "%s[%d] %s\n",                             \
+                       rsnd_mod_name(mod), rsnd_mod_id(mod), #func);   \
+               ret = (mod)->ops->func(mod, rdai);                      \
+               mod->status = (mod->status & ~mask) | (~call & mask);   \
+       }                                                               \
+       ret;                                                            \
 })
 
 #define rsnd_mod_call(mod, func, rdai...)      \
@@ -456,6 +470,13 @@ static int rsnd_dai_connect(struct rsnd_mod *mod,
        return 0;
 }
 
+static void rsnd_dai_disconnect(struct rsnd_mod *mod,
+                               struct rsnd_dai_stream *io)
+{
+       mod->io = NULL;
+       io->mod[mod->type] = NULL;
+}
+
 int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai)
 {
        int id = rdai - priv->rdai;
@@ -686,6 +707,20 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
        ret;                                                    \
 })
 
+#define rsnd_path_break(priv, io, type)                                \
+{                                                              \
+       struct rsnd_mod *mod;                                   \
+       int id = -1;                                            \
+                                                               \
+       if (rsnd_is_enable_path(io, type)) {                    \
+               id = rsnd_info_id(priv, io, type);              \
+               if (id >= 0) {                                  \
+                       mod = rsnd_##type##_mod_get(priv, id);  \
+                       rsnd_dai_disconnect(mod, io);           \
+               }                                               \
+       }                                                       \
+}
+
 static int rsnd_path_init(struct rsnd_priv *priv,
                          struct rsnd_dai *rdai,
                          struct rsnd_dai_stream *io)
@@ -886,8 +921,7 @@ static int rsnd_dai_probe(struct platform_device *pdev,
 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,
+                       SNDRV_PCM_INFO_MMAP_VALID,
        .buffer_bytes_max       = 64 * 1024,
        .period_bytes_min       = 32,
        .period_bytes_max       = 8192,
@@ -934,6 +968,150 @@ static struct snd_pcm_ops rsnd_pcm_ops = {
        .pointer        = rsnd_pointer,
 };
 
+/*
+ *             snd_kcontrol
+ */
+#define kcontrol_to_cfg(kctrl) ((struct rsnd_kctrl_cfg *)kctrl->private_value)
+static int rsnd_kctrl_info(struct snd_kcontrol *kctrl,
+                          struct snd_ctl_elem_info *uinfo)
+{
+       struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl);
+
+       if (cfg->texts) {
+               uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+               uinfo->count = cfg->size;
+               uinfo->value.enumerated.items = cfg->max;
+               if (uinfo->value.enumerated.item >= cfg->max)
+                       uinfo->value.enumerated.item = cfg->max - 1;
+               strlcpy(uinfo->value.enumerated.name,
+                       cfg->texts[uinfo->value.enumerated.item],
+                       sizeof(uinfo->value.enumerated.name));
+       } else {
+               uinfo->count = cfg->size;
+               uinfo->value.integer.min = 0;
+               uinfo->value.integer.max = cfg->max;
+               uinfo->type = (cfg->max == 1) ?
+                       SNDRV_CTL_ELEM_TYPE_BOOLEAN :
+                       SNDRV_CTL_ELEM_TYPE_INTEGER;
+       }
+
+       return 0;
+}
+
+static int rsnd_kctrl_get(struct snd_kcontrol *kctrl,
+                         struct snd_ctl_elem_value *uc)
+{
+       struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl);
+       int i;
+
+       for (i = 0; i < cfg->size; i++)
+               if (cfg->texts)
+                       uc->value.enumerated.item[i] = cfg->val[i];
+               else
+                       uc->value.integer.value[i] = cfg->val[i];
+
+       return 0;
+}
+
+static int rsnd_kctrl_put(struct snd_kcontrol *kctrl,
+                         struct snd_ctl_elem_value *uc)
+{
+       struct rsnd_mod *mod = snd_kcontrol_chip(kctrl);
+       struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl);
+       int i, change = 0;
+
+       for (i = 0; i < cfg->size; i++) {
+               if (cfg->texts) {
+                       change |= (uc->value.enumerated.item[i] != cfg->val[i]);
+                       cfg->val[i] = uc->value.enumerated.item[i];
+               } else {
+                       change |= (uc->value.integer.value[i] != cfg->val[i]);
+                       cfg->val[i] = uc->value.integer.value[i];
+               }
+       }
+
+       if (change)
+               cfg->update(mod);
+
+       return change;
+}
+
+static int __rsnd_kctrl_new(struct rsnd_mod *mod,
+                           struct rsnd_dai *rdai,
+                           struct snd_soc_pcm_runtime *rtd,
+                           const unsigned char *name,
+                           struct rsnd_kctrl_cfg *cfg,
+                           void (*update)(struct rsnd_mod *mod))
+{
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_kcontrol *kctrl;
+       struct snd_kcontrol_new knew = {
+               .iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name           = name,
+               .info           = rsnd_kctrl_info,
+               .get            = rsnd_kctrl_get,
+               .put            = rsnd_kctrl_put,
+               .private_value  = (unsigned long)cfg,
+       };
+       int ret;
+
+       kctrl = snd_ctl_new1(&knew, mod);
+       if (!kctrl)
+               return -ENOMEM;
+
+       ret = snd_ctl_add(card, kctrl);
+       if (ret < 0)
+               return ret;
+
+       cfg->update = update;
+
+       return 0;
+}
+
+int rsnd_kctrl_new_m(struct rsnd_mod *mod,
+                    struct rsnd_dai *rdai,
+                    struct snd_soc_pcm_runtime *rtd,
+                    const unsigned char *name,
+                    void (*update)(struct rsnd_mod *mod),
+                    struct rsnd_kctrl_cfg_m *_cfg,
+                    u32 max)
+{
+       _cfg->cfg.max   = max;
+       _cfg->cfg.size  = RSND_DVC_CHANNELS;
+       _cfg->cfg.val   = _cfg->val;
+       return __rsnd_kctrl_new(mod, rdai, rtd, name, &_cfg->cfg, update);
+}
+
+int rsnd_kctrl_new_s(struct rsnd_mod *mod,
+                    struct rsnd_dai *rdai,
+                    struct snd_soc_pcm_runtime *rtd,
+                    const unsigned char *name,
+                    void (*update)(struct rsnd_mod *mod),
+                    struct rsnd_kctrl_cfg_s *_cfg,
+                    u32 max)
+{
+       _cfg->cfg.max   = max;
+       _cfg->cfg.size  = 1;
+       _cfg->cfg.val   = &_cfg->val;
+       return __rsnd_kctrl_new(mod, rdai, rtd, name, &_cfg->cfg, update);
+}
+
+int rsnd_kctrl_new_e(struct rsnd_mod *mod,
+                    struct rsnd_dai *rdai,
+                    struct snd_soc_pcm_runtime *rtd,
+                    const unsigned char *name,
+                    struct rsnd_kctrl_cfg_s *_cfg,
+                    void (*update)(struct rsnd_mod *mod),
+                    const char * const *texts,
+                    u32 max)
+{
+       _cfg->cfg.max   = max;
+       _cfg->cfg.size  = 1;
+       _cfg->cfg.val   = &_cfg->val;
+       _cfg->cfg.texts = texts;
+       return __rsnd_kctrl_new(mod, rdai, rtd, name, &_cfg->cfg, update);
+}
+
 /*
  *             snd_soc_platform
  */
@@ -977,6 +1155,49 @@ static const struct snd_soc_component_driver rsnd_soc_component = {
        .name           = "rsnd",
 };
 
+static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
+                                      struct rsnd_dai *rdai,
+                                      int is_play)
+{
+       struct rsnd_dai_stream *io = is_play ? &rdai->playback : &rdai->capture;
+       int ret;
+
+       ret = rsnd_dai_call(probe, io, rdai);
+       if (ret == -EAGAIN) {
+               /*
+                * Fallback to PIO mode
+                */
+
+               /*
+                * call "remove" for SSI/SRC/DVC
+                * SSI will be switch to PIO mode if it was DMA mode
+                * see
+                *      rsnd_dma_init()
+                *      rsnd_ssi_fallback()
+                */
+               rsnd_dai_call(remove, io, rdai);
+
+               /*
+                * remove SRC/DVC from DAI,
+                */
+               rsnd_path_break(priv, io, src);
+               rsnd_path_break(priv, io, dvc);
+
+               /*
+                * fallback
+                */
+               rsnd_dai_call(fallback, io, rdai);
+
+               /*
+                * retry to "probe".
+                * DAI has SSI which is PIO mode only now.
+                */
+               ret = rsnd_dai_call(probe, io, rdai);
+       }
+
+       return ret;
+}
+
 /*
  *     rsnd probe
  */
@@ -1038,11 +1259,11 @@ static int rsnd_probe(struct platform_device *pdev)
        }
 
        for_each_rsnd_dai(rdai, priv, i) {
-               ret = rsnd_dai_call(probe, &rdai->playback, rdai);
+               ret = rsnd_rdai_continuance_probe(priv, rdai, 1);
                if (ret)
                        goto exit_snd_probe;
 
-               ret = rsnd_dai_call(probe, &rdai->capture, rdai);
+               ret = rsnd_rdai_continuance_probe(priv, rdai, 0);
                if (ret)
                        goto exit_snd_probe;
        }
index 3f44393..5380a48 100644 (file)
@@ -11,8 +11,6 @@
 #include "rsnd.h"
 
 #define RSND_DVC_NAME_SIZE     16
-#define RSND_DVC_VOLUME_MAX    100
-#define RSND_DVC_VOLUME_NUM    2
 
 #define DVC_NAME "dvc"
 
@@ -20,8 +18,11 @@ struct rsnd_dvc {
        struct rsnd_dvc_platform_info *info; /* rcar_snd.h */
        struct rsnd_mod mod;
        struct clk *clk;
-       u8 volume[RSND_DVC_VOLUME_NUM];
-       u8 mute[RSND_DVC_VOLUME_NUM];
+       struct rsnd_kctrl_cfg_m volume;
+       struct rsnd_kctrl_cfg_m mute;
+       struct rsnd_kctrl_cfg_s ren;    /* Ramp Enable */
+       struct rsnd_kctrl_cfg_s rup;    /* Ramp Rate Up */
+       struct rsnd_kctrl_cfg_s rdown;  /* Ramp Rate Down */
 };
 
 #define rsnd_mod_to_dvc(_mod)  \
@@ -33,23 +34,87 @@ struct rsnd_dvc {
             ((pos) = (struct rsnd_dvc *)(priv)->dvc + i);      \
             i++)
 
+static const char const *dvc_ramp_rate[] = {
+       "128 dB/1 step",         /* 00000 */
+       "64 dB/1 step",          /* 00001 */
+       "32 dB/1 step",          /* 00010 */
+       "16 dB/1 step",          /* 00011 */
+       "8 dB/1 step",           /* 00100 */
+       "4 dB/1 step",           /* 00101 */
+       "2 dB/1 step",           /* 00110 */
+       "1 dB/1 step",           /* 00111 */
+       "0.5 dB/1 step",         /* 01000 */
+       "0.25 dB/1 step",        /* 01001 */
+       "0.125 dB/1 step",       /* 01010 */
+       "0.125 dB/2 steps",      /* 01011 */
+       "0.125 dB/4 steps",      /* 01100 */
+       "0.125 dB/8 steps",      /* 01101 */
+       "0.125 dB/16 steps",     /* 01110 */
+       "0.125 dB/32 steps",     /* 01111 */
+       "0.125 dB/64 steps",     /* 10000 */
+       "0.125 dB/128 steps",    /* 10001 */
+       "0.125 dB/256 steps",    /* 10010 */
+       "0.125 dB/512 steps",    /* 10011 */
+       "0.125 dB/1024 steps",   /* 10100 */
+       "0.125 dB/2048 steps",   /* 10101 */
+       "0.125 dB/4096 steps",   /* 10110 */
+       "0.125 dB/8192 steps",   /* 10111 */
+};
+
 static void rsnd_dvc_volume_update(struct rsnd_mod *mod)
 {
        struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
-       u32 max = (0x00800000 - 1);
-       u32 vol[RSND_DVC_VOLUME_NUM];
+       u32 val[RSND_DVC_CHANNELS];
+       u32 dvucr = 0;
        u32 mute = 0;
        int i;
 
-       for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) {
-               vol[i] = max / RSND_DVC_VOLUME_MAX * dvc->volume[i];
-               mute |= (!!dvc->mute[i]) << i;
+       for (i = 0; i < dvc->mute.cfg.size; i++)
+               mute |= (!!dvc->mute.cfg.val[i]) << i;
+
+       /* Disable DVC Register access */
+       rsnd_mod_write(mod, DVC_DVUER, 0);
+
+       /* Enable Ramp */
+       if (dvc->ren.val) {
+               dvucr |= 0x10;
+
+               /* Digital Volume Max */
+               for (i = 0; i < RSND_DVC_CHANNELS; i++)
+                       val[i] = dvc->volume.cfg.max;
+
+               rsnd_mod_write(mod, DVC_VRCTR, 0xff);
+               rsnd_mod_write(mod, DVC_VRPDR, dvc->rup.val << 8 |
+                                              dvc->rdown.val);
+               /*
+                * FIXME !!
+                * use scale-downed Digital Volume
+                * as Volume Ramp
+                * 7F FFFF -> 3FF
+                */
+               rsnd_mod_write(mod, DVC_VRDBR,
+                              0x3ff - (dvc->volume.val[0] >> 13));
+
+       } else {
+               for (i = 0; i < RSND_DVC_CHANNELS; i++)
+                       val[i] = dvc->volume.val[i];
+       }
+
+       /* Enable Digital Volume */
+       dvucr |= 0x100;
+       rsnd_mod_write(mod, DVC_VOL0R, val[0]);
+       rsnd_mod_write(mod, DVC_VOL1R, val[1]);
+
+       /*  Enable Mute */
+       if (mute) {
+               dvucr |= 0x1;
+               rsnd_mod_write(mod, DVC_ZCMCR, mute);
        }
 
-       rsnd_mod_write(mod, DVC_VOL0R, vol[0]);
-       rsnd_mod_write(mod, DVC_VOL1R, vol[1]);
+       rsnd_mod_write(mod, DVC_DVUCR, dvucr);
 
-       rsnd_mod_write(mod, DVC_ZCMCR, mute);
+       /* Enable DVC Register access */
+       rsnd_mod_write(mod, DVC_DVUER, 1);
 }
 
 static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod,
@@ -58,7 +123,8 @@ static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod,
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct device *dev = rsnd_priv_to_dev(priv);
 
-       dev_dbg(dev, "%s (Gen2) is probed\n", rsnd_mod_name(mod));
+       dev_dbg(dev, "%s[%d] (Gen2) is probed\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod));
 
        return 0;
 }
@@ -102,16 +168,11 @@ static int rsnd_dvc_init(struct rsnd_mod *dvc_mod,
 
        rsnd_mod_write(dvc_mod, DVC_ADINR, rsnd_get_adinr(dvc_mod));
 
-       /*  enable Volume / Mute */
-       rsnd_mod_write(dvc_mod, DVC_DVUCR, 0x101);
-
        /* ch0/ch1 Volume */
        rsnd_dvc_volume_update(dvc_mod);
 
        rsnd_mod_write(dvc_mod, DVC_DVUIR, 0);
 
-       rsnd_mod_write(dvc_mod, DVC_DVUER, 1);
-
        rsnd_adg_set_cmd_timsel_gen2(rdai, dvc_mod, io);
 
        return 0;
@@ -143,86 +204,6 @@ static int rsnd_dvc_stop(struct rsnd_mod *mod,
        return 0;
 }
 
-static int rsnd_dvc_volume_info(struct snd_kcontrol *kctrl,
-                              struct snd_ctl_elem_info *uinfo)
-{
-       struct rsnd_mod *mod = snd_kcontrol_chip(kctrl);
-       struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
-       u8 *val = (u8 *)kctrl->private_value;
-
-       uinfo->count = RSND_DVC_VOLUME_NUM;
-       uinfo->value.integer.min = 0;
-
-       if (val == dvc->volume) {
-               uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-               uinfo->value.integer.max = RSND_DVC_VOLUME_MAX;
-       } else {
-               uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-               uinfo->value.integer.max = 1;
-       }
-
-       return 0;
-}
-
-static int rsnd_dvc_volume_get(struct snd_kcontrol *kctrl,
-                             struct snd_ctl_elem_value *ucontrol)
-{
-       u8 *val = (u8 *)kctrl->private_value;
-       int i;
-
-       for (i = 0; i < RSND_DVC_VOLUME_NUM; i++)
-               ucontrol->value.integer.value[i] = val[i];
-
-       return 0;
-}
-
-static int rsnd_dvc_volume_put(struct snd_kcontrol *kctrl,
-                             struct snd_ctl_elem_value *ucontrol)
-{
-       struct rsnd_mod *mod = snd_kcontrol_chip(kctrl);
-       u8 *val = (u8 *)kctrl->private_value;
-       int i, change = 0;
-
-       for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) {
-               change |= (ucontrol->value.integer.value[i] != val[i]);
-               val[i] = ucontrol->value.integer.value[i];
-       }
-
-       if (change)
-               rsnd_dvc_volume_update(mod);
-
-       return change;
-}
-
-static int __rsnd_dvc_pcm_new(struct rsnd_mod *mod,
-                             struct rsnd_dai *rdai,
-                             struct snd_soc_pcm_runtime *rtd,
-                             const unsigned char *name,
-                             u8 *private)
-{
-       struct snd_card *card = rtd->card->snd_card;
-       struct snd_kcontrol *kctrl;
-       struct snd_kcontrol_new knew = {
-               .iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name           = name,
-               .info           = rsnd_dvc_volume_info,
-               .get            = rsnd_dvc_volume_get,
-               .put            = rsnd_dvc_volume_put,
-               .private_value  = (unsigned long)private,
-       };
-       int ret;
-
-       kctrl = snd_ctl_new1(&knew, mod);
-       if (!kctrl)
-               return -ENOMEM;
-
-       ret = snd_ctl_add(card, kctrl);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-
 static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
                            struct rsnd_dai *rdai,
                            struct snd_soc_pcm_runtime *rtd)
@@ -232,18 +213,48 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
        int ret;
 
        /* Volume */
-       ret = __rsnd_dvc_pcm_new(mod, rdai, rtd,
+       ret = rsnd_kctrl_new_m(mod, rdai, rtd,
                        rsnd_dai_is_play(rdai, io) ?
                        "DVC Out Playback Volume" : "DVC In Capture Volume",
-                       dvc->volume);
+                       rsnd_dvc_volume_update,
+                       &dvc->volume, 0x00800000 - 1);
        if (ret < 0)
                return ret;
 
        /* Mute */
-       ret = __rsnd_dvc_pcm_new(mod, rdai, rtd,
+       ret = rsnd_kctrl_new_m(mod, rdai, rtd,
                        rsnd_dai_is_play(rdai, io) ?
                        "DVC Out Mute Switch" : "DVC In Mute Switch",
-                       dvc->mute);
+                       rsnd_dvc_volume_update,
+                       &dvc->mute, 1);
+       if (ret < 0)
+               return ret;
+
+       /* Ramp */
+       ret = rsnd_kctrl_new_s(mod, rdai, rtd,
+                       rsnd_dai_is_play(rdai, io) ?
+                       "DVC Out Ramp Switch" : "DVC In Ramp Switch",
+                       rsnd_dvc_volume_update,
+                       &dvc->ren, 1);
+       if (ret < 0)
+               return ret;
+
+       ret = rsnd_kctrl_new_e(mod, rdai, rtd,
+                       rsnd_dai_is_play(rdai, io) ?
+                       "DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate",
+                       &dvc->rup,
+                       rsnd_dvc_volume_update,
+                       dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate));
+       if (ret < 0)
+               return ret;
+
+       ret = rsnd_kctrl_new_e(mod, rdai, rtd,
+                       rsnd_dai_is_play(rdai, io) ?
+                       "DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate",
+                       &dvc->rdown,
+                       rsnd_dvc_volume_update,
+                       dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate));
+
        if (ret < 0)
                return ret;
 
index f95e7ab..87a6f2d 100644 (file)
@@ -8,6 +8,17 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
+
+/*
+ * #define DEBUG
+ *
+ * you can also add below in
+ * ${LINUX}/drivers/base/regmap/regmap.c
+ * for regmap debug
+ *
+ * #define LOG_DEVICE "xxxx.rcar_sound"
+ */
+
 #include "rsnd.h"
 
 struct rsnd_gen {
@@ -67,9 +78,10 @@ u32 rsnd_read(struct rsnd_priv *priv,
        if (!rsnd_is_accessible_reg(priv, gen, reg))
                return 0;
 
-       regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
+       dev_dbg(dev, "r %s[%d] - %4d : %08x\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod), reg, val);
 
-       dev_dbg(dev, "r %s - 0x%04d : %08x\n", rsnd_mod_name(mod), reg, val);
+       regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
 
        return val;
 }
@@ -84,9 +96,10 @@ void rsnd_write(struct rsnd_priv *priv,
        if (!rsnd_is_accessible_reg(priv, gen, reg))
                return;
 
-       regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data);
+       dev_dbg(dev, "w %s[%d] - %4d : %08x\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data);
 
-       dev_dbg(dev, "w %s - 0x%04d : %08x\n", rsnd_mod_name(mod), reg, data);
+       regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data);
 }
 
 void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
@@ -98,11 +111,11 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
        if (!rsnd_is_accessible_reg(priv, gen, reg))
                return;
 
+       dev_dbg(dev, "b %s[%d] - %4d : %08x/%08x\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data, mask);
+
        regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod),
                                  mask, data);
-
-       dev_dbg(dev, "b %s - 0x%04d : %08x/%08x\n",
-               rsnd_mod_name(mod), reg, data, mask);
 }
 
 #define rsnd_gen_regmap_init(priv, id_size, reg_id, conf)              \
@@ -311,6 +324,9 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
                RSND_GEN_M_REG(DVC_ADINR,       0xe08,  0x100),
                RSND_GEN_M_REG(DVC_DVUCR,       0xe10,  0x100),
                RSND_GEN_M_REG(DVC_ZCMCR,       0xe14,  0x100),
+               RSND_GEN_M_REG(DVC_VRCTR,       0xe18,  0x100),
+               RSND_GEN_M_REG(DVC_VRPDR,       0xe1c,  0x100),
+               RSND_GEN_M_REG(DVC_VRDBR,       0xe20,  0x100),
                RSND_GEN_M_REG(DVC_VOL0R,       0xe28,  0x100),
                RSND_GEN_M_REG(DVC_VOL1R,       0xe2c,  0x100),
                RSND_GEN_M_REG(DVC_DVUER,       0xe48,  0x100),
index d119adf..5826c8a 100644 (file)
@@ -91,6 +91,9 @@ enum rsnd_reg {
        RSND_REG_SHARE20,
        RSND_REG_SHARE21,
        RSND_REG_SHARE22,
+       RSND_REG_SHARE23,
+       RSND_REG_SHARE24,
+       RSND_REG_SHARE25,
 
        RSND_REG_MAX,
 };
@@ -129,6 +132,9 @@ enum rsnd_reg {
 #define RSND_REG_CMD_CTRL              RSND_REG_SHARE20
 #define RSND_REG_CMDOUT_TIMSEL         RSND_REG_SHARE21
 #define RSND_REG_BUSIF_DALIGN          RSND_REG_SHARE22
+#define RSND_REG_DVC_VRCTR             RSND_REG_SHARE23
+#define RSND_REG_DVC_VRPDR             RSND_REG_SHARE24
+#define RSND_REG_DVC_VRDBR             RSND_REG_SHARE25
 
 struct rsnd_of_data;
 struct rsnd_priv;
@@ -200,6 +206,8 @@ struct rsnd_mod_ops {
        int (*pcm_new)(struct rsnd_mod *mod,
                       struct rsnd_dai *rdai,
                       struct snd_soc_pcm_runtime *rtd);
+       int (*fallback)(struct rsnd_mod *mod,
+                       struct rsnd_dai *rdai);
 };
 
 struct rsnd_dai_stream;
@@ -210,7 +218,35 @@ struct rsnd_mod {
        struct rsnd_mod_ops *ops;
        struct rsnd_dma dma;
        struct rsnd_dai_stream *io;
+       u32 status;
 };
+/*
+ * status
+ *
+ * bit
+ * 0   0: probe        1: remove
+ * 1   0: init         1: quit
+ * 2   0: start        1: stop
+ * 3   0: pcm_new
+ * 4   0: fallback
+ */
+#define __rsnd_mod_shift_probe         0
+#define __rsnd_mod_shift_remove                0
+#define __rsnd_mod_shift_init          1
+#define __rsnd_mod_shift_quit          1
+#define __rsnd_mod_shift_start         2
+#define __rsnd_mod_shift_stop          2
+#define __rsnd_mod_shift_pcm_new       3
+#define __rsnd_mod_shift_fallback      4
+
+#define __rsnd_mod_call_probe          0
+#define __rsnd_mod_call_remove         1
+#define __rsnd_mod_call_init           0
+#define __rsnd_mod_call_quit           1
+#define __rsnd_mod_call_start          0
+#define __rsnd_mod_call_stop           1
+#define __rsnd_mod_call_pcm_new                0
+#define __rsnd_mod_call_fallback       0
 
 #define rsnd_mod_to_priv(mod) ((mod)->priv)
 #define rsnd_mod_to_dma(mod) (&(mod)->dma)
@@ -267,7 +303,8 @@ struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id);
 int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io);
 int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai);
 #define rsnd_dai_get_platform_info(rdai) ((rdai)->info)
-#define rsnd_io_to_runtime(io) ((io)->substream->runtime)
+#define rsnd_io_to_runtime(io) ((io)->substream ? \
+                               (io)->substream->runtime : NULL)
 
 void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt);
 int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
@@ -381,6 +418,51 @@ struct rsnd_priv {
        is_play;                                                        \
 })
 
+/*
+ *     rsnd_kctrl
+ */
+struct rsnd_kctrl_cfg {
+       unsigned int max;
+       unsigned int size;
+       u32 *val;
+       const char * const *texts;
+       void (*update)(struct rsnd_mod *mod);
+};
+
+#define RSND_DVC_CHANNELS      2
+struct rsnd_kctrl_cfg_m {
+       struct rsnd_kctrl_cfg cfg;
+       u32 val[RSND_DVC_CHANNELS];
+};
+
+struct rsnd_kctrl_cfg_s {
+       struct rsnd_kctrl_cfg cfg;
+       u32 val;
+};
+
+int rsnd_kctrl_new_m(struct rsnd_mod *mod,
+                    struct rsnd_dai *rdai,
+                    struct snd_soc_pcm_runtime *rtd,
+                    const unsigned char *name,
+                    void (*update)(struct rsnd_mod *mod),
+                    struct rsnd_kctrl_cfg_m *_cfg,
+                    u32 max);
+int rsnd_kctrl_new_s(struct rsnd_mod *mod,
+                    struct rsnd_dai *rdai,
+                    struct snd_soc_pcm_runtime *rtd,
+                    const unsigned char *name,
+                    void (*update)(struct rsnd_mod *mod),
+                    struct rsnd_kctrl_cfg_s *_cfg,
+                    u32 max);
+int rsnd_kctrl_new_e(struct rsnd_mod *mod,
+                    struct rsnd_dai *rdai,
+                    struct snd_soc_pcm_runtime *rtd,
+                    const unsigned char *name,
+                    struct rsnd_kctrl_cfg_s *_cfg,
+                    void (*update)(struct rsnd_mod *mod),
+                    const char * const *texts,
+                    u32 max);
+
 /*
  *     R-Car SRC
  */
@@ -395,10 +477,11 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
                        struct rsnd_dai *rdai,
                        int use_busif);
 int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod,
-                      struct rsnd_dai *rdai,
-                      int use_busif);
-int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod,
+                      struct rsnd_dai *rdai);
+int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod,
                            struct rsnd_dai *rdai);
+int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod,
+                            struct rsnd_dai *rdai);
 
 #define rsnd_src_nr(priv) ((priv)->src_nr)
 
@@ -410,6 +493,7 @@ int rsnd_ssi_probe(struct platform_device *pdev,
                   struct rsnd_priv *priv);
 struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
 int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
+int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
 
 /*
  *     R-Car DVC
index 9183e01..eede3ac 100644 (file)
@@ -175,30 +175,47 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
 }
 
 int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod,
-                       struct rsnd_dai *rdai,
-                       int use_busif)
+                      struct rsnd_dai *rdai)
 {
        /*
         * DMA settings for SSIU
         */
-       if (use_busif)
-               rsnd_mod_write(ssi_mod, SSI_CTRL, 0);
+       rsnd_mod_write(ssi_mod, SSI_CTRL, 0);
 
        return 0;
 }
 
-int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod,
+int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod,
                            struct rsnd_dai *rdai)
 {
        struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
 
-       /* enable PIO interrupt if Gen2 */
-       if (rsnd_is_gen2(priv))
+       if (rsnd_is_gen1(priv))
+               return 0;
+
+       /* enable SSI interrupt if Gen2 */
+       if (rsnd_ssi_is_dma_mode(ssi_mod))
+               rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0e000000);
+       else
                rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0f000000);
 
        return 0;
 }
 
+int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod,
+                           struct rsnd_dai *rdai)
+{
+       struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
+
+       if (rsnd_is_gen1(priv))
+               return 0;
+
+       /* disable SSI interrupt if Gen2 */
+       rsnd_mod_write(ssi_mod, INT_ENABLE, 0x00000000);
+
+       return 0;
+}
+
 unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
                                   struct rsnd_dai_stream *io,
                                   struct snd_pcm_runtime *runtime)
@@ -239,12 +256,6 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod,
        rsnd_mod_write(mod, SRC_SWRSR, 0);
        rsnd_mod_write(mod, SRC_SWRSR, 1);
 
-       /*
-        * Initialize the operation of the SRC internal circuits
-        * see rsnd_src_start()
-        */
-       rsnd_mod_write(mod, SRC_SRCIR, 1);
-
        /* Set channel number and output bit length */
        rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr(mod));
 
@@ -269,6 +280,12 @@ static int rsnd_src_init(struct rsnd_mod *mod,
 
        clk_prepare_enable(src->clk);
 
+       /*
+        * Initialize the operation of the SRC internal circuits
+        * see rsnd_src_start()
+        */
+       rsnd_mod_write(mod, SRC_SRCIR, 1);
+
        return 0;
 }
 
@@ -282,32 +299,20 @@ static int rsnd_src_quit(struct rsnd_mod *mod,
        return 0;
 }
 
-static int rsnd_src_start(struct rsnd_mod *mod,
-                         struct rsnd_dai *rdai)
+static int rsnd_src_start(struct rsnd_mod *mod)
 {
-       struct rsnd_src *src = rsnd_mod_to_src(mod);
-
        /*
         * Cancel the initialization and operate the SRC function
-        * see rsnd_src_set_convert_rate()
+        * see rsnd_src_init()
         */
        rsnd_mod_write(mod, SRC_SRCIR, 0);
 
-       if (rsnd_src_convert_rate(src))
-               rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
-
        return 0;
 }
 
-
-static int rsnd_src_stop(struct rsnd_mod *mod,
-                        struct rsnd_dai *rdai)
+static int rsnd_src_stop(struct rsnd_mod *mod)
 {
-       struct rsnd_src *src = rsnd_mod_to_src(mod);
-
-       if (rsnd_src_convert_rate(src))
-               rsnd_mod_write(mod, SRC_ROUTE_MODE0, 0);
-
+       /* nothing to do */
        return 0;
 }
 
@@ -414,6 +419,7 @@ static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod,
 static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod,
                                          struct rsnd_dai *rdai)
 {
+       struct rsnd_src *src = rsnd_mod_to_src(mod);
        int ret;
 
        ret = rsnd_src_set_convert_rate(mod, rdai);
@@ -427,6 +433,10 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod,
        rsnd_mod_write(mod, SRC_MNFSR,
                       rsnd_mod_read(mod, SRC_IFSVR) / 100 * 98);
 
+       /* Gen1/Gen2 are not compatible */
+       if (rsnd_src_convert_rate(src))
+               rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
+
        /* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */
 
        return 0;
@@ -438,7 +448,8 @@ static int rsnd_src_probe_gen1(struct rsnd_mod *mod,
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct device *dev = rsnd_priv_to_dev(priv);
 
-       dev_dbg(dev, "%s (Gen1) is probed\n", rsnd_mod_name(mod));
+       dev_dbg(dev, "%s[%d] (Gen1) is probed\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod));
 
        return 0;
 }
@@ -474,7 +485,7 @@ static int rsnd_src_start_gen1(struct rsnd_mod *mod,
 
        rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), (1 << id));
 
-       return rsnd_src_start(mod, rdai);
+       return rsnd_src_start(mod);
 }
 
 static int rsnd_src_stop_gen1(struct rsnd_mod *mod,
@@ -484,7 +495,7 @@ static int rsnd_src_stop_gen1(struct rsnd_mod *mod,
 
        rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), 0);
 
-       return rsnd_src_stop(mod, rdai);
+       return rsnd_src_stop(mod);
 }
 
 static struct rsnd_mod_ops rsnd_src_gen1_ops = {
@@ -507,16 +518,17 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod,
        struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
        struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
        struct rsnd_src *src = rsnd_mod_to_src(mod);
+       u32 convert_rate = rsnd_src_convert_rate(src);
        uint ratio;
        int ret;
 
        /* 6 - 1/6 are very enough ratio for SRC_BSDSR */
-       if (!rsnd_src_convert_rate(src))
+       if (!convert_rate)
                ratio = 0;
-       else if (rsnd_src_convert_rate(src) > runtime->rate)
-               ratio = 100 * rsnd_src_convert_rate(src) / runtime->rate;
+       else if (convert_rate > runtime->rate)
+               ratio = 100 * convert_rate / runtime->rate;
        else
-               ratio = 100 * runtime->rate / rsnd_src_convert_rate(src);
+               ratio = 100 * runtime->rate / convert_rate;
 
        if (ratio > 600) {
                dev_err(dev, "FSO/FSI ratio error\n");
@@ -529,6 +541,11 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod,
 
        rsnd_mod_write(mod, SRC_SRCCR, 0x00011110);
 
+       if (convert_rate) {
+               /* Gen1/Gen2 are not compatible */
+               rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
+       }
+
        switch (rsnd_mod_id(mod)) {
        case 5:
        case 6:
@@ -578,9 +595,11 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod,
                            rsnd_info_is_playback(priv, src),
                            src->info->dma_id);
        if (ret < 0)
-               dev_err(dev, "SRC DMA failed\n");
-
-       dev_dbg(dev, "%s (Gen2) is probed\n", rsnd_mod_name(mod));
+               dev_err(dev, "%s[%d] (Gen2) failed\n",
+                       rsnd_mod_name(mod), rsnd_mod_id(mod));
+       else
+               dev_dbg(dev, "%s[%d] (Gen2) is probed\n",
+                       rsnd_mod_name(mod), rsnd_mod_id(mod));
 
        return ret;
 }
@@ -624,7 +643,7 @@ static int rsnd_src_start_gen2(struct rsnd_mod *mod,
 
        rsnd_mod_write(mod, SRC_CTRL, val);
 
-       return rsnd_src_start(mod, rdai);
+       return rsnd_src_start(mod);
 }
 
 static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
@@ -636,7 +655,7 @@ static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
 
        rsnd_dma_stop(rsnd_mod_to_dma(&src->mod));
 
-       return rsnd_src_stop(mod, rdai);
+       return rsnd_src_stop(mod);
 }
 
 static struct rsnd_mod_ops rsnd_src_gen2_ops = {
index 34e8400..3844fbe 100644 (file)
@@ -68,7 +68,6 @@ struct rsnd_ssi {
        struct rsnd_dai *rdai;
        u32 cr_own;
        u32 cr_clk;
-       u32 cr_etc;
        int err;
        unsigned int usrcnt;
        unsigned int rate;
@@ -83,7 +82,7 @@ struct rsnd_ssi {
 #define rsnd_ssi_nr(priv) ((priv)->ssi_nr)
 #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
 #define rsnd_dma_to_ssi(dma)  rsnd_mod_to_ssi(rsnd_dma_to_mod(dma))
-#define rsnd_ssi_pio_available(ssi) ((ssi)->info->pio_irq > 0)
+#define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0)
 #define rsnd_ssi_dma_available(ssi) \
        rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod))
 #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent)
@@ -96,6 +95,9 @@ static int rsnd_ssi_use_busif(struct rsnd_mod *mod)
        struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
        int use_busif = 0;
 
+       if (!rsnd_ssi_is_dma_mode(mod))
+               return 0;
+
        if (!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_NO_BUSIF))
                use_busif = 1;
        if (rsnd_io_to_mod_src(io))
@@ -159,7 +161,8 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
                                ssi->cr_clk     = FORCE | SWL_32 |
                                                  SCKD | SWSD | CKDV(j);
 
-                               dev_dbg(dev, "ssi%d outputs %u Hz\n",
+                               dev_dbg(dev, "%s[%d] outputs %u Hz\n",
+                                       rsnd_mod_name(&ssi->mod),
                                        rsnd_mod_id(&ssi->mod), rate);
 
                                return 0;
@@ -184,6 +187,7 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
 {
        struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
        struct device *dev = rsnd_priv_to_dev(priv);
+       u32 cr_mode;
        u32 cr;
 
        if (0 == ssi->usrcnt) {
@@ -197,16 +201,29 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
                }
        }
 
+       cr_mode = rsnd_ssi_is_dma_mode(&ssi->mod) ?
+               DMEN :  /* DMA : enable DMA */
+               DIEN;   /* PIO : enable Data interrupt */
+
+
        cr  =   ssi->cr_own     |
                ssi->cr_clk     |
-               ssi->cr_etc     |
-               EN;
+               cr_mode         |
+               UIEN | OIEN | EN;
 
        rsnd_mod_write(&ssi->mod, SSICR, cr);
 
+       /* enable WS continue */
+       if (rsnd_dai_is_clk_master(rdai))
+               rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
+
+       /* clear error status */
+       rsnd_mod_write(&ssi->mod, SSISR, 0);
+
        ssi->usrcnt++;
 
-       dev_dbg(dev, "ssi%d hw started\n", rsnd_mod_id(&ssi->mod));
+       dev_dbg(dev, "%s[%d] hw started\n",
+               rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
 }
 
 static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi,
@@ -249,7 +266,8 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi,
                clk_disable_unprepare(ssi->clk);
        }
 
-       dev_dbg(dev, "ssi%d hw stopped\n", rsnd_mod_id(&ssi->mod));
+       dev_dbg(dev, "%s[%d] hw stopped\n",
+               rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
 }
 
 /*
@@ -334,25 +352,54 @@ static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
        }
 }
 
-/*
- *             SSI PIO
- */
-static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)
+static int rsnd_ssi_start(struct rsnd_mod *mod,
+                         struct rsnd_dai *rdai)
+{
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+
+       rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod));
+
+       rsnd_ssi_hw_start(ssi, rdai, io);
+
+       rsnd_src_ssi_irq_enable(mod, rdai);
+
+       return 0;
+}
+
+static int rsnd_ssi_stop(struct rsnd_mod *mod,
+                        struct rsnd_dai *rdai)
+{
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+       rsnd_src_ssi_irq_disable(mod, rdai);
+
+       rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR));
+
+       rsnd_ssi_hw_stop(ssi, rdai);
+
+       rsnd_src_ssiu_stop(mod, rdai);
+
+       return 0;
+}
+
+static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
 {
        struct rsnd_ssi *ssi = data;
+       struct rsnd_dai *rdai = ssi->rdai;
        struct rsnd_mod *mod = &ssi->mod;
        struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
        u32 status = rsnd_mod_read(mod, SSISR);
-       irqreturn_t ret = IRQ_NONE;
 
-       if (io && (status & DIRQ)) {
-               struct rsnd_dai *rdai = ssi->rdai;
+       if (!io)
+               return IRQ_NONE;
+
+       /* PIO only */
+       if (status & DIRQ) {
                struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
                u32 *buf = (u32 *)(runtime->dma_area +
                                   rsnd_dai_pointer_offset(io, 0));
 
-               rsnd_ssi_record_error(ssi, status);
-
                /*
                 * 8/16/32 data can be assesse to TDR/RDR register
                 * directly as 32bit data
@@ -364,73 +411,60 @@ static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)
                        *buf = rsnd_mod_read(mod, SSIRDR);
 
                rsnd_dai_pointer_update(io, sizeof(*buf));
+       }
+
+       /* PIO / DMA */
+       if (status & (UIRQ | OIRQ)) {
+               struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+               struct device *dev = rsnd_priv_to_dev(priv);
+
+               /*
+                * restart SSI
+                */
+               rsnd_ssi_stop(mod, rdai);
+               rsnd_ssi_start(mod, rdai);
 
-               ret = IRQ_HANDLED;
+               dev_dbg(dev, "%s[%d] restart\n",
+                       rsnd_mod_name(mod), rsnd_mod_id(mod));
        }
 
-       return ret;
+       rsnd_ssi_record_error(ssi, status);
+
+       return IRQ_HANDLED;
 }
 
+/*
+ *             SSI PIO
+ */
 static int rsnd_ssi_pio_probe(struct rsnd_mod *mod,
                              struct rsnd_dai *rdai)
 {
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct device *dev = rsnd_priv_to_dev(priv);
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-       int irq = ssi->info->pio_irq;
        int ret;
 
-       ret = devm_request_irq(dev, irq,
-                              rsnd_ssi_pio_interrupt,
+       ret = devm_request_irq(dev, ssi->info->irq,
+                              rsnd_ssi_interrupt,
                               IRQF_SHARED,
                               dev_name(dev), ssi);
        if (ret)
-               dev_err(dev, "SSI request interrupt failed\n");
-
-       dev_dbg(dev, "%s (PIO) is probed\n", rsnd_mod_name(mod));
+               dev_err(dev, "%s[%d] (PIO) request interrupt failed\n",
+                       rsnd_mod_name(mod), rsnd_mod_id(mod));
+       else
+               dev_dbg(dev, "%s[%d] (PIO) is probed\n",
+                       rsnd_mod_name(mod), rsnd_mod_id(mod));
 
        return ret;
 }
 
-static int rsnd_ssi_pio_start(struct rsnd_mod *mod,
-                             struct rsnd_dai *rdai)
-{
-       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
-
-       /* enable PIO IRQ */
-       ssi->cr_etc = UIEN | OIEN | DIEN;
-
-       rsnd_src_ssiu_start(mod, rdai, 0);
-
-       rsnd_src_enable_ssi_irq(mod, rdai);
-
-       rsnd_ssi_hw_start(ssi, rdai, io);
-
-       return 0;
-}
-
-static int rsnd_ssi_pio_stop(struct rsnd_mod *mod,
-                            struct rsnd_dai *rdai)
-{
-       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-
-       ssi->cr_etc = 0;
-
-       rsnd_ssi_hw_stop(ssi, rdai);
-
-       rsnd_src_ssiu_stop(mod, rdai, 0);
-
-       return 0;
-}
-
 static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
        .name   = SSI_NAME,
        .probe  = rsnd_ssi_pio_probe,
        .init   = rsnd_ssi_init,
        .quit   = rsnd_ssi_quit,
-       .start  = rsnd_ssi_pio_start,
-       .stop   = rsnd_ssi_pio_stop,
+       .start  = rsnd_ssi_start,
+       .stop   = rsnd_ssi_stop,
 };
 
 static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
@@ -442,15 +476,28 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
        int dma_id = ssi->info->dma_id;
        int ret;
 
+       ret = devm_request_irq(dev, ssi->info->irq,
+                              rsnd_ssi_interrupt,
+                              IRQF_SHARED,
+                              dev_name(dev), ssi);
+       if (ret)
+               goto rsnd_ssi_dma_probe_fail;
+
        ret = rsnd_dma_init(
                priv, rsnd_mod_to_dma(mod),
                rsnd_info_is_playback(priv, ssi),
                dma_id);
+       if (ret)
+               goto rsnd_ssi_dma_probe_fail;
 
-       if (ret < 0)
-               dev_err(dev, "SSI DMA failed\n");
+       dev_dbg(dev, "%s[%d] (DMA) is probed\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+       return ret;
 
-       dev_dbg(dev, "%s (DMA) is probed\n", rsnd_mod_name(mod));
+rsnd_ssi_dma_probe_fail:
+       dev_err(dev, "%s[%d] (DMA) is failed\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod));
 
        return ret;
 }
@@ -458,30 +505,48 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
 static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
                               struct rsnd_dai *rdai)
 {
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       int irq = ssi->info->irq;
+
        rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod));
 
+       /* PIO will request IRQ again */
+       devm_free_irq(dev, irq, ssi);
+
        return 0;
 }
 
-static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
-                             struct rsnd_dai *rdai)
+static int rsnd_ssi_fallback(struct rsnd_mod *mod,
+                            struct rsnd_dai *rdai)
 {
-       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-       struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);
-       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
 
-       /* enable DMA transfer */
-       ssi->cr_etc = DMEN;
+       /*
+        * fallback to PIO
+        *
+        * SSI .probe might be called again.
+        * see
+        *      rsnd_rdai_continuance_probe()
+        */
+       mod->ops = &rsnd_ssi_pio_ops;
 
-       rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod));
+       dev_info(dev, "%s[%d] fallback to PIO mode\n",
+                rsnd_mod_name(mod), rsnd_mod_id(mod));
 
-       rsnd_dma_start(dma);
+       return 0;
+}
 
-       rsnd_ssi_hw_start(ssi, ssi->rdai, io);
+static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
+                             struct rsnd_dai *rdai)
+{
+       struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
 
-       /* enable WS continue */
-       if (rsnd_dai_is_clk_master(rdai))
-               rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
+       rsnd_ssi_start(mod, rdai);
+
+       rsnd_dma_start(dma);
 
        return 0;
 }
@@ -489,18 +554,11 @@ static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
 static int rsnd_ssi_dma_stop(struct rsnd_mod *mod,
                             struct rsnd_dai *rdai)
 {
-       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-       struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);
-
-       ssi->cr_etc = 0;
-
-       rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR));
-
-       rsnd_ssi_hw_stop(ssi, rdai);
+       struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
 
        rsnd_dma_stop(dma);
 
-       rsnd_src_ssiu_stop(mod, rdai, 1);
+       rsnd_ssi_stop(mod, rdai);
 
        return 0;
 }
@@ -519,8 +577,15 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
        .quit   = rsnd_ssi_quit,
        .start  = rsnd_ssi_dma_start,
        .stop   = rsnd_ssi_dma_stop,
+       .fallback = rsnd_ssi_fallback,
 };
 
+int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod)
+{
+       return mod->ops == &rsnd_ssi_dma_ops;
+}
+
+
 /*
  *             Non SSI
  */
@@ -614,7 +679,7 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev,
                /*
                 * irq
                 */
-               ssi_info->pio_irq = irq_of_parse_and_map(np, 0);
+               ssi_info->irq = irq_of_parse_and_map(np, 0);
 
                /*
                 * DMA
diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c
new file mode 100644 (file)
index 0000000..2e10e9a
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * soc-ac97.c  --  ALSA SoC Audio Layer AC97 support
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ * Copyright (C) 2010 Slimlogic Ltd.
+ * Copyright (C) 2010 Texas Instruments Inc.
+ *
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ *         with code, comments and ideas from :-
+ *         Richard Purdie <richard@openedhand.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/slab.h>
+#include <sound/ac97_codec.h>
+#include <sound/soc.h>
+
+struct snd_ac97_reset_cfg {
+       struct pinctrl *pctl;
+       struct pinctrl_state *pstate_reset;
+       struct pinctrl_state *pstate_warm_reset;
+       struct pinctrl_state *pstate_run;
+       int gpio_sdata;
+       int gpio_sync;
+       int gpio_reset;
+};
+
+static struct snd_ac97_bus soc_ac97_bus = {
+       .ops = NULL, /* Gets initialized in snd_soc_set_ac97_ops() */
+};
+
+static void soc_ac97_device_release(struct device *dev)
+{
+       kfree(to_ac97_t(dev));
+}
+
+/**
+ * snd_soc_new_ac97_codec - initailise AC97 device
+ * @codec: audio codec
+ *
+ * Initialises AC97 codec resources for use by ad-hoc devices only.
+ */
+struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec)
+{
+       struct snd_ac97 *ac97;
+       int ret;
+
+       ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL);
+       if (ac97 == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       ac97->bus = &soc_ac97_bus;
+       ac97->num = 0;
+
+       ac97->dev.bus = &ac97_bus_type;
+       ac97->dev.parent = codec->component.card->dev;
+       ac97->dev.release = soc_ac97_device_release;
+
+       dev_set_name(&ac97->dev, "%d-%d:%s",
+                    codec->component.card->snd_card->number, 0,
+                    codec->component.name);
+
+       ret = device_register(&ac97->dev);
+       if (ret) {
+               put_device(&ac97->dev);
+               return ERR_PTR(ret);
+       }
+
+       return ac97;
+}
+EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
+
+/**
+ * snd_soc_free_ac97_codec - free AC97 codec device
+ * @codec: audio codec
+ *
+ * Frees AC97 codec device resources.
+ */
+void snd_soc_free_ac97_codec(struct snd_ac97 *ac97)
+{
+       device_del(&ac97->dev);
+       ac97->bus = NULL;
+       put_device(&ac97->dev);
+}
+EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec);
+
+static struct snd_ac97_reset_cfg snd_ac97_rst_cfg;
+
+static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+       struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
+
+       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset);
+
+       gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1);
+
+       udelay(10);
+
+       gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
+
+       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
+       msleep(2);
+}
+
+static void snd_soc_ac97_reset(struct snd_ac97 *ac97)
+{
+       struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
+
+       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset);
+
+       gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
+       gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0);
+       gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0);
+
+       udelay(10);
+
+       gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1);
+
+       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
+       msleep(2);
+}
+
+static int snd_soc_ac97_parse_pinctl(struct device *dev,
+               struct snd_ac97_reset_cfg *cfg)
+{
+       struct pinctrl *p;
+       struct pinctrl_state *state;
+       int gpio;
+       int ret;
+
+       p = devm_pinctrl_get(dev);
+       if (IS_ERR(p)) {
+               dev_err(dev, "Failed to get pinctrl\n");
+               return PTR_ERR(p);
+       }
+       cfg->pctl = p;
+
+       state = pinctrl_lookup_state(p, "ac97-reset");
+       if (IS_ERR(state)) {
+               dev_err(dev, "Can't find pinctrl state ac97-reset\n");
+               return PTR_ERR(state);
+       }
+       cfg->pstate_reset = state;
+
+       state = pinctrl_lookup_state(p, "ac97-warm-reset");
+       if (IS_ERR(state)) {
+               dev_err(dev, "Can't find pinctrl state ac97-warm-reset\n");
+               return PTR_ERR(state);
+       }
+       cfg->pstate_warm_reset = state;
+
+       state = pinctrl_lookup_state(p, "ac97-running");
+       if (IS_ERR(state)) {
+               dev_err(dev, "Can't find pinctrl state ac97-running\n");
+               return PTR_ERR(state);
+       }
+       cfg->pstate_run = state;
+
+       gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0);
+       if (gpio < 0) {
+               dev_err(dev, "Can't find ac97-sync gpio\n");
+               return gpio;
+       }
+       ret = devm_gpio_request(dev, gpio, "AC97 link sync");
+       if (ret) {
+               dev_err(dev, "Failed requesting ac97-sync gpio\n");
+               return ret;
+       }
+       cfg->gpio_sync = gpio;
+
+       gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1);
+       if (gpio < 0) {
+               dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio);
+               return gpio;
+       }
+       ret = devm_gpio_request(dev, gpio, "AC97 link sdata");
+       if (ret) {
+               dev_err(dev, "Failed requesting ac97-sdata gpio\n");
+               return ret;
+       }
+       cfg->gpio_sdata = gpio;
+
+       gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2);
+       if (gpio < 0) {
+               dev_err(dev, "Can't find ac97-reset gpio\n");
+               return gpio;
+       }
+       ret = devm_gpio_request(dev, gpio, "AC97 link reset");
+       if (ret) {
+               dev_err(dev, "Failed requesting ac97-reset gpio\n");
+               return ret;
+       }
+       cfg->gpio_reset = gpio;
+
+       return 0;
+}
+
+struct snd_ac97_bus_ops *soc_ac97_ops;
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops)
+{
+       if (ops == soc_ac97_ops)
+               return 0;
+
+       if (soc_ac97_ops && ops)
+               return -EBUSY;
+
+       soc_ac97_ops = ops;
+       soc_ac97_bus.ops = ops;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops);
+
+/**
+ * snd_soc_set_ac97_ops_of_reset - Set ac97 ops with generic ac97 reset functions
+ *
+ * This function sets the reset and warm_reset properties of ops and parses
+ * the device node of pdev to get pinctrl states and gpio numbers to use.
+ */
+int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
+               struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct snd_ac97_reset_cfg cfg;
+       int ret;
+
+       ret = snd_soc_ac97_parse_pinctl(dev, &cfg);
+       if (ret)
+               return ret;
+
+       ret = snd_soc_set_ac97_ops(ops);
+       if (ret)
+               return ret;
+
+       ops->warm_reset = snd_soc_ac97_warm_reset;
+       ops->reset = snd_soc_ac97_reset;
+
+       snd_ac97_rst_cfg = cfg;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset);
index a9f82b5..07f4335 100644 (file)
 #include <linux/export.h>
 #include <linux/slab.h>
 
-#include <trace/events/asoc.h>
-
-static bool snd_soc_set_cache_val(void *base, unsigned int idx,
-                                 unsigned int val, unsigned int word_size)
-{
-       switch (word_size) {
-       case 1: {
-               u8 *cache = base;
-               if (cache[idx] == val)
-                       return true;
-               cache[idx] = val;
-               break;
-       }
-       case 2: {
-               u16 *cache = base;
-               if (cache[idx] == val)
-                       return true;
-               cache[idx] = val;
-               break;
-       }
-       default:
-               WARN(1, "Invalid word_size %d\n", word_size);
-               break;
-       }
-       return false;
-}
-
-static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx,
-               unsigned int word_size)
-{
-       if (!base)
-               return -1;
-
-       switch (word_size) {
-       case 1: {
-               const u8 *cache = base;
-               return cache[idx];
-       }
-       case 2: {
-               const u16 *cache = base;
-               return cache[idx];
-       }
-       default:
-               WARN(1, "Invalid word_size %d\n", word_size);
-               break;
-       }
-       /* unreachable */
-       return -1;
-}
-
 int snd_soc_cache_init(struct snd_soc_codec *codec)
 {
        const struct snd_soc_codec_driver *codec_drv = codec->driver;
@@ -75,8 +25,6 @@ int snd_soc_cache_init(struct snd_soc_codec *codec)
        if (!reg_size)
                return 0;
 
-       mutex_init(&codec->cache_rw_mutex);
-
        dev_dbg(codec->dev, "ASoC: Initializing cache for %s codec\n",
                                codec->component.name);
 
@@ -103,100 +51,3 @@ int snd_soc_cache_exit(struct snd_soc_codec *codec)
        codec->reg_cache = NULL;
        return 0;
 }
-
-/**
- * snd_soc_cache_read: Fetch the value of a given register from the cache.
- *
- * @codec: CODEC to configure.
- * @reg: The register index.
- * @value: The value to be returned.
- */
-int snd_soc_cache_read(struct snd_soc_codec *codec,
-                      unsigned int reg, unsigned int *value)
-{
-       if (!value)
-               return -EINVAL;
-
-       mutex_lock(&codec->cache_rw_mutex);
-       if (!ZERO_OR_NULL_PTR(codec->reg_cache))
-               *value = snd_soc_get_cache_val(codec->reg_cache, reg,
-                                              codec->driver->reg_word_size);
-       mutex_unlock(&codec->cache_rw_mutex);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_cache_read);
-
-/**
- * snd_soc_cache_write: Set the value of a given register in the cache.
- *
- * @codec: CODEC to configure.
- * @reg: The register index.
- * @value: The new register value.
- */
-int snd_soc_cache_write(struct snd_soc_codec *codec,
-                       unsigned int reg, unsigned int value)
-{
-       mutex_lock(&codec->cache_rw_mutex);
-       if (!ZERO_OR_NULL_PTR(codec->reg_cache))
-               snd_soc_set_cache_val(codec->reg_cache, reg, value,
-                                     codec->driver->reg_word_size);
-       mutex_unlock(&codec->cache_rw_mutex);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_cache_write);
-
-static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec)
-{
-       int i;
-       int ret;
-       const struct snd_soc_codec_driver *codec_drv;
-       unsigned int val;
-
-       codec_drv = codec->driver;
-       for (i = 0; i < codec_drv->reg_cache_size; ++i) {
-               ret = snd_soc_cache_read(codec, i, &val);
-               if (ret)
-                       return ret;
-               if (codec_drv->reg_cache_default)
-                       if (snd_soc_get_cache_val(codec_drv->reg_cache_default,
-                                                 i, codec_drv->reg_word_size) == val)
-                               continue;
-
-               ret = snd_soc_write(codec, i, val);
-               if (ret)
-                       return ret;
-               dev_dbg(codec->dev, "ASoC: Synced register %#x, value = %#x\n",
-                       i, val);
-       }
-       return 0;
-}
-
-/**
- * snd_soc_cache_sync: Sync the register cache with the hardware.
- *
- * @codec: CODEC to configure.
- *
- * Any registers that should not be synced should be marked as
- * volatile.  In general drivers can choose not to use the provided
- * syncing functionality if they so require.
- */
-int snd_soc_cache_sync(struct snd_soc_codec *codec)
-{
-       const char *name = "flat";
-       int ret;
-
-       if (!codec->cache_sync)
-               return 0;
-
-       dev_dbg(codec->dev, "ASoC: Syncing cache for %s codec\n",
-               codec->component.name);
-       trace_snd_soc_cache_sync(codec, name, "start");
-       ret = snd_soc_flat_cache_sync(codec);
-       if (!ret)
-               codec->cache_sync = 0;
-       trace_snd_soc_cache_sync(codec, name, "end");
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_cache_sync);
index cecfab3..590a82f 100644 (file)
@@ -258,10 +258,7 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream)
        list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be)
                dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
 
-       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
-               dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
-       else
-               dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
+       dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
 
        fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
        fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
@@ -456,11 +453,7 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream,
        if (ret < 0)
                goto out;
 
-       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
-               dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
-       else
-               dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
-
+       dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
        fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
 
 out:
index 4c8f8a2..9357210 100644 (file)
@@ -34,9 +34,6 @@
 #include <linux/ctype.h>
 #include <linux/slab.h>
 #include <linux/of.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
-#include <sound/ac97_codec.h>
 #include <sound/core.h>
 #include <sound/jack.h>
 #include <sound/pcm.h>
@@ -69,16 +66,6 @@ static int pmdown_time = 5000;
 module_param(pmdown_time, int, 0);
 MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)");
 
-struct snd_ac97_reset_cfg {
-       struct pinctrl *pctl;
-       struct pinctrl_state *pstate_reset;
-       struct pinctrl_state *pstate_warm_reset;
-       struct pinctrl_state *pstate_run;
-       int gpio_sdata;
-       int gpio_sync;
-       int gpio_reset;
-};
-
 /* returns the minimum number of bytes needed to represent
  * a particular given value */
 static int min_bytes_needed(unsigned long val)
@@ -309,9 +296,6 @@ static void soc_init_codec_debugfs(struct snd_soc_component *component)
 {
        struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
 
-       debugfs_create_bool("cache_sync", 0444, codec->component.debugfs_root,
-                           &codec->cache_sync);
-
        codec->debugfs_reg = debugfs_create_file("codec_reg", 0644,
                                                 codec->component.debugfs_root,
                                                 codec, &codec_reg_fops);
@@ -499,40 +483,6 @@ struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card,
 }
 EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime);
 
-#ifdef CONFIG_SND_SOC_AC97_BUS
-/* unregister ac97 codec */
-static int soc_ac97_dev_unregister(struct snd_soc_codec *codec)
-{
-       if (codec->ac97->dev.bus)
-               device_unregister(&codec->ac97->dev);
-       return 0;
-}
-
-/* stop no dev release warning */
-static void soc_ac97_device_release(struct device *dev){}
-
-/* register ac97 codec to bus */
-static int soc_ac97_dev_register(struct snd_soc_codec *codec)
-{
-       int err;
-
-       codec->ac97->dev.bus = &ac97_bus_type;
-       codec->ac97->dev.parent = codec->component.card->dev;
-       codec->ac97->dev.release = soc_ac97_device_release;
-
-       dev_set_name(&codec->ac97->dev, "%d-%d:%s",
-                    codec->component.card->snd_card->number, 0,
-                    codec->component.name);
-       err = device_register(&codec->ac97->dev);
-       if (err < 0) {
-               dev_err(codec->dev, "ASoC: Can't register ac97 bus\n");
-               codec->ac97->dev.bus = NULL;
-               return err;
-       }
-       return 0;
-}
-#endif
-
 static void codec2codec_close_delayed_work(struct work_struct *work)
 {
        /* Currently nothing to do for c2c links
@@ -592,17 +542,12 @@ int snd_soc_suspend(struct device *dev)
 
        for (i = 0; i < card->num_rtd; i++) {
                struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
-               struct snd_soc_platform *platform = card->rtd[i].platform;
 
                if (card->rtd[i].dai_link->ignore_suspend)
                        continue;
 
-               if (cpu_dai->driver->suspend && !cpu_dai->driver->ac97_control)
+               if (cpu_dai->driver->suspend && !cpu_dai->driver->bus_control)
                        cpu_dai->driver->suspend(cpu_dai);
-               if (platform->driver->suspend && !platform->suspended) {
-                       platform->driver->suspend(cpu_dai);
-                       platform->suspended = 1;
-               }
        }
 
        /* close any waiting streams and save state */
@@ -629,8 +574,8 @@ int snd_soc_suspend(struct device *dev)
                                          SND_SOC_DAPM_STREAM_SUSPEND);
        }
 
-       /* Recheck all analogue paths too */
-       dapm_mark_io_dirty(&card->dapm);
+       /* Recheck all endpoints too, their state is affected by suspend */
+       dapm_mark_endpoints_dirty(card);
        snd_soc_dapm_sync(&card->dapm);
 
        /* suspend all CODECs */
@@ -656,7 +601,6 @@ int snd_soc_suspend(struct device *dev)
                                if (codec->driver->suspend)
                                        codec->driver->suspend(codec);
                                codec->suspended = 1;
-                               codec->cache_sync = 1;
                                if (codec->component.regmap)
                                        regcache_mark_dirty(codec->component.regmap);
                                /* deactivate pins to sleep state */
@@ -676,7 +620,7 @@ int snd_soc_suspend(struct device *dev)
                if (card->rtd[i].dai_link->ignore_suspend)
                        continue;
 
-               if (cpu_dai->driver->suspend && cpu_dai->driver->ac97_control)
+               if (cpu_dai->driver->suspend && cpu_dai->driver->bus_control)
                        cpu_dai->driver->suspend(cpu_dai);
 
                /* deactivate pins to sleep state */
@@ -712,14 +656,14 @@ static void soc_resume_deferred(struct work_struct *work)
        if (card->resume_pre)
                card->resume_pre(card);
 
-       /* resume AC97 DAIs */
+       /* resume control bus DAIs */
        for (i = 0; i < card->num_rtd; i++) {
                struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
 
                if (card->rtd[i].dai_link->ignore_suspend)
                        continue;
 
-               if (cpu_dai->driver->resume && cpu_dai->driver->ac97_control)
+               if (cpu_dai->driver->resume && cpu_dai->driver->bus_control)
                        cpu_dai->driver->resume(cpu_dai);
        }
 
@@ -775,17 +719,12 @@ static void soc_resume_deferred(struct work_struct *work)
 
        for (i = 0; i < card->num_rtd; i++) {
                struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
-               struct snd_soc_platform *platform = card->rtd[i].platform;
 
                if (card->rtd[i].dai_link->ignore_suspend)
                        continue;
 
-               if (cpu_dai->driver->resume && !cpu_dai->driver->ac97_control)
+               if (cpu_dai->driver->resume && !cpu_dai->driver->bus_control)
                        cpu_dai->driver->resume(cpu_dai);
-               if (platform->driver->resume && platform->suspended) {
-                       platform->driver->resume(cpu_dai);
-                       platform->suspended = 0;
-               }
        }
 
        if (card->resume_post)
@@ -796,8 +735,8 @@ static void soc_resume_deferred(struct work_struct *work)
        /* userspace can access us now we are back as we were before */
        snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0);
 
-       /* Recheck all analogue paths too */
-       dapm_mark_io_dirty(&card->dapm);
+       /* Recheck all endpoints too, their state is affected by suspend */
+       dapm_mark_endpoints_dirty(card);
        snd_soc_dapm_sync(&card->dapm);
 }
 
@@ -805,7 +744,8 @@ static void soc_resume_deferred(struct work_struct *work)
 int snd_soc_resume(struct device *dev)
 {
        struct snd_soc_card *card = dev_get_drvdata(dev);
-       int i, ac97_control = 0;
+       bool bus_control = false;
+       int i;
 
        /* If the card is not initialized yet there is nothing to do */
        if (!card->instantiated)
@@ -828,17 +768,18 @@ int snd_soc_resume(struct device *dev)
                }
        }
 
-       /* AC97 devices might have other drivers hanging off them so
-        * need to resume immediately.  Other drivers don't have that
-        * problem and may take a substantial amount of time to resume
+       /*
+        * DAIs that also act as the control bus master might have other drivers
+        * hanging off them so need to resume immediately. Other drivers don't
+        * have that problem and may take a substantial amount of time to resume
         * due to I/O costs and anti-pop so handle them out of line.
         */
        for (i = 0; i < card->num_rtd; i++) {
                struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
-               ac97_control |= cpu_dai->driver->ac97_control;
+               bus_control |= cpu_dai->driver->bus_control;
        }
-       if (ac97_control) {
-               dev_dbg(dev, "ASoC: Resuming AC97 immediately\n");
+       if (bus_control) {
+               dev_dbg(dev, "ASoC: Resuming control bus master immediately\n");
                soc_resume_deferred(&card->deferred_resume_work);
        } else {
                dev_dbg(dev, "ASoC: Scheduling resume work\n");
@@ -884,7 +825,7 @@ static struct snd_soc_dai *snd_soc_find_dai(
        list_for_each_entry(component, &component_list, list) {
                if (dlc->of_node && component->dev->of_node != dlc->of_node)
                        continue;
-               if (dlc->name && strcmp(dev_name(component->dev), dlc->name))
+               if (dlc->name && strcmp(component->name, dlc->name))
                        continue;
                list_for_each_entry(dai, &component->dai_list, list) {
                        if (dlc->dai_name && strcmp(dai->name, dlc->dai_name))
@@ -1251,25 +1192,22 @@ static int soc_probe_link_components(struct snd_soc_card *card, int num,
        return 0;
 }
 
-static int soc_probe_codec_dai(struct snd_soc_card *card,
-                              struct snd_soc_dai *codec_dai,
-                              int order)
+static int soc_probe_dai(struct snd_soc_dai *dai, int order)
 {
        int ret;
 
-       if (!codec_dai->probed && codec_dai->driver->probe_order == order) {
-               if (codec_dai->driver->probe) {
-                       ret = codec_dai->driver->probe(codec_dai);
+       if (!dai->probed && dai->driver->probe_order == order) {
+               if (dai->driver->probe) {
+                       ret = dai->driver->probe(dai);
                        if (ret < 0) {
-                               dev_err(codec_dai->dev,
-                                       "ASoC: failed to probe CODEC DAI %s: %d\n",
-                                       codec_dai->name, ret);
+                               dev_err(dai->dev,
+                                       "ASoC: failed to probe DAI %s: %d\n",
+                                       dai->name, ret);
                                return ret;
                        }
                }
 
-               /* mark codec_dai as probed and add to card dai list */
-               codec_dai->probed = 1;
+               dai->probed = 1;
        }
 
        return 0;
@@ -1319,40 +1257,22 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
 {
        struct snd_soc_dai_link *dai_link = &card->dai_link[num];
        struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
-       struct snd_soc_platform *platform = rtd->platform;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        int i, ret;
 
        dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n",
                        card->name, num, order);
 
-       /* config components */
-       cpu_dai->platform = platform;
-       cpu_dai->card = card;
-       for (i = 0; i < rtd->num_codecs; i++)
-               rtd->codec_dais[i]->card = card;
-
        /* set default power off timeout */
        rtd->pmdown_time = pmdown_time;
 
-       /* probe the cpu_dai */
-       if (!cpu_dai->probed &&
-                       cpu_dai->driver->probe_order == order) {
-               if (cpu_dai->driver->probe) {
-                       ret = cpu_dai->driver->probe(cpu_dai);
-                       if (ret < 0) {
-                               dev_err(cpu_dai->dev,
-                                       "ASoC: failed to probe CPU DAI %s: %d\n",
-                                       cpu_dai->name, ret);
-                               return ret;
-                       }
-               }
-               cpu_dai->probed = 1;
-       }
+       ret = soc_probe_dai(cpu_dai, order);
+       if (ret)
+               return ret;
 
        /* probe the CODEC DAI */
        for (i = 0; i < rtd->num_codecs; i++) {
-               ret = soc_probe_codec_dai(card, rtd->codec_dais[i], order);
+               ret = soc_probe_dai(rtd->codec_dais[i], order);
                if (ret)
                        return ret;
        }
@@ -1422,84 +1342,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
                }
        }
 
-       /* add platform data for AC97 devices */
-       for (i = 0; i < rtd->num_codecs; i++) {
-               if (rtd->codec_dais[i]->driver->ac97_control)
-                       snd_ac97_dev_add_pdata(rtd->codec_dais[i]->codec->ac97,
-                                              rtd->cpu_dai->ac97_pdata);
-       }
-
-       return 0;
-}
-
-#ifdef CONFIG_SND_SOC_AC97_BUS
-static int soc_register_ac97_codec(struct snd_soc_codec *codec,
-                                  struct snd_soc_dai *codec_dai)
-{
-       int ret;
-
-       /* Only instantiate AC97 if not already done by the adaptor
-        * for the generic AC97 subsystem.
-        */
-       if (codec_dai->driver->ac97_control && !codec->ac97_registered) {
-               /*
-                * It is possible that the AC97 device is already registered to
-                * the device subsystem. This happens when the device is created
-                * via snd_ac97_mixer(). Currently only SoC codec that does so
-                * is the generic AC97 glue but others migh emerge.
-                *
-                * In those cases we don't try to register the device again.
-                */
-               if (!codec->ac97_created)
-                       return 0;
-
-               ret = soc_ac97_dev_register(codec);
-               if (ret < 0) {
-                       dev_err(codec->dev,
-                               "ASoC: AC97 device register failed: %d\n", ret);
-                       return ret;
-               }
-
-               codec->ac97_registered = 1;
-       }
-       return 0;
-}
-
-static void soc_unregister_ac97_codec(struct snd_soc_codec *codec)
-{
-       if (codec->ac97_registered) {
-               soc_ac97_dev_unregister(codec);
-               codec->ac97_registered = 0;
-       }
-}
-
-static int soc_register_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
-{
-       int i, ret;
-
-       for (i = 0; i < rtd->num_codecs; i++) {
-               struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
-
-               ret = soc_register_ac97_codec(codec_dai->codec, codec_dai);
-               if (ret) {
-                       while (--i >= 0)
-                               soc_unregister_ac97_codec(codec_dai->codec);
-                       return ret;
-               }
-       }
-
        return 0;
 }
 
-static void soc_unregister_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
-{
-       int i;
-
-       for (i = 0; i < rtd->num_codecs; i++)
-               soc_unregister_ac97_codec(rtd->codec_dais[i]->codec);
-}
-#endif
-
 static int soc_bind_aux_dev(struct snd_soc_card *card, int num)
 {
        struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
@@ -1793,20 +1638,6 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
                goto probe_aux_dev_err;
        }
 
-#ifdef CONFIG_SND_SOC_AC97_BUS
-       /* register any AC97 codecs */
-       for (i = 0; i < card->num_rtd; i++) {
-               ret = soc_register_ac97_dai_link(&card->rtd[i]);
-               if (ret < 0) {
-                       dev_err(card->dev,
-                               "ASoC: failed to register AC97: %d\n", ret);
-                       while (--i >= 0)
-                               soc_unregister_ac97_dai_link(&card->rtd[i]);
-                       goto probe_aux_dev_err;
-               }
-       }
-#endif
-
        card->instantiated = 1;
        snd_soc_dapm_sync(&card->dapm);
        mutex_unlock(&card->mutex);
@@ -1948,216 +1779,6 @@ static struct platform_driver soc_driver = {
        .remove         = soc_remove,
 };
 
-/**
- * snd_soc_new_ac97_codec - initailise AC97 device
- * @codec: audio codec
- * @ops: AC97 bus operations
- * @num: AC97 codec number
- *
- * Initialises AC97 codec resources for use by ad-hoc devices only.
- */
-int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
-       struct snd_ac97_bus_ops *ops, int num)
-{
-       codec->ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL);
-       if (codec->ac97 == NULL)
-               return -ENOMEM;
-
-       codec->ac97->bus = kzalloc(sizeof(struct snd_ac97_bus), GFP_KERNEL);
-       if (codec->ac97->bus == NULL) {
-               kfree(codec->ac97);
-               codec->ac97 = NULL;
-               return -ENOMEM;
-       }
-
-       codec->ac97->bus->ops = ops;
-       codec->ac97->num = num;
-
-       /*
-        * Mark the AC97 device to be created by us. This way we ensure that the
-        * device will be registered with the device subsystem later on.
-        */
-       codec->ac97_created = 1;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
-
-static struct snd_ac97_reset_cfg snd_ac97_rst_cfg;
-
-static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97)
-{
-       struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
-
-       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset);
-
-       gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1);
-
-       udelay(10);
-
-       gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
-
-       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
-       msleep(2);
-}
-
-static void snd_soc_ac97_reset(struct snd_ac97 *ac97)
-{
-       struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
-
-       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset);
-
-       gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
-       gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0);
-       gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0);
-
-       udelay(10);
-
-       gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1);
-
-       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
-       msleep(2);
-}
-
-static int snd_soc_ac97_parse_pinctl(struct device *dev,
-               struct snd_ac97_reset_cfg *cfg)
-{
-       struct pinctrl *p;
-       struct pinctrl_state *state;
-       int gpio;
-       int ret;
-
-       p = devm_pinctrl_get(dev);
-       if (IS_ERR(p)) {
-               dev_err(dev, "Failed to get pinctrl\n");
-               return PTR_ERR(p);
-       }
-       cfg->pctl = p;
-
-       state = pinctrl_lookup_state(p, "ac97-reset");
-       if (IS_ERR(state)) {
-               dev_err(dev, "Can't find pinctrl state ac97-reset\n");
-               return PTR_ERR(state);
-       }
-       cfg->pstate_reset = state;
-
-       state = pinctrl_lookup_state(p, "ac97-warm-reset");
-       if (IS_ERR(state)) {
-               dev_err(dev, "Can't find pinctrl state ac97-warm-reset\n");
-               return PTR_ERR(state);
-       }
-       cfg->pstate_warm_reset = state;
-
-       state = pinctrl_lookup_state(p, "ac97-running");
-       if (IS_ERR(state)) {
-               dev_err(dev, "Can't find pinctrl state ac97-running\n");
-               return PTR_ERR(state);
-       }
-       cfg->pstate_run = state;
-
-       gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0);
-       if (gpio < 0) {
-               dev_err(dev, "Can't find ac97-sync gpio\n");
-               return gpio;
-       }
-       ret = devm_gpio_request(dev, gpio, "AC97 link sync");
-       if (ret) {
-               dev_err(dev, "Failed requesting ac97-sync gpio\n");
-               return ret;
-       }
-       cfg->gpio_sync = gpio;
-
-       gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1);
-       if (gpio < 0) {
-               dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio);
-               return gpio;
-       }
-       ret = devm_gpio_request(dev, gpio, "AC97 link sdata");
-       if (ret) {
-               dev_err(dev, "Failed requesting ac97-sdata gpio\n");
-               return ret;
-       }
-       cfg->gpio_sdata = gpio;
-
-       gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2);
-       if (gpio < 0) {
-               dev_err(dev, "Can't find ac97-reset gpio\n");
-               return gpio;
-       }
-       ret = devm_gpio_request(dev, gpio, "AC97 link reset");
-       if (ret) {
-               dev_err(dev, "Failed requesting ac97-reset gpio\n");
-               return ret;
-       }
-       cfg->gpio_reset = gpio;
-
-       return 0;
-}
-
-struct snd_ac97_bus_ops *soc_ac97_ops;
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
-
-int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops)
-{
-       if (ops == soc_ac97_ops)
-               return 0;
-
-       if (soc_ac97_ops && ops)
-               return -EBUSY;
-
-       soc_ac97_ops = ops;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops);
-
-/**
- * snd_soc_set_ac97_ops_of_reset - Set ac97 ops with generic ac97 reset functions
- *
- * This function sets the reset and warm_reset properties of ops and parses
- * the device node of pdev to get pinctrl states and gpio numbers to use.
- */
-int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
-               struct platform_device *pdev)
-{
-       struct device *dev = &pdev->dev;
-       struct snd_ac97_reset_cfg cfg;
-       int ret;
-
-       ret = snd_soc_ac97_parse_pinctl(dev, &cfg);
-       if (ret)
-               return ret;
-
-       ret = snd_soc_set_ac97_ops(ops);
-       if (ret)
-               return ret;
-
-       ops->warm_reset = snd_soc_ac97_warm_reset;
-       ops->reset = snd_soc_ac97_reset;
-
-       snd_ac97_rst_cfg = cfg;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset);
-
-/**
- * snd_soc_free_ac97_codec - free AC97 codec device
- * @codec: audio codec
- *
- * Frees AC97 codec device resources.
- */
-void snd_soc_free_ac97_codec(struct snd_soc_codec *codec)
-{
-#ifdef CONFIG_SND_SOC_AC97_BUS
-       soc_unregister_ac97_codec(codec);
-#endif
-       kfree(codec->ac97->bus);
-       kfree(codec->ac97);
-       codec->ac97 = NULL;
-       codec->ac97_created = 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec);
-
 /**
  * snd_soc_cnew - create new control
  * @_template: control template
@@ -2326,7 +1947,7 @@ EXPORT_SYMBOL_GPL(snd_soc_add_card_controls);
 int snd_soc_add_dai_controls(struct snd_soc_dai *dai,
        const struct snd_kcontrol_new *controls, int num_controls)
 {
-       struct snd_card *card = dai->card->snd_card;
+       struct snd_card *card = dai->component->card->snd_card;
 
        return snd_soc_add_controls(card, dai->dev, controls, num_controls,
                        NULL, dai);
@@ -2334,1105 +1955,91 @@ int snd_soc_add_dai_controls(struct snd_soc_dai *dai,
 EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls);
 
 /**
- * snd_soc_info_enum_double - enumerated double mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information about a double enumerated
- * mixer control.
+ * snd_soc_dai_set_sysclk - configure DAI system or master clock.
+ * @dai: DAI
+ * @clk_id: DAI specific clock ID
+ * @freq: new clock frequency in Hz
+ * @dir: new clock direction - input/output.
  *
- * Returns 0 for success.
+ * Configures the DAI master (MCLK) or system (SYSCLK) clocking.
  */
-int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_info *uinfo)
+int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+       unsigned int freq, int dir)
 {
-       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-       uinfo->count = e->shift_l == e->shift_r ? 1 : 2;
-       uinfo->value.enumerated.items = e->items;
-
-       if (uinfo->value.enumerated.item >= e->items)
-               uinfo->value.enumerated.item = e->items - 1;
-       strlcpy(uinfo->value.enumerated.name,
-               e->texts[uinfo->value.enumerated.item],
-               sizeof(uinfo->value.enumerated.name));
-       return 0;
+       if (dai->driver && dai->driver->ops->set_sysclk)
+               return dai->driver->ops->set_sysclk(dai, clk_id, freq, dir);
+       else if (dai->codec && dai->codec->driver->set_sysclk)
+               return dai->codec->driver->set_sysclk(dai->codec, clk_id, 0,
+                                                     freq, dir);
+       else
+               return -ENOTSUPP;
 }
-EXPORT_SYMBOL_GPL(snd_soc_info_enum_double);
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk);
 
 /**
- * snd_soc_get_enum_double - enumerated double mixer get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value of a double enumerated mixer.
+ * snd_soc_codec_set_sysclk - configure CODEC system or master clock.
+ * @codec: CODEC
+ * @clk_id: DAI specific clock ID
+ * @source: Source for the clock
+ * @freq: new clock frequency in Hz
+ * @dir: new clock direction - input/output.
  *
- * Returns 0 for success.
+ * Configures the CODEC master (MCLK) or system (SYSCLK) clocking.
  */
-int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
+int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+                            int source, unsigned int freq, int dir)
 {
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-       unsigned int val, item;
-       unsigned int reg_val;
-       int ret;
-
-       ret = snd_soc_component_read(component, e->reg, &reg_val);
-       if (ret)
-               return ret;
-       val = (reg_val >> e->shift_l) & e->mask;
-       item = snd_soc_enum_val_to_item(e, val);
-       ucontrol->value.enumerated.item[0] = item;
-       if (e->shift_l != e->shift_r) {
-               val = (reg_val >> e->shift_l) & e->mask;
-               item = snd_soc_enum_val_to_item(e, val);
-               ucontrol->value.enumerated.item[1] = item;
-       }
-
-       return 0;
+       if (codec->driver->set_sysclk)
+               return codec->driver->set_sysclk(codec, clk_id, source,
+                                                freq, dir);
+       else
+               return -ENOTSUPP;
 }
-EXPORT_SYMBOL_GPL(snd_soc_get_enum_double);
+EXPORT_SYMBOL_GPL(snd_soc_codec_set_sysclk);
 
 /**
- * snd_soc_put_enum_double - enumerated double mixer put callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to set the value of a double enumerated mixer.
+ * snd_soc_dai_set_clkdiv - configure DAI clock dividers.
+ * @dai: DAI
+ * @div_id: DAI specific clock divider ID
+ * @div: new clock divisor.
  *
- * Returns 0 for success.
+ * Configures the clock dividers. This is used to derive the best DAI bit and
+ * frame clocks from the system or master clock. It's best to set the DAI bit
+ * and frame clocks as low as possible to save system power.
  */
-int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
+int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
+       int div_id, int div)
 {
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-       unsigned int *item = ucontrol->value.enumerated.item;
-       unsigned int val;
-       unsigned int mask;
-
-       if (item[0] >= e->items)
+       if (dai->driver && dai->driver->ops->set_clkdiv)
+               return dai->driver->ops->set_clkdiv(dai, div_id, div);
+       else
                return -EINVAL;
-       val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
-       mask = e->mask << e->shift_l;
-       if (e->shift_l != e->shift_r) {
-               if (item[1] >= e->items)
-                       return -EINVAL;
-               val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r;
-               mask |= e->mask << e->shift_r;
-       }
-
-       return snd_soc_component_update_bits(component, e->reg, mask, val);
 }
-EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv);
 
 /**
- * snd_soc_read_signed - Read a codec register and interprete as signed value
- * @component: component
- * @reg: Register to read
- * @mask: Mask to use after shifting the register value
- * @shift: Right shift of register value
- * @sign_bit: Bit that describes if a number is negative or not.
- * @signed_val: Pointer to where the read value should be stored
- *
- * This functions reads a codec register. The register value is shifted right
- * by 'shift' bits and masked with the given 'mask'. Afterwards it translates
- * the given registervalue into a signed integer if sign_bit is non-zero.
+ * snd_soc_dai_set_pll - configure DAI PLL.
+ * @dai: DAI
+ * @pll_id: DAI specific PLL ID
+ * @source: DAI specific source for the PLL
+ * @freq_in: PLL input clock frequency in Hz
+ * @freq_out: requested PLL output clock frequency in Hz
  *
- * Returns 0 on sucess, otherwise an error value
+ * Configures and enables PLL to generate output clock based on input clock.
  */
-static int snd_soc_read_signed(struct snd_soc_component *component,
-       unsigned int reg, unsigned int mask, unsigned int shift,
-       unsigned int sign_bit, int *signed_val)
+int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
+       unsigned int freq_in, unsigned int freq_out)
 {
-       int ret;
-       unsigned int val;
-
-       ret = snd_soc_component_read(component, reg, &val);
-       if (ret < 0)
-               return ret;
-
-       val = (val >> shift) & mask;
-
-       if (!sign_bit) {
-               *signed_val = val;
-               return 0;
-       }
-
-       /* non-negative number */
-       if (!(val & BIT(sign_bit))) {
-               *signed_val = val;
-               return 0;
-       }
-
-       ret = val;
-
-       /*
-        * The register most probably does not contain a full-sized int.
-        * Instead we have an arbitrary number of bits in a signed
-        * representation which has to be translated into a full-sized int.
-        * This is done by filling up all bits above the sign-bit.
-        */
-       ret |= ~((int)(BIT(sign_bit) - 1));
-
-       *signed_val = ret;
-
-       return 0;
-}
-
-/**
- * snd_soc_info_volsw - single mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information about a single mixer control, or a double
- * mixer control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_info *uinfo)
-{
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       int platform_max;
-
-       if (!mc->platform_max)
-               mc->platform_max = mc->max;
-       platform_max = mc->platform_max;
-
-       if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume"))
-               uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-       else
-               uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-
-       uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
-       uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = platform_max - mc->min;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
-
-/**
- * snd_soc_get_volsw - single mixer get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value of a single mixer control, or a double mixer
- * control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       unsigned int reg = mc->reg;
-       unsigned int reg2 = mc->rreg;
-       unsigned int shift = mc->shift;
-       unsigned int rshift = mc->rshift;
-       int max = mc->max;
-       int min = mc->min;
-       int sign_bit = mc->sign_bit;
-       unsigned int mask = (1 << fls(max)) - 1;
-       unsigned int invert = mc->invert;
-       int val;
-       int ret;
-
-       if (sign_bit)
-               mask = BIT(sign_bit + 1) - 1;
-
-       ret = snd_soc_read_signed(component, reg, mask, shift, sign_bit, &val);
-       if (ret)
-               return ret;
-
-       ucontrol->value.integer.value[0] = val - min;
-       if (invert)
-               ucontrol->value.integer.value[0] =
-                       max - ucontrol->value.integer.value[0];
-
-       if (snd_soc_volsw_is_stereo(mc)) {
-               if (reg == reg2)
-                       ret = snd_soc_read_signed(component, reg, mask, rshift,
-                               sign_bit, &val);
-               else
-                       ret = snd_soc_read_signed(component, reg2, mask, shift,
-                               sign_bit, &val);
-               if (ret)
-                       return ret;
-
-               ucontrol->value.integer.value[1] = val - min;
-               if (invert)
-                       ucontrol->value.integer.value[1] =
-                               max - ucontrol->value.integer.value[1];
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_volsw);
-
-/**
- * snd_soc_put_volsw - single mixer put callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to set the value of a single mixer control, or a double mixer
- * control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       unsigned int reg = mc->reg;
-       unsigned int reg2 = mc->rreg;
-       unsigned int shift = mc->shift;
-       unsigned int rshift = mc->rshift;
-       int max = mc->max;
-       int min = mc->min;
-       unsigned int sign_bit = mc->sign_bit;
-       unsigned int mask = (1 << fls(max)) - 1;
-       unsigned int invert = mc->invert;
-       int err;
-       bool type_2r = false;
-       unsigned int val2 = 0;
-       unsigned int val, val_mask;
-
-       if (sign_bit)
-               mask = BIT(sign_bit + 1) - 1;
-
-       val = ((ucontrol->value.integer.value[0] + min) & mask);
-       if (invert)
-               val = max - val;
-       val_mask = mask << shift;
-       val = val << shift;
-       if (snd_soc_volsw_is_stereo(mc)) {
-               val2 = ((ucontrol->value.integer.value[1] + min) & mask);
-               if (invert)
-                       val2 = max - val2;
-               if (reg == reg2) {
-                       val_mask |= mask << rshift;
-                       val |= val2 << rshift;
-               } else {
-                       val2 = val2 << shift;
-                       type_2r = true;
-               }
-       }
-       err = snd_soc_component_update_bits(component, reg, val_mask, val);
-       if (err < 0)
-               return err;
-
-       if (type_2r)
-               err = snd_soc_component_update_bits(component, reg2, val_mask,
-                       val2);
-
-       return err;
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
-
-/**
- * snd_soc_get_volsw_sx - single mixer get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value of a single mixer control, or a double mixer
- * control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
-                     struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_mixer_control *mc =
-           (struct soc_mixer_control *)kcontrol->private_value;
-       unsigned int reg = mc->reg;
-       unsigned int reg2 = mc->rreg;
-       unsigned int shift = mc->shift;
-       unsigned int rshift = mc->rshift;
-       int max = mc->max;
-       int min = mc->min;
-       int mask = (1 << (fls(min + max) - 1)) - 1;
-       unsigned int val;
-       int ret;
-
-       ret = snd_soc_component_read(component, reg, &val);
-       if (ret < 0)
-               return ret;
-
-       ucontrol->value.integer.value[0] = ((val >> shift) - min) & mask;
-
-       if (snd_soc_volsw_is_stereo(mc)) {
-               ret = snd_soc_component_read(component, reg2, &val);
-               if (ret < 0)
-                       return ret;
-
-               val = ((val >> rshift) - min) & mask;
-               ucontrol->value.integer.value[1] = val;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx);
-
-/**
- * snd_soc_put_volsw_sx - double mixer set callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to set the value of a double mixer control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
-                        struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_mixer_control *mc =
-           (struct soc_mixer_control *)kcontrol->private_value;
-
-       unsigned int reg = mc->reg;
-       unsigned int reg2 = mc->rreg;
-       unsigned int shift = mc->shift;
-       unsigned int rshift = mc->rshift;
-       int max = mc->max;
-       int min = mc->min;
-       int mask = (1 << (fls(min + max) - 1)) - 1;
-       int err = 0;
-       unsigned int val, val_mask, val2 = 0;
-
-       val_mask = mask << shift;
-       val = (ucontrol->value.integer.value[0] + min) & mask;
-       val = val << shift;
-
-       err = snd_soc_component_update_bits(component, reg, val_mask, val);
-       if (err < 0)
-               return err;
-
-       if (snd_soc_volsw_is_stereo(mc)) {
-               val_mask = mask << rshift;
-               val2 = (ucontrol->value.integer.value[1] + min) & mask;
-               val2 = val2 << rshift;
-
-               err = snd_soc_component_update_bits(component, reg2, val_mask,
-                       val2);
-       }
-       return err;
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx);
-
-/**
- * snd_soc_info_volsw_s8 - signed mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information about a signed mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_info *uinfo)
-{
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       int platform_max;
-       int min = mc->min;
-
-       if (!mc->platform_max)
-               mc->platform_max = mc->max;
-       platform_max = mc->platform_max;
-
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-       uinfo->count = 2;
-       uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = platform_max - min;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_volsw_s8);
-
-/**
- * snd_soc_get_volsw_s8 - signed mixer get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value of a signed mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       unsigned int reg = mc->reg;
-       unsigned int val;
-       int min = mc->min;
-       int ret;
-
-       ret = snd_soc_component_read(component, reg, &val);
-       if (ret)
-               return ret;
-
-       ucontrol->value.integer.value[0] =
-               ((signed char)(val & 0xff))-min;
-       ucontrol->value.integer.value[1] =
-               ((signed char)((val >> 8) & 0xff))-min;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_volsw_s8);
-
-/**
- * snd_soc_put_volsw_sgn - signed mixer put callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to set the value of a signed mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       unsigned int reg = mc->reg;
-       int min = mc->min;
-       unsigned int val;
-
-       val = (ucontrol->value.integer.value[0]+min) & 0xff;
-       val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8;
-
-       return snd_soc_component_update_bits(component, reg, 0xffff, val);
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
-
-/**
- * snd_soc_info_volsw_range - single mixer info callback with range.
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information, within a range, about a single
- * mixer control.
- *
- * returns 0 for success.
- */
-int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_info *uinfo)
-{
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       int platform_max;
-       int min = mc->min;
-
-       if (!mc->platform_max)
-               mc->platform_max = mc->max;
-       platform_max = mc->platform_max;
-
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-       uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
-       uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = platform_max - min;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_volsw_range);
-
-/**
- * snd_soc_put_volsw_range - single mixer put value callback with range.
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to set the value, within a range, for a single mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       unsigned int reg = mc->reg;
-       unsigned int rreg = mc->rreg;
-       unsigned int shift = mc->shift;
-       int min = mc->min;
-       int max = mc->max;
-       unsigned int mask = (1 << fls(max)) - 1;
-       unsigned int invert = mc->invert;
-       unsigned int val, val_mask;
-       int ret;
-
-       if (invert)
-               val = (max - ucontrol->value.integer.value[0]) & mask;
-       else
-               val = ((ucontrol->value.integer.value[0] + min) & mask);
-       val_mask = mask << shift;
-       val = val << shift;
-
-       ret = snd_soc_component_update_bits(component, reg, val_mask, val);
-       if (ret < 0)
-               return ret;
-
-       if (snd_soc_volsw_is_stereo(mc)) {
-               if (invert)
-                       val = (max - ucontrol->value.integer.value[1]) & mask;
-               else
-                       val = ((ucontrol->value.integer.value[1] + min) & mask);
-               val_mask = mask << shift;
-               val = val << shift;
-
-               ret = snd_soc_component_update_bits(component, rreg, val_mask,
-                       val);
-       }
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range);
-
-/**
- * snd_soc_get_volsw_range - single mixer get callback with range
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value, within a range, of a single mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       unsigned int reg = mc->reg;
-       unsigned int rreg = mc->rreg;
-       unsigned int shift = mc->shift;
-       int min = mc->min;
-       int max = mc->max;
-       unsigned int mask = (1 << fls(max)) - 1;
-       unsigned int invert = mc->invert;
-       unsigned int val;
-       int ret;
-
-       ret = snd_soc_component_read(component, reg, &val);
-       if (ret)
-               return ret;
-
-       ucontrol->value.integer.value[0] = (val >> shift) & mask;
-       if (invert)
-               ucontrol->value.integer.value[0] =
-                       max - ucontrol->value.integer.value[0];
-       else
-               ucontrol->value.integer.value[0] =
-                       ucontrol->value.integer.value[0] - min;
-
-       if (snd_soc_volsw_is_stereo(mc)) {
-               ret = snd_soc_component_read(component, rreg, &val);
-               if (ret)
-                       return ret;
-
-               ucontrol->value.integer.value[1] = (val >> shift) & mask;
-               if (invert)
-                       ucontrol->value.integer.value[1] =
-                               max - ucontrol->value.integer.value[1];
-               else
-                       ucontrol->value.integer.value[1] =
-                               ucontrol->value.integer.value[1] - min;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range);
-
-/**
- * snd_soc_limit_volume - Set new limit to an existing volume control.
- *
- * @codec: where to look for the control
- * @name: Name of the control
- * @max: new maximum limit
- *
- * Return 0 for success, else error.
- */
-int snd_soc_limit_volume(struct snd_soc_codec *codec,
-       const char *name, int max)
-{
-       struct snd_card *card = codec->component.card->snd_card;
-       struct snd_kcontrol *kctl;
-       struct soc_mixer_control *mc;
-       int found = 0;
-       int ret = -EINVAL;
-
-       /* Sanity check for name and max */
-       if (unlikely(!name || max <= 0))
-               return -EINVAL;
-
-       list_for_each_entry(kctl, &card->controls, list) {
-               if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) {
-                       found = 1;
-                       break;
-               }
-       }
-       if (found) {
-               mc = (struct soc_mixer_control *)kctl->private_value;
-               if (max <= mc->max) {
-                       mc->platform_max = max;
-                       ret = 0;
-               }
-       }
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_limit_volume);
-
-int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
-                      struct snd_ctl_elem_info *uinfo)
-{
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_bytes *params = (void *)kcontrol->private_value;
-
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
-       uinfo->count = params->num_regs * component->val_bytes;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_info);
-
-int snd_soc_bytes_get(struct snd_kcontrol *kcontrol,
-                     struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_bytes *params = (void *)kcontrol->private_value;
-       int ret;
-
-       if (component->regmap)
-               ret = regmap_raw_read(component->regmap, params->base,
-                                     ucontrol->value.bytes.data,
-                                     params->num_regs * component->val_bytes);
-       else
-               ret = -EINVAL;
-
-       /* Hide any masked bytes to ensure consistent data reporting */
-       if (ret == 0 && params->mask) {
-               switch (component->val_bytes) {
-               case 1:
-                       ucontrol->value.bytes.data[0] &= ~params->mask;
-                       break;
-               case 2:
-                       ((u16 *)(&ucontrol->value.bytes.data))[0]
-                               &= cpu_to_be16(~params->mask);
-                       break;
-               case 4:
-                       ((u32 *)(&ucontrol->value.bytes.data))[0]
-                               &= cpu_to_be32(~params->mask);
-                       break;
-               default:
-                       return -EINVAL;
-               }
-       }
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_get);
-
-int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
-                     struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_bytes *params = (void *)kcontrol->private_value;
-       int ret, len;
-       unsigned int val, mask;
-       void *data;
-
-       if (!component->regmap || !params->num_regs)
-               return -EINVAL;
-
-       len = params->num_regs * component->val_bytes;
-
-       data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA);
-       if (!data)
-               return -ENOMEM;
-
-       /*
-        * If we've got a mask then we need to preserve the register
-        * bits.  We shouldn't modify the incoming data so take a
-        * copy.
-        */
-       if (params->mask) {
-               ret = regmap_read(component->regmap, params->base, &val);
-               if (ret != 0)
-                       goto out;
-
-               val &= params->mask;
-
-               switch (component->val_bytes) {
-               case 1:
-                       ((u8 *)data)[0] &= ~params->mask;
-                       ((u8 *)data)[0] |= val;
-                       break;
-               case 2:
-                       mask = ~params->mask;
-                       ret = regmap_parse_val(component->regmap,
-                                                       &mask, &mask);
-                       if (ret != 0)
-                               goto out;
-
-                       ((u16 *)data)[0] &= mask;
-
-                       ret = regmap_parse_val(component->regmap,
-                                                       &val, &val);
-                       if (ret != 0)
-                               goto out;
-
-                       ((u16 *)data)[0] |= val;
-                       break;
-               case 4:
-                       mask = ~params->mask;
-                       ret = regmap_parse_val(component->regmap,
-                                                       &mask, &mask);
-                       if (ret != 0)
-                               goto out;
-
-                       ((u32 *)data)[0] &= mask;
-
-                       ret = regmap_parse_val(component->regmap,
-                                                       &val, &val);
-                       if (ret != 0)
-                               goto out;
-
-                       ((u32 *)data)[0] |= val;
-                       break;
-               default:
-                       ret = -EINVAL;
-                       goto out;
-               }
-       }
-
-       ret = regmap_raw_write(component->regmap, params->base,
-                              data, len);
-
-out:
-       kfree(data);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_put);
-
-int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol,
-                       struct snd_ctl_elem_info *ucontrol)
-{
-       struct soc_bytes_ext *params = (void *)kcontrol->private_value;
-
-       ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES;
-       ucontrol->count = params->max;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_info_ext);
-
-int snd_soc_bytes_tlv_callback(struct snd_kcontrol *kcontrol, int op_flag,
-                               unsigned int size, unsigned int __user *tlv)
-{
-       struct soc_bytes_ext *params = (void *)kcontrol->private_value;
-       unsigned int count = size < params->max ? size : params->max;
-       int ret = -ENXIO;
-
-       switch (op_flag) {
-       case SNDRV_CTL_TLV_OP_READ:
-               if (params->get)
-                       ret = params->get(tlv, count);
-               break;
-       case SNDRV_CTL_TLV_OP_WRITE:
-               if (params->put)
-                       ret = params->put(tlv, count);
-               break;
-       }
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_tlv_callback);
-
-/**
- * snd_soc_info_xr_sx - signed multi register info callback
- * @kcontrol: mreg control
- * @uinfo: control element information
- *
- * Callback to provide information of a control that can
- * span multiple codec registers which together
- * forms a single signed value in a MSB/LSB manner.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_info *uinfo)
-{
-       struct soc_mreg_control *mc =
-               (struct soc_mreg_control *)kcontrol->private_value;
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-       uinfo->count = 1;
-       uinfo->value.integer.min = mc->min;
-       uinfo->value.integer.max = mc->max;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx);
-
-/**
- * snd_soc_get_xr_sx - signed multi register get callback
- * @kcontrol: mreg control
- * @ucontrol: control element information
- *
- * Callback to get the value of a control that can span
- * multiple codec registers which together forms a single
- * signed value in a MSB/LSB manner. The control supports
- * specifying total no of bits used to allow for bitfields
- * across the multiple codec registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_mreg_control *mc =
-               (struct soc_mreg_control *)kcontrol->private_value;
-       unsigned int regbase = mc->regbase;
-       unsigned int regcount = mc->regcount;
-       unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
-       unsigned int regwmask = (1<<regwshift)-1;
-       unsigned int invert = mc->invert;
-       unsigned long mask = (1UL<<mc->nbits)-1;
-       long min = mc->min;
-       long max = mc->max;
-       long val = 0;
-       unsigned int regval;
-       unsigned int i;
-       int ret;
-
-       for (i = 0; i < regcount; i++) {
-               ret = snd_soc_component_read(component, regbase+i, &regval);
-               if (ret)
-                       return ret;
-               val |= (regval & regwmask) << (regwshift*(regcount-i-1));
-       }
-       val &= mask;
-       if (min < 0 && val > max)
-               val |= ~mask;
-       if (invert)
-               val = max - val;
-       ucontrol->value.integer.value[0] = val;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx);
-
-/**
- * snd_soc_put_xr_sx - signed multi register get callback
- * @kcontrol: mreg control
- * @ucontrol: control element information
- *
- * Callback to set the value of a control that can span
- * multiple codec registers which together forms a single
- * signed value in a MSB/LSB manner. The control supports
- * specifying total no of bits used to allow for bitfields
- * across the multiple codec registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_mreg_control *mc =
-               (struct soc_mreg_control *)kcontrol->private_value;
-       unsigned int regbase = mc->regbase;
-       unsigned int regcount = mc->regcount;
-       unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
-       unsigned int regwmask = (1<<regwshift)-1;
-       unsigned int invert = mc->invert;
-       unsigned long mask = (1UL<<mc->nbits)-1;
-       long max = mc->max;
-       long val = ucontrol->value.integer.value[0];
-       unsigned int i, regval, regmask;
-       int err;
-
-       if (invert)
-               val = max - val;
-       val &= mask;
-       for (i = 0; i < regcount; i++) {
-               regval = (val >> (regwshift*(regcount-i-1))) & regwmask;
-               regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask;
-               err = snd_soc_component_update_bits(component, regbase+i,
-                               regmask, regval);
-               if (err < 0)
-                       return err;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx);
-
-/**
- * snd_soc_get_strobe - strobe get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback get the value of a strobe mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_strobe(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       unsigned int reg = mc->reg;
-       unsigned int shift = mc->shift;
-       unsigned int mask = 1 << shift;
-       unsigned int invert = mc->invert != 0;
-       unsigned int val;
-       int ret;
-
-       ret = snd_soc_component_read(component, reg, &val);
-       if (ret)
-               return ret;
-
-       val &= mask;
-
-       if (shift != 0 && val != 0)
-               val = val >> shift;
-       ucontrol->value.enumerated.item[0] = val ^ invert;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_strobe);
-
-/**
- * snd_soc_put_strobe - strobe put callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback strobe a register bit to high then low (or the inverse)
- * in one pass of a single mixer enum control.
- *
- * Returns 1 for success.
- */
-int snd_soc_put_strobe(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       unsigned int reg = mc->reg;
-       unsigned int shift = mc->shift;
-       unsigned int mask = 1 << shift;
-       unsigned int invert = mc->invert != 0;
-       unsigned int strobe = ucontrol->value.enumerated.item[0] != 0;
-       unsigned int val1 = (strobe ^ invert) ? mask : 0;
-       unsigned int val2 = (strobe ^ invert) ? 0 : mask;
-       int err;
-
-       err = snd_soc_component_update_bits(component, reg, mask, val1);
-       if (err < 0)
-               return err;
-
-       return snd_soc_component_update_bits(component, reg, mask, val2);
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_strobe);
-
-/**
- * snd_soc_dai_set_sysclk - configure DAI system or master clock.
- * @dai: DAI
- * @clk_id: DAI specific clock ID
- * @freq: new clock frequency in Hz
- * @dir: new clock direction - input/output.
- *
- * Configures the DAI master (MCLK) or system (SYSCLK) clocking.
- */
-int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
-       unsigned int freq, int dir)
-{
-       if (dai->driver && dai->driver->ops->set_sysclk)
-               return dai->driver->ops->set_sysclk(dai, clk_id, freq, dir);
-       else if (dai->codec && dai->codec->driver->set_sysclk)
-               return dai->codec->driver->set_sysclk(dai->codec, clk_id, 0,
-                                                     freq, dir);
-       else
-               return -ENOTSUPP;
-}
-EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk);
-
-/**
- * snd_soc_codec_set_sysclk - configure CODEC system or master clock.
- * @codec: CODEC
- * @clk_id: DAI specific clock ID
- * @source: Source for the clock
- * @freq: new clock frequency in Hz
- * @dir: new clock direction - input/output.
- *
- * Configures the CODEC master (MCLK) or system (SYSCLK) clocking.
- */
-int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id,
-                            int source, unsigned int freq, int dir)
-{
-       if (codec->driver->set_sysclk)
-               return codec->driver->set_sysclk(codec, clk_id, source,
-                                                freq, dir);
-       else
-               return -ENOTSUPP;
-}
-EXPORT_SYMBOL_GPL(snd_soc_codec_set_sysclk);
-
-/**
- * snd_soc_dai_set_clkdiv - configure DAI clock dividers.
- * @dai: DAI
- * @div_id: DAI specific clock divider ID
- * @div: new clock divisor.
- *
- * Configures the clock dividers. This is used to derive the best DAI bit and
- * frame clocks from the system or master clock. It's best to set the DAI bit
- * and frame clocks as low as possible to save system power.
- */
-int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
-       int div_id, int div)
-{
-       if (dai->driver && dai->driver->ops->set_clkdiv)
-               return dai->driver->ops->set_clkdiv(dai, div_id, div);
-       else
-               return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv);
-
-/**
- * snd_soc_dai_set_pll - configure DAI PLL.
- * @dai: DAI
- * @pll_id: DAI specific PLL ID
- * @source: DAI specific source for the PLL
- * @freq_in: PLL input clock frequency in Hz
- * @freq_out: requested PLL output clock frequency in Hz
- *
- * Configures and enables PLL to generate output clock based on input clock.
- */
-int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
-       unsigned int freq_in, unsigned int freq_out)
-{
-       if (dai->driver && dai->driver->ops->set_pll)
-               return dai->driver->ops->set_pll(dai, pll_id, source,
-                                        freq_in, freq_out);
-       else if (dai->codec && dai->codec->driver->set_pll)
-               return dai->codec->driver->set_pll(dai->codec, pll_id, source,
-                                                  freq_in, freq_out);
-       else
-               return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll);
+       if (dai->driver && dai->driver->ops->set_pll)
+               return dai->driver->ops->set_pll(dai, pll_id, source,
+                                        freq_in, freq_out);
+       else if (dai->codec && dai->codec->driver->set_pll)
+               return dai->codec->driver->set_pll(dai->codec, pll_id, source,
+                                                  freq_in, freq_out);
+       else
+               return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll);
 
 /*
  * snd_soc_codec_set_pll - configure codec PLL.
@@ -3996,22 +2603,62 @@ static int snd_soc_component_initialize(struct snd_soc_component *component,
        return 0;
 }
 
-static void snd_soc_component_init_regmap(struct snd_soc_component *component)
+static void snd_soc_component_setup_regmap(struct snd_soc_component *component)
 {
-       if (!component->regmap)
-               component->regmap = dev_get_regmap(component->dev, NULL);
-       if (component->regmap) {
-               int val_bytes = regmap_get_val_bytes(component->regmap);
-               /* Errors are legitimate for non-integer byte multiples */
-               if (val_bytes > 0)
-                       component->val_bytes = val_bytes;
-       }
+       int val_bytes = regmap_get_val_bytes(component->regmap);
+
+       /* Errors are legitimate for non-integer byte multiples */
+       if (val_bytes > 0)
+               component->val_bytes = val_bytes;
+}
+
+#ifdef CONFIG_REGMAP
+
+/**
+ * snd_soc_component_init_regmap() - Initialize regmap instance for the component
+ * @component: The component for which to initialize the regmap instance
+ * @regmap: The regmap instance that should be used by the component
+ *
+ * This function allows deferred assignment of the regmap instance that is
+ * associated with the component. Only use this if the regmap instance is not
+ * yet ready when the component is registered. The function must also be called
+ * before the first IO attempt of the component.
+ */
+void snd_soc_component_init_regmap(struct snd_soc_component *component,
+       struct regmap *regmap)
+{
+       component->regmap = regmap;
+       snd_soc_component_setup_regmap(component);
 }
+EXPORT_SYMBOL_GPL(snd_soc_component_init_regmap);
+
+/**
+ * snd_soc_component_exit_regmap() - De-initialize regmap instance for the component
+ * @component: The component for which to de-initialize the regmap instance
+ *
+ * Calls regmap_exit() on the regmap instance associated to the component and
+ * removes the regmap instance from the component.
+ *
+ * This function should only be used if snd_soc_component_init_regmap() was used
+ * to initialize the regmap instance.
+ */
+void snd_soc_component_exit_regmap(struct snd_soc_component *component)
+{
+       regmap_exit(component->regmap);
+       component->regmap = NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_exit_regmap);
+
+#endif
 
 static void snd_soc_component_add_unlocked(struct snd_soc_component *component)
 {
-       if (!component->write && !component->read)
-               snd_soc_component_init_regmap(component);
+       if (!component->write && !component->read) {
+               if (!component->regmap)
+                       component->regmap = dev_get_regmap(component->dev, NULL);
+               if (component->regmap)
+                       snd_soc_component_setup_regmap(component);
+       }
 
        list_add(&component->list, &component_list);
 }
@@ -4362,7 +3009,6 @@ int snd_soc_register_codec(struct device *dev,
        codec->dev = dev;
        codec->driver = codec_drv;
        codec->component.val_bytes = codec_drv->reg_word_size;
-       mutex_init(&codec->mutex);
 
 #ifdef CONFIG_DEBUG_FS
        codec->component.init_debugfs = soc_init_codec_debugfs;
@@ -4585,7 +3231,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
                                   const char *propname)
 {
        struct device_node *np = card->dev->of_node;
-       int num_routes;
+       int num_routes, old_routes;
        struct snd_soc_dapm_route *routes;
        int i, ret;
 
@@ -4603,7 +3249,9 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
                return -EINVAL;
        }
 
-       routes = devm_kzalloc(card->dev, num_routes * sizeof(*routes),
+       old_routes = card->num_dapm_routes;
+       routes = devm_kzalloc(card->dev,
+                             (old_routes + num_routes) * sizeof(*routes),
                              GFP_KERNEL);
        if (!routes) {
                dev_err(card->dev,
@@ -4611,9 +3259,11 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
                return -EINVAL;
        }
 
+       memcpy(routes, card->dapm_routes, old_routes * sizeof(*routes));
+
        for (i = 0; i < num_routes; i++) {
                ret = of_property_read_string_index(np, propname,
-                       2 * i, &routes[i].sink);
+                       2 * i, &routes[old_routes + i].sink);
                if (ret) {
                        dev_err(card->dev,
                                "ASoC: Property '%s' index %d could not be read: %d\n",
@@ -4621,7 +3271,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
                        return -EINVAL;
                }
                ret = of_property_read_string_index(np, propname,
-                       (2 * i) + 1, &routes[i].source);
+                       (2 * i) + 1, &routes[old_routes + i].source);
                if (ret) {
                        dev_err(card->dev,
                                "ASoC: Property '%s' index %d could not be read: %d\n",
@@ -4630,7 +3280,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
                }
        }
 
-       card->num_dapm_routes = num_routes;
+       card->num_dapm_routes += num_routes;
        card->dapm_routes = routes;
 
        return 0;
@@ -4750,36 +3400,30 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
 }
 EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt);
 
-int snd_soc_of_get_dai_name(struct device_node *of_node,
-                           const char **dai_name)
+static int snd_soc_get_dai_name(struct of_phandle_args *args,
+                               const char **dai_name)
 {
        struct snd_soc_component *pos;
-       struct of_phandle_args args;
-       int ret;
-
-       ret = of_parse_phandle_with_args(of_node, "sound-dai",
-                                        "#sound-dai-cells", 0, &args);
-       if (ret)
-               return ret;
-
-       ret = -EPROBE_DEFER;
+       int ret = -EPROBE_DEFER;
 
        mutex_lock(&client_mutex);
        list_for_each_entry(pos, &component_list, list) {
-               if (pos->dev->of_node != args.np)
+               if (pos->dev->of_node != args->np)
                        continue;
 
                if (pos->driver->of_xlate_dai_name) {
-                       ret = pos->driver->of_xlate_dai_name(pos, &args, dai_name);
+                       ret = pos->driver->of_xlate_dai_name(pos,
+                                                            args,
+                                                            dai_name);
                } else {
                        int id = -1;
 
-                       switch (args.args_count) {
+                       switch (args->args_count) {
                        case 0:
                                id = 0; /* same as dai_drv[0] */
                                break;
                        case 1:
-                               id = args.args[0];
+                               id = args->args[0];
                                break;
                        default:
                                /* not supported */
@@ -4801,6 +3445,21 @@ int snd_soc_of_get_dai_name(struct device_node *of_node,
                break;
        }
        mutex_unlock(&client_mutex);
+       return ret;
+}
+
+int snd_soc_of_get_dai_name(struct device_node *of_node,
+                           const char **dai_name)
+{
+       struct of_phandle_args args;
+       int ret;
+
+       ret = of_parse_phandle_with_args(of_node, "sound-dai",
+                                        "#sound-dai-cells", 0, &args);
+       if (ret)
+               return ret;
+
+       ret = snd_soc_get_dai_name(&args, dai_name);
 
        of_node_put(args.np);
 
@@ -4808,6 +3467,77 @@ int snd_soc_of_get_dai_name(struct device_node *of_node,
 }
 EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_name);
 
+/*
+ * snd_soc_of_get_dai_link_codecs - Parse a list of CODECs in the devicetree
+ * @dev: Card device
+ * @of_node: Device node
+ * @dai_link: DAI link
+ *
+ * Builds an array of CODEC DAI components from the DAI link property
+ * 'sound-dai'.
+ * The array is set in the DAI link and the number of DAIs is set accordingly.
+ * The device nodes in the array (of_node) must be dereferenced by the caller.
+ *
+ * Returns 0 for success
+ */
+int snd_soc_of_get_dai_link_codecs(struct device *dev,
+                                  struct device_node *of_node,
+                                  struct snd_soc_dai_link *dai_link)
+{
+       struct of_phandle_args args;
+       struct snd_soc_dai_link_component *component;
+       char *name;
+       int index, num_codecs, ret;
+
+       /* Count the number of CODECs */
+       name = "sound-dai";
+       num_codecs = of_count_phandle_with_args(of_node, name,
+                                               "#sound-dai-cells");
+       if (num_codecs <= 0) {
+               if (num_codecs == -ENOENT)
+                       dev_err(dev, "No 'sound-dai' property\n");
+               else
+                       dev_err(dev, "Bad phandle in 'sound-dai'\n");
+               return num_codecs;
+       }
+       component = devm_kzalloc(dev,
+                                sizeof *component * num_codecs,
+                                GFP_KERNEL);
+       if (!component)
+               return -ENOMEM;
+       dai_link->codecs = component;
+       dai_link->num_codecs = num_codecs;
+
+       /* Parse the list */
+       for (index = 0, component = dai_link->codecs;
+            index < dai_link->num_codecs;
+            index++, component++) {
+               ret = of_parse_phandle_with_args(of_node, name,
+                                                "#sound-dai-cells",
+                                                 index, &args);
+               if (ret)
+                       goto err;
+               component->of_node = args.np;
+               ret = snd_soc_get_dai_name(&args, &component->dai_name);
+               if (ret < 0)
+                       goto err;
+       }
+       return 0;
+err:
+       for (index = 0, component = dai_link->codecs;
+            index < dai_link->num_codecs;
+            index++, component++) {
+               if (!component->of_node)
+                       break;
+               of_node_put(component->of_node);
+               component->of_node = NULL;
+       }
+       dai_link->codecs = NULL;
+       dai_link->num_codecs = 0;
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_link_codecs);
+
 static int __init snd_soc_init(void)
 {
 #ifdef CONFIG_DEBUG_FS
index c61cb9c..c5136bb 100644 (file)
@@ -159,27 +159,135 @@ static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason)
        }
 }
 
-void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm)
+/*
+ * dapm_widget_invalidate_input_paths() - Invalidate the cached number of input
+ *  paths
+ * @w: The widget for which to invalidate the cached number of input paths
+ *
+ * The function resets the cached number of inputs for the specified widget and
+ * all widgets that can be reached via outgoing paths from the widget.
+ *
+ * This function must be called if the number of input paths for a widget might
+ * have changed. E.g. if the source state of a widget changes or a path is added
+ * or activated with the widget as the sink.
+ */
+static void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w)
+{
+       struct snd_soc_dapm_widget *sink;
+       struct snd_soc_dapm_path *p;
+       LIST_HEAD(list);
+
+       dapm_assert_locked(w->dapm);
+
+       if (w->inputs == -1)
+               return;
+
+       w->inputs = -1;
+       list_add_tail(&w->work_list, &list);
+
+       list_for_each_entry(w, &list, work_list) {
+               list_for_each_entry(p, &w->sinks, list_source) {
+                       if (p->is_supply || p->weak || !p->connect)
+                               continue;
+                       sink = p->sink;
+                       if (sink->inputs != -1) {
+                               sink->inputs = -1;
+                               list_add_tail(&sink->work_list, &list);
+                       }
+               }
+       }
+}
+
+/*
+ * dapm_widget_invalidate_output_paths() - Invalidate the cached number of
+ *  output paths
+ * @w: The widget for which to invalidate the cached number of output paths
+ *
+ * Resets the cached number of outputs for the specified widget and all widgets
+ * that can be reached via incoming paths from the widget.
+ *
+ * This function must be called if the number of output paths for a widget might
+ * have changed. E.g. if the sink state of a widget changes or a path is added
+ * or activated with the widget as the source.
+ */
+static void dapm_widget_invalidate_output_paths(struct snd_soc_dapm_widget *w)
+{
+       struct snd_soc_dapm_widget *source;
+       struct snd_soc_dapm_path *p;
+       LIST_HEAD(list);
+
+       dapm_assert_locked(w->dapm);
+
+       if (w->outputs == -1)
+               return;
+
+       w->outputs = -1;
+       list_add_tail(&w->work_list, &list);
+
+       list_for_each_entry(w, &list, work_list) {
+               list_for_each_entry(p, &w->sources, list_sink) {
+                       if (p->is_supply || p->weak || !p->connect)
+                               continue;
+                       source = p->source;
+                       if (source->outputs != -1) {
+                               source->outputs = -1;
+                               list_add_tail(&source->work_list, &list);
+                       }
+               }
+       }
+}
+
+/*
+ * dapm_path_invalidate() - Invalidates the cached number of inputs and outputs
+ *  for the widgets connected to a path
+ * @p: The path to invalidate
+ *
+ * Resets the cached number of inputs for the sink of the path and the cached
+ * number of outputs for the source of the path.
+ *
+ * This function must be called when a path is added, removed or the connected
+ * state changes.
+ */
+static void dapm_path_invalidate(struct snd_soc_dapm_path *p)
+{
+       /*
+        * Weak paths or supply paths do not influence the number of input or
+        * output paths of their neighbors.
+        */
+       if (p->weak || p->is_supply)
+               return;
+
+       /*
+        * The number of connected endpoints is the sum of the number of
+        * connected endpoints of all neighbors. If a node with 0 connected
+        * endpoints is either connected or disconnected that sum won't change,
+        * so there is no need to re-check the path.
+        */
+       if (p->source->inputs != 0)
+               dapm_widget_invalidate_input_paths(p->sink);
+       if (p->sink->outputs != 0)
+               dapm_widget_invalidate_output_paths(p->source);
+}
+
+void dapm_mark_endpoints_dirty(struct snd_soc_card *card)
 {
-       struct snd_soc_card *card = dapm->card;
        struct snd_soc_dapm_widget *w;
 
        mutex_lock(&card->dapm_mutex);
 
        list_for_each_entry(w, &card->widgets, list) {
-               switch (w->id) {
-               case snd_soc_dapm_input:
-               case snd_soc_dapm_output:
-                       dapm_mark_dirty(w, "Rechecking inputs and outputs");
-                       break;
-               default:
-                       break;
+               if (w->is_sink || w->is_source) {
+                       dapm_mark_dirty(w, "Rechecking endpoints");
+                       if (w->is_sink)
+                               dapm_widget_invalidate_output_paths(w);
+                       if (w->is_source)
+                               dapm_widget_invalidate_input_paths(w);
                }
        }
 
        mutex_unlock(&card->dapm_mutex);
 }
-EXPORT_SYMBOL_GPL(dapm_mark_io_dirty);
+EXPORT_SYMBOL_GPL(dapm_mark_endpoints_dirty);
 
 /* create a new dapm widget */
 static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
@@ -386,8 +494,6 @@ static void dapm_reset(struct snd_soc_card *card)
        list_for_each_entry(w, &card->widgets, list) {
                w->new_power = w->power;
                w->power_checked = false;
-               w->inputs = -1;
-               w->outputs = -1;
        }
 }
 
@@ -469,10 +575,9 @@ out:
 
 /* connect mux widget to its interconnecting audio paths */
 static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
-       struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
-       struct snd_soc_dapm_path *path, const char *control_name,
-       const struct snd_kcontrol_new *kcontrol)
+       struct snd_soc_dapm_path *path, const char *control_name)
 {
+       const struct snd_kcontrol_new *kcontrol = &path->sink->kcontrol_news[0];
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        unsigned int val, item;
        int i;
@@ -493,10 +598,7 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
 
        for (i = 0; i < e->items; i++) {
                if (!(strcmp(control_name, e->texts[i]))) {
-                       list_add(&path->list, &dapm->card->paths);
-                       list_add(&path->list_sink, &dest->sources);
-                       list_add(&path->list_source, &src->sinks);
-                       path->name = (char*)e->texts[i];
+                       path->name = e->texts[i];
                        if (i == item)
                                path->connect = 1;
                        else
@@ -509,11 +611,10 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
 }
 
 /* set up initial codec paths */
-static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w,
-       struct snd_soc_dapm_path *p, int i)
+static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i)
 {
        struct soc_mixer_control *mc = (struct soc_mixer_control *)
-               w->kcontrol_news[i].private_value;
+               p->sink->kcontrol_news[i].private_value;
        unsigned int reg = mc->reg;
        unsigned int shift = mc->shift;
        unsigned int max = mc->max;
@@ -522,7 +623,7 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w,
        unsigned int val;
 
        if (reg != SND_SOC_NOPM) {
-               soc_dapm_read(w->dapm, reg, &val);
+               soc_dapm_read(p->sink->dapm, reg, &val);
                val = (val >> shift) & mask;
                if (invert)
                        val = max - val;
@@ -534,19 +635,15 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w,
 
 /* connect mixer widget to its interconnecting audio paths */
 static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
-       struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
        struct snd_soc_dapm_path *path, const char *control_name)
 {
        int i;
 
        /* search for mixer kcontrol */
-       for (i = 0; i < dest->num_kcontrols; i++) {
-               if (!strcmp(control_name, dest->kcontrol_news[i].name)) {
-                       list_add(&path->list, &dapm->card->paths);
-                       list_add(&path->list_sink, &dest->sources);
-                       list_add(&path->list_source, &src->sinks);
-                       path->name = dest->kcontrol_news[i].name;
-                       dapm_set_mixer_path_status(dest, path, i);
+       for (i = 0; i < path->sink->num_kcontrols; i++) {
+               if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) {
+                       path->name = path->sink->kcontrol_news[i].name;
+                       dapm_set_mixer_path_status(path, i);
                        return 0;
                }
        }
@@ -738,8 +835,10 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
        if (ret < 0)
                return ret;
 
-       list_for_each_entry(path, &w->sources, list_sink)
-               dapm_kcontrol_add_path(w->kcontrols[0], path);
+       list_for_each_entry(path, &w->sources, list_sink) {
+               if (path->name)
+                       dapm_kcontrol_add_path(w->kcontrols[0], path);
+       }
 
        return 0;
 }
@@ -754,34 +853,6 @@ static int dapm_new_pga(struct snd_soc_dapm_widget *w)
        return 0;
 }
 
-/* reset 'walked' bit for each dapm path */
-static void dapm_clear_walk_output(struct snd_soc_dapm_context *dapm,
-                                  struct list_head *sink)
-{
-       struct snd_soc_dapm_path *p;
-
-       list_for_each_entry(p, sink, list_source) {
-               if (p->walked) {
-                       p->walked = 0;
-                       dapm_clear_walk_output(dapm, &p->sink->sinks);
-               }
-       }
-}
-
-static void dapm_clear_walk_input(struct snd_soc_dapm_context *dapm,
-                                 struct list_head *source)
-{
-       struct snd_soc_dapm_path *p;
-
-       list_for_each_entry(p, source, list_sink) {
-               if (p->walked) {
-                       p->walked = 0;
-                       dapm_clear_walk_input(dapm, &p->source->sources);
-               }
-       }
-}
-
-
 /* We implement power down on suspend by checking the power state of
  * the ALSA card - when we are suspending the ALSA state for the card
  * is set to D3.
@@ -856,61 +927,23 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
 
        DAPM_UPDATE_STAT(widget, path_checks);
 
-       switch (widget->id) {
-       case snd_soc_dapm_supply:
-       case snd_soc_dapm_regulator_supply:
-       case snd_soc_dapm_clock_supply:
-       case snd_soc_dapm_kcontrol:
-               return 0;
-       default:
-               break;
-       }
-
-       switch (widget->id) {
-       case snd_soc_dapm_adc:
-       case snd_soc_dapm_aif_out:
-       case snd_soc_dapm_dai_out:
-               if (widget->active) {
-                       widget->outputs = snd_soc_dapm_suspend_check(widget);
-                       return widget->outputs;
-               }
-       default:
-               break;
-       }
-
-       if (widget->connected) {
-               /* connected pin ? */
-               if (widget->id == snd_soc_dapm_output && !widget->ext) {
-                       widget->outputs = snd_soc_dapm_suspend_check(widget);
-                       return widget->outputs;
-               }
-
-               /* connected jack or spk ? */
-               if (widget->id == snd_soc_dapm_hp ||
-                   widget->id == snd_soc_dapm_spk ||
-                   (widget->id == snd_soc_dapm_line &&
-                    !list_empty(&widget->sources))) {
-                       widget->outputs = snd_soc_dapm_suspend_check(widget);
-                       return widget->outputs;
-               }
+       if (widget->is_sink && widget->connected) {
+               widget->outputs = snd_soc_dapm_suspend_check(widget);
+               return widget->outputs;
        }
 
        list_for_each_entry(path, &widget->sinks, list_source) {
                DAPM_UPDATE_STAT(widget, neighbour_checks);
 
-               if (path->weak)
+               if (path->weak || path->is_supply)
                        continue;
 
                if (path->walking)
                        return 1;
 
-               if (path->walked)
-                       continue;
-
                trace_snd_soc_dapm_output_path(widget, path);
 
-               if (path->sink && path->connect) {
-                       path->walked = 1;
+               if (path->connect) {
                        path->walking = 1;
 
                        /* do we need to add this widget to the list ? */
@@ -952,73 +985,23 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
 
        DAPM_UPDATE_STAT(widget, path_checks);
 
-       switch (widget->id) {
-       case snd_soc_dapm_supply:
-       case snd_soc_dapm_regulator_supply:
-       case snd_soc_dapm_clock_supply:
-       case snd_soc_dapm_kcontrol:
-               return 0;
-       default:
-               break;
-       }
-
-       /* active stream ? */
-       switch (widget->id) {
-       case snd_soc_dapm_dac:
-       case snd_soc_dapm_aif_in:
-       case snd_soc_dapm_dai_in:
-               if (widget->active) {
-                       widget->inputs = snd_soc_dapm_suspend_check(widget);
-                       return widget->inputs;
-               }
-       default:
-               break;
-       }
-
-       if (widget->connected) {
-               /* connected pin ? */
-               if (widget->id == snd_soc_dapm_input && !widget->ext) {
-                       widget->inputs = snd_soc_dapm_suspend_check(widget);
-                       return widget->inputs;
-               }
-
-               /* connected VMID/Bias for lower pops */
-               if (widget->id == snd_soc_dapm_vmid) {
-                       widget->inputs = snd_soc_dapm_suspend_check(widget);
-                       return widget->inputs;
-               }
-
-               /* connected jack ? */
-               if (widget->id == snd_soc_dapm_mic ||
-                   (widget->id == snd_soc_dapm_line &&
-                    !list_empty(&widget->sinks))) {
-                       widget->inputs = snd_soc_dapm_suspend_check(widget);
-                       return widget->inputs;
-               }
-
-               /* signal generator */
-               if (widget->id == snd_soc_dapm_siggen) {
-                       widget->inputs = snd_soc_dapm_suspend_check(widget);
-                       return widget->inputs;
-               }
+       if (widget->is_source && widget->connected) {
+               widget->inputs = snd_soc_dapm_suspend_check(widget);
+               return widget->inputs;
        }
 
        list_for_each_entry(path, &widget->sources, list_sink) {
                DAPM_UPDATE_STAT(widget, neighbour_checks);
 
-               if (path->weak)
+               if (path->weak || path->is_supply)
                        continue;
 
                if (path->walking)
                        return 1;
 
-               if (path->walked)
-                       continue;
-
                trace_snd_soc_dapm_input_path(widget, path);
 
-               if (path->source && path->connect) {
-                       path->walked = 1;
+               if (path->connect) {
                        path->walking = 1;
 
                        /* do we need to add this widget to the list ? */
@@ -1060,21 +1043,25 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
 int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
        struct snd_soc_dapm_widget_list **list)
 {
-       struct snd_soc_card *card = dai->card;
+       struct snd_soc_card *card = dai->component->card;
+       struct snd_soc_dapm_widget *w;
        int paths;
 
        mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
-       dapm_reset(card);
 
-       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+       /*
+        * For is_connected_{output,input}_ep fully discover the graph we need
+        * to reset the cached number of inputs and outputs.
+        */
+       list_for_each_entry(w, &card->widgets, list) {
+               w->inputs = -1;
+               w->outputs = -1;
+       }
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
                paths = is_connected_output_ep(dai->playback_widget, list);
-               dapm_clear_walk_output(&card->dapm,
-                                      &dai->playback_widget->sinks);
-       } else {
+       else
                paths = is_connected_input_ep(dai->capture_widget, list);
-               dapm_clear_walk_input(&card->dapm,
-                                     &dai->capture_widget->sources);
-       }
 
        trace_snd_soc_dapm_connected(paths, stream);
        mutex_unlock(&card->dapm_mutex);
@@ -1163,44 +1150,10 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
        DAPM_UPDATE_STAT(w, power_checks);
 
        in = is_connected_input_ep(w, NULL);
-       dapm_clear_walk_input(w->dapm, &w->sources);
        out = is_connected_output_ep(w, NULL);
-       dapm_clear_walk_output(w->dapm, &w->sinks);
        return out != 0 && in != 0;
 }
 
-/* Check to see if an ADC has power */
-static int dapm_adc_check_power(struct snd_soc_dapm_widget *w)
-{
-       int in;
-
-       DAPM_UPDATE_STAT(w, power_checks);
-
-       if (w->active) {
-               in = is_connected_input_ep(w, NULL);
-               dapm_clear_walk_input(w->dapm, &w->sources);
-               return in != 0;
-       } else {
-               return dapm_generic_check_power(w);
-       }
-}
-
-/* Check to see if a DAC has power */
-static int dapm_dac_check_power(struct snd_soc_dapm_widget *w)
-{
-       int out;
-
-       DAPM_UPDATE_STAT(w, power_checks);
-
-       if (w->active) {
-               out = is_connected_output_ep(w, NULL);
-               dapm_clear_walk_output(w->dapm, &w->sinks);
-               return out != 0;
-       } else {
-               return dapm_generic_check_power(w);
-       }
-}
-
 /* Check to see if a power supply is needed */
 static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
 {
@@ -1219,9 +1172,6 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
                    !path->connected(path->source, path->sink))
                        continue;
 
-               if (!path->sink)
-                       continue;
-
                if (dapm_widget_power_check(path->sink))
                        return 1;
        }
@@ -1636,27 +1586,14 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power,
        /* If we changed our power state perhaps our neigbours changed
         * also.
         */
-       list_for_each_entry(path, &w->sources, list_sink) {
-               if (path->source) {
-                       dapm_widget_set_peer_power(path->source, power,
+       list_for_each_entry(path, &w->sources, list_sink)
+               dapm_widget_set_peer_power(path->source, power, path->connect);
+
+       /* Supplies can't affect their outputs, only their inputs */
+       if (!w->is_supply) {
+               list_for_each_entry(path, &w->sinks, list_source)
+                       dapm_widget_set_peer_power(path->sink, power,
                                                   path->connect);
-               }
-       }
-       switch (w->id) {
-       case snd_soc_dapm_supply:
-       case snd_soc_dapm_regulator_supply:
-       case snd_soc_dapm_clock_supply:
-       case snd_soc_dapm_kcontrol:
-               /* Supplies can't affect their outputs, only their inputs */
-               break;
-       default:
-               list_for_each_entry(path, &w->sinks, list_source) {
-                       if (path->sink) {
-                               dapm_widget_set_peer_power(path->sink, power,
-                                                          path->connect);
-                       }
-               }
-               break;
        }
 
        if (power)
@@ -1863,10 +1800,14 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
        if (!buf)
                return -ENOMEM;
 
-       in = is_connected_input_ep(w, NULL);
-       dapm_clear_walk_input(w->dapm, &w->sources);
-       out = is_connected_output_ep(w, NULL);
-       dapm_clear_walk_output(w->dapm, &w->sinks);
+       /* Supply widgets are not handled by is_connected_{input,output}_ep() */
+       if (w->is_supply) {
+               in = 0;
+               out = 0;
+       } else {
+               in = is_connected_input_ep(w, NULL);
+               out = is_connected_output_ep(w, NULL);
+       }
 
        ret = snprintf(buf, PAGE_SIZE, "%s: %s%s  in %d out %d",
                       w->name, w->power ? "On" : "Off",
@@ -2011,32 +1952,45 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
 
 #endif
 
+/*
+ * soc_dapm_connect_path() - Connects or disconnects a path
+ * @path: The path to update
+ * @connect: The new connect state of the path. True if the path is connected,
+ *  false if it is disconneted.
+ * @reason: The reason why the path changed (for debugging only)
+ */
+static void soc_dapm_connect_path(struct snd_soc_dapm_path *path,
+       bool connect, const char *reason)
+{
+       if (path->connect == connect)
+               return;
+
+       path->connect = connect;
+       dapm_mark_dirty(path->source, reason);
+       dapm_mark_dirty(path->sink, reason);
+       dapm_path_invalidate(path);
+}
+
 /* test and update the power status of a mux widget */
 static int soc_dapm_mux_update_power(struct snd_soc_card *card,
                                 struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e)
 {
        struct snd_soc_dapm_path *path;
        int found = 0;
+       bool connect;
 
        lockdep_assert_held(&card->dapm_mutex);
 
        /* find dapm widget path assoc with kcontrol */
        dapm_kcontrol_for_each_path(path, kcontrol) {
-               if (!path->name || !e->texts[mux])
-                       continue;
-
                found = 1;
                /* we now need to match the string in the enum to the path */
-               if (!(strcmp(path->name, e->texts[mux]))) {
-                       path->connect = 1; /* new connection */
-                       dapm_mark_dirty(path->source, "mux connection");
-               } else {
-                       if (path->connect)
-                               dapm_mark_dirty(path->source,
-                                               "mux disconnection");
-                       path->connect = 0; /* old connection must be powered down */
-               }
-               dapm_mark_dirty(path->sink, "mux change");
+               if (!(strcmp(path->name, e->texts[mux])))
+                       connect = true;
+               else
+                       connect = false;
+
+               soc_dapm_connect_path(path, connect, "mux update");
        }
 
        if (found)
@@ -2075,9 +2029,7 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
        /* find dapm widget path assoc with kcontrol */
        dapm_kcontrol_for_each_path(path, kcontrol) {
                found = 1;
-               path->connect = connect;
-               dapm_mark_dirty(path->source, "mixer connection");
-               dapm_mark_dirty(path->sink, "mixer update");
+               soc_dapm_connect_path(path, connect, "mixer update");
        }
 
        if (found)
@@ -2255,8 +2207,11 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
                return -EINVAL;
        }
 
-       if (w->connected != status)
+       if (w->connected != status) {
                dapm_mark_dirty(w, "pin configuration");
+               dapm_widget_invalidate_input_paths(w);
+               dapm_widget_invalidate_output_paths(w);
+       }
 
        w->connected = status;
        if (status == 0)
@@ -2309,6 +2264,53 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm)
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
 
+/*
+ * dapm_update_widget_flags() - Re-compute widget sink and source flags
+ * @w: The widget for which to update the flags
+ *
+ * Some widgets have a dynamic category which depends on which neighbors they
+ * are connected to. This function update the category for these widgets.
+ *
+ * This function must be called whenever a path is added or removed to a widget.
+ */
+static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w)
+{
+       struct snd_soc_dapm_path *p;
+
+       switch (w->id) {
+       case snd_soc_dapm_input:
+               w->is_source = 1;
+               list_for_each_entry(p, &w->sources, list_sink) {
+                       if (p->source->id == snd_soc_dapm_micbias ||
+                               p->source->id == snd_soc_dapm_mic ||
+                               p->source->id == snd_soc_dapm_line ||
+                               p->source->id == snd_soc_dapm_output) {
+                                       w->is_source = 0;
+                                       break;
+                       }
+               }
+               break;
+       case snd_soc_dapm_output:
+               w->is_sink = 1;
+               list_for_each_entry(p, &w->sinks, list_source) {
+                       if (p->sink->id == snd_soc_dapm_spk ||
+                               p->sink->id == snd_soc_dapm_hp ||
+                               p->sink->id == snd_soc_dapm_line ||
+                               p->sink->id == snd_soc_dapm_input) {
+                                       w->is_sink = 0;
+                                       break;
+                       }
+               }
+               break;
+       case snd_soc_dapm_line:
+               w->is_sink = !list_empty(&w->sources);
+               w->is_source = !list_empty(&w->sinks);
+               break;
+       default:
+               break;
+       }
+}
+
 static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
        struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,
        const char *control,
@@ -2318,6 +2320,27 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
        struct snd_soc_dapm_path *path;
        int ret;
 
+       if (wsink->is_supply && !wsource->is_supply) {
+               dev_err(dapm->dev,
+                       "Connecting non-supply widget to supply widget is not supported (%s -> %s)\n",
+                       wsource->name, wsink->name);
+               return -EINVAL;
+       }
+
+       if (connected && !wsource->is_supply) {
+               dev_err(dapm->dev,
+                       "connected() callback only supported for supply widgets (%s -> %s)\n",
+                       wsource->name, wsink->name);
+               return -EINVAL;
+       }
+
+       if (wsource->is_supply && control) {
+               dev_err(dapm->dev,
+                       "Conditional paths are not supported for supply widgets (%s -> [%s] -> %s)\n",
+                       wsource->name, control, wsink->name);
+               return -EINVAL;
+       }
+
        path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
        if (!path)
                return -ENOMEM;
@@ -2330,85 +2353,49 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
        INIT_LIST_HEAD(&path->list_source);
        INIT_LIST_HEAD(&path->list_sink);
 
-       /* check for external widgets */
-       if (wsink->id == snd_soc_dapm_input) {
-               if (wsource->id == snd_soc_dapm_micbias ||
-                       wsource->id == snd_soc_dapm_mic ||
-                       wsource->id == snd_soc_dapm_line ||
-                       wsource->id == snd_soc_dapm_output)
-                       wsink->ext = 1;
-       }
-       if (wsource->id == snd_soc_dapm_output) {
-               if (wsink->id == snd_soc_dapm_spk ||
-                       wsink->id == snd_soc_dapm_hp ||
-                       wsink->id == snd_soc_dapm_line ||
-                       wsink->id == snd_soc_dapm_input)
-                       wsource->ext = 1;
-       }
-
-       dapm_mark_dirty(wsource, "Route added");
-       dapm_mark_dirty(wsink, "Route added");
+       if (wsource->is_supply || wsink->is_supply)
+               path->is_supply = 1;
 
        /* connect static paths */
        if (control == NULL) {
-               list_add(&path->list, &dapm->card->paths);
-               list_add(&path->list_sink, &wsink->sources);
-               list_add(&path->list_source, &wsource->sinks);
                path->connect = 1;
-               return 0;
-       }
-
-       /* connect dynamic paths */
-       switch (wsink->id) {
-       case snd_soc_dapm_adc:
-       case snd_soc_dapm_dac:
-       case snd_soc_dapm_pga:
-       case snd_soc_dapm_out_drv:
-       case snd_soc_dapm_input:
-       case snd_soc_dapm_output:
-       case snd_soc_dapm_siggen:
-       case snd_soc_dapm_micbias:
-       case snd_soc_dapm_vmid:
-       case snd_soc_dapm_pre:
-       case snd_soc_dapm_post:
-       case snd_soc_dapm_supply:
-       case snd_soc_dapm_regulator_supply:
-       case snd_soc_dapm_clock_supply:
-       case snd_soc_dapm_aif_in:
-       case snd_soc_dapm_aif_out:
-       case snd_soc_dapm_dai_in:
-       case snd_soc_dapm_dai_out:
-       case snd_soc_dapm_dai_link:
-       case snd_soc_dapm_kcontrol:
-               list_add(&path->list, &dapm->card->paths);
-               list_add(&path->list_sink, &wsink->sources);
-               list_add(&path->list_source, &wsource->sinks);
-               path->connect = 1;
-               return 0;
-       case snd_soc_dapm_mux:
-               ret = dapm_connect_mux(dapm, wsource, wsink, path, control,
-                       &wsink->kcontrol_news[0]);
-               if (ret != 0)
-                       goto err;
-               break;
-       case snd_soc_dapm_switch:
-       case snd_soc_dapm_mixer:
-       case snd_soc_dapm_mixer_named_ctl:
-               ret = dapm_connect_mixer(dapm, wsource, wsink, path, control);
-               if (ret != 0)
+       } else {
+               /* connect dynamic paths */
+               switch (wsink->id) {
+               case snd_soc_dapm_mux:
+                       ret = dapm_connect_mux(dapm, path, control);
+                       if (ret != 0)
+                               goto err;
+                       break;
+               case snd_soc_dapm_switch:
+               case snd_soc_dapm_mixer:
+               case snd_soc_dapm_mixer_named_ctl:
+                       ret = dapm_connect_mixer(dapm, path, control);
+                       if (ret != 0)
+                               goto err;
+                       break;
+               default:
+                       dev_err(dapm->dev,
+                               "Control not supported for path %s -> [%s] -> %s\n",
+                               wsource->name, control, wsink->name);
+                       ret = -EINVAL;
                        goto err;
-               break;
-       case snd_soc_dapm_hp:
-       case snd_soc_dapm_mic:
-       case snd_soc_dapm_line:
-       case snd_soc_dapm_spk:
-               list_add(&path->list, &dapm->card->paths);
-               list_add(&path->list_sink, &wsink->sources);
-               list_add(&path->list_source, &wsource->sinks);
-               path->connect = 0;
-               return 0;
+               }
        }
 
+       list_add(&path->list, &dapm->card->paths);
+       list_add(&path->list_sink, &wsink->sources);
+       list_add(&path->list_source, &wsource->sinks);
+
+       dapm_update_widget_flags(wsource);
+       dapm_update_widget_flags(wsink);
+
+       dapm_mark_dirty(wsource, "Route added");
+       dapm_mark_dirty(wsink, "Route added");
+
+       if (dapm->card->instantiated && path->connect)
+               dapm_path_invalidate(path);
+
        return 0;
 err:
        kfree(path);
@@ -2489,6 +2476,7 @@ err:
 static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
                                  const struct snd_soc_dapm_route *route)
 {
+       struct snd_soc_dapm_widget *wsource, *wsink;
        struct snd_soc_dapm_path *path, *p;
        const char *sink;
        const char *source;
@@ -2526,10 +2514,19 @@ static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
        }
 
        if (path) {
-               dapm_mark_dirty(path->source, "Route removed");
-               dapm_mark_dirty(path->sink, "Route removed");
+               wsource = path->source;
+               wsink = path->sink;
+
+               dapm_mark_dirty(wsource, "Route removed");
+               dapm_mark_dirty(wsink, "Route removed");
+               if (path->connect)
+                       dapm_path_invalidate(path);
 
                dapm_free_path(path);
+
+               /* Update any path related flags */
+               dapm_update_widget_flags(wsource);
+               dapm_update_widget_flags(wsink);
        } else {
                dev_warn(dapm->dev, "ASoC: Route %s->%s does not exist\n",
                         source, sink);
@@ -3087,40 +3084,44 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
        }
 
        switch (w->id) {
-       case snd_soc_dapm_switch:
-       case snd_soc_dapm_mixer:
-       case snd_soc_dapm_mixer_named_ctl:
+       case snd_soc_dapm_mic:
+       case snd_soc_dapm_input:
+               w->is_source = 1;
                w->power_check = dapm_generic_check_power;
                break;
-       case snd_soc_dapm_mux:
+       case snd_soc_dapm_spk:
+       case snd_soc_dapm_hp:
+       case snd_soc_dapm_output:
+               w->is_sink = 1;
                w->power_check = dapm_generic_check_power;
                break;
-       case snd_soc_dapm_dai_out:
-               w->power_check = dapm_adc_check_power;
-               break;
-       case snd_soc_dapm_dai_in:
-               w->power_check = dapm_dac_check_power;
+       case snd_soc_dapm_vmid:
+       case snd_soc_dapm_siggen:
+               w->is_source = 1;
+               w->power_check = dapm_always_on_check_power;
                break;
+       case snd_soc_dapm_mux:
+       case snd_soc_dapm_switch:
+       case snd_soc_dapm_mixer:
+       case snd_soc_dapm_mixer_named_ctl:
        case snd_soc_dapm_adc:
        case snd_soc_dapm_aif_out:
        case snd_soc_dapm_dac:
        case snd_soc_dapm_aif_in:
        case snd_soc_dapm_pga:
        case snd_soc_dapm_out_drv:
-       case snd_soc_dapm_input:
-       case snd_soc_dapm_output:
        case snd_soc_dapm_micbias:
-       case snd_soc_dapm_spk:
-       case snd_soc_dapm_hp:
-       case snd_soc_dapm_mic:
        case snd_soc_dapm_line:
        case snd_soc_dapm_dai_link:
+       case snd_soc_dapm_dai_out:
+       case snd_soc_dapm_dai_in:
                w->power_check = dapm_generic_check_power;
                break;
        case snd_soc_dapm_supply:
        case snd_soc_dapm_regulator_supply:
        case snd_soc_dapm_clock_supply:
        case snd_soc_dapm_kcontrol:
+               w->is_supply = 1;
                w->power_check = dapm_supply_check_power;
                break;
        default:
@@ -3137,6 +3138,9 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
        INIT_LIST_HEAD(&w->dirty);
        list_add(&w->list, &dapm->card->widgets);
 
+       w->inputs = -1;
+       w->outputs = -1;
+
        /* machine layer set ups unconnected pins and insertions */
        w->connected = 1;
        return w;
@@ -3484,6 +3488,14 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
                case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
                        break;
                }
+
+               if (w->id == snd_soc_dapm_dai_in) {
+                       w->is_source = w->active;
+                       dapm_widget_invalidate_input_paths(w);
+               } else {
+                       w->is_sink = w->active;
+                       dapm_widget_invalidate_output_paths(w);
+               }
        }
 }
 
@@ -3610,7 +3622,15 @@ int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm,
        }
 
        dev_dbg(w->dapm->dev, "ASoC: force enable pin %s\n", pin);
-       w->connected = 1;
+       if (!w->connected) {
+               /*
+                * w->force does not affect the number of input or output paths,
+                * so we only have to recheck if w->connected is changed
+                */
+               dapm_widget_invalidate_input_paths(w);
+               dapm_widget_invalidate_output_paths(w);
+               w->connected = 1;
+       }
        w->force = 1;
        dapm_mark_dirty(w, "force enable");
 
@@ -3788,35 +3808,54 @@ int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm,
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend);
 
+/**
+ * dapm_is_external_path() - Checks if a path is a external path
+ * @card: The card the path belongs to
+ * @path: The path to check
+ *
+ * Returns true if the path is either between two different DAPM contexts or
+ * between two external pins of the same DAPM context. Otherwise returns
+ * false.
+ */
+static bool dapm_is_external_path(struct snd_soc_card *card,
+       struct snd_soc_dapm_path *path)
+{
+       dev_dbg(card->dev,
+               "... Path %s(id:%d dapm:%p) - %s(id:%d dapm:%p)\n",
+               path->source->name, path->source->id, path->source->dapm,
+               path->sink->name, path->sink->id, path->sink->dapm);
+
+       /* Connection between two different DAPM contexts */
+       if (path->source->dapm != path->sink->dapm)
+               return true;
+
+       /* Loopback connection from external pin to external pin */
+       if (path->sink->id == snd_soc_dapm_input) {
+               switch (path->source->id) {
+               case snd_soc_dapm_output:
+               case snd_soc_dapm_micbias:
+                       return true;
+               default:
+                       break;
+               }
+       }
+
+       return false;
+}
+
 static bool snd_soc_dapm_widget_in_card_paths(struct snd_soc_card *card,
                                              struct snd_soc_dapm_widget *w)
 {
        struct snd_soc_dapm_path *p;
 
-       list_for_each_entry(p, &card->paths, list) {
-               if ((p->source == w) || (p->sink == w)) {
-                       dev_dbg(card->dev,
-                           "... Path %s(id:%d dapm:%p) - %s(id:%d dapm:%p)\n",
-                           p->source->name, p->source->id, p->source->dapm,
-                           p->sink->name, p->sink->id, p->sink->dapm);
+       list_for_each_entry(p, &w->sources, list_sink) {
+               if (dapm_is_external_path(card, p))
+                       return true;
+       }
 
-                       /* Connected to something other than the codec */
-                       if (p->source->dapm != p->sink->dapm)
-                               return true;
-                       /*
-                        * Loopback connection from codec external pin to
-                        * codec external pin
-                        */
-                       if (p->sink->id == snd_soc_dapm_input) {
-                               switch (p->source->id) {
-                               case snd_soc_dapm_output:
-                               case snd_soc_dapm_micbias:
-                                       return true;
-                               default:
-                                       break;
-                               }
-                       }
-               }
+       list_for_each_entry(p, &w->sinks, list_source) {
+               if (dapm_is_external_path(card, p))
+                       return true;
        }
 
        return false;
index ab47fea..4380dcc 100644 (file)
@@ -116,7 +116,7 @@ EXPORT_SYMBOL_GPL(snd_soc_jack_report);
  *
  * @jack:  ASoC jack
  * @count: Number of zones
- * @zone:  Array of zones
+ * @zones:  Array of zones
  *
  * After this function has been called the zones specified in the
  * array will be associated with the jack.
@@ -309,7 +309,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
                        /* GPIO descriptor */
                        gpios[i].desc = gpiod_get_index(gpios[i].gpiod_dev,
                                                        gpios[i].name,
-                                                       gpios[i].idx);
+                                                       gpios[i].idx, GPIOD_IN);
                        if (IS_ERR(gpios[i].desc)) {
                                ret = PTR_ERR(gpios[i].desc);
                                dev_err(gpios[i].gpiod_dev,
@@ -327,17 +327,14 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
                                goto undo;
                        }
 
-                       ret = gpio_request(gpios[i].gpio, gpios[i].name);
+                       ret = gpio_request_one(gpios[i].gpio, GPIOF_IN,
+                                              gpios[i].name);
                        if (ret)
                                goto undo;
 
                        gpios[i].desc = gpio_to_desc(gpios[i].gpio);
                }
 
-               ret = gpiod_direction_input(gpios[i].desc);
-               if (ret)
-                       goto err;
-
                INIT_DELAYED_WORK(&gpios[i].work, gpio_work);
                gpios[i].jack = jack;
 
diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c
new file mode 100644 (file)
index 0000000..100d92b
--- /dev/null
@@ -0,0 +1,952 @@
+/*
+ * soc-ops.c  --  Generic ASoC operations
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ * Copyright (C) 2010 Slimlogic Ltd.
+ * Copyright (C) 2010 Texas Instruments Inc.
+ *
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ *         with code, comments and ideas from :-
+ *         Richard Purdie <richard@openedhand.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dpcm.h>
+#include <sound/initval.h>
+
+/**
+ * snd_soc_info_enum_double - enumerated double mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a double enumerated
+ * mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_info *uinfo)
+{
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+
+       return snd_ctl_enum_info(uinfo, e->shift_l == e->shift_r ? 1 : 2,
+                                e->items, e->texts);
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_enum_double);
+
+/**
+ * snd_soc_get_enum_double - enumerated double mixer get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a double enumerated mixer.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       unsigned int val, item;
+       unsigned int reg_val;
+       int ret;
+
+       ret = snd_soc_component_read(component, e->reg, &reg_val);
+       if (ret)
+               return ret;
+       val = (reg_val >> e->shift_l) & e->mask;
+       item = snd_soc_enum_val_to_item(e, val);
+       ucontrol->value.enumerated.item[0] = item;
+       if (e->shift_l != e->shift_r) {
+               val = (reg_val >> e->shift_l) & e->mask;
+               item = snd_soc_enum_val_to_item(e, val);
+               ucontrol->value.enumerated.item[1] = item;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_enum_double);
+
+/**
+ * snd_soc_put_enum_double - enumerated double mixer put callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value of a double enumerated mixer.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       unsigned int *item = ucontrol->value.enumerated.item;
+       unsigned int val;
+       unsigned int mask;
+
+       if (item[0] >= e->items)
+               return -EINVAL;
+       val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+       mask = e->mask << e->shift_l;
+       if (e->shift_l != e->shift_r) {
+               if (item[1] >= e->items)
+                       return -EINVAL;
+               val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r;
+               mask |= e->mask << e->shift_r;
+       }
+
+       return snd_soc_component_update_bits(component, e->reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
+
+/**
+ * snd_soc_read_signed - Read a codec register and interprete as signed value
+ * @component: component
+ * @reg: Register to read
+ * @mask: Mask to use after shifting the register value
+ * @shift: Right shift of register value
+ * @sign_bit: Bit that describes if a number is negative or not.
+ * @signed_val: Pointer to where the read value should be stored
+ *
+ * This functions reads a codec register. The register value is shifted right
+ * by 'shift' bits and masked with the given 'mask'. Afterwards it translates
+ * the given registervalue into a signed integer if sign_bit is non-zero.
+ *
+ * Returns 0 on sucess, otherwise an error value
+ */
+static int snd_soc_read_signed(struct snd_soc_component *component,
+       unsigned int reg, unsigned int mask, unsigned int shift,
+       unsigned int sign_bit, int *signed_val)
+{
+       int ret;
+       unsigned int val;
+
+       ret = snd_soc_component_read(component, reg, &val);
+       if (ret < 0)
+               return ret;
+
+       val = (val >> shift) & mask;
+
+       if (!sign_bit) {
+               *signed_val = val;
+               return 0;
+       }
+
+       /* non-negative number */
+       if (!(val & BIT(sign_bit))) {
+               *signed_val = val;
+               return 0;
+       }
+
+       ret = val;
+
+       /*
+        * The register most probably does not contain a full-sized int.
+        * Instead we have an arbitrary number of bits in a signed
+        * representation which has to be translated into a full-sized int.
+        * This is done by filling up all bits above the sign-bit.
+        */
+       ret |= ~((int)(BIT(sign_bit) - 1));
+
+       *signed_val = ret;
+
+       return 0;
+}
+
+/**
+ * snd_soc_info_volsw - single mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a single mixer control, or a double
+ * mixer control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_info *uinfo)
+{
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       int platform_max;
+
+       if (!mc->platform_max)
+               mc->platform_max = mc->max;
+       platform_max = mc->platform_max;
+
+       if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume"))
+               uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       else
+               uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+
+       uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = platform_max - mc->min;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
+
+/**
+ * snd_soc_get_volsw - single mixer get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a single mixer control, or a double mixer
+ * control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int reg2 = mc->rreg;
+       unsigned int shift = mc->shift;
+       unsigned int rshift = mc->rshift;
+       int max = mc->max;
+       int min = mc->min;
+       int sign_bit = mc->sign_bit;
+       unsigned int mask = (1 << fls(max)) - 1;
+       unsigned int invert = mc->invert;
+       int val;
+       int ret;
+
+       if (sign_bit)
+               mask = BIT(sign_bit + 1) - 1;
+
+       ret = snd_soc_read_signed(component, reg, mask, shift, sign_bit, &val);
+       if (ret)
+               return ret;
+
+       ucontrol->value.integer.value[0] = val - min;
+       if (invert)
+               ucontrol->value.integer.value[0] =
+                       max - ucontrol->value.integer.value[0];
+
+       if (snd_soc_volsw_is_stereo(mc)) {
+               if (reg == reg2)
+                       ret = snd_soc_read_signed(component, reg, mask, rshift,
+                               sign_bit, &val);
+               else
+                       ret = snd_soc_read_signed(component, reg2, mask, shift,
+                               sign_bit, &val);
+               if (ret)
+                       return ret;
+
+               ucontrol->value.integer.value[1] = val - min;
+               if (invert)
+                       ucontrol->value.integer.value[1] =
+                               max - ucontrol->value.integer.value[1];
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw);
+
+/**
+ * snd_soc_put_volsw - single mixer put callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value of a single mixer control, or a double mixer
+ * control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int reg2 = mc->rreg;
+       unsigned int shift = mc->shift;
+       unsigned int rshift = mc->rshift;
+       int max = mc->max;
+       int min = mc->min;
+       unsigned int sign_bit = mc->sign_bit;
+       unsigned int mask = (1 << fls(max)) - 1;
+       unsigned int invert = mc->invert;
+       int err;
+       bool type_2r = false;
+       unsigned int val2 = 0;
+       unsigned int val, val_mask;
+
+       if (sign_bit)
+               mask = BIT(sign_bit + 1) - 1;
+
+       val = ((ucontrol->value.integer.value[0] + min) & mask);
+       if (invert)
+               val = max - val;
+       val_mask = mask << shift;
+       val = val << shift;
+       if (snd_soc_volsw_is_stereo(mc)) {
+               val2 = ((ucontrol->value.integer.value[1] + min) & mask);
+               if (invert)
+                       val2 = max - val2;
+               if (reg == reg2) {
+                       val_mask |= mask << rshift;
+                       val |= val2 << rshift;
+               } else {
+                       val2 = val2 << shift;
+                       type_2r = true;
+               }
+       }
+       err = snd_soc_component_update_bits(component, reg, val_mask, val);
+       if (err < 0)
+               return err;
+
+       if (type_2r)
+               err = snd_soc_component_update_bits(component, reg2, val_mask,
+                       val2);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
+
+/**
+ * snd_soc_get_volsw_sx - single mixer get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a single mixer control, or a double mixer
+ * control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
+                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_mixer_control *mc =
+           (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int reg2 = mc->rreg;
+       unsigned int shift = mc->shift;
+       unsigned int rshift = mc->rshift;
+       int max = mc->max;
+       int min = mc->min;
+       int mask = (1 << (fls(min + max) - 1)) - 1;
+       unsigned int val;
+       int ret;
+
+       ret = snd_soc_component_read(component, reg, &val);
+       if (ret < 0)
+               return ret;
+
+       ucontrol->value.integer.value[0] = ((val >> shift) - min) & mask;
+
+       if (snd_soc_volsw_is_stereo(mc)) {
+               ret = snd_soc_component_read(component, reg2, &val);
+               if (ret < 0)
+                       return ret;
+
+               val = ((val >> rshift) - min) & mask;
+               ucontrol->value.integer.value[1] = val;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx);
+
+/**
+ * snd_soc_put_volsw_sx - double mixer set callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to set the value of a double mixer control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_mixer_control *mc =
+           (struct soc_mixer_control *)kcontrol->private_value;
+
+       unsigned int reg = mc->reg;
+       unsigned int reg2 = mc->rreg;
+       unsigned int shift = mc->shift;
+       unsigned int rshift = mc->rshift;
+       int max = mc->max;
+       int min = mc->min;
+       int mask = (1 << (fls(min + max) - 1)) - 1;
+       int err = 0;
+       unsigned int val, val_mask, val2 = 0;
+
+       val_mask = mask << shift;
+       val = (ucontrol->value.integer.value[0] + min) & mask;
+       val = val << shift;
+
+       err = snd_soc_component_update_bits(component, reg, val_mask, val);
+       if (err < 0)
+               return err;
+
+       if (snd_soc_volsw_is_stereo(mc)) {
+               val_mask = mask << rshift;
+               val2 = (ucontrol->value.integer.value[1] + min) & mask;
+               val2 = val2 << rshift;
+
+               err = snd_soc_component_update_bits(component, reg2, val_mask,
+                       val2);
+       }
+       return err;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx);
+
+/**
+ * snd_soc_info_volsw_range - single mixer info callback with range.
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information, within a range, about a single
+ * mixer control.
+ *
+ * returns 0 for success.
+ */
+int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_info *uinfo)
+{
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       int platform_max;
+       int min = mc->min;
+
+       if (!mc->platform_max)
+               mc->platform_max = mc->max;
+       platform_max = mc->platform_max;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = platform_max - min;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_volsw_range);
+
+/**
+ * snd_soc_put_volsw_range - single mixer put value callback with range.
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value, within a range, for a single mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       unsigned int reg = mc->reg;
+       unsigned int rreg = mc->rreg;
+       unsigned int shift = mc->shift;
+       int min = mc->min;
+       int max = mc->max;
+       unsigned int mask = (1 << fls(max)) - 1;
+       unsigned int invert = mc->invert;
+       unsigned int val, val_mask;
+       int ret;
+
+       if (invert)
+               val = (max - ucontrol->value.integer.value[0]) & mask;
+       else
+               val = ((ucontrol->value.integer.value[0] + min) & mask);
+       val_mask = mask << shift;
+       val = val << shift;
+
+       ret = snd_soc_component_update_bits(component, reg, val_mask, val);
+       if (ret < 0)
+               return ret;
+
+       if (snd_soc_volsw_is_stereo(mc)) {
+               if (invert)
+                       val = (max - ucontrol->value.integer.value[1]) & mask;
+               else
+                       val = ((ucontrol->value.integer.value[1] + min) & mask);
+               val_mask = mask << shift;
+               val = val << shift;
+
+               ret = snd_soc_component_update_bits(component, rreg, val_mask,
+                       val);
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range);
+
+/**
+ * snd_soc_get_volsw_range - single mixer get callback with range
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value, within a range, of a single mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int rreg = mc->rreg;
+       unsigned int shift = mc->shift;
+       int min = mc->min;
+       int max = mc->max;
+       unsigned int mask = (1 << fls(max)) - 1;
+       unsigned int invert = mc->invert;
+       unsigned int val;
+       int ret;
+
+       ret = snd_soc_component_read(component, reg, &val);
+       if (ret)
+               return ret;
+
+       ucontrol->value.integer.value[0] = (val >> shift) & mask;
+       if (invert)
+               ucontrol->value.integer.value[0] =
+                       max - ucontrol->value.integer.value[0];
+       else
+               ucontrol->value.integer.value[0] =
+                       ucontrol->value.integer.value[0] - min;
+
+       if (snd_soc_volsw_is_stereo(mc)) {
+               ret = snd_soc_component_read(component, rreg, &val);
+               if (ret)
+                       return ret;
+
+               ucontrol->value.integer.value[1] = (val >> shift) & mask;
+               if (invert)
+                       ucontrol->value.integer.value[1] =
+                               max - ucontrol->value.integer.value[1];
+               else
+                       ucontrol->value.integer.value[1] =
+                               ucontrol->value.integer.value[1] - min;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range);
+
+/**
+ * snd_soc_limit_volume - Set new limit to an existing volume control.
+ *
+ * @codec: where to look for the control
+ * @name: Name of the control
+ * @max: new maximum limit
+ *
+ * Return 0 for success, else error.
+ */
+int snd_soc_limit_volume(struct snd_soc_codec *codec,
+       const char *name, int max)
+{
+       struct snd_card *card = codec->component.card->snd_card;
+       struct snd_kcontrol *kctl;
+       struct soc_mixer_control *mc;
+       int found = 0;
+       int ret = -EINVAL;
+
+       /* Sanity check for name and max */
+       if (unlikely(!name || max <= 0))
+               return -EINVAL;
+
+       list_for_each_entry(kctl, &card->controls, list) {
+               if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) {
+                       found = 1;
+                       break;
+               }
+       }
+       if (found) {
+               mc = (struct soc_mixer_control *)kctl->private_value;
+               if (max <= mc->max) {
+                       mc->platform_max = max;
+                       ret = 0;
+               }
+       }
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_limit_volume);
+
+int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
+                      struct snd_ctl_elem_info *uinfo)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_bytes *params = (void *)kcontrol->private_value;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+       uinfo->count = params->num_regs * component->val_bytes;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_info);
+
+int snd_soc_bytes_get(struct snd_kcontrol *kcontrol,
+                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_bytes *params = (void *)kcontrol->private_value;
+       int ret;
+
+       if (component->regmap)
+               ret = regmap_raw_read(component->regmap, params->base,
+                                     ucontrol->value.bytes.data,
+                                     params->num_regs * component->val_bytes);
+       else
+               ret = -EINVAL;
+
+       /* Hide any masked bytes to ensure consistent data reporting */
+       if (ret == 0 && params->mask) {
+               switch (component->val_bytes) {
+               case 1:
+                       ucontrol->value.bytes.data[0] &= ~params->mask;
+                       break;
+               case 2:
+                       ((u16 *)(&ucontrol->value.bytes.data))[0]
+                               &= cpu_to_be16(~params->mask);
+                       break;
+               case 4:
+                       ((u32 *)(&ucontrol->value.bytes.data))[0]
+                               &= cpu_to_be32(~params->mask);
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_get);
+
+int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
+                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_bytes *params = (void *)kcontrol->private_value;
+       int ret, len;
+       unsigned int val, mask;
+       void *data;
+
+       if (!component->regmap || !params->num_regs)
+               return -EINVAL;
+
+       len = params->num_regs * component->val_bytes;
+
+       data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA);
+       if (!data)
+               return -ENOMEM;
+
+       /*
+        * If we've got a mask then we need to preserve the register
+        * bits.  We shouldn't modify the incoming data so take a
+        * copy.
+        */
+       if (params->mask) {
+               ret = regmap_read(component->regmap, params->base, &val);
+               if (ret != 0)
+                       goto out;
+
+               val &= params->mask;
+
+               switch (component->val_bytes) {
+               case 1:
+                       ((u8 *)data)[0] &= ~params->mask;
+                       ((u8 *)data)[0] |= val;
+                       break;
+               case 2:
+                       mask = ~params->mask;
+                       ret = regmap_parse_val(component->regmap,
+                                                       &mask, &mask);
+                       if (ret != 0)
+                               goto out;
+
+                       ((u16 *)data)[0] &= mask;
+
+                       ret = regmap_parse_val(component->regmap,
+                                                       &val, &val);
+                       if (ret != 0)
+                               goto out;
+
+                       ((u16 *)data)[0] |= val;
+                       break;
+               case 4:
+                       mask = ~params->mask;
+                       ret = regmap_parse_val(component->regmap,
+                                                       &mask, &mask);
+                       if (ret != 0)
+                               goto out;
+
+                       ((u32 *)data)[0] &= mask;
+
+                       ret = regmap_parse_val(component->regmap,
+                                                       &val, &val);
+                       if (ret != 0)
+                               goto out;
+
+                       ((u32 *)data)[0] |= val;
+                       break;
+               default:
+                       ret = -EINVAL;
+                       goto out;
+               }
+       }
+
+       ret = regmap_raw_write(component->regmap, params->base,
+                              data, len);
+
+out:
+       kfree(data);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_put);
+
+int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_info *ucontrol)
+{
+       struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+
+       ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+       ucontrol->count = params->max;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_info_ext);
+
+int snd_soc_bytes_tlv_callback(struct snd_kcontrol *kcontrol, int op_flag,
+                               unsigned int size, unsigned int __user *tlv)
+{
+       struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+       unsigned int count = size < params->max ? size : params->max;
+       int ret = -ENXIO;
+
+       switch (op_flag) {
+       case SNDRV_CTL_TLV_OP_READ:
+               if (params->get)
+                       ret = params->get(tlv, count);
+               break;
+       case SNDRV_CTL_TLV_OP_WRITE:
+               if (params->put)
+                       ret = params->put(tlv, count);
+               break;
+       }
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_tlv_callback);
+
+/**
+ * snd_soc_info_xr_sx - signed multi register info callback
+ * @kcontrol: mreg control
+ * @uinfo: control element information
+ *
+ * Callback to provide information of a control that can
+ * span multiple codec registers which together
+ * forms a single signed value in a MSB/LSB manner.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_info *uinfo)
+{
+       struct soc_mreg_control *mc =
+               (struct soc_mreg_control *)kcontrol->private_value;
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = mc->min;
+       uinfo->value.integer.max = mc->max;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx);
+
+/**
+ * snd_soc_get_xr_sx - signed multi register get callback
+ * @kcontrol: mreg control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a control that can span
+ * multiple codec registers which together forms a single
+ * signed value in a MSB/LSB manner. The control supports
+ * specifying total no of bits used to allow for bitfields
+ * across the multiple codec registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_mreg_control *mc =
+               (struct soc_mreg_control *)kcontrol->private_value;
+       unsigned int regbase = mc->regbase;
+       unsigned int regcount = mc->regcount;
+       unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
+       unsigned int regwmask = (1<<regwshift)-1;
+       unsigned int invert = mc->invert;
+       unsigned long mask = (1UL<<mc->nbits)-1;
+       long min = mc->min;
+       long max = mc->max;
+       long val = 0;
+       unsigned int regval;
+       unsigned int i;
+       int ret;
+
+       for (i = 0; i < regcount; i++) {
+               ret = snd_soc_component_read(component, regbase+i, &regval);
+               if (ret)
+                       return ret;
+               val |= (regval & regwmask) << (regwshift*(regcount-i-1));
+       }
+       val &= mask;
+       if (min < 0 && val > max)
+               val |= ~mask;
+       if (invert)
+               val = max - val;
+       ucontrol->value.integer.value[0] = val;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx);
+
+/**
+ * snd_soc_put_xr_sx - signed multi register get callback
+ * @kcontrol: mreg control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value of a control that can span
+ * multiple codec registers which together forms a single
+ * signed value in a MSB/LSB manner. The control supports
+ * specifying total no of bits used to allow for bitfields
+ * across the multiple codec registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_mreg_control *mc =
+               (struct soc_mreg_control *)kcontrol->private_value;
+       unsigned int regbase = mc->regbase;
+       unsigned int regcount = mc->regcount;
+       unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
+       unsigned int regwmask = (1<<regwshift)-1;
+       unsigned int invert = mc->invert;
+       unsigned long mask = (1UL<<mc->nbits)-1;
+       long max = mc->max;
+       long val = ucontrol->value.integer.value[0];
+       unsigned int i, regval, regmask;
+       int err;
+
+       if (invert)
+               val = max - val;
+       val &= mask;
+       for (i = 0; i < regcount; i++) {
+               regval = (val >> (regwshift*(regcount-i-1))) & regwmask;
+               regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask;
+               err = snd_soc_component_update_bits(component, regbase+i,
+                               regmask, regval);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx);
+
+/**
+ * snd_soc_get_strobe - strobe get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback get the value of a strobe mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_strobe(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int shift = mc->shift;
+       unsigned int mask = 1 << shift;
+       unsigned int invert = mc->invert != 0;
+       unsigned int val;
+       int ret;
+
+       ret = snd_soc_component_read(component, reg, &val);
+       if (ret)
+               return ret;
+
+       val &= mask;
+
+       if (shift != 0 && val != 0)
+               val = val >> shift;
+       ucontrol->value.enumerated.item[0] = val ^ invert;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_strobe);
+
+/**
+ * snd_soc_put_strobe - strobe put callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback strobe a register bit to high then low (or the inverse)
+ * in one pass of a single mixer enum control.
+ *
+ * Returns 1 for success.
+ */
+int snd_soc_put_strobe(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int shift = mc->shift;
+       unsigned int mask = 1 << shift;
+       unsigned int invert = mc->invert != 0;
+       unsigned int strobe = ucontrol->value.enumerated.item[0] != 0;
+       unsigned int val1 = (strobe ^ invert) ? mask : 0;
+       unsigned int val2 = (strobe ^ invert) ? 0 : mask;
+       int err;
+
+       err = snd_soc_component_update_bits(component, reg, mask, val1);
+       if (err < 0)
+               return err;
+
+       return snd_soc_component_update_bits(component, reg, mask, val2);
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_strobe);
index 002311a..eb87d96 100644 (file)
@@ -654,6 +654,8 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
                        codec_dai->rate = 0;
        }
 
+       snd_soc_dai_digital_mute(cpu_dai, 1, substream->stream);
+
        if (cpu_dai->driver->ops->shutdown)
                cpu_dai->driver->ops->shutdown(substream, cpu_dai);
 
@@ -772,6 +774,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
        for (i = 0; i < rtd->num_codecs; i++)
                snd_soc_dai_digital_mute(rtd->codec_dais[i], 0,
                                         substream->stream);
+       snd_soc_dai_digital_mute(cpu_dai, 0, substream->stream);
 
 out:
        mutex_unlock(&rtd->pcm_mutex);
@@ -1522,13 +1525,36 @@ static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream)
                dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture);
 }
 
+static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd);
+
+/* Set FE's runtime_update state; the state is protected via PCM stream lock
+ * for avoiding the race with trigger callback.
+ * If the state is unset and a trigger is pending while the previous operation,
+ * process the pending trigger action here.
+ */
+static void dpcm_set_fe_update_state(struct snd_soc_pcm_runtime *fe,
+                                    int stream, enum snd_soc_dpcm_update state)
+{
+       struct snd_pcm_substream *substream =
+               snd_soc_dpcm_get_substream(fe, stream);
+
+       snd_pcm_stream_lock_irq(substream);
+       if (state == SND_SOC_DPCM_UPDATE_NO && fe->dpcm[stream].trigger_pending) {
+               dpcm_fe_dai_do_trigger(substream,
+                                      fe->dpcm[stream].trigger_pending - 1);
+               fe->dpcm[stream].trigger_pending = 0;
+       }
+       fe->dpcm[stream].runtime_update = state;
+       snd_pcm_stream_unlock_irq(substream);
+}
+
 static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
 {
        struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
        struct snd_pcm_runtime *runtime = fe_substream->runtime;
        int stream = fe_substream->stream, ret = 0;
 
-       fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
 
        ret = dpcm_be_dai_startup(fe, fe_substream->stream);
        if (ret < 0) {
@@ -1550,13 +1576,13 @@ static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
        dpcm_set_fe_runtime(fe_substream);
        snd_pcm_limit_hw_rates(runtime);
 
-       fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
        return 0;
 
 unwind:
        dpcm_be_dai_startup_unwind(fe, fe_substream->stream);
 be_err:
-       fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
        return ret;
 }
 
@@ -1603,7 +1629,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream)
        struct snd_soc_pcm_runtime *fe = substream->private_data;
        int stream = substream->stream;
 
-       fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
 
        /* shutdown the BEs */
        dpcm_be_dai_shutdown(fe, substream->stream);
@@ -1617,7 +1643,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream)
        dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
 
        fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
-       fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
        return 0;
 }
 
@@ -1641,6 +1667,10 @@ int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream)
                if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
                                continue;
 
+               /* do not free hw if this BE is used by other FE */
+               if (be->dpcm[stream].users > 1)
+                       continue;
+
                if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
                    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
                    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
@@ -1665,7 +1695,7 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream)
        int err, stream = substream->stream;
 
        mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
-       fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
 
        dev_dbg(fe->dev, "ASoC: hw_free FE %s\n", fe->dai_link->name);
 
@@ -1680,7 +1710,7 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream)
        err = dpcm_be_dai_hw_free(fe, stream);
 
        fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
-       fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
 
        mutex_unlock(&fe->card->mutex);
        return 0;
@@ -1773,7 +1803,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream,
        int ret, stream = substream->stream;
 
        mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
-       fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
 
        memcpy(&fe->dpcm[substream->stream].hw_params, params,
                        sizeof(struct snd_pcm_hw_params));
@@ -1796,7 +1826,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream,
                fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
 
 out:
-       fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
        mutex_unlock(&fe->card->mutex);
        return ret;
 }
@@ -1910,7 +1940,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
 }
 EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger);
 
-static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd)
+static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
 {
        struct snd_soc_pcm_runtime *fe = substream->private_data;
        int stream = substream->stream, ret;
@@ -1984,6 +2014,23 @@ out:
        return ret;
 }
 
+static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_soc_pcm_runtime *fe = substream->private_data;
+       int stream = substream->stream;
+
+       /* if FE's runtime_update is already set, we're in race;
+        * process this trigger later at exit
+        */
+       if (fe->dpcm[stream].runtime_update != SND_SOC_DPCM_UPDATE_NO) {
+               fe->dpcm[stream].trigger_pending = cmd + 1;
+               return 0; /* delayed, assuming it's successful */
+       }
+
+       /* we're alone, let's trigger */
+       return dpcm_fe_dai_do_trigger(substream, cmd);
+}
+
 int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream)
 {
        struct snd_soc_dpcm *dpcm;
@@ -2027,7 +2074,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
 
        dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name);
 
-       fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
 
        /* there is no point preparing this FE if there are no BEs */
        if (list_empty(&fe->dpcm[stream].be_clients)) {
@@ -2054,7 +2101,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
        fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
 
 out:
-       fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
        mutex_unlock(&fe->card->mutex);
 
        return ret;
@@ -2201,11 +2248,11 @@ static int dpcm_run_new_update(struct snd_soc_pcm_runtime *fe, int stream)
 {
        int ret;
 
-       fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
+       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE);
        ret = dpcm_run_update_startup(fe, stream);
        if (ret < 0)
                dev_err(fe->dev, "ASoC: failed to startup some BEs\n");
-       fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
 
        return ret;
 }
@@ -2214,11 +2261,11 @@ static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream)
 {
        int ret;
 
-       fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
+       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE);
        ret = dpcm_run_update_shutdown(fe, stream);
        if (ret < 0)
                dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n");
-       fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+       dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
 
        return ret;
 }
@@ -2248,7 +2295,13 @@ int soc_dpcm_runtime_update(struct snd_soc_card *card)
                        fe->dai_link->name);
 
                /* skip if FE doesn't have playback capability */
-               if (!fe->cpu_dai->driver->playback.channels_min)
+               if (!fe->cpu_dai->driver->playback.channels_min
+                   || !fe->codec_dai->driver->playback.channels_min)
+                       goto capture;
+
+               /* skip if FE isn't currently playing */
+               if (!fe->cpu_dai->playback_active
+                   || !fe->codec_dai->playback_active)
                        goto capture;
 
                paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list);
@@ -2278,7 +2331,13 @@ int soc_dpcm_runtime_update(struct snd_soc_card *card)
                dpcm_path_put(&list);
 capture:
                /* skip if FE doesn't have capture capability */
-               if (!fe->cpu_dai->driver->capture.channels_min)
+               if (!fe->cpu_dai->driver->capture.channels_min
+                   || !fe->codec_dai->driver->capture.channels_min)
+                       continue;
+
+               /* skip if FE isn't currently capturing */
+               if (!fe->cpu_dai->capture_active
+                   || !fe->codec_dai->capture_active)
                        continue;
 
                paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list);
index 3b0fa12..29a9957 100644 (file)
@@ -228,7 +228,7 @@ static int tegra20_ac97_probe(struct snd_soc_dai *dai)
 
 static struct snd_soc_dai_driver tegra20_ac97_dai = {
        .name = "tegra-ac97-pcm",
-       .ac97_control = 1,
+       .bus_control = true,
        .probe = tegra20_ac97_probe,
        .playback = {
                .stream_name = "PCM Playback",
index 9edd68d..f7135cd 100644 (file)
@@ -152,7 +152,7 @@ static int txx9aclc_ac97_remove(struct snd_soc_dai *dai)
 }
 
 static struct snd_soc_dai_driver txx9aclc_ac97_dai = {
-       .ac97_control           = 1,
+       .bus_control            = true,
        .probe                  = txx9aclc_ac97_probe,
        .remove                 = txx9aclc_ac97_remove,
        .playback = {
index cd71fd8..00b7e2d 100644 (file)
@@ -292,7 +292,7 @@ static int txx9aclc_pcm_new(struct snd_soc_pcm_runtime *rtd)
        struct snd_card *card = rtd->card->snd_card;
        struct snd_soc_dai *dai = rtd->cpu_dai;
        struct snd_pcm *pcm = rtd->pcm;
-       struct platform_device *pdev = to_platform_device(dai->platform->dev);
+       struct platform_device *pdev = to_platform_device(rtd->platform->dev);
        struct txx9aclc_soc_device *dev;
        struct resource *r;
        int i;
index b3b66aa..9f2d045 100644 (file)
@@ -63,12 +63,8 @@ static void mop500_of_node_put(void)
        int i;
 
        for (i = 0; i < 2; i++) {
-               if (mop500_dai_links[i].cpu_of_node)
-                       of_node_put((struct device_node *)
-                               mop500_dai_links[i].cpu_of_node);
-               if (mop500_dai_links[i].codec_of_node)
-                       of_node_put((struct device_node *)
-                               mop500_dai_links[i].codec_of_node);
+               of_node_put(mop500_dai_links[i].cpu_of_node);
+               of_node_put(mop500_dai_links[i].codec_of_node);
        }
 }
 
index 7ecd0e8..f61ebb1 100644 (file)
@@ -591,18 +591,19 @@ static void snd_usb_audio_disconnect(struct usb_device *dev,
 {
        struct snd_card *card;
        struct list_head *p;
+       bool was_shutdown;
 
        if (chip == (void *)-1L)
                return;
 
        card = chip->card;
        down_write(&chip->shutdown_rwsem);
+       was_shutdown = chip->shutdown;
        chip->shutdown = 1;
        up_write(&chip->shutdown_rwsem);
 
        mutex_lock(&register_mutex);
-       chip->num_interfaces--;
-       if (chip->num_interfaces <= 0) {
+       if (!was_shutdown) {
                struct snd_usb_endpoint *ep;
 
                snd_card_disconnect(card);
@@ -622,6 +623,10 @@ static void snd_usb_audio_disconnect(struct usb_device *dev,
                list_for_each(p, &chip->mixer_list) {
                        snd_usb_mixer_disconnect(p);
                }
+       }
+
+       chip->num_interfaces--;
+       if (chip->num_interfaces <= 0) {
                usb_chip[chip->index] = NULL;
                mutex_unlock(&register_mutex);
                snd_card_free_when_closed(card);
index 2e4a9db..6e354d3 100644 (file)
@@ -2033,10 +2033,11 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
        cval->res = 1;
        cval->initialized = 1;
 
-       if (desc->bDescriptorSubtype == UAC2_CLOCK_SELECTOR)
-               cval->control = UAC2_CX_CLOCK_SELECTOR;
-       else
+       if (state->mixer->protocol == UAC_VERSION_1)
                cval->control = 0;
+       else /* UAC_VERSION_2 */
+               cval->control = (desc->bDescriptorSubtype == UAC2_CLOCK_SELECTOR) ?
+                       UAC2_CX_CLOCK_SELECTOR : UAC2_SU_SELECTOR;
 
        namelist = kmalloc(sizeof(char *) * desc->bNrInPins, GFP_KERNEL);
        if (!namelist) {
index f119a41..8c9bf4b 100644 (file)
@@ -593,10 +593,10 @@ static int snd_nativeinstruments_control_get(struct snd_kcontrol *kcontrol,
        if (mixer->chip->shutdown)
                ret = -ENODEV;
        else
-               ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), bRequest,
+               ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), bRequest,
                                  USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
                                  0, wIndex,
-                                 &tmp, sizeof(tmp), 1000);
+                                 &tmp, sizeof(tmp));
        up_read(&mixer->chip->shutdown_rwsem);
 
        if (ret < 0) {
@@ -885,6 +885,11 @@ static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl,
        return changed;
 }
 
+static void kctl_private_value_free(struct snd_kcontrol *kctl)
+{
+       kfree((void *)kctl->private_value);
+}
+
 static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer,
        int validx, int bUnitID)
 {
@@ -919,6 +924,7 @@ static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer,
                return -ENOMEM;
        }
 
+       kctl->private_free = kctl_private_value_free;
        err = snd_ctl_add(mixer->chip->card, kctl);
        if (err < 0)
                return err;
index d2aa45a..60dfe0d 100644 (file)
@@ -1146,6 +1146,20 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
        if ((le16_to_cpu(dev->descriptor.idVendor) == 0x23ba) &&
            (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
                mdelay(20);
+
+       /* Marantz/Denon devices with USB DAC functionality need a delay
+        * after each class compliant request
+        */
+       if ((le16_to_cpu(dev->descriptor.idVendor) == 0x154e) &&
+           (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) {
+
+               switch (le16_to_cpu(dev->descriptor.idProduct)) {
+               case 0x3005: /* Marantz HD-DAC1 */
+               case 0x3006: /* Marantz SA-14S1 */
+                       mdelay(20);
+                       break;
+               }
+       }
 }
 
 /*
@@ -1179,12 +1193,12 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
        /* iFi Audio micro/nano iDSD */
        case USB_ID(0x20b1, 0x3008):
                if (fp->altsetting == 2)
-                       return SNDRV_PCM_FMTBIT_DSD_U32_LE;
+                       return SNDRV_PCM_FMTBIT_DSD_U32_BE;
                break;
        /* DIYINHK DSD DXD 384kHz USB to I2S/DSD */
        case USB_ID(0x20b1, 0x2009):
                if (fp->altsetting == 3)
-                       return SNDRV_PCM_FMTBIT_DSD_U32_LE;
+                       return SNDRV_PCM_FMTBIT_DSD_U32_BE;
                break;
        default:
                break;
index a8f81c7..5152476 100755 (executable)
@@ -82,7 +82,7 @@ parse_opts() { # opts
 }
 
 # Parameters
-DEBUGFS_DIR=`grep debugfs /proc/mounts | cut -f2 -d' '`
+DEBUGFS_DIR=`grep debugfs /proc/mounts | cut -f2 -d' ' | head -1`
 TRACING_DIR=$DEBUGFS_DIR/tracing
 TOP_DIR=`absdir $0`
 TEST_DIR=$TOP_DIR/test.d
index 57b9c2b..6f67333 100644 (file)
@@ -128,7 +128,7 @@ static int sock_fanout_read_ring(int fd, void *ring)
        struct tpacket2_hdr *header = ring;
        int count = 0;
 
-       while (header->tp_status & TP_STATUS_USER && count < RING_NUM_FRAMES) {
+       while (count < RING_NUM_FRAMES && header->tp_status & TP_STATUS_USER) {
                count++;
                header = ring + (count * getpagesize());
        }
index 3aaca49..aacdb59 100644 (file)
@@ -1933,7 +1933,7 @@ out:
 
 int kvm_vgic_create(struct kvm *kvm)
 {
-       int i, vcpu_lock_idx = -1, ret = 0;
+       int i, vcpu_lock_idx = -1, ret;
        struct kvm_vcpu *vcpu;
 
        mutex_lock(&kvm->lock);
@@ -1948,6 +1948,7 @@ int kvm_vgic_create(struct kvm *kvm)
         * vcpu->mutex.  By grabbing the vcpu->mutex of all VCPUs we ensure
         * that no other VCPUs are run while we create the vgic.
         */
+       ret = -EBUSY;
        kvm_for_each_vcpu(i, vcpu, kvm) {
                if (!mutex_trylock(&vcpu->mutex))
                        goto out_unlock;
@@ -1955,11 +1956,10 @@ int kvm_vgic_create(struct kvm *kvm)
        }
 
        kvm_for_each_vcpu(i, vcpu, kvm) {
-               if (vcpu->arch.has_run_once) {
-                       ret = -EBUSY;
+               if (vcpu->arch.has_run_once)
                        goto out_unlock;
-               }
        }
+       ret = 0;
 
        spin_lock_init(&kvm->arch.vgic.lock);
        kvm->arch.vgic.in_kernel = true;
index 25ffac9..3cee7b1 100644 (file)
@@ -107,10 +107,10 @@ EXPORT_SYMBOL_GPL(kvm_rebooting);
 
 static bool largepages_enabled = true;
 
-bool kvm_is_mmio_pfn(pfn_t pfn)
+bool kvm_is_reserved_pfn(pfn_t pfn)
 {
        if (pfn_valid(pfn))
-               return !is_zero_pfn(pfn) && PageReserved(pfn_to_page(pfn));
+               return PageReserved(pfn_to_page(pfn));
 
        return true;
 }
@@ -1321,7 +1321,7 @@ static pfn_t hva_to_pfn(unsigned long addr, bool atomic, bool *async,
        else if ((vma->vm_flags & VM_PFNMAP)) {
                pfn = ((addr - vma->vm_start) >> PAGE_SHIFT) +
                        vma->vm_pgoff;
-               BUG_ON(!kvm_is_mmio_pfn(pfn));
+               BUG_ON(!kvm_is_reserved_pfn(pfn));
        } else {
                if (async && vma_is_valid(vma, write_fault))
                        *async = true;
@@ -1427,7 +1427,7 @@ static struct page *kvm_pfn_to_page(pfn_t pfn)
        if (is_error_noslot_pfn(pfn))
                return KVM_ERR_PTR_BAD_PAGE;
 
-       if (kvm_is_mmio_pfn(pfn)) {
+       if (kvm_is_reserved_pfn(pfn)) {
                WARN_ON(1);
                return KVM_ERR_PTR_BAD_PAGE;
        }
@@ -1456,7 +1456,7 @@ EXPORT_SYMBOL_GPL(kvm_release_page_clean);
 
 void kvm_release_pfn_clean(pfn_t pfn)
 {
-       if (!is_error_noslot_pfn(pfn) && !kvm_is_mmio_pfn(pfn))
+       if (!is_error_noslot_pfn(pfn) && !kvm_is_reserved_pfn(pfn))
                put_page(pfn_to_page(pfn));
 }
 EXPORT_SYMBOL_GPL(kvm_release_pfn_clean);
@@ -1477,7 +1477,7 @@ static void kvm_release_pfn_dirty(pfn_t pfn)
 
 void kvm_set_pfn_dirty(pfn_t pfn)
 {
-       if (!kvm_is_mmio_pfn(pfn)) {
+       if (!kvm_is_reserved_pfn(pfn)) {
                struct page *page = pfn_to_page(pfn);
                if (!PageReserved(page))
                        SetPageDirty(page);
@@ -1487,14 +1487,14 @@ EXPORT_SYMBOL_GPL(kvm_set_pfn_dirty);
 
 void kvm_set_pfn_accessed(pfn_t pfn)
 {
-       if (!kvm_is_mmio_pfn(pfn))
+       if (!kvm_is_reserved_pfn(pfn))
                mark_page_accessed(pfn_to_page(pfn));
 }
 EXPORT_SYMBOL_GPL(kvm_set_pfn_accessed);
 
 void kvm_get_pfn(pfn_t pfn)
 {
-       if (!kvm_is_mmio_pfn(pfn))
+       if (!kvm_is_reserved_pfn(pfn))
                get_page(pfn_to_page(pfn));
 }
 EXPORT_SYMBOL_GPL(kvm_get_pfn);