]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
Merge tag 'asoc-v4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound...
authorTakashi Iwai <tiwai@suse.de>
Tue, 26 Jul 2016 08:35:31 +0000 (10:35 +0200)
committerTakashi Iwai <tiwai@suse.de>
Tue, 26 Jul 2016 08:35:31 +0000 (10:35 +0200)
ASoC: Updates for v4.8

Not really any framework work this time around (though we have seen one
of the Analog Devices drivers move more to the clock API which is good
to see) but rather a lot of new drivers:

 - Lots of updates for the Intel drivers, mostly board support and bug
   fixing, and to the NAU8825 driver.
 - Work on generalizing bits of simple-card to allow more code sharing
   with the Renesas rsrc-card (which can't use simple-card due to DPCM).
 - Removal of the Odroid X2 driver due to replacement with simple-card.
 - Support for several new Mediatek platforms and associated boards.
 - New drivers for Allwinner A10, Analog Devices ADAU7002, Broadcom
   Cygnus, Cirrus Logic CS35L33 and CS53L30, Maxim MAX8960 and MAX98504,
   Realtek RT5514 and Wolfson WM8758

745 files changed:
Documentation/devicetree/bindings/sound/adi,adau17x1.txt
Documentation/devicetree/bindings/sound/adi,adau7002.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/brcm,cygnus-audio.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/bt-sco.txt
Documentation/devicetree/bindings/sound/cs35l33.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/cs53l30.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/designware-i2s.txt
Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
Documentation/devicetree/bindings/sound/max98504.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/max9860.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/mt2701-cs42448.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/mt8173-rt5650.txt
Documentation/devicetree/bindings/sound/omap-mcpdm.txt
Documentation/devicetree/bindings/sound/renesas,rsnd.txt
Documentation/devicetree/bindings/sound/rockchip-i2s.txt
Documentation/devicetree/bindings/sound/rt5514.txt
Documentation/devicetree/bindings/sound/samsung,odroidx2-max98090.txt [deleted file]
Documentation/devicetree/bindings/sound/sgtl5000.txt
Documentation/devicetree/bindings/sound/st,sti-asoc-card.txt
Documentation/devicetree/bindings/sound/sun4i-i2s.txt [new file with mode: 0644]
Documentation/gdb-kernel-debugging.txt
Documentation/sound/alsa/soc/machine.txt
Documentation/x86/intel_mpx.txt
Documentation/x86/tlb.txt
Documentation/x86/x86_64/machinecheck
MAINTAINERS
Makefile
arch/arc/Makefile
arch/arc/kernel/stacktrace.c
arch/arm/boot/dts/armada-385-linksys.dtsi
arch/arm/boot/dts/sun4i-a10.dtsi
arch/arm/boot/dts/sun5i-a10s.dtsi
arch/arm/boot/dts/sun5i-r8-chip.dts
arch/arm/boot/dts/sun7i-a20.dtsi
arch/arm/boot/dts/tegra30-beaver.dts
arch/arm/kvm/arm.c
arch/arm/mach-mvebu/Makefile
arch/arm/mach-mvebu/coherency.c
arch/arm64/include/asm/cputype.h
arch/arm64/include/asm/ptrace.h
arch/arm64/kernel/asm-offsets.c
arch/arm64/kernel/cpu_errata.c
arch/arm64/kernel/entry.S
arch/arm64/mm/fault.c
arch/m32r/boot/compressed/m32r_sio.c
arch/m68k/coldfire/head.S
arch/m68k/coldfire/m5272.c
arch/m68k/coldfire/pci.c
arch/m68k/configs/amiga_defconfig
arch/m68k/configs/apollo_defconfig
arch/m68k/configs/atari_defconfig
arch/m68k/configs/bvme6000_defconfig
arch/m68k/configs/hp300_defconfig
arch/m68k/configs/mac_defconfig
arch/m68k/configs/multi_defconfig
arch/m68k/configs/mvme147_defconfig
arch/m68k/configs/mvme16x_defconfig
arch/m68k/configs/q40_defconfig
arch/m68k/configs/sun3_defconfig
arch/m68k/configs/sun3x_defconfig
arch/m68k/ifpsp060/src/fpsp.S
arch/m68k/ifpsp060/src/pfpsp.S
arch/m68k/include/asm/dma.h
arch/m68k/include/asm/m525xsim.h
arch/m68k/include/asm/mcfmmu.h
arch/m68k/include/asm/q40_master.h
arch/m68k/mac/iop.c
arch/m68k/math-emu/fp_decode.h
arch/mips/include/asm/pgtable.h
arch/powerpc/include/asm/book3s/64/pgtable.h
arch/powerpc/kernel/eeh_driver.c
arch/powerpc/kernel/pci_64.c
arch/powerpc/kernel/process.c
arch/powerpc/kernel/tm.S
arch/powerpc/mm/hash_utils_64.c
arch/powerpc/mm/pgtable-radix.c
arch/s390/include/asm/fpu/api.h
arch/s390/kernel/ipl.c
arch/x86/events/core.c
arch/x86/events/intel/Makefile
arch/x86/events/intel/core.c
arch/x86/include/asm/cpufeatures.h
arch/x86/include/asm/pvclock.h
arch/x86/kernel/amd_nb.c
arch/x86/kernel/early-quirks.c
arch/x86/kernel/pvclock.c
arch/x86/kvm/lapic.c
arch/x86/kvm/vmx.c
arch/x86/kvm/x86.c
arch/x86/kvm/x86.h
arch/x86/mm/kasan_init_64.c
arch/x86/pci/acpi.c
arch/x86/power/hibernate_64.c
arch/x86/power/hibernate_asm_64.S
block/ioprio.c
crypto/asymmetric_keys/mscode_parser.c
crypto/asymmetric_keys/pkcs7_verify.c
crypto/asymmetric_keys/restrict.c
crypto/crypto_user.c
crypto/rsa-pkcs1pad.c
drivers/acpi/acpi_dbg.c
drivers/acpi/acpica/exconfig.c
drivers/acpi/acpica/nsparse.c
drivers/acpi/ec.c
drivers/acpi/nfit.c
drivers/acpi/nfit.h
drivers/acpi/pci_link.c
drivers/ata/ahci_seattle.c
drivers/ata/libata-core.c
drivers/ata/sata_mv.c
drivers/base/property.c
drivers/bcma/bcma_private.h
drivers/block/xen-blkfront.c
drivers/clk/at91/clk-programmable.c
drivers/clk/clk-oxnas.c
drivers/clk/rockchip/clk-cpu.c
drivers/clk/rockchip/clk-mmc-phase.c
drivers/clk/rockchip/clk-rk3399.c
drivers/clk/sunxi/clk-sun4i-display.c
drivers/clk/sunxi/clk-sun4i-tcon-ch1.c
drivers/connector/cn_proc.c
drivers/cpufreq/cpufreq-dt-platdev.c
drivers/cpufreq/cpufreq.c
drivers/cpufreq/intel_pstate.c
drivers/cpuidle/cpuidle.c
drivers/crypto/qat/qat_common/Makefile
drivers/crypto/ux500/hash/hash_core.c
drivers/crypto/vmx/aes_cbc.c
drivers/crypto/vmx/aes_ctr.c
drivers/crypto/vmx/ppc-xlate.pl
drivers/edac/sb_edac.c
drivers/gpio/Kconfig
drivers/gpio/gpio-sch.c
drivers/gpio/gpio-tegra.c
drivers/gpio/gpiolib-legacy.c
drivers/gpio/gpiolib.c
drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
drivers/gpu/drm/amd/amdgpu/atombios_i2c.c
drivers/gpu/drm/amd/amdgpu/atombios_i2c.h
drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_hwmgr.c
drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_hwmgr.h
drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.c
drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.h
drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.c
drivers/gpu/drm/amd/powerplay/hwmgr/tonga_processpptables.c
drivers/gpu/drm/amd/powerplay/inc/hwmgr.h
drivers/gpu/drm/amd/powerplay/inc/polaris10_ppsmc.h
drivers/gpu/drm/amd/powerplay/inc/smu74_discrete.h
drivers/gpu/drm/i915/i915_debugfs.c
drivers/gpu/drm/i915/i915_drv.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_gem_shrinker.c
drivers/gpu/drm/i915/i915_gem_stolen.c
drivers/gpu/drm/i915/i915_irq.c
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_csr.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_dp.c
drivers/gpu/drm/i915/intel_dpll_mgr.c
drivers/gpu/drm/i915/intel_lrc.c
drivers/gpu/drm/i915/intel_opregion.c
drivers/gpu/drm/i915/intel_panel.c
drivers/gpu/drm/i915/intel_pm.c
drivers/gpu/drm/i915/intel_ringbuffer.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/sorgf119.c
drivers/gpu/drm/sun4i/sun4i_crtc.c
drivers/gpu/drm/sun4i/sun4i_drv.c
drivers/gpu/drm/ttm/ttm_bo.c
drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c
drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
drivers/gpu/drm/vmwgfx/vmwgfx_msg.c
drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
drivers/hid/hid-multitouch.c
drivers/i2c/busses/i2c-qup.c
drivers/i2c/busses/i2c-tegra.c
drivers/i2c/i2c-boardinfo.c
drivers/i2c/muxes/i2c-mux-reg.c
drivers/iio/accel/kxsd9.c
drivers/iio/adc/ad7266.c
drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c
drivers/infiniband/core/sysfs.c
drivers/infiniband/hw/hfi1/chip.c
drivers/infiniband/hw/hfi1/ud.c
drivers/infiniband/hw/i40iw/i40iw_main.c
drivers/infiniband/hw/i40iw/i40iw_verbs.c
drivers/input/joystick/xpad.c
drivers/input/mouse/elantech.c
drivers/input/mouse/vmmouse.c
drivers/input/rmi4/rmi_bus.c
drivers/input/rmi4/rmi_f12.c
drivers/input/touchscreen/ts4800-ts.c
drivers/input/touchscreen/tsc2004.c
drivers/input/touchscreen/tsc2005.c
drivers/input/touchscreen/tsc200x-core.c
drivers/input/touchscreen/tsc200x-core.h
drivers/input/touchscreen/wacom_w8001.c
drivers/iommu/amd_iommu_init.c
drivers/iommu/intel-iommu.c
drivers/iommu/iova.c
drivers/irqchip/irq-mips-gic.c
drivers/media/i2c/adv7604.c
drivers/media/usb/airspy/airspy.c
drivers/media/v4l2-core/v4l2-ioctl.c
drivers/mfd/max77620.c
drivers/mmc/card/block.c
drivers/mmc/host/pxamci.c
drivers/mtd/nand/omap2.c
drivers/net/bonding/bond_3ad.c
drivers/net/bonding/bond_alb.c
drivers/net/bonding/bond_main.c
drivers/net/bonding/bond_netlink.c
drivers/net/can/at91_can.c
drivers/net/can/c_can/c_can.c
drivers/net/can/dev.c
drivers/net/can/usb/Kconfig
drivers/net/can/usb/gs_usb.c
drivers/net/can/usb/kvaser_usb.c
drivers/net/ethernet/agere/et131x.c
drivers/net/ethernet/amd/au1000_eth.c
drivers/net/ethernet/atheros/alx/alx.h
drivers/net/ethernet/atheros/alx/main.c
drivers/net/ethernet/aurora/nb8800.c
drivers/net/ethernet/broadcom/bcmsysport.c
drivers/net/ethernet/broadcom/bgmac.c
drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
drivers/net/ethernet/cavium/liquidio/lio_main.c
drivers/net/ethernet/cavium/thunder/nic_main.c
drivers/net/ethernet/cavium/thunder/thunder_bgx.c
drivers/net/ethernet/cavium/thunder/thunder_bgx.h
drivers/net/ethernet/chelsio/cxgb4/t4fw_version.h
drivers/net/ethernet/ethoc.c
drivers/net/ethernet/ezchip/nps_enet.c
drivers/net/ethernet/ibm/ibmvnic.c
drivers/net/ethernet/ibm/ibmvnic.h
drivers/net/ethernet/intel/e1000e/netdev.c
drivers/net/ethernet/intel/i40e/i40e_main.c
drivers/net/ethernet/intel/i40e/i40e_txrx.c
drivers/net/ethernet/intel/i40evf/i40e_txrx.c
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
drivers/net/ethernet/intel/ixgbevf/mbx.c
drivers/net/ethernet/marvell/mvneta.c
drivers/net/ethernet/mediatek/mtk_eth_soc.c
drivers/net/ethernet/mediatek/mtk_eth_soc.h
drivers/net/ethernet/mellanox/mlx4/cmd.c
drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
drivers/net/ethernet/mellanox/mlx4/en_netdev.c
drivers/net/ethernet/mellanox/mlx4/en_rx.c
drivers/net/ethernet/mellanox/mlx4/main.c
drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
drivers/net/ethernet/mellanox/mlx5/core/cmd.c
drivers/net/ethernet/mellanox/mlx5/core/en.h
drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c
drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
drivers/net/ethernet/mellanox/mlx5/core/en_main.c
drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
drivers/net/ethernet/mellanox/mlx5/core/en_stats.h
drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
drivers/net/ethernet/mellanox/mlx5/core/health.c
drivers/net/ethernet/mellanox/mlx5/core/main.c
drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c
drivers/net/ethernet/mellanox/mlx5/core/vport.c
drivers/net/ethernet/mellanox/mlx5/core/vxlan.c
drivers/net/ethernet/mellanox/mlx5/core/wq.c
drivers/net/ethernet/mellanox/mlxsw/reg.h
drivers/net/ethernet/mellanox/mlxsw/spectrum.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_buffers.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c
drivers/net/ethernet/mellanox/mlxsw/switchx2.c
drivers/net/ethernet/microchip/enc28j60.c
drivers/net/ethernet/netronome/nfp/nfp_net_common.c
drivers/net/ethernet/qlogic/qed/qed_hsi.h
drivers/net/ethernet/qlogic/qed/qed_l2.c
drivers/net/ethernet/qlogic/qed/qed_main.c
drivers/net/ethernet/qlogic/qed/qed_spq.c
drivers/net/ethernet/qlogic/qede/qede_main.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
drivers/net/ethernet/sfc/farch.c
drivers/net/ethernet/smsc/smsc911x.c
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/ethernet/ti/cpsw.c
drivers/net/ethernet/tile/tilegx.c
drivers/net/fddi/skfp/Makefile
drivers/net/geneve.c
drivers/net/macsec.c
drivers/net/phy/dp83867.c
drivers/net/phy/fixed_phy.c
drivers/net/phy/marvell.c
drivers/net/phy/smsc.c
drivers/net/ppp/ppp_generic.c
drivers/net/team/team.c
drivers/net/usb/cdc_ncm.c
drivers/net/usb/r8152.c
drivers/net/usb/usbnet.c
drivers/net/vrf.c
drivers/net/vxlan.c
drivers/net/wireless/ath/ath10k/core.c
drivers/net/wireless/ath/ath10k/htt_rx.c
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath9k/reg.h
drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
drivers/net/wireless/intel/iwlwifi/mvm/scan.c
drivers/net/wireless/intel/iwlwifi/mvm/sta.c
drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_8192e.c
drivers/nvdimm/pfn_devs.c
drivers/nvme/host/core.c
drivers/phy/phy-bcm-ns-usb2.c
drivers/phy/phy-miphy28lp.c
drivers/phy/phy-rcar-gen3-usb2.c
drivers/phy/phy-rockchip-dp.c
drivers/phy/phy-stih407-usb.c
drivers/phy/phy-sun4i-usb.c
drivers/pinctrl/Makefile
drivers/pinctrl/freescale/pinctrl-imx.c
drivers/pinctrl/intel/pinctrl-baytrail.c
drivers/pinctrl/pinctrl-single.c
drivers/pinctrl/tegra/Makefile
drivers/platform/chrome/cros_ec_dev.c
drivers/power/power_supply_core.c
drivers/power/tps65217_charger.c
drivers/pps/clients/pps_parport.c
drivers/regulator/anatop-regulator.c
drivers/regulator/max77620-regulator.c
drivers/regulator/qcom_smd-regulator.c
drivers/s390/net/qeth_l2_main.c
drivers/s390/net/qeth_l3_main.c
drivers/scsi/ipr.c
drivers/scsi/qla2xxx/qla_isr.c
drivers/scsi/scsi_devinfo.c
drivers/spi/spi-rockchip.c
drivers/spi/spi-sun4i.c
drivers/spi/spi-sun6i.c
drivers/spi/spi-ti-qspi.c
drivers/staging/iio/accel/sca3000_core.c
drivers/staging/iio/adc/ad7606_spi.c
drivers/staging/iio/impedance-analyzer/ad5933.c
drivers/tty/pty.c
drivers/tty/vt/keyboard.c
drivers/tty/vt/vt.c
drivers/usb/common/usb-otg-fsm.c
drivers/usb/core/hcd.c
drivers/usb/dwc3/dwc3-st.c
drivers/usb/host/ehci-st.c
drivers/usb/host/ohci-st.c
drivers/xen/xen-acpi-processor.c
drivers/xen/xenbus/xenbus_dev_frontend.c
drivers/xen/xenbus/xenbus_xs.c
fs/9p/vfs_file.c
fs/9p/vfs_inode.c
fs/9p/vfs_inode_dotl.c
fs/ceph/export.c
fs/ceph/file.c
fs/cifs/cifs_unicode.c
fs/cifs/cifs_unicode.h
fs/cifs/cifsfs.c
fs/cifs/cifsglob.h
fs/cifs/connect.c
fs/cifs/dir.c
fs/cifs/file.c
fs/cifs/ntlmssp.h
fs/cifs/sess.c
fs/cifs/smb2pdu.c
fs/configfs/file.c
fs/dax.c
fs/ecryptfs/crypto.c
fs/ecryptfs/file.c
fs/ecryptfs/kthread.c
fs/ecryptfs/main.c
fs/fs-writeback.c
fs/fuse/dir.c
fs/fuse/fuse_i.h
fs/fuse/inode.c
fs/gfs2/inode.c
fs/libfs.c
fs/lockd/svc.c
fs/locks.c
fs/namespace.c
fs/nfs/dir.c
fs/nfs/direct.c
fs/nfs/inode.c
fs/nfs/nfs4proc.c
fs/nfs/nfs4state.c
fs/nfs/pnfs.c
fs/nfs/pnfs_nfs.c
fs/nfs/read.c
fs/overlayfs/dir.c
fs/overlayfs/inode.c
fs/overlayfs/overlayfs.h
fs/overlayfs/super.c
fs/xfs/xfs_ioctl.c
include/acpi/acpi_bus.h
include/acpi/acpi_drivers.h
include/acpi/acpixf.h
include/asm-generic/vmlinux.lds.h
include/drm/i915_pciids.h
include/drm/ttm/ttm_bo_api.h
include/kvm/arm_pmu.h
include/linux/acpi.h
include/linux/audit.h
include/linux/bcma/bcma.h
include/linux/bpf.h
include/linux/filter.h
include/linux/huge_mm.h
include/linux/inet_diag.h
include/linux/memcontrol.h
include/linux/mfd/arizona/core.h
include/linux/mfd/da9052/da9052.h
include/linux/mlx4/device.h
include/linux/mlx5/driver.h
include/linux/net.h
include/linux/netdevice.h
include/linux/of.h
include/linux/posix_acl.h
include/linux/property.h
include/linux/pwm.h
include/linux/qed/qed_eth_if.h
include/linux/radix-tree.h
include/linux/reset.h
include/linux/rmap.h
include/linux/skbuff.h
include/linux/sock_diag.h
include/linux/usb/ehci_def.h
include/net/bonding.h
include/net/gre.h
include/net/ip.h
include/net/netfilter/nf_conntrack.h
include/net/netfilter/nf_tables.h
include/net/sock.h
include/net/switchdev.h
include/net/tc_act/tc_ife.h
include/sound/compress_driver.h
include/sound/cs35l33.h [new file with mode: 0644]
include/sound/hdmi-codec.h
include/sound/simple_card.h
include/sound/simple_card_utils.h [new file with mode: 0644]
include/sound/soc-dapm.h
include/sound/soc.h
include/uapi/linux/Kbuild
include/uapi/linux/fuse.h
include/uapi/linux/input-event-codes.h
include/uapi/linux/input.h
include/uapi/linux/netfilter/Kbuild
include/uapi/linux/netfilter/xt_SYNPROXY.h
init/Kconfig
kernel/audit.c
kernel/audit.h
kernel/auditsc.c
kernel/bpf/verifier.c
kernel/cgroup.c
kernel/cpu.c
kernel/events/core.c
kernel/gcov/gcc_4_7.c
kernel/sched/core.c
kernel/sched/fair.c
kernel/sched/loadavg.c
kernel/sched/sched.h
kernel/time/posix-cpu-timers.c
kernel/trace/bpf_trace.c
kernel/workqueue.c
mm/compaction.c
mm/huge_memory.c
mm/hugetlb.c
mm/kasan/quarantine.c
mm/memcontrol.c
mm/memory.c
mm/page_alloc.c
mm/rmap.c
mm/shmem.c
mm/slab_common.c
mm/workingset.c
net/8021q/vlan_dev.c
net/8021q/vlan_netlink.c
net/ax25/af_ax25.c
net/ax25/ax25_ds_timer.c
net/ax25/ax25_std_timer.c
net/ax25/ax25_subr.c
net/batman-adv/bridge_loop_avoidance.c
net/batman-adv/distributed-arp-table.c
net/batman-adv/originator.c
net/batman-adv/routing.c
net/batman-adv/send.c
net/batman-adv/soft-interface.c
net/batman-adv/translation-table.c
net/batman-adv/types.h
net/bridge/br_input.c
net/bridge/br_multicast.c
net/bridge/br_netfilter_hooks.c
net/bridge/br_netlink.c
net/bridge/br_private.h
net/ceph/osdmap.c
net/core/filter.c
net/core/flow_dissector.c
net/core/neighbour.c
net/core/skbuff.c
net/core/sock.c
net/dccp/ipv4.c
net/dccp/ipv6.c
net/decnet/dn_fib.c
net/ipv4/esp4.c
net/ipv4/fib_semantics.c
net/ipv4/gre_demux.c
net/ipv4/ip_gre.c
net/ipv4/ip_output.c
net/ipv4/ipconfig.c
net/ipv4/ipmr.c
net/ipv4/tcp_input.c
net/ipv4/tcp_output.c
net/ipv4/udp.c
net/ipv6/icmp.c
net/ipv6/ip6_checksum.c
net/ipv6/ip6_fib.c
net/ipv6/ip6_gre.c
net/ipv6/ip6mr.c
net/ipv6/route.c
net/ipv6/sit.c
net/ipv6/tcp_ipv6.c
net/ipv6/udp.c
net/kcm/kcmproc.c
net/mac80211/mesh.c
net/netfilter/ipvs/ip_vs_sync.c
net/netfilter/nf_conntrack_core.c
net/netfilter/nf_tables_api.c
net/netfilter/nf_tables_core.c
net/netfilter/nft_ct.c
net/netfilter/nft_hash.c
net/netfilter/nft_meta.c
net/netfilter/nft_rbtree.c
net/openvswitch/conntrack.c
net/packet/af_packet.c
net/rds/ib_cm.c
net/rds/loop.c
net/rds/sysctl.c
net/rds/tcp.c
net/rds/tcp.h
net/rds/tcp_connect.c
net/rds/tcp_listen.c
net/rds/tcp_recv.c
net/rds/tcp_send.c
net/rds/transport.c
net/rose/rose_in.c
net/sched/act_api.c
net/sched/act_ife.c
net/sched/act_ipt.c
net/sched/act_mirred.c
net/sched/sch_fifo.c
net/sched/sch_htb.c
net/sched/sch_netem.c
net/sched/sch_prio.c
net/sctp/input.c
net/sctp/sctp_diag.c
net/tipc/bearer.c
net/tipc/bearer.h
net/tipc/link.c
net/tipc/msg.c
net/tipc/msg.h
net/tipc/netlink_compat.c
net/tipc/node.c
net/tipc/socket.c
net/vmw_vsock/af_vsock.c
net/wireless/nl80211.c
net/wireless/util.c
scripts/gdb/linux/.gitignore
scripts/gdb/linux/Makefile
scripts/gdb/linux/constants.py.in
scripts/gdb/linux/radixtree.py [deleted file]
scripts/gdb/linux/symbols.py
scripts/gdb/vmlinux-gdb.py
security/apparmor/lsm.c
sound/core/compress_offload.c
sound/soc/atmel/Kconfig
sound/soc/atmel/atmel-classd.c
sound/soc/atmel/atmel-pdmic.c
sound/soc/atmel/atmel_ssc_dai.c
sound/soc/bcm/Kconfig
sound/soc/bcm/Makefile
sound/soc/bcm/cygnus-pcm.c [new file with mode: 0644]
sound/soc/bcm/cygnus-ssp.c [new file with mode: 0644]
sound/soc/bcm/cygnus-ssp.h [new file with mode: 0644]
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/adau-utils.c [new file with mode: 0644]
sound/soc/codecs/adau-utils.h [new file with mode: 0644]
sound/soc/codecs/adau1373.c
sound/soc/codecs/adau1761-i2c.c
sound/soc/codecs/adau1761-spi.c
sound/soc/codecs/adau1781-i2c.c
sound/soc/codecs/adau1781-spi.c
sound/soc/codecs/adau17x1.c
sound/soc/codecs/adau17x1.h
sound/soc/codecs/adau7002.c [new file with mode: 0644]
sound/soc/codecs/ak4613.c
sound/soc/codecs/ak4642.c
sound/soc/codecs/arizona.c
sound/soc/codecs/arizona.h
sound/soc/codecs/bt-sco.c
sound/soc/codecs/cs35l33.c [new file with mode: 0644]
sound/soc/codecs/cs35l33.h [new file with mode: 0644]
sound/soc/codecs/cs47l24.c
sound/soc/codecs/cs53l30.c [new file with mode: 0644]
sound/soc/codecs/cs53l30.h [new file with mode: 0644]
sound/soc/codecs/da7219-aad.c
sound/soc/codecs/da7219.c
sound/soc/codecs/hdac_hdmi.c
sound/soc/codecs/hdmi-codec.c
sound/soc/codecs/max98504.c [new file with mode: 0644]
sound/soc/codecs/max98504.h [new file with mode: 0644]
sound/soc/codecs/max9860.c [new file with mode: 0644]
sound/soc/codecs/max9860.h [new file with mode: 0644]
sound/soc/codecs/max9867.c [changed mode: 0755->0644]
sound/soc/codecs/max9867.h [changed mode: 0755->0644]
sound/soc/codecs/max9877.h
sound/soc/codecs/nau8825.c
sound/soc/codecs/nau8825.h
sound/soc/codecs/pcm1681.c
sound/soc/codecs/pcm179x.c
sound/soc/codecs/pcm5102a.c
sound/soc/codecs/rt286.c
sound/soc/codecs/rt5514-spi.c [new file with mode: 0644]
sound/soc/codecs/rt5514-spi.h [new file with mode: 0644]
sound/soc/codecs/rt5514.c
sound/soc/codecs/rt5514.h
sound/soc/codecs/rt5645.c
sound/soc/codecs/rt5645.h
sound/soc/codecs/rt5670.c
sound/soc/codecs/sgtl5000.c
sound/soc/codecs/sgtl5000.h
sound/soc/codecs/tas571x.c
sound/soc/codecs/tas571x.h
sound/soc/codecs/tlv320aic31xx.h
sound/soc/codecs/tpa6130a2.c
sound/soc/codecs/tpa6130a2.h
sound/soc/codecs/wm5102.c
sound/soc/codecs/wm5110.c
sound/soc/codecs/wm8731.c
sound/soc/codecs/wm8753.c
sound/soc/codecs/wm8985.c
sound/soc/codecs/wm8985.h
sound/soc/codecs/wm8998.c
sound/soc/codecs/wm_adsp.c
sound/soc/codecs/wm_adsp.h
sound/soc/davinci/davinci-mcasp.c
sound/soc/dwc/Kconfig
sound/soc/dwc/Makefile
sound/soc/dwc/designware_i2s.c
sound/soc/dwc/designware_pcm.c [new file with mode: 0644]
sound/soc/dwc/local.h [new file with mode: 0644]
sound/soc/fsl/Kconfig
sound/soc/fsl/fsl_spdif.c
sound/soc/generic/Kconfig
sound/soc/generic/Makefile
sound/soc/generic/simple-card-utils.c [new file with mode: 0644]
sound/soc/generic/simple-card.c
sound/soc/intel/Kconfig
sound/soc/intel/atom/sst/sst_acpi.c
sound/soc/intel/boards/Makefile
sound/soc/intel/boards/bxt_da7219_max98357a.c [new file with mode: 0644]
sound/soc/intel/boards/bxt_rt298.c
sound/soc/intel/boards/cht_bsw_rt5645.c
sound/soc/intel/boards/skl_nau88l25_max98357a.c
sound/soc/intel/boards/skl_nau88l25_ssm4567.c
sound/soc/intel/boards/skl_rt286.c
sound/soc/intel/common/Makefile
sound/soc/intel/common/sst-acpi.h
sound/soc/intel/common/sst-dsp-priv.h
sound/soc/intel/common/sst-dsp.c
sound/soc/intel/common/sst-dsp.h
sound/soc/intel/common/sst-firmware.c
sound/soc/intel/haswell/sst-haswell-pcm.c
sound/soc/intel/skylake/Makefile
sound/soc/intel/skylake/bxt-sst.c
sound/soc/intel/skylake/skl-messages.c
sound/soc/intel/skylake/skl-nhlt.c
sound/soc/intel/skylake/skl-nhlt.h
sound/soc/intel/skylake/skl-pcm.c
sound/soc/intel/skylake/skl-sst-dsp.c
sound/soc/intel/skylake/skl-sst-dsp.h
sound/soc/intel/skylake/skl-sst-ipc.c
sound/soc/intel/skylake/skl-sst-ipc.h
sound/soc/intel/skylake/skl-sst-utils.c [new file with mode: 0644]
sound/soc/intel/skylake/skl-sst.c
sound/soc/intel/skylake/skl-topology.c
sound/soc/intel/skylake/skl-topology.h
sound/soc/intel/skylake/skl.c
sound/soc/intel/skylake/skl.h
sound/soc/mediatek/Kconfig
sound/soc/mediatek/Makefile
sound/soc/mediatek/common/Makefile [new file with mode: 0644]
sound/soc/mediatek/common/mtk-afe-fe-dai.c [new file with mode: 0644]
sound/soc/mediatek/common/mtk-afe-fe-dai.h [new file with mode: 0644]
sound/soc/mediatek/common/mtk-afe-platform-driver.c [new file with mode: 0644]
sound/soc/mediatek/common/mtk-afe-platform-driver.h [new file with mode: 0644]
sound/soc/mediatek/common/mtk-base-afe.h [new file with mode: 0644]
sound/soc/mediatek/mt2701/Makefile [new file with mode: 0644]
sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c [new file with mode: 0644]
sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h [new file with mode: 0644]
sound/soc/mediatek/mt2701/mt2701-afe-common.h [new file with mode: 0644]
sound/soc/mediatek/mt2701/mt2701-afe-pcm.c [new file with mode: 0644]
sound/soc/mediatek/mt2701/mt2701-cs42448.c [new file with mode: 0644]
sound/soc/mediatek/mt2701/mt2701-reg.h [new file with mode: 0644]
sound/soc/mediatek/mt8173/Makefile [new file with mode: 0644]
sound/soc/mediatek/mt8173/mt8173-afe-common.h [new file with mode: 0644]
sound/soc/mediatek/mt8173/mt8173-afe-pcm.c [moved from sound/soc/mediatek/mtk-afe-pcm.c with 51% similarity]
sound/soc/mediatek/mt8173/mt8173-max98090.c [moved from sound/soc/mediatek/mt8173-max98090.c with 99% similarity]
sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c [moved from sound/soc/mediatek/mt8173-rt5650-rt5514.c with 99% similarity]
sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c [moved from sound/soc/mediatek/mt8173-rt5650-rt5676.c with 99% similarity]
sound/soc/mediatek/mt8173/mt8173-rt5650.c [moved from sound/soc/mediatek/mt8173-rt5650.c with 80% similarity]
sound/soc/mediatek/mtk-afe-common.h [deleted file]
sound/soc/omap/Kconfig
sound/soc/omap/omap-mcpdm.c
sound/soc/omap/rx51.c
sound/soc/rockchip/rockchip_i2s.c
sound/soc/rockchip/rockchip_i2s.h
sound/soc/rockchip/rockchip_max98090.c
sound/soc/rockchip/rockchip_spdif.c
sound/soc/samsung/Kconfig
sound/soc/samsung/Makefile
sound/soc/samsung/ac97.c
sound/soc/samsung/dma.h
sound/soc/samsung/dmaengine.c
sound/soc/samsung/i2s.c
sound/soc/samsung/odroidx2_max98090.c [deleted file]
sound/soc/samsung/pcm.c
sound/soc/samsung/s3c-i2s-v2.c
sound/soc/samsung/s3c2412-i2s.c
sound/soc/samsung/s3c24xx-i2s.c
sound/soc/samsung/spdif.c
sound/soc/sh/Kconfig
sound/soc/sh/rcar/adg.c
sound/soc/sh/rcar/gen.c
sound/soc/sh/rcar/rsrc-card.c
sound/soc/soc-compress.c
sound/soc/soc-dapm.c
sound/soc/soc-pcm.c
sound/soc/sti/uniperif_player.c
sound/soc/sunxi/Kconfig
sound/soc/sunxi/Makefile
sound/soc/sunxi/sun4i-i2s.c [new file with mode: 0644]
tools/objtool/builtin-check.c
tools/testing/radix-tree/tag_check.c
tools/vm/slabinfo.c

index 8dbce0e18dda266b3c824e1c34c5ad9b744ca2d2..1447dec281252cb1f71374742e54331ebebd7c5b 100644 (file)
@@ -13,6 +13,11 @@ Required properties:
  - reg:                        The i2c address. Value depends on the state of ADDR0
                        and ADDR1, as wired in hardware.
 
+Optional properties:
+ - clock-names:                If provided must be "mclk".
+ - clocks:             phandle + clock-specifiers for the clock that provides
+                       the audio master clock for the device.
+
 Examples:
 #include <dt-bindings/sound/adau17x1.h>
 
@@ -20,5 +25,8 @@ Examples:
                adau1361@38 {
                        compatible = "adi,adau1761";
                        reg = <0x38>;
+
+                       clock-names = "mclk";
+                       clocks = <&audio_clock>;
                };
        };
diff --git a/Documentation/devicetree/bindings/sound/adi,adau7002.txt b/Documentation/devicetree/bindings/sound/adi,adau7002.txt
new file mode 100644 (file)
index 0000000..f144ee1
--- /dev/null
@@ -0,0 +1,19 @@
+Analog Devices ADAU7002 Stereo PDM-to-I2S/TDM Converter
+
+Required properties:
+
+ - compatible: Must be "adi,adau7002"
+
+Optional properties:
+
+ - IOVDD-supply: Phandle and specifier for the power supply providing the IOVDD
+       supply as covered in Documentation/devicetree/bindings/regulator/regulator.txt
+
+       If this property is not present it is assumed that the supply pin is
+       hardwired to always on.
+
+Example:
+       adau7002: pdm-to-i2s {
+               compatible = "adi,adau7002";
+               IOVDD-supply = <&supply>;
+       };
diff --git a/Documentation/devicetree/bindings/sound/brcm,cygnus-audio.txt b/Documentation/devicetree/bindings/sound/brcm,cygnus-audio.txt
new file mode 100644 (file)
index 0000000..b139e66
--- /dev/null
@@ -0,0 +1,67 @@
+BROADCOM Cygnus Audio I2S/TDM/SPDIF controller
+
+Required properties:
+       - compatible : "brcm,cygnus-audio"
+       - #address-cells: 32bit valued, 1 cell.
+       - #size-cells:  32bit valued, 0 cell.
+       - reg : Should contain audio registers location and length
+       - reg-names: names of the registers listed in "reg" property
+               Valid names are "aud" and "i2s_in". "aud" contains a
+               set of DMA, I2S_OUT and SPDIF registers. "i2s_in" contains
+               a set of I2S_IN registers.
+       - clocks: PLL and leaf clocks used by audio ports
+       - assigned-clocks: PLL and leaf clocks
+       - assigned-clock-parents: parent clocks of the assigned clocks
+               (usually the PLL)
+       - assigned-clock-rates: List of clock frequencies of the
+               assigned clocks
+       - clock-names: names of 3 leaf clocks used by audio ports
+               Valid names are "ch0_audio", "ch1_audio", "ch2_audio"
+       - interrupts: audio DMA interrupt number
+
+SSP Subnode properties:
+- reg: The index of ssp port interface to use
+       Valid value are 0, 1, 2, or 3 (for spdif)
+
+Example:
+       cygnus_audio: audio@180ae000 {
+               compatible = "brcm,cygnus-audio";
+               #address-cells = <1>;
+               #size-cells = <0>;
+               reg = <0x180ae000 0xafd>, <0x180aec00 0x1f8>;
+               reg-names = "aud", "i2s_in";
+               clocks = <&audiopll BCM_CYGNUS_AUDIOPLL_CH0>,
+                               <&audiopll BCM_CYGNUS_AUDIOPLL_CH1>,
+                               <&audiopll BCM_CYGNUS_AUDIOPLL_CH2>;
+               assigned-clocks = <&audiopll BCM_CYGNUS_AUDIOPLL>,
+                                                       <&audiopll BCM_CYGNUS_AUDIOPLL_CH0>,
+                                                       <&audiopll BCM_CYGNUS_AUDIOPLL_CH1>,
+                                                       <&audiopll BCM_CYGNUS_AUDIOPLL_CH2>;
+               assigned-clock-parents = <&audiopll BCM_CYGNUS_AUDIOPLL>;
+               assigned-clock-rates = <1769470191>,
+                                                               <0>,
+                                                               <0>,
+                                                               <0>;
+               clock-names = "ch0_audio", "ch1_audio", "ch2_audio";
+               interrupts = <GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>;
+
+               ssp0: ssp_port@0 {
+                       reg = <0>;
+                       status = "okay";
+               };
+
+               ssp1: ssp_port@1 {
+                       reg = <1>;
+                       status = "disabled";
+               };
+
+               ssp2: ssp_port@2 {
+                       reg = <2>;
+                       status = "disabled";
+               };
+
+               spdif: spdif_port@3 {
+                       reg = <3>;
+                       status = "disabled";
+               };
+       };
index 29b8e5d40203f8955de22c7f1286d872c287a0b6..641edf75e18466f4cf917418e3fc4d5640103cdc 100644 (file)
@@ -4,7 +4,7 @@ This device support generic Bluetooth SCO link.
 
 Required properties:
 
-  - compatible : "delta,dfbmcs320"
+  - compatible : "delta,dfbmcs320" or "linux,bt-sco"
 
 Example:
 
diff --git a/Documentation/devicetree/bindings/sound/cs35l33.txt b/Documentation/devicetree/bindings/sound/cs35l33.txt
new file mode 100644 (file)
index 0000000..acfb475
--- /dev/null
@@ -0,0 +1,126 @@
+CS35L33 Speaker Amplifier
+
+Required properties:
+
+  - compatible : "cirrus,cs35l33"
+
+  - reg : the I2C address of the device for I2C
+
+  - VA-supply, VP-supply : power supplies for the device,
+    as covered in
+    Documentation/devicetree/bindings/regulator/regulator.txt.
+
+Optional properties:
+
+  - reset-gpios : gpio used to reset the amplifier
+
+  - interrupt-parent : Specifies the phandle of the interrupt controller to
+    which the IRQs from CS35L33 are delivered to.
+ -  interrupts : IRQ line info CS35L33.
+    (See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
+    for further information relating to interrupt properties)
+
+  - cirrus,boost-ctl : Booster voltage use to supply the amp. If the value is
+    0, then VBST = VP. If greater than 0, the boost voltage will be 3300mV with
+    a value of 1 and will increase at a step size of 100mV until a maximum of
+    8000mV.
+
+  - cirrus,ramp-rate : On power up, it affects the time from when the power
+    up sequence begins to the time the audio reaches a full-scale output.
+    On power down, it affects the time from when the power-down sequence
+    begins to when the amplifier disables the PWM outputs. If this property
+    is not set then soft ramping will be disabled and ramp time would be
+    20ms. If this property is set to 0,1,2,3 then ramp times would be 40ms,
+    60ms,100ms,175ms respectively for 48KHz sample rate.
+
+  - cirrus,boost-ipk : The maximum current allowed for the boost converter.
+    The range starts at 1850000uA and goes to a maximum of 3600000uA
+    with a step size of 15625uA. The default is 2500000uA.
+
+  - cirrus,imon-adc-scale : Configures the scaling of data bits from the IMON
+    ADC data word. This property can be set as a value of 0 for bits 15 down
+    to 0, 6 for 21 down to 6, 7, for 22 down to 7, 8 for 23 down to 8.
+
+
+Optional H/G Algorithm sub-node:
+
+The cs35l33 node can have a single "cirrus,hg-algo" sub-node that will enable
+the internal H/G Algorithm.
+
+  - cirrus,hg-algo : Sub-node for internal Class H/G algorithm that
+    controls the amplifier supplies.
+
+Optional properties for the "cirrus,hg-algo" sub-node:
+
+  - cirrus,mem-depth : Memory depth for the Class H/G algorithm measured in
+    LRCLK cycles. If this property is set to 0, 1, 2, or 3 then the memory
+    depths will be 1, 4, 8, 16 LRCLK cycles.  The default is 16 LRCLK cycles.
+
+    cirrus,release-rate : The number of consecutive LRCLK periods before
+    allowing release condition tracking updates. The number of LRCLK periods
+    start at 3 to a maximum of 255.
+
+  - cirrus,ldo-thld : Configures the signal threshold at which the PWM output
+    stage enters LDO operation. Starts as a default value of 50mV for a value
+    of 1 and increases with a step size of 50mV to a maximum of 750mV (value of
+    0xF).
+
+  - cirrus,ldo-path-disable : This is a boolean property. If present, the H/G
+    algorithm uses the max detection path.  If not present, the LDO
+    detection path is used.
+
+  - cirrus,ldo-entry-delay : The LDO entry delay in milliseconds before the H/G
+    algorithm switches to the LDO voltage.  This property can be set to values
+    from 0 to 7 for delays of 5ms, 10ms, 50ms, 100ms, 200ms, 500ms, 1000ms.
+    The default is 100ms.
+
+  - cirrus,vp-hg-auto : This is a boolean property.  When set, class H/G VPhg
+    automatic updating is enabled.
+
+  - cirrus,vp-hg :  Class H/G algorithm VPhg.  Controls the H/G algorithm's
+    reference to the VP voltage for when to start generating a boosted VBST.
+    The reference voltage starts at 3000mV with a value of 0x3 and is increased
+    by 100mV per step to a maximum of 5500mV.
+
+  - cirrus,vp-hg-rate : The rate (number of LRCLK periods) at which the VPhg is
+    allowed to increase to a higher voltage when using VPhg automatic
+    tracking. This property can be set to values from 0 to 3 with rates of 128
+    periods, 2048 periods, 32768 periods, and 524288 periods.
+    The default is 32768 periods.
+
+  - cirrus,vp-hg-va : VA calculation reference for automatic VPhg tracking
+    using VPMON. This property can be set to values from 0 to 6 starting at
+    1800mV with a step size of 50mV up to a maximum value of 1750mV.
+    Default is 1800mV.
+
+Example:
+
+cs35l33: cs35l33@40 {
+       compatible = "cirrus,cs35l33";
+       reg = <0x40>;
+
+       VA-supply = <&ldo5_reg>;
+       VP-supply = <&ldo5_reg>;
+
+       interrupt-parent = <&gpio8>;
+       interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
+
+       reset-gpios = <&cs47l91 34 0>;
+
+       cirrus,ramp-rate = <0x0>;
+       cirrus,boost-ctl = <0x30>;  /* VBST = 8000mV */
+       cirrus,boost-ipk = <0xE0>; /* 3600mA */
+       cirrus,imon-adc-scale = <0> /* Bits 15 down to 0 */
+
+       cirrus,hg-algo {
+               cirrus,mem-depth = <0x3>;
+               cirrus,release-rate = <0x3>;
+               cirrus,ldo-thld = <0x1>;
+               cirrus,ldo-path-disable = <0x0>;
+               cirrus,ldo-entry-delay=<0x4>;
+               cirrus,vp-hg-auto;
+               cirrus,vp-hg=<0xF>;
+               cirrus,vp-hg-rate=<0x2>;
+               cirrus,vp-hg-va=<0x0>;
+       };
+};
diff --git a/Documentation/devicetree/bindings/sound/cs53l30.txt b/Documentation/devicetree/bindings/sound/cs53l30.txt
new file mode 100644 (file)
index 0000000..4dbfb82
--- /dev/null
@@ -0,0 +1,44 @@
+CS53L30 audio CODEC
+
+Required properties:
+
+  - compatible : "cirrus,cs53l30"
+
+  - reg : the I2C address of the device
+
+  - VA-supply, VP-supply : power supplies for the device,
+    as covered in Documentation/devicetree/bindings/regulator/regulator.txt.
+
+Optional properties:
+
+  - reset-gpios : a GPIO spec for the reset pin.
+
+  - mute-gpios : a GPIO spec for the MUTE pin. The active state can be either
+                GPIO_ACTIVE_HIGH or GPIO_ACTIVE_LOW, which would be handled
+                by the driver automatically.
+
+  - cirrus,micbias-lvl : Set the output voltage level on the MICBIAS Pin.
+                        0 = Hi-Z
+                        1 = 1.80 V
+                        2 = 2.75 V
+
+  - cirrus,use-sdout2 : This is a boolean property. If present, it indicates
+                       the hardware design connects both SDOUT1 and SDOUT2
+                       pins to output data. Otherwise, it indicates that
+                       only SDOUT1 is connected for data output.
+                       * CS53l30 supports 4-channel data output in the same
+                       * frame using two different ways:
+                       * 1) Normal I2S mode on two data pins -- each SDOUT
+                       *    carries 2-channel data in the same time.
+                       * 2) TDM mode on one signle data pin -- SDOUT1 carries
+                       *    4-channel data per frame.
+
+Example:
+
+codec: cs53l30@48 {
+       compatible = "cirrus,cs53l30";
+       reg = <0x48>;
+       reset-gpios = <&gpio 54 0>;
+       VA-supply = <&cs53l30_va>;
+       VP-supply = <&cs53l30_vp>;
+};
index 7bb54247f8e8df95e22e319b83adc262cda83563..6a536d570e294bb662610b392f5fd4866af18223 100644 (file)
@@ -12,6 +12,10 @@ Required properties:
    one for receive.
  - dma-names : "tx" for the transmit channel, "rx" for the receive channel.
 
+Optional properties:
+ - interrupts: The interrupt line number for the I2S controller. Add this
+   parameter if the I2S controller that you are using does not support DMA.
+
 For more details on the 'dma', 'dma-names', 'clock' and 'clock-names'
 properties please check:
        * resource-names.txt
index ceaef512698967ff16677b3771d2fd8c5227aaac..f749e2744824d8273bfe22a1592fa50803b4e51d 100644 (file)
@@ -58,7 +58,7 @@ Required properties:
                           * DMIC (stands for Digital Microphone Jack)
 
                          Note: The "Mic Jack" and "AMIC" are redundant while
-                               coexsiting in order to support the old bindings
+                               coexisting in order to support the old bindings
                                of wm8962 and sgtl5000.
 
 Optional properties:
diff --git a/Documentation/devicetree/bindings/sound/max98504.txt b/Documentation/devicetree/bindings/sound/max98504.txt
new file mode 100644 (file)
index 0000000..583ed5f
--- /dev/null
@@ -0,0 +1,44 @@
+Maxim MAX98504 class D mono speaker amplifier
+
+This device supports I2C control interface and an IRQ output signal. It features
+a PCM and PDM digital audio interface (DAI) and a differential analog input.
+
+Required properties:
+
+ - compatible : "maxim,max98504"
+ - reg : should contain the I2C slave device address
+ - DVDD-supply, DIOVDD-supply, PVDD-supply: power supplies for the device,
+   as covered in ../regulator/regulator.txt
+ - interrupts : should specify the interrupt line the device is connected to,
+   as described in ../interrupt-controller/interrupts.txt
+
+Optional properties:
+
+ - maxim,brownout-threshold - the PVDD brownout threshold, the value must be
+   from 0, 1...21 range, corresponding to 2.6V, 2.65V...3.65V voltage range
+ - maxim,brownout-attenuation - the brownout attenuation to the speaker gain
+   applied during the "attack hold" and "timed hold" phase, the value must be
+   from 0...6 (dB) range
+ - maxim,brownout-attack-hold-ms - the brownout attack hold phase time in ms,
+   0...255 (VBATBROWN_ATTK_HOLD, register 0x0018)
+ - maxim,brownout-timed-hold-ms - the brownout timed hold phase time in ms,
+   0...255 (VBATBROWN_TIME_HOLD, register 0x0019)
+ - maxim,brownout-release-rate-ms - the brownout release phase step time in ms,
+   0...255 (VBATBROWN_RELEASE, register 0x001A)
+
+The default value when the above properties are not specified is 0,
+the maxim,brownout-threshold property must be specified to actually enable
+the PVDD brownout protection.
+
+Example:
+
+ max98504@31 {
+       compatible = "maxim,max98504";
+       reg = <0x31>;
+       interrupt-parent = <&gpio_bank_0>;
+       interrupts = <2 0>;
+
+       DVDD-supply = <&regulator>;
+       DIOVDD-supply = <&regulator>;
+       PVDD-supply = <&regulator>;
+};
diff --git a/Documentation/devicetree/bindings/sound/max9860.txt b/Documentation/devicetree/bindings/sound/max9860.txt
new file mode 100644 (file)
index 0000000..e0d4e95
--- /dev/null
@@ -0,0 +1,28 @@
+MAX9860 Mono Audio Voice Codec
+
+Required properties:
+
+  - compatible : "maxim,max9860"
+
+  - reg : the I2C address of the device
+
+  - AVDD-supply, DVDD-supply and DVDDIO-supply : power supplies for
+    the device, as covered in bindings/regulator/regulator.txt
+
+  - clock-names : Required element: "mclk".
+
+  - clocks : A clock specifier for the clock connected as MCLK.
+
+Examples:
+
+       max9860: max9860@10 {
+               compatible = "maxim,max9860";
+               reg = <0x10>;
+
+               AVDD-supply = <&reg_1v8>;
+               DVDD-supply = <&reg_1v8>;
+               DVDDIO-supply = <&reg_3v0>;
+
+               clock-names = "mclk";
+               clocks = <&pck2>;
+       };
diff --git a/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt b/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
new file mode 100644 (file)
index 0000000..3e623a7
--- /dev/null
@@ -0,0 +1,150 @@
+Mediatek AFE PCM controller for mt2701
+
+Required properties:
+- compatible = "mediatek,mt2701-audio";
+- reg: register location and size
+- interrupts: Should contain AFE interrupt
+- clock-names: should have these clock names:
+               "infra_sys_audio_clk",
+               "top_audio_mux1_sel",
+               "top_audio_mux2_sel",
+               "top_audio_mux1_div",
+               "top_audio_mux2_div",
+               "top_audio_48k_timing",
+               "top_audio_44k_timing",
+               "top_audpll_mux_sel",
+               "top_apll_sel",
+               "top_aud1_pll_98M",
+               "top_aud2_pll_90M",
+               "top_hadds2_pll_98M",
+               "top_hadds2_pll_294M",
+               "top_audpll",
+               "top_audpll_d4",
+               "top_audpll_d8",
+               "top_audpll_d16",
+               "top_audpll_d24",
+               "top_audintbus_sel",
+               "clk_26m",
+               "top_syspll1_d4",
+               "top_aud_k1_src_sel",
+               "top_aud_k2_src_sel",
+               "top_aud_k3_src_sel",
+               "top_aud_k4_src_sel",
+               "top_aud_k5_src_sel",
+               "top_aud_k6_src_sel",
+               "top_aud_k1_src_div",
+               "top_aud_k2_src_div",
+               "top_aud_k3_src_div",
+               "top_aud_k4_src_div",
+               "top_aud_k5_src_div",
+               "top_aud_k6_src_div",
+               "top_aud_i2s1_mclk",
+               "top_aud_i2s2_mclk",
+               "top_aud_i2s3_mclk",
+               "top_aud_i2s4_mclk",
+               "top_aud_i2s5_mclk",
+               "top_aud_i2s6_mclk",
+               "top_asm_m_sel",
+               "top_asm_h_sel",
+               "top_univpll2_d4",
+               "top_univpll2_d2",
+               "top_syspll_d5";
+
+Example:
+
+       afe: mt2701-afe-pcm@11220000 {
+               compatible = "mediatek,mt2701-audio";
+               reg = <0 0x11220000 0 0x2000>,
+                     <0 0x112A0000 0 0x20000>;
+               interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_LOW>,
+                            <GIC_SPI 132 IRQ_TYPE_LEVEL_LOW>;
+               clocks = <&infracfg CLK_INFRA_AUDIO>,
+                        <&topckgen CLK_TOP_AUD_MUX1_SEL>,
+                        <&topckgen CLK_TOP_AUD_MUX2_SEL>,
+                        <&topckgen CLK_TOP_AUD_MUX1_DIV>,
+                        <&topckgen CLK_TOP_AUD_MUX2_DIV>,
+                        <&topckgen CLK_TOP_AUD_48K_TIMING>,
+                        <&topckgen CLK_TOP_AUD_44K_TIMING>,
+                        <&topckgen CLK_TOP_AUDPLL_MUX_SEL>,
+                        <&topckgen CLK_TOP_APLL_SEL>,
+                        <&topckgen CLK_TOP_AUD1PLL_98M>,
+                        <&topckgen CLK_TOP_AUD2PLL_90M>,
+                        <&topckgen CLK_TOP_HADDS2PLL_98M>,
+                        <&topckgen CLK_TOP_HADDS2PLL_294M>,
+                        <&topckgen CLK_TOP_AUDPLL>,
+                        <&topckgen CLK_TOP_AUDPLL_D4>,
+                        <&topckgen CLK_TOP_AUDPLL_D8>,
+                        <&topckgen CLK_TOP_AUDPLL_D16>,
+                        <&topckgen CLK_TOP_AUDPLL_D24>,
+                        <&topckgen CLK_TOP_AUDINTBUS_SEL>,
+                        <&clk26m>,
+                        <&topckgen CLK_TOP_SYSPLL1_D4>,
+                        <&topckgen CLK_TOP_AUD_K1_SRC_SEL>,
+                        <&topckgen CLK_TOP_AUD_K2_SRC_SEL>,
+                        <&topckgen CLK_TOP_AUD_K3_SRC_SEL>,
+                        <&topckgen CLK_TOP_AUD_K4_SRC_SEL>,
+                        <&topckgen CLK_TOP_AUD_K5_SRC_SEL>,
+                        <&topckgen CLK_TOP_AUD_K6_SRC_SEL>,
+                        <&topckgen CLK_TOP_AUD_K1_SRC_DIV>,
+                        <&topckgen CLK_TOP_AUD_K2_SRC_DIV>,
+                        <&topckgen CLK_TOP_AUD_K3_SRC_DIV>,
+                        <&topckgen CLK_TOP_AUD_K4_SRC_DIV>,
+                        <&topckgen CLK_TOP_AUD_K5_SRC_DIV>,
+                        <&topckgen CLK_TOP_AUD_K6_SRC_DIV>,
+                        <&topckgen CLK_TOP_AUD_I2S1_MCLK>,
+                        <&topckgen CLK_TOP_AUD_I2S2_MCLK>,
+                        <&topckgen CLK_TOP_AUD_I2S3_MCLK>,
+                        <&topckgen CLK_TOP_AUD_I2S4_MCLK>,
+                        <&topckgen CLK_TOP_AUD_I2S5_MCLK>,
+                        <&topckgen CLK_TOP_AUD_I2S6_MCLK>,
+                        <&topckgen CLK_TOP_ASM_M_SEL>,
+                        <&topckgen CLK_TOP_ASM_H_SEL>,
+                        <&topckgen CLK_TOP_UNIVPLL2_D4>,
+                        <&topckgen CLK_TOP_UNIVPLL2_D2>,
+                        <&topckgen CLK_TOP_SYSPLL_D5>;
+
+               clock-names = "infra_sys_audio_clk",
+                             "top_audio_mux1_sel",
+                             "top_audio_mux2_sel",
+                             "top_audio_mux1_div",
+                             "top_audio_mux2_div",
+                             "top_audio_48k_timing",
+                             "top_audio_44k_timing",
+                             "top_audpll_mux_sel",
+                             "top_apll_sel",
+                             "top_aud1_pll_98M",
+                             "top_aud2_pll_90M",
+                             "top_hadds2_pll_98M",
+                             "top_hadds2_pll_294M",
+                             "top_audpll",
+                             "top_audpll_d4",
+                             "top_audpll_d8",
+                             "top_audpll_d16",
+                             "top_audpll_d24",
+                             "top_audintbus_sel",
+                             "clk_26m",
+                             "top_syspll1_d4",
+                             "top_aud_k1_src_sel",
+                             "top_aud_k2_src_sel",
+                             "top_aud_k3_src_sel",
+                             "top_aud_k4_src_sel",
+                             "top_aud_k5_src_sel",
+                             "top_aud_k6_src_sel",
+                             "top_aud_k1_src_div",
+                             "top_aud_k2_src_div",
+                             "top_aud_k3_src_div",
+                             "top_aud_k4_src_div",
+                             "top_aud_k5_src_div",
+                             "top_aud_k6_src_div",
+                             "top_aud_i2s1_mclk",
+                             "top_aud_i2s2_mclk",
+                             "top_aud_i2s3_mclk",
+                             "top_aud_i2s4_mclk",
+                             "top_aud_i2s5_mclk",
+                             "top_aud_i2s6_mclk",
+                             "top_asm_m_sel",
+                             "top_asm_h_sel",
+                             "top_univpll2_d4",
+                             "top_univpll2_d2",
+                             "top_syspll_d5";
+       };
diff --git a/Documentation/devicetree/bindings/sound/mt2701-cs42448.txt b/Documentation/devicetree/bindings/sound/mt2701-cs42448.txt
new file mode 100644 (file)
index 0000000..0557444
--- /dev/null
@@ -0,0 +1,43 @@
+MT2701 with CS42448 CODEC
+
+Required properties:
+- compatible: "mediatek,mt2701-cs42448-machine"
+- mediatek,platform: the phandle of MT2701 ASoC platform
+- audio-routing: a list of the connections between audio
+- mediatek,audio-codec: the phandles of cs42448 codec
+- mediatek,audio-codec-bt-mrg the phandles of bt-sco dummy codec
+- pinctrl-names: Should contain only one value - "default"
+- pinctrl-0: Should specify pin control groups used for this controller.
+- i2s1-in-sel-gpio1, i2s1-in-sel-gpio2: Should specify two gpio pins to
+                                       control I2S1-in mux.
+
+Example:
+
+       sound:sound {
+               compatible = "mediatek,mt2701-cs42448-machine";
+               mediatek,platform = <&afe>;
+               /* CS42448 Machine name */
+               audio-routing =
+                       "Line Out Jack", "AOUT1L",
+                       "Line Out Jack", "AOUT1R",
+                       "Line Out Jack", "AOUT2L",
+                       "Line Out Jack", "AOUT2R",
+                       "Line Out Jack", "AOUT3L",
+                       "Line Out Jack", "AOUT3R",
+                       "Line Out Jack", "AOUT4L",
+                       "Line Out Jack", "AOUT4R",
+                       "AIN1L", "AMIC",
+                       "AIN1R", "AMIC",
+                       "AIN2L", "Tuner In",
+                       "AIN2R", "Tuner In",
+                       "AIN3L", "Satellite Tuner In",
+                       "AIN3R", "Satellite Tuner In",
+                       "AIN3L", "AUX In",
+                       "AIN3R", "AUX In";
+               mediatek,audio-codec = <&cs42448>;
+               mediatek,audio-codec-bt-mrg = <&bt_sco_codec>;
+               pinctrl-names = "default";
+               pinctrl-0 = <&aud_pins_default>;
+               i2s1-in-sel-gpio1 = <&pio 53 0>;
+               i2s1-in-sel-gpio2 = <&pio 54 0>;
+       };
index 5bfa6b60530bdcaab86e9a7cd2266cbf04f1e0a0..29dce2ac8773a9e57387647d23bfbca4bfe2709e 100644 (file)
@@ -1,8 +1,9 @@
-MT8173 with RT5650 CODECS
+MT8173 with RT5650 CODECS and HDMI via I2S
 
 Required properties:
 - compatible : "mediatek,mt8173-rt5650"
 - mediatek,audio-codec: the phandles of rt5650 codecs
+                        and of the hdmi encoder node
 - mediatek,platform: the phandle of MT8173 ASoC platform
 
 Optional subnodes:
@@ -12,12 +13,17 @@ Required codec-capture subnode properties:
   <&rt5650 0> : Default setting. Connect rt5650 I2S1 for capture. (dai_name = rt5645-aif1)
   <&rt5650 1> : Connect rt5650 I2S2 for capture. (dai_name = rt5645-aif2)
 
+- mediatek,mclk: the MCLK source
+  0 : external oscillator, MCLK = 12.288M
+  1 : internal source from mt8173, MCLK = sampling rate*256
+
 Example:
 
        sound {
                compatible = "mediatek,mt8173-rt5650";
-               mediatek,audio-codec = <&rt5650>;
+               mediatek,audio-codec = <&rt5650 &hdmi0>;
                mediatek,platform = <&afe>;
+               mediatek,mclk = <0>;
                codec-capture {
                        sound-dai = <&rt5650 1>;
                };
index 0741dff048dd39ba3336f67c6186918c306a563f..6f6c2f8e908db15ff37567413c20b19a5ed68d17 100644 (file)
@@ -8,6 +8,8 @@ Required properties:
 - interrupts: Interrupt number for McPDM
 - interrupt-parent: The parent interrupt controller
 - ti,hwmods: Name of the hwmod associated to the McPDM
+- clocks:  phandle for the pdmclk provider, likely <&twl6040>
+- clock-names: Must be "pdmclk"
 
 Example:
 
@@ -19,3 +21,11 @@ mcpdm: mcpdm@40132000 {
        interrupt-parent = <&gic>;
        ti,hwmods = "mcpdm";
 };
+
+In board DTS file the pdmclk needs to be added:
+
+&mcpdm {
+       clocks = <&twl6040>;
+       clock-names = "pdmclk";
+       status = "okay";
+};
index c7b29df4a96353735f23d75576f284d8f16aa649..15a7316e4c913c837fc8f03358c94a466447fe38 100644 (file)
@@ -373,6 +373,8 @@ Optional properties:
 - #clock-cells                 : it must be 0 if your system has audio_clkout
                                  it must be 1 if your system has audio_clkout0/1/2/3
 - clock-frequency              : for all audio_clkout0/1/2/3
+- clkout-lr-asynchronous       : boolean property. it indicates that audio_clkoutn
+                                 is asynchronizes with lr-clock.
 
 SSI subnode properties:
 - interrupts                   : Should contain SSI interrupt for PIO transfer
index 6e86d8aa29b4639397f51a8e73b8d8ede79c28fe..4ea29aa9af59a86d34577329835b8437e72bd86e 100644 (file)
@@ -23,6 +23,11 @@ Required properties:
 - rockchip,playback-channels: max playback channels, if not set, 8 channels default.
 - rockchip,capture-channels: max capture channels, if not set, 2 channels default.
 
+Required properties for controller which support multi channels
+playback/capture:
+
+- rockchip,grf: the phandle of the syscon node for GRF register.
+
 Example for rk3288 I2S controller:
 
 i2s@ff890000 {
index e24436fc5ea9986ae845cbfc03fff0216ed03d70..9cabfc18cb57be3dfbeca45a828d185b7e80f52c 100644 (file)
@@ -8,6 +8,11 @@ Required properties:
 
 - reg : The I2C address of the device.
 
+Optional properties:
+
+- clocks: The phandle of the master clock to the CODEC
+- clock-names: Should be "mclk"
+
 Pins on the device (for linking into audio routes) for RT5514:
 
   * DMIC1L
diff --git a/Documentation/devicetree/bindings/sound/samsung,odroidx2-max98090.txt b/Documentation/devicetree/bindings/sound/samsung,odroidx2-max98090.txt
deleted file mode 100644 (file)
index 9148f72..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-Samsung Exynos Odroid X2/U3 audio complex with MAX98090 codec
-
-Required properties:
- - compatible : "samsung,odroidx2-audio" - for Odroid X2 board,
-               "samsung,odroidu3-audio" - for Odroid U3 board
- - samsung,model : the user-visible name of this sound complex
- - samsung,i2s-controller : the phandle of the I2S controller
- - samsung,audio-codec : the phandle of the MAX98090 audio codec
- - samsung,audio-routing : a list of the connections between audio
-   components;  each entry is a pair of strings, the first being the
-   connection's sink, the second being the connection's source;
-   valid names for sources and sinks are the MAX98090's pins (as
-   documented in its binding), and the jacks on the board
-   For Odroid X2:
-     * Headphone Jack
-     * Mic Jack
-     * DMIC
-
-   For Odroid U3:
-     * Headphone Jack
-     * Speakers
-
-Example:
-
-sound {
-       compatible = "samsung,odroidu3-audio";
-       samsung,i2s-controller = <&i2s0>;
-       samsung,audio-codec = <&max98090>;
-       samsung,model = "Odroid-X2";
-       samsung,audio-routing =
-               "Headphone Jack", "HPL",
-               "Headphone Jack", "HPR",
-               "IN1", "Mic Jack",
-               "Mic Jack", "MICBIAS";
-};
index 0e5e4eb3ef1bdd9c3b85ccfcd8c81fbbc5455f08..5666da7b86059f39c411a6acebf5b8dc758342ac 100644 (file)
@@ -7,6 +7,14 @@ Required properties:
 
 - clocks : the clock provider of SYS_MCLK
 
+- VDDA-supply : the regulator provider of VDDA
+
+- VDDIO-supply: the regulator provider of VDDIO
+
+Optional properties:
+
+- VDDD-supply : the regulator provider of VDDD
+
 - micbias-resistor-k-ohms : the bias resistor to be used in kOmhs
        The resistor can take values of 2k, 4k or 8k.
        If set to 0 it will be off.
@@ -15,17 +23,9 @@ Required properties:
 
 - micbias-voltage-m-volts : the bias voltage to be used in mVolts
        The voltage can take values from 1.25V to 3V by 250mV steps
-       If this node is not mentionned or the value is unknown, then
+       If this node is not mentioned or the value is unknown, then
        the value is set to 1.25V.
 
-- VDDA-supply : the regulator provider of VDDA
-
-- VDDIO-supply: the regulator provider of VDDIO
-
-Optional properties:
-
-- VDDD-supply : the regulator provider of VDDD
-
 Example:
 
 codec: sgtl5000@0a {
index 4d9a83d9a017cd2fcb69898a0d2df9f7f3585597..16bcdfb6760e83aecae47135180df8b437f6f486 100644 (file)
@@ -33,11 +33,11 @@ Required properties:
        "tx" for "st,sti-uni-player" compatibility
        "rx" for "st,sti-uni-reader" compatibility
 
-  - version: IP version integrated in SOC.
+  - st,version: IP version integrated in SOC.
 
   - dai-name: DAI name that describes the IP.
 
-  - IP mode: IP working mode depending on associated codec.
+  - st,mode: IP working mode depending on associated codec.
        "HDMI" connected to HDMI codec and support IEC HDMI formats (player only).
        "SPDIF" connected to SPDIF codec and support SPDIF formats (player only).
        "PCM" PCM standard mode for I2S or TDM bus.
@@ -47,7 +47,7 @@ Required properties ("st,sti-uni-player" compatibility only):
   - clocks: CPU_DAI IP clock source, listed in the same order than the
            CPU_DAI properties.
 
-  - uniperiph-id: internal SOC IP instance ID.
+  - st,uniperiph-id: internal SOC IP instance ID.
 
 Optional properties:
   - pinctrl-0: defined for CPU_DAI@1 and CPU_DAI@4 to describe I2S PIOs for
@@ -84,9 +84,9 @@ Example:
                dmas = <&fdma0 4 0 1>;
                dai-name = "Uni Player #2 (DAC)";
                dma-names = "tx";
-               uniperiph-id = <2>;
-               version = <5>;
-               mode = "PCM";
+               st,uniperiph-id = <2>;
+               st,version = <5>;
+               st,mode = "PCM";
        };
 
        sti_uni_player3: sti-uni-player@3 {
@@ -100,9 +100,9 @@ Example:
                dmas = <&fdma0 7 0 1>;
                dma-names = "tx";
                dai-name = "Uni Player #3 (SPDIF)";
-               uniperiph-id = <3>;
-               version = <5>;
-               mode = "SPDIF";
+               st,uniperiph-id = <3>;
+               st,version = <5>;
+               st,mode = "SPDIF";
        };
 
        sti_uni_reader1: sti-uni-reader@1 {
@@ -115,7 +115,7 @@ Example:
                dmas = <&fdma0 6 0 1>;
                dma-names = "rx";
                dai-name = "Uni Reader #1 (HDMI RX)";
-               version = <3>;
+               st,version = <3>;
                st,mode = "PCM";
        };
 
diff --git a/Documentation/devicetree/bindings/sound/sun4i-i2s.txt b/Documentation/devicetree/bindings/sound/sun4i-i2s.txt
new file mode 100644 (file)
index 0000000..7b526ec
--- /dev/null
@@ -0,0 +1,34 @@
+* Allwinner A10 I2S controller
+
+The I2S bus (Inter-IC sound bus) is a serial link for digital
+audio data transfer between devices in the system.
+
+Required properties:
+
+- compatible: should be one of the followings
+   - "allwinner,sun4i-a10-i2s"
+- reg: physical base address of the controller and length of memory mapped
+  region.
+- interrupts: should contain the I2S interrupt.
+- dmas: DMA specifiers for tx and rx dma. See the DMA client binding,
+       Documentation/devicetree/bindings/dma/dma.txt
+- dma-names: should include "tx" and "rx".
+- clocks: a list of phandle + clock-specifer pairs, one for each entry in clock-names.
+- clock-names: should contain followings:
+   - "apb" : clock for the I2S bus interface
+   - "mod" : module clock for the I2S controller
+- #sound-dai-cells : Must be equal to 0
+
+Example:
+
+i2s0: i2s@01c22400 {
+       #sound-dai-cells = <0>;
+       compatible = "allwinner,sun4i-a10-i2s";
+       reg = <0x01c22400 0x400>;
+       interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+       clocks = <&apb0_gates 3>, <&i2s0_clk>;
+       clock-names = "apb", "mod";
+       dmas = <&dma SUN4I_DMA_NORMAL 3>,
+              <&dma SUN4I_DMA_NORMAL 3>;
+       dma-names = "rx", "tx";
+};
index 4ab7d43d07544f7a22b35e49f9eb691eb3e9204d..7050ce8794b9a4b3dd93b76dd9e2a6d708b468ee 100644 (file)
@@ -139,27 +139,6 @@ Examples of using the Linux-provided gdb helpers
       start_comm = "swapper/2\000\000\000\000\000\000"
     }
 
- o Dig into a radix tree data structure, such as the IRQ descriptors:
-    (gdb) print (struct irq_desc)$lx_radix_tree_lookup(irq_desc_tree, 18)
-    $6 = {
-      irq_common_data = {
-        state_use_accessors = 67584,
-        handler_data = 0x0 <__vectors_start>,
-        msi_desc = 0x0 <__vectors_start>,
-        affinity = {{
-            bits = {65535}
-          }}
-      },
-      irq_data = {
-        mask = 0,
-        irq = 18,
-        hwirq = 27,
-        common = 0xee803d80,
-        chip = 0xc0eb0854 <gic_data>,
-        domain = 0xee808000,
-        parent_data = 0x0 <__vectors_start>,
-        chip_data = 0xc0eb0854 <gic_data>
-      } <... trimmed ...>
 
 List of commands and functions
 ------------------------------
index 74056dba52be7aab7adbcadd45064fa71d79534f..6bf2d2063b52418322a930a5c5e3dfb3d103503a 100644 (file)
@@ -3,7 +3,7 @@ ASoC Machine Driver
 
 The ASoC machine (or board) driver is the code that glues together all the
 component drivers (e.g. codecs, platforms and DAIs). It also describes the
-relationships between each componnent which include audio paths, GPIOs,
+relationships between each component which include audio paths, GPIOs,
 interrupts, clocking, jacks and voltage regulators.
 
 The machine driver can contain codec and platform specific code. It registers
index 1a5a12184a358dc395874447ccc3c502d67f567b..85d0549ad84636652d1867d7d0a58c7ed1cc3c19 100644 (file)
@@ -45,7 +45,7 @@ is how we expect the compiler, application and kernel to work together.
    MPX-instrumented.
 3) The kernel detects that the CPU has MPX, allows the new prctl() to
    succeed, and notes the location of the bounds directory. Userspace is
-   expected to keep the bounds directory at that locationWe note it
+   expected to keep the bounds directory at that locationWe note it
    instead of reading it each time because the 'xsave' operation needed
    to access the bounds directory register is an expensive operation.
 4) If the application needs to spill bounds out of the 4 registers, it
@@ -167,7 +167,7 @@ If a #BR is generated due to a bounds violation caused by MPX.
 We need to decode MPX instructions to get violation address and
 set this address into extended struct siginfo.
 
-The _sigfault feild of struct siginfo is extended as follow:
+The _sigfault field of struct siginfo is extended as follow:
 
 87             /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
 88             struct {
@@ -240,5 +240,5 @@ them at the same bounds table.
 This is allowed architecturally.  See more information "Intel(R) Architecture
 Instruction Set Extensions Programming Reference" (9.3.4).
 
-However, if users did this, the kernel might be fooled in to unmaping an
+However, if users did this, the kernel might be fooled in to unmapping an
 in-use bounds table since it does not recognize sharing.
index 39d1723267036a5ba7dff02dc82e39d69a1eeae8..6a0607b99ed8780c45920676e9b122efefa77235 100644 (file)
@@ -5,7 +5,7 @@ memory, it has two choices:
     from areas other than the one we are trying to flush will be
     destroyed and must be refilled later, at some cost.
  2. Use the invlpg instruction to invalidate a single page at a
-    time.  This could potentialy cost many more instructions, but
+    time.  This could potentially cost many more instructions, but
     it is a much more precise operation, causing no collateral
     damage to other TLB entries.
 
@@ -19,7 +19,7 @@ Which method to do depends on a few things:
     work.
  3. The size of the TLB.  The larger the TLB, the more collateral
     damage we do with a full flush.  So, the larger the TLB, the
-    more attrative an individual flush looks.  Data and
+    more attractive an individual flush looks.  Data and
     instructions have separate TLBs, as do different page sizes.
  4. The microarchitecture.  The TLB has become a multi-level
     cache on modern CPUs, and the global flushes have become more
index b1fb30273286c7ae6878f6b43fecb6df8f8f1603..d0648a74fceb50659d2eb018d47178619636b74e 100644 (file)
@@ -36,7 +36,7 @@ between all CPUs.
 
 check_interval
        How often to poll for corrected machine check errors, in seconds
-       (Note output is hexademical). Default 5 minutes.  When the poller
+       (Note output is hexadecimal). Default 5 minutes.  When the poller
        finds MCEs it triggers an exponential speedup (poll more often) on
        the polling interval.  When the poller stops finding MCEs, it
        triggers an exponential backoff (poll less often) on the polling
index 952fd2aba7b7f15119ab5371521c13d7804f5ae0..75eab376e487bf6504283e90770d3b6f6001a586 100644 (file)
@@ -595,6 +595,10 @@ S: Odd Fixes
 L:     linux-alpha@vger.kernel.org
 F:     arch/alpha/
 
+ALPS PS/2 TOUCHPAD DRIVER
+R:     Pali Rohár <pali.rohar@gmail.com>
+F:     drivers/input/mouse/alps.*
+
 ALTERA MAILBOX DRIVER
 M:     Ley Foon Tan <lftan@altera.com>
 L:     nios2-dev@lists.rocketboards.org (moderated for non-subscribers)
@@ -1604,7 +1608,6 @@ F:        drivers/*/*/*s3c2410*
 F:     drivers/memory/samsung/*
 F:     drivers/soc/samsung/*
 F:     drivers/spi/spi-s3c*
-F:     sound/soc/samsung/*
 F:     Documentation/arm/Samsung/
 F:     Documentation/devicetree/bindings/arm/samsung/
 F:     Documentation/devicetree/bindings/sram/samsung-sram.txt
@@ -1690,8 +1693,6 @@ S:        Maintained
 F:     drivers/edac/altera_edac.
 
 ARM/STI ARCHITECTURE
-M:     Srinivas Kandagatla <srinivas.kandagatla@gmail.com>
-M:     Maxime Coquelin <maxime.coquelin@st.com>
 M:     Patrice Chotard <patrice.chotard@st.com>
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 L:     kernel@stlinux.com
@@ -1724,6 +1725,7 @@ F:        drivers/ata/ahci_st.c
 
 ARM/STM32 ARCHITECTURE
 M:     Maxime Coquelin <mcoquelin.stm32@gmail.com>
+M:     Alexandre Torgue <alexandre.torgue@st.com>
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:     Maintained
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/mcoquelin/stm32.git
@@ -4473,7 +4475,7 @@ S:        Orphan
 F:     fs/efs/
 
 EHEA (IBM pSeries eHEA 10Gb ethernet adapter) DRIVER
-M:     Thadeu Lima de Souza Cascardo <cascardo@linux.vnet.ibm.com>
+M:     Douglas Miller <dougmill@linux.vnet.ibm.com>
 L:     netdev@vger.kernel.org
 S:     Maintained
 F:     drivers/net/ethernet/ibm/ehea/
@@ -7267,6 +7269,13 @@ F:       Documentation/devicetree/bindings/i2c/max6697.txt
 F:     drivers/hwmon/max6697.c
 F:     include/linux/platform_data/max6697.h
 
+MAX9860 MONO AUDIO VOICE CODEC DRIVER
+M:     Peter Rosin <peda@axentia.se>
+L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
+S:     Maintained
+F:     Documentation/devicetree/bindings/sound/max9860.txt
+F:     sound/soc/codecs/max9860.*
+
 MAXIM MUIC CHARGER DRIVERS FOR EXYNOS BASED BOARDS
 M:     Krzysztof Kozlowski <k.kozlowski@samsung.com>
 L:     linux-pm@vger.kernel.org
@@ -7420,7 +7429,7 @@ F:        drivers/scsi/megaraid.*
 F:     drivers/scsi/megaraid/
 
 MELLANOX ETHERNET DRIVER (mlx4_en)
-M:     Eugenia Emantayev <eugenia@mellanox.com>
+M:     Tariq Toukan <tariqt@mellanox.com>
 L:     netdev@vger.kernel.org
 S:     Supported
 W:     http://www.mellanox.com
@@ -7472,6 +7481,7 @@ Q:        http://patchwork.ozlabs.org/project/linux-mtd/list/
 T:     git git://git.infradead.org/linux-mtd.git
 T:     git git://git.infradead.org/l2-mtd.git
 S:     Maintained
+F:     Documentation/devicetree/bindings/mtd/
 F:     drivers/mtd/
 F:     include/linux/mtd/
 F:     include/uapi/mtd/
@@ -8959,6 +8969,7 @@ L:        linux-gpio@vger.kernel.org
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl.git
 S:     Maintained
 F:     Documentation/devicetree/bindings/pinctrl/
+F:     Documentation/pinctrl.txt
 F:     drivers/pinctrl/
 F:     include/linux/pinctrl/
 
@@ -9891,7 +9902,9 @@ S:        Maintained
 F:     drivers/platform/x86/samsung-laptop.c
 
 SAMSUNG AUDIO (ASoC) DRIVERS
+M:     Krzysztof Kozlowski <k.kozlowski@samsung.com>
 M:     Sangbeom Kim <sbkim73@samsung.com>
+M:     Sylwester Nawrocki <s.nawrocki@samsung.com>
 L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
 S:     Supported
 F:     sound/soc/samsung/
@@ -10719,6 +10732,7 @@ T:      git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
 L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
 W:     http://alsa-project.org/main/index.php/ASoC
 S:     Supported
+F:     Documentation/devicetree/bindings/sound/
 F:     Documentation/sound/alsa/soc/
 F:     sound/soc/
 F:     include/sound/soc*
index 6471f20ca4009639995023471ae95a09e8364701..66da9a38b13b772ec11551fb7b17be007ac643d8 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 4
 PATCHLEVEL = 7
 SUBLEVEL = 0
-EXTRAVERSION = -rc5
+EXTRAVERSION =
 NAME = Psychotic Stoned Sheep
 
 # *DOCUMENTATION*
@@ -363,11 +363,13 @@ CHECK             = sparse
 
 CHECKFLAGS     := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \
                  -Wbitwise -Wno-return-void $(CF)
+NOSTDINC_FLAGS  =
 CFLAGS_MODULE   =
 AFLAGS_MODULE   =
 LDFLAGS_MODULE  =
 CFLAGS_KERNEL  =
 AFLAGS_KERNEL  =
+LDFLAGS_vmlinux =
 CFLAGS_GCOV    = -fprofile-arcs -ftest-coverage -fno-tree-loop-im -Wno-maybe-uninitialized
 CFLAGS_KCOV    = -fsanitize-coverage=trace-pc
 
index d4df6be66d583d8708585013c07020fbde28b658..85814e74677d54209908d41025fc329d159d2902 100644 (file)
@@ -66,8 +66,6 @@ endif
 
 endif
 
-cflags-$(CONFIG_ARC_DW2_UNWIND)                += -fasynchronous-unwind-tables
-
 # By default gcc 4.8 generates dwarf4 which kernel unwinder can't grok
 ifeq ($(atleast_gcc48),y)
 cflags-$(CONFIG_ARC_DW2_UNWIND)                += -gdwarf-2
index e0efff15a5aece29f9bf8ca45b95f856fc206eab..b9192a653b7e3a2a2a34c8ddaed1dfad037f30c0 100644 (file)
@@ -142,7 +142,7 @@ arc_unwind_core(struct task_struct *tsk, struct pt_regs *regs,
         * prelogue is setup (callee regs saved and then fp set and not other
         * way around
         */
-       pr_warn("CONFIG_ARC_DW2_UNWIND needs to be enabled\n");
+       pr_warn_once("CONFIG_ARC_DW2_UNWIND needs to be enabled\n");
        return 0;
 
 #endif
index 8450944b28e6bb7728ccd731dcb709fa7346aa28..22f7a13e20b40e094611a10953180fd1679636cd 100644 (file)
@@ -58,8 +58,8 @@ memory {
        soc {
                ranges = <MBUS_ID(0xf0, 0x01) 0 0xf1000000 0x100000
                          MBUS_ID(0x01, 0x1d) 0 0xfff00000 0x100000
-                         MBUS_ID(0x09, 0x09) 0 0xf1100000 0x10000
-                         MBUS_ID(0x09, 0x05) 0 0xf1110000 0x10000>;
+                         MBUS_ID(0x09, 0x19) 0 0xf1100000 0x10000
+                         MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000>;
 
                internal-regs {
 
index a03e56fb5dbc7882abb90fc71298bac1c3e8a56b..ca58eb279d5541631bcc3dea5e56ef87797071ea 100644 (file)
@@ -65,8 +65,9 @@ framebuffer@0 {
                        compatible = "allwinner,simple-framebuffer",
                                     "simple-framebuffer";
                        allwinner,pipeline = "de_be0-lcd0-hdmi";
-                       clocks = <&pll5 1>, <&ahb_gates 36>, <&ahb_gates 43>,
-                                <&ahb_gates 44>, <&dram_gates 26>;
+                       clocks = <&pll3>, <&pll5 1>, <&ahb_gates 36>,
+                                <&ahb_gates 43>, <&ahb_gates 44>,
+                                <&dram_gates 26>;
                        status = "disabled";
                };
 
@@ -74,8 +75,9 @@ framebuffer@1 {
                        compatible = "allwinner,simple-framebuffer",
                                     "simple-framebuffer";
                        allwinner,pipeline = "de_fe0-de_be0-lcd0-hdmi";
-                       clocks = <&pll5 1>, <&ahb_gates 36>, <&ahb_gates 43>,
-                                <&ahb_gates 44>, <&ahb_gates 46>,
+                       clocks = <&pll3>, <&pll5 1>, <&ahb_gates 36>,
+                                <&ahb_gates 43>, <&ahb_gates 44>,
+                                <&ahb_gates 46>,
                                 <&dram_gates 25>, <&dram_gates 26>;
                        status = "disabled";
                };
@@ -84,9 +86,9 @@ framebuffer@2 {
                        compatible = "allwinner,simple-framebuffer",
                                     "simple-framebuffer";
                        allwinner,pipeline = "de_fe0-de_be0-lcd0";
-                       clocks = <&pll5 1>, <&ahb_gates 36>, <&ahb_gates 44>,
-                                <&ahb_gates 46>, <&dram_gates 25>,
-                                <&dram_gates 26>;
+                       clocks = <&pll3>, <&pll5 1>, <&ahb_gates 36>,
+                                <&ahb_gates 44>, <&ahb_gates 46>,
+                                <&dram_gates 25>, <&dram_gates 26>;
                        status = "disabled";
                };
 
@@ -94,8 +96,9 @@ framebuffer@3 {
                        compatible = "allwinner,simple-framebuffer",
                                     "simple-framebuffer";
                        allwinner,pipeline = "de_fe0-de_be0-lcd0-tve0";
-                       clocks = <&pll5 1>, <&ahb_gates 34>, <&ahb_gates 36>,
-                                <&ahb_gates 44>, <&ahb_gates 46>,
+                       clocks = <&pll3>, <&pll5 1>, <&ahb_gates 34>,
+                                <&ahb_gates 36>, <&ahb_gates 44>,
+                                <&ahb_gates 46>,
                                 <&dram_gates 5>, <&dram_gates 25>, <&dram_gates 26>;
                        status = "disabled";
                };
index bddd0de88af6be1d3e68b027b644a56e5e0ee61b..367f3301249364e7ad47e4c2e594ebfe6ecd88fc 100644 (file)
@@ -65,8 +65,8 @@ framebuffer@0 {
                        compatible = "allwinner,simple-framebuffer",
                                     "simple-framebuffer";
                        allwinner,pipeline = "de_be0-lcd0-hdmi";
-                       clocks = <&pll5 1>, <&ahb_gates 36>, <&ahb_gates 43>,
-                                <&ahb_gates 44>;
+                       clocks = <&pll3>, <&pll5 1>, <&ahb_gates 36>,
+                                <&ahb_gates 43>, <&ahb_gates 44>;
                        status = "disabled";
                };
 
@@ -74,7 +74,8 @@ framebuffer@1 {
                        compatible = "allwinner,simple-framebuffer",
                                     "simple-framebuffer";
                        allwinner,pipeline = "de_be0-lcd0";
-                       clocks = <&pll5 1>, <&ahb_gates 36>, <&ahb_gates 44>;
+                       clocks = <&pll3>, <&pll5 1>, <&ahb_gates 36>,
+                                <&ahb_gates 44>;
                        status = "disabled";
                };
 
@@ -82,8 +83,8 @@ framebuffer@2 {
                        compatible = "allwinner,simple-framebuffer",
                                     "simple-framebuffer";
                        allwinner,pipeline = "de_be0-lcd0-tve0";
-                       clocks = <&pll5 1>, <&ahb_gates 34>, <&ahb_gates 36>,
-                                <&ahb_gates 44>;
+                       clocks = <&pll3>, <&pll5 1>, <&ahb_gates 34>,
+                                <&ahb_gates 36>, <&ahb_gates 44>;
                        status = "disabled";
                };
        };
index a8d8b4582397a2dee85929aa43d955efce5009f2..f694482bdeb64ccf1f21ffaa69d3fb3f1027ea0b 100644 (file)
@@ -52,7 +52,7 @@
 
 / {
        model = "NextThing C.H.I.P.";
-       compatible = "nextthing,chip", "allwinner,sun5i-r8";
+       compatible = "nextthing,chip", "allwinner,sun5i-r8", "allwinner,sun5i-a13";
 
        aliases {
                i2c0 = &i2c0;
index febdf4c72fb013d3a2c222a20d4f30cf149d2513..2c34bbbb95700a4c1af75ffb44d92a312b927dea 100644 (file)
@@ -67,8 +67,9 @@ framebuffer@0 {
                        compatible = "allwinner,simple-framebuffer",
                                     "simple-framebuffer";
                        allwinner,pipeline = "de_be0-lcd0-hdmi";
-                       clocks = <&pll5 1>, <&ahb_gates 36>, <&ahb_gates 43>,
-                                <&ahb_gates 44>, <&dram_gates 26>;
+                       clocks = <&pll3>, <&pll5 1>, <&ahb_gates 36>,
+                                <&ahb_gates 43>, <&ahb_gates 44>,
+                                <&dram_gates 26>;
                        status = "disabled";
                };
 
@@ -76,8 +77,8 @@ framebuffer@1 {
                        compatible = "allwinner,simple-framebuffer",
                                     "simple-framebuffer";
                        allwinner,pipeline = "de_be0-lcd0";
-                       clocks = <&pll5 1>, <&ahb_gates 36>, <&ahb_gates 44>,
-                                <&dram_gates 26>;
+                       clocks = <&pll3>, <&pll5 1>, <&ahb_gates 36>,
+                                <&ahb_gates 44>, <&dram_gates 26>;
                        status = "disabled";
                };
 
@@ -85,7 +86,7 @@ framebuffer@2 {
                        compatible = "allwinner,simple-framebuffer",
                                     "simple-framebuffer";
                        allwinner,pipeline = "de_be0-lcd0-tve0";
-                       clocks = <&pll5 1>,
+                       clocks = <&pll3>, <&pll5 1>,
                                 <&ahb_gates 34>, <&ahb_gates 36>, <&ahb_gates 44>,
                                 <&dram_gates 5>, <&dram_gates 26>;
                        status = "disabled";
@@ -231,6 +232,7 @@ pll3: clk@01c20010 {
                pll3x2: pll3x2_clk {
                        #clock-cells = <0>;
                        compatible = "fixed-factor-clock";
+                       clocks = <&pll3>;
                        clock-div = <1>;
                        clock-mult = <2>;
                        clock-output-names = "pll3-2x";
@@ -272,6 +274,7 @@ pll7: clk@01c20030 {
                pll7x2: pll7x2_clk {
                        #clock-cells = <0>;
                        compatible = "fixed-factor-clock";
+                       clocks = <&pll7>;
                        clock-div = <1>;
                        clock-mult = <2>;
                        clock-output-names = "pll7-2x";
index 1eca3b28ac6480e8b1d628cfebfb2c58ebd24280..b6da15d823a61bcbc8d77e6c7d295306795cef09 100644 (file)
@@ -1843,7 +1843,7 @@ ldo4_reg: ldo4 {
 
                                ldo5_reg: ldo5 {
                                        regulator-name = "vddio_sdmmc,avdd_vdac";
-                                       regulator-min-microvolt = <3300000>;
+                                       regulator-min-microvolt = <1800000>;
                                        regulator-max-microvolt = <3300000>;
                                        regulator-always-on;
                                };
@@ -1914,6 +1914,7 @@ i2s@70080400 {
 
        sdhci@78000000 {
                status = "okay";
+               vqmmc-supply = <&ldo5_reg>;
                cd-gpios = <&gpio TEGRA_GPIO(I, 5) GPIO_ACTIVE_LOW>;
                wp-gpios = <&gpio TEGRA_GPIO(T, 3) GPIO_ACTIVE_HIGH>;
                power-gpios = <&gpio TEGRA_GPIO(D, 7) GPIO_ACTIVE_HIGH>;
index 893941ec98dc6da787629068359275a863516056..f1bde7c4e736de72253b8e8ae1419688d910b098 100644 (file)
@@ -263,6 +263,7 @@ void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu)
        kvm_timer_vcpu_terminate(vcpu);
        kvm_vgic_vcpu_destroy(vcpu);
        kvm_pmu_vcpu_destroy(vcpu);
+       kvm_vcpu_uninit(vcpu);
        kmem_cache_free(kvm_vcpu_cache, vcpu);
 }
 
index ecf9e0c3b107808752fe49af062b8758e1784986..e53c6cfcab51cd12c798fd11d663686c77761b02 100644 (file)
@@ -7,9 +7,15 @@ CFLAGS_pmsu.o                  := -march=armv7-a
 obj-$(CONFIG_MACH_MVEBU_ANY)    += system-controller.o mvebu-soc-id.o
 
 ifeq ($(CONFIG_MACH_MVEBU_V7),y)
-obj-y                           += cpu-reset.o board-v7.o coherency.o coherency_ll.o pmsu.o pmsu_ll.o pm.o pm-board.o
+obj-y                           += cpu-reset.o board-v7.o coherency.o coherency_ll.o pmsu.o pmsu_ll.o
+
+obj-$(CONFIG_PM)                += pm.o pm-board.o
 obj-$(CONFIG_SMP)               += platsmp.o headsmp.o platsmp-a9.o headsmp-a9.o
 endif
 
 obj-$(CONFIG_MACH_DOVE)                 += dove.o
-obj-$(CONFIG_MACH_KIRKWOOD)     += kirkwood.o kirkwood-pm.o
+
+ifeq ($(CONFIG_MACH_KIRKWOOD),y)
+obj-y                           += kirkwood.o
+obj-$(CONFIG_PM)                += kirkwood-pm.o
+endif
index 7e989d61159c384df1de62b27156ddf22d8fa44f..e80f0dde218919dab8d7a2b5873a4962755f3ee2 100644 (file)
@@ -162,22 +162,16 @@ static void __init armada_370_coherency_init(struct device_node *np)
 }
 
 /*
- * This ioremap hook is used on Armada 375/38x to ensure that PCIe
- * memory areas are mapped as MT_UNCACHED instead of MT_DEVICE. This
- * is needed as a workaround for a deadlock issue between the PCIe
- * interface and the cache controller.
+ * This ioremap hook is used on Armada 375/38x to ensure that all MMIO
+ * areas are mapped as MT_UNCACHED instead of MT_DEVICE. This is
+ * needed for the HW I/O coherency mechanism to work properly without
+ * deadlock.
  */
 static void __iomem *
-armada_pcie_wa_ioremap_caller(phys_addr_t phys_addr, size_t size,
-                             unsigned int mtype, void *caller)
+armada_wa_ioremap_caller(phys_addr_t phys_addr, size_t size,
+                        unsigned int mtype, void *caller)
 {
-       struct resource pcie_mem;
-
-       mvebu_mbus_get_pcie_mem_aperture(&pcie_mem);
-
-       if (pcie_mem.start <= phys_addr && (phys_addr + size) <= pcie_mem.end)
-               mtype = MT_UNCACHED;
-
+       mtype = MT_UNCACHED;
        return __arm_ioremap_caller(phys_addr, size, mtype, caller);
 }
 
@@ -186,7 +180,8 @@ static void __init armada_375_380_coherency_init(struct device_node *np)
        struct device_node *cache_dn;
 
        coherency_cpu_base = of_iomap(np, 0);
-       arch_ioremap_caller = armada_pcie_wa_ioremap_caller;
+       arch_ioremap_caller = armada_wa_ioremap_caller;
+       pci_ioremap_set_mem_type(MT_UNCACHED);
 
        /*
         * We should switch the PL310 to I/O coherency mode only if
index 87e1985f3be8c5912d1b1ca4fbc5d8d70024db50..9d9fd4b9a72e574baba29be7381e8740f06b2d66 100644 (file)
 #define APM_CPU_PART_POTENZA           0x000
 
 #define CAVIUM_CPU_PART_THUNDERX       0x0A1
+#define CAVIUM_CPU_PART_THUNDERX_81XX  0x0A2
 
 #define BRCM_CPU_PART_VULCAN           0x516
 
 #define MIDR_CORTEX_A53 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A53)
 #define MIDR_CORTEX_A57 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A57)
 #define MIDR_THUNDERX  MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX)
+#define MIDR_THUNDERX_81XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_81XX)
 
 #ifndef __ASSEMBLY__
 
index a307eb6e7fa8d75e536fe3cff56c5d4f978fe212..7f94755089e200afbd4c015316da4517fe113bcd 100644 (file)
@@ -117,6 +117,8 @@ struct pt_regs {
        };
        u64 orig_x0;
        u64 syscallno;
+       u64 orig_addr_limit;
+       u64 unused;     // maintain 16 byte alignment
 };
 
 #define arch_has_single_step() (1)
index f8e5d47f08807aa41d33c84a323e4bf1f37ffebf..2f4ba774488ad5cc72444a8d351ce63fa504351c 100644 (file)
@@ -60,6 +60,7 @@ int main(void)
   DEFINE(S_PC,                 offsetof(struct pt_regs, pc));
   DEFINE(S_ORIG_X0,            offsetof(struct pt_regs, orig_x0));
   DEFINE(S_SYSCALLNO,          offsetof(struct pt_regs, syscallno));
+  DEFINE(S_ORIG_ADDR_LIMIT,    offsetof(struct pt_regs, orig_addr_limit));
   DEFINE(S_FRAME_SIZE,         sizeof(struct pt_regs));
   BLANK();
   DEFINE(MM_CONTEXT_ID,                offsetof(struct mm_struct, context.id.counter));
index d42789499f17eb322a47da83046ca0119e643c75..af716b65110d901d1a1fea40da801240475c9ccf 100644 (file)
@@ -98,6 +98,12 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
                MIDR_RANGE(MIDR_THUNDERX, 0x00,
                           (1 << MIDR_VARIANT_SHIFT) | 1),
        },
+       {
+       /* Cavium ThunderX, T81 pass 1.0 */
+               .desc = "Cavium erratum 27456",
+               .capability = ARM64_WORKAROUND_CAVIUM_27456,
+               MIDR_RANGE(MIDR_THUNDERX_81XX, 0x00, 0x00),
+       },
 #endif
        {
        }
index 12e8d2bcb3f9da4e643649442685138dea7966d1..6c3b7345a6c428f5952909ac0833684ca0401f1a 100644 (file)
@@ -28,6 +28,7 @@
 #include <asm/errno.h>
 #include <asm/esr.h>
 #include <asm/irq.h>
+#include <asm/memory.h>
 #include <asm/thread_info.h>
 #include <asm/unistd.h>
 
        mov     x29, xzr                        // fp pointed to user-space
        .else
        add     x21, sp, #S_FRAME_SIZE
-       .endif
+       get_thread_info tsk
+       /* Save the task's original addr_limit and set USER_DS (TASK_SIZE_64) */
+       ldr     x20, [tsk, #TI_ADDR_LIMIT]
+       str     x20, [sp, #S_ORIG_ADDR_LIMIT]
+       mov     x20, #TASK_SIZE_64
+       str     x20, [tsk, #TI_ADDR_LIMIT]
+       ALTERNATIVE(nop, SET_PSTATE_UAO(0), ARM64_HAS_UAO, CONFIG_ARM64_UAO)
+       .endif /* \el == 0 */
        mrs     x22, elr_el1
        mrs     x23, spsr_el1
        stp     lr, x21, [sp, #S_LR]
        .endm
 
        .macro  kernel_exit, el
+       .if     \el != 0
+       /* Restore the task's original addr_limit. */
+       ldr     x20, [sp, #S_ORIG_ADDR_LIMIT]
+       str     x20, [tsk, #TI_ADDR_LIMIT]
+
+       /* No need to restore UAO, it will be restored from SPSR_EL1 */
+       .endif
+
        ldp     x21, x22, [sp, #S_PC]           // load ELR, SPSR
        .if     \el == 0
        ct_user_enter
@@ -406,7 +422,6 @@ el1_irq:
        bl      trace_hardirqs_off
 #endif
 
-       get_thread_info tsk
        irq_handler
 
 #ifdef CONFIG_PREEMPT
index 013e2cbe792474eb3777c8f01ff65f2356527260..b1166d1e5955cd80096ea7412a2371008ec81b7c 100644 (file)
@@ -280,7 +280,8 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
        }
 
        if (permission_fault(esr) && (addr < USER_DS)) {
-               if (get_fs() == KERNEL_DS)
+               /* regs->orig_addr_limit may be 0 if we entered from EL0 */
+               if (regs->orig_addr_limit == KERNEL_DS)
                        die("Accessing user space memory with fs=KERNEL_DS", regs, esr);
 
                if (!search_exception_tables(regs->pc))
index 01d877c6868f05e5a8fb72639a980cd4b7ef66dc..cf3023dced49d7fbe55ab50a8f357b95e5f5194a 100644 (file)
@@ -8,12 +8,13 @@
 
 #include <asm/processor.h>
 
-static void putc(char c);
+static void m32r_putc(char c);
 
 static int puts(const char *s)
 {
        char c;
-       while ((c = *s++)) putc(c);
+       while ((c = *s++))
+               m32r_putc(c);
        return 0;
 }
 
@@ -41,7 +42,7 @@ static int puts(const char *s)
 #define BOOT_SIO0TXB   PLD_ESIO0TXB
 #endif
 
-static void putc(char c)
+static void m32r_putc(char c)
 {
        while ((*BOOT_SIO0STS & 0x3) != 0x3)
                cpu_relax();
@@ -61,7 +62,7 @@ static void putc(char c)
 #define SIO0TXB        (volatile unsigned short *)(0x00efd000 + 30)
 #endif
 
-static void putc(char c)
+static void m32r_putc(char c)
 {
        while ((*SIO0STS & 0x1) == 0)
                cpu_relax();
index fa31be297b85acf1ba31ca4059f2f48825c91d87..73d92ea0ce65b5caca8e79f208995c034872b004 100644 (file)
@@ -288,7 +288,7 @@ _clear_bss:
 #endif
 
        /*
-        *      Assember start up done, start code proper.
+        *      Assembler start up done, start code proper.
         */
        jsr     start_kernel                    /* start Linux kernel */
 
index c525e4c08f8477f45bb0e978b0c436af07471cbc..9abb1a441da082e2fcf8724a0171adec50e4e7b1 100644 (file)
@@ -111,7 +111,7 @@ void __init config_BSP(char *commandp, int size)
 /***************************************************************************/
 
 /*
- * Some 5272 based boards have the FEC ethernet diectly connected to
+ * Some 5272 based boards have the FEC ethernet directly connected to
  * an ethernet switch. In this case we need to use the fixed phy type,
  * and we need to declare it early in boot.
  */
index 821de928dc3f9d14adbb16c1ad70accc9e57f477..6a640be485684f00497a63ee9630bd77df9dbe4a 100644 (file)
@@ -42,7 +42,7 @@ static unsigned long iospace;
 
 /*
  * We need to be carefull probing on bus 0 (directly connected to host
- * bridge). We should only acccess the well defined possible devices in
+ * bridge). We should only access the well defined possible devices in
  * use, ignore aliases and the like.
  */
 static unsigned char mcf_host_slot2sid[32] = {
index 3ee6976f60885e5b2e0e4e2a80df6d824de14694..8f5b6f7dd1366024b973a6eb7846bdd0dd47bd02 100644 (file)
@@ -9,6 +9,7 @@ CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_PID_NS is not set
 # CONFIG_NET_NS is not set
 CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_USERFAULTFD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -359,6 +360,7 @@ CONFIG_MACVTAP=m
 CONFIG_IPVLAN=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
@@ -553,7 +555,9 @@ CONFIG_TEST_STRING_HELPERS=m
 CONFIG_TEST_KSTRTOX=m
 CONFIG_TEST_PRINTF=m
 CONFIG_TEST_BITMAP=m
+CONFIG_TEST_UUID=m
 CONFIG_TEST_RHASHTABLE=m
+CONFIG_TEST_HASH=m
 CONFIG_TEST_LKM=m
 CONFIG_TEST_USER_COPY=m
 CONFIG_TEST_BPF=m
index e96787ffcbced33f9893d2760259177086b13c1f..31bded9c83d45f3a4b6861e2a5a11379324b4209 100644 (file)
@@ -9,6 +9,7 @@ CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_PID_NS is not set
 # CONFIG_NET_NS is not set
 CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_USERFAULTFD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -341,6 +342,7 @@ CONFIG_MACVTAP=m
 CONFIG_IPVLAN=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
@@ -512,7 +514,9 @@ CONFIG_TEST_STRING_HELPERS=m
 CONFIG_TEST_KSTRTOX=m
 CONFIG_TEST_PRINTF=m
 CONFIG_TEST_BITMAP=m
+CONFIG_TEST_UUID=m
 CONFIG_TEST_RHASHTABLE=m
+CONFIG_TEST_HASH=m
 CONFIG_TEST_LKM=m
 CONFIG_TEST_USER_COPY=m
 CONFIG_TEST_BPF=m
index 083fe6beac149da81250ca4fccd50798f9e4ef0d..0d7739e04ae2f13d63719db1a45a3a712b668e36 100644 (file)
@@ -9,6 +9,7 @@ CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_PID_NS is not set
 # CONFIG_NET_NS is not set
 CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_USERFAULTFD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -350,6 +351,7 @@ CONFIG_MACVTAP=m
 CONFIG_IPVLAN=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
@@ -533,7 +535,9 @@ CONFIG_TEST_STRING_HELPERS=m
 CONFIG_TEST_KSTRTOX=m
 CONFIG_TEST_PRINTF=m
 CONFIG_TEST_BITMAP=m
+CONFIG_TEST_UUID=m
 CONFIG_TEST_RHASHTABLE=m
+CONFIG_TEST_HASH=m
 CONFIG_TEST_LKM=m
 CONFIG_TEST_USER_COPY=m
 CONFIG_TEST_BPF=m
index 475130c06dcba04b646ec2e7c199aee7045dea7d..2cbb5c465fec03487046c08b8aef427a6cc81518 100644 (file)
@@ -9,6 +9,7 @@ CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_PID_NS is not set
 # CONFIG_NET_NS is not set
 CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_USERFAULTFD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -340,6 +341,7 @@ CONFIG_MACVTAP=m
 CONFIG_IPVLAN=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
@@ -504,7 +506,9 @@ CONFIG_TEST_STRING_HELPERS=m
 CONFIG_TEST_KSTRTOX=m
 CONFIG_TEST_PRINTF=m
 CONFIG_TEST_BITMAP=m
+CONFIG_TEST_UUID=m
 CONFIG_TEST_RHASHTABLE=m
+CONFIG_TEST_HASH=m
 CONFIG_TEST_LKM=m
 CONFIG_TEST_USER_COPY=m
 CONFIG_TEST_BPF=m
index 4339658c200f3eb8af4bc2645836e109466055cb..96102a42c156ee2d94fa5e069ba8e8c7f879387d 100644 (file)
@@ -9,6 +9,7 @@ CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_PID_NS is not set
 # CONFIG_NET_NS is not set
 CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_USERFAULTFD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -341,6 +342,7 @@ CONFIG_MACVTAP=m
 CONFIG_IPVLAN=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
@@ -514,7 +516,9 @@ CONFIG_TEST_STRING_HELPERS=m
 CONFIG_TEST_KSTRTOX=m
 CONFIG_TEST_PRINTF=m
 CONFIG_TEST_BITMAP=m
+CONFIG_TEST_UUID=m
 CONFIG_TEST_RHASHTABLE=m
+CONFIG_TEST_HASH=m
 CONFIG_TEST_LKM=m
 CONFIG_TEST_USER_COPY=m
 CONFIG_TEST_BPF=m
index 831cc8c3a2e259f67d318257e72bcab84e36b8c1..97d88f7dc5a7bd60db5f05db2483bec15de4723b 100644 (file)
@@ -9,6 +9,7 @@ CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_PID_NS is not set
 # CONFIG_NET_NS is not set
 CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_USERFAULTFD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -357,6 +358,7 @@ CONFIG_MACVTAP=m
 CONFIG_IPVLAN=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
@@ -536,7 +538,9 @@ CONFIG_TEST_STRING_HELPERS=m
 CONFIG_TEST_KSTRTOX=m
 CONFIG_TEST_PRINTF=m
 CONFIG_TEST_BITMAP=m
+CONFIG_TEST_UUID=m
 CONFIG_TEST_RHASHTABLE=m
+CONFIG_TEST_HASH=m
 CONFIG_TEST_LKM=m
 CONFIG_TEST_USER_COPY=m
 CONFIG_TEST_BPF=m
index 6377afeb522bbb9a235d1bd57f36cc14ca36ac5b..be25ef208f0f6237f055dea3b6cef375e59ea0a9 100644 (file)
@@ -9,6 +9,7 @@ CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_PID_NS is not set
 # CONFIG_NET_NS is not set
 CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_USERFAULTFD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -390,6 +391,7 @@ CONFIG_MACVTAP=m
 CONFIG_IPVLAN=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
@@ -616,7 +618,9 @@ CONFIG_TEST_STRING_HELPERS=m
 CONFIG_TEST_KSTRTOX=m
 CONFIG_TEST_PRINTF=m
 CONFIG_TEST_BITMAP=m
+CONFIG_TEST_UUID=m
 CONFIG_TEST_RHASHTABLE=m
+CONFIG_TEST_HASH=m
 CONFIG_TEST_LKM=m
 CONFIG_TEST_USER_COPY=m
 CONFIG_TEST_BPF=m
index 4304b3d56262bc677383ba4389b65d5e9a7625cd..a008344360c9161183afc063795d16bef1a945ff 100644 (file)
@@ -9,6 +9,7 @@ CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_PID_NS is not set
 # CONFIG_NET_NS is not set
 CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_USERFAULTFD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -339,6 +340,7 @@ CONFIG_MACVTAP=m
 CONFIG_IPVLAN=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
@@ -504,7 +506,9 @@ CONFIG_TEST_STRING_HELPERS=m
 CONFIG_TEST_KSTRTOX=m
 CONFIG_TEST_PRINTF=m
 CONFIG_TEST_BITMAP=m
+CONFIG_TEST_UUID=m
 CONFIG_TEST_RHASHTABLE=m
+CONFIG_TEST_HASH=m
 CONFIG_TEST_LKM=m
 CONFIG_TEST_USER_COPY=m
 CONFIG_TEST_BPF=m
index 074bda4094ffd5b0913d4411371e2597774faab2..6735a25f36d446f389a0620faede9d3f37c42c8e 100644 (file)
@@ -9,6 +9,7 @@ CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_PID_NS is not set
 # CONFIG_NET_NS is not set
 CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_USERFAULTFD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -340,6 +341,7 @@ CONFIG_MACVTAP=m
 CONFIG_IPVLAN=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
@@ -504,7 +506,9 @@ CONFIG_TEST_STRING_HELPERS=m
 CONFIG_TEST_KSTRTOX=m
 CONFIG_TEST_PRINTF=m
 CONFIG_TEST_BITMAP=m
+CONFIG_TEST_UUID=m
 CONFIG_TEST_RHASHTABLE=m
+CONFIG_TEST_HASH=m
 CONFIG_TEST_LKM=m
 CONFIG_TEST_USER_COPY=m
 CONFIG_TEST_BPF=m
index 07b9fa8d7f2ea71dd09a26c8d9abe4a519baa07c..780c6e9f6cf9113df4c75ab3a2a8bd3220f741a4 100644 (file)
@@ -9,6 +9,7 @@ CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_PID_NS is not set
 # CONFIG_NET_NS is not set
 CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_USERFAULTFD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -346,6 +347,7 @@ CONFIG_MACVTAP=m
 CONFIG_IPVLAN=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
@@ -527,7 +529,9 @@ CONFIG_TEST_STRING_HELPERS=m
 CONFIG_TEST_KSTRTOX=m
 CONFIG_TEST_PRINTF=m
 CONFIG_TEST_BITMAP=m
+CONFIG_TEST_UUID=m
 CONFIG_TEST_RHASHTABLE=m
+CONFIG_TEST_HASH=m
 CONFIG_TEST_LKM=m
 CONFIG_TEST_USER_COPY=m
 CONFIG_TEST_BPF=m
index 36e6fae02d458e2dcb1a96c0caee68a5301f0341..44693cf361e5df9c2600f26d36e033e00e7bf2b2 100644 (file)
@@ -9,6 +9,7 @@ CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_PID_NS is not set
 # CONFIG_NET_NS is not set
 CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_USERFAULTFD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -337,6 +338,7 @@ CONFIG_MACVTAP=m
 CONFIG_IPVLAN=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
@@ -506,7 +508,9 @@ CONFIG_TEST_STRING_HELPERS=m
 CONFIG_TEST_KSTRTOX=m
 CONFIG_TEST_PRINTF=m
 CONFIG_TEST_BITMAP=m
+CONFIG_TEST_UUID=m
 CONFIG_TEST_RHASHTABLE=m
+CONFIG_TEST_HASH=m
 CONFIG_TEST_LKM=m
 CONFIG_TEST_USER_COPY=m
 CONFIG_TEST_BPF=m
index 903acf929511e5066403bfdd1c4d78bbc4084c35..ef0071d6115834657ee43e674dbb1b0fd8422482 100644 (file)
@@ -9,6 +9,7 @@ CONFIG_LOG_BUF_SHIFT=16
 # CONFIG_PID_NS is not set
 # CONFIG_NET_NS is not set
 CONFIG_BLK_DEV_INITRD=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
 CONFIG_USERFAULTFD=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
@@ -337,6 +338,7 @@ CONFIG_MACVTAP=m
 CONFIG_IPVLAN=m
 CONFIG_VXLAN=m
 CONFIG_GENEVE=m
+CONFIG_GTP=m
 CONFIG_MACSEC=m
 CONFIG_NETCONSOLE=m
 CONFIG_NETCONSOLE_DYNAMIC=y
@@ -506,7 +508,9 @@ CONFIG_TEST_STRING_HELPERS=m
 CONFIG_TEST_KSTRTOX=m
 CONFIG_TEST_PRINTF=m
 CONFIG_TEST_BITMAP=m
+CONFIG_TEST_UUID=m
 CONFIG_TEST_RHASHTABLE=m
+CONFIG_TEST_HASH=m
 CONFIG_TEST_LKM=m
 CONFIG_TEST_USER_COPY=m
 CONFIG_TEST_BPF=m
index 78cb60f5bb4dda18b28276c4b8b79c787e948590..9bbffebe3eb504833ed0937670bd4168751d61a4 100644 (file)
@@ -10191,7 +10191,7 @@ xdnrm_con:
 xdnrm_sd:
        mov.l           %a1,-(%sp)
        tst.b           LOCAL_EX(%a0)           # is denorm pos or neg?
-       smi.b           %d1                     # set d0 accodingly
+       smi.b           %d1                     # set d0 accordingly
        bsr.l           unf_sub
        mov.l           (%sp)+,%a1
 xdnrm_exit:
@@ -10990,7 +10990,7 @@ src_qnan_m:
 # routines where an instruction is selected by an index into
 # a large jump table corresponding to a given instruction which
 # has been decoded. Flow continues here where we now decode
-# further accoding to the source operand type.
+# further according to the source operand type.
 #
 
        global          fsinh
@@ -23196,14 +23196,14 @@ m_sign:
 #
 #  1. Branch on the sign of the adjusted exponent.
 #  2p.(positive exp)
-#   2. Check M16 and the digits in lwords 2 and 3 in decending order.
+#   2. Check M16 and the digits in lwords 2 and 3 in descending order.
 #   3. Add one for each zero encountered until a non-zero digit.
 #   4. Subtract the count from the exp.
 #   5. Check if the exp has crossed zero in #3 above; make the exp abs
 #         and set SE.
 #      6. Multiply the mantissa by 10**count.
 #  2n.(negative exp)
-#   2. Check the digits in lwords 3 and 2 in decending order.
+#   2. Check the digits in lwords 3 and 2 in descending order.
 #   3. Add one for each zero encountered until a non-zero digit.
 #   4. Add the count to the exp.
 #   5. Check if the exp has crossed zero in #3 above; clear SE.
index 4aedef973cf6fbd02cc67ea65bcbcede0da21c68..3535e6c87eec611bbe03c965c0fc5aeeaeff5343 100644 (file)
@@ -13156,14 +13156,14 @@ m_sign:
 #
 #  1. Branch on the sign of the adjusted exponent.
 #  2p.(positive exp)
-#   2. Check M16 and the digits in lwords 2 and 3 in decending order.
+#   2. Check M16 and the digits in lwords 2 and 3 in descending order.
 #   3. Add one for each zero encountered until a non-zero digit.
 #   4. Subtract the count from the exp.
 #   5. Check if the exp has crossed zero in #3 above; make the exp abs
 #         and set SE.
 #      6. Multiply the mantissa by 10**count.
 #  2n.(negative exp)
-#   2. Check the digits in lwords 3 and 2 in decending order.
+#   2. Check the digits in lwords 3 and 2 in descending order.
 #   3. Add one for each zero encountered until a non-zero digit.
 #   4. Add the count to the exp.
 #   5. Check if the exp has crossed zero in #3 above; clear SE.
index 429fe26e320c9813afdf88011195552498a90032..208b4daa14b334f4ce4365e8eb88ca91ab30b818 100644 (file)
@@ -18,7 +18,7 @@
  * AUG/22/2000 : added support for 32-bit Dual-Address-Mode (K) 2000
  *               Oliver Kamphenkel (O.Kamphenkel@tu-bs.de)
  *
- * AUG/25/2000 : addad support for 8, 16 and 32-bit Single-Address-Mode (K)2000
+ * AUG/25/2000 : added support for 8, 16 and 32-bit Single-Address-Mode (K)2000
  *               Oliver Kamphenkel (O.Kamphenkel@tu-bs.de)
  *
  * APR/18/2002 : added proper support for MCF5272 DMA controller.
index f186459072e9b0042fbf413d9224367350adcfa6..699f20c8a0fee4fbe6e093ccf0cee8cf042f3cfb 100644 (file)
 /*
  *     I2C module.
  */
-#define MCFI2C_BASE0           (MCF_MBAR + 0x280)      /* Base addreess I2C0 */
+#define MCFI2C_BASE0           (MCF_MBAR + 0x280)      /* Base address I2C0 */
 #define MCFI2C_SIZE0           0x20                    /* Register set size */
 
-#define MCFI2C_BASE1           (MCF_MBAR2 + 0x440)     /* Base addreess I2C1 */
+#define MCFI2C_BASE1           (MCF_MBAR2 + 0x440)     /* Base address I2C1 */
 #define MCFI2C_SIZE1           0x20                    /* Register set size */
 
 /*
index 26cc3d5a63f82f2de7021a8a3da28e554f1c815e..8824236e303fe815fe99a24008ba4a7b125fb1a5 100644 (file)
@@ -38,7 +38,7 @@
 /*
  *     MMU Operation register.
  */
-#define        MMUOR_UAA       0x00000001              /* Update allocatiom address */
+#define        MMUOR_UAA       0x00000001              /* Update allocation address */
 #define        MMUOR_ACC       0x00000002              /* TLB access */
 #define        MMUOR_RD        0x00000004              /* TLB access read */
 #define        MMUOR_WR        0x00000000              /* TLB access write */
index fc5b36278d040b0620778cbf1f67499f8db5eb6f..c48d21b68f0485fbce8dc5a85bf553b51ac05642 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Q40 master Chip Control
- * RTC stuff merged for compactnes..
+ * RTC stuff merged for compactness.
 */
 
 #ifndef _Q40_MASTER_H
index 4d2adfb32a2ab4f61951357114a8d053bf74de82..7990b6f50105b19b48417843343778b00c7dc03e 100644 (file)
@@ -60,7 +60,7 @@
  *
  * The host talks to the IOPs using a rather simple message-passing scheme via
  * a shared memory area in the IOP RAM. Each IOP has seven "channels"; each
- * channel is conneced to a specific software driver on the IOP. For example
+ * channel is connected to a specific software driver on the IOP. For example
  * on the SCC IOP there is one channel for each serial port. Each channel has
  * an incoming and and outgoing message queue with a depth of one.
  *
index 759679d9ab96610bec00ec8935063e25f2cfae4d..6d1e760e2a0e21825a4ff54ee83ec7ccca0fa5a3 100644 (file)
@@ -130,7 +130,7 @@ do_fscc=0
        bfextu  %d2{#13,#3},%d0
 .endm
 
-| decode the 8bit diplacement from the brief extension word
+| decode the 8bit displacement from the brief extension word
 .macro fp_decode_disp8
        move.b  %d2,%d0
        ext.w   %d0
index a6b611f1da4390356cd3412672cc0b35398bb0e8..7d44e888134f9621d8056ac56fdf6916394619b7 100644 (file)
@@ -24,7 +24,7 @@ struct mm_struct;
 struct vm_area_struct;
 
 #define PAGE_NONE      __pgprot(_PAGE_PRESENT | _PAGE_NO_READ | \
-                                _CACHE_CACHABLE_NONCOHERENT)
+                                _page_cachable_default)
 #define PAGE_SHARED    __pgprot(_PAGE_PRESENT | _PAGE_WRITE | \
                                 _page_cachable_default)
 #define PAGE_COPY      __pgprot(_PAGE_PRESENT | _PAGE_NO_EXEC | \
@@ -476,7 +476,7 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
        pte.pte_low  &= (_PAGE_MODIFIED | _PAGE_ACCESSED | _PFNX_MASK);
        pte.pte_high &= (_PFN_MASK | _CACHE_MASK);
        pte.pte_low  |= pgprot_val(newprot) & ~_PFNX_MASK;
-       pte.pte_high |= pgprot_val(newprot) & ~_PFN_MASK;
+       pte.pte_high |= pgprot_val(newprot) & ~(_PFN_MASK | _CACHE_MASK);
        return pte;
 }
 #elif defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32)
@@ -491,7 +491,8 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
 #else
 static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
 {
-       return __pte((pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot));
+       return __pte((pte_val(pte) & _PAGE_CHG_MASK) |
+                    (pgprot_val(newprot) & ~_PAGE_CHG_MASK));
 }
 #endif
 
@@ -632,7 +633,8 @@ static inline struct page *pmd_page(pmd_t pmd)
 
 static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot)
 {
-       pmd_val(pmd) = (pmd_val(pmd) & _PAGE_CHG_MASK) | pgprot_val(newprot);
+       pmd_val(pmd) = (pmd_val(pmd) & (_PAGE_CHG_MASK | _PAGE_HUGE)) |
+                      (pgprot_val(newprot) & ~_PAGE_CHG_MASK);
        return pmd;
 }
 
index 88a5ecaa157b5a2fd474f23759e11e2764f973e5..ab84c89c9e982129c3cde79792548c6078d42065 100644 (file)
@@ -230,6 +230,7 @@ extern unsigned long __kernel_virt_size;
 #define KERN_VIRT_SIZE  __kernel_virt_size
 extern struct page *vmemmap;
 extern unsigned long ioremap_bot;
+extern unsigned long pci_io_base;
 #endif /* __ASSEMBLY__ */
 
 #include <asm/book3s/64/hash.h>
index b5f73cb5eeb6e34b43fd75d5054f867128e792f7..d70101e1e25c1ac902996b2ba29bb8aa6f3f335e 100644 (file)
@@ -647,7 +647,7 @@ static int eeh_reset_device(struct eeh_pe *pe, struct pci_bus *bus,
                        pci_unlock_rescan_remove();
                }
        } else if (frozen_bus) {
-               eeh_pe_dev_traverse(pe, eeh_rmv_device, &rmv_data);
+               eeh_pe_dev_traverse(pe, eeh_rmv_device, rmv_data);
        }
 
        /*
index 3759df52bd671d883c38aec23fe4b133e6c0d0f7..a5ae49a2dcc47a12163e6ec6ae50ff85d3bd2bb4 100644 (file)
@@ -47,7 +47,6 @@ static int __init pcibios_init(void)
 
        printk(KERN_INFO "PCI: Probing PCI hardware\n");
 
-       pci_io_base = ISA_IO_BASE;
        /* For now, override phys_mem_access_prot. If we need it,g
         * later, we may move that initialization to each ppc_md
         */
index e2f12cbcade9a49287c370204bcc71a4fb23b33a..0b93893424f5b2fe8246694e5e5e31c7b5324a0f 100644 (file)
@@ -1505,6 +1505,16 @@ void start_thread(struct pt_regs *regs, unsigned long start, unsigned long sp)
                current->thread.regs = regs - 1;
        }
 
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+       /*
+        * Clear any transactional state, we're exec()ing. The cause is
+        * not important as there will never be a recheckpoint so it's not
+        * user visible.
+        */
+       if (MSR_TM_SUSPENDED(mfmsr()))
+               tm_reclaim_current(0);
+#endif
+
        memset(regs->gpr, 0, sizeof(regs->gpr));
        regs->ctr = 0;
        regs->link = 0;
index bf8f34a5867088d1a2346d17b89e2daaabe365ab..b7019b559ddbffd817563e6f0d3ae5f3d134b2ca 100644 (file)
@@ -110,17 +110,11 @@ _GLOBAL(tm_reclaim)
        std     r3, STK_PARAM(R3)(r1)
        SAVE_NVGPRS(r1)
 
-       /* We need to setup MSR for VSX register save instructions.  Here we
-        * also clear the MSR RI since when we do the treclaim, we won't have a
-        * valid kernel pointer for a while.  We clear RI here as it avoids
-        * adding another mtmsr closer to the treclaim.  This makes the region
-        * maked as non-recoverable wider than it needs to be but it saves on
-        * inserting another mtmsrd later.
-        */
+       /* We need to setup MSR for VSX register save instructions. */
        mfmsr   r14
        mr      r15, r14
        ori     r15, r15, MSR_FP
-       li      r16, MSR_RI
+       li      r16, 0
        ori     r16, r16, MSR_EE /* IRQs hard off */
        andc    r15, r15, r16
        oris    r15, r15, MSR_VEC@h
@@ -176,7 +170,17 @@ dont_backup_fp:
 1:     tdeqi   r6, 0
        EMIT_BUG_ENTRY 1b,__FILE__,__LINE__,0
 
-       /* The moment we treclaim, ALL of our GPRs will switch
+       /* Clear MSR RI since we are about to change r1, EE is already off. */
+       li      r4, 0
+       mtmsrd  r4, 1
+
+       /*
+        * BE CAREFUL HERE:
+        * At this point we can't take an SLB miss since we have MSR_RI
+        * off. Load only to/from the stack/paca which are in SLB bolted regions
+        * until we turn MSR RI back on.
+        *
+        * The moment we treclaim, ALL of our GPRs will switch
         * to user register state.  (FPRs, CCR etc. also!)
         * Use an sprg and a tm_scratch in the PACA to shuffle.
         */
@@ -197,6 +201,11 @@ dont_backup_fp:
 
        /* Store the PPR in r11 and reset to decent value */
        std     r11, GPR11(r1)                  /* Temporary stash */
+
+       /* Reset MSR RI so we can take SLB faults again */
+       li      r11, MSR_RI
+       mtmsrd  r11, 1
+
        mfspr   r11, SPRN_PPR
        HMT_MEDIUM
 
@@ -397,11 +406,6 @@ restore_gprs:
        ld      r5, THREAD_TM_DSCR(r3)
        ld      r6, THREAD_TM_PPR(r3)
 
-       /* Clear the MSR RI since we are about to change R1.  EE is already off
-        */
-       li      r4, 0
-       mtmsrd  r4, 1
-
        REST_GPR(0, r7)                         /* GPR0 */
        REST_2GPRS(2, r7)                       /* GPR2-3 */
        REST_GPR(4, r7)                         /* GPR4 */
@@ -439,10 +443,33 @@ restore_gprs:
        ld      r6, _CCR(r7)
        mtcr    r6
 
-       REST_GPR(1, r7)                         /* GPR1 */
-       REST_GPR(5, r7)                         /* GPR5-7 */
        REST_GPR(6, r7)
-       ld      r7, GPR7(r7)
+
+       /*
+        * Store r1 and r5 on the stack so that we can access them
+        * after we clear MSR RI.
+        */
+
+       REST_GPR(5, r7)
+       std     r5, -8(r1)
+       ld      r5, GPR1(r7)
+       std     r5, -16(r1)
+
+       REST_GPR(7, r7)
+
+       /* Clear MSR RI since we are about to change r1. EE is already off */
+       li      r5, 0
+       mtmsrd  r5, 1
+
+       /*
+        * BE CAREFUL HERE:
+        * At this point we can't take an SLB miss since we have MSR_RI
+        * off. Load only to/from the stack/paca which are in SLB bolted regions
+        * until we turn MSR RI back on.
+        */
+
+       ld      r5, -8(r1)
+       ld      r1, -16(r1)
 
        /* Commit register state as checkpointed state: */
        TRECHKPT
index 5b22ba0b58bc9e5168a4d12022bb6c176ab3a14c..2971ea18c768bcda67c36874b4fba274f607abc5 100644 (file)
@@ -922,6 +922,10 @@ void __init hash__early_init_mmu(void)
        vmemmap = (struct page *)H_VMEMMAP_BASE;
        ioremap_bot = IOREMAP_BASE;
 
+#ifdef CONFIG_PCI
+       pci_io_base = ISA_IO_BASE;
+#endif
+
        /* Initialize the MMU Hash table and create the linear mapping
         * of memory. Has to be done before SLB initialization as this is
         * currently where the page size encoding is obtained.
index e58707deef5c2f90530d088dbc786417d191af04..7931e1496f0d59d2ab5c015a48824d15e5fb3f05 100644 (file)
@@ -328,6 +328,11 @@ void __init radix__early_init_mmu(void)
        __vmalloc_end = RADIX_VMALLOC_END;
        vmemmap = (struct page *)RADIX_VMEMMAP_BASE;
        ioremap_bot = IOREMAP_BASE;
+
+#ifdef CONFIG_PCI
+       pci_io_base = ISA_IO_BASE;
+#endif
+
        /*
         * For now radix also use the same frag size
         */
index 5e04f3cbd320d78e963c7ae18639c94d4b83952c..8ae236b0f80b35b127d3b87d64e1abff23d437c8 100644 (file)
@@ -22,7 +22,7 @@ static inline int test_fp_ctl(u32 fpc)
                "       la      %0,0\n"
                "1:\n"
                EX_TABLE(0b,1b)
-               : "=d" (rc), "=d" (orig_fpc)
+               : "=d" (rc), "=&d" (orig_fpc)
                : "d" (fpc), "0" (-EINVAL));
        return rc;
 }
index f20abdb5630adceaa75690907ddf7cdedc098146..d14069d4b88dcd1a38fe2a3ca309cf8723909bf3 100644 (file)
@@ -2064,12 +2064,5 @@ void s390_reset_system(void)
        S390_lowcore.program_new_psw.addr =
                (unsigned long) s390_base_pgm_handler;
 
-       /*
-        * Clear subchannel ID and number to signal new kernel that no CCW or
-        * SCSI IPL has been done (for kexec and kdump)
-        */
-       S390_lowcore.subchannel_id = 0;
-       S390_lowcore.subchannel_nr = 0;
-
        do_reset_calls();
 }
index 33787ee817f0cdaad78814849b0aad2dc7e2b407..91eac39625beb1b85fb3651a1b1758d296779c9a 100644 (file)
@@ -263,7 +263,7 @@ static bool check_hw_exists(void)
 
 msr_fail:
        pr_cont("Broken PMU hardware detected, using software events only.\n");
-       pr_info("%sFailed to access perfctr msr (MSR %x is %Lx)\n",
+       printk("%sFailed to access perfctr msr (MSR %x is %Lx)\n",
                boot_cpu_has(X86_FEATURE_HYPERVISOR) ? KERN_INFO : KERN_ERR,
                reg, val_new);
 
@@ -2319,7 +2319,7 @@ void
 perf_callchain_user(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs)
 {
        struct stack_frame frame;
-       const void __user *fp;
+       const unsigned long __user *fp;
 
        if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
                /* TODO: We don't support guest os callchain now */
@@ -2332,7 +2332,7 @@ perf_callchain_user(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs
        if (regs->flags & (X86_VM_MASK | PERF_EFLAGS_VM))
                return;
 
-       fp = (void __user *)regs->bp;
+       fp = (unsigned long __user *)regs->bp;
 
        perf_callchain_store(entry, regs->ip);
 
@@ -2345,16 +2345,17 @@ perf_callchain_user(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs
        pagefault_disable();
        while (entry->nr < entry->max_stack) {
                unsigned long bytes;
+
                frame.next_frame             = NULL;
                frame.return_address = 0;
 
-               if (!access_ok(VERIFY_READ, fp, 16))
+               if (!access_ok(VERIFY_READ, fp, sizeof(*fp) * 2))
                        break;
 
-               bytes = __copy_from_user_nmi(&frame.next_frame, fp, 8);
+               bytes = __copy_from_user_nmi(&frame.next_frame, fp, sizeof(*fp));
                if (bytes != 0)
                        break;
-               bytes = __copy_from_user_nmi(&frame.return_address, fp+8, 8);
+               bytes = __copy_from_user_nmi(&frame.return_address, fp + 1, sizeof(*fp));
                if (bytes != 0)
                        break;
 
index 3660b2cf245ad7d22eaf0f9d1cf2602d270b62c2..06c2baa518144336133f775299b086d0ac4306ec 100644 (file)
@@ -1,8 +1,8 @@
 obj-$(CONFIG_CPU_SUP_INTEL)            += core.o bts.o cqm.o
 obj-$(CONFIG_CPU_SUP_INTEL)            += ds.o knc.o
 obj-$(CONFIG_CPU_SUP_INTEL)            += lbr.o p4.o p6.o pt.o
-obj-$(CONFIG_PERF_EVENTS_INTEL_RAPL)   += intel-rapl.o
-intel-rapl-objs                                := rapl.o
+obj-$(CONFIG_PERF_EVENTS_INTEL_RAPL)   += intel-rapl-perf.o
+intel-rapl-perf-objs                   := rapl.o
 obj-$(CONFIG_PERF_EVENTS_INTEL_UNCORE) += intel-uncore.o
 intel-uncore-objs                      := uncore.o uncore_nhmex.o uncore_snb.o uncore_snbep.o
 obj-$(CONFIG_PERF_EVENTS_INTEL_CSTATE) += intel-cstate.o
index 7c666958a6250354aa204d24e73f94670264ffee..9b4f9d3ce4650cc7c38632b73c10e5020c0695a3 100644 (file)
@@ -115,6 +115,10 @@ static struct event_constraint intel_snb_event_constraints[] __read_mostly =
        INTEL_UEVENT_CONSTRAINT(0x04a3, 0xf), /* CYCLE_ACTIVITY.CYCLES_NO_DISPATCH */
        INTEL_UEVENT_CONSTRAINT(0x02a3, 0x4), /* CYCLE_ACTIVITY.CYCLES_L1D_PENDING */
 
+       /*
+        * When HT is off these events can only run on the bottom 4 counters
+        * When HT is on, they are impacted by the HT bug and require EXCL access
+        */
        INTEL_EXCLEVT_CONSTRAINT(0xd0, 0xf), /* MEM_UOPS_RETIRED.* */
        INTEL_EXCLEVT_CONSTRAINT(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */
        INTEL_EXCLEVT_CONSTRAINT(0xd2, 0xf), /* MEM_LOAD_UOPS_LLC_HIT_RETIRED.* */
@@ -139,6 +143,10 @@ static struct event_constraint intel_ivb_event_constraints[] __read_mostly =
        INTEL_UEVENT_CONSTRAINT(0x0ca3, 0x4), /* CYCLE_ACTIVITY.STALLS_L1D_PENDING */
        INTEL_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PREC_DIST */
 
+       /*
+        * When HT is off these events can only run on the bottom 4 counters
+        * When HT is on, they are impacted by the HT bug and require EXCL access
+        */
        INTEL_EXCLEVT_CONSTRAINT(0xd0, 0xf), /* MEM_UOPS_RETIRED.* */
        INTEL_EXCLEVT_CONSTRAINT(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */
        INTEL_EXCLEVT_CONSTRAINT(0xd2, 0xf), /* MEM_LOAD_UOPS_LLC_HIT_RETIRED.* */
@@ -182,6 +190,16 @@ struct event_constraint intel_skl_event_constraints[] = {
        FIXED_EVENT_CONSTRAINT(0x003c, 1),      /* CPU_CLK_UNHALTED.CORE */
        FIXED_EVENT_CONSTRAINT(0x0300, 2),      /* CPU_CLK_UNHALTED.REF */
        INTEL_UEVENT_CONSTRAINT(0x1c0, 0x2),    /* INST_RETIRED.PREC_DIST */
+
+       /*
+        * when HT is off, these can only run on the bottom 4 counters
+        */
+       INTEL_EVENT_CONSTRAINT(0xd0, 0xf),      /* MEM_INST_RETIRED.* */
+       INTEL_EVENT_CONSTRAINT(0xd1, 0xf),      /* MEM_LOAD_RETIRED.* */
+       INTEL_EVENT_CONSTRAINT(0xd2, 0xf),      /* MEM_LOAD_L3_HIT_RETIRED.* */
+       INTEL_EVENT_CONSTRAINT(0xcd, 0xf),      /* MEM_TRANS_RETIRED.* */
+       INTEL_EVENT_CONSTRAINT(0xc6, 0xf),      /* FRONTEND_RETIRED.* */
+
        EVENT_CONSTRAINT_END
 };
 
@@ -250,6 +268,10 @@ static struct event_constraint intel_hsw_event_constraints[] = {
        /* CYCLE_ACTIVITY.CYCLES_NO_EXECUTE */
        INTEL_UEVENT_CONSTRAINT(0x04a3, 0xf),
 
+       /*
+        * When HT is off these events can only run on the bottom 4 counters
+        * When HT is on, they are impacted by the HT bug and require EXCL access
+        */
        INTEL_EXCLEVT_CONSTRAINT(0xd0, 0xf), /* MEM_UOPS_RETIRED.* */
        INTEL_EXCLEVT_CONSTRAINT(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */
        INTEL_EXCLEVT_CONSTRAINT(0xd2, 0xf), /* MEM_LOAD_UOPS_LLC_HIT_RETIRED.* */
@@ -264,6 +286,13 @@ struct event_constraint intel_bdw_event_constraints[] = {
        FIXED_EVENT_CONSTRAINT(0x0300, 2),      /* CPU_CLK_UNHALTED.REF */
        INTEL_UEVENT_CONSTRAINT(0x148, 0x4),    /* L1D_PEND_MISS.PENDING */
        INTEL_UBIT_EVENT_CONSTRAINT(0x8a3, 0x4),        /* CYCLE_ACTIVITY.CYCLES_L1D_MISS */
+       /*
+        * when HT is off, these can only run on the bottom 4 counters
+        */
+       INTEL_EVENT_CONSTRAINT(0xd0, 0xf),      /* MEM_INST_RETIRED.* */
+       INTEL_EVENT_CONSTRAINT(0xd1, 0xf),      /* MEM_LOAD_RETIRED.* */
+       INTEL_EVENT_CONSTRAINT(0xd2, 0xf),      /* MEM_LOAD_L3_HIT_RETIRED.* */
+       INTEL_EVENT_CONSTRAINT(0xcd, 0xf),      /* MEM_TRANS_RETIRED.* */
        EVENT_CONSTRAINT_END
 };
 
index 4a413485f9eb8ef58ec71c77ff2594f4300c8ea6..c64b1e9c5d1a30d916be2d944a3e94134b240fb0 100644 (file)
 #define X86_BUG_FXSAVE_LEAK    X86_BUG(6) /* FXSAVE leaks FOP/FIP/FOP */
 #define X86_BUG_CLFLUSH_MONITOR        X86_BUG(7) /* AAI65, CLFLUSH required before MONITOR */
 #define X86_BUG_SYSRET_SS_ATTRS        X86_BUG(8) /* SYSRET doesn't fix up SS attrs */
-#define X86_BUG_NULL_SEG       X86_BUG(9) /* Nulling a selector preserves the base */
-#define X86_BUG_SWAPGS_FENCE   X86_BUG(10) /* SWAPGS without input dep on GS */
-
-
 #ifdef CONFIG_X86_32
 /*
  * 64-bit kernels don't use X86_BUG_ESPFIX.  Make the define conditional
  */
 #define X86_BUG_ESPFIX         X86_BUG(9) /* "" IRET to 16-bit SS corrupts ESP/RSP high bits */
 #endif
+#define X86_BUG_NULL_SEG       X86_BUG(10) /* Nulling a selector preserves the base */
+#define X86_BUG_SWAPGS_FENCE   X86_BUG(11) /* SWAPGS without input dep on GS */
 
 #endif /* _ASM_X86_CPUFEATURES_H */
index fdcc04020636e33269d7a756162cfe852f509823..7c1c89598688bab2edc26dce70ab722e5f79b123 100644 (file)
@@ -68,30 +68,23 @@ static inline u64 pvclock_scale_delta(u64 delta, u32 mul_frac, int shift)
        return product;
 }
 
-static __always_inline
-u64 pvclock_get_nsec_offset(const struct pvclock_vcpu_time_info *src)
-{
-       u64 delta = rdtsc_ordered() - src->tsc_timestamp;
-       return pvclock_scale_delta(delta, src->tsc_to_system_mul,
-                                  src->tsc_shift);
-}
-
 static __always_inline
 unsigned __pvclock_read_cycles(const struct pvclock_vcpu_time_info *src,
                               cycle_t *cycles, u8 *flags)
 {
        unsigned version;
-       cycle_t ret, offset;
-       u8 ret_flags;
+       cycle_t offset;
+       u64 delta;
 
        version = src->version;
+       /* Make the latest version visible */
+       smp_rmb();
 
-       offset = pvclock_get_nsec_offset(src);
-       ret = src->system_time + offset;
-       ret_flags = src->flags;
-
-       *cycles = ret;
-       *flags = ret_flags;
+       delta = rdtsc_ordered() - src->tsc_timestamp;
+       offset = pvclock_scale_delta(delta, src->tsc_to_system_mul,
+                                  src->tsc_shift);
+       *cycles = src->system_time + offset;
+       *flags = src->flags;
        return version;
 }
 
index a147e676fc7b3439d156534472d63be5c2d10f4a..e991d5c8bb3a1b1d21c08f198a6d38c97f45effb 100644 (file)
@@ -71,8 +71,8 @@ int amd_cache_northbridges(void)
        while ((misc = next_northbridge(misc, amd_nb_misc_ids)) != NULL)
                i++;
 
-       if (i == 0)
-               return 0;
+       if (!i)
+               return -ENODEV;
 
        nb = kzalloc(i * sizeof(struct amd_northbridge), GFP_KERNEL);
        if (!nb)
index bca14c899137bfd754395772a37fbce01f721119..57b71373bae30c47e02a6806ef1a757f111fcd8e 100644 (file)
 
 #include <linux/pci.h>
 #include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/dmi.h>
 #include <linux/pci_ids.h>
+#include <linux/bcma/bcma.h>
+#include <linux/bcma/bcma_regs.h>
 #include <drm/i915_drm.h>
 #include <asm/pci-direct.h>
 #include <asm/dma.h>
@@ -21,6 +25,9 @@
 #include <asm/iommu.h>
 #include <asm/gart.h>
 #include <asm/irq_remapping.h>
+#include <asm/early_ioremap.h>
+
+#define dev_err(msg)  pr_err("pci 0000:%02x:%02x.%d: %s", bus, slot, func, msg)
 
 static void __init fix_hypertransport_config(int num, int slot, int func)
 {
@@ -75,6 +82,13 @@ static void __init nvidia_bugs(int num, int slot, int func)
 {
 #ifdef CONFIG_ACPI
 #ifdef CONFIG_X86_IO_APIC
+       /*
+        * Only applies to Nvidia root ports (bus 0) and not to
+        * Nvidia graphics cards with PCI ports on secondary buses.
+        */
+       if (num)
+               return;
+
        /*
         * All timer overrides on Nvidia are
         * wrong unless HPET is enabled.
@@ -590,6 +604,61 @@ static void __init force_disable_hpet(int num, int slot, int func)
 #endif
 }
 
+#define BCM4331_MMIO_SIZE      16384
+#define BCM4331_PM_CAP         0x40
+#define bcma_aread32(reg)      ioread32(mmio + 1 * BCMA_CORE_SIZE + reg)
+#define bcma_awrite32(reg, val)        iowrite32(val, mmio + 1 * BCMA_CORE_SIZE + reg)
+
+static void __init apple_airport_reset(int bus, int slot, int func)
+{
+       void __iomem *mmio;
+       u16 pmcsr;
+       u64 addr;
+       int i;
+
+       if (!dmi_match(DMI_SYS_VENDOR, "Apple Inc."))
+               return;
+
+       /* Card may have been put into PCI_D3hot by grub quirk */
+       pmcsr = read_pci_config_16(bus, slot, func, BCM4331_PM_CAP + PCI_PM_CTRL);
+
+       if ((pmcsr & PCI_PM_CTRL_STATE_MASK) != PCI_D0) {
+               pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
+               write_pci_config_16(bus, slot, func, BCM4331_PM_CAP + PCI_PM_CTRL, pmcsr);
+               mdelay(10);
+
+               pmcsr = read_pci_config_16(bus, slot, func, BCM4331_PM_CAP + PCI_PM_CTRL);
+               if ((pmcsr & PCI_PM_CTRL_STATE_MASK) != PCI_D0) {
+                       dev_err("Cannot power up Apple AirPort card\n");
+                       return;
+               }
+       }
+
+       addr  =      read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_0);
+       addr |= (u64)read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_1) << 32;
+       addr &= PCI_BASE_ADDRESS_MEM_MASK;
+
+       mmio = early_ioremap(addr, BCM4331_MMIO_SIZE);
+       if (!mmio) {
+               dev_err("Cannot iomap Apple AirPort card\n");
+               return;
+       }
+
+       pr_info("Resetting Apple AirPort card (left enabled by EFI)\n");
+
+       for (i = 0; bcma_aread32(BCMA_RESET_ST) && i < 30; i++)
+               udelay(10);
+
+       bcma_awrite32(BCMA_RESET_CTL, BCMA_RESET_CTL_RESET);
+       bcma_aread32(BCMA_RESET_CTL);
+       udelay(1);
+
+       bcma_awrite32(BCMA_RESET_CTL, 0);
+       bcma_aread32(BCMA_RESET_CTL);
+       udelay(10);
+
+       early_iounmap(mmio, BCM4331_MMIO_SIZE);
+}
 
 #define QFLAG_APPLY_ONCE       0x1
 #define QFLAG_APPLIED          0x2
@@ -603,12 +672,6 @@ struct chipset {
        void (*f)(int num, int slot, int func);
 };
 
-/*
- * Only works for devices on the root bus. If you add any devices
- * not on bus 0 readd another loop level in early_quirks(). But
- * be careful because at least the Nvidia quirk here relies on
- * only matching on bus 0.
- */
 static struct chipset early_qrk[] __initdata = {
        { PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID,
          PCI_CLASS_BRIDGE_PCI, PCI_ANY_ID, QFLAG_APPLY_ONCE, nvidia_bugs },
@@ -638,9 +701,13 @@ static struct chipset early_qrk[] __initdata = {
         */
        { PCI_VENDOR_ID_INTEL, 0x0f00,
                PCI_CLASS_BRIDGE_HOST, PCI_ANY_ID, 0, force_disable_hpet},
+       { PCI_VENDOR_ID_BROADCOM, 0x4331,
+         PCI_CLASS_NETWORK_OTHER, PCI_ANY_ID, 0, apple_airport_reset},
        {}
 };
 
+static void __init early_pci_scan_bus(int bus);
+
 /**
  * check_dev_quirk - apply early quirks to a given PCI device
  * @num: bus number
@@ -649,7 +716,7 @@ static struct chipset early_qrk[] __initdata = {
  *
  * Check the vendor & device ID against the early quirks table.
  *
- * If the device is single function, let early_quirks() know so we don't
+ * If the device is single function, let early_pci_scan_bus() know so we don't
  * poke at this device again.
  */
 static int __init check_dev_quirk(int num, int slot, int func)
@@ -658,6 +725,7 @@ static int __init check_dev_quirk(int num, int slot, int func)
        u16 vendor;
        u16 device;
        u8 type;
+       u8 sec;
        int i;
 
        class = read_pci_config_16(num, slot, func, PCI_CLASS_DEVICE);
@@ -685,25 +753,36 @@ static int __init check_dev_quirk(int num, int slot, int func)
 
        type = read_pci_config_byte(num, slot, func,
                                    PCI_HEADER_TYPE);
+
+       if ((type & 0x7f) == PCI_HEADER_TYPE_BRIDGE) {
+               sec = read_pci_config_byte(num, slot, func, PCI_SECONDARY_BUS);
+               if (sec > num)
+                       early_pci_scan_bus(sec);
+       }
+
        if (!(type & 0x80))
                return -1;
 
        return 0;
 }
 
-void __init early_quirks(void)
+static void __init early_pci_scan_bus(int bus)
 {
        int slot, func;
 
-       if (!early_pci_allowed())
-               return;
-
        /* Poor man's PCI discovery */
-       /* Only scan the root bus */
        for (slot = 0; slot < 32; slot++)
                for (func = 0; func < 8; func++) {
                        /* Only probe function 0 on single fn devices */
-                       if (check_dev_quirk(0, slot, func))
+                       if (check_dev_quirk(bus, slot, func))
                                break;
                }
 }
+
+void __init early_quirks(void)
+{
+       if (!early_pci_allowed())
+               return;
+
+       early_pci_scan_bus(0);
+}
index 99bfc025111d3dd62bc7c1753d126e97fb4e48ac..06c58ce46762ed9165cd3317292bbc57efa7055c 100644 (file)
@@ -61,11 +61,16 @@ void pvclock_resume(void)
 u8 pvclock_read_flags(struct pvclock_vcpu_time_info *src)
 {
        unsigned version;
-       cycle_t ret;
        u8 flags;
 
        do {
-               version = __pvclock_read_cycles(src, &ret, &flags);
+               version = src->version;
+               /* Make the latest version visible */
+               smp_rmb();
+
+               flags = src->flags;
+               /* Make sure that the version double-check is last. */
+               smp_rmb();
        } while ((src->version & 1) || version != src->version);
 
        return flags & valid_flags;
@@ -80,6 +85,8 @@ cycle_t pvclock_clocksource_read(struct pvclock_vcpu_time_info *src)
 
        do {
                version = __pvclock_read_cycles(src, &ret, &flags);
+               /* Make sure that the version double-check is last. */
+               smp_rmb();
        } while ((src->version & 1) || version != src->version);
 
        if (unlikely((flags & PVCLOCK_GUEST_STOPPED) != 0)) {
index bbb5b283ff63a9285ddcedc113a91dbf22859bbb..a397200281c119aabb283fce45c4a921f3f4cd24 100644 (file)
@@ -1310,7 +1310,8 @@ void wait_lapic_expire(struct kvm_vcpu *vcpu)
 
        /* __delay is delay_tsc whenever the hardware has TSC, thus always.  */
        if (guest_tsc < tsc_deadline)
-               __delay(tsc_deadline - guest_tsc);
+               __delay(min(tsc_deadline - guest_tsc,
+                       nsec_to_cycles(vcpu, lapic_timer_advance_ns)));
 }
 
 static void start_apic_timer(struct kvm_lapic *apic)
index 003618e324ce4bd584458ffbf3b5de6d32635d4e..64a79f271276a025ea76759189cddfbe967ae1ed 100644 (file)
@@ -6671,7 +6671,13 @@ static int get_vmx_mem_address(struct kvm_vcpu *vcpu,
 
        /* Checks for #GP/#SS exceptions. */
        exn = false;
-       if (is_protmode(vcpu)) {
+       if (is_long_mode(vcpu)) {
+               /* Long mode: #GP(0)/#SS(0) if the memory address is in a
+                * non-canonical form. This is the only check on the memory
+                * destination for long mode!
+                */
+               exn = is_noncanonical_address(*ret);
+       } else if (is_protmode(vcpu)) {
                /* Protected mode: apply checks for segment validity in the
                 * following order:
                 * - segment type check (#GP(0) may be thrown)
@@ -6688,17 +6694,10 @@ static int get_vmx_mem_address(struct kvm_vcpu *vcpu,
                         * execute-only code segment
                         */
                        exn = ((s.type & 0xa) == 8);
-       }
-       if (exn) {
-               kvm_queue_exception_e(vcpu, GP_VECTOR, 0);
-               return 1;
-       }
-       if (is_long_mode(vcpu)) {
-               /* Long mode: #GP(0)/#SS(0) if the memory address is in a
-                * non-canonical form. This is an only check for long mode.
-                */
-               exn = is_noncanonical_address(*ret);
-       } else if (is_protmode(vcpu)) {
+               if (exn) {
+                       kvm_queue_exception_e(vcpu, GP_VECTOR, 0);
+                       return 1;
+               }
                /* Protected mode: #GP(0)/#SS(0) if the segment is unusable.
                 */
                exn = (s.unusable != 0);
index 902d9da123929be3b8a02e2acc3f315d71c80c2a..7da5dd2057a928fd3eebed2052a6d89ee0750924 100644 (file)
@@ -1244,12 +1244,6 @@ static atomic_t kvm_guest_has_master_clock = ATOMIC_INIT(0);
 static DEFINE_PER_CPU(unsigned long, cpu_tsc_khz);
 static unsigned long max_tsc_khz;
 
-static inline u64 nsec_to_cycles(struct kvm_vcpu *vcpu, u64 nsec)
-{
-       return pvclock_scale_delta(nsec, vcpu->arch.virtual_tsc_mult,
-                                  vcpu->arch.virtual_tsc_shift);
-}
-
 static u32 adjust_tsc_khz(u32 khz, s32 ppm)
 {
        u64 v = (u64)khz * (1000000 + ppm);
index 7ce3634ab5fe6189a95163d8cd9781a923d21727..a82ca466b62e862c88a64b89bb602139fff3ab42 100644 (file)
@@ -2,6 +2,7 @@
 #define ARCH_X86_KVM_X86_H
 
 #include <linux/kvm_host.h>
+#include <asm/pvclock.h>
 #include "kvm_cache_regs.h"
 
 #define MSR_IA32_CR_PAT_DEFAULT  0x0007040600070406ULL
@@ -195,6 +196,12 @@ extern unsigned int lapic_timer_advance_ns;
 
 extern struct static_key kvm_no_apic_vcpu;
 
+static inline u64 nsec_to_cycles(struct kvm_vcpu *vcpu, u64 nsec)
+{
+       return pvclock_scale_delta(nsec, vcpu->arch.virtual_tsc_mult,
+                                  vcpu->arch.virtual_tsc_shift);
+}
+
 /* Same "calling convention" as do_div:
  * - divide (n << 32) by base
  * - put result in n
index 1b1110fa00570e0d242926ca8f06adc37db518a5..0493c17b8a516f4212bc21fe5e32d0bce466c24f 100644 (file)
@@ -54,8 +54,8 @@ static int kasan_die_handler(struct notifier_block *self,
                             void *data)
 {
        if (val == DIE_GPF) {
-               pr_emerg("CONFIG_KASAN_INLINE enabled");
-               pr_emerg("GPF could be caused by NULL-ptr deref or user memory access");
+               pr_emerg("CONFIG_KASAN_INLINE enabled\n");
+               pr_emerg("GPF could be caused by NULL-ptr deref or user memory access\n");
        }
        return NOTIFY_OK;
 }
index b2a4e2a61f6b8f41775c47b0aed020a6441e5d66..3cd69832d7f4c6f3743bbb3a190c39672c89461d 100644 (file)
@@ -396,6 +396,7 @@ int __init pci_acpi_init(void)
                return -ENODEV;
 
        printk(KERN_INFO "PCI: Using ACPI for IRQ routing\n");
+       acpi_irq_penalty_init();
        pcibios_enable_irq = acpi_pci_irq_enable;
        pcibios_disable_irq = acpi_pci_irq_disable;
        x86_init.pci.init_irq = x86_init_noop;
index 009947d419a61ae2fc41f58ad9d85ece1555fec2..f2b5e6a5cf956102905f64462db824a8a355cca5 100644 (file)
@@ -19,6 +19,7 @@
 #include <asm/mtrr.h>
 #include <asm/sections.h>
 #include <asm/suspend.h>
+#include <asm/tlbflush.h>
 
 /* Defined in hibernate_asm_64.S */
 extern asmlinkage __visible int restore_image(void);
@@ -28,6 +29,7 @@ extern asmlinkage __visible int restore_image(void);
  * kernel's text (this value is passed in the image header).
  */
 unsigned long restore_jump_address __visible;
+unsigned long jump_address_phys;
 
 /*
  * Value of the cr3 register from before the hibernation (this value is passed
@@ -37,7 +39,43 @@ unsigned long restore_cr3 __visible;
 
 pgd_t *temp_level4_pgt __visible;
 
-void *relocated_restore_code __visible;
+unsigned long relocated_restore_code __visible;
+
+static int set_up_temporary_text_mapping(void)
+{
+       pmd_t *pmd;
+       pud_t *pud;
+
+       /*
+        * The new mapping only has to cover the page containing the image
+        * kernel's entry point (jump_address_phys), because the switch over to
+        * it is carried out by relocated code running from a page allocated
+        * specifically for this purpose and covered by the identity mapping, so
+        * the temporary kernel text mapping is only needed for the final jump.
+        * Moreover, in that mapping the virtual address of the image kernel's
+        * entry point must be the same as its virtual address in the image
+        * kernel (restore_jump_address), so the image kernel's
+        * restore_registers() code doesn't find itself in a different area of
+        * the virtual address space after switching over to the original page
+        * tables used by the image kernel.
+        */
+       pud = (pud_t *)get_safe_page(GFP_ATOMIC);
+       if (!pud)
+               return -ENOMEM;
+
+       pmd = (pmd_t *)get_safe_page(GFP_ATOMIC);
+       if (!pmd)
+               return -ENOMEM;
+
+       set_pmd(pmd + pmd_index(restore_jump_address),
+               __pmd((jump_address_phys & PMD_MASK) | __PAGE_KERNEL_LARGE_EXEC));
+       set_pud(pud + pud_index(restore_jump_address),
+               __pud(__pa(pmd) | _KERNPG_TABLE));
+       set_pgd(temp_level4_pgt + pgd_index(restore_jump_address),
+               __pgd(__pa(pud) | _KERNPG_TABLE));
+
+       return 0;
+}
 
 static void *alloc_pgt_page(void *context)
 {
@@ -59,9 +97,10 @@ static int set_up_temporary_mappings(void)
        if (!temp_level4_pgt)
                return -ENOMEM;
 
-       /* It is safe to reuse the original kernel mapping */
-       set_pgd(temp_level4_pgt + pgd_index(__START_KERNEL_map),
-               init_level4_pgt[pgd_index(__START_KERNEL_map)]);
+       /* Prepare a temporary mapping for the kernel text */
+       result = set_up_temporary_text_mapping();
+       if (result)
+               return result;
 
        /* Set up the direct mapping from scratch */
        for (i = 0; i < nr_pfn_mapped; i++) {
@@ -78,19 +117,50 @@ static int set_up_temporary_mappings(void)
        return 0;
 }
 
+static int relocate_restore_code(void)
+{
+       pgd_t *pgd;
+       pud_t *pud;
+
+       relocated_restore_code = get_safe_page(GFP_ATOMIC);
+       if (!relocated_restore_code)
+               return -ENOMEM;
+
+       memcpy((void *)relocated_restore_code, &core_restore_code, PAGE_SIZE);
+
+       /* Make the page containing the relocated code executable */
+       pgd = (pgd_t *)__va(read_cr3()) + pgd_index(relocated_restore_code);
+       pud = pud_offset(pgd, relocated_restore_code);
+       if (pud_large(*pud)) {
+               set_pud(pud, __pud(pud_val(*pud) & ~_PAGE_NX));
+       } else {
+               pmd_t *pmd = pmd_offset(pud, relocated_restore_code);
+
+               if (pmd_large(*pmd)) {
+                       set_pmd(pmd, __pmd(pmd_val(*pmd) & ~_PAGE_NX));
+               } else {
+                       pte_t *pte = pte_offset_kernel(pmd, relocated_restore_code);
+
+                       set_pte(pte, __pte(pte_val(*pte) & ~_PAGE_NX));
+               }
+       }
+       __flush_tlb_all();
+
+       return 0;
+}
+
 int swsusp_arch_resume(void)
 {
        int error;
 
        /* We have got enough memory and from now on we cannot recover */
-       if ((error = set_up_temporary_mappings()))
+       error = set_up_temporary_mappings();
+       if (error)
                return error;
 
-       relocated_restore_code = (void *)get_safe_page(GFP_ATOMIC);
-       if (!relocated_restore_code)
-               return -ENOMEM;
-       memcpy(relocated_restore_code, &core_restore_code,
-              &restore_registers - &core_restore_code);
+       error = relocate_restore_code();
+       if (error)
+               return error;
 
        restore_image();
        return 0;
@@ -109,11 +179,12 @@ int pfn_is_nosave(unsigned long pfn)
 
 struct restore_data_record {
        unsigned long jump_address;
+       unsigned long jump_address_phys;
        unsigned long cr3;
        unsigned long magic;
 };
 
-#define RESTORE_MAGIC  0x0123456789ABCDEFUL
+#define RESTORE_MAGIC  0x123456789ABCDEF0UL
 
 /**
  *     arch_hibernation_header_save - populate the architecture specific part
@@ -126,7 +197,8 @@ int arch_hibernation_header_save(void *addr, unsigned int max_size)
 
        if (max_size < sizeof(struct restore_data_record))
                return -EOVERFLOW;
-       rdr->jump_address = restore_jump_address;
+       rdr->jump_address = (unsigned long)&restore_registers;
+       rdr->jump_address_phys = __pa_symbol(&restore_registers);
        rdr->cr3 = restore_cr3;
        rdr->magic = RESTORE_MAGIC;
        return 0;
@@ -142,6 +214,7 @@ int arch_hibernation_header_restore(void *addr)
        struct restore_data_record *rdr = addr;
 
        restore_jump_address = rdr->jump_address;
+       jump_address_phys = rdr->jump_address_phys;
        restore_cr3 = rdr->cr3;
        return (rdr->magic == RESTORE_MAGIC) ? 0 : -EINVAL;
 }
index 4400a43b9e28f20aaac68c1a20091e683e31b799..3177c2bc26f63e9fe1bb82739be9c0c2e406f00f 100644 (file)
@@ -44,9 +44,6 @@ ENTRY(swsusp_arch_suspend)
        pushfq
        popq    pt_regs_flags(%rax)
 
-       /* save the address of restore_registers */
-       movq    $restore_registers, %rax
-       movq    %rax, restore_jump_address(%rip)
        /* save cr3 */
        movq    %cr3, %rax
        movq    %rax, restore_cr3(%rip)
@@ -57,31 +54,34 @@ ENTRY(swsusp_arch_suspend)
 ENDPROC(swsusp_arch_suspend)
 
 ENTRY(restore_image)
-       /* switch to temporary page tables */
-       movq    $__PAGE_OFFSET, %rdx
-       movq    temp_level4_pgt(%rip), %rax
-       subq    %rdx, %rax
-       movq    %rax, %cr3
-       /* Flush TLB */
-       movq    mmu_cr4_features(%rip), %rax
-       movq    %rax, %rdx
-       andq    $~(X86_CR4_PGE), %rdx
-       movq    %rdx, %cr4;  # turn off PGE
-       movq    %cr3, %rcx;  # flush TLB
-       movq    %rcx, %cr3;
-       movq    %rax, %cr4;  # turn PGE back on
-
        /* prepare to jump to the image kernel */
-       movq    restore_jump_address(%rip), %rax
-       movq    restore_cr3(%rip), %rbx
+       movq    restore_jump_address(%rip), %r8
+       movq    restore_cr3(%rip), %r9
+
+       /* prepare to switch to temporary page tables */
+       movq    temp_level4_pgt(%rip), %rax
+       movq    mmu_cr4_features(%rip), %rbx
 
        /* prepare to copy image data to their original locations */
        movq    restore_pblist(%rip), %rdx
+
+       /* jump to relocated restore code */
        movq    relocated_restore_code(%rip), %rcx
        jmpq    *%rcx
 
        /* code below has been relocated to a safe page */
 ENTRY(core_restore_code)
+       /* switch to temporary page tables */
+       movq    $__PAGE_OFFSET, %rcx
+       subq    %rcx, %rax
+       movq    %rax, %cr3
+       /* flush TLB */
+       movq    %rbx, %rcx
+       andq    $~(X86_CR4_PGE), %rcx
+       movq    %rcx, %cr4;  # turn off PGE
+       movq    %cr3, %rcx;  # flush TLB
+       movq    %rcx, %cr3;
+       movq    %rbx, %cr4;  # turn PGE back on
 .Lloop:
        testq   %rdx, %rdx
        jz      .Ldone
@@ -96,24 +96,17 @@ ENTRY(core_restore_code)
        /* progress to the next pbe */
        movq    pbe_next(%rdx), %rdx
        jmp     .Lloop
+
 .Ldone:
        /* jump to the restore_registers address from the image header */
-       jmpq    *%rax
-       /*
-        * NOTE: This assumes that the boot kernel's text mapping covers the
-        * image kernel's page containing restore_registers and the address of
-        * this page is the same as in the image kernel's text mapping (it
-        * should always be true, because the text mapping is linear, starting
-        * from 0, and is supposed to cover the entire kernel text for every
-        * kernel).
-        *
-        * code below belongs to the image kernel
-        */
+       jmpq    *%r8
 
+        /* code below belongs to the image kernel */
+       .align PAGE_SIZE
 ENTRY(restore_registers)
        FRAME_BEGIN
        /* go back to the original page tables */
-       movq    %rbx, %cr3
+       movq    %r9, %cr3
 
        /* Flush TLB, including "global" things (vmalloc) */
        movq    mmu_cr4_features(%rip), %rax
index cc7800e9eb441e2b7737a152f0dbb60182821408..01b8116298a13b5463e7969ce66c0b037bcfccd5 100644 (file)
@@ -150,8 +150,10 @@ static int get_task_ioprio(struct task_struct *p)
        if (ret)
                goto out;
        ret = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_NONE, IOPRIO_NORM);
+       task_lock(p);
        if (p->io_context)
                ret = p->io_context->ioprio;
+       task_unlock(p);
 out:
        return ret;
 }
index 6a76d5c70ef6e1ddb742bfc94671a499184c891d..9492e1c22d3891417d3e42baf2e99661041c1135 100644 (file)
@@ -124,5 +124,10 @@ int mscode_note_digest(void *context, size_t hdrlen,
        struct pefile_context *ctx = context;
 
        ctx->digest = kmemdup(value, vlen, GFP_KERNEL);
-       return ctx->digest ? 0 : -ENOMEM;
+       if (!ctx->digest)
+               return -ENOMEM;
+
+       ctx->digest_len = vlen;
+
+       return 0;
 }
index 44b746e9df1b4110e6f8da4ac5b23419cb63feac..2ffd69769466082eaf55cdfe71fb67704e0364af 100644 (file)
@@ -227,7 +227,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7,
                                if (asymmetric_key_id_same(p->id, auth))
                                        goto found_issuer_check_skid;
                        }
-               } else {
+               } else if (sig->auth_ids[1]) {
                        auth = sig->auth_ids[1];
                        pr_debug("- want %*phN\n", auth->len, auth->data);
                        for (p = pkcs7->certs; p; p = p->next) {
index ac4bddf669de2195bce0864a28308031602245da..19d1afb9890f660e43ee95261cf0a703e44f92c6 100644 (file)
@@ -87,7 +87,7 @@ int restrict_link_by_signature(struct key *trust_keyring,
 
        sig = payload->data[asym_auth];
        if (!sig->auth_ids[0] && !sig->auth_ids[1])
-               return 0;
+               return -ENOKEY;
 
        if (ca_keyid && !asymmetric_key_id_partial(sig->auth_ids[1], ca_keyid))
                return -EPERM;
index 43fe85f20d577b4f3d1bbd6576b6d752bc578531..7097a3395b2529fd123b2c0b14bcce6992fdee49 100644 (file)
@@ -455,6 +455,7 @@ static const int crypto_msg_min[CRYPTO_NR_MSGTYPES] = {
        [CRYPTO_MSG_NEWALG      - CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),
        [CRYPTO_MSG_DELALG      - CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),
        [CRYPTO_MSG_UPDATEALG   - CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),
+       [CRYPTO_MSG_GETALG      - CRYPTO_MSG_BASE] = MSGSIZE(crypto_user_alg),
        [CRYPTO_MSG_DELRNG      - CRYPTO_MSG_BASE] = 0,
 };
 
index ead8dc0d084e749e35733abc164f1e209aca3f6f..8ba426635b1b39631824f7bb2b1f2e3a056bafa4 100644 (file)
@@ -102,10 +102,10 @@ struct pkcs1pad_inst_ctx {
 };
 
 struct pkcs1pad_request {
-       struct akcipher_request child_req;
-
        struct scatterlist in_sg[3], out_sg[2];
        uint8_t *in_buf, *out_buf;
+
+       struct akcipher_request child_req;
 };
 
 static int pkcs1pad_set_pub_key(struct crypto_akcipher *tfm, const void *key,
index 1f4128487dd4be1b342df522e010de6c29c6756b..dee86925a9a107f028ef14adf110eb583500c0c7 100644 (file)
@@ -602,7 +602,7 @@ static int acpi_aml_read_user(char __user *buf, int len)
        crc->tail = (crc->tail + n) & (ACPI_AML_BUF_SIZE - 1);
        ret = n;
 out:
-       acpi_aml_unlock_fifo(ACPI_AML_OUT_USER, !ret);
+       acpi_aml_unlock_fifo(ACPI_AML_OUT_USER, ret >= 0);
        return ret;
 }
 
@@ -672,7 +672,7 @@ static int acpi_aml_write_user(const char __user *buf, int len)
        crc->head = (crc->head + n) & (ACPI_AML_BUF_SIZE - 1);
        ret = n;
 out:
-       acpi_aml_unlock_fifo(ACPI_AML_IN_USER, !ret);
+       acpi_aml_unlock_fifo(ACPI_AML_IN_USER, ret >= 0);
        return n;
 }
 
index 21932d640a41f6f5b060b8fcb64fbc2d9ac131ab..a1d177d58254cd8e201836481a51b809ec98c628 100644 (file)
@@ -108,9 +108,7 @@ acpi_ex_add_table(u32 table_index,
 
        /* Add the table to the namespace */
 
-       acpi_ex_exit_interpreter();
        status = acpi_ns_load_table(table_index, parent_node);
-       acpi_ex_enter_interpreter();
        if (ACPI_FAILURE(status)) {
                acpi_ut_remove_reference(obj_desc);
                *ddb_handle = NULL;
index 1783cd7e14467b8f43e9f087884b3b847e6b5492..f631a47724f05332644a54e290d9667b4b326010 100644 (file)
@@ -47,7 +47,6 @@
 #include "acparser.h"
 #include "acdispat.h"
 #include "actables.h"
-#include "acinterp.h"
 
 #define _COMPONENT          ACPI_NAMESPACE
 ACPI_MODULE_NAME("nsparse")
@@ -171,8 +170,6 @@ acpi_ns_parse_table(u32 table_index, struct acpi_namespace_node *start_node)
 
        ACPI_FUNCTION_TRACE(ns_parse_table);
 
-       acpi_ex_enter_interpreter();
-
        /*
         * AML Parse, pass 1
         *
@@ -188,7 +185,7 @@ acpi_ns_parse_table(u32 table_index, struct acpi_namespace_node *start_node)
        status = acpi_ns_one_complete_parse(ACPI_IMODE_LOAD_PASS1,
                                            table_index, start_node);
        if (ACPI_FAILURE(status)) {
-               goto error_exit;
+               return_ACPI_STATUS(status);
        }
 
        /*
@@ -204,10 +201,8 @@ acpi_ns_parse_table(u32 table_index, struct acpi_namespace_node *start_node)
        status = acpi_ns_one_complete_parse(ACPI_IMODE_LOAD_PASS2,
                                            table_index, start_node);
        if (ACPI_FAILURE(status)) {
-               goto error_exit;
+               return_ACPI_STATUS(status);
        }
 
-error_exit:
-       acpi_ex_exit_interpreter();
        return_ACPI_STATUS(status);
 }
index 73c76d646064700dc1bb51905b4dbc70b415aabf..290d6f5be44b414b7d89ec0a48be1b234330c254 100644 (file)
@@ -1331,8 +1331,6 @@ static int ec_install_handlers(struct acpi_ec *ec)
 
 static void ec_remove_handlers(struct acpi_ec *ec)
 {
-       acpi_ec_stop(ec, false);
-
        if (test_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags)) {
                if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle,
                                        ACPI_ADR_SPACE_EC, &acpi_ec_space_handler)))
@@ -1340,6 +1338,19 @@ static void ec_remove_handlers(struct acpi_ec *ec)
                clear_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags);
        }
 
+       /*
+        * Stops handling the EC transactions after removing the operation
+        * region handler. This is required because _REG(DISCONNECT)
+        * invoked during the removal can result in new EC transactions.
+        *
+        * Flushes the EC requests and thus disables the GPE before
+        * removing the GPE handler. This is required by the current ACPICA
+        * GPE core. ACPICA GPE core will automatically disable a GPE when
+        * it is indicated but there is no way to handle it. So the drivers
+        * must disable the GPEs prior to removing the GPE handlers.
+        */
+       acpi_ec_stop(ec, false);
+
        if (test_bit(EC_FLAGS_GPE_HANDLER_INSTALLED, &ec->flags)) {
                if (ACPI_FAILURE(acpi_remove_gpe_handler(NULL, ec->gpe,
                                        &acpi_ec_gpe_handler)))
index 2215fc847fa90c572b40b426a5edc2a88f5792a2..1f0e06065ae6c5180d2e8deae426aef0966fa3dd 100644 (file)
@@ -928,7 +928,7 @@ static ssize_t format_show(struct device *dev,
 {
        struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
 
-       return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->code));
+       return sprintf(buf, "0x%04x\n", le16_to_cpu(dcr->code));
 }
 static DEVICE_ATTR_RO(format);
 
@@ -961,8 +961,8 @@ static ssize_t format1_show(struct device *dev,
                                continue;
                        if (nfit_dcr->dcr->code == dcr->code)
                                continue;
-                       rc = sprintf(buf, "%#x\n",
-                                       be16_to_cpu(nfit_dcr->dcr->code));
+                       rc = sprintf(buf, "0x%04x\n",
+                                       le16_to_cpu(nfit_dcr->dcr->code));
                        break;
                }
                if (rc != ENXIO)
@@ -1151,9 +1151,10 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
                if (disable_vendor_specific)
                        dsm_mask &= ~(1 << 8);
        } else {
-               dev_err(dev, "unknown dimm command family\n");
+               dev_dbg(dev, "unknown dimm command family\n");
                nfit_mem->family = -1;
-               return force_enable_dimms ? 0 : -ENODEV;
+               /* DSMs are optional, continue loading the driver... */
+               return 0;
        }
 
        uuid = to_nfit_uuid(nfit_mem->family);
index 11cb38348aef600cac3215985d98bc7559e37d63..02b9ea1e8d2e6cc6ad978493529aa0ec59f606d8 100644 (file)
@@ -53,12 +53,12 @@ enum nfit_uuids {
 };
 
 /*
- * Region format interface codes are stored as an array of bytes in the
- * NFIT DIMM Control Region structure
+ * Region format interface codes are stored with the interface as the
+ * LSB and the function as the MSB.
  */
-#define NFIT_FIC_BYTE cpu_to_be16(0x101) /* byte-addressable energy backed */
-#define NFIT_FIC_BLK cpu_to_be16(0x201) /* block-addressable non-energy backed */
-#define NFIT_FIC_BYTEN cpu_to_be16(0x301) /* byte-addressable non-energy backed */
+#define NFIT_FIC_BYTE cpu_to_le16(0x101) /* byte-addressable energy backed */
+#define NFIT_FIC_BLK cpu_to_le16(0x201) /* block-addressable non-energy backed */
+#define NFIT_FIC_BYTEN cpu_to_le16(0x301) /* byte-addressable non-energy backed */
 
 enum {
        NFIT_BLK_READ_FLUSH = 1,
index 8fc7323ed3e847197cfdf1e6fdee728cdfdd8e7b..c983bf733ad37d7b608c9410108dcef32cdbf02b 100644 (file)
@@ -470,6 +470,7 @@ static int acpi_irq_pci_sharing_penalty(int irq)
 {
        struct acpi_pci_link *link;
        int penalty = 0;
+       int i;
 
        list_for_each_entry(link, &acpi_link_list, list) {
                /*
@@ -478,18 +479,14 @@ static int acpi_irq_pci_sharing_penalty(int irq)
                 */
                if (link->irq.active && link->irq.active == irq)
                        penalty += PIRQ_PENALTY_PCI_USING;
-               else {
-                       int i;
-
-                       /*
-                        * If a link is inactive, penalize the IRQs it
-                        * might use, but not as severely.
-                        */
-                       for (i = 0; i < link->irq.possible_count; i++)
-                               if (link->irq.possible[i] == irq)
-                                       penalty += PIRQ_PENALTY_PCI_POSSIBLE /
-                                               link->irq.possible_count;
-               }
+
+               /*
+                * penalize the IRQs PCI might use, but not as severely.
+                */
+               for (i = 0; i < link->irq.possible_count; i++)
+                       if (link->irq.possible[i] == irq)
+                               penalty += PIRQ_PENALTY_PCI_POSSIBLE /
+                                       link->irq.possible_count;
        }
 
        return penalty;
@@ -499,9 +496,6 @@ static int acpi_irq_get_penalty(int irq)
 {
        int penalty = 0;
 
-       if (irq < ACPI_MAX_ISA_IRQS)
-               penalty += acpi_isa_irq_penalty[irq];
-
        /*
        * Penalize IRQ used by ACPI SCI. If ACPI SCI pin attributes conflict
        * with PCI IRQ attributes, mark ACPI SCI as ISA_ALWAYS so it won't be
@@ -516,10 +510,49 @@ static int acpi_irq_get_penalty(int irq)
                        penalty += PIRQ_PENALTY_PCI_USING;
        }
 
+       if (irq < ACPI_MAX_ISA_IRQS)
+               return penalty + acpi_isa_irq_penalty[irq];
+
        penalty += acpi_irq_pci_sharing_penalty(irq);
        return penalty;
 }
 
+int __init acpi_irq_penalty_init(void)
+{
+       struct acpi_pci_link *link;
+       int i;
+
+       /*
+        * Update penalties to facilitate IRQ balancing.
+        */
+       list_for_each_entry(link, &acpi_link_list, list) {
+
+               /*
+                * reflect the possible and active irqs in the penalty table --
+                * useful for breaking ties.
+                */
+               if (link->irq.possible_count) {
+                       int penalty =
+                           PIRQ_PENALTY_PCI_POSSIBLE /
+                           link->irq.possible_count;
+
+                       for (i = 0; i < link->irq.possible_count; i++) {
+                               if (link->irq.possible[i] < ACPI_MAX_ISA_IRQS)
+                                       acpi_isa_irq_penalty[link->irq.
+                                                        possible[i]] +=
+                                           penalty;
+                       }
+
+               } else if (link->irq.active &&
+                               (link->irq.active < ACPI_MAX_ISA_IRQS)) {
+                       acpi_isa_irq_penalty[link->irq.active] +=
+                           PIRQ_PENALTY_PCI_POSSIBLE;
+               }
+       }
+
+       return 0;
+}
+
 static int acpi_irq_balance = -1;      /* 0: static, 1: balance */
 
 static int acpi_pci_link_allocate(struct acpi_pci_link *link)
@@ -839,7 +872,7 @@ void acpi_penalize_isa_irq(int irq, int active)
 {
        if ((irq >= 0) && (irq < ARRAY_SIZE(acpi_isa_irq_penalty)))
                acpi_isa_irq_penalty[irq] = acpi_irq_get_penalty(irq) +
-                       active ? PIRQ_PENALTY_ISA_USED : PIRQ_PENALTY_PCI_USING;
+                 (active ? PIRQ_PENALTY_ISA_USED : PIRQ_PENALTY_PCI_USING);
 }
 
 bool acpi_isa_irq_available(int irq)
index 6e702ab572204deb87d96c0bd399b42d42f2a1f0..1d31c0c0fc20b48c287d2df293776cf9c1e52329 100644 (file)
@@ -137,7 +137,7 @@ static const struct ata_port_info *ahci_seattle_get_port_info(
        u32 val;
 
        plat_data = devm_kzalloc(dev, sizeof(*plat_data), GFP_KERNEL);
-       if (IS_ERR(plat_data))
+       if (!plat_data)
                return &ahci_port_info;
 
        plat_data->sgpio_ctrl = devm_ioremap_resource(dev,
index 6be7770f68e9a16a2aa8555727358c7cf1df5735..31c183aed368c69c9544c92ecb19853548fb8784 100644 (file)
@@ -4314,6 +4314,12 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
         */
        { "ST380013AS",         "3.20",         ATA_HORKAGE_MAX_SEC_1024 },
 
+       /*
+        * Device times out with higher max sects.
+        * https://bugzilla.kernel.org/show_bug.cgi?id=121671
+        */
+       { "LITEON CX1-JB256-HP", NULL,          ATA_HORKAGE_MAX_SEC_1024 },
+
        /* Devices we expect to fail diagnostics */
 
        /* Devices where NCQ should be avoided */
index bd74ee555278513118d057e832b3ec599923bb69..745489a1c86ab2c11701b124f8998126608c398a 100644 (file)
@@ -986,7 +986,7 @@ static inline void mv_write_cached_reg(void __iomem *addr, u32 *old, u32 new)
                 * Looks like a lot of fuss, but it avoids an unnecessary
                 * +1 usec read-after-write delay for unaffected registers.
                 */
-               laddr = (long)addr & 0xffff;
+               laddr = (unsigned long)addr & 0xffff;
                if (laddr >= 0x300 && laddr <= 0x33c) {
                        laddr &= 0x000f;
                        if (laddr == 0x4 || laddr == 0xc) {
index f38c21de29b735982480f8042dce35e8d2ed2a8a..43a36d68c3fded1602c9e8ce97358264c9f33c8a 100644 (file)
@@ -887,6 +887,34 @@ struct fwnode_handle *device_get_next_child_node(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(device_get_next_child_node);
 
+/**
+ * device_get_named_child_node - Return first matching named child node handle
+ * @dev: Device to find the named child node for.
+ * @childname: String to match child node name against.
+ */
+struct fwnode_handle *device_get_named_child_node(struct device *dev,
+                                                 const char *childname)
+{
+       struct fwnode_handle *child;
+
+       /*
+        * Find first matching named child node of this device.
+        * For ACPI this will be a data only sub-node.
+        */
+       device_for_each_child_node(dev, child) {
+               if (is_of_node(child)) {
+                       if (!of_node_cmp(to_of_node(child)->name, childname))
+                               return child;
+               } else if (is_acpi_data_node(child)) {
+                       if (acpi_data_node_match(child, childname))
+                               return child;
+               }
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(device_get_named_child_node);
+
 /**
  * fwnode_handle_put - Drop reference to a device node
  * @fwnode: Pointer to the device node to drop the reference to.
index eda09090cb523f5ddc2a6c174502110ae8999a57..f642c4264c277bc05d98dc99eb15ac8091886ba5 100644 (file)
@@ -8,8 +8,6 @@
 #include <linux/bcma/bcma.h>
 #include <linux/delay.h>
 
-#define BCMA_CORE_SIZE         0x1000
-
 #define bcma_err(bus, fmt, ...) \
        pr_err("bus%d: " fmt, (bus)->num, ##__VA_ARGS__)
 #define bcma_warn(bus, fmt, ...) \
index 2e6d1e9c3345f190b01f9d9caf8d27e6f5f8b9ce..fcc5b4e0aef29ed8d5e863e0277687e1abe700e6 100644 (file)
@@ -207,6 +207,9 @@ struct blkfront_info
        struct blk_mq_tag_set tag_set;
        struct blkfront_ring_info *rinfo;
        unsigned int nr_rings;
+       /* Save uncomplete reqs and bios for migration. */
+       struct list_head requests;
+       struct bio_list bio_list;
 };
 
 static unsigned int nr_minors;
@@ -2002,69 +2005,22 @@ static int blkif_recover(struct blkfront_info *info)
 {
        unsigned int i, r_index;
        struct request *req, *n;
-       struct blk_shadow *copy;
        int rc;
        struct bio *bio, *cloned_bio;
-       struct bio_list bio_list, merge_bio;
        unsigned int segs, offset;
        int pending, size;
        struct split_bio *split_bio;
-       struct list_head requests;
 
        blkfront_gather_backend_features(info);
        segs = info->max_indirect_segments ? : BLKIF_MAX_SEGMENTS_PER_REQUEST;
        blk_queue_max_segments(info->rq, segs);
-       bio_list_init(&bio_list);
-       INIT_LIST_HEAD(&requests);
 
        for (r_index = 0; r_index < info->nr_rings; r_index++) {
-               struct blkfront_ring_info *rinfo;
-
-               rinfo = &info->rinfo[r_index];
-               /* Stage 1: Make a safe copy of the shadow state. */
-               copy = kmemdup(rinfo->shadow, sizeof(rinfo->shadow),
-                              GFP_NOIO | __GFP_REPEAT | __GFP_HIGH);
-               if (!copy)
-                       return -ENOMEM;
-
-               /* Stage 2: Set up free list. */
-               memset(&rinfo->shadow, 0, sizeof(rinfo->shadow));
-               for (i = 0; i < BLK_RING_SIZE(info); i++)
-                       rinfo->shadow[i].req.u.rw.id = i+1;
-               rinfo->shadow_free = rinfo->ring.req_prod_pvt;
-               rinfo->shadow[BLK_RING_SIZE(info)-1].req.u.rw.id = 0x0fffffff;
+               struct blkfront_ring_info *rinfo = &info->rinfo[r_index];
 
                rc = blkfront_setup_indirect(rinfo);
-               if (rc) {
-                       kfree(copy);
+               if (rc)
                        return rc;
-               }
-
-               for (i = 0; i < BLK_RING_SIZE(info); i++) {
-                       /* Not in use? */
-                       if (!copy[i].request)
-                               continue;
-
-                       /*
-                        * Get the bios in the request so we can re-queue them.
-                        */
-                       if (copy[i].request->cmd_flags &
-                           (REQ_FLUSH | REQ_FUA | REQ_DISCARD | REQ_SECURE)) {
-                               /*
-                                * Flush operations don't contain bios, so
-                                * we need to requeue the whole request
-                                */
-                               list_add(&copy[i].request->queuelist, &requests);
-                               continue;
-                       }
-                       merge_bio.head = copy[i].request->bio;
-                       merge_bio.tail = copy[i].request->biotail;
-                       bio_list_merge(&bio_list, &merge_bio);
-                       copy[i].request->bio = NULL;
-                       blk_end_request_all(copy[i].request, 0);
-               }
-
-               kfree(copy);
        }
        xenbus_switch_state(info->xbdev, XenbusStateConnected);
 
@@ -2079,7 +2035,7 @@ static int blkif_recover(struct blkfront_info *info)
                kick_pending_request_queues(rinfo);
        }
 
-       list_for_each_entry_safe(req, n, &requests, queuelist) {
+       list_for_each_entry_safe(req, n, &info->requests, queuelist) {
                /* Requeue pending requests (flush or discard) */
                list_del_init(&req->queuelist);
                BUG_ON(req->nr_phys_segments > segs);
@@ -2087,7 +2043,7 @@ static int blkif_recover(struct blkfront_info *info)
        }
        blk_mq_kick_requeue_list(info->rq);
 
-       while ((bio = bio_list_pop(&bio_list)) != NULL) {
+       while ((bio = bio_list_pop(&info->bio_list)) != NULL) {
                /* Traverse the list of pending bios and re-queue them */
                if (bio_segments(bio) > segs) {
                        /*
@@ -2133,9 +2089,42 @@ static int blkfront_resume(struct xenbus_device *dev)
 {
        struct blkfront_info *info = dev_get_drvdata(&dev->dev);
        int err = 0;
+       unsigned int i, j;
 
        dev_dbg(&dev->dev, "blkfront_resume: %s\n", dev->nodename);
 
+       bio_list_init(&info->bio_list);
+       INIT_LIST_HEAD(&info->requests);
+       for (i = 0; i < info->nr_rings; i++) {
+               struct blkfront_ring_info *rinfo = &info->rinfo[i];
+               struct bio_list merge_bio;
+               struct blk_shadow *shadow = rinfo->shadow;
+
+               for (j = 0; j < BLK_RING_SIZE(info); j++) {
+                       /* Not in use? */
+                       if (!shadow[j].request)
+                               continue;
+
+                       /*
+                        * Get the bios in the request so we can re-queue them.
+                        */
+                       if (shadow[j].request->cmd_flags &
+                                       (REQ_FLUSH | REQ_FUA | REQ_DISCARD | REQ_SECURE)) {
+                               /*
+                                * Flush operations don't contain bios, so
+                                * we need to requeue the whole request
+                                */
+                               list_add(&shadow[j].request->queuelist, &info->requests);
+                               continue;
+                       }
+                       merge_bio.head = shadow[j].request->bio;
+                       merge_bio.tail = shadow[j].request->biotail;
+                       bio_list_merge(&info->bio_list, &merge_bio);
+                       shadow[j].request->bio = NULL;
+                       blk_mq_end_request(shadow[j].request, 0);
+               }
+       }
+
        blkif_free(info, info->connected == BLKIF_STATE_CONNECTED);
 
        err = negotiate_mq(info);
index 10f846cc8db172c5491ddc2508f7084ac5ef5e71..25d5906640c365fe48c618361cb23c98e6a69897 100644 (file)
@@ -99,7 +99,7 @@ static int clk_programmable_set_parent(struct clk_hw *hw, u8 index)
        struct clk_programmable *prog = to_clk_programmable(hw);
        const struct clk_programmable_layout *layout = prog->layout;
        unsigned int mask = layout->css_mask;
-       unsigned int pckr = 0;
+       unsigned int pckr = index;
 
        if (layout->have_slck_mck)
                mask |= AT91_PMC_CSSMCK_MCK;
index efba7d4dbcfc4134a88dff1efa5309f0551a8ea6..79bcb2e4206048c74add49b88f9efe97d11622ff 100644 (file)
@@ -144,9 +144,9 @@ static int oxnas_stdclk_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        regmap = syscon_node_to_regmap(of_get_parent(np));
-       if (!regmap) {
+       if (IS_ERR(regmap)) {
                dev_err(&pdev->dev, "failed to have parent regmap\n");
-               return -EINVAL;
+               return PTR_ERR(regmap);
        }
 
        for (i = 0; i < ARRAY_SIZE(clk_oxnas_init); i++) {
index 4bb130cd006275f5c70e27bcda7a746a00c76d34..05b3d73bfefaacdd54c6123609165d6d2d8481d8 100644 (file)
@@ -321,9 +321,9 @@ struct clk *rockchip_clk_register_cpuclk(const char *name,
        }
 
        cclk = clk_register(NULL, &cpuclk->hw);
-       if (IS_ERR(clk)) {
+       if (IS_ERR(cclk)) {
                pr_err("%s: could not register cpuclk %s\n", __func__,  name);
-               ret = PTR_ERR(clk);
+               ret = PTR_ERR(cclk);
                goto free_rate_table;
        }
 
index bc856f21f6b20d35e2c421c51a54e1718274302e..077fcdc7908bb9f3791fe20bc60a0266327ca050 100644 (file)
@@ -41,8 +41,6 @@ static unsigned long rockchip_mmc_recalc(struct clk_hw *hw,
 #define ROCKCHIP_MMC_DEGREE_MASK 0x3
 #define ROCKCHIP_MMC_DELAYNUM_OFFSET 2
 #define ROCKCHIP_MMC_DELAYNUM_MASK (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET)
-#define ROCKCHIP_MMC_INIT_STATE_RESET 0x1
-#define ROCKCHIP_MMC_INIT_STATE_SHIFT 1
 
 #define PSECS_PER_SEC 1000000000000LL
 
@@ -154,6 +152,7 @@ struct clk *rockchip_clk_register_mmc(const char *name,
                return ERR_PTR(-ENOMEM);
 
        init.name = name;
+       init.flags = 0;
        init.num_parents = num_parents;
        init.parent_names = parent_names;
        init.ops = &rockchip_mmc_clk_ops;
@@ -162,15 +161,6 @@ struct clk *rockchip_clk_register_mmc(const char *name,
        mmc_clock->reg = reg;
        mmc_clock->shift = shift;
 
-       /*
-        * Assert init_state to soft reset the CLKGEN
-        * for mmc tuning phase and degree
-        */
-       if (mmc_clock->shift == ROCKCHIP_MMC_INIT_STATE_SHIFT)
-               writel(HIWORD_UPDATE(ROCKCHIP_MMC_INIT_STATE_RESET,
-                                    ROCKCHIP_MMC_INIT_STATE_RESET,
-                                    mmc_clock->shift), mmc_clock->reg);
-
        clk = clk_register(NULL, &mmc_clock->hw);
        if (IS_ERR(clk))
                kfree(mmc_clock);
index 291543f52caad63630b45cf3500d94e2bf4f9733..8059a8d3ea36430e359e4857b33b58a5f4952aec 100644 (file)
@@ -832,9 +832,9 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
                        RK3399_CLKGATE_CON(13), 1, GFLAGS),
 
        /* perihp */
-       GATE(0, "cpll_aclk_perihp_src", "gpll", CLK_IGNORE_UNUSED,
+       GATE(0, "cpll_aclk_perihp_src", "cpll", CLK_IGNORE_UNUSED,
                        RK3399_CLKGATE_CON(5), 0, GFLAGS),
-       GATE(0, "gpll_aclk_perihp_src", "cpll", CLK_IGNORE_UNUSED,
+       GATE(0, "gpll_aclk_perihp_src", "gpll", CLK_IGNORE_UNUSED,
                        RK3399_CLKGATE_CON(5), 1, GFLAGS),
        COMPOSITE(ACLK_PERIHP, "aclk_perihp", mux_aclk_perihp_p, CLK_IGNORE_UNUSED,
                        RK3399_CLKSEL_CON(14), 7, 1, MFLAGS, 0, 5, DFLAGS,
@@ -1466,6 +1466,8 @@ static struct rockchip_clk_branch rk3399_clk_pmu_branches[] __initdata = {
 
 static const char *const rk3399_cru_critical_clocks[] __initconst = {
        "aclk_cci_pre",
+       "aclk_gic",
+       "aclk_gic_noc",
        "pclk_perilp0",
        "pclk_perilp0",
        "hclk_perilp0",
@@ -1508,6 +1510,7 @@ static void __init rk3399_clk_init(struct device_node *np)
        ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
        if (IS_ERR(ctx)) {
                pr_err("%s: rockchip clk init failed\n", __func__);
+               iounmap(reg_base);
                return;
        }
 
@@ -1553,6 +1556,7 @@ static void __init rk3399_pmu_clk_init(struct device_node *np)
        ctx = rockchip_clk_init(np, reg_base, CLKPMU_NR_CLKS);
        if (IS_ERR(ctx)) {
                pr_err("%s: rockchip pmu clk init failed\n", __func__);
+               iounmap(reg_base);
                return;
        }
 
index 445a7498d6df8f590ef14f447e39dc3e72392a77..9780fac6d029b9b1b85a03ce3d489b31be598281 100644 (file)
@@ -33,6 +33,8 @@ struct sun4i_a10_display_clk_data {
 
        u8      width_div;
        u8      width_mux;
+
+       u32     flags;
 };
 
 struct reset_data {
@@ -166,7 +168,7 @@ static void __init sun4i_a10_display_init(struct device_node *node,
                                     data->has_div ? &div->hw : NULL,
                                     data->has_div ? &clk_divider_ops : NULL,
                                     &gate->hw, &clk_gate_ops,
-                                    0);
+                                    data->flags);
        if (IS_ERR(clk)) {
                pr_err("%s: Couldn't register the clock\n", clk_name);
                goto free_div;
@@ -232,6 +234,7 @@ static const struct sun4i_a10_display_clk_data sun4i_a10_tcon_ch0_data __initcon
        .offset_rst     = 29,
        .offset_mux     = 24,
        .width_mux      = 2,
+       .flags          = CLK_SET_RATE_PARENT,
 };
 
 static void __init sun4i_a10_tcon_ch0_setup(struct device_node *node)
index 98a4582de56a27c370848b240cb044345e145dc3..b6d29d1bedcaead534320d5a2cad836005821aac 100644 (file)
@@ -79,15 +79,11 @@ static int tcon_ch1_is_enabled(struct clk_hw *hw)
 static u8 tcon_ch1_get_parent(struct clk_hw *hw)
 {
        struct tcon_ch1_clk *tclk = hw_to_tclk(hw);
-       int num_parents = clk_hw_get_num_parents(hw);
        u32 reg;
 
        reg = readl(tclk->reg) >> TCON_CH1_SCLK2_MUX_SHIFT;
        reg &= reg >> TCON_CH1_SCLK2_MUX_MASK;
 
-       if (reg >= num_parents)
-               return -EINVAL;
-
        return reg;
 }
 
index 15d06fcf0b500c323e938c08de5947e394121cb9..b02f9c606e0baa59023629b3c1cba8c7ebfbc686 100644 (file)
@@ -56,11 +56,21 @@ static struct cb_id cn_proc_event_id = { CN_IDX_PROC, CN_VAL_PROC };
 /* proc_event_counts is used as the sequence number of the netlink message */
 static DEFINE_PER_CPU(__u32, proc_event_counts) = { 0 };
 
-static inline void get_seq(__u32 *ts, int *cpu)
+static inline void send_msg(struct cn_msg *msg)
 {
        preempt_disable();
-       *ts = __this_cpu_inc_return(proc_event_counts) - 1;
-       *cpu = smp_processor_id();
+
+       msg->seq = __this_cpu_inc_return(proc_event_counts) - 1;
+       ((struct proc_event *)msg->data)->cpu = smp_processor_id();
+
+       /*
+        * Preemption remains disabled during send to ensure the messages are
+        * ordered according to their sequence numbers.
+        *
+        * If cn_netlink_send() fails, the data is not sent.
+        */
+       cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_NOWAIT);
+
        preempt_enable();
 }
 
@@ -77,7 +87,6 @@ void proc_fork_connector(struct task_struct *task)
        msg = buffer_to_cn_msg(buffer);
        ev = (struct proc_event *)msg->data;
        memset(&ev->event_data, 0, sizeof(ev->event_data));
-       get_seq(&msg->seq, &ev->cpu);
        ev->timestamp_ns = ktime_get_ns();
        ev->what = PROC_EVENT_FORK;
        rcu_read_lock();
@@ -92,8 +101,7 @@ void proc_fork_connector(struct task_struct *task)
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
        msg->flags = 0; /* not used */
-       /*  If cn_netlink_send() failed, the data is not sent */
-       cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
+       send_msg(msg);
 }
 
 void proc_exec_connector(struct task_struct *task)
@@ -108,7 +116,6 @@ void proc_exec_connector(struct task_struct *task)
        msg = buffer_to_cn_msg(buffer);
        ev = (struct proc_event *)msg->data;
        memset(&ev->event_data, 0, sizeof(ev->event_data));
-       get_seq(&msg->seq, &ev->cpu);
        ev->timestamp_ns = ktime_get_ns();
        ev->what = PROC_EVENT_EXEC;
        ev->event_data.exec.process_pid = task->pid;
@@ -118,7 +125,7 @@ void proc_exec_connector(struct task_struct *task)
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
        msg->flags = 0; /* not used */
-       cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
+       send_msg(msg);
 }
 
 void proc_id_connector(struct task_struct *task, int which_id)
@@ -150,14 +157,13 @@ void proc_id_connector(struct task_struct *task, int which_id)
                return;
        }
        rcu_read_unlock();
-       get_seq(&msg->seq, &ev->cpu);
        ev->timestamp_ns = ktime_get_ns();
 
        memcpy(&msg->id, &cn_proc_event_id, sizeof(msg->id));
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
        msg->flags = 0; /* not used */
-       cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
+       send_msg(msg);
 }
 
 void proc_sid_connector(struct task_struct *task)
@@ -172,7 +178,6 @@ void proc_sid_connector(struct task_struct *task)
        msg = buffer_to_cn_msg(buffer);
        ev = (struct proc_event *)msg->data;
        memset(&ev->event_data, 0, sizeof(ev->event_data));
-       get_seq(&msg->seq, &ev->cpu);
        ev->timestamp_ns = ktime_get_ns();
        ev->what = PROC_EVENT_SID;
        ev->event_data.sid.process_pid = task->pid;
@@ -182,7 +187,7 @@ void proc_sid_connector(struct task_struct *task)
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
        msg->flags = 0; /* not used */
-       cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
+       send_msg(msg);
 }
 
 void proc_ptrace_connector(struct task_struct *task, int ptrace_id)
@@ -197,7 +202,6 @@ void proc_ptrace_connector(struct task_struct *task, int ptrace_id)
        msg = buffer_to_cn_msg(buffer);
        ev = (struct proc_event *)msg->data;
        memset(&ev->event_data, 0, sizeof(ev->event_data));
-       get_seq(&msg->seq, &ev->cpu);
        ev->timestamp_ns = ktime_get_ns();
        ev->what = PROC_EVENT_PTRACE;
        ev->event_data.ptrace.process_pid  = task->pid;
@@ -215,7 +219,7 @@ void proc_ptrace_connector(struct task_struct *task, int ptrace_id)
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
        msg->flags = 0; /* not used */
-       cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
+       send_msg(msg);
 }
 
 void proc_comm_connector(struct task_struct *task)
@@ -230,7 +234,6 @@ void proc_comm_connector(struct task_struct *task)
        msg = buffer_to_cn_msg(buffer);
        ev = (struct proc_event *)msg->data;
        memset(&ev->event_data, 0, sizeof(ev->event_data));
-       get_seq(&msg->seq, &ev->cpu);
        ev->timestamp_ns = ktime_get_ns();
        ev->what = PROC_EVENT_COMM;
        ev->event_data.comm.process_pid  = task->pid;
@@ -241,7 +244,7 @@ void proc_comm_connector(struct task_struct *task)
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
        msg->flags = 0; /* not used */
-       cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
+       send_msg(msg);
 }
 
 void proc_coredump_connector(struct task_struct *task)
@@ -256,7 +259,6 @@ void proc_coredump_connector(struct task_struct *task)
        msg = buffer_to_cn_msg(buffer);
        ev = (struct proc_event *)msg->data;
        memset(&ev->event_data, 0, sizeof(ev->event_data));
-       get_seq(&msg->seq, &ev->cpu);
        ev->timestamp_ns = ktime_get_ns();
        ev->what = PROC_EVENT_COREDUMP;
        ev->event_data.coredump.process_pid = task->pid;
@@ -266,7 +268,7 @@ void proc_coredump_connector(struct task_struct *task)
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
        msg->flags = 0; /* not used */
-       cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
+       send_msg(msg);
 }
 
 void proc_exit_connector(struct task_struct *task)
@@ -281,7 +283,6 @@ void proc_exit_connector(struct task_struct *task)
        msg = buffer_to_cn_msg(buffer);
        ev = (struct proc_event *)msg->data;
        memset(&ev->event_data, 0, sizeof(ev->event_data));
-       get_seq(&msg->seq, &ev->cpu);
        ev->timestamp_ns = ktime_get_ns();
        ev->what = PROC_EVENT_EXIT;
        ev->event_data.exit.process_pid = task->pid;
@@ -293,7 +294,7 @@ void proc_exit_connector(struct task_struct *task)
        msg->ack = 0; /* not used */
        msg->len = sizeof(*ev);
        msg->flags = 0; /* not used */
-       cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
+       send_msg(msg);
 }
 
 /*
@@ -325,7 +326,7 @@ static void cn_proc_ack(int err, int rcvd_seq, int rcvd_ack)
        msg->ack = rcvd_ack + 1;
        msg->len = sizeof(*ev);
        msg->flags = 0; /* not used */
-       cn_netlink_send(msg, 0, CN_IDX_PROC, GFP_KERNEL);
+       send_msg(msg);
 }
 
 /**
index 3646b143bbf5342f0ac8e0a1b90a86efc7c8cef5..0bb44d5b5df49d385b5bcfd43297b750417461a7 100644 (file)
@@ -79,15 +79,16 @@ static const struct of_device_id machines[] __initconst = {
 static int __init cpufreq_dt_platdev_init(void)
 {
        struct device_node *np = of_find_node_by_path("/");
+       const struct of_device_id *match;
 
        if (!np)
                return -ENODEV;
 
-       if (!of_match_node(machines, np))
+       match = of_match_node(machines, np);
+       of_node_put(np);
+       if (!match)
                return -ENODEV;
 
-       of_node_put(of_root);
-
        return PTR_ERR_OR_ZERO(platform_device_register_simple("cpufreq-dt", -1,
                                                               NULL, 0));
 }
index 9009295f5134247374cacf2a55717e07ff61a057..5617c7087d775ff5f4f93cc084b6bc8769be77ec 100644 (file)
@@ -2261,6 +2261,10 @@ int cpufreq_update_policy(unsigned int cpu)
         * -> ask driver for current freq and notify governors about a change
         */
        if (cpufreq_driver->get && !cpufreq_driver->setpolicy) {
+               if (cpufreq_suspended) {
+                       ret = -EAGAIN;
+                       goto unlock;
+               }
                new_policy.cur = cpufreq_update_current_freq(policy);
                if (WARN_ON(!new_policy.cur)) {
                        ret = -EIO;
index fe9dc17ea8733b6c691502c74f9a10ad88f97c88..1fa1a32928d70a30050b30ca2d6eef4aea62d8e3 100644 (file)
@@ -1400,6 +1400,9 @@ static void intel_pstate_set_update_util_hook(unsigned int cpu_num)
 {
        struct cpudata *cpu = all_cpu_data[cpu_num];
 
+       if (cpu->update_util_set)
+               return;
+
        /* Prevent intel_pstate_update_util() from using stale data. */
        cpu->sample.time = 0;
        cpufreq_add_update_util_hook(cpu_num, &cpu->update_util,
@@ -1440,8 +1443,6 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy)
        if (!policy->cpuinfo.max_freq)
                return -ENODEV;
 
-       intel_pstate_clear_update_util_hook(policy->cpu);
-
        pr_debug("set_policy cpuinfo.max %u policy->max %u\n",
                 policy->cpuinfo.max_freq, policy->max);
 
index a4d0059e232cbd22478d29d92370eb027e8bde79..c73207abb5a44a77ed0819101ab75cd626ceb353 100644 (file)
@@ -173,7 +173,7 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
 
        struct cpuidle_state *target_state = &drv->states[index];
        bool broadcast = !!(target_state->flags & CPUIDLE_FLAG_TIMER_STOP);
-       u64 time_start, time_end;
+       ktime_t time_start, time_end;
        s64 diff;
 
        /*
@@ -195,13 +195,13 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
        sched_idle_set_state(target_state);
 
        trace_cpu_idle_rcuidle(index, dev->cpu);
-       time_start = local_clock();
+       time_start = ns_to_ktime(local_clock());
 
        stop_critical_timings();
        entered_state = target_state->enter(dev, drv, index);
        start_critical_timings();
 
-       time_end = local_clock();
+       time_end = ns_to_ktime(local_clock());
        trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu);
 
        /* The cpu is no longer idle or about to enter idle. */
@@ -217,11 +217,7 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
        if (!cpuidle_state_is_coupled(drv, index))
                local_irq_enable();
 
-       /*
-        * local_clock() returns the time in nanosecond, let's shift
-        * by 10 (divide by 1024) to have microsecond based time.
-        */
-       diff = (time_end - time_start) >> 10;
+       diff = ktime_us_delta(time_end, time_start);
        if (diff > INT_MAX)
                diff = INT_MAX;
 
index 6d74b91f2152807a0f65470dd799aa5080a3c111..5fc3dbb9ada0240011f58fa072e3015fb69bea7e 100644 (file)
@@ -2,6 +2,7 @@ $(obj)/qat_rsapubkey-asn1.o: $(obj)/qat_rsapubkey-asn1.c \
                             $(obj)/qat_rsapubkey-asn1.h
 $(obj)/qat_rsaprivkey-asn1.o: $(obj)/qat_rsaprivkey-asn1.c \
                              $(obj)/qat_rsaprivkey-asn1.h
+$(obj)/qat_asym_algs.o: $(obj)/qat_rsapubkey-asn1.h $(obj)/qat_rsaprivkey-asn1.h
 
 clean-files += qat_rsapubkey-asn1.c qat_rsapubkey-asn1.h
 clean-files += qat_rsaprivkey-asn1.c qat_rsaprivkey-asn1.h
index 574e87c7f2b8933c04242de63afce3ac65723cf3..9acccad26928a4d60cb4fdcbde3feab45a11c3ad 100644 (file)
@@ -781,7 +781,7 @@ static int hash_process_data(struct hash_device_data *device_data,
                                                &device_data->state);
                                memmove(req_ctx->state.buffer,
                                        device_data->state.buffer,
-                                       HASH_BLOCK_SIZE / sizeof(u32));
+                                       HASH_BLOCK_SIZE);
                                if (ret) {
                                        dev_err(device_data->dev,
                                                "%s: hash_resume_state() failed!\n",
@@ -832,7 +832,7 @@ static int hash_process_data(struct hash_device_data *device_data,
 
                        memmove(device_data->state.buffer,
                                req_ctx->state.buffer,
-                               HASH_BLOCK_SIZE / sizeof(u32));
+                               HASH_BLOCK_SIZE);
                        if (ret) {
                                dev_err(device_data->dev, "%s: hash_save_state() failed!\n",
                                        __func__);
index 495577b6d31b33d72792bb7f0f14b3d0a2c66b7e..94ad5c0adbcbd3002e4813d70b100459cfa1ec57 100644 (file)
@@ -182,7 +182,7 @@ struct crypto_alg p8_aes_cbc_alg = {
        .cra_name = "cbc(aes)",
        .cra_driver_name = "p8_aes_cbc",
        .cra_module = THIS_MODULE,
-       .cra_priority = 1000,
+       .cra_priority = 2000,
        .cra_type = &crypto_blkcipher_type,
        .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | CRYPTO_ALG_NEED_FALLBACK,
        .cra_alignmask = 0,
index 0a3c1b04cf3c6057fe667ec95e10588906613935..38ed10d761d006eb2f9e24e1c318f98a09a4a323 100644 (file)
@@ -166,7 +166,7 @@ struct crypto_alg p8_aes_ctr_alg = {
        .cra_name = "ctr(aes)",
        .cra_driver_name = "p8_aes_ctr",
        .cra_module = THIS_MODULE,
-       .cra_priority = 1000,
+       .cra_priority = 2000,
        .cra_type = &crypto_blkcipher_type,
        .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | CRYPTO_ALG_NEED_FALLBACK,
        .cra_alignmask = 0,
index 9f4994cabcc7f44af299a8c8d3fbd90c41982fa4..b18e67d0e065d897bd34d4e2b255056293a17550 100644 (file)
@@ -141,7 +141,7 @@ my $vmr = sub {
 
 # Some ABIs specify vrsave, special-purpose register #256, as reserved
 # for system use.
-my $no_vrsave = ($flavour =~ /aix|linux64le/);
+my $no_vrsave = ($flavour =~ /linux-ppc64le/);
 my $mtspr = sub {
     my ($f,$idx,$ra) = @_;
     if ($idx == 256 && $no_vrsave) {
index 6744d88bdea89782c524ede7d843c3d6bcd67ee2..4fb2eb7c800d8839c6329cd34589eeea4dbaa5c0 100644 (file)
@@ -2378,22 +2378,19 @@ static int sbridge_get_onedevice(struct pci_dev **prev,
  * @num_mc: pointer to the memory controllers count, to be incremented in case
  *         of success.
  * @table: model specific table
- * @allow_dups: allow for multiple devices to exist with the same device id
- *              (as implemented, this isn't expected to work correctly in the
- *              multi-socket case).
- * @multi_bus: don't assume devices on different buses belong to different
- *             memory controllers.
  *
  * returns 0 in case of success or error code
  */
-static int sbridge_get_all_devices_full(u8 *num_mc,
-                                       const struct pci_id_table *table,
-                                       int allow_dups,
-                                       int multi_bus)
+static int sbridge_get_all_devices(u8 *num_mc,
+                                       const struct pci_id_table *table)
 {
        int i, rc;
        struct pci_dev *pdev = NULL;
+       int allow_dups = 0;
+       int multi_bus = 0;
 
+       if (table->type == KNIGHTS_LANDING)
+               allow_dups = multi_bus = 1;
        while (table && table->descr) {
                for (i = 0; i < table->n_devs; i++) {
                        if (!allow_dups || i == 0 ||
@@ -2420,11 +2417,6 @@ static int sbridge_get_all_devices_full(u8 *num_mc,
        return 0;
 }
 
-#define sbridge_get_all_devices(num_mc, table) \
-               sbridge_get_all_devices_full(num_mc, table, 0, 0)
-#define sbridge_get_all_devices_knl(num_mc, table) \
-               sbridge_get_all_devices_full(num_mc, table, 1, 1)
-
 static int sbridge_mci_bind_devs(struct mem_ctl_info *mci,
                                 struct sbridge_dev *sbridge_dev)
 {
index cebcb405812ec0c8a98f771b94f8a4c5caddabc5..d7860614f87f532caeb21145c78352d38161b2a3 100644 (file)
@@ -49,7 +49,7 @@ config GPIO_DEVRES
 
 config OF_GPIO
        def_bool y
-       depends on OF || COMPILE_TEST
+       depends on OF
 
 config GPIO_ACPI
        def_bool y
@@ -402,9 +402,12 @@ config GPIO_TB10X
        select OF_GPIO
 
 config GPIO_TEGRA
-       bool
-       default y
+       bool "NVIDIA Tegra GPIO support"
+       default ARCH_TEGRA
        depends on ARCH_TEGRA || COMPILE_TEST
+       depends on OF
+       help
+         Say yes here to support GPIO pins on NVIDIA Tegra SoCs.
 
 config GPIO_TS4800
        tristate "TS-4800 DIO blocks and compatibles"
index e85e7539cf5d23d0eae1d4a1433b236253dd10cc..eb43ae4835c15d8a65ac8f4fde2de07c7b6e19ee 100644 (file)
@@ -61,9 +61,8 @@ static unsigned sch_gpio_bit(struct sch_gpio *sch, unsigned gpio)
        return gpio % 8;
 }
 
-static int sch_gpio_reg_get(struct gpio_chip *gc, unsigned gpio, unsigned reg)
+static int sch_gpio_reg_get(struct sch_gpio *sch, unsigned gpio, unsigned reg)
 {
-       struct sch_gpio *sch = gpiochip_get_data(gc);
        unsigned short offset, bit;
        u8 reg_val;
 
@@ -75,10 +74,9 @@ static int sch_gpio_reg_get(struct gpio_chip *gc, unsigned gpio, unsigned reg)
        return reg_val;
 }
 
-static void sch_gpio_reg_set(struct gpio_chip *gc, unsigned gpio, unsigned reg,
+static void sch_gpio_reg_set(struct sch_gpio *sch, unsigned gpio, unsigned reg,
                             int val)
 {
-       struct sch_gpio *sch = gpiochip_get_data(gc);
        unsigned short offset, bit;
        u8 reg_val;
 
@@ -98,14 +96,15 @@ static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned gpio_num)
        struct sch_gpio *sch = gpiochip_get_data(gc);
 
        spin_lock(&sch->lock);
-       sch_gpio_reg_set(gc, gpio_num, GIO, 1);
+       sch_gpio_reg_set(sch, gpio_num, GIO, 1);
        spin_unlock(&sch->lock);
        return 0;
 }
 
 static int sch_gpio_get(struct gpio_chip *gc, unsigned gpio_num)
 {
-       return sch_gpio_reg_get(gc, gpio_num, GLV);
+       struct sch_gpio *sch = gpiochip_get_data(gc);
+       return sch_gpio_reg_get(sch, gpio_num, GLV);
 }
 
 static void sch_gpio_set(struct gpio_chip *gc, unsigned gpio_num, int val)
@@ -113,7 +112,7 @@ static void sch_gpio_set(struct gpio_chip *gc, unsigned gpio_num, int val)
        struct sch_gpio *sch = gpiochip_get_data(gc);
 
        spin_lock(&sch->lock);
-       sch_gpio_reg_set(gc, gpio_num, GLV, val);
+       sch_gpio_reg_set(sch, gpio_num, GLV, val);
        spin_unlock(&sch->lock);
 }
 
@@ -123,7 +122,7 @@ static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned gpio_num,
        struct sch_gpio *sch = gpiochip_get_data(gc);
 
        spin_lock(&sch->lock);
-       sch_gpio_reg_set(gc, gpio_num, GIO, 0);
+       sch_gpio_reg_set(sch, gpio_num, GIO, 0);
        spin_unlock(&sch->lock);
 
        /*
@@ -182,13 +181,13 @@ static int sch_gpio_probe(struct platform_device *pdev)
                 * GPIO7 is configured by the CMC as SLPIOVR
                 * Enable GPIO[9:8] core powered gpios explicitly
                 */
-               sch_gpio_reg_set(&sch->chip, 8, GEN, 1);
-               sch_gpio_reg_set(&sch->chip, 9, GEN, 1);
+               sch_gpio_reg_set(sch, 8, GEN, 1);
+               sch_gpio_reg_set(sch, 9, GEN, 1);
                /*
                 * SUS_GPIO[2:0] enabled by default
                 * Enable SUS_GPIO3 resume powered gpio explicitly
                 */
-               sch_gpio_reg_set(&sch->chip, 13, GEN, 1);
+               sch_gpio_reg_set(sch, 13, GEN, 1);
                break;
 
        case PCI_DEVICE_ID_INTEL_ITC_LPC:
index ec891a27952f88e33bd9a7d6f6bfe6e5069f995a..661b0e34e0672eedba9e7896c742944aeda18477 100644 (file)
@@ -98,7 +98,6 @@ struct tegra_gpio_info {
        const struct tegra_gpio_soc_config      *soc;
        struct gpio_chip                        gc;
        struct irq_chip                         ic;
-       struct lock_class_key                   lock_class;
        u32                                     bank_count;
 };
 
@@ -547,6 +546,12 @@ static const struct dev_pm_ops tegra_gpio_pm_ops = {
        SET_SYSTEM_SLEEP_PM_OPS(tegra_gpio_suspend, tegra_gpio_resume)
 };
 
+/*
+ * This lock class tells lockdep that GPIO irqs are in a different category
+ * than their parents, so it won't report false recursion.
+ */
+static struct lock_class_key gpio_lock_class;
+
 static int tegra_gpio_probe(struct platform_device *pdev)
 {
        const struct tegra_gpio_soc_config *config;
@@ -660,7 +665,7 @@ static int tegra_gpio_probe(struct platform_device *pdev)
 
                bank = &tgi->bank_info[GPIO_BANK(gpio)];
 
-               irq_set_lockdep_class(irq, &tgi->lock_class);
+               irq_set_lockdep_class(irq, &gpio_lock_class);
                irq_set_chip_data(irq, bank);
                irq_set_chip_and_handler(irq, &tgi->ic, handle_simple_irq);
        }
index 3a5c7011ad3b3e832d7cce6e55ed62562bba94be..8b830996fe0212d3ae0153ae5b708a84fa53976e 100644 (file)
@@ -28,6 +28,10 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
        if (!desc && gpio_is_valid(gpio))
                return -EPROBE_DEFER;
 
+       err = gpiod_request(desc, label);
+       if (err)
+               return err;
+
        if (flags & GPIOF_OPEN_DRAIN)
                set_bit(FLAG_OPEN_DRAIN, &desc->flags);
 
@@ -37,10 +41,6 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
        if (flags & GPIOF_ACTIVE_LOW)
                set_bit(FLAG_ACTIVE_LOW, &desc->flags);
 
-       err = gpiod_request(desc, label);
-       if (err)
-               return err;
-
        if (flags & GPIOF_DIR_IN)
                err = gpiod_direction_input(desc);
        else
index 570771ed19e6f1f326575fa20cdc621ea2588432..be74bd370f1fc5443586d57076e9c04a80d74e1f 100644 (file)
@@ -1352,14 +1352,6 @@ static int __gpiod_request(struct gpio_desc *desc, const char *label)
                spin_lock_irqsave(&gpio_lock, flags);
        }
 done:
-       if (status < 0) {
-               /* Clear flags that might have been set by the caller before
-                * requesting the GPIO.
-                */
-               clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
-               clear_bit(FLAG_OPEN_DRAIN, &desc->flags);
-               clear_bit(FLAG_OPEN_SOURCE, &desc->flags);
-       }
        spin_unlock_irqrestore(&gpio_lock, flags);
        return status;
 }
@@ -2587,28 +2579,13 @@ struct gpio_desc *__must_check gpiod_get_optional(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(gpiod_get_optional);
 
-/**
- * gpiod_parse_flags - helper function to parse GPIO lookup flags
- * @desc:      gpio to be setup
- * @lflags:    gpio_lookup_flags - returned from of_find_gpio() or
- *             of_get_gpio_hog()
- *
- * Set the GPIO descriptor flags based on the given GPIO lookup flags.
- */
-static void gpiod_parse_flags(struct gpio_desc *desc, unsigned long lflags)
-{
-       if (lflags & GPIO_ACTIVE_LOW)
-               set_bit(FLAG_ACTIVE_LOW, &desc->flags);
-       if (lflags & GPIO_OPEN_DRAIN)
-               set_bit(FLAG_OPEN_DRAIN, &desc->flags);
-       if (lflags & GPIO_OPEN_SOURCE)
-               set_bit(FLAG_OPEN_SOURCE, &desc->flags);
-}
 
 /**
  * gpiod_configure_flags - helper function to configure a given GPIO
  * @desc:      gpio whose value will be assigned
  * @con_id:    function within the GPIO consumer
+ * @lflags:    gpio_lookup_flags - returned from of_find_gpio() or
+ *             of_get_gpio_hog()
  * @dflags:    gpiod_flags - optional GPIO initialization flags
  *
  * Return 0 on success, -ENOENT if no GPIO has been assigned to the
@@ -2616,10 +2593,17 @@ static void gpiod_parse_flags(struct gpio_desc *desc, unsigned long lflags)
  * occurred while trying to acquire the GPIO.
  */
 static int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
-                                enum gpiod_flags dflags)
+               unsigned long lflags, enum gpiod_flags dflags)
 {
        int status;
 
+       if (lflags & GPIO_ACTIVE_LOW)
+               set_bit(FLAG_ACTIVE_LOW, &desc->flags);
+       if (lflags & GPIO_OPEN_DRAIN)
+               set_bit(FLAG_OPEN_DRAIN, &desc->flags);
+       if (lflags & GPIO_OPEN_SOURCE)
+               set_bit(FLAG_OPEN_SOURCE, &desc->flags);
+
        /* No particular flag request, return here... */
        if (!(dflags & GPIOD_FLAGS_BIT_DIR_SET)) {
                pr_debug("no flags found for %s\n", con_id);
@@ -2686,13 +2670,11 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
                return desc;
        }
 
-       gpiod_parse_flags(desc, lookupflags);
-
        status = gpiod_request(desc, con_id);
        if (status < 0)
                return ERR_PTR(status);
 
-       status = gpiod_configure_flags(desc, con_id, flags);
+       status = gpiod_configure_flags(desc, con_id, lookupflags, flags);
        if (status < 0) {
                dev_dbg(dev, "setup of GPIO %s failed\n", con_id);
                gpiod_put(desc);
@@ -2748,6 +2730,10 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
        if (IS_ERR(desc))
                return desc;
 
+       ret = gpiod_request(desc, NULL);
+       if (ret)
+               return ERR_PTR(ret);
+
        if (active_low)
                set_bit(FLAG_ACTIVE_LOW, &desc->flags);
 
@@ -2758,10 +2744,6 @@ struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
                        set_bit(FLAG_OPEN_SOURCE, &desc->flags);
        }
 
-       ret = gpiod_request(desc, NULL);
-       if (ret)
-               return ERR_PTR(ret);
-
        return desc;
 }
 EXPORT_SYMBOL_GPL(fwnode_get_named_gpiod);
@@ -2814,8 +2796,6 @@ int gpiod_hog(struct gpio_desc *desc, const char *name,
        chip = gpiod_to_chip(desc);
        hwnum = gpio_chip_hwgpio(desc);
 
-       gpiod_parse_flags(desc, lflags);
-
        local_desc = gpiochip_request_own_desc(chip, hwnum, name);
        if (IS_ERR(local_desc)) {
                status = PTR_ERR(local_desc);
@@ -2824,7 +2804,7 @@ int gpiod_hog(struct gpio_desc *desc, const char *name,
                return status;
        }
 
-       status = gpiod_configure_flags(desc, name, dflags);
+       status = gpiod_configure_flags(desc, name, lflags, dflags);
        if (status < 0) {
                pr_err("setup of hog GPIO %s (chip %s, offset %d) failed, %d\n",
                       name, chip->label, hwnum, status);
index e19520c4b4b6e3cd6bd4285c008885998e87187f..d9c88d13f8db5efbc958b1571ac844bc091a76f4 100644 (file)
@@ -1106,6 +1106,10 @@ static void amdgpu_uvd_idle_work_handler(struct work_struct *work)
        if (fences == 0 && handles == 0) {
                if (adev->pm.dpm_enabled) {
                        amdgpu_dpm_enable_uvd(adev, false);
+                       /* just work around for uvd clock remain high even
+                        * when uvd dpm disabled on Polaris10 */
+                       if (adev->asic_type == CHIP_POLARIS10)
+                               amdgpu_asic_set_uvd_clocks(adev, 0, 0);
                } else {
                        amdgpu_asic_set_uvd_clocks(adev, 0, 0);
                }
index 13cdb01e9b45022aebf6d052523d718d52ceaa91..bc56c8a181e628b575861233bd705dd986f9f061 100644 (file)
@@ -156,3 +156,18 @@ u32 amdgpu_atombios_i2c_func(struct i2c_adapter *adap)
        return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
 }
 
+void amdgpu_atombios_i2c_channel_trans(struct amdgpu_device* adev, u8 slave_addr, u8 line_number, u8 offset, u8 data)
+{
+       PROCESS_I2C_CHANNEL_TRANSACTION_PS_ALLOCATION args;
+       int index = GetIndexIntoMasterTable(COMMAND, ProcessI2cChannelTransaction);
+
+       args.ucRegIndex = offset;
+       args.lpI2CDataOut = data;
+       args.ucFlag = 1;
+       args.ucI2CSpeed = TARGET_HW_I2C_CLOCK;
+       args.ucTransBytes = 1;
+       args.ucSlaveAddr = slave_addr;
+       args.ucLineNumber = line_number;
+
+       amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
+}
index d6128d9de56e4b9db096de8e07d30d51e2e4a4f2..251aaf41f65d5a277104c3b9fda29fc71bbca680 100644 (file)
@@ -27,5 +27,7 @@
 int amdgpu_atombios_i2c_xfer(struct i2c_adapter *i2c_adap,
                      struct i2c_msg *msgs, int num);
 u32 amdgpu_atombios_i2c_func(struct i2c_adapter *adap);
+void amdgpu_atombios_i2c_channel_trans(struct amdgpu_device* adev,
+               u8 slave_addr, u8 line_number, u8 offset, u8 data);
 
 #endif
index 1a5cbaff1e34ed23d54aaa5e8131c375e58deb69..c2ef94511f7020da5e1ff4dcfa8b8ed854246637 100644 (file)
@@ -28,6 +28,7 @@
 #include "vid.h"
 #include "amdgpu_ucode.h"
 #include "amdgpu_atombios.h"
+#include "atombios_i2c.h"
 #include "clearstate_vi.h"
 
 #include "gmc/gmc_8_2_d.h"
@@ -47,6 +48,8 @@
 #include "dce/dce_10_0_d.h"
 #include "dce/dce_10_0_sh_mask.h"
 
+#include "smu/smu_7_1_3_d.h"
+
 #define GFX8_NUM_GFX_RINGS     1
 #define GFX8_NUM_COMPUTE_RINGS 8
 
@@ -282,6 +285,7 @@ static const u32 golden_settings_polaris11_a11[] =
        mmTCP_ADDR_CONFIG, 0x000003ff, 0x000000f3,
        mmTCP_CHAN_STEER_HI, 0xffffffff, 0x00000000,
        mmTCP_CHAN_STEER_LO, 0xffffffff, 0x00003210,
+       mmVGT_RESET_DEBUG, 0x00000004, 0x00000004,
 };
 
 static const u32 polaris11_golden_common_all[] =
@@ -312,6 +316,7 @@ static const u32 golden_settings_polaris10_a11[] =
        mmTCC_CTRL, 0x00100000, 0xf31fff7f,
        mmTCP_ADDR_CONFIG, 0x000003ff, 0x000000f7,
        mmTCP_CHAN_STEER_HI, 0xffffffff, 0x00000000,
+       mmVGT_RESET_DEBUG, 0x00000004, 0x00000004,
 };
 
 static const u32 polaris10_golden_common_all[] =
@@ -693,6 +698,11 @@ static void gfx_v8_0_init_golden_registers(struct amdgpu_device *adev)
                amdgpu_program_register_sequence(adev,
                                                 polaris10_golden_common_all,
                                                 (const u32)ARRAY_SIZE(polaris10_golden_common_all));
+               WREG32_SMC(ixCG_ACLK_CNTL, 0x0000001C);
+               if (adev->pdev->revision == 0xc7) {
+                       amdgpu_atombios_i2c_channel_trans(adev, 0x10, 0x96, 0x1E, 0xDD);
+                       amdgpu_atombios_i2c_channel_trans(adev, 0x10, 0x96, 0x1F, 0xD0);
+               }
                break;
        case CHIP_CARRIZO:
                amdgpu_program_register_sequence(adev,
index 64ee78f7d41e3c3f22e3808fdb3cbb2711b00011..91e25f942d909b0ac6c07d85632514cef6a28023 100644 (file)
@@ -732,7 +732,7 @@ static int polaris10_populate_smc_mvdd_table(struct pp_hwmgr *hwmgr,
                        table->Smio[level] |=
                                data->mvdd_voltage_table.entries[level].smio_low;
                }
-               table->SmioMask2 = data->vddci_voltage_table.mask_low;
+               table->SmioMask2 = data->mvdd_voltage_table.mask_low;
 
                table->MvddLevelCount = (uint32_t) PP_HOST_TO_SMC_UL(count);
        }
@@ -1422,22 +1422,19 @@ static int polaris10_populate_smc_acpi_level(struct pp_hwmgr *hwmgr,
 
        table->ACPILevel.Flags &= ~PPSMC_SWSTATE_FLAG_DC;
 
-       if (!data->sclk_dpm_key_disabled) {
-               /* Get MinVoltage and Frequency from DPM0,
-                * already converted to SMC_UL */
-               sclk_frequency = data->dpm_table.sclk_table.dpm_levels[0].value;
-               result = polaris10_get_dependency_volt_by_clk(hwmgr,
-                               table_info->vdd_dep_on_sclk,
-                               table->ACPILevel.SclkFrequency,
-                               &table->ACPILevel.MinVoltage, &mvdd);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "Cannot find ACPI VDDC voltage value "
-                               "in Clock Dependency Table", );
-       } else {
-               sclk_frequency = data->vbios_boot_state.sclk_bootup_value;
-               table->ACPILevel.MinVoltage =
-                               data->vbios_boot_state.vddc_bootup_value * VOLTAGE_SCALE;
-       }
+
+       /* Get MinVoltage and Frequency from DPM0,
+        * already converted to SMC_UL */
+       sclk_frequency = data->dpm_table.sclk_table.dpm_levels[0].value;
+       result = polaris10_get_dependency_volt_by_clk(hwmgr,
+                       table_info->vdd_dep_on_sclk,
+                       sclk_frequency,
+                       &table->ACPILevel.MinVoltage, &mvdd);
+       PP_ASSERT_WITH_CODE((0 == result),
+                       "Cannot find ACPI VDDC voltage value "
+                       "in Clock Dependency Table",
+                       );
+
 
        result = polaris10_calculate_sclk_params(hwmgr, sclk_frequency,  &(table->ACPILevel.SclkSetting));
        PP_ASSERT_WITH_CODE(result == 0, "Error retrieving Engine Clock dividers from VBIOS.", return result);
@@ -1462,24 +1459,18 @@ static int polaris10_populate_smc_acpi_level(struct pp_hwmgr *hwmgr,
        CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Fcw1_frac);
        CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Sclk_ss_slew_rate);
 
-       if (!data->mclk_dpm_key_disabled) {
-               /* Get MinVoltage and Frequency from DPM0, already converted to SMC_UL */
-               table->MemoryACPILevel.MclkFrequency =
-                               data->dpm_table.mclk_table.dpm_levels[0].value;
-               result = polaris10_get_dependency_volt_by_clk(hwmgr,
-                               table_info->vdd_dep_on_mclk,
-                               table->MemoryACPILevel.MclkFrequency,
-                               &table->MemoryACPILevel.MinVoltage, &mvdd);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "Cannot find ACPI VDDCI voltage value "
-                               "in Clock Dependency Table",
-                               );
-       } else {
-               table->MemoryACPILevel.MclkFrequency =
-                               data->vbios_boot_state.mclk_bootup_value;
-               table->MemoryACPILevel.MinVoltage =
-                               data->vbios_boot_state.vddci_bootup_value * VOLTAGE_SCALE;
-       }
+
+       /* Get MinVoltage and Frequency from DPM0, already converted to SMC_UL */
+       table->MemoryACPILevel.MclkFrequency =
+                       data->dpm_table.mclk_table.dpm_levels[0].value;
+       result = polaris10_get_dependency_volt_by_clk(hwmgr,
+                       table_info->vdd_dep_on_mclk,
+                       table->MemoryACPILevel.MclkFrequency,
+                       &table->MemoryACPILevel.MinVoltage, &mvdd);
+       PP_ASSERT_WITH_CODE((0 == result),
+                       "Cannot find ACPI VDDCI voltage value "
+                       "in Clock Dependency Table",
+                       );
 
        us_mvdd = 0;
        if ((POLARIS10_VOLTAGE_CONTROL_NONE == data->mvdd_control) ||
@@ -1524,6 +1515,7 @@ static int polaris10_populate_smc_vce_level(struct pp_hwmgr *hwmgr,
        struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
                        table_info->mm_dep_table;
        struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
+       uint32_t vddci;
 
        table->VceLevelCount = (uint8_t)(mm_table->count);
        table->VceBootLevel = 0;
@@ -1533,9 +1525,18 @@ static int polaris10_populate_smc_vce_level(struct pp_hwmgr *hwmgr,
                table->VceLevel[count].MinVoltage = 0;
                table->VceLevel[count].MinVoltage |=
                                (mm_table->entries[count].vddc * VOLTAGE_SCALE) << VDDC_SHIFT;
+
+               if (POLARIS10_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control)
+                       vddci = (uint32_t)phm_find_closest_vddci(&(data->vddci_voltage_table),
+                                               mm_table->entries[count].vddc - VDDC_VDDCI_DELTA);
+               else if (POLARIS10_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control)
+                       vddci = mm_table->entries[count].vddc - VDDC_VDDCI_DELTA;
+               else
+                       vddci = (data->vbios_boot_state.vddci_bootup_value * VOLTAGE_SCALE) << VDDCI_SHIFT;
+
+
                table->VceLevel[count].MinVoltage |=
-                               ((mm_table->entries[count].vddc - data->vddc_vddci_delta) *
-                                               VOLTAGE_SCALE) << VDDCI_SHIFT;
+                               (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
                table->VceLevel[count].MinVoltage |= 1 << PHASES_SHIFT;
 
                /*retrieve divider value for VBIOS */
@@ -1564,6 +1565,7 @@ static int polaris10_populate_smc_samu_level(struct pp_hwmgr *hwmgr,
        struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
                        table_info->mm_dep_table;
        struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
+       uint32_t vddci;
 
        table->SamuBootLevel = 0;
        table->SamuLevelCount = (uint8_t)(mm_table->count);
@@ -1574,8 +1576,16 @@ static int polaris10_populate_smc_samu_level(struct pp_hwmgr *hwmgr,
                table->SamuLevel[count].Frequency = mm_table->entries[count].samclock;
                table->SamuLevel[count].MinVoltage |= (mm_table->entries[count].vddc *
                                VOLTAGE_SCALE) << VDDC_SHIFT;
-               table->SamuLevel[count].MinVoltage |= ((mm_table->entries[count].vddc -
-                               data->vddc_vddci_delta) * VOLTAGE_SCALE) << VDDCI_SHIFT;
+
+               if (POLARIS10_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control)
+                       vddci = (uint32_t)phm_find_closest_vddci(&(data->vddci_voltage_table),
+                                               mm_table->entries[count].vddc - VDDC_VDDCI_DELTA);
+               else if (POLARIS10_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control)
+                       vddci = mm_table->entries[count].vddc - VDDC_VDDCI_DELTA;
+               else
+                       vddci = (data->vbios_boot_state.vddci_bootup_value * VOLTAGE_SCALE) << VDDCI_SHIFT;
+
+               table->SamuLevel[count].MinVoltage |= (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
                table->SamuLevel[count].MinVoltage |= 1 << PHASES_SHIFT;
 
                /* retrieve divider value for VBIOS */
@@ -1658,6 +1668,7 @@ static int polaris10_populate_smc_uvd_level(struct pp_hwmgr *hwmgr,
        struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
                        table_info->mm_dep_table;
        struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
+       uint32_t vddci;
 
        table->UvdLevelCount = (uint8_t)(mm_table->count);
        table->UvdBootLevel = 0;
@@ -1668,8 +1679,16 @@ static int polaris10_populate_smc_uvd_level(struct pp_hwmgr *hwmgr,
                table->UvdLevel[count].DclkFrequency = mm_table->entries[count].dclk;
                table->UvdLevel[count].MinVoltage |= (mm_table->entries[count].vddc *
                                VOLTAGE_SCALE) << VDDC_SHIFT;
-               table->UvdLevel[count].MinVoltage |= ((mm_table->entries[count].vddc -
-                               data->vddc_vddci_delta) * VOLTAGE_SCALE) << VDDCI_SHIFT;
+
+               if (POLARIS10_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control)
+                       vddci = (uint32_t)phm_find_closest_vddci(&(data->vddci_voltage_table),
+                                               mm_table->entries[count].vddc - VDDC_VDDCI_DELTA);
+               else if (POLARIS10_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control)
+                       vddci = mm_table->entries[count].vddc - VDDC_VDDCI_DELTA;
+               else
+                       vddci = (data->vbios_boot_state.vddci_bootup_value * VOLTAGE_SCALE) << VDDCI_SHIFT;
+
+               table->UvdLevel[count].MinVoltage |= (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
                table->UvdLevel[count].MinVoltage |= 1 << PHASES_SHIFT;
 
                /* retrieve divider value for VBIOS */
@@ -1690,8 +1709,8 @@ static int polaris10_populate_smc_uvd_level(struct pp_hwmgr *hwmgr,
                CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].VclkFrequency);
                CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].DclkFrequency);
                CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].MinVoltage);
-
        }
+
        return result;
 }
 
@@ -1791,20 +1810,26 @@ static int polaris10_populate_clock_stretcher_data_table(struct pp_hwmgr *hwmgr)
        for (i = 0; i < sclk_table->count; i++) {
                data->smc_state_table.Sclk_CKS_masterEn0_7 |=
                                sclk_table->entries[i].cks_enable << i;
-
-               volt_without_cks =  (uint32_t)(((ro - 40) * 1000 - 2753594 - sclk_table->entries[i].clk/100 * 136418 /1000) / \
-                                       (sclk_table->entries[i].clk/100 * 1132925 /10000 - 242418)/100);
-
-               volt_with_cks = (uint32_t)((ro * 1000 -2396351 - sclk_table->entries[i].clk/100 * 329021/1000) / \
-                               (sclk_table->entries[i].clk/10000 * 649434 /1000  - 18005)/10);
+               if (hwmgr->chip_id == CHIP_POLARIS10) {
+                       volt_without_cks = (uint32_t)((2753594000U + (sclk_table->entries[i].clk/100) * 136418 -(ro - 70) * 1000000) / \
+                                               (2424180 - (sclk_table->entries[i].clk/100) * 1132925/1000));
+                       volt_with_cks = (uint32_t)((2797202000U + sclk_table->entries[i].clk/100 * 3232 - (ro - 65) * 1000000) / \
+                                       (2522480 - sclk_table->entries[i].clk/100 * 115764/100));
+               } else {
+                       volt_without_cks = (uint32_t)((2416794800U + (sclk_table->entries[i].clk/100) * 1476925/10 -(ro - 50) * 1000000) / \
+                                               (2625416 - (sclk_table->entries[i].clk/100) * (12586807/10000)));
+                       volt_with_cks = (uint32_t)((2999656000U - sclk_table->entries[i].clk/100 * 392803 - (ro - 44) * 1000000) / \
+                                       (3422454 - sclk_table->entries[i].clk/100 * (18886376/10000)));
+               }
 
                if (volt_without_cks >= volt_with_cks)
                        volt_offset = (uint8_t)(((volt_without_cks - volt_with_cks +
-                                       sclk_table->entries[i].cks_voffset) * 100 / 625) + 1);
+                                       sclk_table->entries[i].cks_voffset) * 100 + 624) / 625);
 
                data->smc_state_table.Sclk_voltageOffset[i] = volt_offset;
        }
 
+       data->smc_state_table.LdoRefSel = (table_info->cac_dtp_table->ucCKS_LDO_REFSEL != 0) ? table_info->cac_dtp_table->ucCKS_LDO_REFSEL : 6;
        /* Populate CKS Lookup Table */
        if (stretch_amount == 1 || stretch_amount == 2 || stretch_amount == 5)
                stretch_amount2 = 0;
@@ -2487,6 +2512,8 @@ int polaris10_enable_dpm_tasks(struct pp_hwmgr *hwmgr)
        PP_ASSERT_WITH_CODE((0 == tmp_result),
                        "Failed to enable VR hot GPIO interrupt!", result = tmp_result);
 
+       smum_send_msg_to_smc(hwmgr->smumgr, (PPSMC_Msg)PPSMC_HasDisplay);
+
        tmp_result = polaris10_enable_sclk_control(hwmgr);
        PP_ASSERT_WITH_CODE((0 == tmp_result),
                        "Failed to enable SCLK control!", result = tmp_result);
@@ -2655,7 +2682,7 @@ static int polaris10_get_evv_voltages(struct pp_hwmgr *hwmgr)
 {
        struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
        uint16_t vv_id;
-       uint16_t vddc = 0;
+       uint32_t vddc = 0;
        uint16_t i, j;
        uint32_t sclk = 0;
        struct phm_ppt_v1_information *table_info =
@@ -2686,8 +2713,9 @@ static int polaris10_get_evv_voltages(struct pp_hwmgr *hwmgr)
                                                continue);
 
 
-                       /* need to make sure vddc is less than 2v or else, it could burn the ASIC. */
-                       PP_ASSERT_WITH_CODE((vddc < 2000 && vddc != 0),
+                       /* need to make sure vddc is less than 2v or else, it could burn the ASIC.
+                        * real voltage level in unit of 0.01mv */
+                       PP_ASSERT_WITH_CODE((vddc < 200000 && vddc != 0),
                                        "Invalid VDDC value", result = -EINVAL;);
 
                        /* the voltage should not be zero nor equal to leakage ID */
@@ -2913,6 +2941,31 @@ static int polaris10_set_private_data_based_on_pptable(struct pp_hwmgr *hwmgr)
        return 0;
 }
 
+int polaris10_patch_voltage_workaround(struct pp_hwmgr *hwmgr)
+{
+       struct phm_ppt_v1_information *table_info =
+                      (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct phm_ppt_v1_clock_voltage_dependency_table *dep_mclk_table =
+                       table_info->vdd_dep_on_mclk;
+       struct phm_ppt_v1_voltage_lookup_table *lookup_table =
+                       table_info->vddc_lookup_table;
+       uint32_t i;
+
+       if (hwmgr->chip_id == CHIP_POLARIS10 && hwmgr->hw_revision == 0xC7) {
+               if (lookup_table->entries[dep_mclk_table->entries[dep_mclk_table->count-1].vddInd].us_vdd >= 1000)
+                       return 0;
+
+               for (i = 0; i < lookup_table->count; i++) {
+                       if (lookup_table->entries[i].us_vdd < 0xff01 && lookup_table->entries[i].us_vdd >= 1000) {
+                               dep_mclk_table->entries[dep_mclk_table->count-1].vddInd = (uint8_t) i;
+                               return 0;
+                       }
+               }
+       }
+       return 0;
+}
+
+
 int polaris10_hwmgr_backend_init(struct pp_hwmgr *hwmgr)
 {
        struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
@@ -2990,6 +3043,7 @@ int polaris10_hwmgr_backend_init(struct pp_hwmgr *hwmgr)
 
        polaris10_set_features_platform_caps(hwmgr);
 
+       polaris10_patch_voltage_workaround(hwmgr);
        polaris10_init_dpm_defaults(hwmgr);
 
        /* Get leakage voltage based on leakage ID. */
@@ -4359,6 +4413,15 @@ static int polaris10_notify_link_speed_change_after_state_change(
        return 0;
 }
 
+static int polaris10_notify_smc_display(struct pp_hwmgr *hwmgr)
+{
+       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
+
+       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+               (PPSMC_Msg)PPSMC_MSG_SetVBITimeout, data->frame_time_x2);
+       return (smum_send_msg_to_smc(hwmgr->smumgr, (PPSMC_Msg)PPSMC_HasDisplay) == 0) ?  0 : -EINVAL;
+}
+
 static int polaris10_set_power_state_tasks(struct pp_hwmgr *hwmgr, const void *input)
 {
        int tmp_result, result = 0;
@@ -4407,6 +4470,11 @@ static int polaris10_set_power_state_tasks(struct pp_hwmgr *hwmgr, const void *i
                        "Failed to program memory timing parameters!",
                        result = tmp_result);
 
+       tmp_result = polaris10_notify_smc_display(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to notify smc display settings!",
+                       result = tmp_result);
+
        tmp_result = polaris10_unfreeze_sclk_mclk_dpm(hwmgr);
        PP_ASSERT_WITH_CODE((0 == tmp_result),
                        "Failed to unfreeze SCLK MCLK DPM!",
@@ -4441,6 +4509,7 @@ static int polaris10_set_max_fan_pwm_output(struct pp_hwmgr *hwmgr, uint16_t us_
                        PPSMC_MSG_SetFanPwmMax, us_max_fan_pwm);
 }
 
+
 int polaris10_notify_smc_display_change(struct pp_hwmgr *hwmgr, bool has_display)
 {
        PPSMC_Msg msg = has_display ? (PPSMC_Msg)PPSMC_HasDisplay : (PPSMC_Msg)PPSMC_NoDisplay;
@@ -4460,8 +4529,6 @@ int polaris10_notify_smc_display_config_after_ps_adjustment(struct pp_hwmgr *hwm
 
        if (num_active_displays > 1)  /* to do && (pHwMgr->pPECI->displayConfiguration.bMultiMonitorInSync != TRUE)) */
                polaris10_notify_smc_display_change(hwmgr, false);
-       else
-               polaris10_notify_smc_display_change(hwmgr, true);
 
        return 0;
 }
@@ -4502,6 +4569,8 @@ int polaris10_program_display_gap(struct pp_hwmgr *hwmgr)
        frame_time_in_us = 1000000 / refresh_rate;
 
        pre_vbi_time_in_us = frame_time_in_us - 200 - mode_info.vblank_time_us;
+       data->frame_time_x2 = frame_time_in_us * 2 / 100;
+
        display_gap2 = pre_vbi_time_in_us * (ref_clock / 100);
 
        cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_DISPLAY_GAP_CNTL2, display_gap2);
@@ -4510,8 +4579,6 @@ int polaris10_program_display_gap(struct pp_hwmgr *hwmgr)
 
        cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, data->soft_regs_start + offsetof(SMU74_SoftRegisters, VBlankTimeout), (frame_time_in_us - pre_vbi_time_in_us));
 
-       polaris10_notify_smc_display_change(hwmgr, num_active_displays != 0);
-
        return 0;
 }
 
@@ -4623,7 +4690,7 @@ int polaris10_upload_mc_firmware(struct pp_hwmgr *hwmgr)
                return 0;
        }
 
-       data->need_long_memory_training = true;
+       data->need_long_memory_training = false;
 
 /*
  *     PPMCME_FirmwareDescriptorEntry *pfd = NULL;
index d717789441f5bd52c37b7a9aa253edd84353cd8b..afc3434822d1589224ebfccc1b2826f4fedd9ffb 100644 (file)
@@ -315,6 +315,7 @@ struct polaris10_hwmgr {
 
        uint32_t                              avfs_vdroop_override_setting;
        bool                                  apply_avfs_cks_off_voltage;
+       uint32_t                              frame_time_x2;
 };
 
 /* To convert to Q8.8 format for firmware */
index bf4e18fd38724e3f285a58a4e7595d481fc33411..90b35c5c10a4f01b0c584ccb3dbb30c482208470 100644 (file)
@@ -1256,7 +1256,7 @@ int atomctrl_set_ac_timing_ai(struct pp_hwmgr *hwmgr, uint32_t memory_clock,
 }
 
 int atomctrl_get_voltage_evv_on_sclk_ai(struct pp_hwmgr *hwmgr, uint8_t voltage_type,
-                               uint32_t sclk, uint16_t virtual_voltage_Id, uint16_t *voltage)
+                               uint32_t sclk, uint16_t virtual_voltage_Id, uint32_t *voltage)
 {
 
        int result;
@@ -1274,7 +1274,7 @@ int atomctrl_get_voltage_evv_on_sclk_ai(struct pp_hwmgr *hwmgr, uint8_t voltage_
        if (0 != result)
                return result;
 
-       *voltage = get_voltage_info_param_space.usVoltageLevel;
+       *voltage = ((GET_EVV_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_3 *)(&get_voltage_info_param_space))->ulVoltageLevel;
 
        return result;
 }
index 248c5db5f38025559d345d311f1cfe3d7e1eb1ad..1e35a9625baf913ab8733604ad3a67a4e86ee2e4 100644 (file)
@@ -305,7 +305,7 @@ extern int atomctrl_get_engine_pll_dividers_ai(struct pp_hwmgr *hwmgr, uint32_t
 extern int atomctrl_set_ac_timing_ai(struct pp_hwmgr *hwmgr, uint32_t memory_clock,
                                                                uint8_t level);
 extern int atomctrl_get_voltage_evv_on_sclk_ai(struct pp_hwmgr *hwmgr, uint8_t voltage_type,
-                               uint32_t sclk, uint16_t virtual_voltage_Id, uint16_t *voltage);
+                               uint32_t sclk, uint16_t virtual_voltage_Id, uint32_t *voltage);
 extern int atomctrl_get_smc_sclk_range_table(struct pp_hwmgr *hwmgr, struct pp_atom_ctrl_sclk_range_table *table);
 
 extern int atomctrl_get_avfs_information(struct pp_hwmgr *hwmgr, struct pp_atom_ctrl__avfs_parameters *param);
index 233eb7f36c1da19e76689d430f83e4cc12e1fc9d..5d0f655bf160b85582383d8fcca771af00d43ae7 100644 (file)
@@ -1302,7 +1302,7 @@ static int tonga_populate_smc_mvdd_table(struct pp_hwmgr *hwmgr,
                        table->Smio[count] |=
                                data->mvdd_voltage_table.entries[count].smio_low;
                }
-               table->SmioMask2 = data->vddci_voltage_table.mask_low;
+               table->SmioMask2 = data->mvdd_voltage_table.mask_low;
 
                CONVERT_FROM_HOST_TO_SMC_UL(table->MvddLevelCount);
        }
index 671fdb4d615a4812d624c30daa20e8192e159346..dccc859f638c2b8f4769e4f699f7742ca9432240 100644 (file)
@@ -302,7 +302,7 @@ static int init_dpm_2_parameters(
                        (((unsigned long)powerplay_table) + le16_to_cpu(powerplay_table->usPPMTableOffset));
 
                if (0 != powerplay_table->usPPMTableOffset) {
-                       if (1 == get_platform_power_management_table(hwmgr, atom_ppm_table)) {
+                       if (get_platform_power_management_table(hwmgr, atom_ppm_table) == 0) {
                                phm_cap_set(hwmgr->platform_descriptor.platformCaps,
                                        PHM_PlatformCaps_EnablePlatformPowerManagement);
                        }
index 28f571449495d6e639c6135d4394d8eedaea7587..77e8e33d58702910fbdc7a57e5e848107c69c758 100644 (file)
@@ -411,6 +411,8 @@ struct phm_cac_tdp_table {
        uint8_t  ucVr_I2C_Line;
        uint8_t  ucPlx_I2C_address;
        uint8_t  ucPlx_I2C_Line;
+       uint32_t usBoostPowerLimit;
+       uint8_t  ucCKS_LDO_REFSEL;
 };
 
 struct phm_ppm_table {
index d41d37ab5b7c59f586f32c3133240230de93d597..b8f4b73c322e1b0a501cef8a774b0c4265f15cb6 100644 (file)
@@ -392,6 +392,8 @@ typedef uint16_t PPSMC_Result;
 #define PPSMC_MSG_SetGpuPllDfsForSclk         ((uint16_t) 0x300)
 #define PPSMC_MSG_Didt_Block_Function            ((uint16_t) 0x301)
 
+#define PPSMC_MSG_SetVBITimeout               ((uint16_t) 0x306)
+
 #define PPSMC_MSG_SecureSRBMWrite             ((uint16_t) 0x600)
 #define PPSMC_MSG_SecureSRBMRead              ((uint16_t) 0x601)
 #define PPSMC_MSG_SetAddress                  ((uint16_t) 0x800)
index b85ff5400e57eb2ca78a2e1500bfbf8369f57cf6..899d6d8108c210fb6b5548f3f965e29a02b80f8b 100644 (file)
@@ -270,7 +270,8 @@ struct SMU74_Discrete_DpmTable {
        uint8_t                             BootPhases;
 
        uint8_t                             VRHotLevel;
-       uint8_t                             Reserved1[3];
+       uint8_t                             LdoRefSel;
+       uint8_t                             Reserved1[2];
        uint16_t                            FanStartTemperature;
        uint16_t                            FanStopTemperature;
        uint16_t                            MaxVoltage;
index 32690332d441dc16b7ba51bd641add5e984cc52e..103546834b60d9f9af9043a2c6881c5a08d1388d 100644 (file)
@@ -2365,16 +2365,16 @@ static int i915_ppgtt_info(struct seq_file *m, void *data)
                task = get_pid_task(file->pid, PIDTYPE_PID);
                if (!task) {
                        ret = -ESRCH;
-                       goto out_put;
+                       goto out_unlock;
                }
                seq_printf(m, "\nproc: %s\n", task->comm);
                put_task_struct(task);
                idr_for_each(&file_priv->context_idr, per_file_ctx,
                             (void *)(unsigned long)m);
        }
+out_unlock:
        mutex_unlock(&dev->filelist_mutex);
 
-out_put:
        intel_runtime_pm_put(dev_priv);
        mutex_unlock(&dev->struct_mutex);
 
index f313b4d8344f4e6abf8f0784ae9bf98abdc5eb9e..85c4debf47e04ebc6ed5300a2974b037a227376d 100644 (file)
@@ -512,6 +512,10 @@ void intel_detect_pch(struct drm_device *dev)
                                DRM_DEBUG_KMS("Found SunrisePoint LP PCH\n");
                                WARN_ON(!IS_SKYLAKE(dev) &&
                                        !IS_KABYLAKE(dev));
+                       } else if (id == INTEL_PCH_KBP_DEVICE_ID_TYPE) {
+                               dev_priv->pch_type = PCH_KBP;
+                               DRM_DEBUG_KMS("Found KabyPoint PCH\n");
+                               WARN_ON(!IS_KABYLAKE(dev));
                        } else if ((id == INTEL_PCH_P2X_DEVICE_ID_TYPE) ||
                                   (id == INTEL_PCH_P3X_DEVICE_ID_TYPE) ||
                                   ((id == INTEL_PCH_QEMU_DEVICE_ID_TYPE) &&
index 7c334e9022660340fb826ff627368dab7b594f00..bc3f2e6842e7be13771fa4b638c9cc85f36ad359 100644 (file)
@@ -990,6 +990,7 @@ enum intel_pch {
        PCH_CPT,        /* Cougarpoint PCH */
        PCH_LPT,        /* Lynxpoint PCH */
        PCH_SPT,        /* Sunrisepoint PCH */
+       PCH_KBP,        /* Kabypoint PCH */
        PCH_NOP,
 };
 
@@ -2600,6 +2601,15 @@ struct drm_i915_cmd_table {
 
 #define IS_BXT_REVID(p, since, until) (IS_BROXTON(p) && IS_REVID(p, since, until))
 
+#define KBL_REVID_A0           0x0
+#define KBL_REVID_B0           0x1
+#define KBL_REVID_C0           0x2
+#define KBL_REVID_D0           0x3
+#define KBL_REVID_E0           0x4
+
+#define IS_KBL_REVID(p, since, until) \
+       (IS_KABYLAKE(p) && IS_REVID(p, since, until))
+
 /*
  * The genX designation typically refers to the render engine, so render
  * capability related checks should use IS_GEN, while display and other checks
@@ -2708,11 +2718,13 @@ struct drm_i915_cmd_table {
 #define INTEL_PCH_LPT_LP_DEVICE_ID_TYPE                0x9c00
 #define INTEL_PCH_SPT_DEVICE_ID_TYPE           0xA100
 #define INTEL_PCH_SPT_LP_DEVICE_ID_TYPE                0x9D00
+#define INTEL_PCH_KBP_DEVICE_ID_TYPE           0xA200
 #define INTEL_PCH_P2X_DEVICE_ID_TYPE           0x7100
 #define INTEL_PCH_P3X_DEVICE_ID_TYPE           0x7000
 #define INTEL_PCH_QEMU_DEVICE_ID_TYPE          0x2900 /* qemu q35 has 2918 */
 
 #define INTEL_PCH_TYPE(dev) (__I915__(dev)->pch_type)
+#define HAS_PCH_KBP(dev) (INTEL_PCH_TYPE(dev) == PCH_KBP)
 #define HAS_PCH_SPT(dev) (INTEL_PCH_TYPE(dev) == PCH_SPT)
 #define HAS_PCH_LPT(dev) (INTEL_PCH_TYPE(dev) == PCH_LPT)
 #define HAS_PCH_LPT_LP(dev) (__I915__(dev)->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE)
index 425e721aac58e5cd0d7dd3d80f984f118fcfc960..66571466e9a88d6380ddc0f6d668c4240488d90a 100644 (file)
@@ -40,7 +40,7 @@ static bool mutex_is_locked_by(struct mutex *mutex, struct task_struct *task)
        if (!mutex_is_locked(mutex))
                return false;
 
-#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_MUTEXES)
+#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_MUTEX_SPIN_ON_OWNER)
        return mutex->owner == task;
 #else
        /* Since UP may be pre-empted, we cannot assume that we own the lock */
index b7ce963fb8f8d23345cab6cf0b6e4e446e6e1b3c..44004e3f09e446d108f635444ed9c67818deed7f 100644 (file)
@@ -55,8 +55,10 @@ int i915_gem_stolen_insert_node_in_range(struct drm_i915_private *dev_priv,
                return -ENODEV;
 
        /* See the comment at the drm_mm_init() call for more about this check.
-        * WaSkipStolenMemoryFirstPage:bdw,chv (incomplete) */
-       if (INTEL_INFO(dev_priv)->gen == 8 && start < 4096)
+        * WaSkipStolenMemoryFirstPage:bdw,chv,kbl (incomplete)
+        */
+       if (start < 4096 && (IS_GEN8(dev_priv) ||
+                            IS_KBL_REVID(dev_priv, 0, KBL_REVID_A0)))
                start = 4096;
 
        mutex_lock(&dev_priv->mm.stolen_lock);
index 2f6fd33c07ba2aaabbce9764cf4a66178637199c..aab47f7bb61b9aae2478cc22e03a8406ea6cd64c 100644 (file)
@@ -2471,7 +2471,7 @@ gen8_de_irq_handler(struct drm_i915_private *dev_priv, u32 master_ctl)
                        I915_WRITE(SDEIIR, iir);
                        ret = IRQ_HANDLED;
 
-                       if (HAS_PCH_SPT(dev_priv))
+                       if (HAS_PCH_SPT(dev_priv) || HAS_PCH_KBP(dev_priv))
                                spt_irq_handler(dev, iir);
                        else
                                cpt_irq_handler(dev, iir);
@@ -4661,7 +4661,7 @@ void intel_irq_init(struct drm_i915_private *dev_priv)
                dev->driver->disable_vblank = gen8_disable_vblank;
                if (IS_BROXTON(dev))
                        dev_priv->display.hpd_irq_setup = bxt_hpd_irq_setup;
-               else if (HAS_PCH_SPT(dev))
+               else if (HAS_PCH_SPT(dev) || HAS_PCH_KBP(dev))
                        dev_priv->display.hpd_irq_setup = spt_hpd_irq_setup;
                else
                        dev_priv->display.hpd_irq_setup = ilk_hpd_irq_setup;
index b407411e31ba8e649ccbec2255888a976fffde2c..3fcf7dd5b6ca5585a48f921d287b863b126a80dd 100644 (file)
@@ -220,6 +220,9 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
 #define   ECOCHK_PPGTT_WT_HSW          (0x2<<3)
 #define   ECOCHK_PPGTT_WB_HSW          (0x3<<3)
 
+#define GEN8_CONFIG0                   _MMIO(0xD00)
+#define  GEN9_DEFAULT_FIXES            (1 << 3 | 1 << 2 | 1 << 1)
+
 #define GAC_ECO_BITS                   _MMIO(0x14090)
 #define   ECOBITS_SNB_BIT              (1<<13)
 #define   ECOBITS_PPGTT_CACHE64B       (3<<8)
@@ -1669,6 +1672,9 @@ enum skl_disp_power_wells {
 
 #define GEN7_TLB_RD_ADDR       _MMIO(0x4700)
 
+#define GAMT_CHKN_BIT_REG      _MMIO(0x4ab8)
+#define   GAMT_CHKN_DISABLE_DYNAMIC_CREDIT_SHARING     (1<<28)
+
 #if 0
 #define PRB0_TAIL      _MMIO(0x2030)
 #define PRB0_HEAD      _MMIO(0x2034)
@@ -1804,6 +1810,10 @@ enum skl_disp_power_wells {
 #define   GEN9_IZ_HASHING_MASK(slice)                  (0x3 << ((slice) * 2))
 #define   GEN9_IZ_HASHING(slice, val)                  ((val) << ((slice) * 2))
 
+/* chicken reg for WaConextSwitchWithConcurrentTLBInvalidate */
+#define GEN9_CSFE_CHICKEN1_RCS _MMIO(0x20D4)
+#define   GEN9_PREEMPT_GPGPU_SYNC_SWITCH_DISABLE (1 << 2)
+
 /* WaClearTdlStateAckDirtyBits */
 #define GEN8_STATE_ACK         _MMIO(0x20F0)
 #define GEN9_STATE_ACK_SLICE1  _MMIO(0x20F8)
@@ -2200,6 +2210,8 @@ enum skl_disp_power_wells {
 #define ILK_DPFC_STATUS                _MMIO(0x43210)
 #define ILK_DPFC_FENCE_YOFF    _MMIO(0x43218)
 #define ILK_DPFC_CHICKEN       _MMIO(0x43224)
+#define   ILK_DPFC_DISABLE_DUMMY0 (1<<8)
+#define   ILK_DPFC_NUKE_ON_ANY_MODIFICATION    (1<<23)
 #define ILK_FBC_RT_BASE                _MMIO(0x2128)
 #define   ILK_FBC_RT_VALID     (1<<0)
 #define   SNB_FBC_FRONT_BUFFER (1<<1)
@@ -6031,6 +6043,7 @@ enum skl_disp_power_wells {
 #define CHICKEN_PAR1_1         _MMIO(0x42080)
 #define  DPA_MASK_VBLANK_SRD   (1 << 15)
 #define  FORCE_ARB_IDLE_PLANES (1 << 14)
+#define  SKL_EDP_PSR_FIX_RDWRAP        (1 << 3)
 
 #define _CHICKEN_PIPESL_1_A    0x420b0
 #define _CHICKEN_PIPESL_1_B    0x420b4
@@ -6039,6 +6052,7 @@ enum skl_disp_power_wells {
 #define CHICKEN_PIPESL_1(pipe) _MMIO_PIPE(pipe, _CHICKEN_PIPESL_1_A, _CHICKEN_PIPESL_1_B)
 
 #define DISP_ARB_CTL   _MMIO(0x45000)
+#define  DISP_FBC_MEMORY_WAKE          (1<<31)
 #define  DISP_TILE_SURFACE_SWIZZLING   (1<<13)
 #define  DISP_FBC_WM_DIS               (1<<15)
 #define DISP_ARB_CTL2  _MMIO(0x45004)
@@ -6052,6 +6066,9 @@ enum skl_disp_power_wells {
 #define HSW_NDE_RSTWRN_OPT     _MMIO(0x46408)
 #define  RESET_PCH_HANDSHAKE_ENABLE    (1<<4)
 
+#define GEN8_CHICKEN_DCPR_1            _MMIO(0x46430)
+#define   MASK_WAKEMEM                 (1<<13)
+
 #define SKL_DFSM                       _MMIO(0x51000)
 #define SKL_DFSM_CDCLK_LIMIT_MASK      (3 << 23)
 #define SKL_DFSM_CDCLK_LIMIT_675       (0 << 23)
@@ -6069,6 +6086,7 @@ enum skl_disp_power_wells {
 #define  GEN9_TSG_BARRIER_ACK_DISABLE          (1<<8)
 
 #define GEN9_CS_DEBUG_MODE1            _MMIO(0x20ec)
+#define GEN9_CTX_PREEMPT_REG           _MMIO(0x2248)
 #define GEN8_CS_CHICKEN1               _MMIO(0x2580)
 
 /* GEN7 chicken */
@@ -6076,6 +6094,7 @@ enum skl_disp_power_wells {
 # define GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC     ((1<<10) | (1<<26))
 # define GEN9_RHWO_OPTIMIZATION_DISABLE                (1<<14)
 #define COMMON_SLICE_CHICKEN2                  _MMIO(0x7014)
+# define GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION (1<<8)
 # define GEN8_CSC2_SBE_VUE_CACHE_CONSERVATIVE  (1<<0)
 
 #define HIZ_CHICKEN                                    _MMIO(0x7018)
@@ -6921,6 +6940,7 @@ enum skl_disp_power_wells {
 #define    EDRAM_SETS_IDX(cap)                 (((cap) >> 8) & 0x3)
 
 #define GEN6_UCGCTL1                           _MMIO(0x9400)
+# define GEN6_GAMUNIT_CLOCK_GATE_DISABLE               (1 << 22)
 # define GEN6_EU_TCUNIT_CLOCK_GATE_DISABLE             (1 << 16)
 # define GEN6_BLBUNIT_CLOCK_GATE_DISABLE               (1 << 5)
 # define GEN6_CSUNIT_CLOCK_GATE_DISABLE                        (1 << 7)
@@ -6937,6 +6957,7 @@ enum skl_disp_power_wells {
 
 #define GEN7_UCGCTL4                           _MMIO(0x940c)
 #define  GEN7_L3BANK2X_CLOCK_GATE_DISABLE      (1<<25)
+#define  GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE     (1<<14)
 
 #define GEN6_RCGCTL1                           _MMIO(0x9410)
 #define GEN6_RCGCTL2                           _MMIO(0x9414)
index a34c23eceba0448bb93b5afdb338a82db90e179f..2b3b428d9cd2b01e4d5d3627e89e712b18937547 100644 (file)
  * be moved to FW_FAILED.
  */
 
+#define I915_CSR_KBL "i915/kbl_dmc_ver1.bin"
+MODULE_FIRMWARE(I915_CSR_KBL);
+#define KBL_CSR_VERSION_REQUIRED       CSR_VERSION(1, 1)
+
 #define I915_CSR_SKL "i915/skl_dmc_ver1.bin"
+MODULE_FIRMWARE(I915_CSR_SKL);
+#define SKL_CSR_VERSION_REQUIRED       CSR_VERSION(1, 23)
+
 #define I915_CSR_BXT "i915/bxt_dmc_ver1.bin"
+MODULE_FIRMWARE(I915_CSR_BXT);
+#define BXT_CSR_VERSION_REQUIRED       CSR_VERSION(1, 7)
 
 #define FIRMWARE_URL  "https://01.org/linuxgraphics/intel-linux-graphics-firmwares"
 
-MODULE_FIRMWARE(I915_CSR_SKL);
-MODULE_FIRMWARE(I915_CSR_BXT);
 
-#define SKL_CSR_VERSION_REQUIRED       CSR_VERSION(1, 23)
-#define BXT_CSR_VERSION_REQUIRED       CSR_VERSION(1, 7)
+
 
 #define CSR_MAX_FW_SIZE                        0x2FFF
 #define CSR_DEFAULT_FW_OFFSET          0xFFFFFFFF
@@ -169,12 +175,10 @@ struct stepping_info {
        char substepping;
 };
 
-/*
- * Kabylake derivated from Skylake H0, so SKL H0
- * is the right firmware for KBL A0 (revid 0).
- */
 static const struct stepping_info kbl_stepping_info[] = {
-       {'H', '0'}, {'I', '0'}
+       {'A', '0'}, {'B', '0'}, {'C', '0'},
+       {'D', '0'}, {'E', '0'}, {'F', '0'},
+       {'G', '0'}, {'H', '0'}, {'I', '0'},
 };
 
 static const struct stepping_info skl_stepping_info[] = {
@@ -298,7 +302,9 @@ static uint32_t *parse_csr_fw(struct drm_i915_private *dev_priv,
 
        csr->version = css_header->version;
 
-       if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) {
+       if (IS_KABYLAKE(dev_priv)) {
+               required_min_version = KBL_CSR_VERSION_REQUIRED;
+       } else if (IS_SKYLAKE(dev_priv)) {
                required_min_version = SKL_CSR_VERSION_REQUIRED;
        } else if (IS_BROXTON(dev_priv)) {
                required_min_version = BXT_CSR_VERSION_REQUIRED;
@@ -446,7 +452,9 @@ void intel_csr_ucode_init(struct drm_i915_private *dev_priv)
        if (!HAS_CSR(dev_priv))
                return;
 
-       if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv))
+       if (IS_KABYLAKE(dev_priv))
+               csr->fw_path = I915_CSR_KBL;
+       else if (IS_SKYLAKE(dev_priv))
                csr->fw_path = I915_CSR_SKL;
        else if (IS_BROXTON(dev_priv))
                csr->fw_path = I915_CSR_BXT;
index 56a1637c864f4af792a182354e03a58f58c73cab..3074c56a643d46436b4ce6e626407638c118fbb0 100644 (file)
@@ -8447,16 +8447,16 @@ static void lpt_reset_fdi_mphy(struct drm_i915_private *dev_priv)
        tmp |= FDI_MPHY_IOSFSB_RESET_CTL;
        I915_WRITE(SOUTH_CHICKEN2, tmp);
 
-       if (wait_for_atomic_us(I915_READ(SOUTH_CHICKEN2) &
-                              FDI_MPHY_IOSFSB_RESET_STATUS, 100))
+       if (wait_for_us(I915_READ(SOUTH_CHICKEN2) &
+                       FDI_MPHY_IOSFSB_RESET_STATUS, 100))
                DRM_ERROR("FDI mPHY reset assert timeout\n");
 
        tmp = I915_READ(SOUTH_CHICKEN2);
        tmp &= ~FDI_MPHY_IOSFSB_RESET_CTL;
        I915_WRITE(SOUTH_CHICKEN2, tmp);
 
-       if (wait_for_atomic_us((I915_READ(SOUTH_CHICKEN2) &
-                               FDI_MPHY_IOSFSB_RESET_STATUS) == 0, 100))
+       if (wait_for_us((I915_READ(SOUTH_CHICKEN2) &
+                        FDI_MPHY_IOSFSB_RESET_STATUS) == 0, 100))
                DRM_ERROR("FDI mPHY reset de-assert timeout\n");
 }
 
@@ -9440,8 +9440,8 @@ static void hsw_disable_lcpll(struct drm_i915_private *dev_priv,
                val |= LCPLL_CD_SOURCE_FCLK;
                I915_WRITE(LCPLL_CTL, val);
 
-               if (wait_for_atomic_us(I915_READ(LCPLL_CTL) &
-                                      LCPLL_CD_SOURCE_FCLK_DONE, 1))
+               if (wait_for_us(I915_READ(LCPLL_CTL) &
+                               LCPLL_CD_SOURCE_FCLK_DONE, 1))
                        DRM_ERROR("Switching to FCLK failed\n");
 
                val = I915_READ(LCPLL_CTL);
@@ -9514,8 +9514,8 @@ static void hsw_restore_lcpll(struct drm_i915_private *dev_priv)
                val &= ~LCPLL_CD_SOURCE_FCLK;
                I915_WRITE(LCPLL_CTL, val);
 
-               if (wait_for_atomic_us((I915_READ(LCPLL_CTL) &
-                                       LCPLL_CD_SOURCE_FCLK_DONE) == 0, 1))
+               if (wait_for_us((I915_READ(LCPLL_CTL) &
+                                LCPLL_CD_SOURCE_FCLK_DONE) == 0, 1))
                        DRM_ERROR("Switching back to LCPLL failed\n");
        }
 
@@ -11997,6 +11997,12 @@ static int intel_crtc_atomic_check(struct drm_crtc *crtc,
                ret = intel_color_check(crtc, crtc_state);
                if (ret)
                        return ret;
+
+               /*
+                * Changing color management on Intel hardware is
+                * handled as part of planes update.
+                */
+               crtc_state->planes_changed = true;
        }
 
        ret = 0;
index 79cf2d5f5a20913025c8e9f1f308eae73068008b..891107f92d9fa7e8de0aebe02e44e256e45bb4e1 100644 (file)
@@ -663,7 +663,7 @@ intel_dp_aux_wait_done(struct intel_dp *intel_dp, bool has_aux_irq)
                done = wait_event_timeout(dev_priv->gmbus_wait_queue, C,
                                          msecs_to_jiffies_timeout(10));
        else
-               done = wait_for_atomic(C, 10) == 0;
+               done = wait_for(C, 10) == 0;
        if (!done)
                DRM_ERROR("dp aux hw did not signal timeout (has irq: %i)!\n",
                          has_aux_irq);
@@ -4645,7 +4645,7 @@ intel_dp_detect(struct drm_connector *connector, bool force)
 
        intel_dp->detect_done = false;
 
-       if (intel_connector->detect_edid)
+       if (is_edp(intel_dp) || intel_connector->detect_edid)
                return connector_status_connected;
        else
                return connector_status_disconnected;
@@ -4899,13 +4899,15 @@ static void intel_edp_panel_vdd_sanitize(struct intel_dp *intel_dp)
 
 void intel_dp_encoder_reset(struct drm_encoder *encoder)
 {
-       struct intel_dp *intel_dp;
+       struct drm_i915_private *dev_priv = to_i915(encoder->dev);
+       struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+
+       if (!HAS_DDI(dev_priv))
+               intel_dp->DP = I915_READ(intel_dp->output_reg);
 
        if (to_intel_encoder(encoder)->type != INTEL_OUTPUT_EDP)
                return;
 
-       intel_dp = enc_to_intel_dp(encoder);
-
        pps_lock(intel_dp);
 
        /*
index baf6f5584cbd8197334279210e5f08e976d46806..58f60b27837efc963770800c2fd1c07e7f041c04 100644 (file)
@@ -1377,8 +1377,8 @@ static void bxt_ddi_pll_enable(struct drm_i915_private *dev_priv,
        I915_WRITE(BXT_PORT_PLL_ENABLE(port), temp);
        POSTING_READ(BXT_PORT_PLL_ENABLE(port));
 
-       if (wait_for_atomic_us((I915_READ(BXT_PORT_PLL_ENABLE(port)) &
-                       PORT_PLL_LOCK), 200))
+       if (wait_for_us((I915_READ(BXT_PORT_PLL_ENABLE(port)) & PORT_PLL_LOCK),
+                       200))
                DRM_ERROR("PLL %d not locked\n", port);
 
        /*
index 42eac37de047b31b1b496a86ac8bf717569e3cfe..7f2d8415ed8b219b6ff5a2c16bf4c24fde66b90c 100644 (file)
@@ -1103,15 +1103,17 @@ static inline int gen8_emit_flush_coherentl3_wa(struct intel_engine_cs *engine,
                                                uint32_t *const batch,
                                                uint32_t index)
 {
+       struct drm_i915_private *dev_priv = engine->dev->dev_private;
        uint32_t l3sqc4_flush = (0x40400000 | GEN8_LQSC_FLUSH_COHERENT_LINES);
 
        /*
-        * WaDisableLSQCROPERFforOCL:skl
+        * WaDisableLSQCROPERFforOCL:skl,kbl
         * This WA is implemented in skl_init_clock_gating() but since
         * this batch updates GEN8_L3SQCREG4 with default value we need to
         * set this bit here to retain the WA during flush.
         */
-       if (IS_SKL_REVID(engine->dev, 0, SKL_REVID_E0))
+       if (IS_SKL_REVID(dev_priv, 0, SKL_REVID_E0) ||
+           IS_KBL_REVID(dev_priv, 0, KBL_REVID_E0))
                l3sqc4_flush |= GEN8_LQSC_RO_PERF_DIS;
 
        wa_ctx_emit(batch, index, (MI_STORE_REGISTER_MEM_GEN8 |
@@ -1273,6 +1275,7 @@ static int gen9_init_indirectctx_bb(struct intel_engine_cs *engine,
 {
        int ret;
        struct drm_device *dev = engine->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
        uint32_t index = wa_ctx_start(wa_ctx, *offset, CACHELINE_DWORDS);
 
        /* WaDisableCtxRestoreArbitration:skl,bxt */
@@ -1286,6 +1289,22 @@ static int gen9_init_indirectctx_bb(struct intel_engine_cs *engine,
                return ret;
        index = ret;
 
+       /* WaClearSlmSpaceAtContextSwitch:kbl */
+       /* Actual scratch location is at 128 bytes offset */
+       if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_A0)) {
+               uint32_t scratch_addr
+                       = engine->scratch.gtt_offset + 2*CACHELINE_BYTES;
+
+               wa_ctx_emit(batch, index, GFX_OP_PIPE_CONTROL(6));
+               wa_ctx_emit(batch, index, (PIPE_CONTROL_FLUSH_L3 |
+                                          PIPE_CONTROL_GLOBAL_GTT_IVB |
+                                          PIPE_CONTROL_CS_STALL |
+                                          PIPE_CONTROL_QW_WRITE));
+               wa_ctx_emit(batch, index, scratch_addr);
+               wa_ctx_emit(batch, index, 0);
+               wa_ctx_emit(batch, index, 0);
+               wa_ctx_emit(batch, index, 0);
+       }
        /* Pad to end of cacheline */
        while (index % CACHELINE_DWORDS)
                wa_ctx_emit(batch, index, MI_NOOP);
@@ -1687,9 +1706,10 @@ static int gen8_emit_flush_render(struct drm_i915_gem_request *request,
        struct intel_ringbuffer *ringbuf = request->ringbuf;
        struct intel_engine_cs *engine = ringbuf->engine;
        u32 scratch_addr = engine->scratch.gtt_offset + 2 * CACHELINE_BYTES;
-       bool vf_flush_wa = false;
+       bool vf_flush_wa = false, dc_flush_wa = false;
        u32 flags = 0;
        int ret;
+       int len;
 
        flags |= PIPE_CONTROL_CS_STALL;
 
@@ -1716,9 +1736,21 @@ static int gen8_emit_flush_render(struct drm_i915_gem_request *request,
                 */
                if (IS_GEN9(engine->dev))
                        vf_flush_wa = true;
+
+               /* WaForGAMHang:kbl */
+               if (IS_KBL_REVID(request->i915, 0, KBL_REVID_B0))
+                       dc_flush_wa = true;
        }
 
-       ret = intel_ring_begin(request, vf_flush_wa ? 12 : 6);
+       len = 6;
+
+       if (vf_flush_wa)
+               len += 6;
+
+       if (dc_flush_wa)
+               len += 12;
+
+       ret = intel_ring_begin(request, len);
        if (ret)
                return ret;
 
@@ -1731,12 +1763,31 @@ static int gen8_emit_flush_render(struct drm_i915_gem_request *request,
                intel_logical_ring_emit(ringbuf, 0);
        }
 
+       if (dc_flush_wa) {
+               intel_logical_ring_emit(ringbuf, GFX_OP_PIPE_CONTROL(6));
+               intel_logical_ring_emit(ringbuf, PIPE_CONTROL_DC_FLUSH_ENABLE);
+               intel_logical_ring_emit(ringbuf, 0);
+               intel_logical_ring_emit(ringbuf, 0);
+               intel_logical_ring_emit(ringbuf, 0);
+               intel_logical_ring_emit(ringbuf, 0);
+       }
+
        intel_logical_ring_emit(ringbuf, GFX_OP_PIPE_CONTROL(6));
        intel_logical_ring_emit(ringbuf, flags);
        intel_logical_ring_emit(ringbuf, scratch_addr);
        intel_logical_ring_emit(ringbuf, 0);
        intel_logical_ring_emit(ringbuf, 0);
        intel_logical_ring_emit(ringbuf, 0);
+
+       if (dc_flush_wa) {
+               intel_logical_ring_emit(ringbuf, GFX_OP_PIPE_CONTROL(6));
+               intel_logical_ring_emit(ringbuf, PIPE_CONTROL_CS_STALL);
+               intel_logical_ring_emit(ringbuf, 0);
+               intel_logical_ring_emit(ringbuf, 0);
+               intel_logical_ring_emit(ringbuf, 0);
+               intel_logical_ring_emit(ringbuf, 0);
+       }
+
        intel_logical_ring_advance(ringbuf);
 
        return 0;
index 99e26034ae8d0df783d706839b67b0fd2f161585..16e209d326b69469278fd93f47e8d7e26237a29c 100644 (file)
@@ -1038,5 +1038,16 @@ intel_opregion_get_panel_type(struct drm_device *dev)
                return -ENODEV;
        }
 
+       /*
+        * FIXME On Dell XPS 13 9350 the OpRegion panel type (0) gives us
+        * low vswing for eDP, whereas the VBT panel type (2) gives us normal
+        * vswing instead. Low vswing results in some display flickers, so
+        * let's simply ignore the OpRegion panel type on SKL for now.
+        */
+       if (IS_SKYLAKE(dev)) {
+               DRM_DEBUG_KMS("Ignoring OpRegion panel type (%d)\n", ret - 1);
+               return -ENODEV;
+       }
+
        return ret - 1;
 }
index 8357d571553a56471ff42ab0f048bab7f52f475f..aba94099886bf4ce9f402e6c253dc767636d3d56 100644 (file)
@@ -1731,7 +1731,8 @@ intel_panel_init_backlight_funcs(struct intel_panel *panel)
                panel->backlight.set = bxt_set_backlight;
                panel->backlight.get = bxt_get_backlight;
                panel->backlight.hz_to_pwm = bxt_hz_to_pwm;
-       } else if (HAS_PCH_LPT(dev_priv) || HAS_PCH_SPT(dev_priv)) {
+       } else if (HAS_PCH_LPT(dev_priv) || HAS_PCH_SPT(dev_priv) ||
+                  HAS_PCH_KBP(dev_priv)) {
                panel->backlight.setup = lpt_setup_backlight;
                panel->backlight.enable = lpt_enable_backlight;
                panel->backlight.disable = lpt_disable_backlight;
index a7ef45da0a9e8dc517f13a66f0f6429aa6334609..2863b92c9da6d157bd45bdf4760b514e403899a6 100644 (file)
 #define INTEL_RC6p_ENABLE                      (1<<1)
 #define INTEL_RC6pp_ENABLE                     (1<<2)
 
+static void gen9_init_clock_gating(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       /* See Bspec note for PSR2_CTL bit 31, Wa#828:skl,bxt,kbl */
+       I915_WRITE(CHICKEN_PAR1_1,
+                  I915_READ(CHICKEN_PAR1_1) | SKL_EDP_PSR_FIX_RDWRAP);
+
+       I915_WRITE(GEN8_CONFIG0,
+                  I915_READ(GEN8_CONFIG0) | GEN9_DEFAULT_FIXES);
+
+       /* WaEnableChickenDCPR:skl,bxt,kbl */
+       I915_WRITE(GEN8_CHICKEN_DCPR_1,
+                  I915_READ(GEN8_CHICKEN_DCPR_1) | MASK_WAKEMEM);
+
+       /* WaFbcTurnOffFbcWatermark:skl,bxt,kbl */
+       /* WaFbcWakeMemOn:skl,bxt,kbl */
+       I915_WRITE(DISP_ARB_CTL, I915_READ(DISP_ARB_CTL) |
+                  DISP_FBC_WM_DIS |
+                  DISP_FBC_MEMORY_WAKE);
+
+       /* WaFbcHighMemBwCorruptionAvoidance:skl,bxt,kbl */
+       I915_WRITE(ILK_DPFC_CHICKEN, I915_READ(ILK_DPFC_CHICKEN) |
+                  ILK_DPFC_DISABLE_DUMMY0);
+}
+
 static void bxt_init_clock_gating(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
+       gen9_init_clock_gating(dev);
+
        /* WaDisableSDEUnitClockGating:bxt */
        I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) |
                   GEN8_SDEUNIT_CLOCK_GATE_DISABLE);
@@ -6698,6 +6726,38 @@ static void lpt_suspend_hw(struct drm_device *dev)
        }
 }
 
+static void kabylake_init_clock_gating(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       gen9_init_clock_gating(dev);
+
+       /* WaDisableSDEUnitClockGating:kbl */
+       if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_B0))
+               I915_WRITE(GEN8_UCGCTL6, I915_READ(GEN8_UCGCTL6) |
+                          GEN8_SDEUNIT_CLOCK_GATE_DISABLE);
+
+       /* WaDisableGamClockGating:kbl */
+       if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_B0))
+               I915_WRITE(GEN6_UCGCTL1, I915_READ(GEN6_UCGCTL1) |
+                          GEN6_GAMUNIT_CLOCK_GATE_DISABLE);
+
+       /* WaFbcNukeOnHostModify:kbl */
+       I915_WRITE(ILK_DPFC_CHICKEN, I915_READ(ILK_DPFC_CHICKEN) |
+                  ILK_DPFC_NUKE_ON_ANY_MODIFICATION);
+}
+
+static void skylake_init_clock_gating(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       gen9_init_clock_gating(dev);
+
+       /* WaFbcNukeOnHostModify:skl */
+       I915_WRITE(ILK_DPFC_CHICKEN, I915_READ(ILK_DPFC_CHICKEN) |
+                  ILK_DPFC_NUKE_ON_ANY_MODIFICATION);
+}
+
 static void broadwell_init_clock_gating(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -7163,9 +7223,9 @@ static void nop_init_clock_gating(struct drm_device *dev)
 void intel_init_clock_gating_hooks(struct drm_i915_private *dev_priv)
 {
        if (IS_SKYLAKE(dev_priv))
-               dev_priv->display.init_clock_gating = nop_init_clock_gating;
+               dev_priv->display.init_clock_gating = skylake_init_clock_gating;
        else if (IS_KABYLAKE(dev_priv))
-               dev_priv->display.init_clock_gating = nop_init_clock_gating;
+               dev_priv->display.init_clock_gating = kabylake_init_clock_gating;
        else if (IS_BROXTON(dev_priv))
                dev_priv->display.init_clock_gating = bxt_init_clock_gating;
        else if (IS_BROADWELL(dev_priv))
index 04402bb9d26b9e96cac9225b37d31587891521f6..68c5af079ef85fd1cfc7ba23338f475f075454c6 100644 (file)
@@ -913,24 +913,26 @@ static int gen9_init_workarounds(struct intel_engine_cs *engine)
 {
        struct drm_device *dev = engine->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       uint32_t tmp;
        int ret;
 
-       /* WaEnableLbsSlaRetryTimerDecrement:skl */
+       /* WaConextSwitchWithConcurrentTLBInvalidate:skl,bxt,kbl */
+       I915_WRITE(GEN9_CSFE_CHICKEN1_RCS, _MASKED_BIT_ENABLE(GEN9_PREEMPT_GPGPU_SYNC_SWITCH_DISABLE));
+
+       /* WaEnableLbsSlaRetryTimerDecrement:skl,bxt,kbl */
        I915_WRITE(BDW_SCRATCH1, I915_READ(BDW_SCRATCH1) |
                   GEN9_LBS_SLA_RETRY_TIMER_DECREMENT_ENABLE);
 
-       /* WaDisableKillLogic:bxt,skl */
+       /* WaDisableKillLogic:bxt,skl,kbl */
        I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) |
                   ECOCHK_DIS_TLB);
 
-       /* WaClearFlowControlGpgpuContextSave:skl,bxt */
-       /* WaDisablePartialInstShootdown:skl,bxt */
+       /* WaClearFlowControlGpgpuContextSave:skl,bxt,kbl */
+       /* WaDisablePartialInstShootdown:skl,bxt,kbl */
        WA_SET_BIT_MASKED(GEN8_ROW_CHICKEN,
                          FLOW_CONTROL_ENABLE |
                          PARTIAL_INSTRUCTION_SHOOTDOWN_DISABLE);
 
-       /* Syncing dependencies between camera and graphics:skl,bxt */
+       /* Syncing dependencies between camera and graphics:skl,bxt,kbl */
        WA_SET_BIT_MASKED(HALF_SLICE_CHICKEN3,
                          GEN9_DISABLE_OCL_OOB_SUPPRESS_LOGIC);
 
@@ -952,18 +954,18 @@ static int gen9_init_workarounds(struct intel_engine_cs *engine)
                 */
        }
 
-       /* WaEnableYV12BugFixInHalfSliceChicken7:skl,bxt */
-       /* WaEnableSamplerGPGPUPreemptionSupport:skl,bxt */
+       /* WaEnableYV12BugFixInHalfSliceChicken7:skl,bxt,kbl */
+       /* WaEnableSamplerGPGPUPreemptionSupport:skl,bxt,kbl */
        WA_SET_BIT_MASKED(GEN9_HALF_SLICE_CHICKEN7,
                          GEN9_ENABLE_YV12_BUGFIX |
                          GEN9_ENABLE_GPGPU_PREEMPTION);
 
-       /* Wa4x4STCOptimizationDisable:skl,bxt */
-       /* WaDisablePartialResolveInVc:skl,bxt */
+       /* Wa4x4STCOptimizationDisable:skl,bxt,kbl */
+       /* WaDisablePartialResolveInVc:skl,bxt,kbl */
        WA_SET_BIT_MASKED(CACHE_MODE_1, (GEN8_4x4_STC_OPTIMIZATION_DISABLE |
                                         GEN9_PARTIAL_RESOLVE_IN_VC_DISABLE));
 
-       /* WaCcsTlbPrefetchDisable:skl,bxt */
+       /* WaCcsTlbPrefetchDisable:skl,bxt,kbl */
        WA_CLR_BIT_MASKED(GEN9_HALF_SLICE_CHICKEN5,
                          GEN9_CCS_TLB_PREFETCH_ENABLE);
 
@@ -973,31 +975,57 @@ static int gen9_init_workarounds(struct intel_engine_cs *engine)
                WA_SET_BIT_MASKED(SLICE_ECO_CHICKEN0,
                                  PIXEL_MASK_CAMMING_DISABLE);
 
-       /* WaForceContextSaveRestoreNonCoherent:skl,bxt */
-       tmp = HDC_FORCE_CONTEXT_SAVE_RESTORE_NON_COHERENT;
-       if (IS_SKL_REVID(dev, SKL_REVID_F0, REVID_FOREVER) ||
-           IS_BXT_REVID(dev, BXT_REVID_B0, REVID_FOREVER))
-               tmp |= HDC_FORCE_CSR_NON_COHERENT_OVR_DISABLE;
-       WA_SET_BIT_MASKED(HDC_CHICKEN0, tmp);
+       /* WaForceContextSaveRestoreNonCoherent:skl,bxt,kbl */
+       WA_SET_BIT_MASKED(HDC_CHICKEN0,
+                         HDC_FORCE_CONTEXT_SAVE_RESTORE_NON_COHERENT |
+                         HDC_FORCE_CSR_NON_COHERENT_OVR_DISABLE);
+
+       /* WaForceEnableNonCoherent and WaDisableHDCInvalidation are
+        * both tied to WaForceContextSaveRestoreNonCoherent
+        * in some hsds for skl. We keep the tie for all gen9. The
+        * documentation is a bit hazy and so we want to get common behaviour,
+        * even though there is no clear evidence we would need both on kbl/bxt.
+        * This area has been source of system hangs so we play it safe
+        * and mimic the skl regardless of what bspec says.
+        *
+        * Use Force Non-Coherent whenever executing a 3D context. This
+        * is a workaround for a possible hang in the unlikely event
+        * a TLB invalidation occurs during a PSD flush.
+        */
 
-       /* WaDisableSamplerPowerBypassForSOPingPong:skl,bxt */
-       if (IS_SKYLAKE(dev) || IS_BXT_REVID(dev, 0, BXT_REVID_B0))
+       /* WaForceEnableNonCoherent:skl,bxt,kbl */
+       WA_SET_BIT_MASKED(HDC_CHICKEN0,
+                         HDC_FORCE_NON_COHERENT);
+
+       /* WaDisableHDCInvalidation:skl,bxt,kbl */
+       I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) |
+                  BDW_DISABLE_HDC_INVALIDATION);
+
+       /* WaDisableSamplerPowerBypassForSOPingPong:skl,bxt,kbl */
+       if (IS_SKYLAKE(dev_priv) ||
+           IS_KABYLAKE(dev_priv) ||
+           IS_BXT_REVID(dev_priv, 0, BXT_REVID_B0))
                WA_SET_BIT_MASKED(HALF_SLICE_CHICKEN3,
                                  GEN8_SAMPLER_POWER_BYPASS_DIS);
 
-       /* WaDisableSTUnitPowerOptimization:skl,bxt */
+       /* WaDisableSTUnitPowerOptimization:skl,bxt,kbl */
        WA_SET_BIT_MASKED(HALF_SLICE_CHICKEN2, GEN8_ST_PO_DISABLE);
 
-       /* WaOCLCoherentLineFlush:skl,bxt */
+       /* WaOCLCoherentLineFlush:skl,bxt,kbl */
        I915_WRITE(GEN8_L3SQCREG4, (I915_READ(GEN8_L3SQCREG4) |
                                    GEN8_LQSC_FLUSH_COHERENT_LINES));
 
-       /* WaEnablePreemptionGranularityControlByUMD:skl,bxt */
+       /* WaVFEStateAfterPipeControlwithMediaStateClear:skl,bxt */
+       ret = wa_ring_whitelist_reg(engine, GEN9_CTX_PREEMPT_REG);
+       if (ret)
+               return ret;
+
+       /* WaEnablePreemptionGranularityControlByUMD:skl,bxt,kbl */
        ret= wa_ring_whitelist_reg(engine, GEN8_CS_CHICKEN1);
        if (ret)
                return ret;
 
-       /* WaAllowUMDToModifyHDCChicken1:skl,bxt */
+       /* WaAllowUMDToModifyHDCChicken1:skl,bxt,kbl */
        ret = wa_ring_whitelist_reg(engine, GEN8_HDC_CHICKEN1);
        if (ret)
                return ret;
@@ -1092,22 +1120,6 @@ static int skl_init_workarounds(struct intel_engine_cs *engine)
                WA_SET_BIT_MASKED(HIZ_CHICKEN,
                                  BDW_HIZ_POWER_COMPILER_CLOCK_GATING_DISABLE);
 
-       /* This is tied to WaForceContextSaveRestoreNonCoherent */
-       if (IS_SKL_REVID(dev, 0, REVID_FOREVER)) {
-               /*
-                *Use Force Non-Coherent whenever executing a 3D context. This
-                * is a workaround for a possible hang in the unlikely event
-                * a TLB invalidation occurs during a PSD flush.
-                */
-               /* WaForceEnableNonCoherent:skl */
-               WA_SET_BIT_MASKED(HDC_CHICKEN0,
-                                 HDC_FORCE_NON_COHERENT);
-
-               /* WaDisableHDCInvalidation:skl */
-               I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) |
-                          BDW_DISABLE_HDC_INVALIDATION);
-       }
-
        /* WaBarrierPerformanceFixDisable:skl */
        if (IS_SKL_REVID(dev, SKL_REVID_C0, SKL_REVID_D0))
                WA_SET_BIT_MASKED(HDC_CHICKEN0,
@@ -1120,6 +1132,9 @@ static int skl_init_workarounds(struct intel_engine_cs *engine)
                        GEN7_HALF_SLICE_CHICKEN1,
                        GEN7_SBE_SS_CACHE_DISPATCH_PORT_SHARING_DISABLE);
 
+       /* WaDisableGafsUnitClkGating:skl */
+       WA_SET_BIT(GEN7_UCGCTL4, GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE);
+
        /* WaDisableLSQCROPERFforOCL:skl */
        ret = wa_ring_whitelist_reg(engine, GEN8_L3SQCREG4);
        if (ret)
@@ -1174,6 +1189,63 @@ static int bxt_init_workarounds(struct intel_engine_cs *engine)
                        return ret;
        }
 
+       /* WaInsertDummyPushConstPs:bxt */
+       if (IS_BXT_REVID(dev_priv, 0, BXT_REVID_B0))
+               WA_SET_BIT_MASKED(COMMON_SLICE_CHICKEN2,
+                                 GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION);
+
+       return 0;
+}
+
+static int kbl_init_workarounds(struct intel_engine_cs *engine)
+{
+       struct drm_i915_private *dev_priv = engine->dev->dev_private;
+       int ret;
+
+       ret = gen9_init_workarounds(engine);
+       if (ret)
+               return ret;
+
+       /* WaEnableGapsTsvCreditFix:kbl */
+       I915_WRITE(GEN8_GARBCNTL, (I915_READ(GEN8_GARBCNTL) |
+                                  GEN9_GAPS_TSV_CREDIT_DISABLE));
+
+       /* WaDisableDynamicCreditSharing:kbl */
+       if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_B0))
+               WA_SET_BIT(GAMT_CHKN_BIT_REG,
+                          GAMT_CHKN_DISABLE_DYNAMIC_CREDIT_SHARING);
+
+       /* WaDisableFenceDestinationToSLM:kbl (pre-prod) */
+       if (IS_KBL_REVID(dev_priv, KBL_REVID_A0, KBL_REVID_A0))
+               WA_SET_BIT_MASKED(HDC_CHICKEN0,
+                                 HDC_FENCE_DEST_SLM_DISABLE);
+
+       /* GEN8_L3SQCREG4 has a dependency with WA batch so any new changes
+        * involving this register should also be added to WA batch as required.
+        */
+       if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_E0))
+               /* WaDisableLSQCROPERFforOCL:kbl */
+               I915_WRITE(GEN8_L3SQCREG4, I915_READ(GEN8_L3SQCREG4) |
+                          GEN8_LQSC_RO_PERF_DIS);
+
+       /* WaInsertDummyPushConstPs:kbl */
+       if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_B0))
+               WA_SET_BIT_MASKED(COMMON_SLICE_CHICKEN2,
+                                 GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION);
+
+       /* WaDisableGafsUnitClkGating:kbl */
+       WA_SET_BIT(GEN7_UCGCTL4, GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE);
+
+       /* WaDisableSbeCacheDispatchPortSharing:kbl */
+       WA_SET_BIT_MASKED(
+               GEN7_HALF_SLICE_CHICKEN1,
+               GEN7_SBE_SS_CACHE_DISPATCH_PORT_SHARING_DISABLE);
+
+       /* WaDisableLSQCROPERFforOCL:kbl */
+       ret = wa_ring_whitelist_reg(engine, GEN8_L3SQCREG4);
+       if (ret)
+               return ret;
+
        return 0;
 }
 
@@ -1199,6 +1271,9 @@ int init_workarounds_ring(struct intel_engine_cs *engine)
        if (IS_BROXTON(dev))
                return bxt_init_workarounds(engine);
 
+       if (IS_KABYLAKE(dev_priv))
+               return kbl_init_workarounds(engine);
+
        return 0;
 }
 
index 22706c0a54b53a9f95a1ad7e453217b7276cfcaf..49bd5da194e1b368b0d069825e6fb03b03166684 100644 (file)
@@ -40,7 +40,8 @@ static int
 gf119_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
 {
        struct nvkm_device *device = outp->base.disp->engine.subdev.device;
-       nvkm_mask(device, 0x61c110, 0x0f0f0f0f, 0x01010101 * pattern);
+       const u32 soff = gf119_sor_soff(outp);
+       nvkm_mask(device, 0x61c110 + soff, 0x0f0f0f0f, 0x01010101 * pattern);
        return 0;
 }
 
index 4182a21f5923dddbc06d2042bb82837f0f7ab0f5..41cacecbea9a85e280a6978ed41b91752a3bcced 100644 (file)
@@ -65,6 +65,14 @@ static void sun4i_crtc_disable(struct drm_crtc *crtc)
        DRM_DEBUG_DRIVER("Disabling the CRTC\n");
 
        sun4i_tcon_disable(drv->tcon);
+
+       if (crtc->state->event && !crtc->state->active) {
+               spin_lock_irq(&crtc->dev->event_lock);
+               drm_crtc_send_vblank_event(crtc, crtc->state->event);
+               spin_unlock_irq(&crtc->dev->event_lock);
+
+               crtc->state->event = NULL;
+       }
 }
 
 static void sun4i_crtc_enable(struct drm_crtc *crtc)
index 257d2b4f36456a10e0d4bdabd4407deb1aa2b581..937394cbc24122372518961e6b343abe9ab3a680 100644 (file)
@@ -92,7 +92,7 @@ static struct drm_driver sun4i_drv_driver = {
        /* Frame Buffer Operations */
 
        /* VBlank Operations */
-       .get_vblank_counter     = drm_vblank_count,
+       .get_vblank_counter     = drm_vblank_no_hw_counter,
        .enable_vblank          = sun4i_drv_enable_vblank,
        .disable_vblank         = sun4i_drv_disable_vblank,
 };
@@ -310,6 +310,7 @@ static int sun4i_drv_probe(struct platform_device *pdev)
 
                count += sun4i_drv_add_endpoints(&pdev->dev, &match,
                                                pipeline);
+               of_node_put(pipeline);
 
                DRM_DEBUG_DRIVER("Queued %d outputs on pipeline %d\n",
                                 count, i);
index 39386f50af876fd9494f9064384c5d050918d301..a71cf98c655fa7083c932683261c4c0517b25a8a 100644 (file)
@@ -1034,9 +1034,9 @@ static int ttm_bo_move_buffer(struct ttm_buffer_object *bo,
        return ret;
 }
 
-static bool ttm_bo_mem_compat(struct ttm_placement *placement,
-                             struct ttm_mem_reg *mem,
-                             uint32_t *new_flags)
+bool ttm_bo_mem_compat(struct ttm_placement *placement,
+                      struct ttm_mem_reg *mem,
+                      uint32_t *new_flags)
 {
        int i;
 
@@ -1068,6 +1068,7 @@ static bool ttm_bo_mem_compat(struct ttm_placement *placement,
 
        return false;
 }
+EXPORT_SYMBOL(ttm_bo_mem_compat);
 
 int ttm_bo_validate(struct ttm_buffer_object *bo,
                        struct ttm_placement *placement,
index 9b078a4939968d8521a96b3f83280f4f06b407bc..0cd889015dc57d5c0d0a8b89f39cd17b10303fbf 100644 (file)
@@ -49,6 +49,7 @@ int vmw_dmabuf_pin_in_placement(struct vmw_private *dev_priv,
 {
        struct ttm_buffer_object *bo = &buf->base;
        int ret;
+       uint32_t new_flags;
 
        ret = ttm_write_lock(&dev_priv->reservation_sem, interruptible);
        if (unlikely(ret != 0))
@@ -60,7 +61,12 @@ int vmw_dmabuf_pin_in_placement(struct vmw_private *dev_priv,
        if (unlikely(ret != 0))
                goto err;
 
-       ret = ttm_bo_validate(bo, placement, interruptible, false);
+       if (buf->pin_count > 0)
+               ret = ttm_bo_mem_compat(placement, &bo->mem,
+                                       &new_flags) == true ? 0 : -EINVAL;
+       else
+               ret = ttm_bo_validate(bo, placement, interruptible, false);
+
        if (!ret)
                vmw_bo_pin_reserved(buf, true);
 
@@ -91,6 +97,7 @@ int vmw_dmabuf_pin_in_vram_or_gmr(struct vmw_private *dev_priv,
 {
        struct ttm_buffer_object *bo = &buf->base;
        int ret;
+       uint32_t new_flags;
 
        ret = ttm_write_lock(&dev_priv->reservation_sem, interruptible);
        if (unlikely(ret != 0))
@@ -102,6 +109,12 @@ int vmw_dmabuf_pin_in_vram_or_gmr(struct vmw_private *dev_priv,
        if (unlikely(ret != 0))
                goto err;
 
+       if (buf->pin_count > 0) {
+               ret = ttm_bo_mem_compat(&vmw_vram_gmr_placement, &bo->mem,
+                                       &new_flags) == true ? 0 : -EINVAL;
+               goto out_unreserve;
+       }
+
        ret = ttm_bo_validate(bo, &vmw_vram_gmr_placement, interruptible,
                              false);
        if (likely(ret == 0) || ret == -ERESTARTSYS)
@@ -161,6 +174,7 @@ int vmw_dmabuf_pin_in_start_of_vram(struct vmw_private *dev_priv,
        struct ttm_placement placement;
        struct ttm_place place;
        int ret = 0;
+       uint32_t new_flags;
 
        place = vmw_vram_placement.placement[0];
        place.lpfn = bo->num_pages;
@@ -185,10 +199,15 @@ int vmw_dmabuf_pin_in_start_of_vram(struct vmw_private *dev_priv,
         */
        if (bo->mem.mem_type == TTM_PL_VRAM &&
            bo->mem.start < bo->num_pages &&
-           bo->mem.start > 0)
+           bo->mem.start > 0 &&
+           buf->pin_count == 0)
                (void) ttm_bo_validate(bo, &vmw_sys_placement, false, false);
 
-       ret = ttm_bo_validate(bo, &placement, interruptible, false);
+       if (buf->pin_count > 0)
+               ret = ttm_bo_mem_compat(&placement, &bo->mem,
+                                       &new_flags) == true ? 0 : -EINVAL;
+       else
+               ret = ttm_bo_validate(bo, &placement, interruptible, false);
 
        /* For some reason we didn't end up at the start of vram */
        WARN_ON(ret == 0 && bo->offset != 0);
index 9fcd8200d485e61aae3ff8a0cbf4fd4289a6d54b..8d528fcf6e9606d4614c744eb39cc9f47b644e8c 100644 (file)
@@ -233,6 +233,7 @@ static int vmw_force_iommu;
 static int vmw_restrict_iommu;
 static int vmw_force_coherent;
 static int vmw_restrict_dma_mask;
+static int vmw_assume_16bpp;
 
 static int vmw_probe(struct pci_dev *, const struct pci_device_id *);
 static void vmw_master_init(struct vmw_master *);
@@ -249,6 +250,8 @@ MODULE_PARM_DESC(force_coherent, "Force coherent TTM pages");
 module_param_named(force_coherent, vmw_force_coherent, int, 0600);
 MODULE_PARM_DESC(restrict_dma_mask, "Restrict DMA mask to 44 bits with IOMMU");
 module_param_named(restrict_dma_mask, vmw_restrict_dma_mask, int, 0600);
+MODULE_PARM_DESC(assume_16bpp, "Assume 16-bpp when filtering modes");
+module_param_named(assume_16bpp, vmw_assume_16bpp, int, 0600);
 
 
 static void vmw_print_capabilities(uint32_t capabilities)
@@ -660,6 +663,8 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
        dev_priv->vram_start = pci_resource_start(dev->pdev, 1);
        dev_priv->mmio_start = pci_resource_start(dev->pdev, 2);
 
+       dev_priv->assume_16bpp = !!vmw_assume_16bpp;
+
        dev_priv->enable_fb = enable_fbdev;
 
        vmw_write(dev_priv, SVGA_REG_ID, SVGA_ID_2);
@@ -706,6 +711,13 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
                        vmw_read(dev_priv,
                                 SVGA_REG_SUGGESTED_GBOBJECT_MEM_SIZE_KB);
 
+               /*
+                * Workaround for low memory 2D VMs to compensate for the
+                * allocation taken by fbdev
+                */
+               if (!(dev_priv->capabilities & SVGA_CAP_3D))
+                       mem_size *= 2;
+
                dev_priv->max_mob_pages = mem_size * 1024 / PAGE_SIZE;
                dev_priv->prim_bb_mem =
                        vmw_read(dev_priv,
index 1980e2a28265d195db5002977d39239f6b3251a9..89fb19443a3f50e81a43ce4923e92ce8cf69579a 100644 (file)
@@ -386,6 +386,7 @@ struct vmw_private {
        spinlock_t hw_lock;
        spinlock_t cap_lock;
        bool has_dx;
+       bool assume_16bpp;
 
        /*
         * VGA registers.
index 679a4cb98ee306bb47c5c0097f1aa829c17a7fc8..d2d93959b1198ce41470a1a76a8341553c1ddc6c 100644 (file)
@@ -517,28 +517,6 @@ static int vmw_fb_kms_framebuffer(struct fb_info *info)
 
        par->set_fb = &vfb->base;
 
-       if (!par->bo_ptr) {
-               /*
-                * Pin before mapping. Since we don't know in what placement
-                * to pin, call into KMS to do it for us.
-                */
-               ret = vfb->pin(vfb);
-               if (ret) {
-                       DRM_ERROR("Could not pin the fbdev framebuffer.\n");
-                       return ret;
-               }
-
-               ret = ttm_bo_kmap(&par->vmw_bo->base, 0,
-                                 par->vmw_bo->base.num_pages, &par->map);
-               if (ret) {
-                       vfb->unpin(vfb);
-                       DRM_ERROR("Could not map the fbdev framebuffer.\n");
-                       return ret;
-               }
-
-               par->bo_ptr = ttm_kmap_obj_virtual(&par->map, &par->bo_iowrite);
-       }
-
        return 0;
 }
 
@@ -601,6 +579,31 @@ static int vmw_fb_set_par(struct fb_info *info)
        if (ret)
                goto out_unlock;
 
+       if (!par->bo_ptr) {
+               struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(set.fb);
+
+               /*
+                * Pin before mapping. Since we don't know in what placement
+                * to pin, call into KMS to do it for us.
+                */
+               ret = vfb->pin(vfb);
+               if (ret) {
+                       DRM_ERROR("Could not pin the fbdev framebuffer.\n");
+                       goto out_unlock;
+               }
+
+               ret = ttm_bo_kmap(&par->vmw_bo->base, 0,
+                                 par->vmw_bo->base.num_pages, &par->map);
+               if (ret) {
+                       vfb->unpin(vfb);
+                       DRM_ERROR("Could not map the fbdev framebuffer.\n");
+                       goto out_unlock;
+               }
+
+               par->bo_ptr = ttm_kmap_obj_virtual(&par->map, &par->bo_iowrite);
+       }
+
+
        vmw_fb_dirty_mark(par, par->fb_x, par->fb_y,
                          par->set_fb->width, par->set_fb->height);
 
index 55231cce73a01f047a1d660d3f3192d1f7ba80a6..e29da45a2847ecd28e829ced3b65a8a39622d798 100644 (file)
@@ -1553,14 +1553,10 @@ int vmw_du_connector_fill_modes(struct drm_connector *connector,
                DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC)
        };
        int i;
-       u32 assumed_bpp = 2;
+       u32 assumed_bpp = 4;
 
-       /*
-        * If using screen objects, then assume 32-bpp because that's what the
-        * SVGA device is assuming
-        */
-       if (dev_priv->active_display_unit == vmw_du_screen_object)
-               assumed_bpp = 4;
+       if (dev_priv->assume_16bpp)
+               assumed_bpp = 2;
 
        if (dev_priv->active_display_unit == vmw_du_screen_target) {
                max_width  = min(max_width,  dev_priv->stdu_max_width);
index f0374f9b56cad3a522119f7d4cf8a8940f445bf0..e57a0bad7a626daf505625ce019f2583218a51ab 100644 (file)
@@ -300,6 +300,9 @@ static int vmw_recv_msg(struct rpc_channel *channel, void **msg,
                break;
        }
 
+       if (retries == RETRIES)
+               return -EINVAL;
+
        *msg_len = reply_len;
        *msg     = reply;
 
index 9ca818fb034c923c4737ccfedb3ea98002aaae84..41932a7c4f79516da86f9e35d1223c8e033cf248 100644 (file)
@@ -399,8 +399,10 @@ static int vmw_stdu_bind_fb(struct vmw_private *dev_priv,
 
        WARN_ON_ONCE(!stdu->defined);
 
-       if (!vfb->dmabuf && new_fb->width == mode->hdisplay &&
-           new_fb->height == mode->vdisplay)
+       new_vfbs = (vfb->dmabuf) ? NULL : vmw_framebuffer_to_vfbs(new_fb);
+
+       if (new_vfbs && new_vfbs->surface->base_size.width == mode->hdisplay &&
+           new_vfbs->surface->base_size.height == mode->vdisplay)
                new_content_type = SAME_AS_DISPLAY;
        else if (vfb->dmabuf)
                new_content_type = SEPARATE_DMA;
@@ -444,7 +446,6 @@ static int vmw_stdu_bind_fb(struct vmw_private *dev_priv,
                        content_srf.mip_levels[0]     = 1;
                        content_srf.multisample_count = 0;
                } else {
-                       new_vfbs = vmw_framebuffer_to_vfbs(new_fb);
                        content_srf = *new_vfbs->surface;
                }
 
@@ -464,7 +465,6 @@ static int vmw_stdu_bind_fb(struct vmw_private *dev_priv,
                        return ret;
                }
        } else if (new_content_type == SAME_AS_DISPLAY) {
-               new_vfbs = vmw_framebuffer_to_vfbs(new_fb);
                new_display_srf = vmw_surface_reference(new_vfbs->surface);
        }
 
index 95b7d61d9910f3c65fced473c4e2fab5ce0d4e9b..fb6f1f4472795700c1f12889c405b9350b4ebcb0 100644 (file)
@@ -61,6 +61,7 @@ MODULE_LICENSE("GPL");
 #define MT_QUIRK_ALWAYS_VALID          (1 << 4)
 #define MT_QUIRK_VALID_IS_INRANGE      (1 << 5)
 #define MT_QUIRK_VALID_IS_CONFIDENCE   (1 << 6)
+#define MT_QUIRK_CONFIDENCE            (1 << 7)
 #define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE   (1 << 8)
 #define MT_QUIRK_NO_AREA               (1 << 9)
 #define MT_QUIRK_IGNORE_DUPLICATES     (1 << 10)
@@ -78,6 +79,7 @@ struct mt_slot {
        __s32 contactid;        /* the device ContactID assigned to this slot */
        bool touch_state;       /* is the touch valid? */
        bool inrange_state;     /* is the finger in proximity of the sensor? */
+       bool confidence_state;  /* is the touch made by a finger? */
 };
 
 struct mt_class {
@@ -503,10 +505,8 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                        return 1;
                case HID_DG_CONFIDENCE:
                        if (cls->name == MT_CLS_WIN_8 &&
-                               field->application == HID_DG_TOUCHPAD) {
-                               cls->quirks &= ~MT_QUIRK_ALWAYS_VALID;
-                               cls->quirks |= MT_QUIRK_VALID_IS_CONFIDENCE;
-                       }
+                               field->application == HID_DG_TOUCHPAD)
+                               cls->quirks |= MT_QUIRK_CONFIDENCE;
                        mt_store_field(usage, td, hi);
                        return 1;
                case HID_DG_TIPSWITCH:
@@ -619,6 +619,7 @@ static void mt_complete_slot(struct mt_device *td, struct input_dev *input)
                return;
 
        if (td->curvalid || (td->mtclass.quirks & MT_QUIRK_ALWAYS_VALID)) {
+               int active;
                int slotnum = mt_compute_slot(td, input);
                struct mt_slot *s = &td->curdata;
                struct input_mt *mt = input->mt;
@@ -633,10 +634,14 @@ static void mt_complete_slot(struct mt_device *td, struct input_dev *input)
                                return;
                }
 
+               if (!(td->mtclass.quirks & MT_QUIRK_CONFIDENCE))
+                       s->confidence_state = 1;
+               active = (s->touch_state || s->inrange_state) &&
+                                                       s->confidence_state;
+
                input_mt_slot(input, slotnum);
-               input_mt_report_slot_state(input, MT_TOOL_FINGER,
-                       s->touch_state || s->inrange_state);
-               if (s->touch_state || s->inrange_state) {
+               input_mt_report_slot_state(input, MT_TOOL_FINGER, active);
+               if (active) {
                        /* this finger is in proximity of the sensor */
                        int wide = (s->w > s->h);
                        /* divided by two to match visual scale of touch */
@@ -701,6 +706,8 @@ static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field,
                        td->curdata.touch_state = value;
                        break;
                case HID_DG_CONFIDENCE:
+                       if (quirks & MT_QUIRK_CONFIDENCE)
+                               td->curdata.confidence_state = value;
                        if (quirks & MT_QUIRK_VALID_IS_CONFIDENCE)
                                td->curvalid = value;
                        break;
index cc6439ab3f714145f4d86c49c3d3c79716a058b1..041050edd80991713d9bb5a776c9a1d402f23bf5 100644 (file)
@@ -1268,6 +1268,8 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
                }
        }
 
+       idx = 0;
+
        do {
                if (msgs[idx].len == 0) {
                        ret = -EINVAL;
index 445398c314a3303a41ba51313bbcbfd3a40c421c..b126dbaa47e37014acaf78ba528a6e177bffa9d8 100644 (file)
@@ -912,7 +912,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
        ret = tegra_i2c_init(i2c_dev);
        if (ret) {
                dev_err(&pdev->dev, "Failed to initialize i2c controller");
-               goto unprepare_div_clk;
+               goto disable_div_clk;
        }
 
        ret = devm_request_irq(&pdev->dev, i2c_dev->irq,
index e33022e2d459f18b57027337c9c9de9243488c07..6e5fac6a5262a0824d69ebe534b18fd855d35922 100644 (file)
@@ -56,9 +56,7 @@ EXPORT_SYMBOL_GPL(__i2c_first_dynamic_bus_num);
  * The board info passed can safely be __initdata, but be careful of embedded
  * pointers (for platform_data, functions, etc) since that won't be copied.
  */
-int __init
-i2c_register_board_info(int busnum,
-       struct i2c_board_info const *info, unsigned len)
+int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
 {
        int status;
 
index 26e7c5187a589b473857fd991acdd60a1bdb4609..c6a90b4a9c626dcf4fdf65e51e2f01b5b6ac4968 100644 (file)
@@ -145,7 +145,7 @@ static int i2c_mux_reg_probe_dt(struct regmux *mux,
                mux->data.idle_in_use = true;
 
        /* map address from "reg" if exists */
-       if (of_address_to_resource(np, 0, &res)) {
+       if (of_address_to_resource(np, 0, &res) == 0) {
                mux->data.reg_size = resource_size(&res);
                mux->data.reg = devm_ioremap_resource(&pdev->dev, &res);
                if (IS_ERR(mux->data.reg))
index 923f56598d4b51a84ee7b5b322c2c9bfefe4f0a5..3a9f106787d28b2f85248402b68628fd02604460 100644 (file)
@@ -81,7 +81,7 @@ static int kxsd9_write_scale(struct iio_dev *indio_dev, int micro)
 
        mutex_lock(&st->buf_lock);
        ret = spi_w8r8(st->us, KXSD9_READ(KXSD9_REG_CTRL_C));
-       if (ret)
+       if (ret < 0)
                goto error_ret;
        st->tx[0] = KXSD9_WRITE(KXSD9_REG_CTRL_C);
        st->tx[1] = (ret & ~KXSD9_FS_MASK) | i;
@@ -163,7 +163,7 @@ static int kxsd9_read_raw(struct iio_dev *indio_dev,
                break;
        case IIO_CHAN_INFO_SCALE:
                ret = spi_w8r8(st->us, KXSD9_READ(KXSD9_REG_CTRL_C));
-               if (ret)
+               if (ret < 0)
                        goto error_ret;
                *val2 = kxsd9_micro_scales[ret & KXSD9_FS_MASK];
                ret = IIO_VAL_INT_PLUS_MICRO;
index 21e19b60e2b98305b209a648690e8f18f64b40f9..2123f0ac2e2a47b608eedc6095f1681802edfcd9 100644 (file)
@@ -396,8 +396,8 @@ static int ad7266_probe(struct spi_device *spi)
 
        st = iio_priv(indio_dev);
 
-       st->reg = devm_regulator_get(&spi->dev, "vref");
-       if (!IS_ERR_OR_NULL(st->reg)) {
+       st->reg = devm_regulator_get_optional(&spi->dev, "vref");
+       if (!IS_ERR(st->reg)) {
                ret = regulator_enable(st->reg);
                if (ret)
                        return ret;
@@ -408,6 +408,9 @@ static int ad7266_probe(struct spi_device *spi)
 
                st->vref_mv = ret / 1000;
        } else {
+               /* Any other error indicates that the regulator does exist */
+               if (PTR_ERR(st->reg) != -ENODEV)
+                       return PTR_ERR(st->reg);
                /* Use internal reference */
                st->vref_mv = 2500;
        }
index f62b8bd9ad7ef0fb440e8d28a55061db4ffb3496..dd6fc6d21f9d0c6057b6b5029800eef2fa82dacb 100644 (file)
@@ -56,6 +56,7 @@ static int asus_acpi_get_sensor_info(struct acpi_device *adev,
        int i;
        acpi_status status;
        union acpi_object *cpm;
+       int ret;
 
        status = acpi_evaluate_object(adev->handle, "CNF0", NULL, &buffer);
        if (ACPI_FAILURE(status))
@@ -82,10 +83,10 @@ static int asus_acpi_get_sensor_info(struct acpi_device *adev,
                        }
                }
        }
-
+       ret = cpm->package.count;
        kfree(buffer.pointer);
 
-       return cpm->package.count;
+       return ret;
 }
 
 static int acpi_i2c_check_resource(struct acpi_resource *ares, void *data)
index a5793c8f15901483e24e24393173ea8dbee6b191..60df4f8e81bed10ac8e9b6149ec4c3ef5c6abf9f 100644 (file)
@@ -530,6 +530,7 @@ static PORT_PMA_ATTR(port_xmit_data             , 12, 32, 192);
 static PORT_PMA_ATTR(port_rcv_data                 , 13, 32, 224);
 static PORT_PMA_ATTR(port_xmit_packets             , 14, 32, 256);
 static PORT_PMA_ATTR(port_rcv_packets              , 15, 32, 288);
+static PORT_PMA_ATTR(port_xmit_wait                ,  0, 32, 320);
 
 /*
  * Counters added by extended set
@@ -560,6 +561,7 @@ static struct attribute *pma_attrs[] = {
        &port_pma_attr_port_rcv_data.attr.attr,
        &port_pma_attr_port_xmit_packets.attr.attr,
        &port_pma_attr_port_rcv_packets.attr.attr,
+       &port_pma_attr_port_xmit_wait.attr.attr,
        NULL
 };
 
@@ -579,6 +581,7 @@ static struct attribute *pma_attrs_ext[] = {
        &port_pma_attr_ext_port_xmit_data.attr.attr,
        &port_pma_attr_ext_port_rcv_data.attr.attr,
        &port_pma_attr_ext_port_xmit_packets.attr.attr,
+       &port_pma_attr_port_xmit_wait.attr.attr,
        &port_pma_attr_ext_port_rcv_packets.attr.attr,
        &port_pma_attr_ext_unicast_rcv_packets.attr.attr,
        &port_pma_attr_ext_unicast_xmit_packets.attr.attr,
@@ -604,6 +607,7 @@ static struct attribute *pma_attrs_noietf[] = {
        &port_pma_attr_ext_port_rcv_data.attr.attr,
        &port_pma_attr_ext_port_xmit_packets.attr.attr,
        &port_pma_attr_ext_port_rcv_packets.attr.attr,
+       &port_pma_attr_port_xmit_wait.attr.attr,
        NULL
 };
 
index f5de851780555a5c9f06d91c2eb9bb2ff14c666e..dad4d0ebbdffb45e2c667cc95b86aabf39b53a87 100644 (file)
@@ -14113,8 +14113,14 @@ static int init_asic_data(struct hfi1_devdata *dd)
 {
        unsigned long flags;
        struct hfi1_devdata *tmp, *peer = NULL;
+       struct hfi1_asic_data *asic_data;
        int ret = 0;
 
+       /* pre-allocate the asic structure in case we are the first device */
+       asic_data = kzalloc(sizeof(*dd->asic_data), GFP_KERNEL);
+       if (!asic_data)
+               return -ENOMEM;
+
        spin_lock_irqsave(&hfi1_devs_lock, flags);
        /* Find our peer device */
        list_for_each_entry(tmp, &hfi1_dev_list, list) {
@@ -14126,18 +14132,14 @@ static int init_asic_data(struct hfi1_devdata *dd)
        }
 
        if (peer) {
+               /* use already allocated structure */
                dd->asic_data = peer->asic_data;
+               kfree(asic_data);
        } else {
-               dd->asic_data = kzalloc(sizeof(*dd->asic_data), GFP_KERNEL);
-               if (!dd->asic_data) {
-                       ret = -ENOMEM;
-                       goto done;
-               }
+               dd->asic_data = asic_data;
                mutex_init(&dd->asic_data->asic_resource_mutex);
        }
        dd->asic_data->dds[dd->hfi1_id] = dd; /* self back-pointer */
-
-done:
        spin_unlock_irqrestore(&hfi1_devs_lock, flags);
        return ret;
 }
index 1e503ad0bebb764ee1e05462604538e6324002f8..be91f6fa1c87b16fe4f31affd6c12848b1951792 100644 (file)
@@ -678,8 +678,7 @@ void hfi1_ud_rcv(struct hfi1_packet *packet)
        u32 tlen = packet->tlen;
        struct rvt_qp *qp = packet->qp;
        bool has_grh = rcv_flags & HFI1_HAS_GRH;
-       bool sc4_bit = has_sc4_bit(packet);
-       u8 sc;
+       u8 sc5 = hdr2sc((struct hfi1_message_header *)hdr, packet->rhf);
        u32 bth1;
        int is_mcast;
        struct ib_grh *grh = NULL;
@@ -697,10 +696,8 @@ void hfi1_ud_rcv(struct hfi1_packet *packet)
                 */
                struct hfi1_pportdata *ppd = ppd_from_ibp(ibp);
                u32 lqpn =  be32_to_cpu(ohdr->bth[1]) & RVT_QPN_MASK;
-               u8 sl, sc5;
+               u8 sl;
 
-               sc5 = (be16_to_cpu(hdr->lrh[0]) >> 12) & 0xf;
-               sc5 |= sc4_bit;
                sl = ibp->sc_to_sl[sc5];
 
                process_becn(ppd, sl, 0, lqpn, 0, IB_CC_SVCTYPE_UD);
@@ -717,10 +714,6 @@ void hfi1_ud_rcv(struct hfi1_packet *packet)
 
        if (!is_mcast && (opcode != IB_OPCODE_CNP) && bth1 & HFI1_FECN_SMASK) {
                u16 slid = be16_to_cpu(hdr->lrh[3]);
-               u8 sc5;
-
-               sc5 = (be16_to_cpu(hdr->lrh[0]) >> 12) & 0xf;
-               sc5 |= sc4_bit;
 
                return_cnp(ibp, qp, src_qp, pkey, dlid, slid, sc5, grh);
        }
@@ -745,10 +738,6 @@ void hfi1_ud_rcv(struct hfi1_packet *packet)
                if (qp->ibqp.qp_num > 1) {
                        struct hfi1_pportdata *ppd = ppd_from_ibp(ibp);
                        u16 slid;
-                       u8 sc5;
-
-                       sc5 = (be16_to_cpu(hdr->lrh[0]) >> 12) & 0xf;
-                       sc5 |= sc4_bit;
 
                        slid = be16_to_cpu(hdr->lrh[3]);
                        if (unlikely(rcv_pkey_check(ppd, pkey, sc5, slid))) {
@@ -790,10 +779,6 @@ void hfi1_ud_rcv(struct hfi1_packet *packet)
                /* Received on QP0, and so by definition, this is an SMP */
                struct opa_smp *smp = (struct opa_smp *)data;
                u16 slid = be16_to_cpu(hdr->lrh[3]);
-               u8 sc5;
-
-               sc5 = (be16_to_cpu(hdr->lrh[0]) >> 12) & 0xf;
-               sc5 |= sc4_bit;
 
                if (opa_smp_check(ibp, pkey, sc5, qp, slid, smp))
                        goto drop;
@@ -890,9 +875,7 @@ void hfi1_ud_rcv(struct hfi1_packet *packet)
        }
 
        wc.slid = be16_to_cpu(hdr->lrh[3]);
-       sc = (be16_to_cpu(hdr->lrh[0]) >> 12) & 0xf;
-       sc |= sc4_bit;
-       wc.sl = ibp->sc_to_sl[sc];
+       wc.sl = ibp->sc_to_sl[sc5];
 
        /*
         * Save the LMC lower bits if the destination LID is a unicast LID.
index c963cad92f5a8eb061af74ae964e9222115e1815..6e9081380a276cbb78da8687820b9c7092f684af 100644 (file)
@@ -600,8 +600,7 @@ static enum i40iw_status_code i40iw_create_cqp(struct i40iw_device *iwdev)
        cqp_init_info.scratch_array = cqp->scratch_array;
        status = dev->cqp_ops->cqp_init(dev->cqp, &cqp_init_info);
        if (status) {
-               i40iw_pr_err("cqp init status %d maj_err %d min_err %d\n",
-                            status, maj_err, min_err);
+               i40iw_pr_err("cqp init status %d\n", status);
                goto exit;
        }
        status = dev->cqp_ops->cqp_create(dev->cqp, true, &maj_err, &min_err);
index 33959ed14563e0f43e5209320ba0fd5ff0338abb..283b64c942eebfea6378ee8f4ad5206e6f549876 100644 (file)
@@ -1474,6 +1474,7 @@ static int i40iw_hw_alloc_stag(struct i40iw_device *iwdev, struct i40iw_mr *iwmr
        info->stag_idx = iwmr->stag >> I40IW_CQPSQ_STAG_IDX_SHIFT;
        info->pd_id = iwpd->sc_pd.pd_id;
        info->total_len = iwmr->length;
+       info->remote_access = true;
        cqp_info->cqp_cmd = OP_ALLOC_STAG;
        cqp_info->post_sq = 1;
        cqp_info->in.u.alloc_stag.dev = &iwdev->sc_dev;
index 804dbcc37d3f70bc929d30bc63b13b9464f9aee0..a529a4535457217e761fd1e8f8b0748cb48a5498 100644 (file)
@@ -1031,17 +1031,17 @@ static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect
 
        case XTYPE_XBOXONE:
                packet->data[0] = 0x09; /* activate rumble */
-               packet->data[1] = 0x08;
+               packet->data[1] = 0x00;
                packet->data[2] = xpad->odata_serial++;
-               packet->data[3] = 0x08; /* continuous effect */
-               packet->data[4] = 0x00; /* simple rumble mode */
-               packet->data[5] = 0x03; /* L and R actuator only */
-               packet->data[6] = 0x00; /* TODO: LT actuator */
-               packet->data[7] = 0x00; /* TODO: RT actuator */
+               packet->data[3] = 0x09;
+               packet->data[4] = 0x00;
+               packet->data[5] = 0x0F;
+               packet->data[6] = 0x00;
+               packet->data[7] = 0x00;
                packet->data[8] = strong / 512; /* left actuator */
                packet->data[9] = weak / 512;   /* right actuator */
-               packet->data[10] = 0x80;        /* length of pulse */
-               packet->data[11] = 0x00;        /* stop period of pulse */
+               packet->data[10] = 0xFF;
+               packet->data[11] = 0x00;
                packet->data[12] = 0x00;
                packet->len = 13;
                packet->pending = true;
@@ -1431,22 +1431,15 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
        int ep_irq_in_idx;
        int i, error;
 
+       if (intf->cur_altsetting->desc.bNumEndpoints != 2)
+               return -ENODEV;
+
        for (i = 0; xpad_device[i].idVendor; i++) {
                if ((le16_to_cpu(udev->descriptor.idVendor) == xpad_device[i].idVendor) &&
                    (le16_to_cpu(udev->descriptor.idProduct) == xpad_device[i].idProduct))
                        break;
        }
 
-       if (xpad_device[i].xtype == XTYPE_XBOXONE &&
-           intf->cur_altsetting->desc.bInterfaceNumber != 0) {
-               /*
-                * The Xbox One controller lists three interfaces all with the
-                * same interface class, subclass and protocol. Differentiate by
-                * interface number.
-                */
-               return -ENODEV;
-       }
-
        xpad = kzalloc(sizeof(struct usb_xpad), GFP_KERNEL);
        if (!xpad)
                return -ENOMEM;
@@ -1478,6 +1471,8 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
                if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) {
                        if (intf->cur_altsetting->desc.bInterfaceProtocol == 129)
                                xpad->xtype = XTYPE_XBOX360W;
+                       else if (intf->cur_altsetting->desc.bInterfaceProtocol == 208)
+                               xpad->xtype = XTYPE_XBOXONE;
                        else
                                xpad->xtype = XTYPE_XBOX360;
                } else {
@@ -1492,6 +1487,17 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
                        xpad->mapping |= MAP_STICKS_TO_NULL;
        }
 
+       if (xpad->xtype == XTYPE_XBOXONE &&
+           intf->cur_altsetting->desc.bInterfaceNumber != 0) {
+               /*
+                * The Xbox One controller lists three interfaces all with the
+                * same interface class, subclass and protocol. Differentiate by
+                * interface number.
+                */
+               error = -ENODEV;
+               goto err_free_in_urb;
+       }
+
        error = xpad_init_output(intf, xpad);
        if (error)
                goto err_free_in_urb;
index 78f93cf68840d24328ebd0630f86c4e372d051bf..be5b399da5d3ec7b982fa49945bcc4d3be576b39 100644 (file)
@@ -1568,13 +1568,7 @@ static int elantech_set_properties(struct elantech_data *etd)
                case 5:
                        etd->hw_version = 3;
                        break;
-               case 6:
-               case 7:
-               case 8:
-               case 9:
-               case 10:
-               case 13:
-               case 14:
+               case 6 ... 14:
                        etd->hw_version = 4;
                        break;
                default:
index a3f0f5a47490e936e31b45594861d692503429b1..0f586780ceb4bee18a38428b889dba8d994ea4c7 100644 (file)
@@ -355,18 +355,11 @@ int vmmouse_detect(struct psmouse *psmouse, bool set_properties)
                return -ENXIO;
        }
 
-       if (!request_region(VMMOUSE_PROTO_PORT, 4, "vmmouse")) {
-               psmouse_dbg(psmouse, "VMMouse port in use.\n");
-               return -EBUSY;
-       }
-
        /* Check if the device is present */
        response = ~VMMOUSE_PROTO_MAGIC;
        VMMOUSE_CMD(GETVERSION, 0, version, response, dummy1, dummy2);
-       if (response != VMMOUSE_PROTO_MAGIC || version == 0xffffffffU) {
-               release_region(VMMOUSE_PROTO_PORT, 4);
+       if (response != VMMOUSE_PROTO_MAGIC || version == 0xffffffffU)
                return -ENXIO;
-       }
 
        if (set_properties) {
                psmouse->vendor = VMMOUSE_VENDOR;
@@ -374,8 +367,6 @@ int vmmouse_detect(struct psmouse *psmouse, bool set_properties)
                psmouse->model = version;
        }
 
-       release_region(VMMOUSE_PROTO_PORT, 4);
-
        return 0;
 }
 
@@ -394,7 +385,6 @@ static void vmmouse_disconnect(struct psmouse *psmouse)
        psmouse_reset(psmouse);
        input_unregister_device(priv->abs_dev);
        kfree(priv);
-       release_region(VMMOUSE_PROTO_PORT, 4);
 }
 
 /**
@@ -438,15 +428,10 @@ int vmmouse_init(struct psmouse *psmouse)
        struct input_dev *rel_dev = psmouse->dev, *abs_dev;
        int error;
 
-       if (!request_region(VMMOUSE_PROTO_PORT, 4, "vmmouse")) {
-               psmouse_dbg(psmouse, "VMMouse port in use.\n");
-               return -EBUSY;
-       }
-
        psmouse_reset(psmouse);
        error = vmmouse_enable(psmouse);
        if (error)
-               goto release_region;
+               return error;
 
        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
        abs_dev = input_allocate_device();
@@ -502,8 +487,5 @@ int vmmouse_init(struct psmouse *psmouse)
        kfree(priv);
        psmouse->private = NULL;
 
-release_region:
-       release_region(VMMOUSE_PROTO_PORT, 4);
-
        return error;
 }
index b368b0515c5a3aa26161dd7893a1c22c2678e4b2..253df96be4276cc9196a6c0ab25b04f838d061ac 100644 (file)
@@ -157,11 +157,11 @@ static int rmi_function_match(struct device *dev, struct device_driver *drv)
 static void rmi_function_of_probe(struct rmi_function *fn)
 {
        char of_name[9];
+       struct device_node *node = fn->rmi_dev->xport->dev->of_node;
 
        snprintf(of_name, sizeof(of_name), "rmi4-f%02x",
                fn->fd.function_number);
-       fn->dev.of_node = of_find_node_by_name(
-                               fn->rmi_dev->xport->dev->of_node, of_name);
+       fn->dev.of_node = of_get_child_by_name(node, of_name);
 }
 #else
 static inline void rmi_function_of_probe(struct rmi_function *fn)
index 8dd3fb5e1f9433f013f18c51728205c425a61560..88e91559c84e2dcad267a0e20fd4e65bc56238bb 100644 (file)
@@ -66,7 +66,7 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12)
        struct rmi_device *rmi_dev = fn->rmi_dev;
        int ret;
        int offset;
-       u8 buf[14];
+       u8 buf[15];
        int pitch_x = 0;
        int pitch_y = 0;
        int clip_x_low = 0;
@@ -86,9 +86,10 @@ static int rmi_f12_read_sensor_tuning(struct f12_data *f12)
 
        offset = rmi_register_desc_calc_reg_offset(&f12->control_reg_desc, 8);
 
-       if (item->reg_size > 14) {
-               dev_err(&fn->dev, "F12 control8 should be 14 bytes, not: %ld\n",
-                       item->reg_size);
+       if (item->reg_size > sizeof(buf)) {
+               dev_err(&fn->dev,
+                       "F12 control8 should be no bigger than %zd bytes, not: %ld\n",
+                       sizeof(buf), item->reg_size);
                return -ENODEV;
        }
 
index 3c3dd78303be0e68f1373cb01d6609be6a1efbc7..fed73eeb47b3c6e263ca2e0415d9b385cc2f4854 100644 (file)
@@ -118,6 +118,13 @@ static int ts4800_parse_dt(struct platform_device *pdev,
                return -ENODEV;
        }
 
+       ts->regmap = syscon_node_to_regmap(syscon_np);
+       of_node_put(syscon_np);
+       if (IS_ERR(ts->regmap)) {
+               dev_err(dev, "cannot get parent's regmap\n");
+               return PTR_ERR(ts->regmap);
+       }
+
        error = of_property_read_u32_index(np, "syscon", 1, &reg);
        if (error < 0) {
                dev_err(dev, "no offset in syscon\n");
@@ -134,12 +141,6 @@ static int ts4800_parse_dt(struct platform_device *pdev,
 
        ts->bit = BIT(bit);
 
-       ts->regmap = syscon_node_to_regmap(syscon_np);
-       if (IS_ERR(ts->regmap)) {
-               dev_err(dev, "cannot get parent's regmap\n");
-               return PTR_ERR(ts->regmap);
-       }
-
        return 0;
 }
 
index 7295c198aa086be8630b3a77e202415649c6937b..6fe55d598facac3a05114e57e03b92b9d162eb87 100644 (file)
 #include <linux/regmap.h>
 #include "tsc200x-core.h"
 
+static const struct input_id tsc2004_input_id = {
+       .bustype = BUS_I2C,
+       .product = 2004,
+};
+
 static int tsc2004_cmd(struct device *dev, u8 cmd)
 {
        u8 tx = TSC200X_CMD | TSC200X_CMD_12BIT | cmd;
@@ -42,7 +47,7 @@ static int tsc2004_probe(struct i2c_client *i2c,
                         const struct i2c_device_id *id)
 
 {
-       return tsc200x_probe(&i2c->dev, i2c->irq, BUS_I2C,
+       return tsc200x_probe(&i2c->dev, i2c->irq, &tsc2004_input_id,
                             devm_regmap_init_i2c(i2c, &tsc200x_regmap_config),
                             tsc2004_cmd);
 }
index b9f593dfd2ef8368223e5d7d7d9ca06856afcc8d..f2c5f0e47f77dd6ab177adec2bb102ed0da5dd08 100644 (file)
 #include <linux/regmap.h>
 #include "tsc200x-core.h"
 
+static const struct input_id tsc2005_input_id = {
+       .bustype = BUS_SPI,
+       .product = 2005,
+};
+
 static int tsc2005_cmd(struct device *dev, u8 cmd)
 {
        u8 tx = TSC200X_CMD | TSC200X_CMD_12BIT | cmd;
@@ -62,7 +67,7 @@ static int tsc2005_probe(struct spi_device *spi)
        if (error)
                return error;
 
-       return tsc200x_probe(&spi->dev, spi->irq, BUS_SPI,
+       return tsc200x_probe(&spi->dev, spi->irq, &tsc2005_input_id,
                             devm_regmap_init_spi(spi, &tsc200x_regmap_config),
                             tsc2005_cmd);
 }
index 15240c1ee850abd4f5099db9a9e21a4b8ecf725b..dfa7f1c4f5453bd9d1544badc24548eab0851361 100644 (file)
@@ -450,7 +450,7 @@ static void tsc200x_close(struct input_dev *input)
        mutex_unlock(&ts->mutex);
 }
 
-int tsc200x_probe(struct device *dev, int irq, __u16 bustype,
+int tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id,
                  struct regmap *regmap,
                  int (*tsc200x_cmd)(struct device *dev, u8 cmd))
 {
@@ -547,9 +547,18 @@ int tsc200x_probe(struct device *dev, int irq, __u16 bustype,
        snprintf(ts->phys, sizeof(ts->phys),
                 "%s/input-ts", dev_name(dev));
 
-       input_dev->name = "TSC200X touchscreen";
+       if (tsc_id->product == 2004) {
+               input_dev->name = "TSC200X touchscreen";
+       } else {
+               input_dev->name = devm_kasprintf(dev, GFP_KERNEL,
+                                                "TSC%04d touchscreen",
+                                                tsc_id->product);
+               if (!input_dev->name)
+                       return -ENOMEM;
+       }
+
        input_dev->phys = ts->phys;
-       input_dev->id.bustype = bustype;
+       input_dev->id = *tsc_id;
        input_dev->dev.parent = dev;
        input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
        input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
index 7a482d1026148c692988172773af1c3f2ad9e57e..49a63a3c684094a331a7465dda370f1a2f0fcb21 100644 (file)
@@ -70,7 +70,7 @@
 extern const struct regmap_config tsc200x_regmap_config;
 extern const struct dev_pm_ops tsc200x_pm_ops;
 
-int tsc200x_probe(struct device *dev, int irq, __u16 bustype,
+int tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id,
                  struct regmap *regmap,
                  int (*tsc200x_cmd)(struct device *dev, u8 cmd));
 int tsc200x_remove(struct device *dev);
index bab3c6acf6a228f13fcb44756b43374293bf030f..b6fc4bde79ded75430725f97f72d7a9c3bae2c6b 100644 (file)
@@ -27,7 +27,7 @@ MODULE_AUTHOR("Jaya Kumar <jayakumar.lkml@gmail.com>");
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
 
-#define W8001_MAX_LENGTH       11
+#define W8001_MAX_LENGTH       13
 #define W8001_LEAD_MASK                0x80
 #define W8001_LEAD_BYTE                0x80
 #define W8001_TAB_MASK         0x40
@@ -155,6 +155,7 @@ static void parse_multi_touch(struct w8001 *w8001)
                bool touch = data[0] & (1 << i);
 
                input_mt_slot(dev, i);
+               input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch);
                if (touch) {
                        x = (data[6 * i + 1] << 7) | data[6 * i + 2];
                        y = (data[6 * i + 3] << 7) | data[6 * i + 4];
@@ -339,6 +340,15 @@ static irqreturn_t w8001_interrupt(struct serio *serio,
                w8001->idx = 0;
                parse_multi_touch(w8001);
                break;
+
+       default:
+               /*
+                * ThinkPad X60 Tablet PC (pen only device) sometimes
+                * sends invalid data packets that are larger than
+                * W8001_PKTLEN_TPCPEN. Let's start over again.
+                */
+               if (!w8001->touch_dev && w8001->idx > W8001_PKTLEN_TPCPEN - 1)
+                       w8001->idx = 0;
        }
 
        return IRQ_HANDLED;
@@ -513,6 +523,8 @@ static int w8001_setup_touch(struct w8001 *w8001, char *basename,
                                        0, touch.x, 0, 0);
                input_set_abs_params(dev, ABS_MT_POSITION_Y,
                                        0, touch.y, 0, 0);
+               input_set_abs_params(dev, ABS_MT_TOOL_TYPE,
+                                       0, MT_TOOL_MAX, 0, 0);
 
                strlcat(basename, " 2FG", basename_sz);
                if (w8001->max_pen_x && w8001->max_pen_y)
index 9e0034196e10468ea6e624b64737a1487dade7fa..59741ead7e158ca4e7b0d6506a0529c03c3fe412 100644 (file)
@@ -1107,13 +1107,13 @@ static int __init init_iommu_from_acpi(struct amd_iommu *iommu,
                                break;
                        }
 
+                       devid = e->devid;
                        DUMP_printk("  DEV_ACPI_HID(%s[%s])\t\tdevid: %02x:%02x.%x\n",
                                    hid, uid,
                                    PCI_BUS_NUM(devid),
                                    PCI_SLOT(devid),
                                    PCI_FUNC(devid));
 
-                       devid  = e->devid;
                        flags = e->flags;
 
                        ret = add_acpi_hid_device(hid, uid, &devid, false);
@@ -1568,13 +1568,23 @@ static int __init amd_iommu_init_pci(void)
                        break;
        }
 
+       /*
+        * Order is important here to make sure any unity map requirements are
+        * fulfilled. The unity mappings are created and written to the device
+        * table during the amd_iommu_init_api() call.
+        *
+        * After that we call init_device_table_dma() to make sure any
+        * uninitialized DTE will block DMA, and in the end we flush the caches
+        * of all IOMMUs to make sure the changes to the device table are
+        * active.
+        */
+       ret = amd_iommu_init_api();
+
        init_device_table_dma();
 
        for_each_iommu(iommu)
                iommu_flush_all_caches(iommu);
 
-       ret = amd_iommu_init_api();
-
        if (!ret)
                print_iommu_info();
 
index 10700945994eed2e470c92d267c0bb5723ed0d1a..323dac9900ba3cc0a0c1351dfc39de86b4cb06ab 100644 (file)
@@ -4602,13 +4602,13 @@ static void free_all_cpu_cached_iovas(unsigned int cpu)
        for (i = 0; i < g_num_of_iommus; i++) {
                struct intel_iommu *iommu = g_iommus[i];
                struct dmar_domain *domain;
-               u16 did;
+               int did;
 
                if (!iommu)
                        continue;
 
-               for (did = 0; did < 0xffff; did++) {
-                       domain = get_iommu_domain(iommu, did);
+               for (did = 0; did < cap_ndoms(iommu->cap); did++) {
+                       domain = get_iommu_domain(iommu, (u16)did);
 
                        if (!domain)
                                continue;
index ba764a0835d3cd881981f0e049956e6f71a62788..e23001bfcfee996f8ab18f1695d1e3476bc6b9c2 100644 (file)
@@ -420,8 +420,10 @@ alloc_iova_fast(struct iova_domain *iovad, unsigned long size,
 
                /* Try replenishing IOVAs by flushing rcache. */
                flushed_rcache = true;
+               preempt_disable();
                for_each_online_cpu(cpu)
                        free_cpu_cached_iovas(cpu, iovad);
+               preempt_enable();
                goto retry;
        }
 
@@ -749,7 +751,7 @@ static bool __iova_rcache_insert(struct iova_domain *iovad,
        bool can_insert = false;
        unsigned long flags;
 
-       cpu_rcache = this_cpu_ptr(rcache->cpu_rcaches);
+       cpu_rcache = get_cpu_ptr(rcache->cpu_rcaches);
        spin_lock_irqsave(&cpu_rcache->lock, flags);
 
        if (!iova_magazine_full(cpu_rcache->loaded)) {
@@ -779,6 +781,7 @@ static bool __iova_rcache_insert(struct iova_domain *iovad,
                iova_magazine_push(cpu_rcache->loaded, iova_pfn);
 
        spin_unlock_irqrestore(&cpu_rcache->lock, flags);
+       put_cpu_ptr(rcache->cpu_rcaches);
 
        if (mag_to_free) {
                iova_magazine_free_pfns(mag_to_free, iovad);
@@ -812,7 +815,7 @@ static unsigned long __iova_rcache_get(struct iova_rcache *rcache,
        bool has_pfn = false;
        unsigned long flags;
 
-       cpu_rcache = this_cpu_ptr(rcache->cpu_rcaches);
+       cpu_rcache = get_cpu_ptr(rcache->cpu_rcaches);
        spin_lock_irqsave(&cpu_rcache->lock, flags);
 
        if (!iova_magazine_empty(cpu_rcache->loaded)) {
@@ -834,6 +837,7 @@ static unsigned long __iova_rcache_get(struct iova_rcache *rcache,
                iova_pfn = iova_magazine_pop(cpu_rcache->loaded, limit_pfn);
 
        spin_unlock_irqrestore(&cpu_rcache->lock, flags);
+       put_cpu_ptr(rcache->cpu_rcaches);
 
        return iova_pfn;
 }
index 8a4adbeb2b8cd694a08e18bfb3d2df8d31b7e937..70ed1d0151b8b271018d8fcf7465342843f38467 100644 (file)
@@ -718,7 +718,7 @@ static int gic_shared_irq_domain_map(struct irq_domain *d, unsigned int virq,
 
        spin_lock_irqsave(&gic_lock, flags);
        gic_map_to_pin(intr, gic_cpu_pin);
-       gic_map_to_vpe(intr, vpe);
+       gic_map_to_vpe(intr, mips_cm_vp_id(vpe));
        for (i = 0; i < min(gic_vpes, NR_CPUS); i++)
                clear_bit(intr, pcpu_masks[i].pcpu_mask);
        set_bit(intr, pcpu_masks[vpe].pcpu_mask);
@@ -959,7 +959,7 @@ int gic_ipi_domain_match(struct irq_domain *d, struct device_node *node,
        switch (bus_token) {
        case DOMAIN_BUS_IPI:
                is_ipi = d->bus_token == bus_token;
-               return to_of_node(d->fwnode) == node && is_ipi;
+               return (!node || to_of_node(d->fwnode) == node) && is_ipi;
                break;
        default:
                return 0;
index beb2841ceae58bbb69d65d99bf3697ee6f5dc686..3f1ab4986cfc507d2cf70a451cbbf2962deac2b8 100644 (file)
@@ -779,11 +779,31 @@ static const struct v4l2_dv_timings_cap adv76xx_timings_cap_digital = {
                        V4L2_DV_BT_CAP_CUSTOM)
 };
 
-static inline const struct v4l2_dv_timings_cap *
-adv76xx_get_dv_timings_cap(struct v4l2_subdev *sd)
+/*
+ * Return the DV timings capabilities for the requested sink pad. As a special
+ * case, pad value -1 returns the capabilities for the currently selected input.
+ */
+static const struct v4l2_dv_timings_cap *
+adv76xx_get_dv_timings_cap(struct v4l2_subdev *sd, int pad)
 {
-       return is_digital_input(sd) ? &adv76xx_timings_cap_digital :
-                                     &adv7604_timings_cap_analog;
+       if (pad == -1) {
+               struct adv76xx_state *state = to_state(sd);
+
+               pad = state->selected_input;
+       }
+
+       switch (pad) {
+       case ADV76XX_PAD_HDMI_PORT_A:
+       case ADV7604_PAD_HDMI_PORT_B:
+       case ADV7604_PAD_HDMI_PORT_C:
+       case ADV7604_PAD_HDMI_PORT_D:
+               return &adv76xx_timings_cap_digital;
+
+       case ADV7604_PAD_VGA_RGB:
+       case ADV7604_PAD_VGA_COMP:
+       default:
+               return &adv7604_timings_cap_analog;
+       }
 }
 
 
@@ -1329,7 +1349,7 @@ static int stdi2dv_timings(struct v4l2_subdev *sd,
                const struct v4l2_bt_timings *bt = &v4l2_dv_timings_presets[i].bt;
 
                if (!v4l2_valid_dv_timings(&v4l2_dv_timings_presets[i],
-                                          adv76xx_get_dv_timings_cap(sd),
+                                          adv76xx_get_dv_timings_cap(sd, -1),
                                           adv76xx_check_dv_timings, NULL))
                        continue;
                if (vtotal(bt) != stdi->lcf + 1)
@@ -1430,18 +1450,22 @@ static int adv76xx_enum_dv_timings(struct v4l2_subdev *sd,
                return -EINVAL;
 
        return v4l2_enum_dv_timings_cap(timings,
-               adv76xx_get_dv_timings_cap(sd), adv76xx_check_dv_timings, NULL);
+               adv76xx_get_dv_timings_cap(sd, timings->pad),
+               adv76xx_check_dv_timings, NULL);
 }
 
 static int adv76xx_dv_timings_cap(struct v4l2_subdev *sd,
                        struct v4l2_dv_timings_cap *cap)
 {
        struct adv76xx_state *state = to_state(sd);
+       unsigned int pad = cap->pad;
 
        if (cap->pad >= state->source_pad)
                return -EINVAL;
 
-       *cap = *adv76xx_get_dv_timings_cap(sd);
+       *cap = *adv76xx_get_dv_timings_cap(sd, pad);
+       cap->pad = pad;
+
        return 0;
 }
 
@@ -1450,9 +1474,9 @@ static int adv76xx_dv_timings_cap(struct v4l2_subdev *sd,
 static void adv76xx_fill_optional_dv_timings_fields(struct v4l2_subdev *sd,
                struct v4l2_dv_timings *timings)
 {
-       v4l2_find_dv_timings_cap(timings, adv76xx_get_dv_timings_cap(sd),
-                       is_digital_input(sd) ? 250000 : 1000000,
-                       adv76xx_check_dv_timings, NULL);
+       v4l2_find_dv_timings_cap(timings, adv76xx_get_dv_timings_cap(sd, -1),
+                                is_digital_input(sd) ? 250000 : 1000000,
+                                adv76xx_check_dv_timings, NULL);
 }
 
 static unsigned int adv7604_read_hdmi_pixelclock(struct v4l2_subdev *sd)
@@ -1620,7 +1644,7 @@ static int adv76xx_s_dv_timings(struct v4l2_subdev *sd,
 
        bt = &timings->bt;
 
-       if (!v4l2_valid_dv_timings(timings, adv76xx_get_dv_timings_cap(sd),
+       if (!v4l2_valid_dv_timings(timings, adv76xx_get_dv_timings_cap(sd, -1),
                                   adv76xx_check_dv_timings, NULL))
                return -ERANGE;
 
index 87c12930416f565a118c145501e54df1a6b13cc7..92d9d4214c3adb326dc1a23d0c2c43840172611c 100644 (file)
@@ -1072,7 +1072,7 @@ static int airspy_probe(struct usb_interface *intf,
        if (ret) {
                dev_err(s->dev, "Failed to register as video device (%d)\n",
                                ret);
-               goto err_unregister_v4l2_dev;
+               goto err_free_controls;
        }
        dev_info(s->dev, "Registered as %s\n",
                        video_device_node_name(&s->vdev));
@@ -1081,7 +1081,6 @@ static int airspy_probe(struct usb_interface *intf,
 
 err_free_controls:
        v4l2_ctrl_handler_free(&s->hdl);
-err_unregister_v4l2_dev:
        v4l2_device_unregister(&s->v4l2_dev);
 err_free_mem:
        kfree(s);
index 28e5be2c2eef22f75a1a902131ee987bb927ad0d..528390f33b5336842491d039658a87f11b3a75f2 100644 (file)
@@ -2171,7 +2171,7 @@ static int v4l_cropcap(const struct v4l2_ioctl_ops *ops,
         * The determine_valid_ioctls() call already should ensure
         * that this can never happen, but just in case...
         */
-       if (WARN_ON(!ops->vidioc_cropcap && !ops->vidioc_cropcap))
+       if (WARN_ON(!ops->vidioc_cropcap && !ops->vidioc_g_selection))
                return -ENOTTY;
 
        if (ops->vidioc_cropcap)
index 199d261990be58c6c7ebaf9f783000d03d51098b..f32fbb8e8129e0b013b766c98457446d9588117d 100644 (file)
@@ -203,6 +203,7 @@ static int max77620_get_fps_period_reg_value(struct max77620_chip *chip,
                break;
        case MAX77620:
                fps_min_period = MAX77620_FPS_PERIOD_MIN_US;
+               break;
        default:
                return -EINVAL;
        }
@@ -236,6 +237,7 @@ static int max77620_config_fps(struct max77620_chip *chip,
                break;
        case MAX77620:
                fps_max_period = MAX77620_FPS_PERIOD_MAX_US;
+               break;
        default:
                return -EINVAL;
        }
index e62fde3ac431c111ec945d4aba9c877f6308d81d..c5472e3c923126097fd93f2abf31402a1717c228 100644 (file)
@@ -355,8 +355,10 @@ static struct mmc_blk_ioc_data *mmc_blk_ioctl_copy_from_user(
                goto idata_err;
        }
 
-       if (!idata->buf_bytes)
+       if (!idata->buf_bytes) {
+               idata->buf = NULL;
                return idata;
+       }
 
        idata->buf = kmalloc(idata->buf_bytes, GFP_KERNEL);
        if (!idata->buf) {
@@ -1786,8 +1788,8 @@ static void mmc_blk_packed_hdr_wrq_prep(struct mmc_queue_req *mqrq,
 
        packed_cmd_hdr = packed->cmd_hdr;
        memset(packed_cmd_hdr, 0, sizeof(packed->cmd_hdr));
-       packed_cmd_hdr[0] = (packed->nr_entries << 16) |
-               (PACKED_CMD_WR << 8) | PACKED_CMD_VER;
+       packed_cmd_hdr[0] = cpu_to_le32((packed->nr_entries << 16) |
+               (PACKED_CMD_WR << 8) | PACKED_CMD_VER);
        hdr_blocks = mmc_large_sector(card) ? 8 : 1;
 
        /*
@@ -1801,14 +1803,14 @@ static void mmc_blk_packed_hdr_wrq_prep(struct mmc_queue_req *mqrq,
                        ((brq->data.blocks * brq->data.blksz) >=
                         card->ext_csd.data_tag_unit_size);
                /* Argument of CMD23 */
-               packed_cmd_hdr[(i * 2)] =
+               packed_cmd_hdr[(i * 2)] = cpu_to_le32(
                        (do_rel_wr ? MMC_CMD23_ARG_REL_WR : 0) |
                        (do_data_tag ? MMC_CMD23_ARG_TAG_REQ : 0) |
-                       blk_rq_sectors(prq);
+                       blk_rq_sectors(prq));
                /* Argument of CMD18 or CMD25 */
-               packed_cmd_hdr[((i * 2)) + 1] =
+               packed_cmd_hdr[((i * 2)) + 1] = cpu_to_le32(
                        mmc_card_blockaddr(card) ?
-                       blk_rq_pos(prq) : blk_rq_pos(prq) << 9;
+                       blk_rq_pos(prq) : blk_rq_pos(prq) << 9);
                packed->blocks += blk_rq_sectors(prq);
                i++;
        }
index 86fac3e8683378acde59d1da87a0fad3878bf481..c763b404510f3a29d3864290297b22f596701cc3 100644 (file)
@@ -789,14 +789,16 @@ static int pxamci_probe(struct platform_device *pdev)
                gpio_direction_output(gpio_power,
                                      host->pdata->gpio_power_invert);
        }
-       if (gpio_is_valid(gpio_ro))
+       if (gpio_is_valid(gpio_ro)) {
                ret = mmc_gpio_request_ro(mmc, gpio_ro);
-       if (ret) {
-               dev_err(&pdev->dev, "Failed requesting gpio_ro %d\n", gpio_ro);
-               goto out;
-       } else {
-               mmc->caps2 |= host->pdata->gpio_card_ro_invert ?
-                       0 : MMC_CAP2_RO_ACTIVE_HIGH;
+               if (ret) {
+                       dev_err(&pdev->dev, "Failed requesting gpio_ro %d\n",
+                               gpio_ro);
+                       goto out;
+               } else {
+                       mmc->caps2 |= host->pdata->gpio_card_ro_invert ?
+                               0 : MMC_CAP2_RO_ACTIVE_HIGH;
+               }
        }
 
        if (gpio_is_valid(gpio_cd))
index 08e158895635cddda0bbea4e6973cfb8dd727e74..a136da8df6fe897d4f908d2723d4a95d8b152933 100644 (file)
@@ -1657,8 +1657,11 @@ static int omap_get_dt_info(struct device *dev, struct omap_nand_info *info)
 
        /* detect availability of ELM module. Won't be present pre-OMAP4 */
        info->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0);
-       if (!info->elm_of_node)
-               dev_dbg(dev, "ti,elm-id not in DT\n");
+       if (!info->elm_of_node) {
+               info->elm_of_node = of_parse_phandle(child, "elm_id", 0);
+               if (!info->elm_of_node)
+                       dev_dbg(dev, "ti,elm-id not in DT\n");
+       }
 
        /* select ecc-scheme for NAND */
        if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) {
index b9304a295f8648286c1d19e468c30b44084a4241..edc70ffad6607ac06d0a40b48316bef554c5f4c2 100644 (file)
@@ -101,11 +101,14 @@ enum ad_link_speed_type {
 #define MAC_ADDRESS_EQUAL(A, B)        \
        ether_addr_equal_64bits((const u8 *)A, (const u8 *)B)
 
-static struct mac_addr null_mac_addr = { { 0, 0, 0, 0, 0, 0 } };
+static const u8 null_mac_addr[ETH_ALEN + 2] __long_aligned = {
+       0, 0, 0, 0, 0, 0
+};
 static u16 ad_ticks_per_sec;
 static const int ad_delta_in_ticks = (AD_TIMER_INTERVAL * HZ) / 1000;
 
-static const u8 lacpdu_mcast_addr[ETH_ALEN] = MULTICAST_LACPDU_ADDR;
+static const u8 lacpdu_mcast_addr[ETH_ALEN + 2] __long_aligned =
+       MULTICAST_LACPDU_ADDR;
 
 /* ================= main 802.3ad protocol functions ================== */
 static int ad_lacpdu_send(struct port *port);
@@ -657,6 +660,20 @@ static void __set_agg_ports_ready(struct aggregator *aggregator, int val)
        }
 }
 
+static int __agg_active_ports(struct aggregator *agg)
+{
+       struct port *port;
+       int active = 0;
+
+       for (port = agg->lag_ports; port;
+            port = port->next_port_in_aggregator) {
+               if (port->is_enabled)
+                       active++;
+       }
+
+       return active;
+}
+
 /**
  * __get_agg_bandwidth - get the total bandwidth of an aggregator
  * @aggregator: the aggregator we're looking at
@@ -664,39 +681,40 @@ static void __set_agg_ports_ready(struct aggregator *aggregator, int val)
  */
 static u32 __get_agg_bandwidth(struct aggregator *aggregator)
 {
+       int nports = __agg_active_ports(aggregator);
        u32 bandwidth = 0;
 
-       if (aggregator->num_of_ports) {
+       if (nports) {
                switch (__get_link_speed(aggregator->lag_ports)) {
                case AD_LINK_SPEED_1MBPS:
-                       bandwidth = aggregator->num_of_ports;
+                       bandwidth = nports;
                        break;
                case AD_LINK_SPEED_10MBPS:
-                       bandwidth = aggregator->num_of_ports * 10;
+                       bandwidth = nports * 10;
                        break;
                case AD_LINK_SPEED_100MBPS:
-                       bandwidth = aggregator->num_of_ports * 100;
+                       bandwidth = nports * 100;
                        break;
                case AD_LINK_SPEED_1000MBPS:
-                       bandwidth = aggregator->num_of_ports * 1000;
+                       bandwidth = nports * 1000;
                        break;
                case AD_LINK_SPEED_2500MBPS:
-                       bandwidth = aggregator->num_of_ports * 2500;
+                       bandwidth = nports * 2500;
                        break;
                case AD_LINK_SPEED_10000MBPS:
-                       bandwidth = aggregator->num_of_ports * 10000;
+                       bandwidth = nports * 10000;
                        break;
                case AD_LINK_SPEED_20000MBPS:
-                       bandwidth = aggregator->num_of_ports * 20000;
+                       bandwidth = nports * 20000;
                        break;
                case AD_LINK_SPEED_40000MBPS:
-                       bandwidth = aggregator->num_of_ports * 40000;
+                       bandwidth = nports * 40000;
                        break;
                case AD_LINK_SPEED_56000MBPS:
-                       bandwidth = aggregator->num_of_ports * 56000;
+                       bandwidth = nports * 56000;
                        break;
                case AD_LINK_SPEED_100000MBPS:
-                       bandwidth = aggregator->num_of_ports * 100000;
+                       bandwidth = nports * 100000;
                        break;
                default:
                        bandwidth = 0; /* to silence the compiler */
@@ -1530,10 +1548,10 @@ static struct aggregator *ad_agg_selection_test(struct aggregator *best,
 
        switch (__get_agg_selection_mode(curr->lag_ports)) {
        case BOND_AD_COUNT:
-               if (curr->num_of_ports > best->num_of_ports)
+               if (__agg_active_ports(curr) > __agg_active_ports(best))
                        return curr;
 
-               if (curr->num_of_ports < best->num_of_ports)
+               if (__agg_active_ports(curr) < __agg_active_ports(best))
                        return best;
 
                /*FALLTHROUGH*/
@@ -1561,8 +1579,14 @@ static int agg_device_up(const struct aggregator *agg)
        if (!port)
                return 0;
 
-       return netif_running(port->slave->dev) &&
-              netif_carrier_ok(port->slave->dev);
+       for (port = agg->lag_ports; port;
+            port = port->next_port_in_aggregator) {
+               if (netif_running(port->slave->dev) &&
+                   netif_carrier_ok(port->slave->dev))
+                       return 1;
+       }
+
+       return 0;
 }
 
 /**
@@ -1610,7 +1634,7 @@ static void ad_agg_selection_logic(struct aggregator *agg,
 
                agg->is_active = 0;
 
-               if (agg->num_of_ports && agg_device_up(agg))
+               if (__agg_active_ports(agg) && agg_device_up(agg))
                        best = ad_agg_selection_test(best, agg);
        }
 
@@ -1622,7 +1646,7 @@ static void ad_agg_selection_logic(struct aggregator *agg,
                 * answering partner.
                 */
                if (active && active->lag_ports &&
-                   active->lag_ports->is_enabled &&
+                   __agg_active_ports(active) &&
                    (__agg_has_partner(active) ||
                     (!__agg_has_partner(active) &&
                     !__agg_has_partner(best)))) {
@@ -1718,7 +1742,7 @@ static void ad_clear_agg(struct aggregator *aggregator)
                aggregator->is_individual = false;
                aggregator->actor_admin_aggregator_key = 0;
                aggregator->actor_oper_aggregator_key = 0;
-               aggregator->partner_system = null_mac_addr;
+               eth_zero_addr(aggregator->partner_system.mac_addr_value);
                aggregator->partner_system_priority = 0;
                aggregator->partner_oper_aggregator_key = 0;
                aggregator->receive_state = 0;
@@ -1740,7 +1764,7 @@ static void ad_initialize_agg(struct aggregator *aggregator)
        if (aggregator) {
                ad_clear_agg(aggregator);
 
-               aggregator->aggregator_mac_address = null_mac_addr;
+               eth_zero_addr(aggregator->aggregator_mac_address.mac_addr_value);
                aggregator->aggregator_identifier = 0;
                aggregator->slave = NULL;
        }
@@ -2133,7 +2157,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
                                else
                                        temp_aggregator->lag_ports = temp_port->next_port_in_aggregator;
                                temp_aggregator->num_of_ports--;
-                               if (temp_aggregator->num_of_ports == 0) {
+                               if (__agg_active_ports(temp_aggregator) == 0) {
                                        select_new_active_agg = temp_aggregator->is_active;
                                        ad_clear_agg(temp_aggregator);
                                        if (select_new_active_agg) {
@@ -2432,7 +2456,9 @@ void bond_3ad_adapter_speed_duplex_changed(struct slave *slave)
  */
 void bond_3ad_handle_link_change(struct slave *slave, char link)
 {
+       struct aggregator *agg;
        struct port *port;
+       bool dummy;
 
        port = &(SLAVE_AD_INFO(slave)->port);
 
@@ -2459,6 +2485,9 @@ void bond_3ad_handle_link_change(struct slave *slave, char link)
                port->is_enabled = false;
                ad_update_actor_keys(port, true);
        }
+       agg = __get_first_agg(port);
+       ad_agg_selection_logic(agg, &dummy);
+
        netdev_dbg(slave->bond->dev, "Port %d changed link status to %s\n",
                   port->actor_port_number,
                   link == BOND_LINK_UP ? "UP" : "DOWN");
@@ -2499,7 +2528,7 @@ int bond_3ad_set_carrier(struct bonding *bond)
        active = __get_active_agg(&(SLAVE_AD_INFO(first_slave)->aggregator));
        if (active) {
                /* are enough slaves available to consider link up? */
-               if (active->num_of_ports < bond->params.min_links) {
+               if (__agg_active_ports(active) < bond->params.min_links) {
                        if (netif_carrier_ok(bond->dev)) {
                                netif_carrier_off(bond->dev);
                                goto out;
index c5ac160a8ae954d1104084c3d94795725cc261ae..551f0f8dead3945cf1516aeff01afe6c74117072 100644 (file)
 
 
 
-#ifndef __long_aligned
-#define __long_aligned __attribute__((aligned((sizeof(long)))))
-#endif
-static const u8 mac_bcast[ETH_ALEN] __long_aligned = {
+static const u8 mac_bcast[ETH_ALEN + 2] __long_aligned = {
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff
 };
-static const u8 mac_v6_allmcast[ETH_ALEN] __long_aligned = {
+static const u8 mac_v6_allmcast[ETH_ALEN + 2] __long_aligned = {
        0x33, 0x33, 0x00, 0x00, 0x00, 0x01
 };
 static const int alb_delta_in_ticks = HZ / ALB_TIMER_TICKS_PER_SEC;
index 941ec99cd3b69b6c9d3596e5b65b6aa89bc960c7..a2afa3be17a4bcc4e662a17e49f5c6bc31bce4d3 100644 (file)
@@ -1584,6 +1584,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
        }
 
        /* check for initial state */
+       new_slave->link = BOND_LINK_NOCHANGE;
        if (bond->params.miimon) {
                if (bond_check_dev_link(bond, slave_dev, 0) == BMSR_LSTATUS) {
                        if (bond->params.updelay) {
index db760e84119fcb970b7b34f7c4fac92b1acfed52..b8df0f5e8c25ae35b1ce368b04536c40761a1c96 100644 (file)
@@ -446,7 +446,11 @@ static int bond_newlink(struct net *src_net, struct net_device *bond_dev,
        if (err < 0)
                return err;
 
-       return register_netdevice(bond_dev);
+       err = register_netdevice(bond_dev);
+
+       netif_carrier_off(bond_dev);
+
+       return err;
 }
 
 static size_t bond_get_size(const struct net_device *bond_dev)
index 8b3275d7792acbab2d0ba9efe9fe3e2a6b283231..8f5e93cb79752703c141704011490535664b941e 100644 (file)
@@ -712,9 +712,10 @@ static int at91_poll_rx(struct net_device *dev, int quota)
 
        /* upper group completed, look again in lower */
        if (priv->rx_next > get_mb_rx_low_last(priv) &&
-           quota > 0 && mb > get_mb_rx_last(priv)) {
+           mb > get_mb_rx_last(priv)) {
                priv->rx_next = get_mb_rx_first(priv);
-               goto again;
+               if (quota > 0)
+                       goto again;
        }
 
        return received;
index f91b094288dad3d86064f24a33a97ad58756f3ca..e3dccd3200d5d834f13ad036c290c51e3091052e 100644 (file)
@@ -332,9 +332,23 @@ static void c_can_setup_tx_object(struct net_device *dev, int iface,
 
        priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), ctrl);
 
-       for (i = 0; i < frame->can_dlc; i += 2) {
-               priv->write_reg(priv, C_CAN_IFACE(DATA1_REG, iface) + i / 2,
-                               frame->data[i] | (frame->data[i + 1] << 8));
+       if (priv->type == BOSCH_D_CAN) {
+               u32 data = 0, dreg = C_CAN_IFACE(DATA1_REG, iface);
+
+               for (i = 0; i < frame->can_dlc; i += 4, dreg += 2) {
+                       data = (u32)frame->data[i];
+                       data |= (u32)frame->data[i + 1] << 8;
+                       data |= (u32)frame->data[i + 2] << 16;
+                       data |= (u32)frame->data[i + 3] << 24;
+                       priv->write_reg32(priv, dreg, data);
+               }
+       } else {
+               for (i = 0; i < frame->can_dlc; i += 2) {
+                       priv->write_reg(priv,
+                                       C_CAN_IFACE(DATA1_REG, iface) + i / 2,
+                                       frame->data[i] |
+                                       (frame->data[i + 1] << 8));
+               }
        }
 }
 
@@ -402,10 +416,20 @@ static int c_can_read_msg_object(struct net_device *dev, int iface, u32 ctrl)
        } else {
                int i, dreg = C_CAN_IFACE(DATA1_REG, iface);
 
-               for (i = 0; i < frame->can_dlc; i += 2, dreg ++) {
-                       data = priv->read_reg(priv, dreg);
-                       frame->data[i] = data;
-                       frame->data[i + 1] = data >> 8;
+               if (priv->type == BOSCH_D_CAN) {
+                       for (i = 0; i < frame->can_dlc; i += 4, dreg += 2) {
+                               data = priv->read_reg32(priv, dreg);
+                               frame->data[i] = data;
+                               frame->data[i + 1] = data >> 8;
+                               frame->data[i + 2] = data >> 16;
+                               frame->data[i + 3] = data >> 24;
+                       }
+               } else {
+                       for (i = 0; i < frame->can_dlc; i += 2, dreg++) {
+                               data = priv->read_reg(priv, dreg);
+                               frame->data[i] = data;
+                               frame->data[i + 1] = data >> 8;
+                       }
                }
        }
 
index 910c12e2638e3615084ca9f6a148030c8f2358e1..ad535a854e5cf28309a71547b49e411db85b385b 100644 (file)
@@ -798,6 +798,9 @@ static int can_validate(struct nlattr *tb[], struct nlattr *data[])
         * - control mode with CAN_CTRLMODE_FD set
         */
 
+       if (!data)
+               return 0;
+
        if (data[IFLA_CAN_CTRLMODE]) {
                struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]);
 
@@ -1008,6 +1011,11 @@ static int can_newlink(struct net *src_net, struct net_device *dev,
        return -EOPNOTSUPP;
 }
 
+static void can_dellink(struct net_device *dev, struct list_head *head)
+{
+       return;
+}
+
 static struct rtnl_link_ops can_link_ops __read_mostly = {
        .kind           = "can",
        .maxtype        = IFLA_CAN_MAX,
@@ -1016,6 +1024,7 @@ static struct rtnl_link_ops can_link_ops __read_mostly = {
        .validate       = can_validate,
        .newlink        = can_newlink,
        .changelink     = can_changelink,
+       .dellink        = can_dellink,
        .get_size       = can_get_size,
        .fill_info      = can_fill_info,
        .get_xstats_size = can_get_xstats_size,
index bcb272f6c68a9ac242964af7901bf6597a499306..8483a40e7e9ef52327e15e03fa4100c73f68a53f 100644 (file)
@@ -16,7 +16,8 @@ config CAN_ESD_USB2
 config CAN_GS_USB
        tristate "Geschwister Schneider UG interfaces"
        ---help---
-         This driver supports the Geschwister Schneider USB/CAN devices.
+         This driver supports the Geschwister Schneider and bytewerk.org
+         candleLight USB CAN interfaces USB/CAN devices
          If unsure choose N,
          choose Y for built in support,
          M to compile as module (module will be named: gs_usb).
@@ -46,6 +47,8 @@ config CAN_KVASER_USB
            - Kvaser USBcan R
            - Kvaser Leaf Light v2
            - Kvaser Mini PCI Express HS
+           - Kvaser Mini PCI Express 2xHS
+           - Kvaser USBcan Light 2xHS
            - Kvaser USBcan II HS/HS
            - Kvaser USBcan II HS/LS
            - Kvaser USBcan Rugged ("USBcan Rev B")
index 1556d428623531026ef58fe15260ff2e88a04372..acb0c8490673a081fca9738659235f0673e5167d 100644 (file)
@@ -1,7 +1,9 @@
-/* CAN driver for Geschwister Schneider USB/CAN devices.
+/* CAN driver for Geschwister Schneider USB/CAN devices
+ * and bytewerk.org candleLight USB CAN interfaces.
  *
- * Copyright (C) 2013 Geschwister Schneider Technologie-,
+ * Copyright (C) 2013-2016 Geschwister Schneider Technologie-,
  * Entwicklungs- und Vertriebs UG (Haftungsbeschränkt).
+ * Copyright (C) 2016 Hubert Denkmair
  *
  * Many thanks to all socketcan devs!
  *
@@ -29,6 +31,9 @@
 #define USB_GSUSB_1_VENDOR_ID      0x1d50
 #define USB_GSUSB_1_PRODUCT_ID     0x606f
 
+#define USB_CANDLELIGHT_VENDOR_ID  0x1209
+#define USB_CANDLELIGHT_PRODUCT_ID 0x2323
+
 #define GSUSB_ENDPOINT_IN          1
 #define GSUSB_ENDPOINT_OUT         2
 
@@ -952,6 +957,8 @@ static void gs_usb_disconnect(struct usb_interface *intf)
 static const struct usb_device_id gs_usb_table[] = {
        { USB_DEVICE_INTERFACE_NUMBER(USB_GSUSB_1_VENDOR_ID,
                                      USB_GSUSB_1_PRODUCT_ID, 0) },
+       { USB_DEVICE_INTERFACE_NUMBER(USB_CANDLELIGHT_VENDOR_ID,
+                                     USB_CANDLELIGHT_PRODUCT_ID, 0) },
        {} /* Terminating entry */
 };
 
@@ -969,5 +976,6 @@ module_usb_driver(gs_usb_driver);
 MODULE_AUTHOR("Maximilian Schneider <mws@schneidersoft.net>");
 MODULE_DESCRIPTION(
 "Socket CAN device driver for Geschwister Schneider Technologie-, "
-"Entwicklungs- und Vertriebs UG. USB2.0 to CAN interfaces.");
+"Entwicklungs- und Vertriebs UG. USB2.0 to CAN interfaces\n"
+"and bytewerk.org candleLight USB CAN interfaces.");
 MODULE_LICENSE("GPL v2");
index 022bfa13ebfa0c85491cfc2cb17a3437c01f0205..6f1f3b675ff553a96d4342b619e34a606fa9be29 100644 (file)
 #define USB_CAN_R_PRODUCT_ID           39
 #define USB_LEAF_LITE_V2_PRODUCT_ID    288
 #define USB_MINI_PCIE_HS_PRODUCT_ID    289
+#define USB_LEAF_LIGHT_HS_V2_OEM_PRODUCT_ID 290
+#define USB_USBCAN_LIGHT_2HS_PRODUCT_ID        291
+#define USB_MINI_PCIE_2HS_PRODUCT_ID   292
 
 static inline bool kvaser_is_leaf(const struct usb_device_id *id)
 {
        return id->idProduct >= USB_LEAF_DEVEL_PRODUCT_ID &&
-              id->idProduct <= USB_MINI_PCIE_HS_PRODUCT_ID;
+              id->idProduct <= USB_MINI_PCIE_2HS_PRODUCT_ID;
 }
 
 /* Kvaser USBCan-II devices */
@@ -537,6 +540,9 @@ static const struct usb_device_id kvaser_usb_table[] = {
                .driver_info = KVASER_HAS_TXRX_ERRORS },
        { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_V2_PRODUCT_ID) },
        { USB_DEVICE(KVASER_VENDOR_ID, USB_MINI_PCIE_HS_PRODUCT_ID) },
+       { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LIGHT_HS_V2_OEM_PRODUCT_ID) },
+       { USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN_LIGHT_2HS_PRODUCT_ID) },
+       { USB_DEVICE(KVASER_VENDOR_ID, USB_MINI_PCIE_2HS_PRODUCT_ID) },
 
        /* USBCANII family IDs */
        { USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN2_PRODUCT_ID),
index 30defe6c81f22d6463c4df091a421014392f6ff2..821d86c38ab214a9c450f3079398b1d3a4af2e24 100644 (file)
@@ -3851,7 +3851,7 @@ static void et131x_tx_timeout(struct net_device *netdev)
        unsigned long flags;
 
        /* If the device is closed, ignore the timeout */
-       if (~(adapter->flags & FMP_ADAPTER_INTERRUPT_IN_USE))
+       if (!(adapter->flags & FMP_ADAPTER_INTERRUPT_IN_USE))
                return;
 
        /* Any nonrecoverable hardware error?
index e0fb0f1122db42553c0ed4a907d89a1e03e1f209..20760e10211a48ba15daa72a5c4322c90ceb0157 100644 (file)
@@ -509,8 +509,8 @@ static int au1000_mii_probe(struct net_device *dev)
         * on the current MAC's MII bus
         */
        for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++)
-               if (mdiobus_get_phy(aup->mii_bus, aup->phy_addr)) {
-                       phydev = mdiobus_get_phy(aup->mii_bus, aup->phy_addr);
+               if (mdiobus_get_phy(aup->mii_bus, phy_addr)) {
+                       phydev = mdiobus_get_phy(aup->mii_bus, phy_addr);
                        if (!aup->phy_search_highest_addr)
                                /* break out with first one found */
                                break;
index d02c4240b7df1fab72f1ff102d43361957d74078..8fc93c5f6abca337e01f6939678cadd24f36b0f1 100644 (file)
@@ -96,10 +96,6 @@ struct alx_priv {
        unsigned int rx_ringsz;
        unsigned int rxbuf_size;
 
-       struct page  *rx_page;
-       unsigned int rx_page_offset;
-       unsigned int rx_frag_size;
-
        struct napi_struct napi;
        struct alx_tx_queue txq;
        struct alx_rx_queue rxq;
index c98acdc0d14f5304918d922372b635bf38c6f666..e708e360a9e3919df7b4e7b6e8500e5c27d24f55 100644 (file)
@@ -70,35 +70,6 @@ static void alx_free_txbuf(struct alx_priv *alx, int entry)
        }
 }
 
-static struct sk_buff *alx_alloc_skb(struct alx_priv *alx, gfp_t gfp)
-{
-       struct sk_buff *skb;
-       struct page *page;
-
-       if (alx->rx_frag_size > PAGE_SIZE)
-               return __netdev_alloc_skb(alx->dev, alx->rxbuf_size, gfp);
-
-       page = alx->rx_page;
-       if (!page) {
-               alx->rx_page = page = alloc_page(gfp);
-               if (unlikely(!page))
-                       return NULL;
-               alx->rx_page_offset = 0;
-       }
-
-       skb = build_skb(page_address(page) + alx->rx_page_offset,
-                       alx->rx_frag_size);
-       if (likely(skb)) {
-               alx->rx_page_offset += alx->rx_frag_size;
-               if (alx->rx_page_offset >= PAGE_SIZE)
-                       alx->rx_page = NULL;
-               else
-                       get_page(page);
-       }
-       return skb;
-}
-
-
 static int alx_refill_rx_ring(struct alx_priv *alx, gfp_t gfp)
 {
        struct alx_rx_queue *rxq = &alx->rxq;
@@ -115,9 +86,22 @@ static int alx_refill_rx_ring(struct alx_priv *alx, gfp_t gfp)
        while (!cur_buf->skb && next != rxq->read_idx) {
                struct alx_rfd *rfd = &rxq->rfd[cur];
 
-               skb = alx_alloc_skb(alx, gfp);
+               /*
+                * When DMA RX address is set to something like
+                * 0x....fc0, it will be very likely to cause DMA
+                * RFD overflow issue.
+                *
+                * To work around it, we apply rx skb with 64 bytes
+                * longer space, and offset the address whenever
+                * 0x....fc0 is detected.
+                */
+               skb = __netdev_alloc_skb(alx->dev, alx->rxbuf_size + 64, gfp);
                if (!skb)
                        break;
+
+               if (((unsigned long)skb->data & 0xfff) == 0xfc0)
+                       skb_reserve(skb, 64);
+
                dma = dma_map_single(&alx->hw.pdev->dev,
                                     skb->data, alx->rxbuf_size,
                                     DMA_FROM_DEVICE);
@@ -153,7 +137,6 @@ static int alx_refill_rx_ring(struct alx_priv *alx, gfp_t gfp)
                alx_write_mem16(&alx->hw, ALX_RFD_PIDX, cur);
        }
 
-
        return count;
 }
 
@@ -622,11 +605,6 @@ static void alx_free_rings(struct alx_priv *alx)
        kfree(alx->txq.bufs);
        kfree(alx->rxq.bufs);
 
-       if (alx->rx_page) {
-               put_page(alx->rx_page);
-               alx->rx_page = NULL;
-       }
-
        dma_free_coherent(&alx->hw.pdev->dev,
                          alx->descmem.size,
                          alx->descmem.virt,
@@ -681,7 +659,6 @@ static int alx_request_irq(struct alx_priv *alx)
                                  alx->dev->name, alx);
                if (!err)
                        goto out;
-
                /* fall back to legacy interrupt */
                pci_disable_msi(alx->hw.pdev);
        }
@@ -725,7 +702,6 @@ static int alx_init_sw(struct alx_priv *alx)
        struct pci_dev *pdev = alx->hw.pdev;
        struct alx_hw *hw = &alx->hw;
        int err;
-       unsigned int head_size;
 
        err = alx_identify_hw(alx);
        if (err) {
@@ -741,12 +717,7 @@ static int alx_init_sw(struct alx_priv *alx)
 
        hw->smb_timer = 400;
        hw->mtu = alx->dev->mtu;
-
        alx->rxbuf_size = ALX_MAX_FRAME_LEN(hw->mtu);
-       head_size = SKB_DATA_ALIGN(alx->rxbuf_size + NET_SKB_PAD) +
-                   SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
-       alx->rx_frag_size = roundup_pow_of_two(head_size);
-
        alx->tx_ringsz = 256;
        alx->rx_ringsz = 512;
        hw->imt = 200;
@@ -848,7 +819,6 @@ static int alx_change_mtu(struct net_device *netdev, int mtu)
 {
        struct alx_priv *alx = netdev_priv(netdev);
        int max_frame = ALX_MAX_FRAME_LEN(mtu);
-       unsigned int head_size;
 
        if ((max_frame < ALX_MIN_FRAME_SIZE) ||
            (max_frame > ALX_MAX_FRAME_SIZE))
@@ -860,9 +830,6 @@ static int alx_change_mtu(struct net_device *netdev, int mtu)
        netdev->mtu = mtu;
        alx->hw.mtu = mtu;
        alx->rxbuf_size = max(max_frame, ALX_DEF_RXBUF_SIZE);
-       head_size = SKB_DATA_ALIGN(alx->rxbuf_size + NET_SKB_PAD) +
-                   SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
-       alx->rx_frag_size = roundup_pow_of_two(head_size);
        netdev_update_features(netdev);
        if (netif_running(netdev))
                alx_reinit(alx);
index 08a23e6b60e947894783f2115c2fe16abece84bc..1a3555d03a96a823e4936c06a2e9ce64d27ae851 100644 (file)
@@ -259,6 +259,7 @@ static void nb8800_receive(struct net_device *dev, unsigned int i,
                if (err) {
                        netdev_err(dev, "rx buffer allocation failed\n");
                        dev->stats.rx_dropped++;
+                       dev_kfree_skb(skb);
                        return;
                }
 
index 543bf38105c9240d9ae374708377755c5e4db9a6..bfa26a2590c979b41bc6c46aee275d730e1ce89d 100644 (file)
@@ -392,7 +392,7 @@ static void bcm_sysport_get_stats(struct net_device *dev,
                else
                        p = (char *)priv;
                p += s->stat_offset;
-               data[i] = *(u32 *)p;
+               data[i] = *(unsigned long *)p;
        }
 }
 
index ee5f431ab32af4f2b7c175f836ac10f697016e1e..25bbae5928d43a4d911d0d5852612d8524437cdb 100644 (file)
@@ -231,7 +231,7 @@ static netdev_tx_t bgmac_dma_tx_add(struct bgmac *bgmac,
        dma_unmap_single(dma_dev, slot->dma_addr, skb_headlen(skb),
                         DMA_TO_DEVICE);
 
-       while (i > 0) {
+       while (i-- > 0) {
                int index = (ring->end + i) % BGMAC_TX_RING_SLOTS;
                struct bgmac_slot_info *slot = &ring->slots[index];
                u32 ctl1 = le32_to_cpu(ring->cpu_base[index].ctl1);
@@ -267,15 +267,16 @@ static void bgmac_dma_tx_free(struct bgmac *bgmac, struct bgmac_dma_ring *ring)
        while (ring->start != ring->end) {
                int slot_idx = ring->start % BGMAC_TX_RING_SLOTS;
                struct bgmac_slot_info *slot = &ring->slots[slot_idx];
-               u32 ctl1;
+               u32 ctl0, ctl1;
                int len;
 
                if (slot_idx == empty_slot)
                        break;
 
+               ctl0 = le32_to_cpu(ring->cpu_base[slot_idx].ctl0);
                ctl1 = le32_to_cpu(ring->cpu_base[slot_idx].ctl1);
                len = ctl1 & BGMAC_DESC_CTL1_LEN;
-               if (ctl1 & BGMAC_DESC_CTL0_SOF)
+               if (ctl0 & BGMAC_DESC_CTL0_SOF)
                        /* Unmap no longer used buffer */
                        dma_unmap_single(dma_dev, slot->dma_addr, len,
                                         DMA_TO_DEVICE);
@@ -1312,7 +1313,8 @@ static int bgmac_open(struct net_device *net_dev)
 
        phy_start(bgmac->phy_dev);
 
-       netif_carrier_on(net_dev);
+       netif_start_queue(net_dev);
+
        return 0;
 }
 
index a38cb047b54083897fa6e8df5c098c7e1b98d7ac..1b0ae4a72e9ecd81a1d404c5e6edadb3f00ce8cc 100644 (file)
@@ -1591,7 +1591,7 @@ static int bnxt_get_module_eeprom(struct net_device *dev,
 {
        struct bnxt *bp = netdev_priv(dev);
        u16  start = eeprom->offset, length = eeprom->len;
-       int rc;
+       int rc = 0;
 
        memset(data, 0, eeprom->len);
 
index 8de79ae63231b0ad21e47edb9a150e026adf6bd3..0e7e7da8d201c0b3af122c542ef42467c03cb94c 100644 (file)
@@ -2821,7 +2821,7 @@ static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev)
                if (!g) {
                        netif_info(lio, tx_err, lio->netdev,
                                   "Transmit scatter gather: glist null!\n");
-                       goto lio_xmit_failed;
+                       goto lio_xmit_dma_failed;
                }
 
                cmdsetup.s.gather = 1;
@@ -2892,7 +2892,7 @@ static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev)
        else
                status = octnet_send_nic_data_pkt(oct, &ndata, xmit_more);
        if (status == IQ_SEND_FAILED)
-               goto lio_xmit_failed;
+               goto lio_xmit_dma_failed;
 
        netif_info(lio, tx_queued, lio->netdev, "Transmit queued successfully\n");
 
@@ -2906,12 +2906,13 @@ static int liquidio_xmit(struct sk_buff *skb, struct net_device *netdev)
 
        return NETDEV_TX_OK;
 
+lio_xmit_dma_failed:
+       dma_unmap_single(&oct->pci_dev->dev, ndata.cmd.dptr,
+                        ndata.datasize, DMA_TO_DEVICE);
 lio_xmit_failed:
        stats->tx_dropped++;
        netif_info(lio, tx_err, lio->netdev, "IQ%d Transmit dropped:%llu\n",
                   iq_no, stats->tx_dropped);
-       dma_unmap_single(&oct->pci_dev->dev, ndata.cmd.dptr,
-                        ndata.datasize, DMA_TO_DEVICE);
        recv_buffer_free(skb);
        return NETDEV_TX_OK;
 }
index 95f17f8cadacc08637c485e555242e78aafb48c5..16ed20357c5c30d2cbb786cb414a6d4544fecf83 100644 (file)
@@ -499,6 +499,7 @@ static void nic_tx_channel_cfg(struct nicpf *nic, u8 vnic,
        u32 rr_quantum;
        u8 sq_idx = sq->sq_num;
        u8 pqs_vnic;
+       int svf;
 
        if (sq->sqs_mode)
                pqs_vnic = nic->pqs_vf[vnic];
@@ -511,10 +512,19 @@ static void nic_tx_channel_cfg(struct nicpf *nic, u8 vnic,
        /* 24 bytes for FCS, IPG and preamble */
        rr_quantum = ((NIC_HW_MAX_FRS + 24) / 4);
 
-       tl4 = (lmac * NIC_TL4_PER_LMAC) + (bgx * NIC_TL4_PER_BGX);
+       if (!sq->sqs_mode) {
+               tl4 = (lmac * NIC_TL4_PER_LMAC) + (bgx * NIC_TL4_PER_BGX);
+       } else {
+               for (svf = 0; svf < MAX_SQS_PER_VF; svf++) {
+                       if (nic->vf_sqs[pqs_vnic][svf] == vnic)
+                               break;
+               }
+               tl4 = (MAX_LMAC_PER_BGX * NIC_TL4_PER_LMAC);
+               tl4 += (lmac * NIC_TL4_PER_LMAC * MAX_SQS_PER_VF);
+               tl4 += (svf * NIC_TL4_PER_LMAC);
+               tl4 += (bgx * NIC_TL4_PER_BGX);
+       }
        tl4 += sq_idx;
-       if (sq->sqs_mode)
-               tl4 += vnic * 8;
 
        tl3 = tl4 / (NIC_MAX_TL4 / NIC_MAX_TL3);
        nic_reg_write(nic, NIC_PF_QSET_0_127_SQ_0_7_CFG2 |
index 3ed21988626b5ffaceb4e72e0276c63cee1c4347..63a39ac97d53beb65e562bba0a9fb1b86f7baac7 100644 (file)
@@ -551,7 +551,9 @@ static int bgx_xaui_check_link(struct lmac *lmac)
        }
 
        /* Clear rcvflt bit (latching high) and read it back */
-       bgx_reg_modify(bgx, lmacid, BGX_SPUX_STATUS2, SPU_STATUS2_RCVFLT);
+       if (bgx_reg_read(bgx, lmacid, BGX_SPUX_STATUS2) & SPU_STATUS2_RCVFLT)
+               bgx_reg_modify(bgx, lmacid,
+                              BGX_SPUX_STATUS2, SPU_STATUS2_RCVFLT);
        if (bgx_reg_read(bgx, lmacid, BGX_SPUX_STATUS2) & SPU_STATUS2_RCVFLT) {
                dev_err(&bgx->pdev->dev, "Receive fault, retry training\n");
                if (bgx->use_training) {
@@ -570,13 +572,6 @@ static int bgx_xaui_check_link(struct lmac *lmac)
                return -1;
        }
 
-       /* Wait for MAC RX to be ready */
-       if (bgx_poll_reg(bgx, lmacid, BGX_SMUX_RX_CTL,
-                        SMU_RX_CTL_STATUS, true)) {
-               dev_err(&bgx->pdev->dev, "SMU RX link not okay\n");
-               return -1;
-       }
-
        /* Wait for BGX RX to be idle */
        if (bgx_poll_reg(bgx, lmacid, BGX_SMUX_CTL, SMU_CTL_RX_IDLE, false)) {
                dev_err(&bgx->pdev->dev, "SMU RX not idle\n");
@@ -589,29 +584,30 @@ static int bgx_xaui_check_link(struct lmac *lmac)
                return -1;
        }
 
-       if (bgx_reg_read(bgx, lmacid, BGX_SPUX_STATUS2) & SPU_STATUS2_RCVFLT) {
-               dev_err(&bgx->pdev->dev, "Receive fault\n");
-               return -1;
-       }
-
-       /* Receive link is latching low. Force it high and verify it */
-       bgx_reg_modify(bgx, lmacid, BGX_SPUX_STATUS1, SPU_STATUS1_RCV_LNK);
-       if (bgx_poll_reg(bgx, lmacid, BGX_SPUX_STATUS1,
-                        SPU_STATUS1_RCV_LNK, false)) {
-               dev_err(&bgx->pdev->dev, "SPU receive link down\n");
-               return -1;
-       }
-
+       /* Clear receive packet disable */
        cfg = bgx_reg_read(bgx, lmacid, BGX_SPUX_MISC_CONTROL);
        cfg &= ~SPU_MISC_CTL_RX_DIS;
        bgx_reg_write(bgx, lmacid, BGX_SPUX_MISC_CONTROL, cfg);
-       return 0;
+
+       /* Check for MAC RX faults */
+       cfg = bgx_reg_read(bgx, lmacid, BGX_SMUX_RX_CTL);
+       /* 0 - Link is okay, 1 - Local fault, 2 - Remote fault */
+       cfg &= SMU_RX_CTL_STATUS;
+       if (!cfg)
+               return 0;
+
+       /* Rx local/remote fault seen.
+        * Do lmac reinit to see if condition recovers
+        */
+       bgx_lmac_xaui_init(bgx, lmacid, bgx->lmac_type);
+
+       return -1;
 }
 
 static void bgx_poll_for_link(struct work_struct *work)
 {
        struct lmac *lmac;
-       u64 link;
+       u64 spu_link, smu_link;
 
        lmac = container_of(work, struct lmac, dwork.work);
 
@@ -621,8 +617,11 @@ static void bgx_poll_for_link(struct work_struct *work)
        bgx_poll_reg(lmac->bgx, lmac->lmacid, BGX_SPUX_STATUS1,
                     SPU_STATUS1_RCV_LNK, false);
 
-       link = bgx_reg_read(lmac->bgx, lmac->lmacid, BGX_SPUX_STATUS1);
-       if (link & SPU_STATUS1_RCV_LNK) {
+       spu_link = bgx_reg_read(lmac->bgx, lmac->lmacid, BGX_SPUX_STATUS1);
+       smu_link = bgx_reg_read(lmac->bgx, lmac->lmacid, BGX_SMUX_RX_CTL);
+
+       if ((spu_link & SPU_STATUS1_RCV_LNK) &&
+           !(smu_link & SMU_RX_CTL_STATUS)) {
                lmac->link_up = 1;
                if (lmac->bgx->lmac_type == BGX_MODE_XLAUI)
                        lmac->last_speed = 40000;
@@ -636,9 +635,15 @@ static void bgx_poll_for_link(struct work_struct *work)
        }
 
        if (lmac->last_link != lmac->link_up) {
+               if (lmac->link_up) {
+                       if (bgx_xaui_check_link(lmac)) {
+                               /* Errors, clear link_up state */
+                               lmac->link_up = 0;
+                               lmac->last_speed = SPEED_UNKNOWN;
+                               lmac->last_duplex = DUPLEX_UNKNOWN;
+                       }
+               }
                lmac->last_link = lmac->link_up;
-               if (lmac->link_up)
-                       bgx_xaui_check_link(lmac);
        }
 
        queue_delayed_work(lmac->check_link, &lmac->dwork, HZ * 2);
@@ -710,7 +715,7 @@ static int bgx_lmac_enable(struct bgx *bgx, u8 lmacid)
 static void bgx_lmac_disable(struct bgx *bgx, u8 lmacid)
 {
        struct lmac *lmac;
-       u64 cmrx_cfg;
+       u64 cfg;
 
        lmac = &bgx->lmac[lmacid];
        if (lmac->check_link) {
@@ -719,9 +724,33 @@ static void bgx_lmac_disable(struct bgx *bgx, u8 lmacid)
                destroy_workqueue(lmac->check_link);
        }
 
-       cmrx_cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG);
-       cmrx_cfg &= ~(1 << 15);
-       bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cmrx_cfg);
+       /* Disable packet reception */
+       cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG);
+       cfg &= ~CMR_PKT_RX_EN;
+       bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg);
+
+       /* Give chance for Rx/Tx FIFO to get drained */
+       bgx_poll_reg(bgx, lmacid, BGX_CMRX_RX_FIFO_LEN, (u64)0x1FFF, true);
+       bgx_poll_reg(bgx, lmacid, BGX_CMRX_TX_FIFO_LEN, (u64)0x3FFF, true);
+
+       /* Disable packet transmission */
+       cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG);
+       cfg &= ~CMR_PKT_TX_EN;
+       bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg);
+
+       /* Disable serdes lanes */
+        if (!lmac->is_sgmii)
+                bgx_reg_modify(bgx, lmacid,
+                               BGX_SPUX_CONTROL1, SPU_CTL_LOW_POWER);
+        else
+                bgx_reg_modify(bgx, lmacid,
+                               BGX_GMP_PCS_MRX_CTL, PCS_MRX_CTL_PWR_DN);
+
+       /* Disable LMAC */
+       cfg = bgx_reg_read(bgx, lmacid, BGX_CMRX_CFG);
+       cfg &= ~CMR_EN;
+       bgx_reg_write(bgx, lmacid, BGX_CMRX_CFG, cfg);
+
        bgx_flush_dmac_addrs(bgx, lmacid);
 
        if ((bgx->lmac_type != BGX_MODE_XFI) &&
index 149e179363a1e6dd71e0b7cbd00f26a5e5ef0116..42010d2e5ddf4e3d46eab4b2c9cee6caac5e252a 100644 (file)
@@ -41,6 +41,7 @@
 #define BGX_CMRX_RX_STAT10             0xC0
 #define BGX_CMRX_RX_BP_DROP            0xC8
 #define BGX_CMRX_RX_DMAC_CTL           0x0E8
+#define BGX_CMRX_RX_FIFO_LEN           0x108
 #define BGX_CMR_RX_DMACX_CAM           0x200
 #define  RX_DMACX_CAM_EN                       BIT_ULL(48)
 #define  RX_DMACX_CAM_LMACID(x)                        (x << 49)
@@ -50,6 +51,7 @@
 #define BGX_CMR_CHAN_MSK_AND           0x450
 #define BGX_CMR_BIST_STATUS            0x460
 #define BGX_CMR_RX_LMACS               0x468
+#define BGX_CMRX_TX_FIFO_LEN           0x518
 #define BGX_CMRX_TX_STAT0              0x600
 #define BGX_CMRX_TX_STAT1              0x608
 #define BGX_CMRX_TX_STAT2              0x610
index c4b262ca7d43623fba1cef20dfc6c03ebeadfd7f..2accab38632327ae007baf1ef293dca1ef7da7d9 100644 (file)
@@ -36,8 +36,8 @@
 #define __T4FW_VERSION_H__
 
 #define T4FW_VERSION_MAJOR 0x01
-#define T4FW_VERSION_MINOR 0x0E
-#define T4FW_VERSION_MICRO 0x04
+#define T4FW_VERSION_MINOR 0x0F
+#define T4FW_VERSION_MICRO 0x25
 #define T4FW_VERSION_BUILD 0x00
 
 #define T4FW_MIN_VERSION_MAJOR 0x01
@@ -45,8 +45,8 @@
 #define T4FW_MIN_VERSION_MICRO 0x00
 
 #define T5FW_VERSION_MAJOR 0x01
-#define T5FW_VERSION_MINOR 0x0E
-#define T5FW_VERSION_MICRO 0x04
+#define T5FW_VERSION_MINOR 0x0F
+#define T5FW_VERSION_MICRO 0x25
 #define T5FW_VERSION_BUILD 0x00
 
 #define T5FW_MIN_VERSION_MAJOR 0x00
@@ -54,8 +54,8 @@
 #define T5FW_MIN_VERSION_MICRO 0x00
 
 #define T6FW_VERSION_MAJOR 0x01
-#define T6FW_VERSION_MINOR 0x0E
-#define T6FW_VERSION_MICRO 0x04
+#define T6FW_VERSION_MINOR 0x0F
+#define T6FW_VERSION_MICRO 0x25
 #define T6FW_VERSION_BUILD 0x00
 
 #define T6FW_MIN_VERSION_MAJOR 0x00
index 4edb98c3c6c70a9c620caa7928e8e5d555db1242..4466a11871109b41861cd1be9ff3dc70d207512f 100644 (file)
@@ -860,6 +860,11 @@ static netdev_tx_t ethoc_start_xmit(struct sk_buff *skb, struct net_device *dev)
        unsigned int entry;
        void *dest;
 
+       if (skb_put_padto(skb, ETHOC_ZLEN)) {
+               dev->stats.tx_errors++;
+               goto out_no_free;
+       }
+
        if (unlikely(skb->len > ETHOC_BUFSIZ)) {
                dev->stats.tx_errors++;
                goto out;
@@ -894,6 +899,7 @@ static netdev_tx_t ethoc_start_xmit(struct sk_buff *skb, struct net_device *dev)
        skb_tx_timestamp(skb);
 out:
        dev_kfree_skb(skb);
+out_no_free:
        return NETDEV_TX_OK;
 }
 
@@ -1086,7 +1092,7 @@ static int ethoc_probe(struct platform_device *pdev)
        if (!priv->iobase) {
                dev_err(&pdev->dev, "cannot remap I/O memory space\n");
                ret = -ENXIO;
-               goto error;
+               goto free;
        }
 
        if (netdev->mem_end) {
@@ -1095,7 +1101,7 @@ static int ethoc_probe(struct platform_device *pdev)
                if (!priv->membase) {
                        dev_err(&pdev->dev, "cannot remap memory space\n");
                        ret = -ENXIO;
-                       goto error;
+                       goto free;
                }
        } else {
                /* Allocate buffer memory */
@@ -1106,7 +1112,7 @@ static int ethoc_probe(struct platform_device *pdev)
                        dev_err(&pdev->dev, "cannot allocate %dB buffer\n",
                                buffer_size);
                        ret = -ENOMEM;
-                       goto error;
+                       goto free;
                }
                netdev->mem_end = netdev->mem_start + buffer_size;
                priv->dma_alloc = buffer_size;
@@ -1120,7 +1126,7 @@ static int ethoc_probe(struct platform_device *pdev)
                128, (netdev->mem_end - netdev->mem_start + 1) / ETHOC_BUFSIZ);
        if (num_bd < 4) {
                ret = -ENODEV;
-               goto error;
+               goto free;
        }
        priv->num_bd = num_bd;
        /* num_tx must be a power of two */
@@ -1133,7 +1139,7 @@ static int ethoc_probe(struct platform_device *pdev)
        priv->vma = devm_kzalloc(&pdev->dev, num_bd*sizeof(void *), GFP_KERNEL);
        if (!priv->vma) {
                ret = -ENOMEM;
-               goto error;
+               goto free;
        }
 
        /* Allow the platform setup code to pass in a MAC address. */
index 06f031715b578c897863c906b07176589b862f07..9b7a3f5a2818f1fdc7dfe183991a2a7d3ab03523 100644 (file)
@@ -285,6 +285,7 @@ static void nps_enet_hw_reset(struct net_device *ndev)
        ge_rst_value |= NPS_ENET_ENABLE << RST_GMAC_0_SHIFT;
        nps_enet_reg_set(priv, NPS_ENET_REG_GE_RST, ge_rst_value);
        usleep_range(10, 20);
+       ge_rst_value = 0;
        nps_enet_reg_set(priv, NPS_ENET_REG_GE_RST, ge_rst_value);
 
        /* Tx fifo reset sequence */
index 864cb21351a410bf1cd7059cf9952a6010784860..88f3c85fb04ada575e367042d79f4b8188e12245 100644 (file)
@@ -75,6 +75,7 @@
 #include <linux/uaccess.h>
 #include <asm/firmware.h>
 #include <linux/seq_file.h>
+#include <linux/workqueue.h>
 
 #include "ibmvnic.h"
 
@@ -89,6 +90,7 @@ MODULE_VERSION(IBMVNIC_DRIVER_VERSION);
 static int ibmvnic_version = IBMVNIC_INITIAL_VERSION;
 static int ibmvnic_remove(struct vio_dev *);
 static void release_sub_crqs(struct ibmvnic_adapter *);
+static void release_sub_crqs_no_irqs(struct ibmvnic_adapter *);
 static int ibmvnic_reset_crq(struct ibmvnic_adapter *);
 static int ibmvnic_send_crq_init(struct ibmvnic_adapter *);
 static int ibmvnic_reenable_crq_queue(struct ibmvnic_adapter *);
@@ -469,7 +471,8 @@ static int ibmvnic_open(struct net_device *netdev)
        crq.logical_link_state.link_state = IBMVNIC_LOGICAL_LNK_UP;
        ibmvnic_send_crq(adapter, &crq);
 
-       netif_start_queue(netdev);
+       netif_tx_start_all_queues(netdev);
+
        return 0;
 
 bounce_map_failed:
@@ -519,7 +522,7 @@ static int ibmvnic_close(struct net_device *netdev)
        for (i = 0; i < adapter->req_rx_queues; i++)
                napi_disable(&adapter->napi[i]);
 
-       netif_stop_queue(netdev);
+       netif_tx_stop_all_queues(netdev);
 
        if (adapter->bounce_buffer) {
                if (!dma_mapping_error(dev, adapter->bounce_buffer_dma)) {
@@ -1212,12 +1215,6 @@ static struct ibmvnic_sub_crq_queue *init_sub_crq_queue(struct ibmvnic_adapter
                goto reg_failed;
        }
 
-       scrq->irq = irq_create_mapping(NULL, scrq->hw_irq);
-       if (scrq->irq == NO_IRQ) {
-               dev_err(dev, "Error mapping irq\n");
-               goto map_irq_failed;
-       }
-
        scrq->adapter = adapter;
        scrq->size = 4 * PAGE_SIZE / sizeof(*scrq->msgs);
        scrq->cur = 0;
@@ -1230,12 +1227,6 @@ static struct ibmvnic_sub_crq_queue *init_sub_crq_queue(struct ibmvnic_adapter
 
        return scrq;
 
-map_irq_failed:
-       do {
-               rc = plpar_hcall_norets(H_FREE_SUB_CRQ,
-                                       adapter->vdev->unit_address,
-                                       scrq->crq_num);
-       } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
 reg_failed:
        dma_unmap_single(dev, scrq->msg_token, 4 * PAGE_SIZE,
                         DMA_BIDIRECTIONAL);
@@ -1256,6 +1247,7 @@ static void release_sub_crqs(struct ibmvnic_adapter *adapter)
                        if (adapter->tx_scrq[i]) {
                                free_irq(adapter->tx_scrq[i]->irq,
                                         adapter->tx_scrq[i]);
+                               irq_dispose_mapping(adapter->tx_scrq[i]->irq);
                                release_sub_crq_queue(adapter,
                                                      adapter->tx_scrq[i]);
                        }
@@ -1267,6 +1259,7 @@ static void release_sub_crqs(struct ibmvnic_adapter *adapter)
                        if (adapter->rx_scrq[i]) {
                                free_irq(adapter->rx_scrq[i]->irq,
                                         adapter->rx_scrq[i]);
+                               irq_dispose_mapping(adapter->rx_scrq[i]->irq);
                                release_sub_crq_queue(adapter,
                                                      adapter->rx_scrq[i]);
                        }
@@ -1276,6 +1269,29 @@ static void release_sub_crqs(struct ibmvnic_adapter *adapter)
        adapter->requested_caps = 0;
 }
 
+static void release_sub_crqs_no_irqs(struct ibmvnic_adapter *adapter)
+{
+       int i;
+
+       if (adapter->tx_scrq) {
+               for (i = 0; i < adapter->req_tx_queues; i++)
+                       if (adapter->tx_scrq[i])
+                               release_sub_crq_queue(adapter,
+                                                     adapter->tx_scrq[i]);
+               adapter->tx_scrq = NULL;
+       }
+
+       if (adapter->rx_scrq) {
+               for (i = 0; i < adapter->req_rx_queues; i++)
+                       if (adapter->rx_scrq[i])
+                               release_sub_crq_queue(adapter,
+                                                     adapter->rx_scrq[i]);
+               adapter->rx_scrq = NULL;
+       }
+
+       adapter->requested_caps = 0;
+}
+
 static int disable_scrq_irq(struct ibmvnic_adapter *adapter,
                            struct ibmvnic_sub_crq_queue *scrq)
 {
@@ -1395,6 +1411,66 @@ static irqreturn_t ibmvnic_interrupt_rx(int irq, void *instance)
        return IRQ_HANDLED;
 }
 
+static int init_sub_crq_irqs(struct ibmvnic_adapter *adapter)
+{
+       struct device *dev = &adapter->vdev->dev;
+       struct ibmvnic_sub_crq_queue *scrq;
+       int i = 0, j = 0;
+       int rc = 0;
+
+       for (i = 0; i < adapter->req_tx_queues; i++) {
+               scrq = adapter->tx_scrq[i];
+               scrq->irq = irq_create_mapping(NULL, scrq->hw_irq);
+
+               if (scrq->irq == NO_IRQ) {
+                       rc = -EINVAL;
+                       dev_err(dev, "Error mapping irq\n");
+                       goto req_tx_irq_failed;
+               }
+
+               rc = request_irq(scrq->irq, ibmvnic_interrupt_tx,
+                                0, "ibmvnic_tx", scrq);
+
+               if (rc) {
+                       dev_err(dev, "Couldn't register tx irq 0x%x. rc=%d\n",
+                               scrq->irq, rc);
+                       irq_dispose_mapping(scrq->irq);
+                       goto req_rx_irq_failed;
+               }
+       }
+
+       for (i = 0; i < adapter->req_rx_queues; i++) {
+               scrq = adapter->rx_scrq[i];
+               scrq->irq = irq_create_mapping(NULL, scrq->hw_irq);
+               if (scrq->irq == NO_IRQ) {
+                       rc = -EINVAL;
+                       dev_err(dev, "Error mapping irq\n");
+                       goto req_rx_irq_failed;
+               }
+               rc = request_irq(scrq->irq, ibmvnic_interrupt_rx,
+                                0, "ibmvnic_rx", scrq);
+               if (rc) {
+                       dev_err(dev, "Couldn't register rx irq 0x%x. rc=%d\n",
+                               scrq->irq, rc);
+                       irq_dispose_mapping(scrq->irq);
+                       goto req_rx_irq_failed;
+               }
+       }
+       return rc;
+
+req_rx_irq_failed:
+       for (j = 0; j < i; j++)
+               free_irq(adapter->rx_scrq[j]->irq, adapter->rx_scrq[j]);
+               irq_dispose_mapping(adapter->rx_scrq[j]->irq);
+       i = adapter->req_tx_queues;
+req_tx_irq_failed:
+       for (j = 0; j < i; j++)
+               free_irq(adapter->tx_scrq[j]->irq, adapter->tx_scrq[j]);
+               irq_dispose_mapping(adapter->rx_scrq[j]->irq);
+       release_sub_crqs_no_irqs(adapter);
+       return rc;
+}
+
 static void init_sub_crqs(struct ibmvnic_adapter *adapter, int retry)
 {
        struct device *dev = &adapter->vdev->dev;
@@ -1403,8 +1479,7 @@ static void init_sub_crqs(struct ibmvnic_adapter *adapter, int retry)
        union ibmvnic_crq crq;
        int total_queues;
        int more = 0;
-       int i, j;
-       int rc;
+       int i;
 
        if (!retry) {
                /* Sub-CRQ entries are 32 byte long */
@@ -1483,13 +1558,6 @@ static void init_sub_crqs(struct ibmvnic_adapter *adapter, int retry)
        for (i = 0; i < adapter->req_tx_queues; i++) {
                adapter->tx_scrq[i] = allqueues[i];
                adapter->tx_scrq[i]->pool_index = i;
-               rc = request_irq(adapter->tx_scrq[i]->irq, ibmvnic_interrupt_tx,
-                                0, "ibmvnic_tx", adapter->tx_scrq[i]);
-               if (rc) {
-                       dev_err(dev, "Couldn't register tx irq 0x%x. rc=%d\n",
-                               adapter->tx_scrq[i]->irq, rc);
-                       goto req_tx_irq_failed;
-               }
        }
 
        adapter->rx_scrq = kcalloc(adapter->req_rx_queues,
@@ -1500,13 +1568,6 @@ static void init_sub_crqs(struct ibmvnic_adapter *adapter, int retry)
        for (i = 0; i < adapter->req_rx_queues; i++) {
                adapter->rx_scrq[i] = allqueues[i + adapter->req_tx_queues];
                adapter->rx_scrq[i]->scrq_num = i;
-               rc = request_irq(adapter->rx_scrq[i]->irq, ibmvnic_interrupt_rx,
-                                0, "ibmvnic_rx", adapter->rx_scrq[i]);
-               if (rc) {
-                       dev_err(dev, "Couldn't register rx irq 0x%x. rc=%d\n",
-                               adapter->rx_scrq[i]->irq, rc);
-                       goto req_rx_irq_failed;
-               }
        }
 
        memset(&crq, 0, sizeof(crq));
@@ -1559,15 +1620,6 @@ static void init_sub_crqs(struct ibmvnic_adapter *adapter, int retry)
 
        return;
 
-req_rx_irq_failed:
-       for (j = 0; j < i; j++)
-               free_irq(adapter->rx_scrq[j]->irq, adapter->rx_scrq[j]);
-       i = adapter->req_tx_queues;
-req_tx_irq_failed:
-       for (j = 0; j < i; j++)
-               free_irq(adapter->tx_scrq[j]->irq, adapter->tx_scrq[j]);
-       kfree(adapter->rx_scrq);
-       adapter->rx_scrq = NULL;
 rx_failed:
        kfree(adapter->tx_scrq);
        adapter->tx_scrq = NULL;
@@ -2121,7 +2173,7 @@ static void handle_error_info_rsp(union ibmvnic_crq *crq,
                                  struct ibmvnic_adapter *adapter)
 {
        struct device *dev = &adapter->vdev->dev;
-       struct ibmvnic_error_buff *error_buff;
+       struct ibmvnic_error_buff *error_buff, *tmp;
        unsigned long flags;
        bool found = false;
        int i;
@@ -2133,7 +2185,7 @@ static void handle_error_info_rsp(union ibmvnic_crq *crq,
        }
 
        spin_lock_irqsave(&adapter->error_list_lock, flags);
-       list_for_each_entry(error_buff, &adapter->errors, list)
+       list_for_each_entry_safe(error_buff, tmp, &adapter->errors, list)
                if (error_buff->error_id == crq->request_error_rsp.error_id) {
                        found = true;
                        list_del(&error_buff->list);
@@ -2348,9 +2400,9 @@ static void handle_request_cap_rsp(union ibmvnic_crq *crq,
                         *req_value,
                         (long int)be32_to_cpu(crq->request_capability_rsp.
                                               number), name);
-               release_sub_crqs(adapter);
+               release_sub_crqs_no_irqs(adapter);
                *req_value = be32_to_cpu(crq->request_capability_rsp.number);
-               complete(&adapter->init_done);
+               init_sub_crqs(adapter, 1);
                return;
        default:
                dev_err(dev, "Error %d in request cap rsp\n",
@@ -2659,7 +2711,7 @@ static void handle_query_cap_rsp(union ibmvnic_crq *crq,
 
 out:
        if (atomic_read(&adapter->running_cap_queries) == 0)
-               complete(&adapter->init_done);
+               init_sub_crqs(adapter, 0);
                /* We're done querying the capabilities, initialize sub-crqs */
 }
 
@@ -3141,14 +3193,14 @@ static void handle_request_ras_comp_num_rsp(union ibmvnic_crq *crq,
 
 static void ibmvnic_free_inflight(struct ibmvnic_adapter *adapter)
 {
-       struct ibmvnic_inflight_cmd *inflight_cmd;
+       struct ibmvnic_inflight_cmd *inflight_cmd, *tmp1;
        struct device *dev = &adapter->vdev->dev;
-       struct ibmvnic_error_buff *error_buff;
+       struct ibmvnic_error_buff *error_buff, *tmp2;
        unsigned long flags;
        unsigned long flags2;
 
        spin_lock_irqsave(&adapter->inflight_lock, flags);
-       list_for_each_entry(inflight_cmd, &adapter->inflight, list) {
+       list_for_each_entry_safe(inflight_cmd, tmp1, &adapter->inflight, list) {
                switch (inflight_cmd->crq.generic.cmd) {
                case LOGIN:
                        dma_unmap_single(dev, adapter->login_buf_token,
@@ -3165,8 +3217,8 @@ static void ibmvnic_free_inflight(struct ibmvnic_adapter *adapter)
                        break;
                case REQUEST_ERROR_INFO:
                        spin_lock_irqsave(&adapter->error_list_lock, flags2);
-                       list_for_each_entry(error_buff, &adapter->errors,
-                                           list) {
+                       list_for_each_entry_safe(error_buff, tmp2,
+                                                &adapter->errors, list) {
                                dma_unmap_single(dev, error_buff->dma,
                                                 error_buff->len,
                                                 DMA_FROM_DEVICE);
@@ -3202,8 +3254,8 @@ static void ibmvnic_handle_crq(union ibmvnic_crq *crq,
                        dev_info(dev, "Partner initialized\n");
                        /* Send back a response */
                        rc = ibmvnic_send_crq_init_complete(adapter);
-                       if (rc == 0)
-                               send_version_xchg(adapter);
+                       if (!rc)
+                               schedule_work(&adapter->vnic_crq_init);
                        else
                                dev_err(dev, "Can't send initrsp rc=%ld\n", rc);
                        break;
@@ -3555,8 +3607,63 @@ static const struct file_operations ibmvnic_dump_ops = {
        .release        = single_release,
 };
 
+static void handle_crq_init_rsp(struct work_struct *work)
+{
+       struct ibmvnic_adapter *adapter = container_of(work,
+                                                      struct ibmvnic_adapter,
+                                                      vnic_crq_init);
+       struct device *dev = &adapter->vdev->dev;
+       struct net_device *netdev = adapter->netdev;
+       unsigned long timeout = msecs_to_jiffies(30000);
+       int rc;
+
+       send_version_xchg(adapter);
+       reinit_completion(&adapter->init_done);
+       if (!wait_for_completion_timeout(&adapter->init_done, timeout)) {
+               dev_err(dev, "Passive init timeout\n");
+               goto task_failed;
+       }
+
+       do {
+               if (adapter->renegotiate) {
+                       adapter->renegotiate = false;
+                       release_sub_crqs_no_irqs(adapter);
+                       send_cap_queries(adapter);
+
+                       reinit_completion(&adapter->init_done);
+                       if (!wait_for_completion_timeout(&adapter->init_done,
+                                                        timeout)) {
+                               dev_err(dev, "Passive init timeout\n");
+                               goto task_failed;
+                       }
+               }
+       } while (adapter->renegotiate);
+       rc = init_sub_crq_irqs(adapter);
+
+       if (rc)
+               goto task_failed;
+
+       netdev->real_num_tx_queues = adapter->req_tx_queues;
+
+       rc = register_netdev(netdev);
+       if (rc) {
+               dev_err(dev,
+                       "failed to register netdev rc=%d\n", rc);
+               goto register_failed;
+       }
+       dev_info(dev, "ibmvnic registered\n");
+
+       return;
+
+register_failed:
+       release_sub_crqs(adapter);
+task_failed:
+       dev_err(dev, "Passive initialization was not successful\n");
+}
+
 static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
 {
+       unsigned long timeout = msecs_to_jiffies(30000);
        struct ibmvnic_adapter *adapter;
        struct net_device *netdev;
        unsigned char *mac_addr_p;
@@ -3593,6 +3700,8 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
        netdev->ethtool_ops = &ibmvnic_ethtool_ops;
        SET_NETDEV_DEV(netdev, &dev->dev);
 
+       INIT_WORK(&adapter->vnic_crq_init, handle_crq_init_rsp);
+
        spin_lock_init(&adapter->stats_lock);
 
        rc = ibmvnic_init_crq_queue(adapter);
@@ -3635,30 +3744,26 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
        ibmvnic_send_crq_init(adapter);
 
        init_completion(&adapter->init_done);
-       wait_for_completion(&adapter->init_done);
+       if (!wait_for_completion_timeout(&adapter->init_done, timeout))
+               return 0;
 
        do {
-               adapter->renegotiate = false;
-
-               init_sub_crqs(adapter, 0);
-               reinit_completion(&adapter->init_done);
-               wait_for_completion(&adapter->init_done);
-
                if (adapter->renegotiate) {
-                       release_sub_crqs(adapter);
+                       adapter->renegotiate = false;
+                       release_sub_crqs_no_irqs(adapter);
                        send_cap_queries(adapter);
 
                        reinit_completion(&adapter->init_done);
-                       wait_for_completion(&adapter->init_done);
+                       if (!wait_for_completion_timeout(&adapter->init_done,
+                                                        timeout))
+                               return 0;
                }
        } while (adapter->renegotiate);
 
-       /* if init_sub_crqs is partially successful, retry */
-       while (!adapter->tx_scrq || !adapter->rx_scrq) {
-               init_sub_crqs(adapter, 1);
-
-               reinit_completion(&adapter->init_done);
-               wait_for_completion(&adapter->init_done);
+       rc = init_sub_crq_irqs(adapter);
+       if (rc) {
+               dev_err(&dev->dev, "failed to initialize sub crq irqs\n");
+               goto free_debugfs;
        }
 
        netdev->real_num_tx_queues = adapter->req_tx_queues;
@@ -3666,12 +3771,14 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id)
        rc = register_netdev(netdev);
        if (rc) {
                dev_err(&dev->dev, "failed to register netdev rc=%d\n", rc);
-               goto free_debugfs;
+               goto free_sub_crqs;
        }
        dev_info(&dev->dev, "ibmvnic registered\n");
 
        return 0;
 
+free_sub_crqs:
+       release_sub_crqs(adapter);
 free_debugfs:
        if (adapter->debugfs_dir && !IS_ERR(adapter->debugfs_dir))
                debugfs_remove_recursive(adapter->debugfs_dir);
index 0b66a506a4e44e4d36aae861c8ba6289318cd8e1..e82898fd518ef890afbbf79c307833b8127e782c 100644 (file)
@@ -1045,4 +1045,6 @@ struct ibmvnic_adapter {
        u64 opt_rxba_entries_per_subcrq;
        __be64 tx_rx_desc_req;
        u8 map_id;
+
+       struct work_struct vnic_crq_init;
 };
index 75e60897b7e748bca73317ae0e6d9247e6a9bbf9..2b2e2f8c636994219e997bdb53ff8d3379275bb9 100644 (file)
@@ -2789,7 +2789,7 @@ static void e1000e_vlan_filter_enable(struct e1000_adapter *adapter)
 }
 
 /**
- * e1000e_vlan_strip_enable - helper to disable HW VLAN stripping
+ * e1000e_vlan_strip_disable - helper to disable HW VLAN stripping
  * @adapter: board private structure to initialize
  **/
 static void e1000e_vlan_strip_disable(struct e1000_adapter *adapter)
@@ -6915,6 +6915,14 @@ static netdev_features_t e1000_fix_features(struct net_device *netdev,
        if ((hw->mac.type >= e1000_pch2lan) && (netdev->mtu > ETH_DATA_LEN))
                features &= ~NETIF_F_RXFCS;
 
+       /* Since there is no support for separate Rx/Tx vlan accel
+        * enable/disable make sure Tx flag is always in same state as Rx.
+        */
+       if (features & NETIF_F_HW_VLAN_CTAG_RX)
+               features |= NETIF_F_HW_VLAN_CTAG_TX;
+       else
+               features &= ~NETIF_F_HW_VLAN_CTAG_TX;
+
        return features;
 }
 
index 5ea22008d721d0a0ad769f32b7ee74ef8b19249d..501f15d9f4d6eef599733f12e7052fedc1563186 100644 (file)
@@ -1344,6 +1344,13 @@ struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi,
        if (!vsi || !macaddr)
                return NULL;
 
+       /* Do not allow broadcast filter to be added since broadcast filter
+        * is added as part of add VSI for any newly created VSI except
+        * FDIR VSI
+        */
+       if (is_broadcast_ether_addr(macaddr))
+               return NULL;
+
        f = i40e_find_filter(vsi, macaddr, vlan, is_vf, is_netdev);
        if (!f) {
                f = kzalloc(sizeof(*f), GFP_ATOMIC);
@@ -2151,18 +2158,6 @@ int i40e_sync_vsi_filters(struct i40e_vsi *vsi)
                                         aq_ret, pf->hw.aq.asq_last_status);
                        }
                }
-               aq_ret = i40e_aq_set_vsi_broadcast(&vsi->back->hw,
-                                                  vsi->seid,
-                                                  cur_promisc, NULL);
-               if (aq_ret) {
-                       retval = i40e_aq_rc_to_posix(aq_ret,
-                                                    pf->hw.aq.asq_last_status);
-                       dev_info(&pf->pdev->dev,
-                                "set brdcast promisc failed, err %s, aq_err %s\n",
-                                i40e_stat_str(&pf->hw, aq_ret),
-                                i40e_aq_str(&pf->hw,
-                                            pf->hw.aq.asq_last_status));
-               }
        }
 out:
        /* if something went wrong then set the changed flag so we try again */
@@ -7726,10 +7721,11 @@ static int i40e_init_msix(struct i40e_pf *pf)
  * i40e_vsi_alloc_q_vector - Allocate memory for a single interrupt vector
  * @vsi: the VSI being configured
  * @v_idx: index of the vector in the vsi struct
+ * @cpu: cpu to be used on affinity_mask
  *
  * We allocate one q_vector.  If allocation fails we return -ENOMEM.
  **/
-static int i40e_vsi_alloc_q_vector(struct i40e_vsi *vsi, int v_idx)
+static int i40e_vsi_alloc_q_vector(struct i40e_vsi *vsi, int v_idx, int cpu)
 {
        struct i40e_q_vector *q_vector;
 
@@ -7740,7 +7736,8 @@ static int i40e_vsi_alloc_q_vector(struct i40e_vsi *vsi, int v_idx)
 
        q_vector->vsi = vsi;
        q_vector->v_idx = v_idx;
-       cpumask_set_cpu(v_idx, &q_vector->affinity_mask);
+       cpumask_set_cpu(cpu, &q_vector->affinity_mask);
+
        if (vsi->netdev)
                netif_napi_add(vsi->netdev, &q_vector->napi,
                               i40e_napi_poll, NAPI_POLL_WEIGHT);
@@ -7764,8 +7761,7 @@ static int i40e_vsi_alloc_q_vector(struct i40e_vsi *vsi, int v_idx)
 static int i40e_vsi_alloc_q_vectors(struct i40e_vsi *vsi)
 {
        struct i40e_pf *pf = vsi->back;
-       int v_idx, num_q_vectors;
-       int err;
+       int err, v_idx, num_q_vectors, current_cpu;
 
        /* if not MSIX, give the one vector only to the LAN VSI */
        if (pf->flags & I40E_FLAG_MSIX_ENABLED)
@@ -7775,10 +7771,15 @@ static int i40e_vsi_alloc_q_vectors(struct i40e_vsi *vsi)
        else
                return -EINVAL;
 
+       current_cpu = cpumask_first(cpu_online_mask);
+
        for (v_idx = 0; v_idx < num_q_vectors; v_idx++) {
-               err = i40e_vsi_alloc_q_vector(vsi, v_idx);
+               err = i40e_vsi_alloc_q_vector(vsi, v_idx, current_cpu);
                if (err)
                        goto err_out;
+               current_cpu = cpumask_next(current_cpu, cpu_online_mask);
+               if (unlikely(current_cpu >= nr_cpu_ids))
+                       current_cpu = cpumask_first(cpu_online_mask);
        }
 
        return 0;
@@ -9224,6 +9225,7 @@ int i40e_is_vsi_uplink_mode_veb(struct i40e_vsi *vsi)
 static int i40e_add_vsi(struct i40e_vsi *vsi)
 {
        int ret = -ENODEV;
+       i40e_status aq_ret = 0;
        u8 laa_macaddr[ETH_ALEN];
        bool found_laa_mac_filter = false;
        struct i40e_pf *pf = vsi->back;
@@ -9413,6 +9415,18 @@ static int i40e_add_vsi(struct i40e_vsi *vsi)
                vsi->seid = ctxt.seid;
                vsi->id = ctxt.vsi_number;
        }
+       /* Except FDIR VSI, for all othet VSI set the broadcast filter */
+       if (vsi->type != I40E_VSI_FDIR) {
+               aq_ret = i40e_aq_set_vsi_broadcast(hw, vsi->seid, true, NULL);
+               if (aq_ret) {
+                       ret = i40e_aq_rc_to_posix(aq_ret,
+                                                 hw->aq.asq_last_status);
+                       dev_info(&pf->pdev->dev,
+                                "set brdcast promisc failed, err %s, aq_err %s\n",
+                                i40e_stat_str(hw, aq_ret),
+                                i40e_aq_str(hw, hw->aq.asq_last_status));
+               }
+       }
 
        spin_lock_bh(&vsi->mac_filter_list_lock);
        /* If macvlan filters already exist, force them to get loaded */
index 55f151fca1dcb785089511105920755fef9397b1..a8868e1bf832557ffa7c68d2840a0b098fdf874d 100644 (file)
@@ -1280,8 +1280,8 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
                                    union i40e_rx_desc *rx_desc)
 {
        struct i40e_rx_ptype_decoded decoded;
-       bool ipv4, ipv6, tunnel = false;
        u32 rx_error, rx_status;
+       bool ipv4, ipv6;
        u8 ptype;
        u64 qword;
 
@@ -1336,19 +1336,23 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
        if (rx_error & BIT(I40E_RX_DESC_ERROR_PPRS_SHIFT))
                return;
 
-       /* The hardware supported by this driver does not validate outer
-        * checksums for tunneled VXLAN or GENEVE frames.  I don't agree
-        * with it but the specification states that you "MAY validate", it
-        * doesn't make it a hard requirement so if we have validated the
-        * inner checksum report CHECKSUM_UNNECESSARY.
+       /* If there is an outer header present that might contain a checksum
+        * we need to bump the checksum level by 1 to reflect the fact that
+        * we are indicating we validated the inner checksum.
         */
-       if (decoded.inner_prot & (I40E_RX_PTYPE_INNER_PROT_TCP |
-                                 I40E_RX_PTYPE_INNER_PROT_UDP |
-                                 I40E_RX_PTYPE_INNER_PROT_SCTP))
-               tunnel = true;
-
-       skb->ip_summed = CHECKSUM_UNNECESSARY;
-       skb->csum_level = tunnel ? 1 : 0;
+       if (decoded.tunnel_type >= I40E_RX_PTYPE_TUNNEL_IP_GRENAT)
+               skb->csum_level = 1;
+
+       /* Only report checksum unnecessary for TCP, UDP, or SCTP */
+       switch (decoded.inner_prot) {
+       case I40E_RX_PTYPE_INNER_PROT_TCP:
+       case I40E_RX_PTYPE_INNER_PROT_UDP:
+       case I40E_RX_PTYPE_INNER_PROT_SCTP:
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+               /* fall though */
+       default:
+               break;
+       }
 
        return;
 
index be99189da925fc5862e9c318489694341a3a68f9..79d99cd91b24317d0cc970a6bdedbb24abeac356 100644 (file)
@@ -752,8 +752,8 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
                                    union i40e_rx_desc *rx_desc)
 {
        struct i40e_rx_ptype_decoded decoded;
-       bool ipv4, ipv6, tunnel = false;
        u32 rx_error, rx_status;
+       bool ipv4, ipv6;
        u8 ptype;
        u64 qword;
 
@@ -808,19 +808,23 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi,
        if (rx_error & BIT(I40E_RX_DESC_ERROR_PPRS_SHIFT))
                return;
 
-       /* The hardware supported by this driver does not validate outer
-        * checksums for tunneled VXLAN or GENEVE frames.  I don't agree
-        * with it but the specification states that you "MAY validate", it
-        * doesn't make it a hard requirement so if we have validated the
-        * inner checksum report CHECKSUM_UNNECESSARY.
+       /* If there is an outer header present that might contain a checksum
+        * we need to bump the checksum level by 1 to reflect the fact that
+        * we are indicating we validated the inner checksum.
         */
-       if (decoded.inner_prot & (I40E_RX_PTYPE_INNER_PROT_TCP |
-                                 I40E_RX_PTYPE_INNER_PROT_UDP |
-                                 I40E_RX_PTYPE_INNER_PROT_SCTP))
-               tunnel = true;
-
-       skb->ip_summed = CHECKSUM_UNNECESSARY;
-       skb->csum_level = tunnel ? 1 : 0;
+       if (decoded.tunnel_type >= I40E_RX_PTYPE_TUNNEL_IP_GRENAT)
+               skb->csum_level = 1;
+
+       /* Only report checksum unnecessary for TCP, UDP, or SCTP */
+       switch (decoded.inner_prot) {
+       case I40E_RX_PTYPE_INNER_PROT_TCP:
+       case I40E_RX_PTYPE_INNER_PROT_UDP:
+       case I40E_RX_PTYPE_INNER_PROT_SCTP:
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+               /* fall though */
+       default:
+               break;
+       }
 
        return;
 
index 088c47cf27d97d0f5a8a40992c95ec7c7a761947..8bebd862a54ccd7f4f3defe4fd1a1035188294d6 100644 (file)
@@ -2887,7 +2887,7 @@ int ixgbe_poll(struct napi_struct *napi, int budget)
        if (!test_bit(__IXGBE_DOWN, &adapter->state))
                ixgbe_irq_enable_queues(adapter, BIT_ULL(q_vector->v_idx));
 
-       return 0;
+       return min(work_done, budget - 1);
 }
 
 /**
index 61a80da8b6f0dec4323f094a245fc3fcbb6bcab0..2819abc454c7e71c024ab280ec3a5a1780a07a4b 100644 (file)
@@ -85,7 +85,7 @@ static s32 ixgbevf_poll_for_ack(struct ixgbe_hw *hw)
 static s32 ixgbevf_read_posted_mbx(struct ixgbe_hw *hw, u32 *msg, u16 size)
 {
        struct ixgbe_mbx_info *mbx = &hw->mbx;
-       s32 ret_val = -IXGBE_ERR_MBX;
+       s32 ret_val = IXGBE_ERR_MBX;
 
        if (!mbx->ops.read)
                goto out;
@@ -111,7 +111,7 @@ static s32 ixgbevf_read_posted_mbx(struct ixgbe_hw *hw, u32 *msg, u16 size)
 static s32 ixgbevf_write_posted_mbx(struct ixgbe_hw *hw, u32 *msg, u16 size)
 {
        struct ixgbe_mbx_info *mbx = &hw->mbx;
-       s32 ret_val = -IXGBE_ERR_MBX;
+       s32 ret_val = IXGBE_ERR_MBX;
 
        /* exit if either we can't write or there isn't a defined timeout */
        if (!mbx->ops.write || !mbx->timeout)
index a6d26d351dfc47c777b39c04a44c2f17bca0feab..f92018b13d2869e57ec307029072e3b85ac5041a 100644 (file)
 /* Various constants */
 
 /* Coalescing */
-#define MVNETA_TXDONE_COAL_PKTS                1
+#define MVNETA_TXDONE_COAL_PKTS                0       /* interrupt per packet */
 #define MVNETA_RX_COAL_PKTS            32
 #define MVNETA_RX_COAL_USEC            100
 
@@ -3458,6 +3458,8 @@ static int mvneta_open(struct net_device *dev)
        return 0;
 
 err_free_irq:
+       unregister_cpu_notifier(&pp->cpu_notifier);
+       on_each_cpu(mvneta_percpu_disable, pp, true);
        free_percpu_irq(pp->dev->irq, pp->ports);
 err_cleanup_txqs:
        mvneta_cleanup_txqs(pp);
index 4763252bbf8557ac98c73876e685d4b7413e3f43..d1cdc2d7615158f612885e100a0cc274c22a3fe9 100644 (file)
@@ -481,20 +481,23 @@ static inline void mtk_rx_get_desc(struct mtk_rx_dma *rxd,
 /* the qdma core needs scratch memory to be setup */
 static int mtk_init_fq_dma(struct mtk_eth *eth)
 {
-       dma_addr_t phy_ring_head, phy_ring_tail;
+       dma_addr_t phy_ring_tail;
        int cnt = MTK_DMA_SIZE;
        dma_addr_t dma_addr;
        int i;
 
        eth->scratch_ring = dma_alloc_coherent(eth->dev,
                                               cnt * sizeof(struct mtk_tx_dma),
-                                              &phy_ring_head,
+                                              &eth->phy_scratch_ring,
                                               GFP_ATOMIC | __GFP_ZERO);
        if (unlikely(!eth->scratch_ring))
                return -ENOMEM;
 
        eth->scratch_head = kcalloc(cnt, MTK_QDMA_PAGE_SIZE,
                                    GFP_KERNEL);
+       if (unlikely(!eth->scratch_head))
+               return -ENOMEM;
+
        dma_addr = dma_map_single(eth->dev,
                                  eth->scratch_head, cnt * MTK_QDMA_PAGE_SIZE,
                                  DMA_FROM_DEVICE);
@@ -502,19 +505,19 @@ static int mtk_init_fq_dma(struct mtk_eth *eth)
                return -ENOMEM;
 
        memset(eth->scratch_ring, 0x0, sizeof(struct mtk_tx_dma) * cnt);
-       phy_ring_tail = phy_ring_head +
+       phy_ring_tail = eth->phy_scratch_ring +
                        (sizeof(struct mtk_tx_dma) * (cnt - 1));
 
        for (i = 0; i < cnt; i++) {
                eth->scratch_ring[i].txd1 =
                                        (dma_addr + (i * MTK_QDMA_PAGE_SIZE));
                if (i < cnt - 1)
-                       eth->scratch_ring[i].txd2 = (phy_ring_head +
+                       eth->scratch_ring[i].txd2 = (eth->phy_scratch_ring +
                                ((i + 1) * sizeof(struct mtk_tx_dma)));
                eth->scratch_ring[i].txd3 = TX_DMA_SDL(MTK_QDMA_PAGE_SIZE);
        }
 
-       mtk_w32(eth, phy_ring_head, MTK_QDMA_FQ_HEAD);
+       mtk_w32(eth, eth->phy_scratch_ring, MTK_QDMA_FQ_HEAD);
        mtk_w32(eth, phy_ring_tail, MTK_QDMA_FQ_TAIL);
        mtk_w32(eth, (cnt << 16) | cnt, MTK_QDMA_FQ_CNT);
        mtk_w32(eth, MTK_QDMA_PAGE_SIZE << 16, MTK_QDMA_FQ_BLEN);
@@ -671,7 +674,7 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev,
 
 err_dma:
        do {
-               tx_buf = mtk_desc_to_tx_buf(ring, txd);
+               tx_buf = mtk_desc_to_tx_buf(ring, itxd);
 
                /* unmap dma */
                mtk_tx_unmap(&dev->dev, tx_buf);
@@ -701,6 +704,20 @@ static inline int mtk_cal_txd_req(struct sk_buff *skb)
        return nfrags;
 }
 
+static int mtk_queue_stopped(struct mtk_eth *eth)
+{
+       int i;
+
+       for (i = 0; i < MTK_MAC_COUNT; i++) {
+               if (!eth->netdev[i])
+                       continue;
+               if (netif_queue_stopped(eth->netdev[i]))
+                       return 1;
+       }
+
+       return 0;
+}
+
 static void mtk_wake_queue(struct mtk_eth *eth)
 {
        int i;
@@ -766,12 +783,9 @@ static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
        if (mtk_tx_map(skb, dev, tx_num, ring, gso) < 0)
                goto drop;
 
-       if (unlikely(atomic_read(&ring->free_count) <= ring->thresh)) {
+       if (unlikely(atomic_read(&ring->free_count) <= ring->thresh))
                mtk_stop_queue(eth);
-               if (unlikely(atomic_read(&ring->free_count) >
-                            ring->thresh))
-                       mtk_wake_queue(eth);
-       }
+
        spin_unlock_irqrestore(&eth->page_lock, flags);
 
        return NETDEV_TX_OK;
@@ -826,6 +840,7 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
                                          DMA_FROM_DEVICE);
                if (unlikely(dma_mapping_error(&netdev->dev, dma_addr))) {
                        skb_free_frag(new_data);
+                       netdev->stats.rx_dropped++;
                        goto release_desc;
                }
 
@@ -833,6 +848,7 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
                skb = build_skb(data, ring->frag_size);
                if (unlikely(!skb)) {
                        put_page(virt_to_head_page(new_data));
+                       netdev->stats.rx_dropped++;
                        goto release_desc;
                }
                skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
@@ -921,7 +937,6 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
                }
                mtk_tx_unmap(eth->dev, tx_buf);
 
-               ring->last_free->txd2 = next_cpu;
                ring->last_free = desc;
                atomic_inc(&ring->free_count);
 
@@ -946,7 +961,8 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
        if (!total)
                return 0;
 
-       if (atomic_read(&ring->free_count) > ring->thresh)
+       if (mtk_queue_stopped(eth) &&
+           (atomic_read(&ring->free_count) > ring->thresh))
                mtk_wake_queue(eth);
 
        return total;
@@ -1027,9 +1043,8 @@ static int mtk_tx_alloc(struct mtk_eth *eth)
 
        atomic_set(&ring->free_count, MTK_DMA_SIZE - 2);
        ring->next_free = &ring->dma[0];
-       ring->last_free = &ring->dma[MTK_DMA_SIZE - 2];
-       ring->thresh = max((unsigned long)MTK_DMA_SIZE >> 2,
-                             MAX_SKB_FRAGS);
+       ring->last_free = &ring->dma[MTK_DMA_SIZE - 1];
+       ring->thresh = MAX_SKB_FRAGS;
 
        /* make sure that all changes to the dma ring are flushed before we
         * continue
@@ -1207,6 +1222,14 @@ static void mtk_dma_free(struct mtk_eth *eth)
        for (i = 0; i < MTK_MAC_COUNT; i++)
                if (eth->netdev[i])
                        netdev_reset_queue(eth->netdev[i]);
+       if (eth->scratch_ring) {
+               dma_free_coherent(eth->dev,
+                                 MTK_DMA_SIZE * sizeof(struct mtk_tx_dma),
+                                 eth->scratch_ring,
+                                 eth->phy_scratch_ring);
+               eth->scratch_ring = NULL;
+               eth->phy_scratch_ring = 0;
+       }
        mtk_tx_clean(eth);
        mtk_rx_clean(eth);
        kfree(eth->scratch_head);
@@ -1269,7 +1292,7 @@ static int mtk_start_dma(struct mtk_eth *eth)
        mtk_w32(eth,
                MTK_TX_WB_DDONE | MTK_RX_DMA_EN | MTK_TX_DMA_EN |
                MTK_RX_2B_OFFSET | MTK_DMA_SIZE_16DWORDS |
-               MTK_RX_BT_32DWORDS,
+               MTK_RX_BT_32DWORDS | MTK_NDP_CO_PRO,
                MTK_QDMA_GLO_CFG);
 
        return 0;
@@ -1383,7 +1406,7 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
 
        /* disable delay and normal interrupt */
        mtk_w32(eth, 0, MTK_QDMA_DELAY_INT);
-       mtk_irq_disable(eth, MTK_TX_DONE_INT | MTK_RX_DONE_INT);
+       mtk_irq_disable(eth, ~0);
        mtk_w32(eth, RST_GL_PSE, MTK_RST_GL);
        mtk_w32(eth, 0, MTK_RST_GL);
 
@@ -1697,7 +1720,7 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
        mac->hw_stats->reg_offset = id * MTK_STAT_OFFSET;
 
        SET_NETDEV_DEV(eth->netdev[id], eth->dev);
-       eth->netdev[id]->watchdog_timeo = HZ;
+       eth->netdev[id]->watchdog_timeo = 5 * HZ;
        eth->netdev[id]->netdev_ops = &mtk_netdev_ops;
        eth->netdev[id]->base_addr = (unsigned long)eth->base;
        eth->netdev[id]->vlan_features = MTK_HW_FEATURES &
index eed626d56ea4f1d3df5f2ed3aacbd8bded28cead..a5eb7c62306b66116df10a75df115fb4810534d9 100644 (file)
@@ -91,6 +91,7 @@
 #define MTK_QDMA_GLO_CFG       0x1A04
 #define MTK_RX_2B_OFFSET       BIT(31)
 #define MTK_RX_BT_32DWORDS     (3 << 11)
+#define MTK_NDP_CO_PRO         BIT(10)
 #define MTK_TX_WB_DDONE                BIT(6)
 #define MTK_DMA_SIZE_16DWORDS  (2 << 4)
 #define MTK_RX_DMA_BUSY                BIT(3)
@@ -357,6 +358,7 @@ struct mtk_rx_ring {
  * @rx_ring:           Pointer to the memore holding info about the RX ring
  * @rx_napi:           The NAPI struct
  * @scratch_ring:      Newer SoCs need memory for a second HW managed TX ring
+ * @phy_scratch_ring:  physical address of scratch_ring
  * @scratch_head:      The scratch memory that scratch_ring points to.
  * @clk_ethif:         The ethif clock
  * @clk_esw:           The switch clock
@@ -384,6 +386,7 @@ struct mtk_eth {
        struct mtk_rx_ring              rx_ring;
        struct napi_struct              rx_napi;
        struct mtk_tx_dma               *scratch_ring;
+       dma_addr_t                      phy_scratch_ring;
        void                            *scratch_head;
        struct clk                      *clk_ethif;
        struct clk                      *clk_esw;
index e94ca1c3fc7c6a83a190a29d4116086dcc9de8ca..f04a423ff79dd977c081300822a767173fd3c57e 100644 (file)
@@ -2597,7 +2597,6 @@ int mlx4_cmd_use_events(struct mlx4_dev *dev)
        priv->cmd.free_head = 0;
 
        sema_init(&priv->cmd.event_sem, priv->cmd.max_cmds);
-       spin_lock_init(&priv->cmd.context_lock);
 
        for (priv->cmd.token_mask = 1;
             priv->cmd.token_mask < priv->cmd.max_cmds;
index fc95affaf76b5d7b36d3c745e7fe271c8442cbb8..44cf16d01f4275501870e3711fa27e5d8ec43a0e 100644 (file)
@@ -1042,6 +1042,8 @@ static int mlx4_en_set_ringparam(struct net_device *dev,
 {
        struct mlx4_en_priv *priv = netdev_priv(dev);
        struct mlx4_en_dev *mdev = priv->mdev;
+       struct mlx4_en_port_profile new_prof;
+       struct mlx4_en_priv *tmp;
        u32 rx_size, tx_size;
        int port_up = 0;
        int err = 0;
@@ -1061,22 +1063,25 @@ static int mlx4_en_set_ringparam(struct net_device *dev,
            tx_size == priv->tx_ring[0]->size)
                return 0;
 
+       tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
+       if (!tmp)
+               return -ENOMEM;
+
        mutex_lock(&mdev->state_lock);
+       memcpy(&new_prof, priv->prof, sizeof(struct mlx4_en_port_profile));
+       new_prof.tx_ring_size = tx_size;
+       new_prof.rx_ring_size = rx_size;
+       err = mlx4_en_try_alloc_resources(priv, tmp, &new_prof);
+       if (err)
+               goto out;
+
        if (priv->port_up) {
                port_up = 1;
                mlx4_en_stop_port(dev, 1);
        }
 
-       mlx4_en_free_resources(priv);
-
-       priv->prof->tx_ring_size = tx_size;
-       priv->prof->rx_ring_size = rx_size;
+       mlx4_en_safe_replace_resources(priv, tmp);
 
-       err = mlx4_en_alloc_resources(priv);
-       if (err) {
-               en_err(priv, "Failed reallocating port resources\n");
-               goto out;
-       }
        if (port_up) {
                err = mlx4_en_start_port(dev);
                if (err)
@@ -1084,8 +1089,8 @@ static int mlx4_en_set_ringparam(struct net_device *dev,
        }
 
        err = mlx4_en_moderation_update(priv);
-
 out:
+       kfree(tmp);
        mutex_unlock(&mdev->state_lock);
        return err;
 }
@@ -1714,6 +1719,8 @@ static int mlx4_en_set_channels(struct net_device *dev,
 {
        struct mlx4_en_priv *priv = netdev_priv(dev);
        struct mlx4_en_dev *mdev = priv->mdev;
+       struct mlx4_en_port_profile new_prof;
+       struct mlx4_en_priv *tmp;
        int port_up = 0;
        int err = 0;
 
@@ -1723,23 +1730,26 @@ static int mlx4_en_set_channels(struct net_device *dev,
            !channel->tx_count || !channel->rx_count)
                return -EINVAL;
 
+       tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
+       if (!tmp)
+               return -ENOMEM;
+
        mutex_lock(&mdev->state_lock);
+       memcpy(&new_prof, priv->prof, sizeof(struct mlx4_en_port_profile));
+       new_prof.num_tx_rings_p_up = channel->tx_count;
+       new_prof.tx_ring_num = channel->tx_count * MLX4_EN_NUM_UP;
+       new_prof.rx_ring_num = channel->rx_count;
+
+       err = mlx4_en_try_alloc_resources(priv, tmp, &new_prof);
+       if (err)
+               goto out;
+
        if (priv->port_up) {
                port_up = 1;
                mlx4_en_stop_port(dev, 1);
        }
 
-       mlx4_en_free_resources(priv);
-
-       priv->num_tx_rings_p_up = channel->tx_count;
-       priv->tx_ring_num = channel->tx_count * MLX4_EN_NUM_UP;
-       priv->rx_ring_num = channel->rx_count;
-
-       err = mlx4_en_alloc_resources(priv);
-       if (err) {
-               en_err(priv, "Failed reallocating port resources\n");
-               goto out;
-       }
+       mlx4_en_safe_replace_resources(priv, tmp);
 
        netif_set_real_num_tx_queues(dev, priv->tx_ring_num);
        netif_set_real_num_rx_queues(dev, priv->rx_ring_num);
@@ -1757,8 +1767,8 @@ static int mlx4_en_set_channels(struct net_device *dev,
        }
 
        err = mlx4_en_moderation_update(priv);
-
 out:
+       kfree(tmp);
        mutex_unlock(&mdev->state_lock);
        return err;
 }
index 19ceced6736c600798b8ce50f70bb3c9072b05bd..8359e9e51b3b4c99d95238575fbe0285b61fa571 100644 (file)
@@ -406,14 +406,18 @@ static int mlx4_en_vlan_rx_add_vid(struct net_device *dev,
        mutex_lock(&mdev->state_lock);
        if (mdev->device_up && priv->port_up) {
                err = mlx4_SET_VLAN_FLTR(mdev->dev, priv);
-               if (err)
+               if (err) {
                        en_err(priv, "Failed configuring VLAN filter\n");
+                       goto out;
+               }
        }
-       if (mlx4_register_vlan(mdev->dev, priv->port, vid, &idx))
-               en_dbg(HW, priv, "failed adding vlan %d\n", vid);
-       mutex_unlock(&mdev->state_lock);
+       err = mlx4_register_vlan(mdev->dev, priv->port, vid, &idx);
+       if (err)
+               en_dbg(HW, priv, "Failed adding vlan %d\n", vid);
 
-       return 0;
+out:
+       mutex_unlock(&mdev->state_lock);
+       return err;
 }
 
 static int mlx4_en_vlan_rx_kill_vid(struct net_device *dev,
@@ -421,7 +425,7 @@ static int mlx4_en_vlan_rx_kill_vid(struct net_device *dev,
 {
        struct mlx4_en_priv *priv = netdev_priv(dev);
        struct mlx4_en_dev *mdev = priv->mdev;
-       int err;
+       int err = 0;
 
        en_dbg(HW, priv, "Killing VID:%d\n", vid);
 
@@ -438,7 +442,7 @@ static int mlx4_en_vlan_rx_kill_vid(struct net_device *dev,
        }
        mutex_unlock(&mdev->state_lock);
 
-       return 0;
+       return err;
 }
 
 static void mlx4_en_u64_to_mac(unsigned char dst_mac[ETH_ALEN + 2], u64 src_mac)
@@ -1950,7 +1954,7 @@ static int mlx4_en_close(struct net_device *dev)
        return 0;
 }
 
-void mlx4_en_free_resources(struct mlx4_en_priv *priv)
+static void mlx4_en_free_resources(struct mlx4_en_priv *priv)
 {
        int i;
 
@@ -1975,7 +1979,7 @@ void mlx4_en_free_resources(struct mlx4_en_priv *priv)
 
 }
 
-int mlx4_en_alloc_resources(struct mlx4_en_priv *priv)
+static int mlx4_en_alloc_resources(struct mlx4_en_priv *priv)
 {
        struct mlx4_en_port_profile *prof = priv->prof;
        int i;
@@ -2032,11 +2036,91 @@ int mlx4_en_alloc_resources(struct mlx4_en_priv *priv)
        return -ENOMEM;
 }
 
+static void mlx4_en_shutdown(struct net_device *dev)
+{
+       rtnl_lock();
+       netif_device_detach(dev);
+       mlx4_en_close(dev);
+       rtnl_unlock();
+}
+
+static int mlx4_en_copy_priv(struct mlx4_en_priv *dst,
+                            struct mlx4_en_priv *src,
+                            struct mlx4_en_port_profile *prof)
+{
+       memcpy(&dst->hwtstamp_config, &prof->hwtstamp_config,
+              sizeof(dst->hwtstamp_config));
+       dst->num_tx_rings_p_up = src->mdev->profile.num_tx_rings_p_up;
+       dst->tx_ring_num = prof->tx_ring_num;
+       dst->rx_ring_num = prof->rx_ring_num;
+       dst->flags = prof->flags;
+       dst->mdev = src->mdev;
+       dst->port = src->port;
+       dst->dev = src->dev;
+       dst->prof = prof;
+       dst->stride = roundup_pow_of_two(sizeof(struct mlx4_en_rx_desc) +
+                                        DS_SIZE * MLX4_EN_MAX_RX_FRAGS);
+
+       dst->tx_ring = kzalloc(sizeof(struct mlx4_en_tx_ring *) * MAX_TX_RINGS,
+                               GFP_KERNEL);
+       if (!dst->tx_ring)
+               return -ENOMEM;
+
+       dst->tx_cq = kzalloc(sizeof(struct mlx4_en_cq *) * MAX_TX_RINGS,
+                             GFP_KERNEL);
+       if (!dst->tx_cq) {
+               kfree(dst->tx_ring);
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+static void mlx4_en_update_priv(struct mlx4_en_priv *dst,
+                               struct mlx4_en_priv *src)
+{
+       memcpy(dst->rx_ring, src->rx_ring,
+              sizeof(struct mlx4_en_rx_ring *) * src->rx_ring_num);
+       memcpy(dst->rx_cq, src->rx_cq,
+              sizeof(struct mlx4_en_cq *) * src->rx_ring_num);
+       memcpy(&dst->hwtstamp_config, &src->hwtstamp_config,
+              sizeof(dst->hwtstamp_config));
+       dst->tx_ring_num = src->tx_ring_num;
+       dst->rx_ring_num = src->rx_ring_num;
+       dst->tx_ring = src->tx_ring;
+       dst->tx_cq = src->tx_cq;
+       memcpy(dst->prof, src->prof, sizeof(struct mlx4_en_port_profile));
+}
+
+int mlx4_en_try_alloc_resources(struct mlx4_en_priv *priv,
+                               struct mlx4_en_priv *tmp,
+                               struct mlx4_en_port_profile *prof)
+{
+       mlx4_en_copy_priv(tmp, priv, prof);
+
+       if (mlx4_en_alloc_resources(tmp)) {
+               en_warn(priv,
+                       "%s: Resource allocation failed, using previous configuration\n",
+                       __func__);
+               kfree(tmp->tx_ring);
+               kfree(tmp->tx_cq);
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+void mlx4_en_safe_replace_resources(struct mlx4_en_priv *priv,
+                                   struct mlx4_en_priv *tmp)
+{
+       mlx4_en_free_resources(priv);
+       mlx4_en_update_priv(priv, tmp);
+}
 
 void mlx4_en_destroy_netdev(struct net_device *dev)
 {
        struct mlx4_en_priv *priv = netdev_priv(dev);
        struct mlx4_en_dev *mdev = priv->mdev;
+       bool shutdown = mdev->dev->persist->interface_state &
+                                           MLX4_INTERFACE_STATE_SHUTDOWN;
 
        en_dbg(DRV, priv, "Destroying netdev on port:%d\n", priv->port);
 
@@ -2044,7 +2128,10 @@ void mlx4_en_destroy_netdev(struct net_device *dev)
        if (priv->registered) {
                devlink_port_type_clear(mlx4_get_devlink_port(mdev->dev,
                                                              priv->port));
-               unregister_netdev(dev);
+               if (shutdown)
+                       mlx4_en_shutdown(dev);
+               else
+                       unregister_netdev(dev);
        }
 
        if (priv->allocated)
@@ -2064,12 +2151,17 @@ void mlx4_en_destroy_netdev(struct net_device *dev)
        mdev->upper[priv->port] = NULL;
        mutex_unlock(&mdev->state_lock);
 
+#ifdef CONFIG_RFS_ACCEL
+       mlx4_en_cleanup_filters(priv);
+#endif
+
        mlx4_en_free_resources(priv);
 
        kfree(priv->tx_ring);
        kfree(priv->tx_cq);
 
-       free_netdev(dev);
+       if (!shutdown)
+               free_netdev(dev);
 }
 
 static int mlx4_en_change_mtu(struct net_device *dev, int new_mtu)
@@ -2447,9 +2539,14 @@ static netdev_features_t mlx4_en_features_check(struct sk_buff *skb,
         * strip that feature if this is an IPv6 encapsulated frame.
         */
        if (skb->encapsulation &&
-           (skb->ip_summed == CHECKSUM_PARTIAL) &&
-           (ip_hdr(skb)->version != 4))
-               features &= ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
+           (skb->ip_summed == CHECKSUM_PARTIAL)) {
+               struct mlx4_en_priv *priv = netdev_priv(dev);
+
+               if (!priv->vxlan_port ||
+                   (ip_hdr(skb)->version != 4) ||
+                   (udp_hdr(skb)->dest != priv->vxlan_port))
+                       features &= ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
+       }
 
        return features;
 }
@@ -3102,6 +3199,8 @@ int mlx4_en_reset_config(struct net_device *dev,
 {
        struct mlx4_en_priv *priv = netdev_priv(dev);
        struct mlx4_en_dev *mdev = priv->mdev;
+       struct mlx4_en_port_profile new_prof;
+       struct mlx4_en_priv *tmp;
        int port_up = 0;
        int err = 0;
 
@@ -3118,19 +3217,29 @@ int mlx4_en_reset_config(struct net_device *dev,
                return -EINVAL;
        }
 
+       tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
+       if (!tmp)
+               return -ENOMEM;
+
        mutex_lock(&mdev->state_lock);
+
+       memcpy(&new_prof, priv->prof, sizeof(struct mlx4_en_port_profile));
+       memcpy(&new_prof.hwtstamp_config, &ts_config, sizeof(ts_config));
+
+       err = mlx4_en_try_alloc_resources(priv, tmp, &new_prof);
+       if (err)
+               goto out;
+
        if (priv->port_up) {
                port_up = 1;
                mlx4_en_stop_port(dev, 1);
        }
 
-       mlx4_en_free_resources(priv);
-
        en_warn(priv, "Changing device configuration rx filter(%x) rx vlan(%x)\n",
-               ts_config.rx_filter, !!(features & NETIF_F_HW_VLAN_CTAG_RX));
+               ts_config.rx_filter,
+               !!(features & NETIF_F_HW_VLAN_CTAG_RX));
 
-       priv->hwtstamp_config.tx_type = ts_config.tx_type;
-       priv->hwtstamp_config.rx_filter = ts_config.rx_filter;
+       mlx4_en_safe_replace_resources(priv, tmp);
 
        if (DEV_FEATURE_CHANGED(dev, features, NETIF_F_HW_VLAN_CTAG_RX)) {
                if (features & NETIF_F_HW_VLAN_CTAG_RX)
@@ -3164,11 +3273,6 @@ int mlx4_en_reset_config(struct net_device *dev,
                dev->features &= ~NETIF_F_HW_VLAN_CTAG_RX;
        }
 
-       err = mlx4_en_alloc_resources(priv);
-       if (err) {
-               en_err(priv, "Failed reallocating port resources\n");
-               goto out;
-       }
        if (port_up) {
                err = mlx4_en_start_port(dev);
                if (err)
@@ -3177,6 +3281,8 @@ int mlx4_en_reset_config(struct net_device *dev,
 
 out:
        mutex_unlock(&mdev->state_lock);
-       netdev_features_change(dev);
+       kfree(tmp);
+       if (!err)
+               netdev_features_change(dev);
        return err;
 }
index c1b3a9c8cf3b4db9412e722e09983e72c64e3caa..99b5407f2278c0f292961cf8b458e4b4dfa584e7 100644 (file)
@@ -514,9 +514,6 @@ void mlx4_en_destroy_rx_ring(struct mlx4_en_priv *priv,
        ring->rx_info = NULL;
        kfree(ring);
        *pring = NULL;
-#ifdef CONFIG_RFS_ACCEL
-       mlx4_en_cleanup_filters(priv);
-#endif
 }
 
 void mlx4_en_deactivate_rx_ring(struct mlx4_en_priv *priv,
index 12c77a70abdb451c475680c05bb2b7cf86436cdf..546fab0ecc3bb5e8f8cbaa4fe401dcca1ca070d9 100644 (file)
@@ -3222,6 +3222,7 @@ static int mlx4_load_one(struct pci_dev *pdev, int pci_dev_data,
 
        INIT_LIST_HEAD(&priv->pgdir_list);
        mutex_init(&priv->pgdir_mutex);
+       spin_lock_init(&priv->cmd.context_lock);
 
        INIT_LIST_HEAD(&priv->bf_list);
        mutex_init(&priv->bf_mutex);
@@ -4134,8 +4135,11 @@ static void mlx4_shutdown(struct pci_dev *pdev)
 
        mlx4_info(persist->dev, "mlx4_shutdown was called\n");
        mutex_lock(&persist->interface_state_mutex);
-       if (persist->interface_state & MLX4_INTERFACE_STATE_UP)
+       if (persist->interface_state & MLX4_INTERFACE_STATE_UP) {
+               /* Notify mlx4 clients that the kernel is being shut down */
+               persist->interface_state |= MLX4_INTERFACE_STATE_SHUTDOWN;
                mlx4_unload_one(pdev);
+       }
        mutex_unlock(&persist->interface_state_mutex);
 }
 
index 467d47ed2c394093ca8be3fa0ce1f1a26dc1a038..13d297ee34bb3f7fa195891e3959ce62605e580b 100644 (file)
@@ -353,12 +353,14 @@ struct mlx4_en_port_profile {
        u32 rx_ring_num;
        u32 tx_ring_size;
        u32 rx_ring_size;
+       u8 num_tx_rings_p_up;
        u8 rx_pause;
        u8 rx_ppp;
        u8 tx_pause;
        u8 tx_ppp;
        int rss_rings;
        int inline_thold;
+       struct hwtstamp_config hwtstamp_config;
 };
 
 struct mlx4_en_profile {
@@ -623,8 +625,11 @@ void mlx4_en_set_stats_bitmap(struct mlx4_dev *dev,
                              u8 rx_ppp, u8 rx_pause,
                              u8 tx_ppp, u8 tx_pause);
 
-void mlx4_en_free_resources(struct mlx4_en_priv *priv);
-int mlx4_en_alloc_resources(struct mlx4_en_priv *priv);
+int mlx4_en_try_alloc_resources(struct mlx4_en_priv *priv,
+                               struct mlx4_en_priv *tmp,
+                               struct mlx4_en_port_profile *prof);
+void mlx4_en_safe_replace_resources(struct mlx4_en_priv *priv,
+                                   struct mlx4_en_priv *tmp);
 
 int mlx4_en_create_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq **pcq,
                      int entries, int ring, enum cq_type mode, int node);
index dcd2df6518de32a8e412b10a548c0973caa15568..d6e2a1cae19ae2d6d636f1d306bca8e607106095 100644 (file)
@@ -295,6 +295,12 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op,
        case MLX5_CMD_OP_DESTROY_FLOW_GROUP:
        case MLX5_CMD_OP_DELETE_FLOW_TABLE_ENTRY:
        case MLX5_CMD_OP_DEALLOC_FLOW_COUNTER:
+       case MLX5_CMD_OP_2ERR_QP:
+       case MLX5_CMD_OP_2RST_QP:
+       case MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT:
+       case MLX5_CMD_OP_MODIFY_FLOW_TABLE:
+       case MLX5_CMD_OP_SET_FLOW_TABLE_ENTRY:
+       case MLX5_CMD_OP_SET_FLOW_TABLE_ROOT:
                return MLX5_CMD_STAT_OK;
 
        case MLX5_CMD_OP_QUERY_HCA_CAP:
@@ -321,8 +327,6 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op,
        case MLX5_CMD_OP_RTR2RTS_QP:
        case MLX5_CMD_OP_RTS2RTS_QP:
        case MLX5_CMD_OP_SQERR2RTS_QP:
-       case MLX5_CMD_OP_2ERR_QP:
-       case MLX5_CMD_OP_2RST_QP:
        case MLX5_CMD_OP_QUERY_QP:
        case MLX5_CMD_OP_SQD_RTS_QP:
        case MLX5_CMD_OP_INIT2INIT_QP:
@@ -342,7 +346,6 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op,
        case MLX5_CMD_OP_QUERY_ESW_VPORT_CONTEXT:
        case MLX5_CMD_OP_MODIFY_ESW_VPORT_CONTEXT:
        case MLX5_CMD_OP_QUERY_NIC_VPORT_CONTEXT:
-       case MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT:
        case MLX5_CMD_OP_QUERY_ROCE_ADDRESS:
        case MLX5_CMD_OP_SET_ROCE_ADDRESS:
        case MLX5_CMD_OP_QUERY_HCA_VPORT_CONTEXT:
@@ -390,11 +393,12 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op,
        case MLX5_CMD_OP_CREATE_RQT:
        case MLX5_CMD_OP_MODIFY_RQT:
        case MLX5_CMD_OP_QUERY_RQT:
+
        case MLX5_CMD_OP_CREATE_FLOW_TABLE:
        case MLX5_CMD_OP_QUERY_FLOW_TABLE:
        case MLX5_CMD_OP_CREATE_FLOW_GROUP:
        case MLX5_CMD_OP_QUERY_FLOW_GROUP:
-       case MLX5_CMD_OP_SET_FLOW_TABLE_ENTRY:
+
        case MLX5_CMD_OP_QUERY_FLOW_TABLE_ENTRY:
        case MLX5_CMD_OP_ALLOC_FLOW_COUNTER:
        case MLX5_CMD_OP_QUERY_FLOW_COUNTER:
@@ -545,6 +549,7 @@ const char *mlx5_command_str(int command)
        MLX5_COMMAND_STR_CASE(ALLOC_FLOW_COUNTER);
        MLX5_COMMAND_STR_CASE(DEALLOC_FLOW_COUNTER);
        MLX5_COMMAND_STR_CASE(QUERY_FLOW_COUNTER);
+       MLX5_COMMAND_STR_CASE(MODIFY_FLOW_TABLE);
        default: return "unknown command opcode";
        }
 }
@@ -601,11 +606,36 @@ static void dump_command(struct mlx5_core_dev *dev,
                pr_debug("\n");
 }
 
+static u16 msg_to_opcode(struct mlx5_cmd_msg *in)
+{
+       struct mlx5_inbox_hdr *hdr = (struct mlx5_inbox_hdr *)(in->first.data);
+
+       return be16_to_cpu(hdr->opcode);
+}
+
+static void cb_timeout_handler(struct work_struct *work)
+{
+       struct delayed_work *dwork = container_of(work, struct delayed_work,
+                                                 work);
+       struct mlx5_cmd_work_ent *ent = container_of(dwork,
+                                                    struct mlx5_cmd_work_ent,
+                                                    cb_timeout_work);
+       struct mlx5_core_dev *dev = container_of(ent->cmd, struct mlx5_core_dev,
+                                                cmd);
+
+       ent->ret = -ETIMEDOUT;
+       mlx5_core_warn(dev, "%s(0x%x) timeout. Will cause a leak of a command resource\n",
+                      mlx5_command_str(msg_to_opcode(ent->in)),
+                      msg_to_opcode(ent->in));
+       mlx5_cmd_comp_handler(dev, 1UL << ent->idx);
+}
+
 static void cmd_work_handler(struct work_struct *work)
 {
        struct mlx5_cmd_work_ent *ent = container_of(work, struct mlx5_cmd_work_ent, work);
        struct mlx5_cmd *cmd = ent->cmd;
        struct mlx5_core_dev *dev = container_of(cmd, struct mlx5_core_dev, cmd);
+       unsigned long cb_timeout = msecs_to_jiffies(MLX5_CMD_TIMEOUT_MSEC);
        struct mlx5_cmd_layout *lay;
        struct semaphore *sem;
        unsigned long flags;
@@ -646,6 +676,9 @@ static void cmd_work_handler(struct work_struct *work)
        dump_command(dev, ent, 1);
        ent->ts1 = ktime_get_ns();
 
+       if (ent->callback)
+               schedule_delayed_work(&ent->cb_timeout_work, cb_timeout);
+
        /* ring doorbell after the descriptor is valid */
        mlx5_core_dbg(dev, "writing 0x%x to command doorbell\n", 1 << ent->idx);
        wmb();
@@ -690,13 +723,6 @@ static const char *deliv_status_to_str(u8 status)
        }
 }
 
-static u16 msg_to_opcode(struct mlx5_cmd_msg *in)
-{
-       struct mlx5_inbox_hdr *hdr = (struct mlx5_inbox_hdr *)(in->first.data);
-
-       return be16_to_cpu(hdr->opcode);
-}
-
 static int wait_func(struct mlx5_core_dev *dev, struct mlx5_cmd_work_ent *ent)
 {
        unsigned long timeout = msecs_to_jiffies(MLX5_CMD_TIMEOUT_MSEC);
@@ -705,13 +731,13 @@ static int wait_func(struct mlx5_core_dev *dev, struct mlx5_cmd_work_ent *ent)
 
        if (cmd->mode == CMD_MODE_POLLING) {
                wait_for_completion(&ent->done);
-               err = ent->ret;
-       } else {
-               if (!wait_for_completion_timeout(&ent->done, timeout))
-                       err = -ETIMEDOUT;
-               else
-                       err = 0;
+       } else if (!wait_for_completion_timeout(&ent->done, timeout)) {
+               ent->ret = -ETIMEDOUT;
+               mlx5_cmd_comp_handler(dev, 1UL << ent->idx);
        }
+
+       err = ent->ret;
+
        if (err == -ETIMEDOUT) {
                mlx5_core_warn(dev, "%s(0x%x) timeout. Will cause a leak of a command resource\n",
                               mlx5_command_str(msg_to_opcode(ent->in)),
@@ -760,6 +786,7 @@ static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in,
        if (!callback)
                init_completion(&ent->done);
 
+       INIT_DELAYED_WORK(&ent->cb_timeout_work, cb_timeout_handler);
        INIT_WORK(&ent->work, cmd_work_handler);
        if (page_queue) {
                cmd_work_handler(&ent->work);
@@ -769,28 +796,26 @@ static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in,
                goto out_free;
        }
 
-       if (!callback) {
-               err = wait_func(dev, ent);
-               if (err == -ETIMEDOUT)
-                       goto out;
-
-               ds = ent->ts2 - ent->ts1;
-               op = be16_to_cpu(((struct mlx5_inbox_hdr *)in->first.data)->opcode);
-               if (op < ARRAY_SIZE(cmd->stats)) {
-                       stats = &cmd->stats[op];
-                       spin_lock_irq(&stats->lock);
-                       stats->sum += ds;
-                       ++stats->n;
-                       spin_unlock_irq(&stats->lock);
-               }
-               mlx5_core_dbg_mask(dev, 1 << MLX5_CMD_TIME,
-                                  "fw exec time for %s is %lld nsec\n",
-                                  mlx5_command_str(op), ds);
-               *status = ent->status;
-               free_cmd(ent);
-       }
+       if (callback)
+               goto out;
 
-       return err;
+       err = wait_func(dev, ent);
+       if (err == -ETIMEDOUT)
+               goto out_free;
+
+       ds = ent->ts2 - ent->ts1;
+       op = be16_to_cpu(((struct mlx5_inbox_hdr *)in->first.data)->opcode);
+       if (op < ARRAY_SIZE(cmd->stats)) {
+               stats = &cmd->stats[op];
+               spin_lock_irq(&stats->lock);
+               stats->sum += ds;
+               ++stats->n;
+               spin_unlock_irq(&stats->lock);
+       }
+       mlx5_core_dbg_mask(dev, 1 << MLX5_CMD_TIME,
+                          "fw exec time for %s is %lld nsec\n",
+                          mlx5_command_str(op), ds);
+       *status = ent->status;
 
 out_free:
        free_cmd(ent);
@@ -1180,41 +1205,30 @@ static int create_debugfs_files(struct mlx5_core_dev *dev)
        return err;
 }
 
-void mlx5_cmd_use_events(struct mlx5_core_dev *dev)
+static void mlx5_cmd_change_mod(struct mlx5_core_dev *dev, int mode)
 {
        struct mlx5_cmd *cmd = &dev->cmd;
        int i;
 
        for (i = 0; i < cmd->max_reg_cmds; i++)
                down(&cmd->sem);
-
        down(&cmd->pages_sem);
 
-       flush_workqueue(cmd->wq);
-
-       cmd->mode = CMD_MODE_EVENTS;
+       cmd->mode = mode;
 
        up(&cmd->pages_sem);
        for (i = 0; i < cmd->max_reg_cmds; i++)
                up(&cmd->sem);
 }
 
-void mlx5_cmd_use_polling(struct mlx5_core_dev *dev)
+void mlx5_cmd_use_events(struct mlx5_core_dev *dev)
 {
-       struct mlx5_cmd *cmd = &dev->cmd;
-       int i;
-
-       for (i = 0; i < cmd->max_reg_cmds; i++)
-               down(&cmd->sem);
-
-       down(&cmd->pages_sem);
-
-       flush_workqueue(cmd->wq);
-       cmd->mode = CMD_MODE_POLLING;
+       mlx5_cmd_change_mod(dev, CMD_MODE_EVENTS);
+}
 
-       up(&cmd->pages_sem);
-       for (i = 0; i < cmd->max_reg_cmds; i++)
-               up(&cmd->sem);
+void mlx5_cmd_use_polling(struct mlx5_core_dev *dev)
+{
+       mlx5_cmd_change_mod(dev, CMD_MODE_POLLING);
 }
 
 static void free_msg(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *msg)
@@ -1250,6 +1264,8 @@ void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec)
                        struct semaphore *sem;
 
                        ent = cmd->ent_arr[i];
+                       if (ent->callback)
+                               cancel_delayed_work(&ent->cb_timeout_work);
                        if (ent->page_queue)
                                sem = &cmd->pages_sem;
                        else
index e8a6c3325b396c6295db60c833309c4e4102e780..943b1bd434bf50cf6ef26b408035f3af4ea6c38a 100644 (file)
@@ -145,7 +145,6 @@ struct mlx5e_umr_wqe {
 
 #ifdef CONFIG_MLX5_CORE_EN_DCB
 #define MLX5E_MAX_BW_ALLOC 100 /* Max percentage of BW allocation */
-#define MLX5E_MIN_BW_ALLOC 1   /* Min percentage of BW allocation */
 #endif
 
 struct mlx5e_params {
@@ -191,6 +190,7 @@ struct mlx5e_tstamp {
 enum {
        MLX5E_RQ_STATE_POST_WQES_ENABLE,
        MLX5E_RQ_STATE_UMR_WQE_IN_PROGRESS,
+       MLX5E_RQ_STATE_FLUSH_TIMEOUT,
 };
 
 struct mlx5e_cq {
@@ -220,6 +220,8 @@ typedef void (*mlx5e_fp_handle_rx_cqe)(struct mlx5e_rq *rq,
 typedef int (*mlx5e_fp_alloc_wqe)(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe,
                                  u16 ix);
 
+typedef void (*mlx5e_fp_dealloc_wqe)(struct mlx5e_rq *rq, u16 ix);
+
 struct mlx5e_dma_info {
        struct page     *page;
        dma_addr_t      addr;
@@ -241,6 +243,7 @@ struct mlx5e_rq {
        struct mlx5e_cq        cq;
        mlx5e_fp_handle_rx_cqe handle_rx_cqe;
        mlx5e_fp_alloc_wqe     alloc_wqe;
+       mlx5e_fp_dealloc_wqe   dealloc_wqe;
 
        unsigned long          state;
        int                    ix;
@@ -305,6 +308,7 @@ struct mlx5e_sq_dma {
 enum {
        MLX5E_SQ_STATE_WAKE_TXQ_ENABLE,
        MLX5E_SQ_STATE_BF_ENABLE,
+       MLX5E_SQ_STATE_TX_TIMEOUT,
 };
 
 struct mlx5e_ico_wqe_info {
@@ -401,7 +405,7 @@ enum mlx5e_traffic_types {
 };
 
 enum {
-       MLX5E_STATE_ASYNC_EVENTS_ENABLE,
+       MLX5E_STATE_ASYNC_EVENTS_ENABLED,
        MLX5E_STATE_OPENED,
        MLX5E_STATE_DESTROYING,
 };
@@ -538,6 +542,7 @@ struct mlx5e_priv {
        struct workqueue_struct    *wq;
        struct work_struct         update_carrier_work;
        struct work_struct         set_rx_mode_work;
+       struct work_struct         tx_timeout_work;
        struct delayed_work        update_stats_work;
 
        struct mlx5_core_dev      *mdev;
@@ -589,12 +594,16 @@ void mlx5e_cq_error_event(struct mlx5_core_cq *mcq, enum mlx5_event event);
 int mlx5e_napi_poll(struct napi_struct *napi, int budget);
 bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget);
 int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget);
+void mlx5e_free_tx_descs(struct mlx5e_sq *sq);
+void mlx5e_free_rx_descs(struct mlx5e_rq *rq);
 
 void mlx5e_handle_rx_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe);
 void mlx5e_handle_rx_cqe_mpwrq(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe);
 bool mlx5e_post_rx_wqes(struct mlx5e_rq *rq);
 int mlx5e_alloc_rx_wqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix);
 int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix);
+void mlx5e_dealloc_rx_wqe(struct mlx5e_rq *rq, u16 ix);
+void mlx5e_dealloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix);
 void mlx5e_post_rx_fragmented_mpwqe(struct mlx5e_rq *rq);
 void mlx5e_complete_rx_linear_mpwqe(struct mlx5e_rq *rq,
                                    struct mlx5_cqe64 *cqe,
index b2db180ae2a5bbdda29219d63c72feea0958c12c..c585349e05c38ed26b49898d23be1ef83fa721a6 100644 (file)
@@ -96,7 +96,7 @@ static void mlx5e_build_tc_tx_bw(struct ieee_ets *ets, u8 *tc_tx_bw,
                        tc_tx_bw[i] = MLX5E_MAX_BW_ALLOC;
                        break;
                case IEEE_8021QAZ_TSA_ETS:
-                       tc_tx_bw[i] = ets->tc_tx_bw[i] ?: MLX5E_MIN_BW_ALLOC;
+                       tc_tx_bw[i] = ets->tc_tx_bw[i];
                        break;
                }
        }
@@ -140,8 +140,12 @@ static int mlx5e_dbcnl_validate_ets(struct ieee_ets *ets)
 
        /* Validate Bandwidth Sum */
        for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
-               if (ets->tc_tsa[i] == IEEE_8021QAZ_TSA_ETS)
+               if (ets->tc_tsa[i] == IEEE_8021QAZ_TSA_ETS) {
+                       if (!ets->tc_tx_bw[i])
+                               return -EINVAL;
+
                        bw_sum += ets->tc_tx_bw[i];
+               }
        }
 
        if (bw_sum != 0 && bw_sum != 100)
index fc7dcc03b1debd36c6362d887e4f90b3cefab50f..e667a870e0c273e72c49e3708ba0de6370dceaca 100644 (file)
@@ -184,7 +184,9 @@ static unsigned long mlx5e_query_pfc_combined(struct mlx5e_priv *priv)
 #define MLX5E_NUM_SQ_STATS(priv) \
        (NUM_SQ_STATS * priv->params.num_channels * priv->params.num_tc * \
         test_bit(MLX5E_STATE_OPENED, &priv->state))
-#define MLX5E_NUM_PFC_COUNTERS(priv) hweight8(mlx5e_query_pfc_combined(priv))
+#define MLX5E_NUM_PFC_COUNTERS(priv) \
+       (hweight8(mlx5e_query_pfc_combined(priv)) * \
+        NUM_PPORT_PER_PRIO_PFC_COUNTERS)
 
 static int mlx5e_get_sset_count(struct net_device *dev, int sset)
 {
@@ -211,42 +213,41 @@ static void mlx5e_fill_stats_strings(struct mlx5e_priv *priv, uint8_t *data)
 
        /* SW counters */
        for (i = 0; i < NUM_SW_COUNTERS; i++)
-               strcpy(data + (idx++) * ETH_GSTRING_LEN, sw_stats_desc[i].name);
+               strcpy(data + (idx++) * ETH_GSTRING_LEN, sw_stats_desc[i].format);
 
        /* Q counters */
        for (i = 0; i < MLX5E_NUM_Q_CNTRS(priv); i++)
-               strcpy(data + (idx++) * ETH_GSTRING_LEN, q_stats_desc[i].name);
+               strcpy(data + (idx++) * ETH_GSTRING_LEN, q_stats_desc[i].format);
 
        /* VPORT counters */
        for (i = 0; i < NUM_VPORT_COUNTERS; i++)
                strcpy(data + (idx++) * ETH_GSTRING_LEN,
-                      vport_stats_desc[i].name);
+                      vport_stats_desc[i].format);
 
        /* PPORT counters */
        for (i = 0; i < NUM_PPORT_802_3_COUNTERS; i++)
                strcpy(data + (idx++) * ETH_GSTRING_LEN,
-                      pport_802_3_stats_desc[i].name);
+                      pport_802_3_stats_desc[i].format);
 
        for (i = 0; i < NUM_PPORT_2863_COUNTERS; i++)
                strcpy(data + (idx++) * ETH_GSTRING_LEN,
-                      pport_2863_stats_desc[i].name);
+                      pport_2863_stats_desc[i].format);
 
        for (i = 0; i < NUM_PPORT_2819_COUNTERS; i++)
                strcpy(data + (idx++) * ETH_GSTRING_LEN,
-                      pport_2819_stats_desc[i].name);
+                      pport_2819_stats_desc[i].format);
 
        for (prio = 0; prio < NUM_PPORT_PRIO; prio++) {
                for (i = 0; i < NUM_PPORT_PER_PRIO_TRAFFIC_COUNTERS; i++)
-                       sprintf(data + (idx++) * ETH_GSTRING_LEN, "prio%d_%s",
-                               prio,
-                               pport_per_prio_traffic_stats_desc[i].name);
+                       sprintf(data + (idx++) * ETH_GSTRING_LEN,
+                               pport_per_prio_traffic_stats_desc[i].format, prio);
        }
 
        pfc_combined = mlx5e_query_pfc_combined(priv);
        for_each_set_bit(prio, &pfc_combined, NUM_PPORT_PRIO) {
                for (i = 0; i < NUM_PPORT_PER_PRIO_PFC_COUNTERS; i++) {
-                       sprintf(data + (idx++) * ETH_GSTRING_LEN, "prio%d_%s",
-                               prio, pport_per_prio_pfc_stats_desc[i].name);
+                       sprintf(data + (idx++) * ETH_GSTRING_LEN,
+                               pport_per_prio_pfc_stats_desc[i].format, prio);
                }
        }
 
@@ -256,16 +257,15 @@ static void mlx5e_fill_stats_strings(struct mlx5e_priv *priv, uint8_t *data)
        /* per channel counters */
        for (i = 0; i < priv->params.num_channels; i++)
                for (j = 0; j < NUM_RQ_STATS; j++)
-                       sprintf(data + (idx++) * ETH_GSTRING_LEN, "rx%d_%s", i,
-                               rq_stats_desc[j].name);
+                       sprintf(data + (idx++) * ETH_GSTRING_LEN,
+                               rq_stats_desc[j].format, i);
 
        for (tc = 0; tc < priv->params.num_tc; tc++)
                for (i = 0; i < priv->params.num_channels; i++)
                        for (j = 0; j < NUM_SQ_STATS; j++)
                                sprintf(data + (idx++) * ETH_GSTRING_LEN,
-                                       "tx%d_%s",
-                                       priv->channeltc_to_txq_map[i][tc],
-                                       sq_stats_desc[j].name);
+                                       sq_stats_desc[j].format,
+                                       priv->channeltc_to_txq_map[i][tc]);
 }
 
 static void mlx5e_get_strings(struct net_device *dev,
index f5c8d5db25a8cebc367c752c4333bc839b72505a..5a4d88c2cdb292880496831603fb741f5700a880 100644 (file)
 #include "eswitch.h"
 #include "vxlan.h"
 
+enum {
+       MLX5_EN_QP_FLUSH_TIMEOUT_MS     = 5000,
+       MLX5_EN_QP_FLUSH_MSLEEP_QUANT   = 20,
+       MLX5_EN_QP_FLUSH_MAX_ITER       = MLX5_EN_QP_FLUSH_TIMEOUT_MS /
+                                         MLX5_EN_QP_FLUSH_MSLEEP_QUANT,
+};
+
 struct mlx5e_rq_param {
        u32                        rqc[MLX5_ST_SZ_DW(rqc)];
        struct mlx5_wq_param       wq;
@@ -74,10 +81,13 @@ static void mlx5e_update_carrier(struct mlx5e_priv *priv)
        port_state = mlx5_query_vport_state(mdev,
                MLX5_QUERY_VPORT_STATE_IN_OP_MOD_VNIC_VPORT, 0);
 
-       if (port_state == VPORT_STATE_UP)
+       if (port_state == VPORT_STATE_UP) {
+               netdev_info(priv->netdev, "Link up\n");
                netif_carrier_on(priv->netdev);
-       else
+       } else {
+               netdev_info(priv->netdev, "Link down\n");
                netif_carrier_off(priv->netdev);
+       }
 }
 
 static void mlx5e_update_carrier_work(struct work_struct *work)
@@ -91,6 +101,26 @@ static void mlx5e_update_carrier_work(struct work_struct *work)
        mutex_unlock(&priv->state_lock);
 }
 
+static void mlx5e_tx_timeout_work(struct work_struct *work)
+{
+       struct mlx5e_priv *priv = container_of(work, struct mlx5e_priv,
+                                              tx_timeout_work);
+       int err;
+
+       rtnl_lock();
+       mutex_lock(&priv->state_lock);
+       if (!test_bit(MLX5E_STATE_OPENED, &priv->state))
+               goto unlock;
+       mlx5e_close_locked(priv->netdev);
+       err = mlx5e_open_locked(priv->netdev);
+       if (err)
+               netdev_err(priv->netdev, "mlx5e_open_locked failed recovering from a tx_timeout, err(%d).\n",
+                          err);
+unlock:
+       mutex_unlock(&priv->state_lock);
+       rtnl_unlock();
+}
+
 static void mlx5e_update_sw_counters(struct mlx5e_priv *priv)
 {
        struct mlx5e_sw_stats *s = &priv->stats.sw;
@@ -105,11 +135,11 @@ static void mlx5e_update_sw_counters(struct mlx5e_priv *priv)
 
                s->rx_packets   += rq_stats->packets;
                s->rx_bytes     += rq_stats->bytes;
-               s->lro_packets  += rq_stats->lro_packets;
-               s->lro_bytes    += rq_stats->lro_bytes;
+               s->rx_lro_packets += rq_stats->lro_packets;
+               s->rx_lro_bytes += rq_stats->lro_bytes;
                s->rx_csum_none += rq_stats->csum_none;
-               s->rx_csum_sw   += rq_stats->csum_sw;
-               s->rx_csum_inner += rq_stats->csum_inner;
+               s->rx_csum_complete += rq_stats->csum_complete;
+               s->rx_csum_unnecessary_inner += rq_stats->csum_unnecessary_inner;
                s->rx_wqe_err   += rq_stats->wqe_err;
                s->rx_mpwqe_filler += rq_stats->mpwqe_filler;
                s->rx_mpwqe_frag   += rq_stats->mpwqe_frag;
@@ -122,24 +152,23 @@ static void mlx5e_update_sw_counters(struct mlx5e_priv *priv)
 
                        s->tx_packets           += sq_stats->packets;
                        s->tx_bytes             += sq_stats->bytes;
-                       s->tso_packets          += sq_stats->tso_packets;
-                       s->tso_bytes            += sq_stats->tso_bytes;
-                       s->tso_inner_packets    += sq_stats->tso_inner_packets;
-                       s->tso_inner_bytes      += sq_stats->tso_inner_bytes;
+                       s->tx_tso_packets       += sq_stats->tso_packets;
+                       s->tx_tso_bytes         += sq_stats->tso_bytes;
+                       s->tx_tso_inner_packets += sq_stats->tso_inner_packets;
+                       s->tx_tso_inner_bytes   += sq_stats->tso_inner_bytes;
                        s->tx_queue_stopped     += sq_stats->stopped;
                        s->tx_queue_wake        += sq_stats->wake;
                        s->tx_queue_dropped     += sq_stats->dropped;
-                       s->tx_csum_inner        += sq_stats->csum_offload_inner;
-                       tx_offload_none         += sq_stats->csum_offload_none;
+                       s->tx_csum_partial_inner += sq_stats->csum_partial_inner;
+                       tx_offload_none         += sq_stats->csum_none;
                }
        }
 
        /* Update calculated offload counters */
-       s->tx_csum_offload = s->tx_packets - tx_offload_none - s->tx_csum_inner;
-       s->rx_csum_good    = s->rx_packets - s->rx_csum_none -
-                            s->rx_csum_sw;
+       s->tx_csum_partial = s->tx_packets - tx_offload_none - s->tx_csum_partial_inner;
+       s->rx_csum_unnecessary = s->rx_packets - s->rx_csum_none - s->rx_csum_complete;
 
-       s->link_down_events = MLX5_GET(ppcnt_reg,
+       s->link_down_events_phy = MLX5_GET(ppcnt_reg,
                                priv->stats.pport.phy_counters,
                                counter_set.phys_layer_cntrs.link_down_events);
 }
@@ -244,7 +273,7 @@ static void mlx5e_async_event(struct mlx5_core_dev *mdev, void *vpriv,
 {
        struct mlx5e_priv *priv = vpriv;
 
-       if (!test_bit(MLX5E_STATE_ASYNC_EVENTS_ENABLE, &priv->state))
+       if (!test_bit(MLX5E_STATE_ASYNC_EVENTS_ENABLED, &priv->state))
                return;
 
        switch (event) {
@@ -260,12 +289,12 @@ static void mlx5e_async_event(struct mlx5_core_dev *mdev, void *vpriv,
 
 static void mlx5e_enable_async_events(struct mlx5e_priv *priv)
 {
-       set_bit(MLX5E_STATE_ASYNC_EVENTS_ENABLE, &priv->state);
+       set_bit(MLX5E_STATE_ASYNC_EVENTS_ENABLED, &priv->state);
 }
 
 static void mlx5e_disable_async_events(struct mlx5e_priv *priv)
 {
-       clear_bit(MLX5E_STATE_ASYNC_EVENTS_ENABLE, &priv->state);
+       clear_bit(MLX5E_STATE_ASYNC_EVENTS_ENABLED, &priv->state);
        synchronize_irq(mlx5_get_msix_vec(priv->mdev, MLX5_EQ_VEC_ASYNC));
 }
 
@@ -306,6 +335,7 @@ static int mlx5e_create_rq(struct mlx5e_channel *c,
                }
                rq->handle_rx_cqe = mlx5e_handle_rx_cqe_mpwrq;
                rq->alloc_wqe = mlx5e_alloc_rx_mpwqe;
+               rq->dealloc_wqe = mlx5e_dealloc_rx_mpwqe;
 
                rq->mpwqe_stride_sz = BIT(priv->params.mpwqe_log_stride_sz);
                rq->mpwqe_num_strides = BIT(priv->params.mpwqe_log_num_strides);
@@ -321,6 +351,7 @@ static int mlx5e_create_rq(struct mlx5e_channel *c,
                }
                rq->handle_rx_cqe = mlx5e_handle_rx_cqe;
                rq->alloc_wqe = mlx5e_alloc_rx_wqe;
+               rq->dealloc_wqe = mlx5e_dealloc_rx_wqe;
 
                rq->wqe_sz = (priv->params.lro_en) ?
                                priv->params.lro_wqe_sz :
@@ -526,17 +557,25 @@ static int mlx5e_open_rq(struct mlx5e_channel *c,
 
 static void mlx5e_close_rq(struct mlx5e_rq *rq)
 {
+       int tout = 0;
+       int err;
+
        clear_bit(MLX5E_RQ_STATE_POST_WQES_ENABLE, &rq->state);
        napi_synchronize(&rq->channel->napi); /* prevent mlx5e_post_rx_wqes */
 
-       mlx5e_modify_rq_state(rq, MLX5_RQC_STATE_RDY, MLX5_RQC_STATE_ERR);
-       while (!mlx5_wq_ll_is_empty(&rq->wq))
-               msleep(20);
+       err = mlx5e_modify_rq_state(rq, MLX5_RQC_STATE_RDY, MLX5_RQC_STATE_ERR);
+       while (!mlx5_wq_ll_is_empty(&rq->wq) && !err &&
+              tout++ < MLX5_EN_QP_FLUSH_MAX_ITER)
+               msleep(MLX5_EN_QP_FLUSH_MSLEEP_QUANT);
+
+       if (err || tout == MLX5_EN_QP_FLUSH_MAX_ITER)
+               set_bit(MLX5E_RQ_STATE_FLUSH_TIMEOUT, &rq->state);
 
        /* avoid destroying rq before mlx5e_poll_rx_cq() is done with it */
        napi_synchronize(&rq->channel->napi);
 
        mlx5e_disable_rq(rq);
+       mlx5e_free_rx_descs(rq);
        mlx5e_destroy_rq(rq);
 }
 
@@ -580,7 +619,7 @@ static int mlx5e_create_sq(struct mlx5e_channel *c,
        void *sqc_wq = MLX5_ADDR_OF(sqc, sqc, wq);
        int err;
 
-       err = mlx5_alloc_map_uar(mdev, &sq->uar, true);
+       err = mlx5_alloc_map_uar(mdev, &sq->uar, !!MLX5_CAP_GEN(mdev, bf));
        if (err)
                return err;
 
@@ -783,6 +822,9 @@ static inline void netif_tx_disable_queue(struct netdev_queue *txq)
 
 static void mlx5e_close_sq(struct mlx5e_sq *sq)
 {
+       int tout = 0;
+       int err;
+
        if (sq->txq) {
                clear_bit(MLX5E_SQ_STATE_WAKE_TXQ_ENABLE, &sq->state);
                /* prevent netif_tx_wake_queue */
@@ -793,15 +835,24 @@ static void mlx5e_close_sq(struct mlx5e_sq *sq)
                if (mlx5e_sq_has_room_for(sq, 1))
                        mlx5e_send_nop(sq, true);
 
-               mlx5e_modify_sq(sq, MLX5_SQC_STATE_RDY, MLX5_SQC_STATE_ERR);
+               err = mlx5e_modify_sq(sq, MLX5_SQC_STATE_RDY,
+                                     MLX5_SQC_STATE_ERR);
+               if (err)
+                       set_bit(MLX5E_SQ_STATE_TX_TIMEOUT, &sq->state);
        }
 
-       while (sq->cc != sq->pc) /* wait till sq is empty */
-               msleep(20);
+       /* wait till sq is empty, unless a TX timeout occurred on this SQ */
+       while (sq->cc != sq->pc &&
+              !test_bit(MLX5E_SQ_STATE_TX_TIMEOUT, &sq->state)) {
+               msleep(MLX5_EN_QP_FLUSH_MSLEEP_QUANT);
+               if (tout++ > MLX5_EN_QP_FLUSH_MAX_ITER)
+                       set_bit(MLX5E_SQ_STATE_TX_TIMEOUT, &sq->state);
+       }
 
        /* avoid destroying sq before mlx5e_poll_tx_cq() is done with it */
        napi_synchronize(&sq->channel->napi);
 
+       mlx5e_free_tx_descs(sq);
        mlx5e_disable_sq(sq);
        mlx5e_destroy_sq(sq);
 }
@@ -1297,6 +1348,11 @@ static int mlx5e_open_channels(struct mlx5e_priv *priv)
                        goto err_close_channels;
        }
 
+       /* FIXME: This is a W/A for tx timeout watch dog false alarm when
+        * polling for inactive tx queues.
+        */
+       netif_tx_start_all_queues(priv->netdev);
+
        kfree(cparam);
        return 0;
 
@@ -1316,6 +1372,12 @@ static void mlx5e_close_channels(struct mlx5e_priv *priv)
 {
        int i;
 
+       /* FIXME: This is a W/A only for tx timeout watch dog false alarm when
+        * polling for inactive tx queues.
+        */
+       netif_tx_stop_all_queues(priv->netdev);
+       netif_tx_disable(priv->netdev);
+
        for (i = 0; i < priv->params.num_channels; i++)
                mlx5e_close_channel(priv->channel[i]);
 
@@ -1659,8 +1721,11 @@ static void mlx5e_netdev_set_tcs(struct net_device *netdev)
 
        netdev_set_num_tc(netdev, ntc);
 
+       /* Map netdev TCs to offset 0
+        * We have our own UP to TXQ mapping for QoS
+        */
        for (tc = 0; tc < ntc; tc++)
-               netdev_set_tc_queue(netdev, tc, nch, tc * nch);
+               netdev_set_tc_queue(netdev, tc, nch, 0);
 }
 
 int mlx5e_open_locked(struct net_device *netdev)
@@ -2591,6 +2656,29 @@ static netdev_features_t mlx5e_features_check(struct sk_buff *skb,
        return features;
 }
 
+static void mlx5e_tx_timeout(struct net_device *dev)
+{
+       struct mlx5e_priv *priv = netdev_priv(dev);
+       bool sched_work = false;
+       int i;
+
+       netdev_err(dev, "TX timeout detected\n");
+
+       for (i = 0; i < priv->params.num_channels * priv->params.num_tc; i++) {
+               struct mlx5e_sq *sq = priv->txq_to_sq_map[i];
+
+               if (!netif_xmit_stopped(netdev_get_tx_queue(dev, i)))
+                       continue;
+               sched_work = true;
+               set_bit(MLX5E_SQ_STATE_TX_TIMEOUT, &sq->state);
+               netdev_err(dev, "TX timeout on queue: %d, SQ: 0x%x, CQ: 0x%x, SQ Cons: 0x%x SQ Prod: 0x%x\n",
+                          i, sq->sqn, sq->cq.mcq.cqn, sq->cc, sq->pc);
+       }
+
+       if (sched_work && test_bit(MLX5E_STATE_OPENED, &priv->state))
+               schedule_work(&priv->tx_timeout_work);
+}
+
 static const struct net_device_ops mlx5e_netdev_ops_basic = {
        .ndo_open                = mlx5e_open,
        .ndo_stop                = mlx5e_close,
@@ -2608,6 +2696,7 @@ static const struct net_device_ops mlx5e_netdev_ops_basic = {
 #ifdef CONFIG_RFS_ACCEL
        .ndo_rx_flow_steer       = mlx5e_rx_flow_steer,
 #endif
+       .ndo_tx_timeout          = mlx5e_tx_timeout,
 };
 
 static const struct net_device_ops mlx5e_netdev_ops_sriov = {
@@ -2637,6 +2726,7 @@ static const struct net_device_ops mlx5e_netdev_ops_sriov = {
        .ndo_get_vf_config       = mlx5e_get_vf_config,
        .ndo_set_vf_link_state   = mlx5e_set_vf_link_state,
        .ndo_get_vf_stats        = mlx5e_get_vf_stats,
+       .ndo_tx_timeout          = mlx5e_tx_timeout,
 };
 
 static int mlx5e_check_required_hca_cap(struct mlx5_core_dev *mdev)
@@ -2839,6 +2929,7 @@ static void mlx5e_build_netdev_priv(struct mlx5_core_dev *mdev,
 
        INIT_WORK(&priv->update_carrier_work, mlx5e_update_carrier_work);
        INIT_WORK(&priv->set_rx_mode_work, mlx5e_set_rx_mode_work);
+       INIT_WORK(&priv->tx_timeout_work, mlx5e_tx_timeout_work);
        INIT_DELAYED_WORK(&priv->update_stats_work, mlx5e_update_stats_work);
 }
 
index bd947704b59c41747a71d75725a5d230bde5dfc0..9f2a16a507e04f8cd9861251ab3d0d5973b23e90 100644 (file)
@@ -212,6 +212,20 @@ int mlx5e_alloc_rx_wqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix)
        return -ENOMEM;
 }
 
+void mlx5e_dealloc_rx_wqe(struct mlx5e_rq *rq, u16 ix)
+{
+       struct sk_buff *skb = rq->skb[ix];
+
+       if (skb) {
+               rq->skb[ix] = NULL;
+               dma_unmap_single(rq->pdev,
+                                *((dma_addr_t *)skb->cb),
+                                rq->wqe_sz,
+                                DMA_FROM_DEVICE);
+               dev_kfree_skb(skb);
+       }
+}
+
 static inline int mlx5e_mpwqe_strides_per_page(struct mlx5e_rq *rq)
 {
        return rq->mpwqe_num_strides >> MLX5_MPWRQ_WQE_PAGE_ORDER;
@@ -574,6 +588,30 @@ int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix)
        return 0;
 }
 
+void mlx5e_dealloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix)
+{
+       struct mlx5e_mpw_info *wi = &rq->wqe_info[ix];
+
+       wi->free_wqe(rq, wi);
+}
+
+void mlx5e_free_rx_descs(struct mlx5e_rq *rq)
+{
+       struct mlx5_wq_ll *wq = &rq->wq;
+       struct mlx5e_rx_wqe *wqe;
+       __be16 wqe_ix_be;
+       u16 wqe_ix;
+
+       while (!mlx5_wq_ll_is_empty(wq)) {
+               wqe_ix_be = *wq->tail_next;
+               wqe_ix    = be16_to_cpu(wqe_ix_be);
+               wqe       = mlx5_wq_ll_get_wqe(&rq->wq, wqe_ix);
+               rq->dealloc_wqe(rq, wqe_ix);
+               mlx5_wq_ll_pop(&rq->wq, wqe_ix_be,
+                              &wqe->next.next_wqe_index);
+       }
+}
+
 #define RQ_CANNOT_POST(rq) \
                (!test_bit(MLX5E_RQ_STATE_POST_WQES_ENABLE, &rq->state) || \
                 test_bit(MLX5E_RQ_STATE_UMR_WQE_IN_PROGRESS, &rq->state))
@@ -689,7 +727,7 @@ static inline void mlx5e_handle_csum(struct net_device *netdev,
        if (is_first_ethertype_ip(skb)) {
                skb->ip_summed = CHECKSUM_COMPLETE;
                skb->csum = csum_unfold((__force __sum16)cqe->check_sum);
-               rq->stats.csum_sw++;
+               rq->stats.csum_complete++;
                return;
        }
 
@@ -699,7 +737,7 @@ static inline void mlx5e_handle_csum(struct net_device *netdev,
                if (cqe_is_tunneled(cqe)) {
                        skb->csum_level = 1;
                        skb->encapsulation = 1;
-                       rq->stats.csum_inner++;
+                       rq->stats.csum_unnecessary_inner++;
                }
                return;
        }
@@ -878,6 +916,9 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget)
        struct mlx5e_rq *rq = container_of(cq, struct mlx5e_rq, cq);
        int work_done = 0;
 
+       if (unlikely(test_bit(MLX5E_RQ_STATE_FLUSH_TIMEOUT, &rq->state)))
+               return 0;
+
        if (cq->decmprs_left)
                work_done += mlx5e_decompress_cqes_cont(rq, cq, 0, budget);
 
index 83bc32b25849caae0b9422688e09fba1a3928fe2..fcd490cc56109d2279444db47131bb31432f923b 100644 (file)
        be64_to_cpu(*(__be32 *)((char *)ptr + dsc[i].offset))
 
 #define MLX5E_DECLARE_STAT(type, fld) #fld, offsetof(type, fld)
+#define MLX5E_DECLARE_RX_STAT(type, fld) "rx%d_"#fld, offsetof(type, fld)
+#define MLX5E_DECLARE_TX_STAT(type, fld) "tx%d_"#fld, offsetof(type, fld)
 
 struct counter_desc {
-       char            name[ETH_GSTRING_LEN];
+       char            format[ETH_GSTRING_LEN];
        int             offset; /* Byte offset */
 };
 
@@ -53,18 +55,18 @@ struct mlx5e_sw_stats {
        u64 rx_bytes;
        u64 tx_packets;
        u64 tx_bytes;
-       u64 tso_packets;
-       u64 tso_bytes;
-       u64 tso_inner_packets;
-       u64 tso_inner_bytes;
-       u64 lro_packets;
-       u64 lro_bytes;
-       u64 rx_csum_good;
+       u64 tx_tso_packets;
+       u64 tx_tso_bytes;
+       u64 tx_tso_inner_packets;
+       u64 tx_tso_inner_bytes;
+       u64 rx_lro_packets;
+       u64 rx_lro_bytes;
+       u64 rx_csum_unnecessary;
        u64 rx_csum_none;
-       u64 rx_csum_sw;
-       u64 rx_csum_inner;
-       u64 tx_csum_offload;
-       u64 tx_csum_inner;
+       u64 rx_csum_complete;
+       u64 rx_csum_unnecessary_inner;
+       u64 tx_csum_partial;
+       u64 tx_csum_partial_inner;
        u64 tx_queue_stopped;
        u64 tx_queue_wake;
        u64 tx_queue_dropped;
@@ -76,7 +78,7 @@ struct mlx5e_sw_stats {
        u64 rx_cqe_compress_pkts;
 
        /* Special handling counters */
-       u64 link_down_events;
+       u64 link_down_events_phy;
 };
 
 static const struct counter_desc sw_stats_desc[] = {
@@ -84,18 +86,18 @@ static const struct counter_desc sw_stats_desc[] = {
        { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_bytes) },
        { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_packets) },
        { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_bytes) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tso_packets) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tso_bytes) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tso_inner_packets) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tso_inner_bytes) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, lro_packets) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, lro_bytes) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_csum_good) },
+       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_tso_packets) },
+       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_tso_bytes) },
+       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_tso_inner_packets) },
+       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_tso_inner_bytes) },
+       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_lro_packets) },
+       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_lro_bytes) },
+       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_csum_unnecessary) },
        { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_csum_none) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_csum_sw) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_csum_inner) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_csum_offload) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_csum_inner) },
+       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_csum_complete) },
+       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_csum_unnecessary_inner) },
+       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_csum_partial) },
+       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_csum_partial_inner) },
        { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_queue_stopped) },
        { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_queue_wake) },
        { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, tx_queue_dropped) },
@@ -105,7 +107,7 @@ static const struct counter_desc sw_stats_desc[] = {
        { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_buff_alloc_err) },
        { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_cqe_compress_blks) },
        { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_cqe_compress_pkts) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, link_down_events) },
+       { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, link_down_events_phy) },
 };
 
 struct mlx5e_qcounter_stats {
@@ -125,12 +127,6 @@ struct mlx5e_vport_stats {
 };
 
 static const struct counter_desc vport_stats_desc[] = {
-       { "rx_vport_error_packets",
-               VPORT_COUNTER_OFF(received_errors.packets) },
-       { "rx_vport_error_bytes", VPORT_COUNTER_OFF(received_errors.octets) },
-       { "tx_vport_error_packets",
-               VPORT_COUNTER_OFF(transmit_errors.packets) },
-       { "tx_vport_error_bytes", VPORT_COUNTER_OFF(transmit_errors.octets) },
        { "rx_vport_unicast_packets",
                VPORT_COUNTER_OFF(received_eth_unicast.packets) },
        { "rx_vport_unicast_bytes",
@@ -192,94 +188,68 @@ struct mlx5e_pport_stats {
 };
 
 static const struct counter_desc pport_802_3_stats_desc[] = {
-       { "frames_tx", PPORT_802_3_OFF(a_frames_transmitted_ok) },
-       { "frames_rx", PPORT_802_3_OFF(a_frames_received_ok) },
-       { "check_seq_err", PPORT_802_3_OFF(a_frame_check_sequence_errors) },
-       { "alignment_err", PPORT_802_3_OFF(a_alignment_errors) },
-       { "octets_tx", PPORT_802_3_OFF(a_octets_transmitted_ok) },
-       { "octets_received", PPORT_802_3_OFF(a_octets_received_ok) },
-       { "multicast_xmitted", PPORT_802_3_OFF(a_multicast_frames_xmitted_ok) },
-       { "broadcast_xmitted", PPORT_802_3_OFF(a_broadcast_frames_xmitted_ok) },
-       { "multicast_rx", PPORT_802_3_OFF(a_multicast_frames_received_ok) },
-       { "broadcast_rx", PPORT_802_3_OFF(a_broadcast_frames_received_ok) },
-       { "in_range_len_errors", PPORT_802_3_OFF(a_in_range_length_errors) },
-       { "out_of_range_len", PPORT_802_3_OFF(a_out_of_range_length_field) },
-       { "too_long_errors", PPORT_802_3_OFF(a_frame_too_long_errors) },
-       { "symbol_err", PPORT_802_3_OFF(a_symbol_error_during_carrier) },
-       { "mac_control_tx", PPORT_802_3_OFF(a_mac_control_frames_transmitted) },
-       { "mac_control_rx", PPORT_802_3_OFF(a_mac_control_frames_received) },
-       { "unsupported_op_rx",
-               PPORT_802_3_OFF(a_unsupported_opcodes_received) },
-       { "pause_ctrl_rx", PPORT_802_3_OFF(a_pause_mac_ctrl_frames_received) },
-       { "pause_ctrl_tx",
-               PPORT_802_3_OFF(a_pause_mac_ctrl_frames_transmitted) },
+       { "tx_packets_phy", PPORT_802_3_OFF(a_frames_transmitted_ok) },
+       { "rx_packets_phy", PPORT_802_3_OFF(a_frames_received_ok) },
+       { "rx_crc_errors_phy", PPORT_802_3_OFF(a_frame_check_sequence_errors) },
+       { "tx_bytes_phy", PPORT_802_3_OFF(a_octets_transmitted_ok) },
+       { "rx_bytes_phy", PPORT_802_3_OFF(a_octets_received_ok) },
+       { "tx_multicast_phy", PPORT_802_3_OFF(a_multicast_frames_xmitted_ok) },
+       { "tx_broadcast_phy", PPORT_802_3_OFF(a_broadcast_frames_xmitted_ok) },
+       { "rx_multicast_phy", PPORT_802_3_OFF(a_multicast_frames_received_ok) },
+       { "rx_broadcast_phy", PPORT_802_3_OFF(a_broadcast_frames_received_ok) },
+       { "rx_in_range_len_errors_phy", PPORT_802_3_OFF(a_in_range_length_errors) },
+       { "rx_out_of_range_len_phy", PPORT_802_3_OFF(a_out_of_range_length_field) },
+       { "rx_oversize_pkts_phy", PPORT_802_3_OFF(a_frame_too_long_errors) },
+       { "rx_symbol_err_phy", PPORT_802_3_OFF(a_symbol_error_during_carrier) },
+       { "tx_mac_control_phy", PPORT_802_3_OFF(a_mac_control_frames_transmitted) },
+       { "rx_mac_control_phy", PPORT_802_3_OFF(a_mac_control_frames_received) },
+       { "rx_unsupported_op_phy", PPORT_802_3_OFF(a_unsupported_opcodes_received) },
+       { "rx_pause_ctrl_phy", PPORT_802_3_OFF(a_pause_mac_ctrl_frames_received) },
+       { "tx_pause_ctrl_phy", PPORT_802_3_OFF(a_pause_mac_ctrl_frames_transmitted) },
 };
 
 static const struct counter_desc pport_2863_stats_desc[] = {
-       { "in_octets", PPORT_2863_OFF(if_in_octets) },
-       { "in_ucast_pkts", PPORT_2863_OFF(if_in_ucast_pkts) },
-       { "in_discards", PPORT_2863_OFF(if_in_discards) },
-       { "in_errors", PPORT_2863_OFF(if_in_errors) },
-       { "in_unknown_protos", PPORT_2863_OFF(if_in_unknown_protos) },
-       { "out_octets", PPORT_2863_OFF(if_out_octets) },
-       { "out_ucast_pkts", PPORT_2863_OFF(if_out_ucast_pkts) },
-       { "out_discards", PPORT_2863_OFF(if_out_discards) },
-       { "out_errors", PPORT_2863_OFF(if_out_errors) },
-       { "in_multicast_pkts", PPORT_2863_OFF(if_in_multicast_pkts) },
-       { "in_broadcast_pkts", PPORT_2863_OFF(if_in_broadcast_pkts) },
-       { "out_multicast_pkts", PPORT_2863_OFF(if_out_multicast_pkts) },
-       { "out_broadcast_pkts", PPORT_2863_OFF(if_out_broadcast_pkts) },
+       { "rx_discards_phy", PPORT_2863_OFF(if_in_discards) },
+       { "tx_discards_phy", PPORT_2863_OFF(if_out_discards) },
+       { "tx_errors_phy", PPORT_2863_OFF(if_out_errors) },
 };
 
 static const struct counter_desc pport_2819_stats_desc[] = {
-       { "drop_events", PPORT_2819_OFF(ether_stats_drop_events) },
-       { "octets", PPORT_2819_OFF(ether_stats_octets) },
-       { "pkts", PPORT_2819_OFF(ether_stats_pkts) },
-       { "broadcast_pkts", PPORT_2819_OFF(ether_stats_broadcast_pkts) },
-       { "multicast_pkts", PPORT_2819_OFF(ether_stats_multicast_pkts) },
-       { "crc_align_errors", PPORT_2819_OFF(ether_stats_crc_align_errors) },
-       { "undersize_pkts", PPORT_2819_OFF(ether_stats_undersize_pkts) },
-       { "oversize_pkts", PPORT_2819_OFF(ether_stats_oversize_pkts) },
-       { "fragments", PPORT_2819_OFF(ether_stats_fragments) },
-       { "jabbers", PPORT_2819_OFF(ether_stats_jabbers) },
-       { "collisions", PPORT_2819_OFF(ether_stats_collisions) },
-       { "p64octets", PPORT_2819_OFF(ether_stats_pkts64octets) },
-       { "p65to127octets", PPORT_2819_OFF(ether_stats_pkts65to127octets) },
-       { "p128to255octets", PPORT_2819_OFF(ether_stats_pkts128to255octets) },
-       { "p256to511octets", PPORT_2819_OFF(ether_stats_pkts256to511octets) },
-       { "p512to1023octets", PPORT_2819_OFF(ether_stats_pkts512to1023octets) },
-       { "p1024to1518octets",
-               PPORT_2819_OFF(ether_stats_pkts1024to1518octets) },
-       { "p1519to2047octets",
-               PPORT_2819_OFF(ether_stats_pkts1519to2047octets) },
-       { "p2048to4095octets",
-               PPORT_2819_OFF(ether_stats_pkts2048to4095octets) },
-       { "p4096to8191octets",
-               PPORT_2819_OFF(ether_stats_pkts4096to8191octets) },
-       { "p8192to10239octets",
-               PPORT_2819_OFF(ether_stats_pkts8192to10239octets) },
+       { "rx_undersize_pkts_phy", PPORT_2819_OFF(ether_stats_undersize_pkts) },
+       { "rx_fragments_phy", PPORT_2819_OFF(ether_stats_fragments) },
+       { "rx_jabbers_phy", PPORT_2819_OFF(ether_stats_jabbers) },
+       { "rx_64_bytes_phy", PPORT_2819_OFF(ether_stats_pkts64octets) },
+       { "rx_65_to_127_bytes_phy", PPORT_2819_OFF(ether_stats_pkts65to127octets) },
+       { "rx_128_to_255_bytes_phy", PPORT_2819_OFF(ether_stats_pkts128to255octets) },
+       { "rx_256_to_511_bytes_phy", PPORT_2819_OFF(ether_stats_pkts256to511octets) },
+       { "rx_512_to_1023_bytes_phy", PPORT_2819_OFF(ether_stats_pkts512to1023octets) },
+       { "rx_1024_to_1518_bytes_phy", PPORT_2819_OFF(ether_stats_pkts1024to1518octets) },
+       { "rx_1519_to_2047_bytes_phy", PPORT_2819_OFF(ether_stats_pkts1519to2047octets) },
+       { "rx_2048_to_4095_bytes_phy", PPORT_2819_OFF(ether_stats_pkts2048to4095octets) },
+       { "rx_4096_to_8191_bytes_phy", PPORT_2819_OFF(ether_stats_pkts4096to8191octets) },
+       { "rx_8192_to_10239_bytes_phy", PPORT_2819_OFF(ether_stats_pkts8192to10239octets) },
 };
 
 static const struct counter_desc pport_per_prio_traffic_stats_desc[] = {
-       { "rx_octets", PPORT_PER_PRIO_OFF(rx_octets) },
-       { "rx_frames", PPORT_PER_PRIO_OFF(rx_frames) },
-       { "tx_octets", PPORT_PER_PRIO_OFF(tx_octets) },
-       { "tx_frames", PPORT_PER_PRIO_OFF(tx_frames) },
+       { "rx_prio%d_bytes", PPORT_PER_PRIO_OFF(rx_octets) },
+       { "rx_prio%d_packets", PPORT_PER_PRIO_OFF(rx_frames) },
+       { "tx_prio%d_bytes", PPORT_PER_PRIO_OFF(tx_octets) },
+       { "tx_prio%d_packets", PPORT_PER_PRIO_OFF(tx_frames) },
 };
 
 static const struct counter_desc pport_per_prio_pfc_stats_desc[] = {
-       { "rx_pause", PPORT_PER_PRIO_OFF(rx_pause) },
-       { "rx_pause_duration", PPORT_PER_PRIO_OFF(rx_pause_duration) },
-       { "tx_pause", PPORT_PER_PRIO_OFF(tx_pause) },
-       { "tx_pause_duration", PPORT_PER_PRIO_OFF(tx_pause_duration) },
-       { "rx_pause_transition", PPORT_PER_PRIO_OFF(rx_pause_transition) },
+       { "rx_prio%d_pause", PPORT_PER_PRIO_OFF(rx_pause) },
+       { "rx_prio%d_pause_duration", PPORT_PER_PRIO_OFF(rx_pause_duration) },
+       { "tx_prio%d_pause", PPORT_PER_PRIO_OFF(tx_pause) },
+       { "tx_prio%d_pause_duration", PPORT_PER_PRIO_OFF(tx_pause_duration) },
+       { "rx_prio%d_pause_transition", PPORT_PER_PRIO_OFF(rx_pause_transition) },
 };
 
 struct mlx5e_rq_stats {
        u64 packets;
        u64 bytes;
-       u64 csum_sw;
-       u64 csum_inner;
+       u64 csum_complete;
+       u64 csum_unnecessary_inner;
        u64 csum_none;
        u64 lro_packets;
        u64 lro_bytes;
@@ -292,19 +262,19 @@ struct mlx5e_rq_stats {
 };
 
 static const struct counter_desc rq_stats_desc[] = {
-       { MLX5E_DECLARE_STAT(struct mlx5e_rq_stats, packets) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_rq_stats, bytes) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_rq_stats, csum_sw) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_rq_stats, csum_inner) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_rq_stats, csum_none) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_rq_stats, lro_packets) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_rq_stats, lro_bytes) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_rq_stats, wqe_err) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_rq_stats, mpwqe_filler) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_rq_stats, mpwqe_frag) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_rq_stats, buff_alloc_err) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_rq_stats, cqe_compress_blks) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_rq_stats, cqe_compress_pkts) },
+       { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, packets) },
+       { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, bytes) },
+       { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, csum_complete) },
+       { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, csum_unnecessary_inner) },
+       { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, csum_none) },
+       { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, lro_packets) },
+       { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, lro_bytes) },
+       { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, wqe_err) },
+       { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, mpwqe_filler) },
+       { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, mpwqe_frag) },
+       { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, buff_alloc_err) },
+       { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, cqe_compress_blks) },
+       { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, cqe_compress_pkts) },
 };
 
 struct mlx5e_sq_stats {
@@ -315,28 +285,28 @@ struct mlx5e_sq_stats {
        u64 tso_bytes;
        u64 tso_inner_packets;
        u64 tso_inner_bytes;
-       u64 csum_offload_inner;
+       u64 csum_partial_inner;
        u64 nop;
        /* less likely accessed in data path */
-       u64 csum_offload_none;
+       u64 csum_none;
        u64 stopped;
        u64 wake;
        u64 dropped;
 };
 
 static const struct counter_desc sq_stats_desc[] = {
-       { MLX5E_DECLARE_STAT(struct mlx5e_sq_stats, packets) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_sq_stats, bytes) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_sq_stats, tso_packets) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_sq_stats, tso_bytes) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_sq_stats, tso_inner_packets) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_sq_stats, tso_inner_bytes) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_sq_stats, csum_offload_inner) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_sq_stats, nop) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_sq_stats, csum_offload_none) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_sq_stats, stopped) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_sq_stats, wake) },
-       { MLX5E_DECLARE_STAT(struct mlx5e_sq_stats, dropped) },
+       { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, packets) },
+       { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, bytes) },
+       { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, tso_packets) },
+       { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, tso_bytes) },
+       { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, tso_inner_packets) },
+       { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, tso_inner_bytes) },
+       { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, csum_partial_inner) },
+       { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, nop) },
+       { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, csum_none) },
+       { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, stopped) },
+       { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, wake) },
+       { MLX5E_DECLARE_TX_STAT(struct mlx5e_sq_stats, dropped) },
 };
 
 #define NUM_SW_COUNTERS                        ARRAY_SIZE(sw_stats_desc)
index b000ddc29553bb40e297f075c1a3f7606487a87e..5740b465ef8430f359ed218adf9f93bce9c46e8a 100644 (file)
@@ -110,8 +110,20 @@ u16 mlx5e_select_queue(struct net_device *dev, struct sk_buff *skb,
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
        int channel_ix = fallback(dev, skb);
-       int up = (netdev_get_num_tc(dev) && skb_vlan_tag_present(skb)) ?
-                skb->vlan_tci >> VLAN_PRIO_SHIFT : 0;
+       int up = 0;
+
+       if (!netdev_get_num_tc(dev))
+               return channel_ix;
+
+       if (skb_vlan_tag_present(skb))
+               up = skb->vlan_tci >> VLAN_PRIO_SHIFT;
+
+       /* channel_ix can be larger than num_channels since
+        * dev->num_real_tx_queues = num_channels * num_tc
+        */
+       if (channel_ix >= priv->params.num_channels)
+               channel_ix = reciprocal_scale(channel_ix,
+                                             priv->params.num_channels);
 
        return priv->channeltc_to_txq_map[channel_ix][up];
 }
@@ -123,7 +135,7 @@ static inline u16 mlx5e_get_inline_hdr_size(struct mlx5e_sq *sq,
         * headers and occur before the data gather.
         * Therefore these headers must be copied into the WQE
         */
-#define MLX5E_MIN_INLINE ETH_HLEN
+#define MLX5E_MIN_INLINE (ETH_HLEN + VLAN_HLEN)
 
        if (bf) {
                u16 ihs = skb_headlen(skb);
@@ -135,7 +147,7 @@ static inline u16 mlx5e_get_inline_hdr_size(struct mlx5e_sq *sq,
                        return skb_headlen(skb);
        }
 
-       return MLX5E_MIN_INLINE;
+       return max(skb_network_offset(skb), MLX5E_MIN_INLINE);
 }
 
 static inline void mlx5e_tx_skb_pull_inline(unsigned char **skb_data,
@@ -192,12 +204,12 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_sq *sq, struct sk_buff *skb)
                if (skb->encapsulation) {
                        eseg->cs_flags |= MLX5_ETH_WQE_L3_INNER_CSUM |
                                          MLX5_ETH_WQE_L4_INNER_CSUM;
-                       sq->stats.csum_offload_inner++;
+                       sq->stats.csum_partial_inner++;
                } else {
                        eseg->cs_flags |= MLX5_ETH_WQE_L4_CSUM;
                }
        } else
-               sq->stats.csum_offload_none++;
+               sq->stats.csum_none++;
 
        if (sq->cc != sq->prev_cc) {
                sq->prev_cc = sq->cc;
@@ -341,6 +353,35 @@ netdev_tx_t mlx5e_xmit(struct sk_buff *skb, struct net_device *dev)
        return mlx5e_sq_xmit(sq, skb);
 }
 
+void mlx5e_free_tx_descs(struct mlx5e_sq *sq)
+{
+       struct mlx5e_tx_wqe_info *wi;
+       struct sk_buff *skb;
+       u16 ci;
+       int i;
+
+       while (sq->cc != sq->pc) {
+               ci = sq->cc & sq->wq.sz_m1;
+               skb = sq->skb[ci];
+               wi = &sq->wqe_info[ci];
+
+               if (!skb) { /* nop */
+                       sq->cc++;
+                       continue;
+               }
+
+               for (i = 0; i < wi->num_dma; i++) {
+                       struct mlx5e_sq_dma *dma =
+                               mlx5e_dma_get(sq, sq->dma_fifo_cc++);
+
+                       mlx5e_tx_dma_unmap(sq->pdev, dma);
+               }
+
+               dev_kfree_skb_any(skb);
+               sq->cc += wi->num_wqebbs;
+       }
+}
+
 bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget)
 {
        struct mlx5e_sq *sq;
@@ -352,6 +393,9 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget)
 
        sq = container_of(cq, struct mlx5e_sq, cq);
 
+       if (unlikely(test_bit(MLX5E_SQ_STATE_TX_TIMEOUT, &sq->state)))
+               return false;
+
        npkts = 0;
        nbytes = 0;
 
index 42d16b9458e485205a344ab9a988caf7ae185632..96a59463ae65f1773856949b17a9224531cfe930 100644 (file)
@@ -108,15 +108,21 @@ static int in_fatal(struct mlx5_core_dev *dev)
 
 void mlx5_enter_error_state(struct mlx5_core_dev *dev)
 {
+       mutex_lock(&dev->intf_state_mutex);
        if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR)
-               return;
+               goto unlock;
 
        mlx5_core_err(dev, "start\n");
-       if (pci_channel_offline(dev->pdev) || in_fatal(dev))
+       if (pci_channel_offline(dev->pdev) || in_fatal(dev)) {
                dev->state = MLX5_DEVICE_STATE_INTERNAL_ERROR;
+               trigger_cmd_completions(dev);
+       }
 
        mlx5_core_event(dev, MLX5_DEV_EVENT_SYS_ERROR, 0);
        mlx5_core_err(dev, "end\n");
+
+unlock:
+       mutex_unlock(&dev->intf_state_mutex);
 }
 
 static void mlx5_handle_bad_state(struct mlx5_core_dev *dev)
@@ -245,7 +251,6 @@ static void poll_health(unsigned long data)
        u32 count;
 
        if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) {
-               trigger_cmd_completions(dev);
                mod_timer(&health->timer, get_next_poll_jiffies());
                return;
        }
index a19b59348dd685816c88736a4d9e47f78b3f847c..6695893ddd2d407743e8329959a05840c28ad3f1 100644 (file)
@@ -1422,46 +1422,31 @@ void mlx5_disable_device(struct mlx5_core_dev *dev)
        mlx5_pci_err_detected(dev->pdev, 0);
 }
 
-/* wait for the device to show vital signs. For now we check
- * that we can read the device ID and that the health buffer
- * shows a non zero value which is different than 0xffffffff
+/* wait for the device to show vital signs by waiting
+ * for the health counter to start counting.
  */
-static void wait_vital(struct pci_dev *pdev)
+static int wait_vital(struct pci_dev *pdev)
 {
        struct mlx5_core_dev *dev = pci_get_drvdata(pdev);
        struct mlx5_core_health *health = &dev->priv.health;
        const int niter = 100;
+       u32 last_count = 0;
        u32 count;
-       u16 did;
        int i;
 
-       /* Wait for firmware to be ready after reset */
-       msleep(1000);
-       for (i = 0; i < niter; i++) {
-               if (pci_read_config_word(pdev, 2, &did)) {
-                       dev_warn(&pdev->dev, "failed reading config word\n");
-                       break;
-               }
-               if (did == pdev->device) {
-                       dev_info(&pdev->dev, "device ID correctly read after %d iterations\n", i);
-                       break;
-               }
-               msleep(50);
-       }
-       if (i == niter)
-               dev_warn(&pdev->dev, "%s-%d: could not read device ID\n", __func__, __LINE__);
-
        for (i = 0; i < niter; i++) {
                count = ioread32be(health->health_counter);
                if (count && count != 0xffffffff) {
-                       dev_info(&pdev->dev, "Counter value 0x%x after %d iterations\n", count, i);
-                       break;
+                       if (last_count && last_count != count) {
+                               dev_info(&pdev->dev, "Counter value 0x%x after %d iterations\n", count, i);
+                               return 0;
+                       }
+                       last_count = count;
                }
                msleep(50);
        }
 
-       if (i == niter)
-               dev_warn(&pdev->dev, "%s-%d: could not read device ID\n", __func__, __LINE__);
+       return -ETIMEDOUT;
 }
 
 static void mlx5_pci_resume(struct pci_dev *pdev)
@@ -1473,7 +1458,11 @@ static void mlx5_pci_resume(struct pci_dev *pdev)
        dev_info(&pdev->dev, "%s was called\n", __func__);
 
        pci_save_state(pdev);
-       wait_vital(pdev);
+       err = wait_vital(pdev);
+       if (err) {
+               dev_err(&pdev->dev, "%s: wait_vital timed out\n", __func__);
+               return;
+       }
 
        err = mlx5_load_one(dev, priv);
        if (err)
@@ -1508,8 +1497,9 @@ static const struct pci_device_id mlx5_core_pci_table[] = {
        { PCI_VDEVICE(MELLANOX, 0x1014), MLX5_PCI_DEV_IS_VF},   /* ConnectX-4 VF */
        { PCI_VDEVICE(MELLANOX, 0x1015) },                      /* ConnectX-4LX */
        { PCI_VDEVICE(MELLANOX, 0x1016), MLX5_PCI_DEV_IS_VF},   /* ConnectX-4LX VF */
-       { PCI_VDEVICE(MELLANOX, 0x1017) },                      /* ConnectX-5 */
+       { PCI_VDEVICE(MELLANOX, 0x1017) },                      /* ConnectX-5, PCIe 3.0 */
        { PCI_VDEVICE(MELLANOX, 0x1018), MLX5_PCI_DEV_IS_VF},   /* ConnectX-5 VF */
+       { PCI_VDEVICE(MELLANOX, 0x1019) },                      /* ConnectX-5, PCIe 4.0 */
        { 0, }
 };
 
index 9eeee0545f1cf294ecddb7bb0becc873df8906da..32dea3524cee3ed4d984eb5aafc7a06abdbd713b 100644 (file)
@@ -345,7 +345,6 @@ static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages,
                               func_id, npages, err);
                goto out_4k;
        }
-       dev->priv.fw_pages += npages;
 
        err = mlx5_cmd_status_to_err(&out.hdr);
        if (err) {
@@ -373,6 +372,33 @@ static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages,
        return err;
 }
 
+static int reclaim_pages_cmd(struct mlx5_core_dev *dev,
+                            struct mlx5_manage_pages_inbox *in, int in_size,
+                            struct mlx5_manage_pages_outbox *out, int out_size)
+{
+       struct fw_page *fwp;
+       struct rb_node *p;
+       u32 npages;
+       u32 i = 0;
+
+       if (dev->state != MLX5_DEVICE_STATE_INTERNAL_ERROR)
+               return mlx5_cmd_exec_check_status(dev, (u32 *)in, in_size,
+                                                 (u32 *)out, out_size);
+
+       npages = be32_to_cpu(in->num_entries);
+
+       p = rb_first(&dev->priv.page_root);
+       while (p && i < npages) {
+               fwp = rb_entry(p, struct fw_page, rb_node);
+               out->pas[i] = cpu_to_be64(fwp->addr);
+               p = rb_next(p);
+               i++;
+       }
+
+       out->num_entries = cpu_to_be32(i);
+       return 0;
+}
+
 static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages,
                         int *nclaimed)
 {
@@ -398,15 +424,9 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages,
        in.func_id = cpu_to_be16(func_id);
        in.num_entries = cpu_to_be32(npages);
        mlx5_core_dbg(dev, "npages %d, outlen %d\n", npages, outlen);
-       err = mlx5_cmd_exec(dev, &in, sizeof(in), out, outlen);
+       err = reclaim_pages_cmd(dev, &in, sizeof(in), out, outlen);
        if (err) {
-               mlx5_core_err(dev, "failed reclaiming pages\n");
-               goto out_free;
-       }
-       dev->priv.fw_pages -= npages;
-
-       if (out->hdr.status) {
-               err = mlx5_cmd_status_to_err(&out->hdr);
+               mlx5_core_err(dev, "failed reclaiming pages: err %d\n", err);
                goto out_free;
        }
 
@@ -417,13 +437,15 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages,
                err = -EINVAL;
                goto out_free;
        }
-       if (nclaimed)
-               *nclaimed = num_claimed;
 
        for (i = 0; i < num_claimed; i++) {
                addr = be64_to_cpu(out->pas[i]);
                free_4k(dev, addr);
        }
+
+       if (nclaimed)
+               *nclaimed = num_claimed;
+
        dev->priv.fw_pages -= num_claimed;
        if (func_id)
                dev->priv.vfs_pages -= num_claimed;
@@ -514,14 +536,10 @@ int mlx5_reclaim_startup_pages(struct mlx5_core_dev *dev)
                p = rb_first(&dev->priv.page_root);
                if (p) {
                        fwp = rb_entry(p, struct fw_page, rb_node);
-                       if (dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) {
-                               free_4k(dev, fwp->addr);
-                               nclaimed = 1;
-                       } else {
-                               err = reclaim_pages(dev, fwp->func_id,
-                                                   optimal_reclaimed_pages(),
-                                                   &nclaimed);
-                       }
+                       err = reclaim_pages(dev, fwp->func_id,
+                                           optimal_reclaimed_pages(),
+                                           &nclaimed);
+
                        if (err) {
                                mlx5_core_warn(dev, "failed reclaiming pages (%d)\n",
                                               err);
@@ -536,6 +554,13 @@ int mlx5_reclaim_startup_pages(struct mlx5_core_dev *dev)
                }
        } while (p);
 
+       WARN(dev->priv.fw_pages,
+            "FW pages counter is %d after reclaiming all pages\n",
+            dev->priv.fw_pages);
+       WARN(dev->priv.vfs_pages,
+            "VFs FW pages counter is %d after reclaiming all pages\n",
+            dev->priv.vfs_pages);
+
        return 0;
 }
 
index daf44cd4c566f45649b9efe1e4417b4e868c9048..91846dfcbe9cf1b39f0fbf6af8856ad4771dd1ff 100644 (file)
@@ -513,7 +513,6 @@ int mlx5_modify_nic_vport_node_guid(struct mlx5_core_dev *mdev,
 {
        int inlen = MLX5_ST_SZ_BYTES(modify_nic_vport_context_in);
        void *nic_vport_context;
-       u8 *guid;
        void *in;
        int err;
 
@@ -535,8 +534,6 @@ int mlx5_modify_nic_vport_node_guid(struct mlx5_core_dev *mdev,
 
        nic_vport_context = MLX5_ADDR_OF(modify_nic_vport_context_in,
                                         in, nic_vport_context);
-       guid = MLX5_ADDR_OF(nic_vport_context, nic_vport_context,
-                           node_guid);
        MLX5_SET64(nic_vport_context, nic_vport_context, node_guid, node_guid);
 
        err = mlx5_modify_nic_vport_context(mdev, in, inlen);
index f2fd1ef16da7eba68deb5af1648b7ff28cf7ff44..e25a73ed2981848efa6175a6eeb43ad6891df1ec 100644 (file)
@@ -72,8 +72,8 @@ static int mlx5e_vxlan_core_del_port_cmd(struct mlx5_core_dev *mdev, u16 port)
        u32 in[MLX5_ST_SZ_DW(delete_vxlan_udp_dport_in)];
        u32 out[MLX5_ST_SZ_DW(delete_vxlan_udp_dport_out)];
 
-       memset(&in, 0, sizeof(in));
-       memset(&out, 0, sizeof(out));
+       memset(in, 0, sizeof(in));
+       memset(out, 0, sizeof(out));
 
        MLX5_SET(delete_vxlan_udp_dport_in, in, opcode,
                 MLX5_CMD_OP_DELETE_VXLAN_UDP_DPORT);
@@ -105,6 +105,9 @@ static void mlx5e_vxlan_add_port(struct work_struct *work)
        struct mlx5e_vxlan *vxlan;
        int err;
 
+       if (mlx5e_vxlan_lookup_port(priv, port))
+               goto free_work;
+
        if (mlx5e_vxlan_core_add_port_cmd(priv->mdev, port))
                goto free_work;
 
index ce21ee5b23577ee63e8b5679a1fe2204c3ccd895..821a087c7ae221200f1a79c54ef399910325f644 100644 (file)
@@ -75,14 +75,14 @@ int mlx5_wq_cyc_create(struct mlx5_core_dev *mdev, struct mlx5_wq_param *param,
 
        err = mlx5_db_alloc_node(mdev, &wq_ctrl->db, param->db_numa_node);
        if (err) {
-               mlx5_core_warn(mdev, "mlx5_db_alloc() failed, %d\n", err);
+               mlx5_core_warn(mdev, "mlx5_db_alloc_node() failed, %d\n", err);
                return err;
        }
 
        err = mlx5_buf_alloc_node(mdev, mlx5_wq_cyc_get_byte_size(wq),
                                  &wq_ctrl->buf, param->buf_numa_node);
        if (err) {
-               mlx5_core_warn(mdev, "mlx5_buf_alloc() failed, %d\n", err);
+               mlx5_core_warn(mdev, "mlx5_buf_alloc_node() failed, %d\n", err);
                goto err_db_free;
        }
 
@@ -111,14 +111,14 @@ int mlx5_cqwq_create(struct mlx5_core_dev *mdev, struct mlx5_wq_param *param,
 
        err = mlx5_db_alloc_node(mdev, &wq_ctrl->db, param->db_numa_node);
        if (err) {
-               mlx5_core_warn(mdev, "mlx5_db_alloc() failed, %d\n", err);
+               mlx5_core_warn(mdev, "mlx5_db_alloc_node() failed, %d\n", err);
                return err;
        }
 
        err = mlx5_buf_alloc_node(mdev, mlx5_cqwq_get_byte_size(wq),
                                  &wq_ctrl->buf, param->buf_numa_node);
        if (err) {
-               mlx5_core_warn(mdev, "mlx5_buf_alloc() failed, %d\n", err);
+               mlx5_core_warn(mdev, "mlx5_buf_alloc_node() failed, %d\n", err);
                goto err_db_free;
        }
 
@@ -148,13 +148,14 @@ int mlx5_wq_ll_create(struct mlx5_core_dev *mdev, struct mlx5_wq_param *param,
 
        err = mlx5_db_alloc_node(mdev, &wq_ctrl->db, param->db_numa_node);
        if (err) {
-               mlx5_core_warn(mdev, "mlx5_db_alloc() failed, %d\n", err);
+               mlx5_core_warn(mdev, "mlx5_db_alloc_node() failed, %d\n", err);
                return err;
        }
 
-       err = mlx5_buf_alloc(mdev, mlx5_wq_ll_get_byte_size(wq), &wq_ctrl->buf);
+       err = mlx5_buf_alloc_node(mdev, mlx5_wq_ll_get_byte_size(wq),
+                                 &wq_ctrl->buf, param->buf_numa_node);
        if (err) {
-               mlx5_core_warn(mdev, "mlx5_buf_alloc() failed, %d\n", err);
+               mlx5_core_warn(mdev, "mlx5_buf_alloc_node() failed, %d\n", err);
                goto err_db_free;
        }
 
index 1977e7a5c5301cc591fade66cb290eb7dbd163e1..57d48da709fb77076c2cd737ad87c02ef0ff3f90 100644 (file)
@@ -2718,7 +2718,7 @@ static inline void mlxsw_reg_ppcnt_pack(char *payload, u8 local_port,
  * Configures the switch priority to buffer table.
  */
 #define MLXSW_REG_PPTB_ID 0x500B
-#define MLXSW_REG_PPTB_LEN 0x0C
+#define MLXSW_REG_PPTB_LEN 0x10
 
 static const struct mlxsw_reg_info mlxsw_reg_pptb = {
        .id = MLXSW_REG_PPTB_ID,
@@ -2784,6 +2784,13 @@ MLXSW_ITEM32(reg, pptb, pm_msb, 0x08, 24, 8);
  */
 MLXSW_ITEM32(reg, pptb, untagged_buff, 0x08, 0, 4);
 
+/* reg_pptb_prio_to_buff_msb
+ * Mapping of switch priority <i+8> to one of the allocated receive port
+ * buffers.
+ * Access: RW
+ */
+MLXSW_ITEM_BIT_ARRAY(reg, pptb, prio_to_buff_msb, 0x0C, 0x04, 4);
+
 #define MLXSW_REG_PPTB_ALL_PRIO 0xFF
 
 static inline void mlxsw_reg_pptb_pack(char *payload, u8 local_port)
@@ -2792,6 +2799,14 @@ static inline void mlxsw_reg_pptb_pack(char *payload, u8 local_port)
        mlxsw_reg_pptb_mm_set(payload, MLXSW_REG_PPTB_MM_UM);
        mlxsw_reg_pptb_local_port_set(payload, local_port);
        mlxsw_reg_pptb_pm_set(payload, MLXSW_REG_PPTB_ALL_PRIO);
+       mlxsw_reg_pptb_pm_msb_set(payload, MLXSW_REG_PPTB_ALL_PRIO);
+}
+
+static inline void mlxsw_reg_pptb_prio_to_buff_pack(char *payload, u8 prio,
+                                                   u8 buff)
+{
+       mlxsw_reg_pptb_prio_to_buff_set(payload, prio, buff);
+       mlxsw_reg_pptb_prio_to_buff_msb_set(payload, prio, buff);
 }
 
 /* PBMC - Port Buffer Management Control Register
index 6f9e3ddff4a8df98dd95818db7e722aaf8cb76a9..374080027b2f2da103a5a2e89b16f16e689239ec 100644 (file)
@@ -171,23 +171,6 @@ static int mlxsw_sp_port_admin_status_set(struct mlxsw_sp_port *mlxsw_sp_port,
        return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(paos), paos_pl);
 }
 
-static int mlxsw_sp_port_oper_status_get(struct mlxsw_sp_port *mlxsw_sp_port,
-                                        bool *p_is_up)
-{
-       struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
-       char paos_pl[MLXSW_REG_PAOS_LEN];
-       u8 oper_status;
-       int err;
-
-       mlxsw_reg_paos_pack(paos_pl, mlxsw_sp_port->local_port, 0);
-       err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(paos), paos_pl);
-       if (err)
-               return err;
-       oper_status = mlxsw_reg_paos_oper_status_get(paos_pl);
-       *p_is_up = oper_status == MLXSW_PORT_ADMIN_STATUS_UP ? true : false;
-       return 0;
-}
-
 static int mlxsw_sp_port_dev_addr_set(struct mlxsw_sp_port *mlxsw_sp_port,
                                      unsigned char *addr)
 {
@@ -408,7 +391,11 @@ static netdev_tx_t mlxsw_sp_port_xmit(struct sk_buff *skb,
        }
 
        mlxsw_sp_txhdr_construct(skb, &tx_info);
-       len = skb->len;
+       /* TX header is consumed by HW on the way so we shouldn't count its
+        * bytes as being sent.
+        */
+       len = skb->len - MLXSW_TXHDR_LEN;
+
        /* Due to a race we might fail here because of a full queue. In that
         * unlikely case we simply drop the packet.
         */
@@ -1430,7 +1417,8 @@ static int mlxsw_sp_port_get_settings(struct net_device *dev,
 
        cmd->supported = mlxsw_sp_from_ptys_supported_port(eth_proto_cap) |
                         mlxsw_sp_from_ptys_supported_link(eth_proto_cap) |
-                        SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+                        SUPPORTED_Pause | SUPPORTED_Asym_Pause |
+                        SUPPORTED_Autoneg;
        cmd->advertising = mlxsw_sp_from_ptys_advert_link(eth_proto_admin);
        mlxsw_sp_from_ptys_speed_duplex(netif_carrier_ok(dev),
                                        eth_proto_oper, cmd);
@@ -1489,7 +1477,6 @@ static int mlxsw_sp_port_set_settings(struct net_device *dev,
        u32 eth_proto_new;
        u32 eth_proto_cap;
        u32 eth_proto_admin;
-       bool is_up;
        int err;
 
        speed = ethtool_cmd_speed(cmd);
@@ -1521,12 +1508,7 @@ static int mlxsw_sp_port_set_settings(struct net_device *dev,
                return err;
        }
 
-       err = mlxsw_sp_port_oper_status_get(mlxsw_sp_port, &is_up);
-       if (err) {
-               netdev_err(dev, "Failed to get oper status");
-               return err;
-       }
-       if (!is_up)
+       if (!netif_running(dev))
                return 0;
 
        err = mlxsw_sp_port_admin_status_set(mlxsw_sp_port, false);
index a3720a0fad7d6ff0573256bc95d081e8d25ff2e1..074cdda7b6f337a6985e10a8d3620dd2825d2f3e 100644 (file)
@@ -194,7 +194,7 @@ static int mlxsw_sp_port_pb_prio_init(struct mlxsw_sp_port *mlxsw_sp_port)
 
        mlxsw_reg_pptb_pack(pptb_pl, mlxsw_sp_port->local_port);
        for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
-               mlxsw_reg_pptb_prio_to_buff_set(pptb_pl, i, 0);
+               mlxsw_reg_pptb_prio_to_buff_pack(pptb_pl, i, 0);
        return mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(pptb),
                               pptb_pl);
 }
index 0b323661c0b6b3122c9aba83fc8d0e3d0f2e68ad..01cfb75128278ca2a1b263636c62314d5bc3d1c2 100644 (file)
@@ -103,7 +103,8 @@ static int mlxsw_sp_port_pg_prio_map(struct mlxsw_sp_port *mlxsw_sp_port,
 
        mlxsw_reg_pptb_pack(pptb_pl, mlxsw_sp_port->local_port);
        for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
-               mlxsw_reg_pptb_prio_to_buff_set(pptb_pl, i, prio_tc[i]);
+               mlxsw_reg_pptb_prio_to_buff_pack(pptb_pl, i, prio_tc[i]);
+
        return mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(pptb),
                               pptb_pl);
 }
@@ -249,6 +250,7 @@ static int mlxsw_sp_dcbnl_ieee_setets(struct net_device *dev,
                return err;
 
        memcpy(mlxsw_sp_port->dcb.ets, ets, sizeof(*ets));
+       mlxsw_sp_port->dcb.ets->ets_cap = IEEE_8021QAZ_MAX_TCS;
 
        return 0;
 }
@@ -351,7 +353,8 @@ static int mlxsw_sp_dcbnl_ieee_setpfc(struct net_device *dev,
        struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
        int err;
 
-       if (mlxsw_sp_port->link.tx_pause || mlxsw_sp_port->link.rx_pause) {
+       if ((mlxsw_sp_port->link.tx_pause || mlxsw_sp_port->link.rx_pause) &&
+           pfc->pfc_en) {
                netdev_err(dev, "PAUSE frames already enabled on port\n");
                return -EINVAL;
        }
@@ -371,6 +374,7 @@ static int mlxsw_sp_dcbnl_ieee_setpfc(struct net_device *dev,
        }
 
        memcpy(mlxsw_sp_port->dcb.pfc, pfc, sizeof(*pfc));
+       mlxsw_sp_port->dcb.pfc->pfc_cap = IEEE_8021QAZ_MAX_TCS;
 
        return 0;
 
index 3842eab9449a4fd294ac7a24d02c6b48de4a4802..25f658b3849a6beb6c1febc9ba5beab66bec5472 100644 (file)
@@ -316,7 +316,10 @@ static netdev_tx_t mlxsw_sx_port_xmit(struct sk_buff *skb,
                }
        }
        mlxsw_sx_txhdr_construct(skb, &tx_info);
-       len = skb->len;
+       /* TX header is consumed by HW on the way so we shouldn't count its
+        * bytes as being sent.
+        */
+       len = skb->len - MLXSW_TXHDR_LEN;
        /* Due to a race we might fail here because of a full queue. In that
         * unlikely case we simply drop the packet.
         */
index 7066954c39d682fe229bf8c151ac46047c4eae77..0a26b11ca8f61eff9f7c5b880b770a99948f07bf 100644 (file)
@@ -1151,7 +1151,8 @@ static void enc28j60_irq_work_handler(struct work_struct *work)
                        enc28j60_phy_read(priv, PHIR);
                }
                /* TX complete handler */
-               if ((intflags & EIR_TXIF) != 0) {
+               if (((intflags & EIR_TXIF) != 0) &&
+                   ((intflags & EIR_TXERIF) == 0)) {
                        bool err = false;
                        loop++;
                        if (netif_msg_intr(priv))
@@ -1203,7 +1204,7 @@ static void enc28j60_irq_work_handler(struct work_struct *work)
                                        enc28j60_tx_clear(ndev, true);
                        } else
                                enc28j60_tx_clear(ndev, true);
-                       locked_reg_bfclr(priv, EIR, EIR_TXERIF);
+                       locked_reg_bfclr(priv, EIR, EIR_TXERIF | EIR_TXIF);
                }
                /* RX Error handler */
                if ((intflags & EIR_RXERIF) != 0) {
@@ -1238,6 +1239,8 @@ static void enc28j60_irq_work_handler(struct work_struct *work)
  */
 static void enc28j60_hw_tx(struct enc28j60_net *priv)
 {
+       BUG_ON(!priv->tx_skb);
+
        if (netif_msg_tx_queued(priv))
                printk(KERN_DEBUG DRV_NAME
                        ": Tx Packet Len:%d\n", priv->tx_skb->len);
index fa47c14c743ad811252c753302d4b52e944123f8..ba26bb356b8dff20f3ef6e9efffc089d96c30281 100644 (file)
@@ -2015,7 +2015,7 @@ static void nfp_net_open_stack(struct nfp_net *nn)
 
        netif_tx_wake_all_queues(nn->netdev);
 
-       enable_irq(nn->irq_entries[NFP_NET_CFG_LSC].vector);
+       enable_irq(nn->irq_entries[NFP_NET_IRQ_LSC_IDX].vector);
        nfp_net_read_link_status(nn);
 }
 
@@ -2044,7 +2044,7 @@ static int nfp_net_netdev_open(struct net_device *netdev)
                                      NFP_NET_IRQ_LSC_IDX, nn->lsc_handler);
        if (err)
                goto err_free_exn;
-       disable_irq(nn->irq_entries[NFP_NET_CFG_LSC].vector);
+       disable_irq(nn->irq_entries[NFP_NET_IRQ_LSC_IDX].vector);
 
        nn->rx_rings = kcalloc(nn->num_rx_rings, sizeof(*nn->rx_rings),
                               GFP_KERNEL);
@@ -2133,7 +2133,7 @@ static void nfp_net_close_stack(struct nfp_net *nn)
 {
        unsigned int r;
 
-       disable_irq(nn->irq_entries[NFP_NET_CFG_LSC].vector);
+       disable_irq(nn->irq_entries[NFP_NET_IRQ_LSC_IDX].vector);
        netif_carrier_off(nn->netdev);
        nn->link_up = false;
 
index 9afc15fdbb02af0762a2b46f1ed5fde59322e846..e29ed5a69566357d144b1997151559792f66a656 100644 (file)
@@ -3700,6 +3700,7 @@ struct public_port {
 #define MEDIA_DA_TWINAX         0x3
 #define MEDIA_BASE_T            0x4
 #define MEDIA_SFP_1G_FIBER      0x5
+#define MEDIA_MODULE_FIBER      0x6
 #define MEDIA_KR                0xf0
 #define MEDIA_NOT_PRESENT       0xff
 
index 8fba87dd48afaf6ce9ce756bdb24112e7d921176..aada4c7e095f2130c688e30384cdb29b944cc9d5 100644 (file)
@@ -72,6 +72,7 @@ int qed_sp_eth_vport_start(struct qed_hwfn *p_hwfn,
        p_ramrod->mtu                   = cpu_to_le16(p_params->mtu);
        p_ramrod->inner_vlan_removal_en = p_params->remove_inner_vlan;
        p_ramrod->drop_ttl0_en          = p_params->drop_ttl0;
+       p_ramrod->untagged              = p_params->only_untagged;
 
        SET_FIELD(rx_mode, ETH_VPORT_RX_MODE_UCAST_DROP_ALL, 1);
        SET_FIELD(rx_mode, ETH_VPORT_RX_MODE_MCAST_DROP_ALL, 1);
@@ -247,10 +248,6 @@ qed_sp_update_accept_mode(struct qed_hwfn *p_hwfn,
                SET_FIELD(state, ETH_VPORT_TX_MODE_UCAST_DROP_ALL,
                          !!(accept_filter & QED_ACCEPT_NONE));
 
-               SET_FIELD(state, ETH_VPORT_TX_MODE_UCAST_ACCEPT_ALL,
-                         (!!(accept_filter & QED_ACCEPT_UCAST_MATCHED) &&
-                          !!(accept_filter & QED_ACCEPT_UCAST_UNMATCHED)));
-
                SET_FIELD(state, ETH_VPORT_TX_MODE_MCAST_DROP_ALL,
                          !!(accept_filter & QED_ACCEPT_NONE));
 
@@ -1748,7 +1745,8 @@ static int qed_start_vport(struct qed_dev *cdev,
                           start.vport_id, start.mtu);
        }
 
-       qed_reset_vport_stats(cdev);
+       if (params->clear_stats)
+               qed_reset_vport_stats(cdev);
 
        return 0;
 }
index 61cc6869fa6508f549d59bc9a525e5e3901d73a7..c7e01b3035407e83c8c0c2e8216275ae9c8811aa 100644 (file)
@@ -1085,6 +1085,7 @@ static int qed_get_port_type(u32 media_type)
        case MEDIA_SFPP_10G_FIBER:
        case MEDIA_SFP_1G_FIBER:
        case MEDIA_XFP_FIBER:
+       case MEDIA_MODULE_FIBER:
        case MEDIA_KR:
                port_type = PORT_FIBRE;
                break;
index acac6626a1b29417eacdbda3751cee4cb241ce9a..b122f6013b6c1d786dcf8bb6b4b1bcea914c31e3 100644 (file)
@@ -213,19 +213,15 @@ static int qed_spq_hw_post(struct qed_hwfn *p_hwfn,
        SET_FIELD(db.params, CORE_DB_DATA_AGG_VAL_SEL,
                  DQ_XCM_CORE_SPQ_PROD_CMD);
        db.agg_flags = DQ_XCM_CORE_DQ_CF_CMD;
-
-       /* validate producer is up to-date */
-       rmb();
-
        db.spq_prod = cpu_to_le16(qed_chain_get_prod_idx(p_chain));
 
-       /* do not reorder */
-       barrier();
+       /* make sure the SPQE is updated before the doorbell */
+       wmb();
 
        DOORBELL(p_hwfn, qed_db_addr(p_spq->cid, DQ_DEMS_LEGACY), *(u32 *)&db);
 
        /* make sure doorbell is rang */
-       mmiowb();
+       wmb();
 
        DP_VERBOSE(p_hwfn, QED_MSG_SPQ,
                   "Doorbelled [0x%08x, CID 0x%08x] with Flags: %02x agg_params: %02x, prod: %04x\n",
@@ -614,7 +610,9 @@ qed_spq_add_entry(struct qed_hwfn *p_hwfn,
 
                        *p_en2 = *p_ent;
 
-                       kfree(p_ent);
+                       /* EBLOCK responsible to free the allocated p_ent */
+                       if (p_ent->comp_mode != QED_SPQ_MODE_EBLOCK)
+                               kfree(p_ent);
 
                        p_ent = p_en2;
                }
@@ -749,6 +747,15 @@ int qed_spq_post(struct qed_hwfn *p_hwfn,
                 * Thus, after gaining the answer perform the cleanup here.
                 */
                rc = qed_spq_block(p_hwfn, p_ent, fw_return_code);
+
+               if (p_ent->queue == &p_spq->unlimited_pending) {
+                       /* This is an allocated p_ent which does not need to
+                        * return to pool.
+                        */
+                       kfree(p_ent);
+                       return rc;
+               }
+
                if (rc)
                        goto spq_post_fail2;
 
@@ -844,8 +851,12 @@ int qed_spq_completion(struct qed_hwfn *p_hwfn,
                found->comp_cb.function(p_hwfn, found->comp_cb.cookie, p_data,
                                        fw_return_code);
 
-       if (found->comp_mode != QED_SPQ_MODE_EBLOCK)
-               /* EBLOCK is responsible for freeing its own entry */
+       if ((found->comp_mode != QED_SPQ_MODE_EBLOCK) ||
+           (found->queue == &p_spq->unlimited_pending))
+               /* EBLOCK  is responsible for returning its own entry into the
+                * free list, unless it originally added the entry into the
+                * unlimited pending list.
+                */
                qed_spq_return_entry(p_hwfn, found);
 
        /* Attempt to post pending requests */
index 5733d188822348c45c5a4f23cd245a0e3cfc8188..f8e11f953acb060b4501ab03da0743a7dc548dab 100644 (file)
@@ -3231,7 +3231,7 @@ static int qede_stop_queues(struct qede_dev *edev)
        return rc;
 }
 
-static int qede_start_queues(struct qede_dev *edev)
+static int qede_start_queues(struct qede_dev *edev, bool clear_stats)
 {
        int rc, tc, i;
        int vlan_removal_en = 1;
@@ -3462,6 +3462,7 @@ static void qede_unload(struct qede_dev *edev, enum qede_unload_mode mode)
 
 enum qede_load_mode {
        QEDE_LOAD_NORMAL,
+       QEDE_LOAD_RELOAD,
 };
 
 static int qede_load(struct qede_dev *edev, enum qede_load_mode mode)
@@ -3500,7 +3501,7 @@ static int qede_load(struct qede_dev *edev, enum qede_load_mode mode)
                goto err3;
        DP_INFO(edev, "Setup IRQs succeeded\n");
 
-       rc = qede_start_queues(edev);
+       rc = qede_start_queues(edev, mode != QEDE_LOAD_RELOAD);
        if (rc)
                goto err4;
        DP_INFO(edev, "Start VPORT, RXQ and TXQ succeeded\n");
@@ -3555,7 +3556,7 @@ void qede_reload(struct qede_dev *edev,
        if (func)
                func(edev, args);
 
-       qede_load(edev, QEDE_LOAD_NORMAL);
+       qede_load(edev, QEDE_LOAD_RELOAD);
 
        mutex_lock(&edev->qede_lock);
        qede_config_rx_mode(edev->ndev);
index 7bd6f25b4625f3bd634f9ac519127edca0a4fbce..87c642d3b075b2bc9845ba2cdbb4204e788028c3 100644 (file)
@@ -772,6 +772,8 @@ netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
        tx_ring->tx_stats.tx_bytes += skb->len;
        tx_ring->tx_stats.xmit_called++;
 
+       /* Ensure writes are complete before HW fetches Tx descriptors */
+       wmb();
        qlcnic_update_cmd_producer(tx_ring);
 
        return NETDEV_TX_OK;
@@ -2220,7 +2222,7 @@ void qlcnic_83xx_process_rcv_ring_diag(struct qlcnic_host_sds_ring *sds_ring)
        if (!opcode)
                return;
 
-       ring = QLCNIC_FETCH_RING_ID(qlcnic_83xx_hndl(sts_data[0]));
+       ring = QLCNIC_FETCH_RING_ID(sts_data[0]);
        qlcnic_83xx_process_rcv_diag(adapter, ring, sts_data);
        desc = &sds_ring->desc_head[consumer];
        desc->status_desc_data[0] = cpu_to_le64(STATUS_OWNER_PHANTOM);
index 133e9e35be9e9212bf14531bffd1c0cf78fde742..4c83739d158f1d36714b40d4eedfbec34c942882 100644 (file)
@@ -104,7 +104,8 @@ int efx_farch_test_registers(struct efx_nic *efx,
                             const struct efx_farch_register_test *regs,
                             size_t n_regs)
 {
-       unsigned address = 0, i, j;
+       unsigned address = 0;
+       int i, j;
        efx_oword_t mask, imask, original, reg, buf;
 
        for (i = 0; i < n_regs; ++i) {
index 8af25563f627034e49be2304ad8531ed6c19bd99..b5ab5e120bca3a8631e3634134f740449773e4b2 100644 (file)
@@ -116,7 +116,6 @@ struct smsc911x_data {
 
        struct phy_device *phy_dev;
        struct mii_bus *mii_bus;
-       int phy_irq[PHY_MAX_ADDR];
        unsigned int using_extphy;
        int last_duplex;
        int last_carrier;
@@ -1073,7 +1072,6 @@ static int smsc911x_mii_init(struct platform_device *pdev,
        pdata->mii_bus->priv = pdata;
        pdata->mii_bus->read = smsc911x_mii_read;
        pdata->mii_bus->write = smsc911x_mii_write;
-       memcpy(pdata->mii_bus->irq, pdata->phy_irq, sizeof(pdata->mii_bus));
 
        pdata->mii_bus->parent = &pdev->dev;
 
index a473c182c91d0e4f981640ee285486e4a48e95d8..e4071265be76f2240915879f13130fa30e592a39 100644 (file)
@@ -2804,7 +2804,7 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
                                priv->tx_path_in_lpi_mode = true;
                        if (status & CORE_IRQ_TX_PATH_EXIT_LPI_MODE)
                                priv->tx_path_in_lpi_mode = false;
-                       if (status & CORE_IRQ_MTL_RX_OVERFLOW)
+                       if (status & CORE_IRQ_MTL_RX_OVERFLOW && priv->hw->dma->set_rx_tail_ptr)
                                priv->hw->dma->set_rx_tail_ptr(priv->ioaddr,
                                                        priv->rx_tail_addr,
                                                        STMMAC_CHAN0);
index e6bb0ecb12c74cb88204635d3ec2712816597a44..53190894f17a04d00cd8061ad95dcd2776ffcda6 100644 (file)
@@ -2505,8 +2505,6 @@ static int cpsw_probe(struct platform_device *pdev)
 clean_ale_ret:
        cpsw_ale_destroy(priv->ale);
 clean_dma_ret:
-       cpdma_chan_destroy(priv->txch);
-       cpdma_chan_destroy(priv->rxch);
        cpdma_ctlr_destroy(priv->dma);
 clean_runtime_disable_ret:
        pm_runtime_disable(&pdev->dev);
@@ -2534,8 +2532,6 @@ static int cpsw_remove(struct platform_device *pdev)
        unregister_netdev(ndev);
 
        cpsw_ale_destroy(priv->ale);
-       cpdma_chan_destroy(priv->txch);
-       cpdma_chan_destroy(priv->rxch);
        cpdma_ctlr_destroy(priv->dma);
        pm_runtime_disable(&pdev->dev);
        device_for_each_child(&pdev->dev, NULL, cpsw_remove_child_device);
index 0a15acc075b3abfac4ce3ac5eed3f1bbf1f0f76e..11213a38c7952be27cd032933881df9124ed5ac8 100644 (file)
@@ -462,7 +462,7 @@ static void tile_tx_timestamp(struct sk_buff *skb, int instance)
        if (unlikely((shtx->tx_flags & SKBTX_HW_TSTAMP) != 0)) {
                struct mpipe_data *md = &mpipe_data[instance];
                struct skb_shared_hwtstamps shhwtstamps;
-               struct timespec ts;
+               struct timespec64 ts;
 
                shtx->tx_flags |= SKBTX_IN_PROGRESS;
                gxio_mpipe_get_timestamp(&md->context, &ts);
@@ -886,9 +886,9 @@ static struct ptp_clock_info ptp_mpipe_caps = {
 /* Sync mPIPE's timestamp up with Linux system time and register PTP clock. */
 static void register_ptp_clock(struct net_device *dev, struct mpipe_data *md)
 {
-       struct timespec ts;
+       struct timespec64 ts;
 
-       getnstimeofday(&ts);
+       ktime_get_ts64(&ts);
        gxio_mpipe_set_timestamp(&md->context, &ts);
 
        mutex_init(&md->ptp_lock);
index b0be0234abf6a3bc8a1060434a2b5c6af88112f5..a957a1c7e5babd91b5fa824afa266956a7e8d4fc 100644 (file)
@@ -17,4 +17,4 @@ skfp-objs :=  skfddi.o    hwmtm.o    fplustm.o  smt.o      cfm.o     \
 #   projects. To keep the source common for all those drivers (and
 #   thus simplify fixes to it), please do not clean it up!
 
-ccflags-y := -Idrivers/net/skfp -DPCI -DMEM_MAPPED_IO -Wno-strict-prototypes
+ccflags-y := -DPCI -DMEM_MAPPED_IO -Wno-strict-prototypes
index cadefe4fdaa2aa71cfab4dc8dc0dd4b326f5745b..9b3dc3c61e00b5839fbd6c98ea6b3383b753df84 100644 (file)
@@ -958,8 +958,8 @@ static netdev_tx_t geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
                dev->stats.collisions++;
        else if (err == -ENETUNREACH)
                dev->stats.tx_carrier_errors++;
-       else
-               dev->stats.tx_errors++;
+
+       dev->stats.tx_errors++;
        return NETDEV_TX_OK;
 }
 
@@ -1048,8 +1048,8 @@ static netdev_tx_t geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
                dev->stats.collisions++;
        else if (err == -ENETUNREACH)
                dev->stats.tx_carrier_errors++;
-       else
-               dev->stats.tx_errors++;
+
+       dev->stats.tx_errors++;
        return NETDEV_TX_OK;
 }
 #endif
@@ -1072,12 +1072,17 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 
 static int __geneve_change_mtu(struct net_device *dev, int new_mtu, bool strict)
 {
+       struct geneve_dev *geneve = netdev_priv(dev);
        /* The max_mtu calculation does not take account of GENEVE
         * options, to avoid excluding potentially valid
         * configurations.
         */
-       int max_mtu = IP_MAX_MTU - GENEVE_BASE_HLEN - sizeof(struct iphdr)
-               - dev->hard_header_len;
+       int max_mtu = IP_MAX_MTU - GENEVE_BASE_HLEN - dev->hard_header_len;
+
+       if (geneve->remote.sa.sa_family == AF_INET6)
+               max_mtu -= sizeof(struct ipv6hdr);
+       else
+               max_mtu -= sizeof(struct iphdr);
 
        if (new_mtu < 68)
                return -EINVAL;
@@ -1508,6 +1513,7 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
 {
        struct nlattr *tb[IFLA_MAX + 1];
        struct net_device *dev;
+       LIST_HEAD(list_kill);
        int err;
 
        memset(tb, 0, sizeof(tb));
@@ -1519,8 +1525,10 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
        err = geneve_configure(net, dev, &geneve_remote_unspec,
                               0, 0, 0, 0, htons(dst_port), true,
                               GENEVE_F_UDP_ZERO_CSUM6_RX);
-       if (err)
-               goto err;
+       if (err) {
+               free_netdev(dev);
+               return ERR_PTR(err);
+       }
 
        /* openvswitch users expect packet sizes to be unrestricted,
         * so set the largest MTU we can.
@@ -1529,10 +1537,15 @@ struct net_device *geneve_dev_create_fb(struct net *net, const char *name,
        if (err)
                goto err;
 
+       err = rtnl_configure_link(dev, NULL);
+       if (err < 0)
+               goto err;
+
        return dev;
 
  err:
-       free_netdev(dev);
+       geneve_dellink(dev, &list_kill);
+       unregister_netdevice_many(&list_kill);
        return ERR_PTR(err);
 }
 EXPORT_SYMBOL_GPL(geneve_dev_create_fb);
index 47ee2c840b55e730a8913b9736e8fdef073574dc..8bcd78f9496638e30c313abb74d004729d0dae16 100644 (file)
@@ -605,12 +605,41 @@ static void macsec_encrypt_done(struct crypto_async_request *base, int err)
        dev_put(dev);
 }
 
+static struct aead_request *macsec_alloc_req(struct crypto_aead *tfm,
+                                            unsigned char **iv,
+                                            struct scatterlist **sg)
+{
+       size_t size, iv_offset, sg_offset;
+       struct aead_request *req;
+       void *tmp;
+
+       size = sizeof(struct aead_request) + crypto_aead_reqsize(tfm);
+       iv_offset = size;
+       size += GCM_AES_IV_LEN;
+
+       size = ALIGN(size, __alignof__(struct scatterlist));
+       sg_offset = size;
+       size += sizeof(struct scatterlist) * (MAX_SKB_FRAGS + 1);
+
+       tmp = kmalloc(size, GFP_ATOMIC);
+       if (!tmp)
+               return NULL;
+
+       *iv = (unsigned char *)(tmp + iv_offset);
+       *sg = (struct scatterlist *)(tmp + sg_offset);
+       req = tmp;
+
+       aead_request_set_tfm(req, tfm);
+
+       return req;
+}
+
 static struct sk_buff *macsec_encrypt(struct sk_buff *skb,
                                      struct net_device *dev)
 {
        int ret;
-       struct scatterlist sg[MAX_SKB_FRAGS + 1];
-       unsigned char iv[GCM_AES_IV_LEN];
+       struct scatterlist *sg;
+       unsigned char *iv;
        struct ethhdr *eth;
        struct macsec_eth_header *hh;
        size_t unprotected_len;
@@ -668,8 +697,6 @@ static struct sk_buff *macsec_encrypt(struct sk_buff *skb,
        macsec_fill_sectag(hh, secy, pn);
        macsec_set_shortlen(hh, unprotected_len - 2 * ETH_ALEN);
 
-       macsec_fill_iv(iv, secy->sci, pn);
-
        skb_put(skb, secy->icv_len);
 
        if (skb->len - ETH_HLEN > macsec_priv(dev)->real_dev->mtu) {
@@ -684,13 +711,15 @@ static struct sk_buff *macsec_encrypt(struct sk_buff *skb,
                return ERR_PTR(-EINVAL);
        }
 
-       req = aead_request_alloc(tx_sa->key.tfm, GFP_ATOMIC);
+       req = macsec_alloc_req(tx_sa->key.tfm, &iv, &sg);
        if (!req) {
                macsec_txsa_put(tx_sa);
                kfree_skb(skb);
                return ERR_PTR(-ENOMEM);
        }
 
+       macsec_fill_iv(iv, secy->sci, pn);
+
        sg_init_table(sg, MAX_SKB_FRAGS + 1);
        skb_to_sgvec(skb, sg, 0, skb->len);
 
@@ -861,7 +890,6 @@ static void macsec_decrypt_done(struct crypto_async_request *base, int err)
 out:
        macsec_rxsa_put(rx_sa);
        dev_put(dev);
-       return;
 }
 
 static struct sk_buff *macsec_decrypt(struct sk_buff *skb,
@@ -871,8 +899,8 @@ static struct sk_buff *macsec_decrypt(struct sk_buff *skb,
                                      struct macsec_secy *secy)
 {
        int ret;
-       struct scatterlist sg[MAX_SKB_FRAGS + 1];
-       unsigned char iv[GCM_AES_IV_LEN];
+       struct scatterlist *sg;
+       unsigned char *iv;
        struct aead_request *req;
        struct macsec_eth_header *hdr;
        u16 icv_len = secy->icv_len;
@@ -882,7 +910,7 @@ static struct sk_buff *macsec_decrypt(struct sk_buff *skb,
        if (!skb)
                return ERR_PTR(-ENOMEM);
 
-       req = aead_request_alloc(rx_sa->key.tfm, GFP_ATOMIC);
+       req = macsec_alloc_req(rx_sa->key.tfm, &iv, &sg);
        if (!req) {
                kfree_skb(skb);
                return ERR_PTR(-ENOMEM);
@@ -1234,7 +1262,7 @@ static struct crypto_aead *macsec_alloc_tfm(char *key, int key_len, int icv_len)
        struct crypto_aead *tfm;
        int ret;
 
-       tfm = crypto_alloc_aead("gcm(aes)", 0, CRYPTO_ALG_ASYNC);
+       tfm = crypto_alloc_aead("gcm(aes)", 0, 0);
        if (!tfm || IS_ERR(tfm))
                return NULL;
 
@@ -2612,6 +2640,7 @@ static netdev_tx_t macsec_start_xmit(struct sk_buff *skb,
                u64_stats_update_begin(&secy_stats->syncp);
                secy_stats->stats.OutPktsUntagged++;
                u64_stats_update_end(&secy_stats->syncp);
+               skb->dev = macsec->real_dev;
                len = skb->len;
                ret = dev_queue_xmit(skb);
                count_tx(dev, ret, len);
@@ -3361,6 +3390,7 @@ static void __exit macsec_exit(void)
        genl_unregister_family(&macsec_fam);
        rtnl_link_unregister(&macsec_link_ops);
        unregister_netdevice_notifier(&macsec_notifier);
+       rcu_barrier();
 }
 
 module_init(macsec_init);
index 2afa61b51d411d45bd0b673e6cc93125c2eb9927..91177a4a32ad21c1cbea044424659f5604cef7de 100644 (file)
@@ -57,6 +57,7 @@
 
 /* PHY CTRL bits */
 #define DP83867_PHYCR_FIFO_DEPTH_SHIFT         14
+#define DP83867_PHYCR_FIFO_DEPTH_MASK          (3 << 14)
 
 /* RGMIIDCTL bits */
 #define DP83867_RGMII_TX_CLK_DELAY_SHIFT       4
@@ -133,8 +134,8 @@ static int dp83867_of_init(struct phy_device *phydev)
 static int dp83867_config_init(struct phy_device *phydev)
 {
        struct dp83867_private *dp83867;
-       int ret;
-       u16 val, delay;
+       int ret, val;
+       u16 delay;
 
        if (!phydev->priv) {
                dp83867 = devm_kzalloc(&phydev->mdio.dev, sizeof(*dp83867),
@@ -151,8 +152,12 @@ static int dp83867_config_init(struct phy_device *phydev)
        }
 
        if (phy_interface_is_rgmii(phydev)) {
-               ret = phy_write(phydev, MII_DP83867_PHYCTRL,
-                       (dp83867->fifo_depth << DP83867_PHYCR_FIFO_DEPTH_SHIFT));
+               val = phy_read(phydev, MII_DP83867_PHYCTRL);
+               if (val < 0)
+                       return val;
+               val &= ~DP83867_PHYCR_FIFO_DEPTH_MASK;
+               val |= (dp83867->fifo_depth << DP83867_PHYCR_FIFO_DEPTH_SHIFT);
+               ret = phy_write(phydev, MII_DP83867_PHYCTRL, val);
                if (ret)
                        return ret;
        }
index 2d2e4339f0df40b5379be29b12f87587db1944a3..9ec7f735343482eb4db054daa5fb5af4cec236ba 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/slab.h>
 #include <linux/of.h>
 #include <linux/gpio.h>
+#include <linux/idr.h>
 
 #define MII_REGS_NUM 29
 
@@ -286,6 +287,8 @@ int fixed_phy_add(unsigned int irq, int phy_addr,
 }
 EXPORT_SYMBOL_GPL(fixed_phy_add);
 
+static DEFINE_IDA(phy_fixed_ida);
+
 static void fixed_phy_del(int phy_addr)
 {
        struct fixed_mdio_bus *fmb = &platform_fmb;
@@ -297,14 +300,12 @@ static void fixed_phy_del(int phy_addr)
                        if (gpio_is_valid(fp->link_gpio))
                                gpio_free(fp->link_gpio);
                        kfree(fp);
+                       ida_simple_remove(&phy_fixed_ida, phy_addr);
                        return;
                }
        }
 }
 
-static int phy_fixed_addr;
-static DEFINE_SPINLOCK(phy_fixed_addr_lock);
-
 struct phy_device *fixed_phy_register(unsigned int irq,
                                      struct fixed_phy_status *status,
                                      int link_gpio,
@@ -319,17 +320,15 @@ struct phy_device *fixed_phy_register(unsigned int irq,
                return ERR_PTR(-EPROBE_DEFER);
 
        /* Get the next available PHY address, up to PHY_MAX_ADDR */
-       spin_lock(&phy_fixed_addr_lock);
-       if (phy_fixed_addr == PHY_MAX_ADDR) {
-               spin_unlock(&phy_fixed_addr_lock);
-               return ERR_PTR(-ENOSPC);
-       }
-       phy_addr = phy_fixed_addr++;
-       spin_unlock(&phy_fixed_addr_lock);
+       phy_addr = ida_simple_get(&phy_fixed_ida, 0, PHY_MAX_ADDR, GFP_KERNEL);
+       if (phy_addr < 0)
+               return ERR_PTR(phy_addr);
 
        ret = fixed_phy_add(irq, phy_addr, status, link_gpio);
-       if (ret < 0)
+       if (ret < 0) {
+               ida_simple_remove(&phy_fixed_ida, phy_addr);
                return ERR_PTR(ret);
+       }
 
        phy = get_phy_device(fmb->mii_bus, phy_addr, false);
        if (IS_ERR(phy)) {
@@ -434,6 +433,7 @@ static void __exit fixed_mdio_bus_exit(void)
                list_del(&fp->node);
                kfree(fp);
        }
+       ida_destroy(&phy_fixed_ida);
 }
 module_exit(fixed_mdio_bus_exit);
 
index 280e8795b46367af40d717d9b16d56f748d6f585..ec2c1eee64051919a7623abf160eacf4233b57de 100644 (file)
@@ -285,6 +285,48 @@ static int marvell_config_aneg(struct phy_device *phydev)
        return 0;
 }
 
+static int m88e1111_config_aneg(struct phy_device *phydev)
+{
+       int err;
+
+       /* The Marvell PHY has an errata which requires
+        * that certain registers get written in order
+        * to restart autonegotiation
+        */
+       err = phy_write(phydev, MII_BMCR, BMCR_RESET);
+
+       err = marvell_set_polarity(phydev, phydev->mdix);
+       if (err < 0)
+               return err;
+
+       err = phy_write(phydev, MII_M1111_PHY_LED_CONTROL,
+                       MII_M1111_PHY_LED_DIRECT);
+       if (err < 0)
+               return err;
+
+       err = genphy_config_aneg(phydev);
+       if (err < 0)
+               return err;
+
+       if (phydev->autoneg != AUTONEG_ENABLE) {
+               int bmcr;
+
+               /* A write to speed/duplex bits (that is performed by
+                * genphy_config_aneg() call above) must be followed by
+                * a software reset. Otherwise, the write has no effect.
+                */
+               bmcr = phy_read(phydev, MII_BMCR);
+               if (bmcr < 0)
+                       return bmcr;
+
+               err = phy_write(phydev, MII_BMCR, bmcr | BMCR_RESET);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
 #ifdef CONFIG_OF_MDIO
 /*
  * Set and/or override some configuration registers based on the
@@ -407,15 +449,7 @@ static int m88e1121_config_aneg(struct phy_device *phydev)
        if (err < 0)
                return err;
 
-       oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
-
-       phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_88E1121_PHY_LED_PAGE);
-       phy_write(phydev, MII_88E1121_PHY_LED_CTRL, MII_88E1121_PHY_LED_DEF);
-       phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
-
-       err = genphy_config_aneg(phydev);
-
-       return err;
+       return genphy_config_aneg(phydev);
 }
 
 static int m88e1318_config_aneg(struct phy_device *phydev)
@@ -636,6 +670,28 @@ static int m88e1111_config_init(struct phy_device *phydev)
        return phy_write(phydev, MII_BMCR, BMCR_RESET);
 }
 
+static int m88e1121_config_init(struct phy_device *phydev)
+{
+       int err, oldpage;
+
+       oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE);
+
+       err = phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_88E1121_PHY_LED_PAGE);
+       if (err < 0)
+               return err;
+
+       /* Default PHY LED config: LED[0] .. Link, LED[1] .. Activity */
+       err = phy_write(phydev, MII_88E1121_PHY_LED_CTRL,
+                       MII_88E1121_PHY_LED_DEF);
+       if (err < 0)
+               return err;
+
+       phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage);
+
+       /* Set marvell,reg-init configuration from device tree */
+       return marvell_config_init(phydev);
+}
+
 static int m88e1510_config_init(struct phy_device *phydev)
 {
        int err;
@@ -668,7 +724,7 @@ static int m88e1510_config_init(struct phy_device *phydev)
                        return err;
        }
 
-       return marvell_config_init(phydev);
+       return m88e1121_config_init(phydev);
 }
 
 static int m88e1118_config_aneg(struct phy_device *phydev)
@@ -1161,7 +1217,7 @@ static struct phy_driver marvell_drivers[] = {
                .flags = PHY_HAS_INTERRUPT,
                .probe = marvell_probe,
                .config_init = &m88e1111_config_init,
-               .config_aneg = &marvell_config_aneg,
+               .config_aneg = &m88e1111_config_aneg,
                .read_status = &marvell_read_status,
                .ack_interrupt = &marvell_ack_interrupt,
                .config_intr = &marvell_config_intr,
@@ -1196,7 +1252,7 @@ static struct phy_driver marvell_drivers[] = {
                .features = PHY_GBIT_FEATURES,
                .flags = PHY_HAS_INTERRUPT,
                .probe = marvell_probe,
-               .config_init = &marvell_config_init,
+               .config_init = &m88e1121_config_init,
                .config_aneg = &m88e1121_config_aneg,
                .read_status = &marvell_read_status,
                .ack_interrupt = &marvell_ack_interrupt,
@@ -1215,7 +1271,7 @@ static struct phy_driver marvell_drivers[] = {
                .features = PHY_GBIT_FEATURES,
                .flags = PHY_HAS_INTERRUPT,
                .probe = marvell_probe,
-               .config_init = &marvell_config_init,
+               .config_init = &m88e1121_config_init,
                .config_aneg = &m88e1318_config_aneg,
                .read_status = &marvell_read_status,
                .ack_interrupt = &marvell_ack_interrupt,
index 2e21e9366f76a184aa2c8d770557e6d9ff0db235..b62c4aaee40bd8986a80965a615d327e9c270293 100644 (file)
@@ -75,22 +75,13 @@ static int smsc_phy_reset(struct phy_device *phydev)
         * in all capable mode before using it.
         */
        if ((rc & MII_LAN83C185_MODE_MASK) == MII_LAN83C185_MODE_POWERDOWN) {
-               int timeout = 50000;
-
-               /* set "all capable" mode and reset the phy */
+               /* set "all capable" mode */
                rc |= MII_LAN83C185_MODE_ALL;
                phy_write(phydev, MII_LAN83C185_SPECIAL_MODES, rc);
-               phy_write(phydev, MII_BMCR, BMCR_RESET);
-
-               /* wait end of reset (max 500 ms) */
-               do {
-                       udelay(10);
-                       if (timeout-- == 0)
-                               return -1;
-                       rc = phy_read(phydev, MII_BMCR);
-               } while (rc & BMCR_RESET);
        }
-       return 0;
+
+       /* reset the phy */
+       return genphy_soft_reset(phydev);
 }
 
 static int lan911x_config_init(struct phy_device *phydev)
index 8dedafa1a95d0b2f8e1db526cc64770876488f46..a30ee427efab3330492c9cda8ceb65259c190423 100644 (file)
@@ -2601,8 +2601,6 @@ ppp_unregister_channel(struct ppp_channel *chan)
        spin_lock_bh(&pn->all_channels_lock);
        list_del(&pch->list);
        spin_unlock_bh(&pn->all_channels_lock);
-       put_net(pch->chan_net);
-       pch->chan_net = NULL;
 
        pch->file.dead = 1;
        wake_up_interruptible(&pch->file.rwait);
@@ -3136,6 +3134,9 @@ ppp_disconnect_channel(struct channel *pch)
  */
 static void ppp_destroy_channel(struct channel *pch)
 {
+       put_net(pch->chan_net);
+       pch->chan_net = NULL;
+
        atomic_dec(&channel_count);
 
        if (!pch->file.dead) {
index 2ace126533cda080a60d3c03ef8e4e61aafc816f..fdee772073236a0542eabc00461957e487fa1370 100644 (file)
@@ -1203,8 +1203,10 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
                goto err_dev_open;
        }
 
+       netif_addr_lock_bh(dev);
        dev_uc_sync_multiple(port_dev, dev);
        dev_mc_sync_multiple(port_dev, dev);
+       netif_addr_unlock_bh(dev);
 
        err = vlan_vids_add_by_dev(port_dev, dev);
        if (err) {
index 53759c315b97aeeb54ce5b41fc0e4ec73ee8fac8..877c9516e78174dc41bd1e9d3f4c506d193134a9 100644 (file)
@@ -854,6 +854,13 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_
        if (cdc_ncm_init(dev))
                goto error2;
 
+       /* Some firmwares need a pause here or they will silently fail
+        * to set up the interface properly.  This value was decided
+        * empirically on a Sierra Wireless MC7455 running 02.08.02.00
+        * firmware.
+        */
+       usleep_range(10000, 20000);
+
        /* configure data interface */
        temp = usb_set_interface(dev->udev, iface_no, data_altsetting);
        if (temp) {
index 3f9f6ed3eec4de5c14e43039e08d53b209e52cb4..e9654a6853818933334488c28a98dbb3073a84cf 100644 (file)
 #include <linux/mdio.h>
 #include <linux/usb/cdc.h>
 #include <linux/suspend.h>
+#include <linux/acpi.h>
 
 /* Information for net-next */
 #define NETNEXT_VERSION                "08"
 
 /* Information for net */
-#define NET_VERSION            "3"
+#define NET_VERSION            "5"
 
 #define DRIVER_VERSION         "v1." NETNEXT_VERSION "." NET_VERSION
 #define DRIVER_AUTHOR "Realtek linux nic maintainers <nic_swsd@realtek.com>"
 #define USB_TX_DMA             0xd434
 #define USB_TOLERANCE          0xd490
 #define USB_LPM_CTRL           0xd41a
+#define USB_BMU_RESET          0xd4b0
 #define USB_UPS_CTRL           0xd800
 #define USB_MISC_0             0xd81a
 #define USB_POWER_CUT          0xd80a
 #define TEST_MODE_DISABLE      0x00000001
 #define TX_SIZE_ADJUST1                0x00000100
 
+/* USB_BMU_RESET */
+#define BMU_RESET_EP_IN                0x01
+#define BMU_RESET_EP_OUT       0x02
+
 /* USB_UPS_CTRL */
 #define POWER_CUT              0x0100
 
 /* SRAM_IMPEDANCE */
 #define RX_DRIVING_MASK                0x6000
 
+/* MAC PASSTHRU */
+#define AD_MASK                        0xfee0
+#define EFUSE                  0xcfdb
+#define PASS_THRU_MASK         0x1
+
 enum rtl_register_content {
        _1000bps        = 0x10,
        _100bps         = 0x08,
@@ -619,6 +630,7 @@ struct r8152 {
                int (*eee_get)(struct r8152 *, struct ethtool_eee *);
                int (*eee_set)(struct r8152 *, struct ethtool_eee *);
                bool (*in_nway)(struct r8152 *);
+               void (*autosuspend_en)(struct r8152 *tp, bool enable);
        } rtl_ops;
 
        int intr_interval;
@@ -1030,6 +1042,65 @@ static int rtl8152_set_mac_address(struct net_device *netdev, void *p)
        return ret;
 }
 
+/* Devices containing RTL8153-AD can support a persistent
+ * host system provided MAC address.
+ * Examples of this are Dell TB15 and Dell WD15 docks
+ */
+static int vendor_mac_passthru_addr_read(struct r8152 *tp, struct sockaddr *sa)
+{
+       acpi_status status;
+       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object *obj;
+       int ret = -EINVAL;
+       u32 ocp_data;
+       unsigned char buf[6];
+
+       /* test for -AD variant of RTL8153 */
+       ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0);
+       if ((ocp_data & AD_MASK) != 0x1000)
+               return -ENODEV;
+
+       /* test for MAC address pass-through bit */
+       ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, EFUSE);
+       if ((ocp_data & PASS_THRU_MASK) != 1)
+               return -ENODEV;
+
+       /* returns _AUXMAC_#AABBCCDDEEFF# */
+       status = acpi_evaluate_object(NULL, "\\_SB.AMAC", NULL, &buffer);
+       obj = (union acpi_object *)buffer.pointer;
+       if (!ACPI_SUCCESS(status))
+               return -ENODEV;
+       if (obj->type != ACPI_TYPE_BUFFER || obj->string.length != 0x17) {
+               netif_warn(tp, probe, tp->netdev,
+                          "Invalid buffer when reading pass-thru MAC addr: "
+                          "(%d, %d)\n",
+                          obj->type, obj->string.length);
+               goto amacout;
+       }
+       if (strncmp(obj->string.pointer, "_AUXMAC_#", 9) != 0 ||
+           strncmp(obj->string.pointer + 0x15, "#", 1) != 0) {
+               netif_warn(tp, probe, tp->netdev,
+                          "Invalid header when reading pass-thru MAC addr\n");
+               goto amacout;
+       }
+       ret = hex2bin(buf, obj->string.pointer + 9, 6);
+       if (!(ret == 0 && is_valid_ether_addr(buf))) {
+               netif_warn(tp, probe, tp->netdev,
+                          "Invalid MAC when reading pass-thru MAC addr: "
+                          "%d, %pM\n", ret, buf);
+               ret = -EINVAL;
+               goto amacout;
+       }
+       memcpy(sa->sa_data, buf, 6);
+       ether_addr_copy(tp->netdev->dev_addr, sa->sa_data);
+       netif_info(tp, probe, tp->netdev,
+                  "Using pass-thru MAC addr %pM\n", sa->sa_data);
+
+amacout:
+       kfree(obj);
+       return ret;
+}
+
 static int set_ethernet_addr(struct r8152 *tp)
 {
        struct net_device *dev = tp->netdev;
@@ -1038,8 +1109,15 @@ static int set_ethernet_addr(struct r8152 *tp)
 
        if (tp->version == RTL_VER_01)
                ret = pla_ocp_read(tp, PLA_IDR, 8, sa.sa_data);
-       else
-               ret = pla_ocp_read(tp, PLA_BACKUP, 8, sa.sa_data);
+       else {
+               /* if this is not an RTL8153-AD, no eFuse mac pass thru set,
+                * or system doesn't provide valid _SB.AMAC this will be
+                * be expected to non-zero
+                */
+               ret = vendor_mac_passthru_addr_read(tp, &sa);
+               if (ret < 0)
+                       ret = pla_ocp_read(tp, PLA_BACKUP, 8, sa.sa_data);
+       }
 
        if (ret < 0) {
                netif_err(tp, probe, dev, "Get ether addr fail\n");
@@ -2169,7 +2247,7 @@ static void r8153_set_rx_early_timeout(struct r8152 *tp)
 static void r8153_set_rx_early_size(struct r8152 *tp)
 {
        u32 mtu = tp->netdev->mtu;
-       u32 ocp_data = (agg_buf_sz - mtu - VLAN_ETH_HLEN - VLAN_HLEN) / 4;
+       u32 ocp_data = (agg_buf_sz - mtu - VLAN_ETH_HLEN - VLAN_HLEN) / 8;
 
        ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE, ocp_data);
 }
@@ -2290,10 +2368,6 @@ static u32 __rtl_get_wol(struct r8152 *tp)
        u32 ocp_data;
        u32 wolopts = 0;
 
-       ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CONFIG5);
-       if (!(ocp_data & LAN_WAKE_EN))
-               return 0;
-
        ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34);
        if (ocp_data & LINK_ON_WAKE_EN)
                wolopts |= WAKE_PHY;
@@ -2326,15 +2400,13 @@ static void __rtl_set_wol(struct r8152 *tp, u32 wolopts)
        ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG34, ocp_data);
 
        ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG5);
-       ocp_data &= ~(UWF_EN | BWF_EN | MWF_EN | LAN_WAKE_EN);
+       ocp_data &= ~(UWF_EN | BWF_EN | MWF_EN);
        if (wolopts & WAKE_UCAST)
                ocp_data |= UWF_EN;
        if (wolopts & WAKE_BCAST)
                ocp_data |= BWF_EN;
        if (wolopts & WAKE_MCAST)
                ocp_data |= MWF_EN;
-       if (wolopts & WAKE_ANY)
-               ocp_data |= LAN_WAKE_EN;
        ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG5, ocp_data);
 
        ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML);
@@ -2403,9 +2475,6 @@ static void rtl_runtime_suspend_enable(struct r8152 *tp, bool enable)
        if (enable) {
                u32 ocp_data;
 
-               r8153_u1u2en(tp, false);
-               r8153_u2p3en(tp, false);
-
                __rtl_set_wol(tp, WAKE_ANY);
 
                ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG);
@@ -2416,7 +2485,28 @@ static void rtl_runtime_suspend_enable(struct r8152 *tp, bool enable)
 
                ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML);
        } else {
+               u32 ocp_data;
+
                __rtl_set_wol(tp, tp->saved_wolopts);
+
+               ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG);
+
+               ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_CONFIG34);
+               ocp_data &= ~LINK_OFF_WAKE_EN;
+               ocp_write_word(tp, MCU_TYPE_PLA, PLA_CONFIG34, ocp_data);
+
+               ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML);
+       }
+}
+
+static void rtl8153_runtime_enable(struct r8152 *tp, bool enable)
+{
+       rtl_runtime_suspend_enable(tp, enable);
+
+       if (enable) {
+               r8153_u1u2en(tp, false);
+               r8153_u2p3en(tp, false);
+       } else {
                r8153_u2p3en(tp, true);
                r8153_u1u2en(tp, true);
        }
@@ -2456,6 +2546,17 @@ static void r8153_teredo_off(struct r8152 *tp)
        ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TEREDO_TIMER, 0);
 }
 
+static void rtl_reset_bmu(struct r8152 *tp)
+{
+       u32 ocp_data;
+
+       ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_BMU_RESET);
+       ocp_data &= ~(BMU_RESET_EP_IN | BMU_RESET_EP_OUT);
+       ocp_write_byte(tp, MCU_TYPE_USB, USB_BMU_RESET, ocp_data);
+       ocp_data |= BMU_RESET_EP_IN | BMU_RESET_EP_OUT;
+       ocp_write_byte(tp, MCU_TYPE_USB, USB_BMU_RESET, ocp_data);
+}
+
 static void r8152_aldps_en(struct r8152 *tp, bool enable)
 {
        if (enable) {
@@ -2681,6 +2782,7 @@ static void r8153_first_init(struct r8152 *tp)
        r8153_hw_phy_cfg(tp);
 
        rtl8152_nic_reset(tp);
+       rtl_reset_bmu(tp);
 
        ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
        ocp_data &= ~NOW_IS_OOB;
@@ -2742,6 +2844,7 @@ static void r8153_enter_oob(struct r8152 *tp)
        ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data);
 
        rtl_disable(tp);
+       rtl_reset_bmu(tp);
 
        for (i = 0; i < 1000; i++) {
                ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL);
@@ -2803,6 +2906,7 @@ static void rtl8153_disable(struct r8152 *tp)
 {
        r8153_aldps_en(tp, false);
        rtl_disable(tp);
+       rtl_reset_bmu(tp);
        r8153_aldps_en(tp, true);
        usb_enable_lpm(tp->udev);
 }
@@ -3382,15 +3486,11 @@ static void r8153_init(struct r8152 *tp)
        r8153_power_cut_en(tp, false);
        r8153_u1u2en(tp, true);
 
-       ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL, ALDPS_SPDWN_RATIO);
-       ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, EEE_SPDWN_RATIO);
-       ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3,
-                      PKT_AVAIL_SPDWN_EN | SUSPEND_SPDWN_EN |
-                      U1U2_SPDWN_EN | L1_SPDWN_EN);
-       ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4,
-                      PWRSAVE_SPDWN_EN | RXDV_SPDWN_EN | TX10MIDLE_EN |
-                      TP100_SPDWN_EN | TP500_SPDWN_EN | TP1000_SPDWN_EN |
-                      EEE_SPDWN_EN);
+       /* MAC clock speed down */
+       ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL, 0);
+       ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, 0);
+       ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, 0);
+       ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL4, 0);
 
        r8153_enable_eee(tp);
        r8153_aldps_en(tp, true);
@@ -3497,7 +3597,7 @@ static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message)
                napi_disable(&tp->napi);
                if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) {
                        rtl_stop_rx(tp);
-                       rtl_runtime_suspend_enable(tp, true);
+                       tp->rtl_ops.autosuspend_en(tp, true);
                } else {
                        cancel_delayed_work_sync(&tp->schedule);
                        tp->rtl_ops.down(tp);
@@ -3523,7 +3623,7 @@ static int rtl8152_resume(struct usb_interface *intf)
 
        if (netif_running(tp->netdev) && tp->netdev->flags & IFF_UP) {
                if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) {
-                       rtl_runtime_suspend_enable(tp, false);
+                       tp->rtl_ops.autosuspend_en(tp, false);
                        clear_bit(SELECTIVE_SUSPEND, &tp->flags);
                        napi_disable(&tp->napi);
                        set_bit(WORK_ENABLE, &tp->flags);
@@ -3542,7 +3642,7 @@ static int rtl8152_resume(struct usb_interface *intf)
                usb_submit_urb(tp->intr_urb, GFP_KERNEL);
        } else if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) {
                if (tp->netdev->flags & IFF_UP)
-                       rtl_runtime_suspend_enable(tp, false);
+                       tp->rtl_ops.autosuspend_en(tp, false);
                clear_bit(SELECTIVE_SUSPEND, &tp->flags);
        }
 
@@ -4122,6 +4222,7 @@ static int rtl_ops_init(struct r8152 *tp)
                ops->eee_get            = r8152_get_eee;
                ops->eee_set            = r8152_set_eee;
                ops->in_nway            = rtl8152_in_nway;
+               ops->autosuspend_en     = rtl_runtime_suspend_enable;
                break;
 
        case RTL_VER_03:
@@ -4137,6 +4238,7 @@ static int rtl_ops_init(struct r8152 *tp)
                ops->eee_get            = r8153_get_eee;
                ops->eee_set            = r8153_set_eee;
                ops->in_nway            = rtl8153_in_nway;
+               ops->autosuspend_en     = rtl8153_runtime_enable;
                break;
 
        default:
@@ -4323,3 +4425,4 @@ module_usb_driver(rtl8152_driver);
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
index 61ba464049374593316e1c0b869774861ee79b5e..6086a0163249c5e05d8cdf92b1b834c3da4a71a7 100644 (file)
@@ -395,8 +395,11 @@ int usbnet_change_mtu (struct net_device *net, int new_mtu)
        dev->hard_mtu = net->mtu + net->hard_header_len;
        if (dev->rx_urb_size == old_hard_mtu) {
                dev->rx_urb_size = dev->hard_mtu;
-               if (dev->rx_urb_size > old_rx_urb_size)
+               if (dev->rx_urb_size > old_rx_urb_size) {
+                       usbnet_pause_rx(dev);
                        usbnet_unlink_rx_urbs(dev);
+                       usbnet_resume_rx(dev);
+               }
        }
 
        /* max qlen depend on hard_mtu and rx_urb_size */
@@ -1508,8 +1511,9 @@ static void usbnet_bh (unsigned long param)
        } else if (netif_running (dev->net) &&
                   netif_device_present (dev->net) &&
                   netif_carrier_ok(dev->net) &&
-                  !timer_pending (&dev->delay) &&
-                  !test_bit (EVENT_RX_HALT, &dev->flags)) {
+                  !timer_pending(&dev->delay) &&
+                  !test_bit(EVENT_RX_PAUSED, &dev->flags) &&
+                  !test_bit(EVENT_RX_HALT, &dev->flags)) {
                int     temp = dev->rxq.qlen;
 
                if (temp < RX_QLEN(dev)) {
index dff08842f26d034dc21a7a73b8a1c9b0cb72236a..8bd8c7e1ee8724c56c670850c9ecc20590a68707 100644 (file)
@@ -304,7 +304,7 @@ static int vrf_rt6_create(struct net_device *dev)
        dst_hold(&rt6->dst);
 
        rt6->rt6i_table = rt6i_table;
-       rt6->dst.output = vrf_output6;
+       rt6->dst.output = vrf_output6;
        rcu_assign_pointer(vrf->rt6, rt6);
 
        rc = 0;
@@ -403,7 +403,7 @@ static int vrf_rtable_create(struct net_device *dev)
        if (!rth)
                return -ENOMEM;
 
-       rth->dst.output = vrf_output;
+       rth->dst.output = vrf_output;
        rth->rt_table_id = vrf->tb_id;
 
        rcu_assign_pointer(vrf->rth, rth);
index f999db2f97b4bb1b9254affc58f187b35da1c3f1..b3b9db68f758a2062a2f6bde2bfb9805d271cc6c 100644 (file)
@@ -2952,30 +2952,6 @@ static int vxlan_dev_configure(struct net *src_net, struct net_device *dev,
        return 0;
 }
 
-struct net_device *vxlan_dev_create(struct net *net, const char *name,
-                                   u8 name_assign_type, struct vxlan_config *conf)
-{
-       struct nlattr *tb[IFLA_MAX+1];
-       struct net_device *dev;
-       int err;
-
-       memset(&tb, 0, sizeof(tb));
-
-       dev = rtnl_create_link(net, name, name_assign_type,
-                              &vxlan_link_ops, tb);
-       if (IS_ERR(dev))
-               return dev;
-
-       err = vxlan_dev_configure(net, dev, conf);
-       if (err < 0) {
-               free_netdev(dev);
-               return ERR_PTR(err);
-       }
-
-       return dev;
-}
-EXPORT_SYMBOL_GPL(vxlan_dev_create);
-
 static int vxlan_newlink(struct net *src_net, struct net_device *dev,
                         struct nlattr *tb[], struct nlattr *data[])
 {
@@ -3268,6 +3244,40 @@ static struct rtnl_link_ops vxlan_link_ops __read_mostly = {
        .get_link_net   = vxlan_get_link_net,
 };
 
+struct net_device *vxlan_dev_create(struct net *net, const char *name,
+                                   u8 name_assign_type,
+                                   struct vxlan_config *conf)
+{
+       struct nlattr *tb[IFLA_MAX + 1];
+       struct net_device *dev;
+       int err;
+
+       memset(&tb, 0, sizeof(tb));
+
+       dev = rtnl_create_link(net, name, name_assign_type,
+                              &vxlan_link_ops, tb);
+       if (IS_ERR(dev))
+               return dev;
+
+       err = vxlan_dev_configure(net, dev, conf);
+       if (err < 0) {
+               free_netdev(dev);
+               return ERR_PTR(err);
+       }
+
+       err = rtnl_configure_link(dev, NULL);
+       if (err < 0) {
+               LIST_HEAD(list_kill);
+
+               vxlan_dellink(dev, &list_kill);
+               unregister_netdevice_many(&list_kill);
+               return ERR_PTR(err);
+       }
+
+       return dev;
+}
+EXPORT_SYMBOL_GPL(vxlan_dev_create);
+
 static void vxlan_handle_lowerdev_unregister(struct vxlan_net *vn,
                                             struct net_device *dev)
 {
index 49af62428c8864571593a98c0682f97170cfecd2..a92a0ba829f5c19c8237b539be0b9db184efe31b 100644 (file)
@@ -1083,7 +1083,7 @@ int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name,
                        }
 
                        ath10k_dbg_dump(ar, ATH10K_DBG_BOOT, "features", "",
-                                       ar->running_fw->fw_file.fw_features,
+                                       fw_file->fw_features,
                                        sizeof(fw_file->fw_features));
                        break;
                case ATH10K_FW_IE_FW_IMAGE:
index cc979a4faeb02621b230842dc8cef71a55783bdf..813cdd2621a168ff5696b723ca924f874b2a3a57 100644 (file)
@@ -1904,7 +1904,6 @@ static void ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb)
                        return;
                }
        }
-       ath10k_htt_rx_msdu_buff_replenish(htt);
 }
 
 static void ath10k_htt_rx_tx_fetch_resp_id_confirm(struct ath10k *ar,
index 6dd1d26b357f5dc8076c72ef3f2c05f3594ff2ce..4040f9413e86e8d06df8e94e31759cd1c8096c90 100644 (file)
@@ -679,10 +679,10 @@ static int ath10k_peer_create(struct ath10k *ar,
 
        peer = ath10k_peer_find(ar, vdev_id, addr);
        if (!peer) {
+               spin_unlock_bh(&ar->data_lock);
                ath10k_warn(ar, "failed to find peer %pM on vdev %i after creation\n",
                            addr, vdev_id);
                ath10k_wmi_peer_delete(ar, vdev_id, addr);
-               spin_unlock_bh(&ar->data_lock);
                return -ENOENT;
        }
 
index 9272ca90632b983b5eec1a57612ac29307f15973..80ff69f9922977e6810e34efa62641ea72316318 100644 (file)
@@ -1122,12 +1122,12 @@ enum {
 #define AR9300_NUM_GPIO                          16
 #define AR9330_NUM_GPIO                                 16
 #define AR9340_NUM_GPIO                                 23
-#define AR9462_NUM_GPIO                                 10
+#define AR9462_NUM_GPIO                                 14
 #define AR9485_NUM_GPIO                                 12
 #define AR9531_NUM_GPIO                                 18
 #define AR9550_NUM_GPIO                                 24
 #define AR9561_NUM_GPIO                                 23
-#define AR9565_NUM_GPIO                                 12
+#define AR9565_NUM_GPIO                                 14
 #define AR9580_NUM_GPIO                                 16
 #define AR7010_NUM_GPIO                          16
 
@@ -1139,12 +1139,12 @@ enum {
 #define AR9300_GPIO_MASK                        0x0000F4FF
 #define AR9330_GPIO_MASK                        0x0000F4FF
 #define AR9340_GPIO_MASK                        0x0000000F
-#define AR9462_GPIO_MASK                        0x000003FF
+#define AR9462_GPIO_MASK                        0x00003FFF
 #define AR9485_GPIO_MASK                        0x00000FFF
 #define AR9531_GPIO_MASK                        0x0000000F
 #define AR9550_GPIO_MASK                        0x0000000F
 #define AR9561_GPIO_MASK                        0x0000000F
-#define AR9565_GPIO_MASK                        0x00000FFF
+#define AR9565_GPIO_MASK                        0x00003FFF
 #define AR9580_GPIO_MASK                        0x0000F4FF
 #define AR7010_GPIO_MASK                        0x0000FFFF
 
index e5f267b21316ca413f41ed0741a9144c4e7d0f17..18a8474b5760ca6ce6c5636a0e39770232ae818a 100644 (file)
@@ -3851,8 +3851,8 @@ static int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx,
        if (idx != 0)
                return -ENOENT;
 
-       if (fw_has_capa(&mvm->fw->ucode_capa,
-                       IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
+       if (!fw_has_capa(&mvm->fw->ucode_capa,
+                        IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
                return -ENOENT;
 
        mutex_lock(&mvm->mutex);
@@ -3898,8 +3898,8 @@ static void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw,
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
 
-       if (fw_has_capa(&mvm->fw->ucode_capa,
-                       IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
+       if (!fw_has_capa(&mvm->fw->ucode_capa,
+                        IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
                return;
 
        /* if beacon filtering isn't on mac80211 does it anyway */
index ac2c5718e454d732e0ac3ed42a716e07ec8df402..2c61516d06fff52318d972feb0defbab41654d62 100644 (file)
@@ -581,7 +581,7 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,
                            struct iwl_rx_mpdu_desc *desc)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-       struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+       struct iwl_mvm_sta *mvm_sta;
        struct iwl_mvm_baid_data *baid_data;
        struct iwl_mvm_reorder_buffer *buffer;
        struct sk_buff *tail;
@@ -604,6 +604,8 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,
        if (WARN_ON(IS_ERR_OR_NULL(sta)))
                return false;
 
+       mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+
        /* not a data packet */
        if (!ieee80211_is_data_qos(hdr->frame_control) ||
            is_multicast_ether_addr(hdr->addr1))
index 6f609dd5c2220dc3b038e4d74b6407cee27a990e..e78fc567ff7d83b26161fd709092493107107cf9 100644 (file)
@@ -1222,7 +1222,7 @@ static int iwl_mvm_check_running_scans(struct iwl_mvm *mvm, int type)
        return -EIO;
 }
 
-#define SCAN_TIMEOUT (16 * HZ)
+#define SCAN_TIMEOUT (20 * HZ)
 
 void iwl_mvm_scan_timeout(unsigned long data)
 {
index fea4d3437e2f29543a2322af44d7892817125fe2..b23ab4a4504f7f140877ff734682b92607e276b6 100644 (file)
@@ -1852,12 +1852,18 @@ static struct iwl_mvm_sta *iwl_mvm_get_key_sta(struct iwl_mvm *mvm,
            mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
                u8 sta_id = mvmvif->ap_sta_id;
 
+               sta = rcu_dereference_check(mvm->fw_id_to_mac_id[sta_id],
+                                           lockdep_is_held(&mvm->mutex));
+
                /*
                 * It is possible that the 'sta' parameter is NULL,
                 * for example when a GTK is removed - the sta_id will then
                 * be the AP ID, and no station was passed by mac80211.
                 */
-               return iwl_mvm_sta_from_staid_protected(mvm, sta_id);
+               if (IS_ERR_OR_NULL(sta))
+                       return NULL;
+
+               return iwl_mvm_sta_from_mac80211(sta);
        }
 
        return NULL;
@@ -1955,6 +1961,14 @@ static int iwl_mvm_send_sta_igtk(struct iwl_mvm *mvm,
                struct ieee80211_key_seq seq;
                const u8 *pn;
 
+               switch (keyconf->cipher) {
+               case WLAN_CIPHER_SUITE_AES_CMAC:
+                       igtk_cmd.ctrl_flags |= cpu_to_le32(STA_KEY_FLG_CCM);
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
                memcpy(igtk_cmd.IGTK, keyconf->key, keyconf->keylen);
                ieee80211_get_key_rx_seq(keyconf, 0, &seq);
                pn = seq.aes_cmac.pn;
index fe19ace0d6a01bf75ff0f6e2d9ba38b6c32b2de3..b04cf30f3959221c99a4c53cd5f12345fee27017 100644 (file)
@@ -1149,7 +1149,7 @@ static void rtl8192eu_phy_iqcalibrate(struct rtl8xxxu_priv *priv,
 
                for (i = 0; i < retry; i++) {
                        path_b_ok = rtl8192eu_rx_iqk_path_b(priv);
-                       if (path_a_ok == 0x03) {
+                       if (path_b_ok == 0x03) {
                                val32 = rtl8xxxu_read32(priv,
                                                        REG_RX_POWER_BEFORE_IQK_B_2);
                                result[t][6] = (val32 >> 16) & 0x3ff;
index f7718ec685fadaee4e1460210b3733b5950acfc5..cea8350fbc7ec2f2e617199fd2bc8b8cb1df2fbb 100644 (file)
@@ -344,6 +344,8 @@ struct device *nd_pfn_create(struct nd_region *nd_region)
 int nd_pfn_validate(struct nd_pfn *nd_pfn, const char *sig)
 {
        u64 checksum, offset;
+       unsigned long align;
+       enum nd_pfn_mode mode;
        struct nd_namespace_io *nsio;
        struct nd_pfn_sb *pfn_sb = nd_pfn->pfn_sb;
        struct nd_namespace_common *ndns = nd_pfn->ndns;
@@ -386,22 +388,50 @@ int nd_pfn_validate(struct nd_pfn *nd_pfn, const char *sig)
                return -ENXIO;
        }
 
+       align = le32_to_cpu(pfn_sb->align);
+       offset = le64_to_cpu(pfn_sb->dataoff);
+       if (align == 0)
+               align = 1UL << ilog2(offset);
+       mode = le32_to_cpu(pfn_sb->mode);
+
        if (!nd_pfn->uuid) {
-               /* from probe we allocate */
+               /*
+                * When probing a namepace via nd_pfn_probe() the uuid
+                * is NULL (see: nd_pfn_devinit()) we init settings from
+                * pfn_sb
+                */
                nd_pfn->uuid = kmemdup(pfn_sb->uuid, 16, GFP_KERNEL);
                if (!nd_pfn->uuid)
                        return -ENOMEM;
+               nd_pfn->align = align;
+               nd_pfn->mode = mode;
        } else {
-               /* from init we validate */
+               /*
+                * When probing a pfn / dax instance we validate the
+                * live settings against the pfn_sb
+                */
                if (memcmp(nd_pfn->uuid, pfn_sb->uuid, 16) != 0)
                        return -ENODEV;
+
+               /*
+                * If the uuid validates, but other settings mismatch
+                * return EINVAL because userspace has managed to change
+                * the configuration without specifying new
+                * identification.
+                */
+               if (nd_pfn->align != align || nd_pfn->mode != mode) {
+                       dev_err(&nd_pfn->dev,
+                                       "init failed, settings mismatch\n");
+                       dev_dbg(&nd_pfn->dev, "align: %lx:%lx mode: %d:%d\n",
+                                       nd_pfn->align, align, nd_pfn->mode,
+                                       mode);
+                       return -EINVAL;
+               }
        }
 
-       if (nd_pfn->align == 0)
-               nd_pfn->align = le32_to_cpu(pfn_sb->align);
-       if (nd_pfn->align > nvdimm_namespace_capacity(ndns)) {
+       if (align > nvdimm_namespace_capacity(ndns)) {
                dev_err(&nd_pfn->dev, "alignment: %lx exceeds capacity %llx\n",
-                               nd_pfn->align, nvdimm_namespace_capacity(ndns));
+                               align, nvdimm_namespace_capacity(ndns));
                return -EINVAL;
        }
 
@@ -411,7 +441,6 @@ int nd_pfn_validate(struct nd_pfn *nd_pfn, const char *sig)
         * namespace has changed since the pfn superblock was
         * established.
         */
-       offset = le64_to_cpu(pfn_sb->dataoff);
        nsio = to_nd_namespace_io(&ndns->dev);
        if (offset >= resource_size(&nsio->res)) {
                dev_err(&nd_pfn->dev, "pfn array size exceeds capacity of %s\n",
@@ -419,10 +448,11 @@ int nd_pfn_validate(struct nd_pfn *nd_pfn, const char *sig)
                return -EBUSY;
        }
 
-       if ((nd_pfn->align && !IS_ALIGNED(offset, nd_pfn->align))
+       if ((align && !IS_ALIGNED(offset, align))
                        || !IS_ALIGNED(offset, PAGE_SIZE)) {
-               dev_err(&nd_pfn->dev, "bad offset: %#llx dax disabled\n",
-                               offset);
+               dev_err(&nd_pfn->dev,
+                               "bad offset: %#llx dax disabled align: %#lx\n",
+                               offset, align);
                return -ENXIO;
        }
 
@@ -502,7 +532,6 @@ static struct vmem_altmap *__nvdimm_setup_pfn(struct nd_pfn *nd_pfn,
        res->start += start_pad;
        res->end -= end_trunc;
 
-       nd_pfn->mode = le32_to_cpu(nd_pfn->pfn_sb->mode);
        if (nd_pfn->mode == PFN_MODE_RAM) {
                if (offset < SZ_8K)
                        return ERR_PTR(-EINVAL);
index 1a51584a382bf28dcf567a3fb039bc1a571cc144..d5fb55c0a9d95cdd2e6ac9cc99ca17f44b2d6ef7 100644 (file)
@@ -1394,19 +1394,22 @@ static int ns_cmp(void *priv, struct list_head *a, struct list_head *b)
        return nsa->ns_id - nsb->ns_id;
 }
 
-static struct nvme_ns *nvme_find_ns(struct nvme_ctrl *ctrl, unsigned nsid)
+static struct nvme_ns *nvme_find_get_ns(struct nvme_ctrl *ctrl, unsigned nsid)
 {
-       struct nvme_ns *ns;
-
-       lockdep_assert_held(&ctrl->namespaces_mutex);
+       struct nvme_ns *ns, *ret = NULL;
 
+       mutex_lock(&ctrl->namespaces_mutex);
        list_for_each_entry(ns, &ctrl->namespaces, list) {
-               if (ns->ns_id == nsid)
-                       return ns;
+               if (ns->ns_id == nsid) {
+                       kref_get(&ns->kref);
+                       ret = ns;
+                       break;
+               }
                if (ns->ns_id > nsid)
                        break;
        }
-       return NULL;
+       mutex_unlock(&ctrl->namespaces_mutex);
+       return ret;
 }
 
 static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
@@ -1415,8 +1418,6 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
        struct gendisk *disk;
        int node = dev_to_node(ctrl->dev);
 
-       lockdep_assert_held(&ctrl->namespaces_mutex);
-
        ns = kzalloc_node(sizeof(*ns), GFP_KERNEL, node);
        if (!ns)
                return;
@@ -1457,7 +1458,10 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
        if (nvme_revalidate_disk(ns->disk))
                goto out_free_disk;
 
-       list_add_tail_rcu(&ns->list, &ctrl->namespaces);
+       mutex_lock(&ctrl->namespaces_mutex);
+       list_add_tail(&ns->list, &ctrl->namespaces);
+       mutex_unlock(&ctrl->namespaces_mutex);
+
        kref_get(&ctrl->kref);
        if (ns->type == NVME_NS_LIGHTNVM)
                return;
@@ -1480,8 +1484,6 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid)
 
 static void nvme_ns_remove(struct nvme_ns *ns)
 {
-       lockdep_assert_held(&ns->ctrl->namespaces_mutex);
-
        if (test_and_set_bit(NVME_NS_REMOVING, &ns->flags))
                return;
 
@@ -1494,8 +1496,11 @@ static void nvme_ns_remove(struct nvme_ns *ns)
                blk_mq_abort_requeue_list(ns->queue);
                blk_cleanup_queue(ns->queue);
        }
+
+       mutex_lock(&ns->ctrl->namespaces_mutex);
        list_del_init(&ns->list);
-       synchronize_rcu();
+       mutex_unlock(&ns->ctrl->namespaces_mutex);
+
        nvme_put_ns(ns);
 }
 
@@ -1503,10 +1508,11 @@ static void nvme_validate_ns(struct nvme_ctrl *ctrl, unsigned nsid)
 {
        struct nvme_ns *ns;
 
-       ns = nvme_find_ns(ctrl, nsid);
+       ns = nvme_find_get_ns(ctrl, nsid);
        if (ns) {
                if (revalidate_disk(ns->disk))
                        nvme_ns_remove(ns);
+               nvme_put_ns(ns);
        } else
                nvme_alloc_ns(ctrl, nsid);
 }
@@ -1535,9 +1541,11 @@ static int nvme_scan_ns_list(struct nvme_ctrl *ctrl, unsigned nn)
                        nvme_validate_ns(ctrl, nsid);
 
                        while (++prev < nsid) {
-                               ns = nvme_find_ns(ctrl, prev);
-                               if (ns)
+                               ns = nvme_find_get_ns(ctrl, prev);
+                               if (ns) {
                                        nvme_ns_remove(ns);
+                                       nvme_put_ns(ns);
+                               }
                        }
                }
                nn -= j;
@@ -1552,8 +1560,6 @@ static void nvme_scan_ns_sequential(struct nvme_ctrl *ctrl, unsigned nn)
        struct nvme_ns *ns, *next;
        unsigned i;
 
-       lockdep_assert_held(&ctrl->namespaces_mutex);
-
        for (i = 1; i <= nn; i++)
                nvme_validate_ns(ctrl, i);
 
@@ -1576,7 +1582,6 @@ static void nvme_scan_work(struct work_struct *work)
        if (nvme_identify_ctrl(ctrl, &id))
                return;
 
-       mutex_lock(&ctrl->namespaces_mutex);
        nn = le32_to_cpu(id->nn);
        if (ctrl->vs >= NVME_VS(1, 1) &&
            !(ctrl->quirks & NVME_QUIRK_IDENTIFY_CNS)) {
@@ -1585,6 +1590,7 @@ static void nvme_scan_work(struct work_struct *work)
        }
        nvme_scan_ns_sequential(ctrl, nn);
  done:
+       mutex_lock(&ctrl->namespaces_mutex);
        list_sort(NULL, &ctrl->namespaces, ns_cmp);
        mutex_unlock(&ctrl->namespaces_mutex);
        kfree(id);
@@ -1604,6 +1610,11 @@ void nvme_queue_scan(struct nvme_ctrl *ctrl)
 }
 EXPORT_SYMBOL_GPL(nvme_queue_scan);
 
+/*
+ * This function iterates the namespace list unlocked to allow recovery from
+ * controller failure. It is up to the caller to ensure the namespace list is
+ * not modified by scan work while this function is executing.
+ */
 void nvme_remove_namespaces(struct nvme_ctrl *ctrl)
 {
        struct nvme_ns *ns, *next;
@@ -1617,10 +1628,8 @@ void nvme_remove_namespaces(struct nvme_ctrl *ctrl)
        if (ctrl->state == NVME_CTRL_DEAD)
                nvme_kill_queues(ctrl);
 
-       mutex_lock(&ctrl->namespaces_mutex);
        list_for_each_entry_safe(ns, next, &ctrl->namespaces, list)
                nvme_ns_remove(ns);
-       mutex_unlock(&ctrl->namespaces_mutex);
 }
 EXPORT_SYMBOL_GPL(nvme_remove_namespaces);
 
@@ -1791,11 +1800,8 @@ void nvme_kill_queues(struct nvme_ctrl *ctrl)
 {
        struct nvme_ns *ns;
 
-       rcu_read_lock();
-       list_for_each_entry_rcu(ns, &ctrl->namespaces, list) {
-               if (!kref_get_unless_zero(&ns->kref))
-                       continue;
-
+       mutex_lock(&ctrl->namespaces_mutex);
+       list_for_each_entry(ns, &ctrl->namespaces, list) {
                /*
                 * Revalidating a dead namespace sets capacity to 0. This will
                 * end buffered writers dirtying pages that can't be synced.
@@ -1806,10 +1812,8 @@ void nvme_kill_queues(struct nvme_ctrl *ctrl)
                blk_set_queue_dying(ns->queue);
                blk_mq_abort_requeue_list(ns->queue);
                blk_mq_start_stopped_hw_queues(ns->queue, true);
-
-               nvme_put_ns(ns);
        }
-       rcu_read_unlock();
+       mutex_unlock(&ctrl->namespaces_mutex);
 }
 EXPORT_SYMBOL_GPL(nvme_kill_queues);
 
@@ -1817,8 +1821,8 @@ void nvme_stop_queues(struct nvme_ctrl *ctrl)
 {
        struct nvme_ns *ns;
 
-       rcu_read_lock();
-       list_for_each_entry_rcu(ns, &ctrl->namespaces, list) {
+       mutex_lock(&ctrl->namespaces_mutex);
+       list_for_each_entry(ns, &ctrl->namespaces, list) {
                spin_lock_irq(ns->queue->queue_lock);
                queue_flag_set(QUEUE_FLAG_STOPPED, ns->queue);
                spin_unlock_irq(ns->queue->queue_lock);
@@ -1826,7 +1830,7 @@ void nvme_stop_queues(struct nvme_ctrl *ctrl)
                blk_mq_cancel_requeue_work(ns->queue);
                blk_mq_stop_hw_queues(ns->queue);
        }
-       rcu_read_unlock();
+       mutex_unlock(&ctrl->namespaces_mutex);
 }
 EXPORT_SYMBOL_GPL(nvme_stop_queues);
 
@@ -1834,13 +1838,13 @@ void nvme_start_queues(struct nvme_ctrl *ctrl)
 {
        struct nvme_ns *ns;
 
-       rcu_read_lock();
-       list_for_each_entry_rcu(ns, &ctrl->namespaces, list) {
+       mutex_lock(&ctrl->namespaces_mutex);
+       list_for_each_entry(ns, &ctrl->namespaces, list) {
                queue_flag_clear_unlocked(QUEUE_FLAG_STOPPED, ns->queue);
                blk_mq_start_stopped_hw_queues(ns->queue, true);
                blk_mq_kick_requeue_list(ns->queue);
        }
-       rcu_read_unlock();
+       mutex_unlock(&ctrl->namespaces_mutex);
 }
 EXPORT_SYMBOL_GPL(nvme_start_queues);
 
index 95ab6b2a0de5e1bc8dda4bdd640d2ff725f436ae..58dff80e9386d67d43ac5a1b9f67ba55f7c49788 100644 (file)
@@ -109,8 +109,8 @@ static int bcm_ns_usb2_probe(struct platform_device *pdev)
        }
 
        usb2->phy = devm_phy_create(dev, NULL, &ops);
-       if (IS_ERR(dev))
-               return PTR_ERR(dev);
+       if (IS_ERR(usb2->phy))
+               return PTR_ERR(usb2->phy);
 
        phy_set_drvdata(usb2->phy, usb2);
        platform_set_drvdata(pdev, usb2);
index 3acd2a1808dfbf9ccfa5d58467b6defdb793c2bb..213e2e15339c44dff742b2394455d7138e0d524e 100644 (file)
@@ -1143,7 +1143,8 @@ static int miphy28lp_probe_resets(struct device_node *node,
        struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
        int err;
 
-       miphy_phy->miphy_rst = of_reset_control_get(node, "miphy-sw-rst");
+       miphy_phy->miphy_rst =
+               of_reset_control_get_shared(node, "miphy-sw-rst");
 
        if (IS_ERR(miphy_phy->miphy_rst)) {
                dev_err(miphy_dev->dev,
index 76bb88f0700a72b8d06480daf4d8e2aa8b0b51de..4be3f5dbbc9f1d8b9dd416e8b930cb24b6ee3919 100644 (file)
@@ -144,12 +144,6 @@ static void rcar_gen3_init_for_peri(struct rcar_gen3_chan *ch)
        extcon_set_cable_state_(ch->extcon, EXTCON_USB, true);
 }
 
-static bool rcar_gen3_check_vbus(struct rcar_gen3_chan *ch)
-{
-       return !!(readl(ch->base + USB2_ADPCTRL) &
-                 USB2_ADPCTRL_OTGSESSVLD);
-}
-
 static bool rcar_gen3_check_id(struct rcar_gen3_chan *ch)
 {
        return !!(readl(ch->base + USB2_ADPCTRL) & USB2_ADPCTRL_IDDIG);
@@ -157,13 +151,7 @@ static bool rcar_gen3_check_id(struct rcar_gen3_chan *ch)
 
 static void rcar_gen3_device_recognition(struct rcar_gen3_chan *ch)
 {
-       bool is_host = true;
-
-       /* B-device? */
-       if (rcar_gen3_check_id(ch) && rcar_gen3_check_vbus(ch))
-               is_host = false;
-
-       if (is_host)
+       if (!rcar_gen3_check_id(ch))
                rcar_gen3_init_for_host(ch);
        else
                rcar_gen3_init_for_peri(ch);
index 793ecb6d87bcaa2a56a914b2745bea6453929637..8b267a746576f03be24adf5dec028d45289e31ea 100644 (file)
@@ -90,7 +90,7 @@ static int rockchip_dp_phy_probe(struct platform_device *pdev)
                return -ENODEV;
 
        dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
-       if (IS_ERR(dp))
+       if (!dp)
                return -ENOMEM;
 
        dp->dev = dev;
index 1d5ae5f8ef694cf9ca389dd3a0742bec189a6955..b1f44ab669fb5cef099a54daf07eeb26f76fc770 100644 (file)
@@ -105,13 +105,13 @@ static int stih407_usb2_picophy_probe(struct platform_device *pdev)
        phy_dev->dev = dev;
        dev_set_drvdata(dev, phy_dev);
 
-       phy_dev->rstc = devm_reset_control_get(dev, "global");
+       phy_dev->rstc = devm_reset_control_get_shared(dev, "global");
        if (IS_ERR(phy_dev->rstc)) {
                dev_err(dev, "failed to ctrl picoPHY reset\n");
                return PTR_ERR(phy_dev->rstc);
        }
 
-       phy_dev->rstport = devm_reset_control_get(dev, "port");
+       phy_dev->rstport = devm_reset_control_get_exclusive(dev, "port");
        if (IS_ERR(phy_dev->rstport)) {
                dev_err(dev, "failed to ctrl picoPHY reset\n");
                return PTR_ERR(phy_dev->rstport);
index bae54f7a1f4877b88297858783b0b864783ec1fe..de3101fbbf40e1f3c77e7a1f22c6929c2db8fb01 100644 (file)
@@ -175,7 +175,7 @@ static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
 {
        struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy);
        u32 temp, usbc_bit = BIT(phy->index * 2);
-       void *phyctl = phy_data->base + phy_data->cfg->phyctl_offset;
+       void __iomem *phyctl = phy_data->base + phy_data->cfg->phyctl_offset;
        int i;
 
        mutex_lock(&phy_data->mutex);
@@ -514,9 +514,9 @@ static int sun4i_usb_phy_remove(struct platform_device *pdev)
 
        if (data->vbus_power_nb_registered)
                power_supply_unreg_notifier(&data->vbus_power_nb);
-       if (data->id_det_irq >= 0)
+       if (data->id_det_irq > 0)
                devm_free_irq(dev, data->id_det_irq, data);
-       if (data->vbus_det_irq >= 0)
+       if (data->vbus_det_irq > 0)
                devm_free_irq(dev, data->vbus_det_irq, data);
 
        cancel_delayed_work_sync(&data->detect);
@@ -645,11 +645,11 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
 
        data->id_det_irq = gpiod_to_irq(data->id_det_gpio);
        data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio);
-       if ((data->id_det_gpio && data->id_det_irq < 0) ||
-           (data->vbus_det_gpio && data->vbus_det_irq < 0))
+       if ((data->id_det_gpio && data->id_det_irq <= 0) ||
+           (data->vbus_det_gpio && data->vbus_det_irq <= 0))
                data->phy0_poll = true;
 
-       if (data->id_det_irq >= 0) {
+       if (data->id_det_irq > 0) {
                ret = devm_request_irq(dev, data->id_det_irq,
                                sun4i_usb_phy0_id_vbus_det_irq,
                                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
@@ -660,7 +660,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
                }
        }
 
-       if (data->vbus_det_irq >= 0) {
+       if (data->vbus_det_irq > 0) {
                ret = devm_request_irq(dev, data->vbus_det_irq,
                                sun4i_usb_phy0_id_vbus_det_irq,
                                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
index e4bc1151e04f3c2d7ed0eac8b659ae27244432ba..42a5c1dddfefaf7414eca1809ef7d1bf75a11628 100644 (file)
@@ -23,7 +23,7 @@ obj-$(CONFIG_PINCTRL_PISTACHIO)       += pinctrl-pistachio.o
 obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o
 obj-$(CONFIG_PINCTRL_SINGLE)   += pinctrl-single.o
 obj-$(CONFIG_PINCTRL_SIRF)     += sirf/
-obj-$(CONFIG_PINCTRL_TEGRA)    += tegra/
+obj-$(CONFIG_ARCH_TEGRA)       += tegra/
 obj-$(CONFIG_PINCTRL_TZ1090)   += pinctrl-tz1090.o
 obj-$(CONFIG_PINCTRL_TZ1090_PDC)       += pinctrl-tz1090-pdc.o
 obj-$(CONFIG_PINCTRL_U300)     += pinctrl-u300.o
index 47ccfcc8a647ccacdd0880ab770dc2c102d2ba9c..eccb47480e1db12d0212ea54c47b94d4b79d5cc8 100644 (file)
@@ -209,9 +209,9 @@ static int imx_pmx_set(struct pinctrl_dev *pctldev, unsigned selector,
                pin_reg = &info->pin_regs[pin_id];
 
                if (pin_reg->mux_reg == -1) {
-                       dev_err(ipctl->dev, "Pin(%s) does not support mux function\n",
+                       dev_dbg(ipctl->dev, "Pin(%s) does not support mux function\n",
                                info->pins[pin_id].name);
-                       return -EINVAL;
+                       continue;
                }
 
                if (info->flags & SHARE_MUX_CONF_REG) {
index 677a811b3a6ffcb604d8a6c4cb836ec8dd81575e..7abfd42e8ffdf86953764ad2fa2c1c9bf70fcc57 100644 (file)
@@ -401,9 +401,9 @@ static const struct byt_simple_func_mux byt_score_sata_mux[] = {
 static const unsigned int byt_score_plt_clk0_pins[] = { 96 };
 static const unsigned int byt_score_plt_clk1_pins[] = { 97 };
 static const unsigned int byt_score_plt_clk2_pins[] = { 98 };
-static const unsigned int byt_score_plt_clk4_pins[] = { 99 };
-static const unsigned int byt_score_plt_clk5_pins[] = { 100 };
-static const unsigned int byt_score_plt_clk3_pins[] = { 101 };
+static const unsigned int byt_score_plt_clk3_pins[] = { 99 };
+static const unsigned int byt_score_plt_clk4_pins[] = { 100 };
+static const unsigned int byt_score_plt_clk5_pins[] = { 101 };
 static const struct byt_simple_func_mux byt_score_plt_clk_mux[] = {
        SIMPLE_FUNC("plt_clk", 1),
 };
index cf9bafa10acfb51ad42af96cf6580d6de48c1b1d..bfdf720db270d8b2c47007455406d9887ed028c6 100644 (file)
@@ -1580,6 +1580,9 @@ static inline void pcs_irq_set(struct pcs_soc_data *pcs_soc,
                else
                        mask &= ~soc_mask;
                pcs->write(mask, pcswi->reg);
+
+               /* flush posted write */
+               mask = pcs->read(pcswi->reg);
                raw_spin_unlock(&pcs->lock);
        }
 
index a927379b67949d1603eaf20e180febbc4e1c0823..d9ea2be69cc4a3a682045c345e0ce88800717a58 100644 (file)
@@ -1,4 +1,4 @@
-obj-y                                  += pinctrl-tegra.o
+obj-$(CONFIG_PINCTRL_TEGRA)            += pinctrl-tegra.o
 obj-$(CONFIG_PINCTRL_TEGRA20)          += pinctrl-tegra20.o
 obj-$(CONFIG_PINCTRL_TEGRA30)          += pinctrl-tegra30.o
 obj-$(CONFIG_PINCTRL_TEGRA114)         += pinctrl-tegra114.o
index 6d8ee3b1587276494274617d65029534df1d1af6..8abd80dbcbed7974b4ace4265dd9dad1bca89edd 100644 (file)
@@ -151,13 +151,19 @@ static long ec_device_ioctl_xcmd(struct cros_ec_dev *ec, void __user *arg)
                goto exit;
        }
 
+       if (u_cmd.outsize != s_cmd->outsize ||
+           u_cmd.insize != s_cmd->insize) {
+               ret = -EINVAL;
+               goto exit;
+       }
+
        s_cmd->command += ec->cmd_offset;
        ret = cros_ec_cmd_xfer(ec->ec_dev, s_cmd);
        /* Only copy data to userland if data was received. */
        if (ret < 0)
                goto exit;
 
-       if (copy_to_user(arg, s_cmd, sizeof(*s_cmd) + u_cmd.insize))
+       if (copy_to_user(arg, s_cmd, sizeof(*s_cmd) + s_cmd->insize))
                ret = -EFAULT;
 exit:
        kfree(s_cmd);
index 456987c88baab9f4b8a0d2b7224b10d659b8bddc..b13cd074c52afc90c086bc16cc011b7adb0ac91f 100644 (file)
@@ -565,11 +565,12 @@ static int power_supply_read_temp(struct thermal_zone_device *tzd,
 
        WARN_ON(tzd == NULL);
        psy = tzd->devdata;
-       ret = psy->desc->get_property(psy, POWER_SUPPLY_PROP_TEMP, &val);
+       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_TEMP, &val);
+       if (ret)
+               return ret;
 
        /* Convert tenths of degree Celsius to milli degree Celsius. */
-       if (!ret)
-               *temp = val.intval * 100;
+       *temp = val.intval * 100;
 
        return ret;
 }
@@ -612,10 +613,12 @@ static int ps_get_max_charge_cntl_limit(struct thermal_cooling_device *tcd,
        int ret;
 
        psy = tcd->devdata;
-       ret = psy->desc->get_property(psy,
-               POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, &val);
-       if (!ret)
-               *state = val.intval;
+       ret = power_supply_get_property(psy,
+                       POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, &val);
+       if (ret)
+               return ret;
+
+       *state = val.intval;
 
        return ret;
 }
@@ -628,10 +631,12 @@ static int ps_get_cur_chrage_cntl_limit(struct thermal_cooling_device *tcd,
        int ret;
 
        psy = tcd->devdata;
-       ret = psy->desc->get_property(psy,
-               POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val);
-       if (!ret)
-               *state = val.intval;
+       ret = power_supply_get_property(psy,
+                       POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val);
+       if (ret)
+               return ret;
+
+       *state = val.intval;
 
        return ret;
 }
index d9f56730c735819010e9efff4f8499814c7fbe58..73dfae41def8a659978eec9a738448f604cb1973 100644 (file)
@@ -197,6 +197,7 @@ static int tps65217_charger_probe(struct platform_device *pdev)
 {
        struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
        struct tps65217_charger *charger;
+       struct power_supply_config cfg = {};
        int ret;
 
        dev_dbg(&pdev->dev, "%s\n", __func__);
@@ -208,9 +209,12 @@ static int tps65217_charger_probe(struct platform_device *pdev)
        charger->tps = tps;
        charger->dev = &pdev->dev;
 
+       cfg.of_node = pdev->dev.of_node;
+       cfg.drv_data = charger;
+
        charger->ac = devm_power_supply_register(&pdev->dev,
                                                 &tps65217_charger_desc,
-                                                NULL);
+                                                &cfg);
        if (IS_ERR(charger->ac)) {
                dev_err(&pdev->dev, "failed: power supply register\n");
                return PTR_ERR(charger->ac);
index 38a8bbe7481008232e7336777850d683afb765be..83797d89c30fe88665c5d7214c391a0a571cfe88 100644 (file)
@@ -195,7 +195,7 @@ static void parport_detach(struct parport *port)
        struct pps_client_pp *device;
 
        /* FIXME: oooh, this is ugly! */
-       if (strcmp(pardev->name, KBUILD_MODNAME))
+       if (!pardev || strcmp(pardev->name, KBUILD_MODNAME))
                /* not our port */
                return;
 
index 63cd5e68c86494724eae44502bed2d7f590cbf78..3a6d0290c54c0fbd0f1c82ffcd5329c2facc56ae 100644 (file)
@@ -296,7 +296,7 @@ static int anatop_regulator_probe(struct platform_device *pdev)
                if (!sreg->sel && !strcmp(sreg->name, "vddpu"))
                        sreg->sel = 22;
 
-               if (!sreg->sel) {
+               if (!sreg->bypass && !sreg->sel) {
                        dev_err(&pdev->dev, "Failed to read a valid default voltage selector.\n");
                        return -EINVAL;
                }
index 321e804aeab0a33c075caafb95ccc7bff4bf417f..a1b49a6d538f1493489f2e45092feb6dd0b96219 100644 (file)
@@ -123,6 +123,9 @@ static int max77620_regulator_set_fps_src(struct max77620_regulator *pmic,
        unsigned int val;
        int ret;
 
+       if (!rinfo)
+               return 0;
+
        switch (fps_src) {
        case MAX77620_FPS_SRC_0:
        case MAX77620_FPS_SRC_1:
@@ -171,6 +174,9 @@ static int max77620_regulator_set_fps_slots(struct max77620_regulator *pmic,
        int pd = rpdata->active_fps_pd_slot;
        int ret = 0;
 
+       if (!rinfo)
+               return 0;
+
        if (is_suspend) {
                pu = rpdata->suspend_fps_pu_slot;
                pd = rpdata->suspend_fps_pd_slot;
@@ -680,7 +686,6 @@ static struct max77620_regulator_info max77620_regs_info[MAX77620_NUM_REGS] = {
        RAIL_SD(SD1, sd1, "in-sd1", SD1, 600000, 1550000, 12500, 0x22, SD1),
        RAIL_SD(SD2, sd2, "in-sd2", SDX, 600000, 3787500, 12500, 0xFF, NONE),
        RAIL_SD(SD3, sd3, "in-sd3", SDX, 600000, 3787500, 12500, 0xFF, NONE),
-       RAIL_SD(SD4, sd4, "in-sd4", SDX, 600000, 3787500, 12500, 0xFF, NONE),
 
        RAIL_LDO(LDO0, ldo0, "in-ldo0-1", N, 800000, 2375000, 25000),
        RAIL_LDO(LDO1, ldo1, "in-ldo0-1", N, 800000, 2375000, 25000),
index 526bf23dcb49a543032c6ac17da76c2e09009545..6c7fe4778793758478d9a27e3f9d11f6d4b4a1b2 100644 (file)
@@ -152,7 +152,6 @@ static const struct regulator_ops rpm_smps_ldo_ops_fixed = {
        .enable = rpm_reg_enable,
        .disable = rpm_reg_disable,
        .is_enabled = rpm_reg_is_enabled,
-       .list_voltage = regulator_list_voltage_linear_range,
 
        .get_voltage = rpm_reg_get_voltage,
        .set_voltage = rpm_reg_set_voltage,
index 80b1979e8d955f6022e7810a4b65a62f13ec1224..df036b872b050b835d0fea6f620726dc8876225b 100644 (file)
@@ -1051,6 +1051,7 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev)
                qeth_l2_set_offline(cgdev);
 
        if (card->dev) {
+               netif_napi_del(&card->napi);
                unregister_netdev(card->dev);
                card->dev = NULL;
        }
index ac544330daeb7cffaccc37306f041deffd1475bb..709b52339ff9a5907812d315e65bca964ddd11be 100644 (file)
@@ -3226,6 +3226,7 @@ static void qeth_l3_remove_device(struct ccwgroup_device *cgdev)
                qeth_l3_set_offline(cgdev);
 
        if (card->dev) {
+               netif_napi_del(&card->napi);
                unregister_netdev(card->dev);
                card->dev = NULL;
        }
index d6a691e27d33317c72d785340b5b371378932741..d6803a9e5ab8a49ea045bcbfafce7029ef439c5d 100644 (file)
@@ -10093,6 +10093,7 @@ static int ipr_probe_ioa(struct pci_dev *pdev,
                ioa_cfg->intr_flag = IPR_USE_MSI;
        else {
                ioa_cfg->intr_flag = IPR_USE_LSI;
+               ioa_cfg->clear_isr = 1;
                ioa_cfg->nvectors = 1;
                dev_info(&pdev->dev, "Cannot enable MSI.\n");
        }
index 5649c200d37ce12f80fc49ad860a185c1a52af5f..a92a62dea7934429e48c252f77405a8dd70b6400 100644 (file)
@@ -2548,7 +2548,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
        if (!vha->flags.online)
                return;
 
-       if (rsp->msix->cpuid != smp_processor_id()) {
+       if (rsp->msix && rsp->msix->cpuid != smp_processor_id()) {
                /* if kernel does not notify qla of IRQ's CPU change,
                 * then set it here.
                 */
index ff41c310c900a5760bf33423a5db4362c5dfc25b..eaccd651ccda0d239af91ebfb6dfdbd97ac340e3 100644 (file)
@@ -429,7 +429,7 @@ static struct scsi_dev_info_list *scsi_dev_info_list_find(const char *vendor,
         * here, and we don't know what device it is
         * trying to work with, leave it as-is.
         */
-       vmax = 8;       /* max length of vendor */
+       vmax = sizeof(devinfo->vendor);
        vskip = vendor;
        while (vmax > 0 && *vskip == ' ') {
                vmax--;
@@ -439,7 +439,7 @@ static struct scsi_dev_info_list *scsi_dev_info_list_find(const char *vendor,
        while (vmax > 0 && vskip[vmax - 1] == ' ')
                --vmax;
 
-       mmax = 16;      /* max length of model */
+       mmax = sizeof(devinfo->model);
        mskip = model;
        while (mmax > 0 && *mskip == ' ') {
                mmax--;
@@ -455,10 +455,12 @@ static struct scsi_dev_info_list *scsi_dev_info_list_find(const char *vendor,
                         * Behave like the older version of get_device_flags.
                         */
                        if (memcmp(devinfo->vendor, vskip, vmax) ||
-                                       devinfo->vendor[vmax])
+                                       (vmax < sizeof(devinfo->vendor) &&
+                                               devinfo->vendor[vmax]))
                                continue;
                        if (memcmp(devinfo->model, mskip, mmax) ||
-                                       devinfo->model[mmax])
+                                       (mmax < sizeof(devinfo->model) &&
+                                               devinfo->model[mmax]))
                                continue;
                        return devinfo;
                } else {
index cd89682065b98d7a06a7d7064ec30aff2a5e7fef..1026e180eed79019f0ec55d9ed5ba427ce71cefa 100644 (file)
@@ -578,7 +578,7 @@ static int rockchip_spi_transfer_one(
                struct spi_device *spi,
                struct spi_transfer *xfer)
 {
-       int ret = 1;
+       int ret = 0;
        struct rockchip_spi *rs = spi_master_get_devdata(master);
 
        WARN_ON(readl_relaxed(rs->regs + ROCKCHIP_SPI_SSIENR) &&
@@ -627,6 +627,8 @@ static int rockchip_spi_transfer_one(
                        spi_enable_chip(rs, 1);
                        ret = rockchip_spi_prepare_dma(rs);
                }
+               /* successful DMA prepare means the transfer is in progress */
+               ret = ret ? ret : 1;
        } else {
                spi_enable_chip(rs, 1);
                ret = rockchip_spi_pio_transfer(rs);
index 1ddd9e2309b685576a8c63abc1770f731a7acd0c..cf007f3b83ec92f1d8f976ea2b68978dbdd36f20 100644 (file)
@@ -173,13 +173,17 @@ static int sun4i_spi_transfer_one(struct spi_master *master,
 {
        struct sun4i_spi *sspi = spi_master_get_devdata(master);
        unsigned int mclk_rate, div, timeout;
+       unsigned int start, end, tx_time;
        unsigned int tx_len = 0;
        int ret = 0;
        u32 reg;
 
        /* We don't support transfer larger than the FIFO */
        if (tfr->len > SUN4I_FIFO_DEPTH)
-               return -EINVAL;
+               return -EMSGSIZE;
+
+       if (tfr->tx_buf && tfr->len >= SUN4I_FIFO_DEPTH)
+               return -EMSGSIZE;
 
        reinit_completion(&sspi->done);
        sspi->tx_buf = tfr->tx_buf;
@@ -269,8 +273,12 @@ static int sun4i_spi_transfer_one(struct spi_master *master,
        sun4i_spi_write(sspi, SUN4I_BURST_CNT_REG, SUN4I_BURST_CNT(tfr->len));
        sun4i_spi_write(sspi, SUN4I_XMIT_CNT_REG, SUN4I_XMIT_CNT(tx_len));
 
-       /* Fill the TX FIFO */
-       sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH);
+       /*
+        * Fill the TX FIFO
+        * Filling the FIFO fully causes timeout for some reason
+        * at least on spi2 on A10s
+        */
+       sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH - 1);
 
        /* Enable the interrupts */
        sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, SUN4I_INT_CTL_TC);
@@ -279,9 +287,16 @@ static int sun4i_spi_transfer_one(struct spi_master *master,
        reg = sun4i_spi_read(sspi, SUN4I_CTL_REG);
        sun4i_spi_write(sspi, SUN4I_CTL_REG, reg | SUN4I_CTL_XCH);
 
+       tx_time = max(tfr->len * 8 * 2 / (tfr->speed_hz / 1000), 100U);
+       start = jiffies;
        timeout = wait_for_completion_timeout(&sspi->done,
-                                             msecs_to_jiffies(1000));
+                                             msecs_to_jiffies(tx_time));
+       end = jiffies;
        if (!timeout) {
+               dev_warn(&master->dev,
+                        "%s: timeout transferring %u bytes@%iHz for %i(%i)ms",
+                        dev_name(&spi->dev), tfr->len, tfr->speed_hz,
+                        jiffies_to_msecs(end - start), tx_time);
                ret = -ETIMEDOUT;
                goto out;
        }
index 42e2c4bd690a253b423f42b0dfc6d1fc7c379ae9..7fce79a60608873d42af3b4b5203b4a94942ade1 100644 (file)
@@ -160,6 +160,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
 {
        struct sun6i_spi *sspi = spi_master_get_devdata(master);
        unsigned int mclk_rate, div, timeout;
+       unsigned int start, end, tx_time;
        unsigned int tx_len = 0;
        int ret = 0;
        u32 reg;
@@ -269,9 +270,16 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
        reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
        sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg | SUN6I_TFR_CTL_XCH);
 
+       tx_time = max(tfr->len * 8 * 2 / (tfr->speed_hz / 1000), 100U);
+       start = jiffies;
        timeout = wait_for_completion_timeout(&sspi->done,
-                                             msecs_to_jiffies(1000));
+                                             msecs_to_jiffies(tx_time));
+       end = jiffies;
        if (!timeout) {
+               dev_warn(&master->dev,
+                        "%s: timeout transferring %u bytes@%iHz for %i(%i)ms",
+                        dev_name(&spi->dev), tfr->len, tfr->speed_hz,
+                        jiffies_to_msecs(end - start), tx_time);
                ret = -ETIMEDOUT;
                goto out;
        }
index 443f664534e144fd388e2baba7f4d9bb49134772..29ea8d2f982498c4290e4afc15093165656e7897 100644 (file)
@@ -646,6 +646,13 @@ static int ti_qspi_probe(struct platform_device *pdev)
 
 static int ti_qspi_remove(struct platform_device *pdev)
 {
+       struct ti_qspi *qspi = platform_get_drvdata(pdev);
+       int rc;
+
+       rc = spi_master_suspend(qspi->master);
+       if (rc)
+               return rc;
+
        pm_runtime_put_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
 
index a8f533af9ecaeffcd39143ca1379dd8186714179..ec12181822e659a508a977caf5e8acf43b7472ed 100644 (file)
@@ -594,7 +594,7 @@ static ssize_t sca3000_read_frequency(struct device *dev,
                goto error_ret_mut;
        ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL);
        mutex_unlock(&st->lock);
-       if (ret)
+       if (ret < 0)
                goto error_ret;
        val = ret;
        if (base_freq > 0)
index 825da0769936ace09907977be92392dda5447407..9587fa86dc69589fc34aa0105584ef3b24add210 100644 (file)
@@ -21,7 +21,7 @@ static int ad7606_spi_read_block(struct device *dev,
 {
        struct spi_device *spi = to_spi_device(dev);
        int i, ret;
-       unsigned short *data;
+       unsigned short *data = buf;
        __be16 *bdata = buf;
 
        ret = spi_read(spi, buf, count * 2);
index 9f43976f4ef217f822916c5cead94abfeb764385..170ac980abcb84f1c4079b99187d6a18262ca1a4 100644 (file)
@@ -444,10 +444,10 @@ static ssize_t ad5933_store(struct device *dev,
                st->settling_cycles = val;
 
                /* 2x, 4x handling, see datasheet */
-               if (val > 511)
-                       val = (val >> 1) | (1 << 9);
-               else if (val > 1022)
+               if (val > 1022)
                        val = (val >> 2) | (3 << 9);
+               else if (val > 511)
+                       val = (val >> 1) | (1 << 9);
 
                dat = cpu_to_be16(val);
                ret = ad5933_i2c_write(st->client,
index f856c4544eeaea7ac243e4a0fb2a6383524f60b0..51e0d32883ba7d3bbb5b626dcb6314a49d210dff 100644 (file)
@@ -667,8 +667,11 @@ static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty)
                fsi = tty->driver_data;
        else
                fsi = tty->link->driver_data;
-       devpts_kill_index(fsi, tty->index);
-       devpts_release(fsi);
+
+       if (fsi) {
+               devpts_kill_index(fsi, tty->index);
+               devpts_release(fsi);
+       }
 }
 
 static const struct tty_operations ptm_unix98_ops = {
index f973bfce5d089256086b945fb37148fffebf902d..1e93a37e27f05091c6ade07f2798a514c55ae2b8 100644 (file)
@@ -366,34 +366,22 @@ static void to_utf8(struct vc_data *vc, uint c)
 
 static void do_compute_shiftstate(void)
 {
-       unsigned int i, j, k, sym, val;
+       unsigned int k, sym, val;
 
        shift_state = 0;
        memset(shift_down, 0, sizeof(shift_down));
 
-       for (i = 0; i < ARRAY_SIZE(key_down); i++) {
-
-               if (!key_down[i])
+       for_each_set_bit(k, key_down, min(NR_KEYS, KEY_CNT)) {
+               sym = U(key_maps[0][k]);
+               if (KTYP(sym) != KT_SHIFT && KTYP(sym) != KT_SLOCK)
                        continue;
 
-               k = i * BITS_PER_LONG;
-
-               for (j = 0; j < BITS_PER_LONG; j++, k++) {
-
-                       if (!test_bit(k, key_down))
-                               continue;
+               val = KVAL(sym);
+               if (val == KVAL(K_CAPSSHIFT))
+                       val = KVAL(K_SHIFT);
 
-                       sym = U(key_maps[0][k]);
-                       if (KTYP(sym) != KT_SHIFT && KTYP(sym) != KT_SLOCK)
-                               continue;
-
-                       val = KVAL(sym);
-                       if (val == KVAL(K_CAPSSHIFT))
-                               val = KVAL(K_SHIFT);
-
-                       shift_down[val]++;
-                       shift_state |= (1 << val);
-               }
+               shift_down[val]++;
+               shift_state |= BIT(val);
        }
 }
 
index dc125322f48f9d8c958ff09ce6e0406e77d48882..5b0fe97c46ca9fb0bff6b66e0638226bf1b5781d 100644 (file)
@@ -750,6 +750,7 @@ static void visual_init(struct vc_data *vc, int num, int init)
        vc->vc_complement_mask = 0;
        vc->vc_can_do_color = 0;
        vc->vc_panic_force_write = false;
+       vc->vc_cur_blink_ms = DEFAULT_CURSOR_BLINK_MS;
        vc->vc_sw->con_init(vc, init);
        if (!vc->vc_complement_mask)
                vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
index 9059b7dc185ee7dcb5e7d33d123337ff862fa6bd..2f537bbdda093d90285789cd92f77da608dbbe14 100644 (file)
@@ -21,6 +21,7 @@
  * 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/mutex.h>
@@ -450,3 +451,4 @@ int otg_statemachine(struct otg_fsm *fsm)
        return fsm->state_changed;
 }
 EXPORT_SYMBOL_GPL(otg_statemachine);
+MODULE_LICENSE("GPL");
index 34b837ae1ed79ebb909c47e3fa8c75345714b88b..d2e3f655c26f6ce16e4b63a82d7e83afbd15a524 100644 (file)
@@ -2598,26 +2598,23 @@ EXPORT_SYMBOL_GPL(usb_create_hcd);
  * Don't deallocate the bandwidth_mutex until the last shared usb_hcd is
  * deallocated.
  *
- * Make sure to only deallocate the bandwidth_mutex when the primary HCD is
- * freed.  When hcd_release() is called for either hcd in a peer set
- * invalidate the peer's ->shared_hcd and ->primary_hcd pointers to
- * block new peering attempts
+ * Make sure to deallocate the bandwidth_mutex only when the last HCD is
+ * freed.  When hcd_release() is called for either hcd in a peer set,
+ * invalidate the peer's ->shared_hcd and ->primary_hcd pointers.
  */
 static void hcd_release(struct kref *kref)
 {
        struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref);
 
        mutex_lock(&usb_port_peer_mutex);
-       if (usb_hcd_is_primary_hcd(hcd)) {
-               kfree(hcd->address0_mutex);
-               kfree(hcd->bandwidth_mutex);
-       }
        if (hcd->shared_hcd) {
                struct usb_hcd *peer = hcd->shared_hcd;
 
                peer->shared_hcd = NULL;
-               if (peer->primary_hcd == hcd)
-                       peer->primary_hcd = NULL;
+               peer->primary_hcd = NULL;
+       } else {
+               kfree(hcd->address0_mutex);
+               kfree(hcd->bandwidth_mutex);
        }
        mutex_unlock(&usb_port_peer_mutex);
        kfree(hcd);
index 50d6ae6f88bc336a8631ce76f9c45c8de077b481..89a2f712fdfe32f5fc0a6fc0681b0d8db005e2a2 100644 (file)
@@ -233,7 +233,8 @@ static int st_dwc3_probe(struct platform_device *pdev)
        dev_vdbg(&pdev->dev, "glue-logic addr 0x%p, syscfg-reg offset 0x%x\n",
                 dwc3_data->glue_base, dwc3_data->syscfg_reg_off);
 
-       dwc3_data->rstc_pwrdn = devm_reset_control_get(dev, "powerdown");
+       dwc3_data->rstc_pwrdn =
+               devm_reset_control_get_exclusive(dev, "powerdown");
        if (IS_ERR(dwc3_data->rstc_pwrdn)) {
                dev_err(&pdev->dev, "could not get power controller\n");
                ret = PTR_ERR(dwc3_data->rstc_pwrdn);
@@ -243,7 +244,8 @@ static int st_dwc3_probe(struct platform_device *pdev)
        /* Manage PowerDown */
        reset_control_deassert(dwc3_data->rstc_pwrdn);
 
-       dwc3_data->rstc_rst = devm_reset_control_get(dev, "softreset");
+       dwc3_data->rstc_rst =
+               devm_reset_control_get_shared(dev, "softreset");
        if (IS_ERR(dwc3_data->rstc_rst)) {
                dev_err(&pdev->dev, "could not get reset controller\n");
                ret = PTR_ERR(dwc3_data->rstc_rst);
index a94ed677d93747c143d01879a175bad23f8f795f..be4a2788fc5828ea172c5c72f5e5f5b5822f052d 100644 (file)
@@ -206,7 +206,8 @@ static int st_ehci_platform_probe(struct platform_device *dev)
                priv->clk48 = NULL;
        }
 
-       priv->pwr = devm_reset_control_get_optional(&dev->dev, "power");
+       priv->pwr =
+               devm_reset_control_get_optional_shared(&dev->dev, "power");
        if (IS_ERR(priv->pwr)) {
                err = PTR_ERR(priv->pwr);
                if (err == -EPROBE_DEFER)
@@ -214,7 +215,8 @@ static int st_ehci_platform_probe(struct platform_device *dev)
                priv->pwr = NULL;
        }
 
-       priv->rst = devm_reset_control_get_optional(&dev->dev, "softreset");
+       priv->rst =
+               devm_reset_control_get_optional_shared(&dev->dev, "softreset");
        if (IS_ERR(priv->rst)) {
                err = PTR_ERR(priv->rst);
                if (err == -EPROBE_DEFER)
index acf2eb2a56766358c2bbdef931b070c2c81ad43d..02816a1515a1dc4dc0ec06506855bf0fc57a799c 100644 (file)
@@ -188,13 +188,15 @@ static int st_ohci_platform_probe(struct platform_device *dev)
                priv->clk48 = NULL;
        }
 
-       priv->pwr = devm_reset_control_get_optional(&dev->dev, "power");
+       priv->pwr =
+               devm_reset_control_get_optional_shared(&dev->dev, "power");
        if (IS_ERR(priv->pwr)) {
                err = PTR_ERR(priv->pwr);
                goto err_put_clks;
        }
 
-       priv->rst = devm_reset_control_get_optional(&dev->dev, "softreset");
+       priv->rst =
+               devm_reset_control_get_optional_shared(&dev->dev, "softreset");
        if (IS_ERR(priv->rst)) {
                err = PTR_ERR(priv->rst);
                goto err_put_clks;
index 076970a54f894b80366da951f220a97203466bb7..4ce10bcca18b1f600c351675142240dbe94a4022 100644 (file)
@@ -423,36 +423,7 @@ static int check_acpi_ids(struct acpi_processor *pr_backup)
 
        return 0;
 }
-static int __init check_prereq(void)
-{
-       struct cpuinfo_x86 *c = &cpu_data(0);
-
-       if (!xen_initial_domain())
-               return -ENODEV;
-
-       if (!acpi_gbl_FADT.smi_command)
-               return -ENODEV;
-
-       if (c->x86_vendor == X86_VENDOR_INTEL) {
-               if (!cpu_has(c, X86_FEATURE_EST))
-                       return -ENODEV;
 
-               return 0;
-       }
-       if (c->x86_vendor == X86_VENDOR_AMD) {
-               /* Copied from powernow-k8.h, can't include ../cpufreq/powernow
-                * as we get compile warnings for the static functions.
-                */
-#define CPUID_FREQ_VOLT_CAPABILITIES    0x80000007
-#define USE_HW_PSTATE                   0x00000080
-               u32 eax, ebx, ecx, edx;
-               cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
-               if ((edx & USE_HW_PSTATE) != USE_HW_PSTATE)
-                       return -ENODEV;
-               return 0;
-       }
-       return -ENODEV;
-}
 /* acpi_perf_data is a pointer to percpu data. */
 static struct acpi_processor_performance __percpu *acpi_perf_data;
 
@@ -509,10 +480,10 @@ struct notifier_block xen_acpi_processor_resume_nb = {
 static int __init xen_acpi_processor_init(void)
 {
        unsigned int i;
-       int rc = check_prereq();
+       int rc;
 
-       if (rc)
-               return rc;
+       if (!xen_initial_domain())
+               return -ENODEV;
 
        nr_acpi_bits = get_max_acpi_id() + 1;
        acpi_ids_done = kcalloc(BITS_TO_LONGS(nr_acpi_bits), sizeof(unsigned long), GFP_KERNEL);
index cacf30d14747baa20d5f2d8a6a999ebdcba14018..7487971f9f788b12a637216c29cc853bb933c1ca 100644 (file)
@@ -316,11 +316,18 @@ static int xenbus_write_transaction(unsigned msg_type,
                        rc = -ENOMEM;
                        goto out;
                }
+       } else {
+               list_for_each_entry(trans, &u->transactions, list)
+                       if (trans->handle.id == u->u.msg.tx_id)
+                               break;
+               if (&trans->list == &u->transactions)
+                       return -ESRCH;
        }
 
        reply = xenbus_dev_request_and_reply(&u->u.msg);
        if (IS_ERR(reply)) {
-               kfree(trans);
+               if (msg_type == XS_TRANSACTION_START)
+                       kfree(trans);
                rc = PTR_ERR(reply);
                goto out;
        }
@@ -333,12 +340,7 @@ static int xenbus_write_transaction(unsigned msg_type,
                        list_add(&trans->list, &u->transactions);
                }
        } else if (u->u.msg.type == XS_TRANSACTION_END) {
-               list_for_each_entry(trans, &u->transactions, list)
-                       if (trans->handle.id == u->u.msg.tx_id)
-                               break;
-               BUG_ON(&trans->list == &u->transactions);
                list_del(&trans->list);
-
                kfree(trans);
        }
 
index 374b12af88127c2aef2f359ef3f13013ec548fd1..22f7cd711c5792e25eac035f0aa138c9992d9bd9 100644 (file)
@@ -232,10 +232,10 @@ static void transaction_resume(void)
 void *xenbus_dev_request_and_reply(struct xsd_sockmsg *msg)
 {
        void *ret;
-       struct xsd_sockmsg req_msg = *msg;
+       enum xsd_sockmsg_type type = msg->type;
        int err;
 
-       if (req_msg.type == XS_TRANSACTION_START)
+       if (type == XS_TRANSACTION_START)
                transaction_start();
 
        mutex_lock(&xs_state.request_mutex);
@@ -249,12 +249,8 @@ void *xenbus_dev_request_and_reply(struct xsd_sockmsg *msg)
 
        mutex_unlock(&xs_state.request_mutex);
 
-       if (IS_ERR(ret))
-               return ret;
-
        if ((msg->type == XS_TRANSACTION_END) ||
-           ((req_msg.type == XS_TRANSACTION_START) &&
-            (msg->type == XS_ERROR)))
+           ((type == XS_TRANSACTION_START) && (msg->type == XS_ERROR)))
                transaction_end();
 
        return ret;
index b84c291ba1ebf38d0295b0dd919c794c28caa099..d7b78d531e63f99f6e2a350e081c9836918b64b5 100644 (file)
@@ -74,7 +74,7 @@ int v9fs_file_open(struct inode *inode, struct file *file)
                                        v9fs_proto_dotu(v9ses));
        fid = file->private_data;
        if (!fid) {
-               fid = v9fs_fid_clone(file->f_path.dentry);
+               fid = v9fs_fid_clone(file_dentry(file));
                if (IS_ERR(fid))
                        return PTR_ERR(fid);
 
@@ -100,7 +100,7 @@ int v9fs_file_open(struct inode *inode, struct file *file)
                 * because we want write after unlink usecase
                 * to work.
                 */
-               fid = v9fs_writeback_fid(file->f_path.dentry);
+               fid = v9fs_writeback_fid(file_dentry(file));
                if (IS_ERR(fid)) {
                        err = PTR_ERR(fid);
                        mutex_unlock(&v9inode->v_mutex);
@@ -516,7 +516,7 @@ v9fs_mmap_file_mmap(struct file *filp, struct vm_area_struct *vma)
                 * because we want write after unlink usecase
                 * to work.
                 */
-               fid = v9fs_writeback_fid(filp->f_path.dentry);
+               fid = v9fs_writeback_fid(file_dentry(filp));
                if (IS_ERR(fid)) {
                        retval = PTR_ERR(fid);
                        mutex_unlock(&v9inode->v_mutex);
index f4645c51526290e0d22a1e2589dbe21bf8e7148b..e2e7c749925ad237c57758ed18186f5bed7d497b 100644 (file)
@@ -853,7 +853,7 @@ v9fs_vfs_atomic_open(struct inode *dir, struct dentry *dentry,
        struct p9_fid *fid, *inode_fid;
        struct dentry *res = NULL;
 
-       if (d_unhashed(dentry)) {
+       if (d_in_lookup(dentry)) {
                res = v9fs_vfs_lookup(dir, dentry, 0);
                if (IS_ERR(res))
                        return PTR_ERR(res);
index a34702c998f593f60515d72fcf093cd556f5d951..1b51eaa5e2dd05445d0ba3e9c33038bcfbe4d277 100644 (file)
@@ -254,7 +254,7 @@ v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry,
        struct posix_acl *pacl = NULL, *dacl = NULL;
        struct dentry *res = NULL;
 
-       if (d_unhashed(dentry)) {
+       if (d_in_lookup(dentry)) {
                res = v9fs_vfs_lookup(dir, dentry, 0);
                if (IS_ERR(res))
                        return PTR_ERR(res);
index 6e72c98162d5351a9ad27b581eb17737eb4a1203..1780218a48f0843e2fa0777831d958cb4c4e9a37 100644 (file)
@@ -95,10 +95,8 @@ static struct dentry *__fh_to_dentry(struct super_block *sb, u64 ino)
        }
 
        dentry = d_obtain_alias(inode);
-       if (IS_ERR(dentry)) {
-               iput(inode);
+       if (IS_ERR(dentry))
                return dentry;
-       }
        err = ceph_init_dentry(dentry);
        if (err < 0) {
                dput(dentry);
@@ -167,10 +165,8 @@ static struct dentry *__get_parent(struct super_block *sb,
                return ERR_PTR(-ENOENT);
 
        dentry = d_obtain_alias(inode);
-       if (IS_ERR(dentry)) {
-               iput(inode);
+       if (IS_ERR(dentry))
                return dentry;
-       }
        err = ceph_init_dentry(dentry);
        if (err < 0) {
                dput(dentry);
@@ -210,7 +206,7 @@ static struct dentry *ceph_fh_to_parent(struct super_block *sb,
 
        dout("fh_to_parent %llx\n", cfh->parent_ino);
        dentry = __get_parent(sb, NULL, cfh->ino);
-       if (IS_ERR(dentry) && PTR_ERR(dentry) == -ENOENT)
+       if (unlikely(dentry == ERR_PTR(-ENOENT)))
                dentry = __fh_to_dentry(sb, cfh->parent_ino);
        return dentry;
 }
index ce2f5795e44bc73d404817a884cd5ecb0664802d..0daaf7ceedc55f3769abb441a2477ea9fe4f0f75 100644 (file)
@@ -394,7 +394,7 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
        if ((flags & O_CREAT) && !req->r_reply_info.head->is_dentry)
                err = ceph_handle_notrace_create(dir, dentry);
 
-       if (d_unhashed(dentry)) {
+       if (d_in_lookup(dentry)) {
                dn = ceph_finish_lookup(req, dentry, err);
                if (IS_ERR(dn))
                        err = PTR_ERR(dn);
index 5a53ac6b1e02515be90a4e446b103aa9f6f26874..02b071bf3732ac29808183974636e2ac9ca56ce6 100644 (file)
@@ -101,6 +101,12 @@ convert_sfm_char(const __u16 src_char, char *target)
        case SFM_SLASH:
                *target = '\\';
                break;
+       case SFM_SPACE:
+               *target = ' ';
+               break;
+       case SFM_PERIOD:
+               *target = '.';
+               break;
        default:
                return false;
        }
@@ -404,7 +410,7 @@ static __le16 convert_to_sfu_char(char src_char)
        return dest_char;
 }
 
-static __le16 convert_to_sfm_char(char src_char)
+static __le16 convert_to_sfm_char(char src_char, bool end_of_string)
 {
        __le16 dest_char;
 
@@ -427,6 +433,18 @@ static __le16 convert_to_sfm_char(char src_char)
        case '|':
                dest_char = cpu_to_le16(SFM_PIPE);
                break;
+       case '.':
+               if (end_of_string)
+                       dest_char = cpu_to_le16(SFM_PERIOD);
+               else
+                       dest_char = 0;
+               break;
+       case ' ':
+               if (end_of_string)
+                       dest_char = cpu_to_le16(SFM_SPACE);
+               else
+                       dest_char = 0;
+               break;
        default:
                dest_char = 0;
        }
@@ -469,9 +487,16 @@ cifsConvertToUTF16(__le16 *target, const char *source, int srclen,
                /* see if we must remap this char */
                if (map_chars == SFU_MAP_UNI_RSVD)
                        dst_char = convert_to_sfu_char(src_char);
-               else if (map_chars == SFM_MAP_UNI_RSVD)
-                       dst_char = convert_to_sfm_char(src_char);
-               else
+               else if (map_chars == SFM_MAP_UNI_RSVD) {
+                       bool end_of_string;
+
+                       if (i == srclen - 1)
+                               end_of_string = true;
+                       else
+                               end_of_string = false;
+
+                       dst_char = convert_to_sfm_char(src_char, end_of_string);
+               } else
                        dst_char = 0;
                /*
                 * FIXME: We can not handle remapping backslash (UNI_SLASH)
index bdc52cb9a676db0a9041d7e198810ce8df2aca9a..479bc0a941f35f79056dedc45ab99825669f1e6e 100644 (file)
@@ -64,6 +64,8 @@
 #define SFM_LESSTHAN    ((__u16) 0xF023)
 #define SFM_PIPE        ((__u16) 0xF027)
 #define SFM_SLASH       ((__u16) 0xF026)
+#define SFM_PERIOD     ((__u16) 0xF028)
+#define SFM_SPACE      ((__u16) 0xF029)
 
 /*
  * Mapping mechanism to use when one of the seven reserved characters is
index 5d8b7edf8a8f69009114e56e28c530fb4e7e686f..5d841f39c4b70e853e5b74d1cb6e01e7d112fd25 100644 (file)
@@ -87,6 +87,7 @@ extern mempool_t *cifs_req_poolp;
 extern mempool_t *cifs_mid_poolp;
 
 struct workqueue_struct        *cifsiod_wq;
+__u32 cifs_lock_secret;
 
 /*
  * Bumps refcount for cifs super block.
@@ -1266,6 +1267,8 @@ init_cifs(void)
        spin_lock_init(&cifs_file_list_lock);
        spin_lock_init(&GlobalMid_Lock);
 
+       get_random_bytes(&cifs_lock_secret, sizeof(cifs_lock_secret));
+
        if (cifs_max_pending < 2) {
                cifs_max_pending = 2;
                cifs_dbg(FYI, "cifs_max_pending set to min of 2\n");
index bba106cdc43c4c5703568ac82016714983e1d7bc..8f1d8c1e72bece412a926d0e20b9bdc95a7fa9ee 100644 (file)
@@ -1619,6 +1619,7 @@ void cifs_oplock_break(struct work_struct *work);
 
 extern const struct slow_work_ops cifs_oplock_break_ops;
 extern struct workqueue_struct *cifsiod_wq;
+extern __u32 cifs_lock_secret;
 
 extern mempool_t *cifs_mid_poolp;
 
index 66736f57b5abdf785a724aab8701d620abbb7898..7d2b15c060909b103e96a90a330c6f0ed7ecd2d0 100644 (file)
@@ -428,7 +428,9 @@ cifs_echo_request(struct work_struct *work)
         * server->ops->need_neg() == true. Also, no need to ping if
         * we got a response recently.
         */
-       if (!server->ops->need_neg || server->ops->need_neg(server) ||
+
+       if (server->tcpStatus == CifsNeedReconnect ||
+           server->tcpStatus == CifsExiting || server->tcpStatus == CifsNew ||
            (server->ops->can_echo && !server->ops->can_echo(server)) ||
            time_before(jiffies, server->lstrp + echo_interval - HZ))
                goto requeue_echo;
index c3eb998a99bd18a2ed9b7b843c99be15fedab9df..fb0903fffc22c8738c34f958beb7784400a93339 100644 (file)
@@ -445,7 +445,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
                 * Check for hashed negative dentry. We have already revalidated
                 * the dentry and it is fine. No need to perform another lookup.
                 */
-               if (!d_unhashed(direntry))
+               if (!d_in_lookup(direntry))
                        return -ENOENT;
 
                res = cifs_lookup(inode, direntry, 0);
index 9793ae0bcaa2bc678f9a688c167ef2b091cc858f..d4890b6dc22db0a4c7345b195e0209c5d32cf0b0 100644 (file)
@@ -1112,6 +1112,12 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
        return rc;
 }
 
+static __u32
+hash_lockowner(fl_owner_t owner)
+{
+       return cifs_lock_secret ^ hash32_ptr((const void *)owner);
+}
+
 struct lock_to_push {
        struct list_head llist;
        __u64 offset;
@@ -1178,7 +1184,7 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile)
                else
                        type = CIFS_WRLCK;
                lck = list_entry(el, struct lock_to_push, llist);
-               lck->pid = flock->fl_pid;
+               lck->pid = hash_lockowner(flock->fl_owner);
                lck->netfid = cfile->fid.netfid;
                lck->length = length;
                lck->type = type;
@@ -1305,7 +1311,8 @@ cifs_getlk(struct file *file, struct file_lock *flock, __u32 type,
                        posix_lock_type = CIFS_RDLCK;
                else
                        posix_lock_type = CIFS_WRLCK;
-               rc = CIFSSMBPosixLock(xid, tcon, netfid, current->tgid,
+               rc = CIFSSMBPosixLock(xid, tcon, netfid,
+                                     hash_lockowner(flock->fl_owner),
                                      flock->fl_start, length, flock,
                                      posix_lock_type, wait_flag);
                return rc;
@@ -1505,7 +1512,8 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type,
                        posix_lock_type = CIFS_UNLCK;
 
                rc = CIFSSMBPosixLock(xid, tcon, cfile->fid.netfid,
-                                     current->tgid, flock->fl_start, length,
+                                     hash_lockowner(flock->fl_owner),
+                                     flock->fl_start, length,
                                      NULL, posix_lock_type, wait_flag);
                goto out;
        }
index 848249fa120fc270d751d02597107a20319cfa29..3079b38f0afbd197bc0fc9f27df086a92ab90fbf 100644 (file)
@@ -133,6 +133,6 @@ typedef struct _AUTHENTICATE_MESSAGE {
 
 int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses);
 void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, struct cifs_ses *ses);
-int build_ntlmssp_auth_blob(unsigned char *pbuffer, u16 *buflen,
+int build_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen,
                        struct cifs_ses *ses,
                        const struct nls_table *nls_cp);
index af0ec2d5ad0e9da25af4ec8528266451a42cc8b5..538d9b55699a10f1185b8df25a1f36f9a24ae75c 100644 (file)
@@ -364,19 +364,43 @@ void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
        sec_blob->DomainName.MaximumLength = 0;
 }
 
-/* We do not malloc the blob, it is passed in pbuffer, because its
-   maximum possible size is fixed and small, making this approach cleaner.
-   This function returns the length of the data in the blob */
-int build_ntlmssp_auth_blob(unsigned char *pbuffer,
+static int size_of_ntlmssp_blob(struct cifs_ses *ses)
+{
+       int sz = sizeof(AUTHENTICATE_MESSAGE) + ses->auth_key.len
+               - CIFS_SESS_KEY_SIZE + CIFS_CPHTXT_SIZE + 2;
+
+       if (ses->domainName)
+               sz += 2 * strnlen(ses->domainName, CIFS_MAX_DOMAINNAME_LEN);
+       else
+               sz += 2;
+
+       if (ses->user_name)
+               sz += 2 * strnlen(ses->user_name, CIFS_MAX_USERNAME_LEN);
+       else
+               sz += 2;
+
+       return sz;
+}
+
+int build_ntlmssp_auth_blob(unsigned char **pbuffer,
                                        u16 *buflen,
                                   struct cifs_ses *ses,
                                   const struct nls_table *nls_cp)
 {
        int rc;
-       AUTHENTICATE_MESSAGE *sec_blob = (AUTHENTICATE_MESSAGE *)pbuffer;
+       AUTHENTICATE_MESSAGE *sec_blob;
        __u32 flags;
        unsigned char *tmp;
 
+       rc = setup_ntlmv2_rsp(ses, nls_cp);
+       if (rc) {
+               cifs_dbg(VFS, "Error %d during NTLMSSP authentication\n", rc);
+               *buflen = 0;
+               goto setup_ntlmv2_ret;
+       }
+       *pbuffer = kmalloc(size_of_ntlmssp_blob(ses), GFP_KERNEL);
+       sec_blob = (AUTHENTICATE_MESSAGE *)*pbuffer;
+
        memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8);
        sec_blob->MessageType = NtLmAuthenticate;
 
@@ -391,7 +415,7 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer,
                        flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
        }
 
-       tmp = pbuffer + sizeof(AUTHENTICATE_MESSAGE);
+       tmp = *pbuffer + sizeof(AUTHENTICATE_MESSAGE);
        sec_blob->NegotiateFlags = cpu_to_le32(flags);
 
        sec_blob->LmChallengeResponse.BufferOffset =
@@ -399,13 +423,9 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer,
        sec_blob->LmChallengeResponse.Length = 0;
        sec_blob->LmChallengeResponse.MaximumLength = 0;
 
-       sec_blob->NtChallengeResponse.BufferOffset = cpu_to_le32(tmp - pbuffer);
+       sec_blob->NtChallengeResponse.BufferOffset =
+                               cpu_to_le32(tmp - *pbuffer);
        if (ses->user_name != NULL) {
-               rc = setup_ntlmv2_rsp(ses, nls_cp);
-               if (rc) {
-                       cifs_dbg(VFS, "Error %d during NTLMSSP authentication\n", rc);
-                       goto setup_ntlmv2_ret;
-               }
                memcpy(tmp, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
                                ses->auth_key.len - CIFS_SESS_KEY_SIZE);
                tmp += ses->auth_key.len - CIFS_SESS_KEY_SIZE;
@@ -423,23 +443,23 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer,
        }
 
        if (ses->domainName == NULL) {
-               sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer);
+               sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
                sec_blob->DomainName.Length = 0;
                sec_blob->DomainName.MaximumLength = 0;
                tmp += 2;
        } else {
                int len;
                len = cifs_strtoUTF16((__le16 *)tmp, ses->domainName,
-                                     CIFS_MAX_USERNAME_LEN, nls_cp);
+                                     CIFS_MAX_DOMAINNAME_LEN, nls_cp);
                len *= 2; /* unicode is 2 bytes each */
-               sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer);
+               sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
                sec_blob->DomainName.Length = cpu_to_le16(len);
                sec_blob->DomainName.MaximumLength = cpu_to_le16(len);
                tmp += len;
        }
 
        if (ses->user_name == NULL) {
-               sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer);
+               sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
                sec_blob->UserName.Length = 0;
                sec_blob->UserName.MaximumLength = 0;
                tmp += 2;
@@ -448,13 +468,13 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer,
                len = cifs_strtoUTF16((__le16 *)tmp, ses->user_name,
                                      CIFS_MAX_USERNAME_LEN, nls_cp);
                len *= 2; /* unicode is 2 bytes each */
-               sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer);
+               sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
                sec_blob->UserName.Length = cpu_to_le16(len);
                sec_blob->UserName.MaximumLength = cpu_to_le16(len);
                tmp += len;
        }
 
-       sec_blob->WorkstationName.BufferOffset = cpu_to_le32(tmp - pbuffer);
+       sec_blob->WorkstationName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
        sec_blob->WorkstationName.Length = 0;
        sec_blob->WorkstationName.MaximumLength = 0;
        tmp += 2;
@@ -463,19 +483,19 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer,
                (ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_EXTENDED_SEC))
                        && !calc_seckey(ses)) {
                memcpy(tmp, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE);
-               sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - pbuffer);
+               sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - *pbuffer);
                sec_blob->SessionKey.Length = cpu_to_le16(CIFS_CPHTXT_SIZE);
                sec_blob->SessionKey.MaximumLength =
                                cpu_to_le16(CIFS_CPHTXT_SIZE);
                tmp += CIFS_CPHTXT_SIZE;
        } else {
-               sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - pbuffer);
+               sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - *pbuffer);
                sec_blob->SessionKey.Length = 0;
                sec_blob->SessionKey.MaximumLength = 0;
        }
 
+       *buflen = tmp - *pbuffer;
 setup_ntlmv2_ret:
-       *buflen = tmp - pbuffer;
        return rc;
 }
 
@@ -690,6 +710,8 @@ sess_auth_lanman(struct sess_data *sess_data)
                rc = calc_lanman_hash(ses->password, ses->server->cryptkey,
                                      ses->server->sec_mode & SECMODE_PW_ENCRYPT ?
                                      true : false, lnm_session_key);
+               if (rc)
+                       goto out;
 
                memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_AUTH_RESP_SIZE);
                bcc_ptr += CIFS_AUTH_RESP_SIZE;
@@ -1266,7 +1288,7 @@ sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data)
        struct cifs_ses *ses = sess_data->ses;
        __u16 bytes_remaining;
        char *bcc_ptr;
-       char *ntlmsspblob = NULL;
+       unsigned char *ntlmsspblob = NULL;
        u16 blob_len;
 
        cifs_dbg(FYI, "rawntlmssp session setup authenticate phase\n");
@@ -1279,19 +1301,7 @@ sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data)
        /* Build security blob before we assemble the request */
        pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
        smb_buf = (struct smb_hdr *)pSMB;
-       /*
-        * 5 is an empirical value, large enough to hold
-        * authenticate message plus max 10 of av paris,
-        * domain, user, workstation names, flags, etc.
-        */
-       ntlmsspblob = kzalloc(5*sizeof(struct _AUTHENTICATE_MESSAGE),
-                               GFP_KERNEL);
-       if (!ntlmsspblob) {
-               rc = -ENOMEM;
-               goto out;
-       }
-
-       rc = build_ntlmssp_auth_blob(ntlmsspblob,
+       rc = build_ntlmssp_auth_blob(&ntlmsspblob,
                                        &blob_len, ses, sess_data->nls_cp);
        if (rc)
                goto out_free_ntlmsspblob;
index 8f38e33d365bcca5ce9079dc64e5d10525df801c..29e06db5f187bea7d4f5ce29d2cf5c0faad6ae09 100644 (file)
@@ -588,7 +588,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
        u16 blob_length = 0;
        struct key *spnego_key = NULL;
        char *security_blob = NULL;
-       char *ntlmssp_blob = NULL;
+       unsigned char *ntlmssp_blob = NULL;
        bool use_spnego = false; /* else use raw ntlmssp */
 
        cifs_dbg(FYI, "Session Setup\n");
@@ -713,13 +713,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
                iov[1].iov_len = blob_length;
        } else if (phase == NtLmAuthenticate) {
                req->hdr.SessionId = ses->Suid;
-               ntlmssp_blob = kzalloc(sizeof(struct _NEGOTIATE_MESSAGE) + 500,
-                                      GFP_KERNEL);
-               if (ntlmssp_blob == NULL) {
-                       rc = -ENOMEM;
-                       goto ssetup_exit;
-               }
-               rc = build_ntlmssp_auth_blob(ntlmssp_blob, &blob_length, ses,
+               rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses,
                                             nls_cp);
                if (rc) {
                        cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n",
@@ -1818,6 +1812,33 @@ SMB2_echo(struct TCP_Server_Info *server)
 
        cifs_dbg(FYI, "In echo request\n");
 
+       if (server->tcpStatus == CifsNeedNegotiate) {
+               struct list_head *tmp, *tmp2;
+               struct cifs_ses *ses;
+               struct cifs_tcon *tcon;
+
+               cifs_dbg(FYI, "Need negotiate, reconnecting tcons\n");
+               spin_lock(&cifs_tcp_ses_lock);
+               list_for_each(tmp, &server->smb_ses_list) {
+                       ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
+                       list_for_each(tmp2, &ses->tcon_list) {
+                               tcon = list_entry(tmp2, struct cifs_tcon,
+                                                 tcon_list);
+                               /* add check for persistent handle reconnect */
+                               if (tcon && tcon->need_reconnect) {
+                                       spin_unlock(&cifs_tcp_ses_lock);
+                                       rc = smb2_reconnect(SMB2_ECHO, tcon);
+                                       spin_lock(&cifs_tcp_ses_lock);
+                               }
+                       }
+               }
+               spin_unlock(&cifs_tcp_ses_lock);
+       }
+
+       /* if no session, renegotiate failed above */
+       if (server->tcpStatus == CifsNeedNegotiate)
+               return -EIO;
+
        rc = small_smb2_init(SMB2_ECHO, NULL, (void **)&req);
        if (rc)
                return rc;
index 33b7ee34eda5f135fef480f0bdf55bb4037ab334..bbc1252a59f5f1431ee779e16780f19a5f47a5d8 100644 (file)
@@ -357,8 +357,6 @@ configfs_write_bin_file(struct file *file, const char __user *buf,
 
        len = simple_write_to_buffer(buffer->bin_buffer,
                        buffer->bin_buffer_size, ppos, buf, count);
-       if (len > 0)
-               *ppos += len;
 out:
        mutex_unlock(&buffer->mutex);
        return len;
index 761495bf5eb91d97c6483646d89a67f10933864f..e207f8f9b7007bfa9c9a44dd7d24cc1407bac098 100644 (file)
--- a/fs/dax.c
+++ b/fs/dax.c
@@ -208,7 +208,12 @@ static ssize_t dax_io(struct inode *inode, struct iov_iter *iter,
                                dax.addr += first;
                                size = map_len - first;
                        }
-                       max = min(pos + size, end);
+                       /*
+                        * pos + size is one past the last offset for IO,
+                        * so pos + size can overflow loff_t at extreme offsets.
+                        * Cast to u64 to catch this and get the true minimum.
+                        */
+                       max = min_t(u64, pos + size, end);
                }
 
                if (iov_iter_rw(iter) == WRITE) {
index 0d8eb3455b34d68cde59a48b5b46da9b67498445..e5e29f8c920b18bc6959cdb16d2ee57fec7d45a6 100644 (file)
@@ -45,7 +45,7 @@
  * ecryptfs_to_hex
  * @dst: Buffer to take hex character representation of contents of
  *       src; must be at least of size (src_size * 2)
- * @src: Buffer to be converted to a hex string respresentation
+ * @src: Buffer to be converted to a hex string representation
  * @src_size: number of bytes to convert
  */
 void ecryptfs_to_hex(char *dst, char *src, size_t src_size)
@@ -60,7 +60,7 @@ void ecryptfs_to_hex(char *dst, char *src, size_t src_size)
  * ecryptfs_from_hex
  * @dst: Buffer to take the bytes from src hex; must be at least of
  *       size (src_size / 2)
- * @src: Buffer to be converted from a hex string respresentation to raw value
+ * @src: Buffer to be converted from a hex string representation to raw value
  * @dst_size: size of dst buffer, or number of hex characters pairs to convert
  */
 void ecryptfs_from_hex(char *dst, char *src, int dst_size)
@@ -953,7 +953,7 @@ struct ecryptfs_cipher_code_str_map_elem {
 };
 
 /* Add support for additional ciphers by adding elements here. The
- * cipher_code is whatever OpenPGP applicatoins use to identify the
+ * cipher_code is whatever OpenPGP applications use to identify the
  * ciphers. List in order of probability. */
 static struct ecryptfs_cipher_code_str_map_elem
 ecryptfs_cipher_code_str_map[] = {
@@ -1410,7 +1410,7 @@ int ecryptfs_read_and_validate_xattr_region(struct dentry *dentry,
  *
  * Common entry point for reading file metadata. From here, we could
  * retrieve the header information from the header region of the file,
- * the xattr region of the file, or some other repostory that is
+ * the xattr region of the file, or some other repository that is
  * stored separately from the file itself. The current implementation
  * supports retrieving the metadata information from the file contents
  * and from the xattr region.
index 7000b96b783ef04a56f056a83df595c8093533aa..ca4e83750214adc2b0ea77358937c167d02d6ba7 100644 (file)
@@ -169,9 +169,22 @@ static int read_or_initialize_metadata(struct dentry *dentry)
        return rc;
 }
 
+static int ecryptfs_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct file *lower_file = ecryptfs_file_to_lower(file);
+       /*
+        * Don't allow mmap on top of file systems that don't support it
+        * natively.  If FILESYSTEM_MAX_STACK_DEPTH > 2 or ecryptfs
+        * allows recursive mounting, this will need to be extended.
+        */
+       if (!lower_file->f_op->mmap)
+               return -ENODEV;
+       return generic_file_mmap(file, vma);
+}
+
 /**
  * ecryptfs_open
- * @inode: inode speciying file to open
+ * @inode: inode specifying file to open
  * @file: Structure to return filled in
  *
  * Opens the file specified by inode.
@@ -240,7 +253,7 @@ static int ecryptfs_open(struct inode *inode, struct file *file)
 
 /**
  * ecryptfs_dir_open
- * @inode: inode speciying file to open
+ * @inode: inode specifying file to open
  * @file: Structure to return filled in
  *
  * Opens the file specified by inode.
@@ -403,7 +416,7 @@ const struct file_operations ecryptfs_main_fops = {
 #ifdef CONFIG_COMPAT
        .compat_ioctl = ecryptfs_compat_ioctl,
 #endif
-       .mmap = generic_file_mmap,
+       .mmap = ecryptfs_mmap,
        .open = ecryptfs_open,
        .flush = ecryptfs_flush,
        .release = ecryptfs_release,
index e818f5ac7a2692b6bb5c1a132d7bbfd967ad956a..866bb18efefea9953250ba1cbdc145e7d4be49af 100644 (file)
@@ -25,7 +25,6 @@
 #include <linux/slab.h>
 #include <linux/wait.h>
 #include <linux/mount.h>
-#include <linux/file.h>
 #include "ecryptfs_kernel.h"
 
 struct ecryptfs_open_req {
@@ -148,7 +147,7 @@ int ecryptfs_privileged_open(struct file **lower_file,
        flags |= IS_RDONLY(d_inode(lower_dentry)) ? O_RDONLY : O_RDWR;
        (*lower_file) = dentry_open(&req.path, flags, cred);
        if (!IS_ERR(*lower_file))
-               goto have_file;
+               goto out;
        if ((flags & O_ACCMODE) == O_RDONLY) {
                rc = PTR_ERR((*lower_file));
                goto out;
@@ -166,16 +165,8 @@ int ecryptfs_privileged_open(struct file **lower_file,
        mutex_unlock(&ecryptfs_kthread_ctl.mux);
        wake_up(&ecryptfs_kthread_ctl.wait);
        wait_for_completion(&req.done);
-       if (IS_ERR(*lower_file)) {
+       if (IS_ERR(*lower_file))
                rc = PTR_ERR(*lower_file);
-               goto out;
-       }
-have_file:
-       if ((*lower_file)->f_op->mmap == NULL) {
-               fput(*lower_file);
-               *lower_file = NULL;
-               rc = -EMEDIUMTYPE;
-       }
 out:
        return rc;
 }
index 1698132d0e576d4fea3690f56190242de33645fc..6120044951415d7840308eea25a21bd125085109 100644 (file)
@@ -738,8 +738,7 @@ static void ecryptfs_free_kmem_caches(void)
                struct ecryptfs_cache_info *info;
 
                info = &ecryptfs_cache_infos[i];
-               if (*(info->cache))
-                       kmem_cache_destroy(*(info->cache));
+               kmem_cache_destroy(*(info->cache));
        }
 }
 
index 989a2cef6b765023b670d75af4fb3bdbf6f650e6..fe7e83a45efffeb85329b194b03208e6223817e3 100644 (file)
@@ -483,9 +483,9 @@ static void inode_switch_wbs(struct inode *inode, int new_wb_id)
                goto out_free;
        }
        inode->i_state |= I_WB_SWITCH;
+       __iget(inode);
        spin_unlock(&inode->i_lock);
 
-       ihold(inode);
        isw->inode = inode;
 
        atomic_inc(&isw_nr_in_flight);
index ccd4971cc6c1ac787ede8998d73f3e68f3a1294a..cca7b048c07b26e4919769fed461b46771005a19 100644 (file)
@@ -341,8 +341,10 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
        struct dentry *newent;
        bool outarg_valid = true;
 
+       fuse_lock_inode(dir);
        err = fuse_lookup_name(dir->i_sb, get_node_id(dir), &entry->d_name,
                               &outarg, &inode);
+       fuse_unlock_inode(dir);
        if (err == -ENOENT) {
                outarg_valid = false;
                err = 0;
@@ -478,7 +480,7 @@ static int fuse_atomic_open(struct inode *dir, struct dentry *entry,
        struct fuse_conn *fc = get_fuse_conn(dir);
        struct dentry *res = NULL;
 
-       if (d_unhashed(entry)) {
+       if (d_in_lookup(entry)) {
                res = fuse_lookup(dir, entry, 0);
                if (IS_ERR(res))
                        return PTR_ERR(res);
@@ -1341,7 +1343,9 @@ static int fuse_readdir(struct file *file, struct dir_context *ctx)
                fuse_read_fill(req, file, ctx->pos, PAGE_SIZE,
                               FUSE_READDIR);
        }
+       fuse_lock_inode(inode);
        fuse_request_send(fc, req);
+       fuse_unlock_inode(inode);
        nbytes = req->out.args[0].size;
        err = req->out.h.error;
        fuse_put_request(fc, req);
index eddbe02c402892cc00970844021fd12512f133b4..929c383432b034f3f695e0cc83e55e9b48c35898 100644 (file)
@@ -110,6 +110,9 @@ struct fuse_inode {
 
        /** Miscellaneous bits describing inode state */
        unsigned long state;
+
+       /** Lock for serializing lookup and readdir for back compatibility*/
+       struct mutex mutex;
 };
 
 /** FUSE inode state bits */
@@ -540,6 +543,9 @@ struct fuse_conn {
        /** write-back cache policy (default is write-through) */
        unsigned writeback_cache:1;
 
+       /** allow parallel lookups and readdir (default is serialized) */
+       unsigned parallel_dirops:1;
+
        /*
         * The following bitfields are only for optimization purposes
         * and hence races in setting them will not cause malfunction
@@ -956,4 +962,7 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr,
 
 void fuse_set_initialized(struct fuse_conn *fc);
 
+void fuse_unlock_inode(struct inode *inode);
+void fuse_lock_inode(struct inode *inode);
+
 #endif /* _FS_FUSE_I_H */
index 1ce67668a8e17d2d721c456ed02865e01c01f7a2..9961d8432ce335ba445df4a36824cd12912f1419 100644 (file)
@@ -97,6 +97,7 @@ static struct inode *fuse_alloc_inode(struct super_block *sb)
        INIT_LIST_HEAD(&fi->queued_writes);
        INIT_LIST_HEAD(&fi->writepages);
        init_waitqueue_head(&fi->page_waitq);
+       mutex_init(&fi->mutex);
        fi->forget = fuse_alloc_forget();
        if (!fi->forget) {
                kmem_cache_free(fuse_inode_cachep, inode);
@@ -117,6 +118,7 @@ static void fuse_destroy_inode(struct inode *inode)
        struct fuse_inode *fi = get_fuse_inode(inode);
        BUG_ON(!list_empty(&fi->write_files));
        BUG_ON(!list_empty(&fi->queued_writes));
+       mutex_destroy(&fi->mutex);
        kfree(fi->forget);
        call_rcu(&inode->i_rcu, fuse_i_callback);
 }
@@ -351,6 +353,18 @@ int fuse_reverse_inval_inode(struct super_block *sb, u64 nodeid,
        return 0;
 }
 
+void fuse_lock_inode(struct inode *inode)
+{
+       if (!get_fuse_conn(inode)->parallel_dirops)
+               mutex_lock(&get_fuse_inode(inode)->mutex);
+}
+
+void fuse_unlock_inode(struct inode *inode)
+{
+       if (!get_fuse_conn(inode)->parallel_dirops)
+               mutex_unlock(&get_fuse_inode(inode)->mutex);
+}
+
 static void fuse_umount_begin(struct super_block *sb)
 {
        fuse_abort_conn(get_fuse_conn_super(sb));
@@ -898,6 +912,8 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
                                fc->async_dio = 1;
                        if (arg->flags & FUSE_WRITEBACK_CACHE)
                                fc->writeback_cache = 1;
+                       if (arg->flags & FUSE_PARALLEL_DIROPS)
+                               fc->parallel_dirops = 1;
                        if (arg->time_gran && arg->time_gran <= 1000000000)
                                fc->sb->s_time_gran = arg->time_gran;
                } else {
@@ -928,7 +944,8 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
                FUSE_SPLICE_WRITE | FUSE_SPLICE_MOVE | FUSE_SPLICE_READ |
                FUSE_FLOCK_LOCKS | FUSE_IOCTL_DIR | FUSE_AUTO_INVAL_DATA |
                FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO |
-               FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT;
+               FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT |
+               FUSE_PARALLEL_DIROPS;
        req->in.h.opcode = FUSE_INIT;
        req->in.numargs = 1;
        req->in.args[0].size = sizeof(*arg);
index 21dc784f66c2268d2e857314104847b600cc09de..9bad79fede37fb7e27023df1c1908e8a1783fb88 100644 (file)
@@ -1189,7 +1189,7 @@ static int gfs2_atomic_open(struct inode *dir, struct dentry *dentry,
        struct dentry *d;
        bool excl = !!(flags & O_EXCL);
 
-       if (!d_unhashed(dentry))
+       if (!d_in_lookup(dentry))
                goto skip_lookup;
 
        d = __gfs2_lookup(dir, dentry, file, opened);
index cedeacbae303a9483fea836ba4a5cc24cb022f41..74dc8b9e7f53ab394113783c96fb754c045735fa 100644 (file)
@@ -84,6 +84,61 @@ int dcache_dir_close(struct inode *inode, struct file *file)
 }
 EXPORT_SYMBOL(dcache_dir_close);
 
+/* parent is locked at least shared */
+static struct dentry *next_positive(struct dentry *parent,
+                                   struct list_head *from,
+                                   int count)
+{
+       unsigned *seq = &parent->d_inode->i_dir_seq, n;
+       struct dentry *res;
+       struct list_head *p;
+       bool skipped;
+       int i;
+
+retry:
+       i = count;
+       skipped = false;
+       n = smp_load_acquire(seq) & ~1;
+       res = NULL;
+       rcu_read_lock();
+       for (p = from->next; p != &parent->d_subdirs; p = p->next) {
+               struct dentry *d = list_entry(p, struct dentry, d_child);
+               if (!simple_positive(d)) {
+                       skipped = true;
+               } else if (!--i) {
+                       res = d;
+                       break;
+               }
+       }
+       rcu_read_unlock();
+       if (skipped) {
+               smp_rmb();
+               if (unlikely(*seq != n))
+                       goto retry;
+       }
+       return res;
+}
+
+static void move_cursor(struct dentry *cursor, struct list_head *after)
+{
+       struct dentry *parent = cursor->d_parent;
+       unsigned n, *seq = &parent->d_inode->i_dir_seq;
+       spin_lock(&parent->d_lock);
+       for (;;) {
+               n = *seq;
+               if (!(n & 1) && cmpxchg(seq, n, n + 1) == n)
+                       break;
+               cpu_relax();
+       }
+       __list_del(cursor->d_child.prev, cursor->d_child.next);
+       if (after)
+               list_add(&cursor->d_child, after);
+       else
+               list_add_tail(&cursor->d_child, &parent->d_subdirs);
+       smp_store_release(seq, n + 2);
+       spin_unlock(&parent->d_lock);
+}
+
 loff_t dcache_dir_lseek(struct file *file, loff_t offset, int whence)
 {
        struct dentry *dentry = file->f_path.dentry;
@@ -99,25 +154,14 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int whence)
        if (offset != file->f_pos) {
                file->f_pos = offset;
                if (file->f_pos >= 2) {
-                       struct list_head *p;
                        struct dentry *cursor = file->private_data;
+                       struct dentry *to;
                        loff_t n = file->f_pos - 2;
 
-                       spin_lock(&dentry->d_lock);
-                       /* d_lock not required for cursor */
-                       list_del(&cursor->d_child);
-                       p = dentry->d_subdirs.next;
-                       while (n && p != &dentry->d_subdirs) {
-                               struct dentry *next;
-                               next = list_entry(p, struct dentry, d_child);
-                               spin_lock_nested(&next->d_lock, DENTRY_D_LOCK_NESTED);
-                               if (simple_positive(next))
-                                       n--;
-                               spin_unlock(&next->d_lock);
-                               p = p->next;
-                       }
-                       list_add_tail(&cursor->d_child, p);
-                       spin_unlock(&dentry->d_lock);
+                       inode_lock_shared(dentry->d_inode);
+                       to = next_positive(dentry, &dentry->d_subdirs, n);
+                       move_cursor(cursor, to ? &to->d_child : NULL);
+                       inode_unlock_shared(dentry->d_inode);
                }
        }
        return offset;
@@ -140,36 +184,25 @@ int dcache_readdir(struct file *file, struct dir_context *ctx)
 {
        struct dentry *dentry = file->f_path.dentry;
        struct dentry *cursor = file->private_data;
-       struct list_head *p, *q = &cursor->d_child;
+       struct list_head *p = &cursor->d_child;
+       struct dentry *next;
+       bool moved = false;
 
        if (!dir_emit_dots(file, ctx))
                return 0;
-       spin_lock(&dentry->d_lock);
-       if (ctx->pos == 2)
-               list_move(q, &dentry->d_subdirs);
 
-       for (p = q->next; p != &dentry->d_subdirs; p = p->next) {
-               struct dentry *next = list_entry(p, struct dentry, d_child);
-               spin_lock_nested(&next->d_lock, DENTRY_D_LOCK_NESTED);
-               if (!simple_positive(next)) {
-                       spin_unlock(&next->d_lock);
-                       continue;
-               }
-
-               spin_unlock(&next->d_lock);
-               spin_unlock(&dentry->d_lock);
+       if (ctx->pos == 2)
+               p = &dentry->d_subdirs;
+       while ((next = next_positive(dentry, p, 1)) != NULL) {
                if (!dir_emit(ctx, next->d_name.name, next->d_name.len,
                              d_inode(next)->i_ino, dt_type(d_inode(next))))
-                       return 0;
-               spin_lock(&dentry->d_lock);
-               spin_lock_nested(&next->d_lock, DENTRY_D_LOCK_NESTED);
-               /* next is still alive */
-               list_move(q, p);
-               spin_unlock(&next->d_lock);
-               p = q;
+                       break;
+               moved = true;
+               p = &next->d_child;
                ctx->pos++;
        }
-       spin_unlock(&dentry->d_lock);
+       if (moved)
+               move_cursor(cursor, p);
        return 0;
 }
 EXPORT_SYMBOL(dcache_readdir);
index 154a107cd376238349ef6a889bb85ab0d23a0e31..fc4084ef4736d47a410e27052497cd00829204c2 100644 (file)
@@ -335,12 +335,17 @@ static struct notifier_block lockd_inet6addr_notifier = {
 };
 #endif
 
-static void lockd_svc_exit_thread(void)
+static void lockd_unregister_notifiers(void)
 {
        unregister_inetaddr_notifier(&lockd_inetaddr_notifier);
 #if IS_ENABLED(CONFIG_IPV6)
        unregister_inet6addr_notifier(&lockd_inet6addr_notifier);
 #endif
+}
+
+static void lockd_svc_exit_thread(void)
+{
+       lockd_unregister_notifiers();
        svc_exit_thread(nlmsvc_rqst);
 }
 
@@ -462,7 +467,7 @@ int lockd_up(struct net *net)
         * Note: svc_serv structures have an initial use count of 1,
         * so we exit through here on both success and failure.
         */
-err_net:
+err_put:
        svc_destroy(serv);
 err_create:
        mutex_unlock(&nlmsvc_mutex);
@@ -470,7 +475,9 @@ int lockd_up(struct net *net)
 
 err_start:
        lockd_down_net(serv, net);
-       goto err_net;
+err_net:
+       lockd_unregister_notifiers();
+       goto err_put;
 }
 EXPORT_SYMBOL_GPL(lockd_up);
 
index 7c5f91be9b65c4ddabe441d00bbda616ca0c2f26..ee1b15f6fc135c33e2e0564eb5e10db927080533 100644 (file)
@@ -1628,7 +1628,7 @@ generic_add_lease(struct file *filp, long arg, struct file_lock **flp, void **pr
 {
        struct file_lock *fl, *my_fl = NULL, *lease;
        struct dentry *dentry = filp->f_path.dentry;
-       struct inode *inode = dentry->d_inode;
+       struct inode *inode = file_inode(filp);
        struct file_lock_context *ctx;
        bool is_deleg = (*flp)->fl_flags & FL_DELEG;
        int error;
index 783004af57077a9947a8f3a3228cbc8465a79b3e..419f746d851d1c1745fc0edca4a37459a4b17f35 100644 (file)
@@ -1562,6 +1562,7 @@ void __detach_mounts(struct dentry *dentry)
                goto out_unlock;
 
        lock_mount_hash();
+       event++;
        while (!hlist_empty(&mp->m_list)) {
                mnt = hlist_entry(mp->m_list.first, struct mount, mnt_mp_list);
                if (mnt->mnt.mnt_flags & MNT_UMOUNT) {
index aaf7bd0cbae20216333042c5a082a7560f6af61f..19d93d0cd400f5ac175a9d257ec8a8ced0e04c21 100644 (file)
@@ -424,12 +424,17 @@ static int xdr_decode(nfs_readdir_descriptor_t *desc,
 static
 int nfs_same_file(struct dentry *dentry, struct nfs_entry *entry)
 {
+       struct inode *inode;
        struct nfs_inode *nfsi;
 
        if (d_really_is_negative(dentry))
                return 0;
 
-       nfsi = NFS_I(d_inode(dentry));
+       inode = d_inode(dentry);
+       if (is_bad_inode(inode) || NFS_STALE(inode))
+               return 0;
+
+       nfsi = NFS_I(inode);
        if (entry->fattr->fileid == nfsi->fileid)
                return 1;
        if (nfs_compare_fh(entry->fh, &nfsi->fh) == 0)
@@ -1363,7 +1368,6 @@ EXPORT_SYMBOL_GPL(nfs_dentry_operations);
 struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned int flags)
 {
        struct dentry *res;
-       struct dentry *parent;
        struct inode *inode = NULL;
        struct nfs_fh *fhandle = NULL;
        struct nfs_fattr *fattr = NULL;
@@ -1393,7 +1397,6 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
        if (IS_ERR(label))
                goto out;
 
-       parent = dentry->d_parent;
        /* Protect against concurrent sillydeletes */
        trace_nfs_lookup_enter(dir, dentry, flags);
        error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label);
@@ -1482,11 +1485,13 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
                    struct file *file, unsigned open_flags,
                    umode_t mode, int *opened)
 {
+       DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
        struct nfs_open_context *ctx;
        struct dentry *res;
        struct iattr attr = { .ia_valid = ATTR_OPEN };
        struct inode *inode;
        unsigned int lookup_flags = 0;
+       bool switched = false;
        int err;
 
        /* Expect a negative dentry */
@@ -1501,7 +1506,7 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
 
        /* NFS only supports OPEN on regular files */
        if ((open_flags & O_DIRECTORY)) {
-               if (!d_unhashed(dentry)) {
+               if (!d_in_lookup(dentry)) {
                        /*
                         * Hashed negative dentry with O_DIRECTORY: dentry was
                         * revalidated and is fine, no need to perform lookup
@@ -1525,6 +1530,17 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
                attr.ia_size = 0;
        }
 
+       if (!(open_flags & O_CREAT) && !d_in_lookup(dentry)) {
+               d_drop(dentry);
+               switched = true;
+               dentry = d_alloc_parallel(dentry->d_parent,
+                                         &dentry->d_name, &wq);
+               if (IS_ERR(dentry))
+                       return PTR_ERR(dentry);
+               if (unlikely(!d_in_lookup(dentry)))
+                       return finish_no_open(file, dentry);
+       }
+
        ctx = create_nfs_open_context(dentry, open_flags);
        err = PTR_ERR(ctx);
        if (IS_ERR(ctx))
@@ -1536,9 +1552,9 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
                err = PTR_ERR(inode);
                trace_nfs_atomic_open_exit(dir, ctx, open_flags, err);
                put_nfs_open_context(ctx);
+               d_drop(dentry);
                switch (err) {
                case -ENOENT:
-                       d_drop(dentry);
                        d_add(dentry, NULL);
                        nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
                        break;
@@ -1560,14 +1576,23 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
        trace_nfs_atomic_open_exit(dir, ctx, open_flags, err);
        put_nfs_open_context(ctx);
 out:
+       if (unlikely(switched)) {
+               d_lookup_done(dentry);
+               dput(dentry);
+       }
        return err;
 
 no_open:
        res = nfs_lookup(dir, dentry, lookup_flags);
-       err = PTR_ERR(res);
+       if (switched) {
+               d_lookup_done(dentry);
+               if (!res)
+                       res = dentry;
+               else
+                       dput(dentry);
+       }
        if (IS_ERR(res))
-               goto out;
-
+               return PTR_ERR(res);
        return finish_no_open(file, res);
 }
 EXPORT_SYMBOL_GPL(nfs_atomic_open);
index 979b3c4dee6aedbb354faefd1aca8cf1b3be8789..c7326c2af2c3d33df7a96497c427664d90f7861f 100644 (file)
@@ -353,10 +353,12 @@ static ssize_t nfs_direct_wait(struct nfs_direct_req *dreq)
 
        result = wait_for_completion_killable(&dreq->completion);
 
+       if (!result) {
+               result = dreq->count;
+               WARN_ON_ONCE(dreq->count < 0);
+       }
        if (!result)
                result = dreq->error;
-       if (!result)
-               result = dreq->count;
 
 out:
        return (ssize_t) result;
@@ -386,8 +388,10 @@ static void nfs_direct_complete(struct nfs_direct_req *dreq, bool write)
 
        if (dreq->iocb) {
                long res = (long) dreq->error;
-               if (!res)
+               if (dreq->count != 0) {
                        res = (long) dreq->count;
+                       WARN_ON_ONCE(dreq->count < 0);
+               }
                dreq->iocb->ki_complete(dreq->iocb, res, 0);
        }
 
index 52e7d6869e3b2445dc6417875b3c753edb681ecc..dda689d7a8a706862e21737cb5189ab175d976e1 100644 (file)
@@ -282,6 +282,7 @@ nfs_init_locked(struct inode *inode, void *opaque)
        struct nfs_fattr        *fattr = desc->fattr;
 
        set_nfs_fileid(inode, fattr->fileid);
+       inode->i_mode = fattr->mode;
        nfs_copy_fh(NFS_FH(inode), desc->fh);
        return 0;
 }
index de97567795a52c523abfa5936829303a388560e8..ff416d0e24bc25215a991c282e8ae19a65d31832 100644 (file)
@@ -2882,12 +2882,11 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
                        call_close |= is_wronly;
                else if (is_wronly)
                        calldata->arg.fmode |= FMODE_WRITE;
+               if (calldata->arg.fmode != (FMODE_READ|FMODE_WRITE))
+                       call_close |= is_rdwr;
        } else if (is_rdwr)
                calldata->arg.fmode |= FMODE_READ|FMODE_WRITE;
 
-       if (calldata->arg.fmode == 0)
-               call_close |= is_rdwr;
-
        if (!nfs4_valid_open_stateid(state))
                call_close = 0;
        spin_unlock(&state->owner->so_lock);
@@ -7924,8 +7923,8 @@ nfs4_layoutget_handle_exception(struct rpc_task *task,
                        break;
                }
                lo = NFS_I(inode)->layout;
-               if (lo && nfs4_stateid_match(&lgp->args.stateid,
-                                       &lo->plh_stateid)) {
+               if (lo && !test_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags) &&
+                   nfs4_stateid_match_other(&lgp->args.stateid, &lo->plh_stateid)) {
                        LIST_HEAD(head);
 
                        /*
@@ -7936,10 +7935,10 @@ nfs4_layoutget_handle_exception(struct rpc_task *task,
                        pnfs_mark_matching_lsegs_invalid(lo, &head, NULL, 0);
                        spin_unlock(&inode->i_lock);
                        pnfs_free_lseg_list(&head);
+                       status = -EAGAIN;
+                       goto out;
                } else
                        spin_unlock(&inode->i_lock);
-               status = -EAGAIN;
-               goto out;
        }
 
        status = nfs4_handle_exception(server, status, exception);
@@ -8036,7 +8035,10 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, long *timeout, gfp_t gfp_flags)
                .flags = RPC_TASK_ASYNC,
        };
        struct pnfs_layout_segment *lseg = NULL;
-       struct nfs4_exception exception = { .timeout = *timeout };
+       struct nfs4_exception exception = {
+               .inode = inode,
+               .timeout = *timeout,
+       };
        int status = 0;
 
        dprintk("--> %s\n", __func__);
index 9679f47493640d4ce4a8a5e571727ece40e13625..834b875900d62addf6db7f6eb590ce5c2d1b09bc 100644 (file)
@@ -1488,9 +1488,9 @@ static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs
                                        }
                                        spin_unlock(&state->state_lock);
                                }
-                               nfs4_put_open_state(state);
                                clear_bit(NFS_STATE_RECLAIM_NOGRACE,
                                        &state->flags);
+                               nfs4_put_open_state(state);
                                spin_lock(&sp->so_lock);
                                goto restart;
                        }
index 0c7e0d45a4de6ee1fba11c40417d3fb01678049b..0fbe734cc38cb8d27ba2c8efaf985a676f4d0cd8 100644 (file)
@@ -361,8 +361,10 @@ pnfs_layout_remove_lseg(struct pnfs_layout_hdr *lo,
        list_del_init(&lseg->pls_list);
        /* Matched by pnfs_get_layout_hdr in pnfs_layout_insert_lseg */
        atomic_dec(&lo->plh_refcount);
-       if (list_empty(&lo->plh_segs))
+       if (list_empty(&lo->plh_segs)) {
+               set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags);
                clear_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags);
+       }
        rpc_wake_up(&NFS_SERVER(inode)->roc_rpcwaitq);
 }
 
@@ -1290,6 +1292,7 @@ alloc_init_layout_hdr(struct inode *ino,
        INIT_LIST_HEAD(&lo->plh_bulk_destroy);
        lo->plh_inode = ino;
        lo->plh_lc_cred = get_rpccred(ctx->cred);
+       lo->plh_flags |= 1 << NFS_LAYOUT_INVALID_STID;
        return lo;
 }
 
@@ -1297,6 +1300,8 @@ static struct pnfs_layout_hdr *
 pnfs_find_alloc_layout(struct inode *ino,
                       struct nfs_open_context *ctx,
                       gfp_t gfp_flags)
+       __releases(&ino->i_lock)
+       __acquires(&ino->i_lock)
 {
        struct nfs_inode *nfsi = NFS_I(ino);
        struct pnfs_layout_hdr *new = NULL;
@@ -1565,8 +1570,7 @@ pnfs_update_layout(struct inode *ino,
         * stateid, or it has been invalidated, then we must use the open
         * stateid.
         */
-       if (lo->plh_stateid.seqid == 0 ||
-           test_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags)) {
+       if (test_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags)) {
 
                /*
                 * The first layoutget for the file. Need to serialize per
index 0dfc476da3e10918bcf0242d779cda343e6dac61..b38e3c0dc7908566acd1c8bc9d51402e8069ef6a 100644 (file)
@@ -247,7 +247,11 @@ void pnfs_fetch_commit_bucket_list(struct list_head *pages,
 }
 
 /* Helper function for pnfs_generic_commit_pagelist to catch an empty
- * page list. This can happen when two commits race. */
+ * page list. This can happen when two commits race.
+ *
+ * This must be called instead of nfs_init_commit - call one or the other, but
+ * not both!
+ */
 static bool
 pnfs_generic_commit_cancel_empty_pagelist(struct list_head *pages,
                                          struct nfs_commit_data *data,
@@ -256,7 +260,11 @@ pnfs_generic_commit_cancel_empty_pagelist(struct list_head *pages,
        if (list_empty(pages)) {
                if (atomic_dec_and_test(&cinfo->mds->rpcs_out))
                        wake_up_atomic_t(&cinfo->mds->rpcs_out);
-               nfs_commitdata_release(data);
+               /* don't call nfs_commitdata_release - it tries to put
+                * the open_context which is not acquired until nfs_init_commit
+                * which has not been called on @data */
+               WARN_ON_ONCE(data->context);
+               nfs_commit_free(data);
                return true;
        }
 
index 6776d7a7839e0e8afcd966d296678fbc0d69ca7d..572e5b3b06f1566f40e7df7be33b8f14aafd2e16 100644 (file)
@@ -367,13 +367,13 @@ readpage_async_filler(void *data, struct page *page)
                nfs_list_remove_request(new);
                nfs_readpage_release(new);
                error = desc->pgio->pg_error;
-               goto out_unlock;
+               goto out;
        }
        return 0;
 out_error:
        error = PTR_ERR(new);
-out_unlock:
        unlock_page(page);
+out:
        return error;
 }
 
index c2a6b08940228c838375ed9ef37c548c5052a2cc..5c9d2d80ff70bf851e835c97adf775bbac963641 100644 (file)
@@ -505,6 +505,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir)
        struct dentry *upper;
        struct dentry *opaquedir = NULL;
        int err;
+       int flags = 0;
 
        if (WARN_ON(!workdir))
                return -EROFS;
@@ -534,46 +535,39 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir)
        if (err)
                goto out_dput;
 
-       whiteout = ovl_whiteout(workdir, dentry);
-       err = PTR_ERR(whiteout);
-       if (IS_ERR(whiteout))
+       upper = lookup_one_len(dentry->d_name.name, upperdir,
+                              dentry->d_name.len);
+       err = PTR_ERR(upper);
+       if (IS_ERR(upper))
                goto out_unlock;
 
-       upper = ovl_dentry_upper(dentry);
-       if (!upper) {
-               upper = lookup_one_len(dentry->d_name.name, upperdir,
-                                      dentry->d_name.len);
-               err = PTR_ERR(upper);
-               if (IS_ERR(upper))
-                       goto kill_whiteout;
-
-               err = ovl_do_rename(wdir, whiteout, udir, upper, 0);
-               dput(upper);
-               if (err)
-                       goto kill_whiteout;
-       } else {
-               int flags = 0;
+       err = -ESTALE;
+       if ((opaquedir && upper != opaquedir) ||
+           (!opaquedir && ovl_dentry_upper(dentry) &&
+            upper != ovl_dentry_upper(dentry))) {
+               goto out_dput_upper;
+       }
 
-               if (opaquedir)
-                       upper = opaquedir;
-               err = -ESTALE;
-               if (upper->d_parent != upperdir)
-                       goto kill_whiteout;
+       whiteout = ovl_whiteout(workdir, dentry);
+       err = PTR_ERR(whiteout);
+       if (IS_ERR(whiteout))
+               goto out_dput_upper;
 
-               if (is_dir)
-                       flags |= RENAME_EXCHANGE;
+       if (d_is_dir(upper))
+               flags = RENAME_EXCHANGE;
 
-               err = ovl_do_rename(wdir, whiteout, udir, upper, flags);
-               if (err)
-                       goto kill_whiteout;
+       err = ovl_do_rename(wdir, whiteout, udir, upper, flags);
+       if (err)
+               goto kill_whiteout;
+       if (flags)
+               ovl_cleanup(wdir, upper);
 
-               if (is_dir)
-                       ovl_cleanup(wdir, upper);
-       }
        ovl_dentry_version_inc(dentry->d_parent);
 out_d_drop:
        d_drop(dentry);
        dput(whiteout);
+out_dput_upper:
+       dput(upper);
 out_unlock:
        unlock_rename(workdir, upperdir);
 out_dput:
index 1dbeab6cf96e54c7e43cf040ef3d909ce6fe2071..d1cdc60dd68fa25aa74e2474a09e07aab08b7e26 100644 (file)
@@ -59,16 +59,40 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr)
        if (err)
                goto out;
 
+       if (attr->ia_valid & ATTR_SIZE) {
+               struct inode *realinode = d_inode(ovl_dentry_real(dentry));
+
+               err = -ETXTBSY;
+               if (atomic_read(&realinode->i_writecount) < 0)
+                       goto out_drop_write;
+       }
+
        err = ovl_copy_up(dentry);
        if (!err) {
+               struct inode *winode = NULL;
+
                upperdentry = ovl_dentry_upper(dentry);
 
+               if (attr->ia_valid & ATTR_SIZE) {
+                       winode = d_inode(upperdentry);
+                       err = get_write_access(winode);
+                       if (err)
+                               goto out_drop_write;
+               }
+
+               if (attr->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
+                       attr->ia_valid &= ~ATTR_MODE;
+
                inode_lock(upperdentry->d_inode);
                err = notify_change(upperdentry, attr, NULL);
                if (!err)
                        ovl_copyattr(upperdentry->d_inode, dentry->d_inode);
                inode_unlock(upperdentry->d_inode);
+
+               if (winode)
+                       put_write_access(winode);
        }
+out_drop_write:
        ovl_drop_write(dentry);
 out:
        return err;
@@ -121,16 +145,18 @@ int ovl_permission(struct inode *inode, int mask)
 
                err = vfs_getattr(&realpath, &stat);
                if (err)
-                       return err;
+                       goto out_dput;
 
+               err = -ESTALE;
                if ((stat.mode ^ inode->i_mode) & S_IFMT)
-                       return -ESTALE;
+                       goto out_dput;
 
                inode->i_mode = stat.mode;
                inode->i_uid = stat.uid;
                inode->i_gid = stat.gid;
 
-               return generic_permission(inode, mask);
+               err = generic_permission(inode, mask);
+               goto out_dput;
        }
 
        /* Careful in RCU walk mode */
@@ -387,12 +413,11 @@ struct inode *ovl_new_inode(struct super_block *sb, umode_t mode,
        if (!inode)
                return NULL;
 
-       mode &= S_IFMT;
-
        inode->i_ino = get_next_ino();
        inode->i_mode = mode;
        inode->i_flags |= S_NOATIME | S_NOCMTIME;
 
+       mode &= S_IFMT;
        switch (mode) {
        case S_IFDIR:
                inode->i_private = oe;
index 4bd9b5ba8f42008b7f72cf271fac89c67b7eca6d..cfbca53590d078b89ca613e65c3b10486763f656 100644 (file)
@@ -187,6 +187,7 @@ static inline void ovl_copyattr(struct inode *from, struct inode *to)
 {
        to->i_uid = from->i_uid;
        to->i_gid = from->i_gid;
+       to->i_mode = from->i_mode;
 }
 
 /* dir.c */
index ce02f46029da7c3aa7c69be9291d62c81baeb642..9a7693d5f8fffb38bd4a07804e4627ed7f50361a 100644 (file)
@@ -1082,11 +1082,13 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
                        if (err < 0)
                                goto out_put_workdir;
 
-                       if (!err) {
-                               pr_err("overlayfs: upper fs needs to support d_type.\n");
-                               err = -EINVAL;
-                               goto out_put_workdir;
-                       }
+                       /*
+                        * We allowed this configuration and don't want to
+                        * break users over kernel upgrade. So warn instead
+                        * of erroring out.
+                        */
+                       if (!err)
+                               pr_warn("overlayfs: upper fs needs to support d_type.\n");
                }
        }
 
index dbca7375deefa3f7d2499f516697ffdd28178546..63a6ff2cfc6821961265136e1f62cf455da3b195 100644 (file)
@@ -1575,6 +1575,12 @@ xfs_ioc_swapext(
                goto out_put_tmp_file;
        }
 
+       if (f.file->f_op != &xfs_file_operations ||
+           tmp.file->f_op != &xfs_file_operations) {
+               error = -EINVAL;
+               goto out_put_tmp_file;
+       }
+
        ip = XFS_I(file_inode(f.file));
        tip = XFS_I(file_inode(tmp.file));
 
index 788c6c35291a42c448296876ab6c32bff25a5200..c1a524de67c5b8ab8f1c63ea77365dec085fcd91 100644 (file)
@@ -420,6 +420,13 @@ static inline struct acpi_data_node *to_acpi_data_node(struct fwnode_handle *fwn
                container_of(fwnode, struct acpi_data_node, fwnode) : NULL;
 }
 
+static inline bool acpi_data_node_match(struct fwnode_handle *fwnode,
+                                       const char *name)
+{
+       return is_acpi_data_node(fwnode) ?
+               (!strcmp(to_acpi_data_node(fwnode)->name, name)) : false;
+}
+
 static inline struct fwnode_handle *acpi_fwnode_handle(struct acpi_device *adev)
 {
        return &adev->fwnode;
index 797ae2ec8eee2d129653514cea7b891db96e38d6..29c691265b49357bc0d036b71897348806c58e6c 100644 (file)
@@ -78,6 +78,7 @@
 
 /* ACPI PCI Interrupt Link (pci_link.c) */
 
+int acpi_irq_penalty_init(void);
 int acpi_pci_link_allocate_irq(acpi_handle handle, int index, int *triggering,
                               int *polarity, char **name);
 int acpi_pci_link_free_irq(acpi_handle handle);
index 4e4c21491c4188a4401887feb55978fe58b1ff47..1ff3a76c265dbcbaea2f427359bb16c75e719200 100644 (file)
@@ -192,7 +192,7 @@ ACPI_INIT_GLOBAL(u8, acpi_gbl_do_not_use_xsdt, FALSE);
 /*
  * Optionally support group module level code.
  */
-ACPI_INIT_GLOBAL(u8, acpi_gbl_group_module_level_code, FALSE);
+ACPI_INIT_GLOBAL(u8, acpi_gbl_group_module_level_code, TRUE);
 
 /*
  * Optionally use 32-bit FADT addresses if and when there is a conflict
index 6a67ab94b553363934bc9c2e07eb12d6c8a977f7..081d0f258d4c98d3cb36615c2dd252b630052b99 100644 (file)
 
 #define INIT_TEXT                                                      \
        *(.init.text)                                                   \
+       *(.text.startup)                                                \
        MEM_DISCARD(init.text)
 
 #define EXIT_DATA                                                      \
        *(.exit.data)                                                   \
+       *(.fini_array)                                                  \
+       *(.dtors)                                                       \
        MEM_DISCARD(exit.data)                                          \
        MEM_DISCARD(exit.rodata)
 
 #define EXIT_TEXT                                                      \
        *(.exit.text)                                                   \
+       *(.text.exit)                                                   \
        MEM_DISCARD(exit.text)
 
 #define EXIT_CALL                                                      \
index 9094599a11509c190bfc5ae4df853531f5a442da..33466bfc6440363d4f36d1ca2f09d44098098e8c 100644 (file)
        INTEL_VGA_DEVICE(0x5906, info), /* ULT GT1 */ \
        INTEL_VGA_DEVICE(0x590E, info), /* ULX GT1 */ \
        INTEL_VGA_DEVICE(0x5902, info), /* DT  GT1 */ \
+       INTEL_VGA_DEVICE(0x5908, info), /* Halo GT1 */ \
        INTEL_VGA_DEVICE(0x590B, info), /* Halo GT1 */ \
        INTEL_VGA_DEVICE(0x590A, info) /* SRV GT1 */
 
        INTEL_VGA_DEVICE(0x591D, info) /* WKS GT2 */
 
 #define INTEL_KBL_GT3_IDS(info) \
+       INTEL_VGA_DEVICE(0x5923, info), /* ULT GT3 */ \
        INTEL_VGA_DEVICE(0x5926, info), /* ULT GT3 */ \
-       INTEL_VGA_DEVICE(0x592B, info), /* Halo GT3 */ \
-       INTEL_VGA_DEVICE(0x592A, info) /* SRV GT3 */
+       INTEL_VGA_DEVICE(0x5927, info) /* ULT GT3 */
 
 #define INTEL_KBL_GT4_IDS(info) \
-       INTEL_VGA_DEVICE(0x5932, info), /* DT  GT4 */ \
-       INTEL_VGA_DEVICE(0x593B, info), /* Halo GT4 */ \
-       INTEL_VGA_DEVICE(0x593A, info), /* SRV GT4 */ \
-       INTEL_VGA_DEVICE(0x593D, info)  /* WKS GT4 */
+       INTEL_VGA_DEVICE(0x593B, info) /* Halo GT4 */
 
 #define INTEL_KBL_IDS(info) \
        INTEL_KBL_GT1_IDS(info), \
index c801d9028e37388bc30a8254be8c4ae7df6acb22..4cecb0b75b9cb702ba2189f77b1ebab78d5bd6ab 100644 (file)
@@ -316,6 +316,20 @@ ttm_bo_reference(struct ttm_buffer_object *bo)
  */
 extern int ttm_bo_wait(struct ttm_buffer_object *bo,
                       bool interruptible, bool no_wait);
+
+/**
+ * ttm_bo_mem_compat - Check if proposed placement is compatible with a bo
+ *
+ * @placement:  Return immediately if buffer is busy.
+ * @mem:  The struct ttm_mem_reg indicating the region where the bo resides
+ * @new_flags: Describes compatible placement found
+ *
+ * Returns true if the placement is compatible
+ */
+extern bool ttm_bo_mem_compat(struct ttm_placement *placement,
+                             struct ttm_mem_reg *mem,
+                             uint32_t *new_flags);
+
 /**
  * ttm_bo_validate
  *
index fe389ac3148915416c4fe7dba63a6b5159a3bc3d..92e7e97ca8ff0f95ef9dba95b3e7078dd8c73049 100644 (file)
 #ifndef __ASM_ARM_KVM_PMU_H
 #define __ASM_ARM_KVM_PMU_H
 
-#ifdef CONFIG_KVM_ARM_PMU
-
 #include <linux/perf_event.h>
 #include <asm/perf_event.h>
 
 #define ARMV8_PMU_CYCLE_IDX            (ARMV8_PMU_MAX_COUNTERS - 1)
 
+#ifdef CONFIG_KVM_ARM_PMU
+
 struct kvm_pmc {
        u8 idx; /* index into the pmu->pmc array */
        struct perf_event *perf_event;
index 288fac5294f5e26aacfe97ac772d5f0247aa1dd2..03039c472e951a718075931f9b24916610aed7e7 100644 (file)
@@ -568,6 +568,12 @@ static inline struct acpi_data_node *to_acpi_data_node(struct fwnode_handle *fwn
        return NULL;
 }
 
+static inline bool acpi_data_node_match(struct fwnode_handle *fwnode,
+                                       const char *name)
+{
+       return false;
+}
+
 static inline struct fwnode_handle *acpi_fwnode_handle(struct acpi_device *adev)
 {
        return NULL;
index 961a417d641e5221264e75b4d987fc3015607d41..e38e3fc13ea8764a66d4c84a6dea1bee3f4e630b 100644 (file)
@@ -26,7 +26,6 @@
 #include <linux/sched.h>
 #include <linux/ptrace.h>
 #include <uapi/linux/audit.h>
-#include <linux/tty.h>
 
 #define AUDIT_INO_UNSET ((unsigned long)-1)
 #define AUDIT_DEV_UNSET ((dev_t)-1)
@@ -348,23 +347,6 @@ static inline unsigned int audit_get_sessionid(struct task_struct *tsk)
        return tsk->sessionid;
 }
 
-static inline struct tty_struct *audit_get_tty(struct task_struct *tsk)
-{
-       struct tty_struct *tty = NULL;
-       unsigned long flags;
-
-       spin_lock_irqsave(&tsk->sighand->siglock, flags);
-       if (tsk->signal)
-               tty = tty_kref_get(tsk->signal->tty);
-       spin_unlock_irqrestore(&tsk->sighand->siglock, flags);
-       return tty;
-}
-
-static inline void audit_put_tty(struct tty_struct *tty)
-{
-       tty_kref_put(tty);
-}
-
 extern void __audit_ipc_obj(struct kern_ipc_perm *ipcp);
 extern void __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, umode_t mode);
 extern void __audit_bprm(struct linux_binprm *bprm);
@@ -522,12 +504,6 @@ static inline unsigned int audit_get_sessionid(struct task_struct *tsk)
 {
        return -1;
 }
-static inline struct tty_struct *audit_get_tty(struct task_struct *tsk)
-{
-       return NULL;
-}
-static inline void audit_put_tty(struct tty_struct *tty)
-{ }
 static inline void audit_ipc_obj(struct kern_ipc_perm *ipcp)
 { }
 static inline void audit_ipc_set_perm(unsigned long qbytes, uid_t uid,
index e6b41f42602ba7cc32e2945e15309d06f3028f95..3db25df396cbb88d2135874818f4fb38a71b2eee 100644 (file)
@@ -159,6 +159,7 @@ struct bcma_host_ops {
 #define BCMA_CORE_DEFAULT              0xFFF
 
 #define BCMA_MAX_NR_CORES              16
+#define BCMA_CORE_SIZE                 0x1000
 
 /* Chip IDs of PCIe devices */
 #define BCMA_CHIP_ID_BCM4313   0x4313
index 8ee27b8afe81c5d45b66f7b629c976ed8138d26a..0de4de6dd43e09aae40a72500b0e5a2fb9e89c1e 100644 (file)
@@ -111,6 +111,31 @@ enum bpf_access_type {
        BPF_WRITE = 2
 };
 
+/* types of values stored in eBPF registers */
+enum bpf_reg_type {
+       NOT_INIT = 0,            /* nothing was written into register */
+       UNKNOWN_VALUE,           /* reg doesn't contain a valid pointer */
+       PTR_TO_CTX,              /* reg points to bpf_context */
+       CONST_PTR_TO_MAP,        /* reg points to struct bpf_map */
+       PTR_TO_MAP_VALUE,        /* reg points to map element value */
+       PTR_TO_MAP_VALUE_OR_NULL,/* points to map elem value or NULL */
+       FRAME_PTR,               /* reg == frame_pointer */
+       PTR_TO_STACK,            /* reg == frame_pointer + imm */
+       CONST_IMM,               /* constant integer value */
+
+       /* PTR_TO_PACKET represents:
+        * skb->data
+        * skb->data + imm
+        * skb->data + (u16) var
+        * skb->data + (u16) var + imm
+        * if (range > 0) then [ptr, ptr + range - off) is safe to access
+        * if (id > 0) means that some 'var' was added
+        * if (off > 0) menas that 'imm' was added
+        */
+       PTR_TO_PACKET,
+       PTR_TO_PACKET_END,       /* skb->data + headlen */
+};
+
 struct bpf_prog;
 
 struct bpf_verifier_ops {
@@ -120,7 +145,8 @@ struct bpf_verifier_ops {
        /* return true if 'size' wide access at offset 'off' within bpf_context
         * with 'type' (read or write) is allowed
         */
-       bool (*is_valid_access)(int off, int size, enum bpf_access_type type);
+       bool (*is_valid_access)(int off, int size, enum bpf_access_type type,
+                               enum bpf_reg_type *reg_type);
 
        u32 (*convert_ctx_access)(enum bpf_access_type type, int dst_reg,
                                  int src_reg, int ctx_off,
@@ -238,6 +264,10 @@ static inline struct bpf_prog *bpf_prog_get(u32 ufd)
 static inline void bpf_prog_put(struct bpf_prog *prog)
 {
 }
+
+static inline void bpf_prog_put_rcu(struct bpf_prog *prog)
+{
+}
 #endif /* CONFIG_BPF_SYSCALL */
 
 /* verifier prototypes for helper functions called from eBPF programs */
index 6fc31ef1da2d8e9efe5d91b8e4349ed4fe0dd809..8f74f3d61894a4ad58b5cc3457f68f318a84c970 100644 (file)
@@ -467,7 +467,11 @@ static inline void bpf_prog_unlock_ro(struct bpf_prog *fp)
 }
 #endif /* CONFIG_DEBUG_SET_MODULE_RONX */
 
-int sk_filter(struct sock *sk, struct sk_buff *skb);
+int sk_filter_trim_cap(struct sock *sk, struct sk_buff *skb, unsigned int cap);
+static inline int sk_filter(struct sock *sk, struct sk_buff *skb)
+{
+       return sk_filter_trim_cap(sk, skb, 1);
+}
 
 struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err);
 void bpf_prog_free(struct bpf_prog *fp);
index 419fb9e03447aff8aef55934e89bbd844a28d7e7..f0a7a0320300bae6eca05e27a2083f80ed54282e 100644 (file)
@@ -94,7 +94,7 @@ static inline int split_huge_page(struct page *page)
 void deferred_split_huge_page(struct page *page);
 
 void __split_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
-               unsigned long address, bool freeze);
+               unsigned long address, bool freeze, struct page *page);
 
 #define split_huge_pmd(__vma, __pmd, __address)                                \
        do {                                                            \
@@ -102,7 +102,7 @@ void __split_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
                if (pmd_trans_huge(*____pmd)                            \
                                        || pmd_devmap(*____pmd))        \
                        __split_huge_pmd(__vma, __pmd, __address,       \
-                                               false);                 \
+                                               false, NULL);           \
        }  while (0)
 
 
index 7c27fa1030e873d1841f3e291e9fc806992967e3..feb04ea20f11cb6e1a39fc93f7ab9d054907de99 100644 (file)
@@ -52,6 +52,12 @@ struct sock *inet_diag_find_one_icsk(struct net *net,
 
 int inet_diag_bc_sk(const struct nlattr *_bc, struct sock *sk);
 
+void inet_diag_msg_common_fill(struct inet_diag_msg *r, struct sock *sk);
+
+int inet_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb,
+                            struct inet_diag_msg *r, int ext,
+                            struct user_namespace *user_ns);
+
 extern int  inet_diag_register(const struct inet_diag_handler *handler);
 extern void inet_diag_unregister(const struct inet_diag_handler *handler);
 #endif /* _INET_DIAG_H_ */
index a805474df4abd8c70c83bdbd3b383ad9c02eca1c..56e6069d245271539f14bf34c204122665e28ab6 100644 (file)
@@ -97,6 +97,11 @@ enum mem_cgroup_events_target {
 #define MEM_CGROUP_ID_SHIFT    16
 #define MEM_CGROUP_ID_MAX      USHRT_MAX
 
+struct mem_cgroup_id {
+       int id;
+       atomic_t ref;
+};
+
 struct mem_cgroup_stat_cpu {
        long count[MEMCG_NR_STAT];
        unsigned long events[MEMCG_NR_EVENTS];
@@ -172,6 +177,9 @@ enum memcg_kmem_state {
 struct mem_cgroup {
        struct cgroup_subsys_state css;
 
+       /* Private memcg ID. Used to ID objects that outlive the cgroup */
+       struct mem_cgroup_id id;
+
        /* Accounted resources */
        struct page_counter memory;
        struct page_counter swap;
@@ -330,22 +338,9 @@ static inline unsigned short mem_cgroup_id(struct mem_cgroup *memcg)
        if (mem_cgroup_disabled())
                return 0;
 
-       return memcg->css.id;
-}
-
-/**
- * mem_cgroup_from_id - look up a memcg from an id
- * @id: the id to look up
- *
- * Caller must hold rcu_read_lock() and use css_tryget() as necessary.
- */
-static inline struct mem_cgroup *mem_cgroup_from_id(unsigned short id)
-{
-       struct cgroup_subsys_state *css;
-
-       css = css_from_id(id, &memory_cgrp_subsys);
-       return mem_cgroup_from_css(css);
+       return memcg->id.id;
 }
+struct mem_cgroup *mem_cgroup_from_id(unsigned short id);
 
 /**
  * parent_mem_cgroup - find the accounting parent of a memcg
index d55a42297d4914a6da0c86266810c9b873af9c15..58ab4c0fe761393cd614e4fa81d52a5d0fa4627f 100644 (file)
@@ -14,6 +14,7 @@
 #define _WM_ARIZONA_CORE_H
 
 #include <linux/interrupt.h>
+#include <linux/notifier.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 #include <linux/mfd/arizona/pdata.h>
@@ -148,8 +149,17 @@ struct arizona {
        uint16_t dac_comp_coeff;
        uint8_t dac_comp_enabled;
        struct mutex dac_comp_lock;
+
+       struct blocking_notifier_head notifier;
 };
 
+static inline int arizona_call_notifiers(struct arizona *arizona,
+                                        unsigned long event,
+                                        void *data)
+{
+       return blocking_notifier_call_chain(&arizona->notifier, event, data);
+}
+
 int arizona_clk32k_enable(struct arizona *arizona);
 int arizona_clk32k_disable(struct arizona *arizona);
 
index c18a4c19d6fcedcd66adebdb45c383af09930408..ce9230af09c20c0498e1111fdf148b8f6406b30e 100644 (file)
@@ -171,7 +171,7 @@ static inline int da9052_group_read(struct da9052 *da9052, unsigned char reg,
 static inline int da9052_group_write(struct da9052 *da9052, unsigned char reg,
                                      unsigned reg_cnt, unsigned char *val)
 {
-       int ret;
+       int ret = 0;
        int i;
 
        for (i = 0; i < reg_cnt; i++) {
index 80dec87a94f8431024d88a6303f1f9189b2114e6..d46a0e7f144df536bfba29325904a2a0bf8c7cf7 100644 (file)
@@ -466,6 +466,7 @@ enum {
 enum {
        MLX4_INTERFACE_STATE_UP         = 1 << 0,
        MLX4_INTERFACE_STATE_DELETION   = 1 << 1,
+       MLX4_INTERFACE_STATE_SHUTDOWN   = 1 << 2,
 };
 
 #define MSTR_SM_CHANGE_MASK (MLX4_EQ_PORT_INFO_MSTR_SM_SL_CHANGE_MASK | \
index 80776d0c52dc9c48b7a02842caacbf9699e73507..fd72ecf0ce9fe55e3e0b1be1bc1ea6231c4378f2 100644 (file)
@@ -629,6 +629,7 @@ struct mlx5_cmd_work_ent {
        void                   *uout;
        int                     uout_size;
        mlx5_cmd_cbk_t          callback;
+       struct delayed_work     cb_timeout_work;
        void                   *context;
        int                     idx;
        struct completion       done;
index 9aa49a05fe389214c46b6ea48577c813d525a874..25aa03b51c4e1e2f6fa1a9d257493dc62098f982 100644 (file)
@@ -251,7 +251,8 @@ do {                                                                        \
        DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt);                 \
        if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT) &&        \
            net_ratelimit())                                            \
-               __dynamic_pr_debug(&descriptor, fmt, ##__VA_ARGS__);    \
+               __dynamic_pr_debug(&descriptor, pr_fmt(fmt),            \
+                                  ##__VA_ARGS__);                      \
 } while (0)
 #elif defined(DEBUG)
 #define net_dbg_ratelimited(fmt, ...)                          \
index f45929ce815725d868261e9a2585ac53d0c8f128..da4b33bea9828857a28ab9060cbfe2d491cd3058 100644 (file)
@@ -4145,6 +4145,13 @@ static inline void netif_keep_dst(struct net_device *dev)
        dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM);
 }
 
+/* return true if dev can't cope with mtu frames that need vlan tag insertion */
+static inline bool netif_reduces_vlan_mtu(struct net_device *dev)
+{
+       /* TODO: reserve and use an additional IFF bit, if we get more users */
+       return dev->priv_flags & IFF_MACSEC;
+}
+
 extern struct pernet_operations __net_initdata loopback_net_ops;
 
 /* Logging, debugging and troubleshooting/diagnostic helpers. */
index 74eb28cadbef032017ae12fe4d11ab4f70996d63..310e32faa7bfdf9d7c8b2fb0d5c0cdfa8c65d035 100644 (file)
@@ -238,13 +238,6 @@ static inline unsigned long of_read_ulong(const __be32 *cell, int size)
 #define OF_ROOT_NODE_SIZE_CELLS_DEFAULT 1
 #endif
 
-/* Default string compare functions, Allow arch asm/prom.h to override */
-#if !defined(of_compat_cmp)
-#define of_compat_cmp(s1, s2, l)       strcasecmp((s1), (s2))
-#define of_prop_cmp(s1, s2)            strcmp((s1), (s2))
-#define of_node_cmp(s1, s2)            strcasecmp((s1), (s2))
-#endif
-
 #define OF_IS_DYNAMIC(x) test_bit(OF_DYNAMIC, &x->_flags)
 #define OF_MARK_DYNAMIC(x) set_bit(OF_DYNAMIC, &x->_flags)
 
@@ -726,6 +719,13 @@ static inline void of_property_clear_flag(struct property *p, unsigned long flag
 #define of_match_node(_matches, _node) NULL
 #endif /* CONFIG_OF */
 
+/* Default string compare functions, Allow arch asm/prom.h to override */
+#if !defined(of_compat_cmp)
+#define of_compat_cmp(s1, s2, l)       strcasecmp((s1), (s2))
+#define of_prop_cmp(s1, s2)            strcmp((s1), (s2))
+#define of_node_cmp(s1, s2)            strcasecmp((s1), (s2))
+#endif
+
 #if defined(CONFIG_OF) && defined(CONFIG_NUMA)
 extern int of_node_to_nid(struct device_node *np);
 #else
index 5b5a80cc59265882813cddba568b0875797e5e6b..c818772d9f9d13538309226a89894b03a78c1aa4 100644 (file)
@@ -43,10 +43,8 @@ struct posix_acl_entry {
 };
 
 struct posix_acl {
-       union {
-               atomic_t                a_refcount;
-               struct rcu_head         a_rcu;
-       };
+       atomic_t                a_refcount;
+       struct rcu_head         a_rcu;
        unsigned int            a_count;
        struct posix_acl_entry  a_entries[0];
 };
index ecab11e407942fcfea3b92d10dc938dd365460cd..3a2f9ae25c86c43827cdcd0c4602cd5e281a4cf9 100644 (file)
@@ -77,6 +77,9 @@ struct fwnode_handle *device_get_next_child_node(struct device *dev,
        for (child = device_get_next_child_node(dev, NULL); child;      \
             child = device_get_next_child_node(dev, child))
 
+struct fwnode_handle *device_get_named_child_node(struct device *dev,
+                                                 const char *childname);
+
 void fwnode_handle_put(struct fwnode_handle *fwnode);
 
 unsigned int device_get_child_node_count(struct device *dev);
index 908b67c847cd656489f0a679422852e7c845d18f..c038ae36b10e7580645520d443d5faa8a792e645 100644 (file)
@@ -464,6 +464,8 @@ static inline bool pwm_can_sleep(struct pwm_device *pwm)
 
 static inline void pwm_apply_args(struct pwm_device *pwm)
 {
+       struct pwm_state state = { };
+
        /*
         * PWM users calling pwm_apply_args() expect to have a fresh config
         * where the polarity and period are set according to pwm_args info.
@@ -476,18 +478,20 @@ static inline void pwm_apply_args(struct pwm_device *pwm)
         * at startup (even if they are actually enabled), thus authorizing
         * polarity setting.
         *
-        * Instead of setting ->enabled to false, we call pwm_disable()
-        * before pwm_set_polarity() to ensure that everything is configured
-        * as expected, and the PWM is really disabled when the user request
-        * it.
+        * To fulfill this requirement, we apply a new state which disables
+        * the PWM device and set the reference period and polarity config.
         *
         * Note that PWM users requiring a smooth handover between the
         * bootloader and the kernel (like critical regulators controlled by
         * PWM devices) will have to switch to the atomic API and avoid calling
         * pwm_apply_args().
         */
-       pwm_disable(pwm);
-       pwm_set_polarity(pwm, pwm->args.polarity);
+
+       state.enabled = false;
+       state.polarity = pwm->args.polarity;
+       state.period = pwm->args.period;
+
+       pwm_apply_state(pwm, &state);
 }
 
 struct pwm_lookup {
index 6ae8cb4a61d39969283a382076c8cf261a77bfa6..6c876a63558d71c4ff3a957fba45cfc3b2bbbc3a 100644 (file)
@@ -49,6 +49,7 @@ struct qed_start_vport_params {
        bool drop_ttl0;
        u8 vport_id;
        u16 mtu;
+       bool clear_stats;
 };
 
 struct qed_stop_rxq_params {
index cb4b7e8cee81a40cbfedf0a781c7f701ed9e6ee0..eca6f626c16e7d513489341327588846e1239ffb 100644 (file)
@@ -407,6 +407,7 @@ static inline __must_check
 void **radix_tree_iter_retry(struct radix_tree_iter *iter)
 {
        iter->next_index = iter->index;
+       iter->tags = 0;
        return NULL;
 }
 
index ec0306ce7b92ab6f0e4bb420347ca9301b1355e3..45a4abeb6acba0193fffd134176a956950b53a4a 100644 (file)
@@ -84,8 +84,8 @@ static inline struct reset_control *__devm_reset_control_get(
 #endif /* CONFIG_RESET_CONTROLLER */
 
 /**
- * reset_control_get - Lookup and obtain an exclusive reference to a
- *                     reset controller.
+ * reset_control_get_exclusive - Lookup and obtain an exclusive reference
+ *                               to a reset controller.
  * @dev: device to be reset by the controller
  * @id: reset line name
  *
@@ -98,8 +98,8 @@ static inline struct reset_control *__devm_reset_control_get(
  *
  * Use of id names is optional.
  */
-static inline struct reset_control *__must_check reset_control_get(
-                                       struct device *dev, const char *id)
+static inline struct reset_control *
+__must_check reset_control_get_exclusive(struct device *dev, const char *id)
 {
 #ifndef CONFIG_RESET_CONTROLLER
        WARN_ON(1);
@@ -107,12 +107,6 @@ static inline struct reset_control *__must_check reset_control_get(
        return __of_reset_control_get(dev ? dev->of_node : NULL, id, 0, 0);
 }
 
-static inline struct reset_control *reset_control_get_optional(
-                                       struct device *dev, const char *id)
-{
-       return __of_reset_control_get(dev ? dev->of_node : NULL, id, 0, 0);
-}
-
 /**
  * reset_control_get_shared - Lookup and obtain a shared reference to a
  *                            reset controller.
@@ -141,9 +135,21 @@ static inline struct reset_control *reset_control_get_shared(
        return __of_reset_control_get(dev ? dev->of_node : NULL, id, 0, 1);
 }
 
+static inline struct reset_control *reset_control_get_optional_exclusive(
+                                       struct device *dev, const char *id)
+{
+       return __of_reset_control_get(dev ? dev->of_node : NULL, id, 0, 0);
+}
+
+static inline struct reset_control *reset_control_get_optional_shared(
+                                       struct device *dev, const char *id)
+{
+       return __of_reset_control_get(dev ? dev->of_node : NULL, id, 0, 1);
+}
+
 /**
- * of_reset_control_get - Lookup and obtain an exclusive reference to a
- *                        reset controller.
+ * of_reset_control_get_exclusive - Lookup and obtain an exclusive reference
+ *                                  to a reset controller.
  * @node: device to be reset by the controller
  * @id: reset line name
  *
@@ -151,15 +157,41 @@ static inline struct reset_control *reset_control_get_shared(
  *
  * Use of id names is optional.
  */
-static inline struct reset_control *of_reset_control_get(
+static inline struct reset_control *of_reset_control_get_exclusive(
                                struct device_node *node, const char *id)
 {
        return __of_reset_control_get(node, id, 0, 0);
 }
 
 /**
- * of_reset_control_get_by_index - Lookup and obtain an exclusive reference to
- *                                 a reset controller by index.
+ * of_reset_control_get_shared - Lookup and obtain an shared reference
+ *                               to a reset controller.
+ * @node: device to be reset by the controller
+ * @id: reset line name
+ *
+ * When a reset-control is shared, the behavior of reset_control_assert /
+ * deassert is changed, the reset-core will keep track of a deassert_count
+ * and only (re-)assert the reset after reset_control_assert has been called
+ * as many times as reset_control_deassert was called. Also see the remark
+ * about shared reset-controls in the reset_control_assert docs.
+ *
+ * Calling reset_control_assert without first calling reset_control_deassert
+ * is not allowed on a shared reset control. Calling reset_control_reset is
+ * also not allowed on a shared reset control.
+ * Returns a struct reset_control or IS_ERR() condition containing errno.
+ *
+ * Use of id names is optional.
+ */
+static inline struct reset_control *of_reset_control_get_shared(
+                               struct device_node *node, const char *id)
+{
+       return __of_reset_control_get(node, id, 0, 1);
+}
+
+/**
+ * of_reset_control_get_exclusive_by_index - Lookup and obtain an exclusive
+ *                                           reference to a reset controller
+ *                                           by index.
  * @node: device to be reset by the controller
  * @index: index of the reset controller
  *
@@ -167,49 +199,60 @@ static inline struct reset_control *of_reset_control_get(
  * in whatever order. Returns a struct reset_control or IS_ERR() condition
  * containing errno.
  */
-static inline struct reset_control *of_reset_control_get_by_index(
+static inline struct reset_control *of_reset_control_get_exclusive_by_index(
                                        struct device_node *node, int index)
 {
        return __of_reset_control_get(node, NULL, index, 0);
 }
 
 /**
- * devm_reset_control_get - resource managed reset_control_get()
- * @dev: device to be reset by the controller
- * @id: reset line name
+ * of_reset_control_get_shared_by_index - Lookup and obtain an shared
+ *                                        reference to a reset controller
+ *                                        by index.
+ * @node: device to be reset by the controller
+ * @index: index of the reset controller
+ *
+ * When a reset-control is shared, the behavior of reset_control_assert /
+ * deassert is changed, the reset-core will keep track of a deassert_count
+ * and only (re-)assert the reset after reset_control_assert has been called
+ * as many times as reset_control_deassert was called. Also see the remark
+ * about shared reset-controls in the reset_control_assert docs.
+ *
+ * Calling reset_control_assert without first calling reset_control_deassert
+ * is not allowed on a shared reset control. Calling reset_control_reset is
+ * also not allowed on a shared reset control.
+ * Returns a struct reset_control or IS_ERR() condition containing errno.
  *
- * Managed reset_control_get(). For reset controllers returned from this
- * function, reset_control_put() is called automatically on driver detach.
- * See reset_control_get() for more information.
+ * This is to be used to perform a list of resets for a device or power domain
+ * in whatever order. Returns a struct reset_control or IS_ERR() condition
+ * containing errno.
  */
-static inline struct reset_control *__must_check devm_reset_control_get(
-                                       struct device *dev, const char *id)
-{
-#ifndef CONFIG_RESET_CONTROLLER
-       WARN_ON(1);
-#endif
-       return __devm_reset_control_get(dev, id, 0, 0);
-}
-
-static inline struct reset_control *devm_reset_control_get_optional(
-                                       struct device *dev, const char *id)
+static inline struct reset_control *of_reset_control_get_shared_by_index(
+                                       struct device_node *node, int index)
 {
-       return __devm_reset_control_get(dev, id, 0, 0);
+       return __of_reset_control_get(node, NULL, index, 1);
 }
 
 /**
- * devm_reset_control_get_by_index - resource managed reset_control_get
+ * devm_reset_control_get_exclusive - resource managed
+ *                                    reset_control_get_exclusive()
  * @dev: device to be reset by the controller
- * @index: index of the reset controller
+ * @id: reset line name
  *
- * Managed reset_control_get(). For reset controllers returned from this
- * function, reset_control_put() is called automatically on driver detach.
- * See reset_control_get() for more information.
+ * Managed reset_control_get_exclusive(). For reset controllers returned
+ * from this function, reset_control_put() is called automatically on driver
+ * detach.
+ *
+ * See reset_control_get_exclusive() for more information.
  */
-static inline struct reset_control *devm_reset_control_get_by_index(
-                                       struct device *dev, int index)
+static inline struct reset_control *
+__must_check devm_reset_control_get_exclusive(struct device *dev,
+                                             const char *id)
 {
-       return __devm_reset_control_get(dev, NULL, index, 0);
+#ifndef CONFIG_RESET_CONTROLLER
+       WARN_ON(1);
+#endif
+       return __devm_reset_control_get(dev, id, 0, 0);
 }
 
 /**
@@ -227,6 +270,36 @@ static inline struct reset_control *devm_reset_control_get_shared(
        return __devm_reset_control_get(dev, id, 0, 1);
 }
 
+static inline struct reset_control *devm_reset_control_get_optional_exclusive(
+                                       struct device *dev, const char *id)
+{
+       return __devm_reset_control_get(dev, id, 0, 0);
+}
+
+static inline struct reset_control *devm_reset_control_get_optional_shared(
+                                       struct device *dev, const char *id)
+{
+       return __devm_reset_control_get(dev, id, 0, 1);
+}
+
+/**
+ * devm_reset_control_get_exclusive_by_index - resource managed
+ *                                             reset_control_get_exclusive()
+ * @dev: device to be reset by the controller
+ * @index: index of the reset controller
+ *
+ * Managed reset_control_get_exclusive(). For reset controllers returned from
+ * this function, reset_control_put() is called automatically on driver
+ * detach.
+ *
+ * See reset_control_get_exclusive() for more information.
+ */
+static inline struct reset_control *
+devm_reset_control_get_exclusive_by_index(struct device *dev, int index)
+{
+       return __devm_reset_control_get(dev, NULL, index, 0);
+}
+
 /**
  * devm_reset_control_get_shared_by_index - resource managed
  * reset_control_get_shared
@@ -237,10 +310,60 @@ static inline struct reset_control *devm_reset_control_get_shared(
  * this function, reset_control_put() is called automatically on driver detach.
  * See reset_control_get_shared() for more information.
  */
-static inline struct reset_control *devm_reset_control_get_shared_by_index(
-                                       struct device *dev, int index)
+static inline struct reset_control *
+devm_reset_control_get_shared_by_index(struct device *dev, int index)
 {
        return __devm_reset_control_get(dev, NULL, index, 1);
 }
 
+/*
+ * TEMPORARY calls to use during transition:
+ *
+ *   of_reset_control_get() => of_reset_control_get_exclusive()
+ *
+ * These inline function calls will be removed once all consumers
+ * have been moved over to the new explicit API.
+ */
+static inline struct reset_control *reset_control_get(
+                               struct device *dev, const char *id)
+{
+       return reset_control_get_exclusive(dev, id);
+}
+
+static inline struct reset_control *reset_control_get_optional(
+                                       struct device *dev, const char *id)
+{
+       return reset_control_get_optional_exclusive(dev, id);
+}
+
+static inline struct reset_control *of_reset_control_get(
+                               struct device_node *node, const char *id)
+{
+       return of_reset_control_get_exclusive(node, id);
+}
+
+static inline struct reset_control *of_reset_control_get_by_index(
+                               struct device_node *node, int index)
+{
+       return of_reset_control_get_exclusive_by_index(node, index);
+}
+
+static inline struct reset_control *devm_reset_control_get(
+                               struct device *dev, const char *id)
+{
+       return devm_reset_control_get_exclusive(dev, id);
+}
+
+static inline struct reset_control *devm_reset_control_get_optional(
+                               struct device *dev, const char *id)
+{
+       return devm_reset_control_get_optional_exclusive(dev, id);
+
+}
+
+static inline struct reset_control *devm_reset_control_get_by_index(
+                               struct device *dev, int index)
+{
+       return devm_reset_control_get_exclusive_by_index(dev, index);
+}
 #endif
index 49eb4f8ebac9636a394dbe097532c70a08e056d7..2b0fad83683f793959fbe330045abb7c684edfcf 100644 (file)
@@ -158,7 +158,7 @@ struct anon_vma *page_get_anon_vma(struct page *page);
 /*
  * rmap interfaces called when adding or removing pte of page
  */
-void page_move_anon_rmap(struct page *, struct vm_area_struct *, unsigned long);
+void page_move_anon_rmap(struct page *, struct vm_area_struct *);
 void page_add_anon_rmap(struct page *, struct vm_area_struct *,
                unsigned long, bool);
 void do_page_add_anon_rmap(struct page *, struct vm_area_struct *,
index ee38a41274759f279be1c0752a7fab63fac517c8..f39b37180c414deb6d71c0ab5d674f89958630c0 100644 (file)
@@ -1062,6 +1062,7 @@ __skb_set_sw_hash(struct sk_buff *skb, __u32 hash, bool is_l4)
 }
 
 void __skb_get_hash(struct sk_buff *skb);
+u32 __skb_get_hash_symmetric(struct sk_buff *skb);
 u32 skb_get_poff(const struct sk_buff *skb);
 u32 __skb_get_poff(const struct sk_buff *skb, void *data,
                   const struct flow_keys *keys, int hlen);
@@ -2869,6 +2870,25 @@ static inline void skb_postpush_rcsum(struct sk_buff *skb,
                skb->csum = csum_partial(start, len, skb->csum);
 }
 
+/**
+ *     skb_push_rcsum - push skb and update receive checksum
+ *     @skb: buffer to update
+ *     @len: length of data pulled
+ *
+ *     This function performs an skb_push on the packet and updates
+ *     the CHECKSUM_COMPLETE checksum.  It should be used on
+ *     receive path processing instead of skb_push unless you know
+ *     that the checksum difference is zero (e.g., a valid IP header)
+ *     or you are setting ip_summed to CHECKSUM_NONE.
+ */
+static inline unsigned char *skb_push_rcsum(struct sk_buff *skb,
+                                           unsigned int len)
+{
+       skb_push(skb, len);
+       skb_postpush_rcsum(skb, skb->data, len);
+       return skb->data;
+}
+
 /**
  *     pskb_trim_rcsum - trim received skb and update checksum
  *     @skb: buffer to trim
index 4018b48f2b3b4f115802fba8b67fcc3cd113c773..a0596ca0e80ac77aeb0afa29648532ef51a5deae 100644 (file)
@@ -36,6 +36,9 @@ enum sknetlink_groups sock_diag_destroy_group(const struct sock *sk)
 {
        switch (sk->sk_family) {
        case AF_INET:
+               if (sk->sk_type == SOCK_RAW)
+                       return SKNLGRP_NONE;
+
                switch (sk->sk_protocol) {
                case IPPROTO_TCP:
                        return SKNLGRP_INET_TCP_DESTROY;
@@ -45,6 +48,9 @@ enum sknetlink_groups sock_diag_destroy_group(const struct sock *sk)
                        return SKNLGRP_NONE;
                }
        case AF_INET6:
+               if (sk->sk_type == SOCK_RAW)
+                       return SKNLGRP_NONE;
+
                switch (sk->sk_protocol) {
                case IPPROTO_TCP:
                        return SKNLGRP_INET6_TCP_DESTROY;
index 966889a20ea375a29fdba96ab2615babf073e8d5..e479033bd7829148de1a63928c6db99ed91295aa 100644 (file)
@@ -180,11 +180,11 @@ struct ehci_regs {
  * PORTSCx
  */
        /* HOSTPC: offset 0x84 */
-       u32             hostpc[1];      /* HOSTPC extension */
+       u32             hostpc[0];      /* HOSTPC extension */
 #define HOSTPC_PHCD    (1<<22)         /* Phy clock disable */
 #define HOSTPC_PSPD    (3<<25)         /* Port speed detection */
 
-       u32             reserved5[16];
+       u32             reserved5[17];
 
        /* USBMODE_EX: offset 0xc8 */
        u32             usbmode_ex;     /* USB Device mode extension */
index 791800ddd6d90de23d7b7acea7a91e500e646eda..6360c259da6d62cd3c4b99c858acd6db19eff401 100644 (file)
@@ -34,6 +34,9 @@
 
 #define BOND_DEFAULT_MIIMON    100
 
+#ifndef __long_aligned
+#define __long_aligned __attribute__((aligned((sizeof(long)))))
+#endif
 /*
  * Less bad way to call ioctl from within the kernel; this needs to be
  * done some other way to get the call out of interrupt context.
@@ -138,7 +141,9 @@ struct bond_params {
        struct reciprocal_value reciprocal_packets_per_slave;
        u16 ad_actor_sys_prio;
        u16 ad_user_port_key;
-       u8 ad_actor_system[ETH_ALEN];
+
+       /* 2 bytes of padding : see ether_addr_equal_64bits() */
+       u8 ad_actor_system[ETH_ALEN + 2];
 };
 
 struct bond_parm_tbl {
index 5dce30a6abe3e67091c5d14facd2750edd8426f0..7a54a31d1d4cf7988ff7bd3bf3015b4a8f23e359 100644 (file)
@@ -26,7 +26,7 @@ int gre_del_protocol(const struct gre_protocol *proto, u8 version);
 struct net_device *gretap_fb_dev_create(struct net *net, const char *name,
                                       u8 name_assign_type);
 int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
-                    bool *csum_err, __be16 proto);
+                    bool *csum_err, __be16 proto, int nhs);
 
 static inline int gre_calc_hlen(__be16 o_flags)
 {
index 37165fba3741ac68e5a93a8a22473eae70361e45..08f36cd2b874b5493bc9b51c4071722591dcc321 100644 (file)
@@ -313,10 +313,9 @@ static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst,
        return min(dst->dev->mtu, IP_MAX_MTU);
 }
 
-static inline unsigned int ip_skb_dst_mtu(const struct sk_buff *skb)
+static inline unsigned int ip_skb_dst_mtu(struct sock *sk,
+                                         const struct sk_buff *skb)
 {
-       struct sock *sk = skb->sk;
-
        if (!sk || !sk_fullsock(sk) || ip_sk_use_pmtu(sk)) {
                bool forwarding = IPCB(skb)->flags & IPSKB_FORWARDED;
 
index dd78bea227c8b0baf2fd14eb878f26f265ac76e0..b6083c34ef0d4ed0d03f9c030307f335d3a4cba0 100644 (file)
@@ -284,6 +284,14 @@ static inline bool nf_is_loopback_packet(const struct sk_buff *skb)
        return skb->dev && skb->skb_iif && skb->dev->flags & IFF_LOOPBACK;
 }
 
+/* jiffies until ct expires, 0 if already expired */
+static inline unsigned long nf_ct_expires(const struct nf_conn *ct)
+{
+       long timeout = (long)ct->timeout.expires - (long)jiffies;
+
+       return timeout > 0 ? timeout : 0;
+}
+
 struct kernel_param;
 
 int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp);
index 092235458691fd5eadc09cab3266d100f7140dc6..f7c291ff4074510333888184b3b481c77eefc546 100644 (file)
@@ -167,6 +167,7 @@ struct nft_set_elem {
 
 struct nft_set;
 struct nft_set_iter {
+       u8              genmask;
        unsigned int    count;
        unsigned int    skip;
        int             err;
index 649d2a8c17fc36f04b4d317c41c33edd6cdfc8b7..ff5be7e8ddeae6f9d2f9eac889d7abbfbd396bbd 100644 (file)
@@ -1576,7 +1576,13 @@ static inline void sock_put(struct sock *sk)
  */
 void sock_gen_put(struct sock *sk);
 
-int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested);
+int __sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested,
+                    unsigned int trim_cap);
+static inline int sk_receive_skb(struct sock *sk, struct sk_buff *skb,
+                                const int nested)
+{
+       return __sk_receive_skb(sk, skb, nested, 1);
+}
 
 static inline void sk_tx_queue_set(struct sock *sk, int tx_queue)
 {
index 985619a593230a6532017e4f61dfaa945b04a08f..1d8e158241da742eb845ac30bab4292cec33d479 100644 (file)
@@ -60,7 +60,7 @@ struct switchdev_attr {
                struct netdev_phys_item_id ppid;        /* PORT_PARENT_ID */
                u8 stp_state;                           /* PORT_STP_STATE */
                unsigned long brport_flags;             /* PORT_BRIDGE_FLAGS */
-               u32 ageing_time;                        /* BRIDGE_AGEING_TIME */
+               clock_t ageing_time;                    /* BRIDGE_AGEING_TIME */
                bool vlan_filtering;                    /* BRIDGE_VLAN_FILTERING */
        } u;
 };
index dc9a09aefb3368cec313d31af7cbc60d76c54fe8..c55facd17b7ec0b4c38ecf7e3690118056a93567 100644 (file)
@@ -36,7 +36,7 @@ struct tcf_meta_ops {
        int     (*encode)(struct sk_buff *, void *, struct tcf_meta_info *);
        int     (*decode)(struct sk_buff *, void *, u16 len);
        int     (*get)(struct sk_buff *skb, struct tcf_meta_info *mi);
-       int     (*alloc)(struct tcf_meta_info *, void *);
+       int     (*alloc)(struct tcf_meta_info *, void *, gfp_t);
        void    (*release)(struct tcf_meta_info *);
        int     (*validate)(void *val, int len);
        struct module   *owner;
@@ -48,8 +48,8 @@ int ife_get_meta_u32(struct sk_buff *skb, struct tcf_meta_info *mi);
 int ife_get_meta_u16(struct sk_buff *skb, struct tcf_meta_info *mi);
 int ife_tlv_meta_encode(void *skbdata, u16 attrtype, u16 dlen,
                        const void *dval);
-int ife_alloc_meta_u32(struct tcf_meta_info *mi, void *metaval);
-int ife_alloc_meta_u16(struct tcf_meta_info *mi, void *metaval);
+int ife_alloc_meta_u32(struct tcf_meta_info *mi, void *metaval, gfp_t gfp);
+int ife_alloc_meta_u16(struct tcf_meta_info *mi, void *metaval, gfp_t gfp);
 int ife_check_meta_u32(u32 metaval, struct tcf_meta_info *mi);
 int ife_encode_meta_u32(u32 metaval, void *skbdata, struct tcf_meta_info *mi);
 int ife_validate_meta_u32(void *val, int len);
index c0abcdc1147083f9faa8a036357a5468429ff00a..cee8c00f3d3e6f69c4c899f2f24bbbf6fbb30c13 100644 (file)
@@ -68,6 +68,7 @@ struct snd_compr_runtime {
  * @ops: pointer to DSP callbacks
  * @runtime: pointer to runtime structure
  * @device: device pointer
+ * @error_work: delayed work used when closing the stream due to an error
  * @direction: stream direction, playback/recording
  * @metadata_set: metadata set flag, true when set
  * @next_track: has userspace signal next track transition, true when set
@@ -78,6 +79,7 @@ struct snd_compr_stream {
        struct snd_compr_ops *ops;
        struct snd_compr_runtime *runtime;
        struct snd_compr *device;
+       struct delayed_work error_work;
        enum snd_compr_direction direction;
        bool metadata_set;
        bool next_track;
@@ -187,4 +189,7 @@ static inline void snd_compr_drain_notify(struct snd_compr_stream *stream)
        wake_up(&stream->runtime->sleep);
 }
 
+int snd_compr_stop_error(struct snd_compr_stream *stream,
+                        snd_pcm_state_t state);
+
 #endif
diff --git a/include/sound/cs35l33.h b/include/sound/cs35l33.h
new file mode 100644 (file)
index 0000000..b6eadce
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * linux/sound/cs35l33.h -- Platform data for CS35l33
+ *
+ * Copyright (c) 2016 Cirrus Logic Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __CS35L33_H
+#define __CS35L33_H
+
+struct cs35l33_hg {
+       bool enable_hg_algo;
+       unsigned int mem_depth;
+       unsigned int release_rate;
+       unsigned int hd_rm;
+       unsigned int ldo_thld;
+       unsigned int ldo_path_disable;
+       unsigned int ldo_entry_delay;
+       bool vp_hg_auto;
+       unsigned int vp_hg;
+       unsigned int vp_hg_rate;
+       unsigned int vp_hg_va;
+};
+
+struct cs35l33_pdata {
+       /* Boost Controller Voltage Setting */
+       unsigned int boost_ctl;
+
+       /* Boost Controller Peak Current */
+       unsigned int boost_ipk;
+
+       /* Amplifier Drive Select */
+       unsigned int amp_drv_sel;
+
+       /* soft volume ramp */
+       unsigned int ramp_rate;
+
+       /* IMON adc scale */
+       unsigned int imon_adc_scale;
+
+       /* H/G algo configuration */
+       struct cs35l33_hg hg_config;
+};
+
+#endif /* __CS35L33_H */
index fc3a481ad91ecdb5f25fd5c3c7d9b210d4f74f5e..530c57bdefa06567c3bcaba9249845af1d4e535a 100644 (file)
@@ -53,18 +53,19 @@ struct hdmi_codec_params {
        int channels;
 };
 
+struct hdmi_codec_pdata;
 struct hdmi_codec_ops {
        /*
         * Called when ASoC starts an audio stream setup.
         * Optional
         */
-       int (*audio_startup)(struct device *dev);
+       int (*audio_startup)(struct device *dev, void *data);
 
        /*
         * Configures HDMI-encoder for audio stream.
         * Mandatory
         */
-       int (*hw_params)(struct device *dev,
+       int (*hw_params)(struct device *dev, void *data,
                         struct hdmi_codec_daifmt *fmt,
                         struct hdmi_codec_params *hparms);
 
@@ -72,19 +73,20 @@ struct hdmi_codec_ops {
         * Shuts down the audio stream.
         * Mandatory
         */
-       void (*audio_shutdown)(struct device *dev);
+       void (*audio_shutdown)(struct device *dev, void *data);
 
        /*
         * Mute/unmute HDMI audio stream.
         * Optional
         */
-       int (*digital_mute)(struct device *dev, bool enable);
+       int (*digital_mute)(struct device *dev, void *data, bool enable);
 
        /*
         * Provides EDID-Like-Data from connected HDMI device.
         * Optional
         */
-       int (*get_eld)(struct device *dev, uint8_t *buf, size_t len);
+       int (*get_eld)(struct device *dev, void *data,
+                      uint8_t *buf, size_t len);
 };
 
 /* HDMI codec initalization data */
@@ -93,6 +95,7 @@ struct hdmi_codec_pdata {
        uint i2s:1;
        uint spdif:1;
        int max_i2s_channels;
+       void *data;
 };
 
 #define HDMI_CODEC_DRV_NAME "hdmi-audio-codec"
index 0399352f3a6243569c87d3691fd664480749802c..a6a2e1547092a8e232c27bf5a8778b4eaa63a1bb 100644 (file)
 #define __SIMPLE_CARD_H
 
 #include <sound/soc.h>
-
-struct asoc_simple_dai {
-       const char *name;
-       unsigned int sysclk;
-       int slots;
-       int slot_width;
-       unsigned int tx_slot_mask;
-       unsigned int rx_slot_mask;
-       struct clk *clk;
-};
+#include <sound/simple_card_utils.h>
 
 struct asoc_simple_card_info {
        const char *name;
diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h
new file mode 100644 (file)
index 0000000..86088ae
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * simple_card_core.h
+ *
+ * Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __SIMPLE_CARD_CORE_H
+#define __SIMPLE_CARD_CORE_H
+
+#include <sound/soc.h>
+
+struct asoc_simple_dai {
+       const char *name;
+       unsigned int sysclk;
+       int slots;
+       int slot_width;
+       unsigned int tx_slot_mask;
+       unsigned int rx_slot_mask;
+       struct clk *clk;
+};
+
+int asoc_simple_card_parse_daifmt(struct device *dev,
+                                 struct device_node *node,
+                                 struct device_node *codec,
+                                 char *prefix,
+                                 unsigned int *retfmt);
+int asoc_simple_card_set_dailink_name(struct device *dev,
+                                     struct snd_soc_dai_link *dai_link,
+                                     const char *fmt, ...);
+int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
+                                    char *prefix);
+
+#endif /* __SIMPLE_CARD_CORE_H */
index 3101d53468aad5e1e86f7d65a90fdc37ffbafd8f..f60d755f7ac6c947ff5d6c817ae8ebce430f1224 100644 (file)
@@ -358,6 +358,7 @@ struct snd_soc_dapm_context;
 struct regulator;
 struct snd_soc_dapm_widget_list;
 struct snd_soc_dapm_update;
+enum snd_soc_dapm_direction;
 
 int dapm_regulator_event(struct snd_soc_dapm_widget *w,
                         struct snd_kcontrol *kcontrol, int event);
@@ -382,6 +383,9 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol,
 int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
        const struct snd_soc_dapm_widget *widget,
        int num);
+struct snd_soc_dapm_widget *snd_soc_dapm_new_control(
+               struct snd_soc_dapm_context *dapm,
+               const struct snd_soc_dapm_widget *widget);
 int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
                                 struct snd_soc_dai *dai);
 int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card);
@@ -451,7 +455,9 @@ 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,
-       struct snd_soc_dapm_widget_list **list);
+       struct snd_soc_dapm_widget_list **list,
+       bool (*custom_stop_condition)(struct snd_soc_dapm_widget *,
+                                     enum snd_soc_dapm_direction));
 
 struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm(
        struct snd_kcontrol *kcontrol);
index fd7b58a58d6f9c26b484210a237df709e3fb2f4d..6144882cc96a59f3b6251ace1fefff0584e8f084 100644 (file)
        .get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \
        .private_value = SOC_DOUBLE_R_S_VALUE(reg_left, reg_right, xshift, \
                                            xmin, xmax, xsign_bit, xinvert) }
+#define SOC_SINGLE_S8_TLV(xname, xreg, xmin, xmax, tlv_array) \
+{      .iface  = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+                 SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+       .tlv.p  = (tlv_array), \
+       .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
+       .put = snd_soc_put_volsw, \
+       .private_value = (unsigned long)&(struct soc_mixer_control) \
+       {.reg = xreg, .rreg = xreg,  \
+        .min = xmin, .max = xmax, .platform_max = xmax, \
+       .sign_bit = 7,} }
 #define SOC_DOUBLE_S8_TLV(xname, xreg, xmin, xmax, tlv_array) \
 {      .iface  = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
        .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
index 8bdae34d1f9add25d968182c6540c431d994c7ce..ec10cfef166afb3a7cdc777ade3903e076d30376 100644 (file)
@@ -245,6 +245,7 @@ endif
 header-y += hw_breakpoint.h
 header-y += l2tp.h
 header-y += libc-compat.h
+header-y += lirc.h
 header-y += limits.h
 header-y += llc.h
 header-y += loop.h
index 5974fae54e12c534826d323b35917956275fd5a6..27e17363263abcc0ee9a6459ecd247de07b71a86 100644 (file)
  *
  *  7.24
  *  - add FUSE_LSEEK for SEEK_HOLE and SEEK_DATA support
+ *
+ *  7.25
+ *  - add FUSE_PARALLEL_DIROPS
  */
 
 #ifndef _LINUX_FUSE_H
 #define FUSE_KERNEL_VERSION 7
 
 /** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 24
+#define FUSE_KERNEL_MINOR_VERSION 25
 
 /** The node ID of the root inode */
 #define FUSE_ROOT_ID 1
@@ -234,6 +237,7 @@ struct fuse_file_lock {
  * FUSE_ASYNC_DIO: asynchronous direct I/O submission
  * FUSE_WRITEBACK_CACHE: use writeback cache for buffered writes
  * FUSE_NO_OPEN_SUPPORT: kernel supports zero-message opens
+ * FUSE_PARALLEL_DIROPS: allow parallel lookups and readdir
  */
 #define FUSE_ASYNC_READ                (1 << 0)
 #define FUSE_POSIX_LOCKS       (1 << 1)
@@ -253,6 +257,7 @@ struct fuse_file_lock {
 #define FUSE_ASYNC_DIO         (1 << 15)
 #define FUSE_WRITEBACK_CACHE   (1 << 16)
 #define FUSE_NO_OPEN_SUPPORT   (1 << 17)
+#define FUSE_PARALLEL_DIROPS    (1 << 18)
 
 /**
  * CUSE INIT request/reply flags
index 87cf351bab03998d51817f7085d2d1f7abda0b43..d6d071fc3c568249bf70f24602f95770eb1643df 100644 (file)
 #define KEY_KBDINPUTASSIST_ACCEPT              0x264
 #define KEY_KBDINPUTASSIST_CANCEL              0x265
 
+/* Diagonal movement keys */
+#define KEY_RIGHT_UP                   0x266
+#define KEY_RIGHT_DOWN                 0x267
+#define KEY_LEFT_UP                    0x268
+#define KEY_LEFT_DOWN                  0x269
+
+#define KEY_ROOT_MENU                  0x26a /* Show Device's Root Menu */
+/* Show Top Menu of the Media (e.g. DVD) */
+#define KEY_MEDIA_TOP_MENU             0x26b
+#define KEY_NUMERIC_11                 0x26c
+#define KEY_NUMERIC_12                 0x26d
+/*
+ * Toggle Audio Description: refers to an audio service that helps blind and
+ * visually impaired consumers understand the action in a program. Note: in
+ * some countries this is referred to as "Video Description".
+ */
+#define KEY_AUDIO_DESC                 0x26e
+#define KEY_3D_MODE                    0x26f
+#define KEY_NEXT_FAVORITE              0x270
+#define KEY_STOP_RECORD                        0x271
+#define KEY_PAUSE_RECORD               0x272
+#define KEY_VOD                                0x273 /* Video on Demand */
+#define KEY_UNMUTE                     0x274
+#define KEY_FASTREVERSE                        0x275
+#define KEY_SLOWREVERSE                        0x276
+/*
+ * Control a data application associated with the currently viewed channel,
+ * e.g. teletext or data broadcast application (MHEG, MHP, HbbTV, etc.)
+ */
+#define KEY_DATA                       0x275
+
 #define BTN_TRIGGER_HAPPY              0x2c0
 #define BTN_TRIGGER_HAPPY1             0x2c0
 #define BTN_TRIGGER_HAPPY2             0x2c1
 #define SW_ROTATE_LOCK         0x0c  /* set = rotate locked/disabled */
 #define SW_LINEIN_INSERT       0x0d  /* set = inserted */
 #define SW_MUTE_DEVICE         0x0e  /* set = device disabled */
+#define SW_PEN_INSERTED                0x0f  /* set = pen inserted */
 #define SW_MAX                 0x0f
 #define SW_CNT                 (SW_MAX+1)
 
index 01113841190d3f9ee6c654b2f728d95b44a62bb3..c514941198173c0a73c19d5441867d5d7fa4fe76 100644 (file)
@@ -247,6 +247,7 @@ struct input_mask {
 #define BUS_ATARI              0x1B
 #define BUS_SPI                        0x1C
 #define BUS_RMI                        0x1D
+#define BUS_CEC                        0x1E
 
 /*
  * MT_TOOL types
index 1d973d2ba417247f4265d8dee6bf6566ae1f9047..cd26d7a0fd07094499f97568dc8ca4f50ae93c0f 100644 (file)
@@ -33,6 +33,7 @@ header-y += xt_NFLOG.h
 header-y += xt_NFQUEUE.h
 header-y += xt_RATEEST.h
 header-y += xt_SECMARK.h
+header-y += xt_SYNPROXY.h
 header-y += xt_TCPMSS.h
 header-y += xt_TCPOPTSTRIP.h
 header-y += xt_TEE.h
index 2d59fbaa93c621af56c8bc01e98445c4e54ae745..ca67e61d2a619e6a017e0d254844cbc49ab96629 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef _XT_SYNPROXY_H
 #define _XT_SYNPROXY_H
 
+#include <linux/types.h>
+
 #define XT_SYNPROXY_OPT_MSS            0x01
 #define XT_SYNPROXY_OPT_WSCALE         0x02
 #define XT_SYNPROXY_OPT_SACK_PERM      0x04
index f755a602d4a176e006dc2cb5830d1c4812bac81f..c02d89777713b8772e47c2759570f4a838cb18e7 100644 (file)
@@ -1458,6 +1458,7 @@ config KALLSYMS_ALL
 
 config KALLSYMS_ABSOLUTE_PERCPU
        bool
+       depends on KALLSYMS
        default X86_64 && SMP
 
 config KALLSYMS_BASE_RELATIVE
index 22bb4f24f071df56dbddcb3f84fbf3ad2e8c63bc..8d528f9930dad639c22c797da715545db717ce6e 100644 (file)
@@ -1883,6 +1883,23 @@ void audit_log_d_path_exe(struct audit_buffer *ab,
        audit_log_format(ab, " exe=(null)");
 }
 
+struct tty_struct *audit_get_tty(struct task_struct *tsk)
+{
+       struct tty_struct *tty = NULL;
+       unsigned long flags;
+
+       spin_lock_irqsave(&tsk->sighand->siglock, flags);
+       if (tsk->signal)
+               tty = tty_kref_get(tsk->signal->tty);
+       spin_unlock_irqrestore(&tsk->sighand->siglock, flags);
+       return tty;
+}
+
+void audit_put_tty(struct tty_struct *tty)
+{
+       tty_kref_put(tty);
+}
+
 void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk)
 {
        const struct cred *cred;
index cbbe6bb6496ea934f86ffcfedffe971c76bbb692..a492f4c4e7106aa7c4c23e7bcb8e502477df6638 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/audit.h>
 #include <linux/skbuff.h>
 #include <uapi/linux/mqueue.h>
+#include <linux/tty.h>
 
 /* AUDIT_NAMES is the number of slots we reserve in the audit_context
  * for saving names from getname().  If we get more names we will allocate
@@ -262,6 +263,9 @@ extern struct audit_entry *audit_dupe_rule(struct audit_krule *old);
 extern void audit_log_d_path_exe(struct audit_buffer *ab,
                                 struct mm_struct *mm);
 
+extern struct tty_struct *audit_get_tty(struct task_struct *tsk);
+extern void audit_put_tty(struct tty_struct *tty);
+
 /* audit watch functions */
 #ifdef CONFIG_AUDIT_WATCH
 extern void audit_put_watch(struct audit_watch *watch);
index 62ab53d7619cfb249154276e40a38d1e69a906f6..2672d105cffcdaf7703d9f655e7338dd0b102cc0 100644 (file)
@@ -63,7 +63,6 @@
 #include <asm/unistd.h>
 #include <linux/security.h>
 #include <linux/list.h>
-#include <linux/tty.h>
 #include <linux/binfmts.h>
 #include <linux/highmem.h>
 #include <linux/syscalls.h>
@@ -1985,14 +1984,15 @@ static void audit_log_set_loginuid(kuid_t koldloginuid, kuid_t kloginuid,
        if (!audit_enabled)
                return;
 
+       ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN);
+       if (!ab)
+               return;
+
        uid = from_kuid(&init_user_ns, task_uid(current));
        oldloginuid = from_kuid(&init_user_ns, koldloginuid);
        loginuid = from_kuid(&init_user_ns, kloginuid),
        tty = audit_get_tty(current);
 
-       ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN);
-       if (!ab)
-               return;
        audit_log_format(ab, "pid=%d uid=%u", task_pid_nr(current), uid);
        audit_log_task_context(ab);
        audit_log_format(ab, " old-auid=%u auid=%u tty=%s old-ses=%u ses=%u res=%d",
index 668e07903c8f1a3950c4e494eb7443748710f08c..eec9f90ba030410a5104991cfcd377400cb4bb7d 100644 (file)
  * are set to NOT_INIT to indicate that they are no longer readable.
  */
 
-/* types of values stored in eBPF registers */
-enum bpf_reg_type {
-       NOT_INIT = 0,            /* nothing was written into register */
-       UNKNOWN_VALUE,           /* reg doesn't contain a valid pointer */
-       PTR_TO_CTX,              /* reg points to bpf_context */
-       CONST_PTR_TO_MAP,        /* reg points to struct bpf_map */
-       PTR_TO_MAP_VALUE,        /* reg points to map element value */
-       PTR_TO_MAP_VALUE_OR_NULL,/* points to map elem value or NULL */
-       FRAME_PTR,               /* reg == frame_pointer */
-       PTR_TO_STACK,            /* reg == frame_pointer + imm */
-       CONST_IMM,               /* constant integer value */
-
-       /* PTR_TO_PACKET represents:
-        * skb->data
-        * skb->data + imm
-        * skb->data + (u16) var
-        * skb->data + (u16) var + imm
-        * if (range > 0) then [ptr, ptr + range - off) is safe to access
-        * if (id > 0) means that some 'var' was added
-        * if (off > 0) menas that 'imm' was added
-        */
-       PTR_TO_PACKET,
-       PTR_TO_PACKET_END,       /* skb->data + headlen */
-};
-
 struct reg_state {
        enum bpf_reg_type type;
        union {
@@ -695,10 +670,10 @@ static int check_packet_access(struct verifier_env *env, u32 regno, int off,
 
 /* check access to 'struct bpf_context' fields */
 static int check_ctx_access(struct verifier_env *env, int off, int size,
-                           enum bpf_access_type t)
+                           enum bpf_access_type t, enum bpf_reg_type *reg_type)
 {
        if (env->prog->aux->ops->is_valid_access &&
-           env->prog->aux->ops->is_valid_access(off, size, t)) {
+           env->prog->aux->ops->is_valid_access(off, size, t, reg_type)) {
                /* remember the offset of last byte accessed in ctx */
                if (env->prog->aux->max_ctx_offset < off + size)
                        env->prog->aux->max_ctx_offset = off + size;
@@ -798,21 +773,19 @@ static int check_mem_access(struct verifier_env *env, u32 regno, int off,
                        mark_reg_unknown_value(state->regs, value_regno);
 
        } else if (reg->type == PTR_TO_CTX) {
+               enum bpf_reg_type reg_type = UNKNOWN_VALUE;
+
                if (t == BPF_WRITE && value_regno >= 0 &&
                    is_pointer_value(env, value_regno)) {
                        verbose("R%d leaks addr into ctx\n", value_regno);
                        return -EACCES;
                }
-               err = check_ctx_access(env, off, size, t);
+               err = check_ctx_access(env, off, size, t, &reg_type);
                if (!err && t == BPF_READ && value_regno >= 0) {
                        mark_reg_unknown_value(state->regs, value_regno);
-                       if (off == offsetof(struct __sk_buff, data) &&
-                           env->allow_ptr_leaks)
+                       if (env->allow_ptr_leaks)
                                /* note that reg.[id|off|range] == 0 */
-                               state->regs[value_regno].type = PTR_TO_PACKET;
-                       else if (off == offsetof(struct __sk_buff, data_end) &&
-                                env->allow_ptr_leaks)
-                               state->regs[value_regno].type = PTR_TO_PACKET_END;
+                               state->regs[value_regno].type = reg_type;
                }
 
        } else if (reg->type == FRAME_PTR || reg->type == PTR_TO_STACK) {
index 86cb5c6e89320f28e17691c6d69e58c9dfde81fb..75c0ff00aca60d298062755539e83cbfeaffaaf2 100644 (file)
@@ -837,6 +837,8 @@ static void put_css_set_locked(struct css_set *cset)
 
 static void put_css_set(struct css_set *cset)
 {
+       unsigned long flags;
+
        /*
         * Ensure that the refcount doesn't hit zero while any readers
         * can see it. Similar to atomic_dec_and_lock(), but for an
@@ -845,9 +847,9 @@ static void put_css_set(struct css_set *cset)
        if (atomic_add_unless(&cset->refcount, -1, 1))
                return;
 
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irqsave(&css_set_lock, flags);
        put_css_set_locked(cset);
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irqrestore(&css_set_lock, flags);
 }
 
 /*
@@ -1070,11 +1072,11 @@ static struct css_set *find_css_set(struct css_set *old_cset,
 
        /* First see if we already have a cgroup group that matches
         * the desired set */
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
        cset = find_existing_css_set(old_cset, cgrp, template);
        if (cset)
                get_css_set(cset);
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
 
        if (cset)
                return cset;
@@ -1102,7 +1104,7 @@ static struct css_set *find_css_set(struct css_set *old_cset,
         * find_existing_css_set() */
        memcpy(cset->subsys, template, sizeof(cset->subsys));
 
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
        /* Add reference counts and links from the new css_set. */
        list_for_each_entry(link, &old_cset->cgrp_links, cgrp_link) {
                struct cgroup *c = link->cgrp;
@@ -1128,7 +1130,7 @@ static struct css_set *find_css_set(struct css_set *old_cset,
                css_get(css);
        }
 
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
 
        return cset;
 }
@@ -1192,7 +1194,7 @@ static void cgroup_destroy_root(struct cgroup_root *root)
         * Release all the links from cset_links to this hierarchy's
         * root cgroup
         */
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
 
        list_for_each_entry_safe(link, tmp_link, &cgrp->cset_links, cset_link) {
                list_del(&link->cset_link);
@@ -1200,7 +1202,7 @@ static void cgroup_destroy_root(struct cgroup_root *root)
                kfree(link);
        }
 
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
 
        if (!list_empty(&root->root_list)) {
                list_del(&root->root_list);
@@ -1600,11 +1602,11 @@ static int rebind_subsystems(struct cgroup_root *dst_root, u16 ss_mask)
                ss->root = dst_root;
                css->cgroup = dcgrp;
 
-               spin_lock_bh(&css_set_lock);
+               spin_lock_irq(&css_set_lock);
                hash_for_each(css_set_table, i, cset, hlist)
                        list_move_tail(&cset->e_cset_node[ss->id],
                                       &dcgrp->e_csets[ss->id]);
-               spin_unlock_bh(&css_set_lock);
+               spin_unlock_irq(&css_set_lock);
 
                /* default hierarchy doesn't enable controllers by default */
                dst_root->subsys_mask |= 1 << ssid;
@@ -1640,10 +1642,10 @@ static int cgroup_show_path(struct seq_file *sf, struct kernfs_node *kf_node,
        if (!buf)
                return -ENOMEM;
 
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
        ns_cgroup = current_cgns_cgroup_from_root(kf_cgroot);
        len = kernfs_path_from_node(kf_node, ns_cgroup->kn, buf, PATH_MAX);
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
 
        if (len >= PATH_MAX)
                len = -ERANGE;
@@ -1897,7 +1899,7 @@ static void cgroup_enable_task_cg_lists(void)
 {
        struct task_struct *p, *g;
 
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
 
        if (use_task_css_set_links)
                goto out_unlock;
@@ -1922,8 +1924,12 @@ static void cgroup_enable_task_cg_lists(void)
                 * entry won't be deleted though the process has exited.
                 * Do it while holding siglock so that we don't end up
                 * racing against cgroup_exit().
+                *
+                * Interrupts were already disabled while acquiring
+                * the css_set_lock, so we do not need to disable it
+                * again when acquiring the sighand->siglock here.
                 */
-               spin_lock_irq(&p->sighand->siglock);
+               spin_lock(&p->sighand->siglock);
                if (!(p->flags & PF_EXITING)) {
                        struct css_set *cset = task_css_set(p);
 
@@ -1932,11 +1938,11 @@ static void cgroup_enable_task_cg_lists(void)
                        list_add_tail(&p->cg_list, &cset->tasks);
                        get_css_set(cset);
                }
-               spin_unlock_irq(&p->sighand->siglock);
+               spin_unlock(&p->sighand->siglock);
        } while_each_thread(g, p);
        read_unlock(&tasklist_lock);
 out_unlock:
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
 }
 
 static void init_cgroup_housekeeping(struct cgroup *cgrp)
@@ -2043,13 +2049,13 @@ static int cgroup_setup_root(struct cgroup_root *root, u16 ss_mask)
         * Link the root cgroup in this hierarchy into all the css_set
         * objects.
         */
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
        hash_for_each(css_set_table, i, cset, hlist) {
                link_css_set(&tmp_links, cset, root_cgrp);
                if (css_set_populated(cset))
                        cgroup_update_populated(root_cgrp, true);
        }
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
 
        BUG_ON(!list_empty(&root_cgrp->self.children));
        BUG_ON(atomic_read(&root->nr_cgrps) != 1);
@@ -2256,11 +2262,11 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type,
                struct cgroup *cgrp;
 
                mutex_lock(&cgroup_mutex);
-               spin_lock_bh(&css_set_lock);
+               spin_lock_irq(&css_set_lock);
 
                cgrp = cset_cgroup_from_root(ns->root_cset, root);
 
-               spin_unlock_bh(&css_set_lock);
+               spin_unlock_irq(&css_set_lock);
                mutex_unlock(&cgroup_mutex);
 
                nsdentry = kernfs_node_dentry(cgrp->kn, dentry->d_sb);
@@ -2337,11 +2343,11 @@ char *cgroup_path_ns(struct cgroup *cgrp, char *buf, size_t buflen,
        char *ret;
 
        mutex_lock(&cgroup_mutex);
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
 
        ret = cgroup_path_ns_locked(cgrp, buf, buflen, ns);
 
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
        mutex_unlock(&cgroup_mutex);
 
        return ret;
@@ -2369,7 +2375,7 @@ char *task_cgroup_path(struct task_struct *task, char *buf, size_t buflen)
        char *path = NULL;
 
        mutex_lock(&cgroup_mutex);
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
 
        root = idr_get_next(&cgroup_hierarchy_idr, &hierarchy_id);
 
@@ -2382,7 +2388,7 @@ char *task_cgroup_path(struct task_struct *task, char *buf, size_t buflen)
                        path = buf;
        }
 
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
        mutex_unlock(&cgroup_mutex);
        return path;
 }
@@ -2557,7 +2563,7 @@ static int cgroup_taskset_migrate(struct cgroup_taskset *tset,
         * the new cgroup.  There are no failure cases after here, so this
         * is the commit point.
         */
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
        list_for_each_entry(cset, &tset->src_csets, mg_node) {
                list_for_each_entry_safe(task, tmp_task, &cset->mg_tasks, cg_list) {
                        struct css_set *from_cset = task_css_set(task);
@@ -2568,7 +2574,7 @@ static int cgroup_taskset_migrate(struct cgroup_taskset *tset,
                        put_css_set_locked(from_cset);
                }
        }
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
 
        /*
         * Migration is committed, all target tasks are now on dst_csets.
@@ -2597,13 +2603,13 @@ static int cgroup_taskset_migrate(struct cgroup_taskset *tset,
                }
        } while_each_subsys_mask();
 out_release_tset:
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
        list_splice_init(&tset->dst_csets, &tset->src_csets);
        list_for_each_entry_safe(cset, tmp_cset, &tset->src_csets, mg_node) {
                list_splice_tail_init(&cset->mg_tasks, &cset->tasks);
                list_del_init(&cset->mg_node);
        }
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
        return ret;
 }
 
@@ -2634,7 +2640,7 @@ static void cgroup_migrate_finish(struct list_head *preloaded_csets)
 
        lockdep_assert_held(&cgroup_mutex);
 
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
        list_for_each_entry_safe(cset, tmp_cset, preloaded_csets, mg_preload_node) {
                cset->mg_src_cgrp = NULL;
                cset->mg_dst_cgrp = NULL;
@@ -2642,7 +2648,7 @@ static void cgroup_migrate_finish(struct list_head *preloaded_csets)
                list_del_init(&cset->mg_preload_node);
                put_css_set_locked(cset);
        }
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
 }
 
 /**
@@ -2783,7 +2789,7 @@ static int cgroup_migrate(struct task_struct *leader, bool threadgroup,
         * already PF_EXITING could be freed from underneath us unless we
         * take an rcu_read_lock.
         */
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
        rcu_read_lock();
        task = leader;
        do {
@@ -2792,7 +2798,7 @@ static int cgroup_migrate(struct task_struct *leader, bool threadgroup,
                        break;
        } while_each_thread(leader, task);
        rcu_read_unlock();
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
 
        return cgroup_taskset_migrate(&tset, root);
 }
@@ -2816,7 +2822,7 @@ static int cgroup_attach_task(struct cgroup *dst_cgrp,
                return -EBUSY;
 
        /* look up all src csets */
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
        rcu_read_lock();
        task = leader;
        do {
@@ -2826,7 +2832,7 @@ static int cgroup_attach_task(struct cgroup *dst_cgrp,
                        break;
        } while_each_thread(leader, task);
        rcu_read_unlock();
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
 
        /* prepare dst csets and commit */
        ret = cgroup_migrate_prepare_dst(&preloaded_csets);
@@ -2859,9 +2865,9 @@ static int cgroup_procs_write_permission(struct task_struct *task,
                struct cgroup *cgrp;
                struct inode *inode;
 
-               spin_lock_bh(&css_set_lock);
+               spin_lock_irq(&css_set_lock);
                cgrp = task_cgroup_from_root(task, &cgrp_dfl_root);
-               spin_unlock_bh(&css_set_lock);
+               spin_unlock_irq(&css_set_lock);
 
                while (!cgroup_is_descendant(dst_cgrp, cgrp))
                        cgrp = cgroup_parent(cgrp);
@@ -2962,9 +2968,9 @@ int cgroup_attach_task_all(struct task_struct *from, struct task_struct *tsk)
                if (root == &cgrp_dfl_root)
                        continue;
 
-               spin_lock_bh(&css_set_lock);
+               spin_lock_irq(&css_set_lock);
                from_cgrp = task_cgroup_from_root(from, root);
-               spin_unlock_bh(&css_set_lock);
+               spin_unlock_irq(&css_set_lock);
 
                retval = cgroup_attach_task(from_cgrp, tsk, false);
                if (retval)
@@ -3080,7 +3086,7 @@ static int cgroup_update_dfl_csses(struct cgroup *cgrp)
        percpu_down_write(&cgroup_threadgroup_rwsem);
 
        /* look up all csses currently attached to @cgrp's subtree */
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
        cgroup_for_each_live_descendant_pre(dsct, d_css, cgrp) {
                struct cgrp_cset_link *link;
 
@@ -3088,14 +3094,14 @@ static int cgroup_update_dfl_csses(struct cgroup *cgrp)
                        cgroup_migrate_add_src(link->cset, dsct,
                                               &preloaded_csets);
        }
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
 
        /* NULL dst indicates self on default hierarchy */
        ret = cgroup_migrate_prepare_dst(&preloaded_csets);
        if (ret)
                goto out_finish;
 
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
        list_for_each_entry(src_cset, &preloaded_csets, mg_preload_node) {
                struct task_struct *task, *ntask;
 
@@ -3107,7 +3113,7 @@ static int cgroup_update_dfl_csses(struct cgroup *cgrp)
                list_for_each_entry_safe(task, ntask, &src_cset->tasks, cg_list)
                        cgroup_taskset_add(task, &tset);
        }
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
 
        ret = cgroup_taskset_migrate(&tset, cgrp->root);
 out_finish:
@@ -3908,10 +3914,10 @@ static int cgroup_task_count(const struct cgroup *cgrp)
        int count = 0;
        struct cgrp_cset_link *link;
 
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
        list_for_each_entry(link, &cgrp->cset_links, cset_link)
                count += atomic_read(&link->cset->refcount);
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
        return count;
 }
 
@@ -4249,7 +4255,7 @@ void css_task_iter_start(struct cgroup_subsys_state *css,
 
        memset(it, 0, sizeof(*it));
 
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
 
        it->ss = css->ss;
 
@@ -4262,7 +4268,7 @@ void css_task_iter_start(struct cgroup_subsys_state *css,
 
        css_task_iter_advance_css_set(it);
 
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
 }
 
 /**
@@ -4280,7 +4286,7 @@ struct task_struct *css_task_iter_next(struct css_task_iter *it)
                it->cur_task = NULL;
        }
 
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
 
        if (it->task_pos) {
                it->cur_task = list_entry(it->task_pos, struct task_struct,
@@ -4289,7 +4295,7 @@ struct task_struct *css_task_iter_next(struct css_task_iter *it)
                css_task_iter_advance(it);
        }
 
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
 
        return it->cur_task;
 }
@@ -4303,10 +4309,10 @@ struct task_struct *css_task_iter_next(struct css_task_iter *it)
 void css_task_iter_end(struct css_task_iter *it)
 {
        if (it->cur_cset) {
-               spin_lock_bh(&css_set_lock);
+               spin_lock_irq(&css_set_lock);
                list_del(&it->iters_node);
                put_css_set_locked(it->cur_cset);
-               spin_unlock_bh(&css_set_lock);
+               spin_unlock_irq(&css_set_lock);
        }
 
        if (it->cur_task)
@@ -4338,10 +4344,10 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from)
        mutex_lock(&cgroup_mutex);
 
        /* all tasks in @from are being moved, all csets are source */
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
        list_for_each_entry(link, &from->cset_links, cset_link)
                cgroup_migrate_add_src(link->cset, to, &preloaded_csets);
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
 
        ret = cgroup_migrate_prepare_dst(&preloaded_csets);
        if (ret)
@@ -5063,6 +5069,7 @@ static void init_and_link_css(struct cgroup_subsys_state *css,
        memset(css, 0, sizeof(*css));
        css->cgroup = cgrp;
        css->ss = ss;
+       css->id = -1;
        INIT_LIST_HEAD(&css->sibling);
        INIT_LIST_HEAD(&css->children);
        css->serial_nr = css_serial_nr_next++;
@@ -5150,7 +5157,7 @@ static struct cgroup_subsys_state *css_create(struct cgroup *cgrp,
 
        err = cgroup_idr_alloc(&ss->css_idr, NULL, 2, 0, GFP_KERNEL);
        if (err < 0)
-               goto err_free_percpu_ref;
+               goto err_free_css;
        css->id = err;
 
        /* @css is ready to be brought online now, make it visible */
@@ -5174,9 +5181,6 @@ static struct cgroup_subsys_state *css_create(struct cgroup *cgrp,
 
 err_list_del:
        list_del_rcu(&css->sibling);
-       cgroup_idr_remove(&ss->css_idr, css->id);
-err_free_percpu_ref:
-       percpu_ref_exit(&css->refcnt);
 err_free_css:
        call_rcu(&css->rcu_head, css_free_rcu_fn);
        return ERR_PTR(err);
@@ -5451,10 +5455,10 @@ static int cgroup_destroy_locked(struct cgroup *cgrp)
         */
        cgrp->self.flags &= ~CSS_ONLINE;
 
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
        list_for_each_entry(link, &cgrp->cset_links, cset_link)
                link->cset->dead = true;
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
 
        /* initiate massacre of all css's */
        for_each_css(css, ssid, cgrp)
@@ -5725,7 +5729,7 @@ int proc_cgroup_show(struct seq_file *m, struct pid_namespace *ns,
                goto out;
 
        mutex_lock(&cgroup_mutex);
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
 
        for_each_root(root) {
                struct cgroup_subsys *ss;
@@ -5778,7 +5782,7 @@ int proc_cgroup_show(struct seq_file *m, struct pid_namespace *ns,
 
        retval = 0;
 out_unlock:
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
        mutex_unlock(&cgroup_mutex);
        kfree(buf);
 out:
@@ -5923,13 +5927,13 @@ void cgroup_post_fork(struct task_struct *child)
        if (use_task_css_set_links) {
                struct css_set *cset;
 
-               spin_lock_bh(&css_set_lock);
+               spin_lock_irq(&css_set_lock);
                cset = task_css_set(current);
                if (list_empty(&child->cg_list)) {
                        get_css_set(cset);
                        css_set_move_task(child, NULL, cset, false);
                }
-               spin_unlock_bh(&css_set_lock);
+               spin_unlock_irq(&css_set_lock);
        }
 
        /*
@@ -5974,9 +5978,9 @@ void cgroup_exit(struct task_struct *tsk)
        cset = task_css_set(tsk);
 
        if (!list_empty(&tsk->cg_list)) {
-               spin_lock_bh(&css_set_lock);
+               spin_lock_irq(&css_set_lock);
                css_set_move_task(tsk, cset, NULL, false);
-               spin_unlock_bh(&css_set_lock);
+               spin_unlock_irq(&css_set_lock);
        } else {
                get_css_set(cset);
        }
@@ -6044,9 +6048,9 @@ static void cgroup_release_agent(struct work_struct *work)
        if (!pathbuf || !agentbuf)
                goto out;
 
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
        path = cgroup_path_ns_locked(cgrp, pathbuf, PATH_MAX, &init_cgroup_ns);
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
        if (!path)
                goto out;
 
@@ -6306,12 +6310,12 @@ struct cgroup_namespace *copy_cgroup_ns(unsigned long flags,
                return ERR_PTR(-EPERM);
 
        mutex_lock(&cgroup_mutex);
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
 
        cset = task_css_set(current);
        get_css_set(cset);
 
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
        mutex_unlock(&cgroup_mutex);
 
        new_ns = alloc_cgroup_ns();
@@ -6435,7 +6439,7 @@ static int current_css_set_cg_links_read(struct seq_file *seq, void *v)
        if (!name_buf)
                return -ENOMEM;
 
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
        rcu_read_lock();
        cset = rcu_dereference(current->cgroups);
        list_for_each_entry(link, &cset->cgrp_links, cgrp_link) {
@@ -6446,7 +6450,7 @@ static int current_css_set_cg_links_read(struct seq_file *seq, void *v)
                           c->root->hierarchy_id, name_buf);
        }
        rcu_read_unlock();
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
        kfree(name_buf);
        return 0;
 }
@@ -6457,7 +6461,7 @@ static int cgroup_css_links_read(struct seq_file *seq, void *v)
        struct cgroup_subsys_state *css = seq_css(seq);
        struct cgrp_cset_link *link;
 
-       spin_lock_bh(&css_set_lock);
+       spin_lock_irq(&css_set_lock);
        list_for_each_entry(link, &css->cgroup->cset_links, cset_link) {
                struct css_set *cset = link->cset;
                struct task_struct *task;
@@ -6480,7 +6484,7 @@ static int cgroup_css_links_read(struct seq_file *seq, void *v)
        overflow:
                seq_puts(seq, "  ...\n");
        }
-       spin_unlock_bh(&css_set_lock);
+       spin_unlock_irq(&css_set_lock);
        return 0;
 }
 
index d948e44c471ea89aa2953f7a687c6b5783bbdf0e..7b61887f7ccdf57fdcb3083574a56c5580110d73 100644 (file)
@@ -1201,6 +1201,8 @@ static struct cpuhp_step cpuhp_bp_states[] = {
                .teardown               = takedown_cpu,
                .cant_stop              = true,
        },
+#else
+       [CPUHP_BRINGUP_CPU] = { },
 #endif
 };
 
index 9c51ec3f0f440f35dda1bf9e9b3e02e421388df3..43d43a2d5811d548271c138c0e1ed31d541c19ad 100644 (file)
@@ -1678,12 +1678,33 @@ static bool is_orphaned_event(struct perf_event *event)
        return event->state == PERF_EVENT_STATE_DEAD;
 }
 
-static inline int pmu_filter_match(struct perf_event *event)
+static inline int __pmu_filter_match(struct perf_event *event)
 {
        struct pmu *pmu = event->pmu;
        return pmu->filter_match ? pmu->filter_match(event) : 1;
 }
 
+/*
+ * Check whether we should attempt to schedule an event group based on
+ * PMU-specific filtering. An event group can consist of HW and SW events,
+ * potentially with a SW leader, so we must check all the filters, to
+ * determine whether a group is schedulable:
+ */
+static inline int pmu_filter_match(struct perf_event *event)
+{
+       struct perf_event *child;
+
+       if (!__pmu_filter_match(event))
+               return 0;
+
+       list_for_each_entry(child, &event->sibling_list, group_entry) {
+               if (!__pmu_filter_match(child))
+                       return 0;
+       }
+
+       return 1;
+}
+
 static inline int
 event_filter_match(struct perf_event *event)
 {
@@ -7529,7 +7550,7 @@ static void perf_event_free_bpf_prog(struct perf_event *event)
        prog = event->tp_event->prog;
        if (prog) {
                event->tp_event->prog = NULL;
-               bpf_prog_put(prog);
+               bpf_prog_put_rcu(prog);
        }
 }
 
index e25e92fb44face315265d43d981b71e193693f2a..6a5c239c7669c5ad8e01565be5a6fd38ae7d4452 100644 (file)
@@ -18,7 +18,7 @@
 #include <linux/vmalloc.h>
 #include "gcov.h"
 
-#if __GNUC__ == 5 && __GNUC_MINOR__ >= 1
+#if (__GNUC__ > 5) || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)
 #define GCOV_COUNTERS                  10
 #elif __GNUC__ == 4 && __GNUC_MINOR__ >= 9
 #define GCOV_COUNTERS                  9
index 51d7105f529a5527ee8c90ac789f08583027a373..97ee9ac7e97c7743249295fa0c4d2da32740bbf7 100644 (file)
@@ -5394,13 +5394,15 @@ void idle_task_exit(void)
 /*
  * Since this CPU is going 'away' for a while, fold any nr_active delta
  * we might have. Assumes we're called after migrate_tasks() so that the
- * nr_active count is stable.
+ * nr_active count is stable. We need to take the teardown thread which
+ * is calling this into account, so we hand in adjust = 1 to the load
+ * calculation.
  *
  * Also see the comment "Global load-average calculations".
  */
 static void calc_load_migrate(struct rq *rq)
 {
-       long delta = calc_load_fold_active(rq);
+       long delta = calc_load_fold_active(rq, 1);
        if (delta)
                atomic_long_add(delta, &calc_load_tasks);
 }
index bdcbeea90c950523b5276cfd1d8464ceef2d4fd8..c8c5d2d484249048cca7dd62fdfc38fbe7de2883 100644 (file)
@@ -735,8 +735,6 @@ void post_init_entity_util_avg(struct sched_entity *se)
        }
 }
 
-static inline unsigned long cfs_rq_runnable_load_avg(struct cfs_rq *cfs_rq);
-static inline unsigned long cfs_rq_load_avg(struct cfs_rq *cfs_rq);
 #else
 void init_entity_runnable_average(struct sched_entity *se)
 {
@@ -2499,28 +2497,22 @@ account_entity_dequeue(struct cfs_rq *cfs_rq, struct sched_entity *se)
 
 #ifdef CONFIG_FAIR_GROUP_SCHED
 # ifdef CONFIG_SMP
-static inline long calc_tg_weight(struct task_group *tg, struct cfs_rq *cfs_rq)
+static long calc_cfs_shares(struct cfs_rq *cfs_rq, struct task_group *tg)
 {
-       long tg_weight;
+       long tg_weight, load, shares;
 
        /*
-        * Use this CPU's real-time load instead of the last load contribution
-        * as the updating of the contribution is delayed, and we will use the
-        * the real-time load to calc the share. See update_tg_load_avg().
+        * This really should be: cfs_rq->avg.load_avg, but instead we use
+        * cfs_rq->load.weight, which is its upper bound. This helps ramp up
+        * the shares for small weight interactive tasks.
         */
-       tg_weight = atomic_long_read(&tg->load_avg);
-       tg_weight -= cfs_rq->tg_load_avg_contrib;
-       tg_weight += cfs_rq->load.weight;
+       load = scale_load_down(cfs_rq->load.weight);
 
-       return tg_weight;
-}
-
-static long calc_cfs_shares(struct cfs_rq *cfs_rq, struct task_group *tg)
-{
-       long tg_weight, load, shares;
+       tg_weight = atomic_long_read(&tg->load_avg);
 
-       tg_weight = calc_tg_weight(tg, cfs_rq);
-       load = cfs_rq->load.weight;
+       /* Ensure tg_weight >= load */
+       tg_weight -= cfs_rq->tg_load_avg_contrib;
+       tg_weight += load;
 
        shares = (tg->shares * load);
        if (tg_weight)
@@ -2539,6 +2531,7 @@ static inline long calc_cfs_shares(struct cfs_rq *cfs_rq, struct task_group *tg)
        return tg->shares;
 }
 # endif /* CONFIG_SMP */
+
 static void reweight_entity(struct cfs_rq *cfs_rq, struct sched_entity *se,
                            unsigned long weight)
 {
@@ -4946,19 +4939,24 @@ static long effective_load(struct task_group *tg, int cpu, long wl, long wg)
                return wl;
 
        for_each_sched_entity(se) {
-               long w, W;
+               struct cfs_rq *cfs_rq = se->my_q;
+               long W, w = cfs_rq_load_avg(cfs_rq);
 
-               tg = se->my_q->tg;
+               tg = cfs_rq->tg;
 
                /*
                 * W = @wg + \Sum rw_j
                 */
-               W = wg + calc_tg_weight(tg, se->my_q);
+               W = wg + atomic_long_read(&tg->load_avg);
+
+               /* Ensure \Sum rw_j >= rw_i */
+               W -= cfs_rq->tg_load_avg_contrib;
+               W += w;
 
                /*
                 * w = rw_i + @wl
                 */
-               w = cfs_rq_load_avg(se->my_q) + wl;
+               w += wl;
 
                /*
                 * wl = S * s'_i; see (2)
index b0b93fd33af9e4bb4d61edcda77d3b761cb9b8de..a2d6eb71f06b80527b86dd99a83f6d4621265cec 100644 (file)
@@ -78,11 +78,11 @@ void get_avenrun(unsigned long *loads, unsigned long offset, int shift)
        loads[2] = (avenrun[2] + offset) << shift;
 }
 
-long calc_load_fold_active(struct rq *this_rq)
+long calc_load_fold_active(struct rq *this_rq, long adjust)
 {
        long nr_active, delta = 0;
 
-       nr_active = this_rq->nr_running;
+       nr_active = this_rq->nr_running - adjust;
        nr_active += (long)this_rq->nr_uninterruptible;
 
        if (nr_active != this_rq->calc_load_active) {
@@ -188,7 +188,7 @@ void calc_load_enter_idle(void)
         * We're going into NOHZ mode, if there's any pending delta, fold it
         * into the pending idle delta.
         */
-       delta = calc_load_fold_active(this_rq);
+       delta = calc_load_fold_active(this_rq, 0);
        if (delta) {
                int idx = calc_load_write_idx();
 
@@ -389,7 +389,7 @@ void calc_global_load_tick(struct rq *this_rq)
        if (time_before(jiffies, this_rq->calc_load_update))
                return;
 
-       delta  = calc_load_fold_active(this_rq);
+       delta  = calc_load_fold_active(this_rq, 0);
        if (delta)
                atomic_long_add(delta, &calc_load_tasks);
 
index 7cbeb92a1cb9361102b8514f612ee81be87fef10..898c0d2f18feb78e73746f0f3546b6f4de375dca 100644 (file)
@@ -28,7 +28,7 @@ extern unsigned long calc_load_update;
 extern atomic_long_t calc_load_tasks;
 
 extern void calc_global_load_tick(struct rq *this_rq);
-extern long calc_load_fold_active(struct rq *this_rq);
+extern long calc_load_fold_active(struct rq *this_rq, long adjust);
 
 #ifdef CONFIG_SMP
 extern void cpu_load_update_active(struct rq *this_rq);
index 1cafba860b08ceb6030fa2a1f237e3950a4e0aa7..39008d78927acb4f9a62f582cb7baba3f27620ee 100644 (file)
@@ -777,6 +777,7 @@ static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec *itp)
                        timer->it.cpu.expires = 0;
                        sample_to_timespec(timer->it_clock, timer->it.cpu.expires,
                                           &itp->it_value);
+                       return;
                } else {
                        cpu_timer_sample_group(timer->it_clock, p, &now);
                        unlock_task_sighand(p, &flags);
index 720b7bb01d43ae5f49761da3a34d8fce4e55dbc2..26f603da7e26867fc47835a8fcd3eca4121e2d86 100644 (file)
@@ -209,6 +209,10 @@ static u64 bpf_perf_event_read(u64 r1, u64 index, u64 r3, u64 r4, u64 r5)
            event->pmu->count)
                return -EINVAL;
 
+       if (unlikely(event->attr.type != PERF_TYPE_HARDWARE &&
+                    event->attr.type != PERF_TYPE_RAW))
+               return -EINVAL;
+
        /*
         * we don't know if the function is run successfully by the
         * return value. It can be judged in other places, such as
@@ -349,7 +353,8 @@ static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func
 }
 
 /* bpf+kprobe programs can access fields of 'struct pt_regs' */
-static bool kprobe_prog_is_valid_access(int off, int size, enum bpf_access_type type)
+static bool kprobe_prog_is_valid_access(int off, int size, enum bpf_access_type type,
+                                       enum bpf_reg_type *reg_type)
 {
        /* check bounds */
        if (off < 0 || off >= sizeof(struct pt_regs))
@@ -427,7 +432,8 @@ static const struct bpf_func_proto *tp_prog_func_proto(enum bpf_func_id func_id)
        }
 }
 
-static bool tp_prog_is_valid_access(int off, int size, enum bpf_access_type type)
+static bool tp_prog_is_valid_access(int off, int size, enum bpf_access_type type,
+                                   enum bpf_reg_type *reg_type)
 {
        if (off < sizeof(void *) || off >= PERF_MAX_TRACE_SIZE)
                return false;
index e1c0e996b5ae63175feb24d5d6a6c8b541b63287..97e7b793df35be3a1ce01d3bf22ed2dc2d28fd84 100644 (file)
@@ -4600,15 +4600,11 @@ static void restore_unbound_workers_cpumask(struct worker_pool *pool, int cpu)
        if (!cpumask_test_cpu(cpu, pool->attrs->cpumask))
                return;
 
-       /* is @cpu the only online CPU? */
        cpumask_and(&cpumask, pool->attrs->cpumask, cpu_online_mask);
-       if (cpumask_weight(&cpumask) != 1)
-               return;
 
        /* as we're called from CPU_ONLINE, the following shouldn't fail */
        for_each_pool_worker(worker, pool)
-               WARN_ON_ONCE(set_cpus_allowed_ptr(worker->task,
-                                                 pool->attrs->cpumask) < 0);
+               WARN_ON_ONCE(set_cpus_allowed_ptr(worker->task, &cpumask) < 0);
 }
 
 /*
index 79bfe0e06907ac91875dddc9a3fbcf579b0c4d51..7bc04778f84dd6e2b68e4fadec74530f4bc64d57 100644 (file)
@@ -1009,8 +1009,6 @@ static void isolate_freepages(struct compact_control *cc)
                                block_end_pfn = block_start_pfn,
                                block_start_pfn -= pageblock_nr_pages,
                                isolate_start_pfn = block_start_pfn) {
-               unsigned long isolated;
-
                /*
                 * This can iterate a massively long zone without finding any
                 * suitable migration targets, so periodically check if we need
@@ -1034,36 +1032,30 @@ static void isolate_freepages(struct compact_control *cc)
                        continue;
 
                /* Found a block suitable for isolating free pages from. */
-               isolated = isolate_freepages_block(cc, &isolate_start_pfn,
-                                               block_end_pfn, freelist, false);
-               /* If isolation failed early, do not continue needlessly */
-               if (!isolated && isolate_start_pfn < block_end_pfn &&
-                   cc->nr_migratepages > cc->nr_freepages)
-                       break;
+               isolate_freepages_block(cc, &isolate_start_pfn, block_end_pfn,
+                                       freelist, false);
 
                /*
-                * If we isolated enough freepages, or aborted due to async
-                * compaction being contended, terminate the loop.
-                * Remember where the free scanner should restart next time,
-                * which is where isolate_freepages_block() left off.
-                * But if it scanned the whole pageblock, isolate_start_pfn
-                * now points at block_end_pfn, which is the start of the next
-                * pageblock.
-                * In that case we will however want to restart at the start
-                * of the previous pageblock.
+                * If we isolated enough freepages, or aborted due to lock
+                * contention, terminate.
                 */
                if ((cc->nr_freepages >= cc->nr_migratepages)
                                                        || cc->contended) {
-                       if (isolate_start_pfn >= block_end_pfn)
+                       if (isolate_start_pfn >= block_end_pfn) {
+                               /*
+                                * Restart at previous pageblock if more
+                                * freepages can be isolated next time.
+                                */
                                isolate_start_pfn =
                                        block_start_pfn - pageblock_nr_pages;
+                       }
                        break;
-               } else {
+               } else if (isolate_start_pfn < block_end_pfn) {
                        /*
-                        * isolate_freepages_block() should not terminate
-                        * prematurely unless contended, or isolated enough
+                        * If isolation failed early, do not continue
+                        * needlessly.
                         */
-                       VM_BUG_ON(isolate_start_pfn < block_end_pfn);
+                       break;
                }
        }
 
index 9ed58530f6957bef1e2e0fa166ed87085dcae523..343a2b7e57aa25f7d02709ff13ab8a5e47c68a87 100644 (file)
@@ -1624,14 +1624,9 @@ int madvise_free_huge_pmd(struct mmu_gather *tlb, struct vm_area_struct *vma,
        if (next - addr != HPAGE_PMD_SIZE) {
                get_page(page);
                spin_unlock(ptl);
-               if (split_huge_page(page)) {
-                       put_page(page);
-                       unlock_page(page);
-                       goto out_unlocked;
-               }
+               split_huge_page(page);
                put_page(page);
                unlock_page(page);
-               ret = 1;
                goto out_unlocked;
        }
 
@@ -2989,7 +2984,7 @@ static void __split_huge_pmd_locked(struct vm_area_struct *vma, pmd_t *pmd,
 }
 
 void __split_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
-               unsigned long address, bool freeze)
+               unsigned long address, bool freeze, struct page *page)
 {
        spinlock_t *ptl;
        struct mm_struct *mm = vma->vm_mm;
@@ -2997,8 +2992,17 @@ void __split_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
 
        mmu_notifier_invalidate_range_start(mm, haddr, haddr + HPAGE_PMD_SIZE);
        ptl = pmd_lock(mm, pmd);
+
+       /*
+        * If caller asks to setup a migration entries, we need a page to check
+        * pmd against. Otherwise we can end up replacing wrong page.
+        */
+       VM_BUG_ON(freeze && !page);
+       if (page && page != pmd_page(*pmd))
+               goto out;
+
        if (pmd_trans_huge(*pmd)) {
-               struct page *page = pmd_page(*pmd);
+               page = pmd_page(*pmd);
                if (PageMlocked(page))
                        clear_page_mlock(page);
        } else if (!pmd_devmap(*pmd))
@@ -3025,24 +3029,8 @@ void split_huge_pmd_address(struct vm_area_struct *vma, unsigned long address,
                return;
 
        pmd = pmd_offset(pud, address);
-       if (!pmd_present(*pmd) || (!pmd_trans_huge(*pmd) && !pmd_devmap(*pmd)))
-               return;
 
-       /*
-        * If caller asks to setup a migration entries, we need a page to check
-        * pmd against. Otherwise we can end up replacing wrong page.
-        */
-       VM_BUG_ON(freeze && !page);
-       if (page && page != pmd_page(*pmd))
-               return;
-
-       /*
-        * Caller holds the mmap_sem write mode or the anon_vma lock,
-        * so a huge pmd cannot materialize from under us (khugepaged
-        * holds both the mmap_sem write mode and the anon_vma lock
-        * write mode).
-        */
-       __split_huge_pmd(vma, pmd, address, freeze);
+       __split_huge_pmd(vma, pmd, address, freeze, page);
 }
 
 void vma_adjust_trans_huge(struct vm_area_struct *vma,
index c1f3c0be150a94f09588672ce44ade9047d78958..addfe4accc076817cdbc4009bb9f44d5e61315d4 100644 (file)
@@ -3383,7 +3383,7 @@ static int hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma,
        /* If no-one else is actually using this page, avoid the copy
         * and just make the page writable */
        if (page_mapcount(old_page) == 1 && PageAnon(old_page)) {
-               page_move_anon_rmap(old_page, vma, address);
+               page_move_anon_rmap(old_page, vma);
                set_huge_ptep_writable(vma, address, ptep);
                return 0;
        }
index 4973505a9bdde8fda4291debdf21bdadfaa76c3b..65793f150d1f6e1f77d32ad64d37966f0ad9704c 100644 (file)
@@ -238,30 +238,23 @@ static void qlist_move_cache(struct qlist_head *from,
                                   struct qlist_head *to,
                                   struct kmem_cache *cache)
 {
-       struct qlist_node *prev = NULL, *curr;
+       struct qlist_node *curr;
 
        if (unlikely(qlist_empty(from)))
                return;
 
        curr = from->head;
+       qlist_init(from);
        while (curr) {
-               struct qlist_node *qlink = curr;
-               struct kmem_cache *obj_cache = qlink_to_cache(qlink);
-
-               if (obj_cache == cache) {
-                       if (unlikely(from->head == qlink)) {
-                               from->head = curr->next;
-                               prev = curr;
-                       } else
-                               prev->next = curr->next;
-                       if (unlikely(from->tail == qlink))
-                               from->tail = curr->next;
-                       from->bytes -= cache->size;
-                       qlist_put(to, qlink, cache->size);
-               } else {
-                       prev = curr;
-               }
-               curr = curr->next;
+               struct qlist_node *next = curr->next;
+               struct kmem_cache *obj_cache = qlink_to_cache(curr);
+
+               if (obj_cache == cache)
+                       qlist_put(to, curr, obj_cache->size);
+               else
+                       qlist_put(from, curr, obj_cache->size);
+
+               curr = next;
        }
 }
 
index ac8664db38232f5ede345ee4b2f2f9ec0c5ac79d..5339c89dff6317510b2710e9ab2770c23ac71e1e 100644 (file)
@@ -4057,6 +4057,60 @@ static struct cftype mem_cgroup_legacy_files[] = {
        { },    /* terminate */
 };
 
+/*
+ * Private memory cgroup IDR
+ *
+ * Swap-out records and page cache shadow entries need to store memcg
+ * references in constrained space, so we maintain an ID space that is
+ * limited to 16 bit (MEM_CGROUP_ID_MAX), limiting the total number of
+ * memory-controlled cgroups to 64k.
+ *
+ * However, there usually are many references to the oflline CSS after
+ * the cgroup has been destroyed, such as page cache or reclaimable
+ * slab objects, that don't need to hang on to the ID. We want to keep
+ * those dead CSS from occupying IDs, or we might quickly exhaust the
+ * relatively small ID space and prevent the creation of new cgroups
+ * even when there are much fewer than 64k cgroups - possibly none.
+ *
+ * Maintain a private 16-bit ID space for memcg, and allow the ID to
+ * be freed and recycled when it's no longer needed, which is usually
+ * when the CSS is offlined.
+ *
+ * The only exception to that are records of swapped out tmpfs/shmem
+ * pages that need to be attributed to live ancestors on swapin. But
+ * those references are manageable from userspace.
+ */
+
+static DEFINE_IDR(mem_cgroup_idr);
+
+static void mem_cgroup_id_get(struct mem_cgroup *memcg)
+{
+       atomic_inc(&memcg->id.ref);
+}
+
+static void mem_cgroup_id_put(struct mem_cgroup *memcg)
+{
+       if (atomic_dec_and_test(&memcg->id.ref)) {
+               idr_remove(&mem_cgroup_idr, memcg->id.id);
+               memcg->id.id = 0;
+
+               /* Memcg ID pins CSS */
+               css_put(&memcg->css);
+       }
+}
+
+/**
+ * mem_cgroup_from_id - look up a memcg from a memcg id
+ * @id: the memcg id to look up
+ *
+ * Caller must hold rcu_read_lock().
+ */
+struct mem_cgroup *mem_cgroup_from_id(unsigned short id)
+{
+       WARN_ON_ONCE(!rcu_read_lock_held());
+       return idr_find(&mem_cgroup_idr, id);
+}
+
 static int alloc_mem_cgroup_per_zone_info(struct mem_cgroup *memcg, int node)
 {
        struct mem_cgroup_per_node *pn;
@@ -4116,6 +4170,12 @@ static struct mem_cgroup *mem_cgroup_alloc(void)
        if (!memcg)
                return NULL;
 
+       memcg->id.id = idr_alloc(&mem_cgroup_idr, NULL,
+                                1, MEM_CGROUP_ID_MAX,
+                                GFP_KERNEL);
+       if (memcg->id.id < 0)
+               goto fail;
+
        memcg->stat = alloc_percpu(struct mem_cgroup_stat_cpu);
        if (!memcg->stat)
                goto fail;
@@ -4142,8 +4202,11 @@ static struct mem_cgroup *mem_cgroup_alloc(void)
 #ifdef CONFIG_CGROUP_WRITEBACK
        INIT_LIST_HEAD(&memcg->cgwb_list);
 #endif
+       idr_replace(&mem_cgroup_idr, memcg, memcg->id.id);
        return memcg;
 fail:
+       if (memcg->id.id > 0)
+               idr_remove(&mem_cgroup_idr, memcg->id.id);
        mem_cgroup_free(memcg);
        return NULL;
 }
@@ -4206,12 +4269,11 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
        return ERR_PTR(-ENOMEM);
 }
 
-static int
-mem_cgroup_css_online(struct cgroup_subsys_state *css)
+static int mem_cgroup_css_online(struct cgroup_subsys_state *css)
 {
-       if (css->id > MEM_CGROUP_ID_MAX)
-               return -ENOSPC;
-
+       /* Online state pins memcg ID, memcg ID pins CSS */
+       mem_cgroup_id_get(mem_cgroup_from_css(css));
+       css_get(css);
        return 0;
 }
 
@@ -4234,6 +4296,8 @@ static void mem_cgroup_css_offline(struct cgroup_subsys_state *css)
 
        memcg_offline_kmem(memcg);
        wb_memcg_offline(memcg);
+
+       mem_cgroup_id_put(memcg);
 }
 
 static void mem_cgroup_css_released(struct cgroup_subsys_state *css)
@@ -5756,6 +5820,7 @@ void mem_cgroup_swapout(struct page *page, swp_entry_t entry)
        if (!memcg)
                return;
 
+       mem_cgroup_id_get(memcg);
        oldid = swap_cgroup_record(entry, mem_cgroup_id(memcg));
        VM_BUG_ON_PAGE(oldid, page);
        mem_cgroup_swap_statistics(memcg, true);
@@ -5774,6 +5839,9 @@ void mem_cgroup_swapout(struct page *page, swp_entry_t entry)
        VM_BUG_ON(!irqs_disabled());
        mem_cgroup_charge_statistics(memcg, page, false, -1);
        memcg_check_events(memcg, page);
+
+       if (!mem_cgroup_is_root(memcg))
+               css_put(&memcg->css);
 }
 
 /*
@@ -5804,11 +5872,11 @@ int mem_cgroup_try_charge_swap(struct page *page, swp_entry_t entry)
            !page_counter_try_charge(&memcg->swap, 1, &counter))
                return -ENOMEM;
 
+       mem_cgroup_id_get(memcg);
        oldid = swap_cgroup_record(entry, mem_cgroup_id(memcg));
        VM_BUG_ON_PAGE(oldid, page);
        mem_cgroup_swap_statistics(memcg, true);
 
-       css_get(&memcg->css);
        return 0;
 }
 
@@ -5837,7 +5905,7 @@ void mem_cgroup_uncharge_swap(swp_entry_t entry)
                                page_counter_uncharge(&memcg->memsw, 1);
                }
                mem_cgroup_swap_statistics(memcg, false);
-               css_put(&memcg->css);
+               mem_cgroup_id_put(memcg);
        }
        rcu_read_unlock();
 }
index cd1f29e4897e3e5207071ed9cccabc55f86e6a87..9e046819e619487ed5a8e3cc65f4131812168692 100644 (file)
@@ -2399,8 +2399,7 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
                                 * Protected against the rmap code by
                                 * the page lock.
                                 */
-                               page_move_anon_rmap(compound_head(old_page),
-                                                   vma, address);
+                               page_move_anon_rmap(old_page, vma);
                        }
                        unlock_page(old_page);
                        return wp_page_reuse(mm, vma, address, page_table, ptl,
index 6903b695ebaef81ef890f4b5e41c749d89dce2e1..8b3e1341b7544608cac4777a37bbd424432488e1 100644 (file)
@@ -286,7 +286,9 @@ static inline void reset_deferred_meminit(pg_data_t *pgdat)
 /* Returns true if the struct page for the pfn is uninitialised */
 static inline bool __meminit early_page_uninitialised(unsigned long pfn)
 {
-       if (pfn >= NODE_DATA(early_pfn_to_nid(pfn))->first_deferred_pfn)
+       int nid = early_pfn_to_nid(pfn);
+
+       if (node_online(nid) && pfn >= NODE_DATA(nid)->first_deferred_pfn)
                return true;
 
        return false;
@@ -1273,7 +1275,7 @@ int __meminit early_pfn_to_nid(unsigned long pfn)
        spin_lock(&early_pfn_lock);
        nid = __early_pfn_to_nid(pfn, &early_pfnnid_cache);
        if (nid < 0)
-               nid = 0;
+               nid = first_online_node;
        spin_unlock(&early_pfn_lock);
 
        return nid;
index 0ea5d9071b32b967d012f36e600a2ee75acd8f3d..701b93fea2a0677cd8a58e646360747bf5626c5a 100644 (file)
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -1084,23 +1084,20 @@ EXPORT_SYMBOL_GPL(page_mkclean);
  * page_move_anon_rmap - move a page to our anon_vma
  * @page:      the page to move to our anon_vma
  * @vma:       the vma the page belongs to
- * @address:   the user virtual address mapped
  *
  * When a page belongs exclusively to one process after a COW event,
  * that page can be moved into the anon_vma that belongs to just that
  * process, so the rmap code will not search the parent or sibling
  * processes.
  */
-void page_move_anon_rmap(struct page *page,
-       struct vm_area_struct *vma, unsigned long address)
+void page_move_anon_rmap(struct page *page, struct vm_area_struct *vma)
 {
        struct anon_vma *anon_vma = vma->anon_vma;
 
+       page = compound_head(page);
+
        VM_BUG_ON_PAGE(!PageLocked(page), page);
        VM_BUG_ON_VMA(!anon_vma, vma);
-       if (IS_ENABLED(CONFIG_DEBUG_VM) && PageTransHuge(page))
-               address &= HPAGE_PMD_MASK;
-       VM_BUG_ON_PAGE(page->index != linear_page_index(vma, address), page);
 
        anon_vma = (void *) anon_vma + PAGE_MAPPING_ANON;
        /*
@@ -1427,7 +1424,8 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
                        goto out;
        }
 
-       pte = page_check_address(page, mm, address, &ptl, 0);
+       pte = page_check_address(page, mm, address, &ptl,
+                                PageTransCompound(page));
        if (!pte)
                goto out;
 
index 24463b67b6efa5817e7c1e806d1bf6337d300ba2..171dee7a131f6959fe444405f280374be7eb25d0 100644 (file)
@@ -2225,9 +2225,11 @@ static long shmem_fallocate(struct file *file, int mode, loff_t offset,
                        error = shmem_getpage(inode, index, &page, SGP_FALLOC);
                if (error) {
                        /* Remove the !PageUptodate pages we added */
-                       shmem_undo_range(inode,
-                               (loff_t)start << PAGE_SHIFT,
-                               ((loff_t)index << PAGE_SHIFT) - 1, true);
+                       if (index > start) {
+                               shmem_undo_range(inode,
+                                   (loff_t)start << PAGE_SHIFT,
+                                   ((loff_t)index << PAGE_SHIFT) - 1, true);
+                       }
                        goto undone;
                }
 
index a65dad7fdcd12495a51eabd91fc76ed96edb0576..82317abb03edc7aa2c89e0a20032fc57a532a723 100644 (file)
@@ -526,8 +526,8 @@ void memcg_create_kmem_cache(struct mem_cgroup *memcg,
                goto out_unlock;
 
        cgroup_name(css->cgroup, memcg_name_buf, sizeof(memcg_name_buf));
-       cache_name = kasprintf(GFP_KERNEL, "%s(%d:%s)", root_cache->name,
-                              css->id, memcg_name_buf);
+       cache_name = kasprintf(GFP_KERNEL, "%s(%llu:%s)", root_cache->name,
+                              css->serial_nr, memcg_name_buf);
        if (!cache_name)
                goto out_unlock;
 
index 8a75f8d2916af99a1efb6101666038dcd4309c04..577277546d985db41aee898a3c69b1b0488d289b 100644 (file)
@@ -491,7 +491,7 @@ static int __init workingset_init(void)
        max_order = fls_long(totalram_pages - 1);
        if (max_order > timestamp_bits)
                bucket_order = max_order - timestamp_bits;
-       printk("workingset: timestamp_bits=%d max_order=%d bucket_order=%u\n",
+       pr_info("workingset: timestamp_bits=%d max_order=%d bucket_order=%u\n",
               timestamp_bits, max_order, bucket_order);
 
        ret = list_lru_init_key(&workingset_shadow_nodes, &shadow_nodes_key);
index 86ae75b77390964ee42056c0dd1763650d9fb9ff..516b0e73263c7cac1db1546d05eb6a1d6f642ab5 100644 (file)
@@ -146,10 +146,12 @@ static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb,
 
 static int vlan_dev_change_mtu(struct net_device *dev, int new_mtu)
 {
-       /* TODO: gotta make sure the underlying layer can handle it,
-        * maybe an IFF_VLAN_CAPABLE flag for devices?
-        */
-       if (vlan_dev_priv(dev)->real_dev->mtu < new_mtu)
+       struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;
+       unsigned int max_mtu = real_dev->mtu;
+
+       if (netif_reduces_vlan_mtu(real_dev))
+               max_mtu -= VLAN_HLEN;
+       if (max_mtu < new_mtu)
                return -ERANGE;
 
        dev->mtu = new_mtu;
index c92b52f37d38de143022f172881dd03f076b0194..1270207f3d7c9dd6fde0e1f7839acfe18a4d82c9 100644 (file)
@@ -118,6 +118,7 @@ static int vlan_newlink(struct net *src_net, struct net_device *dev,
 {
        struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
        struct net_device *real_dev;
+       unsigned int max_mtu;
        __be16 proto;
        int err;
 
@@ -144,9 +145,11 @@ static int vlan_newlink(struct net *src_net, struct net_device *dev,
        if (err < 0)
                return err;
 
+       max_mtu = netif_reduces_vlan_mtu(real_dev) ? real_dev->mtu - VLAN_HLEN :
+                                                    real_dev->mtu;
        if (!tb[IFLA_MTU])
-               dev->mtu = real_dev->mtu;
-       else if (dev->mtu > real_dev->mtu)
+               dev->mtu = max_mtu;
+       else if (dev->mtu > max_mtu)
                return -EINVAL;
 
        err = vlan_changelink(dev, tb, data);
index fbd0acf80b13236bd8c768bc8bf5d69d6a7e7125..2fdebabbfacd14690abaca3c57dfcddf944466a1 100644 (file)
@@ -976,7 +976,8 @@ static int ax25_release(struct socket *sock)
                        release_sock(sk);
                        ax25_disconnect(ax25, 0);
                        lock_sock(sk);
-                       ax25_destroy_socket(ax25);
+                       if (!sock_flag(ax25->sk, SOCK_DESTROY))
+                               ax25_destroy_socket(ax25);
                        break;
 
                case AX25_STATE_3:
index 951cd57bb07df3930410a998b12c468ac413d62b..5237dff6941d81e3b2cd98c47013746c58798e77 100644 (file)
@@ -102,6 +102,7 @@ void ax25_ds_heartbeat_expiry(ax25_cb *ax25)
        switch (ax25->state) {
 
        case AX25_STATE_0:
+       case AX25_STATE_2:
                /* Magic here: If we listen() and a new link dies before it
                   is accepted() it isn't 'dead' so doesn't get removed. */
                if (!sk || sock_flag(sk, SOCK_DESTROY) ||
@@ -111,6 +112,7 @@ void ax25_ds_heartbeat_expiry(ax25_cb *ax25)
                                sock_hold(sk);
                                ax25_destroy_socket(ax25);
                                bh_unlock_sock(sk);
+                               /* Ungrab socket and destroy it */
                                sock_put(sk);
                        } else
                                ax25_destroy_socket(ax25);
@@ -213,7 +215,8 @@ void ax25_ds_t1_timeout(ax25_cb *ax25)
        case AX25_STATE_2:
                if (ax25->n2count == ax25->n2) {
                        ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
-                       ax25_disconnect(ax25, ETIMEDOUT);
+                       if (!sock_flag(ax25->sk, SOCK_DESTROY))
+                               ax25_disconnect(ax25, ETIMEDOUT);
                        return;
                } else {
                        ax25->n2count++;
index 004467c9e6e15ada9c6eae056cabff7dd8d5777e..2c0d6ef66f9d581d31b88ecf0875f78b90728a36 100644 (file)
@@ -38,6 +38,7 @@ void ax25_std_heartbeat_expiry(ax25_cb *ax25)
 
        switch (ax25->state) {
        case AX25_STATE_0:
+       case AX25_STATE_2:
                /* Magic here: If we listen() and a new link dies before it
                   is accepted() it isn't 'dead' so doesn't get removed. */
                if (!sk || sock_flag(sk, SOCK_DESTROY) ||
@@ -47,6 +48,7 @@ void ax25_std_heartbeat_expiry(ax25_cb *ax25)
                                sock_hold(sk);
                                ax25_destroy_socket(ax25);
                                bh_unlock_sock(sk);
+                               /* Ungrab socket and destroy it */
                                sock_put(sk);
                        } else
                                ax25_destroy_socket(ax25);
@@ -144,7 +146,8 @@ void ax25_std_t1timer_expiry(ax25_cb *ax25)
        case AX25_STATE_2:
                if (ax25->n2count == ax25->n2) {
                        ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
-                       ax25_disconnect(ax25, ETIMEDOUT);
+                       if (!sock_flag(ax25->sk, SOCK_DESTROY))
+                               ax25_disconnect(ax25, ETIMEDOUT);
                        return;
                } else {
                        ax25->n2count++;
index 3b78e8473a01b4a82e376266b04078e714ce1e26..655a7d4c96e1e26b7390b8c783cebb2a31ca4ca8 100644 (file)
@@ -264,7 +264,8 @@ void ax25_disconnect(ax25_cb *ax25, int reason)
 {
        ax25_clear_queues(ax25);
 
-       ax25_stop_heartbeat(ax25);
+       if (!sock_flag(ax25->sk, SOCK_DESTROY))
+               ax25_stop_heartbeat(ax25);
        ax25_stop_t1timer(ax25);
        ax25_stop_t2timer(ax25);
        ax25_stop_t3timer(ax25);
index 748a9ead7ce50fd65a0a09f2b19eaa035a566327..825a5cdf4382d08ff3b90c7499fd605f940b150b 100644 (file)
@@ -177,10 +177,21 @@ static void batadv_backbone_gw_put(struct batadv_bla_backbone_gw *backbone_gw)
 static void batadv_claim_release(struct kref *ref)
 {
        struct batadv_bla_claim *claim;
+       struct batadv_bla_backbone_gw *old_backbone_gw;
 
        claim = container_of(ref, struct batadv_bla_claim, refcount);
 
-       batadv_backbone_gw_put(claim->backbone_gw);
+       spin_lock_bh(&claim->backbone_lock);
+       old_backbone_gw = claim->backbone_gw;
+       claim->backbone_gw = NULL;
+       spin_unlock_bh(&claim->backbone_lock);
+
+       spin_lock_bh(&old_backbone_gw->crc_lock);
+       old_backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
+       spin_unlock_bh(&old_backbone_gw->crc_lock);
+
+       batadv_backbone_gw_put(old_backbone_gw);
+
        kfree_rcu(claim, rcu);
 }
 
@@ -418,9 +429,12 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, u8 *mac,
                break;
        }
 
-       if (vid & BATADV_VLAN_HAS_TAG)
+       if (vid & BATADV_VLAN_HAS_TAG) {
                skb = vlan_insert_tag(skb, htons(ETH_P_8021Q),
                                      vid & VLAN_VID_MASK);
+               if (!skb)
+                       goto out;
+       }
 
        skb_reset_mac_header(skb);
        skb->protocol = eth_type_trans(skb, soft_iface);
@@ -674,8 +688,10 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
                                 const u8 *mac, const unsigned short vid,
                                 struct batadv_bla_backbone_gw *backbone_gw)
 {
+       struct batadv_bla_backbone_gw *old_backbone_gw;
        struct batadv_bla_claim *claim;
        struct batadv_bla_claim search_claim;
+       bool remove_crc = false;
        int hash_added;
 
        ether_addr_copy(search_claim.addr, mac);
@@ -689,8 +705,10 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
                        return;
 
                ether_addr_copy(claim->addr, mac);
+               spin_lock_init(&claim->backbone_lock);
                claim->vid = vid;
                claim->lasttime = jiffies;
+               kref_get(&backbone_gw->refcount);
                claim->backbone_gw = backbone_gw;
 
                kref_init(&claim->refcount);
@@ -718,15 +736,26 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
                           "bla_add_claim(): changing ownership for %pM, vid %d\n",
                           mac, BATADV_PRINT_VID(vid));
 
-               spin_lock_bh(&claim->backbone_gw->crc_lock);
-               claim->backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
-               spin_unlock_bh(&claim->backbone_gw->crc_lock);
-               batadv_backbone_gw_put(claim->backbone_gw);
+               remove_crc = true;
        }
-       /* set (new) backbone gw */
+
+       /* replace backbone_gw atomically and adjust reference counters */
+       spin_lock_bh(&claim->backbone_lock);
+       old_backbone_gw = claim->backbone_gw;
        kref_get(&backbone_gw->refcount);
        claim->backbone_gw = backbone_gw;
+       spin_unlock_bh(&claim->backbone_lock);
 
+       if (remove_crc) {
+               /* remove claim address from old backbone_gw */
+               spin_lock_bh(&old_backbone_gw->crc_lock);
+               old_backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
+               spin_unlock_bh(&old_backbone_gw->crc_lock);
+       }
+
+       batadv_backbone_gw_put(old_backbone_gw);
+
+       /* add claim address to new backbone_gw */
        spin_lock_bh(&backbone_gw->crc_lock);
        backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
        spin_unlock_bh(&backbone_gw->crc_lock);
@@ -736,6 +765,26 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv,
        batadv_claim_put(claim);
 }
 
+/**
+ * batadv_bla_claim_get_backbone_gw - Get valid reference for backbone_gw of
+ *  claim
+ * @claim: claim whose backbone_gw should be returned
+ *
+ * Return: valid reference to claim::backbone_gw
+ */
+static struct batadv_bla_backbone_gw *
+batadv_bla_claim_get_backbone_gw(struct batadv_bla_claim *claim)
+{
+       struct batadv_bla_backbone_gw *backbone_gw;
+
+       spin_lock_bh(&claim->backbone_lock);
+       backbone_gw = claim->backbone_gw;
+       kref_get(&backbone_gw->refcount);
+       spin_unlock_bh(&claim->backbone_lock);
+
+       return backbone_gw;
+}
+
 /**
  * batadv_bla_del_claim - delete a claim from the claim hash
  * @bat_priv: the bat priv with all the soft interface information
@@ -760,10 +809,6 @@ static void batadv_bla_del_claim(struct batadv_priv *bat_priv,
                           batadv_choose_claim, claim);
        batadv_claim_put(claim); /* reference from the hash is gone */
 
-       spin_lock_bh(&claim->backbone_gw->crc_lock);
-       claim->backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);
-       spin_unlock_bh(&claim->backbone_gw->crc_lock);
-
        /* don't need the reference from hash_find() anymore */
        batadv_claim_put(claim);
 }
@@ -1216,6 +1261,7 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv,
                                    struct batadv_hard_iface *primary_if,
                                    int now)
 {
+       struct batadv_bla_backbone_gw *backbone_gw;
        struct batadv_bla_claim *claim;
        struct hlist_head *head;
        struct batadv_hashtable *hash;
@@ -1230,14 +1276,17 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv,
 
                rcu_read_lock();
                hlist_for_each_entry_rcu(claim, head, hash_entry) {
+                       backbone_gw = batadv_bla_claim_get_backbone_gw(claim);
                        if (now)
                                goto purge_now;
-                       if (!batadv_compare_eth(claim->backbone_gw->orig,
+
+                       if (!batadv_compare_eth(backbone_gw->orig,
                                                primary_if->net_dev->dev_addr))
-                               continue;
+                               goto skip;
+
                        if (!batadv_has_timed_out(claim->lasttime,
                                                  BATADV_BLA_CLAIM_TIMEOUT))
-                               continue;
+                               goto skip;
 
                        batadv_dbg(BATADV_DBG_BLA, bat_priv,
                                   "bla_purge_claims(): %pM, vid %d, time out\n",
@@ -1245,8 +1294,10 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv,
 
 purge_now:
                        batadv_handle_unclaim(bat_priv, primary_if,
-                                             claim->backbone_gw->orig,
+                                             backbone_gw->orig,
                                              claim->addr, claim->vid);
+skip:
+                       batadv_backbone_gw_put(backbone_gw);
                }
                rcu_read_unlock();
        }
@@ -1757,9 +1808,11 @@ batadv_bla_loopdetect_check(struct batadv_priv *bat_priv, struct sk_buff *skb,
 bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb,
                   unsigned short vid, bool is_bcast)
 {
+       struct batadv_bla_backbone_gw *backbone_gw;
        struct ethhdr *ethhdr;
        struct batadv_bla_claim search_claim, *claim = NULL;
        struct batadv_hard_iface *primary_if;
+       bool own_claim;
        bool ret;
 
        ethhdr = eth_hdr(skb);
@@ -1794,8 +1847,12 @@ bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb,
        }
 
        /* if it is our own claim ... */
-       if (batadv_compare_eth(claim->backbone_gw->orig,
-                              primary_if->net_dev->dev_addr)) {
+       backbone_gw = batadv_bla_claim_get_backbone_gw(claim);
+       own_claim = batadv_compare_eth(backbone_gw->orig,
+                                      primary_if->net_dev->dev_addr);
+       batadv_backbone_gw_put(backbone_gw);
+
+       if (own_claim) {
                /* ... allow it in any case */
                claim->lasttime = jiffies;
                goto allow;
@@ -1859,7 +1916,9 @@ bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb,
 {
        struct ethhdr *ethhdr;
        struct batadv_bla_claim search_claim, *claim = NULL;
+       struct batadv_bla_backbone_gw *backbone_gw;
        struct batadv_hard_iface *primary_if;
+       bool client_roamed;
        bool ret = false;
 
        primary_if = batadv_primary_if_get_selected(bat_priv);
@@ -1889,8 +1948,12 @@ bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb,
                goto allow;
 
        /* check if we are responsible. */
-       if (batadv_compare_eth(claim->backbone_gw->orig,
-                              primary_if->net_dev->dev_addr)) {
+       backbone_gw = batadv_bla_claim_get_backbone_gw(claim);
+       client_roamed = batadv_compare_eth(backbone_gw->orig,
+                                          primary_if->net_dev->dev_addr);
+       batadv_backbone_gw_put(backbone_gw);
+
+       if (client_roamed) {
                /* if yes, the client has roamed and we have
                 * to unclaim it.
                 */
@@ -1938,6 +2001,7 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset)
        struct net_device *net_dev = (struct net_device *)seq->private;
        struct batadv_priv *bat_priv = netdev_priv(net_dev);
        struct batadv_hashtable *hash = bat_priv->bla.claim_hash;
+       struct batadv_bla_backbone_gw *backbone_gw;
        struct batadv_bla_claim *claim;
        struct batadv_hard_iface *primary_if;
        struct hlist_head *head;
@@ -1962,17 +2026,21 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset)
 
                rcu_read_lock();
                hlist_for_each_entry_rcu(claim, head, hash_entry) {
-                       is_own = batadv_compare_eth(claim->backbone_gw->orig,
+                       backbone_gw = batadv_bla_claim_get_backbone_gw(claim);
+
+                       is_own = batadv_compare_eth(backbone_gw->orig,
                                                    primary_addr);
 
-                       spin_lock_bh(&claim->backbone_gw->crc_lock);
-                       backbone_crc = claim->backbone_gw->crc;
-                       spin_unlock_bh(&claim->backbone_gw->crc_lock);
+                       spin_lock_bh(&backbone_gw->crc_lock);
+                       backbone_crc = backbone_gw->crc;
+                       spin_unlock_bh(&backbone_gw->crc_lock);
                        seq_printf(seq, " * %pM on %5d by %pM [%c] (%#.4x)\n",
                                   claim->addr, BATADV_PRINT_VID(claim->vid),
-                                  claim->backbone_gw->orig,
+                                  backbone_gw->orig,
                                   (is_own ? 'x' : ' '),
                                   backbone_crc);
+
+                       batadv_backbone_gw_put(backbone_gw);
                }
                rcu_read_unlock();
        }
index 278800a99c69458c083da635bd39bcd19e788842..aee3b39914717390ed4fe2899a781b6e7625451f 100644 (file)
@@ -1009,9 +1009,12 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
                if (!skb_new)
                        goto out;
 
-               if (vid & BATADV_VLAN_HAS_TAG)
+               if (vid & BATADV_VLAN_HAS_TAG) {
                        skb_new = vlan_insert_tag(skb_new, htons(ETH_P_8021Q),
                                                  vid & VLAN_VID_MASK);
+                       if (!skb_new)
+                               goto out;
+               }
 
                skb_reset_mac_header(skb_new);
                skb_new->protocol = eth_type_trans(skb_new,
@@ -1089,9 +1092,12 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
         */
        skb_reset_mac_header(skb_new);
 
-       if (vid & BATADV_VLAN_HAS_TAG)
+       if (vid & BATADV_VLAN_HAS_TAG) {
                skb_new = vlan_insert_tag(skb_new, htons(ETH_P_8021Q),
                                          vid & VLAN_VID_MASK);
+               if (!skb_new)
+                       goto out;
+       }
 
        /* To preserve backwards compatibility, the node has choose the outgoing
         * format based on the incoming request packet type. The assumption is
index 7f51bc2c06eb7699492c41f2fe46fe85b26147a2..ab8c4f9738fe2206f878ed9f0dfda8803455ec5b 100644 (file)
@@ -765,6 +765,8 @@ static void batadv_orig_node_release(struct kref *ref)
        struct batadv_neigh_node *neigh_node;
        struct batadv_orig_node *orig_node;
        struct batadv_orig_ifinfo *orig_ifinfo;
+       struct batadv_orig_node_vlan *vlan;
+       struct batadv_orig_ifinfo *last_candidate;
 
        orig_node = container_of(ref, struct batadv_orig_node, refcount);
 
@@ -782,8 +784,21 @@ static void batadv_orig_node_release(struct kref *ref)
                hlist_del_rcu(&orig_ifinfo->list);
                batadv_orig_ifinfo_put(orig_ifinfo);
        }
+
+       last_candidate = orig_node->last_bonding_candidate;
+       orig_node->last_bonding_candidate = NULL;
        spin_unlock_bh(&orig_node->neigh_list_lock);
 
+       if (last_candidate)
+               batadv_orig_ifinfo_put(last_candidate);
+
+       spin_lock_bh(&orig_node->vlan_list_lock);
+       hlist_for_each_entry_safe(vlan, node_tmp, &orig_node->vlan_list, list) {
+               hlist_del_rcu(&vlan->list);
+               batadv_orig_node_vlan_put(vlan);
+       }
+       spin_unlock_bh(&orig_node->vlan_list_lock);
+
        /* Free nc_nodes */
        batadv_nc_purge_orig(orig_node->bat_priv, orig_node, NULL);
 
index e3857ed4057f55d2f90ff72ed7cb8a14532d6fe2..bfac086b4d015053f88dee70e63bebd1ae9b3c56 100644 (file)
@@ -374,6 +374,7 @@ int batadv_recv_icmp_packet(struct sk_buff *skb,
                if (skb_cow(skb, ETH_HLEN) < 0)
                        goto out;
 
+               ethhdr = eth_hdr(skb);
                icmph = (struct batadv_icmp_header *)skb->data;
                icmp_packet_rr = (struct batadv_icmp_packet_rr *)icmph;
                if (icmp_packet_rr->rr_cur >= BATADV_RR_LEN)
@@ -454,6 +455,29 @@ static int batadv_check_unicast_packet(struct batadv_priv *bat_priv,
        return 0;
 }
 
+/**
+ * batadv_last_bonding_replace - Replace last_bonding_candidate of orig_node
+ * @orig_node: originator node whose bonding candidates should be replaced
+ * @new_candidate: new bonding candidate or NULL
+ */
+static void
+batadv_last_bonding_replace(struct batadv_orig_node *orig_node,
+                           struct batadv_orig_ifinfo *new_candidate)
+{
+       struct batadv_orig_ifinfo *old_candidate;
+
+       spin_lock_bh(&orig_node->neigh_list_lock);
+       old_candidate = orig_node->last_bonding_candidate;
+
+       if (new_candidate)
+               kref_get(&new_candidate->refcount);
+       orig_node->last_bonding_candidate = new_candidate;
+       spin_unlock_bh(&orig_node->neigh_list_lock);
+
+       if (old_candidate)
+               batadv_orig_ifinfo_put(old_candidate);
+}
+
 /**
  * batadv_find_router - find a suitable router for this originator
  * @bat_priv: the bat priv with all the soft interface information
@@ -561,10 +585,6 @@ batadv_find_router(struct batadv_priv *bat_priv,
        }
        rcu_read_unlock();
 
-       /* last_bonding_candidate is reset below, remove the old reference. */
-       if (orig_node->last_bonding_candidate)
-               batadv_orig_ifinfo_put(orig_node->last_bonding_candidate);
-
        /* After finding candidates, handle the three cases:
         * 1) there is a next candidate, use that
         * 2) there is no next candidate, use the first of the list
@@ -573,21 +593,28 @@ batadv_find_router(struct batadv_priv *bat_priv,
        if (next_candidate) {
                batadv_neigh_node_put(router);
 
-               /* remove references to first candidate, we don't need it. */
-               if (first_candidate) {
-                       batadv_neigh_node_put(first_candidate_router);
-                       batadv_orig_ifinfo_put(first_candidate);
-               }
+               kref_get(&next_candidate_router->refcount);
                router = next_candidate_router;
-               orig_node->last_bonding_candidate = next_candidate;
+               batadv_last_bonding_replace(orig_node, next_candidate);
        } else if (first_candidate) {
                batadv_neigh_node_put(router);
 
-               /* refcounting has already been done in the loop above. */
+               kref_get(&first_candidate_router->refcount);
                router = first_candidate_router;
-               orig_node->last_bonding_candidate = first_candidate;
+               batadv_last_bonding_replace(orig_node, first_candidate);
        } else {
-               orig_node->last_bonding_candidate = NULL;
+               batadv_last_bonding_replace(orig_node, NULL);
+       }
+
+       /* cleanup of candidates */
+       if (first_candidate) {
+               batadv_neigh_node_put(first_candidate_router);
+               batadv_orig_ifinfo_put(first_candidate);
+       }
+
+       if (next_candidate) {
+               batadv_neigh_node_put(next_candidate_router);
+               batadv_orig_ifinfo_put(next_candidate);
        }
 
        return router;
index f2f125684ed9c199e1c4f7f7b3b6109b3b9a51c2..010397650fa5b7c2c4e4912dc680440dbaf7e21d 100644 (file)
@@ -424,8 +424,8 @@ int batadv_send_skb_via_gw(struct batadv_priv *bat_priv, struct sk_buff *skb,
        struct batadv_orig_node *orig_node;
 
        orig_node = batadv_gw_get_selected_orig(bat_priv);
-       return batadv_send_skb_unicast(bat_priv, skb, BATADV_UNICAST, 0,
-                                      orig_node, vid);
+       return batadv_send_skb_unicast(bat_priv, skb, BATADV_UNICAST_4ADDR,
+                                      BATADV_P_DATA, orig_node, vid);
 }
 
 void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface)
index 343d2c90439928cfd986654de265eeb10ca9a10a..287a3879ed7e38dfd0f0d3f79e2cb2580ea84276 100644 (file)
@@ -1033,7 +1033,9 @@ void batadv_softif_destroy_sysfs(struct net_device *soft_iface)
 static void batadv_softif_destroy_netlink(struct net_device *soft_iface,
                                          struct list_head *head)
 {
+       struct batadv_priv *bat_priv = netdev_priv(soft_iface);
        struct batadv_hard_iface *hard_iface;
+       struct batadv_softif_vlan *vlan;
 
        list_for_each_entry(hard_iface, &batadv_hardif_list, list) {
                if (hard_iface->soft_iface == soft_iface)
@@ -1041,6 +1043,13 @@ static void batadv_softif_destroy_netlink(struct net_device *soft_iface,
                                                        BATADV_IF_CLEANUP_KEEP);
        }
 
+       /* destroy the "untagged" VLAN */
+       vlan = batadv_softif_vlan_get(bat_priv, BATADV_NO_FLAGS);
+       if (vlan) {
+               batadv_softif_destroy_vlan(bat_priv, vlan);
+               batadv_softif_vlan_put(vlan);
+       }
+
        batadv_sysfs_del_meshif(soft_iface);
        unregister_netdevice_queue(soft_iface, head);
 }
index feaf492b01ca011b221fbfb74bba295da794ac15..57ec87f37050d98faefc37e989024a572d26b421 100644 (file)
@@ -650,8 +650,10 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr,
 
        /* increase the refcounter of the related vlan */
        vlan = batadv_softif_vlan_get(bat_priv, vid);
-       if (WARN(!vlan, "adding TT local entry %pM to non-existent VLAN %d",
-                addr, BATADV_PRINT_VID(vid))) {
+       if (!vlan) {
+               net_ratelimited_function(batadv_info, soft_iface,
+                                        "adding TT local entry %pM to non-existent VLAN %d\n",
+                                        addr, BATADV_PRINT_VID(vid));
                kfree(tt_local);
                tt_local = NULL;
                goto out;
@@ -691,7 +693,6 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr,
        if (unlikely(hash_added != 0)) {
                /* remove the reference for the hash */
                batadv_tt_local_entry_put(tt_local);
-               batadv_softif_vlan_put(vlan);
                goto out;
        }
 
@@ -2269,6 +2270,29 @@ static u32 batadv_tt_local_crc(struct batadv_priv *bat_priv,
        return crc;
 }
 
+/**
+ * batadv_tt_req_node_release - free tt_req node entry
+ * @ref: kref pointer of the tt req_node entry
+ */
+static void batadv_tt_req_node_release(struct kref *ref)
+{
+       struct batadv_tt_req_node *tt_req_node;
+
+       tt_req_node = container_of(ref, struct batadv_tt_req_node, refcount);
+
+       kfree(tt_req_node);
+}
+
+/**
+ * batadv_tt_req_node_put - decrement the tt_req_node refcounter and
+ *  possibly release it
+ * @tt_req_node: tt_req_node to be free'd
+ */
+static void batadv_tt_req_node_put(struct batadv_tt_req_node *tt_req_node)
+{
+       kref_put(&tt_req_node->refcount, batadv_tt_req_node_release);
+}
+
 static void batadv_tt_req_list_free(struct batadv_priv *bat_priv)
 {
        struct batadv_tt_req_node *node;
@@ -2278,7 +2302,7 @@ static void batadv_tt_req_list_free(struct batadv_priv *bat_priv)
 
        hlist_for_each_entry_safe(node, safe, &bat_priv->tt.req_list, list) {
                hlist_del_init(&node->list);
-               kfree(node);
+               batadv_tt_req_node_put(node);
        }
 
        spin_unlock_bh(&bat_priv->tt.req_list_lock);
@@ -2315,7 +2339,7 @@ static void batadv_tt_req_purge(struct batadv_priv *bat_priv)
                if (batadv_has_timed_out(node->issued_at,
                                         BATADV_TT_REQUEST_TIMEOUT)) {
                        hlist_del_init(&node->list);
-                       kfree(node);
+                       batadv_tt_req_node_put(node);
                }
        }
        spin_unlock_bh(&bat_priv->tt.req_list_lock);
@@ -2347,9 +2371,11 @@ batadv_tt_req_node_new(struct batadv_priv *bat_priv,
        if (!tt_req_node)
                goto unlock;
 
+       kref_init(&tt_req_node->refcount);
        ether_addr_copy(tt_req_node->addr, orig_node->orig);
        tt_req_node->issued_at = jiffies;
 
+       kref_get(&tt_req_node->refcount);
        hlist_add_head(&tt_req_node->list, &bat_priv->tt.req_list);
 unlock:
        spin_unlock_bh(&bat_priv->tt.req_list_lock);
@@ -2613,13 +2639,19 @@ static bool batadv_send_tt_request(struct batadv_priv *bat_priv,
 out:
        if (primary_if)
                batadv_hardif_put(primary_if);
+
        if (ret && tt_req_node) {
                spin_lock_bh(&bat_priv->tt.req_list_lock);
-               /* hlist_del_init() verifies tt_req_node still is in the list */
-               hlist_del_init(&tt_req_node->list);
+               if (!hlist_unhashed(&tt_req_node->list)) {
+                       hlist_del_init(&tt_req_node->list);
+                       batadv_tt_req_node_put(tt_req_node);
+               }
                spin_unlock_bh(&bat_priv->tt.req_list_lock);
-               kfree(tt_req_node);
        }
+
+       if (tt_req_node)
+               batadv_tt_req_node_put(tt_req_node);
+
        kfree(tvlv_tt_data);
        return ret;
 }
@@ -3055,7 +3087,7 @@ static void batadv_handle_tt_response(struct batadv_priv *bat_priv,
                if (!batadv_compare_eth(node->addr, resp_src))
                        continue;
                hlist_del_init(&node->list);
-               kfree(node);
+               batadv_tt_req_node_put(node);
        }
 
        spin_unlock_bh(&bat_priv->tt.req_list_lock);
index 6a577f4f8ba77ce1b2b923c55deada8a050802e8..74d865a4df464f03a54f1d7f9f87ad5a095ad088 100644 (file)
@@ -330,7 +330,9 @@ struct batadv_orig_node {
        DECLARE_BITMAP(bcast_bits, BATADV_TQ_LOCAL_WINDOW_SIZE);
        u32 last_bcast_seqno;
        struct hlist_head neigh_list;
-       /* neigh_list_lock protects: neigh_list and router */
+       /* neigh_list_lock protects: neigh_list, ifinfo_list,
+        * last_bonding_candidate and router
+        */
        spinlock_t neigh_list_lock;
        struct hlist_node hash_entry;
        struct batadv_priv *bat_priv;
@@ -1042,6 +1044,7 @@ struct batadv_bla_backbone_gw {
  * @addr: mac address of claimed non-mesh client
  * @vid: vlan id this client was detected on
  * @backbone_gw: pointer to backbone gw claiming this client
+ * @backbone_lock: lock protecting backbone_gw pointer
  * @lasttime: last time we heard of claim (locals only)
  * @hash_entry: hlist node for batadv_priv_bla::claim_hash
  * @refcount: number of contexts the object is used
@@ -1051,6 +1054,7 @@ struct batadv_bla_claim {
        u8 addr[ETH_ALEN];
        unsigned short vid;
        struct batadv_bla_backbone_gw *backbone_gw;
+       spinlock_t backbone_lock; /* protects backbone_gw */
        unsigned long lasttime;
        struct hlist_node hash_entry;
        struct rcu_head rcu;
@@ -1137,11 +1141,13 @@ struct batadv_tt_change_node {
  * struct batadv_tt_req_node - data to keep track of the tt requests in flight
  * @addr: mac address address of the originator this request was sent to
  * @issued_at: timestamp used for purging stale tt requests
+ * @refcount: number of contexts the object is used by
  * @list: list node for batadv_priv_tt::req_list
  */
 struct batadv_tt_req_node {
        u8 addr[ETH_ALEN];
        unsigned long issued_at;
+       struct kref refcount;
        struct hlist_node list;
 };
 
index 16079772222860977264133fa49bb0cf33b9b0d5..43d2cd862bc260f81c3ea51c1b3106984db45623 100644 (file)
@@ -213,8 +213,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
 }
 EXPORT_SYMBOL_GPL(br_handle_frame_finish);
 
-/* note: already called with rcu_read_lock */
-static int br_handle_local_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
+static void __br_handle_local_finish(struct sk_buff *skb)
 {
        struct net_bridge_port *p = br_port_get_rcu(skb->dev);
        u16 vid = 0;
@@ -222,6 +221,14 @@ static int br_handle_local_finish(struct net *net, struct sock *sk, struct sk_bu
        /* check if vlan is allowed, to avoid spoofing */
        if (p->flags & BR_LEARNING && br_should_learn(p, skb, &vid))
                br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid, false);
+}
+
+/* note: already called with rcu_read_lock */
+static int br_handle_local_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
+{
+       struct net_bridge_port *p = br_port_get_rcu(skb->dev);
+
+       __br_handle_local_finish(skb);
 
        BR_INPUT_SKB_CB(skb)->brdev = p->br->dev;
        br_pass_frame_up(skb);
@@ -274,7 +281,9 @@ rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
                        if (p->br->stp_enabled == BR_NO_STP ||
                            fwd_mask & (1u << dest[5]))
                                goto forward;
-                       break;
+                       *pskb = skb;
+                       __br_handle_local_finish(skb);
+                       return RX_HANDLER_PASS;
 
                case 0x01:      /* IEEE MAC (Pause) */
                        goto drop;
index 6852f3c7009c2b1cc2cbfa9e08b6b15104b4d12b..43844144c9c4f17a11252e3b1cc1ef4162d7ddb7 100644 (file)
@@ -464,8 +464,11 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br,
        if (ipv6_dev_get_saddr(dev_net(br->dev), br->dev, &ip6h->daddr, 0,
                               &ip6h->saddr)) {
                kfree_skb(skb);
+               br->has_ipv6_addr = 0;
                return NULL;
        }
+
+       br->has_ipv6_addr = 1;
        ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest);
 
        hopopt = (u8 *)(ip6h + 1);
@@ -1745,6 +1748,7 @@ void br_multicast_init(struct net_bridge *br)
        br->ip6_other_query.delay_time = 0;
        br->ip6_querier.port = NULL;
 #endif
+       br->has_ipv6_addr = 1;
 
        spin_lock_init(&br->multicast_lock);
        setup_timer(&br->multicast_router_timer,
index 2d25979273a6f57378da645460d9d6c2a0d91e5c..77e7f69bf80d4ca8e31e09b5b07230bca1abf170 100644 (file)
@@ -700,7 +700,7 @@ static int
 br_nf_ip_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
                  int (*output)(struct net *, struct sock *, struct sk_buff *))
 {
-       unsigned int mtu = ip_skb_dst_mtu(skb);
+       unsigned int mtu = ip_skb_dst_mtu(sk, skb);
        struct iphdr *iph = ip_hdr(skb);
 
        if (unlikely(((iph->frag_off & htons(IP_DF)) && !skb->ignore_df) ||
index a5343c7232bfbf37ca95580a70a740950354e691..85e89f693589d1a7e712d9165cdac8147e0bf02c 100644 (file)
@@ -1273,7 +1273,7 @@ static int br_fill_linkxstats(struct sk_buff *skb, const struct net_device *dev,
                struct bridge_vlan_xstats vxi;
                struct br_vlan_stats stats;
 
-               if (vl_idx++ < *prividx)
+               if (++vl_idx < *prividx)
                        continue;
                memset(&vxi, 0, sizeof(vxi));
                vxi.vid = v->vid;
index c7fb5d7a7218c2c286160874fc30278108083ca6..52edecf3c294cde218e79e90bf86c7595578b062 100644 (file)
@@ -314,6 +314,7 @@ struct net_bridge
        u8                              multicast_disabled:1;
        u8                              multicast_querier:1;
        u8                              multicast_query_use_ifaddr:1;
+       u8                              has_ipv6_addr:1;
 
        u32                             hash_elasticity;
        u32                             hash_max;
@@ -588,10 +589,22 @@ static inline bool br_multicast_is_router(struct net_bridge *br)
 
 static inline bool
 __br_multicast_querier_exists(struct net_bridge *br,
-                             struct bridge_mcast_other_query *querier)
+                               struct bridge_mcast_other_query *querier,
+                               const bool is_ipv6)
 {
+       bool own_querier_enabled;
+
+       if (br->multicast_querier) {
+               if (is_ipv6 && !br->has_ipv6_addr)
+                       own_querier_enabled = false;
+               else
+                       own_querier_enabled = true;
+       } else {
+               own_querier_enabled = false;
+       }
+
        return time_is_before_jiffies(querier->delay_time) &&
-              (br->multicast_querier || timer_pending(&querier->timer));
+              (own_querier_enabled || timer_pending(&querier->timer));
 }
 
 static inline bool br_multicast_querier_exists(struct net_bridge *br,
@@ -599,10 +612,12 @@ static inline bool br_multicast_querier_exists(struct net_bridge *br,
 {
        switch (eth->h_proto) {
        case (htons(ETH_P_IP)):
-               return __br_multicast_querier_exists(br, &br->ip4_other_query);
+               return __br_multicast_querier_exists(br,
+                       &br->ip4_other_query, false);
 #if IS_ENABLED(CONFIG_IPV6)
        case (htons(ETH_P_IPV6)):
-               return __br_multicast_querier_exists(br, &br->ip6_other_query);
+               return __br_multicast_querier_exists(br,
+                       &br->ip6_other_query, true);
 #endif
        default:
                return false;
index 03062bb763b34cc64eb7c0b38c042801e0be0b1c..7e480bf75bcf5bfd69bd89cdccf29fc082a15e16 100644 (file)
@@ -1260,6 +1260,115 @@ struct ceph_osdmap *ceph_osdmap_decode(void **p, void *end)
        return map;
 }
 
+/*
+ * Encoding order is (new_up_client, new_state, new_weight).  Need to
+ * apply in the (new_weight, new_state, new_up_client) order, because
+ * an incremental map may look like e.g.
+ *
+ *     new_up_client: { osd=6, addr=... } # set osd_state and addr
+ *     new_state: { osd=6, xorstate=EXISTS } # clear osd_state
+ */
+static int decode_new_up_state_weight(void **p, void *end,
+                                     struct ceph_osdmap *map)
+{
+       void *new_up_client;
+       void *new_state;
+       void *new_weight_end;
+       u32 len;
+
+       new_up_client = *p;
+       ceph_decode_32_safe(p, end, len, e_inval);
+       len *= sizeof(u32) + sizeof(struct ceph_entity_addr);
+       ceph_decode_need(p, end, len, e_inval);
+       *p += len;
+
+       new_state = *p;
+       ceph_decode_32_safe(p, end, len, e_inval);
+       len *= sizeof(u32) + sizeof(u8);
+       ceph_decode_need(p, end, len, e_inval);
+       *p += len;
+
+       /* new_weight */
+       ceph_decode_32_safe(p, end, len, e_inval);
+       while (len--) {
+               s32 osd;
+               u32 w;
+
+               ceph_decode_need(p, end, 2*sizeof(u32), e_inval);
+               osd = ceph_decode_32(p);
+               w = ceph_decode_32(p);
+               BUG_ON(osd >= map->max_osd);
+               pr_info("osd%d weight 0x%x %s\n", osd, w,
+                    w == CEPH_OSD_IN ? "(in)" :
+                    (w == CEPH_OSD_OUT ? "(out)" : ""));
+               map->osd_weight[osd] = w;
+
+               /*
+                * If we are marking in, set the EXISTS, and clear the
+                * AUTOOUT and NEW bits.
+                */
+               if (w) {
+                       map->osd_state[osd] |= CEPH_OSD_EXISTS;
+                       map->osd_state[osd] &= ~(CEPH_OSD_AUTOOUT |
+                                                CEPH_OSD_NEW);
+               }
+       }
+       new_weight_end = *p;
+
+       /* new_state (up/down) */
+       *p = new_state;
+       len = ceph_decode_32(p);
+       while (len--) {
+               s32 osd;
+               u8 xorstate;
+               int ret;
+
+               osd = ceph_decode_32(p);
+               xorstate = ceph_decode_8(p);
+               if (xorstate == 0)
+                       xorstate = CEPH_OSD_UP;
+               BUG_ON(osd >= map->max_osd);
+               if ((map->osd_state[osd] & CEPH_OSD_UP) &&
+                   (xorstate & CEPH_OSD_UP))
+                       pr_info("osd%d down\n", osd);
+               if ((map->osd_state[osd] & CEPH_OSD_EXISTS) &&
+                   (xorstate & CEPH_OSD_EXISTS)) {
+                       pr_info("osd%d does not exist\n", osd);
+                       map->osd_weight[osd] = CEPH_OSD_IN;
+                       ret = set_primary_affinity(map, osd,
+                                                  CEPH_OSD_DEFAULT_PRIMARY_AFFINITY);
+                       if (ret)
+                               return ret;
+                       memset(map->osd_addr + osd, 0, sizeof(*map->osd_addr));
+                       map->osd_state[osd] = 0;
+               } else {
+                       map->osd_state[osd] ^= xorstate;
+               }
+       }
+
+       /* new_up_client */
+       *p = new_up_client;
+       len = ceph_decode_32(p);
+       while (len--) {
+               s32 osd;
+               struct ceph_entity_addr addr;
+
+               osd = ceph_decode_32(p);
+               ceph_decode_copy(p, &addr, sizeof(addr));
+               ceph_decode_addr(&addr);
+               BUG_ON(osd >= map->max_osd);
+               pr_info("osd%d up\n", osd);
+               map->osd_state[osd] |= CEPH_OSD_EXISTS | CEPH_OSD_UP;
+               map->osd_addr[osd] = addr;
+       }
+
+       *p = new_weight_end;
+       return 0;
+
+e_inval:
+       return -EINVAL;
+}
+
 /*
  * decode and apply an incremental map update.
  */
@@ -1358,49 +1467,10 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end,
                        __remove_pg_pool(&map->pg_pools, pi);
        }
 
-       /* new_up */
-       ceph_decode_32_safe(p, end, len, e_inval);
-       while (len--) {
-               u32 osd;
-               struct ceph_entity_addr addr;
-               ceph_decode_32_safe(p, end, osd, e_inval);
-               ceph_decode_copy_safe(p, end, &addr, sizeof(addr), e_inval);
-               ceph_decode_addr(&addr);
-               pr_info("osd%d up\n", osd);
-               BUG_ON(osd >= map->max_osd);
-               map->osd_state[osd] |= CEPH_OSD_UP | CEPH_OSD_EXISTS;
-               map->osd_addr[osd] = addr;
-       }
-
-       /* new_state */
-       ceph_decode_32_safe(p, end, len, e_inval);
-       while (len--) {
-               u32 osd;
-               u8 xorstate;
-               ceph_decode_32_safe(p, end, osd, e_inval);
-               xorstate = **(u8 **)p;
-               (*p)++;  /* clean flag */
-               if (xorstate == 0)
-                       xorstate = CEPH_OSD_UP;
-               if (xorstate & CEPH_OSD_UP)
-                       pr_info("osd%d down\n", osd);
-               if (osd < map->max_osd)
-                       map->osd_state[osd] ^= xorstate;
-       }
-
-       /* new_weight */
-       ceph_decode_32_safe(p, end, len, e_inval);
-       while (len--) {
-               u32 osd, off;
-               ceph_decode_need(p, end, sizeof(u32)*2, e_inval);
-               osd = ceph_decode_32(p);
-               off = ceph_decode_32(p);
-               pr_info("osd%d weight 0x%x %s\n", osd, off,
-                    off == CEPH_OSD_IN ? "(in)" :
-                    (off == CEPH_OSD_OUT ? "(out)" : ""));
-               if (osd < map->max_osd)
-                       map->osd_weight[osd] = off;
-       }
+       /* new_up_client, new_state, new_weight */
+       err = decode_new_up_state_weight(p, end, map);
+       if (err)
+               goto bad;
 
        /* new_pg_temp */
        err = decode_new_pg_temp(p, end, map);
index 68adb5f52110d85fead496a8a76a7248ae8cefae..e759d90e8cef037ec117a7e70941c3be050e3103 100644 (file)
 #include <net/sock_reuseport.h>
 
 /**
- *     sk_filter - run a packet through a socket filter
+ *     sk_filter_trim_cap - run a packet through a socket filter
  *     @sk: sock associated with &sk_buff
  *     @skb: buffer to filter
+ *     @cap: limit on how short the eBPF program may trim the packet
  *
  * Run the eBPF program and then cut skb->data to correct size returned by
  * the program. If pkt_len is 0 we toss packet. If skb->len is smaller
@@ -64,7 +65,7 @@
  * be accepted or -EPERM if the packet should be tossed.
  *
  */
-int sk_filter(struct sock *sk, struct sk_buff *skb)
+int sk_filter_trim_cap(struct sock *sk, struct sk_buff *skb, unsigned int cap)
 {
        int err;
        struct sk_filter *filter;
@@ -85,14 +86,13 @@ int sk_filter(struct sock *sk, struct sk_buff *skb)
        filter = rcu_dereference(sk->sk_filter);
        if (filter) {
                unsigned int pkt_len = bpf_prog_run_save_cb(filter->prog, skb);
-
-               err = pkt_len ? pskb_trim(skb, pkt_len) : -EPERM;
+               err = pkt_len ? pskb_trim(skb, max(cap, pkt_len)) : -EPERM;
        }
        rcu_read_unlock();
 
        return err;
 }
-EXPORT_SYMBOL(sk_filter);
+EXPORT_SYMBOL(sk_filter_trim_cap);
 
 static u64 __skb_get_pay_offset(u64 ctx, u64 a, u64 x, u64 r4, u64 r5)
 {
@@ -2085,7 +2085,8 @@ static bool __is_valid_access(int off, int size, enum bpf_access_type type)
 }
 
 static bool sk_filter_is_valid_access(int off, int size,
-                                     enum bpf_access_type type)
+                                     enum bpf_access_type type,
+                                     enum bpf_reg_type *reg_type)
 {
        switch (off) {
        case offsetof(struct __sk_buff, tc_classid):
@@ -2108,7 +2109,8 @@ static bool sk_filter_is_valid_access(int off, int size,
 }
 
 static bool tc_cls_act_is_valid_access(int off, int size,
-                                      enum bpf_access_type type)
+                                      enum bpf_access_type type,
+                                      enum bpf_reg_type *reg_type)
 {
        if (type == BPF_WRITE) {
                switch (off) {
@@ -2123,6 +2125,16 @@ static bool tc_cls_act_is_valid_access(int off, int size,
                        return false;
                }
        }
+
+       switch (off) {
+       case offsetof(struct __sk_buff, data):
+               *reg_type = PTR_TO_PACKET;
+               break;
+       case offsetof(struct __sk_buff, data_end):
+               *reg_type = PTR_TO_PACKET_END;
+               break;
+       }
+
        return __is_valid_access(off, size, type);
 }
 
index a669dea146c61b2f7f5b1feaf46512b135ee28f7..61ad43f61c5edbffa48f4983d8bf1a7472a66cdc 100644 (file)
@@ -651,6 +651,23 @@ void make_flow_keys_digest(struct flow_keys_digest *digest,
 }
 EXPORT_SYMBOL(make_flow_keys_digest);
 
+static struct flow_dissector flow_keys_dissector_symmetric __read_mostly;
+
+u32 __skb_get_hash_symmetric(struct sk_buff *skb)
+{
+       struct flow_keys keys;
+
+       __flow_hash_secret_init();
+
+       memset(&keys, 0, sizeof(keys));
+       __skb_flow_dissect(skb, &flow_keys_dissector_symmetric, &keys,
+                          NULL, 0, 0, 0,
+                          FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL);
+
+       return __flow_hash_from_keys(&keys, hashrnd);
+}
+EXPORT_SYMBOL_GPL(__skb_get_hash_symmetric);
+
 /**
  * __skb_get_hash: calculate a flow hash
  * @skb: sk_buff to calculate flow hash from
@@ -868,6 +885,29 @@ static const struct flow_dissector_key flow_keys_dissector_keys[] = {
        },
 };
 
+static const struct flow_dissector_key flow_keys_dissector_symmetric_keys[] = {
+       {
+               .key_id = FLOW_DISSECTOR_KEY_CONTROL,
+               .offset = offsetof(struct flow_keys, control),
+       },
+       {
+               .key_id = FLOW_DISSECTOR_KEY_BASIC,
+               .offset = offsetof(struct flow_keys, basic),
+       },
+       {
+               .key_id = FLOW_DISSECTOR_KEY_IPV4_ADDRS,
+               .offset = offsetof(struct flow_keys, addrs.v4addrs),
+       },
+       {
+               .key_id = FLOW_DISSECTOR_KEY_IPV6_ADDRS,
+               .offset = offsetof(struct flow_keys, addrs.v6addrs),
+       },
+       {
+               .key_id = FLOW_DISSECTOR_KEY_PORTS,
+               .offset = offsetof(struct flow_keys, ports),
+       },
+};
+
 static const struct flow_dissector_key flow_keys_buf_dissector_keys[] = {
        {
                .key_id = FLOW_DISSECTOR_KEY_CONTROL,
@@ -889,6 +929,9 @@ static int __init init_default_flow_dissectors(void)
        skb_flow_dissector_init(&flow_keys_dissector,
                                flow_keys_dissector_keys,
                                ARRAY_SIZE(flow_keys_dissector_keys));
+       skb_flow_dissector_init(&flow_keys_dissector_symmetric,
+                               flow_keys_dissector_symmetric_keys,
+                               ARRAY_SIZE(flow_keys_dissector_symmetric_keys));
        skb_flow_dissector_init(&flow_keys_buf_dissector,
                                flow_keys_buf_dissector_keys,
                                ARRAY_SIZE(flow_keys_buf_dissector_keys));
index 29dd8cc22bbf87a05d555ff16d62e6dee0b536d4..510cd62fcb9963f92591e03b6c4d40502f2f37bf 100644 (file)
@@ -2469,13 +2469,17 @@ int neigh_xmit(int index, struct net_device *dev,
                tbl = neigh_tables[index];
                if (!tbl)
                        goto out;
+               rcu_read_lock_bh();
                neigh = __neigh_lookup_noref(tbl, addr, dev);
                if (!neigh)
                        neigh = __neigh_create(tbl, addr, dev, false);
                err = PTR_ERR(neigh);
-               if (IS_ERR(neigh))
+               if (IS_ERR(neigh)) {
+                       rcu_read_unlock_bh();
                        goto out_kfree_skb;
+               }
                err = neigh->output(neigh, skb);
+               rcu_read_unlock_bh();
        }
        else if (index == NEIGH_LINK_TABLE) {
                err = dev_hard_header(skb, dev, ntohs(skb->protocol),
index f2b77e549c03a771909cd9c87c40ec2b7826cd31..eb12d2161fb2b5f9f26ebb8f534bbd6a673b65b1 100644 (file)
@@ -3015,24 +3015,6 @@ int skb_append_pagefrags(struct sk_buff *skb, struct page *page,
 }
 EXPORT_SYMBOL_GPL(skb_append_pagefrags);
 
-/**
- *     skb_push_rcsum - push skb and update receive checksum
- *     @skb: buffer to update
- *     @len: length of data pulled
- *
- *     This function performs an skb_push on the packet and updates
- *     the CHECKSUM_COMPLETE checksum.  It should be used on
- *     receive path processing instead of skb_push unless you know
- *     that the checksum difference is zero (e.g., a valid IP header)
- *     or you are setting ip_summed to CHECKSUM_NONE.
- */
-static unsigned char *skb_push_rcsum(struct sk_buff *skb, unsigned len)
-{
-       skb_push(skb, len);
-       skb_postpush_rcsum(skb, skb->data, len);
-       return skb->data;
-}
-
 /**
  *     skb_pull_rcsum - pull skb and update receive checksum
  *     @skb: buffer to update
index 08bf97eceeb3827d0b237d8c01910e5e0a0f5d6a..25dab8b60223e25ee92dae45a226fce2a6bb5a03 100644 (file)
@@ -452,11 +452,12 @@ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 }
 EXPORT_SYMBOL(sock_queue_rcv_skb);
 
-int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested)
+int __sk_receive_skb(struct sock *sk, struct sk_buff *skb,
+                    const int nested, unsigned int trim_cap)
 {
        int rc = NET_RX_SUCCESS;
 
-       if (sk_filter(sk, skb))
+       if (sk_filter_trim_cap(sk, skb, trim_cap))
                goto discard_and_relse;
 
        skb->dev = NULL;
@@ -492,7 +493,7 @@ int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested)
        kfree_skb(skb);
        goto out;
 }
-EXPORT_SYMBOL(sk_receive_skb);
+EXPORT_SYMBOL(__sk_receive_skb);
 
 struct dst_entry *__sk_dst_check(struct sock *sk, u32 cookie)
 {
@@ -1938,6 +1939,10 @@ int __sock_cmsg_send(struct sock *sk, struct msghdr *msg, struct cmsghdr *cmsg,
                sockc->tsflags &= ~SOF_TIMESTAMPING_TX_RECORD_MASK;
                sockc->tsflags |= tsflags;
                break;
+       /* SCM_RIGHTS and SCM_CREDENTIALS are semantically in SOL_UNIX. */
+       case SCM_RIGHTS:
+       case SCM_CREDENTIALS:
+               break;
        default:
                return -EINVAL;
        }
index 5c7e413a3ae407e67565b48a8bd6f43e3b02de4d..345a3aeb8c7e36449a765298cd6512eab8cfef4b 100644 (file)
@@ -462,7 +462,7 @@ static struct dst_entry* dccp_v4_route_skb(struct net *net, struct sock *sk,
        security_skb_classify_flow(skb, flowi4_to_flowi(&fl4));
        rt = ip_route_output_flow(net, &fl4, sk);
        if (IS_ERR(rt)) {
-               __IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES);
+               IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES);
                return NULL;
        }
 
@@ -527,17 +527,19 @@ static void dccp_v4_ctl_send_reset(const struct sock *sk, struct sk_buff *rxskb)
                                                                 rxiph->daddr);
        skb_dst_set(skb, dst_clone(dst));
 
+       local_bh_disable();
        bh_lock_sock(ctl_sk);
        err = ip_build_and_send_pkt(skb, ctl_sk,
                                    rxiph->daddr, rxiph->saddr, NULL);
        bh_unlock_sock(ctl_sk);
 
        if (net_xmit_eval(err) == 0) {
-               DCCP_INC_STATS(DCCP_MIB_OUTSEGS);
-               DCCP_INC_STATS(DCCP_MIB_OUTRSTS);
+               __DCCP_INC_STATS(DCCP_MIB_OUTSEGS);
+               __DCCP_INC_STATS(DCCP_MIB_OUTRSTS);
        }
+       local_bh_enable();
 out:
-        dst_release(dst);
+       dst_release(dst);
 }
 
 static void dccp_v4_reqsk_destructor(struct request_sock *req)
@@ -866,7 +868,7 @@ static int dccp_v4_rcv(struct sk_buff *skb)
                goto discard_and_relse;
        nf_reset(skb);
 
-       return sk_receive_skb(sk, skb, 1);
+       return __sk_receive_skb(sk, skb, 1, dh->dccph_doff * 4);
 
 no_dccp_socket:
        if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
index d176f4e66369a399f5fe8a440eb864dbac9c7542..3ff137d9471d8420748fd72c8631d67eb57ffb66 100644 (file)
@@ -732,7 +732,7 @@ static int dccp_v6_rcv(struct sk_buff *skb)
        if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
                goto discard_and_relse;
 
-       return sk_receive_skb(sk, skb, 1) ? -1 : 0;
+       return __sk_receive_skb(sk, skb, 1, dh->dccph_doff * 4) ? -1 : 0;
 
 no_dccp_socket:
        if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
index df48034378889eac83c6b262a76a32028202efb4..a796fc7cbc3542972eaa19bbc3d3dc07a428ff1c 100644 (file)
@@ -41,6 +41,7 @@
 #include <net/dn_fib.h>
 #include <net/dn_neigh.h>
 #include <net/dn_dev.h>
+#include <net/nexthop.h>
 
 #define RT_MIN_TABLE 1
 
@@ -150,14 +151,13 @@ static int dn_fib_count_nhs(const struct nlattr *attr)
        struct rtnexthop *nhp = nla_data(attr);
        int nhs = 0, nhlen = nla_len(attr);
 
-       while(nhlen >= (int)sizeof(struct rtnexthop)) {
-               if ((nhlen -= nhp->rtnh_len) < 0)
-                       return 0;
+       while (rtnh_ok(nhp, nhlen)) {
                nhs++;
-               nhp = RTNH_NEXT(nhp);
+               nhp = rtnh_next(nhp, &nhlen);
        }
 
-       return nhs;
+       /* leftover implies invalid nexthop configuration, discard it */
+       return nhlen > 0 ? 0 : nhs;
 }
 
 static int dn_fib_get_nhs(struct dn_fib_info *fi, const struct nlattr *attr,
@@ -167,21 +167,24 @@ static int dn_fib_get_nhs(struct dn_fib_info *fi, const struct nlattr *attr,
        int nhlen = nla_len(attr);
 
        change_nexthops(fi) {
-               int attrlen = nhlen - sizeof(struct rtnexthop);
-               if (attrlen < 0 || (nhlen -= nhp->rtnh_len) < 0)
+               int attrlen;
+
+               if (!rtnh_ok(nhp, nhlen))
                        return -EINVAL;
 
                nh->nh_flags  = (r->rtm_flags&~0xFF) | nhp->rtnh_flags;
                nh->nh_oif    = nhp->rtnh_ifindex;
                nh->nh_weight = nhp->rtnh_hops + 1;
 
-               if (attrlen) {
+               attrlen = rtnh_attrlen(nhp);
+               if (attrlen > 0) {
                        struct nlattr *gw_attr;
 
                        gw_attr = nla_find((struct nlattr *) (nhp + 1), attrlen, RTA_GATEWAY);
                        nh->nh_gw = gw_attr ? nla_get_le16(gw_attr) : 0;
                }
-               nhp = RTNH_NEXT(nhp);
+
+               nhp = rtnh_next(nhp, &nhlen);
        } endfor_nexthops(fi);
 
        return 0;
index 477937465a202ccdf458a808776ea3e90c853b84..d95631d0924899f17549e69801567bed076491b9 100644 (file)
@@ -23,6 +23,11 @@ struct esp_skb_cb {
        void *tmp;
 };
 
+struct esp_output_extra {
+       __be32 seqhi;
+       u32 esphoff;
+};
+
 #define ESP_SKB_CB(__skb) ((struct esp_skb_cb *)&((__skb)->cb[0]))
 
 static u32 esp4_get_mtu(struct xfrm_state *x, int mtu);
@@ -35,11 +40,11 @@ static u32 esp4_get_mtu(struct xfrm_state *x, int mtu);
  *
  * TODO: Use spare space in skb for this where possible.
  */
-static void *esp_alloc_tmp(struct crypto_aead *aead, int nfrags, int seqhilen)
+static void *esp_alloc_tmp(struct crypto_aead *aead, int nfrags, int extralen)
 {
        unsigned int len;
 
-       len = seqhilen;
+       len = extralen;
 
        len += crypto_aead_ivsize(aead);
 
@@ -57,15 +62,16 @@ static void *esp_alloc_tmp(struct crypto_aead *aead, int nfrags, int seqhilen)
        return kmalloc(len, GFP_ATOMIC);
 }
 
-static inline __be32 *esp_tmp_seqhi(void *tmp)
+static inline void *esp_tmp_extra(void *tmp)
 {
-       return PTR_ALIGN((__be32 *)tmp, __alignof__(__be32));
+       return PTR_ALIGN(tmp, __alignof__(struct esp_output_extra));
 }
-static inline u8 *esp_tmp_iv(struct crypto_aead *aead, void *tmp, int seqhilen)
+
+static inline u8 *esp_tmp_iv(struct crypto_aead *aead, void *tmp, int extralen)
 {
        return crypto_aead_ivsize(aead) ?
-              PTR_ALIGN((u8 *)tmp + seqhilen,
-                        crypto_aead_alignmask(aead) + 1) : tmp + seqhilen;
+              PTR_ALIGN((u8 *)tmp + extralen,
+                        crypto_aead_alignmask(aead) + 1) : tmp + extralen;
 }
 
 static inline struct aead_request *esp_tmp_req(struct crypto_aead *aead, u8 *iv)
@@ -99,7 +105,7 @@ static void esp_restore_header(struct sk_buff *skb, unsigned int offset)
 {
        struct ip_esp_hdr *esph = (void *)(skb->data + offset);
        void *tmp = ESP_SKB_CB(skb)->tmp;
-       __be32 *seqhi = esp_tmp_seqhi(tmp);
+       __be32 *seqhi = esp_tmp_extra(tmp);
 
        esph->seq_no = esph->spi;
        esph->spi = *seqhi;
@@ -107,7 +113,11 @@ static void esp_restore_header(struct sk_buff *skb, unsigned int offset)
 
 static void esp_output_restore_header(struct sk_buff *skb)
 {
-       esp_restore_header(skb, skb_transport_offset(skb) - sizeof(__be32));
+       void *tmp = ESP_SKB_CB(skb)->tmp;
+       struct esp_output_extra *extra = esp_tmp_extra(tmp);
+
+       esp_restore_header(skb, skb_transport_offset(skb) + extra->esphoff -
+                               sizeof(__be32));
 }
 
 static void esp_output_done_esn(struct crypto_async_request *base, int err)
@@ -121,6 +131,7 @@ static void esp_output_done_esn(struct crypto_async_request *base, int err)
 static int esp_output(struct xfrm_state *x, struct sk_buff *skb)
 {
        int err;
+       struct esp_output_extra *extra;
        struct ip_esp_hdr *esph;
        struct crypto_aead *aead;
        struct aead_request *req;
@@ -137,8 +148,7 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb)
        int tfclen;
        int nfrags;
        int assoclen;
-       int seqhilen;
-       __be32 *seqhi;
+       int extralen;
        __be64 seqno;
 
        /* skb is pure payload to encrypt */
@@ -166,21 +176,21 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb)
        nfrags = err;
 
        assoclen = sizeof(*esph);
-       seqhilen = 0;
+       extralen = 0;
 
        if (x->props.flags & XFRM_STATE_ESN) {
-               seqhilen += sizeof(__be32);
-               assoclen += seqhilen;
+               extralen += sizeof(*extra);
+               assoclen += sizeof(__be32);
        }
 
-       tmp = esp_alloc_tmp(aead, nfrags, seqhilen);
+       tmp = esp_alloc_tmp(aead, nfrags, extralen);
        if (!tmp) {
                err = -ENOMEM;
                goto error;
        }
 
-       seqhi = esp_tmp_seqhi(tmp);
-       iv = esp_tmp_iv(aead, tmp, seqhilen);
+       extra = esp_tmp_extra(tmp);
+       iv = esp_tmp_iv(aead, tmp, extralen);
        req = esp_tmp_req(aead, iv);
        sg = esp_req_sg(aead, req);
 
@@ -247,8 +257,10 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb)
         * encryption.
         */
        if ((x->props.flags & XFRM_STATE_ESN)) {
-               esph = (void *)(skb_transport_header(skb) - sizeof(__be32));
-               *seqhi = esph->spi;
+               extra->esphoff = (unsigned char *)esph -
+                                skb_transport_header(skb);
+               esph = (struct ip_esp_hdr *)((unsigned char *)esph - 4);
+               extra->seqhi = esph->spi;
                esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.hi);
                aead_request_set_callback(req, 0, esp_output_done_esn, skb);
        }
@@ -445,7 +457,7 @@ static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
                goto out;
 
        ESP_SKB_CB(skb)->tmp = tmp;
-       seqhi = esp_tmp_seqhi(tmp);
+       seqhi = esp_tmp_extra(tmp);
        iv = esp_tmp_iv(aead, tmp, seqhilen);
        req = esp_tmp_req(aead, iv);
        sg = esp_req_sg(aead, req);
index d09173bf95005be87d4d0cbcc754147b3c8e62c9..539fa264e67d71148364c9fc0e694c78fd35e69b 100644 (file)
@@ -479,6 +479,9 @@ static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh,
                if (!rtnh_ok(rtnh, remaining))
                        return -EINVAL;
 
+               if (rtnh->rtnh_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))
+                       return -EINVAL;
+
                nexthop_nh->nh_flags =
                        (cfg->fc_flags & ~0xFF) | rtnh->rtnh_flags;
                nexthop_nh->nh_oif = rtnh->rtnh_ifindex;
@@ -1003,6 +1006,9 @@ struct fib_info *fib_create_info(struct fib_config *cfg)
        if (fib_props[cfg->fc_type].scope > cfg->fc_scope)
                goto err_inval;
 
+       if (cfg->fc_flags & (RTNH_F_DEAD | RTNH_F_LINKDOWN))
+               goto err_inval;
+
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
        if (cfg->fc_mp) {
                nhs = fib_count_nexthops(cfg->fc_mp, cfg->fc_mp_len);
index 4c39f4fd332a3569267c5c1330e692c5f40a49e7..de1d119a4497e7d512cba30043f01bb6609f6f2d 100644 (file)
@@ -62,26 +62,26 @@ EXPORT_SYMBOL_GPL(gre_del_protocol);
 
 /* Fills in tpi and returns header length to be pulled. */
 int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
-                    bool *csum_err, __be16 proto)
+                    bool *csum_err, __be16 proto, int nhs)
 {
        const struct gre_base_hdr *greh;
        __be32 *options;
        int hdr_len;
 
-       if (unlikely(!pskb_may_pull(skb, sizeof(struct gre_base_hdr))))
+       if (unlikely(!pskb_may_pull(skb, nhs + sizeof(struct gre_base_hdr))))
                return -EINVAL;
 
-       greh = (struct gre_base_hdr *)skb_transport_header(skb);
+       greh = (struct gre_base_hdr *)(skb->data + nhs);
        if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING)))
                return -EINVAL;
 
        tpi->flags = gre_flags_to_tnl_flags(greh->flags);
        hdr_len = gre_calc_hlen(tpi->flags);
 
-       if (!pskb_may_pull(skb, hdr_len))
+       if (!pskb_may_pull(skb, nhs + hdr_len))
                return -EINVAL;
 
-       greh = (struct gre_base_hdr *)skb_transport_header(skb);
+       greh = (struct gre_base_hdr *)(skb->data + nhs);
        tpi->proto = greh->protocol;
 
        options = (__be32 *)(greh + 1);
index 4d2025f7ec578b7e3f3fd342e82e734d36395068..1d000af7f5617e32e6dd8ec5e034a87fd449ab06 100644 (file)
 #include <net/gre.h>
 #include <net/dst_metadata.h>
 
-#if IS_ENABLED(CONFIG_IPV6)
-#include <net/ipv6.h>
-#include <net/ip6_fib.h>
-#include <net/ip6_route.h>
-#endif
-
 /*
    Problems & solutions
    --------------------
@@ -217,12 +211,14 @@ static void gre_err(struct sk_buff *skb, u32 info)
         * by themselves???
         */
 
+       const struct iphdr *iph = (struct iphdr *)skb->data;
        const int type = icmp_hdr(skb)->type;
        const int code = icmp_hdr(skb)->code;
        struct tnl_ptk_info tpi;
        bool csum_err = false;
 
-       if (gre_parse_header(skb, &tpi, &csum_err, htons(ETH_P_IP)) < 0) {
+       if (gre_parse_header(skb, &tpi, &csum_err, htons(ETH_P_IP),
+                            iph->ihl * 4) < 0) {
                if (!csum_err)          /* ignore csum errors. */
                        return;
        }
@@ -338,7 +334,7 @@ static int gre_rcv(struct sk_buff *skb)
        }
 #endif
 
-       hdr_len = gre_parse_header(skb, &tpi, &csum_err, htons(ETH_P_IP));
+       hdr_len = gre_parse_header(skb, &tpi, &csum_err, htons(ETH_P_IP), 0);
        if (hdr_len < 0)
                goto drop;
 
@@ -1121,6 +1117,7 @@ struct net_device *gretap_fb_dev_create(struct net *net, const char *name,
 {
        struct nlattr *tb[IFLA_MAX + 1];
        struct net_device *dev;
+       LIST_HEAD(list_kill);
        struct ip_tunnel *t;
        int err;
 
@@ -1136,8 +1133,10 @@ struct net_device *gretap_fb_dev_create(struct net *net, const char *name,
        t->collect_md = true;
 
        err = ipgre_newlink(net, dev, tb, NULL);
-       if (err < 0)
-               goto out;
+       if (err < 0) {
+               free_netdev(dev);
+               return ERR_PTR(err);
+       }
 
        /* openvswitch users expect packet sizes to be unrestricted,
         * so set the largest MTU we can.
@@ -1146,9 +1145,14 @@ struct net_device *gretap_fb_dev_create(struct net *net, const char *name,
        if (err)
                goto out;
 
+       err = rtnl_configure_link(dev, NULL);
+       if (err < 0)
+               goto out;
+
        return dev;
 out:
-       free_netdev(dev);
+       ip_tunnel_dellink(dev, &list_kill);
+       unregister_netdevice_many(&list_kill);
        return ERR_PTR(err);
 }
 EXPORT_SYMBOL_GPL(gretap_fb_dev_create);
index 124bf0a663283502deb03397343160d493a378b1..4bd4921639c3e6415f8899896f72fe1564a68c55 100644 (file)
@@ -271,7 +271,7 @@ static int ip_finish_output(struct net *net, struct sock *sk, struct sk_buff *sk
                return dst_output(net, sk, skb);
        }
 #endif
-       mtu = ip_skb_dst_mtu(skb);
+       mtu = ip_skb_dst_mtu(sk, skb);
        if (skb_is_gso(skb))
                return ip_finish_output_gso(net, sk, skb, mtu);
 
@@ -541,7 +541,7 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
 
        iph = ip_hdr(skb);
 
-       mtu = ip_skb_dst_mtu(skb);
+       mtu = ip_skb_dst_mtu(sk, skb);
        if (IPCB(skb)->frag_max_size && IPCB(skb)->frag_max_size < mtu)
                mtu = IPCB(skb)->frag_max_size;
 
index 2ed9dd2b5f2f5a6ea3ea52208e5ec1424a501fc5..1d71c40eaaf35a74b1cec69d3ffdbd80b675aae9 100644 (file)
@@ -127,7 +127,9 @@ __be32 ic_myaddr = NONE;            /* My IP address */
 static __be32 ic_netmask = NONE;       /* Netmask for local subnet */
 __be32 ic_gateway = NONE;      /* Gateway IP address */
 
-__be32 ic_addrservaddr = NONE; /* IP Address of the IP addresses'server */
+#ifdef IPCONFIG_DYNAMIC
+static __be32 ic_addrservaddr = NONE;  /* IP Address of the IP addresses'server */
+#endif
 
 __be32 ic_servaddr = NONE;     /* Boot server IP address */
 
index 21a38e296fe2da2c4a095c94ab204362d9eacc89..5ad48ec7771007a0c01c8210001ecb17ab012863 100644 (file)
@@ -891,8 +891,10 @@ static struct mfc_cache *ipmr_cache_alloc(void)
 {
        struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL);
 
-       if (c)
+       if (c) {
+               c->mfc_un.res.last_assert = jiffies - MFC_ASSERT_THRESH - 1;
                c->mfc_un.res.minvif = MAXVIFS;
+       }
        return c;
 }
 
index d6c8f4cd080001a527f7c137021cc6a3f3604344..42bf89aaf6a5206cf384068a0a50a3130abe2a4e 100644 (file)
@@ -87,7 +87,7 @@ int sysctl_tcp_adv_win_scale __read_mostly = 1;
 EXPORT_SYMBOL(sysctl_tcp_adv_win_scale);
 
 /* rfc5961 challenge ack rate limiting */
-int sysctl_tcp_challenge_ack_limit = 100;
+int sysctl_tcp_challenge_ack_limit = 1000;
 
 int sysctl_tcp_stdurg __read_mostly;
 int sysctl_tcp_rfc1337 __read_mostly;
@@ -3421,6 +3421,23 @@ static int tcp_ack_update_window(struct sock *sk, const struct sk_buff *skb, u32
        return flag;
 }
 
+static bool __tcp_oow_rate_limited(struct net *net, int mib_idx,
+                                  u32 *last_oow_ack_time)
+{
+       if (*last_oow_ack_time) {
+               s32 elapsed = (s32)(tcp_time_stamp - *last_oow_ack_time);
+
+               if (0 <= elapsed && elapsed < sysctl_tcp_invalid_ratelimit) {
+                       NET_INC_STATS(net, mib_idx);
+                       return true;    /* rate-limited: don't send yet! */
+               }
+       }
+
+       *last_oow_ack_time = tcp_time_stamp;
+
+       return false;   /* not rate-limited: go ahead, send dupack now! */
+}
+
 /* Return true if we're currently rate-limiting out-of-window ACKs and
  * thus shouldn't send a dupack right now. We rate-limit dupacks in
  * response to out-of-window SYNs or ACKs to mitigate ACK loops or DoS
@@ -3434,21 +3451,9 @@ bool tcp_oow_rate_limited(struct net *net, const struct sk_buff *skb,
        /* Data packets without SYNs are not likely part of an ACK loop. */
        if ((TCP_SKB_CB(skb)->seq != TCP_SKB_CB(skb)->end_seq) &&
            !tcp_hdr(skb)->syn)
-               goto not_rate_limited;
-
-       if (*last_oow_ack_time) {
-               s32 elapsed = (s32)(tcp_time_stamp - *last_oow_ack_time);
-
-               if (0 <= elapsed && elapsed < sysctl_tcp_invalid_ratelimit) {
-                       NET_INC_STATS(net, mib_idx);
-                       return true;    /* rate-limited: don't send yet! */
-               }
-       }
-
-       *last_oow_ack_time = tcp_time_stamp;
+               return false;
 
-not_rate_limited:
-       return false;   /* not rate-limited: go ahead, send dupack now! */
+       return __tcp_oow_rate_limited(net, mib_idx, last_oow_ack_time);
 }
 
 /* RFC 5961 7 [ACK Throttling] */
@@ -3458,21 +3463,26 @@ static void tcp_send_challenge_ack(struct sock *sk, const struct sk_buff *skb)
        static u32 challenge_timestamp;
        static unsigned int challenge_count;
        struct tcp_sock *tp = tcp_sk(sk);
-       u32 now;
+       u32 count, now;
 
        /* First check our per-socket dupack rate limit. */
-       if (tcp_oow_rate_limited(sock_net(sk), skb,
-                                LINUX_MIB_TCPACKSKIPPEDCHALLENGE,
-                                &tp->last_oow_ack_time))
+       if (__tcp_oow_rate_limited(sock_net(sk),
+                                  LINUX_MIB_TCPACKSKIPPEDCHALLENGE,
+                                  &tp->last_oow_ack_time))
                return;
 
-       /* Then check the check host-wide RFC 5961 rate limit. */
+       /* Then check host-wide RFC 5961 rate limit. */
        now = jiffies / HZ;
        if (now != challenge_timestamp) {
+               u32 half = (sysctl_tcp_challenge_ack_limit + 1) >> 1;
+
                challenge_timestamp = now;
-               challenge_count = 0;
+               WRITE_ONCE(challenge_count, half +
+                          prandom_u32_max(sysctl_tcp_challenge_ack_limit));
        }
-       if (++challenge_count <= sysctl_tcp_challenge_ack_limit) {
+       count = READ_ONCE(challenge_count);
+       if (count > 0) {
+               WRITE_ONCE(challenge_count, count - 1);
                NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPCHALLENGEACK);
                tcp_send_ack(sk);
        }
index 8bd9911fdd163ab739d2dfda34e053b5e8e0bc03..e00e972c4e6a750d4d01bce3c1bc6125eb49f102 100644 (file)
@@ -2751,7 +2751,7 @@ void tcp_xmit_retransmit_queue(struct sock *sk)
        struct tcp_sock *tp = tcp_sk(sk);
        struct sk_buff *skb;
        struct sk_buff *hole = NULL;
-       u32 last_lost;
+       u32 max_segs, last_lost;
        int mib_idx;
        int fwd_rexmitting = 0;
 
@@ -2771,6 +2771,7 @@ void tcp_xmit_retransmit_queue(struct sock *sk)
                last_lost = tp->snd_una;
        }
 
+       max_segs = tcp_tso_autosize(sk, tcp_current_mss(sk));
        tcp_for_write_queue_from(skb, sk) {
                __u8 sacked = TCP_SKB_CB(skb)->sacked;
                int segs;
@@ -2784,6 +2785,10 @@ void tcp_xmit_retransmit_queue(struct sock *sk)
                segs = tp->snd_cwnd - tcp_packets_in_flight(tp);
                if (segs <= 0)
                        return;
+               /* In case tcp_shift_skb_data() have aggregated large skbs,
+                * we need to make sure not sending too bigs TSO packets
+                */
+               segs = min_t(int, segs, max_segs);
 
                if (fwd_rexmitting) {
 begin_fwd:
index 0ff31d97d48586af00c6973d123576e040ed422a..4aed8fc23d328592f8cf267fca70582f62fc6a3e 100644 (file)
@@ -391,9 +391,9 @@ int udp_v4_get_port(struct sock *sk, unsigned short snum)
        return udp_lib_get_port(sk, snum, ipv4_rcv_saddr_equal, hash2_nulladdr);
 }
 
-static inline int compute_score(struct sock *sk, struct net *net,
-                               __be32 saddr, unsigned short hnum, __be16 sport,
-                               __be32 daddr, __be16 dport, int dif)
+static int compute_score(struct sock *sk, struct net *net,
+                        __be32 saddr, __be16 sport,
+                        __be32 daddr, unsigned short hnum, int dif)
 {
        int score;
        struct inet_sock *inet;
@@ -434,52 +434,6 @@ static inline int compute_score(struct sock *sk, struct net *net,
        return score;
 }
 
-/*
- * In this second variant, we check (daddr, dport) matches (inet_rcv_sadd, inet_num)
- */
-static inline int compute_score2(struct sock *sk, struct net *net,
-                                __be32 saddr, __be16 sport,
-                                __be32 daddr, unsigned int hnum, int dif)
-{
-       int score;
-       struct inet_sock *inet;
-
-       if (!net_eq(sock_net(sk), net) ||
-           ipv6_only_sock(sk))
-               return -1;
-
-       inet = inet_sk(sk);
-
-       if (inet->inet_rcv_saddr != daddr ||
-           inet->inet_num != hnum)
-               return -1;
-
-       score = (sk->sk_family == PF_INET) ? 2 : 1;
-
-       if (inet->inet_daddr) {
-               if (inet->inet_daddr != saddr)
-                       return -1;
-               score += 4;
-       }
-
-       if (inet->inet_dport) {
-               if (inet->inet_dport != sport)
-                       return -1;
-               score += 4;
-       }
-
-       if (sk->sk_bound_dev_if) {
-               if (sk->sk_bound_dev_if != dif)
-                       return -1;
-               score += 4;
-       }
-
-       if (sk->sk_incoming_cpu == raw_smp_processor_id())
-               score++;
-
-       return score;
-}
-
 static u32 udp_ehashfn(const struct net *net, const __be32 laddr,
                       const __u16 lport, const __be32 faddr,
                       const __be16 fport)
@@ -492,11 +446,11 @@ static u32 udp_ehashfn(const struct net *net, const __be32 laddr,
                              udp_ehash_secret + net_hash_mix(net));
 }
 
-/* called with read_rcu_lock() */
+/* called with rcu_read_lock() */
 static struct sock *udp4_lib_lookup2(struct net *net,
                __be32 saddr, __be16 sport,
                __be32 daddr, unsigned int hnum, int dif,
-               struct udp_hslot *hslot2, unsigned int slot2,
+               struct udp_hslot *hslot2,
                struct sk_buff *skb)
 {
        struct sock *sk, *result;
@@ -506,7 +460,7 @@ static struct sock *udp4_lib_lookup2(struct net *net,
        result = NULL;
        badness = 0;
        udp_portaddr_for_each_entry_rcu(sk, &hslot2->head) {
-               score = compute_score2(sk, net, saddr, sport,
+               score = compute_score(sk, net, saddr, sport,
                                      daddr, hnum, dif);
                if (score > badness) {
                        reuseport = sk->sk_reuseport;
@@ -554,17 +508,22 @@ struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr,
 
                result = udp4_lib_lookup2(net, saddr, sport,
                                          daddr, hnum, dif,
-                                         hslot2, slot2, skb);
+                                         hslot2, skb);
                if (!result) {
+                       unsigned int old_slot2 = slot2;
                        hash2 = udp4_portaddr_hash(net, htonl(INADDR_ANY), hnum);
                        slot2 = hash2 & udptable->mask;
+                       /* avoid searching the same slot again. */
+                       if (unlikely(slot2 == old_slot2))
+                               return result;
+
                        hslot2 = &udptable->hash2[slot2];
                        if (hslot->count < hslot2->count)
                                goto begin;
 
                        result = udp4_lib_lookup2(net, saddr, sport,
-                                                 htonl(INADDR_ANY), hnum, dif,
-                                                 hslot2, slot2, skb);
+                                                 daddr, hnum, dif,
+                                                 hslot2, skb);
                }
                return result;
        }
@@ -572,8 +531,8 @@ struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr,
        result = NULL;
        badness = 0;
        sk_for_each_rcu(sk, &hslot->head) {
-               score = compute_score(sk, net, saddr, hnum, sport,
-                                     daddr, dport, dif);
+               score = compute_score(sk, net, saddr, sport,
+                                     daddr, hnum, dif);
                if (score > badness) {
                        reuseport = sk->sk_reuseport;
                        if (reuseport) {
@@ -1624,6 +1583,8 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 
        if (sk_filter(sk, skb))
                goto drop;
+       if (unlikely(skb->len < sizeof(struct udphdr)))
+               goto drop;
 
        udp_csum_pull_header(skb);
        if (sk_rcvqueues_full(sk, sk->sk_rcvbuf)) {
@@ -1755,8 +1716,11 @@ static inline int udp4_csum_init(struct sk_buff *skb, struct udphdr *uh,
                        return err;
        }
 
-       return skb_checksum_init_zero_check(skb, proto, uh->check,
-                                           inet_compute_pseudo);
+       /* Note, we are only interested in != 0 or == 0, thus the
+        * force to int.
+        */
+       return (__force int)skb_checksum_init_zero_check(skb, proto, uh->check,
+                                                        inet_compute_pseudo);
 }
 
 /*
index 4527285fcaa2c2c8134c089b88e0cfeeeddad292..a4fa840769690a271b259aee1630fb061bf0ae92 100644 (file)
@@ -98,7 +98,7 @@ static void icmpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
 
        if (!(type & ICMPV6_INFOMSG_MASK))
                if (icmp6->icmp6_type == ICMPV6_ECHO_REQUEST)
-                       ping_err(skb, offset, info);
+                       ping_err(skb, offset, ntohl(info));
 }
 
 static int icmpv6_rcv(struct sk_buff *skb);
index b2025bf3da4af20a19b61de59808d3ca91eead51..c0cbcb259f5a9acf9157d4626c61f532d8077855 100644 (file)
@@ -78,9 +78,12 @@ int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto)
         * we accept a checksum of zero here. When we find the socket
         * for the UDP packet we'll check if that socket allows zero checksum
         * for IPv6 (set by socket option).
+        *
+        * Note, we are only interested in != 0 or == 0, thus the
+        * force to int.
         */
-       return skb_checksum_init_zero_check(skb, proto, uh->check,
-                                          ip6_compute_pseudo);
+       return (__force int)skb_checksum_init_zero_check(skb, proto, uh->check,
+                                                        ip6_compute_pseudo);
 }
 EXPORT_SYMBOL(udp6_csum_init);
 
index 1bcef2369d64e6f1325dcab50c14601e6ca5a40a..771be1fa41764aa8ea3b570a058ee84b109903b9 100644 (file)
@@ -177,6 +177,7 @@ static void rt6_free_pcpu(struct rt6_info *non_pcpu_rt)
                }
        }
 
+       free_percpu(non_pcpu_rt->rt6i_pcpu);
        non_pcpu_rt->rt6i_pcpu = NULL;
 }
 
index fdc9de276ab1aeecfbc917e8718953d0aa0691ee..776d145113e138872f45d97e7f66ff0416762d85 100644 (file)
@@ -468,7 +468,7 @@ static int gre_rcv(struct sk_buff *skb)
        bool csum_err = false;
        int hdr_len;
 
-       hdr_len = gre_parse_header(skb, &tpi, &csum_err, htons(ETH_P_IPV6));
+       hdr_len = gre_parse_header(skb, &tpi, &csum_err, htons(ETH_P_IPV6), 0);
        if (hdr_len < 0)
                goto drop;
 
index f2e2013f834621fe2598d9dd9b452b83cd4f9018..487ef3bc7bbcde7f3509b76b3a68aee9d2ab7e8b 100644 (file)
@@ -1074,6 +1074,7 @@ static struct mfc6_cache *ip6mr_cache_alloc(void)
        struct mfc6_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL);
        if (!c)
                return NULL;
+       c->mfc_un.res.last_assert = jiffies - MFC_ASSERT_THRESH - 1;
        c->mfc_un.res.minvif = MAXMIFS;
        return c;
 }
index 969913da494fdf1d80ce674c3b6c421fdab18d3d..520b7884d0c2b55ab6f5fe4b0e0c86fcad8aab08 100644 (file)
@@ -1782,7 +1782,7 @@ static struct rt6_info *ip6_nh_lookup_table(struct net *net,
        };
        struct fib6_table *table;
        struct rt6_info *rt;
-       int flags = 0;
+       int flags = RT6_LOOKUP_F_IFACE;
 
        table = fib6_get_table(net, cfg->fc_table);
        if (!table)
index 0a5a255277e562ecb11ab703d964143f21bf85cd..0619ac70836d4ea5ed4e43a1f5c3ac1f870979c4 100644 (file)
@@ -560,13 +560,13 @@ static int ipip6_err(struct sk_buff *skb, u32 info)
 
        if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
                ipv4_update_pmtu(skb, dev_net(skb->dev), info,
-                                t->parms.link, 0, IPPROTO_IPV6, 0);
+                                t->parms.link, 0, iph->protocol, 0);
                err = 0;
                goto out;
        }
        if (type == ICMP_REDIRECT) {
                ipv4_redirect(skb, dev_net(skb->dev), t->parms.link, 0,
-                             IPPROTO_IPV6, 0);
+                             iph->protocol, 0);
                err = 0;
                goto out;
        }
index f36c2d076fce0df30d202d7683a67e3614d77fe9..2255d2bf5f6bdb532c51eff6792b3024b3eeefa1 100644 (file)
@@ -738,7 +738,7 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = {
 static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32 seq,
                                 u32 ack, u32 win, u32 tsval, u32 tsecr,
                                 int oif, struct tcp_md5sig_key *key, int rst,
-                                u8 tclass, u32 label)
+                                u8 tclass, __be32 label)
 {
        const struct tcphdr *th = tcp_hdr(skb);
        struct tcphdr *t1;
@@ -911,7 +911,7 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
 static void tcp_v6_send_ack(const struct sock *sk, struct sk_buff *skb, u32 seq,
                            u32 ack, u32 win, u32 tsval, u32 tsecr, int oif,
                            struct tcp_md5sig_key *key, u8 tclass,
-                           u32 label)
+                           __be32 label)
 {
        tcp_v6_send_response(sk, skb, seq, ack, win, tsval, tsecr, oif, key, 0,
                             tclass, label);
index f421c9f23c5bef7bf58937635474713cf4722516..acc09705618b4cccd622122f58ce4ab1639f22d1 100644 (file)
@@ -115,11 +115,10 @@ static void udp_v6_rehash(struct sock *sk)
        udp_lib_rehash(sk, new_hash);
 }
 
-static inline int compute_score(struct sock *sk, struct net *net,
-                               unsigned short hnum,
-                               const struct in6_addr *saddr, __be16 sport,
-                               const struct in6_addr *daddr, __be16 dport,
-                               int dif)
+static int compute_score(struct sock *sk, struct net *net,
+                        const struct in6_addr *saddr, __be16 sport,
+                        const struct in6_addr *daddr, unsigned short hnum,
+                        int dif)
 {
        int score;
        struct inet_sock *inet;
@@ -162,54 +161,11 @@ static inline int compute_score(struct sock *sk, struct net *net,
        return score;
 }
 
-static inline int compute_score2(struct sock *sk, struct net *net,
-                                const struct in6_addr *saddr, __be16 sport,
-                                const struct in6_addr *daddr,
-                                unsigned short hnum, int dif)
-{
-       int score;
-       struct inet_sock *inet;
-
-       if (!net_eq(sock_net(sk), net) ||
-           udp_sk(sk)->udp_port_hash != hnum ||
-           sk->sk_family != PF_INET6)
-               return -1;
-
-       if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr))
-               return -1;
-
-       score = 0;
-       inet = inet_sk(sk);
-
-       if (inet->inet_dport) {
-               if (inet->inet_dport != sport)
-                       return -1;
-               score++;
-       }
-
-       if (!ipv6_addr_any(&sk->sk_v6_daddr)) {
-               if (!ipv6_addr_equal(&sk->sk_v6_daddr, saddr))
-                       return -1;
-               score++;
-       }
-
-       if (sk->sk_bound_dev_if) {
-               if (sk->sk_bound_dev_if != dif)
-                       return -1;
-               score++;
-       }
-
-       if (sk->sk_incoming_cpu == raw_smp_processor_id())
-               score++;
-
-       return score;
-}
-
-/* called with read_rcu_lock() */
+/* called with rcu_read_lock() */
 static struct sock *udp6_lib_lookup2(struct net *net,
                const struct in6_addr *saddr, __be16 sport,
                const struct in6_addr *daddr, unsigned int hnum, int dif,
-               struct udp_hslot *hslot2, unsigned int slot2,
+               struct udp_hslot *hslot2,
                struct sk_buff *skb)
 {
        struct sock *sk, *result;
@@ -219,7 +175,7 @@ static struct sock *udp6_lib_lookup2(struct net *net,
        result = NULL;
        badness = -1;
        udp_portaddr_for_each_entry_rcu(sk, &hslot2->head) {
-               score = compute_score2(sk, net, saddr, sport,
+               score = compute_score(sk, net, saddr, sport,
                                      daddr, hnum, dif);
                if (score > badness) {
                        reuseport = sk->sk_reuseport;
@@ -268,17 +224,22 @@ struct sock *__udp6_lib_lookup(struct net *net,
 
                result = udp6_lib_lookup2(net, saddr, sport,
                                          daddr, hnum, dif,
-                                         hslot2, slot2, skb);
+                                         hslot2, skb);
                if (!result) {
+                       unsigned int old_slot2 = slot2;
                        hash2 = udp6_portaddr_hash(net, &in6addr_any, hnum);
                        slot2 = hash2 & udptable->mask;
+                       /* avoid searching the same slot again. */
+                       if (unlikely(slot2 == old_slot2))
+                               return result;
+
                        hslot2 = &udptable->hash2[slot2];
                        if (hslot->count < hslot2->count)
                                goto begin;
 
                        result = udp6_lib_lookup2(net, saddr, sport,
-                                                 &in6addr_any, hnum, dif,
-                                                 hslot2, slot2, skb);
+                                                 daddr, hnum, dif,
+                                                 hslot2, skb);
                }
                return result;
        }
@@ -286,7 +247,7 @@ struct sock *__udp6_lib_lookup(struct net *net,
        result = NULL;
        badness = -1;
        sk_for_each_rcu(sk, &hslot->head) {
-               score = compute_score(sk, net, hnum, saddr, sport, daddr, dport, dif);
+               score = compute_score(sk, net, saddr, sport, daddr, hnum, dif);
                if (score > badness) {
                        reuseport = sk->sk_reuseport;
                        if (reuseport) {
@@ -659,6 +620,8 @@ int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 
        if (sk_filter(sk, skb))
                goto drop;
+       if (unlikely(skb->len < sizeof(struct udphdr)))
+               goto drop;
 
        udp_csum_pull_header(skb);
        if (sk_rcvqueues_full(sk, sk->sk_rcvbuf)) {
index 738008726cc6f74710382ea1771a5a6118aa7a94..fda7f4715c58fa4f457f4410e1e3998819a1e77e 100644 (file)
@@ -241,6 +241,7 @@ static const struct file_operations kcm_seq_fops = {
        .open           = kcm_seq_open,
        .read           = seq_read,
        .llseek         = seq_lseek,
+       .release        = seq_release_net,
 };
 
 static struct kcm_seq_muxinfo kcm_seq_muxinfo = {
index 21b1fdf5d01d50d751ac6c3cf86670ecf98850be..6a1603bcdcedbccd99627d09d2b0367b361e1454 100644 (file)
@@ -148,14 +148,17 @@ u32 mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata)
 void mesh_sta_cleanup(struct sta_info *sta)
 {
        struct ieee80211_sub_if_data *sdata = sta->sdata;
-       u32 changed;
+       u32 changed = 0;
 
        /*
         * maybe userspace handles peer allocation and peering, but in either
         * case the beacon is still generated by the kernel and we might need
         * an update.
         */
-       changed = mesh_accept_plinks_update(sdata);
+       if (sdata->u.mesh.user_mpm &&
+           sta->mesh->plink_state == NL80211_PLINK_ESTAB)
+               changed |= mesh_plink_dec_estab_count(sdata);
+       changed |= mesh_accept_plinks_update(sdata);
        if (!sdata->u.mesh.user_mpm) {
                changed |= mesh_plink_deactivate(sta);
                del_timer_sync(&sta->mesh->plink_timer);
index 803001a45aa16e6b5a372ab385dba8e9c09bd2f0..1b07578bedf336c53e3b6072c8c3324f7f18081b 100644 (file)
@@ -1545,7 +1545,8 @@ static struct socket *make_send_sock(struct netns_ipvs *ipvs, int id)
 /*
  *      Set up receiving multicast socket over UDP
  */
-static struct socket *make_receive_sock(struct netns_ipvs *ipvs, int id)
+static struct socket *make_receive_sock(struct netns_ipvs *ipvs, int id,
+                                       int ifindex)
 {
        /* multicast addr */
        union ipvs_sockaddr mcast_addr;
@@ -1566,6 +1567,7 @@ static struct socket *make_receive_sock(struct netns_ipvs *ipvs, int id)
                set_sock_size(sock->sk, 0, result);
 
        get_mcast_sockaddr(&mcast_addr, &salen, &ipvs->bcfg, id);
+       sock->sk->sk_bound_dev_if = ifindex;
        result = sock->ops->bind(sock, (struct sockaddr *)&mcast_addr, salen);
        if (result < 0) {
                pr_err("Error binding to the multicast addr\n");
@@ -1868,7 +1870,7 @@ int start_sync_thread(struct netns_ipvs *ipvs, struct ipvs_sync_daemon_cfg *c,
                if (state == IP_VS_STATE_MASTER)
                        sock = make_send_sock(ipvs, id);
                else
-                       sock = make_receive_sock(ipvs, id);
+                       sock = make_receive_sock(ipvs, id, dev->ifindex);
                if (IS_ERR(sock)) {
                        result = PTR_ERR(sock);
                        goto outtinfo;
index db2312eeb2a47c44db0f0ac5a529a10a0a8f8d2f..9f530adad10d54a9277a9fbfdee4236055e3f99a 100644 (file)
@@ -646,6 +646,7 @@ static int nf_ct_resolve_clash(struct net *net, struct sk_buff *skb,
 
        l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct));
        if (l4proto->allow_clash &&
+           !nfct_nat(ct) &&
            !nf_ct_is_dying(ct) &&
            atomic_inc_not_zero(&ct->ct_general.use)) {
                nf_ct_acct_merge(ct, ctinfo, (struct nf_conn *)skb->nfct);
@@ -1544,6 +1545,8 @@ void nf_conntrack_cleanup_end(void)
        nf_conntrack_tstamp_fini();
        nf_conntrack_acct_fini();
        nf_conntrack_expect_fini();
+
+       kmem_cache_destroy(nf_conntrack_cachep);
 }
 
 /*
@@ -1599,8 +1602,15 @@ void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls)
        unsigned int nr_slots, i;
        size_t sz;
 
+       if (*sizep > (UINT_MAX / sizeof(struct hlist_nulls_head)))
+               return NULL;
+
        BUILD_BUG_ON(sizeof(struct hlist_nulls_head) != sizeof(struct hlist_head));
        nr_slots = *sizep = roundup(*sizep, PAGE_SIZE / sizeof(struct hlist_nulls_head));
+
+       if (nr_slots > (UINT_MAX / sizeof(struct hlist_nulls_head)))
+               return NULL;
+
        sz = nr_slots * sizeof(struct hlist_nulls_head);
        hash = (void *)__get_free_pages(GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO,
                                        get_order(sz));
index 7b7aa871a174fa2a1fd1e002fe0838231035e305..cf7c74599cbe5e6b800b584bee75d60f62d84a73 100644 (file)
@@ -1724,9 +1724,11 @@ struct nft_expr *nft_expr_init(const struct nft_ctx *ctx,
 
        err = nf_tables_newexpr(ctx, &info, expr);
        if (err < 0)
-               goto err2;
+               goto err3;
 
        return expr;
+err3:
+       kfree(expr);
 err2:
        module_put(info.ops->type->owner);
 err1:
@@ -2946,24 +2948,20 @@ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
                 * jumps are already validated for that chain.
                 */
                list_for_each_entry(i, &set->bindings, list) {
-                       if (binding->flags & NFT_SET_MAP &&
+                       if (i->flags & NFT_SET_MAP &&
                            i->chain == binding->chain)
                                goto bind;
                }
 
+               iter.genmask    = nft_genmask_next(ctx->net);
                iter.skip       = 0;
                iter.count      = 0;
                iter.err        = 0;
                iter.fn         = nf_tables_bind_check_setelem;
 
                set->ops->walk(ctx, set, &iter);
-               if (iter.err < 0) {
-                       /* Destroy anonymous sets if binding fails */
-                       if (set->flags & NFT_SET_ANONYMOUS)
-                               nf_tables_set_destroy(ctx, set);
-
+               if (iter.err < 0)
                        return iter.err;
-               }
        }
 bind:
        binding->chain = ctx->chain;
@@ -3192,12 +3190,13 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
        if (nest == NULL)
                goto nla_put_failure;
 
-       args.cb         = cb;
-       args.skb        = skb;
-       args.iter.skip  = cb->args[0];
-       args.iter.count = 0;
-       args.iter.err   = 0;
-       args.iter.fn    = nf_tables_dump_setelem;
+       args.cb                 = cb;
+       args.skb                = skb;
+       args.iter.genmask       = nft_genmask_cur(ctx.net);
+       args.iter.skip          = cb->args[0];
+       args.iter.count         = 0;
+       args.iter.err           = 0;
+       args.iter.fn            = nf_tables_dump_setelem;
        set->ops->walk(&ctx, set, &args.iter);
 
        nla_nest_end(skb, nest);
@@ -4284,6 +4283,7 @@ static int nf_tables_check_loops(const struct nft_ctx *ctx,
                            binding->chain != chain)
                                continue;
 
+                       iter.genmask    = nft_genmask_next(ctx->net);
                        iter.skip       = 0;
                        iter.count      = 0;
                        iter.err        = 0;
index e9f8dffcc244573e0fe209a9f03318f2bc53968d..fb8b5892b5ffa8ec6b0cf35792991b9125f082eb 100644 (file)
@@ -143,7 +143,7 @@ nft_do_chain(struct nft_pktinfo *pkt, void *priv)
        list_for_each_entry_continue_rcu(rule, &chain->rules, list) {
 
                /* This rule is not active, skip. */
-               if (unlikely(rule->genmask & (1 << gencursor)))
+               if (unlikely(rule->genmask & gencursor))
                        continue;
 
                rulenum++;
index 137e308d5b24c0865336da92e97686d8fef0b9d1..81fbb450783e59932ecc1c67147fc0ca89b475d3 100644 (file)
@@ -54,7 +54,6 @@ static void nft_ct_get_eval(const struct nft_expr *expr,
        const struct nf_conn_help *help;
        const struct nf_conntrack_tuple *tuple;
        const struct nf_conntrack_helper *helper;
-       long diff;
        unsigned int state;
 
        ct = nf_ct_get(pkt->skb, &ctinfo);
@@ -94,10 +93,7 @@ static void nft_ct_get_eval(const struct nft_expr *expr,
                return;
 #endif
        case NFT_CT_EXPIRATION:
-               diff = (long)jiffies - (long)ct->timeout.expires;
-               if (diff < 0)
-                       diff = 0;
-               *dest = jiffies_to_msecs(diff);
+               *dest = jiffies_to_msecs(nf_ct_expires(ct));
                return;
        case NFT_CT_HELPER:
                if (ct->master == NULL)
index 6fa016564f90cbf821a4ae45ac580b2e4e7c43ec..f39c53a159eba6cbe9586599b04147e8247d9523 100644 (file)
@@ -189,7 +189,6 @@ static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set,
        struct nft_hash_elem *he;
        struct rhashtable_iter hti;
        struct nft_set_elem elem;
-       u8 genmask = nft_genmask_cur(read_pnet(&set->pnet));
        int err;
 
        err = rhashtable_walk_init(&priv->ht, &hti, GFP_KERNEL);
@@ -218,7 +217,7 @@ static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set,
                        goto cont;
                if (nft_set_elem_expired(&he->ext))
                        goto cont;
-               if (!nft_set_elem_active(&he->ext, genmask))
+               if (!nft_set_elem_active(&he->ext, iter->genmask))
                        goto cont;
 
                elem.priv = he;
index 16c50b0dd426840f79bda0bb3ebbd28ecb2845e5..f4bad9dc15c48b0d8635632dc50179457b5c49c1 100644 (file)
@@ -227,7 +227,7 @@ void nft_meta_set_eval(const struct nft_expr *expr,
                        skb->pkt_type = value;
                break;
        case NFT_META_NFTRACE:
-               skb->nf_trace = 1;
+               skb->nf_trace = !!value;
                break;
        default:
                WARN_ON(1);
index f762094af7c1ca7e7684448381e7e49ee430045e..7201d57b5a932ebbb8d7fe72c691d5e69ff99784 100644 (file)
@@ -211,7 +211,6 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx,
        struct nft_rbtree_elem *rbe;
        struct nft_set_elem elem;
        struct rb_node *node;
-       u8 genmask = nft_genmask_cur(read_pnet(&set->pnet));
 
        spin_lock_bh(&nft_rbtree_lock);
        for (node = rb_first(&priv->root); node != NULL; node = rb_next(node)) {
@@ -219,7 +218,7 @@ static void nft_rbtree_walk(const struct nft_ctx *ctx,
 
                if (iter->count < iter->skip)
                        goto cont;
-               if (!nft_set_elem_active(&rbe->ext, genmask))
+               if (!nft_set_elem_active(&rbe->ext, iter->genmask))
                        goto cont;
 
                elem.priv = rbe;
index 3d5feede962dc584408e9b5a79e4b723f5308319..d84312584ee46a5fa82040c1802b98897f4b5aef 100644 (file)
@@ -818,8 +818,18 @@ static int ovs_ct_lookup(struct net *net, struct sw_flow_key *key,
                 */
                state = OVS_CS_F_TRACKED | OVS_CS_F_NEW | OVS_CS_F_RELATED;
                __ovs_ct_update_key(key, state, &info->zone, exp->master);
-       } else
-               return __ovs_ct_lookup(net, key, info, skb);
+       } else {
+               struct nf_conn *ct;
+               int err;
+
+               err = __ovs_ct_lookup(net, key, info, skb);
+               if (err)
+                       return err;
+
+               ct = (struct nf_conn *)skb->nfct;
+               if (ct)
+                       nf_ct_deliver_cached_events(ct);
+       }
 
        return 0;
 }
index 9bff6ef16fa7632fcfc05f23dd696d75ead6d5e8..b43c4015b2f79678bb5a2edffdd4e57cf9bdb880 100644 (file)
@@ -1341,7 +1341,7 @@ static unsigned int fanout_demux_hash(struct packet_fanout *f,
                                      struct sk_buff *skb,
                                      unsigned int num)
 {
-       return reciprocal_scale(skb_get_hash(skb), num);
+       return reciprocal_scale(__skb_get_hash_symmetric(skb), num);
 }
 
 static unsigned int fanout_demux_lb(struct packet_fanout *f,
@@ -1927,13 +1927,11 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg,
                goto out_unlock;
        }
 
-       sockc.tsflags = 0;
+       sockc.tsflags = sk->sk_tsflags;
        if (msg->msg_controllen) {
                err = sock_cmsg_send(sk, msg, &sockc);
-               if (unlikely(err)) {
-                       err = -EINVAL;
+               if (unlikely(err))
                        goto out_unlock;
-               }
        }
 
        skb->protocol = proto;
@@ -2678,7 +2676,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
                dev = dev_get_by_index(sock_net(&po->sk), saddr->sll_ifindex);
        }
 
-       sockc.tsflags = 0;
+       sockc.tsflags = po->sk.sk_tsflags;
        if (msg->msg_controllen) {
                err = sock_cmsg_send(&po->sk, msg, &sockc);
                if (unlikely(err))
@@ -2881,7 +2879,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
        if (unlikely(!(dev->flags & IFF_UP)))
                goto out_unlock;
 
-       sockc.tsflags = 0;
+       sockc.tsflags = sk->sk_tsflags;
        sockc.mark = sk->sk_mark;
        if (msg->msg_controllen) {
                err = sock_cmsg_send(sk, msg, &sockc);
index 310cabce23111cfaa45af97adef28f4d41d1bbda..7c2a65a6af5c6ab96eeef220c854ba01e846e35c 100644 (file)
@@ -111,7 +111,7 @@ void rds_ib_cm_connect_complete(struct rds_connection *conn, struct rdma_cm_even
                }
        }
 
-       if (conn->c_version < RDS_PROTOCOL(3,1)) {
+       if (conn->c_version < RDS_PROTOCOL(3, 1)) {
                printk(KERN_NOTICE "RDS/IB: Connection to %pI4 version %u.%u failed,"
                       " no longer supported\n",
                       &conn->c_faddr,
index 6b12b68541ae96fb8be76e72cb8d0e6f8c89abee..814173b466d9a80b21da91c5e38055221e9abcf8 100644 (file)
@@ -95,8 +95,9 @@ static int rds_loop_xmit(struct rds_connection *conn, struct rds_message *rm,
  */
 static void rds_loop_inc_free(struct rds_incoming *inc)
 {
-        struct rds_message *rm = container_of(inc, struct rds_message, m_inc);
-        rds_message_put(rm);
+       struct rds_message *rm = container_of(inc, struct rds_message, m_inc);
+
+       rds_message_put(rm);
 }
 
 /* we need to at least give the thread something to succeed */
index c173f69e1479bfaf643b9e5c69f4c9a151b18c67..e381bbcd9cc1c37d93eb19647b2a2262d8d2bae9 100644 (file)
@@ -102,7 +102,8 @@ int rds_sysctl_init(void)
        rds_sysctl_reconnect_min = msecs_to_jiffies(1);
        rds_sysctl_reconnect_min_jiffies = rds_sysctl_reconnect_min;
 
-       rds_sysctl_reg_table = register_net_sysctl(&init_net,"net/rds", rds_sysctl_rds_table);
+       rds_sysctl_reg_table =
+               register_net_sysctl(&init_net, "net/rds", rds_sysctl_rds_table);
        if (!rds_sysctl_reg_table)
                return -ENOMEM;
        return 0;
index 74ee126a6fe6c00ce8bce45053ec2b8805fc222e..c8a7b4c90190cafe8dc6fefe3e0a1935c558c357 100644 (file)
@@ -616,7 +616,7 @@ static int rds_tcp_init(void)
 
        ret = rds_tcp_recv_init();
        if (ret)
-               goto out_slab;
+               goto out_pernet;
 
        ret = rds_trans_register(&rds_tcp_transport);
        if (ret)
@@ -628,8 +628,9 @@ static int rds_tcp_init(void)
 
 out_recv:
        rds_tcp_recv_exit();
-out_slab:
+out_pernet:
        unregister_pernet_subsys(&rds_tcp_net_ops);
+out_slab:
        kmem_cache_destroy(rds_tcp_conn_slab);
 out:
        return ret;
index ec0602b0dc24b443c40437d5697037082d270f44..7940babf6c718617c729247ac348b2b6ed68a53a 100644 (file)
@@ -83,7 +83,7 @@ int rds_tcp_inc_copy_to_user(struct rds_incoming *inc, struct iov_iter *to);
 void rds_tcp_xmit_prepare(struct rds_connection *conn);
 void rds_tcp_xmit_complete(struct rds_connection *conn);
 int rds_tcp_xmit(struct rds_connection *conn, struct rds_message *rm,
-                unsigned int hdr_off, unsigned int sg, unsigned int off);
+                unsigned int hdr_off, unsigned int sg, unsigned int off);
 void rds_tcp_write_space(struct sock *sk);
 
 /* tcp_stats.c */
index fba13d0305fb25f12c476e6014a2a9d4c5559075..f6e95d60db54b488e9e67375bf96b6cbda82d788 100644 (file)
@@ -54,19 +54,19 @@ void rds_tcp_state_change(struct sock *sk)
 
        rdsdebug("sock %p state_change to %d\n", tc->t_sock, sk->sk_state);
 
-       switch(sk->sk_state) {
-               /* ignore connecting sockets as they make progress */
-               case TCP_SYN_SENT:
-               case TCP_SYN_RECV:
-                       break;
-               case TCP_ESTABLISHED:
-                       rds_connect_path_complete(conn, RDS_CONN_CONNECTING);
-                       break;
-               case TCP_CLOSE_WAIT:
-               case TCP_CLOSE:
-                       rds_conn_drop(conn);
-               default:
-                       break;
+       switch (sk->sk_state) {
+       /* ignore connecting sockets as they make progress */
+       case TCP_SYN_SENT:
+       case TCP_SYN_RECV:
+               break;
+       case TCP_ESTABLISHED:
+               rds_connect_path_complete(conn, RDS_CONN_CONNECTING);
+               break;
+       case TCP_CLOSE_WAIT:
+       case TCP_CLOSE:
+               rds_conn_drop(conn);
+       default:
+               break;
        }
 out:
        read_unlock_bh(&sk->sk_callback_lock);
index 686b1d03a55858aeb59d9863339c2c8e99e03d90..245542ca4718dd69f6f3e87903136d8307e4f055 100644 (file)
@@ -138,7 +138,7 @@ int rds_tcp_accept_one(struct socket *sock)
                        rds_tcp_reset_callbacks(new_sock, conn);
                        conn->c_outgoing = 0;
                        /* rds_connect_path_complete() marks RDS_CONN_UP */
-                       rds_connect_path_complete(conn, RDS_CONN_DISCONNECTING);
+                       rds_connect_path_complete(conn, RDS_CONN_RESETTING);
                }
        } else {
                rds_tcp_set_callbacks(new_sock, conn);
index c3196f9d070aaf9429947969120662f974ae44a7..6e6a7111a03406ae9a430c3c5efe6f56fe08a8da 100644 (file)
@@ -171,7 +171,7 @@ static int rds_tcp_data_recv(read_descriptor_t *desc, struct sk_buff *skb,
        while (left) {
                if (!tinc) {
                        tinc = kmem_cache_alloc(rds_tcp_incoming_slab,
-                                               arg->gfp);
+                                               arg->gfp);
                        if (!tinc) {
                                desc->error = -ENOMEM;
                                goto out;
index 22d0f2020a79f834d3a2a6536253e16c690baa41..618be69c9c3b7668c040584b1c4b40a8058f5e38 100644 (file)
@@ -66,19 +66,19 @@ void rds_tcp_xmit_complete(struct rds_connection *conn)
 static int rds_tcp_sendmsg(struct socket *sock, void *data, unsigned int len)
 {
        struct kvec vec = {
-                .iov_base = data,
-                .iov_len = len,
+               .iov_base = data,
+               .iov_len = len,
+       };
+       struct msghdr msg = {
+               .msg_flags = MSG_DONTWAIT | MSG_NOSIGNAL,
        };
-        struct msghdr msg = {
-                .msg_flags = MSG_DONTWAIT | MSG_NOSIGNAL,
-        };
 
        return kernel_sendmsg(sock, &msg, &vec, 1, vec.iov_len);
 }
 
 /* the core send_sem serializes this with other xmit and shutdown */
 int rds_tcp_xmit(struct rds_connection *conn, struct rds_message *rm,
-                unsigned int hdr_off, unsigned int sg, unsigned int off)
+                unsigned int hdr_off, unsigned int sg, unsigned int off)
 {
        struct rds_tcp_connection *tc = conn->c_transport_data;
        int done = 0;
@@ -196,7 +196,7 @@ void rds_tcp_write_space(struct sock *sk)
        tc->t_last_seen_una = rds_tcp_snd_una(tc);
        rds_send_drop_acked(conn, rds_tcp_snd_una(tc), rds_tcp_is_acked);
 
-        if ((atomic_read(&sk->sk_wmem_alloc) << 1) <= sk->sk_sndbuf)
+       if ((atomic_read(&sk->sk_wmem_alloc) << 1) <= sk->sk_sndbuf)
                queue_delayed_work(rds_wq, &conn->c_send_w, 0);
 
 out:
index f3afd1d60d3c7a7880993554c6375754fb4c6ff8..2ffd3e30c6434e62333ac4eefdc1b8fea50dfbe1 100644 (file)
@@ -140,8 +140,7 @@ unsigned int rds_trans_stats_info_copy(struct rds_info_iterator *iter,
        rds_info_iter_unmap(iter);
        down_read(&rds_trans_sem);
 
-       for (i = 0; i < RDS_TRANS_COUNT; i++)
-       {
+       for (i = 0; i < RDS_TRANS_COUNT; i++) {
                trans = transports[i];
                if (!trans || !trans->stats_info_copy)
                        continue;
index 79c4abcfa6b4ee86fbd92358f5585f88b74319f5..0a6394754e81db5469906b92f4b40046444bdb42 100644 (file)
@@ -164,7 +164,8 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety
                rose_frames_acked(sk, nr);
                if (ns == rose->vr) {
                        rose_start_idletimer(sk);
-                       if (sock_queue_rcv_skb(sk, skb) == 0) {
+                       if (sk_filter_trim_cap(sk, skb, ROSE_MIN_LEN) == 0 &&
+                           __sock_queue_rcv_skb(sk, skb) == 0) {
                                rose->vr = (rose->vr + 1) % ROSE_MODULUS;
                                queued = 1;
                        } else {
index 336774a535c3959f4f25b05d1732c014d0d1763c..c7a0b0d481c08ab7533d273e57c1897c2760d1d0 100644 (file)
@@ -1118,7 +1118,7 @@ tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb)
                nla_nest_end(skb, nest);
                ret = skb->len;
        } else
-               nla_nest_cancel(skb, nest);
+               nlmsg_trim(skb, b);
 
        nlh->nlmsg_len = skb_tail_pointer(skb) - b;
        if (NETLINK_CB(cb->skb).portid && ret)
index 658046dfe02d7210501ee9f0324158b50f6e7858..ea4a2fef1b71c506a89ca668c115db367aaa79a6 100644 (file)
@@ -106,9 +106,9 @@ int ife_get_meta_u16(struct sk_buff *skb, struct tcf_meta_info *mi)
 }
 EXPORT_SYMBOL_GPL(ife_get_meta_u16);
 
-int ife_alloc_meta_u32(struct tcf_meta_info *mi, void *metaval)
+int ife_alloc_meta_u32(struct tcf_meta_info *mi, void *metaval, gfp_t gfp)
 {
-       mi->metaval = kmemdup(metaval, sizeof(u32), GFP_KERNEL);
+       mi->metaval = kmemdup(metaval, sizeof(u32), gfp);
        if (!mi->metaval)
                return -ENOMEM;
 
@@ -116,9 +116,9 @@ int ife_alloc_meta_u32(struct tcf_meta_info *mi, void *metaval)
 }
 EXPORT_SYMBOL_GPL(ife_alloc_meta_u32);
 
-int ife_alloc_meta_u16(struct tcf_meta_info *mi, void *metaval)
+int ife_alloc_meta_u16(struct tcf_meta_info *mi, void *metaval, gfp_t gfp)
 {
-       mi->metaval = kmemdup(metaval, sizeof(u16), GFP_KERNEL);
+       mi->metaval = kmemdup(metaval, sizeof(u16), gfp);
        if (!mi->metaval)
                return -ENOMEM;
 
@@ -240,10 +240,10 @@ static int ife_validate_metatype(struct tcf_meta_ops *ops, void *val, int len)
 }
 
 /* called when adding new meta information
- * under ife->tcf_lock
+ * under ife->tcf_lock for existing action
 */
 static int load_metaops_and_vet(struct tcf_ife_info *ife, u32 metaid,
-                               void *val, int len)
+                               void *val, int len, bool exists)
 {
        struct tcf_meta_ops *ops = find_ife_oplist(metaid);
        int ret = 0;
@@ -251,11 +251,13 @@ static int load_metaops_and_vet(struct tcf_ife_info *ife, u32 metaid,
        if (!ops) {
                ret = -ENOENT;
 #ifdef CONFIG_MODULES
-               spin_unlock_bh(&ife->tcf_lock);
+               if (exists)
+                       spin_unlock_bh(&ife->tcf_lock);
                rtnl_unlock();
                request_module("ifemeta%u", metaid);
                rtnl_lock();
-               spin_lock_bh(&ife->tcf_lock);
+               if (exists)
+                       spin_lock_bh(&ife->tcf_lock);
                ops = find_ife_oplist(metaid);
 #endif
        }
@@ -272,10 +274,10 @@ static int load_metaops_and_vet(struct tcf_ife_info *ife, u32 metaid,
 }
 
 /* called when adding new meta information
- * under ife->tcf_lock
+ * under ife->tcf_lock for existing action
 */
 static int add_metainfo(struct tcf_ife_info *ife, u32 metaid, void *metaval,
-                       int len)
+                       int len, bool atomic)
 {
        struct tcf_meta_info *mi = NULL;
        struct tcf_meta_ops *ops = find_ife_oplist(metaid);
@@ -284,7 +286,7 @@ static int add_metainfo(struct tcf_ife_info *ife, u32 metaid, void *metaval,
        if (!ops)
                return -ENOENT;
 
-       mi = kzalloc(sizeof(*mi), GFP_KERNEL);
+       mi = kzalloc(sizeof(*mi), atomic ? GFP_ATOMIC : GFP_KERNEL);
        if (!mi) {
                /*put back what find_ife_oplist took */
                module_put(ops->owner);
@@ -294,7 +296,7 @@ static int add_metainfo(struct tcf_ife_info *ife, u32 metaid, void *metaval,
        mi->metaid = metaid;
        mi->ops = ops;
        if (len > 0) {
-               ret = ops->alloc(mi, metaval);
+               ret = ops->alloc(mi, metaval, atomic ? GFP_ATOMIC : GFP_KERNEL);
                if (ret != 0) {
                        kfree(mi);
                        module_put(ops->owner);
@@ -313,11 +315,13 @@ static int use_all_metadata(struct tcf_ife_info *ife)
        int rc = 0;
        int installed = 0;
 
+       read_lock(&ife_mod_lock);
        list_for_each_entry(o, &ifeoplist, list) {
-               rc = add_metainfo(ife, o->metaid, NULL, 0);
+               rc = add_metainfo(ife, o->metaid, NULL, 0, true);
                if (rc == 0)
                        installed += 1;
        }
+       read_unlock(&ife_mod_lock);
 
        if (installed)
                return 0;
@@ -385,8 +389,9 @@ static void tcf_ife_cleanup(struct tc_action *a, int bind)
        spin_unlock_bh(&ife->tcf_lock);
 }
 
-/* under ife->tcf_lock */
-static int populate_metalist(struct tcf_ife_info *ife, struct nlattr **tb)
+/* under ife->tcf_lock for existing action */
+static int populate_metalist(struct tcf_ife_info *ife, struct nlattr **tb,
+                            bool exists)
 {
        int len = 0;
        int rc = 0;
@@ -398,11 +403,11 @@ static int populate_metalist(struct tcf_ife_info *ife, struct nlattr **tb)
                        val = nla_data(tb[i]);
                        len = nla_len(tb[i]);
 
-                       rc = load_metaops_and_vet(ife, i, val, len);
+                       rc = load_metaops_and_vet(ife, i, val, len, exists);
                        if (rc != 0)
                                return rc;
 
-                       rc = add_metainfo(ife, i, val, len);
+                       rc = add_metainfo(ife, i, val, len, exists);
                        if (rc)
                                return rc;
                }
@@ -474,7 +479,8 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla,
                        saddr = nla_data(tb[TCA_IFE_SMAC]);
        }
 
-       spin_lock_bh(&ife->tcf_lock);
+       if (exists)
+               spin_lock_bh(&ife->tcf_lock);
        ife->tcf_action = parm->action;
 
        if (parm->flags & IFE_ENCODE) {
@@ -504,11 +510,12 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla,
                        if (ret == ACT_P_CREATED)
                                _tcf_ife_cleanup(a, bind);
 
-                       spin_unlock_bh(&ife->tcf_lock);
+                       if (exists)
+                               spin_unlock_bh(&ife->tcf_lock);
                        return err;
                }
 
-               err = populate_metalist(ife, tb2);
+               err = populate_metalist(ife, tb2, exists);
                if (err)
                        goto metadata_parse_err;
 
@@ -523,12 +530,14 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla,
                        if (ret == ACT_P_CREATED)
                                _tcf_ife_cleanup(a, bind);
 
-                       spin_unlock_bh(&ife->tcf_lock);
+                       if (exists)
+                               spin_unlock_bh(&ife->tcf_lock);
                        return err;
                }
        }
 
-       spin_unlock_bh(&ife->tcf_lock);
+       if (exists)
+               spin_unlock_bh(&ife->tcf_lock);
 
        if (ret == ACT_P_CREATED)
                tcf_hash_insert(tn, a);
index 9f002ada7074ccdb648433b443870562962c8694..d4bd19ee5822d4849cbf6c473d3245fb2cbff094 100644 (file)
@@ -121,10 +121,13 @@ static int __tcf_ipt_init(struct tc_action_net *tn, struct nlattr *nla,
        }
 
        td = (struct xt_entry_target *)nla_data(tb[TCA_IPT_TARG]);
-       if (nla_len(tb[TCA_IPT_TARG]) < td->u.target_size)
+       if (nla_len(tb[TCA_IPT_TARG]) < td->u.target_size) {
+               if (exists)
+                       tcf_hash_release(a, bind);
                return -EINVAL;
+       }
 
-       if (!tcf_hash_check(tn, index, a, bind)) {
+       if (!exists) {
                ret = tcf_hash_create(tn, index, est, a, sizeof(*ipt), bind,
                                      false);
                if (ret)
index 128942bc9e42e82ed11dad14448b69cd6858e541..1f5bd6ccbd2c6162f328aa209e2140bad99f1e3f 100644 (file)
@@ -181,7 +181,7 @@ static int tcf_mirred(struct sk_buff *skb, const struct tc_action *a,
 
        if (!(at & AT_EGRESS)) {
                if (m->tcfm_ok_push)
-                       skb_push(skb2, skb->mac_len);
+                       skb_push_rcsum(skb2, skb->mac_len);
        }
 
        /* mirror is always swallowed */
index 2177eac0a61ed00c6c60655f577e0dd816fd2c08..2e4bd2c0a50c497fbc6cbfe5338cab981559b491 100644 (file)
@@ -37,14 +37,18 @@ static int pfifo_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 
 static int pfifo_tail_enqueue(struct sk_buff *skb, struct Qdisc *sch)
 {
+       unsigned int prev_backlog;
+
        if (likely(skb_queue_len(&sch->q) < sch->limit))
                return qdisc_enqueue_tail(skb, sch);
 
+       prev_backlog = sch->qstats.backlog;
        /* queue full, remove one skb to fulfill the limit */
        __qdisc_queue_drop_head(sch, &sch->q);
        qdisc_qstats_drop(sch);
        qdisc_enqueue_tail(skb, sch);
 
+       qdisc_tree_reduce_backlog(sch, 0, prev_backlog - sch->qstats.backlog);
        return NET_XMIT_CN;
 }
 
index d4b4218af6b1b8dc9824408a3b4f62986cae3f18..052f84d6cc236176b64adbd2c2c3c7f38b18cb43 100644 (file)
@@ -1007,7 +1007,9 @@ static void htb_work_func(struct work_struct *work)
        struct htb_sched *q = container_of(work, struct htb_sched, work);
        struct Qdisc *sch = q->watchdog.qdisc;
 
+       rcu_read_lock();
        __netif_schedule(qdisc_root(sch));
+       rcu_read_unlock();
 }
 
 static int htb_init(struct Qdisc *sch, struct nlattr *opt)
@@ -1138,8 +1140,10 @@ htb_dump_class_stats(struct Qdisc *sch, unsigned long arg, struct gnet_dump *d)
 
        if (!cl->level && cl->un.leaf.q)
                qlen = cl->un.leaf.q->q.qlen;
-       cl->xstats.tokens = PSCHED_NS2TICKS(cl->tokens);
-       cl->xstats.ctokens = PSCHED_NS2TICKS(cl->ctokens);
+       cl->xstats.tokens = clamp_t(s64, PSCHED_NS2TICKS(cl->tokens),
+                                   INT_MIN, INT_MAX);
+       cl->xstats.ctokens = clamp_t(s64, PSCHED_NS2TICKS(cl->ctokens),
+                                    INT_MIN, INT_MAX);
 
        if (gnet_stats_copy_basic(d, NULL, &cl->bstats) < 0 ||
            gnet_stats_copy_rate_est(d, NULL, &cl->rate_est) < 0 ||
index 205bed00dd3463c62696ecc61eb78f2c97b3d0c9..178f1630a036414629aed50256cf6a8c3a105984 100644 (file)
@@ -650,14 +650,14 @@ static struct sk_buff *netem_dequeue(struct Qdisc *sch)
 #endif
 
                        if (q->qdisc) {
+                               unsigned int pkt_len = qdisc_pkt_len(skb);
                                int err = qdisc_enqueue(skb, q->qdisc);
 
-                               if (unlikely(err != NET_XMIT_SUCCESS)) {
-                                       if (net_xmit_drop_count(err)) {
-                                               qdisc_qstats_drop(sch);
-                                               qdisc_tree_reduce_backlog(sch, 1,
-                                                                         qdisc_pkt_len(skb));
-                                       }
+                               if (err != NET_XMIT_SUCCESS &&
+                                   net_xmit_drop_count(err)) {
+                                       qdisc_qstats_drop(sch);
+                                       qdisc_tree_reduce_backlog(sch, 1,
+                                                                 pkt_len);
                                }
                                goto tfifo_dequeue;
                        }
index 4b0a82191bc4011c9574a029fe8c30133c5f6f50..a356450b747ba57552d6c1e1e35255935580f02a 100644 (file)
@@ -172,8 +172,9 @@ prio_destroy(struct Qdisc *sch)
 static int prio_tune(struct Qdisc *sch, struct nlattr *opt)
 {
        struct prio_sched_data *q = qdisc_priv(sch);
+       struct Qdisc *queues[TCQ_PRIO_BANDS];
+       int oldbands = q->bands, i;
        struct tc_prio_qopt *qopt;
-       int i;
 
        if (nla_len(opt) < sizeof(*qopt))
                return -EINVAL;
@@ -187,62 +188,42 @@ static int prio_tune(struct Qdisc *sch, struct nlattr *opt)
                        return -EINVAL;
        }
 
+       /* Before commit, make sure we can allocate all new qdiscs */
+       for (i = oldbands; i < qopt->bands; i++) {
+               queues[i] = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops,
+                                             TC_H_MAKE(sch->handle, i + 1));
+               if (!queues[i]) {
+                       while (i > oldbands)
+                               qdisc_destroy(queues[--i]);
+                       return -ENOMEM;
+               }
+       }
+
        sch_tree_lock(sch);
        q->bands = qopt->bands;
        memcpy(q->prio2band, qopt->priomap, TC_PRIO_MAX+1);
 
-       for (i = q->bands; i < TCQ_PRIO_BANDS; i++) {
+       for (i = q->bands; i < oldbands; i++) {
                struct Qdisc *child = q->queues[i];
-               q->queues[i] = &noop_qdisc;
-               if (child != &noop_qdisc) {
-                       qdisc_tree_reduce_backlog(child, child->q.qlen, child->qstats.backlog);
-                       qdisc_destroy(child);
-               }
-       }
-       sch_tree_unlock(sch);
 
-       for (i = 0; i < q->bands; i++) {
-               if (q->queues[i] == &noop_qdisc) {
-                       struct Qdisc *child, *old;
-
-                       child = qdisc_create_dflt(sch->dev_queue,
-                                                 &pfifo_qdisc_ops,
-                                                 TC_H_MAKE(sch->handle, i + 1));
-                       if (child) {
-                               sch_tree_lock(sch);
-                               old = q->queues[i];
-                               q->queues[i] = child;
-
-                               if (old != &noop_qdisc) {
-                                       qdisc_tree_reduce_backlog(old,
-                                                                 old->q.qlen,
-                                                                 old->qstats.backlog);
-                                       qdisc_destroy(old);
-                               }
-                               sch_tree_unlock(sch);
-                       }
-               }
+               qdisc_tree_reduce_backlog(child, child->q.qlen,
+                                         child->qstats.backlog);
+               qdisc_destroy(child);
        }
+
+       for (i = oldbands; i < q->bands; i++)
+               q->queues[i] = queues[i];
+
+       sch_tree_unlock(sch);
        return 0;
 }
 
 static int prio_init(struct Qdisc *sch, struct nlattr *opt)
 {
-       struct prio_sched_data *q = qdisc_priv(sch);
-       int i;
-
-       for (i = 0; i < TCQ_PRIO_BANDS; i++)
-               q->queues[i] = &noop_qdisc;
-
-       if (opt == NULL) {
+       if (!opt)
                return -EINVAL;
-       } else {
-               int err;
 
-               if ((err = prio_tune(sch, opt)) != 0)
-                       return err;
-       }
-       return 0;
+       return prio_tune(sch, opt);
 }
 
 static int prio_dump(struct Qdisc *sch, struct sk_buff *skb)
index a701527a9480faff1b8d91257e1dbf3c0f09ed68..47cf4604d19c23a4bf392c87d06de3e44680d5c0 100644 (file)
@@ -112,7 +112,6 @@ int sctp_rcv(struct sk_buff *skb)
        struct sctp_ep_common *rcvr;
        struct sctp_transport *transport = NULL;
        struct sctp_chunk *chunk;
-       struct sctphdr *sh;
        union sctp_addr src;
        union sctp_addr dest;
        int family;
@@ -127,8 +126,6 @@ int sctp_rcv(struct sk_buff *skb)
        if (skb_linearize(skb))
                goto discard_it;
 
-       sh = sctp_hdr(skb);
-
        /* Pull up the IP and SCTP headers. */
        __skb_pull(skb, skb_transport_offset(skb));
        if (skb->len < sizeof(struct sctphdr))
@@ -230,7 +227,7 @@ int sctp_rcv(struct sk_buff *skb)
        chunk->rcvr = rcvr;
 
        /* Remember the SCTP header. */
-       chunk->sctp_hdr = sh;
+       chunk->sctp_hdr = sctp_hdr(skb);
 
        /* Set the source and destination addresses of the incoming chunk.  */
        sctp_init_addrs(chunk, &src, &dest);
index 1ce724b87618a08c147b768a33c2fcef7f9ff6cb..f69edcf219e514d864c4af2d57eb0429e8f4938a 100644 (file)
@@ -3,12 +3,6 @@
 #include <linux/sock_diag.h>
 #include <net/sctp/sctp.h>
 
-extern void inet_diag_msg_common_fill(struct inet_diag_msg *r,
-                                     struct sock *sk);
-extern int inet_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb,
-                                   struct inet_diag_msg *r, int ext,
-                                   struct user_namespace *user_ns);
-
 static void sctp_diag_get_info(struct sock *sk, struct inet_diag_msg *r,
                               void *info);
 
index 6f11c62bc8f9cbae5d9d69da5127e9f92e000f2f..a597708ae3818b25f9eaee8f1c3584865432a37d 100644 (file)
@@ -330,6 +330,21 @@ static int tipc_reset_bearer(struct net *net, struct tipc_bearer *b)
        return 0;
 }
 
+/* tipc_bearer_reset_all - reset all links on all bearers
+ */
+void tipc_bearer_reset_all(struct net *net)
+{
+       struct tipc_net *tn = tipc_net(net);
+       struct tipc_bearer *b;
+       int i;
+
+       for (i = 0; i < MAX_BEARERS; i++) {
+               b = rcu_dereference_rtnl(tn->bearer_list[i]);
+               if (b)
+                       tipc_reset_bearer(net, b);
+       }
+}
+
 /**
  * bearer_disable
  *
@@ -405,7 +420,7 @@ int tipc_l2_send_msg(struct net *net, struct sk_buff *skb,
                return 0;
 
        /* Send RESET message even if bearer is detached from device */
-       tipc_ptr = rtnl_dereference(dev->tipc_ptr);
+       tipc_ptr = rcu_dereference_rtnl(dev->tipc_ptr);
        if (unlikely(!tipc_ptr && !msg_is_reset(buf_msg(skb))))
                goto drop;
 
index f686e41b5abb880cc09f9c1a707356047d32749f..60e49c3be19c1e44b44175926b10c6aecb64fa58 100644 (file)
@@ -198,6 +198,7 @@ void tipc_bearer_add_dest(struct net *net, u32 bearer_id, u32 dest);
 void tipc_bearer_remove_dest(struct net *net, u32 bearer_id, u32 dest);
 struct tipc_bearer *tipc_bearer_find(struct net *net, const char *name);
 struct tipc_media *tipc_media_find(const char *name);
+void tipc_bearer_reset_all(struct net *net);
 int tipc_bearer_setup(void);
 void tipc_bearer_cleanup(void);
 void tipc_bearer_stop(struct net *net);
index 7059c94f33c55f03ce70e193a017cbf2bb441f3e..7d89f8713d4984f3a9d732c9cabc56d410219ad8 100644 (file)
@@ -349,6 +349,8 @@ void tipc_link_remove_bc_peer(struct tipc_link *snd_l,
        u16 ack = snd_l->snd_nxt - 1;
 
        snd_l->ackers--;
+       rcv_l->bc_peer_is_up = true;
+       rcv_l->state = LINK_ESTABLISHED;
        tipc_link_bc_ack_rcv(rcv_l, ack, xmitq);
        tipc_link_reset(rcv_l);
        rcv_l->state = LINK_RESET;
@@ -704,7 +706,8 @@ static void link_profile_stats(struct tipc_link *l)
  */
 int tipc_link_timeout(struct tipc_link *l, struct sk_buff_head *xmitq)
 {
-       int mtyp, rc = 0;
+       int mtyp = 0;
+       int rc = 0;
        bool state = false;
        bool probe = false;
        bool setup = false;
@@ -1558,7 +1561,12 @@ void tipc_link_bc_sync_rcv(struct tipc_link *l, struct tipc_msg *hdr,
        if (!msg_peer_node_is_up(hdr))
                return;
 
-       l->bc_peer_is_up = true;
+       /* Open when peer ackowledges our bcast init msg (pkt #1) */
+       if (msg_ack(hdr))
+               l->bc_peer_is_up = true;
+
+       if (!l->bc_peer_is_up)
+               return;
 
        /* Ignore if peers_snd_nxt goes beyond receive window */
        if (more(peers_snd_nxt, l->rcv_nxt + l->window))
index 8740930f07872ff1ec8ff9d6b712b2772ba48336..17201aa8423ddd816a4792621e289dbeee8d4928 100644 (file)
@@ -41,6 +41,8 @@
 #include "name_table.h"
 
 #define MAX_FORWARD_SIZE 1024
+#define BUF_HEADROOM (LL_MAX_HEADER + 48)
+#define BUF_TAILROOM 16
 
 static unsigned int align(unsigned int i)
 {
@@ -505,6 +507,10 @@ bool tipc_msg_reverse(u32 own_node,  struct sk_buff **skb, int err)
                msg_set_hdr_sz(hdr, BASIC_H_SIZE);
        }
 
+       if (skb_cloned(_skb) &&
+           pskb_expand_head(_skb, BUF_HEADROOM, BUF_TAILROOM, GFP_KERNEL))
+               goto exit;
+
        /* Now reverse the concerned fields */
        msg_set_errcode(hdr, err);
        msg_set_origport(hdr, msg_destport(&ohdr));
index 024da8af91f0edf55e78c30edc682fe5741c6198..7cf52fb39bee6aa38379e81af31fe3d5c6fea557 100644 (file)
@@ -94,17 +94,6 @@ struct plist;
 
 #define TIPC_MEDIA_INFO_OFFSET 5
 
-/**
- * TIPC message buffer code
- *
- * TIPC message buffer headroom reserves space for the worst-case
- * link-level device header (in case the message is sent off-node).
- *
- * Note: Headroom should be a multiple of 4 to ensure the TIPC header fields
- *       are word aligned for quicker access
- */
-#define BUF_HEADROOM (LL_MAX_HEADER + 48)
-
 struct tipc_skb_cb {
        void *handle;
        struct sk_buff *tail;
index 3ad9fab1985f1cdca35c876f0f228d28b9b5db00..1fd4647647650b75f17f41d19d288be7abe436a3 100644 (file)
@@ -604,7 +604,7 @@ static int tipc_nl_compat_link_dump(struct tipc_nl_compat_msg *msg,
 
        link_info.dest = nla_get_flag(link[TIPC_NLA_LINK_DEST]);
        link_info.up = htonl(nla_get_flag(link[TIPC_NLA_LINK_UP]));
-       nla_strlcpy(link_info.str, nla_data(link[TIPC_NLA_LINK_NAME]),
+       nla_strlcpy(link_info.str, link[TIPC_NLA_LINK_NAME],
                    TIPC_MAX_LINK_NAME);
 
        return tipc_add_tlv(msg->rep, TIPC_TLV_LINK_INFO,
index e01e2c71b5a16fde975b6e87bd134ae57cd57326..23d4761842a0ed9ac62eb1af5cc03259aadeb22d 100644 (file)
@@ -1297,10 +1297,6 @@ static void tipc_node_bc_rcv(struct net *net, struct sk_buff *skb, int bearer_id
 
        rc = tipc_bcast_rcv(net, be->link, skb);
 
-       /* Broadcast link reset may happen at reassembly failure */
-       if (rc & TIPC_LINK_DOWN_EVT)
-               tipc_node_reset_links(n);
-
        /* Broadcast ACKs are sent on a unicast link */
        if (rc & TIPC_LINK_SND_BC_ACK) {
                tipc_node_read_lock(n);
@@ -1320,6 +1316,17 @@ static void tipc_node_bc_rcv(struct net *net, struct sk_buff *skb, int bearer_id
                spin_unlock_bh(&be->inputq2.lock);
                tipc_sk_mcast_rcv(net, &be->arrvq, &be->inputq2);
        }
+
+       if (rc & TIPC_LINK_DOWN_EVT) {
+               /* Reception reassembly failure => reset all links to peer */
+               if (!tipc_link_is_up(be->link))
+                       tipc_node_reset_links(n);
+
+               /* Retransmission failure => reset all links to all peers */
+               if (!tipc_link_is_up(tipc_bc_sndlink(net)))
+                       tipc_bearer_reset_all(net);
+       }
+
        tipc_node_put(n);
 }
 
index 88bfcd707064145cac007eb7ff88fb1a2eb77418..c49b8df438cbeee021bbedf3c96631d82fb16670 100644 (file)
@@ -796,9 +796,11 @@ void tipc_sk_mcast_rcv(struct net *net, struct sk_buff_head *arrvq,
  * @tsk: receiving socket
  * @skb: pointer to message buffer.
  */
-static void tipc_sk_proto_rcv(struct tipc_sock *tsk, struct sk_buff *skb)
+static void tipc_sk_proto_rcv(struct tipc_sock *tsk, struct sk_buff *skb,
+                             struct sk_buff_head *xmitq)
 {
        struct sock *sk = &tsk->sk;
+       u32 onode = tsk_own_node(tsk);
        struct tipc_msg *hdr = buf_msg(skb);
        int mtyp = msg_type(hdr);
        bool conn_cong;
@@ -811,7 +813,8 @@ static void tipc_sk_proto_rcv(struct tipc_sock *tsk, struct sk_buff *skb)
 
        if (mtyp == CONN_PROBE) {
                msg_set_type(hdr, CONN_PROBE_REPLY);
-               tipc_sk_respond(sk, skb, TIPC_OK);
+               if (tipc_msg_reverse(onode, &skb, TIPC_OK))
+                       __skb_queue_tail(xmitq, skb);
                return;
        } else if (mtyp == CONN_ACK) {
                conn_cong = tsk_conn_cong(tsk);
@@ -1686,7 +1689,8 @@ static unsigned int rcvbuf_limit(struct sock *sk, struct sk_buff *skb)
  *
  * Returns true if message was added to socket receive queue, otherwise false
  */
-static bool filter_rcv(struct sock *sk, struct sk_buff *skb)
+static bool filter_rcv(struct sock *sk, struct sk_buff *skb,
+                      struct sk_buff_head *xmitq)
 {
        struct socket *sock = sk->sk_socket;
        struct tipc_sock *tsk = tipc_sk(sk);
@@ -1696,7 +1700,7 @@ static bool filter_rcv(struct sock *sk, struct sk_buff *skb)
        int usr = msg_user(hdr);
 
        if (unlikely(msg_user(hdr) == CONN_MANAGER)) {
-               tipc_sk_proto_rcv(tsk, skb);
+               tipc_sk_proto_rcv(tsk, skb, xmitq);
                return false;
        }
 
@@ -1739,7 +1743,8 @@ static bool filter_rcv(struct sock *sk, struct sk_buff *skb)
        return true;
 
 reject:
-       tipc_sk_respond(sk, skb, err);
+       if (tipc_msg_reverse(tsk_own_node(tsk), &skb, err))
+               __skb_queue_tail(xmitq, skb);
        return false;
 }
 
@@ -1755,9 +1760,24 @@ static bool filter_rcv(struct sock *sk, struct sk_buff *skb)
 static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *skb)
 {
        unsigned int truesize = skb->truesize;
+       struct sk_buff_head xmitq;
+       u32 dnode, selector;
 
-       if (likely(filter_rcv(sk, skb)))
+       __skb_queue_head_init(&xmitq);
+
+       if (likely(filter_rcv(sk, skb, &xmitq))) {
                atomic_add(truesize, &tipc_sk(sk)->dupl_rcvcnt);
+               return 0;
+       }
+
+       if (skb_queue_empty(&xmitq))
+               return 0;
+
+       /* Send response/rejected message */
+       skb = __skb_dequeue(&xmitq);
+       dnode = msg_destnode(buf_msg(skb));
+       selector = msg_origport(buf_msg(skb));
+       tipc_node_xmit_skb(sock_net(sk), skb, dnode, selector);
        return 0;
 }
 
@@ -1771,12 +1791,13 @@ static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *skb)
  * Caller must hold socket lock
  */
 static void tipc_sk_enqueue(struct sk_buff_head *inputq, struct sock *sk,
-                           u32 dport)
+                           u32 dport, struct sk_buff_head *xmitq)
 {
+       unsigned long time_limit = jiffies + 2;
+       struct sk_buff *skb;
        unsigned int lim;
        atomic_t *dcnt;
-       struct sk_buff *skb;
-       unsigned long time_limit = jiffies + 2;
+       u32 onode;
 
        while (skb_queue_len(inputq)) {
                if (unlikely(time_after_eq(jiffies, time_limit)))
@@ -1788,7 +1809,7 @@ static void tipc_sk_enqueue(struct sk_buff_head *inputq, struct sock *sk,
 
                /* Add message directly to receive queue if possible */
                if (!sock_owned_by_user(sk)) {
-                       filter_rcv(sk, skb);
+                       filter_rcv(sk, skb, xmitq);
                        continue;
                }
 
@@ -1801,7 +1822,9 @@ static void tipc_sk_enqueue(struct sk_buff_head *inputq, struct sock *sk,
                        continue;
 
                /* Overload => reject message back to sender */
-               tipc_sk_respond(sk, skb, TIPC_ERR_OVERLOAD);
+               onode = tipc_own_addr(sock_net(sk));
+               if (tipc_msg_reverse(onode, &skb, TIPC_ERR_OVERLOAD))
+                       __skb_queue_tail(xmitq, skb);
                break;
        }
 }
@@ -1814,12 +1837,14 @@ static void tipc_sk_enqueue(struct sk_buff_head *inputq, struct sock *sk,
  */
 void tipc_sk_rcv(struct net *net, struct sk_buff_head *inputq)
 {
+       struct sk_buff_head xmitq;
        u32 dnode, dport = 0;
        int err;
        struct tipc_sock *tsk;
        struct sock *sk;
        struct sk_buff *skb;
 
+       __skb_queue_head_init(&xmitq);
        while (skb_queue_len(inputq)) {
                dport = tipc_skb_peek_port(inputq, dport);
                tsk = tipc_sk_lookup(net, dport);
@@ -1827,9 +1852,14 @@ void tipc_sk_rcv(struct net *net, struct sk_buff_head *inputq)
                if (likely(tsk)) {
                        sk = &tsk->sk;
                        if (likely(spin_trylock_bh(&sk->sk_lock.slock))) {
-                               tipc_sk_enqueue(inputq, sk, dport);
+                               tipc_sk_enqueue(inputq, sk, dport, &xmitq);
                                spin_unlock_bh(&sk->sk_lock.slock);
                        }
+                       /* Send pending response/rejected messages, if any */
+                       while ((skb = __skb_dequeue(&xmitq))) {
+                               dnode = msg_destnode(buf_msg(skb));
+                               tipc_node_xmit_skb(net, skb, dnode, dport);
+                       }
                        sock_put(sk);
                        continue;
                }
index b5f1221f48d4859156aa640066e1fd80cf2927dc..b96ac918e0ba026d74e2a02dda99b8eb0f613267 100644 (file)
  * function will also cleanup rejected sockets, those that reach the connected
  * state but leave it before they have been accepted.
  *
+ * - Lock ordering for pending or accept queue sockets is:
+ *
+ *     lock_sock(listener);
+ *     lock_sock_nested(pending, SINGLE_DEPTH_NESTING);
+ *
+ * Using explicit nested locking keeps lockdep happy since normally only one
+ * lock of a given class may be taken at a time.
+ *
  * - Sockets created by user action will be cleaned up when the user process
  * calls close(2), causing our release implementation to be called. Our release
  * implementation will perform some cleanup then drop the last reference so our
@@ -443,7 +451,7 @@ void vsock_pending_work(struct work_struct *work)
        cleanup = true;
 
        lock_sock(listener);
-       lock_sock(sk);
+       lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
 
        if (vsock_is_pending(sk)) {
                vsock_remove_pending(listener, sk);
@@ -1292,7 +1300,7 @@ static int vsock_accept(struct socket *sock, struct socket *newsock, int flags)
        if (connected) {
                listener->sk_ack_backlog--;
 
-               lock_sock(connected);
+               lock_sock_nested(connected, SINGLE_DEPTH_NESTING);
                vconnected = vsock_sk(connected);
 
                /* If the listener socket has received an error, then we should
index d7599014055dfc1c3d256d917e3a3645f1a27143..7d72283901a3bb8fa7d3294e5dcdfbc6cd61e3a2 100644 (file)
@@ -3487,16 +3487,16 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
                params.smps_mode = NL80211_SMPS_OFF;
        }
 
+       params.pbss = nla_get_flag(info->attrs[NL80211_ATTR_PBSS]);
+       if (params.pbss && !rdev->wiphy.bands[NL80211_BAND_60GHZ])
+               return -EOPNOTSUPP;
+
        if (info->attrs[NL80211_ATTR_ACL_POLICY]) {
                params.acl = parse_acl_data(&rdev->wiphy, info);
                if (IS_ERR(params.acl))
                        return PTR_ERR(params.acl);
        }
 
-       params.pbss = nla_get_flag(info->attrs[NL80211_ATTR_PBSS]);
-       if (params.pbss && !rdev->wiphy.bands[NL80211_BAND_60GHZ])
-               return -EOPNOTSUPP;
-
        wdev_lock(wdev);
        err = rdev_start_ap(rdev, dev, &params);
        if (!err) {
index 4e809e978b7d274f3967349c87acda43aac57370..b7d1592bd5b8939f50d01ee2ce11a95c0df0e93b 100644 (file)
@@ -509,7 +509,7 @@ static int __ieee80211_data_to_8023(struct sk_buff *skb, struct ethhdr *ehdr,
                 * replace EtherType */
                hdrlen += ETH_ALEN + 2;
        else
-               tmp.h_proto = htons(skb->len);
+               tmp.h_proto = htons(skb->len - hdrlen);
 
        pskb_pull(skb, hdrlen);
 
@@ -721,6 +721,8 @@ __ieee80211_amsdu_copy(struct sk_buff *skb, unsigned int hlen,
         * alignment since sizeof(struct ethhdr) is 14.
         */
        frame = dev_alloc_skb(hlen + sizeof(struct ethhdr) + 2 + cur_len);
+       if (!frame)
+               return NULL;
 
        skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2);
        skb_copy_bits(skb, offset, skb_put(frame, cur_len), cur_len);
index 52e4e61140d1226f5ba80676d1973baa298d3b23..2573543842d06947cf081ff3cf752c8d309f094b 100644 (file)
@@ -1,2 +1,3 @@
 *.pyc
 *.pyo
+constants.py
index cd129e65d1ffdbbb06e6f1096340e00f1cff8d9e..8b00031f53497035b68bfe65bd682989430fe016 100644 (file)
@@ -13,9 +13,11 @@ quiet_cmd_gen_constants_py = GEN     $@
        $(CPP) -E -x c -P $(c_flags) $< > $@ ;\
        sed -i '1,/<!-- end-c-headers -->/d;' $@
 
-$(obj)/constants.py: $(SRCTREE)/$(obj)/constants.py.in
-       $(call if_changed,gen_constants_py)
+targets += constants.py
+$(obj)/constants.py: $(SRCTREE)/$(obj)/constants.py.in FORCE
+       $(call if_changed_dep,gen_constants_py)
 
 build_constants_py: $(obj)/constants.py
+       @:
 
 clean-files := *.pyc *.pyo $(if $(KBUILD_SRC),*.py) $(obj)/constants.py
index 07e6c2befe368665ed7b35bd4fa9f08e1600d4dc..7986f4e0da123a240eeca854666dd3cfac86d69b 100644 (file)
@@ -14,7 +14,6 @@
 
 #include <linux/fs.h>
 #include <linux/mount.h>
-#include <linux/radix-tree.h>
 
 /* We need to stringify expanded macros so that they can be parsed */
 
@@ -51,9 +50,3 @@ LX_VALUE(MNT_NOEXEC)
 LX_VALUE(MNT_NOATIME)
 LX_VALUE(MNT_NODIRATIME)
 LX_VALUE(MNT_RELATIME)
-
-/* linux/radix-tree.h */
-LX_VALUE(RADIX_TREE_INDIRECT_PTR)
-LX_GDBPARSED(RADIX_TREE_HEIGHT_MASK)
-LX_GDBPARSED(RADIX_TREE_MAP_SHIFT)
-LX_GDBPARSED(RADIX_TREE_MAP_MASK)
diff --git a/scripts/gdb/linux/radixtree.py b/scripts/gdb/linux/radixtree.py
deleted file mode 100644 (file)
index 0fdef4e..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-#
-# gdb helper commands and functions for Linux kernel debugging
-#
-#  Radix Tree Parser
-#
-# Copyright (c) 2016 Linaro Ltd
-#
-# Authors:
-#  Kieran Bingham <kieran.bingham@linaro.org>
-#
-# This work is licensed under the terms of the GNU GPL version 2.
-#
-
-import gdb
-
-from linux import utils
-from linux import constants
-
-radix_tree_root_type = utils.CachedType("struct radix_tree_root")
-radix_tree_node_type = utils.CachedType("struct radix_tree_node")
-
-
-def is_indirect_ptr(node):
-    long_type = utils.get_long_type()
-    return (node.cast(long_type) & constants.LX_RADIX_TREE_INDIRECT_PTR)
-
-
-def indirect_to_ptr(node):
-    long_type = utils.get_long_type()
-    node_type = node.type
-    indirect_ptr = node.cast(long_type) & ~constants.LX_RADIX_TREE_INDIRECT_PTR
-    return indirect_ptr.cast(node_type)
-
-
-def maxindex(height):
-    height = height & constants.LX_RADIX_TREE_HEIGHT_MASK
-    return gdb.parse_and_eval("height_to_maxindex["+str(height)+"]")
-
-
-def lookup(root, index):
-    if root.type == radix_tree_root_type.get_type().pointer():
-        root = root.dereference()
-    elif root.type != radix_tree_root_type.get_type():
-        raise gdb.GdbError("Must be struct radix_tree_root not {}"
-                           .format(root.type))
-
-    node = root['rnode']
-    if node is 0:
-        return None
-
-    if not (is_indirect_ptr(node)):
-        if (index > 0):
-            return None
-        return node
-
-    node = indirect_to_ptr(node)
-
-    height = node['path'] & constants.LX_RADIX_TREE_HEIGHT_MASK
-    if (index > maxindex(height)):
-        return None
-
-    shift = (height-1) * constants.LX_RADIX_TREE_MAP_SHIFT
-
-    while True:
-        new_index = (index >> shift) & constants.LX_RADIX_TREE_MAP_MASK
-        slot = node['slots'][new_index]
-
-        node = slot.cast(node.type.pointer()).dereference()
-        if node is 0:
-            return None
-
-        shift -= constants.LX_RADIX_TREE_MAP_SHIFT
-        height -= 1
-
-        if (height <= 0):
-            break
-
-    return node
-
-
-class LxRadixTree(gdb.Function):
-    """ Lookup and return a node from a RadixTree.
-
-$lx_radix_tree_lookup(root_node [, index]): Return the node at the given index.
-If index is omitted, the root node is dereferenced and returned."""
-
-    def __init__(self):
-        super(LxRadixTree, self).__init__("lx_radix_tree_lookup")
-
-    def invoke(self, root, index=0):
-        result = lookup(root, index)
-        if result is None:
-            raise gdb.GdbError("No entry in tree at index {}".format(index))
-
-        return result
-
-LxRadixTree()
index 9a0f8923f67ccb870224a93d8884458a1fa11a30..004b0ac7fa72d25598d26fbab4b2035e3e882305 100644 (file)
@@ -153,7 +153,7 @@ lx-symbols command."""
             saved_state['breakpoint'].enabled = saved_state['enabled']
 
     def invoke(self, arg, from_tty):
-        self.module_paths = arg.split()
+        self.module_paths = [os.path.expanduser(p) for p in arg.split()]
         self.module_paths.append(os.getcwd())
 
         # enforce update
index 3a80ad6eecad7167c3bb11fb2314f1d1c43c0a89..6e0b0afd888ade32768edba8e659b61a4f396ac0 100644 (file)
@@ -31,4 +31,3 @@ else:
     import linux.lists
     import linux.proc
     import linux.constants
-    import linux.radixtree
index 2660fbcf94d1e0e8affa72d4656584bc0aa06216..7798e1608f4f4bb429c94017e2c6f73b07a7afc9 100644 (file)
@@ -500,34 +500,34 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
 {
        struct common_audit_data sa;
        struct apparmor_audit_data aad = {0,};
-       char *command, *args = value;
+       char *command, *largs = NULL, *args = value;
        size_t arg_size;
        int error;
 
        if (size == 0)
                return -EINVAL;
-       /* args points to a PAGE_SIZE buffer, AppArmor requires that
-        * the buffer must be null terminated or have size <= PAGE_SIZE -1
-        * so that AppArmor can null terminate them
-        */
-       if (args[size - 1] != '\0') {
-               if (size == PAGE_SIZE)
-                       return -EINVAL;
-               args[size] = '\0';
-       }
-
        /* task can only write its own attributes */
        if (current != task)
                return -EACCES;
 
-       args = value;
+       /* AppArmor requires that the buffer must be null terminated atm */
+       if (args[size - 1] != '\0') {
+               /* null terminate */
+               largs = args = kmalloc(size + 1, GFP_KERNEL);
+               if (!args)
+                       return -ENOMEM;
+               memcpy(args, value, size);
+               args[size] = '\0';
+       }
+
+       error = -EINVAL;
        args = strim(args);
        command = strsep(&args, " ");
        if (!args)
-               return -EINVAL;
+               goto out;
        args = skip_spaces(args);
        if (!*args)
-               return -EINVAL;
+               goto out;
 
        arg_size = size - (args - (char *) value);
        if (strcmp(name, "current") == 0) {
@@ -553,10 +553,12 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
                        goto fail;
        } else
                /* only support the "current" and "exec" process attributes */
-               return -EINVAL;
+               goto fail;
 
        if (!error)
                error = size;
+out:
+       kfree(largs);
        return error;
 
 fail:
@@ -565,9 +567,9 @@ static int apparmor_setprocattr(struct task_struct *task, char *name,
        aad.profile = aa_current_profile();
        aad.op = OP_SETPROCATTR;
        aad.info = name;
-       aad.error = -EINVAL;
+       aad.error = error = -EINVAL;
        aa_audit_msg(AUDIT_APPARMOR_DENIED, &sa, NULL);
-       return -EINVAL;
+       goto out;
 }
 
 static int apparmor_task_setrlimit(struct task_struct *task,
index 9b3334be9df23c8f48a8768ee8b723318252873f..2c498488af6c5aebe12ec4a2cd7b432076b788af 100644 (file)
@@ -67,6 +67,8 @@ struct snd_compr_file {
        struct snd_compr_stream stream;
 };
 
+static void error_delayed_work(struct work_struct *work);
+
 /*
  * a note on stream states used:
  * we use following states in the compressed core
@@ -123,6 +125,9 @@ static int snd_compr_open(struct inode *inode, struct file *f)
                snd_card_unref(compr->card);
                return -ENOMEM;
        }
+
+       INIT_DELAYED_WORK(&data->stream.error_work, error_delayed_work);
+
        data->stream.ops = compr->ops;
        data->stream.direction = dirn;
        data->stream.private_data = compr->private_data;
@@ -153,6 +158,8 @@ static int snd_compr_free(struct inode *inode, struct file *f)
        struct snd_compr_file *data = f->private_data;
        struct snd_compr_runtime *runtime = data->stream.runtime;
 
+       cancel_delayed_work_sync(&data->stream.error_work);
+
        switch (runtime->state) {
        case SNDRV_PCM_STATE_RUNNING:
        case SNDRV_PCM_STATE_DRAINING:
@@ -237,6 +244,15 @@ snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg)
        avail = snd_compr_calc_avail(stream, &ioctl_avail);
        ioctl_avail.avail = avail;
 
+       switch (stream->runtime->state) {
+       case SNDRV_PCM_STATE_OPEN:
+               return -EBADFD;
+       case SNDRV_PCM_STATE_XRUN:
+               return -EPIPE;
+       default:
+               break;
+       }
+
        if (copy_to_user((__u64 __user *)arg,
                                &ioctl_avail, sizeof(ioctl_avail)))
                return -EFAULT;
@@ -346,11 +362,13 @@ static ssize_t snd_compr_read(struct file *f, char __user *buf,
        switch (stream->runtime->state) {
        case SNDRV_PCM_STATE_OPEN:
        case SNDRV_PCM_STATE_PREPARED:
-       case SNDRV_PCM_STATE_XRUN:
        case SNDRV_PCM_STATE_SUSPENDED:
        case SNDRV_PCM_STATE_DISCONNECTED:
                retval = -EBADFD;
                goto out;
+       case SNDRV_PCM_STATE_XRUN:
+               retval = -EPIPE;
+               goto out;
        }
 
        avail = snd_compr_get_avail(stream);
@@ -399,10 +417,16 @@ static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
        stream = &data->stream;
 
        mutex_lock(&stream->device->lock);
-       if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+
+       switch (stream->runtime->state) {
+       case SNDRV_PCM_STATE_OPEN:
+       case SNDRV_PCM_STATE_XRUN:
                retval = snd_compr_get_poll(stream) | POLLERR;
                goto out;
+       default:
+               break;
        }
+
        poll_wait(f, &stream->runtime->sleep, wait);
 
        avail = snd_compr_get_avail(stream);
@@ -697,6 +721,45 @@ static int snd_compr_stop(struct snd_compr_stream *stream)
        return retval;
 }
 
+static void error_delayed_work(struct work_struct *work)
+{
+       struct snd_compr_stream *stream;
+
+       stream = container_of(work, struct snd_compr_stream, error_work.work);
+
+       mutex_lock(&stream->device->lock);
+
+       stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
+       wake_up(&stream->runtime->sleep);
+
+       mutex_unlock(&stream->device->lock);
+}
+
+/*
+ * snd_compr_stop_error: Report a fatal error on a stream
+ * @stream: pointer to stream
+ * @state: state to transition the stream to
+ *
+ * Stop the stream and set its state.
+ *
+ * Should be called with compressed device lock held.
+ */
+int snd_compr_stop_error(struct snd_compr_stream *stream,
+                        snd_pcm_state_t state)
+{
+       if (stream->runtime->state == state)
+               return 0;
+
+       stream->runtime->state = state;
+
+       pr_debug("Changing state to: %d\n", state);
+
+       queue_delayed_work(system_power_efficient_wq, &stream->error_work, 0);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_compr_stop_error);
+
 static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
 {
        int ret;
index 06e099e802df94039328fdb677480cda521021fb..22aec9a1e9a4991026e98b40879e2064997b92a1 100644 (file)
@@ -10,6 +10,7 @@ if SND_ATMEL_SOC
 
 config SND_ATMEL_SOC_PDC
        tristate
+       depends on HAS_DMA
        default m if SND_ATMEL_SOC_SSC_PDC=m && SND_ATMEL_SOC_SSC=m
        default y if SND_ATMEL_SOC_SSC_PDC=y || (SND_ATMEL_SOC_SSC_PDC=m && SND_ATMEL_SOC_SSC=y)
 
index 6107de9c538b51d27c0f09888e14b56592fad68e..6d9b8b44e2dae93feedc644f0a0568c3dd883927 100644 (file)
@@ -593,11 +593,6 @@ static int atmel_classd_probe(struct platform_device *pdev)
        }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               dev_err(dev, "no memory resource\n");
-               return -ENXIO;
-       }
-
        io_base = devm_ioremap_resource(dev, res);
        if (IS_ERR(io_base)) {
                ret =  PTR_ERR(io_base);
index aee4787a0b89f4a4ad5b28f0981b7e67d201dbb5..5f56da60c92ff6398d3eba82ab2ce7e0b75fd649 100644 (file)
@@ -624,11 +624,6 @@ static int atmel_pdmic_probe(struct platform_device *pdev)
        }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               dev_err(dev, "no memory resource\n");
-               return -ENXIO;
-       }
-
        io_base = devm_ioremap_resource(dev, res);
        if (IS_ERR(io_base)) {
                ret = PTR_ERR(io_base);
index 1267e1af0fae420c694577b208c4643dcc3b41ad..54c09acd3fed06f4fba7ef602d0bd7466825c100 100644 (file)
@@ -321,7 +321,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
                return ret;
        }
 
-       dma_params = &ssc_dma_params[dai->id][dir];
+       dma_params = &ssc_dma_params[pdev->id][dir];
        dma_params->ssc = ssc_p->ssc;
        dma_params->substream = substream;
 
index 6a834e109f1de47e533d17e87cdc73d8ee86fc4d..d528aaceaad95bc6bae2147ebe66249629d2512e 100644 (file)
@@ -7,3 +7,12 @@ config SND_BCM2835_SOC_I2S
          Say Y or M if you want to add support for codecs attached to
          the BCM2835 I2S interface. You will also need
          to select the audio interfaces to support below.
+
+config SND_SOC_CYGNUS
+       tristate "SoC platform audio for Broadcom Cygnus chips"
+       depends on ARCH_BCM_CYGNUS || COMPILE_TEST
+       help
+         Say Y if you want to add support for ASoC audio on Broadcom
+         Cygnus chips (bcm958300, bcm958305, bcm911360)
+
+         If you don't know what to do here, say N.
\ No newline at end of file
index bc816b71e5a40958953bf7bf7e7bd08342e87738..fc739d0078842680802389500dea19334da8fb7b 100644 (file)
@@ -3,3 +3,8 @@ snd-soc-bcm2835-i2s-objs := bcm2835-i2s.o
 
 obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd-soc-bcm2835-i2s.o
 
+# CYGNUS Platform Support
+snd-soc-cygnus-objs := cygnus-pcm.o cygnus-ssp.o
+
+obj-$(CONFIG_SND_SOC_CYGNUS) += snd-soc-cygnus.o
+
diff --git a/sound/soc/bcm/cygnus-pcm.c b/sound/soc/bcm/cygnus-pcm.c
new file mode 100644 (file)
index 0000000..d616e09
--- /dev/null
@@ -0,0 +1,861 @@
+/*
+ * Copyright (C) 2014-2015 Broadcom Corporation
+ *
+ * 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.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/debugfs.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "cygnus-ssp.h"
+
+/* Register offset needed for ASoC PCM module */
+
+#define INTH_R5F_STATUS_OFFSET     0x040
+#define INTH_R5F_CLEAR_OFFSET      0x048
+#define INTH_R5F_MASK_SET_OFFSET   0x050
+#define INTH_R5F_MASK_CLEAR_OFFSET 0x054
+
+#define BF_REARM_FREE_MARK_OFFSET 0x344
+#define BF_REARM_FULL_MARK_OFFSET 0x348
+
+/* Ring Buffer Ctrl Regs --- Start */
+/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_RDADDR_REG_BASE */
+#define SRC_RBUF_0_RDADDR_OFFSET 0x500
+#define SRC_RBUF_1_RDADDR_OFFSET 0x518
+#define SRC_RBUF_2_RDADDR_OFFSET 0x530
+#define SRC_RBUF_3_RDADDR_OFFSET 0x548
+#define SRC_RBUF_4_RDADDR_OFFSET 0x560
+#define SRC_RBUF_5_RDADDR_OFFSET 0x578
+#define SRC_RBUF_6_RDADDR_OFFSET 0x590
+
+/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_WRADDR_REG_BASE */
+#define SRC_RBUF_0_WRADDR_OFFSET 0x504
+#define SRC_RBUF_1_WRADDR_OFFSET 0x51c
+#define SRC_RBUF_2_WRADDR_OFFSET 0x534
+#define SRC_RBUF_3_WRADDR_OFFSET 0x54c
+#define SRC_RBUF_4_WRADDR_OFFSET 0x564
+#define SRC_RBUF_5_WRADDR_OFFSET 0x57c
+#define SRC_RBUF_6_WRADDR_OFFSET 0x594
+
+/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_BASEADDR_REG_BASE */
+#define SRC_RBUF_0_BASEADDR_OFFSET 0x508
+#define SRC_RBUF_1_BASEADDR_OFFSET 0x520
+#define SRC_RBUF_2_BASEADDR_OFFSET 0x538
+#define SRC_RBUF_3_BASEADDR_OFFSET 0x550
+#define SRC_RBUF_4_BASEADDR_OFFSET 0x568
+#define SRC_RBUF_5_BASEADDR_OFFSET 0x580
+#define SRC_RBUF_6_BASEADDR_OFFSET 0x598
+
+/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_ENDADDR_REG_BASE */
+#define SRC_RBUF_0_ENDADDR_OFFSET 0x50c
+#define SRC_RBUF_1_ENDADDR_OFFSET 0x524
+#define SRC_RBUF_2_ENDADDR_OFFSET 0x53c
+#define SRC_RBUF_3_ENDADDR_OFFSET 0x554
+#define SRC_RBUF_4_ENDADDR_OFFSET 0x56c
+#define SRC_RBUF_5_ENDADDR_OFFSET 0x584
+#define SRC_RBUF_6_ENDADDR_OFFSET 0x59c
+
+/* AUD_FMM_BF_CTRL_SOURCECH_RINGBUF_X_FREE_MARK_REG_BASE */
+#define SRC_RBUF_0_FREE_MARK_OFFSET 0x510
+#define SRC_RBUF_1_FREE_MARK_OFFSET 0x528
+#define SRC_RBUF_2_FREE_MARK_OFFSET 0x540
+#define SRC_RBUF_3_FREE_MARK_OFFSET 0x558
+#define SRC_RBUF_4_FREE_MARK_OFFSET 0x570
+#define SRC_RBUF_5_FREE_MARK_OFFSET 0x588
+#define SRC_RBUF_6_FREE_MARK_OFFSET 0x5a0
+
+/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_RDADDR_REG_BASE */
+#define DST_RBUF_0_RDADDR_OFFSET 0x5c0
+#define DST_RBUF_1_RDADDR_OFFSET 0x5d8
+#define DST_RBUF_2_RDADDR_OFFSET 0x5f0
+#define DST_RBUF_3_RDADDR_OFFSET 0x608
+#define DST_RBUF_4_RDADDR_OFFSET 0x620
+#define DST_RBUF_5_RDADDR_OFFSET 0x638
+
+/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_WRADDR_REG_BASE */
+#define DST_RBUF_0_WRADDR_OFFSET 0x5c4
+#define DST_RBUF_1_WRADDR_OFFSET 0x5dc
+#define DST_RBUF_2_WRADDR_OFFSET 0x5f4
+#define DST_RBUF_3_WRADDR_OFFSET 0x60c
+#define DST_RBUF_4_WRADDR_OFFSET 0x624
+#define DST_RBUF_5_WRADDR_OFFSET 0x63c
+
+/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_BASEADDR_REG_BASE */
+#define DST_RBUF_0_BASEADDR_OFFSET 0x5c8
+#define DST_RBUF_1_BASEADDR_OFFSET 0x5e0
+#define DST_RBUF_2_BASEADDR_OFFSET 0x5f8
+#define DST_RBUF_3_BASEADDR_OFFSET 0x610
+#define DST_RBUF_4_BASEADDR_OFFSET 0x628
+#define DST_RBUF_5_BASEADDR_OFFSET 0x640
+
+/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_ENDADDR_REG_BASE */
+#define DST_RBUF_0_ENDADDR_OFFSET 0x5cc
+#define DST_RBUF_1_ENDADDR_OFFSET 0x5e4
+#define DST_RBUF_2_ENDADDR_OFFSET 0x5fc
+#define DST_RBUF_3_ENDADDR_OFFSET 0x614
+#define DST_RBUF_4_ENDADDR_OFFSET 0x62c
+#define DST_RBUF_5_ENDADDR_OFFSET 0x644
+
+/* AUD_FMM_BF_CTRL_DESTCH_RINGBUF_X_FULL_MARK_REG_BASE */
+#define DST_RBUF_0_FULL_MARK_OFFSET 0x5d0
+#define DST_RBUF_1_FULL_MARK_OFFSET 0x5e8
+#define DST_RBUF_2_FULL_MARK_OFFSET 0x600
+#define DST_RBUF_3_FULL_MARK_OFFSET 0x618
+#define DST_RBUF_4_FULL_MARK_OFFSET 0x630
+#define DST_RBUF_5_FULL_MARK_OFFSET 0x648
+/* Ring Buffer Ctrl Regs --- End */
+
+/* Error Status Regs --- Start */
+/* AUD_FMM_BF_ESR_ESRX_STATUS_REG_BASE */
+#define ESR0_STATUS_OFFSET 0x900
+#define ESR1_STATUS_OFFSET 0x918
+#define ESR2_STATUS_OFFSET 0x930
+#define ESR3_STATUS_OFFSET 0x948
+#define ESR4_STATUS_OFFSET 0x960
+
+/* AUD_FMM_BF_ESR_ESRX_STATUS_CLEAR_REG_BASE */
+#define ESR0_STATUS_CLR_OFFSET 0x908
+#define ESR1_STATUS_CLR_OFFSET 0x920
+#define ESR2_STATUS_CLR_OFFSET 0x938
+#define ESR3_STATUS_CLR_OFFSET 0x950
+#define ESR4_STATUS_CLR_OFFSET 0x968
+
+/* AUD_FMM_BF_ESR_ESRX_MASK_REG_BASE */
+#define ESR0_MASK_STATUS_OFFSET 0x90c
+#define ESR1_MASK_STATUS_OFFSET 0x924
+#define ESR2_MASK_STATUS_OFFSET 0x93c
+#define ESR3_MASK_STATUS_OFFSET 0x954
+#define ESR4_MASK_STATUS_OFFSET 0x96c
+
+/* AUD_FMM_BF_ESR_ESRX_MASK_SET_REG_BASE */
+#define ESR0_MASK_SET_OFFSET 0x910
+#define ESR1_MASK_SET_OFFSET 0x928
+#define ESR2_MASK_SET_OFFSET 0x940
+#define ESR3_MASK_SET_OFFSET 0x958
+#define ESR4_MASK_SET_OFFSET 0x970
+
+/* AUD_FMM_BF_ESR_ESRX_MASK_CLEAR_REG_BASE */
+#define ESR0_MASK_CLR_OFFSET 0x914
+#define ESR1_MASK_CLR_OFFSET 0x92c
+#define ESR2_MASK_CLR_OFFSET 0x944
+#define ESR3_MASK_CLR_OFFSET 0x95c
+#define ESR4_MASK_CLR_OFFSET 0x974
+/* Error Status Regs --- End */
+
+#define R5F_ESR0_SHIFT  0    /* esr0 = fifo underflow */
+#define R5F_ESR1_SHIFT  1    /* esr1 = ringbuf underflow */
+#define R5F_ESR2_SHIFT  2    /* esr2 = ringbuf overflow */
+#define R5F_ESR3_SHIFT  3    /* esr3 = freemark */
+#define R5F_ESR4_SHIFT  4    /* esr4 = fullmark */
+
+
+/* Mask for R5F register.  Set all relevant interrupt for playback handler */
+#define ANY_PLAYBACK_IRQ  (BIT(R5F_ESR0_SHIFT) | \
+                          BIT(R5F_ESR1_SHIFT) | \
+                          BIT(R5F_ESR3_SHIFT))
+
+/* Mask for R5F register.  Set all relevant interrupt for capture handler */
+#define ANY_CAPTURE_IRQ   (BIT(R5F_ESR2_SHIFT) | BIT(R5F_ESR4_SHIFT))
+
+/*
+ * PERIOD_BYTES_MIN is the number of bytes to at which the interrupt will tick.
+ * This number should be a multiple of 256. Minimum value is 256
+ */
+#define PERIOD_BYTES_MIN 0x100
+
+static const struct snd_pcm_hardware cygnus_pcm_hw = {
+       .info = SNDRV_PCM_INFO_MMAP |
+                       SNDRV_PCM_INFO_MMAP_VALID |
+                       SNDRV_PCM_INFO_INTERLEAVED,
+       .formats = SNDRV_PCM_FMTBIT_S16_LE |
+                       SNDRV_PCM_FMTBIT_S32_LE,
+
+       /* A period is basically an interrupt */
+       .period_bytes_min = PERIOD_BYTES_MIN,
+       .period_bytes_max = 0x10000,
+
+       /* period_min/max gives range of approx interrupts per buffer */
+       .periods_min = 2,
+       .periods_max = 8,
+
+       /*
+        * maximum buffer size in bytes = period_bytes_max * periods_max
+        * We allocate this amount of data for each enabled channel
+        */
+       .buffer_bytes_max = 4 * 0x8000,
+};
+
+static u64 cygnus_dma_dmamask = DMA_BIT_MASK(32);
+
+static struct cygnus_aio_port *cygnus_dai_get_dma_data(
+                               struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+
+       return snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream);
+}
+
+static void ringbuf_set_initial(void __iomem *audio_io,
+               struct ringbuf_regs *p_rbuf,
+               bool is_playback,
+               u32 start,
+               u32 periodsize,
+               u32 bufsize)
+{
+       u32 initial_rd;
+       u32 initial_wr;
+       u32 end;
+       u32 fmark_val; /* free or full mark */
+
+       p_rbuf->period_bytes = periodsize;
+       p_rbuf->buf_size = bufsize;
+
+       if (is_playback) {
+               /* Set the pointers to indicate full (flip uppermost bit) */
+               initial_rd = start;
+               initial_wr = initial_rd ^ BIT(31);
+       } else {
+               /* Set the pointers to indicate empty */
+               initial_wr = start;
+               initial_rd = initial_wr;
+       }
+
+       end = start + bufsize - 1;
+
+       /*
+        * The interrupt will fire when free/full mark is *exceeded*
+        * The fmark value must be multiple of PERIOD_BYTES_MIN so set fmark
+        * to be PERIOD_BYTES_MIN less than the period size.
+        */
+       fmark_val = periodsize - PERIOD_BYTES_MIN;
+
+       writel(start, audio_io + p_rbuf->baseaddr);
+       writel(end, audio_io + p_rbuf->endaddr);
+       writel(fmark_val, audio_io + p_rbuf->fmark);
+       writel(initial_rd, audio_io + p_rbuf->rdaddr);
+       writel(initial_wr, audio_io + p_rbuf->wraddr);
+}
+
+static int configure_ringbuf_regs(struct snd_pcm_substream *substream)
+{
+       struct cygnus_aio_port *aio;
+       struct ringbuf_regs *p_rbuf;
+       int status = 0;
+
+       aio = cygnus_dai_get_dma_data(substream);
+
+       /* Map the ssp portnum to a set of ring buffers. */
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               p_rbuf = &aio->play_rb_regs;
+
+               switch (aio->portnum) {
+               case 0:
+                       *p_rbuf = RINGBUF_REG_PLAYBACK(0);
+                       break;
+               case 1:
+                       *p_rbuf = RINGBUF_REG_PLAYBACK(2);
+                       break;
+               case 2:
+                       *p_rbuf = RINGBUF_REG_PLAYBACK(4);
+                       break;
+               case 3: /* SPDIF */
+                       *p_rbuf = RINGBUF_REG_PLAYBACK(6);
+                       break;
+               default:
+                       status = -EINVAL;
+               }
+       } else {
+               p_rbuf = &aio->capture_rb_regs;
+
+               switch (aio->portnum) {
+               case 0:
+                       *p_rbuf = RINGBUF_REG_CAPTURE(0);
+                       break;
+               case 1:
+                       *p_rbuf = RINGBUF_REG_CAPTURE(2);
+                       break;
+               case 2:
+                       *p_rbuf = RINGBUF_REG_CAPTURE(4);
+                       break;
+               default:
+                       status = -EINVAL;
+               }
+       }
+
+       return status;
+}
+
+static struct ringbuf_regs *get_ringbuf(struct snd_pcm_substream *substream)
+{
+       struct cygnus_aio_port *aio;
+       struct ringbuf_regs *p_rbuf = NULL;
+
+       aio = cygnus_dai_get_dma_data(substream);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               p_rbuf = &aio->play_rb_regs;
+       else
+               p_rbuf = &aio->capture_rb_regs;
+
+       return p_rbuf;
+}
+
+static void enable_intr(struct snd_pcm_substream *substream)
+{
+       struct cygnus_aio_port *aio;
+       u32 clear_mask;
+
+       aio = cygnus_dai_get_dma_data(substream);
+
+       /* The port number maps to the bit position to be cleared */
+       clear_mask = BIT(aio->portnum);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               /* Clear interrupt status before enabling them */
+               writel(clear_mask, aio->cygaud->audio + ESR0_STATUS_CLR_OFFSET);
+               writel(clear_mask, aio->cygaud->audio + ESR1_STATUS_CLR_OFFSET);
+               writel(clear_mask, aio->cygaud->audio + ESR3_STATUS_CLR_OFFSET);
+               /* Unmask the interrupts of the given port*/
+               writel(clear_mask, aio->cygaud->audio + ESR0_MASK_CLR_OFFSET);
+               writel(clear_mask, aio->cygaud->audio + ESR1_MASK_CLR_OFFSET);
+               writel(clear_mask, aio->cygaud->audio + ESR3_MASK_CLR_OFFSET);
+
+               writel(ANY_PLAYBACK_IRQ,
+                       aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET);
+       } else {
+               writel(clear_mask, aio->cygaud->audio + ESR2_STATUS_CLR_OFFSET);
+               writel(clear_mask, aio->cygaud->audio + ESR4_STATUS_CLR_OFFSET);
+               writel(clear_mask, aio->cygaud->audio + ESR2_MASK_CLR_OFFSET);
+               writel(clear_mask, aio->cygaud->audio + ESR4_MASK_CLR_OFFSET);
+
+               writel(ANY_CAPTURE_IRQ,
+                       aio->cygaud->audio + INTH_R5F_MASK_CLEAR_OFFSET);
+       }
+
+}
+
+static void disable_intr(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct cygnus_aio_port *aio;
+       u32 set_mask;
+
+       aio = cygnus_dai_get_dma_data(substream);
+
+       dev_dbg(rtd->cpu_dai->dev, "%s on port %d\n", __func__, aio->portnum);
+
+       /* The port number maps to the bit position to be set */
+       set_mask = BIT(aio->portnum);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               /* Mask the interrupts of the given port*/
+               writel(set_mask, aio->cygaud->audio + ESR0_MASK_SET_OFFSET);
+               writel(set_mask, aio->cygaud->audio + ESR1_MASK_SET_OFFSET);
+               writel(set_mask, aio->cygaud->audio + ESR3_MASK_SET_OFFSET);
+       } else {
+               writel(set_mask, aio->cygaud->audio + ESR2_MASK_SET_OFFSET);
+               writel(set_mask, aio->cygaud->audio + ESR4_MASK_SET_OFFSET);
+       }
+
+}
+
+static int cygnus_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       int ret = 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+               enable_intr(substream);
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               disable_intr(substream);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static void cygnus_pcm_period_elapsed(struct snd_pcm_substream *substream)
+{
+       struct cygnus_aio_port *aio;
+       struct ringbuf_regs *p_rbuf = NULL;
+       u32 regval;
+
+       aio = cygnus_dai_get_dma_data(substream);
+
+       p_rbuf = get_ringbuf(substream);
+
+       /*
+        * If free/full mark interrupt occurs, provide timestamp
+        * to ALSA and update appropriate idx by period_bytes
+        */
+       snd_pcm_period_elapsed(substream);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               /* Set the ring buffer to full */
+               regval = readl(aio->cygaud->audio + p_rbuf->rdaddr);
+               regval = regval ^ BIT(31);
+               writel(regval, aio->cygaud->audio + p_rbuf->wraddr);
+       } else {
+               /* Set the ring buffer to empty */
+               regval = readl(aio->cygaud->audio + p_rbuf->wraddr);
+               writel(regval, aio->cygaud->audio + p_rbuf->rdaddr);
+       }
+}
+
+/*
+ * ESR0/1/3 status  Description
+ *  0x1        I2S0_out port caused interrupt
+ *  0x2        I2S1_out port caused interrupt
+ *  0x4        I2S2_out port caused interrupt
+ *  0x8        SPDIF_out port caused interrupt
+ */
+static void handle_playback_irq(struct cygnus_audio *cygaud)
+{
+       void __iomem *audio_io;
+       u32 port;
+       u32 esr_status0, esr_status1, esr_status3;
+
+       audio_io = cygaud->audio;
+
+       /*
+        * ESR status gets updates with/without interrupts enabled.
+        * So, check the ESR mask, which provides interrupt enable/
+        * disable status and use it to determine which ESR status
+        * should be serviced.
+        */
+       esr_status0 = readl(audio_io + ESR0_STATUS_OFFSET);
+       esr_status0 &= ~readl(audio_io + ESR0_MASK_STATUS_OFFSET);
+       esr_status1 = readl(audio_io + ESR1_STATUS_OFFSET);
+       esr_status1 &= ~readl(audio_io + ESR1_MASK_STATUS_OFFSET);
+       esr_status3 = readl(audio_io + ESR3_STATUS_OFFSET);
+       esr_status3 &= ~readl(audio_io + ESR3_MASK_STATUS_OFFSET);
+
+       for (port = 0; port < CYGNUS_MAX_PLAYBACK_PORTS; port++) {
+               u32 esrmask = BIT(port);
+
+               /*
+                * Ringbuffer or FIFO underflow
+                * If we get this interrupt then, it is also true that we have
+                * not yet responded to the freemark interrupt.
+                * Log a debug message.  The freemark handler below will
+                * handle getting everything going again.
+                */
+               if ((esrmask & esr_status1) || (esrmask & esr_status0)) {
+                       dev_dbg(cygaud->dev,
+                               "Underrun: esr0=0x%x, esr1=0x%x esr3=0x%x\n",
+                               esr_status0, esr_status1, esr_status3);
+               }
+
+               /*
+                * Freemark is hit. This is the normal interrupt.
+                * In typical operation the read and write regs will be equal
+                */
+               if (esrmask & esr_status3) {
+                       struct snd_pcm_substream *playstr;
+
+                       playstr = cygaud->portinfo[port].play_stream;
+                       cygnus_pcm_period_elapsed(playstr);
+               }
+       }
+
+       /* Clear ESR interrupt */
+       writel(esr_status0, audio_io + ESR0_STATUS_CLR_OFFSET);
+       writel(esr_status1, audio_io + ESR1_STATUS_CLR_OFFSET);
+       writel(esr_status3, audio_io + ESR3_STATUS_CLR_OFFSET);
+       /* Rearm freemark logic by writing 1 to the correct bit */
+       writel(esr_status3, audio_io + BF_REARM_FREE_MARK_OFFSET);
+}
+
+/*
+ * ESR2/4 status  Description
+ *  0x1        I2S0_in port caused interrupt
+ *  0x2        I2S1_in port caused interrupt
+ *  0x4        I2S2_in port caused interrupt
+ */
+static void handle_capture_irq(struct cygnus_audio *cygaud)
+{
+       void __iomem *audio_io;
+       u32 port;
+       u32 esr_status2, esr_status4;
+
+       audio_io = cygaud->audio;
+
+       /*
+        * ESR status gets updates with/without interrupts enabled.
+        * So, check the ESR mask, which provides interrupt enable/
+        * disable status and use it to determine which ESR status
+        * should be serviced.
+        */
+       esr_status2 = readl(audio_io + ESR2_STATUS_OFFSET);
+       esr_status2 &= ~readl(audio_io + ESR2_MASK_STATUS_OFFSET);
+       esr_status4 = readl(audio_io + ESR4_STATUS_OFFSET);
+       esr_status4 &= ~readl(audio_io + ESR4_MASK_STATUS_OFFSET);
+
+       for (port = 0; port < CYGNUS_MAX_CAPTURE_PORTS; port++) {
+               u32 esrmask = BIT(port);
+
+               /*
+                * Ringbuffer or FIFO overflow
+                * If we get this interrupt then, it is also true that we have
+                * not yet responded to the fullmark interrupt.
+                * Log a debug message.  The fullmark handler below will
+                * handle getting everything going again.
+                */
+               if (esrmask & esr_status2)
+                       dev_dbg(cygaud->dev,
+                               "Overflow: esr2=0x%x\n", esr_status2);
+
+               if (esrmask & esr_status4) {
+                       struct snd_pcm_substream *capstr;
+
+                       capstr = cygaud->portinfo[port].capture_stream;
+                       cygnus_pcm_period_elapsed(capstr);
+               }
+       }
+
+       writel(esr_status2, audio_io + ESR2_STATUS_CLR_OFFSET);
+       writel(esr_status4, audio_io + ESR4_STATUS_CLR_OFFSET);
+       /* Rearm fullmark logic by writing 1 to the correct bit */
+       writel(esr_status4, audio_io + BF_REARM_FULL_MARK_OFFSET);
+}
+
+static irqreturn_t cygnus_dma_irq(int irq, void *data)
+{
+       u32 r5_status;
+       struct cygnus_audio *cygaud = data;
+
+       /*
+        * R5 status bits       Description
+        *  0           ESR0 (playback FIFO interrupt)
+        *  1           ESR1 (playback rbuf interrupt)
+        *  2           ESR2 (capture rbuf interrupt)
+        *  3           ESR3 (Freemark play. interrupt)
+        *  4           ESR4 (Fullmark capt. interrupt)
+        */
+       r5_status = readl(cygaud->audio + INTH_R5F_STATUS_OFFSET);
+
+       if (!(r5_status & (ANY_PLAYBACK_IRQ | ANY_CAPTURE_IRQ)))
+               return IRQ_NONE;
+
+       /* If playback interrupt happened */
+       if (ANY_PLAYBACK_IRQ & r5_status) {
+               handle_playback_irq(cygaud);
+               writel(ANY_PLAYBACK_IRQ & r5_status,
+                       cygaud->audio + INTH_R5F_CLEAR_OFFSET);
+       }
+
+       /* If  capture interrupt happened */
+       if (ANY_CAPTURE_IRQ & r5_status) {
+               handle_capture_irq(cygaud);
+               writel(ANY_CAPTURE_IRQ & r5_status,
+                       cygaud->audio + INTH_R5F_CLEAR_OFFSET);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int cygnus_pcm_open(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct cygnus_aio_port *aio;
+       int ret;
+
+       aio = cygnus_dai_get_dma_data(substream);
+       if (!aio)
+               return -ENODEV;
+
+       dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum);
+
+       snd_soc_set_runtime_hwparams(substream, &cygnus_pcm_hw);
+
+       ret = snd_pcm_hw_constraint_step(runtime, 0,
+               SNDRV_PCM_HW_PARAM_PERIOD_BYTES, PERIOD_BYTES_MIN);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_pcm_hw_constraint_step(runtime, 0,
+               SNDRV_PCM_HW_PARAM_BUFFER_BYTES, PERIOD_BYTES_MIN);
+       if (ret < 0)
+               return ret;
+       /*
+        * Keep track of which substream belongs to which port.
+        * This info is needed by snd_pcm_period_elapsed() in irq_handler
+        */
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               aio->play_stream = substream;
+       else
+               aio->capture_stream = substream;
+
+       return 0;
+}
+
+static int cygnus_pcm_close(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct cygnus_aio_port *aio;
+
+       aio = cygnus_dai_get_dma_data(substream);
+
+       dev_dbg(rtd->cpu_dai->dev, "%s  port %d\n", __func__, aio->portnum);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               aio->play_stream = NULL;
+       else
+               aio->capture_stream = NULL;
+
+       if (!aio->play_stream && !aio->capture_stream)
+               dev_dbg(rtd->cpu_dai->dev, "freed  port %d\n", aio->portnum);
+
+       return 0;
+}
+
+static int cygnus_pcm_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct cygnus_aio_port *aio;
+       int ret = 0;
+
+       aio = cygnus_dai_get_dma_data(substream);
+       dev_dbg(rtd->cpu_dai->dev, "%s  port %d\n", __func__, aio->portnum);
+
+       snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+       runtime->dma_bytes = params_buffer_bytes(params);
+
+       return ret;
+}
+
+static int cygnus_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct cygnus_aio_port *aio;
+
+       aio = cygnus_dai_get_dma_data(substream);
+       dev_dbg(rtd->cpu_dai->dev, "%s  port %d\n", __func__, aio->portnum);
+
+       snd_pcm_set_runtime_buffer(substream, NULL);
+       return 0;
+}
+
+static int cygnus_pcm_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct cygnus_aio_port *aio;
+       unsigned long bufsize, periodsize;
+       int ret = 0;
+       bool is_play;
+       u32 start;
+       struct ringbuf_regs *p_rbuf = NULL;
+
+       aio = cygnus_dai_get_dma_data(substream);
+       dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum);
+
+       bufsize = snd_pcm_lib_buffer_bytes(substream);
+       periodsize = snd_pcm_lib_period_bytes(substream);
+
+       dev_dbg(rtd->cpu_dai->dev, "%s (buf_size %lu) (period_size %lu)\n",
+                       __func__, bufsize, periodsize);
+
+       configure_ringbuf_regs(substream);
+
+       p_rbuf = get_ringbuf(substream);
+
+       start = runtime->dma_addr;
+
+       is_play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 1 : 0;
+
+       ringbuf_set_initial(aio->cygaud->audio, p_rbuf, is_play, start,
+                               periodsize, bufsize);
+
+       return ret;
+}
+
+static snd_pcm_uframes_t cygnus_pcm_pointer(struct snd_pcm_substream *substream)
+{
+       struct cygnus_aio_port *aio;
+       unsigned int res = 0, cur = 0, base = 0;
+       struct ringbuf_regs *p_rbuf = NULL;
+
+       aio = cygnus_dai_get_dma_data(substream);
+
+       /*
+        * Get the offset of the current read (for playack) or write
+        * index (for capture).  Report this value back to the asoc framework.
+        */
+       p_rbuf = get_ringbuf(substream);
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               cur = readl(aio->cygaud->audio + p_rbuf->rdaddr);
+       else
+               cur = readl(aio->cygaud->audio + p_rbuf->wraddr);
+
+       base = readl(aio->cygaud->audio + p_rbuf->baseaddr);
+
+       /*
+        * Mask off the MSB of the rdaddr,wraddr and baseaddr
+        * since MSB is not part of the address
+        */
+       res = (cur & 0x7fffffff) - (base & 0x7fffffff);
+
+       return bytes_to_frames(substream->runtime, res);
+}
+
+static int cygnus_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+       struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_dma_buffer *buf = &substream->dma_buffer;
+       size_t size;
+
+       size = cygnus_pcm_hw.buffer_bytes_max;
+
+       buf->dev.type = SNDRV_DMA_TYPE_DEV;
+       buf->dev.dev = pcm->card->dev;
+       buf->private_data = NULL;
+       buf->area = dma_alloc_coherent(pcm->card->dev, size,
+                       &buf->addr, GFP_KERNEL);
+
+       dev_dbg(rtd->cpu_dai->dev, "%s: size 0x%zx @ %pK\n",
+                               __func__, size, buf->area);
+
+       if (!buf->area) {
+               dev_err(rtd->cpu_dai->dev, "%s: dma_alloc failed\n", __func__);
+               return -ENOMEM;
+       }
+       buf->bytes = size;
+
+       return 0;
+}
+
+
+static const struct snd_pcm_ops cygnus_pcm_ops = {
+       .open           = cygnus_pcm_open,
+       .close          = cygnus_pcm_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = cygnus_pcm_hw_params,
+       .hw_free        = cygnus_pcm_hw_free,
+       .prepare        = cygnus_pcm_prepare,
+       .trigger        = cygnus_pcm_trigger,
+       .pointer        = cygnus_pcm_pointer,
+};
+
+static void cygnus_dma_free_dma_buffers(struct snd_pcm *pcm)
+{
+       struct snd_pcm_substream *substream;
+       struct snd_dma_buffer *buf;
+
+       substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+       if (substream) {
+               buf = &substream->dma_buffer;
+               if (buf->area) {
+                       dma_free_coherent(pcm->card->dev, buf->bytes,
+                               buf->area, buf->addr);
+                       buf->area = NULL;
+               }
+       }
+
+       substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+       if (substream) {
+               buf = &substream->dma_buffer;
+               if (buf->area) {
+                       dma_free_coherent(pcm->card->dev, buf->bytes,
+                               buf->area, buf->addr);
+                       buf->area = NULL;
+               }
+       }
+}
+
+static int cygnus_dma_new(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_pcm *pcm = rtd->pcm;
+       int ret;
+
+       if (!card->dev->dma_mask)
+               card->dev->dma_mask = &cygnus_dma_dmamask;
+       if (!card->dev->coherent_dma_mask)
+               card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+       if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+               ret = cygnus_pcm_preallocate_dma_buffer(pcm,
+                               SNDRV_PCM_STREAM_PLAYBACK);
+               if (ret)
+                       return ret;
+       }
+
+       if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+               ret = cygnus_pcm_preallocate_dma_buffer(pcm,
+                               SNDRV_PCM_STREAM_CAPTURE);
+               if (ret) {
+                       cygnus_dma_free_dma_buffers(pcm);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static struct snd_soc_platform_driver cygnus_soc_platform = {
+       .ops            = &cygnus_pcm_ops,
+       .pcm_new        = cygnus_dma_new,
+       .pcm_free       = cygnus_dma_free_dma_buffers,
+};
+
+int cygnus_soc_platform_register(struct device *dev,
+                                struct cygnus_audio *cygaud)
+{
+       int rc = 0;
+
+       dev_dbg(dev, "%s Enter\n", __func__);
+
+       rc = devm_request_irq(dev, cygaud->irq_num, cygnus_dma_irq,
+                               IRQF_SHARED, "cygnus-audio", cygaud);
+       if (rc) {
+               dev_err(dev, "%s request_irq error %d\n", __func__, rc);
+               return rc;
+       }
+
+       rc = snd_soc_register_platform(dev, &cygnus_soc_platform);
+       if (rc) {
+               dev_err(dev, "%s failed\n", __func__);
+               return rc;
+       }
+
+       return 0;
+}
+
+int cygnus_soc_platform_unregister(struct device *dev)
+{
+       snd_soc_unregister_platform(dev);
+
+       return 0;
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Broadcom");
+MODULE_DESCRIPTION("Cygnus ASoC PCM module");
diff --git a/sound/soc/bcm/cygnus-ssp.c b/sound/soc/bcm/cygnus-ssp.c
new file mode 100644 (file)
index 0000000..e710bb0
--- /dev/null
@@ -0,0 +1,1529 @@
+/*
+ * Copyright (C) 2014-2015 Broadcom Corporation
+ *
+ * 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.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "cygnus-ssp.h"
+
+#define DEFAULT_VCO    1354750204
+
+#define CYGNUS_TDM_RATE \
+               (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
+               SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050 | \
+               SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+               SNDRV_PCM_RATE_48000)
+
+#define CAPTURE_FCI_ID_BASE 0x180
+#define CYGNUS_SSP_TRISTATE_MASK 0x001fff
+#define CYGNUS_PLLCLKSEL_MASK 0xf
+
+/* Used with stream_on field to indicate which streams are active */
+#define  PLAYBACK_STREAM_MASK   BIT(0)
+#define  CAPTURE_STREAM_MASK    BIT(1)
+
+#define I2S_STREAM_CFG_MASK      0xff003ff
+#define I2S_CAP_STREAM_CFG_MASK  0xf0
+#define SPDIF_STREAM_CFG_MASK    0x3ff
+#define CH_GRP_STEREO            0x1
+
+/* Begin register offset defines */
+#define AUD_MISC_SEROUT_OE_REG_BASE  0x01c
+#define AUD_MISC_SEROUT_SPDIF_OE  12
+#define AUD_MISC_SEROUT_MCLK_OE   3
+#define AUD_MISC_SEROUT_LRCK_OE   2
+#define AUD_MISC_SEROUT_SCLK_OE   1
+#define AUD_MISC_SEROUT_SDAT_OE   0
+
+/* AUD_FMM_BF_CTRL_xxx regs */
+#define BF_DST_CFG0_OFFSET  0x100
+#define BF_DST_CFG1_OFFSET  0x104
+#define BF_DST_CFG2_OFFSET  0x108
+
+#define BF_DST_CTRL0_OFFSET 0x130
+#define BF_DST_CTRL1_OFFSET 0x134
+#define BF_DST_CTRL2_OFFSET 0x138
+
+#define BF_SRC_CFG0_OFFSET  0x148
+#define BF_SRC_CFG1_OFFSET  0x14c
+#define BF_SRC_CFG2_OFFSET  0x150
+#define BF_SRC_CFG3_OFFSET  0x154
+
+#define BF_SRC_CTRL0_OFFSET 0x1c0
+#define BF_SRC_CTRL1_OFFSET 0x1c4
+#define BF_SRC_CTRL2_OFFSET 0x1c8
+#define BF_SRC_CTRL3_OFFSET 0x1cc
+
+#define BF_SRC_GRP0_OFFSET  0x1fc
+#define BF_SRC_GRP1_OFFSET  0x200
+#define BF_SRC_GRP2_OFFSET  0x204
+#define BF_SRC_GRP3_OFFSET  0x208
+
+#define BF_SRC_GRP_EN_OFFSET        0x320
+#define BF_SRC_GRP_FLOWON_OFFSET    0x324
+#define BF_SRC_GRP_SYNC_DIS_OFFSET  0x328
+
+/* AUD_FMM_IOP_OUT_I2S_xxx regs */
+#define OUT_I2S_0_STREAM_CFG_OFFSET 0xa00
+#define OUT_I2S_0_CFG_OFFSET        0xa04
+#define OUT_I2S_0_MCLK_CFG_OFFSET   0xa0c
+
+#define OUT_I2S_1_STREAM_CFG_OFFSET 0xa40
+#define OUT_I2S_1_CFG_OFFSET        0xa44
+#define OUT_I2S_1_MCLK_CFG_OFFSET   0xa4c
+
+#define OUT_I2S_2_STREAM_CFG_OFFSET 0xa80
+#define OUT_I2S_2_CFG_OFFSET        0xa84
+#define OUT_I2S_2_MCLK_CFG_OFFSET   0xa8c
+
+/* AUD_FMM_IOP_OUT_SPDIF_xxx regs */
+#define SPDIF_STREAM_CFG_OFFSET  0xac0
+#define SPDIF_CTRL_OFFSET        0xac4
+#define SPDIF_FORMAT_CFG_OFFSET  0xad8
+#define SPDIF_MCLK_CFG_OFFSET    0xadc
+
+/* AUD_FMM_IOP_PLL_0_xxx regs */
+#define IOP_PLL_0_MACRO_OFFSET    0xb00
+#define IOP_PLL_0_MDIV_Ch0_OFFSET 0xb14
+#define IOP_PLL_0_MDIV_Ch1_OFFSET 0xb18
+#define IOP_PLL_0_MDIV_Ch2_OFFSET 0xb1c
+
+#define IOP_PLL_0_ACTIVE_MDIV_Ch0_OFFSET 0xb30
+#define IOP_PLL_0_ACTIVE_MDIV_Ch1_OFFSET 0xb34
+#define IOP_PLL_0_ACTIVE_MDIV_Ch2_OFFSET 0xb38
+
+/* AUD_FMM_IOP_xxx regs */
+#define IOP_PLL_0_CONTROL_OFFSET     0xb04
+#define IOP_PLL_0_USER_NDIV_OFFSET   0xb08
+#define IOP_PLL_0_ACTIVE_NDIV_OFFSET 0xb20
+#define IOP_PLL_0_RESET_OFFSET       0xb5c
+
+/* AUD_FMM_IOP_IN_I2S_xxx regs */
+#define IN_I2S_0_STREAM_CFG_OFFSET 0x00
+#define IN_I2S_0_CFG_OFFSET        0x04
+#define IN_I2S_1_STREAM_CFG_OFFSET 0x40
+#define IN_I2S_1_CFG_OFFSET        0x44
+#define IN_I2S_2_STREAM_CFG_OFFSET 0x80
+#define IN_I2S_2_CFG_OFFSET        0x84
+
+/* AUD_FMM_IOP_MISC_xxx regs */
+#define IOP_SW_INIT_LOGIC          0x1c0
+
+/* End register offset defines */
+
+
+/* AUD_FMM_IOP_OUT_I2S_x_MCLK_CFG_0_REG */
+#define I2S_OUT_MCLKRATE_SHIFT 16
+
+/* AUD_FMM_IOP_OUT_I2S_x_MCLK_CFG_REG */
+#define I2S_OUT_PLLCLKSEL_SHIFT  0
+
+/* AUD_FMM_IOP_OUT_I2S_x_STREAM_CFG */
+#define I2S_OUT_STREAM_ENA  31
+#define I2S_OUT_STREAM_CFG_GROUP_ID  20
+#define I2S_OUT_STREAM_CFG_CHANNEL_GROUPING  24
+
+/* AUD_FMM_IOP_IN_I2S_x_CAP */
+#define I2S_IN_STREAM_CFG_CAP_ENA   31
+#define I2S_IN_STREAM_CFG_0_GROUP_ID 4
+
+/* AUD_FMM_IOP_OUT_I2S_x_I2S_CFG_REG */
+#define I2S_OUT_CFGX_CLK_ENA         0
+#define I2S_OUT_CFGX_DATA_ENABLE     1
+#define I2S_OUT_CFGX_DATA_ALIGNMENT  6
+#define I2S_OUT_CFGX_BITS_PER_SLOT  13
+#define I2S_OUT_CFGX_VALID_SLOT     14
+#define I2S_OUT_CFGX_FSYNC_WIDTH    18
+#define I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32 26
+#define I2S_OUT_CFGX_SLAVE_MODE     30
+#define I2S_OUT_CFGX_TDM_MODE       31
+
+/* AUD_FMM_BF_CTRL_SOURCECH_CFGx_REG */
+#define BF_SRC_CFGX_SFIFO_ENA              0
+#define BF_SRC_CFGX_BUFFER_PAIR_ENABLE     1
+#define BF_SRC_CFGX_SAMPLE_CH_MODE         2
+#define BF_SRC_CFGX_SFIFO_SZ_DOUBLE        5
+#define BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY  10
+#define BF_SRC_CFGX_BIT_RES               20
+#define BF_SRC_CFGX_PROCESS_SEQ_ID_VALID  31
+
+/* AUD_FMM_BF_CTRL_DESTCH_CFGx_REG */
+#define BF_DST_CFGX_CAP_ENA              0
+#define BF_DST_CFGX_BUFFER_PAIR_ENABLE   1
+#define BF_DST_CFGX_DFIFO_SZ_DOUBLE      2
+#define BF_DST_CFGX_NOT_PAUSE_WHEN_FULL 11
+#define BF_DST_CFGX_FCI_ID              12
+#define BF_DST_CFGX_CAP_MODE            24
+#define BF_DST_CFGX_PROC_SEQ_ID_VALID   31
+
+/* AUD_FMM_IOP_OUT_SPDIF_xxx */
+#define SPDIF_0_OUT_DITHER_ENA     3
+#define SPDIF_0_OUT_STREAM_ENA    31
+
+/* AUD_FMM_IOP_PLL_0_USER */
+#define IOP_PLL_0_USER_NDIV_FRAC   10
+
+/* AUD_FMM_IOP_PLL_0_ACTIVE */
+#define IOP_PLL_0_ACTIVE_NDIV_FRAC 10
+
+
+#define INIT_SSP_REGS(num) (struct cygnus_ssp_regs){ \
+               .i2s_stream_cfg = OUT_I2S_ ##num## _STREAM_CFG_OFFSET, \
+               .i2s_cap_stream_cfg = IN_I2S_ ##num## _STREAM_CFG_OFFSET, \
+               .i2s_cfg = OUT_I2S_ ##num## _CFG_OFFSET, \
+               .i2s_cap_cfg = IN_I2S_ ##num## _CFG_OFFSET, \
+               .i2s_mclk_cfg = OUT_I2S_ ##num## _MCLK_CFG_OFFSET, \
+               .bf_destch_ctrl = BF_DST_CTRL ##num## _OFFSET, \
+               .bf_destch_cfg = BF_DST_CFG ##num## _OFFSET, \
+               .bf_sourcech_ctrl = BF_SRC_CTRL ##num## _OFFSET, \
+               .bf_sourcech_cfg = BF_SRC_CFG ##num## _OFFSET, \
+               .bf_sourcech_grp = BF_SRC_GRP ##num## _OFFSET \
+}
+
+struct pll_macro_entry {
+       u32 mclk;
+       u32 pll_ch_num;
+};
+
+/*
+ * PLL has 3 output channels (1x, 2x, and 4x). Below are
+ * the common MCLK frequencies used by audio driver
+ */
+static const struct pll_macro_entry pll_predef_mclk[] = {
+       { 4096000, 0},
+       { 8192000, 1},
+       {16384000, 2},
+
+       { 5644800, 0},
+       {11289600, 1},
+       {22579200, 2},
+
+       { 6144000, 0},
+       {12288000, 1},
+       {24576000, 2},
+
+       {12288000, 0},
+       {24576000, 1},
+       {49152000, 2},
+
+       {22579200, 0},
+       {45158400, 1},
+       {90316800, 2},
+
+       {24576000, 0},
+       {49152000, 1},
+       {98304000, 2},
+};
+
+/* List of valid frame sizes for tdm mode */
+static const int ssp_valid_tdm_framesize[] = {32, 64, 128, 256, 512};
+
+/*
+ * Use this relationship to derive the sampling rate (lrclk)
+ * lrclk = (mclk) / ((2*mclk_to_sclk_ratio) * (32 * SCLK))).
+ *
+ * Use mclk and pll_ch from the table above
+ *
+ * Valid SCLK = 0/1/2/4/8/12
+ *
+ * mclk_to_sclk_ratio = number of MCLK per SCLK. Division is twice the
+ * value programmed in this field.
+ * Valid mclk_to_sclk_ratio = 1 through to 15
+ *
+ * eg: To set lrclk = 48khz, set mclk = 12288000, mclk_to_sclk_ratio = 2,
+ * SCLK = 64
+ */
+struct _ssp_clk_coeff {
+       u32 mclk;
+       u32 sclk_rate;
+       u32 rate;
+       u32 mclk_rate;
+};
+
+static const struct _ssp_clk_coeff ssp_clk_coeff[] = {
+       { 4096000,  32,  16000, 4},
+       { 4096000,  32,  32000, 2},
+       { 4096000,  64,   8000, 4},
+       { 4096000,  64,  16000, 2},
+       { 4096000,  64,  32000, 1},
+       { 4096000, 128,   8000, 2},
+       { 4096000, 128,  16000, 1},
+       { 4096000, 256,   8000, 1},
+
+       { 6144000,  32,  16000, 6},
+       { 6144000,  32,  32000, 3},
+       { 6144000,  32,  48000, 2},
+       { 6144000,  32,  96000, 1},
+       { 6144000,  64,   8000, 6},
+       { 6144000,  64,  16000, 3},
+       { 6144000,  64,  48000, 1},
+       { 6144000, 128,   8000, 3},
+
+       { 8192000,  32,  32000, 4},
+       { 8192000,  64,  16000, 4},
+       { 8192000,  64,  32000, 2},
+       { 8192000, 128,   8000, 4},
+       { 8192000, 128,  16000, 2},
+       { 8192000, 128,  32000, 1},
+       { 8192000, 256,   8000, 2},
+       { 8192000, 256,  16000, 1},
+       { 8192000, 512,   8000, 1},
+
+       {12288000,  32,  32000, 6},
+       {12288000,  32,  48000, 4},
+       {12288000,  32,  96000, 2},
+       {12288000,  32, 192000, 1},
+       {12288000,  64,  16000, 6},
+       {12288000,  64,  32000, 3},
+       {12288000,  64,  48000, 2},
+       {12288000,  64,  96000, 1},
+       {12288000, 128,   8000, 6},
+       {12288000, 128,  16000, 3},
+       {12288000, 128,  48000, 1},
+       {12288000, 256,   8000, 3},
+
+       {16384000,  64,  32000, 4},
+       {16384000, 128,  16000, 4},
+       {16384000, 128,  32000, 2},
+       {16384000, 256,   8000, 4},
+       {16384000, 256,  16000, 2},
+       {16384000, 256,  32000, 1},
+       {16384000, 512,   8000, 2},
+       {16384000, 512,  16000, 1},
+
+       {24576000,  32,  96000, 4},
+       {24576000,  32, 192000, 2},
+       {24576000,  64,  32000, 6},
+       {24576000,  64,  48000, 4},
+       {24576000,  64,  96000, 2},
+       {24576000,  64, 192000, 1},
+       {24576000, 128,  16000, 6},
+       {24576000, 128,  32000, 3},
+       {24576000, 128,  48000, 2},
+       {24576000, 256,   8000, 6},
+       {24576000, 256,  16000, 3},
+       {24576000, 256,  48000, 1},
+       {24576000, 512,   8000, 3},
+
+       {49152000,  32, 192000, 4},
+       {49152000,  64,  96000, 4},
+       {49152000,  64, 192000, 2},
+       {49152000, 128,  32000, 6},
+       {49152000, 128,  48000, 4},
+       {49152000, 128,  96000, 2},
+       {49152000, 128, 192000, 1},
+       {49152000, 256,  16000, 6},
+       {49152000, 256,  32000, 3},
+       {49152000, 256,  48000, 2},
+       {49152000, 256,  96000, 1},
+       {49152000, 512,   8000, 6},
+       {49152000, 512,  16000, 3},
+       {49152000, 512,  48000, 1},
+
+       { 5644800,  32,  22050, 4},
+       { 5644800,  32,  44100, 2},
+       { 5644800,  32,  88200, 1},
+       { 5644800,  64,  11025, 4},
+       { 5644800,  64,  22050, 2},
+       { 5644800,  64,  44100, 1},
+
+       {11289600,  32,  44100, 4},
+       {11289600,  32,  88200, 2},
+       {11289600,  32, 176400, 1},
+       {11289600,  64,  22050, 4},
+       {11289600,  64,  44100, 2},
+       {11289600,  64,  88200, 1},
+       {11289600, 128,  11025, 4},
+       {11289600, 128,  22050, 2},
+       {11289600, 128,  44100, 1},
+
+       {22579200,  32,  88200, 4},
+       {22579200,  32, 176400, 2},
+       {22579200,  64,  44100, 4},
+       {22579200,  64,  88200, 2},
+       {22579200,  64, 176400, 1},
+       {22579200, 128,  22050, 4},
+       {22579200, 128,  44100, 2},
+       {22579200, 128,  88200, 1},
+       {22579200, 256,  11025, 4},
+       {22579200, 256,  22050, 2},
+       {22579200, 256,  44100, 1},
+
+       {45158400,  32, 176400, 4},
+       {45158400,  64,  88200, 4},
+       {45158400,  64, 176400, 2},
+       {45158400, 128,  44100, 4},
+       {45158400, 128,  88200, 2},
+       {45158400, 128, 176400, 1},
+       {45158400, 256,  22050, 4},
+       {45158400, 256,  44100, 2},
+       {45158400, 256,  88200, 1},
+       {45158400, 512,  11025, 4},
+       {45158400, 512,  22050, 2},
+       {45158400, 512,  44100, 1},
+};
+
+static struct cygnus_aio_port *cygnus_dai_get_portinfo(struct snd_soc_dai *dai)
+{
+       struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai);
+
+       return &cygaud->portinfo[dai->id];
+}
+
+static int audio_ssp_init_portregs(struct cygnus_aio_port *aio)
+{
+       u32 value, fci_id;
+       int status = 0;
+
+       switch (aio->port_type) {
+       case PORT_TDM:
+               value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg);
+               value &= ~I2S_STREAM_CFG_MASK;
+
+               /* Set Group ID */
+               writel(aio->portnum,
+                       aio->cygaud->audio + aio->regs.bf_sourcech_grp);
+
+               /* Configure the AUD_FMM_IOP_OUT_I2S_x_STREAM_CFG reg */
+               value |= aio->portnum << I2S_OUT_STREAM_CFG_GROUP_ID;
+               value |= aio->portnum; /* FCI ID is the port num */
+               value |= CH_GRP_STEREO << I2S_OUT_STREAM_CFG_CHANNEL_GROUPING;
+               writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg);
+
+               /* Configure the AUD_FMM_BF_CTRL_SOURCECH_CFGX reg */
+               value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+               value &= ~BIT(BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY);
+               value |= BIT(BF_SRC_CFGX_SFIFO_SZ_DOUBLE);
+               value |= BIT(BF_SRC_CFGX_PROCESS_SEQ_ID_VALID);
+               writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+
+               /* Configure the AUD_FMM_IOP_IN_I2S_x_CAP_STREAM_CFG_0 reg */
+               value = readl(aio->cygaud->i2s_in +
+                       aio->regs.i2s_cap_stream_cfg);
+               value &= ~I2S_CAP_STREAM_CFG_MASK;
+               value |= aio->portnum << I2S_IN_STREAM_CFG_0_GROUP_ID;
+               writel(value, aio->cygaud->i2s_in +
+                       aio->regs.i2s_cap_stream_cfg);
+
+               /* Configure the AUD_FMM_BF_CTRL_DESTCH_CFGX_REG_BASE reg */
+               fci_id = CAPTURE_FCI_ID_BASE + aio->portnum;
+
+               value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg);
+               value |= BIT(BF_DST_CFGX_DFIFO_SZ_DOUBLE);
+               value &= ~BIT(BF_DST_CFGX_NOT_PAUSE_WHEN_FULL);
+               value |= (fci_id << BF_DST_CFGX_FCI_ID);
+               value |= BIT(BF_DST_CFGX_PROC_SEQ_ID_VALID);
+               writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg);
+
+               /* Enable the transmit pin for this port */
+               value = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+               value &= ~BIT((aio->portnum * 4) + AUD_MISC_SEROUT_SDAT_OE);
+               writel(value, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+               break;
+       case PORT_SPDIF:
+               writel(aio->portnum, aio->cygaud->audio + BF_SRC_GRP3_OFFSET);
+
+               value = readl(aio->cygaud->audio + SPDIF_CTRL_OFFSET);
+               value |= BIT(SPDIF_0_OUT_DITHER_ENA);
+               writel(value, aio->cygaud->audio + SPDIF_CTRL_OFFSET);
+
+               /* Enable and set the FCI ID for the SPDIF channel */
+               value = readl(aio->cygaud->audio + SPDIF_STREAM_CFG_OFFSET);
+               value &= ~SPDIF_STREAM_CFG_MASK;
+               value |= aio->portnum; /* FCI ID is the port num */
+               value |= BIT(SPDIF_0_OUT_STREAM_ENA);
+               writel(value, aio->cygaud->audio + SPDIF_STREAM_CFG_OFFSET);
+
+               value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+               value &= ~BIT(BF_SRC_CFGX_NOT_PAUSE_WHEN_EMPTY);
+               value |= BIT(BF_SRC_CFGX_SFIFO_SZ_DOUBLE);
+               value |= BIT(BF_SRC_CFGX_PROCESS_SEQ_ID_VALID);
+               writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+
+               /* Enable the spdif output pin */
+               value = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+               value &= ~BIT(AUD_MISC_SEROUT_SPDIF_OE);
+               writel(value, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+               break;
+       default:
+               dev_err(aio->cygaud->dev, "Port not supported\n");
+               status = -EINVAL;
+       }
+
+       return status;
+}
+
+static void audio_ssp_in_enable(struct cygnus_aio_port *aio)
+{
+       u32 value;
+
+       value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg);
+       value |= BIT(BF_DST_CFGX_CAP_ENA);
+       writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg);
+
+       writel(0x1, aio->cygaud->audio + aio->regs.bf_destch_ctrl);
+
+       value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+       value |= BIT(I2S_OUT_CFGX_CLK_ENA);
+       value |= BIT(I2S_OUT_CFGX_DATA_ENABLE);
+       writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
+
+       value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);
+       value |= BIT(I2S_IN_STREAM_CFG_CAP_ENA);
+       writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);
+
+       aio->streams_on |= CAPTURE_STREAM_MASK;
+}
+
+static void audio_ssp_in_disable(struct cygnus_aio_port *aio)
+{
+       u32 value;
+
+       value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);
+       value &= ~BIT(I2S_IN_STREAM_CFG_CAP_ENA);
+       writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_stream_cfg);
+
+       aio->streams_on &= ~CAPTURE_STREAM_MASK;
+
+       /* If both playback and capture are off */
+       if (!aio->streams_on) {
+               value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+               value &= ~BIT(I2S_OUT_CFGX_CLK_ENA);
+               value &= ~BIT(I2S_OUT_CFGX_DATA_ENABLE);
+               writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
+       }
+
+       writel(0x0, aio->cygaud->audio + aio->regs.bf_destch_ctrl);
+
+       value = readl(aio->cygaud->audio + aio->regs.bf_destch_cfg);
+       value &= ~BIT(BF_DST_CFGX_CAP_ENA);
+       writel(value, aio->cygaud->audio + aio->regs.bf_destch_cfg);
+}
+
+static int audio_ssp_out_enable(struct cygnus_aio_port *aio)
+{
+       u32 value;
+       int status = 0;
+
+       switch (aio->port_type) {
+       case PORT_TDM:
+               value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg);
+               value |= BIT(I2S_OUT_STREAM_ENA);
+               writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg);
+
+               writel(1, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);
+
+               value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+               value |= BIT(I2S_OUT_CFGX_CLK_ENA);
+               value |= BIT(I2S_OUT_CFGX_DATA_ENABLE);
+               writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
+
+               value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+               value |= BIT(BF_SRC_CFGX_SFIFO_ENA);
+               writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+
+               aio->streams_on |= PLAYBACK_STREAM_MASK;
+               break;
+       case PORT_SPDIF:
+               value = readl(aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);
+               value |= 0x3;
+               writel(value, aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);
+
+               writel(1, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);
+
+               value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+               value |= BIT(BF_SRC_CFGX_SFIFO_ENA);
+               writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+               break;
+       default:
+               dev_err(aio->cygaud->dev,
+                       "Port not supported %d\n", aio->portnum);
+               status = -EINVAL;
+       }
+
+       return status;
+}
+
+static int audio_ssp_out_disable(struct cygnus_aio_port *aio)
+{
+       u32 value;
+       int status = 0;
+
+       switch (aio->port_type) {
+       case PORT_TDM:
+               aio->streams_on &= ~PLAYBACK_STREAM_MASK;
+
+               /* If both playback and capture are off */
+               if (!aio->streams_on) {
+                       value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+                       value &= ~BIT(I2S_OUT_CFGX_CLK_ENA);
+                       value &= ~BIT(I2S_OUT_CFGX_DATA_ENABLE);
+                       writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
+               }
+
+               /* set group_sync_dis = 1 */
+               value = readl(aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);
+               value |= BIT(aio->portnum);
+               writel(value, aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);
+
+               writel(0, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);
+
+               value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+               value &= ~BIT(BF_SRC_CFGX_SFIFO_ENA);
+               writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+
+               /* set group_sync_dis = 0 */
+               value = readl(aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);
+               value &= ~BIT(aio->portnum);
+               writel(value, aio->cygaud->audio + BF_SRC_GRP_SYNC_DIS_OFFSET);
+
+               value = readl(aio->cygaud->audio + aio->regs.i2s_stream_cfg);
+               value &= ~BIT(I2S_OUT_STREAM_ENA);
+               writel(value, aio->cygaud->audio + aio->regs.i2s_stream_cfg);
+
+               /* IOP SW INIT on OUT_I2S_x */
+               value = readl(aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC);
+               value |= BIT(aio->portnum);
+               writel(value, aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC);
+               value &= ~BIT(aio->portnum);
+               writel(value, aio->cygaud->i2s_in + IOP_SW_INIT_LOGIC);
+               break;
+       case PORT_SPDIF:
+               value = readl(aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);
+               value &= ~0x3;
+               writel(value, aio->cygaud->audio + SPDIF_FORMAT_CFG_OFFSET);
+               writel(0, aio->cygaud->audio + aio->regs.bf_sourcech_ctrl);
+
+               value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+               value &= ~BIT(BF_SRC_CFGX_SFIFO_ENA);
+               writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+               break;
+       default:
+               dev_err(aio->cygaud->dev,
+                       "Port not supported %d\n", aio->portnum);
+               status = -EINVAL;
+       }
+
+       return status;
+}
+
+static int pll_configure_mclk(struct cygnus_audio *cygaud, u32 mclk,
+       struct cygnus_aio_port *aio)
+{
+       int i = 0, error;
+       bool found = false;
+       const struct pll_macro_entry *p_entry;
+       struct clk *ch_clk;
+
+       for (i = 0; i < ARRAY_SIZE(pll_predef_mclk); i++) {
+               p_entry = &pll_predef_mclk[i];
+               if (p_entry->mclk == mclk) {
+                       found = true;
+                       break;
+               }
+       }
+       if (!found) {
+               dev_err(cygaud->dev,
+                       "%s No valid mclk freq (%u) found!\n", __func__, mclk);
+               return -EINVAL;
+       }
+
+       ch_clk = cygaud->audio_clk[p_entry->pll_ch_num];
+
+       if ((aio->clk_trace.cap_en) && (!aio->clk_trace.cap_clk_en)) {
+               error = clk_prepare_enable(ch_clk);
+               if (error) {
+                       dev_err(cygaud->dev, "%s clk_prepare_enable failed %d\n",
+                               __func__, error);
+                       return error;
+               }
+               aio->clk_trace.cap_clk_en = true;
+       }
+
+       if ((aio->clk_trace.play_en) && (!aio->clk_trace.play_clk_en)) {
+               error = clk_prepare_enable(ch_clk);
+               if (error) {
+                       dev_err(cygaud->dev, "%s clk_prepare_enable failed %d\n",
+                               __func__, error);
+                       return error;
+               }
+               aio->clk_trace.play_clk_en = true;
+       }
+
+       error = clk_set_rate(ch_clk, mclk);
+       if (error) {
+               dev_err(cygaud->dev, "%s Set MCLK rate failed: %d\n",
+                       __func__, error);
+               return error;
+       }
+
+       return p_entry->pll_ch_num;
+}
+
+static int cygnus_ssp_set_clocks(struct cygnus_aio_port *aio,
+                       struct cygnus_audio *cygaud)
+{
+       u32 value, i = 0;
+       u32 mask = 0xf;
+       u32 sclk;
+       bool found = false;
+       const struct _ssp_clk_coeff *p_entry = NULL;
+
+       for (i = 0; i < ARRAY_SIZE(ssp_clk_coeff); i++) {
+               p_entry = &ssp_clk_coeff[i];
+               if ((p_entry->rate == aio->lrclk) &&
+                   (p_entry->sclk_rate == aio->bit_per_frame) &&
+                   (p_entry->mclk == aio->mclk)) {
+                       found = true;
+                       break;
+               }
+       }
+       if (!found) {
+               dev_err(aio->cygaud->dev,
+                       "No valid match found in ssp_clk_coeff array\n");
+               dev_err(aio->cygaud->dev, "lrclk = %u, bits/frame = %u, mclk = %u\n",
+                       aio->lrclk, aio->bit_per_frame, aio->mclk);
+               return -EINVAL;
+       }
+
+       sclk = aio->bit_per_frame;
+       if (sclk == 512)
+               sclk = 0;
+       /* sclks_per_1fs_div = sclk cycles/32 */
+       sclk /= 32;
+       /* Set sclk rate */
+       switch (aio->port_type) {
+       case PORT_TDM:
+               /* Set number of bitclks per frame */
+               value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+               value &= ~(mask << I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32);
+               value |= sclk << I2S_OUT_CFGX_SCLKS_PER_1FS_DIV32;
+               writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
+               dev_dbg(aio->cygaud->dev,
+                       "SCLKS_PER_1FS_DIV32 = 0x%x\n", value);
+               break;
+       case PORT_SPDIF:
+               break;
+       default:
+               dev_err(aio->cygaud->dev, "Unknown port type\n");
+               return -EINVAL;
+       }
+
+       /* Set MCLK_RATE ssp port (spdif and ssp are the same) */
+       value = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
+       value &= ~(0xf << I2S_OUT_MCLKRATE_SHIFT);
+       value |= (p_entry->mclk_rate << I2S_OUT_MCLKRATE_SHIFT);
+       writel(value, aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
+
+       dev_dbg(aio->cygaud->dev, "mclk cfg reg = 0x%x\n", value);
+       dev_dbg(aio->cygaud->dev, "bits per frame = %u, mclk = %u Hz, lrclk = %u Hz\n",
+                       aio->bit_per_frame, aio->mclk, aio->lrclk);
+       return 0;
+}
+
+static int cygnus_ssp_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *dai)
+{
+       struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
+       struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai);
+       int rate, bitres;
+       u32 value;
+       u32 mask = 0x1f;
+       int ret = 0;
+
+       dev_dbg(aio->cygaud->dev, "%s port = %d\n", __func__, aio->portnum);
+       dev_dbg(aio->cygaud->dev, "params_channels %d\n",
+                       params_channels(params));
+       dev_dbg(aio->cygaud->dev, "rate %d\n", params_rate(params));
+       dev_dbg(aio->cygaud->dev, "format %d\n", params_format(params));
+
+       rate = params_rate(params);
+
+       switch (aio->mode) {
+       case CYGNUS_SSPMODE_TDM:
+               if ((rate == 192000) && (params_channels(params) > 4)) {
+                       dev_err(aio->cygaud->dev, "Cannot run %d channels at %dHz\n",
+                               params_channels(params), rate);
+                       return -EINVAL;
+               }
+               break;
+       case CYGNUS_SSPMODE_I2S:
+               aio->bit_per_frame = 64; /* I2S must be 64 bit per frame */
+               break;
+       default:
+               dev_err(aio->cygaud->dev,
+                       "%s port running in unknown mode\n", __func__);
+               return -EINVAL;
+       }
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+               value &= ~BIT(BF_SRC_CFGX_BUFFER_PAIR_ENABLE);
+               /* Configure channels as mono or stereo/TDM */
+               if (params_channels(params) == 1)
+                       value |= BIT(BF_SRC_CFGX_SAMPLE_CH_MODE);
+               else
+                       value &= ~BIT(BF_SRC_CFGX_SAMPLE_CH_MODE);
+               writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+
+               switch (params_format(params)) {
+               case SNDRV_PCM_FORMAT_S8:
+                       if (aio->port_type == PORT_SPDIF) {
+                               dev_err(aio->cygaud->dev,
+                               "SPDIF does not support 8bit format\n");
+                               return -EINVAL;
+                       }
+                       bitres = 8;
+                       break;
+
+               case SNDRV_PCM_FORMAT_S16_LE:
+                       bitres = 16;
+                       break;
+
+               case SNDRV_PCM_FORMAT_S32_LE:
+                       /* 32 bit mode is coded as 0 */
+                       bitres = 0;
+                       break;
+
+               default:
+                       return -EINVAL;
+               }
+
+               value = readl(aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+               value &= ~(mask << BF_SRC_CFGX_BIT_RES);
+               value |= (bitres << BF_SRC_CFGX_BIT_RES);
+               writel(value, aio->cygaud->audio + aio->regs.bf_sourcech_cfg);
+
+       } else {
+
+               switch (params_format(params)) {
+               case SNDRV_PCM_FORMAT_S16_LE:
+                       value = readl(aio->cygaud->audio +
+                                       aio->regs.bf_destch_cfg);
+                       value |= BIT(BF_DST_CFGX_CAP_MODE);
+                       writel(value, aio->cygaud->audio +
+                                       aio->regs.bf_destch_cfg);
+                       break;
+
+               case SNDRV_PCM_FORMAT_S32_LE:
+                       value = readl(aio->cygaud->audio +
+                                       aio->regs.bf_destch_cfg);
+                       value &= ~BIT(BF_DST_CFGX_CAP_MODE);
+                       writel(value, aio->cygaud->audio +
+                                       aio->regs.bf_destch_cfg);
+                       break;
+
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       aio->lrclk = rate;
+
+       if (!aio->is_slave)
+               ret = cygnus_ssp_set_clocks(aio, cygaud);
+
+       return ret;
+}
+
+/*
+ * This function sets the mclk frequency for pll clock
+ */
+static int cygnus_ssp_set_sysclk(struct snd_soc_dai *dai,
+                       int clk_id, unsigned int freq, int dir)
+{
+       int sel;
+       u32 value;
+       struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
+       struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai);
+
+       dev_dbg(aio->cygaud->dev,
+               "%s Enter port = %d\n", __func__, aio->portnum);
+       sel = pll_configure_mclk(cygaud, freq, aio);
+       if (sel < 0) {
+               dev_err(aio->cygaud->dev,
+                       "%s Setting mclk failed.\n", __func__);
+               return -EINVAL;
+       }
+
+       aio->mclk = freq;
+
+       dev_dbg(aio->cygaud->dev, "%s Setting MCLKSEL to %d\n", __func__, sel);
+       value = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
+       value &= ~(0xf << I2S_OUT_PLLCLKSEL_SHIFT);
+       value |= (sel << I2S_OUT_PLLCLKSEL_SHIFT);
+       writel(value, aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
+
+       return 0;
+}
+
+static int cygnus_ssp_startup(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
+{
+       struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
+
+       snd_soc_dai_set_dma_data(dai, substream, aio);
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               aio->clk_trace.play_en = true;
+       else
+               aio->clk_trace.cap_en = true;
+
+       return 0;
+}
+
+static void cygnus_ssp_shutdown(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
+{
+       struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               aio->clk_trace.play_en = false;
+       else
+               aio->clk_trace.cap_en = false;
+
+       if (!aio->is_slave) {
+               u32 val;
+
+               val = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
+               val &= CYGNUS_PLLCLKSEL_MASK;
+               if (val >= ARRAY_SIZE(aio->cygaud->audio_clk)) {
+                       dev_err(aio->cygaud->dev, "Clk index %u is out of bounds\n",
+                               val);
+                       return;
+               }
+
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       if (aio->clk_trace.play_clk_en) {
+                               clk_disable_unprepare(aio->cygaud->
+                                               audio_clk[val]);
+                               aio->clk_trace.play_clk_en = false;
+                       }
+               } else {
+                       if (aio->clk_trace.cap_clk_en) {
+                               clk_disable_unprepare(aio->cygaud->
+                                               audio_clk[val]);
+                               aio->clk_trace.cap_clk_en = false;
+                       }
+               }
+       }
+}
+
+/*
+ * Bit    Update  Notes
+ * 31     Yes     TDM Mode        (1 = TDM, 0 = i2s)
+ * 30     Yes     Slave Mode     (1 = Slave, 0 = Master)
+ * 29:26  No      Sclks per frame
+ * 25:18  Yes     FS Width
+ * 17:14  No      Valid Slots
+ * 13     No      Bits           (1 = 16 bits, 0 = 32 bits)
+ * 12:08  No     Bits per samp
+ * 07     Yes     Justifcation    (1 = LSB, 0 = MSB)
+ * 06     Yes     Alignment       (1 = Delay 1 clk, 0 = no delay
+ * 05     Yes     SCLK polarity   (1 = Rising, 0 = Falling)
+ * 04     Yes     LRCLK Polarity  (1 = High for left, 0 = Low for left)
+ * 03:02  Yes     Reserved - write as zero
+ * 01     No      Data Enable
+ * 00     No      CLK Enable
+ */
+#define I2S_OUT_CFG_REG_UPDATE_MASK   0x3C03FF03
+
+/* Input cfg is same as output, but the FS width is not a valid field */
+#define I2S_IN_CFG_REG_UPDATE_MASK  (I2S_OUT_CFG_REG_UPDATE_MASK | 0x03FC0000)
+
+int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai, int len)
+{
+       struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
+
+       if ((len > 0) && (len < 256)) {
+               aio->fsync_width = len;
+               return 0;
+       } else {
+               return -EINVAL;
+       }
+}
+
+static int cygnus_ssp_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+       struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
+       u32 ssp_curcfg;
+       u32 ssp_newcfg;
+       u32 ssp_outcfg;
+       u32 ssp_incfg;
+       u32 val;
+       u32 mask;
+
+       dev_dbg(aio->cygaud->dev, "%s Enter  fmt: %x\n", __func__, fmt);
+
+       if (aio->port_type == PORT_SPDIF)
+               return -EINVAL;
+
+       ssp_newcfg = 0;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               ssp_newcfg |= BIT(I2S_OUT_CFGX_SLAVE_MODE);
+               aio->is_slave = 1;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               ssp_newcfg &= ~BIT(I2S_OUT_CFGX_SLAVE_MODE);
+               aio->is_slave = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               ssp_newcfg |= BIT(I2S_OUT_CFGX_DATA_ALIGNMENT);
+               ssp_newcfg |= BIT(I2S_OUT_CFGX_FSYNC_WIDTH);
+               aio->mode = CYGNUS_SSPMODE_I2S;
+               break;
+
+       case SND_SOC_DAIFMT_DSP_A:
+       case SND_SOC_DAIFMT_DSP_B:
+               ssp_newcfg |= BIT(I2S_OUT_CFGX_TDM_MODE);
+
+               /* DSP_A = data after FS, DSP_B = data during FS */
+               if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_DSP_A)
+                       ssp_newcfg |= BIT(I2S_OUT_CFGX_DATA_ALIGNMENT);
+
+               if ((aio->fsync_width > 0) && (aio->fsync_width < 256))
+                       ssp_newcfg |=
+                               (aio->fsync_width << I2S_OUT_CFGX_FSYNC_WIDTH);
+               else
+                       ssp_newcfg |= BIT(I2S_OUT_CFGX_FSYNC_WIDTH);
+
+               aio->mode = CYGNUS_SSPMODE_TDM;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       /*
+        * SSP out cfg.
+        * Retain bits we do not want to update, then OR in new bits
+        */
+       ssp_curcfg = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+       ssp_outcfg = (ssp_curcfg & I2S_OUT_CFG_REG_UPDATE_MASK) | ssp_newcfg;
+       writel(ssp_outcfg, aio->cygaud->audio + aio->regs.i2s_cfg);
+
+       /*
+        * SSP in cfg.
+        * Retain bits we do not want to update, then OR in new bits
+        */
+       ssp_curcfg = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);
+       ssp_incfg = (ssp_curcfg & I2S_IN_CFG_REG_UPDATE_MASK) | ssp_newcfg;
+       writel(ssp_incfg, aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);
+
+       val = readl(aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+
+       /*
+        * Configure the word clk and bit clk as output or tristate
+        * Each port has 4 bits for controlling its pins.
+        * Shift the mask based upon port number.
+        */
+       mask = BIT(AUD_MISC_SEROUT_LRCK_OE)
+                       | BIT(AUD_MISC_SEROUT_SCLK_OE)
+                       | BIT(AUD_MISC_SEROUT_MCLK_OE);
+       mask = mask << (aio->portnum * 4);
+       if (aio->is_slave)
+               /* Set bit for tri-state */
+               val |= mask;
+       else
+               /* Clear bit for drive */
+               val &= ~mask;
+
+       dev_dbg(aio->cygaud->dev, "%s  Set OE bits 0x%x\n", __func__, val);
+       writel(val, aio->cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+
+       return 0;
+}
+
+static int cygnus_ssp_trigger(struct snd_pcm_substream *substream, int cmd,
+                              struct snd_soc_dai *dai)
+{
+       struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(dai);
+       struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(dai);
+
+       dev_dbg(aio->cygaud->dev,
+               "%s cmd %d at port = %d\n", __func__, cmd, aio->portnum);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+       case SNDRV_PCM_TRIGGER_RESUME:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       audio_ssp_out_enable(aio);
+               else
+                       audio_ssp_in_enable(aio);
+               cygaud->active_ports++;
+
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       audio_ssp_out_disable(aio);
+               else
+                       audio_ssp_in_disable(aio);
+               cygaud->active_ports--;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int cygnus_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
+       unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+       struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
+       u32 value;
+       int bits_per_slot = 0;     /* default to 32-bits per slot */
+       int frame_bits;
+       unsigned int active_slots;
+       bool found = false;
+       int i;
+
+       if (tx_mask != rx_mask) {
+               dev_err(aio->cygaud->dev,
+                       "%s tx_mask must equal rx_mask\n", __func__);
+               return -EINVAL;
+       }
+
+       active_slots = hweight32(tx_mask);
+
+       if ((active_slots < 0) || (active_slots > 16))
+               return -EINVAL;
+
+       /* Slot value must be even */
+       if (active_slots % 2)
+               return -EINVAL;
+
+       /* We encode 16 slots as 0 in the reg */
+       if (active_slots == 16)
+               active_slots = 0;
+
+       /* Slot Width is either 16 or 32 */
+       switch (slot_width) {
+       case 16:
+               bits_per_slot = 1;
+               break;
+       case 32:
+               bits_per_slot = 0;
+               break;
+       default:
+               bits_per_slot = 0;
+               dev_warn(aio->cygaud->dev,
+                       "%s Defaulting Slot Width to 32\n", __func__);
+       }
+
+       frame_bits = slots * slot_width;
+
+       for (i = 0; i < ARRAY_SIZE(ssp_valid_tdm_framesize); i++) {
+               if (ssp_valid_tdm_framesize[i] == frame_bits) {
+                       found = true;
+                       break;
+               }
+       }
+
+       if (!found) {
+               dev_err(aio->cygaud->dev,
+                       "%s In TDM mode, frame bits INVALID (%d)\n",
+                       __func__, frame_bits);
+               return -EINVAL;
+       }
+
+       aio->bit_per_frame = frame_bits;
+
+       dev_dbg(aio->cygaud->dev, "%s active_slots %u, bits per frame %d\n",
+                       __func__, active_slots, frame_bits);
+
+       /* Set capture side of ssp port */
+       value = readl(aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);
+       value &= ~(0xf << I2S_OUT_CFGX_VALID_SLOT);
+       value |= (active_slots << I2S_OUT_CFGX_VALID_SLOT);
+       value &= ~BIT(I2S_OUT_CFGX_BITS_PER_SLOT);
+       value |= (bits_per_slot << I2S_OUT_CFGX_BITS_PER_SLOT);
+       writel(value, aio->cygaud->i2s_in + aio->regs.i2s_cap_cfg);
+
+       /* Set playback side of ssp port */
+       value = readl(aio->cygaud->audio + aio->regs.i2s_cfg);
+       value &= ~(0xf << I2S_OUT_CFGX_VALID_SLOT);
+       value |= (active_slots << I2S_OUT_CFGX_VALID_SLOT);
+       value &= ~BIT(I2S_OUT_CFGX_BITS_PER_SLOT);
+       value |= (bits_per_slot << I2S_OUT_CFGX_BITS_PER_SLOT);
+       writel(value, aio->cygaud->audio + aio->regs.i2s_cfg);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int cygnus_ssp_suspend(struct snd_soc_dai *cpu_dai)
+{
+       struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
+
+       if (!aio->is_slave) {
+               u32 val;
+
+               val = readl(aio->cygaud->audio + aio->regs.i2s_mclk_cfg);
+               val &= CYGNUS_PLLCLKSEL_MASK;
+               if (val >= ARRAY_SIZE(aio->cygaud->audio_clk)) {
+                       dev_err(aio->cygaud->dev, "Clk index %u is out of bounds\n",
+                               val);
+                       return -EINVAL;
+               }
+
+               if (aio->clk_trace.cap_clk_en)
+                       clk_disable_unprepare(aio->cygaud->audio_clk[val]);
+               if (aio->clk_trace.play_clk_en)
+                       clk_disable_unprepare(aio->cygaud->audio_clk[val]);
+
+               aio->pll_clk_num = val;
+       }
+
+       return 0;
+}
+
+static int cygnus_ssp_resume(struct snd_soc_dai *cpu_dai)
+{
+       struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
+       int error;
+
+       if (!aio->is_slave) {
+               if (aio->clk_trace.cap_clk_en) {
+                       error = clk_prepare_enable(aio->cygaud->
+                                       audio_clk[aio->pll_clk_num]);
+                       if (error) {
+                               dev_err(aio->cygaud->dev, "%s clk_prepare_enable failed\n",
+                                       __func__);
+                               return -EINVAL;
+                       }
+               }
+               if (aio->clk_trace.play_clk_en) {
+                       error = clk_prepare_enable(aio->cygaud->
+                                       audio_clk[aio->pll_clk_num]);
+                       if (error) {
+                               if (aio->clk_trace.cap_clk_en)
+                                       clk_disable_unprepare(aio->cygaud->
+                                               audio_clk[aio->pll_clk_num]);
+                               dev_err(aio->cygaud->dev, "%s clk_prepare_enable failed\n",
+                                       __func__);
+                               return -EINVAL;
+                       }
+               }
+       }
+
+       return 0;
+}
+#else
+#define cygnus_ssp_suspend NULL
+#define cygnus_ssp_resume  NULL
+#endif
+
+static const struct snd_soc_dai_ops cygnus_ssp_dai_ops = {
+       .startup        = cygnus_ssp_startup,
+       .shutdown       = cygnus_ssp_shutdown,
+       .trigger        = cygnus_ssp_trigger,
+       .hw_params      = cygnus_ssp_hw_params,
+       .set_fmt        = cygnus_ssp_set_fmt,
+       .set_sysclk     = cygnus_ssp_set_sysclk,
+       .set_tdm_slot   = cygnus_set_dai_tdm_slot,
+};
+
+
+#define INIT_CPU_DAI(num) { \
+       .name = "cygnus-ssp" #num, \
+       .playback = { \
+               .channels_min = 1, \
+               .channels_max = 16, \
+               .rates = CYGNUS_TDM_RATE | SNDRV_PCM_RATE_88200 | \
+                       SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \
+                       SNDRV_PCM_RATE_192000, \
+               .formats = SNDRV_PCM_FMTBIT_S8 | \
+                               SNDRV_PCM_FMTBIT_S16_LE | \
+                               SNDRV_PCM_FMTBIT_S32_LE, \
+       }, \
+       .capture = { \
+               .channels_min = 2, \
+               .channels_max = 16, \
+               .rates = CYGNUS_TDM_RATE | SNDRV_PCM_RATE_88200 | \
+                       SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \
+                       SNDRV_PCM_RATE_192000, \
+               .formats =  SNDRV_PCM_FMTBIT_S16_LE | \
+                                       SNDRV_PCM_FMTBIT_S32_LE, \
+       }, \
+       .ops = &cygnus_ssp_dai_ops, \
+       .suspend = cygnus_ssp_suspend, \
+       .resume = cygnus_ssp_resume, \
+}
+
+static const struct snd_soc_dai_driver cygnus_ssp_dai_info[] = {
+       INIT_CPU_DAI(0),
+       INIT_CPU_DAI(1),
+       INIT_CPU_DAI(2),
+};
+
+static struct snd_soc_dai_driver cygnus_spdif_dai_info = {
+       .name = "cygnus-spdif",
+       .playback = {
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = CYGNUS_TDM_RATE | SNDRV_PCM_RATE_88200 |
+                       SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+                       SNDRV_PCM_RATE_192000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE |
+                       SNDRV_PCM_FMTBIT_S32_LE,
+       },
+       .ops = &cygnus_ssp_dai_ops,
+       .suspend = cygnus_ssp_suspend,
+       .resume = cygnus_ssp_resume,
+};
+
+static struct snd_soc_dai_driver cygnus_ssp_dai[CYGNUS_MAX_PORTS];
+
+static const struct snd_soc_component_driver cygnus_ssp_component = {
+       .name           = "cygnus-audio",
+};
+
+/*
+ * Return < 0 if error
+ * Return 0 if disabled
+ * Return 1 if enabled and node is parsed successfully
+ */
+static int parse_ssp_child_node(struct platform_device *pdev,
+                               struct device_node *dn,
+                               struct cygnus_audio *cygaud,
+                               struct snd_soc_dai_driver *p_dai)
+{
+       struct cygnus_aio_port *aio;
+       struct cygnus_ssp_regs ssp_regs[3];
+       u32 rawval;
+       int portnum = -1;
+       enum cygnus_audio_port_type port_type;
+
+       if (of_property_read_u32(dn, "reg", &rawval)) {
+               dev_err(&pdev->dev, "Missing reg property\n");
+               return -EINVAL;
+       }
+
+       portnum = rawval;
+       switch (rawval) {
+       case 0:
+               ssp_regs[0] = INIT_SSP_REGS(0);
+               port_type = PORT_TDM;
+               break;
+       case 1:
+               ssp_regs[1] = INIT_SSP_REGS(1);
+               port_type = PORT_TDM;
+               break;
+       case 2:
+               ssp_regs[2] = INIT_SSP_REGS(2);
+               port_type = PORT_TDM;
+               break;
+       case 3:
+               port_type = PORT_SPDIF;
+               break;
+       default:
+               dev_err(&pdev->dev, "Bad value for reg %u\n", rawval);
+               return -EINVAL;
+       }
+
+       aio = &cygaud->portinfo[portnum];
+       aio->cygaud = cygaud;
+       aio->portnum = portnum;
+       aio->port_type = port_type;
+       aio->fsync_width = -1;
+
+       switch (port_type) {
+       case PORT_TDM:
+               aio->regs = ssp_regs[portnum];
+               *p_dai = cygnus_ssp_dai_info[portnum];
+               aio->mode = CYGNUS_SSPMODE_UNKNOWN;
+               break;
+
+       case PORT_SPDIF:
+               aio->regs.bf_sourcech_cfg = BF_SRC_CFG3_OFFSET;
+               aio->regs.bf_sourcech_ctrl = BF_SRC_CTRL3_OFFSET;
+               aio->regs.i2s_mclk_cfg = SPDIF_MCLK_CFG_OFFSET;
+               aio->regs.i2s_stream_cfg = SPDIF_STREAM_CFG_OFFSET;
+               *p_dai = cygnus_spdif_dai_info;
+
+               /* For the purposes of this code SPDIF can be I2S mode */
+               aio->mode = CYGNUS_SSPMODE_I2S;
+               break;
+       default:
+               dev_err(&pdev->dev, "Bad value for port_type %d\n", port_type);
+               return -EINVAL;
+       }
+
+       dev_dbg(&pdev->dev, "%s portnum = %d\n", __func__, aio->portnum);
+       aio->streams_on = 0;
+       aio->cygaud->dev = &pdev->dev;
+       aio->clk_trace.play_en = false;
+       aio->clk_trace.cap_en = false;
+
+       audio_ssp_init_portregs(aio);
+       return 0;
+}
+
+static int audio_clk_init(struct platform_device *pdev,
+                                               struct cygnus_audio *cygaud)
+{
+       int i;
+       char clk_name[PROP_LEN_MAX];
+
+       for (i = 0; i < ARRAY_SIZE(cygaud->audio_clk); i++) {
+               snprintf(clk_name, PROP_LEN_MAX, "ch%d_audio", i);
+
+               cygaud->audio_clk[i] = devm_clk_get(&pdev->dev, clk_name);
+               if (IS_ERR(cygaud->audio_clk[i]))
+                       return PTR_ERR(cygaud->audio_clk[i]);
+       }
+
+       return 0;
+}
+
+static int cygnus_ssp_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *child_node;
+       struct resource *res = pdev->resource;
+       struct cygnus_audio *cygaud;
+       int err = -EINVAL;
+       int node_count;
+       int active_port_count;
+
+       cygaud = devm_kzalloc(dev, sizeof(struct cygnus_audio), GFP_KERNEL);
+       if (!cygaud)
+               return -ENOMEM;
+
+       dev_set_drvdata(dev, cygaud);
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aud");
+       cygaud->audio = devm_ioremap_resource(dev, res);
+       if (IS_ERR(cygaud->audio))
+               return PTR_ERR(cygaud->audio);
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "i2s_in");
+       cygaud->i2s_in = devm_ioremap_resource(dev, res);
+       if (IS_ERR(cygaud->i2s_in))
+               return PTR_ERR(cygaud->i2s_in);
+
+       /* Tri-state all controlable pins until we know that we need them */
+       writel(CYGNUS_SSP_TRISTATE_MASK,
+                       cygaud->audio + AUD_MISC_SEROUT_OE_REG_BASE);
+
+       node_count = of_get_child_count(pdev->dev.of_node);
+       if ((node_count < 1) || (node_count > CYGNUS_MAX_PORTS)) {
+               dev_err(dev, "child nodes is %d.  Must be between 1 and %d\n",
+                       node_count, CYGNUS_MAX_PORTS);
+               return -EINVAL;
+       }
+
+       active_port_count = 0;
+
+       for_each_available_child_of_node(pdev->dev.of_node, child_node) {
+               err = parse_ssp_child_node(pdev, child_node, cygaud,
+                                       &cygnus_ssp_dai[active_port_count]);
+
+               /* negative is err, 0 is active and good, 1 is disabled */
+               if (err < 0)
+                       return err;
+               else if (!err) {
+                       dev_dbg(dev, "Activating DAI: %s\n",
+                               cygnus_ssp_dai[active_port_count].name);
+                       active_port_count++;
+               }
+       }
+
+       cygaud->dev = dev;
+       cygaud->active_ports = 0;
+
+       dev_dbg(dev, "Registering %d DAIs\n", active_port_count);
+       err = snd_soc_register_component(dev, &cygnus_ssp_component,
+                               cygnus_ssp_dai, active_port_count);
+       if (err) {
+               dev_err(dev, "snd_soc_register_dai failed\n");
+               return err;
+       }
+
+       cygaud->irq_num = platform_get_irq(pdev, 0);
+       if (cygaud->irq_num <= 0) {
+               dev_err(dev, "platform_get_irq failed\n");
+               err = cygaud->irq_num;
+               goto err_irq;
+       }
+
+       err = audio_clk_init(pdev, cygaud);
+       if (err) {
+               dev_err(dev, "audio clock initialization failed\n");
+               goto err_irq;
+       }
+
+       err = cygnus_soc_platform_register(dev, cygaud);
+       if (err) {
+               dev_err(dev, "platform reg error %d\n", err);
+               goto err_irq;
+       }
+
+       return 0;
+
+err_irq:
+       snd_soc_unregister_component(dev);
+       return err;
+}
+
+static int cygnus_ssp_remove(struct platform_device *pdev)
+{
+       cygnus_soc_platform_unregister(&pdev->dev);
+       snd_soc_unregister_component(&pdev->dev);
+
+       return 0;
+}
+
+static const struct of_device_id cygnus_ssp_of_match[] = {
+       { .compatible = "brcm,cygnus-audio" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, cygnus_ssp_of_match);
+
+static struct platform_driver cygnus_ssp_driver = {
+       .probe          = cygnus_ssp_probe,
+       .remove         = cygnus_ssp_remove,
+       .driver         = {
+               .name   = "cygnus-ssp",
+               .of_match_table = cygnus_ssp_of_match,
+       },
+};
+
+module_platform_driver(cygnus_ssp_driver);
+
+MODULE_ALIAS("platform:cygnus-ssp");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Broadcom");
+MODULE_DESCRIPTION("Cygnus ASoC SSP Interface");
diff --git a/sound/soc/bcm/cygnus-ssp.h b/sound/soc/bcm/cygnus-ssp.h
new file mode 100644 (file)
index 0000000..33dd343
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2014-2015 Broadcom Corporation
+ *
+ * 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.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __CYGNUS_SSP_H__
+#define __CYGNUS_SSP_H__
+
+#define CYGNUS_TDM_DAI_MAX_SLOTS 16
+
+#define CYGNUS_MAX_PLAYBACK_PORTS 4
+#define CYGNUS_MAX_CAPTURE_PORTS 3
+#define CYGNUS_MAX_I2S_PORTS 3
+#define CYGNUS_MAX_PORTS  CYGNUS_MAX_PLAYBACK_PORTS
+#define CYGNUS_AUIDO_MAX_NUM_CLKS 3
+
+#define CYGNUS_SSP_FRAMEBITS_DIV 1
+
+#define CYGNUS_SSPMODE_I2S 0
+#define CYGNUS_SSPMODE_TDM 1
+#define CYGNUS_SSPMODE_UNKNOWN -1
+
+#define CYGNUS_SSP_CLKSRC_PLL      0
+
+/* Max string length of our dt property names */
+#define PROP_LEN_MAX 40
+
+struct ringbuf_regs {
+       unsigned rdaddr;
+       unsigned wraddr;
+       unsigned baseaddr;
+       unsigned endaddr;
+       unsigned fmark;   /* freemark for play, fullmark for caputure */
+       unsigned period_bytes;
+       unsigned buf_size;
+};
+
+#define RINGBUF_REG_PLAYBACK(num) ((struct ringbuf_regs) { \
+       .rdaddr = SRC_RBUF_ ##num## _RDADDR_OFFSET, \
+       .wraddr = SRC_RBUF_ ##num## _WRADDR_OFFSET, \
+       .baseaddr = SRC_RBUF_ ##num## _BASEADDR_OFFSET, \
+       .endaddr = SRC_RBUF_ ##num## _ENDADDR_OFFSET, \
+       .fmark = SRC_RBUF_ ##num## _FREE_MARK_OFFSET, \
+       .period_bytes = 0, \
+       .buf_size = 0, \
+})
+
+#define RINGBUF_REG_CAPTURE(num) ((struct ringbuf_regs)  { \
+       .rdaddr = DST_RBUF_ ##num## _RDADDR_OFFSET, \
+       .wraddr = DST_RBUF_ ##num## _WRADDR_OFFSET, \
+       .baseaddr = DST_RBUF_ ##num## _BASEADDR_OFFSET, \
+       .endaddr = DST_RBUF_ ##num## _ENDADDR_OFFSET, \
+       .fmark = DST_RBUF_ ##num## _FULL_MARK_OFFSET, \
+       .period_bytes = 0, \
+       .buf_size = 0, \
+})
+
+enum cygnus_audio_port_type {
+       PORT_TDM,
+       PORT_SPDIF,
+};
+
+struct cygnus_ssp_regs {
+       u32 i2s_stream_cfg;
+       u32 i2s_cfg;
+       u32 i2s_cap_stream_cfg;
+       u32 i2s_cap_cfg;
+       u32 i2s_mclk_cfg;
+
+       u32 bf_destch_ctrl;
+       u32 bf_destch_cfg;
+       u32 bf_sourcech_ctrl;
+       u32 bf_sourcech_cfg;
+       u32 bf_sourcech_grp;
+};
+
+struct cygnus_track_clk {
+       bool cap_en;
+       bool play_en;
+       bool cap_clk_en;
+       bool play_clk_en;
+};
+
+struct cygnus_aio_port {
+       int portnum;
+       int mode;
+       bool is_slave;
+       int streams_on;   /* will be 0 if both capture and play are off */
+       int fsync_width;
+       int port_type;
+
+       u32 mclk;
+       u32 lrclk;
+       u32 bit_per_frame;
+       u32 pll_clk_num;
+
+       struct cygnus_audio *cygaud;
+       struct cygnus_ssp_regs regs;
+
+       struct ringbuf_regs play_rb_regs;
+       struct ringbuf_regs capture_rb_regs;
+
+       struct snd_pcm_substream *play_stream;
+       struct snd_pcm_substream *capture_stream;
+
+       struct cygnus_track_clk clk_trace;
+};
+
+
+struct cygnus_audio {
+       struct cygnus_aio_port  portinfo[CYGNUS_MAX_PORTS];
+
+       int irq_num;
+       void __iomem *audio;
+       struct device *dev;
+       void __iomem *i2s_in;
+
+       struct clk *audio_clk[CYGNUS_AUIDO_MAX_NUM_CLKS];
+       int active_ports;
+       unsigned long vco_rate;
+};
+
+extern int cygnus_ssp_get_mode(struct snd_soc_dai *cpu_dai);
+extern int cygnus_ssp_add_pll_tweak_controls(struct snd_soc_pcm_runtime *rtd);
+extern int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai,
+                                               int len);
+extern int cygnus_soc_platform_register(struct device *dev,
+                                       struct cygnus_audio *cygaud);
+extern int cygnus_soc_platform_unregister(struct device *dev);
+extern int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai,
+       int len);
+#endif
index f3fb98f0a995bb41dd473ccc4443164e404c9883..1cd6ab344d67def61db455d956ac2d7075a1f6d8 100644 (file)
@@ -32,6 +32,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_ADAU1977_SPI if SPI_MASTER
        select SND_SOC_ADAU1977_I2C if I2C
        select SND_SOC_ADAU1701 if I2C
+       select SND_SOC_ADAU7002
        select SND_SOC_ADS117X
        select SND_SOC_AK4104 if SPI_MASTER
        select SND_SOC_AK4535 if I2C
@@ -46,6 +47,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_BT_SCO
        select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
        select SND_SOC_CS35L32 if I2C
+       select SND_SOC_CS35L33 if I2C
        select SND_SOC_CS42L51_I2C if I2C
        select SND_SOC_CS42L52 if I2C && INPUT
        select SND_SOC_CS42L56 if I2C && INPUT
@@ -57,6 +59,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_CS42XX8_I2C if I2C
        select SND_SOC_CS4349 if I2C
        select SND_SOC_CS47L24 if MFD_CS47L24
+       select SND_SOC_CS53L30 if I2C
        select SND_SOC_CX20442 if TTY
        select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI
        select SND_SOC_DA7213 if I2C
@@ -84,6 +87,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_MAX98925 if I2C
        select SND_SOC_MAX98926 if I2C
        select SND_SOC_MAX9850 if I2C
+       select SND_SOC_MAX9860 if I2C
        select SND_SOC_MAX9768 if I2C
        select SND_SOC_MAX9877 if I2C
        select SND_SOC_MC13783 if MFD_MC13XXX
@@ -269,8 +273,12 @@ config SND_SOC_AD1980
 config SND_SOC_AD73311
        tristate
 
+config SND_SOC_ADAU_UTILS
+       tristate
+
 config SND_SOC_ADAU1373
        tristate
+       select SND_SOC_ADAU_UTILS
 
 config SND_SOC_ADAU1701
        tristate "Analog Devices ADAU1701 CODEC"
@@ -280,6 +288,7 @@ config SND_SOC_ADAU1701
 config SND_SOC_ADAU17X1
        tristate
        select SND_SOC_SIGMADSP_REGMAP
+       select SND_SOC_ADAU_UTILS
 
 config SND_SOC_ADAU1761
        tristate
@@ -322,6 +331,9 @@ config SND_SOC_ADAU1977_I2C
        select SND_SOC_ADAU1977
        select REGMAP_I2C
 
+config SND_SOC_ADAU7002
+       tristate "Analog Devices ADAU7002 Stereo PDM-to-I2S/TDM Converter"
+
 config SND_SOC_ADAV80X
        tristate
 
@@ -371,7 +383,7 @@ config SND_SOC_ALC5632
        tristate
 
 config SND_SOC_BT_SCO
-       tristate
+       tristate "Dummy BT SCO codec driver"
 
 config SND_SOC_CQ0093VC
        tristate
@@ -380,6 +392,10 @@ config SND_SOC_CS35L32
        tristate "Cirrus Logic CS35L32 CODEC"
        depends on I2C
 
+config SND_SOC_CS35L33
+       tristate "Cirrus Logic CS35L33 CODEC"
+       depends on I2C
+
 config SND_SOC_CS42L51
        tristate
 
@@ -450,6 +466,11 @@ config SND_SOC_CS4349
 config SND_SOC_CS47L24
        tristate
 
+# Cirrus Logic Quad-Channel ADC
+config SND_SOC_CS53L30
+       tristate "Cirrus Logic CS53L30 CODEC"
+       depends on I2C
+
 config SND_SOC_CX20442
        tristate
        depends on TTY
@@ -536,6 +557,10 @@ config SND_SOC_MAX98357A
 config SND_SOC_MAX98371
        tristate
 
+config SND_SOC_MAX98504
+       tristate "Maxim MAX98504 speaker amplifier"
+       depends on I2C
+
 config SND_SOC_MAX9867
        tristate
 
@@ -548,6 +573,11 @@ config SND_SOC_MAX98926
 config SND_SOC_MAX9850
        tristate
 
+config SND_SOC_MAX9860
+       tristate "Maxim MAX9860 Mono Audio Voice Codec"
+       depends on I2C
+       select REGMAP_I2C
+
 config SND_SOC_PCM1681
        tristate "Texas Instruments PCM1681 CODEC"
        depends on I2C
@@ -644,6 +674,9 @@ config SND_SOC_RT298
 config SND_SOC_RT5514
        tristate
 
+config SND_SOC_RT5514_SPI
+       tristate
+
 config SND_SOC_RT5616
        tristate "Realtek RT5616 CODEC"
        depends on I2C
@@ -969,7 +1002,8 @@ config SND_SOC_WM8983
        tristate
 
 config SND_SOC_WM8985
-       tristate
+       tristate "Wolfson Microelectronics WM8985 and WM8758 codec driver"
+       depends on SND_SOC_I2C_AND_SPI
 
 config SND_SOC_WM8988
        tristate
index 0f548fd34ca3fd9e8a1b080cd2d38a480c3c5212..58036af2c7d9837b347ea2b6cdccaad72a711a53 100644 (file)
@@ -7,6 +7,7 @@ snd-soc-ad193x-spi-objs := ad193x-spi.o
 snd-soc-ad193x-i2c-objs := ad193x-i2c.o
 snd-soc-ad1980-objs := ad1980.o
 snd-soc-ad73311-objs := ad73311.o
+snd-soc-adau-utils-objs := adau-utils.o
 snd-soc-adau1373-objs := adau1373.o
 snd-soc-adau1701-objs := adau1701.o
 snd-soc-adau17x1-objs := adau17x1.o
@@ -19,6 +20,7 @@ snd-soc-adau1781-spi-objs := adau1781-spi.o
 snd-soc-adau1977-objs := adau1977.o
 snd-soc-adau1977-spi-objs := adau1977-spi.o
 snd-soc-adau1977-i2c-objs := adau1977-i2c.o
+snd-soc-adau7002-objs := adau7002.o
 snd-soc-adav80x-objs := adav80x.o
 snd-soc-adav801-objs := adav801.o
 snd-soc-adav803-objs := adav803.o
@@ -35,6 +37,7 @@ snd-soc-arizona-objs := arizona.o
 snd-soc-bt-sco-objs := bt-sco.o
 snd-soc-cq93vc-objs := cq93vc.o
 snd-soc-cs35l32-objs := cs35l32.o
+snd-soc-cs35l33-objs := cs35l33.o
 snd-soc-cs42l51-objs := cs42l51.o
 snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
 snd-soc-cs42l52-objs := cs42l52.o
@@ -49,6 +52,7 @@ snd-soc-cs42xx8-objs := cs42xx8.o
 snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
 snd-soc-cs4349-objs := cs4349.o
 snd-soc-cs47l24-objs := cs47l24.o
+snd-soc-cs53l30-objs := cs53l30.o
 snd-soc-cx20442-objs := cx20442.o
 snd-soc-da7210-objs := da7210.o
 snd-soc-da7213-objs := da7213.o
@@ -79,6 +83,7 @@ snd-soc-max9867-objs := max9867.o
 snd-soc-max98925-objs := max98925.o
 snd-soc-max98926-objs := max98926.o
 snd-soc-max9850-objs := max9850.o
+snd-soc-max9860-objs := max9860.o
 snd-soc-mc13783-objs := mc13783.o
 snd-soc-ml26124-objs := ml26124.o
 snd-soc-nau8825-objs := nau8825.o
@@ -100,6 +105,7 @@ snd-soc-rl6347a-objs := rl6347a.o
 snd-soc-rt286-objs := rt286.o
 snd-soc-rt298-objs := rt298.o
 snd-soc-rt5514-objs := rt5514.o
+snd-soc-rt5514-spi-objs := rt5514-spi.o
 snd-soc-rt5616-objs := rt5616.o
 snd-soc-rt5631-objs := rt5631.o
 snd-soc-rt5640-objs := rt5640.o
@@ -208,6 +214,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o
 
 # Amp
 snd-soc-max9877-objs := max9877.o
+snd-soc-max98504-objs := max98504.o
 snd-soc-tpa6130a2-objs := tpa6130a2.o
 snd-soc-tas2552-objs := tas2552.o
 
@@ -220,6 +227,7 @@ obj-$(CONFIG_SND_SOC_AD193X_SPI)    += snd-soc-ad193x-spi.o
 obj-$(CONFIG_SND_SOC_AD193X_I2C)       += snd-soc-ad193x-i2c.o
 obj-$(CONFIG_SND_SOC_AD1980)   += snd-soc-ad1980.o
 obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
+obj-$(CONFIG_SND_SOC_ADAU_UTILS)       += snd-soc-adau-utils.o
 obj-$(CONFIG_SND_SOC_ADAU1373) += snd-soc-adau1373.o
 obj-$(CONFIG_SND_SOC_ADAU1701)         += snd-soc-adau1701.o
 obj-$(CONFIG_SND_SOC_ADAU17X1)         += snd-soc-adau17x1.o
@@ -232,6 +240,7 @@ obj-$(CONFIG_SND_SOC_ADAU1781_SPI)  += snd-soc-adau1781-spi.o
 obj-$(CONFIG_SND_SOC_ADAU1977)         += snd-soc-adau1977.o
 obj-$(CONFIG_SND_SOC_ADAU1977_SPI)     += snd-soc-adau1977-spi.o
 obj-$(CONFIG_SND_SOC_ADAU1977_I2C)     += snd-soc-adau1977-i2c.o
+obj-$(CONFIG_SND_SOC_ADAU7002) += snd-soc-adau7002.o
 obj-$(CONFIG_SND_SOC_ADAV80X)  += snd-soc-adav80x.o
 obj-$(CONFIG_SND_SOC_ADAV801)  += snd-soc-adav801.o
 obj-$(CONFIG_SND_SOC_ADAV803)  += snd-soc-adav803.o
@@ -250,6 +259,7 @@ obj-$(CONFIG_SND_SOC_ARIZONA)       += snd-soc-arizona.o
 obj-$(CONFIG_SND_SOC_BT_SCO)   += snd-soc-bt-sco.o
 obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
 obj-$(CONFIG_SND_SOC_CS35L32)  += snd-soc-cs35l32.o
+obj-$(CONFIG_SND_SOC_CS35L33)  += snd-soc-cs35l33.o
 obj-$(CONFIG_SND_SOC_CS42L51)  += snd-soc-cs42l51.o
 obj-$(CONFIG_SND_SOC_CS42L51_I2C)      += snd-soc-cs42l51-i2c.o
 obj-$(CONFIG_SND_SOC_CS42L52)  += snd-soc-cs42l52.o
@@ -264,6 +274,7 @@ obj-$(CONFIG_SND_SOC_CS42XX8)       += snd-soc-cs42xx8.o
 obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
 obj-$(CONFIG_SND_SOC_CS4349)   += snd-soc-cs4349.o
 obj-$(CONFIG_SND_SOC_CS47L24)  += snd-soc-cs47l24.o
+obj-$(CONFIG_SND_SOC_CS53L30)  += snd-soc-cs53l30.o
 obj-$(CONFIG_SND_SOC_CX20442)  += snd-soc-cx20442.o
 obj-$(CONFIG_SND_SOC_DA7210)   += snd-soc-da7210.o
 obj-$(CONFIG_SND_SOC_DA7213)   += snd-soc-da7213.o
@@ -293,6 +304,7 @@ obj-$(CONFIG_SND_SOC_MAX9867)       += snd-soc-max9867.o
 obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o
 obj-$(CONFIG_SND_SOC_MAX98926) += snd-soc-max98926.o
 obj-$(CONFIG_SND_SOC_MAX9850)  += snd-soc-max9850.o
+obj-$(CONFIG_SND_SOC_MAX9860)  += snd-soc-max9860.o
 obj-$(CONFIG_SND_SOC_MC13783)  += snd-soc-mc13783.o
 obj-$(CONFIG_SND_SOC_ML26124)  += snd-soc-ml26124.o
 obj-$(CONFIG_SND_SOC_NAU8825)   += snd-soc-nau8825.o
@@ -314,6 +326,7 @@ obj-$(CONFIG_SND_SOC_RL6347A)       += snd-soc-rl6347a.o
 obj-$(CONFIG_SND_SOC_RT286)    += snd-soc-rt286.o
 obj-$(CONFIG_SND_SOC_RT298)    += snd-soc-rt298.o
 obj-$(CONFIG_SND_SOC_RT5514)   += snd-soc-rt5514.o
+obj-$(CONFIG_SND_SOC_RT5514_SPI)       += snd-soc-rt5514-spi.o
 obj-$(CONFIG_SND_SOC_RT5616)   += snd-soc-rt5616.o
 obj-$(CONFIG_SND_SOC_RT5631)   += snd-soc-rt5631.o
 obj-$(CONFIG_SND_SOC_RT5640)   += snd-soc-rt5640.o
@@ -419,4 +432,5 @@ obj-$(CONFIG_SND_SOC_WM_HUBS)       += snd-soc-wm-hubs.o
 
 # Amp
 obj-$(CONFIG_SND_SOC_MAX9877)  += snd-soc-max9877.o
+obj-$(CONFIG_SND_SOC_MAX98504) += snd-soc-max98504.o
 obj-$(CONFIG_SND_SOC_TPA6130A2)        += snd-soc-tpa6130a2.o
diff --git a/sound/soc/codecs/adau-utils.c b/sound/soc/codecs/adau-utils.c
new file mode 100644 (file)
index 0000000..19d6a6f
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Shared helper functions for devices from the ADAU family
+ *
+ * Copyright 2011-2016 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/gcd.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "adau-utils.h"
+
+int adau_calc_pll_cfg(unsigned int freq_in, unsigned int freq_out,
+       uint8_t regs[5])
+{
+       unsigned int r, n, m, i, j;
+       unsigned int div;
+
+       if (!freq_out) {
+               r = 0;
+               n = 0;
+               m = 0;
+               div = 0;
+       } else {
+               if (freq_out % freq_in != 0) {
+                       div = DIV_ROUND_UP(freq_in, 13500000);
+                       freq_in /= div;
+                       r = freq_out / freq_in;
+                       i = freq_out % freq_in;
+                       j = gcd(i, freq_in);
+                       n = i / j;
+                       m = freq_in / j;
+                       div--;
+               } else {
+                       r = freq_out / freq_in;
+                       n = 0;
+                       m = 0;
+                       div = 0;
+               }
+               if (n > 0xffff || m > 0xffff || div > 3 || r > 8 || r < 2)
+                       return -EINVAL;
+       }
+
+       regs[0] = m >> 8;
+       regs[1] = m & 0xff;
+       regs[2] = n >> 8;
+       regs[3] = n & 0xff;
+       regs[4] = (r << 3) | (div << 1);
+       if (m != 0)
+               regs[4] |= 1; /* Fractional mode */
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(adau_calc_pll_cfg);
+
+MODULE_DESCRIPTION("ASoC ADAU audio CODECs shared helper functions");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/adau-utils.h b/sound/soc/codecs/adau-utils.h
new file mode 100644 (file)
index 0000000..939b5f3
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef SOUND_SOC_CODECS_ADAU_PLL_H
+#define SOUND_SOC_CODECS_ADAU_PLL_H
+
+int adau_calc_pll_cfg(unsigned int freq_in, unsigned int freq_out,
+       uint8_t regs[5]);
+
+#endif
index fe1353a797b9449e7b8186771c8476b2cf746021..1556b360fa1516b2c5048b980274e72d8540a228 100644 (file)
@@ -23,6 +23,7 @@
 #include <sound/adau1373.h>
 
 #include "adau1373.h"
+#include "adau-utils.h"
 
 struct adau1373_dai {
        unsigned int clk_src;
@@ -1254,7 +1255,8 @@ static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
 {
        struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
        unsigned int dpll_div = 0;
-       unsigned int x, r, n, m, i, j, mode;
+       uint8_t pll_regs[5];
+       int ret;
 
        switch (pll_id) {
        case ADAU1373_PLL1:
@@ -1295,27 +1297,8 @@ static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
                dpll_div++;
        }
 
-       if (freq_out % freq_in != 0) {
-               /* fout = fin * (r + (n/m)) / x */
-               x = DIV_ROUND_UP(freq_in, 13500000);
-               freq_in /= x;
-               r = freq_out / freq_in;
-               i = freq_out % freq_in;
-               j = gcd(i, freq_in);
-               n = i / j;
-               m = freq_in / j;
-               x--;
-               mode = 1;
-       } else {
-               /* fout = fin / r */
-               r = freq_out / freq_in;
-               n = 0;
-               m = 0;
-               x = 0;
-               mode = 0;
-       }
-
-       if (r < 2 || r > 8 || x > 3 || m > 0xffff || n > 0xffff)
+       ret = adau_calc_pll_cfg(freq_in, freq_out, pll_regs);
+       if (ret)
                return -EINVAL;
 
        if (dpll_div) {
@@ -1330,12 +1313,11 @@ static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
 
        regmap_write(adau1373->regmap, ADAU1373_DPLL_CTRL(pll_id),
                (source << 4) | dpll_div);
-       regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL1(pll_id), (m >> 8) & 0xff);
-       regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL2(pll_id), m & 0xff);
-       regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL3(pll_id), (n >> 8) & 0xff);
-       regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL4(pll_id), n & 0xff);
-       regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL5(pll_id),
-               (r << 3) | (x << 1) | mode);
+       regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL1(pll_id), pll_regs[0]);
+       regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL2(pll_id), pll_regs[1]);
+       regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL3(pll_id), pll_regs[2]);
+       regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL4(pll_id), pll_regs[3]);
+       regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL5(pll_id), pll_regs[4]);
 
        /* Set sysclk to pll_rate / 4 */
        regmap_update_bits(adau1373->regmap, ADAU1373_CLK_SRC_DIV(pll_id), 0x3f, 0x09);
index 8de010f758cd8d5007cf661a590f6e7c473da385..9e7f257f17f87591f380152553fc4c41725b1e2e 100644 (file)
@@ -31,7 +31,7 @@ static int adau1761_i2c_probe(struct i2c_client *client,
 
 static int adau1761_i2c_remove(struct i2c_client *client)
 {
-       snd_soc_unregister_codec(&client->dev);
+       adau17x1_remove(&client->dev);
        return 0;
 }
 
index d9171245bd9f11a177ce2429c0cbf50ef159af9d..a0b214be759a8700d3ad81eb098ed111b4b4d6c8 100644 (file)
@@ -48,7 +48,7 @@ static int adau1761_spi_probe(struct spi_device *spi)
 
 static int adau1761_spi_remove(struct spi_device *spi)
 {
-       snd_soc_unregister_codec(&spi->dev);
+       adau17x1_remove(&spi->dev);
        return 0;
 }
 
index 06cbca84cf02150ab0a83bbf114955ffe6ea62d1..7b9d1802d1598e9c170d28df0d28014c06642742 100644 (file)
@@ -31,7 +31,7 @@ static int adau1781_i2c_probe(struct i2c_client *client,
 
 static int adau1781_i2c_remove(struct i2c_client *client)
 {
-       snd_soc_unregister_codec(&client->dev);
+       adau17x1_remove(&client->dev);
        return 0;
 }
 
index 3d965a01b99cee318da30a080baebc8a060852cc..9b233544d2e8f34a49791237162cb8f965b453d5 100644 (file)
@@ -48,7 +48,7 @@ static int adau1781_spi_probe(struct spi_device *spi)
 
 static int adau1781_spi_remove(struct spi_device *spi)
 {
-       snd_soc_unregister_codec(&spi->dev);
+       adau17x1_remove(&spi->dev);
        return 0;
 }
 
index fcf05b254ecd5c37a91dbff5a3ccc3de1069e2f0..439aa3ff1f99cd517f28988f80fe2b34c6489ab2 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <sound/core.h>
@@ -23,6 +24,7 @@
 
 #include "sigmadsp.h"
 #include "adau17x1.h"
+#include "adau-utils.h"
 
 static const char * const adau17x1_capture_mixer_boost_text[] = {
        "Normal operation", "Boost Level 1", "Boost Level 2", "Boost Level 3",
@@ -302,6 +304,116 @@ bool adau17x1_has_dsp(struct adau *adau)
 }
 EXPORT_SYMBOL_GPL(adau17x1_has_dsp);
 
+static int adau17x1_set_dai_pll(struct snd_soc_dai *dai, int pll_id,
+       int source, unsigned int freq_in, unsigned int freq_out)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct adau *adau = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       if (freq_in < 8000000 || freq_in > 27000000)
+               return -EINVAL;
+
+       ret = adau_calc_pll_cfg(freq_in, freq_out, adau->pll_regs);
+       if (ret < 0)
+               return ret;
+
+       /* The PLL register is 6 bytes long and can only be written at once. */
+       ret = regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
+                       adau->pll_regs, ARRAY_SIZE(adau->pll_regs));
+       if (ret)
+               return ret;
+
+       adau->pll_freq = freq_out;
+
+       return 0;
+}
+
+static int adau17x1_set_dai_sysclk(struct snd_soc_dai *dai,
+               int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(dai->codec);
+       struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
+       bool is_pll;
+       bool was_pll;
+
+       switch (clk_id) {
+       case ADAU17X1_CLK_SRC_MCLK:
+               is_pll = false;
+               break;
+       case ADAU17X1_CLK_SRC_PLL_AUTO:
+               if (!adau->mclk)
+                       return -EINVAL;
+               /* Fall-through */
+       case ADAU17X1_CLK_SRC_PLL:
+               is_pll = true;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (adau->clk_src) {
+       case ADAU17X1_CLK_SRC_MCLK:
+               was_pll = false;
+               break;
+       case ADAU17X1_CLK_SRC_PLL:
+       case ADAU17X1_CLK_SRC_PLL_AUTO:
+               was_pll = true;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       adau->sysclk = freq;
+
+       if (is_pll != was_pll) {
+               if (is_pll) {
+                       snd_soc_dapm_add_routes(dapm,
+                               &adau17x1_dapm_pll_route, 1);
+               } else {
+                       snd_soc_dapm_del_routes(dapm,
+                               &adau17x1_dapm_pll_route, 1);
+               }
+       }
+
+       adau->clk_src = clk_id;
+
+       return 0;
+}
+
+static int adau17x1_auto_pll(struct snd_soc_dai *dai,
+       struct snd_pcm_hw_params *params)
+{
+       struct adau *adau = snd_soc_dai_get_drvdata(dai);
+       unsigned int pll_rate;
+
+       switch (params_rate(params)) {
+       case 48000:
+       case 8000:
+       case 12000:
+       case 16000:
+       case 24000:
+       case 32000:
+       case 96000:
+               pll_rate = 48000 * 1024;
+               break;
+       case 44100:
+       case 7350:
+       case 11025:
+       case 14700:
+       case 22050:
+       case 29400:
+       case 88200:
+               pll_rate = 44100 * 1024;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return adau17x1_set_dai_pll(dai, ADAU17X1_PLL, ADAU17X1_PLL_SRC_MCLK,
+               clk_get_rate(adau->mclk), pll_rate);
+}
+
 static int adau17x1_hw_params(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 {
@@ -311,10 +423,19 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream,
        unsigned int freq;
        int ret;
 
-       if (adau->clk_src == ADAU17X1_CLK_SRC_PLL)
+       switch (adau->clk_src) {
+       case ADAU17X1_CLK_SRC_PLL_AUTO:
+               ret = adau17x1_auto_pll(dai, params);
+               if (ret)
+                       return ret;
+               /* Fall-through */
+       case ADAU17X1_CLK_SRC_PLL:
                freq = adau->pll_freq;
-       else
+               break;
+       default:
                freq = adau->sysclk;
+               break;
+       }
 
        if (freq % params_rate(params) != 0)
                return -EINVAL;
@@ -386,93 +507,6 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream,
                        ADAU17X1_SERIAL_PORT1_DELAY_MASK, val);
 }
 
-static int adau17x1_set_dai_pll(struct snd_soc_dai *dai, int pll_id,
-       int source, unsigned int freq_in, unsigned int freq_out)
-{
-       struct snd_soc_codec *codec = dai->codec;
-       struct adau *adau = snd_soc_codec_get_drvdata(codec);
-       unsigned int r, n, m, i, j;
-       unsigned int div;
-       int ret;
-
-       if (freq_in < 8000000 || freq_in > 27000000)
-               return -EINVAL;
-
-       if (!freq_out) {
-               r = 0;
-               n = 0;
-               m = 0;
-               div = 0;
-       } else {
-               if (freq_out % freq_in != 0) {
-                       div = DIV_ROUND_UP(freq_in, 13500000);
-                       freq_in /= div;
-                       r = freq_out / freq_in;
-                       i = freq_out % freq_in;
-                       j = gcd(i, freq_in);
-                       n = i / j;
-                       m = freq_in / j;
-                       div--;
-               } else {
-                       r = freq_out / freq_in;
-                       n = 0;
-                       m = 0;
-                       div = 0;
-               }
-               if (n > 0xffff || m > 0xffff || div > 3 || r > 8 || r < 2)
-                       return -EINVAL;
-       }
-
-       adau->pll_regs[0] = m >> 8;
-       adau->pll_regs[1] = m & 0xff;
-       adau->pll_regs[2] = n >> 8;
-       adau->pll_regs[3] = n & 0xff;
-       adau->pll_regs[4] = (r << 3) | (div << 1);
-       if (m != 0)
-               adau->pll_regs[4] |= 1; /* Fractional mode */
-
-       /* The PLL register is 6 bytes long and can only be written at once. */
-       ret = regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
-                       adau->pll_regs, ARRAY_SIZE(adau->pll_regs));
-       if (ret)
-               return ret;
-
-       adau->pll_freq = freq_out;
-
-       return 0;
-}
-
-static int adau17x1_set_dai_sysclk(struct snd_soc_dai *dai,
-               int clk_id, unsigned int freq, int dir)
-{
-       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(dai->codec);
-       struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
-
-       switch (clk_id) {
-       case ADAU17X1_CLK_SRC_MCLK:
-       case ADAU17X1_CLK_SRC_PLL:
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       adau->sysclk = freq;
-
-       if (adau->clk_src != clk_id) {
-               if (clk_id == ADAU17X1_CLK_SRC_PLL) {
-                       snd_soc_dapm_add_routes(dapm,
-                               &adau17x1_dapm_pll_route, 1);
-               } else {
-                       snd_soc_dapm_del_routes(dapm,
-                               &adau17x1_dapm_pll_route, 1);
-               }
-       }
-
-       adau->clk_src = clk_id;
-
-       return 0;
-}
-
 static int adau17x1_set_dai_fmt(struct snd_soc_dai *dai,
                unsigned int fmt)
 {
@@ -857,6 +891,10 @@ int adau17x1_add_routes(struct snd_soc_codec *codec)
                ret = snd_soc_dapm_add_routes(dapm, adau17x1_no_dsp_dapm_routes,
                        ARRAY_SIZE(adau17x1_no_dsp_dapm_routes));
        }
+
+       if (adau->clk_src != ADAU17X1_CLK_SRC_MCLK)
+               snd_soc_dapm_add_routes(dapm, &adau17x1_dapm_pll_route, 1);
+
        return ret;
 }
 EXPORT_SYMBOL_GPL(adau17x1_add_routes);
@@ -879,6 +917,7 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
        const char *firmware_name)
 {
        struct adau *adau;
+       int ret;
 
        if (IS_ERR(regmap))
                return PTR_ERR(regmap);
@@ -887,6 +926,30 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
        if (!adau)
                return -ENOMEM;
 
+       adau->mclk = devm_clk_get(dev, "mclk");
+       if (IS_ERR(adau->mclk)) {
+               if (PTR_ERR(adau->mclk) != -ENOENT)
+                       return PTR_ERR(adau->mclk);
+               /* Clock is optional (for the driver) */
+               adau->mclk = NULL;
+       } else if (adau->mclk) {
+               adau->clk_src = ADAU17X1_CLK_SRC_PLL_AUTO;
+
+               /*
+                * Any valid PLL output rate will work at this point, use one
+                * that is likely to be chosen later as well. The register will
+                * be written when the PLL is powered up for the first time.
+                */
+               ret = adau_calc_pll_cfg(clk_get_rate(adau->mclk), 48000 * 1024,
+                               adau->pll_regs);
+               if (ret < 0)
+                       return ret;
+
+               ret = clk_prepare_enable(adau->mclk);
+               if (ret)
+                       return ret;
+       }
+
        adau->regmap = regmap;
        adau->switch_mode = switch_mode;
        adau->type = type;
@@ -910,6 +973,16 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
 }
 EXPORT_SYMBOL_GPL(adau17x1_probe);
 
+void adau17x1_remove(struct device *dev)
+{
+       struct adau *adau = dev_get_drvdata(dev);
+
+       snd_soc_unregister_codec(dev);
+       if (adau->mclk)
+               clk_disable_unprepare(adau->mclk);
+}
+EXPORT_SYMBOL_GPL(adau17x1_remove);
+
 MODULE_DESCRIPTION("ASoC ADAU1X61/ADAU1X81 common code");
 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
 MODULE_LICENSE("GPL");
index 5ae87a084d97e24a6aa243ba6cb1afc45290ac43..bf04b7efee4084cf77fa934eadf6ab7a3506b1ad 100644 (file)
@@ -22,13 +22,18 @@ enum adau17x1_pll_src {
 };
 
 enum adau17x1_clk_src {
+       /* Automatically configure PLL based on the sample rate */
+       ADAU17X1_CLK_SRC_PLL_AUTO,
        ADAU17X1_CLK_SRC_MCLK,
        ADAU17X1_CLK_SRC_PLL,
 };
 
+struct clk;
+
 struct adau {
        unsigned int sysclk;
        unsigned int pll_freq;
+       struct clk *mclk;
 
        enum adau17x1_clk_src clk_src;
        enum adau17x1_type type;
@@ -52,6 +57,7 @@ int adau17x1_add_routes(struct snd_soc_codec *codec);
 int adau17x1_probe(struct device *dev, struct regmap *regmap,
        enum adau17x1_type type, void (*switch_mode)(struct device *dev),
        const char *firmware_name);
+void adau17x1_remove(struct device *dev);
 int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
        enum adau17x1_micbias_voltage micbias);
 bool adau17x1_readable_register(struct device *dev, unsigned int reg);
diff --git a/sound/soc/codecs/adau7002.c b/sound/soc/codecs/adau7002.c
new file mode 100644 (file)
index 0000000..9df72c6
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * ADAU7002 Stereo PDM-to-I2S/TDM converter driver
+ *
+ * Copyright 2014-2016 Analog Devices
+ *  Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include <sound/soc.h>
+
+static const struct snd_soc_dapm_widget adau7002_widgets[] = {
+       SND_SOC_DAPM_INPUT("PDM_DAT"),
+       SND_SOC_DAPM_REGULATOR_SUPPLY("IOVDD", 0, 0),
+};
+
+static const struct snd_soc_dapm_route adau7002_routes[] = {
+       { "Capture", NULL, "PDM_DAT" },
+       { "Capture", NULL, "IOVDD" },
+};
+
+static struct snd_soc_dai_driver adau7002_dai = {
+       .name = "adau7002-hifi",
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_96000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE |
+                       SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE |
+                       SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE,
+               .sig_bits = 20,
+       },
+};
+
+static const struct snd_soc_codec_driver adau7002_codec_driver = {
+       .dapm_widgets = adau7002_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(adau7002_widgets),
+       .dapm_routes = adau7002_routes,
+       .num_dapm_routes = ARRAY_SIZE(adau7002_routes),
+};
+
+static int adau7002_probe(struct platform_device *pdev)
+{
+       return snd_soc_register_codec(&pdev->dev, &adau7002_codec_driver,
+                       &adau7002_dai, 1);
+}
+
+static int adau7002_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_codec(&pdev->dev);
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id adau7002_dt_ids[] = {
+       { .compatible = "adi,adau7002", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, adau7002_dt_ids);
+#endif
+
+static struct platform_driver adau7002_driver = {
+       .driver = {
+               .name = "adau7002",
+               .of_match_table = of_match_ptr(adau7002_dt_ids),
+       },
+       .probe = adau7002_probe,
+       .remove = adau7002_remove,
+};
+module_platform_driver(adau7002_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ADAU7002 Stereo PDM-to-I2S/TDM Converter driver");
+MODULE_LICENSE("GPL v2");
index 5013d2ba0c10a968045d83e5e5cef6e223e55436..97798d250f08689eff1093979e2ce9bb116f66eb 100644 (file)
@@ -437,15 +437,25 @@ static struct snd_soc_dai_driver ak4613_dai = {
        .symmetric_rates = 1,
 };
 
-static int ak4613_resume(struct snd_soc_codec *codec)
+static int ak4613_suspend(struct snd_soc_codec *codec)
 {
        struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
 
+       regcache_cache_only(regmap, true);
        regcache_mark_dirty(regmap);
+       return 0;
+}
+
+static int ak4613_resume(struct snd_soc_codec *codec)
+{
+       struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+
+       regcache_cache_only(regmap, false);
        return regcache_sync(regmap);
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_ak4613 = {
+       .suspend                = ak4613_suspend,
        .resume                 = ak4613_resume,
        .set_bias_level         = ak4613_set_bias_level,
        .controls               = ak4613_snd_controls,
index 4d8b9e49e8d6dcb47f7c6516561fd98d847bfdf9..cc941d66ec3d2f4d1d75104f46c1329ebb9abb8a 100644 (file)
@@ -523,15 +523,23 @@ static struct snd_soc_dai_driver ak4642_dai = {
        .symmetric_rates = 1,
 };
 
-static int ak4642_resume(struct snd_soc_codec *codec)
+static int ak4642_suspend(struct snd_soc_codec *codec)
 {
        struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
 
+       regcache_cache_only(regmap, true);
        regcache_mark_dirty(regmap);
-       regcache_sync(regmap);
        return 0;
 }
 
+static int ak4642_resume(struct snd_soc_codec *codec)
+{
+       struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+
+       regcache_cache_only(regmap, false);
+       regcache_sync(regmap);
+       return 0;
+}
 static int ak4642_probe(struct snd_soc_codec *codec)
 {
        struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
@@ -544,6 +552,7 @@ static int ak4642_probe(struct snd_soc_codec *codec)
 
 static struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
        .probe                  = ak4642_probe,
+       .suspend                = ak4642_suspend,
        .resume                 = ak4642_resume,
        .set_bias_level         = ak4642_set_bias_level,
        .controls               = ak4642_snd_controls,
index 664a8c044ffb1c3abd58afe458ba538a775d9a47..ecfdbfcae366cf5840e40b1c847d9bc6d56ba4e4 100644 (file)
@@ -85,30 +85,9 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w,
 {
        struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
-       struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
-       bool manual_ena = false;
        int val;
 
-       switch (arizona->type) {
-       case WM5102:
-               switch (arizona->rev) {
-               case 0:
-                       break;
-               default:
-                       manual_ena = true;
-                       break;
-               }
-       default:
-               break;
-       }
-
        switch (event) {
-       case SND_SOC_DAPM_PRE_PMU:
-               if (!priv->spk_ena && manual_ena) {
-                       regmap_write_async(arizona->regmap, 0x4f5, 0x25a);
-                       priv->spk_ena_pending = true;
-               }
-               break;
        case SND_SOC_DAPM_POST_PMU:
                val = snd_soc_read(codec, ARIZONA_INTERRUPT_RAW_STATUS_3);
                if (val & ARIZONA_SPK_OVERHEAT_STS) {
@@ -120,33 +99,12 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w,
                regmap_update_bits_async(arizona->regmap,
                                         ARIZONA_OUTPUT_ENABLES_1,
                                         1 << w->shift, 1 << w->shift);
-
-               if (priv->spk_ena_pending) {
-                       msleep(75);
-                       regmap_write_async(arizona->regmap, 0x4f5, 0xda);
-                       priv->spk_ena_pending = false;
-                       priv->spk_ena++;
-               }
                break;
        case SND_SOC_DAPM_PRE_PMD:
-               if (manual_ena) {
-                       priv->spk_ena--;
-                       if (!priv->spk_ena)
-                               regmap_write_async(arizona->regmap,
-                                                  0x4f5, 0x25a);
-               }
-
                regmap_update_bits_async(arizona->regmap,
                                         ARIZONA_OUTPUT_ENABLES_1,
                                         1 << w->shift, 0);
                break;
-       case SND_SOC_DAPM_POST_PMD:
-               if (manual_ena) {
-                       if (!priv->spk_ena)
-                               regmap_write_async(arizona->regmap,
-                                                  0x4f5, 0x0da);
-               }
-               break;
        default:
                break;
        }
@@ -324,6 +282,17 @@ int arizona_init_gpio(struct snd_soc_codec *codec)
 }
 EXPORT_SYMBOL_GPL(arizona_init_gpio);
 
+int arizona_init_notifiers(struct snd_soc_codec *codec)
+{
+       struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct arizona *arizona = priv->arizona;
+
+       BLOCKING_INIT_NOTIFIER_HEAD(&arizona->notifier);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_notifiers);
+
 const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
        "None",
        "Tone Generator 1",
@@ -619,7 +588,7 @@ const struct soc_enum arizona_asrc_rate1 =
                              arizona_rate_text, arizona_rate_val);
 EXPORT_SYMBOL_GPL(arizona_asrc_rate1);
 
-static const char *arizona_vol_ramp_text[] = {
+static const char * const arizona_vol_ramp_text[] = {
        "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB",
        "15ms/6dB", "30ms/6dB",
 };
@@ -648,7 +617,7 @@ SOC_ENUM_SINGLE_DECL(arizona_out_vi_ramp,
                     arizona_vol_ramp_text);
 EXPORT_SYMBOL_GPL(arizona_out_vi_ramp);
 
-static const char *arizona_lhpf_mode_text[] = {
+static const char * const arizona_lhpf_mode_text[] = {
        "Low-pass", "High-pass"
 };
 
@@ -676,7 +645,7 @@ SOC_ENUM_SINGLE_DECL(arizona_lhpf4_mode,
                     arizona_lhpf_mode_text);
 EXPORT_SYMBOL_GPL(arizona_lhpf4_mode);
 
-static const char *arizona_ng_hold_text[] = {
+static const char * const arizona_ng_hold_text[] = {
        "30ms", "120ms", "250ms", "500ms",
 };
 
@@ -810,6 +779,14 @@ const struct soc_enum arizona_output_anc_src[] = {
 };
 EXPORT_SYMBOL_GPL(arizona_output_anc_src);
 
+const struct snd_kcontrol_new arizona_voice_trigger_switch[] = {
+       SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+       SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 1, 1, 0),
+       SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 2, 1, 0),
+       SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 3, 1, 0),
+};
+EXPORT_SYMBOL_GPL(arizona_voice_trigger_switch);
+
 static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
 {
        struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
@@ -2573,6 +2550,30 @@ int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_GPL(arizona_lhpf_coeff_put);
 
+int arizona_register_notifier(struct snd_soc_codec *codec,
+                             struct notifier_block *nb,
+                             int (*notify)(struct notifier_block *nb,
+                                           unsigned long action, void *data))
+{
+       struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct arizona *arizona = priv->arizona;
+
+       nb->notifier_call = notify;
+
+       return blocking_notifier_chain_register(&arizona->notifier, nb);
+}
+EXPORT_SYMBOL_GPL(arizona_register_notifier);
+
+int arizona_unregister_notifier(struct snd_soc_codec *codec,
+                               struct notifier_block *nb)
+{
+       struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct arizona *arizona = priv->arizona;
+
+       return blocking_notifier_chain_unregister(&arizona->notifier, nb);
+}
+EXPORT_SYMBOL_GPL(arizona_unregister_notifier);
+
 MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support");
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 MODULE_LICENSE("GPL");
index ce0531b8c6329ed4af58246f5eaa115193090b66..69da1ef3a17c6a8f497562f973ac03324ae68372 100644 (file)
@@ -63,6 +63,9 @@
 #define ARIZONA_DVFS_SR1_RQ    0x001
 #define ARIZONA_DVFS_ADSP1_RQ  0x100
 
+/* Notifier events */
+#define ARIZONA_NOTIFY_VOICE_TRIGGER   0x1
+
 struct arizona;
 struct wm_adsp;
 
@@ -87,14 +90,15 @@ struct arizona_priv {
        unsigned int out_down_pending;
        unsigned int out_down_delay;
 
-       unsigned int spk_ena:2;
-       unsigned int spk_ena_pending:1;
-
        unsigned int dvfs_reqs;
        struct mutex dvfs_lock;
        bool dvfs_cached;
 };
 
+struct arizona_voice_trigger_info {
+       int core;
+};
+
 #define ARIZONA_NUM_MIXER_INPUTS 104
 
 extern const unsigned int arizona_mixer_tlv[];
@@ -248,6 +252,8 @@ extern const struct soc_enum arizona_anc_input_src[];
 extern const struct soc_enum arizona_anc_ng_enum;
 extern const struct soc_enum arizona_output_anc_src[];
 
+extern const struct snd_kcontrol_new arizona_voice_trigger_switch[];
+
 extern int arizona_in_ev(struct snd_soc_dapm_widget *w,
                         struct snd_kcontrol *kcontrol,
                         int event);
@@ -306,6 +312,7 @@ extern int arizona_set_fll(struct arizona_fll *fll, int source,
 extern int arizona_init_spk(struct snd_soc_codec *codec);
 extern int arizona_init_gpio(struct snd_soc_codec *codec);
 extern int arizona_init_mono(struct snd_soc_codec *codec);
+extern int arizona_init_notifiers(struct snd_soc_codec *codec);
 
 extern int arizona_free_spk(struct snd_soc_codec *codec);
 
@@ -317,4 +324,13 @@ int arizona_set_output_mode(struct snd_soc_codec *codec, int output,
 extern bool arizona_input_analog(struct snd_soc_codec *codec, int shift);
 
 extern const char *arizona_sample_rate_val_to_name(unsigned int rate_val);
+
+extern int arizona_register_notifier(struct snd_soc_codec *codec,
+                                    struct notifier_block *nb,
+                                    int (*notify)(struct notifier_block *nb,
+                                                  unsigned long action,
+                                                  void *data));
+extern int arizona_unregister_notifier(struct snd_soc_codec *codec,
+                                      struct notifier_block *nb);
+
 #endif
index b084ad113e967824a6809dd3e3394f18990e3a72..2a8d0ee141d414228bb8cc1d67994416b0a7cd25 100644 (file)
@@ -25,22 +25,41 @@ static const struct snd_soc_dapm_route bt_sco_routes[] = {
        { "TX", NULL, "Playback" },
 };
 
-static struct snd_soc_dai_driver bt_sco_dai = {
-       .name = "bt-sco-pcm",
-       .playback = {
-               .stream_name = "Playback",
-               .channels_min = 1,
-               .channels_max = 1,
-               .rates = SNDRV_PCM_RATE_8000,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,
-       },
-       .capture = {
-                .stream_name = "Capture",
-               .channels_min = 1,
-               .channels_max = 1,
-               .rates = SNDRV_PCM_RATE_8000,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+static struct snd_soc_dai_driver bt_sco_dai[] = {
+       {
+               .name = "bt-sco-pcm",
+               .playback = {
+                       .stream_name = "Playback",
+                       .channels_min = 1,
+                       .channels_max = 1,
+                       .rates = SNDRV_PCM_RATE_8000,
+                       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+               },
+               .capture = {
+                        .stream_name = "Capture",
+                       .channels_min = 1,
+                       .channels_max = 1,
+                       .rates = SNDRV_PCM_RATE_8000,
+                       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+               },
        },
+       {
+               .name = "bt-sco-pcm-wb",
+               .playback = {
+                       .stream_name = "Playback",
+                       .channels_min = 1,
+                       .channels_max = 1,
+                       .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+                       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+               },
+               .capture = {
+                        .stream_name = "Capture",
+                       .channels_min = 1,
+                       .channels_max = 1,
+                       .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+                       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+               },
+       }
 };
 
 static struct snd_soc_codec_driver soc_codec_dev_bt_sco = {
@@ -53,7 +72,7 @@ static struct snd_soc_codec_driver soc_codec_dev_bt_sco = {
 static int bt_sco_probe(struct platform_device *pdev)
 {
        return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_bt_sco,
-                       &bt_sco_dai, 1);
+                                     bt_sco_dai, ARRAY_SIZE(bt_sco_dai));
 }
 
 static int bt_sco_remove(struct platform_device *pdev)
@@ -77,6 +96,7 @@ MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids);
 #if defined(CONFIG_OF)
 static const struct of_device_id bt_sco_codec_of_match[] = {
        { .compatible = "delta,dfbmcs320", },
+       { .compatible = "linux,bt-sco", },
        {},
 };
 MODULE_DEVICE_TABLE(of, bt_sco_codec_of_match);
diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c
new file mode 100644 (file)
index 0000000..6f9c1ad
--- /dev/null
@@ -0,0 +1,1303 @@
+/*
+ * cs35l33.c -- CS35L33 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <paul.handrigan@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <sound/cs35l33.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/machine.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#include "cs35l33.h"
+
+#define CS35L33_BOOT_DELAY     50
+
+struct cs35l33_private {
+       struct snd_soc_codec *codec;
+       struct cs35l33_pdata pdata;
+       struct regmap *regmap;
+       struct gpio_desc *reset_gpio;
+       bool amp_cal;
+       int mclk_int;
+       struct regulator_bulk_data core_supplies[2];
+       int num_core_supplies;
+       bool is_tdm_mode;
+       bool enable_soft_ramp;
+};
+
+static const struct reg_default cs35l33_reg[] = {
+       {CS35L33_PWRCTL1, 0x85},
+       {CS35L33_PWRCTL2, 0xFE},
+       {CS35L33_CLK_CTL, 0x0C},
+       {CS35L33_BST_PEAK_CTL, 0x90},
+       {CS35L33_PROTECT_CTL, 0x55},
+       {CS35L33_BST_CTL1, 0x00},
+       {CS35L33_BST_CTL2, 0x01},
+       {CS35L33_ADSP_CTL, 0x00},
+       {CS35L33_ADC_CTL, 0xC8},
+       {CS35L33_DAC_CTL, 0x14},
+       {CS35L33_DIG_VOL_CTL, 0x00},
+       {CS35L33_CLASSD_CTL, 0x04},
+       {CS35L33_AMP_CTL, 0x90},
+       {CS35L33_INT_MASK_1, 0xFF},
+       {CS35L33_INT_MASK_2, 0xFF},
+       {CS35L33_DIAG_LOCK, 0x00},
+       {CS35L33_DIAG_CTRL_1, 0x40},
+       {CS35L33_DIAG_CTRL_2, 0x00},
+       {CS35L33_HG_MEMLDO_CTL, 0x62},
+       {CS35L33_HG_REL_RATE, 0x03},
+       {CS35L33_LDO_DEL, 0x12},
+       {CS35L33_HG_HEAD, 0x0A},
+       {CS35L33_HG_EN, 0x05},
+       {CS35L33_TX_VMON, 0x00},
+       {CS35L33_TX_IMON, 0x03},
+       {CS35L33_TX_VPMON, 0x02},
+       {CS35L33_TX_VBSTMON, 0x05},
+       {CS35L33_TX_FLAG, 0x06},
+       {CS35L33_TX_EN1, 0x00},
+       {CS35L33_TX_EN2, 0x00},
+       {CS35L33_TX_EN3, 0x00},
+       {CS35L33_TX_EN4, 0x00},
+       {CS35L33_RX_AUD, 0x40},
+       {CS35L33_RX_SPLY, 0x03},
+       {CS35L33_RX_ALIVE, 0x04},
+       {CS35L33_BST_CTL4, 0x63},
+};
+
+static const struct reg_sequence cs35l33_patch[] = {
+       { 0x00,  0x99, 0 },
+       { 0x59,  0x02, 0 },
+       { 0x52,  0x30, 0 },
+       { 0x39,  0x45, 0 },
+       { 0x57,  0x30, 0 },
+       { 0x2C,  0x68, 0 },
+       { 0x00,  0x00, 0 },
+};
+
+static bool cs35l33_volatile_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CS35L33_DEVID_AB:
+       case CS35L33_DEVID_CD:
+       case CS35L33_DEVID_E:
+       case CS35L33_REV_ID:
+       case CS35L33_INT_STATUS_1:
+       case CS35L33_INT_STATUS_2:
+       case CS35L33_HG_STATUS:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool cs35l33_writeable_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       /* these are read only registers */
+       case CS35L33_DEVID_AB:
+       case CS35L33_DEVID_CD:
+       case CS35L33_DEVID_E:
+       case CS35L33_REV_ID:
+       case CS35L33_INT_STATUS_1:
+       case CS35L33_INT_STATUS_2:
+       case CS35L33_HG_STATUS:
+               return false;
+       default:
+               return true;
+       }
+}
+
+static bool cs35l33_readable_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CS35L33_DEVID_AB:
+       case CS35L33_DEVID_CD:
+       case CS35L33_DEVID_E:
+       case CS35L33_REV_ID:
+       case CS35L33_PWRCTL1:
+       case CS35L33_PWRCTL2:
+       case CS35L33_CLK_CTL:
+       case CS35L33_BST_PEAK_CTL:
+       case CS35L33_PROTECT_CTL:
+       case CS35L33_BST_CTL1:
+       case CS35L33_BST_CTL2:
+       case CS35L33_ADSP_CTL:
+       case CS35L33_ADC_CTL:
+       case CS35L33_DAC_CTL:
+       case CS35L33_DIG_VOL_CTL:
+       case CS35L33_CLASSD_CTL:
+       case CS35L33_AMP_CTL:
+       case CS35L33_INT_MASK_1:
+       case CS35L33_INT_MASK_2:
+       case CS35L33_INT_STATUS_1:
+       case CS35L33_INT_STATUS_2:
+       case CS35L33_DIAG_LOCK:
+       case CS35L33_DIAG_CTRL_1:
+       case CS35L33_DIAG_CTRL_2:
+       case CS35L33_HG_MEMLDO_CTL:
+       case CS35L33_HG_REL_RATE:
+       case CS35L33_LDO_DEL:
+       case CS35L33_HG_HEAD:
+       case CS35L33_HG_EN:
+       case CS35L33_TX_VMON:
+       case CS35L33_TX_IMON:
+       case CS35L33_TX_VPMON:
+       case CS35L33_TX_VBSTMON:
+       case CS35L33_TX_FLAG:
+       case CS35L33_TX_EN1:
+       case CS35L33_TX_EN2:
+       case CS35L33_TX_EN3:
+       case CS35L33_TX_EN4:
+       case CS35L33_RX_AUD:
+       case CS35L33_RX_SPLY:
+       case CS35L33_RX_ALIVE:
+       case CS35L33_BST_CTL4:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static DECLARE_TLV_DB_SCALE(classd_ctl_tlv, 900, 100, 0);
+static DECLARE_TLV_DB_SCALE(dac_tlv, -10200, 50, 0);
+
+static const struct snd_kcontrol_new cs35l33_snd_controls[] = {
+
+       SOC_SINGLE_TLV("SPK Amp Volume", CS35L33_AMP_CTL,
+                      4, 0x09, 0, classd_ctl_tlv),
+       SOC_SINGLE_SX_TLV("DAC Volume", CS35L33_DIG_VOL_CTL,
+                       0, 0x34, 0xE4, dac_tlv),
+};
+
+static int cs35l33_spkrdrv_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               if (!priv->amp_cal) {
+                       usleep_range(8000, 9000);
+                       priv->amp_cal = true;
+                       regmap_update_bits(priv->regmap, CS35L33_CLASSD_CTL,
+                                   CS35L33_AMP_CAL, 0);
+                       dev_dbg(codec->dev, "Amp calibration done\n");
+               }
+               dev_dbg(codec->dev, "Amp turned on\n");
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               dev_dbg(codec->dev, "Amp turned off\n");
+               break;
+       default:
+               dev_err(codec->dev, "Invalid event = 0x%x\n", event);
+               break;
+       }
+
+       return 0;
+}
+
+static int cs35l33_sdin_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+       unsigned int val;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               regmap_update_bits(priv->regmap, CS35L33_PWRCTL1,
+                                   CS35L33_PDN_BST, 0);
+               val = priv->is_tdm_mode ? 0 : CS35L33_PDN_TDM;
+               regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+                                   CS35L33_PDN_TDM, val);
+               dev_dbg(codec->dev, "BST turned on\n");
+               break;
+       case SND_SOC_DAPM_POST_PMU:
+               dev_dbg(codec->dev, "SDIN turned on\n");
+               if (!priv->amp_cal) {
+                       regmap_update_bits(priv->regmap, CS35L33_CLASSD_CTL,
+                                   CS35L33_AMP_CAL, CS35L33_AMP_CAL);
+                       dev_dbg(codec->dev, "Amp calibration started\n");
+                       usleep_range(10000, 11000);
+               }
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+                                   CS35L33_PDN_TDM, CS35L33_PDN_TDM);
+               usleep_range(4000, 4100);
+               regmap_update_bits(priv->regmap, CS35L33_PWRCTL1,
+                                   CS35L33_PDN_BST, CS35L33_PDN_BST);
+               dev_dbg(codec->dev, "BST and SDIN turned off\n");
+               break;
+       default:
+               dev_err(codec->dev, "Invalid event = 0x%x\n", event);
+
+       }
+
+       return 0;
+}
+
+static int cs35l33_sdout_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+       unsigned int mask = CS35L33_SDOUT_3ST_I2S | CS35L33_PDN_TDM;
+       unsigned int mask2 = CS35L33_SDOUT_3ST_TDM;
+       unsigned int val, val2;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               if (priv->is_tdm_mode) {
+                       /* set sdout_3st_i2s and reset pdn_tdm */
+                       val = CS35L33_SDOUT_3ST_I2S;
+                       /* reset sdout_3st_tdm */
+                       val2 = 0;
+               } else {
+                       /* reset sdout_3st_i2s and set pdn_tdm */
+                       val = CS35L33_PDN_TDM;
+                       /* set sdout_3st_tdm */
+                       val2 = CS35L33_SDOUT_3ST_TDM;
+               }
+               dev_dbg(codec->dev, "SDOUT turned on\n");
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               val = CS35L33_SDOUT_3ST_I2S | CS35L33_PDN_TDM;
+               val2 = CS35L33_SDOUT_3ST_TDM;
+               dev_dbg(codec->dev, "SDOUT turned off\n");
+               break;
+       default:
+               dev_err(codec->dev, "Invalid event = 0x%x\n", event);
+               return 0;
+       }
+
+       regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+               mask, val);
+       regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+               mask2, val2);
+
+       return 0;
+}
+
+static const struct snd_soc_dapm_widget cs35l33_dapm_widgets[] = {
+
+       SND_SOC_DAPM_OUTPUT("SPK"),
+       SND_SOC_DAPM_OUT_DRV_E("SPKDRV", CS35L33_PWRCTL1, 7, 1, NULL, 0,
+               cs35l33_spkrdrv_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_AIF_IN_E("SDIN", NULL, 0, CS35L33_PWRCTL2,
+               2, 1, cs35l33_sdin_event, SND_SOC_DAPM_PRE_PMU |
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_INPUT("MON"),
+
+       SND_SOC_DAPM_ADC("VMON", NULL,
+               CS35L33_PWRCTL2, CS35L33_PDN_VMON_SHIFT, 1),
+       SND_SOC_DAPM_ADC("IMON", NULL,
+               CS35L33_PWRCTL2, CS35L33_PDN_IMON_SHIFT, 1),
+       SND_SOC_DAPM_ADC("VPMON", NULL,
+               CS35L33_PWRCTL2, CS35L33_PDN_VPMON_SHIFT, 1),
+       SND_SOC_DAPM_ADC("VBSTMON", NULL,
+               CS35L33_PWRCTL2, CS35L33_PDN_VBSTMON_SHIFT, 1),
+
+       SND_SOC_DAPM_AIF_OUT_E("SDOUT", NULL, 0, SND_SOC_NOPM, 0, 0,
+               cs35l33_sdout_event, SND_SOC_DAPM_PRE_PMU |
+               SND_SOC_DAPM_PRE_PMD),
+};
+
+static const struct snd_soc_dapm_route cs35l33_audio_map[] = {
+       {"SDIN", NULL, "CS35L33 Playback"},
+       {"SPKDRV", NULL, "SDIN"},
+       {"SPK", NULL, "SPKDRV"},
+
+       {"VMON", NULL, "MON"},
+       {"IMON", NULL, "MON"},
+
+       {"SDOUT", NULL, "VMON"},
+       {"SDOUT", NULL, "IMON"},
+       {"CS35L33 Capture", NULL, "SDOUT"},
+};
+
+static const struct snd_soc_dapm_route cs35l33_vphg_auto_route[] = {
+       {"SPKDRV", NULL, "VPMON"},
+       {"VPMON", NULL, "CS35L33 Playback"},
+};
+
+static const struct snd_soc_dapm_route cs35l33_vp_vbst_mon_route[] = {
+       {"SDOUT", NULL, "VPMON"},
+       {"VPMON", NULL, "MON"},
+       {"SDOUT", NULL, "VBSTMON"},
+       {"VBSTMON", NULL, "MON"},
+};
+
+static int cs35l33_set_bias_level(struct snd_soc_codec *codec,
+                                 enum snd_soc_bias_level level)
+{
+       unsigned int val;
+       struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+       case SND_SOC_BIAS_PREPARE:
+               regmap_update_bits(priv->regmap, CS35L33_PWRCTL1,
+                                   CS35L33_PDN_ALL, 0);
+               regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+                                   CS35L33_MCLKDIS, 0);
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               regmap_update_bits(priv->regmap, CS35L33_PWRCTL1,
+                                   CS35L33_PDN_ALL, CS35L33_PDN_ALL);
+               regmap_read(priv->regmap, CS35L33_INT_STATUS_2, &val);
+               usleep_range(1000, 1100);
+               if (val & CS35L33_PDN_DONE)
+                       regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+                                           CS35L33_MCLKDIS, CS35L33_MCLKDIS);
+               break;
+       case SND_SOC_BIAS_OFF:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+struct cs35l33_mclk_div {
+       int mclk;
+       int srate;
+       u8 adsp_rate;
+       u8 int_fs_ratio;
+};
+
+static const struct cs35l33_mclk_div cs35l33_mclk_coeffs[] = {
+       /* MCLK, Sample Rate, adsp_rate, int_fs_ratio */
+       {5644800, 11025, 0x4, CS35L33_INT_FS_RATE},
+       {5644800, 22050, 0x8, CS35L33_INT_FS_RATE},
+       {5644800, 44100, 0xC, CS35L33_INT_FS_RATE},
+
+       {6000000,  8000, 0x1, 0},
+       {6000000, 11025, 0x2, 0},
+       {6000000, 11029, 0x3, 0},
+       {6000000, 12000, 0x4, 0},
+       {6000000, 16000, 0x5, 0},
+       {6000000, 22050, 0x6, 0},
+       {6000000, 22059, 0x7, 0},
+       {6000000, 24000, 0x8, 0},
+       {6000000, 32000, 0x9, 0},
+       {6000000, 44100, 0xA, 0},
+       {6000000, 44118, 0xB, 0},
+       {6000000, 48000, 0xC, 0},
+
+       {6144000,  8000, 0x1, CS35L33_INT_FS_RATE},
+       {6144000, 12000, 0x4, CS35L33_INT_FS_RATE},
+       {6144000, 16000, 0x5, CS35L33_INT_FS_RATE},
+       {6144000, 24000, 0x8, CS35L33_INT_FS_RATE},
+       {6144000, 32000, 0x9, CS35L33_INT_FS_RATE},
+       {6144000, 48000, 0xC, CS35L33_INT_FS_RATE},
+};
+
+static int cs35l33_get_mclk_coeff(int mclk, int srate)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(cs35l33_mclk_coeffs); i++) {
+               if (cs35l33_mclk_coeffs[i].mclk == mclk &&
+                       cs35l33_mclk_coeffs[i].srate == srate)
+                       return i;
+       }
+       return -EINVAL;
+}
+
+static int cs35l33_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               regmap_update_bits(priv->regmap, CS35L33_ADSP_CTL,
+                       CS35L33_MS_MASK, CS35L33_MS_MASK);
+               dev_dbg(codec->dev, "Audio port in master mode\n");
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               regmap_update_bits(priv->regmap, CS35L33_ADSP_CTL,
+                       CS35L33_MS_MASK, 0);
+               dev_dbg(codec->dev, "Audio port in slave mode\n");
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_DSP_A:
+               /*
+                * tdm mode in cs35l33 resembles dsp-a mode very
+                * closely, it is dsp-a with fsync shifted left by half bclk
+                */
+               priv->is_tdm_mode = true;
+               dev_dbg(codec->dev, "Audio port in TDM mode\n");
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               priv->is_tdm_mode = false;
+               dev_dbg(codec->dev, "Audio port in I2S mode\n");
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int cs35l33_pcm_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+       int sample_size = params_width(params);
+       int coeff = cs35l33_get_mclk_coeff(priv->mclk_int, params_rate(params));
+
+       if (coeff < 0)
+               return coeff;
+
+       regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+               CS35L33_ADSP_FS | CS35L33_INT_FS_RATE,
+               cs35l33_mclk_coeffs[coeff].int_fs_ratio
+               | cs35l33_mclk_coeffs[coeff].adsp_rate);
+
+       if (priv->is_tdm_mode) {
+               sample_size = (sample_size / 8) - 1;
+               if (sample_size > 2)
+                       sample_size = 2;
+               regmap_update_bits(priv->regmap, CS35L33_RX_AUD,
+                       CS35L33_AUDIN_RX_DEPTH,
+                       sample_size << CS35L33_AUDIN_RX_DEPTH_SHIFT);
+       }
+
+       dev_dbg(codec->dev, "sample rate=%d, bits per sample=%d\n",
+               params_rate(params), params_width(params));
+
+       return 0;
+}
+
+static const unsigned int cs35l33_src_rates[] = {
+       8000, 11025, 11029, 12000, 16000, 22050,
+       22059, 24000, 32000, 44100, 44118, 48000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l33_constraints = {
+       .count  = ARRAY_SIZE(cs35l33_src_rates),
+       .list   = cs35l33_src_rates,
+};
+
+static int cs35l33_pcm_startup(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
+{
+       snd_pcm_hw_constraint_list(substream->runtime, 0,
+                                       SNDRV_PCM_HW_PARAM_RATE,
+                                       &cs35l33_constraints);
+       return 0;
+}
+
+static int cs35l33_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+
+       if (tristate) {
+               regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+                       CS35L33_SDOUT_3ST_I2S, CS35L33_SDOUT_3ST_I2S);
+               regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+                       CS35L33_SDOUT_3ST_TDM, CS35L33_SDOUT_3ST_TDM);
+       } else {
+               regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+                       CS35L33_SDOUT_3ST_I2S, 0);
+               regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+                       CS35L33_SDOUT_3ST_TDM, 0);
+       }
+
+       return 0;
+}
+
+static int cs35l33_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+                               unsigned int rx_mask, int slots, int slot_width)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+       struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+       unsigned int reg, bit_pos, i;
+       int slot, slot_num;
+
+       if (slot_width != 8)
+               return -EINVAL;
+
+       /* scan rx_mask for aud slot */
+       slot = ffs(rx_mask) - 1;
+       if (slot >= 0) {
+               regmap_update_bits(priv->regmap, CS35L33_RX_AUD,
+                       CS35L33_X_LOC, slot);
+               dev_dbg(codec->dev, "Audio starts from slots %d", slot);
+       }
+
+       /*
+        * scan tx_mask: vmon(2 slots); imon (2 slots);
+        * vpmon (1 slot) vbstmon (1 slot)
+        */
+       slot = ffs(tx_mask) - 1;
+       slot_num = 0;
+
+       for (i = 0; i < 2 ; i++) {
+               /* disable vpmon/vbstmon: enable later if set in tx_mask */
+               regmap_update_bits(priv->regmap, CS35L33_TX_VPMON + i,
+                       CS35L33_X_STATE | CS35L33_X_LOC, CS35L33_X_STATE
+                       | CS35L33_X_LOC);
+       }
+
+       /* disconnect {vp,vbst}_mon routes: eanble later if set in tx_mask*/
+       snd_soc_dapm_del_routes(dapm, cs35l33_vp_vbst_mon_route,
+               ARRAY_SIZE(cs35l33_vp_vbst_mon_route));
+
+       while (slot >= 0) {
+               /* configure VMON_TX_LOC */
+               if (slot_num == 0) {
+                       regmap_update_bits(priv->regmap, CS35L33_TX_VMON,
+                               CS35L33_X_STATE | CS35L33_X_LOC, slot);
+                       dev_dbg(codec->dev, "VMON enabled in slots %d-%d",
+                               slot, slot + 1);
+               }
+
+               /* configure IMON_TX_LOC */
+               if (slot_num == 3) {
+                       regmap_update_bits(priv->regmap, CS35L33_TX_IMON,
+                               CS35L33_X_STATE | CS35L33_X_LOC, slot);
+                       dev_dbg(codec->dev, "IMON enabled in slots %d-%d",
+                               slot, slot + 1);
+               }
+
+               /* configure VPMON_TX_LOC */
+               if (slot_num == 4) {
+                       regmap_update_bits(priv->regmap, CS35L33_TX_VPMON,
+                               CS35L33_X_STATE | CS35L33_X_LOC, slot);
+                       snd_soc_dapm_add_routes(dapm,
+                               &cs35l33_vp_vbst_mon_route[0], 2);
+                       dev_dbg(codec->dev, "VPMON enabled in slots %d", slot);
+               }
+
+               /* configure VBSTMON_TX_LOC */
+               if (slot_num == 5) {
+                       regmap_update_bits(priv->regmap, CS35L33_TX_VBSTMON,
+                               CS35L33_X_STATE | CS35L33_X_LOC, slot);
+                       snd_soc_dapm_add_routes(dapm,
+                               &cs35l33_vp_vbst_mon_route[2], 2);
+                       dev_dbg(codec->dev,
+                               "VBSTMON enabled in slots %d", slot);
+               }
+
+               /* Enable the relevant tx slot */
+               reg = CS35L33_TX_EN4 - (slot/8);
+               bit_pos = slot - ((slot / 8) * (8));
+               regmap_update_bits(priv->regmap, reg,
+                       1 << bit_pos, 1 << bit_pos);
+
+               tx_mask &= ~(1 << slot);
+               slot = ffs(tx_mask) - 1;
+               slot_num++;
+       }
+
+       return 0;
+}
+
+static int cs35l33_codec_set_sysclk(struct snd_soc_codec *codec,
+               int clk_id, int source, unsigned int freq, int dir)
+{
+       struct cs35l33_private *cs35l33 = snd_soc_codec_get_drvdata(codec);
+
+       switch (freq) {
+       case CS35L33_MCLK_5644:
+       case CS35L33_MCLK_6:
+       case CS35L33_MCLK_6144:
+               regmap_update_bits(cs35l33->regmap, CS35L33_CLK_CTL,
+                       CS35L33_MCLKDIV2, 0);
+               cs35l33->mclk_int = freq;
+               break;
+       case CS35L33_MCLK_11289:
+       case CS35L33_MCLK_12:
+       case CS35L33_MCLK_12288:
+               regmap_update_bits(cs35l33->regmap, CS35L33_CLK_CTL,
+                       CS35L33_MCLKDIV2, CS35L33_MCLKDIV2);
+               cs35l33->mclk_int = freq/2;
+               break;
+       default:
+               cs35l33->mclk_int = 0;
+               return -EINVAL;
+       }
+
+       dev_dbg(codec->dev, "external mclk freq=%d, internal mclk freq=%d\n",
+               freq, cs35l33->mclk_int);
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l33_ops = {
+       .startup = cs35l33_pcm_startup,
+       .set_tristate = cs35l33_set_tristate,
+       .set_fmt = cs35l33_set_dai_fmt,
+       .hw_params = cs35l33_pcm_hw_params,
+       .set_tdm_slot = cs35l33_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver cs35l33_dai = {
+               .name = "cs35l33-dai",
+               .id = 0,
+               .playback = {
+                       .stream_name = "CS35L33 Playback",
+                       .channels_min = 1,
+                       .channels_max = 1,
+                       .rates = CS35L33_RATES,
+                       .formats = CS35L33_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "CS35L33 Capture",
+                       .channels_min = 2,
+                       .channels_max = 2,
+                       .rates = CS35L33_RATES,
+                       .formats = CS35L33_FORMATS,
+               },
+               .ops = &cs35l33_ops,
+               .symmetric_rates = 1,
+};
+
+static int cs35l33_set_hg_data(struct snd_soc_codec *codec,
+                              struct cs35l33_pdata *pdata)
+{
+       struct cs35l33_hg *hg_config = &pdata->hg_config;
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+       struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+
+       if (hg_config->enable_hg_algo) {
+               regmap_update_bits(priv->regmap, CS35L33_HG_MEMLDO_CTL,
+                       CS35L33_MEM_DEPTH_MASK,
+                       hg_config->mem_depth << CS35L33_MEM_DEPTH_SHIFT);
+               regmap_write(priv->regmap, CS35L33_HG_REL_RATE,
+                       hg_config->release_rate);
+               regmap_update_bits(priv->regmap, CS35L33_HG_HEAD,
+                       CS35L33_HD_RM_MASK,
+                       hg_config->hd_rm << CS35L33_HD_RM_SHIFT);
+               regmap_update_bits(priv->regmap, CS35L33_HG_MEMLDO_CTL,
+                       CS35L33_LDO_THLD_MASK,
+                       hg_config->ldo_thld << CS35L33_LDO_THLD_SHIFT);
+               regmap_update_bits(priv->regmap, CS35L33_HG_MEMLDO_CTL,
+                       CS35L33_LDO_DISABLE_MASK,
+                       hg_config->ldo_path_disable <<
+                               CS35L33_LDO_DISABLE_SHIFT);
+               regmap_update_bits(priv->regmap, CS35L33_LDO_DEL,
+                       CS35L33_LDO_ENTRY_DELAY_MASK,
+                       hg_config->ldo_entry_delay <<
+                               CS35L33_LDO_ENTRY_DELAY_SHIFT);
+               if (hg_config->vp_hg_auto) {
+                       regmap_update_bits(priv->regmap, CS35L33_HG_EN,
+                               CS35L33_VP_HG_AUTO_MASK,
+                               CS35L33_VP_HG_AUTO_MASK);
+                       snd_soc_dapm_add_routes(dapm, cs35l33_vphg_auto_route,
+                               ARRAY_SIZE(cs35l33_vphg_auto_route));
+               }
+               regmap_update_bits(priv->regmap, CS35L33_HG_EN,
+                       CS35L33_VP_HG_MASK,
+                       hg_config->vp_hg << CS35L33_VP_HG_SHIFT);
+               regmap_update_bits(priv->regmap, CS35L33_LDO_DEL,
+                       CS35L33_VP_HG_RATE_MASK,
+                       hg_config->vp_hg_rate << CS35L33_VP_HG_RATE_SHIFT);
+               regmap_update_bits(priv->regmap, CS35L33_LDO_DEL,
+                       CS35L33_VP_HG_VA_MASK,
+                       hg_config->vp_hg_va << CS35L33_VP_HG_VA_SHIFT);
+               regmap_update_bits(priv->regmap, CS35L33_HG_EN,
+                       CS35L33_CLASS_HG_EN_MASK, CS35L33_CLASS_HG_EN_MASK);
+       }
+       return 0;
+}
+
+static int cs35l33_set_bst_ipk(struct snd_soc_codec *codec, unsigned int bst)
+{
+       struct cs35l33_private *cs35l33 = snd_soc_codec_get_drvdata(codec);
+       int ret = 0, steps = 0;
+
+       /* Boost current in uA */
+       if (bst > 3600000 || bst < 1850000) {
+               dev_err(codec->dev, "Invalid boost current %d\n", bst);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       if (bst % 15625) {
+               dev_err(codec->dev, "Current not a multiple of 15625uA (%d)\n",
+                       bst);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       while (bst > 1850000) {
+               bst -= 15625;
+               steps++;
+       }
+
+       regmap_write(cs35l33->regmap, CS35L33_BST_PEAK_CTL,
+               steps+0x70);
+
+err:
+       return ret;
+}
+
+static int cs35l33_probe(struct snd_soc_codec *codec)
+{
+       struct cs35l33_private *cs35l33 = snd_soc_codec_get_drvdata(codec);
+
+       cs35l33->codec = codec;
+       pm_runtime_get_sync(codec->dev);
+
+       regmap_update_bits(cs35l33->regmap, CS35L33_PROTECT_CTL,
+               CS35L33_ALIVE_WD_DIS, 0x8);
+       regmap_update_bits(cs35l33->regmap, CS35L33_BST_CTL2,
+                               CS35L33_ALIVE_WD_DIS2,
+                               CS35L33_ALIVE_WD_DIS2);
+
+       /* Set Platform Data */
+       regmap_update_bits(cs35l33->regmap, CS35L33_BST_CTL1,
+               CS35L33_BST_CTL_MASK, cs35l33->pdata.boost_ctl);
+       regmap_update_bits(cs35l33->regmap, CS35L33_CLASSD_CTL,
+               CS35L33_AMP_DRV_SEL_MASK,
+               cs35l33->pdata.amp_drv_sel << CS35L33_AMP_DRV_SEL_SHIFT);
+
+       if (cs35l33->pdata.boost_ipk)
+               cs35l33_set_bst_ipk(codec, cs35l33->pdata.boost_ipk);
+
+       if (cs35l33->enable_soft_ramp) {
+               snd_soc_update_bits(codec, CS35L33_DAC_CTL,
+                       CS35L33_DIGSFT, CS35L33_DIGSFT);
+               snd_soc_update_bits(codec, CS35L33_DAC_CTL,
+                       CS35L33_DSR_RATE, cs35l33->pdata.ramp_rate);
+       } else {
+               snd_soc_update_bits(codec, CS35L33_DAC_CTL,
+                       CS35L33_DIGSFT, 0);
+       }
+
+       /* update IMON scaling rate if different from default of 0x8 */
+       if (cs35l33->pdata.imon_adc_scale != 0x8)
+               snd_soc_update_bits(codec, CS35L33_ADC_CTL,
+                       CS35L33_IMON_SCALE, cs35l33->pdata.imon_adc_scale);
+
+       cs35l33_set_hg_data(codec, &(cs35l33->pdata));
+
+       /*
+        * unmask important interrupts that causes the chip to enter
+        * speaker safe mode and hence deserves user attention
+        */
+       regmap_update_bits(cs35l33->regmap, CS35L33_INT_MASK_1,
+               CS35L33_M_OTE | CS35L33_M_OTW | CS35L33_M_AMP_SHORT |
+               CS35L33_M_CAL_ERR, 0);
+
+       pm_runtime_put_sync(codec->dev);
+
+       return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_cs35l33 = {
+       .probe = cs35l33_probe,
+
+       .set_bias_level = cs35l33_set_bias_level,
+       .set_sysclk = cs35l33_codec_set_sysclk,
+
+       .dapm_widgets = cs35l33_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(cs35l33_dapm_widgets),
+       .dapm_routes = cs35l33_audio_map,
+       .num_dapm_routes = ARRAY_SIZE(cs35l33_audio_map),
+       .controls = cs35l33_snd_controls,
+       .num_controls = ARRAY_SIZE(cs35l33_snd_controls),
+
+       .idle_bias_off = true,
+};
+
+static const struct regmap_config cs35l33_regmap = {
+       .reg_bits = 8,
+       .val_bits = 8,
+
+       .max_register = CS35L33_MAX_REGISTER,
+       .reg_defaults = cs35l33_reg,
+       .num_reg_defaults = ARRAY_SIZE(cs35l33_reg),
+       .volatile_reg = cs35l33_volatile_register,
+       .readable_reg = cs35l33_readable_register,
+       .writeable_reg = cs35l33_writeable_register,
+       .cache_type = REGCACHE_RBTREE,
+       .use_single_rw = true,
+};
+
+static int __maybe_unused cs35l33_runtime_resume(struct device *dev)
+{
+       struct cs35l33_private *cs35l33 = dev_get_drvdata(dev);
+       int ret;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (cs35l33->reset_gpio)
+               gpiod_set_value_cansleep(cs35l33->reset_gpio, 0);
+
+       ret = regulator_bulk_enable(cs35l33->num_core_supplies,
+               cs35l33->core_supplies);
+       if (ret != 0) {
+               dev_err(dev, "Failed to enable core supplies: %d\n", ret);
+               return ret;
+       }
+
+       regcache_cache_only(cs35l33->regmap, false);
+
+       if (cs35l33->reset_gpio)
+               gpiod_set_value_cansleep(cs35l33->reset_gpio, 1);
+
+       msleep(CS35L33_BOOT_DELAY);
+
+       ret = regcache_sync(cs35l33->regmap);
+       if (ret != 0) {
+               dev_err(dev, "Failed to restore register cache\n");
+               goto err;
+       }
+
+       return 0;
+
+err:
+       regcache_cache_only(cs35l33->regmap, true);
+       regulator_bulk_disable(cs35l33->num_core_supplies,
+               cs35l33->core_supplies);
+
+       return ret;
+}
+
+static int __maybe_unused cs35l33_runtime_suspend(struct device *dev)
+{
+       struct cs35l33_private *cs35l33 = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       /* redo the calibration in next power up */
+       cs35l33->amp_cal = false;
+
+       regcache_cache_only(cs35l33->regmap, true);
+       regcache_mark_dirty(cs35l33->regmap);
+       regulator_bulk_disable(cs35l33->num_core_supplies,
+               cs35l33->core_supplies);
+
+       return 0;
+}
+
+static const struct dev_pm_ops cs35l33_pm_ops = {
+       SET_RUNTIME_PM_OPS(cs35l33_runtime_suspend,
+                          cs35l33_runtime_resume,
+                          NULL)
+};
+
+static int cs35l33_get_hg_data(const struct device_node *np,
+                              struct cs35l33_pdata *pdata)
+{
+       struct device_node *hg;
+       struct cs35l33_hg *hg_config = &pdata->hg_config;
+       u32 val32;
+
+       hg = of_get_child_by_name(np, "cirrus,hg-algo");
+       hg_config->enable_hg_algo = hg ? true : false;
+
+       if (hg_config->enable_hg_algo) {
+               if (of_property_read_u32(hg, "cirrus,mem-depth", &val32) >= 0)
+                       hg_config->mem_depth = val32;
+               if (of_property_read_u32(hg, "cirrus,release-rate",
+                               &val32) >= 0)
+                       hg_config->release_rate = val32;
+               if (of_property_read_u32(hg, "cirrus,ldo-thld", &val32) >= 0)
+                       hg_config->ldo_thld = val32;
+               if (of_property_read_u32(hg, "cirrus,ldo-path-disable",
+                               &val32) >= 0)
+                       hg_config->ldo_path_disable = val32;
+               if (of_property_read_u32(hg, "cirrus,ldo-entry-delay",
+                               &val32) >= 0)
+                       hg_config->ldo_entry_delay = val32;
+
+               hg_config->vp_hg_auto = of_property_read_bool(hg,
+                       "cirrus,vp-hg-auto");
+
+               if (of_property_read_u32(hg, "cirrus,vp-hg", &val32) >= 0)
+                       hg_config->vp_hg = val32;
+               if (of_property_read_u32(hg, "cirrus,vp-hg-rate", &val32) >= 0)
+                       hg_config->vp_hg_rate = val32;
+               if (of_property_read_u32(hg, "cirrus,vp-hg-va", &val32) >= 0)
+                       hg_config->vp_hg_va = val32;
+       }
+
+       of_node_put(hg);
+
+       return 0;
+}
+
+static irqreturn_t cs35l33_irq_thread(int irq, void *data)
+{
+       struct cs35l33_private *cs35l33 = data;
+       struct snd_soc_codec *codec = cs35l33->codec;
+       unsigned int sticky_val1, sticky_val2, current_val, mask1, mask2;
+
+       regmap_read(cs35l33->regmap, CS35L33_INT_STATUS_2,
+               &sticky_val2);
+       regmap_read(cs35l33->regmap, CS35L33_INT_STATUS_1,
+               &sticky_val1);
+       regmap_read(cs35l33->regmap, CS35L33_INT_MASK_2, &mask2);
+       regmap_read(cs35l33->regmap, CS35L33_INT_MASK_1, &mask1);
+
+       /* Check to see if the unmasked bits are active,
+        *  if not then exit.
+        */
+       if (!(sticky_val1 & ~mask1) && !(sticky_val2 & ~mask2))
+               return IRQ_NONE;
+
+       regmap_read(cs35l33->regmap, CS35L33_INT_STATUS_1,
+               &current_val);
+
+       /* handle the interrupts */
+
+       if (sticky_val1 & CS35L33_AMP_SHORT) {
+               dev_crit(codec->dev, "Amp short error\n");
+               if (!(current_val & CS35L33_AMP_SHORT)) {
+                       dev_dbg(codec->dev,
+                               "Amp short error release\n");
+                       regmap_update_bits(cs35l33->regmap,
+                               CS35L33_AMP_CTL,
+                               CS35L33_AMP_SHORT_RLS, 0);
+                       regmap_update_bits(cs35l33->regmap,
+                               CS35L33_AMP_CTL,
+                               CS35L33_AMP_SHORT_RLS,
+                               CS35L33_AMP_SHORT_RLS);
+                       regmap_update_bits(cs35l33->regmap,
+                               CS35L33_AMP_CTL, CS35L33_AMP_SHORT_RLS,
+                               0);
+               }
+       }
+
+       if (sticky_val1 & CS35L33_CAL_ERR) {
+               dev_err(codec->dev, "Cal error\n");
+
+               /* redo the calibration in next power up */
+               cs35l33->amp_cal = false;
+
+               if (!(current_val & CS35L33_CAL_ERR)) {
+                       dev_dbg(codec->dev, "Cal error release\n");
+                       regmap_update_bits(cs35l33->regmap,
+                               CS35L33_AMP_CTL, CS35L33_CAL_ERR_RLS,
+                               0);
+                       regmap_update_bits(cs35l33->regmap,
+                               CS35L33_AMP_CTL, CS35L33_CAL_ERR_RLS,
+                               CS35L33_CAL_ERR_RLS);
+                       regmap_update_bits(cs35l33->regmap,
+                               CS35L33_AMP_CTL, CS35L33_CAL_ERR_RLS,
+                               0);
+               }
+       }
+
+       if (sticky_val1 & CS35L33_OTE) {
+               dev_crit(codec->dev, "Over temperature error\n");
+               if (!(current_val & CS35L33_OTE)) {
+                       dev_dbg(codec->dev,
+                               "Over temperature error release\n");
+                       regmap_update_bits(cs35l33->regmap,
+                               CS35L33_AMP_CTL, CS35L33_OTE_RLS, 0);
+                       regmap_update_bits(cs35l33->regmap,
+                               CS35L33_AMP_CTL, CS35L33_OTE_RLS,
+                               CS35L33_OTE_RLS);
+                       regmap_update_bits(cs35l33->regmap,
+                               CS35L33_AMP_CTL, CS35L33_OTE_RLS, 0);
+               }
+       }
+
+       if (sticky_val1 & CS35L33_OTW) {
+               dev_err(codec->dev, "Over temperature warning\n");
+               if (!(current_val & CS35L33_OTW)) {
+                       dev_dbg(codec->dev,
+                               "Over temperature warning release\n");
+                       regmap_update_bits(cs35l33->regmap,
+                               CS35L33_AMP_CTL, CS35L33_OTW_RLS, 0);
+                       regmap_update_bits(cs35l33->regmap,
+                               CS35L33_AMP_CTL, CS35L33_OTW_RLS,
+                               CS35L33_OTW_RLS);
+                       regmap_update_bits(cs35l33->regmap,
+                               CS35L33_AMP_CTL, CS35L33_OTW_RLS, 0);
+               }
+       }
+       if (CS35L33_ALIVE_ERR & sticky_val1)
+               dev_err(codec->dev, "ERROR: ADSPCLK Interrupt\n");
+
+       if (CS35L33_MCLK_ERR & sticky_val1)
+               dev_err(codec->dev, "ERROR: MCLK Interrupt\n");
+
+       if (CS35L33_VMON_OVFL & sticky_val2)
+               dev_err(codec->dev,
+                       "ERROR: VMON Overflow Interrupt\n");
+
+       if (CS35L33_IMON_OVFL & sticky_val2)
+               dev_err(codec->dev,
+                       "ERROR: IMON Overflow Interrupt\n");
+
+       if (CS35L33_VPMON_OVFL & sticky_val2)
+               dev_err(codec->dev,
+                       "ERROR: VPMON Overflow Interrupt\n");
+
+       return IRQ_HANDLED;
+}
+
+static const char * const cs35l33_core_supplies[] = {
+       "VA",
+       "VP",
+};
+
+static int cs35l33_of_get_pdata(struct device *dev,
+                               struct cs35l33_private *cs35l33)
+{
+       struct device_node *np = dev->of_node;
+       struct cs35l33_pdata *pdata = &cs35l33->pdata;
+       u32 val32;
+
+       if (!np)
+               return 0;
+
+       if (of_property_read_u32(np, "cirrus,boost-ctl", &val32) >= 0) {
+               pdata->boost_ctl = val32;
+               pdata->amp_drv_sel = 1;
+       }
+
+       if (of_property_read_u32(np, "cirrus,ramp-rate", &val32) >= 0) {
+               pdata->ramp_rate = val32;
+               cs35l33->enable_soft_ramp = true;
+       }
+
+       if (of_property_read_u32(np, "cirrus,boost-ipk", &val32) >= 0)
+               pdata->boost_ipk = val32;
+
+       if (of_property_read_u32(np, "cirrus,imon-adc-scale", &val32) >= 0) {
+               if ((val32 == 0x0) || (val32 == 0x7) || (val32 == 0x6))
+                       pdata->imon_adc_scale = val32;
+               else
+                       /* use default value */
+                       pdata->imon_adc_scale = 0x8;
+       } else {
+               /* use default value */
+               pdata->imon_adc_scale = 0x8;
+       }
+
+       cs35l33_get_hg_data(np, pdata);
+
+       return 0;
+}
+
+static int cs35l33_i2c_probe(struct i2c_client *i2c_client,
+                                      const struct i2c_device_id *id)
+{
+       struct cs35l33_private *cs35l33;
+       struct cs35l33_pdata *pdata = dev_get_platdata(&i2c_client->dev);
+       int ret, devid, i;
+       unsigned int reg;
+
+       cs35l33 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs35l33_private),
+                              GFP_KERNEL);
+       if (!cs35l33)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c_client, cs35l33);
+       cs35l33->regmap = devm_regmap_init_i2c(i2c_client, &cs35l33_regmap);
+       if (IS_ERR(cs35l33->regmap)) {
+               ret = PTR_ERR(cs35l33->regmap);
+               dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+               return ret;
+       }
+
+       regcache_cache_only(cs35l33->regmap, true);
+
+       for (i = 0; i < ARRAY_SIZE(cs35l33_core_supplies); i++)
+               cs35l33->core_supplies[i].supply
+                       = cs35l33_core_supplies[i];
+       cs35l33->num_core_supplies = ARRAY_SIZE(cs35l33_core_supplies);
+
+       ret = devm_regulator_bulk_get(&i2c_client->dev,
+                       cs35l33->num_core_supplies,
+                       cs35l33->core_supplies);
+       if (ret != 0) {
+               dev_err(&i2c_client->dev,
+                       "Failed to request core supplies: %d\n",
+                       ret);
+               return ret;
+       }
+
+       if (pdata) {
+               cs35l33->pdata = *pdata;
+       } else {
+               cs35l33_of_get_pdata(&i2c_client->dev, cs35l33);
+               pdata = &cs35l33->pdata;
+       }
+
+       ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL,
+                       cs35l33_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+                       "cs35l33", cs35l33);
+       if (ret != 0)
+               dev_warn(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
+
+       /* We could issue !RST or skip it based on AMP topology */
+       cs35l33->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+                       "reset-gpios", GPIOD_OUT_HIGH);
+       if (IS_ERR(cs35l33->reset_gpio)) {
+               dev_err(&i2c_client->dev, "%s ERROR: Can't get reset GPIO\n",
+                       __func__);
+               return PTR_ERR(cs35l33->reset_gpio);
+       }
+
+       ret = regulator_bulk_enable(cs35l33->num_core_supplies,
+                                       cs35l33->core_supplies);
+       if (ret != 0) {
+               dev_err(&i2c_client->dev,
+                       "Failed to enable core supplies: %d\n",
+                       ret);
+               return ret;
+       }
+
+       if (cs35l33->reset_gpio)
+               gpiod_set_value_cansleep(cs35l33->reset_gpio, 1);
+
+       msleep(CS35L33_BOOT_DELAY);
+       regcache_cache_only(cs35l33->regmap, false);
+
+       /* initialize codec */
+       ret = regmap_read(cs35l33->regmap, CS35L33_DEVID_AB, &reg);
+       devid = (reg & 0xFF) << 12;
+       ret = regmap_read(cs35l33->regmap, CS35L33_DEVID_CD, &reg);
+       devid |= (reg & 0xFF) << 4;
+       ret = regmap_read(cs35l33->regmap, CS35L33_DEVID_E, &reg);
+       devid |= (reg & 0xF0) >> 4;
+
+       if (devid != CS35L33_CHIP_ID) {
+               dev_err(&i2c_client->dev,
+                       "CS35L33 Device ID (%X). Expected ID %X\n",
+                       devid, CS35L33_CHIP_ID);
+               goto err_enable;
+       }
+
+       ret = regmap_read(cs35l33->regmap, CS35L33_REV_ID, &reg);
+       if (ret < 0) {
+               dev_err(&i2c_client->dev, "Get Revision ID failed\n");
+               goto err_enable;
+       }
+
+       dev_info(&i2c_client->dev,
+                "Cirrus Logic CS35L33, Revision: %02X\n", reg & 0xFF);
+
+       ret = regmap_register_patch(cs35l33->regmap,
+                       cs35l33_patch, ARRAY_SIZE(cs35l33_patch));
+       if (ret < 0) {
+               dev_err(&i2c_client->dev,
+                       "Error in applying regmap patch: %d\n", ret);
+               goto err_enable;
+       }
+
+       /* disable mclk and tdm */
+       regmap_update_bits(cs35l33->regmap, CS35L33_CLK_CTL,
+               CS35L33_MCLKDIS | CS35L33_SDOUT_3ST_TDM,
+               CS35L33_MCLKDIS | CS35L33_SDOUT_3ST_TDM);
+
+       pm_runtime_set_autosuspend_delay(&i2c_client->dev, 100);
+       pm_runtime_use_autosuspend(&i2c_client->dev);
+       pm_runtime_set_active(&i2c_client->dev);
+       pm_runtime_enable(&i2c_client->dev);
+
+       ret =  snd_soc_register_codec(&i2c_client->dev,
+                       &soc_codec_dev_cs35l33, &cs35l33_dai, 1);
+       if (ret < 0) {
+               dev_err(&i2c_client->dev, "%s: Register codec failed\n",
+                       __func__);
+               goto err_enable;
+       }
+
+       return 0;
+
+err_enable:
+       regulator_bulk_disable(cs35l33->num_core_supplies,
+                              cs35l33->core_supplies);
+
+       return ret;
+}
+
+static int cs35l33_i2c_remove(struct i2c_client *client)
+{
+       struct cs35l33_private *cs35l33 = i2c_get_clientdata(client);
+
+       snd_soc_unregister_codec(&client->dev);
+
+       if (cs35l33->reset_gpio)
+               gpiod_set_value_cansleep(cs35l33->reset_gpio, 0);
+
+       pm_runtime_disable(&client->dev);
+       regulator_bulk_disable(cs35l33->num_core_supplies,
+               cs35l33->core_supplies);
+
+       return 0;
+}
+
+static const struct of_device_id cs35l33_of_match[] = {
+       { .compatible = "cirrus,cs35l33", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, cs35l33_of_match);
+
+static const struct i2c_device_id cs35l33_id[] = {
+       {"cs35l33", 0},
+       {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs35l33_id);
+
+static struct i2c_driver cs35l33_i2c_driver = {
+       .driver = {
+               .name = "cs35l33",
+               .pm = &cs35l33_pm_ops,
+               .of_match_table = cs35l33_of_match,
+
+               },
+       .id_table = cs35l33_id,
+       .probe = cs35l33_i2c_probe,
+       .remove = cs35l33_i2c_remove,
+
+};
+module_i2c_driver(cs35l33_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L33 driver");
+MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <paul.handrigan@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l33.h b/sound/soc/codecs/cs35l33.h
new file mode 100644 (file)
index 0000000..c045737
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * cs35l33.h -- CS35L33 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <paul.handrigan@cirrus.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 __CS35L33_H__
+#define __CS35L33_H__
+
+#define CS35L33_CHIP_ID                0x00035A33
+#define CS35L33_DEVID_AB       0x01    /* Device ID A & B [RO] */
+#define CS35L33_DEVID_CD       0x02    /* Device ID C & D [RO] */
+#define CS35L33_DEVID_E                0x03    /* Device ID E [RO] */
+#define CS35L33_FAB_ID         0x04    /* Fab ID [RO] */
+#define CS35L33_REV_ID         0x05    /* Revision ID [RO] */
+#define CS35L33_PWRCTL1                0x06    /* Power Ctl 1 */
+#define CS35L33_PWRCTL2                0x07    /* Power Ctl 2 */
+#define CS35L33_CLK_CTL                0x08    /* Clock Ctl */
+#define CS35L33_BST_PEAK_CTL   0x09    /* Max Current for Boost */
+#define CS35L33_PROTECT_CTL    0x0A    /* Amp Protection Parameters */
+#define CS35L33_BST_CTL1       0x0B    /* Boost Converter CTL1 */
+#define CS35L33_BST_CTL2       0x0C    /* Boost Converter CTL2 */
+#define CS35L33_ADSP_CTL       0x0D    /* Serial Port Control */
+#define CS35L33_ADC_CTL                0x0E    /* ADC Control */
+#define CS35L33_DAC_CTL                0x0F    /* DAC Control */
+#define CS35L33_DIG_VOL_CTL    0x10    /* Digital Volume CTL */
+#define CS35L33_CLASSD_CTL     0x11    /* Class D Amp CTL */
+#define CS35L33_AMP_CTL                0x12    /* Amp Gain/Protecton Release CTL */
+#define CS35L33_INT_MASK_1     0x13    /* Interrupt Mask 1 */
+#define CS35L33_INT_MASK_2     0x14    /* Interrupt Mask 2 */
+#define CS35L33_INT_STATUS_1   0x15    /* Interrupt Status 1 [RO] */
+#define CS35L33_INT_STATUS_2   0x16    /* Interrupt Status 2 [RO] */
+#define CS35L33_DIAG_LOCK      0x17    /* Diagnostic Mode Register Lock */
+#define CS35L33_DIAG_CTRL_1    0x18    /* Diagnostic Mode Register Control */
+#define CS35L33_DIAG_CTRL_2    0x19    /* Diagnostic Mode Register Control 2 */
+#define CS35L33_HG_MEMLDO_CTL  0x23    /* H/G Memory/LDO CTL */
+#define CS35L33_HG_REL_RATE    0x24    /* H/G Release Rate */
+#define CS35L33_LDO_DEL                0x25    /* LDO Entry Delay/VPhg Control 1 */
+#define CS35L33_HG_HEAD                0x29    /* H/G Headroom */
+#define CS35L33_HG_EN          0x2A    /* H/G Enable/VPhg CNT2 */
+#define CS35L33_TX_VMON                0x2D    /* TDM TX Control 1 (VMON) */
+#define CS35L33_TX_IMON                0x2E    /* TDM TX Control 2 (IMON) */
+#define CS35L33_TX_VPMON       0x2F    /* TDM TX Control 3 (VPMON) */
+#define CS35L33_TX_VBSTMON     0x30    /* TDM TX Control 4 (VBSTMON) */
+#define CS35L33_TX_FLAG                0x31    /* TDM TX Control 5 (FLAG) */
+#define CS35L33_TX_EN1         0x32    /* TDM TX Enable 1 */
+#define CS35L33_TX_EN2         0x33    /* TDM TX Enable 2 */
+#define CS35L33_TX_EN3         0x34    /* TDM TX Enable 3 */
+#define CS35L33_TX_EN4         0x35    /* TDM TX Enable 4 */
+#define CS35L33_RX_AUD         0x36    /* TDM RX Control 1 */
+#define CS35L33_RX_SPLY                0x37    /* TDM RX Control 2 */
+#define CS35L33_RX_ALIVE       0x38    /* TDM RX Control 3 */
+#define CS35L33_BST_CTL4       0x39    /* Boost Converter Control 4 */
+#define CS35L33_HG_STATUS      0x3F    /* H/G Status */
+#define CS35L33_MAX_REGISTER   0x59
+
+#define CS35L33_MCLK_5644      5644800
+#define CS35L33_MCLK_6144      6144000
+#define CS35L33_MCLK_6         6000000
+#define CS35L33_MCLK_11289     11289600
+#define CS35L33_MCLK_12                12000000
+#define CS35L33_MCLK_12288     12288000
+
+/* CS35L33_PWRCTL1 */
+#define CS35L33_PDN_AMP                        (1 << 7)
+#define CS35L33_PDN_BST                        (1 << 2)
+#define CS35L33_PDN_ALL                        1
+
+/* CS35L33_PWRCTL2 */
+#define CS35L33_PDN_VMON_SHIFT         7
+#define CS35L33_PDN_VMON               (1 << CS35L33_PDN_VMON_SHIFT)
+#define CS35L33_PDN_IMON_SHIFT         6
+#define CS35L33_PDN_IMON               (1 << CS35L33_PDN_IMON_SHIFT)
+#define CS35L33_PDN_VPMON_SHIFT                5
+#define CS35L33_PDN_VPMON              (1 << CS35L33_PDN_VPMON_SHIFT)
+#define CS35L33_PDN_VBSTMON_SHIFT      4
+#define CS35L33_PDN_VBSTMON            (1 << CS35L33_PDN_VBSTMON_SHIFT)
+#define CS35L33_SDOUT_3ST_I2S_SHIFT    3
+#define CS35L33_SDOUT_3ST_I2S          (1 << CS35L33_SDOUT_3ST_I2S_SHIFT)
+#define CS35L33_PDN_SDIN_SHIFT         2
+#define CS35L33_PDN_SDIN               (1 << CS35L33_PDN_SDIN_SHIFT)
+#define CS35L33_PDN_TDM_SHIFT          1
+#define CS35L33_PDN_TDM                        (1 << CS35L33_PDN_TDM_SHIFT)
+
+/* CS35L33_CLK_CTL */
+#define CS35L33_MCLKDIS                        (1 << 7)
+#define CS35L33_MCLKDIV2               (1 << 6)
+#define CS35L33_SDOUT_3ST_TDM          (1 << 5)
+#define CS35L33_INT_FS_RATE            (1 << 4)
+#define CS35L33_ADSP_FS                        0xF
+
+/* CS35L33_PROTECT_CTL */
+#define CS35L33_ALIVE_WD_DIS           (3 << 2)
+
+/* CS35L33_BST_CTL1 */
+#define CS35L33_BST_CTL_SRC            (1 << 6)
+#define CS35L33_BST_CTL_SHIFT          (1 << 5)
+#define CS35L33_BST_CTL_MASK           0x3F
+
+/* CS35L33_BST_CTL2 */
+#define CS35L33_TDM_WD_SEL             (1 << 4)
+#define CS35L33_ALIVE_WD_DIS2          (1 << 3)
+#define CS35L33_VBST_SR_STEP           0x3
+
+/* CS35L33_ADSP_CTL */
+#define CS35L33_ADSP_DRIVE             (1 << 7)
+#define CS35L33_MS_MASK                        (1 << 6)
+#define CS35L33_SDIN_LOC               (3 << 4)
+#define CS35L33_ALIVE_RATE             0x3
+
+/* CS35L33_ADC_CTL */
+#define CS35L33_INV_VMON               (1 << 7)
+#define CS35L33_INV_IMON               (1 << 6)
+#define CS35L33_ADC_NOTCH_DIS          (1 << 5)
+#define CS35L33_IMON_SCALE             0xF
+
+/* CS35L33_DAC_CTL */
+#define CS35L33_INV_DAC                        (1 << 7)
+#define CS35L33_DAC_NOTCH_DIS          (1 << 5)
+#define CS35L33_DIGSFT                 (1 << 4)
+#define CS35L33_DSR_RATE               0xF
+
+/* CS35L33_CLASSD_CTL */
+#define CS35L33_AMP_SD                 (1 << 6)
+#define CS35L33_AMP_DRV_SEL_SRC                (1 << 5)
+#define CS35L33_AMP_DRV_SEL_MASK       0x10
+#define CS35L33_AMP_DRV_SEL_SHIFT      4
+#define CS35L33_AMP_CAL                        (1 << 3)
+#define CS35L33_GAIN_CHG_ZC_MASK       0x04
+#define CS35L33_GAIN_CHG_ZC_SHIFT      2
+#define CS35L33_CLASS_D_CTL_MASK       0x3F
+
+/* CS35L33_AMP_CTL */
+#define CS35L33_AMP_GAIN               0xF0
+#define CS35L33_CAL_ERR_RLS            (1 << 3)
+#define CS35L33_AMP_SHORT_RLS          (1 << 2)
+#define CS35L33_OTW_RLS                        (1 << 1)
+#define CS35L33_OTE_RLS                        1
+
+/* CS35L33_INT_MASK_1 */
+#define CS35L33_M_CAL_ERR_SHIFT                6
+#define CS35L33_M_CAL_ERR              (1 << CS35L33_M_CAL_ERR_SHIFT)
+#define CS35L33_M_ALIVE_ERR_SHIFT      5
+#define CS35L33_M_ALIVE_ERR            (1 << CS35L33_M_ALIVE_ERR_SHIFT)
+#define CS35L33_M_AMP_SHORT_SHIFT      2
+#define CS35L33_M_AMP_SHORT            (1 << CS35L33_M_AMP_SHORT_SHIFT)
+#define CS35L33_M_OTW_SHIFT            1
+#define CS35L33_M_OTW                  (1 << CS35L33_M_OTW_SHIFT)
+#define CS35L33_M_OTE_SHIFT            0
+#define CS35L33_M_OTE                  (1 << CS35L33_M_OTE_SHIFT)
+
+/* CS35L33_INT_STATUS_1 */
+#define CS35L33_CAL_ERR                        (1 << 6)
+#define CS35L33_ALIVE_ERR              (1 << 5)
+#define CS35L33_ADSPCLK_ERR            (1 << 4)
+#define CS35L33_MCLK_ERR               (1 << 3)
+#define CS35L33_AMP_SHORT              (1 << 2)
+#define CS35L33_OTW                    (1 << 1)
+#define CS35L33_OTE                    (1 << 0)
+
+/* CS35L33_INT_STATUS_2 */
+#define CS35L33_VMON_OVFL              (1 << 7)
+#define CS35L33_IMON_OVFL              (1 << 6)
+#define CS35L33_VPMON_OVFL             (1 << 5)
+#define CS35L33_VBSTMON_OVFL           (1 << 4)
+#define CS35L33_PDN_DONE               1
+
+/* CS35L33_BST_CTL4 */
+#define CS35L33_BST_RGS                        0x70
+#define CS35L33_BST_COEFF3             0xF
+
+/* CS35L33_HG_MEMLDO_CTL */
+#define CS35L33_MEM_DEPTH_SHIFT                5
+#define CS35L33_MEM_DEPTH_MASK         (0x3 << CS35L33_MEM_DEPTH_SHIFT)
+#define CS35L33_LDO_THLD_SHIFT         1
+#define CS35L33_LDO_THLD_MASK          (0xF << CS35L33_LDO_THLD_SHIFT)
+#define CS35L33_LDO_DISABLE_SHIFT      0
+#define CS35L33_LDO_DISABLE_MASK       (0x1 << CS35L33_LDO_DISABLE_SHIFT)
+
+/* CS35L33_LDO_DEL */
+#define CS35L33_VP_HG_VA_SHIFT         5
+#define CS35L33_VP_HG_VA_MASK          (0x7 << CS35L33_VP_HG_VA_SHIFT)
+#define CS35L33_LDO_ENTRY_DELAY_SHIFT  2
+#define CS35L33_LDO_ENTRY_DELAY_MASK   (0x7 << CS35L33_LDO_ENTRY_DELAY_SHIFT)
+#define CS35L33_VP_HG_RATE_SHIFT       0
+#define CS35L33_VP_HG_RATE_MASK                (0x3 << CS35L33_VP_HG_RATE_SHIFT)
+
+/* CS35L33_HG_HEAD */
+#define CS35L33_HD_RM_SHIFT            0
+#define CS35L33_HD_RM_MASK             (0x7F << CS35L33_HD_RM_SHIFT)
+
+/* CS35L33_HG_EN */
+#define CS35L33_CLASS_HG_ENA_SHIFT     7
+#define CS35L33_CLASS_HG_EN_MASK       (0x1 << CS35L33_CLASS_HG_ENA_SHIFT)
+#define CS35L33_VP_HG_AUTO_SHIFT       6
+#define CS35L33_VP_HG_AUTO_MASK                (0x1 << 6)
+#define CS35L33_VP_HG_SHIFT            0
+#define CS35L33_VP_HG_MASK             (0x1F << CS35L33_VP_HG_SHIFT)
+
+#define CS35L33_RATES (SNDRV_PCM_RATE_8000_48000)
+#define CS35L33_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+                       SNDRV_PCM_FMTBIT_S24_LE)
+
+/* CS35L33_{RX,TX}_X */
+#define CS35L33_X_STATE_SHIFT          7
+#define CS35L33_X_STATE                        (1 << CS35L33_X_STATE_SHIFT)
+#define CS35L33_X_LOC_SHIFT            0
+#define CS35L33_X_LOC                  (0x1F << CS35L33_X_LOC_SHIFT)
+
+/* CS35L33_RX_AUD */
+#define CS35L33_AUDIN_RX_DEPTH_SHIFT   5
+#define CS35L33_AUDIN_RX_DEPTH         (0x7 << CS35L33_AUDIN_RX_DEPTH_SHIFT)
+
+#endif
index 5ec5a682d186971daa09d532d0b4627806eecaa3..954a4f5d3338c5c96b137296e16e10042e67795f 100644 (file)
@@ -359,6 +359,11 @@ SND_SOC_DAPM_INPUT("IN2R"),
 SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
 SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
 
+SND_SOC_DAPM_OUTPUT("DSP Voice Trigger"),
+
+SND_SOC_DAPM_SWITCH("DSP3 Voice Trigger", SND_SOC_NOPM, 2, 0,
+                   &arizona_voice_trigger_switch[2]),
+
 SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
                   0, NULL, 0, arizona_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
@@ -899,10 +904,16 @@ static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = {
 
        { "MICSUPP", NULL, "SYSCLK" },
 
+       { "DRC1 Signal Activity", NULL, "SYSCLK" },
+       { "DRC2 Signal Activity", NULL, "SYSCLK" },
        { "DRC1 Signal Activity", NULL, "DRC1L" },
        { "DRC1 Signal Activity", NULL, "DRC1R" },
        { "DRC2 Signal Activity", NULL, "DRC2L" },
        { "DRC2 Signal Activity", NULL, "DRC2R" },
+
+       { "DSP Voice Trigger", NULL, "SYSCLK" },
+       { "DSP Voice Trigger", NULL, "DSP3 Voice Trigger" },
+       { "DSP3 Voice Trigger", "Switch", "DSP3" },
 };
 
 static int cs47l24_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
@@ -1067,6 +1078,7 @@ static irqreturn_t cs47l24_adsp2_irq(int irq, void *data)
 {
        struct cs47l24_priv *priv = data;
        struct arizona *arizona = priv->core.arizona;
+       struct arizona_voice_trigger_info info;
        int serviced = 0;
        int i, ret;
 
@@ -1074,6 +1086,12 @@ static irqreturn_t cs47l24_adsp2_irq(int irq, void *data)
                ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]);
                if (ret != -ENODEV)
                        serviced++;
+               if (ret == WM_ADSP_COMPR_VOICE_TRIGGER) {
+                       info.core = i;
+                       arizona_call_notifiers(arizona,
+                                              ARIZONA_NOTIFY_VOICE_TRIGGER,
+                                              &info);
+               }
        }
 
        if (!serviced) {
@@ -1096,6 +1114,7 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec)
        arizona_init_spk(codec);
        arizona_init_gpio(codec);
        arizona_init_mono(codec);
+       arizona_init_notifiers(codec);
 
        ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
                                  "ADSP2 Compressed IRQ", cs47l24_adsp2_irq,
diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c
new file mode 100644 (file)
index 0000000..2c0d9c4
--- /dev/null
@@ -0,0 +1,1143 @@
+/*
+ * cs53l30.c  --  CS53l30 ALSA Soc Audio driver
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Authors: Paul Handrigan <Paul.Handrigan@cirrus.com>,
+ *          Tim Howe <Tim.Howe@cirrus.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/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "cs53l30.h"
+
+#define CS53L30_NUM_SUPPLIES 2
+static const char *const cs53l30_supply_names[CS53L30_NUM_SUPPLIES] = {
+       "VA",
+       "VP",
+};
+
+struct cs53l30_private {
+       struct regulator_bulk_data      supplies[CS53L30_NUM_SUPPLIES];
+       struct regmap                   *regmap;
+       struct gpio_desc                *reset_gpio;
+       struct gpio_desc                *mute_gpio;
+       struct clk                      *mclk;
+       bool                            use_sdout2;
+       u32                             mclk_rate;
+};
+
+static const struct reg_default cs53l30_reg_defaults[] = {
+       { CS53L30_PWRCTL,               CS53L30_PWRCTL_DEFAULT },
+       { CS53L30_MCLKCTL,              CS53L30_MCLKCTL_DEFAULT },
+       { CS53L30_INT_SR_CTL,           CS53L30_INT_SR_CTL_DEFAULT },
+       { CS53L30_MICBIAS_CTL,          CS53L30_MICBIAS_CTL_DEFAULT },
+       { CS53L30_ASPCFG_CTL,           CS53L30_ASPCFG_CTL_DEFAULT },
+       { CS53L30_ASP_CTL1,             CS53L30_ASP_CTL1_DEFAULT },
+       { CS53L30_ASP_TDMTX_CTL1,       CS53L30_ASP_TDMTX_CTLx_DEFAULT },
+       { CS53L30_ASP_TDMTX_CTL2,       CS53L30_ASP_TDMTX_CTLx_DEFAULT },
+       { CS53L30_ASP_TDMTX_CTL3,       CS53L30_ASP_TDMTX_CTLx_DEFAULT },
+       { CS53L30_ASP_TDMTX_CTL4,       CS53L30_ASP_TDMTX_CTLx_DEFAULT },
+       { CS53L30_ASP_TDMTX_EN1,        CS53L30_ASP_TDMTX_ENx_DEFAULT },
+       { CS53L30_ASP_TDMTX_EN2,        CS53L30_ASP_TDMTX_ENx_DEFAULT },
+       { CS53L30_ASP_TDMTX_EN3,        CS53L30_ASP_TDMTX_ENx_DEFAULT },
+       { CS53L30_ASP_TDMTX_EN4,        CS53L30_ASP_TDMTX_ENx_DEFAULT },
+       { CS53L30_ASP_TDMTX_EN5,        CS53L30_ASP_TDMTX_ENx_DEFAULT },
+       { CS53L30_ASP_TDMTX_EN6,        CS53L30_ASP_TDMTX_ENx_DEFAULT },
+       { CS53L30_ASP_CTL2,             CS53L30_ASP_CTL2_DEFAULT },
+       { CS53L30_SFT_RAMP,             CS53L30_SFT_RMP_DEFAULT },
+       { CS53L30_LRCK_CTL1,            CS53L30_LRCK_CTLx_DEFAULT },
+       { CS53L30_LRCK_CTL2,            CS53L30_LRCK_CTLx_DEFAULT },
+       { CS53L30_MUTEP_CTL1,           CS53L30_MUTEP_CTL1_DEFAULT },
+       { CS53L30_MUTEP_CTL2,           CS53L30_MUTEP_CTL2_DEFAULT },
+       { CS53L30_INBIAS_CTL1,          CS53L30_INBIAS_CTL1_DEFAULT },
+       { CS53L30_INBIAS_CTL2,          CS53L30_INBIAS_CTL2_DEFAULT },
+       { CS53L30_DMIC1_STR_CTL,        CS53L30_DMIC1_STR_CTL_DEFAULT },
+       { CS53L30_DMIC2_STR_CTL,        CS53L30_DMIC2_STR_CTL_DEFAULT },
+       { CS53L30_ADCDMIC1_CTL1,        CS53L30_ADCDMICx_CTL1_DEFAULT },
+       { CS53L30_ADCDMIC1_CTL2,        CS53L30_ADCDMIC1_CTL2_DEFAULT },
+       { CS53L30_ADC1_CTL3,            CS53L30_ADCx_CTL3_DEFAULT },
+       { CS53L30_ADC1_NG_CTL,          CS53L30_ADCx_NG_CTL_DEFAULT },
+       { CS53L30_ADC1A_AFE_CTL,        CS53L30_ADCxy_AFE_CTL_DEFAULT },
+       { CS53L30_ADC1B_AFE_CTL,        CS53L30_ADCxy_AFE_CTL_DEFAULT },
+       { CS53L30_ADC1A_DIG_VOL,        CS53L30_ADCxy_DIG_VOL_DEFAULT },
+       { CS53L30_ADC1B_DIG_VOL,        CS53L30_ADCxy_DIG_VOL_DEFAULT },
+       { CS53L30_ADCDMIC2_CTL1,        CS53L30_ADCDMICx_CTL1_DEFAULT },
+       { CS53L30_ADCDMIC2_CTL2,        CS53L30_ADCDMIC1_CTL2_DEFAULT },
+       { CS53L30_ADC2_CTL3,            CS53L30_ADCx_CTL3_DEFAULT },
+       { CS53L30_ADC2_NG_CTL,          CS53L30_ADCx_NG_CTL_DEFAULT },
+       { CS53L30_ADC2A_AFE_CTL,        CS53L30_ADCxy_AFE_CTL_DEFAULT },
+       { CS53L30_ADC2B_AFE_CTL,        CS53L30_ADCxy_AFE_CTL_DEFAULT },
+       { CS53L30_ADC2A_DIG_VOL,        CS53L30_ADCxy_DIG_VOL_DEFAULT },
+       { CS53L30_ADC2B_DIG_VOL,        CS53L30_ADCxy_DIG_VOL_DEFAULT },
+       { CS53L30_INT_MASK,             CS53L30_DEVICE_INT_MASK },
+};
+
+static bool cs53l30_volatile_register(struct device *dev, unsigned int reg)
+{
+       if (reg == CS53L30_IS)
+               return true;
+       else
+               return false;
+}
+
+static bool cs53l30_writeable_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CS53L30_DEVID_AB:
+       case CS53L30_DEVID_CD:
+       case CS53L30_DEVID_E:
+       case CS53L30_REVID:
+       case CS53L30_IS:
+               return false;
+       default:
+               return true;
+       }
+}
+
+static bool cs53l30_readable_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CS53L30_DEVID_AB:
+       case CS53L30_DEVID_CD:
+       case CS53L30_DEVID_E:
+       case CS53L30_REVID:
+       case CS53L30_PWRCTL:
+       case CS53L30_MCLKCTL:
+       case CS53L30_INT_SR_CTL:
+       case CS53L30_MICBIAS_CTL:
+       case CS53L30_ASPCFG_CTL:
+       case CS53L30_ASP_CTL1:
+       case CS53L30_ASP_TDMTX_CTL1:
+       case CS53L30_ASP_TDMTX_CTL2:
+       case CS53L30_ASP_TDMTX_CTL3:
+       case CS53L30_ASP_TDMTX_CTL4:
+       case CS53L30_ASP_TDMTX_EN1:
+       case CS53L30_ASP_TDMTX_EN2:
+       case CS53L30_ASP_TDMTX_EN3:
+       case CS53L30_ASP_TDMTX_EN4:
+       case CS53L30_ASP_TDMTX_EN5:
+       case CS53L30_ASP_TDMTX_EN6:
+       case CS53L30_ASP_CTL2:
+       case CS53L30_SFT_RAMP:
+       case CS53L30_LRCK_CTL1:
+       case CS53L30_LRCK_CTL2:
+       case CS53L30_MUTEP_CTL1:
+       case CS53L30_MUTEP_CTL2:
+       case CS53L30_INBIAS_CTL1:
+       case CS53L30_INBIAS_CTL2:
+       case CS53L30_DMIC1_STR_CTL:
+       case CS53L30_DMIC2_STR_CTL:
+       case CS53L30_ADCDMIC1_CTL1:
+       case CS53L30_ADCDMIC1_CTL2:
+       case CS53L30_ADC1_CTL3:
+       case CS53L30_ADC1_NG_CTL:
+       case CS53L30_ADC1A_AFE_CTL:
+       case CS53L30_ADC1B_AFE_CTL:
+       case CS53L30_ADC1A_DIG_VOL:
+       case CS53L30_ADC1B_DIG_VOL:
+       case CS53L30_ADCDMIC2_CTL1:
+       case CS53L30_ADCDMIC2_CTL2:
+       case CS53L30_ADC2_CTL3:
+       case CS53L30_ADC2_NG_CTL:
+       case CS53L30_ADC2A_AFE_CTL:
+       case CS53L30_ADC2B_AFE_CTL:
+       case CS53L30_ADC2A_DIG_VOL:
+       case CS53L30_ADC2B_DIG_VOL:
+       case CS53L30_INT_MASK:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static DECLARE_TLV_DB_SCALE(adc_boost_tlv, 0, 2000, 0);
+static DECLARE_TLV_DB_SCALE(adc_ng_boost_tlv, 0, 3000, 0);
+static DECLARE_TLV_DB_SCALE(pga_tlv, -600, 50, 0);
+static DECLARE_TLV_DB_SCALE(dig_tlv, -9600, 100, 1);
+static DECLARE_TLV_DB_SCALE(pga_preamp_tlv, 0, 10000, 0);
+
+static const char * const input1_sel_text[] = {
+       "DMIC1 On AB In",
+       "DMIC1 On A In",
+       "DMIC1 On B In",
+       "ADC1 On AB In",
+       "ADC1 On A In",
+       "ADC1 On B In",
+       "DMIC1 Off ADC1 Off",
+};
+
+static unsigned int const input1_sel_values[] = {
+       CS53L30_CH_TYPE,
+       CS53L30_ADCxB_PDN | CS53L30_CH_TYPE,
+       CS53L30_ADCxA_PDN | CS53L30_CH_TYPE,
+       CS53L30_DMICx_PDN,
+       CS53L30_ADCxB_PDN | CS53L30_DMICx_PDN,
+       CS53L30_ADCxA_PDN | CS53L30_DMICx_PDN,
+       CS53L30_ADCxA_PDN | CS53L30_ADCxB_PDN | CS53L30_DMICx_PDN,
+};
+
+static const char * const input2_sel_text[] = {
+       "DMIC2 On AB In",
+       "DMIC2 On A In",
+       "DMIC2 On B In",
+       "ADC2 On AB In",
+       "ADC2 On A In",
+       "ADC2 On B In",
+       "DMIC2 Off ADC2 Off",
+};
+
+static unsigned int const input2_sel_values[] = {
+       0x0,
+       CS53L30_ADCxB_PDN,
+       CS53L30_ADCxA_PDN,
+       CS53L30_DMICx_PDN,
+       CS53L30_ADCxB_PDN | CS53L30_DMICx_PDN,
+       CS53L30_ADCxA_PDN | CS53L30_DMICx_PDN,
+       CS53L30_ADCxA_PDN | CS53L30_ADCxB_PDN | CS53L30_DMICx_PDN,
+};
+
+static const char * const input1_route_sel_text[] = {
+       "ADC1_SEL", "DMIC1_SEL",
+};
+
+static const struct soc_enum input1_route_sel_enum =
+       SOC_ENUM_SINGLE(CS53L30_ADCDMIC1_CTL1, CS53L30_CH_TYPE_SHIFT,
+                       ARRAY_SIZE(input1_route_sel_text),
+                       input1_route_sel_text);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(input1_sel_enum, CS53L30_ADCDMIC1_CTL1, 0,
+                                 CS53L30_ADCDMICx_PDN_MASK, input1_sel_text,
+                                 input1_sel_values);
+
+static const struct snd_kcontrol_new input1_route_sel_mux =
+       SOC_DAPM_ENUM("Input 1 Route", input1_route_sel_enum);
+
+static const char * const input2_route_sel_text[] = {
+       "ADC2_SEL", "DMIC2_SEL",
+};
+
+/* Note: CS53L30_ADCDMIC1_CTL1 CH_TYPE controls inputs 1 and 2 */
+static const struct soc_enum input2_route_sel_enum =
+       SOC_ENUM_SINGLE(CS53L30_ADCDMIC1_CTL1, 0,
+                       ARRAY_SIZE(input2_route_sel_text),
+                       input2_route_sel_text);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(input2_sel_enum, CS53L30_ADCDMIC2_CTL1, 0,
+                                 CS53L30_ADCDMICx_PDN_MASK, input2_sel_text,
+                                 input2_sel_values);
+
+static const struct snd_kcontrol_new input2_route_sel_mux =
+       SOC_DAPM_ENUM("Input 2 Route", input2_route_sel_enum);
+
+/*
+ * TB = 6144*(MCLK(int) scaling factor)/MCLK(internal)
+ * TB - Time base
+ * NOTE: If MCLK_INT_SCALE = 0, then TB=1
+ */
+static const char * const cs53l30_ng_delay_text[] = {
+       "TB*50ms", "TB*100ms", "TB*150ms", "TB*200ms",
+};
+
+static const struct soc_enum adc1_ng_delay_enum =
+       SOC_ENUM_SINGLE(CS53L30_ADC1_NG_CTL, CS53L30_ADCx_NG_DELAY_SHIFT,
+                       ARRAY_SIZE(cs53l30_ng_delay_text),
+                       cs53l30_ng_delay_text);
+
+static const struct soc_enum adc2_ng_delay_enum =
+       SOC_ENUM_SINGLE(CS53L30_ADC2_NG_CTL, CS53L30_ADCx_NG_DELAY_SHIFT,
+                       ARRAY_SIZE(cs53l30_ng_delay_text),
+                       cs53l30_ng_delay_text);
+
+/* The noise gate threshold selected will depend on NG Boost */
+static const char * const cs53l30_ng_thres_text[] = {
+       "-64dB/-34dB", "-66dB/-36dB", "-70dB/-40dB", "-73dB/-43dB",
+       "-76dB/-46dB", "-82dB/-52dB", "-58dB", "-64dB",
+};
+
+static const struct soc_enum adc1_ng_thres_enum =
+       SOC_ENUM_SINGLE(CS53L30_ADC1_NG_CTL, CS53L30_ADCx_NG_THRESH_SHIFT,
+                       ARRAY_SIZE(cs53l30_ng_thres_text),
+                       cs53l30_ng_thres_text);
+
+static const struct soc_enum adc2_ng_thres_enum =
+       SOC_ENUM_SINGLE(CS53L30_ADC2_NG_CTL, CS53L30_ADCx_NG_THRESH_SHIFT,
+                       ARRAY_SIZE(cs53l30_ng_thres_text),
+                       cs53l30_ng_thres_text);
+
+/* Corner frequencies are with an Fs of 48kHz. */
+static const char * const hpf_corner_freq_text[] = {
+       "1.86Hz", "120Hz", "235Hz", "466Hz",
+};
+
+static const struct soc_enum adc1_hpf_enum =
+       SOC_ENUM_SINGLE(CS53L30_ADC1_CTL3, CS53L30_ADCx_HPF_CF_SHIFT,
+                       ARRAY_SIZE(hpf_corner_freq_text), hpf_corner_freq_text);
+
+static const struct soc_enum adc2_hpf_enum =
+       SOC_ENUM_SINGLE(CS53L30_ADC2_CTL3, CS53L30_ADCx_HPF_CF_SHIFT,
+                       ARRAY_SIZE(hpf_corner_freq_text), hpf_corner_freq_text);
+
+static const struct snd_kcontrol_new cs53l30_snd_controls[] = {
+       SOC_SINGLE("Digital Soft-Ramp Switch", CS53L30_SFT_RAMP,
+                  CS53L30_DIGSFT_SHIFT, 1, 0),
+       SOC_SINGLE("ADC1 Noise Gate Ganging Switch", CS53L30_ADC1_CTL3,
+                  CS53L30_ADCx_NG_ALL_SHIFT, 1, 0),
+       SOC_SINGLE("ADC2 Noise Gate Ganging Switch", CS53L30_ADC2_CTL3,
+                  CS53L30_ADCx_NG_ALL_SHIFT, 1, 0),
+       SOC_SINGLE("ADC1A Noise Gate Enable Switch", CS53L30_ADC1_NG_CTL,
+                  CS53L30_ADCxA_NG_SHIFT, 1, 0),
+       SOC_SINGLE("ADC1B Noise Gate Enable Switch", CS53L30_ADC1_NG_CTL,
+                  CS53L30_ADCxB_NG_SHIFT, 1, 0),
+       SOC_SINGLE("ADC2A Noise Gate Enable Switch", CS53L30_ADC2_NG_CTL,
+                  CS53L30_ADCxA_NG_SHIFT, 1, 0),
+       SOC_SINGLE("ADC2B Noise Gate Enable Switch", CS53L30_ADC2_NG_CTL,
+                  CS53L30_ADCxB_NG_SHIFT, 1, 0),
+       SOC_SINGLE("ADC1 Notch Filter Switch", CS53L30_ADCDMIC1_CTL2,
+                  CS53L30_ADCx_NOTCH_DIS_SHIFT, 1, 1),
+       SOC_SINGLE("ADC2 Notch Filter Switch", CS53L30_ADCDMIC2_CTL2,
+                  CS53L30_ADCx_NOTCH_DIS_SHIFT, 1, 1),
+       SOC_SINGLE("ADC1A Invert Switch", CS53L30_ADCDMIC1_CTL2,
+                  CS53L30_ADCxA_INV_SHIFT, 1, 0),
+       SOC_SINGLE("ADC1B Invert Switch", CS53L30_ADCDMIC1_CTL2,
+                  CS53L30_ADCxB_INV_SHIFT, 1, 0),
+       SOC_SINGLE("ADC2A Invert Switch", CS53L30_ADCDMIC2_CTL2,
+                  CS53L30_ADCxA_INV_SHIFT, 1, 0),
+       SOC_SINGLE("ADC2B Invert Switch", CS53L30_ADCDMIC2_CTL2,
+                  CS53L30_ADCxB_INV_SHIFT, 1, 0),
+
+       SOC_SINGLE_TLV("ADC1A Digital Boost Volume", CS53L30_ADCDMIC1_CTL2,
+                      CS53L30_ADCxA_DIG_BOOST_SHIFT, 1, 0, adc_boost_tlv),
+       SOC_SINGLE_TLV("ADC1B Digital Boost Volume", CS53L30_ADCDMIC1_CTL2,
+                      CS53L30_ADCxB_DIG_BOOST_SHIFT, 1, 0, adc_boost_tlv),
+       SOC_SINGLE_TLV("ADC2A Digital Boost Volume", CS53L30_ADCDMIC2_CTL2,
+                      CS53L30_ADCxA_DIG_BOOST_SHIFT, 1, 0, adc_boost_tlv),
+       SOC_SINGLE_TLV("ADC2B Digital Boost Volume", CS53L30_ADCDMIC2_CTL2,
+                      CS53L30_ADCxB_DIG_BOOST_SHIFT, 1, 0, adc_boost_tlv),
+       SOC_SINGLE_TLV("ADC1 NG Boost Volume", CS53L30_ADC1_NG_CTL,
+                      CS53L30_ADCx_NG_BOOST_SHIFT, 1, 0, adc_ng_boost_tlv),
+       SOC_SINGLE_TLV("ADC2 NG Boost Volume", CS53L30_ADC2_NG_CTL,
+                      CS53L30_ADCx_NG_BOOST_SHIFT, 1, 0, adc_ng_boost_tlv),
+
+       SOC_DOUBLE_R_TLV("ADC1 Preamplifier Volume", CS53L30_ADC1A_AFE_CTL,
+                        CS53L30_ADC1B_AFE_CTL, CS53L30_ADCxy_PREAMP_SHIFT,
+                        2, 0, pga_preamp_tlv),
+       SOC_DOUBLE_R_TLV("ADC2 Preamplifier Volume", CS53L30_ADC2A_AFE_CTL,
+                        CS53L30_ADC2B_AFE_CTL, CS53L30_ADCxy_PREAMP_SHIFT,
+                        2, 0, pga_preamp_tlv),
+
+       SOC_ENUM("Input 1 Channel Select", input1_sel_enum),
+       SOC_ENUM("Input 2 Channel Select", input2_sel_enum),
+
+       SOC_ENUM("ADC1 HPF Select", adc1_hpf_enum),
+       SOC_ENUM("ADC2 HPF Select", adc2_hpf_enum),
+       SOC_ENUM("ADC1 NG Threshold", adc1_ng_thres_enum),
+       SOC_ENUM("ADC2 NG Threshold", adc2_ng_thres_enum),
+       SOC_ENUM("ADC1 NG Delay", adc1_ng_delay_enum),
+       SOC_ENUM("ADC2 NG Delay", adc2_ng_delay_enum),
+
+       SOC_SINGLE_SX_TLV("ADC1A PGA Volume",
+                   CS53L30_ADC1A_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
+       SOC_SINGLE_SX_TLV("ADC1B PGA Volume",
+                   CS53L30_ADC1B_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
+       SOC_SINGLE_SX_TLV("ADC2A PGA Volume",
+                   CS53L30_ADC2A_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
+       SOC_SINGLE_SX_TLV("ADC2B PGA Volume",
+                   CS53L30_ADC2B_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
+
+       SOC_SINGLE_SX_TLV("ADC1A Digital Volume",
+                   CS53L30_ADC1A_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
+       SOC_SINGLE_SX_TLV("ADC1B Digital Volume",
+                   CS53L30_ADC1B_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
+       SOC_SINGLE_SX_TLV("ADC2A Digital Volume",
+                   CS53L30_ADC2A_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
+       SOC_SINGLE_SX_TLV("ADC2B Digital Volume",
+                   CS53L30_ADC2B_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
+};
+
+static const struct snd_soc_dapm_widget cs53l30_dapm_widgets[] = {
+       SND_SOC_DAPM_INPUT("IN1_DMIC1"),
+       SND_SOC_DAPM_INPUT("IN2"),
+       SND_SOC_DAPM_INPUT("IN3_DMIC2"),
+       SND_SOC_DAPM_INPUT("IN4"),
+       SND_SOC_DAPM_SUPPLY("MIC1 Bias", CS53L30_MICBIAS_CTL,
+                           CS53L30_MIC1_BIAS_PDN_SHIFT, 1, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("MIC2 Bias", CS53L30_MICBIAS_CTL,
+                           CS53L30_MIC2_BIAS_PDN_SHIFT, 1, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("MIC3 Bias", CS53L30_MICBIAS_CTL,
+                           CS53L30_MIC3_BIAS_PDN_SHIFT, 1, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("MIC4 Bias", CS53L30_MICBIAS_CTL,
+                           CS53L30_MIC4_BIAS_PDN_SHIFT, 1, NULL, 0),
+
+       SND_SOC_DAPM_AIF_OUT("ASP_SDOUT1", NULL, 0, CS53L30_ASP_CTL1,
+                            CS53L30_ASP_SDOUTx_PDN_SHIFT, 1),
+       SND_SOC_DAPM_AIF_OUT("ASP_SDOUT2", NULL, 0, CS53L30_ASP_CTL2,
+                            CS53L30_ASP_SDOUTx_PDN_SHIFT, 1),
+
+       SND_SOC_DAPM_MUX("Input Mux 1", SND_SOC_NOPM, 0, 0,
+                        &input1_route_sel_mux),
+       SND_SOC_DAPM_MUX("Input Mux 2", SND_SOC_NOPM, 0, 0,
+                        &input2_route_sel_mux),
+
+       SND_SOC_DAPM_ADC("ADC1A", NULL, CS53L30_ADCDMIC1_CTL1,
+                        CS53L30_ADCxA_PDN_SHIFT, 1),
+       SND_SOC_DAPM_ADC("ADC1B", NULL, CS53L30_ADCDMIC1_CTL1,
+                        CS53L30_ADCxB_PDN_SHIFT, 1),
+       SND_SOC_DAPM_ADC("ADC2A", NULL, CS53L30_ADCDMIC2_CTL1,
+                        CS53L30_ADCxA_PDN_SHIFT, 1),
+       SND_SOC_DAPM_ADC("ADC2B", NULL, CS53L30_ADCDMIC2_CTL1,
+                        CS53L30_ADCxB_PDN_SHIFT, 1),
+       SND_SOC_DAPM_ADC("DMIC1", NULL, CS53L30_ADCDMIC1_CTL1,
+                        CS53L30_DMICx_PDN_SHIFT, 1),
+       SND_SOC_DAPM_ADC("DMIC2", NULL, CS53L30_ADCDMIC2_CTL1,
+                        CS53L30_DMICx_PDN_SHIFT, 1),
+};
+
+static const struct snd_soc_dapm_route cs53l30_dapm_routes[] = {
+       /* ADC Input Paths */
+       {"ADC1A", NULL, "IN1_DMIC1"},
+       {"Input Mux 1", "ADC1_SEL", "ADC1A"},
+       {"ADC1B", NULL, "IN2"},
+
+       {"ADC2A", NULL, "IN3_DMIC2"},
+       {"Input Mux 2", "ADC2_SEL", "ADC2A"},
+       {"ADC2B", NULL, "IN4"},
+
+       /* MIC Bias Paths */
+       {"ADC1A", NULL, "MIC1 Bias"},
+       {"ADC1B", NULL, "MIC2 Bias"},
+       {"ADC2A", NULL, "MIC3 Bias"},
+       {"ADC2B", NULL, "MIC4 Bias"},
+
+       /* DMIC Paths */
+       {"DMIC1", NULL, "IN1_DMIC1"},
+       {"Input Mux 1", "DMIC1_SEL", "DMIC1"},
+
+       {"DMIC2", NULL, "IN3_DMIC2"},
+       {"Input Mux 2", "DMIC2_SEL", "DMIC2"},
+};
+
+static const struct snd_soc_dapm_route cs53l30_dapm_routes_sdout1[] = {
+       /* Output Paths when using SDOUT1 only */
+       {"ASP_SDOUT1", NULL, "ADC1A" },
+       {"ASP_SDOUT1", NULL, "Input Mux 1"},
+       {"ASP_SDOUT1", NULL, "ADC1B"},
+
+       {"ASP_SDOUT1", NULL, "ADC2A"},
+       {"ASP_SDOUT1", NULL, "Input Mux 2"},
+       {"ASP_SDOUT1", NULL, "ADC2B"},
+
+       {"Capture", NULL, "ASP_SDOUT1"},
+};
+
+static const struct snd_soc_dapm_route cs53l30_dapm_routes_sdout2[] = {
+       /* Output Paths when using both SDOUT1 and SDOUT2 */
+       {"ASP_SDOUT1", NULL, "ADC1A" },
+       {"ASP_SDOUT1", NULL, "Input Mux 1"},
+       {"ASP_SDOUT1", NULL, "ADC1B"},
+
+       {"ASP_SDOUT2", NULL, "ADC2A"},
+       {"ASP_SDOUT2", NULL, "Input Mux 2"},
+       {"ASP_SDOUT2", NULL, "ADC2B"},
+
+       {"Capture", NULL, "ASP_SDOUT1"},
+       {"Capture", NULL, "ASP_SDOUT2"},
+};
+
+struct cs53l30_mclk_div {
+       u32 mclk_rate;
+       u32 srate;
+       u8 asp_rate;
+       u8 internal_fs_ratio;
+       u8 mclk_int_scale;
+};
+
+static struct cs53l30_mclk_div cs53l30_mclk_coeffs[] = {
+       /* NOTE: Enable MCLK_INT_SCALE to save power. */
+
+       /* MCLK, Sample Rate, asp_rate, internal_fs_ratio, mclk_int_scale */
+       {5644800, 11025, 0x4, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+       {5644800, 22050, 0x8, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+       {5644800, 44100, 0xC, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+
+       {6000000,  8000, 0x1, 0, CS53L30_MCLK_INT_SCALE},
+       {6000000, 11025, 0x2, 0, CS53L30_MCLK_INT_SCALE},
+       {6000000, 12000, 0x4, 0, CS53L30_MCLK_INT_SCALE},
+       {6000000, 16000, 0x5, 0, CS53L30_MCLK_INT_SCALE},
+       {6000000, 22050, 0x6, 0, CS53L30_MCLK_INT_SCALE},
+       {6000000, 24000, 0x8, 0, CS53L30_MCLK_INT_SCALE},
+       {6000000, 32000, 0x9, 0, CS53L30_MCLK_INT_SCALE},
+       {6000000, 44100, 0xA, 0, CS53L30_MCLK_INT_SCALE},
+       {6000000, 48000, 0xC, 0, CS53L30_MCLK_INT_SCALE},
+
+       {6144000,  8000, 0x1, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+       {6144000, 11025, 0x2, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+       {6144000, 12000, 0x4, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+       {6144000, 16000, 0x5, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+       {6144000, 22050, 0x6, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+       {6144000, 24000, 0x8, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+       {6144000, 32000, 0x9, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+       {6144000, 44100, 0xA, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+       {6144000, 48000, 0xC, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+
+       {6400000,  8000, 0x1, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+       {6400000, 11025, 0x2, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+       {6400000, 12000, 0x4, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+       {6400000, 16000, 0x5, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+       {6400000, 22050, 0x6, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+       {6400000, 24000, 0x8, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+       {6400000, 32000, 0x9, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+       {6400000, 44100, 0xA, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+       {6400000, 48000, 0xC, CS53L30_INTRNL_FS_RATIO, CS53L30_MCLK_INT_SCALE},
+};
+
+struct cs53l30_mclkx_div {
+       u32 mclkx;
+       u8 ratio;
+       u8 mclkdiv;
+};
+
+static struct cs53l30_mclkx_div cs53l30_mclkx_coeffs[] = {
+       {5644800,  1, CS53L30_MCLK_DIV_BY_1},
+       {6000000,  1, CS53L30_MCLK_DIV_BY_1},
+       {6144000,  1, CS53L30_MCLK_DIV_BY_1},
+       {11289600, 2, CS53L30_MCLK_DIV_BY_2},
+       {12288000, 2, CS53L30_MCLK_DIV_BY_2},
+       {12000000, 2, CS53L30_MCLK_DIV_BY_2},
+       {19200000, 3, CS53L30_MCLK_DIV_BY_3},
+};
+
+static int cs53l30_get_mclkx_coeff(int mclkx)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(cs53l30_mclkx_coeffs); i++) {
+               if (cs53l30_mclkx_coeffs[i].mclkx == mclkx)
+                       return i;
+       }
+
+       return -EINVAL;
+}
+
+static int cs53l30_get_mclk_coeff(int mclk_rate, int srate)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(cs53l30_mclk_coeffs); i++) {
+               if (cs53l30_mclk_coeffs[i].mclk_rate == mclk_rate &&
+                   cs53l30_mclk_coeffs[i].srate == srate)
+                       return i;
+       }
+
+       return -EINVAL;
+}
+
+static int cs53l30_set_sysclk(struct snd_soc_dai *dai,
+                             int clk_id, unsigned int freq, int dir)
+{
+       struct cs53l30_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+       int mclkx_coeff;
+       u32 mclk_rate;
+
+       /* MCLKX -> MCLK */
+       mclkx_coeff = cs53l30_get_mclkx_coeff(freq);
+       if (mclkx_coeff < 0)
+               return mclkx_coeff;
+
+       mclk_rate = cs53l30_mclkx_coeffs[mclkx_coeff].mclkx /
+                   cs53l30_mclkx_coeffs[mclkx_coeff].ratio;
+
+       regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
+                          CS53L30_MCLK_DIV_MASK,
+                          cs53l30_mclkx_coeffs[mclkx_coeff].mclkdiv);
+
+       priv->mclk_rate = mclk_rate;
+
+       return 0;
+}
+
+static int cs53l30_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct cs53l30_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+       u8 aspcfg = 0, aspctl1 = 0;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               aspcfg |= CS53L30_ASP_MS;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* DAI mode */
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               /* Set TDM_PDN to turn off TDM mode -- Reset default */
+               aspctl1 |= CS53L30_ASP_TDM_PDN;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               /*
+                * Clear TDM_PDN to turn on TDM mode; Use ASP_SCLK_INV = 0
+                * with SHIFT_LEFT = 1 combination as Figure 4-13 shows in
+                * the CS53L30 datasheet
+                */
+               aspctl1 |= CS53L30_SHIFT_LEFT;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* Check to see if the SCLK is inverted */
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_IB_NF:
+       case SND_SOC_DAIFMT_IB_IF:
+               aspcfg ^= CS53L30_ASP_SCLK_INV;
+               break;
+       default:
+               break;
+       }
+
+       regmap_update_bits(priv->regmap, CS53L30_ASPCFG_CTL,
+                          CS53L30_ASP_MS | CS53L30_ASP_SCLK_INV, aspcfg);
+
+       regmap_update_bits(priv->regmap, CS53L30_ASP_CTL1,
+                          CS53L30_ASP_TDM_PDN | CS53L30_SHIFT_LEFT, aspctl1);
+
+       return 0;
+}
+
+static int cs53l30_pcm_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *dai)
+{
+       struct cs53l30_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+       int srate = params_rate(params);
+       int mclk_coeff;
+
+       /* MCLK -> srate */
+       mclk_coeff = cs53l30_get_mclk_coeff(priv->mclk_rate, srate);
+       if (mclk_coeff < 0)
+               return -EINVAL;
+
+       regmap_update_bits(priv->regmap, CS53L30_INT_SR_CTL,
+                          CS53L30_INTRNL_FS_RATIO_MASK,
+                          cs53l30_mclk_coeffs[mclk_coeff].internal_fs_ratio);
+
+       regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
+                          CS53L30_MCLK_INT_SCALE_MASK,
+                          cs53l30_mclk_coeffs[mclk_coeff].mclk_int_scale);
+
+       regmap_update_bits(priv->regmap, CS53L30_ASPCFG_CTL,
+                          CS53L30_ASP_RATE_MASK,
+                          cs53l30_mclk_coeffs[mclk_coeff].asp_rate);
+
+       return 0;
+}
+
+static int cs53l30_set_bias_level(struct snd_soc_codec *codec,
+                                 enum snd_soc_bias_level level)
+{
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+       struct cs53l30_private *priv = snd_soc_codec_get_drvdata(codec);
+       unsigned int reg;
+       int i, inter_max_check, ret;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+       case SND_SOC_BIAS_PREPARE:
+               if (dapm->bias_level == SND_SOC_BIAS_STANDBY)
+                       regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+                                          CS53L30_PDN_LP_MASK, 0);
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               if (dapm->bias_level == SND_SOC_BIAS_OFF) {
+                       ret = clk_prepare_enable(priv->mclk);
+                       if (ret) {
+                               dev_err(codec->dev,
+                                       "failed to enable MCLK: %d\n", ret);
+                               return ret;
+                       }
+                       regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
+                                          CS53L30_MCLK_DIS_MASK, 0);
+                       regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+                                          CS53L30_PDN_ULP_MASK, 0);
+                       msleep(50);
+               } else {
+                       regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+                                          CS53L30_PDN_ULP_MASK,
+                                          CS53L30_PDN_ULP);
+               }
+               break;
+       case SND_SOC_BIAS_OFF:
+               regmap_update_bits(priv->regmap, CS53L30_INT_MASK,
+                                  CS53L30_PDN_DONE, 0);
+               /*
+                * If digital softramp is set, the amount of time required
+                * for power down increases and depends on the digital
+                * volume setting.
+                */
+
+               /* Set the max possible time if digsft is set */
+               regmap_read(priv->regmap, CS53L30_SFT_RAMP, &reg);
+               if (reg & CS53L30_DIGSFT_MASK)
+                       inter_max_check = CS53L30_PDN_POLL_MAX;
+               else
+                       inter_max_check = 10;
+
+               regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+                                  CS53L30_PDN_ULP_MASK,
+                                  CS53L30_PDN_ULP);
+               /* PDN_DONE will take a min of 20ms to be set.*/
+               msleep(20);
+               /* Clr status */
+               regmap_read(priv->regmap, CS53L30_IS, &reg);
+               for (i = 0; i < inter_max_check; i++) {
+                       if (inter_max_check < 10) {
+                               usleep_range(1000, 1100);
+                               regmap_read(priv->regmap, CS53L30_IS, &reg);
+                               if (reg & CS53L30_PDN_DONE)
+                                       break;
+                       } else {
+                               usleep_range(10000, 10100);
+                               regmap_read(priv->regmap, CS53L30_IS, &reg);
+                               if (reg & CS53L30_PDN_DONE)
+                                       break;
+                       }
+               }
+               /* PDN_DONE is set. We now can disable the MCLK */
+               regmap_update_bits(priv->regmap, CS53L30_INT_MASK,
+                                  CS53L30_PDN_DONE, CS53L30_PDN_DONE);
+               regmap_update_bits(priv->regmap, CS53L30_MCLKCTL,
+                                  CS53L30_MCLK_DIS_MASK,
+                                  CS53L30_MCLK_DIS);
+               clk_disable_unprepare(priv->mclk);
+               break;
+       }
+
+       return 0;
+}
+
+static int cs53l30_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+       struct cs53l30_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+       u8 val = tristate ? CS53L30_ASP_3ST : 0;
+
+       return regmap_update_bits(priv->regmap, CS53L30_ASP_CTL1,
+                                 CS53L30_ASP_3ST_MASK, val);
+}
+
+static unsigned int const cs53l30_src_rates[] = {
+       8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static struct snd_pcm_hw_constraint_list src_constraints = {
+       .count = ARRAY_SIZE(cs53l30_src_rates),
+       .list = cs53l30_src_rates,
+};
+
+static int cs53l30_pcm_startup(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
+{
+       snd_pcm_hw_constraint_list(substream->runtime, 0,
+                                  SNDRV_PCM_HW_PARAM_RATE, &src_constraints);
+
+       return 0;
+}
+
+/*
+ * Note: CS53L30 counts the slot number per byte while ASoC counts the slot
+ * number per slot_width. So there is a difference between the slots of ASoC
+ * and the slots of CS53L30.
+ */
+static int cs53l30_set_dai_tdm_slot(struct snd_soc_dai *dai,
+                                   unsigned int tx_mask, unsigned int rx_mask,
+                                   int slots, int slot_width)
+{
+       struct cs53l30_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+       unsigned int loc[CS53L30_TDM_SLOT_MAX] = {48, 48, 48, 48};
+       unsigned int slot_next, slot_step;
+       u64 tx_enable = 0;
+       int i;
+
+       if (!rx_mask) {
+               dev_err(dai->dev, "rx masks must not be 0\n");
+               return -EINVAL;
+       }
+
+       /* Assuming slot_width is not supposed to be greater than 64 */
+       if (slots <= 0 || slot_width <= 0 || slot_width > 64) {
+               dev_err(dai->dev, "invalid slot number or slot width\n");
+               return -EINVAL;
+       }
+
+       if (slot_width & 0x7) {
+               dev_err(dai->dev, "slot width must count in byte\n");
+               return -EINVAL;
+       }
+
+       /* How many bytes in each ASoC slot */
+       slot_step = slot_width >> 3;
+
+       for (i = 0; rx_mask && i < CS53L30_TDM_SLOT_MAX; i++) {
+               /* Find the first slot from LSB */
+               slot_next = __ffs(rx_mask);
+               /* Save the slot location by converting to CS53L30 slot */
+               loc[i] = slot_next * slot_step;
+               /* Create the mask of CS53L30 slot */
+               tx_enable |= (u64)((u64)(1 << slot_step) - 1) << (u64)loc[i];
+               /* Clear this slot from rx_mask */
+               rx_mask &= ~(1 << slot_next);
+       }
+
+       /* Error out to avoid slot shift */
+       if (rx_mask && i == CS53L30_TDM_SLOT_MAX) {
+               dev_err(dai->dev, "rx_mask exceeds max slot number: %d\n",
+                       CS53L30_TDM_SLOT_MAX);
+               return -EINVAL;
+       }
+
+       /* Validate the last active CS53L30 slot */
+       slot_next = loc[i - 1] + slot_step - 1;
+       if (slot_next > 47) {
+               dev_err(dai->dev, "slot selection out of bounds: %u\n",
+                       slot_next);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < CS53L30_TDM_SLOT_MAX && loc[i] != 48; i++) {
+               regmap_update_bits(priv->regmap, CS53L30_ASP_TDMTX_CTL(i),
+                                  CS53L30_ASP_CHx_TX_LOC_MASK, loc[i]);
+               dev_dbg(dai->dev, "loc[%d]=%x\n", i, loc[i]);
+       }
+
+       for (i = 0; i < CS53L30_ASP_TDMTX_ENx_MAX && tx_enable; i++) {
+               regmap_write(priv->regmap, CS53L30_ASP_TDMTX_ENx(i),
+                            tx_enable & 0xff);
+               tx_enable >>= 8;
+               dev_dbg(dai->dev, "en_reg=%x, tx_enable=%llx\n",
+                       CS53L30_ASP_TDMTX_ENx(i), tx_enable & 0xff);
+       }
+
+       return 0;
+}
+
+static int cs53l30_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+       struct cs53l30_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+
+       if (priv->mute_gpio)
+               gpiod_set_value_cansleep(priv->mute_gpio, mute);
+
+       return 0;
+}
+
+/* SNDRV_PCM_RATE_KNOT -> 12000, 24000 Hz, limit with constraint list */
+#define CS53L30_RATES (SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT)
+
+#define CS53L30_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+                       SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops cs53l30_ops = {
+       .startup = cs53l30_pcm_startup,
+       .hw_params = cs53l30_pcm_hw_params,
+       .set_fmt = cs53l30_set_dai_fmt,
+       .set_sysclk = cs53l30_set_sysclk,
+       .set_tristate = cs53l30_set_tristate,
+       .set_tdm_slot = cs53l30_set_dai_tdm_slot,
+       .mute_stream = cs53l30_mute_stream,
+};
+
+static struct snd_soc_dai_driver cs53l30_dai = {
+       .name = "cs53l30",
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = 4,
+               .rates = CS53L30_RATES,
+               .formats = CS53L30_FORMATS,
+       },
+       .ops = &cs53l30_ops,
+       .symmetric_rates = 1,
+};
+
+static int cs53l30_codec_probe(struct snd_soc_codec *codec)
+{
+       struct cs53l30_private *priv = snd_soc_codec_get_drvdata(codec);
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+       if (priv->use_sdout2)
+               snd_soc_dapm_add_routes(dapm, cs53l30_dapm_routes_sdout2,
+                                       ARRAY_SIZE(cs53l30_dapm_routes_sdout2));
+       else
+               snd_soc_dapm_add_routes(dapm, cs53l30_dapm_routes_sdout1,
+                                       ARRAY_SIZE(cs53l30_dapm_routes_sdout1));
+
+       return 0;
+}
+
+static struct snd_soc_codec_driver cs53l30_driver = {
+       .probe = cs53l30_codec_probe,
+       .set_bias_level = cs53l30_set_bias_level,
+       .idle_bias_off = true,
+
+       .dapm_widgets = cs53l30_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(cs53l30_dapm_widgets),
+       .dapm_routes = cs53l30_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(cs53l30_dapm_routes),
+
+       .controls = cs53l30_snd_controls,
+       .num_controls = ARRAY_SIZE(cs53l30_snd_controls),
+};
+
+static struct regmap_config cs53l30_regmap = {
+       .reg_bits = 8,
+       .val_bits = 8,
+
+       .max_register = CS53L30_MAX_REGISTER,
+       .reg_defaults = cs53l30_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(cs53l30_reg_defaults),
+       .volatile_reg = cs53l30_volatile_register,
+       .writeable_reg = cs53l30_writeable_register,
+       .readable_reg = cs53l30_readable_register,
+       .cache_type = REGCACHE_RBTREE,
+};
+
+static int cs53l30_i2c_probe(struct i2c_client *client,
+                            const struct i2c_device_id *id)
+{
+       const struct device_node *np = client->dev.of_node;
+       struct device *dev = &client->dev;
+       struct cs53l30_private *cs53l30;
+       unsigned int devid = 0;
+       unsigned int reg;
+       int ret = 0, i;
+       u8 val;
+
+       cs53l30 = devm_kzalloc(dev, sizeof(*cs53l30), GFP_KERNEL);
+       if (!cs53l30)
+               return -ENOMEM;
+
+       for (i = 0; i < ARRAY_SIZE(cs53l30->supplies); i++)
+               cs53l30->supplies[i].supply = cs53l30_supply_names[i];
+
+       ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs53l30->supplies),
+                                     cs53l30->supplies);
+       if (ret) {
+               dev_err(dev, "failed to get supplies: %d\n", ret);
+               return ret;
+       }
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(cs53l30->supplies),
+                                   cs53l30->supplies);
+       if (ret) {
+               dev_err(dev, "failed to enable supplies: %d\n", ret);
+               return ret;
+       }
+
+       /* Reset the Device */
+       cs53l30->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+                                                     GPIOD_OUT_LOW);
+       if (IS_ERR(cs53l30->reset_gpio)) {
+               ret = PTR_ERR(cs53l30->reset_gpio);
+               goto error;
+       }
+
+       if (cs53l30->reset_gpio)
+               gpiod_set_value_cansleep(cs53l30->reset_gpio, 1);
+
+       i2c_set_clientdata(client, cs53l30);
+
+       cs53l30->mclk_rate = 0;
+
+       cs53l30->regmap = devm_regmap_init_i2c(client, &cs53l30_regmap);
+       if (IS_ERR(cs53l30->regmap)) {
+               ret = PTR_ERR(cs53l30->regmap);
+               dev_err(dev, "regmap_init() failed: %d\n", ret);
+               goto error;
+       }
+
+       /* Initialize codec */
+       ret = regmap_read(cs53l30->regmap, CS53L30_DEVID_AB, &reg);
+       devid = reg << 12;
+
+       ret = regmap_read(cs53l30->regmap, CS53L30_DEVID_CD, &reg);
+       devid |= reg << 4;
+
+       ret = regmap_read(cs53l30->regmap, CS53L30_DEVID_E, &reg);
+       devid |= (reg & 0xF0) >> 4;
+
+       if (devid != CS53L30_DEVID) {
+               ret = -ENODEV;
+               dev_err(dev, "Device ID (%X). Expected %X\n",
+                       devid, CS53L30_DEVID);
+               goto error;
+       }
+
+       ret = regmap_read(cs53l30->regmap, CS53L30_REVID, &reg);
+       if (ret < 0) {
+               dev_err(dev, "failed to get Revision ID: %d\n", ret);
+               goto error;
+       }
+
+       /* Check if MCLK provided */
+       cs53l30->mclk = devm_clk_get(dev, "mclk");
+       if (IS_ERR(cs53l30->mclk)) {
+               if (PTR_ERR(cs53l30->mclk) == -EPROBE_DEFER) {
+                       ret = -EPROBE_DEFER;
+                       goto error;
+               }
+               /* Otherwise mark the mclk pointer to NULL */
+               cs53l30->mclk = NULL;
+       }
+
+       /* Fetch the MUTE control */
+       cs53l30->mute_gpio = devm_gpiod_get_optional(dev, "mute",
+                                                    GPIOD_OUT_HIGH);
+       if (IS_ERR(cs53l30->mute_gpio)) {
+               ret = PTR_ERR(cs53l30->mute_gpio);
+               goto error;
+       }
+
+       if (cs53l30->mute_gpio) {
+               /* Enable MUTE controls via MUTE pin */
+               regmap_write(cs53l30->regmap, CS53L30_MUTEP_CTL1,
+                            CS53L30_MUTEP_CTL1_MUTEALL);
+               /* Flip the polarity of MUTE pin */
+               if (gpiod_is_active_low(cs53l30->mute_gpio))
+                       regmap_update_bits(cs53l30->regmap, CS53L30_MUTEP_CTL2,
+                                          CS53L30_MUTE_PIN_POLARITY, 0);
+       }
+
+       if (!of_property_read_u8(np, "cirrus,micbias-lvl", &val))
+               regmap_update_bits(cs53l30->regmap, CS53L30_MICBIAS_CTL,
+                                  CS53L30_MIC_BIAS_CTRL_MASK, val);
+
+       if (of_property_read_bool(np, "cirrus,use-sdout2"))
+               cs53l30->use_sdout2 = true;
+
+       dev_info(dev, "Cirrus Logic CS53L30, Revision: %02X\n", reg & 0xFF);
+
+       ret = snd_soc_register_codec(dev, &cs53l30_driver, &cs53l30_dai, 1);
+       if (ret) {
+               dev_err(dev, "failed to register codec: %d\n", ret);
+               goto error;
+       }
+
+       return 0;
+
+error:
+       regulator_bulk_disable(ARRAY_SIZE(cs53l30->supplies),
+                              cs53l30->supplies);
+       return ret;
+}
+
+static int cs53l30_i2c_remove(struct i2c_client *client)
+{
+       struct cs53l30_private *cs53l30 = i2c_get_clientdata(client);
+
+       snd_soc_unregister_codec(&client->dev);
+
+       /* Hold down reset */
+       if (cs53l30->reset_gpio)
+               gpiod_set_value_cansleep(cs53l30->reset_gpio, 0);
+
+       regulator_bulk_disable(ARRAY_SIZE(cs53l30->supplies),
+                              cs53l30->supplies);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int cs53l30_runtime_suspend(struct device *dev)
+{
+       struct cs53l30_private *cs53l30 = dev_get_drvdata(dev);
+
+       regcache_cache_only(cs53l30->regmap, true);
+
+       /* Hold down reset */
+       if (cs53l30->reset_gpio)
+               gpiod_set_value_cansleep(cs53l30->reset_gpio, 0);
+
+       regulator_bulk_disable(ARRAY_SIZE(cs53l30->supplies),
+                              cs53l30->supplies);
+
+       return 0;
+}
+
+static int cs53l30_runtime_resume(struct device *dev)
+{
+       struct cs53l30_private *cs53l30 = dev_get_drvdata(dev);
+       int ret;
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(cs53l30->supplies),
+                                   cs53l30->supplies);
+       if (ret) {
+               dev_err(dev, "failed to enable supplies: %d\n", ret);
+               return ret;
+       }
+
+       if (cs53l30->reset_gpio)
+               gpiod_set_value_cansleep(cs53l30->reset_gpio, 1);
+
+       regcache_cache_only(cs53l30->regmap, false);
+       ret = regcache_sync(cs53l30->regmap);
+       if (ret) {
+               dev_err(dev, "failed to synchronize regcache: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops cs53l30_runtime_pm = {
+       SET_RUNTIME_PM_OPS(cs53l30_runtime_suspend, cs53l30_runtime_resume,
+                          NULL)
+};
+
+static const struct of_device_id cs53l30_of_match[] = {
+       { .compatible = "cirrus,cs53l30", },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, cs53l30_of_match);
+
+static const struct i2c_device_id cs53l30_id[] = {
+       { "cs53l30", 0 },
+       {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs53l30_id);
+
+static struct i2c_driver cs53l30_i2c_driver = {
+       .driver = {
+               .name = "cs53l30",
+               .pm = &cs53l30_runtime_pm,
+       },
+       .id_table = cs53l30_id,
+       .probe = cs53l30_i2c_probe,
+       .remove = cs53l30_i2c_remove,
+};
+
+module_i2c_driver(cs53l30_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS53L30 driver");
+MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <Paul.Handrigan@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs53l30.h b/sound/soc/codecs/cs53l30.h
new file mode 100644 (file)
index 0000000..5e39da5
--- /dev/null
@@ -0,0 +1,459 @@
+/*
+ * ALSA SoC CS53L30 codec driver
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <Paul.Handrigan@cirrus.com>,
+ *         Tim Howe <Tim.Howe@cirrus.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 __CS53L30_H__
+#define __CS53L30_H__
+
+/* I2C Registers */
+#define CS53L30_DEVID_AB       0x01     /* Device ID A & B [RO]. */
+#define CS53L30_DEVID_CD       0x02     /* Device ID C & D [RO]. */
+#define CS53L30_DEVID_E                0x03     /* Device ID E [RO]. */
+#define CS53L30_REVID          0x05     /* Revision ID [RO]. */
+#define CS53L30_PWRCTL         0x06     /* Power Control. */
+#define CS53L30_MCLKCTL                0x07     /* MCLK Control. */
+#define CS53L30_INT_SR_CTL     0x08     /* Internal Sample Rate Control. */
+#define CS53L30_MICBIAS_CTL    0x0A     /* Mic Bias Control. */
+#define CS53L30_ASPCFG_CTL     0x0C     /* ASP Config Control. */
+#define CS53L30_ASP_CTL1       0x0D     /* ASP1 Control. */
+#define CS53L30_ASP_TDMTX_CTL1 0x0E     /* ASP1 TDM TX Control 1 */
+#define CS53L30_ASP_TDMTX_CTL2 0x0F     /* ASP1 TDM TX Control 2 */
+#define CS53L30_ASP_TDMTX_CTL3 0x10     /* ASP1 TDM TX Control 3 */
+#define CS53L30_ASP_TDMTX_CTL4 0x11     /* ASP1 TDM TX Control 4 */
+#define CS53L30_ASP_TDMTX_EN1  0x12     /* ASP1 TDM TX Enable 1 */
+#define CS53L30_ASP_TDMTX_EN2  0x13     /* ASP1 TDM TX Enable 2 */
+#define CS53L30_ASP_TDMTX_EN3  0x14     /* ASP1 TDM TX Enable 3 */
+#define CS53L30_ASP_TDMTX_EN4  0x15     /* ASP1 TDM TX Enable 4 */
+#define CS53L30_ASP_TDMTX_EN5  0x16     /* ASP1 TDM TX Enable 5 */
+#define CS53L30_ASP_TDMTX_EN6  0x17     /* ASP1 TDM TX Enable 6 */
+#define CS53L30_ASP_CTL2       0x18     /* ASP2 Control. */
+#define CS53L30_SFT_RAMP       0x1A     /* Soft Ramp Control. */
+#define CS53L30_LRCK_CTL1      0x1B     /* LRCK Control 1. */
+#define CS53L30_LRCK_CTL2      0x1C     /* LRCK Control 2. */
+#define CS53L30_MUTEP_CTL1     0x1F     /* Mute Pin Control 1. */
+#define CS53L30_MUTEP_CTL2     0x20     /* Mute Pin Control 2. */
+#define CS53L30_INBIAS_CTL1    0x21     /* Input Bias Control 1. */
+#define CS53L30_INBIAS_CTL2    0x22     /* Input Bias Control 2. */
+#define CS53L30_DMIC1_STR_CTL   0x23     /* DMIC1 Stereo Control. */
+#define CS53L30_DMIC2_STR_CTL   0x24     /* DMIC2 Stereo Control. */
+#define CS53L30_ADCDMIC1_CTL1   0x25     /* ADC1/DMIC1 Control 1. */
+#define CS53L30_ADCDMIC1_CTL2   0x26     /* ADC1/DMIC1 Control 2. */
+#define CS53L30_ADC1_CTL3      0x27     /* ADC1 Control 3. */
+#define CS53L30_ADC1_NG_CTL    0x28     /* ADC1 Noise Gate Control. */
+#define CS53L30_ADC1A_AFE_CTL  0x29     /* ADC1A AFE Control. */
+#define CS53L30_ADC1B_AFE_CTL  0x2A     /* ADC1B AFE Control. */
+#define CS53L30_ADC1A_DIG_VOL  0x2B     /* ADC1A Digital Volume. */
+#define CS53L30_ADC1B_DIG_VOL  0x2C     /* ADC1B Digital Volume. */
+#define CS53L30_ADCDMIC2_CTL1   0x2D     /* ADC2/DMIC2 Control 1. */
+#define CS53L30_ADCDMIC2_CTL2   0x2E     /* ADC2/DMIC2 Control 2. */
+#define CS53L30_ADC2_CTL3      0x2F     /* ADC2 Control 3. */
+#define CS53L30_ADC2_NG_CTL    0x30     /* ADC2 Noise Gate Control. */
+#define CS53L30_ADC2A_AFE_CTL  0x31     /* ADC2A AFE Control. */
+#define CS53L30_ADC2B_AFE_CTL  0x32     /* ADC2B AFE Control. */
+#define CS53L30_ADC2A_DIG_VOL  0x33     /* ADC2A Digital Volume. */
+#define CS53L30_ADC2B_DIG_VOL  0x34     /* ADC2B Digital Volume. */
+#define CS53L30_INT_MASK       0x35     /* Interrupt Mask. */
+#define CS53L30_IS             0x36     /* Interrupt Status. */
+#define CS53L30_MAX_REGISTER   0x36
+
+#define CS53L30_TDM_SLOT_MAX           4
+#define CS53L30_ASP_TDMTX_CTL(x)       (CS53L30_ASP_TDMTX_CTL1 + (x))
+/* x : index for registers; n : index for slot; 8 slots per register */
+#define CS53L30_ASP_TDMTX_ENx(x)       (CS53L30_ASP_TDMTX_EN6 - (x))
+#define CS53L30_ASP_TDMTX_ENn(n)       CS53L30_ASP_TDMTX_ENx((n) >> 3)
+#define CS53L30_ASP_TDMTX_ENx_MAX      6
+
+/* Device ID */
+#define CS53L30_DEVID          0x53A30
+
+/* PDN_DONE Poll Maximum
+ * If soft ramp is set it will take much longer to power down
+ * the system.
+ */
+#define CS53L30_PDN_POLL_MAX   90
+
+/* Bitfield Definitions */
+
+/* R6 (0x06) CS53L30_PWRCTL - Power Control */
+#define CS53L30_PDN_ULP_SHIFT          7
+#define CS53L30_PDN_ULP_MASK           (1 << CS53L30_PDN_ULP_SHIFT)
+#define CS53L30_PDN_ULP                        (1 << CS53L30_PDN_ULP_SHIFT)
+#define CS53L30_PDN_LP_SHIFT           6
+#define CS53L30_PDN_LP_MASK            (1 << CS53L30_PDN_LP_SHIFT)
+#define CS53L30_PDN_LP                 (1 << CS53L30_PDN_LP_SHIFT)
+#define CS53L30_DISCHARGE_FILT_SHIFT   5
+#define CS53L30_DISCHARGE_FILT_MASK    (1 << CS53L30_DISCHARGE_FILT_SHIFT)
+#define CS53L30_DISCHARGE_FILT         (1 << CS53L30_DISCHARGE_FILT_SHIFT)
+#define CS53L30_THMS_PDN_SHIFT         4
+#define CS53L30_THMS_PDN_MASK          (1 << CS53L30_THMS_PDN_SHIFT)
+#define CS53L30_THMS_PDN               (1 << CS53L30_THMS_PDN_SHIFT)
+
+#define CS53L30_PWRCTL_DEFAULT         (CS53L30_THMS_PDN)
+
+/* R7 (0x07) CS53L30_MCLKCTL - MCLK Control */
+#define CS53L30_MCLK_DIS_SHIFT         7
+#define CS53L30_MCLK_DIS_MASK          (1 << CS53L30_MCLK_DIS_SHIFT)
+#define CS53L30_MCLK_DIS               (1 << CS53L30_MCLK_DIS_SHIFT)
+#define CS53L30_MCLK_INT_SCALE_SHIFT   6
+#define CS53L30_MCLK_INT_SCALE_MASK    (1 << CS53L30_MCLK_INT_SCALE_SHIFT)
+#define CS53L30_MCLK_INT_SCALE         (1 << CS53L30_MCLK_INT_SCALE_SHIFT)
+#define CS53L30_DMIC_DRIVE_SHIFT       5
+#define CS53L30_DMIC_DRIVE_MASK                (1 << CS53L30_DMIC_DRIVE_SHIFT)
+#define CS53L30_DMIC_DRIVE             (1 << CS53L30_DMIC_DRIVE_SHIFT)
+#define CS53L30_MCLK_DIV_SHIFT         2
+#define CS53L30_MCLK_DIV_WIDTH         2
+#define CS53L30_MCLK_DIV_MASK          (((1 << CS53L30_MCLK_DIV_WIDTH) - 1) << CS53L30_MCLK_DIV_SHIFT)
+#define CS53L30_MCLK_DIV_BY_1          (0x0 << CS53L30_MCLK_DIV_SHIFT)
+#define CS53L30_MCLK_DIV_BY_2          (0x1 << CS53L30_MCLK_DIV_SHIFT)
+#define CS53L30_MCLK_DIV_BY_3          (0x2 << CS53L30_MCLK_DIV_SHIFT)
+#define CS53L30_SYNC_EN_SHIFT          1
+#define CS53L30_SYNC_EN_MASK           (1 << CS53L30_SYNC_EN_SHIFT)
+#define CS53L30_SYNC_EN                        (1 << CS53L30_SYNC_EN_SHIFT)
+
+#define CS53L30_MCLKCTL_DEFAULT                (CS53L30_MCLK_DIV_BY_2)
+
+/* R8 (0x08) CS53L30_INT_SR_CTL - Internal Sample Rate Control */
+#define CS53L30_INTRNL_FS_RATIO_SHIFT  4
+#define CS53L30_INTRNL_FS_RATIO_MASK   (1 << CS53L30_INTRNL_FS_RATIO_SHIFT)
+#define CS53L30_INTRNL_FS_RATIO                (1 << CS53L30_INTRNL_FS_RATIO_SHIFT)
+#define CS53L30_MCLK_19MHZ_EN_SHIFT    0
+#define CS53L30_MCLK_19MHZ_EN_MASK     (1 << CS53L30_MCLK_19MHZ_EN_SHIFT)
+#define CS53L30_MCLK_19MHZ_EN          (1 << CS53L30_MCLK_19MHZ_EN_SHIFT)
+
+/* 0x6 << 1 is reserved bits */
+#define CS53L30_INT_SR_CTL_DEFAULT     (CS53L30_INTRNL_FS_RATIO | 0x6 << 1)
+
+/* R10 (0x0A) CS53L30_MICBIAS_CTL - Mic Bias Control */
+#define CS53L30_MIC4_BIAS_PDN_SHIFT    7
+#define CS53L30_MIC4_BIAS_PDN_MASK     (1 << CS53L30_MIC4_BIAS_PDN_SHIFT)
+#define CS53L30_MIC4_BIAS_PDN          (1 << CS53L30_MIC4_BIAS_PDN_SHIFT)
+#define CS53L30_MIC3_BIAS_PDN_SHIFT    6
+#define CS53L30_MIC3_BIAS_PDN_MASK     (1 << CS53L30_MIC3_BIAS_PDN_SHIFT)
+#define CS53L30_MIC3_BIAS_PDN          (1 << CS53L30_MIC3_BIAS_PDN_SHIFT)
+#define CS53L30_MIC2_BIAS_PDN_SHIFT    5
+#define CS53L30_MIC2_BIAS_PDN_MASK     (1 << CS53L30_MIC2_BIAS_PDN_SHIFT)
+#define CS53L30_MIC2_BIAS_PDN          (1 << CS53L30_MIC2_BIAS_PDN_SHIFT)
+#define CS53L30_MIC1_BIAS_PDN_SHIFT    4
+#define CS53L30_MIC1_BIAS_PDN_MASK     (1 << CS53L30_MIC1_BIAS_PDN_SHIFT)
+#define CS53L30_MIC1_BIAS_PDN          (1 << CS53L30_MIC1_BIAS_PDN_SHIFT)
+#define CS53L30_MICx_BIAS_PDN          (0xf << CS53L30_MIC1_BIAS_PDN_SHIFT)
+#define CS53L30_VP_MIN_SHIFT           2
+#define CS53L30_VP_MIN_MASK            (1 << CS53L30_VP_MIN_SHIFT)
+#define CS53L30_VP_MIN                 (1 << CS53L30_VP_MIN_SHIFT)
+#define CS53L30_MIC_BIAS_CTRL_SHIFT    0
+#define CS53L30_MIC_BIAS_CTRL_WIDTH    2
+#define CS53L30_MIC_BIAS_CTRL_MASK     (((1 << CS53L30_MIC_BIAS_CTRL_WIDTH) - 1) << CS53L30_MIC_BIAS_CTRL_SHIFT)
+#define CS53L30_MIC_BIAS_CTRL_HIZ      (0 << CS53L30_MIC_BIAS_CTRL_SHIFT)
+#define CS53L30_MIC_BIAS_CTRL_1V8      (1 << CS53L30_MIC_BIAS_CTRL_SHIFT)
+#define CS53L30_MIC_BIAS_CTRL_2V75     (2 << CS53L30_MIC_BIAS_CTRL_SHIFT)
+
+#define CS53L30_MICBIAS_CTL_DEFAULT    (CS53L30_MICx_BIAS_PDN | CS53L30_VP_MIN)
+
+/* R12 (0x0C) CS53L30_ASPCFG_CTL - ASP Configuration Control */
+#define CS53L30_ASP_MS_SHIFT           7
+#define CS53L30_ASP_MS_MASK            (1 << CS53L30_ASP_MS_SHIFT)
+#define CS53L30_ASP_MS                 (1 << CS53L30_ASP_MS_SHIFT)
+#define CS53L30_ASP_SCLK_INV_SHIFT     4
+#define CS53L30_ASP_SCLK_INV_MASK      (1 << CS53L30_ASP_SCLK_INV_SHIFT)
+#define CS53L30_ASP_SCLK_INV           (1 << CS53L30_ASP_SCLK_INV_SHIFT)
+#define CS53L30_ASP_RATE_SHIFT         0
+#define CS53L30_ASP_RATE_WIDTH         4
+#define CS53L30_ASP_RATE_MASK          (((1 << CS53L30_ASP_RATE_WIDTH) - 1) << CS53L30_ASP_RATE_SHIFT)
+#define CS53L30_ASP_RATE_48K           (0xc << CS53L30_ASP_RATE_SHIFT)
+
+#define CS53L30_ASPCFG_CTL_DEFAULT     (CS53L30_ASP_RATE_48K)
+
+/* R13/R24 (0x0D/0x18) CS53L30_ASP_CTL1 & CS53L30_ASP_CTL2 - ASP Control 1~2 */
+#define CS53L30_ASP_TDM_PDN_SHIFT      7
+#define CS53L30_ASP_TDM_PDN_MASK       (1 << CS53L30_ASP_TDM_PDN_SHIFT)
+#define CS53L30_ASP_TDM_PDN            (1 << CS53L30_ASP_TDM_PDN_SHIFT)
+#define CS53L30_ASP_SDOUTx_PDN_SHIFT   6
+#define CS53L30_ASP_SDOUTx_PDN_MASK    (1 << CS53L30_ASP_SDOUTx_PDN_SHIFT)
+#define CS53L30_ASP_SDOUTx_PDN         (1 << CS53L30_ASP_SDOUTx_PDN_SHIFT)
+#define CS53L30_ASP_3ST_SHIFT          5
+#define CS53L30_ASP_3ST_MASK           (1 << CS53L30_ASP_3ST_SHIFT)
+#define CS53L30_ASP_3ST                        (1 << CS53L30_ASP_3ST_SHIFT)
+#define CS53L30_SHIFT_LEFT_SHIFT       4
+#define CS53L30_SHIFT_LEFT_MASK                (1 << CS53L30_SHIFT_LEFT_SHIFT)
+#define CS53L30_SHIFT_LEFT             (1 << CS53L30_SHIFT_LEFT_SHIFT)
+#define CS53L30_ASP_SDOUTx_DRIVE_SHIFT 0
+#define CS53L30_ASP_SDOUTx_DRIVE_MASK  (1 << CS53L30_ASP_SDOUTx_DRIVE_SHIFT)
+#define CS53L30_ASP_SDOUTx_DRIVE       (1 << CS53L30_ASP_SDOUTx_DRIVE_SHIFT)
+
+#define CS53L30_ASP_CTL1_DEFAULT       (CS53L30_ASP_TDM_PDN)
+#define CS53L30_ASP_CTL2_DEFAULT       (0)
+
+/* R14 (0x0E) ~ R17 (0x11) CS53L30_ASP_TDMTX_CTLx - ASP TDM TX Control 1~4 */
+#define CS53L30_ASP_CHx_TX_STATE_SHIFT 7
+#define CS53L30_ASP_CHx_TX_STATE_MASK  (1 << CS53L30_ASP_CHx_TX_STATE_SHIFT)
+#define CS53L30_ASP_CHx_TX_STATE       (1 << CS53L30_ASP_CHx_TX_STATE_SHIFT)
+#define CS53L30_ASP_CHx_TX_LOC_SHIFT   0
+#define CS53L30_ASP_CHx_TX_LOC_WIDTH   6
+#define CS53L30_ASP_CHx_TX_LOC_MASK    (((1 << CS53L30_ASP_CHx_TX_LOC_WIDTH) - 1) << CS53L30_ASP_CHx_TX_LOC_SHIFT)
+#define CS53L30_ASP_CHx_TX_LOC_MAX     (47 << CS53L30_ASP_CHx_TX_LOC_SHIFT)
+#define CS53L30_ASP_CHx_TX_LOC(x)      ((x) << CS53L30_ASP_CHx_TX_LOC_SHIFT)
+
+#define CS53L30_ASP_TDMTX_CTLx_DEFAULT (CS53L30_ASP_CHx_TX_LOC_MAX)
+
+/* R18 (0x12) ~ R23 (0x17) CS53L30_ASP_TDMTX_ENx - ASP TDM TX Enable 1~6 */
+#define CS53L30_ASP_TDMTX_ENx_DEFAULT  (0)
+
+/* R26 (0x1A) CS53L30_SFT_RAMP - Soft Ramp Control */
+#define CS53L30_DIGSFT_SHIFT           5
+#define CS53L30_DIGSFT_MASK            (1 << CS53L30_DIGSFT_SHIFT)
+#define CS53L30_DIGSFT                 (1 << CS53L30_DIGSFT_SHIFT)
+
+#define CS53L30_SFT_RMP_DEFAULT                (0)
+
+/* R28 (0x1C) CS53L30_LRCK_CTL2 - LRCK Control 2 */
+#define CS53L30_LRCK_50_NPW_SHIFT      3
+#define CS53L30_LRCK_50_NPW_MASK       (1 << CS53L30_LRCK_50_NPW_SHIFT)
+#define CS53L30_LRCK_50_NPW            (1 << CS53L30_LRCK_50_NPW_SHIFT)
+#define CS53L30_LRCK_TPWH_SHIFT                0
+#define CS53L30_LRCK_TPWH_WIDTH                3
+#define CS53L30_LRCK_TPWH_MASK         (((1 << CS53L30_LRCK_TPWH_WIDTH) - 1) << CS53L30_LRCK_TPWH_SHIFT)
+#define CS53L30_LRCK_TPWH(x)           (((x) << CS53L30_LRCK_TPWH_SHIFT) & CS53L30_LRCK_TPWH_MASK)
+
+#define CS53L30_LRCK_CTLx_DEFAULT      (0)
+
+/* R31 (0x1F) CS53L30_MUTEP_CTL1 - MUTE Pin Control 1 */
+#define CS53L30_MUTE_PDN_ULP_SHIFT     7
+#define CS53L30_MUTE_PDN_ULP_MASK      (1 << CS53L30_MUTE_PDN_ULP_SHIFT)
+#define CS53L30_MUTE_PDN_ULP           (1 << CS53L30_MUTE_PDN_ULP_SHIFT)
+#define CS53L30_MUTE_PDN_LP_SHIFT      6
+#define CS53L30_MUTE_PDN_LP_MASK       (1 << CS53L30_MUTE_PDN_LP_SHIFT)
+#define CS53L30_MUTE_PDN_LP            (1 << CS53L30_MUTE_PDN_LP_SHIFT)
+#define CS53L30_MUTE_M4B_PDN_SHIFT     4
+#define CS53L30_MUTE_M4B_PDN_MASK      (1 << CS53L30_MUTE_M4B_PDN_SHIFT)
+#define CS53L30_MUTE_M4B_PDN           (1 << CS53L30_MUTE_M4B_PDN_SHIFT)
+#define CS53L30_MUTE_M3B_PDN_SHIFT     3
+#define CS53L30_MUTE_M3B_PDN_MASK      (1 << CS53L30_MUTE_M3B_PDN_SHIFT)
+#define CS53L30_MUTE_M3B_PDN           (1 << CS53L30_MUTE_M3B_PDN_SHIFT)
+#define CS53L30_MUTE_M2B_PDN_SHIFT     2
+#define CS53L30_MUTE_M2B_PDN_MASK      (1 << CS53L30_MUTE_M2B_PDN_SHIFT)
+#define CS53L30_MUTE_M2B_PDN           (1 << CS53L30_MUTE_M2B_PDN_SHIFT)
+#define CS53L30_MUTE_M1B_PDN_SHIFT     1
+#define CS53L30_MUTE_M1B_PDN_MASK      (1 << CS53L30_MUTE_M1B_PDN_SHIFT)
+#define CS53L30_MUTE_M1B_PDN           (1 << CS53L30_MUTE_M1B_PDN_SHIFT)
+/* Note: be careful - x starts from 0 */
+#define CS53L30_MUTE_MxB_PDN_SHIFT(x)  (CS53L30_MUTE_M1B_PDN_SHIFT + (x))
+#define CS53L30_MUTE_MxB_PDN_MASK(x)   (1 << CS53L30_MUTE_MxB_PDN_SHIFT(x))
+#define CS53L30_MUTE_MxB_PDN(x)                (1 << CS53L30_MUTE_MxB_PDN_SHIFT(x))
+#define CS53L30_MUTE_MB_ALL_PDN_SHIFT  0
+#define CS53L30_MUTE_MB_ALL_PDN_MASK   (1 << CS53L30_MUTE_MB_ALL_PDN_SHIFT)
+#define CS53L30_MUTE_MB_ALL_PDN                (1 << CS53L30_MUTE_MB_ALL_PDN_SHIFT)
+
+#define CS53L30_MUTEP_CTL1_MUTEALL     (0xdf)
+#define CS53L30_MUTEP_CTL1_DEFAULT     (0)
+
+/* R32 (0x20) CS53L30_MUTEP_CTL2 - MUTE Pin Control 2 */
+#define CS53L30_MUTE_PIN_POLARITY_SHIFT        7
+#define CS53L30_MUTE_PIN_POLARITY_MASK (1 << CS53L30_MUTE_PIN_POLARITY_SHIFT)
+#define CS53L30_MUTE_PIN_POLARITY      (1 << CS53L30_MUTE_PIN_POLARITY_SHIFT)
+#define CS53L30_MUTE_ASP_TDM_PDN_SHIFT 6
+#define CS53L30_MUTE_ASP_TDM_PDN_MASK  (1 << CS53L30_MUTE_ASP_TDM_PDN_SHIFT)
+#define CS53L30_MUTE_ASP_TDM_PDN       (1 << CS53L30_MUTE_ASP_TDM_PDN_SHIFT)
+#define CS53L30_MUTE_ASP_SDOUT2_PDN_SHIFT 5
+#define CS53L30_MUTE_ASP_SDOUT2_PDN_MASK (1 << CS53L30_MUTE_ASP_SDOUT2_PDN_SHIFT)
+#define CS53L30_MUTE_ASP_SDOUT2_PDN    (1 << CS53L30_MUTE_ASP_SDOUT2_PDN_SHIFT)
+#define CS53L30_MUTE_ASP_SDOUT1_PDN_SHIFT 4
+#define CS53L30_MUTE_ASP_SDOUT1_PDN_MASK (1 << CS53L30_MUTE_ASP_SDOUT1_PDN_SHIFT)
+#define CS53L30_MUTE_ASP_SDOUT1_PDN    (1 << CS53L30_MUTE_ASP_SDOUT1_PDN_SHIFT)
+/* Note: be careful - x starts from 0 */
+#define CS53L30_MUTE_ASP_SDOUTx_PDN_SHIFT(x) ((x) + CS53L30_MUTE_ASP_SDOUT1_PDN_SHIFT)
+#define CS53L30_MUTE_ASP_SDOUTx_PDN_MASK(x) (1 << CS53L30_MUTE_ASP_SDOUTx_PDN_SHIFT(x))
+#define CS53L30_MUTE_ASP_SDOUTx_PDN    (1 << CS53L30_MUTE_ASP_SDOUTx_PDN_SHIFT(x))
+#define CS53L30_MUTE_ADC2B_PDN_SHIFT   3
+#define CS53L30_MUTE_ADC2B_PDN_MASK    (1 << CS53L30_MUTE_ADC2B_PDN_SHIFT)
+#define CS53L30_MUTE_ADC2B_PDN         (1 << CS53L30_MUTE_ADC2B_PDN_SHIFT)
+#define CS53L30_MUTE_ADC2A_PDN_SHIFT   2
+#define CS53L30_MUTE_ADC2A_PDN_MASK    (1 << CS53L30_MUTE_ADC2A_PDN_SHIFT)
+#define CS53L30_MUTE_ADC2A_PDN         (1 << CS53L30_MUTE_ADC2A_PDN_SHIFT)
+#define CS53L30_MUTE_ADC1B_PDN_SHIFT   1
+#define CS53L30_MUTE_ADC1B_PDN_MASK    (1 << CS53L30_MUTE_ADC1B_PDN_SHIFT)
+#define CS53L30_MUTE_ADC1B_PDN         (1 << CS53L30_MUTE_ADC1B_PDN_SHIFT)
+#define CS53L30_MUTE_ADC1A_PDN_SHIFT   0
+#define CS53L30_MUTE_ADC1A_PDN_MASK    (1 << CS53L30_MUTE_ADC1A_PDN_SHIFT)
+#define CS53L30_MUTE_ADC1A_PDN         (1 << CS53L30_MUTE_ADC1A_PDN_SHIFT)
+
+#define CS53L30_MUTEP_CTL2_DEFAULT     (CS53L30_MUTE_PIN_POLARITY)
+
+/* R33 (0x21) CS53L30_INBIAS_CTL1 - Input Bias Control 1 */
+#define CS53L30_IN4M_BIAS_SHIFT                6
+#define CS53L30_IN4M_BIAS_WIDTH                2
+#define CS53L30_IN4M_BIAS_MASK         (((1 << CS53L30_IN4M_BIAS_WIDTH) - 1) << CS53L30_IN4M_BIAS_SHIFT)
+#define CS53L30_IN4M_BIAS_OPEN         (0 << CS53L30_IN4M_BIAS_SHIFT)
+#define CS53L30_IN4M_BIAS_PULL_DOWN    (1 << CS53L30_IN4M_BIAS_SHIFT)
+#define CS53L30_IN4M_BIAS_VCM          (2 << CS53L30_IN4M_BIAS_SHIFT)
+#define CS53L30_IN4P_BIAS_SHIFT                4
+#define CS53L30_IN4P_BIAS_WIDTH                2
+#define CS53L30_IN4P_BIAS_MASK         (((1 << CS53L30_IN4P_BIAS_WIDTH) - 1) << CS53L30_IN4P_BIAS_SHIFT)
+#define CS53L30_IN4P_BIAS_OPEN         (0 << CS53L30_IN4P_BIAS_SHIFT)
+#define CS53L30_IN4P_BIAS_PULL_DOWN    (1 << CS53L30_IN4P_BIAS_SHIFT)
+#define CS53L30_IN4P_BIAS_VCM          (2 << CS53L30_IN4P_BIAS_SHIFT)
+#define CS53L30_IN3M_BIAS_SHIFT                2
+#define CS53L30_IN3M_BIAS_WIDTH                2
+#define CS53L30_IN3M_BIAS_MASK         (((1 << CS53L30_IN3M_BIAS_WIDTH) - 1) << CS53L30_IN4M_BIAS_SHIFT)
+#define CS53L30_IN3M_BIAS_OPEN         (0 << CS53L30_IN3M_BIAS_SHIFT)
+#define CS53L30_IN3M_BIAS_PULL_DOWN    (1 << CS53L30_IN3M_BIAS_SHIFT)
+#define CS53L30_IN3M_BIAS_VCM          (2 << CS53L30_IN3M_BIAS_SHIFT)
+#define CS53L30_IN3P_BIAS_SHIFT                0
+#define CS53L30_IN3P_BIAS_WIDTH                2
+#define CS53L30_IN3P_BIAS_MASK         (((1 << CS53L30_IN3P_BIAS_WIDTH) - 1) << CS53L30_IN3P_BIAS_SHIFT)
+#define CS53L30_IN3P_BIAS_OPEN         (0 << CS53L30_IN3P_BIAS_SHIFT)
+#define CS53L30_IN3P_BIAS_PULL_DOWN    (1 << CS53L30_IN3P_BIAS_SHIFT)
+#define CS53L30_IN3P_BIAS_VCM          (2 << CS53L30_IN3P_BIAS_SHIFT)
+
+#define CS53L30_INBIAS_CTL1_DEFAULT    (CS53L30_IN4M_BIAS_VCM | CS53L30_IN4P_BIAS_VCM |\
+                                        CS53L30_IN3M_BIAS_VCM | CS53L30_IN3P_BIAS_VCM)
+
+/* R34 (0x22) CS53L30_INBIAS_CTL2 - Input Bias Control 2 */
+#define CS53L30_IN2M_BIAS_SHIFT                6
+#define CS53L30_IN2M_BIAS_WIDTH                2
+#define CS53L30_IN2M_BIAS_MASK         (((1 << CS53L30_IN2M_BIAS_WIDTH) - 1) << CS53L30_IN2M_BIAS_SHIFT)
+#define CS53L30_IN2M_BIAS_OPEN         (0 << CS53L30_IN2M_BIAS_SHIFT)
+#define CS53L30_IN2M_BIAS_PULL_DOWN    (1 << CS53L30_IN2M_BIAS_SHIFT)
+#define CS53L30_IN2M_BIAS_VCM          (2 << CS53L30_IN2M_BIAS_SHIFT)
+#define CS53L30_IN2P_BIAS_SHIFT                4
+#define CS53L30_IN2P_BIAS_WIDTH                2
+#define CS53L30_IN2P_BIAS_MASK         (((1 << CS53L30_IN2P_BIAS_WIDTH) - 1) << CS53L30_IN2P_BIAS_SHIFT)
+#define CS53L30_IN2P_BIAS_OPEN         (0 << CS53L30_IN2P_BIAS_SHIFT)
+#define CS53L30_IN2P_BIAS_PULL_DOWN    (1 << CS53L30_IN2P_BIAS_SHIFT)
+#define CS53L30_IN2P_BIAS_VCM          (2 << CS53L30_IN2P_BIAS_SHIFT)
+#define CS53L30_IN1M_BIAS_SHIFT                2
+#define CS53L30_IN1M_BIAS_WIDTH                2
+#define CS53L30_IN1M_BIAS_MASK         (((1 << CS53L30_IN1M_BIAS_WIDTH) - 1) << CS53L30_IN1M_BIAS_SHIFT)
+#define CS53L30_IN1M_BIAS_OPEN         (0 << CS53L30_IN1M_BIAS_SHIFT)
+#define CS53L30_IN1M_BIAS_PULL_DOWN    (1 << CS53L30_IN1M_BIAS_SHIFT)
+#define CS53L30_IN1M_BIAS_VCM          (2 << CS53L30_IN1M_BIAS_SHIFT)
+#define CS53L30_IN1P_BIAS_SHIFT                0
+#define CS53L30_IN1P_BIAS_WIDTH                2
+#define CS53L30_IN1P_BIAS_MASK         (((1 << CS53L30_IN1P_BIAS_WIDTH) - 1) << CS53L30_IN1P_BIAS_SHIFT)
+#define CS53L30_IN1P_BIAS_OPEN         (0 << CS53L30_IN1P_BIAS_SHIFT)
+#define CS53L30_IN1P_BIAS_PULL_DOWN    (1 << CS53L30_IN1P_BIAS_SHIFT)
+#define CS53L30_IN1P_BIAS_VCM          (2 << CS53L30_IN1P_BIAS_SHIFT)
+
+#define CS53L30_INBIAS_CTL2_DEFAULT    (CS53L30_IN2M_BIAS_VCM | CS53L30_IN2P_BIAS_VCM |\
+                                        CS53L30_IN1M_BIAS_VCM | CS53L30_IN1P_BIAS_VCM)
+
+/* R35 (0x23) & R36 (0x24) CS53L30_DMICx_STR_CTL - DMIC1 & DMIC2 Stereo Control */
+#define CS53L30_DMICx_STEREO_ENB_SHIFT 5
+#define CS53L30_DMICx_STEREO_ENB_MASK  (1 << CS53L30_DMICx_STEREO_ENB_SHIFT)
+#define CS53L30_DMICx_STEREO_ENB       (1 << CS53L30_DMICx_STEREO_ENB_SHIFT)
+
+/* 0x88 and 0xCC are reserved bits */
+#define CS53L30_DMIC1_STR_CTL_DEFAULT  (CS53L30_DMICx_STEREO_ENB | 0x88)
+#define CS53L30_DMIC2_STR_CTL_DEFAULT  (CS53L30_DMICx_STEREO_ENB | 0xCC)
+
+/* R37/R45 (0x25/0x2D) CS53L30_ADCDMICx_CTL1 - ADC1/DMIC1 & ADC2/DMIC2 Control 1 */
+#define CS53L30_ADCxB_PDN_SHIFT                7
+#define CS53L30_ADCxB_PDN_MASK         (1 << CS53L30_ADCxB_PDN_SHIFT)
+#define CS53L30_ADCxB_PDN              (1 << CS53L30_ADCxB_PDN_SHIFT)
+#define CS53L30_ADCxA_PDN_SHIFT                6
+#define CS53L30_ADCxA_PDN_MASK         (1 << CS53L30_ADCxA_PDN_SHIFT)
+#define CS53L30_ADCxA_PDN              (1 << CS53L30_ADCxA_PDN_SHIFT)
+#define CS53L30_DMICx_PDN_SHIFT                2
+#define CS53L30_DMICx_PDN_MASK         (1 << CS53L30_DMICx_PDN_SHIFT)
+#define CS53L30_DMICx_PDN              (1 << CS53L30_DMICx_PDN_SHIFT)
+#define CS53L30_DMICx_SCLK_DIV_SHIFT   1
+#define CS53L30_DMICx_SCLK_DIV_MASK    (1 << CS53L30_DMICx_SCLK_DIV_SHIFT)
+#define CS53L30_DMICx_SCLK_DIV         (1 << CS53L30_DMICx_SCLK_DIV_SHIFT)
+#define CS53L30_CH_TYPE_SHIFT          0
+#define CS53L30_CH_TYPE_MASK           (1 << CS53L30_CH_TYPE_SHIFT)
+#define CS53L30_CH_TYPE                        (1 << CS53L30_CH_TYPE_SHIFT)
+
+#define CS53L30_ADCDMICx_PDN_MASK      0xFF
+#define CS53L30_ADCDMICx_CTL1_DEFAULT  (CS53L30_DMICx_PDN)
+
+/* R38/R46 (0x26/0x2E) CS53L30_ADCDMICx_CTL2 - ADC1/DMIC1 & ADC2/DMIC2 Control 2 */
+#define CS53L30_ADCx_NOTCH_DIS_SHIFT   7
+#define CS53L30_ADCx_NOTCH_DIS_MASK    (1 << CS53L30_ADCx_NOTCH_DIS_SHIFT)
+#define CS53L30_ADCx_NOTCH_DIS         (1 << CS53L30_ADCx_NOTCH_DIS_SHIFT)
+#define CS53L30_ADCxB_INV_SHIFT                5
+#define CS53L30_ADCxB_INV_MASK         (1 << CS53L30_ADCxB_INV_SHIFT)
+#define CS53L30_ADCxB_INV              (1 << CS53L30_ADCxB_INV_SHIFT)
+#define CS53L30_ADCxA_INV_SHIFT                4
+#define CS53L30_ADCxA_INV_MASK         (1 << CS53L30_ADCxA_INV_SHIFT)
+#define CS53L30_ADCxA_INV              (1 << CS53L30_ADCxA_INV_SHIFT)
+#define CS53L30_ADCxB_DIG_BOOST_SHIFT  1
+#define CS53L30_ADCxB_DIG_BOOST_MASK   (1 << CS53L30_ADCxB_DIG_BOOST_SHIFT)
+#define CS53L30_ADCxB_DIG_BOOST                (1 << CS53L30_ADCxB_DIG_BOOST_SHIFT)
+#define CS53L30_ADCxA_DIG_BOOST_SHIFT  0
+#define CS53L30_ADCxA_DIG_BOOST_MASK   (1 << CS53L30_ADCxA_DIG_BOOST_SHIFT)
+#define CS53L30_ADCxA_DIG_BOOST                (1 << CS53L30_ADCxA_DIG_BOOST_SHIFT)
+
+#define CS53L30_ADCDMIC1_CTL2_DEFAULT  (0)
+
+/* R39/R47 (0x27/0x2F) CS53L30_ADCx_CTL3 - ADC1/ADC2 Control 3 */
+#define CS53L30_ADCx_HPF_EN_SHIFT      3
+#define CS53L30_ADCx_HPF_EN_MASK       (1 << CS53L30_ADCx_HPF_EN_SHIFT)
+#define CS53L30_ADCx_HPF_EN            (1 << CS53L30_ADCx_HPF_EN_SHIFT)
+#define CS53L30_ADCx_HPF_CF_SHIFT      1
+#define CS53L30_ADCx_HPF_CF_WIDTH      2
+#define CS53L30_ADCx_HPF_CF_MASK       (((1 << CS53L30_ADCx_HPF_CF_WIDTH) - 1) << CS53L30_ADCx_HPF_CF_SHIFT)
+#define CS53L30_ADCx_HPF_CF_1HZ86      (0 << CS53L30_ADCx_HPF_CF_SHIFT)
+#define CS53L30_ADCx_HPF_CF_120HZ      (1 << CS53L30_ADCx_HPF_CF_SHIFT)
+#define CS53L30_ADCx_HPF_CF_235HZ      (2 << CS53L30_ADCx_HPF_CF_SHIFT)
+#define CS53L30_ADCx_HPF_CF_466HZ      (3 << CS53L30_ADCx_HPF_CF_SHIFT)
+#define CS53L30_ADCx_NG_ALL_SHIFT      0
+#define CS53L30_ADCx_NG_ALL_MASK       (1 << CS53L30_ADCx_NG_ALL_SHIFT)
+#define CS53L30_ADCx_NG_ALL            (1 << CS53L30_ADCx_NG_ALL_SHIFT)
+
+#define CS53L30_ADCx_CTL3_DEFAULT      (CS53L30_ADCx_HPF_EN)
+
+/* R40/R48 (0x28/0x30) CS53L30_ADCx_NG_CTL - ADC1/ADC2 Noise Gate Control */
+#define CS53L30_ADCxB_NG_SHIFT         7
+#define CS53L30_ADCxB_NG_MASK          (1 << CS53L30_ADCxB_NG_SHIFT)
+#define CS53L30_ADCxB_NG               (1 << CS53L30_ADCxB_NG_SHIFT)
+#define CS53L30_ADCxA_NG_SHIFT         6
+#define CS53L30_ADCxA_NG_MASK          (1 << CS53L30_ADCxA_NG_SHIFT)
+#define CS53L30_ADCxA_NG               (1 << CS53L30_ADCxA_NG_SHIFT)
+#define CS53L30_ADCx_NG_BOOST_SHIFT    5
+#define CS53L30_ADCx_NG_BOOST_MASK     (1 << CS53L30_ADCx_NG_BOOST_SHIFT)
+#define CS53L30_ADCx_NG_BOOST          (1 << CS53L30_ADCx_NG_BOOST_SHIFT)
+#define CS53L30_ADCx_NG_THRESH_SHIFT   2
+#define CS53L30_ADCx_NG_THRESH_WIDTH   3
+#define CS53L30_ADCx_NG_THRESH_MASK    (((1 << CS53L30_ADCx_NG_THRESH_WIDTH) - 1) << CS53L30_ADCx_NG_THRESH_SHIFT)
+#define CS53L30_ADCx_NG_DELAY_SHIFT    0
+#define CS53L30_ADCx_NG_DELAY_WIDTH    2
+#define CS53L30_ADCx_NG_DELAY_MASK     (((1 << CS53L30_ADCx_NG_DELAY_WIDTH) - 1) << CS53L30_ADCx_NG_DELAY_SHIFT)
+
+#define CS53L30_ADCx_NG_CTL_DEFAULT    (0)
+
+/* R41/R42/R49/R50 (0x29/0x2A/0x31/0x32) CS53L30_ADCxy_AFE_CTL - ADC1A/1B/2A/2B AFE Control */
+#define CS53L30_ADCxy_PREAMP_SHIFT     6
+#define CS53L30_ADCxy_PREAMP_WIDTH     2
+#define CS53L30_ADCxy_PREAMP_MASK      (((1 << CS53L30_ADCxy_PREAMP_WIDTH) - 1) << CS53L30_ADCxy_PREAMP_SHIFT)
+#define CS53L30_ADCxy_PGA_VOL_SHIFT    0
+#define CS53L30_ADCxy_PGA_VOL_WIDTH    6
+#define CS53L30_ADCxy_PGA_VOL_MASK     (((1 << CS53L30_ADCxy_PGA_VOL_WIDTH) - 1) << CS53L30_ADCxy_PGA_VOL_SHIFT)
+
+#define CS53L30_ADCxy_AFE_CTL_DEFAULT  (0)
+
+/* R43/R44/R51/R52 (0x2B/0x2C/0x33/0x34) CS53L30_ADCxy_DIG_VOL - ADC1A/1B/2A/2B Digital Volume */
+#define CS53L30_ADCxy_VOL_MUTE         (0x80)
+
+#define CS53L30_ADCxy_DIG_VOL_DEFAULT  (0x0)
+
+/* CS53L30_INT */
+#define CS53L30_PDN_DONE               (1 << 7)
+#define CS53L30_THMS_TRIP              (1 << 6)
+#define CS53L30_SYNC_DONE              (1 << 5)
+#define CS53L30_ADC2B_OVFL             (1 << 4)
+#define CS53L30_ADC2A_OVFL             (1 << 3)
+#define CS53L30_ADC1B_OVFL             (1 << 2)
+#define CS53L30_ADC1A_OVFL             (1 << 1)
+#define CS53L30_MUTE_PIN               (1 << 0)
+#define CS53L30_DEVICE_INT_MASK                0xFF
+
+#endif /* __CS53L30_H__ */
index 9459593eef13d5eb0e56c88a66fa390c0cdf9a53..f0057cd223a4e2dfa23e2acd23d6154cd9ec993f 100644 (file)
@@ -13,8 +13,8 @@
 
 #include <linux/module.h>
 #include <linux/platform_device.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
+#include <linux/i2c.h>
+#include <linux/property.h>
 #include <linux/pm_wakeirq.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
@@ -382,11 +382,11 @@ static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
 }
 
 /*
- * DT to pdata conversion
+ * DT/ACPI to pdata conversion
  */
 
 static enum da7219_aad_micbias_pulse_lvl
-       da7219_aad_of_micbias_pulse_lvl(struct snd_soc_codec *codec, u32 val)
+       da7219_aad_fw_micbias_pulse_lvl(struct snd_soc_codec *codec, u32 val)
 {
        switch (val) {
        case 2800:
@@ -400,7 +400,7 @@ static enum da7219_aad_micbias_pulse_lvl
 }
 
 static enum da7219_aad_btn_cfg
-       da7219_aad_of_btn_cfg(struct snd_soc_codec *codec, u32 val)
+       da7219_aad_fw_btn_cfg(struct snd_soc_codec *codec, u32 val)
 {
        switch (val) {
        case 2:
@@ -424,7 +424,7 @@ static enum da7219_aad_btn_cfg
 }
 
 static enum da7219_aad_mic_det_thr
-       da7219_aad_of_mic_det_thr(struct snd_soc_codec *codec, u32 val)
+       da7219_aad_fw_mic_det_thr(struct snd_soc_codec *codec, u32 val)
 {
        switch (val) {
        case 200:
@@ -442,7 +442,7 @@ static enum da7219_aad_mic_det_thr
 }
 
 static enum da7219_aad_jack_ins_deb
-       da7219_aad_of_jack_ins_deb(struct snd_soc_codec *codec, u32 val)
+       da7219_aad_fw_jack_ins_deb(struct snd_soc_codec *codec, u32 val)
 {
        switch (val) {
        case 5:
@@ -468,7 +468,7 @@ static enum da7219_aad_jack_ins_deb
 }
 
 static enum da7219_aad_jack_det_rate
-       da7219_aad_of_jack_det_rate(struct snd_soc_codec *codec, const char *str)
+       da7219_aad_fw_jack_det_rate(struct snd_soc_codec *codec, const char *str)
 {
        if (!strcmp(str, "32ms_64ms")) {
                return DA7219_AAD_JACK_DET_RATE_32_64MS;
@@ -485,7 +485,7 @@ static enum da7219_aad_jack_det_rate
 }
 
 static enum da7219_aad_jack_rem_deb
-       da7219_aad_of_jack_rem_deb(struct snd_soc_codec *codec, u32 val)
+       da7219_aad_fw_jack_rem_deb(struct snd_soc_codec *codec, u32 val)
 {
        switch (val) {
        case 1:
@@ -503,7 +503,7 @@ static enum da7219_aad_jack_rem_deb
 }
 
 static enum da7219_aad_btn_avg
-       da7219_aad_of_btn_avg(struct snd_soc_codec *codec, u32 val)
+       da7219_aad_fw_btn_avg(struct snd_soc_codec *codec, u32 val)
 {
        switch (val) {
        case 1:
@@ -521,7 +521,7 @@ static enum da7219_aad_btn_avg
 }
 
 static enum da7219_aad_adc_1bit_rpt
-       da7219_aad_of_adc_1bit_rpt(struct snd_soc_codec *codec, u32 val)
+       da7219_aad_fw_adc_1bit_rpt(struct snd_soc_codec *codec, u32 val)
 {
        switch (val) {
        case 1:
@@ -538,97 +538,96 @@ static enum da7219_aad_adc_1bit_rpt
        }
 }
 
-static struct da7219_aad_pdata *da7219_aad_of_to_pdata(struct snd_soc_codec *codec)
+static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct snd_soc_codec *codec)
 {
-       struct device_node *np = codec->dev->of_node;
-       struct device_node *aad_np = of_find_node_by_name(np, "da7219_aad");
+       struct device *dev = codec->dev;
+       struct i2c_client *i2c = to_i2c_client(dev);
+       struct fwnode_handle *aad_np;
        struct da7219_aad_pdata *aad_pdata;
-       const char *of_str;
-       u32 of_val32;
+       const char *fw_str;
+       u32 fw_val32;
 
+       aad_np = device_get_named_child_node(dev, "da7219_aad");
        if (!aad_np)
                return NULL;
 
        aad_pdata = devm_kzalloc(codec->dev, sizeof(*aad_pdata), GFP_KERNEL);
        if (!aad_pdata)
-               goto out;
+               return NULL;
 
-       aad_pdata->irq = irq_of_parse_and_map(np, 0);
+       aad_pdata->irq = i2c->irq;
 
-       if (of_property_read_u32(aad_np, "dlg,micbias-pulse-lvl",
-                                &of_val32) >= 0)
+       if (fwnode_property_read_u32(aad_np, "dlg,micbias-pulse-lvl",
+                                    &fw_val32) >= 0)
                aad_pdata->micbias_pulse_lvl =
-                       da7219_aad_of_micbias_pulse_lvl(codec, of_val32);
+                       da7219_aad_fw_micbias_pulse_lvl(codec, fw_val32);
        else
                aad_pdata->micbias_pulse_lvl = DA7219_AAD_MICBIAS_PULSE_LVL_OFF;
 
-       if (of_property_read_u32(aad_np, "dlg,micbias-pulse-time",
-                                &of_val32) >= 0)
-               aad_pdata->micbias_pulse_time = of_val32;
+       if (fwnode_property_read_u32(aad_np, "dlg,micbias-pulse-time",
+                                    &fw_val32) >= 0)
+               aad_pdata->micbias_pulse_time = fw_val32;
 
-       if (of_property_read_u32(aad_np, "dlg,btn-cfg", &of_val32) >= 0)
-               aad_pdata->btn_cfg = da7219_aad_of_btn_cfg(codec, of_val32);
+       if (fwnode_property_read_u32(aad_np, "dlg,btn-cfg", &fw_val32) >= 0)
+               aad_pdata->btn_cfg = da7219_aad_fw_btn_cfg(codec, fw_val32);
        else
                aad_pdata->btn_cfg = DA7219_AAD_BTN_CFG_10MS;
 
-       if (of_property_read_u32(aad_np, "dlg,mic-det-thr", &of_val32) >= 0)
+       if (fwnode_property_read_u32(aad_np, "dlg,mic-det-thr", &fw_val32) >= 0)
                aad_pdata->mic_det_thr =
-                       da7219_aad_of_mic_det_thr(codec, of_val32);
+                       da7219_aad_fw_mic_det_thr(codec, fw_val32);
        else
                aad_pdata->mic_det_thr = DA7219_AAD_MIC_DET_THR_500_OHMS;
 
-       if (of_property_read_u32(aad_np, "dlg,jack-ins-deb", &of_val32) >= 0)
+       if (fwnode_property_read_u32(aad_np, "dlg,jack-ins-deb", &fw_val32) >= 0)
                aad_pdata->jack_ins_deb =
-                       da7219_aad_of_jack_ins_deb(codec, of_val32);
+                       da7219_aad_fw_jack_ins_deb(codec, fw_val32);
        else
                aad_pdata->jack_ins_deb = DA7219_AAD_JACK_INS_DEB_20MS;
 
-       if (!of_property_read_string(aad_np, "dlg,jack-det-rate", &of_str))
+       if (!fwnode_property_read_string(aad_np, "dlg,jack-det-rate", &fw_str))
                aad_pdata->jack_det_rate =
-                       da7219_aad_of_jack_det_rate(codec, of_str);
+                       da7219_aad_fw_jack_det_rate(codec, fw_str);
        else
                aad_pdata->jack_det_rate = DA7219_AAD_JACK_DET_RATE_256_512MS;
 
-       if (of_property_read_u32(aad_np, "dlg,jack-rem-deb", &of_val32) >= 0)
+       if (fwnode_property_read_u32(aad_np, "dlg,jack-rem-deb", &fw_val32) >= 0)
                aad_pdata->jack_rem_deb =
-                       da7219_aad_of_jack_rem_deb(codec, of_val32);
+                       da7219_aad_fw_jack_rem_deb(codec, fw_val32);
        else
                aad_pdata->jack_rem_deb = DA7219_AAD_JACK_REM_DEB_1MS;
 
-       if (of_property_read_u32(aad_np, "dlg,a-d-btn-thr", &of_val32) >= 0)
-               aad_pdata->a_d_btn_thr = (u8) of_val32;
+       if (fwnode_property_read_u32(aad_np, "dlg,a-d-btn-thr", &fw_val32) >= 0)
+               aad_pdata->a_d_btn_thr = (u8) fw_val32;
        else
                aad_pdata->a_d_btn_thr = 0xA;
 
-       if (of_property_read_u32(aad_np, "dlg,d-b-btn-thr", &of_val32) >= 0)
-               aad_pdata->d_b_btn_thr = (u8) of_val32;
+       if (fwnode_property_read_u32(aad_np, "dlg,d-b-btn-thr", &fw_val32) >= 0)
+               aad_pdata->d_b_btn_thr = (u8) fw_val32;
        else
                aad_pdata->d_b_btn_thr = 0x16;
 
-       if (of_property_read_u32(aad_np, "dlg,b-c-btn-thr", &of_val32) >= 0)
-               aad_pdata->b_c_btn_thr = (u8) of_val32;
+       if (fwnode_property_read_u32(aad_np, "dlg,b-c-btn-thr", &fw_val32) >= 0)
+               aad_pdata->b_c_btn_thr = (u8) fw_val32;
        else
                aad_pdata->b_c_btn_thr = 0x21;
 
-       if (of_property_read_u32(aad_np, "dlg,c-mic-btn-thr", &of_val32) >= 0)
-               aad_pdata->c_mic_btn_thr = (u8) of_val32;
+       if (fwnode_property_read_u32(aad_np, "dlg,c-mic-btn-thr", &fw_val32) >= 0)
+               aad_pdata->c_mic_btn_thr = (u8) fw_val32;
        else
                aad_pdata->c_mic_btn_thr = 0x3E;
 
-       if (of_property_read_u32(aad_np, "dlg,btn-avg", &of_val32) >= 0)
-               aad_pdata->btn_avg = da7219_aad_of_btn_avg(codec, of_val32);
+       if (fwnode_property_read_u32(aad_np, "dlg,btn-avg", &fw_val32) >= 0)
+               aad_pdata->btn_avg = da7219_aad_fw_btn_avg(codec, fw_val32);
        else
                aad_pdata->btn_avg = DA7219_AAD_BTN_AVG_2;
 
-       if (of_property_read_u32(aad_np, "dlg,adc-1bit-rpt", &of_val32) >= 0)
+       if (fwnode_property_read_u32(aad_np, "dlg,adc-1bit-rpt", &fw_val32) >= 0)
                aad_pdata->adc_1bit_rpt =
-                       da7219_aad_of_adc_1bit_rpt(codec, of_val32);
+                       da7219_aad_fw_adc_1bit_rpt(codec, fw_val32);
        else
                aad_pdata->adc_1bit_rpt = DA7219_AAD_ADC_1BIT_RPT_1;
 
-out:
-       of_node_put(aad_np);
-
        return aad_pdata;
 }
 
@@ -769,9 +768,9 @@ int da7219_aad_init(struct snd_soc_codec *codec)
        da7219->aad = da7219_aad;
        da7219_aad->codec = codec;
 
-       /* Handle any DT/platform data */
-       if ((codec->dev->of_node) && (da7219->pdata))
-               da7219->pdata->aad_pdata = da7219_aad_of_to_pdata(codec);
+       /* Handle any DT/ACPI/platform data */
+       if (da7219->pdata && !da7219->pdata->aad_pdata)
+               da7219->pdata->aad_pdata = da7219_aad_fw_to_pdata(codec);
 
        da7219_aad_handle_pdata(codec);
 
index 5c93899f1f0e51390f171c6f9cdf2c2482be4388..50ea94317cb3216d116b9351cf5387a0213b4689 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/clk.h>
 #include <linux/i2c.h>
 #include <linux/of_device.h>
+#include <linux/property.h>
 #include <linux/regmap.h>
 #include <linux/slab.h>
 #include <linux/pm.h>
@@ -1418,7 +1419,7 @@ static struct snd_soc_dai_driver da7219_dai = {
 
 
 /*
- * DT
+ * DT/ACPI
  */
 
 static const struct of_device_id da7219_of_match[] = {
@@ -1434,7 +1435,7 @@ static const struct acpi_device_id da7219_acpi_match[] = {
 MODULE_DEVICE_TABLE(acpi, da7219_acpi_match);
 
 static enum da7219_micbias_voltage
-       da7219_of_micbias_lvl(struct snd_soc_codec *codec, u32 val)
+       da7219_fw_micbias_lvl(struct device *dev, u32 val)
 {
        switch (val) {
        case 1600:
@@ -1450,13 +1451,13 @@ static enum da7219_micbias_voltage
        case 2600:
                return DA7219_MICBIAS_2_6V;
        default:
-               dev_warn(codec->dev, "Invalid micbias level");
+               dev_warn(dev, "Invalid micbias level");
                return DA7219_MICBIAS_2_2V;
        }
 }
 
 static enum da7219_mic_amp_in_sel
-       da7219_of_mic_amp_in_sel(struct snd_soc_codec *codec, const char *str)
+       da7219_fw_mic_amp_in_sel(struct device *dev, const char *str)
 {
        if (!strcmp(str, "diff")) {
                return DA7219_MIC_AMP_IN_SEL_DIFF;
@@ -1465,29 +1466,29 @@ static enum da7219_mic_amp_in_sel
        } else if (!strcmp(str, "se_n")) {
                return DA7219_MIC_AMP_IN_SEL_SE_N;
        } else {
-               dev_warn(codec->dev, "Invalid mic input type selection");
+               dev_warn(dev, "Invalid mic input type selection");
                return DA7219_MIC_AMP_IN_SEL_DIFF;
        }
 }
 
-static struct da7219_pdata *da7219_of_to_pdata(struct snd_soc_codec *codec)
+static struct da7219_pdata *da7219_fw_to_pdata(struct snd_soc_codec *codec)
 {
-       struct device_node *np = codec->dev->of_node;
+       struct device *dev = codec->dev;
        struct da7219_pdata *pdata;
        const char *of_str;
        u32 of_val32;
 
-       pdata = devm_kzalloc(codec->dev, sizeof(*pdata), GFP_KERNEL);
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
        if (!pdata)
                return NULL;
 
-       if (of_property_read_u32(np, "dlg,micbias-lvl", &of_val32) >= 0)
-               pdata->micbias_lvl = da7219_of_micbias_lvl(codec, of_val32);
+       if (device_property_read_u32(dev, "dlg,micbias-lvl", &of_val32) >= 0)
+               pdata->micbias_lvl = da7219_fw_micbias_lvl(dev, of_val32);
        else
                pdata->micbias_lvl = DA7219_MICBIAS_2_2V;
 
-       if (!of_property_read_string(np, "dlg,mic-amp-in-sel", &of_str))
-               pdata->mic_amp_in_sel = da7219_of_mic_amp_in_sel(codec, of_str);
+       if (!device_property_read_string(dev, "dlg,mic-amp-in-sel", &of_str))
+               pdata->mic_amp_in_sel = da7219_fw_mic_amp_in_sel(dev, of_str);
        else
                pdata->mic_amp_in_sel = DA7219_MIC_AMP_IN_SEL_DIFF;
 
@@ -1662,11 +1663,10 @@ static int da7219_probe(struct snd_soc_codec *codec)
                break;
        }
 
-       /* Handle DT/Platform data */
-       if (codec->dev->of_node)
-               da7219->pdata = da7219_of_to_pdata(codec);
-       else
-               da7219->pdata = dev_get_platdata(codec->dev);
+       /* Handle DT/ACPI/Platform data */
+       da7219->pdata = dev_get_platdata(codec->dev);
+       if (!da7219->pdata)
+               da7219->pdata = da7219_fw_to_pdata(codec);
 
        da7219_handle_pdata(codec);
 
index 2abb742fc47b53f19a3a2024fd6424a86c63c631..4e181b270d95022b01f50d37b618b72eb0e68bed 100644 (file)
@@ -1124,8 +1124,10 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
                        }
                        hdac_hdmi_parse_eld(edev, pin);
 
-                       print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET,
-                                       pin->eld.eld_buffer, pin->eld.eld_size);
+                       print_hex_dump_debug("ELD: ",
+                                       DUMP_PREFIX_OFFSET, 16, 1,
+                                       pin->eld.eld_buffer, pin->eld.eld_size,
+                                       true);
                } else {
                        pin->eld.monitor_present = false;
                        pin->eld.eld_valid = false;
@@ -1816,6 +1818,7 @@ static const struct dev_pm_ops hdac_hdmi_pm = {
 static const struct hda_device_id hdmi_list[] = {
        HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0),
        HDA_CODEC_EXT_ENTRY(0x8086280a, 0x100000, "Broxton HDMI", 0),
+       HDA_CODEC_EXT_ENTRY(0x8086280b, 0x100000, "Kabylake HDMI", 0),
        {}
 };
 
index 8e36e883e453e6bda2a770cd9caa515ff0c20fce..f27d115626db7d4cb250c41e788eb5f4b329d91c 100644 (file)
@@ -112,7 +112,7 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream,
                return ret;
 
        if (hcp->hcd.ops->audio_startup) {
-               ret = hcp->hcd.ops->audio_startup(dai->dev->parent);
+               ret = hcp->hcd.ops->audio_startup(dai->dev->parent, hcp->hcd.data);
                if (ret) {
                        mutex_lock(&hcp->current_stream_lock);
                        hcp->current_stream = NULL;
@@ -122,8 +122,8 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream,
        }
 
        if (hcp->hcd.ops->get_eld) {
-               ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->eld,
-                                           sizeof(hcp->eld));
+               ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->hcd.data,
+                                           hcp->eld, sizeof(hcp->eld));
 
                if (!ret) {
                        ret = snd_pcm_hw_constraint_eld(substream->runtime,
@@ -144,7 +144,7 @@ static void hdmi_codec_shutdown(struct snd_pcm_substream *substream,
 
        WARN_ON(hcp->current_stream != substream);
 
-       hcp->hcd.ops->audio_shutdown(dai->dev->parent);
+       hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data);
 
        mutex_lock(&hcp->current_stream_lock);
        hcp->current_stream = NULL;
@@ -195,8 +195,8 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
        hp.sample_rate = params_rate(params);
        hp.channels = params_channels(params);
 
-       return hcp->hcd.ops->hw_params(dai->dev->parent, &hcp->daifmt[dai->id],
-                                      &hp);
+       return hcp->hcd.ops->hw_params(dai->dev->parent, hcp->hcd.data,
+                                      &hcp->daifmt[dai->id], &hp);
 }
 
 static int hdmi_codec_set_fmt(struct snd_soc_dai *dai,
@@ -280,7 +280,8 @@ static int hdmi_codec_digital_mute(struct snd_soc_dai *dai, int mute)
        dev_dbg(dai->dev, "%s()\n", __func__);
 
        if (hcp->hcd.ops->digital_mute)
-               return hcp->hcd.ops->digital_mute(dai->dev->parent, mute);
+               return hcp->hcd.ops->digital_mute(dai->dev->parent,
+                                                 hcp->hcd.data, mute);
 
        return 0;
 }
diff --git a/sound/soc/codecs/max98504.c b/sound/soc/codecs/max98504.c
new file mode 100644 (file)
index 0000000..a7320e7
--- /dev/null
@@ -0,0 +1,383 @@
+/*
+ * MAX98504 ALSA SoC Audio driver
+ *
+ * Copyright 2013 - 2014 Maxim Integrated Products
+ * Copyright 2016 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <sound/soc.h>
+
+#include "max98504.h"
+
+static const char * const max98504_supply_names[] = {
+       "DVDD",
+       "DIOVDD",
+       "PVDD",
+};
+#define MAX98504_NUM_SUPPLIES ARRAY_SIZE(max98504_supply_names)
+
+struct max98504_priv {
+       struct regmap *regmap;
+       struct regulator_bulk_data supplies[MAX98504_NUM_SUPPLIES];
+       unsigned int pcm_rx_channels;
+       bool brownout_enable;
+       unsigned int brownout_threshold;
+       unsigned int brownout_attenuation;
+       unsigned int brownout_attack_hold;
+       unsigned int brownout_timed_hold;
+       unsigned int brownout_release_rate;
+};
+
+static struct reg_default max98504_reg_defaults[] = {
+       { 0x01, 0},
+       { 0x02, 0},
+       { 0x03, 0},
+       { 0x04, 0},
+       { 0x10, 0},
+       { 0x11, 0},
+       { 0x12, 0},
+       { 0x13, 0},
+       { 0x14, 0},
+       { 0x15, 0},
+       { 0x16, 0},
+       { 0x17, 0},
+       { 0x18, 0},
+       { 0x19, 0},
+       { 0x1A, 0},
+       { 0x20, 0},
+       { 0x21, 0},
+       { 0x22, 0},
+       { 0x23, 0},
+       { 0x24, 0},
+       { 0x25, 0},
+       { 0x26, 0},
+       { 0x27, 0},
+       { 0x28, 0},
+       { 0x30, 0},
+       { 0x31, 0},
+       { 0x32, 0},
+       { 0x33, 0},
+       { 0x34, 0},
+       { 0x35, 0},
+       { 0x36, 0},
+       { 0x37, 0},
+       { 0x38, 0},
+       { 0x39, 0},
+       { 0x40, 0},
+       { 0x41, 0},
+};
+
+static bool max98504_volatile_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case MAX98504_INTERRUPT_STATUS:
+       case MAX98504_INTERRUPT_FLAGS:
+       case MAX98504_INTERRUPT_FLAG_CLEARS:
+       case MAX98504_WATCHDOG_CLEAR:
+       case MAX98504_GLOBAL_ENABLE:
+       case MAX98504_SOFTWARE_RESET:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool max98504_readable_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case MAX98504_SOFTWARE_RESET:
+       case MAX98504_WATCHDOG_CLEAR:
+       case MAX98504_INTERRUPT_FLAG_CLEARS:
+               return false;
+       default:
+               return true;
+       }
+}
+
+static int max98504_pcm_rx_ev(struct snd_soc_dapm_widget *w,
+                             struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+       struct max98504_priv *max98504 = snd_soc_component_get_drvdata(c);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               regmap_write(max98504->regmap, MAX98504_PCM_RX_ENABLE,
+                            max98504->pcm_rx_channels);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               regmap_write(max98504->regmap, MAX98504_PCM_RX_ENABLE, 0);
+               break;
+       }
+
+       return 0;
+}
+
+static int max98504_component_probe(struct snd_soc_component *c)
+{
+       struct max98504_priv *max98504 = snd_soc_component_get_drvdata(c);
+       struct regmap *map = max98504->regmap;
+       int ret;
+
+       ret = regulator_bulk_enable(MAX98504_NUM_SUPPLIES, max98504->supplies);
+       if (ret < 0)
+               return ret;
+
+       regmap_write(map, MAX98504_SOFTWARE_RESET, 0x1);
+       msleep(20);
+
+       if (!max98504->brownout_enable)
+               return 0;
+
+       regmap_write(map, MAX98504_PVDD_BROWNOUT_ENABLE, 0x1);
+
+       regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_1,
+                    (max98504->brownout_threshold & 0x1f) << 3 |
+                    (max98504->brownout_attenuation & 0x3));
+
+       regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_2,
+                    max98504->brownout_attack_hold & 0xff);
+
+       regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_3,
+                    max98504->brownout_timed_hold & 0xff);
+
+       regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_4,
+                    max98504->brownout_release_rate & 0xff);
+
+       return 0;
+}
+
+static void max98504_component_remove(struct snd_soc_component *c)
+{
+       struct max98504_priv *max98504 = snd_soc_component_get_drvdata(c);
+
+       regulator_bulk_disable(MAX98504_NUM_SUPPLIES, max98504->supplies);
+}
+
+static const char *spk_source_mux_text[] = {
+       "PCM Monomix", "Analog In", "PDM Left", "PDM Right"
+};
+
+static const struct soc_enum spk_source_mux_enum =
+       SOC_ENUM_SINGLE(MAX98504_SPEAKER_SOURCE_SELECT,
+                       0, ARRAY_SIZE(spk_source_mux_text),
+                       spk_source_mux_text);
+
+static const struct snd_kcontrol_new spk_source_mux =
+       SOC_DAPM_ENUM("SPK Source", spk_source_mux_enum);
+
+static const struct snd_soc_dapm_route max98504_dapm_routes[] = {
+       { "SPKOUT", NULL, "Global Enable" },
+       { "SPK Source", "PCM Monomix", "DAC PCM" },
+       { "SPK Source", "Analog In", "AIN" },
+       { "SPK Source", "PDM Left", "DAC PDM" },
+       { "SPK Source", "PDM Right", "DAC PDM" },
+};
+
+static const struct snd_soc_dapm_widget max98504_dapm_widgets[] = {
+       SND_SOC_DAPM_SUPPLY("Global Enable", MAX98504_GLOBAL_ENABLE,
+               0, 0, NULL, 0),
+       SND_SOC_DAPM_INPUT("AIN"),
+       SND_SOC_DAPM_AIF_OUT("AIF2OUTL", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("AIF2OUTR", "AIF2 Capture", 1, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_DAC_E("DAC PCM", NULL, SND_SOC_NOPM, 0, 0,
+               max98504_pcm_rx_ev,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_DAC("DAC PDM", NULL, MAX98504_PDM_RX_ENABLE, 0, 0),
+       SND_SOC_DAPM_MUX("SPK Source", SND_SOC_NOPM, 0, 0, &spk_source_mux),
+       SND_SOC_DAPM_REG(snd_soc_dapm_spk, "SPKOUT",
+               MAX98504_SPEAKER_ENABLE, 0, 1, 1, 0),
+};
+
+static int max98504_set_tdm_slot(struct snd_soc_dai *dai,
+               unsigned int tx_mask, unsigned int rx_mask,
+               int slots, int slot_width)
+{
+       struct max98504_priv *max98504 = snd_soc_dai_get_drvdata(dai);
+       struct regmap *map = max98504->regmap;
+
+
+       switch (dai->id) {
+       case MAX98504_DAI_ID_PCM:
+               regmap_write(map, MAX98504_PCM_TX_ENABLE, tx_mask);
+               max98504->pcm_rx_channels = rx_mask;
+               break;
+
+       case MAX98504_DAI_ID_PDM:
+               regmap_write(map, MAX98504_PDM_TX_ENABLE, tx_mask);
+               break;
+       default:
+               WARN_ON(1);
+       }
+
+       return 0;
+}
+static int max98504_set_channel_map(struct snd_soc_dai *dai,
+               unsigned int tx_num, unsigned int *tx_slot,
+               unsigned int rx_num, unsigned int *rx_slot)
+{
+       struct max98504_priv *max98504 = snd_soc_dai_get_drvdata(dai);
+       struct regmap *map = max98504->regmap;
+       unsigned int i, sources = 0;
+
+       for (i = 0; i < tx_num; i++)
+               if (tx_slot[i])
+                       sources |= (1 << i);
+
+       switch (dai->id) {
+       case MAX98504_DAI_ID_PCM:
+               regmap_write(map, MAX98504_PCM_TX_CHANNEL_SOURCES,
+                            sources);
+               break;
+
+       case MAX98504_DAI_ID_PDM:
+               regmap_write(map, MAX98504_PDM_TX_CONTROL, sources);
+               break;
+       default:
+               WARN_ON(1);
+       }
+
+       regmap_write(map, MAX98504_MEASUREMENT_ENABLE, sources ? 0x3 : 0x01);
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops max98504_dai_ops = {
+       .set_tdm_slot           = max98504_set_tdm_slot,
+       .set_channel_map        = max98504_set_channel_map,
+};
+
+#define MAX98504_FORMATS       (SNDRV_PCM_FMTBIT_S8|SNDRV_PCM_FMTBIT_S16_LE|\
+                               SNDRV_PCM_FMTBIT_S24_LE|SNDRV_PCM_FMTBIT_S32_LE)
+#define MAX98504_PDM_RATES     (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|\
+                               SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100|\
+                               SNDRV_PCM_RATE_48000|SNDRV_PCM_RATE_88200|\
+                               SNDRV_PCM_RATE_96000)
+
+static struct snd_soc_dai_driver max98504_dai[] = {
+       /* TODO: Add the PCM interface definitions */
+       {
+               .name = "max98504-aif2",
+               .id = MAX98504_DAI_ID_PDM,
+               .playback = {
+                       .stream_name    = "AIF2 Playback",
+                       .channels_min   = 1,
+                       .channels_max   = 2,
+                       .rates          = MAX98504_PDM_RATES,
+                       .formats        = MAX98504_FORMATS,
+               },
+               .capture = {
+                       .stream_name    = "AIF2 Capture",
+                       .channels_min   = 1,
+                       .channels_max   = 2,
+                       .rates          = MAX98504_PDM_RATES,
+                       .formats        = MAX98504_FORMATS,
+               },
+               .ops = &max98504_dai_ops,
+       },
+};
+
+static const struct snd_soc_component_driver max98504_component_driver = {
+       .probe                  = max98504_component_probe,
+       .remove                 = max98504_component_remove,
+       .dapm_widgets           = max98504_dapm_widgets,
+       .num_dapm_widgets       = ARRAY_SIZE(max98504_dapm_widgets),
+       .dapm_routes            = max98504_dapm_routes,
+       .num_dapm_routes        = ARRAY_SIZE(max98504_dapm_routes),
+};
+
+static const struct regmap_config max98504_regmap = {
+       .reg_bits               = 16,
+       .val_bits               = 8,
+       .max_register           = MAX98504_MAX_REGISTER,
+       .reg_defaults           = max98504_reg_defaults,
+       .num_reg_defaults       = ARRAY_SIZE(max98504_reg_defaults),
+       .volatile_reg           = max98504_volatile_register,
+       .readable_reg           = max98504_readable_register,
+       .cache_type             = REGCACHE_RBTREE,
+};
+
+static int max98504_i2c_probe(struct i2c_client *client,
+                             const struct i2c_device_id *id)
+{
+       struct device *dev = &client->dev;
+       struct device_node *node = dev->of_node;
+       struct max98504_priv *max98504;
+       int i, ret;
+
+       max98504 = devm_kzalloc(dev, sizeof(*max98504), GFP_KERNEL);
+       if (!max98504)
+               return -ENOMEM;
+
+       if (node) {
+               if (!of_property_read_u32(node, "maxim,brownout-threshold",
+                                       &max98504->brownout_threshold))
+                       max98504->brownout_enable = true;
+
+               of_property_read_u32(node, "maxim,brownout-attenuation",
+                                       &max98504->brownout_attenuation);
+               of_property_read_u32(node, "maxim,brownout-attack-hold-ms",
+                                       &max98504->brownout_attack_hold);
+               of_property_read_u32(node, "maxim,brownout-timed-hold-ms",
+                                       &max98504->brownout_timed_hold);
+               of_property_read_u32(node, "maxim,brownout-release-rate-ms",
+                                       &max98504->brownout_release_rate);
+       }
+
+       max98504->regmap = devm_regmap_init_i2c(client, &max98504_regmap);
+       if (IS_ERR(max98504->regmap)) {
+               ret = PTR_ERR(max98504->regmap);
+               dev_err(&client->dev, "regmap initialization failed: %d\n", ret);
+               return ret;
+       }
+
+       for (i = 0; i < MAX98504_NUM_SUPPLIES; i++)
+               max98504->supplies[i].supply = max98504_supply_names[i];
+
+       ret = devm_regulator_bulk_get(dev, MAX98504_NUM_SUPPLIES,
+                                     max98504->supplies);
+       if (ret < 0)
+               return ret;
+
+       i2c_set_clientdata(client, max98504);
+
+       return devm_snd_soc_register_component(dev, &max98504_component_driver,
+                               max98504_dai, ARRAY_SIZE(max98504_dai));
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id max98504_of_match[] = {
+       { .compatible = "maxim,max98504" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, max98504_of_match);
+#endif
+
+static const struct i2c_device_id max98504_i2c_id[] = {
+       { "max98504" },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max98504_i2c_id);
+
+static struct i2c_driver max98504_i2c_driver = {
+       .driver = {
+               .name = "max98504",
+               .of_match_table = of_match_ptr(max98504_of_match),
+       },
+       .probe = max98504_i2c_probe,
+       .id_table = max98504_i2c_id,
+};
+module_i2c_driver(max98504_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC MAX98504 driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98504.h b/sound/soc/codecs/max98504.h
new file mode 100644 (file)
index 0000000..afbefad
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * MAX98504 ALSA SoC Audio driver
+ *
+ * Copyright 2011 - 2012 Maxim Integrated Products
+ * Copyright 2016 Samsung Electronics Co., Ltd.
+ *
+ * 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 MAX98504_H_
+#define MAX98504_H_
+
+/*
+ * MAX98504 Register Definitions
+ */
+#define MAX98504_INTERRUPT_STATUS              0x01
+#define MAX98504_INTERRUPT_FLAGS               0x02
+#define MAX98504_INTERRUPT_ENABLE              0x03
+#define MAX98504_INTERRUPT_FLAG_CLEARS         0x04
+#define MAX98504_GPIO_ENABLE                   0x10
+#define MAX98504_GPIO_CONFIG                   0x11
+#define MAX98504_WATCHDOG_ENABLE               0x12
+#define MAX98504_WATCHDOG_CONFIG               0x13
+#define MAX98504_WATCHDOG_CLEAR                        0x14
+#define MAX98504_CLOCK_MONITOR_ENABLE          0x15
+#define MAX98504_PVDD_BROWNOUT_ENABLE          0x16
+#define MAX98504_PVDD_BROWNOUT_CONFIG_1                0x17
+#define MAX98504_PVDD_BROWNOUT_CONFIG_2                0x18
+#define MAX98504_PVDD_BROWNOUT_CONFIG_3                0x19
+#define MAX98504_PVDD_BROWNOUT_CONFIG_4                0x1a
+#define MAX98504_PCM_RX_ENABLE                 0x20
+#define MAX98504_PCM_TX_ENABLE                 0x21
+#define MAX98504_PCM_TX_HIZ_CONTROL            0x22
+#define MAX98504_PCM_TX_CHANNEL_SOURCES                0x23
+#define MAX98504_PCM_MODE_CONFIG               0x24
+#define MAX98504_PCM_DSP_CONFIG                        0x25
+#define MAX98504_PCM_CLOCK_SETUP               0x26
+#define MAX98504_PCM_SAMPLE_RATE_SETUP         0x27
+#define MAX98504_PCM_TO_SPEAKER_MONOMIX                0x28
+#define MAX98504_PDM_TX_ENABLE                 0x30
+#define MAX98504_PDM_TX_HIZ_CONTROL            0x31
+#define MAX98504_PDM_TX_CONTROL                        0x32
+#define MAX98504_PDM_RX_ENABLE                 0x33
+#define MAX98504_SPEAKER_ENABLE                        0x34
+#define MAX98504_SPEAKER_SOURCE_SELECT         0x35
+#define MAX98504_MEASUREMENT_ENABLE            0x36
+#define MAX98504_ANALOGUE_INPUT_GAIN           0x37
+#define MAX98504_TEMPERATURE_LIMIT_CONFIG      0x38
+#define MAX98504_GLOBAL_ENABLE                 0x40
+#define MAX98504_SOFTWARE_RESET                        0x41
+#define MAX98504_REV_ID                                0x7fff
+
+#define MAX98504_MAX_REGISTER                  0x7fff
+
+#define MAX98504_DAI_ID_PCM                    1
+#define MAX98504_DAI_ID_PDM                    2
+
+#endif /* MAX98504_H_ */
diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c
new file mode 100644 (file)
index 0000000..68074c9
--- /dev/null
@@ -0,0 +1,753 @@
+/*
+ * Driver for the MAX9860 Mono Audio Voice Codec
+ *
+ * https://datasheets.maximintegrated.com/en/ds/MAX9860.pdf
+ *
+ * The driver does not support sidetone since the DVST register field is
+ * backwards with the mute near the maximum level instead of the minimum.
+ *
+ * Author: Peter Rosin <peda@axentia.s>
+ *         Copyright 2016 Axentia Technologies
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#include "max9860.h"
+
+struct max9860_priv {
+       struct regmap *regmap;
+       struct regulator *dvddio;
+       struct notifier_block dvddio_nb;
+       u8 psclk;
+       unsigned long pclk_rate;
+       int fmt;
+};
+
+static int max9860_dvddio_event(struct notifier_block *nb,
+                               unsigned long event, void *data)
+{
+       struct max9860_priv *max9860 = container_of(nb, struct max9860_priv,
+                                                   dvddio_nb);
+       if (event & REGULATOR_EVENT_DISABLE) {
+               regcache_mark_dirty(max9860->regmap);
+               regcache_cache_only(max9860->regmap, true);
+       }
+
+       return 0;
+}
+
+static const struct reg_default max9860_reg_defaults[] = {
+       { MAX9860_PWRMAN,       0x00 },
+       { MAX9860_INTEN,        0x00 },
+       { MAX9860_SYSCLK,       0x00 },
+       { MAX9860_AUDIOCLKHIGH, 0x00 },
+       { MAX9860_AUDIOCLKLOW,  0x00 },
+       { MAX9860_IFC1A,        0x00 },
+       { MAX9860_IFC1B,        0x00 },
+       { MAX9860_VOICEFLTR,    0x00 },
+       { MAX9860_DACATTN,      0x00 },
+       { MAX9860_ADCLEVEL,     0x00 },
+       { MAX9860_DACGAIN,      0x00 },
+       { MAX9860_MICGAIN,      0x00 },
+       { MAX9860_MICADC,       0x00 },
+       { MAX9860_NOISEGATE,    0x00 },
+};
+
+static bool max9860_readable(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case MAX9860_INTRSTATUS ... MAX9860_MICGAIN:
+       case MAX9860_MICADC ... MAX9860_PWRMAN:
+       case MAX9860_REVISION:
+               return true;
+       }
+
+       return false;
+}
+
+static bool max9860_writeable(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case MAX9860_INTEN ... MAX9860_MICGAIN:
+       case MAX9860_MICADC ... MAX9860_PWRMAN:
+               return true;
+       }
+
+       return false;
+}
+
+static bool max9860_volatile(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case MAX9860_INTRSTATUS:
+       case MAX9860_MICREADBACK:
+               return true;
+       }
+
+       return false;
+}
+
+static bool max9860_precious(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case MAX9860_INTRSTATUS:
+               return true;
+       }
+
+       return false;
+}
+
+static const struct regmap_config max9860_regmap = {
+       .reg_bits = 8,
+       .val_bits = 8,
+
+       .readable_reg = max9860_readable,
+       .writeable_reg = max9860_writeable,
+       .volatile_reg = max9860_volatile,
+       .precious_reg = max9860_precious,
+
+       .max_register = MAX9860_MAX_REGISTER,
+       .reg_defaults = max9860_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(max9860_reg_defaults),
+       .cache_type = REGCACHE_RBTREE,
+};
+
+static const DECLARE_TLV_DB_SCALE(dva_tlv, -9100, 100, 1);
+static const DECLARE_TLV_DB_SCALE(dvg_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_RANGE(pam_tlv,
+       0, MAX9860_PAM_MAX - 1,             TLV_DB_SCALE_ITEM(-2000, 2000, 1),
+       MAX9860_PAM_MAX, MAX9860_PAM_MAX,   TLV_DB_SCALE_ITEM(3000, 0, 0));
+static const DECLARE_TLV_DB_SCALE(pgam_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(anth_tlv, -7600, 400, 1);
+static const DECLARE_TLV_DB_SCALE(agcth_tlv, -1800, 100, 0);
+
+static const char * const agchld_text[] = {
+       "AGC Disabled", "50ms", "100ms", "400ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(agchld_enum, MAX9860_MICADC,
+                           MAX9860_AGCHLD_SHIFT, agchld_text);
+
+static const char * const agcsrc_text[] = {
+       "Left ADC", "Left/Right ADC"
+};
+
+static SOC_ENUM_SINGLE_DECL(agcsrc_enum, MAX9860_MICADC,
+                           MAX9860_AGCSRC_SHIFT, agcsrc_text);
+
+static const char * const agcatk_text[] = {
+       "3ms", "12ms", "50ms", "200ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(agcatk_enum, MAX9860_MICADC,
+                           MAX9860_AGCATK_SHIFT, agcatk_text);
+
+static const char * const agcrls_text[] = {
+       "78ms", "156ms", "312ms", "625ms",
+       "1.25s", "2.5s", "5s", "10s"
+};
+
+static SOC_ENUM_SINGLE_DECL(agcrls_enum, MAX9860_MICADC,
+                           MAX9860_AGCRLS_SHIFT, agcrls_text);
+
+static const char * const filter_text[] = {
+       "Disabled",
+       "Elliptical HP 217Hz notch (16kHz)",
+       "Butterworth HP 500Hz (16kHz)",
+       "Elliptical HP 217Hz notch (8kHz)",
+       "Butterworth HP 500Hz (8kHz)",
+       "Butterworth HP 200Hz (48kHz)"
+};
+
+static SOC_ENUM_SINGLE_DECL(avflt_enum, MAX9860_VOICEFLTR,
+                           MAX9860_AVFLT_SHIFT, filter_text);
+
+static SOC_ENUM_SINGLE_DECL(dvflt_enum, MAX9860_VOICEFLTR,
+                           MAX9860_DVFLT_SHIFT, filter_text);
+
+static const struct snd_kcontrol_new max9860_controls[] = {
+SOC_SINGLE_TLV("Master Playback Volume", MAX9860_DACATTN,
+              MAX9860_DVA_SHIFT, MAX9860_DVA_MUTE, 1, dva_tlv),
+SOC_SINGLE_TLV("DAC Gain Volume", MAX9860_DACGAIN,
+              MAX9860_DVG_SHIFT, MAX9860_DVG_MAX, 0, dvg_tlv),
+SOC_DOUBLE_TLV("Line Capture Volume", MAX9860_ADCLEVEL,
+              MAX9860_ADCLL_SHIFT, MAX9860_ADCRL_SHIFT, MAX9860_ADCxL_MIN, 1,
+              adc_tlv),
+
+SOC_ENUM("AGC Hold Time", agchld_enum),
+SOC_ENUM("AGC/Noise Gate Source", agcsrc_enum),
+SOC_ENUM("AGC Attack Time", agcatk_enum),
+SOC_ENUM("AGC Release Time", agcrls_enum),
+
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", MAX9860_NOISEGATE,
+              MAX9860_ANTH_SHIFT, MAX9860_ANTH_MAX, 0, anth_tlv),
+SOC_SINGLE_TLV("AGC Signal Threshold Volume", MAX9860_NOISEGATE,
+              MAX9860_AGCTH_SHIFT, MAX9860_AGCTH_MIN, 1, agcth_tlv),
+
+SOC_SINGLE_TLV("Mic PGA Volume", MAX9860_MICGAIN,
+              MAX9860_PGAM_SHIFT, MAX9860_PGAM_MIN, 1, pgam_tlv),
+SOC_SINGLE_TLV("Mic Preamp Volume", MAX9860_MICGAIN,
+              MAX9860_PAM_SHIFT, MAX9860_PAM_MAX, 0, pam_tlv),
+
+SOC_ENUM("ADC Filter", avflt_enum),
+SOC_ENUM("DAC Filter", dvflt_enum),
+};
+
+static const struct snd_soc_dapm_widget max9860_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("MICL"),
+SND_SOC_DAPM_INPUT("MICR"),
+
+SND_SOC_DAPM_ADC("ADCL", NULL, MAX9860_PWRMAN, MAX9860_ADCLEN_SHIFT, 0),
+SND_SOC_DAPM_ADC("ADCR", NULL, MAX9860_PWRMAN, MAX9860_ADCREN_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIFOUTL", "Capture", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIFOUTR", "Capture", 1, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_DAC("DAC", NULL, MAX9860_PWRMAN, MAX9860_DACEN_SHIFT, 0),
+
+SND_SOC_DAPM_OUTPUT("OUT"),
+
+SND_SOC_DAPM_SUPPLY("Supply", SND_SOC_NOPM, 0, 0,
+                   NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_REGULATOR_SUPPLY("AVDD", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DVDD", 0, 0),
+SND_SOC_DAPM_CLOCK_SUPPLY("mclk"),
+};
+
+static const struct snd_soc_dapm_route max9860_dapm_routes[] = {
+       { "ADCL", NULL, "MICL" },
+       { "ADCR", NULL, "MICR" },
+       { "AIFOUTL", NULL, "ADCL" },
+       { "AIFOUTR", NULL, "ADCR" },
+
+       { "DAC", NULL, "AIFINL" },
+       { "DAC", NULL, "AIFINR" },
+       { "OUT", NULL, "DAC" },
+
+       { "Supply", NULL, "AVDD" },
+       { "Supply", NULL, "DVDD" },
+       { "Supply", NULL, "mclk" },
+
+       { "DAC", NULL, "Supply" },
+       { "ADCL", NULL, "Supply" },
+       { "ADCR", NULL, "Supply" },
+};
+
+static int max9860_hw_params(struct snd_pcm_substream *substream,
+                            struct snd_pcm_hw_params *params,
+                            struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max9860_priv *max9860 = snd_soc_codec_get_drvdata(codec);
+       u8 master;
+       u8 ifc1a = 0;
+       u8 ifc1b = 0;
+       u8 sysclk = 0;
+       unsigned long n;
+       int ret;
+
+       dev_dbg(codec->dev, "hw_params %u Hz, %u channels\n",
+               params_rate(params),
+               params_channels(params));
+
+       if (params_channels(params) == 2)
+               ifc1b |= MAX9860_ST;
+
+       switch (max9860->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               master = 0;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFM:
+               master = MAX9860_MASTER;
+               break;
+       default:
+               return -EINVAL;
+       }
+       ifc1a |= master;
+
+       if (master) {
+               if (params_width(params) * params_channels(params) > 48)
+                       ifc1b |= MAX9860_BSEL_64X;
+               else
+                       ifc1b |= MAX9860_BSEL_48X;
+       }
+
+       switch (max9860->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               ifc1a |= MAX9860_DDLY;
+               ifc1b |= MAX9860_ADLY;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               ifc1a |= MAX9860_WCI;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               if (params_width(params) != 16) {
+                       dev_err(codec->dev,
+                               "DSP_A works for 16 bits per sample only.\n");
+                       return -EINVAL;
+               }
+               ifc1a |= MAX9860_DDLY | MAX9860_WCI | MAX9860_HIZ | MAX9860_TDM;
+               ifc1b |= MAX9860_ADLY;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               if (params_width(params) != 16) {
+                       dev_err(codec->dev,
+                               "DSP_B works for 16 bits per sample only.\n");
+                       return -EINVAL;
+               }
+               ifc1a |= MAX9860_WCI | MAX9860_HIZ | MAX9860_TDM;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (max9860->fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               switch (max9860->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+               case SND_SOC_DAIFMT_DSP_A:
+               case SND_SOC_DAIFMT_DSP_B:
+                       return -EINVAL;
+               }
+               ifc1a ^= MAX9860_WCI;
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               switch (max9860->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+               case SND_SOC_DAIFMT_DSP_A:
+               case SND_SOC_DAIFMT_DSP_B:
+                       return -EINVAL;
+               }
+               ifc1a ^= MAX9860_WCI;
+               /* fall through */
+       case SND_SOC_DAIFMT_IB_NF:
+               ifc1a ^= MAX9860_DBCI;
+               ifc1b ^= MAX9860_ABCI;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       dev_dbg(codec->dev, "IFC1A  %02x\n", ifc1a);
+       ret = regmap_write(max9860->regmap, MAX9860_IFC1A, ifc1a);
+       if (ret) {
+               dev_err(codec->dev, "Failed to set IFC1A: %d\n", ret);
+               return ret;
+       }
+       dev_dbg(codec->dev, "IFC1B  %02x\n", ifc1b);
+       ret = regmap_write(max9860->regmap, MAX9860_IFC1B, ifc1b);
+       if (ret) {
+               dev_err(codec->dev, "Failed to set IFC1B: %d\n", ret);
+               return ret;
+       }
+
+       /*
+        * Check if Integer Clock Mode is possible, but avoid it in slave mode
+        * since we then do not know if lrclk is derived from pclk and the
+        * datasheet mentions that the frequencies have to match exactly in
+        * order for this to work.
+        */
+       if (params_rate(params) == 8000 || params_rate(params) == 16000) {
+               if (master) {
+                       switch (max9860->pclk_rate) {
+                       case 12000000:
+                               sysclk = MAX9860_FREQ_12MHZ;
+                               break;
+                       case 13000000:
+                               sysclk = MAX9860_FREQ_13MHZ;
+                               break;
+                       case 19200000:
+                               sysclk = MAX9860_FREQ_19_2MHZ;
+                               break;
+                       default:
+                               /*
+                                * Integer Clock Mode not possible. Leave
+                                * sysclk at zero and fall through to the
+                                * code below for PLL mode.
+                                */
+                               break;
+                       }
+
+                       if (sysclk && params_rate(params) == 16000)
+                               sysclk |= MAX9860_16KHZ;
+               }
+       }
+
+       /*
+        * Largest possible n:
+        *    65536 * 96 * 48kHz / 10MHz -> 30199
+        * Smallest possible n:
+        *    65536 * 96 *  8kHz / 20MHz -> 2517
+        * Both fit nicely in the available 15 bits, no need to apply any mask.
+        */
+       n = DIV_ROUND_CLOSEST_ULL(65536ULL * 96 * params_rate(params),
+                                 max9860->pclk_rate);
+
+       if (!sysclk) {
+               /* PLL mode */
+               if (params_rate(params) > 24000)
+                       sysclk |= MAX9860_16KHZ;
+
+               if (!master)
+                       n |= 1; /* trigger rapid pll lock mode */
+       }
+
+       sysclk |= max9860->psclk;
+       dev_dbg(codec->dev, "SYSCLK %02x\n", sysclk);
+       ret = regmap_write(max9860->regmap,
+                          MAX9860_SYSCLK, sysclk);
+       if (ret) {
+               dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret);
+               return ret;
+       }
+       dev_dbg(codec->dev, "N %lu\n", n);
+       ret = regmap_write(max9860->regmap,
+                          MAX9860_AUDIOCLKHIGH, n >> 8);
+       if (ret) {
+               dev_err(codec->dev, "Failed to set NHI: %d\n", ret);
+               return ret;
+       }
+       ret = regmap_write(max9860->regmap,
+                          MAX9860_AUDIOCLKLOW, n & 0xff);
+       if (ret) {
+               dev_err(codec->dev, "Failed to set NLO: %d\n", ret);
+               return ret;
+       }
+
+       if (!master) {
+               dev_dbg(codec->dev, "Enable PLL\n");
+               ret = regmap_update_bits(max9860->regmap, MAX9860_AUDIOCLKHIGH,
+                                        MAX9860_PLL, MAX9860_PLL);
+               if (ret) {
+                       dev_err(codec->dev, "Failed to enable PLL: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static int max9860_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max9860_priv *max9860 = snd_soc_codec_get_drvdata(codec);
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+       case SND_SOC_DAIFMT_CBS_CFS:
+               max9860->fmt = fmt;
+               return 0;
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static const struct snd_soc_dai_ops max9860_dai_ops = {
+       .hw_params = max9860_hw_params,
+       .set_fmt = max9860_set_fmt,
+};
+
+static struct snd_soc_dai_driver max9860_dai = {
+       .name = "max9860-hifi",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_CONTINUOUS,
+               .rate_min = 8000,
+               .rate_max = 48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE |
+                          SNDRV_PCM_FMTBIT_S24_LE |
+                          SNDRV_PCM_FMTBIT_S32_LE,
+       },
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_CONTINUOUS,
+               .rate_min = 8000,
+               .rate_max = 48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE |
+                          SNDRV_PCM_FMTBIT_S24_LE |
+                          SNDRV_PCM_FMTBIT_S32_LE,
+       },
+       .ops = &max9860_dai_ops,
+       .symmetric_rates = 1,
+};
+
+static int max9860_set_bias_level(struct snd_soc_codec *codec,
+                                 enum snd_soc_bias_level level)
+{
+       struct max9860_priv *max9860 = dev_get_drvdata(codec->dev);
+       int ret;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+       case SND_SOC_BIAS_PREPARE:
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               ret = regmap_update_bits(max9860->regmap, MAX9860_PWRMAN,
+                                        MAX9860_SHDN, MAX9860_SHDN);
+               if (ret) {
+                       dev_err(codec->dev, "Failed to remove SHDN: %d\n", ret);
+                       return ret;
+               }
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               ret = regmap_update_bits(max9860->regmap, MAX9860_PWRMAN,
+                                        MAX9860_SHDN, 0);
+               if (ret) {
+                       dev_err(codec->dev, "Failed to request SHDN: %d\n",
+                               ret);
+                       return ret;
+               }
+               break;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_codec_driver max9860_codec_driver = {
+       .set_bias_level = max9860_set_bias_level,
+       .idle_bias_off = true,
+
+       .controls = max9860_controls,
+       .num_controls = ARRAY_SIZE(max9860_controls),
+       .dapm_widgets = max9860_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(max9860_dapm_widgets),
+       .dapm_routes = max9860_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(max9860_dapm_routes),
+};
+
+#ifdef CONFIG_PM
+static int max9860_suspend(struct device *dev)
+{
+       struct max9860_priv *max9860 = dev_get_drvdata(dev);
+       int ret;
+
+       ret = regmap_update_bits(max9860->regmap, MAX9860_SYSCLK,
+                                MAX9860_PSCLK, MAX9860_PSCLK_OFF);
+       if (ret) {
+               dev_err(dev, "Failed to disable clock: %d\n", ret);
+               return ret;
+       }
+
+       regulator_disable(max9860->dvddio);
+
+       return 0;
+}
+
+static int max9860_resume(struct device *dev)
+{
+       struct max9860_priv *max9860 = dev_get_drvdata(dev);
+       int ret;
+
+       ret = regulator_enable(max9860->dvddio);
+       if (ret) {
+               dev_err(dev, "Failed to enable DVDDIO: %d\n", ret);
+               return ret;
+       }
+
+       regcache_cache_only(max9860->regmap, false);
+       ret = regcache_sync(max9860->regmap);
+       if (ret) {
+               dev_err(dev, "Failed to sync cache: %d\n", ret);
+               return ret;
+       }
+
+       ret = regmap_update_bits(max9860->regmap, MAX9860_SYSCLK,
+                                MAX9860_PSCLK, max9860->psclk);
+       if (ret) {
+               dev_err(dev, "Failed to enable clock: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops max9860_pm_ops = {
+       SET_RUNTIME_PM_OPS(max9860_suspend, max9860_resume, NULL)
+};
+
+static int max9860_probe(struct i2c_client *i2c,
+                        const struct i2c_device_id *id)
+{
+       struct device *dev = &i2c->dev;
+       struct max9860_priv *max9860;
+       int ret;
+       struct clk *mclk;
+       unsigned long mclk_rate;
+       int i;
+       int intr;
+
+       max9860 = devm_kzalloc(dev, sizeof(struct max9860_priv), GFP_KERNEL);
+       if (!max9860)
+               return -ENOMEM;
+
+       max9860->dvddio = devm_regulator_get(dev, "DVDDIO");
+       if (IS_ERR(max9860->dvddio)) {
+               ret = PTR_ERR(max9860->dvddio);
+               if (ret != -EPROBE_DEFER)
+                       dev_err(dev, "Failed to get DVDDIO supply: %d\n", ret);
+               return ret;
+       }
+
+       max9860->dvddio_nb.notifier_call = max9860_dvddio_event;
+
+       ret = regulator_register_notifier(max9860->dvddio, &max9860->dvddio_nb);
+       if (ret)
+               dev_err(dev, "Failed to register DVDDIO notifier: %d\n", ret);
+
+       ret = regulator_enable(max9860->dvddio);
+       if (ret != 0) {
+               dev_err(dev, "Failed to enable DVDDIO: %d\n", ret);
+               return ret;
+       }
+
+       max9860->regmap = devm_regmap_init_i2c(i2c, &max9860_regmap);
+       if (IS_ERR(max9860->regmap)) {
+               ret = PTR_ERR(max9860->regmap);
+               goto err_regulator;
+       }
+
+       dev_set_drvdata(dev, max9860);
+
+       /*
+        * mclk has to be in the 10MHz to 60MHz range.
+        * psclk is used to scale mclk into pclk so that
+        * pclk is in the 10MHz to 20MHz range.
+        */
+       mclk = clk_get(dev, "mclk");
+
+       if (IS_ERR(mclk)) {
+               ret = PTR_ERR(mclk);
+               if (ret != -EPROBE_DEFER)
+                       dev_err(dev, "Failed to get MCLK: %d\n", ret);
+               goto err_regulator;
+       }
+
+       mclk_rate = clk_get_rate(mclk);
+       clk_put(mclk);
+
+       if (mclk_rate > 60000000 || mclk_rate < 10000000) {
+               dev_err(dev, "Bad mclk %luHz (needs 10MHz - 60MHz)\n",
+                       mclk_rate);
+               ret = -EINVAL;
+               goto err_regulator;
+       }
+       if (mclk_rate >= 40000000)
+               max9860->psclk = 3;
+       else if (mclk_rate >= 20000000)
+               max9860->psclk = 2;
+       else
+               max9860->psclk = 1;
+       max9860->pclk_rate = mclk_rate >> (max9860->psclk - 1);
+       max9860->psclk <<= MAX9860_PSCLK_SHIFT;
+       dev_dbg(dev, "mclk %lu pclk %lu\n", mclk_rate, max9860->pclk_rate);
+
+       regcache_cache_bypass(max9860->regmap, true);
+       for (i = 0; i < max9860_regmap.num_reg_defaults; ++i) {
+               ret = regmap_write(max9860->regmap,
+                                  max9860_regmap.reg_defaults[i].reg,
+                                  max9860_regmap.reg_defaults[i].def);
+               if (ret) {
+                       dev_err(dev, "Failed to initialize register %u: %d\n",
+                               max9860_regmap.reg_defaults[i].reg, ret);
+                       goto err_regulator;
+               }
+       }
+       regcache_cache_bypass(max9860->regmap, false);
+
+       ret = regmap_read(max9860->regmap, MAX9860_INTRSTATUS, &intr);
+       if (ret) {
+               dev_err(dev, "Failed to clear INTRSTATUS: %d\n", ret);
+               goto err_regulator;
+       }
+
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
+       pm_runtime_idle(dev);
+
+       ret = snd_soc_register_codec(dev, &max9860_codec_driver,
+                                    &max9860_dai, 1);
+       if (ret) {
+               dev_err(dev, "Failed to register CODEC: %d\n", ret);
+               goto err_pm;
+       }
+
+       return 0;
+
+err_pm:
+       pm_runtime_disable(dev);
+err_regulator:
+       regulator_disable(max9860->dvddio);
+       return ret;
+}
+
+static int max9860_remove(struct i2c_client *i2c)
+{
+       struct device *dev = &i2c->dev;
+       struct max9860_priv *max9860 = dev_get_drvdata(dev);
+
+       snd_soc_unregister_codec(dev);
+       pm_runtime_disable(dev);
+       regulator_disable(max9860->dvddio);
+       return 0;
+}
+
+static const struct i2c_device_id max9860_i2c_id[] = {
+       { "max9860", },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max9860_i2c_id);
+
+static const struct of_device_id max9860_of_match[] = {
+       { .compatible = "maxim,max9860", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, max9860_of_match);
+
+static struct i2c_driver max9860_i2c_driver = {
+       .probe          = max9860_probe,
+       .remove         = max9860_remove,
+       .id_table       = max9860_i2c_id,
+       .driver         = {
+               .name           = "max9860",
+               .of_match_table = max9860_of_match,
+               .pm             = &max9860_pm_ops,
+       },
+};
+
+module_i2c_driver(max9860_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC MAX9860 Mono Audio Voice Codec driver");
+MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/max9860.h b/sound/soc/codecs/max9860.h
new file mode 100644 (file)
index 0000000..22041bd
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Driver for the MAX9860 Mono Audio Voice Codec
+ *
+ * Author: Peter Rosin <peda@axentia.s>
+ *         Copyright 2016 Axentia Technologies
+ *
+ * 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.
+ */
+
+#ifndef _SND_SOC_MAX9860
+#define _SND_SOC_MAX9860
+
+#define MAX9860_INTRSTATUS   0x00
+#define MAX9860_MICREADBACK  0x01
+#define MAX9860_INTEN        0x02
+#define MAX9860_SYSCLK       0x03
+#define MAX9860_AUDIOCLKHIGH 0x04
+#define MAX9860_AUDIOCLKLOW  0x05
+#define MAX9860_IFC1A        0x06
+#define MAX9860_IFC1B        0x07
+#define MAX9860_VOICEFLTR    0x08
+#define MAX9860_DACATTN      0x09
+#define MAX9860_ADCLEVEL     0x0a
+#define MAX9860_DACGAIN      0x0b
+#define MAX9860_MICGAIN      0x0c
+#define MAX9860_RESERVED     0x0d
+#define MAX9860_MICADC       0x0e
+#define MAX9860_NOISEGATE    0x0f
+#define MAX9860_PWRMAN       0x10
+#define MAX9860_REVISION     0xff
+
+#define MAX9860_MAX_REGISTER 0xff
+
+/* INTRSTATUS */
+#define MAX9860_CLD          0x80
+#define MAX9860_SLD          0x40
+#define MAX9860_ULK          0x20
+
+/* MICREADBACK */
+#define MAX9860_NG           0xe0
+#define MAX9860_AGC          0x1f
+
+/* INTEN */
+#define MAX9860_ICLD         0x80
+#define MAX9860_ISLD         0x40
+#define MAX9860_IULK         0x20
+
+/* SYSCLK */
+#define MAX9860_PSCLK        0x30
+#define MAX9860_PSCLK_OFF    0x00
+#define MAX9860_PSCLK_SHIFT  4
+#define MAX9860_FREQ         0x06
+#define MAX9860_FREQ_NORMAL  0x00
+#define MAX9860_FREQ_12MHZ   0x02
+#define MAX9860_FREQ_13MHZ   0x04
+#define MAX9860_FREQ_19_2MHZ 0x06
+#define MAX9860_16KHZ        0x01
+
+/* AUDIOCLKHIGH */
+#define MAX9860_PLL          0x80
+#define MAX9860_NHI          0x7f
+
+/* AUDIOCLKLOW */
+#define MAX9860_NLO          0xff
+
+/* IFC1A */
+#define MAX9860_MASTER       0x80
+#define MAX9860_WCI          0x40
+#define MAX9860_DBCI         0x20
+#define MAX9860_DDLY         0x10
+#define MAX9860_HIZ          0x08
+#define MAX9860_TDM          0x04
+
+/* IFC1B */
+#define MAX9860_ABCI         0x20
+#define MAX9860_ADLY         0x10
+#define MAX9860_ST           0x08
+#define MAX9860_BSEL         0x07
+#define MAX9860_BSEL_OFF     0x00
+#define MAX9860_BSEL_64X     0x01
+#define MAX9860_BSEL_48X     0x02
+#define MAX9860_BSEL_PCLK_2  0x04
+#define MAX9860_BSEL_PCLK_4  0x05
+#define MAX9860_BSEL_PCLK_8  0x06
+#define MAX9860_BSEL_PCLK_16 0x07
+
+/* VOICEFLTR */
+#define MAX9860_AVFLT        0xf0
+#define MAX9860_AVFLT_SHIFT  4
+#define MAX9860_AVFLT_COUNT  6
+#define MAX9860_DVFLT        0x0f
+#define MAX9860_DVFLT_SHIFT  0
+#define MAX9860_DVFLT_COUNT  6
+
+/* DACATTN */
+#define MAX9860_DVA          0xfe
+#define MAX9860_DVA_SHIFT    1
+#define MAX9860_DVA_MUTE     0x5e
+
+/* ADCLEVEL */
+#define MAX9860_ADCRL        0xf0
+#define MAX9860_ADCRL_SHIFT  4
+#define MAX9860_ADCLL        0x0f
+#define MAX9860_ADCLL_SHIFT  0
+#define MAX9860_ADCxL_MIN    15
+
+/* DACGAIN */
+#define MAX9860_DVG          0x60
+#define MAX9860_DVG_SHIFT    5
+#define MAX9860_DVG_MAX      3
+#define MAX9860_DVST         0x1f
+#define MAX9860_DVST_SHIFT   0
+#define MAX9860_DVST_MIN     31
+
+/* MICGAIN */
+#define MAX9860_PAM          0x60
+#define MAX9860_PAM_SHIFT    5
+#define MAX9860_PAM_MAX      3
+#define MAX9860_PGAM         0x1f
+#define MAX9860_PGAM_SHIFT   0
+#define MAX9860_PGAM_MIN     20
+
+/* MICADC */
+#define MAX9860_AGCSRC       0x80
+#define MAX9860_AGCSRC_SHIFT 7
+#define MAX9860_AGCSRC_COUNT 2
+#define MAX9860_AGCRLS       0x70
+#define MAX9860_AGCRLS_SHIFT 4
+#define MAX9860_AGCRLS_COUNT 8
+#define MAX9860_AGCATK       0x0c
+#define MAX9860_AGCATK_SHIFT 2
+#define MAX9860_AGCATK_COUNT 4
+#define MAX9860_AGCHLD       0x03
+#define MAX9860_AGCHLD_OFF   0x00
+#define MAX9860_AGCHLD_SHIFT 0
+#define MAX9860_AGCHLD_COUNT 4
+
+/* NOISEGATE */
+#define MAX9860_ANTH         0xf0
+#define MAX9860_ANTH_SHIFT   4
+#define MAX9860_ANTH_MAX     15
+#define MAX9860_AGCTH        0x0f
+#define MAX9860_AGCTH_SHIFT  0
+#define MAX9860_AGCTH_MIN    15
+
+/* PWRMAN */
+#define MAX9860_SHDN         0x80
+#define MAX9860_DACEN        0x08
+#define MAX9860_DACEN_SHIFT  3
+#define MAX9860_ADCLEN       0x02
+#define MAX9860_ADCLEN_SHIFT 1
+#define MAX9860_ADCREN       0x01
+#define MAX9860_ADCREN_SHIFT 0
+
+#endif /* _SND_SOC_MAX9860 */
old mode 100755 (executable)
new mode 100644 (file)
old mode 100755 (executable)
new mode 100644 (file)
index 6da72290ac58228799f6e952a9f2eb45186629ac..368343f29dd0fae4be730799a56573b0f56a65c4 100644 (file)
@@ -32,6 +32,4 @@
 #define MAX9877_BYPASS                 (1 << 6)
 #define MAX9877_SHDN                   (1 << 7)
 
-extern int max9877_add_controls(struct snd_soc_codec *codec);
-
 #endif
index 683769f0f24693bae0ce89eee8938b815721a6fd..5c9707ac4bbff20e1240d9961ea28bcc8d5af82f 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/clk.h>
 #include <linux/acpi.h>
 #include <linux/math64.h>
+#include <linux/semaphore.h>
 
 #include <sound/initval.h>
 #include <sound/tlv.h>
 
 #include "nau8825.h"
 
+
+#define NUVOTON_CODEC_DAI "nau8825-hifi"
+
 #define NAU_FREF_MAX 13500000
-#define NAU_FVCO_MAX 100000000
+#define NAU_FVCO_MAX 124000000
 #define NAU_FVCO_MIN 90000000
 
+/* cross talk suppression detection */
+#define LOG10_MAGIC 646456993
+#define GAIN_AUGMENT 22500
+#define SIDETONE_BASE 207000
+
+
+static int nau8825_configure_sysclk(struct nau8825 *nau8825,
+               int clk_id, unsigned int freq);
+
 struct nau8825_fll {
        int mclk_src;
        int ratio;
@@ -156,6 +169,661 @@ static const struct reg_default nau8825_reg_defaults[] = {
        { NAU8825_REG_CHARGE_PUMP, 0x0 },
 };
 
+/* register backup table when cross talk detection */
+static struct reg_default nau8825_xtalk_baktab[] = {
+       { NAU8825_REG_ADC_DGAIN_CTRL, 0 },
+       { NAU8825_REG_HSVOL_CTRL, 0 },
+       { NAU8825_REG_DACL_CTRL, 0 },
+       { NAU8825_REG_DACR_CTRL, 0 },
+};
+
+static const unsigned short logtable[256] = {
+       0x0000, 0x0171, 0x02e0, 0x044e, 0x05ba, 0x0725, 0x088e, 0x09f7,
+       0x0b5d, 0x0cc3, 0x0e27, 0x0f8a, 0x10eb, 0x124b, 0x13aa, 0x1508,
+       0x1664, 0x17bf, 0x1919, 0x1a71, 0x1bc8, 0x1d1e, 0x1e73, 0x1fc6,
+       0x2119, 0x226a, 0x23ba, 0x2508, 0x2656, 0x27a2, 0x28ed, 0x2a37,
+       0x2b80, 0x2cc8, 0x2e0f, 0x2f54, 0x3098, 0x31dc, 0x331e, 0x345f,
+       0x359f, 0x36de, 0x381b, 0x3958, 0x3a94, 0x3bce, 0x3d08, 0x3e41,
+       0x3f78, 0x40af, 0x41e4, 0x4319, 0x444c, 0x457f, 0x46b0, 0x47e1,
+       0x4910, 0x4a3f, 0x4b6c, 0x4c99, 0x4dc5, 0x4eef, 0x5019, 0x5142,
+       0x526a, 0x5391, 0x54b7, 0x55dc, 0x5700, 0x5824, 0x5946, 0x5a68,
+       0x5b89, 0x5ca8, 0x5dc7, 0x5ee5, 0x6003, 0x611f, 0x623a, 0x6355,
+       0x646f, 0x6588, 0x66a0, 0x67b7, 0x68ce, 0x69e4, 0x6af8, 0x6c0c,
+       0x6d20, 0x6e32, 0x6f44, 0x7055, 0x7165, 0x7274, 0x7383, 0x7490,
+       0x759d, 0x76aa, 0x77b5, 0x78c0, 0x79ca, 0x7ad3, 0x7bdb, 0x7ce3,
+       0x7dea, 0x7ef0, 0x7ff6, 0x80fb, 0x81ff, 0x8302, 0x8405, 0x8507,
+       0x8608, 0x8709, 0x8809, 0x8908, 0x8a06, 0x8b04, 0x8c01, 0x8cfe,
+       0x8dfa, 0x8ef5, 0x8fef, 0x90e9, 0x91e2, 0x92db, 0x93d2, 0x94ca,
+       0x95c0, 0x96b6, 0x97ab, 0x98a0, 0x9994, 0x9a87, 0x9b7a, 0x9c6c,
+       0x9d5e, 0x9e4f, 0x9f3f, 0xa02e, 0xa11e, 0xa20c, 0xa2fa, 0xa3e7,
+       0xa4d4, 0xa5c0, 0xa6ab, 0xa796, 0xa881, 0xa96a, 0xaa53, 0xab3c,
+       0xac24, 0xad0c, 0xadf2, 0xaed9, 0xafbe, 0xb0a4, 0xb188, 0xb26c,
+       0xb350, 0xb433, 0xb515, 0xb5f7, 0xb6d9, 0xb7ba, 0xb89a, 0xb97a,
+       0xba59, 0xbb38, 0xbc16, 0xbcf4, 0xbdd1, 0xbead, 0xbf8a, 0xc065,
+       0xc140, 0xc21b, 0xc2f5, 0xc3cf, 0xc4a8, 0xc580, 0xc658, 0xc730,
+       0xc807, 0xc8de, 0xc9b4, 0xca8a, 0xcb5f, 0xcc34, 0xcd08, 0xcddc,
+       0xceaf, 0xcf82, 0xd054, 0xd126, 0xd1f7, 0xd2c8, 0xd399, 0xd469,
+       0xd538, 0xd607, 0xd6d6, 0xd7a4, 0xd872, 0xd93f, 0xda0c, 0xdad9,
+       0xdba5, 0xdc70, 0xdd3b, 0xde06, 0xded0, 0xdf9a, 0xe063, 0xe12c,
+       0xe1f5, 0xe2bd, 0xe385, 0xe44c, 0xe513, 0xe5d9, 0xe69f, 0xe765,
+       0xe82a, 0xe8ef, 0xe9b3, 0xea77, 0xeb3b, 0xebfe, 0xecc1, 0xed83,
+       0xee45, 0xef06, 0xefc8, 0xf088, 0xf149, 0xf209, 0xf2c8, 0xf387,
+       0xf446, 0xf505, 0xf5c3, 0xf680, 0xf73e, 0xf7fb, 0xf8b7, 0xf973,
+       0xfa2f, 0xfaea, 0xfba5, 0xfc60, 0xfd1a, 0xfdd4, 0xfe8e, 0xff47
+};
+
+static struct snd_soc_dai *nau8825_get_codec_dai(struct nau8825 *nau8825)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(nau8825->dapm);
+       struct snd_soc_component *component = &codec->component;
+       struct snd_soc_dai *codec_dai, *_dai;
+
+       list_for_each_entry_safe(codec_dai, _dai, &component->dai_list, list) {
+               if (!strncmp(codec_dai->name, NUVOTON_CODEC_DAI,
+                       strlen(NUVOTON_CODEC_DAI)))
+                       return codec_dai;
+       }
+       return NULL;
+}
+
+static bool nau8825_dai_is_active(struct nau8825 *nau8825)
+{
+       struct snd_soc_dai *codec_dai = nau8825_get_codec_dai(nau8825);
+
+       if (codec_dai) {
+               if (codec_dai->playback_active || codec_dai->capture_active)
+                       return true;
+       }
+       return false;
+}
+
+/**
+ * nau8825_sema_acquire - acquire the semaphore of nau88l25
+ * @nau8825:  component to register the codec private data with
+ * @timeout: how long in jiffies to wait before failure or zero to wait
+ * until release
+ *
+ * Attempts to acquire the semaphore with number of jiffies. If no more
+ * tasks are allowed to acquire the semaphore, calling this function will
+ * put the task to sleep. If the semaphore is not released within the
+ * specified number of jiffies, this function returns.
+ * Acquires the semaphore without jiffies. If no more tasks are allowed
+ * to acquire the semaphore, calling this function will put the task to
+ * sleep until the semaphore is released.
+ * It returns if the semaphore was acquired.
+ */
+static void nau8825_sema_acquire(struct nau8825 *nau8825, long timeout)
+{
+       int ret;
+
+       if (timeout)
+               ret = down_timeout(&nau8825->xtalk_sem, timeout);
+       else
+               ret = down_interruptible(&nau8825->xtalk_sem);
+
+       if (ret < 0)
+               dev_warn(nau8825->dev, "Acquire semaphone fail\n");
+}
+
+/**
+ * nau8825_sema_release - release the semaphore of nau88l25
+ * @nau8825:  component to register the codec private data with
+ *
+ * Release the semaphore which may be called from any context and
+ * even by tasks which have never called down().
+ */
+static inline void nau8825_sema_release(struct nau8825 *nau8825)
+{
+       up(&nau8825->xtalk_sem);
+}
+
+/**
+ * nau8825_sema_reset - reset the semaphore for nau88l25
+ * @nau8825:  component to register the codec private data with
+ *
+ * Reset the counter of the semaphore. Call this function to restart
+ * a new round task management.
+ */
+static inline void nau8825_sema_reset(struct nau8825 *nau8825)
+{
+       nau8825->xtalk_sem.count = 1;
+}
+
+/**
+ * Ramp up the headphone volume change gradually to target level.
+ *
+ * @nau8825:  component to register the codec private data with
+ * @vol_from: the volume to start up
+ * @vol_to: the target volume
+ * @step: the volume span to move on
+ *
+ * The headphone volume is from 0dB to minimum -54dB and -1dB per step.
+ * If the volume changes sharp, there is a pop noise heard in headphone. We
+ * provide the function to ramp up the volume up or down by delaying 10ms
+ * per step.
+ */
+static void nau8825_hpvol_ramp(struct nau8825 *nau8825,
+       unsigned int vol_from, unsigned int vol_to, unsigned int step)
+{
+       unsigned int value, volume, ramp_up, from, to;
+
+       if (vol_from == vol_to || step == 0) {
+               return;
+       } else if (vol_from < vol_to) {
+               ramp_up = true;
+               from = vol_from;
+               to = vol_to;
+       } else {
+               ramp_up = false;
+               from = vol_to;
+               to = vol_from;
+       }
+       /* only handle volume from 0dB to minimum -54dB */
+       if (to > NAU8825_HP_VOL_MIN)
+               to = NAU8825_HP_VOL_MIN;
+
+       for (volume = from; volume < to; volume += step) {
+               if (ramp_up)
+                       value = volume;
+               else
+                       value = to - volume + from;
+               regmap_update_bits(nau8825->regmap, NAU8825_REG_HSVOL_CTRL,
+                       NAU8825_HPL_VOL_MASK | NAU8825_HPR_VOL_MASK,
+                       (value << NAU8825_HPL_VOL_SFT) | value);
+               usleep_range(10000, 10500);
+       }
+       if (ramp_up)
+               value = to;
+       else
+               value = from;
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_HSVOL_CTRL,
+               NAU8825_HPL_VOL_MASK | NAU8825_HPR_VOL_MASK,
+               (value << NAU8825_HPL_VOL_SFT) | value);
+}
+
+/**
+ * Computes log10 of a value; the result is round off to 3 decimal. This func-
+ * tion takes reference to dvb-math. The source code locates as the following.
+ * Linux/drivers/media/dvb-core/dvb_math.c
+ *
+ * return log10(value) * 1000
+ */
+static u32 nau8825_intlog10_dec3(u32 value)
+{
+       u32 msb, logentry, significand, interpolation, log10val;
+       u64 log2val;
+
+       /* first detect the msb (count begins at 0) */
+       msb = fls(value) - 1;
+       /**
+        *      now we use a logtable after the following method:
+        *
+        *      log2(2^x * y) * 2^24 = x * 2^24 + log2(y) * 2^24
+        *      where x = msb and therefore 1 <= y < 2
+        *      first y is determined by shifting the value left
+        *      so that msb is bit 31
+        *              0x00231f56 -> 0x8C7D5800
+        *      the result is y * 2^31 -> "significand"
+        *      then the highest 9 bits are used for a table lookup
+        *      the highest bit is discarded because it's always set
+        *      the highest nine bits in our example are 100011000
+        *      so we would use the entry 0x18
+        */
+       significand = value << (31 - msb);
+       logentry = (significand >> 23) & 0xff;
+       /**
+        *      last step we do is interpolation because of the
+        *      limitations of the log table the error is that part of
+        *      the significand which isn't used for lookup then we
+        *      compute the ratio between the error and the next table entry
+        *      and interpolate it between the log table entry used and the
+        *      next one the biggest error possible is 0x7fffff
+        *      (in our example it's 0x7D5800)
+        *      needed value for next table entry is 0x800000
+        *      so the interpolation is
+        *      (error / 0x800000) * (logtable_next - logtable_current)
+        *      in the implementation the division is moved to the end for
+        *      better accuracy there is also an overflow correction if
+        *      logtable_next is 256
+        */
+       interpolation = ((significand & 0x7fffff) *
+               ((logtable[(logentry + 1) & 0xff] -
+               logtable[logentry]) & 0xffff)) >> 15;
+
+       log2val = ((msb << 24) + (logtable[logentry] << 8) + interpolation);
+       /**
+        *      log10(x) = log2(x) * log10(2)
+        */
+       log10val = (log2val * LOG10_MAGIC) >> 31;
+       /**
+        *      the result is round off to 3 decimal
+        */
+       return log10val / ((1 << 24) / 1000);
+}
+
+/**
+ * computes cross talk suppression sidetone gain.
+ *
+ * @sig_org: orignal signal level
+ * @sig_cros: cross talk signal level
+ *
+ * The orignal and cross talk signal vlues need to be characterized.
+ * Once these values have been characterized, this sidetone value
+ * can be converted to decibel with the equation below.
+ * sidetone = 20 * log (original signal level / crosstalk signal level)
+ *
+ * return cross talk sidetone gain
+ */
+static u32 nau8825_xtalk_sidetone(u32 sig_org, u32 sig_cros)
+{
+       u32 gain, sidetone;
+
+       if (unlikely(sig_org == 0) || unlikely(sig_cros == 0)) {
+               WARN_ON(1);
+               return 0;
+       }
+
+       sig_org = nau8825_intlog10_dec3(sig_org);
+       sig_cros = nau8825_intlog10_dec3(sig_cros);
+       if (sig_org >= sig_cros)
+               gain = (sig_org - sig_cros) * 20 + GAIN_AUGMENT;
+       else
+               gain = (sig_cros - sig_org) * 20 + GAIN_AUGMENT;
+       sidetone = SIDETONE_BASE - gain * 2;
+       sidetone /= 1000;
+
+       return sidetone;
+}
+
+static int nau8825_xtalk_baktab_index_by_reg(unsigned int reg)
+{
+       int index;
+
+       for (index = 0; index < ARRAY_SIZE(nau8825_xtalk_baktab); index++)
+               if (nau8825_xtalk_baktab[index].reg == reg)
+                       return index;
+       return -EINVAL;
+}
+
+static void nau8825_xtalk_backup(struct nau8825 *nau8825)
+{
+       int i;
+
+       /* Backup some register values to backup table */
+       for (i = 0; i < ARRAY_SIZE(nau8825_xtalk_baktab); i++)
+               regmap_read(nau8825->regmap, nau8825_xtalk_baktab[i].reg,
+                               &nau8825_xtalk_baktab[i].def);
+}
+
+static void nau8825_xtalk_restore(struct nau8825 *nau8825)
+{
+       int i, volume;
+
+       /* Restore register values from backup table; When the driver restores
+        * the headphone volumem, it needs recover to original level gradually
+        * with 3dB per step for less pop noise.
+        */
+       for (i = 0; i < ARRAY_SIZE(nau8825_xtalk_baktab); i++) {
+               if (nau8825_xtalk_baktab[i].reg == NAU8825_REG_HSVOL_CTRL) {
+                       /* Ramping up the volume change to reduce pop noise */
+                       volume = nau8825_xtalk_baktab[i].def &
+                               NAU8825_HPR_VOL_MASK;
+                       nau8825_hpvol_ramp(nau8825, 0, volume, 3);
+                       continue;
+               }
+               regmap_write(nau8825->regmap, nau8825_xtalk_baktab[i].reg,
+                               nau8825_xtalk_baktab[i].def);
+       }
+}
+
+static void nau8825_xtalk_prepare_dac(struct nau8825 *nau8825)
+{
+       /* Enable power of DAC path */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL,
+               NAU8825_ENABLE_DACR | NAU8825_ENABLE_DACL |
+               NAU8825_ENABLE_ADC | NAU8825_ENABLE_ADC_CLK |
+               NAU8825_ENABLE_DAC_CLK, NAU8825_ENABLE_DACR |
+               NAU8825_ENABLE_DACL | NAU8825_ENABLE_ADC |
+               NAU8825_ENABLE_ADC_CLK | NAU8825_ENABLE_DAC_CLK);
+       /* Prevent startup click by letting charge pump to ramp up and
+        * change bump enable
+        */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+               NAU8825_JAMNODCLOW | NAU8825_CHANRGE_PUMP_EN,
+               NAU8825_JAMNODCLOW | NAU8825_CHANRGE_PUMP_EN);
+       /* Enable clock sync of DAC and DAC clock */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_RDAC,
+               NAU8825_RDAC_EN | NAU8825_RDAC_CLK_EN |
+               NAU8825_RDAC_FS_BCLK_ENB,
+               NAU8825_RDAC_EN | NAU8825_RDAC_CLK_EN);
+       /* Power up output driver with 2 stage */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_POWER_UP_CONTROL,
+               NAU8825_POWERUP_INTEGR_R | NAU8825_POWERUP_INTEGR_L |
+               NAU8825_POWERUP_DRV_IN_R | NAU8825_POWERUP_DRV_IN_L,
+               NAU8825_POWERUP_INTEGR_R | NAU8825_POWERUP_INTEGR_L |
+               NAU8825_POWERUP_DRV_IN_R | NAU8825_POWERUP_DRV_IN_L);
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_POWER_UP_CONTROL,
+               NAU8825_POWERUP_HP_DRV_R | NAU8825_POWERUP_HP_DRV_L,
+               NAU8825_POWERUP_HP_DRV_R | NAU8825_POWERUP_HP_DRV_L);
+       /* HP outputs not shouted to ground  */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_HSD_CTRL,
+               NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L, 0);
+       /* Enable HP boost driver */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST,
+               NAU8825_HP_BOOST_DIS, NAU8825_HP_BOOST_DIS);
+       /* Enable class G compare path to supply 1.8V or 0.9V. */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_CLASSG_CTRL,
+               NAU8825_CLASSG_LDAC_EN | NAU8825_CLASSG_RDAC_EN,
+               NAU8825_CLASSG_LDAC_EN | NAU8825_CLASSG_RDAC_EN);
+}
+
+static void nau8825_xtalk_prepare_adc(struct nau8825 *nau8825)
+{
+       /* Power up left ADC and raise 5dB than Vmid for Vref  */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_ANALOG_ADC_2,
+               NAU8825_POWERUP_ADCL | NAU8825_ADC_VREFSEL_MASK,
+               NAU8825_POWERUP_ADCL | NAU8825_ADC_VREFSEL_VMID_PLUS_0_5DB);
+}
+
+static void nau8825_xtalk_clock(struct nau8825 *nau8825)
+{
+       /* Recover FLL default value */
+       regmap_write(nau8825->regmap, NAU8825_REG_FLL1, 0x0);
+       regmap_write(nau8825->regmap, NAU8825_REG_FLL2, 0x3126);
+       regmap_write(nau8825->regmap, NAU8825_REG_FLL3, 0x0008);
+       regmap_write(nau8825->regmap, NAU8825_REG_FLL4, 0x0010);
+       regmap_write(nau8825->regmap, NAU8825_REG_FLL5, 0x0);
+       regmap_write(nau8825->regmap, NAU8825_REG_FLL6, 0x6000);
+       /* Enable internal VCO clock for detection signal generated */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
+               NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO);
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN,
+               NAU8825_DCO_EN);
+       /* Given specific clock frequency of internal clock to
+        * generate signal.
+        */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
+               NAU8825_CLK_MCLK_SRC_MASK, 0xf);
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1,
+               NAU8825_FLL_RATIO_MASK, 0x10);
+}
+
+static void nau8825_xtalk_prepare(struct nau8825 *nau8825)
+{
+       int volume, index;
+
+       /* Backup those registers changed by cross talk detection */
+       nau8825_xtalk_backup(nau8825);
+       /* Config IIS as master to output signal by codec */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2,
+               NAU8825_I2S_MS_MASK | NAU8825_I2S_DRV_MASK |
+               NAU8825_I2S_BLK_DIV_MASK, NAU8825_I2S_MS_MASTER |
+               (0x2 << NAU8825_I2S_DRV_SFT) | 0x1);
+       /* Ramp up headphone volume to 0dB to get better performance and
+        * avoid pop noise in headphone.
+        */
+       index = nau8825_xtalk_baktab_index_by_reg(NAU8825_REG_HSVOL_CTRL);
+       if (index != -EINVAL) {
+               volume = nau8825_xtalk_baktab[index].def &
+                               NAU8825_HPR_VOL_MASK;
+               nau8825_hpvol_ramp(nau8825, volume, 0, 3);
+       }
+       nau8825_xtalk_clock(nau8825);
+       nau8825_xtalk_prepare_dac(nau8825);
+       nau8825_xtalk_prepare_adc(nau8825);
+       /* Config channel path and digital gain */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_DACL_CTRL,
+               NAU8825_DACL_CH_SEL_MASK | NAU8825_DACL_CH_VOL_MASK,
+               NAU8825_DACL_CH_SEL_L | 0xab);
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_DACR_CTRL,
+               NAU8825_DACR_CH_SEL_MASK | NAU8825_DACR_CH_VOL_MASK,
+               NAU8825_DACR_CH_SEL_R | 0xab);
+       /* Config cross talk parameters and generate the 23Hz sine wave with
+        * 1/16 full scale of signal level for impedance measurement.
+        */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_IMM_MODE_CTRL,
+               NAU8825_IMM_THD_MASK | NAU8825_IMM_GEN_VOL_MASK |
+               NAU8825_IMM_CYC_MASK | NAU8825_IMM_DAC_SRC_MASK,
+               (0x9 << NAU8825_IMM_THD_SFT) | NAU8825_IMM_GEN_VOL_1_16th |
+               NAU8825_IMM_CYC_8192 | NAU8825_IMM_DAC_SRC_SIN);
+       /* RMS intrruption enable */
+       regmap_update_bits(nau8825->regmap,
+               NAU8825_REG_INTERRUPT_MASK, NAU8825_IRQ_RMS_EN, 0);
+       /* Power up left and right DAC */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+               NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0);
+}
+
+static void nau8825_xtalk_clean_dac(struct nau8825 *nau8825)
+{
+       /* Disable HP boost driver */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST,
+               NAU8825_HP_BOOST_DIS, 0);
+       /* HP outputs shouted to ground  */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_HSD_CTRL,
+               NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L,
+               NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L);
+       /* Power down left and right DAC */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+               NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL,
+               NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL);
+       /* Enable the TESTDAC and  disable L/R HP impedance */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+               NAU8825_BIAS_HPR_IMP | NAU8825_BIAS_HPL_IMP |
+               NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN);
+       /* Power down output driver with 2 stage */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_POWER_UP_CONTROL,
+               NAU8825_POWERUP_HP_DRV_R | NAU8825_POWERUP_HP_DRV_L, 0);
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_POWER_UP_CONTROL,
+               NAU8825_POWERUP_INTEGR_R | NAU8825_POWERUP_INTEGR_L |
+               NAU8825_POWERUP_DRV_IN_R | NAU8825_POWERUP_DRV_IN_L, 0);
+       /* Disable clock sync of DAC and DAC clock */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_RDAC,
+               NAU8825_RDAC_EN | NAU8825_RDAC_CLK_EN, 0);
+       /* Disable charge pump ramp up function and change bump */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+               NAU8825_JAMNODCLOW | NAU8825_CHANRGE_PUMP_EN, 0);
+       /* Disable power of DAC path */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL,
+               NAU8825_ENABLE_DACR | NAU8825_ENABLE_DACL |
+               NAU8825_ENABLE_ADC_CLK | NAU8825_ENABLE_DAC_CLK, 0);
+       if (!nau8825->irq)
+               regmap_update_bits(nau8825->regmap,
+                       NAU8825_REG_ENA_CTRL, NAU8825_ENABLE_ADC, 0);
+}
+
+static void nau8825_xtalk_clean_adc(struct nau8825 *nau8825)
+{
+       /* Power down left ADC and restore voltage to Vmid */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_ANALOG_ADC_2,
+               NAU8825_POWERUP_ADCL | NAU8825_ADC_VREFSEL_MASK, 0);
+}
+
+static void nau8825_xtalk_clean(struct nau8825 *nau8825)
+{
+       /* Enable internal VCO needed for interruptions */
+       nau8825_configure_sysclk(nau8825, NAU8825_CLK_INTERNAL, 0);
+       nau8825_xtalk_clean_dac(nau8825);
+       nau8825_xtalk_clean_adc(nau8825);
+       /* Clear cross talk parameters and disable */
+       regmap_write(nau8825->regmap, NAU8825_REG_IMM_MODE_CTRL, 0);
+       /* RMS intrruption disable */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_INTERRUPT_MASK,
+               NAU8825_IRQ_RMS_EN, NAU8825_IRQ_RMS_EN);
+       /* Recover default value for IIS */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2,
+               NAU8825_I2S_MS_MASK | NAU8825_I2S_DRV_MASK |
+               NAU8825_I2S_BLK_DIV_MASK, NAU8825_I2S_MS_SLAVE);
+       /* Restore value of specific register for cross talk */
+       nau8825_xtalk_restore(nau8825);
+}
+
+static void nau8825_xtalk_imm_start(struct nau8825 *nau8825, int vol)
+{
+       /* Apply ADC volume for better cross talk performance */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_ADC_DGAIN_CTRL,
+                               NAU8825_ADC_DIG_VOL_MASK, vol);
+       /* Disables JKTIP(HPL) DAC channel for right to left measurement.
+        * Do it before sending signal in order to erase pop noise.
+        */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+               NAU8825_BIAS_TESTDACR_EN | NAU8825_BIAS_TESTDACL_EN,
+               NAU8825_BIAS_TESTDACL_EN);
+       switch (nau8825->xtalk_state) {
+       case NAU8825_XTALK_HPR_R2L:
+               /* Enable right headphone impedance */
+               regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+                       NAU8825_BIAS_HPR_IMP | NAU8825_BIAS_HPL_IMP,
+                       NAU8825_BIAS_HPR_IMP);
+               break;
+       case NAU8825_XTALK_HPL_R2L:
+               /* Enable left headphone impedance */
+               regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+                       NAU8825_BIAS_HPR_IMP | NAU8825_BIAS_HPL_IMP,
+                       NAU8825_BIAS_HPL_IMP);
+               break;
+       default:
+               break;
+       }
+       msleep(100);
+       /* Impedance measurement mode enable */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_IMM_MODE_CTRL,
+                               NAU8825_IMM_EN, NAU8825_IMM_EN);
+}
+
+static void nau8825_xtalk_imm_stop(struct nau8825 *nau8825)
+{
+       /* Impedance measurement mode disable */
+       regmap_update_bits(nau8825->regmap,
+               NAU8825_REG_IMM_MODE_CTRL, NAU8825_IMM_EN, 0);
+}
+
+/* The cross talk measurement function can reduce cross talk across the
+ * JKTIP(HPL) and JKR1(HPR) outputs which measures the cross talk signal
+ * level to determine what cross talk reduction gain is. This system works by
+ * sending a 23Hz -24dBV sine wave into the headset output DAC and through
+ * the PGA. The output of the PGA is then connected to an internal current
+ * sense which measures the attenuated 23Hz signal and passing the output to
+ * an ADC which converts the measurement to a binary code. With two separated
+ * measurement, one for JKR1(HPR) and the other JKTIP(HPL), measurement data
+ * can be separated read in IMM_RMS_L for HSR and HSL after each measurement.
+ * Thus, the measurement function has four states to complete whole sequence.
+ * 1. Prepare state : Prepare the resource for detection and transfer to HPR
+ *     IMM stat to make JKR1(HPR) impedance measure.
+ * 2. HPR IMM state : Read out orignal signal level of JKR1(HPR) and transfer
+ *     to HPL IMM state to make JKTIP(HPL) impedance measure.
+ * 3. HPL IMM state : Read out cross talk signal level of JKTIP(HPL) and
+ *     transfer to IMM state to determine suppression sidetone gain.
+ * 4. IMM state : Computes cross talk suppression sidetone gain with orignal
+ *     and cross talk signal level. Apply this gain and then restore codec
+ *     configuration. Then transfer to Done state for ending.
+ */
+static void nau8825_xtalk_measure(struct nau8825 *nau8825)
+{
+       u32 sidetone;
+
+       switch (nau8825->xtalk_state) {
+       case NAU8825_XTALK_PREPARE:
+               /* In prepare state, set up clock, intrruption, DAC path, ADC
+                * path and cross talk detection parameters for preparation.
+                */
+               nau8825_xtalk_prepare(nau8825);
+               msleep(280);
+               /* Trigger right headphone impedance detection */
+               nau8825->xtalk_state = NAU8825_XTALK_HPR_R2L;
+               nau8825_xtalk_imm_start(nau8825, 0x00d2);
+               break;
+       case NAU8825_XTALK_HPR_R2L:
+               /* In right headphone IMM state, read out right headphone
+                * impedance measure result, and then start up left side.
+                */
+               regmap_read(nau8825->regmap, NAU8825_REG_IMM_RMS_L,
+                       &nau8825->imp_rms[NAU8825_XTALK_HPR_R2L]);
+               dev_dbg(nau8825->dev, "HPR_R2L imm: %x\n",
+                       nau8825->imp_rms[NAU8825_XTALK_HPR_R2L]);
+               /* Disable then re-enable IMM mode to update */
+               nau8825_xtalk_imm_stop(nau8825);
+               /* Trigger left headphone impedance detection */
+               nau8825->xtalk_state = NAU8825_XTALK_HPL_R2L;
+               nau8825_xtalk_imm_start(nau8825, 0x00ff);
+               break;
+       case NAU8825_XTALK_HPL_R2L:
+               /* In left headphone IMM state, read out left headphone
+                * impedance measure result, and delay some time to wait
+                * detection sine wave output finish. Then, we can calculate
+                * the cross talk suppresstion side tone according to the L/R
+                * headphone imedance.
+                */
+               regmap_read(nau8825->regmap, NAU8825_REG_IMM_RMS_L,
+                       &nau8825->imp_rms[NAU8825_XTALK_HPL_R2L]);
+               dev_dbg(nau8825->dev, "HPL_R2L imm: %x\n",
+                       nau8825->imp_rms[NAU8825_XTALK_HPL_R2L]);
+               nau8825_xtalk_imm_stop(nau8825);
+               msleep(150);
+               nau8825->xtalk_state = NAU8825_XTALK_IMM;
+               break;
+       case NAU8825_XTALK_IMM:
+               /* In impedance measure state, the orignal and cross talk
+                * signal level vlues are ready. The side tone gain is deter-
+                * mined with these signal level. After all, restore codec
+                * configuration.
+                */
+               sidetone = nau8825_xtalk_sidetone(
+                       nau8825->imp_rms[NAU8825_XTALK_HPR_R2L],
+                       nau8825->imp_rms[NAU8825_XTALK_HPL_R2L]);
+               dev_dbg(nau8825->dev, "cross talk sidetone: %x\n", sidetone);
+               regmap_write(nau8825->regmap, NAU8825_REG_DAC_DGAIN_CTRL,
+                                       (sidetone << 8) | sidetone);
+               nau8825_xtalk_clean(nau8825);
+               nau8825->xtalk_state = NAU8825_XTALK_DONE;
+               break;
+       default:
+               break;
+       }
+}
+
+static void nau8825_xtalk_work(struct work_struct *work)
+{
+       struct nau8825 *nau8825 = container_of(
+               work, struct nau8825, xtalk_work);
+
+       nau8825_xtalk_measure(nau8825);
+       /* To determine the cross talk side tone gain when reach
+        * the impedance measure state.
+        */
+       if (nau8825->xtalk_state == NAU8825_XTALK_IMM)
+               nau8825_xtalk_measure(nau8825);
+
+       /* Delay jack report until cross talk detection process
+        * completed. It can avoid application to do playback
+        * preparation before cross talk detection is still working.
+        * Meanwhile, the protection of the cross talk detection
+        * is released.
+        */
+       if (nau8825->xtalk_state == NAU8825_XTALK_DONE) {
+               snd_soc_jack_report(nau8825->jack, nau8825->xtalk_event,
+                               nau8825->xtalk_event_mask);
+               nau8825_sema_release(nau8825);
+               nau8825->xtalk_protect = false;
+       }
+}
+
+static void nau8825_xtalk_cancel(struct nau8825 *nau8825)
+{
+       /* If the xtalk_protect is true, that means the process is still
+        * on going. The driver forces to cancel the cross talk task and
+        * restores the configuration to original status.
+        */
+       if (nau8825->xtalk_protect) {
+               cancel_work_sync(&nau8825->xtalk_work);
+               nau8825_xtalk_clean(nau8825);
+       }
+       /* Reset parameters for cross talk suppression function */
+       nau8825_sema_reset(nau8825);
+       nau8825->xtalk_state = NAU8825_XTALK_DONE;
+       nau8825->xtalk_protect = false;
+}
+
 static bool nau8825_readable_reg(struct device *dev, unsigned int reg)
 {
        switch (reg) {
@@ -217,12 +885,36 @@ static bool nau8825_volatile_reg(struct device *dev, unsigned int reg)
        case NAU8825_REG_SARDOUT_RAM_STATUS:
        case NAU8825_REG_CHARGE_PUMP_INPUT_READ:
        case NAU8825_REG_GENERAL_STATUS:
+       case NAU8825_REG_BIQ_CTRL ... NAU8825_REG_BIQ_COF10:
                return true;
        default:
                return false;
        }
 }
 
+static int nau8825_adc_event(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL,
+                       NAU8825_ENABLE_ADC, NAU8825_ENABLE_ADC);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               if (!nau8825->irq)
+                       regmap_update_bits(nau8825->regmap,
+                               NAU8825_REG_ENA_CTRL, NAU8825_ENABLE_ADC, 0);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int nau8825_pump_event(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
 {
@@ -270,6 +962,54 @@ static int nau8825_output_dac_event(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
+static int nau8825_biq_coeff_get(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+
+       if (!component->regmap)
+               return -EINVAL;
+
+       regmap_raw_read(component->regmap, NAU8825_REG_BIQ_COF1,
+               ucontrol->value.bytes.data, params->max);
+       return 0;
+}
+
+static int nau8825_biq_coeff_put(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+       void *data;
+
+       if (!component->regmap)
+               return -EINVAL;
+
+       data = kmemdup(ucontrol->value.bytes.data,
+               params->max, GFP_KERNEL | GFP_DMA);
+       if (!data)
+               return -ENOMEM;
+
+       regmap_update_bits(component->regmap, NAU8825_REG_BIQ_CTRL,
+               NAU8825_BIQ_WRT_EN, 0);
+       regmap_raw_write(component->regmap, NAU8825_REG_BIQ_COF1,
+               data, params->max);
+       regmap_update_bits(component->regmap, NAU8825_REG_BIQ_CTRL,
+               NAU8825_BIQ_WRT_EN, NAU8825_BIQ_WRT_EN);
+
+       kfree(data);
+       return 0;
+}
+
+static const char * const nau8825_biq_path[] = {
+       "ADC", "DAC"
+};
+
+static const struct soc_enum nau8825_biq_path_enum =
+       SOC_ENUM_SINGLE(NAU8825_REG_BIQ_CTRL, NAU8825_BIQ_PATH_SFT,
+               ARRAY_SIZE(nau8825_biq_path), nau8825_biq_path);
+
 static const char * const nau8825_adc_decimation[] = {
        "32", "64", "128", "256"
 };
@@ -306,6 +1046,10 @@ static const struct snd_kcontrol_new nau8825_controls[] = {
 
        SOC_ENUM("ADC Decimation Rate", nau8825_adc_decimation_enum),
        SOC_ENUM("DAC Oversampling Rate", nau8825_dac_oversampl_enum),
+       /* programmable biquad filter */
+       SOC_ENUM("BIQ Path Select", nau8825_biq_path_enum),
+       SND_SOC_BYTES_EXT("BIQ Coefficients", 20,
+                 nau8825_biq_coeff_get, nau8825_biq_coeff_put),
 };
 
 /* DAC Mux 0x33[9] and 0x34[9] */
@@ -338,7 +1082,9 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = {
        SND_SOC_DAPM_PGA("Frontend PGA", NAU8825_REG_POWER_UP_CONTROL, 14, 0,
                NULL, 0),
 
-       SND_SOC_DAPM_ADC("ADC", NULL, NAU8825_REG_ENA_CTRL, 8, 0),
+       SND_SOC_DAPM_ADC_E("ADC", NULL, SND_SOC_NOPM, 0, 0,
+               nau8825_adc_event, SND_SOC_DAPM_POST_PMU |
+               SND_SOC_DAPM_POST_PMD),
        SND_SOC_DAPM_SUPPLY("ADC Clock", NAU8825_REG_ENA_CTRL, 7, 0, NULL, 0),
        SND_SOC_DAPM_SUPPLY("ADC Power", NAU8825_REG_ANALOG_ADC_2, 6, 0, NULL,
                0),
@@ -592,9 +1338,6 @@ int nau8825_enable_jack_detect(struct snd_soc_codec *codec,
                NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L,
                NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L);
 
-       regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
-               NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_EJECT_EN, 0);
-
        return 0;
 }
 EXPORT_SYMBOL_GPL(nau8825_enable_jack_detect);
@@ -602,24 +1345,21 @@ EXPORT_SYMBOL_GPL(nau8825_enable_jack_detect);
 
 static bool nau8825_is_jack_inserted(struct regmap *regmap)
 {
-       int status;
+       bool active_high, is_high;
+       int status, jkdet;
 
+       regmap_read(regmap, NAU8825_REG_JACK_DET_CTRL, &jkdet);
+       active_high = jkdet & NAU8825_JACK_POLARITY;
        regmap_read(regmap, NAU8825_REG_I2C_DEVICE_ID, &status);
-       return !(status & NAU8825_GPIO2JD1);
+       is_high = status & NAU8825_GPIO2JD1;
+       /* return jack connection status according to jack insertion logic
+        * active high or active low.
+        */
+       return active_high == is_high;
 }
 
 static void nau8825_restart_jack_detection(struct regmap *regmap)
 {
-       /* Chip needs one FSCLK cycle in order to generate interrupts,
-        * as we cannot guarantee one will be provided by the system. Turning
-        * master mode on then off enables us to generate that FSCLK cycle
-        * with a minimum of contention on the clock bus.
-        */
-       regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
-               NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_MASTER);
-       regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
-               NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_SLAVE);
-
        /* this will restart the entire jack detection process including MIC/GND
         * switching and create interrupts. We have to go from 0 to 1 and back
         * to 0 to restart.
@@ -630,11 +1370,30 @@ static void nau8825_restart_jack_detection(struct regmap *regmap)
                NAU8825_JACK_DET_RESTART, 0);
 }
 
+static void nau8825_int_status_clear_all(struct regmap *regmap)
+{
+       int active_irq, clear_irq, i;
+
+       /* Reset the intrruption status from rightmost bit if the corres-
+        * ponding irq event occurs.
+        */
+       regmap_read(regmap, NAU8825_REG_IRQ_STATUS, &active_irq);
+       for (i = 0; i < NAU8825_REG_DATA_LEN; i++) {
+               clear_irq = (0x1 << i);
+               if (active_irq & clear_irq)
+                       regmap_write(regmap,
+                               NAU8825_REG_INT_CLR_KEY_STATUS, clear_irq);
+       }
+}
+
 static void nau8825_eject_jack(struct nau8825 *nau8825)
 {
        struct snd_soc_dapm_context *dapm = nau8825->dapm;
        struct regmap *regmap = nau8825->regmap;
 
+       /* Force to cancel the cross talk detection process */
+       nau8825_xtalk_cancel(nau8825);
+
        snd_soc_dapm_disable_pin(dapm, "SAR");
        snd_soc_dapm_disable_pin(dapm, "MICBIAS");
        /* Detach 2kOhm Resistors from MICBIAS to MICGND1/2 */
@@ -644,6 +1403,69 @@ static void nau8825_eject_jack(struct nau8825 *nau8825)
        regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0xf, 0xf);
 
        snd_soc_dapm_sync(dapm);
+
+       /* Clear all interruption status */
+       nau8825_int_status_clear_all(regmap);
+
+       /* Enable the insertion interruption, disable the ejection inter-
+        * ruption, and then bypass de-bounce circuit.
+        */
+       regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_DIS_CTRL,
+               NAU8825_IRQ_EJECT_DIS | NAU8825_IRQ_INSERT_DIS,
+               NAU8825_IRQ_EJECT_DIS);
+       regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+               NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_EJECT_EN |
+               NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_INSERT_EN,
+               NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_EJECT_EN |
+               NAU8825_IRQ_HEADSET_COMPLETE_EN);
+       regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+               NAU8825_JACK_DET_DB_BYPASS, NAU8825_JACK_DET_DB_BYPASS);
+
+       /* Disable ADC needed for interruptions at audo mode */
+       regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL,
+               NAU8825_ENABLE_ADC, 0);
+
+       /* Close clock for jack type detection at manual mode */
+       nau8825_configure_sysclk(nau8825, NAU8825_CLK_DIS, 0);
+}
+
+/* Enable audo mode interruptions with internal clock. */
+static void nau8825_setup_auto_irq(struct nau8825 *nau8825)
+{
+       struct regmap *regmap = nau8825->regmap;
+
+       /* Enable headset jack type detection complete interruption and
+        * jack ejection interruption.
+        */
+       regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+               NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_EJECT_EN, 0);
+
+       /* Enable internal VCO needed for interruptions */
+       nau8825_configure_sysclk(nau8825, NAU8825_CLK_INTERNAL, 0);
+
+       /* Enable ADC needed for interruptions */
+       regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL,
+               NAU8825_ENABLE_ADC, NAU8825_ENABLE_ADC);
+
+       /* Chip needs one FSCLK cycle in order to generate interruptions,
+        * as we cannot guarantee one will be provided by the system. Turning
+        * master mode on then off enables us to generate that FSCLK cycle
+        * with a minimum of contention on the clock bus.
+        */
+       regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
+               NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_MASTER);
+       regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
+               NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_SLAVE);
+
+       /* Not bypass de-bounce circuit */
+       regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+               NAU8825_JACK_DET_DB_BYPASS, 0);
+
+       /* Unmask all interruptions */
+       regmap_write(regmap, NAU8825_REG_INTERRUPT_DIS_CTRL, 0);
+
+       /* Restart the jack detection process at auto mode */
+       nau8825_restart_jack_detection(regmap);
 }
 
 static int nau8825_button_decode(int value)
@@ -676,6 +1498,11 @@ static int nau8825_jack_insert(struct nau8825 *nau8825)
 
        regmap_read(regmap, NAU8825_REG_GENERAL_STATUS, &jack_status_reg);
        mic_detected = (jack_status_reg >> 10) & 3;
+       /* The JKSLV and JKR2 all detected in high impedance headset */
+       if (mic_detected == 0x3)
+               nau8825->high_imped = true;
+       else
+               nau8825->high_imped = false;
 
        switch (mic_detected) {
        case 0:
@@ -773,6 +1600,33 @@ static irqreturn_t nau8825_interrupt(int irq, void *data)
        } else if (active_irq & NAU8825_HEADSET_COMPLETION_IRQ) {
                if (nau8825_is_jack_inserted(regmap)) {
                        event |= nau8825_jack_insert(nau8825);
+                       if (!nau8825->high_imped) {
+                               /* Apply the cross talk suppression in the
+                                * headset without high impedance.
+                                */
+                               if (!nau8825->xtalk_protect) {
+                                       /* Raise protection for cross talk de-
+                                        * tection if no protection before.
+                                        * The driver has to cancel the pro-
+                                        * cess and restore changes if process
+                                        * is ongoing when ejection.
+                                        */
+                                       nau8825->xtalk_protect = true;
+                                       nau8825_sema_acquire(nau8825, 0);
+                               }
+                               /* Startup cross talk detection process */
+                               nau8825->xtalk_state = NAU8825_XTALK_PREPARE;
+                               schedule_work(&nau8825->xtalk_work);
+                       } else {
+                               /* The cross talk suppression shouldn't apply
+                                * in the headset with high impedance. Thus,
+                                * relieve the protection raised before.
+                                */
+                               if (nau8825->xtalk_protect) {
+                                       nau8825_sema_release(nau8825);
+                                       nau8825->xtalk_protect = false;
+                               }
+                       }
                } else {
                        dev_warn(nau8825->dev, "Headset completion IRQ fired but no headset connected\n");
                        nau8825_eject_jack(nau8825);
@@ -780,6 +1634,37 @@ static irqreturn_t nau8825_interrupt(int irq, void *data)
 
                event_mask |= SND_JACK_HEADSET;
                clear_irq = NAU8825_HEADSET_COMPLETION_IRQ;
+               /* Record the interruption report event for driver to report
+                * the event later. The jack report will delay until cross
+                * talk detection process is done.
+                */
+               if (nau8825->xtalk_state == NAU8825_XTALK_PREPARE) {
+                       nau8825->xtalk_event = event;
+                       nau8825->xtalk_event_mask = event_mask;
+               }
+       } else if (active_irq & NAU8825_IMPEDANCE_MEAS_IRQ) {
+               schedule_work(&nau8825->xtalk_work);
+               clear_irq = NAU8825_IMPEDANCE_MEAS_IRQ;
+       } else if ((active_irq & NAU8825_JACK_INSERTION_IRQ_MASK) ==
+               NAU8825_JACK_INSERTION_DETECTED) {
+               /* One more step to check GPIO status directly. Thus, the
+                * driver can confirm the real insertion interruption because
+                * the intrruption at manual mode has bypassed debounce
+                * circuit which can get rid of unstable status.
+                */
+               if (nau8825_is_jack_inserted(regmap)) {
+                       /* Turn off insertion interruption at manual mode */
+                       regmap_update_bits(regmap,
+                               NAU8825_REG_INTERRUPT_DIS_CTRL,
+                               NAU8825_IRQ_INSERT_DIS,
+                               NAU8825_IRQ_INSERT_DIS);
+                       regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+                               NAU8825_IRQ_INSERT_EN, NAU8825_IRQ_INSERT_EN);
+                       /* Enable interruption for jack type detection at audo
+                        * mode which can detect microphone and jack type.
+                        */
+                       nau8825_setup_auto_irq(nau8825);
+               }
        }
 
        if (!clear_irq)
@@ -787,7 +1672,12 @@ static irqreturn_t nau8825_interrupt(int irq, void *data)
        /* clears the rightmost interruption */
        regmap_write(regmap, NAU8825_REG_INT_CLR_KEY_STATUS, clear_irq);
 
-       if (event_mask)
+       /* Delay jack report until cross talk detection is done. It can avoid
+        * application to do playback preparation when cross talk detection
+        * process is still working. Otherwise, the resource like clock and
+        * power will be issued by them at the same time and conflict happens.
+        */
+       if (event_mask && nau8825->xtalk_state == NAU8825_XTALK_DONE)
                snd_soc_jack_report(nau8825->jack, event, event_mask);
 
        return IRQ_HANDLED;
@@ -921,11 +1811,16 @@ static void nau8825_init_regs(struct nau8825 *nau8825)
                NAU8825_RDAC_CLK_DELAY_MASK | NAU8825_RDAC_VREF_MASK,
                (0x2 << NAU8825_RDAC_CLK_DELAY_SFT) |
                (0x3 << NAU8825_RDAC_VREF_SFT));
+       /* Config L/R channel */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_DACL_CTRL,
+               NAU8825_DACL_CH_SEL_MASK, NAU8825_DACL_CH_SEL_L);
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_DACR_CTRL,
+               NAU8825_DACL_CH_SEL_MASK, NAU8825_DACL_CH_SEL_R);
 }
 
 static const struct regmap_config nau8825_regmap_config = {
-       .val_bits = 16,
-       .reg_bits = 16,
+       .val_bits = NAU8825_REG_DATA_LEN,
+       .reg_bits = NAU8825_REG_ADDR_LEN,
 
        .max_register = NAU8825_REG_MAX,
        .readable_reg = nau8825_readable_reg,
@@ -944,18 +1839,15 @@ static int nau8825_codec_probe(struct snd_soc_codec *codec)
 
        nau8825->dapm = dapm;
 
-       /* The interrupt clock is gated by x1[10:8],
-        * one of them needs to be enabled all the time for
-        * interrupts to happen.
-        */
-       snd_soc_dapm_force_enable_pin(dapm, "DDACR");
-       snd_soc_dapm_sync(dapm);
+       return 0;
+}
 
-       /* Unmask interruptions. Handler uses dapm object so we can enable
-        * interruptions only after dapm is fully initialized.
-        */
-       regmap_write(nau8825->regmap, NAU8825_REG_INTERRUPT_DIS_CTRL, 0);
-       nau8825_restart_jack_detection(nau8825->regmap);
+static int nau8825_codec_remove(struct snd_soc_codec *codec)
+{
+       struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+
+       /* Cancel and reset cross tak suppresstion detection funciton */
+       nau8825_xtalk_cancel(nau8825);
 
        return 0;
 }
@@ -973,8 +1865,8 @@ static int nau8825_codec_probe(struct snd_soc_codec *codec)
 static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs,
                struct nau8825_fll *fll_param)
 {
-       u64 fvco;
-       unsigned int fref, i;
+       u64 fvco, fvco_max;
+       unsigned int fref, i, fvco_sel;
 
        /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing
         * freq_in by 1, 2, 4, or 8 using FLL pre-scalar.
@@ -999,18 +1891,23 @@ static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs,
        fll_param->ratio = fll_ratio[i].val;
 
        /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs.
-        * FDCO must be within the 90MHz - 100MHz or the FFL cannot be
+        * FDCO must be within the 90MHz - 124MHz or the FFL cannot be
         * guaranteed across the full range of operation.
         * FDCO = freq_out * 2 * mclk_src_scaling
         */
+       fvco_max = 0;
+       fvco_sel = ARRAY_SIZE(mclk_src_scaling);
        for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
                fvco = 256 * fs * 2 * mclk_src_scaling[i].param;
-               if (NAU_FVCO_MIN < fvco && fvco < NAU_FVCO_MAX)
-                       break;
+               if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX &&
+                       fvco_max < fvco) {
+                       fvco_max = fvco;
+                       fvco_sel = i;
+               }
        }
-       if (i == ARRAY_SIZE(mclk_src_scaling))
+       if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel)
                return -EINVAL;
-       fll_param->mclk_src = mclk_src_scaling[i].val;
+       fll_param->mclk_src = mclk_src_scaling[fvco_sel].val;
 
        /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional
         * input based on FDCO, FREF and FLL ratio.
@@ -1025,7 +1922,8 @@ static void nau8825_fll_apply(struct nau8825 *nau8825,
                struct nau8825_fll *fll_param)
 {
        regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
-               NAU8825_CLK_MCLK_SRC_MASK, fll_param->mclk_src);
+               NAU8825_CLK_SRC_MASK | NAU8825_CLK_MCLK_SRC_MASK,
+               NAU8825_CLK_SRC_MCLK | fll_param->mclk_src);
        regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1,
                        NAU8825_FLL_RATIO_MASK, fll_param->ratio);
        /* FLL 16-bit fractional input */
@@ -1038,10 +1936,25 @@ static void nau8825_fll_apply(struct nau8825 *nau8825,
                        NAU8825_FLL_REF_DIV_MASK, fll_param->clk_ref_div);
        /* select divided VCO input */
        regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5,
-                       NAU8825_FLL_FILTER_SW_MASK, 0x0000);
-       /* FLL sigma delta modulator enable */
-       regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6,
-                       NAU8825_SDM_EN_MASK, NAU8825_SDM_EN);
+               NAU8825_FLL_CLK_SW_MASK, NAU8825_FLL_CLK_SW_REF);
+       /* Disable free-running mode */
+       regmap_update_bits(nau8825->regmap,
+               NAU8825_REG_FLL6, NAU8825_DCO_EN, 0);
+       if (fll_param->fll_frac) {
+               regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5,
+                       NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN |
+                       NAU8825_FLL_FTR_SW_MASK,
+                       NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN |
+                       NAU8825_FLL_FTR_SW_FILTER);
+               regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6,
+                       NAU8825_SDM_EN, NAU8825_SDM_EN);
+       } else {
+               regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5,
+                       NAU8825_FLL_PDB_DAC_EN | NAU8825_FLL_LOOP_FTR_EN |
+                       NAU8825_FLL_FTR_SW_MASK, NAU8825_FLL_FTR_SW_ACCU);
+               regmap_update_bits(nau8825->regmap,
+                       NAU8825_REG_FLL6, NAU8825_SDM_EN, 0);
+       }
 }
 
 /* freq_out must be 256*Fs in order to achieve the best performance */
@@ -1069,6 +1982,45 @@ static int nau8825_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
        return 0;
 }
 
+static int nau8825_mclk_prepare(struct nau8825 *nau8825, unsigned int freq)
+{
+       int ret = 0;
+
+       nau8825->mclk = devm_clk_get(nau8825->dev, "mclk");
+       if (IS_ERR(nau8825->mclk)) {
+               dev_info(nau8825->dev, "No 'mclk' clock found, assume MCLK is managed externally");
+               return 0;
+       }
+
+       if (!nau8825->mclk_freq) {
+               ret = clk_prepare_enable(nau8825->mclk);
+               if (ret) {
+                       dev_err(nau8825->dev, "Unable to prepare codec mclk\n");
+                       return ret;
+               }
+       }
+
+       if (nau8825->mclk_freq != freq) {
+               freq = clk_round_rate(nau8825->mclk, freq);
+               ret = clk_set_rate(nau8825->mclk, freq);
+               if (ret) {
+                       dev_err(nau8825->dev, "Unable to set mclk rate\n");
+                       return ret;
+               }
+               nau8825->mclk_freq = freq;
+       }
+
+       return 0;
+}
+
+static void nau8825_configure_mclk_as_sysclk(struct regmap *regmap)
+{
+       regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+               NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_MCLK);
+       regmap_update_bits(regmap, NAU8825_REG_FLL6,
+               NAU8825_DCO_EN, 0);
+}
+
 static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id,
        unsigned int freq)
 {
@@ -1076,40 +2028,106 @@ static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id,
        int ret;
 
        switch (clk_id) {
+       case NAU8825_CLK_DIS:
+               /* Clock provided externally and disable internal VCO clock */
+               nau8825_configure_mclk_as_sysclk(regmap);
+               if (nau8825->mclk_freq) {
+                       clk_disable_unprepare(nau8825->mclk);
+                       nau8825->mclk_freq = 0;
+               }
+
+               break;
        case NAU8825_CLK_MCLK:
+               /* Acquire the semaphone to synchronize the playback and
+                * interrupt handler. In order to avoid the playback inter-
+                * fered by cross talk process, the driver make the playback
+                * preparation halted until cross talk process finish.
+                */
+               nau8825_sema_acquire(nau8825, 2 * HZ);
+               nau8825_configure_mclk_as_sysclk(regmap);
+               /* MCLK not changed by clock tree */
                regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
-                       NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_MCLK);
-               regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN, 0);
+                       NAU8825_CLK_MCLK_SRC_MASK, 0);
+               /* Release the semaphone. */
+               nau8825_sema_release(nau8825);
 
-               /* We selected MCLK source but the clock itself managed externally */
-               if (!nau8825->mclk)
-                       break;
+               ret = nau8825_mclk_prepare(nau8825, freq);
+               if (ret)
+                       return ret;
 
-               if (!nau8825->mclk_freq) {
-                       ret = clk_prepare_enable(nau8825->mclk);
-                       if (ret) {
-                               dev_err(nau8825->dev, "Unable to prepare codec mclk\n");
-                               return ret;
-                       }
+               break;
+       case NAU8825_CLK_INTERNAL:
+               if (nau8825_is_jack_inserted(nau8825->regmap)) {
+                       regmap_update_bits(regmap, NAU8825_REG_FLL6,
+                               NAU8825_DCO_EN, NAU8825_DCO_EN);
+                       regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+                               NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO);
+                       /* Decrease the VCO frequency for power saving */
+                       regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+                               NAU8825_CLK_MCLK_SRC_MASK, 0xf);
+                       regmap_update_bits(regmap, NAU8825_REG_FLL1,
+                               NAU8825_FLL_RATIO_MASK, 0x10);
+                       regmap_update_bits(regmap, NAU8825_REG_FLL6,
+                               NAU8825_SDM_EN, NAU8825_SDM_EN);
+               } else {
+                       /* The clock turns off intentionally for power saving
+                        * when no headset connected.
+                        */
+                       nau8825_configure_mclk_as_sysclk(regmap);
+                       dev_warn(nau8825->dev, "Disable clock for power saving when no headset connected\n");
+               }
+               if (nau8825->mclk_freq) {
+                       clk_disable_unprepare(nau8825->mclk);
+                       nau8825->mclk_freq = 0;
                }
 
-               if (nau8825->mclk_freq != freq) {
-                       nau8825->mclk_freq = freq;
+               break;
+       case NAU8825_CLK_FLL_MCLK:
+               /* Acquire the semaphone to synchronize the playback and
+                * interrupt handler. In order to avoid the playback inter-
+                * fered by cross talk process, the driver make the playback
+                * preparation halted until cross talk process finish.
+                */
+               nau8825_sema_acquire(nau8825, 2 * HZ);
+               regmap_update_bits(regmap, NAU8825_REG_FLL3,
+                       NAU8825_FLL_CLK_SRC_MASK, NAU8825_FLL_CLK_SRC_MCLK);
+               /* Release the semaphone. */
+               nau8825_sema_release(nau8825);
 
-                       freq = clk_round_rate(nau8825->mclk, freq);
-                       ret = clk_set_rate(nau8825->mclk, freq);
-                       if (ret) {
-                               dev_err(nau8825->dev, "Unable to set mclk rate\n");
-                               return ret;
-                       }
+               ret = nau8825_mclk_prepare(nau8825, freq);
+               if (ret)
+                       return ret;
+
+               break;
+       case NAU8825_CLK_FLL_BLK:
+               /* Acquire the semaphone to synchronize the playback and
+                * interrupt handler. In order to avoid the playback inter-
+                * fered by cross talk process, the driver make the playback
+                * preparation halted until cross talk process finish.
+                */
+               nau8825_sema_acquire(nau8825, 2 * HZ);
+               regmap_update_bits(regmap, NAU8825_REG_FLL3,
+                       NAU8825_FLL_CLK_SRC_MASK, NAU8825_FLL_CLK_SRC_BLK);
+               /* Release the semaphone. */
+               nau8825_sema_release(nau8825);
+
+               if (nau8825->mclk_freq) {
+                       clk_disable_unprepare(nau8825->mclk);
+                       nau8825->mclk_freq = 0;
                }
 
                break;
-       case NAU8825_CLK_INTERNAL:
-               regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN,
-                       NAU8825_DCO_EN);
-               regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
-                       NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO);
+       case NAU8825_CLK_FLL_FS:
+               /* Acquire the semaphone to synchronize the playback and
+                * interrupt handler. In order to avoid the playback inter-
+                * fered by cross talk process, the driver make the playback
+                * preparation halted until cross talk process finish.
+                */
+               nau8825_sema_acquire(nau8825, 2 * HZ);
+               regmap_update_bits(regmap, NAU8825_REG_FLL3,
+                       NAU8825_FLL_CLK_SRC_MASK, NAU8825_FLL_CLK_SRC_FS);
+               /* Release the semaphone. */
+               nau8825_sema_release(nau8825);
 
                if (nau8825->mclk_freq) {
                        clk_disable_unprepare(nau8825->mclk);
@@ -1135,6 +2153,31 @@ static int nau8825_set_sysclk(struct snd_soc_codec *codec, int clk_id,
        return nau8825_configure_sysclk(nau8825, clk_id, freq);
 }
 
+static int nau8825_resume_setup(struct nau8825 *nau8825)
+{
+       struct regmap *regmap = nau8825->regmap;
+
+       /* Close clock when jack type detection at manual mode */
+       nau8825_configure_sysclk(nau8825, NAU8825_CLK_DIS, 0);
+
+       /* Clear all interruption status */
+       nau8825_int_status_clear_all(regmap);
+
+       /* Enable both insertion and ejection interruptions, and then
+        * bypass de-bounce circuit.
+        */
+       regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+               NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_HEADSET_COMPLETE_EN |
+               NAU8825_IRQ_EJECT_EN | NAU8825_IRQ_INSERT_EN,
+               NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_HEADSET_COMPLETE_EN);
+       regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+               NAU8825_JACK_DET_DB_BYPASS, NAU8825_JACK_DET_DB_BYPASS);
+       regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_DIS_CTRL,
+               NAU8825_IRQ_INSERT_DIS | NAU8825_IRQ_EJECT_DIS, 0);
+
+       return 0;
+}
+
 static int nau8825_set_bias_level(struct snd_soc_codec *codec,
                                   enum snd_soc_bias_level level)
 {
@@ -1157,10 +2200,22 @@ static int nau8825_set_bias_level(struct snd_soc_codec *codec,
                                        return ret;
                                }
                        }
+                       /* Setup codec configuration after resume */
+                       nau8825_resume_setup(nau8825);
                }
                break;
 
        case SND_SOC_BIAS_OFF:
+               /* Cancel and reset cross talk detection funciton */
+               nau8825_xtalk_cancel(nau8825);
+               /* Turn off all interruptions before system shutdown. Keep the
+                * interruption quiet before resume setup completes.
+                */
+               regmap_write(nau8825->regmap,
+                       NAU8825_REG_INTERRUPT_DIS_CTRL, 0xffff);
+               /* Disable ADC needed for interruptions at audo mode */
+               regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL,
+                       NAU8825_ENABLE_ADC, 0);
                if (nau8825->mclk_freq)
                        clk_disable_unprepare(nau8825->mclk);
                break;
@@ -1168,57 +2223,46 @@ static int nau8825_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int nau8825_suspend(struct snd_soc_codec *codec)
+static int __maybe_unused nau8825_suspend(struct snd_soc_codec *codec)
 {
        struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
 
        disable_irq(nau8825->irq);
+       snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
        regcache_cache_only(nau8825->regmap, true);
        regcache_mark_dirty(nau8825->regmap);
 
        return 0;
 }
 
-static int nau8825_resume(struct snd_soc_codec *codec)
+static int __maybe_unused nau8825_resume(struct snd_soc_codec *codec)
 {
        struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
 
-       /* The chip may lose power and reset in S3. regcache_sync restores
-        * register values including configurations for sysclk, irq, and
-        * jack/button detection.
-        */
        regcache_cache_only(nau8825->regmap, false);
        regcache_sync(nau8825->regmap);
-
-       /* Check the jack plug status directly. If the headset is unplugged
-        * during S3 when the chip has no power, there will be no jack
-        * detection irq even after the nau8825_restart_jack_detection below,
-        * because the chip just thinks no headset has ever been plugged in.
-        */
-       if (!nau8825_is_jack_inserted(nau8825->regmap)) {
-               nau8825_eject_jack(nau8825);
-               snd_soc_jack_report(nau8825->jack, 0, SND_JACK_HEADSET);
+       if (nau8825_is_jack_inserted(nau8825->regmap)) {
+               /* If the jack is inserted, we need to check whether the play-
+                * back is active before suspend. If active, the driver has to
+                * raise the protection for cross talk function to avoid the
+                * playback recovers before cross talk process finish. Other-
+                * wise, the playback will be interfered by cross talk func-
+                * tion. It is better to apply hardware related parameters
+                * before starting playback or record.
+                */
+               if (nau8825_dai_is_active(nau8825)) {
+                       nau8825->xtalk_protect = true;
+                       nau8825_sema_acquire(nau8825, 0);
+               }
        }
-
        enable_irq(nau8825->irq);
 
-       /* Run jack detection to check the type (OMTP or CTIA) of the headset
-        * if there is one. This handles the case where a different type of
-        * headset is plugged in during S3. This triggers an IRQ iff a headset
-        * is already plugged in.
-        */
-       nau8825_restart_jack_detection(nau8825->regmap);
-
        return 0;
 }
-#else
-#define nau8825_suspend NULL
-#define nau8825_resume NULL
-#endif
 
 static struct snd_soc_codec_driver nau8825_codec_driver = {
        .probe = nau8825_codec_probe,
+       .remove = nau8825_codec_remove,
        .set_sysclk = nau8825_set_sysclk,
        .set_pll = nau8825_set_pll,
        .set_bias_level = nau8825_set_bias_level,
@@ -1318,22 +2362,8 @@ static int nau8825_read_device_properties(struct device *dev,
 
 static int nau8825_setup_irq(struct nau8825 *nau8825)
 {
-       struct regmap *regmap = nau8825->regmap;
        int ret;
 
-       /* IRQ Output Enable */
-       regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
-               NAU8825_IRQ_OUTPUT_EN, NAU8825_IRQ_OUTPUT_EN);
-
-       /* Enable internal VCO needed for interruptions */
-       nau8825_configure_sysclk(nau8825, NAU8825_CLK_INTERNAL, 0);
-
-       /* Enable DDACR needed for interrupts
-        * It is the same as force_enable_pin("DDACR") we do later
-        */
-       regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL,
-               NAU8825_ENABLE_DACR, NAU8825_ENABLE_DACR);
-
        ret = devm_request_threaded_irq(nau8825->dev, nau8825->irq, NULL,
                nau8825_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
                "nau8825", nau8825);
@@ -1370,6 +2400,13 @@ static int nau8825_i2c_probe(struct i2c_client *i2c,
                return PTR_ERR(nau8825->regmap);
        nau8825->dev = dev;
        nau8825->irq = i2c->irq;
+       /* Initiate parameters, semaphone and work queue which are needed in
+        * cross talk suppression measurment function.
+        */
+       nau8825->xtalk_state = NAU8825_XTALK_DONE;
+       nau8825->xtalk_protect = false;
+       sema_init(&nau8825->xtalk_sem, 1);
+       INIT_WORK(&nau8825->xtalk_work, nau8825_xtalk_work);
 
        nau8825_print_device_properties(nau8825);
 
@@ -1405,6 +2442,7 @@ static const struct i2c_device_id nau8825_i2c_ids[] = {
        { "nau8825", 0 },
        { }
 };
+MODULE_DEVICE_TABLE(i2c, nau8825_i2c_ids);
 
 #ifdef CONFIG_OF
 static const struct of_device_id nau8825_of_ids[] = {
index 8ceb5f38547846e89239e2023fac8bed20c4359e..1c63e2abafa99cddc7f9a2577e0e088803a68214 100644 (file)
 #define NAU8825_REG_CHARGE_PUMP_INPUT_READ             0x81
 #define NAU8825_REG_GENERAL_STATUS             0x82
 #define NAU8825_REG_MAX                NAU8825_REG_GENERAL_STATUS
+/* 16-bit control register address, and 16-bits control register data */
+#define NAU8825_REG_ADDR_LEN           16
+#define NAU8825_REG_DATA_LEN           16
 
 /* ENA_CTRL (0x1) */
 #define NAU8825_ENABLE_DACR_SFT        10
 #define NAU8825_ENABLE_DACR    (1 << NAU8825_ENABLE_DACR_SFT)
 #define NAU8825_ENABLE_DACL_SFT        9
+#define NAU8825_ENABLE_DACL            (1 << NAU8825_ENABLE_DACL_SFT)
 #define NAU8825_ENABLE_ADC_SFT 8
+#define NAU8825_ENABLE_ADC             (1 << NAU8825_ENABLE_ADC_SFT)
+#define NAU8825_ENABLE_ADC_CLK_SFT     7
+#define NAU8825_ENABLE_ADC_CLK (1 << NAU8825_ENABLE_ADC_CLK_SFT)
+#define NAU8825_ENABLE_DAC_CLK_SFT     6
+#define NAU8825_ENABLE_DAC_CLK (1 << NAU8825_ENABLE_DAC_CLK_SFT)
 #define NAU8825_ENABLE_SAR_SFT 1
 
 /* CLK_DIVIDER (0x3) */
 
 /* FLL3 (0x06) */
 #define NAU8825_FLL_INTEGER_MASK               (0x3ff << 0)
+#define NAU8825_FLL_CLK_SRC_SFT                10
+#define NAU8825_FLL_CLK_SRC_MASK               (0x3 << NAU8825_FLL_CLK_SRC_SFT)
+#define NAU8825_FLL_CLK_SRC_MCLK               (0 << NAU8825_FLL_CLK_SRC_SFT)
+#define NAU8825_FLL_CLK_SRC_BLK                (0x2 << NAU8825_FLL_CLK_SRC_SFT)
+#define NAU8825_FLL_CLK_SRC_FS                 (0x3 << NAU8825_FLL_CLK_SRC_SFT)
 
 /* FLL4 (0x07) */
 #define NAU8825_FLL_REF_DIV_MASK               (0x3 << 10)
 
 /* FLL5 (0x08) */
-#define NAU8825_FLL_FILTER_SW_MASK             (0x1 << 14)
+#define NAU8825_FLL_PDB_DAC_EN         (0x1 << 15)
+#define NAU8825_FLL_LOOP_FTR_EN                (0x1 << 14)
+#define NAU8825_FLL_CLK_SW_MASK                (0x1 << 13)
+#define NAU8825_FLL_CLK_SW_N2                  (0x1 << 13)
+#define NAU8825_FLL_CLK_SW_REF         (0x0 << 13)
+#define NAU8825_FLL_FTR_SW_MASK                (0x1 << 12)
+#define NAU8825_FLL_FTR_SW_ACCU                (0x1 << 12)
+#define NAU8825_FLL_FTR_SW_FILTER              (0x0 << 12)
 
 /* FLL6 (0x9) */
-#define NAU8825_DCO_EN_MASK                    (0x1 << 15)
 #define NAU8825_DCO_EN                         (0x1 << 15)
-#define NAU8825_DCO_DIS                                (0x0 << 15)
-#define NAU8825_SDM_EN_MASK                    (0x1 << 14)
 #define NAU8825_SDM_EN                         (0x1 << 14)
-#define NAU8825_SDM_DIS                                (0x0 << 14)
 
 /* HSD_CTRL (0xc) */
 #define NAU8825_HSD_AUTO_MODE  (1 << 6)
 
 /* JACK_DET_CTRL (0xd) */
 #define NAU8825_JACK_DET_RESTART       (1 << 9)
+#define NAU8825_JACK_DET_DB_BYPASS     (1 << 8)
 #define NAU8825_JACK_INSERT_DEBOUNCE_SFT       5
 #define NAU8825_JACK_INSERT_DEBOUNCE_MASK      (0x7 << NAU8825_JACK_INSERT_DEBOUNCE_SFT)
 #define NAU8825_JACK_EJECT_DEBOUNCE_SFT                2
 /* INTERRUPT_MASK (0xf) */
 #define NAU8825_IRQ_OUTPUT_EN (1 << 11)
 #define NAU8825_IRQ_HEADSET_COMPLETE_EN (1 << 10)
+#define NAU8825_IRQ_RMS_EN (1 << 8)
 #define NAU8825_IRQ_KEY_RELEASE_EN (1 << 7)
 #define NAU8825_IRQ_KEY_SHORT_PRESS_EN (1 << 5)
 #define NAU8825_IRQ_EJECT_EN (1 << 2)
+#define NAU8825_IRQ_INSERT_EN (1 << 0)
 
 /* IRQ_STATUS (0x10) */
 #define NAU8825_HEADSET_COMPLETION_IRQ (1 << 10)
 #define NAU8825_IRQ_KEY_RELEASE_DIS (1 << 7)
 #define NAU8825_IRQ_KEY_SHORT_PRESS_DIS (1 << 5)
 #define NAU8825_IRQ_EJECT_DIS (1 << 2)
+#define NAU8825_IRQ_INSERT_DIS (1 << 0)
 
 /* SAR_CTRL (0x13) */
 #define NAU8825_SAR_ADC_EN_SFT 12
 
 /* I2S_PCM_CTRL2 (0x1d) */
 #define NAU8825_I2S_TRISTATE   (1 << 15) /* 0 - normal mode, 1 - Hi-Z output */
+#define NAU8825_I2S_DRV_SFT    12
+#define NAU8825_I2S_DRV_MASK   (0x3 << NAU8825_I2S_DRV_SFT)
 #define NAU8825_I2S_MS_SFT     3
 #define NAU8825_I2S_MS_MASK    (1 << NAU8825_I2S_MS_SFT)
 #define NAU8825_I2S_MS_MASTER  (1 << NAU8825_I2S_MS_SFT)
 #define NAU8825_I2S_MS_SLAVE   (0 << NAU8825_I2S_MS_SFT)
+#define NAU8825_I2S_BLK_DIV_MASK       0x7
+
+/* BIQ_CTRL (0x20) */
+#define NAU8825_BIQ_WRT_SFT   4
+#define NAU8825_BIQ_WRT_EN     (1 << NAU8825_BIQ_WRT_SFT)
+#define NAU8825_BIQ_PATH_SFT   0
+#define NAU8825_BIQ_PATH_MASK  (1 << NAU8825_BIQ_PATH_SFT)
+#define NAU8825_BIQ_PATH_ADC   (0 << NAU8825_BIQ_PATH_SFT)
+#define NAU8825_BIQ_PATH_DAC   (1 << NAU8825_BIQ_PATH_SFT)
 
 /* ADC_RATE (0x2b) */
 #define NAU8825_ADC_SYNC_DOWN_SFT      0
 #define NAU8825_DAC_OVERSAMPLE_128     2
 #define NAU8825_DAC_OVERSAMPLE_32      4
 
+/* ADC_DGAIN_CTRL (0x30) */
+#define NAU8825_ADC_DIG_VOL_MASK       0xff
+
 /* MUTE_CTRL (0x31) */
 #define NAU8825_DAC_ZERO_CROSSING_EN   (1 << 9)
 #define NAU8825_DAC_SOFT_MUTE  (1 << 9)
 
 /* HSVOL_CTRL (0x32) */
 #define NAU8825_HP_MUTE        (1 << 15)
+#define NAU8825_HP_MUTE_AUTO   (1 << 14)
+#define NAU8825_HPL_MUTE       (1 << 13)
+#define NAU8825_HPR_MUTE       (1 << 12)
+#define NAU8825_HPL_VOL_SFT    6
+#define NAU8825_HPL_VOL_MASK   (0x3f << NAU8825_HPL_VOL_SFT)
+#define NAU8825_HPR_VOL_SFT    0
+#define NAU8825_HPR_VOL_MASK   (0x3f << NAU8825_HPR_VOL_SFT)
+#define NAU8825_HP_VOL_MIN     0x36
 
 /* DACL_CTRL (0x33) */
 #define NAU8825_DACL_CH_SEL_SFT        9
+#define NAU8825_DACL_CH_SEL_MASK (0x1 << NAU8825_DACL_CH_SEL_SFT)
+#define NAU8825_DACL_CH_SEL_L    (0x0 << NAU8825_DACL_CH_SEL_SFT)
+#define NAU8825_DACL_CH_SEL_R    (0x1 << NAU8825_DACL_CH_SEL_SFT)
+#define NAU8825_DACL_CH_VOL_MASK       0xff
 
 /* DACR_CTRL (0x34) */
 #define NAU8825_DACR_CH_SEL_SFT        9
+#define NAU8825_DACR_CH_SEL_MASK (0x1 << NAU8825_DACR_CH_SEL_SFT)
+#define NAU8825_DACR_CH_SEL_L    (0x0 << NAU8825_DACR_CH_SEL_SFT)
+#define NAU8825_DACR_CH_SEL_R    (0x1 << NAU8825_DACR_CH_SEL_SFT)
+#define NAU8825_DACR_CH_VOL_MASK       0xff
+
+/* IMM_MODE_CTRL (0x4C) */
+#define NAU8825_IMM_THD_SFT            8
+#define NAU8825_IMM_THD_MASK           (0x3f << NAU8825_IMM_THD_SFT)
+#define NAU8825_IMM_GEN_VOL_SFT        6
+#define NAU8825_IMM_GEN_VOL_MASK       (0x3 << NAU8825_IMM_GEN_VOL_SFT)
+#define NAU8825_IMM_GEN_VOL_1_2nd      (0x0 << NAU8825_IMM_GEN_VOL_SFT)
+#define NAU8825_IMM_GEN_VOL_1_4th      (0x1 << NAU8825_IMM_GEN_VOL_SFT)
+#define NAU8825_IMM_GEN_VOL_1_8th      (0x2 << NAU8825_IMM_GEN_VOL_SFT)
+#define NAU8825_IMM_GEN_VOL_1_16th     (0x3 << NAU8825_IMM_GEN_VOL_SFT)
+
+#define NAU8825_IMM_CYC_SFT            4
+#define NAU8825_IMM_CYC_MASK           (0x3 << NAU8825_IMM_CYC_SFT)
+#define NAU8825_IMM_CYC_1024           (0x0 << NAU8825_IMM_CYC_SFT)
+#define NAU8825_IMM_CYC_2048           (0x1 << NAU8825_IMM_CYC_SFT)
+#define NAU8825_IMM_CYC_4096           (0x2 << NAU8825_IMM_CYC_SFT)
+#define NAU8825_IMM_CYC_8192           (0x3 << NAU8825_IMM_CYC_SFT)
+#define NAU8825_IMM_EN                 (1 << 3)
+#define NAU8825_IMM_DAC_SRC_MASK       0x7
+#define NAU8825_IMM_DAC_SRC_BIQ        0x0
+#define NAU8825_IMM_DAC_SRC_DRC        0x1
+#define NAU8825_IMM_DAC_SRC_MIX        0x2
+#define NAU8825_IMM_DAC_SRC_SIN        0x3
 
 /* CLASSG_CTRL (0x50) */
 #define NAU8825_CLASSG_TIMER_SFT       8
 #define NAU8825_CLASSG_TIMER_MASK      (0x3f << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_TIMER_1ms       (0x1 << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_TIMER_2ms       (0x2 << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_TIMER_8ms       (0x4 << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_TIMER_16ms      (0x8 << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_TIMER_32ms      (0x10 << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_TIMER_64ms      (0x20 << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_LDAC_EN         (0x1 << 2)
+#define NAU8825_CLASSG_RDAC_EN         (0x1 << 1)
 #define NAU8825_CLASSG_EN              (1 << 0)
 
 /* I2C_DEVICE_ID (0x58) */
 #define NAU8825_SOFTWARE_ID_NAU8825    0x0
 
 /* BIAS_ADJ (0x66) */
-#define NAU8825_BIAS_TESTDAC_EN        (0x3 << 8)
+#define NAU8825_BIAS_HPR_IMP           (1 << 15)
+#define NAU8825_BIAS_HPL_IMP           (1 << 14)
+#define NAU8825_BIAS_TESTDAC_SFT       8
+#define NAU8825_BIAS_TESTDAC_EN        (0x3 << NAU8825_BIAS_TESTDAC_SFT)
+#define NAU8825_BIAS_TESTDACR_EN       (0x2 << NAU8825_BIAS_TESTDAC_SFT)
+#define NAU8825_BIAS_TESTDACL_EN       (0x1 << NAU8825_BIAS_TESTDAC_SFT)
 #define NAU8825_BIAS_VMID      (1 << 6)
 #define NAU8825_BIAS_VMID_SEL_SFT      4
 #define NAU8825_BIAS_VMID_SEL_MASK     (3 << NAU8825_BIAS_VMID_SEL_SFT)
 #define NAU8825_POWERUP_ADCL   (1 << 6)
 
 /* RDAC (0x73) */
+#define NAU8825_RDAC_FS_BCLK_ENB       (1 << 15)
+#define NAU8825_RDAC_EN_SFT            12
+#define NAU8825_RDAC_EN                (0x3 << NAU8825_RDAC_EN_SFT)
+#define NAU8825_RDAC_CLK_EN_SFT        8
+#define NAU8825_RDAC_CLK_EN            (0x3 << NAU8825_RDAC_CLK_EN_SFT)
 #define NAU8825_RDAC_CLK_DELAY_SFT     4
 #define NAU8825_RDAC_CLK_DELAY_MASK    (0x7 << NAU8825_RDAC_CLK_DELAY_SFT)
 #define NAU8825_RDAC_VREF_SFT  2
 
 /* System Clock Source */
 enum {
-       NAU8825_CLK_MCLK = 0,
+       NAU8825_CLK_DIS = 0,
+       NAU8825_CLK_MCLK,
        NAU8825_CLK_INTERNAL,
+       NAU8825_CLK_FLL_MCLK,
+       NAU8825_CLK_FLL_BLK,
+       NAU8825_CLK_FLL_FS,
+};
+
+/* Cross talk detection state */
+enum {
+       NAU8825_XTALK_PREPARE = 0,
+       NAU8825_XTALK_HPR_R2L,
+       NAU8825_XTALK_HPL_R2L,
+       NAU8825_XTALK_IMM,
+       NAU8825_XTALK_DONE,
 };
 
 struct nau8825 {
@@ -328,6 +433,8 @@ struct nau8825 {
        struct snd_soc_dapm_context *dapm;
        struct snd_soc_jack *jack;
        struct clk *mclk;
+       struct work_struct xtalk_work;
+       struct semaphore xtalk_sem;
        int irq;
        int mclk_freq; /* 0 - mclk is disabled */
        int button_pressed;
@@ -346,6 +453,12 @@ struct nau8825 {
        int key_debounce;
        int jack_insert_debounce;
        int jack_eject_debounce;
+       int high_imped;
+       int xtalk_state;
+       int xtalk_event;
+       int xtalk_event_mask;
+       bool xtalk_protect;
+       int imp_rms[NAU8825_XTALK_IMM];
 };
 
 int nau8825_enable_jack_detect(struct snd_soc_codec *codec,
index 58325234285c779c8c566486383dda6267795dec..33e1fc2d1598bcc0a0f19647644663c9035facfe 100644 (file)
@@ -73,7 +73,7 @@ static bool pcm1681_accessible_reg(struct device *dev, unsigned int reg)
        return !((reg == 0x00) || (reg == 0x0f));
 }
 
-static bool pcm1681_writeable_reg(struct device *dev, unsigned register reg)
+static bool pcm1681_writeable_reg(struct device *dev, unsigned int reg)
 {
        return pcm1681_accessible_reg(dev, reg) &&
                (reg != PCM1681_ZERO_DETECT_STATUS);
index 06a66579ca6d34964044c0ac297333a805b2a3c3..88fbdd184aa0a5b6d2e6e5a757738e8442a8b0d0 100644 (file)
@@ -59,7 +59,7 @@ static bool pcm179x_accessible_reg(struct device *dev, unsigned int reg)
        return reg >= 0x10 && reg <= 0x17;
 }
 
-static bool pcm179x_writeable_reg(struct device *dev, unsigned register reg)
+static bool pcm179x_writeable_reg(struct device *dev, unsigned int reg)
 {
        bool accessible;
 
index ed515677409bda0bd02081527b1b65d08de78fc9..8ba322a00363cf107cbb364634740655681b8e25 100644 (file)
@@ -57,7 +57,6 @@ static struct platform_driver pcm5102a_codec_driver = {
        .remove         = pcm5102a_remove,
        .driver         = {
                .name   = "pcm5102a-codec",
-               .owner  = THIS_MODULE,
                .of_match_table = pcm5102a_of_match,
        },
 };
index 1bd31644a782eac26042fccf47bfbc65e380b63f..74c0e4eb3788dca775f17207c6c7fb35f5629cab 100644 (file)
@@ -1100,6 +1100,13 @@ static const struct dmi_system_id force_combo_jack_table[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "Skylake Client platform")
                }
        },
+       {
+               .ident = "Intel Kabylake RVP",
+               .matches = {
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Kabylake Client platform")
+               }
+       },
+
        { }
 };
 
diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c
new file mode 100644 (file)
index 0000000..77ff8eb
--- /dev/null
@@ -0,0 +1,447 @@
+/*
+ * rt5514-spi.c  --  RT5514 SPI driver
+ *
+ * Copyright 2015 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/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 <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rt5514-spi.h"
+
+static struct spi_device *rt5514_spi;
+
+struct rt5514_dsp {
+       struct device *dev;
+       struct delayed_work copy_work;
+       struct mutex dma_lock;
+       struct snd_pcm_substream *substream;
+       unsigned int buf_base, buf_limit, buf_rp;
+       size_t buf_size;
+       size_t dma_offset;
+       size_t dsp_offset;
+};
+
+static const struct snd_pcm_hardware rt5514_spi_pcm_hardware = {
+       .info                   = SNDRV_PCM_INFO_MMAP |
+                                 SNDRV_PCM_INFO_MMAP_VALID |
+                                 SNDRV_PCM_INFO_INTERLEAVED,
+       .formats                = SNDRV_PCM_FMTBIT_S16_LE,
+       .period_bytes_min       = PAGE_SIZE,
+       .period_bytes_max       = 0x20000 / 8,
+       .periods_min            = 8,
+       .periods_max            = 8,
+       .channels_min           = 1,
+       .channels_max           = 1,
+       .buffer_bytes_max       = 0x20000,
+};
+
+static struct snd_soc_dai_driver rt5514_spi_dai = {
+       .name = "rt5514-dsp-cpu-dai",
+       .id = 0,
+       .capture = {
+               .stream_name = "DSP Capture",
+               .channels_min = 1,
+               .channels_max = 1,
+               .rates = SNDRV_PCM_RATE_16000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+};
+
+static void rt5514_spi_copy_work(struct work_struct *work)
+{
+       struct rt5514_dsp *rt5514_dsp =
+               container_of(work, struct rt5514_dsp, copy_work.work);
+       struct snd_pcm_runtime *runtime;
+       size_t period_bytes, truncated_bytes = 0;
+
+       mutex_lock(&rt5514_dsp->dma_lock);
+       if (!rt5514_dsp->substream) {
+               dev_err(rt5514_dsp->dev, "No pcm substream\n");
+               goto done;
+       }
+
+       runtime = rt5514_dsp->substream->runtime;
+       period_bytes = snd_pcm_lib_period_bytes(rt5514_dsp->substream);
+
+       if (rt5514_dsp->buf_size - rt5514_dsp->dsp_offset <  period_bytes)
+               period_bytes = rt5514_dsp->buf_size - rt5514_dsp->dsp_offset;
+
+       if (rt5514_dsp->buf_rp + period_bytes <= rt5514_dsp->buf_limit) {
+               rt5514_spi_burst_read(rt5514_dsp->buf_rp,
+                       runtime->dma_area + rt5514_dsp->dma_offset,
+                       period_bytes);
+
+               if (rt5514_dsp->buf_rp + period_bytes == rt5514_dsp->buf_limit)
+                       rt5514_dsp->buf_rp = rt5514_dsp->buf_base;
+               else
+                       rt5514_dsp->buf_rp += period_bytes;
+       } else {
+               truncated_bytes = rt5514_dsp->buf_limit - rt5514_dsp->buf_rp;
+               rt5514_spi_burst_read(rt5514_dsp->buf_rp,
+                       runtime->dma_area + rt5514_dsp->dma_offset,
+                       truncated_bytes);
+
+               rt5514_spi_burst_read(rt5514_dsp->buf_base,
+                       runtime->dma_area + rt5514_dsp->dma_offset +
+                       truncated_bytes, period_bytes - truncated_bytes);
+
+                       rt5514_dsp->buf_rp = rt5514_dsp->buf_base +
+                               period_bytes - truncated_bytes;
+       }
+
+       rt5514_dsp->dma_offset += period_bytes;
+       if (rt5514_dsp->dma_offset >= runtime->dma_bytes)
+               rt5514_dsp->dma_offset = 0;
+
+       rt5514_dsp->dsp_offset += period_bytes;
+
+       snd_pcm_period_elapsed(rt5514_dsp->substream);
+
+       if (rt5514_dsp->dsp_offset < rt5514_dsp->buf_size)
+               schedule_delayed_work(&rt5514_dsp->copy_work, 5);
+done:
+       mutex_unlock(&rt5514_dsp->dma_lock);
+}
+
+/* PCM for streaming audio from the DSP buffer */
+static int rt5514_spi_pcm_open(struct snd_pcm_substream *substream)
+{
+       snd_soc_set_runtime_hwparams(substream, &rt5514_spi_pcm_hardware);
+
+       return 0;
+}
+
+static int rt5514_spi_hw_params(struct snd_pcm_substream *substream,
+                              struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct rt5514_dsp *rt5514_dsp =
+                       snd_soc_platform_get_drvdata(rtd->platform);
+       int ret;
+
+       mutex_lock(&rt5514_dsp->dma_lock);
+       ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+                       params_buffer_bytes(hw_params));
+       rt5514_dsp->substream = substream;
+       mutex_unlock(&rt5514_dsp->dma_lock);
+
+       return ret;
+}
+
+static int rt5514_spi_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct rt5514_dsp *rt5514_dsp =
+                       snd_soc_platform_get_drvdata(rtd->platform);
+
+       mutex_lock(&rt5514_dsp->dma_lock);
+       rt5514_dsp->substream = NULL;
+       mutex_unlock(&rt5514_dsp->dma_lock);
+
+       cancel_delayed_work_sync(&rt5514_dsp->copy_work);
+
+       return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int rt5514_spi_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct rt5514_dsp *rt5514_dsp =
+                       snd_soc_platform_get_drvdata(rtd->platform);
+       u8 buf[8];
+
+       rt5514_dsp->dma_offset = 0;
+       rt5514_dsp->dsp_offset = 0;
+
+       /**
+        * The address area x1800XXXX is the register address, and it cannot
+        * support spi burst read perfectly. So we use the spi burst read
+        * individually to make sure the data correctly.
+       */
+       rt5514_spi_burst_read(RT5514_BUFFER_VOICE_BASE, (u8 *)&buf,
+               sizeof(buf));
+       rt5514_dsp->buf_base = buf[0] | buf[1] << 8 | buf[2] << 16 |
+                               buf[3] << 24;
+
+       rt5514_spi_burst_read(RT5514_BUFFER_VOICE_LIMIT, (u8 *)&buf,
+               sizeof(buf));
+       rt5514_dsp->buf_limit = buf[0] | buf[1] << 8 | buf[2] << 16 |
+                               buf[3] << 24;
+
+       rt5514_spi_burst_read(RT5514_BUFFER_VOICE_RP, (u8 *)&buf,
+               sizeof(buf));
+       rt5514_dsp->buf_rp = buf[0] | buf[1] << 8 | buf[2] << 16 |
+                               buf[3] << 24;
+
+       rt5514_spi_burst_read(RT5514_BUFFER_VOICE_SIZE, (u8 *)&buf,
+               sizeof(buf));
+       rt5514_dsp->buf_size = buf[0] | buf[1] << 8 | buf[2] << 16 |
+                               buf[3] << 24;
+
+       return 0;
+}
+
+static int rt5514_spi_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct rt5514_dsp *rt5514_dsp =
+                       snd_soc_platform_get_drvdata(rtd->platform);
+
+       if (cmd == SNDRV_PCM_TRIGGER_START) {
+               if (rt5514_dsp->buf_base && rt5514_dsp->buf_limit &&
+                       rt5514_dsp->buf_rp && rt5514_dsp->buf_size)
+                       schedule_delayed_work(&rt5514_dsp->copy_work, 0);
+       }
+
+       return 0;
+}
+
+static snd_pcm_uframes_t rt5514_spi_pcm_pointer(
+               struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct rt5514_dsp *rt5514_dsp =
+               snd_soc_platform_get_drvdata(rtd->platform);
+
+       return bytes_to_frames(runtime, rt5514_dsp->dma_offset);
+}
+
+static struct snd_pcm_ops rt5514_spi_pcm_ops = {
+       .open           = rt5514_spi_pcm_open,
+       .hw_params      = rt5514_spi_hw_params,
+       .hw_free        = rt5514_spi_hw_free,
+       .trigger        = rt5514_spi_trigger,
+       .prepare        = rt5514_spi_prepare,
+       .pointer        = rt5514_spi_pcm_pointer,
+       .mmap           = snd_pcm_lib_mmap_vmalloc,
+       .page           = snd_pcm_lib_get_vmalloc_page,
+};
+
+static int rt5514_spi_pcm_probe(struct snd_soc_platform *platform)
+{
+       struct rt5514_dsp *rt5514_dsp;
+
+       rt5514_dsp = devm_kzalloc(platform->dev, sizeof(*rt5514_dsp),
+                       GFP_KERNEL);
+
+       rt5514_dsp->dev = &rt5514_spi->dev;
+       mutex_init(&rt5514_dsp->dma_lock);
+       INIT_DELAYED_WORK(&rt5514_dsp->copy_work, rt5514_spi_copy_work);
+       snd_soc_platform_set_drvdata(platform, rt5514_dsp);
+
+       return 0;
+}
+
+static struct snd_soc_platform_driver rt5514_spi_platform = {
+       .probe = rt5514_spi_pcm_probe,
+       .ops = &rt5514_spi_pcm_ops,
+};
+
+static const struct snd_soc_component_driver rt5514_spi_dai_component = {
+       .name           = "rt5514-spi-dai",
+};
+
+/**
+ * rt5514_spi_burst_read - Read data from SPI by rt5514 address.
+ * @addr: Start address.
+ * @rxbuf: Data Buffer for reading.
+ * @len: Data length, it must be a multiple of 8.
+ *
+ *
+ * Returns true for success.
+ */
+int rt5514_spi_burst_read(unsigned int addr, u8 *rxbuf, size_t len)
+{
+       u8 spi_cmd = RT5514_SPI_CMD_BURST_READ;
+       int status;
+       u8 write_buf[8];
+       unsigned int i, end, offset = 0;
+
+       struct spi_message message;
+       struct spi_transfer x[3];
+
+       while (offset < len) {
+               if (offset + RT5514_SPI_BUF_LEN <= len)
+                       end = RT5514_SPI_BUF_LEN;
+               else
+                       end = len % RT5514_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;
+
+               spi_message_init(&message);
+               memset(x, 0, sizeof(x));
+
+               x[0].len = 5;
+               x[0].tx_buf = write_buf;
+               spi_message_add_tail(&x[0], &message);
+
+               x[1].len = 4;
+               x[1].tx_buf = write_buf;
+               spi_message_add_tail(&x[1], &message);
+
+               x[2].len = end;
+               x[2].rx_buf = rxbuf + offset;
+               spi_message_add_tail(&x[2], &message);
+
+               status = spi_sync(rt5514_spi, &message);
+
+               if (status)
+                       return false;
+
+               offset += RT5514_SPI_BUF_LEN;
+       }
+
+       for (i = 0; i < len; i += 8) {
+               write_buf[0] = rxbuf[i + 0];
+               write_buf[1] = rxbuf[i + 1];
+               write_buf[2] = rxbuf[i + 2];
+               write_buf[3] = rxbuf[i + 3];
+               write_buf[4] = rxbuf[i + 4];
+               write_buf[5] = rxbuf[i + 5];
+               write_buf[6] = rxbuf[i + 6];
+               write_buf[7] = rxbuf[i + 7];
+
+               rxbuf[i + 0] = write_buf[7];
+               rxbuf[i + 1] = write_buf[6];
+               rxbuf[i + 2] = write_buf[5];
+               rxbuf[i + 3] = write_buf[4];
+               rxbuf[i + 4] = write_buf[3];
+               rxbuf[i + 5] = write_buf[2];
+               rxbuf[i + 6] = write_buf[1];
+               rxbuf[i + 7] = write_buf[0];
+       }
+
+       return true;
+}
+
+/**
+ * rt5514_spi_burst_write - Write data to SPI by rt5514 address.
+ * @addr: Start address.
+ * @txbuf: Data Buffer for writng.
+ * @len: Data length, it must be a multiple of 8.
+ *
+ *
+ * Returns true for success.
+ */
+int rt5514_spi_burst_write(u32 addr, const u8 *txbuf, size_t len)
+{
+       u8 spi_cmd = RT5514_SPI_CMD_BURST_WRITE;
+       u8 *write_buf;
+       unsigned int i, end, offset = 0;
+
+       write_buf = kmalloc(RT5514_SPI_BUF_LEN + 6, GFP_KERNEL);
+
+       if (write_buf == NULL)
+               return -ENOMEM;
+
+       while (offset < len) {
+               if (offset + RT5514_SPI_BUF_LEN <= len)
+                       end = RT5514_SPI_BUF_LEN;
+               else
+                       end = len % RT5514_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] = txbuf[offset + i + 0];
+                       write_buf[i + 11] = txbuf[offset + i + 1];
+                       write_buf[i + 10] = txbuf[offset + i + 2];
+                       write_buf[i +  9] = txbuf[offset + i + 3];
+                       write_buf[i +  8] = txbuf[offset + i + 4];
+                       write_buf[i +  7] = txbuf[offset + i + 5];
+                       write_buf[i +  6] = txbuf[offset + i + 6];
+                       write_buf[i +  5] = txbuf[offset + i + 7];
+               }
+
+               write_buf[end + 5] = spi_cmd;
+
+               spi_write(rt5514_spi, write_buf, end + 6);
+
+               offset += RT5514_SPI_BUF_LEN;
+       }
+
+       kfree(write_buf);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rt5514_spi_burst_write);
+
+static int rt5514_spi_probe(struct spi_device *spi)
+{
+       int ret;
+
+       rt5514_spi = spi;
+
+       ret = devm_snd_soc_register_platform(&spi->dev, &rt5514_spi_platform);
+       if (ret < 0) {
+               dev_err(&spi->dev, "Failed to register platform.\n");
+               return ret;
+       }
+
+       ret = devm_snd_soc_register_component(&spi->dev,
+                                             &rt5514_spi_dai_component,
+                                             &rt5514_spi_dai, 1);
+       if (ret < 0) {
+               dev_err(&spi->dev, "Failed to register component.\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static const struct of_device_id rt5514_of_match[] = {
+       { .compatible = "realtek,rt5514", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rt5514_of_match);
+
+static struct spi_driver rt5514_spi_driver = {
+       .driver = {
+               .name = "rt5514",
+               .of_match_table = of_match_ptr(rt5514_of_match),
+       },
+       .probe = rt5514_spi_probe,
+};
+module_spi_driver(rt5514_spi_driver);
+
+MODULE_DESCRIPTION("RT5514 SPI driver");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5514-spi.h b/sound/soc/codecs/rt5514-spi.h
new file mode 100644 (file)
index 0000000..f69b1cd
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * rt5514-spi.h  --  RT5514 driver
+ *
+ * Copyright 2015 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __RT5514_SPI_H__
+#define __RT5514_SPI_H__
+
+/**
+ * RT5514_SPI_BUF_LEN is the buffer size of SPI master controller.
+*/
+#define RT5514_SPI_BUF_LEN             240
+
+#define RT5514_BUFFER_VOICE_BASE       0x18001034
+#define RT5514_BUFFER_VOICE_LIMIT      0x18001038
+#define RT5514_BUFFER_VOICE_RP         0x1800103c
+#define RT5514_BUFFER_VOICE_SIZE       0x18001040
+
+/* SPI Command */
+enum {
+       RT5514_SPI_CMD_16_READ = 0,
+       RT5514_SPI_CMD_16_WRITE,
+       RT5514_SPI_CMD_32_READ,
+       RT5514_SPI_CMD_32_WRITE,
+       RT5514_SPI_CMD_BURST_READ,
+       RT5514_SPI_CMD_BURST_WRITE,
+};
+
+int rt5514_spi_burst_read(unsigned int addr, u8 *rxbuf, size_t len);
+int rt5514_spi_burst_write(u32 addr, const u8 *txbuf, size_t len);
+
+#endif /* __RT5514_SPI_H__ */
index 879bf60f49658d63ef0cfaec82f561f940c5c82f..7162f05101d9e8859b4451aa20af7e3233b9270a 100644 (file)
@@ -30,6 +30,9 @@
 
 #include "rl6231.h"
 #include "rt5514.h"
+#if defined(CONFIG_SND_SOC_RT5514_SPI)
+#include "rt5514-spi.h"
+#endif
 
 static const struct reg_sequence rt5514_i2c_patch[] = {
        {0x1800101c, 0x00000000},
@@ -110,6 +113,35 @@ static const struct reg_default rt5514_reg[] = {
        {RT5514_VENDOR_ID2,             0x10ec5514},
 };
 
+static void rt5514_enable_dsp_prepare(struct rt5514_priv *rt5514)
+{
+       /* Reset */
+       regmap_write(rt5514->i2c_regmap, 0x18002000, 0x000010ec);
+       /* LDO_I_limit */
+       regmap_write(rt5514->i2c_regmap, 0x18002200, 0x00028604);
+       /* I2C bypass enable */
+       regmap_write(rt5514->i2c_regmap, 0xfafafafa, 0x00000001);
+       /* mini-core reset */
+       regmap_write(rt5514->i2c_regmap, 0x18002f00, 0x0005514b);
+       regmap_write(rt5514->i2c_regmap, 0x18002f00, 0x00055149);
+       /* I2C bypass disable */
+       regmap_write(rt5514->i2c_regmap, 0xfafafafa, 0x00000000);
+       /* PIN config */
+       regmap_write(rt5514->i2c_regmap, 0x18002070, 0x00000040);
+       /* PLL3(QN)=RCOSC*(10+2) */
+       regmap_write(rt5514->i2c_regmap, 0x18002240, 0x0000000a);
+       /* PLL3 source=RCOSC, fsi=rt_clk */
+       regmap_write(rt5514->i2c_regmap, 0x18002100, 0x0000000b);
+       /* Power on RCOSC, pll3 */
+       regmap_write(rt5514->i2c_regmap, 0x18002004, 0x00808b81);
+       /* DSP clk source = pll3, ENABLE DSP clk */
+       regmap_write(rt5514->i2c_regmap, 0x18002f08, 0x00000005);
+       /* Enable DSP clk auto switch */
+       regmap_write(rt5514->i2c_regmap, 0x18001114, 0x00000001);
+       /* Reduce DSP power */
+       regmap_write(rt5514->i2c_regmap, 0x18001118, 0x00000001);
+}
+
 static bool rt5514_volatile_register(struct device *dev, unsigned int reg)
 {
        switch (reg) {
@@ -248,6 +280,74 @@ static const DECLARE_TLV_DB_RANGE(bst_tlv,
 
 static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
 
+static int rt5514_dsp_voice_wake_up_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+
+       ucontrol->value.integer.value[0] = rt5514->dsp_enabled;
+
+       return 0;
+}
+
+static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+       struct snd_soc_codec *codec = rt5514->codec;
+       const struct firmware *fw = NULL;
+
+       if (ucontrol->value.integer.value[0] == rt5514->dsp_enabled)
+               return 0;
+
+       if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+               rt5514->dsp_enabled = ucontrol->value.integer.value[0];
+
+               if (rt5514->dsp_enabled) {
+                       rt5514_enable_dsp_prepare(rt5514);
+
+                       request_firmware(&fw, RT5514_FIRMWARE1, codec->dev);
+                       if (fw) {
+#if defined(CONFIG_SND_SOC_RT5514_SPI)
+                               rt5514_spi_burst_write(0x4ff60000, fw->data,
+                                       ((fw->size/8)+1)*8);
+#else
+                               dev_err(codec->dev, "There is no SPI driver for"
+                                       " loading the firmware\n");
+#endif
+                               release_firmware(fw);
+                               fw = NULL;
+                       }
+
+                       request_firmware(&fw, RT5514_FIRMWARE2, codec->dev);
+                       if (fw) {
+#if defined(CONFIG_SND_SOC_RT5514_SPI)
+                               rt5514_spi_burst_write(0x4ffc0000, fw->data,
+                                       ((fw->size/8)+1)*8);
+#else
+                               dev_err(codec->dev, "There is no SPI driver for"
+                                       " loading the firmware\n");
+#endif
+                               release_firmware(fw);
+                               fw = NULL;
+                       }
+
+                       /* DSP run */
+                       regmap_write(rt5514->i2c_regmap, 0x18002f00,
+                               0x00055148);
+               } else {
+                       regmap_multi_reg_write(rt5514->i2c_regmap,
+                               rt5514_i2c_patch, ARRAY_SIZE(rt5514_i2c_patch));
+                       regcache_mark_dirty(rt5514->regmap);
+                       regcache_sync(rt5514->regmap);
+               }
+       }
+
+       return 0;
+}
+
 static const struct snd_kcontrol_new rt5514_snd_controls[] = {
        SOC_DOUBLE_TLV("MIC Boost Volume", RT5514_ANA_CTRL_MICBST,
                RT5514_SEL_BSTL_SFT, RT5514_SEL_BSTR_SFT, 8, 0, bst_tlv),
@@ -257,6 +357,8 @@ static const struct snd_kcontrol_new rt5514_snd_controls[] = {
        SOC_DOUBLE_R_TLV("ADC2 Capture Volume", RT5514_DOWNFILTER1_CTRL1,
                RT5514_DOWNFILTER1_CTRL2, RT5514_AD_GAIN_SFT, 127, 0,
                adc_vol_tlv),
+       SOC_SINGLE_EXT("DSP Voice Wake Up", SND_SOC_NOPM, 0, 1, 0,
+               rt5514_dsp_voice_wake_up_get, rt5514_dsp_voice_wake_up_put),
 };
 
 /* ADC Mixer*/
@@ -365,6 +467,35 @@ static int rt5514_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
                return 0;
 }
 
+static int rt5514_pre_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               /**
+                * If the DSP is enabled in start of recording, the DSP
+                * should be disabled, and sync back to normal recording
+                * settings to make sure recording properly.
+               */
+               if (rt5514->dsp_enabled) {
+                       rt5514->dsp_enabled = 0;
+                       regmap_multi_reg_write(rt5514->i2c_regmap,
+                               rt5514_i2c_patch, ARRAY_SIZE(rt5514_i2c_patch));
+                       regcache_mark_dirty(rt5514->regmap);
+                       regcache_sync(rt5514->regmap);
+               }
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
 static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = {
        /* Input Lines */
        SND_SOC_DAPM_INPUT("DMIC1L"),
@@ -472,6 +603,8 @@ static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = {
 
        /* Audio Interface */
        SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+       SND_SOC_DAPM_PRE("DAPM Pre", rt5514_pre_event),
 };
 
 static const struct snd_soc_dapm_route rt5514_dapm_routes[] = {
@@ -799,10 +932,41 @@ static int rt5514_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
        return 0;
 }
 
+static int rt5514_set_bias_level(struct snd_soc_codec *codec,
+                       enum snd_soc_bias_level level)
+{
+       struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       switch (level) {
+       case SND_SOC_BIAS_PREPARE:
+               if (IS_ERR(rt5514->mclk))
+                       break;
+
+               if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) {
+                       clk_disable_unprepare(rt5514->mclk);
+               } else {
+                       ret = clk_prepare_enable(rt5514->mclk);
+                       if (ret)
+                               return ret;
+               }
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
 static int rt5514_probe(struct snd_soc_codec *codec)
 {
        struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
 
+       rt5514->mclk = devm_clk_get(codec->dev, "mclk");
+       if (PTR_ERR(rt5514->mclk) == -EPROBE_DEFER)
+               return -EPROBE_DEFER;
+
        rt5514->codec = codec;
 
        return 0;
@@ -858,6 +1022,7 @@ struct snd_soc_dai_driver rt5514_dai[] = {
 static struct snd_soc_codec_driver soc_codec_dev_rt5514 = {
        .probe = rt5514_probe,
        .idle_bias_off = true,
+       .set_bias_level = rt5514_set_bias_level,
        .controls = rt5514_snd_controls,
        .num_controls = ARRAY_SIZE(rt5514_snd_controls),
        .dapm_widgets = rt5514_dapm_widgets,
@@ -871,7 +1036,6 @@ static const struct regmap_config rt5514_i2c_regmap = {
        .reg_bits = 32,
        .val_bits = 32,
 
-       .max_register = RT5514_DSP_MAPPING | RT5514_VENDOR_ID2,
        .readable_reg = rt5514_i2c_readable_register,
 
        .cache_type = REGCACHE_NONE,
@@ -944,7 +1108,7 @@ static int rt5514_i2c_probe(struct i2c_client *i2c,
                return -ENODEV;
        }
 
-       ret = regmap_register_patch(rt5514->i2c_regmap, rt5514_i2c_patch,
+       ret = regmap_multi_reg_write(rt5514->i2c_regmap, rt5514_i2c_patch,
                                    ARRAY_SIZE(rt5514_i2c_patch));
        if (ret != 0)
                dev_warn(&i2c->dev, "Failed to apply i2c_regmap patch: %d\n",
index 6ad8a612f659a61028864d0923abc8ceeaeca6b2..68883c68e999cc42ce64ff15cd88720936352c35 100644 (file)
@@ -12,6 +12,8 @@
 #ifndef __RT5514_H__
 #define __RT5514_H__
 
+#include <linux/clk.h>
+
 #define RT5514_DEVICE_ID                       0x10ec5514
 
 #define RT5514_RESET                           0x2000
 #define RT5514_PLL_INP_MAX                     40000000
 #define RT5514_PLL_INP_MIN                     256000
 
+#define RT5514_FIRMWARE1       "rt5514_dsp_fw1.bin"
+#define RT5514_FIRMWARE2       "rt5514_dsp_fw2.bin"
+
 /* System Clock Source */
 enum {
        RT5514_SCLK_S_MCLK,
@@ -240,6 +245,7 @@ enum {
 struct rt5514_priv {
        struct snd_soc_codec *codec;
        struct regmap *i2c_regmap, *regmap;
+       struct clk *mclk;
        int sysclk;
        int sysclk_src;
        int lrck;
@@ -247,6 +253,7 @@ struct rt5514_priv {
        int pll_src;
        int pll_in;
        int pll_out;
+       int dsp_enabled;
 };
 
 #endif /* __RT5514_H__ */
index d70847c9eeb03d8aeba2c316eadbaf68aed237bc..490bfe66134636ecf3efaaee3a5c741ce87e5b1e 100644 (file)
@@ -63,6 +63,7 @@ static const struct reg_sequence init_list[] = {
        {RT5645_PR_BASE + 0x20, 0x611f},
        {RT5645_PR_BASE + 0x21, 0x4040},
        {RT5645_PR_BASE + 0x23, 0x0004},
+       {RT5645_ASRC_4, 0x0120},
 };
 
 static const struct reg_sequence rt5650_init_list[] = {
@@ -157,7 +158,7 @@ static const struct reg_default rt5645_reg[] = {
        { 0x83, 0x0000 },
        { 0x84, 0x0000 },
        { 0x85, 0x0000 },
-       { 0x8a, 0x0000 },
+       { 0x8a, 0x0120 },
        { 0x8e, 0x0004 },
        { 0x8f, 0x1100 },
        { 0x90, 0x0646 },
@@ -314,7 +315,7 @@ static const struct reg_default rt5650_reg[] = {
        { 0x83, 0x0000 },
        { 0x84, 0x0000 },
        { 0x85, 0x0000 },
-       { 0x8a, 0x0000 },
+       { 0x8a, 0x0120 },
        { 0x8e, 0x0004 },
        { 0x8f, 0x1100 },
        { 0x90, 0x0646 },
@@ -440,6 +441,7 @@ static bool rt5645_volatile_register(struct device *dev, unsigned int reg)
 
        switch (reg) {
        case RT5645_RESET:
+       case RT5645_PRIV_INDEX:
        case RT5645_PRIV_DATA:
        case RT5645_IN1_CTRL1:
        case RT5645_IN1_CTRL2:
@@ -740,6 +742,14 @@ static int rt5645_spk_put_volsw(struct snd_kcontrol *kcontrol,
        return ret;
 }
 
+static const char * const rt5645_dac1_vol_ctrl_mode_text[] = {
+       "immediately", "zero crossing", "soft ramp"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5645_dac1_vol_ctrl_mode, RT5645_PR_BASE,
+       RT5645_DA1_ZDET_SFT, rt5645_dac1_vol_ctrl_mode_text);
+
 static const struct snd_kcontrol_new rt5645_snd_controls[] = {
        /* Speaker Output Volume */
        SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL,
@@ -806,6 +816,9 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
        SOC_SINGLE("I2S2 Func Switch", RT5645_GPIO_CTRL1, RT5645_I2S2_SEL_SFT,
                1, 1),
        RT5645_HWEQ("Speaker HWEQ"),
+
+       /* Digital Soft Volume Control */
+       SOC_ENUM("DAC1 Digital Volume Control Func", rt5645_dac1_vol_ctrl_mode),
 };
 
 /**
@@ -3531,6 +3544,7 @@ MODULE_DEVICE_TABLE(i2c, rt5645_i2c_id);
 static const struct acpi_device_id rt5645_acpi_match[] = {
        { "10EC5645", 0 },
        { "10EC5650", 0 },
+       { "10EC5640", 0 },
        {},
 };
 MODULE_DEVICE_TABLE(acpi, rt5645_acpi_match);
@@ -3561,6 +3575,12 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "Setzer"),
                },
        },
+       {
+               .ident = "Microsoft Surface 3",
+               .matches = {
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"),
+               },
+       },
        { }
 };
 
index 205e0715c99abad45e771c0504b92a512f5b6adf..cfc5f97549eb656b9b861a612e0d60b40c2e93be 100644 (file)
 
 
 /* Codec Private Register definition */
+/* DAC ADC Digital Volume (0x00) */
+#define RT5645_DA1_ZDET_SFT                    6
+
 /* 3D Speaker Control (0x63) */
 #define RT5645_3D_SPK_MASK                     (0x1 << 15)
 #define RT5645_3D_SPK_SFT                      15
index 0af5ddbef1daaa0b4fa7dbfc89c72c52b6a84bfd..8ef467f64f03b309082c66f0d43007eb39cb3aba 100644 (file)
@@ -55,6 +55,7 @@ static const struct reg_sequence init_list[] = {
        { RT5670_PR_BASE + 0x14, 0x9a8a },
        { RT5670_PR_BASE + 0x38, 0x3ba1 },
        { RT5670_PR_BASE + 0x3d, 0x3640 },
+       { 0x8a, 0x0123 },
 };
 
 static const struct reg_default rt5670_reg[] = {
@@ -131,7 +132,7 @@ static const struct reg_default rt5670_reg[] = {
        { 0x87, 0x0000 },
        { 0x88, 0x0000 },
        { 0x89, 0x0000 },
-       { 0x8a, 0x0000 },
+       { 0x8a, 0x0123 },
        { 0x8b, 0x0000 },
        { 0x8c, 0x0003 },
        { 0x8d, 0x0000 },
index 08b40460663c2f28bd450261889cd365116f0bf9..527b759c1562dab6229f1d4c345d1ac21530c62b 100644 (file)
@@ -38,7 +38,6 @@
 /* default value of sgtl5000 registers */
 static const struct reg_default sgtl5000_reg_defaults[] = {
        { SGTL5000_CHIP_DIG_POWER,              0x0000 },
-       { SGTL5000_CHIP_CLK_CTRL,               0x0008 },
        { SGTL5000_CHIP_I2S_CTRL,               0x0010 },
        { SGTL5000_CHIP_SSS_CTRL,               0x0010 },
        { SGTL5000_CHIP_ADCDAC_CTRL,            0x020c },
@@ -47,12 +46,10 @@ static const struct reg_default sgtl5000_reg_defaults[] = {
        { SGTL5000_CHIP_ANA_ADC_CTRL,           0x0000 },
        { SGTL5000_CHIP_ANA_HP_CTRL,            0x1818 },
        { SGTL5000_CHIP_ANA_CTRL,               0x0111 },
-       { SGTL5000_CHIP_LINREG_CTRL,            0x0000 },
        { SGTL5000_CHIP_REF_CTRL,               0x0000 },
        { SGTL5000_CHIP_MIC_CTRL,               0x0000 },
        { SGTL5000_CHIP_LINE_OUT_CTRL,          0x0000 },
        { SGTL5000_CHIP_LINE_OUT_VOL,           0x0404 },
-       { SGTL5000_CHIP_ANA_POWER,              0x7060 },
        { SGTL5000_CHIP_PLL_CTRL,               0x5000 },
        { SGTL5000_CHIP_CLK_TOP_CTRL,           0x0000 },
        { SGTL5000_CHIP_ANA_STATUS,             0x0000 },
@@ -92,35 +89,8 @@ static const char *supply_names[SGTL5000_SUPPLY_NUM] = {
        "VDDD"
 };
 
-#define LDO_CONSUMER_NAME      "VDDD_LDO"
 #define LDO_VOLTAGE            1200000
-
-static struct regulator_consumer_supply ldo_consumer[] = {
-       REGULATOR_SUPPLY(LDO_CONSUMER_NAME, NULL),
-};
-
-static struct regulator_init_data ldo_init_data = {
-       .constraints = {
-               .min_uV                 = 1200000,
-               .max_uV                 = 1200000,
-               .valid_modes_mask       = REGULATOR_MODE_NORMAL,
-               .valid_ops_mask         = REGULATOR_CHANGE_STATUS,
-       },
-       .num_consumer_supplies = 1,
-       .consumer_supplies = &ldo_consumer[0],
-};
-
-/*
- * sgtl5000 internal ldo regulator,
- * enabled when VDDD not provided
- */
-struct ldo_regulator {
-       struct regulator_desc desc;
-       struct regulator_dev *dev;
-       int voltage;
-       void *codec_data;
-       bool enabled;
-};
+#define LINREG_VDDD    ((1600 - LDO_VOLTAGE / 1000) / 50)
 
 enum sgtl5000_micbias_resistor {
        SGTL5000_MICBIAS_OFF = 0,
@@ -135,7 +105,7 @@ struct sgtl5000_priv {
        int master;     /* i2s master or not */
        int fmt;        /* i2s data format */
        struct regulator_bulk_data supplies[SGTL5000_SUPPLY_NUM];
-       struct ldo_regulator *ldo;
+       int num_supplies;
        struct regmap *regmap;
        struct clk *mclk;
        int revision;
@@ -415,6 +385,9 @@ static const DECLARE_TLV_DB_RANGE(mic_gain_tlv,
 /* tlv for hp volume, -51.5db to 12.0db, step .5db */
 static const DECLARE_TLV_DB_SCALE(headphone_volume, -5150, 50, 0);
 
+/* tlv for lineout volume, 31 steps of .5db each */
+static const DECLARE_TLV_DB_SCALE(lineout_volume, -1550, 50, 0);
+
 static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
        /* SOC_DOUBLE_S8_TLV with invert */
        {
@@ -443,6 +416,13 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
 
        SOC_SINGLE_TLV("Mic Volume", SGTL5000_CHIP_MIC_CTRL,
                        0, 3, 0, mic_gain_tlv),
+
+       SOC_DOUBLE_TLV("Lineout Playback Volume",
+                       SGTL5000_CHIP_LINE_OUT_VOL,
+                       SGTL5000_LINE_OUT_VOL_LEFT_SHIFT,
+                       SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT,
+                       0x1f, 1,
+                       lineout_volume),
 };
 
 /* mute the codec used by alsa core */
@@ -778,155 +758,6 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
-#ifdef CONFIG_REGULATOR
-static int ldo_regulator_is_enabled(struct regulator_dev *dev)
-{
-       struct ldo_regulator *ldo = rdev_get_drvdata(dev);
-
-       return ldo->enabled;
-}
-
-static int ldo_regulator_enable(struct regulator_dev *dev)
-{
-       struct ldo_regulator *ldo = rdev_get_drvdata(dev);
-       struct snd_soc_codec *codec = (struct snd_soc_codec *)ldo->codec_data;
-       int reg;
-
-       if (ldo_regulator_is_enabled(dev))
-               return 0;
-
-       /* set regulator value firstly */
-       reg = (1600 - ldo->voltage / 1000) / 50;
-       reg = clamp(reg, 0x0, 0xf);
-
-       /* amend the voltage value, unit: uV */
-       ldo->voltage = (1600 - reg * 50) * 1000;
-
-       /* set voltage to register */
-       snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL,
-                               SGTL5000_LINREG_VDDD_MASK, reg);
-
-       snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
-                               SGTL5000_LINEREG_D_POWERUP,
-                               SGTL5000_LINEREG_D_POWERUP);
-
-       /* when internal ldo is enabled, simple digital power can be disabled */
-       snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
-                               SGTL5000_LINREG_SIMPLE_POWERUP,
-                               0);
-
-       ldo->enabled = 1;
-       return 0;
-}
-
-static int ldo_regulator_disable(struct regulator_dev *dev)
-{
-       struct ldo_regulator *ldo = rdev_get_drvdata(dev);
-       struct snd_soc_codec *codec = (struct snd_soc_codec *)ldo->codec_data;
-
-       snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
-                               SGTL5000_LINEREG_D_POWERUP,
-                               0);
-
-       /* clear voltage info */
-       snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL,
-                               SGTL5000_LINREG_VDDD_MASK, 0);
-
-       ldo->enabled = 0;
-
-       return 0;
-}
-
-static int ldo_regulator_get_voltage(struct regulator_dev *dev)
-{
-       struct ldo_regulator *ldo = rdev_get_drvdata(dev);
-
-       return ldo->voltage;
-}
-
-static struct regulator_ops ldo_regulator_ops = {
-       .is_enabled = ldo_regulator_is_enabled,
-       .enable = ldo_regulator_enable,
-       .disable = ldo_regulator_disable,
-       .get_voltage = ldo_regulator_get_voltage,
-};
-
-static int ldo_regulator_register(struct snd_soc_codec *codec,
-                               struct regulator_init_data *init_data,
-                               int voltage)
-{
-       struct ldo_regulator *ldo;
-       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
-       struct regulator_config config = { };
-
-       ldo = kzalloc(sizeof(struct ldo_regulator), GFP_KERNEL);
-
-       if (!ldo)
-               return -ENOMEM;
-
-       ldo->desc.name = kstrdup(dev_name(codec->dev), GFP_KERNEL);
-       if (!ldo->desc.name) {
-               kfree(ldo);
-               dev_err(codec->dev, "failed to allocate decs name memory\n");
-               return -ENOMEM;
-       }
-
-       ldo->desc.type  = REGULATOR_VOLTAGE;
-       ldo->desc.owner = THIS_MODULE;
-       ldo->desc.ops   = &ldo_regulator_ops;
-       ldo->desc.n_voltages = 1;
-
-       ldo->codec_data = codec;
-       ldo->voltage = voltage;
-
-       config.dev = codec->dev;
-       config.driver_data = ldo;
-       config.init_data = init_data;
-
-       ldo->dev = regulator_register(&ldo->desc, &config);
-       if (IS_ERR(ldo->dev)) {
-               int ret = PTR_ERR(ldo->dev);
-
-               dev_err(codec->dev, "failed to register regulator\n");
-               kfree(ldo->desc.name);
-               kfree(ldo);
-
-               return ret;
-       }
-       sgtl5000->ldo = ldo;
-
-       return 0;
-}
-
-static int ldo_regulator_remove(struct snd_soc_codec *codec)
-{
-       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
-       struct ldo_regulator *ldo = sgtl5000->ldo;
-
-       if (!ldo)
-               return 0;
-
-       regulator_unregister(ldo->dev);
-       kfree(ldo->desc.name);
-       kfree(ldo);
-
-       return 0;
-}
-#else
-static int ldo_regulator_register(struct snd_soc_codec *codec,
-                               struct regulator_init_data *init_data,
-                               int voltage)
-{
-       dev_err(codec->dev, "this setup needs regulator support in the kernel\n");
-       return -EINVAL;
-}
-
-static int ldo_regulator_remove(struct snd_soc_codec *codec)
-{
-       return 0;
-}
-#endif
-
 /*
  * set dac bias
  * common state changes:
@@ -940,42 +771,17 @@ static int ldo_regulator_remove(struct snd_soc_codec *codec)
 static int sgtl5000_set_bias_level(struct snd_soc_codec *codec,
                                   enum snd_soc_bias_level level)
 {
-       int ret;
-       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
-
        switch (level) {
        case SND_SOC_BIAS_ON:
        case SND_SOC_BIAS_PREPARE:
-               break;
        case SND_SOC_BIAS_STANDBY:
-               if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
-                       ret = regulator_bulk_enable(
-                                               ARRAY_SIZE(sgtl5000->supplies),
-                                               sgtl5000->supplies);
-                       if (ret)
-                               return ret;
-                       udelay(10);
-
-                       regcache_cache_only(sgtl5000->regmap, false);
-
-                       ret = regcache_sync(sgtl5000->regmap);
-                       if (ret != 0) {
-                               dev_err(codec->dev,
-                                       "Failed to restore cache: %d\n", ret);
-
-                               regcache_cache_only(sgtl5000->regmap, true);
-                               regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
-                                                      sgtl5000->supplies);
-
-                               return ret;
-                       }
-               }
-
+               snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+                                   SGTL5000_REFTOP_POWERUP,
+                                   SGTL5000_REFTOP_POWERUP);
                break;
        case SND_SOC_BIAS_OFF:
-               regcache_cache_only(sgtl5000->regmap, true);
-               regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
-                                       sgtl5000->supplies);
+               snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+                                   SGTL5000_REFTOP_POWERUP, 0);
                break;
        }
 
@@ -1113,7 +919,6 @@ static const u8 vol_quot_table[] = {
  * and should be set according to:
  * 1. vddd provided by external or not
  * 2. vdda and vddio voltage value. > 3.1v or not
- * 3. chip revision >=0x11 or not. If >=0x11, not use external vddd.
  */
 static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
 {
@@ -1131,7 +936,9 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
 
        vdda  = regulator_get_voltage(sgtl5000->supplies[VDDA].consumer);
        vddio = regulator_get_voltage(sgtl5000->supplies[VDDIO].consumer);
-       vddd  = regulator_get_voltage(sgtl5000->supplies[VDDD].consumer);
+       vddd  = (sgtl5000->num_supplies > VDDD)
+               ? regulator_get_voltage(sgtl5000->supplies[VDDD].consumer)
+               : LDO_VOLTAGE;
 
        vdda  = vdda / 1000;
        vddio = vddio / 1000;
@@ -1178,25 +985,6 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
 
        snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER, ana_pwr);
 
-       /* set voltage to register */
-       snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL,
-                               SGTL5000_LINREG_VDDD_MASK, 0x8);
-
-       /*
-        * if vddd linear reg has been enabled,
-        * simple digital supply should be clear to get
-        * proper VDDD voltage.
-        */
-       if (ana_pwr & SGTL5000_LINEREG_D_POWERUP)
-               snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
-                               SGTL5000_LINREG_SIMPLE_POWERUP,
-                               0);
-       else
-               snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
-                               SGTL5000_LINREG_SIMPLE_POWERUP |
-                               SGTL5000_STARTUP_POWERUP,
-                               0);
-
        /*
         * set ADC/DAC VAG to vdda / 2,
         * should stay in range (0.8v, 1.575v)
@@ -1256,78 +1044,43 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
        return 0;
 }
 
-static int sgtl5000_replace_vddd_with_ldo(struct snd_soc_codec *codec)
-{
-       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
-       int ret;
-
-       /* set internal ldo to 1.2v */
-       ret = ldo_regulator_register(codec, &ldo_init_data, LDO_VOLTAGE);
-       if (ret) {
-               dev_err(codec->dev,
-                       "Failed to register vddd internal supplies: %d\n", ret);
-               return ret;
-       }
-
-       sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME;
-
-       dev_info(codec->dev, "Using internal LDO instead of VDDD\n");
-       return 0;
-}
-
-static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
+static int sgtl5000_enable_regulators(struct i2c_client *client)
 {
        int ret;
        int i;
        int external_vddd = 0;
-       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
        struct regulator *vddd;
+       struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client);
 
        for (i = 0; i < ARRAY_SIZE(sgtl5000->supplies); i++)
                sgtl5000->supplies[i].supply = supply_names[i];
 
-       /* External VDDD only works before revision 0x11 */
-       if (sgtl5000->revision < 0x11) {
-               vddd = regulator_get_optional(codec->dev, "VDDD");
-               if (IS_ERR(vddd)) {
-                       /* See if it's just not registered yet */
-                       if (PTR_ERR(vddd) == -EPROBE_DEFER)
-                               return -EPROBE_DEFER;
-               } else {
-                       external_vddd = 1;
-                       regulator_put(vddd);
-               }
-       }
-
-       if (!external_vddd) {
-               ret = sgtl5000_replace_vddd_with_ldo(codec);
-               if (ret)
-                       return ret;
+       vddd = regulator_get_optional(&client->dev, "VDDD");
+       if (IS_ERR(vddd)) {
+               /* See if it's just not registered yet */
+               if (PTR_ERR(vddd) == -EPROBE_DEFER)
+                       return -EPROBE_DEFER;
+       } else {
+               external_vddd = 1;
+               regulator_put(vddd);
        }
 
-       ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies),
+       sgtl5000->num_supplies = ARRAY_SIZE(sgtl5000->supplies)
+                                - 1 + external_vddd;
+       ret = regulator_bulk_get(&client->dev, sgtl5000->num_supplies,
                                 sgtl5000->supplies);
        if (ret)
-               goto err_ldo_remove;
-
-       ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies),
-                                       sgtl5000->supplies);
-       if (ret)
-               goto err_regulator_free;
-
-       /* wait for all power rails bring up */
-       udelay(10);
+               return ret;
 
-       return 0;
+       ret = regulator_bulk_enable(sgtl5000->num_supplies,
+                                   sgtl5000->supplies);
+       if (!ret)
+               usleep_range(10, 20);
+       else
+               regulator_bulk_free(sgtl5000->num_supplies,
+                                   sgtl5000->supplies);
 
-err_regulator_free:
-       regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
-                               sgtl5000->supplies);
-err_ldo_remove:
-       if (!external_vddd)
-               ldo_regulator_remove(codec);
        return ret;
-
 }
 
 static int sgtl5000_probe(struct snd_soc_codec *codec)
@@ -1335,10 +1088,6 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
        int ret;
        struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
 
-       ret = sgtl5000_enable_regulators(codec);
-       if (ret)
-               return ret;
-
        /* power up sgtl5000 */
        ret = sgtl5000_set_power_regs(codec);
        if (ret)
@@ -1389,25 +1138,11 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
        return 0;
 
 err:
-       regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
-                                               sgtl5000->supplies);
-       regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
-                               sgtl5000->supplies);
-       ldo_regulator_remove(codec);
-
        return ret;
 }
 
 static int sgtl5000_remove(struct snd_soc_codec *codec)
 {
-       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
-
-       regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
-                                               sgtl5000->supplies);
-       regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
-                               sgtl5000->supplies);
-       ldo_regulator_remove(codec);
-
        return 0;
 }
 
@@ -1448,8 +1183,9 @@ static const struct regmap_config sgtl5000_regmap = {
  * and avoid problems like, not being able to probe after an audio playback
  * followed by a system reset or a 'reboot' command in Linux
  */
-static int sgtl5000_fill_defaults(struct sgtl5000_priv *sgtl5000)
+static void sgtl5000_fill_defaults(struct i2c_client *client)
 {
+       struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client);
        int i, ret, val, index;
 
        for (i = 0; i < ARRAY_SIZE(sgtl5000_reg_defaults); i++) {
@@ -1457,10 +1193,10 @@ static int sgtl5000_fill_defaults(struct sgtl5000_priv *sgtl5000)
                index = sgtl5000_reg_defaults[i].reg;
                ret = regmap_write(sgtl5000->regmap, index, val);
                if (ret)
-                       return ret;
+                       dev_err(&client->dev,
+                               "%s: error %d setting reg 0x%02x to 0x%04x\n",
+                               __func__, ret, index, val);
        }
-
-       return 0;
 }
 
 static int sgtl5000_i2c_probe(struct i2c_client *client,
@@ -1470,16 +1206,23 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
        int ret, reg, rev;
        struct device_node *np = client->dev.of_node;
        u32 value;
+       u16 ana_pwr;
 
        sgtl5000 = devm_kzalloc(&client->dev, sizeof(*sgtl5000), GFP_KERNEL);
        if (!sgtl5000)
                return -ENOMEM;
 
+       i2c_set_clientdata(client, sgtl5000);
+
+       ret = sgtl5000_enable_regulators(client);
+       if (ret)
+               return ret;
+
        sgtl5000->regmap = devm_regmap_init_i2c(client, &sgtl5000_regmap);
        if (IS_ERR(sgtl5000->regmap)) {
                ret = PTR_ERR(sgtl5000->regmap);
                dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret);
-               return ret;
+               goto disable_regs;
        }
 
        sgtl5000->mclk = devm_clk_get(&client->dev, NULL);
@@ -1488,21 +1231,25 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
                dev_err(&client->dev, "Failed to get mclock: %d\n", ret);
                /* Defer the probe to see if the clk will be provided later */
                if (ret == -ENOENT)
-                       return -EPROBE_DEFER;
-               return ret;
+                       ret = -EPROBE_DEFER;
+               goto disable_regs;
        }
 
        ret = clk_prepare_enable(sgtl5000->mclk);
-       if (ret)
-               return ret;
+       if (ret) {
+               dev_err(&client->dev, "Error enabling clock %d\n", ret);
+               goto disable_regs;
+       }
 
        /* Need 8 clocks before I2C accesses */
        udelay(1);
 
        /* read chip information */
        ret = regmap_read(sgtl5000->regmap, SGTL5000_CHIP_ID, &reg);
-       if (ret)
+       if (ret) {
+               dev_err(&client->dev, "Error reading chip id %d\n", ret);
                goto disable_clk;
+       }
 
        if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) !=
            SGTL5000_PARTID_PART_ID) {
@@ -1516,6 +1263,44 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
        dev_info(&client->dev, "sgtl5000 revision 0x%x\n", rev);
        sgtl5000->revision = rev;
 
+       /* reconfigure the clocks in case we're using the PLL */
+       ret = regmap_write(sgtl5000->regmap,
+                          SGTL5000_CHIP_CLK_CTRL,
+                          SGTL5000_CHIP_CLK_CTRL_DEFAULT);
+       if (ret)
+               dev_err(&client->dev,
+                       "Error %d initializing CHIP_CLK_CTRL\n", ret);
+
+       /* Follow section 2.2.1.1 of AN3663 */
+       ana_pwr = SGTL5000_ANA_POWER_DEFAULT;
+       if (sgtl5000->num_supplies <= VDDD) {
+               /* internal VDDD at 1.2V */
+               ret = regmap_update_bits(sgtl5000->regmap,
+                                        SGTL5000_CHIP_LINREG_CTRL,
+                                        SGTL5000_LINREG_VDDD_MASK,
+                                        LINREG_VDDD);
+               if (ret)
+                       dev_err(&client->dev,
+                               "Error %d setting LINREG_VDDD\n", ret);
+
+               ana_pwr |= SGTL5000_LINEREG_D_POWERUP;
+               dev_info(&client->dev,
+                        "Using internal LDO instead of VDDD: check ER1\n");
+       } else {
+               /* using external LDO for VDDD
+                * Clear startup powerup and simple powerup
+                * bits to save power
+                */
+               ana_pwr &= ~(SGTL5000_STARTUP_POWERUP
+                            | SGTL5000_LINREG_SIMPLE_POWERUP);
+               dev_dbg(&client->dev, "Using external VDDD\n");
+       }
+       ret = regmap_write(sgtl5000->regmap, SGTL5000_CHIP_ANA_POWER, ana_pwr);
+       if (ret)
+               dev_err(&client->dev,
+                       "Error %d setting CHIP_ANA_POWER to %04x\n",
+                       ret, ana_pwr);
+
        if (np) {
                if (!of_property_read_u32(np,
                        "micbias-resistor-k-ohms", &value)) {
@@ -1557,12 +1342,8 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
                }
        }
 
-       i2c_set_clientdata(client, sgtl5000);
-
        /* Ensure sgtl5000 will start with sane register values */
-       ret = sgtl5000_fill_defaults(sgtl5000);
-       if (ret)
-               goto disable_clk;
+       sgtl5000_fill_defaults(client);
 
        ret = snd_soc_register_codec(&client->dev,
                        &sgtl5000_driver, &sgtl5000_dai, 1);
@@ -1573,6 +1354,11 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
 
 disable_clk:
        clk_disable_unprepare(sgtl5000->mclk);
+
+disable_regs:
+       regulator_bulk_disable(sgtl5000->num_supplies, sgtl5000->supplies);
+       regulator_bulk_free(sgtl5000->num_supplies, sgtl5000->supplies);
+
        return ret;
 }
 
@@ -1582,6 +1368,9 @@ static int sgtl5000_i2c_remove(struct i2c_client *client)
 
        snd_soc_unregister_codec(&client->dev);
        clk_disable_unprepare(sgtl5000->mclk);
+       regulator_bulk_disable(sgtl5000->num_supplies, sgtl5000->supplies);
+       regulator_bulk_free(sgtl5000->num_supplies, sgtl5000->supplies);
+
        return 0;
 }
 
index 1c317de2617623438bd169d9ba7cb89623139794..22f3442af9826ce5868619bf993123bafdb0c82f 100644 (file)
@@ -92,6 +92,7 @@
 /*
  * SGTL5000_CHIP_CLK_CTRL
  */
+#define SGTL5000_CHIP_CLK_CTRL_DEFAULT         0x0008
 #define SGTL5000_RATE_MODE_MASK                        0x0030
 #define SGTL5000_RATE_MODE_SHIFT               4
 #define SGTL5000_RATE_MODE_WIDTH               2
 /*
  * SGTL5000_CHIP_ANA_POWER
  */
+#define SGTL5000_ANA_POWER_DEFAULT             0x7060
 #define SGTL5000_DAC_STEREO                    0x4000
 #define SGTL5000_LINREG_SIMPLE_POWERUP         0x2000
 #define SGTL5000_STARTUP_POWERUP               0x1000
index b8d19b77bde981a86860f4fbb67364271a014e3b..d8baca3f8413d19bcdefd44a8917769bf484766f 100644 (file)
@@ -28,6 +28,7 @@
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/tlv.h>
+#include <asm/unaligned.h>
 
 #include "tas571x.h"
 
@@ -63,6 +64,10 @@ static int tas571x_register_size(struct tas571x_private *priv, unsigned int reg)
        case TAS571X_INPUT_MUX_REG:
        case TAS571X_CH4_SRC_SELECT_REG:
        case TAS571X_PWM_MUX_REG:
+       case TAS5717_CH1_RIGHT_CH_MIX_REG:
+       case TAS5717_CH1_LEFT_CH_MIX_REG:
+       case TAS5717_CH2_LEFT_CH_MIX_REG:
+       case TAS5717_CH2_RIGHT_CH_MIX_REG:
                return 4;
        default:
                return 1;
@@ -135,6 +140,129 @@ static int tas571x_reg_read(void *context, unsigned int reg,
        return 0;
 }
 
+/*
+ * register write for 8- and 20-byte registers
+ */
+static int tas571x_reg_write_multiword(struct i2c_client *client,
+               unsigned int reg, const long values[], size_t len)
+{
+       size_t i;
+       uint8_t *buf, *p;
+       int ret;
+       size_t send_size = 1 + len * sizeof(uint32_t);
+
+       buf = kzalloc(send_size, GFP_KERNEL | GFP_DMA);
+       if (!buf)
+               return -ENOMEM;
+       buf[0] = reg;
+
+       for (i = 0, p = buf + 1; i < len; i++, p += sizeof(uint32_t))
+               put_unaligned_be32(values[i], p);
+
+       ret = i2c_master_send(client, buf, send_size);
+
+       kfree(buf);
+
+       if (ret == send_size)
+               return 0;
+       else if (ret < 0)
+               return ret;
+       else
+               return -EIO;
+}
+
+/*
+ * register read for 8- and 20-byte registers
+ */
+static int tas571x_reg_read_multiword(struct i2c_client *client,
+               unsigned int reg, long values[], size_t len)
+{
+       unsigned int i;
+       uint8_t send_buf;
+       uint8_t *recv_buf, *p;
+       struct i2c_msg msgs[2];
+       unsigned int recv_size = len * sizeof(uint32_t);
+       int ret;
+
+       recv_buf = kzalloc(recv_size, GFP_KERNEL | GFP_DMA);
+       if (!recv_buf)
+               return -ENOMEM;
+
+       send_buf = reg;
+
+       msgs[0].addr = client->addr;
+       msgs[0].len = sizeof(send_buf);
+       msgs[0].buf = &send_buf;
+       msgs[0].flags = 0;
+
+       msgs[1].addr = client->addr;
+       msgs[1].len = recv_size;
+       msgs[1].buf = recv_buf;
+       msgs[1].flags = I2C_M_RD;
+
+       ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+       if (ret < 0)
+               goto err_ret;
+       else if (ret != ARRAY_SIZE(msgs)) {
+               ret = -EIO;
+               goto err_ret;
+       }
+
+       for (i = 0, p = recv_buf; i < len; i++, p += sizeof(uint32_t))
+               values[i] = get_unaligned_be32(p);
+
+err_ret:
+       kfree(recv_buf);
+       return ret;
+}
+
+/*
+ * Integer array controls for setting biquad, mixer, DRC coefficients.
+ * According to the datasheet each coefficient is effectively 26bits,
+ * i.e. stored as 32bits, where bits [31:26] are ignored.
+ * TI's TAS57xx Graphical Development Environment tool however produces
+ * coefficients with more than 26 bits. For this reason we allow values
+ * in the full 32-bits reange.
+ * The coefficients are ordered as given in the TAS571x data sheet:
+ * b0, b1, b2, a1, a2
+ */
+
+static int tas571x_coefficient_info(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_info *uinfo)
+{
+       int numcoef = kcontrol->private_value >> 16;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = numcoef;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 0xffffffff;
+       return 0;
+}
+
+static int tas571x_coefficient_get(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct i2c_client *i2c = to_i2c_client(codec->dev);
+       int numcoef = kcontrol->private_value >> 16;
+       int index = kcontrol->private_value & 0xffff;
+
+       return tas571x_reg_read_multiword(i2c, index,
+               ucontrol->value.integer.value, numcoef);
+}
+
+static int tas571x_coefficient_put(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct i2c_client *i2c = to_i2c_client(codec->dev);
+       int numcoef = kcontrol->private_value >> 16;
+       int index = kcontrol->private_value & 0xffff;
+
+       return tas571x_reg_write_multiword(i2c, index,
+               ucontrol->value.integer.value, numcoef);
+}
+
 static int tas571x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format)
 {
        struct tas571x_private *priv = snd_soc_codec_get_drvdata(dai->codec);
@@ -241,6 +369,15 @@ static const struct snd_soc_dai_ops tas571x_dai_ops = {
        .digital_mute   = tas571x_mute,
 };
 
+
+#define BIQUAD_COEFS(xname, reg) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = tas571x_coefficient_info, \
+       .get = tas571x_coefficient_get,\
+       .put = tas571x_coefficient_put, \
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+       .private_value = reg | (5 << 16) }
+
 static const char *const tas5711_supply_names[] = {
        "AVDD",
        "DVDD",
@@ -264,6 +401,16 @@ static const struct snd_kcontrol_new tas5711_controls[] = {
                   TAS571X_SOFT_MUTE_REG,
                   TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
                   1, 1),
+
+       SOC_DOUBLE_R_RANGE("CH1 Mixer Volume",
+                          TAS5717_CH1_LEFT_CH_MIX_REG,
+                          TAS5717_CH1_RIGHT_CH_MIX_REG,
+                          16, 0, 0x80, 0),
+
+       SOC_DOUBLE_R_RANGE("CH2 Mixer Volume",
+                          TAS5717_CH2_LEFT_CH_MIX_REG,
+                          TAS5717_CH2_RIGHT_CH_MIX_REG,
+                          16, 0, 0x80, 0),
 };
 
 static const struct regmap_range tas571x_readonly_regs_range[] = {
@@ -340,6 +487,43 @@ static const struct snd_kcontrol_new tas5717_controls[] = {
                   TAS571X_SOFT_MUTE_REG,
                   TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
                   1, 1),
+
+       /*
+        * The biquads are named according to the register names.
+        * Please note that TI's TAS57xx Graphical Development Environment
+        * tool names them different.
+        */
+       BIQUAD_COEFS("CH1 - Biquad 0", TAS5717_CH1_BQ0_REG),
+       BIQUAD_COEFS("CH1 - Biquad 1", TAS5717_CH1_BQ1_REG),
+       BIQUAD_COEFS("CH1 - Biquad 2", TAS5717_CH1_BQ2_REG),
+       BIQUAD_COEFS("CH1 - Biquad 3", TAS5717_CH1_BQ3_REG),
+       BIQUAD_COEFS("CH1 - Biquad 4", TAS5717_CH1_BQ4_REG),
+       BIQUAD_COEFS("CH1 - Biquad 5", TAS5717_CH1_BQ5_REG),
+       BIQUAD_COEFS("CH1 - Biquad 6", TAS5717_CH1_BQ6_REG),
+       BIQUAD_COEFS("CH1 - Biquad 7", TAS5717_CH1_BQ7_REG),
+       BIQUAD_COEFS("CH1 - Biquad 8", TAS5717_CH1_BQ8_REG),
+       BIQUAD_COEFS("CH1 - Biquad 9", TAS5717_CH1_BQ9_REG),
+       BIQUAD_COEFS("CH1 - Biquad 10", TAS5717_CH1_BQ10_REG),
+       BIQUAD_COEFS("CH1 - Biquad 11", TAS5717_CH1_BQ11_REG),
+
+       BIQUAD_COEFS("CH2 - Biquad 0", TAS5717_CH2_BQ0_REG),
+       BIQUAD_COEFS("CH2 - Biquad 1", TAS5717_CH2_BQ1_REG),
+       BIQUAD_COEFS("CH2 - Biquad 2", TAS5717_CH2_BQ2_REG),
+       BIQUAD_COEFS("CH2 - Biquad 3", TAS5717_CH2_BQ3_REG),
+       BIQUAD_COEFS("CH2 - Biquad 4", TAS5717_CH2_BQ4_REG),
+       BIQUAD_COEFS("CH2 - Biquad 5", TAS5717_CH2_BQ5_REG),
+       BIQUAD_COEFS("CH2 - Biquad 6", TAS5717_CH2_BQ6_REG),
+       BIQUAD_COEFS("CH2 - Biquad 7", TAS5717_CH2_BQ7_REG),
+       BIQUAD_COEFS("CH2 - Biquad 8", TAS5717_CH2_BQ8_REG),
+       BIQUAD_COEFS("CH2 - Biquad 9", TAS5717_CH2_BQ9_REG),
+       BIQUAD_COEFS("CH2 - Biquad 10", TAS5717_CH2_BQ10_REG),
+       BIQUAD_COEFS("CH2 - Biquad 11", TAS5717_CH2_BQ11_REG),
+
+       BIQUAD_COEFS("CH3 - Biquad 0", TAS5717_CH3_BQ0_REG),
+       BIQUAD_COEFS("CH3 - Biquad 1", TAS5717_CH3_BQ1_REG),
+
+       BIQUAD_COEFS("CH4 - Biquad 0", TAS5717_CH4_BQ0_REG),
+       BIQUAD_COEFS("CH4 - Biquad 1", TAS5717_CH4_BQ1_REG),
 };
 
 static const struct reg_default tas5717_reg_defaults[] = {
@@ -350,6 +534,10 @@ static const struct reg_default tas5717_reg_defaults[] = {
        { 0x08, 0x00c0 },
        { 0x09, 0x00c0 },
        { 0x1b, 0x82 },
+       { TAS5717_CH1_RIGHT_CH_MIX_REG, 0x0 },
+       { TAS5717_CH1_LEFT_CH_MIX_REG, 0x800000},
+       { TAS5717_CH2_LEFT_CH_MIX_REG, 0x0 },
+       { TAS5717_CH2_RIGHT_CH_MIX_REG, 0x800000},
 };
 
 static const struct regmap_config tas5717_regmap_config = {
index cf800c364f0fe97db98f51879024a4aa3769151e..c45677bc26ad2fb14fb85efda9239a87f8ec52e7 100644 (file)
 #define TAS571X_CH4_SRC_SELECT_REG     0x21
 #define TAS571X_PWM_MUX_REG            0x25
 
+/* 20-byte biquad registers */
+#define TAS5717_CH1_BQ0_REG            0x26
+#define TAS5717_CH1_BQ1_REG            0x27
+#define TAS5717_CH1_BQ2_REG            0x28
+#define TAS5717_CH1_BQ3_REG            0x29
+#define TAS5717_CH1_BQ4_REG            0x2a
+#define TAS5717_CH1_BQ5_REG            0x2b
+#define TAS5717_CH1_BQ6_REG            0x2c
+#define TAS5717_CH1_BQ7_REG            0x2d
+#define TAS5717_CH1_BQ8_REG            0x2e
+#define TAS5717_CH1_BQ9_REG            0x2f
+
+#define TAS5717_CH2_BQ0_REG            0x30
+#define TAS5717_CH2_BQ1_REG            0x31
+#define TAS5717_CH2_BQ2_REG            0x32
+#define TAS5717_CH2_BQ3_REG            0x33
+#define TAS5717_CH2_BQ4_REG            0x34
+#define TAS5717_CH2_BQ5_REG            0x35
+#define TAS5717_CH2_BQ6_REG            0x36
+#define TAS5717_CH2_BQ7_REG            0x37
+#define TAS5717_CH2_BQ8_REG            0x38
+#define TAS5717_CH2_BQ9_REG            0x39
+
+#define TAS5717_CH1_BQ10_REG           0x58
+#define TAS5717_CH1_BQ11_REG           0x59
+
+#define TAS5717_CH4_BQ0_REG            0x5a
+#define TAS5717_CH4_BQ1_REG            0x5b
+
+#define TAS5717_CH2_BQ10_REG           0x5c
+#define TAS5717_CH2_BQ11_REG           0x5d
+
+#define TAS5717_CH3_BQ0_REG            0x5e
+#define TAS5717_CH3_BQ1_REG            0x5f
+
+#define TAS5717_CH1_RIGHT_CH_MIX_REG   0x72
+#define TAS5717_CH1_LEFT_CH_MIX_REG    0x73
+#define TAS5717_CH2_LEFT_CH_MIX_REG    0x76
+#define TAS5717_CH2_RIGHT_CH_MIX_REG   0x77
+
 #endif /* _TAS571X_H */
index fe16c34607bb143fd28f8469c38bd27e17d0f489..ac9b146526eb5338d58ed94eb6aa8731a415e75e 100644 (file)
@@ -38,141 +38,143 @@ struct aic31xx_pdata {
        int micbias_vg;
 };
 
+#define AIC31XX_REG(page, reg) ((page * 128) + reg)
+
 /* Page Control Register */
-#define AIC31XX_PAGECTL                                0x00
+#define AIC31XX_PAGECTL                AIC31XX_REG(0, 0)
 
 /* Page 0 Registers */
 /* Software reset register */
-#define AIC31XX_RESET                          0x01
+#define AIC31XX_RESET          AIC31XX_REG(0, 1)
 /* OT FLAG register */
-#define AIC31XX_OT_FLAG                                0x03
+#define AIC31XX_OT_FLAG                AIC31XX_REG(0, 3)
 /* Clock clock Gen muxing, Multiplexers*/
-#define AIC31XX_CLKMUX                         0x04
+#define AIC31XX_CLKMUX         AIC31XX_REG(0, 4)
 /* PLL P and R-VAL register */
-#define AIC31XX_PLLPR                          0x05
+#define AIC31XX_PLLPR          AIC31XX_REG(0, 5)
 /* PLL J-VAL register */
-#define AIC31XX_PLLJ                           0x06
+#define AIC31XX_PLLJ           AIC31XX_REG(0, 6)
 /* PLL D-VAL MSB register */
-#define AIC31XX_PLLDMSB                                0x07
+#define AIC31XX_PLLDMSB                AIC31XX_REG(0, 7)
 /* PLL D-VAL LSB register */
-#define AIC31XX_PLLDLSB                                0x08
+#define AIC31XX_PLLDLSB                AIC31XX_REG(0, 8)
 /* DAC NDAC_VAL register*/
-#define AIC31XX_NDAC                           0x0B
+#define AIC31XX_NDAC           AIC31XX_REG(0, 11)
 /* DAC MDAC_VAL register */
-#define AIC31XX_MDAC                           0x0C
+#define AIC31XX_MDAC           AIC31XX_REG(0, 12)
 /* DAC OSR setting register 1, MSB value */
-#define AIC31XX_DOSRMSB                                0x0D
+#define AIC31XX_DOSRMSB                AIC31XX_REG(0, 13)
 /* DAC OSR setting register 2, LSB value */
-#define AIC31XX_DOSRLSB                                0x0E
-#define AIC31XX_MINI_DSP_INPOL                 0x10
+#define AIC31XX_DOSRLSB                AIC31XX_REG(0, 14)
+#define AIC31XX_MINI_DSP_INPOL AIC31XX_REG(0, 16)
 /* Clock setting register 8, PLL */
-#define AIC31XX_NADC                           0x12
+#define AIC31XX_NADC           AIC31XX_REG(0, 18)
 /* Clock setting register 9, PLL */
-#define AIC31XX_MADC                           0x13
+#define AIC31XX_MADC           AIC31XX_REG(0, 19)
 /* ADC Oversampling (AOSR) Register */
-#define AIC31XX_AOSR                           0x14
+#define AIC31XX_AOSR           AIC31XX_REG(0, 20)
 /* Clock setting register 9, Multiplexers */
-#define AIC31XX_CLKOUTMUX                      0x19
+#define AIC31XX_CLKOUTMUX      AIC31XX_REG(0, 25)
 /* Clock setting register 10, CLOCKOUT M divider value */
-#define AIC31XX_CLKOUTMVAL                     0x1A
+#define AIC31XX_CLKOUTMVAL     AIC31XX_REG(0, 26)
 /* Audio Interface Setting Register 1 */
-#define AIC31XX_IFACE1                         0x1B
+#define AIC31XX_IFACE1         AIC31XX_REG(0, 27)
 /* Audio Data Slot Offset Programming */
-#define AIC31XX_DATA_OFFSET                    0x1C
+#define AIC31XX_DATA_OFFSET    AIC31XX_REG(0, 28)
 /* Audio Interface Setting Register 2 */
-#define AIC31XX_IFACE2                         0x1D
+#define AIC31XX_IFACE2         AIC31XX_REG(0, 29)
 /* Clock setting register 11, BCLK N Divider */
-#define AIC31XX_BCLKN                          0x1E
+#define AIC31XX_BCLKN          AIC31XX_REG(0, 30)
 /* Audio Interface Setting Register 3, Secondary Audio Interface */
-#define AIC31XX_IFACESEC1                      0x1F
+#define AIC31XX_IFACESEC1      AIC31XX_REG(0, 31)
 /* Audio Interface Setting Register 4 */
-#define AIC31XX_IFACESEC2                      0x20
+#define AIC31XX_IFACESEC2      AIC31XX_REG(0, 32)
 /* Audio Interface Setting Register 5 */
-#define AIC31XX_IFACESEC3                      0x21
+#define AIC31XX_IFACESEC3      AIC31XX_REG(0, 33)
 /* I2C Bus Condition */
-#define AIC31XX_I2C                            0x22
+#define AIC31XX_I2C            AIC31XX_REG(0, 34)
 /* ADC FLAG */
-#define AIC31XX_ADCFLAG                                0x24
+#define AIC31XX_ADCFLAG                AIC31XX_REG(0, 36)
 /* DAC Flag Registers */
-#define AIC31XX_DACFLAG1                       0x25
-#define AIC31XX_DACFLAG2                       0x26
+#define AIC31XX_DACFLAG1       AIC31XX_REG(0, 37)
+#define AIC31XX_DACFLAG2       AIC31XX_REG(0, 38)
 /* Sticky Interrupt flag (overflow) */
-#define AIC31XX_OFFLAG                         0x27
+#define AIC31XX_OFFLAG         AIC31XX_REG(0, 39)
 /* Sticy DAC Interrupt flags */
-#define AIC31XX_INTRDACFLAG                    0x2C
+#define AIC31XX_INTRDACFLAG    AIC31XX_REG(0, 44)
 /* Sticy ADC Interrupt flags */
-#define AIC31XX_INTRADCFLAG                    0x2D
+#define AIC31XX_INTRADCFLAG    AIC31XX_REG(0, 45)
 /* DAC Interrupt flags 2 */
-#define AIC31XX_INTRDACFLAG2                   0x2E
+#define AIC31XX_INTRDACFLAG2   AIC31XX_REG(0, 46)
 /* ADC Interrupt flags 2 */
-#define AIC31XX_INTRADCFLAG2                   0x2F
+#define AIC31XX_INTRADCFLAG2   AIC31XX_REG(0, 47)
 /* INT1 interrupt control */
-#define AIC31XX_INT1CTRL                       0x30
+#define AIC31XX_INT1CTRL       AIC31XX_REG(0, 48)
 /* INT2 interrupt control */
-#define AIC31XX_INT2CTRL                       0x31
+#define AIC31XX_INT2CTRL       AIC31XX_REG(0, 49)
 /* GPIO1 control */
-#define AIC31XX_GPIO1                          0x33
+#define AIC31XX_GPIO1          AIC31XX_REG(0, 50)
 
-#define AIC31XX_DACPRB                         0x3C
+#define AIC31XX_DACPRB         AIC31XX_REG(0, 60)
 /* ADC Instruction Set Register */
-#define AIC31XX_ADCPRB                         0x3D
+#define AIC31XX_ADCPRB         AIC31XX_REG(0, 61)
 /* DAC channel setup register */
-#define AIC31XX_DACSETUP                       0x3F
+#define AIC31XX_DACSETUP       AIC31XX_REG(0, 63)
 /* DAC Mute and volume control register */
-#define AIC31XX_DACMUTE                                0x40
+#define AIC31XX_DACMUTE                AIC31XX_REG(0, 64)
 /* Left DAC channel digital volume control */
-#define AIC31XX_LDACVOL                                0x41
+#define AIC31XX_LDACVOL                AIC31XX_REG(0, 65)
 /* Right DAC channel digital volume control */
-#define AIC31XX_RDACVOL                                0x42
+#define AIC31XX_RDACVOL                AIC31XX_REG(0, 66)
 /* Headset detection */
-#define AIC31XX_HSDETECT                       0x43
+#define AIC31XX_HSDETECT       AIC31XX_REG(0, 67)
 /* ADC Digital Mic */
-#define AIC31XX_ADCSETUP                       0x51
+#define AIC31XX_ADCSETUP       AIC31XX_REG(0, 81)
 /* ADC Digital Volume Control Fine Adjust */
-#define AIC31XX_ADCFGA                         0x52
+#define AIC31XX_ADCFGA         AIC31XX_REG(0, 82)
 /* ADC Digital Volume Control Coarse Adjust */
-#define AIC31XX_ADCVOL                         0x53
+#define AIC31XX_ADCVOL         AIC31XX_REG(0, 83)
 
 
 /* Page 1 Registers */
 /* Headphone drivers */
-#define AIC31XX_HPDRIVER                       0x9F
+#define AIC31XX_HPDRIVER       AIC31XX_REG(1, 31)
 /* Class-D Speakear Amplifier */
-#define AIC31XX_SPKAMP                         0xA0
+#define AIC31XX_SPKAMP         AIC31XX_REG(1, 32)
 /* HP Output Drivers POP Removal Settings */
-#define AIC31XX_HPPOP                          0xA1
+#define AIC31XX_HPPOP          AIC31XX_REG(1, 33)
 /* Output Driver PGA Ramp-Down Period Control */
-#define AIC31XX_SPPGARAMP                      0xA2
+#define AIC31XX_SPPGARAMP      AIC31XX_REG(1, 34)
 /* DAC_L and DAC_R Output Mixer Routing */
-#define AIC31XX_DACMIXERROUTE                  0xA3
+#define AIC31XX_DACMIXERROUTE  AIC31XX_REG(1, 35)
 /* Left Analog Vol to HPL */
-#define AIC31XX_LANALOGHPL                     0xA4
+#define AIC31XX_LANALOGHPL     AIC31XX_REG(1, 36)
 /* Right Analog Vol to HPR */
-#define AIC31XX_RANALOGHPR                     0xA5
+#define AIC31XX_RANALOGHPR     AIC31XX_REG(1, 37)
 /* Left Analog Vol to SPL */
-#define AIC31XX_LANALOGSPL                     0xA6
+#define AIC31XX_LANALOGSPL     AIC31XX_REG(1, 38)
 /* Right Analog Vol to SPR */
-#define AIC31XX_RANALOGSPR                     0xA7
+#define AIC31XX_RANALOGSPR     AIC31XX_REG(1, 39)
 /* HPL Driver */
-#define AIC31XX_HPLGAIN                                0xA8
+#define AIC31XX_HPLGAIN                AIC31XX_REG(1, 40)
 /* HPR Driver */
-#define AIC31XX_HPRGAIN                                0xA9
+#define AIC31XX_HPRGAIN                AIC31XX_REG(1, 41)
 /* SPL Driver */
-#define AIC31XX_SPLGAIN                                0xAA
+#define AIC31XX_SPLGAIN                AIC31XX_REG(1, 42)
 /* SPR Driver */
-#define AIC31XX_SPRGAIN                                0xAB
+#define AIC31XX_SPRGAIN                AIC31XX_REG(1, 43)
 /* HP Driver Control */
-#define AIC31XX_HPCONTROL                      0xAC
+#define AIC31XX_HPCONTROL      AIC31XX_REG(1, 44)
 /* MIC Bias Control */
-#define AIC31XX_MICBIAS                                0xAE
+#define AIC31XX_MICBIAS                AIC31XX_REG(1, 46)
 /* MIC PGA*/
-#define AIC31XX_MICPGA                         0xAF
+#define AIC31XX_MICPGA         AIC31XX_REG(1, 47)
 /* Delta-Sigma Mono ADC Channel Fine-Gain Input Selection for P-Terminal */
-#define AIC31XX_MICPGAPI                       0xB0
+#define AIC31XX_MICPGAPI       AIC31XX_REG(1, 48)
 /* ADC Input Selection for M-Terminal */
-#define AIC31XX_MICPGAMI                       0xB1
+#define AIC31XX_MICPGAMI       AIC31XX_REG(1, 49)
 /* Input CM Settings */
-#define AIC31XX_MICPGACM                       0xB2
+#define AIC31XX_MICPGACM       AIC31XX_REG(1, 50)
 
 /* Bits, masks and shifts */
 
index 11d85c5c787addb8d8add7fc2b52ab91cc49b63d..f1ea052a822e1f584eb55cbc876b8e079e813ccd 100644 (file)
@@ -32,6 +32,7 @@
 #include <sound/tlv.h>
 #include <linux/of.h>
 #include <linux/of_gpio.h>
+#include <linux/regmap.h>
 
 #include "tpa6130a2.h"
 
@@ -40,219 +41,72 @@ enum tpa_model {
        TPA6140A2,
 };
 
-static struct i2c_client *tpa6130a2_client;
-
 /* This struct is used to save the context */
 struct tpa6130a2_data {
-       struct mutex mutex;
-       unsigned char regs[TPA6130A2_CACHEREGNUM];
+       struct device *dev;
+       struct regmap *regmap;
        struct regulator *supply;
        int power_gpio;
-       u8 power_state:1;
        enum tpa_model id;
 };
 
-static int tpa6130a2_i2c_read(int reg)
-{
-       struct tpa6130a2_data *data;
-       int val;
-
-       if (WARN_ON(!tpa6130a2_client))
-               return -EINVAL;
-       data = i2c_get_clientdata(tpa6130a2_client);
-
-       /* If powered off, return the cached value */
-       if (data->power_state) {
-               val = i2c_smbus_read_byte_data(tpa6130a2_client, reg);
-               if (val < 0)
-                       dev_err(&tpa6130a2_client->dev, "Read failed\n");
-               else
-                       data->regs[reg] = val;
-       } else {
-               val = data->regs[reg];
-       }
-
-       return val;
-}
-
-static int tpa6130a2_i2c_write(int reg, u8 value)
-{
-       struct tpa6130a2_data *data;
-       int val = 0;
-
-       if (WARN_ON(!tpa6130a2_client))
-               return -EINVAL;
-       data = i2c_get_clientdata(tpa6130a2_client);
-
-       if (data->power_state) {
-               val = i2c_smbus_write_byte_data(tpa6130a2_client, reg, value);
-               if (val < 0) {
-                       dev_err(&tpa6130a2_client->dev, "Write failed\n");
-                       return val;
-               }
-       }
-
-       /* Either powered on or off, we save the context */
-       data->regs[reg] = value;
-
-       return val;
-}
-
-static u8 tpa6130a2_read(int reg)
-{
-       struct tpa6130a2_data *data;
-
-       if (WARN_ON(!tpa6130a2_client))
-               return 0;
-       data = i2c_get_clientdata(tpa6130a2_client);
-
-       return data->regs[reg];
-}
-
-static int tpa6130a2_initialize(void)
-{
-       struct tpa6130a2_data *data;
-       int i, ret = 0;
-
-       if (WARN_ON(!tpa6130a2_client))
-               return -EINVAL;
-       data = i2c_get_clientdata(tpa6130a2_client);
-
-       for (i = 1; i < TPA6130A2_REG_VERSION; i++) {
-               ret = tpa6130a2_i2c_write(i, data->regs[i]);
-               if (ret < 0)
-                       break;
-       }
-
-       return ret;
-}
-
-static int tpa6130a2_power(u8 power)
+static int tpa6130a2_power(struct tpa6130a2_data *data, bool enable)
 {
-       struct  tpa6130a2_data *data;
-       u8      val;
-       int     ret = 0;
-
-       if (WARN_ON(!tpa6130a2_client))
-               return -EINVAL;
-       data = i2c_get_clientdata(tpa6130a2_client);
-
-       mutex_lock(&data->mutex);
-       if (power == data->power_state)
-               goto exit;
+       int ret;
 
-       if (power) {
+       if (enable) {
                ret = regulator_enable(data->supply);
                if (ret != 0) {
-                       dev_err(&tpa6130a2_client->dev,
+                       dev_err(data->dev,
                                "Failed to enable supply: %d\n", ret);
-                       goto exit;
+                       return ret;
                }
                /* Power on */
                if (data->power_gpio >= 0)
                        gpio_set_value(data->power_gpio, 1);
-
-               data->power_state = 1;
-               ret = tpa6130a2_initialize();
-               if (ret < 0) {
-                       dev_err(&tpa6130a2_client->dev,
-                               "Failed to initialize chip\n");
-                       if (data->power_gpio >= 0)
-                               gpio_set_value(data->power_gpio, 0);
-                       regulator_disable(data->supply);
-                       data->power_state = 0;
-                       goto exit;
-               }
        } else {
-               /* set SWS */
-               val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
-               val |= TPA6130A2_SWS;
-               tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
-
                /* Power off */
                if (data->power_gpio >= 0)
                        gpio_set_value(data->power_gpio, 0);
 
                ret = regulator_disable(data->supply);
                if (ret != 0) {
-                       dev_err(&tpa6130a2_client->dev,
+                       dev_err(data->dev,
                                "Failed to disable supply: %d\n", ret);
-                       goto exit;
+                       return ret;
                }
 
-               data->power_state = 0;
+               /* device regs does not match the cache state anymore */
+               regcache_mark_dirty(data->regmap);
        }
 
-exit:
-       mutex_unlock(&data->mutex);
        return ret;
 }
 
-static int tpa6130a2_get_volsw(struct snd_kcontrol *kcontrol,
-               struct snd_ctl_elem_value *ucontrol)
+static int tpa6130a2_power_event(struct snd_soc_dapm_widget *w,
+                                struct snd_kcontrol *kctrl, int event)
 {
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       struct tpa6130a2_data *data;
-       unsigned int reg = mc->reg;
-       unsigned int shift = mc->shift;
-       int max = mc->max;
-       unsigned int mask = (1 << fls(max)) - 1;
-       unsigned int invert = mc->invert;
-
-       if (WARN_ON(!tpa6130a2_client))
-               return -EINVAL;
-       data = i2c_get_clientdata(tpa6130a2_client);
-
-       mutex_lock(&data->mutex);
-
-       ucontrol->value.integer.value[0] =
-               (tpa6130a2_read(reg) >> shift) & mask;
-
-       if (invert)
-               ucontrol->value.integer.value[0] =
-                       max - ucontrol->value.integer.value[0];
-
-       mutex_unlock(&data->mutex);
-       return 0;
-}
+       struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+       struct tpa6130a2_data *data = snd_soc_component_get_drvdata(c);
+       int ret;
 
-static int tpa6130a2_put_volsw(struct snd_kcontrol *kcontrol,
-               struct snd_ctl_elem_value *ucontrol)
-{
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       struct tpa6130a2_data *data;
-       unsigned int reg = mc->reg;
-       unsigned int shift = mc->shift;
-       int max = mc->max;
-       unsigned int mask = (1 << fls(max)) - 1;
-       unsigned int invert = mc->invert;
-       unsigned int val = (ucontrol->value.integer.value[0] & mask);
-       unsigned int val_reg;
-
-       if (WARN_ON(!tpa6130a2_client))
-               return -EINVAL;
-       data = i2c_get_clientdata(tpa6130a2_client);
-
-       if (invert)
-               val = max - val;
-
-       mutex_lock(&data->mutex);
-
-       val_reg = tpa6130a2_read(reg);
-       if (((val_reg >> shift) & mask) == val) {
-               mutex_unlock(&data->mutex);
-               return 0;
+       /* before widget power up */
+       if (SND_SOC_DAPM_EVENT_ON(event)) {
+               /* Turn on the chip */
+               tpa6130a2_power(data, true);
+               /* Sync the registers */
+               ret = regcache_sync(data->regmap);
+               if (ret < 0) {
+                       dev_err(c->dev, "Failed to initialize chip\n");
+                       tpa6130a2_power(data, false);
+                       return ret;
+               }
+       /* after widget power down */
+       } else {
+               tpa6130a2_power(data, false);
        }
 
-       val_reg &= ~(mask << shift);
-       val_reg |= val << shift;
-       tpa6130a2_i2c_write(reg, val_reg);
-
-       mutex_unlock(&data->mutex);
-
-       return 1;
+       return 0;
 }
 
 /*
@@ -273,9 +127,8 @@ static const DECLARE_TLV_DB_RANGE(tpa6130_tlv,
 );
 
 static const struct snd_kcontrol_new tpa6130a2_controls[] = {
-       SOC_SINGLE_EXT_TLV("TPA6130A2 Headphone Playback Volume",
+       SOC_SINGLE_TLV("Headphone Playback Volume",
                       TPA6130A2_REG_VOL_MUTE, 0, 0x3f, 0,
-                      tpa6130a2_get_volsw, tpa6130a2_put_volsw,
                       tpa6130_tlv),
 };
 
@@ -286,85 +139,79 @@ static const DECLARE_TLV_DB_RANGE(tpa6140_tlv,
 );
 
 static const struct snd_kcontrol_new tpa6140a2_controls[] = {
-       SOC_SINGLE_EXT_TLV("TPA6140A2 Headphone Playback Volume",
+       SOC_SINGLE_TLV("Headphone Playback Volume",
                       TPA6130A2_REG_VOL_MUTE, 1, 0x1f, 0,
-                      tpa6130a2_get_volsw, tpa6130a2_put_volsw,
                       tpa6140_tlv),
 };
 
-/*
- * Enable or disable channel (left or right)
- * The bit number for mute and amplifier are the same per channel:
- * bit 6: Right channel
- * bit 7: Left channel
- * in both registers.
- */
-static void tpa6130a2_channel_enable(u8 channel, int enable)
+static int tpa6130a2_component_probe(struct snd_soc_component *component)
 {
-       u8      val;
+       struct tpa6130a2_data *data = snd_soc_component_get_drvdata(component);
 
-       if (enable) {
-               /* Enable channel */
-               /* Enable amplifier */
-               val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
-               val |= channel;
-               val &= ~TPA6130A2_SWS;
-               tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
-
-               /* Unmute channel */
-               val = tpa6130a2_read(TPA6130A2_REG_VOL_MUTE);
-               val &= ~channel;
-               tpa6130a2_i2c_write(TPA6130A2_REG_VOL_MUTE, val);
-       } else {
-               /* Disable channel */
-               /* Mute channel */
-               val = tpa6130a2_read(TPA6130A2_REG_VOL_MUTE);
-               val |= channel;
-               tpa6130a2_i2c_write(TPA6130A2_REG_VOL_MUTE, val);
-
-               /* Disable amplifier */
-               val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
-               val &= ~channel;
-               tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
-       }
+       if (data->id == TPA6140A2)
+               return snd_soc_add_component_controls(component,
+                       tpa6140a2_controls, ARRAY_SIZE(tpa6140a2_controls));
+       else
+               return snd_soc_add_component_controls(component,
+                       tpa6130a2_controls, ARRAY_SIZE(tpa6130a2_controls));
 }
 
-int tpa6130a2_stereo_enable(struct snd_soc_codec *codec, int enable)
-{
-       int ret = 0;
-       if (enable) {
-               ret = tpa6130a2_power(1);
-               if (ret < 0)
-                       return ret;
-               tpa6130a2_channel_enable(TPA6130A2_HP_EN_R | TPA6130A2_HP_EN_L,
-                                        1);
-       } else {
-               tpa6130a2_channel_enable(TPA6130A2_HP_EN_R | TPA6130A2_HP_EN_L,
-                                        0);
-               ret = tpa6130a2_power(0);
-       }
+static const struct snd_soc_dapm_widget tpa6130a2_dapm_widgets[] = {
+       SND_SOC_DAPM_INPUT("LEFTIN"),
+       SND_SOC_DAPM_INPUT("RIGHTIN"),
+       SND_SOC_DAPM_OUTPUT("HPLEFT"),
+       SND_SOC_DAPM_OUTPUT("HPRIGHT"),
+
+       SND_SOC_DAPM_PGA("Left Mute", TPA6130A2_REG_VOL_MUTE,
+                        TPA6130A2_HP_EN_L_SHIFT, 1, NULL, 0),
+       SND_SOC_DAPM_PGA("Right Mute", TPA6130A2_REG_VOL_MUTE,
+                        TPA6130A2_HP_EN_R_SHIFT, 1, NULL, 0),
+       SND_SOC_DAPM_PGA("Left PGA", TPA6130A2_REG_CONTROL,
+                        TPA6130A2_HP_EN_L_SHIFT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Right PGA", TPA6130A2_REG_CONTROL,
+                        TPA6130A2_HP_EN_R_SHIFT, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("Power", TPA6130A2_REG_CONTROL,
+                           TPA6130A2_SWS_SHIFT, 1, tpa6130a2_power_event,
+                           SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
 
-       return ret;
-}
-EXPORT_SYMBOL_GPL(tpa6130a2_stereo_enable);
+static const struct snd_soc_dapm_route tpa6130a2_dapm_routes[] = {
+       { "Left PGA", NULL, "LEFTIN" },
+       { "Right PGA", NULL, "RIGHTIN" },
 
-int tpa6130a2_add_controls(struct snd_soc_codec *codec)
-{
-       struct  tpa6130a2_data *data;
+       { "Left Mute", NULL, "Left PGA" },
+       { "Right Mute", NULL, "Right PGA" },
 
-       if (tpa6130a2_client == NULL)
-               return -ENODEV;
+       { "HPLEFT", NULL, "Left Mute" },
+       { "HPRIGHT", NULL, "Right Mute" },
 
-       data = i2c_get_clientdata(tpa6130a2_client);
+       { "Left PGA", NULL, "Power" },
+       { "Right PGA", NULL, "Power" },
+};
 
-       if (data->id == TPA6140A2)
-               return snd_soc_add_codec_controls(codec, tpa6140a2_controls,
-                                               ARRAY_SIZE(tpa6140a2_controls));
-       else
-               return snd_soc_add_codec_controls(codec, tpa6130a2_controls,
-                                               ARRAY_SIZE(tpa6130a2_controls));
-}
-EXPORT_SYMBOL_GPL(tpa6130a2_add_controls);
+struct snd_soc_component_driver tpa6130a2_component_driver = {
+       .name = "tpa6130a2",
+       .probe = tpa6130a2_component_probe,
+       .dapm_widgets = tpa6130a2_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(tpa6130a2_dapm_widgets),
+       .dapm_routes = tpa6130a2_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(tpa6130a2_dapm_routes),
+};
+
+static const struct reg_default tpa6130a2_reg_defaults[] = {
+       { TPA6130A2_REG_CONTROL, TPA6130A2_SWS },
+       { TPA6130A2_REG_VOL_MUTE, TPA6130A2_MUTE_R | TPA6130A2_MUTE_L },
+};
+
+static const struct regmap_config tpa6130a2_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = TPA6130A2_REG_VERSION,
+       .reg_defaults = tpa6130a2_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(tpa6130a2_reg_defaults),
+       .cache_type = REGCACHE_RBTREE,
+};
 
 static int tpa6130a2_probe(struct i2c_client *client,
                           const struct i2c_device_id *id)
@@ -374,6 +221,7 @@ static int tpa6130a2_probe(struct i2c_client *client,
        struct tpa6130a2_platform_data *pdata = client->dev.platform_data;
        struct device_node *np = client->dev.of_node;
        const char *regulator;
+       unsigned int version;
        int ret;
 
        dev = &client->dev;
@@ -382,6 +230,12 @@ static int tpa6130a2_probe(struct i2c_client *client,
        if (!data)
                return -ENOMEM;
 
+       data->dev = dev;
+
+       data->regmap = devm_regmap_init_i2c(client, &tpa6130a2_regmap_config);
+       if (IS_ERR(data->regmap))
+               return PTR_ERR(data->regmap);
+
        if (pdata) {
                data->power_gpio = pdata->power_gpio;
        } else if (np) {
@@ -392,26 +246,17 @@ static int tpa6130a2_probe(struct i2c_client *client,
                return -ENODEV;
        }
 
-       tpa6130a2_client = client;
-
-       i2c_set_clientdata(tpa6130a2_client, data);
+       i2c_set_clientdata(client, data);
 
        data->id = id->driver_data;
 
-       mutex_init(&data->mutex);
-
-       /* Set default register values */
-       data->regs[TPA6130A2_REG_CONTROL] =     TPA6130A2_SWS;
-       data->regs[TPA6130A2_REG_VOL_MUTE] =    TPA6130A2_MUTE_R |
-                                               TPA6130A2_MUTE_L;
-
        if (data->power_gpio >= 0) {
                ret = devm_gpio_request(dev, data->power_gpio,
                                        "tpa6130a2 enable");
                if (ret < 0) {
                        dev_err(dev, "Failed to request power GPIO (%d)\n",
                                data->power_gpio);
-                       goto err_gpio;
+                       return ret;
                }
                gpio_direction_output(data->power_gpio, 0);
        }
@@ -432,39 +277,27 @@ static int tpa6130a2_probe(struct i2c_client *client,
        if (IS_ERR(data->supply)) {
                ret = PTR_ERR(data->supply);
                dev_err(dev, "Failed to request supply: %d\n", ret);
-               goto err_gpio;
+               return ret;
        }
 
-       ret = tpa6130a2_power(1);
+       ret = tpa6130a2_power(data, true);
        if (ret != 0)
-               goto err_gpio;
+               return ret;
 
 
        /* Read version */
-       ret = tpa6130a2_i2c_read(TPA6130A2_REG_VERSION) &
-                                TPA6130A2_VERSION_MASK;
-       if ((ret != 1) && (ret != 2))
-               dev_warn(dev, "UNTESTED version detected (%d)\n", ret);
+       regmap_read(data->regmap, TPA6130A2_REG_VERSION, &version);
+       version &= TPA6130A2_VERSION_MASK;
+       if ((version != 1) && (version != 2))
+               dev_warn(dev, "UNTESTED version detected (%d)\n", version);
 
        /* Disable the chip */
-       ret = tpa6130a2_power(0);
+       ret = tpa6130a2_power(data, false);
        if (ret != 0)
-               goto err_gpio;
-
-       return 0;
-
-err_gpio:
-       tpa6130a2_client = NULL;
+               return ret;
 
-       return ret;
-}
-
-static int tpa6130a2_remove(struct i2c_client *client)
-{
-       tpa6130a2_power(0);
-       tpa6130a2_client = NULL;
-
-       return 0;
+       return devm_snd_soc_register_component(&client->dev,
+                       &tpa6130a2_component_driver, NULL, 0);
 }
 
 static const struct i2c_device_id tpa6130a2_id[] = {
@@ -489,7 +322,6 @@ static struct i2c_driver tpa6130a2_i2c_driver = {
                .of_match_table = of_match_ptr(tpa6130a2_of_match),
        },
        .probe = tpa6130a2_probe,
-       .remove = tpa6130a2_remove,
        .id_table = tpa6130a2_id,
 };
 
index 417444020ba6f898a29099ecbf9c787a5be1e593..f19cad5d4172185bf11b586d172c89841416ce58 100644 (file)
 #define TPA6130A2_REG_OUT_IMPEDANCE    0x03
 #define TPA6130A2_REG_VERSION          0x04
 
-#define TPA6130A2_CACHEREGNUM  (TPA6130A2_REG_VERSION + 1)
-
 /* Register bits */
 /* TPA6130A2_REG_CONTROL (0x01) */
-#define TPA6130A2_SWS                  (0x01 << 0)
+#define TPA6130A2_SWS_SHIFT            0
+#define TPA6130A2_SWS                  (0x01 << TPA6130A2_SWS_SHIFT)
 #define TPA6130A2_TERMAL               (0x01 << 1)
 #define TPA6130A2_MODE(x)              (x << 4)
 #define TPA6130A2_MODE_STEREO          (0x00)
 #define TPA6130A2_MODE_DUAL_MONO       (0x01)
 #define TPA6130A2_MODE_BRIDGE          (0x02)
 #define TPA6130A2_MODE_MASK            (0x03)
-#define TPA6130A2_HP_EN_R              (0x01 << 6)
-#define TPA6130A2_HP_EN_L              (0x01 << 7)
+#define TPA6130A2_HP_EN_R_SHIFT                6
+#define TPA6130A2_HP_EN_R              (0x01 << TPA6130A2_HP_EN_R_SHIFT)
+#define TPA6130A2_HP_EN_L_SHIFT                7
+#define TPA6130A2_HP_EN_L              (0x01 << TPA6130A2_HP_EN_L_SHIFT)
 
 /* TPA6130A2_REG_VOL_MUTE (0x02) */
 #define TPA6130A2_VOLUME(x)            ((x & 0x3f) << 0)
@@ -56,7 +57,4 @@
 /* TPA6130A2_REG_VERSION (0x04) */
 #define TPA6130A2_VERSION_MASK         (0x0f)
 
-extern int tpa6130a2_add_controls(struct snd_soc_codec *codec);
-extern int tpa6130a2_stereo_enable(struct snd_soc_codec *codec, int enable);
-
 #endif /* __TPA6130A2_H__ */
index e7fe6b7b95b7fc8e29b3da06f770c9d52f8bd155..846deed6af417c7fcca17f35960679acb1ac4013 100644 (file)
@@ -1713,6 +1713,7 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
 
        { "MICSUPP", NULL, "SYSCLK" },
 
+       { "DRC1 Signal Activity", NULL, "SYSCLK" },
        { "DRC1 Signal Activity", NULL, "DRC1L" },
        { "DRC1 Signal Activity", NULL, "DRC1R" },
 };
index d54f1b46c9ec08107427ff0f6bf81bf4670103bb..156547026a40e86506b01b7d56c1d84c73239227 100644 (file)
@@ -1104,6 +1104,11 @@ SND_SOC_DAPM_INPUT("IN4R"),
 SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
 SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
 
+SND_SOC_DAPM_OUTPUT("DSP Voice Trigger"),
+
+SND_SOC_DAPM_SWITCH("DSP3 Voice Trigger", SND_SOC_NOPM, 2, 0,
+                   &arizona_voice_trigger_switch[2]),
+
 SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
                   0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
@@ -1998,10 +2003,16 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
 
        { "MICSUPP", NULL, "SYSCLK" },
 
+       { "DRC1 Signal Activity", NULL, "SYSCLK" },
+       { "DRC2 Signal Activity", NULL, "SYSCLK" },
        { "DRC1 Signal Activity", NULL, "DRC1L" },
        { "DRC1 Signal Activity", NULL, "DRC1R" },
        { "DRC2 Signal Activity", NULL, "DRC2L" },
        { "DRC2 Signal Activity", NULL, "DRC2R" },
+
+       { "DSP Voice Trigger", NULL, "SYSCLK" },
+       { "DSP Voice Trigger", NULL, "DSP3 Voice Trigger" },
+       { "DSP3 Voice Trigger", "Switch", "DSP3" },
 };
 
 static int wm5110_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
@@ -2223,6 +2234,7 @@ static irqreturn_t wm5110_adsp2_irq(int irq, void *data)
 {
        struct wm5110_priv *priv = data;
        struct arizona *arizona = priv->core.arizona;
+       struct arizona_voice_trigger_info info;
        int serviced = 0;
        int i, ret;
 
@@ -2230,6 +2242,12 @@ static irqreturn_t wm5110_adsp2_irq(int irq, void *data)
                ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]);
                if (ret != -ENODEV)
                        serviced++;
+               if (ret == WM_ADSP_COMPR_VOICE_TRIGGER) {
+                       info.core = i;
+                       arizona_call_notifiers(arizona,
+                                              ARIZONA_NOTIFY_VOICE_TRIGGER,
+                                              &info);
+               }
        }
 
        if (!serviced) {
@@ -2252,6 +2270,7 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
        arizona_init_spk(codec);
        arizona_init_gpio(codec);
        arizona_init_mono(codec);
+       arizona_init_notifiers(codec);
 
        ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
                                  "ADSP2 Compressed IRQ", wm5110_adsp2_irq,
index 4bcf5f8ece50f3681948a9fbc47b85400d9a8b51..d18261a442560cd2c108c34295bd5b1776a6ebf4 100644 (file)
@@ -358,6 +358,9 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream,
        case 24:
                iface |= 0x0008;
                break;
+       case 32:
+               iface |= 0x000c;
+               break;
        }
 
        wm8731_set_deemph(codec);
@@ -541,7 +544,7 @@ static int wm8731_startup(struct snd_pcm_substream *substream,
 #define WM8731_RATES SNDRV_PCM_RATE_8000_96000
 
 #define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
-       SNDRV_PCM_FMTBIT_S24_LE)
+       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
 
 static const struct snd_soc_dai_ops wm8731_dai_ops = {
        .startup        = wm8731_startup,
index 6f1024f48b193bc7d3127cc8b12d56d14124413b..cdcc91282e8a15ac3d226591df88da1b45da4c99 100644 (file)
@@ -32,7 +32,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/moduleparam.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/delay.h>
@@ -486,7 +485,7 @@ SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", WM8753_PWR1, 4, 0),
 SND_SOC_DAPM_OUTPUT("MONO1"),
 SND_SOC_DAPM_MUX("Mono 2 Mux", SND_SOC_NOPM, 0, 0, &wm8753_mono2_controls),
 SND_SOC_DAPM_OUTPUT("MONO2"),
-SND_SOC_DAPM_MIXER("Out3 Left + Right", -1, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Out3 Left + Right", SND_SOC_NOPM, 0, 0, NULL, 0),
 SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0, &wm8753_out3_controls),
 SND_SOC_DAPM_PGA("Out 3", WM8753_PWR3, 4, 0, NULL, 0),
 SND_SOC_DAPM_OUTPUT("OUT3"),
index 6ac76fe116b028db34c8cdbf3691babeb2a68844..7347abff4b2c3eaaa71c4c18131622d2d9ec3402 100644 (file)
@@ -1,10 +1,13 @@
 /*
- * wm8985.c  --  WM8985 ALSA SoC Audio driver
+ * wm8985.c  --  WM8985 / WM8758 ALSA SoC Audio driver
  *
  * Copyright 2010 Wolfson Microelectronics plc
- *
  * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
  *
+ * WM8758 support:
+ * Copyright: 2016 Barix AG
+ * Author: Petr Kulhavy <petr@barix.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.
@@ -40,6 +43,11 @@ static const char *wm8985_supply_names[WM8985_NUM_SUPPLIES] = {
        "AVDD2"
 };
 
+enum wm8985_type {
+       WM8985,
+       WM8758,
+};
+
 static const struct reg_default wm8985_reg_defaults[] = {
        { 1,  0x0000 },     /* R1  - Power management 1 */
        { 2,  0x0000 },     /* R2  - Power management 2 */
@@ -181,6 +189,7 @@ static const int volume_update_regs[] = {
 struct wm8985_priv {
        struct regmap *regmap;
        struct regulator_bulk_data supplies[WM8985_NUM_SUPPLIES];
+       enum wm8985_type dev_type;
        unsigned int sysclk;
        unsigned int bclk;
 };
@@ -289,7 +298,7 @@ static const char *depth_3d_text[] = {
 };
 static SOC_ENUM_SINGLE_DECL(depth_3d, WM8985_3D_CONTROL, 0, depth_3d_text);
 
-static const struct snd_kcontrol_new wm8985_snd_controls[] = {
+static const struct snd_kcontrol_new wm8985_common_snd_controls[] = {
        SOC_SINGLE("Digital Loopback Switch", WM8985_COMPANDING_CONTROL,
                0, 1, 0),
 
@@ -355,10 +364,6 @@ static const struct snd_kcontrol_new wm8985_snd_controls[] = {
        SOC_ENUM("High Pass Filter Mode", filter_mode),
        SOC_SINGLE("High Pass Filter Cutoff", WM8985_ADC_CONTROL, 4, 7, 0),
 
-       SOC_DOUBLE_R_TLV("Aux Bypass Volume",
-               WM8985_LEFT_MIXER_CTRL, WM8985_RIGHT_MIXER_CTRL, 6, 7, 0,
-               aux_tlv),
-
        SOC_DOUBLE_R_TLV("Input PGA Bypass Volume",
                WM8985_LEFT_MIXER_CTRL, WM8985_RIGHT_MIXER_CTRL, 2, 7, 0,
                bypass_tlv),
@@ -379,20 +384,30 @@ static const struct snd_kcontrol_new wm8985_snd_controls[] = {
        SOC_SINGLE_TLV("EQ5 Volume", WM8985_EQ5_HIGH_SHELF, 0, 24, 1, eq_tlv),
 
        SOC_ENUM("3D Depth", depth_3d),
+};
+
+static const struct snd_kcontrol_new wm8985_specific_snd_controls[] = {
+       SOC_DOUBLE_R_TLV("Aux Bypass Volume",
+               WM8985_LEFT_MIXER_CTRL, WM8985_RIGHT_MIXER_CTRL, 6, 7, 0,
+               aux_tlv),
 
        SOC_ENUM("Speaker Mode", speaker_mode)
 };
 
 static const struct snd_kcontrol_new left_out_mixer[] = {
        SOC_DAPM_SINGLE("Line Switch", WM8985_LEFT_MIXER_CTRL, 1, 1, 0),
-       SOC_DAPM_SINGLE("Aux Switch", WM8985_LEFT_MIXER_CTRL, 5, 1, 0),
        SOC_DAPM_SINGLE("PCM Switch", WM8985_LEFT_MIXER_CTRL, 0, 1, 0),
+
+       /* --- WM8985 only --- */
+       SOC_DAPM_SINGLE("Aux Switch", WM8985_LEFT_MIXER_CTRL, 5, 1, 0),
 };
 
 static const struct snd_kcontrol_new right_out_mixer[] = {
        SOC_DAPM_SINGLE("Line Switch", WM8985_RIGHT_MIXER_CTRL, 1, 1, 0),
-       SOC_DAPM_SINGLE("Aux Switch", WM8985_RIGHT_MIXER_CTRL, 5, 1, 0),
        SOC_DAPM_SINGLE("PCM Switch", WM8985_RIGHT_MIXER_CTRL, 0, 1, 0),
+
+       /* --- WM8985 only --- */
+       SOC_DAPM_SINGLE("Aux Switch", WM8985_RIGHT_MIXER_CTRL, 5, 1, 0),
 };
 
 static const struct snd_kcontrol_new left_input_mixer[] = {
@@ -410,6 +425,8 @@ static const struct snd_kcontrol_new right_input_mixer[] = {
 static const struct snd_kcontrol_new left_boost_mixer[] = {
        SOC_DAPM_SINGLE_TLV("L2 Volume", WM8985_LEFT_ADC_BOOST_CTRL,
                4, 7, 0, boost_tlv),
+
+       /* --- WM8985 only --- */
        SOC_DAPM_SINGLE_TLV("AUXL Volume", WM8985_LEFT_ADC_BOOST_CTRL,
                0, 7, 0, boost_tlv)
 };
@@ -417,11 +434,13 @@ static const struct snd_kcontrol_new left_boost_mixer[] = {
 static const struct snd_kcontrol_new right_boost_mixer[] = {
        SOC_DAPM_SINGLE_TLV("R2 Volume", WM8985_RIGHT_ADC_BOOST_CTRL,
                4, 7, 0, boost_tlv),
+
+       /* --- WM8985 only --- */
        SOC_DAPM_SINGLE_TLV("AUXR Volume", WM8985_RIGHT_ADC_BOOST_CTRL,
                0, 7, 0, boost_tlv)
 };
 
-static const struct snd_soc_dapm_widget wm8985_dapm_widgets[] = {
+static const struct snd_soc_dapm_widget wm8985_common_dapm_widgets[] = {
        SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8985_POWER_MANAGEMENT_3,
                0, 0),
        SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8985_POWER_MANAGEMENT_3,
@@ -431,21 +450,11 @@ static const struct snd_soc_dapm_widget wm8985_dapm_widgets[] = {
        SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8985_POWER_MANAGEMENT_2,
                1, 0),
 
-       SND_SOC_DAPM_MIXER("Left Output Mixer", WM8985_POWER_MANAGEMENT_3,
-               2, 0, left_out_mixer, ARRAY_SIZE(left_out_mixer)),
-       SND_SOC_DAPM_MIXER("Right Output Mixer", WM8985_POWER_MANAGEMENT_3,
-               3, 0, right_out_mixer, ARRAY_SIZE(right_out_mixer)),
-
        SND_SOC_DAPM_MIXER("Left Input Mixer", WM8985_POWER_MANAGEMENT_2,
                2, 0, left_input_mixer, ARRAY_SIZE(left_input_mixer)),
        SND_SOC_DAPM_MIXER("Right Input Mixer", WM8985_POWER_MANAGEMENT_2,
                3, 0, right_input_mixer, ARRAY_SIZE(right_input_mixer)),
 
-       SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8985_POWER_MANAGEMENT_2,
-               4, 0, left_boost_mixer, ARRAY_SIZE(left_boost_mixer)),
-       SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8985_POWER_MANAGEMENT_2,
-               5, 0, right_boost_mixer, ARRAY_SIZE(right_boost_mixer)),
-
        SND_SOC_DAPM_PGA("Left Capture PGA", WM8985_LEFT_INP_PGA_GAIN_CTRL,
                6, 1, NULL, 0),
        SND_SOC_DAPM_PGA("Right Capture PGA", WM8985_RIGHT_INP_PGA_GAIN_CTRL,
@@ -468,8 +477,6 @@ static const struct snd_soc_dapm_widget wm8985_dapm_widgets[] = {
        SND_SOC_DAPM_INPUT("LIP"),
        SND_SOC_DAPM_INPUT("RIN"),
        SND_SOC_DAPM_INPUT("RIP"),
-       SND_SOC_DAPM_INPUT("AUXL"),
-       SND_SOC_DAPM_INPUT("AUXR"),
        SND_SOC_DAPM_INPUT("L2"),
        SND_SOC_DAPM_INPUT("R2"),
        SND_SOC_DAPM_OUTPUT("HPL"),
@@ -478,13 +485,42 @@ static const struct snd_soc_dapm_widget wm8985_dapm_widgets[] = {
        SND_SOC_DAPM_OUTPUT("SPKR")
 };
 
-static const struct snd_soc_dapm_route wm8985_dapm_routes[] = {
+static const struct snd_soc_dapm_widget wm8985_dapm_widgets[] = {
+       SND_SOC_DAPM_MIXER("Left Output Mixer", WM8985_POWER_MANAGEMENT_3,
+               2, 0, left_out_mixer, ARRAY_SIZE(left_out_mixer)),
+       SND_SOC_DAPM_MIXER("Right Output Mixer", WM8985_POWER_MANAGEMENT_3,
+               3, 0, right_out_mixer, ARRAY_SIZE(right_out_mixer)),
+
+       SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8985_POWER_MANAGEMENT_2,
+               4, 0, left_boost_mixer, ARRAY_SIZE(left_boost_mixer)),
+       SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8985_POWER_MANAGEMENT_2,
+               5, 0, right_boost_mixer, ARRAY_SIZE(right_boost_mixer)),
+
+       SND_SOC_DAPM_INPUT("AUXL"),
+       SND_SOC_DAPM_INPUT("AUXR"),
+};
+
+static const struct snd_soc_dapm_widget wm8758_dapm_widgets[] = {
+       SND_SOC_DAPM_MIXER("Left Output Mixer", WM8985_POWER_MANAGEMENT_3,
+               2, 0, left_out_mixer,
+               ARRAY_SIZE(left_out_mixer) - 1),
+       SND_SOC_DAPM_MIXER("Right Output Mixer", WM8985_POWER_MANAGEMENT_3,
+               3, 0, right_out_mixer,
+               ARRAY_SIZE(right_out_mixer) - 1),
+
+       SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8985_POWER_MANAGEMENT_2,
+               4, 0, left_boost_mixer,
+               ARRAY_SIZE(left_boost_mixer) - 1),
+       SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8985_POWER_MANAGEMENT_2,
+               5, 0, right_boost_mixer,
+               ARRAY_SIZE(right_boost_mixer) - 1),
+};
+
+static const struct snd_soc_dapm_route wm8985_common_dapm_routes[] = {
        { "Right Output Mixer", "PCM Switch", "Right DAC" },
-       { "Right Output Mixer", "Aux Switch", "AUXR" },
        { "Right Output Mixer", "Line Switch", "Right Boost Mixer" },
 
        { "Left Output Mixer", "PCM Switch", "Left DAC" },
-       { "Left Output Mixer", "Aux Switch", "AUXL" },
        { "Left Output Mixer", "Line Switch", "Left Boost Mixer" },
 
        { "Right Headphone Out", NULL, "Right Output Mixer" },
@@ -501,13 +537,11 @@ static const struct snd_soc_dapm_route wm8985_dapm_routes[] = {
 
        { "Right ADC", NULL, "Right Boost Mixer" },
 
-       { "Right Boost Mixer", "AUXR Volume", "AUXR" },
        { "Right Boost Mixer", NULL, "Right Capture PGA" },
        { "Right Boost Mixer", "R2 Volume", "R2" },
 
        { "Left ADC", NULL, "Left Boost Mixer" },
 
-       { "Left Boost Mixer", "AUXL Volume", "AUXL" },
        { "Left Boost Mixer", NULL, "Left Capture PGA" },
        { "Left Boost Mixer", "L2 Volume", "L2" },
 
@@ -522,6 +556,38 @@ static const struct snd_soc_dapm_route wm8985_dapm_routes[] = {
        { "Left Input Mixer", "MicN Switch", "LIN" },
        { "Left Input Mixer", "MicP Switch", "LIP" },
 };
+static const struct snd_soc_dapm_route wm8985_aux_dapm_routes[] = {
+       { "Right Output Mixer", "Aux Switch", "AUXR" },
+       { "Left Output Mixer", "Aux Switch", "AUXL" },
+
+       { "Right Boost Mixer", "AUXR Volume", "AUXR" },
+       { "Left Boost Mixer", "AUXL Volume", "AUXL" },
+};
+
+static int wm8985_add_widgets(struct snd_soc_codec *codec)
+{
+       struct wm8985_priv *wm8985 = snd_soc_codec_get_drvdata(codec);
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+       switch (wm8985->dev_type) {
+       case WM8758:
+               snd_soc_dapm_new_controls(dapm, wm8758_dapm_widgets,
+                                         ARRAY_SIZE(wm8758_dapm_widgets));
+               break;
+
+       case WM8985:
+               snd_soc_add_codec_controls(codec, wm8985_specific_snd_controls,
+                       ARRAY_SIZE(wm8985_specific_snd_controls));
+
+               snd_soc_dapm_new_controls(dapm, wm8985_dapm_widgets,
+                       ARRAY_SIZE(wm8985_dapm_widgets));
+               snd_soc_dapm_add_routes(dapm, wm8985_aux_dapm_routes,
+                       ARRAY_SIZE(wm8985_aux_dapm_routes));
+               break;
+       }
+
+       return 0;
+}
 
 static int eqmode_get(struct snd_kcontrol *kcontrol,
                      struct snd_ctl_elem_value *ucontrol)
@@ -999,6 +1065,8 @@ static int wm8985_probe(struct snd_soc_codec *codec)
        snd_soc_update_bits(codec, WM8985_BIAS_CTRL, WM8985_BIASCUT,
                            WM8985_BIASCUT);
 
+       wm8985_add_widgets(codec);
+
        return 0;
 
 err_reg_enable:
@@ -1042,12 +1110,12 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8985 = {
        .set_bias_level = wm8985_set_bias_level,
        .suspend_bias_off = true,
 
-       .controls = wm8985_snd_controls,
-       .num_controls = ARRAY_SIZE(wm8985_snd_controls),
-       .dapm_widgets = wm8985_dapm_widgets,
-       .num_dapm_widgets = ARRAY_SIZE(wm8985_dapm_widgets),
-       .dapm_routes = wm8985_dapm_routes,
-       .num_dapm_routes = ARRAY_SIZE(wm8985_dapm_routes),
+       .controls = wm8985_common_snd_controls,
+       .num_controls = ARRAY_SIZE(wm8985_common_snd_controls),
+       .dapm_widgets = wm8985_common_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(wm8985_common_dapm_widgets),
+       .dapm_routes = wm8985_common_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(wm8985_common_dapm_routes),
 };
 
 static const struct regmap_config wm8985_regmap = {
@@ -1074,6 +1142,8 @@ static int wm8985_spi_probe(struct spi_device *spi)
 
        spi_set_drvdata(spi, wm8985);
 
+       wm8985->dev_type = WM8985;
+
        wm8985->regmap = devm_regmap_init_spi(spi, &wm8985_regmap);
        if (IS_ERR(wm8985->regmap)) {
                ret = PTR_ERR(wm8985->regmap);
@@ -1115,6 +1185,8 @@ static int wm8985_i2c_probe(struct i2c_client *i2c,
 
        i2c_set_clientdata(i2c, wm8985);
 
+       wm8985->dev_type = id->driver_data;
+
        wm8985->regmap = devm_regmap_init_i2c(i2c, &wm8985_regmap);
        if (IS_ERR(wm8985->regmap)) {
                ret = PTR_ERR(wm8985->regmap);
@@ -1135,7 +1207,8 @@ static int wm8985_i2c_remove(struct i2c_client *i2c)
 }
 
 static const struct i2c_device_id wm8985_i2c_id[] = {
-       { "wm8985", 0 },
+       { "wm8985", WM8985 },
+       { "wm8758", WM8758 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, wm8985_i2c_id);
@@ -1183,6 +1256,6 @@ static void __exit wm8985_exit(void)
 }
 module_exit(wm8985_exit);
 
-MODULE_DESCRIPTION("ASoC WM8985 driver");
+MODULE_DESCRIPTION("ASoC WM8985 / WM8758 driver");
 MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>");
 MODULE_LICENSE("GPL");
index 2e71ff507638f1f4c8a73b0f60bc0b3beb364e7d..41b1048e3c9731218b54090ddf29334f1ec8f4d4 100644 (file)
 #define WM8985_GPIO1GPD_MASK                    0x0040  /* GPIO1GPD */
 #define WM8985_GPIO1GPD_SHIFT                        6  /* GPIO1GPD */
 #define WM8985_GPIO1GPD_WIDTH                        1  /* GPIO1GPD */
+#define WM8758_OPCLKDIV_MASK                    0x0030  /* OPCLKDIV - [1:0] */
+#define WM8758_OPCLKDIV_SHIFT                        4  /* OPCLKDIV - [1:0] */
+#define WM8758_OPCLKDIV_WIDTH                        2  /* OPCLKDIV - [1:0] */
 #define WM8985_GPIO1POL                         0x0008  /* GPIO1POL */
 #define WM8985_GPIO1POL_MASK                    0x0008  /* GPIO1POL */
 #define WM8985_GPIO1POL_SHIFT                        3  /* GPIO1POL */
 /*
  * R9 (0x09) - Jack Detect Control 1
  */
+#define WM8758_JD_VMID1_MASK                    0x0100  /* JD_VMID1 */
+#define WM8758_JD_VMID1_SHIFT                        8  /* JD_VMID1 */
+#define WM8758_JD_VMID1_WIDTH                        1  /* JD_VMID1 */
+#define WM8758_JD_VMID0_MASK                    0x0080  /* JD_VMID0 */
+#define WM8758_JD_VMID0_SHIFT                        7  /* JD_VMID0 */
+#define WM8758_JD_VMID0_WIDTH                        1  /* JD_VMID0 */
 #define WM8985_JD_EN                            0x0040  /* JD_EN */
 #define WM8985_JD_EN_MASK                       0x0040  /* JD_EN */
 #define WM8985_JD_EN_SHIFT                           6  /* JD_EN */
 #define WM8985_OUT4_2LNR_MASK                   0x0020  /* OUT4_2LNR */
 #define WM8985_OUT4_2LNR_SHIFT                       5  /* OUT4_2LNR */
 #define WM8985_OUT4_2LNR_WIDTH                       1  /* OUT4_2LNR */
+#define WM8758_VMIDTOG_MASK                     0x0010  /* VMIDTOG */
+#define WM8758_VMIDTOG_SHIFT                         4  /* VMIDTOG */
+#define WM8758_VMIDTOG_WIDTH                         1  /* VMIDTOG */
+#define WM8758_OUT2DEL_MASK                     0x0008  /* OUT2DEL */
+#define WM8758_OUT2DEL_SHIFT                         3  /* OUT2DEL */
+#define WM8758_OUT2DEL_WIDTH                         1  /* OUT2DEL */
 #define WM8985_POBCTRL                          0x0004  /* POBCTRL */
 #define WM8985_POBCTRL_MASK                     0x0004  /* POBCTRL */
 #define WM8985_POBCTRL_SHIFT                         2  /* POBCTRL */
 #define WM8985_BEEPVOL_MASK                     0x000E  /* BEEPVOL - [3:1] */
 #define WM8985_BEEPVOL_SHIFT                         1  /* BEEPVOL - [3:1] */
 #define WM8985_BEEPVOL_WIDTH                         3  /* BEEPVOL - [3:1] */
+#define WM8758_DELEN2_MASK                      0x0004  /* DELEN2 */
+#define WM8758_DELEN2_SHIFT                          2  /* DELEN2 */
+#define WM8758_DELEN2_WIDTH                          1  /* DELEN2 */
 #define WM8985_BEEPEN                           0x0001  /* BEEPEN */
 #define WM8985_BEEPEN_MASK                      0x0001  /* BEEPEN */
 #define WM8985_BEEPEN_SHIFT                          0  /* BEEPEN */
 /*
  * R49 (0x31) - Output ctrl
  */
+#define WM8758_HP_COM                           0x0100  /* HP_COM */
+#define WM8758_HP_COM_MASK                      0x0100  /* HP_COM */
+#define WM8758_HP_COM_SHIFT                          8  /* HP_COM */
+#define WM8758_HP_COM_WIDTH                          1  /* HP_COM */
+#define WM8758_LINE_COM                         0x0080  /* LINE_COM */
+#define WM8758_LINE_COM_MASK                    0x0080  /* LINE_COM */
+#define WM8758_LINE_COM_SHIFT                        7  /* LINE_COM */
+#define WM8758_LINE_COM_WIDTH                        1  /* LINE_COM */
 #define WM8985_DACL2RMIX                        0x0040  /* DACL2RMIX */
 #define WM8985_DACL2RMIX_MASK                   0x0040  /* DACL2RMIX */
 #define WM8985_DACL2RMIX_SHIFT                       6  /* DACL2RMIX */
 #define WM8985_OUT3BOOST_MASK                   0x0008  /* OUT3BOOST */
 #define WM8985_OUT3BOOST_SHIFT                       3  /* OUT3BOOST */
 #define WM8985_OUT3BOOST_WIDTH                       1  /* OUT3BOOST */
+#define WM8758_OUT4ENDEL                        0x0010  /* OUT4ENDEL */
+#define WM8758_OUT4ENDEL_MASK                   0x0010  /* OUT4ENDEL */
+#define WM8758_OUT4ENDEL_SHIFT                       4  /* OUT4ENDEL */
+#define WM8758_OUT4ENDEL_WIDTH                       1  /* OUT4ENDEL */
+#define WM8758_OUT3ENDEL                        0x0008  /* OUT3ENDEL */
+#define WM8758_OUT3ENDEL_MASK                   0x0008  /* OUT3ENDEL */
+#define WM8758_OUT3ENDEL_SHIFT                       3  /* OUT3ENDEL */
+#define WM8758_OUT3ENDEL_WIDTH                       1  /* OUT3ENDEL */
 #define WM8985_TSOPCTRL                         0x0004  /* TSOPCTRL */
 #define WM8985_TSOPCTRL_MASK                    0x0004  /* TSOPCTRL */
 #define WM8985_TSOPCTRL_SHIFT                        2  /* TSOPCTRL */
 #define WM8985_HALFIPBIAS_MASK                  0x0080  /* HALFIPBIAS */
 #define WM8985_HALFIPBIAS_SHIFT                      7  /* HALFIPBIAS */
 #define WM8985_HALFIPBIAS_WIDTH                      1  /* HALFIPBIAS */
+#define WM8758_HALFIPBIAS                       0x0040  /* HALFI_IPGA */
+#define WM8758_HALFI_IPGA_MASK                  0x0040  /* HALFI_IPGA */
+#define WM8758_HALFI_IPGA_SHIFT                      6  /* HALFI_IPGA */
+#define WM8758_HALFI_IPGA_WIDTH                      1  /* HALFI_IPGA */
 #define WM8985_VBBIASTST_MASK                   0x0060  /* VBBIASTST - [6:5] */
 #define WM8985_VBBIASTST_SHIFT                       5  /* VBBIASTST - [6:5] */
 #define WM8985_VBBIASTST_WIDTH                       2  /* VBBIASTST - [6:5] */
index 449f66636205a823b372e17e2b297fedfcd7ee1b..3a5c896a2d138f3fd9c7294786e5663373c10231 100644 (file)
@@ -1166,6 +1166,7 @@ static const struct snd_soc_dapm_route wm8998_dapm_routes[] = {
 
        { "MICSUPP", NULL, "SYSCLK" },
 
+       { "DRC1 Signal Activity", NULL, "SYSCLK" },
        { "DRC1 Signal Activity", NULL, "DRC1L" },
        { "DRC1 Signal Activity", NULL, "DRC1R" },
 };
index a07bd7c2c587dd9d9d0135015b1aa4b578307e93..21fbe7d070635dd7235a53bdd25db4c472eb3cc8 100644 (file)
@@ -394,6 +394,7 @@ static const struct {
        int compr_direction;
        int num_caps;
        const struct wm_adsp_fw_caps *caps;
+       bool voice_trigger;
 } wm_adsp_fw[WM_ADSP_NUM_FW] = {
        [WM_ADSP_FW_MBC_VSS] =  { .file = "mbc-vss" },
        [WM_ADSP_FW_HIFI] =     { .file = "hifi" },
@@ -406,6 +407,7 @@ static const struct {
                .compr_direction = SND_COMPRESS_CAPTURE,
                .num_caps = ARRAY_SIZE(ctrl_caps),
                .caps = ctrl_caps,
+               .voice_trigger = true,
        },
        [WM_ADSP_FW_ASR] =      { .file = "asr" },
        [WM_ADSP_FW_TRACE] =    {
@@ -2366,13 +2368,15 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
                dsp->running = false;
 
                regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
-                                  ADSP2_SYS_ENA | ADSP2_CORE_ENA |
-                                  ADSP2_START, 0);
+                                  ADSP2_CORE_ENA | ADSP2_START, 0);
 
                /* Make sure DMAs are quiesced */
+               regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
                regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
                regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
-               regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
+
+               regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+                                  ADSP2_SYS_ENA, 0);
 
                list_for_each_entry(ctl, &dsp->ctl_list, list)
                        ctl->enabled = 0;
@@ -2998,6 +3002,9 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
                goto out;
        }
 
+       if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2)
+               ret = WM_ADSP_COMPR_VOICE_TRIGGER;
+
 out_notify:
        if (compr && compr->stream)
                snd_compr_fragment_elapsed(compr->stream);
@@ -3037,12 +3044,8 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
 
        buf = compr->buf;
 
-       if (!compr->buf) {
-               ret = -ENXIO;
-               goto out;
-       }
-
-       if (compr->buf->error) {
+       if (!compr->buf || compr->buf->error) {
+               snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN);
                ret = -EIO;
                goto out;
        }
@@ -3060,8 +3063,12 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
                 */
                if (buf->avail < wm_adsp_compr_frag_words(compr)) {
                        ret = wm_adsp_buffer_get_error(buf);
-                       if (ret < 0)
+                       if (ret < 0) {
+                               if (compr->buf->error)
+                                       snd_compr_stop_error(stream,
+                                                       SNDRV_PCM_STATE_XRUN);
                                goto out;
+                       }
 
                        ret = wm_adsp_buffer_reenable_irq(buf);
                        if (ret < 0) {
@@ -3156,11 +3163,10 @@ static int wm_adsp_compr_read(struct wm_adsp_compr *compr,
 
        adsp_dbg(dsp, "Requested read of %zu bytes\n", count);
 
-       if (!compr->buf)
-               return -ENXIO;
-
-       if (compr->buf->error)
+       if (!compr->buf || compr->buf->error) {
+               snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN);
                return -EIO;
+       }
 
        count /= WM_ADSP_DATA_WORD_SIZE;
 
index feb61e2c4bb46717831414bc81f6d0267630722c..be3b5bcb7f178c00e0cbb406b7e330970fc7bba1 100644 (file)
 
 #include "wmfw.h"
 
+/* Return values for wm_adsp_compr_handle_irq */
+#define WM_ADSP_COMPR_OK                 0
+#define WM_ADSP_COMPR_VOICE_TRIGGER      1
+
 struct wm_adsp_region {
        int type;
        unsigned int base;
index 237dc67002efbefd600f96bb76997d21890a8b9f..05c2d33aa74dd9273100c9e8edf1b00bb57d8e35 100644 (file)
@@ -1599,7 +1599,14 @@ static struct davinci_mcasp_pdata *davinci_mcasp_set_pdata_from_of(
                pdata = pdev->dev.platform_data;
                return pdata;
        } else if (match) {
-               pdata = (struct davinci_mcasp_pdata*) match->data;
+               pdata = devm_kmemdup(&pdev->dev, match->data, sizeof(*pdata),
+                                    GFP_KERNEL);
+               if (!pdata) {
+                       dev_err(&pdev->dev,
+                               "Failed to allocate memory for pdata\n");
+                       ret = -ENOMEM;
+                       return pdata;
+               }
        } else {
                /* control shouldn't reach here. something is wrong */
                ret = -EINVAL;
index d50e08517dce57502d6548d7852d5792c9c56ad7..c297efe43861821e7313790d12c6613812e2b80c 100644 (file)
@@ -7,4 +7,13 @@ config SND_DESIGNWARE_I2S
         Synopsys desigwnware I2S device. The device supports upto
         maximum of 8 channels each for play and record.
 
+config SND_DESIGNWARE_PCM
+       tristate "PCM PIO extension for I2S driver"
+       depends on SND_DESIGNWARE_I2S
+       help
+        Say Y, M or N if you want to add a custom ALSA extension that registers
+        a PCM and uses PIO to transfer data.
+
+        This functionality is specially suited for I2S devices that don't have
+        DMA support.
 
index 319371f690f42ddf4278c2b1ba9b3b25543f534c..38f1ca31c5fa021c717775070bb140d42759e165 100644 (file)
@@ -1,3 +1,5 @@
 # SYNOPSYS Platform Support
 obj-$(CONFIG_SND_DESIGNWARE_I2S) += designware_i2s.o
-
+ifdef CONFIG_SND_DESIGNWARE_PCM
+obj-$(CONFIG_SND_DESIGNWARE_I2S) += designware_pcm.o
+endif
index 0db69b7e96170e471fe3101982a1cfeaa50dfb60..dc97f4349e66fc5c6c337c01c2cf75356902b34b 100644 (file)
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/dmaengine_pcm.h>
-
-/* common register for all channel */
-#define IER            0x000
-#define IRER           0x004
-#define ITER           0x008
-#define CER            0x00C
-#define CCR            0x010
-#define RXFFR          0x014
-#define TXFFR          0x018
-
-/* I2STxRxRegisters for all channels */
-#define LRBR_LTHR(x)   (0x40 * x + 0x020)
-#define RRBR_RTHR(x)   (0x40 * x + 0x024)
-#define RER(x)         (0x40 * x + 0x028)
-#define TER(x)         (0x40 * x + 0x02C)
-#define RCR(x)         (0x40 * x + 0x030)
-#define TCR(x)         (0x40 * x + 0x034)
-#define ISR(x)         (0x40 * x + 0x038)
-#define IMR(x)         (0x40 * x + 0x03C)
-#define ROR(x)         (0x40 * x + 0x040)
-#define TOR(x)         (0x40 * x + 0x044)
-#define RFCR(x)                (0x40 * x + 0x048)
-#define TFCR(x)                (0x40 * x + 0x04C)
-#define RFF(x)         (0x40 * x + 0x050)
-#define TFF(x)         (0x40 * x + 0x054)
-
-/* I2SCOMPRegisters */
-#define I2S_COMP_PARAM_2       0x01F0
-#define I2S_COMP_PARAM_1       0x01F4
-#define I2S_COMP_VERSION       0x01F8
-#define I2S_COMP_TYPE          0x01FC
-
-/*
- * Component parameter register fields - define the I2S block's
- * configuration.
- */
-#define        COMP1_TX_WORDSIZE_3(r)  (((r) & GENMASK(27, 25)) >> 25)
-#define        COMP1_TX_WORDSIZE_2(r)  (((r) & GENMASK(24, 22)) >> 22)
-#define        COMP1_TX_WORDSIZE_1(r)  (((r) & GENMASK(21, 19)) >> 19)
-#define        COMP1_TX_WORDSIZE_0(r)  (((r) & GENMASK(18, 16)) >> 16)
-#define        COMP1_TX_CHANNELS(r)    (((r) & GENMASK(10, 9)) >> 9)
-#define        COMP1_RX_CHANNELS(r)    (((r) & GENMASK(8, 7)) >> 7)
-#define        COMP1_RX_ENABLED(r)     (((r) & BIT(6)) >> 6)
-#define        COMP1_TX_ENABLED(r)     (((r) & BIT(5)) >> 5)
-#define        COMP1_MODE_EN(r)        (((r) & BIT(4)) >> 4)
-#define        COMP1_FIFO_DEPTH_GLOBAL(r)      (((r) & GENMASK(3, 2)) >> 2)
-#define        COMP1_APB_DATA_WIDTH(r) (((r) & GENMASK(1, 0)) >> 0)
-
-#define        COMP2_RX_WORDSIZE_3(r)  (((r) & GENMASK(12, 10)) >> 10)
-#define        COMP2_RX_WORDSIZE_2(r)  (((r) & GENMASK(9, 7)) >> 7)
-#define        COMP2_RX_WORDSIZE_1(r)  (((r) & GENMASK(5, 3)) >> 3)
-#define        COMP2_RX_WORDSIZE_0(r)  (((r) & GENMASK(2, 0)) >> 0)
-
-/* Number of entries in WORDSIZE and DATA_WIDTH parameter registers */
-#define        COMP_MAX_WORDSIZE       (1 << 3)
-#define        COMP_MAX_DATA_WIDTH     (1 << 2)
-
-#define MAX_CHANNEL_NUM                8
-#define MIN_CHANNEL_NUM                2
-
-union dw_i2s_snd_dma_data {
-       struct i2s_dma_data pd;
-       struct snd_dmaengine_dai_dma_data dt;
-};
-
-struct dw_i2s_dev {
-       void __iomem *i2s_base;
-       struct clk *clk;
-       int active;
-       unsigned int capability;
-       unsigned int quirks;
-       unsigned int i2s_reg_comp1;
-       unsigned int i2s_reg_comp2;
-       struct device *dev;
-       u32 ccr;
-       u32 xfer_resolution;
-       u32 fifo_th;
-
-       /* data related to DMA transfers b/w i2s and DMAC */
-       union dw_i2s_snd_dma_data play_dma_data;
-       union dw_i2s_snd_dma_data capture_dma_data;
-       struct i2s_clk_config_data config;
-       int (*i2s_clk_cfg)(struct i2s_clk_config_data *config);
-};
+#include "local.h"
 
 static inline void i2s_write_reg(void __iomem *io_base, int reg, u32 val)
 {
@@ -145,51 +62,115 @@ static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream)
        }
 }
 
-static void i2s_start(struct dw_i2s_dev *dev,
-                     struct snd_pcm_substream *substream)
+static inline void i2s_disable_irqs(struct dw_i2s_dev *dev, u32 stream,
+                                   int chan_nr)
+{
+       u32 i, irq;
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               for (i = 0; i < (chan_nr / 2); i++) {
+                       irq = i2s_read_reg(dev->i2s_base, IMR(i));
+                       i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x30);
+               }
+       } else {
+               for (i = 0; i < (chan_nr / 2); i++) {
+                       irq = i2s_read_reg(dev->i2s_base, IMR(i));
+                       i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x03);
+               }
+       }
+}
+
+static inline void i2s_enable_irqs(struct dw_i2s_dev *dev, u32 stream,
+                                  int chan_nr)
 {
-       struct i2s_clk_config_data *config = &dev->config;
        u32 i, irq;
-       i2s_write_reg(dev->i2s_base, IER, 1);
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               for (i = 0; i < (config->chan_nr / 2); i++) {
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               for (i = 0; i < (chan_nr / 2); i++) {
                        irq = i2s_read_reg(dev->i2s_base, IMR(i));
                        i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x30);
                }
-               i2s_write_reg(dev->i2s_base, ITER, 1);
        } else {
-               for (i = 0; i < (config->chan_nr / 2); i++) {
+               for (i = 0; i < (chan_nr / 2); i++) {
                        irq = i2s_read_reg(dev->i2s_base, IMR(i));
                        i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x03);
                }
-               i2s_write_reg(dev->i2s_base, IRER, 1);
+       }
+}
+
+static irqreturn_t i2s_irq_handler(int irq, void *dev_id)
+{
+       struct dw_i2s_dev *dev = dev_id;
+       bool irq_valid = false;
+       u32 isr[4];
+       int i;
+
+       for (i = 0; i < 4; i++)
+               isr[i] = i2s_read_reg(dev->i2s_base, ISR(i));
+
+       i2s_clear_irqs(dev, SNDRV_PCM_STREAM_PLAYBACK);
+       i2s_clear_irqs(dev, SNDRV_PCM_STREAM_CAPTURE);
+
+       for (i = 0; i < 4; i++) {
+               /*
+                * Check if TX fifo is empty. If empty fill FIFO with samples
+                * NOTE: Only two channels supported
+                */
+               if ((isr[i] & ISR_TXFE) && (i == 0) && dev->use_pio) {
+                       dw_pcm_push_tx(dev);
+                       irq_valid = true;
+               }
+
+               /* Data available. Record mode not supported in PIO mode */
+               if (isr[i] & ISR_RXDA)
+                       irq_valid = true;
+
+               /* Error Handling: TX */
+               if (isr[i] & ISR_TXFO) {
+                       dev_err(dev->dev, "TX overrun (ch_id=%d)\n", i);
+                       irq_valid = true;
+               }
+
+               /* Error Handling: TX */
+               if (isr[i] & ISR_RXFO) {
+                       dev_err(dev->dev, "RX overrun (ch_id=%d)\n", i);
+                       irq_valid = true;
+               }
        }
 
+       if (irq_valid)
+               return IRQ_HANDLED;
+       else
+               return IRQ_NONE;
+}
+
+static void i2s_start(struct dw_i2s_dev *dev,
+                     struct snd_pcm_substream *substream)
+{
+       struct i2s_clk_config_data *config = &dev->config;
+
+       i2s_write_reg(dev->i2s_base, IER, 1);
+       i2s_enable_irqs(dev, substream->stream, config->chan_nr);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               i2s_write_reg(dev->i2s_base, ITER, 1);
+       else
+               i2s_write_reg(dev->i2s_base, IRER, 1);
+
        i2s_write_reg(dev->i2s_base, CER, 1);
 }
 
 static void i2s_stop(struct dw_i2s_dev *dev,
                struct snd_pcm_substream *substream)
 {
-       u32 i = 0, irq;
 
        i2s_clear_irqs(dev, substream->stream);
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                i2s_write_reg(dev->i2s_base, ITER, 0);
-
-               for (i = 0; i < 4; i++) {
-                       irq = i2s_read_reg(dev->i2s_base, IMR(i));
-                       i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x30);
-               }
-       } else {
+       else
                i2s_write_reg(dev->i2s_base, IRER, 0);
 
-               for (i = 0; i < 4; i++) {
-                       irq = i2s_read_reg(dev->i2s_base, IMR(i));
-                       i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x03);
-               }
-       }
+       i2s_disable_irqs(dev, substream->stream, 8);
 
        if (!dev->active) {
                i2s_write_reg(dev->i2s_base, CER, 0);
@@ -223,7 +204,7 @@ static int dw_i2s_startup(struct snd_pcm_substream *substream,
 
 static void dw_i2s_config(struct dw_i2s_dev *dev, int stream)
 {
-       u32 ch_reg, irq;
+       u32 ch_reg;
        struct i2s_clk_config_data *config = &dev->config;
 
 
@@ -235,16 +216,12 @@ static void dw_i2s_config(struct dw_i2s_dev *dev, int stream)
                                      dev->xfer_resolution);
                        i2s_write_reg(dev->i2s_base, TFCR(ch_reg),
                                      dev->fifo_th - 1);
-                       irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
-                       i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30);
                        i2s_write_reg(dev->i2s_base, TER(ch_reg), 1);
                } else {
                        i2s_write_reg(dev->i2s_base, RCR(ch_reg),
                                      dev->xfer_resolution);
                        i2s_write_reg(dev->i2s_base, RFCR(ch_reg),
                                      dev->fifo_th - 1);
-                       irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
-                       i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03);
                        i2s_write_reg(dev->i2s_base, RER(ch_reg), 1);
                }
 
@@ -278,7 +255,7 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
                break;
 
        default:
-               dev_err(dev->dev, "designware-i2s: unsuppted PCM fmt");
+               dev_err(dev->dev, "designware-i2s: unsupported PCM fmt");
                return -EINVAL;
        }
 
@@ -626,7 +603,7 @@ static int dw_i2s_probe(struct platform_device *pdev)
        const struct i2s_platform_data *pdata = pdev->dev.platform_data;
        struct dw_i2s_dev *dev;
        struct resource *res;
-       int ret;
+       int ret, irq;
        struct snd_soc_dai_driver *dw_i2s_dai;
        const char *clk_id;
 
@@ -651,6 +628,16 @@ static int dw_i2s_probe(struct platform_device *pdev)
 
        dev->dev = &pdev->dev;
 
+       irq = platform_get_irq(pdev, 0);
+       if (irq >= 0) {
+               ret = devm_request_irq(&pdev->dev, irq, i2s_irq_handler, 0,
+                               pdev->name, dev);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "failed to request irq\n");
+                       return ret;
+               }
+       }
+
        dev->i2s_reg_comp1 = I2S_COMP_PARAM_1;
        dev->i2s_reg_comp2 = I2S_COMP_PARAM_2;
        if (pdata) {
@@ -697,12 +684,24 @@ static int dw_i2s_probe(struct platform_device *pdev)
 
        if (!pdata) {
                ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
-               if (ret) {
+               if (ret == -EPROBE_DEFER) {
+                       dev_err(&pdev->dev,
+                               "failed to register PCM, deferring probe\n");
+                       return ret;
+               } else if (ret) {
                        dev_err(&pdev->dev,
-                               "Could not register PCM: %d\n", ret);
-                       goto err_clk_disable;
+                               "Could not register DMA PCM: %d\n"
+                               "falling back to PIO mode\n", ret);
+                       ret = dw_pcm_register(pdev);
+                       if (ret) {
+                               dev_err(&pdev->dev,
+                                       "Could not register PIO PCM: %d\n",
+                                       ret);
+                               goto err_clk_disable;
+                       }
                }
        }
+
        pm_runtime_enable(&pdev->dev);
        return 0;
 
diff --git a/sound/soc/dwc/designware_pcm.c b/sound/soc/dwc/designware_pcm.c
new file mode 100644 (file)
index 0000000..4a83a22
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * ALSA SoC Synopsys PIO PCM for I2S driver
+ *
+ * sound/soc/dwc/designware_pcm.c
+ *
+ * Copyright (C) 2016 Synopsys
+ * Jose Abreu <joabreu@synopsys.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/io.h>
+#include <linux/rcupdate.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "local.h"
+
+#define BUFFER_BYTES_MAX       (3 * 2 * 8 * PERIOD_BYTES_MIN)
+#define PERIOD_BYTES_MIN       4096
+#define PERIODS_MIN            2
+
+#define dw_pcm_tx_fn(sample_bits) \
+static unsigned int dw_pcm_tx_##sample_bits(struct dw_i2s_dev *dev, \
+               struct snd_pcm_runtime *runtime, unsigned int tx_ptr, \
+               bool *period_elapsed) \
+{ \
+       const u##sample_bits (*p)[2] = (void *)runtime->dma_area; \
+       unsigned int period_pos = tx_ptr % runtime->period_size; \
+       int i; \
+\
+       for (i = 0; i < dev->fifo_th; i++) { \
+               iowrite32(p[tx_ptr][0], dev->i2s_base + LRBR_LTHR(0)); \
+               iowrite32(p[tx_ptr][1], dev->i2s_base + RRBR_RTHR(0)); \
+               period_pos++; \
+               if (++tx_ptr >= runtime->buffer_size) \
+                       tx_ptr = 0; \
+       } \
+       *period_elapsed = period_pos >= runtime->period_size; \
+       return tx_ptr; \
+}
+
+dw_pcm_tx_fn(16);
+dw_pcm_tx_fn(32);
+
+#undef dw_pcm_tx_fn
+
+static const struct snd_pcm_hardware dw_pcm_hardware = {
+       .info = SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_BLOCK_TRANSFER,
+       .rates = SNDRV_PCM_RATE_32000 |
+               SNDRV_PCM_RATE_44100 |
+               SNDRV_PCM_RATE_48000,
+       .rate_min = 32000,
+       .rate_max = 48000,
+       .formats = SNDRV_PCM_FMTBIT_S16_LE |
+               SNDRV_PCM_FMTBIT_S32_LE,
+       .channels_min = 2,
+       .channels_max = 2,
+       .buffer_bytes_max = BUFFER_BYTES_MAX,
+       .period_bytes_min = PERIOD_BYTES_MIN,
+       .period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN,
+       .periods_min = PERIODS_MIN,
+       .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
+       .fifo_size = 16,
+};
+
+void dw_pcm_push_tx(struct dw_i2s_dev *dev)
+{
+       struct snd_pcm_substream *tx_substream;
+       bool tx_active, period_elapsed;
+
+       rcu_read_lock();
+       tx_substream = rcu_dereference(dev->tx_substream);
+       tx_active = tx_substream && snd_pcm_running(tx_substream);
+       if (tx_active) {
+               unsigned int tx_ptr = READ_ONCE(dev->tx_ptr);
+               unsigned int new_tx_ptr = dev->tx_fn(dev, tx_substream->runtime,
+                               tx_ptr, &period_elapsed);
+               cmpxchg(&dev->tx_ptr, tx_ptr, new_tx_ptr);
+
+               if (period_elapsed)
+                       snd_pcm_period_elapsed(tx_substream);
+       }
+       rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(dw_pcm_push_tx);
+
+static int dw_pcm_open(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+
+       snd_soc_set_runtime_hwparams(substream, &dw_pcm_hardware);
+       snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+       runtime->private_data = dev;
+
+       return 0;
+}
+
+static int dw_pcm_close(struct snd_pcm_substream *substream)
+{
+       synchronize_rcu();
+       return 0;
+}
+
+static int dw_pcm_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct dw_i2s_dev *dev = runtime->private_data;
+       int ret;
+
+       switch (params_channels(hw_params)) {
+       case 2:
+               break;
+       default:
+               dev_err(dev->dev, "invalid channels number\n");
+               return -EINVAL;
+       }
+
+       switch (params_format(hw_params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               dev->tx_fn = dw_pcm_tx_16;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               dev->tx_fn = dw_pcm_tx_32;
+               break;
+       default:
+               dev_err(dev->dev, "invalid format\n");
+               return -EINVAL;
+       }
+
+       if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) {
+               dev_err(dev->dev, "only playback is available\n");
+               return -EINVAL;
+       }
+
+       ret = snd_pcm_lib_malloc_pages(substream,
+                       params_buffer_bytes(hw_params));
+       if (ret < 0)
+               return ret;
+       else
+               return 0;
+}
+
+static int dw_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+       return snd_pcm_lib_free_pages(substream);
+}
+
+static int dw_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct dw_i2s_dev *dev = runtime->private_data;
+       int ret = 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               WRITE_ONCE(dev->tx_ptr, 0);
+               rcu_assign_pointer(dev->tx_substream, substream);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               rcu_assign_pointer(dev->tx_substream, NULL);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static snd_pcm_uframes_t dw_pcm_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct dw_i2s_dev *dev = runtime->private_data;
+       snd_pcm_uframes_t pos = READ_ONCE(dev->tx_ptr);
+
+       return pos < runtime->buffer_size ? pos : 0;
+}
+
+static int dw_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+       size_t size = dw_pcm_hardware.buffer_bytes_max;
+
+       return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
+                       SNDRV_DMA_TYPE_CONTINUOUS,
+                       snd_dma_continuous_data(GFP_KERNEL), size, size);
+}
+
+static void dw_pcm_free(struct snd_pcm *pcm)
+{
+       snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static const struct snd_pcm_ops dw_pcm_ops = {
+       .open = dw_pcm_open,
+       .close = dw_pcm_close,
+       .ioctl = snd_pcm_lib_ioctl,
+       .hw_params = dw_pcm_hw_params,
+       .hw_free = dw_pcm_hw_free,
+       .trigger = dw_pcm_trigger,
+       .pointer = dw_pcm_pointer,
+};
+
+static const struct snd_soc_platform_driver dw_pcm_platform = {
+       .pcm_new = dw_pcm_new,
+       .pcm_free = dw_pcm_free,
+       .ops = &dw_pcm_ops,
+};
+
+int dw_pcm_register(struct platform_device *pdev)
+{
+       return devm_snd_soc_register_platform(&pdev->dev, &dw_pcm_platform);
+}
+EXPORT_SYMBOL_GPL(dw_pcm_register);
diff --git a/sound/soc/dwc/local.h b/sound/soc/dwc/local.h
new file mode 100644 (file)
index 0000000..68afd75
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright (ST) 2012 Rajeev Kumar (rajeevkumar.linux@gmail.com)
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __DESIGNWARE_LOCAL_H
+#define __DESIGNWARE_LOCAL_H
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm.h>
+#include <sound/designware_i2s.h>
+
+/* common register for all channel */
+#define IER            0x000
+#define IRER           0x004
+#define ITER           0x008
+#define CER            0x00C
+#define CCR            0x010
+#define RXFFR          0x014
+#define TXFFR          0x018
+
+/* Interrupt status register fields */
+#define ISR_TXFO       BIT(5)
+#define ISR_TXFE       BIT(4)
+#define ISR_RXFO       BIT(1)
+#define ISR_RXDA       BIT(0)
+
+/* I2STxRxRegisters for all channels */
+#define LRBR_LTHR(x)   (0x40 * x + 0x020)
+#define RRBR_RTHR(x)   (0x40 * x + 0x024)
+#define RER(x)         (0x40 * x + 0x028)
+#define TER(x)         (0x40 * x + 0x02C)
+#define RCR(x)         (0x40 * x + 0x030)
+#define TCR(x)         (0x40 * x + 0x034)
+#define ISR(x)         (0x40 * x + 0x038)
+#define IMR(x)         (0x40 * x + 0x03C)
+#define ROR(x)         (0x40 * x + 0x040)
+#define TOR(x)         (0x40 * x + 0x044)
+#define RFCR(x)                (0x40 * x + 0x048)
+#define TFCR(x)                (0x40 * x + 0x04C)
+#define RFF(x)         (0x40 * x + 0x050)
+#define TFF(x)         (0x40 * x + 0x054)
+
+/* I2SCOMPRegisters */
+#define I2S_COMP_PARAM_2       0x01F0
+#define I2S_COMP_PARAM_1       0x01F4
+#define I2S_COMP_VERSION       0x01F8
+#define I2S_COMP_TYPE          0x01FC
+
+/*
+ * Component parameter register fields - define the I2S block's
+ * configuration.
+ */
+#define        COMP1_TX_WORDSIZE_3(r)  (((r) & GENMASK(27, 25)) >> 25)
+#define        COMP1_TX_WORDSIZE_2(r)  (((r) & GENMASK(24, 22)) >> 22)
+#define        COMP1_TX_WORDSIZE_1(r)  (((r) & GENMASK(21, 19)) >> 19)
+#define        COMP1_TX_WORDSIZE_0(r)  (((r) & GENMASK(18, 16)) >> 16)
+#define        COMP1_TX_CHANNELS(r)    (((r) & GENMASK(10, 9)) >> 9)
+#define        COMP1_RX_CHANNELS(r)    (((r) & GENMASK(8, 7)) >> 7)
+#define        COMP1_RX_ENABLED(r)     (((r) & BIT(6)) >> 6)
+#define        COMP1_TX_ENABLED(r)     (((r) & BIT(5)) >> 5)
+#define        COMP1_MODE_EN(r)        (((r) & BIT(4)) >> 4)
+#define        COMP1_FIFO_DEPTH_GLOBAL(r)      (((r) & GENMASK(3, 2)) >> 2)
+#define        COMP1_APB_DATA_WIDTH(r) (((r) & GENMASK(1, 0)) >> 0)
+
+#define        COMP2_RX_WORDSIZE_3(r)  (((r) & GENMASK(12, 10)) >> 10)
+#define        COMP2_RX_WORDSIZE_2(r)  (((r) & GENMASK(9, 7)) >> 7)
+#define        COMP2_RX_WORDSIZE_1(r)  (((r) & GENMASK(5, 3)) >> 3)
+#define        COMP2_RX_WORDSIZE_0(r)  (((r) & GENMASK(2, 0)) >> 0)
+
+/* Number of entries in WORDSIZE and DATA_WIDTH parameter registers */
+#define        COMP_MAX_WORDSIZE       (1 << 3)
+#define        COMP_MAX_DATA_WIDTH     (1 << 2)
+
+#define MAX_CHANNEL_NUM                8
+#define MIN_CHANNEL_NUM                2
+
+union dw_i2s_snd_dma_data {
+       struct i2s_dma_data pd;
+       struct snd_dmaengine_dai_dma_data dt;
+};
+
+struct dw_i2s_dev {
+       void __iomem *i2s_base;
+       struct clk *clk;
+       int active;
+       unsigned int capability;
+       unsigned int quirks;
+       unsigned int i2s_reg_comp1;
+       unsigned int i2s_reg_comp2;
+       struct device *dev;
+       u32 ccr;
+       u32 xfer_resolution;
+       u32 fifo_th;
+
+       /* data related to DMA transfers b/w i2s and DMAC */
+       union dw_i2s_snd_dma_data play_dma_data;
+       union dw_i2s_snd_dma_data capture_dma_data;
+       struct i2s_clk_config_data config;
+       int (*i2s_clk_cfg)(struct i2s_clk_config_data *config);
+
+       /* data related to PIO transfers (TX) */
+       bool use_pio;
+       struct snd_pcm_substream __rcu *tx_substream;
+       unsigned int (*tx_fn)(struct dw_i2s_dev *dev,
+                       struct snd_pcm_runtime *runtime, unsigned int tx_ptr,
+                       bool *period_elapsed);
+       unsigned int tx_ptr;
+};
+
+#if IS_ENABLED(CONFIG_SND_DESIGNWARE_PCM)
+void dw_pcm_push_tx(struct dw_i2s_dev *dev);
+int dw_pcm_register(struct platform_device *pdev);
+#else
+void dw_pcm_push_tx(struct dw_i2s_dev *dev) { }
+int dw_pcm_register(struct platform_device *pdev)
+{
+       return -EINVAL;
+}
+#endif
+
+#endif
index 35aabf9dc503b8f4ac78bba4df86343585aaf8fb..19bdcac71775a50134070960d51b995c06687e75 100644 (file)
@@ -4,6 +4,7 @@ comment "Common SoC Audio options for Freescale CPUs:"
 
 config SND_SOC_FSL_ASRC
        tristate "Asynchronous Sample Rate Converter (ASRC) module support"
+       depends on HAS_DMA
        select REGMAP_MMIO
        select SND_SOC_GENERIC_DMAENGINE_PCM
        help
index 151849f7986396b335863222e1a3e599557bcd62..beec7934a2650057646354982af39a173bdee61f 100644 (file)
@@ -172,7 +172,7 @@ static void spdif_irq_uqrx_full(struct fsl_spdif_priv *spdif_priv, char name)
        if (*pos >= size * 2) {
                *pos = 0;
        } else if (unlikely((*pos % size) + 3 > size)) {
-               dev_err(&pdev->dev, "User bit receivce buffer overflow\n");
+               dev_err(&pdev->dev, "User bit receive buffer overflow\n");
                return;
        }
 
index 610f6125164093c65ba393a4a44d7e4b73a6ad86..c01c5dd68601b69d519f0fb7d1953efc994f61bd 100644 (file)
@@ -1,4 +1,8 @@
+config SND_SIMPLE_CARD_UTILS
+       tristate
+
 config SND_SIMPLE_CARD
        tristate "ASoC Simple sound card support"
+       select SND_SIMPLE_CARD_UTILS
        help
          This option enables generic simple sound card support
index 9c3b246792bf3858195417c11060c3ef9785087f..45602ca8536ea7935cf0a019eb399d2dd432cb5f 100644 (file)
@@ -1,3 +1,5 @@
+obj-$(CONFIG_SND_SIMPLE_CARD_UTILS) := simple-card-utils.o
+
 snd-soc-simple-card-objs       := simple-card.o
 
 obj-$(CONFIG_SND_SIMPLE_CARD)  += snd-soc-simple-card.o
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
new file mode 100644 (file)
index 0000000..d89a9a1
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * simple-card-core.c
+ *
+ * Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/of.h>
+#include <sound/simple_card_utils.h>
+
+int asoc_simple_card_parse_daifmt(struct device *dev,
+                                 struct device_node *node,
+                                 struct device_node *codec,
+                                 char *prefix,
+                                 unsigned int *retfmt)
+{
+       struct device_node *bitclkmaster = NULL;
+       struct device_node *framemaster = NULL;
+       int prefix_len = prefix ? strlen(prefix) : 0;
+       unsigned int daifmt;
+
+       daifmt = snd_soc_of_parse_daifmt(node, prefix,
+                                        &bitclkmaster, &framemaster);
+       daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
+
+       if (prefix_len && !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");
+
+               daifmt = 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;
+       }
+
+       of_node_put(bitclkmaster);
+       of_node_put(framemaster);
+
+       *retfmt = daifmt;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(asoc_simple_card_parse_daifmt);
+
+int asoc_simple_card_set_dailink_name(struct device *dev,
+                                     struct snd_soc_dai_link *dai_link,
+                                     const char *fmt, ...)
+{
+       va_list ap;
+       char *name = NULL;
+       int ret = -ENOMEM;
+
+       va_start(ap, fmt);
+       name = devm_kvasprintf(dev, GFP_KERNEL, fmt, ap);
+       va_end(ap);
+
+       if (name) {
+               ret = 0;
+
+               dai_link->name          = name;
+               dai_link->stream_name   = name;
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(asoc_simple_card_set_dailink_name);
+
+int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
+                                    char *prefix)
+{
+       char prop[128];
+       int ret;
+
+       snprintf(prop, sizeof(prop), "%sname", prefix);
+
+       /* Parse the card name from DT */
+       ret = snd_soc_of_parse_card_name(card, prop);
+       if (ret < 0)
+               return ret;
+
+       if (!card->name && card->dai_link)
+               card->name = card->dai_link->name;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name);
index 466492b7d4f59d00f115a92867031ea046832581..43295f02498239ef0578168f281f2856e38f6b5a 100644 (file)
 #include <sound/soc-dai.h>
 #include <sound/soc.h>
 
+struct asoc_simple_jack {
+       struct snd_soc_jack jack;
+       struct snd_soc_jack_pin pin;
+       struct snd_soc_jack_gpio gpio;
+};
+
 struct simple_card_data {
        struct snd_soc_card snd_card;
        struct simple_dai_props {
@@ -29,10 +35,8 @@ struct simple_card_data {
                unsigned int mclk_fs;
        } *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 asoc_simple_jack hp_jack;
+       struct asoc_simple_jack mic_jack;
        struct snd_soc_dai_link dai_link[];     /* dynamically allocated */
 };
 
@@ -40,6 +44,69 @@ struct simple_card_data {
 #define simple_priv_to_link(priv, i) ((priv)->snd_card.dai_link + i)
 #define simple_priv_to_props(priv, i) ((priv)->dai_props + i)
 
+#define PREFIX "simple-audio-card,"
+
+#define asoc_simple_card_init_hp(card, sjack, prefix)\
+       asoc_simple_card_init_jack(card, sjack, 1, prefix)
+#define asoc_simple_card_init_mic(card, sjack, prefix)\
+       asoc_simple_card_init_jack(card, sjack, 0, prefix)
+static int asoc_simple_card_init_jack(struct snd_soc_card *card,
+                                     struct asoc_simple_jack *sjack,
+                                     int is_hp, char *prefix)
+{
+       struct device *dev = card->dev;
+       enum of_gpio_flags flags;
+       char prop[128];
+       char *pin_name;
+       char *gpio_name;
+       int mask;
+       int det;
+
+       sjack->gpio.gpio = -ENOENT;
+
+       if (is_hp) {
+               snprintf(prop, sizeof(prop), "%shp-det-gpio", prefix);
+               pin_name        = "Headphones";
+               gpio_name       = "Headphone detection";
+               mask            = SND_JACK_HEADPHONE;
+       } else {
+               snprintf(prop, sizeof(prop), "%smic-det-gpio", prefix);
+               pin_name        = "Mic Jack";
+               gpio_name       = "Mic detection";
+               mask            = SND_JACK_MICROPHONE;
+       }
+
+       det = of_get_named_gpio_flags(dev->of_node, prop, 0, &flags);
+       if (det == -EPROBE_DEFER)
+               return -EPROBE_DEFER;
+
+       if (gpio_is_valid(det)) {
+               sjack->pin.pin          = pin_name;
+               sjack->pin.mask         = mask;
+
+               sjack->gpio.name        = gpio_name;
+               sjack->gpio.report      = mask;
+               sjack->gpio.gpio        = det;
+               sjack->gpio.invert      = !!(flags & OF_GPIO_ACTIVE_LOW);
+               sjack->gpio.debounce_time = 150;
+
+               snd_soc_card_jack_new(card, pin_name, mask,
+                                     &sjack->jack,
+                                     &sjack->pin, 1);
+
+               snd_soc_jack_add_gpios(&sjack->jack, 1,
+                                      &sjack->gpio);
+       }
+
+       return 0;
+}
+
+static void asoc_simple_card_remove_jack(struct asoc_simple_jack *sjack)
+{
+       if (gpio_is_valid(sjack->gpio.gpio))
+               snd_soc_jack_free_gpios(&sjack->jack, 1, &sjack->gpio);
+}
+
 static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -110,32 +177,6 @@ static struct snd_soc_ops asoc_simple_card_ops = {
        .hw_params = asoc_simple_card_hw_params,
 };
 
-static struct snd_soc_jack simple_card_hp_jack;
-static struct snd_soc_jack_pin simple_card_hp_jack_pins[] = {
-       {
-               .pin = "Headphones",
-               .mask = SND_JACK_HEADPHONE,
-       },
-};
-static struct snd_soc_jack_gpio simple_card_hp_jack_gpio = {
-       .name = "Headphone detection",
-       .report = SND_JACK_HEADPHONE,
-       .debounce_time = 150,
-};
-
-static struct snd_soc_jack simple_card_mic_jack;
-static struct snd_soc_jack_pin simple_card_mic_jack_pins[] = {
-       {
-               .pin = "Mic Jack",
-               .mask = SND_JACK_MICROPHONE,
-       },
-};
-static struct snd_soc_jack_gpio simple_card_mic_jack_gpio = {
-       .name = "Mic detection",
-       .report = SND_JACK_MICROPHONE,
-       .debounce_time = 150,
-};
-
 static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
                                       struct asoc_simple_dai *set)
 {
@@ -184,30 +225,14 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
        if (ret < 0)
                return ret;
 
-       if (gpio_is_valid(priv->gpio_hp_det)) {
-               snd_soc_card_jack_new(rtd->card, "Headphones",
-                                     SND_JACK_HEADPHONE,
-                                     &simple_card_hp_jack,
-                                     simple_card_hp_jack_pins,
-                                     ARRAY_SIZE(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);
-       }
+       ret = asoc_simple_card_init_hp(rtd->card, &priv->hp_jack, PREFIX);
+       if (ret < 0)
+               return ret;
+
+       ret = asoc_simple_card_init_mic(rtd->card, &priv->hp_jack, PREFIX);
+       if (ret < 0)
+               return ret;
 
-       if (gpio_is_valid(priv->gpio_mic_det)) {
-               snd_soc_card_jack_new(rtd->card, "Mic Jack",
-                                     SND_JACK_MICROPHONE,
-                                     &simple_card_mic_jack,
-                                     simple_card_mic_jack_pins,
-                                     ARRAY_SIZE(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);
-       }
        return 0;
 }
 
@@ -223,6 +248,9 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
        u32 val;
        int ret;
 
+       if (!np)
+               return 0;
+
        /*
         * Get node via "sound-dai = <&phandle port>"
         * it will be used as xxx_of_node on soc_bind_dai_link()
@@ -238,9 +266,14 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
                *args_count = args.args_count;
 
        /* Get dai->name */
-       ret = snd_soc_of_get_dai_name(np, name);
-       if (ret < 0)
-               return ret;
+       if (name) {
+               ret = snd_soc_of_get_dai_name(np, name);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (!dai)
+               return 0;
 
        /* Parse TDM slot */
        ret = snd_soc_of_parse_tdm_slot(np, &dai->tx_slot_mask,
@@ -275,48 +308,6 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
        return 0;
 }
 
-static int asoc_simple_card_parse_daifmt(struct device_node *node,
-                                        struct simple_card_data *priv,
-                                        struct device_node *codec,
-                                        char *prefix, int idx)
-{
-       struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
-       struct device *dev = simple_priv_to_dev(priv);
-       struct device_node *bitclkmaster = NULL;
-       struct device_node *framemaster = NULL;
-       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");
-
-               daifmt = 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;
-       }
-
-       dai_link->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,
@@ -328,7 +319,6 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
        struct device_node *cpu = NULL;
        struct device_node *plat = NULL;
        struct device_node *codec = NULL;
-       char *name;
        char prop[128];
        char *prefix = "";
        int ret, cpu_args;
@@ -336,7 +326,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
 
        /* For single DAI link & old style of DT node */
        if (is_top_level_node)
-               prefix = "simple-audio-card,";
+               prefix = PREFIX;
 
        snprintf(prop, sizeof(prop), "%scpu", prefix);
        cpu = of_get_child_by_name(node, prop);
@@ -353,8 +343,8 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
                goto dai_link_of_err;
        }
 
-       ret = asoc_simple_card_parse_daifmt(node, priv,
-                                           codec, prefix, idx);
+       ret = asoc_simple_card_parse_daifmt(dev, node, codec,
+                                           prefix, &dai_link->dai_fmt);
        if (ret < 0)
                goto dai_link_of_err;
 
@@ -374,35 +364,28 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
        if (ret < 0)
                goto dai_link_of_err;
 
+       ret = asoc_simple_card_sub_parse_of(plat, NULL,
+                                           &dai_link->platform_of_node,
+                                           NULL, NULL);
+       if (ret < 0)
+               goto dai_link_of_err;
+
        if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) {
                ret = -EINVAL;
                goto dai_link_of_err;
        }
 
-       if (plat) {
-               struct of_phandle_args args;
-
-               ret = of_parse_phandle_with_args(plat, "sound-dai",
-                                                "#sound-dai-cells", 0, &args);
-               dai_link->platform_of_node = args.np;
-       } else {
-               /* Assumes platform == cpu */
+       /* Assumes platform == cpu */
+       if (!dai_link->platform_of_node)
                dai_link->platform_of_node = dai_link->cpu_of_node;
-       }
 
-       /* DAI link name is created from CPU/CODEC dai name */
-       name = devm_kzalloc(dev,
-                           strlen(dai_link->cpu_dai_name)   +
-                           strlen(dai_link->codec_dai_name) + 2,
-                           GFP_KERNEL);
-       if (!name) {
-               ret = -ENOMEM;
+       ret = asoc_simple_card_set_dailink_name(dev, dai_link,
+                                               "%s-%s",
+                                               dai_link->cpu_dai_name,
+                                               dai_link->codec_dai_name);
+       if (ret < 0)
                goto dai_link_of_err;
-       }
 
-       sprintf(name, "%s-%s", dai_link->cpu_dai_name,
-                               dai_link->codec_dai_name);
-       dai_link->name = dai_link->stream_name = name;
        dai_link->ops = &asoc_simple_card_ops;
        dai_link->init = asoc_simple_card_dai_init;
 
@@ -438,42 +421,35 @@ 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;
 
        if (!node)
                return -EINVAL;
 
-       /* Parse the card name from DT */
-       snd_soc_of_parse_card_name(&priv->snd_card, "simple-audio-card,name");
-
        /* The off-codec widgets */
-       if (of_property_read_bool(node, "simple-audio-card,widgets")) {
+       if (of_property_read_bool(node, PREFIX "widgets")) {
                ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card,
-                                       "simple-audio-card,widgets");
+                                       PREFIX "widgets");
                if (ret)
                        return ret;
        }
 
        /* DAPM routes */
-       if (of_property_read_bool(node, "simple-audio-card,routing")) {
+       if (of_property_read_bool(node, PREFIX "routing")) {
                ret = snd_soc_of_parse_audio_routing(&priv->snd_card,
-                                       "simple-audio-card,routing");
+                                       PREFIX "routing");
                if (ret)
                        return ret;
        }
 
        /* Factor to mclk, used in hw_params() */
-       ret = of_property_read_u32(node, "simple-audio-card,mclk-fs", &val);
+       ret = of_property_read_u32(node, PREFIX "mclk-fs", &val);
        if (ret == 0)
                priv->mclk_fs = val;
 
-       dev_dbg(dev, "New simple-card: %s\n", priv->snd_card.name ?
-               priv->snd_card.name : "");
-
        /* Single/Muti DAI link(s) & New style of DT node */
-       if (of_get_child_by_name(node, "simple-audio-card,dai-link")) {
+       if (of_get_child_by_name(node, PREFIX "dai-link")) {
                struct device_node *np = NULL;
                int i = 0;
 
@@ -494,20 +470,9 @@ static int asoc_simple_card_parse_of(struct device_node *node,
                        return ret;
        }
 
-       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_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;
-
-       if (!priv->snd_card.name)
-               priv->snd_card.name = priv->snd_card.dai_link->name;
+       ret = asoc_simple_card_parse_card_name(&priv->snd_card, PREFIX);
+       if (ret)
+               return ret;
 
        return 0;
 }
@@ -536,7 +501,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
        int num_links, ret;
 
        /* Get the number of DAI links */
-       if (np && of_get_child_by_name(np, "simple-audio-card,dai-link"))
+       if (np && of_get_child_by_name(np, PREFIX "dai-link"))
                num_links = of_get_child_count(np);
        else
                num_links = 1;
@@ -555,9 +520,6 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
        priv->snd_card.dai_link = dai_link;
        priv->snd_card.num_links = num_links;
 
-       priv->gpio_hp_det = -ENOENT;
-       priv->gpio_mic_det = -ENOENT;
-
        /* Get room for the other properties */
        priv->dai_props = devm_kzalloc(dev,
                        sizeof(*priv->dai_props) * num_links,
@@ -624,12 +586,8 @@ static int asoc_simple_card_remove(struct platform_device *pdev)
        struct snd_soc_card *card = platform_get_drvdata(pdev);
        struct simple_card_data *priv = snd_soc_card_get_drvdata(card);
 
-       if (gpio_is_valid(priv->gpio_hp_det))
-               snd_soc_jack_free_gpios(&simple_card_hp_jack, 1,
-                                       &simple_card_hp_jack_gpio);
-       if (gpio_is_valid(priv->gpio_mic_det))
-               snd_soc_jack_free_gpios(&simple_card_mic_jack, 1,
-                                       &simple_card_mic_jack_gpio);
+       asoc_simple_card_remove_jack(&priv->hp_jack);
+       asoc_simple_card_remove_jack(&priv->mic_jack);
 
        return asoc_simple_card_unref(card);
 }
index 91c15abb625e81390311f3f875060af387cd6ae5..a20c3dfbcb5dd5fa2b058defabb6a318a895cc79 100644 (file)
@@ -7,7 +7,7 @@ config SND_MFLD_MACHINE
        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
-          Say Y if you have such a device
+          Say Y if you have such a device.
           If unsure select "N".
 
 config SND_SST_MFLD_PLATFORM
@@ -25,7 +25,6 @@ config SND_SST_IPC_ACPI
        tristate
        select SND_SST_IPC
        select SND_SOC_INTEL_SST
-       depends on ACPI
 
 config SND_SOC_INTEL_SST
        tristate
@@ -33,6 +32,12 @@ config SND_SOC_INTEL_SST
        select SND_SOC_INTEL_SST_MATCH if ACPI
        depends on (X86 || COMPILE_TEST)
 
+# firmware stuff depends DW_DMAC_CORE; since there is no depends-on from
+# the reverse selection, each machine driver needs to select
+# SND_SOC_INTEL_SST_FIRMWARE carefully depending on DW_DMAC_CORE
+config SND_SOC_INTEL_SST_FIRMWARE
+       tristate
+
 config SND_SOC_INTEL_SST_ACPI
        tristate
 
@@ -48,16 +53,33 @@ config SND_SOC_INTEL_BAYTRAIL
 config SND_SOC_INTEL_HASWELL_MACH
        tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint"
        depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM
-       depends on DW_DMAC_CORE=y
+       depends on DW_DMAC_CORE
        select SND_SOC_INTEL_SST
+       select SND_SOC_INTEL_SST_FIRMWARE
        select SND_SOC_INTEL_HASWELL
        select SND_SOC_RT5640
        help
          This adds support for the Lynxpoint Audio DSP on Intel(R) Haswell
          Ultrabook platforms.
-         Say Y if you have such a device
+         Say Y if you have such a device.
          If unsure select "N".
 
+config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH
+       tristate "ASoC Audio driver for Broxton with DA7219 and MAX98357A in I2S Mode"
+       depends on X86 && ACPI && I2C
+       select SND_SOC_INTEL_SST
+       select SND_SOC_INTEL_SKYLAKE
+       select SND_SOC_DA7219
+       select SND_SOC_MAX98357A
+       select SND_SOC_DMIC
+       select SND_SOC_HDAC_HDMI
+       select SND_HDA_DSP_LOADER
+       help
+          This adds support for ASoC machine driver for Broxton-P platforms
+          with DA7219 + MAX98357A I2S audio codec.
+          Say Y if you have such a device.
+          If unsure select "N".
+
 config SND_SOC_INTEL_BXT_RT298_MACH
        tristate "ASoC Audio driver for Broxton with RT298 I2S mode"
        depends on X86 && ACPI && I2C
@@ -70,26 +92,28 @@ config SND_SOC_INTEL_BXT_RT298_MACH
        help
           This adds support for ASoC machine driver for Broxton platforms
           with RT286 I2S audio codec.
-          Say Y if you have such a device
+          Say Y if you have such a device.
           If unsure select "N".
 
 config SND_SOC_INTEL_BYT_RT5640_MACH
        tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec"
        depends on X86_INTEL_LPSS && I2C
-       depends on DW_DMAC_CORE=y && (SND_SST_IPC_ACPI = n)
+       depends on DW_DMAC_CORE && (SND_SST_IPC_ACPI = n)
        select SND_SOC_INTEL_SST
+       select SND_SOC_INTEL_SST_FIRMWARE
        select SND_SOC_INTEL_BAYTRAIL
        select SND_SOC_RT5640
        help
          This adds audio driver for Intel Baytrail platform based boards
          with the RT5640 audio codec. This driver is deprecated, use
-         SND_SOC_INTEL_BYTCR_RT5640_MACH instead for better functionality
+         SND_SOC_INTEL_BYTCR_RT5640_MACH instead for better functionality.
 
 config SND_SOC_INTEL_BYT_MAX98090_MACH
        tristate "ASoC Audio driver for Intel Baytrail with MAX98090 codec"
        depends on X86_INTEL_LPSS && I2C
-       depends on DW_DMAC_CORE=y && (SND_SST_IPC_ACPI = n)
+       depends on DW_DMAC_CORE && (SND_SST_IPC_ACPI = n)
        select SND_SOC_INTEL_SST
+       select SND_SOC_INTEL_SST_FIRMWARE
        select SND_SOC_INTEL_BAYTRAIL
        select SND_SOC_MAX98090
        help
@@ -100,19 +124,20 @@ config SND_SOC_INTEL_BROADWELL_MACH
        tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint"
        depends on X86_INTEL_LPSS && I2C && DW_DMAC && \
                   I2C_DESIGNWARE_PLATFORM
-       depends on DW_DMAC_CORE=y
+       depends on DW_DMAC_CORE
        select SND_SOC_INTEL_SST
+       select SND_SOC_INTEL_SST_FIRMWARE
        select SND_SOC_INTEL_HASWELL
        select SND_SOC_RT286
        help
          This adds support for the Wilcatpoint Audio DSP on Intel(R) Broadwell
          Ultrabook platforms.
-         Say Y if you have such a device
+         Say Y if you have such a device.
          If unsure select "N".
 
 config SND_SOC_INTEL_BYTCR_RT5640_MACH
         tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5640 codec"
-       depends on X86 && I2C
+       depends on X86 && I2C && ACPI
        select SND_SOC_RT5640
        select SND_SST_MFLD_PLATFORM
        select SND_SST_IPC_ACPI
@@ -120,12 +145,12 @@ config SND_SOC_INTEL_BYTCR_RT5640_MACH
        help
           This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR
           platforms with RT5640 audio codec.
-          Say Y if you have such a device
+          Say Y if you have such a device.
           If unsure select "N".
 
 config SND_SOC_INTEL_BYTCR_RT5651_MACH
         tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5651 codec"
-       depends on X86 && I2C
+       depends on X86 && I2C && ACPI
        select SND_SOC_RT5651
        select SND_SST_MFLD_PLATFORM
        select SND_SST_IPC_ACPI
@@ -133,12 +158,12 @@ config SND_SOC_INTEL_BYTCR_RT5651_MACH
        help
           This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR
           platforms with RT5651 audio codec.
-          Say Y if you have such a device
+          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 && I2C
+        depends on X86_INTEL_LPSS && I2C && ACPI
         select SND_SOC_RT5670
         select SND_SST_MFLD_PLATFORM
         select SND_SST_IPC_ACPI
@@ -146,12 +171,12 @@ config SND_SOC_INTEL_CHT_BSW_RT5672_MACH
         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
+          Say Y if you have such a device.
           If unsure select "N".
 
 config SND_SOC_INTEL_CHT_BSW_RT5645_MACH
        tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5645/5650 codec"
-       depends on X86_INTEL_LPSS && I2C
+       depends on X86_INTEL_LPSS && I2C && ACPI
        select SND_SOC_RT5645
        select SND_SST_MFLD_PLATFORM
        select SND_SST_IPC_ACPI
@@ -163,16 +188,16 @@ config SND_SOC_INTEL_CHT_BSW_RT5645_MACH
 
 config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH
        tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with MAX98090 & TI codec"
-       depends on X86_INTEL_LPSS && I2C
+       depends on X86_INTEL_LPSS && I2C && ACPI
        select SND_SOC_MAX98090
        select SND_SOC_TS3A227E
        select SND_SST_MFLD_PLATFORM
        select SND_SST_IPC_ACPI
        select SND_SOC_INTEL_SST_MATCH if ACPI
        help
-      This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
-      platforms with MAX98090 audio codec it also can support TI jack chip as aux device.
-      If unsure select "N".
+         This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
+         platforms with MAX98090 audio codec it also can support TI jack chip as aux device.
+         If unsure select "N".
 
 config SND_SOC_INTEL_SKYLAKE
        tristate
@@ -192,7 +217,7 @@ config SND_SOC_INTEL_SKL_RT286_MACH
        help
           This adds support for ASoC machine driver for Skylake platforms
           with RT286 I2S audio codec.
-          Say Y if you have such a device
+          Say Y if you have such a device.
           If unsure select "N".
 
 config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH
@@ -207,7 +232,7 @@ config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH
        help
          This adds support for ASoC Onboard Codec I2S machine driver. This will
          create an alsa sound card for NAU88L25 + SSM4567.
-         Say Y if you have such a device
+         Say Y if you have such a device.
          If unsure select "N".
 
 config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH
@@ -222,5 +247,5 @@ config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH
        help
          This adds support for ASoC Onboard Codec I2S machine driver. This will
          create an alsa sound card for NAU88L25 + MAX98357A.
-         Say Y if you have such a device
+         Say Y if you have such a device.
          If unsure select "N".
index 3bc4b63b2f9daaefe7d135dd2100e1ea94f479b4..4d31849712273712549f404c9773daef0860f186 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/firmware.h>
 #include <linux/pm_runtime.h>
 #include <linux/pm_qos.h>
+#include <linux/dmi.h>
 #include <linux/acpi.h>
 #include <asm/platform_sst_audio.h>
 #include <sound/core.h>
@@ -237,6 +238,9 @@ static int sst_acpi_probe(struct platform_device *pdev)
                dev_err(dev, "No matching machine driver found\n");
                return -ENODEV;
        }
+       if (mach->machine_quirk)
+               mach = mach->machine_quirk(mach);
+
        pdata = mach->pdata;
 
        ret = kstrtouint(id->id, 16, &dev_id);
@@ -320,6 +324,44 @@ static int sst_acpi_remove(struct platform_device *pdev)
        return 0;
 }
 
+static unsigned long cht_machine_id;
+
+#define CHT_SURFACE_MACH 1
+
+static int cht_surface_quirk_cb(const struct dmi_system_id *id)
+{
+       cht_machine_id = CHT_SURFACE_MACH;
+       return 1;
+}
+
+
+static const struct dmi_system_id cht_table[] = {
+       {
+               .callback = cht_surface_quirk_cb,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"),
+               },
+       },
+};
+
+
+static struct sst_acpi_mach cht_surface_mach = {
+       "10EC5640", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
+                                                               &chv_platform_data };
+
+static struct sst_acpi_mach *cht_quirk(void *arg)
+{
+       struct sst_acpi_mach *mach = arg;
+
+       dmi_check_system(cht_table);
+
+       if (cht_machine_id == CHT_SURFACE_MACH)
+               return &cht_surface_mach;
+       else
+               return mach;
+}
+
 static struct sst_acpi_mach sst_acpi_bytcr[] = {
        {"10EC5640", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL,
                                                &byt_rvp_platform_data },
@@ -343,7 +385,7 @@ static struct sst_acpi_mach sst_acpi_chv[] = {
        {"193C9890", "cht-bsw-max98090", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
                                                &chv_platform_data },
        /* some CHT-T platforms rely on RT5640, use Baytrail machine driver */
-       {"10EC5640", "bytcr_rt5640", "intel/fw_sst_22a8.bin", "bytcr_rt5640", NULL,
+       {"10EC5640", "bytcr_rt5640", "intel/fw_sst_22a8.bin", "bytcr_rt5640", cht_quirk,
                                                &chv_platform_data },
 
        {},
index a8506774f510e6072ebdda949fcb5ddd99bb680b..dac03a06bfd8aabd5aad03a1afab6b7128e27bb4 100644 (file)
@@ -2,6 +2,7 @@ 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-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o
 snd-soc-sst-bxt-rt298-objs := bxt_rt298.o
 snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o
 snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o
@@ -15,6 +16,7 @@ snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.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_BXT_DA7219_MAX98357A_MACH) += snd-soc-sst-bxt-da7219_max98357a.o
 obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.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-rt5640.o
diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c
new file mode 100644 (file)
index 0000000..3774b11
--- /dev/null
@@ -0,0 +1,460 @@
+/*
+ * Intel Broxton-P I2S Machine Driver
+ *
+ * Copyright (C) 2016, Intel Corporation. All rights reserved.
+ *
+ * Modified from:
+ *   Intel Skylake I2S Machine driver
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../../codecs/hdac_hdmi.h"
+#include "../../codecs/da7219.h"
+#include "../../codecs/da7219-aad.h"
+
+#define BXT_DIALOG_CODEC_DAI   "da7219-hifi"
+#define BXT_MAXIM_CODEC_DAI    "HiFi"
+#define DUAL_CHANNEL           2
+
+static struct snd_soc_jack broxton_headset;
+
+enum {
+       BXT_DPCM_AUDIO_PB = 0,
+       BXT_DPCM_AUDIO_CP,
+       BXT_DPCM_AUDIO_REF_CP,
+       BXT_DPCM_AUDIO_HDMI1_PB,
+       BXT_DPCM_AUDIO_HDMI2_PB,
+       BXT_DPCM_AUDIO_HDMI3_PB,
+};
+
+static const struct snd_kcontrol_new broxton_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+       SOC_DAPM_PIN_SWITCH("Headset Mic"),
+       SOC_DAPM_PIN_SWITCH("Spk"),
+};
+
+static const struct snd_soc_dapm_widget broxton_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone Jack", NULL),
+       SND_SOC_DAPM_MIC("Headset Mic", NULL),
+       SND_SOC_DAPM_SPK("Spk", NULL),
+       SND_SOC_DAPM_MIC("SoC DMIC", NULL),
+       SND_SOC_DAPM_SPK("HDMI1", NULL),
+       SND_SOC_DAPM_SPK("HDMI2", NULL),
+       SND_SOC_DAPM_SPK("HDMI3", NULL),
+};
+
+static const struct snd_soc_dapm_route broxton_map[] = {
+       /* HP jack connectors - unknown if we have jack detection */
+       {"Headphone Jack", NULL, "HPL"},
+       {"Headphone Jack", NULL, "HPR"},
+
+       /* speaker */
+       {"Spk", NULL, "Speaker"},
+
+       /* other jacks */
+       {"MIC", NULL, "Headset Mic"},
+
+       /* digital mics */
+       {"DMic", NULL, "SoC DMIC"},
+
+       /* CODEC BE connections */
+       {"HiFi Playback", NULL, "ssp5 Tx"},
+       {"ssp5 Tx", NULL, "codec0_out"},
+
+       {"Playback", NULL, "ssp1 Tx"},
+       {"ssp1 Tx", NULL, "codec1_out"},
+
+       {"codec0_in", NULL, "ssp1 Rx"},
+       {"ssp1 Rx", NULL, "Capture"},
+
+       {"HDMI1", NULL, "hif5 Output"},
+       {"HDMI2", NULL, "hif6 Output"},
+       {"HDMI3", NULL, "hif7 Output"},
+
+       {"hifi3", NULL, "iDisp3 Tx"},
+       {"iDisp3 Tx", NULL, "iDisp3_out"},
+       {"hifi2", NULL, "iDisp2 Tx"},
+       {"iDisp2 Tx", NULL, "iDisp2_out"},
+       {"hifi1", NULL, "iDisp1 Tx"},
+       {"iDisp1 Tx", NULL, "iDisp1_out"},
+
+       /* DMIC */
+       {"dmic01_hifi", NULL, "DMIC01 Rx"},
+       {"DMIC01 Rx", NULL, "DMIC AIF"},
+};
+
+static int broxton_ssp_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);
+       struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+       /* The ADSP will convert the FE rate to 48k, stereo */
+       rate->min = rate->max = 48000;
+       channels->min = channels->max = DUAL_CHANNEL;
+
+       /* set SSP to 24 bit */
+       snd_mask_none(fmt);
+       snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE);
+
+       return 0;
+}
+
+static int broxton_da7219_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+       int ret;
+       struct snd_soc_codec *codec = rtd->codec;
+
+       /*
+        * Headset buttons map to the google Reference headset.
+        * These can be configured by userspace.
+        */
+       ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
+                       SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+                       SND_JACK_BTN_2 | SND_JACK_BTN_3, &broxton_headset,
+                       NULL, 0);
+       if (ret) {
+               dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
+               return ret;
+       }
+
+       da7219_aad_jack_det(codec, &broxton_headset);
+
+       snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
+
+       return ret;
+}
+
+static int broxton_hdmi_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_dai *dai = rtd->codec_dai;
+
+       return hdac_hdmi_jack_init(dai, BXT_DPCM_AUDIO_HDMI1_PB + dai->id);
+}
+
+static int broxton_da7219_fe_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_dapm_context *dapm;
+       struct snd_soc_component *component = rtd->cpu_dai->component;
+
+       dapm = snd_soc_component_get_dapm(component);
+       snd_soc_dapm_ignore_suspend(dapm, "Reference Capture");
+
+       return 0;
+}
+
+static unsigned int rates[] = {
+       48000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_rates = {
+       .count = ARRAY_SIZE(rates),
+       .list  = rates,
+       .mask = 0,
+};
+
+static unsigned int channels[] = {
+       DUAL_CHANNEL,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_channels = {
+       .count = ARRAY_SIZE(channels),
+       .list = channels,
+       .mask = 0,
+};
+
+static int bxt_fe_startup(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       /*
+        * On this platform for PCM device we support,
+        * 48Khz
+        * stereo
+        * 16 bit audio
+        */
+
+       runtime->hw.channels_max = DUAL_CHANNEL;
+       snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                                          &constraints_channels);
+
+       runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+       snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
+
+       snd_pcm_hw_constraint_list(runtime, 0,
+                               SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
+
+       return 0;
+}
+
+static const struct snd_soc_ops broxton_da7219_fe_ops = {
+       .startup = bxt_fe_startup,
+};
+
+static int broxton_da7219_hw_params(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       int ret;
+
+       ret = snd_soc_dai_set_sysclk(codec_dai,
+                       DA7219_CLKSRC_MCLK, 19200000, SND_SOC_CLOCK_IN);
+       if (ret < 0)
+               dev_err(codec_dai->dev, "can't set codec sysclk configuration\n");
+
+       ret = snd_soc_dai_set_pll(codec_dai, 0,
+                       DA7219_SYSCLK_PLL_SRM, 0, DA7219_PLL_FREQ_OUT_98304);
+       if (ret < 0) {
+               dev_err(codec_dai->dev, "failed to start PLL: %d\n", ret);
+               return -EIO;
+       }
+
+       return ret;
+}
+
+static int broxton_da7219_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       int ret;
+
+       ret = snd_soc_dai_set_pll(codec_dai, 0,
+                       DA7219_SYSCLK_MCLK, 0, 0);
+       if (ret < 0) {
+               dev_err(codec_dai->dev, "failed to stop PLL: %d\n", ret);
+               return -EIO;
+       }
+
+       return ret;
+}
+
+static struct snd_soc_ops broxton_da7219_ops = {
+       .hw_params = broxton_da7219_hw_params,
+       .hw_free = broxton_da7219_hw_free,
+};
+
+/* broxton digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link broxton_dais[] = {
+       /* Front End DAI links */
+       [BXT_DPCM_AUDIO_PB]
+       {
+               .name = "Bxt Audio Port",
+               .stream_name = "Audio",
+               .cpu_dai_name = "System Pin",
+               .platform_name = "0000:00:0e.0",
+               .dynamic = 1,
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .nonatomic = 1,
+               .init = broxton_da7219_fe_init,
+               .trigger = {
+                       SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+               .dpcm_playback = 1,
+               .ops = &broxton_da7219_fe_ops,
+       },
+       [BXT_DPCM_AUDIO_CP]
+       {
+               .name = "Bxt Audio Capture Port",
+               .stream_name = "Audio Record",
+               .cpu_dai_name = "System Pin",
+               .platform_name = "0000:00:0e.0",
+               .dynamic = 1,
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .nonatomic = 1,
+               .trigger = {
+                       SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+               .dpcm_capture = 1,
+               .ops = &broxton_da7219_fe_ops,
+       },
+       [BXT_DPCM_AUDIO_REF_CP]
+       {
+               .name = "Bxt Audio Reference cap",
+               .stream_name = "Refcap",
+               .cpu_dai_name = "Reference Pin",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .platform_name = "0000:00:0e.0",
+               .init = NULL,
+               .dpcm_capture = 1,
+               .ignore_suspend = 1,
+               .nonatomic = 1,
+               .dynamic = 1,
+       },
+       [BXT_DPCM_AUDIO_HDMI1_PB]
+       {
+               .name = "Bxt HDMI Port1",
+               .stream_name = "Hdmi1",
+               .cpu_dai_name = "HDMI1 Pin",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .platform_name = "0000:00:0e.0",
+               .dpcm_playback = 1,
+               .init = NULL,
+               .nonatomic = 1,
+               .dynamic = 1,
+       },
+       [BXT_DPCM_AUDIO_HDMI2_PB]
+       {
+               .name = "Bxt HDMI Port2",
+               .stream_name = "Hdmi2",
+               .cpu_dai_name = "HDMI2 Pin",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .platform_name = "0000:00:0e.0",
+               .dpcm_playback = 1,
+               .init = NULL,
+               .nonatomic = 1,
+               .dynamic = 1,
+       },
+       [BXT_DPCM_AUDIO_HDMI3_PB]
+       {
+               .name = "Bxt HDMI Port3",
+               .stream_name = "Hdmi3",
+               .cpu_dai_name = "HDMI3 Pin",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .platform_name = "0000:00:0e.0",
+               .dpcm_playback = 1,
+               .init = NULL,
+               .nonatomic = 1,
+               .dynamic = 1,
+       },
+       /* Back End DAI links */
+       {
+               /* SSP5 - Codec */
+               .name = "SSP5-Codec",
+               .id = 0,
+               .cpu_dai_name = "SSP5 Pin",
+               .platform_name = "0000:00:0e.0",
+               .no_pcm = 1,
+               .codec_name = "MX98357A:00",
+               .codec_dai_name = BXT_MAXIM_CODEC_DAI,
+               .dai_fmt = SND_SOC_DAIFMT_I2S |
+                       SND_SOC_DAIFMT_NB_NF |
+                       SND_SOC_DAIFMT_CBS_CFS,
+               .ignore_pmdown_time = 1,
+               .be_hw_params_fixup = broxton_ssp_fixup,
+               .dpcm_playback = 1,
+       },
+       {
+               /* SSP1 - Codec */
+               .name = "SSP1-Codec",
+               .id = 1,
+               .cpu_dai_name = "SSP1 Pin",
+               .platform_name = "0000:00:0e.0",
+               .no_pcm = 1,
+               .codec_name = "i2c-DLGS7219:00",
+               .codec_dai_name = BXT_DIALOG_CODEC_DAI,
+               .init = broxton_da7219_codec_init,
+               .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+                       SND_SOC_DAIFMT_CBS_CFS,
+               .ignore_pmdown_time = 1,
+               .be_hw_params_fixup = broxton_ssp_fixup,
+               .ops = &broxton_da7219_ops,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+       },
+       {
+               .name = "dmic01",
+               .id = 2,
+               .cpu_dai_name = "DMIC01 Pin",
+               .codec_name = "dmic-codec",
+               .codec_dai_name = "dmic-hifi",
+               .platform_name = "0000:00:0e.0",
+               .ignore_suspend = 1,
+               .dpcm_capture = 1,
+               .no_pcm = 1,
+       },
+       {
+               .name = "iDisp1",
+               .id = 3,
+               .cpu_dai_name = "iDisp1 Pin",
+               .codec_name = "ehdaudio0D2",
+               .codec_dai_name = "intel-hdmi-hifi1",
+               .platform_name = "0000:00:0e.0",
+               .init = broxton_hdmi_init,
+               .dpcm_playback = 1,
+               .no_pcm = 1,
+       },
+       {
+               .name = "iDisp2",
+               .id = 4,
+               .cpu_dai_name = "iDisp2 Pin",
+               .codec_name = "ehdaudio0D2",
+               .codec_dai_name = "intel-hdmi-hifi2",
+               .platform_name = "0000:00:0e.0",
+               .init = broxton_hdmi_init,
+               .dpcm_playback = 1,
+               .no_pcm = 1,
+       },
+       {
+               .name = "iDisp3",
+               .id = 5,
+               .cpu_dai_name = "iDisp3 Pin",
+               .codec_name = "ehdaudio0D2",
+               .codec_dai_name = "intel-hdmi-hifi3",
+               .platform_name = "0000:00:0e.0",
+               .init = broxton_hdmi_init,
+               .dpcm_playback = 1,
+               .no_pcm = 1,
+       },
+};
+
+/* broxton audio machine driver for SPT + da7219 */
+static struct snd_soc_card broxton_audio_card = {
+       .name = "bxtda7219max",
+       .owner = THIS_MODULE,
+       .dai_link = broxton_dais,
+       .num_links = ARRAY_SIZE(broxton_dais),
+       .controls = broxton_controls,
+       .num_controls = ARRAY_SIZE(broxton_controls),
+       .dapm_widgets = broxton_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(broxton_widgets),
+       .dapm_routes = broxton_map,
+       .num_dapm_routes = ARRAY_SIZE(broxton_map),
+       .fully_routed = true,
+};
+
+static int broxton_audio_probe(struct platform_device *pdev)
+{
+       broxton_audio_card.dev = &pdev->dev;
+       return devm_snd_soc_register_card(&pdev->dev, &broxton_audio_card);
+}
+
+static struct platform_driver broxton_audio = {
+       .probe = broxton_audio_probe,
+       .driver = {
+               .name = "bxt_da7219_max98357a_i2s",
+               .pm = &snd_soc_pm_ops,
+       },
+};
+module_platform_driver(broxton_audio)
+
+/* Module information */
+MODULE_DESCRIPTION("Audio Machine driver-DA7219 & MAX98357A in I2S mode");
+MODULE_AUTHOR("Sathyanarayana Nujella <sathyanarayana.nujella@intel.com>");
+MODULE_AUTHOR("Rohit Ainapure <rohit.m.ainapure@intel.com>");
+MODULE_AUTHOR("Harsha Priya <harshapriya.n@intel.com>");
+MODULE_AUTHOR("Conrad Cooke <conrad.cooke@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bxt_da7219_max98357a_i2s");
index f4787515c0edf9d42efa16b89c67fdcac1e997bc..253d7bfbf51179b14eeb7763f52a4a6775f38508 100644 (file)
@@ -33,6 +33,7 @@ enum {
        BXT_DPCM_AUDIO_PB = 0,
        BXT_DPCM_AUDIO_CP,
        BXT_DPCM_AUDIO_REF_CP,
+       BXT_DPCM_AUDIO_DMIC_CP,
        BXT_DPCM_AUDIO_HDMI1_PB,
        BXT_DPCM_AUDIO_HDMI2_PB,
        BXT_DPCM_AUDIO_HDMI3_PB,
@@ -88,6 +89,7 @@ static const struct snd_soc_dapm_route broxton_rt298_map[] = {
        /* CODEC BE connections */
        { "AIF1 Playback", NULL, "ssp5 Tx"},
        { "ssp5 Tx", NULL, "codec0_out"},
+       { "ssp5 Tx", NULL, "codec1_out"},
 
        { "codec0_in", NULL, "ssp5 Rx" },
        { "ssp5 Rx", NULL, "AIF1 Capture" },
@@ -104,6 +106,17 @@ static const struct snd_soc_dapm_route broxton_rt298_map[] = {
 
 };
 
+static int broxton_rt298_fe_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_dapm_context *dapm;
+       struct snd_soc_component *component = rtd->cpu_dai->component;
+
+       dapm = snd_soc_component_get_dapm(component);
+       snd_soc_dapm_ignore_suspend(dapm, "Reference Capture");
+
+       return 0;
+}
+
 static int broxton_rt298_codec_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_codec *codec = rtd->codec;
@@ -118,6 +131,9 @@ static int broxton_rt298_codec_init(struct snd_soc_pcm_runtime *rtd)
                return ret;
 
        rt298_mic_detect(codec, &broxton_headset);
+
+       snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
+
        return 0;
 }
 
@@ -169,6 +185,89 @@ static struct snd_soc_ops broxton_rt298_ops = {
        .hw_params = broxton_rt298_hw_params,
 };
 
+static unsigned int rates[] = {
+       48000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_rates = {
+       .count = ARRAY_SIZE(rates),
+       .list  = rates,
+       .mask = 0,
+};
+
+static int broxton_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
+                               struct snd_pcm_hw_params *params)
+{
+       struct snd_interval *channels = hw_param_interval(params,
+                                               SNDRV_PCM_HW_PARAM_CHANNELS);
+       if (params_channels(params) == 2)
+               channels->min = channels->max = 2;
+       else
+               channels->min = channels->max = 4;
+
+       return 0;
+}
+
+static unsigned int channels_dmic[] = {
+       2, 4,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_dmic_channels = {
+       .count = ARRAY_SIZE(channels_dmic),
+       .list = channels_dmic,
+       .mask = 0,
+};
+
+static int broxton_dmic_startup(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       runtime->hw.channels_max = 4;
+       snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                                       &constraints_dmic_channels);
+
+       return snd_pcm_hw_constraint_list(substream->runtime, 0,
+                               SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
+}
+
+static struct snd_soc_ops broxton_dmic_ops = {
+       .startup = broxton_dmic_startup,
+};
+
+static unsigned int channels[] = {
+       2,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_channels = {
+       .count = ARRAY_SIZE(channels),
+       .list = channels,
+       .mask = 0,
+};
+
+static int bxt_fe_startup(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       /*
+        * on this platform for PCM device we support:
+        *      48Khz
+        *      stereo
+        */
+
+       runtime->hw.channels_max = 2;
+       snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                               &constraints_channels);
+
+       snd_pcm_hw_constraint_list(runtime, 0,
+                               SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
+
+       return 0;
+}
+
+static const struct snd_soc_ops broxton_rt286_fe_ops = {
+       .startup = bxt_fe_startup,
+};
+
 /* broxton digital audio interface glue - connects codec <--> CPU */
 static struct snd_soc_dai_link broxton_rt298_dais[] = {
        /* Front End DAI links */
@@ -182,8 +281,10 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
                .dynamic = 1,
                .codec_name = "snd-soc-dummy",
                .codec_dai_name = "snd-soc-dummy-dai",
+               .init = broxton_rt298_fe_init,
                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
                .dpcm_playback = 1,
+               .ops = &broxton_rt286_fe_ops,
        },
        [BXT_DPCM_AUDIO_CP]
        {
@@ -197,6 +298,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
                .codec_dai_name = "snd-soc-dummy-dai",
                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
                .dpcm_capture = 1,
+               .ops = &broxton_rt286_fe_ops,
        },
        [BXT_DPCM_AUDIO_REF_CP]
        {
@@ -211,6 +313,20 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
                .nonatomic = 1,
                .dynamic = 1,
        },
+       [BXT_DPCM_AUDIO_DMIC_CP]
+       {
+               .name = "Bxt Audio DMIC cap",
+               .stream_name = "dmiccap",
+               .cpu_dai_name = "DMIC Pin",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .platform_name = "0000:00:0e.0",
+               .init = NULL,
+               .dpcm_capture = 1,
+               .nonatomic = 1,
+               .dynamic = 1,
+               .ops = &broxton_dmic_ops,
+       },
        [BXT_DPCM_AUDIO_HDMI1_PB]
        {
                .name = "Bxt HDMI Port1",
@@ -276,6 +392,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
                .codec_name = "dmic-codec",
                .codec_dai_name = "dmic-hifi",
                .platform_name = "0000:00:0e.0",
+               .be_hw_params_fixup = broxton_dmic_fixup,
                .ignore_suspend = 1,
                .dpcm_capture = 1,
                .no_pcm = 1,
@@ -341,6 +458,7 @@ static struct platform_driver broxton_audio = {
        .probe = broxton_audio_probe,
        .driver = {
                .name = "bxt_alc298s_i2s",
+               .pm = &snd_soc_pm_ops,
        },
 };
 module_platform_driver(broxton_audio)
index d7ef292c402d385c6522b321df632996e64cd755..56056ed7fcfd1e90b48f96316e1750fd92e113df 100644 (file)
@@ -30,6 +30,7 @@
 #include <sound/jack.h>
 #include "../../codecs/rt5645.h"
 #include "../atom/sst-atom-controls.h"
+#include "../common/sst-acpi.h"
 
 #define CHT_PLAT_CLK_3_HZ      19200000
 #define CHT_CODEC_DAI  "rt5645-aif1"
@@ -340,10 +341,13 @@ static struct snd_soc_card snd_soc_card_chtrt5650 = {
 };
 
 static struct cht_acpi_card snd_soc_cards[] = {
+       {"10EC5640", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
        {"10EC5645", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
        {"10EC5650", CODEC_TYPE_RT5650, &snd_soc_card_chtrt5650},
 };
 
+static char cht_rt5640_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
+
 static int snd_cht_mc_probe(struct platform_device *pdev)
 {
        int ret_val = 0;
@@ -351,6 +355,9 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
        struct cht_mc_private *drv;
        struct snd_soc_card *card = snd_soc_cards[0].soc_card;
        char codec_name[16];
+       struct sst_acpi_mach *mach;
+       const char *i2c_name = NULL;
+       int dai_index = 0;
 
        drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
        if (!drv)
@@ -366,12 +373,23 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
                }
        }
        card->dev = &pdev->dev;
+       mach = card->dev->platform_data;
        sprintf(codec_name, "i2c-%s:00", drv->acpi_card->codec_id);
 
        /* set correct codec name */
        for (i = 0; i < ARRAY_SIZE(cht_dailink); i++)
-               if (!strcmp(card->dai_link[i].codec_name, "i2c-10EC5645:00"))
+               if (!strcmp(card->dai_link[i].codec_name, "i2c-10EC5645:00")) {
                        card->dai_link[i].codec_name = kstrdup(codec_name, GFP_KERNEL);
+                       dai_index = i;
+               }
+
+       /* fixup codec name based on HID */
+       i2c_name = sst_acpi_find_name_from_hid(mach->id);
+       if (i2c_name != NULL) {
+               snprintf(cht_rt5640_codec_name, sizeof(cht_rt5640_codec_name),
+                       "%s%s", "i2c-", i2c_name);
+               cht_dailink[dai_index].codec_name = cht_rt5640_codec_name;
+       }
 
        snd_soc_card_set_drvdata(card, drv);
        ret_val = devm_snd_soc_register_card(&pdev->dev, card);
index d2808652b9741f2fced8381e356563b63d54bd48..25db5be7fdfade30fd2a084df5320ccd0f0179ec 100644 (file)
 #include <sound/soc.h>
 #include "../../codecs/nau8825.h"
 #include "../../codecs/hdac_hdmi.h"
+#include "../skylake/skl.h"
 
 #define SKL_NUVOTON_CODEC_DAI  "nau8825-hifi"
 #define SKL_MAXIM_CODEC_DAI "HiFi"
+#define DMIC_CH(p)     p->list[p->count-1]
 
 static struct snd_soc_jack skylake_headset;
 static struct snd_soc_card skylake_audio_card;
+static const struct snd_pcm_hw_constraint_list *dmic_constraints;
 
 struct skl_hdmi_pcm {
        struct list_head head;
@@ -339,7 +342,7 @@ static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
        struct snd_interval *channels = hw_param_interval(params,
                                SNDRV_PCM_HW_PARAM_CHANNELS);
 
-       if (params_channels(params) == 2)
+       if (params_channels(params) == 2 || DMIC_CH(dmic_constraints) == 2)
                channels->min = channels->max = 2;
        else
                channels->min = channels->max = 4;
@@ -357,13 +360,23 @@ static struct snd_pcm_hw_constraint_list constraints_dmic_channels = {
        .mask = 0,
 };
 
+static const unsigned int dmic_2ch[] = {
+       2,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_dmic_2ch = {
+       .count = ARRAY_SIZE(dmic_2ch),
+       .list = dmic_2ch,
+       .mask = 0,
+};
+
 static int skylake_dmic_startup(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
 
-       runtime->hw.channels_max = 4;
+       runtime->hw.channels_max = DMIC_CH(dmic_constraints);
        snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
-                       &constraints_dmic_channels);
+                       dmic_constraints);
 
        return snd_pcm_hw_constraint_list(substream->runtime, 0,
                        SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
@@ -382,8 +395,22 @@ static struct snd_pcm_hw_constraint_list constraints_16000 = {
        .list  = rates_16000,
 };
 
+static const unsigned int ch_mono[] = {
+       1,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_refcap = {
+       .count = ARRAY_SIZE(ch_mono),
+       .list  = ch_mono,
+};
+
 static int skylake_refcap_startup(struct snd_pcm_substream *substream)
 {
+       substream->runtime->hw.channels_max = 1;
+       snd_pcm_hw_constraint_list(substream->runtime, 0,
+                                       SNDRV_PCM_HW_PARAM_CHANNELS,
+                                       &constraints_refcap);
+
        return snd_pcm_hw_constraint_list(substream->runtime, 0,
                                SNDRV_PCM_HW_PARAM_RATE,
                                &constraints_16000);
@@ -610,6 +637,7 @@ static struct snd_soc_card skylake_audio_card = {
 static int skylake_audio_probe(struct platform_device *pdev)
 {
        struct skl_nau8825_private *ctx;
+       struct skl_machine_pdata *pdata;
 
        ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
        if (!ctx)
@@ -620,15 +648,27 @@ static int skylake_audio_probe(struct platform_device *pdev)
        skylake_audio_card.dev = &pdev->dev;
        snd_soc_card_set_drvdata(&skylake_audio_card, ctx);
 
+       pdata = dev_get_drvdata(&pdev->dev);
+       if (pdata)
+               dmic_constraints = pdata->dmic_num == 2 ?
+                       &constraints_dmic_2ch : &constraints_dmic_channels;
+
        return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card);
 }
 
+static const struct platform_device_id skl_board_ids[] = {
+       { .name = "skl_n88l25_m98357a" },
+       { .name = "kbl_n88l25_m98357a" },
+       { }
+};
+
 static struct platform_driver skylake_audio = {
        .probe = skylake_audio_probe,
        .driver = {
-               .name = "skl_nau88l25_max98357a_i2s",
+               .name = "skl_n88l25_m98357a",
                .pm = &snd_soc_pm_ops,
        },
+       .id_table = skl_board_ids,
 };
 
 module_platform_driver(skylake_audio)
@@ -637,4 +677,5 @@ module_platform_driver(skylake_audio)
 MODULE_DESCRIPTION("Audio Machine driver-NAU88L25 & MAX98357A in I2S mode");
 MODULE_AUTHOR("Rohit Ainapure <rohit.m.ainapure@intel.com");
 MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:skl_nau88l25_max98357a_i2s");
+MODULE_ALIAS("platform:skl_n88l25_m98357a");
+MODULE_ALIAS("platform:kbl_n88l25_m98357a");
index e19aa99c4f7246914fafd1819e561f3b60c7b250..69c5d5da4e86a6f5d6b3934ee8a0c3e844cd7fdf 100644 (file)
 #include <sound/pcm_params.h>
 #include "../../codecs/nau8825.h"
 #include "../../codecs/hdac_hdmi.h"
+#include "../skylake/skl.h"
 
 #define SKL_NUVOTON_CODEC_DAI  "nau8825-hifi"
 #define SKL_SSM_CODEC_DAI      "ssm4567-hifi"
+#define DMIC_CH(p)     p->list[p->count-1]
 
 static struct snd_soc_jack skylake_headset;
 static struct snd_soc_card skylake_audio_card;
+static const struct snd_pcm_hw_constraint_list *dmic_constraints;
 
 struct skl_hdmi_pcm {
        struct list_head head;
@@ -367,7 +370,7 @@ static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
 {
        struct snd_interval *channels = hw_param_interval(params,
                                                SNDRV_PCM_HW_PARAM_CHANNELS);
-       if (params_channels(params) == 2)
+       if (params_channels(params) == 2 || DMIC_CH(dmic_constraints) == 2)
                channels->min = channels->max = 2;
        else
                channels->min = channels->max = 4;
@@ -405,13 +408,23 @@ static struct snd_pcm_hw_constraint_list constraints_dmic_channels = {
        .mask = 0,
 };
 
+static const unsigned int dmic_2ch[] = {
+       2,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_dmic_2ch = {
+       .count = ARRAY_SIZE(dmic_2ch),
+       .list = dmic_2ch,
+       .mask = 0,
+};
+
 static int skylake_dmic_startup(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
 
-       runtime->hw.channels_max = 4;
+       runtime->hw.channels_max = DMIC_CH(dmic_constraints);
        snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
-                       &constraints_dmic_channels);
+                       dmic_constraints);
 
        return snd_pcm_hw_constraint_list(substream->runtime, 0,
                        SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
@@ -430,8 +443,22 @@ static struct snd_pcm_hw_constraint_list constraints_16000 = {
        .list  = rates_16000,
 };
 
+static const unsigned int ch_mono[] = {
+       1,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_refcap = {
+       .count = ARRAY_SIZE(ch_mono),
+       .list  = ch_mono,
+};
+
 static int skylake_refcap_startup(struct snd_pcm_substream *substream)
 {
+       substream->runtime->hw.channels_max = 1;
+       snd_pcm_hw_constraint_list(substream->runtime, 0,
+                                       SNDRV_PCM_HW_PARAM_CHANNELS,
+                                       &constraints_refcap);
+
        return snd_pcm_hw_constraint_list(substream->runtime, 0,
                        SNDRV_PCM_HW_PARAM_RATE,
                        &constraints_16000);
@@ -662,6 +689,7 @@ static struct snd_soc_card skylake_audio_card = {
 static int skylake_audio_probe(struct platform_device *pdev)
 {
        struct skl_nau88125_private *ctx;
+       struct skl_machine_pdata *pdata;
 
        ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
        if (!ctx)
@@ -672,15 +700,27 @@ static int skylake_audio_probe(struct platform_device *pdev)
        skylake_audio_card.dev = &pdev->dev;
        snd_soc_card_set_drvdata(&skylake_audio_card, ctx);
 
+       pdata = dev_get_drvdata(&pdev->dev);
+       if (pdata)
+               dmic_constraints = pdata->dmic_num == 2 ?
+                       &constraints_dmic_2ch : &constraints_dmic_channels;
+
        return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card);
 }
 
+static const struct platform_device_id skl_board_ids[] = {
+       { .name = "skl_n88l25_s4567" },
+       { .name = "kbl_n88l25_s4567" },
+       { }
+};
+
 static struct platform_driver skylake_audio = {
        .probe = skylake_audio_probe,
        .driver = {
-               .name = "skl_nau88l25_ssm4567_i2s",
+               .name = "skl_n88l25_s4567",
                .pm = &snd_soc_pm_ops,
        },
+       .id_table = skl_board_ids,
 };
 
 module_platform_driver(skylake_audio)
@@ -693,4 +733,5 @@ MODULE_AUTHOR("Sathya Prakash M R <sathya.prakash.m.r@intel.com>");
 MODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>");
 MODULE_DESCRIPTION("Intel Audio Machine driver for SKL with NAU88L25 and SSM4567 in I2S Mode");
 MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:skl_nau88l25_ssm4567_i2s");
+MODULE_ALIAS("platform:skl_n88l25_s4567");
+MODULE_ALIAS("platform:kbl_n88l25_s4567");
index 426b48233fdb1eed38824dc4b949a1dac8ec3907..88c61e8cb87f5e505df51eca34e2a6c88e321e96 100644 (file)
@@ -505,12 +505,20 @@ static int skylake_audio_probe(struct platform_device *pdev)
        return devm_snd_soc_register_card(&pdev->dev, &skylake_rt286);
 }
 
+static const struct platform_device_id skl_board_ids[] = {
+       { .name = "skl_alc286s_i2s" },
+       { .name = "kbl_alc286s_i2s" },
+       { }
+};
+
 static struct platform_driver skylake_audio = {
        .probe = skylake_audio_probe,
        .driver = {
                .name = "skl_alc286s_i2s",
                .pm = &snd_soc_pm_ops,
        },
+       .id_table = skl_board_ids,
+
 };
 
 module_platform_driver(skylake_audio)
@@ -520,3 +528,4 @@ MODULE_AUTHOR("Omair Mohammed Abdullah <omair.m.abdullah@intel.com>");
 MODULE_DESCRIPTION("Intel SST Audio for Skylake");
 MODULE_LICENSE("GPL v2");
 MODULE_ALIAS("platform:skl_alc286s_i2s");
+MODULE_ALIAS("platform:kbl_alc286s_i2s");
index fbbb25c2ceed2949bd909f4b2e2e152154493227..1a35149bcad7d5e33b21f3c15da26ba6ee45c733 100644 (file)
@@ -2,9 +2,9 @@ snd-soc-sst-dsp-objs := sst-dsp.o
 snd-soc-sst-acpi-objs := sst-acpi.o
 snd-soc-sst-match-objs := sst-match-acpi.o
 snd-soc-sst-ipc-objs := sst-ipc.o
-
-snd-soc-sst-dsp-$(CONFIG_DW_DMAC_CORE) += sst-firmware.o
+snd-soc-sst-firmware-objs := sst-firmware.o
 
 obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o
 obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o
 obj-$(CONFIG_SND_SOC_INTEL_SST_MATCH) += snd-soc-sst-match.o
+obj-$(CONFIG_SND_SOC_INTEL_SST_FIRMWARE) += snd-soc-sst-firmware.o
index 8398cb227ba9224bab3344aa908e44c8696f2b67..5d2949324d0ee68969b8102a2eb8da22b65813da 100644 (file)
@@ -20,7 +20,7 @@
 #if IS_ENABLED(CONFIG_ACPI)
 const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]);
 #else
-inline const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN])
+static inline const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN])
 {
        return NULL;
 }
@@ -40,6 +40,6 @@ struct sst_acpi_mach {
 
        /* board name */
        const char *board;
-       void (*machine_quirk)(void);
+       struct sst_acpi_mach * (*machine_quirk)(void *arg);
        void *pdata;
 };
index 97dc1ae05e69d60858f78ab1c6702dee629aaadf..d13c84364c3c0fd6e79ff3b38b4fe885eb0e2e92 100644 (file)
@@ -383,10 +383,6 @@ struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
        u32 index, 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 b5bbdf4fe93a68443021f4a50ff2fa955ba1a1c5..c00ede4ea4d7040c255556802ea8ebddd12bd1ad 100644 (file)
@@ -285,7 +285,7 @@ int sst_dsp_register_poll(struct sst_dsp *ctx, u32 offset, u32 mask,
        }
 
        reg = sst_dsp_shim_read_unlocked(ctx, offset);
-       dev_info(ctx->dev, "FW Poll Status: reg=%#x %s %s\n", reg, operation,
+       dev_dbg(ctx->dev, "FW Poll Status: reg=%#x %s %s\n", reg, operation,
                        (time < timeout) ? "successful" : "timedout");
        ret = time < timeout ? 0 : -ETIME;
 
@@ -420,73 +420,6 @@ void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes)
 }
 EXPORT_SYMBOL_GPL(sst_dsp_inbox_read);
 
-#ifdef CONFIG_DW_DMAC_CORE
-struct sst_dsp *sst_dsp_new(struct device *dev,
-       struct sst_dsp_device *sst_dev, struct sst_pdata *pdata)
-{
-       struct sst_dsp *sst;
-       int err;
-
-       dev_dbg(dev, "initialising audio DSP id 0x%x\n", pdata->id);
-
-       sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL);
-       if (sst == NULL)
-               return NULL;
-
-       spin_lock_init(&sst->spinlock);
-       mutex_init(&sst->mutex);
-       sst->dev = dev;
-       sst->dma_dev = pdata->dma_dev;
-       sst->thread_context = sst_dev->thread_context;
-       sst->sst_dev = sst_dev;
-       sst->id = pdata->id;
-       sst->irq = pdata->irq;
-       sst->ops = sst_dev->ops;
-       sst->pdata = pdata;
-       INIT_LIST_HEAD(&sst->used_block_list);
-       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) {
-               err = sst->ops->init(sst, pdata);
-               if (err < 0)
-                       return NULL;
-       }
-
-       /* Register the ISR */
-       err = request_threaded_irq(sst->irq, sst->ops->irq_handler,
-               sst_dev->thread, IRQF_SHARED, "AudioDSP", sst);
-       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:
-       if (sst->ops->free)
-               sst->ops->free(sst);
-
-       return NULL;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_new);
-
-void sst_dsp_free(struct sst_dsp *sst)
-{
-       free_irq(sst->irq, sst);
-       if (sst->ops->free)
-               sst->ops->free(sst);
-
-       sst_dma_free(sst->dma);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_free);
-#endif
-
 /* Module information */
 MODULE_AUTHOR("Liam Girdwood");
 MODULE_DESCRIPTION("Intel SST Core");
index 0b84c719ec4885b5ccb453f773190c800fdbe391..859f0de003391489b2da65c0195f62caa2e0b9ef 100644 (file)
@@ -216,7 +216,7 @@ struct sst_pdata {
        void *dsp;
 };
 
-#ifdef CONFIG_DW_DMAC_CORE
+#if IS_ENABLED(CONFIG_DW_DMAC_CORE)
 /* Initialization */
 struct sst_dsp *sst_dsp_new(struct device *dev,
        struct sst_dsp_device *sst_dev, struct sst_pdata *pdata);
index 25993527370b9f5063a4541cd0c53e84d457aeea..a086c35f91bb94bfe879ebc743fc81177e787118 100644 (file)
@@ -1211,3 +1211,71 @@ u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset,
        }
 }
 EXPORT_SYMBOL_GPL(sst_dsp_get_offset);
+
+struct sst_dsp *sst_dsp_new(struct device *dev,
+       struct sst_dsp_device *sst_dev, struct sst_pdata *pdata)
+{
+       struct sst_dsp *sst;
+       int err;
+
+       dev_dbg(dev, "initialising audio DSP id 0x%x\n", pdata->id);
+
+       sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL);
+       if (sst == NULL)
+               return NULL;
+
+       spin_lock_init(&sst->spinlock);
+       mutex_init(&sst->mutex);
+       sst->dev = dev;
+       sst->dma_dev = pdata->dma_dev;
+       sst->thread_context = sst_dev->thread_context;
+       sst->sst_dev = sst_dev;
+       sst->id = pdata->id;
+       sst->irq = pdata->irq;
+       sst->ops = sst_dev->ops;
+       sst->pdata = pdata;
+       INIT_LIST_HEAD(&sst->used_block_list);
+       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) {
+               err = sst->ops->init(sst, pdata);
+               if (err < 0)
+                       return NULL;
+       }
+
+       /* Register the ISR */
+       err = request_threaded_irq(sst->irq, sst->ops->irq_handler,
+               sst_dev->thread, IRQF_SHARED, "AudioDSP", sst);
+       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:
+       if (sst->ops->free)
+               sst->ops->free(sst);
+
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_new);
+
+void sst_dsp_free(struct sst_dsp *sst)
+{
+       free_irq(sst->irq, sst);
+       if (sst->ops->free)
+               sst->ops->free(sst);
+
+       sst_dma_free(sst->dma);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_free);
+
+MODULE_DESCRIPTION("Intel SST Firmware Loader");
+MODULE_LICENSE("GPL v2");
index 994256b39b9c5646cd5a26c3f9d3cc0f1da02236..3154525c2b83596eac3a2c57ce4d6b2ac0dfaf8d 100644 (file)
@@ -819,7 +819,6 @@ static int hsw_pcm_open(struct snd_pcm_substream *substream)
        mutex_lock(&pcm_data->mutex);
        pm_runtime_get_sync(pdata->dev);
 
-       snd_soc_pcm_set_drvdata(rtd, pcm_data);
        pcm_data->substream = substream;
 
        snd_soc_set_runtime_hwparams(substream, &hsw_pcm_hardware);
index c28f5d0e1d99fee2d254d65c595638c4c0cb71bb..60fbc9bbe47330c720fd286e1c57606d34b0312d 100644 (file)
@@ -5,6 +5,6 @@ obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o
 
 # Skylake IPC Support
 snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o skl-sst-cldma.o \
-               skl-sst.o bxt-sst.o
+               skl-sst.o bxt-sst.o skl-sst-utils.o
 
 obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o
index 8b95e09e23e8bd3fe9ddd886538b9f07d8a4c07d..2663781278aa8b9648e23d7bc6fcc7623bb834e8 100644 (file)
 
 #define BXT_ADSP_SRAM1_BASE    0xA0000
 
+#define BXT_INSTANCE_ID 0
+#define BXT_BASE_FW_MODULE_ID 0
+
 static unsigned int bxt_get_errorcode(struct sst_dsp *ctx)
 {
         return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE);
 }
 
+/*
+ * First boot sequence has some extra steps. Core 0 waits for power
+ * status on core 1, so power up core 1 also momentarily, keep it in
+ * reset/stall and then turn it off
+ */
 static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
                        const void *fwdata, u32 fwsize)
 {
@@ -49,7 +57,7 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
        u32 reg;
 
        stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab);
-       if (stream_tag < 0) {
+       if (stream_tag <= 0) {
                dev_err(ctx->dev, "Failed to prepare DMA FW loading err: %x\n",
                                stream_tag);
                return stream_tag;
@@ -58,17 +66,27 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
        ctx->dsp_ops.stream_tag = stream_tag;
        memcpy(ctx->dmab.area, fwdata, fwsize);
 
-       /* Purge FW request */
+       /* Step 1: Power up core 0 and core1 */
+       ret = skl_dsp_core_power_up(ctx, SKL_DSP_CORE0_MASK |
+                               SKL_DSP_CORE_MASK(1));
+       if (ret < 0) {
+               dev_err(ctx->dev, "dsp core0/1 power up failed\n");
+               goto base_fw_load_failed;
+       }
+
+       /* Step 2: Purge FW request */
        sst_dsp_shim_write(ctx, SKL_ADSP_REG_HIPCI, SKL_ADSP_REG_HIPCI_BUSY |
-                                        BXT_IPC_PURGE_FW | (stream_tag - 1));
+                               (BXT_IPC_PURGE_FW | ((stream_tag - 1) << 9)));
 
-       ret = skl_dsp_enable_core(ctx);
+       /* Step 3: Unset core0 reset state & unstall/run core0 */
+       ret = skl_dsp_start_core(ctx, SKL_DSP_CORE0_MASK);
        if (ret < 0) {
-               dev_err(ctx->dev, "Boot dsp core failed ret: %d\n", ret);
+               dev_err(ctx->dev, "Start dsp core failed ret: %d\n", ret);
                ret = -EIO;
                goto base_fw_load_failed;
        }
 
+       /* Step 4: Wait for DONE Bit */
        for (i = BXT_INIT_TIMEOUT; i > 0; --i) {
                reg = sst_dsp_shim_read(ctx, SKL_ADSP_REG_HIPCIE);
 
@@ -88,10 +106,18 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
                                SKL_ADSP_REG_HIPCIE_DONE);
        }
 
-       /* enable Interrupt */
+       /* Step 5: power down core1 */
+       ret = skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1));
+       if (ret < 0) {
+               dev_err(ctx->dev, "dsp core1 power down failed\n");
+               goto base_fw_load_failed;
+       }
+
+       /* Step 6: Enable Interrupt */
        skl_ipc_int_enable(ctx);
        skl_ipc_op_int_enable(ctx);
 
+       /* Step 7: Wait for ROM init */
        for (i = BXT_INIT_TIMEOUT; i > 0; --i) {
                if (SKL_FW_INIT ==
                                (sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS) &
@@ -112,7 +138,8 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
 
 base_fw_load_failed:
        ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, stream_tag);
-       skl_dsp_disable_core(ctx);
+       skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1));
+       skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
        return ret;
 }
 
@@ -130,23 +157,41 @@ static int sst_transfer_fw_host_dma(struct sst_dsp *ctx)
        return ret;
 }
 
+#define BXT_ADSP_FW_BIN_HDR_OFFSET 0x2000
+
 static int bxt_load_base_firmware(struct sst_dsp *ctx)
 {
-       const struct firmware *fw = NULL;
+       struct firmware stripped_fw;
        struct skl_sst *skl = ctx->thread_context;
        int ret;
 
-       ret = request_firmware(&fw, ctx->fw_name, ctx->dev);
+       ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev);
        if (ret < 0) {
                dev_err(ctx->dev, "Request firmware failed %d\n", ret);
                goto sst_load_base_firmware_failed;
        }
 
-       ret = sst_bxt_prepare_fw(ctx, fw->data, fw->size);
+       /* check for extended manifest */
+       if (ctx->fw == NULL)
+               goto sst_load_base_firmware_failed;
+
+       ret = snd_skl_parse_uuids(ctx, BXT_ADSP_FW_BIN_HDR_OFFSET);
+       if (ret < 0)
+               goto sst_load_base_firmware_failed;
+
+       stripped_fw.data = ctx->fw->data;
+       stripped_fw.size = ctx->fw->size;
+       skl_dsp_strip_extended_manifest(&stripped_fw);
+
+       ret = sst_bxt_prepare_fw(ctx, stripped_fw.data, stripped_fw.size);
        /* Retry Enabling core and ROM load. Retry seemed to help */
        if (ret < 0) {
-               ret = sst_bxt_prepare_fw(ctx, fw->data, fw->size);
+               ret = sst_bxt_prepare_fw(ctx, stripped_fw.data, stripped_fw.size);
                if (ret < 0) {
+                       dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n",
+                       sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE),
+                       sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS));
+
                        dev_err(ctx->dev, "Core En/ROM load fail:%d\n", ret);
                        goto sst_load_base_firmware_failed;
                }
@@ -159,83 +204,135 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx)
                        sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE),
                        sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS));
 
-               skl_dsp_disable_core(ctx);
+               skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
        } else {
                dev_dbg(ctx->dev, "Firmware download successful\n");
                ret = wait_event_timeout(skl->boot_wait, skl->boot_complete,
                                        msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
                if (ret == 0) {
                        dev_err(ctx->dev, "DSP boot fail, FW Ready timeout\n");
-                       skl_dsp_disable_core(ctx);
+                       skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
                        ret = -EIO;
                } else {
-                       skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
                        ret = 0;
+                       skl->fw_loaded = true;
                }
        }
 
 sst_load_base_firmware_failed:
-       release_firmware(fw);
+       release_firmware(ctx->fw);
        return ret;
 }
 
-static int bxt_set_dsp_D0(struct sst_dsp *ctx)
+static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)
 {
        struct skl_sst *skl = ctx->thread_context;
        int ret;
+       struct skl_ipc_dxstate_info dx;
+       unsigned int core_mask = SKL_DSP_CORE_MASK(core_id);
 
-       skl->boot_complete = false;
-
-       ret = skl_dsp_enable_core(ctx);
-       if (ret < 0) {
-               dev_err(ctx->dev, "enable dsp core failed ret: %d\n", ret);
+       if (skl->fw_loaded == false) {
+               skl->boot_complete = false;
+               ret = bxt_load_base_firmware(ctx);
+               if (ret < 0)
+                       dev_err(ctx->dev, "reload fw failed: %d\n", ret);
                return ret;
        }
 
-       /* enable interrupt */
-       skl_ipc_int_enable(ctx);
-       skl_ipc_op_int_enable(ctx);
+       /* If core 0 is being turned on, turn on core 1 as well */
+       if (core_id == SKL_DSP_CORE0_ID)
+               ret = skl_dsp_core_power_up(ctx, core_mask |
+                               SKL_DSP_CORE_MASK(1));
+       else
+               ret = skl_dsp_core_power_up(ctx, core_mask);
+
+       if (ret < 0)
+               goto err;
+
+       if (core_id == SKL_DSP_CORE0_ID) {
+
+               /*
+                * Enable interrupt after SPA is set and before
+                * DSP is unstalled
+                */
+               skl_ipc_int_enable(ctx);
+               skl_ipc_op_int_enable(ctx);
+               skl->boot_complete = false;
+       }
 
-       ret = wait_event_timeout(skl->boot_wait, skl->boot_complete,
-                                       msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
-       if (ret == 0) {
-               dev_err(ctx->dev, "ipc: error DSP boot timeout\n");
-               dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n",
-                       sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE),
-                       sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS));
-               return -EIO;
+       ret = skl_dsp_start_core(ctx, core_mask);
+       if (ret < 0)
+               goto err;
+
+       if (core_id == SKL_DSP_CORE0_ID) {
+               ret = wait_event_timeout(skl->boot_wait,
+                               skl->boot_complete,
+                               msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
+
+       /* If core 1 was turned on for booting core 0, turn it off */
+               skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1));
+               if (ret == 0) {
+                       dev_err(ctx->dev, "%s: DSP boot timeout\n", __func__);
+                       dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n",
+                               sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE),
+                               sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS));
+                       dev_err(ctx->dev, "Failed to set core0 to D0 state\n");
+                       ret = -EIO;
+                       goto err;
+               }
+       }
+
+       /* Tell FW if additional core in now On */
+
+       if (core_id != SKL_DSP_CORE0_ID) {
+               dx.core_mask = core_mask;
+               dx.dx_mask = core_mask;
+
+               ret = skl_ipc_set_dx(&skl->ipc, BXT_INSTANCE_ID,
+                                       BXT_BASE_FW_MODULE_ID, &dx);
+               if (ret < 0) {
+                       dev_err(ctx->dev, "IPC set_dx for core %d fail: %d\n",
+                                                               core_id, ret);
+                       goto err;
+               }
        }
 
-       skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
+       skl->cores.state[core_id] = SKL_DSP_RUNNING;
        return 0;
+err:
+       if (core_id == SKL_DSP_CORE0_ID)
+               core_mask |= SKL_DSP_CORE_MASK(1);
+       skl_dsp_disable_core(ctx, core_mask);
+
+       return ret;
 }
 
-static int bxt_set_dsp_D3(struct sst_dsp *ctx)
+static int bxt_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id)
 {
+       int ret;
        struct skl_ipc_dxstate_info dx;
        struct skl_sst *skl = ctx->thread_context;
-       int ret = 0;
+       unsigned int core_mask = SKL_DSP_CORE_MASK(core_id);
 
-       if (!is_skl_dsp_running(ctx))
-               return ret;
-
-       dx.core_mask = SKL_DSP_CORE0_MASK;
+       dx.core_mask = core_mask;
        dx.dx_mask = SKL_IPC_D3_MASK;
 
-       ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID,
-                               SKL_BASE_FW_MODULE_ID, &dx);
-       if (ret < 0) {
-               dev_err(ctx->dev, "Failed to set DSP to D3 state: %d\n", ret);
-               return ret;
-       }
+       dev_dbg(ctx->dev, "core mask=%x dx_mask=%x\n",
+                       dx.core_mask, dx.dx_mask);
+
+       ret = skl_ipc_set_dx(&skl->ipc, BXT_INSTANCE_ID,
+                               BXT_BASE_FW_MODULE_ID, &dx);
+       if (ret < 0)
+               dev_err(ctx->dev,
+               "Failed to set DSP to D3:core id = %d;Continue reset\n",
+               core_id);
 
-       ret = skl_dsp_disable_core(ctx);
+       ret = skl_dsp_disable_core(ctx, core_mask);
        if (ret < 0) {
-               dev_err(ctx->dev, "disbale dsp core failed: %d\n", ret);
-               ret = -EIO;
+               dev_err(ctx->dev, "Failed to disable core %d", ret);
+               return ret;
        }
-
-       skl_dsp_set_state_locked(ctx, SKL_DSP_RESET);
+       skl->cores.state[core_id] = SKL_DSP_RESET;
        return 0;
 }
 
@@ -274,6 +371,7 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
 
        skl->dev = dev;
        skl_dev.thread_context = skl;
+       INIT_LIST_HEAD(&skl->uuid_list);
 
        skl->dsp = skl_dsp_ctx_init(dev, &skl_dev, irq);
        if (!skl->dsp) {
@@ -296,6 +394,7 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
        if (ret)
                return ret;
 
+       skl->cores.count = 2;
        skl->boot_complete = false;
        init_waitqueue_head(&skl->boot_wait);
 
@@ -305,6 +404,8 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
                return ret;
        }
 
+       skl_dsp_init_core_state(sst);
+
        if (dsp)
                *dsp = skl;
 
@@ -315,6 +416,7 @@ EXPORT_SYMBOL_GPL(bxt_sst_dsp_init);
 
 void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
 {
+       skl_freeup_uuid_list(ctx);
        skl_ipc_free(&ctx->ipc);
        ctx->dsp->cl_dev.ops.cl_cleanup_controller(ctx->dsp);
 
index 226db84ba20f0286e3b175850f4087b3e4902a59..44ab595ce21ab111e1a9297858561019aac0abe2 100644 (file)
@@ -205,6 +205,12 @@ static const struct skl_dsp_ops dsp_ops[] = {
                .init = skl_sst_dsp_init,
                .cleanup = skl_sst_dsp_cleanup
        },
+       {
+               .id = 0x9d71,
+               .loader_ops = skl_get_loader_ops,
+               .init = skl_sst_dsp_init,
+               .cleanup = skl_sst_dsp_cleanup
+       },
        {
                .id = 0x5a98,
                .loader_ops = bxt_get_loader_ops,
@@ -730,7 +736,7 @@ static int skl_set_module_format(struct skl_sst *ctx,
 
        dev_dbg(ctx->dev, "Module type=%d config size: %d bytes\n",
                        module_config->id.module_id, param_size);
-       print_hex_dump(KERN_DEBUG, "Module params:", DUMP_PREFIX_OFFSET, 8, 4,
+       print_hex_dump_debug("Module params:", DUMP_PREFIX_OFFSET, 8, 4,
                        *param_data, param_size, false);
        return 0;
 }
@@ -1046,7 +1052,7 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
 
        dev_dbg(ctx->dev, "%s: pipe = %d\n", __func__, pipe->ppl_id);
 
-       /* If pipe is not started, do not try to stop the pipe in FW. */
+       /* If pipe is started, do stop the pipe in FW. */
        if (pipe->state > SKL_PIPE_STARTED) {
                ret = skl_set_pipe_state(ctx, pipe, PPL_PAUSED);
                if (ret < 0) {
@@ -1055,18 +1061,20 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
                }
 
                pipe->state = SKL_PIPE_PAUSED;
-       } else {
-               /* If pipe was not created in FW, do not try to delete it */
-               if (pipe->state < SKL_PIPE_CREATED)
-                       return 0;
+       }
 
-               ret = skl_ipc_delete_pipeline(&ctx->ipc, pipe->ppl_id);
-               if (ret < 0)
-                       dev_err(ctx->dev, "Failed to delete pipeline\n");
+       /* If pipe was not created in FW, do not try to delete it */
+       if (pipe->state < SKL_PIPE_CREATED)
+               return 0;
 
-               pipe->state = SKL_PIPE_INVALID;
+       ret = skl_ipc_delete_pipeline(&ctx->ipc, pipe->ppl_id);
+       if (ret < 0) {
+               dev_err(ctx->dev, "Failed to delete pipeline\n");
+               return ret;
        }
 
+       pipe->state = SKL_PIPE_INVALID;
+
        return ret;
 }
 
@@ -1125,7 +1133,30 @@ int skl_stop_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
                return ret;
        }
 
-       pipe->state = SKL_PIPE_CREATED;
+       pipe->state = SKL_PIPE_PAUSED;
+
+       return 0;
+}
+
+/*
+ * Reset the pipeline by sending set pipe state IPC this will reset the DMA
+ * from the DSP side
+ */
+int skl_reset_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
+{
+       int ret;
+
+       /* If pipe was not created in FW, do not try to pause or delete */
+       if (pipe->state < SKL_PIPE_PAUSED)
+               return 0;
+
+       ret = skl_set_pipe_state(ctx, pipe, PPL_RESET);
+       if (ret < 0) {
+               dev_dbg(ctx->dev, "Failed to reset pipe ret=%d\n", ret);
+               return ret;
+       }
+
+       pipe->state = SKL_PIPE_RESET;
 
        return 0;
 }
index 7d73648e5f9ab22570f67b5153fb74fd36d1985c..3f8e6f0b7eb5d10647f99efe3c1e5b9be751c2e3 100644 (file)
@@ -17,6 +17,7 @@
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  *
  */
+#include <linux/pci.h>
 #include "skl.h"
 
 /* Unique identification for getting NHLT blobs */
@@ -149,6 +150,45 @@ struct nhlt_specific_cfg
        return NULL;
 }
 
+int skl_get_dmic_geo(struct skl *skl)
+{
+       struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
+       struct nhlt_endpoint *epnt;
+       struct nhlt_dmic_array_config *cfg;
+       struct device *dev = &skl->pci->dev;
+       unsigned int dmic_geo = 0;
+       u8 j;
+
+       epnt = (struct nhlt_endpoint *)nhlt->desc;
+
+       for (j = 0; j < nhlt->endpoint_count; j++) {
+               if (epnt->linktype == NHLT_LINK_DMIC) {
+                       cfg = (struct nhlt_dmic_array_config  *)
+                                       (epnt->config.caps);
+                       switch (cfg->array_type) {
+                       case NHLT_MIC_ARRAY_2CH_SMALL:
+                       case NHLT_MIC_ARRAY_2CH_BIG:
+                               dmic_geo |= MIC_ARRAY_2CH;
+                               break;
+
+                       case NHLT_MIC_ARRAY_4CH_1ST_GEOM:
+                       case NHLT_MIC_ARRAY_4CH_L_SHAPED:
+                       case NHLT_MIC_ARRAY_4CH_2ND_GEOM:
+                               dmic_geo |= MIC_ARRAY_4CH;
+                               break;
+
+                       default:
+                               dev_warn(dev, "undefined DMIC array_type 0x%0x\n",
+                                               cfg->array_type);
+
+                       }
+               }
+               epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+       }
+
+       return dmic_geo;
+}
+
 static void skl_nhlt_trim_space(struct skl *skl)
 {
        char *s = skl->tplg_name;
index 3769f9fefe2bb9f0af6d41c088c3f1755eb2df73..116534e7b3c53244b0eb82d0122ca41aaa4d84ed 100644 (file)
@@ -103,4 +103,26 @@ struct nhlt_resource_desc  {
        u64 length;
 } __packed;
 
+#define MIC_ARRAY_2CH 2
+#define MIC_ARRAY_4CH 4
+
+struct nhlt_tdm_config {
+       u8 virtual_slot;
+       u8 config_type;
+} __packed;
+
+struct nhlt_dmic_array_config {
+       struct nhlt_tdm_config tdm_config;
+       u8 array_type;
+} __packed;
+
+enum {
+       NHLT_MIC_ARRAY_2CH_SMALL = 0xa,
+       NHLT_MIC_ARRAY_2CH_BIG = 0xb,
+       NHLT_MIC_ARRAY_4CH_1ST_GEOM = 0xc,
+       NHLT_MIC_ARRAY_4CH_L_SHAPED = 0xd,
+       NHLT_MIC_ARRAY_4CH_2ND_GEOM = 0xe,
+       NHLT_MIC_ARRAY_VENDOR_DEFINED = 0xf,
+};
+
 #endif
index 7c81b31748ffcba2ab7c5d8a8961eb83d4134f55..6e05bf8622f7ad1f1cf0ac6f9b11516469e4b86f 100644 (file)
@@ -227,16 +227,25 @@ static int skl_pcm_prepare(struct snd_pcm_substream *substream,
                struct snd_soc_dai *dai)
 {
        struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
+       struct skl *skl = get_skl_ctx(dai->dev);
        unsigned int format_val;
        int err;
+       struct skl_module_cfg *mconfig;
 
        dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
 
+       mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream);
+
        format_val = skl_get_format(substream, dai);
        dev_dbg(dai->dev, "stream_tag=%d formatvalue=%d\n",
                                hdac_stream(stream)->stream_tag, format_val);
        snd_hdac_stream_reset(hdac_stream(stream));
 
+       /* In case of XRUN recovery, reset the FW pipe to clean state */
+       if (mconfig && (substream->runtime->status->state ==
+                                       SNDRV_PCM_STATE_XRUN))
+               skl_reset_pipe(skl->skl_sst, mconfig->pipe);
+
        err = snd_hdac_stream_set_params(hdac_stream(stream), format_val);
        if (err < 0)
                return err;
@@ -521,6 +530,8 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream,
        struct skl_dma_params *dma_params;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        struct hdac_ext_link *link;
+       struct skl *skl = get_skl_ctx(dai->dev);
+       struct skl_module_cfg *mconfig = NULL;
 
        dma_params  = (struct skl_dma_params *)
                        snd_soc_dai_get_dma_data(codec_dai, substream);
@@ -535,6 +546,12 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream,
 
        snd_hdac_ext_link_stream_reset(link_dev);
 
+       /* In case of XRUN recovery, reset the FW pipe to clean state */
+       mconfig = skl_tplg_be_get_cpr_module(dai, substream->stream);
+       if (mconfig && (substream->runtime->status->state ==
+                                       SNDRV_PCM_STATE_XRUN))
+               skl_reset_pipe(skl->skl_sst, mconfig->pipe);
+
        snd_hdac_ext_link_stream_setup(link_dev, format_val);
 
        snd_hdac_ext_link_set_stream_id(link, hdac_stream(link_dev)->stream_tag);
@@ -1009,51 +1026,11 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream,
        return 0;
 }
 
-/* calculate runtime delay from LPIB */
-static int skl_get_delay_from_lpib(struct hdac_ext_bus *ebus,
-                               struct hdac_ext_stream *sstream,
-                               unsigned int pos)
-{
-       struct hdac_bus *bus = ebus_to_hbus(ebus);
-       struct hdac_stream *hstream = hdac_stream(sstream);
-       struct snd_pcm_substream *substream = hstream->substream;
-       int stream = substream->stream;
-       unsigned int lpib_pos = snd_hdac_stream_get_pos_lpib(hstream);
-       int delay;
-
-       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
-               delay = pos - lpib_pos;
-       else
-               delay = lpib_pos - pos;
-
-       if (delay < 0) {
-               if (delay >= hstream->delay_negative_threshold)
-                       delay = 0;
-               else
-                       delay += hstream->bufsize;
-       }
-
-       if (hstream->bufsize == delay)
-               delay = 0;
-
-       if (delay >= hstream->period_bytes) {
-               dev_info(bus->dev,
-                        "Unstable LPIB (%d >= %d); disabling LPIB delay counting\n",
-                        delay, hstream->period_bytes);
-               delay = 0;
-       }
-
-       return bytes_to_frames(substream->runtime, delay);
-}
-
-static unsigned int skl_get_position(struct hdac_ext_stream *hstream,
-                                       int codec_delay)
+static snd_pcm_uframes_t skl_platform_pcm_pointer
+                       (struct snd_pcm_substream *substream)
 {
-       struct hdac_stream *hstr = hdac_stream(hstream);
-       struct snd_pcm_substream *substream = hstr->substream;
-       struct hdac_ext_bus *ebus;
+       struct hdac_ext_stream *hstream = get_hdac_ext_stream(substream);
        unsigned int pos;
-       int delay;
 
        /* use the position buffer as default */
        pos = snd_hdac_stream_get_pos_posbuf(hdac_stream(hstream));
@@ -1061,23 +1038,7 @@ static unsigned int skl_get_position(struct hdac_ext_stream *hstream,
        if (pos >= hdac_stream(hstream)->bufsize)
                pos = 0;
 
-       if (substream->runtime) {
-               ebus = get_bus_ctx(substream);
-               delay = skl_get_delay_from_lpib(ebus, hstream, pos)
-                                                + codec_delay;
-               substream->runtime->delay += delay;
-       }
-
-       return pos;
-}
-
-static snd_pcm_uframes_t skl_platform_pcm_pointer
-                       (struct snd_pcm_substream *substream)
-{
-       struct hdac_ext_stream *hstream = get_hdac_ext_stream(substream);
-
-       return bytes_to_frames(substream->runtime,
-                              skl_get_position(hstream, 0));
+       return bytes_to_frames(substream->runtime, pos);
 }
 
 static u64 skl_adjust_codec_delay(struct snd_pcm_substream *substream,
@@ -1180,9 +1141,17 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd)
 static int skl_platform_soc_probe(struct snd_soc_platform *platform)
 {
        struct hdac_ext_bus *ebus = dev_get_drvdata(platform->dev);
+       struct skl *skl = ebus_to_skl(ebus);
+       int ret;
 
-       if (ebus->ppcap)
-               return skl_tplg_init(platform, ebus);
+       if (ebus->ppcap) {
+               ret = skl_tplg_init(platform, ebus);
+               if (ret < 0) {
+                       dev_err(platform->dev, "Failed to init topology!\n");
+                       return ret;
+               }
+               skl->platform = platform;
+       }
 
        return 0;
 }
index 13c19855ee1ac0cd57473fd2fa2f122b65d58b2f..c3deefab65d6f80a6efa2c3c58fa119d99e3925e 100644 (file)
@@ -34,33 +34,84 @@ void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state)
        mutex_unlock(&ctx->mutex);
 }
 
-static int skl_dsp_core_set_reset_state(struct sst_dsp *ctx)
+/*
+ * Initialize core power state and usage count. To be called after
+ * successful first boot. Hence core 0 will be running and other cores
+ * will be reset
+ */
+void skl_dsp_init_core_state(struct sst_dsp *ctx)
+{
+       struct skl_sst *skl = ctx->thread_context;
+       int i;
+
+       skl->cores.state[SKL_DSP_CORE0_ID] = SKL_DSP_RUNNING;
+       skl->cores.usage_count[SKL_DSP_CORE0_ID] = 1;
+
+       for (i = SKL_DSP_CORE0_ID + 1; i < SKL_DSP_CORES_MAX; i++) {
+               skl->cores.state[i] = SKL_DSP_RESET;
+               skl->cores.usage_count[i] = 0;
+       }
+}
+
+/* Get the mask for all enabled cores */
+unsigned int skl_dsp_get_enabled_cores(struct sst_dsp *ctx)
+{
+       struct skl_sst *skl = ctx->thread_context;
+       unsigned int core_mask, en_cores_mask;
+       u32 val;
+
+       core_mask = SKL_DSP_CORES_MASK(skl->cores.count);
+
+       val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS);
+
+       /* Cores having CPA bit set */
+       en_cores_mask = (val & SKL_ADSPCS_CPA_MASK(core_mask)) >>
+                       SKL_ADSPCS_CPA_SHIFT;
+
+       /* And cores having CRST bit cleared */
+       en_cores_mask &= (~val & SKL_ADSPCS_CRST_MASK(core_mask)) >>
+                       SKL_ADSPCS_CRST_SHIFT;
+
+       /* And cores having CSTALL bit cleared */
+       en_cores_mask &= (~val & SKL_ADSPCS_CSTALL_MASK(core_mask)) >>
+                       SKL_ADSPCS_CSTALL_SHIFT;
+       en_cores_mask &= core_mask;
+
+       dev_dbg(ctx->dev, "DSP enabled cores mask = %x\n", en_cores_mask);
+
+       return en_cores_mask;
+}
+
+static int
+skl_dsp_core_set_reset_state(struct sst_dsp *ctx, unsigned int core_mask)
 {
        int ret;
 
        /* update bits */
        sst_dsp_shim_update_bits_unlocked(ctx,
-                       SKL_ADSP_REG_ADSPCS, SKL_ADSPCS_CRST_MASK,
-                       SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK));
+                       SKL_ADSP_REG_ADSPCS, SKL_ADSPCS_CRST_MASK(core_mask),
+                       SKL_ADSPCS_CRST_MASK(core_mask));
 
        /* poll with timeout to check if operation successful */
        ret = sst_dsp_register_poll(ctx,
                        SKL_ADSP_REG_ADSPCS,
-                       SKL_ADSPCS_CRST_MASK,
-                       SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK),
+                       SKL_ADSPCS_CRST_MASK(core_mask),
+                       SKL_ADSPCS_CRST_MASK(core_mask),
                        SKL_DSP_RESET_TO,
                        "Set reset");
        if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
-                               SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) !=
-                               SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) {
-               dev_err(ctx->dev, "Set reset state failed\n");
+                               SKL_ADSPCS_CRST_MASK(core_mask)) !=
+                               SKL_ADSPCS_CRST_MASK(core_mask)) {
+               dev_err(ctx->dev, "Set reset state failed: core_mask %x\n",
+                                                       core_mask);
                ret = -EIO;
        }
 
        return ret;
 }
 
-static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx)
+int skl_dsp_core_unset_reset_state(
+               struct sst_dsp *ctx, unsigned int core_mask)
 {
        int ret;
 
@@ -68,152 +119,160 @@ static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx)
 
        /* update bits */
        sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
-                                       SKL_ADSPCS_CRST_MASK, 0);
+                               SKL_ADSPCS_CRST_MASK(core_mask), 0);
 
        /* poll with timeout to check if operation successful */
        ret = sst_dsp_register_poll(ctx,
                        SKL_ADSP_REG_ADSPCS,
-                       SKL_ADSPCS_CRST_MASK,
+                       SKL_ADSPCS_CRST_MASK(core_mask),
                        0,
                        SKL_DSP_RESET_TO,
                        "Unset reset");
 
        if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
-                                SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) != 0) {
-               dev_err(ctx->dev, "Unset reset state failed\n");
+                               SKL_ADSPCS_CRST_MASK(core_mask)) != 0) {
+               dev_err(ctx->dev, "Unset reset state failed: core_mask %x\n",
+                               core_mask);
                ret = -EIO;
        }
 
        return ret;
 }
 
-static bool is_skl_dsp_core_enable(struct sst_dsp *ctx)
+static bool
+is_skl_dsp_core_enable(struct sst_dsp *ctx, unsigned int core_mask)
 {
        int val;
        bool is_enable;
 
        val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS);
 
-       is_enable = ((val & SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) &&
-                       (val & SKL_ADSPCS_SPA(SKL_DSP_CORES_MASK)) &&
-                       !(val & SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) &&
-                       !(val & SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK)));
+       is_enable = ((val & SKL_ADSPCS_CPA_MASK(core_mask)) &&
+                       (val & SKL_ADSPCS_SPA_MASK(core_mask)) &&
+                       !(val & SKL_ADSPCS_CRST_MASK(core_mask)) &&
+                       !(val & SKL_ADSPCS_CSTALL_MASK(core_mask)));
+
+       dev_dbg(ctx->dev, "DSP core(s) enabled? %d : core_mask %x\n",
+                                               is_enable, core_mask);
 
-       dev_dbg(ctx->dev, "DSP core is enabled=%d\n", is_enable);
        return is_enable;
 }
 
-static int skl_dsp_reset_core(struct sst_dsp *ctx)
+static int skl_dsp_reset_core(struct sst_dsp *ctx, unsigned int core_mask)
 {
        /* stall core */
-       sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
-                        sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
-                               SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK));
+       sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
+                       SKL_ADSPCS_CSTALL_MASK(core_mask),
+                       SKL_ADSPCS_CSTALL_MASK(core_mask));
 
        /* set reset state */
-       return skl_dsp_core_set_reset_state(ctx);
+       return skl_dsp_core_set_reset_state(ctx, core_mask);
 }
 
-static int skl_dsp_start_core(struct sst_dsp *ctx)
+int skl_dsp_start_core(struct sst_dsp *ctx, unsigned int core_mask)
 {
        int ret;
 
        /* unset reset state */
-       ret = skl_dsp_core_unset_reset_state(ctx);
-       if (ret < 0) {
-               dev_dbg(ctx->dev, "dsp unset reset fails\n");
+       ret = skl_dsp_core_unset_reset_state(ctx, core_mask);
+       if (ret < 0)
                return ret;
-       }
 
        /* run core */
-       dev_dbg(ctx->dev, "run core...\n");
-       sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
-                        sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
-                               ~SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK));
-
-       if (!is_skl_dsp_core_enable(ctx)) {
-               skl_dsp_reset_core(ctx);
-               dev_err(ctx->dev, "DSP core enable failed\n");
+       dev_dbg(ctx->dev, "unstall/run core: core_mask = %x\n", core_mask);
+       sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
+                       SKL_ADSPCS_CSTALL_MASK(core_mask), 0);
+
+       if (!is_skl_dsp_core_enable(ctx, core_mask)) {
+               skl_dsp_reset_core(ctx, core_mask);
+               dev_err(ctx->dev, "DSP start core failed: core_mask %x\n",
+                                                       core_mask);
                ret = -EIO;
        }
 
        return ret;
 }
 
-static int skl_dsp_core_power_up(struct sst_dsp *ctx)
+int skl_dsp_core_power_up(struct sst_dsp *ctx, unsigned int core_mask)
 {
        int ret;
 
        /* update bits */
        sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
-                       SKL_ADSPCS_SPA_MASK, SKL_ADSPCS_SPA(SKL_DSP_CORES_MASK));
+                       SKL_ADSPCS_SPA_MASK(core_mask),
+                       SKL_ADSPCS_SPA_MASK(core_mask));
 
        /* poll with timeout to check if operation successful */
        ret = sst_dsp_register_poll(ctx,
                        SKL_ADSP_REG_ADSPCS,
-                       SKL_ADSPCS_CPA_MASK,
-                       SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK),
+                       SKL_ADSPCS_CPA_MASK(core_mask),
+                       SKL_ADSPCS_CPA_MASK(core_mask),
                        SKL_DSP_PU_TO,
                        "Power up");
 
        if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
-                       SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) !=
-                       SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) {
-               dev_err(ctx->dev, "DSP core power up failed\n");
+                       SKL_ADSPCS_CPA_MASK(core_mask)) !=
+                       SKL_ADSPCS_CPA_MASK(core_mask)) {
+               dev_err(ctx->dev, "DSP core power up failed: core_mask %x\n",
+                               core_mask);
                ret = -EIO;
        }
 
        return ret;
 }
 
-static int skl_dsp_core_power_down(struct sst_dsp *ctx)
+int skl_dsp_core_power_down(struct sst_dsp  *ctx, unsigned int core_mask)
 {
        /* update bits */
        sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
-                                       SKL_ADSPCS_SPA_MASK, 0);
+                               SKL_ADSPCS_SPA_MASK(core_mask), 0);
 
        /* poll with timeout to check if operation successful */
        return sst_dsp_register_poll(ctx,
                        SKL_ADSP_REG_ADSPCS,
-                       SKL_ADSPCS_CPA_MASK,
+                       SKL_ADSPCS_CPA_MASK(core_mask),
                        0,
                        SKL_DSP_PD_TO,
                        "Power down");
 }
 
-int skl_dsp_enable_core(struct sst_dsp *ctx)
+int skl_dsp_enable_core(struct sst_dsp  *ctx, unsigned int core_mask)
 {
        int ret;
 
        /* power up */
-       ret = skl_dsp_core_power_up(ctx);
+       ret = skl_dsp_core_power_up(ctx, core_mask);
        if (ret < 0) {
-               dev_dbg(ctx->dev, "dsp core power up failed\n");
+               dev_err(ctx->dev, "dsp core power up failed: core_mask %x\n",
+                                                       core_mask);
                return ret;
        }
 
-       return skl_dsp_start_core(ctx);
+       return skl_dsp_start_core(ctx, core_mask);
 }
 
-int skl_dsp_disable_core(struct sst_dsp *ctx)
+int skl_dsp_disable_core(struct sst_dsp *ctx, unsigned int core_mask)
 {
        int ret;
 
-       ret = skl_dsp_reset_core(ctx);
+       ret = skl_dsp_reset_core(ctx, core_mask);
        if (ret < 0) {
-               dev_err(ctx->dev, "dsp core reset failed\n");
+               dev_err(ctx->dev, "dsp core reset failed: core_mask %x\n",
+                                                       core_mask);
                return ret;
        }
 
        /* power down core*/
-       ret = skl_dsp_core_power_down(ctx);
+       ret = skl_dsp_core_power_down(ctx, core_mask);
        if (ret < 0) {
-               dev_err(ctx->dev, "dsp core power down failed\n");
+               dev_err(ctx->dev, "dsp core power down fail mask %x: %d\n",
+                                                       core_mask, ret);
                return ret;
        }
 
-       if (is_skl_dsp_core_enable(ctx)) {
-               dev_err(ctx->dev, "DSP core disable failed\n");
+       if (is_skl_dsp_core_enable(ctx, core_mask)) {
+               dev_err(ctx->dev, "dsp core disable fail mask %x: %d\n",
+                                                       core_mask, ret);
                ret = -EIO;
        }
 
@@ -224,28 +283,25 @@ int skl_dsp_boot(struct sst_dsp *ctx)
 {
        int ret;
 
-       if (is_skl_dsp_core_enable(ctx)) {
-               dev_dbg(ctx->dev, "dsp core is already enabled, so reset the dap core\n");
-               ret = skl_dsp_reset_core(ctx);
+       if (is_skl_dsp_core_enable(ctx, SKL_DSP_CORE0_MASK)) {
+               ret = skl_dsp_reset_core(ctx, SKL_DSP_CORE0_MASK);
                if (ret < 0) {
-                       dev_err(ctx->dev, "dsp reset failed\n");
+                       dev_err(ctx->dev, "dsp core0 reset fail: %d\n", ret);
                        return ret;
                }
 
-               ret = skl_dsp_start_core(ctx);
+               ret = skl_dsp_start_core(ctx, SKL_DSP_CORE0_MASK);
                if (ret < 0) {
-                       dev_err(ctx->dev, "dsp start failed\n");
+                       dev_err(ctx->dev, "dsp core0 start fail: %d\n", ret);
                        return ret;
                }
        } else {
-               dev_dbg(ctx->dev, "disable and enable to make sure DSP is invalid state\n");
-               ret = skl_dsp_disable_core(ctx);
-
+               ret = skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
                if (ret < 0) {
-                       dev_err(ctx->dev, "dsp disable core failes\n");
+                       dev_err(ctx->dev, "dsp core0 disable fail: %d\n", ret);
                        return ret;
                }
-               ret = skl_dsp_enable_core(ctx);
+               ret = skl_dsp_enable_core(ctx, SKL_DSP_CORE0_MASK);
        }
 
        return ret;
@@ -281,16 +337,74 @@ irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id)
 
        return result;
 }
+/*
+ * skl_dsp_get_core/skl_dsp_put_core will be called inside DAPM context
+ * within the dapm mutex. Hence no separate lock is used.
+ */
+int skl_dsp_get_core(struct sst_dsp *ctx, unsigned int core_id)
+{
+       struct skl_sst *skl = ctx->thread_context;
+       int ret = 0;
+
+       if (core_id >= skl->cores.count) {
+               dev_err(ctx->dev, "invalid core id: %d\n", core_id);
+               return -EINVAL;
+       }
+
+       if (skl->cores.state[core_id] == SKL_DSP_RESET) {
+               ret = ctx->fw_ops.set_state_D0(ctx, core_id);
+               if (ret < 0) {
+                       dev_err(ctx->dev, "unable to get core%d\n", core_id);
+                       return ret;
+               }
+       }
+
+       skl->cores.usage_count[core_id]++;
+
+       dev_dbg(ctx->dev, "core id %d state %d usage_count %d\n",
+                       core_id, skl->cores.state[core_id],
+                       skl->cores.usage_count[core_id]);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(skl_dsp_get_core);
+
+int skl_dsp_put_core(struct sst_dsp *ctx, unsigned int core_id)
+{
+       struct skl_sst *skl = ctx->thread_context;
+       int ret = 0;
+
+       if (core_id >= skl->cores.count) {
+               dev_err(ctx->dev, "invalid core id: %d\n", core_id);
+               return -EINVAL;
+       }
+
+       if (--skl->cores.usage_count[core_id] == 0) {
+               ret = ctx->fw_ops.set_state_D3(ctx, core_id);
+               if (ret < 0) {
+                       dev_err(ctx->dev, "unable to put core %d: %d\n",
+                                       core_id, ret);
+                       skl->cores.usage_count[core_id]++;
+               }
+       }
+
+       dev_dbg(ctx->dev, "core id %d state %d usage_count %d\n",
+                       core_id, skl->cores.state[core_id],
+                       skl->cores.usage_count[core_id]);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(skl_dsp_put_core);
 
 int skl_dsp_wake(struct sst_dsp *ctx)
 {
-       return ctx->fw_ops.set_state_D0(ctx);
+       return skl_dsp_get_core(ctx, SKL_DSP_CORE0_ID);
 }
 EXPORT_SYMBOL_GPL(skl_dsp_wake);
 
 int skl_dsp_sleep(struct sst_dsp *ctx)
 {
-       return ctx->fw_ops.set_state_D3(ctx);
+       return skl_dsp_put_core(ctx, SKL_DSP_CORE0_ID);
 }
 EXPORT_SYMBOL_GPL(skl_dsp_sleep);
 
@@ -337,9 +451,7 @@ void skl_dsp_free(struct sst_dsp *dsp)
 
        free_irq(dsp->irq, dsp);
        skl_ipc_op_int_disable(dsp);
-       skl_ipc_int_disable(dsp);
-
-       skl_dsp_disable_core(dsp);
+       skl_dsp_disable_core(dsp, SKL_DSP_CORE0_MASK);
 }
 EXPORT_SYMBOL_GPL(skl_dsp_free);
 
index deabe7308d3bc779389982b15bc3cea10d4d32b0..0f8629ef79ac86a315029b19e28a780323d9b11c 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/interrupt.h>
 #include <sound/memalloc.h>
 #include "skl-sst-cldma.h"
+#include "skl-tplg-interface.h"
 
 struct sst_dsp;
 struct skl_sst;
@@ -76,35 +77,53 @@ struct sst_dsp_device;
 #define SKL_ADSPIC_IPC                 1
 #define SKL_ADSPIS_IPC                 1
 
+/* Core ID of core0 */
+#define SKL_DSP_CORE0_ID               0
+
+/* Mask for a given core index, c = 0.. number of supported cores - 1 */
+#define SKL_DSP_CORE_MASK(c)           BIT(c)
+
+/*
+ * Core 0 mask = SKL_DSP_CORE_MASK(0); Defined separately
+ * since Core0 is primary core and it is used often
+ */
+#define SKL_DSP_CORE0_MASK             BIT(0)
+
+/*
+ * Mask for a given number of cores
+ * nc = number of supported cores
+ */
+#define SKL_DSP_CORES_MASK(nc) GENMASK((nc - 1), 0)
+
 /* ADSPCS - Audio DSP Control & Status */
-#define SKL_DSP_CORES          1
-#define SKL_DSP_CORE0_MASK     1
-#define SKL_DSP_CORES_MASK     ((1 << SKL_DSP_CORES) - 1)
-
-/* Core Reset - asserted high */
-#define SKL_ADSPCS_CRST_SHIFT  0
-#define SKL_ADSPCS_CRST_MASK   (SKL_DSP_CORES_MASK << SKL_ADSPCS_CRST_SHIFT)
-#define SKL_ADSPCS_CRST(x)     ((x << SKL_ADSPCS_CRST_SHIFT) & SKL_ADSPCS_CRST_MASK)
-
-/* Core run/stall - when set to '1' core is stalled */
-#define SKL_ADSPCS_CSTALL_SHIFT        8
-#define SKL_ADSPCS_CSTALL_MASK (SKL_DSP_CORES_MASK <<  \
-                                       SKL_ADSPCS_CSTALL_SHIFT)
-#define SKL_ADSPCS_CSTALL(x)   ((x << SKL_ADSPCS_CSTALL_SHIFT) &       \
-                               SKL_ADSPCS_CSTALL_MASK)
-
-/* Set Power Active - when set to '1' turn cores on */
-#define SKL_ADSPCS_SPA_SHIFT   16
-#define SKL_ADSPCS_SPA_MASK    (SKL_DSP_CORES_MASK << SKL_ADSPCS_SPA_SHIFT)
-#define SKL_ADSPCS_SPA(x)      ((x << SKL_ADSPCS_SPA_SHIFT) & SKL_ADSPCS_SPA_MASK)
-
-/* Current Power Active - power status of cores, set by hardware */
-#define SKL_ADSPCS_CPA_SHIFT   24
-#define SKL_ADSPCS_CPA_MASK    (SKL_DSP_CORES_MASK << SKL_ADSPCS_CPA_SHIFT)
-#define SKL_ADSPCS_CPA(x)      ((x << SKL_ADSPCS_CPA_SHIFT) & SKL_ADSPCS_CPA_MASK)
-
-#define SST_DSP_POWER_D0       0x0  /* full On */
-#define SST_DSP_POWER_D3       0x3  /* Off */
+
+/*
+ * Core Reset - asserted high
+ * CRST Mask for a given core mask pattern, cm
+ */
+#define SKL_ADSPCS_CRST_SHIFT          0
+#define SKL_ADSPCS_CRST_MASK(cm)       ((cm) << SKL_ADSPCS_CRST_SHIFT)
+
+/*
+ * Core run/stall - when set to '1' core is stalled
+ * CSTALL Mask for a given core mask pattern, cm
+ */
+#define SKL_ADSPCS_CSTALL_SHIFT                8
+#define SKL_ADSPCS_CSTALL_MASK(cm)     ((cm) << SKL_ADSPCS_CSTALL_SHIFT)
+
+/*
+ * Set Power Active - when set to '1' turn cores on
+ * SPA Mask for a given core mask pattern, cm
+ */
+#define SKL_ADSPCS_SPA_SHIFT           16
+#define SKL_ADSPCS_SPA_MASK(cm)                ((cm) << SKL_ADSPCS_SPA_SHIFT)
+
+/*
+ * Current Power Active - power status of cores, set by hardware
+ * CPA Mask for a given core mask pattern, cm
+ */
+#define SKL_ADSPCS_CPA_SHIFT           24
+#define SKL_ADSPCS_CPA_MASK(cm)                ((cm) << SKL_ADSPCS_CPA_SHIFT)
 
 enum skl_dsp_states {
        SKL_DSP_RUNNING = 1,
@@ -115,8 +134,8 @@ struct skl_dsp_fw_ops {
        int (*load_fw)(struct sst_dsp  *ctx);
        /* FW module parser/loader */
        int (*parse_fw)(struct sst_dsp *ctx);
-       int (*set_state_D0)(struct sst_dsp *ctx);
-       int (*set_state_D3)(struct sst_dsp *ctx);
+       int (*set_state_D0)(struct sst_dsp *ctx, unsigned int core_id);
+       int (*set_state_D3)(struct sst_dsp *ctx, unsigned int core_id);
        unsigned int (*get_fw_errcode)(struct sst_dsp *ctx);
        int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, u8 *mod_name);
        int (*unload_mod)(struct sst_dsp *ctx, u16 mod_id);
@@ -157,14 +176,26 @@ int skl_cldma_prepare(struct sst_dsp *ctx);
 void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state);
 struct sst_dsp *skl_dsp_ctx_init(struct device *dev,
                struct sst_dsp_device *sst_dev, int irq);
-int skl_dsp_enable_core(struct sst_dsp *ctx);
-int skl_dsp_disable_core(struct sst_dsp *ctx);
 bool is_skl_dsp_running(struct sst_dsp *ctx);
+
+unsigned int skl_dsp_get_enabled_cores(struct sst_dsp *ctx);
+void skl_dsp_init_core_state(struct sst_dsp *ctx);
+int skl_dsp_enable_core(struct sst_dsp *ctx, unsigned int core_mask);
+int skl_dsp_disable_core(struct sst_dsp *ctx, unsigned int core_mask);
+int skl_dsp_core_power_up(struct sst_dsp *ctx, unsigned int core_mask);
+int skl_dsp_core_power_down(struct sst_dsp *ctx, unsigned int core_mask);
+int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx,
+                                       unsigned int core_mask);
+int skl_dsp_start_core(struct sst_dsp *ctx, unsigned int core_mask);
+
 irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id);
 int skl_dsp_wake(struct sst_dsp *ctx);
 int skl_dsp_sleep(struct sst_dsp *ctx);
 void skl_dsp_free(struct sst_dsp *dsp);
 
+int skl_dsp_get_core(struct sst_dsp *ctx, unsigned int core_id);
+int skl_dsp_put_core(struct sst_dsp *ctx, unsigned int core_id);
+
 int skl_dsp_boot(struct sst_dsp *ctx);
 int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
                const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
@@ -175,4 +206,11 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
 void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx);
 void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx);
 
+int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid,
+               struct skl_dfw_module *dfw_config);
+int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset);
+void skl_freeup_uuid_list(struct skl_sst *ctx);
+
+int skl_dsp_strip_extended_manifest(struct firmware *fw);
+
 #endif /*__SKL_SST_DSP_H__*/
index 543460293b0046fd2dd132feefe03a83ca39f93c..96f2f6889b1893bea1cce2445cdf024b35b4e21e 100644 (file)
@@ -363,7 +363,7 @@ static void skl_ipc_process_reply(struct sst_generic_ipc *ipc,
        /* first process the header */
        switch (reply) {
        case IPC_GLB_REPLY_SUCCESS:
-               dev_info(ipc->dev, "ipc FW reply %x: success\n", header.primary);
+               dev_dbg(ipc->dev, "ipc FW reply %x: success\n", header.primary);
                /* copy the rx data from the mailbox */
                sst_dsp_inbox_read(ipc->dsp, msg->rx_data, msg->rx_size);
                break;
@@ -692,7 +692,7 @@ int skl_ipc_init_instance(struct sst_generic_ipc *ipc,
         /* param_block_size must be in dwords */
        u16 param_block_size = msg->param_data_size / sizeof(u32);
 
-       print_hex_dump(KERN_DEBUG, NULL, DUMP_PREFIX_NONE,
+       print_hex_dump_debug("Param data:", DUMP_PREFIX_NONE,
                16, 4, buffer, param_block_size, false);
 
        header.primary = IPC_MSG_TARGET(IPC_MOD_MSG);
index d59d1ba62a430db7b7a2f3e44fc30ff7f8f85594..2e3d4e80ef97553fb0573b1d9f7d888fb41cb58c 100644 (file)
@@ -45,6 +45,14 @@ struct skl_ipc_header {
        u32 extension;
 };
 
+#define SKL_DSP_CORES_MAX  2
+
+struct skl_dsp_cores {
+       unsigned int count;
+       enum skl_dsp_states state[SKL_DSP_CORES_MAX];
+       int usage_count[SKL_DSP_CORES_MAX];
+};
+
 struct skl_sst {
        struct device *dev;
        struct sst_dsp *dsp;
@@ -60,6 +68,15 @@ struct skl_sst {
        void (*enable_miscbdcge)(struct device *dev, bool enable);
        /*Is CGCTL.MISCBDCGE disabled*/
        bool miscbdcg_disabled;
+
+       /* Populate module information */
+       struct list_head uuid_list;
+
+       /* Is firmware loaded */
+       bool fw_loaded;
+
+       /* multi-core */
+       struct skl_dsp_cores cores;
 };
 
 struct skl_ipc_init_instance_msg {
@@ -136,5 +153,6 @@ void skl_ipc_int_disable(struct sst_dsp *dsp);
 bool skl_ipc_int_status(struct sst_dsp *dsp);
 void skl_ipc_free(struct sst_generic_ipc *ipc);
 int skl_ipc_init(struct device *dev, struct skl_sst *skl);
+void skl_clear_module_cnt(struct sst_dsp *ctx);
 
 #endif /* __SKL_IPC_H */
diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c
new file mode 100644 (file)
index 0000000..25fcb79
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ *  skl-sst-utils.c - SKL sst utils functions
+ *
+ *  Copyright (C) 2016 Intel Corp
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/uuid.h>
+#include "skl-sst-dsp.h"
+#include "../common/sst-dsp.h"
+#include "../common/sst-dsp-priv.h"
+#include "skl-sst-ipc.h"
+
+
+#define UUID_STR_SIZE 37
+#define DEFAULT_HASH_SHA256_LEN 32
+
+/* FW Extended Manifest Header id = $AE1 */
+#define SKL_EXT_MANIFEST_HEADER_MAGIC   0x31454124
+
+struct skl_dfw_module_mod {
+       char name[100];
+       struct skl_dfw_module skl_dfw_mod;
+};
+
+struct UUID {
+       u8 id[16];
+};
+
+union seg_flags {
+       u32 ul;
+       struct {
+               u32 contents : 1;
+               u32 alloc    : 1;
+               u32 load     : 1;
+               u32 read_only : 1;
+               u32 code     : 1;
+               u32 data     : 1;
+               u32 _rsvd0   : 2;
+               u32 type     : 4;
+               u32 _rsvd1   : 4;
+               u32 length   : 16;
+       } r;
+} __packed;
+
+struct segment_desc {
+       union seg_flags flags;
+       u32 v_base_addr;
+       u32 file_offset;
+};
+
+struct module_type {
+       u32 load_type  : 4;
+       u32 auto_start : 1;
+       u32 domain_ll  : 1;
+       u32 domain_dp  : 1;
+       u32 rsvd       : 25;
+} __packed;
+
+struct adsp_module_entry {
+       u32 struct_id;
+       u8  name[8];
+       struct UUID uuid;
+       struct module_type type;
+       u8  hash1[DEFAULT_HASH_SHA256_LEN];
+       u32 entry_point;
+       u16 cfg_offset;
+       u16 cfg_count;
+       u32 affinity_mask;
+       u16 instance_max_count;
+       u16 instance_bss_size;
+       struct segment_desc segments[3];
+} __packed;
+
+struct adsp_fw_hdr {
+       u32 id;
+       u32 len;
+       u8  name[8];
+       u32 preload_page_count;
+       u32 fw_image_flags;
+       u32 feature_mask;
+       u16 major;
+       u16 minor;
+       u16 hotfix;
+       u16 build;
+       u32 num_modules;
+       u32 hw_buf_base;
+       u32 hw_buf_length;
+       u32 load_offset;
+} __packed;
+
+struct uuid_module {
+       uuid_le uuid;
+       int id;
+       int is_loadable;
+
+       struct list_head list;
+};
+
+struct skl_ext_manifest_hdr {
+       u32 id;
+       u32 len;
+       u16 version_major;
+       u16 version_minor;
+       u32 entries;
+};
+
+int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid,
+                       struct skl_dfw_module *dfw_config)
+{
+       struct uuid_module *module;
+       uuid_le *uuid_mod;
+
+       uuid_mod = (uuid_le *)uuid;
+
+       list_for_each_entry(module, &ctx->uuid_list, list) {
+               if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) {
+                       dfw_config->module_id = module->id;
+                       dfw_config->is_loadable = module->is_loadable;
+
+                       return 0;
+               }
+       }
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_skl_get_module_info);
+
+/*
+ * Parse the firmware binary to get the UUID, module id
+ * and loadable flags
+ */
+int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset)
+{
+       struct adsp_fw_hdr *adsp_hdr;
+       struct adsp_module_entry *mod_entry;
+       int i, num_entry;
+       uuid_le *uuid_bin;
+       const char *buf;
+       struct skl_sst *skl = ctx->thread_context;
+       struct uuid_module *module;
+       struct firmware stripped_fw;
+       unsigned int safe_file;
+
+       /* Get the FW pointer to derive ADSP header */
+       stripped_fw.data = ctx->fw->data;
+       stripped_fw.size = ctx->fw->size;
+
+       skl_dsp_strip_extended_manifest(&stripped_fw);
+
+       buf = stripped_fw.data;
+
+       /* check if we have enough space in file to move to header */
+       safe_file = sizeof(*adsp_hdr) + offset;
+       if (stripped_fw.size <= safe_file) {
+               dev_err(ctx->dev, "Small fw file size, No space for hdr\n");
+               return -EINVAL;
+       }
+
+       adsp_hdr = (struct adsp_fw_hdr *)(buf + offset);
+
+       /* check 1st module entry is in file */
+       safe_file += adsp_hdr->len + sizeof(*mod_entry);
+       if (stripped_fw.size <= safe_file) {
+               dev_err(ctx->dev, "Small fw file size, No module entry\n");
+               return -EINVAL;
+       }
+
+       mod_entry = (struct adsp_module_entry *)
+               (buf + offset + adsp_hdr->len);
+
+       num_entry = adsp_hdr->num_modules;
+
+       /* check all entries are in file */
+       safe_file += num_entry * sizeof(*mod_entry);
+       if (stripped_fw.size <= safe_file) {
+               dev_err(ctx->dev, "Small fw file size, No modules\n");
+               return -EINVAL;
+       }
+
+
+       /*
+        * Read the UUID(GUID) from FW Manifest.
+        *
+        * The 16 byte UUID format is: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX
+        * Populate the UUID table to store module_id and loadable flags
+        * for the module.
+        */
+
+       for (i = 0; i < num_entry; i++, mod_entry++) {
+               module = kzalloc(sizeof(*module), GFP_KERNEL);
+               if (!module)
+                       return -ENOMEM;
+
+               uuid_bin = (uuid_le *)mod_entry->uuid.id;
+               memcpy(&module->uuid, uuid_bin, sizeof(module->uuid));
+
+               module->id = i;
+               module->is_loadable = mod_entry->type.load_type;
+
+               list_add_tail(&module->list, &skl->uuid_list);
+
+               dev_dbg(ctx->dev,
+                       "Adding uuid :%pUL   mod id: %d  Loadable: %d\n",
+                       &module->uuid, module->id, module->is_loadable);
+       }
+
+       return 0;
+}
+
+void skl_freeup_uuid_list(struct skl_sst *ctx)
+{
+       struct uuid_module *uuid, *_uuid;
+
+       list_for_each_entry_safe(uuid, _uuid, &ctx->uuid_list, list) {
+               list_del(&uuid->list);
+               kfree(uuid);
+       }
+}
+
+/*
+ * some firmware binary contains some extended manifest. This needs
+ * to be stripped in that case before we load and use that image.
+ *
+ * Get the module id for the module by checking
+ * the table for the UUID for the module
+ */
+int skl_dsp_strip_extended_manifest(struct firmware *fw)
+{
+       struct skl_ext_manifest_hdr *hdr;
+
+       /* check if fw file is greater than header we are looking */
+       if (fw->size < sizeof(hdr)) {
+               pr_err("%s: Firmware file small, no hdr\n", __func__);
+               return -EINVAL;
+       }
+
+       hdr = (struct skl_ext_manifest_hdr *)fw->data;
+
+       if (hdr->id == SKL_EXT_MANIFEST_HEADER_MAGIC) {
+               fw->size -= hdr->len;
+               fw->data += hdr->len;
+       }
+
+       return 0;
+}
index 13ec8d53b526d49e57c50e069fdc5ca076ec5fd1..588f899ceb652b5efcb2b87451a058f6dfc2ab0d 100644 (file)
@@ -68,10 +68,13 @@ static int skl_transfer_firmware(struct sst_dsp *ctx,
        return ret;
 }
 
+#define SKL_ADSP_FW_BIN_HDR_OFFSET 0x284
+
 static int skl_load_base_firmware(struct sst_dsp *ctx)
 {
        int ret = 0, i;
        struct skl_sst *skl = ctx->thread_context;
+       struct firmware stripped_fw;
        u32 reg;
 
        skl->boot_complete = false;
@@ -81,11 +84,25 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
                ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev);
                if (ret < 0) {
                        dev_err(ctx->dev, "Request firmware failed %d\n", ret);
-                       skl_dsp_disable_core(ctx);
                        return -EIO;
                }
        }
 
+       ret = snd_skl_parse_uuids(ctx, SKL_ADSP_FW_BIN_HDR_OFFSET);
+       if (ret < 0) {
+               dev_err(ctx->dev,
+                               "UUID parsing err: %d\n", ret);
+               release_firmware(ctx->fw);
+               skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
+               return ret;
+       }
+
+       /* check for extended manifest */
+       stripped_fw.data = ctx->fw->data;
+       stripped_fw.size = ctx->fw->size;
+
+       skl_dsp_strip_extended_manifest(&stripped_fw);
+
        ret = skl_dsp_boot(ctx);
        if (ret < 0) {
                dev_err(ctx->dev, "Boot dsp core failed ret: %d", ret);
@@ -119,7 +136,7 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
                goto transfer_firmware_failed;
        }
 
-       ret = skl_transfer_firmware(ctx, ctx->fw->data, ctx->fw->size);
+       ret = skl_transfer_firmware(ctx, stripped_fw.data, stripped_fw.size);
        if (ret < 0) {
                dev_err(ctx->dev, "Transfer firmware failed%d\n", ret);
                goto transfer_firmware_failed;
@@ -133,67 +150,87 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
                }
 
                dev_dbg(ctx->dev, "Download firmware successful%d\n", ret);
-               skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
+               skl->fw_loaded = true;
        }
        return 0;
 transfer_firmware_failed:
        ctx->cl_dev.ops.cl_cleanup_controller(ctx);
 skl_load_base_firmware_failed:
-       skl_dsp_disable_core(ctx);
+       skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
        release_firmware(ctx->fw);
        ctx->fw = NULL;
        return ret;
 }
 
-static int skl_set_dsp_D0(struct sst_dsp *ctx)
+static int skl_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)
 {
        int ret;
+       struct skl_ipc_dxstate_info dx;
+       struct skl_sst *skl = ctx->thread_context;
+       unsigned int core_mask = SKL_DSP_CORE_MASK(core_id);
 
-       ret = skl_load_base_firmware(ctx);
-       if (ret < 0) {
-               dev_err(ctx->dev, "unable to load firmware\n");
-               return ret;
+       /* If core0 is being turned on, we need to load the FW */
+       if (core_id == SKL_DSP_CORE0_ID) {
+               ret = skl_load_base_firmware(ctx);
+               if (ret < 0) {
+                       dev_err(ctx->dev, "unable to load firmware\n");
+                       return ret;
+               }
        }
 
-       skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
+       /*
+        * If any core other than core 0 is being moved to D0, enable the
+        * core and send the set dx IPC for the core.
+        */
+       if (core_id != SKL_DSP_CORE0_ID) {
+               ret = skl_dsp_enable_core(ctx, core_mask);
+               if (ret < 0)
+                       return ret;
+
+               dx.core_mask = core_mask;
+               dx.dx_mask = core_mask;
+
+               ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID,
+                                       SKL_BASE_FW_MODULE_ID, &dx);
+               if (ret < 0) {
+                       dev_err(ctx->dev, "Failed to set dsp to D0:core id= %d\n",
+                                       core_id);
+                       skl_dsp_disable_core(ctx, core_mask);
+               }
+       }
+
+       skl->cores.state[core_id] = SKL_DSP_RUNNING;
 
        return ret;
 }
 
-static int skl_set_dsp_D3(struct sst_dsp *ctx)
+static int skl_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id)
 {
        int ret;
        struct skl_ipc_dxstate_info dx;
        struct skl_sst *skl = ctx->thread_context;
+       unsigned int core_mask = SKL_DSP_CORE_MASK(core_id);
 
-       dev_dbg(ctx->dev, "In %s:\n", __func__);
-       mutex_lock(&ctx->mutex);
-       if (!is_skl_dsp_running(ctx)) {
-               mutex_unlock(&ctx->mutex);
-               return 0;
-       }
-       mutex_unlock(&ctx->mutex);
-
-       dx.core_mask = SKL_DSP_CORE0_MASK;
+       dx.core_mask = core_mask;
        dx.dx_mask = SKL_IPC_D3_MASK;
+
        ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID, SKL_BASE_FW_MODULE_ID, &dx);
        if (ret < 0)
-               dev_err(ctx->dev,
-                       "D3 request to FW failed, continuing reset: %d", ret);
-
-       /* disable Interrupt */
-       ctx->cl_dev.ops.cl_cleanup_controller(ctx);
-       skl_cldma_int_disable(ctx);
-       skl_ipc_op_int_disable(ctx);
-       skl_ipc_int_disable(ctx);
-
-       ret = skl_dsp_disable_core(ctx);
-       if (ret < 0) {
-               dev_err(ctx->dev, "disable dsp core failed ret: %d\n", ret);
-               ret = -EIO;
+               dev_err(ctx->dev, "set Dx core %d fail: %d\n", core_id, ret);
+
+       if (core_id == SKL_DSP_CORE0_ID) {
+               /* disable Interrupt */
+               ctx->cl_dev.ops.cl_cleanup_controller(ctx);
+               skl_cldma_int_disable(ctx);
+               skl_ipc_op_int_disable(ctx);
+               skl_ipc_int_disable(ctx);
        }
-       skl_dsp_set_state_locked(ctx, SKL_DSP_RESET);
 
+       ret = skl_dsp_disable_core(ctx, core_mask);
+       if (ret < 0)
+               return ret;
+
+       skl->cores.state[core_id] = SKL_DSP_RESET;
        return ret;
 }
 
@@ -360,6 +397,19 @@ static int skl_unload_module(struct sst_dsp *ctx, u16 mod_id)
        return ret;
 }
 
+void skl_clear_module_cnt(struct sst_dsp *ctx)
+{
+       struct skl_module_table *module;
+
+       if (list_empty(&ctx->module_list))
+               return;
+
+       list_for_each_entry(module, &ctx->module_list, list) {
+               module->usage_cnt = 0;
+       }
+}
+EXPORT_SYMBOL_GPL(skl_clear_module_cnt);
+
 static void skl_clear_module_table(struct sst_dsp *ctx)
 {
        struct skl_module_table *module, *tmp;
@@ -409,6 +459,7 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
 
        skl->dev = dev;
        skl_dev.thread_context = skl;
+       INIT_LIST_HEAD(&skl->uuid_list);
 
        skl->dsp = skl_dsp_ctx_init(dev, &skl_dev, irq);
        if (!skl->dsp) {
@@ -432,12 +483,16 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
        if (ret)
                return ret;
 
+       skl->cores.count = 2;
+
        ret = sst->fw_ops.load_fw(sst);
        if (ret < 0) {
                dev_err(dev, "Load base fw failed : %d", ret);
                goto cleanup;
        }
 
+       skl_dsp_init_core_state(sst);
+
        if (dsp)
                *dsp = skl;
 
@@ -452,6 +507,7 @@ EXPORT_SYMBOL_GPL(skl_sst_dsp_init);
 void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
 {
        skl_clear_module_table(ctx->dsp);
+       skl_freeup_uuid_list(ctx);
        skl_ipc_free(&ctx->ipc);
        ctx->dsp->ops->free(ctx->dsp);
        if (ctx->boot_complete) {
index 3e036b0349b9771db5c16f0ce0b781fb4b8dca77..cc0150fc2601ef10e163a239b76a4a6fabd1130c 100644 (file)
@@ -378,43 +378,6 @@ static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w,
        skl_dump_mconfig(ctx, m_cfg);
 }
 
-/*
- * A pipe can have multiple modules, each of them will be a DAPM widget as
- * well. While managing a pipeline we need to get the list of all the
- * widgets in a pipelines, so this helper - skl_tplg_get_pipe_widget() helps
- * to get the SKL type widgets in that pipeline
- */
-static int skl_tplg_alloc_pipe_widget(struct device *dev,
-       struct snd_soc_dapm_widget *w, struct skl_pipe *pipe)
-{
-       struct skl_module_cfg *src_module = NULL;
-       struct snd_soc_dapm_path *p = NULL;
-       struct skl_pipe_module *p_module = NULL;
-
-       p_module = devm_kzalloc(dev, sizeof(*p_module), GFP_KERNEL);
-       if (!p_module)
-               return -ENOMEM;
-
-       p_module->w = w;
-       list_add_tail(&p_module->node, &pipe->w_list);
-
-       snd_soc_dapm_widget_for_each_sink_path(w, p) {
-               if ((p->sink->priv == NULL)
-                               && (!is_skl_dsp_widget_type(w)))
-                       continue;
-
-               if ((p->sink->priv != NULL) && p->connect
-                               && is_skl_dsp_widget_type(p->sink)) {
-
-                       src_module = p->sink->priv;
-                       if (pipe->ppl_id == src_module->pipe->ppl_id)
-                               skl_tplg_alloc_pipe_widget(dev,
-                                                       p->sink, pipe);
-               }
-       }
-       return 0;
-}
-
 /*
  * some modules can have multiple params set from user control and
  * need to be set after module is initialized. If set_param flag is
@@ -448,7 +411,7 @@ static int skl_tplg_set_module_params(struct snd_soc_dapm_widget *w,
 
                        if (bc->set_params == SKL_PARAM_SET) {
                                ret = skl_set_module_params(ctx,
-                                               (u32 *)bc->params, bc->max,
+                                               (u32 *)bc->params, bc->size,
                                                bc->param_id, mconfig);
                                if (ret < 0)
                                        return ret;
@@ -483,7 +446,7 @@ static int skl_tplg_set_module_init_data(struct snd_soc_dapm_widget *w)
                                continue;
 
                        mconfig->formats_config.caps = (u32 *)&bc->params;
-                       mconfig->formats_config.caps_size = bc->max;
+                       mconfig->formats_config.caps_size = bc->size;
 
                        break;
                }
@@ -514,8 +477,6 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
                if (!skl_is_pipe_mcps_avail(skl, mconfig))
                        return -ENOMEM;
 
-               skl_tplg_alloc_pipe_mcps(skl, mconfig);
-
                if (mconfig->is_loadable && ctx->dsp->fw_ops.load_mod) {
                        ret = ctx->dsp->fw_ops.load_mod(ctx->dsp,
                                mconfig->id.module_id, mconfig->guid);
@@ -539,6 +500,7 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
                if (ret < 0)
                        return ret;
 
+               skl_tplg_alloc_pipe_mcps(skl, mconfig);
                ret = skl_tplg_set_module_params(w, ctx);
                if (ret < 0)
                        return ret;
@@ -591,9 +553,6 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
        if (!skl_is_pipe_mem_avail(skl, mconfig))
                return -ENOMEM;
 
-       skl_tplg_alloc_pipe_mem(skl, mconfig);
-       skl_tplg_alloc_pipe_mcps(skl, mconfig);
-
        /*
         * Create a list of modules for pipe.
         * This list contains modules from source to sink
@@ -602,19 +561,8 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
        if (ret < 0)
                return ret;
 
-       /*
-        * we create a w_list of all widgets in that pipe. This list is not
-        * freed on PMD event as widgets within a pipe are static. This
-        * saves us cycles to get widgets in pipe every time.
-        *
-        * So if we have already initialized all the widgets of a pipeline
-        * we skip, so check for list_empty and create the list if empty
-        */
-       if (list_empty(&s_pipe->w_list)) {
-               ret = skl_tplg_alloc_pipe_widget(ctx->dev, w, s_pipe);
-               if (ret < 0)
-                       return ret;
-       }
+       skl_tplg_alloc_pipe_mem(skl, mconfig);
+       skl_tplg_alloc_pipe_mcps(skl, mconfig);
 
        /* Init all pipe modules from source to sink */
        ret = skl_tplg_init_pipe_modules(skl, s_pipe);
@@ -949,13 +897,17 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
        struct skl_pipe *s_pipe = mconfig->pipe;
        int ret = 0;
 
+       if (s_pipe->state == SKL_PIPE_INVALID)
+               return -EINVAL;
+
        skl_tplg_free_pipe_mcps(skl, mconfig);
        skl_tplg_free_pipe_mem(skl, mconfig);
 
        list_for_each_entry(w_module, &s_pipe->w_list, node) {
                dst_module = w_module->w->priv;
 
-               skl_tplg_free_pipe_mcps(skl, dst_module);
+               if (mconfig->m_state >= SKL_MODULE_INIT_DONE)
+                       skl_tplg_free_pipe_mcps(skl, dst_module);
                if (src_module == NULL) {
                        src_module = dst_module;
                        continue;
@@ -1102,7 +1054,7 @@ static int skl_tplg_tlv_control_get(struct snd_kcontrol *kcontrol,
 
        if (w->power)
                skl_get_module_params(skl->skl_sst, (u32 *)bc->params,
-                                     bc->max, bc->param_id, mconfig);
+                                     bc->size, bc->param_id, mconfig);
 
        /* decrement size for TLV header */
        size -= 2 * sizeof(u32);
@@ -1136,6 +1088,10 @@ static int skl_tplg_tlv_control_set(struct snd_kcontrol *kcontrol,
        struct skl *skl = get_skl_ctx(w->dapm->dev);
 
        if (ac->params) {
+               if (size > ac->max)
+                       return -EINVAL;
+
+               ac->size = size;
                /*
                 * if the param_is is of type Vendor, firmware expects actual
                 * parameter id and size from the control.
@@ -1151,13 +1107,46 @@ static int skl_tplg_tlv_control_set(struct snd_kcontrol *kcontrol,
 
                if (w->power)
                        return skl_set_module_params(skl->skl_sst,
-                                               (u32 *)ac->params, ac->max,
+                                               (u32 *)ac->params, ac->size,
                                                ac->param_id, mconfig);
        }
 
        return 0;
 }
 
+/*
+ * Fill the dma id for host and link. In case of passthrough
+ * pipeline, this will both host and link in the same
+ * pipeline, so need to copy the link and host based on dev_type
+ */
+static void skl_tplg_fill_dma_id(struct skl_module_cfg *mcfg,
+                               struct skl_pipe_params *params)
+{
+       struct skl_pipe *pipe = mcfg->pipe;
+
+       if (pipe->passthru) {
+               switch (mcfg->dev_type) {
+               case SKL_DEVICE_HDALINK:
+                       pipe->p_params->link_dma_id = params->link_dma_id;
+                       break;
+
+               case SKL_DEVICE_HDAHOST:
+                       pipe->p_params->host_dma_id = params->host_dma_id;
+                       break;
+
+               default:
+                       break;
+               }
+               pipe->p_params->s_fmt = params->s_fmt;
+               pipe->p_params->ch = params->ch;
+               pipe->p_params->s_freq = params->s_freq;
+               pipe->p_params->stream = params->stream;
+
+       } else {
+               memcpy(pipe->p_params, params, sizeof(*params));
+       }
+}
+
 /*
  * The FE params are passed by hw_params of the DAI.
  * On hw_params, the params are stored in Gateway module of the FE and we
@@ -1168,10 +1157,9 @@ int skl_tplg_update_pipe_params(struct device *dev,
                        struct skl_module_cfg *mconfig,
                        struct skl_pipe_params *params)
 {
-       struct skl_pipe *pipe = mconfig->pipe;
        struct skl_module_fmt *format = NULL;
 
-       memcpy(pipe->p_params, params, sizeof(*params));
+       skl_tplg_fill_dma_id(mconfig, params);
 
        if (params->stream == SNDRV_PCM_STREAM_PLAYBACK)
                format = &mconfig->in_fmt[0];
@@ -1358,12 +1346,11 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai,
                                struct skl_module_cfg *mconfig,
                                struct skl_pipe_params *params)
 {
-       struct skl_pipe *pipe = mconfig->pipe;
        struct nhlt_specific_cfg *cfg;
        struct skl *skl = get_skl_ctx(dai->dev);
        int link_type = skl_tplg_be_link_type(mconfig->dev_type);
 
-       memcpy(pipe->p_params, params, sizeof(*params));
+       skl_tplg_fill_dma_id(mconfig, params);
 
        if (link_type == NHLT_LINK_HDA)
                return 0;
@@ -1554,6 +1541,55 @@ static void skl_tplg_fill_fmt(struct skl_module_fmt *dst_fmt,
        }
 }
 
+static void skl_clear_pin_config(struct snd_soc_platform *platform,
+                               struct snd_soc_dapm_widget *w)
+{
+       int i;
+       struct skl_module_cfg *mconfig;
+       struct skl_pipe *pipe;
+
+       if (!strncmp(w->dapm->component->name, platform->component.name,
+                                       strlen(platform->component.name))) {
+               mconfig = w->priv;
+               pipe = mconfig->pipe;
+               for (i = 0; i < mconfig->max_in_queue; i++) {
+                       mconfig->m_in_pin[i].in_use = false;
+                       mconfig->m_in_pin[i].pin_state = SKL_PIN_UNBIND;
+               }
+               for (i = 0; i < mconfig->max_out_queue; i++) {
+                       mconfig->m_out_pin[i].in_use = false;
+                       mconfig->m_out_pin[i].pin_state = SKL_PIN_UNBIND;
+               }
+               pipe->state = SKL_PIPE_INVALID;
+               mconfig->m_state = SKL_MODULE_UNINIT;
+       }
+}
+
+void skl_cleanup_resources(struct skl *skl)
+{
+       struct skl_sst *ctx = skl->skl_sst;
+       struct snd_soc_platform *soc_platform = skl->platform;
+       struct snd_soc_dapm_widget *w;
+       struct snd_soc_card *card;
+
+       if (soc_platform == NULL)
+               return;
+
+       card = soc_platform->component.card;
+       if (!card || !card->instantiated)
+               return;
+
+       skl->resource.mem = 0;
+       skl->resource.mcps = 0;
+
+       list_for_each_entry(w, &card->widgets, list) {
+               if (is_skl_dsp_widget_type(w) && (w->priv != NULL))
+                       skl_clear_pin_config(soc_platform, w);
+       }
+
+       skl_clear_module_cnt(ctx->dsp);
+}
+
 /*
  * Topology core widget load callback
  *
@@ -1585,6 +1621,10 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
        w->priv = mconfig;
        memcpy(&mconfig->guid, &dfw_config->uuid, 16);
 
+       ret = snd_skl_get_module_info(skl->skl_sst, mconfig->guid, dfw_config);
+       if (ret < 0)
+               return ret;
+
        mconfig->id.module_id = dfw_config->module_id;
        mconfig->id.instance_id = dfw_config->instance_id;
        mconfig->mcps = dfw_config->max_mcps;
@@ -1683,6 +1723,7 @@ static int skl_init_algo_data(struct device *dev, struct soc_bytes_ext *be,
        ac->max = dfw_ac->max;
        ac->param_id = dfw_ac->param_id;
        ac->set_params = dfw_ac->set_params;
+       ac->size = dfw_ac->max;
 
        if (ac->max) {
                ac->params = (char *) devm_kzalloc(dev, ac->max, GFP_KERNEL);
@@ -1733,6 +1774,60 @@ static struct snd_soc_tplg_ops skl_tplg_ops  = {
        .bytes_ext_ops_count = ARRAY_SIZE(skl_tlv_ops),
 };
 
+/*
+ * A pipe can have multiple modules, each of them will be a DAPM widget as
+ * well. While managing a pipeline we need to get the list of all the
+ * widgets in a pipelines, so this helper - skl_tplg_create_pipe_widget_list()
+ * helps to get the SKL type widgets in that pipeline
+ */
+static int skl_tplg_create_pipe_widget_list(struct snd_soc_platform *platform)
+{
+       struct snd_soc_dapm_widget *w;
+       struct skl_module_cfg *mcfg = NULL;
+       struct skl_pipe_module *p_module = NULL;
+       struct skl_pipe *pipe;
+
+       list_for_each_entry(w, &platform->component.card->widgets, list) {
+               if (is_skl_dsp_widget_type(w) && w->priv != NULL) {
+                       mcfg = w->priv;
+                       pipe = mcfg->pipe;
+
+                       p_module = devm_kzalloc(platform->dev,
+                                               sizeof(*p_module), GFP_KERNEL);
+                       if (!p_module)
+                               return -ENOMEM;
+
+                       p_module->w = w;
+                       list_add_tail(&p_module->node, &pipe->w_list);
+               }
+       }
+
+       return 0;
+}
+
+static void skl_tplg_set_pipe_type(struct skl *skl, struct skl_pipe *pipe)
+{
+       struct skl_pipe_module *w_module;
+       struct snd_soc_dapm_widget *w;
+       struct skl_module_cfg *mconfig;
+       bool host_found = false, link_found = false;
+
+       list_for_each_entry(w_module, &pipe->w_list, node) {
+               w = w_module->w;
+               mconfig = w->priv;
+
+               if (mconfig->dev_type == SKL_DEVICE_HDAHOST)
+                       host_found = true;
+               else if (mconfig->dev_type != SKL_DEVICE_NONE)
+                       link_found = true;
+       }
+
+       if (host_found && link_found)
+               pipe->passthru = true;
+       else
+               pipe->passthru = false;
+}
+
 /* This will be read from topology manifest, currently defined here */
 #define SKL_MAX_MCPS 30000000
 #define SKL_FW_MAX_MEM 1000000
@@ -1746,6 +1841,7 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus)
        const struct firmware *fw;
        struct hdac_bus *bus = ebus_to_hbus(ebus);
        struct skl *skl = ebus_to_skl(ebus);
+       struct skl_pipeline *ppl;
 
        ret = request_firmware(&fw, skl->tplg_name, bus->dev);
        if (ret < 0) {
@@ -1775,6 +1871,12 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus)
        skl->resource.max_mem = SKL_FW_MAX_MEM;
 
        skl->tplg = fw;
+       ret = skl_tplg_create_pipe_widget_list(platform);
+       if (ret < 0)
+               return ret;
+
+       list_for_each_entry(ppl, &skl->ppl_list, node)
+               skl_tplg_set_pipe_type(skl, ppl->pipe);
 
        return 0;
 }
index e4b399cd7868fd85a4f79e48c01deb770e32f220..22d3ef83817dfdfa762c65d08bba11b4628dc74f 100644 (file)
@@ -244,7 +244,8 @@ enum skl_pipe_state {
        SKL_PIPE_INVALID = 0,
        SKL_PIPE_CREATED = 1,
        SKL_PIPE_PAUSED = 2,
-       SKL_PIPE_STARTED = 3
+       SKL_PIPE_STARTED = 3,
+       SKL_PIPE_RESET = 4
 };
 
 struct skl_pipe_module {
@@ -270,6 +271,7 @@ struct skl_pipe {
        struct skl_pipe_params *p_params;
        enum skl_pipe_state state;
        struct list_head w_list;
+       bool passthru;
 };
 
 enum skl_module_state {
@@ -319,6 +321,7 @@ struct skl_algo_data {
        u32 param_id;
        u32 set_params;
        u32 max;
+       u32 size;
        char *params;
 };
 
@@ -357,6 +360,8 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
 
 int skl_stop_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
 
+int skl_reset_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
+
 int skl_init_module(struct skl_sst *ctx, struct skl_module_cfg *module_config);
 
 int skl_bind_modules(struct skl_sst *ctx, struct skl_module_cfg
index 06d8c263c68f93342931fbcfe447cd986408f2f3..cd59536a761dd84bcca2fb3afb44dc82adf1b417 100644 (file)
@@ -35,6 +35,8 @@
 #include "skl-sst-dsp.h"
 #include "skl-sst-ipc.h"
 
+static struct skl_machine_pdata skl_dmic_data;
+
 /*
  * initialize the PCI registers
  */
@@ -184,6 +186,7 @@ static int _skl_suspend(struct hdac_ext_bus *ebus)
 {
        struct skl *skl = ebus_to_skl(ebus);
        struct hdac_bus *bus = ebus_to_hbus(ebus);
+       struct pci_dev *pci = to_pci_dev(bus->dev);
        int ret;
 
        snd_hdac_ext_bus_link_power_down_all(ebus);
@@ -193,9 +196,12 @@ static int _skl_suspend(struct hdac_ext_bus *ebus)
                return ret;
 
        snd_hdac_bus_stop_chip(bus);
+       update_pci_dword(pci, AZX_PCIREG_PGCTL,
+               AZX_PGCTL_LSRMD_MASK, AZX_PGCTL_LSRMD_MASK);
        skl_enable_miscbdcge(bus->dev, false);
        snd_hdac_bus_enter_link_reset(bus);
        skl_enable_miscbdcge(bus->dev, true);
+       skl_cleanup_resources(skl);
 
        return 0;
 }
@@ -242,6 +248,7 @@ static int skl_suspend(struct device *dev)
                ret = _skl_suspend(ebus);
                if (ret < 0)
                        return ret;
+               skl->skl_sst->fw_loaded = false;
        }
 
        if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) {
@@ -397,6 +404,10 @@ static int skl_machine_device_register(struct skl *skl, void *driver_data)
                platform_device_put(pdev);
                return -EIO;
        }
+
+       if (mach->pdata)
+               dev_set_drvdata(&pdev->dev, mach->pdata);
+
        skl->i2s_dev = pdev;
 
        return 0;
@@ -657,6 +668,8 @@ static int skl_probe(struct pci_dev *pci,
 
        skl->pci_id = pci->device;
 
+       device_disable_async_suspend(bus->dev);
+
        skl->nhlt = skl_nhlt_init(bus->dev);
 
        if (skl->nhlt == NULL)
@@ -666,6 +679,8 @@ static int skl_probe(struct pci_dev *pci,
 
        pci_set_drvdata(skl->pci, ebus);
 
+       skl_dmic_data.dmic_num = skl_get_dmic_geo(skl);
+
        /* check if dsp is there */
        if (ebus->ppcap) {
                err = skl_machine_device_register(skl,
@@ -713,7 +728,7 @@ static int skl_probe(struct pci_dev *pci,
        list_for_each_entry(hlink, &ebus->hlink_list, list)
                snd_hdac_ext_bus_link_put(ebus, hlink);
 
-       /*configure PM */
+       /* configure PM */
        pm_runtime_put_noidle(bus->dev);
        pm_runtime_allow(bus->dev);
 
@@ -766,8 +781,7 @@ static void skl_remove(struct pci_dev *pci)
        struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
        struct skl *skl = ebus_to_skl(ebus);
 
-       if (skl->tplg)
-               release_firmware(skl->tplg);
+       release_firmware(skl->tplg);
 
        if (pci_dev_run_wake(pci))
                pm_runtime_get_noresume(&pci->dev);
@@ -786,15 +800,23 @@ static void skl_remove(struct pci_dev *pci)
 
 static struct sst_acpi_mach sst_skl_devdata[] = {
        { "INT343A", "skl_alc286s_i2s", "intel/dsp_fw_release.bin", NULL, NULL, NULL },
-       { "INT343B", "skl_nau88l25_ssm4567_i2s", "intel/dsp_fw_release.bin",
-                               NULL, NULL, NULL },
-       { "MX98357A", "skl_nau88l25_max98357a_i2s", "intel/dsp_fw_release.bin",
-                               NULL, NULL, NULL },
+       { "INT343B", "skl_n88l25_s4567", "intel/dsp_fw_release.bin",
+                               NULL, NULL, &skl_dmic_data },
+       { "MX98357A", "skl_n88l25_m98357a", "intel/dsp_fw_release.bin",
+                               NULL, NULL, &skl_dmic_data },
        {}
 };
 
 static struct sst_acpi_mach sst_bxtp_devdata[] = {
        { "INT343A", "bxt_alc298s_i2s", "intel/dsp_fw_bxtn.bin", NULL, NULL, NULL },
+       { "DLGS7219", "bxt_da7219_max98357a_i2s", "intel/dsp_fw_bxtn.bin", NULL, NULL, NULL },
+};
+
+static struct sst_acpi_mach sst_kbl_devdata[] = {
+       { "INT343A", "kbl_alc286s_i2s", "intel/dsp_fw_kbl.bin", NULL, NULL, NULL },
+       { "INT343B", "kbl_n88l25_s4567", "intel/dsp_fw_kbl.bin", NULL, NULL, &skl_dmic_data },
+       { "MX98357A", "kbl_n88l25_m98357a", "intel/dsp_fw_kbl.bin", NULL, NULL, &skl_dmic_data },
+       {}
 };
 
 /* PCI IDs */
@@ -805,6 +827,9 @@ static const struct pci_device_id skl_ids[] = {
        /* BXT-P */
        { PCI_DEVICE(0x8086, 0x5a98),
                .driver_data = (unsigned long)&sst_bxtp_devdata},
+       /* KBL */
+       { PCI_DEVICE(0x8086, 0x9D71),
+               .driver_data = (unsigned long)&sst_kbl_devdata},
        { 0, }
 };
 MODULE_DEVICE_TABLE(pci, skl_ids);
index 4b4b3876aea9a9ab6a70e2bce86d822c5c30168b..9064e5b0d67689be97299d5f5ae2f9a361a547f6 100644 (file)
@@ -48,6 +48,8 @@
 #define AZX_REG_VS_SDXEFIFOS_XBASE     0x1094
 #define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20
 
+#define AZX_PCIREG_PGCTL               0x44
+#define AZX_PGCTL_LSRMD_MASK           (1 << 4)
 #define AZX_PCIREG_CGCTL               0x48
 #define AZX_CGCTL_MISCBDCGE_MASK       (1 << 6)
 
@@ -65,6 +67,7 @@ struct skl {
        unsigned int init_failed:1; /* delayed init failed */
        struct platform_device *dmic_dev;
        struct platform_device *i2s_dev;
+       struct snd_soc_platform *platform;
 
        struct nhlt_acpi_table *nhlt; /* nhlt ptr */
        struct skl_sst *skl_sst; /* sst skl ctx */
@@ -90,6 +93,11 @@ struct skl_dma_params {
        u8 stream_tag;
 };
 
+/* to pass dmic data */
+struct skl_machine_pdata {
+       u32 dmic_num;
+};
+
 struct skl_dsp_ops {
        int id;
        struct skl_dsp_loader_ops (*loader_ops)(void);
@@ -108,9 +116,11 @@ void skl_nhlt_free(struct nhlt_acpi_table *addr);
 struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance,
                        u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn);
 
+int skl_get_dmic_geo(struct skl *skl);
 int skl_nhlt_update_topology_bin(struct skl *skl);
 int skl_init_dsp(struct skl *skl);
 int skl_free_dsp(struct skl *skl);
 int skl_suspend_dsp(struct skl *skl);
 int skl_resume_dsp(struct skl *skl);
+void skl_cleanup_resources(struct skl *skl);
 #endif /* __SOUND_SOC_SKL_H */
index 3abf51c07851ec42d5d9da17d3185abbc6e1021a..05cf809cf9e1467dca19e4f02433f6e6b34532a3 100644 (file)
@@ -1,15 +1,40 @@
 config SND_SOC_MEDIATEK
-       tristate "ASoC support for Mediatek chip"
+       tristate
+
+config SND_SOC_MT2701
+       tristate "ASoC support for Mediatek MT2701 chip"
+       depends on ARCH_MEDIATEK
+       select SND_SOC_MEDIATEK
+       help
+         This adds ASoC driver for Mediatek MT2701 boards
+         that can be used with other codecs.
+         Select Y if you have such device.
+         If unsure select "N".
+
+config SND_SOC_MT2701_CS42448
+       tristate "ASoc Audio driver for MT2701 with CS42448 codec"
+       depends on SND_SOC_MT2701
+       select SND_SOC_CS42XX8_I2C
+       select SND_SOC_BT_SCO
+       help
+         This adds ASoC driver for Mediatek MT2701 boards
+         with the CS42448 codecs.
+         Select Y if you have such device.
+         If unsure select "N".
+
+config SND_SOC_MT8173
+       tristate "ASoC support for Mediatek MT8173 chip"
        depends on ARCH_MEDIATEK
+       select SND_SOC_MEDIATEK
        help
-         This adds ASoC platform driver support for Mediatek chip
+         This adds ASoC platform driver support for Mediatek MT8173 chip
          that can be used with other codecs.
          Select Y if you have such device.
          Ex: MT8173
 
 config SND_SOC_MT8173_MAX98090
        tristate "ASoC Audio driver for MT8173 with MAX98090 codec"
-       depends on SND_SOC_MEDIATEK && I2C
+       depends on SND_SOC_MT8173 && I2C
        select SND_SOC_MAX98090
        help
          This adds ASoC driver for Mediatek MT8173 boards
@@ -19,8 +44,9 @@ config SND_SOC_MT8173_MAX98090
 
 config SND_SOC_MT8173_RT5650
        tristate "ASoC Audio driver for MT8173 with RT5650 codec"
-       depends on SND_SOC_MEDIATEK && I2C
+       depends on SND_SOC_MT8173 && I2C
        select SND_SOC_RT5645
+       select SND_SOC_HDMI_CODEC
        help
          This adds ASoC driver for Mediatek MT8173 boards
          with the RT5650 audio codec.
@@ -29,7 +55,7 @@ config SND_SOC_MT8173_RT5650
 
 config SND_SOC_MT8173_RT5650_RT5514
        tristate "ASoC Audio driver for MT8173 with RT5650 RT5514 codecs"
-       depends on SND_SOC_MEDIATEK && I2C
+       depends on SND_SOC_MT8173 && I2C
        select SND_SOC_RT5645
        select SND_SOC_RT5514
        help
@@ -40,7 +66,7 @@ config SND_SOC_MT8173_RT5650_RT5514
 
 config SND_SOC_MT8173_RT5650_RT5676
        tristate "ASoC Audio driver for MT8173 with RT5650 RT5676 codecs"
-       depends on SND_SOC_MEDIATEK && I2C
+       depends on SND_SOC_MT8173 && I2C
        select SND_SOC_RT5645
        select SND_SOC_RT5677
        select SND_SOC_HDMI_CODEC
index d486860c0a88e832f44b78f02d1579b550844d0b..6bcab35dc82868d40e0afe17ea0a79e966276902 100644 (file)
@@ -1,7 +1,3 @@
-# MTK Platform Support
-obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o
-# Machine support
-obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o
-obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173-rt5650.o
-obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173-rt5650-rt5514.o
-obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o
+obj-$(CONFIG_SND_SOC_MEDIATEK) += common/
+obj-$(CONFIG_SND_SOC_MT2701) += mt2701/
+obj-$(CONFIG_SND_SOC_MT8173) += mt8173/
diff --git a/sound/soc/mediatek/common/Makefile b/sound/soc/mediatek/common/Makefile
new file mode 100644 (file)
index 0000000..a55d33b
--- /dev/null
@@ -0,0 +1,16 @@
+#
+# Copyright (C) 2015 MediaTek Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# 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.
+#
+
+# platform driver
+snd-soc-mtk-common-objs := mtk-afe-platform-driver.o mtk-afe-fe-dai.o
+obj-$(CONFIG_SND_SOC_MEDIATEK) += snd-soc-mtk-common.o
diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.c b/sound/soc/mediatek/common/mtk-afe-fe-dai.c
new file mode 100644 (file)
index 0000000..b788791
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * mtk-afe-fe-dais.c  --  Mediatek afe fe dai operator
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "mtk-afe-fe-dai.h"
+#include "mtk-base-afe.h"
+
+#define AFE_BASE_END_OFFSET 8
+
+int mtk_regmap_update_bits(struct regmap *map, int reg, unsigned int mask,
+                          unsigned int val)
+{
+       if (reg < 0)
+               return 0;
+       return regmap_update_bits(map, reg, mask, val);
+}
+
+int mtk_regmap_write(struct regmap *map, int reg, unsigned int val)
+{
+       if (reg < 0)
+               return 0;
+       return regmap_write(map, reg, val);
+}
+
+int mtk_afe_fe_startup(struct snd_pcm_substream *substream,
+                      struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int memif_num = rtd->cpu_dai->id;
+       struct mtk_base_afe_memif *memif = &afe->memif[memif_num];
+       const struct snd_pcm_hardware *mtk_afe_hardware = afe->mtk_afe_hardware;
+       int ret;
+
+       memif->substream = substream;
+
+       snd_pcm_hw_constraint_step(substream->runtime, 0,
+                                  SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
+       /* enable agent */
+       mtk_regmap_update_bits(afe->regmap, memif->data->agent_disable_reg,
+                              1 << memif->data->agent_disable_shift,
+                              0 << memif->data->agent_disable_shift);
+
+       snd_soc_set_runtime_hwparams(substream, mtk_afe_hardware);
+
+       /*
+        * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be
+        * smaller than period_size due to AFE's internal buffer.
+        * This easily leads to overrun when avail_min is period_size.
+        * One more period can hold the possible unread buffer.
+        */
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+               int periods_max = mtk_afe_hardware->periods_max;
+
+               ret = snd_pcm_hw_constraint_minmax(runtime,
+                                                  SNDRV_PCM_HW_PARAM_PERIODS,
+                                                  3, periods_max);
+               if (ret < 0) {
+                       dev_err(afe->dev, "hw_constraint_minmax failed\n");
+                       return ret;
+               }
+       }
+
+       ret = snd_pcm_hw_constraint_integer(runtime,
+                                           SNDRV_PCM_HW_PARAM_PERIODS);
+       if (ret < 0)
+               dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
+
+       /* dynamic allocate irq to memif */
+       if (memif->irq_usage < 0) {
+               int irq_id = mtk_dynamic_irq_acquire(afe);
+
+               if (irq_id != afe->irqs_size) {
+                       /* link */
+                       memif->irq_usage = irq_id;
+               } else {
+                       dev_err(afe->dev, "%s() error: no more asys irq\n",
+                               __func__);
+                       ret = -EBUSY;
+               }
+       }
+       return ret;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_startup);
+
+void mtk_afe_fe_shutdown(struct snd_pcm_substream *substream,
+                        struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+       int irq_id;
+
+       irq_id = memif->irq_usage;
+
+       mtk_regmap_update_bits(afe->regmap, memif->data->agent_disable_reg,
+                              1 << memif->data->agent_disable_shift,
+                              1 << memif->data->agent_disable_shift);
+
+       if (!memif->const_irq) {
+               mtk_dynamic_irq_release(afe, irq_id);
+               memif->irq_usage = -1;
+               memif->substream = NULL;
+       }
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_shutdown);
+
+int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream,
+                        struct snd_pcm_hw_params *params,
+                        struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+       int msb_at_bit33 = 0;
+       int ret, fs = 0;
+
+       ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+       if (ret < 0)
+               return ret;
+
+       msb_at_bit33 = upper_32_bits(substream->runtime->dma_addr) ? 1 : 0;
+       memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr);
+       memif->buffer_size = substream->runtime->dma_bytes;
+
+       /* start */
+       mtk_regmap_write(afe->regmap, memif->data->reg_ofs_base,
+                        memif->phys_buf_addr);
+       /* end */
+       mtk_regmap_write(afe->regmap,
+                        memif->data->reg_ofs_base + AFE_BASE_END_OFFSET,
+                        memif->phys_buf_addr + memif->buffer_size - 1);
+
+       /* set MSB to 33-bit */
+       mtk_regmap_update_bits(afe->regmap, memif->data->msb_reg,
+                              1 << memif->data->msb_shift,
+                              msb_at_bit33 << memif->data->msb_shift);
+
+       /* set channel */
+       if (memif->data->mono_shift >= 0) {
+               unsigned int mono = (params_channels(params) == 1) ? 1 : 0;
+
+               mtk_regmap_update_bits(afe->regmap, memif->data->mono_reg,
+                                      1 << memif->data->mono_shift,
+                                      mono << memif->data->mono_shift);
+       }
+
+       /* set rate */
+       if (memif->data->fs_shift < 0)
+               return 0;
+
+       fs = afe->memif_fs(substream, params_rate(params));
+
+       if (fs < 0)
+               return -EINVAL;
+
+       mtk_regmap_update_bits(afe->regmap, memif->data->fs_reg,
+                              memif->data->fs_maskbit << memif->data->fs_shift,
+                              fs << memif->data->fs_shift);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_hw_params);
+
+int mtk_afe_fe_hw_free(struct snd_pcm_substream *substream,
+                      struct snd_soc_dai *dai)
+{
+       return snd_pcm_lib_free_pages(substream);
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_hw_free);
+
+int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+                      struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_pcm_runtime * const runtime = substream->runtime;
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+       struct mtk_base_afe_irq *irqs = &afe->irqs[memif->irq_usage];
+       const struct mtk_base_irq_data *irq_data = irqs->irq_data;
+       unsigned int counter = runtime->period_size;
+       int fs;
+
+       dev_dbg(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+               if (memif->data->enable_shift >= 0)
+                       mtk_regmap_update_bits(afe->regmap,
+                                              memif->data->enable_reg,
+                                              1 << memif->data->enable_shift,
+                                              1 << memif->data->enable_shift);
+
+               /* set irq counter */
+               mtk_regmap_update_bits(afe->regmap, irq_data->irq_cnt_reg,
+                                      irq_data->irq_cnt_maskbit
+                                      << irq_data->irq_cnt_shift,
+                                      counter << irq_data->irq_cnt_shift);
+
+               /* set irq fs */
+               fs = afe->irq_fs(substream, runtime->rate);
+
+               if (fs < 0)
+                       return -EINVAL;
+
+               mtk_regmap_update_bits(afe->regmap, irq_data->irq_fs_reg,
+                                      irq_data->irq_fs_maskbit
+                                      << irq_data->irq_fs_shift,
+                                      fs << irq_data->irq_fs_shift);
+
+               /* enable interrupt */
+               mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
+                                      1 << irq_data->irq_en_shift,
+                                      1 << irq_data->irq_en_shift);
+
+               return 0;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               mtk_regmap_update_bits(afe->regmap, memif->data->enable_reg,
+                                      1 << memif->data->enable_shift, 0);
+               /* disable interrupt */
+               mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
+                                      1 << irq_data->irq_en_shift,
+                                      0 << irq_data->irq_en_shift);
+               /* and clear pending IRQ */
+               mtk_regmap_write(afe->regmap, irq_data->irq_clr_reg,
+                                1 << irq_data->irq_clr_shift);
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_trigger);
+
+int mtk_afe_fe_prepare(struct snd_pcm_substream *substream,
+                      struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd  = substream->private_data;
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+       int hd_audio = 0;
+
+       /* set hd mode */
+       switch (substream->runtime->format) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               hd_audio = 0;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               hd_audio = 1;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               hd_audio = 1;
+               break;
+       default:
+               dev_err(afe->dev, "%s() error: unsupported format %d\n",
+                       __func__, substream->runtime->format);
+               break;
+       }
+
+       mtk_regmap_update_bits(afe->regmap, memif->data->hd_reg,
+                              1 << memif->data->hd_shift,
+                              hd_audio << memif->data->hd_shift);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_prepare);
+
+const struct snd_soc_dai_ops mtk_afe_fe_ops = {
+       .startup        = mtk_afe_fe_startup,
+       .shutdown       = mtk_afe_fe_shutdown,
+       .hw_params      = mtk_afe_fe_hw_params,
+       .hw_free        = mtk_afe_fe_hw_free,
+       .prepare        = mtk_afe_fe_prepare,
+       .trigger        = mtk_afe_fe_trigger,
+};
+EXPORT_SYMBOL_GPL(mtk_afe_fe_ops);
+
+static DEFINE_MUTEX(irqs_lock);
+int mtk_dynamic_irq_acquire(struct mtk_base_afe *afe)
+{
+       int i;
+
+       mutex_lock(&afe->irq_alloc_lock);
+       for (i = 0; i < afe->irqs_size; ++i) {
+               if (afe->irqs[i].irq_occupyed == 0) {
+                       afe->irqs[i].irq_occupyed = 1;
+                       mutex_unlock(&afe->irq_alloc_lock);
+                       return i;
+               }
+       }
+       mutex_unlock(&afe->irq_alloc_lock);
+       return afe->irqs_size;
+}
+EXPORT_SYMBOL_GPL(mtk_dynamic_irq_acquire);
+
+int mtk_dynamic_irq_release(struct mtk_base_afe *afe, int irq_id)
+{
+       mutex_lock(&afe->irq_alloc_lock);
+       if (irq_id >= 0 && irq_id < afe->irqs_size) {
+               afe->irqs[irq_id].irq_occupyed = 0;
+               mutex_unlock(&afe->irq_alloc_lock);
+               return 0;
+       }
+       mutex_unlock(&afe->irq_alloc_lock);
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(mtk_dynamic_irq_release);
+
+int mtk_afe_dai_suspend(struct snd_soc_dai *dai)
+{
+       struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+       struct device *dev = afe->dev;
+       struct regmap *regmap = afe->regmap;
+       int i;
+
+       if (pm_runtime_status_suspended(dev) || afe->suspended)
+               return 0;
+
+       if (!afe->reg_back_up)
+               afe->reg_back_up =
+                       devm_kcalloc(dev, afe->reg_back_up_list_num,
+                                    sizeof(unsigned int), GFP_KERNEL);
+
+       for (i = 0; i < afe->reg_back_up_list_num; i++)
+               regmap_read(regmap, afe->reg_back_up_list[i],
+                           &afe->reg_back_up[i]);
+
+       afe->suspended = true;
+       afe->runtime_suspend(dev);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_dai_suspend);
+
+int mtk_afe_dai_resume(struct snd_soc_dai *dai)
+{
+       struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+       struct device *dev = afe->dev;
+       struct regmap *regmap = afe->regmap;
+       int i = 0;
+
+       if (pm_runtime_status_suspended(dev) || !afe->suspended)
+               return 0;
+
+       afe->runtime_resume(dev);
+
+       if (!afe->reg_back_up)
+               dev_dbg(dev, "%s no reg_backup\n", __func__);
+
+       for (i = 0; i < afe->reg_back_up_list_num; i++)
+               mtk_regmap_write(regmap, afe->reg_back_up_list[i],
+                                afe->reg_back_up[i]);
+
+       afe->suspended = false;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_dai_resume);
+
+MODULE_DESCRIPTION("Mediatek simple fe dai operator");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+
diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.h b/sound/soc/mediatek/common/mtk-afe-fe-dai.h
new file mode 100644 (file)
index 0000000..28cb178
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * mtk-afe-fe-dais.h  --  Mediatek afe fe dai operator definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_AFE_FE_DAI_H_
+#define _MTK_AFE_FE_DAI_H_
+
+struct snd_soc_dai_ops;
+struct mtk_base_afe;
+struct mtk_base_afe_memif;
+
+int mtk_afe_fe_startup(struct snd_pcm_substream *substream,
+                      struct snd_soc_dai *dai);
+void mtk_afe_fe_shutdown(struct snd_pcm_substream *substream,
+                        struct snd_soc_dai *dai);
+int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream,
+                        struct snd_pcm_hw_params *params,
+                        struct snd_soc_dai *dai);
+int mtk_afe_fe_hw_free(struct snd_pcm_substream *substream,
+                      struct snd_soc_dai *dai);
+int mtk_afe_fe_prepare(struct snd_pcm_substream *substream,
+                      struct snd_soc_dai *dai);
+int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+                      struct snd_soc_dai *dai);
+
+extern const struct snd_soc_dai_ops mtk_afe_fe_ops;
+
+int mtk_dynamic_irq_acquire(struct mtk_base_afe *afe);
+int mtk_dynamic_irq_release(struct mtk_base_afe *afe, int irq_id);
+int mtk_afe_dai_suspend(struct snd_soc_dai *dai);
+int mtk_afe_dai_resume(struct snd_soc_dai *dai);
+
+#endif
diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.c b/sound/soc/mediatek/common/mtk-afe-platform-driver.c
new file mode 100644 (file)
index 0000000..82d439c
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * mtk-afe-platform-driver.c  --  Mediatek afe platform driver
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <sound/soc.h>
+
+#include "mtk-afe-platform-driver.h"
+#include "mtk-base-afe.h"
+
+static snd_pcm_uframes_t mtk_afe_pcm_pointer
+                        (struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+       const struct mtk_base_memif_data *memif_data = memif->data;
+       struct regmap *regmap = afe->regmap;
+       struct device *dev = afe->dev;
+       int reg_ofs_base = memif_data->reg_ofs_base;
+       int reg_ofs_cur = memif_data->reg_ofs_cur;
+       unsigned int hw_ptr = 0, hw_base = 0;
+       int ret, pcm_ptr_bytes;
+
+       ret = regmap_read(regmap, reg_ofs_cur, &hw_ptr);
+       if (ret || hw_ptr == 0) {
+               dev_err(dev, "%s hw_ptr err\n", __func__);
+               pcm_ptr_bytes = 0;
+               goto POINTER_RETURN_FRAMES;
+       }
+
+       ret = regmap_read(regmap, reg_ofs_base, &hw_base);
+       if (ret || hw_base == 0) {
+               dev_err(dev, "%s hw_ptr err\n", __func__);
+               pcm_ptr_bytes = 0;
+               goto POINTER_RETURN_FRAMES;
+       }
+
+       pcm_ptr_bytes = hw_ptr - hw_base;
+
+POINTER_RETURN_FRAMES:
+       return bytes_to_frames(substream->runtime, pcm_ptr_bytes);
+}
+
+static const struct snd_pcm_ops mtk_afe_pcm_ops = {
+       .ioctl = snd_pcm_lib_ioctl,
+       .pointer = mtk_afe_pcm_pointer,
+};
+
+static int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+       size_t size;
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_pcm *pcm = rtd->pcm;
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+
+       size = afe->mtk_afe_hardware->buffer_bytes_max;
+       return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+                                                    card->dev, size, size);
+}
+
+static void mtk_afe_pcm_free(struct snd_pcm *pcm)
+{
+       snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+const struct snd_soc_platform_driver mtk_afe_pcm_platform = {
+       .ops = &mtk_afe_pcm_ops,
+       .pcm_new = mtk_afe_pcm_new,
+       .pcm_free = mtk_afe_pcm_free,
+};
+EXPORT_SYMBOL_GPL(mtk_afe_pcm_platform);
+
+MODULE_DESCRIPTION("Mediatek simple platform driver");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+
diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.h b/sound/soc/mediatek/common/mtk-afe-platform-driver.h
new file mode 100644 (file)
index 0000000..a973fc9
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * mtk-afe-platform-driver.h  --  Mediatek afe platform driver definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_AFE_PLATFORM_DRIVER_H_
+#define _MTK_AFE_PLATFORM_DRIVER_H_
+
+extern const struct snd_soc_platform_driver mtk_afe_pcm_platform;
+
+#endif
+
diff --git a/sound/soc/mediatek/common/mtk-base-afe.h b/sound/soc/mediatek/common/mtk-base-afe.h
new file mode 100644 (file)
index 0000000..3a78f6f
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * mtk-base-afe.h  --  Mediatek base afe structure
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_BASE_AFE_H_
+#define _MTK_BASE_AFE_H_
+
+struct mtk_base_memif_data {
+       int id;
+       const char *name;
+       int reg_ofs_base;
+       int reg_ofs_cur;
+       int fs_reg;
+       int fs_shift;
+       int fs_maskbit;
+       int mono_reg;
+       int mono_shift;
+       int enable_reg;
+       int enable_shift;
+       int hd_reg;
+       int hd_shift;
+       int msb_reg;
+       int msb_shift;
+       int agent_disable_reg;
+       int agent_disable_shift;
+};
+
+struct mtk_base_irq_data {
+       int id;
+       int irq_cnt_reg;
+       int irq_cnt_shift;
+       int irq_cnt_maskbit;
+       int irq_fs_reg;
+       int irq_fs_shift;
+       int irq_fs_maskbit;
+       int irq_en_reg;
+       int irq_en_shift;
+       int irq_clr_reg;
+       int irq_clr_shift;
+};
+
+struct device;
+struct mtk_base_afe_memif;
+struct mtk_base_afe_irq;
+struct regmap;
+struct snd_pcm_substream;
+struct snd_soc_dai;
+
+struct mtk_base_afe {
+       void __iomem *base_addr;
+       struct device *dev;
+       struct regmap *regmap;
+       struct mutex irq_alloc_lock; /* dynamic alloc irq lock */
+
+       unsigned int const *reg_back_up_list;
+       unsigned int *reg_back_up;
+       unsigned int reg_back_up_list_num;
+
+       int (*runtime_suspend)(struct device *dev);
+       int (*runtime_resume)(struct device *dev);
+       bool suspended;
+
+       struct mtk_base_afe_memif *memif;
+       int memif_size;
+       struct mtk_base_afe_irq *irqs;
+       int irqs_size;
+
+       const struct snd_pcm_hardware *mtk_afe_hardware;
+       int (*memif_fs)(struct snd_pcm_substream *substream,
+                       unsigned int rate);
+       int (*irq_fs)(struct snd_pcm_substream *substream,
+                     unsigned int rate);
+
+       void *platform_priv;
+};
+
+struct mtk_base_afe_memif {
+       unsigned int phys_buf_addr;
+       int buffer_size;
+       struct snd_pcm_substream *substream;
+       const struct mtk_base_memif_data *data;
+       int irq_usage;
+       int const_irq;
+};
+
+struct mtk_base_afe_irq {
+       const struct mtk_base_irq_data *irq_data;
+       int irq_occupyed;
+};
+
+#endif
+
diff --git a/sound/soc/mediatek/mt2701/Makefile b/sound/soc/mediatek/mt2701/Makefile
new file mode 100644 (file)
index 0000000..31c3d04
--- /dev/null
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2015 MediaTek Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# 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.
+#
+
+# platform driver
+snd-soc-mt2701-afe-objs := mt2701-afe-pcm.o mt2701-afe-clock-ctrl.o
+obj-$(CONFIG_SND_SOC_MT2701) += snd-soc-mt2701-afe.o
+
+# machine driver
+obj-$(CONFIG_SND_SOC_MT2701_CS42448) += mt2701-cs42448.o
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
new file mode 100644 (file)
index 0000000..b815ecc
--- /dev/null
@@ -0,0 +1,464 @@
+/*
+ * mt2701-afe-clock-ctrl.c  --  Mediatek 2701 afe clock ctrl
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <sound/soc.h>
+#include <linux/regmap.h>
+#include <linux/pm_runtime.h>
+
+#include "mt2701-afe-common.h"
+#include "mt2701-afe-clock-ctrl.h"
+
+static const char *aud_clks[MT2701_CLOCK_NUM] = {
+       [MT2701_AUD_INFRA_SYS_AUDIO] = "infra_sys_audio_clk",
+       [MT2701_AUD_AUD_MUX1_SEL] = "top_audio_mux1_sel",
+       [MT2701_AUD_AUD_MUX2_SEL] = "top_audio_mux2_sel",
+       [MT2701_AUD_AUD_MUX1_DIV] = "top_audio_mux1_div",
+       [MT2701_AUD_AUD_MUX2_DIV] = "top_audio_mux2_div",
+       [MT2701_AUD_AUD_48K_TIMING] = "top_audio_48k_timing",
+       [MT2701_AUD_AUD_44K_TIMING] = "top_audio_44k_timing",
+       [MT2701_AUD_AUDPLL_MUX_SEL] = "top_audpll_mux_sel",
+       [MT2701_AUD_APLL_SEL] = "top_apll_sel",
+       [MT2701_AUD_AUD1PLL_98M] = "top_aud1_pll_98M",
+       [MT2701_AUD_AUD2PLL_90M] = "top_aud2_pll_90M",
+       [MT2701_AUD_HADDS2PLL_98M] = "top_hadds2_pll_98M",
+       [MT2701_AUD_HADDS2PLL_294M] = "top_hadds2_pll_294M",
+       [MT2701_AUD_AUDPLL] = "top_audpll",
+       [MT2701_AUD_AUDPLL_D4] = "top_audpll_d4",
+       [MT2701_AUD_AUDPLL_D8] = "top_audpll_d8",
+       [MT2701_AUD_AUDPLL_D16] = "top_audpll_d16",
+       [MT2701_AUD_AUDPLL_D24] = "top_audpll_d24",
+       [MT2701_AUD_AUDINTBUS] = "top_audintbus_sel",
+       [MT2701_AUD_CLK_26M] = "clk_26m",
+       [MT2701_AUD_SYSPLL1_D4] = "top_syspll1_d4",
+       [MT2701_AUD_AUD_K1_SRC_SEL] = "top_aud_k1_src_sel",
+       [MT2701_AUD_AUD_K2_SRC_SEL] = "top_aud_k2_src_sel",
+       [MT2701_AUD_AUD_K3_SRC_SEL] = "top_aud_k3_src_sel",
+       [MT2701_AUD_AUD_K4_SRC_SEL] = "top_aud_k4_src_sel",
+       [MT2701_AUD_AUD_K5_SRC_SEL] = "top_aud_k5_src_sel",
+       [MT2701_AUD_AUD_K6_SRC_SEL] = "top_aud_k6_src_sel",
+       [MT2701_AUD_AUD_K1_SRC_DIV] = "top_aud_k1_src_div",
+       [MT2701_AUD_AUD_K2_SRC_DIV] = "top_aud_k2_src_div",
+       [MT2701_AUD_AUD_K3_SRC_DIV] = "top_aud_k3_src_div",
+       [MT2701_AUD_AUD_K4_SRC_DIV] = "top_aud_k4_src_div",
+       [MT2701_AUD_AUD_K5_SRC_DIV] = "top_aud_k5_src_div",
+       [MT2701_AUD_AUD_K6_SRC_DIV] = "top_aud_k6_src_div",
+       [MT2701_AUD_AUD_I2S1_MCLK] = "top_aud_i2s1_mclk",
+       [MT2701_AUD_AUD_I2S2_MCLK] = "top_aud_i2s2_mclk",
+       [MT2701_AUD_AUD_I2S3_MCLK] = "top_aud_i2s3_mclk",
+       [MT2701_AUD_AUD_I2S4_MCLK] = "top_aud_i2s4_mclk",
+       [MT2701_AUD_AUD_I2S5_MCLK] = "top_aud_i2s5_mclk",
+       [MT2701_AUD_AUD_I2S6_MCLK] = "top_aud_i2s6_mclk",
+       [MT2701_AUD_ASM_M_SEL] = "top_asm_m_sel",
+       [MT2701_AUD_ASM_H_SEL] = "top_asm_h_sel",
+       [MT2701_AUD_UNIVPLL2_D4] = "top_univpll2_d4",
+       [MT2701_AUD_UNIVPLL2_D2] = "top_univpll2_d2",
+       [MT2701_AUD_SYSPLL_D5] = "top_syspll_d5",
+};
+
+int mt2701_init_clock(struct mtk_base_afe *afe)
+{
+       struct mt2701_afe_private *afe_priv = afe->platform_priv;
+       int i = 0;
+
+       for (i = 0; i < MT2701_CLOCK_NUM; i++) {
+               afe_priv->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
+               if (IS_ERR(aud_clks[i])) {
+                       dev_warn(afe->dev, "%s devm_clk_get %s fail\n",
+                                __func__, aud_clks[i]);
+                       return PTR_ERR(aud_clks[i]);
+               }
+       }
+
+       return 0;
+}
+
+int mt2701_afe_enable_clock(struct mtk_base_afe *afe)
+{
+       int ret = 0;
+
+       ret = mt2701_turn_on_a1sys_clock(afe);
+       if (ret) {
+               dev_err(afe->dev, "%s turn_on_a1sys_clock fail %d\n",
+                       __func__, ret);
+               return ret;
+       }
+
+       ret = mt2701_turn_on_a2sys_clock(afe);
+       if (ret) {
+               dev_err(afe->dev, "%s turn_on_a2sys_clock fail %d\n",
+                       __func__, ret);
+               mt2701_turn_off_a1sys_clock(afe);
+               return ret;
+       }
+
+       ret = mt2701_turn_on_afe_clock(afe);
+       if (ret) {
+               dev_err(afe->dev, "%s turn_on_afe_clock fail %d\n",
+                       __func__, ret);
+               mt2701_turn_off_a1sys_clock(afe);
+               mt2701_turn_off_a2sys_clock(afe);
+               return ret;
+       }
+
+       regmap_update_bits(afe->regmap, ASYS_TOP_CON,
+                          AUDIO_TOP_CON0_A1SYS_A2SYS_ON,
+                          AUDIO_TOP_CON0_A1SYS_A2SYS_ON);
+       regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+                          AFE_DAC_CON0_AFE_ON,
+                          AFE_DAC_CON0_AFE_ON);
+       regmap_write(afe->regmap, PWR2_TOP_CON,
+                    PWR2_TOP_CON_INIT_VAL);
+       regmap_write(afe->regmap, PWR1_ASM_CON1,
+                    PWR1_ASM_CON1_INIT_VAL);
+       regmap_write(afe->regmap, PWR2_ASM_CON1,
+                    PWR2_ASM_CON1_INIT_VAL);
+
+       return 0;
+}
+
+void mt2701_afe_disable_clock(struct mtk_base_afe *afe)
+{
+       mt2701_turn_off_afe_clock(afe);
+       mt2701_turn_off_a1sys_clock(afe);
+       mt2701_turn_off_a2sys_clock(afe);
+       regmap_update_bits(afe->regmap, ASYS_TOP_CON,
+                          AUDIO_TOP_CON0_A1SYS_A2SYS_ON, 0);
+       regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+                          AFE_DAC_CON0_AFE_ON, 0);
+}
+
+int mt2701_turn_on_a1sys_clock(struct mtk_base_afe *afe)
+{
+       struct mt2701_afe_private *afe_priv = afe->platform_priv;
+       int ret = 0;
+
+       /* Set Mux */
+       ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+       if (ret) {
+               dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+                       __func__, aud_clks[MT2701_AUD_AUD_MUX1_SEL], ret);
+               goto A1SYS_CLK_AUD_MUX1_SEL_ERR;
+       }
+
+       ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL],
+                            afe_priv->clocks[MT2701_AUD_AUD1PLL_98M]);
+       if (ret) {
+               dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+                       aud_clks[MT2701_AUD_AUD_MUX1_SEL],
+                       aud_clks[MT2701_AUD_AUD1PLL_98M], ret);
+               goto A1SYS_CLK_AUD_MUX1_SEL_ERR;
+       }
+
+       /* Set Divider */
+       ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]);
+       if (ret) {
+               dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+                       __func__,
+                       aud_clks[MT2701_AUD_AUD_MUX1_DIV],
+                       ret);
+               goto A1SYS_CLK_AUD_MUX1_DIV_ERR;
+       }
+
+       ret = clk_set_rate(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV],
+                          MT2701_AUD_AUD_MUX1_DIV_RATE);
+       if (ret) {
+               dev_err(afe->dev, "%s clk_set_parent %s-%d fail %d\n", __func__,
+                       aud_clks[MT2701_AUD_AUD_MUX1_DIV],
+                       MT2701_AUD_AUD_MUX1_DIV_RATE, ret);
+               goto A1SYS_CLK_AUD_MUX1_DIV_ERR;
+       }
+
+       /* Enable clock gate */
+       ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]);
+       if (ret) {
+               dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+                       __func__, aud_clks[MT2701_AUD_AUD_48K_TIMING], ret);
+               goto A1SYS_CLK_AUD_48K_ERR;
+       }
+
+       /* Enable infra audio */
+       ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+       if (ret) {
+               dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+                       __func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret);
+               goto A1SYS_CLK_INFRA_ERR;
+       }
+
+       return 0;
+
+A1SYS_CLK_INFRA_ERR:
+       clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+A1SYS_CLK_AUD_48K_ERR:
+       clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]);
+A1SYS_CLK_AUD_MUX1_DIV_ERR:
+       clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]);
+A1SYS_CLK_AUD_MUX1_SEL_ERR:
+       clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+
+       return ret;
+}
+
+void mt2701_turn_off_a1sys_clock(struct mtk_base_afe *afe)
+{
+       struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+       clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+       clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]);
+       clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]);
+       clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+}
+
+int mt2701_turn_on_a2sys_clock(struct mtk_base_afe *afe)
+{
+       struct mt2701_afe_private *afe_priv = afe->platform_priv;
+       int ret = 0;
+
+       /* Set Mux */
+       ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+       if (ret) {
+               dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+                       __func__, aud_clks[MT2701_AUD_AUD_MUX2_SEL], ret);
+               goto A2SYS_CLK_AUD_MUX2_SEL_ERR;
+       }
+
+       ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL],
+                            afe_priv->clocks[MT2701_AUD_AUD2PLL_90M]);
+       if (ret) {
+               dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+                       aud_clks[MT2701_AUD_AUD_MUX2_SEL],
+                       aud_clks[MT2701_AUD_AUD2PLL_90M], ret);
+               goto A2SYS_CLK_AUD_MUX2_SEL_ERR;
+       }
+
+       /* Set Divider */
+       ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]);
+       if (ret) {
+               dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+                       __func__, aud_clks[MT2701_AUD_AUD_MUX2_DIV], ret);
+               goto A2SYS_CLK_AUD_MUX2_DIV_ERR;
+       }
+
+       ret = clk_set_rate(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV],
+                          MT2701_AUD_AUD_MUX2_DIV_RATE);
+       if (ret) {
+               dev_err(afe->dev, "%s clk_set_parent %s-%d fail %d\n", __func__,
+                       aud_clks[MT2701_AUD_AUD_MUX2_DIV],
+                       MT2701_AUD_AUD_MUX2_DIV_RATE, ret);
+               goto A2SYS_CLK_AUD_MUX2_DIV_ERR;
+       }
+
+       /* Enable clock gate */
+       ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]);
+       if (ret) {
+               dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+                       __func__, aud_clks[MT2701_AUD_AUD_44K_TIMING], ret);
+               goto A2SYS_CLK_AUD_44K_ERR;
+       }
+
+       /* Enable infra audio */
+       ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+       if (ret) {
+               dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+                       __func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret);
+               goto A2SYS_CLK_INFRA_ERR;
+       }
+
+       return 0;
+
+A2SYS_CLK_INFRA_ERR:
+       clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+A2SYS_CLK_AUD_44K_ERR:
+       clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]);
+A2SYS_CLK_AUD_MUX2_DIV_ERR:
+       clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]);
+A2SYS_CLK_AUD_MUX2_SEL_ERR:
+       clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+
+       return ret;
+}
+
+void mt2701_turn_off_a2sys_clock(struct mtk_base_afe *afe)
+{
+       struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+       clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+       clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]);
+       clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]);
+       clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+}
+
+int mt2701_turn_on_afe_clock(struct mtk_base_afe *afe)
+{
+       struct mt2701_afe_private *afe_priv = afe->platform_priv;
+       int ret;
+
+       /* enable INFRA_SYS */
+       ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+       if (ret) {
+               dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+                       __func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret);
+               goto AFE_AUD_INFRA_ERR;
+       }
+
+       /* Set MT2701_AUD_AUDINTBUS to MT2701_AUD_SYSPLL1_D4 */
+       ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUDINTBUS]);
+       if (ret) {
+               dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+                       __func__, aud_clks[MT2701_AUD_AUDINTBUS], ret);
+               goto AFE_AUD_AUDINTBUS_ERR;
+       }
+
+       ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUDINTBUS],
+                            afe_priv->clocks[MT2701_AUD_SYSPLL1_D4]);
+       if (ret) {
+               dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+                       aud_clks[MT2701_AUD_AUDINTBUS],
+                       aud_clks[MT2701_AUD_SYSPLL1_D4], ret);
+               goto AFE_AUD_AUDINTBUS_ERR;
+       }
+
+       /* Set MT2701_AUD_ASM_H_SEL to MT2701_AUD_UNIVPLL2_D2 */
+       ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]);
+       if (ret) {
+               dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+                       __func__, aud_clks[MT2701_AUD_ASM_H_SEL], ret);
+               goto AFE_AUD_ASM_H_ERR;
+       }
+
+       ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_ASM_H_SEL],
+                            afe_priv->clocks[MT2701_AUD_UNIVPLL2_D2]);
+       if (ret) {
+               dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+                       aud_clks[MT2701_AUD_ASM_H_SEL],
+                       aud_clks[MT2701_AUD_UNIVPLL2_D2], ret);
+               goto AFE_AUD_ASM_H_ERR;
+       }
+
+       /* Set MT2701_AUD_ASM_M_SEL to MT2701_AUD_UNIVPLL2_D4 */
+       ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]);
+       if (ret) {
+               dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+                       __func__, aud_clks[MT2701_AUD_ASM_M_SEL], ret);
+               goto AFE_AUD_ASM_M_ERR;
+       }
+
+       ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_ASM_M_SEL],
+                            afe_priv->clocks[MT2701_AUD_UNIVPLL2_D4]);
+       if (ret) {
+               dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+                       aud_clks[MT2701_AUD_ASM_M_SEL],
+                       aud_clks[MT2701_AUD_UNIVPLL2_D4], ret);
+               goto AFE_AUD_ASM_M_ERR;
+       }
+
+       regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+                          AUDIO_TOP_CON0_PDN_AFE, 0);
+       regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+                          AUDIO_TOP_CON0_PDN_APLL_CK, 0);
+       regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+                          AUDIO_TOP_CON4_PDN_A1SYS, 0);
+       regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+                          AUDIO_TOP_CON4_PDN_A2SYS, 0);
+       regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+                          AUDIO_TOP_CON4_PDN_AFE_CONN, 0);
+
+       return 0;
+
+AFE_AUD_ASM_M_ERR:
+       clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]);
+AFE_AUD_ASM_H_ERR:
+       clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]);
+AFE_AUD_AUDINTBUS_ERR:
+       clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUDINTBUS]);
+AFE_AUD_INFRA_ERR:
+       clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+
+       return ret;
+}
+
+void mt2701_turn_off_afe_clock(struct mtk_base_afe *afe)
+{
+       struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+       clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+
+       clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUDINTBUS]);
+       clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]);
+       clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]);
+
+       regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+                          AUDIO_TOP_CON0_PDN_AFE, AUDIO_TOP_CON0_PDN_AFE);
+       regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+                          AUDIO_TOP_CON0_PDN_APLL_CK,
+                          AUDIO_TOP_CON0_PDN_APLL_CK);
+       regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+                          AUDIO_TOP_CON4_PDN_A1SYS,
+                          AUDIO_TOP_CON4_PDN_A1SYS);
+       regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+                          AUDIO_TOP_CON4_PDN_A2SYS,
+                          AUDIO_TOP_CON4_PDN_A2SYS);
+       regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+                          AUDIO_TOP_CON4_PDN_AFE_CONN,
+                          AUDIO_TOP_CON4_PDN_AFE_CONN);
+}
+
+void mt2701_mclk_configuration(struct mtk_base_afe *afe, int id, int domain,
+                              int mclk)
+{
+       struct mt2701_afe_private *afe_priv = afe->platform_priv;
+       int ret;
+       int aud_src_div_id = MT2701_AUD_AUD_K1_SRC_DIV + id;
+       int aud_src_clk_id = MT2701_AUD_AUD_K1_SRC_SEL + id;
+
+       /* Set MCLK Kx_SRC_SEL(domain) */
+       ret = clk_prepare_enable(afe_priv->clocks[aud_src_clk_id]);
+       if (ret)
+               dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+                       __func__, aud_clks[aud_src_clk_id], ret);
+
+       if (domain == 0) {
+               ret = clk_set_parent(afe_priv->clocks[aud_src_clk_id],
+                                    afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+               if (ret)
+                       dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+                               __func__, aud_clks[aud_src_clk_id],
+                               aud_clks[MT2701_AUD_AUD_MUX1_SEL], ret);
+       } else {
+               ret = clk_set_parent(afe_priv->clocks[aud_src_clk_id],
+                                    afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+               if (ret)
+                       dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+                               __func__, aud_clks[aud_src_clk_id],
+                               aud_clks[MT2701_AUD_AUD_MUX2_SEL], ret);
+       }
+       clk_disable_unprepare(afe_priv->clocks[aud_src_clk_id]);
+
+       /* Set MCLK Kx_SRC_DIV(divider) */
+       ret = clk_prepare_enable(afe_priv->clocks[aud_src_div_id]);
+       if (ret)
+               dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+                       __func__, aud_clks[aud_src_div_id], ret);
+
+       ret = clk_set_rate(afe_priv->clocks[aud_src_div_id], mclk);
+       if (ret)
+               dev_err(afe->dev, "%s clk_set_rate %s-%d fail %d\n", __func__,
+                       aud_clks[aud_src_div_id], mclk, ret);
+       clk_disable_unprepare(afe_priv->clocks[aud_src_div_id]);
+}
+
+MODULE_DESCRIPTION("MT2701 afe clock control");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng@mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h
new file mode 100644 (file)
index 0000000..6497d57
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * mt2701-afe-clock-ctrl.h  --  Mediatek 2701 afe clock ctrl definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT2701_AFE_CLOCK_CTRL_H_
+#define _MT2701_AFE_CLOCK_CTRL_H_
+
+struct mtk_base_afe;
+
+int mt2701_init_clock(struct mtk_base_afe *afe);
+int mt2701_afe_enable_clock(struct mtk_base_afe *afe);
+void mt2701_afe_disable_clock(struct mtk_base_afe *afe);
+
+int mt2701_turn_on_a1sys_clock(struct mtk_base_afe *afe);
+void mt2701_turn_off_a1sys_clock(struct mtk_base_afe *afe);
+
+int mt2701_turn_on_a2sys_clock(struct mtk_base_afe *afe);
+void mt2701_turn_off_a2sys_clock(struct mtk_base_afe *afe);
+
+int mt2701_turn_on_afe_clock(struct mtk_base_afe *afe);
+void mt2701_turn_off_afe_clock(struct mtk_base_afe *afe);
+
+void mt2701_mclk_configuration(struct mtk_base_afe *afe, int id, int domain,
+                              int mclk);
+
+#endif
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-common.h b/sound/soc/mediatek/mt2701/mt2701-afe-common.h
new file mode 100644 (file)
index 0000000..c19430e
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * mt2701-afe-common.h  --  Mediatek 2701 audio driver definitions
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT_2701_AFE_COMMON_H_
+#define _MT_2701_AFE_COMMON_H_
+#include <sound/soc.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include "mt2701-reg.h"
+#include "../common/mtk-base-afe.h"
+
+#define MT2701_STREAM_DIR_NUM (SNDRV_PCM_STREAM_LAST + 1)
+#define MT2701_PLL_DOMAIN_0_RATE       98304000
+#define MT2701_PLL_DOMAIN_1_RATE       90316800
+#define MT2701_AUD_AUD_MUX1_DIV_RATE (MT2701_PLL_DOMAIN_0_RATE / 2)
+#define MT2701_AUD_AUD_MUX2_DIV_RATE (MT2701_PLL_DOMAIN_1_RATE / 2)
+
+enum {
+       MT2701_I2S_1,
+       MT2701_I2S_2,
+       MT2701_I2S_3,
+       MT2701_I2S_4,
+       MT2701_I2S_NUM,
+};
+
+enum {
+       MT2701_MEMIF_DL1,
+       MT2701_MEMIF_DL2,
+       MT2701_MEMIF_DL3,
+       MT2701_MEMIF_DL4,
+       MT2701_MEMIF_DL5,
+       MT2701_MEMIF_DL_SINGLE_NUM,
+       MT2701_MEMIF_DLM = MT2701_MEMIF_DL_SINGLE_NUM,
+       MT2701_MEMIF_UL1,
+       MT2701_MEMIF_UL2,
+       MT2701_MEMIF_UL3,
+       MT2701_MEMIF_UL4,
+       MT2701_MEMIF_UL5,
+       MT2701_MEMIF_DLBT,
+       MT2701_MEMIF_ULBT,
+       MT2701_MEMIF_NUM,
+       MT2701_IO_I2S = MT2701_MEMIF_NUM,
+       MT2701_IO_2ND_I2S,
+       MT2701_IO_3RD_I2S,
+       MT2701_IO_4TH_I2S,
+       MT2701_IO_5TH_I2S,
+       MT2701_IO_6TH_I2S,
+       MT2701_IO_MRG,
+};
+
+enum {
+       MT2701_IRQ_ASYS_START,
+       MT2701_IRQ_ASYS_IRQ1 = MT2701_IRQ_ASYS_START,
+       MT2701_IRQ_ASYS_IRQ2,
+       MT2701_IRQ_ASYS_IRQ3,
+       MT2701_IRQ_ASYS_END,
+};
+
+/* 2701 clock def */
+enum audio_system_clock_type {
+       MT2701_AUD_INFRA_SYS_AUDIO,
+       MT2701_AUD_AUD_MUX1_SEL,
+       MT2701_AUD_AUD_MUX2_SEL,
+       MT2701_AUD_AUD_MUX1_DIV,
+       MT2701_AUD_AUD_MUX2_DIV,
+       MT2701_AUD_AUD_48K_TIMING,
+       MT2701_AUD_AUD_44K_TIMING,
+       MT2701_AUD_AUDPLL_MUX_SEL,
+       MT2701_AUD_APLL_SEL,
+       MT2701_AUD_AUD1PLL_98M,
+       MT2701_AUD_AUD2PLL_90M,
+       MT2701_AUD_HADDS2PLL_98M,
+       MT2701_AUD_HADDS2PLL_294M,
+       MT2701_AUD_AUDPLL,
+       MT2701_AUD_AUDPLL_D4,
+       MT2701_AUD_AUDPLL_D8,
+       MT2701_AUD_AUDPLL_D16,
+       MT2701_AUD_AUDPLL_D24,
+       MT2701_AUD_AUDINTBUS,
+       MT2701_AUD_CLK_26M,
+       MT2701_AUD_SYSPLL1_D4,
+       MT2701_AUD_AUD_K1_SRC_SEL,
+       MT2701_AUD_AUD_K2_SRC_SEL,
+       MT2701_AUD_AUD_K3_SRC_SEL,
+       MT2701_AUD_AUD_K4_SRC_SEL,
+       MT2701_AUD_AUD_K5_SRC_SEL,
+       MT2701_AUD_AUD_K6_SRC_SEL,
+       MT2701_AUD_AUD_K1_SRC_DIV,
+       MT2701_AUD_AUD_K2_SRC_DIV,
+       MT2701_AUD_AUD_K3_SRC_DIV,
+       MT2701_AUD_AUD_K4_SRC_DIV,
+       MT2701_AUD_AUD_K5_SRC_DIV,
+       MT2701_AUD_AUD_K6_SRC_DIV,
+       MT2701_AUD_AUD_I2S1_MCLK,
+       MT2701_AUD_AUD_I2S2_MCLK,
+       MT2701_AUD_AUD_I2S3_MCLK,
+       MT2701_AUD_AUD_I2S4_MCLK,
+       MT2701_AUD_AUD_I2S5_MCLK,
+       MT2701_AUD_AUD_I2S6_MCLK,
+       MT2701_AUD_ASM_M_SEL,
+       MT2701_AUD_ASM_H_SEL,
+       MT2701_AUD_UNIVPLL2_D4,
+       MT2701_AUD_UNIVPLL2_D2,
+       MT2701_AUD_SYSPLL_D5,
+       MT2701_CLOCK_NUM
+};
+
+static const unsigned int mt2701_afe_backup_list[] = {
+       AUDIO_TOP_CON0,
+       AUDIO_TOP_CON4,
+       AUDIO_TOP_CON5,
+       ASYS_TOP_CON,
+       AFE_CONN0,
+       AFE_CONN1,
+       AFE_CONN2,
+       AFE_CONN3,
+       AFE_CONN15,
+       AFE_CONN16,
+       AFE_CONN17,
+       AFE_CONN18,
+       AFE_CONN19,
+       AFE_CONN20,
+       AFE_CONN21,
+       AFE_CONN22,
+       AFE_DAC_CON0,
+       AFE_MEMIF_PBUF_SIZE,
+};
+
+struct snd_pcm_substream;
+struct mtk_base_irq_data;
+
+struct mt2701_i2s_data {
+       int i2s_ctrl_reg;
+       int i2s_pwn_shift;
+       int i2s_asrc_fs_shift;
+       int i2s_asrc_fs_mask;
+};
+
+enum mt2701_i2s_dir {
+       I2S_OUT,
+       I2S_IN,
+       I2S_DIR_NUM,
+};
+
+struct mt2701_i2s_path {
+       int dai_id;
+       int mclk_rate;
+       int on[I2S_DIR_NUM];
+       int occupied[I2S_DIR_NUM];
+       const struct mt2701_i2s_data *i2s_data[2];
+};
+
+struct mt2701_afe_private {
+       struct clk *clocks[MT2701_CLOCK_NUM];
+       struct mt2701_i2s_path i2s_path[MT2701_I2S_NUM];
+       bool mrg_enable[MT2701_STREAM_DIR_NUM];
+};
+
+#endif
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
new file mode 100644 (file)
index 0000000..34a6123
--- /dev/null
@@ -0,0 +1,1656 @@
+/*
+ * Mediatek ALSA SoC AFE platform driver for 2701
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@mediatek.com>
+ *             Ir Lian <ir.lian@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+
+#include "mt2701-afe-common.h"
+
+#include "mt2701-afe-clock-ctrl.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-afe-fe-dai.h"
+
+#define AFE_IRQ_STATUS_BITS    0xff
+
+static const struct snd_pcm_hardware mt2701_afe_hardware = {
+       .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED
+               | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID,
+       .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE
+                  | SNDRV_PCM_FMTBIT_S32_LE,
+       .period_bytes_min = 1024,
+       .period_bytes_max = 1024 * 256,
+       .periods_min = 4,
+       .periods_max = 1024,
+       .buffer_bytes_max = 1024 * 1024 * 16,
+       .fifo_size = 0,
+};
+
+struct mt2701_afe_rate {
+       unsigned int rate;
+       unsigned int regvalue;
+};
+
+static const struct mt2701_afe_rate mt2701_afe_i2s_rates[] = {
+       { .rate = 8000, .regvalue = 0 },
+       { .rate = 12000, .regvalue = 1 },
+       { .rate = 16000, .regvalue = 2 },
+       { .rate = 24000, .regvalue = 3 },
+       { .rate = 32000, .regvalue = 4 },
+       { .rate = 48000, .regvalue = 5 },
+       { .rate = 96000, .regvalue = 6 },
+       { .rate = 192000, .regvalue = 7 },
+       { .rate = 384000, .regvalue = 8 },
+       { .rate = 7350, .regvalue = 16 },
+       { .rate = 11025, .regvalue = 17 },
+       { .rate = 14700, .regvalue = 18 },
+       { .rate = 22050, .regvalue = 19 },
+       { .rate = 29400, .regvalue = 20 },
+       { .rate = 44100, .regvalue = 21 },
+       { .rate = 88200, .regvalue = 22 },
+       { .rate = 176400, .regvalue = 23 },
+       { .rate = 352800, .regvalue = 24 },
+};
+
+static int mt2701_dai_num_to_i2s(struct mtk_base_afe *afe, int num)
+{
+       int val = num - MT2701_IO_I2S;
+
+       if (val < 0 || val >= MT2701_I2S_NUM) {
+               dev_err(afe->dev, "%s, num not available, num %d, val %d\n",
+                       __func__, num, val);
+               return -EINVAL;
+       }
+       return val;
+}
+
+static int mt2701_afe_i2s_fs(unsigned int sample_rate)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(mt2701_afe_i2s_rates); i++)
+               if (mt2701_afe_i2s_rates[i].rate == sample_rate)
+                       return mt2701_afe_i2s_rates[i].regvalue;
+
+       return -EINVAL;
+}
+
+static int mt2701_afe_i2s_startup(struct snd_pcm_substream *substream,
+                                 struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mt2701_afe_private *afe_priv = afe->platform_priv;
+       int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+       int clk_num = MT2701_AUD_AUD_I2S1_MCLK + i2s_num;
+       int ret = 0;
+
+       if (i2s_num < 0)
+               return i2s_num;
+
+       /* enable mclk */
+       ret = clk_prepare_enable(afe_priv->clocks[clk_num]);
+       if (ret)
+               dev_err(afe->dev, "Failed to enable mclk for I2S: %d\n",
+                       i2s_num);
+
+       return ret;
+}
+
+static int mt2701_afe_i2s_path_shutdown(struct snd_pcm_substream *substream,
+                                       struct snd_soc_dai *dai,
+                                       int dir_invert)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mt2701_afe_private *afe_priv = afe->platform_priv;
+       int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+       struct mt2701_i2s_path *i2s_path;
+       const struct mt2701_i2s_data *i2s_data;
+       int stream_dir = substream->stream;
+
+       if (i2s_num < 0)
+               return i2s_num;
+
+       i2s_path = &afe_priv->i2s_path[i2s_num];
+
+       if (dir_invert) {
+               if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+                       stream_dir = SNDRV_PCM_STREAM_CAPTURE;
+               else
+                       stream_dir = SNDRV_PCM_STREAM_PLAYBACK;
+       }
+       i2s_data = i2s_path->i2s_data[stream_dir];
+
+       i2s_path->on[stream_dir]--;
+       if (i2s_path->on[stream_dir] < 0) {
+               dev_warn(afe->dev, "i2s_path->on: %d, dir: %d\n",
+                        i2s_path->on[stream_dir], stream_dir);
+               i2s_path->on[stream_dir] = 0;
+       }
+       if (i2s_path->on[stream_dir])
+               return 0;
+
+       /* disable i2s */
+       regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+                          ASYS_I2S_CON_I2S_EN, 0);
+       regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+                          1 << i2s_data->i2s_pwn_shift,
+                          1 << i2s_data->i2s_pwn_shift);
+       return 0;
+}
+
+static void mt2701_afe_i2s_shutdown(struct snd_pcm_substream *substream,
+                                   struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mt2701_afe_private *afe_priv = afe->platform_priv;
+       int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+       struct mt2701_i2s_path *i2s_path;
+       int clk_num = MT2701_AUD_AUD_I2S1_MCLK + i2s_num;
+
+       if (i2s_num < 0)
+               return;
+
+       i2s_path = &afe_priv->i2s_path[i2s_num];
+
+       if (i2s_path->occupied[substream->stream])
+               i2s_path->occupied[substream->stream] = 0;
+       else
+               goto I2S_UNSTART;
+
+       mt2701_afe_i2s_path_shutdown(substream, dai, 0);
+
+       /* need to disable i2s-out path when disable i2s-in */
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+               mt2701_afe_i2s_path_shutdown(substream, dai, 1);
+
+I2S_UNSTART:
+       /* disable mclk */
+       clk_disable_unprepare(afe_priv->clocks[clk_num]);
+}
+
+static int mt2701_i2s_path_prepare_enable(struct snd_pcm_substream *substream,
+                                         struct snd_soc_dai *dai,
+                                         int dir_invert)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mt2701_afe_private *afe_priv = afe->platform_priv;
+       int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+       struct mt2701_i2s_path *i2s_path;
+       const struct mt2701_i2s_data *i2s_data;
+       struct snd_pcm_runtime * const runtime = substream->runtime;
+       int reg, fs, w_len = 1; /* now we support bck 64bits only */
+       int stream_dir = substream->stream;
+       unsigned int mask = 0, val = 0;
+
+       if (i2s_num < 0)
+               return i2s_num;
+
+       i2s_path = &afe_priv->i2s_path[i2s_num];
+
+       if (dir_invert) {
+               if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+                       stream_dir = SNDRV_PCM_STREAM_CAPTURE;
+               else
+                       stream_dir = SNDRV_PCM_STREAM_PLAYBACK;
+       }
+       i2s_data = i2s_path->i2s_data[stream_dir];
+
+       /* no need to enable if already done */
+       i2s_path->on[stream_dir]++;
+
+       if (i2s_path->on[stream_dir] != 1)
+               return 0;
+
+       fs = mt2701_afe_i2s_fs(runtime->rate);
+
+       mask = ASYS_I2S_CON_FS |
+              ASYS_I2S_CON_I2S_COUPLE_MODE | /* 0 */
+              ASYS_I2S_CON_I2S_MODE |
+              ASYS_I2S_CON_WIDE_MODE;
+
+       val = ASYS_I2S_CON_FS_SET(fs) |
+             ASYS_I2S_CON_I2S_MODE |
+             ASYS_I2S_CON_WIDE_MODE_SET(w_len);
+
+       if (stream_dir == SNDRV_PCM_STREAM_CAPTURE) {
+               mask |= ASYS_I2S_IN_PHASE_FIX;
+               val |= ASYS_I2S_IN_PHASE_FIX;
+       }
+
+       regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg, mask, val);
+
+       if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+               reg = ASMO_TIMING_CON1;
+       else
+               reg = ASMI_TIMING_CON1;
+
+       regmap_update_bits(afe->regmap, reg,
+                          i2s_data->i2s_asrc_fs_mask
+                          << i2s_data->i2s_asrc_fs_shift,
+                          fs << i2s_data->i2s_asrc_fs_shift);
+
+       /* enable i2s */
+       regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+                          1 << i2s_data->i2s_pwn_shift,
+                          0 << i2s_data->i2s_pwn_shift);
+
+       /* reset i2s hw status before enable */
+       regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+                          ASYS_I2S_CON_RESET, ASYS_I2S_CON_RESET);
+       udelay(1);
+       regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+                          ASYS_I2S_CON_RESET, 0);
+       udelay(1);
+       regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+                          ASYS_I2S_CON_I2S_EN, ASYS_I2S_CON_I2S_EN);
+       return 0;
+}
+
+static int mt2701_afe_i2s_prepare(struct snd_pcm_substream *substream,
+                                 struct snd_soc_dai *dai)
+{
+       int clk_domain;
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mt2701_afe_private *afe_priv = afe->platform_priv;
+       int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+       struct mt2701_i2s_path *i2s_path;
+       int mclk_rate;
+
+       if (i2s_num < 0)
+               return i2s_num;
+
+       i2s_path = &afe_priv->i2s_path[i2s_num];
+       mclk_rate = i2s_path->mclk_rate;
+
+       if (i2s_path->occupied[substream->stream])
+               return -EBUSY;
+       i2s_path->occupied[substream->stream] = 1;
+
+       if (MT2701_PLL_DOMAIN_0_RATE % mclk_rate == 0) {
+               clk_domain = 0;
+       } else if (MT2701_PLL_DOMAIN_1_RATE % mclk_rate == 0) {
+               clk_domain = 1;
+       } else {
+               dev_err(dai->dev, "%s() bad mclk rate %d\n",
+                       __func__, mclk_rate);
+               return -EINVAL;
+       }
+       mt2701_mclk_configuration(afe, i2s_num, clk_domain, mclk_rate);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               mt2701_i2s_path_prepare_enable(substream, dai, 0);
+       } else {
+               /* need to enable i2s-out path when enable i2s-in */
+               /* prepare for another direction "out" */
+               mt2701_i2s_path_prepare_enable(substream, dai, 1);
+               /* prepare for "in" */
+               mt2701_i2s_path_prepare_enable(substream, dai, 0);
+       }
+
+       return 0;
+}
+
+static int mt2701_afe_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+                                    unsigned int freq, int dir)
+{
+       struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+       struct mt2701_afe_private *afe_priv = afe->platform_priv;
+       int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+
+       if (i2s_num < 0)
+               return i2s_num;
+
+       /* mclk */
+       if (dir == SND_SOC_CLOCK_IN) {
+               dev_warn(dai->dev,
+                        "%s() warning: mt2701 doesn't support mclk input\n",
+                       __func__);
+               return -EINVAL;
+       }
+       afe_priv->i2s_path[i2s_num].mclk_rate = freq;
+       return 0;
+}
+
+static int mt2701_btmrg_startup(struct snd_pcm_substream *substream,
+                               struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+       regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+                          AUDIO_TOP_CON4_PDN_MRGIF, 0);
+
+       afe_priv->mrg_enable[substream->stream] = 1;
+       return 0;
+}
+
+static int mt2701_btmrg_hw_params(struct snd_pcm_substream *substream,
+                                 struct snd_pcm_hw_params *params,
+                                 struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       int stream_fs;
+       u32 val, msk;
+
+       stream_fs = params_rate(params);
+
+       if ((stream_fs != 8000) && (stream_fs != 16000)) {
+               dev_err(afe->dev, "%s() btmgr not supprt this stream_fs %d\n",
+                       __func__, stream_fs);
+               return -EINVAL;
+       }
+
+       regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+                          AFE_MRGIF_CON_I2S_MODE_MASK,
+                          AFE_MRGIF_CON_I2S_MODE_32K);
+
+       val = AFE_DAIBT_CON0_BT_FUNC_EN | AFE_DAIBT_CON0_BT_FUNC_RDY
+             | AFE_DAIBT_CON0_MRG_USE;
+       msk = val;
+
+       if (stream_fs == 16000)
+               val |= AFE_DAIBT_CON0_BT_WIDE_MODE_EN;
+
+       msk |= AFE_DAIBT_CON0_BT_WIDE_MODE_EN;
+
+       regmap_update_bits(afe->regmap, AFE_DAIBT_CON0, msk, val);
+
+       regmap_update_bits(afe->regmap, AFE_DAIBT_CON0,
+                          AFE_DAIBT_CON0_DAIBT_EN,
+                          AFE_DAIBT_CON0_DAIBT_EN);
+       regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+                          AFE_MRGIF_CON_MRG_I2S_EN,
+                          AFE_MRGIF_CON_MRG_I2S_EN);
+       regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+                          AFE_MRGIF_CON_MRG_EN,
+                          AFE_MRGIF_CON_MRG_EN);
+       return 0;
+}
+
+static void mt2701_btmrg_shutdown(struct snd_pcm_substream *substream,
+                                 struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+       /* if the other direction stream is not occupied */
+       if (!afe_priv->mrg_enable[!substream->stream]) {
+               regmap_update_bits(afe->regmap, AFE_DAIBT_CON0,
+                                  AFE_DAIBT_CON0_DAIBT_EN, 0);
+               regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+                                  AFE_MRGIF_CON_MRG_EN, 0);
+               regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+                                  AFE_MRGIF_CON_MRG_I2S_EN, 0);
+               regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+                                  AUDIO_TOP_CON4_PDN_MRGIF,
+                                  AUDIO_TOP_CON4_PDN_MRGIF);
+       }
+       afe_priv->mrg_enable[substream->stream] = 0;
+}
+
+static int mt2701_simple_fe_startup(struct snd_pcm_substream *substream,
+                                   struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       int stream_dir = substream->stream;
+       int memif_num = rtd->cpu_dai->id;
+       struct mtk_base_afe_memif *memif_tmp;
+
+       /* can't run single DL & DLM at the same time */
+       if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) {
+               memif_tmp = &afe->memif[MT2701_MEMIF_DLM];
+               if (memif_tmp->substream) {
+                       dev_warn(afe->dev, "%s memif is not available, stream_dir %d, memif_num %d\n",
+                                __func__, stream_dir, memif_num);
+                       return -EBUSY;
+               }
+       }
+       return mtk_afe_fe_startup(substream, dai);
+}
+
+static int mt2701_simple_fe_hw_params(struct snd_pcm_substream *substream,
+                                     struct snd_pcm_hw_params *params,
+                                     struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       int stream_dir = substream->stream;
+
+       /* single DL use PAIR_INTERLEAVE */
+       if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) {
+               regmap_update_bits(afe->regmap,
+                                  AFE_MEMIF_PBUF_SIZE,
+                                  AFE_MEMIF_PBUF_SIZE_DLM_MASK,
+                                  AFE_MEMIF_PBUF_SIZE_PAIR_INTERLEAVE);
+       }
+       return mtk_afe_fe_hw_params(substream, params, dai);
+}
+
+static int mt2701_dlm_fe_startup(struct snd_pcm_substream *substream,
+                                struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mtk_base_afe_memif *memif_tmp;
+       const struct mtk_base_memif_data *memif_data;
+       int i;
+
+       for (i = MT2701_MEMIF_DL1; i < MT2701_MEMIF_DL_SINGLE_NUM; ++i) {
+               memif_tmp = &afe->memif[i];
+               if (memif_tmp->substream)
+                       return -EBUSY;
+       }
+
+       /* enable agent for all signal DL (due to hw design) */
+       for (i = MT2701_MEMIF_DL1; i < MT2701_MEMIF_DL_SINGLE_NUM; ++i) {
+               memif_data = afe->memif[i].data;
+               regmap_update_bits(afe->regmap,
+                                  memif_data->agent_disable_reg,
+                                  1 << memif_data->agent_disable_shift,
+                                  0 << memif_data->agent_disable_shift);
+       }
+
+       return mtk_afe_fe_startup(substream, dai);
+}
+
+static void mt2701_dlm_fe_shutdown(struct snd_pcm_substream *substream,
+                                  struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       const struct mtk_base_memif_data *memif_data;
+       int i;
+
+       for (i = MT2701_MEMIF_DL1; i < MT2701_MEMIF_DL_SINGLE_NUM; ++i) {
+               memif_data = afe->memif[i].data;
+               regmap_update_bits(afe->regmap,
+                                  memif_data->agent_disable_reg,
+                                  1 << memif_data->agent_disable_shift,
+                                  1 << memif_data->agent_disable_shift);
+       }
+       return mtk_afe_fe_shutdown(substream, dai);
+}
+
+static int mt2701_dlm_fe_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params,
+                                  struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       int channels = params_channels(params);
+
+       regmap_update_bits(afe->regmap,
+                          AFE_MEMIF_PBUF_SIZE,
+                          AFE_MEMIF_PBUF_SIZE_DLM_MASK,
+                          AFE_MEMIF_PBUF_SIZE_FULL_INTERLEAVE);
+       regmap_update_bits(afe->regmap,
+                          AFE_MEMIF_PBUF_SIZE,
+                          AFE_MEMIF_PBUF_SIZE_DLM_BYTE_MASK,
+                          AFE_MEMIF_PBUF_SIZE_DLM_32BYTES);
+       regmap_update_bits(afe->regmap,
+                          AFE_MEMIF_PBUF_SIZE,
+                          AFE_MEMIF_PBUF_SIZE_DLM_CH_MASK,
+                          AFE_MEMIF_PBUF_SIZE_DLM_CH(channels));
+
+       return mtk_afe_fe_hw_params(substream, params, dai);
+}
+
+static int mt2701_dlm_fe_trigger(struct snd_pcm_substream *substream,
+                                int cmd, struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mtk_base_afe_memif *memif_tmp = &afe->memif[MT2701_MEMIF_DL1];
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+               regmap_update_bits(afe->regmap, memif_tmp->data->enable_reg,
+                                  1 << memif_tmp->data->enable_shift,
+                                  1 << memif_tmp->data->enable_shift);
+               mtk_afe_fe_trigger(substream, cmd, dai);
+               return 0;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               mtk_afe_fe_trigger(substream, cmd, dai);
+               regmap_update_bits(afe->regmap, memif_tmp->data->enable_reg,
+                                  1 << memif_tmp->data->enable_shift, 0);
+
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int mt2701_memif_fs(struct snd_pcm_substream *substream,
+                          unsigned int rate)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       int fs;
+
+       if (rtd->cpu_dai->id != MT2701_MEMIF_ULBT)
+               fs = mt2701_afe_i2s_fs(rate);
+       else
+               fs = (rate == 16000 ? 1 : 0);
+       return fs;
+}
+
+static int mt2701_irq_fs(struct snd_pcm_substream *substream, unsigned int rate)
+{
+       return mt2701_afe_i2s_fs(rate);
+}
+
+/* FE DAIs */
+static const struct snd_soc_dai_ops mt2701_single_memif_dai_ops = {
+       .startup        = mt2701_simple_fe_startup,
+       .shutdown       = mtk_afe_fe_shutdown,
+       .hw_params      = mt2701_simple_fe_hw_params,
+       .hw_free        = mtk_afe_fe_hw_free,
+       .prepare        = mtk_afe_fe_prepare,
+       .trigger        = mtk_afe_fe_trigger,
+
+};
+
+static const struct snd_soc_dai_ops mt2701_dlm_memif_dai_ops = {
+       .startup        = mt2701_dlm_fe_startup,
+       .shutdown       = mt2701_dlm_fe_shutdown,
+       .hw_params      = mt2701_dlm_fe_hw_params,
+       .hw_free        = mtk_afe_fe_hw_free,
+       .prepare        = mtk_afe_fe_prepare,
+       .trigger        = mt2701_dlm_fe_trigger,
+};
+
+/* I2S BE DAIs */
+static const struct snd_soc_dai_ops mt2701_afe_i2s_ops = {
+       .startup        = mt2701_afe_i2s_startup,
+       .shutdown       = mt2701_afe_i2s_shutdown,
+       .prepare        = mt2701_afe_i2s_prepare,
+       .set_sysclk     = mt2701_afe_i2s_set_sysclk,
+};
+
+/* MRG BE DAIs */
+static struct snd_soc_dai_ops mt2701_btmrg_ops = {
+       .startup = mt2701_btmrg_startup,
+       .shutdown = mt2701_btmrg_shutdown,
+       .hw_params = mt2701_btmrg_hw_params,
+};
+
+static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = {
+       /* FE DAIs: memory intefaces to CPU */
+       {
+               .name = "PCM_multi",
+               .id = MT2701_MEMIF_DLM,
+               .suspend = mtk_afe_dai_suspend,
+               .resume = mtk_afe_dai_resume,
+               .playback = {
+                       .stream_name = "DLM",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = SNDRV_PCM_RATE_8000_192000,
+                       .formats = (SNDRV_PCM_FMTBIT_S16_LE
+                               | SNDRV_PCM_FMTBIT_S24_LE
+                               | SNDRV_PCM_FMTBIT_S32_LE)
+
+               },
+               .ops = &mt2701_dlm_memif_dai_ops,
+       },
+       {
+               .name = "PCM0",
+               .id = MT2701_MEMIF_UL1,
+               .suspend = mtk_afe_dai_suspend,
+               .resume = mtk_afe_dai_resume,
+               .capture = {
+                       .stream_name = "UL1",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000_48000,
+                       .formats = (SNDRV_PCM_FMTBIT_S16_LE
+                               | SNDRV_PCM_FMTBIT_S24_LE
+                               | SNDRV_PCM_FMTBIT_S32_LE)
+               },
+               .ops = &mt2701_single_memif_dai_ops,
+       },
+       {
+               .name = "PCM1",
+               .id = MT2701_MEMIF_UL2,
+               .suspend = mtk_afe_dai_suspend,
+               .resume = mtk_afe_dai_resume,
+               .capture = {
+                       .stream_name = "UL2",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000_192000,
+                       .formats = (SNDRV_PCM_FMTBIT_S16_LE
+                               | SNDRV_PCM_FMTBIT_S24_LE
+                               | SNDRV_PCM_FMTBIT_S32_LE)
+
+               },
+               .ops = &mt2701_single_memif_dai_ops,
+       },
+       {
+               .name = "PCM_BT_DL",
+               .id = MT2701_MEMIF_DLBT,
+               .suspend = mtk_afe_dai_suspend,
+               .resume = mtk_afe_dai_resume,
+               .playback = {
+                       .stream_name = "DLBT",
+                       .channels_min = 1,
+                       .channels_max = 1,
+                       .rates = (SNDRV_PCM_RATE_8000
+                               | SNDRV_PCM_RATE_16000),
+                       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+               },
+               .ops = &mt2701_single_memif_dai_ops,
+       },
+       {
+               .name = "PCM_BT_UL",
+               .id = MT2701_MEMIF_ULBT,
+               .suspend = mtk_afe_dai_suspend,
+               .resume = mtk_afe_dai_resume,
+               .capture = {
+                       .stream_name = "ULBT",
+                       .channels_min = 1,
+                       .channels_max = 1,
+                       .rates = (SNDRV_PCM_RATE_8000
+                               | SNDRV_PCM_RATE_16000),
+                       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+               },
+               .ops = &mt2701_single_memif_dai_ops,
+       },
+       /* BE DAIs */
+       {
+               .name = "I2S0",
+               .id = MT2701_IO_I2S,
+               .playback = {
+                       .stream_name = "I2S0 Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000_192000,
+                       .formats = (SNDRV_PCM_FMTBIT_S16_LE
+                               | SNDRV_PCM_FMTBIT_S24_LE
+                               | SNDRV_PCM_FMTBIT_S32_LE)
+
+               },
+               .capture = {
+                       .stream_name = "I2S0 Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000_192000,
+                       .formats = (SNDRV_PCM_FMTBIT_S16_LE
+                               | SNDRV_PCM_FMTBIT_S24_LE
+                               | SNDRV_PCM_FMTBIT_S32_LE)
+
+               },
+               .ops = &mt2701_afe_i2s_ops,
+               .symmetric_rates = 1,
+       },
+       {
+               .name = "I2S1",
+               .id = MT2701_IO_2ND_I2S,
+               .playback = {
+                       .stream_name = "I2S1 Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000_192000,
+                       .formats = (SNDRV_PCM_FMTBIT_S16_LE
+                               | SNDRV_PCM_FMTBIT_S24_LE
+                               | SNDRV_PCM_FMTBIT_S32_LE)
+                       },
+               .capture = {
+                       .stream_name = "I2S1 Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000_192000,
+                       .formats = (SNDRV_PCM_FMTBIT_S16_LE
+                               | SNDRV_PCM_FMTBIT_S24_LE
+                               | SNDRV_PCM_FMTBIT_S32_LE)
+                       },
+               .ops = &mt2701_afe_i2s_ops,
+               .symmetric_rates = 1,
+       },
+       {
+               .name = "I2S2",
+               .id = MT2701_IO_3RD_I2S,
+               .playback = {
+                       .stream_name = "I2S2 Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000_192000,
+                       .formats = (SNDRV_PCM_FMTBIT_S16_LE
+                               | SNDRV_PCM_FMTBIT_S24_LE
+                               | SNDRV_PCM_FMTBIT_S32_LE)
+                       },
+               .capture = {
+                       .stream_name = "I2S2 Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000_192000,
+                       .formats = (SNDRV_PCM_FMTBIT_S16_LE
+                               | SNDRV_PCM_FMTBIT_S24_LE
+                               | SNDRV_PCM_FMTBIT_S32_LE)
+                       },
+               .ops = &mt2701_afe_i2s_ops,
+               .symmetric_rates = 1,
+       },
+       {
+               .name = "I2S3",
+               .id = MT2701_IO_4TH_I2S,
+               .playback = {
+                       .stream_name = "I2S3 Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000_192000,
+                       .formats = (SNDRV_PCM_FMTBIT_S16_LE
+                               | SNDRV_PCM_FMTBIT_S24_LE
+                               | SNDRV_PCM_FMTBIT_S32_LE)
+                       },
+               .capture = {
+                       .stream_name = "I2S3 Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000_192000,
+                       .formats = (SNDRV_PCM_FMTBIT_S16_LE
+                               | SNDRV_PCM_FMTBIT_S24_LE
+                               | SNDRV_PCM_FMTBIT_S32_LE)
+                       },
+               .ops = &mt2701_afe_i2s_ops,
+               .symmetric_rates = 1,
+       },
+       {
+               .name = "MRG BT",
+               .id = MT2701_IO_MRG,
+               .playback = {
+                       .stream_name = "BT Playback",
+                       .channels_min = 1,
+                       .channels_max = 1,
+                       .rates = (SNDRV_PCM_RATE_8000
+                               | SNDRV_PCM_RATE_16000),
+                       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+               },
+               .capture = {
+                       .stream_name = "BT Capture",
+                       .channels_min = 1,
+                       .channels_max = 1,
+                       .rates = (SNDRV_PCM_RATE_8000
+                               | SNDRV_PCM_RATE_16000),
+                       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+               },
+               .ops = &mt2701_btmrg_ops,
+               .symmetric_rates = 1,
+       }
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o00_mix[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("I00 Switch", AFE_CONN0, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o01_mix[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("I01 Switch", AFE_CONN1, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o02_mix[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("I02 Switch", AFE_CONN2, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o03_mix[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN3, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o14_mix[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("I26 Switch", AFE_CONN14, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o15_mix[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("I12 Switch", AFE_CONN15, 12, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o16_mix[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("I13 Switch", AFE_CONN16, 13, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o17_mix[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("I14 Switch", AFE_CONN17, 14, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o18_mix[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("I15 Switch", AFE_CONN18, 15, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o19_mix[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("I16 Switch", AFE_CONN19, 16, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o20_mix[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN20, 17, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o21_mix[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN21, 18, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o22_mix[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("I19 Switch", AFE_CONN22, 19, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o23_mix[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("I20 Switch", AFE_CONN23, 20, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o24_mix[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("I21 Switch", AFE_CONN24, 21, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o31_mix[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("I35 Switch", AFE_CONN41, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_i02_mix[] = {
+       SOC_DAPM_SINGLE("I2S0 Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s0[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S0 Out Switch",
+                                   ASYS_I2SO1_CON, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s1[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S1 Out Switch",
+                                   ASYS_I2SO2_CON, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s2[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S2 Out Switch",
+                                   PWR2_TOP_CON, 17, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s3[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S3 Out Switch",
+                                   PWR2_TOP_CON, 18, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s4[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S4 Out Switch",
+                                   PWR2_TOP_CON, 19, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc0[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("Asrc0 out Switch", AUDIO_TOP_CON4, 14, 1,
+                                   1),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc1[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("Asrc1 out Switch", AUDIO_TOP_CON4, 15, 1,
+                                   1),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc2[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("Asrc2 out Switch", PWR2_TOP_CON, 6, 1,
+                                   1),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc3[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("Asrc3 out Switch", PWR2_TOP_CON, 7, 1,
+                                   1),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc4[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("Asrc4 out Switch", PWR2_TOP_CON, 8, 1,
+                                   1),
+};
+
+static const struct snd_soc_dapm_widget mt2701_afe_pcm_widgets[] = {
+       /* inter-connections */
+       SND_SOC_DAPM_MIXER("I00", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("I01", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("I02", SND_SOC_NOPM, 0, 0, mt2701_afe_i02_mix,
+                          ARRAY_SIZE(mt2701_afe_i02_mix)),
+       SND_SOC_DAPM_MIXER("I03", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("I12", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("I13", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("I14", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("I15", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("I16", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("I17", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("I18", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("I19", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("I26", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("I35", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       SND_SOC_DAPM_MIXER("O00", SND_SOC_NOPM, 0, 0, mt2701_afe_o00_mix,
+                          ARRAY_SIZE(mt2701_afe_o00_mix)),
+       SND_SOC_DAPM_MIXER("O01", SND_SOC_NOPM, 0, 0, mt2701_afe_o01_mix,
+                          ARRAY_SIZE(mt2701_afe_o01_mix)),
+       SND_SOC_DAPM_MIXER("O02", SND_SOC_NOPM, 0, 0, mt2701_afe_o02_mix,
+                          ARRAY_SIZE(mt2701_afe_o02_mix)),
+       SND_SOC_DAPM_MIXER("O03", SND_SOC_NOPM, 0, 0, mt2701_afe_o03_mix,
+                          ARRAY_SIZE(mt2701_afe_o03_mix)),
+       SND_SOC_DAPM_MIXER("O14", SND_SOC_NOPM, 0, 0, mt2701_afe_o14_mix,
+                          ARRAY_SIZE(mt2701_afe_o14_mix)),
+       SND_SOC_DAPM_MIXER("O15", SND_SOC_NOPM, 0, 0, mt2701_afe_o15_mix,
+                          ARRAY_SIZE(mt2701_afe_o15_mix)),
+       SND_SOC_DAPM_MIXER("O16", SND_SOC_NOPM, 0, 0, mt2701_afe_o16_mix,
+                          ARRAY_SIZE(mt2701_afe_o16_mix)),
+       SND_SOC_DAPM_MIXER("O17", SND_SOC_NOPM, 0, 0, mt2701_afe_o17_mix,
+                          ARRAY_SIZE(mt2701_afe_o17_mix)),
+       SND_SOC_DAPM_MIXER("O18", SND_SOC_NOPM, 0, 0, mt2701_afe_o18_mix,
+                          ARRAY_SIZE(mt2701_afe_o18_mix)),
+       SND_SOC_DAPM_MIXER("O19", SND_SOC_NOPM, 0, 0, mt2701_afe_o19_mix,
+                          ARRAY_SIZE(mt2701_afe_o19_mix)),
+       SND_SOC_DAPM_MIXER("O20", SND_SOC_NOPM, 0, 0, mt2701_afe_o20_mix,
+                          ARRAY_SIZE(mt2701_afe_o20_mix)),
+       SND_SOC_DAPM_MIXER("O21", SND_SOC_NOPM, 0, 0, mt2701_afe_o21_mix,
+                          ARRAY_SIZE(mt2701_afe_o21_mix)),
+       SND_SOC_DAPM_MIXER("O22", SND_SOC_NOPM, 0, 0, mt2701_afe_o22_mix,
+                          ARRAY_SIZE(mt2701_afe_o22_mix)),
+       SND_SOC_DAPM_MIXER("O31", SND_SOC_NOPM, 0, 0, mt2701_afe_o31_mix,
+                          ARRAY_SIZE(mt2701_afe_o31_mix)),
+
+       SND_SOC_DAPM_MIXER("I12I13", SND_SOC_NOPM, 0, 0,
+                          mt2701_afe_multi_ch_out_i2s0,
+                          ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s0)),
+       SND_SOC_DAPM_MIXER("I14I15", SND_SOC_NOPM, 0, 0,
+                          mt2701_afe_multi_ch_out_i2s1,
+                          ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s1)),
+       SND_SOC_DAPM_MIXER("I16I17", SND_SOC_NOPM, 0, 0,
+                          mt2701_afe_multi_ch_out_i2s2,
+                          ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s2)),
+       SND_SOC_DAPM_MIXER("I18I19", SND_SOC_NOPM, 0, 0,
+                          mt2701_afe_multi_ch_out_i2s3,
+                          ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s3)),
+
+       SND_SOC_DAPM_MIXER("ASRC_O0", SND_SOC_NOPM, 0, 0,
+                          mt2701_afe_multi_ch_out_asrc0,
+                          ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc0)),
+       SND_SOC_DAPM_MIXER("ASRC_O1", SND_SOC_NOPM, 0, 0,
+                          mt2701_afe_multi_ch_out_asrc1,
+                          ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc1)),
+       SND_SOC_DAPM_MIXER("ASRC_O2", SND_SOC_NOPM, 0, 0,
+                          mt2701_afe_multi_ch_out_asrc2,
+                          ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc2)),
+       SND_SOC_DAPM_MIXER("ASRC_O3", SND_SOC_NOPM, 0, 0,
+                          mt2701_afe_multi_ch_out_asrc3,
+                          ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc3)),
+};
+
+static const struct snd_soc_dapm_route mt2701_afe_pcm_routes[] = {
+       {"I12", NULL, "DL1"},
+       {"I13", NULL, "DL1"},
+       {"I35", NULL, "DLBT"},
+
+       {"I2S0 Playback", NULL, "O15"},
+       {"I2S0 Playback", NULL, "O16"},
+
+       {"I2S1 Playback", NULL, "O17"},
+       {"I2S1 Playback", NULL, "O18"},
+       {"I2S2 Playback", NULL, "O19"},
+       {"I2S2 Playback", NULL, "O20"},
+       {"I2S3 Playback", NULL, "O21"},
+       {"I2S3 Playback", NULL, "O22"},
+       {"BT Playback", NULL, "O31"},
+
+       {"UL1", NULL, "O00"},
+       {"UL1", NULL, "O01"},
+       {"UL2", NULL, "O02"},
+       {"UL2", NULL, "O03"},
+       {"ULBT", NULL, "O14"},
+
+       {"I00", NULL, "I2S0 Capture"},
+       {"I01", NULL, "I2S0 Capture"},
+
+       {"I02", NULL, "I2S1 Capture"},
+       {"I03", NULL, "I2S1 Capture"},
+       /* I02,03 link to UL2, also need to open I2S0 */
+       {"I02", "I2S0 Switch", "I2S0 Capture"},
+
+       {"I26", NULL, "BT Capture"},
+
+       {"ASRC_O0", "Asrc0 out Switch", "DLM"},
+       {"ASRC_O1", "Asrc1 out Switch", "DLM"},
+       {"ASRC_O2", "Asrc2 out Switch", "DLM"},
+       {"ASRC_O3", "Asrc3 out Switch", "DLM"},
+
+       {"I12I13", "Multich I2S0 Out Switch", "ASRC_O0"},
+       {"I14I15", "Multich I2S1 Out Switch", "ASRC_O1"},
+       {"I16I17", "Multich I2S2 Out Switch", "ASRC_O2"},
+       {"I18I19", "Multich I2S3 Out Switch", "ASRC_O3"},
+
+       { "I12", NULL, "I12I13" },
+       { "I13", NULL, "I12I13" },
+       { "I14", NULL, "I14I15" },
+       { "I15", NULL, "I14I15" },
+       { "I16", NULL, "I16I17" },
+       { "I17", NULL, "I16I17" },
+       { "I18", NULL, "I18I19" },
+       { "I19", NULL, "I18I19" },
+
+       { "O00", "I00 Switch", "I00" },
+       { "O01", "I01 Switch", "I01" },
+       { "O02", "I02 Switch", "I02" },
+       { "O03", "I03 Switch", "I03" },
+       { "O14", "I26 Switch", "I26" },
+       { "O15", "I12 Switch", "I12" },
+       { "O16", "I13 Switch", "I13" },
+       { "O17", "I14 Switch", "I14" },
+       { "O18", "I15 Switch", "I15" },
+       { "O19", "I16 Switch", "I16" },
+       { "O20", "I17 Switch", "I17" },
+       { "O21", "I18 Switch", "I18" },
+       { "O22", "I19 Switch", "I19" },
+       { "O31", "I35 Switch", "I35" },
+
+};
+
+static const struct snd_soc_component_driver mt2701_afe_pcm_dai_component = {
+       .name = "mt2701-afe-pcm-dai",
+       .dapm_widgets = mt2701_afe_pcm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(mt2701_afe_pcm_widgets),
+       .dapm_routes = mt2701_afe_pcm_routes,
+       .num_dapm_routes = ARRAY_SIZE(mt2701_afe_pcm_routes),
+};
+
+static const struct mtk_base_memif_data memif_data[MT2701_MEMIF_NUM] = {
+       {
+               .name = "DL1",
+               .id = MT2701_MEMIF_DL1,
+               .reg_ofs_base = AFE_DL1_BASE,
+               .reg_ofs_cur = AFE_DL1_CUR,
+               .fs_reg = AFE_DAC_CON1,
+               .fs_shift = 0,
+               .fs_maskbit = 0x1f,
+               .mono_reg = AFE_DAC_CON3,
+               .mono_shift = 16,
+               .enable_reg = AFE_DAC_CON0,
+               .enable_shift = 1,
+               .hd_reg = AFE_MEMIF_HD_CON0,
+               .hd_shift = 0,
+               .agent_disable_reg = AUDIO_TOP_CON5,
+               .agent_disable_shift = 6,
+               .msb_reg = -1,
+               .msb_shift = -1,
+       },
+       {
+               .name = "DL2",
+               .id = MT2701_MEMIF_DL2,
+               .reg_ofs_base = AFE_DL2_BASE,
+               .reg_ofs_cur = AFE_DL2_CUR,
+               .fs_reg = AFE_DAC_CON1,
+               .fs_shift = 5,
+               .fs_maskbit = 0x1f,
+               .mono_reg = AFE_DAC_CON3,
+               .mono_shift = 17,
+               .enable_reg = AFE_DAC_CON0,
+               .enable_shift = 2,
+               .hd_reg = AFE_MEMIF_HD_CON0,
+               .hd_shift = 2,
+               .agent_disable_reg = AUDIO_TOP_CON5,
+               .agent_disable_shift = 7,
+               .msb_reg = -1,
+               .msb_shift = -1,
+       },
+       {
+               .name = "DL3",
+               .id = MT2701_MEMIF_DL3,
+               .reg_ofs_base = AFE_DL3_BASE,
+               .reg_ofs_cur = AFE_DL3_CUR,
+               .fs_reg = AFE_DAC_CON1,
+               .fs_shift = 10,
+               .fs_maskbit = 0x1f,
+               .mono_reg = AFE_DAC_CON3,
+               .mono_shift = 18,
+               .enable_reg = AFE_DAC_CON0,
+               .enable_shift = 3,
+               .hd_reg = AFE_MEMIF_HD_CON0,
+               .hd_shift = 4,
+               .agent_disable_reg = AUDIO_TOP_CON5,
+               .agent_disable_shift = 8,
+               .msb_reg = -1,
+               .msb_shift = -1,
+       },
+       {
+               .name = "DL4",
+               .id = MT2701_MEMIF_DL4,
+               .reg_ofs_base = AFE_DL4_BASE,
+               .reg_ofs_cur = AFE_DL4_CUR,
+               .fs_reg = AFE_DAC_CON1,
+               .fs_shift = 15,
+               .fs_maskbit = 0x1f,
+               .mono_reg = AFE_DAC_CON3,
+               .mono_shift = 19,
+               .enable_reg = AFE_DAC_CON0,
+               .enable_shift = 4,
+               .hd_reg = AFE_MEMIF_HD_CON0,
+               .hd_shift = 6,
+               .agent_disable_reg = AUDIO_TOP_CON5,
+               .agent_disable_shift = 9,
+               .msb_reg = -1,
+               .msb_shift = -1,
+       },
+       {
+               .name = "DL5",
+               .id = MT2701_MEMIF_DL5,
+               .reg_ofs_base = AFE_DL5_BASE,
+               .reg_ofs_cur = AFE_DL5_CUR,
+               .fs_reg = AFE_DAC_CON1,
+               .fs_shift = 20,
+               .fs_maskbit = 0x1f,
+               .mono_reg = AFE_DAC_CON3,
+               .mono_shift = 20,
+               .enable_reg = AFE_DAC_CON0,
+               .enable_shift = 5,
+               .hd_reg = AFE_MEMIF_HD_CON0,
+               .hd_shift = 8,
+               .agent_disable_reg = AUDIO_TOP_CON5,
+               .agent_disable_shift = 10,
+               .msb_reg = -1,
+               .msb_shift = -1,
+       },
+       {
+               .name = "DLM",
+               .id = MT2701_MEMIF_DLM,
+               .reg_ofs_base = AFE_DLMCH_BASE,
+               .reg_ofs_cur = AFE_DLMCH_CUR,
+               .fs_reg = AFE_DAC_CON1,
+               .fs_shift = 0,
+               .fs_maskbit = 0x1f,
+               .mono_reg = -1,
+               .mono_shift = -1,
+               .enable_reg = AFE_DAC_CON0,
+               .enable_shift = 7,
+               .hd_reg = AFE_MEMIF_PBUF_SIZE,
+               .hd_shift = 28,
+               .agent_disable_reg = AUDIO_TOP_CON5,
+               .agent_disable_shift = 12,
+               .msb_reg = -1,
+               .msb_shift = -1,
+       },
+       {
+               .name = "UL1",
+               .id = MT2701_MEMIF_UL1,
+               .reg_ofs_base = AFE_VUL_BASE,
+               .reg_ofs_cur = AFE_VUL_CUR,
+               .fs_reg = AFE_DAC_CON2,
+               .fs_shift = 0,
+               .fs_maskbit = 0x1f,
+               .mono_reg = AFE_DAC_CON4,
+               .mono_shift = 0,
+               .enable_reg = AFE_DAC_CON0,
+               .enable_shift = 10,
+               .hd_reg = AFE_MEMIF_HD_CON1,
+               .hd_shift = 0,
+               .agent_disable_reg = AUDIO_TOP_CON5,
+               .agent_disable_shift = 0,
+               .msb_reg = -1,
+               .msb_shift = -1,
+       },
+       {
+               .name = "UL2",
+               .id = MT2701_MEMIF_UL2,
+               .reg_ofs_base = AFE_UL2_BASE,
+               .reg_ofs_cur = AFE_UL2_CUR,
+               .fs_reg = AFE_DAC_CON2,
+               .fs_shift = 5,
+               .fs_maskbit = 0x1f,
+               .mono_reg = AFE_DAC_CON4,
+               .mono_shift = 2,
+               .enable_reg = AFE_DAC_CON0,
+               .enable_shift = 11,
+               .hd_reg = AFE_MEMIF_HD_CON1,
+               .hd_shift = 2,
+               .agent_disable_reg = AUDIO_TOP_CON5,
+               .agent_disable_shift = 1,
+               .msb_reg = -1,
+               .msb_shift = -1,
+       },
+       {
+               .name = "UL3",
+               .id = MT2701_MEMIF_UL3,
+               .reg_ofs_base = AFE_UL3_BASE,
+               .reg_ofs_cur = AFE_UL3_CUR,
+               .fs_reg = AFE_DAC_CON2,
+               .fs_shift = 10,
+               .fs_maskbit = 0x1f,
+               .mono_reg = AFE_DAC_CON4,
+               .mono_shift = 4,
+               .enable_reg = AFE_DAC_CON0,
+               .enable_shift = 12,
+               .hd_reg = AFE_MEMIF_HD_CON0,
+               .hd_shift = 0,
+               .agent_disable_reg = AUDIO_TOP_CON5,
+               .agent_disable_shift = 2,
+               .msb_reg = -1,
+               .msb_shift = -1,
+       },
+       {
+               .name = "UL4",
+               .id = MT2701_MEMIF_UL4,
+               .reg_ofs_base = AFE_UL4_BASE,
+               .reg_ofs_cur = AFE_UL4_CUR,
+               .fs_reg = AFE_DAC_CON2,
+               .fs_shift = 15,
+               .fs_maskbit = 0x1f,
+               .mono_reg = AFE_DAC_CON4,
+               .mono_shift = 6,
+               .enable_reg = AFE_DAC_CON0,
+               .enable_shift = 13,
+               .hd_reg = AFE_MEMIF_HD_CON0,
+               .hd_shift = 6,
+               .agent_disable_reg = AUDIO_TOP_CON5,
+               .agent_disable_shift = 3,
+               .msb_reg = -1,
+               .msb_shift = -1,
+       },
+       {
+               .name = "UL5",
+               .id = MT2701_MEMIF_UL5,
+               .reg_ofs_base = AFE_UL5_BASE,
+               .reg_ofs_cur = AFE_UL5_CUR,
+               .fs_reg = AFE_DAC_CON2,
+               .fs_shift = 20,
+               .mono_reg = AFE_DAC_CON4,
+               .mono_shift = 8,
+               .fs_maskbit = 0x1f,
+               .enable_reg = AFE_DAC_CON0,
+               .enable_shift = 14,
+               .hd_reg = AFE_MEMIF_HD_CON0,
+               .hd_shift = 8,
+               .agent_disable_reg = AUDIO_TOP_CON5,
+               .agent_disable_shift = 4,
+               .msb_reg = -1,
+               .msb_shift = -1,
+       },
+       {
+               .name = "DLBT",
+               .id = MT2701_MEMIF_DLBT,
+               .reg_ofs_base = AFE_ARB1_BASE,
+               .reg_ofs_cur = AFE_ARB1_CUR,
+               .fs_reg = AFE_DAC_CON3,
+               .fs_shift = 10,
+               .fs_maskbit = 0x1f,
+               .mono_reg = AFE_DAC_CON3,
+               .mono_shift = 22,
+               .enable_reg = AFE_DAC_CON0,
+               .enable_shift = 8,
+               .hd_reg = AFE_MEMIF_HD_CON0,
+               .hd_shift = 14,
+               .agent_disable_reg = AUDIO_TOP_CON5,
+               .agent_disable_shift = 13,
+               .msb_reg = -1,
+               .msb_shift = -1,
+       },
+       {
+               .name = "ULBT",
+               .id = MT2701_MEMIF_ULBT,
+               .reg_ofs_base = AFE_DAI_BASE,
+               .reg_ofs_cur = AFE_DAI_CUR,
+               .fs_reg = AFE_DAC_CON2,
+               .fs_shift = 30,
+               .fs_maskbit = 0x1,
+               .mono_reg = -1,
+               .mono_shift = -1,
+               .enable_reg = AFE_DAC_CON0,
+               .enable_shift = 17,
+               .hd_reg = AFE_MEMIF_HD_CON1,
+               .hd_shift = 20,
+               .agent_disable_reg = AUDIO_TOP_CON5,
+               .agent_disable_shift = 16,
+               .msb_reg = -1,
+               .msb_shift = -1,
+       },
+};
+
+static const struct mtk_base_irq_data irq_data[MT2701_IRQ_ASYS_END] = {
+       {
+               .id = MT2701_IRQ_ASYS_IRQ1,
+               .irq_cnt_reg = ASYS_IRQ1_CON,
+               .irq_cnt_shift = 0,
+               .irq_cnt_maskbit = 0xffffff,
+               .irq_fs_reg = ASYS_IRQ1_CON,
+               .irq_fs_shift = 24,
+               .irq_fs_maskbit = 0x1f,
+               .irq_en_reg = ASYS_IRQ1_CON,
+               .irq_en_shift = 31,
+               .irq_clr_reg = ASYS_IRQ_CLR,
+               .irq_clr_shift = 0,
+       },
+       {
+               .id = MT2701_IRQ_ASYS_IRQ2,
+               .irq_cnt_reg = ASYS_IRQ2_CON,
+               .irq_cnt_shift = 0,
+               .irq_cnt_maskbit = 0xffffff,
+               .irq_fs_reg = ASYS_IRQ2_CON,
+               .irq_fs_shift = 24,
+               .irq_fs_maskbit = 0x1f,
+               .irq_en_reg = ASYS_IRQ2_CON,
+               .irq_en_shift = 31,
+               .irq_clr_reg = ASYS_IRQ_CLR,
+               .irq_clr_shift = 1,
+       },
+       {
+               .id = MT2701_IRQ_ASYS_IRQ3,
+               .irq_cnt_reg = ASYS_IRQ3_CON,
+               .irq_cnt_shift = 0,
+               .irq_cnt_maskbit = 0xffffff,
+               .irq_fs_reg = ASYS_IRQ3_CON,
+               .irq_fs_shift = 24,
+               .irq_fs_maskbit = 0x1f,
+               .irq_en_reg = ASYS_IRQ3_CON,
+               .irq_en_shift = 31,
+               .irq_clr_reg = ASYS_IRQ_CLR,
+               .irq_clr_shift = 2,
+       }
+};
+
+static const struct mt2701_i2s_data mt2701_i2s_data[MT2701_I2S_NUM][2] = {
+       {
+               {
+                       .i2s_ctrl_reg = ASYS_I2SO1_CON,
+                       .i2s_pwn_shift = 6,
+                       .i2s_asrc_fs_shift = 0,
+                       .i2s_asrc_fs_mask = 0x1f,
+
+               },
+               {
+                       .i2s_ctrl_reg = ASYS_I2SIN1_CON,
+                       .i2s_pwn_shift = 0,
+                       .i2s_asrc_fs_shift = 0,
+                       .i2s_asrc_fs_mask = 0x1f,
+
+               },
+       },
+       {
+               {
+                       .i2s_ctrl_reg = ASYS_I2SO2_CON,
+                       .i2s_pwn_shift = 7,
+                       .i2s_asrc_fs_shift = 5,
+                       .i2s_asrc_fs_mask = 0x1f,
+
+               },
+               {
+                       .i2s_ctrl_reg = ASYS_I2SIN2_CON,
+                       .i2s_pwn_shift = 1,
+                       .i2s_asrc_fs_shift = 5,
+                       .i2s_asrc_fs_mask = 0x1f,
+
+               },
+       },
+       {
+               {
+                       .i2s_ctrl_reg = ASYS_I2SO3_CON,
+                       .i2s_pwn_shift = 8,
+                       .i2s_asrc_fs_shift = 10,
+                       .i2s_asrc_fs_mask = 0x1f,
+
+               },
+               {
+                       .i2s_ctrl_reg = ASYS_I2SIN3_CON,
+                       .i2s_pwn_shift = 2,
+                       .i2s_asrc_fs_shift = 10,
+                       .i2s_asrc_fs_mask = 0x1f,
+
+               },
+       },
+       {
+               {
+                       .i2s_ctrl_reg = ASYS_I2SO4_CON,
+                       .i2s_pwn_shift = 9,
+                       .i2s_asrc_fs_shift = 15,
+                       .i2s_asrc_fs_mask = 0x1f,
+
+               },
+               {
+                       .i2s_ctrl_reg = ASYS_I2SIN4_CON,
+                       .i2s_pwn_shift = 3,
+                       .i2s_asrc_fs_shift = 15,
+                       .i2s_asrc_fs_mask = 0x1f,
+
+               },
+       },
+};
+
+static const struct regmap_config mt2701_afe_regmap_config = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+       .max_register = AFE_END_ADDR,
+       .cache_type = REGCACHE_NONE,
+};
+
+static irqreturn_t mt2701_asys_isr(int irq_id, void *dev)
+{
+       int id;
+       struct mtk_base_afe *afe = dev;
+       struct mtk_base_afe_memif *memif;
+       struct mtk_base_afe_irq *irq;
+       u32 status;
+
+       regmap_read(afe->regmap, ASYS_IRQ_STATUS, &status);
+       regmap_write(afe->regmap, ASYS_IRQ_CLR, status);
+
+       for (id = 0; id < MT2701_MEMIF_NUM; ++id) {
+               memif = &afe->memif[id];
+               if (memif->irq_usage < 0)
+                       continue;
+               irq = &afe->irqs[memif->irq_usage];
+               if (status & 1 << (irq->irq_data->irq_clr_shift))
+                       snd_pcm_period_elapsed(memif->substream);
+       }
+       return IRQ_HANDLED;
+}
+
+static int mt2701_afe_runtime_suspend(struct device *dev)
+{
+       struct mtk_base_afe *afe = dev_get_drvdata(dev);
+
+       mt2701_afe_disable_clock(afe);
+       return 0;
+}
+
+static int mt2701_afe_runtime_resume(struct device *dev)
+{
+       struct mtk_base_afe *afe = dev_get_drvdata(dev);
+
+       return mt2701_afe_enable_clock(afe);
+}
+
+static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev)
+{
+       int ret, i;
+       unsigned int irq_id;
+       struct mtk_base_afe *afe;
+       struct mt2701_afe_private *afe_priv;
+       struct resource *res;
+       struct device *dev;
+
+       ret = 0;
+       afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
+       if (!afe)
+               return -ENOMEM;
+       afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
+                                         GFP_KERNEL);
+       if (!afe->platform_priv)
+               return -ENOMEM;
+       afe_priv = afe->platform_priv;
+
+       afe->dev = &pdev->dev;
+       dev = afe->dev;
+
+       irq_id = platform_get_irq(pdev, 0);
+       if (!irq_id) {
+               dev_err(dev, "%s no irq found\n", dev->of_node->name);
+               return -ENXIO;
+       }
+       ret = devm_request_irq(dev, irq_id, mt2701_asys_isr,
+                              IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
+       if (ret) {
+               dev_err(dev, "could not request_irq for asys-isr\n");
+               return ret;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       afe->base_addr = devm_ioremap_resource(&pdev->dev, res);
+
+       if (IS_ERR(afe->base_addr))
+               return PTR_ERR(afe->base_addr);
+
+       afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
+               &mt2701_afe_regmap_config);
+       if (IS_ERR(afe->regmap))
+               return PTR_ERR(afe->regmap);
+
+       mutex_init(&afe->irq_alloc_lock);
+
+       /* memif initialize */
+       afe->memif_size = MT2701_MEMIF_NUM;
+       afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif),
+                                 GFP_KERNEL);
+
+       if (!afe->memif)
+               return -ENOMEM;
+
+       for (i = 0; i < afe->memif_size; i++) {
+               afe->memif[i].data = &memif_data[i];
+               afe->memif[i].irq_usage = -1;
+       }
+
+       /* irq initialize */
+       afe->irqs_size = MT2701_IRQ_ASYS_END;
+       afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs),
+                                GFP_KERNEL);
+
+       if (!afe->irqs)
+               return -ENOMEM;
+
+       for (i = 0; i < afe->irqs_size; i++)
+               afe->irqs[i].irq_data = &irq_data[i];
+
+       /* I2S initialize */
+       for (i = 0; i < MT2701_I2S_NUM; i++) {
+               afe_priv->i2s_path[i].i2s_data[I2S_OUT]
+                       = &mt2701_i2s_data[i][I2S_OUT];
+               afe_priv->i2s_path[i].i2s_data[I2S_IN]
+                       = &mt2701_i2s_data[i][I2S_IN];
+       }
+
+       afe->mtk_afe_hardware = &mt2701_afe_hardware;
+       afe->memif_fs = mt2701_memif_fs;
+       afe->irq_fs = mt2701_irq_fs;
+
+       afe->reg_back_up_list = mt2701_afe_backup_list;
+       afe->reg_back_up_list_num = ARRAY_SIZE(mt2701_afe_backup_list);
+       afe->runtime_resume = mt2701_afe_runtime_resume;
+       afe->runtime_suspend = mt2701_afe_runtime_suspend;
+
+       /* initial audio related clock */
+       ret = mt2701_init_clock(afe);
+       if (ret) {
+               dev_err(dev, "init clock error\n");
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, afe);
+       pm_runtime_enable(&pdev->dev);
+       if (!pm_runtime_enabled(&pdev->dev))
+               goto err_pm_disable;
+
+       ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform);
+       if (ret) {
+               dev_warn(dev, "err_platform\n");
+               goto err_platform;
+       }
+
+       ret = snd_soc_register_component(&pdev->dev,
+                                        &mt2701_afe_pcm_dai_component,
+                                        mt2701_afe_pcm_dais,
+                                        ARRAY_SIZE(mt2701_afe_pcm_dais));
+       if (ret) {
+               dev_warn(dev, "err_dai_component\n");
+               goto err_dai_component;
+       }
+
+       mt2701_afe_runtime_resume(&pdev->dev);
+
+       return 0;
+
+err_dai_component:
+       snd_soc_unregister_component(&pdev->dev);
+
+err_platform:
+       snd_soc_unregister_platform(&pdev->dev);
+
+err_pm_disable:
+       pm_runtime_disable(&pdev->dev);
+
+       return ret;
+}
+
+static int mt2701_afe_pcm_dev_remove(struct platform_device *pdev)
+{
+       struct mtk_base_afe *afe = platform_get_drvdata(pdev);
+
+       pm_runtime_disable(&pdev->dev);
+       if (!pm_runtime_status_suspended(&pdev->dev))
+               mt2701_afe_runtime_suspend(&pdev->dev);
+
+       snd_soc_unregister_component(&pdev->dev);
+       snd_soc_unregister_platform(&pdev->dev);
+       /* disable afe clock */
+       mt2701_afe_disable_clock(afe);
+       return 0;
+}
+
+static const struct of_device_id mt2701_afe_pcm_dt_match[] = {
+       { .compatible = "mediatek,mt2701-audio", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, mt2701_afe_pcm_dt_match);
+
+static const struct dev_pm_ops mt2701_afe_pm_ops = {
+       SET_RUNTIME_PM_OPS(mt2701_afe_runtime_suspend,
+                          mt2701_afe_runtime_resume, NULL)
+};
+
+static struct platform_driver mt2701_afe_pcm_driver = {
+       .driver = {
+                  .name = "mt2701-audio",
+                  .of_match_table = mt2701_afe_pcm_dt_match,
+#ifdef CONFIG_PM
+                  .pm = &mt2701_afe_pm_ops,
+#endif
+       },
+       .probe = mt2701_afe_pcm_dev_probe,
+       .remove = mt2701_afe_pcm_dev_remove,
+};
+
+module_platform_driver(mt2701_afe_pcm_driver);
+
+MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver for 2701");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+
diff --git a/sound/soc/mediatek/mt2701/mt2701-cs42448.c b/sound/soc/mediatek/mt2701/mt2701-cs42448.c
new file mode 100644 (file)
index 0000000..1e7e8d4
--- /dev/null
@@ -0,0 +1,412 @@
+/*
+ * mt2701-cs42448.c  --  MT2701 CS42448 ALSA SoC machine driver
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Ir Lian <ir.lian@mediatek.com>
+ *              Garlic Tseng <garlic.tseng@mediatek.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/of_gpio.h>
+
+#include "mt2701-afe-common.h"
+
+struct mt2701_cs42448_private {
+       int i2s1_in_mux;
+       int i2s1_in_mux_gpio_sel_1;
+       int i2s1_in_mux_gpio_sel_2;
+};
+
+static const char * const i2sin_mux_switch_text[] = {
+       "ADC_SDOUT2",
+       "ADC_SDOUT3",
+       "I2S_IN_1",
+       "I2S_IN_2",
+};
+
+static const struct soc_enum i2sin_mux_enum =
+       SOC_ENUM_SINGLE_EXT(4, i2sin_mux_switch_text);
+
+static int mt2701_cs42448_i2sin1_mux_get(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+       struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);
+
+       ucontrol->value.integer.value[0] = priv->i2s1_in_mux;
+       return 0;
+}
+
+static int mt2701_cs42448_i2sin1_mux_set(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+       struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);
+
+       if (ucontrol->value.integer.value[0] == priv->i2s1_in_mux)
+               return 0;
+
+       switch (ucontrol->value.integer.value[0]) {
+       case 0:
+               gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 0);
+               gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 0);
+               break;
+       case 1:
+               gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 1);
+               gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 0);
+               break;
+       case 2:
+               gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 0);
+               gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 1);
+               break;
+       case 3:
+               gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 1);
+               gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 1);
+               break;
+       default:
+               dev_warn(card->dev, "%s invalid setting\n", __func__);
+       }
+
+       priv->i2s1_in_mux = ucontrol->value.integer.value[0];
+       return 0;
+}
+
+static const struct snd_soc_dapm_widget
+                       mt2701_cs42448_asoc_card_dapm_widgets[] = {
+       SND_SOC_DAPM_LINE("Line Out Jack", NULL),
+       SND_SOC_DAPM_MIC("AMIC", NULL),
+       SND_SOC_DAPM_LINE("Tuner In", NULL),
+       SND_SOC_DAPM_LINE("Satellite Tuner In", NULL),
+       SND_SOC_DAPM_LINE("AUX In", NULL),
+};
+
+static const struct snd_kcontrol_new mt2701_cs42448_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Line Out Jack"),
+       SOC_DAPM_PIN_SWITCH("AMIC"),
+       SOC_DAPM_PIN_SWITCH("Tuner In"),
+       SOC_DAPM_PIN_SWITCH("Satellite Tuner In"),
+       SOC_DAPM_PIN_SWITCH("AUX In"),
+       SOC_ENUM_EXT("I2SIN1_MUX_Switch", i2sin_mux_enum,
+                    mt2701_cs42448_i2sin1_mux_get,
+                    mt2701_cs42448_i2sin1_mux_set),
+};
+
+static const unsigned int mt2701_cs42448_sampling_rates[] = {48000};
+
+static struct snd_pcm_hw_constraint_list mt2701_cs42448_constraints_rates = {
+               .count = ARRAY_SIZE(mt2701_cs42448_sampling_rates),
+               .list = mt2701_cs42448_sampling_rates,
+               .mask = 0,
+};
+
+static int mt2701_cs42448_fe_ops_startup(struct snd_pcm_substream *substream)
+{
+       int err;
+
+       err = snd_pcm_hw_constraint_list(substream->runtime, 0,
+                                        SNDRV_PCM_HW_PARAM_RATE,
+                                        &mt2701_cs42448_constraints_rates);
+       if (err < 0) {
+               dev_err(substream->pcm->card->dev,
+                       "%s snd_pcm_hw_constraint_list failed: 0x%x\n",
+                       __func__, err);
+               return err;
+       }
+       return 0;
+}
+
+static struct snd_soc_ops mt2701_cs42448_48k_fe_ops = {
+       .startup = mt2701_cs42448_fe_ops_startup,
+};
+
+static int mt2701_cs42448_be_ops_hw_params(struct snd_pcm_substream *substream,
+                                          struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       unsigned int mclk_rate;
+       unsigned int rate = params_rate(params);
+       unsigned int div_mclk_over_bck = rate > 192000 ? 2 : 4;
+       unsigned int div_bck_over_lrck = 64;
+
+       mclk_rate = rate * div_bck_over_lrck * div_mclk_over_bck;
+
+       /* mt2701 mclk */
+       snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, SND_SOC_CLOCK_OUT);
+
+       /* codec mclk */
+       snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, SND_SOC_CLOCK_IN);
+
+       return 0;
+}
+
+static struct snd_soc_ops mt2701_cs42448_be_ops = {
+       .hw_params = mt2701_cs42448_be_ops_hw_params
+};
+
+enum {
+       DAI_LINK_FE_MULTI_CH_OUT,
+       DAI_LINK_FE_PCM0_IN,
+       DAI_LINK_FE_PCM1_IN,
+       DAI_LINK_FE_BT_OUT,
+       DAI_LINK_FE_BT_IN,
+       DAI_LINK_BE_I2S0,
+       DAI_LINK_BE_I2S1,
+       DAI_LINK_BE_I2S2,
+       DAI_LINK_BE_I2S3,
+       DAI_LINK_BE_MRG_BT,
+};
+
+static struct snd_soc_dai_link mt2701_cs42448_dai_links[] = {
+       /* FE */
+       [DAI_LINK_FE_MULTI_CH_OUT] = {
+               .name = "mt2701-cs42448-multi-ch-out",
+               .stream_name = "mt2701-cs42448-multi-ch-out",
+               .cpu_dai_name = "PCM_multi",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+                           SND_SOC_DPCM_TRIGGER_POST},
+               .ops = &mt2701_cs42448_48k_fe_ops,
+               .dynamic = 1,
+               .dpcm_playback = 1,
+       },
+       [DAI_LINK_FE_PCM0_IN] = {
+               .name = "mt2701-cs42448-pcm0",
+               .stream_name = "mt2701-cs42448-pcm0-data-UL",
+               .cpu_dai_name = "PCM0",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+                           SND_SOC_DPCM_TRIGGER_POST},
+               .ops = &mt2701_cs42448_48k_fe_ops,
+               .dynamic = 1,
+               .dpcm_capture = 1,
+       },
+       [DAI_LINK_FE_PCM1_IN] = {
+               .name = "mt2701-cs42448-pcm1-data-UL",
+               .stream_name = "mt2701-cs42448-pcm1-data-UL",
+               .cpu_dai_name = "PCM1",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+                           SND_SOC_DPCM_TRIGGER_POST},
+               .ops = &mt2701_cs42448_48k_fe_ops,
+               .dynamic = 1,
+               .dpcm_capture = 1,
+       },
+       [DAI_LINK_FE_BT_OUT] = {
+               .name = "mt2701-cs42448-pcm-BT-out",
+               .stream_name = "mt2701-cs42448-pcm-BT",
+               .cpu_dai_name = "PCM_BT_DL",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+                           SND_SOC_DPCM_TRIGGER_POST},
+               .dynamic = 1,
+               .dpcm_playback = 1,
+       },
+       [DAI_LINK_FE_BT_IN] = {
+               .name = "mt2701-cs42448-pcm-BT-in",
+               .stream_name = "mt2701-cs42448-pcm-BT",
+               .cpu_dai_name = "PCM_BT_UL",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+                           SND_SOC_DPCM_TRIGGER_POST},
+               .dynamic = 1,
+               .dpcm_capture = 1,
+       },
+       /* BE */
+       [DAI_LINK_BE_I2S0] = {
+               .name = "mt2701-cs42448-I2S0",
+               .cpu_dai_name = "I2S0",
+               .no_pcm = 1,
+               .codec_dai_name = "cs42448",
+               .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+                        | SND_SOC_DAIFMT_GATED,
+               .ops = &mt2701_cs42448_be_ops,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+       },
+       [DAI_LINK_BE_I2S1] = {
+               .name = "mt2701-cs42448-I2S1",
+               .cpu_dai_name = "I2S1",
+               .no_pcm = 1,
+               .codec_dai_name = "cs42448",
+               .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+                        | SND_SOC_DAIFMT_GATED,
+               .ops = &mt2701_cs42448_be_ops,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+       },
+       [DAI_LINK_BE_I2S2] = {
+               .name = "mt2701-cs42448-I2S2",
+               .cpu_dai_name = "I2S2",
+               .no_pcm = 1,
+               .codec_dai_name = "cs42448",
+               .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+                        | SND_SOC_DAIFMT_GATED,
+               .ops = &mt2701_cs42448_be_ops,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+       },
+       [DAI_LINK_BE_I2S3] = {
+               .name = "mt2701-cs42448-I2S3",
+               .cpu_dai_name = "I2S3",
+               .no_pcm = 1,
+               .codec_dai_name = "cs42448",
+               .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+                        | SND_SOC_DAIFMT_GATED,
+               .ops = &mt2701_cs42448_be_ops,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+       },
+       [DAI_LINK_BE_MRG_BT] = {
+               .name = "mt2701-cs42448-MRG-BT",
+               .cpu_dai_name = "MRG BT",
+               .no_pcm = 1,
+               .codec_dai_name = "bt-sco-pcm-wb",
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+       },
+};
+
+static struct snd_soc_card mt2701_cs42448_soc_card = {
+       .name = "mt2701-cs42448",
+       .owner = THIS_MODULE,
+       .dai_link = mt2701_cs42448_dai_links,
+       .num_links = ARRAY_SIZE(mt2701_cs42448_dai_links),
+       .controls = mt2701_cs42448_controls,
+       .num_controls = ARRAY_SIZE(mt2701_cs42448_controls),
+       .dapm_widgets = mt2701_cs42448_asoc_card_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(mt2701_cs42448_asoc_card_dapm_widgets),
+};
+
+static int mt2701_cs42448_machine_probe(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = &mt2701_cs42448_soc_card;
+       int ret;
+       int i;
+       struct device_node *platform_node, *codec_node, *codec_node_bt_mrg;
+       struct mt2701_cs42448_private *priv =
+               devm_kzalloc(&pdev->dev, sizeof(struct mt2701_cs42448_private),
+                            GFP_KERNEL);
+       struct device *dev = &pdev->dev;
+
+       if (!priv)
+               return -ENOMEM;
+
+       platform_node = of_parse_phandle(pdev->dev.of_node,
+                                        "mediatek,platform", 0);
+       if (!platform_node) {
+               dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+               return -EINVAL;
+       }
+       for (i = 0; i < card->num_links; i++) {
+               if (mt2701_cs42448_dai_links[i].platform_name)
+                       continue;
+               mt2701_cs42448_dai_links[i].platform_of_node = platform_node;
+       }
+
+       card->dev = dev;
+
+       codec_node = of_parse_phandle(pdev->dev.of_node,
+                                     "mediatek,audio-codec", 0);
+       if (!codec_node) {
+               dev_err(&pdev->dev,
+                       "Property 'audio-codec' missing or invalid\n");
+               return -EINVAL;
+       }
+       for (i = 0; i < card->num_links; i++) {
+               if (mt2701_cs42448_dai_links[i].codec_name)
+                       continue;
+               mt2701_cs42448_dai_links[i].codec_of_node = codec_node;
+       }
+
+       codec_node_bt_mrg = of_parse_phandle(pdev->dev.of_node,
+                                            "mediatek,audio-codec-bt-mrg", 0);
+       if (!codec_node_bt_mrg) {
+               dev_err(&pdev->dev,
+                       "Property 'audio-codec-bt-mrg' missing or invalid\n");
+               return -EINVAL;
+       }
+       mt2701_cs42448_dai_links[DAI_LINK_BE_MRG_BT].codec_of_node
+                                                       = codec_node_bt_mrg;
+
+       ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
+       if (ret) {
+               dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);
+               return ret;
+       }
+
+       priv->i2s1_in_mux_gpio_sel_1 =
+               of_get_named_gpio(dev->of_node, "i2s1-in-sel-gpio1", 0);
+       if (gpio_is_valid(priv->i2s1_in_mux_gpio_sel_1)) {
+               ret = devm_gpio_request(dev, priv->i2s1_in_mux_gpio_sel_1,
+                                       "i2s1_in_mux_gpio_sel_1");
+               if (ret)
+                       dev_warn(&pdev->dev, "%s devm_gpio_request fail %d\n",
+                                __func__, ret);
+               gpio_direction_output(priv->i2s1_in_mux_gpio_sel_1, 0);
+       }
+
+       priv->i2s1_in_mux_gpio_sel_2 =
+               of_get_named_gpio(dev->of_node, "i2s1-in-sel-gpio2", 0);
+       if (gpio_is_valid(priv->i2s1_in_mux_gpio_sel_2)) {
+               ret = devm_gpio_request(dev, priv->i2s1_in_mux_gpio_sel_2,
+                                       "i2s1_in_mux_gpio_sel_2");
+               if (ret)
+                       dev_warn(&pdev->dev, "%s devm_gpio_request fail2 %d\n",
+                                __func__, ret);
+               gpio_direction_output(priv->i2s1_in_mux_gpio_sel_2, 0);
+       }
+       snd_soc_card_set_drvdata(card, priv);
+
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
+
+       if (ret)
+               dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
+                       __func__, ret);
+       return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id mt2701_cs42448_machine_dt_match[] = {
+       {.compatible = "mediatek,mt2701-cs42448-machine",},
+       {}
+};
+#endif
+
+static struct platform_driver mt2701_cs42448_machine = {
+       .driver = {
+               .name = "mt2701-cs42448",
+                  #ifdef CONFIG_OF
+                  .of_match_table = mt2701_cs42448_machine_dt_match,
+                  #endif
+       },
+       .probe = mt2701_cs42448_machine_probe,
+};
+
+module_platform_driver(mt2701_cs42448_machine);
+
+/* Module information */
+MODULE_DESCRIPTION("MT2701 CS42448 ALSA SoC machine driver");
+MODULE_AUTHOR("Ir Lian <ir.lian@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("mt2701 cs42448 soc card");
diff --git a/sound/soc/mediatek/mt2701/mt2701-reg.h b/sound/soc/mediatek/mt2701/mt2701-reg.h
new file mode 100644 (file)
index 0000000..bb62b1c
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * mt2701-reg.h  --  Mediatek 2701 audio driver reg definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT2701_REG_H_
+#define _MT2701_REG_H_
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include "mt2701-afe-common.h"
+
+/*****************************************************************************
+ *                  R E G I S T E R       D E F I N I T I O N
+ *****************************************************************************/
+#define AUDIO_TOP_CON0 0x0000
+#define AUDIO_TOP_CON4 0x0010
+#define AUDIO_TOP_CON5 0x0014
+#define AFE_DAIBT_CON0 0x001c
+#define AFE_MRGIF_CON 0x003c
+#define ASMI_TIMING_CON1 0x0100
+#define ASMO_TIMING_CON1 0x0104
+#define PWR1_ASM_CON1 0x0108
+#define ASYS_TOP_CON 0x0600
+#define ASYS_I2SIN1_CON 0x0604
+#define ASYS_I2SIN2_CON 0x0608
+#define ASYS_I2SIN3_CON 0x060c
+#define ASYS_I2SIN4_CON 0x0610
+#define ASYS_I2SIN5_CON 0x0614
+#define ASYS_I2SO1_CON 0x061C
+#define ASYS_I2SO2_CON 0x0620
+#define ASYS_I2SO3_CON 0x0624
+#define ASYS_I2SO4_CON 0x0628
+#define ASYS_I2SO5_CON 0x062c
+#define PWR2_TOP_CON 0x0634
+#define AFE_CONN0 0x06c0
+#define AFE_CONN1 0x06c4
+#define AFE_CONN2 0x06c8
+#define AFE_CONN3 0x06cc
+#define AFE_CONN14 0x06f8
+#define AFE_CONN15 0x06fc
+#define AFE_CONN16 0x0700
+#define AFE_CONN17 0x0704
+#define AFE_CONN18 0x0708
+#define AFE_CONN19 0x070c
+#define AFE_CONN20 0x0710
+#define AFE_CONN21 0x0714
+#define AFE_CONN22 0x0718
+#define AFE_CONN23 0x071c
+#define AFE_CONN24 0x0720
+#define AFE_CONN41 0x0764
+#define ASYS_IRQ1_CON 0x0780
+#define ASYS_IRQ2_CON 0x0784
+#define ASYS_IRQ3_CON 0x0788
+#define ASYS_IRQ_CLR 0x07c0
+#define ASYS_IRQ_STATUS 0x07c4
+#define PWR2_ASM_CON1 0x1070
+#define AFE_DAC_CON0 0x1200
+#define AFE_DAC_CON1 0x1204
+#define AFE_DAC_CON2 0x1208
+#define AFE_DAC_CON3 0x120c
+#define AFE_DAC_CON4 0x1210
+#define AFE_MEMIF_HD_CON1 0x121c
+#define AFE_MEMIF_PBUF_SIZE 0x1238
+#define AFE_MEMIF_HD_CON0 0x123c
+#define AFE_DL1_BASE 0x1240
+#define AFE_DL1_CUR 0x1244
+#define AFE_DL2_BASE 0x1250
+#define AFE_DL2_CUR 0x1254
+#define AFE_DL3_BASE 0x1260
+#define AFE_DL3_CUR 0x1264
+#define AFE_DL4_BASE 0x1270
+#define AFE_DL4_CUR 0x1274
+#define AFE_DL5_BASE 0x1280
+#define AFE_DL5_CUR 0x1284
+#define AFE_DLMCH_BASE 0x12a0
+#define AFE_DLMCH_CUR 0x12a4
+#define AFE_ARB1_BASE 0x12b0
+#define AFE_ARB1_CUR 0x12b4
+#define AFE_VUL_BASE 0x1300
+#define AFE_VUL_CUR 0x130c
+#define AFE_UL2_BASE 0x1310
+#define AFE_UL2_END 0x1318
+#define AFE_UL2_CUR 0x131c
+#define AFE_UL3_BASE 0x1320
+#define AFE_UL3_END 0x1328
+#define AFE_UL3_CUR 0x132c
+#define AFE_UL4_BASE 0x1330
+#define AFE_UL4_END 0x1338
+#define AFE_UL4_CUR 0x133c
+#define AFE_UL5_BASE 0x1340
+#define AFE_UL5_END 0x1348
+#define AFE_UL5_CUR 0x134c
+#define AFE_DAI_BASE 0x1370
+#define AFE_DAI_CUR 0x137c
+
+/* AUDIO_TOP_CON0 (0x0000) */
+#define AUDIO_TOP_CON0_A1SYS_A2SYS_ON  (0x3 << 0)
+#define AUDIO_TOP_CON0_PDN_AFE         (0x1 << 2)
+#define AUDIO_TOP_CON0_PDN_APLL_CK     (0x1 << 23)
+
+/* AUDIO_TOP_CON4 (0x0010) */
+#define AUDIO_TOP_CON4_I2SO1_PWN       (0x1 << 6)
+#define AUDIO_TOP_CON4_PDN_A1SYS       (0x1 << 21)
+#define AUDIO_TOP_CON4_PDN_A2SYS       (0x1 << 22)
+#define AUDIO_TOP_CON4_PDN_AFE_CONN    (0x1 << 23)
+#define AUDIO_TOP_CON4_PDN_MRGIF       (0x1 << 25)
+
+/* AFE_DAIBT_CON0 (0x001c) */
+#define AFE_DAIBT_CON0_DAIBT_EN                (0x1 << 0)
+#define AFE_DAIBT_CON0_BT_FUNC_EN      (0x1 << 1)
+#define AFE_DAIBT_CON0_BT_FUNC_RDY     (0x1 << 3)
+#define AFE_DAIBT_CON0_BT_WIDE_MODE_EN (0x1 << 9)
+#define AFE_DAIBT_CON0_MRG_USE         (0x1 << 12)
+
+/* PWR1_ASM_CON1 (0x0108) */
+#define PWR1_ASM_CON1_INIT_VAL         (0x492)
+
+/* AFE_MRGIF_CON (0x003c) */
+#define AFE_MRGIF_CON_MRG_EN           (0x1 << 0)
+#define AFE_MRGIF_CON_MRG_I2S_EN       (0x1 << 16)
+#define AFE_MRGIF_CON_I2S_MODE_MASK    (0xf << 20)
+#define AFE_MRGIF_CON_I2S_MODE_32K     (0x4 << 20)
+
+/* ASYS_I2SO1_CON (0x061c) */
+#define ASYS_I2SO1_CON_FS              (0x1f << 8)
+#define ASYS_I2SO1_CON_FS_SET(x)       ((x) << 8)
+#define ASYS_I2SO1_CON_MULTI_CH                (0x1 << 16)
+#define ASYS_I2SO1_CON_SIDEGEN         (0x1 << 30)
+#define ASYS_I2SO1_CON_I2S_EN          (0x1 << 0)
+/* 0:EIAJ 1:I2S */
+#define ASYS_I2SO1_CON_I2S_MODE                (0x1 << 3)
+#define ASYS_I2SO1_CON_WIDE_MODE       (0x1 << 1)
+#define ASYS_I2SO1_CON_WIDE_MODE_SET(x)        ((x) << 1)
+
+/* PWR2_TOP_CON (0x0634) */
+#define PWR2_TOP_CON_INIT_VAL          (0xffe1ffff)
+
+/* ASYS_IRQ_CLR (0x07c0) */
+#define ASYS_IRQ_CLR_ALL               (0xffffffff)
+
+/* PWR2_ASM_CON1 (0x1070) */
+#define PWR2_ASM_CON1_INIT_VAL         (0x492492)
+
+/* AFE_DAC_CON0 (0x1200) */
+#define AFE_DAC_CON0_AFE_ON            (0x1 << 0)
+
+/* AFE_MEMIF_PBUF_SIZE (0x1238) */
+#define AFE_MEMIF_PBUF_SIZE_DLM_MASK           (0x1 << 29)
+#define AFE_MEMIF_PBUF_SIZE_PAIR_INTERLEAVE    (0x0 << 29)
+#define AFE_MEMIF_PBUF_SIZE_FULL_INTERLEAVE    (0x1 << 29)
+#define DLMCH_BIT_WIDTH_MASK                   (0x1 << 28)
+#define AFE_MEMIF_PBUF_SIZE_DLM_CH_MASK                (0xf << 24)
+#define AFE_MEMIF_PBUF_SIZE_DLM_CH(x)          ((x) << 24)
+#define AFE_MEMIF_PBUF_SIZE_DLM_BYTE_MASK      (0x3 << 12)
+#define AFE_MEMIF_PBUF_SIZE_DLM_32BYTES                (0x1 << 12)
+
+/* I2S in/out register bit control */
+#define ASYS_I2S_CON_FS                        (0x1f << 8)
+#define ASYS_I2S_CON_FS_SET(x)         ((x) << 8)
+#define ASYS_I2S_CON_RESET             (0x1 << 30)
+#define ASYS_I2S_CON_I2S_EN            (0x1 << 0)
+#define ASYS_I2S_CON_I2S_COUPLE_MODE   (0x1 << 17)
+/* 0:EIAJ 1:I2S */
+#define ASYS_I2S_CON_I2S_MODE          (0x1 << 3)
+#define ASYS_I2S_CON_WIDE_MODE         (0x1 << 1)
+#define ASYS_I2S_CON_WIDE_MODE_SET(x)  ((x) << 1)
+#define ASYS_I2S_IN_PHASE_FIX          (0x1 << 31)
+
+#define AFE_END_ADDR 0x15e0
+#endif
diff --git a/sound/soc/mediatek/mt8173/Makefile b/sound/soc/mediatek/mt8173/Makefile
new file mode 100644 (file)
index 0000000..0357b27
--- /dev/null
@@ -0,0 +1,7 @@
+# MTK Platform Support
+obj-$(CONFIG_SND_SOC_MT8173) += mt8173-afe-pcm.o
+# Machine support
+obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o
+obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173-rt5650.o
+obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173-rt5650-rt5514.o
+obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o
diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-common.h b/sound/soc/mediatek/mt8173/mt8173-afe-common.h
new file mode 100644 (file)
index 0000000..9a4837c
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * mt8173_afe_common.h  --  Mediatek 8173 audio driver common definitions
+ *
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author: Koro Chen <koro.chen@mediatek.com>
+ *             Sascha Hauer <s.hauer@pengutronix.de>
+ *             Hidalgo Huang <hidalgo.huang@mediatek.com>
+ *             Ir Lian <ir.lian@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT8173_AFE_COMMON_H_
+#define _MT8173_AFE_COMMON_H_
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+enum {
+       MT8173_AFE_MEMIF_DL1,
+       MT8173_AFE_MEMIF_DL2,
+       MT8173_AFE_MEMIF_VUL,
+       MT8173_AFE_MEMIF_DAI,
+       MT8173_AFE_MEMIF_AWB,
+       MT8173_AFE_MEMIF_MOD_DAI,
+       MT8173_AFE_MEMIF_HDMI,
+       MT8173_AFE_MEMIF_NUM,
+       MT8173_AFE_IO_MOD_PCM1 = MT8173_AFE_MEMIF_NUM,
+       MT8173_AFE_IO_MOD_PCM2,
+       MT8173_AFE_IO_PMIC,
+       MT8173_AFE_IO_I2S,
+       MT8173_AFE_IO_2ND_I2S,
+       MT8173_AFE_IO_HW_GAIN1,
+       MT8173_AFE_IO_HW_GAIN2,
+       MT8173_AFE_IO_MRG_O,
+       MT8173_AFE_IO_MRG_I,
+       MT8173_AFE_IO_DAIBT,
+       MT8173_AFE_IO_HDMI,
+};
+
+enum {
+       MT8173_AFE_IRQ_DL1,
+       MT8173_AFE_IRQ_DL2,
+       MT8173_AFE_IRQ_VUL,
+       MT8173_AFE_IRQ_DAI,
+       MT8173_AFE_IRQ_AWB,
+       MT8173_AFE_IRQ_MOD_DAI,
+       MT8173_AFE_IRQ_HDMI,
+       MT8173_AFE_IRQ_NUM,
+};
+
+enum {
+       MT8173_CLK_INFRASYS_AUD,
+       MT8173_CLK_TOP_PDN_AUD,
+       MT8173_CLK_TOP_PDN_AUD_BUS,
+       MT8173_CLK_I2S0_M,
+       MT8173_CLK_I2S1_M,
+       MT8173_CLK_I2S2_M,
+       MT8173_CLK_I2S3_M,
+       MT8173_CLK_I2S3_B,
+       MT8173_CLK_BCK0,
+       MT8173_CLK_BCK1,
+       MT8173_CLK_NUM
+};
+
+#endif
similarity index 51%
rename from sound/soc/mediatek/mtk-afe-pcm.c
rename to sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
index 2b5df2ef51a332d99bd2182c0f88a0b58e1cbfb3..8a643a35d3d4c8c2ae0c418231ee85772d0e995c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Mediatek ALSA SoC AFE platform driver
+ * Mediatek 8173 ALSA SoC AFE platform driver
  *
  * Copyright (c) 2015 MediaTek Inc.
  * Author: Koro Chen <koro.chen@mediatek.com>
 #include <linux/dma-mapping.h>
 #include <linux/pm_runtime.h>
 #include <sound/soc.h>
-#include "mtk-afe-common.h"
+#include "mt8173-afe-common.h"
+#include "../common/mtk-base-afe.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-afe-fe-dai.h"
 
 /*****************************************************************************
  *                  R E G I S T E R       D E F I N I T I O N
@@ -81,7 +84,6 @@
 #define AFE_TDM_CON1           0x0548
 #define AFE_TDM_CON2           0x054c
 
-#define AFE_BASE_END_OFFSET    8
 #define AFE_IRQ_STATUS_BITS    0xff
 
 /* AUDIO_TOP_CON0 (0x0000) */
@@ -135,7 +137,7 @@ enum afe_tdm_ch_start {
        AFE_TDM_CH_ZERO,
 };
 
-static const unsigned int mtk_afe_backup_list[] = {
+static const unsigned int mt8173_afe_backup_list[] = {
        AUDIO_TOP_CON0,
        AFE_CONN1,
        AFE_CONN2,
@@ -152,18 +154,11 @@ static const unsigned int mtk_afe_backup_list[] = {
        AFE_DAC_CON0,
 };
 
-struct mtk_afe {
-       /* address for ioremap audio hardware register */
-       void __iomem *base_addr;
-       struct device *dev;
-       struct regmap *regmap;
-       struct mtk_afe_memif memif[MTK_AFE_MEMIF_NUM];
-       struct clk *clocks[MTK_CLK_NUM];
-       unsigned int backup_regs[ARRAY_SIZE(mtk_afe_backup_list)];
-       bool suspended;
+struct mt8173_afe_private {
+       struct clk *clocks[MT8173_CLK_NUM];
 };
 
-static const struct snd_pcm_hardware mtk_afe_hardware = {
+static const struct snd_pcm_hardware mt8173_afe_hardware = {
        .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
                 SNDRV_PCM_INFO_MMAP_VALID),
        .buffer_bytes_max = 256 * 1024,
@@ -174,59 +169,12 @@ static const struct snd_pcm_hardware mtk_afe_hardware = {
        .fifo_size = 0,
 };
 
-static snd_pcm_uframes_t mtk_afe_pcm_pointer
-                        (struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-       struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-       unsigned int hw_ptr;
-       int ret;
-
-       ret = regmap_read(afe->regmap, memif->data->reg_ofs_cur, &hw_ptr);
-       if (ret || hw_ptr == 0) {
-               dev_err(afe->dev, "%s hw_ptr err\n", __func__);
-               hw_ptr = memif->phys_buf_addr;
-       }
-
-       return bytes_to_frames(substream->runtime,
-                              hw_ptr - memif->phys_buf_addr);
-}
-
-static const struct snd_pcm_ops mtk_afe_pcm_ops = {
-       .ioctl = snd_pcm_lib_ioctl,
-       .pointer = mtk_afe_pcm_pointer,
-};
-
-static int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
-       size_t size;
-       struct snd_card *card = rtd->card->snd_card;
-       struct snd_pcm *pcm = rtd->pcm;
-
-       size = mtk_afe_hardware.buffer_bytes_max;
-
-       return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
-                                                    card->dev, size, size);
-}
-
-static void mtk_afe_pcm_free(struct snd_pcm *pcm)
-{
-       snd_pcm_lib_preallocate_free_for_all(pcm);
-}
-
-static const struct snd_soc_platform_driver mtk_afe_pcm_platform = {
-       .ops = &mtk_afe_pcm_ops,
-       .pcm_new = mtk_afe_pcm_new,
-       .pcm_free = mtk_afe_pcm_free,
-};
-
-struct mtk_afe_rate {
+struct mt8173_afe_rate {
        unsigned int rate;
        unsigned int regvalue;
 };
 
-static const struct mtk_afe_rate mtk_afe_i2s_rates[] = {
+static const struct mt8173_afe_rate mt8173_afe_i2s_rates[] = {
        { .rate = 8000, .regvalue = 0 },
        { .rate = 11025, .regvalue = 1 },
        { .rate = 12000, .regvalue = 2 },
@@ -242,21 +190,21 @@ static const struct mtk_afe_rate mtk_afe_i2s_rates[] = {
        { .rate = 192000, .regvalue = 14 },
 };
 
-static int mtk_afe_i2s_fs(unsigned int sample_rate)
+static int mt8173_afe_i2s_fs(unsigned int sample_rate)
 {
        int i;
 
-       for (i = 0; i < ARRAY_SIZE(mtk_afe_i2s_rates); i++)
-               if (mtk_afe_i2s_rates[i].rate == sample_rate)
-                       return mtk_afe_i2s_rates[i].regvalue;
+       for (i = 0; i < ARRAY_SIZE(mt8173_afe_i2s_rates); i++)
+               if (mt8173_afe_i2s_rates[i].rate == sample_rate)
+                       return mt8173_afe_i2s_rates[i].regvalue;
 
        return -EINVAL;
 }
 
-static int mtk_afe_set_i2s(struct mtk_afe *afe, unsigned int rate)
+static int mt8173_afe_set_i2s(struct mtk_base_afe *afe, unsigned int rate)
 {
        unsigned int val;
-       int fs = mtk_afe_i2s_fs(rate);
+       int fs = mt8173_afe_i2s_fs(rate);
 
        if (fs < 0)
                return -EINVAL;
@@ -281,7 +229,7 @@ static int mtk_afe_set_i2s(struct mtk_afe *afe, unsigned int rate)
        return 0;
 }
 
-static void mtk_afe_set_i2s_enable(struct mtk_afe *afe, bool enable)
+static void mt8173_afe_set_i2s_enable(struct mtk_base_afe *afe, bool enable)
 {
        unsigned int val;
 
@@ -296,8 +244,8 @@ static void mtk_afe_set_i2s_enable(struct mtk_afe *afe, bool enable)
        regmap_update_bits(afe->regmap, AFE_I2S_CON1, 0x1, enable);
 }
 
-static int mtk_afe_dais_enable_clks(struct mtk_afe *afe,
-                                   struct clk *m_ck, struct clk *b_ck)
+static int mt8173_afe_dais_enable_clks(struct mtk_base_afe *afe,
+                                      struct clk *m_ck, struct clk *b_ck)
 {
        int ret;
 
@@ -319,9 +267,9 @@ static int mtk_afe_dais_enable_clks(struct mtk_afe *afe,
        return 0;
 }
 
-static int mtk_afe_dais_set_clks(struct mtk_afe *afe,
-                                struct clk *m_ck, unsigned int mck_rate,
-                                struct clk *b_ck, unsigned int bck_rate)
+static int mt8173_afe_dais_set_clks(struct mtk_base_afe *afe,
+                                   struct clk *m_ck, unsigned int mck_rate,
+                                   struct clk *b_ck, unsigned int bck_rate)
 {
        int ret;
 
@@ -343,8 +291,8 @@ static int mtk_afe_dais_set_clks(struct mtk_afe *afe,
        return 0;
 }
 
-static void mtk_afe_dais_disable_clks(struct mtk_afe *afe,
-                                     struct clk *m_ck, struct clk *b_ck)
+static void mt8173_afe_dais_disable_clks(struct mtk_base_afe *afe,
+                                        struct clk *m_ck, struct clk *b_ck)
 {
        if (m_ck)
                clk_disable_unprepare(m_ck);
@@ -352,102 +300,101 @@ static void mtk_afe_dais_disable_clks(struct mtk_afe *afe,
                clk_disable_unprepare(b_ck);
 }
 
-static int mtk_afe_i2s_startup(struct snd_pcm_substream *substream,
-                              struct snd_soc_dai *dai)
+static int mt8173_afe_i2s_startup(struct snd_pcm_substream *substream,
+                                 struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
        if (dai->active)
                return 0;
 
-       mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL);
-       mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S2_M], NULL);
        regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
                           AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, 0);
        return 0;
 }
 
-static void mtk_afe_i2s_shutdown(struct snd_pcm_substream *substream,
-                                struct snd_soc_dai *dai)
+static void mt8173_afe_i2s_shutdown(struct snd_pcm_substream *substream,
+                                   struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
        if (dai->active)
                return;
 
-       mtk_afe_set_i2s_enable(afe, false);
+       mt8173_afe_set_i2s_enable(afe, false);
        regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
                           AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M,
                           AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M);
-       mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL);
-       mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S2_M], NULL);
 }
 
-static int mtk_afe_i2s_prepare(struct snd_pcm_substream *substream,
-                              struct snd_soc_dai *dai)
+static int mt8173_afe_i2s_prepare(struct snd_pcm_substream *substream,
+                                 struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_pcm_runtime * const runtime = substream->runtime;
-       struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mt8173_afe_private *afe_priv = afe->platform_priv;
        int ret;
 
-       mtk_afe_dais_set_clks(afe,
-                             afe->clocks[MTK_CLK_I2S1_M], runtime->rate * 256,
-                             NULL, 0);
-       mtk_afe_dais_set_clks(afe,
-                             afe->clocks[MTK_CLK_I2S2_M], runtime->rate * 256,
-                             NULL, 0);
+       mt8173_afe_dais_set_clks(afe, afe_priv->clocks[MT8173_CLK_I2S1_M],
+                                runtime->rate * 256, NULL, 0);
+       mt8173_afe_dais_set_clks(afe, afe_priv->clocks[MT8173_CLK_I2S2_M],
+                                runtime->rate * 256, NULL, 0);
        /* config I2S */
-       ret = mtk_afe_set_i2s(afe, substream->runtime->rate);
+       ret = mt8173_afe_set_i2s(afe, substream->runtime->rate);
        if (ret)
                return ret;
 
-       mtk_afe_set_i2s_enable(afe, true);
+       mt8173_afe_set_i2s_enable(afe, true);
 
        return 0;
 }
 
-static int mtk_afe_hdmi_startup(struct snd_pcm_substream *substream,
-                               struct snd_soc_dai *dai)
+static int mt8173_afe_hdmi_startup(struct snd_pcm_substream *substream,
+                                  struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mt8173_afe_private *afe_priv = afe->platform_priv;
 
        if (dai->active)
                return 0;
 
-       mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S3_M],
-                                afe->clocks[MTK_CLK_I2S3_B]);
+       mt8173_afe_dais_enable_clks(afe, afe_priv->clocks[MT8173_CLK_I2S3_M],
+                                   afe_priv->clocks[MT8173_CLK_I2S3_B]);
        return 0;
 }
 
-static void mtk_afe_hdmi_shutdown(struct snd_pcm_substream *substream,
-                                 struct snd_soc_dai *dai)
+static void mt8173_afe_hdmi_shutdown(struct snd_pcm_substream *substream,
+                                    struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mt8173_afe_private *afe_priv = afe->platform_priv;
 
        if (dai->active)
                return;
 
-       mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S3_M],
-                                 afe->clocks[MTK_CLK_I2S3_B]);
+       mt8173_afe_dais_disable_clks(afe, afe_priv->clocks[MT8173_CLK_I2S3_M],
+                                    afe_priv->clocks[MT8173_CLK_I2S3_B]);
 }
 
-static int mtk_afe_hdmi_prepare(struct snd_pcm_substream *substream,
-                               struct snd_soc_dai *dai)
+static int mt8173_afe_hdmi_prepare(struct snd_pcm_substream *substream,
+                                  struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_pcm_runtime * const runtime = substream->runtime;
-       struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mt8173_afe_private *afe_priv = afe->platform_priv;
+
        unsigned int val;
 
-       mtk_afe_dais_set_clks(afe,
-                             afe->clocks[MTK_CLK_I2S3_M], runtime->rate * 128,
-                             afe->clocks[MTK_CLK_I2S3_B],
-                             runtime->rate * runtime->channels * 32);
+       mt8173_afe_dais_set_clks(afe, afe_priv->clocks[MT8173_CLK_I2S3_M],
+                                runtime->rate * 128,
+                                afe_priv->clocks[MT8173_CLK_I2S3_B],
+                                runtime->rate * runtime->channels * 32);
 
        val = AFE_TDM_CON1_BCK_INV |
              AFE_TDM_CON1_LRCK_INV |
@@ -498,11 +445,11 @@ static int mtk_afe_hdmi_prepare(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int mtk_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
-                               struct snd_soc_dai *dai)
+static int mt8173_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
+                                  struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
        dev_info(afe->dev, "%s cmd=%d %s\n", __func__, cmd, dai->name);
 
@@ -514,10 +461,14 @@ static int mtk_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
 
                /* set connections:  O30~O37: L/R/LS/RS/C/LFE/CH7/CH8 */
                regmap_write(afe->regmap, AFE_HDMI_CONN0,
-                            AFE_HDMI_CONN0_O30_I30 | AFE_HDMI_CONN0_O31_I31 |
-                            AFE_HDMI_CONN0_O32_I34 | AFE_HDMI_CONN0_O33_I35 |
-                            AFE_HDMI_CONN0_O34_I32 | AFE_HDMI_CONN0_O35_I33 |
-                            AFE_HDMI_CONN0_O36_I36 | AFE_HDMI_CONN0_O37_I37);
+                                AFE_HDMI_CONN0_O30_I30 |
+                                AFE_HDMI_CONN0_O31_I31 |
+                                AFE_HDMI_CONN0_O32_I34 |
+                                AFE_HDMI_CONN0_O33_I35 |
+                                AFE_HDMI_CONN0_O34_I32 |
+                                AFE_HDMI_CONN0_O35_I33 |
+                                AFE_HDMI_CONN0_O36_I36 |
+                                AFE_HDMI_CONN0_O37_I37);
 
                /* enable Out control */
                regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0x1);
@@ -537,284 +488,65 @@ static int mtk_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
                regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
                                   AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF,
                                   AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF);
-
                return 0;
        default:
                return -EINVAL;
        }
 }
 
-static int mtk_afe_dais_startup(struct snd_pcm_substream *substream,
-                               struct snd_soc_dai *dai)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-       int ret;
-
-       memif->substream = substream;
-
-       snd_soc_set_runtime_hwparams(substream, &mtk_afe_hardware);
-
-       /*
-        * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be
-        * smaller than period_size due to AFE's internal buffer.
-        * This easily leads to overrun when avail_min is period_size.
-        * One more period can hold the possible unread buffer.
-        */
-       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-               ret = snd_pcm_hw_constraint_minmax(runtime,
-                                                  SNDRV_PCM_HW_PARAM_PERIODS,
-                                                  3,
-                                                  mtk_afe_hardware.periods_max);
-               if (ret < 0) {
-                       dev_err(afe->dev, "hw_constraint_minmax failed\n");
-                       return ret;
-               }
-       }
-       ret = snd_pcm_hw_constraint_integer(runtime,
-                                           SNDRV_PCM_HW_PARAM_PERIODS);
-       if (ret < 0)
-               dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
-       return ret;
-}
-
-static void mtk_afe_dais_shutdown(struct snd_pcm_substream *substream,
-                                 struct snd_soc_dai *dai)
+static int mt8173_memif_fs(struct snd_pcm_substream *substream,
+                          unsigned int rate)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-       struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-
-       memif->substream = NULL;
-}
-
-static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
-                                 struct snd_pcm_hw_params *params,
-                                 struct snd_soc_dai *dai)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-       struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-       int msb_at_bit33 = 0;
-       int ret;
-
-       dev_dbg(afe->dev,
-               "%s period = %u, rate= %u, channels=%u\n",
-               __func__, params_period_size(params), params_rate(params),
-               params_channels(params));
+       struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+       struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+       int fs;
 
-       ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
-       if (ret < 0)
-               return ret;
-
-       msb_at_bit33 = upper_32_bits(substream->runtime->dma_addr) ? 1 : 0;
-       memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr);
-       memif->buffer_size = substream->runtime->dma_bytes;
-
-       /* start */
-       regmap_write(afe->regmap,
-                    memif->data->reg_ofs_base, memif->phys_buf_addr);
-       /* end */
-       regmap_write(afe->regmap,
-                    memif->data->reg_ofs_base + AFE_BASE_END_OFFSET,
-                    memif->phys_buf_addr + memif->buffer_size - 1);
-
-       /* set MSB to 33-bit */
-       regmap_update_bits(afe->regmap, AFE_MEMIF_MSB,
-                          1 << memif->data->msb_shift,
-                          msb_at_bit33 << memif->data->msb_shift);
-
-       /* set channel */
-       if (memif->data->mono_shift >= 0) {
-               unsigned int mono = (params_channels(params) == 1) ? 1 : 0;
-
-               regmap_update_bits(afe->regmap, AFE_DAC_CON1,
-                                  1 << memif->data->mono_shift,
-                                  mono << memif->data->mono_shift);
-       }
-
-       /* set rate */
-       if (memif->data->fs_shift < 0)
-               return 0;
-       if (memif->data->id == MTK_AFE_MEMIF_DAI ||
-           memif->data->id == MTK_AFE_MEMIF_MOD_DAI) {
-               unsigned int val;
-
-               switch (params_rate(params)) {
+       if (memif->data->id == MT8173_AFE_MEMIF_DAI ||
+           memif->data->id == MT8173_AFE_MEMIF_MOD_DAI) {
+               switch (rate) {
                case 8000:
-                       val = 0;
+                       fs = 0;
                        break;
                case 16000:
-                       val = 1;
+                       fs = 1;
                        break;
                case 32000:
-                       val = 2;
+                       fs = 2;
                        break;
                default:
                        return -EINVAL;
                }
-
-               if (memif->data->id == MTK_AFE_MEMIF_DAI)
-                       regmap_update_bits(afe->regmap, AFE_DAC_CON0,
-                                          0x3 << memif->data->fs_shift,
-                                          val << memif->data->fs_shift);
-               else
-                       regmap_update_bits(afe->regmap, AFE_DAC_CON1,
-                                          0x3 << memif->data->fs_shift,
-                                          val << memif->data->fs_shift);
-
        } else {
-               int fs = mtk_afe_i2s_fs(params_rate(params));
-
-               if (fs < 0)
-                       return -EINVAL;
-
-               regmap_update_bits(afe->regmap, AFE_DAC_CON1,
-                                  0xf << memif->data->fs_shift,
-                                  fs << memif->data->fs_shift);
+               fs = mt8173_afe_i2s_fs(rate);
        }
-
-       return 0;
+       return fs;
 }
 
-static int mtk_afe_dais_hw_free(struct snd_pcm_substream *substream,
-                               struct snd_soc_dai *dai)
+static int mt8173_irq_fs(struct snd_pcm_substream *substream, unsigned int rate)
 {
-       return snd_pcm_lib_free_pages(substream);
+       return mt8173_afe_i2s_fs(rate);
 }
 
-static int mtk_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
-                               struct snd_soc_dai *dai)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_pcm_runtime * const runtime = substream->runtime;
-       struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-       struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-       unsigned int counter = runtime->period_size;
-
-       dev_info(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);
-
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-       case SNDRV_PCM_TRIGGER_RESUME:
-               if (memif->data->enable_shift >= 0)
-                       regmap_update_bits(afe->regmap, AFE_DAC_CON0,
-                                          1 << memif->data->enable_shift,
-                                          1 << memif->data->enable_shift);
-
-               /* set irq counter */
-               regmap_update_bits(afe->regmap,
-                                  memif->data->irq_reg_cnt,
-                                  0x3ffff << memif->data->irq_cnt_shift,
-                                  counter << memif->data->irq_cnt_shift);
-
-               /* set irq fs */
-               if (memif->data->irq_fs_shift >= 0) {
-                       int fs = mtk_afe_i2s_fs(runtime->rate);
-
-                       if (fs < 0)
-                               return -EINVAL;
-
-                       regmap_update_bits(afe->regmap,
-                                          AFE_IRQ_MCU_CON,
-                                          0xf << memif->data->irq_fs_shift,
-                                          fs << memif->data->irq_fs_shift);
-               }
-               /* enable interrupt */
-               regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON,
-                                  1 << memif->data->irq_en_shift,
-                                  1 << memif->data->irq_en_shift);
-
-               return 0;
-       case SNDRV_PCM_TRIGGER_STOP:
-       case SNDRV_PCM_TRIGGER_SUSPEND:
-               if (memif->data->enable_shift >= 0)
-                       regmap_update_bits(afe->regmap, AFE_DAC_CON0,
-                                          1 << memif->data->enable_shift, 0);
-               /* disable interrupt */
-               regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON,
-                                  1 << memif->data->irq_en_shift,
-                                  0 << memif->data->irq_en_shift);
-               /* and clear pending IRQ */
-               regmap_write(afe->regmap, AFE_IRQ_CLR,
-                            1 << memif->data->irq_clr_shift);
-               return 0;
-       default:
-               return -EINVAL;
-       }
-}
-
-/* FE DAIs */
-static const struct snd_soc_dai_ops mtk_afe_dai_ops = {
-       .startup        = mtk_afe_dais_startup,
-       .shutdown       = mtk_afe_dais_shutdown,
-       .hw_params      = mtk_afe_dais_hw_params,
-       .hw_free        = mtk_afe_dais_hw_free,
-       .trigger        = mtk_afe_dais_trigger,
-};
-
 /* BE DAIs */
-static const struct snd_soc_dai_ops mtk_afe_i2s_ops = {
-       .startup        = mtk_afe_i2s_startup,
-       .shutdown       = mtk_afe_i2s_shutdown,
-       .prepare        = mtk_afe_i2s_prepare,
+static const struct snd_soc_dai_ops mt8173_afe_i2s_ops = {
+       .startup        = mt8173_afe_i2s_startup,
+       .shutdown       = mt8173_afe_i2s_shutdown,
+       .prepare        = mt8173_afe_i2s_prepare,
 };
 
-static const struct snd_soc_dai_ops mtk_afe_hdmi_ops = {
-       .startup        = mtk_afe_hdmi_startup,
-       .shutdown       = mtk_afe_hdmi_shutdown,
-       .prepare        = mtk_afe_hdmi_prepare,
-       .trigger        = mtk_afe_hdmi_trigger,
-
+static const struct snd_soc_dai_ops mt8173_afe_hdmi_ops = {
+       .startup        = mt8173_afe_hdmi_startup,
+       .shutdown       = mt8173_afe_hdmi_shutdown,
+       .prepare        = mt8173_afe_hdmi_prepare,
+       .trigger        = mt8173_afe_hdmi_trigger,
 };
 
-static int mtk_afe_runtime_suspend(struct device *dev);
-static int mtk_afe_runtime_resume(struct device *dev);
-
-static int mtk_afe_dai_suspend(struct snd_soc_dai *dai)
-{
-       struct mtk_afe *afe = snd_soc_dai_get_drvdata(dai);
-       int i;
-
-       dev_dbg(afe->dev, "%s\n", __func__);
-       if (pm_runtime_status_suspended(afe->dev) || afe->suspended)
-               return 0;
-
-       for (i = 0; i < ARRAY_SIZE(mtk_afe_backup_list); i++)
-               regmap_read(afe->regmap, mtk_afe_backup_list[i],
-                           &afe->backup_regs[i]);
-
-       afe->suspended = true;
-       mtk_afe_runtime_suspend(afe->dev);
-       return 0;
-}
-
-static int mtk_afe_dai_resume(struct snd_soc_dai *dai)
-{
-       struct mtk_afe *afe = snd_soc_dai_get_drvdata(dai);
-       int i = 0;
-
-       dev_dbg(afe->dev, "%s\n", __func__);
-       if (pm_runtime_status_suspended(afe->dev) || !afe->suspended)
-               return 0;
-
-       mtk_afe_runtime_resume(afe->dev);
-
-       for (i = 0; i < ARRAY_SIZE(mtk_afe_backup_list); i++)
-               regmap_write(afe->regmap, mtk_afe_backup_list[i],
-                            afe->backup_regs[i]);
-
-       afe->suspended = false;
-       return 0;
-}
-
-static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
+static struct snd_soc_dai_driver mt8173_afe_pcm_dais[] = {
        /* FE DAIs: memory intefaces to CPU */
        {
                .name = "DL1", /* downlink 1 */
-               .id = MTK_AFE_MEMIF_DL1,
+               .id = MT8173_AFE_MEMIF_DL1,
                .suspend = mtk_afe_dai_suspend,
                .resume = mtk_afe_dai_resume,
                .playback = {
@@ -824,10 +556,10 @@ static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
                        .rates = SNDRV_PCM_RATE_8000_48000,
                        .formats = SNDRV_PCM_FMTBIT_S16_LE,
                },
-               .ops = &mtk_afe_dai_ops,
+               .ops = &mtk_afe_fe_ops,
        }, {
                .name = "VUL", /* voice uplink */
-               .id = MTK_AFE_MEMIF_VUL,
+               .id = MT8173_AFE_MEMIF_VUL,
                .suspend = mtk_afe_dai_suspend,
                .resume = mtk_afe_dai_resume,
                .capture = {
@@ -837,11 +569,11 @@ static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
                        .rates = SNDRV_PCM_RATE_8000_48000,
                        .formats = SNDRV_PCM_FMTBIT_S16_LE,
                },
-               .ops = &mtk_afe_dai_ops,
+               .ops = &mtk_afe_fe_ops,
        }, {
        /* BE DAIs */
                .name = "I2S",
-               .id = MTK_AFE_IO_I2S,
+               .id = MT8173_AFE_IO_I2S,
                .playback = {
                        .stream_name = "I2S Playback",
                        .channels_min = 1,
@@ -856,16 +588,16 @@ static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
                        .rates = SNDRV_PCM_RATE_8000_48000,
                        .formats = SNDRV_PCM_FMTBIT_S16_LE,
                },
-               .ops = &mtk_afe_i2s_ops,
+               .ops = &mt8173_afe_i2s_ops,
                .symmetric_rates = 1,
        },
 };
 
-static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = {
+static struct snd_soc_dai_driver mt8173_afe_hdmi_dais[] = {
        /* FE DAIs */
        {
                .name = "HDMI",
-               .id = MTK_AFE_MEMIF_HDMI,
+               .id = MT8173_AFE_MEMIF_HDMI,
                .suspend = mtk_afe_dai_suspend,
                .resume = mtk_afe_dai_resume,
                .playback = {
@@ -878,11 +610,11 @@ static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = {
                                SNDRV_PCM_RATE_192000,
                        .formats = SNDRV_PCM_FMTBIT_S16_LE,
                },
-               .ops = &mtk_afe_dai_ops,
+               .ops = &mtk_afe_fe_ops,
        }, {
        /* BE DAIs */
                .name = "HDMIO",
-               .id = MTK_AFE_IO_HDMI,
+               .id = MT8173_AFE_IO_HDMI,
                .playback = {
                        .stream_name = "HDMIO Playback",
                        .channels_min = 2,
@@ -893,29 +625,29 @@ static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = {
                                SNDRV_PCM_RATE_192000,
                        .formats = SNDRV_PCM_FMTBIT_S16_LE,
                },
-               .ops = &mtk_afe_hdmi_ops,
+               .ops = &mt8173_afe_hdmi_ops,
        },
 };
 
-static const struct snd_kcontrol_new mtk_afe_o03_mix[] = {
+static const struct snd_kcontrol_new mt8173_afe_o03_mix[] = {
        SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN1, 21, 1, 0),
 };
 
-static const struct snd_kcontrol_new mtk_afe_o04_mix[] = {
+static const struct snd_kcontrol_new mt8173_afe_o04_mix[] = {
        SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN2, 6, 1, 0),
 };
 
-static const struct snd_kcontrol_new mtk_afe_o09_mix[] = {
+static const struct snd_kcontrol_new mt8173_afe_o09_mix[] = {
        SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN3, 0, 1, 0),
        SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN7, 30, 1, 0),
 };
 
-static const struct snd_kcontrol_new mtk_afe_o10_mix[] = {
+static const struct snd_kcontrol_new mt8173_afe_o10_mix[] = {
        SOC_DAPM_SINGLE_AUTODISABLE("I04 Switch", AFE_CONN3, 3, 1, 0),
        SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN8, 0, 1, 0),
 };
 
-static const struct snd_soc_dapm_widget mtk_afe_pcm_widgets[] = {
+static const struct snd_soc_dapm_widget mt8173_afe_pcm_widgets[] = {
        /* inter-connections */
        SND_SOC_DAPM_MIXER("I03", SND_SOC_NOPM, 0, 0, NULL, 0),
        SND_SOC_DAPM_MIXER("I04", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -925,16 +657,16 @@ static const struct snd_soc_dapm_widget mtk_afe_pcm_widgets[] = {
        SND_SOC_DAPM_MIXER("I18", SND_SOC_NOPM, 0, 0, NULL, 0),
 
        SND_SOC_DAPM_MIXER("O03", SND_SOC_NOPM, 0, 0,
-                          mtk_afe_o03_mix, ARRAY_SIZE(mtk_afe_o03_mix)),
+                          mt8173_afe_o03_mix, ARRAY_SIZE(mt8173_afe_o03_mix)),
        SND_SOC_DAPM_MIXER("O04", SND_SOC_NOPM, 0, 0,
-                          mtk_afe_o04_mix, ARRAY_SIZE(mtk_afe_o04_mix)),
+                          mt8173_afe_o04_mix, ARRAY_SIZE(mt8173_afe_o04_mix)),
        SND_SOC_DAPM_MIXER("O09", SND_SOC_NOPM, 0, 0,
-                          mtk_afe_o09_mix, ARRAY_SIZE(mtk_afe_o09_mix)),
+                          mt8173_afe_o09_mix, ARRAY_SIZE(mt8173_afe_o09_mix)),
        SND_SOC_DAPM_MIXER("O10", SND_SOC_NOPM, 0, 0,
-                          mtk_afe_o10_mix, ARRAY_SIZE(mtk_afe_o10_mix)),
+                          mt8173_afe_o10_mix, ARRAY_SIZE(mt8173_afe_o10_mix)),
 };
 
-static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = {
+static const struct snd_soc_dapm_route mt8173_afe_pcm_routes[] = {
        {"I05", NULL, "DL1"},
        {"I06", NULL, "DL1"},
        {"I2S Playback", NULL, "O03"},
@@ -953,140 +685,257 @@ static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = {
        { "O10", "I04 Switch", "I04" },
 };
 
-static const struct snd_soc_dapm_route mtk_afe_hdmi_routes[] = {
+static const struct snd_soc_dapm_route mt8173_afe_hdmi_routes[] = {
        {"HDMIO Playback", NULL, "HDMI"},
 };
 
-static const struct snd_soc_component_driver mtk_afe_pcm_dai_component = {
-       .name = "mtk-afe-pcm-dai",
-       .dapm_widgets = mtk_afe_pcm_widgets,
-       .num_dapm_widgets = ARRAY_SIZE(mtk_afe_pcm_widgets),
-       .dapm_routes = mtk_afe_pcm_routes,
-       .num_dapm_routes = ARRAY_SIZE(mtk_afe_pcm_routes),
+static const struct snd_soc_component_driver mt8173_afe_pcm_dai_component = {
+       .name = "mt8173-afe-pcm-dai",
+       .dapm_widgets = mt8173_afe_pcm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(mt8173_afe_pcm_widgets),
+       .dapm_routes = mt8173_afe_pcm_routes,
+       .num_dapm_routes = ARRAY_SIZE(mt8173_afe_pcm_routes),
 };
 
-static const struct snd_soc_component_driver mtk_afe_hdmi_dai_component = {
-       .name = "mtk-afe-hdmi-dai",
-       .dapm_routes = mtk_afe_hdmi_routes,
-       .num_dapm_routes = ARRAY_SIZE(mtk_afe_hdmi_routes),
+static const struct snd_soc_component_driver mt8173_afe_hdmi_dai_component = {
+       .name = "mt8173-afe-hdmi-dai",
+       .dapm_routes = mt8173_afe_hdmi_routes,
+       .num_dapm_routes = ARRAY_SIZE(mt8173_afe_hdmi_routes),
 };
 
-static const char *aud_clks[MTK_CLK_NUM] = {
-       [MTK_CLK_INFRASYS_AUD] = "infra_sys_audio_clk",
-       [MTK_CLK_TOP_PDN_AUD] = "top_pdn_audio",
-       [MTK_CLK_TOP_PDN_AUD_BUS] = "top_pdn_aud_intbus",
-       [MTK_CLK_I2S0_M] =  "i2s0_m",
-       [MTK_CLK_I2S1_M] =  "i2s1_m",
-       [MTK_CLK_I2S2_M] =  "i2s2_m",
-       [MTK_CLK_I2S3_M] =  "i2s3_m",
-       [MTK_CLK_I2S3_B] =  "i2s3_b",
-       [MTK_CLK_BCK0] =  "bck0",
-       [MTK_CLK_BCK1] =  "bck1",
+static const char *aud_clks[MT8173_CLK_NUM] = {
+       [MT8173_CLK_INFRASYS_AUD] = "infra_sys_audio_clk",
+       [MT8173_CLK_TOP_PDN_AUD] = "top_pdn_audio",
+       [MT8173_CLK_TOP_PDN_AUD_BUS] = "top_pdn_aud_intbus",
+       [MT8173_CLK_I2S0_M] =  "i2s0_m",
+       [MT8173_CLK_I2S1_M] =  "i2s1_m",
+       [MT8173_CLK_I2S2_M] =  "i2s2_m",
+       [MT8173_CLK_I2S3_M] =  "i2s3_m",
+       [MT8173_CLK_I2S3_B] =  "i2s3_b",
+       [MT8173_CLK_BCK0] =  "bck0",
+       [MT8173_CLK_BCK1] =  "bck1",
 };
 
-static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
+static const struct mtk_base_memif_data memif_data[MT8173_AFE_MEMIF_NUM] = {
        {
                .name = "DL1",
-               .id = MTK_AFE_MEMIF_DL1,
+               .id = MT8173_AFE_MEMIF_DL1,
                .reg_ofs_base = AFE_DL1_BASE,
                .reg_ofs_cur = AFE_DL1_CUR,
+               .fs_reg = AFE_DAC_CON1,
                .fs_shift = 0,
+               .fs_maskbit = 0xf,
+               .mono_reg = AFE_DAC_CON1,
                .mono_shift = 21,
+               .hd_reg = -1,
+               .hd_shift = -1,
+               .enable_reg = AFE_DAC_CON0,
                .enable_shift = 1,
-               .irq_reg_cnt = AFE_IRQ_CNT1,
-               .irq_cnt_shift = 0,
-               .irq_en_shift = 0,
-               .irq_fs_shift = 4,
-               .irq_clr_shift = 0,
+               .msb_reg = AFE_MEMIF_MSB,
                .msb_shift = 0,
+               .agent_disable_reg = -1,
+               .agent_disable_shift = -1,
        }, {
                .name = "DL2",
-               .id = MTK_AFE_MEMIF_DL2,
+               .id = MT8173_AFE_MEMIF_DL2,
                .reg_ofs_base = AFE_DL2_BASE,
                .reg_ofs_cur = AFE_DL2_CUR,
+               .fs_reg = AFE_DAC_CON1,
                .fs_shift = 4,
+               .fs_maskbit = 0xf,
+               .mono_reg = AFE_DAC_CON1,
                .mono_shift = 22,
+               .hd_reg = -1,
+               .hd_shift = -1,
+               .enable_reg = AFE_DAC_CON0,
                .enable_shift = 2,
-               .irq_reg_cnt = AFE_IRQ_CNT1,
-               .irq_cnt_shift = 20,
-               .irq_en_shift = 2,
-               .irq_fs_shift = 16,
-               .irq_clr_shift = 2,
+               .msb_reg = AFE_MEMIF_MSB,
                .msb_shift = 1,
+               .agent_disable_reg = -1,
+               .agent_disable_shift = -1,
        }, {
                .name = "VUL",
-               .id = MTK_AFE_MEMIF_VUL,
+               .id = MT8173_AFE_MEMIF_VUL,
                .reg_ofs_base = AFE_VUL_BASE,
                .reg_ofs_cur = AFE_VUL_CUR,
+               .fs_reg = AFE_DAC_CON1,
                .fs_shift = 16,
+               .fs_maskbit = 0xf,
+               .mono_reg = AFE_DAC_CON1,
                .mono_shift = 27,
+               .hd_reg = -1,
+               .hd_shift = -1,
+               .enable_reg = AFE_DAC_CON0,
                .enable_shift = 3,
-               .irq_reg_cnt = AFE_IRQ_CNT2,
-               .irq_cnt_shift = 0,
-               .irq_en_shift = 1,
-               .irq_fs_shift = 8,
-               .irq_clr_shift = 1,
+               .msb_reg = AFE_MEMIF_MSB,
                .msb_shift = 6,
+               .agent_disable_reg = -1,
+               .agent_disable_shift = -1,
        }, {
                .name = "DAI",
-               .id = MTK_AFE_MEMIF_DAI,
+               .id = MT8173_AFE_MEMIF_DAI,
                .reg_ofs_base = AFE_DAI_BASE,
                .reg_ofs_cur = AFE_DAI_CUR,
+               .fs_reg = AFE_DAC_CON0,
                .fs_shift = 24,
+               .fs_maskbit = 0x3,
+               .mono_reg = -1,
                .mono_shift = -1,
+               .hd_reg = -1,
+               .hd_shift = -1,
+               .enable_reg = AFE_DAC_CON0,
                .enable_shift = 4,
-               .irq_reg_cnt = AFE_IRQ_CNT2,
-               .irq_cnt_shift = 20,
-               .irq_en_shift = 3,
-               .irq_fs_shift = 20,
-               .irq_clr_shift = 3,
+               .msb_reg = AFE_MEMIF_MSB,
                .msb_shift = 5,
+               .agent_disable_reg = -1,
+               .agent_disable_shift = -1,
        }, {
                .name = "AWB",
-               .id = MTK_AFE_MEMIF_AWB,
+               .id = MT8173_AFE_MEMIF_AWB,
                .reg_ofs_base = AFE_AWB_BASE,
                .reg_ofs_cur = AFE_AWB_CUR,
+               .fs_reg = AFE_DAC_CON1,
                .fs_shift = 12,
+               .fs_maskbit = 0xf,
+               .mono_reg = AFE_DAC_CON1,
                .mono_shift = 24,
+               .hd_reg = -1,
+               .hd_shift = -1,
+               .enable_reg = AFE_DAC_CON0,
                .enable_shift = 6,
-               .irq_reg_cnt = AFE_IRQ_CNT7,
-               .irq_cnt_shift = 0,
-               .irq_en_shift = 14,
-               .irq_fs_shift = 24,
-               .irq_clr_shift = 6,
+               .msb_reg = AFE_MEMIF_MSB,
                .msb_shift = 3,
+               .agent_disable_reg = -1,
+               .agent_disable_shift = -1,
        }, {
                .name = "MOD_DAI",
-               .id = MTK_AFE_MEMIF_MOD_DAI,
+               .id = MT8173_AFE_MEMIF_MOD_DAI,
                .reg_ofs_base = AFE_MOD_PCM_BASE,
                .reg_ofs_cur = AFE_MOD_PCM_CUR,
+               .fs_reg = AFE_DAC_CON1,
                .fs_shift = 30,
+               .fs_maskbit = 0x3,
+               .mono_reg = AFE_DAC_CON1,
                .mono_shift = 30,
+               .hd_reg = -1,
+               .hd_shift = -1,
+               .enable_reg = AFE_DAC_CON0,
                .enable_shift = 7,
-               .irq_reg_cnt = AFE_IRQ_CNT2,
-               .irq_cnt_shift = 20,
-               .irq_en_shift = 3,
-               .irq_fs_shift = 20,
-               .irq_clr_shift = 3,
+               .msb_reg = AFE_MEMIF_MSB,
                .msb_shift = 4,
+               .agent_disable_reg = -1,
+               .agent_disable_shift = -1,
        }, {
                .name = "HDMI",
-               .id = MTK_AFE_MEMIF_HDMI,
+               .id = MT8173_AFE_MEMIF_HDMI,
                .reg_ofs_base = AFE_HDMI_OUT_BASE,
                .reg_ofs_cur = AFE_HDMI_OUT_CUR,
+               .fs_reg = -1,
                .fs_shift = -1,
+               .fs_maskbit = -1,
+               .mono_reg = -1,
                .mono_shift = -1,
+               .hd_reg = -1,
+               .hd_shift = -1,
+               .enable_reg = -1,
                .enable_shift = -1,
-               .irq_reg_cnt = AFE_IRQ_CNT5,
+               .msb_reg = AFE_MEMIF_MSB,
+               .msb_shift = 8,
+               .agent_disable_reg = -1,
+               .agent_disable_shift = -1,
+       },
+};
+
+static const struct mtk_base_irq_data irq_data[MT8173_AFE_IRQ_NUM] = {
+       {
+               .id = MT8173_AFE_IRQ_DL1,
+               .irq_cnt_reg = AFE_IRQ_CNT1,
+               .irq_cnt_shift = 0,
+               .irq_cnt_maskbit = 0x3ffff,
+               .irq_en_reg = AFE_IRQ_MCU_CON,
+               .irq_en_shift = 0,
+               .irq_fs_reg = AFE_IRQ_MCU_CON,
+               .irq_fs_shift = 4,
+               .irq_fs_maskbit = 0xf,
+               .irq_clr_reg = AFE_IRQ_CLR,
+               .irq_clr_shift = 0,
+       }, {
+               .id = MT8173_AFE_IRQ_DL2,
+               .irq_cnt_reg = AFE_IRQ_CNT1,
+               .irq_cnt_shift = 20,
+               .irq_cnt_maskbit = 0x3ffff,
+               .irq_en_reg = AFE_IRQ_MCU_CON,
+               .irq_en_shift = 2,
+               .irq_fs_reg = AFE_IRQ_MCU_CON,
+               .irq_fs_shift = 16,
+               .irq_fs_maskbit = 0xf,
+               .irq_clr_reg = AFE_IRQ_CLR,
+               .irq_clr_shift = 2,
+
+       }, {
+               .id = MT8173_AFE_IRQ_VUL,
+               .irq_cnt_reg = AFE_IRQ_CNT2,
+               .irq_cnt_shift = 0,
+               .irq_cnt_maskbit = 0x3ffff,
+               .irq_en_reg = AFE_IRQ_MCU_CON,
+               .irq_en_shift = 1,
+               .irq_fs_reg = AFE_IRQ_MCU_CON,
+               .irq_fs_shift = 8,
+               .irq_fs_maskbit = 0xf,
+               .irq_clr_reg = AFE_IRQ_CLR,
+               .irq_clr_shift = 1,
+       }, {
+               .id = MT8173_AFE_IRQ_DAI,
+               .irq_cnt_reg = AFE_IRQ_CNT2,
+               .irq_cnt_shift = 20,
+               .irq_cnt_maskbit = 0x3ffff,
+               .irq_en_reg = AFE_IRQ_MCU_CON,
+               .irq_en_shift = 3,
+               .irq_fs_reg = AFE_IRQ_MCU_CON,
+               .irq_fs_shift = 20,
+               .irq_fs_maskbit = 0xf,
+               .irq_clr_reg = AFE_IRQ_CLR,
+               .irq_clr_shift = 3,
+       }, {
+               .id = MT8173_AFE_IRQ_AWB,
+               .irq_cnt_reg = AFE_IRQ_CNT7,
                .irq_cnt_shift = 0,
+               .irq_cnt_maskbit = 0x3ffff,
+               .irq_en_reg = AFE_IRQ_MCU_CON,
+               .irq_en_shift = 14,
+               .irq_fs_reg = AFE_IRQ_MCU_CON,
+               .irq_fs_shift = 24,
+               .irq_fs_maskbit = 0xf,
+               .irq_clr_reg = AFE_IRQ_CLR,
+               .irq_clr_shift = 6,
+       }, {
+               .id = MT8173_AFE_IRQ_DAI,
+               .irq_cnt_reg = AFE_IRQ_CNT2,
+               .irq_cnt_shift = 20,
+               .irq_cnt_maskbit = 0x3ffff,
+               .irq_en_reg = AFE_IRQ_MCU_CON,
+               .irq_en_shift = 3,
+               .irq_fs_reg = AFE_IRQ_MCU_CON,
+               .irq_fs_shift = 20,
+               .irq_fs_maskbit = 0xf,
+               .irq_clr_reg = AFE_IRQ_CLR,
+               .irq_clr_shift = 3,
+       }, {
+               .id = MT8173_AFE_IRQ_HDMI,
+               .irq_cnt_reg = AFE_IRQ_CNT5,
+               .irq_cnt_shift = 0,
+               .irq_cnt_maskbit = 0x3ffff,
+               .irq_en_reg = AFE_IRQ_MCU_CON,
                .irq_en_shift = 12,
+               .irq_fs_reg = -1,
                .irq_fs_shift = -1,
+               .irq_fs_maskbit = -1,
+               .irq_clr_reg = AFE_IRQ_CLR,
                .irq_clr_shift = 4,
-               .msb_shift = 8,
        },
 };
 
-static const struct regmap_config mtk_afe_regmap_config = {
+static const struct regmap_config mt8173_afe_regmap_config = {
        .reg_bits = 32,
        .reg_stride = 4,
        .val_bits = 32,
@@ -1094,9 +943,9 @@ static const struct regmap_config mtk_afe_regmap_config = {
        .cache_type = REGCACHE_NONE,
 };
 
-static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id)
+static irqreturn_t mt8173_afe_irq_handler(int irq, void *dev_id)
 {
-       struct mtk_afe *afe = dev_id;
+       struct mtk_base_afe *afe = dev_id;
        unsigned int reg_value;
        int i, ret;
 
@@ -1107,10 +956,16 @@ static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id)
                goto err_irq;
        }
 
-       for (i = 0; i < MTK_AFE_MEMIF_NUM; i++) {
-               struct mtk_afe_memif *memif = &afe->memif[i];
+       for (i = 0; i < MT8173_AFE_MEMIF_NUM; i++) {
+               struct mtk_base_afe_memif *memif = &afe->memif[i];
+               struct mtk_base_afe_irq *irq;
 
-               if (!(reg_value & (1 << memif->data->irq_clr_shift)))
+               if (memif->irq_usage < 0)
+                       continue;
+
+               irq = &afe->irqs[memif->irq_usage];
+
+               if (!(reg_value & (1 << irq->irq_data->irq_clr_shift)))
                        continue;
 
                snd_pcm_period_elapsed(memif->substream);
@@ -1118,14 +973,16 @@ static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id)
 
 err_irq:
        /* clear irq */
-       regmap_write(afe->regmap, AFE_IRQ_CLR, reg_value & AFE_IRQ_STATUS_BITS);
+       regmap_write(afe->regmap, AFE_IRQ_CLR,
+                    reg_value & AFE_IRQ_STATUS_BITS);
 
        return IRQ_HANDLED;
 }
 
-static int mtk_afe_runtime_suspend(struct device *dev)
+static int mt8173_afe_runtime_suspend(struct device *dev)
 {
-       struct mtk_afe *afe = dev_get_drvdata(dev);
+       struct mtk_base_afe *afe = dev_get_drvdata(dev);
+       struct mt8173_afe_private *afe_priv = afe->platform_priv;
 
        /* disable AFE */
        regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0);
@@ -1134,38 +991,47 @@ static int mtk_afe_runtime_suspend(struct device *dev)
        regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
                           AUD_TCON0_PDN_AFE, AUD_TCON0_PDN_AFE);
 
-       clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]);
-       clk_disable_unprepare(afe->clocks[MTK_CLK_BCK1]);
-       clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]);
-       clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]);
-       clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]);
+       clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S1_M]);
+       clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S2_M]);
+       clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_BCK0]);
+       clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_BCK1]);
+       clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD]);
+       clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
+       clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_INFRASYS_AUD]);
        return 0;
 }
 
-static int mtk_afe_runtime_resume(struct device *dev)
+static int mt8173_afe_runtime_resume(struct device *dev)
 {
-       struct mtk_afe *afe = dev_get_drvdata(dev);
+       struct mtk_base_afe *afe = dev_get_drvdata(dev);
+       struct mt8173_afe_private *afe_priv = afe->platform_priv;
        int ret;
 
-       ret = clk_prepare_enable(afe->clocks[MTK_CLK_INFRASYS_AUD]);
+       ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_INFRASYS_AUD]);
        if (ret)
                return ret;
 
-       ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]);
+       ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
        if (ret)
                goto err_infra;
 
-       ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD]);
+       ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD]);
        if (ret)
                goto err_top_aud_bus;
 
-       ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK0]);
+       ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_BCK0]);
        if (ret)
                goto err_top_aud;
 
-       ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK1]);
+       ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_BCK1]);
        if (ret)
                goto err_bck0;
+       ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_I2S1_M]);
+       if (ret)
+               goto err_i2s1_m;
+       ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_I2S2_M]);
+       if (ret)
+               goto err_i2s2_m;
 
        /* enable AFE clk */
        regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, AUD_TCON0_PDN_AFE, 0);
@@ -1181,39 +1047,45 @@ static int mtk_afe_runtime_resume(struct device *dev)
        regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1);
        return 0;
 
+err_i2s1_m:
+       clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S1_M]);
+err_i2s2_m:
+       clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S2_M]);
 err_bck0:
-       clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]);
+       clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_BCK0]);
 err_top_aud:
-       clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]);
+       clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD]);
 err_top_aud_bus:
-       clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]);
+       clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
 err_infra:
-       clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]);
+       clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_INFRASYS_AUD]);
        return ret;
 }
 
-static int mtk_afe_init_audio_clk(struct mtk_afe *afe)
+static int mt8173_afe_init_audio_clk(struct mtk_base_afe *afe)
 {
        size_t i;
+       struct mt8173_afe_private *afe_priv = afe->platform_priv;
 
        for (i = 0; i < ARRAY_SIZE(aud_clks); i++) {
-               afe->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
-               if (IS_ERR(afe->clocks[i])) {
+               afe_priv->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
+               if (IS_ERR(afe_priv->clocks[i])) {
                        dev_err(afe->dev, "%s devm_clk_get %s fail\n",
                                __func__, aud_clks[i]);
-                       return PTR_ERR(afe->clocks[i]);
+                       return PTR_ERR(afe_priv->clocks[i]);
                }
        }
-       clk_set_rate(afe->clocks[MTK_CLK_BCK0], 22579200); /* 22M */
-       clk_set_rate(afe->clocks[MTK_CLK_BCK1], 24576000); /* 24M */
+       clk_set_rate(afe_priv->clocks[MT8173_CLK_BCK0], 22579200); /* 22M */
+       clk_set_rate(afe_priv->clocks[MT8173_CLK_BCK1], 24576000); /* 24M */
        return 0;
 }
 
-static int mtk_afe_pcm_dev_probe(struct platform_device *pdev)
+static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 {
        int ret, i;
        unsigned int irq_id;
-       struct mtk_afe *afe;
+       struct mtk_base_afe *afe;
+       struct mt8173_afe_private *afe_priv;
        struct resource *res;
 
        ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33));
@@ -1224,6 +1096,12 @@ static int mtk_afe_pcm_dev_probe(struct platform_device *pdev)
        if (!afe)
                return -ENOMEM;
 
+       afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
+                                         GFP_KERNEL);
+       afe_priv = afe->platform_priv;
+       if (!afe_priv)
+               return -ENOMEM;
+
        afe->dev = &pdev->dev;
 
        irq_id = platform_get_irq(pdev, 0);
@@ -1231,7 +1109,7 @@ static int mtk_afe_pcm_dev_probe(struct platform_device *pdev)
                dev_err(afe->dev, "np %s no irq\n", afe->dev->of_node->name);
                return -ENXIO;
        }
-       ret = devm_request_irq(afe->dev, irq_id, mtk_afe_irq_handler,
+       ret = devm_request_irq(afe->dev, irq_id, mt8173_afe_irq_handler,
                               0, "Afe_ISR_Handle", (void *)afe);
        if (ret) {
                dev_err(afe->dev, "could not request_irq\n");
@@ -1244,48 +1122,75 @@ static int mtk_afe_pcm_dev_probe(struct platform_device *pdev)
                return PTR_ERR(afe->base_addr);
 
        afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
-               &mtk_afe_regmap_config);
+               &mt8173_afe_regmap_config);
        if (IS_ERR(afe->regmap))
                return PTR_ERR(afe->regmap);
 
        /* initial audio related clock */
-       ret = mtk_afe_init_audio_clk(afe);
+       ret = mt8173_afe_init_audio_clk(afe);
        if (ret) {
-               dev_err(afe->dev, "mtk_afe_init_audio_clk fail\n");
+               dev_err(afe->dev, "mt8173_afe_init_audio_clk fail\n");
                return ret;
        }
 
-       for (i = 0; i < MTK_AFE_MEMIF_NUM; i++)
+       /* memif % irq initialize*/
+       afe->memif_size = MT8173_AFE_MEMIF_NUM;
+       afe->memif = devm_kcalloc(afe->dev, afe->memif_size,
+                                 sizeof(*afe->memif), GFP_KERNEL);
+       if (!afe->memif)
+               return -ENOMEM;
+
+       afe->irqs_size = MT8173_AFE_IRQ_NUM;
+       afe->irqs = devm_kcalloc(afe->dev, afe->irqs_size,
+                                sizeof(*afe->irqs), GFP_KERNEL);
+       if (!afe->irqs)
+               return -ENOMEM;
+
+       for (i = 0; i < afe->irqs_size; i++) {
                afe->memif[i].data = &memif_data[i];
+               afe->irqs[i].irq_data = &irq_data[i];
+               afe->irqs[i].irq_occupyed = true;
+               afe->memif[i].irq_usage = i;
+               afe->memif[i].const_irq = 1;
+       }
+
+       afe->mtk_afe_hardware = &mt8173_afe_hardware;
+       afe->memif_fs = mt8173_memif_fs;
+       afe->irq_fs = mt8173_irq_fs;
 
        platform_set_drvdata(pdev, afe);
 
        pm_runtime_enable(&pdev->dev);
        if (!pm_runtime_enabled(&pdev->dev)) {
-               ret = mtk_afe_runtime_resume(&pdev->dev);
+               ret = mt8173_afe_runtime_resume(&pdev->dev);
                if (ret)
                        goto err_pm_disable;
        }
 
+       afe->reg_back_up_list = mt8173_afe_backup_list;
+       afe->reg_back_up_list_num = ARRAY_SIZE(mt8173_afe_backup_list);
+       afe->runtime_resume = mt8173_afe_runtime_resume;
+       afe->runtime_suspend = mt8173_afe_runtime_suspend;
+
        ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform);
        if (ret)
                goto err_pm_disable;
 
        ret = snd_soc_register_component(&pdev->dev,
-                                        &mtk_afe_pcm_dai_component,
-                                        mtk_afe_pcm_dais,
-                                        ARRAY_SIZE(mtk_afe_pcm_dais));
+                                        &mt8173_afe_pcm_dai_component,
+                                        mt8173_afe_pcm_dais,
+                                        ARRAY_SIZE(mt8173_afe_pcm_dais));
        if (ret)
                goto err_platform;
 
        ret = snd_soc_register_component(&pdev->dev,
-                                        &mtk_afe_hdmi_dai_component,
-                                        mtk_afe_hdmi_dais,
-                                        ARRAY_SIZE(mtk_afe_hdmi_dais));
+                                        &mt8173_afe_hdmi_dai_component,
+                                        mt8173_afe_hdmi_dais,
+                                        ARRAY_SIZE(mt8173_afe_hdmi_dais));
        if (ret)
                goto err_comp;
 
-       dev_info(&pdev->dev, "MTK AFE driver initialized.\n");
+       dev_info(&pdev->dev, "MT8173 AFE driver initialized.\n");
        return 0;
 
 err_comp:
@@ -1297,38 +1202,38 @@ static int mtk_afe_pcm_dev_probe(struct platform_device *pdev)
        return ret;
 }
 
-static int mtk_afe_pcm_dev_remove(struct platform_device *pdev)
+static int mt8173_afe_pcm_dev_remove(struct platform_device *pdev)
 {
        pm_runtime_disable(&pdev->dev);
        if (!pm_runtime_status_suspended(&pdev->dev))
-               mtk_afe_runtime_suspend(&pdev->dev);
+               mt8173_afe_runtime_suspend(&pdev->dev);
        snd_soc_unregister_component(&pdev->dev);
        snd_soc_unregister_platform(&pdev->dev);
        return 0;
 }
 
-static const struct of_device_id mtk_afe_pcm_dt_match[] = {
+static const struct of_device_id mt8173_afe_pcm_dt_match[] = {
        { .compatible = "mediatek,mt8173-afe-pcm", },
        { }
 };
-MODULE_DEVICE_TABLE(of, mtk_afe_pcm_dt_match);
+MODULE_DEVICE_TABLE(of, mt8173_afe_pcm_dt_match);
 
-static const struct dev_pm_ops mtk_afe_pm_ops = {
-       SET_RUNTIME_PM_OPS(mtk_afe_runtime_suspend, mtk_afe_runtime_resume,
-                          NULL)
+static const struct dev_pm_ops mt8173_afe_pm_ops = {
+       SET_RUNTIME_PM_OPS(mt8173_afe_runtime_suspend,
+                          mt8173_afe_runtime_resume, NULL)
 };
 
-static struct platform_driver mtk_afe_pcm_driver = {
+static struct platform_driver mt8173_afe_pcm_driver = {
        .driver = {
-                  .name = "mtk-afe-pcm",
-                  .of_match_table = mtk_afe_pcm_dt_match,
-                  .pm = &mtk_afe_pm_ops,
+                  .name = "mt8173-afe-pcm",
+                  .of_match_table = mt8173_afe_pcm_dt_match,
+                  .pm = &mt8173_afe_pm_ops,
        },
-       .probe = mtk_afe_pcm_dev_probe,
-       .remove = mtk_afe_pcm_dev_remove,
+       .probe = mt8173_afe_pcm_dev_probe,
+       .remove = mt8173_afe_pcm_dev_remove,
 };
 
-module_platform_driver(mtk_afe_pcm_driver);
+module_platform_driver(mt8173_afe_pcm_driver);
 
 MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver");
 MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>");
similarity index 99%
rename from sound/soc/mediatek/mt8173-max98090.c
rename to sound/soc/mediatek/mt8173/mt8173-max98090.c
index 71a1a35047bafd085881efd5369f5275bf9a050f..5524a2c727ec76ad25af3a8386eda5f09678369e 100644 (file)
@@ -18,7 +18,7 @@
 #include <sound/soc.h>
 #include <sound/jack.h>
 #include <linux/gpio.h>
-#include "../codecs/max98090.h"
+#include "../../codecs/max98090.h"
 
 static struct snd_soc_jack mt8173_max98090_jack;
 
similarity index 99%
rename from sound/soc/mediatek/mt8173-rt5650-rt5514.c
rename to sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
index 58e083642dea4aa3c3bb468f2733d7c06b738de4..467f7049a28863fca5f3750fb27bf1ad9369e0c3 100644 (file)
@@ -19,7 +19,7 @@
 #include <linux/of_gpio.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
-#include "../codecs/rt5645.h"
+#include "../../codecs/rt5645.h"
 
 #define MCLK_FOR_CODECS                12288000
 
similarity index 99%
rename from sound/soc/mediatek/mt8173-rt5650-rt5676.c
rename to sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
index bb593926c62d252e8f5ec4edf92b25cc97ac9245..1b8b2a77884505515460a477309bb658312aed2a 100644 (file)
@@ -19,8 +19,8 @@
 #include <linux/of_gpio.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
-#include "../codecs/rt5645.h"
-#include "../codecs/rt5677.h"
+#include "../../codecs/rt5645.h"
+#include "../../codecs/rt5677.h"
 
 #define MCLK_FOR_CODECS                12288000
 
similarity index 80%
rename from sound/soc/mediatek/mt8173-rt5650.c
rename to sound/soc/mediatek/mt8173/mt8173-rt5650.c
index a27a6673dbe3191ee06a0f5f5821cdab230768b0..ba65f4157a7e0ef6a60e5406e2570cfb1aaa14d9 100644 (file)
 #include <linux/of_gpio.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
-#include "../codecs/rt5645.h"
+#include "../../codecs/rt5645.h"
 
 #define MCLK_FOR_CODECS                12288000
 
+enum mt8173_rt5650_mclk {
+       MT8173_RT5650_MCLK_EXTERNAL = 0,
+       MT8173_RT5650_MCLK_INTERNAL,
+};
+
+struct mt8173_rt5650_platform_data {
+       enum mt8173_rt5650_mclk pll_from;
+       /* 0 = external oscillator; 1 = internal source from mt8173 */
+};
+
+static struct mt8173_rt5650_platform_data mt8173_rt5650_priv = {
+       .pll_from = MT8173_RT5650_MCLK_EXTERNAL,
+};
+
 static const struct snd_soc_dapm_widget mt8173_rt5650_widgets[] = {
        SND_SOC_DAPM_SPK("Speaker", NULL),
        SND_SOC_DAPM_MIC("Int Mic", NULL),
@@ -54,13 +68,29 @@ static int mt8173_rt5650_hw_params(struct snd_pcm_substream *substream,
                                   struct snd_pcm_hw_params *params)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       unsigned int mclk_clock;
        int i, ret;
 
+       switch (mt8173_rt5650_priv.pll_from) {
+       case MT8173_RT5650_MCLK_EXTERNAL:
+               /* mclk = 12.288M */
+               mclk_clock = MCLK_FOR_CODECS;
+               break;
+       case MT8173_RT5650_MCLK_INTERNAL:
+               /* mclk = sampling rate*256 */
+               mclk_clock = params_rate(params) * 256;
+               break;
+       default:
+               /* mclk = 12.288M */
+               mclk_clock = MCLK_FOR_CODECS;
+               break;
+       }
+
        for (i = 0; i < rtd->num_codecs; i++) {
                struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
 
-               /* pll from mclk 12.288M */
-               ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS,
+               /* pll from mclk */
+               ret = snd_soc_dai_set_pll(codec_dai, 0, 0, mclk_clock,
                                          params_rate(params) * 512);
                if (ret)
                        return ret;
@@ -139,7 +169,9 @@ static struct snd_soc_dai_link_component mt8173_rt5650_codecs[] = {
 enum {
        DAI_LINK_PLAYBACK,
        DAI_LINK_CAPTURE,
+       DAI_LINK_HDMI,
        DAI_LINK_CODEC_I2S,
+       DAI_LINK_HDMI_I2S,
 };
 
 /* Digital audio interface glue - connects codec <---> CPU */
@@ -165,6 +197,16 @@ static struct snd_soc_dai_link mt8173_rt5650_dais[] = {
                .dynamic = 1,
                .dpcm_capture = 1,
        },
+       [DAI_LINK_HDMI] = {
+               .name = "HDMI",
+               .stream_name = "HDMI PCM",
+               .cpu_dai_name = "HDMI",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+               .dynamic = 1,
+               .dpcm_playback = 1,
+       },
        /* Back End DAI links */
        [DAI_LINK_CODEC_I2S] = {
                .name = "Codec",
@@ -180,6 +222,13 @@ static struct snd_soc_dai_link mt8173_rt5650_dais[] = {
                .dpcm_playback = 1,
                .dpcm_capture = 1,
        },
+       [DAI_LINK_HDMI_I2S] = {
+               .name = "HDMI BE",
+               .cpu_dai_name = "HDMIO",
+               .no_pcm = 1,
+               .codec_dai_name = "i2s-hifi",
+               .dpcm_playback = 1,
+       },
 };
 
 static struct snd_soc_card mt8173_rt5650_card = {
@@ -243,6 +292,24 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev)
                mt8173_rt5650_codecs[1].dai_name = codec_capture_dai;
        }
 
+       if (device_property_present(&pdev->dev, "mediatek,mclk")) {
+               ret = device_property_read_u32(&pdev->dev,
+                                              "mediatek,mclk",
+                                              &mt8173_rt5650_priv.pll_from);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "%s snd_soc_register_card fail %d\n",
+                               __func__, ret);
+               }
+       }
+
+       mt8173_rt5650_dais[DAI_LINK_HDMI_I2S].codec_of_node =
+               of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1);
+       if (!mt8173_rt5650_dais[DAI_LINK_HDMI_I2S].codec_of_node) {
+               dev_err(&pdev->dev,
+                       "Property 'audio-codec' missing or invalid\n");
+               return -EINVAL;
+       }
        card->dev = &pdev->dev;
        platform_set_drvdata(pdev, card);
 
diff --git a/sound/soc/mediatek/mtk-afe-common.h b/sound/soc/mediatek/mtk-afe-common.h
deleted file mode 100644 (file)
index f341f62..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * mtk_afe_common.h  --  Mediatek audio driver common definitions
- *
- * Copyright (c) 2015 MediaTek Inc.
- * Author: Koro Chen <koro.chen@mediatek.com>
- *             Sascha Hauer <s.hauer@pengutronix.de>
- *             Hidalgo Huang <hidalgo.huang@mediatek.com>
- *             Ir Lian <ir.lian@mediatek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#ifndef _MTK_AFE_COMMON_H_
-#define _MTK_AFE_COMMON_H_
-
-#include <linux/clk.h>
-#include <linux/regmap.h>
-
-enum {
-       MTK_AFE_MEMIF_DL1,
-       MTK_AFE_MEMIF_DL2,
-       MTK_AFE_MEMIF_VUL,
-       MTK_AFE_MEMIF_DAI,
-       MTK_AFE_MEMIF_AWB,
-       MTK_AFE_MEMIF_MOD_DAI,
-       MTK_AFE_MEMIF_HDMI,
-       MTK_AFE_MEMIF_NUM,
-       MTK_AFE_IO_MOD_PCM1 = MTK_AFE_MEMIF_NUM,
-       MTK_AFE_IO_MOD_PCM2,
-       MTK_AFE_IO_PMIC,
-       MTK_AFE_IO_I2S,
-       MTK_AFE_IO_2ND_I2S,
-       MTK_AFE_IO_HW_GAIN1,
-       MTK_AFE_IO_HW_GAIN2,
-       MTK_AFE_IO_MRG_O,
-       MTK_AFE_IO_MRG_I,
-       MTK_AFE_IO_DAIBT,
-       MTK_AFE_IO_HDMI,
-};
-
-enum {
-       MTK_AFE_IRQ_1,
-       MTK_AFE_IRQ_2,
-       MTK_AFE_IRQ_3,
-       MTK_AFE_IRQ_4,
-       MTK_AFE_IRQ_5,
-       MTK_AFE_IRQ_6,
-       MTK_AFE_IRQ_7,
-       MTK_AFE_IRQ_8,
-       MTK_AFE_IRQ_NUM,
-};
-
-enum {
-       MTK_CLK_INFRASYS_AUD,
-       MTK_CLK_TOP_PDN_AUD,
-       MTK_CLK_TOP_PDN_AUD_BUS,
-       MTK_CLK_I2S0_M,
-       MTK_CLK_I2S1_M,
-       MTK_CLK_I2S2_M,
-       MTK_CLK_I2S3_M,
-       MTK_CLK_I2S3_B,
-       MTK_CLK_BCK0,
-       MTK_CLK_BCK1,
-       MTK_CLK_NUM
-};
-
-struct mtk_afe;
-struct snd_pcm_substream;
-
-struct mtk_afe_memif_data {
-       int id;
-       const char *name;
-       int reg_ofs_base;
-       int reg_ofs_cur;
-       int fs_shift;
-       int mono_shift;
-       int enable_shift;
-       int irq_reg_cnt;
-       int irq_cnt_shift;
-       int irq_en_shift;
-       int irq_fs_shift;
-       int irq_clr_shift;
-       int msb_shift;
-};
-
-struct mtk_afe_memif {
-       unsigned int phys_buf_addr;
-       int buffer_size;
-       struct snd_pcm_substream *substream;
-       const struct mtk_afe_memif_data *data;
-       const struct mtk_afe_irq_data *irqdata;
-};
-
-#endif
index 5185a3844da90d207413457defec90d29a691ff4..f5451c78ede55e777b5aa6814b9a1db2daa16136 100644 (file)
@@ -100,13 +100,14 @@ config SND_OMAP_SOC_OMAP_TWL4030
 
 config SND_OMAP_SOC_OMAP_ABE_TWL6040
        tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec"
-       depends on TWL6040_CORE && SND_OMAP_SOC
+       depends on TWL6040_CORE && SND_OMAP_SOC && COMMON_CLK
        depends on ARCH_OMAP4 || (SOC_OMAP5 && MFD_PALMAS) || COMPILE_TEST
        select SND_OMAP_SOC_DMIC
        select SND_OMAP_SOC_MCPDM
        select SND_SOC_TWL6040
        select SND_SOC_DMIC
        select COMMON_CLK_PALMAS if (SOC_OMAP5 && MFD_PALMAS)
+       select CLK_TWL6040
        help
          Say Y if you want to add support for SoC audio on OMAP boards using
          ABE and twl6040 codec. This driver currently supports:
index b837265ac3e9afd90dc26e5e00b2b52d0d1d9239..e7cdc51fd806587a9ec1f8b4c8103bbffb5858d8 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/irq.h>
+#include <linux/clk.h>
 #include <linux/slab.h>
 #include <linux/pm_runtime.h>
 #include <linux/of_device.h>
@@ -54,6 +55,7 @@ struct omap_mcpdm {
        unsigned long phys_base;
        void __iomem *io_base;
        int irq;
+       struct clk *pdmclk;
 
        struct mutex mutex;
 
@@ -66,6 +68,9 @@ struct omap_mcpdm {
        /* McPDM needs to be restarted due to runtime reconfiguration */
        bool restart;
 
+       /* pm state for suspend/resume handling */
+       int pm_active_count;
+
        struct snd_dmaengine_dai_dma_data dma_data[2];
 };
 
@@ -173,6 +178,10 @@ static inline int omap_mcpdm_active(struct omap_mcpdm *mcpdm)
  */
 static void omap_mcpdm_open_streams(struct omap_mcpdm *mcpdm)
 {
+       u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL);
+
+       omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl | MCPDM_WD_EN);
+
        omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_SET,
                        MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL |
                        MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL);
@@ -258,12 +267,9 @@ static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream,
 
        mutex_lock(&mcpdm->mutex);
 
-       if (!dai->active) {
-               u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL);
-
-               omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl | MCPDM_WD_EN);
+       if (!dai->active)
                omap_mcpdm_open_streams(mcpdm);
-       }
+
        mutex_unlock(&mcpdm->mutex);
 
        return 0;
@@ -384,6 +390,7 @@ static int omap_mcpdm_probe(struct snd_soc_dai *dai)
        struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
        int ret;
 
+       clk_prepare_enable(mcpdm->pdmclk);
        pm_runtime_enable(mcpdm->dev);
 
        /* Disable lines while request is ongoing */
@@ -418,8 +425,54 @@ static int omap_mcpdm_remove(struct snd_soc_dai *dai)
 
        pm_runtime_disable(mcpdm->dev);
 
+       clk_disable_unprepare(mcpdm->pdmclk);
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int omap_mcpdm_suspend(struct snd_soc_dai *dai)
+{
+       struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+
+       if (dai->active) {
+               omap_mcpdm_stop(mcpdm);
+               omap_mcpdm_close_streams(mcpdm);
+       }
+
+       mcpdm->pm_active_count = 0;
+       while (pm_runtime_active(mcpdm->dev)) {
+               pm_runtime_put_sync(mcpdm->dev);
+               mcpdm->pm_active_count++;
+       }
+
+       clk_disable_unprepare(mcpdm->pdmclk);
+
+       return 0;
+}
+
+static int omap_mcpdm_resume(struct snd_soc_dai *dai)
+{
+       struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+
+       clk_prepare_enable(mcpdm->pdmclk);
+
+       if (mcpdm->pm_active_count) {
+               while (mcpdm->pm_active_count--)
+                       pm_runtime_get_sync(mcpdm->dev);
+
+               if (dai->active) {
+                       omap_mcpdm_open_streams(mcpdm);
+                       omap_mcpdm_start(mcpdm);
+               }
+       }
+
+
        return 0;
 }
+#else
+#define omap_mcpdm_suspend NULL
+#define omap_mcpdm_resume NULL
+#endif
 
 #define OMAP_MCPDM_RATES       (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
 #define OMAP_MCPDM_FORMATS     SNDRV_PCM_FMTBIT_S32_LE
@@ -427,6 +480,8 @@ static int omap_mcpdm_remove(struct snd_soc_dai *dai)
 static struct snd_soc_dai_driver omap_mcpdm_dai = {
        .probe = omap_mcpdm_probe,
        .remove = omap_mcpdm_remove,
+       .suspend = omap_mcpdm_suspend,
+       .resume = omap_mcpdm_resume,
        .probe_order = SND_SOC_COMP_ORDER_LATE,
        .remove_order = SND_SOC_COMP_ORDER_EARLY,
        .playback = {
@@ -494,6 +549,15 @@ static int asoc_mcpdm_probe(struct platform_device *pdev)
 
        mcpdm->dev = &pdev->dev;
 
+       mcpdm->pdmclk = devm_clk_get(&pdev->dev, "pdmclk");
+       if (IS_ERR(mcpdm->pdmclk)) {
+               if (PTR_ERR(mcpdm->pdmclk) == -EPROBE_DEFER)
+                       return -EPROBE_DEFER;
+               dev_warn(&pdev->dev, "Error getting pdmclk (%ld)!\n",
+                        PTR_ERR(mcpdm->pdmclk));
+               mcpdm->pdmclk = NULL;
+       }
+
        ret =  devm_snd_soc_register_component(&pdev->dev,
                                               &omap_mcpdm_component,
                                               &omap_mcpdm_dai, 1);
index 54949242bc7075587e6a73a7235049db2e108734..a76845748a103447e039ee4db94cf3f8abe10c93 100644 (file)
@@ -33,7 +33,6 @@
 #include <sound/pcm.h>
 #include <sound/soc.h>
 #include <linux/platform_data/asoc-ti-mcbsp.h>
-#include "../codecs/tpa6130a2.h"
 
 #include <asm/mach-types.h>
 
@@ -164,19 +163,6 @@ static int rx51_spk_event(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
-static int rx51_hp_event(struct snd_soc_dapm_widget *w,
-                        struct snd_kcontrol *k, int event)
-{
-       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
-
-       if (SND_SOC_DAPM_EVENT_ON(event))
-               tpa6130a2_stereo_enable(codec, 1);
-       else
-               tpa6130a2_stereo_enable(codec, 0);
-
-       return 0;
-}
-
 static int rx51_get_input(struct snd_kcontrol *kcontrol,
                          struct snd_ctl_elem_value *ucontrol)
 {
@@ -235,7 +221,7 @@ static struct snd_soc_jack_gpio rx51_av_jack_gpios[] = {
 static const struct snd_soc_dapm_widget aic34_dapm_widgets[] = {
        SND_SOC_DAPM_SPK("Ext Spk", rx51_spk_event),
        SND_SOC_DAPM_MIC("DMic", NULL),
-       SND_SOC_DAPM_HP("Headphone Jack", rx51_hp_event),
+       SND_SOC_DAPM_HP("Headphone Jack", NULL),
        SND_SOC_DAPM_MIC("HS Mic", NULL),
        SND_SOC_DAPM_LINE("FM Transmitter", NULL),
        SND_SOC_DAPM_SPK("Earphone", NULL),
@@ -246,11 +232,14 @@ static const struct snd_soc_dapm_route audio_map[] = {
        {"Ext Spk", NULL, "HPROUT"},
        {"Ext Spk", NULL, "HPLCOM"},
        {"Ext Spk", NULL, "HPRCOM"},
-       {"Headphone Jack", NULL, "LLOUT"},
-       {"Headphone Jack", NULL, "RLOUT"},
        {"FM Transmitter", NULL, "LLOUT"},
        {"FM Transmitter", NULL, "RLOUT"},
 
+       {"Headphone Jack", NULL, "TPA6130A2 HPLEFT"},
+       {"Headphone Jack", NULL, "TPA6130A2 HPRIGHT"},
+       {"TPA6130A2 LEFTIN", NULL, "LLOUT"},
+       {"TPA6130A2 RIGHTIN", NULL, "RLOUT"},
+
        {"DMic Rate 64", NULL, "DMic"},
        {"DMic", NULL, "Mic Bias"},
 
@@ -286,16 +275,10 @@ static const struct snd_kcontrol_new aic34_rx51_controls[] = {
 
 static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
 {
-       struct snd_soc_codec *codec = rtd->codec;
        struct snd_soc_card *card = rtd->card;
        struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
        int err;
 
-       err = tpa6130a2_add_controls(codec);
-       if (err < 0) {
-               dev_err(card->dev, "Failed to add TPA6130A2 controls\n");
-               return err;
-       }
        snd_soc_limit_volume(card, "TPA6130A2 Headphone Playback Volume", 42);
 
        err = omap_mcbsp_st_add_controls(rtd, 2);
@@ -357,6 +340,10 @@ static struct snd_soc_aux_dev rx51_aux_dev[] = {
                .name = "TLV320AIC34b",
                .codec_name = "tlv320aic3x-codec.2-0019",
        },
+       {
+               .name = "TPA61320A2",
+               .codec_name = "tpa6130a2.2-0060",
+       },
 };
 
 static struct snd_soc_codec_conf rx51_codec_conf[] = {
@@ -364,6 +351,10 @@ static struct snd_soc_codec_conf rx51_codec_conf[] = {
                .dev_name = "tlv320aic3x-codec.2-0019",
                .name_prefix = "b",
        },
+       {
+               .dev_name = "tpa6130a2.2-0060",
+               .name_prefix = "TPA6130A2",
+       },
 };
 
 /* Audio card */
@@ -435,11 +426,10 @@ static int rx51_soc_probe(struct platform_device *pdev)
                        dev_err(&pdev->dev, "Headphone amplifier node is not provided\n");
                        return -EINVAL;
                }
-
-               /* TODO: tpa6130a2a driver supports only a single instance, so
-                * this driver ignores the headphone-amplifier node for now.
-                * It's already mandatory in the DT binding to be future proof.
-                */
+               rx51_aux_dev[1].codec_name = NULL;
+               rx51_aux_dev[1].codec_of_node = dai_node;
+               rx51_codec_conf[1].dev_name = NULL;
+               rx51_codec_conf[1].of_node = dai_node;
        }
 
        pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
index 574c6af28c068e164601794ec102c87b33ba40e1..652e8c5ea1667185f6bb191a2cc8a3714cd74c1b 100644 (file)
  */
 
 #include <linux/module.h>
+#include <linux/mfd/syscon.h>
 #include <linux/delay.h>
 #include <linux/of_gpio.h>
+#include <linux/of_device.h>
 #include <linux/clk.h>
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 
 #define DRV_NAME "rockchip-i2s"
 
+struct rk_i2s_pins {
+       u32 reg_offset;
+       u32 shift;
+};
+
 struct rk_i2s_dev {
        struct device *dev;
 
@@ -33,6 +40,7 @@ struct rk_i2s_dev {
        struct snd_dmaengine_dai_dma_data playback_dma_data;
 
        struct regmap *regmap;
+       struct regmap *grf;
 
 /*
  * Used to indicate the tx/rx status.
@@ -42,6 +50,7 @@ struct rk_i2s_dev {
        bool tx_start;
        bool rx_start;
        bool is_master_mode;
+       const struct rk_i2s_pins *pins;
 };
 
 static int i2s_runtime_suspend(struct device *dev)
@@ -300,14 +309,38 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
                                   I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK,
                                   val);
 
+       if (!IS_ERR(i2s->grf) && i2s->pins) {
+               regmap_read(i2s->regmap, I2S_TXCR, &val);
+               val &= I2S_TXCR_CSR_MASK;
+
+               switch (val) {
+               case I2S_CHN_4:
+                       val = I2S_IO_4CH_OUT_6CH_IN;
+                       break;
+               case I2S_CHN_6:
+                       val = I2S_IO_6CH_OUT_4CH_IN;
+                       break;
+               case I2S_CHN_8:
+                       val = I2S_IO_8CH_OUT_2CH_IN;
+                       break;
+               default:
+                       val = I2S_IO_2CH_OUT_8CH_IN;
+                       break;
+               }
+
+               val <<= i2s->pins->shift;
+               val |= (I2S_IO_DIRECTION_MASK << i2s->pins->shift) << 16;
+               regmap_write(i2s->grf, i2s->pins->reg_offset, val);
+       }
+
        regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK,
                           I2S_DMACR_TDL(16));
        regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK,
                           I2S_DMACR_RDL(16));
 
        val = I2S_CKR_TRCM_TXRX;
-       if (dai->driver->symmetric_rates || rtd->dai_link->symmetric_rates)
-               val = I2S_CKR_TRCM_TXSHARE;
+       if (dai->driver->symmetric_rates && rtd->dai_link->symmetric_rates)
+               val = I2S_CKR_TRCM_TXONLY;
 
        regmap_update_bits(i2s->regmap, I2S_CKR,
                           I2S_CKR_TRCM_MASK,
@@ -485,9 +518,23 @@ static const struct regmap_config rockchip_i2s_regmap_config = {
        .cache_type = REGCACHE_FLAT,
 };
 
+static const struct rk_i2s_pins rk3399_i2s_pins = {
+       .reg_offset = 0xe220,
+       .shift = 11,
+};
+
+static const struct of_device_id rockchip_i2s_match[] = {
+       { .compatible = "rockchip,rk3066-i2s", },
+       { .compatible = "rockchip,rk3188-i2s", },
+       { .compatible = "rockchip,rk3288-i2s", },
+       { .compatible = "rockchip,rk3399-i2s", .data = &rk3399_i2s_pins },
+       {},
+};
+
 static int rockchip_i2s_probe(struct platform_device *pdev)
 {
        struct device_node *node = pdev->dev.of_node;
+       const struct of_device_id *of_id;
        struct rk_i2s_dev *i2s;
        struct snd_soc_dai_driver *soc_dai;
        struct resource *res;
@@ -501,6 +548,17 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
+       i2s->dev = &pdev->dev;
+
+       i2s->grf = syscon_regmap_lookup_by_phandle(node, "rockchip,grf");
+       if (!IS_ERR(i2s->grf)) {
+               of_id = of_match_device(rockchip_i2s_match, &pdev->dev);
+               if (!of_id || !of_id->data)
+                       return -EINVAL;
+
+               i2s->pins = of_id->data;
+       }
+
        /* try to prepare related clocks */
        i2s->hclk = devm_clk_get(&pdev->dev, "i2s_hclk");
        if (IS_ERR(i2s->hclk)) {
@@ -540,7 +598,6 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
        i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
        i2s->capture_dma_data.maxburst = 4;
 
-       i2s->dev = &pdev->dev;
        dev_set_drvdata(&pdev->dev, i2s);
 
        pm_runtime_enable(&pdev->dev);
@@ -606,14 +663,6 @@ static int rockchip_i2s_remove(struct platform_device *pdev)
        return 0;
 }
 
-static const struct of_device_id rockchip_i2s_match[] = {
-       { .compatible = "rockchip,rk3066-i2s", },
-       { .compatible = "rockchip,rk3188-i2s", },
-       { .compatible = "rockchip,rk3288-i2s", },
-       { .compatible = "rockchip,rk3399-i2s", },
-       {},
-};
-
 static const struct dev_pm_ops rockchip_i2s_pm_ops = {
        SET_RUNTIME_PM_OPS(i2s_runtime_suspend, i2s_runtime_resume,
                           NULL)
index dc6e2c74d08818bf68d333f2ca4d811eb914b7ed..31f11fd25393ac37d0a696bdc3e5de5b37150590 100644 (file)
@@ -81,8 +81,8 @@
 #define I2S_CKR_TRCM_SHIFT     28
 #define I2S_CKR_TRCM(x)        (x << I2S_CKR_TRCM_SHIFT)
 #define I2S_CKR_TRCM_TXRX      (0 << I2S_CKR_TRCM_SHIFT)
-#define I2S_CKR_TRCM_TXSHARE   (1 << I2S_CKR_TRCM_SHIFT)
-#define I2S_CKR_TRCM_RXSHARE   (2 << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_TXONLY    (1 << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_RXONLY    (2 << I2S_CKR_TRCM_SHIFT)
 #define I2S_CKR_TRCM_MASK      (3 << I2S_CKR_TRCM_SHIFT)
 #define I2S_CKR_MSS_SHIFT      27
 #define I2S_CKR_MSS_MASTER     (0 << I2S_CKR_MSS_SHIFT)
@@ -236,4 +236,11 @@ enum {
 #define I2S_TXDR       (0x0024)
 #define I2S_RXDR       (0x0028)
 
+/* io direction cfg register */
+#define I2S_IO_DIRECTION_MASK  (7)
+#define I2S_IO_8CH_OUT_2CH_IN  (0)
+#define I2S_IO_6CH_OUT_4CH_IN  (4)
+#define I2S_IO_4CH_OUT_6CH_IN  (6)
+#define I2S_IO_2CH_OUT_8CH_IN  (7)
+
 #endif /* _ROCKCHIP_IIS_H */
index 543610282cdb05c007b5fbe52b0c2f77435b3a83..e70ffad07184fafa3584c2f7ee18a883baa386fb 100644 (file)
 #define DRV_NAME "rockchip-snd-max98090"
 
 static struct snd_soc_jack headset_jack;
+
+/* Headset jack detection DAPM pins */
 static struct snd_soc_jack_pin headset_jack_pins[] = {
        {
-               .pin = "Headset Jack",
-               .mask = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
-                       SND_JACK_BTN_0 | SND_JACK_BTN_1 |
-                       SND_JACK_BTN_2 | SND_JACK_BTN_3,
+               .pin = "Headphone",
+               .mask = SND_JACK_HEADPHONE,
+       },
+       {
+               .pin = "Headset Mic",
+               .mask = SND_JACK_MICROPHONE,
        },
+
 };
 
 static const struct snd_soc_dapm_widget rk_dapm_widgets[] = {
@@ -53,7 +58,7 @@ static const struct snd_soc_dapm_widget rk_dapm_widgets[] = {
 static const struct snd_soc_dapm_route rk_audio_map[] = {
        {"IN34", NULL, "Headset Mic"},
        {"IN34", NULL, "MICBIAS"},
-       {"MICBIAS", NULL, "Headset Mic"},
+       {"Headset Mic", NULL, "MICBIAS"},
        {"DMICL", NULL, "Int Mic"},
        {"Headphone", NULL, "HPL"},
        {"Headphone", NULL, "HPR"},
@@ -114,43 +119,27 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
        return ret;
 }
 
-static int rk_init(struct snd_soc_pcm_runtime *runtime)
-{
-       /* Enable Headset and 4 Buttons Jack detection */
-       return snd_soc_card_jack_new(runtime->card, "Headset Jack",
-                              SND_JACK_HEADSET |
-                              SND_JACK_BTN_0 | SND_JACK_BTN_1 |
-                              SND_JACK_BTN_2 | SND_JACK_BTN_3,
-                              &headset_jack,
-                              headset_jack_pins,
-                              ARRAY_SIZE(headset_jack_pins));
-}
-
-static int rk_98090_headset_init(struct snd_soc_component *component)
-{
-       return ts3a227e_enable_jack_detect(component, &headset_jack);
-}
-
 static struct snd_soc_ops rk_aif1_ops = {
        .hw_params = rk_aif1_hw_params,
 };
 
-static struct snd_soc_aux_dev rk_98090_headset_dev = {
-       .name = "Headset Chip",
-       .init = rk_98090_headset_init,
-};
-
 static struct snd_soc_dai_link rk_dailink = {
        .name = "max98090",
        .stream_name = "Audio",
        .codec_dai_name = "HiFi",
-       .init = rk_init,
        .ops = &rk_aif1_ops,
        /* set max98090 as slave */
        .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
                SND_SOC_DAIFMT_CBS_CFS,
 };
 
+static int rk_98090_headset_init(struct snd_soc_component *component);
+
+static struct snd_soc_aux_dev rk_98090_headset_dev = {
+       .name = "Headset Chip",
+       .init = rk_98090_headset_init,
+};
+
 static struct snd_soc_card snd_soc_card_rk = {
        .name = "ROCKCHIP-I2S",
        .owner = THIS_MODULE,
@@ -166,6 +155,26 @@ static struct snd_soc_card snd_soc_card_rk = {
        .num_controls = ARRAY_SIZE(rk_mc_controls),
 };
 
+static int rk_98090_headset_init(struct snd_soc_component *component)
+{
+       int ret;
+
+       /* Enable Headset and 4 Buttons Jack detection */
+       ret = snd_soc_card_jack_new(&snd_soc_card_rk, "Headset Jack",
+                                   SND_JACK_HEADSET |
+                                   SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+                                   SND_JACK_BTN_2 | SND_JACK_BTN_3,
+                                   &headset_jack,
+                                   headset_jack_pins,
+                                   ARRAY_SIZE(headset_jack_pins));
+       if (ret)
+               return ret;
+
+       ret = ts3a227e_enable_jack_detect(component, &headset_jack);
+
+       return ret;
+}
+
 static int snd_rk_mc_probe(struct platform_device *pdev)
 {
        int ret = 0;
index 100781e37848a7b7c716cde4ddae69501867150c..4ca265737edaaf3def95014793f6837d9de065b0 100644 (file)
@@ -101,21 +101,7 @@ static int rk_spdif_hw_params(struct snd_pcm_substream *substream,
        int ret;
 
        srate = params_rate(params);
-       switch (srate) {
-       case 32000:
-       case 48000:
-       case 96000:
-               mclk = 96000 * 128; /* 12288000 hz */
-               break;
-       case 44100:
-               mclk = 44100 * 256; /* 11289600 hz */
-               break;
-       case 192000:
-               mclk = 192000 * 128; /* 24576000 hz */
-               break;
-       default:
-               return -EINVAL;
-       }
+       mclk = srate * 128;
 
        switch (params_format(params)) {
        case SNDRV_PCM_FORMAT_S16_LE:
@@ -139,7 +125,6 @@ static int rk_spdif_hw_params(struct snd_pcm_substream *substream,
                return ret;
        }
 
-       val |= SPDIF_CFGR_CLK_DIV(mclk/(srate * 256));
        ret = regmap_update_bits(spdif->regmap, SPDIF_CFGR,
                SPDIF_CFGR_CLK_DIV_MASK | SPDIF_CFGR_HALFWORD_ENABLE |
                SDPIF_CFGR_VDW_MASK,
index 78baa26e938b5e783d4c5babdfeb3ec7914b5770..7b722b0094d92c6dbdf41717361cbf1b9e934c85 100644 (file)
@@ -224,14 +224,6 @@ config SND_SOC_SNOW
          Say Y if you want to add audio support for various Snow
          boards based on Exynos5 series of SoCs.
 
-config SND_SOC_ODROIDX2
-       tristate "Audio support for Odroid-X2 and Odroid-U3"
-       depends on SND_SOC_SAMSUNG && I2C
-       select SND_SOC_MAX98090
-       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 && I2C
index 052fe71be518311fda390ea913b6170d66cab985..5d03f5ce6916cc2ef644936f331e60806dbd6eac 100644 (file)
@@ -43,7 +43,6 @@ snd-soc-tobermory-objs := tobermory.o
 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
@@ -69,5 +68,4 @@ obj-$(CONFIG_SND_SOC_TOBERMORY) += snd-soc-tobermory.o
 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 4a7a503fe13c121ea55c046772a5c43d4f1349f3..547d3103208897ab1d515621bf9d303e345337ba 100644 (file)
@@ -389,7 +389,8 @@ static int s3c_ac97_probe(struct platform_device *pdev)
                goto err5;
 
        ret = samsung_asoc_dma_platform_register(&pdev->dev,
-                                                ac97_pdata->dma_filter);
+                                                ac97_pdata->dma_filter,
+                                                NULL, NULL);
        if (ret) {
                dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret);
                goto err5;
index a7616cc9b39ee5a59221ddaaf5ed61859d253234..3830f297e0b668d1ac8900f6e9ae10ed5a020bf7 100644 (file)
@@ -26,7 +26,10 @@ struct s3c_dma_params {
 void samsung_asoc_init_dma_data(struct snd_soc_dai *dai,
                                struct s3c_dma_params *playback,
                                struct s3c_dma_params *capture);
-int samsung_asoc_dma_platform_register(struct device *dev,
-                                      dma_filter_fn fn);
-
+/*
+ * @tx, @rx arguments can be NULL if the DMA channel names are "tx", "rx",
+ * otherwise actual DMA channel names must be passed to this function.
+ */
+int samsung_asoc_dma_platform_register(struct device *dev, dma_filter_fn filter,
+                                      const char *tx, const char *rx);
 #endif
index 063125937311e9600f063a3cc8ddac308e876c7f..2c87f380bfc434b12591412a149e3f43e12c993d 100644 (file)
 
 #include "dma.h"
 
-static struct snd_dmaengine_pcm_config samsung_dmaengine_pcm_config = {
-       .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
-};
-
 void samsung_asoc_init_dma_data(struct snd_soc_dai *dai,
                                struct s3c_dma_params *playback,
                                struct s3c_dma_params *capture)
@@ -58,15 +54,28 @@ void samsung_asoc_init_dma_data(struct snd_soc_dai *dai,
 }
 EXPORT_SYMBOL_GPL(samsung_asoc_init_dma_data);
 
-int samsung_asoc_dma_platform_register(struct device *dev,
-                                      dma_filter_fn filter)
+int samsung_asoc_dma_platform_register(struct device *dev, dma_filter_fn filter,
+                                      const char *tx, const char *rx)
 {
-       samsung_dmaengine_pcm_config.compat_filter_fn = filter;
+       unsigned int flags = SND_DMAENGINE_PCM_FLAG_COMPAT;
+
+       struct snd_dmaengine_pcm_config *pcm_conf;
+
+       pcm_conf = devm_kzalloc(dev, sizeof(*pcm_conf), GFP_KERNEL);
+       if (!pcm_conf)
+               return -ENOMEM;
+
+       pcm_conf->prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config;
+       pcm_conf->compat_filter_fn = filter;
+
+       if (dev->of_node) {
+               pcm_conf->chan_names[SNDRV_PCM_STREAM_PLAYBACK] = tx;
+               pcm_conf->chan_names[SNDRV_PCM_STREAM_CAPTURE] = rx;
+       } else {
+               flags |= SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME;
+       }
 
-       return devm_snd_dmaengine_pcm_register(dev,
-                       &samsung_dmaengine_pcm_config,
-                       SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME |
-                       SND_DMAENGINE_PCM_FLAG_COMPAT);
+       return devm_snd_dmaengine_pcm_register(dev, pcm_conf, flags);
 }
 EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_register);
 
index 70a2559b63f9050bcbb86049717518d022463e2f..50635ee8ff20cb74478d6726bdd083c6557418fc 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/of_gpio.h>
 #include <linux/pm_runtime.h>
 
@@ -1106,19 +1107,9 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec)
        return i2s;
 }
 
-static const struct of_device_id exynos_i2s_match[];
-
-static inline const struct samsung_i2s_dai_data *samsung_i2s_get_driver_data(
-                                               struct platform_device *pdev)
+static void i2s_free_sec_dai(struct i2s_dai *i2s)
 {
-       if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
-               const struct of_device_id *match;
-               match = of_match_node(exynos_i2s_match, pdev->dev.of_node);
-               return match ? match->data : NULL;
-       } else {
-               return (struct samsung_i2s_dai_data *)
-                               platform_get_device_id(pdev)->driver_data;
-       }
+       platform_device_del(i2s->pdev);
 }
 
 #ifdef CONFIG_PM
@@ -1233,9 +1224,13 @@ static int samsung_i2s_probe(struct platform_device *pdev)
        const struct samsung_i2s_dai_data *i2s_dai_data;
        int ret;
 
-       /* Call during Seconday interface registration */
-       i2s_dai_data = samsung_i2s_get_driver_data(pdev);
+       if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node)
+               i2s_dai_data = of_device_get_match_data(&pdev->dev);
+       else
+               i2s_dai_data = (struct samsung_i2s_dai_data *)
+                               platform_get_device_id(pdev)->driver_data;
 
+       /* Call during the secondary interface registration */
        if (i2s_dai_data->dai_type == TYPE_SEC) {
                sec_dai = dev_get_drvdata(&pdev->dev);
                if (!sec_dai) {
@@ -1249,7 +1244,7 @@ static int samsung_i2s_probe(struct platform_device *pdev)
                        return ret;
 
                return samsung_asoc_dma_platform_register(&pdev->dev,
-                                                         sec_dai->filter);
+                                       sec_dai->filter, "tx-sec", NULL);
        }
 
        pri_dai = i2s_alloc_dai(pdev, false);
@@ -1350,17 +1345,28 @@ static int samsung_i2s_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       devm_snd_soc_register_component(&pri_dai->pdev->dev,
+       ret = devm_snd_soc_register_component(&pri_dai->pdev->dev,
                                        &samsung_i2s_component,
                                        &pri_dai->i2s_dai_drv, 1);
+       if (ret < 0)
+               goto err_free_dai;
+
+       ret = samsung_asoc_dma_platform_register(&pdev->dev, pri_dai->filter,
+                                                NULL, NULL);
+       if (ret < 0)
+               goto err_free_dai;
 
        pm_runtime_enable(&pdev->dev);
 
-       ret = samsung_asoc_dma_platform_register(&pdev->dev, pri_dai->filter);
-       if (ret != 0)
-               return ret;
+       ret = i2s_register_clock_provider(pdev);
+       if (!ret)
+               return 0;
 
-       return i2s_register_clock_provider(pdev);
+       pm_runtime_disable(&pdev->dev);
+err_free_dai:
+       if (sec_dai)
+               i2s_free_sec_dai(sec_dai);
+       return ret;
 }
 
 static int samsung_i2s_remove(struct platform_device *pdev)
@@ -1477,10 +1483,6 @@ static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 = {
        .i2s_variant_regs = &i2sv5_i2s1_regs,
 };
 
-static const struct samsung_i2s_dai_data samsung_dai_type_pri = {
-       .dai_type = TYPE_PRI,
-};
-
 static const struct samsung_i2s_dai_data samsung_dai_type_sec = {
        .dai_type = TYPE_SEC,
 };
@@ -1492,9 +1494,6 @@ static const struct platform_device_id samsung_i2s_driver_ids[] = {
        }, {
                .name           = "samsung-i2s-sec",
                .driver_data    = (kernel_ulong_t)&samsung_dai_type_sec,
-       }, {
-               .name           = "samsung-i2sv4",
-               .driver_data    = (kernel_ulong_t)&i2sv5_dai_type,
        },
        {},
 };
diff --git a/sound/soc/samsung/odroidx2_max98090.c b/sound/soc/samsung/odroidx2_max98090.c
deleted file mode 100644 (file)
index 0421727..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2014 Samsung Electronics Co., Ltd.
- *
- * This program is free software; you can redistribute  it and/or modify it
- * under  the terms of  the GNU General  Public License as published by the
- * Free Software Foundation;  either version 2 of the  License, or (at your
- * option) any later version.
- */
-
-#include <linux/of.h>
-#include <linux/module.h>
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-#include "i2s.h"
-
-struct odroidx2_drv_data {
-       const struct snd_soc_dapm_widget *dapm_widgets;
-       unsigned int num_dapm_widgets;
-};
-
-/* The I2S CDCLK output clock frequency for the MAX98090 codec */
-#define MAX98090_MCLK 19200000
-
-static struct snd_soc_dai_link odroidx2_dai[];
-
-static int odroidx2_late_probe(struct snd_soc_card *card)
-{
-       struct snd_soc_pcm_runtime *rtd;
-       struct snd_soc_dai *codec_dai;
-       struct snd_soc_dai *cpu_dai;
-       int ret;
-
-       rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
-       codec_dai = rtd->codec_dai;
-       cpu_dai = rtd->cpu_dai;
-
-       ret = snd_soc_dai_set_sysclk(codec_dai, 0, MAX98090_MCLK,
-                                               SND_SOC_CLOCK_IN);
-
-       if (ret < 0 || of_find_property(odroidx2_dai[0].codec_of_node,
-                                       "clocks", NULL))
-               return ret;
-
-       /* Set the cpu DAI configuration in order to use CDCLK */
-       return snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,
-                                       0, SND_SOC_CLOCK_OUT);
-}
-
-static const struct snd_soc_dapm_widget odroidx2_dapm_widgets[] = {
-       SND_SOC_DAPM_HP("Headphone Jack", NULL),
-       SND_SOC_DAPM_MIC("Mic Jack", NULL),
-       SND_SOC_DAPM_MIC("DMIC", NULL),
-};
-
-static const struct snd_soc_dapm_widget odroidu3_dapm_widgets[] = {
-       SND_SOC_DAPM_HP("Headphone Jack", NULL),
-       SND_SOC_DAPM_SPK("Speakers", NULL),
-};
-
-static struct snd_soc_dai_link odroidx2_dai[] = {
-       {
-               .name           = "MAX98090",
-               .stream_name    = "MAX98090 PCM",
-               .codec_dai_name = "HiFi",
-               .dai_fmt        = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
-                                 SND_SOC_DAIFMT_CBM_CFM,
-       }
-};
-
-static struct snd_soc_card odroidx2 = {
-       .owner                  = THIS_MODULE,
-       .dai_link               = odroidx2_dai,
-       .num_links              = ARRAY_SIZE(odroidx2_dai),
-       .fully_routed           = true,
-       .late_probe             = odroidx2_late_probe,
-};
-
-static const struct odroidx2_drv_data odroidx2_drvdata = {
-       .dapm_widgets           = odroidx2_dapm_widgets,
-       .num_dapm_widgets       = ARRAY_SIZE(odroidx2_dapm_widgets),
-};
-
-static const struct odroidx2_drv_data odroidu3_drvdata = {
-       .dapm_widgets           = odroidu3_dapm_widgets,
-       .num_dapm_widgets       = ARRAY_SIZE(odroidu3_dapm_widgets),
-};
-
-static const struct of_device_id odroidx2_audio_of_match[] = {
-       {
-               .compatible     = "samsung,odroidx2-audio",
-               .data           = &odroidx2_drvdata,
-       }, {
-               .compatible     = "samsung,odroidu3-audio",
-               .data           = &odroidu3_drvdata,
-       },
-       { },
-};
-MODULE_DEVICE_TABLE(of, odroidx2_audio_of_match);
-
-static int odroidx2_audio_probe(struct platform_device *pdev)
-{
-       struct device_node *snd_node = pdev->dev.of_node;
-       struct snd_soc_card *card = &odroidx2;
-       struct device_node *i2s_node, *codec_node;
-       struct odroidx2_drv_data *dd;
-       const struct of_device_id *of_id;
-       int ret;
-
-       of_id = of_match_node(odroidx2_audio_of_match, snd_node);
-       dd = (struct odroidx2_drv_data *)of_id->data;
-
-       card->num_dapm_widgets = dd->num_dapm_widgets;
-       card->dapm_widgets = dd->dapm_widgets;
-
-       card->dev = &pdev->dev;
-
-       ret = snd_soc_of_parse_card_name(card, "samsung,model");
-       if (ret < 0)
-               return ret;
-
-       ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing");
-       if (ret < 0)
-               return ret;
-
-       codec_node = of_parse_phandle(snd_node, "samsung,audio-codec", 0);
-       if (!codec_node) {
-               dev_err(&pdev->dev,
-                       "Failed parsing samsung,i2s-codec property\n");
-               return -EINVAL;
-       }
-
-       i2s_node = of_parse_phandle(snd_node, "samsung,i2s-controller", 0);
-       if (!i2s_node) {
-               dev_err(&pdev->dev,
-                       "Failed parsing samsung,i2s-controller property\n");
-               ret = -EINVAL;
-               goto err_put_codec_n;
-       }
-
-       odroidx2_dai[0].codec_of_node = codec_node;
-       odroidx2_dai[0].cpu_of_node = i2s_node;
-       odroidx2_dai[0].platform_of_node = i2s_node;
-
-       ret = snd_soc_register_card(card);
-       if (ret) {
-               dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
-                       ret);
-               goto err_put_i2s_n;
-       }
-       return 0;
-
-err_put_i2s_n:
-       of_node_put(i2s_node);
-err_put_codec_n:
-       of_node_put(codec_node);
-       return ret;
-}
-
-static int odroidx2_audio_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
-
-       of_node_put(odroidx2_dai[0].cpu_of_node);
-       of_node_put(odroidx2_dai[0].codec_of_node);
-
-       return 0;
-}
-
-static struct platform_driver odroidx2_audio_driver = {
-       .driver = {
-               .name           = "odroidx2-audio",
-               .of_match_table = odroidx2_audio_of_match,
-               .pm             = &snd_soc_pm_ops,
-       },
-       .probe  = odroidx2_audio_probe,
-       .remove = odroidx2_audio_remove,
-};
-module_platform_driver(odroidx2_audio_driver);
-
-MODULE_AUTHOR("Chen Zhen <zhen1.chen@samsung.com>");
-MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
-MODULE_DESCRIPTION("ALSA SoC Odroid X2/U3 Audio Support");
-MODULE_LICENSE("GPL v2");
index 498f563a4c9cb2e7fb483c05f367ef348370bb5d..490c1a87fd66232c798741ed13452802cfcf7223 100644 (file)
@@ -576,7 +576,8 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev)
                goto err5;
        }
 
-       ret = samsung_asoc_dma_platform_register(&pdev->dev, filter);
+       ret = samsung_asoc_dma_platform_register(&pdev->dev, filter,
+                                                NULL, NULL);
        if (ret) {
                dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret);
                goto err5;
index b6ab3fc5789e89a92b937e1b2c95a1c6fa2c0063..bf8ae79b0fd2fcf9c9856de51e89a8183b9f847d 100644 (file)
@@ -268,7 +268,7 @@ static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
                iismod &= ~S3C2412_IISMOD_SLAVE;
                break;
        default:
-               pr_err("unknwon master/slave format\n");
+               pr_err("unknown master/slave format\n");
                return -EINVAL;
        }
 
index 204029d12f5b2f639f487ffe6ef591636fdf4746..d45dffb297d8f90d92113e84b2b45b08547f47d4 100644 (file)
@@ -177,7 +177,8 @@ static int s3c2412_iis_dev_probe(struct platform_device *pdev)
        }
 
        ret = samsung_asoc_dma_platform_register(&pdev->dev,
-                                                pdata->dma_filter);
+                                                pdata->dma_filter,
+                                                NULL, NULL);
        if (ret)
                pr_err("failed to register the DMA: %d\n", ret);
 
index b3a475d73ba790c6d67bc21ebd271950e3c36f2c..3e76f2a75a24d53477a8d4d7844c9b052a1fbd94 100644 (file)
@@ -482,7 +482,8 @@ static int s3c24xx_iis_dev_probe(struct platform_device *pdev)
        }
 
        ret = samsung_asoc_dma_platform_register(&pdev->dev,
-                                                pdata->dma_filter);
+                                                pdata->dma_filter,
+                                                NULL, NULL);
        if (ret)
                pr_err("failed to register the dma: %d\n", ret);
 
index 4687f521197c5c46a7255873e94f9b0d67215d9a..0cb9c8567546fe2e9aad562d0a997f1a290346e3 100644 (file)
@@ -435,7 +435,8 @@ static int spdif_probe(struct platform_device *pdev)
 
        spdif->dma_playback = &spdif_stereo_out;
 
-       ret = samsung_asoc_dma_platform_register(&pdev->dev, filter);
+       ret = samsung_asoc_dma_platform_register(&pdev->dev, filter,
+                                                NULL, NULL);
        if (ret) {
                dev_err(&pdev->dev, "failed to register DMA: %d\n", ret);
                goto err4;
index c9902a6d6fa0b6a903a77c8dd6dd1ee46b0db160..9311f119feb5da5b8889427d603a642e3b124cd3 100644 (file)
@@ -44,6 +44,7 @@ config SND_SOC_RCAR
 
 config SND_SOC_RSRC_CARD
        tristate "Renesas Sampling Rate Convert Sound Card"
+       select SND_SIMPLE_CARD_UTILS
        help
          This option enables simple sound if you need sampling rate convert
 
index c4c51a4d3c8fd7fdbeec39defae6745500726e1a..2145957d02290f4e61b97fdf01b01b4716b7f0b6 100644 (file)
@@ -33,11 +33,15 @@ struct rsnd_adg {
        struct clk *clkout[CLKOUTMAX];
        struct clk_onecell_data onecell;
        struct rsnd_mod mod;
+       u32 flags;
 
        int rbga_rate_for_441khz; /* RBGA */
        int rbgb_rate_for_48khz;  /* RBGB */
 };
 
+#define LRCLK_ASYNC    (1 << 0)
+#define adg_mode_flags(adg)    (adg->flags)
+
 #define for_each_rsnd_clk(pos, adg, i)         \
        for (i = 0;                             \
             (i < CLKMAX) &&                    \
@@ -355,6 +359,16 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate)
 
        rsnd_adg_set_ssi_clk(ssi_mod, data);
 
+       if (!(adg_mode_flags(adg) & LRCLK_ASYNC)) {
+               struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
+               u32 ckr = 0;
+
+               if (0 == (rate % 8000))
+                       ckr = 0x80000000;
+
+               rsnd_mod_bset(adg_mod, SSICKR, 0x80000000, ckr);
+       }
+
        dev_dbg(dev, "ADG: %s[%d] selects 0x%x for %d\n",
                rsnd_mod_name(ssi_mod), rsnd_mod_id(ssi_mod),
                data, rate);
@@ -532,6 +546,7 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
 {
        struct rsnd_adg *adg;
        struct device *dev = rsnd_priv_to_dev(priv);
+       struct device_node *np = dev->of_node;
 
        adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL);
        if (!adg) {
@@ -545,6 +560,9 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
        rsnd_adg_get_clkin(priv, adg);
        rsnd_adg_get_clkout(priv, adg);
 
+       if (of_get_property(np, "clkout-lr-asynchronous", NULL))
+               adg->flags = LRCLK_ASYNC;
+
        priv->adg = adg;
 
        return 0;
index 46c0ba7b6414944ad6d5887735ec7ccce93c80c5..7d2fdf8dd1882f7188b4e2a33e7e771dfbe064dc 100644 (file)
@@ -206,7 +206,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
  */
 static int rsnd_gen2_probe(struct rsnd_priv *priv)
 {
-       const static struct rsnd_regmap_field_conf conf_ssiu[] = {
+       static const struct rsnd_regmap_field_conf conf_ssiu[] = {
                RSND_GEN_S_REG(SSI_MODE0,       0x800),
                RSND_GEN_S_REG(SSI_MODE1,       0x804),
                RSND_GEN_S_REG(SSI_MODE2,       0x808),
@@ -221,7 +221,7 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
                RSND_GEN_M_REG(SSI_INT_ENABLE,  0x18,   0x80),
        };
 
-       const static struct rsnd_regmap_field_conf conf_scu[] = {
+       static const struct rsnd_regmap_field_conf conf_scu[] = {
                RSND_GEN_M_REG(SRC_I_BUSIF_MODE,0x0,    0x20),
                RSND_GEN_M_REG(SRC_O_BUSIF_MODE,0x4,    0x20),
                RSND_GEN_M_REG(SRC_BUSIF_DALIGN,0x8,    0x20),
@@ -308,7 +308,7 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
                RSND_GEN_M_REG(DVC_VOL7R,       0xe44,  0x100),
                RSND_GEN_M_REG(DVC_DVUER,       0xe48,  0x100),
        };
-       const static struct rsnd_regmap_field_conf conf_adg[] = {
+       static const struct rsnd_regmap_field_conf conf_adg[] = {
                RSND_GEN_S_REG(BRRA,            0x00),
                RSND_GEN_S_REG(BRRB,            0x04),
                RSND_GEN_S_REG(SSICKR,          0x08),
@@ -328,7 +328,7 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
                RSND_GEN_S_REG(SRCOUT_TIMSEL4,  0x58),
                RSND_GEN_S_REG(CMDOUT_TIMSEL,   0x5c),
        };
-       const static struct rsnd_regmap_field_conf conf_ssi[] = {
+       static const struct rsnd_regmap_field_conf conf_ssi[] = {
                RSND_GEN_M_REG(SSICR,           0x00,   0x40),
                RSND_GEN_M_REG(SSISR,           0x04,   0x40),
                RSND_GEN_M_REG(SSITDR,          0x08,   0x40),
@@ -359,14 +359,14 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
 
 static int rsnd_gen1_probe(struct rsnd_priv *priv)
 {
-       const static struct rsnd_regmap_field_conf conf_adg[] = {
+       static const struct rsnd_regmap_field_conf conf_adg[] = {
                RSND_GEN_S_REG(BRRA,            0x00),
                RSND_GEN_S_REG(BRRB,            0x04),
                RSND_GEN_S_REG(SSICKR,          0x08),
                RSND_GEN_S_REG(AUDIO_CLK_SEL0,  0x0c),
                RSND_GEN_S_REG(AUDIO_CLK_SEL1,  0x10),
        };
-       const static struct rsnd_regmap_field_conf conf_ssi[] = {
+       static const struct rsnd_regmap_field_conf conf_ssi[] = {
                RSND_GEN_M_REG(SSICR,           0x00,   0x40),
                RSND_GEN_M_REG(SSISR,           0x04,   0x40),
                RSND_GEN_M_REG(SSITDR,          0x08,   0x40),
index 1bc7ecfc42a9e18004d7726e2de45ca06f1542ca..fa37f842b62f51d197c2da27c0a45be2a13880d1 100644 (file)
@@ -20,6 +20,7 @@
 #include <sound/jack.h>
 #include <sound/soc.h>
 #include <sound/soc-dai.h>
+#include <sound/simple_card_utils.h>
 
 struct rsrc_card_of_data {
        const char *prefix;
@@ -46,25 +47,13 @@ static const struct of_device_id rsrc_card_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, rsrc_card_of_match);
 
-#define DAI_NAME_NUM   32
-struct rsrc_card_dai {
-       unsigned int sysclk;
-       unsigned int tx_slot_mask;
-       unsigned int rx_slot_mask;
-       int slots;
-       int slot_width;
-       struct clk *clk;
-       char dai_name[DAI_NAME_NUM];
-};
-
 #define IDX_CPU                0
 #define IDX_CODEC      1
 struct rsrc_card_priv {
        struct snd_soc_card snd_card;
        struct snd_soc_codec_conf codec_conf;
-       struct rsrc_card_dai *dai_props;
+       struct asoc_simple_dai *dai_props;
        struct snd_soc_dai_link *dai_link;
-       int dai_num;
        u32 convert_rate;
        u32 convert_channels;
 };
@@ -77,7 +66,7 @@ static int rsrc_card_startup(struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct rsrc_card_priv *priv =   snd_soc_card_get_drvdata(rtd->card);
-       struct rsrc_card_dai *dai_props =
+       struct asoc_simple_dai *dai_props =
                rsrc_priv_to_props(priv, rtd->num);
 
        return clk_prepare_enable(dai_props->clk);
@@ -87,7 +76,7 @@ static void rsrc_card_shutdown(struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct rsrc_card_priv *priv =   snd_soc_card_get_drvdata(rtd->card);
-       struct rsrc_card_dai *dai_props =
+       struct asoc_simple_dai *dai_props =
                rsrc_priv_to_props(priv, rtd->num);
 
        clk_disable_unprepare(dai_props->clk);
@@ -103,7 +92,7 @@ static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd)
        struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
        struct snd_soc_dai *dai;
        struct snd_soc_dai_link *dai_link;
-       struct rsrc_card_dai *dai_props;
+       struct asoc_simple_dai *dai_props;
        int num = rtd->num;
        int ret;
 
@@ -159,44 +148,13 @@ static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
        return 0;
 }
 
-static int rsrc_card_parse_daifmt(struct device_node *node,
-                                 struct device_node *codec,
-                                 struct rsrc_card_priv *priv,
-                                 struct snd_soc_dai_link *dai_link,
-                                 unsigned int *retfmt)
-{
-       struct device_node *bitclkmaster = NULL;
-       struct device_node *framemaster = NULL;
-       unsigned int daifmt;
-
-       daifmt = snd_soc_of_parse_daifmt(node, NULL,
-                                        &bitclkmaster, &framemaster);
-       daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
-
-       if (!bitclkmaster && !framemaster)
-               return -EINVAL;
-
-       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;
-
-       of_node_put(bitclkmaster);
-       of_node_put(framemaster);
-
-       *retfmt = daifmt;
-
-       return 0;
-}
-
 static int rsrc_card_parse_links(struct device_node *np,
                                 struct rsrc_card_priv *priv,
                                 int idx, bool is_fe)
 {
+       struct device *dev = rsrc_priv_to_dev(priv);
        struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx);
-       struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx);
+       struct asoc_simple_dai *dai_props = rsrc_priv_to_props(priv, idx);
        struct of_phandle_args args;
        int ret;
 
@@ -232,9 +190,11 @@ static int rsrc_card_parse_links(struct device_node *np,
                if (ret < 0)
                        return ret;
 
-               /* set dai_name */
-               snprintf(dai_props->dai_name, DAI_NAME_NUM, "fe.%s",
-                        dai_link->cpu_dai_name);
+               ret = asoc_simple_card_set_dailink_name(dev, dai_link,
+                                                       "fe.%s",
+                                                       dai_link->cpu_dai_name);
+               if (ret < 0)
+                       return ret;
 
                /*
                 * In soc_bind_dai_link() will check cpu name after
@@ -248,7 +208,6 @@ static int rsrc_card_parse_links(struct device_node *np,
                if (!args.args_count)
                        dai_link->cpu_dai_name = NULL;
        } else {
-               struct device *dev = rsrc_priv_to_dev(priv);
                const struct rsrc_card_of_data *of_data;
 
                of_data = of_device_get_match_data(dev);
@@ -266,6 +225,12 @@ static int rsrc_card_parse_links(struct device_node *np,
                if (ret < 0)
                        return ret;
 
+               ret = asoc_simple_card_set_dailink_name(dev, dai_link,
+                                                       "be.%s",
+                                                       dai_link->codec_dai_name);
+               if (ret < 0)
+                       return ret;
+
                /* additional name prefix */
                if (of_data) {
                        priv->codec_conf.of_node = dai_link->codec_of_node;
@@ -276,18 +241,12 @@ static int rsrc_card_parse_links(struct device_node *np,
                                                      dai_link->codec_of_node,
                                                      "audio-prefix");
                }
-
-               /* set dai_name */
-               snprintf(dai_props->dai_name, DAI_NAME_NUM, "be.%s",
-                        dai_link->codec_dai_name);
        }
 
        /* Simple Card assumes platform == cpu */
        dai_link->platform_of_node      = dai_link->cpu_of_node;
        dai_link->dpcm_playback         = 1;
        dai_link->dpcm_capture          = 1;
-       dai_link->name                  = dai_props->dai_name;
-       dai_link->stream_name           = dai_props->dai_name;
        dai_link->ops                   = &rsrc_card_ops;
        dai_link->init                  = rsrc_card_dai_init;
 
@@ -299,7 +258,7 @@ static int rsrc_card_parse_clk(struct device_node *np,
                               int idx, bool is_fe)
 {
        struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx);
-       struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx);
+       struct asoc_simple_dai *dai_props = rsrc_priv_to_props(priv, idx);
        struct clk *clk;
        struct device_node *of_np = is_fe ?     dai_link->cpu_of_node :
                                                dai_link->codec_of_node;
@@ -336,7 +295,7 @@ static int rsrc_card_dai_sub_link_of(struct device_node *node,
 {
        struct device *dev = rsrc_priv_to_dev(priv);
        struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx);
-       struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx);
+       struct asoc_simple_dai *dai_props = rsrc_priv_to_props(priv, idx);
        int ret;
 
        ret = rsrc_card_parse_links(np, priv, idx, is_fe);
@@ -348,7 +307,7 @@ static int rsrc_card_dai_sub_link_of(struct device_node *node,
                return ret;
 
        dev_dbg(dev, "\t%s / %04x / %d\n",
-               dai_props->dai_name,
+               dai_link->name,
                dai_link->dai_fmt,
                dai_props->sysclk);
 
@@ -358,6 +317,7 @@ static int rsrc_card_dai_sub_link_of(struct device_node *node,
 static int rsrc_card_dai_link_of(struct device_node *node,
                                 struct rsrc_card_priv *priv)
 {
+       struct device *dev = rsrc_priv_to_dev(priv);
        struct snd_soc_dai_link *dai_link;
        struct device_node *np;
        unsigned int daifmt = 0;
@@ -370,8 +330,8 @@ static int rsrc_card_dai_link_of(struct device_node *node,
                dai_link = rsrc_priv_to_link(priv, i);
 
                if (strcmp(np->name, "codec") == 0) {
-                       ret = rsrc_card_parse_daifmt(node, np, priv,
-                                                    dai_link, &daifmt);
+                       ret = asoc_simple_card_parse_daifmt(dev, node, np,
+                                                           NULL, &daifmt);
                        if (ret < 0)
                                return ret;
                        break;
@@ -402,7 +362,7 @@ static int rsrc_card_parse_of(struct device_node *node,
                              struct device *dev)
 {
        const struct rsrc_card_of_data *of_data = of_device_get_match_data(dev);
-       struct rsrc_card_dai *props;
+       struct asoc_simple_dai *props;
        struct snd_soc_dai_link *links;
        int ret;
        int num;
@@ -418,7 +378,6 @@ static int rsrc_card_parse_of(struct device_node *node,
 
        priv->dai_props = props;
        priv->dai_link  = links;
-       priv->dai_num   = num;
 
        /* Init snd_soc_card */
        priv->snd_card.owner                    = THIS_MODULE;
@@ -436,9 +395,6 @@ static int rsrc_card_parse_of(struct device_node *node,
                                               "audio-routing");
        }
 
-       /* Parse the card name from DT */
-       snd_soc_of_parse_card_name(&priv->snd_card, "card-name");
-
        /* sampling rate convert */
        of_property_read_u32(node, "convert-rate", &priv->convert_rate);
 
@@ -454,8 +410,9 @@ static int rsrc_card_parse_of(struct device_node *node,
        if (ret < 0)
                return ret;
 
-       if (!priv->snd_card.name)
-               priv->snd_card.name = priv->snd_card.dai_link->name;
+       ret = asoc_simple_card_parse_card_name(&priv->snd_card, "card-");
+       if (ret < 0)
+               return ret;
 
        return 0;
 }
index 875733c52953a6a3ec13ac2f0c932bb695ae5b3e..d2df46c14c6823599c6210b3a31c9b1aa60ce72e 100644 (file)
@@ -530,14 +530,15 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream,
 {
        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
        struct snd_soc_platform *platform = rtd->platform;
+       int ret = 0;
 
        mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
 
        if (platform->driver->compr_ops && platform->driver->compr_ops->pointer)
-                platform->driver->compr_ops->pointer(cstream, tstamp);
+               ret = platform->driver->compr_ops->pointer(cstream, tstamp);
 
        mutex_unlock(&rtd->pcm_mutex);
-       return 0;
+       return ret;
 }
 
 static int soc_compr_copy(struct snd_compr_stream *cstream,
index c4464858bf0160397c3dcbf484a4bd6e3897c2bb..8698c26773b3c8d43e52a5610155348d6261a2e5 100644 (file)
@@ -1073,7 +1073,11 @@ static int dapm_widget_list_create(struct snd_soc_dapm_widget_list **list,
  */
 static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,
        struct list_head *list, enum snd_soc_dapm_direction dir,
-       int (*fn)(struct snd_soc_dapm_widget *, struct list_head *))
+       int (*fn)(struct snd_soc_dapm_widget *, struct list_head *,
+                 bool (*custom_stop_condition)(struct snd_soc_dapm_widget *,
+                                               enum snd_soc_dapm_direction)),
+       bool (*custom_stop_condition)(struct snd_soc_dapm_widget *,
+                                     enum snd_soc_dapm_direction))
 {
        enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir);
        struct snd_soc_dapm_path *path;
@@ -1088,6 +1092,11 @@ static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,
        if (list)
                list_add_tail(&widget->work_list, list);
 
+       if (custom_stop_condition && custom_stop_condition(widget, dir)) {
+               widget->endpoints[dir] = 1;
+               return widget->endpoints[dir];
+       }
+
        if ((widget->is_ep & SND_SOC_DAPM_DIR_TO_EP(dir)) && widget->connected) {
                widget->endpoints[dir] = snd_soc_dapm_suspend_check(widget);
                return widget->endpoints[dir];
@@ -1106,7 +1115,7 @@ static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,
 
                if (path->connect) {
                        path->walking = 1;
-                       con += fn(path->node[dir], list);
+                       con += fn(path->node[dir], list, custom_stop_condition);
                        path->walking = 0;
                }
        }
@@ -1119,23 +1128,37 @@ static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,
 /*
  * Recursively check for a completed path to an active or physically connected
  * output widget. Returns number of complete paths.
+ *
+ * Optionally, can be supplied with a function acting as a stopping condition.
+ * This function takes the dapm widget currently being examined and the walk
+ * direction as an arguments, it should return true if the walk should be
+ * stopped and false otherwise.
  */
 static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
-       struct list_head *list)
+       struct list_head *list,
+       bool (*custom_stop_condition)(struct snd_soc_dapm_widget *i,
+                                     enum snd_soc_dapm_direction))
 {
        return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_OUT,
-                       is_connected_output_ep);
+                       is_connected_output_ep, custom_stop_condition);
 }
 
 /*
  * Recursively check for a completed path to an active or physically connected
  * input widget. Returns number of complete paths.
+ *
+ * Optionally, can be supplied with a function acting as a stopping condition.
+ * This function takes the dapm widget currently being examined and the walk
+ * direction as an arguments, it should return true if the walk should be
+ * stopped and false otherwise.
  */
 static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
-       struct list_head *list)
+       struct list_head *list,
+       bool (*custom_stop_condition)(struct snd_soc_dapm_widget *i,
+                                     enum snd_soc_dapm_direction))
 {
        return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_IN,
-                       is_connected_input_ep);
+                       is_connected_input_ep, custom_stop_condition);
 }
 
 /**
@@ -1143,15 +1166,24 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
  * @dai: the soc DAI.
  * @stream: stream direction.
  * @list: list of active widgets for this stream.
+ * @custom_stop_condition: (optional) a function meant to stop the widget graph
+ *                         walk based on custom logic.
  *
  * Queries DAPM graph as to whether an valid audio stream path exists for
  * the initial stream specified by name. This takes into account
  * current mixer and mux kcontrol settings. Creates list of valid widgets.
  *
+ * Optionally, can be supplied with a function acting as a stopping condition.
+ * This function takes the dapm widget currently being examined and the walk
+ * direction as an arguments, it should return true if the walk should be
+ * stopped and false otherwise.
+ *
  * Returns the number of valid paths or negative error.
  */
 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_dapm_widget_list **list,
+       bool (*custom_stop_condition)(struct snd_soc_dapm_widget *,
+                                     enum snd_soc_dapm_direction))
 {
        struct snd_soc_card *card = dai->component->card;
        struct snd_soc_dapm_widget *w;
@@ -1171,9 +1203,11 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
        }
 
        if (stream == SNDRV_PCM_STREAM_PLAYBACK)
-               paths = is_connected_output_ep(dai->playback_widget, &widgets);
+               paths = is_connected_output_ep(dai->playback_widget, &widgets,
+                               custom_stop_condition);
        else
-               paths = is_connected_input_ep(dai->capture_widget, &widgets);
+               paths = is_connected_input_ep(dai->capture_widget, &widgets,
+                               custom_stop_condition);
 
        /* Drop starting point */
        list_del(widgets.next);
@@ -1268,8 +1302,8 @@ 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);
-       out = is_connected_output_ep(w, NULL);
+       in = is_connected_input_ep(w, NULL, NULL);
+       out = is_connected_output_ep(w, NULL, NULL);
        return out != 0 && in != 0;
 }
 
@@ -1928,8 +1962,8 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
                in = 0;
                out = 0;
        } else {
-               in = is_connected_input_ep(w, NULL);
-               out = is_connected_output_ep(w, NULL);
+               in = is_connected_input_ep(w, NULL, NULL);
+               out = is_connected_output_ep(w, NULL, NULL);
        }
 
        ret = snprintf(buf, PAGE_SIZE, "%s: %s%s  in %d out %d",
@@ -3282,6 +3316,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
        mutex_unlock(&dapm->card->dapm_mutex);
        return w;
 }
+EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control);
 
 struct snd_soc_dapm_widget *
 snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
index aa99dac31b3bff3ce6bb510b4b07bba637363371..60d702f8b9f0e1728ef03e17f5fe31e17489d300 100644 (file)
@@ -1287,6 +1287,46 @@ static int widget_in_list(struct snd_soc_dapm_widget_list *list,
        return 0;
 }
 
+static bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget,
+               enum snd_soc_dapm_direction dir)
+{
+       struct snd_soc_card *card = widget->dapm->card;
+       struct snd_soc_pcm_runtime *rtd;
+       int i;
+
+       if (dir == SND_SOC_DAPM_DIR_OUT) {
+               list_for_each_entry(rtd, &card->rtd_list, list) {
+                       if (!rtd->dai_link->no_pcm)
+                               continue;
+
+                       if (rtd->cpu_dai->playback_widget == widget)
+                               return true;
+
+                       for (i = 0; i < rtd->num_codecs; ++i) {
+                               struct snd_soc_dai *dai = rtd->codec_dais[i];
+                               if (dai->playback_widget == widget)
+                                       return true;
+                       }
+               }
+       } else { /* SND_SOC_DAPM_DIR_IN */
+               list_for_each_entry(rtd, &card->rtd_list, list) {
+                       if (!rtd->dai_link->no_pcm)
+                               continue;
+
+                       if (rtd->cpu_dai->capture_widget == widget)
+                               return true;
+
+                       for (i = 0; i < rtd->num_codecs; ++i) {
+                               struct snd_soc_dai *dai = rtd->codec_dais[i];
+                               if (dai->capture_widget == widget)
+                                       return true;
+                       }
+               }
+       }
+
+       return false;
+}
+
 int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
        int stream, struct snd_soc_dapm_widget_list **list)
 {
@@ -1294,7 +1334,8 @@ int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
        int paths;
 
        /* get number of valid DAI paths and their widgets */
-       paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, list);
+       paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, list,
+                       dpcm_end_walk_at_be);
 
        dev_dbg(fe->dev, "ASoC: found %d audio %s paths\n", paths,
                        stream ? "capture" : "playback");
index ee1c7c245bc7980544eb2fcf60269dcc15714ea8..1ac2db205a0d090c71dc0d031a9c589a972f4730 100644 (file)
@@ -1029,9 +1029,9 @@ static int uni_player_parse_dt_audio_glue(struct platform_device *pdev,
 
        regmap = syscon_regmap_lookup_by_phandle(node, "st,syscfg");
 
-       if (!regmap) {
+       if (IS_ERR(regmap)) {
                dev_err(&pdev->dev, "sti-audio-clk-glue syscf not found\n");
-               return -EINVAL;
+               return PTR_ERR(regmap);
        }
 
        player->clk_sel = regmap_field_alloc(regmap, regfield[0]);
index ae42294ef688c4422b063fda841181a4b55fb349..2a954bd01fd83e827e8e0b544384046703b973b5 100644 (file)
@@ -8,6 +8,15 @@ config SND_SUN4I_CODEC
          Select Y or M to add support for the Codec embedded in the Allwinner
          A10 and affiliated SoCs.
 
+config SND_SUN4I_I2S
+       tristate "Allwinner A10 I2S Support"
+       select SND_SOC_GENERIC_DMAENGINE_PCM
+       select REGMAP_MMIO
+       help
+         Say Y or M if you want to add support for codecs attached to
+         the Allwinner A10 I2S. You will also need to select the
+         individual machine drivers to support below.
+
 config SND_SUN4I_SPDIF
        tristate "Allwinner A10 SPDIF Support"
        depends on OF
index 8f5e889667f11986d2e837be43c7173fff66774f..604c7b842837278d5c53d4f329b9c24a2ec040b3 100644 (file)
@@ -1,3 +1,3 @@
 obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o
-
+obj-$(CONFIG_SND_SUN4I_I2S) += sun4i-i2s.o
 obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
new file mode 100644 (file)
index 0000000..687a8f8
--- /dev/null
@@ -0,0 +1,701 @@
+/*
+ * Copyright (C) 2015 Andrea Venturi
+ * Andrea Venturi <be17068@iperbole.bo.it>
+ *
+ * Copyright (C) 2016 Maxime Ripard
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/dmaengine.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#define SUN4I_I2S_CTRL_REG             0x00
+#define SUN4I_I2S_CTRL_SDO_EN_MASK             GENMASK(11, 8)
+#define SUN4I_I2S_CTRL_SDO_EN(sdo)                     BIT(8 + (sdo))
+#define SUN4I_I2S_CTRL_MODE_MASK               BIT(5)
+#define SUN4I_I2S_CTRL_MODE_SLAVE                      (1 << 5)
+#define SUN4I_I2S_CTRL_MODE_MASTER                     (0 << 5)
+#define SUN4I_I2S_CTRL_TX_EN                   BIT(2)
+#define SUN4I_I2S_CTRL_RX_EN                   BIT(1)
+#define SUN4I_I2S_CTRL_GL_EN                   BIT(0)
+
+#define SUN4I_I2S_FMT0_REG             0x04
+#define SUN4I_I2S_FMT0_LRCLK_POLARITY_MASK     BIT(7)
+#define SUN4I_I2S_FMT0_LRCLK_POLARITY_INVERTED         (1 << 7)
+#define SUN4I_I2S_FMT0_LRCLK_POLARITY_NORMAL           (0 << 7)
+#define SUN4I_I2S_FMT0_BCLK_POLARITY_MASK      BIT(6)
+#define SUN4I_I2S_FMT0_BCLK_POLARITY_INVERTED          (1 << 6)
+#define SUN4I_I2S_FMT0_BCLK_POLARITY_NORMAL            (0 << 6)
+#define SUN4I_I2S_FMT0_SR_MASK                 GENMASK(5, 4)
+#define SUN4I_I2S_FMT0_SR(sr)                          ((sr) << 4)
+#define SUN4I_I2S_FMT0_WSS_MASK                        GENMASK(3, 2)
+#define SUN4I_I2S_FMT0_WSS(wss)                                ((wss) << 2)
+#define SUN4I_I2S_FMT0_FMT_MASK                        GENMASK(1, 0)
+#define SUN4I_I2S_FMT0_FMT_RIGHT_J                     (2 << 0)
+#define SUN4I_I2S_FMT0_FMT_LEFT_J                      (1 << 0)
+#define SUN4I_I2S_FMT0_FMT_I2S                         (0 << 0)
+
+#define SUN4I_I2S_FMT1_REG             0x08
+#define SUN4I_I2S_FIFO_TX_REG          0x0c
+#define SUN4I_I2S_FIFO_RX_REG          0x10
+
+#define SUN4I_I2S_FIFO_CTRL_REG                0x14
+#define SUN4I_I2S_FIFO_CTRL_FLUSH_TX           BIT(25)
+#define SUN4I_I2S_FIFO_CTRL_FLUSH_RX           BIT(24)
+#define SUN4I_I2S_FIFO_CTRL_TX_MODE_MASK       BIT(2)
+#define SUN4I_I2S_FIFO_CTRL_TX_MODE(mode)              ((mode) << 2)
+#define SUN4I_I2S_FIFO_CTRL_RX_MODE_MASK       GENMASK(1, 0)
+#define SUN4I_I2S_FIFO_CTRL_RX_MODE(mode)              (mode)
+
+#define SUN4I_I2S_FIFO_STA_REG         0x18
+
+#define SUN4I_I2S_DMA_INT_CTRL_REG     0x1c
+#define SUN4I_I2S_DMA_INT_CTRL_TX_DRQ_EN       BIT(7)
+#define SUN4I_I2S_DMA_INT_CTRL_RX_DRQ_EN       BIT(3)
+
+#define SUN4I_I2S_INT_STA_REG          0x20
+
+#define SUN4I_I2S_CLK_DIV_REG          0x24
+#define SUN4I_I2S_CLK_DIV_MCLK_EN              BIT(7)
+#define SUN4I_I2S_CLK_DIV_BCLK_MASK            GENMASK(6, 4)
+#define SUN4I_I2S_CLK_DIV_BCLK(bclk)                   ((bclk) << 4)
+#define SUN4I_I2S_CLK_DIV_MCLK_MASK            GENMASK(3, 0)
+#define SUN4I_I2S_CLK_DIV_MCLK(mclk)                   ((mclk) << 0)
+
+#define SUN4I_I2S_RX_CNT_REG           0x28
+#define SUN4I_I2S_TX_CNT_REG           0x2c
+
+#define SUN4I_I2S_TX_CHAN_SEL_REG      0x30
+#define SUN4I_I2S_TX_CHAN_SEL(num_chan)                (((num_chan) - 1) << 0)
+
+#define SUN4I_I2S_TX_CHAN_MAP_REG      0x34
+#define SUN4I_I2S_TX_CHAN_MAP(chan, sample)    ((sample) << (chan << 2))
+
+#define SUN4I_I2S_RX_CHAN_SEL_REG      0x38
+#define SUN4I_I2S_RX_CHAN_MAP_REG      0x3c
+
+struct sun4i_i2s {
+       struct clk      *bus_clk;
+       struct clk      *mod_clk;
+       struct regmap   *regmap;
+
+       struct snd_dmaengine_dai_dma_data       playback_dma_data;
+};
+
+struct sun4i_i2s_clk_div {
+       u8      div;
+       u8      val;
+};
+
+static const struct sun4i_i2s_clk_div sun4i_i2s_bclk_div[] = {
+       { .div = 2, .val = 0 },
+       { .div = 4, .val = 1 },
+       { .div = 6, .val = 2 },
+       { .div = 8, .val = 3 },
+       { .div = 12, .val = 4 },
+       { .div = 16, .val = 5 },
+};
+
+static const struct sun4i_i2s_clk_div sun4i_i2s_mclk_div[] = {
+       { .div = 1, .val = 0 },
+       { .div = 2, .val = 1 },
+       { .div = 4, .val = 2 },
+       { .div = 6, .val = 3 },
+       { .div = 8, .val = 4 },
+       { .div = 12, .val = 5 },
+       { .div = 16, .val = 6 },
+       { .div = 24, .val = 7 },
+};
+
+static int sun4i_i2s_get_bclk_div(struct sun4i_i2s *i2s,
+                                 unsigned int oversample_rate,
+                                 unsigned int word_size)
+{
+       int div = oversample_rate / word_size / 2;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(sun4i_i2s_bclk_div); i++) {
+               const struct sun4i_i2s_clk_div *bdiv = &sun4i_i2s_bclk_div[i];
+
+               if (bdiv->div == div)
+                       return bdiv->val;
+       }
+
+       return -EINVAL;
+}
+
+static int sun4i_i2s_get_mclk_div(struct sun4i_i2s *i2s,
+                                 unsigned int oversample_rate,
+                                 unsigned int module_rate,
+                                 unsigned int sampling_rate)
+{
+       int div = module_rate / sampling_rate / oversample_rate;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(sun4i_i2s_mclk_div); i++) {
+               const struct sun4i_i2s_clk_div *mdiv = &sun4i_i2s_mclk_div[i];
+
+               if (mdiv->div == div)
+                       return mdiv->val;
+       }
+
+       return -EINVAL;
+}
+
+static int sun4i_i2s_oversample_rates[] = { 128, 192, 256, 384, 512, 768 };
+
+static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s,
+                                 unsigned int rate,
+                                 unsigned int word_size)
+{
+       unsigned int clk_rate;
+       int bclk_div, mclk_div;
+       int ret, i;
+
+       switch (rate) {
+       case 176400:
+       case 88200:
+       case 44100:
+       case 22050:
+       case 11025:
+               clk_rate = 22579200;
+               break;
+
+       case 192000:
+       case 128000:
+       case 96000:
+       case 64000:
+       case 48000:
+       case 32000:
+       case 24000:
+       case 16000:
+       case 12000:
+       case 8000:
+               clk_rate = 24576000;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       ret = clk_set_rate(i2s->mod_clk, clk_rate);
+       if (ret)
+               return ret;
+
+       /* Always favor the highest oversampling rate */
+       for (i = (ARRAY_SIZE(sun4i_i2s_oversample_rates) - 1); i >= 0; i--) {
+               unsigned int oversample_rate = sun4i_i2s_oversample_rates[i];
+
+               bclk_div = sun4i_i2s_get_bclk_div(i2s, oversample_rate,
+                                                 word_size);
+               mclk_div = sun4i_i2s_get_mclk_div(i2s, oversample_rate,
+                                                 clk_rate,
+                                                 rate);
+
+               if ((bclk_div >= 0) && (mclk_div >= 0))
+                       break;
+       }
+
+       if ((bclk_div < 0) || (mclk_div < 0))
+               return -EINVAL;
+
+       regmap_write(i2s->regmap, SUN4I_I2S_CLK_DIV_REG,
+                    SUN4I_I2S_CLK_DIV_BCLK(bclk_div) |
+                    SUN4I_I2S_CLK_DIV_MCLK(mclk_div) |
+                    SUN4I_I2S_CLK_DIV_MCLK_EN);
+
+       return 0;
+}
+
+static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
+                              struct snd_pcm_hw_params *params,
+                              struct snd_soc_dai *dai)
+{
+       struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+       int sr, wss;
+       u32 width;
+
+       if (params_channels(params) != 2)
+               return -EINVAL;
+
+       switch (params_physical_width(params)) {
+       case 16:
+               width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+               break;
+       default:
+               return -EINVAL;
+       }
+       i2s->playback_dma_data.addr_width = width;
+
+       switch (params_width(params)) {
+       case 16:
+               sr = 0;
+               wss = 0;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
+                          SUN4I_I2S_FMT0_WSS_MASK | SUN4I_I2S_FMT0_SR_MASK,
+                          SUN4I_I2S_FMT0_WSS(wss) | SUN4I_I2S_FMT0_SR(sr));
+
+       return sun4i_i2s_set_clk_rate(i2s, params_rate(params),
+                                     params_width(params));
+}
+
+static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+       u32 val;
+
+       /* DAI Mode */
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               val = SUN4I_I2S_FMT0_FMT_I2S;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               val = SUN4I_I2S_FMT0_FMT_LEFT_J;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               val = SUN4I_I2S_FMT0_FMT_RIGHT_J;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
+                          SUN4I_I2S_FMT0_FMT_MASK,
+                          val);
+
+       /* DAI clock polarity */
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_IB_IF:
+               /* Invert both clocks */
+               val = SUN4I_I2S_FMT0_BCLK_POLARITY_INVERTED |
+                       SUN4I_I2S_FMT0_LRCLK_POLARITY_INVERTED;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               /* Invert bit clock */
+               val = SUN4I_I2S_FMT0_BCLK_POLARITY_INVERTED |
+                       SUN4I_I2S_FMT0_LRCLK_POLARITY_NORMAL;
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               /* Invert frame clock */
+               val = SUN4I_I2S_FMT0_LRCLK_POLARITY_INVERTED |
+                       SUN4I_I2S_FMT0_BCLK_POLARITY_NORMAL;
+               break;
+       case SND_SOC_DAIFMT_NB_NF:
+               /* Nothing to do for both normal cases */
+               val = SUN4I_I2S_FMT0_BCLK_POLARITY_NORMAL |
+                       SUN4I_I2S_FMT0_LRCLK_POLARITY_NORMAL;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
+                          SUN4I_I2S_FMT0_BCLK_POLARITY_MASK |
+                          SUN4I_I2S_FMT0_LRCLK_POLARITY_MASK,
+                          val);
+
+       /* DAI clock master masks */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               /* BCLK and LRCLK master */
+               val = SUN4I_I2S_CTRL_MODE_MASTER;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFM:
+               /* BCLK and LRCLK slave */
+               val = SUN4I_I2S_CTRL_MODE_SLAVE;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
+                          SUN4I_I2S_CTRL_MODE_MASK,
+                          val);
+
+       /* Set significant bits in our FIFOs */
+       regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG,
+                          SUN4I_I2S_FIFO_CTRL_TX_MODE_MASK |
+                          SUN4I_I2S_FIFO_CTRL_RX_MODE_MASK,
+                          SUN4I_I2S_FIFO_CTRL_TX_MODE(1) |
+                          SUN4I_I2S_FIFO_CTRL_RX_MODE(1));
+       return 0;
+}
+
+static void sun4i_i2s_start_playback(struct sun4i_i2s *i2s)
+{
+       /* Flush TX FIFO */
+       regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG,
+                          SUN4I_I2S_FIFO_CTRL_FLUSH_TX,
+                          SUN4I_I2S_FIFO_CTRL_FLUSH_TX);
+
+       /* Clear TX counter */
+       regmap_write(i2s->regmap, SUN4I_I2S_TX_CNT_REG, 0);
+
+       /* Enable TX Block */
+       regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
+                          SUN4I_I2S_CTRL_TX_EN,
+                          SUN4I_I2S_CTRL_TX_EN);
+
+       /* Enable TX DRQ */
+       regmap_update_bits(i2s->regmap, SUN4I_I2S_DMA_INT_CTRL_REG,
+                          SUN4I_I2S_DMA_INT_CTRL_TX_DRQ_EN,
+                          SUN4I_I2S_DMA_INT_CTRL_TX_DRQ_EN);
+}
+
+
+static void sun4i_i2s_stop_playback(struct sun4i_i2s *i2s)
+{
+       /* Disable TX Block */
+       regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
+                          SUN4I_I2S_CTRL_TX_EN,
+                          0);
+
+       /* Disable TX DRQ */
+       regmap_update_bits(i2s->regmap, SUN4I_I2S_DMA_INT_CTRL_REG,
+                          SUN4I_I2S_DMA_INT_CTRL_TX_DRQ_EN,
+                          0);
+}
+
+static int sun4i_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+                            struct snd_soc_dai *dai)
+{
+       struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+       case SNDRV_PCM_TRIGGER_RESUME:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       sun4i_i2s_start_playback(i2s);
+               else
+                       return -EINVAL;
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       sun4i_i2s_stop_playback(i2s);
+               else
+                       return -EINVAL;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int sun4i_i2s_startup(struct snd_pcm_substream *substream,
+                            struct snd_soc_dai *dai)
+{
+       struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+       /* Enable the whole hardware block */
+       regmap_write(i2s->regmap, SUN4I_I2S_CTRL_REG,
+                    SUN4I_I2S_CTRL_GL_EN);
+
+       /* Enable the first output line */
+       regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
+                          SUN4I_I2S_CTRL_SDO_EN_MASK,
+                          SUN4I_I2S_CTRL_SDO_EN(0));
+
+       /* Enable the first two channels */
+       regmap_write(i2s->regmap, SUN4I_I2S_TX_CHAN_SEL_REG,
+                    SUN4I_I2S_TX_CHAN_SEL(2));
+
+       /* Map them to the two first samples coming in */
+       regmap_write(i2s->regmap, SUN4I_I2S_TX_CHAN_MAP_REG,
+                    SUN4I_I2S_TX_CHAN_MAP(0, 0) | SUN4I_I2S_TX_CHAN_MAP(1, 1));
+
+       return clk_prepare_enable(i2s->mod_clk);
+}
+
+static void sun4i_i2s_shutdown(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
+{
+       struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+       clk_disable_unprepare(i2s->mod_clk);
+
+       /* Disable our output lines */
+       regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
+                          SUN4I_I2S_CTRL_SDO_EN_MASK, 0);
+
+       /* Disable the whole hardware block */
+       regmap_write(i2s->regmap, SUN4I_I2S_CTRL_REG, 0);
+}
+
+static const struct snd_soc_dai_ops sun4i_i2s_dai_ops = {
+       .hw_params      = sun4i_i2s_hw_params,
+       .set_fmt        = sun4i_i2s_set_fmt,
+       .shutdown       = sun4i_i2s_shutdown,
+       .startup        = sun4i_i2s_startup,
+       .trigger        = sun4i_i2s_trigger,
+};
+
+static int sun4i_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+       struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+       snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data, NULL);
+
+       snd_soc_dai_set_drvdata(dai, i2s);
+
+       return 0;
+}
+
+static struct snd_soc_dai_driver sun4i_i2s_dai = {
+       .probe = sun4i_i2s_dai_probe,
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_192000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .ops = &sun4i_i2s_dai_ops,
+       .symmetric_rates = 1,
+};
+
+static const struct snd_soc_component_driver sun4i_i2s_component = {
+       .name   = "sun4i-dai",
+};
+
+static bool sun4i_i2s_rd_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case SUN4I_I2S_FIFO_TX_REG:
+               return false;
+
+       default:
+               return true;
+       }
+}
+
+static bool sun4i_i2s_wr_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case SUN4I_I2S_FIFO_RX_REG:
+       case SUN4I_I2S_FIFO_STA_REG:
+               return false;
+
+       default:
+               return true;
+       }
+}
+
+static bool sun4i_i2s_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case SUN4I_I2S_FIFO_RX_REG:
+       case SUN4I_I2S_INT_STA_REG:
+       case SUN4I_I2S_RX_CNT_REG:
+       case SUN4I_I2S_TX_CNT_REG:
+               return true;
+
+       default:
+               return false;
+       }
+}
+
+static const struct reg_default sun4i_i2s_reg_defaults[] = {
+       { SUN4I_I2S_CTRL_REG, 0x00000000 },
+       { SUN4I_I2S_FMT0_REG, 0x0000000c },
+       { SUN4I_I2S_FMT1_REG, 0x00004020 },
+       { SUN4I_I2S_FIFO_CTRL_REG, 0x000400f0 },
+       { SUN4I_I2S_DMA_INT_CTRL_REG, 0x00000000 },
+       { SUN4I_I2S_CLK_DIV_REG, 0x00000000 },
+       { SUN4I_I2S_TX_CHAN_SEL_REG, 0x00000001 },
+       { SUN4I_I2S_TX_CHAN_MAP_REG, 0x76543210 },
+       { SUN4I_I2S_RX_CHAN_SEL_REG, 0x00000001 },
+       { SUN4I_I2S_RX_CHAN_MAP_REG, 0x00003210 },
+};
+
+static const struct regmap_config sun4i_i2s_regmap_config = {
+       .reg_bits       = 32,
+       .reg_stride     = 4,
+       .val_bits       = 32,
+       .max_register   = SUN4I_I2S_RX_CHAN_MAP_REG,
+
+       .cache_type     = REGCACHE_FLAT,
+       .reg_defaults   = sun4i_i2s_reg_defaults,
+       .num_reg_defaults       = ARRAY_SIZE(sun4i_i2s_reg_defaults),
+       .writeable_reg  = sun4i_i2s_wr_reg,
+       .readable_reg   = sun4i_i2s_rd_reg,
+       .volatile_reg   = sun4i_i2s_volatile_reg,
+};
+
+static int sun4i_i2s_runtime_resume(struct device *dev)
+{
+       struct sun4i_i2s *i2s = dev_get_drvdata(dev);
+       int ret;
+
+       ret = clk_prepare_enable(i2s->bus_clk);
+       if (ret) {
+               dev_err(dev, "Failed to enable bus clock\n");
+               return ret;
+       }
+
+       regcache_cache_only(i2s->regmap, false);
+       regcache_mark_dirty(i2s->regmap);
+
+       ret = regcache_sync(i2s->regmap);
+       if (ret) {
+               dev_err(dev, "Failed to sync regmap cache\n");
+               goto err_disable_clk;
+       }
+
+       return 0;
+
+err_disable_clk:
+       clk_disable_unprepare(i2s->bus_clk);
+       return ret;
+}
+
+static int sun4i_i2s_runtime_suspend(struct device *dev)
+{
+       struct sun4i_i2s *i2s = dev_get_drvdata(dev);
+
+       regcache_cache_only(i2s->regmap, true);
+
+       clk_disable_unprepare(i2s->bus_clk);
+
+       return 0;
+}
+
+static int sun4i_i2s_probe(struct platform_device *pdev)
+{
+       struct sun4i_i2s *i2s;
+       struct resource *res;
+       void __iomem *regs;
+       int irq, ret;
+
+       i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
+       if (!i2s)
+               return -ENOMEM;
+       platform_set_drvdata(pdev, i2s);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(regs))
+               return PTR_ERR(regs);
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(&pdev->dev, "Can't retrieve our interrupt\n");
+               return irq;
+       }
+
+       i2s->bus_clk = devm_clk_get(&pdev->dev, "apb");
+       if (IS_ERR(i2s->bus_clk)) {
+               dev_err(&pdev->dev, "Can't get our bus clock\n");
+               return PTR_ERR(i2s->bus_clk);
+       }
+
+       i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+                                           &sun4i_i2s_regmap_config);
+       if (IS_ERR(i2s->regmap)) {
+               dev_err(&pdev->dev, "Regmap initialisation failed\n");
+               return PTR_ERR(i2s->regmap);
+       }
+
+       i2s->mod_clk = devm_clk_get(&pdev->dev, "mod");
+       if (IS_ERR(i2s->mod_clk)) {
+               dev_err(&pdev->dev, "Can't get our mod clock\n");
+               return PTR_ERR(i2s->mod_clk);
+       }
+       
+       i2s->playback_dma_data.addr = res->start + SUN4I_I2S_FIFO_TX_REG;
+       i2s->playback_dma_data.maxburst = 4;
+
+       pm_runtime_enable(&pdev->dev);
+       if (!pm_runtime_enabled(&pdev->dev)) {
+               ret = sun4i_i2s_runtime_resume(&pdev->dev);
+               if (ret)
+                       goto err_pm_disable;
+       }
+
+       ret = devm_snd_soc_register_component(&pdev->dev,
+                                             &sun4i_i2s_component,
+                                             &sun4i_i2s_dai, 1);
+       if (ret) {
+               dev_err(&pdev->dev, "Could not register DAI\n");
+               goto err_suspend;
+       }
+
+       ret = snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+       if (ret) {
+               dev_err(&pdev->dev, "Could not register PCM\n");
+               goto err_suspend;
+       }
+
+       return 0;
+
+err_suspend:
+       if (!pm_runtime_status_suspended(&pdev->dev))
+               sun4i_i2s_runtime_suspend(&pdev->dev);
+err_pm_disable:
+       pm_runtime_disable(&pdev->dev);
+
+       return ret;
+}
+
+static int sun4i_i2s_remove(struct platform_device *pdev)
+{
+       snd_dmaengine_pcm_unregister(&pdev->dev);
+
+       pm_runtime_disable(&pdev->dev);
+       if (!pm_runtime_status_suspended(&pdev->dev))
+               sun4i_i2s_runtime_suspend(&pdev->dev);
+
+       return 0;
+}
+
+static const struct of_device_id sun4i_i2s_match[] = {
+       { .compatible = "allwinner,sun4i-a10-i2s", },
+       {}
+};
+MODULE_DEVICE_TABLE(of, sun4i_i2s_match);
+
+static const struct dev_pm_ops sun4i_i2s_pm_ops = {
+       .runtime_resume         = sun4i_i2s_runtime_resume,
+       .runtime_suspend        = sun4i_i2s_runtime_suspend,
+};
+
+static struct platform_driver sun4i_i2s_driver = {
+       .probe  = sun4i_i2s_probe,
+       .remove = sun4i_i2s_remove,
+       .driver = {
+               .name           = "sun4i-i2s",
+               .of_match_table = sun4i_i2s_match,
+               .pm             = &sun4i_i2s_pm_ops,
+       },
+};
+module_platform_driver(sun4i_i2s_driver);
+
+MODULE_AUTHOR("Andrea Venturi <be17068@iperbole.bo.it>");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_DESCRIPTION("Allwinner A10 I2S driver");
+MODULE_LICENSE("GPL");
index e8a1e69eb92c5235735d42847e4f34b2e737a972..25d803148f5c6e0c9bb3109db1616a2e95fd4359 100644 (file)
@@ -122,10 +122,14 @@ static bool ignore_func(struct objtool_file *file, struct symbol *func)
 
        /* check for STACK_FRAME_NON_STANDARD */
        if (file->whitelist && file->whitelist->rela)
-               list_for_each_entry(rela, &file->whitelist->rela->rela_list, list)
-                       if (rela->sym->sec == func->sec &&
+               list_for_each_entry(rela, &file->whitelist->rela->rela_list, list) {
+                       if (rela->sym->type == STT_SECTION &&
+                           rela->sym->sec == func->sec &&
                            rela->addend == func->offset)
                                return true;
+                       if (rela->sym->type == STT_FUNC && rela->sym == func)
+                               return true;
+               }
 
        /* check if it has a context switching instruction */
        func_for_each_insn(file, func, insn)
index b7447ceb75e9816509286f711030c0085703fdf2..b0ac057417500d3f7d41b180fe09c459c7cf5de5 100644 (file)
@@ -122,7 +122,7 @@ enum {
        NODE_TAGGED = 2,
 };
 
-#define THRASH_SIZE            1000 * 1000
+#define THRASH_SIZE            (1000 * 1000)
 #define N 127
 #define BATCH  33
 
index 7cf6e1769903976f400b94d3c0c3a8734002b51a..b9d34b37c017be2ea35b9083877965f8aa224060 100644 (file)
@@ -510,10 +510,11 @@ static void slab_stats(struct slabinfo *s)
                        s->alloc_node_mismatch, (s->alloc_node_mismatch * 100) / total);
        }
 
-       if (s->cmpxchg_double_fail || s->cmpxchg_double_cpu_fail)
+       if (s->cmpxchg_double_fail || s->cmpxchg_double_cpu_fail) {
                printf("\nCmpxchg_double Looping\n------------------------\n");
                printf("Locked Cmpxchg Double redos   %lu\nUnlocked Cmpxchg Double redos %lu\n",
                        s->cmpxchg_double_fail, s->cmpxchg_double_cpu_fail);
+       }
 }
 
 static void report(struct slabinfo *s)