From 7e64e057aef9466836e854ac366fc1de69116c5e Mon Sep 17 00:00:00 2001 From: Chris McClimans Date: Sun, 8 Feb 2015 06:19:23 -0800 Subject: [PATCH] changes from Rel-2-BETA-WW05-15 since R1 --- Documentation/DocBook/media_api.tmpl | 4 +- Documentation/hwmon/k10temp | 1 - Makefile | 4 +- arch/arc/include/asm/delay.h | 5 +- arch/arc/include/asm/ptrace.h | 2 - arch/arc/include/asm/sections.h | 1 + arch/arc/include/asm/spinlock.h | 9 +- arch/arc/include/asm/syscall.h | 5 +- arch/arc/include/asm/uaccess.h | 4 +- arch/arc/kernel/entry.S | 4 +- arch/arc/kernel/head.S | 7 - arch/arc/kernel/irq.c | 3 + arch/arc/kernel/ptrace.c | 2 +- arch/arc/kernel/setup.c | 3 + arch/arc/kernel/signal.c | 25 +- arch/arc/kernel/unaligned.c | 6 - arch/arc/lib/strchr-700.S | 10 - arch/arm/Makefile | 3 - arch/arm/boot/dts/at91rm9200.dtsi | 6 +- arch/arm/boot/dts/at91sam9260.dtsi | 18 +- arch/arm/boot/dts/at91sam9263.dtsi | 8 +- arch/arm/boot/dts/at91sam9g45.dtsi | 8 +- arch/arm/boot/dts/at91sam9n12.dtsi | 8 +- arch/arm/boot/dts/at91sam9n12ek.dts | 4 +- arch/arm/boot/dts/at91sam9x5.dtsi | 10 +- arch/arm/boot/dts/sama5d3.dtsi | 4 - arch/arm/boot/dts/sun4i-a10.dtsi | 4 - arch/arm/boot/dts/sun5i-a13.dtsi | 4 - arch/arm/crypto/aes-armv4.S | 6 +- arch/arm/include/asm/jump_label.h | 2 +- arch/arm/kernel/entry-common.S | 3 +- arch/arm/kernel/process.c | 2 +- arch/arm/kvm/coproc.c | 26 +- arch/arm/kvm/coproc.h | 3 - arch/arm/kvm/coproc_a15.c | 6 +- arch/arm/mach-davinci/board-dm355-leopard.c | 1 - arch/arm/mach-davinci/board-dm644x-evm.c | 1 - arch/arm/mach-davinci/board-dm646x-evm.c | 1 - arch/arm/mach-davinci/board-neuros-osd2.c | 1 - arch/arm/mach-mxs/pm.h | 4 - .../mach-versatile/include/mach/platform.h | 2 - arch/arm/mach-versatile/pci.c | 47 +- arch/arm/mm/Kconfig | 9 +- arch/arm/mm/fault.c | 13 +- arch/arm/xen/enlighten.c | 6 +- arch/arm64/kernel/perf_event.c | 17 +- arch/avr32/kernel/time.c | 9 +- arch/mips/ath79/clock.c | 2 +- arch/mips/include/asm/jump_label.h | 2 +- arch/mips/kernel/proc.c | 6 +- arch/mips/mm/c-r4k.c | 8 +- arch/mips/mm/tlbex.c | 3 +- arch/parisc/kernel/traps.c | 6 +- arch/powerpc/Kconfig | 1 - arch/powerpc/Makefile | 5 +- arch/powerpc/boot/Makefile | 2 +- arch/powerpc/include/asm/jump_label.h | 2 +- arch/powerpc/include/asm/page.h | 10 - arch/powerpc/kernel/align.c | 10 - arch/powerpc/kernel/iommu.c | 2 +- arch/powerpc/kernel/lparcfg.c | 22 +- arch/powerpc/kernel/sysfs.c | 18 +- arch/powerpc/kernel/tm.S | 94 +- arch/powerpc/kernel/vdso32/sigtramp.S | 34 - arch/powerpc/kernel/vio.c | 12 +- arch/powerpc/kvm/book3s_hv_rmhandlers.S | 2 +- arch/powerpc/kvm/book3s_xics.c | 1 - arch/powerpc/lib/checksum_64.S | 58 +- arch/powerpc/mm/slb.c | 1 - arch/powerpc/perf/power8-pmu.c | 5 +- arch/powerpc/platforms/pseries/setup.c | 31 +- arch/s390/include/asm/jump_label.h | 2 +- arch/s390/kernel/entry.S | 1 - arch/s390/kernel/entry64.S | 1 - arch/s390/kvm/kvm-s390.c | 21 +- arch/sparc/include/asm/jump_label.h | 2 +- arch/sparc/kernel/ds.c | 5 +- arch/sparc/kernel/entry.S | 2 +- arch/sparc/kernel/ktlb.S | 3 +- arch/sparc/kernel/syscalls.S | 8 +- arch/sparc/kernel/trampoline_64.S | 2 + arch/sparc/lib/ksyms.c | 9 + arch/tile/include/asm/percpu.h | 34 +- arch/um/include/shared/os.h | 1 - arch/um/kernel/Makefile | 2 +- arch/um/os-Linux/process.c | 52 - arch/x86/configs/i386_edison_defconfig | 104 +- arch/x86/ia32/ia32_signal.c | 2 +- arch/x86/include/asm/bootparam_utils.h | 4 +- arch/x86/include/asm/checksum_32.h | 22 +- arch/x86/include/asm/cpufeature.h | 2 +- arch/x86/include/asm/e820.h | 2 +- arch/x86/include/asm/intel_psh_ipc.h | 35 + arch/x86/include/asm/intel_sst_mrfld.h | 44 + arch/x86/include/asm/jump_label.h | 2 +- arch/x86/include/asm/mce.h | 13 +- arch/x86/include/asm/mmu_context.h | 20 +- arch/x86/include/asm/platform_byt_audio.h | 49 + arch/x86/include/asm/platform_mrfld_audio.h | 25 + arch/x86/include/asm/platform_sst.h | 132 ++ arch/x86/include/asm/platform_sst_audio.h | 161 ++ arch/x86/include/asm/xor_avx.h | 4 +- arch/x86/kernel/amd_nb.c | 13 +- arch/x86/kernel/cpu/vmware.c | 1 - arch/x86/kernel/e820.c | 5 +- arch/x86/kernel/reboot.c | 16 - arch/x86/kernel/setup.c | 19 +- arch/x86/kernel/signal.c | 6 +- arch/x86/kernel/sys_x86_64.c | 2 +- arch/x86/lib/csum-wrappers_64.c | 12 +- arch/x86/mm/init.c | 4 +- arch/x86/mm/mmap.c | 8 +- arch/x86/platform/efi/efi.c | 11 +- arch/x86/platform/intel-mid/board.c | 8 +- .../platform/intel-mid/device_libs/Makefile | 2 + .../device_libs/pci/platform_sst_pci.c | 229 ++ .../intel-mid/device_libs/platform_hsu.c | 8 +- .../intel-mid/device_libs/platform_hsu.h | 6 +- .../device_libs/platform_mrfld_audio.c | 130 ++ .../intel-mid/device_libs/platform_spidev.c | 2 +- .../device_libs/platform_sst_audio.c | 152 ++ .../intel-mid/device_libs/platform_wm8994.c | 221 ++ .../intel-mid/device_libs/platform_wm8994.h | 5 + arch/x86/platform/intel-mid/intel_mid_sfi.c | 20 + arch/x86/xen/setup.c | 22 - arch/x86/xen/smp.c | 11 +- block/cfq-iosched.c | 2 +- crypto/Kconfig | 8 - crypto/Makefile | 2 - crypto/api.c | 7 +- drivers/acpi/acpi_ipmi.c | 24 +- drivers/acpi/acpi_lpss.c | 3 +- drivers/acpi/ec.c | 4 - drivers/acpi/glue.c | 95 +- drivers/ata/libata-pmp.c | 12 +- drivers/ata/sata_fsl.c | 5 +- drivers/base/core.c | 14 +- drivers/base/memory.c | 2 - drivers/base/regmap/regcache-rbtree.c | 2 +- drivers/block/cciss.c | 1 - drivers/block/cpqarray.c | 1 - drivers/block/rbd.c | 14 +- drivers/bluetooth/ath3k.c | 2 - drivers/bluetooth/btusb.c | 2 - drivers/char/random.c | 3 +- drivers/clk/clk-wm831x.c | 2 - drivers/cpuidle/coupled.c | 127 +- drivers/dma/imx-dma.c | 31 +- drivers/dma/intel_mid_dma.c | 2 +- drivers/dma/intel_mid_dma_acpi.c | 31 +- drivers/dma/intel_mid_dma_regs.h | 4 +- drivers/edac/amd64_edac.c | 9 +- drivers/gpio/gpio-omap.c | 158 +- drivers/gpu/drm/ast/ast_drv.h | 2 +- drivers/gpu/drm/drm_crtc.c | 18 +- drivers/gpu/drm/drm_edid.c | 3 - drivers/gpu/drm/i915/i915_irq.c | 76 +- drivers/gpu/drm/i915/i915_reg.h | 16 +- drivers/gpu/drm/i915/intel_display.c | 12 +- drivers/gpu/drm/i915/intel_dp.c | 13 +- drivers/gpu/drm/i915/intel_ringbuffer.c | 12 - drivers/gpu/drm/i915/intel_tv.c | 8 - .../drm/nouveau/core/engine/disp/dacnv50.c | 15 +- .../gpu/drm/nouveau/core/include/subdev/mc.h | 7 +- .../gpu/drm/nouveau/core/subdev/bios/init.c | 19 +- drivers/gpu/drm/nouveau/core/subdev/mc/base.c | 6 +- drivers/gpu/drm/nouveau/core/subdev/mc/nv04.c | 3 +- drivers/gpu/drm/nouveau/core/subdev/mc/nv44.c | 3 +- drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c | 3 +- drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c | 3 +- drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c | 3 +- drivers/gpu/drm/radeon/atombios_dp.c | 6 +- drivers/gpu/drm/radeon/atombios_encoders.c | 8 +- drivers/gpu/drm/radeon/atombios_i2c.c | 4 +- drivers/gpu/drm/radeon/evergreen.c | 27 +- drivers/gpu/drm/radeon/evergreend.h | 8 +- drivers/gpu/drm/radeon/r100.c | 8 +- drivers/gpu/drm/radeon/r600_hdmi.c | 16 +- drivers/gpu/drm/radeon/r600d.h | 2 +- drivers/gpu/drm/radeon/radeon.h | 2 +- drivers/gpu/drm/radeon/radeon_atombios.c | 13 +- drivers/gpu/drm/radeon/radeon_connectors.c | 34 +- drivers/gpu/drm/radeon/radeon_cs.c | 8 +- drivers/gpu/drm/radeon/radeon_device.c | 15 +- drivers/gpu/drm/radeon/radeon_kms.c | 3 - drivers/gpu/drm/radeon/radeon_ring.c | 8 +- drivers/gpu/drm/radeon/radeon_test.c | 4 +- drivers/gpu/drm/radeon/radeon_uvd.c | 8 - drivers/gpu/drm/radeon/rs400.c | 9 +- drivers/gpu/drm/radeon/rv770.c | 12 +- drivers/gpu/drm/radeon/si.c | 129 +- drivers/gpu/drm/radeon/sid.h | 8 +- drivers/gpu/drm/ttm/ttm_tt.c | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c | 58 +- drivers/hid/hid-core.c | 111 +- drivers/hid/hid-ids.h | 8 +- drivers/hid/hid-input.c | 23 +- drivers/hid/hid-lenovo-tpkbd.c | 25 +- drivers/hid/hid-lg2ff.c | 19 +- drivers/hid/hid-lg3ff.c | 29 +- drivers/hid/hid-lg4ff.c | 20 +- drivers/hid/hid-lgff.c | 17 +- drivers/hid/hid-logitech-dj.c | 22 +- drivers/hid/hid-multitouch.c | 26 +- drivers/hid/hid-ntrig.c | 3 +- drivers/hid/hid-picolcd_cir.c | 3 +- drivers/hid/hid-picolcd_core.c | 2 +- drivers/hid/hid-picolcd_debugfs.c | 23 +- drivers/hid/hid-picolcd_fb.c | 6 +- drivers/hid/hid-pl.c | 10 +- drivers/hid/hid-roccat-konepure.c | 3 +- drivers/hid/hid-sensor-hub.c | 3 +- drivers/hid/hid-speedlink.c | 11 +- drivers/hid/hid-steelseries.c | 5 - drivers/hid/hid-zpff.c | 18 +- drivers/hid/hidraw.c | 60 +- drivers/hid/uhid.c | 4 +- drivers/hid/usbhid/hid-core.c | 5 +- drivers/hid/usbhid/hid-quirks.c | 2 - drivers/hwmon/Kconfig | 13 +- drivers/hwmon/Makefile | 1 + drivers/hwmon/applesmc.c | 24 +- drivers/hwmon/intel_mcu_common.c | 700 ++++++ drivers/hwmon/intel_mcu_common.h | 79 + drivers/hwmon/k10temp.c | 3 +- drivers/i2c/busses/i2c-omap.c | 3 - drivers/infiniband/ulp/srpt/ib_srpt.c | 14 +- drivers/input/mouse/bcm5974.c | 6 +- drivers/iommu/intel-iommu.c | 72 +- drivers/leds/leds-wm831x-status.c | 4 +- drivers/md/bcache/bcache.h | 7 +- drivers/md/bcache/bset.c | 39 +- drivers/md/bcache/btree.c | 25 +- drivers/md/bcache/io.c | 2 - drivers/md/bcache/journal.c | 35 +- drivers/md/bcache/request.c | 15 +- drivers/md/bcache/sysfs.c | 8 +- drivers/md/bcache/util.c | 11 +- drivers/md/bcache/util.h | 12 +- drivers/md/bcache/writeback.c | 42 +- drivers/md/dm-mpath.c | 11 +- drivers/md/dm-raid.c | 2 +- drivers/md/dm-snap-persistent.c | 2 +- drivers/md/dm-snap.c | 5 +- drivers/md/dm.c | 11 - drivers/media/common/siano/smsdvb-main.c | 3 +- drivers/media/dvb-frontends/mb86a20s.c | 16 +- drivers/media/pci/Kconfig | 4 +- drivers/media/platform/coda.c | 2 +- drivers/media/platform/exynos-gsc/gsc-core.c | 9 +- drivers/media/platform/exynos-gsc/gsc-core.h | 1 - drivers/media/platform/exynos-gsc/gsc-m2m.c | 1 - drivers/media/platform/exynos4-is/media-dev.c | 2 +- drivers/media/platform/s5p-g2d/g2d.c | 1 - drivers/media/usb/Kconfig | 4 +- drivers/media/usb/hdpvr/hdpvr-core.c | 11 +- drivers/misc/hpilo.c | 4 +- drivers/misc/mei/amthif.c | 1 - drivers/misc/mei/bus.c | 5 +- drivers/misc/mei/client.h | 6 - drivers/misc/mei/hbm.c | 10 +- drivers/misc/mei/hw-me.c | 22 +- drivers/misc/mei/init.c | 6 +- drivers/misc/mei/main.c | 11 +- drivers/misc/mei/mei_dev.h | 6 +- drivers/mmc/card/block.c | 200 +- drivers/mmc/card/queue.c | 2 +- drivers/mmc/card/queue.h | 2 +- drivers/mmc/core/mmc.c | 16 + drivers/mmc/core/mmc_ops.c | 412 ++++ drivers/mmc/host/sdhci.c | 10 + drivers/mmc/host/tmio_mmc_dma.c | 4 +- drivers/mtd/nand/nand_base.c | 8 +- drivers/mtd/ubi/wl.c | 3 - drivers/net/bonding/bond_main.c | 21 +- drivers/net/ethernet/broadcom/bgmac.c | 2 +- drivers/net/ethernet/broadcom/bgmac.h | 2 +- drivers/net/ethernet/broadcom/tg3.c | 18 +- drivers/net/ethernet/emulex/benet/be_main.c | 2 +- drivers/net/ethernet/marvell/mvneta.c | 13 +- .../net/ethernet/oki-semi/pch_gbe/pch_gbe.h | 17 - .../ethernet/oki-semi/pch_gbe/pch_gbe_api.c | 49 +- .../oki-semi/pch_gbe/pch_gbe_ethtool.c | 2 +- .../ethernet/oki-semi/pch_gbe/pch_gbe_main.c | 376 ++-- .../ethernet/oki-semi/pch_gbe/pch_gbe_param.c | 63 +- .../ethernet/oki-semi/pch_gbe/pch_gbe_phy.c | 120 +- .../ethernet/oki-semi/pch_gbe/pch_gbe_phy.h | 1 - drivers/net/ethernet/realtek/8139cp.c | 1 - drivers/net/ethernet/realtek/r8169.c | 1 - drivers/net/ethernet/sfc/filter.c | 2 +- drivers/net/ethernet/via/via-rhine.c | 9 +- drivers/net/ethernet/xilinx/ll_temac_main.c | 6 - drivers/net/macvlan.c | 4 - drivers/net/ppp/pptp.c | 2 +- drivers/net/tun.c | 17 +- drivers/net/usb/cdc_ether.c | 5 - drivers/net/usb/cdc_mbim.c | 4 - drivers/net/usb/dm9601.c | 2 +- drivers/net/usb/qmi_wwan.c | 130 +- drivers/net/vxlan.c | 2 +- drivers/net/wireless/ath/ath9k/ar9003_phy.c | 4 - drivers/net/wireless/ath/ath9k/ath9k.h | 5 +- drivers/net/wireless/ath/ath9k/htc_drv_txrx.c | 10 - drivers/net/wireless/ath/ath9k/init.c | 3 +- drivers/net/wireless/ath/ath9k/main.c | 3 +- drivers/net/wireless/ath/ath9k/recv.c | 17 +- drivers/net/wireless/ath/ath9k/xmit.c | 1 - drivers/net/wireless/ath/carl9170/main.c | 3 +- .../brcm80211/brcmfmac/bcmsdh_sdmmc.c | 28 +- .../net/wireless/brcm80211/brcmfmac/dhd_bus.h | 3 +- .../wireless/brcm80211/brcmfmac/dhd_linux.c | 14 +- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 2 +- drivers/net/wireless/brcm80211/brcmsmac/dma.c | 15 +- drivers/net/wireless/hostap/hostap_ioctl.c | 4 +- drivers/net/wireless/iwlegacy/4965-mac.c | 2 +- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 5 +- drivers/net/wireless/iwlwifi/iwl-7000.c | 12 +- drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h | 4 +- drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h | 8 +- drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c | 11 +- drivers/net/wireless/iwlwifi/mvm/mac80211.c | 6 +- drivers/net/wireless/iwlwifi/mvm/mvm.h | 1 - drivers/net/wireless/iwlwifi/mvm/sta.c | 11 +- drivers/net/wireless/iwlwifi/mvm/tx.c | 6 +- drivers/net/wireless/iwlwifi/pcie/trans.c | 10 +- drivers/net/wireless/mwifiex/11n_aggr.c | 3 +- drivers/net/wireless/mwifiex/11n_aggr.h | 2 +- drivers/net/wireless/mwifiex/cmdevt.c | 5 +- drivers/net/wireless/mwifiex/main.c | 19 +- drivers/net/wireless/mwifiex/sta_ioctl.c | 18 +- drivers/net/wireless/mwifiex/usb.c | 7 + drivers/net/wireless/mwifiex/wmm.c | 3 +- drivers/net/wireless/p54/p54usb.c | 1 - drivers/net/wireless/rt2x00/rt2800lib.c | 25 +- drivers/net/wireless/rtlwifi/wifi.h | 2 +- drivers/net/wireless/zd1201.c | 4 +- drivers/net/xen-netback/netback.c | 94 +- drivers/of/base.c | 1 - drivers/of/fdt.c | 2 - drivers/pci/pci-acpi.c | 21 +- drivers/pinctrl/pinctrl-at91.c | 4 +- drivers/platform/olpc/olpc-ec.c | 2 +- drivers/platform/x86/Kconfig | 17 + drivers/platform/x86/Makefile | 2 + drivers/platform/x86/intel_psh_ipc.c | 637 ++++++ drivers/pwm/pwm-intel-mid.c | 17 +- drivers/remoteproc/intel_mid_rproc_scu.c | 11 +- drivers/rtc/rtc-max77686.c | 4 +- drivers/s390/scsi/zfcp_erp.c | 29 +- drivers/s390/scsi/zfcp_qdio.c | 8 +- drivers/scsi/Kconfig | 1 + drivers/scsi/esp_scsi.c | 14 +- drivers/scsi/esp_scsi.h | 1 - drivers/scsi/mpt3sas/Makefile | 2 +- drivers/scsi/pm8001/pm8001_hwi.c | 4 +- drivers/scsi/pm8001/pm80xx_hwi.c | 4 +- drivers/scsi/sd.c | 11 +- drivers/spi/intel_mid_ssp_spi.c | 56 +- drivers/staging/comedi/drivers.c | 2 +- drivers/staging/comedi/drivers/dt282x.c | 3 +- drivers/staging/comedi/drivers/ni_65xx.c | 25 +- drivers/staging/iio/adc/mxs-lradc.c | 18 +- drivers/staging/imx-drm/imx-drm-core.c | 1 - drivers/staging/vt6656/iwctl.c | 3 - drivers/staging/vt6656/main_usb.c | 3 +- drivers/target/iscsi/iscsi_target.c | 17 +- drivers/target/iscsi/iscsi_target_login.c | 9 +- drivers/target/iscsi/iscsi_target_util.c | 4 +- drivers/target/target_core_alua.c | 2 +- drivers/target/target_core_pr.c | 2 +- drivers/target/target_core_spc.c | 9 +- drivers/tty/hvc/hvc_xen.c | 1 - drivers/tty/hvc/hvsi_lib.c | 4 +- drivers/tty/serial/pch_uart.c | 89 +- drivers/tty/serial/serial-tegra.c | 4 +- drivers/tty/tty_io.c | 3 +- drivers/tty/tty_ioctl.c | 3 - drivers/usb/class/cdc-wdm.c | 13 +- drivers/usb/core/config.c | 3 +- drivers/usb/core/devio.c | 16 - drivers/usb/core/hub.c | 65 +- drivers/usb/core/port.c | 13 +- drivers/usb/dwc3/dwc3-pci.c | 4 - drivers/usb/dwc3/gadget.c | 100 +- drivers/usb/dwc3/gadget.c.rej | 137 ++ drivers/usb/gadget/dummy_hcd.c | 7 +- drivers/usb/gadget/f_acm.c | 1 - drivers/usb/gadget/f_fs.c | 60 +- drivers/usb/gadget/hid.c.1 | 341 +++ drivers/usb/gadget/uvc_queue.c | 6 +- drivers/usb/host/ehci-fsl.c | 12 +- drivers/usb/host/ehci-mxc.c | 2 +- drivers/usb/host/ehci-pci.c | 2 +- drivers/usb/host/ohci-hcd.c | 22 +- drivers/usb/host/ohci-pci.c | 2 +- drivers/usb/host/ohci-q.c | 24 +- drivers/usb/host/pci-quirks.c | 14 +- drivers/usb/host/uhci-pci.c | 2 +- drivers/usb/host/uhci-q.c | 16 +- drivers/usb/host/xhci-hub.c | 2 +- drivers/usb/host/xhci-pci.c | 2 +- drivers/usb/host/xhci-plat.c | 2 +- drivers/usb/host/xhci-ring.c | 24 +- drivers/usb/host/xhci.c | 54 +- drivers/usb/host/xhci.h | 2 - drivers/usb/phy/phy-fsl-usb.h | 2 +- drivers/usb/phy/phy-fsm-usb.c | 2 +- drivers/usb/serial/mos7720.c | 6 +- drivers/usb/serial/option.c | 3 - drivers/vhost/net.c | 9 +- drivers/watchdog/ts72xx_wdt.c | 3 +- drivers/xen/events.c | 13 +- drivers/xen/grant-table.c | 13 +- fs/Kconfig | 2 - fs/Makefile | 2 - fs/binfmt_elf.c | 30 +- fs/bio-integrity.c | 2 +- fs/bio.c | 24 +- fs/btrfs/extent-tree.c | 3 +- fs/btrfs/inode.c | 2 +- fs/btrfs/ioctl.c | 3 - fs/btrfs/relocation.c | 14 +- fs/btrfs/send.c | 3 +- fs/buffer.c | 2 - fs/ceph/ioctl.c | 4 +- fs/cifs/connect.c | 2 - fs/cifs/dir.c | 1 - fs/cifs/smb2misc.c | 148 +- fs/dcache.c | 11 - fs/ext4/inode.c | 109 +- fs/ext4/xattr.c | 2 - fs/fat/Kconfig | 32 - fs/fat/dir.c | 17 +- fs/fat/namei_vfat.c | 113 +- fs/file_table.c | 5 +- fs/fuse/dir.c | 15 +- fs/fuse/file.c | 34 +- fs/fuse/fuse_i.h | 2 - fs/fuse/inode.c | 3 +- fs/hugetlbfs/inode.c | 8 +- fs/inode.c | 4 +- fs/isofs/inode.c | 16 +- fs/jfs/jfs_dtree.c | 31 +- fs/namespace.c | 5 +- fs/nfs/Kconfig | 13 - fs/nfs/client.c | 12 +- fs/nfs/internal.h | 7 +- fs/nfs/mount_clnt.c | 7 +- fs/nfs/nfs4filelayoutdev.c | 18 +- fs/nfs/super.c | 33 +- fs/nilfs2/page.c | 2 - fs/nilfs2/segbuf.c | 5 +- fs/nilfs2/segment.c | 11 +- fs/notify/group.c | 4 - fs/notify/mark.c | 4 - fs/ocfs2/extent_map.c | 1 + fs/open.c | 1 - fs/proc/nommu.c | 4 - fs/proc/root.c | 3 +- fs/proc/task_mmu.c | 6 - fs/proc/task_nommu.c | 4 - fs/splice.c | 12 +- fs/statfs.c | 2 +- fs/sysv/super.c | 1 - fs/udf/super.c | 342 ++- fs/xfs/xfs_da_btree.c | 5 +- include/acpi/acpi_bus.h | 6 +- include/asm-generic/hugetlb.h | 4 +- include/linux/balloon_compaction.h | 25 - include/linux/compat.h | 7 - include/linux/compiler-gcc4.h | 15 - include/linux/dcache.h | 1 - include/linux/device-mapper.h | 3 +- include/linux/fs.h | 1 - include/linux/ftrace_event.h | 2 +- include/linux/hid.h | 9 +- include/linux/intel_mid_dma.h | 2 +- include/linux/ipc_namespace.h | 2 +- include/linux/ipv6.h | 1 - include/linux/mfd/wm8994/pdata.h | 19 + include/linux/mfd/wm8994/registers.h | 8 + include/linux/miscdevice.h | 1 - include/linux/mm_types.h | 3 - include/linux/mmc/card.h | 6 + include/linux/mmc/core.h | 34 + include/linux/nfs_fs_sb.h | 3 - include/linux/nfs_xdr.h | 2 +- include/linux/pci_ids.h | 6 +- include/linux/random.h | 1 - include/linux/rculist.h | 5 +- include/linux/regmap.h | 1 - include/linux/sched.h | 1 + include/linux/sem.h | 6 +- include/linux/signal.h | 8 - include/linux/spi/intel_mid_ssp_spi.h | 10 +- include/linux/splice.h | 6 - include/linux/timex.h | 1 - include/linux/usb/hcd.h | 2 +- include/linux/wait.h | 57 - include/media/v4l2-ctrls.h | 1 - include/net/addrconf.h | 4 - include/net/bluetooth/hci.h | 1 - include/net/bluetooth/hci_core.h | 2 + include/net/bluetooth/l2cap.h | 8 +- include/net/genetlink.h | 20 +- include/net/ip.h | 12 +- include/net/ip_tunnels.h | 14 + include/net/mac80211.h | 1 - include/net/sch_generic.h | 9 +- include/net/secure_seq.h | 1 + include/sound/compress_driver.h | 15 + include/sound/core.h | 5 + include/sound/effect_driver.h | 63 + include/sound/intel_sst_ioctl.h | 63 + include/sound/pcm.h | 1 + include/sound/soc-dai.h | 4 +- include/sound/soc-dapm.h | 2 + include/sound/soc-dpcm.h | 22 + include/sound/soc.h | 35 + include/uapi/drm/radeon_drm.h | 2 - include/uapi/linux/Kbuild | 1 - include/uapi/linux/icmpv6.h | 2 - include/uapi/linux/mmc/ioctl.h | 13 + include/uapi/linux/msdos_fs.h | 1 - include/uapi/linux/nfs_mount.h | 4 +- include/uapi/linux/pkt_sched.h | 10 +- include/uapi/sound/compress_offload.h | 8 +- include/uapi/sound/effect_offload.h | 62 + init/Kconfig | 25 - init/do_mounts.c | 8 +- init/main.c | 9 - ipc/msg.c | 286 +-- ipc/namespace.c | 7 +- ipc/sem.c | 660 ++---- ipc/shm.c | 288 +-- ipc/util.c | 140 +- ipc/util.h | 34 +- kernel/Makefile | 1 - kernel/audit.c | 5 +- kernel/events/uprobes.c | 4 +- kernel/fork.c | 9 +- kernel/pid.c | 1 - kernel/sched/cputime.c | 19 +- kernel/sched/fair.c | 14 +- kernel/softirq.c | 15 +- kernel/time/ntp.c | 6 +- kernel/time/timekeeping.c | 2 - kernel/time/timer_list.c | 41 +- kernel/trace/ftrace.c | 89 +- kernel/trace/trace.c | 173 +- kernel/trace/trace_events.c | 198 +- kernel/trace/trace_events_filter.c | 17 +- kernel/trace/trace_kprobe.c | 42 +- kernel/trace/trace_uprobe.c | 51 +- kernel/workqueue.c | 9 - mm/bounce.c | 2 - mm/huge_memory.c | 2 - mm/memcontrol.c | 8 +- mm/memory.c | 2 - mm/migrate.c | 2 +- mm/mmap.c | 10 - mm/nommu.c | 10 - mm/shmem.c | 8 +- mm/slab.h | 2 - mm/swap.c | 77 +- mm/vmscan.c | 4 +- net/bluetooth/hci_core.c | 26 +- net/bluetooth/hci_event.c | 6 +- net/bluetooth/hidp/core.c | 15 +- net/bluetooth/l2cap_core.c | 20 +- net/bluetooth/l2cap_sock.c | 5 +- net/bridge/br_fdb.c | 10 +- net/bridge/br_multicast.c | 5 +- net/bridge/br_netlink.c | 8 +- net/bridge/br_private.h | 8 +- net/bridge/br_stp.c | 23 +- net/bridge/br_stp_if.c | 12 +- net/bridge/br_vlan.c | 4 +- net/caif/cfctrl.c | 3 +- net/ceph/osd_client.c | 2 - net/ceph/osdmap.c | 2 +- net/core/flow_dissector.c | 15 +- net/core/neighbour.c | 10 +- net/core/netpoll.c | 11 +- net/core/rtnetlink.c | 4 +- net/core/scm.c | 2 +- net/core/secure_seq.c | 27 +- net/core/sysctl_net_core.c | 6 +- net/dccp/ipv6.c | 1 - net/ipv4/af_inet.c | 4 +- net/ipv4/arp.c | 9 +- net/ipv4/devinet.c | 4 +- net/ipv4/fib_trie.c | 5 +- net/ipv4/igmp.c | 8 +- net/ipv4/inetpeer.c | 4 +- net/ipv4/ip_gre.c | 2 +- net/ipv4/ip_output.c | 8 +- net/ipv4/ip_tunnel.c | 14 +- net/ipv4/ipmr.c | 2 +- net/ipv4/raw.c | 5 +- net/ipv4/tcp.c | 10 +- net/ipv4/tcp_cubic.c | 12 +- net/ipv4/tcp_input.c | 9 +- net/ipv4/tcp_output.c | 4 +- net/ipv4/xfrm4_mode_tunnel.c | 2 +- net/ipv6/addrconf.c | 37 +- net/ipv6/addrlabel.c | 48 +- net/ipv6/exthdrs.c | 6 + net/ipv6/icmp.c | 10 +- net/ipv6/ip6_fib.c | 18 +- net/ipv6/ip6_gre.c | 4 +- net/ipv6/ip6_output.c | 53 +- net/ipv6/ip6_tunnel.c | 5 +- net/ipv6/mcast.c | 4 +- net/ipv6/ndisc.c | 14 +- net/ipv6/reassembly.c | 5 - net/ipv6/sit.c | 87 +- net/ipv6/tcp_ipv6.c | 2 +- net/mac80211/ibss.c | 1 - net/mac80211/mlme.c | 15 +- net/mac80211/rc80211_minstrel_ht.c | 3 - net/netfilter/ipset/ip_set_hash_gen.h | 28 +- net/netfilter/ipvs/ip_vs_xmit.c | 2 +- net/netlink/genetlink.c | 67 +- net/nfc/llcp.h | 1 - net/nfc/llcp_sock.c | 8 +- net/packet/af_packet.c | 2 - net/sched/sch_api.c | 41 - net/sched/sch_generic.c | 1 - net/sched/sch_htb.c | 17 +- net/sctp/input.c | 3 +- net/sctp/ipv6.c | 44 +- net/sctp/socket.c | 5 +- net/sunrpc/auth_gss/gss_rpc_upcall.c | 26 - net/sunrpc/auth_gss/gss_rpc_xdr.c | 41 +- net/sunrpc/auth_gss/gss_rpc_xdr.h | 5 +- net/sunrpc/xdr.c | 9 +- net/sysctl_net.c | 4 +- net/tipc/socket.c | 4 +- scripts/kconfig/menu.c | 2 +- scripts/mod/modpost.c | 6 +- security/commoncap.c | 2 - security/device_cgroup.c | 2 - security/security.c | 10 - sound/core/Kconfig | 3 + sound/core/Makefile | 2 + sound/core/compress_offload.c | 126 +- sound/core/effects_offload.c | 307 +++ sound/isa/opti9xx/opti92x-ad1848.c | 8 +- sound/pci/hda/hda_intel.c | 1 - sound/pci/hda/patch_conexant.c | 11 - sound/pci/hda/patch_hdmi.c | 73 +- sound/pci/hda/patch_realtek.c | 38 - sound/soc/Kconfig | 2 +- sound/soc/Makefile | 2 +- sound/soc/codecs/88pm860x-codec.c | 3 - sound/soc/codecs/ab8500-codec.c | 7 +- sound/soc/codecs/max98095.c | 4 +- sound/soc/codecs/mc13783.c | 4 - sound/soc/codecs/wm8960.c | 6 +- sound/soc/codecs/wm8994.c | 491 +++- sound/soc/codecs/wm8994.h | 14 + sound/soc/codecs/wm_hubs.c | 20 +- sound/soc/intel/Kconfig | 58 + sound/soc/intel/Makefile | 22 + sound/soc/intel/board/Makefile | 14 + sound/soc/intel/board/merr_dpcm_dummy.c | 233 ++ sound/soc/intel/board/merr_dpcm_wm8958.c | 932 ++++++++ sound/soc/intel/board/merr_saltbay_wm8958.c | 887 ++++++++ sound/soc/intel/compress.c | 254 +++ sound/soc/intel/effects.c | 405 ++++ sound/soc/intel/pcm.c | 1021 +++++++++ sound/soc/intel/platform-libs/controls_v1.c | 184 ++ sound/soc/intel/platform-libs/controls_v2.c | 1770 +++++++++++++++ sound/soc/intel/platform-libs/controls_v2.h | 708 ++++++ .../intel/platform-libs/controls_v2_dpcm.c | 1803 +++++++++++++++ sound/soc/intel/platform-libs/ipc_lib.h | 33 + sound/soc/intel/platform-libs/ipc_lib_v2.c | 109 + sound/soc/intel/platform-libs/sst_widgets.h | 337 +++ sound/soc/intel/platform_ipc_v2.h | 693 ++++++ sound/soc/intel/ssp/Makefile | 5 + sound/soc/intel/ssp/mid_ssp.c | 1436 ++++++++++++ sound/soc/intel/ssp/mid_ssp.h | 124 + sound/soc/intel/sst/Makefile | 13 + sound/soc/intel/sst/sst.c | 1255 +++++++++++ sound/soc/intel/sst/sst.h | 932 ++++++++ sound/soc/intel/sst/sst_acpi.c | 669 ++++++ .../soc/intel/sst/sst_app_compat_interface.c | 85 + sound/soc/intel/sst/sst_app_interface.c | 342 +++ sound/soc/intel/sst/sst_debug.c | 1328 +++++++++++ sound/soc/intel/sst/sst_drv_interface.c | 1111 +++++++++ sound/soc/intel/sst/sst_dsp.c | 1995 +++++++++++++++++ sound/soc/intel/sst/sst_ipc.c | 768 +++++++ sound/soc/intel/sst/sst_pvt.c | 577 +++++ sound/soc/intel/sst/sst_stream.c | 831 +++++++ sound/soc/intel/sst/sst_trace.h | 147 ++ sound/soc/intel/sst_platform.h | 155 ++ sound/soc/intel/sst_platform_pvt.h | 127 ++ sound/soc/soc-compress.c | 343 ++- sound/soc/soc-core.c | 36 +- sound/soc/soc-dapm.c | 13 +- sound/soc/soc-jack.c | 14 +- sound/soc/soc-pcm.c | 83 +- sound/soc/soc-utils.c | 34 +- sound/usb/usx2y/usbusx2yaudio.c | 22 +- sound/usb/usx2y/usx2yhwdeppcm.c | 7 +- tools/lib/lk/debugfs.c | 1 + tools/perf/Makefile | 12 +- tools/perf/bench/numa.c | 5 +- tools/perf/builtin-sched.c | 41 +- tools/perf/perf.h | 2 - .../scripts/python/Perf-Trace-Util/Context.c | 4 - tools/perf/tests/attr.c | 20 +- tools/perf/tests/bp_signal.c | 2 +- tools/perf/tests/bp_signal_overflow.c | 2 +- tools/perf/util/annotate.c | 2 +- tools/perf/util/include/linux/compiler.h | 1 - tools/perf/util/map.c | 1 - .../scripting-engines/trace-event-python.c | 4 - usr/Makefile | 2 +- 720 files changed, 30315 insertions(+), 7065 deletions(-) create mode 100644 arch/x86/include/asm/intel_psh_ipc.h create mode 100644 arch/x86/include/asm/intel_sst_mrfld.h create mode 100644 arch/x86/include/asm/platform_byt_audio.h create mode 100644 arch/x86/include/asm/platform_mrfld_audio.h create mode 100644 arch/x86/include/asm/platform_sst.h create mode 100644 arch/x86/include/asm/platform_sst_audio.h create mode 100644 arch/x86/platform/intel-mid/device_libs/pci/platform_sst_pci.c create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_mrfld_audio.c create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_sst_audio.c create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_wm8994.c create mode 100644 arch/x86/platform/intel-mid/device_libs/platform_wm8994.h create mode 100644 drivers/hwmon/intel_mcu_common.c create mode 100644 drivers/hwmon/intel_mcu_common.h create mode 100644 drivers/platform/x86/intel_psh_ipc.c create mode 100644 drivers/usb/dwc3/gadget.c.rej create mode 100644 drivers/usb/gadget/hid.c.1 create mode 100644 include/sound/effect_driver.h create mode 100644 include/sound/intel_sst_ioctl.h create mode 100644 include/uapi/sound/effect_offload.h create mode 100644 sound/core/effects_offload.c create mode 100644 sound/soc/intel/Kconfig create mode 100644 sound/soc/intel/Makefile create mode 100644 sound/soc/intel/board/Makefile create mode 100644 sound/soc/intel/board/merr_dpcm_dummy.c create mode 100644 sound/soc/intel/board/merr_dpcm_wm8958.c create mode 100644 sound/soc/intel/board/merr_saltbay_wm8958.c create mode 100644 sound/soc/intel/compress.c create mode 100644 sound/soc/intel/effects.c create mode 100644 sound/soc/intel/pcm.c create mode 100644 sound/soc/intel/platform-libs/controls_v1.c create mode 100644 sound/soc/intel/platform-libs/controls_v2.c create mode 100644 sound/soc/intel/platform-libs/controls_v2.h create mode 100644 sound/soc/intel/platform-libs/controls_v2_dpcm.c create mode 100644 sound/soc/intel/platform-libs/ipc_lib.h create mode 100644 sound/soc/intel/platform-libs/ipc_lib_v2.c create mode 100644 sound/soc/intel/platform-libs/sst_widgets.h create mode 100644 sound/soc/intel/platform_ipc_v2.h create mode 100644 sound/soc/intel/ssp/Makefile create mode 100644 sound/soc/intel/ssp/mid_ssp.c create mode 100644 sound/soc/intel/ssp/mid_ssp.h create mode 100644 sound/soc/intel/sst/Makefile create mode 100644 sound/soc/intel/sst/sst.c create mode 100644 sound/soc/intel/sst/sst.h create mode 100644 sound/soc/intel/sst/sst_acpi.c create mode 100644 sound/soc/intel/sst/sst_app_compat_interface.c create mode 100644 sound/soc/intel/sst/sst_app_interface.c create mode 100644 sound/soc/intel/sst/sst_debug.c create mode 100644 sound/soc/intel/sst/sst_drv_interface.c create mode 100644 sound/soc/intel/sst/sst_dsp.c create mode 100644 sound/soc/intel/sst/sst_ipc.c create mode 100644 sound/soc/intel/sst/sst_pvt.c create mode 100644 sound/soc/intel/sst/sst_stream.c create mode 100644 sound/soc/intel/sst/sst_trace.h create mode 100644 sound/soc/intel/sst_platform.h create mode 100644 sound/soc/intel/sst_platform_pvt.h diff --git a/Documentation/DocBook/media_api.tmpl b/Documentation/DocBook/media_api.tmpl index 9c92bb879b6d..6a8b7158697f 100644 --- a/Documentation/DocBook/media_api.tmpl +++ b/Documentation/DocBook/media_api.tmpl @@ -1,6 +1,6 @@ - %media-entities; diff --git a/Documentation/hwmon/k10temp b/Documentation/hwmon/k10temp index 4dfdc8f83633..90956b618025 100644 --- a/Documentation/hwmon/k10temp +++ b/Documentation/hwmon/k10temp @@ -12,7 +12,6 @@ Supported chips: * AMD Family 12h processors: "Llano" (E2/A4/A6/A8-Series) * AMD Family 14h processors: "Brazos" (C/E/G/Z-Series) * AMD Family 15h processors: "Bulldozer" (FX-Series), "Trinity" -* AMD Family 16h processors: "Kabini" Prefix: 'k10temp' Addresses scanned: PCI space diff --git a/Makefile b/Makefile index ec02856e6487..4b31d6238f7d 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 10 -SUBLEVEL = 17 +SUBLEVEL = 9 EXTRAVERSION = NAME = TOSSUG Baby Fish @@ -406,7 +406,7 @@ export MODVERDIR := $(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/).tmp_ve # Files to ignore in find ... statements RCS_FIND_IGNORE := \( -name SCCS -o -name BitKeeper -o -name .svn -o -name CVS \ - -o -name .pc -o -name .hg -o -name .git -o -name meta \) -prune -o + -o -name .pc -o -name .hg -o -name .git \) -prune -o export RCS_TAR_IGNORE := --exclude SCCS --exclude BitKeeper --exclude .svn \ --exclude CVS --exclude .pc --exclude .hg --exclude .git diff --git a/arch/arc/include/asm/delay.h b/arch/arc/include/asm/delay.h index 43de30256981..442ce5d0f709 100644 --- a/arch/arc/include/asm/delay.h +++ b/arch/arc/include/asm/delay.h @@ -53,10 +53,11 @@ static inline void __udelay(unsigned long usecs) { unsigned long loops; - /* (u64) cast ensures 64 bit MPY - real or emulated + /* (long long) cast ensures 64 bit MPY - real or emulated * HZ * 4295 is pre-evaluated by gcc - hence only 2 mpy ops */ - loops = ((u64) usecs * 4295 * HZ * loops_per_jiffy) >> 32; + loops = ((long long)(usecs * 4295 * HZ) * + (long long)(loops_per_jiffy)) >> 32; __delay(loops); } diff --git a/arch/arc/include/asm/ptrace.h b/arch/arc/include/asm/ptrace.h index 2046a89a57cf..6179de7e07c2 100644 --- a/arch/arc/include/asm/ptrace.h +++ b/arch/arc/include/asm/ptrace.h @@ -52,14 +52,12 @@ struct pt_regs { /*to distinguish bet excp, syscall, irq */ union { - struct { #ifdef CONFIG_CPU_BIG_ENDIAN /* so that assembly code is same for LE/BE */ unsigned long orig_r8:16, event:16; #else unsigned long event:16, orig_r8:16; #endif - }; long orig_r8_word; }; }; diff --git a/arch/arc/include/asm/sections.h b/arch/arc/include/asm/sections.h index 764f1e3ba752..6fc1159dfefe 100644 --- a/arch/arc/include/asm/sections.h +++ b/arch/arc/include/asm/sections.h @@ -11,6 +11,7 @@ #include +extern char _int_vec_base_lds[]; extern char __arc_dccm_base[]; extern char __dtb_start[]; diff --git a/arch/arc/include/asm/spinlock.h b/arch/arc/include/asm/spinlock.h index b6a8c2dfbe6e..f158197ac5b0 100644 --- a/arch/arc/include/asm/spinlock.h +++ b/arch/arc/include/asm/spinlock.h @@ -45,14 +45,7 @@ static inline int arch_spin_trylock(arch_spinlock_t *lock) static inline void arch_spin_unlock(arch_spinlock_t *lock) { - unsigned int tmp = __ARCH_SPIN_LOCK_UNLOCKED__; - - __asm__ __volatile__( - " ex %0, [%1] \n" - : "+r" (tmp) - : "r"(&(lock->slock)) - : "memory"); - + lock->slock = __ARCH_SPIN_LOCK_UNLOCKED__; smp_mb(); } diff --git a/arch/arc/include/asm/syscall.h b/arch/arc/include/asm/syscall.h index 29de09804306..33ab3048e9b2 100644 --- a/arch/arc/include/asm/syscall.h +++ b/arch/arc/include/asm/syscall.h @@ -18,7 +18,7 @@ static inline long syscall_get_nr(struct task_struct *task, struct pt_regs *regs) { if (user_mode(regs) && in_syscall(regs)) - return regs->r8; + return regs->orig_r8; else return -1; } @@ -26,7 +26,8 @@ syscall_get_nr(struct task_struct *task, struct pt_regs *regs) static inline void syscall_rollback(struct task_struct *task, struct pt_regs *regs) { - regs->r0 = regs->orig_r0; + /* XXX: I can't fathom how pt_regs->r8 will be clobbered ? */ + regs->r8 = regs->orig_r8; } static inline long diff --git a/arch/arc/include/asm/uaccess.h b/arch/arc/include/asm/uaccess.h index 30c9baffa96f..32420824375b 100644 --- a/arch/arc/include/asm/uaccess.h +++ b/arch/arc/include/asm/uaccess.h @@ -43,7 +43,7 @@ * Because it essentially checks if buffer end is within limit and @len is * non-ngeative, which implies that buffer start will be within limit too. * - * The reason for rewriting being, for majority of cases, @len is generally + * The reason for rewriting being, for majorit yof cases, @len is generally * compile time constant, causing first sub-expression to be compile time * subsumed. * @@ -53,7 +53,7 @@ * */ #define __user_ok(addr, sz) (((sz) <= TASK_SIZE) && \ - ((addr) <= (get_fs() - (sz)))) + (((addr)+(sz)) <= get_fs())) #define __access_ok(addr, sz) (unlikely(__kernel_ok) || \ likely(__user_ok((addr), (sz)))) diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S index 6dbe359c760d..0c6d664d4a83 100644 --- a/arch/arc/kernel/entry.S +++ b/arch/arc/kernel/entry.S @@ -498,7 +498,7 @@ tracesys_exit: trap_with_param: ; stop_pc info by gdb needs this info - st orig_r8_IS_BRKPT, [sp, PT_orig_r8] + stw orig_r8_IS_BRKPT, [sp, PT_orig_r8] mov r0, r12 lr r1, [efa] @@ -723,7 +723,7 @@ not_exception: ; things to what they were, before returning from L2 context ;---------------------------------------------------------------- - ld r9, [sp, PT_orig_r8] ; get orig_r8 to make sure it is + ldw r9, [sp, PT_orig_r8] ; get orig_r8 to make sure it is brne r9, orig_r8_IS_IRQ2, 149f ; infact a L2 ISR ret path ld r9, [sp, PT_status32] ; get statu32_l2 (saved in pt_regs) diff --git a/arch/arc/kernel/head.S b/arch/arc/kernel/head.S index 0f944f024513..006dec3fc353 100644 --- a/arch/arc/kernel/head.S +++ b/arch/arc/kernel/head.S @@ -27,16 +27,11 @@ stext: ; Don't clobber r0-r4 yet. It might have bootloader provided info ;------------------------------------------------------------------- - sr @_int_vec_base_lds, [AUX_INTR_VEC_BASE] - #ifdef CONFIG_SMP ; Only Boot (Master) proceeds. Others wait in platform dependent way ; IDENTITY Reg [ 3 2 1 0 ] ; (cpu-id) ^^^ => Zero for UP ARC700 ; => #Core-ID if SMP (Master 0) - ; Note that non-boot CPUs might not land here if halt-on-reset and - ; instead breath life from @first_lines_of_secondary, but we still - ; need to make sure only boot cpu takes this path. GET_CPU_ID r5 cmp r5, 0 jnz arc_platform_smp_wait_to_boot @@ -101,8 +96,6 @@ stext: first_lines_of_secondary: - sr @_int_vec_base_lds, [AUX_INTR_VEC_BASE] - ; setup per-cpu idle task as "current" on this CPU ld r0, [@secondary_idle_tsk] SET_CURR_TASK_ON_CPU r0, r1 diff --git a/arch/arc/kernel/irq.c b/arch/arc/kernel/irq.c index a199471ce01e..8115fa531575 100644 --- a/arch/arc/kernel/irq.c +++ b/arch/arc/kernel/irq.c @@ -24,6 +24,7 @@ * -Needed for each CPU (hence not foldable into init_IRQ) * * what it does ? + * -setup Vector Table Base Reg - in case Linux not linked at 0x8000_0000 * -Disable all IRQs (on CPU side) * -Optionally, setup the High priority Interrupts as Level 2 IRQs */ @@ -31,6 +32,8 @@ void __cpuinit arc_init_IRQ(void) { int level_mask = 0; + write_aux_reg(AUX_INTR_VEC_BASE, _int_vec_base_lds); + /* Disable all IRQs: enable them as devices request */ write_aux_reg(AUX_IENABLE, 0); diff --git a/arch/arc/kernel/ptrace.c b/arch/arc/kernel/ptrace.c index 0851604bb9cd..c6a81c58d0f3 100644 --- a/arch/arc/kernel/ptrace.c +++ b/arch/arc/kernel/ptrace.c @@ -92,7 +92,7 @@ static int genregs_set(struct task_struct *target, REG_IN_CHUNK(scratch, callee, ptregs); /* pt_regs[bta..orig_r8] */ REG_IN_CHUNK(callee, efa, cregs); /* callee_regs[r25..r13] */ REG_IGNORE_ONE(efa); /* efa update invalid */ - REG_IGNORE_ONE(stop_pc); /* PC updated via @ret */ + REG_IN_ONE(stop_pc, &ptregs->ret); /* stop_pc: PC update */ return ret; } diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c index 2d7786b69a8a..b2b3731dd1e9 100644 --- a/arch/arc/kernel/setup.c +++ b/arch/arc/kernel/setup.c @@ -47,7 +47,10 @@ void __cpuinit read_arc_build_cfg_regs(void) READ_BCR(AUX_IDENTITY, cpu->core); cpu->timers = read_aux_reg(ARC_REG_TIMERS_BCR); + cpu->vec_base = read_aux_reg(AUX_INTR_VEC_BASE); + if (cpu->vec_base == 0) + cpu->vec_base = (unsigned int)_int_vec_base_lds; READ_BCR(ARC_REG_D_UNCACH_BCR, uncached_space); cpu->uncached_base = uncached_space.start << 24; diff --git a/arch/arc/kernel/signal.c b/arch/arc/kernel/signal.c index 7e95e1a86510..ee6ef2f60a28 100644 --- a/arch/arc/kernel/signal.c +++ b/arch/arc/kernel/signal.c @@ -101,6 +101,7 @@ SYSCALL_DEFINE0(rt_sigreturn) { struct rt_sigframe __user *sf; unsigned int magic; + int err; struct pt_regs *regs = current_pt_regs(); /* Always make any pending restarted system calls return -EINTR */ @@ -118,16 +119,15 @@ SYSCALL_DEFINE0(rt_sigreturn) if (!access_ok(VERIFY_READ, sf, sizeof(*sf))) goto badframe; - if (__get_user(magic, &sf->sigret_magic)) + err = restore_usr_regs(regs, sf); + err |= __get_user(magic, &sf->sigret_magic); + if (err) goto badframe; if (unlikely(is_do_ss_needed(magic))) if (restore_altstack(&sf->uc.uc_stack)) goto badframe; - if (restore_usr_regs(regs, sf)) - goto badframe; - /* Don't restart from sigreturn */ syscall_wont_restart(regs); @@ -190,15 +190,6 @@ setup_rt_frame(int signo, struct k_sigaction *ka, siginfo_t *info, if (!sf) return 1; - /* - * w/o SA_SIGINFO, struct ucontext is partially populated (only - * uc_mcontext/uc_sigmask) for kernel's normal user state preservation - * during signal handler execution. This works for SA_SIGINFO as well - * although the semantics are now overloaded (the same reg state can be - * inspected by userland: but are they allowed to fiddle with it ? - */ - err |= stash_usr_regs(sf, regs, set); - /* * SA_SIGINFO requires 3 args to signal handler: * #1: sig-no (common to any handler) @@ -222,6 +213,14 @@ setup_rt_frame(int signo, struct k_sigaction *ka, siginfo_t *info, magic = MAGIC_SIGALTSTK; } + /* + * w/o SA_SIGINFO, struct ucontext is partially populated (only + * uc_mcontext/uc_sigmask) for kernel's normal user state preservation + * during signal handler execution. This works for SA_SIGINFO as well + * although the semantics are now overloaded (the same reg state can be + * inspected by userland: but are they allowed to fiddle with it ? + */ + err |= stash_usr_regs(sf, regs, set); err |= __put_user(magic, &sf->sigret_magic); if (err) return err; diff --git a/arch/arc/kernel/unaligned.c b/arch/arc/kernel/unaligned.c index 116d3e09b5b5..4cd81633febd 100644 --- a/arch/arc/kernel/unaligned.c +++ b/arch/arc/kernel/unaligned.c @@ -233,12 +233,6 @@ int misaligned_fixup(unsigned long address, struct pt_regs *regs, regs->status32 &= ~STATUS_DE_MASK; } else { regs->ret += state.instr_len; - - /* handle zero-overhead-loop */ - if ((regs->ret == regs->lp_end) && (regs->lp_count)) { - regs->ret = regs->lp_start; - regs->lp_count--; - } } return 0; diff --git a/arch/arc/lib/strchr-700.S b/arch/arc/lib/strchr-700.S index 9c548c7cf001..99c10475d477 100644 --- a/arch/arc/lib/strchr-700.S +++ b/arch/arc/lib/strchr-700.S @@ -39,18 +39,9 @@ ARC_ENTRY strchr ld.a r2,[r0,4] sub r12,r6,r7 bic r12,r12,r6 -#ifdef __LITTLE_ENDIAN__ and r7,r12,r4 breq r7,0,.Loop ; For speed, we want this branch to be unaligned. b .Lfound_char ; Likewise this one. -#else - and r12,r12,r4 - breq r12,0,.Loop ; For speed, we want this branch to be unaligned. - lsr_s r12,r12,7 - bic r2,r7,r6 - b.d .Lfound_char_b - and_s r2,r2,r12 -#endif ; /* We require this code address to be unaligned for speed... */ .Laligned: ld_s r2,[r0] @@ -104,7 +95,6 @@ ARC_ENTRY strchr lsr r7,r7,7 bic r2,r7,r6 -.Lfound_char_b: norm r2,r2 sub_s r0,r0,4 asr_s r2,r2,3 diff --git a/arch/arm/Makefile b/arch/arm/Makefile index a098bd393aa4..1ba358ba16b8 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -283,9 +283,6 @@ archprepare: # Convert bzImage to zImage bzImage: zImage -# These targets cannot be built in parallel -.NOTPARALLEL: zImage Image xipImage bootpImage uImage - zImage Image xipImage bootpImage uImage: vmlinux $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@ diff --git a/arch/arm/boot/dts/at91rm9200.dtsi b/arch/arm/boot/dts/at91rm9200.dtsi index 0af879a4eafa..5d3ed5aafc69 100644 --- a/arch/arm/boot/dts/at91rm9200.dtsi +++ b/arch/arm/boot/dts/at91rm9200.dtsi @@ -35,12 +35,8 @@ ssc2 = &ssc2; }; cpus { - #address-cells = <0>; - #size-cells = <0>; - - cpu { + cpu@0 { compatible = "arm,arm920t"; - device_type = "cpu"; }; }; diff --git a/arch/arm/boot/dts/at91sam9260.dtsi b/arch/arm/boot/dts/at91sam9260.dtsi index 0dbdb846f90a..84c4bef2d726 100644 --- a/arch/arm/boot/dts/at91sam9260.dtsi +++ b/arch/arm/boot/dts/at91sam9260.dtsi @@ -32,12 +32,8 @@ ssc0 = &ssc0; }; cpus { - #address-cells = <0>; - #size-cells = <0>; - - cpu { - compatible = "arm,arm926ej-s"; - device_type = "cpu"; + cpu@0 { + compatible = "arm,arm926ejs"; }; }; @@ -344,14 +340,6 @@ }; }; - i2c_gpio0 { - pinctrl_i2c_gpio0: i2c_gpio0-0 { - atmel,pins = - <0 23 0x0 0x3 /* PA23 gpio I2C_SDA pin */ - 0 24 0x0 0x3>; /* PA24 gpio I2C_SCL pin */ - }; - }; - pioA: gpio@fffff400 { compatible = "atmel,at91rm9200-gpio"; reg = <0xfffff400 0x200>; @@ -604,8 +592,6 @@ i2c-gpio,delay-us = <2>; /* ~100 kHz */ #address-cells = <1>; #size-cells = <0>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_i2c_gpio0>; status = "disabled"; }; }; diff --git a/arch/arm/boot/dts/at91sam9263.dtsi b/arch/arm/boot/dts/at91sam9263.dtsi index fcd38f89904e..94b58ab2cc08 100644 --- a/arch/arm/boot/dts/at91sam9263.dtsi +++ b/arch/arm/boot/dts/at91sam9263.dtsi @@ -29,12 +29,8 @@ ssc1 = &ssc1; }; cpus { - #address-cells = <0>; - #size-cells = <0>; - - cpu { - compatible = "arm,arm926ej-s"; - device_type = "cpu"; + cpu@0 { + compatible = "arm,arm926ejs"; }; }; diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi index 479a0622cdb8..bf18a735c37d 100644 --- a/arch/arm/boot/dts/at91sam9g45.dtsi +++ b/arch/arm/boot/dts/at91sam9g45.dtsi @@ -35,12 +35,8 @@ ssc1 = &ssc1; }; cpus { - #address-cells = <0>; - #size-cells = <0>; - - cpu { - compatible = "arm,arm926ej-s"; - device_type = "cpu"; + cpu@0 { + compatible = "arm,arm926ejs"; }; }; diff --git a/arch/arm/boot/dts/at91sam9n12.dtsi b/arch/arm/boot/dts/at91sam9n12.dtsi index a92ec78349a2..8d25f889928e 100644 --- a/arch/arm/boot/dts/at91sam9n12.dtsi +++ b/arch/arm/boot/dts/at91sam9n12.dtsi @@ -31,12 +31,8 @@ ssc0 = &ssc0; }; cpus { - #address-cells = <0>; - #size-cells = <0>; - - cpu { - compatible = "arm,arm926ej-s"; - device_type = "cpu"; + cpu@0 { + compatible = "arm,arm926ejs"; }; }; diff --git a/arch/arm/boot/dts/at91sam9n12ek.dts b/arch/arm/boot/dts/at91sam9n12ek.dts index 28ba79893877..d30e48bd1e9d 100644 --- a/arch/arm/boot/dts/at91sam9n12ek.dts +++ b/arch/arm/boot/dts/at91sam9n12ek.dts @@ -14,11 +14,11 @@ compatible = "atmel,at91sam9n12ek", "atmel,at91sam9n12", "atmel,at91sam9"; chosen { - bootargs = "console=ttyS0,115200 root=/dev/mtdblock1 rw rootfstype=jffs2"; + bootargs = "mem=128M console=ttyS0,115200 root=/dev/mtdblock1 rw rootfstype=jffs2"; }; memory { - reg = <0x20000000 0x8000000>; + reg = <0x20000000 0x10000000>; }; clocks { diff --git a/arch/arm/boot/dts/at91sam9x5.dtsi b/arch/arm/boot/dts/at91sam9x5.dtsi index 2b2b6923d16b..1145ac330fb7 100644 --- a/arch/arm/boot/dts/at91sam9x5.dtsi +++ b/arch/arm/boot/dts/at91sam9x5.dtsi @@ -33,12 +33,8 @@ ssc0 = &ssc0; }; cpus { - #address-cells = <0>; - #size-cells = <0>; - - cpu { - compatible = "arm,arm926ej-s"; - device_type = "cpu"; + cpu@0 { + compatible = "arm,arm926ejs"; }; }; @@ -647,7 +643,7 @@ }; rtc@fffffeb0 { - compatible = "atmel,at91sam9x5-rtc"; + compatible = "atmel,at91rm9200-rtc"; reg = <0xfffffeb0 0x40>; interrupts = <1 4 7>; status = "disabled"; diff --git a/arch/arm/boot/dts/sama5d3.dtsi b/arch/arm/boot/dts/sama5d3.dtsi index 642775d7ca67..5000e0d42849 100644 --- a/arch/arm/boot/dts/sama5d3.dtsi +++ b/arch/arm/boot/dts/sama5d3.dtsi @@ -35,12 +35,8 @@ ssc1 = &ssc1; }; cpus { - #address-cells = <1>; - #size-cells = <0>; cpu@0 { - device_type = "cpu"; compatible = "arm,cortex-a5"; - reg = <0x0>; }; }; diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi index 06ef8b625dba..e7ef619a70a2 100644 --- a/arch/arm/boot/dts/sun4i-a10.dtsi +++ b/arch/arm/boot/dts/sun4i-a10.dtsi @@ -16,12 +16,8 @@ interrupt-parent = <&intc>; cpus { - #address-cells = <1>; - #size-cells = <0>; cpu@0 { - device_type = "cpu"; compatible = "arm,cortex-a8"; - reg = <0x0>; }; }; diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi index d2852547b572..31fa38f8cc98 100644 --- a/arch/arm/boot/dts/sun5i-a13.dtsi +++ b/arch/arm/boot/dts/sun5i-a13.dtsi @@ -17,12 +17,8 @@ interrupt-parent = <&intc>; cpus { - #address-cells = <1>; - #size-cells = <0>; cpu@0 { - device_type = "cpu"; compatible = "arm,cortex-a8"; - reg = <0x0>; }; }; diff --git a/arch/arm/crypto/aes-armv4.S b/arch/arm/crypto/aes-armv4.S index 3a14ea8fe97e..19d6cd6f29f9 100644 --- a/arch/arm/crypto/aes-armv4.S +++ b/arch/arm/crypto/aes-armv4.S @@ -148,7 +148,7 @@ AES_Te: @ const AES_KEY *key) { .align 5 ENTRY(AES_encrypt) - adr r3,AES_encrypt + sub r3,pc,#8 @ AES_encrypt stmdb sp!,{r1,r4-r12,lr} mov r12,r0 @ inp mov r11,r2 @@ -381,7 +381,7 @@ _armv4_AES_encrypt: .align 5 ENTRY(private_AES_set_encrypt_key) _armv4_AES_set_encrypt_key: - adr r3,_armv4_AES_set_encrypt_key + sub r3,pc,#8 @ AES_set_encrypt_key teq r0,#0 moveq r0,#-1 beq .Labrt @@ -843,7 +843,7 @@ AES_Td: @ const AES_KEY *key) { .align 5 ENTRY(AES_decrypt) - adr r3,AES_decrypt + sub r3,pc,#8 @ AES_decrypt stmdb sp!,{r1,r4-r12,lr} mov r12,r0 @ inp mov r11,r2 diff --git a/arch/arm/include/asm/jump_label.h b/arch/arm/include/asm/jump_label.h index 863c892b4aaa..bfc198c75913 100644 --- a/arch/arm/include/asm/jump_label.h +++ b/arch/arm/include/asm/jump_label.h @@ -16,7 +16,7 @@ static __always_inline bool arch_static_branch(struct static_key *key) { - asm_volatile_goto("1:\n\t" + asm goto("1:\n\t" JUMP_LABEL_NOP "\n\t" ".pushsection __jump_table, \"aw\"\n\t" ".word 1b, %l[l_yes], %c0\n\t" diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index 7dcdfae37034..bc5bc0a97131 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S @@ -484,8 +484,7 @@ __cr_alignment: * With EABI a couple syscalls are obsolete and defined as sys_ni_syscall. */ #define ABI(native, compat) native - -#if defined(CONFIG_AEABI) && !defined(CONFIG_OABI_COMPAT) +#ifdef CONFIG_AEABI #define OBSOLETE(syscall) sys_ni_syscall #else #define OBSOLETE(syscall) syscall diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index ab1fe3bb31f0..5bc2615268d4 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -466,7 +466,7 @@ int in_gate_area_no_mm(unsigned long addr) { return in_gate_area(NULL, addr); } -#define is_gate_vma(vma) ((vma) == &gate_vma) +#define is_gate_vma(vma) ((vma) = &gate_vma) #else #define is_gate_vma(vma) 0 #endif diff --git a/arch/arm/kvm/coproc.c b/arch/arm/kvm/coproc.c index db9cf692d4dd..4a5199070430 100644 --- a/arch/arm/kvm/coproc.c +++ b/arch/arm/kvm/coproc.c @@ -146,11 +146,7 @@ static bool pm_fake(struct kvm_vcpu *vcpu, #define access_pmintenclr pm_fake /* Architected CP15 registers. - * CRn denotes the primary register number, but is copied to the CRm in the - * user space API for 64-bit register access in line with the terminology used - * in the ARM ARM. - * Important: Must be sorted ascending by CRn, CRM, Op1, Op2 and with 64-bit - * registers preceding 32-bit ones. + * Important: Must be sorted ascending by CRn, CRM, Op1, Op2 */ static const struct coproc_reg cp15_regs[] = { /* CSSELR: swapped by interrupt.S. */ @@ -158,8 +154,8 @@ static const struct coproc_reg cp15_regs[] = { NULL, reset_unknown, c0_CSSELR }, /* TTBR0/TTBR1: swapped by interrupt.S. */ - { CRm64( 2), Op1( 0), is64, NULL, reset_unknown64, c2_TTBR0 }, - { CRm64( 2), Op1( 1), is64, NULL, reset_unknown64, c2_TTBR1 }, + { CRm( 2), Op1( 0), is64, NULL, reset_unknown64, c2_TTBR0 }, + { CRm( 2), Op1( 1), is64, NULL, reset_unknown64, c2_TTBR1 }, /* TTBCR: swapped by interrupt.S. */ { CRn( 2), CRm( 0), Op1( 0), Op2( 2), is32, @@ -186,7 +182,7 @@ static const struct coproc_reg cp15_regs[] = { NULL, reset_unknown, c6_IFAR }, /* PAR swapped by interrupt.S */ - { CRm64( 7), Op1( 0), is64, NULL, reset_unknown64, c7_PAR }, + { CRn( 7), Op1( 0), is64, NULL, reset_unknown64, c7_PAR }, /* * DC{C,I,CI}SW operations: @@ -403,13 +399,12 @@ static bool index_to_params(u64 id, struct coproc_params *params) | KVM_REG_ARM_OPC1_MASK)) return false; params->is_64bit = true; - /* CRm to CRn: see cp15_to_index for details */ - params->CRn = ((id & KVM_REG_ARM_CRM_MASK) + params->CRm = ((id & KVM_REG_ARM_CRM_MASK) >> KVM_REG_ARM_CRM_SHIFT); params->Op1 = ((id & KVM_REG_ARM_OPC1_MASK) >> KVM_REG_ARM_OPC1_SHIFT); params->Op2 = 0; - params->CRm = 0; + params->CRn = 0; return true; default: return false; @@ -903,14 +898,7 @@ static u64 cp15_to_index(const struct coproc_reg *reg) if (reg->is_64) { val |= KVM_REG_SIZE_U64; val |= (reg->Op1 << KVM_REG_ARM_OPC1_SHIFT); - /* - * CRn always denotes the primary coproc. reg. nr. for the - * in-kernel representation, but the user space API uses the - * CRm for the encoding, because it is modelled after the - * MRRC/MCRR instructions: see the ARM ARM rev. c page - * B3-1445 - */ - val |= (reg->CRn << KVM_REG_ARM_CRM_SHIFT); + val |= (reg->CRm << KVM_REG_ARM_CRM_SHIFT); } else { val |= KVM_REG_SIZE_U32; val |= (reg->Op1 << KVM_REG_ARM_OPC1_SHIFT); diff --git a/arch/arm/kvm/coproc.h b/arch/arm/kvm/coproc.h index 0461d5c8d3de..b7301d3e4799 100644 --- a/arch/arm/kvm/coproc.h +++ b/arch/arm/kvm/coproc.h @@ -135,8 +135,6 @@ static inline int cmp_reg(const struct coproc_reg *i1, return -1; if (i1->CRn != i2->CRn) return i1->CRn - i2->CRn; - if (i1->is_64 != i2->is_64) - return i2->is_64 - i1->is_64; if (i1->CRm != i2->CRm) return i1->CRm - i2->CRm; if (i1->Op1 != i2->Op1) @@ -147,7 +145,6 @@ static inline int cmp_reg(const struct coproc_reg *i1, #define CRn(_x) .CRn = _x #define CRm(_x) .CRm = _x -#define CRm64(_x) .CRn = _x, .CRm = 0 #define Op1(_x) .Op1 = _x #define Op2(_x) .Op2 = _x #define is64 .is_64 = true diff --git a/arch/arm/kvm/coproc_a15.c b/arch/arm/kvm/coproc_a15.c index cf93472b9dd6..685063a6d0cf 100644 --- a/arch/arm/kvm/coproc_a15.c +++ b/arch/arm/kvm/coproc_a15.c @@ -114,11 +114,7 @@ static bool access_l2ectlr(struct kvm_vcpu *vcpu, /* * A15-specific CP15 registers. - * CRn denotes the primary register number, but is copied to the CRm in the - * user space API for 64-bit register access in line with the terminology used - * in the ARM ARM. - * Important: Must be sorted ascending by CRn, CRM, Op1, Op2 and with 64-bit - * registers preceding 32-bit ones. + * Important: Must be sorted ascending by CRn, CRM, Op1, Op2 */ static const struct coproc_reg a15_regs[] = { /* MPIDR: we use VMPIDR for guest access. */ diff --git a/arch/arm/mach-davinci/board-dm355-leopard.c b/arch/arm/mach-davinci/board-dm355-leopard.c index 139e42da25f0..dff4ddc5ef81 100644 --- a/arch/arm/mach-davinci/board-dm355-leopard.c +++ b/arch/arm/mach-davinci/board-dm355-leopard.c @@ -75,7 +75,6 @@ static struct davinci_nand_pdata davinci_nand_data = { .parts = davinci_nand_partitions, .nr_parts = ARRAY_SIZE(davinci_nand_partitions), .ecc_mode = NAND_ECC_HW_SYNDROME, - .ecc_bits = 4, .bbt_options = NAND_BBT_USE_FLASH, }; diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index fa4bfaf952d8..a33686a6fbb2 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -153,7 +153,6 @@ static struct davinci_nand_pdata davinci_evm_nandflash_data = { .parts = davinci_evm_nandflash_partition, .nr_parts = ARRAY_SIZE(davinci_evm_nandflash_partition), .ecc_mode = NAND_ECC_HW, - .ecc_bits = 1, .bbt_options = NAND_BBT_USE_FLASH, .timing = &davinci_evm_nandflash_timing, }; diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c index 0c005e876cac..fbb8e5ab1dc1 100644 --- a/arch/arm/mach-davinci/board-dm646x-evm.c +++ b/arch/arm/mach-davinci/board-dm646x-evm.c @@ -90,7 +90,6 @@ static struct davinci_nand_pdata davinci_nand_data = { .parts = davinci_nand_partitions, .nr_parts = ARRAY_SIZE(davinci_nand_partitions), .ecc_mode = NAND_ECC_HW, - .ecc_bits = 1, .options = 0, }; diff --git a/arch/arm/mach-davinci/board-neuros-osd2.c b/arch/arm/mach-davinci/board-neuros-osd2.c index 808233b60e3d..2bc112adf565 100644 --- a/arch/arm/mach-davinci/board-neuros-osd2.c +++ b/arch/arm/mach-davinci/board-neuros-osd2.c @@ -88,7 +88,6 @@ static struct davinci_nand_pdata davinci_ntosd2_nandflash_data = { .parts = davinci_ntosd2_nandflash_partition, .nr_parts = ARRAY_SIZE(davinci_ntosd2_nandflash_partition), .ecc_mode = NAND_ECC_HW, - .ecc_bits = 1, .bbt_options = NAND_BBT_USE_FLASH, }; diff --git a/arch/arm/mach-mxs/pm.h b/arch/arm/mach-mxs/pm.h index 09d77b00a96b..f57e7cdece2e 100644 --- a/arch/arm/mach-mxs/pm.h +++ b/arch/arm/mach-mxs/pm.h @@ -9,10 +9,6 @@ #ifndef __ARCH_MXS_PM_H #define __ARCH_MXS_PM_H -#ifdef CONFIG_PM void mxs_pm_init(void); -#else -#define mxs_pm_init NULL -#endif #endif diff --git a/arch/arm/mach-versatile/include/mach/platform.h b/arch/arm/mach-versatile/include/mach/platform.h index 6f938ccb0c54..ec087407b163 100644 --- a/arch/arm/mach-versatile/include/mach/platform.h +++ b/arch/arm/mach-versatile/include/mach/platform.h @@ -231,14 +231,12 @@ /* PCI space */ #define VERSATILE_PCI_BASE 0x41000000 /* PCI Interface */ #define VERSATILE_PCI_CFG_BASE 0x42000000 -#define VERSATILE_PCI_IO_BASE 0x43000000 #define VERSATILE_PCI_MEM_BASE0 0x44000000 #define VERSATILE_PCI_MEM_BASE1 0x50000000 #define VERSATILE_PCI_MEM_BASE2 0x60000000 /* Sizes of above maps */ #define VERSATILE_PCI_BASE_SIZE 0x01000000 #define VERSATILE_PCI_CFG_BASE_SIZE 0x02000000 -#define VERSATILE_PCI_IO_BASE_SIZE 0x01000000 #define VERSATILE_PCI_MEM_BASE0_SIZE 0x0c000000 /* 32Mb */ #define VERSATILE_PCI_MEM_BASE1_SIZE 0x10000000 /* 256Mb */ #define VERSATILE_PCI_MEM_BASE2_SIZE 0x10000000 /* 256Mb */ diff --git a/arch/arm/mach-versatile/pci.c b/arch/arm/mach-versatile/pci.c index c97be4ea76d2..e92e5e0705bc 100644 --- a/arch/arm/mach-versatile/pci.c +++ b/arch/arm/mach-versatile/pci.c @@ -43,9 +43,9 @@ #define PCI_IMAP0 __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0x0) #define PCI_IMAP1 __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0x4) #define PCI_IMAP2 __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0x8) -#define PCI_SMAP0 __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0x14) -#define PCI_SMAP1 __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0x18) -#define PCI_SMAP2 __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0x1c) +#define PCI_SMAP0 __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0x10) +#define PCI_SMAP1 __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0x14) +#define PCI_SMAP2 __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0x18) #define PCI_SELFID __IO_ADDRESS(VERSATILE_PCI_CORE_BASE+0xc) #define DEVICE_ID_OFFSET 0x00 @@ -170,8 +170,8 @@ static struct pci_ops pci_versatile_ops = { .write = versatile_write_config, }; -static struct resource unused_mem = { - .name = "PCI unused", +static struct resource io_mem = { + .name = "PCI I/O space", .start = VERSATILE_PCI_MEM_BASE0, .end = VERSATILE_PCI_MEM_BASE0+VERSATILE_PCI_MEM_BASE0_SIZE-1, .flags = IORESOURCE_MEM, @@ -195,9 +195,9 @@ static int __init pci_versatile_setup_resources(struct pci_sys_data *sys) { int ret = 0; - ret = request_resource(&iomem_resource, &unused_mem); + ret = request_resource(&iomem_resource, &io_mem); if (ret) { - printk(KERN_ERR "PCI: unable to allocate unused " + printk(KERN_ERR "PCI: unable to allocate I/O " "memory region (%d)\n", ret); goto out; } @@ -205,7 +205,7 @@ static int __init pci_versatile_setup_resources(struct pci_sys_data *sys) if (ret) { printk(KERN_ERR "PCI: unable to allocate non-prefetchable " "memory region (%d)\n", ret); - goto release_unused_mem; + goto release_io_mem; } ret = request_resource(&iomem_resource, &pre_mem); if (ret) { @@ -225,8 +225,8 @@ static int __init pci_versatile_setup_resources(struct pci_sys_data *sys) release_non_mem: release_resource(&non_mem); - release_unused_mem: - release_resource(&unused_mem); + release_io_mem: + release_resource(&io_mem); out: return ret; } @@ -246,7 +246,7 @@ int __init pci_versatile_setup(int nr, struct pci_sys_data *sys) goto out; } - ret = pci_ioremap_io(0, VERSATILE_PCI_IO_BASE); + ret = pci_ioremap_io(0, VERSATILE_PCI_MEM_BASE0); if (ret) goto out; @@ -294,19 +294,6 @@ int __init pci_versatile_setup(int nr, struct pci_sys_data *sys) __raw_writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_1); __raw_writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_2); - /* - * For many years the kernel and QEMU were symbiotically buggy - * in that they both assumed the same broken IRQ mapping. - * QEMU therefore attempts to auto-detect old broken kernels - * so that they still work on newer QEMU as they did on old - * QEMU. Since we now use the correct (ie matching-hardware) - * IRQ mapping we write a definitely different value to a - * PCI_INTERRUPT_LINE register to tell QEMU that we expect - * real hardware behaviour and it need not be backwards - * compatible for us. This write is harmless on real hardware. - */ - __raw_writel(0, VERSATILE_PCI_VIRT_BASE+PCI_INTERRUPT_LINE); - /* * Do not to map Versatile FPGA PCI device into memory space */ @@ -340,13 +327,13 @@ static int __init versatile_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { int irq; - /* - * Slot INTA INTB INTC INTD - * 31 PCI1 PCI2 PCI3 PCI0 - * 30 PCI0 PCI1 PCI2 PCI3 - * 29 PCI3 PCI0 PCI1 PCI2 + /* slot, pin, irq + * 24 1 IRQ_SIC_PCI0 + * 25 1 IRQ_SIC_PCI1 + * 26 1 IRQ_SIC_PCI2 + * 27 1 IRQ_SIC_PCI3 */ - irq = IRQ_SIC_PCI0 + ((slot + 2 + pin - 1) & 3); + irq = IRQ_SIC_PCI0 + ((slot - 24 + pin - 1) & 3); return irq; } diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 08c9fe917d1f..2950082a531c 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -789,18 +789,15 @@ config KUSER_HELPERS the CPU type fitted to the system. This permits binaries to be run on ARMv4 through to ARMv7 without modification. - See Documentation/arm/kernel_user_helpers.txt for details. - However, the fixed address nature of these helpers can be used by ROP (return orientated programming) authors when creating exploits. If all of the binaries and libraries which run on your platform are built specifically for your platform, and make no use of - these helpers, then you can turn this option off to hinder - such exploits. However, in that case, if a binary or library - relying on those helpers is run, it will receive a SIGILL signal, - which will terminate the program. + these helpers, then you can turn this option off. However, + when such an binary or library is run, it will receive a SIGILL + signal, which will terminate the program. Say N here only if you are absolutely certain that you do not need these helpers; otherwise, the safe option is to say Y. diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index e207aa5f846f..5dbf13f954f6 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c @@ -446,16 +446,8 @@ do_translation_fault(unsigned long addr, unsigned int fsr, if (pud_none(*pud_k)) goto bad_area; - if (!pud_present(*pud)) { + if (!pud_present(*pud)) set_pud(pud, *pud_k); - /* - * There is a small window during free_pgtables() where the - * user *pud entry is 0 but the TLB has not been invalidated - * and we get a level 2 (pmd) translation fault caused by the - * intermediate TLB caching of the old level 1 (pud) entry. - */ - flush_tlb_kernel_page(addr); - } pmd = pmd_offset(pud, addr); pmd_k = pmd_offset(pud_k, addr); @@ -478,9 +470,8 @@ do_translation_fault(unsigned long addr, unsigned int fsr, #endif if (pmd_none(pmd_k[index])) goto bad_area; - if (!pmd_present(pmd[index])) - copy_pmd(pmd, pmd_k); + copy_pmd(pmd, pmd_k); return 0; bad_area: diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c index 81edd31bb4ac..13609e01f4b7 100644 --- a/arch/arm/xen/enlighten.c +++ b/arch/arm/xen/enlighten.c @@ -170,7 +170,6 @@ static void __init xen_percpu_init(void *unused) per_cpu(xen_vcpu, cpu) = vcpup; enable_percpu_irq(xen_events_irq, 0); - put_cpu(); } static void xen_restart(char str, const char *cmd) @@ -273,15 +272,12 @@ core_initcall(xen_guest_init); static int __init xen_pm_init(void) { - if (!xen_domain()) - return -ENODEV; - pm_power_off = xen_power_off; arm_pm_restart = xen_restart; return 0; } -late_initcall(xen_pm_init); +subsys_initcall(xen_pm_init); static irqreturn_t xen_arm_callback(int irq, void *arg) { diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c index cea1594ff933..9ba33c40cdf8 100644 --- a/arch/arm64/kernel/perf_event.c +++ b/arch/arm64/kernel/perf_event.c @@ -107,12 +107,7 @@ armpmu_map_cache_event(const unsigned (*cache_map) static int armpmu_map_event(const unsigned (*event_map)[PERF_COUNT_HW_MAX], u64 config) { - int mapping; - - if (config >= PERF_COUNT_HW_MAX) - return -EINVAL; - - mapping = (*event_map)[config]; + int mapping = (*event_map)[config]; return mapping == HW_OP_UNSUPPORTED ? -ENOENT : mapping; } @@ -322,13 +317,7 @@ validate_event(struct pmu_hw_events *hw_events, struct hw_perf_event fake_event = event->hw; struct pmu *leader_pmu = event->group_leader->pmu; - if (is_software_event(event)) - return 1; - - if (event->pmu != leader_pmu || event->state < PERF_EVENT_STATE_OFF) - return 1; - - if (event->state == PERF_EVENT_STATE_OFF && !event->attr.enable_on_exec) + if (event->pmu != leader_pmu || event->state <= PERF_EVENT_STATE_OFF) return 1; return armpmu->get_event_idx(hw_events, &fake_event) >= 0; @@ -784,7 +773,7 @@ static const unsigned armv8_pmuv3_perf_cache_map[PERF_COUNT_HW_CACHE_MAX] /* * PMXEVTYPER: Event selection reg */ -#define ARMV8_EVTYPE_MASK 0xc80000ff /* Mask for writable bits */ +#define ARMV8_EVTYPE_MASK 0xc00000ff /* Mask for writable bits */ #define ARMV8_EVTYPE_EVENT 0xff /* Mask for EVENT bits */ /* diff --git a/arch/avr32/kernel/time.c b/arch/avr32/kernel/time.c index 12f828ad5058..869a1c6ffeee 100644 --- a/arch/avr32/kernel/time.c +++ b/arch/avr32/kernel/time.c @@ -98,14 +98,7 @@ static void comparator_mode(enum clock_event_mode mode, case CLOCK_EVT_MODE_SHUTDOWN: sysreg_write(COMPARE, 0); pr_debug("%s: stop\n", evdev->name); - if (evdev->mode == CLOCK_EVT_MODE_ONESHOT || - evdev->mode == CLOCK_EVT_MODE_RESUME) { - /* - * Only disable idle poll if we have forced that - * in a previous call. - */ - cpu_idle_poll_ctrl(false); - } + cpu_idle_poll_ctrl(false); break; default: BUG(); diff --git a/arch/mips/ath79/clock.c b/arch/mips/ath79/clock.c index 733017b3dfe7..765ef30e3e1c 100644 --- a/arch/mips/ath79/clock.c +++ b/arch/mips/ath79/clock.c @@ -164,7 +164,7 @@ static void __init ar933x_clocks_init(void) ath79_ahb_clk.rate = freq / t; } - ath79_wdt_clk.rate = ath79_ahb_clk.rate; + ath79_wdt_clk.rate = ath79_ref_clk.rate; ath79_uart_clk.rate = ath79_ref_clk.rate; } diff --git a/arch/mips/include/asm/jump_label.h b/arch/mips/include/asm/jump_label.h index e194f957ca8c..4d6d77ed9b9d 100644 --- a/arch/mips/include/asm/jump_label.h +++ b/arch/mips/include/asm/jump_label.h @@ -22,7 +22,7 @@ static __always_inline bool arch_static_branch(struct static_key *key) { - asm_volatile_goto("1:\tnop\n\t" + asm goto("1:\tnop\n\t" "nop\n\t" ".pushsection __jump_table, \"aw\"\n\t" WORD_INSN " 1b, %l[l_yes], %0\n\t" diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c index 5d2a69c3b66f..acb34373679e 100644 --- a/arch/mips/kernel/proc.c +++ b/arch/mips/kernel/proc.c @@ -23,7 +23,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) unsigned int version = cpu_data[n].processor_id; unsigned int fp_vers = cpu_data[n].fpu_id; char fmt [64]; - int cpu,i; + int i; #ifdef CONFIG_SMP if (!cpu_online(n)) @@ -43,11 +43,9 @@ static int show_cpuinfo(struct seq_file *m, void *v) seq_printf(m, "processor\t\t: %ld\n", n); sprintf(fmt, "cpu model\t\t: %%s V%%d.%%d%s\n", cpu_data[n].options & MIPS_CPU_FPU ? " FPU V%d.%d" : ""); - cpu = get_cpu(); - seq_printf(m, fmt, __cpu_name[cpu], + seq_printf(m, fmt, __cpu_name[n], (version >> 4) & 0x0f, version & 0x0f, (fp_vers >> 4) & 0x0f, fp_vers & 0x0f); - put_cpu(); seq_printf(m, "BogoMIPS\t\t: %u.%02u\n", cpu_data[n].udelay_val / (500000/HZ), (cpu_data[n].udelay_val / (5000/HZ)) % 100); diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c index d6414bf3abce..21813beec7a5 100644 --- a/arch/mips/mm/c-r4k.c +++ b/arch/mips/mm/c-r4k.c @@ -385,12 +385,12 @@ static inline int has_valid_asid(const struct mm_struct *mm) static void r4k__flush_cache_vmap(void) { - r4k_on_each_cpu((void *)r4k_blast_dcache, NULL); + r4k_blast_dcache(); } static void r4k__flush_cache_vunmap(void) { - r4k_on_each_cpu((void *)r4k_blast_dcache, NULL); + r4k_blast_dcache(); } static inline void local_r4k_flush_cache_range(void * args) @@ -616,7 +616,7 @@ static void r4k_dma_cache_wback_inv(unsigned long addr, unsigned long size) * explicitly */ if (cpu_has_safe_index_cacheops && size >= dcache_size) { - r4k_on_each_cpu((void *)r4k_blast_dcache, NULL); + r4k_blast_dcache(); } else { R4600_HIT_CACHEOP_WAR_IMPL; blast_dcache_range(addr, addr + size); @@ -650,7 +650,7 @@ static void r4k_dma_cache_inv(unsigned long addr, unsigned long size) } if (cpu_has_safe_index_cacheops && size >= dcache_size) { - r4k_on_each_cpu((void *)r4k_blast_dcache, NULL); + r4k_blast_dcache(); } else { R4600_HIT_CACHEOP_WAR_IMPL; blast_inv_dcache_range(addr, addr + size); diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c index 0f25e0017080..afeef93f81a7 100644 --- a/arch/mips/mm/tlbex.c +++ b/arch/mips/mm/tlbex.c @@ -484,7 +484,6 @@ static void __cpuinit __maybe_unused build_tlb_probe_entry(u32 **p) case CPU_R4700: case CPU_R5000: case CPU_NEVADA: - case CPU_4KC: uasm_i_nop(p); uasm_i_tlbp(p); break; @@ -551,7 +550,6 @@ static void __cpuinit build_tlb_write_entry(u32 **p, struct uasm_label **l, case CPU_R4600: case CPU_R4700: - case CPU_4KC: uasm_i_nop(p); tlbw(p); uasm_i_nop(p); @@ -576,6 +574,7 @@ static void __cpuinit build_tlb_write_entry(u32 **p, struct uasm_label **l, case CPU_R10000: case CPU_R12000: case CPU_R14000: + case CPU_4KC: case CPU_4KEC: case CPU_M14KC: case CPU_M14KEC: diff --git a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c index b3f87a3b4bce..04e47c6a4562 100644 --- a/arch/parisc/kernel/traps.c +++ b/arch/parisc/kernel/traps.c @@ -805,14 +805,14 @@ void notrace handle_interruption(int code, struct pt_regs *regs) else { /* - * The kernel should never fault on its own address space, - * unless pagefault_disable() was called before. + * The kernel should never fault on its own address space. */ - if (fault_space == 0 && !in_atomic()) + if (fault_space == 0) { pdc_chassis_send_status(PDC_CHASSIS_DIRECT_PANIC); parisc_terminate("Kernel Fault", regs, code, fault_address); + } } diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index fe404e77246e..74991fee2e62 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -986,7 +986,6 @@ config RELOCATABLE must live at a different physical address than the primary kernel. -# This value must have zeroes in the bottom 60 bits otherwise lots will break config PAGE_OFFSET hex default "0xc000000000000000" diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index 61675af91a70..967fd23ace78 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -192,9 +192,6 @@ relocs_check: arch/powerpc/relocs_check.pl vmlinux zImage: relocs_check endif -# These targets cannot be built in parallel -.NOTPARALLEL: $(BOOT_TARGETS) - $(BOOT_TARGETS1): vmlinux $(Q)$(MAKE) ARCH=ppc64 $(build)=$(boot) $(patsubst %,$(boot)/%,$@) $(BOOT_TARGETS2): vmlinux @@ -241,7 +238,7 @@ endif archclean: $(Q)$(MAKE) $(clean)=$(boot) -archprepare: checkbin arch/powerpc/lib/crtsavres.o +archprepare: checkbin # Use the file '.tmp_gas_check' for binutils tests, as gas won't output # to stdout and these checks are run even on install targets. diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index 61a5cdf168c8..6a15c968d214 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -20,7 +20,7 @@ all: $(obj)/zImage BOOTCFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \ - -fno-strict-aliasing -msoft-float -pipe \ + -fno-strict-aliasing -Os -msoft-float -pipe \ -fomit-frame-pointer -fno-builtin -fPIC -nostdinc \ -isystem $(shell $(CROSS32CC) -print-file-name=include) BOOTAFLAGS := -D__ASSEMBLY__ $(BOOTCFLAGS) -traditional -nostdinc diff --git a/arch/powerpc/include/asm/jump_label.h b/arch/powerpc/include/asm/jump_label.h index f016bb699b5f..ae098c438f00 100644 --- a/arch/powerpc/include/asm/jump_label.h +++ b/arch/powerpc/include/asm/jump_label.h @@ -19,7 +19,7 @@ static __always_inline bool arch_static_branch(struct static_key *key) { - asm_volatile_goto("1:\n\t" + asm goto("1:\n\t" "nop\n\t" ".pushsection __jump_table, \"aw\"\n\t" JUMP_ENTRY_TYPE "1b, %l[l_yes], %c0\n\t" diff --git a/arch/powerpc/include/asm/page.h b/arch/powerpc/include/asm/page.h index b9f426212d3a..988c812aab5b 100644 --- a/arch/powerpc/include/asm/page.h +++ b/arch/powerpc/include/asm/page.h @@ -211,19 +211,9 @@ extern long long virt_phys_offset; #define __va(x) ((void *)(unsigned long)((phys_addr_t)(x) + VIRT_PHYS_OFFSET)) #define __pa(x) ((unsigned long)(x) - VIRT_PHYS_OFFSET) #else -#ifdef CONFIG_PPC64 -/* - * gcc miscompiles (unsigned long)(&static_var) - PAGE_OFFSET - * with -mcmodel=medium, so we use & and | instead of - and + on 64-bit. - */ -#define __va(x) ((void *)(unsigned long)((phys_addr_t)(x) | PAGE_OFFSET)) -#define __pa(x) ((unsigned long)(x) & 0x0fffffffffffffffUL) - -#else /* 32-bit, non book E */ #define __va(x) ((void *)(unsigned long)((phys_addr_t)(x) + PAGE_OFFSET - MEMORY_START)) #define __pa(x) ((unsigned long)(x) - PAGE_OFFSET + MEMORY_START) #endif -#endif /* * Unfortunately the PLT is in the BSS in the PPC32 ELF ABI, diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c index 52e5758ea368..ee5b690a0bed 100644 --- a/arch/powerpc/kernel/align.c +++ b/arch/powerpc/kernel/align.c @@ -764,16 +764,6 @@ int fix_alignment(struct pt_regs *regs) nb = aligninfo[instr].len; flags = aligninfo[instr].flags; - /* ldbrx/stdbrx overlap lfs/stfs in the DSISR unfortunately */ - if (IS_XFORM(instruction) && ((instruction >> 1) & 0x3ff) == 532) { - nb = 8; - flags = LD+SW; - } else if (IS_XFORM(instruction) && - ((instruction >> 1) & 0x3ff) == 660) { - nb = 8; - flags = ST+SW; - } - /* Byteswap little endian loads and stores */ swiz = 0; if (regs->msr & MSR_LE) { diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c index 93d8d96840b5..c0d0dbddfba1 100644 --- a/arch/powerpc/kernel/iommu.c +++ b/arch/powerpc/kernel/iommu.c @@ -658,7 +658,7 @@ struct iommu_table *iommu_init_table(struct iommu_table *tbl, int nid) /* number of bytes needed for the bitmap */ sz = BITS_TO_LONGS(tbl->it_size) * sizeof(unsigned long); - page = alloc_pages_node(nid, GFP_KERNEL, get_order(sz)); + page = alloc_pages_node(nid, GFP_ATOMIC, get_order(sz)); if (!page) panic("iommu_init_table: Can't allocate %ld bytes\n", sz); tbl->it_map = page_address(page); diff --git a/arch/powerpc/kernel/lparcfg.c b/arch/powerpc/kernel/lparcfg.c index e2a0a162299b..d92f3871e9cf 100644 --- a/arch/powerpc/kernel/lparcfg.c +++ b/arch/powerpc/kernel/lparcfg.c @@ -35,13 +35,7 @@ #include #include #include -#include - -/* - * This isn't a module but we expose that to userspace - * via /proc so leave the definitions here - */ #define MODULE_VERS "1.9" #define MODULE_NAME "lparcfg" @@ -424,8 +418,7 @@ static void parse_em_data(struct seq_file *m) { unsigned long retbuf[PLPAR_HCALL_BUFSIZE]; - if (firmware_has_feature(FW_FEATURE_LPAR) && - plpar_hcall(H_GET_EM_PARMS, retbuf) == H_SUCCESS) + if (plpar_hcall(H_GET_EM_PARMS, retbuf) == H_SUCCESS) seq_printf(m, "power_mode_data=%016lx\n", retbuf[0]); } @@ -684,6 +677,7 @@ static int lparcfg_open(struct inode *inode, struct file *file) } static const struct file_operations lparcfg_fops = { + .owner = THIS_MODULE, .read = seq_read, .write = lparcfg_write, .open = lparcfg_open, @@ -705,4 +699,14 @@ static int __init lparcfg_init(void) } return 0; } -machine_device_initcall(pseries, lparcfg_init); + +static void __exit lparcfg_cleanup(void) +{ + remove_proc_subtree("powerpc/lparcfg", NULL); +} + +module_init(lparcfg_init); +module_exit(lparcfg_cleanup); +MODULE_DESCRIPTION("Interface for LPAR configuration data"); +MODULE_AUTHOR("Dave Engebretsen"); +MODULE_LICENSE("GPL"); diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c index a15fd1a0690c..e68a84568b8b 100644 --- a/arch/powerpc/kernel/sysfs.c +++ b/arch/powerpc/kernel/sysfs.c @@ -17,7 +17,6 @@ #include #include #include -#include #include "cacheinfo.h" @@ -180,25 +179,15 @@ SYSFS_PMCSETUP(spurr, SPRN_SPURR); SYSFS_PMCSETUP(dscr, SPRN_DSCR); SYSFS_PMCSETUP(pir, SPRN_PIR); -/* - Lets only enable read for phyp resources and - enable write when needed with a separate function. - Lets be conservative and default to pseries. -*/ static DEVICE_ATTR(mmcra, 0600, show_mmcra, store_mmcra); static DEVICE_ATTR(spurr, 0400, show_spurr, NULL); static DEVICE_ATTR(dscr, 0600, show_dscr, store_dscr); -static DEVICE_ATTR(purr, 0400, show_purr, store_purr); +static DEVICE_ATTR(purr, 0600, show_purr, store_purr); static DEVICE_ATTR(pir, 0400, show_pir, NULL); unsigned long dscr_default = 0; EXPORT_SYMBOL(dscr_default); -static void add_write_permission_dev_attr(struct device_attribute *attr) -{ - attr->attr.mode |= 0200; -} - static ssize_t show_dscr_default(struct device *dev, struct device_attribute *attr, char *buf) { @@ -405,11 +394,8 @@ static void __cpuinit register_cpu_online(unsigned int cpu) if (cpu_has_feature(CPU_FTR_MMCRA)) device_create_file(s, &dev_attr_mmcra); - if (cpu_has_feature(CPU_FTR_PURR)) { - if (!firmware_has_feature(FW_FEATURE_LPAR)) - add_write_permission_dev_attr(&dev_attr_purr); + if (cpu_has_feature(CPU_FTR_PURR)) device_create_file(s, &dev_attr_purr); - } if (cpu_has_feature(CPU_FTR_SPURR)) device_create_file(s, &dev_attr_spurr); diff --git a/arch/powerpc/kernel/tm.S b/arch/powerpc/kernel/tm.S index f2abb219a17b..1edd6c2a168d 100644 --- a/arch/powerpc/kernel/tm.S +++ b/arch/powerpc/kernel/tm.S @@ -79,11 +79,6 @@ _GLOBAL(tm_abort) TABORT(R3) blr - .section ".toc","aw" -DSCR_DEFAULT: - .tc dscr_default[TC],dscr_default - - .section ".text" /* void tm_reclaim(struct thread_struct *thread, * unsigned long orig_msr, @@ -183,18 +178,11 @@ dont_backup_fp: std r1, PACATMSCRATCH(r13) ld r1, PACAR1(r13) - /* Store the PPR in r11 and reset to decent value */ - std r11, GPR11(r1) /* Temporary stash */ - mfspr r11, SPRN_PPR - HMT_MEDIUM - /* Now get some more GPRS free */ std r7, GPR7(r1) /* Temporary stash */ std r12, GPR12(r1) /* '' '' '' */ ld r12, STACK_PARAM(0)(r1) /* Param 0, thread_struct * */ - std r11, THREAD_TM_PPR(r12) /* Store PPR and free r11 */ - addi r7, r12, PT_CKPT_REGS /* Thread's ckpt_regs */ /* Make r7 look like an exception frame so that we @@ -206,19 +194,15 @@ dont_backup_fp: SAVE_GPR(0, r7) /* user r0 */ SAVE_GPR(2, r7) /* user r2 */ SAVE_4GPRS(3, r7) /* user r3-r6 */ - SAVE_GPR(8, r7) /* user r8 */ - SAVE_GPR(9, r7) /* user r9 */ - SAVE_GPR(10, r7) /* user r10 */ + SAVE_4GPRS(8, r7) /* user r8-r11 */ ld r3, PACATMSCRATCH(r13) /* user r1 */ ld r4, GPR7(r1) /* user r7 */ - ld r5, GPR11(r1) /* user r11 */ - ld r6, GPR12(r1) /* user r12 */ - GET_SCRATCH0(8) /* user r13 */ + ld r5, GPR12(r1) /* user r12 */ + GET_SCRATCH0(6) /* user r13 */ std r3, GPR1(r7) std r4, GPR7(r7) - std r5, GPR11(r7) - std r6, GPR12(r7) - std r8, GPR13(r7) + std r5, GPR12(r7) + std r6, GPR13(r7) SAVE_NVGPRS(r7) /* user r14-r31 */ @@ -241,12 +225,14 @@ dont_backup_fp: std r6, _XER(r7) - /* ******************** TAR, DSCR ********** */ + /* ******************** TAR, PPR, DSCR ********** */ mfspr r3, SPRN_TAR - mfspr r4, SPRN_DSCR + mfspr r4, SPRN_PPR + mfspr r5, SPRN_DSCR std r3, THREAD_TM_TAR(r12) - std r4, THREAD_TM_DSCR(r12) + std r4, THREAD_TM_PPR(r12) + std r5, THREAD_TM_DSCR(r12) /* MSR and flags: We don't change CRs, and we don't need to alter * MSR. @@ -263,7 +249,7 @@ dont_backup_fp: std r3, THREAD_TM_TFHAR(r12) std r4, THREAD_TM_TFIAR(r12) - /* AMR is checkpointed too, but is unsupported by Linux. */ + /* AMR and PPR are checkpointed too, but are unsupported by Linux. */ /* Restore original MSR/IRQ state & clear TM mode */ ld r14, TM_FRAME_L0(r1) /* Orig MSR */ @@ -279,12 +265,6 @@ dont_backup_fp: mtcr r4 mtlr r0 ld r2, 40(r1) - - /* Load system default DSCR */ - ld r4, DSCR_DEFAULT@toc(r2) - ld r0, 0(r4) - mtspr SPRN_DSCR, r0 - blr @@ -369,50 +349,44 @@ dont_restore_fp: restore_gprs: - /* ******************** CR,LR,CCR,MSR ********** */ - ld r4, _CTR(r7) - ld r5, _LINK(r7) - ld r6, _CCR(r7) - ld r8, _XER(r7) - - mtctr r4 - mtlr r5 - mtcr r6 - mtxer r8 - - /* ******************** TAR ******************** */ + /* ******************** TAR, PPR, DSCR ********** */ ld r4, THREAD_TM_TAR(r3) + ld r5, THREAD_TM_PPR(r3) + ld r6, THREAD_TM_DSCR(r3) + mtspr SPRN_TAR, r4 + mtspr SPRN_PPR, r5 + mtspr SPRN_DSCR, r6 - /* Load up the PPR and DSCR in GPRs only at this stage */ - ld r5, THREAD_TM_DSCR(r3) - ld r6, THREAD_TM_PPR(r3) + /* ******************** CR,LR,CCR,MSR ********** */ + ld r3, _CTR(r7) + ld r4, _LINK(r7) + ld r5, _CCR(r7) + ld r6, _XER(r7) + + mtctr r3 + mtlr r4 + mtcr r5 + mtxer r6 /* MSR and flags: We don't change CRs, and we don't need to alter * MSR. */ REST_4GPRS(0, r7) /* GPR0-3 */ - REST_GPR(4, r7) /* GPR4 */ + REST_GPR(4, r7) /* GPR4-6 */ + REST_GPR(5, r7) + REST_GPR(6, r7) REST_4GPRS(8, r7) /* GPR8-11 */ REST_2GPRS(12, r7) /* GPR12-13 */ REST_NVGPRS(r7) /* GPR14-31 */ - /* Load up PPR and DSCR here so we don't run with user values for long - */ - mtspr SPRN_DSCR, r5 - mtspr SPRN_PPR, r6 - - REST_GPR(5, r7) /* GPR5-7 */ - REST_GPR(6, r7) - ld r7, GPR7(r7) + ld r7, GPR7(r7) /* GPR7 */ /* Commit register state as checkpointed state: */ TRECHKPT - HMT_MEDIUM - /* Our transactional state has now changed. * * Now just get out of here. Transactional (current) state will be @@ -431,12 +405,6 @@ restore_gprs: mtcr r4 mtlr r0 ld r2, 40(r1) - - /* Load system default DSCR */ - ld r4, DSCR_DEFAULT@toc(r2) - ld r0, 0(r4) - mtspr SPRN_DSCR, r0 - blr /* ****************************************************************** */ diff --git a/arch/powerpc/kernel/vdso32/sigtramp.S b/arch/powerpc/kernel/vdso32/sigtramp.S index 78c88af3890b..cf0c9c9c24f9 100644 --- a/arch/powerpc/kernel/vdso32/sigtramp.S +++ b/arch/powerpc/kernel/vdso32/sigtramp.S @@ -251,40 +251,6 @@ V_FUNCTION_END(__kernel_sigtramp_rt32) vsave_msr1 (31); \ vsave_msr2 (33, 32*16+12); \ vsave (32, 32*16) -#elif defined(CONFIG_SPE) -#define EH_FRAME_VMX \ - rsave (113, VREGS); \ - rsave (114, VREGS + 1*4); \ - rsave (115, VREGS + 2*4); \ - rsave (116, VREGS + 3*4); \ - rsave (117, VREGS + 4*4); \ - rsave (118, VREGS + 5*4); \ - rsave (119, VREGS + 6*4); \ - rsave (120, VREGS + 7*4); \ - rsave (121, VREGS + 8*4); \ - rsave (122, VREGS + 9*4); \ - rsave (123, VREGS + 10*4); \ - rsave (124, VREGS + 11*4); \ - rsave (125, VREGS + 12*4); \ - rsave (126, VREGS + 13*4); \ - rsave (127, VREGS + 14*4); \ - rsave (128, VREGS + 15*4); \ - rsave (129, VREGS + 16*4); \ - rsave (130, VREGS + 17*4); \ - rsave (131, VREGS + 18*4); \ - rsave (132, VREGS + 19*4); \ - rsave (133, VREGS + 20*4); \ - rsave (134, VREGS + 21*4); \ - rsave (135, VREGS + 22*4); \ - rsave (136, VREGS + 23*4); \ - rsave (137, VREGS + 24*4); \ - rsave (138, VREGS + 25*4); \ - rsave (139, VREGS + 26*4); \ - rsave (140, VREGS + 27*4); \ - rsave (141, VREGS + 28*4); \ - rsave (142, VREGS + 29*4); \ - rsave (143, VREGS + 30*4); \ - rsave (144, VREGS + 31*4); #else #define EH_FRAME_VMX #endif diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c index 2d845d8199fc..536016d792ba 100644 --- a/arch/powerpc/kernel/vio.c +++ b/arch/powerpc/kernel/vio.c @@ -1529,15 +1529,11 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, const char *cp; dn = dev->of_node; - if (!dn) { - strcat(buf, "\n"); - return strlen(buf); - } + if (!dn) + return -ENODEV; cp = of_get_property(dn, "compatible", NULL); - if (!cp) { - strcat(buf, "\n"); - return strlen(buf); - } + if (!cp) + return -ENODEV; return sprintf(buf, "vio:T%sS%s\n", vio_dev->type, cp); } diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S index 7bcd4d6e177b..b02f91e4c70d 100644 --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S @@ -1054,7 +1054,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206) BEGIN_FTR_SECTION mfspr r8, SPRN_DSCR ld r7, HSTATE_DSCR(r13) - std r8, VCPU_DSCR(r9) + std r8, VCPU_DSCR(r7) mtspr SPRN_DSCR, r7 END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206) diff --git a/arch/powerpc/kvm/book3s_xics.c b/arch/powerpc/kvm/book3s_xics.c index a3a5cb8ee7ea..94c1dd46b83d 100644 --- a/arch/powerpc/kvm/book3s_xics.c +++ b/arch/powerpc/kvm/book3s_xics.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include diff --git a/arch/powerpc/lib/checksum_64.S b/arch/powerpc/lib/checksum_64.S index 57a072065057..167f72555d60 100644 --- a/arch/powerpc/lib/checksum_64.S +++ b/arch/powerpc/lib/checksum_64.S @@ -226,35 +226,19 @@ _GLOBAL(csum_partial) blr - .macro srcnr -100: - .section __ex_table,"a" - .align 3 - .llong 100b,.Lsrc_error_nr - .previous - .endm - .macro source -150: - .section __ex_table,"a" - .align 3 - .llong 150b,.Lsrc_error - .previous - .endm - - .macro dstnr -200: +100: .section __ex_table,"a" .align 3 - .llong 200b,.Ldest_error_nr + .llong 100b,.Lsrc_error .previous .endm .macro dest -250: +200: .section __ex_table,"a" .align 3 - .llong 250b,.Ldest_error + .llong 200b,.Ldest_error .previous .endm @@ -285,16 +269,16 @@ _GLOBAL(csum_partial_copy_generic) rldicl. r6,r3,64-1,64-2 /* r6 = (r3 & 0x3) >> 1 */ beq .Lcopy_aligned - li r9,4 - sub r6,r9,r6 + li r7,4 + sub r6,r7,r6 mtctr r6 1: -srcnr; lhz r6,0(r3) /* align to doubleword */ +source; lhz r6,0(r3) /* align to doubleword */ subi r5,r5,2 addi r3,r3,2 adde r0,r0,r6 -dstnr; sth r6,0(r4) +dest; sth r6,0(r4) addi r4,r4,2 bdnz 1b @@ -408,10 +392,10 @@ dest; std r16,56(r4) mtctr r6 3: -srcnr; ld r6,0(r3) +source; ld r6,0(r3) addi r3,r3,8 adde r0,r0,r6 -dstnr; std r6,0(r4) +dest; std r6,0(r4) addi r4,r4,8 bdnz 3b @@ -421,10 +405,10 @@ dstnr; std r6,0(r4) srdi. r6,r5,2 beq .Lcopy_tail_halfword -srcnr; lwz r6,0(r3) +source; lwz r6,0(r3) addi r3,r3,4 adde r0,r0,r6 -dstnr; stw r6,0(r4) +dest; stw r6,0(r4) addi r4,r4,4 subi r5,r5,4 @@ -432,10 +416,10 @@ dstnr; stw r6,0(r4) srdi. r6,r5,1 beq .Lcopy_tail_byte -srcnr; lhz r6,0(r3) +source; lhz r6,0(r3) addi r3,r3,2 adde r0,r0,r6 -dstnr; sth r6,0(r4) +dest; sth r6,0(r4) addi r4,r4,2 subi r5,r5,2 @@ -443,10 +427,10 @@ dstnr; sth r6,0(r4) andi. r6,r5,1 beq .Lcopy_finish -srcnr; lbz r6,0(r3) +source; lbz r6,0(r3) sldi r9,r6,8 /* Pad the byte out to 16 bits */ adde r0,r0,r9 -dstnr; stb r6,0(r4) +dest; stb r6,0(r4) .Lcopy_finish: addze r0,r0 /* add in final carry */ @@ -456,11 +440,6 @@ dstnr; stb r6,0(r4) blr .Lsrc_error: - ld r14,STK_REG(R14)(r1) - ld r15,STK_REG(R15)(r1) - ld r16,STK_REG(R16)(r1) - addi r1,r1,STACKFRAMESIZE -.Lsrc_error_nr: cmpdi 0,r7,0 beqlr li r6,-EFAULT @@ -468,11 +447,6 @@ dstnr; stb r6,0(r4) blr .Ldest_error: - ld r14,STK_REG(R14)(r1) - ld r15,STK_REG(R15)(r1) - ld r16,STK_REG(R16)(r1) - addi r1,r1,STACKFRAMESIZE -.Ldest_error_nr: cmpdi 0,r8,0 beqlr li r6,-EFAULT diff --git a/arch/powerpc/mm/slb.c b/arch/powerpc/mm/slb.c index eb421240199a..a538c80db2df 100644 --- a/arch/powerpc/mm/slb.c +++ b/arch/powerpc/mm/slb.c @@ -313,7 +313,6 @@ void slb_initialize(void) asm volatile("isync":::"memory"); asm volatile("slbmte %0,%0"::"r" (0) : "memory"); asm volatile("isync; slbia; isync":::"memory"); - mb(); create_shadowed_slbe(PAGE_OFFSET, mmu_kernel_ssize, lflags, 0); create_shadowed_slbe(VMALLOC_START, mmu_kernel_ssize, vflags, 1); diff --git a/arch/powerpc/perf/power8-pmu.c b/arch/powerpc/perf/power8-pmu.c index 9aefaebedef1..d59f5b2d4c2f 100644 --- a/arch/powerpc/perf/power8-pmu.c +++ b/arch/powerpc/perf/power8-pmu.c @@ -194,7 +194,6 @@ #define MMCR1_UNIT_SHIFT(pmc) (60 - (4 * ((pmc) - 1))) #define MMCR1_COMBINE_SHIFT(pmc) (35 - ((pmc) - 1)) #define MMCR1_PMCSEL_SHIFT(pmc) (24 - (((pmc) - 1)) * 8) -#define MMCR1_FAB_SHIFT 36 #define MMCR1_DC_QUAL_SHIFT 47 #define MMCR1_IC_QUAL_SHIFT 46 @@ -368,8 +367,8 @@ static int power8_compute_mmcr(u64 event[], int n_ev, * the threshold bits are used for the match value. */ if (event_is_fab_match(event[i])) { - mmcr1 |= ((event[i] >> EVENT_THR_CTL_SHIFT) & - EVENT_THR_CTL_MASK) << MMCR1_FAB_SHIFT; + mmcr1 |= (event[i] >> EVENT_THR_CTL_SHIFT) & + EVENT_THR_CTL_MASK; } else { val = (event[i] >> EVENT_THR_CTL_SHIFT) & EVENT_THR_CTL_MASK; mmcra |= val << MMCRA_THR_CTL_SHIFT; diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 54b998f2750d..c11c8238797c 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -354,7 +354,7 @@ static int alloc_dispatch_log_kmem_cache(void) } early_initcall(alloc_dispatch_log_kmem_cache); -static void pseries_lpar_idle(void) +static void pSeries_idle(void) { /* This would call on the cpuidle framework, and the back-end pseries * driver to go to idle states @@ -362,22 +362,10 @@ static void pseries_lpar_idle(void) if (cpuidle_idle_call()) { /* On error, execute default handler * to go into low thread priority and possibly - * low power mode by cedeing processor to hypervisor + * low power mode. */ - - /* Indicate to hypervisor that we are idle. */ - get_lppaca()->idle = 1; - - /* - * Yield the processor to the hypervisor. We return if - * an external interrupt occurs (which are driven prior - * to returning here) or if a prod occurs from another - * processor. When returning here, external interrupts - * are enabled. - */ - cede_processor(); - - get_lppaca()->idle = 0; + HMT_low(); + HMT_very_low(); } } @@ -468,14 +456,15 @@ static void __init pSeries_setup_arch(void) pSeries_nvram_init(); - if (firmware_has_feature(FW_FEATURE_LPAR)) { + if (firmware_has_feature(FW_FEATURE_SPLPAR)) { vpa_init(boot_cpuid); - ppc_md.power_save = pseries_lpar_idle; + ppc_md.power_save = pSeries_idle; + } + + if (firmware_has_feature(FW_FEATURE_LPAR)) ppc_md.enable_pmcs = pseries_lpar_enable_pmcs; - } else { - /* No special idle routine */ + else ppc_md.enable_pmcs = power4_enable_pmcs; - } ppc_md.pcibios_root_bridge_prepare = pseries_root_bridge_prepare; diff --git a/arch/s390/include/asm/jump_label.h b/arch/s390/include/asm/jump_label.h index 346b1c85ffb4..6c32190dc73e 100644 --- a/arch/s390/include/asm/jump_label.h +++ b/arch/s390/include/asm/jump_label.h @@ -15,7 +15,7 @@ static __always_inline bool arch_static_branch(struct static_key *key) { - asm_volatile_goto("0: brcl 0,0\n" + asm goto("0: brcl 0,0\n" ".pushsection __jump_table, \"aw\"\n" ASM_ALIGN "\n" ASM_PTR " 0b, %l[label], %0\n" diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index 32bb7bf29397..4d5e6f8a7978 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -265,7 +265,6 @@ sysc_sigpending: tm __TI_flags+3(%r12),_TIF_SYSCALL jno sysc_return lm %r2,%r7,__PT_R2(%r11) # load svc arguments - l %r10,__TI_sysc_table(%r12) # 31 bit system call table xr %r8,%r8 # svc 0 returns -ENOSYS clc __PT_INT_CODE+2(2,%r11),BASED(.Lnr_syscalls+2) jnl sysc_nr_ok # invalid svc number -> do svc 0 diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S index 2e3befddecee..4c17eece707e 100644 --- a/arch/s390/kernel/entry64.S +++ b/arch/s390/kernel/entry64.S @@ -293,7 +293,6 @@ sysc_sigpending: tm __TI_flags+7(%r12),_TIF_SYSCALL jno sysc_return lmg %r2,%r7,__PT_R2(%r11) # load svc arguments - lg %r10,__TI_sysc_table(%r12) # address of system call table lghi %r8,0 # svc 0 returns -ENOSYS llgh %r1,__PT_INT_CODE+2(%r11) # load new svc number cghi %r1,NR_syscalls diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 698fb826e149..c1c7c683fa26 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -622,25 +622,14 @@ static int __vcpu_run(struct kvm_vcpu *vcpu) kvm_s390_deliver_pending_interrupts(vcpu); vcpu->arch.sie_block->icptcode = 0; + preempt_disable(); + kvm_guest_enter(); + preempt_enable(); VCPU_EVENT(vcpu, 6, "entering sie flags %x", atomic_read(&vcpu->arch.sie_block->cpuflags)); trace_kvm_s390_sie_enter(vcpu, atomic_read(&vcpu->arch.sie_block->cpuflags)); - - /* - * As PF_VCPU will be used in fault handler, between guest_enter - * and guest_exit should be no uaccess. - */ - preempt_disable(); - kvm_guest_enter(); - preempt_enable(); rc = sie64a(vcpu->arch.sie_block, vcpu->run->s.regs.gprs); - kvm_guest_exit(); - - VCPU_EVENT(vcpu, 6, "exit sie icptcode %d", - vcpu->arch.sie_block->icptcode); - trace_kvm_s390_sie_exit(vcpu, vcpu->arch.sie_block->icptcode); - if (rc) { if (kvm_is_ucontrol(vcpu->kvm)) { rc = SIE_INTERCEPT_UCONTROL; @@ -650,6 +639,10 @@ static int __vcpu_run(struct kvm_vcpu *vcpu) rc = kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); } } + VCPU_EVENT(vcpu, 6, "exit sie icptcode %d", + vcpu->arch.sie_block->icptcode); + trace_kvm_s390_sie_exit(vcpu, vcpu->arch.sie_block->icptcode); + kvm_guest_exit(); memcpy(&vcpu->run->s.regs.gprs[14], &vcpu->arch.sie_block->gg14, 16); return rc; diff --git a/arch/sparc/include/asm/jump_label.h b/arch/sparc/include/asm/jump_label.h index ec2e2e2aba7d..5080d16a832f 100644 --- a/arch/sparc/include/asm/jump_label.h +++ b/arch/sparc/include/asm/jump_label.h @@ -9,7 +9,7 @@ static __always_inline bool arch_static_branch(struct static_key *key) { - asm_volatile_goto("1:\n\t" + asm goto("1:\n\t" "nop\n\t" "nop\n\t" ".pushsection __jump_table, \"aw\"\n\t" diff --git a/arch/sparc/kernel/ds.c b/arch/sparc/kernel/ds.c index 252f8768aac6..5ef48dab5636 100644 --- a/arch/sparc/kernel/ds.c +++ b/arch/sparc/kernel/ds.c @@ -842,8 +842,9 @@ void ldom_reboot(const char *boot_command) if (boot_command && strlen(boot_command)) { unsigned long len; - snprintf(full_boot_str, sizeof(full_boot_str), "boot %s", - boot_command); + strcpy(full_boot_str, "boot "); + strlcpy(full_boot_str + strlen("boot "), boot_command, + sizeof(full_boot_str + strlen("boot "))); len = strlen(full_boot_str); if (reboot_data_supported) { diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S index 33c02b15f478..e2a030045089 100644 --- a/arch/sparc/kernel/entry.S +++ b/arch/sparc/kernel/entry.S @@ -839,7 +839,7 @@ sys_sigreturn: nop call syscall_trace - mov 1, %o1 + nop 1: /* We don't want to muck with user registers like a diff --git a/arch/sparc/kernel/ktlb.S b/arch/sparc/kernel/ktlb.S index fde5a419cf27..0746e5e32b37 100644 --- a/arch/sparc/kernel/ktlb.S +++ b/arch/sparc/kernel/ktlb.S @@ -25,10 +25,11 @@ kvmap_itlb: */ kvmap_itlb_4v: +kvmap_itlb_nonlinear: /* Catch kernel NULL pointer calls. */ sethi %hi(PAGE_SIZE), %g5 cmp %g4, %g5 - blu,pn %xcc, kvmap_itlb_longpath + bleu,pn %xcc, kvmap_dtlb_longpath nop KERN_TSB_LOOKUP_TL1(%g4, %g6, %g5, %g1, %g2, %g3, kvmap_itlb_load) diff --git a/arch/sparc/kernel/syscalls.S b/arch/sparc/kernel/syscalls.S index 73ec8a798d95..22a1098961f5 100644 --- a/arch/sparc/kernel/syscalls.S +++ b/arch/sparc/kernel/syscalls.S @@ -152,7 +152,7 @@ linux_syscall_trace32: srl %i4, 0, %o4 srl %i1, 0, %o1 srl %i2, 0, %o2 - ba,pt %xcc, 5f + ba,pt %xcc, 2f srl %i3, 0, %o3 linux_syscall_trace: @@ -182,13 +182,13 @@ linux_sparc_syscall32: srl %i1, 0, %o1 ! IEU0 Group ldx [%g6 + TI_FLAGS], %l0 ! Load - srl %i3, 0, %o3 ! IEU0 + srl %i5, 0, %o5 ! IEU1 srl %i2, 0, %o2 ! IEU0 Group andcc %l0, (_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT|_TIF_SYSCALL_TRACEPOINT), %g0 bne,pn %icc, linux_syscall_trace32 ! CTI mov %i0, %l5 ! IEU1 -5: call %l7 ! CTI Group brk forced - srl %i5, 0, %o5 ! IEU1 + call %l7 ! CTI Group brk forced + srl %i3, 0, %o3 ! IEU0 ba,a,pt %xcc, 3f /* Linux native system calls enter here... */ diff --git a/arch/sparc/kernel/trampoline_64.S b/arch/sparc/kernel/trampoline_64.S index 3a43edb46aa3..2e973a26fbda 100644 --- a/arch/sparc/kernel/trampoline_64.S +++ b/arch/sparc/kernel/trampoline_64.S @@ -131,6 +131,7 @@ startup_continue: clr %l5 sethi %hi(num_kernel_image_mappings), %l6 lduw [%l6 + %lo(num_kernel_image_mappings)], %l6 + add %l6, 1, %l6 mov 15, %l7 BRANCH_IF_ANY_CHEETAH(g1,g5,2f) @@ -223,6 +224,7 @@ niagara_lock_tlb: clr %l5 sethi %hi(num_kernel_image_mappings), %l6 lduw [%l6 + %lo(num_kernel_image_mappings)], %l6 + add %l6, 1, %l6 1: mov HV_FAST_MMU_MAP_PERM_ADDR, %o5 diff --git a/arch/sparc/lib/ksyms.c b/arch/sparc/lib/ksyms.c index 323335b9cd2b..0c4e35e522fa 100644 --- a/arch/sparc/lib/ksyms.c +++ b/arch/sparc/lib/ksyms.c @@ -98,6 +98,15 @@ EXPORT_SYMBOL(___copy_from_user); EXPORT_SYMBOL(___copy_in_user); EXPORT_SYMBOL(__clear_user); +/* RW semaphores */ +EXPORT_SYMBOL(__down_read); +EXPORT_SYMBOL(__down_read_trylock); +EXPORT_SYMBOL(__down_write); +EXPORT_SYMBOL(__down_write_trylock); +EXPORT_SYMBOL(__up_read); +EXPORT_SYMBOL(__up_write); +EXPORT_SYMBOL(__downgrade_write); + /* Atomic counter implementation. */ EXPORT_SYMBOL(atomic_add); EXPORT_SYMBOL(atomic_add_ret); diff --git a/arch/tile/include/asm/percpu.h b/arch/tile/include/asm/percpu.h index 4f7ae39fa202..63294f5a8efb 100644 --- a/arch/tile/include/asm/percpu.h +++ b/arch/tile/include/asm/percpu.h @@ -15,37 +15,9 @@ #ifndef _ASM_TILE_PERCPU_H #define _ASM_TILE_PERCPU_H -register unsigned long my_cpu_offset_reg asm("tp"); - -#ifdef CONFIG_PREEMPT -/* - * For full preemption, we can't just use the register variable - * directly, since we need barrier() to hazard against it, causing the - * compiler to reload anything computed from a previous "tp" value. - * But we also don't want to use volatile asm, since we'd like the - * compiler to be able to cache the value across multiple percpu reads. - * So we use a fake stack read as a hazard against barrier(). - * The 'U' constraint is like 'm' but disallows postincrement. - */ -static inline unsigned long __my_cpu_offset(void) -{ - unsigned long tp; - register unsigned long *sp asm("sp"); - asm("move %0, tp" : "=r" (tp) : "U" (*sp)); - return tp; -} -#define __my_cpu_offset __my_cpu_offset() -#else -/* - * We don't need to hazard against barrier() since "tp" doesn't ever - * change with PREEMPT_NONE, and with PREEMPT_VOLUNTARY it only - * changes at function call points, at which we are already re-reading - * the value of "tp" due to "my_cpu_offset_reg" being a global variable. - */ -#define __my_cpu_offset my_cpu_offset_reg -#endif - -#define set_my_cpu_offset(tp) (my_cpu_offset_reg = (tp)) +register unsigned long __my_cpu_offset __asm__("tp"); +#define __my_cpu_offset __my_cpu_offset +#define set_my_cpu_offset(tp) (__my_cpu_offset = (tp)) #include diff --git a/arch/um/include/shared/os.h b/arch/um/include/shared/os.h index c70a234a3f8c..95feaa47a2fb 100644 --- a/arch/um/include/shared/os.h +++ b/arch/um/include/shared/os.h @@ -200,7 +200,6 @@ extern int os_unmap_memory(void *addr, int len); extern int os_drop_memory(void *addr, int length); extern int can_drop_memory(void); extern void os_flush_stdout(void); -extern int os_mincore(void *addr, unsigned long len); /* execvp.c */ extern int execvp_noalloc(char *buf, const char *file, char *const argv[]); diff --git a/arch/um/kernel/Makefile b/arch/um/kernel/Makefile index d8b78a03855c..babe21826e3e 100644 --- a/arch/um/kernel/Makefile +++ b/arch/um/kernel/Makefile @@ -13,7 +13,7 @@ clean-files := obj-y = config.o exec.o exitcode.o irq.o ksyms.o mem.o \ physmem.o process.o ptrace.o reboot.o sigio.o \ signal.o smp.o syscall.o sysrq.o time.o tlb.o trap.o \ - um_arch.o umid.o maccess.o skas/ + um_arch.o umid.o skas/ obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o obj-$(CONFIG_GPROF) += gprof_syms.o diff --git a/arch/um/os-Linux/process.c b/arch/um/os-Linux/process.c index 67b9c8f5a89e..b8f34c9e53ae 100644 --- a/arch/um/os-Linux/process.c +++ b/arch/um/os-Linux/process.c @@ -4,7 +4,6 @@ */ #include -#include #include #include #include @@ -233,57 +232,6 @@ int __init can_drop_memory(void) return ok; } -static int os_page_mincore(void *addr) -{ - char vec[2]; - int ret; - - ret = mincore(addr, UM_KERN_PAGE_SIZE, vec); - if (ret < 0) { - if (errno == ENOMEM || errno == EINVAL) - return 0; - else - return -errno; - } - - return vec[0] & 1; -} - -int os_mincore(void *addr, unsigned long len) -{ - char *vec; - int ret, i; - - if (len <= UM_KERN_PAGE_SIZE) - return os_page_mincore(addr); - - vec = calloc(1, (len + UM_KERN_PAGE_SIZE - 1) / UM_KERN_PAGE_SIZE); - if (!vec) - return -ENOMEM; - - ret = mincore(addr, UM_KERN_PAGE_SIZE, vec); - if (ret < 0) { - if (errno == ENOMEM || errno == EINVAL) - ret = 0; - else - ret = -errno; - - goto out; - } - - for (i = 0; i < ((len + UM_KERN_PAGE_SIZE - 1) / UM_KERN_PAGE_SIZE); i++) { - if (!(vec[i] & 1)) { - ret = 0; - goto out; - } - } - - ret = 1; -out: - free(vec); - return ret; -} - void init_new_thread_signals(void) { set_handler(SIGSEGV); diff --git a/arch/x86/configs/i386_edison_defconfig b/arch/x86/configs/i386_edison_defconfig index e79bdd1a47d8..b1ed708a94cb 100644 --- a/arch/x86/configs/i386_edison_defconfig +++ b/arch/x86/configs/i386_edison_defconfig @@ -153,7 +153,7 @@ CONFIG_NAMESPACES=y # CONFIG_IPC_NS is not set # CONFIG_USER_NS is not set # CONFIG_PID_NS is not set -# CONFIG_NET_NS is not set +CONFIG_NET_NS=y CONFIG_UIDGID_CONVERTED=y # CONFIG_UIDGID_STRICT_TYPE_CHECKS is not set CONFIG_SCHED_AUTOGROUP=y @@ -1544,7 +1544,7 @@ CONFIG_I2C_ALGOBIT=y CONFIG_I2C_DESIGNWARE_CORE_FORK=y CONFIG_I2C_DESIGNWARE_PCI_FORK=y # CONFIG_I2C_DESIGNWARE_PLATFORM_FORK is not set -# CONFIG_I2C_DW_SPEED_MODE_DEBUG is not set +CONFIG_I2C_DW_SPEED_MODE_DEBUG=y CONFIG_I2C_PMIC=y # CONFIG_I2C_DESIGNWARE_PCI is not set # CONFIG_I2C_EG20T is not set @@ -1719,6 +1719,7 @@ CONFIG_BATTERY_MAX17042=y # CONFIG_POWER_RESET is not set # CONFIG_POWER_AVS is not set CONFIG_HWMON=y +CONFIG_INTEL_MCU=y # CONFIG_HWMON_VID is not set # CONFIG_HWMON_DEBUG_CHIP is not set @@ -2039,13 +2040,80 @@ CONFIG_VIDEO_V4L2_SUBDEV_API=y CONFIG_VIDEO_V4L2=y # CONFIG_VIDEO_ADV_DEBUG is not set # CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +CONFIG_VIDEOBUF2_CORE=m +CONFIG_VIDEOBUF2_MEMOPS=m +CONFIG_VIDEOBUF2_VMALLOC=m # CONFIG_VIDEO_V4L2_INT_DEVICE is not set # CONFIG_TTPCI_EEPROM is not set # # Media drivers # -# CONFIG_MEDIA_USB_SUPPORT is not set +CONFIG_MEDIA_USB_SUPPORT=y + +# +# Webcam devices +# +CONFIG_USB_VIDEO_CLASS=m +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y +CONFIG_USB_GSPCA=m +# CONFIG_USB_M5602 is not set +# CONFIG_USB_STV06XX is not set +# CONFIG_USB_GL860 is not set +# CONFIG_USB_GSPCA_BENQ is not set +# CONFIG_USB_GSPCA_CONEX is not set +# CONFIG_USB_GSPCA_CPIA1 is not set +# CONFIG_USB_GSPCA_ETOMS is not set +# CONFIG_USB_GSPCA_FINEPIX is not set +# CONFIG_USB_GSPCA_JEILINJ is not set +# CONFIG_USB_GSPCA_JL2005BCD is not set +# CONFIG_USB_GSPCA_KINECT is not set +# CONFIG_USB_GSPCA_KONICA is not set +# CONFIG_USB_GSPCA_MARS is not set +# CONFIG_USB_GSPCA_MR97310A is not set +# CONFIG_USB_GSPCA_NW80X is not set +# CONFIG_USB_GSPCA_OV519 is not set +# CONFIG_USB_GSPCA_OV534 is not set +# CONFIG_USB_GSPCA_OV534_9 is not set +# CONFIG_USB_GSPCA_PAC207 is not set +# CONFIG_USB_GSPCA_PAC7302 is not set +# CONFIG_USB_GSPCA_PAC7311 is not set +# CONFIG_USB_GSPCA_SE401 is not set +# CONFIG_USB_GSPCA_SN9C2028 is not set +# CONFIG_USB_GSPCA_SN9C20X is not set +# CONFIG_USB_GSPCA_SONIXB is not set +# CONFIG_USB_GSPCA_SONIXJ is not set +# CONFIG_USB_GSPCA_SPCA500 is not set +# CONFIG_USB_GSPCA_SPCA501 is not set +# CONFIG_USB_GSPCA_SPCA505 is not set +# CONFIG_USB_GSPCA_SPCA506 is not set +# CONFIG_USB_GSPCA_SPCA508 is not set +# CONFIG_USB_GSPCA_SPCA561 is not set +# CONFIG_USB_GSPCA_SPCA1528 is not set +# CONFIG_USB_GSPCA_SQ905 is not set +# CONFIG_USB_GSPCA_SQ905C is not set +# CONFIG_USB_GSPCA_SQ930X is not set +# CONFIG_USB_GSPCA_STK014 is not set +# CONFIG_USB_GSPCA_STV0680 is not set +# CONFIG_USB_GSPCA_SUNPLUS is not set +# CONFIG_USB_GSPCA_T613 is not set +# CONFIG_USB_GSPCA_TOPRO is not set +# CONFIG_USB_GSPCA_TV8532 is not set +# CONFIG_USB_GSPCA_VC032X is not set +# CONFIG_USB_GSPCA_VICAM is not set +# CONFIG_USB_GSPCA_XIRLINK_CIT is not set +# CONFIG_USB_GSPCA_ZC3XX is not set +# CONFIG_USB_PWC is not set +# CONFIG_VIDEO_CPIA2 is not set +# CONFIG_USB_ZR364XX is not set +# CONFIG_USB_STKWEBCAM is not set +# CONFIG_USB_S2255 is not set +# CONFIG_USB_SN9C102 is not set + +# +# Webcam, TV (analog/digital) USB devices +# +# CONFIG_VIDEO_EM28XX is not set # CONFIG_MEDIA_PCI_SUPPORT is not set # CONFIG_V4L_PLATFORM_DRIVERS is not set # CONFIG_V4L_MEM2MEM_DRIVERS is not set @@ -2314,7 +2382,8 @@ CONFIG_SOUND=y CONFIG_SND=y CONFIG_SND_TIMER=y CONFIG_SND_PCM=y -CONFIG_SND_COMPRESS_OFFLOAD=y +# CONFIG_SND_COMPRESS_OFFLOAD is not set +# CONFIG_SND_EFFECTS_OFFLOAD is not set CONFIG_SND_JACK=y CONFIG_SND_SEQUENCER=y # CONFIG_SND_SEQ_DUMMY is not set @@ -2344,7 +2413,7 @@ CONFIG_SND_ALOOP=y # CONFIG_SND_PCI is not set # CONFIG_SND_SPI is not set CONFIG_SND_USB=y -# CONFIG_SND_USB_AUDIO is not set +CONFIG_SND_USB_AUDIO=y # CONFIG_SND_USB_UA101 is not set # CONFIG_SND_USB_USX2Y is not set # CONFIG_SND_USB_CAIAQ is not set @@ -2353,8 +2422,15 @@ CONFIG_SND_USB=y CONFIG_SND_SOC=y # CONFIG_SND_ATMEL_SOC is not set # CONFIG_SND_MFLD_MACHINE is not set +CONFIG_SND_INTEL_SST=y +CONFIG_SND_MRFLD_MACHINE=y +CONFIG_SND_SST_PLATFORM=y +CONFIG_SST_MRFLD_DPCM=y +CONFIG_SND_SST_MACHINE=y CONFIG_SND_SOC_I2C_AND_SPI=y # CONFIG_SND_SOC_ALL_CODECS is not set +CONFIG_SND_SOC_WM_HUBS=y +CONFIG_SND_SOC_WM8994=y # CONFIG_SND_SIMPLE_CARD is not set # CONFIG_SOUND_PRIME is not set @@ -3030,6 +3106,7 @@ CONFIG_INTEL_MID_POWER_BUTTON=y # CONFIG_IBM_RTL is not set # CONFIG_SAMSUNG_LAPTOP is not set CONFIG_INTEL_SCU_FLIS=y +CONFIG_INTEL_PSH_IPC=y # # Hardware Spinlock drivers @@ -3296,7 +3373,22 @@ CONFIG_PSTORE_RAM=y # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set # CONFIG_F2FS_FS is not set -# CONFIG_AUFS_FS is not set +CONFIG_AUFS_FS=m +CONFIG_AUFS_BRANCH_MAX_127=y +# CONFIG_AUFS_BRANCH_MAX_511 is not set +# CONFIG_AUFS_BRANCH_MAX_1023 is not set +# CONFIG_AUFS_BRANCH_MAX_32767 is not set +CONFIG_AUFS_SBILIST=y +# CONFIG_AUFS_HNOTIFY is not set +# CONFIG_AUFS_EXPORT is not set +# CONFIG_AUFS_RDU is not set +# CONFIG_AUFS_PROC_MAP is not set +# CONFIG_AUFS_SP_IATTR is not set +# CONFIG_AUFS_SHWH is not set +# CONFIG_AUFS_BR_RAMFS is not set +# CONFIG_AUFS_BR_FUSE is not set +CONFIG_AUFS_BDEV_LOOP=y +# CONFIG_AUFS_DEBUG is not set CONFIG_NETWORK_FILESYSTEMS=y CONFIG_NFS_FS=y CONFIG_NFS_V2=y diff --git a/arch/x86/ia32/ia32_signal.c b/arch/x86/ia32/ia32_signal.c index 10adb41f162e..cf1a471a18a2 100644 --- a/arch/x86/ia32/ia32_signal.c +++ b/arch/x86/ia32/ia32_signal.c @@ -459,7 +459,7 @@ int ia32_setup_rt_frame(int sig, struct ksignal *ksig, else put_user_ex(0, &frame->uc.uc_flags); put_user_ex(0, &frame->uc.uc_link); - compat_save_altstack_ex(&frame->uc.uc_stack, regs->sp); + err |= __compat_save_altstack(&frame->uc.uc_stack, regs->sp); if (ksig->ka.sa.sa_flags & SA_RESTORER) restorer = ksig->ka.sa.sa_restorer; diff --git a/arch/x86/include/asm/bootparam_utils.h b/arch/x86/include/asm/bootparam_utils.h index 4a8cb8d7cbd5..653668d140f9 100644 --- a/arch/x86/include/asm/bootparam_utils.h +++ b/arch/x86/include/asm/bootparam_utils.h @@ -35,9 +35,9 @@ static void sanitize_boot_params(struct boot_params *boot_params) */ if (boot_params->sentinel) { /* fields in boot_params are left uninitialized, clear them */ - memset(&boot_params->ext_ramdisk_image, 0, + memset(&boot_params->olpc_ofw_header, 0, (char *)&boot_params->efi_info - - (char *)&boot_params->ext_ramdisk_image); + (char *)&boot_params->olpc_ofw_header); memset(&boot_params->kbd_status, 0, (char *)&boot_params->hdr - (char *)&boot_params->kbd_status); diff --git a/arch/x86/include/asm/checksum_32.h b/arch/x86/include/asm/checksum_32.h index f50de6951738..46fc474fd819 100644 --- a/arch/x86/include/asm/checksum_32.h +++ b/arch/x86/include/asm/checksum_32.h @@ -49,15 +49,9 @@ static inline __wsum csum_partial_copy_from_user(const void __user *src, int len, __wsum sum, int *err_ptr) { - __wsum ret; - might_sleep(); - stac(); - ret = csum_partial_copy_generic((__force void *)src, dst, - len, sum, err_ptr, NULL); - clac(); - - return ret; + return csum_partial_copy_generic((__force void *)src, dst, + len, sum, err_ptr, NULL); } /* @@ -182,16 +176,10 @@ static inline __wsum csum_and_copy_to_user(const void *src, int len, __wsum sum, int *err_ptr) { - __wsum ret; - might_sleep(); - if (access_ok(VERIFY_WRITE, dst, len)) { - stac(); - ret = csum_partial_copy_generic(src, (__force void *)dst, - len, sum, NULL, err_ptr); - clac(); - return ret; - } + if (access_ok(VERIFY_WRITE, dst, len)) + return csum_partial_copy_generic(src, (__force void *)dst, + len, sum, NULL, err_ptr); if (len) *err_ptr = -EFAULT; diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h index 4af181dacf9e..e99ac27f95b2 100644 --- a/arch/x86/include/asm/cpufeature.h +++ b/arch/x86/include/asm/cpufeature.h @@ -365,7 +365,7 @@ extern const char * const x86_power_flags[32]; static __always_inline __pure bool __static_cpu_has(u16 bit) { #if __GNUC__ > 4 || __GNUC_MINOR__ >= 5 - asm_volatile_goto("1: jmp %l[t_no]\n" + asm goto("1: jmp %l[t_no]\n" "2:\n" ".section .altinstructions,\"a\"\n" " .long 1b - .\n" diff --git a/arch/x86/include/asm/e820.h b/arch/x86/include/asm/e820.h index 779c2efe2e97..cccd07fa5e3a 100644 --- a/arch/x86/include/asm/e820.h +++ b/arch/x86/include/asm/e820.h @@ -29,7 +29,7 @@ extern void e820_setup_gap(void); extern int e820_search_gap(unsigned long *gapstart, unsigned long *gapsize, unsigned long start_addr, unsigned long long end_addr); struct setup_data; -extern void parse_e820_ext(u64 phys_addr, u32 data_len); +extern void parse_e820_ext(struct setup_data *data); #if defined(CONFIG_X86_64) || \ (defined(CONFIG_X86_32) && defined(CONFIG_HIBERNATION)) diff --git a/arch/x86/include/asm/intel_psh_ipc.h b/arch/x86/include/asm/intel_psh_ipc.h new file mode 100644 index 000000000000..ed4d3c14c79c --- /dev/null +++ b/arch/x86/include/asm/intel_psh_ipc.h @@ -0,0 +1,35 @@ +#ifndef _ASM_X86_INTEL_PSH_IPC_H_ +#define _ASM_X86_INTEL_PSH_IPC_H_ + +#define CHANNEL_BUSY (1 << 31) +#define PSH_IPC_CONTINUE (1 << 30) + +struct psh_msg { + u32 msg; + u32 param; +}; + +enum psh_channel { + PSH_SEND_CH0 = 0, + PSH_SEND_CH1, + PSH_SEND_CH2, + PSH_SEND_CH3, + NUM_IA2PSH_IPC, + PSH_RECV_CH0 = NUM_IA2PSH_IPC, + PSH_RECV_CH1, + PSH_RECV_CH2, + PSH_RECV_CH3, + PSH_RECV_END, + NUM_PSH2IA_IPC = PSH_RECV_END - PSH_RECV_CH0, + NUM_ALL_CH = NUM_IA2PSH_IPC + NUM_PSH2IA_IPC, +}; + +typedef void(*psh_channel_handle_t)(u32 msg, u32 param, void *data); +int intel_ia2psh_command(struct psh_msg *in, struct psh_msg *out, + int ch, int timeout); +int intel_psh_ipc_bind(int ch, psh_channel_handle_t handle, void *data); +void intel_psh_ipc_unbind(int ch); + +void intel_psh_ipc_disable_irq(void); +void intel_psh_ipc_enable_irq(void); +#endif diff --git a/arch/x86/include/asm/intel_sst_mrfld.h b/arch/x86/include/asm/intel_sst_mrfld.h new file mode 100644 index 000000000000..041ff851fe50 --- /dev/null +++ b/arch/x86/include/asm/intel_sst_mrfld.h @@ -0,0 +1,44 @@ +/* intel_sst_mrlfd.h - Common enum of the Merrifield platform + * + * Copyright (C) 2013 Intel Corp + * Author: Samreen Nilofer + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#ifndef _INTEL_SST_MRFLD_H +#define _INTEL_SST_MRFLD_H + +enum { + MERR_SALTBAY_AUDIO = 0, + MERR_SALTBAY_COMPR, + MERR_SALTBAY_VOIP, + MERR_SALTBAY_PROBE, + MERR_SALTBAY_AWARE, + MERR_SALTBAY_VAD, + MERR_SALTBAY_POWER, +}; + +enum { + MERR_DPCM_AUDIO = 0, + MERR_DPCM_DB, + MERR_DPCM_LL, + MERR_DPCM_COMPR, + MERR_DPCM_VOIP, + MERR_DPCM_PROBE, +}; + +#endif diff --git a/arch/x86/include/asm/jump_label.h b/arch/x86/include/asm/jump_label.h index 029766958e69..3a16c1483b45 100644 --- a/arch/x86/include/asm/jump_label.h +++ b/arch/x86/include/asm/jump_label.h @@ -13,7 +13,7 @@ static __always_inline bool arch_static_branch(struct static_key *key) { - asm_volatile_goto("1:" + asm goto("1:" STATIC_KEY_INITIAL_NOP ".pushsection __jump_table, \"aw\" \n\t" _ASM_ALIGN "\n\t" diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h index e6833c655e59..fa5f71e021d5 100644 --- a/arch/x86/include/asm/mce.h +++ b/arch/x86/include/asm/mce.h @@ -32,20 +32,11 @@ #define MCI_STATUS_PCC (1ULL<<57) /* processor context corrupt */ #define MCI_STATUS_S (1ULL<<56) /* Signaled machine check */ #define MCI_STATUS_AR (1ULL<<55) /* Action required */ - -/* - * Note that the full MCACOD field of IA32_MCi_STATUS MSR is - * bits 15:0. But bit 12 is the 'F' bit, defined for corrected - * errors to indicate that errors are being filtered by hardware. - * We should mask out bit 12 when looking for specific signatures - * of uncorrected errors - so the F bit is deliberately skipped - * in this #define. - */ -#define MCACOD 0xefff /* MCA Error Code */ +#define MCACOD 0xffff /* MCA Error Code */ /* Architecturally defined codes from SDM Vol. 3B Chapter 15 */ #define MCACOD_SCRUB 0x00C0 /* 0xC0-0xCF Memory Scrubbing */ -#define MCACOD_SCRUBMSK 0xeff0 /* Skip bit 12 ('F' bit) */ +#define MCACOD_SCRUBMSK 0xfff0 #define MCACOD_L3WB 0x017A /* L3 Explicit Writeback */ #define MCACOD_DATA 0x0134 /* Data Load */ #define MCACOD_INSTR 0x0150 /* Instruction Fetch */ diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h index be12c534fd59..cdbf36776106 100644 --- a/arch/x86/include/asm/mmu_context.h +++ b/arch/x86/include/asm/mmu_context.h @@ -45,28 +45,22 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, /* Re-load page tables */ load_cr3(next->pgd); - /* Stop flush ipis for the previous mm */ + /* stop flush ipis for the previous mm */ cpumask_clear_cpu(cpu, mm_cpumask(prev)); - /* Load the LDT, if the LDT is different: */ + /* + * load the LDT, if the LDT is different: + */ if (unlikely(prev->context.ldt != next->context.ldt)) load_LDT_nolock(&next->context); } #ifdef CONFIG_SMP - else { + else { this_cpu_write(cpu_tlbstate.state, TLBSTATE_OK); BUG_ON(this_cpu_read(cpu_tlbstate.active_mm) != next); - if (!cpumask_test_cpu(cpu, mm_cpumask(next))) { - /* - * On established mms, the mm_cpumask is only changed - * from irq context, from ptep_clear_flush() while in - * lazy tlb mode, and here. Irqs are blocked during - * schedule, protecting us from simultaneous changes. - */ - cpumask_set_cpu(cpu, mm_cpumask(next)); - /* - * We were in lazy tlb mode and leave_mm disabled + if (!cpumask_test_and_set_cpu(cpu, mm_cpumask(next))) { + /* We were in lazy tlb mode and leave_mm disabled * tlb flush IPI delivery. We must reload CR3 * to make sure to use no freed page tables. */ diff --git a/arch/x86/include/asm/platform_byt_audio.h b/arch/x86/include/asm/platform_byt_audio.h new file mode 100644 index 000000000000..fed2b1b2f4bc --- /dev/null +++ b/arch/x86/include/asm/platform_byt_audio.h @@ -0,0 +1,49 @@ +/* + * platform_byt_audio.h: Baytrail audio platform data header file + * + * (C) Copyright 2013 Intel Corporation + * Author: Omair Md Abdullah + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ +#ifndef _PLATFORM_BYT_AUDIO_H_ +#define _PLATFORM_BYT_AUDIO_H_ + +enum { + BYT_AUD_AIF1 = 0, + BYT_AUD_AIF2, + BYT_AUD_COMPR_DEV, +#ifdef CONFIG_SND_SOC_COMMS_SSP + BYT_COMMS_BT, + BYT_COMMS_MODEM, +#endif /* CONFIG_SND_SOC_COMMS_SSP */ + BYT_AUD_PROBE_DEV, +}; + +enum { + BYT_CR_AUD_AIF1 = 0, + BYT_CR_AUD_COMPR_DEV, + BYT_CR_COMMS_BT, +}; +/* LPE viewpoint addresses */ +/* TODO: move to DSDT */ +#define SST_BYT_IRAM_PHY_START 0xff2c0000 +#define SST_BYT_IRAM_PHY_END 0xff2d4000 +#define SST_BYT_DRAM_PHY_START 0xff300000 +#define SST_BYT_DRAM_PHY_END 0xff320000 +#define SST_BYT_IMR_VIRT_START 0xc0000000 /* virtual addr in LPE */ +#define SST_BYT_IMR_VIRT_END 0xc01fffff +#define SST_BYT_SHIM_PHY_ADDR 0xff340000 +#define SST_BYT_MBOX_PHY_ADDR 0xff344000 +#define SST_BYT_DMA0_PHY_ADDR 0xff298000 +#define SST_BYT_DMA1_PHY_ADDR 0xff29c000 +#define SST_BYT_SSP0_PHY_ADDR 0xff2a0000 +#define SST_BYT_SSP2_PHY_ADDR 0xff2a2000 + +#define BYT_FW_MOD_TABLE_OFFSET 0x80000 +#define BYT_FW_MOD_TABLE_SIZE 0x100 + +#endif diff --git a/arch/x86/include/asm/platform_mrfld_audio.h b/arch/x86/include/asm/platform_mrfld_audio.h new file mode 100644 index 000000000000..e2b1749d3cdf --- /dev/null +++ b/arch/x86/include/asm/platform_mrfld_audio.h @@ -0,0 +1,25 @@ +/* + * platform_mrfld_audio.h: MRFLD audio platform data header file + * + * (C) Copyright 2012 Intel Corporation + * Author: Vinod Koul + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ +#ifndef _PLATFORM_MRFLD_AUDIO_H_ +#define _PLATFORM_MRFLD_AUDIO_H_ + +#include + +struct mrfld_audio_platform_data { + const struct soft_platform_id *spid; + int codec_gpio; + int codec_rst; +}; + +extern void __init *merfld_audio_platform_data(void *info) __attribute__((weak)); +extern void __init *merfld_wm8958_audio_platform_data(void *info) __attribute__((weak)); +#endif diff --git a/arch/x86/include/asm/platform_sst.h b/arch/x86/include/asm/platform_sst.h new file mode 100644 index 000000000000..e752ed9d5ab6 --- /dev/null +++ b/arch/x86/include/asm/platform_sst.h @@ -0,0 +1,132 @@ + +/* + * platform_sst.h: sst audio platform data header file + * + * Copyright (C) 2013 Intel Corporation + * Author: Dharageswari R + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#ifndef _PLATFORM_SST_H_ +#define _PLATFORM_SST_H_ + +#include + +#define MAX_NUM_STREAMS_CTP 5 +#define MAX_NUM_STREAMS_MRFLD 25 +#define MAX_NUM_STREAMS MAX_NUM_STREAMS_MRFLD + +#define SST_MAX_SSP_PORTS 4 +#define SST_MAX_DMA 2 + +enum { + SST_SSP_AUDIO = 0, + SST_SSP_MODEM, + SST_SSP_BT, + SST_SSP_FM, +}; + +struct sst_gpio_config { + u32 i2s_rx_alt; + u32 i2s_tx_alt; + u32 i2s_frame; + u32 i2s_clock; + u32 alt_function; +}; + +struct sst_ssp_info { + u32 base_add; + struct sst_gpio_config gpio; + bool gpio_in_use; +}; + +struct sst_info { + u32 iram_start; + u32 iram_end; + bool iram_use; + u32 dram_start; + u32 dram_end; + bool dram_use; + u32 imr_start; + u32 imr_end; + bool imr_use; + u32 mailbox_start; + bool use_elf; + bool lpe_viewpt_rqd; + unsigned int max_streams; + u32 dma_max_len; + u8 num_probes; +}; + +struct sst_ssp_platform_cfg { + u8 ssp_cfg_sst; + u8 port_number; + u8 is_master; + u8 pack_mode; + u8 num_slots_per_frame; + u8 num_bits_per_slot; + u8 active_tx_map; + u8 active_rx_map; + u8 ssp_frame_format; + u8 frame_polarity; + u8 serial_bitrate_clk_mode; + u8 frame_sync_width; + u8 dma_handshake_interface_tx; + u8 dma_handshake_interface_rx; + u8 network_mode; + u8 start_delay; + u32 ssp_base_add; +} __packed; + +struct sst_board_config_data { + struct sst_ssp_platform_cfg ssp_platform_data[SST_MAX_SSP_PORTS]; + u8 active_ssp_ports; + u8 platform_id; + u8 board_id; + u8 ihf_num_chan; + u32 osc_clk_freq; +} __packed; + +struct sst_platform_config_data { + u32 sst_sram_buff_base; + u32 sst_dma_base[SST_MAX_DMA]; +} __packed; + +struct sst_platform_debugfs_data { + u32 ssp_reg_size; + u32 dma_reg_size; + u32 checkpoint_offset; + u32 checkpoint_size; + u8 num_ssp; + u8 num_dma; +}; + +struct sst_ipc_info { + int ipc_offset; + bool use_32bit_ops; + unsigned int mbox_recv_off; +}; + +struct sst_lib_dnld_info { + unsigned int mod_base; + unsigned int mod_end; + unsigned int mod_table_offset; + unsigned int mod_table_size; + bool mod_ddr_dnld; +}; + +struct sst_platform_info { + const struct sst_info *probe_data; + const struct sst_ssp_info *ssp_data; + const struct sst_board_config_data *bdata; + const struct sst_platform_config_data *pdata; + const struct sst_ipc_info *ipc_info; + const struct sst_platform_debugfs_data *debugfs_data; + const struct sst_lib_dnld_info *lib_info; +}; + +#endif diff --git a/arch/x86/include/asm/platform_sst_audio.h b/arch/x86/include/asm/platform_sst_audio.h new file mode 100644 index 000000000000..6333631b14f6 --- /dev/null +++ b/arch/x86/include/asm/platform_sst_audio.h @@ -0,0 +1,161 @@ +/* + * platform_sst_audio.h: sst audio platform data header file + * + * Copyright (C) 2012 Intel Corporation + * Author: Jeeja KP + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ +#ifndef _PLATFORM_SST_AUDIO_H_ +#define _PLATFORM_SST_AUDIO_H_ + +#include + +/* The stream map status is used to dynamically assign + * device-id to a device, for example probe device. If + * a stream map entry is free for a device then the device-id + * for that device will be popluated when the device is + * opened and then the status set to IN_USE. When device + * is closed, the strm map status is set to FREE again. + */ +enum sst_strm_map_status { + SST_DEV_MAP_FREE = 0, + SST_DEV_MAP_IN_USE, +}; + +/* Device IDs for CTP are same as stream IDs */ +enum sst_audio_device_id_ctp { + SST_PCM_OUT0 = 1, + SST_PCM_OUT1 = 2, + SST_COMPRESSED_OUT = 3, + SST_CAPTURE_IN = 4, + SST_PROBE_IN = 5, +}; + +enum sst_audio_task_id_mrfld { + SST_TASK_ID_NONE = 0, + SST_TASK_ID_SBA = 1, + SST_TASK_ID_FBA_UL = 2, + SST_TASK_ID_MEDIA = 3, + SST_TASK_ID_AWARE = 4, + SST_TASK_ID_FBA_DL = 5, + SST_TASK_ID_MAX = SST_TASK_ID_FBA_DL, +}; + +/* Device IDs for Merrifield are Pipe IDs, + * ref: LPE DSP command interface spec v0.75 */ +enum sst_audio_device_id_mrfld { + /* Output pipeline IDs */ + PIPE_ID_OUT_START = 0x0, + PIPE_MODEM_OUT = 0x0, + PIPE_BT_OUT = 0x1, + PIPE_CODEC_OUT0 = 0x2, + PIPE_CODEC_OUT1 = 0x3, + PIPE_SPROT_LOOP_OUT = 0x4, + PIPE_MEDIA_LOOP1_OUT = 0x5, + PIPE_MEDIA_LOOP2_OUT = 0x6, + PIPE_PROBE_OUT = 0x7, + PIPE_HF_SNS_OUT = 0x8, /* VOCIE_UPLINK_REF2 */ + PIPE_HF_OUT = 0x9, /* VOICE_UPLINK_REF1 */ + PIPE_SPEECH_OUT = 0xA, /* VOICE UPLINK */ + PIPE_RxSPEECH_OUT = 0xB, /* VOICE_DOWNLINK */ + PIPE_VOIP_OUT = 0xC, + PIPE_PCM0_OUT = 0xD, + PIPE_PCM1_OUT = 0xE, + PIPE_PCM2_OUT = 0xF, + PIPE_AWARE_OUT = 0x10, + PIPE_VAD_OUT = 0x11, + PIPE_MEDIA0_OUT = 0x12, + PIPE_MEDIA1_OUT = 0x13, + PIPE_FM_OUT = 0x14, + PIPE_PROBE1_OUT = 0x15, + PIPE_PROBE2_OUT = 0x16, + PIPE_PROBE3_OUT = 0x17, + PIPE_PROBE4_OUT = 0x18, + PIPE_PROBE5_OUT = 0x19, + PIPE_PROBE6_OUT = 0x1A, + PIPE_PROBE7_OUT = 0x1B, + PIPE_PROBE8_OUT = 0x1C, +/* Input Pipeline IDs */ + PIPE_ID_IN_START = 0x80, + PIPE_MODEM_IN = 0x80, + PIPE_BT_IN = 0x81, + PIPE_CODEC_IN0 = 0x82, + PIPE_CODEC_IN1 = 0x83, + PIPE_SPROT_LOOP_IN = 0x84, + PIPE_MEDIA_LOOP1_IN = 0x85, + PIPE_MEDIA_LOOP2_IN = 0x86, + PIPE_PROBE_IN = 0x87, + PIPE_SIDETONE_IN = 0x88, + PIPE_TxSPEECH_IN = 0x89, + PIPE_SPEECH_IN = 0x8A, + PIPE_TONE_IN = 0x8B, + PIPE_VOIP_IN = 0x8C, + PIPE_PCM0_IN = 0x8D, + PIPE_PCM1_IN = 0x8E, + PIPE_MEDIA0_IN = 0x8F, + PIPE_MEDIA1_IN = 0x90, + PIPE_MEDIA2_IN = 0x91, + PIPE_FM_IN = 0x92, + PIPE_PROBE1_IN = 0x93, + PIPE_PROBE2_IN = 0x94, + PIPE_PROBE3_IN = 0x95, + PIPE_PROBE4_IN = 0x96, + PIPE_PROBE5_IN = 0x97, + PIPE_PROBE6_IN = 0x98, + PIPE_PROBE7_IN = 0x99, + PIPE_PROBE8_IN = 0x9A, + PIPE_MEDIA3_IN = 0x9C, + PIPE_LOW_PCM0_IN = 0x9D, + PIPE_RSVD = 0xFF, +}; + +/* The stream map for each platform consists of an array of the below + * stream map structure. The array index is used as the static stream-id + * associated with a device and (dev_num,subdev_num,direction) tuple match + * gives the device_id for the device. + */ +struct sst_dev_stream_map { + u8 dev_num; + u8 subdev_num; + u8 direction; + u8 device_id; + u8 task_id; + u8 status; +}; + +#define MAX_DESCRIPTOR_SIZE 172 + +struct sst_dev_effects_map { + char uuid[16]; + u16 algo_id; + char descriptor[MAX_DESCRIPTOR_SIZE]; +}; + +struct sst_dev_effects_resource_map { + char uuid[16]; + unsigned int flags; + u16 cpuLoad; + u16 memoryUsage; +}; + +struct sst_dev_effects { + struct sst_dev_effects_map *effs_map; + struct sst_dev_effects_resource_map *effs_res_map; + unsigned int effs_num_map; +}; + +struct sst_platform_data { + /* Intel software platform id*/ + const struct soft_platform_id *spid; + struct sst_dev_stream_map *pdev_strm_map; + struct sst_dev_effects pdev_effs; + unsigned int strm_map_size; +}; + +int add_sst_platform_device(void); +#endif + diff --git a/arch/x86/include/asm/xor_avx.h b/arch/x86/include/asm/xor_avx.h index 492b29802f57..7ea79c5fa1f2 100644 --- a/arch/x86/include/asm/xor_avx.h +++ b/arch/x86/include/asm/xor_avx.h @@ -167,12 +167,12 @@ static struct xor_block_template xor_block_avx = { #define AVX_XOR_SPEED \ do { \ - if (cpu_has_avx && cpu_has_osxsave) \ + if (cpu_has_avx) \ xor_speed(&xor_block_avx); \ } while (0) #define AVX_SELECT(FASTEST) \ - (cpu_has_avx && cpu_has_osxsave ? &xor_block_avx : FASTEST) + (cpu_has_avx ? &xor_block_avx : FASTEST) #else diff --git a/arch/x86/kernel/amd_nb.c b/arch/x86/kernel/amd_nb.c index 59554dca96ec..3048ded1b598 100644 --- a/arch/x86/kernel/amd_nb.c +++ b/arch/x86/kernel/amd_nb.c @@ -20,7 +20,6 @@ const struct pci_device_id amd_nb_misc_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F3) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) }, {} }; @@ -28,7 +27,6 @@ EXPORT_SYMBOL(amd_nb_misc_ids); static const struct pci_device_id amd_nb_link_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F4) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_NB_F4) }, {} }; @@ -83,19 +81,12 @@ int amd_cache_northbridges(void) next_northbridge(misc, amd_nb_misc_ids); node_to_amd_nb(i)->link = link = next_northbridge(link, amd_nb_link_ids); - } + } - /* GART present only on Fam15h upto model 0fh */ if (boot_cpu_data.x86 == 0xf || boot_cpu_data.x86 == 0x10 || - (boot_cpu_data.x86 == 0x15 && boot_cpu_data.x86_model < 0x10)) + boot_cpu_data.x86 == 0x15) amd_northbridges.flags |= AMD_NB_GART; - /* - * Check for L3 cache presence. - */ - if (!cpuid_edx(0x80000006)) - return 0; - /* * Some CPU families support L3 Cache Index Disable. There are some * limitations because of E382 and E388 on family 0x10. diff --git a/arch/x86/kernel/cpu/vmware.c b/arch/x86/kernel/cpu/vmware.c index 4c77d69e7a06..03a36321ec54 100644 --- a/arch/x86/kernel/cpu/vmware.c +++ b/arch/x86/kernel/cpu/vmware.c @@ -21,7 +21,6 @@ * */ -#include #include #include #include diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c index 174da5fc5a7b..d32abeabbda5 100644 --- a/arch/x86/kernel/e820.c +++ b/arch/x86/kernel/e820.c @@ -658,18 +658,15 @@ __init void e820_setup_gap(void) * boot_params.e820_map, others are passed via SETUP_E820_EXT node of * linked list of struct setup_data, which is parsed here. */ -void __init parse_e820_ext(u64 phys_addr, u32 data_len) +void __init parse_e820_ext(struct setup_data *sdata) { int entries; struct e820entry *extmap; - struct setup_data *sdata; - sdata = early_memremap(phys_addr, data_len); entries = sdata->len / sizeof(struct e820entry); extmap = (struct e820entry *)(sdata->data); __append_e820_map(extmap, entries); sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &e820.nr_map); - early_iounmap(sdata, data_len); printk(KERN_INFO "e820: extended physical RAM map:\n"); e820_print_map("extended"); } diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index 90fd1195f276..76fa1e9a2b39 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -447,22 +447,6 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Precision M6600"), }, }, - { /* Handle problems with rebooting on the Dell PowerEdge C6100. */ - .callback = set_pci_reboot, - .ident = "Dell PowerEdge C6100", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "C6100"), - }, - }, - { /* Some C6100 machines were shipped with vendor being 'Dell'. */ - .callback = set_pci_reboot, - .ident = "Dell PowerEdge C6100", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell"), - DMI_MATCH(DMI_PRODUCT_NAME, "C6100"), - }, - }, { } }; diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 91964c663f0d..56f7fcfe7fa2 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -426,23 +426,25 @@ static void __init reserve_initrd(void) static void __init parse_setup_data(void) { struct setup_data *data; - u64 pa_data, pa_next; + u64 pa_data; pa_data = boot_params.hdr.setup_data; while (pa_data) { - u32 data_len, map_len, data_type; + u32 data_len, map_len; map_len = max(PAGE_SIZE - (pa_data & ~PAGE_MASK), (u64)sizeof(struct setup_data)); data = early_memremap(pa_data, map_len); data_len = data->len + sizeof(struct setup_data); - data_type = data->type; - pa_next = data->next; - early_iounmap(data, map_len); + if (data_len > map_len) { + early_iounmap(data, map_len); + data = early_memremap(pa_data, data_len); + map_len = data_len; + } - switch (data_type) { + switch (data->type) { case SETUP_E820_EXT: - parse_e820_ext(pa_data, data_len); + parse_e820_ext(data); break; case SETUP_DTB: add_dtb(pa_data); @@ -450,7 +452,8 @@ static void __init parse_setup_data(void) default: break; } - pa_data = pa_next; + pa_data = data->next; + early_iounmap(data, map_len); } } diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c index 087ab2af381a..69562992e457 100644 --- a/arch/x86/kernel/signal.c +++ b/arch/x86/kernel/signal.c @@ -364,7 +364,7 @@ static int __setup_rt_frame(int sig, struct ksignal *ksig, else put_user_ex(0, &frame->uc.uc_flags); put_user_ex(0, &frame->uc.uc_link); - save_altstack_ex(&frame->uc.uc_stack, regs->sp); + err |= __save_altstack(&frame->uc.uc_stack, regs->sp); /* Set up to return from userspace. */ restorer = VDSO32_SYMBOL(current->mm->context.vdso, rt_sigreturn); @@ -429,7 +429,7 @@ static int __setup_rt_frame(int sig, struct ksignal *ksig, else put_user_ex(0, &frame->uc.uc_flags); put_user_ex(0, &frame->uc.uc_link); - save_altstack_ex(&frame->uc.uc_stack, regs->sp); + err |= __save_altstack(&frame->uc.uc_stack, regs->sp); /* Set up to return from userspace. If provided, use a stub already in userspace. */ @@ -496,7 +496,7 @@ static int x32_setup_rt_frame(struct ksignal *ksig, else put_user_ex(0, &frame->uc.uc_flags); put_user_ex(0, &frame->uc.uc_link); - compat_save_altstack_ex(&frame->uc.uc_stack, regs->sp); + err |= __compat_save_altstack(&frame->uc.uc_stack, regs->sp); put_user_ex(0, &frame->uc.uc__pad0); if (ksig->ka.sa.sa_flags & SA_RESTORER) { diff --git a/arch/x86/kernel/sys_x86_64.c b/arch/x86/kernel/sys_x86_64.c index 30277e27431a..48f8375e4c6b 100644 --- a/arch/x86/kernel/sys_x86_64.c +++ b/arch/x86/kernel/sys_x86_64.c @@ -101,7 +101,7 @@ static void find_start_end(unsigned long flags, unsigned long *begin, *begin = new_begin; } } else { - *begin = current->mm->mmap_legacy_base; + *begin = mmap_legacy_base(); *end = TASK_SIZE; } } diff --git a/arch/x86/lib/csum-wrappers_64.c b/arch/x86/lib/csum-wrappers_64.c index 7609e0e421ec..25b7ae8d058a 100644 --- a/arch/x86/lib/csum-wrappers_64.c +++ b/arch/x86/lib/csum-wrappers_64.c @@ -6,7 +6,6 @@ */ #include #include -#include /** * csum_partial_copy_from_user - Copy and checksum from user space. @@ -53,10 +52,8 @@ csum_partial_copy_from_user(const void __user *src, void *dst, len -= 2; } } - stac(); isum = csum_partial_copy_generic((__force const void *)src, dst, len, isum, errp, NULL); - clac(); if (unlikely(*errp)) goto out_err; @@ -85,8 +82,6 @@ __wsum csum_partial_copy_to_user(const void *src, void __user *dst, int len, __wsum isum, int *errp) { - __wsum ret; - might_sleep(); if (unlikely(!access_ok(VERIFY_WRITE, dst, len))) { @@ -110,11 +105,8 @@ csum_partial_copy_to_user(const void *src, void __user *dst, } *errp = 0; - stac(); - ret = csum_partial_copy_generic(src, (void __force *)dst, - len, isum, NULL, errp); - clac(); - return ret; + return csum_partial_copy_generic(src, (void __force *)dst, + len, isum, NULL, errp); } EXPORT_SYMBOL(csum_partial_copy_to_user); diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 7a5bf1b76e2f..1f34e9219775 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -78,8 +78,8 @@ __ref void *alloc_low_pages(unsigned int num) return __va(pfn << PAGE_SHIFT); } -/* need 3 4k for initial PMD_SIZE, 3 4k for 0-ISA_END_ADDRESS */ -#define INIT_PGT_BUF_SIZE (6 * PAGE_SIZE) +/* need 4 4k for initial PMD_SIZE, 4k for 0-ISA_END_ADDRESS */ +#define INIT_PGT_BUF_SIZE (5 * PAGE_SIZE) RESERVE_BRK(early_pgt_alloc, INIT_PGT_BUF_SIZE); void __init early_alloc_pgt_buf(void) { diff --git a/arch/x86/mm/mmap.c b/arch/x86/mm/mmap.c index 5c1ae28825cd..c1af32385ab1 100644 --- a/arch/x86/mm/mmap.c +++ b/arch/x86/mm/mmap.c @@ -98,7 +98,7 @@ static unsigned long mmap_base(void) * Bottom-up (legacy) layout on X86_32 did not support randomization, X86_64 * does, but not when emulating X86_32 */ -static unsigned long mmap_legacy_base(void) +unsigned long mmap_legacy_base(void) { if (mmap_is_ia32()) return TASK_UNMAPPED_BASE; @@ -112,14 +112,12 @@ static unsigned long mmap_legacy_base(void) */ void arch_pick_mmap_layout(struct mm_struct *mm) { - mm->mmap_legacy_base = mmap_legacy_base(); - mm->mmap_base = mmap_base(); - if (mmap_is_legacy()) { - mm->mmap_base = mm->mmap_legacy_base; + mm->mmap_base = mmap_legacy_base(); mm->get_unmapped_area = arch_get_unmapped_area; mm->unmap_area = arch_unmap_area; } else { + mm->mmap_base = mmap_base(); mm->get_unmapped_area = arch_get_unmapped_area_topdown; mm->unmap_area = arch_unmap_area_topdown; } diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 91ac654ff4ea..d2fbcedcf6ea 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -910,13 +910,10 @@ void __init efi_enter_virtual_mode(void) for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { md = p; - if (!(md->attribute & EFI_MEMORY_RUNTIME)) { -#ifdef CONFIG_X86_64 - if (md->type != EFI_BOOT_SERVICES_CODE && - md->type != EFI_BOOT_SERVICES_DATA) -#endif - continue; - } + if (!(md->attribute & EFI_MEMORY_RUNTIME) && + md->type != EFI_BOOT_SERVICES_CODE && + md->type != EFI_BOOT_SERVICES_DATA) + continue; size = md->num_pages << EFI_PAGE_SHIFT; end = md->phys_addr + size; diff --git a/arch/x86/platform/intel-mid/board.c b/arch/x86/platform/intel-mid/board.c index eb9d098e889f..0f856fa250e7 100644 --- a/arch/x86/platform/intel-mid/board.c +++ b/arch/x86/platform/intel-mid/board.c @@ -60,6 +60,7 @@ #include "device_libs/platform_soc_thermal.h" #include "device_libs/platform_msic_adc.h" #include "device_libs/platform_bcove_adc.h" +#include #include "device_libs/platform_mrfl_thermal.h" /* @@ -75,6 +76,8 @@ #include "device_libs/platform_bq24261.h" #include "device_libs/platform_pcal9555a.h" +#include "device_libs/platform_wm8994.h" + /* * SPI devices */ @@ -147,14 +150,17 @@ struct devs_id __initconst device_ids[] = { &ipc_device_handler}, {"bcove_thrm", SFI_DEV_TYPE_IPC, 1, &mrfl_thermal_platform_data, &ipc_device_handler}, - + {"wm8994", SFI_DEV_TYPE_I2C, 0, &wm8994_platform_data, NULL}, /* IPC devices */ {"pmic_charger", SFI_DEV_TYPE_IPC, 1, &no_platform_data, NULL}, {"pmic_ccsm", SFI_DEV_TYPE_IPC, 1, &mrfl_pmic_ccsm_platform_data, &ipc_device_handler}, {"i2c_pmic_adap", SFI_DEV_TYPE_IPC, 1, &mrfl_pmic_i2c_platform_data, &ipc_device_handler}, + {"mrfld_wm8958", SFI_DEV_TYPE_IPC, 1, &merfld_wm8958_audio_platform_data, + &ipc_device_handler}, {"soc_thrm", SFI_DEV_TYPE_IPC, 1, &no_platform_data, &soc_thrm_device_handler}, + {"wm8958", SFI_DEV_TYPE_I2C, 0, &wm8994_platform_data, NULL}, {}, }; diff --git a/arch/x86/platform/intel-mid/device_libs/Makefile b/arch/x86/platform/intel-mid/device_libs/Makefile index 750ac8f27ef8..7fc1a261fca7 100644 --- a/arch/x86/platform/intel-mid/device_libs/Makefile +++ b/arch/x86/platform/intel-mid/device_libs/Makefile @@ -1,4 +1,5 @@ # IPC Devices +obj-y += platform_sst_audio.o obj-y += platform_mrfl_regulator.o obj-y += platform_soc_thermal.o obj-$(subst m,y,$(CONFIG_SND_BYT_MACHINE)) += platform_byt_audio.o @@ -36,6 +37,7 @@ obj-$(subst m,y,$(CONFIG_SENSORS_MPU3050)) += platform_mpu3050.o obj-$(subst m,y,$(CONFIG_GPIO_PCA953X)) += platform_tca6416.o obj-$(subst m,y,$(CONFIG_BQ24261_CHARGER)) += platform_bq24261.o obj-$(subst m,y,$(CONFIG_GPIO_PCA953X)) += platform_pcal9555a.o +obj-$(subst m,y,$(CONFIG_SND_SOC_WM8994)) += platform_wm8994.o # SPI Devices obj-$(subst m,y,$(CONFIG_SERIAL_MRST_MAX3110)) += platform_max3111.o obj-$(subst m,y,$(CONFIG_SPI_SPIDEV)) += platform_spidev.o diff --git a/arch/x86/platform/intel-mid/device_libs/pci/platform_sst_pci.c b/arch/x86/platform/intel-mid/device_libs/pci/platform_sst_pci.c new file mode 100644 index 000000000000..8721c3ab72c2 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/pci/platform_sst_pci.c @@ -0,0 +1,229 @@ +/* + * platform_sst_pci.c: SST platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Dharageswari R + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define CTP_SSP_BASE 0xffa23000 +#define CTP_DMA_BASE 0xffaf8000 +#define MRFLD_SSP_BASE 0xff2a0000 +#define MRFLD_DMA_BASE 0xff298000 +#define CTP_MAX_CONFIG_SIZE 500 + +#define SST_CTP_IRAM_START 0 +#define SST_CTP_IRAM_END 0x80000 +#define SST_CTP_DRAM_START 0x400000 +#define SST_CTP_DRAM_END 0x480000 +#define SSP_SIZE 0x1000 +#define DMA_SIZE_CTP 0x1000 +#define DMA_SIZE_MRFLD 0x4000 +#define SST_CHECKPOINT_OFFSET 0x1C00 +#define SST_CHECKPOINT_OFFSET_MRFLD 0x0C00 +#define CHECKPOINT_DUMP_SZ 256 + +#define SST_V1_MAILBOX_RECV 0x800 +#define SST_V2_MAILBOX_RECV 0x400 + +#define MRFLD_FW_LSP_DDR_BASE 0xC5E00000 +#define MRFLD_FW_MOD_END (MRFLD_FW_LSP_DDR_BASE + 0x1FFFFF) +#define MRFLD_FW_MOD_TABLE_OFFSET 0x80000 +#define MRFLD_FW_MOD_TABLE_SIZE 0x100 + +struct sst_platform_info sst_data; + +static struct sst_ssp_info ssp_inf_ctp = { + .base_add = CTP_SSP_BASE, + .gpio = { + .alt_function = LNW_ALT_2, + }, + .gpio_in_use = true, +}; + +static struct sst_ssp_info ssp_inf_mrfld = { + .base_add = MRFLD_SSP_BASE, + .gpio_in_use = false, +}; + +static const struct sst_platform_config_data sst_ctp_pdata = { + .sst_sram_buff_base = 0xfffc0000, + .sst_dma_base[0] = CTP_DMA_BASE, + .sst_dma_base[1] = 0x0, +}; + +static struct sst_platform_config_data sst_mrfld_pdata = { + .sst_dma_base[0] = MRFLD_DMA_BASE, + .sst_dma_base[1] = 0x0, +}; + +static const struct sst_board_config_data sst_ctp_bdata = { + .active_ssp_ports = 4, + .platform_id = 2,/*FIXME: Once the firmware fix is available*/ + .board_id = 1,/*FIXME: Once the firmware fix is available*/ + .ihf_num_chan = 2, + .osc_clk_freq = 19200000, + .ssp_platform_data = { + [SST_SSP_AUDIO] = { + .ssp_cfg_sst = 1, + .port_number = 3, + .is_master = 1, + .pack_mode = 1, + .num_slots_per_frame = 2, + .num_bits_per_slot = 25, + .active_tx_map = 3, + .active_rx_map = 3, + .ssp_frame_format = 3, + .frame_polarity = 0, + .serial_bitrate_clk_mode = 0, + .frame_sync_width = 24, + .dma_handshake_interface_tx = 5, + .dma_handshake_interface_rx = 4, + .ssp_base_add = 0xFFA23000, + }, + [SST_SSP_MODEM] = {0}, + [SST_SSP_BT] = {0}, + [SST_SSP_FM] = {0}, + }, +}; + +static const struct sst_info ctp_sst_info = { + .iram_start = SST_CTP_IRAM_START, + .iram_end = SST_CTP_IRAM_END, + .iram_use = true, + .dram_start = SST_CTP_DRAM_START, + .dram_end = SST_CTP_DRAM_END, + .dram_use = true, + .imr_start = 0, + .imr_end = 0, + .imr_use = false, + .mailbox_start = 0, + .lpe_viewpt_rqd = false, + .use_elf = false, + .max_streams = MAX_NUM_STREAMS_CTP, + .dma_max_len = (SST_MAX_DMA_LEN * 4), + .num_probes = 1, +}; + +static const struct sst_ipc_info ctp_ipc_info = { + .use_32bit_ops = true, + .ipc_offset = 0, + .mbox_recv_off = SST_V1_MAILBOX_RECV, +}; + +static const struct sst_info mrfld_sst_info = { + .iram_start = 0, + .iram_end = 0, + .iram_use = false, + .dram_start = 0, + .dram_end = 0, + .dram_use = false, + .imr_start = 0, + .imr_end = 0, + .imr_use = false, + .mailbox_start = 0, + .use_elf = true, + .lpe_viewpt_rqd = false, + .max_streams = MAX_NUM_STREAMS_MRFLD, + .dma_max_len = SST_MAX_DMA_LEN_MRFLD, + .num_probes = 16, +}; + +static struct sst_platform_debugfs_data ctp_debugfs_data = { + .ssp_reg_size = SSP_SIZE, + .dma_reg_size = DMA_SIZE_CTP, + .num_ssp = 1, + .num_dma = 1, + .checkpoint_offset = SST_CHECKPOINT_OFFSET, + .checkpoint_size = CHECKPOINT_DUMP_SZ, +}; + +static struct sst_platform_debugfs_data mrfld_debugfs_data = { + .ssp_reg_size = SSP_SIZE, + .dma_reg_size = DMA_SIZE_MRFLD, + .num_ssp = 3, + .num_dma = 2, + .checkpoint_offset = SST_CHECKPOINT_OFFSET_MRFLD, + .checkpoint_size = CHECKPOINT_DUMP_SZ, +}; + +static const struct sst_ipc_info mrfld_ipc_info = { + .use_32bit_ops = false, + .ipc_offset = 0, + .mbox_recv_off = SST_V2_MAILBOX_RECV, +}; + +static const struct sst_lib_dnld_info mrfld_lib_dnld_info = { + .mod_base = MRFLD_FW_LSP_DDR_BASE, + .mod_end = MRFLD_FW_MOD_END, + .mod_table_offset = MRFLD_FW_MOD_TABLE_OFFSET, + .mod_table_size = MRFLD_FW_MOD_TABLE_SIZE, + .mod_ddr_dnld = true, +}; + +static int set_ctp_sst_config(struct sst_platform_info *sst_info) +{ + unsigned int conf_len; + + ssp_inf_ctp.gpio.i2s_rx_alt = get_gpio_by_name("gpio_i2s3_rx"); + ssp_inf_ctp.gpio.i2s_tx_alt = get_gpio_by_name("gpio_i2s3_rx"); + ssp_inf_ctp.gpio.i2s_frame = get_gpio_by_name("gpio_i2s3_fs"); + ssp_inf_ctp.gpio.i2s_clock = get_gpio_by_name("gpio_i2s3_clk"); + + sst_info->ssp_data = &ssp_inf_ctp; + conf_len = sizeof(sst_ctp_pdata) + sizeof(sst_ctp_bdata); + if (conf_len > CTP_MAX_CONFIG_SIZE) + return -EINVAL; + sst_info->pdata = &sst_ctp_pdata; + sst_info->bdata = &sst_ctp_bdata; + sst_info->probe_data = &ctp_sst_info; + sst_info->ipc_info = &ctp_ipc_info; + sst_info->debugfs_data = &ctp_debugfs_data; + sst_info->lib_info = NULL; + + return 0; +} + +static void set_mrfld_sst_config(struct sst_platform_info *sst_info) +{ + sst_info->ssp_data = &ssp_inf_mrfld; + sst_info->pdata = &sst_mrfld_pdata; + sst_info->bdata = NULL; + sst_info->probe_data = &mrfld_sst_info; + sst_info->ipc_info = &mrfld_ipc_info; + sst_info->debugfs_data = &mrfld_debugfs_data; + sst_info->lib_info = &mrfld_lib_dnld_info; + + return ; + +} + +static struct sst_platform_info *get_sst_platform_data(struct pci_dev *pdev) +{ + struct sst_platform_info *sst_pinfo = NULL; + + set_mrfld_sst_config(&sst_data); + sst_pinfo = &sst_data; + + return sst_pinfo; +} + +static void sst_pci_early_quirks(struct pci_dev *pci_dev) +{ + pci_dev->dev.platform_data = get_sst_platform_data(pci_dev); +} + +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SST_MRFLD, + sst_pci_early_quirks); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_hsu.c b/arch/x86/platform/intel-mid/device_libs/platform_hsu.c index 293793e6ff8e..f1e3d29cf8b9 100644 --- a/arch/x86/platform/intel-mid/device_libs/platform_hsu.c +++ b/arch/x86/platform/intel-mid/device_libs/platform_hsu.c @@ -51,7 +51,7 @@ hsu_port_pin_cfg hsu_port_pin_cfgs[][hsu_pid_max][hsu_port_max] = { }, [hsu_port1] = { .id = 1, - .name = HSU_GPS_PORT, + .name = HSU_UART1_PORT, .wake_gpio = 130, .rx_gpio = 130, .rx_alt = 1, @@ -64,7 +64,7 @@ hsu_port_pin_cfg hsu_port_pin_cfgs[][hsu_pid_max][hsu_port_max] = { }, [hsu_port2] = { .id = 2, - .name = HSU_DEBUG_PORT, + .name = HSU_UART2_PORT, .wake_gpio = 134, .rx_gpio = 134, .rx_alt = 1, @@ -98,7 +98,7 @@ static struct hsu_port_cfg hsu_port_cfgs[][hsu_port_max] = { .type = gps_port, .hw_ip = hsu_intel, .index = 1, - .name = HSU_GPS_PORT, + .name = HSU_UART1_PORT, .idle = 30, .preamble = 1, .hw_init = intel_mid_hsu_init, @@ -114,7 +114,7 @@ static struct hsu_port_cfg hsu_port_cfgs[][hsu_port_max] = { .type = debug_port, .hw_ip = hsu_intel, .index = 2, - .name = HSU_DEBUG_PORT, + .name = HSU_UART2_PORT, .idle = 5000, .hw_init = intel_mid_hsu_init, .hw_set_alt = intel_mid_hsu_switch, diff --git a/arch/x86/platform/intel-mid/device_libs/platform_hsu.h b/arch/x86/platform/intel-mid/device_libs/platform_hsu.h index 1a0182ca5e37..7bc6eafde25f 100644 --- a/arch/x86/platform/intel-mid/device_libs/platform_hsu.h +++ b/arch/x86/platform/intel-mid/device_libs/platform_hsu.h @@ -13,9 +13,9 @@ #define _PLATFORM_HSU_H_ #define HSU_BT_PORT "hsu_bt_port" -#define HSU_MODEM_PORT "hsu_modem_port" -#define HSU_GPS_PORT "hsu_gps_port" -#define HSU_DEBUG_PORT "hsu_debug_port" +#define HSU_UART0_PORT "hsu_uart0_port" +#define HSU_UART1_PORT "hsu_uart1_port" +#define HSU_UART2_PORT "hsu_uart2_port" enum hsu_core { hsu_pnw, diff --git a/arch/x86/platform/intel-mid/device_libs/platform_mrfld_audio.c b/arch/x86/platform/intel-mid/device_libs/platform_mrfld_audio.c new file mode 100644 index 000000000000..b37818ece99f --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_mrfld_audio.c @@ -0,0 +1,130 @@ +/* + * platform_mrfld_audio.c: MRFLD audio platform data initilization file + * + * (C) Copyright 2012 Intel Corporation + * Author: Dharageswari R + * Vinod Koul + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "platform_msic.h" + +static struct mrfld_audio_platform_data mrfld_audio_pdata; + +void *merfld_audio_platform_data(void *info) +{ + struct platform_device *pdev; + int ret; + + pr_debug("in %s\n", __func__); + + ret = add_sst_platform_device(); + if (ret < 0) { + pr_err("%s failed to sst_platform device\n", __func__); + return NULL; + } + + pdev = platform_device_alloc("hdmi-audio", -1); + if (!pdev) { + pr_err("failed to allocate hdmi-audio platform device\n"); + return NULL; + } + + ret = platform_device_add(pdev); + if (ret) { + pr_err("failed to add hdmi-audio platform device\n"); + platform_device_put(pdev); + return NULL; + } + + /* request the gpios for audio */ + mrfld_audio_pdata.codec_gpio = get_gpio_by_name("audiocodec_int"); + mrfld_audio_pdata.codec_rst = get_gpio_by_name("audiocodec_rst"); + + pdev = platform_device_alloc("mrfld_lm49453", -1); + if (!pdev) { + pr_err("failed to allocate mrfld_lm49453 platform device\n"); + return NULL; + } + + ret = platform_device_add(pdev); + if (ret) { + pr_err("failed to add mrfld_lm49453 platform device\n"); + platform_device_put(pdev); + return NULL; + } + if (platform_device_add_data(pdev, &mrfld_audio_pdata, + sizeof(mrfld_audio_pdata))) { + pr_err("failed to add mrfld_lm49453 platform data\n"); + platform_device_put(pdev); + return NULL; + } + + register_rpmsg_service("rpmsg_msic_mrfld_audio", RPROC_SCU, + RP_MSIC_MRFLD_AUDIO); + + return NULL; +} + +void *merfld_wm8958_audio_platform_data(void *info) +{ + struct platform_device *pdev; + int ret; + + ret = add_sst_platform_device(); + if (ret < 0) { + pr_err("%s failed to sst_platform device\n", __func__); + return NULL; + } + + pdev = platform_device_alloc("hdmi-audio", -1); + if (!pdev) { + pr_err("failed to allocate hdmi-audio platform device\n"); + return NULL; + } + + ret = platform_device_add(pdev); + if (ret) { + pr_err("failed to add hdmi-audio platform device\n"); + platform_device_put(pdev); + return NULL; + } + + pdev = platform_device_alloc("mrfld_wm8958", -1); + if (!pdev) { + pr_err("failed to allocate mrfld_wm8958 platform device\n"); + return NULL; + } + + ret = platform_device_add(pdev); + if (ret) { + pr_err("failed to add mrfld_wm8958 platform device\n"); + platform_device_put(pdev); + return NULL; + } + if (platform_device_add_data(pdev, &mrfld_audio_pdata, + sizeof(mrfld_audio_pdata))) { + pr_err("failed to add mrfld_wm8958 platform data\n"); + platform_device_put(pdev); + return NULL; + } + + register_rpmsg_service("rpmsg_mrfld_wm8958_audio", RPROC_SCU, + RP_MSIC_MRFLD_AUDIO); + + return NULL; +} diff --git a/arch/x86/platform/intel-mid/device_libs/platform_spidev.c b/arch/x86/platform/intel-mid/device_libs/platform_spidev.c index 8eaa92f2f0d2..c6b3f7bcb058 100644 --- a/arch/x86/platform/intel-mid/device_libs/platform_spidev.c +++ b/arch/x86/platform/intel-mid/device_libs/platform_spidev.c @@ -33,7 +33,7 @@ static struct intel_mid_ssp_spi_chip chip = { static void tng_ssp_spi_cs_control(u32 command) { - gpio_set_value(tng_ssp_spi2_FS_gpio, (command == CS_ASSERT) ? 0 : 1); + gpio_set_value(tng_ssp_spi2_FS_gpio, (command != 0) ? 1 : 0); } static void tng_ssp_spi_platform_pinmux(void) diff --git a/arch/x86/platform/intel-mid/device_libs/platform_sst_audio.c b/arch/x86/platform/intel-mid/device_libs/platform_sst_audio.c new file mode 100644 index 000000000000..7270162c0861 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_sst_audio.c @@ -0,0 +1,152 @@ +/* + * platform_sst_libs.c: SST platform data initilization file + * + * Copyright (C) 2012 Intel Corporation + * Author: Jeeja KP + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct sst_platform_data sst_platform_pdata; + +#if IS_BUILTIN(CONFIG_SST_MRFLD_DPCM) +static struct sst_dev_stream_map mrfld_strm_map[] = { + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, /* Reserved, not in use */ + {MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA1_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_DPCM_DB, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA3_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_DPCM_LL, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_LOW_PCM0_IN, SST_TASK_ID_SBA, SST_DEV_MAP_IN_USE}, + {MERR_DPCM_COMPR, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA0_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_DPCM_VOIP, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_VOIP_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_DPCM_PROBE, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_PROBE1_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_DPCM_PROBE, 1, SNDRV_PCM_STREAM_PLAYBACK, PIPE_PROBE2_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_DPCM_PROBE, 2, SNDRV_PCM_STREAM_PLAYBACK, PIPE_PROBE3_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_DPCM_PROBE, 3, SNDRV_PCM_STREAM_PLAYBACK, PIPE_PROBE4_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_DPCM_PROBE, 4, SNDRV_PCM_STREAM_PLAYBACK, PIPE_PROBE5_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_DPCM_PROBE, 5, SNDRV_PCM_STREAM_PLAYBACK, PIPE_PROBE6_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_DPCM_PROBE, 6, SNDRV_PCM_STREAM_PLAYBACK, PIPE_PROBE7_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_DPCM_PROBE, 7, SNDRV_PCM_STREAM_PLAYBACK, PIPE_PROBE8_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_PCM1_OUT, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_DPCM_VOIP, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_VOIP_OUT, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_DPCM_PROBE, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_PROBE1_OUT, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_DPCM_PROBE, 1, SNDRV_PCM_STREAM_CAPTURE, PIPE_PROBE2_OUT, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_DPCM_PROBE, 2, SNDRV_PCM_STREAM_CAPTURE, PIPE_PROBE3_OUT, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_DPCM_PROBE, 3, SNDRV_PCM_STREAM_CAPTURE, PIPE_PROBE4_OUT, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_DPCM_PROBE, 4, SNDRV_PCM_STREAM_CAPTURE, PIPE_PROBE5_OUT, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_DPCM_PROBE, 5, SNDRV_PCM_STREAM_CAPTURE, PIPE_PROBE6_OUT, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_DPCM_PROBE, 6, SNDRV_PCM_STREAM_CAPTURE, PIPE_PROBE7_OUT, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_DPCM_PROBE, 7, SNDRV_PCM_STREAM_CAPTURE, PIPE_PROBE8_OUT, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, +}; +#else +static struct sst_dev_stream_map mrfld_strm_map[] = { + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, /* Reserved, not in use */ + {MERR_SALTBAY_AUDIO, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, + {MERR_SALTBAY_AUDIO, 1, SNDRV_PCM_STREAM_PLAYBACK, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, + {MERR_SALTBAY_AUDIO, 2, SNDRV_PCM_STREAM_PLAYBACK, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, + {MERR_SALTBAY_COMPR, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA0_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_SALTBAY_VOIP, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_VOIP_IN, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_SALTBAY_AUDIO, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_PCM1_OUT, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_SALTBAY_VOIP, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_VOIP_OUT, SST_TASK_ID_MEDIA, SST_DEV_MAP_IN_USE}, + {MERR_SALTBAY_PROBE, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, + {MERR_SALTBAY_PROBE, 1, SNDRV_PCM_STREAM_CAPTURE, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, + {MERR_SALTBAY_PROBE, 2, SNDRV_PCM_STREAM_CAPTURE, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, + {MERR_SALTBAY_PROBE, 3, SNDRV_PCM_STREAM_CAPTURE, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, + {MERR_SALTBAY_PROBE, 4, SNDRV_PCM_STREAM_CAPTURE, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, + {MERR_SALTBAY_PROBE, 5, SNDRV_PCM_STREAM_CAPTURE, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, + {MERR_SALTBAY_PROBE, 6, SNDRV_PCM_STREAM_CAPTURE, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, + {MERR_SALTBAY_PROBE, 7, SNDRV_PCM_STREAM_CAPTURE, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, + {MERR_SALTBAY_PROBE, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, + {MERR_SALTBAY_PROBE, 1, SNDRV_PCM_STREAM_PLAYBACK, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, + {MERR_SALTBAY_PROBE, 2, SNDRV_PCM_STREAM_PLAYBACK, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, + {MERR_SALTBAY_PROBE, 3, SNDRV_PCM_STREAM_PLAYBACK, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, + {MERR_SALTBAY_PROBE, 4, SNDRV_PCM_STREAM_PLAYBACK, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, + {MERR_SALTBAY_PROBE, 5, SNDRV_PCM_STREAM_PLAYBACK, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, + {MERR_SALTBAY_PROBE, 6, SNDRV_PCM_STREAM_PLAYBACK, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, + {MERR_SALTBAY_PROBE, 7, SNDRV_PCM_STREAM_PLAYBACK, PIPE_RSVD, SST_TASK_ID_MEDIA, SST_DEV_MAP_FREE}, + {MERR_SALTBAY_AWARE, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_AWARE_OUT, SST_TASK_ID_AWARE, SST_DEV_MAP_IN_USE}, + {MERR_SALTBAY_VAD, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_VAD_OUT, SST_TASK_ID_AWARE, SST_DEV_MAP_IN_USE}, +}; +#endif + +#define EQ_EFFECT_ALGO_ID 0x99 +static struct sst_dev_effects_map mrfld_effs_map[] = { + { + {0xc1, 0x47, 0xa2, 0xf7, 0x7b, 0x1a, 0xe0, 0x11, 0x0d, 0xbb, 0x2a, 0x30, 0xdf, 0xd7, 0x20, 0x45},/* uuid */ + EQ_EFFECT_ALGO_ID, /* algo id */ + {0x00, 0x43, 0xed, 0x0b, 0xd6, 0xdd, 0xdb, 0x11, 0x34, 0x8f, 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b, /* descriptor */ + 0xc1, 0x47, 0xa2, 0xf7, 0x7b, 0x1a, 0xe0, 0x11, 0x0d, 0xbb, 0x2a, 0x30, 0xdf, 0xd7, 0x20, 0x45, + 0x12, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x45, 0x71, 0x75, 0x61, + 0x6c, 0x69, 0x7a, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x74, 0x65, + 0x6c, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, + } +}; + +static struct sst_dev_effects_resource_map mrfld_effs_res_map[] = { + { + {0xc1, 0x47, 0xa2, 0xf7, 0x7b, 0x1a, 0xe0, 0x11, 0x0d, 0xbb, 0x2a, 0x30, 0xdf, 0xd7, 0x20, 0x45}, /* uuid */ + 0x50, /* Flags */ + 0x00, /* Cpu load */ + 0x01, /* Memory Usage */ + } +}; + +static void set_mrfld_platform_config(void) +{ + sst_platform_pdata.pdev_strm_map = mrfld_strm_map; + sst_platform_pdata.strm_map_size = ARRAY_SIZE(mrfld_strm_map); + sst_platform_pdata.pdev_effs.effs_map = mrfld_effs_map; + sst_platform_pdata.pdev_effs.effs_res_map = mrfld_effs_res_map; + sst_platform_pdata.pdev_effs.effs_num_map = ARRAY_SIZE(mrfld_effs_map); +} + +static void populate_platform_data(void) +{ + set_mrfld_platform_config(); +} + +int add_sst_platform_device(void) +{ + struct platform_device *pdev = NULL; + int ret; + + populate_platform_data(); + + pdev = platform_device_alloc("sst-platform", -1); + if (!pdev) { + pr_err("failed to allocate audio platform device\n"); + return -EINVAL; + } + + ret = platform_device_add_data(pdev, &sst_platform_pdata, + sizeof(sst_platform_pdata)); + if (ret) { + pr_err("failed to add sst platform data\n"); + platform_device_put(pdev); + return -EINVAL; + } + ret = platform_device_add(pdev); + if (ret) { + pr_err("failed to add audio platform device\n"); + platform_device_put(pdev); + return -EINVAL; + } + return ret; +} diff --git a/arch/x86/platform/intel-mid/device_libs/platform_wm8994.c b/arch/x86/platform/intel-mid/device_libs/platform_wm8994.c new file mode 100644 index 000000000000..82ada450bd01 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_wm8994.c @@ -0,0 +1,221 @@ +/* + * platform_wm8994.c: wm8994 platform data initilization file + * + * (C) Copyright 2013 Intel 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 + * of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "platform_wm8994.h" + +/***********WM89941 REGUATOR platform data*************/ +static struct regulator_consumer_supply vwm89941_consumer[] = { + REGULATOR_SUPPLY("DBVDD", "1-001a"), + REGULATOR_SUPPLY("DBVDD1", "1-001a"), + REGULATOR_SUPPLY("DBVDD2", "1-001a"), + REGULATOR_SUPPLY("DBVDD3", "1-001a"), + REGULATOR_SUPPLY("AVDD2", "1-001a"), + REGULATOR_SUPPLY("CPVDD", "1-001a"), +}; + +static struct regulator_init_data vwm89941_data = { + .constraints = { + .always_on = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(vwm89941_consumer), + .consumer_supplies = vwm89941_consumer, +}; + +static struct fixed_voltage_config vwm89941_config = { + .supply_name = "VCC_1.8V_PDA", + .microvolts = 1800000, + .gpio = -EINVAL, + .init_data = &vwm89941_data, +}; + +static struct platform_device vwm89941_device = { + .name = "reg-fixed-voltage", + .id = 0, + .dev = { + .platform_data = &vwm89941_config, + }, +}; + +/***********WM89942 REGUATOR platform data*************/ +static struct regulator_consumer_supply vwm89942_consumer[] = { + REGULATOR_SUPPLY("SPKVDD1", "1-001a"), + REGULATOR_SUPPLY("SPKVDD2", "1-001a"), +}; + +static struct regulator_init_data vwm89942_data = { + .constraints = { + .always_on = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(vwm89942_consumer), + .consumer_supplies = vwm89942_consumer, +}; + +static struct fixed_voltage_config vwm89942_config = { + .supply_name = "V_BAT", + .microvolts = 3700000, + .gpio = -EINVAL, + .init_data = &vwm89942_data, +}; + +static struct platform_device vwm89942_device = { + .name = "reg-fixed-voltage", + .id = 1, + .dev = { + .platform_data = &vwm89942_config, + }, +}; + +static struct platform_device wm8994_ldo1_device; +static struct platform_device wm8994_ldo2_device; +static struct platform_device *wm1811a_reg_devices[] __initdata = { + &vwm89941_device, + &vwm89942_device, + &wm8994_ldo1_device, + &wm8994_ldo2_device +}; + +static struct platform_device *wm8958_reg_devices[] __initdata = { + &vwm89941_device, + &vwm89942_device +}; + +static struct regulator_consumer_supply wm8994_avdd1_supply = + REGULATOR_SUPPLY("AVDD1", "1-001a"); + +static struct regulator_consumer_supply wm8994_dcvdd_supply = + REGULATOR_SUPPLY("DCVDD", "1-001a"); + +static struct regulator_init_data wm8994_ldo1_data = { + .constraints = { + .always_on = 1, + .name = "AVDD1_3.0V", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = 1, + .consumer_supplies = &wm8994_avdd1_supply, +}; + +static struct fixed_voltage_config wm8994_ldo1_config = { + .supply_name = "V_BAT_X", + .microvolts = 3700000, + .gpio = -EINVAL, + .init_data = &wm8994_ldo1_data, +}; + +static struct platform_device wm8994_ldo1_device = { + .name = "reg-fixed-voltage", + .id = 2, + .dev = { + .platform_data = &wm8994_ldo1_config, + }, +}; + + +static struct regulator_init_data wm8994_ldo2_data = { + .constraints = { + .always_on = 1, + .name = "DCVDD_1.0V", + }, + .num_consumer_supplies = 1, + .consumer_supplies = &wm8994_dcvdd_supply, +}; + +static struct fixed_voltage_config wm8994_ldo2_config = { + .supply_name = "V_BAT_Y", + .microvolts = 3700000, + .gpio = -EINVAL, + .init_data = &wm8994_ldo2_data, +}; + +static struct platform_device wm8994_ldo2_device = { + .name = "reg-fixed-voltage", + .id = 3, + .dev = { + .platform_data = &wm8994_ldo2_config, + }, +}; + +static struct wm8958_custom_config custom_config = { + .format = 6, + .rate = 48000, + .channels = 2, +}; + +static struct wm8994_pdata wm8994_pdata = { + /* configure gpio1 function: 0x0001(Logic level input/output) */ + .gpio_defaults[0] = 0x0003, + .irq_flags = IRQF_TRIGGER_RISING | IRQF_ONESHOT, + /* FIXME: Below are 1811A specfic, we need to use SPID for these */ + + /* configure gpio3/4/5/7 function for AIF2 voice */ + .gpio_defaults[2] = 0x8100, + .gpio_defaults[3] = 0x8100, + .gpio_defaults[4] = 0x8100, + .gpio_defaults[6] = 0x0100, + /* configure gpio8/9/10/11 function for AIF3 BT */ + /* gpio7 is codec intr pin for GV M2 */ + .gpio_defaults[7] = 0x0003, + .gpio_defaults[8] = 0x0105, + .gpio_defaults[9] = 0x0100, + .gpio_defaults[10] = 0x0100, + .ldo[0] = { 0, &wm8994_ldo1_data }, /* set actual value at wm8994_platform_data() */ + .ldo[1] = { 0, &wm8994_ldo2_data }, + .ldo_ena_always_driven = 1, + + .mic_id_delay = 300, /*300ms delay*/ + .micdet_delay = 500, + .micb_en_delay = 5000, /* Keeps MICBIAS2 high for 5sec during jack insertion/removal */ + + .custom_cfg = &custom_config, +}; + +static int wm8994_get_irq_data(struct wm8994_pdata *pdata, + struct i2c_board_info *i2c_info, char *name) +{ + int codec_gpio; + + /* alek tells me that since driver is registering a new chip + * irq we need to give it a base which is unused so put + * 256+192 here */ + pdata->irq_base = (256 + 192); + codec_gpio = get_gpio_by_name(name); + if (codec_gpio < 0) { + pr_err("%s failed for : %d\n", __func__, codec_gpio); + return -EINVAL; + } + i2c_info->irq = codec_gpio + INTEL_MID_IRQ_OFFSET; + return codec_gpio; +} + +void __init *wm8994_platform_data(void *info) +{ + struct i2c_board_info *i2c_info = (struct i2c_board_info *)info; + int irq = 0; + + platform_add_devices(wm8958_reg_devices, + ARRAY_SIZE(wm8958_reg_devices)); + + irq = wm8994_get_irq_data(&wm8994_pdata, i2c_info, + "audiocodec_int"); + if (irq < 0) + return NULL; + + return &wm8994_pdata; +} diff --git a/arch/x86/platform/intel-mid/device_libs/platform_wm8994.h b/arch/x86/platform/intel-mid/device_libs/platform_wm8994.h new file mode 100644 index 000000000000..5abead76b288 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_wm8994.h @@ -0,0 +1,5 @@ +#ifndef _PLATFORM_WM8994_H_ +#define _PLATFORM_WM8994_H_ + +extern void *wm8994_platform_data(void *info) __attribute__((weak)); +#endif diff --git a/arch/x86/platform/intel-mid/intel_mid_sfi.c b/arch/x86/platform/intel-mid/intel_mid_sfi.c index 41f98c4c3fc7..f73b84ee849b 100644 --- a/arch/x86/platform/intel-mid/intel_mid_sfi.c +++ b/arch/x86/platform/intel-mid/intel_mid_sfi.c @@ -316,6 +316,26 @@ void intel_scu_devices_destroy(void) } EXPORT_SYMBOL_GPL(intel_scu_devices_destroy); +static struct platform_device *psh_ipc; +void intel_psh_devices_create(void) +{ + psh_ipc = platform_device_alloc("intel_psh_ipc", 0); + if (psh_ipc == NULL) { + pr_err("out of memory for platform device psh_ipc.\n"); + return; + } + + platform_device_add(psh_ipc); +} +EXPORT_SYMBOL_GPL(intel_psh_devices_create); + +void intel_psh_devices_destroy(void) +{ + if (psh_ipc) + platform_device_del(psh_ipc); +} +EXPORT_SYMBOL_GPL(intel_psh_devices_destroy); + void __init install_irq_resource(struct platform_device *pdev, int irq) { /* Single threaded */ diff --git a/arch/x86/xen/setup.c b/arch/x86/xen/setup.c index 0a9fb7a0b452..94eac5c85cdc 100644 --- a/arch/x86/xen/setup.c +++ b/arch/x86/xen/setup.c @@ -313,17 +313,6 @@ static void xen_align_and_add_e820_region(u64 start, u64 size, int type) e820_add_region(start, end - start, type); } -void xen_ignore_unusable(struct e820entry *list, size_t map_size) -{ - struct e820entry *entry; - unsigned int i; - - for (i = 0, entry = list; i < map_size; i++, entry++) { - if (entry->type == E820_UNUSABLE) - entry->type = E820_RAM; - } -} - /** * machine_specific_memory_setup - Hook for machine specific memory setup. **/ @@ -364,17 +353,6 @@ char * __init xen_memory_setup(void) } BUG_ON(rc); - /* - * Xen won't allow a 1:1 mapping to be created to UNUSABLE - * regions, so if we're using the machine memory map leave the - * region as RAM as it is in the pseudo-physical map. - * - * UNUSABLE regions in domUs are not handled and will need - * a patch in the future. - */ - if (xen_initial_domain()) - xen_ignore_unusable(map, memmap.nr_entries); - /* Make sure the Xen-supplied memory map is well-ordered. */ sanitize_e820_map(map, memmap.nr_entries, &memmap.nr_entries); diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c index a1e58e19d0cc..d99cae8147d1 100644 --- a/arch/x86/xen/smp.c +++ b/arch/x86/xen/smp.c @@ -667,15 +667,8 @@ static void __init xen_hvm_smp_prepare_cpus(unsigned int max_cpus) static int __cpuinit xen_hvm_cpu_up(unsigned int cpu, struct task_struct *tidle) { int rc; - /* - * xen_smp_intr_init() needs to run before native_cpu_up() - * so that IPI vectors are set up on the booting CPU before - * it is marked online in native_cpu_up(). - */ - rc = xen_smp_intr_init(cpu); - WARN_ON(rc); - if (!rc) - rc = native_cpu_up(cpu, tidle); + rc = native_cpu_up(cpu, tidle); + WARN_ON (xen_smp_intr_init(cpu)); return rc; } diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index c410752c5c65..d5bbdcfd0dab 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -1803,7 +1803,7 @@ static u64 cfqg_prfill_avg_queue_size(struct seq_file *sf, if (samples) { v = blkg_stat_read(&cfqg->stats.avg_queue_size_sum); - v = div64_u64(v, samples); + do_div(v, samples); } __blkg_prfill_u64(sf, pd, v); return 0; diff --git a/crypto/Kconfig b/crypto/Kconfig index 130275a88d96..bf8148e74e73 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -21,14 +21,6 @@ if CRYPTO comment "Crypto core or helper" -config CRYPTODEV - tristate "cryptodev module support" - help - This is a /dev/crypto device driver, equivalent to those in OpenBSD or - FreeBSD. The main idea is to access of existing ciphers in kernel space - from userspace, thus enabling re-use of a hardware implementation of a - cipher. - config CRYPTO_FIPS bool "FIPS 200 compliance" depends on CRYPTO_ANSI_CPRNG && !CRYPTO_MANAGER_DISABLE_TESTS diff --git a/crypto/Makefile b/crypto/Makefile index b72f5680595d..a8e9b0fefbe9 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -101,5 +101,3 @@ obj-$(CONFIG_CRYPTO_USER_API_SKCIPHER) += algif_skcipher.o obj-$(CONFIG_XOR_BLOCKS) += xor.o obj-$(CONFIG_ASYNC_CORE) += async_tx/ obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys/ -obj-$(CONFIG_CRYPTODEV) += cryptodev/ - diff --git a/crypto/api.c b/crypto/api.c index 37c4c7213de0..3b6180336d3d 100644 --- a/crypto/api.c +++ b/crypto/api.c @@ -34,8 +34,6 @@ EXPORT_SYMBOL_GPL(crypto_alg_sem); BLOCKING_NOTIFIER_HEAD(crypto_chain); EXPORT_SYMBOL_GPL(crypto_chain); -static struct crypto_alg *crypto_larval_wait(struct crypto_alg *alg); - struct crypto_alg *crypto_mod_get(struct crypto_alg *alg) { return try_module_get(alg->cra_module) ? crypto_alg_get(alg) : NULL; @@ -146,11 +144,8 @@ static struct crypto_alg *crypto_larval_add(const char *name, u32 type, } up_write(&crypto_alg_sem); - if (alg != &larval->alg) { + if (alg != &larval->alg) kfree(larval); - if (crypto_is_larval(alg)) - alg = crypto_larval_wait(alg); - } return alg; } diff --git a/drivers/acpi/acpi_ipmi.c b/drivers/acpi/acpi_ipmi.c index a6977e12d574..f40acef80269 100644 --- a/drivers/acpi/acpi_ipmi.c +++ b/drivers/acpi/acpi_ipmi.c @@ -39,7 +39,6 @@ #include #include #include -#include MODULE_AUTHOR("Zhao Yakui"); MODULE_DESCRIPTION("ACPI IPMI Opregion driver"); @@ -58,7 +57,7 @@ struct acpi_ipmi_device { struct list_head head; /* the IPMI request message list */ struct list_head tx_msg_list; - spinlock_t tx_msg_lock; + struct mutex tx_msg_lock; acpi_handle handle; struct pnp_dev *pnp_dev; ipmi_user_t user_interface; @@ -148,7 +147,6 @@ static void acpi_format_ipmi_msg(struct acpi_ipmi_msg *tx_msg, struct kernel_ipmi_msg *msg; struct acpi_ipmi_buffer *buffer; struct acpi_ipmi_device *device; - unsigned long flags; msg = &tx_msg->tx_message; /* @@ -179,10 +177,10 @@ static void acpi_format_ipmi_msg(struct acpi_ipmi_msg *tx_msg, /* Get the msgid */ device = tx_msg->device; - spin_lock_irqsave(&device->tx_msg_lock, flags); + mutex_lock(&device->tx_msg_lock); device->curr_msgid++; tx_msg->tx_msgid = device->curr_msgid; - spin_unlock_irqrestore(&device->tx_msg_lock, flags); + mutex_unlock(&device->tx_msg_lock); } static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg, @@ -244,7 +242,6 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) int msg_found = 0; struct acpi_ipmi_msg *tx_msg; struct pnp_dev *pnp_dev = ipmi_device->pnp_dev; - unsigned long flags; if (msg->user != ipmi_device->user_interface) { dev_warn(&pnp_dev->dev, "Unexpected response is returned. " @@ -253,7 +250,7 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) ipmi_free_recv_msg(msg); return; } - spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags); + mutex_lock(&ipmi_device->tx_msg_lock); list_for_each_entry(tx_msg, &ipmi_device->tx_msg_list, head) { if (msg->msgid == tx_msg->tx_msgid) { msg_found = 1; @@ -261,7 +258,7 @@ static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) } } - spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags); + mutex_unlock(&ipmi_device->tx_msg_lock); if (!msg_found) { dev_warn(&pnp_dev->dev, "Unexpected response (msg id %ld) is " "returned.\n", msg->msgid); @@ -381,7 +378,6 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address, struct acpi_ipmi_device *ipmi_device = handler_context; int err, rem_time; acpi_status status; - unsigned long flags; /* * IPMI opregion message. * IPMI message is firstly written to the BMC and system software @@ -399,9 +395,9 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address, return AE_NO_MEMORY; acpi_format_ipmi_msg(tx_msg, address, value); - spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags); + mutex_lock(&ipmi_device->tx_msg_lock); list_add_tail(&tx_msg->head, &ipmi_device->tx_msg_list); - spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags); + mutex_unlock(&ipmi_device->tx_msg_lock); err = ipmi_request_settime(ipmi_device->user_interface, &tx_msg->addr, tx_msg->tx_msgid, @@ -417,9 +413,9 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address, status = AE_OK; end_label: - spin_lock_irqsave(&ipmi_device->tx_msg_lock, flags); + mutex_lock(&ipmi_device->tx_msg_lock); list_del(&tx_msg->head); - spin_unlock_irqrestore(&ipmi_device->tx_msg_lock, flags); + mutex_unlock(&ipmi_device->tx_msg_lock); kfree(tx_msg); return status; } @@ -461,7 +457,7 @@ static void acpi_add_ipmi_device(struct acpi_ipmi_device *ipmi_device) INIT_LIST_HEAD(&ipmi_device->head); - spin_lock_init(&ipmi_device->tx_msg_lock); + mutex_init(&ipmi_device->tx_msg_lock); INIT_LIST_HEAD(&ipmi_device->tx_msg_list); ipmi_install_space_handler(ipmi_device); diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 7c451cb26254..cab13f2fc28e 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -155,13 +155,12 @@ static int acpi_lpss_create_device(struct acpi_device *adev, pdata->mmio_size = resource_size(&rentry->res); pdata->mmio_base = ioremap(rentry->res.start, pdata->mmio_size); + pdata->dev_desc = dev_desc; break; } acpi_dev_free_resource_list(&resource_list); - pdata->dev_desc = dev_desc; - if (dev_desc->clk_required) { ret = register_device_clock(adev, pdata); if (ret) { diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 45af90a1ec1b..80403c1a89f8 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -987,10 +987,6 @@ static struct dmi_system_id __initdata ec_dmi_table[] = { ec_skip_dsdt_scan, "HP Folio 13", { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "HP Folio 13"),}, NULL}, - { - ec_validate_ecdt, "ASUS hardware", { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTek Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "L4R"),}, NULL}, {}, }; diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 238412077c83..40a84cc6740c 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -78,99 +78,32 @@ static struct acpi_bus_type *acpi_get_bus_type(struct device *dev) return ret; } -static acpi_status acpi_dev_present(acpi_handle handle, u32 lvl_not_used, - void *not_used, void **ret_p) +static acpi_status do_acpi_find_child(acpi_handle handle, u32 lvl_not_used, + void *addr_p, void **ret_p) { - struct acpi_device *adev = NULL; - - acpi_bus_get_device(handle, &adev); - if (adev) { - *ret_p = handle; - return AE_CTRL_TERMINATE; - } - return AE_OK; -} - -static bool acpi_extra_checks_passed(acpi_handle handle, bool is_bridge) -{ - unsigned long long sta; - acpi_status status; - - status = acpi_bus_get_status_handle(handle, &sta); - if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED)) - return false; - - if (is_bridge) { - void *test = NULL; - - /* Check if this object has at least one child device. */ - acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, - acpi_dev_present, NULL, NULL, &test); - return !!test; - } - return true; -} - -struct find_child_context { - u64 addr; - bool is_bridge; - acpi_handle ret; - bool ret_checked; -}; - -static acpi_status do_find_child(acpi_handle handle, u32 lvl_not_used, - void *data, void **not_used) -{ - struct find_child_context *context = data; unsigned long long addr; acpi_status status; status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &addr); - if (ACPI_FAILURE(status) || addr != context->addr) - return AE_OK; - - if (!context->ret) { - /* This is the first matching object. Save its handle. */ - context->ret = handle; - return AE_OK; - } - /* - * There is more than one matching object with the same _ADR value. - * That really is unexpected, so we are kind of beyond the scope of the - * spec here. We have to choose which one to return, though. - * - * First, check if the previously found object is good enough and return - * its handle if so. Second, check the same for the object that we've - * just found. - */ - if (!context->ret_checked) { - if (acpi_extra_checks_passed(context->ret, context->is_bridge)) - return AE_CTRL_TERMINATE; - else - context->ret_checked = true; - } - if (acpi_extra_checks_passed(handle, context->is_bridge)) { - context->ret = handle; + if (ACPI_SUCCESS(status) && addr == *((u64 *)addr_p)) { + *ret_p = handle; return AE_CTRL_TERMINATE; } return AE_OK; } -acpi_handle acpi_find_child(acpi_handle parent, u64 addr, bool is_bridge) +acpi_handle acpi_get_child(acpi_handle parent, u64 address) { - if (parent) { - struct find_child_context context = { - .addr = addr, - .is_bridge = is_bridge, - }; - - acpi_walk_namespace(ACPI_TYPE_DEVICE, parent, 1, do_find_child, - NULL, &context, NULL); - return context.ret; - } - return NULL; + void *ret = NULL; + + if (!parent) + return NULL; + + acpi_walk_namespace(ACPI_TYPE_DEVICE, parent, 1, NULL, + do_acpi_find_child, &address, &ret); + return (acpi_handle)ret; } -EXPORT_SYMBOL_GPL(acpi_find_child); +EXPORT_SYMBOL(acpi_get_child); static int acpi_bind_one(struct device *dev, acpi_handle handle) { diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c index 20fd337a5731..1c41722bb7e2 100644 --- a/drivers/ata/libata-pmp.c +++ b/drivers/ata/libata-pmp.c @@ -289,24 +289,24 @@ static int sata_pmp_configure(struct ata_device *dev, int print_info) /* Disable sending Early R_OK. * With "cached read" HDD testing and multiple ports busy on a SATA - * host controller, 3x26 PMP will very rarely drop a deferred + * host controller, 3726 PMP will very rarely drop a deferred * R_OK that was intended for the host. Symptom will be all * 5 drives under test will timeout, get reset, and recover. */ - if (vendor == 0x1095 && (devid == 0x3726 || devid == 0x3826)) { + if (vendor == 0x1095 && devid == 0x3726) { u32 reg; err_mask = sata_pmp_read(&ap->link, PMP_GSCR_SII_POL, ®); if (err_mask) { rc = -EIO; - reason = "failed to read Sil3x26 Private Register"; + reason = "failed to read Sil3726 Private Register"; goto fail; } reg &= ~0x1; err_mask = sata_pmp_write(&ap->link, PMP_GSCR_SII_POL, reg); if (err_mask) { rc = -EIO; - reason = "failed to write Sil3x26 Private Register"; + reason = "failed to write Sil3726 Private Register"; goto fail; } } @@ -383,8 +383,8 @@ static void sata_pmp_quirks(struct ata_port *ap) u16 devid = sata_pmp_gscr_devid(gscr); struct ata_link *link; - if (vendor == 0x1095 && (devid == 0x3726 || devid == 0x3826)) { - /* sil3x26 quirks */ + if (vendor == 0x1095 && devid == 0x3726) { + /* sil3726 quirks */ ata_for_each_link(link, ap, EDGE) { /* link reports offline after LPM */ link->flags |= ATA_LFLAG_NO_LPM; diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index 8401061b4040..d40e403e82dd 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -293,7 +293,6 @@ static void fsl_sata_set_irq_coalescing(struct ata_host *host, { struct sata_fsl_host_priv *host_priv = host->private_data; void __iomem *hcr_base = host_priv->hcr_base; - unsigned long flags; if (count > ICC_MAX_INT_COUNT_THRESHOLD) count = ICC_MAX_INT_COUNT_THRESHOLD; @@ -306,12 +305,12 @@ static void fsl_sata_set_irq_coalescing(struct ata_host *host, (count > ICC_MIN_INT_COUNT_THRESHOLD)) ticks = ICC_SAFE_INT_TICKS; - spin_lock_irqsave(&host->lock, flags); + spin_lock(&host->lock); iowrite32((count << 24 | ticks), hcr_base + ICC); intr_coalescing_count = count; intr_coalescing_ticks = ticks; - spin_unlock_irqrestore(&host->lock, flags); + spin_unlock(&host->lock); DPRINTK("interrupt coalescing, count = 0x%x, ticks = %x\n", intr_coalescing_count, intr_coalescing_ticks); diff --git a/drivers/base/core.c b/drivers/base/core.c index ca4bcb8b3938..2499cefdcdf2 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1839,7 +1839,7 @@ EXPORT_SYMBOL_GPL(device_move); */ void device_shutdown(void) { - struct device *dev, *parent; + struct device *dev; spin_lock(&devices_kset->list_lock); /* @@ -1856,7 +1856,7 @@ void device_shutdown(void) * prevent it from being freed because parent's * lock is to be held */ - parent = get_device(dev->parent); + get_device(dev->parent); get_device(dev); /* * Make sure the device is off the kset list, in the @@ -1866,8 +1866,8 @@ void device_shutdown(void) spin_unlock(&devices_kset->list_lock); /* hold lock to avoid race with probe/release */ - if (parent) - device_lock(parent); + if (dev->parent) + device_lock(dev->parent); device_lock(dev); /* Don't allow any more runtime suspends */ @@ -1885,11 +1885,11 @@ void device_shutdown(void) } device_unlock(dev); - if (parent) - device_unlock(parent); + if (dev->parent) + device_unlock(dev->parent); put_device(dev); - put_device(parent); + put_device(dev->parent); spin_lock(&devices_kset->list_lock); } diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 86abbff912ec..14f8a6954da0 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -152,8 +152,6 @@ static ssize_t show_mem_removable(struct device *dev, container_of(dev, struct memory_block, dev); for (i = 0; i < sections_per_block; i++) { - if (!present_section_nr(mem->start_section_nr + i)) - continue; pfn = section_nr_to_pfn(mem->start_section_nr + i); ret &= is_mem_section_removable(pfn, PAGES_PER_SECTION); } diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index bb8c3bbc7812..02f490bad30f 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -362,7 +362,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, rbnode = kzalloc(sizeof *rbnode, GFP_KERNEL); if (!rbnode) return -ENOMEM; - rbnode->blklen = 1; + rbnode->blklen = sizeof(*rbnode); rbnode->base_reg = reg; rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size, GFP_KERNEL); diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 90a4e6b940ac..62b6c2cc80b5 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -1189,7 +1189,6 @@ static int cciss_ioctl32_passthru(struct block_device *bdev, fmode_t mode, int err; u32 cp; - memset(&arg64, 0, sizeof(arg64)); err = 0; err |= copy_from_user(&arg64.LUN_info, &arg32->LUN_info, diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c index 2b9440384536..639d26b90b91 100644 --- a/drivers/block/cpqarray.c +++ b/drivers/block/cpqarray.c @@ -1193,7 +1193,6 @@ static int ida_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned in ida_pci_info_struct pciinfo; if (!arg) return -EINVAL; - memset(&pciinfo, 0, sizeof(pciinfo)); pciinfo.bus = host->pci_dev->bus->number; pciinfo.dev_fn = host->pci_dev->devfn; pciinfo.board_id = host->board_id; diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 8c7421af8f15..aff789d6fccd 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -1565,12 +1565,11 @@ rbd_img_obj_request_read_callback(struct rbd_obj_request *obj_request) obj_request, obj_request->img_request, obj_request->result, xferred, length); /* - * ENOENT means a hole in the image. We zero-fill the entire - * length of the request. A short read also implies zero-fill - * to the end of the request. An error requires the whole - * length of the request to be reported finished with an error - * to the block layer. In each case we update the xferred - * count to indicate the whole request was satisfied. + * ENOENT means a hole in the image. We zero-fill the + * entire length of the request. A short read also implies + * zero-fill to the end of the request. Either way we + * update the xferred count to indicate the whole request + * was satisfied. */ rbd_assert(obj_request->type != OBJ_REQUEST_NODATA); if (obj_request->result == -ENOENT) { @@ -1579,13 +1578,14 @@ rbd_img_obj_request_read_callback(struct rbd_obj_request *obj_request) else zero_pages(obj_request->pages, 0, length); obj_request->result = 0; + obj_request->xferred = length; } else if (xferred < length && !obj_request->result) { if (obj_request->type == OBJ_REQUEST_BIO) zero_bio_chain(obj_request->bio_list, xferred); else zero_pages(obj_request->pages, xferred, length); + obj_request->xferred = length; } - obj_request->xferred = length; obj_request_done_set(obj_request); } diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index 0a327f4154a2..a12b923bbaca 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -85,7 +85,6 @@ static struct usb_device_id ath3k_table[] = { { USB_DEVICE(0x04CA, 0x3008) }, { USB_DEVICE(0x13d3, 0x3362) }, { USB_DEVICE(0x0CF3, 0xE004) }, - { USB_DEVICE(0x0CF3, 0xE005) }, { USB_DEVICE(0x0930, 0x0219) }, { USB_DEVICE(0x0489, 0xe057) }, { USB_DEVICE(0x13d3, 0x3393) }, @@ -127,7 +126,6 @@ static struct usb_device_id ath3k_blist_tbl[] = { { USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 }, diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 58491f1b2799..d0b3d900d452 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -102,7 +102,6 @@ static struct usb_device_id btusb_table[] = { /* Broadcom BCM20702A0 */ { USB_DEVICE(0x0b05, 0x17b5) }, - { USB_DEVICE(0x0b05, 0x17cb) }, { USB_DEVICE(0x04ca, 0x2003) }, { USB_DEVICE(0x0489, 0xe042) }, { USB_DEVICE(0x413c, 0x8197) }, @@ -149,7 +148,6 @@ static struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 }, diff --git a/drivers/char/random.c b/drivers/char/random.c index 81eefa1c0d3f..35487e8ded59 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1462,11 +1462,12 @@ ctl_table random_table[] = { static u32 random_int_secret[MD5_MESSAGE_BYTES / 4] ____cacheline_aligned; -int random_int_secret_init(void) +static int __init random_int_secret_init(void) { get_random_bytes(random_int_secret, sizeof(random_int_secret)); return 0; } +late_initcall(random_int_secret_init); /* * Get a random word for internal kernel use only. Similar to urandom but diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c index 917a3ab482f9..16ed06808554 100644 --- a/drivers/clk/clk-wm831x.c +++ b/drivers/clk/clk-wm831x.c @@ -360,8 +360,6 @@ static int wm831x_clk_probe(struct platform_device *pdev) if (!clkdata) return -ENOMEM; - clkdata->wm831x = wm831x; - /* XTAL_ENA can only be set via OTP/InstantConfig so just read once */ ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2); if (ret < 0) { diff --git a/drivers/cpuidle/coupled.c b/drivers/cpuidle/coupled.c index fe853903fe10..2a297f86dbad 100644 --- a/drivers/cpuidle/coupled.c +++ b/drivers/cpuidle/coupled.c @@ -106,7 +106,6 @@ struct cpuidle_coupled { cpumask_t coupled_cpus; int requested_state[NR_CPUS]; atomic_t ready_waiting_counts; - atomic_t abort_barrier; int online_count; int refcnt; int prevent; @@ -123,19 +122,12 @@ static DEFINE_MUTEX(cpuidle_coupled_lock); static DEFINE_PER_CPU(struct call_single_data, cpuidle_coupled_poke_cb); /* - * The cpuidle_coupled_poke_pending mask is used to avoid calling + * The cpuidle_coupled_poked_mask mask is used to avoid calling * __smp_call_function_single with the per cpu call_single_data struct already * in use. This prevents a deadlock where two cpus are waiting for each others * call_single_data struct to be available */ -static cpumask_t cpuidle_coupled_poke_pending; - -/* - * The cpuidle_coupled_poked mask is used to ensure that each cpu has been poked - * once to minimize entering the ready loop with a poke pending, which would - * require aborting and retrying. - */ -static cpumask_t cpuidle_coupled_poked; +static cpumask_t cpuidle_coupled_poked_mask; /** * cpuidle_coupled_parallel_barrier - synchronize all online coupled cpus @@ -299,11 +291,10 @@ static inline int cpuidle_coupled_get_state(struct cpuidle_device *dev, return state; } -static void cpuidle_coupled_handle_poke(void *info) +static void cpuidle_coupled_poked(void *info) { int cpu = (unsigned long)info; - cpumask_set_cpu(cpu, &cpuidle_coupled_poked); - cpumask_clear_cpu(cpu, &cpuidle_coupled_poke_pending); + cpumask_clear_cpu(cpu, &cpuidle_coupled_poked_mask); } /** @@ -322,7 +313,7 @@ static void cpuidle_coupled_poke(int cpu) { struct call_single_data *csd = &per_cpu(cpuidle_coupled_poke_cb, cpu); - if (!cpumask_test_and_set_cpu(cpu, &cpuidle_coupled_poke_pending)) + if (!cpumask_test_and_set_cpu(cpu, &cpuidle_coupled_poked_mask)) __smp_call_function_single(cpu, csd, 0); } @@ -349,19 +340,30 @@ static void cpuidle_coupled_poke_others(int this_cpu, * @coupled: the struct coupled that contains the current cpu * @next_state: the index in drv->states of the requested state for this cpu * - * Updates the requested idle state for the specified cpuidle device. - * Returns the number of waiting cpus. + * Updates the requested idle state for the specified cpuidle device, + * poking all coupled cpus out of idle if necessary to let them see the new + * state. */ -static int cpuidle_coupled_set_waiting(int cpu, +static void cpuidle_coupled_set_waiting(int cpu, struct cpuidle_coupled *coupled, int next_state) { + int w; + coupled->requested_state[cpu] = next_state; /* + * If this is the last cpu to enter the waiting state, poke + * all the other cpus out of their waiting state so they can + * enter a deeper state. This can race with one of the cpus + * exiting the waiting state due to an interrupt and + * decrementing waiting_count, see comment below. + * * The atomic_inc_return provides a write barrier to order the write * to requested_state with the later write that increments ready_count. */ - return atomic_inc_return(&coupled->ready_waiting_counts) & WAITING_MASK; + w = atomic_inc_return(&coupled->ready_waiting_counts) & WAITING_MASK; + if (w == coupled->online_count) + cpuidle_coupled_poke_others(cpu, coupled); } /** @@ -408,33 +410,19 @@ static void cpuidle_coupled_set_done(int cpu, struct cpuidle_coupled *coupled) * been processed and the poke bit has been cleared. * * Other interrupts may also be processed while interrupts are enabled, so - * need_resched() must be tested after this function returns to make sure + * need_resched() must be tested after turning interrupts off again to make sure * the interrupt didn't schedule work that should take the cpu out of idle. * - * Returns 0 if no poke was pending, 1 if a poke was cleared. + * Returns 0 if need_resched was false, -EINTR if need_resched was true. */ static int cpuidle_coupled_clear_pokes(int cpu) { - if (!cpumask_test_cpu(cpu, &cpuidle_coupled_poke_pending)) - return 0; - local_irq_enable(); - while (cpumask_test_cpu(cpu, &cpuidle_coupled_poke_pending)) + while (cpumask_test_cpu(cpu, &cpuidle_coupled_poked_mask)) cpu_relax(); local_irq_disable(); - return 1; -} - -static bool cpuidle_coupled_any_pokes_pending(struct cpuidle_coupled *coupled) -{ - cpumask_t cpus; - int ret; - - cpumask_and(&cpus, cpu_online_mask, &coupled->coupled_cpus); - ret = cpumask_and(&cpus, &cpuidle_coupled_poke_pending, &cpus); - - return ret; + return need_resched() ? -EINTR : 0; } /** @@ -461,14 +449,12 @@ int cpuidle_enter_state_coupled(struct cpuidle_device *dev, { int entered_state = -1; struct cpuidle_coupled *coupled = dev->coupled; - int w; if (!coupled) return -EINVAL; while (coupled->prevent) { - cpuidle_coupled_clear_pokes(dev->cpu); - if (need_resched()) { + if (cpuidle_coupled_clear_pokes(dev->cpu)) { local_irq_enable(); return entered_state; } @@ -479,37 +465,15 @@ int cpuidle_enter_state_coupled(struct cpuidle_device *dev, /* Read barrier ensures online_count is read after prevent is cleared */ smp_rmb(); -reset: - cpumask_clear_cpu(dev->cpu, &cpuidle_coupled_poked); - - w = cpuidle_coupled_set_waiting(dev->cpu, coupled, next_state); - /* - * If this is the last cpu to enter the waiting state, poke - * all the other cpus out of their waiting state so they can - * enter a deeper state. This can race with one of the cpus - * exiting the waiting state due to an interrupt and - * decrementing waiting_count, see comment below. - */ - if (w == coupled->online_count) { - cpumask_set_cpu(dev->cpu, &cpuidle_coupled_poked); - cpuidle_coupled_poke_others(dev->cpu, coupled); - } + cpuidle_coupled_set_waiting(dev->cpu, coupled, next_state); retry: /* * Wait for all coupled cpus to be idle, using the deepest state - * allowed for a single cpu. If this was not the poking cpu, wait - * for at least one poke before leaving to avoid a race where - * two cpus could arrive at the waiting loop at the same time, - * but the first of the two to arrive could skip the loop without - * processing the pokes from the last to arrive. + * allowed for a single cpu. */ - while (!cpuidle_coupled_cpus_waiting(coupled) || - !cpumask_test_cpu(dev->cpu, &cpuidle_coupled_poked)) { - if (cpuidle_coupled_clear_pokes(dev->cpu)) - continue; - - if (need_resched()) { + while (!cpuidle_coupled_cpus_waiting(coupled)) { + if (cpuidle_coupled_clear_pokes(dev->cpu)) { cpuidle_coupled_set_not_waiting(dev->cpu, coupled); goto out; } @@ -523,18 +487,11 @@ int cpuidle_enter_state_coupled(struct cpuidle_device *dev, dev->safe_state_index); } - cpuidle_coupled_clear_pokes(dev->cpu); - if (need_resched()) { + if (cpuidle_coupled_clear_pokes(dev->cpu)) { cpuidle_coupled_set_not_waiting(dev->cpu, coupled); goto out; } - /* - * Make sure final poke status for this cpu is visible before setting - * cpu as ready. - */ - smp_wmb(); - /* * All coupled cpus are probably idle. There is a small chance that * one of the other cpus just became active. Increment the ready count, @@ -554,28 +511,6 @@ int cpuidle_enter_state_coupled(struct cpuidle_device *dev, cpu_relax(); } - /* - * Make sure read of all cpus ready is done before reading pending pokes - */ - smp_rmb(); - - /* - * There is a small chance that a cpu left and reentered idle after this - * cpu saw that all cpus were waiting. The cpu that reentered idle will - * have sent this cpu a poke, which will still be pending after the - * ready loop. The pending interrupt may be lost by the interrupt - * controller when entering the deep idle state. It's not possible to - * clear a pending interrupt without turning interrupts on and handling - * it, and it's too late to turn on interrupts here, so reset the - * coupled idle state of all cpus and retry. - */ - if (cpuidle_coupled_any_pokes_pending(coupled)) { - cpuidle_coupled_set_done(dev->cpu, coupled); - /* Wait for all cpus to see the pending pokes */ - cpuidle_coupled_parallel_barrier(dev, &coupled->abort_barrier); - goto reset; - } - /* all cpus have acked the coupled state */ next_state = cpuidle_coupled_get_state(dev, coupled); @@ -661,7 +596,7 @@ int cpuidle_coupled_register_device(struct cpuidle_device *dev) coupled->refcnt++; csd = &per_cpu(cpuidle_coupled_poke_cb, dev->cpu); - csd->func = cpuidle_coupled_handle_poke; + csd->func = cpuidle_coupled_poked; csd->info = (void *)(unsigned long)dev->cpu; return 0; diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 617d17029065..f28583370d00 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -414,18 +414,17 @@ static void dma_irq_handle_channel(struct imxdma_channel *imxdmac) struct imxdma_engine *imxdma = imxdmac->imxdma; int chno = imxdmac->channel; struct imxdma_desc *desc; - unsigned long flags; - spin_lock_irqsave(&imxdma->lock, flags); + spin_lock(&imxdma->lock); if (list_empty(&imxdmac->ld_active)) { - spin_unlock_irqrestore(&imxdma->lock, flags); + spin_unlock(&imxdma->lock); goto out; } desc = list_first_entry(&imxdmac->ld_active, struct imxdma_desc, node); - spin_unlock_irqrestore(&imxdma->lock, flags); + spin_unlock(&imxdma->lock); if (desc->sg) { u32 tmp; @@ -497,6 +496,7 @@ static int imxdma_xfer_desc(struct imxdma_desc *d) { struct imxdma_channel *imxdmac = to_imxdma_chan(d->desc.chan); struct imxdma_engine *imxdma = imxdmac->imxdma; + unsigned long flags; int slot = -1; int i; @@ -504,6 +504,7 @@ static int imxdma_xfer_desc(struct imxdma_desc *d) switch (d->type) { case IMXDMA_DESC_INTERLEAVED: /* Try to get a free 2D slot */ + spin_lock_irqsave(&imxdma->lock, flags); for (i = 0; i < IMX_DMA_2D_SLOTS; i++) { if ((imxdma->slots_2d[i].count > 0) && ((imxdma->slots_2d[i].xsr != d->x) || @@ -513,8 +514,10 @@ static int imxdma_xfer_desc(struct imxdma_desc *d) slot = i; break; } - if (slot < 0) + if (slot < 0) { + spin_unlock_irqrestore(&imxdma->lock, flags); return -EBUSY; + } imxdma->slots_2d[slot].xsr = d->x; imxdma->slots_2d[slot].ysr = d->y; @@ -523,6 +526,7 @@ static int imxdma_xfer_desc(struct imxdma_desc *d) imxdmac->slot_2d = slot; imxdmac->enabled_2d = true; + spin_unlock_irqrestore(&imxdma->lock, flags); if (slot == IMX_DMA_2D_SLOT_A) { d->config_mem &= ~CCR_MSEL_B; @@ -598,17 +602,18 @@ static void imxdma_tasklet(unsigned long data) struct imxdma_channel *imxdmac = (void *)data; struct imxdma_engine *imxdma = imxdmac->imxdma; struct imxdma_desc *desc; - unsigned long flags; - spin_lock_irqsave(&imxdma->lock, flags); + spin_lock(&imxdma->lock); if (list_empty(&imxdmac->ld_active)) { /* Someone might have called terminate all */ - spin_unlock_irqrestore(&imxdma->lock, flags); - return; + goto out; } desc = list_first_entry(&imxdmac->ld_active, struct imxdma_desc, node); + if (desc->desc.callback) + desc->desc.callback(desc->desc.callback_param); + /* If we are dealing with a cyclic descriptor, keep it on ld_active * and dont mark the descriptor as complete. * Only in non-cyclic cases it would be marked as complete @@ -635,11 +640,7 @@ static void imxdma_tasklet(unsigned long data) __func__, imxdmac->channel); } out: - spin_unlock_irqrestore(&imxdma->lock, flags); - - if (desc->desc.callback) - desc->desc.callback(desc->desc.callback_param); - + spin_unlock(&imxdma->lock); } static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, @@ -861,7 +862,7 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic( kfree(imxdmac->sg_list); imxdmac->sg_list = kcalloc(periods + 1, - sizeof(struct scatterlist), GFP_ATOMIC); + sizeof(struct scatterlist), GFP_KERNEL); if (!imxdmac->sg_list) return NULL; diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c index 2a51cf73c317..91c6f8f0159c 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -1890,7 +1890,7 @@ static int intel_mid_dma_probe(struct pci_dev *pdev, goto err_ioremap; pm_runtime_put_noidle(&pdev->dev); - pm_runtime_allow(&pdev->dev); + pm_runtime_forbid(&pdev->dev); return 0; err_ioremap: diff --git a/drivers/dma/intel_mid_dma_acpi.c b/drivers/dma/intel_mid_dma_acpi.c index 65363b4dfe08..2bef1ab7e80c 100644 --- a/drivers/dma/intel_mid_dma_acpi.c +++ b/drivers/dma/intel_mid_dma_acpi.c @@ -31,17 +31,35 @@ #include #include -#define MAX_CHAN 4 /*max ch across controllers*/ #include "intel_mid_dma_regs.h" -static struct device *acpi_dma_dev; +#define HID_MAX_SIZE 8 -struct device *intel_mid_get_acpi_dma(void) +struct list_head dma_dev_list; + +LIST_HEAD(dma_dev_list); + +struct acpi_dma_dev_list { + struct list_head dmadev_list; + char dma_hid[HID_MAX_SIZE]; + struct device *acpi_dma_dev; +}; + +struct device *intel_mid_get_acpi_dma(const char *hid) { - return acpi_dma_dev; + struct acpi_dma_dev_list *listnode; + if (list_empty(&dma_dev_list)) + return NULL; + + list_for_each_entry(listnode, &dma_dev_list, dmadev_list) { + if (!(strncmp(listnode->dma_hid, hid, HID_MAX_SIZE))) + return listnode->acpi_dma_dev; + } + return NULL; } EXPORT_SYMBOL_GPL(intel_mid_get_acpi_dma); +#if IS_ENABLED(CONFIG_ACPI) static int mid_get_and_map_rsrc(void **dest, struct platform_device *pdev, unsigned int num) { @@ -90,7 +108,6 @@ static int mid_platform_get_resources(struct middma_device *mid_device, return 0; } -#if IS_ENABLED(CONFIG_ACPI) int dma_acpi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -168,9 +185,7 @@ int dma_acpi_probe(struct platform_device *pdev) int dma_acpi_remove(struct platform_device *pdev) { - pm_runtime_get_noresume(&pdev->dev); - pm_runtime_disable(&pdev->dev); - acpi_dma_dev = NULL; + pm_runtime_forbid(&pdev->dev); middma_shutdown(&pdev->dev); platform_set_drvdata(pdev, NULL); return 0; diff --git a/drivers/dma/intel_mid_dma_regs.h b/drivers/dma/intel_mid_dma_regs.h index e82cb8d00dc3..30160e411f5c 100644 --- a/drivers/dma/intel_mid_dma_regs.h +++ b/drivers/dma/intel_mid_dma_regs.h @@ -31,6 +31,8 @@ #define INTEL_MID_DMA_DRIVER_VERSION "1.1.0" +#define MID_MAX_CHAN 8 /*max ch across controllers*/ + #define REG_BIT0 0x00000001 #define REG_BIT8 0x00000100 #define INT_MASK_WE 0x8 @@ -342,7 +344,7 @@ struct middma_device { struct pci_pool *dma_pool; struct dma_device common; struct tasklet_struct tasklet; - struct intel_mid_dma_chan ch[MAX_CHAN]; + struct intel_mid_dma_chan ch[MID_MAX_CHAN]; unsigned int pci_id; unsigned int intr_mask; void __iomem *mask_reg; diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 8b3d90143514..8b6a0343c220 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -2470,15 +2470,8 @@ static int amd64_init_one_instance(struct pci_dev *F2) layers[0].size = pvt->csels[0].b_cnt; layers[0].is_virt_csrow = true; layers[1].type = EDAC_MC_LAYER_CHANNEL; - - /* - * Always allocate two channels since we can have setups with DIMMs on - * only one channel. Also, this simplifies handling later for the price - * of a couple of KBs tops. - */ - layers[1].size = 2; + layers[1].size = pvt->channel_count; layers[1].is_virt_csrow = false; - mci = edac_mc_alloc(nid, ARRAY_SIZE(layers), layers, 0); if (!mci) goto err_siblings; diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 54052125859a..4a430360af5a 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -63,7 +63,6 @@ struct gpio_bank { struct gpio_chip chip; struct clk *dbck; u32 mod_usage; - u32 irq_usage; u32 dbck_enable_mask; bool dbck_enabled; struct device *dev; @@ -87,9 +86,6 @@ struct gpio_bank { #define GPIO_BIT(bank, gpio) (1 << GPIO_INDEX(bank, gpio)) #define GPIO_MOD_CTRL_BIT BIT(0) -#define BANK_USED(bank) (bank->mod_usage || bank->irq_usage) -#define LINE_USED(line, offset) (line & (1 << offset)) - static int irq_to_gpio(struct gpio_bank *bank, unsigned int gpio_irq) { return bank->chip.base + gpio_irq; @@ -424,69 +420,15 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, return 0; } -static void _enable_gpio_module(struct gpio_bank *bank, unsigned offset) -{ - if (bank->regs->pinctrl) { - void __iomem *reg = bank->base + bank->regs->pinctrl; - - /* Claim the pin for MPU */ - __raw_writel(__raw_readl(reg) | (1 << offset), reg); - } - - if (bank->regs->ctrl && !BANK_USED(bank)) { - void __iomem *reg = bank->base + bank->regs->ctrl; - u32 ctrl; - - ctrl = __raw_readl(reg); - /* Module is enabled, clocks are not gated */ - ctrl &= ~GPIO_MOD_CTRL_BIT; - __raw_writel(ctrl, reg); - bank->context.ctrl = ctrl; - } -} - -static void _disable_gpio_module(struct gpio_bank *bank, unsigned offset) -{ - void __iomem *base = bank->base; - - if (bank->regs->wkup_en && - !LINE_USED(bank->mod_usage, offset) && - !LINE_USED(bank->irq_usage, offset)) { - /* Disable wake-up during idle for dynamic tick */ - _gpio_rmw(base, bank->regs->wkup_en, 1 << offset, 0); - bank->context.wake_en = - __raw_readl(bank->base + bank->regs->wkup_en); - } - - if (bank->regs->ctrl && !BANK_USED(bank)) { - void __iomem *reg = bank->base + bank->regs->ctrl; - u32 ctrl; - - ctrl = __raw_readl(reg); - /* Module is disabled, clocks are gated */ - ctrl |= GPIO_MOD_CTRL_BIT; - __raw_writel(ctrl, reg); - bank->context.ctrl = ctrl; - } -} - -static int gpio_is_input(struct gpio_bank *bank, int mask) -{ - void __iomem *reg = bank->base + bank->regs->direction; - - return __raw_readl(reg) & mask; -} - static int gpio_irq_type(struct irq_data *d, unsigned type) { struct gpio_bank *bank = irq_data_get_irq_chip_data(d); unsigned gpio = 0; int retval; unsigned long flags; - unsigned offset; - if (!BANK_USED(bank)) - pm_runtime_get_sync(bank->dev); + if (WARN_ON(!bank->mod_usage)) + return -EINVAL; #ifdef CONFIG_ARCH_OMAP1 if (d->irq > IH_MPUIO_BASE) @@ -504,17 +446,7 @@ static int gpio_irq_type(struct irq_data *d, unsigned type) return -EINVAL; spin_lock_irqsave(&bank->lock, flags); - offset = GPIO_INDEX(bank, gpio); - retval = _set_gpio_triggering(bank, offset, type); - if (!LINE_USED(bank->mod_usage, offset)) { - _enable_gpio_module(bank, offset); - _set_gpio_direction(bank, offset, 1); - } else if (!gpio_is_input(bank, 1 << offset)) { - spin_unlock_irqrestore(&bank->lock, flags); - return -EINVAL; - } - - bank->irq_usage |= 1 << GPIO_INDEX(bank, gpio); + retval = _set_gpio_triggering(bank, GPIO_INDEX(bank, gpio), type); spin_unlock_irqrestore(&bank->lock, flags); if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) @@ -671,19 +603,35 @@ static int omap_gpio_request(struct gpio_chip *chip, unsigned offset) * If this is the first gpio_request for the bank, * enable the bank module. */ - if (!BANK_USED(bank)) + if (!bank->mod_usage) pm_runtime_get_sync(bank->dev); spin_lock_irqsave(&bank->lock, flags); /* Set trigger to none. You need to enable the desired trigger with - * request_irq() or set_irq_type(). Only do this if the IRQ line has - * not already been requested. + * request_irq() or set_irq_type(). */ - if (!LINE_USED(bank->irq_usage, offset)) { - _set_gpio_triggering(bank, offset, IRQ_TYPE_NONE); - _enable_gpio_module(bank, offset); + _set_gpio_triggering(bank, offset, IRQ_TYPE_NONE); + + if (bank->regs->pinctrl) { + void __iomem *reg = bank->base + bank->regs->pinctrl; + + /* Claim the pin for MPU */ + __raw_writel(__raw_readl(reg) | (1 << offset), reg); + } + + if (bank->regs->ctrl && !bank->mod_usage) { + void __iomem *reg = bank->base + bank->regs->ctrl; + u32 ctrl; + + ctrl = __raw_readl(reg); + /* Module is enabled, clocks are not gated */ + ctrl &= ~GPIO_MOD_CTRL_BIT; + __raw_writel(ctrl, reg); + bank->context.ctrl = ctrl; } + bank->mod_usage |= 1 << offset; + spin_unlock_irqrestore(&bank->lock, flags); return 0; @@ -692,11 +640,31 @@ static int omap_gpio_request(struct gpio_chip *chip, unsigned offset) static void omap_gpio_free(struct gpio_chip *chip, unsigned offset) { struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip); + void __iomem *base = bank->base; unsigned long flags; spin_lock_irqsave(&bank->lock, flags); + + if (bank->regs->wkup_en) { + /* Disable wake-up during idle for dynamic tick */ + _gpio_rmw(base, bank->regs->wkup_en, 1 << offset, 0); + bank->context.wake_en = + __raw_readl(bank->base + bank->regs->wkup_en); + } + bank->mod_usage &= ~(1 << offset); - _disable_gpio_module(bank, offset); + + if (bank->regs->ctrl && !bank->mod_usage) { + void __iomem *reg = bank->base + bank->regs->ctrl; + u32 ctrl; + + ctrl = __raw_readl(reg); + /* Module is disabled, clocks are gated */ + ctrl |= GPIO_MOD_CTRL_BIT; + __raw_writel(ctrl, reg); + bank->context.ctrl = ctrl; + } + _reset_gpio(bank, bank->chip.base + offset); spin_unlock_irqrestore(&bank->lock, flags); @@ -704,7 +672,7 @@ static void omap_gpio_free(struct gpio_chip *chip, unsigned offset) * If this is the last gpio to be freed in the bank, * disable the bank module. */ - if (!BANK_USED(bank)) + if (!bank->mod_usage) pm_runtime_put(bank->dev); } @@ -794,20 +762,10 @@ static void gpio_irq_shutdown(struct irq_data *d) struct gpio_bank *bank = irq_data_get_irq_chip_data(d); unsigned int gpio = irq_to_gpio(bank, d->hwirq); unsigned long flags; - unsigned offset = GPIO_INDEX(bank, gpio); spin_lock_irqsave(&bank->lock, flags); - bank->irq_usage &= ~(1 << offset); - _disable_gpio_module(bank, offset); _reset_gpio(bank, gpio); spin_unlock_irqrestore(&bank->lock, flags); - - /* - * If this is the last IRQ to be freed in the bank, - * disable the bank module. - */ - if (!BANK_USED(bank)) - pm_runtime_put(bank->dev); } static void gpio_ack_irq(struct irq_data *d) @@ -939,6 +897,13 @@ static int gpio_input(struct gpio_chip *chip, unsigned offset) return 0; } +static int gpio_is_input(struct gpio_bank *bank, int mask) +{ + void __iomem *reg = bank->base + bank->regs->direction; + + return __raw_readl(reg) & mask; +} + static int gpio_get(struct gpio_chip *chip, unsigned offset) { struct gpio_bank *bank; @@ -957,22 +922,13 @@ static int gpio_output(struct gpio_chip *chip, unsigned offset, int value) { struct gpio_bank *bank; unsigned long flags; - int retval = 0; bank = container_of(chip, struct gpio_bank, chip); spin_lock_irqsave(&bank->lock, flags); - - if (LINE_USED(bank->irq_usage, offset)) { - retval = -EINVAL; - goto exit; - } - bank->set_dataout(bank, offset, value); _set_gpio_direction(bank, offset, 0); - -exit: spin_unlock_irqrestore(&bank->lock, flags); - return retval; + return 0; } static int gpio_debounce(struct gpio_chip *chip, unsigned offset, @@ -1444,7 +1400,7 @@ void omap2_gpio_prepare_for_idle(int pwr_mode) struct gpio_bank *bank; list_for_each_entry(bank, &omap_gpio_list, node) { - if (!BANK_USED(bank) || !bank->loses_context) + if (!bank->mod_usage || !bank->loses_context) continue; bank->power_mode = pwr_mode; @@ -1458,7 +1414,7 @@ void omap2_gpio_resume_after_idle(void) struct gpio_bank *bank; list_for_each_entry(bank, &omap_gpio_list, node) { - if (!BANK_USED(bank) || !bank->loses_context) + if (!bank->mod_usage || !bank->loses_context) continue; pm_runtime_get_sync(bank->dev); diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index b6b7d70f2832..02e52d543e4b 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -177,7 +177,7 @@ uint8_t ast_get_index_reg_mask(struct ast_private *ast, static inline void ast_open_key(struct ast_private *ast) { - ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0x80, 0xA8); + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xA1, 0xFF, 0x04); } #define AST_VIDMEM_SIZE_8M 0x00800000 diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 8759d699bd8e..e7e92429d10f 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2501,22 +2501,10 @@ int drm_mode_getfb(struct drm_device *dev, r->depth = fb->depth; r->bpp = fb->bits_per_pixel; r->pitch = fb->pitches[0]; - if (fb->funcs->create_handle) { - if (file_priv->is_master || capable(CAP_SYS_ADMIN)) { - ret = fb->funcs->create_handle(fb, file_priv, - &r->handle); - } else { - /* GET_FB() is an unprivileged ioctl so we must not - * return a buffer-handle to non-master processes! For - * backwards-compatibility reasons, we cannot make - * GET_FB() privileged, so just return an invalid handle - * for non-masters. */ - r->handle = 0; - ret = 0; - } - } else { + if (fb->funcs->create_handle) + ret = fb->funcs->create_handle(fb, file_priv, &r->handle); + else ret = -ENODEV; - } drm_framebuffer_unreference(fb); diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 0cb9b5d8e30a..9e62bbedb5ad 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -125,9 +125,6 @@ static struct edid_quirk { /* ViewSonic VA2026w */ { "VSC", 5020, EDID_QUIRK_FORCE_REDUCED_BLANKING }, - - /* Medion MD 30217 PG */ - { "MED", 0x7b8, EDID_QUIRK_PREFER_LARGE_75 }, }; /* diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index c8d16a622f3c..e5e328691e3a 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1009,34 +1009,6 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) return ret; } -static void i915_error_wake_up(struct drm_i915_private *dev_priv, - bool reset_completed) -{ - struct intel_ring_buffer *ring; - int i; - - /* - * Notify all waiters for GPU completion events that reset state has - * been changed, and that they need to restart their wait after - * checking for potential errors (and bail out to drop locks if there is - * a gpu reset pending so that i915_error_work_func can acquire them). - */ - - /* Wake up __wait_seqno, potentially holding dev->struct_mutex. */ - for_each_ring(ring, dev_priv, i) - wake_up_all(&ring->irq_queue); - - /* Wake up intel_crtc_wait_for_pending_flips, holding crtc->mutex. */ - wake_up_all(&dev_priv->pending_flip_queue); - - /* - * Signal tasks blocked in i915_gem_wait_for_error that the pending - * reset state is cleared. - */ - if (reset_completed) - wake_up_all(&dev_priv->gpu_error.reset_queue); -} - /** * i915_error_work_func - do process context error handling work * @work: work struct @@ -1051,10 +1023,11 @@ static void i915_error_work_func(struct work_struct *work) drm_i915_private_t *dev_priv = container_of(error, drm_i915_private_t, gpu_error); struct drm_device *dev = dev_priv->dev; + struct intel_ring_buffer *ring; char *error_event[] = { "ERROR=1", NULL }; char *reset_event[] = { "RESET=1", NULL }; char *reset_done_event[] = { "ERROR=0", NULL }; - int ret; + int i, ret; kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event); @@ -1073,16 +1046,8 @@ static void i915_error_work_func(struct work_struct *work) kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event); - /* - * All state reset _must_ be completed before we update the - * reset counter, for otherwise waiters might miss the reset - * pending state and not properly drop locks, resulting in - * deadlocks with the reset work. - */ ret = i915_reset(dev); - intel_display_handle_reset(dev); - if (ret == 0) { /* * After all the gem state is reset, increment the reset @@ -1103,11 +1068,12 @@ static void i915_error_work_func(struct work_struct *work) atomic_set(&error->reset_counter, I915_WEDGED); } - /* - * Note: The wake_up also serves as a memory barrier so that - * waiters see the update value of the reset counter atomic_t. - */ - i915_error_wake_up(dev_priv, true); + for_each_ring(ring, dev_priv, i) + wake_up_all(&ring->irq_queue); + + intel_display_handle_reset(dev); + + wake_up_all(&dev_priv->gpu_error.reset_queue); } } @@ -1743,6 +1709,8 @@ static void i915_report_and_clear_eir(struct drm_device *dev) void i915_handle_error(struct drm_device *dev, bool wedged) { struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring; + int i; i915_capture_error_state(dev); i915_report_and_clear_eir(dev); @@ -1752,28 +1720,14 @@ void i915_handle_error(struct drm_device *dev, bool wedged) &dev_priv->gpu_error.reset_counter); /* - * Wakeup waiting processes so that the reset work function - * i915_error_work_func doesn't deadlock trying to grab various - * locks. By bumping the reset counter first, the woken - * processes will see a reset in progress and back off, - * releasing their locks and then wait for the reset completion. - * We must do this for _all_ gpu waiters that might hold locks - * that the reset work needs to acquire. - * - * Note: The wake_up serves as the required memory barrier to - * ensure that the waiters see the updated value of the reset - * counter atomic_t. + * Wakeup waiting processes so that the reset work item + * doesn't deadlock trying to grab various locks. */ - i915_error_wake_up(dev_priv, false); + for_each_ring(ring, dev_priv, i) + wake_up_all(&ring->irq_queue); } - /* - * Our reset work can grab modeset locks (since it needs to reset the - * state of outstanding pagelips). Hence it must not be run on our own - * dev-priv->wq work queue for otherwise the flush_work in the pageflip - * code will deadlock. - */ - schedule_work(&dev_priv->gpu_error.work); + queue_work(dev_priv->wq, &dev_priv->gpu_error.work); } static void __always_unused i915_pageflip_stall_check(struct drm_device *dev, int pipe) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index b1a0cdba59e7..80b0a6626a23 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -617,8 +617,6 @@ will not assert AGPBUSY# and will only be delivered when out of C3. */ #define INSTPM_FORCE_ORDERING (1<<7) /* GEN6+ */ -#define INSTPM_TLB_INVALIDATE (1<<9) -#define INSTPM_SYNC_FLUSH (1<<5) #define ACTHD 0x020c8 #define FW_BLC 0x020d8 #define FW_BLC2 0x020dc @@ -1675,16 +1673,10 @@ #define CRT_HOTPLUG_DETECT_VOLTAGE_475MV (1 << 2) #define PORT_HOTPLUG_STAT (dev_priv->info->display_mmio_offset + 0x61114) -/* - * HDMI/DP bits are gen4+ - * - * WARNING: Bspec for hpd status bits on gen4 seems to be completely confused. - * Please check the detailed lore in the commit message for for experimental - * evidence. - */ -#define PORTD_HOTPLUG_LIVE_STATUS (1 << 29) +/* HDMI/DP bits are gen4+ */ +#define PORTB_HOTPLUG_LIVE_STATUS (1 << 29) #define PORTC_HOTPLUG_LIVE_STATUS (1 << 28) -#define PORTB_HOTPLUG_LIVE_STATUS (1 << 27) +#define PORTD_HOTPLUG_LIVE_STATUS (1 << 27) #define PORTD_HOTPLUG_INT_STATUS (3 << 21) #define PORTC_HOTPLUG_INT_STATUS (3 << 19) #define PORTB_HOTPLUG_INT_STATUS (3 << 17) @@ -4252,7 +4244,7 @@ #define EDP_LINK_TRAIN_600MV_0DB_IVB (0x30 <<22) #define EDP_LINK_TRAIN_600MV_3_5DB_IVB (0x36 <<22) #define EDP_LINK_TRAIN_800MV_0DB_IVB (0x38 <<22) -#define EDP_LINK_TRAIN_800MV_3_5DB_IVB (0x3e <<22) +#define EDP_LINK_TRAIN_800MV_3_5DB_IVB (0x33 <<22) /* legacy values */ #define EDP_LINK_TRAIN_500MV_0DB_IVB (0x00 <<22) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index ab952595e088..eea5982657a8 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3946,6 +3946,8 @@ static void intel_connector_check_state(struct intel_connector *connector) * consider. */ void intel_connector_dpms(struct drm_connector *connector, int mode) { + struct intel_encoder *encoder = intel_attached_encoder(connector); + /* All the simple cases only support two dpms states. */ if (mode != DRM_MODE_DPMS_ON) mode = DRM_MODE_DPMS_OFF; @@ -3956,8 +3958,10 @@ void intel_connector_dpms(struct drm_connector *connector, int mode) connector->dpms = mode; /* Only need to change hw state when actually enabled */ - if (connector->encoder) - intel_encoder_dpms(to_intel_encoder(connector->encoder), mode); + if (encoder->base.crtc) + intel_encoder_dpms(encoder, mode); + else + WARN_ON(encoder->connectors_active != false); intel_modeset_check_state(connector->dev); } @@ -4560,10 +4564,6 @@ static void i9xx_set_pipeconf(struct intel_crtc *intel_crtc) pipeconf = I915_READ(PIPECONF(intel_crtc->pipe)); - if (dev_priv->quirks & QUIRK_PIPEA_FORCE && - I915_READ(PIPECONF(intel_crtc->pipe)) & PIPECONF_ENABLE) - pipeconf |= PIPECONF_ENABLE; - if (intel_crtc->pipe == 0 && INTEL_INFO(dev)->gen < 4) { /* Enable pixel doubling when the dot clock is > 90% of the (display) * core speed. diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 80feaec88d2b..70789b1b5642 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -604,18 +604,7 @@ intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, DRM_DEBUG_KMS("aux_ch native nack\n"); return -EREMOTEIO; case AUX_NATIVE_REPLY_DEFER: - /* - * For now, just give more slack to branch devices. We - * could check the DPCD for I2C bit rate capabilities, - * and if available, adjust the interval. We could also - * be more careful with DP-to-Legacy adapters where a - * long legacy cable may force very low I2C bit rates. - */ - if (intel_dp->dpcd[DP_DOWNSTREAMPORT_PRESENT] & - DP_DWN_STRM_PORT_PRESENT) - usleep_range(500, 600); - else - usleep_range(300, 400); + udelay(100); continue; default: DRM_ERROR("aux_ch invalid native reply 0x%02x\n", diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 48fe23e8d180..1424f2042838 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -907,18 +907,6 @@ void intel_ring_setup_status_page(struct intel_ring_buffer *ring) I915_WRITE(mmio, (u32)ring->status_page.gfx_addr); POSTING_READ(mmio); - - /* Flush the TLB for this page */ - if (INTEL_INFO(dev)->gen >= 6) { - u32 reg = RING_INSTPM(ring->mmio_base); - I915_WRITE(reg, - _MASKED_BIT_ENABLE(INSTPM_TLB_INVALIDATE | - INSTPM_SYNC_FLUSH)); - if (wait_for((I915_READ(reg) & INSTPM_SYNC_FLUSH) == 0, - 1000)) - DRM_ERROR("%s: wait for SyncFlush to complete for TLB invalidation timed out\n", - ring->name); - } } static int diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index a202d8d08c56..b945bc54207a 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -921,14 +921,6 @@ intel_tv_compute_config(struct intel_encoder *encoder, DRM_DEBUG_KMS("forcing bpc to 8 for TV\n"); pipe_config->pipe_bpp = 8*3; - /* TV has it's own notion of sync and other mode flags, so clear them. */ - pipe_config->adjusted_mode.flags = 0; - - /* - * FIXME: We don't check whether the input mode is actually what we want - * or whether userspace is doing something stupid. - */ - return true; } diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/dacnv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/dacnv50.c index a66b27c0fcab..f02fd9f443ff 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/dacnv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/dacnv50.c @@ -49,23 +49,18 @@ int nv50_dac_sense(struct nv50_disp_priv *priv, int or, u32 loadval) { const u32 doff = (or * 0x800); - + int load = -EINVAL; nv_mask(priv, 0x61a004 + doff, 0x807f0000, 0x80150000); nv_wait(priv, 0x61a004 + doff, 0x80000000, 0x00000000); - nv_wr32(priv, 0x61a00c + doff, 0x00100000 | loadval); mdelay(9); udelay(500); - loadval = nv_mask(priv, 0x61a00c + doff, 0xffffffff, 0x00000000); - + nv_wr32(priv, 0x61a00c + doff, 0x80000000); + load = (nv_rd32(priv, 0x61a00c + doff) & 0x38000000) >> 27; + nv_wr32(priv, 0x61a00c + doff, 0x00000000); nv_mask(priv, 0x61a004 + doff, 0x807f0000, 0x80550000); nv_wait(priv, 0x61a004 + doff, 0x80000000, 0x00000000); - - nv_debug(priv, "DAC%d sense: 0x%08x\n", or, loadval); - if (!(loadval & 0x80000000)) - return -ETIMEDOUT; - - return (loadval & 0x38000000) >> 27; + return load; } int diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/mc.h b/drivers/gpu/drm/nouveau/core/include/subdev/mc.h index 9d2cd2006250..d5502267c30f 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/mc.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/mc.h @@ -20,8 +20,8 @@ nouveau_mc(void *obj) return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_MC]; } -#define nouveau_mc_create(p,e,o,m,d) \ - nouveau_mc_create_((p), (e), (o), (m), sizeof(**d), (void **)d) +#define nouveau_mc_create(p,e,o,d) \ + nouveau_mc_create_((p), (e), (o), sizeof(**d), (void **)d) #define nouveau_mc_destroy(p) ({ \ struct nouveau_mc *pmc = (p); _nouveau_mc_dtor(nv_object(pmc)); \ }) @@ -33,8 +33,7 @@ nouveau_mc(void *obj) }) int nouveau_mc_create_(struct nouveau_object *, struct nouveau_object *, - struct nouveau_oclass *, const struct nouveau_mc_intr *, - int, void **); + struct nouveau_oclass *, int, void **); void _nouveau_mc_dtor(struct nouveau_object *); int _nouveau_mc_init(struct nouveau_object *); int _nouveau_mc_fini(struct nouveau_object *, bool); diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c index e2d7f38447cc..c434d398d16f 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c @@ -580,22 +580,8 @@ static void init_reserved(struct nvbios_init *init) { u8 opcode = nv_ro08(init->bios, init->offset); - u8 length, i; - - switch (opcode) { - case 0xaa: - length = 4; - break; - default: - length = 1; - break; - } - - trace("RESERVED 0x%02x\t", opcode); - for (i = 1; i < length; i++) - cont(" 0x%02x", nv_ro08(init->bios, init->offset + i)); - cont("\n"); - init->offset += length; + trace("RESERVED\t0x%02x\n", opcode); + init->offset += 1; } /** @@ -2150,7 +2136,6 @@ static struct nvbios_init_opcode { [0x99] = { init_zm_auxch }, [0x9a] = { init_i2c_long_if }, [0xa9] = { init_gpio_ne }, - [0xaa] = { init_reserved }, }; #define init_opcode_nr (sizeof(init_opcode) / sizeof(init_opcode[0])) diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/base.c b/drivers/gpu/drm/nouveau/core/subdev/mc/base.c index ec9cd6f10f91..1c0330b8c9a4 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/base.c @@ -80,9 +80,7 @@ _nouveau_mc_dtor(struct nouveau_object *object) int nouveau_mc_create_(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, - const struct nouveau_mc_intr *intr_map, - int length, void **pobject) + struct nouveau_oclass *oclass, int length, void **pobject) { struct nouveau_device *device = nv_device(parent); struct nouveau_mc *pmc; @@ -94,8 +92,6 @@ nouveau_mc_create_(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; - pmc->intr_map = intr_map; - ret = request_irq(device->pdev->irq, nouveau_mc_intr, IRQF_SHARED, "nouveau", pmc); if (ret < 0) diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv04.c index 64aa4edb0d9d..8c769715227b 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv04.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv04.c @@ -50,11 +50,12 @@ nv04_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv04_mc_priv *priv; int ret; - ret = nouveau_mc_create(parent, engine, oclass, nv04_mc_intr, &priv); + ret = nouveau_mc_create(parent, engine, oclass, &priv); *pobject = nv_object(priv); if (ret) return ret; + priv->base.intr_map = nv04_mc_intr; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv44.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv44.c index d9891782bf28..51919371810f 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv44.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv44.c @@ -36,11 +36,12 @@ nv44_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv44_mc_priv *priv; int ret; - ret = nouveau_mc_create(parent, engine, oclass, nv04_mc_intr, &priv); + ret = nouveau_mc_create(parent, engine, oclass, &priv); *pobject = nv_object(priv); if (ret) return ret; + priv->base.intr_map = nv04_mc_intr; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c index 732d8100344b..d796924f9930 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c @@ -52,11 +52,12 @@ nv50_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv50_mc_priv *priv; int ret; - ret = nouveau_mc_create(parent, engine, oclass, nv50_mc_intr, &priv); + ret = nouveau_mc_create(parent, engine, oclass, &priv); *pobject = nv_object(priv); if (ret) return ret; + priv->base.intr_map = nv50_mc_intr; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c index 0d57b4d3e001..e82fd21b5041 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c @@ -54,11 +54,12 @@ nv98_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv98_mc_priv *priv; int ret; - ret = nouveau_mc_create(parent, engine, oclass, nv98_mc_intr, &priv); + ret = nouveau_mc_create(parent, engine, oclass, &priv); *pobject = nv_object(priv); if (ret) return ret; + priv->base.intr_map = nv98_mc_intr; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c index 4c97cd2e7b56..737bd4b682e1 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c @@ -56,11 +56,12 @@ nvc0_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nvc0_mc_priv *priv; int ret; - ret = nouveau_mc_create(parent, engine, oclass, nvc0_mc_intr, &priv); + ret = nouveau_mc_create(parent, engine, oclass, &priv); *pobject = nv_object(priv); if (ret) return ret; + priv->base.intr_map = nvc0_mc_intr; return 0; } diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index 16023986d301..32501f6ec991 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -50,7 +50,7 @@ static char *pre_emph_names[] = { * or from atom. Note that atom operates on * dw units. */ -void radeon_atom_copy_swap(u8 *dst, u8 *src, u8 num_bytes, bool to_le) +static void radeon_copy_swap(u8 *dst, u8 *src, u8 num_bytes, bool to_le) { #ifdef __BIG_ENDIAN u8 src_tmp[20], dst_tmp[20]; /* used for byteswapping */ @@ -100,7 +100,7 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan, base = (unsigned char *)(rdev->mode_info.atom_context->scratch + 1); - radeon_atom_copy_swap(base, send, send_bytes, true); + radeon_copy_swap(base, send, send_bytes, true); args.v1.lpAuxRequest = cpu_to_le16((u16)(0 + 4)); args.v1.lpDataOut = cpu_to_le16((u16)(16 + 4)); @@ -137,7 +137,7 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan, recv_bytes = recv_size; if (recv && recv_size) - radeon_atom_copy_swap(recv, base + 16, recv_bytes, false); + radeon_copy_swap(recv, base + 16, recv_bytes, false); return recv_bytes; } diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c index c272d8832605..4120d355cadd 100644 --- a/drivers/gpu/drm/radeon/atombios_encoders.c +++ b/drivers/gpu/drm/radeon/atombios_encoders.c @@ -1636,12 +1636,8 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode) atombios_dig_encoder_setup(encoder, ATOM_ENABLE, 0); atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_SETUP, 0, 0); atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0); - /* some dce3.x boards have a bug in their transmitter control table. - * ACTION_ENABLE_OUTPUT can probably be dropped since ACTION_ENABLE - * does the same thing and more. - */ - if ((rdev->family != CHIP_RV710) && (rdev->family != CHIP_RV730) && - (rdev->family != CHIP_RS880)) + /* some early dce3.2 boards have a bug in their transmitter control table */ + if ((rdev->family != CHIP_RV710) && (rdev->family != CHIP_RV730)) atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT, 0, 0); } if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(encoder)) && connector) { diff --git a/drivers/gpu/drm/radeon/atombios_i2c.c b/drivers/gpu/drm/radeon/atombios_i2c.c index 2ca389d19258..082338df708a 100644 --- a/drivers/gpu/drm/radeon/atombios_i2c.c +++ b/drivers/gpu/drm/radeon/atombios_i2c.c @@ -27,8 +27,6 @@ #include "radeon.h" #include "atom.h" -extern void radeon_atom_copy_swap(u8 *dst, u8 *src, u8 num_bytes, bool to_le); - #define TARGET_HW_I2C_CLOCK 50 /* these are a limitation of ProcessI2cChannelTransaction not the hw */ @@ -79,7 +77,7 @@ static int radeon_process_i2c_ch(struct radeon_i2c_chan *chan, } if (!(flags & HW_I2C_WRITE)) - radeon_atom_copy_swap(buf, base, num, false); + memcpy(buf, base, num); return 0; } diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 8b6b0ba6ae40..687b421f75c3 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -1718,8 +1718,7 @@ static u32 evergreen_line_buffer_adjust(struct radeon_device *rdev, struct drm_display_mode *mode, struct drm_display_mode *other_mode) { - u32 tmp, buffer_alloc, i; - u32 pipe_offset = radeon_crtc->crtc_id * 0x20; + u32 tmp; /* * Line Buffer Setup * There are 3 line buffers, each one shared by 2 display controllers. @@ -1742,34 +1741,18 @@ static u32 evergreen_line_buffer_adjust(struct radeon_device *rdev, * non-linked crtcs for maximum line buffer allocation. */ if (radeon_crtc->base.enabled && mode) { - if (other_mode) { + if (other_mode) tmp = 0; /* 1/2 */ - buffer_alloc = 1; - } else { + else tmp = 2; /* whole */ - buffer_alloc = 2; - } - } else { + } else tmp = 0; - buffer_alloc = 0; - } /* second controller of the pair uses second half of the lb */ if (radeon_crtc->crtc_id % 2) tmp += 4; WREG32(DC_LB_MEMORY_SPLIT + radeon_crtc->crtc_offset, tmp); - if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE5(rdev)) { - WREG32(PIPE0_DMIF_BUFFER_CONTROL + pipe_offset, - DMIF_BUFFERS_ALLOCATED(buffer_alloc)); - for (i = 0; i < rdev->usec_timeout; i++) { - if (RREG32(PIPE0_DMIF_BUFFER_CONTROL + pipe_offset) & - DMIF_BUFFERS_ALLOCATED_COMPLETED) - break; - udelay(1); - } - } - if (radeon_crtc->base.enabled && mode) { switch (tmp) { case 0: @@ -2990,7 +2973,7 @@ static void evergreen_gpu_init(struct radeon_device *rdev) rdev->config.evergreen.sx_max_export_size = 256; rdev->config.evergreen.sx_max_export_pos_size = 64; rdev->config.evergreen.sx_max_export_smx_size = 192; - rdev->config.evergreen.max_hw_contexts = 4; + rdev->config.evergreen.max_hw_contexts = 8; rdev->config.evergreen.sq_num_cf_insts = 2; rdev->config.evergreen.sc_prim_fifo_size = 0x40; diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h index 150e3186bec0..75c05631146d 100644 --- a/drivers/gpu/drm/radeon/evergreend.h +++ b/drivers/gpu/drm/radeon/evergreend.h @@ -810,10 +810,6 @@ # define LATENCY_LOW_WATERMARK(x) ((x) << 0) # define LATENCY_HIGH_WATERMARK(x) ((x) << 16) -#define PIPE0_DMIF_BUFFER_CONTROL 0x0ca0 -# define DMIF_BUFFERS_ALLOCATED(x) ((x) << 0) -# define DMIF_BUFFERS_ALLOCATED_COMPLETED (1 << 4) - #define IH_RB_CNTL 0x3e00 # define IH_RB_ENABLE (1 << 0) # define IH_IB_SIZE(x) ((x) << 1) /* log2 */ @@ -1104,7 +1100,7 @@ * 6. COMMAND [29:22] | BYTE_COUNT [20:0] */ # define PACKET3_CP_DMA_DST_SEL(x) ((x) << 20) - /* 0 - DST_ADDR + /* 0 - SRC_ADDR * 1 - GDS */ # define PACKET3_CP_DMA_ENGINE(x) ((x) << 27) @@ -1119,7 +1115,7 @@ # define PACKET3_CP_DMA_CP_SYNC (1 << 31) /* COMMAND */ # define PACKET3_CP_DMA_DIS_WC (1 << 21) -# define PACKET3_CP_DMA_CMD_SRC_SWAP(x) ((x) << 22) +# define PACKET3_CP_DMA_CMD_SRC_SWAP(x) ((x) << 23) /* 0 - none * 1 - 8 in 16 * 2 - 8 in 32 diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index 46470dd7c710..d0314ecbd7c1 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -2935,11 +2935,9 @@ static int r100_debugfs_cp_ring_info(struct seq_file *m, void *data) seq_printf(m, "CP_RB_RPTR 0x%08x\n", rdp); seq_printf(m, "%u free dwords in ring\n", ring->ring_free_dw); seq_printf(m, "%u dwords in ring\n", count); - if (ring->ready) { - for (j = 0; j <= count; j++) { - i = (rdp + j) & ring->ptr_mask; - seq_printf(m, "r[%04d]=0x%08x\n", i, ring->ring[i]); - } + for (j = 0; j <= count; j++) { + i = (rdp + j) & ring->ptr_mask; + seq_printf(m, "r[%04d]=0x%08x\n", i, ring->ring[i]); } return 0; } diff --git a/drivers/gpu/drm/radeon/r600_hdmi.c b/drivers/gpu/drm/radeon/r600_hdmi.c index 0efe2a93ca97..b9b1139da356 100644 --- a/drivers/gpu/drm/radeon/r600_hdmi.c +++ b/drivers/gpu/drm/radeon/r600_hdmi.c @@ -238,19 +238,9 @@ void r600_audio_set_dto(struct drm_encoder *encoder, u32 clock) * number (coefficient of two integer numbers. DCCG_AUDIO_DTOx_PHASE * is the numerator, DCCG_AUDIO_DTOx_MODULE is the denominator */ - if (ASIC_IS_DCE32(rdev)) { - if (dig->dig_encoder == 0) { - WREG32(DCCG_AUDIO_DTO0_PHASE, base_rate * 100); - WREG32(DCCG_AUDIO_DTO0_MODULE, clock * 100); - WREG32(DCCG_AUDIO_DTO_SELECT, 0); /* select DTO0 */ - } else { - WREG32(DCCG_AUDIO_DTO1_PHASE, base_rate * 100); - WREG32(DCCG_AUDIO_DTO1_MODULE, clock * 100); - WREG32(DCCG_AUDIO_DTO_SELECT, 1); /* select DTO1 */ - } - } else if (ASIC_IS_DCE3(rdev)) { + if (ASIC_IS_DCE3(rdev)) { /* according to the reg specs, this should DCE3.2 only, but in - * practice it seems to cover DCE3.0/3.1 as well. + * practice it seems to cover DCE3.0 as well. */ if (dig->dig_encoder == 0) { WREG32(DCCG_AUDIO_DTO0_PHASE, base_rate * 100); @@ -262,7 +252,7 @@ void r600_audio_set_dto(struct drm_encoder *encoder, u32 clock) WREG32(DCCG_AUDIO_DTO_SELECT, 1); /* select DTO1 */ } } else { - /* according to the reg specs, this should be DCE2.0 and DCE3.0/3.1 */ + /* according to the reg specs, this should be DCE2.0 and DCE3.0 */ WREG32(AUDIO_DTO, AUDIO_DTO_PHASE(base_rate / 10) | AUDIO_DTO_MODULE(clock / 10)); } diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h index 2fd2241b6df9..79df558f8c40 100644 --- a/drivers/gpu/drm/radeon/r600d.h +++ b/drivers/gpu/drm/radeon/r600d.h @@ -1259,7 +1259,7 @@ */ # define PACKET3_CP_DMA_CP_SYNC (1 << 31) /* COMMAND */ -# define PACKET3_CP_DMA_CMD_SRC_SWAP(x) ((x) << 22) +# define PACKET3_CP_DMA_CMD_SRC_SWAP(x) ((x) << 23) /* 0 - none * 1 - 8 in 16 * 2 - 8 in 32 diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index d4ff48ce1d8b..bdd9d56812af 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1764,7 +1764,7 @@ void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v); WREG32(reg, tmp_); \ } while (0) #define WREG32_AND(reg, and) WREG32_P(reg, 0, and) -#define WREG32_OR(reg, or) WREG32_P(reg, or, ~(or)) +#define WREG32_OR(reg, or) WREG32_P(reg, or, ~or) #define WREG32_PLL_P(reg, val, mask) \ do { \ uint32_t tmp_ = RREG32_PLL(reg); \ diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 239a4074f122..dea6f63c9724 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -715,16 +715,13 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev) (ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *) (ctx->bios + data_offset + le16_to_cpu(router_obj->asObjects[k].usSrcDstTableOffset)); - u8 *num_dst_objs = (u8 *) - ((u8 *)router_src_dst_table + 1 + - (router_src_dst_table->ucNumberOfSrc * 2)); - u16 *dst_objs = (u16 *)(num_dst_objs + 1); int enum_id; router.router_id = router_obj_id; - for (enum_id = 0; enum_id < (*num_dst_objs); enum_id++) { + for (enum_id = 0; enum_id < router_src_dst_table->ucNumberOfDst; + enum_id++) { if (le16_to_cpu(path->usConnObjectId) == - le16_to_cpu(dst_objs[enum_id])) + le16_to_cpu(router_src_dst_table->usDstObjectID[enum_id])) break; } @@ -1654,9 +1651,7 @@ struct radeon_encoder_atom_dig *radeon_atombios_get_lvds_info(struct kfree(edid); } } - record += fake_edid_record->ucFakeEDIDLength ? - fake_edid_record->ucFakeEDIDLength + 2 : - sizeof(ATOM_FAKE_EDID_PATCH_RECORD); + record += sizeof(ATOM_FAKE_EDID_PATCH_RECORD); break; case LCD_PANEL_RESOLUTION_RECORD_TYPE: panel_res_record = (ATOM_PANEL_RESOLUTION_PATCH_RECORD *)record; diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 5a87c9fc78d3..2399f25ec037 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -1489,24 +1489,6 @@ static const struct drm_connector_funcs radeon_dp_connector_funcs = { .force = radeon_dvi_force, }; -static const struct drm_connector_funcs radeon_edp_connector_funcs = { - .dpms = drm_helper_connector_dpms, - .detect = radeon_dp_detect, - .fill_modes = drm_helper_probe_single_connector_modes, - .set_property = radeon_lvds_set_property, - .destroy = radeon_dp_connector_destroy, - .force = radeon_dvi_force, -}; - -static const struct drm_connector_funcs radeon_lvds_bridge_connector_funcs = { - .dpms = drm_helper_connector_dpms, - .detect = radeon_dp_detect, - .fill_modes = drm_helper_probe_single_connector_modes, - .set_property = radeon_lvds_set_property, - .destroy = radeon_dp_connector_destroy, - .force = radeon_dvi_force, -}; - void radeon_add_atom_connector(struct drm_device *dev, uint32_t connector_id, @@ -1598,6 +1580,8 @@ radeon_add_atom_connector(struct drm_device *dev, goto failed; radeon_dig_connector->igp_lane_info = igp_lane_info; radeon_connector->con_priv = radeon_dig_connector; + drm_connector_init(dev, &radeon_connector->base, &radeon_dp_connector_funcs, connector_type); + drm_connector_helper_add(&radeon_connector->base, &radeon_dp_connector_helper_funcs); if (i2c_bus->valid) { /* add DP i2c bus */ if (connector_type == DRM_MODE_CONNECTOR_eDP) @@ -1614,10 +1598,6 @@ radeon_add_atom_connector(struct drm_device *dev, case DRM_MODE_CONNECTOR_VGA: case DRM_MODE_CONNECTOR_DVIA: default: - drm_connector_init(dev, &radeon_connector->base, - &radeon_dp_connector_funcs, connector_type); - drm_connector_helper_add(&radeon_connector->base, - &radeon_dp_connector_helper_funcs); connector->interlace_allowed = true; connector->doublescan_allowed = true; radeon_connector->dac_load_detect = true; @@ -1630,10 +1610,6 @@ radeon_add_atom_connector(struct drm_device *dev, case DRM_MODE_CONNECTOR_HDMIA: case DRM_MODE_CONNECTOR_HDMIB: case DRM_MODE_CONNECTOR_DisplayPort: - drm_connector_init(dev, &radeon_connector->base, - &radeon_dp_connector_funcs, connector_type); - drm_connector_helper_add(&radeon_connector->base, - &radeon_dp_connector_helper_funcs); drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.underscan_property, UNDERSCAN_OFF); @@ -1658,10 +1634,6 @@ radeon_add_atom_connector(struct drm_device *dev, break; case DRM_MODE_CONNECTOR_LVDS: case DRM_MODE_CONNECTOR_eDP: - drm_connector_init(dev, &radeon_connector->base, - &radeon_lvds_bridge_connector_funcs, connector_type); - drm_connector_helper_add(&radeon_connector->base, - &radeon_dp_connector_helper_funcs); drm_object_attach_property(&radeon_connector->base.base, dev->mode_config.scaling_mode_property, DRM_MODE_SCALE_FULLSCREEN); @@ -1825,7 +1797,7 @@ radeon_add_atom_connector(struct drm_device *dev, goto failed; radeon_dig_connector->igp_lane_info = igp_lane_info; radeon_connector->con_priv = radeon_dig_connector; - drm_connector_init(dev, &radeon_connector->base, &radeon_edp_connector_funcs, connector_type); + drm_connector_init(dev, &radeon_connector->base, &radeon_dp_connector_funcs, connector_type); drm_connector_helper_add(&radeon_connector->base, &radeon_dp_connector_helper_funcs); if (i2c_bus->valid) { /* add DP i2c bus */ diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index fe36f1d9496d..7e265a58141f 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -80,11 +80,9 @@ static int radeon_cs_parser_relocs(struct radeon_cs_parser *p) p->relocs[i].lobj.bo = p->relocs[i].robj; p->relocs[i].lobj.written = !!r->write_domain; - /* the first reloc of an UVD job is the msg and that must be in - VRAM, also but everything into VRAM on AGP cards to avoid - image corruptions */ - if (p->ring == R600_RING_TYPE_UVD_INDEX && - (i == 0 || drm_pci_device_is_agp(p->rdev->ddev))) { + /* the first reloc of an UVD job is the + msg and that must be in VRAM */ + if (p->ring == R600_RING_TYPE_UVD_INDEX && i == 0) { /* TODO: is this still needed for NI+ ? */ p->relocs[i].lobj.domain = RADEON_GEM_DOMAIN_VRAM; diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 8df1525f71d2..b0dc0b6cb4e0 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -1196,22 +1196,13 @@ int radeon_device_init(struct radeon_device *rdev, return r; } if ((radeon_testing & 1)) { - if (rdev->accel_working) - radeon_test_moves(rdev); - else - DRM_INFO("radeon: acceleration disabled, skipping move tests\n"); + radeon_test_moves(rdev); } if ((radeon_testing & 2)) { - if (rdev->accel_working) - radeon_test_syncing(rdev); - else - DRM_INFO("radeon: acceleration disabled, skipping sync tests\n"); + radeon_test_syncing(rdev); } if (radeon_benchmarking) { - if (rdev->accel_working) - radeon_benchmark(rdev, radeon_benchmarking); - else - DRM_INFO("radeon: acceleration disabled, skipping benchmarks\n"); + radeon_benchmark(rdev, radeon_benchmarking); } return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 7e292d899209..4f2d4f4c1dab 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -414,9 +414,6 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) value = rdev->config.si.tile_mode_array; value_size = sizeof(uint32_t)*32; break; - case RADEON_INFO_SI_CP_DMA_COMPUTE: - *value = 1; - break; default: DRM_DEBUG_KMS("Invalid request %d\n", info->request); return -EINVAL; diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c index 6e0f4809bda0..83f6295a0e5e 100644 --- a/drivers/gpu/drm/radeon/radeon_ring.c +++ b/drivers/gpu/drm/radeon/radeon_ring.c @@ -823,11 +823,9 @@ static int radeon_debugfs_ring_info(struct seq_file *m, void *data) * packet that is the root issue */ i = (ring->rptr + ring->ptr_mask + 1 - 32) & ring->ptr_mask; - if (ring->ready) { - for (j = 0; j <= (count + 32); j++) { - seq_printf(m, "r[%5d]=0x%08x\n", i, ring->ring[i]); - i = (i + 1) & ring->ptr_mask; - } + for (j = 0; j <= (count + 32); j++) { + seq_printf(m, "r[%5d]=0x%08x\n", i, ring->ring[i]); + i = (i + 1) & ring->ptr_mask; } return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_test.c b/drivers/gpu/drm/radeon/radeon_test.c index f9ebf2bf8b57..bbed4af8d0bc 100644 --- a/drivers/gpu/drm/radeon/radeon_test.c +++ b/drivers/gpu/drm/radeon/radeon_test.c @@ -37,8 +37,8 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag) struct radeon_bo **gtt_obj = NULL; struct radeon_fence *fence = NULL; uint64_t gtt_addr, vram_addr; - unsigned n, size; - int i, r, ring; + unsigned i, n, size; + int r, ring; switch (flag) { case RADEON_TEST_COPY_DMA: diff --git a/drivers/gpu/drm/radeon/radeon_uvd.c b/drivers/gpu/drm/radeon/radeon_uvd.c index f3ccf6d4addb..97002a08211f 100644 --- a/drivers/gpu/drm/radeon/radeon_uvd.c +++ b/drivers/gpu/drm/radeon/radeon_uvd.c @@ -359,14 +359,6 @@ static int radeon_uvd_cs_msg(struct radeon_cs_parser *p, struct radeon_bo *bo, return -EINVAL; } - if (bo->tbo.sync_obj) { - r = radeon_fence_wait(bo->tbo.sync_obj, false); - if (r) { - DRM_ERROR("Failed waiting for UVD message (%d)!\n", r); - return r; - } - } - r = radeon_bo_kmap(bo, &ptr); if (r) return r; diff --git a/drivers/gpu/drm/radeon/rs400.c b/drivers/gpu/drm/radeon/rs400.c index b8074a8ec75a..233a9b9fa1f7 100644 --- a/drivers/gpu/drm/radeon/rs400.c +++ b/drivers/gpu/drm/radeon/rs400.c @@ -174,13 +174,10 @@ int rs400_gart_enable(struct radeon_device *rdev) /* FIXME: according to doc we should set HIDE_MMCFG_BAR=0, * AGPMODE30=0 & AGP30ENHANCED=0 in NB_CNTL */ if ((rdev->family == CHIP_RS690) || (rdev->family == CHIP_RS740)) { - tmp = RREG32_MC(RS480_MC_MISC_CNTL); - tmp |= RS480_GART_INDEX_REG_EN | RS690_BLOCK_GFX_D3_EN; - WREG32_MC(RS480_MC_MISC_CNTL, tmp); + WREG32_MC(RS480_MC_MISC_CNTL, + (RS480_GART_INDEX_REG_EN | RS690_BLOCK_GFX_D3_EN)); } else { - tmp = RREG32_MC(RS480_MC_MISC_CNTL); - tmp |= RS480_GART_INDEX_REG_EN; - WREG32_MC(RS480_MC_MISC_CNTL, tmp); + WREG32_MC(RS480_MC_MISC_CNTL, RS480_GART_INDEX_REG_EN); } /* Enable gart */ WREG32_MC(RS480_AGP_ADDRESS_SPACE_SIZE, (RS480_GART_EN | size_reg)); diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index f5e92cfcc140..bcc68ec204ad 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c @@ -744,10 +744,10 @@ static void rv770_init_golden_registers(struct radeon_device *rdev) (const u32)ARRAY_SIZE(r7xx_golden_dyn_gpr_registers)); radeon_program_register_sequence(rdev, rv730_golden_registers, - (const u32)ARRAY_SIZE(rv730_golden_registers)); + (const u32)ARRAY_SIZE(rv770_golden_registers)); radeon_program_register_sequence(rdev, rv730_mgcg_init, - (const u32)ARRAY_SIZE(rv730_mgcg_init)); + (const u32)ARRAY_SIZE(rv770_mgcg_init)); break; case CHIP_RV710: radeon_program_register_sequence(rdev, @@ -758,18 +758,18 @@ static void rv770_init_golden_registers(struct radeon_device *rdev) (const u32)ARRAY_SIZE(r7xx_golden_dyn_gpr_registers)); radeon_program_register_sequence(rdev, rv710_golden_registers, - (const u32)ARRAY_SIZE(rv710_golden_registers)); + (const u32)ARRAY_SIZE(rv770_golden_registers)); radeon_program_register_sequence(rdev, rv710_mgcg_init, - (const u32)ARRAY_SIZE(rv710_mgcg_init)); + (const u32)ARRAY_SIZE(rv770_mgcg_init)); break; case CHIP_RV740: radeon_program_register_sequence(rdev, rv740_golden_registers, - (const u32)ARRAY_SIZE(rv740_golden_registers)); + (const u32)ARRAY_SIZE(rv770_golden_registers)); radeon_program_register_sequence(rdev, rv740_mgcg_init, - (const u32)ARRAY_SIZE(rv740_mgcg_init)); + (const u32)ARRAY_SIZE(rv770_mgcg_init)); break; default: break; diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index f054a3b6c4b8..1a96a16b9996 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -1467,8 +1467,7 @@ static u32 dce6_line_buffer_adjust(struct radeon_device *rdev, struct drm_display_mode *mode, struct drm_display_mode *other_mode) { - u32 tmp, buffer_alloc, i; - u32 pipe_offset = radeon_crtc->crtc_id * 0x20; + u32 tmp; /* * Line Buffer Setup * There are 3 line buffers, each one shared by 2 display controllers. @@ -1483,30 +1482,16 @@ static u32 dce6_line_buffer_adjust(struct radeon_device *rdev, * non-linked crtcs for maximum line buffer allocation. */ if (radeon_crtc->base.enabled && mode) { - if (other_mode) { + if (other_mode) tmp = 0; /* 1/2 */ - buffer_alloc = 1; - } else { + else tmp = 2; /* whole */ - buffer_alloc = 2; - } - } else { + } else tmp = 0; - buffer_alloc = 0; - } WREG32(DC_LB_MEMORY_SPLIT + radeon_crtc->crtc_offset, DC_LB_MEMORY_CONFIG(tmp)); - WREG32(PIPE0_DMIF_BUFFER_CONTROL + pipe_offset, - DMIF_BUFFERS_ALLOCATED(buffer_alloc)); - for (i = 0; i < rdev->usec_timeout; i++) { - if (RREG32(PIPE0_DMIF_BUFFER_CONTROL + pipe_offset) & - DMIF_BUFFERS_ALLOCATED_COMPLETED) - break; - udelay(1); - } - if (radeon_crtc->base.enabled && mode) { switch (tmp) { case 0: @@ -3811,64 +3796,13 @@ static int si_vm_packet3_ce_check(struct radeon_device *rdev, return 0; } -static int si_vm_packet3_cp_dma_check(u32 *ib, u32 idx) -{ - u32 start_reg, reg, i; - u32 command = ib[idx + 4]; - u32 info = ib[idx + 1]; - u32 idx_value = ib[idx]; - if (command & PACKET3_CP_DMA_CMD_SAS) { - /* src address space is register */ - if (((info & 0x60000000) >> 29) == 0) { - start_reg = idx_value << 2; - if (command & PACKET3_CP_DMA_CMD_SAIC) { - reg = start_reg; - if (!si_vm_reg_valid(reg)) { - DRM_ERROR("CP DMA Bad SRC register\n"); - return -EINVAL; - } - } else { - for (i = 0; i < (command & 0x1fffff); i++) { - reg = start_reg + (4 * i); - if (!si_vm_reg_valid(reg)) { - DRM_ERROR("CP DMA Bad SRC register\n"); - return -EINVAL; - } - } - } - } - } - if (command & PACKET3_CP_DMA_CMD_DAS) { - /* dst address space is register */ - if (((info & 0x00300000) >> 20) == 0) { - start_reg = ib[idx + 2]; - if (command & PACKET3_CP_DMA_CMD_DAIC) { - reg = start_reg; - if (!si_vm_reg_valid(reg)) { - DRM_ERROR("CP DMA Bad DST register\n"); - return -EINVAL; - } - } else { - for (i = 0; i < (command & 0x1fffff); i++) { - reg = start_reg + (4 * i); - if (!si_vm_reg_valid(reg)) { - DRM_ERROR("CP DMA Bad DST register\n"); - return -EINVAL; - } - } - } - } - } - return 0; -} - static int si_vm_packet3_gfx_check(struct radeon_device *rdev, u32 *ib, struct radeon_cs_packet *pkt) { - int r; u32 idx = pkt->idx + 1; u32 idx_value = ib[idx]; u32 start_reg, end_reg, reg, i; + u32 command, info; switch (pkt->opcode) { case PACKET3_NOP: @@ -3969,9 +3903,50 @@ static int si_vm_packet3_gfx_check(struct radeon_device *rdev, } break; case PACKET3_CP_DMA: - r = si_vm_packet3_cp_dma_check(ib, idx); - if (r) - return r; + command = ib[idx + 4]; + info = ib[idx + 1]; + if (command & PACKET3_CP_DMA_CMD_SAS) { + /* src address space is register */ + if (((info & 0x60000000) >> 29) == 0) { + start_reg = idx_value << 2; + if (command & PACKET3_CP_DMA_CMD_SAIC) { + reg = start_reg; + if (!si_vm_reg_valid(reg)) { + DRM_ERROR("CP DMA Bad SRC register\n"); + return -EINVAL; + } + } else { + for (i = 0; i < (command & 0x1fffff); i++) { + reg = start_reg + (4 * i); + if (!si_vm_reg_valid(reg)) { + DRM_ERROR("CP DMA Bad SRC register\n"); + return -EINVAL; + } + } + } + } + } + if (command & PACKET3_CP_DMA_CMD_DAS) { + /* dst address space is register */ + if (((info & 0x00300000) >> 20) == 0) { + start_reg = ib[idx + 2]; + if (command & PACKET3_CP_DMA_CMD_DAIC) { + reg = start_reg; + if (!si_vm_reg_valid(reg)) { + DRM_ERROR("CP DMA Bad DST register\n"); + return -EINVAL; + } + } else { + for (i = 0; i < (command & 0x1fffff); i++) { + reg = start_reg + (4 * i); + if (!si_vm_reg_valid(reg)) { + DRM_ERROR("CP DMA Bad DST register\n"); + return -EINVAL; + } + } + } + } + } break; default: DRM_ERROR("Invalid GFX packet3: 0x%x\n", pkt->opcode); @@ -3983,7 +3958,6 @@ static int si_vm_packet3_gfx_check(struct radeon_device *rdev, static int si_vm_packet3_compute_check(struct radeon_device *rdev, u32 *ib, struct radeon_cs_packet *pkt) { - int r; u32 idx = pkt->idx + 1; u32 idx_value = ib[idx]; u32 start_reg, reg, i; @@ -4056,11 +4030,6 @@ static int si_vm_packet3_compute_check(struct radeon_device *rdev, return -EINVAL; } break; - case PACKET3_CP_DMA: - r = si_vm_packet3_cp_dma_check(ib, idx); - if (r) - return r; - break; default: DRM_ERROR("Invalid Compute packet3: 0x%x\n", pkt->opcode); return -EINVAL; diff --git a/drivers/gpu/drm/radeon/sid.h b/drivers/gpu/drm/radeon/sid.h index 495f41f2900b..8f2d7d4f9b28 100644 --- a/drivers/gpu/drm/radeon/sid.h +++ b/drivers/gpu/drm/radeon/sid.h @@ -97,10 +97,6 @@ #define DMIF_ADDR_CALC 0xC00 -#define PIPE0_DMIF_BUFFER_CONTROL 0x0ca0 -# define DMIF_BUFFERS_ALLOCATED(x) ((x) << 0) -# define DMIF_BUFFERS_ALLOCATED_COMPLETED (1 << 4) - #define SRBM_STATUS 0xE50 #define GRBM_RQ_PENDING (1 << 5) #define VMC_BUSY (1 << 8) @@ -928,7 +924,7 @@ * 6. COMMAND [30:21] | BYTE_COUNT [20:0] */ # define PACKET3_CP_DMA_DST_SEL(x) ((x) << 20) - /* 0 - DST_ADDR + /* 0 - SRC_ADDR * 1 - GDS */ # define PACKET3_CP_DMA_ENGINE(x) ((x) << 27) @@ -943,7 +939,7 @@ # define PACKET3_CP_DMA_CP_SYNC (1 << 31) /* COMMAND */ # define PACKET3_CP_DMA_DIS_WC (1 << 21) -# define PACKET3_CP_DMA_CMD_SRC_SWAP(x) ((x) << 22) +# define PACKET3_CP_DMA_CMD_SRC_SWAP(x) ((x) << 23) /* 0 - none * 1 - 8 in 16 * 2 - 8 in 32 diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c index 210d50365162..5e93a52d4f2c 100644 --- a/drivers/gpu/drm/ttm/ttm_tt.c +++ b/drivers/gpu/drm/ttm/ttm_tt.c @@ -170,7 +170,7 @@ void ttm_tt_destroy(struct ttm_tt *ttm) ttm_tt_unbind(ttm); } - if (ttm->state == tt_unbound) { + if (likely(ttm->pages != NULL)) { ttm->bdev->driver->ttm_tt_unpopulate(ttm); } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c index 1a0bf07fe54b..3751730764a5 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c @@ -29,9 +29,7 @@ #include #include -#define VMW_PPN_SIZE (sizeof(unsigned long)) -/* A future safe maximum remap size. */ -#define VMW_PPN_PER_REMAP ((31 * 1024) / VMW_PPN_SIZE) +#define VMW_PPN_SIZE sizeof(unsigned long) static int vmw_gmr2_bind(struct vmw_private *dev_priv, struct page *pages[], @@ -40,61 +38,43 @@ static int vmw_gmr2_bind(struct vmw_private *dev_priv, { SVGAFifoCmdDefineGMR2 define_cmd; SVGAFifoCmdRemapGMR2 remap_cmd; + uint32_t define_size = sizeof(define_cmd) + 4; + uint32_t remap_size = VMW_PPN_SIZE * num_pages + sizeof(remap_cmd) + 4; uint32_t *cmd; uint32_t *cmd_orig; - uint32_t define_size = sizeof(define_cmd) + sizeof(*cmd); - uint32_t remap_num = num_pages / VMW_PPN_PER_REMAP + ((num_pages % VMW_PPN_PER_REMAP) > 0); - uint32_t remap_size = VMW_PPN_SIZE * num_pages + (sizeof(remap_cmd) + sizeof(*cmd)) * remap_num; - uint32_t remap_pos = 0; - uint32_t cmd_size = define_size + remap_size; uint32_t i; - cmd_orig = cmd = vmw_fifo_reserve(dev_priv, cmd_size); + cmd_orig = cmd = vmw_fifo_reserve(dev_priv, define_size + remap_size); if (unlikely(cmd == NULL)) return -ENOMEM; define_cmd.gmrId = gmr_id; define_cmd.numPages = num_pages; - *cmd++ = SVGA_CMD_DEFINE_GMR2; - memcpy(cmd, &define_cmd, sizeof(define_cmd)); - cmd += sizeof(define_cmd) / sizeof(*cmd); - - /* - * Need to split the command if there are too many - * pages that goes into the gmr. - */ - remap_cmd.gmrId = gmr_id; remap_cmd.flags = (VMW_PPN_SIZE > sizeof(*cmd)) ? SVGA_REMAP_GMR2_PPN64 : SVGA_REMAP_GMR2_PPN32; + remap_cmd.offsetPages = 0; + remap_cmd.numPages = num_pages; - while (num_pages > 0) { - unsigned long nr = min(num_pages, (unsigned long)VMW_PPN_PER_REMAP); - - remap_cmd.offsetPages = remap_pos; - remap_cmd.numPages = nr; + *cmd++ = SVGA_CMD_DEFINE_GMR2; + memcpy(cmd, &define_cmd, sizeof(define_cmd)); + cmd += sizeof(define_cmd) / sizeof(uint32); - *cmd++ = SVGA_CMD_REMAP_GMR2; - memcpy(cmd, &remap_cmd, sizeof(remap_cmd)); - cmd += sizeof(remap_cmd) / sizeof(*cmd); + *cmd++ = SVGA_CMD_REMAP_GMR2; + memcpy(cmd, &remap_cmd, sizeof(remap_cmd)); + cmd += sizeof(remap_cmd) / sizeof(uint32); - for (i = 0; i < nr; ++i) { - if (VMW_PPN_SIZE <= 4) - *cmd = page_to_pfn(*pages++); - else - *((uint64_t *)cmd) = page_to_pfn(*pages++); + for (i = 0; i < num_pages; ++i) { + if (VMW_PPN_SIZE <= 4) + *cmd = page_to_pfn(*pages++); + else + *((uint64_t *)cmd) = page_to_pfn(*pages++); - cmd += VMW_PPN_SIZE / sizeof(*cmd); - } - - num_pages -= nr; - remap_pos += nr; + cmd += VMW_PPN_SIZE / sizeof(*cmd); } - BUG_ON(cmd != cmd_orig + cmd_size / sizeof(*cmd)); - - vmw_fifo_commit(dev_priv, cmd_size); + vmw_fifo_commit(dev_priv, define_size + remap_size); return 0; } diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index e04bf0ef93a9..402f48689943 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -63,8 +63,6 @@ struct hid_report *hid_register_report(struct hid_device *device, unsigned type, struct hid_report_enum *report_enum = device->report_enum + type; struct hid_report *report; - if (id >= HID_MAX_IDS) - return NULL; if (report_enum->report_id_hash[id]) return report_enum->report_id_hash[id]; @@ -94,6 +92,7 @@ EXPORT_SYMBOL_GPL(hid_register_report); static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages, unsigned values) { struct hid_field *field; + int i; if (report->maxfield == HID_MAX_FIELDS) { hid_err(report->device, "too many fields in report\n"); @@ -112,6 +111,9 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned field->value = (s32 *)(field->usage + usages); field->report = report; + for (i = 0; i < usages; i++) + field->usage[i].usage_index = i; + return field; } @@ -222,9 +224,9 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign { struct hid_report *report; struct hid_field *field; - unsigned usages; + int usages; unsigned offset; - unsigned i; + int i; report = hid_register_report(parser->device, report_type, parser->global.report_id); if (!report) { @@ -251,8 +253,7 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign if (!parser->local.usage_index) /* Ignore padding fields */ return 0; - usages = max_t(unsigned, parser->local.usage_index, - parser->global.report_count); + usages = max_t(int, parser->local.usage_index, parser->global.report_count); field = hid_register_field(report, usages, parser->global.report_count); if (!field) @@ -263,14 +264,13 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign field->application = hid_lookup_collection(parser, HID_COLLECTION_APPLICATION); for (i = 0; i < usages; i++) { - unsigned j = i; + int j = i; /* Duplicate the last usage we parsed if we have excess values */ if (i >= parser->local.usage_index) j = parser->local.usage_index - 1; field->usage[i].hid = parser->local.usage[j]; field->usage[i].collection_index = parser->local.collection_index[j]; - field->usage[i].usage_index = i; } field->maxusage = usages; @@ -404,10 +404,8 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) case HID_GLOBAL_ITEM_TAG_REPORT_ID: parser->global.report_id = item_udata(item); - if (parser->global.report_id == 0 || - parser->global.report_id >= HID_MAX_IDS) { - hid_err(parser->device, "report_id %u is invalid\n", - parser->global.report_id); + if (parser->global.report_id == 0) { + hid_err(parser->device, "report_id 0 is invalid\n"); return -1; } return 0; @@ -577,7 +575,7 @@ static void hid_close_report(struct hid_device *device) for (i = 0; i < HID_REPORT_TYPES; i++) { struct hid_report_enum *report_enum = device->report_enum + i; - for (j = 0; j < HID_MAX_IDS; j++) { + for (j = 0; j < 256; j++) { struct hid_report *report = report_enum->report_id_hash[j]; if (report) hid_free_report(report); @@ -757,64 +755,6 @@ int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size) } EXPORT_SYMBOL_GPL(hid_parse_report); -static const char * const hid_report_names[] = { - "HID_INPUT_REPORT", - "HID_OUTPUT_REPORT", - "HID_FEATURE_REPORT", -}; -/** - * hid_validate_values - validate existing device report's value indexes - * - * @device: hid device - * @type: which report type to examine - * @id: which report ID to examine (0 for first) - * @field_index: which report field to examine - * @report_counts: expected number of values - * - * Validate the number of values in a given field of a given report, after - * parsing. - */ -struct hid_report *hid_validate_values(struct hid_device *hid, - unsigned int type, unsigned int id, - unsigned int field_index, - unsigned int report_counts) -{ - struct hid_report *report; - - if (type > HID_FEATURE_REPORT) { - hid_err(hid, "invalid HID report type %u\n", type); - return NULL; - } - - if (id >= HID_MAX_IDS) { - hid_err(hid, "invalid HID report id %u\n", id); - return NULL; - } - - /* - * Explicitly not using hid_get_report() here since it depends on - * ->numbered being checked, which may not always be the case when - * drivers go to access report values. - */ - report = hid->report_enum[type].report_id_hash[id]; - if (!report) { - hid_err(hid, "missing %s %u\n", hid_report_names[type], id); - return NULL; - } - if (report->maxfield <= field_index) { - hid_err(hid, "not enough fields in %s %u\n", - hid_report_names[type], id); - return NULL; - } - if (report->field[field_index]->report_count < report_counts) { - hid_err(hid, "not enough values in %s %u field %u\n", - hid_report_names[type], id, field_index); - return NULL; - } - return report; -} -EXPORT_SYMBOL_GPL(hid_validate_values); - /** * hid_open_report - open a driver-specific device report * @@ -1188,8 +1128,7 @@ static void hid_output_field(const struct hid_device *hid, } /* - * Create a report. 'data' has to be allocated using - * hid_alloc_report_buf() so that it has proper size. + * Create a report. */ void hid_output_report(struct hid_report *report, __u8 *data) @@ -1205,22 +1144,6 @@ void hid_output_report(struct hid_report *report, __u8 *data) } EXPORT_SYMBOL_GPL(hid_output_report); -/* - * Allocator for buffer that is going to be passed to hid_output_report() - */ -u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags) -{ - /* - * 7 extra bytes are necessary to achieve proper functionality - * of implement() working on 8 byte chunks - */ - - int len = ((report->size - 1) >> 3) + 1 + (report->id > 0) + 7; - - return kmalloc(len, flags); -} -EXPORT_SYMBOL_GPL(hid_alloc_report_buf); - /* * Set a field value. The report this field belongs to has to be * created and transferred to the device, to set this value in the @@ -1229,12 +1152,7 @@ EXPORT_SYMBOL_GPL(hid_alloc_report_buf); int hid_set_field(struct hid_field *field, unsigned offset, __s32 value) { - unsigned size; - - if (!field) - return -1; - - size = field->report_size; + unsigned size = field->report_size; hid_dump_input(field->report->device, field->usage + offset, value); @@ -1310,7 +1228,7 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, goto out; } - if (hid->claimed != HID_CLAIMED_HIDRAW && report->maxfield) { + if (hid->claimed != HID_CLAIMED_HIDRAW) { for (a = 0; a < report->maxfield; a++) hid_input_field(hid, report->field[a], cdata, interrupt); hdrv = hid->driver; @@ -1755,7 +1673,6 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE) }, - { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE_OPTICAL) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_LUA) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 114fd1661834..216888538c50 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -135,9 +135,9 @@ #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI 0x0255 #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO 0x0256 -#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0290 -#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0291 -#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0292 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0291 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0292 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0293 #define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b #define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240 @@ -646,7 +646,6 @@ #define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_16 0x0012 #define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_17 0x0013 #define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_18 0x0014 -#define USB_DEVICE_ID_NTRIG_DUOSENSE 0x1500 #define USB_VENDOR_ID_ONTRAK 0x0a07 #define USB_DEVICE_ID_ONTRAK_ADU100 0x0064 @@ -705,7 +704,6 @@ #define USB_DEVICE_ID_ROCCAT_KONE 0x2ced #define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51 #define USB_DEVICE_ID_ROCCAT_KONEPURE 0x2dbe -#define USB_DEVICE_ID_ROCCAT_KONEPURE_OPTICAL 0x2db4 #define USB_DEVICE_ID_ROCCAT_KONEXTD 0x2e22 #define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50 #define USB_DEVICE_ID_ROCCAT_LUA 0x2c2e diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 012880a2228c..945b8158ec4c 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -340,7 +340,7 @@ static int hidinput_get_battery_property(struct power_supply *psy, { struct hid_device *dev = container_of(psy, struct hid_device, battery); int ret = 0; - __u8 *buf; + __u8 buf[2] = {}; switch (prop) { case POWER_SUPPLY_PROP_PRESENT: @@ -349,20 +349,13 @@ static int hidinput_get_battery_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CAPACITY: - - buf = kmalloc(2 * sizeof(__u8), GFP_KERNEL); - if (!buf) { - ret = -ENOMEM; - break; - } ret = dev->hid_get_raw_report(dev, dev->battery_report_id, - buf, 2, + buf, sizeof(buf), dev->battery_report_type); if (ret != 2) { if (ret >= 0) ret = -EINVAL; - kfree(buf); break; } @@ -371,7 +364,6 @@ static int hidinput_get_battery_property(struct power_supply *psy, buf[1] <= dev->battery_max) val->intval = (100 * (buf[1] - dev->battery_min)) / (dev->battery_max - dev->battery_min); - kfree(buf); break; case POWER_SUPPLY_PROP_MODEL_NAME: @@ -485,10 +477,6 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel if (field->flags & HID_MAIN_ITEM_CONSTANT) goto ignore; - /* Ignore if report count is out of bounds. */ - if (field->report_count < 1) - goto ignore; - /* only LED usages are supported in output fields */ if (field->report_type == HID_OUTPUT_REPORT && (usage->hid & HID_USAGE_PAGE) != HID_UP_LED) { @@ -1167,11 +1155,7 @@ static void report_features(struct hid_device *hid) rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; list_for_each_entry(rep, &rep_enum->report_list, list) - for (i = 0; i < rep->maxfield; i++) { - /* Ignore if report count is out of bounds. */ - if (rep->field[i]->report_count < 1) - continue; - + for (i = 0; i < rep->maxfield; i++) for (j = 0; j < rep->field[i]->maxusage; j++) { /* Verify if Battery Strength feature is available */ hidinput_setup_battery(hid, HID_FEATURE_REPORT, rep->field[i]); @@ -1180,7 +1164,6 @@ static void report_features(struct hid_device *hid) drv->feature_mapping(hid, rep->field[i], rep->field[i]->usage + j); } - } } static struct hid_input *hidinput_allocate(struct hid_device *hid) diff --git a/drivers/hid/hid-lenovo-tpkbd.c b/drivers/hid/hid-lenovo-tpkbd.c index 31cf29a6ba17..07837f5a4eb8 100644 --- a/drivers/hid/hid-lenovo-tpkbd.c +++ b/drivers/hid/hid-lenovo-tpkbd.c @@ -339,15 +339,7 @@ static int tpkbd_probe_tp(struct hid_device *hdev) struct tpkbd_data_pointer *data_pointer; size_t name_sz = strlen(dev_name(dev)) + 16; char *name_mute, *name_micmute; - int i, ret; - - /* Validate required reports. */ - for (i = 0; i < 4; i++) { - if (!hid_validate_values(hdev, HID_FEATURE_REPORT, 4, i, 1)) - return -ENODEV; - } - if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 3, 0, 2)) - return -ENODEV; + int ret; if (sysfs_create_group(&hdev->dev.kobj, &tpkbd_attr_group_pointer)) { @@ -414,27 +406,22 @@ static int tpkbd_probe(struct hid_device *hdev, ret = hid_parse(hdev); if (ret) { hid_err(hdev, "hid_parse failed\n"); - goto err; + goto err_free; } ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) { hid_err(hdev, "hid_hw_start failed\n"); - goto err; + goto err_free; } uhdev = (struct usbhid_device *) hdev->driver_data; - if (uhdev->ifnum == 1) { - ret = tpkbd_probe_tp(hdev); - if (ret) - goto err_hid; - } + if (uhdev->ifnum == 1) + return tpkbd_probe_tp(hdev); return 0; -err_hid: - hid_hw_stop(hdev); -err: +err_free: return ret; } diff --git a/drivers/hid/hid-lg2ff.c b/drivers/hid/hid-lg2ff.c index 1a42eaa6ca02..b3cd1507dda2 100644 --- a/drivers/hid/hid-lg2ff.c +++ b/drivers/hid/hid-lg2ff.c @@ -64,13 +64,26 @@ int lg2ff_init(struct hid_device *hid) struct hid_report *report; struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); + struct list_head *report_list = + &hid->report_enum[HID_OUTPUT_REPORT].report_list; struct input_dev *dev = hidinput->input; int error; - /* Check that the report looks ok */ - report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7); - if (!report) + if (list_empty(report_list)) { + hid_err(hid, "no output report found\n"); return -ENODEV; + } + + report = list_entry(report_list->next, struct hid_report, list); + + if (report->maxfield < 1) { + hid_err(hid, "output report is empty\n"); + return -ENODEV; + } + if (report->field[0]->report_count < 7) { + hid_err(hid, "not enough values in the field\n"); + return -ENODEV; + } lg2ff = kmalloc(sizeof(struct lg2ff_device), GFP_KERNEL); if (!lg2ff) diff --git a/drivers/hid/hid-lg3ff.c b/drivers/hid/hid-lg3ff.c index 8c2da183d3bc..e52f181f6aa1 100644 --- a/drivers/hid/hid-lg3ff.c +++ b/drivers/hid/hid-lg3ff.c @@ -66,11 +66,10 @@ static int hid_lg3ff_play(struct input_dev *dev, void *data, int x, y; /* - * Available values in the field should always be 63, but we only use up to - * 35. Instead, clear the entire area, however big it is. + * Maxusage should always be 63 (maximum fields) + * likely a better way to ensure this data is clean */ - memset(report->field[0]->value, 0, - sizeof(__s32) * report->field[0]->report_count); + memset(report->field[0]->value, 0, sizeof(__s32)*report->field[0]->maxusage); switch (effect->type) { case FF_CONSTANT: @@ -130,14 +129,32 @@ static const signed short ff3_joystick_ac[] = { int lg3ff_init(struct hid_device *hid) { struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); + struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; struct input_dev *dev = hidinput->input; + struct hid_report *report; + struct hid_field *field; const signed short *ff_bits = ff3_joystick_ac; int error; int i; + /* Find the report to use */ + if (list_empty(report_list)) { + hid_err(hid, "No output report found\n"); + return -1; + } + /* Check that the report looks ok */ - if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 35)) - return -ENODEV; + report = list_entry(report_list->next, struct hid_report, list); + if (!report) { + hid_err(hid, "NULL output report\n"); + return -1; + } + + field = report->field[0]; + if (!field) { + hid_err(hid, "NULL field\n"); + return -1; + } /* Assume single fixed device G940 */ for (i = 0; ff_bits[i] >= 0; i++) diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index 8782fe1aaa07..0ddae2a00d59 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -484,16 +484,34 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde int lg4ff_init(struct hid_device *hid) { struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); + struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; struct input_dev *dev = hidinput->input; + struct hid_report *report; + struct hid_field *field; struct lg4ff_device_entry *entry; struct lg_drv_data *drv_data; struct usb_device_descriptor *udesc; int error, i, j; __u16 bcdDevice, rev_maj, rev_min; + /* Find the report to use */ + if (list_empty(report_list)) { + hid_err(hid, "No output report found\n"); + return -1; + } + /* Check that the report looks ok */ - if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7)) + report = list_entry(report_list->next, struct hid_report, list); + if (!report) { + hid_err(hid, "NULL output report\n"); return -1; + } + + field = report->field[0]; + if (!field) { + hid_err(hid, "NULL field\n"); + return -1; + } /* Check what wheel has been connected */ for (i = 0; i < ARRAY_SIZE(lg4ff_devices); i++) { diff --git a/drivers/hid/hid-lgff.c b/drivers/hid/hid-lgff.c index e1394af0ae7b..d7ea8c845b40 100644 --- a/drivers/hid/hid-lgff.c +++ b/drivers/hid/hid-lgff.c @@ -128,14 +128,27 @@ static void hid_lgff_set_autocenter(struct input_dev *dev, u16 magnitude) int lgff_init(struct hid_device* hid) { struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); + struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; struct input_dev *dev = hidinput->input; + struct hid_report *report; + struct hid_field *field; const signed short *ff_bits = ff_joystick; int error; int i; + /* Find the report to use */ + if (list_empty(report_list)) { + hid_err(hid, "No output report found\n"); + return -1; + } + /* Check that the report looks ok */ - if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7)) - return -ENODEV; + report = list_entry(report_list->next, struct hid_report, list); + field = report->field[0]; + if (!field) { + hid_err(hid, "NULL field\n"); + return -1; + } for (i = 0; i < ARRAY_SIZE(devices); i++) { if (dev->id.vendor == devices[i].idVendor && diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 4f762bc9456a..5207591a598c 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -421,7 +421,7 @@ static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev, struct hid_report *report; struct hid_report_enum *output_report_enum; u8 *data = (u8 *)(&dj_report->device_index); - unsigned int i; + int i; output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT]; report = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT]; @@ -431,7 +431,7 @@ static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev, return -ENODEV; } - for (i = 0; i < DJREPORT_SHORT_LENGTH - 1; i++) + for (i = 0; i < report->field[0]->report_count; i++) report->field[0]->value[i] = data[i]; hid_hw_request(hdev, report, HID_REQ_SET_REPORT); @@ -574,7 +574,7 @@ static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type, struct hid_field *field; struct hid_report *report; - unsigned char *data; + unsigned char data[8]; int offset; dbg_hid("%s: %s, type:%d | code:%d | value:%d\n", @@ -590,13 +590,6 @@ static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type, return -1; } hid_set_field(field, offset, value); - - data = hid_alloc_report_buf(field->report, GFP_KERNEL); - if (!data) { - dev_warn(&dev->dev, "failed to allocate report buf memory\n"); - return -1; - } - hid_output_report(field->report, &data[0]); output_report_enum = &dj_rcv_hiddev->report_enum[HID_OUTPUT_REPORT]; @@ -607,9 +600,8 @@ static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type, hid_hw_request(dj_rcv_hiddev, report, HID_REQ_SET_REPORT); - kfree(data); - return 0; + } static int logi_dj_ll_start(struct hid_device *hid) @@ -746,12 +738,6 @@ static int logi_dj_probe(struct hid_device *hdev, goto hid_parse_fail; } - if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, REPORT_ID_DJ_SHORT, - 0, DJREPORT_SHORT_LENGTH - 1)) { - retval = -ENODEV; - goto hid_parse_fail; - } - /* Starts the usb device and connects to upper interfaces hiddev and * hidraw */ retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index b6701cec80ff..d39a5cede0b0 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -101,9 +101,9 @@ struct mt_device { unsigned last_slot_field; /* the last field of a slot */ unsigned mt_report_id; /* the report ID of the multitouch device */ unsigned pen_report_id; /* the report ID of the pen device */ - __s16 inputmode; /* InputMode HID feature, -1 if non-existent */ - __s16 inputmode_index; /* InputMode HID feature index in the report */ - __s16 maxcontact_report_id; /* Maximum Contact Number HID feature, + __s8 inputmode; /* InputMode HID feature, -1 if non-existent */ + __s8 inputmode_index; /* InputMode HID feature index in the report */ + __s8 maxcontact_report_id; /* Maximum Contact Number HID feature, -1 if non-existent */ __u8 num_received; /* how many contacts we received */ __u8 num_expected; /* expected last contact index */ @@ -317,17 +317,19 @@ static void mt_feature_mapping(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage) { struct mt_device *td = hid_get_drvdata(hdev); + int i; switch (usage->hid) { case HID_DG_INPUTMODE: - /* Ignore if value index is out of bounds. */ - if (usage->usage_index >= field->report_count) { - dev_err(&hdev->dev, "HID_DG_INPUTMODE out of range\n"); - break; - } - td->inputmode = field->report->id; - td->inputmode_index = usage->usage_index; + td->inputmode_index = 0; /* has to be updated below */ + + for (i=0; i < field->maxusage; i++) { + if (field->usage[i].hid == usage->hid) { + td->inputmode_index = i; + break; + } + } break; case HID_DG_CONTACTMAX: @@ -534,10 +536,6 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, mt_store_field(usage, td, hi); return 1; case HID_DG_CONTACTCOUNT: - /* Ignore if indexes are out of bounds. */ - if (field->index >= field->report->maxfield || - usage->usage_index >= field->report_count) - return 1; td->cc_index = field->index; td->cc_value_index = usage->usage_index; return 1; diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index 5482156ab4de..ef95102515e4 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -115,8 +115,7 @@ static inline int ntrig_get_mode(struct hid_device *hdev) struct hid_report *report = hdev->report_enum[HID_FEATURE_REPORT]. report_id_hash[0x0d]; - if (!report || report->maxfield < 1 || - report->field[0]->report_count < 1) + if (!report) return -EINVAL; hid_hw_request(hdev, report, HID_REQ_GET_REPORT); diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c index 59d5eb1e742c..e346038f0f11 100644 --- a/drivers/hid/hid-picolcd_cir.c +++ b/drivers/hid/hid-picolcd_cir.c @@ -145,7 +145,6 @@ void picolcd_exit_cir(struct picolcd_data *data) struct rc_dev *rdev = data->rc_dev; data->rc_dev = NULL; - if (rdev) - rc_unregister_device(rdev); + rc_unregister_device(rdev); } diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c index acbb021065ec..b48092d0e139 100644 --- a/drivers/hid/hid-picolcd_core.c +++ b/drivers/hid/hid-picolcd_core.c @@ -290,7 +290,7 @@ static ssize_t picolcd_operation_mode_store(struct device *dev, buf += 10; cnt -= 10; } - if (!report || report->maxfield != 1) + if (!report) return -EINVAL; while (cnt > 0 && (buf[cnt-1] == '\n' || buf[cnt-1] == '\r')) diff --git a/drivers/hid/hid-picolcd_debugfs.c b/drivers/hid/hid-picolcd_debugfs.c index 024cdf3c2297..59ab8e157e6b 100644 --- a/drivers/hid/hid-picolcd_debugfs.c +++ b/drivers/hid/hid-picolcd_debugfs.c @@ -394,7 +394,7 @@ static void dump_buff_as_hex(char *dst, size_t dst_sz, const u8 *data, void picolcd_debug_out_report(struct picolcd_data *data, struct hid_device *hdev, struct hid_report *report) { - u8 *raw_data; + u8 raw_data[70]; int raw_size = (report->size >> 3) + 1; char *buff; #define BUFF_SZ 256 @@ -407,19 +407,19 @@ void picolcd_debug_out_report(struct picolcd_data *data, if (!buff) return; - raw_data = hid_alloc_report_buf(report, GFP_ATOMIC); - if (!raw_data) { - kfree(buff); - return; - } - snprintf(buff, BUFF_SZ, "\nout report %d (size %d) = ", report->id, raw_size); hid_debug_event(hdev, buff); - raw_data[0] = report->id; - hid_output_report(report, raw_data); - dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size); - hid_debug_event(hdev, buff); + if (raw_size + 5 > sizeof(raw_data)) { + kfree(buff); + hid_debug_event(hdev, " TOO BIG\n"); + return; + } else { + raw_data[0] = report->id; + hid_output_report(report, raw_data); + dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size); + hid_debug_event(hdev, buff); + } switch (report->id) { case REPORT_LED_STATE: @@ -644,7 +644,6 @@ void picolcd_debug_out_report(struct picolcd_data *data, break; } wake_up_interruptible(&hdev->debug_wait); - kfree(raw_data); kfree(buff); } diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c index c930ab8554ea..591f6b22aa94 100644 --- a/drivers/hid/hid-picolcd_fb.c +++ b/drivers/hid/hid-picolcd_fb.c @@ -593,14 +593,10 @@ int picolcd_init_framebuffer(struct picolcd_data *data) void picolcd_exit_framebuffer(struct picolcd_data *data) { struct fb_info *info = data->fb_info; - struct picolcd_fb_data *fbdata; + struct picolcd_fb_data *fbdata = info->par; unsigned long flags; - if (!info) - return; - device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate); - fbdata = info->par; /* disconnect framebuffer from HID dev */ spin_lock_irqsave(&fbdata->lock, flags); diff --git a/drivers/hid/hid-pl.c b/drivers/hid/hid-pl.c index 2dcd7d98dbd6..d29112fa5cd5 100644 --- a/drivers/hid/hid-pl.c +++ b/drivers/hid/hid-pl.c @@ -132,14 +132,8 @@ static int plff_init(struct hid_device *hid) strong = &report->field[0]->value[2]; weak = &report->field[0]->value[3]; debug("detected single-field device"); - } else if (report->field[0]->maxusage == 1 && - report->field[0]->usage[0].hid == - (HID_UP_LED | 0x43) && - report->maxfield >= 4 && - report->field[0]->report_count >= 1 && - report->field[1]->report_count >= 1 && - report->field[2]->report_count >= 1 && - report->field[3]->report_count >= 1) { + } else if (report->maxfield >= 4 && report->field[0]->maxusage == 1 && + report->field[0]->usage[0].hid == (HID_UP_LED | 0x43)) { report->field[0]->value[0] = 0x00; report->field[1]->value[0] = 0x00; strong = &report->field[2]->value[0]; diff --git a/drivers/hid/hid-roccat-konepure.c b/drivers/hid/hid-roccat-konepure.c index 5850959d48f5..c79d0b06c143 100644 --- a/drivers/hid/hid-roccat-konepure.c +++ b/drivers/hid/hid-roccat-konepure.c @@ -262,7 +262,6 @@ static int konepure_raw_event(struct hid_device *hdev, static const struct hid_device_id konepure_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE) }, - { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE_OPTICAL) }, { } }; @@ -301,5 +300,5 @@ module_init(konepure_init); module_exit(konepure_exit); MODULE_AUTHOR("Stefan Achatz"); -MODULE_DESCRIPTION("USB Roccat KonePure/Optical driver"); +MODULE_DESCRIPTION("USB Roccat KonePure driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index aa34755ca205..ca7498107327 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -221,8 +221,7 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id, mutex_lock(&data->mutex); report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT); - if (!report || (field_index >= report->maxfield) || - report->field[field_index]->report_count < 1) { + if (!report || (field_index >= report->maxfield)) { ret = -EINVAL; goto done_proc; } diff --git a/drivers/hid/hid-speedlink.c b/drivers/hid/hid-speedlink.c index 7112f3e832ee..a2f587d004e1 100644 --- a/drivers/hid/hid-speedlink.c +++ b/drivers/hid/hid-speedlink.c @@ -3,7 +3,7 @@ * Fixes "jumpy" cursor and removes nonexistent keyboard LEDS from * the HID descriptor. * - * Copyright (c) 2011, 2013 Stefan Kriwanek + * Copyright (c) 2011 Stefan Kriwanek */ /* @@ -46,13 +46,8 @@ static int speedlink_event(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value) { /* No other conditions due to usage_table. */ - - /* This fixes the "jumpy" cursor occuring due to invalid events sent - * by the device. Some devices only send them with value==+256, others - * don't. However, catching abs(value)>=256 is restrictive enough not - * to interfere with devices that were bug-free (has been tested). - */ - if (abs(value) >= 256) + /* Fix "jumpy" cursor (invalid events sent by device). */ + if (value == 256) return 1; /* Drop useless distance 0 events (on button clicks etc.) as well */ if (value == 0) diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index 29f328f411fb..d16491192112 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -249,11 +249,6 @@ static int steelseries_srws1_probe(struct hid_device *hdev, goto err_free; } - if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 16)) { - ret = -ENODEV; - goto err_free; - } - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) { hid_err(hdev, "hw start failed\n"); diff --git a/drivers/hid/hid-zpff.c b/drivers/hid/hid-zpff.c index a29756c6ca02..6ec28a37c146 100644 --- a/drivers/hid/hid-zpff.c +++ b/drivers/hid/hid-zpff.c @@ -68,13 +68,21 @@ static int zpff_init(struct hid_device *hid) struct hid_report *report; struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); + struct list_head *report_list = + &hid->report_enum[HID_OUTPUT_REPORT].report_list; struct input_dev *dev = hidinput->input; - int i, error; + int error; - for (i = 0; i < 4; i++) { - report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, i, 1); - if (!report) - return -ENODEV; + if (list_empty(report_list)) { + hid_err(hid, "no output report found\n"); + return -ENODEV; + } + + report = list_entry(report_list->next, struct hid_report, list); + + if (report->maxfield < 4) { + hid_err(hid, "not enough fields in report\n"); + return -ENODEV; } zpff = kzalloc(sizeof(struct zpff_device), GFP_KERNEL); diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 612a655bc9f0..a7451632ceb4 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -113,7 +113,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, __u8 *buf; int ret = 0; - if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { + if (!hidraw_table[minor]) { ret = -ENODEV; goto out; } @@ -261,7 +261,7 @@ static int hidraw_open(struct inode *inode, struct file *file) } mutex_lock(&minors_lock); - if (!hidraw_table[minor] || !hidraw_table[minor]->exist) { + if (!hidraw_table[minor]) { err = -ENODEV; goto out_unlock; } @@ -302,38 +302,39 @@ static int hidraw_fasync(int fd, struct file *file, int on) return fasync_helper(fd, file, on, &list->fasync); } -static void drop_ref(struct hidraw *hidraw, int exists_bit) -{ - if (exists_bit) { - hid_hw_close(hidraw->hid); - hidraw->exist = 0; - if (hidraw->open) - wake_up_interruptible(&hidraw->wait); - } else { - --hidraw->open; - } - - if (!hidraw->open && !hidraw->exist) { - device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); - hidraw_table[hidraw->minor] = NULL; - kfree(hidraw); - } -} - static int hidraw_release(struct inode * inode, struct file * file) { unsigned int minor = iminor(inode); + struct hidraw *dev; struct hidraw_list *list = file->private_data; + int ret; + int i; mutex_lock(&minors_lock); + if (!hidraw_table[minor]) { + ret = -ENODEV; + goto unlock; + } list_del(&list->node); - kfree(list); - - drop_ref(hidraw_table[minor], 0); + dev = hidraw_table[minor]; + if (!--dev->open) { + if (list->hidraw->exist) { + hid_hw_power(dev->hid, PM_HINT_NORMAL); + hid_hw_close(dev->hid); + } else { + kfree(list->hidraw); + } + } + for (i = 0; i < HIDRAW_BUFFER_SIZE; ++i) + kfree(list->buffer[i].value); + kfree(list); + ret = 0; +unlock: mutex_unlock(&minors_lock); - return 0; + + return ret; } static long hidraw_ioctl(struct file *file, unsigned int cmd, @@ -538,9 +539,18 @@ void hidraw_disconnect(struct hid_device *hid) struct hidraw *hidraw = hid->hidraw; mutex_lock(&minors_lock); + hidraw->exist = 0; + + device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); - drop_ref(hidraw, 1); + hidraw_table[hidraw->minor] = NULL; + if (hidraw->open) { + hid_hw_close(hid); + wake_up_interruptible(&hidraw->wait); + } else { + kfree(hidraw); + } mutex_unlock(&minors_lock); } EXPORT_SYMBOL_GPL(hidraw_disconnect); diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 145a4cb5c872..fc307e0422af 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -640,7 +640,7 @@ static const struct file_operations uhid_fops = { static struct miscdevice uhid_misc = { .fops = &uhid_fops, - .minor = UHID_MINOR, + .minor = MISC_DYNAMIC_MINOR, .name = UHID_NAME, }; @@ -659,5 +659,3 @@ module_exit(uhid_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Herrmann "); MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem"); -MODULE_ALIAS_MISCDEV(UHID_MINOR); -MODULE_ALIAS("devname:" UHID_NAME); diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index ada164e1b3a1..99418285222c 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -535,6 +535,7 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re { int head; struct usbhid_device *usbhid = hid->driver_data; + int len = ((report->size - 1) >> 3) + 1 + (report->id > 0); if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN) return; @@ -545,7 +546,7 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re return; } - usbhid->out[usbhid->outhead].raw_report = hid_alloc_report_buf(report, GFP_ATOMIC); + usbhid->out[usbhid->outhead].raw_report = kmalloc(len, GFP_ATOMIC); if (!usbhid->out[usbhid->outhead].raw_report) { hid_warn(hid, "output queueing failed\n"); return; @@ -594,7 +595,7 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re } if (dir == USB_DIR_OUT) { - usbhid->ctrl[usbhid->ctrlhead].raw_report = hid_alloc_report_buf(report, GFP_ATOMIC); + usbhid->ctrl[usbhid->ctrlhead].raw_report = kmalloc(len, GFP_ATOMIC); if (!usbhid->ctrl[usbhid->ctrlhead].raw_report) { hid_warn(hid, "control queueing failed\n"); return; diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 07345521f421..19b8360f2330 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -109,8 +109,6 @@ static const struct hid_blacklist { { USB_VENDOR_ID_SIGMA_MICRO, USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X, HID_QUIRK_MULTI_INPUT }, - { USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS }, - { 0, 0 } }; diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 3899d25d6d9d..72eaa825b467 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -296,8 +296,8 @@ config SENSORS_K10TEMP If you say yes here you get support for the temperature sensor(s) inside your CPU. Supported are later revisions of the AMD Family 10h and all revisions of the AMD Family 11h, - 12h (Llano), 14h (Brazos), 15h (Bulldozer/Trinity) and - 16h (Kabini) microarchitectures. + 12h (Llano), 14h (Brazos) and 15h (Bulldozer/Trinity) + microarchitectures. This driver can also be built as a module. If so, the module will be called k10temp. @@ -838,6 +838,15 @@ config MSIC_GPADC help Say Y here to enable MSIC GPADC driver on Intel Medfield Platform +config INTEL_MCU + tristate "Intel generic MCU control interface" + help + Say Y here to enable control interface for intel mcu + + This driver provide userspace tty interface for the control and + message output. + You could use normal read/write to complete those operation. + config SENSORS_MAX1111 tristate "Maxim MAX1111 Serial 8-bit ADC chip and compatibles" depends on SPI_MASTER diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 4e1d647d5173..11e757a843a4 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -141,6 +141,7 @@ obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o obj-$(CONFIG_MSIC_GPADC) += intel_mid_gpadc.o +obj-$(CONFIG_INTEL_MCU) += intel_mcu_common.o obj-$(CONFIG_PMBUS) += pmbus/ diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index 3288f13d2d87..62c2e32e25ef 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -230,7 +230,6 @@ static int send_argument(const char *key) static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len) { - u8 status, data = 0; int i; if (send_command(cmd) || send_argument(key)) { @@ -238,7 +237,6 @@ static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len) return -EIO; } - /* This has no effect on newer (2012) SMCs */ if (send_byte(len, APPLESMC_DATA_PORT)) { pr_warn("%.4s: read len fail\n", key); return -EIO; @@ -252,17 +250,6 @@ static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len) buffer[i] = inb(APPLESMC_DATA_PORT); } - /* Read the data port until bit0 is cleared */ - for (i = 0; i < 16; i++) { - udelay(APPLESMC_MIN_WAIT); - status = inb(APPLESMC_CMD_PORT); - if (!(status & 0x01)) - break; - data = inb(APPLESMC_DATA_PORT); - } - if (i) - pr_warn("flushed %d bytes, last value is: %d\n", i, data); - return 0; } @@ -538,25 +525,16 @@ static int applesmc_init_smcreg_try(void) { struct applesmc_registers *s = &smcreg; bool left_light_sensor, right_light_sensor; - unsigned int count; u8 tmp[1]; int ret; if (s->init_complete) return 0; - ret = read_register_count(&count); + ret = read_register_count(&s->key_count); if (ret) return ret; - if (s->cache && s->key_count != count) { - pr_warn("key count changed from %d to %d\n", - s->key_count, count); - kfree(s->cache); - s->cache = NULL; - } - s->key_count = count; - if (!s->cache) s->cache = kcalloc(s->key_count, sizeof(*s->cache), GFP_KERNEL); if (!s->cache) diff --git a/drivers/hwmon/intel_mcu_common.c b/drivers/hwmon/intel_mcu_common.c new file mode 100644 index 000000000000..cdf078c4d557 --- /dev/null +++ b/drivers/hwmon/intel_mcu_common.c @@ -0,0 +1,700 @@ +/** + * intel_mcu_common.c - Intel MCU common interface file + * + * Copyright (C) 2014 Intel Inc. - http://www.intel.com + * + * Authors: Lei Wen , + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "intel_mcu_common.h" +#include + +#define APP_IMR_SIZE (1024 * 126) +#define DRIVER_AUTHOR "Lei Wen " +#define DRIVER_DESC "Intel mcu common control interface" +#define INTEL_MCU_TTY_MAJOR 168 +#define INTEL_MCU_TTY_MINORS 3 + +#define LOAD_APP "load mcu app" +#define GET_VERSION "get mcu app version" +struct tty_driver *intel_mcu_tty_driver; + +#define VER_LEN 1024 +struct mcu { + char ver[VER_LEN]; + uintptr_t ddr_phy[2]; + void *ddr[2]; + int load_in_progress; +}; + +struct mcu_data { + struct device *dev; + struct tty_port port; + struct mcu *mcu; + struct completion cmp; + struct loop_buffer lbuf; + int index; +}; + +static struct mcu_data *mcu_table[INTEL_MCU_TTY_MINORS]; +static int log_level = 1; +static char *debug_msg[] = { + "fatal", + "error", + "warning", + "info", + "debug", +}; + +static int send_cmd(struct mcu_data *data, + struct psh_msg *in, int ch, int wait) +{ + int ret; + ret = intel_ia2psh_command(in, NULL, ch, 1000000); + if (ret) + return ret; + + if (wait) { + ret = wait_for_completion_timeout(&data->cmp, 3 * HZ); + if (ret == 0) + return -ETIME; + } + + return 0; +} + +static void lbuf_read_reset(struct loop_buffer *lbuf) +{ + if (lbuf) { + lbuf->off_head = lbuf->off_tail = 0; + lbuf->in_reading = 0; + } +} + +static int lbuf_read_next(struct loop_buffer *lbuf, u8 **buf, u16 *size) +{ + struct frame_head *fhead = + (struct frame_head *)(lbuf->addr + lbuf->off_head); + *buf = NULL; + *size = 0; + + if (lbuf->in_reading) { + lbuf->in_reading = 0; + + /* go over previous frame has been read */ + lbuf->off_head += frame_size(fhead->length); + lbuf->off_tail = lbuf->off_head; + fhead = (struct frame_head *)(lbuf->addr + lbuf->off_head); + } + + if (fhead->sign == LBUF_DISCARD_SIGN) { + fhead = (struct frame_head *)lbuf->addr; + lbuf->off_head = lbuf->off_tail = 0; + } + + if (fhead->sign == LBUF_CELL_SIGN) { + + *buf = lbuf->addr + lbuf->off_head + sizeof(*fhead); + *size = fhead->length; + lbuf->in_reading = 1; + } + + return !lbuf->in_reading; +} + +static int intel_mcu_mcudbg_level(struct mcu_data *data, int level) +{ + struct psh_msg in; + struct cmd_debug_param *param; + + in.param = 0; + in.msg = CMD_MCU_APP_DEBUG; + param = (struct cmd_debug_param *) (&(in.param)); + if (level > 0) { + param->level = level; + param->sub_cmd = CMD_DEBUG_SET_MASK; + } else + param->sub_cmd = CMD_DEBUG_GET_MASK; + + return send_cmd(data, &in, PSH2IA_CHANNEL2, 1); +} + +static void push_char_into_port(struct tty_port *port, const char *buf, int len) +{ + int count; + + if (len <= 0) + return; + + do { + count = tty_insert_flip_string(port, buf, len); + len -= count; + buf += count; + } while (len > 0); + + tty_flip_buffer_push(port); +} + +static int intel_mcu_tty_open(struct tty_struct *tty, struct file *filp) +{ + dev_dbg(tty->dev, "%s\n", __func__); + tty->driver_data = mcu_table[tty->index]; + /* + * For we may get data cached while we don't open this tty, + * so we need to flush out buffer, then we could + * get full content without disappoint user + */ + if (tty->port) + tty_flip_buffer_push(tty->port); + + return 0; +} + +static void intel_mcu_tty_close(struct tty_struct *tty, struct file *filp) +{ + dev_dbg(tty->dev, "%s\n", __func__); + tty->driver_data = NULL; +} + +static int do_get_ver(struct mcu_data *data) +{ + struct psh_msg in; + + in.param = 0; + in.msg = CMD_MCU_APP_GET_VERSION; + return send_cmd(data, &in, PSH2IA_CHANNEL2, 1); +} + +static int do_setup_ddr(struct mcu_data *data) +{ + struct mcu *mcu = data->mcu; + const struct firmware *fw_entry; + static int fw_load_done; + char fname[20]; + struct psh_msg in; + + if (fw_load_done) + return 0; + + snprintf(fname, 20, "intel_mcu.bin"); + if (!request_firmware(&fw_entry, fname, data->dev)) { + if (!fw_entry) + return -ENOMEM; + + pr_debug("psh fw size %d virt:0x%p\n", + (int)fw_entry->size, fw_entry->data); + if (fw_entry->size > APP_IMR_SIZE) { + pr_err("psh fw size too big\n"); + } else { + memcpy(mcu->ddr[0], fw_entry->data, + fw_entry->size); + in.msg = CMD_MCU_LOAD_APP; + in.param = mcu->ddr_phy[0]; + mcu->load_in_progress = 1; + if (send_cmd(data, &in, PSH2IA_CHANNEL3, 1)) + return -1; + fw_load_done = 1; + } + release_firmware(fw_entry); + } else { + pr_err("cannot find psh firmware(%s)\n", fname); + return -ENODEV; + } + in.msg = CMD_MCU_SETUP_DDR; + in.param = mcu->ddr_phy[1]; + return send_cmd(data, &in, PSH2IA_CHANNEL2, 1); +} + +static ssize_t load_app_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int len = strlen(LOAD_APP); + + if (count >= len && strncmp(buf, LOAD_APP, len) == 0) { + do_setup_ddr(mcu_table[2]); + return count; + } + + pr_err("Please provide right string as [%s]!\n", LOAD_APP); + return -1; +} + +static ssize_t get_ver_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct mcu_data *data = mcu_table[2]; + struct mcu *mcu = data->mcu; + + if (do_get_ver(data)) + return -1; + + return scnprintf(buf, VER_LEN, "%s", mcu->ver); +} + +static ssize_t mdbg_control_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + if (intel_mcu_mcudbg_level(mcu_table[2], -1) < 0) + goto err; + + if (log_level > 0 && log_level < 6) + return scnprintf(buf, 8, "%s\n", debug_msg[log_level - 1]); + +err: + pr_info("get log level err\n"); + return -1; +} +/* + *set msg level:echo log_level=fatal|info|warning|error|debug| >control +*/ +#define LOG_LEVEL "fatal|error|warning|info|debug" +static ssize_t mdbg_control_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mcu_data *data = mcu_table[2]; + int level = 0; + long ltmp = 0; + + if (!buf) + return -1; + if (!strncmp(buf, "fatal", strlen("fatal"))) + level = 1; + else if (!strncmp(buf, "error", strlen("error"))) + level = 2; + else if (!strncmp(buf, "warning", strlen("warning"))) + level = 3; + else if (!strncmp(buf, "info", strlen("info"))) + level = 4; + else if (!strncmp(buf, "debug", strlen("debug"))) + level = 5; + else { + int err; + err = kstrtol(buf, 10, <mp); + if (!err && (ltmp > 0) && (ltmp < 6)) + level = ltmp; + else { + pr_err("Please input words as [%s]\n", LOG_LEVEL); + return -1; + } + } + pr_info("set level:%d\n", level); + if (intel_mcu_mcudbg_level(data, level) < 0) + return -1; + return count; +} + +static DEVICE_ATTR(control, 0200, NULL, load_app_store); +static DEVICE_ATTR(fw_version, 0400, get_ver_show, NULL); +static DEVICE_ATTR(log_level, 0600, mdbg_control_show, mdbg_control_store); + +static struct attribute *control_sysfs_attrs[] = { + &dev_attr_control.attr, + &dev_attr_fw_version.attr, + &dev_attr_log_level.attr, + NULL, + +}; + +static struct attribute_group intel_mcu_tty_attribute_group = { + .name = NULL, + .attrs = control_sysfs_attrs, + +}; + +static void raw_output(struct mcu_data *data, int ch, + const unsigned char *buf, int count) +{ + struct psh_msg in; + int i, left; + + for (i = 0; i < count; i += 4) { + left = count - i; + if (left > 4) { + left = 4; + in.msg = PSH_IPC_CONTINUE; + } else + in.msg = 0; + + memcpy(&in.param, buf, left); + buf += left; + send_cmd(data, &in, ch, 0); + } +} + +#define TTY_WRITE_ROOM 512 +static int intel_mcu_tty_write(struct tty_struct *tty, + const unsigned char *buf, int count) +{ + struct mcu_data *data = tty->driver_data; + + switch (tty->index) { + default: + pr_err("TTY index %d not supported!\n", tty->index); + case 1: + return -1; + case 0: + if (count > TTY_WRITE_ROOM) { + pr_err("Port 0's input size is limited by %d!\n", + TTY_WRITE_ROOM); + return -1; + } + raw_output(data, tty->index, buf, count); + break; + } + return count; +} + +static int intel_mcu_tty_write_room(struct tty_struct *tty) +{ + return TTY_WRITE_ROOM; +} + +static const struct tty_operations intel_mcu_ops = { + .open = intel_mcu_tty_open, + .close = intel_mcu_tty_close, + .write = intel_mcu_tty_write, + .write_room = intel_mcu_tty_write_room, +}; + +static int mem_alloc(struct pci_dev *pdev, uintptr_t *phy_addr, + void **virt_addr, int bar) +{ + void __iomem *mem; + int ret = 0; + unsigned long start = 0, len; + + /* dedicate isolated memory region */ + start = pci_resource_start(pdev, bar); + len = pci_resource_len(pdev, bar); + if (!start || !len) { + dev_err(&pdev->dev, "bar %d address not set\n", bar); + ret = -EINVAL; + goto err; + } + + ret = pci_request_region(pdev, bar, "intel_mcu"); + if (ret) { + dev_err(&pdev->dev, + "failed to request psh region 0x%lx-0x%lx\n", + start, + (unsigned long)pci_resource_end(pdev, bar)); + goto err; + } + + mem = ioremap_nocache(start, len); + if (!mem) { + dev_err(&pdev->dev, "can not ioremap app imr address\n"); + ret = -EINVAL; + goto err_ioremap; + } + + *phy_addr = start; + *virt_addr = (void *)mem; + return 0; + +err_ioremap: + pci_release_region(pdev, bar); +err: + return ret; +} + +static void cmd_handler(u32 msg, u32 param, void *_data) +{ + struct mcu_data *data = (struct mcu_data *)_data; + struct mcu *mcu = data->mcu; + struct cmd_resp *resp; + const struct version_resp *version; + struct debug_resp *debug_resp; + u8 *dbuf = NULL; + u16 size = 0; + + if (mcu->load_in_progress) { + mcu->load_in_progress = 0; + goto done; + } + + while (!lbuf_read_next(&data->lbuf, &dbuf, &size)) { + resp = (struct cmd_resp *)dbuf; + + if (!resp->len) + continue; + + switch (resp->cmd_id) { + case CMD_MCU_APP_GET_VERSION: + version = (struct version_resp *)resp->param; + if (version->total_length) + snprintf(mcu->ver, VER_LEN, version->buf, + version->total_length); + break; + case CMD_MCU_APP_DEBUG: + debug_resp = (struct debug_resp *)resp->param; + log_level = debug_resp->level; + default: + break; + } + } +done: + complete(&data->cmp); +} + +static void raw_data_handler(u32 msg, u32 param, void *_data) +{ + struct mcu_data *data = (struct mcu_data *)_data; + struct cmd_resp *resp; + u8 *dbuf = NULL; + u16 size = 0; + + while (!lbuf_read_next(&data->lbuf, &dbuf, &size)) { + resp = (struct cmd_resp *)dbuf; + push_char_into_port(&data->port, resp->param, resp->len); + } + complete(&data->cmp); +} + +static int mcu_platform_probe(struct platform_device *pdev) +{ + int ret, i; + struct mcu_data *data; + struct mcu *mcu; + u8 *base; + + mcu = platform_get_drvdata(pdev); + intel_mcu_tty_driver = alloc_tty_driver(INTEL_MCU_TTY_MINORS); + if (!intel_mcu_tty_driver) { + dev_err(&pdev->dev, "fail to alloc tty driver\n"); + return -ENODEV; + } + + intel_mcu_tty_driver->name = "ttymcu"; + intel_mcu_tty_driver->major = INTEL_MCU_TTY_MAJOR; + intel_mcu_tty_driver->minor_start = 0; + intel_mcu_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + intel_mcu_tty_driver->subtype = SERIAL_TYPE_NORMAL; + intel_mcu_tty_driver->flags = TTY_DRIVER_REAL_RAW + | TTY_DRIVER_DYNAMIC_DEV; + intel_mcu_tty_driver->init_termios = tty_std_termios; + intel_mcu_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | + HUPCL | CLOCAL; + intel_mcu_tty_driver->init_termios.c_ispeed = 38400; + intel_mcu_tty_driver->init_termios.c_ospeed = 38400; + intel_mcu_tty_driver->init_termios.c_iflag = 0; + intel_mcu_tty_driver->init_termios.c_oflag = 0; + intel_mcu_tty_driver->init_termios.c_lflag = 0; + tty_set_operations(intel_mcu_tty_driver, &intel_mcu_ops); + + ret = tty_register_driver(intel_mcu_tty_driver); + if (ret) { + dev_err(&pdev->dev, "fail to register tty driver\n"); + goto tty_reg_fail; + } + + base = (u8 *)mcu->ddr[1]; + for (i = INTEL_MCU_TTY_MINORS - 1; i >= 0; i--) { + data = kzalloc(sizeof(struct mcu_data), GFP_KERNEL); + if (data == NULL) { + dev_err(&pdev->dev, "fail to alloc mcu data\n"); + goto data_alloc_fail; + } + + data->index = i; + tty_port_init(&data->port); + data->dev = tty_port_register_device(&data->port, + intel_mcu_tty_driver, i, &pdev->dev); + mcu_table[i] = data; + data->mcu = mcu; + init_completion(&data->cmp); + data->lbuf.addr = base; + data->lbuf.length = BUF_IA_DDR_SIZE; + lbuf_read_reset(&data->lbuf); + base += BUF_IA_DDR_SIZE; + } + ret = sysfs_create_group(&pdev->dev.kobj, + &intel_mcu_tty_attribute_group); + if (ret) { + pr_err("failed to create the mdbg sysfs attributes\n"); + sysfs_remove_group(&pdev->dev.kobj, + &intel_mcu_tty_attribute_group); + goto data_alloc_fail; + } + + intel_psh_ipc_bind(PSH_RECV_CH0, raw_data_handler, mcu_table[0]); + intel_psh_ipc_bind(PSH_RECV_CH1, raw_data_handler, mcu_table[1]); + intel_psh_ipc_bind(PSH_RECV_CH2, cmd_handler, mcu_table[2]); + + pr_info("MCU detected and ready to used!\n"); + + return 0; + +data_alloc_fail: + for (i = 0; i < INTEL_MCU_TTY_MINORS; i++) + kfree(mcu_table[i]); +tty_reg_fail: + put_tty_driver(intel_mcu_tty_driver); + return ret; +} + +static int mcu_platform_remove(struct platform_device *pdev) +{ + struct mcu *mcu; + int i; + + mcu = platform_get_drvdata(pdev); + sysfs_remove_group(&pdev->dev.kobj, + &intel_mcu_tty_attribute_group); + + for (i = 0; i < INTEL_MCU_TTY_MINORS; i++) + kfree(mcu_table[i]); + put_tty_driver(intel_mcu_tty_driver); + kfree(mcu); + + return 0; +} + +static struct platform_driver intel_mcu_platform = { + .driver = { + .name = "intel_mcu", + }, + .probe = mcu_platform_probe, + .remove = mcu_platform_remove, +}; +module_platform_driver(intel_mcu_platform); + +static int intel_mcu_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct platform_device *dev; + struct mcu *mcu; + int ret; + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "fail to enable psh pci device\n"); + return -ENODEV; + } + + mcu = kzalloc(sizeof(struct mcu), GFP_KERNEL); + if (!mcu) { + dev_err(&pdev->dev, "cannot allocate memory for mcu\n"); + ret = -ENOMEM; + goto mcu_err; + } + + ret = mem_alloc(pdev, &mcu->ddr_phy[0], &mcu->ddr[0], 0); + if (ret) + goto plat_alloc_fail; + + ret = mem_alloc(pdev, &mcu->ddr_phy[1], &mcu->ddr[1], 1); + if (ret) + goto plat_alloc_fail; + + dev = platform_device_alloc("intel_mcu", -1); + if (!dev) { + ret = -ENODEV; + goto plat_alloc_fail; + } + + dev->dev.dma_mask = &dev->dev.coherent_dma_mask; + platform_set_drvdata(dev, mcu); + dev_set_drvdata(&pdev->dev, mcu); + + ret = platform_device_add(dev); + return ret; + +plat_alloc_fail: + kfree(mcu); +mcu_err: + pci_dev_put(pdev); + return ret; +} + +static void intel_mcu_remove(struct pci_dev *pdev) +{ + struct mcu *mcu; + + mcu = dev_get_drvdata(&pdev->dev); + iounmap((void __iomem *)mcu->ddr[0]); + iounmap((void __iomem *)mcu->ddr[1]); + + pci_release_region(pdev, 0); + pci_release_region(pdev, 1); + pci_dev_put(pdev); +} + +static DEFINE_PCI_DEVICE_TABLE(pci_ids) = { + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x11a4)}, + { 0,} +}; + +MODULE_DEVICE_TABLE(pci, pci_ids); +static struct pci_driver intel_mcu_driver = { + .name = "intel_mcu", + .id_table = pci_ids, + .probe = intel_mcu_probe, + .remove = intel_mcu_remove, +}; + +static int __init intel_mcu_init(void) +{ + return pci_register_driver(&intel_mcu_driver); +} + +static void __exit intel_mcu_exit(void) +{ + pci_unregister_driver(&intel_mcu_driver); +} + +module_init(intel_mcu_init); +module_exit(intel_mcu_exit); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV_MAJOR(INTEL_MCU_TTY_MAJOR); diff --git a/drivers/hwmon/intel_mcu_common.h b/drivers/hwmon/intel_mcu_common.h new file mode 100644 index 000000000000..1ffd08124b34 --- /dev/null +++ b/drivers/hwmon/intel_mcu_common.h @@ -0,0 +1,79 @@ +#ifndef _EDISON_COMMON_H_ +#define _EDISON_COMMON_H_ + +#define PSH2IA_CHANNEL0 0 +#define PSH2IA_CHANNEL1 1 +#define PSH2IA_CHANNEL2 2 +#define PSH2IA_CHANNEL3 3 + +enum cmd_id { + CMD_MCU_LOAD_APP = 0, + CMD_MCU_SETUP_DDR, + CMD_MCU_APP_DEBUG, + CMD_MCU_APP_GET_VERSION, +}; + +#define CIRC_SIZE (1024 * 64) +struct ddr_param { + u32 ddr; + u32 ddr1; +} __packed; + +#define CMD_DEBUG_SET_MASK ((u8)0x1) +#define CMD_DEBUG_GET_MASK ((u8)0x2) +#define MCU_DBG_ALL ((u16)-1) +#define MCU_DBG_FATAL 1 +#define MCU_DBG_ERR 2 +#define MCU_DBG_WARN 3 +#define MCU_DBG_INFO 4 +#define MCU_DBG_DBG 5 + +struct cmd_debug_param { + u8 sub_cmd; + u16 level; + char tag[30]; +} __packed; + +#define RESP_PARAM_MAX_SIZE 56 +struct cmd_resp { + u8 cmd_id; + u8 len; + int ret; + char param[RESP_PARAM_MAX_SIZE]; +} __packed; + +struct debug_resp { + u16 level; +} __packed; + +struct version_resp { + u8 total_length; + u8 segment_length; + u8 sequence_number; + char buf[0]; +} __packed; + +#define LBUF_CELL_SIGN ((u16)0x4853) +#define LBUF_EMPTY_SIGN ((u16)0x0000) +#define LBUF_DISCARD_SIGN ((u16)0x4944) +#define size_align(size) ((size % 4) ? (size + 4 - (size % 4)) : size) +#define frame_size(size) (size_align(size) + \ + sizeof(struct frame_head)) + +struct frame_head { + u16 sign; + u16 length; + u8 buf[0]; +} __packed; + +#define BUF_IA_DDR_SIZE 8192 +struct loop_buffer { + int in_reading; + u8 *addr; + u16 length; + + u16 off_head; + u16 off_tail; +}; + +#endif diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index e633856370cf..e3b037c73a7e 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -1,5 +1,5 @@ /* - * k10temp.c - AMD Family 10h/11h/12h/14h/15h/16h processor hardware monitoring + * k10temp.c - AMD Family 10h/11h/12h/14h/15h processor hardware monitoring * * Copyright (c) 2009 Clemens Ladisch * @@ -211,7 +211,6 @@ static DEFINE_PCI_DEVICE_TABLE(k10temp_id_table) = { { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) }, - { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) }, {} }; MODULE_DEVICE_TABLE(pci, k10temp_id_table); diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index b06be8e3bb76..e02f9e36a7b2 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -941,9 +941,6 @@ omap_i2c_isr_thread(int this_irq, void *dev_id) /* * ProDB0017052: Clear ARDY bit twice */ - if (stat & OMAP_I2C_STAT_ARDY) - omap_i2c_ack_stat(dev, OMAP_I2C_STAT_ARDY); - if (stat & (OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK | OMAP_I2C_STAT_AL)) { omap_i2c_ack_stat(dev, (OMAP_I2C_STAT_RRDY | diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index 6c66a728a37f..3f3f0416fbdd 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -1588,7 +1588,7 @@ static int srpt_build_tskmgmt_rsp(struct srpt_rdma_ch *ch, int resp_data_len; int resp_len; - resp_data_len = 4; + resp_data_len = (rsp_code == SRP_TSK_MGMT_SUCCESS) ? 0 : 4; resp_len = sizeof(*srp_rsp) + resp_data_len; srp_rsp = ioctx->ioctx.buf; @@ -1600,9 +1600,11 @@ static int srpt_build_tskmgmt_rsp(struct srpt_rdma_ch *ch, + atomic_xchg(&ch->req_lim_delta, 0)); srp_rsp->tag = tag; - srp_rsp->flags |= SRP_RSP_FLAG_RSPVALID; - srp_rsp->resp_data_len = cpu_to_be32(resp_data_len); - srp_rsp->data[3] = rsp_code; + if (rsp_code != SRP_TSK_MGMT_SUCCESS) { + srp_rsp->flags |= SRP_RSP_FLAG_RSPVALID; + srp_rsp->resp_data_len = cpu_to_be32(resp_data_len); + srp_rsp->data[3] = rsp_code; + } return resp_len; } @@ -2356,8 +2358,6 @@ static void srpt_release_channel_work(struct work_struct *w) transport_deregister_session(se_sess); ch->sess = NULL; - ib_destroy_cm_id(ch->cm_id); - srpt_destroy_ch_ib(ch); srpt_free_ioctx_ring((struct srpt_ioctx **)ch->ioctx_ring, @@ -2368,6 +2368,8 @@ static void srpt_release_channel_work(struct work_struct *w) list_del(&ch->list); spin_unlock_irq(&sdev->spinlock); + ib_destroy_cm_id(ch->cm_id); + if (ch->release_done) complete(ch->release_done); diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c index a73f9618b0ad..4ef4d5e198ae 100644 --- a/drivers/input/mouse/bcm5974.c +++ b/drivers/input/mouse/bcm5974.c @@ -89,9 +89,9 @@ #define USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO 0x025a #define USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS 0x025b /* MacbookAir6,2 (unibody, June 2013) */ -#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0290 -#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0291 -#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0292 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0291 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0292 +#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0293 #define BCM5974_DEVICE(prod) { \ .match_flags = (USB_DEVICE_ID_MATCH_DEVICE | \ diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index fa004b112a89..b4f0e28dfa41 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -890,54 +890,56 @@ static int dma_pte_clear_range(struct dmar_domain *domain, return order; } -static void dma_pte_free_level(struct dmar_domain *domain, int level, - struct dma_pte *pte, unsigned long pfn, - unsigned long start_pfn, unsigned long last_pfn) -{ - pfn = max(start_pfn, pfn); - pte = &pte[pfn_level_offset(pfn, level)]; - - do { - unsigned long level_pfn; - struct dma_pte *level_pte; - - if (!dma_pte_present(pte) || dma_pte_superpage(pte)) - goto next; - - level_pfn = pfn & level_mask(level - 1); - level_pte = phys_to_virt(dma_pte_addr(pte)); - - if (level > 2) - dma_pte_free_level(domain, level - 1, level_pte, - level_pfn, start_pfn, last_pfn); - - /* If range covers entire pagetable, free it */ - if (!(start_pfn > level_pfn || - last_pfn < level_pfn + level_size(level))) { - dma_clear_pte(pte); - domain_flush_cache(domain, pte, sizeof(*pte)); - free_pgtable_page(level_pte); - } -next: - pfn += level_size(level); - } while (!first_pte_in_page(++pte) && pfn <= last_pfn); -} - /* free page table pages. last level pte should already be cleared */ static void dma_pte_free_pagetable(struct dmar_domain *domain, unsigned long start_pfn, unsigned long last_pfn) { int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT; + struct dma_pte *first_pte, *pte; + int total = agaw_to_level(domain->agaw); + int level; + unsigned long tmp; + int large_page = 2; BUG_ON(addr_width < BITS_PER_LONG && start_pfn >> addr_width); BUG_ON(addr_width < BITS_PER_LONG && last_pfn >> addr_width); BUG_ON(start_pfn > last_pfn); /* We don't need lock here; nobody else touches the iova range */ - dma_pte_free_level(domain, agaw_to_level(domain->agaw), - domain->pgd, 0, start_pfn, last_pfn); + level = 2; + while (level <= total) { + tmp = align_to_level(start_pfn, level); + + /* If we can't even clear one PTE at this level, we're done */ + if (tmp + level_size(level) - 1 > last_pfn) + return; + + do { + large_page = level; + first_pte = pte = dma_pfn_level_pte(domain, tmp, level, &large_page); + if (large_page > level) + level = large_page + 1; + if (!pte) { + tmp = align_to_level(tmp + 1, level + 1); + continue; + } + do { + if (dma_pte_present(pte)) { + free_pgtable_page(phys_to_virt(dma_pte_addr(pte))); + dma_clear_pte(pte); + } + pte++; + tmp += level_size(level); + } while (!first_pte_in_page(pte) && + tmp + level_size(level) - 1 <= last_pfn); + domain_flush_cache(domain, first_pte, + (void *)pte - (void *)first_pte); + + } while (tmp && tmp + level_size(level) - 1 <= last_pfn); + level++; + } /* free pgd */ if (start_pfn == 0 && last_pfn == DOMAIN_MAX_PFN(domain->gaw)) { free_pgtable_page(domain->pgd); diff --git a/drivers/leds/leds-wm831x-status.c b/drivers/leds/leds-wm831x-status.c index b7d83d6838f3..6bd5c679d877 100644 --- a/drivers/leds/leds-wm831x-status.c +++ b/drivers/leds/leds-wm831x-status.c @@ -230,9 +230,9 @@ static int wm831x_status_probe(struct platform_device *pdev) int id = pdev->id % ARRAY_SIZE(chip_pdata->status); int ret; - res = platform_get_resource(pdev, IORESOURCE_REG, 0); + res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (res == NULL) { - dev_err(&pdev->dev, "No register resource\n"); + dev_err(&pdev->dev, "No I/O resource\n"); ret = -EINVAL; goto err; } diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h index 6bc016eabdc9..c42b14b2304c 100644 --- a/drivers/md/bcache/bcache.h +++ b/drivers/md/bcache/bcache.h @@ -499,7 +499,7 @@ struct cached_dev { */ atomic_t has_dirty; - struct bch_ratelimit writeback_rate; + struct ratelimit writeback_rate; struct delayed_work writeback_rate_update; /* @@ -508,9 +508,10 @@ struct cached_dev { */ sector_t last_read; - /* Limit number of writeback bios in flight */ - struct semaphore in_flight; + /* Number of writeback bios in flight */ + atomic_t in_flight; struct closure_with_timer writeback; + struct closure_waitlist writeback_wait; struct keybuf writeback_keys; diff --git a/drivers/md/bcache/bset.c b/drivers/md/bcache/bset.c index 14032e8c7731..cb4578a327b9 100644 --- a/drivers/md/bcache/bset.c +++ b/drivers/md/bcache/bset.c @@ -918,45 +918,28 @@ struct bkey *bch_next_recurse_key(struct btree *b, struct bkey *search) /* Mergesort */ -static void sort_key_next(struct btree_iter *iter, - struct btree_iter_set *i) -{ - i->k = bkey_next(i->k); - - if (i->k == i->end) - *i = iter->data[--iter->used]; -} - static void btree_sort_fixup(struct btree_iter *iter) { while (iter->used > 1) { struct btree_iter_set *top = iter->data, *i = top + 1; + struct bkey *k; if (iter->used > 2 && btree_iter_cmp(i[0], i[1])) i++; - if (bkey_cmp(top->k, &START_KEY(i->k)) <= 0) - break; + for (k = i->k; + k != i->end && bkey_cmp(top->k, &START_KEY(k)) > 0; + k = bkey_next(k)) + if (top->k > i->k) + __bch_cut_front(top->k, k); + else if (KEY_SIZE(k)) + bch_cut_back(&START_KEY(k), top->k); - if (!KEY_SIZE(i->k)) { - sort_key_next(iter, i); - heap_sift(iter, i - top, btree_iter_cmp); - continue; - } - - if (top->k > i->k) { - if (bkey_cmp(top->k, i->k) >= 0) - sort_key_next(iter, i); - else - bch_cut_front(top->k, i->k); + if (top->k < i->k || k == i->k) + break; - heap_sift(iter, i - top, btree_iter_cmp); - } else { - /* can't happen because of comparison func */ - BUG_ON(!bkey_cmp(&START_KEY(top->k), &START_KEY(i->k))); - bch_cut_back(&START_KEY(i->k), top->k); - } + heap_sift(iter, i - top, btree_iter_cmp); } } diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c index 7d3deab11fce..7b687a6f3dec 100644 --- a/drivers/md/bcache/btree.c +++ b/drivers/md/bcache/btree.c @@ -326,25 +326,10 @@ static void do_btree_write(struct btree *b) i->csum = btree_csum_set(b, i); btree_bio_init(b); - b->bio->bi_rw = REQ_META|WRITE_SYNC|REQ_FUA; + b->bio->bi_rw = REQ_META|WRITE_SYNC; b->bio->bi_size = set_blocks(i, b->c) * block_bytes(b->c); bch_bio_map(b->bio, i); - /* - * If we're appending to a leaf node, we don't technically need FUA - - * this write just needs to be persisted before the next journal write, - * which will be marked FLUSH|FUA. - * - * Similarly if we're writing a new btree root - the pointer is going to - * be in the next journal entry. - * - * But if we're writing a new btree node (that isn't a root) or - * appending to a non leaf btree node, we need either FUA or a flush - * when we write the parent with the new pointer. FUA is cheaper than a - * flush, and writes appending to leaf nodes aren't blocking anything so - * just make all btree node writes FUA to keep things sane. - */ - bkey_copy(&k.key, &b->key); SET_PTR_OFFSET(&k.key, 0, PTR_OFFSET(&k.key, 0) + bset_offset(b, i)); @@ -633,7 +618,7 @@ static int bch_mca_shrink(struct shrinker *shrink, struct shrink_control *sc) return mca_can_free(c) * c->btree_pages; /* Return -1 if we can't do anything right now */ - if (sc->gfp_mask & __GFP_IO) + if (sc->gfp_mask & __GFP_WAIT) mutex_lock(&c->bucket_lock); else if (!mutex_trylock(&c->bucket_lock)) return -1; @@ -2157,9 +2142,6 @@ int bch_btree_insert(struct btree_op *op, struct cache_set *c) void bch_btree_set_root(struct btree *b) { unsigned i; - struct closure cl; - - closure_init_stack(&cl); BUG_ON(!b->written); @@ -2173,9 +2155,8 @@ void bch_btree_set_root(struct btree *b) b->c->root = b; __bkey_put(b->c, &b->key); - bch_journal_meta(b->c, &cl); + bch_journal_meta(b->c, NULL); pr_debug("%s for %pf", pbtree(b), __builtin_return_address(0)); - closure_sync(&cl); } /* Cache lookup */ diff --git a/drivers/md/bcache/io.c b/drivers/md/bcache/io.c index d285cd49104c..48efd4dea645 100644 --- a/drivers/md/bcache/io.c +++ b/drivers/md/bcache/io.c @@ -97,8 +97,6 @@ struct bio *bch_bio_split(struct bio *bio, int sectors, if (bio->bi_rw & REQ_DISCARD) { ret = bio_alloc_bioset(gfp, 1, bs); - if (!ret) - return NULL; idx = 0; goto out; } diff --git a/drivers/md/bcache/journal.c b/drivers/md/bcache/journal.c index 151a4ab90dd4..8a54d3b4f517 100644 --- a/drivers/md/bcache/journal.c +++ b/drivers/md/bcache/journal.c @@ -151,8 +151,7 @@ int bch_journal_read(struct cache_set *c, struct list_head *list, bitmap_zero(bitmap, SB_JOURNAL_BUCKETS); pr_debug("%u journal buckets", ca->sb.njournal_buckets); - /* - * Read journal buckets ordered by golden ratio hash to quickly + /* Read journal buckets ordered by golden ratio hash to quickly * find a sequence of buckets with valid journal entries */ for (i = 0; i < ca->sb.njournal_buckets; i++) { @@ -165,20 +164,18 @@ int bch_journal_read(struct cache_set *c, struct list_head *list, goto bsearch; } - /* - * If that fails, check all the buckets we haven't checked + /* If that fails, check all the buckets we haven't checked * already */ pr_debug("falling back to linear search"); - for (l = find_first_zero_bit(bitmap, ca->sb.njournal_buckets); - l < ca->sb.njournal_buckets; - l = find_next_zero_bit(bitmap, ca->sb.njournal_buckets, l + 1)) + for (l = 0; l < ca->sb.njournal_buckets; l++) { + if (test_bit(l, bitmap)) + continue; + if (read_bucket(l)) goto bsearch; - - if (list_empty(list)) - continue; + } bsearch: /* Binary search */ m = r = find_next_bit(bitmap, ca->sb.njournal_buckets, l + 1); @@ -198,12 +195,10 @@ int bch_journal_read(struct cache_set *c, struct list_head *list, r = m; } - /* - * Read buckets in reverse order until we stop finding more + /* Read buckets in reverse order until we stop finding more * journal entries */ - pr_debug("finishing up: m %u njournal_buckets %u", - m, ca->sb.njournal_buckets); + pr_debug("finishing up"); l = m; while (1) { @@ -231,10 +226,9 @@ int bch_journal_read(struct cache_set *c, struct list_head *list, } } - if (!list_empty(list)) - c->journal.seq = list_entry(list->prev, - struct journal_replay, - list)->j.seq; + c->journal.seq = list_entry(list->prev, + struct journal_replay, + list)->j.seq; return 0; #undef read_bucket @@ -431,7 +425,7 @@ static void do_journal_discard(struct cache *ca) return; } - switch (atomic_read(&ja->discard_in_flight)) { + switch (atomic_read(&ja->discard_in_flight) == DISCARD_IN_FLIGHT) { case DISCARD_IN_FLIGHT: return; @@ -628,7 +622,7 @@ static void journal_write_unlocked(struct closure *cl) bio_reset(bio); bio->bi_sector = PTR_OFFSET(k, i); bio->bi_bdev = ca->bdev; - bio->bi_rw = REQ_WRITE|REQ_SYNC|REQ_META|REQ_FLUSH|REQ_FUA; + bio->bi_rw = REQ_WRITE|REQ_SYNC|REQ_META|REQ_FLUSH; bio->bi_size = sectors << 9; bio->bi_end_io = journal_write_endio; @@ -692,7 +686,6 @@ void bch_journal_meta(struct cache_set *c, struct closure *cl) if (cl) BUG_ON(!closure_wait(&w->wait, cl)); - closure_flush(&c->journal.io); __journal_try_write(c, true); } } diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c index cbcf8b301edc..2f36743ce708 100644 --- a/drivers/md/bcache/request.c +++ b/drivers/md/bcache/request.c @@ -1053,22 +1053,9 @@ static void request_write(struct cached_dev *dc, struct search *s) trace_bcache_writethrough(s->orig_bio); closure_bio_submit(bio, cl, s->d); } else { + s->op.cache_bio = bio; trace_bcache_writeback(s->orig_bio); bch_writeback_add(dc, bio_sectors(bio)); - s->op.cache_bio = bio; - - if (bio->bi_rw & REQ_FLUSH) { - /* Also need to send a flush to the backing device */ - struct bio *flush = bio_alloc_bioset(0, GFP_NOIO, - dc->disk.bio_split); - - flush->bi_rw = WRITE_FLUSH; - flush->bi_bdev = bio->bi_bdev; - flush->bi_end_io = request_endio; - flush->bi_private = cl; - - closure_bio_submit(flush, cl, s->d); - } } out: closure_call(&s->op.cl, bch_insert_data, NULL, cl); diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c index e9bd6c0cca5b..4d9cca47e4c6 100644 --- a/drivers/md/bcache/sysfs.c +++ b/drivers/md/bcache/sysfs.c @@ -214,13 +214,7 @@ STORE(__cached_dev) } if (attr == &sysfs_label) { - if (size > SB_LABEL_SIZE) - return -EINVAL; - memcpy(dc->sb.label, buf, size); - if (size < SB_LABEL_SIZE) - dc->sb.label[size] = '\0'; - if (size && dc->sb.label[size - 1] == '\n') - dc->sb.label[size - 1] = '\0'; + memcpy(dc->sb.label, buf, SB_LABEL_SIZE); bch_write_bdev_super(dc, NULL); if (dc->disk.c) { memcpy(dc->disk.c->uuids[dc->disk.id].label, diff --git a/drivers/md/bcache/util.c b/drivers/md/bcache/util.c index 38a43f8d36ec..da3a99e85b1e 100644 --- a/drivers/md/bcache/util.c +++ b/drivers/md/bcache/util.c @@ -190,16 +190,7 @@ void bch_time_stats_update(struct time_stats *stats, uint64_t start_time) stats->last = now ?: 1; } -/** - * bch_next_delay() - increment @d by the amount of work done, and return how - * long to delay until the next time to do some work. - * - * @d - the struct bch_ratelimit to update - * @done - the amount of work done, in arbitrary units - * - * Returns the amount of time to delay by, in jiffies - */ -uint64_t bch_next_delay(struct bch_ratelimit *d, uint64_t done) +unsigned bch_next_delay(struct ratelimit *d, uint64_t done) { uint64_t now = local_clock(); diff --git a/drivers/md/bcache/util.h b/drivers/md/bcache/util.h index 43fd78affcea..577393e38c3a 100644 --- a/drivers/md/bcache/util.h +++ b/drivers/md/bcache/util.h @@ -452,23 +452,17 @@ read_attribute(name ## _last_ ## frequency_units) (ewma) >> factor; \ }) -struct bch_ratelimit { - /* Next time we want to do some work, in nanoseconds */ +struct ratelimit { uint64_t next; - - /* - * Rate at which we want to do work, in units per nanosecond - * The units here correspond to the units passed to bch_next_delay() - */ unsigned rate; }; -static inline void bch_ratelimit_reset(struct bch_ratelimit *d) +static inline void ratelimit_reset(struct ratelimit *d) { d->next = local_clock(); } -uint64_t bch_next_delay(struct bch_ratelimit *d, uint64_t done); +unsigned bch_next_delay(struct ratelimit *d, uint64_t done); #define __DIV_SAFE(n, d, zero) \ ({ \ diff --git a/drivers/md/bcache/writeback.c b/drivers/md/bcache/writeback.c index 841f0490d4ef..2714ed3991d1 100644 --- a/drivers/md/bcache/writeback.c +++ b/drivers/md/bcache/writeback.c @@ -91,15 +91,11 @@ static void update_writeback_rate(struct work_struct *work) static unsigned writeback_delay(struct cached_dev *dc, unsigned sectors) { - uint64_t ret; - if (atomic_read(&dc->disk.detaching) || !dc->writeback_percent) return 0; - ret = bch_next_delay(&dc->writeback_rate, sectors * 10000000ULL); - - return min_t(uint64_t, ret, HZ); + return bch_next_delay(&dc->writeback_rate, sectors * 10000000ULL); } /* Background writeback */ @@ -169,7 +165,7 @@ static void refill_dirty(struct closure *cl) up_write(&dc->writeback_lock); - bch_ratelimit_reset(&dc->writeback_rate); + ratelimit_reset(&dc->writeback_rate); /* Punt to workqueue only so we don't recurse and blow the stack */ continue_at(cl, read_dirty, dirty_wq); @@ -250,7 +246,9 @@ static void write_dirty_finish(struct closure *cl) } bch_keybuf_del(&dc->writeback_keys, w); - up(&dc->in_flight); + atomic_dec_bug(&dc->in_flight); + + closure_wake_up(&dc->writeback_wait); closure_return_with_destructor(cl, dirty_io_destructor); } @@ -280,7 +278,7 @@ static void write_dirty(struct closure *cl) trace_bcache_write_dirty(&io->bio); closure_bio_submit(&io->bio, cl, &io->dc->disk); - continue_at(cl, write_dirty_finish, system_wq); + continue_at(cl, write_dirty_finish, dirty_wq); } static void read_dirty_endio(struct bio *bio, int error) @@ -301,7 +299,7 @@ static void read_dirty_submit(struct closure *cl) trace_bcache_read_dirty(&io->bio); closure_bio_submit(&io->bio, cl, &io->dc->disk); - continue_at(cl, write_dirty, system_wq); + continue_at(cl, write_dirty, dirty_wq); } static void read_dirty(struct closure *cl) @@ -326,8 +324,12 @@ static void read_dirty(struct closure *cl) if (delay > 0 && (KEY_START(&w->key) != dc->last_read || - jiffies_to_msecs(delay) > 50)) - delay = schedule_timeout_uninterruptible(delay); + jiffies_to_msecs(delay) > 50)) { + w->private = NULL; + + closure_delay(&dc->writeback, delay); + continue_at(cl, read_dirty, dirty_wq); + } dc->last_read = KEY_OFFSET(&w->key); @@ -352,10 +354,15 @@ static void read_dirty(struct closure *cl) pr_debug("%s", pkey(&w->key)); - down(&dc->in_flight); - closure_call(&io->cl, read_dirty_submit, NULL, cl); + closure_call(&io->cl, read_dirty_submit, NULL, &dc->disk.cl); delay = writeback_delay(dc, KEY_SIZE(&w->key)); + + atomic_inc(&dc->in_flight); + + if (!closure_wait_event(&dc->writeback_wait, cl, + atomic_read(&dc->in_flight) < 64)) + continue_at(cl, read_dirty, dirty_wq); } if (0) { @@ -365,16 +372,11 @@ static void read_dirty(struct closure *cl) bch_keybuf_del(&dc->writeback_keys, w); } - /* - * Wait for outstanding writeback IOs to finish (and keybuf slots to be - * freed) before refilling again - */ - continue_at(cl, refill_dirty, dirty_wq); + refill_dirty(cl); } void bch_cached_dev_writeback_init(struct cached_dev *dc) { - sema_init(&dc->in_flight, 64); closure_init_unlocked(&dc->writeback); init_rwsem(&dc->writeback_lock); @@ -404,7 +406,7 @@ void bch_writeback_exit(void) int __init bch_writeback_init(void) { - dirty_wq = create_workqueue("bcache_writeback"); + dirty_wq = create_singlethread_workqueue("bcache_writeback"); if (!dirty_wq) return -ENOMEM; diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index c5b7b3dd91cc..5adede17ddf6 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -1284,17 +1284,8 @@ static int do_end_io(struct multipath *m, struct request *clone, if (!error && !clone->errors) return 0; /* I/O complete */ - if (error == -EOPNOTSUPP || error == -EREMOTEIO || error == -EILSEQ) { - if ((clone->cmd_flags & REQ_WRITE_SAME) && - !clone->q->limits.max_write_same_sectors) { - struct queue_limits *limits; - - /* device doesn't really support WRITE SAME, disable it */ - limits = dm_get_queue_limits(dm_table_get_md(m->ti->table)); - limits->max_write_same_sectors = 0; - } + if (error == -EOPNOTSUPP || error == -EREMOTEIO || error == -EILSEQ) return error; - } if (mpio->pgpath) fail_path(mpio->pgpath); diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c index 2dea49c4279e..1d3fe1a40a9b 100644 --- a/drivers/md/dm-raid.c +++ b/drivers/md/dm-raid.c @@ -380,7 +380,7 @@ static int validate_region_size(struct raid_set *rs, unsigned long region_size) static int validate_raid_redundancy(struct raid_set *rs) { unsigned i, rebuild_cnt = 0; - unsigned rebuilds_per_group = 0, copies, d; + unsigned rebuilds_per_group, copies, d; unsigned group_size, last_group_start; for (i = 0; i < rs->md.raid_disks; i++) diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c index 4caa8e6d59d7..3ac415675b6c 100644 --- a/drivers/md/dm-snap-persistent.c +++ b/drivers/md/dm-snap-persistent.c @@ -256,7 +256,7 @@ static int chunk_io(struct pstore *ps, void *area, chunk_t chunk, int rw, */ INIT_WORK_ONSTACK(&req.work, do_metadata); queue_work(ps->metadata_wq, &req.work); - flush_workqueue(ps->metadata_wq); + flush_work(&req.work); return req.result; } diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index aec57d76db5d..c434e5aab2df 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -725,16 +725,17 @@ static int calc_max_buckets(void) */ static int init_hash_tables(struct dm_snapshot *s) { - sector_t hash_size, cow_dev_size, max_buckets; + sector_t hash_size, cow_dev_size, origin_dev_size, max_buckets; /* * Calculate based on the size of the original volume or * the COW volume... */ cow_dev_size = get_dev_size(s->cow->bdev); + origin_dev_size = get_dev_size(s->origin->bdev); max_buckets = calc_max_buckets(); - hash_size = cow_dev_size >> s->store->chunk_shift; + hash_size = min(origin_dev_size, cow_dev_size) >> s->store->chunk_shift; hash_size = min(hash_size, max_buckets); if (hash_size < 64) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 1c13071a81be..33f20103d8d5 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -2218,17 +2218,6 @@ struct target_type *dm_get_immutable_target_type(struct mapped_device *md) return md->immutable_target_type; } -/* - * The queue_limits are only valid as long as you have a reference - * count on 'md'. - */ -struct queue_limits *dm_get_queue_limits(struct mapped_device *md) -{ - BUG_ON(!atomic_read(&md->holders)); - return &md->queue->limits; -} -EXPORT_SYMBOL_GPL(dm_get_queue_limits); - /* * Fully initialize a request-based queue (->elevator, ->request_fn, etc). */ diff --git a/drivers/media/common/siano/smsdvb-main.c b/drivers/media/common/siano/smsdvb-main.c index 8df1aea5fbff..297f1b2f9a32 100644 --- a/drivers/media/common/siano/smsdvb-main.c +++ b/drivers/media/common/siano/smsdvb-main.c @@ -275,8 +275,7 @@ static void smsdvb_update_per_slices(struct smsdvb_client_t *client, /* Legacy PER/BER */ tmp = p->ets_packets * 65535; - if (p->ts_packets + p->ets_packets) - do_div(tmp, p->ts_packets + p->ets_packets); + do_div(tmp, p->ts_packets + p->ets_packets); client->legacy_per = tmp; } diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index 2c7217fb1415..856374bd3676 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -157,6 +157,7 @@ static struct regdata mb86a20s_init2[] = { { 0x45, 0x04 }, /* CN symbol 4 */ { 0x48, 0x04 }, /* CN manual mode */ + { 0x50, 0xd5 }, { 0x51, 0x01 }, /* Serial */ { 0x50, 0xd6 }, { 0x51, 0x1f }, { 0x50, 0xd2 }, { 0x51, 0x03 }, { 0x50, 0xd7 }, { 0x51, 0xbf }, @@ -1859,15 +1860,16 @@ static int mb86a20s_initfe(struct dvb_frontend *fe) dev_dbg(&state->i2c->dev, "%s: IF=%d, IF reg=0x%06llx\n", __func__, state->if_freq, (long long)pll); - if (!state->config->is_serial) + if (!state->config->is_serial) { regD5 &= ~1; - rc = mb86a20s_writereg(state, 0x50, 0xd5); - if (rc < 0) - goto err; - rc = mb86a20s_writereg(state, 0x51, regD5); - if (rc < 0) - goto err; + rc = mb86a20s_writereg(state, 0x50, 0xd5); + if (rc < 0) + goto err; + rc = mb86a20s_writereg(state, 0x51, regD5); + if (rc < 0) + goto err; + } rc = mb86a20s_writeregdata(state, mb86a20s_init2); if (rc < 0) diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig index 53196f1366f3..d4e2ed3f27e5 100644 --- a/drivers/media/pci/Kconfig +++ b/drivers/media/pci/Kconfig @@ -1,7 +1,6 @@ -if PCI && MEDIA_SUPPORT - menuconfig MEDIA_PCI_SUPPORT bool "Media PCI Adapters" + depends on PCI && MEDIA_SUPPORT help Enable media drivers for PCI/PCIe bus. If you have such devices, say Y. @@ -46,4 +45,3 @@ source "drivers/media/pci/ddbridge/Kconfig" endif endif #MEDIA_PCI_SUPPORT -endif #PCI diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index c504f70d4e90..9d1481a60bd9 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -1933,7 +1933,7 @@ MODULE_DEVICE_TABLE(platform, coda_platform_ids); #ifdef CONFIG_OF static const struct of_device_id coda_dt_ids[] = { - { .compatible = "fsl,imx27-vpu", .data = &coda_devdata[CODA_IMX27] }, + { .compatible = "fsl,imx27-vpu", .data = &coda_platform_ids[CODA_IMX27] }, { .compatible = "fsl,imx53-vpu", .data = &coda_devdata[CODA_IMX53] }, { /* sentinel */ } }; diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index f45b940d6072..33b5ffc8d66d 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -1122,13 +1122,9 @@ static int gsc_probe(struct platform_device *pdev) goto err_clk; } - ret = v4l2_device_register(dev, &gsc->v4l2_dev); - if (ret) - goto err_clk; - ret = gsc_register_m2m_device(gsc); if (ret) - goto err_v4l2; + goto err_clk; platform_set_drvdata(pdev, gsc); pm_runtime_enable(dev); @@ -1151,8 +1147,6 @@ static int gsc_probe(struct platform_device *pdev) pm_runtime_put(dev); err_m2m: gsc_unregister_m2m_device(gsc); -err_v4l2: - v4l2_device_unregister(&gsc->v4l2_dev); err_clk: gsc_clk_put(gsc); return ret; @@ -1163,7 +1157,6 @@ static int gsc_remove(struct platform_device *pdev) struct gsc_dev *gsc = platform_get_drvdata(pdev); gsc_unregister_m2m_device(gsc); - v4l2_device_unregister(&gsc->v4l2_dev); vb2_dma_contig_cleanup_ctx(gsc->alloc_ctx); pm_runtime_disable(&pdev->dev); diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h index 76435d3bf62d..cc19bba09bd1 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.h +++ b/drivers/media/platform/exynos-gsc/gsc-core.h @@ -343,7 +343,6 @@ struct gsc_dev { unsigned long state; struct vb2_alloc_ctx *alloc_ctx; struct video_device vdev; - struct v4l2_device v4l2_dev; }; /** diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index e576ff2de3de..40a73f7d20da 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -751,7 +751,6 @@ int gsc_register_m2m_device(struct gsc_dev *gsc) gsc->vdev.release = video_device_release_empty; gsc->vdev.lock = &gsc->lock; gsc->vdev.vfl_dir = VFL_DIR_M2M; - gsc->vdev.v4l2_dev = &gsc->v4l2_dev; snprintf(gsc->vdev.name, sizeof(gsc->vdev.name), "%s.%d:m2m", GSC_MODULE_NAME, gsc->id); diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index b5b480befcc3..15ef8f28239b 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -1441,9 +1441,9 @@ static int fimc_md_probe(struct platform_device *pdev) err_unlock: mutex_unlock(&fmd->media_dev.graph_mutex); err_clk: + media_device_unregister(&fmd->media_dev); fimc_md_put_clocks(fmd); fimc_md_unregister_entities(fmd); - media_device_unregister(&fmd->media_dev); err_md: v4l2_device_unregister(&fmd->v4l2_dev); return ret; diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index fd6289d60cde..553d87e5ceab 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -784,7 +784,6 @@ static int g2d_probe(struct platform_device *pdev) } *vfd = g2d_videodev; vfd->lock = &dev->mutex; - vfd->v4l2_dev = &dev->v4l2_dev; ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); if (ret) { v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index 7cac453e34c7..0a7d520636a9 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -1,7 +1,6 @@ -if USB && MEDIA_SUPPORT - menuconfig MEDIA_USB_SUPPORT bool "Media USB Adapters" + depends on USB && MEDIA_SUPPORT help Enable media drivers for USB bus. If you have such devices, say Y. @@ -53,4 +52,3 @@ source "drivers/media/usb/em28xx/Kconfig" endif endif #MEDIA_USB_SUPPORT -endif #USB diff --git a/drivers/media/usb/hdpvr/hdpvr-core.c b/drivers/media/usb/hdpvr/hdpvr-core.c index 77d7b7fbdc7e..8247c19d6260 100644 --- a/drivers/media/usb/hdpvr/hdpvr-core.c +++ b/drivers/media/usb/hdpvr/hdpvr-core.c @@ -311,11 +311,6 @@ static int hdpvr_probe(struct usb_interface *interface, dev->workqueue = 0; - /* init video transfer queues first of all */ - /* to prevent oops in hdpvr_delete() on error paths */ - INIT_LIST_HEAD(&dev->free_buff_list); - INIT_LIST_HEAD(&dev->rec_buff_list); - /* register v4l2_device early so it can be used for printks */ if (v4l2_device_register(&interface->dev, &dev->v4l2_dev)) { dev_err(&interface->dev, "v4l2_device_register failed\n"); @@ -338,6 +333,10 @@ static int hdpvr_probe(struct usb_interface *interface, if (!dev->workqueue) goto error; + /* init video transfer queues */ + INIT_LIST_HEAD(&dev->free_buff_list); + INIT_LIST_HEAD(&dev->rec_buff_list); + dev->options = hdpvr_default_options; if (default_video_input < HDPVR_VIDEO_INPUTS) @@ -414,7 +413,7 @@ static int hdpvr_probe(struct usb_interface *interface, video_nr[atomic_inc_return(&dev_nr)]); if (retval < 0) { v4l2_err(&dev->v4l2_dev, "registering videodev failed\n"); - goto reg_fail; + goto error; } /* let the user know what node this device is now attached to */ diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index b83e3ca12a41..621c7a373390 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c @@ -759,7 +759,7 @@ static int ilo_probe(struct pci_dev *pdev, /* Ignore subsystem_device = 0x1979 (set by BIOS) */ if (pdev->subsystem_device == 0x1979) - return 0; + goto out; if (max_ccb > MAX_CCB) max_ccb = MAX_CCB; @@ -899,7 +899,7 @@ static void __exit ilo_exit(void) class_destroy(ilo_class); } -MODULE_VERSION("1.4.1"); +MODULE_VERSION("1.4"); MODULE_ALIAS(ILO_NAME); MODULE_DESCRIPTION(ILO_NAME); MODULE_AUTHOR("David Altobelli "); diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index 3db9291849f1..b3e50984d2c8 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -57,7 +57,6 @@ void mei_amthif_reset_params(struct mei_device *dev) dev->iamthif_ioctl = false; dev->iamthif_state = MEI_IAMTHIF_IDLE; dev->iamthif_timer = 0; - dev->iamthif_stall_timer = 0; } /** diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 99cc0b07a713..9ecd49a7be1b 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -295,13 +295,10 @@ int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) if (cl->reading_state != MEI_READ_COMPLETE && !waitqueue_active(&cl->rx_wait)) { - mutex_unlock(&dev->device_lock); if (wait_event_interruptible(cl->rx_wait, - cl->reading_state == MEI_READ_COMPLETE || - mei_cl_is_transitioning(cl))) { - + (MEI_READ_COMPLETE == cl->reading_state))) { if (signal_pending(current)) return -EINTR; return -ERESTARTSYS; diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 467d9dda97fa..cfdb144526aa 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -76,12 +76,6 @@ static inline bool mei_cl_cmp_id(const struct mei_cl *cl1, (cl1->host_client_id == cl2->host_client_id) && (cl1->me_client_id == cl2->me_client_id); } -static inline bool mei_cl_is_transitioning(struct mei_cl *cl) -{ - return (MEI_FILE_INITIALIZING == cl->state || - MEI_FILE_DISCONNECTED == cl->state || - MEI_FILE_DISCONNECTING == cl->state); -} int mei_cl_flow_ctrl_creds(struct mei_cl *cl); diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index 23b5b7bcba9f..6916045166eb 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -35,15 +35,11 @@ static void mei_hbm_me_cl_allocate(struct mei_device *dev) struct mei_me_client *clients; int b; - dev->me_clients_num = 0; - dev->me_client_presentation_num = 0; - dev->me_client_index = 0; - /* count how many ME clients we have */ for_each_set_bit(b, dev->me_clients_map, MEI_CLIENTS_MAX) dev->me_clients_num++; - if (dev->me_clients_num == 0) + if (dev->me_clients_num <= 0) return; kfree(dev->me_clients); @@ -225,7 +221,7 @@ static int mei_hbm_prop_req(struct mei_device *dev) struct hbm_props_request *prop_req; const size_t len = sizeof(struct hbm_props_request); unsigned long next_client_index; - unsigned long client_num; + u8 client_num; client_num = dev->me_client_presentation_num; @@ -654,6 +650,8 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) if (dev->dev_state == MEI_DEV_INIT_CLIENTS && dev->hbm_state == MEI_HBM_ENUM_CLIENTS) { dev->init_clients_timer = 0; + dev->me_client_presentation_num = 0; + dev->me_client_index = 0; mei_hbm_me_cl_allocate(dev); dev->hbm_state = MEI_HBM_CLIENT_PROPERTIES; diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 1bf3f8b5ce3a..822170f00348 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -176,14 +176,16 @@ static void mei_me_hw_reset(struct mei_device *dev, bool intr_enable) struct mei_me_hw *hw = to_me_hw(dev); u32 hcsr = mei_hcsr_read(hw); - hcsr |= H_RST | H_IG | H_IS; + dev_dbg(&dev->pdev->dev, "before reset HCSR = 0x%08x.\n", hcsr); + + hcsr |= (H_RST | H_IG); if (intr_enable) hcsr |= H_IE; else - hcsr &= ~H_IE; + hcsr |= ~H_IE; - mei_me_reg_write(hw, H_CSR, hcsr); + mei_hcsr_set(hw, hcsr); if (dev->dev_state == MEI_DEV_POWER_DOWN) mei_me_hw_reset_release(dev); @@ -236,18 +238,14 @@ static int mei_me_hw_ready_wait(struct mei_device *dev) if (mei_me_hw_is_ready(dev)) return 0; - dev->recvd_hw_ready = false; mutex_unlock(&dev->device_lock); err = wait_event_interruptible_timeout(dev->wait_hw_ready, - dev->recvd_hw_ready, - mei_secs_to_jiffies(MEI_INTEROP_TIMEOUT)); + dev->recvd_hw_ready, MEI_INTEROP_TIMEOUT); mutex_lock(&dev->device_lock); if (!err && !dev->recvd_hw_ready) { - if (!err) - err = -ETIMEDOUT; dev_err(&dev->pdev->dev, - "wait hw ready failed. status = %d\n", err); - return err; + "wait hw ready failed. status = 0x%x\n", err); + return -ETIMEDOUT; } dev->recvd_hw_ready = false; @@ -484,9 +482,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) /* check if ME wants a reset */ if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING && - dev->dev_state != MEI_DEV_INITIALIZING && - dev->dev_state != MEI_DEV_POWER_DOWN && - dev->dev_state != MEI_DEV_POWER_UP) { + dev->dev_state != MEI_DEV_INITIALIZING) { dev_dbg(&dev->pdev->dev, "FW not ready.\n"); mei_reset(dev, 1); mutex_unlock(&dev->device_lock); diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 878bc1c29d2b..f580d30bb784 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -143,8 +143,7 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) dev->hbm_state = MEI_HBM_IDLE; - if (dev->dev_state != MEI_DEV_INITIALIZING && - dev->dev_state != MEI_DEV_POWER_UP) { + if (dev->dev_state != MEI_DEV_INITIALIZING) { if (dev->dev_state != MEI_DEV_DISABLED && dev->dev_state != MEI_DEV_POWER_DOWN) dev->dev_state = MEI_DEV_RESETTING; @@ -164,9 +163,6 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg)); } - /* we're already in reset, cancel the init timer */ - dev->init_clients_timer = 0; - dev->me_clients_num = 0; dev->rd_msg_hdr = 0; dev->wd_pending = false; diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 701698de5ac7..053139f61086 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -262,16 +262,19 @@ static ssize_t mei_read(struct file *file, char __user *ubuf, mutex_unlock(&dev->device_lock); if (wait_event_interruptible(cl->rx_wait, - MEI_READ_COMPLETE == cl->reading_state || - mei_cl_is_transitioning(cl))) { - + (MEI_READ_COMPLETE == cl->reading_state || + MEI_FILE_INITIALIZING == cl->state || + MEI_FILE_DISCONNECTED == cl->state || + MEI_FILE_DISCONNECTING == cl->state))) { if (signal_pending(current)) return -EINTR; return -ERESTARTSYS; } mutex_lock(&dev->device_lock); - if (mei_cl_is_transitioning(cl)) { + if (MEI_FILE_INITIALIZING == cl->state || + MEI_FILE_DISCONNECTED == cl->state || + MEI_FILE_DISCONNECTING == cl->state) { rets = -EBUSY; goto out; } diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 73c7700aee21..4de5140e7379 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -402,9 +402,9 @@ struct mei_device { struct mei_me_client *me_clients; /* Note: memory has to be allocated */ DECLARE_BITMAP(me_clients_map, MEI_CLIENTS_MAX); DECLARE_BITMAP(host_clients_map, MEI_CLIENTS_MAX); - unsigned long me_clients_num; - unsigned long me_client_presentation_num; - unsigned long me_client_index; + u8 me_clients_num; + u8 me_client_presentation_num; + u8 me_client_index; struct mei_cl wd_cl; enum mei_wd_states wd_state; diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 72bab34e3b2d..113713081520 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -96,6 +96,7 @@ struct mmc_blk_data { #define MMC_BLK_CMD23 (1 << 0) /* Can do SET_BLOCK_COUNT for multiblock */ #define MMC_BLK_REL_WR (1 << 1) /* MMC Reliable write support */ #define MMC_BLK_PACKED_CMD (1 << 2) /* MMC packed command support */ +#define MMC_BLK_SUSPENDED (1 << 3) /* MMC block device suspended */ unsigned int usage; unsigned int read_only; @@ -106,7 +107,8 @@ struct mmc_blk_data { #define MMC_BLK_WRITE BIT(1) #define MMC_BLK_DISCARD BIT(2) #define MMC_BLK_SECDISCARD BIT(3) - +#define MMC_BLK_RPMB BIT(4) +#define MMC_BLK_USER BIT(5) /* * Only set in main mmc_blk_data associated * with mmc_card with mmc_set_drvdata, and keeps @@ -146,6 +148,9 @@ static inline void mmc_blk_clear_packed(struct mmc_queue_req *mqrq) packed->blocks = 0; } +static int mmc_rpmb_req_process(struct mmc_blk_data *, + struct mmc_ioc_rpmb_req *); + static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) { struct mmc_blk_data *md; @@ -161,12 +166,6 @@ static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) return md; } -int mmc_access_rpmb(struct mmc_queue *mq) -{ - struct mmc_blk_data *md = mq->data; - return md && md->part_type == EXT_CSD_PART_CONFIG_ACC_RPMB; -} - static inline int mmc_get_devidx(struct gendisk *disk) { int devmaj = MAJOR(disk_devt(disk)); @@ -574,12 +573,53 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev, return err; } +static int mmc_blk_ioctl_rpmb_req(struct block_device *bdev, + struct mmc_ioc_rpmb_req __user *ptr) +{ + struct mmc_ioc_rpmb_req req; + struct mmc_blk_data *md = NULL; + int err = 0; + + /* The caller must have CAP_SYS_RAWIO */ + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + memset(&req, 0, sizeof(req)); + + if (copy_from_user(&req, ptr, sizeof(req))) + return -EFAULT; + + md = mmc_blk_get(bdev->bd_disk); + if (!md) { + pr_err("%s: NO eMMC block data. Try it later\n", + __func__); + return -ENODEV; + } + /* handle RPMB request event */ + err = mmc_rpmb_req_process(md, &req); + if (err) { + mmc_blk_put(md); + return err; + } + /* + * feedback to user space + */ + if (copy_to_user(ptr, &req, sizeof(req))) + return -EFAULT; + + mmc_blk_put(md); + return 0; +} + static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { int ret = -EINVAL; if (cmd == MMC_IOC_CMD) ret = mmc_blk_ioctl_cmd(bdev, (struct mmc_ioc_cmd __user *)arg); + else if (cmd == MMC_IOC_RPMB_REQ) + ret = mmc_blk_ioctl_rpmb_req(bdev, + (struct mmc_ioc_rpmb_req __user *)arg); return ret; } @@ -897,6 +937,114 @@ static inline void mmc_blk_reset_success(struct mmc_blk_data *md, int type) md->reset_done &= ~type; } +static int mmc_rpmb_req_process(struct mmc_blk_data *md, + struct mmc_ioc_rpmb_req *req) +{ + struct mmc_core_rpmb_req rpmb_req; + struct mmc_card *card = NULL; + int ret; + + if (!md || !req) + return -EINVAL; + + if (!(md->flags & MMC_BLK_CMD23) || + (md->part_type != EXT_CSD_PART_CONFIG_ACC_RPMB)) + return -EOPNOTSUPP; + + card = md->queue.card; + if (!card || !mmc_card_mmc(card) || !card->ext_csd.rpmb_size) + return -ENODEV; + + memset(&rpmb_req, 0, sizeof(struct mmc_core_rpmb_req)); + rpmb_req.req = req; + /* check request */ + ret = mmc_rpmb_pre_frame(&rpmb_req, card); + if (ret) { + pr_err("%s: prepare frame failed\n", mmc_hostname(card->host)); + return ret; + } + + mmc_claim_host(card->host); + + if (md->flags & MMC_BLK_SUSPENDED) { + pr_warn("%s: MMC block device is already suspended\n", + mmc_hostname(card->host)); + ret = -EPERM; + goto out; + } + /* + * before start, let's change to RPMB partition first + */ + ret = mmc_blk_part_switch(card, md); + if (ret) { + pr_err("%s: Invalid RPMB partition switch (%d)!\n", + mmc_hostname(card->host), ret); + /* + * In case partition is not in user data area, make + * a force partition switch. + * we need reset eMMC card at here + */ + ret = mmc_blk_reset(md, card->host, MMC_BLK_RPMB); + if (!ret) + mmc_blk_reset_success(md, MMC_BLK_RPMB); + else + pr_err("%s: eMMC card reset failed (%d)\n", + mmc_hostname(card->host), ret); + goto out; + } + + ret = mmc_rpmb_partition_ops(&rpmb_req, card); + if (ret) + pr_err("%s: failed (%d) to handle RPMB request type (%d)!\n", + mmc_hostname(card->host), ret, req->type); +out: + mmc_release_host(card->host); + mmc_rpmb_post_frame(&rpmb_req); + return ret; +} + +int mmc_access_rpmb(struct mmc_queue *mq) +{ + struct mmc_blk_data *md = mq->data; + /* + * If this is a RPMB partition access, return ture + */ + if (md && md->part_type == EXT_CSD_PART_CONFIG_ACC_RPMB) + return true; + + return false; +} +EXPORT_SYMBOL_GPL(mmc_access_rpmb); + +int mmc_rpmb_req_handle(struct device *emmc, struct mmc_ioc_rpmb_req *req) +{ + int ret = 0; + struct gendisk *disk = NULL; + struct mmc_blk_data *md = NULL; + + if (!emmc || !req) + return -EINVAL; + + disk = dev_to_disk(emmc); + if (!disk) { + pr_err("%s: NO eMMC disk found. Try it later\n", + __func__); + return -ENODEV; + } + + md = mmc_blk_get(disk); + if (!md) { + pr_err("%s: NO eMMC block data. Try it later\n", + __func__); + return -ENODEV; + } + ret = mmc_rpmb_req_process(md, req); + mmc_blk_put(md); + + return ret; +} +EXPORT_SYMBOL_GPL(mmc_rpmb_req_handle); + static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req) { struct mmc_blk_data *md = mq->data; @@ -1905,11 +2053,16 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) ret = mmc_blk_part_switch(card, md); if (ret) { - if (req) { - blk_end_request_all(req, -EIO); + pr_err("%s: switch part failed. Try to reset eMMC\n", + mmc_hostname(card->host)); + if (mmc_blk_reset(md, card->host, MMC_BLK_USER)) { + if (req) + blk_end_request_all(req, -EIO); + ret = 0; + goto out; } - ret = 0; - goto out; + pr_info("%s: Reset eMMC success\n", mmc_hostname(card->host)); + mmc_blk_reset_success(md, MMC_BLK_USER); } mq->flags &= ~MMC_QUEUE_NEW_REQUEST; @@ -2372,6 +2525,19 @@ static int mmc_blk_suspend(struct mmc_card *card) mmc_queue_suspend(&md->queue); list_for_each_entry(part_md, &md->part, part) { mmc_queue_suspend(&part_md->queue); + if (part_md->part_type == + EXT_CSD_PART_CONFIG_ACC_RPMB) { + /* + * RPMB partition is accessed by API directly. + * Driver need to set a flag when suspending + * MMC block device to notify API that the + * accessing of RPMB partition needs to be + * stopped + */ + mmc_claim_host(card->host); + part_md->flags |= MMC_BLK_SUSPENDED; + mmc_release_host(card->host); + } } } return 0; @@ -2391,6 +2557,18 @@ static int mmc_blk_resume(struct mmc_card *card) mmc_queue_resume(&md->queue); list_for_each_entry(part_md, &md->part, part) { mmc_queue_resume(&part_md->queue); + if (part_md->part_type == + EXT_CSD_PART_CONFIG_ACC_RPMB) { + /* + * RPMB partition is accessed by API directly. + * Driver need to clear MMC_BLK_SUSPENDED flag + * to make sure the next RPMB partition access + * request won't be blocked + */ + mmc_claim_host(card->host); + part_md->flags &= ~MMC_BLK_SUSPENDED; + mmc_release_host(card->host); + } } } return 0; diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index 2c84ec53dbc8..88327f7f2498 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -38,7 +38,7 @@ static int mmc_prep_request(struct request_queue *q, struct request *req) return BLKPREP_KILL; } - if (mq && mmc_card_removed(mq->card) || mmc_access_rpmb(mq)) + if (mq && (mmc_card_removed(mq->card) || mmc_access_rpmb(mq))) return BLKPREP_KILL; req->cmd_flags |= REQ_DONTPREP; diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h index 23460e643aaf..3bbd4e64b1aa 100644 --- a/drivers/mmc/card/queue.h +++ b/drivers/mmc/card/queue.h @@ -73,5 +73,5 @@ extern void mmc_queue_bounce_post(struct mmc_queue_req *); extern int mmc_packed_init(struct mmc_queue *, struct mmc_card *); extern void mmc_packed_clean(struct mmc_queue *); -int mmc_access_rpmb(struct mmc_queue *mq); +extern int mmc_access_rpmb(struct mmc_queue *); #endif diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 89d60824aaa6..fc3917923db3 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -504,6 +504,9 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) * RPMB regions are defined in multiples of 128K. */ card->ext_csd.raw_rpmb_size_mult = ext_csd[EXT_CSD_RPMB_MULT]; + card->ext_csd.rpmb_size = 128 * + card->ext_csd.raw_rpmb_size_mult; + card->ext_csd.rpmb_size <<= 2; /* Unit: half sector */ if (ext_csd[EXT_CSD_RPMB_MULT] && mmc_host_cmd23(card->host)) { mmc_part_add(card, ext_csd[EXT_CSD_RPMB_MULT] << 17, EXT_CSD_PART_CONFIG_ACC_RPMB, @@ -555,6 +558,17 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) card->ext_csd.data_sector_size = 512; } + /* + * If use legacy reliable write, then the blk counts must not + * big than the reliable write sectors + */ + if (!(card->ext_csd.rel_param & EXT_CSD_WR_REL_PARAM_EN)) { + if (card->ext_csd.rel_sectors < RPMB_AVALIABLE_SECTORS) + card->rpmb_max_req = card->ext_csd.rel_sectors; + else + card->rpmb_max_req = RPMB_AVALIABLE_SECTORS; + } else + card->rpmb_max_req = RPMB_AVALIABLE_SECTORS; out: return err; } @@ -642,6 +656,7 @@ MMC_DEV_ATTR(enhanced_area_offset, "%llu\n", MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size); MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult); MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors); +MMC_DEV_ATTR(rpmb_size, "%d\n", card->ext_csd.rpmb_size); static struct attribute *mmc_std_attrs[] = { &dev_attr_cid.attr, @@ -660,6 +675,7 @@ static struct attribute *mmc_std_attrs[] = { &dev_attr_enhanced_area_size.attr, &dev_attr_raw_rpmb_size_mult.attr, &dev_attr_rel_sectors.attr, + &dev_attr_rpmb_size.attr, NULL, }; diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index a3d06158c8e6..432af79a91cc 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -638,3 +638,415 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status) return 0; } + +static int mmc_rpmb_send_command(struct mmc_card *card, u8 *buf, __u16 blks, + __u16 type, u8 req_type) +{ + struct mmc_request mrq = {NULL}; + struct mmc_command cmd = {0}; + struct mmc_command sbc = {0}; + struct mmc_data data = {0}; + struct scatterlist sg; + u8 *transfer_buf = NULL; + + mrq.sbc = &sbc; + mrq.cmd = &cmd; + mrq.data = &data; + mrq.stop = NULL; + transfer_buf = kzalloc(512 * blks, GFP_KERNEL); + if (!transfer_buf) + return -ENOMEM; + + /* + * set CMD23 + */ + sbc.opcode = MMC_SET_BLOCK_COUNT; + sbc.arg = blks; + if ((req_type == RPMB_REQ) && (type == RPMB_WRITE_DATA || + type == RPMB_PROGRAM_KEY)) + sbc.arg |= 1 << 31; + sbc.flags = MMC_RSP_R1 | MMC_CMD_AC; + + /* + * set CMD25/18 + */ + sg_init_one(&sg, transfer_buf, 512 * blks); + if (req_type == RPMB_REQ) { + cmd.opcode = MMC_WRITE_MULTIPLE_BLOCK; + sg_copy_from_buffer(&sg, 1, buf, 512 * blks); + data.flags |= MMC_DATA_WRITE; + } else { + cmd.opcode = MMC_READ_MULTIPLE_BLOCK; + data.flags |= MMC_DATA_READ; + } + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + data.blksz = 512; + data.blocks = blks; + data.sg = &sg; + data.sg_len = 1; + + mmc_set_data_timeout(&data, card); + + mmc_wait_for_req(card->host, &mrq); + + if (req_type != RPMB_REQ) + sg_copy_to_buffer(&sg, 1, buf, 512 * blks); + + kfree(transfer_buf); + + if (cmd.error) + return cmd.error; + if (data.error) + return data.error; + return 0; +} + +void mmc_rpmb_post_frame(struct mmc_core_rpmb_req *rpmb_req) +{ + int i; + struct mmc_ioc_rpmb_req *p_req; + __u8 *buf_frame; + + if (!rpmb_req || !rpmb_req->ready) + return; + + p_req = rpmb_req->req; + buf_frame = rpmb_req->frame; + + if (!p_req || !buf_frame) + return; + /* + * Regarding to the check rules, here is the post + * rules + * All will return result. + * GET_WRITE_COUNTER: + * must: write counter, nonce + * optional: MAC + * WRITE_DATA: + * must: MAC, write counter + * READ_DATA: + * must: nonce, data + * optional: MAC + * PROGRAM_KEY: + * must: Nothing + * + * Except READ_DATA, all of these operations only need to parse + * one frame. READ_DATA needs blks frames to get DATA + */ + + memcpy(p_req->result, buf_frame + RPMB_RES_BEG, 2); + *p_req->result = be16_to_cpup(p_req->result); + + if (p_req->type == RPMB_PROGRAM_KEY) + goto out; + + if (p_req->type == RPMB_GET_WRITE_COUNTER || + p_req->type == RPMB_WRITE_DATA) { + memcpy(p_req->wc, buf_frame + RPMB_WCOUNTER_BEG, 4); + *p_req->wc = be32_to_cpup(p_req->wc); + } + + if (p_req->type == RPMB_GET_WRITE_COUNTER || + p_req->type == RPMB_READ_DATA) { + /* nonce copy */ + memcpy(p_req->nonce, buf_frame + RPMB_NONCE_BEG, 16); + } + /* + * Take MAC within the last package + */ + if (p_req->type == RPMB_READ_DATA) { + __u8 *data = p_req->data; + for (i = 0; i < p_req->blk_cnt; i++) { + memcpy(data, buf_frame + i * 512 + RPMB_DATA_BEG, 256); + data += 256; + } + /* + * MAC stored in the last package + */ + if (p_req->mac) + memcpy(p_req->mac, buf_frame + i * 512 + RPMB_MAC_BEG, + 32); + } else if (p_req->mac) + memcpy(p_req->mac, buf_frame + RPMB_MAC_BEG, 32); +out: + kfree(buf_frame); + rpmb_req->frame = NULL; + return; +} +EXPORT_SYMBOL_GPL(mmc_rpmb_post_frame); + +static int mmc_rpmb_request_check(struct mmc_card *card, + struct mmc_ioc_rpmb_req *p_req) +{ + /* + * Some parameters are a must for the operation. Different + * operation expect different paramters. Below code is + * used for checking this. + * + * All operations will need result. + * GET_WRITE_COUNTER: + * must: write counter, nonce + * optional: MAC + * WRITE_DATA: + * must: MAC, data, write counter + * READ_DATA: + * must: nonce, data + * optional: MAC + * PROGRAM_KEY: + * must: MAC + * + * So here, we only check the 'must' paramters + */ + if (!p_req->result) { + pr_err("%s: Type %d has NULL pointer for result\n", + mmc_hostname(card->host), p_req->type); + return -EINVAL; + } + + if (p_req->type == RPMB_GET_WRITE_COUNTER) { + if (!p_req->nonce || !p_req->wc) { + pr_err("%s: Type %d has NULL pointer for nonce/wc\n", + mmc_hostname(card->host), p_req->type); + return -EINVAL; + } + /* + * used to allocate frame + */ + p_req->blk_cnt = 1; + } else if (p_req->type == RPMB_WRITE_DATA || + p_req->type == RPMB_READ_DATA) { + if ((__u32)(p_req->addr + p_req->blk_cnt) > + card->ext_csd.rpmb_size) { + pr_err("%s Type %d: beyond the RPMB partition rang addr %d, blk_cnt %d, rpmb_size %d\n", + mmc_hostname(card->host), + p_req->type, + p_req->addr, + p_req->blk_cnt, + card->ext_csd.rpmb_size); + return -EINVAL; + } + if (p_req->blk_cnt == 0) { + pr_err("%s: Type %d has zero block count\n", + mmc_hostname(card->host), + p_req->blk_cnt); + return -EINVAL; + } else if (p_req->blk_cnt > card->rpmb_max_req) { + pr_err("%s: Type %d has invalid block count, cannot large than %d\n", + mmc_hostname(card->host), + p_req->blk_cnt, + card->rpmb_max_req); + return -EINVAL; + } + if (!p_req->data) { + pr_err("%s: Type %d has NULL pointer for data\n", + mmc_hostname(card->host), p_req->type); + return -EINVAL; + } + if (p_req->type == RPMB_WRITE_DATA) { + if (!p_req->wc || !p_req->mac) { + pr_err("%s: Type %d has NULL pointer for write counter/MAC\n", + mmc_hostname(card->host), + p_req->type); + return -EINVAL; + } + } else { + if (!p_req->nonce) { + pr_err("%s: Type %d has NULL pointer for nonce\n", + mmc_hostname(card->host), + p_req->type); + return -EINVAL; + } + } + } else if (p_req->type == RPMB_PROGRAM_KEY) { + if (!p_req->mac) { + pr_err("%s: Type %d has NULL pointer for MAC\n", + mmc_hostname(card->host), p_req->type); + return -EINVAL; + } + /* + * used to allocate frame + */ + p_req->blk_cnt = 1; + } else + return -EOPNOTSUPP; + + return 0; +} + +/* + * prepare the request of RPMB frame + * RPMB frame is MSB first + * convert needed bytes + * return how many frames will be prepared + */ +int mmc_rpmb_pre_frame(struct mmc_core_rpmb_req *rpmb_req, + struct mmc_card *card) +{ + int i, ret; + struct mmc_ioc_rpmb_req *p_req; + __u8 *buf_frame; + __u16 blk_cnt, addr, type; + __u32 w_counter; + + if (!rpmb_req || !card) + return -EINVAL; + + p_req = rpmb_req->req; + if (!p_req) { + pr_err("%s: mmc_ioc_rpmb_req is NULL. Wrong parameter\n", + mmc_hostname(card->host)); + return -EINVAL; + } + + /* + * make sure these two items are clear + */ + rpmb_req->ready = 0; + rpmb_req->frame = NULL; + + ret = mmc_rpmb_request_check(card, p_req); + if (ret) + return ret; + + buf_frame = kzalloc(512 * p_req->blk_cnt, GFP_KERNEL); + if (!buf_frame) { + pr_err("%s: cannot allocate frame for type %d\n", + mmc_hostname(card->host), p_req->type); + return -ENOMEM; + } + + type = cpu_to_be16p(&p_req->type); + if (p_req->type == RPMB_GET_WRITE_COUNTER || + p_req->type == RPMB_READ_DATA) { + /* + * One package prepared + * This request needs Nonce and type + * If is data read, then also need addr + */ + memcpy(buf_frame + RPMB_TYPE_BEG, &type, 2); + if (p_req->type == RPMB_READ_DATA) { + addr = cpu_to_be16p(&p_req->addr); + memcpy(buf_frame + RPMB_ADDR_BEG, &addr, 2); + } + /* convert Nonce code */ + memcpy(buf_frame + RPMB_NONCE_BEG, p_req->nonce, 16); + } else if (p_req->type == RPMB_WRITE_DATA) { + __u8 *data = p_req->data; + /* + * multiple package prepared + * This request nees blk_cnt, addr, write_counter, + * data and mac + */ + blk_cnt = cpu_to_be16p(&p_req->blk_cnt); + addr = cpu_to_be16p(&p_req->addr); + w_counter = cpu_to_be32p(p_req->wc); + for (i = 0; i < p_req->blk_cnt; i++) { + memcpy(buf_frame + i * 512 + RPMB_TYPE_BEG, + &type, 2); + memcpy(buf_frame + i * 512 + RPMB_BLKS_BEG, + &blk_cnt, 2); + memcpy(buf_frame + i * 512 + RPMB_ADDR_BEG, + &addr, 2); + memcpy(buf_frame + i * 512 + RPMB_WCOUNTER_BEG, + &w_counter, 4); + memcpy(buf_frame + i * 512 + RPMB_DATA_BEG, + data, 256); + data += 256; + } + /* convert MAC code */ + memcpy(buf_frame + 512 * (i - 1) + RPMB_MAC_BEG, + p_req->mac, 32); + } else if (p_req->type == RPMB_PROGRAM_KEY) { + /* + * One package prepared + * This request only need mac + */ + memcpy(buf_frame + RPMB_TYPE_BEG, &type, 2); + /* convert MAC code */ + memcpy(buf_frame + RPMB_MAC_BEG, + p_req->mac, 32); + } else { + pr_err("%s: We shouldn't be here\n", mmc_hostname(card->host)); + kfree(buf_frame); + return -EINVAL; + } + rpmb_req->ready = 1; + rpmb_req->frame = buf_frame; + return 0; +} +EXPORT_SYMBOL_GPL(mmc_rpmb_pre_frame); + +int mmc_rpmb_partition_ops(struct mmc_core_rpmb_req *rpmb_req, + struct mmc_card *card) +{ + int err = 0; + struct mmc_ioc_rpmb_req *p_req; + __u16 type, blks; + __u8 *buf_frame; + + if (!rpmb_req || !card) + return -EINVAL; + + p_req = rpmb_req->req; + buf_frame = rpmb_req->frame; + + if (!p_req || !rpmb_req->ready || !buf_frame) { + pr_err("%s: mmc_ioc_rpmb_req is not prepared\n", + mmc_hostname(card->host)); + return -EINVAL; + } + + type = p_req->type; + blks = p_req->blk_cnt; + + /* + * STEP 1: send request to RPMB partition + */ + if (type == RPMB_WRITE_DATA) + err = mmc_rpmb_send_command(card, buf_frame, blks, + type, RPMB_REQ); + else + err = mmc_rpmb_send_command(card, buf_frame, 1, type, RPMB_REQ); + + if (err) { + pr_err("%s: request write counter failed (%d)\n", + mmc_hostname(card->host), err); + goto out; + } + + memset(buf_frame, 0, 512 * blks); + /* + * STEP 2: check write result + * Only for WRITE_DATA or Program key + */ + if (type == RPMB_WRITE_DATA || + type == RPMB_PROGRAM_KEY) { + buf_frame[RPMB_TYPE_BEG + 1] = RPMB_RESULT_READ; + err = mmc_rpmb_send_command(card, buf_frame, 1, + RPMB_RESULT_READ, RPMB_REQ); + if (err) { + pr_err("%s: request write counter failed (%d)\n", + mmc_hostname(card->host), err); + goto out; + } + } + + /* + * STEP 3: get response from RPMB partition + */ + + if (type == RPMB_READ_DATA) + err = mmc_rpmb_send_command(card, buf_frame, + blks, type, RPMB_RESP); + else + err = mmc_rpmb_send_command(card, buf_frame, + 1, type, RPMB_RESP); + if (err) { + pr_err("%s: response write counter failed (%d)\n", + mmc_hostname(card->host), err); + } +out: + return err; +} +EXPORT_SYMBOL_GPL(mmc_rpmb_partition_ops); diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 828977711291..1f7c7967191b 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2698,6 +2698,8 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) * The "data complete" interrupt is also used to * indicate that a busy state has ended. See comment * above in sdhci_cmd_irq(). + * + * "data timeout" interrupt may also happen */ if (host->cmd && (host->cmd->flags & MMC_RSP_BUSY)) { if (intmask & SDHCI_INT_DATA_END) { @@ -2706,6 +2708,14 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) else host->r1b_busy_end = 1; return; + } else if (intmask & SDHCI_INT_DATA_TIMEOUT) { + pr_err("%s: Got data interrupt 0x%08x for busy cmd %d\n", + mmc_hostname(host->mmc), + (unsigned)intmask, + host->cmd->opcode); + host->cmd->error = -ETIMEDOUT; + tasklet_schedule(&host->finish_tasklet); + return; } } diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index 491e9ecc92c2..fff928604859 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -104,7 +104,6 @@ static void tmio_mmc_start_dma_rx(struct tmio_mmc_host *host) pio: if (!desc) { /* DMA failed, fall back to PIO */ - tmio_mmc_enable_dma(host, false); if (ret >= 0) ret = -EIO; host->chan_rx = NULL; @@ -117,6 +116,7 @@ static void tmio_mmc_start_dma_rx(struct tmio_mmc_host *host) } dev_warn(&host->pdev->dev, "DMA failed: %d, falling back to PIO\n", ret); + tmio_mmc_enable_dma(host, false); } dev_dbg(&host->pdev->dev, "%s(): desc %p, cookie %d, sg[%d]\n", __func__, @@ -185,7 +185,6 @@ static void tmio_mmc_start_dma_tx(struct tmio_mmc_host *host) pio: if (!desc) { /* DMA failed, fall back to PIO */ - tmio_mmc_enable_dma(host, false); if (ret >= 0) ret = -EIO; host->chan_tx = NULL; @@ -198,6 +197,7 @@ static void tmio_mmc_start_dma_tx(struct tmio_mmc_host *host) } dev_warn(&host->pdev->dev, "DMA failed: %d, falling back to PIO\n", ret); + tmio_mmc_enable_dma(host, false); } dev_dbg(&host->pdev->dev, "%s(): desc %p, cookie %d\n", __func__, diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index fb8c4deaac27..dfcd0a565c5b 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2793,9 +2793,7 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) if (!chip->select_chip) chip->select_chip = nand_select_chip; - - /* If called twice, pointers that depend on busw may need to be reset */ - if (!chip->read_byte || chip->read_byte == nand_read_byte) + if (!chip->read_byte) chip->read_byte = busw ? nand_read_byte16 : nand_read_byte; if (!chip->read_word) chip->read_word = nand_read_word; @@ -2803,9 +2801,9 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) chip->block_bad = nand_block_bad; if (!chip->block_markbad) chip->block_markbad = nand_default_block_markbad; - if (!chip->write_buf || chip->write_buf == nand_write_buf) + if (!chip->write_buf) chip->write_buf = busw ? nand_write_buf16 : nand_write_buf; - if (!chip->read_buf || chip->read_buf == nand_read_buf) + if (!chip->read_buf) chip->read_buf = busw ? nand_read_buf16 : nand_read_buf; if (!chip->scan_bbt) chip->scan_bbt = nand_default_bbt; diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index c95bfb183c62..5df49d3cb5c7 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -1069,9 +1069,6 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD)) { dbg_wl("no WL needed: min used EC %d, max free EC %d", e1->ec, e2->ec); - - /* Give the unused PEB back */ - wl_tree_add(e2, &ubi->free); goto out_cancel; } self_check_in_wl_tree(ubi, e1, &ubi->used); diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 8395b0992a89..f97569613526 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1991,7 +1991,6 @@ static int __bond_release_one(struct net_device *bond_dev, struct bonding *bond = netdev_priv(bond_dev); struct slave *slave, *oldcurrent; struct sockaddr addr; - int old_flags = bond_dev->flags; netdev_features_t old_features = bond_dev->features; /* slave is not a slave or master is not master of this slave */ @@ -2124,18 +2123,12 @@ static int __bond_release_one(struct net_device *bond_dev, * already taken care of above when we detached the slave */ if (!USES_PRIMARY(bond->params.mode)) { - /* unset promiscuity level from slave - * NOTE: The NETDEV_CHANGEADDR call above may change the value - * of the IFF_PROMISC flag in the bond_dev, but we need the - * value of that flag before that change, as that was the value - * when this slave was attached, so we cache at the start of the - * function and use it here. Same goes for ALLMULTI below - */ - if (old_flags & IFF_PROMISC) + /* unset promiscuity level from slave */ + if (bond_dev->flags & IFF_PROMISC) dev_set_promiscuity(slave_dev, -1); /* unset allmulti level from slave */ - if (old_flags & IFF_ALLMULTI) + if (bond_dev->flags & IFF_ALLMULTI) dev_set_allmulti(slave_dev, -1); /* flush master's mc_list from slave */ @@ -3777,17 +3770,11 @@ static int bond_neigh_init(struct neighbour *n) * The bonding ndo_neigh_setup is called at init time beofre any * slave exists. So we must declare proxy setup function which will * be used at run time to resolve the actual slave neigh param setup. - * - * It's also called by master devices (such as vlans) to setup their - * underlying devices. In that case - do nothing, we're already set up from - * our init. */ static int bond_neigh_setup(struct net_device *dev, struct neigh_parms *parms) { - /* modify only our neigh_parms */ - if (parms->dev == dev) - parms->neigh_setup = bond_neigh_init; + parms->neigh_setup = bond_neigh_init; return 0; } diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index 1c6bc9678774..eec0af45b859 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -908,7 +908,7 @@ static void bgmac_chip_reset(struct bgmac *bgmac) struct bcma_drv_cc *cc = &bgmac->core->bus->drv_cc; u8 et_swtype = 0; u8 sw_type = BGMAC_CHIPCTL_1_SW_TYPE_EPHY | - BGMAC_CHIPCTL_1_IF_TYPE_MII; + BGMAC_CHIPCTL_1_IF_TYPE_RMII; char buf[2]; if (bcm47xx_nvram_getenv("et_swtype", buf, 1) > 0) { diff --git a/drivers/net/ethernet/broadcom/bgmac.h b/drivers/net/ethernet/broadcom/bgmac.h index 12a35cf9bb81..98d4b5fcc070 100644 --- a/drivers/net/ethernet/broadcom/bgmac.h +++ b/drivers/net/ethernet/broadcom/bgmac.h @@ -333,7 +333,7 @@ #define BGMAC_CHIPCTL_1_IF_TYPE_MASK 0x00000030 #define BGMAC_CHIPCTL_1_IF_TYPE_RMII 0x00000000 -#define BGMAC_CHIPCTL_1_IF_TYPE_MII 0x00000010 +#define BGMAC_CHIPCTL_1_IF_TYPE_MI 0x00000010 #define BGMAC_CHIPCTL_1_IF_TYPE_RGMII 0x00000020 #define BGMAC_CHIPCTL_1_SW_TYPE_MASK 0x000000C0 #define BGMAC_CHIPCTL_1_SW_TYPE_EPHY 0x00000000 diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 0877a052e3ee..a13463e8a2c3 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -3003,19 +3003,6 @@ static bool tg3_phy_power_bug(struct tg3 *tp) return false; } -static bool tg3_phy_led_bug(struct tg3 *tp) -{ - switch (tg3_asic_rev(tp)) { - case ASIC_REV_5719: - if ((tp->phy_flags & TG3_PHYFLG_MII_SERDES) && - !tp->pci_fn) - return true; - return false; - } - - return false; -} - static void tg3_power_down_phy(struct tg3 *tp, bool do_low_power) { u32 val; @@ -3063,9 +3050,8 @@ static void tg3_power_down_phy(struct tg3 *tp, bool do_low_power) } return; } else if (do_low_power) { - if (!tg3_phy_led_bug(tp)) - tg3_writephy(tp, MII_TG3_EXT_CTRL, - MII_TG3_EXT_CTRL_FORCE_LED_OFF); + tg3_writephy(tp, MII_TG3_EXT_CTRL, + MII_TG3_EXT_CTRL_FORCE_LED_OFF); val = MII_TG3_AUXCTL_PCTL_100TX_LPWR | MII_TG3_AUXCTL_PCTL_SPR_ISOLATE | diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 7371626c56a1..6e4342695fa7 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -2561,8 +2561,8 @@ static int be_close(struct net_device *netdev) /* Wait for all pending tx completions to arrive so that * all tx skbs are freed. */ - netif_tx_disable(netdev); be_tx_compl_clean(adapter); + netif_tx_disable(netdev); be_rx_qs_destroy(adapter); diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 254f255204f9..c96678555233 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -136,9 +136,7 @@ #define MVNETA_GMAC_FORCE_LINK_PASS BIT(1) #define MVNETA_GMAC_CONFIG_MII_SPEED BIT(5) #define MVNETA_GMAC_CONFIG_GMII_SPEED BIT(6) -#define MVNETA_GMAC_AN_SPEED_EN BIT(7) #define MVNETA_GMAC_CONFIG_FULL_DUPLEX BIT(12) -#define MVNETA_GMAC_AN_DUPLEX_EN BIT(13) #define MVNETA_MIB_COUNTERS_BASE 0x3080 #define MVNETA_MIB_LATE_COLLISION 0x7c #define MVNETA_DA_FILT_SPEC_MCAST 0x3400 @@ -913,13 +911,6 @@ static void mvneta_defaults_set(struct mvneta_port *pp) /* Assign port SDMA configuration */ mvreg_write(pp, MVNETA_SDMA_CONFIG, val); - /* Disable PHY polling in hardware, since we're using the - * kernel phylib to do this. - */ - val = mvreg_read(pp, MVNETA_UNIT_CONTROL); - val &= ~MVNETA_PHY_POLLING_ENABLE; - mvreg_write(pp, MVNETA_UNIT_CONTROL, val); - mvneta_set_ucast_table(pp, -1); mvneta_set_special_mcast_table(pp, -1); mvneta_set_other_mcast_table(pp, -1); @@ -2297,9 +2288,7 @@ static void mvneta_adjust_link(struct net_device *ndev) val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED | MVNETA_GMAC_CONFIG_GMII_SPEED | - MVNETA_GMAC_CONFIG_FULL_DUPLEX | - MVNETA_GMAC_AN_SPEED_EN | - MVNETA_GMAC_AN_DUPLEX_EN); + MVNETA_GMAC_CONFIG_FULL_DUPLEX); if (phydev->duplex) val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX; diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h index 6797b1075874..7fb7e178c74e 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h @@ -581,19 +581,6 @@ struct pch_gbe_hw_stats { u32 intr_tcpip_err_count; }; -/** - * struct pch_gbe_privdata - PCI Device ID driver data - * @phy_tx_clk_delay: Bool, configure the PHY TX delay in software - * @phy_disable_hibernate: Bool, disable PHY hibernation - * @platform_init: Platform initialization callback, called from - * probe, prior to PHY initialization. - */ -struct pch_gbe_privdata { - bool phy_tx_clk_delay; - bool phy_disable_hibernate; - int (*platform_init)(struct pci_dev *pdev); -}; - /** * struct pch_gbe_adapter - board specific private data structure * @stats_lock: Spinlock structure for status @@ -617,7 +604,6 @@ struct pch_gbe_privdata { * @rx_buffer_len: Receive buffer length * @tx_queue_len: Transmit queue length * @have_msi: PCI MSI mode flag - * @pch_gbe_privdata: PCI Device ID driver_data */ struct pch_gbe_adapter { @@ -645,11 +631,8 @@ struct pch_gbe_adapter { int hwts_tx_en; int hwts_rx_en; struct pci_dev *ptp_pdev; - struct pch_gbe_privdata *pdata; }; -#define pch_gbe_hw_to_adapter(hw) container_of(hw, struct pch_gbe_adapter, hw) - extern const char pch_driver_version[]; /* pch_gbe_main.c */ diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.c index 001821fff2fe..5ae03e815ee9 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_api.c @@ -70,9 +70,7 @@ static s32 pch_gbe_plat_init_hw(struct pch_gbe_hw *hw) ret_val = pch_gbe_phy_get_id(hw); if (ret_val) { - struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); - - netdev_err(adapter->netdev, "pch_gbe_phy_get_id error\n"); + pr_err("pch_gbe_phy_get_id error\n"); return ret_val; } pch_gbe_phy_init_setting(hw); @@ -117,9 +115,7 @@ static void pch_gbe_plat_init_function_pointers(struct pch_gbe_hw *hw) inline s32 pch_gbe_hal_setup_init_funcs(struct pch_gbe_hw *hw) { if (!hw->reg) { - struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); - - netdev_err(adapter->netdev, "ERROR: Registers not mapped\n"); + pr_err("ERROR: Registers not mapped\n"); return -ENOSYS; } pch_gbe_plat_init_function_pointers(hw); @@ -132,13 +128,10 @@ inline s32 pch_gbe_hal_setup_init_funcs(struct pch_gbe_hw *hw) */ inline void pch_gbe_hal_get_bus_info(struct pch_gbe_hw *hw) { - if (!hw->func->get_bus_info) { - struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); - - netdev_err(adapter->netdev, "ERROR: configuration\n"); - return; - } - hw->func->get_bus_info(hw); + if (!hw->func->get_bus_info) + pr_err("ERROR: configuration\n"); + else + hw->func->get_bus_info(hw); } /** @@ -151,9 +144,7 @@ inline void pch_gbe_hal_get_bus_info(struct pch_gbe_hw *hw) inline s32 pch_gbe_hal_init_hw(struct pch_gbe_hw *hw) { if (!hw->func->init_hw) { - struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); - - netdev_err(adapter->netdev, "ERROR: configuration\n"); + pr_err("ERROR: configuration\n"); return -ENOSYS; } return hw->func->init_hw(hw); @@ -199,13 +190,10 @@ inline s32 pch_gbe_hal_write_phy_reg(struct pch_gbe_hw *hw, u32 offset, */ inline void pch_gbe_hal_phy_hw_reset(struct pch_gbe_hw *hw) { - if (!hw->func->reset_phy) { - struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); - - netdev_err(adapter->netdev, "ERROR: configuration\n"); - return; - } - hw->func->reset_phy(hw); + if (!hw->func->reset_phy) + pr_err("ERROR: configuration\n"); + else + hw->func->reset_phy(hw); } /** @@ -214,13 +202,10 @@ inline void pch_gbe_hal_phy_hw_reset(struct pch_gbe_hw *hw) */ inline void pch_gbe_hal_phy_sw_reset(struct pch_gbe_hw *hw) { - if (!hw->func->sw_reset_phy) { - struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); - - netdev_err(adapter->netdev, "ERROR: configuration\n"); - return; - } - hw->func->sw_reset_phy(hw); + if (!hw->func->sw_reset_phy) + pr_err("ERROR: configuration\n"); + else + hw->func->sw_reset_phy(hw); } /** @@ -233,9 +218,7 @@ inline void pch_gbe_hal_phy_sw_reset(struct pch_gbe_hw *hw) inline s32 pch_gbe_hal_read_mac_addr(struct pch_gbe_hw *hw) { if (!hw->func->read_mac_addr) { - struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); - - netdev_err(adapter->netdev, "ERROR: configuration\n"); + pr_err("ERROR: configuration\n"); return -ENOSYS; } return hw->func->read_mac_addr(hw); diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c index 1129db0cdf82..24b787be6062 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_ethtool.c @@ -122,7 +122,7 @@ static int pch_gbe_set_settings(struct net_device *netdev, } ret = mii_ethtool_sset(&adapter->mii, ecmd); if (ret) { - netdev_err(netdev, "Error: mii_ethtool_sset\n"); + pr_err("Error: mii_ethtool_sset\n"); return ret; } hw->mac.link_speed = speed; diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c index 665bfefd595e..0c1c65a9ce5e 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c @@ -23,7 +23,6 @@ #include #include #include -#include #define DRV_VERSION "1.01" const char pch_driver_version[] = DRV_VERSION; @@ -112,8 +111,6 @@ const char pch_driver_version[] = DRV_VERSION; #define PTP_L4_MULTICAST_SA "01:00:5e:00:01:81" #define PTP_L2_MULTICAST_SA "01:1b:19:00:00:00" -#define MINNOW_PHY_RESET_GPIO 13 - static unsigned int copybreak __read_mostly = PCH_GBE_COPYBREAK_DEFAULT; static int pch_gbe_mdio_read(struct net_device *netdev, int addr, int reg); @@ -303,7 +300,6 @@ inline void pch_gbe_mac_load_mac_addr(struct pch_gbe_hw *hw) */ s32 pch_gbe_mac_read_mac_addr(struct pch_gbe_hw *hw) { - struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); u32 adr1a, adr1b; adr1a = ioread32(&hw->reg->mac_adr[0].high); @@ -316,7 +312,7 @@ s32 pch_gbe_mac_read_mac_addr(struct pch_gbe_hw *hw) hw->mac.addr[4] = (u8)(adr1b & 0xFF); hw->mac.addr[5] = (u8)((adr1b >> 8) & 0xFF); - netdev_dbg(adapter->netdev, "hw->mac.addr : %pM\n", hw->mac.addr); + pr_debug("hw->mac.addr : %pM\n", hw->mac.addr); return 0; } @@ -328,7 +324,6 @@ s32 pch_gbe_mac_read_mac_addr(struct pch_gbe_hw *hw) static void pch_gbe_wait_clr_bit(void *reg, u32 bit) { u32 tmp; - /* wait busy */ tmp = 1000; while ((ioread32(reg) & bit) && --tmp) @@ -345,10 +340,9 @@ static void pch_gbe_wait_clr_bit(void *reg, u32 bit) */ static void pch_gbe_mac_mar_set(struct pch_gbe_hw *hw, u8 * addr, u32 index) { - struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); u32 mar_low, mar_high, adrmask; - netdev_dbg(adapter->netdev, "index : 0x%x\n", index); + pr_debug("index : 0x%x\n", index); /* * HW expects these in little endian so we reverse the byte order @@ -474,11 +468,10 @@ static void pch_gbe_mac_mc_addr_list_update(struct pch_gbe_hw *hw, */ s32 pch_gbe_mac_force_mac_fc(struct pch_gbe_hw *hw) { - struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); struct pch_gbe_mac_info *mac = &hw->mac; u32 rx_fctrl; - netdev_dbg(adapter->netdev, "mac->fc = %u\n", mac->fc); + pr_debug("mac->fc = %u\n", mac->fc); rx_fctrl = ioread32(&hw->reg->RX_FCTRL); @@ -500,16 +493,14 @@ s32 pch_gbe_mac_force_mac_fc(struct pch_gbe_hw *hw) mac->tx_fc_enable = true; break; default: - netdev_err(adapter->netdev, - "Flow control param set incorrectly\n"); + pr_err("Flow control param set incorrectly\n"); return -EINVAL; } if (mac->link_duplex == DUPLEX_HALF) rx_fctrl &= ~PCH_GBE_FL_CTRL_EN; iowrite32(rx_fctrl, &hw->reg->RX_FCTRL); - netdev_dbg(adapter->netdev, - "RX_FCTRL reg : 0x%08x mac->tx_fc_enable : %d\n", - ioread32(&hw->reg->RX_FCTRL), mac->tx_fc_enable); + pr_debug("RX_FCTRL reg : 0x%08x mac->tx_fc_enable : %d\n", + ioread32(&hw->reg->RX_FCTRL), mac->tx_fc_enable); return 0; } @@ -520,11 +511,10 @@ s32 pch_gbe_mac_force_mac_fc(struct pch_gbe_hw *hw) */ static void pch_gbe_mac_set_wol_event(struct pch_gbe_hw *hw, u32 wu_evt) { - struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); u32 addr_mask; - netdev_dbg(adapter->netdev, "wu_evt : 0x%08x ADDR_MASK reg : 0x%08x\n", - wu_evt, ioread32(&hw->reg->ADDR_MASK)); + pr_debug("wu_evt : 0x%08x ADDR_MASK reg : 0x%08x\n", + wu_evt, ioread32(&hw->reg->ADDR_MASK)); if (wu_evt) { /* Set Wake-On-Lan address mask */ @@ -556,7 +546,6 @@ static void pch_gbe_mac_set_wol_event(struct pch_gbe_hw *hw, u32 wu_evt) u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, u32 addr, u32 dir, u32 reg, u16 data) { - struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); u32 data_out = 0; unsigned int i; unsigned long flags; @@ -569,7 +558,7 @@ u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, u32 addr, u32 dir, u32 reg, udelay(20); } if (i == 0) { - netdev_err(adapter->netdev, "pch-gbe.miim won't go Ready\n"); + pr_err("pch-gbe.miim won't go Ready\n"); spin_unlock_irqrestore(&hw->miim_lock, flags); return 0; /* No way to indicate timeout error */ } @@ -584,9 +573,9 @@ u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, u32 addr, u32 dir, u32 reg, } spin_unlock_irqrestore(&hw->miim_lock, flags); - netdev_dbg(adapter->netdev, "PHY %s: reg=%d, data=0x%04X\n", - dir == PCH_GBE_MIIM_OPER_READ ? "READ" : "WRITE", reg, - dir == PCH_GBE_MIIM_OPER_READ ? data_out : data); + pr_debug("PHY %s: reg=%d, data=0x%04X\n", + dir == PCH_GBE_MIIM_OPER_READ ? "READ" : "WRITE", reg, + dir == PCH_GBE_MIIM_OPER_READ ? data_out : data); return (u16) data_out; } @@ -596,7 +585,6 @@ u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, u32 addr, u32 dir, u32 reg, */ static void pch_gbe_mac_set_pause_packet(struct pch_gbe_hw *hw) { - struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); unsigned long tmp2, tmp3; /* Set Pause packet */ @@ -618,13 +606,10 @@ static void pch_gbe_mac_set_pause_packet(struct pch_gbe_hw *hw) /* Transmit Pause Packet */ iowrite32(PCH_GBE_PS_PKT_RQ, &hw->reg->PAUSE_REQ); - netdev_dbg(adapter->netdev, - "PAUSE_PKT1-5 reg : 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", - ioread32(&hw->reg->PAUSE_PKT1), - ioread32(&hw->reg->PAUSE_PKT2), - ioread32(&hw->reg->PAUSE_PKT3), - ioread32(&hw->reg->PAUSE_PKT4), - ioread32(&hw->reg->PAUSE_PKT5)); + pr_debug("PAUSE_PKT1-5 reg : 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + ioread32(&hw->reg->PAUSE_PKT1), ioread32(&hw->reg->PAUSE_PKT2), + ioread32(&hw->reg->PAUSE_PKT3), ioread32(&hw->reg->PAUSE_PKT4), + ioread32(&hw->reg->PAUSE_PKT5)); return; } @@ -639,15 +624,15 @@ static void pch_gbe_mac_set_pause_packet(struct pch_gbe_hw *hw) */ static int pch_gbe_alloc_queues(struct pch_gbe_adapter *adapter) { - adapter->tx_ring = devm_kzalloc(&adapter->pdev->dev, - sizeof(*adapter->tx_ring), GFP_KERNEL); + adapter->tx_ring = kzalloc(sizeof(*adapter->tx_ring), GFP_KERNEL); if (!adapter->tx_ring) return -ENOMEM; - adapter->rx_ring = devm_kzalloc(&adapter->pdev->dev, - sizeof(*adapter->rx_ring), GFP_KERNEL); - if (!adapter->rx_ring) + adapter->rx_ring = kzalloc(sizeof(*adapter->rx_ring), GFP_KERNEL); + if (!adapter->rx_ring) { + kfree(adapter->tx_ring); return -ENOMEM; + } return 0; } @@ -684,8 +669,8 @@ static int pch_gbe_init_phy(struct pch_gbe_adapter *adapter) break; } adapter->hw.phy.addr = adapter->mii.phy_id; - netdev_dbg(netdev, "phy_addr = %d\n", adapter->mii.phy_id); - if (addr == PCH_GBE_PHY_REGS_LEN) + pr_debug("phy_addr = %d\n", adapter->mii.phy_id); + if (addr == 32) return -EAGAIN; /* Selected the phy and isolate the rest */ for (addr = 0; addr < PCH_GBE_PHY_REGS_LEN; addr++) { @@ -773,15 +758,13 @@ void pch_gbe_reinit_locked(struct pch_gbe_adapter *adapter) */ void pch_gbe_reset(struct pch_gbe_adapter *adapter) { - struct net_device *netdev = adapter->netdev; - pch_gbe_mac_reset_hw(&adapter->hw); /* reprogram multicast address register after reset */ - pch_gbe_set_multi(netdev); + pch_gbe_set_multi(adapter->netdev); /* Setup the receive address. */ pch_gbe_mac_init_rx_addrs(&adapter->hw, PCH_GBE_MAR_ENTRIES); if (pch_gbe_hal_init_hw(&adapter->hw)) - netdev_err(netdev, "Hardware Error\n"); + pr_err("Hardware Error\n"); } /** @@ -795,7 +778,7 @@ static void pch_gbe_free_irq(struct pch_gbe_adapter *adapter) free_irq(adapter->pdev->irq, netdev); if (adapter->have_msi) { pci_disable_msi(adapter->pdev); - netdev_dbg(netdev, "call pci_disable_msi\n"); + pr_debug("call pci_disable_msi\n"); } } @@ -812,8 +795,7 @@ static void pch_gbe_irq_disable(struct pch_gbe_adapter *adapter) ioread32(&hw->reg->INT_ST); synchronize_irq(adapter->pdev->irq); - netdev_dbg(adapter->netdev, "INT_EN reg : 0x%08x\n", - ioread32(&hw->reg->INT_EN)); + pr_debug("INT_EN reg : 0x%08x\n", ioread32(&hw->reg->INT_EN)); } /** @@ -827,8 +809,7 @@ static void pch_gbe_irq_enable(struct pch_gbe_adapter *adapter) if (likely(atomic_dec_and_test(&adapter->irq_sem))) iowrite32(PCH_GBE_INT_ENABLE_MASK, &hw->reg->INT_EN); ioread32(&hw->reg->INT_ST); - netdev_dbg(adapter->netdev, "INT_EN reg : 0x%08x\n", - ioread32(&hw->reg->INT_EN)); + pr_debug("INT_EN reg : 0x%08x\n", ioread32(&hw->reg->INT_EN)); } @@ -865,9 +846,9 @@ static void pch_gbe_configure_tx(struct pch_gbe_adapter *adapter) struct pch_gbe_hw *hw = &adapter->hw; u32 tdba, tdlen, dctrl; - netdev_dbg(adapter->netdev, "dma addr = 0x%08llx size = 0x%08x\n", - (unsigned long long)adapter->tx_ring->dma, - adapter->tx_ring->size); + pr_debug("dma addr = 0x%08llx size = 0x%08x\n", + (unsigned long long)adapter->tx_ring->dma, + adapter->tx_ring->size); /* Setup the HW Tx Head and Tail descriptor pointers */ tdba = adapter->tx_ring->dma; @@ -913,9 +894,9 @@ static void pch_gbe_configure_rx(struct pch_gbe_adapter *adapter) struct pch_gbe_hw *hw = &adapter->hw; u32 rdba, rdlen, rxdma; - netdev_dbg(adapter->netdev, "dma adr = 0x%08llx size = 0x%08x\n", - (unsigned long long)adapter->rx_ring->dma, - adapter->rx_ring->size); + pr_debug("dma adr = 0x%08llx size = 0x%08x\n", + (unsigned long long)adapter->rx_ring->dma, + adapter->rx_ring->size); pch_gbe_mac_force_mac_fc(hw); @@ -926,10 +907,9 @@ static void pch_gbe_configure_rx(struct pch_gbe_adapter *adapter) rxdma &= ~PCH_GBE_RX_DMA_EN; iowrite32(rxdma, &hw->reg->DMA_CTRL); - netdev_dbg(adapter->netdev, - "MAC_RX_EN reg = 0x%08x DMA_CTRL reg = 0x%08x\n", - ioread32(&hw->reg->MAC_RX_EN), - ioread32(&hw->reg->DMA_CTRL)); + pr_debug("MAC_RX_EN reg = 0x%08x DMA_CTRL reg = 0x%08x\n", + ioread32(&hw->reg->MAC_RX_EN), + ioread32(&hw->reg->DMA_CTRL)); /* Setup the HW Rx Head and Tail Descriptor Pointers and * the Base and Length of the Rx Descriptor Ring */ @@ -997,8 +977,7 @@ static void pch_gbe_clean_tx_ring(struct pch_gbe_adapter *adapter, buffer_info = &tx_ring->buffer_info[i]; pch_gbe_unmap_and_free_tx_resource(adapter, buffer_info); } - netdev_dbg(adapter->netdev, - "call pch_gbe_unmap_and_free_tx_resource() %d count\n", i); + pr_debug("call pch_gbe_unmap_and_free_tx_resource() %d count\n", i); size = (unsigned long)sizeof(struct pch_gbe_buffer) * tx_ring->count; memset(tx_ring->buffer_info, 0, size); @@ -1030,8 +1009,7 @@ pch_gbe_clean_rx_ring(struct pch_gbe_adapter *adapter, buffer_info = &rx_ring->buffer_info[i]; pch_gbe_unmap_and_free_rx_resource(adapter, buffer_info); } - netdev_dbg(adapter->netdev, - "call pch_gbe_unmap_and_free_rx_resource() %d count\n", i); + pr_debug("call pch_gbe_unmap_and_free_rx_resource() %d count\n", i); size = (unsigned long)sizeof(struct pch_gbe_buffer) * rx_ring->count; memset(rx_ring->buffer_info, 0, size); @@ -1109,7 +1087,7 @@ static void pch_gbe_watchdog(unsigned long data) struct net_device *netdev = adapter->netdev; struct pch_gbe_hw *hw = &adapter->hw; - netdev_dbg(netdev, "right now = %ld\n", jiffies); + pr_debug("right now = %ld\n", jiffies); pch_gbe_update_stats(adapter); if ((mii_link_ok(&adapter->mii)) && (!netif_carrier_ok(netdev))) { @@ -1117,7 +1095,7 @@ static void pch_gbe_watchdog(unsigned long data) netdev->tx_queue_len = adapter->tx_queue_len; /* mii library handles link maintenance tasks */ if (mii_ethtool_gset(&adapter->mii, &cmd)) { - netdev_err(netdev, "ethtool get setting Error\n"); + pr_err("ethtool get setting Error\n"); mod_timer(&adapter->watchdog_timer, round_jiffies(jiffies + PCH_GBE_WATCHDOG_PERIOD)); @@ -1235,7 +1213,7 @@ static void pch_gbe_tx_queue(struct pch_gbe_adapter *adapter, buffer_info->length, DMA_TO_DEVICE); if (dma_mapping_error(&adapter->pdev->dev, buffer_info->dma)) { - netdev_err(adapter->netdev, "TX DMA map failed\n"); + pr_err("TX DMA map failed\n"); buffer_info->dma = 0; buffer_info->time_stamp = 0; tx_ring->next_to_use = ring_num; @@ -1355,13 +1333,13 @@ static irqreturn_t pch_gbe_intr(int irq, void *data) /* When request status is no interruption factor */ if (unlikely(!int_st)) return IRQ_NONE; /* Not our interrupt. End processing. */ - netdev_dbg(netdev, "%s occur int_st = 0x%08x\n", __func__, int_st); + pr_debug("%s occur int_st = 0x%08x\n", __func__, int_st); if (int_st & PCH_GBE_INT_RX_FRAME_ERR) adapter->stats.intr_rx_frame_err_count++; if (int_st & PCH_GBE_INT_RX_FIFO_ERR) if (!adapter->rx_stop_flag) { adapter->stats.intr_rx_fifo_err_count++; - netdev_dbg(netdev, "Rx fifo over run\n"); + pr_debug("Rx fifo over run\n"); adapter->rx_stop_flag = true; int_en = ioread32(&hw->reg->INT_EN); iowrite32((int_en & ~PCH_GBE_INT_RX_FIFO_ERR), @@ -1381,7 +1359,7 @@ static irqreturn_t pch_gbe_intr(int irq, void *data) /* When Rx descriptor is empty */ if ((int_st & PCH_GBE_INT_RX_DSC_EMP)) { adapter->stats.intr_rx_dsc_empty_count++; - netdev_dbg(netdev, "Rx descriptor is empty\n"); + pr_debug("Rx descriptor is empty\n"); int_en = ioread32(&hw->reg->INT_EN); iowrite32((int_en & ~PCH_GBE_INT_RX_DSC_EMP), &hw->reg->INT_EN); if (hw->mac.tx_fc_enable) { @@ -1404,8 +1382,8 @@ static irqreturn_t pch_gbe_intr(int irq, void *data) __napi_schedule(&adapter->napi); } } - netdev_dbg(netdev, "return = 0x%08x INT_EN reg = 0x%08x\n", - IRQ_HANDLED, ioread32(&hw->reg->INT_EN)); + pr_debug("return = 0x%08x INT_EN reg = 0x%08x\n", + IRQ_HANDLED, ioread32(&hw->reg->INT_EN)); return IRQ_HANDLED; } @@ -1459,10 +1437,9 @@ pch_gbe_alloc_rx_buffers(struct pch_gbe_adapter *adapter, rx_desc->buffer_addr = (buffer_info->dma); rx_desc->gbec_status = DSC_INIT16; - netdev_dbg(netdev, - "i = %d buffer_info->dma = 0x08%llx buffer_info->length = 0x%x\n", - i, (unsigned long long)buffer_info->dma, - buffer_info->length); + pr_debug("i = %d buffer_info->dma = 0x08%llx buffer_info->length = 0x%x\n", + i, (unsigned long long)buffer_info->dma, + buffer_info->length); if (unlikely(++i == rx_ring->count)) i = 0; @@ -1554,13 +1531,12 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter, bool cleaned = false; int unused, thresh; - netdev_dbg(adapter->netdev, "next_to_clean : %d\n", - tx_ring->next_to_clean); + pr_debug("next_to_clean : %d\n", tx_ring->next_to_clean); i = tx_ring->next_to_clean; tx_desc = PCH_GBE_TX_DESC(*tx_ring, i); - netdev_dbg(adapter->netdev, "gbec_status:0x%04x dma_status:0x%04x\n", - tx_desc->gbec_status, tx_desc->dma_status); + pr_debug("gbec_status:0x%04x dma_status:0x%04x\n", + tx_desc->gbec_status, tx_desc->dma_status); unused = PCH_GBE_DESC_UNUSED(tx_ring); thresh = tx_ring->count - PCH_GBE_TX_WEIGHT; @@ -1568,10 +1544,8 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter, { /* current marked clean, tx queue filling up, do extra clean */ int j, k; if (unused < 8) { /* tx queue nearly full */ - netdev_dbg(adapter->netdev, - "clean_tx: transmit queue warning (%x,%x) unused=%d\n", - tx_ring->next_to_clean, tx_ring->next_to_use, - unused); + pr_debug("clean_tx: transmit queue warning (%x,%x) unused=%d\n", + tx_ring->next_to_clean,tx_ring->next_to_use,unused); } /* current marked clean, scan for more that need cleaning. */ @@ -1583,56 +1557,49 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter, if (++k >= tx_ring->count) k = 0; /*increment, wrap*/ } if (j < PCH_GBE_TX_WEIGHT) { - netdev_dbg(adapter->netdev, - "clean_tx: unused=%d loops=%d found tx_desc[%x,%x:%x].gbec_status=%04x\n", - unused, j, i, k, tx_ring->next_to_use, - tx_desc->gbec_status); + pr_debug("clean_tx: unused=%d loops=%d found tx_desc[%x,%x:%x].gbec_status=%04x\n", + unused,j, i,k, tx_ring->next_to_use, tx_desc->gbec_status); i = k; /*found one to clean, usu gbec_status==2000.*/ } } while ((tx_desc->gbec_status & DSC_INIT16) == 0x0000) { - netdev_dbg(adapter->netdev, "gbec_status:0x%04x\n", - tx_desc->gbec_status); + pr_debug("gbec_status:0x%04x\n", tx_desc->gbec_status); buffer_info = &tx_ring->buffer_info[i]; skb = buffer_info->skb; cleaned = true; if ((tx_desc->gbec_status & PCH_GBE_TXD_GMAC_STAT_ABT)) { adapter->stats.tx_aborted_errors++; - netdev_err(adapter->netdev, "Transfer Abort Error\n"); + pr_err("Transfer Abort Error\n"); } else if ((tx_desc->gbec_status & PCH_GBE_TXD_GMAC_STAT_CRSER) ) { adapter->stats.tx_carrier_errors++; - netdev_err(adapter->netdev, - "Transfer Carrier Sense Error\n"); + pr_err("Transfer Carrier Sense Error\n"); } else if ((tx_desc->gbec_status & PCH_GBE_TXD_GMAC_STAT_EXCOL) ) { adapter->stats.tx_aborted_errors++; - netdev_err(adapter->netdev, - "Transfer Collision Abort Error\n"); + pr_err("Transfer Collision Abort Error\n"); } else if ((tx_desc->gbec_status & (PCH_GBE_TXD_GMAC_STAT_SNGCOL | PCH_GBE_TXD_GMAC_STAT_MLTCOL))) { adapter->stats.collisions++; adapter->stats.tx_packets++; adapter->stats.tx_bytes += skb->len; - netdev_dbg(adapter->netdev, "Transfer Collision\n"); + pr_debug("Transfer Collision\n"); } else if ((tx_desc->gbec_status & PCH_GBE_TXD_GMAC_STAT_CMPLT) ) { adapter->stats.tx_packets++; adapter->stats.tx_bytes += skb->len; } if (buffer_info->mapped) { - netdev_dbg(adapter->netdev, - "unmap buffer_info->dma : %d\n", i); + pr_debug("unmap buffer_info->dma : %d\n", i); dma_unmap_single(&adapter->pdev->dev, buffer_info->dma, buffer_info->length, DMA_TO_DEVICE); buffer_info->mapped = false; } if (buffer_info->skb) { - netdev_dbg(adapter->netdev, - "trim buffer_info->skb : %d\n", i); + pr_debug("trim buffer_info->skb : %d\n", i); skb_trim(buffer_info->skb, 0); } tx_desc->gbec_status = DSC_INIT16; @@ -1646,9 +1613,8 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter, break; } } - netdev_dbg(adapter->netdev, - "called pch_gbe_unmap_and_free_tx_resource() %d count\n", - cleaned_count); + pr_debug("called pch_gbe_unmap_and_free_tx_resource() %d count\n", + cleaned_count); if (cleaned_count > 0) { /*skip this if nothing cleaned*/ /* Recover from running out of Tx resources in xmit_frame */ spin_lock(&tx_ring->tx_lock); @@ -1656,13 +1622,12 @@ pch_gbe_clean_tx(struct pch_gbe_adapter *adapter, { netif_wake_queue(adapter->netdev); adapter->stats.tx_restart_count++; - netdev_dbg(adapter->netdev, "Tx wake queue\n"); + pr_debug("Tx wake queue\n"); } tx_ring->next_to_clean = i; - netdev_dbg(adapter->netdev, "next_to_clean : %d\n", - tx_ring->next_to_clean); + pr_debug("next_to_clean : %d\n", tx_ring->next_to_clean); spin_unlock(&tx_ring->tx_lock); } return cleaned; @@ -1719,22 +1684,22 @@ pch_gbe_clean_rx(struct pch_gbe_adapter *adapter, buffer_info->length, DMA_FROM_DEVICE); buffer_info->mapped = false; - netdev_dbg(netdev, - "RxDecNo = 0x%04x Status[DMA:0x%02x GBE:0x%04x TCP:0x%08x] BufInf = 0x%p\n", - i, dma_status, gbec_status, tcp_ip_status, - buffer_info); + pr_debug("RxDecNo = 0x%04x Status[DMA:0x%02x GBE:0x%04x " + "TCP:0x%08x] BufInf = 0x%p\n", + i, dma_status, gbec_status, tcp_ip_status, + buffer_info); /* Error check */ if (unlikely(gbec_status & PCH_GBE_RXD_GMAC_STAT_NOTOCTAL)) { adapter->stats.rx_frame_errors++; - netdev_err(netdev, "Receive Not Octal Error\n"); + pr_err("Receive Not Octal Error\n"); } else if (unlikely(gbec_status & PCH_GBE_RXD_GMAC_STAT_NBLERR)) { adapter->stats.rx_frame_errors++; - netdev_err(netdev, "Receive Nibble Error\n"); + pr_err("Receive Nibble Error\n"); } else if (unlikely(gbec_status & PCH_GBE_RXD_GMAC_STAT_CRCERR)) { adapter->stats.rx_crc_errors++; - netdev_err(netdev, "Receive CRC Error\n"); + pr_err("Receive CRC Error\n"); } else { /* get receive length */ /* length convert[-3], length includes FCS length */ @@ -1765,9 +1730,8 @@ pch_gbe_clean_rx(struct pch_gbe_adapter *adapter, napi_gro_receive(&adapter->napi, skb); (*work_done)++; - netdev_dbg(netdev, - "Receive skb->ip_summed: %d length: %d\n", - skb->ip_summed, length); + pr_debug("Receive skb->ip_summed: %d length: %d\n", + skb->ip_summed, length); } /* return some buffers to hardware, one at a time is too slow */ if (unlikely(cleaned_count >= PCH_GBE_RX_BUFFER_WRITE)) { @@ -1823,10 +1787,10 @@ int pch_gbe_setup_tx_resources(struct pch_gbe_adapter *adapter, tx_desc = PCH_GBE_TX_DESC(*tx_ring, desNo); tx_desc->gbec_status = DSC_INIT16; } - netdev_dbg(adapter->netdev, - "tx_ring->desc = 0x%p tx_ring->dma = 0x%08llx next_to_clean = 0x%08x next_to_use = 0x%08x\n", - tx_ring->desc, (unsigned long long)tx_ring->dma, - tx_ring->next_to_clean, tx_ring->next_to_use); + pr_debug("tx_ring->desc = 0x%p tx_ring->dma = 0x%08llx\n" + "next_to_clean = 0x%08x next_to_use = 0x%08x\n", + tx_ring->desc, (unsigned long long)tx_ring->dma, + tx_ring->next_to_clean, tx_ring->next_to_use); return 0; } @@ -1865,10 +1829,10 @@ int pch_gbe_setup_rx_resources(struct pch_gbe_adapter *adapter, rx_desc = PCH_GBE_RX_DESC(*rx_ring, desNo); rx_desc->gbec_status = DSC_INIT16; } - netdev_dbg(adapter->netdev, - "rx_ring->desc = 0x%p rx_ring->dma = 0x%08llx next_to_clean = 0x%08x next_to_use = 0x%08x\n", - rx_ring->desc, (unsigned long long)rx_ring->dma, - rx_ring->next_to_clean, rx_ring->next_to_use); + pr_debug("rx_ring->desc = 0x%p rx_ring->dma = 0x%08llx " + "next_to_clean = 0x%08x next_to_use = 0x%08x\n", + rx_ring->desc, (unsigned long long)rx_ring->dma, + rx_ring->next_to_clean, rx_ring->next_to_use); return 0; } @@ -1922,9 +1886,9 @@ static int pch_gbe_request_irq(struct pch_gbe_adapter *adapter) flags = IRQF_SHARED; adapter->have_msi = false; err = pci_enable_msi(adapter->pdev); - netdev_dbg(netdev, "call pci_enable_msi\n"); + pr_debug("call pci_enable_msi\n"); if (err) { - netdev_dbg(netdev, "call pci_enable_msi - Error: %d\n", err); + pr_debug("call pci_enable_msi - Error: %d\n", err); } else { flags = 0; adapter->have_msi = true; @@ -1932,11 +1896,9 @@ static int pch_gbe_request_irq(struct pch_gbe_adapter *adapter) err = request_irq(adapter->pdev->irq, &pch_gbe_intr, flags, netdev->name, netdev); if (err) - netdev_err(netdev, "Unable to allocate interrupt Error: %d\n", - err); - netdev_dbg(netdev, - "adapter->have_msi : %d flags : 0x%04x return : 0x%04x\n", - adapter->have_msi, flags, err); + pr_err("Unable to allocate interrupt Error: %d\n", err); + pr_debug("adapter->have_msi : %d flags : 0x%04x return : 0x%04x\n", + adapter->have_msi, flags, err); return err; } @@ -1957,7 +1919,7 @@ int pch_gbe_up(struct pch_gbe_adapter *adapter) /* Ensure we have a valid MAC */ if (!is_valid_ether_addr(adapter->hw.mac.addr)) { - netdev_err(netdev, "Error: Invalid MAC address\n"); + pr_err("Error: Invalid MAC address\n"); goto out; } @@ -1971,14 +1933,12 @@ int pch_gbe_up(struct pch_gbe_adapter *adapter) err = pch_gbe_request_irq(adapter); if (err) { - netdev_err(netdev, - "Error: can't bring device up - irq request failed\n"); + pr_err("Error: can't bring device up - irq request failed\n"); goto out; } err = pch_gbe_alloc_rx_buffers_pool(adapter, rx_ring, rx_ring->count); if (err) { - netdev_err(netdev, - "Error: can't bring device up - alloc rx buffers pool failed\n"); + pr_err("Error: can't bring device up - alloc rx buffers pool failed\n"); goto freeirq; } pch_gbe_alloc_tx_buffers(adapter, tx_ring); @@ -2055,11 +2015,11 @@ static int pch_gbe_sw_init(struct pch_gbe_adapter *adapter) /* Initialize the hardware-specific values */ if (pch_gbe_hal_setup_init_funcs(hw)) { - netdev_err(netdev, "Hardware Initialization Failure\n"); + pr_err("Hardware Initialization Failure\n"); return -EIO; } if (pch_gbe_alloc_queues(adapter)) { - netdev_err(netdev, "Unable to allocate memory for queues\n"); + pr_err("Unable to allocate memory for queues\n"); return -ENOMEM; } spin_lock_init(&adapter->hw.miim_lock); @@ -2070,10 +2030,9 @@ static int pch_gbe_sw_init(struct pch_gbe_adapter *adapter) pch_gbe_init_stats(adapter); - netdev_dbg(netdev, - "rx_buffer_len : %d mac.min_frame_size : %d mac.max_frame_size : %d\n", - (u32) adapter->rx_buffer_len, - hw->mac.min_frame_size, hw->mac.max_frame_size); + pr_debug("rx_buffer_len : %d mac.min_frame_size : %d mac.max_frame_size : %d\n", + (u32) adapter->rx_buffer_len, + hw->mac.min_frame_size, hw->mac.max_frame_size); return 0; } @@ -2102,7 +2061,7 @@ static int pch_gbe_open(struct net_device *netdev) err = pch_gbe_up(adapter); if (err) goto err_up; - netdev_dbg(netdev, "Success End\n"); + pr_debug("Success End\n"); return 0; err_up: @@ -2113,7 +2072,7 @@ static int pch_gbe_open(struct net_device *netdev) pch_gbe_free_tx_resources(adapter, adapter->tx_ring); err_setup_tx: pch_gbe_reset(adapter); - netdev_err(netdev, "Error End\n"); + pr_err("Error End\n"); return err; } @@ -2157,9 +2116,8 @@ static int pch_gbe_xmit_frame(struct sk_buff *skb, struct net_device *netdev) if (unlikely(!PCH_GBE_DESC_UNUSED(tx_ring))) { netif_stop_queue(netdev); spin_unlock_irqrestore(&tx_ring->tx_lock, flags); - netdev_dbg(netdev, - "Return : BUSY next_to use : 0x%08x next_to clean : 0x%08x\n", - tx_ring->next_to_use, tx_ring->next_to_clean); + pr_debug("Return : BUSY next_to use : 0x%08x next_to clean : 0x%08x\n", + tx_ring->next_to_use, tx_ring->next_to_clean); return NETDEV_TX_BUSY; } @@ -2194,7 +2152,7 @@ static void pch_gbe_set_multi(struct net_device *netdev) int i; int mc_count; - netdev_dbg(netdev, "netdev->flags : 0x%08x\n", netdev->flags); + pr_debug("netdev->flags : 0x%08x\n", netdev->flags); /* Check for Promiscuous and All Multicast modes */ rctl = ioread32(&hw->reg->RX_MODE); @@ -2234,8 +2192,7 @@ static void pch_gbe_set_multi(struct net_device *netdev) PCH_GBE_MAR_ENTRIES); kfree(mta_list); - netdev_dbg(netdev, - "RX_MODE reg(check bit31,30 ADD,MLT) : 0x%08x netdev->mc_count : 0x%08x\n", + pr_debug("RX_MODE reg(check bit31,30 ADD,MLT) : 0x%08x netdev->mc_count : 0x%08x\n", ioread32(&hw->reg->RX_MODE), mc_count); } @@ -2261,12 +2218,12 @@ static int pch_gbe_set_mac(struct net_device *netdev, void *addr) pch_gbe_mac_mar_set(&adapter->hw, adapter->hw.mac.addr, 0); ret_val = 0; } - netdev_dbg(netdev, "ret_val : 0x%08x\n", ret_val); - netdev_dbg(netdev, "dev_addr : %pM\n", netdev->dev_addr); - netdev_dbg(netdev, "mac_addr : %pM\n", adapter->hw.mac.addr); - netdev_dbg(netdev, "MAC_ADR1AB reg : 0x%08x 0x%08x\n", - ioread32(&adapter->hw.reg->mac_adr[0].high), - ioread32(&adapter->hw.reg->mac_adr[0].low)); + pr_debug("ret_val : 0x%08x\n", ret_val); + pr_debug("dev_addr : %pM\n", netdev->dev_addr); + pr_debug("mac_addr : %pM\n", adapter->hw.mac.addr); + pr_debug("MAC_ADR1AB reg : 0x%08x 0x%08x\n", + ioread32(&adapter->hw.reg->mac_adr[0].high), + ioread32(&adapter->hw.reg->mac_adr[0].low)); return ret_val; } @@ -2288,7 +2245,7 @@ static int pch_gbe_change_mtu(struct net_device *netdev, int new_mtu) max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN; if ((max_frame < ETH_ZLEN + ETH_FCS_LEN) || (max_frame > PCH_GBE_MAX_JUMBO_FRAME_SIZE)) { - netdev_err(netdev, "Invalid MTU setting\n"); + pr_err("Invalid MTU setting\n"); return -EINVAL; } if (max_frame <= PCH_GBE_FRAME_SIZE_2048) @@ -2317,10 +2274,9 @@ static int pch_gbe_change_mtu(struct net_device *netdev, int new_mtu) adapter->hw.mac.max_frame_size = max_frame; } - netdev_dbg(netdev, - "max_frame : %d rx_buffer_len : %d mtu : %d max_frame_size : %d\n", - max_frame, (u32) adapter->rx_buffer_len, netdev->mtu, - adapter->hw.mac.max_frame_size); + pr_debug("max_frame : %d rx_buffer_len : %d mtu : %d max_frame_size : %d\n", + max_frame, (u32) adapter->rx_buffer_len, netdev->mtu, + adapter->hw.mac.max_frame_size); return 0; } @@ -2361,7 +2317,7 @@ static int pch_gbe_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) { struct pch_gbe_adapter *adapter = netdev_priv(netdev); - netdev_dbg(netdev, "cmd : 0x%04x\n", cmd); + pr_debug("cmd : 0x%04x\n", cmd); if (cmd == SIOCSHWTSTAMP) return hwtstamp_ioctl(netdev, ifr, cmd); @@ -2398,7 +2354,7 @@ static int pch_gbe_napi_poll(struct napi_struct *napi, int budget) bool poll_end_flag = false; bool cleaned = false; - netdev_dbg(adapter->netdev, "budget : %d\n", budget); + pr_debug("budget : %d\n", budget); pch_gbe_clean_rx(adapter, adapter->rx_ring, &work_done, budget); cleaned = pch_gbe_clean_tx(adapter, adapter->tx_ring); @@ -2421,9 +2377,8 @@ static int pch_gbe_napi_poll(struct napi_struct *napi, int budget) pch_gbe_enable_dma_rx(&adapter->hw); } - netdev_dbg(adapter->netdev, - "poll_end_flag : %d work_done : %d budget : %d\n", - poll_end_flag, work_done, budget); + pr_debug("poll_end_flag : %d work_done : %d budget : %d\n", + poll_end_flag, work_done, budget); return work_done; } @@ -2480,7 +2435,7 @@ static pci_ers_result_t pch_gbe_io_slot_reset(struct pci_dev *pdev) struct pch_gbe_hw *hw = &adapter->hw; if (pci_enable_device(pdev)) { - netdev_err(netdev, "Cannot re-enable PCI device after reset\n"); + pr_err("Cannot re-enable PCI device after reset\n"); return PCI_ERS_RESULT_DISCONNECT; } pci_set_master(pdev); @@ -2500,8 +2455,7 @@ static void pch_gbe_io_resume(struct pci_dev *pdev) if (netif_running(netdev)) { if (pch_gbe_up(adapter)) { - netdev_dbg(netdev, - "can't bring device back up after reset\n"); + pr_debug("can't bring device back up after reset\n"); return; } } @@ -2555,7 +2509,7 @@ static int pch_gbe_resume(struct device *device) err = pci_enable_device(pdev); if (err) { - netdev_err(netdev, "Cannot enable PCI device from suspend\n"); + pr_err("Cannot enable PCI device from suspend\n"); return err; } pci_set_master(pdev); @@ -2591,7 +2545,13 @@ static void pch_gbe_remove(struct pci_dev *pdev) pch_gbe_hal_phy_hw_reset(&adapter->hw); + kfree(adapter->tx_ring); + kfree(adapter->rx_ring); + + iounmap(adapter->hw.reg); + pci_release_regions(pdev); free_netdev(netdev); + pci_disable_device(pdev); } static int pch_gbe_probe(struct pci_dev *pdev, @@ -2601,7 +2561,7 @@ static int pch_gbe_probe(struct pci_dev *pdev, struct pch_gbe_adapter *adapter; int ret; - ret = pcim_enable_device(pdev); + ret = pci_enable_device(pdev); if (ret) return ret; @@ -2614,22 +2574,24 @@ static int pch_gbe_probe(struct pci_dev *pdev, if (ret) { dev_err(&pdev->dev, "ERR: No usable DMA " "configuration, aborting\n"); - return ret; + goto err_disable_device; } } } - ret = pcim_iomap_regions(pdev, 1 << PCH_GBE_PCI_BAR, pci_name(pdev)); + ret = pci_request_regions(pdev, KBUILD_MODNAME); if (ret) { dev_err(&pdev->dev, "ERR: Can't reserve PCI I/O and memory resources\n"); - return ret; + goto err_disable_device; } pci_set_master(pdev); netdev = alloc_etherdev((int)sizeof(struct pch_gbe_adapter)); - if (!netdev) - return -ENOMEM; + if (!netdev) { + ret = -ENOMEM; + goto err_release_pci; + } SET_NETDEV_DEV(netdev, &pdev->dev); pci_set_drvdata(pdev, netdev); @@ -2637,17 +2599,18 @@ static int pch_gbe_probe(struct pci_dev *pdev, adapter->netdev = netdev; adapter->pdev = pdev; adapter->hw.back = adapter; - adapter->hw.reg = pcim_iomap_table(pdev)[PCH_GBE_PCI_BAR]; - adapter->pdata = (struct pch_gbe_privdata *)pci_id->driver_data; - if (adapter->pdata && adapter->pdata->platform_init) - adapter->pdata->platform_init(pdev); + adapter->hw.reg = pci_iomap(pdev, PCH_GBE_PCI_BAR, 0); + if (!adapter->hw.reg) { + ret = -EIO; + dev_err(&pdev->dev, "Can't ioremap\n"); + goto err_free_netdev; + } adapter->ptp_pdev = pci_get_bus_and_slot(adapter->pdev->bus->number, PCI_DEVFN(12, 4)); if (ptp_filter_init(ptp_filter, ARRAY_SIZE(ptp_filter))) { - dev_err(&pdev->dev, "Bad ptp filter\n"); - ret = -EINVAL; - goto err_free_netdev; + pr_err("Bad ptp filter\n"); + return -EINVAL; } netdev->netdev_ops = &pch_gbe_netdev_ops; @@ -2665,7 +2628,7 @@ static int pch_gbe_probe(struct pci_dev *pdev, /* setup the private structure */ ret = pch_gbe_sw_init(adapter); if (ret) - goto err_free_netdev; + goto err_iounmap; /* Initialize PHY */ ret = pch_gbe_init_phy(adapter); @@ -2716,60 +2679,25 @@ static int pch_gbe_probe(struct pci_dev *pdev, dev_dbg(&pdev->dev, "PCH Network Connection\n"); - /* Disable hibernation on certain platforms */ - if (adapter->pdata && adapter->pdata->phy_disable_hibernate) - pch_gbe_phy_disable_hibernate(&adapter->hw); - device_set_wakeup_enable(&pdev->dev, 1); return 0; err_free_adapter: pch_gbe_hal_phy_hw_reset(&adapter->hw); + kfree(adapter->tx_ring); + kfree(adapter->rx_ring); +err_iounmap: + iounmap(adapter->hw.reg); err_free_netdev: free_netdev(netdev); +err_release_pci: + pci_release_regions(pdev); +err_disable_device: + pci_disable_device(pdev); return ret; } -/* The AR803X PHY on the MinnowBoard requires a physical pin to be toggled to - * ensure it is awake for probe and init. Request the line and reset the PHY. - */ -static int pch_gbe_minnow_platform_init(struct pci_dev *pdev) -{ - unsigned long flags = GPIOF_DIR_OUT | GPIOF_INIT_HIGH | GPIOF_EXPORT; - unsigned gpio = MINNOW_PHY_RESET_GPIO; - int ret; - - ret = devm_gpio_request_one(&pdev->dev, gpio, flags, - "minnow_phy_reset"); - if (ret) { - dev_err(&pdev->dev, - "ERR: Can't request PHY reset GPIO line '%d'\n", gpio); - return ret; - } - - gpio_set_value(gpio, 0); - usleep_range(1250, 1500); - gpio_set_value(gpio, 1); - usleep_range(1250, 1500); - - return ret; -} - -static struct pch_gbe_privdata pch_gbe_minnow_privdata = { - .phy_tx_clk_delay = true, - .phy_disable_hibernate = true, - .platform_init = pch_gbe_minnow_platform_init, -}; - static DEFINE_PCI_DEVICE_TABLE(pch_gbe_pcidev_id) = { - {.vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_IOH1_GBE, - .subvendor = PCI_VENDOR_ID_CIRCUITCO, - .subdevice = PCI_SUBSYSTEM_ID_CIRCUITCO_MINNOWBOARD, - .class = (PCI_CLASS_NETWORK_ETHERNET << 8), - .class_mask = (0xFFFF00), - .driver_data = (kernel_ulong_t)&pch_gbe_minnow_privdata - }, {.vendor = PCI_VENDOR_ID_INTEL, .device = PCI_DEVICE_ID_INTEL_IOH1_GBE, .subvendor = PCI_ANY_ID, diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c index cf7c9b3a255b..8653c3b81f84 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_param.c @@ -237,17 +237,16 @@ static int pch_gbe_validate_option(int *value, case enable_option: switch (*value) { case OPTION_ENABLED: - netdev_dbg(adapter->netdev, "%s Enabled\n", opt->name); + pr_debug("%s Enabled\n", opt->name); return 0; case OPTION_DISABLED: - netdev_dbg(adapter->netdev, "%s Disabled\n", opt->name); + pr_debug("%s Disabled\n", opt->name); return 0; } break; case range_option: if (*value >= opt->arg.r.min && *value <= opt->arg.r.max) { - netdev_dbg(adapter->netdev, "%s set to %i\n", - opt->name, *value); + pr_debug("%s set to %i\n", opt->name, *value); return 0; } break; @@ -259,8 +258,7 @@ static int pch_gbe_validate_option(int *value, ent = &opt->arg.l.p[i]; if (*value == ent->i) { if (ent->str[0] != '\0') - netdev_dbg(adapter->netdev, "%s\n", - ent->str); + pr_debug("%s\n", ent->str); return 0; } } @@ -270,8 +268,8 @@ static int pch_gbe_validate_option(int *value, BUG(); } - netdev_dbg(adapter->netdev, "Invalid %s value specified (%i) %s\n", - opt->name, *value, opt->err); + pr_debug("Invalid %s value specified (%i) %s\n", + opt->name, *value, opt->err); *value = opt->def; return -1; } @@ -320,8 +318,7 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter) .p = an_list} } }; if (speed || dplx) { - netdev_dbg(adapter->netdev, - "AutoNeg specified along with Speed or Duplex, AutoNeg parameter ignored\n"); + pr_debug("AutoNeg specified along with Speed or Duplex, AutoNeg parameter ignored\n"); hw->phy.autoneg_advertised = opt.def; } else { int tmp = AutoNeg; @@ -335,16 +332,13 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter) case 0: hw->mac.autoneg = hw->mac.fc_autoneg = 1; if ((speed || dplx)) - netdev_dbg(adapter->netdev, - "Speed and duplex autonegotiation enabled\n"); + pr_debug("Speed and duplex autonegotiation enabled\n"); hw->mac.link_speed = SPEED_10; hw->mac.link_duplex = DUPLEX_HALF; break; case HALF_DUPLEX: - netdev_dbg(adapter->netdev, - "Half Duplex specified without Speed\n"); - netdev_dbg(adapter->netdev, - "Using Autonegotiation at Half Duplex only\n"); + pr_debug("Half Duplex specified without Speed\n"); + pr_debug("Using Autonegotiation at Half Duplex only\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 1; hw->phy.autoneg_advertised = PHY_ADVERTISE_10_HALF | PHY_ADVERTISE_100_HALF; @@ -352,10 +346,8 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter) hw->mac.link_duplex = DUPLEX_HALF; break; case FULL_DUPLEX: - netdev_dbg(adapter->netdev, - "Full Duplex specified without Speed\n"); - netdev_dbg(adapter->netdev, - "Using Autonegotiation at Full Duplex only\n"); + pr_debug("Full Duplex specified without Speed\n"); + pr_debug("Using Autonegotiation at Full Duplex only\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 1; hw->phy.autoneg_advertised = PHY_ADVERTISE_10_FULL | PHY_ADVERTISE_100_FULL | @@ -364,10 +356,8 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter) hw->mac.link_duplex = DUPLEX_FULL; break; case SPEED_10: - netdev_dbg(adapter->netdev, - "10 Mbps Speed specified without Duplex\n"); - netdev_dbg(adapter->netdev, - "Using Autonegotiation at 10 Mbps only\n"); + pr_debug("10 Mbps Speed specified without Duplex\n"); + pr_debug("Using Autonegotiation at 10 Mbps only\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 1; hw->phy.autoneg_advertised = PHY_ADVERTISE_10_HALF | PHY_ADVERTISE_10_FULL; @@ -375,24 +365,22 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter) hw->mac.link_duplex = DUPLEX_HALF; break; case SPEED_10 + HALF_DUPLEX: - netdev_dbg(adapter->netdev, "Forcing to 10 Mbps Half Duplex\n"); + pr_debug("Forcing to 10 Mbps Half Duplex\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 0; hw->phy.autoneg_advertised = 0; hw->mac.link_speed = SPEED_10; hw->mac.link_duplex = DUPLEX_HALF; break; case SPEED_10 + FULL_DUPLEX: - netdev_dbg(adapter->netdev, "Forcing to 10 Mbps Full Duplex\n"); + pr_debug("Forcing to 10 Mbps Full Duplex\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 0; hw->phy.autoneg_advertised = 0; hw->mac.link_speed = SPEED_10; hw->mac.link_duplex = DUPLEX_FULL; break; case SPEED_100: - netdev_dbg(adapter->netdev, - "100 Mbps Speed specified without Duplex\n"); - netdev_dbg(adapter->netdev, - "Using Autonegotiation at 100 Mbps only\n"); + pr_debug("100 Mbps Speed specified without Duplex\n"); + pr_debug("Using Autonegotiation at 100 Mbps only\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 1; hw->phy.autoneg_advertised = PHY_ADVERTISE_100_HALF | PHY_ADVERTISE_100_FULL; @@ -400,33 +388,28 @@ static void pch_gbe_check_copper_options(struct pch_gbe_adapter *adapter) hw->mac.link_duplex = DUPLEX_HALF; break; case SPEED_100 + HALF_DUPLEX: - netdev_dbg(adapter->netdev, - "Forcing to 100 Mbps Half Duplex\n"); + pr_debug("Forcing to 100 Mbps Half Duplex\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 0; hw->phy.autoneg_advertised = 0; hw->mac.link_speed = SPEED_100; hw->mac.link_duplex = DUPLEX_HALF; break; case SPEED_100 + FULL_DUPLEX: - netdev_dbg(adapter->netdev, - "Forcing to 100 Mbps Full Duplex\n"); + pr_debug("Forcing to 100 Mbps Full Duplex\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 0; hw->phy.autoneg_advertised = 0; hw->mac.link_speed = SPEED_100; hw->mac.link_duplex = DUPLEX_FULL; break; case SPEED_1000: - netdev_dbg(adapter->netdev, - "1000 Mbps Speed specified without Duplex\n"); + pr_debug("1000 Mbps Speed specified without Duplex\n"); goto full_duplex_only; case SPEED_1000 + HALF_DUPLEX: - netdev_dbg(adapter->netdev, - "Half Duplex is not supported at 1000 Mbps\n"); + pr_debug("Half Duplex is not supported at 1000 Mbps\n"); /* fall through */ case SPEED_1000 + FULL_DUPLEX: full_duplex_only: - netdev_dbg(adapter->netdev, - "Using Autonegotiation at 1000 Mbps Full Duplex only\n"); + pr_debug("Using Autonegotiation at 1000 Mbps Full Duplex only\n"); hw->mac.autoneg = hw->mac.fc_autoneg = 1; hw->phy.autoneg_advertised = PHY_ADVERTISE_1000_FULL; hw->mac.link_speed = SPEED_1000; diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.c index 23e7ee8fb07e..28bb9603d736 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.c +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.c @@ -74,15 +74,6 @@ #define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ #define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ -/* AR8031 PHY Debug Registers */ -#define PHY_AR803X_ID 0x00001374 -#define PHY_AR8031_DBG_OFF 0x1D -#define PHY_AR8031_DBG_DAT 0x1E -#define PHY_AR8031_SERDES 0x05 -#define PHY_AR8031_HIBERNATE 0x0B -#define PHY_AR8031_SERDES_TX_CLK_DLY 0x0100 /* TX clock delay of 2.0ns */ -#define PHY_AR8031_PS_HIB_EN 0x8000 /* Hibernate enable */ - /* Phy Id Register (word 2) */ #define PHY_REVISION_MASK 0x000F @@ -106,7 +97,6 @@ */ s32 pch_gbe_phy_get_id(struct pch_gbe_hw *hw) { - struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); struct pch_gbe_phy_info *phy = &hw->phy; s32 ret; u16 phy_id1; @@ -125,9 +115,8 @@ s32 pch_gbe_phy_get_id(struct pch_gbe_hw *hw) phy->id = (u32)phy_id1; phy->id = ((phy->id << 6) | ((phy_id2 & 0xFC00) >> 10)); phy->revision = (u32) (phy_id2 & 0x000F); - netdev_dbg(adapter->netdev, - "phy->id : 0x%08x phy->revision : 0x%08x\n", - phy->id, phy->revision); + pr_debug("phy->id : 0x%08x phy->revision : 0x%08x\n", + phy->id, phy->revision); return 0; } @@ -145,10 +134,7 @@ s32 pch_gbe_phy_read_reg_miic(struct pch_gbe_hw *hw, u32 offset, u16 *data) struct pch_gbe_phy_info *phy = &hw->phy; if (offset > PHY_MAX_REG_ADDRESS) { - struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); - - netdev_err(adapter->netdev, "PHY Address %d is out of range\n", - offset); + pr_err("PHY Address %d is out of range\n", offset); return -EINVAL; } *data = pch_gbe_mac_ctrl_miim(hw, phy->addr, PCH_GBE_HAL_MIIM_READ, @@ -170,10 +156,7 @@ s32 pch_gbe_phy_write_reg_miic(struct pch_gbe_hw *hw, u32 offset, u16 data) struct pch_gbe_phy_info *phy = &hw->phy; if (offset > PHY_MAX_REG_ADDRESS) { - struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); - - netdev_err(adapter->netdev, "PHY Address %d is out of range\n", - offset); + pr_err("PHY Address %d is out of range\n", offset); return -EINVAL; } pch_gbe_mac_ctrl_miim(hw, phy->addr, PCH_GBE_HAL_MIIM_WRITE, @@ -257,65 +240,21 @@ inline void pch_gbe_phy_set_rgmii(struct pch_gbe_hw *hw) pch_gbe_phy_sw_reset(hw); } -/** - * pch_gbe_phy_tx_clk_delay - Setup TX clock delay via the PHY - * @hw: Pointer to the HW structure - * Returns - * 0: Successful. - * -EINVAL: Invalid argument. - */ -static int pch_gbe_phy_tx_clk_delay(struct pch_gbe_hw *hw) -{ - /* The RGMII interface requires a ~2ns TX clock delay. This is typically - * done in layout with a longer trace or via PHY strapping, but can also - * be done via PHY configuration registers. - */ - struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); - u16 mii_reg; - int ret = 0; - - switch (hw->phy.id) { - case PHY_AR803X_ID: - netdev_dbg(adapter->netdev, - "Configuring AR803X PHY for 2ns TX clock delay\n"); - pch_gbe_phy_read_reg_miic(hw, PHY_AR8031_DBG_OFF, &mii_reg); - ret = pch_gbe_phy_write_reg_miic(hw, PHY_AR8031_DBG_OFF, - PHY_AR8031_SERDES); - if (ret) - break; - - pch_gbe_phy_read_reg_miic(hw, PHY_AR8031_DBG_DAT, &mii_reg); - mii_reg |= PHY_AR8031_SERDES_TX_CLK_DLY; - ret = pch_gbe_phy_write_reg_miic(hw, PHY_AR8031_DBG_DAT, - mii_reg); - break; - default: - netdev_err(adapter->netdev, - "Unknown PHY (%x), could not set TX clock delay\n", - hw->phy.id); - return -EINVAL; - } - - if (ret) - netdev_err(adapter->netdev, - "Could not configure tx clock delay for PHY\n"); - return ret; -} - /** * pch_gbe_phy_init_setting - PHY initial setting * @hw: Pointer to the HW structure */ void pch_gbe_phy_init_setting(struct pch_gbe_hw *hw) { - struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); + struct pch_gbe_adapter *adapter; struct ethtool_cmd cmd = { .cmd = ETHTOOL_GSET }; int ret; u16 mii_reg; + adapter = container_of(hw, struct pch_gbe_adapter, hw); ret = mii_ethtool_gset(&adapter->mii, &cmd); if (ret) - netdev_err(adapter->netdev, "Error: mii_ethtool_gset\n"); + pr_err("Error: mii_ethtool_gset\n"); ethtool_cmd_speed_set(&cmd, hw->mac.link_speed); cmd.duplex = hw->mac.link_duplex; @@ -324,7 +263,7 @@ void pch_gbe_phy_init_setting(struct pch_gbe_hw *hw) pch_gbe_phy_write_reg_miic(hw, MII_BMCR, BMCR_RESET); ret = mii_ethtool_sset(&adapter->mii, &cmd); if (ret) - netdev_err(adapter->netdev, "Error: mii_ethtool_sset\n"); + pr_err("Error: mii_ethtool_sset\n"); pch_gbe_phy_sw_reset(hw); @@ -332,47 +271,4 @@ void pch_gbe_phy_init_setting(struct pch_gbe_hw *hw) mii_reg |= PHYSP_CTRL_ASSERT_CRS_TX; pch_gbe_phy_write_reg_miic(hw, PHY_PHYSP_CONTROL, mii_reg); - /* Setup a TX clock delay on certain platforms */ - if (adapter->pdata && adapter->pdata->phy_tx_clk_delay) - pch_gbe_phy_tx_clk_delay(hw); -} - -/** - * pch_gbe_phy_disable_hibernate - Disable the PHY low power state - * @hw: Pointer to the HW structure - * Returns - * 0: Successful. - * -EINVAL: Invalid argument. - */ -int pch_gbe_phy_disable_hibernate(struct pch_gbe_hw *hw) -{ - struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw); - u16 mii_reg; - int ret = 0; - - switch (hw->phy.id) { - case PHY_AR803X_ID: - netdev_dbg(adapter->netdev, - "Disabling hibernation for AR803X PHY\n"); - ret = pch_gbe_phy_write_reg_miic(hw, PHY_AR8031_DBG_OFF, - PHY_AR8031_HIBERNATE); - if (ret) - break; - - pch_gbe_phy_read_reg_miic(hw, PHY_AR8031_DBG_DAT, &mii_reg); - mii_reg &= ~PHY_AR8031_PS_HIB_EN; - ret = pch_gbe_phy_write_reg_miic(hw, PHY_AR8031_DBG_DAT, - mii_reg); - break; - default: - netdev_err(adapter->netdev, - "Unknown PHY (%x), could not disable hibernation\n", - hw->phy.id); - return -EINVAL; - } - - if (ret) - netdev_err(adapter->netdev, - "Could not disable PHY hibernation\n"); - return ret; } diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.h b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.h index 0cbe69206e04..03264dc7b5ec 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.h +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_phy.h @@ -33,6 +33,5 @@ void pch_gbe_phy_power_up(struct pch_gbe_hw *hw); void pch_gbe_phy_power_down(struct pch_gbe_hw *hw); void pch_gbe_phy_set_rgmii(struct pch_gbe_hw *hw); void pch_gbe_phy_init_setting(struct pch_gbe_hw *hw); -int pch_gbe_phy_disable_hibernate(struct pch_gbe_hw *hw); #endif /* _PCH_GBE_PHY_H_ */ diff --git a/drivers/net/ethernet/realtek/8139cp.c b/drivers/net/ethernet/realtek/8139cp.c index 9095ff930f29..887aebea298b 100644 --- a/drivers/net/ethernet/realtek/8139cp.c +++ b/drivers/net/ethernet/realtek/8139cp.c @@ -524,7 +524,6 @@ static int cp_rx_poll(struct napi_struct *napi, int budget) PCI_DMA_FROMDEVICE); if (dma_mapping_error(&cp->pdev->dev, new_mapping)) { dev->stats.rx_dropped++; - kfree_skb(new_skb); goto rx_next; } diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 7199d2a8dcf8..393f961a013c 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -4218,7 +4218,6 @@ static void rtl_init_rxcfg(struct rtl8169_private *tp) case RTL_GIGA_MAC_VER_23: case RTL_GIGA_MAC_VER_24: case RTL_GIGA_MAC_VER_34: - case RTL_GIGA_MAC_VER_35: RTL_W32(RxConfig, RX128_INT_EN | RX_MULTI_EN | RX_DMA_BURST); break; case RTL_GIGA_MAC_VER_40: diff --git a/drivers/net/ethernet/sfc/filter.c b/drivers/net/ethernet/sfc/filter.c index a52046581a6c..2738b5f1c81f 100644 --- a/drivers/net/ethernet/sfc/filter.c +++ b/drivers/net/ethernet/sfc/filter.c @@ -675,7 +675,7 @@ s32 efx_filter_insert_filter(struct efx_nic *efx, struct efx_filter_spec *spec, BUILD_BUG_ON(EFX_FILTER_INDEX_UC_DEF != 0); BUILD_BUG_ON(EFX_FILTER_INDEX_MC_DEF != EFX_FILTER_MC_DEF - EFX_FILTER_UC_DEF); - rep_index = spec->type - EFX_FILTER_UC_DEF; + rep_index = spec->type - EFX_FILTER_INDEX_UC_DEF; ins_index = rep_index; spin_lock_bh(&state->lock); diff --git a/drivers/net/ethernet/via/via-rhine.c b/drivers/net/ethernet/via/via-rhine.c index 75b82b6e8c54..ca98acabf1b4 100644 --- a/drivers/net/ethernet/via/via-rhine.c +++ b/drivers/net/ethernet/via/via-rhine.c @@ -32,7 +32,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define DRV_NAME "via-rhine" -#define DRV_VERSION "1.5.1" +#define DRV_VERSION "1.5.0" #define DRV_RELDATE "2010-10-09" #include @@ -1694,12 +1694,7 @@ static netdev_tx_t rhine_start_tx(struct sk_buff *skb, cpu_to_le32(TXDESC | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN)); if (unlikely(vlan_tx_tag_present(skb))) { - u16 vid_pcp = vlan_tx_tag_get(skb); - - /* drop CFI/DEI bit, register needs VID and PCP */ - vid_pcp = (vid_pcp & VLAN_VID_MASK) | - ((vid_pcp & VLAN_PRIO_MASK) >> 1); - rp->tx_ring[entry].tx_status = cpu_to_le32((vid_pcp) << 16); + rp->tx_ring[entry].tx_status = cpu_to_le32((vlan_tx_tag_get(skb)) << 16); /* request tagging */ rp->tx_ring[entry].desc_length |= cpu_to_le32(0x020000); } diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c index 5444f2b87d01..57c2e5ef2804 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_main.c +++ b/drivers/net/ethernet/xilinx/ll_temac_main.c @@ -297,12 +297,6 @@ static int temac_dma_bd_init(struct net_device *ndev) lp->rx_bd_p + (sizeof(*lp->rx_bd_v) * (RX_BD_NUM - 1))); lp->dma_out(lp, TX_CURDESC_PTR, lp->tx_bd_p); - /* Init descriptor indexes */ - lp->tx_bd_ci = 0; - lp->tx_bd_next = 0; - lp->tx_bd_tail = 0; - lp->rx_bd_ci = 0; - return 0; out: diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 06eba6e480c9..6e91931a1c2c 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -727,10 +727,6 @@ static int macvlan_validate(struct nlattr *tb[], struct nlattr *data[]) return -EADDRNOTAVAIL; } - if (data && data[IFLA_MACVLAN_FLAGS] && - nla_get_u16(data[IFLA_MACVLAN_FLAGS]) & ~MACVLAN_FLAG_NOPROMISC) - return -EINVAL; - if (data && data[IFLA_MACVLAN_MODE]) { switch (nla_get_u32(data[IFLA_MACVLAN_MODE])) { case MACVLAN_MODE_PRIVATE: diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c index 7f10588fe668..162464fe86bf 100644 --- a/drivers/net/ppp/pptp.c +++ b/drivers/net/ppp/pptp.c @@ -281,7 +281,7 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb) nf_reset(skb); skb->ip_summed = CHECKSUM_NONE; - ip_select_ident(skb, &rt->dst, NULL); + ip_select_ident(iph, &rt->dst, NULL); ip_send_check(iph); ip_local_out(skb); diff --git a/drivers/net/tun.c b/drivers/net/tun.c index b18ead556994..2491eb28a8d2 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1076,9 +1076,8 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, u32 rxhash; if (!(tun->flags & TUN_NO_PI)) { - if (len < sizeof(pi)) + if ((len -= sizeof(pi)) > total_len) return -EINVAL; - len -= sizeof(pi); if (memcpy_fromiovecend((void *)&pi, iv, 0, sizeof(pi))) return -EFAULT; @@ -1086,9 +1085,8 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, } if (tun->flags & TUN_VNET_HDR) { - if (len < tun->vnet_hdr_sz) + if ((len -= tun->vnet_hdr_sz) > total_len) return -EINVAL; - len -= tun->vnet_hdr_sz; if (memcpy_fromiovecend((void *)&gso, iv, offset, sizeof(gso))) return -EFAULT; @@ -1693,11 +1691,11 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) INIT_LIST_HEAD(&tun->disabled); err = tun_attach(tun, file); if (err < 0) - goto err_free_flow; + goto err_free_dev; err = register_netdevice(tun->dev); if (err < 0) - goto err_detach; + goto err_free_dev; if (device_create_file(&tun->dev->dev, &dev_attr_tun_flags) || device_create_file(&tun->dev->dev, &dev_attr_owner) || @@ -1741,12 +1739,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) strcpy(ifr->ifr_name, tun->dev->name); return 0; -err_detach: - tun_detach_all(dev); -err_free_flow: - tun_flow_uninit(tun); - security_tun_dev_free_security(tun->security); -err_free_dev: + err_free_dev: free_netdev(dev); return err; } diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index b1897c7afdd8..04ee044dde51 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -708,11 +708,6 @@ static const struct usb_device_id products [] = { .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, .bInterfaceProtocol = USB_CDC_PROTO_NONE, .driver_info = (unsigned long)&wwan_info, -}, { - /* Telit modules */ - USB_VENDOR_AND_INTERFACE_INFO(0x1bc7, USB_CLASS_COMM, - USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), - .driver_info = (kernel_ulong_t) &wwan_info, }, { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index 25ba7eca9a13..872819851aef 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c @@ -400,10 +400,6 @@ static const struct usb_device_id mbim_devs[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x68a2, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), .driver_info = (unsigned long)&cdc_mbim_info_zlp, }, - /* HP hs2434 Mobile Broadband Module needs ZLPs */ - { USB_DEVICE_AND_INTERFACE_INFO(0x3f0, 0x4b1d, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), - .driver_info = (unsigned long)&cdc_mbim_info_zlp, - }, { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), .driver_info = (unsigned long)&cdc_mbim_info, }, diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c index c6867f926cff..2dbb9460349d 100644 --- a/drivers/net/usb/dm9601.c +++ b/drivers/net/usb/dm9601.c @@ -303,7 +303,7 @@ static void dm9601_set_multicast(struct net_device *net) rx_ctl |= 0x02; } else if (net->flags & IFF_ALLMULTI || netdev_mc_count(net) > DM_MAX_MCAST) { - rx_ctl |= 0x08; + rx_ctl |= 0x04; } else if (!netdev_mc_empty(net)) { struct netdev_hw_addr *ha; diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 34a081f1527f..56459215a22b 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -518,135 +518,6 @@ static const struct usb_device_id products[] = { /* 3. Combined interface devices matching on interface number */ {QMI_FIXED_INTF(0x0408, 0xea42, 4)}, /* Yota / Megafon M100-1 */ - {QMI_FIXED_INTF(0x05c6, 0x7000, 0)}, - {QMI_FIXED_INTF(0x05c6, 0x7001, 1)}, - {QMI_FIXED_INTF(0x05c6, 0x7002, 1)}, - {QMI_FIXED_INTF(0x05c6, 0x7101, 1)}, - {QMI_FIXED_INTF(0x05c6, 0x7101, 2)}, - {QMI_FIXED_INTF(0x05c6, 0x7101, 3)}, - {QMI_FIXED_INTF(0x05c6, 0x7102, 1)}, - {QMI_FIXED_INTF(0x05c6, 0x7102, 2)}, - {QMI_FIXED_INTF(0x05c6, 0x7102, 3)}, - {QMI_FIXED_INTF(0x05c6, 0x8000, 7)}, - {QMI_FIXED_INTF(0x05c6, 0x8001, 6)}, - {QMI_FIXED_INTF(0x05c6, 0x9000, 4)}, - {QMI_FIXED_INTF(0x05c6, 0x9003, 4)}, - {QMI_FIXED_INTF(0x05c6, 0x9005, 2)}, - {QMI_FIXED_INTF(0x05c6, 0x900a, 4)}, - {QMI_FIXED_INTF(0x05c6, 0x900b, 2)}, - {QMI_FIXED_INTF(0x05c6, 0x900c, 4)}, - {QMI_FIXED_INTF(0x05c6, 0x900c, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x900c, 6)}, - {QMI_FIXED_INTF(0x05c6, 0x900d, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x900f, 3)}, - {QMI_FIXED_INTF(0x05c6, 0x900f, 4)}, - {QMI_FIXED_INTF(0x05c6, 0x900f, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x9010, 4)}, - {QMI_FIXED_INTF(0x05c6, 0x9010, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x9011, 3)}, - {QMI_FIXED_INTF(0x05c6, 0x9011, 4)}, - {QMI_FIXED_INTF(0x05c6, 0x9021, 1)}, - {QMI_FIXED_INTF(0x05c6, 0x9022, 2)}, - {QMI_FIXED_INTF(0x05c6, 0x9025, 4)}, /* Alcatel-sbell ASB TL131 TDD LTE (China Mobile) */ - {QMI_FIXED_INTF(0x05c6, 0x9026, 3)}, - {QMI_FIXED_INTF(0x05c6, 0x902e, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x9031, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x9032, 4)}, - {QMI_FIXED_INTF(0x05c6, 0x9033, 3)}, - {QMI_FIXED_INTF(0x05c6, 0x9033, 4)}, - {QMI_FIXED_INTF(0x05c6, 0x9033, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x9033, 6)}, - {QMI_FIXED_INTF(0x05c6, 0x9034, 3)}, - {QMI_FIXED_INTF(0x05c6, 0x9034, 4)}, - {QMI_FIXED_INTF(0x05c6, 0x9034, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x9034, 6)}, - {QMI_FIXED_INTF(0x05c6, 0x9034, 7)}, - {QMI_FIXED_INTF(0x05c6, 0x9035, 4)}, - {QMI_FIXED_INTF(0x05c6, 0x9036, 3)}, - {QMI_FIXED_INTF(0x05c6, 0x9037, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x9038, 4)}, - {QMI_FIXED_INTF(0x05c6, 0x903b, 7)}, - {QMI_FIXED_INTF(0x05c6, 0x903c, 6)}, - {QMI_FIXED_INTF(0x05c6, 0x903d, 6)}, - {QMI_FIXED_INTF(0x05c6, 0x903e, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x9043, 3)}, - {QMI_FIXED_INTF(0x05c6, 0x9046, 3)}, - {QMI_FIXED_INTF(0x05c6, 0x9046, 4)}, - {QMI_FIXED_INTF(0x05c6, 0x9046, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x9047, 2)}, - {QMI_FIXED_INTF(0x05c6, 0x9047, 3)}, - {QMI_FIXED_INTF(0x05c6, 0x9047, 4)}, - {QMI_FIXED_INTF(0x05c6, 0x9048, 4)}, - {QMI_FIXED_INTF(0x05c6, 0x9048, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x9048, 6)}, - {QMI_FIXED_INTF(0x05c6, 0x9048, 7)}, - {QMI_FIXED_INTF(0x05c6, 0x9048, 8)}, - {QMI_FIXED_INTF(0x05c6, 0x904c, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x904c, 6)}, - {QMI_FIXED_INTF(0x05c6, 0x904c, 7)}, - {QMI_FIXED_INTF(0x05c6, 0x904c, 8)}, - {QMI_FIXED_INTF(0x05c6, 0x9050, 3)}, - {QMI_FIXED_INTF(0x05c6, 0x9052, 4)}, - {QMI_FIXED_INTF(0x05c6, 0x9053, 6)}, - {QMI_FIXED_INTF(0x05c6, 0x9053, 7)}, - {QMI_FIXED_INTF(0x05c6, 0x9054, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x9054, 6)}, - {QMI_FIXED_INTF(0x05c6, 0x9055, 3)}, - {QMI_FIXED_INTF(0x05c6, 0x9055, 4)}, - {QMI_FIXED_INTF(0x05c6, 0x9055, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x9055, 6)}, - {QMI_FIXED_INTF(0x05c6, 0x9055, 7)}, - {QMI_FIXED_INTF(0x05c6, 0x9056, 3)}, - {QMI_FIXED_INTF(0x05c6, 0x9062, 2)}, - {QMI_FIXED_INTF(0x05c6, 0x9062, 3)}, - {QMI_FIXED_INTF(0x05c6, 0x9062, 4)}, - {QMI_FIXED_INTF(0x05c6, 0x9062, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x9062, 6)}, - {QMI_FIXED_INTF(0x05c6, 0x9062, 7)}, - {QMI_FIXED_INTF(0x05c6, 0x9062, 8)}, - {QMI_FIXED_INTF(0x05c6, 0x9062, 9)}, - {QMI_FIXED_INTF(0x05c6, 0x9064, 3)}, - {QMI_FIXED_INTF(0x05c6, 0x9065, 6)}, - {QMI_FIXED_INTF(0x05c6, 0x9065, 7)}, - {QMI_FIXED_INTF(0x05c6, 0x9066, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x9066, 6)}, - {QMI_FIXED_INTF(0x05c6, 0x9067, 1)}, - {QMI_FIXED_INTF(0x05c6, 0x9068, 2)}, - {QMI_FIXED_INTF(0x05c6, 0x9068, 3)}, - {QMI_FIXED_INTF(0x05c6, 0x9068, 4)}, - {QMI_FIXED_INTF(0x05c6, 0x9068, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x9068, 6)}, - {QMI_FIXED_INTF(0x05c6, 0x9068, 7)}, - {QMI_FIXED_INTF(0x05c6, 0x9069, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x9069, 6)}, - {QMI_FIXED_INTF(0x05c6, 0x9069, 7)}, - {QMI_FIXED_INTF(0x05c6, 0x9069, 8)}, - {QMI_FIXED_INTF(0x05c6, 0x9070, 4)}, - {QMI_FIXED_INTF(0x05c6, 0x9070, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x9075, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x9076, 4)}, - {QMI_FIXED_INTF(0x05c6, 0x9076, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x9076, 6)}, - {QMI_FIXED_INTF(0x05c6, 0x9076, 7)}, - {QMI_FIXED_INTF(0x05c6, 0x9076, 8)}, - {QMI_FIXED_INTF(0x05c6, 0x9077, 3)}, - {QMI_FIXED_INTF(0x05c6, 0x9077, 4)}, - {QMI_FIXED_INTF(0x05c6, 0x9077, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x9077, 6)}, - {QMI_FIXED_INTF(0x05c6, 0x9078, 3)}, - {QMI_FIXED_INTF(0x05c6, 0x9079, 4)}, - {QMI_FIXED_INTF(0x05c6, 0x9079, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x9079, 6)}, - {QMI_FIXED_INTF(0x05c6, 0x9079, 7)}, - {QMI_FIXED_INTF(0x05c6, 0x9079, 8)}, - {QMI_FIXED_INTF(0x05c6, 0x9080, 5)}, - {QMI_FIXED_INTF(0x05c6, 0x9080, 6)}, - {QMI_FIXED_INTF(0x05c6, 0x9080, 7)}, - {QMI_FIXED_INTF(0x05c6, 0x9080, 8)}, - {QMI_FIXED_INTF(0x05c6, 0x9083, 3)}, - {QMI_FIXED_INTF(0x05c6, 0x9084, 4)}, - {QMI_FIXED_INTF(0x05c6, 0x920d, 0)}, - {QMI_FIXED_INTF(0x05c6, 0x920d, 5)}, {QMI_FIXED_INTF(0x12d1, 0x140c, 1)}, /* Huawei E173 */ {QMI_FIXED_INTF(0x12d1, 0x14ac, 1)}, /* Huawei E1820 */ {QMI_FIXED_INTF(0x19d2, 0x0002, 1)}, @@ -739,6 +610,7 @@ static const struct usb_device_id products[] = { {QMI_GOBI_DEVICE(0x413c, 0x8186)}, /* Dell Gobi 2000 Modem device (N0218, VU936) */ {QMI_GOBI_DEVICE(0x413c, 0x8194)}, /* Dell Gobi 3000 Composite */ {QMI_GOBI_DEVICE(0x05c6, 0x920b)}, /* Generic Gobi 2000 Modem device */ + {QMI_GOBI_DEVICE(0x05c6, 0x920d)}, /* Gobi 3000 Composite */ {QMI_GOBI_DEVICE(0x05c6, 0x9225)}, /* Sony Gobi 2000 Modem device (N0279, VU730) */ {QMI_GOBI_DEVICE(0x05c6, 0x9245)}, /* Samsung Gobi 2000 Modem device (VL176) */ {QMI_GOBI_DEVICE(0x03f0, 0x251d)}, /* HP Gobi 2000 Modem device (VP412) */ diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 054489fdf54a..57325f356d4f 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -1090,7 +1090,7 @@ static netdev_tx_t vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, iph->daddr = dst; iph->saddr = fl4.saddr; iph->ttl = ttl ? : ip4_dst_hoplimit(&rt->dst); - __ip_select_ident(iph, &rt->dst, (skb_shinfo(skb)->gso_segs ?: 1) - 1); + tunnel_ip_select_ident(skb, old_iph, &rt->dst); nf_reset(skb); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 3457ca5382f4..e1714d7c9eeb 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -1076,10 +1076,6 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, * is_on == 0 means MRC CCK is OFF (more noise imm) */ bool is_on = param ? 1 : 0; - - if (ah->caps.rx_chainmask == 1) - break; - REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL, AR_PHY_MRC_CCK_ENABLE, is_on); REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL, diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 4ebd9fd8a0a4..42b03dc39d14 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -79,6 +79,10 @@ struct ath_config { sizeof(struct ath_buf_state)); \ } while (0) +#define ATH_RXBUF_RESET(_bf) do { \ + (_bf)->bf_stale = false; \ + } while (0) + /** * enum buffer_type - Buffer type flags * @@ -312,7 +316,6 @@ struct ath_rx { struct ath_descdma rxdma; struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX]; - struct ath_buf *buf_hold; struct sk_buff *frag; u32 ampdu_ref; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index 417a089cd7e1..6bd0e92ea2aa 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -448,7 +448,6 @@ static void ath9k_htc_tx_process(struct ath9k_htc_priv *priv, struct ieee80211_conf *cur_conf = &priv->hw->conf; bool txok; int slot; - int hdrlen, padsize; slot = strip_drv_header(priv, skb); if (slot < 0) { @@ -505,15 +504,6 @@ static void ath9k_htc_tx_process(struct ath9k_htc_priv *priv, ath9k_htc_tx_clear_slot(priv, slot); - /* Remove padding before handing frame back to mac80211 */ - hdrlen = ieee80211_get_hdrlen_from_skb(skb); - - padsize = hdrlen & 3; - if (padsize && skb->len > hdrlen + padsize) { - memmove(skb->data + padsize, skb->data, hdrlen); - skb_pull(skb, padsize); - } - /* Send status to mac80211 */ ieee80211_tx_status(priv->hw, skb); } diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index bd126c25a727..2ba494567777 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -767,8 +767,7 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) IEEE80211_HW_PS_NULLFUNC_STACK | IEEE80211_HW_SPECTRUM_MGMT | IEEE80211_HW_REPORTS_TX_ACK_STATUS | - IEEE80211_HW_SUPPORTS_RC_TABLE | - IEEE80211_HW_SUPPORTS_HT_CCK_RATES; + IEEE80211_HW_SUPPORTS_RC_TABLE; if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index e33a659e224b..35ced100c183 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -173,7 +173,8 @@ static void ath_restart_work(struct ath_softc *sc) { ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0); - if (AR_SREV_9340(sc->sc_ah) || AR_SREV_9330(sc->sc_ah)) + if (AR_SREV_9340(sc->sc_ah) || AR_SREV_9485(sc->sc_ah) || + AR_SREV_9550(sc->sc_ah)) ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, msecs_to_jiffies(ATH_PLL_WORK_INTERVAL)); diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index f53dbd1133ce..8be2b5d8c155 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -42,6 +42,8 @@ static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf) struct ath_desc *ds; struct sk_buff *skb; + ATH_RXBUF_RESET(bf); + ds = bf->bf_desc; ds->ds_link = 0; /* link to null */ ds->ds_data = bf->bf_buf_addr; @@ -68,14 +70,6 @@ static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf) sc->rx.rxlink = &ds->ds_link; } -static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_buf *bf) -{ - if (sc->rx.buf_hold) - ath_rx_buf_link(sc, sc->rx.buf_hold); - - sc->rx.buf_hold = bf; -} - static void ath_setdefantenna(struct ath_softc *sc, u32 antenna) { /* XXX block beacon interrupts */ @@ -123,6 +117,7 @@ static bool ath_rx_edma_buf_link(struct ath_softc *sc, skb = bf->bf_mpdu; + ATH_RXBUF_RESET(bf); memset(skb->data, 0, ah->caps.rx_status_len); dma_sync_single_for_device(sc->dev, bf->bf_buf_addr, ah->caps.rx_status_len, DMA_TO_DEVICE); @@ -437,7 +432,6 @@ int ath_startrecv(struct ath_softc *sc) if (list_empty(&sc->rx.rxbuf)) goto start_recv; - sc->rx.buf_hold = NULL; sc->rx.rxlink = NULL; list_for_each_entry_safe(bf, tbf, &sc->rx.rxbuf, list) { ath_rx_buf_link(sc, bf); @@ -683,9 +677,6 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc, } bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list); - if (bf == sc->rx.buf_hold) - return NULL; - ds = bf->bf_desc; /* @@ -1387,7 +1378,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) if (edma) { ath_rx_edma_buf_link(sc, qtype); } else { - ath_rx_buf_relink(sc, bf); + ath_rx_buf_link(sc, bf); ath9k_hw_rxena(ah); } } while (1); diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index e752f5d4995d..83ab6be3fe6d 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2387,7 +2387,6 @@ void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an) for (acno = 0, ac = &an->ac[acno]; acno < IEEE80211_NUM_ACS; acno++, ac++) { ac->sched = false; - ac->clear_ps_filter = true; ac->txq = sc->tx.txq_map[acno]; INIT_LIST_HEAD(&ac->tid_q); } diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 0686375a97b9..e9010a481dfd 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1857,8 +1857,7 @@ void *carl9170_alloc(size_t priv_size) IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK | IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC | - IEEE80211_HW_SIGNAL_DBM | - IEEE80211_HW_SUPPORTS_HT_CCK_RATES; + IEEE80211_HW_SIGNAL_DBM; if (!modparam_noht) { /* diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c index 0f6eb2b6d381..44fa0cdbf97b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c @@ -575,6 +575,8 @@ static struct sdio_driver brcmf_sdmmc_driver = { static int brcmf_sdio_pd_probe(struct platform_device *pdev) { + int ret; + brcmf_dbg(SDIO, "Enter\n"); brcmfmac_sdio_pdata = pdev->dev.platform_data; @@ -582,7 +584,11 @@ static int brcmf_sdio_pd_probe(struct platform_device *pdev) if (brcmfmac_sdio_pdata->power_on) brcmfmac_sdio_pdata->power_on(); - return 0; + ret = sdio_register_driver(&brcmf_sdmmc_driver); + if (ret) + brcmf_err("sdio_register_driver failed: %d\n", ret); + + return ret; } static int brcmf_sdio_pd_remove(struct platform_device *pdev) @@ -604,15 +610,6 @@ static struct platform_driver brcmf_sdio_pd = { } }; -void brcmf_sdio_register(void) -{ - int ret; - - ret = sdio_register_driver(&brcmf_sdmmc_driver); - if (ret) - brcmf_err("sdio_register_driver failed: %d\n", ret); -} - void brcmf_sdio_exit(void) { brcmf_dbg(SDIO, "Enter\n"); @@ -623,13 +620,18 @@ void brcmf_sdio_exit(void) sdio_unregister_driver(&brcmf_sdmmc_driver); } -void __init brcmf_sdio_init(void) +void brcmf_sdio_init(void) { int ret; brcmf_dbg(SDIO, "Enter\n"); ret = platform_driver_probe(&brcmf_sdio_pd, brcmf_sdio_pd_probe); - if (ret == -ENODEV) - brcmf_dbg(SDIO, "No platform data available.\n"); + if (ret == -ENODEV) { + brcmf_dbg(SDIO, "No platform data available, registering without.\n"); + ret = sdio_register_driver(&brcmf_sdmmc_driver); + } + + if (ret) + brcmf_err("driver registration failed: %d\n", ret); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h index e715d3396c2c..080395f49fa5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h @@ -154,11 +154,10 @@ extern int brcmf_bus_start(struct device *dev); #ifdef CONFIG_BRCMFMAC_SDIO extern void brcmf_sdio_exit(void); extern void brcmf_sdio_init(void); -extern void brcmf_sdio_register(void); #endif #ifdef CONFIG_BRCMFMAC_USB extern void brcmf_usb_exit(void); -extern void brcmf_usb_register(void); +extern void brcmf_usb_init(void); #endif #endif /* _BRCMF_BUS_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 95d709959e16..2c593570497c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -1034,23 +1034,21 @@ u32 brcmf_get_chip_info(struct brcmf_if *ifp) return bus->chip << 4 | bus->chiprev; } -static void brcmf_driver_register(struct work_struct *work) +static void brcmf_driver_init(struct work_struct *work) { + brcmf_debugfs_init(); + #ifdef CONFIG_BRCMFMAC_SDIO - brcmf_sdio_register(); + brcmf_sdio_init(); #endif #ifdef CONFIG_BRCMFMAC_USB - brcmf_usb_register(); + brcmf_usb_init(); #endif } -static DECLARE_WORK(brcmf_driver_work, brcmf_driver_register); +static DECLARE_WORK(brcmf_driver_work, brcmf_driver_init); static int __init brcmfmac_module_init(void) { - brcmf_debugfs_init(); -#ifdef CONFIG_BRCMFMAC_SDIO - brcmf_sdio_init(); -#endif if (!schedule_work(&brcmf_driver_work)) return -EBUSY; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index b0ab98b0a226..01aed7ad6bec 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -1532,7 +1532,7 @@ void brcmf_usb_exit(void) brcmf_release_fw(&fw_image_list); } -void brcmf_usb_register(void) +void brcmf_usb_init(void) { brcmf_dbg(USB, "Enter\n"); INIT_LIST_HEAD(&fw_image_list); diff --git a/drivers/net/wireless/brcm80211/brcmsmac/dma.c b/drivers/net/wireless/brcm80211/brcmsmac/dma.c index 4fb9635d3919..1860c572b3c4 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/dma.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/dma.c @@ -1015,10 +1015,9 @@ static bool dma64_txidle(struct dma_info *di) /* * post receive buffers - * Return false if refill failed completely or dma mapping failed. The ring - * is empty, which will stall the rx dma and user might want to call rxfill - * again asap. This is unlikely to happen on a memory-rich NIC, but often on - * memory-constrained dongle. + * return false is refill failed completely and ring is empty this will stall + * the rx dma and user might want to call rxfill again asap. This unlikely + * happens on memory-rich NIC, but often on memory-constrained dongle */ bool dma_rxfill(struct dma_pub *pub) { @@ -1079,8 +1078,6 @@ bool dma_rxfill(struct dma_pub *pub) pa = dma_map_single(di->dmadev, p->data, di->rxbufsize, DMA_FROM_DEVICE); - if (dma_mapping_error(di->dmadev, pa)) - return false; /* save the free packet pointer */ di->rxp[rxout] = p; @@ -1287,11 +1284,7 @@ static void dma_txenq(struct dma_info *di, struct sk_buff *p) /* get physical address of buffer start */ pa = dma_map_single(di->dmadev, data, len, DMA_TO_DEVICE); - /* if mapping failed, free skb */ - if (dma_mapping_error(di->dmadev, pa)) { - brcmu_pkt_buf_free_skb(p); - return; - } + /* With a DMA segment list, Descriptor table is filled * using the segment list instead of looping over * buffers in multi-chain DMA. Therefore, EOF for SGLIST diff --git a/drivers/net/wireless/hostap/hostap_ioctl.c b/drivers/net/wireless/hostap/hostap_ioctl.c index e5090309824e..ac074731335a 100644 --- a/drivers/net/wireless/hostap/hostap_ioctl.c +++ b/drivers/net/wireless/hostap/hostap_ioctl.c @@ -523,9 +523,9 @@ static int prism2_ioctl_giwaplist(struct net_device *dev, data->length = prism2_ap_get_sta_qual(local, addr, qual, IW_MAX_AP, 1); - memcpy(extra, addr, sizeof(struct sockaddr) * data->length); + memcpy(extra, &addr, sizeof(struct sockaddr) * data->length); data->flags = 1; /* has quality information */ - memcpy(extra + sizeof(struct sockaddr) * data->length, qual, + memcpy(extra + sizeof(struct sockaddr) * data->length, &qual, sizeof(struct iw_quality) * data->length); kfree(addr); diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index a8eff95ee677..900f5f8c93ee 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -4446,9 +4446,9 @@ il4965_irq_tasklet(struct il_priv *il) set_bit(S_RFKILL, &il->status); } else { clear_bit(S_RFKILL, &il->status); + wiphy_rfkill_set_hw_state(il->hw->wiphy, hw_rf_kill); il_force_reset(il, true); } - wiphy_rfkill_set_hw_state(il->hw->wiphy, hw_rf_kill); handled |= CSR_INT_BIT_RF_KILL; } diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index e04f3da1ccb3..cab23af0be9e 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -1059,10 +1059,7 @@ void iwl_chswitch_done(struct iwl_priv *priv, bool is_success) if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; - if (!test_and_clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status)) - return; - - if (ctx->vif) + if (test_and_clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status)) ieee80211_chswitch_done(ctx->vif, is_success); } diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c index dc94d44d95cd..50263e87fe15 100644 --- a/drivers/net/wireless/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/iwlwifi/iwl-7000.c @@ -67,16 +67,16 @@ #include "iwl-agn-hw.h" /* Highest firmware API version supported */ -#define IWL7260_UCODE_API_MAX 7 -#define IWL3160_UCODE_API_MAX 7 +#define IWL7260_UCODE_API_MAX 6 +#define IWL3160_UCODE_API_MAX 6 /* Oldest version we won't warn about */ -#define IWL7260_UCODE_API_OK 7 -#define IWL3160_UCODE_API_OK 7 +#define IWL7260_UCODE_API_OK 6 +#define IWL3160_UCODE_API_OK 6 /* Lowest firmware API version supported */ -#define IWL7260_UCODE_API_MIN 7 -#define IWL3160_UCODE_API_MIN 7 +#define IWL7260_UCODE_API_MIN 6 +#define IWL3160_UCODE_API_MIN 6 /* NVM versions */ #define IWL7260_NVM_VERSION 0x0a1d diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h index 6f8b2c16ae17..51e015d1dfb2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h @@ -75,15 +75,13 @@ enum iwl_d3_wakeup_flags { * struct iwl_d3_manager_config - D3 manager configuration command * @min_sleep_time: minimum sleep time (in usec) * @wakeup_flags: wakeup flags, see &enum iwl_d3_wakeup_flags - * @wakeup_host_timer: force wakeup after this many seconds * * The structure is used for the D3_CONFIG_CMD command. */ struct iwl_d3_manager_config { __le32 min_sleep_time; __le32 wakeup_flags; - __le32 wakeup_host_timer; -} __packed; /* D3_MANAGER_CONFIG_CMD_S_VER_4 */ +} __packed; /* D3_MANAGER_CONFIG_CMD_S_VER_3 */ /* TODO: OFFLOADS_QUERY_API_S_VER_1 */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h index 98b1feb43d38..d68640ea41d4 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h @@ -71,13 +71,7 @@ #define MAC_INDEX_MIN_DRIVER 0 #define NUM_MAC_INDEX_DRIVER MAC_INDEX_AUX -enum iwl_ac { - AC_BK, - AC_BE, - AC_VI, - AC_VO, - AC_NUM, -}; +#define AC_NUM 4 /* Number of access categories */ /** * enum iwl_mac_protection_flags - MAC context flags diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index d8e858cc8dd9..b2cc3d98e0f7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -193,11 +193,14 @@ static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac, u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { - u32 qmask = 0, ac; + u32 qmask, ac; if (vif->type == NL80211_IFTYPE_P2P_DEVICE) return BIT(IWL_MVM_OFFCHANNEL_QUEUE); + qmask = (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE) ? + BIT(vif->cab_queue) : 0; + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE) qmask |= BIT(vif->hw_queue[ac]); @@ -359,7 +362,7 @@ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif) break; case NL80211_IFTYPE_AP: iwl_trans_ac_txq_enable(mvm->trans, vif->cab_queue, - IWL_MVM_TX_FIFO_MCAST); + IWL_MVM_TX_FIFO_VO); /* fall through */ default: for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) @@ -547,10 +550,6 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, cmd->ac[i].fifos_mask = BIT(iwl_mvm_ac_to_tx_fifo[i]); } - /* in AP mode, the MCAST FIFO takes the EDCA params from VO */ - if (vif->type == NL80211_IFTYPE_AP) - cmd->ac[AC_VO].fifos_mask |= BIT(IWL_MVM_TX_FIFO_MCAST); - if (vif->bss_conf.qos) cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA); diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index f7545e06ce2a..b7e95b0a42b7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -243,11 +243,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) if (ret) return ret; - ret = ieee80211_register_hw(mvm->hw); - if (ret) - iwl_mvm_leds_exit(mvm); - - return ret; + return ieee80211_register_hw(mvm->hw); } static void iwl_mvm_mac_tx(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 80862319c42a..9f46b23801bc 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -88,7 +88,6 @@ enum iwl_mvm_tx_fifo { IWL_MVM_TX_FIFO_BE, IWL_MVM_TX_FIFO_VI, IWL_MVM_TX_FIFO_VO, - IWL_MVM_TX_FIFO_MCAST = 5, }; extern struct ieee80211_ops iwl_mvm_hw_ops; diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 68f0bbe1f381..736b50bc962c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -226,6 +226,9 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE) mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]); + if (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE) + mvm_sta->tfd_queue_msk |= BIT(vif->cab_queue); + /* for HW restart - need to reset the seq_number etc... */ memset(mvm_sta->tid_data, 0, sizeof(mvm_sta->tid_data)); @@ -1293,11 +1296,17 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, struct iwl_mvm_add_sta_cmd cmd = { .add_modify = STA_MODE_MODIFY, .sta_id = mvmsta->sta_id, - .station_flags_msk = cpu_to_le32(STA_FLG_PS), + .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT, + .sleep_state_flags = cpu_to_le16(STA_SLEEP_STATE_AWAKE), .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color), }; int ret; + /* + * Same modify mask for sleep_tx_count and sleep_state_flags but this + * should be fine since if we set the STA as "awake", then + * sleep_tx_count is not relevant. + */ ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd); if (ret) IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index a2e6112e91e9..48c1891e3df6 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -175,7 +175,7 @@ static void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, * table is controlled by LINK_QUALITY commands */ - if (ieee80211_is_data(fc) && sta) { + if (ieee80211_is_data(fc)) { tx_cmd->initial_rate_index = 0; tx_cmd->tx_flags |= cpu_to_le32(TX_CMD_FLG_STA_RATE); return; @@ -610,8 +610,8 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, !(info->flags & IEEE80211_TX_STAT_ACK)) info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; - /* W/A FW bug: seq_ctl is wrong when the status isn't success */ - if (status != TX_STATUS_SUCCESS) { + /* W/A FW bug: seq_ctl is wrong when the queue is flushed */ + if (status == TX_STATUS_FAIL_FIFO_FLUSHED) { struct ieee80211_hdr *hdr = (void *)skb->data; seq_ctl = le16_to_cpu(hdr->seq_ctrl); } diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index aeb70e13137a..50ba0a468f94 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -1481,16 +1481,16 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, spin_lock_init(&trans_pcie->reg_lock); init_waitqueue_head(&trans_pcie->ucode_write_waitq); - if (pci_enable_device(pdev)) { - err = -ENODEV; - goto out_no_pci; - } - /* W/A - seems to solve weird behavior. We need to remove this if we * don't want to stay in L1 all the time. This wastes a lot of power */ pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_CLKPM); + if (pci_enable_device(pdev)) { + err = -ENODEV; + goto out_no_pci; + } + pci_set_master(pdev); err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36)); diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c index d69d02435b6b..a78e0651409c 100644 --- a/drivers/net/wireless/mwifiex/11n_aggr.c +++ b/drivers/net/wireless/mwifiex/11n_aggr.c @@ -149,7 +149,7 @@ mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv, */ int mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *pra_list, + struct mwifiex_ra_list_tbl *pra_list, int headroom, int ptrindex, unsigned long ra_list_flags) __releases(&priv->wmm.ra_list_spinlock) { @@ -159,7 +159,6 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, int pad = 0, ret; struct mwifiex_tx_param tx_param; struct txpd *ptx_pd = NULL; - int headroom = adapter->iface_type == MWIFIEX_USB ? 0 : INTF_HEADER_LEN; skb_src = skb_peek(&pra_list->skb_head); if (!skb_src) { diff --git a/drivers/net/wireless/mwifiex/11n_aggr.h b/drivers/net/wireless/mwifiex/11n_aggr.h index 892098d6a696..900e1c62a0cc 100644 --- a/drivers/net/wireless/mwifiex/11n_aggr.h +++ b/drivers/net/wireless/mwifiex/11n_aggr.h @@ -26,7 +26,7 @@ int mwifiex_11n_deaggregate_pkt(struct mwifiex_private *priv, struct sk_buff *skb); int mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ptr, + struct mwifiex_ra_list_tbl *ptr, int headroom, int ptr_index, unsigned long flags) __releases(&priv->wmm.ra_list_spinlock); diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index 7a97f6cba089..26755d9acb55 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -1154,7 +1154,7 @@ int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv, uint32_t conditions = le32_to_cpu(phs_cfg->params.hs_config.conditions); if (phs_cfg->action == cpu_to_le16(HS_ACTIVATE) && - adapter->iface_type != MWIFIEX_USB) { + adapter->iface_type == MWIFIEX_SDIO) { mwifiex_hs_activated_event(priv, true); return 0; } else { @@ -1166,7 +1166,8 @@ int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv, } if (conditions != HS_CFG_CANCEL) { adapter->is_hs_configured = true; - if (adapter->iface_type == MWIFIEX_USB) + if (adapter->iface_type == MWIFIEX_USB || + adapter->iface_type == MWIFIEX_PCIE) mwifiex_hs_activated_event(priv, true); } else { adapter->is_hs_configured = false; diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 8fc44992b5c3..2eb88ea9acf7 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -363,6 +363,20 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) dev_err(adapter->dev, "cannot create default STA interface\n"); goto err_add_intf; } + + /* Create AP interface by default */ + if (!mwifiex_add_virtual_intf(adapter->wiphy, "uap%d", + NL80211_IFTYPE_AP, NULL, NULL)) { + dev_err(adapter->dev, "cannot create default AP interface\n"); + goto err_add_intf; + } + + /* Create P2P interface by default */ + if (!mwifiex_add_virtual_intf(adapter->wiphy, "p2p%d", + NL80211_IFTYPE_P2P_CLIENT, NULL, NULL)) { + dev_err(adapter->dev, "cannot create default P2P interface\n"); + goto err_add_intf; + } rtnl_unlock(); mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1); @@ -559,8 +573,9 @@ static void mwifiex_set_multicast_list(struct net_device *dev) mcast_list.mode = MWIFIEX_ALL_MULTI_MODE; } else { mcast_list.mode = MWIFIEX_MULTICAST_MODE; - mcast_list.num_multicast_addr = - mwifiex_copy_mcast_addr(&mcast_list, dev); + if (netdev_mc_count(dev)) + mcast_list.num_multicast_addr = + mwifiex_copy_mcast_addr(&mcast_list, dev); } mwifiex_request_set_multicast_list(priv, &mcast_list); } diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index 23aa910bc5d0..1a8a19dbd635 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -104,14 +104,16 @@ int mwifiex_request_set_multicast_list(struct mwifiex_private *priv, } else { priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; - dev_dbg(priv->adapter->dev, - "info: Set multicast list=%d\n", - mcast_list->num_multicast_addr); - /* Send multicast addresses to firmware */ - ret = mwifiex_send_cmd_async(priv, - HostCmd_CMD_MAC_MULTICAST_ADR, - HostCmd_ACT_GEN_SET, 0, - mcast_list); + if (mcast_list->num_multicast_addr) { + dev_dbg(priv->adapter->dev, + "info: Set multicast list=%d\n", + mcast_list->num_multicast_addr); + /* Send multicast addresses to firmware */ + ret = mwifiex_send_cmd_async(priv, + HostCmd_CMD_MAC_MULTICAST_ADR, + HostCmd_ACT_GEN_SET, 0, + mcast_list); + } } } dev_dbg(priv->adapter->dev, diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c index b7adf3d46463..f90fe21e5bfd 100644 --- a/drivers/net/wireless/mwifiex/usb.c +++ b/drivers/net/wireless/mwifiex/usb.c @@ -446,6 +446,9 @@ static int mwifiex_usb_suspend(struct usb_interface *intf, pm_message_t message) */ adapter->is_suspended = true; + for (i = 0; i < adapter->priv_num; i++) + netif_carrier_off(adapter->priv[i]->netdev); + if (atomic_read(&card->rx_cmd_urb_pending) && card->rx_cmd.urb) usb_kill_urb(card->rx_cmd.urb); @@ -505,6 +508,10 @@ static int mwifiex_usb_resume(struct usb_interface *intf) MWIFIEX_RX_CMD_BUF_SIZE); } + for (i = 0; i < adapter->priv_num; i++) + if (adapter->priv[i]->media_connected) + netif_carrier_on(adapter->priv[i]->netdev); + /* Disable Host Sleep */ if (adapter->hs_activated) mwifiex_cancel_hs(mwifiex_get_priv(adapter, diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index ae31e8df44d7..4be3d33ceae8 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -1236,7 +1236,8 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter) if (mwifiex_is_amsdu_allowed(priv, tid) && mwifiex_is_11n_aggragation_possible(priv, ptr, adapter->tx_buf_size)) - mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index, flags); + mwifiex_11n_aggregate_pkt(priv, ptr, INTF_HEADER_LEN, + ptr_index, flags); /* ra_list_spinlock has been freed in mwifiex_11n_aggregate_pkt() */ else diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c index f42dc3ccd496..b9deef66cf4b 100644 --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c @@ -83,7 +83,6 @@ static struct usb_device_id p54u_table[] = { {USB_DEVICE(0x06a9, 0x000e)}, /* Westell 802.11g USB (A90-211WG-01) */ {USB_DEVICE(0x06b9, 0x0121)}, /* Thomson SpeedTouch 121g */ {USB_DEVICE(0x0707, 0xee13)}, /* SMC 2862W-G version 2 */ - {USB_DEVICE(0x07aa, 0x0020)}, /* Corega WLUSB2GTST USB */ {USB_DEVICE(0x0803, 0x4310)}, /* Zoom 4410a */ {USB_DEVICE(0x083a, 0x4521)}, /* Siemens Gigaset USB Adapter 54 version 2 */ {USB_DEVICE(0x083a, 0x4531)}, /* T-Com Sinus 154 data II */ diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index f281971919f5..705aa3386ee0 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -2766,13 +2766,6 @@ static int rt2800_get_gain_calibration_delta(struct rt2x00_dev *rt2x00dev) u8 step; int i; - /* - * First check if temperature compensation is supported. - */ - rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom); - if (!rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_EXTERNAL_TX_ALC)) - return 0; - /* * Read TSSI boundaries for temperature compensation from * the EEPROM. @@ -4048,6 +4041,10 @@ static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) u8 reg_id; u8 value; + if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev) || + rt2800_wait_bbp_ready(rt2x00dev))) + return -EACCES; + if (rt2x00_rt(rt2x00dev, RT5592)) { rt2800_init_bbp_5592(rt2x00dev); return 0; @@ -5188,23 +5185,20 @@ int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev) rt2800_init_registers(rt2x00dev))) return -EIO; - if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev))) - return -EIO; - /* * Send signal to firmware during boot time. */ rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0); rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); - if (rt2x00_is_usb(rt2x00dev)) + if (rt2x00_is_usb(rt2x00dev)) { rt2800_register_write(rt2x00dev, H2M_INT_SRC, 0); - rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0, 0, 0); + rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0, 0, 0); + } msleep(1); - if (unlikely(rt2800_wait_bbp_ready(rt2x00dev))) + if (unlikely(rt2800_init_bbp(rt2x00dev))) return -EIO; - rt2800_init_bbp(rt2x00dev); rt2800_init_rfcsr(rt2x00dev); if (rt2x00_is_usb(rt2x00dev) && @@ -5918,8 +5912,7 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK | IEEE80211_HW_AMPDU_AGGREGATION | - IEEE80211_HW_REPORTS_TX_ACK_STATUS | - IEEE80211_HW_SUPPORTS_HT_CCK_RATES; + IEEE80211_HW_REPORTS_TX_ACK_STATUS; /* * Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING for USB devices diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h index 703258742d28..cc03e7c87cbe 100644 --- a/drivers/net/wireless/rtlwifi/wifi.h +++ b/drivers/net/wireless/rtlwifi/wifi.h @@ -2057,7 +2057,7 @@ struct rtl_priv { that it points to the data allocated beyond this structure like: rtl_pci_priv or rtl_usb_priv */ - u8 priv[0] __aligned(sizeof(void *)); + u8 priv[0]; }; #define rtl_priv(hw) (((struct rtl_priv *)(hw)->priv)) diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c index b8ba1f925e75..4941f201d6c8 100644 --- a/drivers/net/wireless/zd1201.c +++ b/drivers/net/wireless/zd1201.c @@ -98,12 +98,10 @@ static int zd1201_fw_upload(struct usb_device *dev, int apfw) goto exit; err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x4, - USB_DIR_IN | 0x40, 0, 0, buf, sizeof(ret), ZD1201_FW_TIMEOUT); + USB_DIR_IN | 0x40, 0,0, &ret, sizeof(ret), ZD1201_FW_TIMEOUT); if (err < 0) goto exit; - memcpy(&ret, buf, sizeof(ret)); - if (ret & 0x80) { err = -EIO; goto exit; diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index 0071f211a08a..8c20935d72c9 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -354,49 +354,6 @@ static bool start_new_rx_buffer(int offset, unsigned long size, int head) return false; } -struct xenvif_count_slot_state { - unsigned long copy_off; - bool head; -}; - -unsigned int xenvif_count_frag_slots(struct xenvif *vif, - unsigned long offset, unsigned long size, - struct xenvif_count_slot_state *state) -{ - unsigned count = 0; - - offset &= ~PAGE_MASK; - - while (size > 0) { - unsigned long bytes; - - bytes = PAGE_SIZE - offset; - - if (bytes > size) - bytes = size; - - if (start_new_rx_buffer(state->copy_off, bytes, state->head)) { - count++; - state->copy_off = 0; - } - - if (state->copy_off + bytes > MAX_BUFFER_OFFSET) - bytes = MAX_BUFFER_OFFSET - state->copy_off; - - state->copy_off += bytes; - - offset += bytes; - size -= bytes; - - if (offset == PAGE_SIZE) - offset = 0; - - state->head = false; - } - - return count; -} - /* * Figure out how many ring slots we're going to need to send @skb to * the guest. This function is essentially a dry run of @@ -404,39 +361,48 @@ unsigned int xenvif_count_frag_slots(struct xenvif *vif, */ unsigned int xen_netbk_count_skb_slots(struct xenvif *vif, struct sk_buff *skb) { - struct xenvif_count_slot_state state; unsigned int count; - unsigned char *data; - unsigned i; + int i, copy_off; - state.head = true; - state.copy_off = 0; + count = DIV_ROUND_UP(skb_headlen(skb), PAGE_SIZE); - /* Slot for the first (partial) page of data. */ - count = 1; + copy_off = skb_headlen(skb) % PAGE_SIZE; - /* Need a slot for the GSO prefix for GSO extra data? */ if (skb_shinfo(skb)->gso_size) count++; - data = skb->data; - while (data < skb_tail_pointer(skb)) { - unsigned long offset = offset_in_page(data); - unsigned long size = PAGE_SIZE - offset; + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + unsigned long size = skb_frag_size(&skb_shinfo(skb)->frags[i]); + unsigned long offset = skb_shinfo(skb)->frags[i].page_offset; + unsigned long bytes; - if (data + size > skb_tail_pointer(skb)) - size = skb_tail_pointer(skb) - data; + offset &= ~PAGE_MASK; - count += xenvif_count_frag_slots(vif, offset, size, &state); + while (size > 0) { + BUG_ON(offset >= PAGE_SIZE); + BUG_ON(copy_off > MAX_BUFFER_OFFSET); - data += size; - } + bytes = PAGE_SIZE - offset; - for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { - unsigned long size = skb_frag_size(&skb_shinfo(skb)->frags[i]); - unsigned long offset = skb_shinfo(skb)->frags[i].page_offset; + if (bytes > size) + bytes = size; - count += xenvif_count_frag_slots(vif, offset, size, &state); + if (start_new_rx_buffer(copy_off, bytes, 0)) { + count++; + copy_off = 0; + } + + if (copy_off + bytes > MAX_BUFFER_OFFSET) + bytes = MAX_BUFFER_OFFSET - copy_off; + + copy_off += bytes; + + offset += bytes; + size -= bytes; + + if (offset == PAGE_SIZE) + offset = 0; + } } return count; } diff --git a/drivers/of/base.c b/drivers/of/base.c index 1d10b4ec6814..a6f584a7f4a1 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1629,7 +1629,6 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)) ap = dt_alloc(sizeof(*ap) + len + 1, 4); if (!ap) continue; - memset(ap, 0, sizeof(*ap) + len + 1); ap->alias = start; of_alias_add(ap, np, id, start, len); } diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 118773751ea4..808be06bb67e 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -392,8 +392,6 @@ static void __unflatten_device_tree(struct boot_param_header *blob, mem = (unsigned long) dt_alloc(size + 4, __alignof__(struct device_node)); - memset((void *)mem, 0, size); - ((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeef); pr_debug(" unflattening %lx...\n", mem); diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 0eab3a3c51c5..e4b1fb2c0f5d 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -47,9 +47,6 @@ static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context) if (event != ACPI_NOTIFY_DEVICE_WAKE || !pci_dev) return; - if (pci_dev->pme_poll) - pci_dev->pme_poll = false; - if (pci_dev->current_state == PCI_D3cold) { pci_wakeup_event(pci_dev); pm_runtime_resume(&pci_dev->dev); @@ -60,6 +57,9 @@ static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context) if (pci_dev->pme_support) pci_check_pme_status(pci_dev); + if (pci_dev->pme_poll) + pci_dev->pme_poll = false; + pci_wakeup_event(pci_dev); pm_runtime_resume(&pci_dev->dev); @@ -317,20 +317,13 @@ void acpi_pci_remove_bus(struct pci_bus *bus) /* ACPI bus type */ static int acpi_pci_find_device(struct device *dev, acpi_handle *handle) { - struct pci_dev *pci_dev = to_pci_dev(dev); - bool is_bridge; - u64 addr; + struct pci_dev * pci_dev; + u64 addr; - /* - * pci_is_bridge() is not suitable here, because pci_dev->subordinate - * is set only after acpi_pci_find_device() has been called for the - * given device. - */ - is_bridge = pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE - || pci_dev->hdr_type == PCI_HEADER_TYPE_CARDBUS; + pci_dev = to_pci_dev(dev); /* Please ref to ACPI spec for the syntax of _ADR */ addr = (PCI_SLOT(pci_dev->devfn) << 16) | PCI_FUNC(pci_dev->devfn); - *handle = acpi_find_child(ACPI_HANDLE(dev->parent), addr, is_bridge); + *handle = acpi_get_child(DEVICE_ACPI_HANDLE(dev->parent), addr); if (!*handle) return -ENODEV; return 0; diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index 314e5e8e9122..5d7529ed5392 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -325,7 +325,7 @@ static void at91_mux_disable_interrupt(void __iomem *pio, unsigned mask) static unsigned at91_mux_get_pullup(void __iomem *pio, unsigned pin) { - return !((readl_relaxed(pio + PIO_PUSR) >> pin) & 0x1); + return (readl_relaxed(pio + PIO_PUSR) >> pin) & 0x1; } static void at91_mux_set_pullup(void __iomem *pio, unsigned mask, bool on) @@ -445,7 +445,7 @@ static void at91_mux_pio3_set_debounce(void __iomem *pio, unsigned mask, static bool at91_mux_pio3_get_pulldown(void __iomem *pio, unsigned pin) { - return !((__raw_readl(pio + PIO_PPDSR) >> pin) & 0x1); + return (__raw_readl(pio + PIO_PPDSR) >> pin) & 0x1; } static void at91_mux_pio3_set_pulldown(void __iomem *pio, unsigned mask, bool is_on) diff --git a/drivers/platform/olpc/olpc-ec.c b/drivers/platform/olpc/olpc-ec.c index f9119525f557..0f9f8596b300 100644 --- a/drivers/platform/olpc/olpc-ec.c +++ b/drivers/platform/olpc/olpc-ec.c @@ -330,7 +330,7 @@ static int __init olpc_ec_init_module(void) return platform_driver_register(&olpc_ec_plat_driver); } -arch_initcall(olpc_ec_init_module); +module_init(olpc_ec_init_module); MODULE_AUTHOR("Andres Salomon "); MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index a1df080a0001..d42f5efc7154 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -812,4 +812,21 @@ config INTEL_SCU_FLIS We could read write the flis address and configure the pin pull up/down using these interfaces. +config INTEL_PSH_IPC + bool "Intel PSH IPC Support" + depends on X86_INTEL_MID + ---help--- + PSH(Platform Services Hub) is a low frequence IA core on Tangier Platform, + whose power consumption is quite low. PSH runs RTOS software inside itself, + which independently controls and collects sensor data, pre-processes the data, + and communicates with Atom. Thus ATOM side could be put into low power mode + with more time, while all the sensor data are collected without any lost. + + PSH IPC is used as a bridge for OS sensor service to control and access PSH + sensors communications between kernel and PSH. This is not needed for PC-type + machines. + + Say Y here to get Intel PSH IPC support. + + endif # X86_PLATFORM_DEVICES diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index c075bb2e8c8a..7d82ebfcca9d 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -53,3 +53,5 @@ obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o obj-$(CONFIG_PVPANIC) += pvpanic.o + +obj-$(CONFIG_INTEL_PSH_IPC) += intel_psh_ipc.o diff --git a/drivers/platform/x86/intel_psh_ipc.c b/drivers/platform/x86/intel_psh_ipc.c new file mode 100644 index 000000000000..12e8bf1d86af --- /dev/null +++ b/drivers/platform/x86/intel_psh_ipc.c @@ -0,0 +1,637 @@ +/* + * intel_psh_ipc.c: Driver for the Intel PSH IPC mechanism + * + * (C) Copyright 2012 Intel Corporation + * Author: Yang Bin (bin.yang@intel.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PSH_ERR(fmt, arg...) dev_err(&ipc_ctrl.pdev->dev, fmt, ##arg) +#define PSH_DBG(fmt, arg...) dev_dbg(&ipc_ctrl.pdev->dev, fmt, ##arg) + +#define STATUS_PSH2IA(x) (1 << ((x) + 6)) +#define FLAG_BIND (1 << 0) + +#define PIMR_ADDR(x) (&ipc_ctrl.psh_regs->psh_regs_b_step.pimr##x) + +#define PSH_REG_ADDR(x) (&ipc_ctrl.psh_regs->psh_regs_b_step.x) + +#define PSH_CH_HANDLE(x) (ipc_ctrl.channel_handle[x]) +#define PSH_CH_DATA(x) (ipc_ctrl.channel_data[x]) +#define PSH_CH_FLAG(x) (ipc_ctrl.flags[x]) + +/* PSH registers */ +union psh_registers { + /* reg mem map A */ + struct { + u32 csr; /* 00h */ + u32 res1; /* padding */ + u32 pisr; /* 08h */ + u32 pimr0; /* 0Ch */ + u32 pimr1; /* 10h */ + u32 pimr2; /* 14h */ + u32 pimr3; /* 18h */ + u32 pmctl; /* 1Ch */ + u32 pmstat; /* 20h */ + u32 res2; /* padding */ + struct psh_msg ia2psh[NUM_IA2PSH_IPC];/* 28h ~ 44h + 3 */ + struct psh_msg cry2psh;/* 48h ~ 4Ch + 3 */ + struct psh_msg scu2psh;/* 50h ~ 54h + 3 */ + u32 res3[2];/* padding */ + struct psh_msg psh2ia[NUM_PSH2IA_IPC];/* 60h ~ 7Ch + 3 */ + struct psh_msg psh2cry;/* 80h ~ 84h + 3 */ + struct psh_msg psh2scu;/* 88h */ + u32 msi_dir;/* 90h */ + u32 res4[3]; + u32 scratchpad[2];/* A0 */ + } __packed psh_regs_a_step; + /* reg mem map B */ + struct { + u32 pimr0; /* 00h */ + u32 csr; /* 04h */ + u32 pmctl; /* 08h */ + u32 pmstat; /* 0Ch */ + u32 psh_msi_direct; /* 10h */ + u32 res1[59]; /* 14h ~ FCh + 3, padding */ + u32 pimr3; /* 100h */ + struct psh_msg scu2psh; /* 104h ~ 108h + 3 */ + struct psh_msg psh2scu; /* 10Ch ~ 110h + 3 */ + u32 res2[187]; /* 114h ~ 3FCh + 3, padding */ + u32 pisr; /* 400h */ + u32 scratchpad[2]; /* 404h ~ 407h */ + u32 res3[61]; /* 40Ch ~ 4FCh + 3, padding */ + u32 pimr1; /* 500h */ + struct psh_msg ia2psh[NUM_IA2PSH_IPC]; /* 504h ~ 520h + 3 */ + struct psh_msg psh2ia[NUM_PSH2IA_IPC]; /* 524h ~ 540h + 3 */ + u32 res4[175]; /* 544h ~ 7FCh + 3, padding */ + u32 pimr2; /* 800h */ + struct psh_msg cry2psh; /* 804h ~ 808h + 3 */ + struct psh_msg psh2cry; /* 80Ch ~ 810h + 3 */ + } __packed psh_regs_b_step; +} __packed; + +static struct ipc_controller_t { + int reg_map; + int initialized; + struct pci_dev *pdev; + spinlock_t lock; + int flags[NUM_ALL_CH]; + union psh_registers *psh_regs; + struct semaphore ch_lock[NUM_ALL_CH]; + struct mutex psh_mutex; + psh_channel_handle_t channel_handle[NUM_PSH2IA_IPC]; + void *channel_data[NUM_PSH2IA_IPC]; +} ipc_ctrl; + + +/** + * intel_ia2psh_command - send IA to PSH command + * Send ia2psh command and return psh message and status + * + * @in: input psh message + * @out: output psh message + * @ch: psh channel + * @timeout: timeout for polling busy bit, in us + */ +int intel_ia2psh_command(struct psh_msg *in, struct psh_msg *out, + int ch, int timeout) +{ + int ret = 0; + u32 status; + + might_sleep(); + + if (!ipc_ctrl.initialized) + return -ENODEV; + + if (ch < PSH_SEND_CH0 || ch > PSH_SEND_CH0 + NUM_IA2PSH_IPC - 1 + || in == NULL) + return -EINVAL; + + if (!in || in->msg & CHANNEL_BUSY) + return -EINVAL; + + pm_runtime_get_sync(&ipc_ctrl.pdev->dev); + down(&ipc_ctrl.ch_lock[ch]); + + in->msg |= CHANNEL_BUSY; + /* Check if channel is ready for IA sending command */ + + if (readl(PSH_REG_ADDR(ia2psh[ch].msg)) & CHANNEL_BUSY) { + ret = -EBUSY; + goto end; + } + + writel(in->param, PSH_REG_ADDR(ia2psh[ch].param)); + writel(in->msg, PSH_REG_ADDR(ia2psh[ch].msg)); + + /* Input timeout is zero, do not check channel status */ + if (timeout == 0) + goto end; + + /* Input timeout is nonzero, check channel status */ + while (((status = readl(PSH_REG_ADDR(ia2psh[ch].msg))) & CHANNEL_BUSY) + && timeout) { + usleep_range(100, 101); + timeout -= 100; + } + + if (timeout <= 0) { + ret = -ETIMEDOUT; + PSH_ERR("ia2psh channel %d is always busy!\n", ch); + goto end; + } else { + if (out == NULL) + goto end; + + out->param = readl(PSH_REG_ADDR(ia2psh[ch].param)); + out->msg = status; + } + +end: + up(&ipc_ctrl.ch_lock[ch]); + pm_runtime_put(&ipc_ctrl.pdev->dev); + + return ret; +} +EXPORT_SYMBOL(intel_ia2psh_command); + +/** + * intel_psh_ipc_bind - bind a handler to a psh channel + * + * @ch: psh channel + * @handle: handle function called when IA received psh interrupt + * @data: data passed to handle + */ +int intel_psh_ipc_bind(int ch, psh_channel_handle_t handle, void *data) +{ + unsigned long flags; + + if (!ipc_ctrl.initialized) + return -ENODEV; + + if (!handle || ch < PSH_RECV_CH0 + || ch > PSH_RECV_CH0 + NUM_PSH2IA_IPC - 1) + return -EINVAL; + + mutex_lock(&ipc_ctrl.psh_mutex); + down(&ipc_ctrl.ch_lock[ch]); + if (PSH_CH_HANDLE(ch - PSH_RECV_CH0) != NULL) { + up(&ipc_ctrl.ch_lock[ch]); + mutex_unlock(&ipc_ctrl.psh_mutex); + return -EBUSY; + } else { + PSH_CH_DATA(ch - PSH_RECV_CH0) = data; + PSH_CH_HANDLE(ch - PSH_RECV_CH0) = handle; + } + up(&ipc_ctrl.ch_lock[ch]); + + pm_runtime_get_sync(&ipc_ctrl.pdev->dev); + spin_lock_irqsave(&ipc_ctrl.lock, flags); + PSH_CH_FLAG(ch) |= FLAG_BIND; + writel(readl(PIMR_ADDR(1)) | (1 << (ch - PSH_RECV_CH0)), PIMR_ADDR(1)); + spin_unlock_irqrestore(&ipc_ctrl.lock, flags); + pm_runtime_put(&ipc_ctrl.pdev->dev); + mutex_unlock(&ipc_ctrl.psh_mutex); + + return 0; +} +EXPORT_SYMBOL(intel_psh_ipc_bind); + +/** + * intel_psh_ipc_unbind - unbind a handler to a psh channel + * + * @ch: psh channel + */ +void intel_psh_ipc_unbind(int ch) +{ + unsigned long flags; + + if (!ipc_ctrl.initialized) + return; + + if (ch < PSH_RECV_CH0 || ch > PSH_RECV_CH0 + NUM_PSH2IA_IPC - 1) + return; + + if (!(PSH_CH_FLAG(ch) & FLAG_BIND)) + return; + + mutex_lock(&ipc_ctrl.psh_mutex); + pm_runtime_get_sync(&ipc_ctrl.pdev->dev); + spin_lock_irqsave(&ipc_ctrl.lock, flags); + PSH_CH_FLAG(ch) &= ~FLAG_BIND; + writel(readl(PIMR_ADDR(1)) & (~(1 << (ch - PSH_RECV_CH0))), + PIMR_ADDR(1)); + spin_unlock_irqrestore(&ipc_ctrl.lock, flags); + pm_runtime_put(&ipc_ctrl.pdev->dev); + + down(&ipc_ctrl.ch_lock[ch]); + PSH_CH_HANDLE(ch - PSH_RECV_CH0) = NULL; + up(&ipc_ctrl.ch_lock[ch]); + mutex_unlock(&ipc_ctrl.psh_mutex); +} +EXPORT_SYMBOL(intel_psh_ipc_unbind); + +void intel_psh_ipc_disable_irq(void) +{ + disable_irq(ipc_ctrl.pdev->irq); +} +EXPORT_SYMBOL(intel_psh_ipc_disable_irq); + +void intel_psh_ipc_enable_irq(void) +{ + enable_irq(ipc_ctrl.pdev->irq); +} +EXPORT_SYMBOL(intel_psh_ipc_enable_irq); + +static void psh_recv_handle(int i) +{ + int msg, param; + + down(&ipc_ctrl.ch_lock[i + PSH_RECV_CH0]); + + msg = readl(PSH_REG_ADDR(psh2ia[i].msg)) & (~CHANNEL_BUSY); + param = readl(PSH_REG_ADDR(psh2ia[i].param)); + + if (PSH_CH_HANDLE(i) == NULL) { + PSH_ERR("Ignore message from channel %d\n", i+PSH_RECV_CH0); + goto end; + } + + /* write back to clear the busy bit */ + writel(msg, PSH_REG_ADDR(psh2ia[i].msg)); + PSH_CH_HANDLE(i)(msg, param, PSH_CH_DATA(i)); +end: + up(&ipc_ctrl.ch_lock[i+PSH_RECV_CH0]); +} + +static irqreturn_t psh_ipc_irq(int irq, void *data) +{ + int i; + u32 status; + + pm_runtime_get_sync(&ipc_ctrl.pdev->dev); + status = readl(PSH_REG_ADDR(pisr)); + + for (i = 0; i < NUM_PSH2IA_IPC; i++) { + if (status & STATUS_PSH2IA(i)) + psh_recv_handle(i); + } + + pm_runtime_put(&ipc_ctrl.pdev->dev); + return IRQ_HANDLED; +} + +static void psh_regs_dump(void) +{ + int i; + + pm_runtime_get_sync(&ipc_ctrl.pdev->dev); + PSH_ERR("\n<-------------start------------>\n"); + + PSH_ERR("csr:\t%#x\n", readl(PSH_REG_ADDR(csr))); + PSH_ERR("pisr:\t%#x\n", readl(PSH_REG_ADDR(pisr))); + + PSH_ERR("pimr0:\t%#x\n", readl(PIMR_ADDR(0))); + PSH_ERR("pimr1:\t%#x\n", readl(PIMR_ADDR(1))); + PSH_ERR("pimr2:\t%#x\n", readl(PIMR_ADDR(2))); + PSH_ERR("pimr3:\t%#x\n", readl(PIMR_ADDR(3))); + + PSH_ERR("pmctl:\t%#x\n", readl(PSH_REG_ADDR(pmctl))); + PSH_ERR("pmstat:\t%#x\n", readl(PSH_REG_ADDR(pmstat))); + PSH_ERR("scratchpad0:\t%#x\n", readl(PSH_REG_ADDR(scratchpad[0]))); + PSH_ERR("scratchpad1:\t%#x\n", readl(PSH_REG_ADDR(scratchpad[1]))); + + for (i = 0; i < NUM_IA2PSH_IPC; i++) { + PSH_ERR("ia2psh[%d].msg:\t%#x\n", i, + readl(PSH_REG_ADDR(ia2psh[i].msg))); + PSH_ERR("ia2psh[%d].param:\t%#x\n", i, + readl(PSH_REG_ADDR(ia2psh[i].param))); + } + + PSH_ERR("cry2psh.msg:\t%#x\n", readl(PSH_REG_ADDR(cry2psh.msg))); + PSH_ERR("cry2psh.param:\t%#x\n", readl(PSH_REG_ADDR(cry2psh.param))); + PSH_ERR("scu2psh.msg:\t%#x\n", readl(PSH_REG_ADDR(scu2psh.msg))); + PSH_ERR("scu2psh.param:\t%#x\n", readl(PSH_REG_ADDR(scu2psh.param))); + + for (i = 0; i < NUM_PSH2IA_IPC; i++) { + PSH_ERR("psh2ia[%d].msg:\t%#x\n", i, + readl(PSH_REG_ADDR(psh2ia[i].msg))); + PSH_ERR("psh2ia[%d].param:\t%#x\n", i, + readl(PSH_REG_ADDR(psh2ia[i].param))); + } + + PSH_ERR("psh2cry.msg:\t%#x\n", readl(PSH_REG_ADDR(psh2cry.msg))); + PSH_ERR("psh2cry.param:\t%#x\n", readl(PSH_REG_ADDR(psh2cry.param))); + + PSH_ERR("\n<-------------end------------>\n"); + pm_runtime_put(&ipc_ctrl.pdev->dev); +} + +static struct psh_msg psh_dbg_msg; +static int psh_ch; + +static ssize_t psh_msg_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, + "\nLast ia2psh command with msg: %#x\nparam: %#x\n", + psh_dbg_msg.msg, psh_dbg_msg.param); +} + +static ssize_t psh_msg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + u32 msg, param; + + memset(&psh_dbg_msg, 0, sizeof(psh_dbg_msg)); + + ret = sscanf(buf, "%x %x", &msg, ¶m); + if (ret != 2) { + PSH_ERR("Input two arguments as psh msg and param\n"); + return -EINVAL; + } + + psh_dbg_msg.msg = msg; + psh_dbg_msg.param = param; + + return size; +} + +static ssize_t psh_ch_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, + "\nLast psh channel: %d\n", psh_ch); +} + +static ssize_t psh_ch_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + + ret = sscanf(buf, "%d", &psh_ch); + if (ret != 1) { + PSH_ERR("Input one argument as psh channel\n"); + return -EINVAL; + } + + return size; +} + +static ssize_t psh_send_cmd_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int psh_dbg_err; + struct psh_msg out_msg; + + memset(&out_msg, 0, sizeof(out_msg)); + + psh_dbg_err = intel_ia2psh_command(&psh_dbg_msg, &out_msg, + psh_ch, 3000000); + if (psh_dbg_err) { + PSH_ERR("Send ia2psh command failed, err %d\n", psh_dbg_err); + psh_regs_dump(); + return psh_dbg_err; + } + + return size; +} + +static DEVICE_ATTR(psh_msg, S_IRUGO | S_IWUSR, psh_msg_show, psh_msg_store); +static DEVICE_ATTR(psh_ch, S_IRUGO | S_IWUSR, psh_ch_show, psh_ch_store); +static DEVICE_ATTR(ia2psh_cmd, S_IWUSR, NULL, psh_send_cmd_store); + +static struct attribute *psh_attrs[] = { + &dev_attr_psh_msg.attr, + &dev_attr_psh_ch.attr, + &dev_attr_ia2psh_cmd.attr, + NULL, +}; + +static struct attribute_group psh_attr_group = { + .name = "psh_debug", + .attrs = psh_attrs, +}; + +static int intel_psh_debug_sysfs_create(struct pci_dev *pdev) +{ + return sysfs_create_group(&pdev->dev.kobj, &psh_attr_group); +} + +static void pmic_sysfs_remove(struct pci_dev *pdev) +{ + sysfs_remove_group(&pdev->dev.kobj, &psh_attr_group); +} + +#ifdef CONFIG_PM +static int psh_ipc_suspend_noirq(struct device *dev) +{ + int i; + int ret = 0; + + for (i = 0; i < NUM_ALL_CH; i++) { + if (down_trylock(&ipc_ctrl.ch_lock[i])) { + ret = -EBUSY; + break; + } + } + + if (ret) { + for (; i > 0; i--) + up(&ipc_ctrl.ch_lock[i - 1]); + } + + return ret; +} + +static int psh_ipc_resume_noirq(struct device *dev) +{ + int i; + + for (i = 0; i < NUM_ALL_CH; i++) + up(&ipc_ctrl.ch_lock[i]); + + return 0; +} + +#else + +#define psh_ipc_suspend_noirq NULL +#define psh_ipc_resume_noirq NULL + +#endif + +#ifdef CONFIG_PM_RUNTIME +static int psh_ipc_runtime_suspend(struct device *dev) +{ + dev_dbg(dev, "runtime suspend called\n"); + return 0; +} + +static int psh_ipc_runtime_resume(struct device *dev) +{ + dev_dbg(dev, "runtime resume called\n"); + return 0; +} + +#else + +#define psh_ipc_runtime_suspend NULL +#define psh_ipc_runtime_resume NULL + +#endif + +static int psh_ipc_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int i, ret; + unsigned long start, len; + + ipc_ctrl.pdev = pci_dev_get(pdev); + ret = pci_enable_device(pdev); + if (ret) + goto err1; + + start = pci_resource_start(pdev, 0); + len = pci_resource_len(pdev, 0); + if (!start || !len) { + ret = -ENODEV; + goto err1; + } + + ret = pci_request_regions(pdev, "intel_psh_ipc"); + if (ret) + goto err1; + + switch (intel_mid_identify_cpu()) { + case INTEL_MID_CPU_CHIP_TANGIER: + ipc_ctrl.reg_map = 1; + break; + case INTEL_MID_CPU_CHIP_ANNIEDALE: + ipc_ctrl.reg_map = 1; + break; + default: + dev_err(&pdev->dev, "error register map\n"); + ret = -EINVAL; + goto err2; + break; + } + + ipc_ctrl.psh_regs = (union psh_registers *)ioremap_nocache(start, len); + if (!ipc_ctrl.psh_regs) { + ret = -ENOMEM; + goto err2; + } + + ret = request_threaded_irq(pdev->irq, NULL, psh_ipc_irq, IRQF_ONESHOT, + "intel_psh_ipc", NULL); + if (ret) { + dev_err(&pdev->dev, "Unable to register irq %d\n", pdev->irq); + goto err3; + } + + irq_set_irq_wake(pdev->irq, 1); + + spin_lock_init(&ipc_ctrl.lock); + mutex_init(&ipc_ctrl.psh_mutex); + + for (i = 0; i < NUM_ALL_CH; i++) + sema_init(&ipc_ctrl.ch_lock[i], 1); + + intel_psh_devices_create(); + + intel_psh_debug_sysfs_create(pdev); + + ipc_ctrl.initialized = 1; + + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_allow(&pdev->dev); + + return 0; + +err3: + iounmap(ipc_ctrl.psh_regs); +err2: + pci_release_regions(pdev); +err1: + pci_dev_put(pdev); + + return ret; +} + +static void psh_ipc_remove(struct pci_dev *pdev) +{ + pm_runtime_forbid(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + free_irq(pdev->irq, NULL); + iounmap(ipc_ctrl.psh_regs); + pci_release_regions(pdev); + pci_dev_put(pdev); + intel_psh_devices_destroy(); + pmic_sysfs_remove(pdev); + ipc_ctrl.initialized = 0; +} + +static const struct dev_pm_ops psh_ipc_drv_pm_ops = { + .suspend_noirq = psh_ipc_suspend_noirq, + .resume_noirq = psh_ipc_resume_noirq, + .runtime_suspend = psh_ipc_runtime_suspend, + .runtime_resume = psh_ipc_runtime_resume, +}; + +static DEFINE_PCI_DEVICE_TABLE(pci_ids) = { + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x11a3)}, + { 0,} +}; +MODULE_DEVICE_TABLE(pci, pci_ids); + +static struct pci_driver psh_ipc_driver = { + .name = "intel_psh_ipc", + .driver = { + .pm = &psh_ipc_drv_pm_ops, + }, + .id_table = pci_ids, + .probe = psh_ipc_probe, + .remove = psh_ipc_remove, +}; + +static int __init psh_ipc_init(void) +{ + return pci_register_driver(&psh_ipc_driver); +} + +static void __exit psh_ipc_exit(void) +{ + pci_unregister_driver(&psh_ipc_driver); +} + +MODULE_AUTHOR("bin.yang@intel.com"); +MODULE_DESCRIPTION("Intel PSH IPC driver"); +MODULE_LICENSE("GPL v2"); + +fs_initcall(psh_ipc_init); +module_exit(psh_ipc_exit); diff --git a/drivers/pwm/pwm-intel-mid.c b/drivers/pwm/pwm-intel-mid.c index 5564a1b4a9c8..7897607240b5 100644 --- a/drivers/pwm/pwm-intel-mid.c +++ b/drivers/pwm/pwm-intel-mid.c @@ -74,15 +74,9 @@ intel_mid_pwm_on_time_divisor(void __iomem *reg, union pwmctrl_reg *pwmctrl, u64 on_time_divisor; /* Calculate and set on time divisor */ - if (duty_cycle == period) { - on_time_divisor = 1UL; - } else { - on_time_divisor = duty_cycle * - (u64)(PWM_COMPARE_UNIT_SIZE - 1UL); - do_div(on_time_divisor, period); - on_time_divisor = PWM_COMPARE_UNIT_SIZE - - on_time_divisor - 1UL; - } + on_time_divisor = duty_cycle * (u64)(PWM_COMPARE_UNIT_SIZE - 1UL); + do_div(on_time_divisor, period); + on_time_divisor = PWM_COMPARE_UNIT_SIZE - on_time_divisor - 1UL; pwmctrl->full = readl(reg); pwmctrl->part.on_time_divisor = on_time_divisor; @@ -167,6 +161,11 @@ intel_mid_pwm_base_unit(void __iomem *reg, union pwmctrl_reg *pwmctrl, fraction -= numerator; } } + /* If both the values are 0, the output will be somehow not correct. + * So if it happens, change the fraction to 1. + */ + if ((0 == base_unit_fraction) && (0 == base_unit_integer)) + base_unit_fraction = 1UL; pwmctrl->full = readl(reg); pwmctrl->part.base_unit_int = (u32)base_unit_integer; diff --git a/drivers/remoteproc/intel_mid_rproc_scu.c b/drivers/remoteproc/intel_mid_rproc_scu.c index fd5023bcbdb7..53a1cdc0e5a3 100644 --- a/drivers/remoteproc/intel_mid_rproc_scu.c +++ b/drivers/remoteproc/intel_mid_rproc_scu.c @@ -257,6 +257,7 @@ static void intel_rproc_scu_kick(struct rproc *rproc, int vqid) struct intel_mid_rproc *iproc; struct rproc_vdev *rvdev; struct device *dev = rproc->dev.parent; + static unsigned long ns_info_all_received; iproc = (struct intel_mid_rproc *)rproc->priv; @@ -268,19 +269,21 @@ static void intel_rproc_scu_kick(struct rproc *rproc, int vqid) switch (idx) { case RX_VRING: - if (iproc->ns_enabled && - !list_is_last(&iproc->ns_info->node, &nslist->list)) { + if (iproc->ns_enabled && !ns_info_all_received) { + /* push messages with ns_info for ALL available + name services in the list (nslist) into + rx buffers. */ list_for_each_entry_continue(iproc->ns_info, &nslist->list, node) { ret = intel_mid_rproc_ns_handle(iproc, - iproc->ns_info); + iproc->ns_info); if (ret) { dev_err(dev, "ns handle error\n"); return; } - break; } + ns_info_all_received = 1; intel_mid_rproc_vq_interrupt(rproc, vqid); } break; diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c index 3bb9401f1cae..771812d62e6b 100644 --- a/drivers/rtc/rtc-max77686.c +++ b/drivers/rtc/rtc-max77686.c @@ -240,9 +240,9 @@ static int max77686_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) } alrm->pending = 0; - ret = regmap_read(info->max77686->regmap, MAX77686_REG_STATUS2, &val); + ret = regmap_read(info->max77686->regmap, MAX77686_REG_STATUS1, &val); if (ret < 0) { - dev_err(info->dev, "%s:%d fail to read status2 reg(%d)\n", + dev_err(info->dev, "%s:%d fail to read status1 reg(%d)\n", __func__, __LINE__, ret); goto out; } diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 8e8f3533d2a1..4133ab6e20f1 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -102,13 +102,10 @@ static void zfcp_erp_action_dismiss_port(struct zfcp_port *port) if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_INUSE) zfcp_erp_action_dismiss(&port->erp_action); - else { - spin_lock(port->adapter->scsi_host->host_lock); - __shost_for_each_device(sdev, port->adapter->scsi_host) + else + shost_for_each_device(sdev, port->adapter->scsi_host) if (sdev_to_zfcp(sdev)->port == port) zfcp_erp_action_dismiss_lun(sdev); - spin_unlock(port->adapter->scsi_host->host_lock); - } } static void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter) @@ -595,11 +592,9 @@ static void _zfcp_erp_lun_reopen_all(struct zfcp_port *port, int clear, { struct scsi_device *sdev; - spin_lock(port->adapter->scsi_host->host_lock); - __shost_for_each_device(sdev, port->adapter->scsi_host) + shost_for_each_device(sdev, port->adapter->scsi_host) if (sdev_to_zfcp(sdev)->port == port) _zfcp_erp_lun_reopen(sdev, clear, id, 0); - spin_unlock(port->adapter->scsi_host->host_lock); } static void zfcp_erp_strategy_followup_failed(struct zfcp_erp_action *act) @@ -1440,10 +1435,8 @@ void zfcp_erp_set_adapter_status(struct zfcp_adapter *adapter, u32 mask) atomic_set_mask(common_mask, &port->status); read_unlock_irqrestore(&adapter->port_list_lock, flags); - spin_lock_irqsave(adapter->scsi_host->host_lock, flags); - __shost_for_each_device(sdev, adapter->scsi_host) + shost_for_each_device(sdev, adapter->scsi_host) atomic_set_mask(common_mask, &sdev_to_zfcp(sdev)->status); - spin_unlock_irqrestore(adapter->scsi_host->host_lock, flags); } /** @@ -1477,13 +1470,11 @@ void zfcp_erp_clear_adapter_status(struct zfcp_adapter *adapter, u32 mask) } read_unlock_irqrestore(&adapter->port_list_lock, flags); - spin_lock_irqsave(adapter->scsi_host->host_lock, flags); - __shost_for_each_device(sdev, adapter->scsi_host) { + shost_for_each_device(sdev, adapter->scsi_host) { atomic_clear_mask(common_mask, &sdev_to_zfcp(sdev)->status); if (clear_counter) atomic_set(&sdev_to_zfcp(sdev)->erp_counter, 0); } - spin_unlock_irqrestore(adapter->scsi_host->host_lock, flags); } /** @@ -1497,19 +1488,16 @@ void zfcp_erp_set_port_status(struct zfcp_port *port, u32 mask) { struct scsi_device *sdev; u32 common_mask = mask & ZFCP_COMMON_FLAGS; - unsigned long flags; atomic_set_mask(mask, &port->status); if (!common_mask) return; - spin_lock_irqsave(port->adapter->scsi_host->host_lock, flags); - __shost_for_each_device(sdev, port->adapter->scsi_host) + shost_for_each_device(sdev, port->adapter->scsi_host) if (sdev_to_zfcp(sdev)->port == port) atomic_set_mask(common_mask, &sdev_to_zfcp(sdev)->status); - spin_unlock_irqrestore(port->adapter->scsi_host->host_lock, flags); } /** @@ -1524,7 +1512,6 @@ void zfcp_erp_clear_port_status(struct zfcp_port *port, u32 mask) struct scsi_device *sdev; u32 common_mask = mask & ZFCP_COMMON_FLAGS; u32 clear_counter = mask & ZFCP_STATUS_COMMON_ERP_FAILED; - unsigned long flags; atomic_clear_mask(mask, &port->status); @@ -1534,15 +1521,13 @@ void zfcp_erp_clear_port_status(struct zfcp_port *port, u32 mask) if (clear_counter) atomic_set(&port->erp_counter, 0); - spin_lock_irqsave(port->adapter->scsi_host->host_lock, flags); - __shost_for_each_device(sdev, port->adapter->scsi_host) + shost_for_each_device(sdev, port->adapter->scsi_host) if (sdev_to_zfcp(sdev)->port == port) { atomic_clear_mask(common_mask, &sdev_to_zfcp(sdev)->status); if (clear_counter) atomic_set(&sdev_to_zfcp(sdev)->erp_counter, 0); } - spin_unlock_irqrestore(port->adapter->scsi_host->host_lock, flags); } /** diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index de0598eaacd2..665e3cfaaf85 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -224,9 +224,11 @@ int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req, static int zfcp_qdio_sbal_check(struct zfcp_qdio *qdio) { + spin_lock_irq(&qdio->req_q_lock); if (atomic_read(&qdio->req_q_free) || !(atomic_read(&qdio->adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP)) return 1; + spin_unlock_irq(&qdio->req_q_lock); return 0; } @@ -244,8 +246,9 @@ int zfcp_qdio_sbal_get(struct zfcp_qdio *qdio) { long ret; - ret = wait_event_interruptible_lock_irq_timeout(qdio->req_q_wq, - zfcp_qdio_sbal_check(qdio), qdio->req_q_lock, 5 * HZ); + spin_unlock_irq(&qdio->req_q_lock); + ret = wait_event_interruptible_timeout(qdio->req_q_wq, + zfcp_qdio_sbal_check(qdio), 5 * HZ); if (!(atomic_read(&qdio->adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP)) return -EIO; @@ -259,6 +262,7 @@ int zfcp_qdio_sbal_get(struct zfcp_qdio *qdio) zfcp_erp_adapter_reopen(qdio->adapter, 0, "qdsbg_1"); } + spin_lock_irq(&qdio->req_q_lock); return -EIO; } diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 1348fa47d127..86af29f53bbe 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1353,6 +1353,7 @@ config SCSI_LPFC tristate "Emulex LightPulse Fibre Channel Support" depends on PCI && SCSI select SCSI_FC_ATTRS + select GENERIC_CSUM select CRC_T10DIF help This lpfc driver supports the Emulex LightPulse diff --git a/drivers/scsi/esp_scsi.c b/drivers/scsi/esp_scsi.c index 55548dc5cec3..34552bf1c023 100644 --- a/drivers/scsi/esp_scsi.c +++ b/drivers/scsi/esp_scsi.c @@ -530,7 +530,7 @@ static int esp_need_to_nego_sync(struct esp_target_data *tp) static int esp_alloc_lun_tag(struct esp_cmd_entry *ent, struct esp_lun_data *lp) { - if (!ent->orig_tag[0]) { + if (!ent->tag[0]) { /* Non-tagged, slot already taken? */ if (lp->non_tagged_cmd) return -EBUSY; @@ -564,9 +564,9 @@ static int esp_alloc_lun_tag(struct esp_cmd_entry *ent, return -EBUSY; } - BUG_ON(lp->tagged_cmds[ent->orig_tag[1]]); + BUG_ON(lp->tagged_cmds[ent->tag[1]]); - lp->tagged_cmds[ent->orig_tag[1]] = ent; + lp->tagged_cmds[ent->tag[1]] = ent; lp->num_tagged++; return 0; @@ -575,9 +575,9 @@ static int esp_alloc_lun_tag(struct esp_cmd_entry *ent, static void esp_free_lun_tag(struct esp_cmd_entry *ent, struct esp_lun_data *lp) { - if (ent->orig_tag[0]) { - BUG_ON(lp->tagged_cmds[ent->orig_tag[1]] != ent); - lp->tagged_cmds[ent->orig_tag[1]] = NULL; + if (ent->tag[0]) { + BUG_ON(lp->tagged_cmds[ent->tag[1]] != ent); + lp->tagged_cmds[ent->tag[1]] = NULL; lp->num_tagged--; } else { BUG_ON(lp->non_tagged_cmd != ent); @@ -667,8 +667,6 @@ static struct esp_cmd_entry *find_and_prep_issuable_command(struct esp *esp) ent->tag[0] = 0; ent->tag[1] = 0; } - ent->orig_tag[0] = ent->tag[0]; - ent->orig_tag[1] = ent->tag[1]; if (esp_alloc_lun_tag(ent, lp) < 0) continue; diff --git a/drivers/scsi/esp_scsi.h b/drivers/scsi/esp_scsi.h index cd68805e8d78..28e22acf87ea 100644 --- a/drivers/scsi/esp_scsi.h +++ b/drivers/scsi/esp_scsi.h @@ -271,7 +271,6 @@ struct esp_cmd_entry { #define ESP_CMD_FLAG_AUTOSENSE 0x04 /* Doing automatic REQUEST_SENSE */ u8 tag[2]; - u8 orig_tag[2]; u8 status; u8 message; diff --git a/drivers/scsi/mpt3sas/Makefile b/drivers/scsi/mpt3sas/Makefile index efb0c4c2e310..4c1d2e7a1176 100644 --- a/drivers/scsi/mpt3sas/Makefile +++ b/drivers/scsi/mpt3sas/Makefile @@ -1,5 +1,5 @@ # mpt3sas makefile -obj-$(CONFIG_SCSI_MPT3SAS) += mpt3sas.o +obj-m += mpt3sas.o mpt3sas-y += mpt3sas_base.o \ mpt3sas_config.o \ mpt3sas_scsih.o \ diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c index ce3f129d39bf..69dd49c05f1e 100644 --- a/drivers/scsi/pm8001/pm8001_hwi.c +++ b/drivers/scsi/pm8001/pm8001_hwi.c @@ -221,7 +221,7 @@ static void init_default_table_values(struct pm8001_hba_info *pm8001_ha) pm8001_ha->main_cfg_tbl.pm8001_tbl.fatal_err_interrupt = 0x01; for (i = 0; i < PM8001_MAX_INB_NUM; i++) { pm8001_ha->inbnd_q_tbl[i].element_pri_size_cnt = - PM8001_MPI_QUEUE | (pm8001_ha->iomb_size << 16) | (0x00<<30); + PM8001_MPI_QUEUE | (64 << 16) | (0x00<<30); pm8001_ha->inbnd_q_tbl[i].upper_base_addr = pm8001_ha->memoryMap.region[IB + i].phys_addr_hi; pm8001_ha->inbnd_q_tbl[i].lower_base_addr = @@ -247,7 +247,7 @@ static void init_default_table_values(struct pm8001_hba_info *pm8001_ha) } for (i = 0; i < PM8001_MAX_OUTB_NUM; i++) { pm8001_ha->outbnd_q_tbl[i].element_size_cnt = - PM8001_MPI_QUEUE | (pm8001_ha->iomb_size << 16) | (0x01<<30); + PM8001_MPI_QUEUE | (64 << 16) | (0x01<<30); pm8001_ha->outbnd_q_tbl[i].upper_base_addr = pm8001_ha->memoryMap.region[OB + i].phys_addr_hi; pm8001_ha->outbnd_q_tbl[i].lower_base_addr = diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c b/drivers/scsi/pm8001/pm80xx_hwi.c index e1c48961ceac..302514d8157b 100644 --- a/drivers/scsi/pm8001/pm80xx_hwi.c +++ b/drivers/scsi/pm8001/pm80xx_hwi.c @@ -275,7 +275,7 @@ static void init_default_table_values(struct pm8001_hba_info *pm8001_ha) for (i = 0; i < PM8001_MAX_SPCV_INB_NUM; i++) { pm8001_ha->inbnd_q_tbl[i].element_pri_size_cnt = - PM8001_MPI_QUEUE | (pm8001_ha->iomb_size << 16) | (0x00<<30); + PM8001_MPI_QUEUE | (64 << 16) | (0x00<<30); pm8001_ha->inbnd_q_tbl[i].upper_base_addr = pm8001_ha->memoryMap.region[IB + i].phys_addr_hi; pm8001_ha->inbnd_q_tbl[i].lower_base_addr = @@ -301,7 +301,7 @@ static void init_default_table_values(struct pm8001_hba_info *pm8001_ha) } for (i = 0; i < PM8001_MAX_SPCV_OUTB_NUM; i++) { pm8001_ha->outbnd_q_tbl[i].element_size_cnt = - PM8001_MPI_QUEUE | (pm8001_ha->iomb_size << 16) | (0x01<<30); + PM8001_MPI_QUEUE | (64 << 16) | (0x01<<30); pm8001_ha->outbnd_q_tbl[i].upper_base_addr = pm8001_ha->memoryMap.region[OB + i].phys_addr_hi; pm8001_ha->outbnd_q_tbl[i].lower_base_addr = diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index c39863441337..610417ec45af 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -2409,9 +2409,14 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer) } } - sd_printk(KERN_ERR, sdkp, "No Caching mode page found\n"); - goto defaults; - + if (modepage == 0x3F) { + sd_printk(KERN_ERR, sdkp, "No Caching mode page " + "present\n"); + goto defaults; + } else if ((buffer[offset] & 0x3f) != modepage) { + sd_printk(KERN_ERR, sdkp, "Got wrong page\n"); + goto defaults; + } Page_found: if (modepage == 8) { sdkp->WCE = ((buffer[offset + 2] & 0x04) != 0); diff --git a/drivers/spi/intel_mid_ssp_spi.c b/drivers/spi/intel_mid_ssp_spi.c index 938180e49e08..d790620e3358 100644 --- a/drivers/spi/intel_mid_ssp_spi.c +++ b/drivers/spi/intel_mid_ssp_spi.c @@ -728,7 +728,7 @@ static void int_transfer_complete(struct ssp_drv_context *sspc) #endif if (sspc->cs_control) - sspc->cs_control(CS_DEASSERT); + sspc->cs_control(!sspc->cs_assert); dev_dbg(dev, "End of transfer. SSSR:%08X\n", read_SSSR(reg)); msg = sspc->cur_msg; @@ -906,9 +906,9 @@ static void start_bitbanging(struct ssp_drv_context *sspc) static unsigned int ssp_get_clk_div(struct ssp_drv_context *sspc, int speed) { - /* The clock divider shall stay between 3 and 4095 as specified in TRM. */ if (sspc->quirks & QUIRKS_PLATFORM_MRFL) - return clamp(25000000 / speed - 1, 3, 4095); + /* The clock divider shall stay between 0 and 4095. */ + return clamp(25000000 / speed - 1, 0, 4095); else return clamp(100000000 / speed - 1, 3, 4095); } @@ -951,7 +951,7 @@ static int handle_message(struct ssp_drv_context *sspc) u32 cr0, saved_cr0, cr1, saved_cr1; struct device *dev = &sspc->pdev->dev; struct spi_message *msg = sspc->cur_msg; - u32 clk_div, saved_speed_hz; + u32 clk_div, saved_speed_hz, speed_hz; u8 dma_enabled; u32 timeout; u8 chip_select; @@ -1123,9 +1123,11 @@ static int handle_message(struct ssp_drv_context *sspc) /* recalculate the frequency for each transfer */ if (transfer->speed_hz) - clk_div = ssp_get_clk_div(sspc, transfer->speed_hz); + speed_hz = transfer->speed_hz; else - clk_div = ssp_get_clk_div(sspc, saved_speed_hz); + speed_hz = saved_speed_hz; + + clk_div = ssp_get_clk_div(sspc, speed_hz); cr0 &= ~SSCR0_SCR; cr0 |= (clk_div & 0xFFF) << 8; @@ -1136,6 +1138,13 @@ static int handle_message(struct ssp_drv_context *sspc) (sspc->quirks & QUIRKS_BIT_BANGING))) { start_bitbanging(sspc); } else { + + /* if speed is higher than 6.25Mhz, enable clock delay */ + if (speed_hz > 6250000) + write_SSCR2((read_SSCR2(reg) | SSCR2_CLK_DEL_EN), reg); + else + write_SSCR2((read_SSCR2(reg) & ~SSCR2_CLK_DEL_EN), reg); + /* (re)start the SSP */ if (ssp_timing_wr) { dev_dbg(dev, "original cr0 before reset:%x", @@ -1153,7 +1162,7 @@ static int handle_message(struct ssp_drv_context *sspc) } if (sspc->cs_control) - sspc->cs_control(CS_ASSERT); + sspc->cs_control(sspc->cs_assert); if (likely(dma_enabled)) { if (unlikely(sspc->quirks & QUIRKS_USE_PM_QOS)) @@ -1168,7 +1177,7 @@ static int handle_message(struct ssp_drv_context *sspc) if (list_is_last(&transfer->transfer_list, &msg->transfers) || sspc->cs_change) { if (sspc->cs_control) - sspc->cs_control(CS_DEASSERT); + sspc->cs_control(!sspc->cs_assert); } } /* end of list_for_each_entry */ @@ -1207,7 +1216,8 @@ static void pump_messages(struct work_struct *work) sspc->cur_msg = NULL; } spin_unlock_irqrestore(&sspc->lock, flags); - pm_runtime_put(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); } /** @@ -1258,6 +1268,26 @@ static int setup(struct spi_device *spi) /* chip_info isn't always needed */ chip->cr1 = 0; if (chip_info) { + /* If user requested CS Active High need to verify that there + * is no transfer pending. If this is the case, kindly fail. */ + if ((spi->mode & SPI_CS_HIGH) != sspc->cs_assert) { + if (sspc->cur_msg) { + dev_err(&spi->dev, "message pending... Failing\n"); + /* A message is currently in transfer. Do not toggle CS */ + spin_unlock_irqrestore(&sspc->lock, flags); + return -EAGAIN; + } + if (!chip_info->cs_control) { + /* unable to control cs by hand */ + dev_err(&spi->dev, + "This CS does not support SPI_CS_HIGH flag\n"); + spin_unlock_irqrestore(&sspc->lock, flags); + return -EINVAL; + } + sspc->cs_assert = spi->mode & SPI_CS_HIGH; + chip_info->cs_control(!sspc->cs_assert); + } + burst_size = chip_info->burst_size; if (burst_size > IMSS_FIFO_BURST_8) burst_size = DFLT_FIFO_BURST_SIZE; @@ -1457,7 +1487,7 @@ static int intel_mid_ssp_spi_probe(struct pci_dev *pdev, if (ssp_cfg_is_spi_slave(ssp_cfg)) sspc->quirks |= QUIRKS_SPI_SLAVE_CLOCK_MODE; - master->mode_bits = SPI_CPOL | SPI_CPHA; + master->mode_bits = SPI_CS_HIGH | SPI_CPOL | SPI_CPHA; master->bus_num = ssp_cfg_get_spi_bus_nb(ssp_cfg); master->num_chipselect = 4; master->cleanup = cleanup; @@ -1558,6 +1588,12 @@ static int intel_mid_ssp_spi_probe(struct pci_dev *pdev, pm_qos_add_request(&sspc->pm_qos_req, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); + pm_runtime_set_autosuspend_delay(&pdev->dev, 25); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) + dev_err(&pdev->dev, "spi runtime pm not enabled!\n"); pm_runtime_put_noidle(&pdev->dev); pm_runtime_allow(&pdev->dev); diff --git a/drivers/staging/comedi/drivers.c b/drivers/staging/comedi/drivers.c index 4a2b04277304..06d190f8fd34 100644 --- a/drivers/staging/comedi/drivers.c +++ b/drivers/staging/comedi/drivers.c @@ -464,7 +464,7 @@ int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it) ret = comedi_device_postconfig(dev); if (ret < 0) { comedi_device_detach(dev); - module_put(driv->module); + module_put(dev->driver->module); } /* On success, the driver module count has been incremented. */ return ret; diff --git a/drivers/staging/comedi/drivers/dt282x.c b/drivers/staging/comedi/drivers/dt282x.c index f4c1e998cbe9..90f2de9bc402 100644 --- a/drivers/staging/comedi/drivers/dt282x.c +++ b/drivers/staging/comedi/drivers/dt282x.c @@ -269,9 +269,8 @@ struct dt282x_private { } \ udelay(5); \ } \ - if (_i) { \ + if (_i) \ b \ - } \ } while (0) static int prep_ai_dma(struct comedi_device *dev, int chan, int size); diff --git a/drivers/staging/comedi/drivers/ni_65xx.c b/drivers/staging/comedi/drivers/ni_65xx.c index 05eb6fefb745..3f71f0f54d3c 100644 --- a/drivers/staging/comedi/drivers/ni_65xx.c +++ b/drivers/staging/comedi/drivers/ni_65xx.c @@ -383,23 +383,28 @@ static int ni_65xx_dio_insn_bits(struct comedi_device *dev, { const struct ni_65xx_board *board = comedi_board(dev); struct ni_65xx_private *devpriv = dev->private; - int base_bitfield_channel; + unsigned base_bitfield_channel; + const unsigned max_ports_per_bitfield = 5; unsigned read_bits = 0; - int last_port_offset = ni_65xx_port_by_channel(s->n_chan - 1); - int port_offset; + unsigned j; base_bitfield_channel = CR_CHAN(insn->chanspec); - for (port_offset = ni_65xx_port_by_channel(base_bitfield_channel); - port_offset <= last_port_offset; port_offset++) { - unsigned port = sprivate(s)->base_port + port_offset; - int base_port_channel = port_offset * ni_65xx_channels_per_port; + for (j = 0; j < max_ports_per_bitfield; ++j) { + const unsigned port_offset = + ni_65xx_port_by_channel(base_bitfield_channel) + j; + const unsigned port = + sprivate(s)->base_port + port_offset; + unsigned base_port_channel; unsigned port_mask, port_data, port_read_bits; - int bitshift = base_port_channel - base_bitfield_channel; - - if (bitshift >= 32) + int bitshift; + if (port >= ni_65xx_total_num_ports(board)) break; + base_port_channel = port_offset * ni_65xx_channels_per_port; port_mask = data[0]; port_data = data[1]; + bitshift = base_port_channel - base_bitfield_channel; + if (bitshift >= 32 || bitshift <= -32) + break; if (bitshift > 0) { port_mask >>= bitshift; port_data >>= bitshift; diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c index 972a0723afac..163c638e4095 100644 --- a/drivers/staging/iio/adc/mxs-lradc.c +++ b/drivers/staging/iio/adc/mxs-lradc.c @@ -234,6 +234,7 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev, { struct mxs_lradc *lradc = iio_priv(iio_dev); int ret; + unsigned long mask; if (m != IIO_CHAN_INFO_RAW) return -EINVAL; @@ -242,6 +243,12 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev, if (chan->channel > LRADC_MAX_TOTAL_CHANS) return -EINVAL; + /* Validate the channel if it doesn't intersect with reserved chans. */ + bitmap_set(&mask, chan->channel, 1); + ret = iio_validate_scan_mask_onehot(iio_dev, &mask); + if (ret) + return -EINVAL; + /* * See if there is no buffered operation in progess. If there is, simply * bail out. This can be improved to support both buffered and raw IO at @@ -654,13 +661,12 @@ static int mxs_lradc_trigger_init(struct iio_dev *iio) { int ret; struct iio_trigger *trig; - struct mxs_lradc *lradc = iio_priv(iio); trig = iio_trigger_alloc("%s-dev%i", iio->name, iio->id); if (trig == NULL) return -ENOMEM; - trig->dev.parent = lradc->dev; + trig->dev.parent = iio->dev.parent; iio_trigger_set_drvdata(trig, iio); trig->ops = &mxs_lradc_trigger_ops; @@ -670,17 +676,15 @@ static int mxs_lradc_trigger_init(struct iio_dev *iio) return ret; } - lradc->trig = trig; + iio->trig = trig; return 0; } static void mxs_lradc_trigger_remove(struct iio_dev *iio) { - struct mxs_lradc *lradc = iio_priv(iio); - - iio_trigger_unregister(lradc->trig); - iio_trigger_free(lradc->trig); + iio_trigger_unregister(iio->trig); + iio_trigger_free(iio->trig); } static int mxs_lradc_buffer_preenable(struct iio_dev *iio) diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c index a532ca568526..64553058b67e 100644 --- a/drivers/staging/imx-drm/imx-drm-core.c +++ b/drivers/staging/imx-drm/imx-drm-core.c @@ -681,7 +681,6 @@ int imx_drm_encoder_get_mux_id(struct imx_drm_encoder *imx_drm_encoder, return i; } -EXPORT_SYMBOL_GPL(imx_drm_encoder_get_mux_id); /* * imx_drm_remove_encoder - remove an encoder diff --git a/drivers/staging/vt6656/iwctl.c b/drivers/staging/vt6656/iwctl.c index 8872e0f84f40..d0cf7d8a20e5 100644 --- a/drivers/staging/vt6656/iwctl.c +++ b/drivers/staging/vt6656/iwctl.c @@ -1634,9 +1634,6 @@ int iwctl_siwencodeext(struct net_device *dev, struct iw_request_info *info, if (pMgmt == NULL) return -EFAULT; - if (!(pDevice->flags & DEVICE_FLAGS_OPENED)) - return -ENODEV; - buf = kzalloc(sizeof(struct viawget_wpa_param), GFP_KERNEL); if (buf == NULL) return -ENOMEM; diff --git a/drivers/staging/vt6656/main_usb.c b/drivers/staging/vt6656/main_usb.c index 06b966cf5bdd..3a3fdc58b6da 100644 --- a/drivers/staging/vt6656/main_usb.c +++ b/drivers/staging/vt6656/main_usb.c @@ -1099,8 +1099,6 @@ static int device_close(struct net_device *dev) memset(pMgmt->abyCurrBSSID, 0, 6); pMgmt->eCurrState = WMAC_STATE_IDLE; - pDevice->flags &= ~DEVICE_FLAGS_OPENED; - device_free_tx_bufs(pDevice); device_free_rx_bufs(pDevice); device_free_int_bufs(pDevice); @@ -1112,6 +1110,7 @@ static int device_close(struct net_device *dev) usb_free_urb(pDevice->pInterruptURB); BSSvClearNodeDBTable(pDevice, 0); + pDevice->flags &=(~DEVICE_FLAGS_OPENED); DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "device_close2 \n"); diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 4c1b8dbdcb36..012ff8bab0f8 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -1086,6 +1086,7 @@ int iscsit_process_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, if (cmd->reject_reason) return 0; + target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd); return 1; } /* @@ -1123,10 +1124,14 @@ iscsit_get_immediate_data(struct iscsi_cmd *cmd, struct iscsi_scsi_req *hdr, */ cmdsn_ret = iscsit_sequence_cmd(cmd->conn, cmd, (unsigned char *)hdr, hdr->cmdsn); - if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) + if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) { return -1; + } else if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) { + target_put_sess_cmd(conn->sess->se_sess, &cmd->se_cmd); + return 0; + } - if (cmd->sense_reason || cmdsn_ret == CMDSN_LOWER_THAN_EXP) { + if (cmd->sense_reason) { int rc; rc = iscsit_dump_data_payload(cmd->conn, @@ -1536,10 +1541,6 @@ int iscsit_handle_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd, if (hdr->itt == RESERVED_ITT && !(hdr->opcode & ISCSI_OP_IMMEDIATE)) { pr_err("NOPOUT ITT is reserved, but Immediate Bit is" " not set, protocol error.\n"); - if (!cmd) - return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR, - (unsigned char *)hdr); - return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR, (unsigned char *)hdr); } @@ -1549,10 +1550,6 @@ int iscsit_handle_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd, " greater than MaxXmitDataSegmentLength: %u, protocol" " error.\n", payload_length, conn->conn_ops->MaxXmitDataSegmentLength); - if (!cmd) - return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR, - (unsigned char *)hdr); - return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR, (unsigned char *)hdr); } diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index bc788c52b6cc..3402241be87c 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -1163,11 +1163,12 @@ static int __iscsi_target_login_thread(struct iscsi_np *np) if (np->np_thread_state == ISCSI_NP_THREAD_RESET) { spin_unlock_bh(&np->np_thread_lock); complete(&np->np_restart_comp); - iscsit_put_transport(conn->conn_transport); - kfree(conn); - conn = NULL; - if (ret == -ENODEV) + if (ret == -ENODEV) { + iscsit_put_transport(conn->conn_transport); + kfree(conn); + conn = NULL; goto out; + } /* Get another socket */ return 1; } diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index 77dad2474c8c..96e7fdbba9fc 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -734,7 +734,7 @@ void iscsit_free_cmd(struct iscsi_cmd *cmd, bool shutdown) * Fallthrough */ case ISCSI_OP_SCSI_TMFUNC: - rc = transport_generic_free_cmd(&cmd->se_cmd, shutdown); + rc = transport_generic_free_cmd(&cmd->se_cmd, 1); if (!rc && shutdown && se_cmd && se_cmd->se_sess) { __iscsit_free_cmd(cmd, true, shutdown); target_put_sess_cmd(se_cmd->se_sess, se_cmd); @@ -750,7 +750,7 @@ void iscsit_free_cmd(struct iscsi_cmd *cmd, bool shutdown) se_cmd = &cmd->se_cmd; __iscsit_free_cmd(cmd, true, shutdown); - rc = transport_generic_free_cmd(&cmd->se_cmd, shutdown); + rc = transport_generic_free_cmd(&cmd->se_cmd, 1); if (!rc && shutdown && se_cmd->se_sess) { __iscsit_free_cmd(cmd, true, shutdown); target_put_sess_cmd(se_cmd->se_sess, se_cmd); diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c index f608fbc14a27..cbe48ab41745 100644 --- a/drivers/target/target_core_alua.c +++ b/drivers/target/target_core_alua.c @@ -730,7 +730,7 @@ static int core_alua_write_tpg_metadata( if (ret < 0) pr_err("Error writing ALUA metadata file: %s\n", path); fput(file); - return (ret < 0) ? -EIO : 0; + return ret ? -EIO : 0; } /* diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 04a74938bb43..3240f2cc81ef 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -1987,7 +1987,7 @@ static int __core_scsi3_write_aptpl_to_file( pr_debug("Error writing APTPL metadata file: %s\n", path); fput(file); - return (ret < 0) ? -EIO : 0; + return ret ? -EIO : 0; } static int diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c index 9fabbf7214cd..4cb667d720a7 100644 --- a/drivers/target/target_core_spc.c +++ b/drivers/target/target_core_spc.c @@ -97,12 +97,9 @@ spc_emulate_inquiry_std(struct se_cmd *cmd, unsigned char *buf) buf[7] = 0x2; /* CmdQue=1 */ - memcpy(&buf[8], "LIO-ORG ", 8); - memset(&buf[16], 0x20, 16); - memcpy(&buf[16], dev->t10_wwn.model, - min_t(size_t, strlen(dev->t10_wwn.model), 16)); - memcpy(&buf[32], dev->t10_wwn.revision, - min_t(size_t, strlen(dev->t10_wwn.revision), 4)); + snprintf(&buf[8], 8, "LIO-ORG"); + snprintf(&buf[16], 16, "%s", dev->t10_wwn.model); + snprintf(&buf[32], 4, "%s", dev->t10_wwn.revision); buf[4] = 31; /* Set additional length to 31 */ return 0; diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c index 4fc32c8091e9..682210d778bd 100644 --- a/drivers/tty/hvc/hvc_xen.c +++ b/drivers/tty/hvc/hvc_xen.c @@ -636,7 +636,6 @@ struct console xenboot_console = { .name = "xenboot", .write = xenboot_write_console, .flags = CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME, - .index = -1, }; #endif /* CONFIG_EARLY_PRINTK */ diff --git a/drivers/tty/hvc/hvsi_lib.c b/drivers/tty/hvc/hvsi_lib.c index ac2767100df5..3396eb9d57a3 100644 --- a/drivers/tty/hvc/hvsi_lib.c +++ b/drivers/tty/hvc/hvsi_lib.c @@ -341,8 +341,8 @@ void hvsilib_establish(struct hvsi_priv *pv) pr_devel("HVSI@%x: ... waiting handshake\n", pv->termno); - /* Try for up to 400ms */ - for (timeout = 0; timeout < 40; timeout++) { + /* Try for up to 200s */ + for (timeout = 0; timeout < 20; timeout++) { if (pv->established) goto established; if (!hvsi_get_packet(pv)) diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 2c5a3e4a17db..572d48189de9 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -232,7 +232,7 @@ struct eg20t_port { unsigned int iobase; struct pci_dev *pdev; int fifo_size; - unsigned int uartclk; + int uartclk; int start_tx; int start_rx; int tx_empty; @@ -373,62 +373,35 @@ static const struct file_operations port_regs_ops = { }; #endif /* CONFIG_DEBUG_FS */ -static struct dmi_system_id pch_uart_dmi_table[] = { - { - .ident = "CM-iTC", - { - DMI_MATCH(DMI_BOARD_NAME, "CM-iTC"), - }, - (void *)CMITC_UARTCLK, - }, - { - .ident = "FRI2", - { - DMI_MATCH(DMI_BIOS_VERSION, "FRI2"), - }, - (void *)FRI2_64_UARTCLK, - }, - { - .ident = "Fish River Island II", - { - DMI_MATCH(DMI_PRODUCT_NAME, "Fish River Island II"), - }, - (void *)FRI2_48_UARTCLK, - }, - { - .ident = "COMe-mTT", - { - DMI_MATCH(DMI_BOARD_NAME, "COMe-mTT"), - }, - (void *)NTC1_UARTCLK, - }, - { - .ident = "nanoETXexpress-TT", - { - DMI_MATCH(DMI_BOARD_NAME, "nanoETXexpress-TT"), - }, - (void *)NTC1_UARTCLK, - }, - { - .ident = "MinnowBoard", - { - DMI_MATCH(DMI_BOARD_NAME, "MinnowBoard"), - }, - (void *)MINNOW_UARTCLK, - }, -}; - /* Return UART clock, checking for board specific clocks. */ -static unsigned int pch_uart_get_uartclk(void) +static int pch_uart_get_uartclk(void) { - const struct dmi_system_id *d; + const char *cmp; if (user_uartclk) return user_uartclk; - d = dmi_first_match(pch_uart_dmi_table); - if (d) - return (unsigned long)d->driver_data; + cmp = dmi_get_system_info(DMI_BOARD_NAME); + if (cmp && strstr(cmp, "CM-iTC")) + return CMITC_UARTCLK; + + cmp = dmi_get_system_info(DMI_BIOS_VERSION); + if (cmp && strnstr(cmp, "FRI2", 4)) + return FRI2_64_UARTCLK; + + cmp = dmi_get_system_info(DMI_PRODUCT_NAME); + if (cmp && strstr(cmp, "Fish River Island II")) + return FRI2_48_UARTCLK; + + /* Kontron COMe-mTT10 (nanoETXexpress-TT) */ + cmp = dmi_get_system_info(DMI_BOARD_NAME); + if (cmp && (strstr(cmp, "COMe-mTT") || + strstr(cmp, "nanoETXexpress-TT"))) + return NTC1_UARTCLK; + + cmp = dmi_get_system_info(DMI_BOARD_NAME); + if (cmp && strstr(cmp, "MinnowBoard")) + return MINNOW_UARTCLK; return DEFAULT_UARTCLK; } @@ -449,7 +422,7 @@ static void pch_uart_hal_disable_interrupt(struct eg20t_port *priv, iowrite8(ier, priv->membase + UART_IER); } -static int pch_uart_hal_set_line(struct eg20t_port *priv, unsigned int baud, +static int pch_uart_hal_set_line(struct eg20t_port *priv, int baud, unsigned int parity, unsigned int bits, unsigned int stb) { @@ -484,7 +457,7 @@ static int pch_uart_hal_set_line(struct eg20t_port *priv, unsigned int baud, lcr |= bits; lcr |= stb; - dev_dbg(priv->port.dev, "%s:baud = %u, div = %04x, lcr = %02x (%lu)\n", + dev_dbg(priv->port.dev, "%s:baud = %d, div = %04x, lcr = %02x (%lu)\n", __func__, baud, div, lcr, jiffies); iowrite8(PCH_UART_LCR_DLAB, priv->membase + UART_LCR); iowrite8(dll, priv->membase + PCH_UART_DLL); @@ -685,12 +658,11 @@ static int dma_push_rx(struct eg20t_port *priv, int size) dev_warn(port->dev, "Rx overrun: dropping %u bytes\n", size - room); if (!room) - goto out; + return room; tty_insert_flip_string(tport, sg_virt(&priv->sg_rx), size); port->icount.rx += room; -out: tty_kref_put(tty); return room; @@ -1099,8 +1071,6 @@ static void pch_uart_err_ir(struct eg20t_port *priv, unsigned int lsr) if (tty == NULL) { for (i = 0; error_msg[i] != NULL; i++) dev_err(&priv->pdev->dev, error_msg[i]); - } else { - tty_kref_put(tty); } } @@ -1393,8 +1363,9 @@ static void pch_uart_shutdown(struct uart_port *port) static void pch_uart_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { + int baud; int rtn; - unsigned int baud, parity, bits, stb; + unsigned int parity, bits, stb; struct eg20t_port *priv; unsigned long flags; @@ -1527,7 +1498,6 @@ static int pch_uart_verify_port(struct uart_port *port, return 0; } -#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_PCH_UART_CONSOLE) /* * Wait for transmitter & holding register to empty */ @@ -1558,7 +1528,6 @@ static void wait_for_xmitr(struct eg20t_port *up, int bits) } } } -#endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_PCH_UART_CONSOLE */ #ifdef CONFIG_CONSOLE_POLL /* diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 357a8370ce2a..9799d043a9bd 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -726,7 +726,7 @@ static irqreturn_t tegra_uart_isr(int irq, void *data) static void tegra_uart_stop_rx(struct uart_port *u) { struct tegra_uart_port *tup = to_tegra_uport(u); - struct tty_struct *tty; + struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port); struct tty_port *port = &u->state->port; struct dma_tx_state state; unsigned long ier; @@ -738,8 +738,6 @@ static void tegra_uart_stop_rx(struct uart_port *u) if (!tup->rx_in_progress) return; - tty = tty_port_tty_get(&tup->uport.state->port); - tegra_uart_wait_sym_time(tup, 1); /* wait a character interval */ ier = tup->ier_shadow; diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 59d26ef538d8..447668213900 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -850,8 +850,7 @@ void disassociate_ctty(int on_exit) struct pid *tty_pgrp = tty_get_pgrp(tty); if (tty_pgrp) { kill_pgrp(tty_pgrp, SIGHUP, on_exit); - if (!on_exit) - kill_pgrp(tty_pgrp, SIGCONT, on_exit); + kill_pgrp(tty_pgrp, SIGCONT, on_exit); put_pid(tty_pgrp); } } diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 088b4ca7d805..3500d4114147 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -1201,9 +1201,6 @@ int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, } return 0; case TCFLSH: - retval = tty_check_change(tty); - if (retval) - return retval; return __tty_perform_flush(tty, arg); default: /* Try the mode commands */ diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index d3318a0df8ee..8a230f0ef77c 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -209,7 +209,6 @@ static void wdm_in_callback(struct urb *urb) static void wdm_int_callback(struct urb *urb) { int rv = 0; - int responding; int status = urb->status; struct wdm_device *desc; struct usb_cdc_notification *dr; @@ -263,8 +262,8 @@ static void wdm_int_callback(struct urb *urb) spin_lock(&desc->iuspin); clear_bit(WDM_READ, &desc->flags); - responding = test_and_set_bit(WDM_RESPONDING, &desc->flags); - if (!responding && !test_bit(WDM_DISCONNECTING, &desc->flags) + set_bit(WDM_RESPONDING, &desc->flags); + if (!test_bit(WDM_DISCONNECTING, &desc->flags) && !test_bit(WDM_SUSPENDING, &desc->flags)) { rv = usb_submit_urb(desc->response, GFP_ATOMIC); dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d", @@ -686,20 +685,16 @@ static void wdm_rxwork(struct work_struct *work) { struct wdm_device *desc = container_of(work, struct wdm_device, rxwork); unsigned long flags; - int rv = 0; - int responding; + int rv; spin_lock_irqsave(&desc->iuspin, flags); if (test_bit(WDM_DISCONNECTING, &desc->flags)) { spin_unlock_irqrestore(&desc->iuspin, flags); } else { - responding = test_and_set_bit(WDM_RESPONDING, &desc->flags); spin_unlock_irqrestore(&desc->iuspin, flags); - if (!responding) - rv = usb_submit_urb(desc->response, GFP_KERNEL); + rv = usb_submit_urb(desc->response, GFP_KERNEL); if (rv < 0 && rv != -EPERM) { spin_lock_irqsave(&desc->iuspin, flags); - clear_bit(WDM_RESPONDING, &desc->flags); if (!test_bit(WDM_DISCONNECTING, &desc->flags)) schedule_work(&desc->rxwork); spin_unlock_irqrestore(&desc->iuspin, flags); diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index a6b2cabe7930..7199adccf444 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -424,8 +424,7 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx, memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE); if (config->desc.bDescriptorType != USB_DT_CONFIG || - config->desc.bLength < USB_DT_CONFIG_SIZE || - config->desc.bLength > size) { + config->desc.bLength < USB_DT_CONFIG_SIZE) { dev_err(ddev, "invalid descriptor for config index %d: " "type = 0x%X, length = %d\n", cfgidx, config->desc.bDescriptorType, config->desc.bLength); diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index ce773cca2bf5..c88c4fb9459d 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -742,22 +742,6 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, if ((index & ~USB_DIR_IN) == 0) return 0; ret = findintfep(ps->dev, index); - if (ret < 0) { - /* - * Some not fully compliant Win apps seem to get - * index wrong and have the endpoint number here - * rather than the endpoint address (with the - * correct direction). Win does let this through, - * so we'll not reject it here but leave it to - * the device to not break KVM. But we warn. - */ - ret = findintfep(ps->dev, index ^ 0x80); - if (ret >= 0) - dev_info(&ps->dev->dev, - "%s: process %i (%s) requesting ep %02x but needs %02x\n", - __func__, task_pid_nr(current), - current->comm, index, index ^ 0x80); - } if (ret >= 0) ret = checkintf(ps, ret); break; diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 6cf2ae0aa1f7..da2905a1a18b 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2916,6 +2916,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) { struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent); struct usb_port *port_dev = hub->ports[udev->portnum - 1]; + enum pm_qos_flags_status pm_qos_stat; int port1 = udev->portnum; int status; bool really_suspend = true; @@ -2953,7 +2954,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) status); /* bail if autosuspend is requested */ if (PMSG_IS_AUTO(msg)) - goto err_wakeup; + return status; } } @@ -2962,16 +2963,14 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) usb_set_usb2_hardware_lpm(udev, 0); if (usb_disable_ltm(udev)) { - dev_err(&udev->dev, "Failed to disable LTM before suspend\n."); - status = -ENOMEM; - if (PMSG_IS_AUTO(msg)) - goto err_ltm; + dev_err(&udev->dev, "%s Failed to disable LTM before suspend\n.", + __func__); + return -ENOMEM; } if (usb_unlocked_disable_lpm(udev)) { - dev_err(&udev->dev, "Failed to disable LPM before suspend\n."); - status = -ENOMEM; - if (PMSG_IS_AUTO(msg)) - goto err_lpm3; + dev_err(&udev->dev, "%s Failed to disable LPM before suspend\n.", + __func__); + return -ENOMEM; } /* see 7.1.7.6 */ @@ -2999,31 +2998,28 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) if (status) { dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n", port1, status); - - /* Try to enable USB3 LPM and LTM again */ - usb_unlocked_enable_lpm(udev); - err_lpm3: - usb_enable_ltm(udev); - err_ltm: - /* Try to enable USB2 hardware LPM again */ - if (udev->usb2_hw_lpm_capable == 1) - usb_set_usb2_hardware_lpm(udev, 1); - + /* paranoia: "should not happen" */ if (udev->do_remote_wakeup) { - if (udev->speed < USB_SPEED_SUPER) - usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + if (!hub_is_superspeed(hub->hdev)) { + (void) usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, USB_DEVICE_REMOTE_WAKEUP, 0, - NULL, 0, USB_CTRL_SET_TIMEOUT); - else - usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - USB_REQ_CLEAR_FEATURE, - USB_RECIP_INTERFACE, - USB_INTRF_FUNC_SUSPEND, 0, - NULL, 0, USB_CTRL_SET_TIMEOUT); + NULL, 0, + USB_CTRL_SET_TIMEOUT); + } else + (void) usb_disable_function_remotewakeup(udev); + } - err_wakeup: + + /* Try to enable USB2 hardware LPM again */ + if (udev->usb2_hw_lpm_capable == 1) + usb_set_usb2_hardware_lpm(udev, 1); + + /* Try to enable USB3 LTM and LPM again */ + usb_enable_ltm(udev); + usb_unlocked_enable_lpm(udev); /* System sleep transitions should never fail */ if (!PMSG_IS_AUTO(msg)) @@ -3041,7 +3037,16 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) usb_set_device_state(udev, USB_STATE_SUSPENDED); } - if (status == 0 && !udev->do_remote_wakeup && udev->persist_enabled) { + /* + * Check whether current status meets the requirement of + * usb port power off mechanism + */ + pm_qos_stat = dev_pm_qos_flags(&port_dev->dev, + PM_QOS_FLAG_NO_POWER_OFF); + if (!udev->do_remote_wakeup + && pm_qos_stat != PM_QOS_FLAGS_ALL + && udev->persist_enabled + && !status) { pm_runtime_put_sync(&port_dev->dev); port_dev->did_runtime_put = true; } diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index ef07b3596d06..b8bad294eeb8 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -89,19 +89,22 @@ static int usb_port_runtime_resume(struct device *dev) retval = usb_hub_set_port_power(hdev, port1, true); if (port_dev->child && !retval) { /* - * Attempt to wait for usb hub port to be reconnected in order - * to make the resume procedure successful. The device may have - * disconnected while the port was powered off, so ignore the - * return status. + * Wait for usb hub port to be reconnected in order to make + * the resume procedure successful. */ retval = hub_port_debounce_be_connected(hub, port1); - if (retval < 0) + if (retval < 0) { dev_dbg(&port_dev->dev, "can't get reconnection after setting port power on, status %d\n", retval); + goto out; + } usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE); + + /* Set return value to 0 if debounce successful */ retval = 0; } +out: clear_bit(port1, hub->busy_bits); usb_autopm_put_interface(intf); return retval; diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 2a367986c59b..18fa8e4d0571 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -48,8 +48,6 @@ /* FIXME define these in */ #define PCI_VENDOR_ID_SYNOPSYS 0x16c3 #define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd -#define PCI_DEVICE_ID_INTEL_BYT 0x0f37 -#define PCI_DEVICE_ID_INTEL_MRFLD 0x119e struct dwc3_pci { struct device *dev; @@ -214,8 +212,6 @@ static DEFINE_PCI_DEVICE_TABLE(dwc3_pci_id_table) = { #define PCI_DEVICE_ID_INTEL_DWC_TNG 0x119e PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DWC_TNG), }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT), }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_MRFLD), }, { } /* Terminating Entry */ }; MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table); diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index d5418acc8da7..c08daeb937dc 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1832,12 +1832,28 @@ static void dwc3_gadget_disable_irq(struct dwc3 *dwc) static irqreturn_t dwc3_interrupt(int irq, void *_dwc); static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc); -static int dwc3_init_for_enumeration(struct dwc3 *dwc) +static int dwc3_gadget_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) { + struct dwc3 *dwc = gadget_to_dwc(g); struct dwc3_ep *dep; + unsigned long flags; int ret = 0; + int irq; u32 reg; + spin_lock_irqsave(&dwc->lock, flags); + + if (dwc->gadget_driver) { + dev_err(dwc->dev, "%s is already bound to %s\n", + dwc->gadget.name, + dwc->gadget_driver->driver.name); + ret = -EBUSY; + goto err0; + } + + dwc->gadget_driver = driver; + reg = dwc3_readl(dwc->regs, DWC3_DCFG); reg &= ~(DWC3_DCFG_SPEED_MASK); @@ -1870,92 +1886,42 @@ static int dwc3_init_for_enumeration(struct dwc3 *dwc) ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); - return ret; + goto err0; } dep = dwc->eps[1]; ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); - goto err0; + goto err1; } /* begin to receive SETUP packets */ dwc->ep0state = EP0_SETUP_PHASE; dwc3_ep0_out_start(dwc); - dwc3_gadget_enable_irq(dwc); - - return 0; -err0: - __dwc3_gadget_ep_disable(dwc->eps[0]); - - return ret; -} - -static int dwc3_gadget_start(struct usb_gadget *g, - struct usb_gadget_driver *driver) -{ - struct dwc3 *dwc = gadget_to_dwc(g); - unsigned long flags; - int ret = 0; - int irq = 0; - struct usb_phy *usb_phy; - - if (dwc->is_otg) { - usb_phy = usb_get_phy(USB_PHY_TYPE_USB2); - if (!usb_phy) { - dev_err(dwc->dev, "OTG driver not available\n"); - ret = -ENODEV; - goto err0; - } - - otg_set_peripheral(usb_phy->otg, &dwc->gadget); - usb_put_phy(usb_phy); - } else { - irq = platform_get_irq(to_platform_device(dwc->dev), 0); - ret = request_threaded_irq(irq, dwc3_interrupt, - dwc3_thread_interrupt, IRQF_SHARED, - "dwc3", dwc); - if (ret) { - dev_err(dwc->dev, "failed to request irq #%d --> %d\n", - irq, ret); - goto err0; - } - } - - spin_lock_irqsave(&dwc->lock, flags); - - if (dwc->gadget_driver) { - dev_err(dwc->dev, "%s is already bound to %s\n", - dwc->gadget.name, - dwc->gadget_driver->driver.name); - ret = -EBUSY; + irq = platform_get_irq(to_platform_device(dwc->dev), 0); + ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt, + IRQF_SHARED | IRQF_ONESHOT, "dwc3", dwc); + if (ret) { + dev_err(dwc->dev, "failed to request irq #%d --> %d\n", + irq, ret); goto err1; } - dwc->gadget_driver = driver; - - if (!dwc->is_otg) { - ret = dwc3_init_for_enumeration(dwc); - if (ret) - goto err2; - } + dwc3_gadget_enable_irq(dwc); spin_unlock_irqrestore(&dwc->lock, flags); return 0; -err2: - dwc->gadget_driver = NULL; - err1: - spin_unlock_irqrestore(&dwc->lock, flags); - - if (!dwc->is_otg) - free_irq(irq, dwc); + __dwc3_gadget_ep_disable(dwc->eps[0]); err0: + dwc->gadget_driver = NULL; + spin_unlock_irqrestore(&dwc->lock, flags); + return ret; } @@ -1969,6 +1935,9 @@ static int dwc3_gadget_stop(struct usb_gadget *g, spin_lock_irqsave(&dwc->lock, flags); dwc3_gadget_disable_irq(dwc); + irq = platform_get_irq(to_platform_device(dwc->dev), 0); + free_irq(irq, dwc); + __dwc3_gadget_ep_disable(dwc->eps[0]); __dwc3_gadget_ep_disable(dwc->eps[1]); @@ -1976,9 +1945,6 @@ static int dwc3_gadget_stop(struct usb_gadget *g, spin_unlock_irqrestore(&dwc->lock, flags); - irq = platform_get_irq(to_platform_device(dwc->dev), 0); - free_irq(irq, dwc); - return 0; } diff --git a/drivers/usb/dwc3/gadget.c.rej b/drivers/usb/dwc3/gadget.c.rej new file mode 100644 index 000000000000..00db088a1486 --- /dev/null +++ b/drivers/usb/dwc3/gadget.c.rej @@ -0,0 +1,137 @@ +--- drivers/usb/dwc3/gadget.c ++++ drivers/usb/dwc3/gadget.c +@@ -1832,37 +2166,12 @@ static void dwc3_gadget_disable_irq(struct dwc3 *dwc) + static irqreturn_t dwc3_interrupt(int irq, void *_dwc); + static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc); + +-static int dwc3_gadget_start(struct usb_gadget *g, +- struct usb_gadget_driver *driver) ++static int dwc3_init_for_enumeration(struct dwc3 *dwc) + { +- struct dwc3 *dwc = gadget_to_dwc(g); + struct dwc3_ep *dep; +- unsigned long flags; + int ret = 0; +- int irq; + u32 reg; + +- irq = platform_get_irq(to_platform_device(dwc->dev), 0); +- ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt, +- IRQF_SHARED | IRQF_ONESHOT, "dwc3", dwc); +- if (ret) { +- dev_err(dwc->dev, "failed to request irq #%d --> %d\n", +- irq, ret); +- goto err0; +- } +- +- spin_lock_irqsave(&dwc->lock, flags); +- +- if (dwc->gadget_driver) { +- dev_err(dwc->dev, "%s is already bound to %s\n", +- dwc->gadget.name, +- dwc->gadget_driver->driver.name); +- ret = -EBUSY; +- goto err1; +- } +- +- dwc->gadget_driver = driver; +- + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~(DWC3_DCFG_SPEED_MASK); + +@@ -1895,14 +2205,14 @@ static int dwc3_gadget_start(struct usb_gadget *g, + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false); + if (ret) { + dev_err(dwc->dev, "failed to enable %s\n", dep->name); +- goto err2; ++ return ret; + } + + dep = dwc->eps[1]; + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false); + if (ret) { + dev_err(dwc->dev, "failed to enable %s\n", dep->name); +- goto err3; ++ goto err0; + } + + /* begin to receive SETUP packets */ +@@ -1911,20 +2221,74 @@ static int dwc3_gadget_start(struct usb_gadget *g, + + dwc3_gadget_enable_irq(dwc); + ++ return 0; ++err0: ++ __dwc3_gadget_ep_disable(dwc->eps[0]); ++ ++ return ret; ++} ++ ++static int dwc3_gadget_start(struct usb_gadget *g, ++ struct usb_gadget_driver *driver) ++{ ++ struct dwc3 *dwc = gadget_to_dwc(g); ++ unsigned long flags; ++ int ret = 0; ++ int irq = 0; ++ struct usb_phy *usb_phy; ++ ++ if (dwc->is_otg) { ++ usb_phy = usb_get_phy(USB_PHY_TYPE_USB2); ++ if (!usb_phy) { ++ dev_err(dwc->dev, "OTG driver not available\n"); ++ ret = -ENODEV; ++ goto err0; ++ } ++ ++ otg_set_peripheral(usb_phy->otg, &dwc->gadget); ++ usb_put_phy(usb_phy); ++ } else { ++ irq = platform_get_irq(to_platform_device(dwc->dev), 0); ++ ret = request_threaded_irq(irq, dwc3_interrupt, ++ dwc3_thread_interrupt, IRQF_SHARED, ++ "dwc3", dwc); ++ if (ret) { ++ dev_err(dwc->dev, "failed to request irq #%d --> %d\n", ++ irq, ret); ++ goto err0; ++ } ++ } ++ ++ spin_lock_irqsave(&dwc->lock, flags); ++ ++ if (dwc->gadget_driver) { ++ dev_err(dwc->dev, "%s is already bound to %s\n", ++ dwc->gadget.name, ++ dwc->gadget_driver->driver.name); ++ ret = -EBUSY; ++ goto err1; ++ } ++ ++ dwc->gadget_driver = driver; ++ ++ if (!dwc->is_otg) { ++ ret = dwc3_init_for_enumeration(dwc); ++ if (ret) ++ goto err2; ++ } ++ + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; + +-err3: +- __dwc3_gadget_ep_disable(dwc->eps[0]); +- + err2: + dwc->gadget_driver = NULL; + + err1: + spin_unlock_irqrestore(&dwc->lock, flags); + +- free_irq(irq, dwc); ++ if (!dwc->is_otg) ++ free_irq(irq, dwc); + + err0: + return ret; diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index ac0e79e2c2e9..c588e8e486e5 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -923,9 +923,8 @@ static int dummy_udc_stop(struct usb_gadget *g, struct dummy_hcd *dum_hcd = gadget_to_dummy_hcd(g); struct dummy *dum = dum_hcd->dum; - if (driver) - dev_dbg(udc_dev(dum), "unregister gadget driver '%s'\n", - driver->driver.name); + dev_dbg(udc_dev(dum), "unregister gadget driver '%s'\n", + driver->driver.name); dum->driver = NULL; @@ -1001,8 +1000,8 @@ static int dummy_udc_remove(struct platform_device *pdev) { struct dummy *dum = platform_get_drvdata(pdev); - device_remove_file(&dum->gadget.dev, &dev_attr_function); usb_del_gadget_udc(&dum->gadget); + device_remove_file(&dum->gadget.dev, &dev_attr_function); return 0; } diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index ab1065afbbd0..4b7e33e5d9c6 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -285,7 +285,6 @@ static struct usb_string acm_string_defs[] = { [ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)", [ACM_DATA_IDX].s = "CDC ACM Data", [ACM_IAD_IDX ].s = "CDC Serial", - { } /* end of list */ }; static struct usb_gadget_strings acm_string_table = { diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index b6e9d917221e..f394f295d63d 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -1034,19 +1034,37 @@ struct ffs_sb_fill_data { struct ffs_file_perms perms; umode_t root_mode; const char *dev_name; - struct ffs_data *ffs_data; + union { + /* set by ffs_fs_mount(), read by ffs_sb_fill() */ + void *private_data; + /* set by ffs_sb_fill(), read by ffs_fs_mount */ + struct ffs_data *ffs_data; + }; }; static int ffs_sb_fill(struct super_block *sb, void *_data, int silent) { struct ffs_sb_fill_data *data = _data; struct inode *inode; - struct ffs_data *ffs = data->ffs_data; + struct ffs_data *ffs; ENTER(); + /* Initialise data */ + ffs = ffs_data_new(); + if (unlikely(!ffs)) + goto Enomem; + ffs->sb = sb; - data->ffs_data = NULL; + ffs->dev_name = kstrdup(data->dev_name, GFP_KERNEL); + if (unlikely(!ffs->dev_name)) + goto Enomem; + ffs->file_perms = data->perms; + ffs->private_data = data->private_data; + + /* used by the caller of this function */ + data->ffs_data = ffs; + sb->s_fs_info = ffs; sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; @@ -1062,14 +1080,17 @@ static int ffs_sb_fill(struct super_block *sb, void *_data, int silent) &data->perms); sb->s_root = d_make_root(inode); if (unlikely(!sb->s_root)) - return -ENOMEM; + goto Enomem; /* EP0 file */ if (unlikely(!ffs_sb_create_file(sb, "ep0", ffs, &ffs_ep0_operations, NULL))) - return -ENOMEM; + goto Enomem; return 0; + +Enomem: + return -ENOMEM; } static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts) @@ -1172,7 +1193,6 @@ ffs_fs_mount(struct file_system_type *t, int flags, struct dentry *rv; int ret; void *ffs_dev; - struct ffs_data *ffs; ENTER(); @@ -1180,30 +1200,18 @@ ffs_fs_mount(struct file_system_type *t, int flags, if (unlikely(ret < 0)) return ERR_PTR(ret); - ffs = ffs_data_new(); - if (unlikely(!ffs)) - return ERR_PTR(-ENOMEM); - ffs->file_perms = data.perms; - - ffs->dev_name = kstrdup(dev_name, GFP_KERNEL); - if (unlikely(!ffs->dev_name)) { - ffs_data_put(ffs); - return ERR_PTR(-ENOMEM); - } - ffs_dev = functionfs_acquire_dev_callback(dev_name); - if (IS_ERR(ffs_dev)) { - ffs_data_put(ffs); - return ERR_CAST(ffs_dev); - } - ffs->private_data = ffs_dev; - data.ffs_data = ffs; + if (IS_ERR(ffs_dev)) + return ffs_dev; + data.dev_name = dev_name; + data.private_data = ffs_dev; rv = mount_nodev(t, flags, &data, ffs_sb_fill); - if (IS_ERR(rv) && data.ffs_data) { + + /* data.ffs_data is set by ffs_sb_fill */ + if (IS_ERR(rv)) functionfs_release_dev_callback(data.ffs_data); - ffs_data_put(data.ffs_data); - } + return rv; } diff --git a/drivers/usb/gadget/hid.c.1 b/drivers/usb/gadget/hid.c.1 new file mode 100644 index 000000000000..7aadf6619b5f --- /dev/null +++ b/drivers/usb/gadget/hid.c.1 @@ -0,0 +1,341 @@ +/* + * hid.c -- HID Composite driver + * + * Based on multi.c + * + * Copyright (C) 2010 Fabien Chouteau + * + * 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 +#include +#include +#include +#include + +#include "gadget_chips.h" +#define DRIVER_DESC "HID Gadget" +#define DRIVER_VERSION "2010/03/16" + +/*-------------------------------------------------------------------------*/ + +#define HIDG_VENDOR_NUM 0x0525 /* XXX NetChip */ +#define HIDG_PRODUCT_NUM 0xa4ac /* Linux-USB HID gadget */ + +/*-------------------------------------------------------------------------*/ + +/* + * kbuild is not very cooperative with respect to linking separately + * compiled library objects into one module. So for now we won't use + * separate compilation ... ensuring init/exit sections work to shrink + * the runtime footprint, and giving us at least some parts of what + * a "gcc --combine ... part1.c part2.c part3.c ... " build would. + */ +#include "f_hid.c" + + +struct hidg_func_node { + struct list_head node; + struct hidg_func_descriptor *func; +}; + +static LIST_HEAD(hidg_func_list); + +/*-------------------------------------------------------------------------*/ +USB_GADGET_COMPOSITE_OPTIONS(); + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = cpu_to_le16(0x0200), + + /* .bDeviceClass = USB_CLASS_COMM, */ + /* .bDeviceSubClass = 0, */ + /* .bDeviceProtocol = 0, */ + .bDeviceClass = USB_CLASS_PER_INTERFACE, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + /* .bMaxPacketSize0 = f(hardware) */ + + /* Vendor and product id can be overridden by module parameters. */ + .idVendor = cpu_to_le16(HIDG_VENDOR_NUM), + .idProduct = cpu_to_le16(HIDG_PRODUCT_NUM), + /* .bcdDevice = f(hardware) */ + /* .iManufacturer = DYNAMIC */ + /* .iProduct = DYNAMIC */ + /* NO SERIAL NUMBER */ + .bNumConfigurations = 1, +}; + +static struct usb_otg_descriptor otg_descriptor = { + .bLength = sizeof otg_descriptor, + .bDescriptorType = USB_DT_OTG, + + /* REVISIT SRP-only hardware is possible, although + * it would not be called "OTG" ... + */ + .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, +}; + +static const struct usb_descriptor_header *otg_desc[] = { + (struct usb_descriptor_header *) &otg_descriptor, + NULL, +}; + + +/* string IDs are assigned dynamically */ +static struct usb_string strings_dev[] = { + [USB_GADGET_MANUFACTURER_IDX].s = "", + [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, + [USB_GADGET_SERIAL_IDX].s = "", + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, +}; + +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, +}; + + + +/****************************** Configurations ******************************/ + +static int __init do_config(struct usb_configuration *c) +{ + struct hidg_func_node *e; + int func = 0, status = 0; + + if (gadget_is_otg(c->cdev->gadget)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + list_for_each_entry(e, &hidg_func_list, node) { + status = hidg_bind_config(c, e->func, func++); + if (status) + break; + } + + return status; +} + +static struct usb_configuration config_driver = { + .label = "HID Gadget", + .bConfigurationValue = 1, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, +}; + +/****************************** Gadget Bind ******************************/ + +static int __init hid_bind(struct usb_composite_dev *cdev) +{ + struct usb_gadget *gadget = cdev->gadget; + struct list_head *tmp; + int status, funcs = 0; + + list_for_each(tmp, &hidg_func_list) + funcs++; + + if (!funcs) + return -ENODEV; + + /* set up HID */ + status = ghid_setup(cdev->gadget, funcs); + if (status < 0) + return status; + + /* Allocate string descriptor numbers ... note that string + * contents can be overridden by the composite_dev glue. + */ + + status = usb_string_ids_tab(cdev, strings_dev); + if (status < 0) + return status; + device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; + device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; + + /* register our configuration */ + status = usb_add_config(cdev, &config_driver, do_config); + if (status < 0) + return status; + + usb_composite_overwrite_options(cdev, &coverwrite); + dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); + + return 0; +} + +static int __exit hid_unbind(struct usb_composite_dev *cdev) +{ + ghid_cleanup(); + return 0; +} + +static int __init hidg_plat_driver_probe(struct platform_device *pdev) +{ + struct hidg_func_descriptor *func = dev_get_platdata(&pdev->dev); + struct hidg_func_node *entry; + + if (!func) { + dev_err(&pdev->dev, "Platform data missing\n"); + return -ENODEV; + } + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->func = func; + list_add_tail(&entry->node, &hidg_func_list); + + return 0; +} + +static int hidg_plat_driver_remove(struct platform_device *pdev) +{ + struct hidg_func_node *e, *n; + + list_for_each_entry_safe(e, n, &hidg_func_list, node) { + list_del(&e->node); + kfree(e); + } + + return 0; +} + + +/****************************** Some noise ******************************/ + + +static __refdata struct usb_composite_driver hidg_driver = { + .name = "g_hid", + .dev = &device_desc, + .strings = dev_strings, + .max_speed = USB_SPEED_HIGH, + .bind = hid_bind, + .unbind = __exit_p(hid_unbind), +}; + +static struct platform_driver hidg_plat_driver = { + .remove = hidg_plat_driver_remove, + .driver = { + .owner = THIS_MODULE, + .name = "hidg", + }, +}; + +/* This is a quick and dirty hack to demonstrate the gadget hid driver. + Normally the platform_device descriptor would be part of the board + support for the device, e.g. in arch/arm/mach-omap2/devices.c, + but for convenience I am putting it in to the g_hid module to make + easier for testing. + + The structures my_hid_data and my_hid are straight from + Documentation/usb/gadget_hid.txt + */ + +/* hid descriptor for a keyboard */ +static struct hidg_func_descriptor my_hid_data = { + .subclass = 0, /* No subclass */ + .protocol = 1, /* Keyboard */ + .report_length = 8, + .report_desc_length = 63, + .report_desc = { + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ + 0x09, 0x06, /* USAGE (Keyboard) */ + 0xa1, 0x01, /* COLLECTION (Application) */ + 0x05, 0x07, /* USAGE_PAGE (Keyboard) */ + 0x19, 0xe0, /* USAGE_MINIMUM (Keyboard LeftControl) */ + 0x29, 0xe7, /* USAGE_MAXIMUM (Keyboard Right GUI) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x95, 0x08, /* REPORT_COUNT (8) */ + 0x81, 0x02, /* INPUT (Data,Var,Abs) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x75, 0x08, /* REPORT_SIZE (8) */ + 0x81, 0x03, /* INPUT (Cnst,Var,Abs) */ + 0x95, 0x05, /* REPORT_COUNT (5) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x05, 0x08, /* USAGE_PAGE (LEDs) */ + 0x19, 0x01, /* USAGE_MINIMUM (Num Lock) */ + 0x29, 0x05, /* USAGE_MAXIMUM (Kana) */ + 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x75, 0x03, /* REPORT_SIZE (3) */ + 0x91, 0x03, /* OUTPUT (Cnst,Var,Abs) */ + 0x95, 0x06, /* REPORT_COUNT (6) */ + 0x75, 0x08, /* REPORT_SIZE (8) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x65, /* LOGICAL_MAXIMUM (101) */ + 0x05, 0x07, /* USAGE_PAGE (Keyboard) */ + 0x19, 0x00, /* USAGE_MINIMUM (Reserved) */ + 0x29, 0x65, /* USAGE_MAXIMUM (Keyboard Application) */ + 0x81, 0x00, /* INPUT (Data,Ary,Abs) */ + 0xc0 /* END_COLLECTION */ + } +}; + +static struct platform_device my_hid = { + .name = "hidg", + .id = 0, + .num_resources = 0, + .resource = 0, + .dev.platform_data = &my_hid_data, +}; + +int __init init_my_hid(void) +{ + pr_info("g_hid: loading keyboard HID descriptor\n"); + return platform_device_register(&my_hid); +} + +/* End hack */ + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Fabien Chouteau, Peter Korsgaard"); +MODULE_LICENSE("GPL"); + +static int __init hidg_init(void) +{ + int status; + + init_my_hid(); + + status = platform_driver_probe(&hidg_plat_driver, + hidg_plat_driver_probe); + if (status < 0) { + pr_info("platform_driver_probe failed: %d\n", status); + platform_device_unregister(&my_hid); + return status; + } + + status = usb_composite_probe(&hidg_driver); + if (status < 0) { + pr_info("usb_composite_probe failed: %d\n", status); + platform_driver_unregister(&hidg_plat_driver); + platform_device_unregister(&my_hid); + } + + return status; +} +module_init(hidg_init); + +static void __exit hidg_cleanup(void) +{ + platform_driver_unregister(&hidg_plat_driver); + usb_composite_unregister(&hidg_driver); +} +module_exit(hidg_cleanup); diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/uvc_queue.c index de456a5a5e0a..7ce27e35550b 100644 --- a/drivers/usb/gadget/uvc_queue.c +++ b/drivers/usb/gadget/uvc_queue.c @@ -177,16 +177,12 @@ static int uvc_queue_buffer(struct uvc_video_queue *queue, mutex_lock(&queue->mutex); ret = vb2_qbuf(&queue->queue, buf); - if (ret < 0) - goto done; - spin_lock_irqsave(&queue->irqlock, flags); ret = (queue->flags & UVC_QUEUE_PAUSED) != 0; queue->flags &= ~UVC_QUEUE_PAUSED; spin_unlock_irqrestore(&queue->irqlock, flags); - -done: mutex_unlock(&queue->mutex); + return ret; } diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 3c0a49a298dd..3be3df233a0e 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -130,7 +130,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, } /* Enable USB controller, 83xx or 8536 */ - if (pdata->have_sysif_regs && pdata->controller_ver < FSL_USB_VER_1_6) + if (pdata->have_sysif_regs) setbits32(hcd->regs + FSL_SOC_USB_CTRL, 0x4); /* Don't need to set host mode here. It will be done by tdi_reset() */ @@ -232,9 +232,15 @@ static int ehci_fsl_setup_phy(struct usb_hcd *hcd, case FSL_USB2_PHY_ULPI: if (pdata->have_sysif_regs && pdata->controller_ver) { /* controller version 1.6 or above */ - clrbits32(non_ehci + FSL_SOC_USB_CTRL, UTMI_PHY_EN); setbits32(non_ehci + FSL_SOC_USB_CTRL, - ULPI_PHY_CLK_SEL | USB_CTRL_USB_EN); + ULPI_PHY_CLK_SEL); + /* + * Due to controller issue of PHY_CLK_VALID in ULPI + * mode, we set USB_CTRL_USB_EN before checking + * PHY_CLK_VALID, otherwise PHY_CLK_VALID doesn't work. + */ + clrsetbits_be32(non_ehci + FSL_SOC_USB_CTRL, + UTMI_PHY_EN, USB_CTRL_USB_EN); } portsc |= PORT_PTS_ULPI; break; diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index ec128bc72deb..c369767b00e2 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -184,7 +184,7 @@ static int ehci_mxc_drv_remove(struct platform_device *pdev) if (pdata && pdata->exit) pdata->exit(pdev); - if (pdata && pdata->otg) + if (pdata->otg) usb_phy_shutdown(pdata->otg); clk_disable_unprepare(priv->usbclk); diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index b74b77d0c530..d850aea1fd0b 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -403,7 +403,7 @@ static struct pci_driver ehci_pci_driver = { .remove = usb_hcd_pci_remove, .shutdown = usb_hcd_pci_shutdown, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .driver = { .pm = &usb_hcd_pci_pm_ops }, diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 865946cde765..fc627fd54116 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -231,26 +231,31 @@ static int ohci_urb_enqueue ( frame &= ~(ed->interval - 1); frame |= ed->branch; urb->start_frame = frame; - ed->last_iso = frame + ed->interval * (size - 1); } } else if (ed->type == PIPE_ISOCHRONOUS) { u16 next = ohci_frame_no(ohci) + 1; u16 frame = ed->last_iso + ed->interval; - u16 length = ed->interval * (size - 1); /* Behind the scheduling threshold? */ if (unlikely(tick_before(frame, next))) { - /* URB_ISO_ASAP: Round up to the first available slot */ + /* USB_ISO_ASAP: Round up to the first available slot */ if (urb->transfer_flags & URB_ISO_ASAP) { frame += (next - frame + ed->interval - 1) & -ed->interval; /* - * Not ASAP: Use the next slot in the stream, - * no matter what. + * Not ASAP: Use the next slot in the stream. If + * the entire URB falls before the threshold, fail. */ } else { + if (tick_before(frame + ed->interval * + (urb->number_of_packets - 1), next)) { + retval = -EXDEV; + usb_hcd_unlink_urb_from_ep(hcd, urb); + goto fail; + } + /* * Some OHCI hardware doesn't handle late TDs * correctly. After retiring them it proceeds @@ -261,16 +266,9 @@ static int ohci_urb_enqueue ( urb_priv->td_cnt = DIV_ROUND_UP( (u16) (next - frame), ed->interval); - if (urb_priv->td_cnt >= urb_priv->length) { - ++urb_priv->td_cnt; /* Mark it */ - ohci_dbg(ohci, "iso underrun %p (%u+%u < %u)\n", - urb, frame, length, - next); - } } } urb->start_frame = frame; - ed->last_iso = frame + length; } /* fill the TDs and link them to the ed; and diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index ef6782bd1fa9..951514ef446d 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -371,7 +371,7 @@ static struct pci_driver ohci_pci_driver = { .remove = usb_hcd_pci_remove, .shutdown = usb_hcd_pci_shutdown, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .driver = { .pm = &usb_hcd_pci_pm_ops }, diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index 37dc8373200a..88731b7c5f42 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -41,12 +41,8 @@ finish_urb(struct ohci_hcd *ohci, struct urb *urb, int status) __releases(ohci->lock) __acquires(ohci->lock) { - struct usb_host_endpoint *ep = urb->ep; - struct urb_priv *urb_priv; - // ASSERT (urb->hcpriv != 0); - restart: urb_free_priv (ohci, urb->hcpriv); urb->hcpriv = NULL; if (likely(status == -EINPROGRESS)) @@ -83,21 +79,6 @@ __acquires(ohci->lock) ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_IE); ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); } - - /* - * An isochronous URB that is sumitted too late won't have any TDs - * (marked by the fact that the td_cnt value is larger than the - * actual number of TDs). If the next URB on this endpoint is like - * that, give it back now. - */ - if (!list_empty(&ep->urb_list)) { - urb = list_first_entry(&ep->urb_list, struct urb, urb_list); - urb_priv = urb->hcpriv; - if (urb_priv->td_cnt > urb_priv->length) { - status = 0; - goto restart; - } - } } @@ -564,6 +545,7 @@ td_fill (struct ohci_hcd *ohci, u32 info, td->hwCBP = cpu_to_hc32 (ohci, data & 0xFFFFF000); *ohci_hwPSWp(ohci, td, 0) = cpu_to_hc16 (ohci, (data & 0x0FFF) | 0xE000); + td->ed->last_iso = info & 0xffff; } else { td->hwCBP = cpu_to_hc32 (ohci, data); } @@ -1012,7 +994,7 @@ finish_unlinks (struct ohci_hcd *ohci, u16 tick) urb_priv->td_cnt++; /* if URB is done, clean up */ - if (urb_priv->td_cnt >= urb_priv->length) { + if (urb_priv->td_cnt == urb_priv->length) { modified = completed = 1; finish_urb(ohci, urb, 0); } @@ -1102,7 +1084,7 @@ static void takeback_td(struct ohci_hcd *ohci, struct td *td) urb_priv->td_cnt++; /* If all this urb's TDs are done, call complete() */ - if (urb_priv->td_cnt >= urb_priv->length) + if (urb_priv->td_cnt == urb_priv->length) finish_urb(ohci, urb, status); /* clean schedule: unlink EDs that are no longer busy */ diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index b3e1f192f465..4c338ec03a07 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -359,9 +359,6 @@ EXPORT_SYMBOL_GPL(usb_amd_dev_put); */ void uhci_reset_hc(struct pci_dev *pdev, unsigned long base) { -#ifdef CONFIG_MIPS_MALTA - int timeout = 10; -#endif /* Turn off PIRQ enable and SMI enable. (This also turns off the * BIOS's USB Legacy Support.) Turn off all the R/WC bits too. */ @@ -375,16 +372,9 @@ void uhci_reset_hc(struct pci_dev *pdev, unsigned long base) outw(UHCI_USBCMD_HCRESET, base + UHCI_USBCMD); mb(); udelay(5); + if (inw(base + UHCI_USBCMD) & UHCI_USBCMD_HCRESET) + dev_warn(&pdev->dev, "HCRESET not completed yet!\n"); -#ifdef CONFIG_MIPS_MALTA - while (inw(base + UHCI_USBCMD) & UHCI_USBCMD_HCRESET) { - if (--timeout < 0) { - dev_warn(&pdev->dev, "HCRESET timed out!\n"); - break; - } - udelay(5); - } -#endif /* Just to be safe, disable interrupt requests and * make sure the controller is stopped. */ diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c index 0f228c46eeda..c300bd2f7d1c 100644 --- a/drivers/usb/host/uhci-pci.c +++ b/drivers/usb/host/uhci-pci.c @@ -293,7 +293,7 @@ static struct pci_driver uhci_pci_driver = { .remove = usb_hcd_pci_remove, .shutdown = uhci_shutdown, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .driver = { .pm = &usb_hcd_pci_pm_ops }, diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 6c55d88048f3..041c6ddb695c 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -71,9 +71,7 @@ static void uhci_fsbr_off(struct uhci_hcd *uhci) static void uhci_add_fsbr(struct uhci_hcd *uhci, struct urb *urb) { struct urb_priv *urbp = urb->hcpriv; -#ifdef CONFIG_MIPS_MALTA - return; -#endif + if (!(urb->transfer_flags & URB_NO_FSBR)) urbp->fsbr = 1; } @@ -1305,7 +1303,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, } /* Fell behind? */ - if (!uhci_frame_before_eq(next, frame)) { + if (uhci_frame_before_eq(frame, next)) { /* USB_ISO_ASAP: Round up to the first available slot */ if (urb->transfer_flags & URB_ISO_ASAP) @@ -1313,17 +1311,13 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, -qh->period; /* - * Not ASAP: Use the next slot in the stream, - * no matter what. + * Not ASAP: Use the next slot in the stream. If + * the entire URB falls before the threshold, fail. */ else if (!uhci_frame_before_eq(next, frame + (urb->number_of_packets - 1) * qh->period)) - dev_dbg(uhci_dev(uhci), "iso underrun %p (%u+%u < %u)\n", - urb, frame, - (urb->number_of_packets - 1) * - qh->period, - next); + return -EXDEV; } } diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 7cdcfd024744..187a3ec1069a 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -286,7 +286,7 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend) if (virt_dev->eps[i].ring && virt_dev->eps[i].ring->dequeue) xhci_queue_stop_endpoint(xhci, slot_id, i, suspend); } - cmd->command_trb = xhci_find_next_enqueue(xhci->cmd_ring); + cmd->command_trb = xhci->cmd_ring->enqueue; list_add_tail(&cmd->cmd_list, &virt_dev->cmd_list); xhci_queue_stop_endpoint(xhci, slot_id, 0, suspend); xhci_ring_cmd_db(xhci); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 159e3c6d92b9..f00cb203faea 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -345,7 +345,7 @@ static struct pci_driver xhci_pci_driver = { /* suspend and resume implemented later */ .shutdown = usb_hcd_pci_shutdown, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .driver = { .pm = &usb_hcd_pci_pm_ops }, diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 37003c8d61c7..ce37aedbe3ad 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -24,7 +24,7 @@ static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci) * here that the generic code does not try to make a pci_dev from our * dev struct in order to setup MSI */ - xhci->quirks |= XHCI_PLAT; + xhci->quirks |= XHCI_BROKEN_MSI; } /* called during probe() after chip reset completes */ diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index bcfb08e41eb6..cc3bfc5d590d 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -122,16 +122,6 @@ static int enqueue_is_link_trb(struct xhci_ring *ring) return TRB_TYPE_LINK_LE32(link->control); } -union xhci_trb *xhci_find_next_enqueue(struct xhci_ring *ring) -{ - /* Enqueue pointer can be left pointing to the link TRB, - * we must handle that - */ - if (TRB_TYPE_LINK_LE32(ring->enqueue->link.control)) - return ring->enq_seg->next->trbs; - return ring->enqueue; -} - /* Updates trb to point to the next TRB in the ring, and updates seg if the next * TRB is in a new segment. This does not skip over link TRBs, and it does not * effect the ring dequeue or enqueue pointers. @@ -857,12 +847,8 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, /* Otherwise ring the doorbell(s) to restart queued transfers */ ring_doorbell_for_active_rings(xhci, slot_id, ep_index); } - - /* Clear stopped_td and stopped_trb if endpoint is not halted */ - if (!(ep->ep_state & EP_HALTED)) { - ep->stopped_td = NULL; - ep->stopped_trb = NULL; - } + ep->stopped_td = NULL; + ep->stopped_trb = NULL; /* * Drop the lock and complete the URBs in the cancelled TD list. @@ -1404,12 +1390,6 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, inc_deq(xhci, xhci->cmd_ring); return; } - /* There is no command to handle if we get a stop event when the - * command ring is empty, event->cmd_trb points to the next - * unset command - */ - if (xhci->cmd_ring->dequeue == xhci->cmd_ring->enqueue) - return; } switch (le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3]) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 4a5251f3614c..9a550b6ad01d 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -342,14 +342,9 @@ static void xhci_msix_sync_irqs(struct xhci_hcd *xhci) static int xhci_try_enable_msi(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct pci_dev *pdev; + struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); int ret; - /* The xhci platform device has set up IRQs through usb_add_hcd. */ - if (xhci->quirks & XHCI_PLAT) - return 0; - - pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); /* * Some Fresco Logic host controllers advertise MSI, but fail to * generate interrupts. Don't even try to enable MSI. @@ -2592,7 +2587,15 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, if (command) { cmd_completion = command->completion; cmd_status = &command->status; - command->command_trb = xhci_find_next_enqueue(xhci->cmd_ring); + command->command_trb = xhci->cmd_ring->enqueue; + + /* Enqueue pointer can be left pointing to the link TRB, + * we must handle that + */ + if (TRB_TYPE_LINK_LE32(command->command_trb->link.control)) + command->command_trb = + xhci->cmd_ring->enq_seg->next->trbs; + list_add_tail(&command->cmd_list, &virt_dev->cmd_list); } else { cmd_completion = &virt_dev->cmd_completion; @@ -2600,7 +2603,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, } init_completion(cmd_completion); - cmd_trb = xhci_find_next_enqueue(xhci->cmd_ring); + cmd_trb = xhci->cmd_ring->dequeue; if (!ctx_change) ret = xhci_queue_configure_endpoint(xhci, in_ctx->dma, udev->slot_id, must_succeed); @@ -3385,7 +3388,14 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) /* Attempt to submit the Reset Device command to the command ring */ spin_lock_irqsave(&xhci->lock, flags); - reset_device_cmd->command_trb = xhci_find_next_enqueue(xhci->cmd_ring); + reset_device_cmd->command_trb = xhci->cmd_ring->enqueue; + + /* Enqueue pointer can be left pointing to the link TRB, + * we must handle that + */ + if (TRB_TYPE_LINK_LE32(reset_device_cmd->command_trb->link.control)) + reset_device_cmd->command_trb = + xhci->cmd_ring->enq_seg->next->trbs; list_add_tail(&reset_device_cmd->cmd_list, &virt_dev->cmd_list); ret = xhci_queue_reset_device(xhci, slot_id); @@ -3496,21 +3506,10 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct xhci_virt_device *virt_dev; - struct device *dev = hcd->self.controller; unsigned long flags; u32 state; int i, ret; -#ifndef CONFIG_USB_DEFAULT_PERSIST - /* - * We called pm_runtime_get_noresume when the device was attached. - * Decrement the counter here to allow controller to runtime suspend - * if no devices remain. - */ - if (xhci->quirks & XHCI_RESET_ON_RESUME) - pm_runtime_put_noidle(dev); -#endif - ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__); /* If the host is halted due to driver unload, we still need to free the * device. @@ -3582,14 +3581,13 @@ static int xhci_reserve_host_control_ep_resources(struct xhci_hcd *xhci) int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct device *dev = hcd->self.controller; unsigned long flags; int timeleft; int ret; union xhci_trb *cmd_trb; spin_lock_irqsave(&xhci->lock, flags); - cmd_trb = xhci_find_next_enqueue(xhci->cmd_ring); + cmd_trb = xhci->cmd_ring->dequeue; ret = xhci_queue_slot_control(xhci, TRB_ENABLE_SLOT, 0); if (ret) { spin_unlock_irqrestore(&xhci->lock, flags); @@ -3635,16 +3633,6 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev) goto disable_slot; } udev->slot_id = xhci->slot_id; - -#ifndef CONFIG_USB_DEFAULT_PERSIST - /* - * If resetting upon resume, we can't put the controller into runtime - * suspend if there is a device attached. - */ - if (xhci->quirks & XHCI_RESET_ON_RESUME) - pm_runtime_get_noresume(dev); -#endif - /* Is this a LS or FS device under a HS hub? */ /* Hub or peripherial? */ return 1; @@ -3716,7 +3704,7 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) xhci_dbg_ctx(xhci, virt_dev->in_ctx, 2); spin_lock_irqsave(&xhci->lock, flags); - cmd_trb = xhci_find_next_enqueue(xhci->cmd_ring); + cmd_trb = xhci->cmd_ring->dequeue; ret = xhci_queue_address_device(xhci, virt_dev->in_ctx->dma, udev->slot_id); if (ret) { diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 627fcd9388ca..77600cefcaf1 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1516,7 +1516,6 @@ struct xhci_hcd { #define XHCI_SPURIOUS_REBOOT (1 << 13) #define XHCI_COMP_MODE_QUIRK (1 << 14) #define XHCI_AVOID_BEI (1 << 15) -#define XHCI_PLAT (1 << 16) unsigned int num_active_eps; unsigned int limit_active_eps; /* There are two roothubs to keep track of bus suspend info for */ @@ -1821,7 +1820,6 @@ int xhci_cancel_cmd(struct xhci_hcd *xhci, struct xhci_command *command, union xhci_trb *cmd_trb); void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, unsigned int stream_id); -union xhci_trb *xhci_find_next_enqueue(struct xhci_ring *ring); /* xHCI roothub code */ void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array, diff --git a/drivers/usb/phy/phy-fsl-usb.h b/drivers/usb/phy/phy-fsl-usb.h index e1859b8ef567..ca266280895d 100644 --- a/drivers/usb/phy/phy-fsl-usb.h +++ b/drivers/usb/phy/phy-fsl-usb.h @@ -15,7 +15,7 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "phy-fsm-usb.h" +#include "otg_fsm.h" #include #include diff --git a/drivers/usb/phy/phy-fsm-usb.c b/drivers/usb/phy/phy-fsm-usb.c index 7f4596606e18..c520b3548e7c 100644 --- a/drivers/usb/phy/phy-fsm-usb.c +++ b/drivers/usb/phy/phy-fsm-usb.c @@ -29,7 +29,7 @@ #include #include -#include "phy-fsm-usb.h" +#include "phy-otg-fsm.h" /* Change USB protocol when there is a protocol change */ static int otg_set_protocol(struct otg_fsm *fsm, int protocol) diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 0f16bf6ea71c..5050cc8584ba 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -374,7 +374,7 @@ static int write_parport_reg_nonblock(struct mos7715_parport *mos_parport, kfree(urbtrack); return -ENOMEM; } - urbtrack->setup = kmalloc(sizeof(*urbtrack->setup), GFP_ATOMIC); + urbtrack->setup = kmalloc(sizeof(*urbtrack->setup), GFP_KERNEL); if (!urbtrack->setup) { usb_free_urb(urbtrack->urb); kfree(urbtrack); @@ -382,8 +382,8 @@ static int write_parport_reg_nonblock(struct mos7715_parport *mos_parport, } urbtrack->setup->bRequestType = (__u8)0x40; urbtrack->setup->bRequest = (__u8)0x0e; - urbtrack->setup->wValue = cpu_to_le16(get_reg_value(reg, dummy)); - urbtrack->setup->wIndex = cpu_to_le16(get_reg_index(reg)); + urbtrack->setup->wValue = get_reg_value(reg, dummy); + urbtrack->setup->wIndex = get_reg_index(reg); urbtrack->setup->wLength = 0; usb_fill_control_urb(urbtrack->urb, usbdev, usb_sndctrlpipe(usbdev, 0), diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 80a7104d5ddb..1cf6f125f5f0 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -81,7 +81,6 @@ static void option_instat_callback(struct urb *urb); #define HUAWEI_VENDOR_ID 0x12D1 #define HUAWEI_PRODUCT_E173 0x140C -#define HUAWEI_PRODUCT_E1750 0x1406 #define HUAWEI_PRODUCT_K4505 0x1464 #define HUAWEI_PRODUCT_K3765 0x1465 #define HUAWEI_PRODUCT_K4605 0x14C6 @@ -568,8 +567,6 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1c23, USB_CLASS_COMM, 0x02, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E173, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t) &net_intf1_blacklist }, - { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1750, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t) &net_intf2_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1441, USB_CLASS_COMM, 0x02, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1442, USB_CLASS_COMM, 0x02, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4505, 0xff, 0xff, 0xff), diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index d6a518ce4d6d..8ca5ac71b845 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -307,11 +307,6 @@ static void vhost_zerocopy_callback(struct ubuf_info *ubuf, bool success) struct vhost_virtqueue *vq = ubufs->vq; int cnt = atomic_read(&ubufs->kref.refcount); - /* set len to mark this desc buffers done DMA */ - vq->heads[ubuf->desc].len = success ? - VHOST_DMA_DONE_LEN : VHOST_DMA_FAILED_LEN; - vhost_net_ubuf_put(ubufs); - /* * Trigger polling thread if guest stopped submitting new buffers: * in this case, the refcount after decrement will eventually reach 1 @@ -322,6 +317,10 @@ static void vhost_zerocopy_callback(struct ubuf_info *ubuf, bool success) */ if (cnt <= 2 || !(cnt % 16)) vhost_poll_queue(&vq->poll); + /* set len to mark this desc buffers done DMA */ + vq->heads[ubuf->desc].len = success ? + VHOST_DMA_DONE_LEN : VHOST_DMA_FAILED_LEN; + vhost_net_ubuf_put(ubufs); } /* Expects to be always run from workqueue - which acts as diff --git a/drivers/watchdog/ts72xx_wdt.c b/drivers/watchdog/ts72xx_wdt.c index 9ad2bd344eb4..b8a92459f10f 100644 --- a/drivers/watchdog/ts72xx_wdt.c +++ b/drivers/watchdog/ts72xx_wdt.c @@ -310,8 +310,7 @@ static long ts72xx_wdt_ioctl(struct file *file, unsigned int cmd, case WDIOC_GETSTATUS: case WDIOC_GETBOOTSTATUS: - error = put_user(0, p); - break; + return put_user(0, p); case WDIOC_KEEPALIVE: ts72xx_wdt_kick(wdt); diff --git a/drivers/xen/events.c b/drivers/xen/events.c index 1faa1305c043..6a6bbe4ede92 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c @@ -346,7 +346,7 @@ static void init_evtchn_cpu_bindings(void) for_each_possible_cpu(i) memset(per_cpu(cpu_evtchn_mask, i), - (i == 0) ? ~0 : 0, NR_EVENT_CHANNELS/8); + (i == 0) ? ~0 : 0, sizeof(*per_cpu(cpu_evtchn_mask, i))); } static inline void clear_evtchn(int port) @@ -1492,10 +1492,8 @@ void rebind_evtchn_irq(int evtchn, int irq) /* Rebind an evtchn so that it gets delivered to a specific cpu */ static int rebind_irq_to_cpu(unsigned irq, unsigned tcpu) { - struct shared_info *s = HYPERVISOR_shared_info; struct evtchn_bind_vcpu bind_vcpu; int evtchn = evtchn_from_irq(irq); - int masked; if (!VALID_EVTCHN(evtchn)) return -1; @@ -1511,12 +1509,6 @@ static int rebind_irq_to_cpu(unsigned irq, unsigned tcpu) bind_vcpu.port = evtchn; bind_vcpu.vcpu = tcpu; - /* - * Mask the event while changing the VCPU binding to prevent - * it being delivered on an unexpected VCPU. - */ - masked = sync_test_and_set_bit(evtchn, BM(s->evtchn_mask)); - /* * If this fails, it usually just indicates that we're dealing with a * virq or IPI channel, which don't actually need to be rebound. Ignore @@ -1525,9 +1517,6 @@ static int rebind_irq_to_cpu(unsigned irq, unsigned tcpu) if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_vcpu, &bind_vcpu) >= 0) bind_evtchn_to_cpu(evtchn, tcpu); - if (!masked) - unmask_evtchn(evtchn); - return 0; } diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index d5418c1be9aa..04c1b2d9b775 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -729,18 +729,9 @@ void gnttab_request_free_callback(struct gnttab_free_callback *callback, void (*fn)(void *), void *arg, u16 count) { unsigned long flags; - struct gnttab_free_callback *cb; - spin_lock_irqsave(&gnttab_list_lock, flags); - - /* Check if the callback is already on the list */ - cb = gnttab_free_callback_list; - while (cb) { - if (cb == callback) - goto out; - cb = cb->next; - } - + if (callback->next) + goto out; callback->fn = fn; callback->arg = arg; callback->count = count; diff --git a/fs/Kconfig b/fs/Kconfig index 056db61a4125..c229f828eb01 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -193,7 +193,6 @@ source "fs/hfsplus/Kconfig" source "fs/befs/Kconfig" source "fs/bfs/Kconfig" source "fs/efs/Kconfig" -source "fs/yaffs2/Kconfig" source "fs/jffs2/Kconfig" # UBIFS File system configuration source "fs/ubifs/Kconfig" @@ -213,7 +212,6 @@ source "fs/ufs/Kconfig" source "fs/exofs/Kconfig" source "fs/f2fs/Kconfig" source "fs/efivarfs/Kconfig" -source "fs/aufs/Kconfig" endif # MISC_FILESYSTEMS diff --git a/fs/Makefile b/fs/Makefile index 04e97df941e4..4fe6df3ec28f 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -126,5 +126,3 @@ obj-y += exofs/ # Multiple modules obj-$(CONFIG_CEPH_FS) += ceph/ obj-$(CONFIG_PSTORE) += pstore/ obj-$(CONFIG_EFIVAR_FS) += efivarfs/ -obj-$(CONFIG_AUFS_FS) += aufs/ -obj-$(CONFIG_YAFFS_FS) += yaffs2/ diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 3aac8e9edac3..f8a0b0efda44 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -1415,7 +1415,7 @@ static void fill_siginfo_note(struct memelfnote *note, user_siginfo_t *csigdata, * long file_ofs * followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL... */ -static int fill_files_note(struct memelfnote *note) +static void fill_files_note(struct memelfnote *note) { struct vm_area_struct *vma; unsigned count, size, names_ofs, remaining, n; @@ -1430,11 +1430,11 @@ static int fill_files_note(struct memelfnote *note) names_ofs = (2 + 3 * count) * sizeof(data[0]); alloc: if (size >= MAX_FILE_NOTE_SIZE) /* paranoia check */ - return -EINVAL; + goto err; size = round_up(size, PAGE_SIZE); data = vmalloc(size); if (!data) - return -ENOMEM; + goto err; start_end_ofs = data + 2; name_base = name_curpos = ((char *)data) + names_ofs; @@ -1487,7 +1487,7 @@ static int fill_files_note(struct memelfnote *note) size = name_curpos - (char *)data; fill_note(note, "CORE", NT_FILE, size, data); - return 0; + err: ; } #ifdef CORE_DUMP_USE_REGSET @@ -1688,8 +1688,8 @@ static int fill_note_info(struct elfhdr *elf, int phdrs, fill_auxv_note(&info->auxv, current->mm); info->size += notesize(&info->auxv); - if (fill_files_note(&info->files) == 0) - info->size += notesize(&info->files); + fill_files_note(&info->files); + info->size += notesize(&info->files); return 1; } @@ -1721,8 +1721,7 @@ static int write_note_info(struct elf_note_info *info, return 0; if (first && !writenote(&info->auxv, file, foffset)) return 0; - if (first && info->files.data && - !writenote(&info->files, file, foffset)) + if (first && !writenote(&info->files, file, foffset)) return 0; for (i = 1; i < info->thread_notes; ++i) @@ -1809,7 +1808,6 @@ static int elf_dump_thread_status(long signr, struct elf_thread_status *t) struct elf_note_info { struct memelfnote *notes; - struct memelfnote *notes_files; struct elf_prstatus *prstatus; /* NT_PRSTATUS */ struct elf_prpsinfo *psinfo; /* NT_PRPSINFO */ struct list_head thread_list; @@ -1900,12 +1898,9 @@ static int fill_note_info(struct elfhdr *elf, int phdrs, fill_siginfo_note(info->notes + 2, &info->csigdata, siginfo); fill_auxv_note(info->notes + 3, current->mm); - info->numnote = 4; + fill_files_note(info->notes + 4); - if (fill_files_note(info->notes + info->numnote) == 0) { - info->notes_files = info->notes + info->numnote; - info->numnote++; - } + info->numnote = 5; /* Try to dump the FPU. */ info->prstatus->pr_fpvalid = elf_core_copy_task_fpregs(current, regs, @@ -1967,9 +1962,8 @@ static void free_note_info(struct elf_note_info *info) kfree(list_entry(tmp, struct elf_thread_status, list)); } - /* Free data possibly allocated by fill_files_note(): */ - if (info->notes_files) - vfree(info->notes_files->data); + /* Free data allocated by fill_files_note(): */ + vfree(info->notes[4].data); kfree(info->prstatus); kfree(info->psinfo); @@ -2052,7 +2046,7 @@ static int elf_core_dump(struct coredump_params *cprm) struct vm_area_struct *vma, *gate_vma; struct elfhdr *elf = NULL; loff_t offset = 0, dataoff, foffset; - struct elf_note_info info = { }; + struct elf_note_info info; struct elf_phdr *phdr4note = NULL; struct elf_shdr *shdr4extnum = NULL; Elf_Half e_phnum; diff --git a/fs/bio-integrity.c b/fs/bio-integrity.c index 45e944fe52a6..8fb42916d8a2 100644 --- a/fs/bio-integrity.c +++ b/fs/bio-integrity.c @@ -734,7 +734,7 @@ void bioset_integrity_free(struct bio_set *bs) mempool_destroy(bs->bio_integrity_pool); if (bs->bvec_integrity_pool) - mempool_destroy(bs->bvec_integrity_pool); + mempool_destroy(bs->bio_integrity_pool); } EXPORT_SYMBOL(bioset_integrity_free); diff --git a/fs/bio.c b/fs/bio.c index 5e7507d79297..94bbc04dba77 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -917,8 +917,8 @@ void bio_copy_data(struct bio *dst, struct bio *src) src_p = kmap_atomic(src_bv->bv_page); dst_p = kmap_atomic(dst_bv->bv_page); - memcpy(dst_p + dst_offset, - src_p + src_offset, + memcpy(dst_p + dst_bv->bv_offset, + src_p + src_bv->bv_offset, bytes); kunmap_atomic(dst_p); @@ -1045,22 +1045,12 @@ static int __bio_copy_iov(struct bio *bio, struct bio_vec *iovecs, int bio_uncopy_user(struct bio *bio) { struct bio_map_data *bmd = bio->bi_private; - struct bio_vec *bvec; - int ret = 0, i; + int ret = 0; - if (!bio_flagged(bio, BIO_NULL_MAPPED)) { - /* - * if we're in a workqueue, the request is orphaned, so - * don't copy into a random user address space, just free. - */ - if (current->mm) - ret = __bio_copy_iov(bio, bmd->iovecs, bmd->sgvecs, - bmd->nr_sgvecs, bio_data_dir(bio) == READ, - 0, bmd->is_our_pages); - else if (bmd->is_our_pages) - bio_for_each_segment_all(bvec, bio, i) - __free_page(bvec->bv_page); - } + if (!bio_flagged(bio, BIO_NULL_MAPPED)) + ret = __bio_copy_iov(bio, bmd->iovecs, bmd->sgvecs, + bmd->nr_sgvecs, bio_data_dir(bio) == READ, + 0, bmd->is_our_pages); bio_free_map_data(bmd); bio_put(bio); return ret; diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 3b6d20bc2388..0b272d068337 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -2402,8 +2402,6 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans, default: WARN_ON(1); } - } else { - list_del_init(&locked_ref->cluster); } spin_unlock(&delayed_refs->lock); @@ -2426,6 +2424,7 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans, * list before we release it. */ if (btrfs_delayed_ref_is_head(ref)) { + list_del_init(&locked_ref->cluster); btrfs_delayed_ref_unlock(locked_ref); locked_ref = NULL; } diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 1e2288dc5346..17f3064b4a3e 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -8146,7 +8146,7 @@ static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, /* check for collisions, even if the name isn't there */ - ret = btrfs_check_dir_item_collision(dest, new_dir->i_ino, + ret = btrfs_check_dir_item_collision(root, new_dir->i_ino, new_dentry->d_name.name, new_dentry->d_name.len); diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 8dedf4019672..0f81d67cdc8d 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -3299,9 +3299,6 @@ static long btrfs_ioctl_dev_replace(struct btrfs_root *root, void __user *arg) switch (p->cmd) { case BTRFS_IOCTL_DEV_REPLACE_CMD_START: - if (root->fs_info->sb->s_flags & MS_RDONLY) - return -EROFS; - if (atomic_xchg( &root->fs_info->mutually_exclusive_operation_running, 1)) { diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index b3896d5f233a..4febca4fc2de 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -691,7 +691,6 @@ struct backref_node *build_backref_tree(struct reloc_control *rc, int cowonly; int ret; int err = 0; - bool need_check = true; path1 = btrfs_alloc_path(); path2 = btrfs_alloc_path(); @@ -915,7 +914,6 @@ struct backref_node *build_backref_tree(struct reloc_control *rc, cur->bytenr); lower = cur; - need_check = true; for (; level < BTRFS_MAX_LEVEL; level++) { if (!path2->nodes[level]) { BUG_ON(btrfs_root_bytenr(&root->root_item) != @@ -959,12 +957,14 @@ struct backref_node *build_backref_tree(struct reloc_control *rc, /* * add the block to pending list if we - * need check its backrefs, we only do this once - * while walking up a tree as we will catch - * anything else later on. + * need check its backrefs. only block + * at 'cur->level + 1' is added to the + * tail of pending list. this guarantees + * we check backrefs from lower level + * blocks to upper level blocks. */ - if (!upper->checked && need_check) { - need_check = false; + if (!upper->checked && + level == cur->level + 1) { list_add_tail(&edge->list[UPPER], &list); } else diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index 09ea0bdde65f..ba9690b9ae24 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -2524,8 +2524,7 @@ static int did_create_dir(struct send_ctx *sctx, u64 dir) di = btrfs_item_ptr(eb, slot, struct btrfs_dir_item); btrfs_dir_item_key_to_cpu(eb, di, &di_key); - if (di_key.type != BTRFS_ROOT_ITEM_KEY && - di_key.objectid < sctx->send_progress) { + if (di_key.objectid < sctx->send_progress) { ret = 1; goto out; } diff --git a/fs/buffer.c b/fs/buffer.c index 7e0240f6f751..d2a4d1bb2d57 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -2388,8 +2388,6 @@ int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf, * fault so this update may be superfluous but who really cares... */ file_update_time(vma->vm_file); - if (vma->vm_prfile) - file_update_time(vma->vm_prfile); ret = __block_page_mkwrite(vma, vmf, get_block); sb_end_pagefault(sb); diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c index a5ce62eb7806..e0b4ef31d3c8 100644 --- a/fs/ceph/ioctl.c +++ b/fs/ceph/ioctl.c @@ -196,10 +196,8 @@ static long ceph_ioctl_get_dataloc(struct file *file, void __user *arg) r = ceph_calc_file_object_mapping(&ci->i_layout, dl.file_offset, len, &dl.object_no, &dl.object_offset, &olen); - if (r < 0) { - up_read(&osdc->map_sem); + if (r < 0) return -EIO; - } dl.file_offset -= dl.object_offset; dl.object_size = ceph_file_layout_object_size(ci->i_layout); dl.block_size = ceph_file_layout_su(ci->i_layout); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index d05a30072023..d6a5c5ac737b 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -377,7 +377,6 @@ cifs_reconnect(struct TCP_Server_Info *server) try_to_freeze(); /* we should try only the port we connected to before */ - mutex_lock(&server->srv_mutex); rc = generic_ip_connect(server); if (rc) { cifs_dbg(FYI, "reconnect error %d\n", rc); @@ -389,7 +388,6 @@ cifs_reconnect(struct TCP_Server_Info *server) server->tcpStatus = CifsNeedNegotiate; spin_unlock(&GlobalMid_Lock); } - mutex_unlock(&server->srv_mutex); } while (server->tcpStatus == CifsNeedReconnect); return rc; diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 0c2425b21974..5699b5036ed8 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -491,7 +491,6 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry, if (server->ops->close) server->ops->close(xid, tcon, &fid); cifs_del_pending_open(&open); - fput(file); rc = -ENOMEM; } diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c index 4f791e0e98d7..10383d8c015b 100644 --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c @@ -413,76 +413,19 @@ cifs_ses_oplock_break(struct work_struct *work) } static bool -smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp, - struct smb2_lease_break_work *lw) -{ - bool found; - __u8 lease_state; - struct list_head *tmp; - struct cifsFileInfo *cfile; - struct cifs_pending_open *open; - struct cifsInodeInfo *cinode; - int ack_req = le32_to_cpu(rsp->Flags & - SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); - - lease_state = smb2_map_lease_to_oplock(rsp->NewLeaseState); - - list_for_each(tmp, &tcon->openFileList) { - cfile = list_entry(tmp, struct cifsFileInfo, tlist); - cinode = CIFS_I(cfile->dentry->d_inode); - - if (memcmp(cinode->lease_key, rsp->LeaseKey, - SMB2_LEASE_KEY_SIZE)) - continue; - - cifs_dbg(FYI, "found in the open list\n"); - cifs_dbg(FYI, "lease key match, lease break 0x%d\n", - le32_to_cpu(rsp->NewLeaseState)); - - smb2_set_oplock_level(cinode, lease_state); - - if (ack_req) - cfile->oplock_break_cancelled = false; - else - cfile->oplock_break_cancelled = true; - - queue_work(cifsiod_wq, &cfile->oplock_break); - kfree(lw); - return true; - } - - found = false; - list_for_each_entry(open, &tcon->pending_opens, olist) { - if (memcmp(open->lease_key, rsp->LeaseKey, - SMB2_LEASE_KEY_SIZE)) - continue; - - if (!found && ack_req) { - found = true; - memcpy(lw->lease_key, open->lease_key, - SMB2_LEASE_KEY_SIZE); - lw->tlink = cifs_get_tlink(open->tlink); - queue_work(cifsiod_wq, &lw->lease_break); - } - - cifs_dbg(FYI, "found in the pending open list\n"); - cifs_dbg(FYI, "lease key match, lease break 0x%d\n", - le32_to_cpu(rsp->NewLeaseState)); - - open->oplock = lease_state; - } - return found; -} - -static bool -smb2_is_valid_lease_break(char *buffer) +smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server) { struct smb2_lease_break *rsp = (struct smb2_lease_break *)buffer; struct list_head *tmp, *tmp1, *tmp2; - struct TCP_Server_Info *server; struct cifs_ses *ses; struct cifs_tcon *tcon; + struct cifsInodeInfo *cinode; + struct cifsFileInfo *cfile; + struct cifs_pending_open *open; struct smb2_lease_break_work *lw; + bool found; + int ack_req = le32_to_cpu(rsp->Flags & + SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL); if (!lw) @@ -495,26 +438,71 @@ smb2_is_valid_lease_break(char *buffer) /* look up tcon based on tid & uid */ spin_lock(&cifs_tcp_ses_lock); - list_for_each(tmp, &cifs_tcp_ses_list) { - server = list_entry(tmp, struct TCP_Server_Info, tcp_ses_list); + list_for_each(tmp, &server->smb_ses_list) { + ses = list_entry(tmp, struct cifs_ses, smb_ses_list); - list_for_each(tmp1, &server->smb_ses_list) { - ses = list_entry(tmp1, struct cifs_ses, smb_ses_list); + spin_lock(&cifs_file_list_lock); + list_for_each(tmp1, &ses->tcon_list) { + tcon = list_entry(tmp1, struct cifs_tcon, tcon_list); - spin_lock(&cifs_file_list_lock); - list_for_each(tmp2, &ses->tcon_list) { - tcon = list_entry(tmp2, struct cifs_tcon, - tcon_list); - cifs_stats_inc( - &tcon->stats.cifs_stats.num_oplock_brks); - if (smb2_tcon_has_lease(tcon, rsp, lw)) { - spin_unlock(&cifs_file_list_lock); - spin_unlock(&cifs_tcp_ses_lock); - return true; + cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks); + list_for_each(tmp2, &tcon->openFileList) { + cfile = list_entry(tmp2, struct cifsFileInfo, + tlist); + cinode = CIFS_I(cfile->dentry->d_inode); + + if (memcmp(cinode->lease_key, rsp->LeaseKey, + SMB2_LEASE_KEY_SIZE)) + continue; + + cifs_dbg(FYI, "found in the open list\n"); + cifs_dbg(FYI, "lease key match, lease break 0x%d\n", + le32_to_cpu(rsp->NewLeaseState)); + + smb2_set_oplock_level(cinode, + smb2_map_lease_to_oplock(rsp->NewLeaseState)); + + if (ack_req) + cfile->oplock_break_cancelled = false; + else + cfile->oplock_break_cancelled = true; + + queue_work(cifsiod_wq, &cfile->oplock_break); + + spin_unlock(&cifs_file_list_lock); + spin_unlock(&cifs_tcp_ses_lock); + return true; + } + + found = false; + list_for_each_entry(open, &tcon->pending_opens, olist) { + if (memcmp(open->lease_key, rsp->LeaseKey, + SMB2_LEASE_KEY_SIZE)) + continue; + + if (!found && ack_req) { + found = true; + memcpy(lw->lease_key, open->lease_key, + SMB2_LEASE_KEY_SIZE); + lw->tlink = cifs_get_tlink(open->tlink); + queue_work(cifsiod_wq, + &lw->lease_break); } + + cifs_dbg(FYI, "found in the pending open list\n"); + cifs_dbg(FYI, "lease key match, lease break 0x%d\n", + le32_to_cpu(rsp->NewLeaseState)); + + open->oplock = + smb2_map_lease_to_oplock(rsp->NewLeaseState); + } + if (found) { + spin_unlock(&cifs_file_list_lock); + spin_unlock(&cifs_tcp_ses_lock); + return true; } - spin_unlock(&cifs_file_list_lock); } + spin_unlock(&cifs_file_list_lock); } spin_unlock(&cifs_tcp_ses_lock); kfree(lw); @@ -540,7 +528,7 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) if (rsp->StructureSize != smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) { if (le16_to_cpu(rsp->StructureSize) == 44) - return smb2_is_valid_lease_break(buffer); + return smb2_is_valid_lease_break(buffer, server); else return false; } diff --git a/fs/dcache.c b/fs/dcache.c index da89cdfb21ab..f09b9085f7d8 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2724,17 +2724,6 @@ char *dynamic_dname(struct dentry *dentry, char *buffer, int buflen, return memcpy(buffer, temp, sz); } -char *simple_dname(struct dentry *dentry, char *buffer, int buflen) -{ - char *end = buffer + buflen; - /* these dentries are never renamed, so d_lock is not needed */ - if (prepend(&end, &buflen, " (deleted)", 11) || - prepend_name(&end, &buflen, &dentry->d_name) || - prepend(&end, &buflen, "/", 1)) - end = ERR_PTR(-ENAMETOOLONG); - return end; -} - /* * Write full pathname from the root of the filesystem into the buffer. */ diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 904ca1a21dce..ccbfbbb12dc5 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4706,9 +4706,7 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) ext4_journal_stop(handle); } - if (attr->ia_valid & ATTR_SIZE && attr->ia_size != inode->i_size) { - handle_t *handle; - loff_t oldsize = inode->i_size; + if (attr->ia_valid & ATTR_SIZE) { if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) { struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); @@ -4716,60 +4714,73 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) if (attr->ia_size > sbi->s_bitmap_maxbytes) return -EFBIG; } - if (S_ISREG(inode->i_mode) && - (attr->ia_size < inode->i_size)) { - if (ext4_should_order_data(inode)) { - error = ext4_begin_ordered_truncate(inode, + } + + if (S_ISREG(inode->i_mode) && + attr->ia_valid & ATTR_SIZE && + (attr->ia_size < inode->i_size)) { + handle_t *handle; + + handle = ext4_journal_start(inode, EXT4_HT_INODE, 3); + if (IS_ERR(handle)) { + error = PTR_ERR(handle); + goto err_out; + } + if (ext4_handle_valid(handle)) { + error = ext4_orphan_add(handle, inode); + orphan = 1; + } + EXT4_I(inode)->i_disksize = attr->ia_size; + rc = ext4_mark_inode_dirty(handle, inode); + if (!error) + error = rc; + ext4_journal_stop(handle); + + if (ext4_should_order_data(inode)) { + error = ext4_begin_ordered_truncate(inode, attr->ia_size); - if (error) - goto err_out; - } - handle = ext4_journal_start(inode, EXT4_HT_INODE, 3); - if (IS_ERR(handle)) { - error = PTR_ERR(handle); - goto err_out; - } - if (ext4_handle_valid(handle)) { - error = ext4_orphan_add(handle, inode); - orphan = 1; - } - EXT4_I(inode)->i_disksize = attr->ia_size; - rc = ext4_mark_inode_dirty(handle, inode); - if (!error) - error = rc; - ext4_journal_stop(handle); if (error) { - ext4_orphan_del(NULL, inode); + /* Do as much error cleanup as possible */ + handle = ext4_journal_start(inode, + EXT4_HT_INODE, 3); + if (IS_ERR(handle)) { + ext4_orphan_del(NULL, inode); + goto err_out; + } + ext4_orphan_del(handle, inode); + orphan = 0; + ext4_journal_stop(handle); goto err_out; } } + } - i_size_write(inode, attr->ia_size); - /* - * Blocks are going to be removed from the inode. Wait - * for dio in flight. Temporarily disable - * dioread_nolock to prevent livelock. - */ - if (orphan) { - if (!ext4_should_journal_data(inode)) { - ext4_inode_block_unlocked_dio(inode); - inode_dio_wait(inode); - ext4_inode_resume_unlocked_dio(inode); - } else - ext4_wait_for_tail_page_commit(inode); + if (attr->ia_valid & ATTR_SIZE) { + if (attr->ia_size != inode->i_size) { + loff_t oldsize = inode->i_size; + + i_size_write(inode, attr->ia_size); + /* + * Blocks are going to be removed from the inode. Wait + * for dio in flight. Temporarily disable + * dioread_nolock to prevent livelock. + */ + if (orphan) { + if (!ext4_should_journal_data(inode)) { + ext4_inode_block_unlocked_dio(inode); + inode_dio_wait(inode); + ext4_inode_resume_unlocked_dio(inode); + } else + ext4_wait_for_tail_page_commit(inode); + } + /* + * Truncate pagecache after we've waited for commit + * in data=journal mode to make pages freeable. + */ + truncate_pagecache(inode, oldsize, inode->i_size); } - /* - * Truncate pagecache after we've waited for commit - * in data=journal mode to make pages freeable. - */ - truncate_pagecache(inode, oldsize, inode->i_size); - } - /* - * We want to call ext4_truncate() even if attr->ia_size == - * inode->i_size for cases like truncation of fallocated space - */ - if (attr->ia_valid & ATTR_SIZE) ext4_truncate(inode); + } if (!rc) { setattr_copy(inode, attr); diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index 03e9bebba198..c081e34f717f 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -1350,8 +1350,6 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize, s_min_extra_isize) { tried_min_extra_isize++; new_extra_isize = s_min_extra_isize; - kfree(is); is = NULL; - kfree(bs); bs = NULL; goto retry; } error = -1; diff --git a/fs/fat/Kconfig b/fs/fat/Kconfig index 6bd12c06b4ac..182f9ffe2b51 100644 --- a/fs/fat/Kconfig +++ b/fs/fat/Kconfig @@ -74,27 +74,6 @@ config VFAT_FS To compile this as a module, choose M here: the module will be called vfat. -config VFAT_FS_NO_DUALNAMES - bool "disable VFAT dual names support (patent workaround)" - depends on VFAT_FS - help - This option disables support for dual filenames on VFAT filesystems. - If this option is enabled then file creation will either put - a short (8.3) name or a long name on the file, but never both. - The field where a shortname would normally go is filled with - invalid characters such that it cannot be considered a valid - short filename. - - That means that long filenames created with this option - disabled will not be accessible at all to operating systems - that do not understand the FAT long filename extensions. - - Users considering disabling this option should consider the - implications of any patents that may exist on dual filenames - in VFAT. - - If unsure, say N - config FAT_DEFAULT_CODEPAGE int "Default codepage for FAT" depends on MSDOS_FS || VFAT_FS @@ -119,14 +98,3 @@ config FAT_DEFAULT_IOCHARSET Enable any character sets you need in File Systems/Native Language Support. - -config VFAT_NO_CREATE_WITH_LONGNAMES - bool "Disable creating files with long names" - depends on VFAT_FS - default n - help - Set this to disable support for creating files or directories with - names longer than 8.3 (the original DOS maximum file name length) - e.g. naming a file FILE1234.TXT would be allowed but creating or - renaming a file to FILE12345.TXT or FILE1234.TEXT would not - be permitted. Reading files with long file names is still permitted. diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 9c94efd35320..7a6f02caf286 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -372,8 +372,7 @@ static int fat_parse_short(struct super_block *sb, dotoffset = 1; } - memcpy(work, de->name, sizeof(de->name)); - + memcpy(work, de->name, sizeof(work)); /* see namei.c, msdos_format_name */ if (work[0] == 0x05) work[0] = 0xE5; @@ -503,13 +502,6 @@ int fat_search_long(struct inode *inode, const unsigned char *name, goto end_of_dir; } - /* - * The FAT_NO_83NAME flag is used to mark files - * created with no 8.3 short name - */ - if (de->lcase & FAT_NO_83NAME) - goto compare_longname; - /* Never prepend '.' to hidden files here. * That is done only for msdos mounts (and only when * 'dotsOK=yes'); if we are executing here, it is in the @@ -523,7 +515,6 @@ int fat_search_long(struct inode *inode, const unsigned char *name, if (fat_name_match(sbi, name, name_len, bufname, len)) goto found; -compare_longname: if (nr_slots) { void *longname = unicode + FAT_MAX_UNI_CHARS; int size = PATH_MAX - FAT_MAX_UNI_SIZE; @@ -620,8 +611,6 @@ static int __fat_readdir(struct inode *inode, struct file *filp, void *dirent, if (de->attr != ATTR_EXT && IS_FREE(de->name)) goto record_end; } else { - if (de->lcase & FAT_NO_83NAME) - goto record_end; if ((de->attr & ATTR_VOLUME) || IS_FREE(de->name)) goto record_end; } @@ -964,10 +953,6 @@ int fat_scan(struct inode *dir, const unsigned char *name, sinfo->bh = NULL; while (fat_get_short_entry(dir, &sinfo->slot_off, &sinfo->bh, &sinfo->de) >= 0) { - /* skip files marked as having no 8.3 short name */ - if (sinfo->de->lcase & FAT_NO_83NAME) - continue; - if (!strncmp(sinfo->de->name, name, MSDOS_NAME)) { sinfo->slot_off -= sizeof(*sinfo->de); sinfo->nr_slots = 1; diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c index 0c26e0a9b682..2da952036a3d 100644 --- a/fs/fat/namei_vfat.c +++ b/fs/fat/namei_vfat.c @@ -21,7 +21,6 @@ #include #include #include -#include #include "fat.h" /* @@ -329,17 +328,6 @@ static int vfat_create_shortname(struct inode *dir, struct nls_table *nls, int sz = 0, extlen, baselen, i, numtail_baselen, numtail2_baselen; int is_shortname; struct shortname_info base_info, ext_info; - unsigned opts_shortname = opts->shortname; - -#ifdef CONFIG_VFAT_FS_NO_DUALNAMES - /* - * When we do not have dualnames, we want to maximise the - * chance that a file will be able to be represented with just - * a 8.3 entry. We can do that by using the WINNT case - * handling extensions to FAT. - */ - opts_shortname = VFAT_SFN_CREATE_WINNT; -#endif is_shortname = 1; INIT_SHORTNAME_INFO(&base_info); @@ -448,22 +436,13 @@ static int vfat_create_shortname(struct inode *dir, struct nls_table *nls, memcpy(name_res, base, baselen); memcpy(name_res + 8, ext, extlen); *lcase = 0; - -#ifdef CONFIG_VFAT_NO_CREATE_WITH_LONGNAMES - if (is_shortname == 0) - return -ENAMETOOLONG; - if (!base_info.valid || !ext_info.valid) - return -EINVAL; - opts_shortname = VFAT_SFN_CREATE_WINNT; -#endif - if (is_shortname && base_info.valid && ext_info.valid) { if (vfat_find_form(dir, name_res) == 0) return -EEXIST; - if (opts_shortname & VFAT_SFN_CREATE_WIN95) { + if (opts->shortname & VFAT_SFN_CREATE_WIN95) { return (base_info.upper && ext_info.upper); - } else if (opts_shortname & VFAT_SFN_CREATE_WINNT) { + } else if (opts->shortname & VFAT_SFN_CREATE_WINNT) { if ((base_info.upper || base_info.lower) && (ext_info.upper || ext_info.lower)) { if (!base_info.upper && base_info.lower) @@ -602,66 +581,6 @@ xlate_to_uni(const unsigned char *name, int len, unsigned char *outname, return 0; } -#ifdef CONFIG_VFAT_FS_NO_DUALNAMES -/* - * This function creates a dummy 8.3 entry which is as compatible as - * possible with existing FAT devices, while not being a valid - * filename under windows or Linux - */ -static void vfat_build_dummy_83_buffer(struct inode *dir, char *msdos_name, - int is_dir) -{ - /* - * These characters are all invalid in 8.3 names, plus have - * been shown to be harmless on all tested devices - */ - const char invalidchar[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x0B, - 0x0C, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, - 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, - 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x22, 0x2a, - 0x3a, 0x3c, 0x3e, 0x3f, 0x5b, 0x5d, 0x7c }; - int i, tilde_pos, slash_pos; - u32 rand_num = random32(); - - /* We need a '~' in the prefix to make Win98 happy. */ - tilde_pos = rand_num % 8; - rand_num >>= 3; - - /* - * the '/' makes sure that even unpatched Linux systems can't - * get at files by the 8.3 entry. Don't put in a / in - * directories as it can cause problems with some - * photo frames - */ - if (is_dir) - slash_pos = -1; - else { - slash_pos = (tilde_pos + 1 + rand_num % 7) % 8; - rand_num >>= 3; - } - - /* - * fill in the first 8 bytes with invalid characters. Note - * that we need to be careful not to run out of randomness. We - * leave the 3 byte extension in place as some cheap MP3 - * players need them. - */ - for (i = 0; i < 8; i++) { - if (i == tilde_pos) - msdos_name[i] = '~'; - else if (i == slash_pos) - msdos_name[i] = '/'; - else { - msdos_name[i] = - invalidchar[rand_num % sizeof(invalidchar)]; - rand_num /= sizeof(invalidchar); - if (rand_num < sizeof(invalidchar)) - rand_num = random32(); - } - } -} -#endif - static int vfat_build_slots(struct inode *dir, const unsigned char *name, int len, int is_dir, int cluster, struct timespec *ts, @@ -669,19 +588,15 @@ static int vfat_build_slots(struct inode *dir, const unsigned char *name, { struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb); struct fat_mount_options *opts = &sbi->options; + struct msdos_dir_slot *ps; struct msdos_dir_entry *de; - unsigned char lcase; + unsigned char cksum, lcase; unsigned char msdos_name[MSDOS_NAME]; wchar_t *uname; __le16 time, date; u8 time_cs; - int err, ulen, usize; -#ifndef CONFIG_VFAT_NO_CREATE_WITH_LONGNAMES - int i; - struct msdos_dir_slot *ps; - unsigned char cksum; + int err, ulen, usize, i; loff_t offset; -#endif *nr_slots = 0; @@ -708,16 +623,6 @@ static int vfat_build_slots(struct inode *dir, const unsigned char *name, goto shortname; } -#ifdef CONFIG_VFAT_FS_NO_DUALNAMES - printk_once(KERN_INFO - "VFAT: not creating 8.3 short filenames for long names\n"); - vfat_build_dummy_83_buffer(dir, msdos_name, is_dir); - lcase = FAT_NO_83NAME; -#endif - -#ifdef CONFIG_VFAT_NO_CREATE_WITH_LONGNAMES - de = (struct msdos_dir_entry *)slots; -#else /* build the entry of long file name */ cksum = fat_checksum(msdos_name); @@ -735,7 +640,6 @@ static int vfat_build_slots(struct inode *dir, const unsigned char *name, } slots[0].id |= 0x40; de = (struct msdos_dir_entry *)ps; -#endif shortname: /* build the entry of 8.3 alias name */ @@ -1164,11 +1068,7 @@ static struct dentry *vfat_mount(struct file_system_type *fs_type, static struct file_system_type vfat_fs_type = { .owner = THIS_MODULE, -#ifdef CONFIG_VFAT_NO_CREATE_WITH_LONGNAMES - .name = "lfat", -#else .name = "vfat", -#endif .mount = vfat_mount, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV, @@ -1188,9 +1088,6 @@ static void __exit exit_vfat_fs(void) MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("VFAT filesystem support"); MODULE_AUTHOR("Gordon Chaffee"); -#ifdef CONFIG_VFAT_NO_CREATE_WITH_LONGNAMES -MODULE_ALIAS("lfat"); -#endif module_init(init_vfat_fs) module_exit(exit_vfat_fs) diff --git a/fs/file_table.c b/fs/file_table.c index e271e281cdc9..485dc0eddd67 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -36,8 +36,7 @@ struct files_stat_struct files_stat = { .max_files = NR_FILE }; -DEFINE_LGLOCK(files_lglock); -EXPORT_SYMBOL(files_lglock); +DEFINE_STATIC_LGLOCK(files_lglock); /* SLAB cache for file structures */ static struct kmem_cache *filp_cachep __read_mostly; @@ -406,8 +405,6 @@ void file_sb_list_del(struct file *file) } } -EXPORT_SYMBOL(file_sb_list_del); - #ifdef CONFIG_SMP /* diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index e67b13de2ebc..5b1274699b08 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1175,8 +1175,6 @@ static int parse_dirfile(char *buf, size_t nbytes, struct file *file, return -EIO; if (reclen > nbytes) break; - if (memchr(dirent->name, '/', dirent->namelen) != NULL) - return -EIO; over = filldir(dstbuf, dirent->name, dirent->namelen, file->f_pos, dirent->ino, dirent->type); @@ -1325,8 +1323,6 @@ static int parse_dirplusfile(char *buf, size_t nbytes, struct file *file, return -EIO; if (reclen > nbytes) break; - if (memchr(dirent->name, '/', dirent->namelen) != NULL) - return -EIO; if (!over) { /* We fill entries into dstbuf only as much as @@ -1598,7 +1594,6 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr, struct file *file) { struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_req *req; struct fuse_setattr_in inarg; struct fuse_attr_out outarg; @@ -1626,10 +1621,8 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr, if (IS_ERR(req)) return PTR_ERR(req); - if (is_truncate) { + if (is_truncate) fuse_set_nowrite(inode); - set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); - } memset(&inarg, 0, sizeof(inarg)); memset(&outarg, 0, sizeof(outarg)); @@ -1691,14 +1684,12 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr, invalidate_inode_pages2(inode->i_mapping); } - clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); return 0; error: if (is_truncate) fuse_release_nowrite(inode); - clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); return err; } @@ -1762,8 +1753,6 @@ static int fuse_setxattr(struct dentry *entry, const char *name, fc->no_setxattr = 1; err = -EOPNOTSUPP; } - if (!err) - fuse_invalidate_attr(inode); return err; } @@ -1893,8 +1882,6 @@ static int fuse_removexattr(struct dentry *entry, const char *name) fc->no_removexattr = 1; err = -EOPNOTSUPP; } - if (!err) - fuse_invalidate_attr(inode); return err; } diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 4fafb8484bbc..35f281033142 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -630,8 +630,7 @@ static void fuse_read_update_size(struct inode *inode, loff_t size, struct fuse_inode *fi = get_fuse_inode(inode); spin_lock(&fc->lock); - if (attr_ver == fi->attr_version && size < inode->i_size && - !test_bit(FUSE_I_SIZE_UNSTABLE, &fi->state)) { + if (attr_ver == fi->attr_version && size < inode->i_size) { fi->attr_version = ++fc->attr_version; i_size_write(inode, size); } @@ -1034,16 +1033,12 @@ static ssize_t fuse_perform_write(struct file *file, { struct inode *inode = mapping->host; struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_inode *fi = get_fuse_inode(inode); int err = 0; ssize_t res = 0; if (is_bad_inode(inode)) return -EIO; - if (inode->i_size < pos + iov_iter_count(ii)) - set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); - do { struct fuse_req *req; ssize_t count; @@ -1079,7 +1074,6 @@ static ssize_t fuse_perform_write(struct file *file, if (res > 0) fuse_write_update_size(inode, pos); - clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); fuse_invalidate_attr(inode); return res > 0 ? res : err; @@ -1536,6 +1530,7 @@ static int fuse_writepage_locked(struct page *page) inc_bdi_stat(mapping->backing_dev_info, BDI_WRITEBACK); inc_zone_page_state(tmp_page, NR_WRITEBACK_TEMP); + end_page_writeback(page); spin_lock(&fc->lock); list_add(&req->writepages_entry, &fi->writepages); @@ -1543,8 +1538,6 @@ static int fuse_writepage_locked(struct page *page) fuse_flush_writepages(inode); spin_unlock(&fc->lock); - end_page_writeback(page); - return 0; err_free: @@ -2468,7 +2461,6 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, { struct fuse_file *ff = file->private_data; struct inode *inode = file->f_inode; - struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_conn *fc = ff->fc; struct fuse_req *req; struct fuse_fallocate_in inarg = { @@ -2486,20 +2478,10 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, if (lock_inode) { mutex_lock(&inode->i_mutex); - if (mode & FALLOC_FL_PUNCH_HOLE) { - loff_t endbyte = offset + length - 1; - err = filemap_write_and_wait_range(inode->i_mapping, - offset, endbyte); - if (err) - goto out; - - fuse_sync_writes(inode); - } + if (mode & FALLOC_FL_PUNCH_HOLE) + fuse_set_nowrite(inode); } - if (!(mode & FALLOC_FL_KEEP_SIZE)) - set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); - req = fuse_get_req_nopages(fc); if (IS_ERR(req)) { err = PTR_ERR(req); @@ -2532,11 +2514,11 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, fuse_invalidate_attr(inode); out: - if (!(mode & FALLOC_FL_KEEP_SIZE)) - clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); - - if (lock_inode) + if (lock_inode) { + if (mode & FALLOC_FL_PUNCH_HOLE) + fuse_release_nowrite(inode); mutex_unlock(&inode->i_mutex); + } return err; } diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 5ced199b50bb..fde7249a3a96 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -115,8 +115,6 @@ struct fuse_inode { enum { /** Advise readdirplus */ FUSE_I_ADVISE_RDPLUS, - /** An operation changing file size is in progress */ - FUSE_I_SIZE_UNSTABLE, }; struct fuse_conn; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index b5718516825b..9a0cdde14a08 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -201,8 +201,7 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, struct timespec old_mtime; spin_lock(&fc->lock); - if ((attr_version != 0 && fi->attr_version > attr_version) || - test_bit(FUSE_I_SIZE_UNSTABLE, &fi->state)) { + if (attr_version != 0 && fi->attr_version > attr_version) { spin_unlock(&fc->lock); return; } diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 4e5f332f15d9..a3f868ae3fd4 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -916,8 +916,14 @@ static int get_hstate_idx(int page_size_log) return h - hstates; } +static char *hugetlb_dname(struct dentry *dentry, char *buffer, int buflen) +{ + return dynamic_dname(dentry, buffer, buflen, "/%s (deleted)", + dentry->d_name.name); +} + static struct dentry_operations anon_ops = { - .d_dname = simple_dname + .d_dname = hugetlb_dname }; /* diff --git a/fs/inode.c b/fs/inode.c index bff7670240d0..00d5fc3b86e1 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -56,7 +56,6 @@ static struct hlist_head *inode_hashtable __read_mostly; static __cacheline_aligned_in_smp DEFINE_SPINLOCK(inode_hash_lock); __cacheline_aligned_in_smp DEFINE_SPINLOCK(inode_sb_list_lock); -EXPORT_SYMBOL(inode_sb_list_lock); /* * Empty aops. Can be used for the cases where the user does not @@ -1499,7 +1498,7 @@ static int relatime_need_update(struct vfsmount *mnt, struct inode *inode, * This does the actual work of updating an inodes time or version. Must have * had called mnt_want_write() before calling this. */ -int update_time(struct inode *inode, struct timespec *time, int flags) +static int update_time(struct inode *inode, struct timespec *time, int flags) { if (inode->i_op->update_time) return inode->i_op->update_time(inode, time, flags); @@ -1515,7 +1514,6 @@ int update_time(struct inode *inode, struct timespec *time, int flags) mark_inode_dirty_sync(inode); return 0; } -EXPORT_SYMBOL(update_time); /** * touch_atime - update the access time diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index d3705490ff9c..d9b8aebdeb22 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -125,8 +125,8 @@ static void destroy_inodecache(void) static int isofs_remount(struct super_block *sb, int *flags, char *data) { - if (!(*flags & MS_RDONLY)) - return -EROFS; + /* we probably want a lot more here */ + *flags |= MS_RDONLY; return 0; } @@ -779,6 +779,15 @@ static int isofs_fill_super(struct super_block *s, void *data, int silent) */ s->s_maxbytes = 0x80000000000LL; + /* + * The CDROM is read-only, has no nodes (devices) on it, and since + * all of the files appear to be owned by root, we really do not want + * to allow suid. (suid or devices will not show up unless we have + * Rock Ridge extensions) + */ + + s->s_flags |= MS_RDONLY /* | MS_NODEV | MS_NOSUID */; + /* Set this for reference. Its not currently used except on write which we don't have .. */ @@ -1537,9 +1546,6 @@ struct inode *isofs_iget(struct super_block *sb, static struct dentry *isofs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { - /* We don't support read-write mounts */ - if (!(flags & MS_RDONLY)) - return ERR_PTR(-EACCES); return mount_bdev(fs_type, flags, dev_name, data, isofs_fill_super); } diff --git a/fs/jfs/jfs_dtree.c b/fs/jfs/jfs_dtree.c index c450fdb3d78d..0ddbeceafc62 100644 --- a/fs/jfs/jfs_dtree.c +++ b/fs/jfs/jfs_dtree.c @@ -3047,14 +3047,6 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) dir_index = (u32) filp->f_pos; - /* - * NFSv4 reserves cookies 1 and 2 for . and .. so we add - * the value we return to the vfs is one greater than the - * one we use internally. - */ - if (dir_index) - dir_index--; - if (dir_index > 1) { struct dir_table_slot dirtab_slot; @@ -3094,7 +3086,7 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) if (p->header.flag & BT_INTERNAL) { jfs_err("jfs_readdir: bad index table"); DT_PUTPAGE(mp); - filp->f_pos = DIREND; + filp->f_pos = -1; return 0; } } else { @@ -3102,7 +3094,7 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) /* * self "." */ - filp->f_pos = 1; + filp->f_pos = 0; if (filldir(dirent, ".", 1, 0, ip->i_ino, DT_DIR)) return 0; @@ -3110,7 +3102,7 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) /* * parent ".." */ - filp->f_pos = 2; + filp->f_pos = 1; if (filldir(dirent, "..", 2, 1, PARENT(ip), DT_DIR)) return 0; @@ -3131,25 +3123,24 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) /* * Legacy filesystem - OS/2 & Linux JFS < 0.3.6 * - * pn = 0; index = 1: First entry "." - * pn = 0; index = 2: Second entry ".." + * pn = index = 0: First entry "." + * pn = 0; index = 1: Second entry ".." * pn > 0: Real entries, pn=1 -> leftmost page * pn = index = -1: No more entries */ dtpos = filp->f_pos; - if (dtpos < 2) { + if (dtpos == 0) { /* build "." entry */ - filp->f_pos = 1; if (filldir(dirent, ".", 1, filp->f_pos, ip->i_ino, DT_DIR)) return 0; - dtoffset->index = 2; + dtoffset->index = 1; filp->f_pos = dtpos; } if (dtoffset->pn == 0) { - if (dtoffset->index == 2) { + if (dtoffset->index == 1) { /* build ".." entry */ if (filldir(dirent, "..", 2, filp->f_pos, @@ -3242,12 +3233,6 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) } jfs_dirent->position = unique_pos++; } - /* - * We add 1 to the index because we may - * use a value of 2 internally, and NFSv4 - * doesn't like that. - */ - jfs_dirent->position++; } else { jfs_dirent->position = dtpos; len = min(d_namleft, DTLHDRDATALEN_LEGACY); diff --git a/fs/namespace.c b/fs/namespace.c index f32874bb5aad..7b1ca9ba0b0a 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -54,7 +54,6 @@ EXPORT_SYMBOL_GPL(fs_kobj); * tree or hash is modified or when a vfsmount structure is modified. */ DEFINE_BRLOCK(vfsmount_lock); -EXPORT_SYMBOL(vfsmount_lock); static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry) { @@ -428,7 +427,6 @@ void __mnt_drop_write(struct vfsmount *mnt) mnt_dec_writers(real_mount(mnt)); preempt_enable(); } -EXPORT_SYMBOL_GPL(__mnt_drop_write); /** * mnt_drop_write - give up write access to a mount @@ -1431,7 +1429,7 @@ struct vfsmount *collect_mounts(struct path *path) CL_COPY_ALL | CL_PRIVATE); namespace_unlock(); if (IS_ERR(tree)) - return ERR_CAST(tree); + return NULL; return &tree->mnt; } @@ -1458,7 +1456,6 @@ int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg, } return 0; } -EXPORT_SYMBOL(iterate_mounts); static void cleanup_group_ids(struct mount *mnt, struct mount *end) { diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index 169e5ab141c4..13ca196385f5 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -39,19 +39,6 @@ config NFS_V2 If unsure, say Y. -config NFS_DEF_FILE_IO_SIZE - int "Default size for NFS I/O read and write at runtime" - depends on NFS_FS - default "4096" - help - To change the default rsize and wsize supported by the NFS client, - adjust NFS_DEF_FILE_IO_SIZE. 64KB is a typical maximum, but some - servers can support a megabyte or more. The default is left at 4096 - bytes, which is reasonable for NFS over UDP, however, for some - systems, setting a smaller value like 1024 can work around - limitations in the driver or hardware and result in overall - improved performance. - config NFS_V3 tristate "NFS client support for NFS version 3" depends on NFS_FS diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 818697c838c3..c513b0cc835f 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -9,6 +9,7 @@ * 2 of the License, or (at your option) any later version. */ + #include #include #include @@ -69,7 +70,7 @@ static const struct rpc_version *nfs_version[5] = { [4] = NULL, }; -struct rpc_program nfs_program = { +const struct rpc_program nfs_program = { .name = "nfs", .number = NFS_PROGRAM, .nrvers = ARRAY_SIZE(nfs_version), @@ -160,7 +161,6 @@ struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) try_module_get(clp->cl_nfs_mod->owner); clp->rpc_ops = clp->cl_nfs_mod->rpc_ops; - clp->nfs_prog = cl_init->nfs_prog; atomic_set(&clp->cl_count, 1); clp->cl_cons_state = NFS_CS_INITING; @@ -418,9 +418,6 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat /* Match nfsv4 minorversion */ if (clp->cl_minorversion != data->minorversion) continue; - if (clp->nfs_prog != data->nfs_prog) - continue; - /* Match the full socket address */ if (!nfs_sockaddr_cmp(sap, clap)) continue; @@ -602,10 +599,6 @@ int nfs_create_rpc_client(struct nfs_client *clp, if (!IS_ERR(clp->cl_rpcclient)) return 0; - if (clp->nfs_prog) - nfs_program.number = clp->nfs_prog; - else - nfs_program.number = NFS_PROGRAM; clnt = rpc_create(&args); if (IS_ERR(clnt)) { dprintk("%s: cannot create RPC client. Error = %ld\n", @@ -749,7 +742,6 @@ static int nfs_init_server(struct nfs_server *server, .nfs_mod = nfs_mod, .proto = data->nfs_server.protocol, .net = data->net, - .nfs_prog = data->nfs_prog, }; struct rpc_timeout timeparms; struct nfs_client *clp; diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 13a36e2155bc..91e59a39fc08 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -73,7 +73,6 @@ struct nfs_client_initdata { int proto; u32 minorversion; struct net *net; - int nfs_prog; }; /* @@ -85,8 +84,6 @@ struct nfs_parsed_mount_data { unsigned int timeo, retrans; unsigned int acregmin, acregmax, acdirmin, acdirmax; - int nfs_prog; - int mount_prog; unsigned int namlen; unsigned int options; unsigned int bsize; @@ -143,11 +140,11 @@ struct nfs_mount_info { struct nfs_fh *mntfh; }; -extern int nfs_mount(struct nfs_mount_request *info, int prog); +extern int nfs_mount(struct nfs_mount_request *info); extern void nfs_umount(const struct nfs_mount_request *info); /* client.c */ -extern struct rpc_program nfs_program; +extern const struct rpc_program nfs_program; extern void nfs_clients_init(struct net *net); extern struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *); int nfs_create_rpc_client(struct nfs_client *, const struct rpc_timeout *, rpc_authflavor_t); diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c index 09eeebcd3b5a..91a6faf811ac 100644 --- a/fs/nfs/mount_clnt.c +++ b/fs/nfs/mount_clnt.c @@ -67,7 +67,7 @@ enum { MOUNTPROC3_EXPORT = 5, }; -static struct rpc_program mnt_program; +static const struct rpc_program mnt_program; /* * Defined by OpenGroup XNFS Version 3W, chapter 8 @@ -141,7 +141,7 @@ struct mnt_fhstatus { * * Uses default timeout parameters specified by underlying transport. */ -int nfs_mount(struct nfs_mount_request *info, int m_prog) +int nfs_mount(struct nfs_mount_request *info) { struct mountres result = { .fh = info->fh, @@ -175,7 +175,6 @@ int nfs_mount(struct nfs_mount_request *info, int m_prog) if (info->noresvport) args.flags |= RPC_CLNT_CREATE_NONPRIVPORT; - mnt_program.number = m_prog; mnt_clnt = rpc_create(&args); if (IS_ERR(mnt_clnt)) goto out_clnt_err; @@ -515,7 +514,7 @@ static const struct rpc_version *mnt_version[] = { static struct rpc_stat mnt_stats; -static struct rpc_program mnt_program = { +static const struct rpc_program mnt_program = { .name = "mount", .number = NFS_MNT_PROGRAM, .nrvers = ARRAY_SIZE(mnt_version), diff --git a/fs/nfs/nfs4filelayoutdev.c b/fs/nfs/nfs4filelayoutdev.c index 678cb8964532..661a0f611215 100644 --- a/fs/nfs/nfs4filelayoutdev.c +++ b/fs/nfs/nfs4filelayoutdev.c @@ -797,34 +797,34 @@ nfs4_fl_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx) struct nfs4_file_layout_dsaddr *dsaddr = FILELAYOUT_LSEG(lseg)->dsaddr; struct nfs4_pnfs_ds *ds = dsaddr->ds_list[ds_idx]; struct nfs4_deviceid_node *devid = FILELAYOUT_DEVID_NODE(lseg); - struct nfs4_pnfs_ds *ret = ds; + + if (filelayout_test_devid_unavailable(devid)) + return NULL; if (ds == NULL) { printk(KERN_ERR "NFS: %s: No data server for offset index %d\n", __func__, ds_idx); filelayout_mark_devid_invalid(devid); - goto out; + return NULL; } if (ds->ds_clp) - goto out_test_devid; + return ds; if (test_and_set_bit(NFS4DS_CONNECTING, &ds->ds_state) == 0) { struct nfs_server *s = NFS_SERVER(lseg->pls_layout->plh_inode); int err; err = nfs4_ds_connect(s, ds); - if (err) + if (err) { nfs4_mark_deviceid_unavailable(devid); + ds = NULL; + } nfs4_clear_ds_conn_bit(ds); } else { /* Either ds is connected, or ds is NULL */ nfs4_wait_ds_connect(ds); } -out_test_devid: - if (filelayout_test_devid_unavailable(devid)) - ret = NULL; -out: - return ret; + return ds; } module_param(dataserver_retrans, uint, 0644); diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 5712e3b602cb..2d7525fbcf25 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -103,8 +103,6 @@ enum { Opt_mountport, Opt_mountvers, Opt_minorversion, - Opt_mountprog, - Opt_nfsprog, /* Mount options that take string arguments */ Opt_nfsvers, @@ -170,8 +168,6 @@ static const match_table_t nfs_mount_option_tokens = { { Opt_mountport, "mountport=%s" }, { Opt_mountvers, "mountvers=%s" }, { Opt_minorversion, "minorversion=%s" }, - { Opt_mountprog, "mountprog=%s" }, - { Opt_nfsprog, "nfsprog=%s" }, { Opt_nfsvers, "nfsvers=%s" }, { Opt_nfsvers, "vers=%s" }, @@ -921,8 +917,6 @@ static struct nfs_parsed_mount_data *nfs_alloc_parsed_mount_data(void) data->acregmax = NFS_DEF_ACREGMAX; data->acdirmin = NFS_DEF_ACDIRMIN; data->acdirmax = NFS_DEF_ACDIRMAX; - data->nfs_prog = NFS_PROGRAM; - data->mount_prog = NFS_MNT_PROGRAM; data->mount_server.port = NFS_UNSPEC_PORT; data->nfs_server.port = NFS_UNSPEC_PORT; data->nfs_server.protocol = XPRT_TRANSPORT_TCP; @@ -1322,26 +1316,6 @@ static int nfs_parse_mount_options(char *raw, goto out_invalid_value; mnt->acdirmax = option; break; - case Opt_mountprog: - string = match_strdup(args); - if (string == NULL) - goto out_nomem; - rc = strict_strtoul(string, 10, &option); - kfree(string); - if (rc != 0) - goto out_invalid_value; - mnt->mount_prog = option; - break; - case Opt_nfsprog: - string = match_strdup(args); - if (string == NULL) - goto out_nomem; - rc = strict_strtoul(string, 10, &option); - kfree(string); - if (rc != 0) - goto out_invalid_value; - mnt->nfs_prog = option; - break; case Opt_actimeo: if (nfs_get_option_ul(args, &option)) goto out_invalid_value; @@ -1775,7 +1749,7 @@ static int nfs_request_mount(struct nfs_parsed_mount_data *args, * Now ask the mount server to map our export path * to a file handle. */ - status = nfs_mount(&request,args->mount_prog); + status = nfs_mount(&request); if (status != 0) { dfprintk(MOUNT, "NFS: unable to mount server %s, error %d\n", request.hostname, status); @@ -1902,7 +1876,6 @@ static int nfs23_validate_mount_data(void *options, { struct nfs_mount_data *data = (struct nfs_mount_data *)options; struct sockaddr *sap = (struct sockaddr *)&args->nfs_server.address; - args->nfs_prog = NFS_PROGRAM; if (data == NULL) goto out_no_data; @@ -1923,8 +1896,6 @@ static int nfs23_validate_mount_data(void *options, goto out_no_sec; case 5: memset(data->context, 0, sizeof(data->context)); - case 7: - args->nfs_prog = (data->version >= 7) ? data->nfs_prog : NFS_PROGRAM; case 6: if (data->flags & NFS_MOUNT_VER3) { if (data->root.size > NFS3_FHSIZE || data->root.size == 0) @@ -2654,8 +2625,6 @@ static int nfs4_validate_mount_data(void *options, args->version = 4; - args->nfs_prog = NFS_PROGRAM; - switch (data->version) { case 1: if (data->host_addrlen > sizeof(args->nfs_server.address)) diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c index da276640f776..0ba679866e50 100644 --- a/fs/nilfs2/page.c +++ b/fs/nilfs2/page.c @@ -94,7 +94,6 @@ void nilfs_forget_buffer(struct buffer_head *bh) clear_buffer_nilfs_volatile(bh); clear_buffer_nilfs_checked(bh); clear_buffer_nilfs_redirected(bh); - clear_buffer_async_write(bh); clear_buffer_dirty(bh); if (nilfs_page_buffers_clean(page)) __nilfs_clear_page_dirty(page); @@ -430,7 +429,6 @@ void nilfs_clear_dirty_page(struct page *page, bool silent) "discard block %llu, size %zu", (u64)bh->b_blocknr, bh->b_size); } - clear_buffer_async_write(bh); clear_buffer_dirty(bh); clear_buffer_nilfs_volatile(bh); clear_buffer_nilfs_checked(bh); diff --git a/fs/nilfs2/segbuf.c b/fs/nilfs2/segbuf.c index 2d8be51f90dc..dc9a913784ab 100644 --- a/fs/nilfs2/segbuf.c +++ b/fs/nilfs2/segbuf.c @@ -345,7 +345,8 @@ static void nilfs_end_bio_write(struct bio *bio, int err) if (err == -EOPNOTSUPP) { set_bit(BIO_EOPNOTSUPP, &bio->bi_flags); - /* to be detected by nilfs_segbuf_submit_bio() */ + bio_put(bio); + /* to be detected by submit_seg_bio() */ } if (!uptodate) @@ -376,12 +377,12 @@ static int nilfs_segbuf_submit_bio(struct nilfs_segment_buffer *segbuf, bio->bi_private = segbuf; bio_get(bio); submit_bio(mode, bio); - segbuf->sb_nbio++; if (bio_flagged(bio, BIO_EOPNOTSUPP)) { bio_put(bio); err = -EOPNOTSUPP; goto failed; } + segbuf->sb_nbio++; bio_put(bio); wi->bio = NULL; diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index cbd66188a28b..a5752a589932 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -665,7 +665,7 @@ static size_t nilfs_lookup_dirty_data_buffers(struct inode *inode, bh = head = page_buffers(page); do { - if (!buffer_dirty(bh) || buffer_async_write(bh)) + if (!buffer_dirty(bh)) continue; get_bh(bh); list_add_tail(&bh->b_assoc_buffers, listp); @@ -699,8 +699,7 @@ static void nilfs_lookup_dirty_node_buffers(struct inode *inode, for (i = 0; i < pagevec_count(&pvec); i++) { bh = head = page_buffers(pvec.pages[i]); do { - if (buffer_dirty(bh) && - !buffer_async_write(bh)) { + if (buffer_dirty(bh)) { get_bh(bh); list_add_tail(&bh->b_assoc_buffers, listp); @@ -1580,7 +1579,6 @@ static void nilfs_segctor_prepare_write(struct nilfs_sc_info *sci) list_for_each_entry(bh, &segbuf->sb_segsum_buffers, b_assoc_buffers) { - set_buffer_async_write(bh); if (bh->b_page != bd_page) { if (bd_page) { lock_page(bd_page); @@ -1594,7 +1592,6 @@ static void nilfs_segctor_prepare_write(struct nilfs_sc_info *sci) list_for_each_entry(bh, &segbuf->sb_payload_buffers, b_assoc_buffers) { - set_buffer_async_write(bh); if (bh == segbuf->sb_super_root) { if (bh->b_page != bd_page) { lock_page(bd_page); @@ -1680,7 +1677,6 @@ static void nilfs_abort_logs(struct list_head *logs, int err) list_for_each_entry(segbuf, logs, sb_list) { list_for_each_entry(bh, &segbuf->sb_segsum_buffers, b_assoc_buffers) { - clear_buffer_async_write(bh); if (bh->b_page != bd_page) { if (bd_page) end_page_writeback(bd_page); @@ -1690,7 +1686,6 @@ static void nilfs_abort_logs(struct list_head *logs, int err) list_for_each_entry(bh, &segbuf->sb_payload_buffers, b_assoc_buffers) { - clear_buffer_async_write(bh); if (bh == segbuf->sb_super_root) { if (bh->b_page != bd_page) { end_page_writeback(bd_page); @@ -1760,7 +1755,6 @@ static void nilfs_segctor_complete_write(struct nilfs_sc_info *sci) b_assoc_buffers) { set_buffer_uptodate(bh); clear_buffer_dirty(bh); - clear_buffer_async_write(bh); if (bh->b_page != bd_page) { if (bd_page) end_page_writeback(bd_page); @@ -1782,7 +1776,6 @@ static void nilfs_segctor_complete_write(struct nilfs_sc_info *sci) b_assoc_buffers) { set_buffer_uptodate(bh); clear_buffer_dirty(bh); - clear_buffer_async_write(bh); clear_buffer_delay(bh); clear_buffer_nilfs_volatile(bh); clear_buffer_nilfs_redirected(bh); diff --git a/fs/notify/group.c b/fs/notify/group.c index 2ff2a0fd5aea..bd2625bd88b4 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include "fsnotify.h" @@ -66,7 +65,6 @@ void fsnotify_get_group(struct fsnotify_group *group) { atomic_inc(&group->refcnt); } -EXPORT_SYMBOL(fsnotify_get_group); /* * Drop a reference to a group. Free it if it's through. @@ -76,7 +74,6 @@ void fsnotify_put_group(struct fsnotify_group *group) if (atomic_dec_and_test(&group->refcnt)) fsnotify_final_destroy_group(group); } -EXPORT_SYMBOL(fsnotify_put_group); /* * Create a new fsnotify_group and hold a reference for the group returned. @@ -105,7 +102,6 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) return group; } -EXPORT_SYMBOL(fsnotify_alloc_group); int fsnotify_fasync(int fd, struct file *file, int on) { diff --git a/fs/notify/mark.c b/fs/notify/mark.c index a6bb87dcbe3d..fc6b49bf7360 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -115,7 +115,6 @@ void fsnotify_put_mark(struct fsnotify_mark *mark) mark->free_mark(mark); } } -EXPORT_SYMBOL(fsnotify_put_mark); /* * Any time a mark is getting freed we end up here. @@ -198,7 +197,6 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark, fsnotify_destroy_mark_locked(mark, group); mutex_unlock(&group->mark_mutex); } -EXPORT_SYMBOL(fsnotify_destroy_mark); void fsnotify_set_mark_mask_locked(struct fsnotify_mark *mark, __u32 mask) { @@ -283,7 +281,6 @@ int fsnotify_add_mark_locked(struct fsnotify_mark *mark, return ret; } -EXPORT_SYMBOL(fsnotify_add_mark); int fsnotify_add_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, struct inode *inode, struct vfsmount *mnt, int allow_dups) @@ -345,7 +342,6 @@ void fsnotify_init_mark(struct fsnotify_mark *mark, atomic_set(&mark->refcnt, 1); mark->free_mark = free_mark; } -EXPORT_SYMBOL(fsnotify_init_mark); static int fsnotify_mark_destroy(void *ignored) { diff --git a/fs/ocfs2/extent_map.c b/fs/ocfs2/extent_map.c index 846064726682..2487116d0d33 100644 --- a/fs/ocfs2/extent_map.c +++ b/fs/ocfs2/extent_map.c @@ -781,6 +781,7 @@ int ocfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, cpos = map_start >> osb->s_clustersize_bits; mapping_end = ocfs2_clusters_for_bytes(inode->i_sb, map_start + map_len); + mapping_end -= cpos; is_last = 0; while (cpos < mapping_end && !is_last) { u32 fe_flags; diff --git a/fs/open.c b/fs/open.c index be563cdb5aac..8c741002f947 100644 --- a/fs/open.c +++ b/fs/open.c @@ -61,7 +61,6 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, mutex_unlock(&dentry->d_inode->i_mutex); return ret; } -EXPORT_SYMBOL(do_truncate); long vfs_truncate(struct path *path, loff_t length) { diff --git a/fs/proc/nommu.c b/fs/proc/nommu.c index c09e2cfefffd..ccfd99bd1c5a 100644 --- a/fs/proc/nommu.c +++ b/fs/proc/nommu.c @@ -46,10 +46,6 @@ static int nommu_region_show(struct seq_file *m, struct vm_region *region) if (file) { struct inode *inode = file_inode(region->vm_file); - if (region->vm_prfile) { - file = region->vm_prfile; - inode = file_inode(file); - } dev = inode->i_sb->s_dev; ino = inode->i_ino; } diff --git a/fs/proc/root.c b/fs/proc/root.c index 04ec276c7bab..41a6ea93f486 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -110,8 +110,7 @@ static struct dentry *proc_mount(struct file_system_type *fs_type, ns = task_active_pid_ns(current); options = data; - if (!current_user_ns()->may_mount_proc || - !ns_capable(ns->user_ns, CAP_SYS_ADMIN)) + if (!current_user_ns()->may_mount_proc) return ERR_PTR(-EPERM); } diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 6cef36ff2151..65fc60a07c47 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -272,10 +272,6 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) if (file) { struct inode *inode = file_inode(vma->vm_file); - if (vma->vm_prfile) { - file = vma->vm_prfile; - inode = file_inode(file); - } dev = inode->i_sb->s_dev; ino = inode->i_ino; pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT; @@ -1289,8 +1285,6 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid) if (file) { seq_printf(m, " file="); - if (vma->vm_prfile) - file = vma->vm_prfile; seq_path(m, &file->f_path, "\n\t= "); } else if (vma->vm_start <= mm->brk && vma->vm_end >= mm->start_brk) { seq_printf(m, " heap"); diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c index b987a882299a..56123a6f462e 100644 --- a/fs/proc/task_nommu.c +++ b/fs/proc/task_nommu.c @@ -150,10 +150,6 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma, if (file) { struct inode *inode = file_inode(vma->vm_file); - if (vma->vm_prfile) { - file = vma->vm_prfile; - inode = file_inode(file); - } dev = inode->i_sb->s_dev; ino = inode->i_ino; pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT; diff --git a/fs/splice.c b/fs/splice.c index 8d6a045f2980..d37431dd60a1 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -1093,8 +1093,8 @@ EXPORT_SYMBOL(generic_splice_sendpage); /* * Attempt to initiate a splice from pipe to file. */ -long do_splice_from(struct pipe_inode_info *pipe, struct file *out, - loff_t *ppos, size_t len, unsigned int flags) +static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags) { ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); @@ -1120,14 +1120,13 @@ long do_splice_from(struct pipe_inode_info *pipe, struct file *out, file_end_write(out); return ret; } -EXPORT_SYMBOL(do_splice_from); /* * Attempt to initiate a splice from a file to a pipe. */ -long do_splice_to(struct file *in, loff_t *ppos, - struct pipe_inode_info *pipe, size_t len, - unsigned int flags) +static long do_splice_to(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags) { ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); @@ -1147,7 +1146,6 @@ long do_splice_to(struct file *in, loff_t *ppos, return splice_read(in, ppos, pipe, len, flags); } -EXPORT_SYMBOL(do_splice_to); /** * splice_direct_to_actor - splices data directly between two non-pipes diff --git a/fs/statfs.c b/fs/statfs.c index 083dc0ac9140..c219e733f553 100644 --- a/fs/statfs.c +++ b/fs/statfs.c @@ -94,7 +94,7 @@ int user_statfs(const char __user *pathname, struct kstatfs *st) int fd_statfs(int fd, struct kstatfs *st) { - struct fd f = fdget_raw(fd); + struct fd f = fdget(fd); int error = -EBADF; if (f.file) { error = vfs_statfs(&f.file->f_path, st); diff --git a/fs/sysv/super.c b/fs/sysv/super.c index eda10959714f..d0c6a007ce83 100644 --- a/fs/sysv/super.c +++ b/fs/sysv/super.c @@ -487,7 +487,6 @@ static int v7_fill_super(struct super_block *sb, void *data, int silent) sbi->s_sb = sb; sbi->s_block_base = 0; sbi->s_type = FSTYPE_V7; - mutex_init(&sbi->s_lock); sb->s_fs_info = sbi; sb_set_blocksize(sb, 512); diff --git a/fs/udf/super.c b/fs/udf/super.c index 839a2bad7f45..9ac4057a86c9 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -630,12 +630,6 @@ static int udf_remount_fs(struct super_block *sb, int *flags, char *options) struct udf_sb_info *sbi = UDF_SB(sb); int error = 0; - if (sbi->s_lvid_bh) { - int write_rev = le16_to_cpu(udf_sb_lvidiu(sbi)->minUDFWriteRev); - if (write_rev > UDF_MAX_WRITE_VERSION && !(*flags & MS_RDONLY)) - return -EACCES; - } - uopt.flags = sbi->s_flags; uopt.uid = sbi->s_uid; uopt.gid = sbi->s_gid; @@ -655,6 +649,12 @@ static int udf_remount_fs(struct super_block *sb, int *flags, char *options) sbi->s_dmode = uopt.dmode; write_unlock(&sbi->s_cred_lock); + if (sbi->s_lvid_bh) { + int write_rev = le16_to_cpu(udf_sb_lvidiu(sbi)->minUDFWriteRev); + if (write_rev > UDF_MAX_WRITE_VERSION) + *flags |= MS_RDONLY; + } + if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) goto out_unlock; @@ -843,38 +843,27 @@ static int udf_find_fileset(struct super_block *sb, return 1; } -/* - * Load primary Volume Descriptor Sequence - * - * Return <0 on error, 0 on success. -EAGAIN is special meaning next sequence - * should be tried. - */ static int udf_load_pvoldesc(struct super_block *sb, sector_t block) { struct primaryVolDesc *pvoldesc; struct ustr *instr, *outstr; struct buffer_head *bh; uint16_t ident; - int ret = -ENOMEM; + int ret = 1; instr = kmalloc(sizeof(struct ustr), GFP_NOFS); if (!instr) - return -ENOMEM; + return 1; outstr = kmalloc(sizeof(struct ustr), GFP_NOFS); if (!outstr) goto out1; bh = udf_read_tagged(sb, block, block, &ident); - if (!bh) { - ret = -EAGAIN; + if (!bh) goto out2; - } - if (ident != TAG_IDENT_PVD) { - ret = -EIO; - goto out_bh; - } + BUG_ON(ident != TAG_IDENT_PVD); pvoldesc = (struct primaryVolDesc *)bh->b_data; @@ -900,9 +889,8 @@ static int udf_load_pvoldesc(struct super_block *sb, sector_t block) if (udf_CS0toUTF8(outstr, instr)) udf_debug("volSetIdent[] = '%s'\n", outstr->u_name); - ret = 0; -out_bh: brelse(bh); + ret = 0; out2: kfree(outstr); out1: @@ -959,7 +947,7 @@ static int udf_load_metadata_files(struct super_block *sb, int partition) if (mdata->s_mirror_fe == NULL) { udf_err(sb, "Both metadata and mirror metadata inode efe can not found\n"); - return -EIO; + goto error_exit; } } @@ -976,18 +964,23 @@ static int udf_load_metadata_files(struct super_block *sb, int partition) addr.logicalBlockNum, addr.partitionReferenceNum); mdata->s_bitmap_fe = udf_iget(sb, &addr); + if (mdata->s_bitmap_fe == NULL) { if (sb->s_flags & MS_RDONLY) udf_warn(sb, "bitmap inode efe not found but it's ok since the disc is mounted read-only\n"); else { udf_err(sb, "bitmap inode efe not found and attempted read-write mount\n"); - return -EIO; + goto error_exit; } } } udf_debug("udf_load_metadata_files Ok\n"); + return 0; + +error_exit: + return 1; } static void udf_load_fileset(struct super_block *sb, struct buffer_head *bh, @@ -1076,7 +1069,7 @@ static int udf_fill_partdesc_info(struct super_block *sb, if (!map->s_uspace.s_table) { udf_debug("cannot load unallocSpaceTable (part %d)\n", p_index); - return -EIO; + return 1; } map->s_partition_flags |= UDF_PART_FLAG_UNALLOC_TABLE; udf_debug("unallocSpaceTable (part %d) @ %ld\n", @@ -1086,7 +1079,7 @@ static int udf_fill_partdesc_info(struct super_block *sb, if (phd->unallocSpaceBitmap.extLength) { struct udf_bitmap *bitmap = udf_sb_alloc_bitmap(sb, p_index); if (!bitmap) - return -ENOMEM; + return 1; map->s_uspace.s_bitmap = bitmap; bitmap->s_extPosition = le32_to_cpu( phd->unallocSpaceBitmap.extPosition); @@ -1109,7 +1102,7 @@ static int udf_fill_partdesc_info(struct super_block *sb, if (!map->s_fspace.s_table) { udf_debug("cannot load freedSpaceTable (part %d)\n", p_index); - return -EIO; + return 1; } map->s_partition_flags |= UDF_PART_FLAG_FREED_TABLE; @@ -1120,7 +1113,7 @@ static int udf_fill_partdesc_info(struct super_block *sb, if (phd->freedSpaceBitmap.extLength) { struct udf_bitmap *bitmap = udf_sb_alloc_bitmap(sb, p_index); if (!bitmap) - return -ENOMEM; + return 1; map->s_fspace.s_bitmap = bitmap; bitmap->s_extPosition = le32_to_cpu( phd->freedSpaceBitmap.extPosition); @@ -1172,7 +1165,7 @@ static int udf_load_vat(struct super_block *sb, int p_index, int type1_index) udf_find_vat_block(sb, p_index, type1_index, blocks - 1); } if (!sbi->s_vat_inode) - return -EIO; + return 1; if (map->s_partition_type == UDF_VIRTUAL_MAP15) { map->s_type_specific.s_virtual.s_start_offset = 0; @@ -1184,7 +1177,7 @@ static int udf_load_vat(struct super_block *sb, int p_index, int type1_index) pos = udf_block_map(sbi->s_vat_inode, 0); bh = sb_bread(sb, pos); if (!bh) - return -EIO; + return 1; vat20 = (struct virtualAllocationTable20 *)bh->b_data; } else { vat20 = (struct virtualAllocationTable20 *) @@ -1202,12 +1195,6 @@ static int udf_load_vat(struct super_block *sb, int p_index, int type1_index) return 0; } -/* - * Load partition descriptor block - * - * Returns <0 on error, 0 on success, -EAGAIN is special - try next descriptor - * sequence. - */ static int udf_load_partdesc(struct super_block *sb, sector_t block) { struct buffer_head *bh; @@ -1217,15 +1204,13 @@ static int udf_load_partdesc(struct super_block *sb, sector_t block) int i, type1_idx; uint16_t partitionNumber; uint16_t ident; - int ret; + int ret = 0; bh = udf_read_tagged(sb, block, block, &ident); if (!bh) - return -EAGAIN; - if (ident != TAG_IDENT_PD) { - ret = 0; + return 1; + if (ident != TAG_IDENT_PD) goto out_bh; - } p = (struct partitionDesc *)bh->b_data; partitionNumber = le16_to_cpu(p->partitionNumber); @@ -1244,13 +1229,10 @@ static int udf_load_partdesc(struct super_block *sb, sector_t block) if (i >= sbi->s_partitions) { udf_debug("Partition (%d) not found in partition map\n", partitionNumber); - ret = 0; goto out_bh; } ret = udf_fill_partdesc_info(sb, p, i); - if (ret < 0) - goto out_bh; /* * Now rescan for VIRTUAL or METADATA partitions when SPARABLE and @@ -1267,37 +1249,32 @@ static int udf_load_partdesc(struct super_block *sb, sector_t block) break; } - if (i >= sbi->s_partitions) { - ret = 0; + if (i >= sbi->s_partitions) goto out_bh; - } ret = udf_fill_partdesc_info(sb, p, i); - if (ret < 0) + if (ret) goto out_bh; if (map->s_partition_type == UDF_METADATA_MAP25) { ret = udf_load_metadata_files(sb, i); - if (ret < 0) { + if (ret) { udf_err(sb, "error loading MetaData partition map %d\n", i); goto out_bh; } } else { - /* - * If we have a partition with virtual map, we don't handle - * writing to it (we overwrite blocks instead of relocating - * them). - */ - if (!(sb->s_flags & MS_RDONLY)) { - ret = -EACCES; - goto out_bh; - } ret = udf_load_vat(sb, i, type1_idx); - if (ret < 0) + if (ret) goto out_bh; + /* + * Mark filesystem read-only if we have a partition with + * virtual map since we don't handle writing to it (we + * overwrite blocks instead of relocating them). + */ + sb->s_flags |= MS_RDONLY; + pr_notice("Filesystem marked read-only because writing to pseudooverwrite partition is not implemented\n"); } - ret = 0; out_bh: /* In case loading failed, we handle cleanup in udf_fill_super */ brelse(bh); @@ -1363,11 +1340,11 @@ static int udf_load_logicalvol(struct super_block *sb, sector_t block, uint16_t ident; struct buffer_head *bh; unsigned int table_len; - int ret; + int ret = 0; bh = udf_read_tagged(sb, block, block, &ident); if (!bh) - return -EAGAIN; + return 1; BUG_ON(ident != TAG_IDENT_LVD); lvd = (struct logicalVolDesc *)bh->b_data; table_len = le32_to_cpu(lvd->mapTableLength); @@ -1375,7 +1352,7 @@ static int udf_load_logicalvol(struct super_block *sb, sector_t block, udf_err(sb, "error loading logical volume descriptor: " "Partition table too long (%u > %lu)\n", table_len, sb->s_blocksize - sizeof(*lvd)); - ret = -EIO; + ret = 1; goto out_bh; } @@ -1419,10 +1396,11 @@ static int udf_load_logicalvol(struct super_block *sb, sector_t block, } else if (!strncmp(upm2->partIdent.ident, UDF_ID_SPARABLE, strlen(UDF_ID_SPARABLE))) { - ret = udf_load_sparable_map(sb, map, - (struct sparablePartitionMap *)gpm); - if (ret < 0) + if (udf_load_sparable_map(sb, map, + (struct sparablePartitionMap *)gpm) < 0) { + ret = 1; goto out_bh; + } } else if (!strncmp(upm2->partIdent.ident, UDF_ID_METADATA, strlen(UDF_ID_METADATA))) { @@ -1487,7 +1465,7 @@ static int udf_load_logicalvol(struct super_block *sb, sector_t block, } if (lvd->integritySeqExt.extLength) udf_load_logicalvolint(sb, leea_to_cpu(lvd->integritySeqExt)); - ret = 0; + out_bh: brelse(bh); return ret; @@ -1525,18 +1503,22 @@ static void udf_load_logicalvolint(struct super_block *sb, struct kernel_extent_ } /* - * Process a main/reserve volume descriptor sequence. - * @block First block of first extent of the sequence. - * @lastblock Lastblock of first extent of the sequence. - * @fileset There we store extent containing root fileset + * udf_process_sequence + * + * PURPOSE + * Process a main/reserve volume descriptor sequence. + * + * PRE-CONDITIONS + * sb Pointer to _locked_ superblock. + * block First block of first extent of the sequence. + * lastblock Lastblock of first extent of the sequence. * - * Returns <0 on error, 0 on success. -EAGAIN is special - try next descriptor - * sequence + * HISTORY + * July 1, 1997 - Andrew E. Mileski + * Written, tested, and released. */ -static noinline int udf_process_sequence( - struct super_block *sb, - sector_t block, sector_t lastblock, - struct kernel_lb_addr *fileset) +static noinline int udf_process_sequence(struct super_block *sb, long block, + long lastblock, struct kernel_lb_addr *fileset) { struct buffer_head *bh = NULL; struct udf_vds_record vds[VDS_POS_LENGTH]; @@ -1547,7 +1529,6 @@ static noinline int udf_process_sequence( uint32_t vdsn; uint16_t ident; long next_s = 0, next_e = 0; - int ret; memset(vds, 0, sizeof(struct udf_vds_record) * VDS_POS_LENGTH); @@ -1562,7 +1543,7 @@ static noinline int udf_process_sequence( udf_err(sb, "Block %llu of volume descriptor sequence is corrupted or we could not read it\n", (unsigned long long)block); - return -EAGAIN; + return 1; } /* Process each descriptor (ISO 13346 3/8.3-8.4) */ @@ -1635,19 +1616,14 @@ static noinline int udf_process_sequence( */ if (!vds[VDS_POS_PRIMARY_VOL_DESC].block) { udf_err(sb, "Primary Volume Descriptor not found!\n"); - return -EAGAIN; - } - ret = udf_load_pvoldesc(sb, vds[VDS_POS_PRIMARY_VOL_DESC].block); - if (ret < 0) - return ret; - - if (vds[VDS_POS_LOGICAL_VOL_DESC].block) { - ret = udf_load_logicalvol(sb, - vds[VDS_POS_LOGICAL_VOL_DESC].block, - fileset); - if (ret < 0) - return ret; + return 1; } + if (udf_load_pvoldesc(sb, vds[VDS_POS_PRIMARY_VOL_DESC].block)) + return 1; + + if (vds[VDS_POS_LOGICAL_VOL_DESC].block && udf_load_logicalvol(sb, + vds[VDS_POS_LOGICAL_VOL_DESC].block, fileset)) + return 1; if (vds[VDS_POS_PARTITION_DESC].block) { /* @@ -1656,27 +1632,19 @@ static noinline int udf_process_sequence( */ for (block = vds[VDS_POS_PARTITION_DESC].block; block < vds[VDS_POS_TERMINATING_DESC].block; - block++) { - ret = udf_load_partdesc(sb, block); - if (ret < 0) - return ret; - } + block++) + if (udf_load_partdesc(sb, block)) + return 1; } return 0; } -/* - * Load Volume Descriptor Sequence described by anchor in bh - * - * Returns <0 on error, 0 on success - */ static int udf_load_sequence(struct super_block *sb, struct buffer_head *bh, struct kernel_lb_addr *fileset) { struct anchorVolDescPtr *anchor; - sector_t main_s, main_e, reserve_s, reserve_e; - int ret; + long main_s, main_e, reserve_s, reserve_e; anchor = (struct anchorVolDescPtr *)bh->b_data; @@ -1694,26 +1662,18 @@ static int udf_load_sequence(struct super_block *sb, struct buffer_head *bh, /* Process the main & reserve sequences */ /* responsible for finding the PartitionDesc(s) */ - ret = udf_process_sequence(sb, main_s, main_e, fileset); - if (ret != -EAGAIN) - return ret; + if (!udf_process_sequence(sb, main_s, main_e, fileset)) + return 1; udf_sb_free_partitions(sb); - ret = udf_process_sequence(sb, reserve_s, reserve_e, fileset); - if (ret < 0) { - udf_sb_free_partitions(sb); - /* No sequence was OK, return -EIO */ - if (ret == -EAGAIN) - ret = -EIO; - } - return ret; + if (!udf_process_sequence(sb, reserve_s, reserve_e, fileset)) + return 1; + udf_sb_free_partitions(sb); + return 0; } /* * Check whether there is an anchor block in the given block and * load Volume Descriptor Sequence if so. - * - * Returns <0 on error, 0 on success, -EAGAIN is special - try next anchor - * block */ static int udf_check_anchor_block(struct super_block *sb, sector_t block, struct kernel_lb_addr *fileset) @@ -1725,40 +1685,33 @@ static int udf_check_anchor_block(struct super_block *sb, sector_t block, if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV) && udf_fixed_to_variable(block) >= sb->s_bdev->bd_inode->i_size >> sb->s_blocksize_bits) - return -EAGAIN; + return 0; bh = udf_read_tagged(sb, block, block, &ident); if (!bh) - return -EAGAIN; + return 0; if (ident != TAG_IDENT_AVDP) { brelse(bh); - return -EAGAIN; + return 0; } ret = udf_load_sequence(sb, bh, fileset); brelse(bh); return ret; } -/* - * Search for an anchor volume descriptor pointer. - * - * Returns < 0 on error, 0 on success. -EAGAIN is special - try next set - * of anchors. - */ -static int udf_scan_anchors(struct super_block *sb, sector_t *lastblock, - struct kernel_lb_addr *fileset) +/* Search for an anchor volume descriptor pointer */ +static sector_t udf_scan_anchors(struct super_block *sb, sector_t lastblock, + struct kernel_lb_addr *fileset) { sector_t last[6]; int i; struct udf_sb_info *sbi = UDF_SB(sb); int last_count = 0; - int ret; /* First try user provided anchor */ if (sbi->s_anchor) { - ret = udf_check_anchor_block(sb, sbi->s_anchor, fileset); - if (ret != -EAGAIN) - return ret; + if (udf_check_anchor_block(sb, sbi->s_anchor, fileset)) + return lastblock; } /* * according to spec, anchor is in either: @@ -1767,46 +1720,39 @@ static int udf_scan_anchors(struct super_block *sb, sector_t *lastblock, * lastblock * however, if the disc isn't closed, it could be 512. */ - ret = udf_check_anchor_block(sb, sbi->s_session + 256, fileset); - if (ret != -EAGAIN) - return ret; + if (udf_check_anchor_block(sb, sbi->s_session + 256, fileset)) + return lastblock; /* * The trouble is which block is the last one. Drives often misreport * this so we try various possibilities. */ - last[last_count++] = *lastblock; - if (*lastblock >= 1) - last[last_count++] = *lastblock - 1; - last[last_count++] = *lastblock + 1; - if (*lastblock >= 2) - last[last_count++] = *lastblock - 2; - if (*lastblock >= 150) - last[last_count++] = *lastblock - 150; - if (*lastblock >= 152) - last[last_count++] = *lastblock - 152; + last[last_count++] = lastblock; + if (lastblock >= 1) + last[last_count++] = lastblock - 1; + last[last_count++] = lastblock + 1; + if (lastblock >= 2) + last[last_count++] = lastblock - 2; + if (lastblock >= 150) + last[last_count++] = lastblock - 150; + if (lastblock >= 152) + last[last_count++] = lastblock - 152; for (i = 0; i < last_count; i++) { if (last[i] >= sb->s_bdev->bd_inode->i_size >> sb->s_blocksize_bits) continue; - ret = udf_check_anchor_block(sb, last[i], fileset); - if (ret != -EAGAIN) { - if (!ret) - *lastblock = last[i]; - return ret; - } + if (udf_check_anchor_block(sb, last[i], fileset)) + return last[i]; if (last[i] < 256) continue; - ret = udf_check_anchor_block(sb, last[i] - 256, fileset); - if (ret != -EAGAIN) { - if (!ret) - *lastblock = last[i]; - return ret; - } + if (udf_check_anchor_block(sb, last[i] - 256, fileset)) + return last[i]; } /* Finally try block 512 in case media is open */ - return udf_check_anchor_block(sb, sbi->s_session + 512, fileset); + if (udf_check_anchor_block(sb, sbi->s_session + 512, fileset)) + return last[0]; + return 0; } /* @@ -1814,59 +1760,54 @@ static int udf_scan_anchors(struct super_block *sb, sector_t *lastblock, * area specified by it. The function expects sbi->s_lastblock to be the last * block on the media. * - * Return <0 on error, 0 if anchor found. -EAGAIN is special meaning anchor - * was not found. + * Return 1 if ok, 0 if not found. + * */ static int udf_find_anchor(struct super_block *sb, struct kernel_lb_addr *fileset) { + sector_t lastblock; struct udf_sb_info *sbi = UDF_SB(sb); - sector_t lastblock = sbi->s_last_block; - int ret; - ret = udf_scan_anchors(sb, &lastblock, fileset); - if (ret != -EAGAIN) + lastblock = udf_scan_anchors(sb, sbi->s_last_block, fileset); + if (lastblock) goto out; /* No anchor found? Try VARCONV conversion of block numbers */ UDF_SET_FLAG(sb, UDF_FLAG_VARCONV); - lastblock = udf_variable_to_fixed(sbi->s_last_block); /* Firstly, we try to not convert number of the last block */ - ret = udf_scan_anchors(sb, &lastblock, fileset); - if (ret != -EAGAIN) + lastblock = udf_scan_anchors(sb, + udf_variable_to_fixed(sbi->s_last_block), + fileset); + if (lastblock) goto out; - lastblock = sbi->s_last_block; /* Secondly, we try with converted number of the last block */ - ret = udf_scan_anchors(sb, &lastblock, fileset); - if (ret < 0) { + lastblock = udf_scan_anchors(sb, sbi->s_last_block, fileset); + if (!lastblock) { /* VARCONV didn't help. Clear it. */ UDF_CLEAR_FLAG(sb, UDF_FLAG_VARCONV); + return 0; } out: - if (ret == 0) - sbi->s_last_block = lastblock; - return ret; + sbi->s_last_block = lastblock; + return 1; } /* * Check Volume Structure Descriptor, find Anchor block and load Volume - * Descriptor Sequence. - * - * Returns < 0 on error, 0 on success. -EAGAIN is special meaning anchor - * block was not found. + * Descriptor Sequence */ static int udf_load_vrs(struct super_block *sb, struct udf_options *uopt, int silent, struct kernel_lb_addr *fileset) { struct udf_sb_info *sbi = UDF_SB(sb); loff_t nsr_off; - int ret; if (!sb_set_blocksize(sb, uopt->blocksize)) { if (!silent) udf_warn(sb, "Bad block size\n"); - return -EINVAL; + return 0; } sbi->s_last_block = uopt->lastblock; if (!uopt->novrs) { @@ -1887,13 +1828,12 @@ static int udf_load_vrs(struct super_block *sb, struct udf_options *uopt, /* Look for anchor block and load Volume Descriptor Sequence */ sbi->s_anchor = uopt->anchor; - ret = udf_find_anchor(sb, fileset); - if (ret < 0) { - if (!silent && ret == -EAGAIN) + if (!udf_find_anchor(sb, fileset)) { + if (!silent) udf_warn(sb, "No anchor found\n"); - return ret; + return 0; } - return 0; + return 1; } static void udf_open_lvid(struct super_block *sb) @@ -1999,7 +1939,7 @@ u64 lvid_get_unique_id(struct super_block *sb) static int udf_fill_super(struct super_block *sb, void *options, int silent) { - int ret = -EINVAL; + int ret; struct inode *inode = NULL; struct udf_options uopt; struct kernel_lb_addr rootdir, fileset; @@ -2071,7 +2011,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) } else { uopt.blocksize = bdev_logical_block_size(sb->s_bdev); ret = udf_load_vrs(sb, &uopt, silent, &fileset); - if (ret == -EAGAIN && uopt.blocksize != UDF_DEFAULT_BLOCKSIZE) { + if (!ret && uopt.blocksize != UDF_DEFAULT_BLOCKSIZE) { if (!silent) pr_notice("Rescanning with blocksize %d\n", UDF_DEFAULT_BLOCKSIZE); @@ -2081,11 +2021,8 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) ret = udf_load_vrs(sb, &uopt, silent, &fileset); } } - if (ret < 0) { - if (ret == -EAGAIN) { - udf_warn(sb, "No partition found (1)\n"); - ret = -EINVAL; - } + if (!ret) { + udf_warn(sb, "No partition found (1)\n"); goto error_out; } @@ -2103,13 +2040,9 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) udf_err(sb, "minUDFReadRev=%x (max is %x)\n", le16_to_cpu(lvidiu->minUDFReadRev), UDF_MAX_READ_VERSION); - ret = -EINVAL; - goto error_out; - } else if (minUDFWriteRev > UDF_MAX_WRITE_VERSION && - !(sb->s_flags & MS_RDONLY)) { - ret = -EACCES; goto error_out; - } + } else if (minUDFWriteRev > UDF_MAX_WRITE_VERSION) + sb->s_flags |= MS_RDONLY; sbi->s_udfrev = minUDFWriteRev; @@ -2121,20 +2054,17 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) if (!sbi->s_partitions) { udf_warn(sb, "No partition found (2)\n"); - ret = -EINVAL; goto error_out; } if (sbi->s_partmaps[sbi->s_partition].s_partition_flags & - UDF_PART_FLAG_READ_ONLY && - !(sb->s_flags & MS_RDONLY)) { - ret = -EACCES; - goto error_out; + UDF_PART_FLAG_READ_ONLY) { + pr_notice("Partition marked readonly; forcing readonly mount\n"); + sb->s_flags |= MS_RDONLY; } if (udf_find_fileset(sb, &fileset, &rootdir)) { udf_warn(sb, "No fileset found\n"); - ret = -EINVAL; goto error_out; } @@ -2156,7 +2086,6 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) if (!inode) { udf_err(sb, "Error in udf_iget, block=%d, partition=%d\n", rootdir.logicalBlockNum, rootdir.partitionReferenceNum); - ret = -EIO; goto error_out; } @@ -2164,7 +2093,6 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) sb->s_root = d_make_root(inode); if (!sb->s_root) { udf_err(sb, "Couldn't allocate root dentry\n"); - ret = -ENOMEM; goto error_out; } sb->s_maxbytes = MAX_LFS_FILESIZE; @@ -2185,7 +2113,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) kfree(sbi); sb->s_fs_info = NULL; - return ret; + return -EINVAL; } void _udf_err(struct super_block *sb, const char *function, diff --git a/fs/xfs/xfs_da_btree.c b/fs/xfs/xfs_da_btree.c index eca6f9d8a263..0b8b2a13cd24 100644 --- a/fs/xfs/xfs_da_btree.c +++ b/fs/xfs/xfs_da_btree.c @@ -1223,7 +1223,6 @@ xfs_da3_node_toosmall( /* start with smaller blk num */ forward = nodehdr.forw < nodehdr.back; for (i = 0; i < 2; forward = !forward, i++) { - struct xfs_da3_icnode_hdr thdr; if (forward) blkno = nodehdr.forw; else @@ -1236,10 +1235,10 @@ xfs_da3_node_toosmall( return(error); node = bp->b_addr; - xfs_da3_node_hdr_from_disk(&thdr, node); + xfs_da3_node_hdr_from_disk(&nodehdr, node); xfs_trans_brelse(state->args->trans, bp); - if (count - thdr.count >= 0) + if (count - nodehdr.count >= 0) break; /* fits with at least 25% to spare */ } if (i >= 2) { diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index f45b2a7800c4..c13c919ab99e 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -455,11 +455,7 @@ struct acpi_pci_root { }; /* helper */ -acpi_handle acpi_find_child(acpi_handle, u64, bool); -static inline acpi_handle acpi_get_child(acpi_handle handle, u64 addr) -{ - return acpi_find_child(handle, addr, false); -} +acpi_handle acpi_get_child(acpi_handle, u64); int acpi_is_root_bridge(acpi_handle); struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle); #define DEVICE_ACPI_HANDLE(dev) ((acpi_handle)ACPI_HANDLE(dev)) diff --git a/include/asm-generic/hugetlb.h b/include/asm-generic/hugetlb.h index 99b490b4d05a..d06079c774a0 100644 --- a/include/asm-generic/hugetlb.h +++ b/include/asm-generic/hugetlb.h @@ -6,12 +6,12 @@ static inline pte_t mk_huge_pte(struct page *page, pgprot_t pgprot) return mk_pte(page, pgprot); } -static inline unsigned long huge_pte_write(pte_t pte) +static inline int huge_pte_write(pte_t pte) { return pte_write(pte); } -static inline unsigned long huge_pte_dirty(pte_t pte) +static inline int huge_pte_dirty(pte_t pte) { return pte_dirty(pte); } diff --git a/include/linux/balloon_compaction.h b/include/linux/balloon_compaction.h index 089743ade734..f7f1d7169b11 100644 --- a/include/linux/balloon_compaction.h +++ b/include/linux/balloon_compaction.h @@ -158,26 +158,6 @@ static inline bool balloon_page_movable(struct page *page) return false; } -/* - * isolated_balloon_page - identify an isolated balloon page on private - * compaction/migration page lists. - * - * After a compaction thread isolates a balloon page for migration, it raises - * the page refcount to prevent concurrent compaction threads from re-isolating - * the same page. For that reason putback_movable_pages(), or other routines - * that need to identify isolated balloon pages on private pagelists, cannot - * rely on balloon_page_movable() to accomplish the task. - */ -static inline bool isolated_balloon_page(struct page *page) -{ - /* Already isolated balloon pages, by default, have a raised refcount */ - if (page_flags_cleared(page) && !page_mapped(page) && - page_count(page) >= 2) - return __is_movable_balloon_page(page); - - return false; -} - /* * balloon_page_insert - insert a page into the balloon's page list and make * the page->mapping assignment accordingly. @@ -263,11 +243,6 @@ static inline bool balloon_page_movable(struct page *page) return false; } -static inline bool isolated_balloon_page(struct page *page) -{ - return false; -} - static inline bool balloon_page_isolate(struct page *page) { return false; diff --git a/include/linux/compat.h b/include/linux/compat.h index ec1aee4aec9c..7f0c1dd09079 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -669,13 +669,6 @@ asmlinkage long compat_sys_sigaltstack(const compat_stack_t __user *uss_ptr, int compat_restore_altstack(const compat_stack_t __user *uss); int __compat_save_altstack(compat_stack_t __user *, unsigned long); -#define compat_save_altstack_ex(uss, sp) do { \ - compat_stack_t __user *__uss = uss; \ - struct task_struct *t = current; \ - put_user_ex(ptr_to_compat((void __user *)t->sas_ss_sp), &__uss->ss_sp); \ - put_user_ex(sas_ss_flags(sp), &__uss->ss_flags); \ - put_user_ex(t->sas_ss_size, &__uss->ss_size); \ -} while (0); asmlinkage long compat_sys_sched_rr_get_interval(compat_pid_t pid, struct compat_timespec __user *interval); diff --git a/include/linux/compiler-gcc4.h b/include/linux/compiler-gcc4.h index ded429966c1f..842de225055f 100644 --- a/include/linux/compiler-gcc4.h +++ b/include/linux/compiler-gcc4.h @@ -65,21 +65,6 @@ #define __visible __attribute__((externally_visible)) #endif -/* - * GCC 'asm goto' miscompiles certain code sequences: - * - * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=58670 - * - * Work it around via a compiler barrier quirk suggested by Jakub Jelinek. - * Fixed in GCC 4.8.2 and later versions. - * - * (asm goto is automatically volatile - the naming reflects this.) - */ -#if GCC_VERSION <= 40801 -# define asm_volatile_goto(x...) do { asm goto(x); asm (""); } while (0) -#else -# define asm_volatile_goto(x...) do { asm goto(x); } while (0) -#endif #ifdef CONFIG_ARCH_USE_BUILTIN_BSWAP #if GCC_VERSION >= 40400 diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 9be5ac960fd8..1a6bb81f0fe5 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -332,7 +332,6 @@ extern int d_validate(struct dentry *, struct dentry *); * helper function for dentry_operations.d_dname() members */ extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...); -extern char *simple_dname(struct dentry *, char *, int); extern char *__d_path(const struct path *, const struct path *, char *, int); extern char *d_absolute_path(const struct path *, char *, int); diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index ef5bd3ff7eb4..3cd32478f2fd 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -405,14 +405,13 @@ int dm_noflush_suspending(struct dm_target *ti); union map_info *dm_get_mapinfo(struct bio *bio); union map_info *dm_get_rq_mapinfo(struct request *rq); -struct queue_limits *dm_get_queue_limits(struct mapped_device *md); - /* * Geometry functions. */ int dm_get_geometry(struct mapped_device *md, struct hd_geometry *geo); int dm_set_geometry(struct mapped_device *md, struct hd_geometry *geo); + /*----------------------------------------------------------------- * Functions for manipulating device-mapper tables. *---------------------------------------------------------------*/ diff --git a/include/linux/fs.h b/include/linux/fs.h index 01482142c3c9..65c2be22b601 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2574,7 +2574,6 @@ extern int inode_change_ok(const struct inode *, struct iattr *); extern int inode_newsize_ok(const struct inode *, loff_t offset); extern void setattr_copy(struct inode *inode, const struct iattr *attr); -extern int update_time(struct inode *, struct timespec *, int); extern int file_update_time(struct file *file); extern int generic_show_options(struct seq_file *m, struct dentry *root); diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index 120d57a1c3a5..44cdc11b15c2 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -334,7 +334,7 @@ extern int trace_define_field(struct ftrace_event_call *call, const char *type, const char *name, int offset, int size, int is_signed, int filter_type); extern int trace_add_event_call(struct ftrace_event_call *call); -extern int trace_remove_event_call(struct ftrace_event_call *call); +extern void trace_remove_event_call(struct ftrace_event_call *call); #define is_signed_type(type) (((type)(-1)) < (type)1) diff --git a/include/linux/hid.h b/include/linux/hid.h index 4f8aa4733fb6..0c48991b0402 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -393,12 +393,10 @@ struct hid_report { struct hid_device *device; /* associated device */ }; -#define HID_MAX_IDS 256 - struct hid_report_enum { unsigned numbered; struct list_head report_list; - struct hid_report *report_id_hash[HID_MAX_IDS]; + struct hid_report *report_id_hash[256]; }; #define HID_REPORT_TYPES 3 @@ -746,14 +744,9 @@ struct hid_field *hidinput_get_led_field(struct hid_device *hid); unsigned int hidinput_count_leds(struct hid_device *hid); __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code); void hid_output_report(struct hid_report *report, __u8 *data); -u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags); struct hid_device *hid_allocate_device(void); struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id); int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size); -struct hid_report *hid_validate_values(struct hid_device *hid, - unsigned int type, unsigned int id, - unsigned int field_index, - unsigned int report_counts); int hid_open_report(struct hid_device *device); int hid_check_keys_pressed(struct hid_device *hid); int hid_connect(struct hid_device *hid, unsigned int connect_mask); diff --git a/include/linux/intel_mid_dma.h b/include/linux/intel_mid_dma.h index 374514e56670..d7f2997378a4 100644 --- a/include/linux/intel_mid_dma.h +++ b/include/linux/intel_mid_dma.h @@ -77,7 +77,7 @@ struct intel_mid_dma_slave { struct dma_slave_config dma_slave; }; -struct device *intel_mid_get_acpi_dma(void); +struct device *intel_mid_get_acpi_dma(const char *hid); dma_addr_t intel_dma_get_src_addr(struct dma_chan *chan); dma_addr_t intel_dma_get_dst_addr(struct dma_chan *chan); #endif /*__INTEL_MID_DMA_H__*/ diff --git a/include/linux/ipc_namespace.h b/include/linux/ipc_namespace.h index 19c19a5eee29..c4d870b0d5e6 100644 --- a/include/linux/ipc_namespace.h +++ b/include/linux/ipc_namespace.h @@ -22,7 +22,7 @@ struct ipc_ids { int in_use; unsigned short seq; unsigned short seq_max; - struct rw_semaphore rwsem; + struct rw_semaphore rw_mutex; struct idr ipcs_idr; int next_id; }; diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index b8b7dc755752..850e95bc766c 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -101,7 +101,6 @@ struct inet6_skb_parm { #define IP6SKB_FORWARDED 2 #define IP6SKB_REROUTED 4 #define IP6SKB_ROUTERALERT 8 -#define IP6SKB_FRAGMENTED 16 }; #define IP6CB(skb) ((struct inet6_skb_parm*)((skb)->cb)) diff --git a/include/linux/mfd/wm8994/pdata.h b/include/linux/mfd/wm8994/pdata.h index 68e776594889..9e96ca72e1ff 100644 --- a/include/linux/mfd/wm8994/pdata.h +++ b/include/linux/mfd/wm8994/pdata.h @@ -130,6 +130,12 @@ struct wm8958_micd_rate { int rate; }; +struct wm8958_custom_config { + int format; + int rate; + int channels; +}; + struct wm8994_pdata { int gpio_base; @@ -182,6 +188,16 @@ struct wm8994_pdata { */ int micdet_delay; + /* Delay between microphone detect completing and reporting on + * insert (specified in ms) + */ + int mic_id_delay; + + /* Keep MICBIAS2 high for micb_en_delay, during jack insertion + * removal + */ + int micb_en_delay; + /* IRQ for microphone detection if brought out directly as a * signal. */ @@ -223,6 +239,9 @@ struct wm8994_pdata { * lines is mastered. */ int max_channels_clocked[WM8994_NUM_AIF]; + + /* custom config for overriding the hw params */ + struct wm8958_custom_config *custom_cfg; }; #endif diff --git a/include/linux/mfd/wm8994/registers.h b/include/linux/mfd/wm8994/registers.h index 053548961c15..db8cef3d5321 100644 --- a/include/linux/mfd/wm8994/registers.h +++ b/include/linux/mfd/wm8994/registers.h @@ -2668,6 +2668,10 @@ /* * R772 (0x304) - AIF1ADC LRCLK */ +#define WM8958_AIF1_LRCLK_INV 0x1000 /* AIF1_LRCLK_INV */ +#define WM8958_AIF1_LRCLK_INV_MASK 0x1000 /* AIF1_LRCLK_INV */ +#define WM8958_AIF1_LRCLK_INV_SHIFT 12 /* AIF1_LRCLK_INV */ +#define WM8958_AIF1_LRCLK_INV_WIDTH 1 /* AIF1_LRCLK_INV */ #define WM8994_AIF1ADC_LRCLK_DIR 0x0800 /* AIF1ADC_LRCLK_DIR */ #define WM8994_AIF1ADC_LRCLK_DIR_MASK 0x0800 /* AIF1ADC_LRCLK_DIR */ #define WM8994_AIF1ADC_LRCLK_DIR_SHIFT 11 /* AIF1ADC_LRCLK_DIR */ @@ -2679,6 +2683,10 @@ /* * R773 (0x305) - AIF1DAC LRCLK */ +#define WM8958_AIF1_LRCLK_INV 0x1000 /* AIF1_LRCLK_INV */ +#define WM8958_AIF1_LRCLK_INV_MASK 0x1000 /* AIF1_LRCLK_INV */ +#define WM8958_AIF1_LRCLK_INV_SHIFT 12 /* AIF1_LRCLK_INV */ +#define WM8958_AIF1_LRCLK_INV_WIDTH 1 /* AIF1_LRCLK_INV */ #define WM8994_AIF1DAC_LRCLK_DIR 0x0800 /* AIF1DAC_LRCLK_DIR */ #define WM8994_AIF1DAC_LRCLK_DIR_MASK 0x0800 /* AIF1DAC_LRCLK_DIR */ #define WM8994_AIF1DAC_LRCLK_DIR_SHIFT 11 /* AIF1DAC_LRCLK_DIR */ diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h index cb358355ef43..09c2300ddb37 100644 --- a/include/linux/miscdevice.h +++ b/include/linux/miscdevice.h @@ -45,7 +45,6 @@ #define MAPPER_CTRL_MINOR 236 #define LOOP_CTRL_MINOR 237 #define VHOST_NET_MINOR 238 -#define UHID_MINOR 239 #define MISC_DYNAMIC_MINOR 255 struct device; diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 0efe855d74e0..ace9a5f01c64 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -213,7 +213,6 @@ struct vm_region { unsigned long vm_top; /* region allocated to here */ unsigned long vm_pgoff; /* the offset in vm_file corresponding to vm_start */ struct file *vm_file; /* the backing file or NULL */ - struct file *vm_prfile; /* the virtual backing file or NULL */ int vm_usage; /* region usage count (access under nommu_region_sem) */ bool vm_icache_flushed : 1; /* true if the icache has been flushed for @@ -282,7 +281,6 @@ struct vm_area_struct { unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE units, *not* PAGE_CACHE_SIZE */ struct file * vm_file; /* File we map to (can be NULL). */ - struct file *vm_prfile; /* shadow of vm_file */ void * vm_private_data; /* was vm_pte (shared mem) */ #ifndef CONFIG_MMU @@ -335,7 +333,6 @@ struct mm_struct { void (*unmap_area) (struct mm_struct *mm, unsigned long addr); #endif unsigned long mmap_base; /* base of mmap area */ - unsigned long mmap_legacy_base; /* base of mmap area in bottom-up allocations */ unsigned long task_size; /* size of task vm space */ unsigned long cached_hole_size; /* if non-zero, the largest hole below free_area_cache */ unsigned long free_area_cache; /* first hole of size cached_hole_size or larger */ diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 39e8279d363f..4b0fe47e7503 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -84,6 +84,7 @@ struct mmc_ext_csd { unsigned int hpi_cmd; /* cmd used as HPI */ bool bkops; /* background support bit */ bool bkops_en; /* background enable bit */ + unsigned int rpmb_size; /* Units: half sector */ unsigned int data_sector_size; /* 512 bytes or 4KB */ unsigned int data_tag_unit_size; /* DATA TAG UNIT size */ unsigned int boot_ro_lock; /* ro lock support */ @@ -297,6 +298,8 @@ struct mmc_card { struct dentry *debugfs_root; struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */ unsigned int nr_parts; + + unsigned int rpmb_max_req; }; /* @@ -525,4 +528,7 @@ extern void mmc_unregister_driver(struct mmc_driver *); extern void mmc_fixup_device(struct mmc_card *card, const struct mmc_fixup *table); +extern int mmc_rpmb_req_handle(struct device *emmc, + struct mmc_ioc_rpmb_req *req); + #endif /* LINUX_MMC_CARD_H */ diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 3e3b95e651d6..a4ab8e129037 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -10,6 +10,8 @@ #include #include +#include +#include struct request; struct mmc_data; @@ -136,6 +138,34 @@ struct mmc_request { struct mmc_host *host; }; +/* + * RPMB frame structure for MMC core stack + */ +struct mmc_core_rpmb_req { + struct mmc_ioc_rpmb_req *req; + __u8 *frame; + bool ready; +}; + +#define RPMB_PROGRAM_KEY 1 /* Program RPMB Authentication Key */ +#define RPMB_GET_WRITE_COUNTER 2 /* Read RPMB write counter */ +#define RPMB_WRITE_DATA 3 /* Write data to RPMB partition */ +#define RPMB_READ_DATA 4 /* Read data from RPMB partition */ +#define RPMB_RESULT_READ 5 /* Read result request */ +#define RPMB_REQ 1 /* RPMB request mark */ +#define RPMB_RESP (1 << 1)/* RPMB response mark */ +#define RPMB_AVALIABLE_SECTORS 8 /* 4K page size */ + +#define RPMB_TYPE_BEG 510 +#define RPMB_RES_BEG 508 +#define RPMB_BLKS_BEG 506 +#define RPMB_ADDR_BEG 504 +#define RPMB_WCOUNTER_BEG 500 + +#define RPMB_NONCE_BEG 484 +#define RPMB_DATA_BEG 228 +#define RPMB_MAC_BEG 196 + struct mmc_card; struct mmc_async_req; @@ -153,6 +183,10 @@ extern void mmc_start_bkops(struct mmc_card *card, bool from_exception); extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, bool); extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int); extern int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd); +extern int mmc_rpmb_partition_ops(struct mmc_core_rpmb_req *, + struct mmc_card *); +extern int mmc_rpmb_pre_frame(struct mmc_core_rpmb_req *, struct mmc_card *); +extern void mmc_rpmb_post_frame(struct mmc_core_rpmb_req *); #define MMC_ERASE_ARG 0x00000000 #define MMC_SECURE_ERASE_ARG 0x80000000 diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 64b267e645ab..3b7fa2abecca 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -54,7 +54,6 @@ struct nfs_client { u32 cl_minorversion;/* NFSv4 minorversion */ struct rpc_cred *cl_machine_cred; - int nfs_prog; #if IS_ENABLED(CONFIG_NFS_V4) u64 cl_clientid; /* constant */ @@ -76,9 +75,7 @@ struct nfs_client { * This is used to generate the mv0 callback address. */ char cl_ipaddr[48]; - u32 cl_cb_ident; /* v4.0 callback identifier */ - const struct nfs4_minor_version_ops *cl_mvops; /* The sequence id to use for the next CREATE_SESSION */ diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index f6507023c84b..104b62f23ee0 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -11,7 +11,7 @@ * reasonable for NFS over UDP. */ #define NFS_MAX_FILE_IO_SIZE (1048576U) -#define NFS_DEF_FILE_IO_SIZE CONFIG_NFS_DEF_FILE_IO_SIZE +#define NFS_DEF_FILE_IO_SIZE (4096U) #define NFS_MIN_FILE_IO_SIZE (1024U) struct nfs4_string { diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index eacb93b24256..94f13942485a 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -518,8 +518,6 @@ #define PCI_DEVICE_ID_AMD_11H_NB_MISC 0x1303 #define PCI_DEVICE_ID_AMD_11H_NB_LINK 0x1304 #define PCI_DEVICE_ID_AMD_15H_M10H_F3 0x1403 -#define PCI_DEVICE_ID_AMD_15H_M30H_NB_F3 0x141d -#define PCI_DEVICE_ID_AMD_15H_M30H_NB_F4 0x141e #define PCI_DEVICE_ID_AMD_15H_NB_F0 0x1600 #define PCI_DEVICE_ID_AMD_15H_NB_F1 0x1601 #define PCI_DEVICE_ID_AMD_15H_NB_F2 0x1602 @@ -2478,9 +2476,6 @@ #define PCI_VENDOR_ID_ASMEDIA 0x1b21 -#define PCI_VENDOR_ID_CIRCUITCO 0x1cc8 -#define PCI_SUBSYSTEM_ID_CIRCUITCO_MINNOWBOARD 0x0001 - #define PCI_VENDOR_ID_TEKRAM 0x1de1 #define PCI_DEVICE_ID_TEKRAM_DC290 0xdc29 @@ -2875,6 +2870,7 @@ #define PCI_DEVICE_ID_INTEL_IXP4XX 0x8500 #define PCI_DEVICE_ID_INTEL_IXP2800 0x9004 #define PCI_DEVICE_ID_INTEL_S21152BB 0xb152 +#define PCI_DEVICE_ID_INTEL_SST_MRFLD 0x119A #define PCI_VENDOR_ID_SCALEMP 0x8686 #define PCI_DEVICE_ID_SCALEMP_VSMP_CTL 0x1010 diff --git a/include/linux/random.h b/include/linux/random.h index 6312dd9ba449..3b9377d6b7a5 100644 --- a/include/linux/random.h +++ b/include/linux/random.h @@ -17,7 +17,6 @@ extern void add_interrupt_randomness(int irq, int irq_flags); extern void get_random_bytes(void *buf, int nbytes); extern void get_random_bytes_arch(void *buf, int nbytes); void generate_random_uuid(unsigned char uuid_out[16]); -extern int random_int_secret_init(void); #ifndef MODULE extern const struct file_operations random_fops, urandom_fops; diff --git a/include/linux/rculist.h b/include/linux/rculist.h index 4106721c4e5e..f4b1001a4676 100644 --- a/include/linux/rculist.h +++ b/include/linux/rculist.h @@ -267,9 +267,8 @@ static inline void list_splice_init_rcu(struct list_head *list, */ #define list_first_or_null_rcu(ptr, type, member) \ ({struct list_head *__ptr = (ptr); \ - struct list_head *__next = ACCESS_ONCE(__ptr->next); \ - likely(__ptr != __next) ? \ - list_entry_rcu(__next, type, member) : NULL; \ + struct list_head __rcu *__next = list_next_rcu(__ptr); \ + likely(__ptr != __next) ? container_of(__next, type, member) : NULL; \ }) /** diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 98c470ced989..f91bb416122d 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -16,7 +16,6 @@ #include #include #include -#include struct module; struct device; diff --git a/include/linux/sched.h b/include/linux/sched.h index 178a8d909f14..3aeb14b06242 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -314,6 +314,7 @@ struct nsproxy; struct user_namespace; #ifdef CONFIG_MMU +extern unsigned long mmap_legacy_base(void); extern void arch_pick_mmap_layout(struct mm_struct *mm); extern unsigned long arch_get_unmapped_area(struct file *, unsigned long, unsigned long, diff --git a/include/linux/sem.h b/include/linux/sem.h index 976ce3a19f1b..53d42650b193 100644 --- a/include/linux/sem.h +++ b/include/linux/sem.h @@ -12,12 +12,10 @@ struct task_struct; struct sem_array { struct kern_ipc_perm ____cacheline_aligned_in_smp sem_perm; /* permissions .. see ipc.h */ + time_t sem_otime; /* last semop time */ time_t sem_ctime; /* last change time */ struct sem *sem_base; /* ptr to first semaphore in array */ - struct list_head pending_alter; /* pending operations */ - /* that alter the array */ - struct list_head pending_const; /* pending complex operations */ - /* that do not alter semvals */ + struct list_head sem_pending; /* pending operations to be processed */ struct list_head list_id; /* undo requests on this array */ int sem_nsems; /* no. of semaphores in array */ int complex_count; /* pending complex operations */ diff --git a/include/linux/signal.h b/include/linux/signal.h index 2ac423bdb676..d897484730c0 100644 --- a/include/linux/signal.h +++ b/include/linux/signal.h @@ -434,14 +434,6 @@ void signals_init(void); int restore_altstack(const stack_t __user *); int __save_altstack(stack_t __user *, unsigned long); -#define save_altstack_ex(uss, sp) do { \ - stack_t __user *__uss = uss; \ - struct task_struct *t = current; \ - put_user_ex((void __user *)t->sas_ss_sp, &__uss->ss_sp); \ - put_user_ex(sas_ss_flags(sp), &__uss->ss_flags); \ - put_user_ex(t->sas_ss_size, &__uss->ss_size); \ -} while (0); - #ifdef CONFIG_PROC_FS struct seq_file; extern void render_sigset_t(struct seq_file *, const char *, sigset_t *); diff --git a/include/linux/spi/intel_mid_ssp_spi.h b/include/linux/spi/intel_mid_ssp_spi.h index 453c40bc84f9..2eed919ffb9d 100644 --- a/include/linux/spi/intel_mid_ssp_spi.h +++ b/include/linux/spi/intel_mid_ssp_spi.h @@ -109,6 +109,7 @@ DEFINE_SSP_REG(SSITR, 0x0c) DEFINE_SSP_REG(SSDR, 0x10) DEFINE_SSP_REG(SSTO, 0x28) DEFINE_SSP_REG(SSPSP, 0x2c) +DEFINE_SSP_REG(SSCR2, 0x40) DEFINE_SSP_REG(SSFS, 0x44) DEFINE_SSP_REG(SFIFOL, 0x68) @@ -208,6 +209,8 @@ DEFINE_SSP_REG(GAFR1_U, 0x44); #define SSPSP_SFRMP (1 << 2) /* Serial Frame Polarity */ #define SSPSP_SCMODE(x) ((x) << 0) /* Serial Bit Rate Clock Mode */ +#define SSCR2_CLK_DEL_EN (1 << 3) /* Delay logic for capturing input data */ + /* * For testing SSCR1 changes that require SSP restart, basically * everything except the service and interrupt enables @@ -220,11 +223,6 @@ DEFINE_SSP_REG(GAFR1_U, 0x44); | SSCR1_RFT | SSCR1_TFT | SSCR1_MWDS \ | SSCR1_SPH | SSCR1_SPO | SSCR1_LBM) -/* add CS control call back feature to give user capability -to control CS signal by themselves*/ -#define CS_DEASSERT 0 -#define CS_ASSERT 1 - struct callback_param { void *drv_context; u32 direction; @@ -301,6 +299,8 @@ struct ssp_drv_context { unsigned long quirks; u32 rx_fifo_threshold; + /* if CS_ACTIVE_HIGH, cs_assert == 1 else cs_assert == 0 */ + int cs_assert; int cs_change; void (*cs_control)(u32 command); }; diff --git a/include/linux/splice.h b/include/linux/splice.h index bfc6fb669d8e..74575cbf2d6f 100644 --- a/include/linux/splice.h +++ b/include/linux/splice.h @@ -92,10 +92,4 @@ extern void splice_shrink_spd(struct splice_pipe_desc *); extern void spd_release_page(struct splice_pipe_desc *, unsigned int); extern const struct pipe_buf_operations page_cache_pipe_buf_ops; - -extern long do_splice_from(struct pipe_inode_info *pipe, struct file *out, - loff_t *ppos, size_t len, unsigned int flags); -extern long do_splice_to(struct file *in, loff_t *ppos, - struct pipe_inode_info *pipe, size_t len, - unsigned int flags); #endif diff --git a/include/linux/timex.h b/include/linux/timex.h index dd3edd7dfc94..b3726e61368e 100644 --- a/include/linux/timex.h +++ b/include/linux/timex.h @@ -141,7 +141,6 @@ extern int do_adjtimex(struct timex *); extern void hardpps(const struct timespec *, const struct timespec *); int read_current_timer(unsigned long *timer_val); -void ntp_notify_cmos_timer(void); /* The clock frequency of the i8253/i8254 PIT */ #define PIT_TICK_RATE 1193182ul diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 39aa5c5a54a5..f20e75b4bd20 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -413,7 +413,7 @@ extern int usb_hcd_pci_probe(struct pci_dev *dev, extern void usb_hcd_pci_remove(struct pci_dev *dev); extern void usb_hcd_pci_shutdown(struct pci_dev *dev); -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP extern const struct dev_pm_ops usb_hcd_pci_pm_ops; #endif #endif /* CONFIG_PCI */ diff --git a/include/linux/wait.h b/include/linux/wait.h index c8e576022234..1133695eb067 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -805,63 +805,6 @@ do { \ __ret; \ }) -#define __wait_event_interruptible_lock_irq_timeout(wq, condition, \ - lock, ret) \ -do { \ - DEFINE_WAIT(__wait); \ - \ - for (;;) { \ - prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \ - if (condition) \ - break; \ - if (signal_pending(current)) { \ - ret = -ERESTARTSYS; \ - break; \ - } \ - spin_unlock_irq(&lock); \ - ret = schedule_timeout(ret); \ - spin_lock_irq(&lock); \ - if (!ret) \ - break; \ - } \ - finish_wait(&wq, &__wait); \ -} while (0) - -/** - * wait_event_interruptible_lock_irq_timeout - sleep until a condition gets true or a timeout elapses. - * The condition is checked under the lock. This is expected - * to be called with the lock taken. - * @wq: the waitqueue to wait on - * @condition: a C expression for the event to wait for - * @lock: a locked spinlock_t, which will be released before schedule() - * and reacquired afterwards. - * @timeout: timeout, in jiffies - * - * The process is put to sleep (TASK_INTERRUPTIBLE) until the - * @condition evaluates to true or signal is received. The @condition is - * checked each time the waitqueue @wq is woken up. - * - * wake_up() has to be called after changing any variable that could - * change the result of the wait condition. - * - * This is supposed to be called while holding the lock. The lock is - * dropped before going to sleep and is reacquired afterwards. - * - * The function returns 0 if the @timeout elapsed, -ERESTARTSYS if it - * was interrupted by a signal, and the remaining jiffies otherwise - * if the condition evaluated to true before the timeout elapsed. - */ -#define wait_event_interruptible_lock_irq_timeout(wq, condition, lock, \ - timeout) \ -({ \ - int __ret = timeout; \ - \ - if (!(condition)) \ - __wait_event_interruptible_lock_irq_timeout( \ - wq, condition, lock, __ret); \ - __ret; \ -}) - /* * These are the old interfaces to sleep waiting for an event. diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 47ada23345a1..7343a27fe819 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -22,7 +22,6 @@ #define _V4L2_CTRLS_H #include -#include #include /* forward references */ diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 25100687babb..01b1a1ad77d2 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -73,10 +73,6 @@ extern int ipv6_chk_home_addr(struct net *net, const struct in6_addr *addr); #endif -bool ipv6_chk_custom_prefix(const struct in6_addr *addr, - const unsigned int prefix_len, - struct net_device *dev); - extern int ipv6_chk_prefix(const struct in6_addr *addr, struct net_device *dev); diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 0ef00066dae8..e0512aaef4b8 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -104,7 +104,6 @@ enum { enum { HCI_SETUP, HCI_AUTO_OFF, - HCI_RFKILLED, HCI_MGMT, HCI_PAIRABLE, HCI_SERVICE_CACHE, diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 7cb6d360d147..b9e7142e2119 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -308,6 +308,8 @@ struct hci_conn { bdaddr_t dst; __u8 dst_type; + bdaddr_t src; + __u8 src_type; __u16 handle; __u16 state; __u8 mode; diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index fb94cf13c777..bfca6cf08c2b 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -442,7 +442,13 @@ struct l2cap_chan { __u8 state; + bdaddr_t dst; + __u8 dst_type; + bdaddr_t src; + __u8 src_type; + __le16 psm; + __le16 sport; __u16 dcid; __u16 scid; @@ -453,8 +459,6 @@ struct l2cap_chan { __u8 chan_type; __u8 chan_policy; - __le16 sport; - __u8 sec_level; __u8 ident; diff --git a/include/net/genetlink.h b/include/net/genetlink.h index 8e0b6c856a13..93024a47e0e2 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -61,7 +61,6 @@ struct genl_family { struct list_head ops_list; /* private */ struct list_head family_list; /* private */ struct list_head mcast_groups; /* private */ - struct module *module; }; /** @@ -122,24 +121,9 @@ struct genl_ops { struct list_head ops_list; }; -extern int __genl_register_family(struct genl_family *family); - -static inline int genl_register_family(struct genl_family *family) -{ - family->module = THIS_MODULE; - return __genl_register_family(family); -} - -extern int __genl_register_family_with_ops(struct genl_family *family, +extern int genl_register_family(struct genl_family *family); +extern int genl_register_family_with_ops(struct genl_family *family, struct genl_ops *ops, size_t n_ops); - -static inline int genl_register_family_with_ops(struct genl_family *family, - struct genl_ops *ops, size_t n_ops) -{ - family->module = THIS_MODULE; - return __genl_register_family_with_ops(family, ops, n_ops); -} - extern int genl_unregister_family(struct genl_family *family); extern int genl_register_ops(struct genl_family *, struct genl_ops *ops); extern int genl_unregister_ops(struct genl_family *, struct genl_ops *ops); diff --git a/include/net/ip.h b/include/net/ip.h index edfa59174d9a..a68f838a132c 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -254,11 +254,9 @@ int ip_dont_fragment(struct sock *sk, struct dst_entry *dst) extern void __ip_select_ident(struct iphdr *iph, struct dst_entry *dst, int more); -static inline void ip_select_ident(struct sk_buff *skb, struct dst_entry *dst, struct sock *sk) +static inline void ip_select_ident(struct iphdr *iph, struct dst_entry *dst, struct sock *sk) { - struct iphdr *iph = ip_hdr(skb); - - if ((iph->frag_off & htons(IP_DF)) && !skb->local_df) { + if (iph->frag_off & htons(IP_DF)) { /* This is only to work around buggy Windows95/2000 * VJ compression implementations. If the ID field * does not change, they drop every other packet in @@ -270,11 +268,9 @@ static inline void ip_select_ident(struct sk_buff *skb, struct dst_entry *dst, s __ip_select_ident(iph, dst, 0); } -static inline void ip_select_ident_more(struct sk_buff *skb, struct dst_entry *dst, struct sock *sk, int more) +static inline void ip_select_ident_more(struct iphdr *iph, struct dst_entry *dst, struct sock *sk, int more) { - struct iphdr *iph = ip_hdr(skb); - - if ((iph->frag_off & htons(IP_DF)) && !skb->local_df) { + if (iph->frag_off & htons(IP_DF)) { if (sk && inet_sk(sk)->inet_daddr) { iph->id = htons(inet_sk(sk)->inet_id); inet_sk(sk)->inet_id += 1 + more; diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index a9942e1faefb..09b1360e10bf 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -141,6 +141,20 @@ static inline u8 ip_tunnel_ecn_encap(u8 tos, const struct iphdr *iph, return INET_ECN_encapsulate(tos, inner); } +static inline void tunnel_ip_select_ident(struct sk_buff *skb, + const struct iphdr *old_iph, + struct dst_entry *dst) +{ + struct iphdr *iph = ip_hdr(skb); + + /* Use inner packet iph-id if possible. */ + if (skb->protocol == htons(ETH_P_IP) && old_iph->id) + iph->id = old_iph->id; + else + __ip_select_ident(iph, dst, + (skb_shinfo(skb)->gso_segs ?: 1) - 1); +} + static inline void iptunnel_xmit(struct sk_buff *skb, struct net_device *dev) { int err; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 4e50d3674431..885898a40d13 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1484,7 +1484,6 @@ enum ieee80211_hw_flags { IEEE80211_HW_SUPPORTS_RC_TABLE = 1<<24, IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF = 1<<25, IEEE80211_HW_TIMING_BEACON_ONLY = 1<<26, - IEEE80211_HW_SUPPORTS_HT_CCK_RATES = 1<<27, }; /** diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 63ed1d1dd9e7..e7f4e21cc3e1 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -682,19 +682,13 @@ struct psched_ratecfg { u64 rate_bps; u32 mult; u16 overhead; - u8 linklayer; u8 shift; }; static inline u64 psched_l2t_ns(const struct psched_ratecfg *r, unsigned int len) { - len += r->overhead; - - if (unlikely(r->linklayer == TC_LINKLAYER_ATM)) - return ((u64)(DIV_ROUND_UP(len,48)*53) * r->mult) >> r->shift; - - return ((u64)len * r->mult) >> r->shift; + return ((u64)(len + r->overhead) * r->mult) >> r->shift; } extern void psched_ratecfg_precompute(struct psched_ratecfg *r, const struct tc_ratespec *conf); @@ -705,7 +699,6 @@ static inline void psched_ratecfg_getrate(struct tc_ratespec *res, memset(res, 0, sizeof(*res)); res->rate = r->rate_bps >> 3; res->overhead = r->overhead; - res->linklayer = (r->linklayer & TC_LINKLAYER_MASK); } #endif diff --git a/include/net/secure_seq.h b/include/net/secure_seq.h index c2e542b27a5a..6ca975bebd37 100644 --- a/include/net/secure_seq.h +++ b/include/net/secure_seq.h @@ -3,6 +3,7 @@ #include +extern void net_secret_init(void); extern __u32 secure_ip_id(__be32 daddr); extern __u32 secure_ipv6_id(const __be32 daddr[4]); extern u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport); diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h index 9031a26249b5..930735972eb9 100644 --- a/include/sound/compress_driver.h +++ b/include/sound/compress_driver.h @@ -32,6 +32,7 @@ #include struct snd_compr_ops; +struct snd_pcm_substream; /** * struct snd_compr_runtime: runtime stream description @@ -48,6 +49,8 @@ struct snd_compr_ops; * the ring buffer * @total_bytes_transferred: cumulative bytes transferred by offload DSP * @sleep: poll sleep + * @wait: drain wait queue + * @drain_wake: condition for drain wake */ struct snd_compr_runtime { snd_pcm_state_t state; @@ -59,6 +62,9 @@ struct snd_compr_runtime { u64 total_bytes_available; u64 total_bytes_transferred; wait_queue_head_t sleep; + wait_queue_head_t wait; + unsigned int drain_wake; + struct snd_pcm_substream *fe_substream; void *private_data; }; @@ -157,6 +163,7 @@ int snd_compress_register(struct snd_compr *device); int snd_compress_deregister(struct snd_compr *device); int snd_compress_new(struct snd_card *card, int device, int type, struct snd_compr *compr); +int snd_compr_stop(struct snd_compr_stream *stream); /* dsp driver callback apis * For playback: driver should call snd_compress_fragment_elapsed() to let the @@ -171,4 +178,12 @@ static inline void snd_compr_fragment_elapsed(struct snd_compr_stream *stream) wake_up(&stream->runtime->sleep); } +static inline void snd_compr_drain_notify(struct snd_compr_stream *stream) +{ + snd_BUG_ON(!stream); + + stream->runtime->drain_wake = 1; + wake_up(&stream->runtime->wait); +} + #endif diff --git a/include/sound/core.h b/include/sound/core.h index 5bfe5136441c..971a2eca066c 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -146,6 +146,11 @@ struct snd_card { struct snd_mixer_oss *mixer_oss; int mixer_oss_change_count; #endif + +#if IS_ENABLED(CONFIG_SND_EFFECTS_OFFLOAD) + struct snd_effect_ops *effect_ops; + struct mutex effect_lock; /* effect lock */ +#endif }; #ifdef CONFIG_PM diff --git a/include/sound/effect_driver.h b/include/sound/effect_driver.h new file mode 100644 index 000000000000..410e5f9ce131 --- /dev/null +++ b/include/sound/effect_driver.h @@ -0,0 +1,63 @@ +/* + * effect_driver.h - effect offload driver APIs + * + * Copyright (C) 2013 Intel Corporation + * Authors: Vinod Koul + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ +#ifndef __EFFECT_DRIVER_H +#define __EFFECT_DRIVER_H + +#include + +struct snd_effect_ops { + int (*create)(struct snd_card *card, struct snd_effect *effect); + int (*destroy)(struct snd_card *card, struct snd_effect *effect); + int (*set_params)(struct snd_card *card, + struct snd_effect_params *params); + int (*get_params)(struct snd_card *card, + struct snd_effect_params *params); + int (*query_num_effects)(struct snd_card *card); + int (*query_effect_caps)(struct snd_card *card, + struct snd_effect_caps *caps); +}; + +#if IS_ENABLED(CONFIG_SND_EFFECTS_OFFLOAD) +int snd_effect_register(struct snd_card *card, struct snd_effect_ops *ops); +int snd_effect_deregister(struct snd_card *card); +#else +static inline int snd_effect_register(struct snd_card *card, + struct snd_effect_ops *ops) +{ + return -ENODEV; +} +static inline int snd_effect_deregister(struct snd_card *card) +{ + return -ENODEV; +} +#endif + +/* IOCTL fns */ +int snd_ctl_effect_create(struct snd_card *card, void *arg); +int snd_ctl_effect_destroy(struct snd_card *card, void *arg); +int snd_ctl_effect_set_params(struct snd_card *card, void *arg); +int snd_ctl_effect_get_params(struct snd_card *card, void *arg); +int snd_ctl_effect_query_num_effects(struct snd_card *card, void *arg); +int snd_ctl_effect_query_effect_caps(struct snd_card *card, void *arg); +#endif diff --git a/include/sound/intel_sst_ioctl.h b/include/sound/intel_sst_ioctl.h new file mode 100644 index 000000000000..6025ef9f3688 --- /dev/null +++ b/include/sound/intel_sst_ioctl.h @@ -0,0 +1,63 @@ +#ifndef __INTEL_SST_IOCTL_H__ +#define __INTEL_SST_IOCTL_H__ +/* + * intel_sst_ioctl.h - Intel SST Driver for audio engine + * + * Copyright (C) 2008-10 Intel Corporation + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R + * KP Jeeja + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This file defines all sst ioctls + */ + +/* codec and post/pre processing related info */ + +#include + +/* Pre and post processing params structure */ +struct snd_ppp_params { + __u8 algo_id;/* Post/Pre processing algorithm ID */ + __u8 str_id; /*Only 5 bits used 0 - 31 are valid*/ + __u8 enable; /* 0= disable, 1= enable*/ + __u8 operation; /* 0 = set_algo, 1 = get_algo */ + __u32 size; /*Size of parameters for all blocks*/ + void *params; +} __packed; + +struct snd_sst_driver_info { + __u32 max_streams; +}; + +struct snd_sst_tuning_params { + __u8 type; + __u8 str_id; + __u8 size; + __u8 rsvd; + __u64 addr; +} __packed; + +/*IOCTL defined here */ +/*SST common ioctls */ +#define SNDRV_SST_DRIVER_INFO _IOR('L', 0x10, struct snd_sst_driver_info) +#define SNDRV_SST_SET_ALGO _IOW('L', 0x30, struct snd_ppp_params) +#define SNDRV_SST_GET_ALGO _IOWR('L', 0x31, struct snd_ppp_params) +#define SNDRV_SST_TUNING_PARAMS _IOW('L', 0x32, struct snd_sst_tuning_params) +#endif /* __INTEL_SST_IOCTL_H__ */ diff --git a/include/sound/pcm.h b/include/sound/pcm.h index b48792fe386b..6f011361b300 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -285,6 +285,7 @@ struct snd_pcm_runtime { unsigned long hw_ptr_jiffies; /* Time when hw_ptr is updated */ unsigned long hw_ptr_buffer_jiffies; /* buffer time in jiffies */ snd_pcm_sframes_t delay; /* extra delay; typically FIFO size */ + snd_pcm_sframes_t soc_delay; /* extra delay; typically delay incurred in soc */ u64 hw_ptr_wrap; /* offset for hw_ptr due to boundary wrap-around */ /* -- HW params -- */ diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index ae9a227d35d3..41e9df9c52c7 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -231,8 +231,8 @@ struct snd_soc_dai { struct snd_soc_dai_driver *driver; /* DAI runtime info */ - unsigned int capture_active:1; /* stream is in use */ - unsigned int playback_active:1; /* stream is in use */ + unsigned int capture_active; /* cap streams is in use */ + unsigned int playback_active; /* pb streams is in use */ unsigned int symmetric_rates:1; struct snd_pcm_runtime *runtime; unsigned int active; diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 385c6329a967..f48d25c72ed9 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -422,6 +422,8 @@ void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm); int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_widget_list **list); +struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol); + /* dapm widget types */ enum snd_soc_dapm_type { snd_soc_dapm_input = 0, /* input pin */ diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index 04598f1efd77..a2e15cad76a0 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h @@ -11,6 +11,7 @@ #ifndef __LINUX_SND_SOC_DPCM_H #define __LINUX_SND_SOC_DPCM_H +#include #include #include @@ -135,4 +136,25 @@ int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute); int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd); int soc_dpcm_runtime_update(struct snd_soc_dapm_widget *); +int dpcm_path_get(struct snd_soc_pcm_runtime *fe, + int stream, struct snd_soc_dapm_widget_list **list_); +int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, + int stream, struct snd_soc_dapm_widget_list **list, int new); +int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream); +int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream); +void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream); +void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream); +int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream); +int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int tream); +int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, int cmd); +int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream); +int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, + int event); + +static inline void dpcm_path_put(struct snd_soc_dapm_widget_list **list) +{ + kfree(*list); +} + + #endif diff --git a/include/sound/soc.h b/include/sound/soc.h index 85c15226103b..9338da89e64a 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -26,6 +26,7 @@ #include #include #include +#include /* * Convenience kcontrol builders @@ -92,6 +93,12 @@ {.reg = xreg, .rreg = xreg, .shift = xshift, \ .rshift = xshift, .min = xmin, .max = xmax, \ .platform_max = xmax, .invert = xinvert} } +#define SND_SOC_BYTES_EXT(xname, xcount, xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_bytes_ext, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct soc_bytes_ext) \ + {.max = xcount} } #define SOC_DOUBLE(xname, reg, shift_left, shift_right, max, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \ @@ -420,6 +427,21 @@ struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card, const char *dai_link, int stream); struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card, const char *dai_link); +#if IS_ENABLED(CONFIG_SND_EFFECTS_OFFLOAD) +int snd_soc_register_effect(struct snd_soc_card *card, + struct snd_effect_ops *ops); +int snd_soc_unregister_effect(struct snd_soc_card *card); +#else +static inline int snd_soc_register_effect(struct snd_soc_card *card, + struct snd_effect_ops *ops) +{ + return -ENODEV; +} +static inline int snd_soc_unregister_effect(struct snd_soc_card *card) +{ + return -ENODEV; +} +#endif /* Utility functions to get clock rates from various things */ int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots); @@ -538,6 +560,8 @@ int snd_soc_get_strobe(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_put_strobe(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_soc_info_bytes_ext(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *ucontrol); /** * struct snd_soc_reg_access - Describes whether a given register is @@ -594,6 +618,7 @@ struct snd_soc_jack_zone { * @name: gpio name * @report: value to report when jack detected * @invert: report presence in low state + * @irq_flag: Interrupt flags for GPIO-Irq line * @debouce_time: debouce time in ms * @wake: enable as wake source * @jack_status_check: callback function which overrides the detection @@ -608,6 +633,7 @@ struct snd_soc_jack_gpio { int invert; int debounce_time; bool wake; + unsigned long irq_flags; struct snd_soc_jack *jack; struct delayed_work work; @@ -932,6 +958,10 @@ struct snd_soc_dai_link { /* machine stream operations */ const struct snd_soc_ops *ops; const struct snd_soc_compr_ops *compr_ops; + + /*no of substreams */ + unsigned int playback_count; + unsigned int capture_count; }; struct snd_soc_codec_conf { @@ -1063,6 +1093,7 @@ struct snd_soc_pcm_runtime { /* Dynamic PCM BE runtime data */ struct snd_soc_dpcm_runtime dpcm[2]; + int fe_compr; long pmdown_time; unsigned char pop_wait:1; @@ -1100,6 +1131,10 @@ struct soc_mreg_control { unsigned int regbase, regcount, nbits, invert; }; +struct soc_bytes_ext { + int max; +}; + /* enumerated kcontrol */ struct soc_enum { unsigned short reg; diff --git a/include/uapi/drm/radeon_drm.h b/include/uapi/drm/radeon_drm.h index fa8b3adf9ffb..321d4ac5c512 100644 --- a/include/uapi/drm/radeon_drm.h +++ b/include/uapi/drm/radeon_drm.h @@ -979,8 +979,6 @@ struct drm_radeon_cs { #define RADEON_INFO_RING_WORKING 0x15 /* SI tile mode array */ #define RADEON_INFO_SI_TILE_MODE_ARRAY 0x16 -/* query if CP DMA is supported on the compute ring */ -#define RADEON_INFO_SI_CP_DMA_COMPUTE 0x17 struct drm_radeon_info { diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 349600cd815f..bdc6e87ff3eb 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -56,7 +56,6 @@ header-y += atmppp.h header-y += atmsap.h header-y += atmsvc.h header-y += audit.h -header-y += aufs_type.h header-y += auto_fs.h header-y += auto_fs4.h header-y += auxvec.h diff --git a/include/uapi/linux/icmpv6.h b/include/uapi/linux/icmpv6.h index 590beda78ea0..e0133c73c304 100644 --- a/include/uapi/linux/icmpv6.h +++ b/include/uapi/linux/icmpv6.h @@ -115,8 +115,6 @@ struct icmp6hdr { #define ICMPV6_NOT_NEIGHBOUR 2 #define ICMPV6_ADDR_UNREACH 3 #define ICMPV6_PORT_UNREACH 4 -#define ICMPV6_POLICY_FAIL 5 -#define ICMPV6_REJECT_ROUTE 6 /* * Codes for Time Exceeded diff --git a/include/uapi/linux/mmc/ioctl.h b/include/uapi/linux/mmc/ioctl.h index 1f5e68923929..98ad95664d6e 100644 --- a/include/uapi/linux/mmc/ioctl.h +++ b/include/uapi/linux/mmc/ioctl.h @@ -47,6 +47,19 @@ struct mmc_ioc_cmd { #define MMC_IOC_CMD _IOWR(MMC_BLOCK_MAJOR, 0, struct mmc_ioc_cmd) +struct mmc_ioc_rpmb_req { + __u16 type; /* RPMB request type */ + __u16 *result; /* response or request result */ + __u16 blk_cnt; /* Number of blocks(half sector 256B) */ + __u16 addr; /* data address */ + __u32 *wc; /* write counter */ + __u8 *nonce; /* Ramdom number */ + __u8 *data; /* Buffer of the user data */ + __u8 *mac; /* Message Authentication Code */ +}; + +#define MMC_IOC_RPMB_REQ _IOWR(MMC_BLOCK_MAJOR, 1, struct mmc_ioc_rpmb_req) + /* * Since this ioctl is only meant to enhance (and not replace) normal access * to the mmc bus device, an upper data transfer limit of MMC_IOC_MAX_BYTES diff --git a/include/uapi/linux/msdos_fs.h b/include/uapi/linux/msdos_fs.h index 9ac8452d8955..f055e58b3147 100644 --- a/include/uapi/linux/msdos_fs.h +++ b/include/uapi/linux/msdos_fs.h @@ -45,7 +45,6 @@ #define CASE_LOWER_BASE 8 /* base is lower case */ #define CASE_LOWER_EXT 16 /* extension is lower case */ -#define FAT_NO_83NAME 32 /* no 8.3 short filename for this file */ #define DELETED_FLAG 0xe5 /* marks file as deleted when in name[0] */ #define IS_FREE(n) (!*(n) || *(n) == DELETED_FLAG) diff --git a/include/uapi/linux/nfs_mount.h b/include/uapi/linux/nfs_mount.h index 9541d718df98..576bddd72e04 100644 --- a/include/uapi/linux/nfs_mount.h +++ b/include/uapi/linux/nfs_mount.h @@ -20,7 +20,7 @@ * mount-to-kernel version compatibility. Some of these aren't used yet * but here they are anyway. */ -#define NFS_MOUNT_VERSION 7 +#define NFS_MOUNT_VERSION 6 #define NFS_MAX_CONTEXT_LEN 256 struct nfs_mount_data { @@ -43,8 +43,6 @@ struct nfs_mount_data { struct nfs3_fh root; /* 4 */ int pseudoflavor; /* 5 */ char context[NFS_MAX_CONTEXT_LEN + 1]; /* 6 */ - int nfs_prog; /* 7 */ - int mount_prog; /* 7 */ }; /* bits in the flags field visible to user space */ diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h index 09d62b9228ff..dbd71b0c7d8c 100644 --- a/include/uapi/linux/pkt_sched.h +++ b/include/uapi/linux/pkt_sched.h @@ -73,17 +73,9 @@ struct tc_estimator { #define TC_H_ROOT (0xFFFFFFFFU) #define TC_H_INGRESS (0xFFFFFFF1U) -/* Need to corrospond to iproute2 tc/tc_core.h "enum link_layer" */ -enum tc_link_layer { - TC_LINKLAYER_UNAWARE, /* Indicate unaware old iproute2 util */ - TC_LINKLAYER_ETHERNET, - TC_LINKLAYER_ATM, -}; -#define TC_LINKLAYER_MASK 0x0F /* limit use to lower 4 bits */ - struct tc_ratespec { unsigned char cell_log; - __u8 linklayer; /* lower 4 bits */ + unsigned char __reserved; unsigned short overhead; short cell_align; unsigned short mpu; diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h index d630163b9a2e..21eed488783f 100644 --- a/include/uapi/sound/compress_offload.h +++ b/include/uapi/sound/compress_offload.h @@ -30,7 +30,7 @@ #include -#define SNDRV_COMPRESS_VERSION SNDRV_PROTOCOL_VERSION(0, 1, 1) +#define SNDRV_COMPRESS_VERSION SNDRV_PROTOCOL_VERSION(0, 1, 2) /** * struct snd_compressed_buffer: compressed buffer * @fragment_size: size of buffer fragment in bytes @@ -67,8 +67,8 @@ struct snd_compr_params { struct snd_compr_tstamp { __u32 byte_offset; __u32 copied_total; - snd_pcm_uframes_t pcm_frames; - snd_pcm_uframes_t pcm_io_frames; + __u32 pcm_frames; + __u32 pcm_io_frames; __u32 sampling_rate; }; @@ -80,7 +80,7 @@ struct snd_compr_tstamp { struct snd_compr_avail { __u64 avail; struct snd_compr_tstamp tstamp; -}; +} __attribute__((packed)); enum snd_compr_direction { SND_COMPRESS_PLAYBACK = 0, diff --git a/include/uapi/sound/effect_offload.h b/include/uapi/sound/effect_offload.h new file mode 100644 index 000000000000..9c30031685d5 --- /dev/null +++ b/include/uapi/sound/effect_offload.h @@ -0,0 +1,62 @@ +/* + * effect_offload.h - effect offload header definations + * + * Copyright (C) 2013 Intel Corporation + * Authors: Vinod Koul + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ +#ifndef __EFFECT_OFFLOAD_H +#define __EFFECT_OFFLOAD_H + +#include + +#define SNDRV_EFFECT_VERSION SNDRV_PROTOCOL_VERSION(0, 1, 0) + +struct snd_effect { + char uuid[16]; /* effect UUID */ + int device; /* streaming interface for effect insertion */ + int pos; /* position of effect to be placed in effect chain */ + int mode; /* Backend for Global device (Headset/Speaker) */ +}; + +struct snd_effect_params { + char uuid[16]; + int device; + u32 size; /* size of parameter blob */ + char *buffer; +}; + +struct snd_effect_caps { + u32 size; /* size of buffer to read effect descriptors */ + char *buffer; +}; + +#define SNDRV_CTL_IOCTL_EFFECT_VERSION _IOR('E', 0x00, int) +#define SNDRV_CTL_IOCTL_EFFECT_CREATE _IOW('E', 0x01,\ + struct snd_effect *) +#define SNDRV_CTL_IOCTL_EFFECT_DESTROY _IOW('E', 0x02,\ + struct snd_effect *) +#define SNDRV_CTL_IOCTL_EFFECT_SET_PARAMS _IOW('E', 0x03,\ + struct snd_effect_params *) +#define SNDRV_CTL_IOCTL_EFFECT_GET_PARAMS _IOWR('E', 0x04,\ + struct snd_effect_params *) +#define SNDRV_CTL_IOCTL_EFFECT_QUERY_NUM _IOR('E', 0x05, int) +#define SNDRV_CTL_IOCTL_EFFECT_QUERY_CAPS _IOWR('E', 0x06,\ + struct snd_effect_caps *) +#endif diff --git a/init/Kconfig b/init/Kconfig index 0b5d0c8e2595..2d9b83104dcf 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1261,31 +1261,6 @@ menuconfig EXPERT environments which can tolerate a "non-standard" kernel. Only use this if you really know what you are doing. -config UPTIME_LIMITED_KERNEL - bool "Create a kernel with uptime limitations" - default n - help - Limit the amount of time a kernel can run. The associated UPTIME_LIMIT* - kernel config options should be used to tune the behaviour. - -config UPTIME_LIMIT_DURATION - int "Kernel uptime limit in minutes" - depends on UPTIME_LIMITED_KERNEL - range 0 14400 - default 0 - help - Define the uptime limitation on a kernel in minutes. Once - the defined time expires the kernel will emit a warning, cease - to be usable and eventually restart. The valid range is 0 (disable) - to 14400 (10 days) - -config UPTIME_LIMIT_KERNEL_REBOOT - bool "Reboot a time limited kernel at expiration" - depends on UPTIME_LIMITED_KERNEL - default y - help - Reboot an uptime limited kernel at expiration. - config UID16 bool "Enable 16-bit UID system calls" if EXPERT depends on HAVE_UID16 diff --git a/init/do_mounts.c b/init/do_mounts.c index 4b4936d1f730..a2b49f2c1bd8 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -412,9 +412,7 @@ void __init mount_block_root(char *name, int flags) printk("DEBUG_BLOCK_EXT_DEVT is enabled, you need to specify " "explicit textual name for \"root=\" boot option.\n"); #endif - printk(KERN_EMERG "VFS: Unable to mount root fs on %s\n", b); - printk(KERN_EMERG "User configuration error - no valid root filesystem found\n"); - panic("Invalid configuration from end user prevents continuing"); + panic("VFS: Unable to mount root fs on %s", b); } printk("List of all partitions:\n"); @@ -426,9 +424,7 @@ void __init mount_block_root(char *name, int flags) #ifdef CONFIG_BLOCK __bdevname(ROOT_DEV, b); #endif - printk(KERN_EMERG "VFS: Unable to mount root fs on %s\n", b); - printk(KERN_EMERG "User configuration error - no valid root filesystem found\n"); - panic("Invalid configuration from end user prevents continuing"); + panic("VFS: Unable to mount root fs on %s", b); out: put_page(page); } diff --git a/init/main.c b/init/main.c index beddd350212f..704f85e8a915 100644 --- a/init/main.c +++ b/init/main.c @@ -74,7 +74,6 @@ #include #include #include -#include #include #include @@ -780,7 +779,6 @@ static void __init do_basic_setup(void) do_ctors(); usermodehelper_enable(); do_initcalls(); - random_int_secret_init(); } static void __init do_pre_smp_initcalls(void) @@ -854,7 +852,6 @@ static int __ref kernel_init(void *unused) static noinline void __init kernel_init_freeable(void) { - struct stat console_stat; /* * Wait until kthreadd is all set-up. */ @@ -884,12 +881,6 @@ static noinline void __init kernel_init_freeable(void) do_basic_setup(); - /* Use /dev/console to infer if the rootfs is setup properly */ - if (sys_newlstat((char __user *) "/dev/console", (struct stat __user *) &console_stat) - || !S_ISCHR(console_stat.st_mode)) { - panic("/dev/console is missing or not a character device!\nPlease ensure your rootfs is properly configured\n"); - } - /* Open the /dev/console on the rootfs, this should never fail */ if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) pr_err("Warning: unable to open an initial console.\n"); diff --git a/ipc/msg.c b/ipc/msg.c index 558aa91186b6..d0c6d967b390 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -70,6 +70,8 @@ struct msg_sender { #define msg_ids(ns) ((ns)->ids[IPC_MSG_IDS]) +#define msg_unlock(msq) ipc_unlock(&(msq)->q_perm) + static void freeque(struct ipc_namespace *, struct kern_ipc_perm *); static int newque(struct ipc_namespace *, struct ipc_params *); #ifdef CONFIG_PROC_FS @@ -139,23 +141,27 @@ void __init msg_init(void) IPC_MSG_IDS, sysvipc_msg_proc_show); } -static inline struct msg_queue *msq_obtain_object(struct ipc_namespace *ns, int id) +/* + * msg_lock_(check_) routines are called in the paths where the rw_mutex + * is not held. + */ +static inline struct msg_queue *msg_lock(struct ipc_namespace *ns, int id) { - struct kern_ipc_perm *ipcp = ipc_obtain_object(&msg_ids(ns), id); + struct kern_ipc_perm *ipcp = ipc_lock(&msg_ids(ns), id); if (IS_ERR(ipcp)) - return ERR_CAST(ipcp); + return (struct msg_queue *)ipcp; return container_of(ipcp, struct msg_queue, q_perm); } -static inline struct msg_queue *msq_obtain_object_check(struct ipc_namespace *ns, - int id) +static inline struct msg_queue *msg_lock_check(struct ipc_namespace *ns, + int id) { - struct kern_ipc_perm *ipcp = ipc_obtain_object_check(&msg_ids(ns), id); + struct kern_ipc_perm *ipcp = ipc_lock_check(&msg_ids(ns), id); if (IS_ERR(ipcp)) - return ERR_CAST(ipcp); + return (struct msg_queue *)ipcp; return container_of(ipcp, struct msg_queue, q_perm); } @@ -165,21 +171,12 @@ static inline void msg_rmid(struct ipc_namespace *ns, struct msg_queue *s) ipc_rmid(&msg_ids(ns), &s->q_perm); } -static void msg_rcu_free(struct rcu_head *head) -{ - struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu); - struct msg_queue *msq = ipc_rcu_to_struct(p); - - security_msg_queue_free(msq); - ipc_rcu_free(head); -} - /** * newque - Create a new msg queue * @ns: namespace * @params: ptr to the structure that contains the key and msgflg * - * Called with msg_ids.rwsem held (writer) + * Called with msg_ids.rw_mutex held (writer) */ static int newque(struct ipc_namespace *ns, struct ipc_params *params) { @@ -198,14 +195,17 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params) msq->q_perm.security = NULL; retval = security_msg_queue_alloc(msq); if (retval) { - ipc_rcu_putref(msq, ipc_rcu_free); + ipc_rcu_putref(msq); return retval; } - /* ipc_addid() locks msq upon success. */ + /* + * ipc_addid() locks msq + */ id = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni); if (id < 0) { - ipc_rcu_putref(msq, msg_rcu_free); + security_msg_queue_free(msq); + ipc_rcu_putref(msq); return id; } @@ -218,8 +218,7 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params) INIT_LIST_HEAD(&msq->q_receivers); INIT_LIST_HEAD(&msq->q_senders); - ipc_unlock_object(&msq->q_perm); - rcu_read_unlock(); + msg_unlock(msq); return msq->q_perm.id; } @@ -265,8 +264,8 @@ static void expunge_all(struct msg_queue *msq, int res) * removes the message queue from message queue ID IDR, and cleans up all the * messages associated with this queue. * - * msg_ids.rwsem (writer) and the spinlock for this message queue are held - * before freeque() is called. msg_ids.rwsem remains locked on exit. + * msg_ids.rw_mutex (writer) and the spinlock for this message queue are held + * before freeque() is called. msg_ids.rw_mutex remains locked on exit. */ static void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) { @@ -276,19 +275,19 @@ static void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) expunge_all(msq, -EIDRM); ss_wakeup(&msq->q_senders, 1); msg_rmid(ns, msq); - ipc_unlock_object(&msq->q_perm); - rcu_read_unlock(); + msg_unlock(msq); list_for_each_entry_safe(msg, t, &msq->q_messages, m_list) { atomic_dec(&ns->msg_hdrs); free_msg(msg); } atomic_sub(msq->q_cbytes, &ns->msg_bytes); - ipc_rcu_putref(msq, msg_rcu_free); + security_msg_queue_free(msq); + ipc_rcu_putref(msq); } /* - * Called with msg_ids.rwsem and ipcp locked. + * Called with msg_ids.rw_mutex and ipcp locked. */ static inline int msg_security(struct kern_ipc_perm *ipcp, int msgflg) { @@ -392,9 +391,9 @@ copy_msqid_from_user(struct msqid64_ds *out, void __user *buf, int version) } /* - * This function handles some msgctl commands which require the rwsem + * This function handles some msgctl commands which require the rw_mutex * to be held in write mode. - * NOTE: no locks must be held, the rwsem is taken inside this function. + * NOTE: no locks must be held, the rw_mutex is taken inside this function. */ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, struct msqid_ds __user *buf, int version) @@ -409,39 +408,31 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, return -EFAULT; } - down_write(&msg_ids(ns).rwsem); - rcu_read_lock(); - - ipcp = ipcctl_pre_down_nolock(ns, &msg_ids(ns), msqid, cmd, - &msqid64.msg_perm, msqid64.msg_qbytes); - if (IS_ERR(ipcp)) { - err = PTR_ERR(ipcp); - goto out_unlock1; - } + ipcp = ipcctl_pre_down(ns, &msg_ids(ns), msqid, cmd, + &msqid64.msg_perm, msqid64.msg_qbytes); + if (IS_ERR(ipcp)) + return PTR_ERR(ipcp); msq = container_of(ipcp, struct msg_queue, q_perm); err = security_msg_queue_msgctl(msq, cmd); if (err) - goto out_unlock1; + goto out_unlock; switch (cmd) { case IPC_RMID: - ipc_lock_object(&msq->q_perm); - /* freeque unlocks the ipc object and rcu */ freeque(ns, ipcp); goto out_up; case IPC_SET: if (msqid64.msg_qbytes > ns->msg_ctlmnb && !capable(CAP_SYS_RESOURCE)) { err = -EPERM; - goto out_unlock1; + goto out_unlock; } - ipc_lock_object(&msq->q_perm); err = ipc_update_perm(&msqid64.msg_perm, ipcp); if (err) - goto out_unlock0; + goto out_unlock; msq->q_qbytes = msqid64.msg_qbytes; @@ -457,23 +448,25 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd, break; default: err = -EINVAL; - goto out_unlock1; } - -out_unlock0: - ipc_unlock_object(&msq->q_perm); -out_unlock1: - rcu_read_unlock(); +out_unlock: + msg_unlock(msq); out_up: - up_write(&msg_ids(ns).rwsem); + up_write(&msg_ids(ns).rw_mutex); return err; } -static int msgctl_nolock(struct ipc_namespace *ns, int msqid, - int cmd, int version, void __user *buf) +SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf) { - int err; struct msg_queue *msq; + int err, version; + struct ipc_namespace *ns; + + if (msqid < 0 || cmd < 0) + return -EINVAL; + + version = ipc_parse_version(&cmd); + ns = current->nsproxy->ipc_ns; switch (cmd) { case IPC_INFO: @@ -484,7 +477,6 @@ static int msgctl_nolock(struct ipc_namespace *ns, int msqid, if (!buf) return -EFAULT; - /* * We must not return kernel stack data. * due to padding, it's not enough @@ -500,7 +492,7 @@ static int msgctl_nolock(struct ipc_namespace *ns, int msqid, msginfo.msgmnb = ns->msg_ctlmnb; msginfo.msgssz = MSGSSZ; msginfo.msgseg = MSGSEG; - down_read(&msg_ids(ns).rwsem); + down_read(&msg_ids(ns).rw_mutex); if (cmd == MSG_INFO) { msginfo.msgpool = msg_ids(ns).in_use; msginfo.msgmap = atomic_read(&ns->msg_hdrs); @@ -511,13 +503,12 @@ static int msgctl_nolock(struct ipc_namespace *ns, int msqid, msginfo.msgtql = MSGTQL; } max_id = ipc_get_maxid(&msg_ids(ns)); - up_read(&msg_ids(ns).rwsem); + up_read(&msg_ids(ns).rw_mutex); if (copy_to_user(buf, &msginfo, sizeof(struct msginfo))) return -EFAULT; return (max_id < 0) ? 0 : max_id; } - - case MSG_STAT: + case MSG_STAT: /* msqid is an index rather than a msg queue id */ case IPC_STAT: { struct msqid64_ds tbuf; @@ -526,25 +517,17 @@ static int msgctl_nolock(struct ipc_namespace *ns, int msqid, if (!buf) return -EFAULT; - memset(&tbuf, 0, sizeof(tbuf)); - - rcu_read_lock(); if (cmd == MSG_STAT) { - msq = msq_obtain_object(ns, msqid); - if (IS_ERR(msq)) { - err = PTR_ERR(msq); - goto out_unlock; - } + msq = msg_lock(ns, msqid); + if (IS_ERR(msq)) + return PTR_ERR(msq); success_return = msq->q_perm.id; } else { - msq = msq_obtain_object_check(ns, msqid); - if (IS_ERR(msq)) { - err = PTR_ERR(msq); - goto out_unlock; - } + msq = msg_lock_check(ns, msqid); + if (IS_ERR(msq)) + return PTR_ERR(msq); success_return = 0; } - err = -EACCES; if (ipcperms(ns, &msq->q_perm, S_IRUGO)) goto out_unlock; @@ -553,6 +536,8 @@ static int msgctl_nolock(struct ipc_namespace *ns, int msqid, if (err) goto out_unlock; + memset(&tbuf, 0, sizeof(tbuf)); + kernel_to_ipc64_perm(&msq->q_perm, &tbuf.msg_perm); tbuf.msg_stime = msq->q_stime; tbuf.msg_rtime = msq->q_rtime; @@ -562,46 +547,22 @@ static int msgctl_nolock(struct ipc_namespace *ns, int msqid, tbuf.msg_qbytes = msq->q_qbytes; tbuf.msg_lspid = msq->q_lspid; tbuf.msg_lrpid = msq->q_lrpid; - rcu_read_unlock(); - + msg_unlock(msq); if (copy_msqid_to_user(buf, &tbuf, version)) return -EFAULT; return success_return; } - - default: - return -EINVAL; - } - - return err; -out_unlock: - rcu_read_unlock(); - return err; -} - -SYSCALL_DEFINE3(msgctl, int, msqid, int, cmd, struct msqid_ds __user *, buf) -{ - int version; - struct ipc_namespace *ns; - - if (msqid < 0 || cmd < 0) - return -EINVAL; - - version = ipc_parse_version(&cmd); - ns = current->nsproxy->ipc_ns; - - switch (cmd) { - case IPC_INFO: - case MSG_INFO: - case MSG_STAT: /* msqid is an index rather than a msg queue id */ - case IPC_STAT: - return msgctl_nolock(ns, msqid, cmd, version, buf); case IPC_SET: case IPC_RMID: - return msgctl_down(ns, msqid, cmd, buf, version); + err = msgctl_down(ns, msqid, cmd, buf, version); + return err; default: return -EINVAL; } + +out_unlock: + msg_unlock(msq); + return err; } static int testmsg(struct msg_msg *msg, long type, int mode) @@ -679,31 +640,22 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext, msg->m_type = mtype; msg->m_ts = msgsz; - rcu_read_lock(); - msq = msq_obtain_object_check(ns, msqid); + msq = msg_lock_check(ns, msqid); if (IS_ERR(msq)) { err = PTR_ERR(msq); - goto out_unlock1; + goto out_free; } - ipc_lock_object(&msq->q_perm); - for (;;) { struct msg_sender s; err = -EACCES; if (ipcperms(ns, &msq->q_perm, S_IWUGO)) - goto out_unlock0; - - /* raced with RMID? */ - if (msq->q_perm.deleted) { - err = -EIDRM; - goto out_unlock0; - } + goto out_unlock_free; err = security_msg_queue_msgsnd(msq, msg, msgflg); if (err) - goto out_unlock0; + goto out_unlock_free; if (msgsz + msq->q_cbytes <= msq->q_qbytes && 1 + msq->q_qnum <= msq->q_qbytes) { @@ -713,37 +665,32 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext, /* queue full, wait: */ if (msgflg & IPC_NOWAIT) { err = -EAGAIN; - goto out_unlock0; + goto out_unlock_free; } - ss_add(msq, &s); if (!ipc_rcu_getref(msq)) { err = -EIDRM; - goto out_unlock0; + goto out_unlock_free; } - ipc_unlock_object(&msq->q_perm); - rcu_read_unlock(); + msg_unlock(msq); schedule(); - rcu_read_lock(); - ipc_lock_object(&msq->q_perm); - - ipc_rcu_putref(msq, ipc_rcu_free); + ipc_lock_by_ptr(&msq->q_perm); + ipc_rcu_putref(msq); if (msq->q_perm.deleted) { err = -EIDRM; - goto out_unlock0; + goto out_unlock_free; } - ss_del(&s); if (signal_pending(current)) { err = -ERESTARTNOHAND; - goto out_unlock0; + goto out_unlock_free; } - } + msq->q_lspid = task_tgid_vnr(current); msq->q_stime = get_seconds(); @@ -759,10 +706,9 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext, err = 0; msg = NULL; -out_unlock0: - ipc_unlock_object(&msq->q_perm); -out_unlock1: - rcu_read_unlock(); +out_unlock_free: + msg_unlock(msq); +out_free: if (msg != NULL) free_msg(msg); return err; @@ -849,7 +795,7 @@ static inline void free_copy(struct msg_msg *copy) static struct msg_msg *find_msg(struct msg_queue *msq, long *msgtyp, int mode) { - struct msg_msg *msg, *found = NULL; + struct msg_msg *msg; long count = 0; list_for_each_entry(msg, &msq->q_messages, m_list) { @@ -858,7 +804,6 @@ static struct msg_msg *find_msg(struct msg_queue *msq, long *msgtyp, int mode) *msgtyp, mode)) { if (mode == SEARCH_LESSEQUAL && msg->m_type != 1) { *msgtyp = msg->m_type - 1; - found = msg; } else if (mode == SEARCH_NUMBER) { if (*msgtyp == count) return msg; @@ -868,22 +813,24 @@ static struct msg_msg *find_msg(struct msg_queue *msq, long *msgtyp, int mode) } } - return found ?: ERR_PTR(-EAGAIN); + return ERR_PTR(-EAGAIN); } -long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgflg, + +long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, + int msgflg, long (*msg_handler)(void __user *, struct msg_msg *, size_t)) { - int mode; struct msg_queue *msq; + struct msg_msg *msg; + int mode; struct ipc_namespace *ns; - struct msg_msg *msg, *copy = NULL; + struct msg_msg *copy = NULL; ns = current->nsproxy->ipc_ns; if (msqid < 0 || (long) bufsz < 0) return -EINVAL; - if (msgflg & MSG_COPY) { copy = prepare_copy(buf, min_t(size_t, bufsz, ns->msg_ctlmax)); if (IS_ERR(copy)) @@ -891,10 +838,8 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgfl } mode = convert_mode(&msgtyp, msgflg); - rcu_read_lock(); - msq = msq_obtain_object_check(ns, msqid); + msq = msg_lock_check(ns, msqid); if (IS_ERR(msq)) { - rcu_read_unlock(); free_copy(copy); return PTR_ERR(msq); } @@ -904,17 +849,10 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgfl msg = ERR_PTR(-EACCES); if (ipcperms(ns, &msq->q_perm, S_IRUGO)) - goto out_unlock1; - - ipc_lock_object(&msq->q_perm); - - /* raced with RMID? */ - if (msq->q_perm.deleted) { - msg = ERR_PTR(-EIDRM); - goto out_unlock0; - } + goto out_unlock; msg = find_msg(msq, &msgtyp, mode); + if (!IS_ERR(msg)) { /* * Found a suitable message. @@ -922,7 +860,7 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgfl */ if ((bufsz < msg->m_ts) && !(msgflg & MSG_NOERROR)) { msg = ERR_PTR(-E2BIG); - goto out_unlock0; + goto out_unlock; } /* * If we are copying, then do not unlink message and do @@ -930,9 +868,8 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgfl */ if (msgflg & MSG_COPY) { msg = copy_msg(msg, copy); - goto out_unlock0; + goto out_unlock; } - list_del(&msg->m_list); msq->q_qnum--; msq->q_rtime = get_seconds(); @@ -941,16 +878,14 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgfl atomic_sub(msg->m_ts, &ns->msg_bytes); atomic_dec(&ns->msg_hdrs); ss_wakeup(&msq->q_senders, 0); - - goto out_unlock0; + msg_unlock(msq); + break; } - /* No message waiting. Wait for a message */ if (msgflg & IPC_NOWAIT) { msg = ERR_PTR(-ENOMSG); - goto out_unlock0; + goto out_unlock; } - list_add_tail(&msr_d.r_list, &msq->q_receivers); msr_d.r_tsk = current; msr_d.r_msgtype = msgtyp; @@ -961,9 +896,8 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgfl msr_d.r_maxsize = bufsz; msr_d.r_msg = ERR_PTR(-EAGAIN); current->state = TASK_INTERRUPTIBLE; + msg_unlock(msq); - ipc_unlock_object(&msq->q_perm); - rcu_read_unlock(); schedule(); /* Lockless receive, part 1: @@ -974,7 +908,7 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgfl * Prior to destruction, expunge_all(-EIRDM) changes r_msg. * Thus if r_msg is -EAGAIN, then the queue not yet destroyed. * rcu_read_lock() prevents preemption between reading r_msg - * and acquiring the q_perm.lock in ipc_lock_object(). + * and the spin_lock() inside ipc_lock_by_ptr(). */ rcu_read_lock(); @@ -993,34 +927,32 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgfl * If there is a message or an error then accept it without * locking. */ - if (msg != ERR_PTR(-EAGAIN)) - goto out_unlock1; + if (msg != ERR_PTR(-EAGAIN)) { + rcu_read_unlock(); + break; + } /* Lockless receive, part 3: * Acquire the queue spinlock. */ - ipc_lock_object(&msq->q_perm); + ipc_lock_by_ptr(&msq->q_perm); + rcu_read_unlock(); /* Lockless receive, part 4: * Repeat test after acquiring the spinlock. */ msg = (struct msg_msg*)msr_d.r_msg; if (msg != ERR_PTR(-EAGAIN)) - goto out_unlock0; + goto out_unlock; list_del(&msr_d.r_list); if (signal_pending(current)) { msg = ERR_PTR(-ERESTARTNOHAND); - goto out_unlock0; +out_unlock: + msg_unlock(msq); + break; } - - ipc_unlock_object(&msq->q_perm); } - -out_unlock0: - ipc_unlock_object(&msq->q_perm); -out_unlock1: - rcu_read_unlock(); if (IS_ERR(msg)) { free_copy(copy); return PTR_ERR(msg); diff --git a/ipc/namespace.c b/ipc/namespace.c index aba9a586c667..7ee61bf44933 100644 --- a/ipc/namespace.c +++ b/ipc/namespace.c @@ -81,7 +81,7 @@ void free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids, int next_id; int total, in_use; - down_write(&ids->rwsem); + down_write(&ids->rw_mutex); in_use = ids->in_use; @@ -89,12 +89,11 @@ void free_ipcs(struct ipc_namespace *ns, struct ipc_ids *ids, perm = idr_find(&ids->ipcs_idr, next_id); if (perm == NULL) continue; - rcu_read_lock(); - ipc_lock_object(perm); + ipc_lock_by_ptr(perm); free(ns, perm); total++; } - up_write(&ids->rwsem); + up_write(&ids->rw_mutex); } static void free_ipc_ns(struct ipc_namespace *ns) diff --git a/ipc/sem.c b/ipc/sem.c index 8c4f59b0204a..70480a3aa698 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -95,12 +95,8 @@ struct sem { int semval; /* current value */ int sempid; /* pid of last operation */ spinlock_t lock; /* spinlock for fine-grained semtimedop */ - struct list_head pending_alter; /* pending single-sop operations */ - /* that alter the semaphore */ - struct list_head pending_const; /* pending single-sop operations */ - /* that do not alter the semaphore*/ - time_t sem_otime; /* candidate for sem_otime */ -} ____cacheline_aligned_in_smp; + struct list_head sem_pending; /* pending single-sop operations */ +}; /* One queue for each sleeping process in the system. */ struct sem_queue { @@ -154,15 +150,12 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it); #define SEMOPM_FAST 64 /* ~ 372 bytes on stack */ /* - * Locking: + * linked list protection: * sem_undo.id_next, - * sem_array.complex_count, - * sem_array.pending{_alter,_cont}, - * sem_array.sem_undo: global sem_lock() for read/write + * sem_array.sem_pending{,last}, + * sem_array.sem_undo: sem_lock() for read/write * sem_undo.proc_next: only "current" is allowed to read/write that field. * - * sem_array.sem_base[i].pending_{const,alter}: - * global or semaphore sem_lock() for read/write */ #define sc_semmsl sem_ctls[0] @@ -196,176 +189,77 @@ void __init sem_init (void) IPC_SEM_IDS, sysvipc_sem_proc_show); } -/** - * unmerge_queues - unmerge queues, if possible. - * @sma: semaphore array - * - * The function unmerges the wait queues if complex_count is 0. - * It must be called prior to dropping the global semaphore array lock. - */ -static void unmerge_queues(struct sem_array *sma) -{ - struct sem_queue *q, *tq; - - /* complex operations still around? */ - if (sma->complex_count) - return; - /* - * We will switch back to simple mode. - * Move all pending operation back into the per-semaphore - * queues. - */ - list_for_each_entry_safe(q, tq, &sma->pending_alter, list) { - struct sem *curr; - curr = &sma->sem_base[q->sops[0].sem_num]; - - list_add_tail(&q->list, &curr->pending_alter); - } - INIT_LIST_HEAD(&sma->pending_alter); -} - -/** - * merge_queues - Merge single semop queues into global queue - * @sma: semaphore array - * - * This function merges all per-semaphore queues into the global queue. - * It is necessary to achieve FIFO ordering for the pending single-sop - * operations when a multi-semop operation must sleep. - * Only the alter operations must be moved, the const operations can stay. - */ -static void merge_queues(struct sem_array *sma) -{ - int i; - for (i = 0; i < sma->sem_nsems; i++) { - struct sem *sem = sma->sem_base + i; - - list_splice_init(&sem->pending_alter, &sma->pending_alter); - } -} - -static void sem_rcu_free(struct rcu_head *head) -{ - struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu); - struct sem_array *sma = ipc_rcu_to_struct(p); - - security_sem_free(sma); - ipc_rcu_free(head); -} - -/* - * Wait until all currently ongoing simple ops have completed. - * Caller must own sem_perm.lock. - * New simple ops cannot start, because simple ops first check - * that sem_perm.lock is free. - * that a) sem_perm.lock is free and b) complex_count is 0. - */ -static void sem_wait_array(struct sem_array *sma) -{ - int i; - struct sem *sem; - - if (sma->complex_count) { - /* The thread that increased sma->complex_count waited on - * all sem->lock locks. Thus we don't need to wait again. - */ - return; - } - - for (i = 0; i < sma->sem_nsems; i++) { - sem = sma->sem_base + i; - spin_unlock_wait(&sem->lock); - } -} - /* * If the request contains only one semaphore operation, and there are * no complex transactions pending, lock only the semaphore involved. * Otherwise, lock the entire semaphore array, since we either have * multiple semaphores in our own semops, or we need to look at * semaphores from other pending complex operations. + * + * Carefully guard against sma->complex_count changing between zero + * and non-zero while we are spinning for the lock. The value of + * sma->complex_count cannot change while we are holding the lock, + * so sem_unlock should be fine. + * + * The global lock path checks that all the local locks have been released, + * checking each local lock once. This means that the local lock paths + * cannot start their critical sections while the global lock is held. */ static inline int sem_lock(struct sem_array *sma, struct sembuf *sops, int nsops) { - struct sem *sem; + int locknum; + again: + if (nsops == 1 && !sma->complex_count) { + struct sem *sem = sma->sem_base + sops->sem_num; - if (nsops != 1) { - /* Complex operation - acquire a full lock */ - ipc_lock_object(&sma->sem_perm); + /* Lock just the semaphore we are interested in. */ + spin_lock(&sem->lock); - /* And wait until all simple ops that are processed - * right now have dropped their locks. + /* + * If sma->complex_count was set while we were spinning, + * we may need to look at things we did not lock here. */ - sem_wait_array(sma); - return -1; - } - - /* - * Only one semaphore affected - try to optimize locking. - * The rules are: - * - optimized locking is possible if no complex operation - * is either enqueued or processed right now. - * - The test for enqueued complex ops is simple: - * sma->complex_count != 0 - * - Testing for complex ops that are processed right now is - * a bit more difficult. Complex ops acquire the full lock - * and first wait that the running simple ops have completed. - * (see above) - * Thus: If we own a simple lock and the global lock is free - * and complex_count is now 0, then it will stay 0 and - * thus just locking sem->lock is sufficient. - */ - sem = sma->sem_base + sops->sem_num; + if (unlikely(sma->complex_count)) { + spin_unlock(&sem->lock); + goto lock_array; + } - if (sma->complex_count == 0) { /* - * It appears that no complex operation is around. - * Acquire the per-semaphore lock. + * Another process is holding the global lock on the + * sem_array; we cannot enter our critical section, + * but have to wait for the global lock to be released. */ - spin_lock(&sem->lock); - - /* Then check that the global lock is free */ - if (!spin_is_locked(&sma->sem_perm.lock)) { - /* spin_is_locked() is not a memory barrier */ - smp_mb(); - - /* Now repeat the test of complex_count: - * It can't change anymore until we drop sem->lock. - * Thus: if is now 0, then it will stay 0. - */ - if (sma->complex_count == 0) { - /* fast path successful! */ - return sops->sem_num; - } + if (unlikely(spin_is_locked(&sma->sem_perm.lock))) { + spin_unlock(&sem->lock); + spin_unlock_wait(&sma->sem_perm.lock); + goto again; } - spin_unlock(&sem->lock); - } - - /* slow path: acquire the full lock */ - ipc_lock_object(&sma->sem_perm); - if (sma->complex_count == 0) { - /* False alarm: - * There is no complex operation, thus we can switch - * back to the fast path. - */ - spin_lock(&sem->lock); - ipc_unlock_object(&sma->sem_perm); - return sops->sem_num; + locknum = sops->sem_num; } else { - /* Not a false alarm, thus complete the sequence for a - * full lock. + int i; + /* + * Lock the semaphore array, and wait for all of the + * individual semaphore locks to go away. The code + * above ensures no new single-lock holders will enter + * their critical section while the array lock is held. */ - sem_wait_array(sma); - return -1; + lock_array: + spin_lock(&sma->sem_perm.lock); + for (i = 0; i < sma->sem_nsems; i++) { + struct sem *sem = sma->sem_base + i; + spin_unlock_wait(&sem->lock); + } + locknum = -1; } + return locknum; } static inline void sem_unlock(struct sem_array *sma, int locknum) { if (locknum == -1) { - unmerge_queues(sma); - ipc_unlock_object(&sma->sem_perm); + spin_unlock(&sma->sem_perm.lock); } else { struct sem *sem = sma->sem_base + locknum; spin_unlock(&sem->lock); @@ -373,7 +267,7 @@ static inline void sem_unlock(struct sem_array *sma, int locknum) } /* - * sem_lock_(check_) routines are called in the paths where the rwsem + * sem_lock_(check_) routines are called in the paths where the rw_mutex * is not held. * * The caller holds the RCU read lock. @@ -425,7 +319,12 @@ static inline struct sem_array *sem_obtain_object_check(struct ipc_namespace *ns static inline void sem_lock_and_putref(struct sem_array *sma) { sem_lock(sma, NULL, -1); - ipc_rcu_putref(sma, ipc_rcu_free); + ipc_rcu_putref(sma); +} + +static inline void sem_putref(struct sem_array *sma) +{ + ipc_rcu_putref(sma); } static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s) @@ -438,7 +337,7 @@ static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s) * Without the check/retry algorithm a lockless wakeup is possible: * - queue.status is initialized to -EINTR before blocking. * - wakeup is performed by - * * unlinking the queue entry from the pending list + * * unlinking the queue entry from sma->sem_pending * * setting queue.status to IN_WAKEUP * This is the notification for the blocked thread that a * result value is imminent. @@ -472,7 +371,7 @@ static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s) * @ns: namespace * @params: ptr to the structure that contains key, semflg and nsems * - * Called with sem_ids.rwsem held (as a writer) + * Called with sem_ids.rw_mutex held (as a writer) */ static int newary(struct ipc_namespace *ns, struct ipc_params *params) @@ -504,13 +403,14 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params) sma->sem_perm.security = NULL; retval = security_sem_alloc(sma); if (retval) { - ipc_rcu_putref(sma, ipc_rcu_free); + ipc_rcu_putref(sma); return retval; } id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni); if (id < 0) { - ipc_rcu_putref(sma, sem_rcu_free); + security_sem_free(sma); + ipc_rcu_putref(sma); return id; } ns->used_sems += nsems; @@ -518,14 +418,12 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params) sma->sem_base = (struct sem *) &sma[1]; for (i = 0; i < nsems; i++) { - INIT_LIST_HEAD(&sma->sem_base[i].pending_alter); - INIT_LIST_HEAD(&sma->sem_base[i].pending_const); + INIT_LIST_HEAD(&sma->sem_base[i].sem_pending); spin_lock_init(&sma->sem_base[i].lock); } sma->complex_count = 0; - INIT_LIST_HEAD(&sma->pending_alter); - INIT_LIST_HEAD(&sma->pending_const); + INIT_LIST_HEAD(&sma->sem_pending); INIT_LIST_HEAD(&sma->list_id); sma->sem_nsems = nsems; sma->sem_ctime = get_seconds(); @@ -537,7 +435,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params) /* - * Called with sem_ids.rwsem and ipcp locked. + * Called with sem_ids.rw_mutex and ipcp locked. */ static inline int sem_security(struct kern_ipc_perm *ipcp, int semflg) { @@ -548,7 +446,7 @@ static inline int sem_security(struct kern_ipc_perm *ipcp, int semflg) } /* - * Called with sem_ids.rwsem and ipcp locked. + * Called with sem_ids.rw_mutex and ipcp locked. */ static inline int sem_more_checks(struct kern_ipc_perm *ipcp, struct ipc_params *params) @@ -584,19 +482,12 @@ SYSCALL_DEFINE3(semget, key_t, key, int, nsems, int, semflg) return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params); } -/** perform_atomic_semop - Perform (if possible) a semaphore operation - * @sma: semaphore array - * @sops: array with operations that should be checked - * @nsems: number of sops - * @un: undo array - * @pid: pid that did the change - * - * Returns 0 if the operation was possible. - * Returns 1 if the operation is impossible, the caller must sleep. - * Negative values are error codes. +/* + * Determine whether a sequence of semaphore operations would succeed + * all at once. Return 0 if yes, 1 if need to sleep, else return error code. */ -static int perform_atomic_semop(struct sem_array *sma, struct sembuf *sops, +static int try_atomic_semop (struct sem_array * sma, struct sembuf * sops, int nsops, struct sem_undo *un, int pid) { int result, sem_op; @@ -718,132 +609,60 @@ static void unlink_queue(struct sem_array *sma, struct sem_queue *q) * update_queue is O(N^2) when it restarts scanning the whole queue of * waiting operations. Therefore this function checks if the restart is * really necessary. It is called after a previously waiting operation - * modified the array. - * Note that wait-for-zero operations are handled without restart. + * was completed. */ static int check_restart(struct sem_array *sma, struct sem_queue *q) { - /* pending complex alter operations are too difficult to analyse */ - if (!list_empty(&sma->pending_alter)) + struct sem *curr; + struct sem_queue *h; + + /* if the operation didn't modify the array, then no restart */ + if (q->alter == 0) + return 0; + + /* pending complex operations are too difficult to analyse */ + if (sma->complex_count) return 1; /* we were a sleeping complex operation. Too difficult */ if (q->nsops > 1) return 1; - /* It is impossible that someone waits for the new value: - * - complex operations always restart. - * - wait-for-zero are handled seperately. - * - q is a previously sleeping simple operation that - * altered the array. It must be a decrement, because - * simple increments never sleep. - * - If there are older (higher priority) decrements - * in the queue, then they have observed the original - * semval value and couldn't proceed. The operation - * decremented to value - thus they won't proceed either. - */ - return 0; -} + curr = sma->sem_base + q->sops[0].sem_num; -/** - * wake_const_ops(sma, semnum, pt) - Wake up non-alter tasks - * @sma: semaphore array. - * @semnum: semaphore that was modified. - * @pt: list head for the tasks that must be woken up. - * - * wake_const_ops must be called after a semaphore in a semaphore array - * was set to 0. If complex const operations are pending, wake_const_ops must - * be called with semnum = -1, as well as with the number of each modified - * semaphore. - * The tasks that must be woken up are added to @pt. The return code - * is stored in q->pid. - * The function returns 1 if at least one operation was completed successfully. - */ -static int wake_const_ops(struct sem_array *sma, int semnum, - struct list_head *pt) -{ - struct sem_queue *q; - struct list_head *walk; - struct list_head *pending_list; - int semop_completed = 0; - - if (semnum == -1) - pending_list = &sma->pending_const; - else - pending_list = &sma->sem_base[semnum].pending_const; - - walk = pending_list->next; - while (walk != pending_list) { - int error; - - q = container_of(walk, struct sem_queue, list); - walk = walk->next; - - error = perform_atomic_semop(sma, q->sops, q->nsops, - q->undo, q->pid); - - if (error <= 0) { - /* operation completed, remove from queue & wakeup */ - - unlink_queue(sma, q); - - wake_up_sem_queue_prepare(pt, q, error); - if (error == 0) - semop_completed = 1; - } - } - return semop_completed; -} - -/** - * do_smart_wakeup_zero(sma, sops, nsops, pt) - wakeup all wait for zero tasks - * @sma: semaphore array - * @sops: operations that were performed - * @nsops: number of operations - * @pt: list head of the tasks that must be woken up. - * - * do_smart_wakeup_zero() checks all required queue for wait-for-zero - * operations, based on the actual changes that were performed on the - * semaphore array. - * The function returns 1 if at least one operation was completed successfully. - */ -static int do_smart_wakeup_zero(struct sem_array *sma, struct sembuf *sops, - int nsops, struct list_head *pt) -{ - int i; - int semop_completed = 0; - int got_zero = 0; - - /* first: the per-semaphore queues, if known */ - if (sops) { - for (i = 0; i < nsops; i++) { - int num = sops[i].sem_num; + /* No-one waits on this queue */ + if (list_empty(&curr->sem_pending)) + return 0; - if (sma->sem_base[num].semval == 0) { - got_zero = 1; - semop_completed |= wake_const_ops(sma, num, pt); - } - } - } else { - /* - * No sops means modified semaphores not known. - * Assume all were changed. + /* the new semaphore value */ + if (curr->semval) { + /* It is impossible that someone waits for the new value: + * - q is a previously sleeping simple operation that + * altered the array. It must be a decrement, because + * simple increments never sleep. + * - The value is not 0, thus wait-for-zero won't proceed. + * - If there are older (higher priority) decrements + * in the queue, then they have observed the original + * semval value and couldn't proceed. The operation + * decremented to value - thus they won't proceed either. */ - for (i = 0; i < sma->sem_nsems; i++) { - if (sma->sem_base[i].semval == 0) { - got_zero = 1; - semop_completed |= wake_const_ops(sma, i, pt); - } - } + BUG_ON(q->sops[0].sem_op >= 0); + return 0; } /* - * If one of the modified semaphores got 0, - * then check the global queue, too. + * semval is 0. Check if there are wait-for-zero semops. + * They must be the first entries in the per-semaphore queue */ - if (got_zero) - semop_completed |= wake_const_ops(sma, -1, pt); + h = list_first_entry(&curr->sem_pending, struct sem_queue, list); + BUG_ON(h->nsops != 1); + BUG_ON(h->sops[0].sem_num != q->sops[0].sem_num); - return semop_completed; + /* Yes, there is a wait-for-zero semop. Restart */ + if (h->sops[0].sem_op == 0) + return 1; + + /* Again - no-one is waiting for the new value. */ + return 0; } @@ -859,8 +678,6 @@ static int do_smart_wakeup_zero(struct sem_array *sma, struct sembuf *sops, * semaphore. * The tasks that must be woken up are added to @pt. The return code * is stored in q->pid. - * The function internally checks if const operations can now succeed. - * * The function return 1 if at least one semop was completed successfully. */ static int update_queue(struct sem_array *sma, int semnum, struct list_head *pt) @@ -871,9 +688,9 @@ static int update_queue(struct sem_array *sma, int semnum, struct list_head *pt) int semop_completed = 0; if (semnum == -1) - pending_list = &sma->pending_alter; + pending_list = &sma->sem_pending; else - pending_list = &sma->sem_base[semnum].pending_alter; + pending_list = &sma->sem_base[semnum].sem_pending; again: walk = pending_list->next; @@ -885,15 +702,16 @@ static int update_queue(struct sem_array *sma, int semnum, struct list_head *pt) /* If we are scanning the single sop, per-semaphore list of * one semaphore and that semaphore is 0, then it is not - * necessary to scan further: simple increments + * necessary to scan the "alter" entries: simple increments * that affect only one entry succeed immediately and cannot * be in the per semaphore pending queue, and decrements * cannot be successful if the value is already 0. */ - if (semnum != -1 && sma->sem_base[semnum].semval == 0) + if (semnum != -1 && sma->sem_base[semnum].semval == 0 && + q->alter) break; - error = perform_atomic_semop(sma, q->sops, q->nsops, + error = try_atomic_semop(sma, q->sops, q->nsops, q->undo, q->pid); /* Does q->sleeper still need to sleep? */ @@ -906,7 +724,6 @@ static int update_queue(struct sem_array *sma, int semnum, struct list_head *pt) restart = 0; } else { semop_completed = 1; - do_smart_wakeup_zero(sma, q->sops, q->nsops, pt); restart = check_restart(sma, q); } @@ -917,24 +734,6 @@ static int update_queue(struct sem_array *sma, int semnum, struct list_head *pt) return semop_completed; } -/** - * set_semotime(sma, sops) - set sem_otime - * @sma: semaphore array - * @sops: operations that modified the array, may be NULL - * - * sem_otime is replicated to avoid cache line trashing. - * This function sets one instance to the current time. - */ -static void set_semotime(struct sem_array *sma, struct sembuf *sops) -{ - if (sops == NULL) { - sma->sem_base[0].sem_otime = get_seconds(); - } else { - sma->sem_base[sops[0].sem_num].sem_otime = - get_seconds(); - } -} - /** * do_smart_update(sma, sops, nsops, otime, pt) - optimized update_queue * @sma: semaphore array @@ -943,8 +742,8 @@ static void set_semotime(struct sem_array *sma, struct sembuf *sops) * @otime: force setting otime * @pt: list head of the tasks that must be woken up. * - * do_smart_update() does the required calls to update_queue and wakeup_zero, - * based on the actual changes that were performed on the semaphore array. + * do_smart_update() does the required called to update_queue, based on the + * actual changes that were performed on the semaphore array. * Note that the function does not do the actual wake-up: the caller is * responsible for calling wake_up_sem_queue_do(@pt). * It is safe to perform this call after dropping all locks. @@ -953,42 +752,52 @@ static void do_smart_update(struct sem_array *sma, struct sembuf *sops, int nsop int otime, struct list_head *pt) { int i; + int progress; + + progress = 1; +retry_global: + if (sma->complex_count) { + if (update_queue(sma, -1, pt)) { + progress = 1; + otime = 1; + sops = NULL; + } + } + if (!progress) + goto done; - otime |= do_smart_wakeup_zero(sma, sops, nsops, pt); - - if (!list_empty(&sma->pending_alter)) { - /* semaphore array uses the global queue - just process it. */ - otime |= update_queue(sma, -1, pt); - } else { - if (!sops) { - /* - * No sops, thus the modified semaphores are not - * known. Check all. - */ - for (i = 0; i < sma->sem_nsems; i++) - otime |= update_queue(sma, i, pt); - } else { - /* - * Check the semaphores that were increased: - * - No complex ops, thus all sleeping ops are - * decrease. - * - if we decreased the value, then any sleeping - * semaphore ops wont be able to run: If the - * previous value was too small, then the new - * value will be too small, too. - */ - for (i = 0; i < nsops; i++) { - if (sops[i].sem_op > 0) { - otime |= update_queue(sma, - sops[i].sem_num, pt); - } + if (!sops) { + /* No semops; something special is going on. */ + for (i = 0; i < sma->sem_nsems; i++) { + if (update_queue(sma, i, pt)) { + otime = 1; + progress = 1; } } + goto done_checkretry; } + + /* Check the semaphores that were modified. */ + for (i = 0; i < nsops; i++) { + if (sops[i].sem_op > 0 || + (sops[i].sem_op < 0 && + sma->sem_base[sops[i].sem_num].semval == 0)) + if (update_queue(sma, sops[i].sem_num, pt)) { + otime = 1; + progress = 1; + } + } +done_checkretry: + if (progress) { + progress = 0; + goto retry_global; + } +done: if (otime) - set_semotime(sma, sops); + sma->sem_otime = get_seconds(); } + /* The following counts are associated to each semaphore: * semncnt number of tasks waiting on semval being nonzero * semzcnt number of tasks waiting on semval being zero @@ -1004,14 +813,14 @@ static int count_semncnt (struct sem_array * sma, ushort semnum) struct sem_queue * q; semncnt = 0; - list_for_each_entry(q, &sma->sem_base[semnum].pending_alter, list) { + list_for_each_entry(q, &sma->sem_base[semnum].sem_pending, list) { struct sembuf * sops = q->sops; BUG_ON(sops->sem_num != semnum); if ((sops->sem_op < 0) && !(sops->sem_flg & IPC_NOWAIT)) semncnt++; } - list_for_each_entry(q, &sma->pending_alter, list) { + list_for_each_entry(q, &sma->sem_pending, list) { struct sembuf * sops = q->sops; int nsops = q->nsops; int i; @@ -1030,14 +839,14 @@ static int count_semzcnt (struct sem_array * sma, ushort semnum) struct sem_queue * q; semzcnt = 0; - list_for_each_entry(q, &sma->sem_base[semnum].pending_const, list) { + list_for_each_entry(q, &sma->sem_base[semnum].sem_pending, list) { struct sembuf * sops = q->sops; BUG_ON(sops->sem_num != semnum); if ((sops->sem_op == 0) && !(sops->sem_flg & IPC_NOWAIT)) semzcnt++; } - list_for_each_entry(q, &sma->pending_const, list) { + list_for_each_entry(q, &sma->sem_pending, list) { struct sembuf * sops = q->sops; int nsops = q->nsops; int i; @@ -1050,8 +859,8 @@ static int count_semzcnt (struct sem_array * sma, ushort semnum) return semzcnt; } -/* Free a semaphore set. freeary() is called with sem_ids.rwsem locked - * as a writer and the spinlock for this semaphore set hold. sem_ids.rwsem +/* Free a semaphore set. freeary() is called with sem_ids.rw_mutex locked + * as a writer and the spinlock for this semaphore set hold. sem_ids.rw_mutex * remains locked on exit. */ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) @@ -1063,7 +872,7 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) int i; /* Free the existing undo structures for this semaphore set. */ - ipc_assert_locked_object(&sma->sem_perm); + assert_spin_locked(&sma->sem_perm.lock); list_for_each_entry_safe(un, tu, &sma->list_id, list_id) { list_del(&un->list_id); spin_lock(&un->ulp->lock); @@ -1075,22 +884,13 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) /* Wake up all pending processes and let them fail with EIDRM. */ INIT_LIST_HEAD(&tasks); - list_for_each_entry_safe(q, tq, &sma->pending_const, list) { - unlink_queue(sma, q); - wake_up_sem_queue_prepare(&tasks, q, -EIDRM); - } - - list_for_each_entry_safe(q, tq, &sma->pending_alter, list) { + list_for_each_entry_safe(q, tq, &sma->sem_pending, list) { unlink_queue(sma, q); wake_up_sem_queue_prepare(&tasks, q, -EIDRM); } for (i = 0; i < sma->sem_nsems; i++) { struct sem *sem = sma->sem_base + i; - list_for_each_entry_safe(q, tq, &sem->pending_const, list) { - unlink_queue(sma, q); - wake_up_sem_queue_prepare(&tasks, q, -EIDRM); - } - list_for_each_entry_safe(q, tq, &sem->pending_alter, list) { + list_for_each_entry_safe(q, tq, &sem->sem_pending, list) { unlink_queue(sma, q); wake_up_sem_queue_prepare(&tasks, q, -EIDRM); } @@ -1103,7 +903,8 @@ static void freeary(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) wake_up_sem_queue_do(&tasks); ns->used_sems -= sma->sem_nsems; - ipc_rcu_putref(sma, sem_rcu_free); + security_sem_free(sma); + ipc_rcu_putref(sma); } static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in, int version) @@ -1130,21 +931,6 @@ static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in, } } -static time_t get_semotime(struct sem_array *sma) -{ - int i; - time_t res; - - res = sma->sem_base[0].sem_otime; - for (i = 1; i < sma->sem_nsems; i++) { - time_t to = sma->sem_base[i].sem_otime; - - if (to > res) - res = to; - } - return res; -} - static int semctl_nolock(struct ipc_namespace *ns, int semid, int cmd, int version, void __user *p) { @@ -1171,7 +957,7 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid, seminfo.semmnu = SEMMNU; seminfo.semmap = SEMMAP; seminfo.semume = SEMUME; - down_read(&sem_ids(ns).rwsem); + down_read(&sem_ids(ns).rw_mutex); if (cmd == SEM_INFO) { seminfo.semusz = sem_ids(ns).in_use; seminfo.semaem = ns->used_sems; @@ -1180,7 +966,7 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid, seminfo.semaem = SEMAEM; } max_id = ipc_get_maxid(&sem_ids(ns)); - up_read(&sem_ids(ns).rwsem); + up_read(&sem_ids(ns).rw_mutex); if (copy_to_user(p, &seminfo, sizeof(struct seminfo))) return -EFAULT; return (max_id < 0) ? 0: max_id; @@ -1218,9 +1004,9 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid, goto out_unlock; kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm); - tbuf.sem_otime = get_semotime(sma); - tbuf.sem_ctime = sma->sem_ctime; - tbuf.sem_nsems = sma->sem_nsems; + tbuf.sem_otime = sma->sem_otime; + tbuf.sem_ctime = sma->sem_ctime; + tbuf.sem_nsems = sma->sem_nsems; rcu_read_unlock(); if (copy_semid_to_user(p, &tbuf, version)) return -EFAULT; @@ -1284,7 +1070,7 @@ static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum, curr = &sma->sem_base[semnum]; - ipc_assert_locked_object(&sma->sem_perm); + assert_spin_locked(&sma->sem_perm.lock); list_for_each_entry(un, &sma->list_id, list_id) un->semadj[semnum] = 0; @@ -1347,7 +1133,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, rcu_read_unlock(); sem_io = ipc_alloc(sizeof(ushort)*nsems); if(sem_io == NULL) { - ipc_rcu_putref(sma, ipc_rcu_free); + sem_putref(sma); return -ENOMEM; } @@ -1383,20 +1169,20 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, if(nsems > SEMMSL_FAST) { sem_io = ipc_alloc(sizeof(ushort)*nsems); if(sem_io == NULL) { - ipc_rcu_putref(sma, ipc_rcu_free); + sem_putref(sma); return -ENOMEM; } } if (copy_from_user (sem_io, p, nsems*sizeof(ushort))) { - ipc_rcu_putref(sma, ipc_rcu_free); + sem_putref(sma); err = -EFAULT; goto out_free; } for (i = 0; i < nsems; i++) { if (sem_io[i] > SEMVMX) { - ipc_rcu_putref(sma, ipc_rcu_free); + sem_putref(sma); err = -ERANGE; goto out_free; } @@ -1413,7 +1199,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, for (i = 0; i < nsems; i++) sma->sem_base[i].semval = sem_io[i]; - ipc_assert_locked_object(&sma->sem_perm); + assert_spin_locked(&sma->sem_perm.lock); list_for_each_entry(un, &sma->list_id, list_id) { for (i = 0; i < nsems; i++) un->semadj[i] = 0; @@ -1486,9 +1272,9 @@ copy_semid_from_user(struct semid64_ds *out, void __user *buf, int version) } /* - * This function handles some semctl commands which require the rwsem + * This function handles some semctl commands which require the rw_mutex * to be held in write mode. - * NOTE: no locks must be held, the rwsem is taken inside this function. + * NOTE: no locks must be held, the rw_mutex is taken inside this function. */ static int semctl_down(struct ipc_namespace *ns, int semid, int cmd, int version, void __user *p) @@ -1503,46 +1289,42 @@ static int semctl_down(struct ipc_namespace *ns, int semid, return -EFAULT; } - down_write(&sem_ids(ns).rwsem); - rcu_read_lock(); - ipcp = ipcctl_pre_down_nolock(ns, &sem_ids(ns), semid, cmd, &semid64.sem_perm, 0); - if (IS_ERR(ipcp)) { - err = PTR_ERR(ipcp); - goto out_unlock1; - } + if (IS_ERR(ipcp)) + return PTR_ERR(ipcp); sma = container_of(ipcp, struct sem_array, sem_perm); err = security_sem_semctl(sma, cmd); - if (err) - goto out_unlock1; + if (err) { + rcu_read_unlock(); + goto out_up; + } - switch (cmd) { + switch(cmd){ case IPC_RMID: sem_lock(sma, NULL, -1); - /* freeary unlocks the ipc object and rcu */ freeary(ns, ipcp); goto out_up; case IPC_SET: sem_lock(sma, NULL, -1); err = ipc_update_perm(&semid64.sem_perm, ipcp); if (err) - goto out_unlock0; + goto out_unlock; sma->sem_ctime = get_seconds(); break; default: + rcu_read_unlock(); err = -EINVAL; - goto out_unlock1; + goto out_up; } -out_unlock0: +out_unlock: sem_unlock(sma, -1); -out_unlock1: rcu_read_unlock(); out_up: - up_write(&sem_ids(ns).rwsem); + up_write(&sem_ids(ns).rw_mutex); return err; } @@ -1684,7 +1466,7 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid) /* step 2: allocate new undo structure */ new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL); if (!new) { - ipc_rcu_putref(sma, ipc_rcu_free); + sem_putref(sma); return ERR_PTR(-ENOMEM); } @@ -1714,7 +1496,7 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid) new->semid = semid; assert_spin_locked(&ulp->lock); list_add_rcu(&new->list_proc, &ulp->list_proc); - ipc_assert_locked_object(&sma->sem_perm); + assert_spin_locked(&sma->sem_perm.lock); list_add(&new->list_id, &sma->list_id); un = new; @@ -1751,6 +1533,7 @@ static int get_queue_result(struct sem_queue *q) return error; } + SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, unsigned, nsops, const struct timespec __user *, timeout) { @@ -1848,19 +1631,13 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, if (un && un->semid == -1) goto out_unlock_free; - error = perform_atomic_semop(sma, sops, nsops, un, - task_tgid_vnr(current)); - if (error == 0) { - /* If the operation was successful, then do - * the required updates. - */ - if (alter) + error = try_atomic_semop (sma, sops, nsops, un, task_tgid_vnr(current)); + if (error <= 0) { + if (alter && error == 0) do_smart_update(sma, sops, nsops, 1, &tasks); - else - set_semotime(sma, sops); - } - if (error <= 0) + goto out_unlock_free; + } /* We need to sleep on this operation, so we put the current * task into the pending queue and go to sleep. @@ -1876,27 +1653,15 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, struct sem *curr; curr = &sma->sem_base[sops->sem_num]; - if (alter) { - if (sma->complex_count) { - list_add_tail(&queue.list, - &sma->pending_alter); - } else { - - list_add_tail(&queue.list, - &curr->pending_alter); - } - } else { - list_add_tail(&queue.list, &curr->pending_const); - } + if (alter) + list_add_tail(&queue.list, &curr->sem_pending); + else + list_add(&queue.list, &curr->sem_pending); } else { - if (!sma->complex_count) - merge_queues(sma); - if (alter) - list_add_tail(&queue.list, &sma->pending_alter); + list_add_tail(&queue.list, &sma->sem_pending); else - list_add_tail(&queue.list, &sma->pending_const); - + list_add(&queue.list, &sma->sem_pending); sma->complex_count++; } @@ -2068,7 +1833,7 @@ void exit_sem(struct task_struct *tsk) } /* remove un from the linked lists */ - ipc_assert_locked_object(&sma->sem_perm); + assert_spin_locked(&sma->sem_perm.lock); list_del(&un->list_id); spin_lock(&ulp->lock); @@ -2117,17 +1882,6 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it) { struct user_namespace *user_ns = seq_user_ns(s); struct sem_array *sma = it; - time_t sem_otime; - - /* - * The proc interface isn't aware of sem_lock(), it calls - * ipc_lock_object() directly (in sysvipc_find_ipc). - * In order to stay compatible with sem_lock(), we must wait until - * all simple semop() calls have left their critical regions. - */ - sem_wait_array(sma); - - sem_otime = get_semotime(sma); return seq_printf(s, "%10d %10d %4o %10u %5u %5u %5u %5u %10lu %10lu\n", @@ -2139,7 +1893,7 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it) from_kgid_munged(user_ns, sma->sem_perm.gid), from_kuid_munged(user_ns, sma->sem_perm.cuid), from_kgid_munged(user_ns, sma->sem_perm.cgid), - sem_otime, + sma->sem_otime, sma->sem_ctime); } #endif diff --git a/ipc/shm.c b/ipc/shm.c index 7b87bea5245b..7e199fa1960f 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -19,9 +19,6 @@ * namespaces support * OpenVZ, SWsoft Inc. * Pavel Emelianov - * - * Better ipc lock (kern_ipc_perm.lock) handling - * Davidlohr Bueso , June 2013. */ #include @@ -83,8 +80,8 @@ void shm_init_ns(struct ipc_namespace *ns) } /* - * Called with shm_ids.rwsem (writer) and the shp structure locked. - * Only shm_ids.rwsem remains locked on exit. + * Called with shm_ids.rw_mutex (writer) and the shp structure locked. + * Only shm_ids.rw_mutex remains locked on exit. */ static void do_shm_rmid(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) { @@ -127,28 +124,8 @@ void __init shm_init (void) IPC_SHM_IDS, sysvipc_shm_proc_show); } -static inline struct shmid_kernel *shm_obtain_object(struct ipc_namespace *ns, int id) -{ - struct kern_ipc_perm *ipcp = ipc_obtain_object(&shm_ids(ns), id); - - if (IS_ERR(ipcp)) - return ERR_CAST(ipcp); - - return container_of(ipcp, struct shmid_kernel, shm_perm); -} - -static inline struct shmid_kernel *shm_obtain_object_check(struct ipc_namespace *ns, int id) -{ - struct kern_ipc_perm *ipcp = ipc_obtain_object_check(&shm_ids(ns), id); - - if (IS_ERR(ipcp)) - return ERR_CAST(ipcp); - - return container_of(ipcp, struct shmid_kernel, shm_perm); -} - /* - * shm_lock_(check_) routines are called in the paths where the rwsem + * shm_lock_(check_) routines are called in the paths where the rw_mutex * is not necessarily held. */ static inline struct shmid_kernel *shm_lock(struct ipc_namespace *ns, int id) @@ -164,16 +141,18 @@ static inline struct shmid_kernel *shm_lock(struct ipc_namespace *ns, int id) static inline void shm_lock_by_ptr(struct shmid_kernel *ipcp) { rcu_read_lock(); - ipc_lock_object(&ipcp->shm_perm); + spin_lock(&ipcp->shm_perm.lock); } -static void shm_rcu_free(struct rcu_head *head) +static inline struct shmid_kernel *shm_lock_check(struct ipc_namespace *ns, + int id) { - struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu); - struct shmid_kernel *shp = ipc_rcu_to_struct(p); + struct kern_ipc_perm *ipcp = ipc_lock_check(&shm_ids(ns), id); - security_shm_free(shp); - ipc_rcu_free(head); + if (IS_ERR(ipcp)) + return (struct shmid_kernel *)ipcp; + + return container_of(ipcp, struct shmid_kernel, shm_perm); } static inline void shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *s) @@ -203,7 +182,7 @@ static void shm_open(struct vm_area_struct *vma) * @ns: namespace * @shp: struct to free * - * It has to be called with shp and shm_ids.rwsem (writer) locked, + * It has to be called with shp and shm_ids.rw_mutex (writer) locked, * but returns with shp unlocked and freed. */ static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp) @@ -217,7 +196,8 @@ static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp) user_shm_unlock(file_inode(shp->shm_file)->i_size, shp->mlock_user); fput (shp->shm_file); - ipc_rcu_putref(shp, shm_rcu_free); + security_shm_free(shp); + ipc_rcu_putref(shp); } /* @@ -250,7 +230,7 @@ static void shm_close(struct vm_area_struct *vma) struct shmid_kernel *shp; struct ipc_namespace *ns = sfd->ns; - down_write(&shm_ids(ns).rwsem); + down_write(&shm_ids(ns).rw_mutex); /* remove from the list of attaches of the shm segment */ shp = shm_lock(ns, sfd->id); BUG_ON(IS_ERR(shp)); @@ -261,10 +241,10 @@ static void shm_close(struct vm_area_struct *vma) shm_destroy(ns, shp); else shm_unlock(shp); - up_write(&shm_ids(ns).rwsem); + up_write(&shm_ids(ns).rw_mutex); } -/* Called with ns->shm_ids(ns).rwsem locked */ +/* Called with ns->shm_ids(ns).rw_mutex locked */ static int shm_try_destroy_current(int id, void *p, void *data) { struct ipc_namespace *ns = data; @@ -295,7 +275,7 @@ static int shm_try_destroy_current(int id, void *p, void *data) return 0; } -/* Called with ns->shm_ids(ns).rwsem locked */ +/* Called with ns->shm_ids(ns).rw_mutex locked */ static int shm_try_destroy_orphaned(int id, void *p, void *data) { struct ipc_namespace *ns = data; @@ -306,7 +286,7 @@ static int shm_try_destroy_orphaned(int id, void *p, void *data) * We want to destroy segments without users and with already * exit'ed originating process. * - * As shp->* are changed under rwsem, it's safe to skip shp locking. + * As shp->* are changed under rw_mutex, it's safe to skip shp locking. */ if (shp->shm_creator != NULL) return 0; @@ -320,10 +300,10 @@ static int shm_try_destroy_orphaned(int id, void *p, void *data) void shm_destroy_orphaned(struct ipc_namespace *ns) { - down_write(&shm_ids(ns).rwsem); + down_write(&shm_ids(ns).rw_mutex); if (shm_ids(ns).in_use) idr_for_each(&shm_ids(ns).ipcs_idr, &shm_try_destroy_orphaned, ns); - up_write(&shm_ids(ns).rwsem); + up_write(&shm_ids(ns).rw_mutex); } @@ -335,10 +315,10 @@ void exit_shm(struct task_struct *task) return; /* Destroy all already created segments, but not mapped yet */ - down_write(&shm_ids(ns).rwsem); + down_write(&shm_ids(ns).rw_mutex); if (shm_ids(ns).in_use) idr_for_each(&shm_ids(ns).ipcs_idr, &shm_try_destroy_current, ns); - up_write(&shm_ids(ns).rwsem); + up_write(&shm_ids(ns).rw_mutex); } static int shm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) @@ -472,7 +452,7 @@ static const struct vm_operations_struct shm_vm_ops = { * @ns: namespace * @params: ptr to the structure that contains key, size and shmflg * - * Called with shm_ids.rwsem held as a writer. + * Called with shm_ids.rw_mutex held as a writer. */ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) @@ -505,7 +485,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) shp->shm_perm.security = NULL; error = security_shm_alloc(shp); if (error) { - ipc_rcu_putref(shp, ipc_rcu_free); + ipc_rcu_putref(shp); return error; } @@ -555,7 +535,6 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) shp->shm_nattch = 0; shp->shm_file = file; shp->shm_creator = current; - /* * shmid gets reported as "inode#" in /proc/pid/maps. * proc-ps tools use this. Changing this will break them. @@ -564,9 +543,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) ns->shm_tot += numpages; error = shp->shm_perm.id; - - ipc_unlock_object(&shp->shm_perm); - rcu_read_unlock(); + shm_unlock(shp); return error; no_id: @@ -574,12 +551,13 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) user_shm_unlock(size, shp->mlock_user); fput(file); no_file: - ipc_rcu_putref(shp, shm_rcu_free); + security_shm_free(shp); + ipc_rcu_putref(shp); return error; } /* - * Called with shm_ids.rwsem and ipcp locked. + * Called with shm_ids.rw_mutex and ipcp locked. */ static inline int shm_security(struct kern_ipc_perm *ipcp, int shmflg) { @@ -590,7 +568,7 @@ static inline int shm_security(struct kern_ipc_perm *ipcp, int shmflg) } /* - * Called with shm_ids.rwsem and ipcp locked. + * Called with shm_ids.rw_mutex and ipcp locked. */ static inline int shm_more_checks(struct kern_ipc_perm *ipcp, struct ipc_params *params) @@ -703,7 +681,7 @@ static inline unsigned long copy_shminfo_to_user(void __user *buf, struct shminf /* * Calculate and add used RSS and swap pages of a shm. - * Called with shm_ids.rwsem held as a reader + * Called with shm_ids.rw_mutex held as a reader */ static void shm_add_rss_swap(struct shmid_kernel *shp, unsigned long *rss_add, unsigned long *swp_add) @@ -730,7 +708,7 @@ static void shm_add_rss_swap(struct shmid_kernel *shp, } /* - * Called with shm_ids.rwsem held as a reader + * Called with shm_ids.rw_mutex held as a reader */ static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss, unsigned long *swp) @@ -759,9 +737,9 @@ static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss, } /* - * This function handles some shmctl commands which require the rwsem + * This function handles some shmctl commands which require the rw_mutex * to be held in write mode. - * NOTE: no locks must be held, the rwsem is taken inside this function. + * NOTE: no locks must be held, the rw_mutex is taken inside this function. */ static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd, struct shmid_ds __user *buf, int version) @@ -776,67 +754,59 @@ static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd, return -EFAULT; } - down_write(&shm_ids(ns).rwsem); - rcu_read_lock(); - - ipcp = ipcctl_pre_down_nolock(ns, &shm_ids(ns), shmid, cmd, - &shmid64.shm_perm, 0); - if (IS_ERR(ipcp)) { - err = PTR_ERR(ipcp); - goto out_unlock1; - } + ipcp = ipcctl_pre_down(ns, &shm_ids(ns), shmid, cmd, + &shmid64.shm_perm, 0); + if (IS_ERR(ipcp)) + return PTR_ERR(ipcp); shp = container_of(ipcp, struct shmid_kernel, shm_perm); err = security_shm_shmctl(shp, cmd); if (err) - goto out_unlock1; - + goto out_unlock; switch (cmd) { case IPC_RMID: - ipc_lock_object(&shp->shm_perm); - /* do_shm_rmid unlocks the ipc object and rcu */ do_shm_rmid(ns, ipcp); goto out_up; case IPC_SET: - ipc_lock_object(&shp->shm_perm); err = ipc_update_perm(&shmid64.shm_perm, ipcp); if (err) - goto out_unlock0; + goto out_unlock; shp->shm_ctim = get_seconds(); break; default: err = -EINVAL; - goto out_unlock1; } - -out_unlock0: - ipc_unlock_object(&shp->shm_perm); -out_unlock1: - rcu_read_unlock(); +out_unlock: + shm_unlock(shp); out_up: - up_write(&shm_ids(ns).rwsem); + up_write(&shm_ids(ns).rw_mutex); return err; } -static int shmctl_nolock(struct ipc_namespace *ns, int shmid, - int cmd, int version, void __user *buf) +SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) { - int err; struct shmid_kernel *shp; + int err, version; + struct ipc_namespace *ns; - /* preliminary security checks for *_INFO */ - if (cmd == IPC_INFO || cmd == SHM_INFO) { - err = security_shm_shmctl(NULL, cmd); - if (err) - return err; + if (cmd < 0 || shmid < 0) { + err = -EINVAL; + goto out; } - switch (cmd) { + version = ipc_parse_version(&cmd); + ns = current->nsproxy->ipc_ns; + + switch (cmd) { /* replace with proc interface ? */ case IPC_INFO: { struct shminfo64 shminfo; + err = security_shm_shmctl(NULL, cmd); + if (err) + return err; + memset(&shminfo, 0, sizeof(shminfo)); shminfo.shmmni = shminfo.shmseg = ns->shm_ctlmni; shminfo.shmmax = ns->shm_ctlmax; @@ -846,9 +816,9 @@ static int shmctl_nolock(struct ipc_namespace *ns, int shmid, if(copy_shminfo_to_user (buf, &shminfo, version)) return -EFAULT; - down_read(&shm_ids(ns).rwsem); + down_read(&shm_ids(ns).rw_mutex); err = ipc_get_maxid(&shm_ids(ns)); - up_read(&shm_ids(ns).rwsem); + up_read(&shm_ids(ns).rw_mutex); if(err<0) err = 0; @@ -858,15 +828,19 @@ static int shmctl_nolock(struct ipc_namespace *ns, int shmid, { struct shm_info shm_info; + err = security_shm_shmctl(NULL, cmd); + if (err) + return err; + memset(&shm_info, 0, sizeof(shm_info)); - down_read(&shm_ids(ns).rwsem); + down_read(&shm_ids(ns).rw_mutex); shm_info.used_ids = shm_ids(ns).in_use; shm_get_stat (ns, &shm_info.shm_rss, &shm_info.shm_swp); shm_info.shm_tot = ns->shm_tot; shm_info.swap_attempts = 0; shm_info.swap_successes = 0; err = ipc_get_maxid(&shm_ids(ns)); - up_read(&shm_ids(ns).rwsem); + up_read(&shm_ids(ns).rw_mutex); if (copy_to_user(buf, &shm_info, sizeof(shm_info))) { err = -EFAULT; goto out; @@ -881,31 +855,27 @@ static int shmctl_nolock(struct ipc_namespace *ns, int shmid, struct shmid64_ds tbuf; int result; - rcu_read_lock(); if (cmd == SHM_STAT) { - shp = shm_obtain_object(ns, shmid); + shp = shm_lock(ns, shmid); if (IS_ERR(shp)) { err = PTR_ERR(shp); - goto out_unlock; + goto out; } result = shp->shm_perm.id; } else { - shp = shm_obtain_object_check(ns, shmid); + shp = shm_lock_check(ns, shmid); if (IS_ERR(shp)) { err = PTR_ERR(shp); - goto out_unlock; + goto out; } result = 0; } - err = -EACCES; if (ipcperms(ns, &shp->shm_perm, S_IRUGO)) goto out_unlock; - err = security_shm_shmctl(shp, cmd); if (err) goto out_unlock; - memset(&tbuf, 0, sizeof(tbuf)); kernel_to_ipc64_perm(&shp->shm_perm, &tbuf.shm_perm); tbuf.shm_segsz = shp->shm_segsz; @@ -915,76 +885,43 @@ static int shmctl_nolock(struct ipc_namespace *ns, int shmid, tbuf.shm_cpid = shp->shm_cprid; tbuf.shm_lpid = shp->shm_lprid; tbuf.shm_nattch = shp->shm_nattch; - rcu_read_unlock(); - - if (copy_shmid_to_user(buf, &tbuf, version)) + shm_unlock(shp); + if(copy_shmid_to_user (buf, &tbuf, version)) err = -EFAULT; else err = result; goto out; } - default: - return -EINVAL; - } - -out_unlock: - rcu_read_unlock(); -out: - return err; -} - -SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) -{ - struct shmid_kernel *shp; - int err, version; - struct ipc_namespace *ns; - - if (cmd < 0 || shmid < 0) - return -EINVAL; - - version = ipc_parse_version(&cmd); - ns = current->nsproxy->ipc_ns; - - switch (cmd) { - case IPC_INFO: - case SHM_INFO: - case SHM_STAT: - case IPC_STAT: - return shmctl_nolock(ns, shmid, cmd, version, buf); - case IPC_RMID: - case IPC_SET: - return shmctl_down(ns, shmid, cmd, buf, version); case SHM_LOCK: case SHM_UNLOCK: { struct file *shm_file; - rcu_read_lock(); - shp = shm_obtain_object_check(ns, shmid); + shp = shm_lock_check(ns, shmid); if (IS_ERR(shp)) { err = PTR_ERR(shp); - goto out_unlock1; + goto out; } audit_ipc_obj(&(shp->shm_perm)); - err = security_shm_shmctl(shp, cmd); - if (err) - goto out_unlock1; - ipc_lock_object(&shp->shm_perm); if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) { kuid_t euid = current_euid(); err = -EPERM; if (!uid_eq(euid, shp->shm_perm.uid) && !uid_eq(euid, shp->shm_perm.cuid)) - goto out_unlock0; + goto out_unlock; if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK)) - goto out_unlock0; + goto out_unlock; } + err = security_shm_shmctl(shp, cmd); + if (err) + goto out_unlock; + shm_file = shp->shm_file; if (is_file_hugepages(shm_file)) - goto out_unlock0; + goto out_unlock; if (cmd == SHM_LOCK) { struct user_struct *user = current_user(); @@ -993,31 +930,32 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) shp->shm_perm.mode |= SHM_LOCKED; shp->mlock_user = user; } - goto out_unlock0; + goto out_unlock; } /* SHM_UNLOCK */ if (!(shp->shm_perm.mode & SHM_LOCKED)) - goto out_unlock0; + goto out_unlock; shmem_lock(shm_file, 0, shp->mlock_user); shp->shm_perm.mode &= ~SHM_LOCKED; shp->mlock_user = NULL; get_file(shm_file); - ipc_unlock_object(&shp->shm_perm); - rcu_read_unlock(); + shm_unlock(shp); shmem_unlock_mapping(shm_file->f_mapping); - fput(shm_file); - return err; + goto out; } + case IPC_RMID: + case IPC_SET: + err = shmctl_down(ns, shmid, cmd, buf, version); + return err; default: return -EINVAL; } -out_unlock0: - ipc_unlock_object(&shp->shm_perm); -out_unlock1: - rcu_read_unlock(); +out_unlock: + shm_unlock(shp); +out: return err; } @@ -1085,11 +1023,10 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr, * additional creator id... */ ns = current->nsproxy->ipc_ns; - rcu_read_lock(); - shp = shm_obtain_object_check(ns, shmid); + shp = shm_lock_check(ns, shmid); if (IS_ERR(shp)) { err = PTR_ERR(shp); - goto out_unlock; + goto out; } err = -EACCES; @@ -1100,31 +1037,24 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr, if (err) goto out_unlock; - ipc_lock_object(&shp->shm_perm); path = shp->shm_file->f_path; path_get(&path); shp->shm_nattch++; size = i_size_read(path.dentry->d_inode); - ipc_unlock_object(&shp->shm_perm); - rcu_read_unlock(); + shm_unlock(shp); err = -ENOMEM; sfd = kzalloc(sizeof(*sfd), GFP_KERNEL); - if (!sfd) { - path_put(&path); - goto out_nattch; - } + if (!sfd) + goto out_put_dentry; file = alloc_file(&path, f_mode, is_file_hugepages(shp->shm_file) ? &shm_file_operations_huge : &shm_file_operations); err = PTR_ERR(file); - if (IS_ERR(file)) { - kfree(sfd); - path_put(&path); - goto out_nattch; - } + if (IS_ERR(file)) + goto out_free; file->private_data = sfd; file->f_mapping = shp->shm_file->f_mapping; @@ -1150,7 +1080,7 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr, addr > current->mm->start_stack - size - PAGE_SIZE * 5) goto invalid; } - + addr = do_mmap_pgoff(file, addr, size, prot, flags, 0, &populate); *raddr = addr; err = 0; @@ -1165,7 +1095,7 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr, fput(file); out_nattch: - down_write(&shm_ids(ns).rwsem); + down_write(&shm_ids(ns).rw_mutex); shp = shm_lock(ns, shmid); BUG_ON(IS_ERR(shp)); shp->shm_nattch--; @@ -1173,13 +1103,20 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr, shm_destroy(ns, shp); else shm_unlock(shp); - up_write(&shm_ids(ns).rwsem); - return err; + up_write(&shm_ids(ns).rw_mutex); -out_unlock: - rcu_read_unlock(); out: return err; + +out_unlock: + shm_unlock(shp); + goto out; + +out_free: + kfree(sfd); +out_put_dentry: + path_put(&path); + goto out_nattch; } SYSCALL_DEFINE3(shmat, int, shmid, char __user *, shmaddr, int, shmflg) @@ -1284,7 +1221,8 @@ SYSCALL_DEFINE1(shmdt, char __user *, shmaddr) #else /* CONFIG_MMU */ /* under NOMMU conditions, the exact address to be destroyed must be * given */ - if (vma && vma->vm_start == addr && vma->vm_ops == &shm_vm_ops) { + retval = -EINVAL; + if (vma->vm_start == addr && vma->vm_ops == &shm_vm_ops) { do_munmap(mm, vma->vm_start, vma->vm_end - vma->vm_start); retval = 0; } diff --git a/ipc/util.c b/ipc/util.c index fdb8ae740775..809ec5ec8122 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -15,14 +15,6 @@ * Jun 2006 - namespaces ssupport * OpenVZ, SWsoft Inc. * Pavel Emelianov - * - * General sysv ipc locking scheme: - * when doing ipc id lookups, take the ids->rwsem - * rcu_read_lock() - * obtain the ipc object (kern_ipc_perm) - * perform security, capabilities, auditing and permission checks, etc. - * acquire the ipc lock (kern_ipc_perm.lock) throught ipc_lock_object() - * perform data updates (ie: SET, RMID, LOCK/UNLOCK commands) */ #include @@ -127,7 +119,7 @@ __initcall(ipc_init); void ipc_init_ids(struct ipc_ids *ids) { - init_rwsem(&ids->rwsem); + init_rwsem(&ids->rw_mutex); ids->in_use = 0; ids->seq = 0; @@ -182,7 +174,7 @@ void __init ipc_init_proc_interface(const char *path, const char *header, * @ids: Identifier set * @key: The key to find * - * Requires ipc_ids.rwsem locked. + * Requires ipc_ids.rw_mutex locked. * Returns the LOCKED pointer to the ipc structure if found or NULL * if not. * If key is found ipc points to the owning ipc structure @@ -205,8 +197,7 @@ static struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key) continue; } - rcu_read_lock(); - ipc_lock_object(ipc); + ipc_lock_by_ptr(ipc); return ipc; } @@ -217,7 +208,7 @@ static struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key) * ipc_get_maxid - get the last assigned id * @ids: IPC identifier set * - * Called with ipc_ids.rwsem held. + * Called with ipc_ids.rw_mutex held. */ int ipc_get_maxid(struct ipc_ids *ids) @@ -255,8 +246,9 @@ int ipc_get_maxid(struct ipc_ids *ids) * is returned. The 'new' entry is returned in a locked state on success. * On failure the entry is not locked and a negative err-code is returned. * - * Called with writer ipc_ids.rwsem held. + * Called with ipc_ids.rw_mutex held as a writer. */ + int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size) { kuid_t euid; @@ -321,9 +313,9 @@ static int ipcget_new(struct ipc_namespace *ns, struct ipc_ids *ids, { int err; - down_write(&ids->rwsem); + down_write(&ids->rw_mutex); err = ops->getnew(ns, params); - up_write(&ids->rwsem); + up_write(&ids->rw_mutex); return err; } @@ -340,7 +332,7 @@ static int ipcget_new(struct ipc_namespace *ns, struct ipc_ids *ids, * * On success, the IPC id is returned. * - * It is called with ipc_ids.rwsem and ipcp->lock held. + * It is called with ipc_ids.rw_mutex and ipcp->lock held. */ static int ipc_check_perms(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp, @@ -385,7 +377,7 @@ static int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids, * Take the lock as a writer since we are potentially going to add * a new entry + read locks are not "upgradable" */ - down_write(&ids->rwsem); + down_write(&ids->rw_mutex); ipcp = ipc_findkey(ids, params->key); if (ipcp == NULL) { /* key not used */ @@ -411,7 +403,7 @@ static int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids, } ipc_unlock(ipcp); } - up_write(&ids->rwsem); + up_write(&ids->rw_mutex); return err; } @@ -422,7 +414,7 @@ static int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids, * @ids: IPC identifier set * @ipcp: ipc perm structure containing the identifier to remove * - * ipc_ids.rwsem (as a writer) and the spinlock for this ID are held + * ipc_ids.rw_mutex (as a writer) and the spinlock for this ID are held * before this function is called, and remain locked on the exit. */ @@ -474,6 +466,13 @@ void ipc_free(void* ptr, int size) kfree(ptr); } +struct ipc_rcu { + struct rcu_head rcu; + atomic_t refcount; + /* "void *" makes sure alignment of following data is sane. */ + void *data[0]; +}; + /** * ipc_rcu_alloc - allocate ipc and rcu space * @size: size desired @@ -490,34 +489,35 @@ void *ipc_rcu_alloc(int size) if (unlikely(!out)) return NULL; atomic_set(&out->refcount, 1); - return out + 1; + return out->data; } int ipc_rcu_getref(void *ptr) { - struct ipc_rcu *p = ((struct ipc_rcu *)ptr) - 1; + return atomic_inc_not_zero(&container_of(ptr, struct ipc_rcu, data)->refcount); +} - return atomic_inc_not_zero(&p->refcount); +/** + * ipc_schedule_free - free ipc + rcu space + * @head: RCU callback structure for queued work + */ +static void ipc_schedule_free(struct rcu_head *head) +{ + vfree(container_of(head, struct ipc_rcu, rcu)); } -void ipc_rcu_putref(void *ptr, void (*func)(struct rcu_head *head)) +void ipc_rcu_putref(void *ptr) { - struct ipc_rcu *p = ((struct ipc_rcu *)ptr) - 1; + struct ipc_rcu *p = container_of(ptr, struct ipc_rcu, data); if (!atomic_dec_and_test(&p->refcount)) return; - call_rcu(&p->rcu, func); -} - -void ipc_rcu_free(struct rcu_head *head) -{ - struct ipc_rcu *p = container_of(head, struct ipc_rcu, rcu); - - if (is_vmalloc_addr(p)) - vfree(p); - else - kfree(p); + if (is_vmalloc_addr(ptr)) { + call_rcu(&p->rcu, ipc_schedule_free); + } else { + kfree_rcu(p, rcu); + } } /** @@ -622,7 +622,7 @@ struct kern_ipc_perm *ipc_obtain_object(struct ipc_ids *ids, int id) } /** - * ipc_lock - Lock an ipc structure without rwsem held + * ipc_lock - Lock an ipc structure without rw_mutex held * @ids: IPC identifier set * @id: ipc id to look for * @@ -678,6 +678,22 @@ struct kern_ipc_perm *ipc_obtain_object_check(struct ipc_ids *ids, int id) return out; } +struct kern_ipc_perm *ipc_lock_check(struct ipc_ids *ids, int id) +{ + struct kern_ipc_perm *out; + + out = ipc_lock(ids, id); + if (IS_ERR(out)) + return out; + + if (ipc_checkid(out, id)) { + ipc_unlock(out); + return ERR_PTR(-EIDRM); + } + + return out; +} + /** * ipcget - Common sys_*get() code * @ns : namsepace @@ -718,7 +734,7 @@ int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out) } /** - * ipcctl_pre_down_nolock - retrieve an ipc and check permissions for some IPC_XXX cmd + * ipcctl_pre_down - retrieve an ipc and check permissions for some IPC_XXX cmd * @ns: the ipc namespace * @ids: the table of ids where to look for the ipc * @id: the id of the ipc to retrieve @@ -731,22 +747,39 @@ int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out) * It must be called without any lock held and * - retrieves the ipc with the given id in the given table. * - performs some audit and permission check, depending on the given cmd - * - returns a pointer to the ipc object or otherwise, the corresponding error. - * - * Call holding the both the rwsem and the rcu read lock. + * - returns the ipc with both ipc and rw_mutex locks held in case of success + * or an err-code without any lock held otherwise. */ +struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, + struct ipc_ids *ids, int id, int cmd, + struct ipc64_perm *perm, int extra_perm) +{ + struct kern_ipc_perm *ipcp; + + ipcp = ipcctl_pre_down_nolock(ns, ids, id, cmd, perm, extra_perm); + if (IS_ERR(ipcp)) + goto out; + + spin_lock(&ipcp->lock); +out: + return ipcp; +} + struct kern_ipc_perm *ipcctl_pre_down_nolock(struct ipc_namespace *ns, - struct ipc_ids *ids, int id, int cmd, - struct ipc64_perm *perm, int extra_perm) + struct ipc_ids *ids, int id, int cmd, + struct ipc64_perm *perm, int extra_perm) { kuid_t euid; int err = -EPERM; struct kern_ipc_perm *ipcp; + down_write(&ids->rw_mutex); + rcu_read_lock(); + ipcp = ipc_obtain_object_check(ids, id); if (IS_ERR(ipcp)) { err = PTR_ERR(ipcp); - goto err; + goto out_up; } audit_ipc_obj(ipcp); @@ -757,8 +790,16 @@ struct kern_ipc_perm *ipcctl_pre_down_nolock(struct ipc_namespace *ns, euid = current_euid(); if (uid_eq(euid, ipcp->cuid) || uid_eq(euid, ipcp->uid) || ns_capable(ns->user_ns, CAP_SYS_ADMIN)) - return ipcp; /* successful lookup */ -err: + return ipcp; + +out_up: + /* + * Unsuccessful lookup, unlock and return + * the corresponding error. + */ + rcu_read_unlock(); + up_write(&ids->rw_mutex); + return ERR_PTR(err); } @@ -815,8 +856,7 @@ static struct kern_ipc_perm *sysvipc_find_ipc(struct ipc_ids *ids, loff_t pos, ipc = idr_find(&ids->ipcs_idr, pos); if (ipc != NULL) { *new_pos = pos + 1; - rcu_read_lock(); - ipc_lock_object(ipc); + ipc_lock_by_ptr(ipc); return ipc; } } @@ -854,7 +894,7 @@ static void *sysvipc_proc_start(struct seq_file *s, loff_t *pos) * Take the lock - this will be released by the corresponding * call to stop(). */ - down_read(&ids->rwsem); + down_read(&ids->rw_mutex); /* pos < 0 is invalid */ if (*pos < 0) @@ -881,7 +921,7 @@ static void sysvipc_proc_stop(struct seq_file *s, void *it) ids = &iter->ns->ids[iface->ids]; /* Release the lock we took in start() */ - up_read(&ids->rwsem); + up_read(&ids->rw_mutex); } static int sysvipc_proc_show(struct seq_file *s, void *it) diff --git a/ipc/util.h b/ipc/util.h index f2f5036f2eed..2b0bdd5d92ce 100644 --- a/ipc/util.h +++ b/ipc/util.h @@ -47,13 +47,6 @@ static inline void msg_exit_ns(struct ipc_namespace *ns) { } static inline void shm_exit_ns(struct ipc_namespace *ns) { } #endif -struct ipc_rcu { - struct rcu_head rcu; - atomic_t refcount; -} ____cacheline_aligned_in_smp; - -#define ipc_rcu_to_struct(p) ((void *)(p+1)) - /* * Structure that holds the parameters needed by the ipc operations * (see after) @@ -101,10 +94,10 @@ void __init ipc_init_proc_interface(const char *path, const char *header, #define ipcid_to_idx(id) ((id) % SEQ_MULTIPLIER) #define ipcid_to_seqx(id) ((id) / SEQ_MULTIPLIER) -/* must be called with ids->rwsem acquired for writing */ +/* must be called with ids->rw_mutex acquired for writing */ int ipc_addid(struct ipc_ids *, struct kern_ipc_perm *, int); -/* must be called with ids->rwsem acquired for reading */ +/* must be called with ids->rw_mutex acquired for reading */ int ipc_get_maxid(struct ipc_ids *); /* must be called with both locks acquired. */ @@ -127,8 +120,7 @@ void ipc_free(void* ptr, int size); */ void* ipc_rcu_alloc(int size); int ipc_rcu_getref(void *ptr); -void ipc_rcu_putref(void *ptr, void (*func)(struct rcu_head *head)); -void ipc_rcu_free(struct rcu_head *head); +void ipc_rcu_putref(void *ptr); struct kern_ipc_perm *ipc_lock(struct ipc_ids *, int); struct kern_ipc_perm *ipc_obtain_object(struct ipc_ids *ids, int id); @@ -139,6 +131,9 @@ int ipc_update_perm(struct ipc64_perm *in, struct kern_ipc_perm *out); struct kern_ipc_perm *ipcctl_pre_down_nolock(struct ipc_namespace *ns, struct ipc_ids *ids, int id, int cmd, struct ipc64_perm *perm, int extra_perm); +struct kern_ipc_perm *ipcctl_pre_down(struct ipc_namespace *ns, + struct ipc_ids *ids, int id, int cmd, + struct ipc64_perm *perm, int extra_perm); #ifndef CONFIG_ARCH_WANT_IPC_PARSE_VERSION /* On IA-64, we always use the "64-bit version" of the IPC structures. */ @@ -164,27 +159,24 @@ static inline int ipc_checkid(struct kern_ipc_perm *ipcp, int uid) return uid / SEQ_MULTIPLIER != ipcp->seq; } -static inline void ipc_lock_object(struct kern_ipc_perm *perm) +static inline void ipc_lock_by_ptr(struct kern_ipc_perm *perm) { + rcu_read_lock(); spin_lock(&perm->lock); } -static inline void ipc_unlock_object(struct kern_ipc_perm *perm) +static inline void ipc_unlock(struct kern_ipc_perm *perm) { spin_unlock(&perm->lock); + rcu_read_unlock(); } -static inline void ipc_assert_locked_object(struct kern_ipc_perm *perm) -{ - assert_spin_locked(&perm->lock); -} - -static inline void ipc_unlock(struct kern_ipc_perm *perm) +static inline void ipc_lock_object(struct kern_ipc_perm *perm) { - ipc_unlock_object(perm); - rcu_read_unlock(); + spin_lock(&perm->lock); } +struct kern_ipc_perm *ipc_lock_check(struct ipc_ids *ids, int id); struct kern_ipc_perm *ipc_obtain_object_check(struct ipc_ids *ids, int id); int ipcget(struct ipc_namespace *ns, struct ipc_ids *ids, struct ipc_ops *ops, struct ipc_params *params); diff --git a/kernel/Makefile b/kernel/Makefile index 5d7a07fd8190..271fd3119af9 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -31,7 +31,6 @@ obj-$(CONFIG_FREEZER) += freezer.o obj-$(CONFIG_PROFILING) += profile.o obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-y += time/ -obj-$(CONFIG_UPTIME_LIMITED_KERNEL) += uptime_limit.o obj-$(CONFIG_DEBUG_MUTEXES) += mutex-debug.o obj-$(CONFIG_LOCKDEP) += lockdep.o ifeq ($(CONFIG_PROC_FS),y) diff --git a/kernel/audit.c b/kernel/audit.c index 7b0e23a740ce..91e53d04b6a9 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -1117,10 +1117,9 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, sleep_time = timeout_start + audit_backlog_wait_time - jiffies; - if ((long)sleep_time > 0) { + if ((long)sleep_time > 0) wait_for_auditd(sleep_time); - continue; - } + continue; } if (audit_rate_check() && printk_ratelimit()) printk(KERN_WARNING diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index ad8e1bdca70e..f3569747d629 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -1682,10 +1682,12 @@ static bool handle_trampoline(struct pt_regs *regs) tmp = ri; ri = ri->next; kfree(tmp); - utask->depth--; if (!chained) break; + + utask->depth--; + BUG_ON(!ri); } diff --git a/kernel/fork.c b/kernel/fork.c index a7ccd52d9f47..ffbc0904794e 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -418,8 +418,6 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) struct address_space *mapping = file->f_mapping; get_file(file); - if (tmp->vm_prfile) - get_file(tmp->vm_prfile); if (tmp->vm_flags & VM_DENYWRITE) atomic_dec(&inode->i_writecount); mutex_lock(&mapping->i_mmap_mutex); @@ -1173,11 +1171,10 @@ static struct task_struct *copy_process(unsigned long clone_flags, return ERR_PTR(-EINVAL); /* - * If the new process will be in a different pid namespace don't - * allow it to share a thread group or signal handlers with the - * forking task. + * If the new process will be in a different pid namespace + * don't allow the creation of threads. */ - if ((clone_flags & (CLONE_SIGHAND | CLONE_NEWPID)) && + if ((clone_flags & (CLONE_VM|CLONE_NEWPID)) && (task_active_pid_ns(current) != current->nsproxy->pid_ns)) return ERR_PTR(-EINVAL); diff --git a/kernel/pid.c b/kernel/pid.c index 0eb6d8e8b1da..0db3e791a06d 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -264,7 +264,6 @@ void free_pid(struct pid *pid) struct pid_namespace *ns = upid->ns; hlist_del_rcu(&upid->pid_chain); switch(--ns->nr_hashed) { - case 2: case 1: /* When all that is left in the pid namespace * is the reaper wake up the reaper. The reaper diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c index 1101d92635c3..b5ccba22603b 100644 --- a/kernel/sched/cputime.c +++ b/kernel/sched/cputime.c @@ -558,7 +558,7 @@ static void cputime_adjust(struct task_cputime *curr, struct cputime *prev, cputime_t *ut, cputime_t *st) { - cputime_t rtime, stime, utime; + cputime_t rtime, stime, utime, total; if (vtime_accounting_enabled()) { *ut = curr->utime; @@ -566,6 +566,9 @@ static void cputime_adjust(struct task_cputime *curr, return; } + stime = curr->stime; + total = stime + curr->utime; + /* * Tick based cputime accounting depend on random scheduling * timeslices of a task to be interrupted or not by the timer. @@ -586,19 +589,13 @@ static void cputime_adjust(struct task_cputime *curr, if (prev->stime + prev->utime >= rtime) goto out; - stime = curr->stime; - utime = curr->utime; - - if (utime == 0) { - stime = rtime; - } else if (stime == 0) { - utime = rtime; - } else { - cputime_t total = stime + utime; - + if (total) { stime = scale_stime((__force u64)stime, (__force u64)rtime, (__force u64)total); utime = rtime - stime; + } else { + stime = rtime; + utime = 0; } /* diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 009a62bb859d..03b73bea33d6 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -5778,15 +5778,11 @@ static void task_fork_fair(struct task_struct *p) cfs_rq = task_cfs_rq(current); curr = cfs_rq->curr; - /* - * Not only the cpu but also the task_group of the parent might have - * been changed after parent->se.parent,cfs_rq were copied to - * child->se.parent,cfs_rq. So call __set_task_cpu() to make those - * of child point to valid ones. - */ - rcu_read_lock(); - __set_task_cpu(p, this_cpu); - rcu_read_unlock(); + if (unlikely(task_cpu(p) != this_cpu)) { + rcu_read_lock(); + __set_task_cpu(p, this_cpu); + rcu_read_unlock(); + } update_curr(cfs_rq); diff --git a/kernel/softirq.c b/kernel/softirq.c index 787b3a032429..3d6833f125d3 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -330,19 +330,10 @@ void irq_enter(void) static inline void invoke_softirq(void) { - if (!force_irqthreads) { - /* - * We can safely execute softirq on the current stack if - * it is the irq stack, because it should be near empty - * at this stage. But we have no way to know if the arch - * calls irq_exit() on the irq stack. So call softirq - * in its own stack to prevent from any overrun on top - * of a potentially deep task stack. - */ - do_softirq(); - } else { + if (!force_irqthreads) + __do_softirq(); + else wakeup_softirqd(); - } } static inline void tick_irq_exit(void) diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index bb2215174f05..8f5b3b98577b 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c @@ -516,13 +516,13 @@ static void sync_cmos_clock(struct work_struct *work) schedule_delayed_work(&sync_cmos_work, timespec_to_jiffies(&next)); } -void ntp_notify_cmos_timer(void) +static void notify_cmos_timer(void) { schedule_delayed_work(&sync_cmos_work, 0); } #else -void ntp_notify_cmos_timer(void) { } +static inline void notify_cmos_timer(void) { } #endif @@ -687,6 +687,8 @@ int __do_adjtimex(struct timex *txc, struct timespec *ts, s32 *time_tai) if (!(time_status & STA_NANO)) txc->time.tv_usec /= NSEC_PER_USEC; + notify_cmos_timer(); + return result; } diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index fcc261c5df29..baeeb5c87cf1 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1682,8 +1682,6 @@ int do_adjtimex(struct timex *txc) write_seqcount_end(&timekeeper_seq); raw_spin_unlock_irqrestore(&timekeeper_lock, flags); - ntp_notify_cmos_timer(); - return ret; } diff --git a/kernel/time/timer_list.c b/kernel/time/timer_list.c index 61ed862cdd37..3bdf28323012 100644 --- a/kernel/time/timer_list.c +++ b/kernel/time/timer_list.c @@ -265,9 +265,10 @@ static inline void timer_list_header(struct seq_file *m, u64 now) static int timer_list_show(struct seq_file *m, void *v) { struct timer_list_iter *iter = v; + u64 now = ktime_to_ns(ktime_get()); if (iter->cpu == -1 && !iter->second_pass) - timer_list_header(m, iter->now); + timer_list_header(m, now); else if (!iter->second_pass) print_cpu(m, iter->cpu, iter->now); #ifdef CONFIG_GENERIC_CLOCKEVENTS @@ -297,41 +298,33 @@ void sysrq_timer_list_show(void) return; } -static void *move_iter(struct timer_list_iter *iter, loff_t offset) +static void *timer_list_start(struct seq_file *file, loff_t *offset) { - for (; offset; offset--) { - iter->cpu = cpumask_next(iter->cpu, cpu_online_mask); - if (iter->cpu >= nr_cpu_ids) { + struct timer_list_iter *iter = file->private; + + if (!*offset) { + iter->cpu = -1; + iter->now = ktime_to_ns(ktime_get()); + } else if (iter->cpu >= nr_cpu_ids) { #ifdef CONFIG_GENERIC_CLOCKEVENTS - if (!iter->second_pass) { - iter->cpu = -1; - iter->second_pass = true; - } else - return NULL; -#else + if (!iter->second_pass) { + iter->cpu = -1; + iter->second_pass = true; + } else return NULL; +#else + return NULL; #endif - } } return iter; } -static void *timer_list_start(struct seq_file *file, loff_t *offset) -{ - struct timer_list_iter *iter = file->private; - - if (!*offset) - iter->now = ktime_to_ns(ktime_get()); - iter->cpu = -1; - iter->second_pass = false; - return move_iter(iter, *offset); -} - static void *timer_list_next(struct seq_file *file, void *v, loff_t *offset) { struct timer_list_iter *iter = file->private; + iter->cpu = cpumask_next(iter->cpu, cpu_online_mask); ++*offset; - return move_iter(iter, 1); + return timer_list_start(file, offset); } static void timer_list_stop(struct seq_file *seq, void *v) diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index f23449d0650e..6c508ff33c62 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -1416,22 +1416,12 @@ ftrace_hash_move(struct ftrace_ops *ops, int enable, * the hashes are freed with call_rcu_sched(). */ static int -ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip, void *regs) +ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip) { struct ftrace_hash *filter_hash; struct ftrace_hash *notrace_hash; int ret; -#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS - /* - * There's a small race when adding ops that the ftrace handler - * that wants regs, may be called without them. We can not - * allow that handler to be called if regs is NULL. - */ - if (regs == NULL && (ops->flags & FTRACE_OPS_FL_SAVE_REGS)) - return 0; -#endif - filter_hash = rcu_dereference_raw_notrace(ops->filter_hash); notrace_hash = rcu_dereference_raw_notrace(ops->notrace_hash); @@ -2144,57 +2134,12 @@ static cycle_t ftrace_update_time; static unsigned long ftrace_update_cnt; unsigned long ftrace_update_tot_cnt; -static inline int ops_traces_mod(struct ftrace_ops *ops) -{ - /* - * Filter_hash being empty will default to trace module. - * But notrace hash requires a test of individual module functions. - */ - return ftrace_hash_empty(ops->filter_hash) && - ftrace_hash_empty(ops->notrace_hash); -} - -/* - * Check if the current ops references the record. - * - * If the ops traces all functions, then it was already accounted for. - * If the ops does not trace the current record function, skip it. - * If the ops ignores the function via notrace filter, skip it. - */ -static inline bool -ops_references_rec(struct ftrace_ops *ops, struct dyn_ftrace *rec) +static int ops_traces_mod(struct ftrace_ops *ops) { - /* If ops isn't enabled, ignore it */ - if (!(ops->flags & FTRACE_OPS_FL_ENABLED)) - return 0; - - /* If ops traces all mods, we already accounted for it */ - if (ops_traces_mod(ops)) - return 0; - - /* The function must be in the filter */ - if (!ftrace_hash_empty(ops->filter_hash) && - !ftrace_lookup_ip(ops->filter_hash, rec->ip)) - return 0; - - /* If in notrace hash, we ignore it too */ - if (ftrace_lookup_ip(ops->notrace_hash, rec->ip)) - return 0; - - return 1; -} - -static int referenced_filters(struct dyn_ftrace *rec) -{ - struct ftrace_ops *ops; - int cnt = 0; - - for (ops = ftrace_ops_list; ops != &ftrace_list_end; ops = ops->next) { - if (ops_references_rec(ops, rec)) - cnt++; - } + struct ftrace_hash *hash; - return cnt; + hash = ops->filter_hash; + return ftrace_hash_empty(hash); } static int ftrace_update_code(struct module *mod) @@ -2203,7 +2148,6 @@ static int ftrace_update_code(struct module *mod) struct dyn_ftrace *p; cycle_t start, stop; unsigned long ref = 0; - bool test = false; int i; /* @@ -2217,12 +2161,9 @@ static int ftrace_update_code(struct module *mod) for (ops = ftrace_ops_list; ops != &ftrace_list_end; ops = ops->next) { - if (ops->flags & FTRACE_OPS_FL_ENABLED) { - if (ops_traces_mod(ops)) - ref++; - else - test = true; - } + if (ops->flags & FTRACE_OPS_FL_ENABLED && + ops_traces_mod(ops)) + ref++; } } @@ -2232,16 +2173,12 @@ static int ftrace_update_code(struct module *mod) for (pg = ftrace_new_pgs; pg; pg = pg->next) { for (i = 0; i < pg->index; i++) { - int cnt = ref; - /* If something went wrong, bail without enabling anything */ if (unlikely(ftrace_disabled)) return -1; p = &pg->records[i]; - if (test) - cnt += referenced_filters(p); - p->flags = cnt; + p->flags = ref; /* * Do the initial record conversion from mcount jump @@ -2261,7 +2198,7 @@ static int ftrace_update_code(struct module *mod) * conversion puts the module to the correct state, thus * passing the ftrace_make_call check. */ - if (ftrace_start_up && cnt) { + if (ftrace_start_up && ref) { int failed = __ftrace_replace_code(p, 1); if (failed) ftrace_bug(failed, p->ip); @@ -4251,7 +4188,7 @@ static inline void ftrace_startup_enable(int command) { } # define ftrace_shutdown_sysctl() do { } while (0) static inline int -ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip, void *regs) +ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip) { return 1; } @@ -4274,7 +4211,7 @@ ftrace_ops_control_func(unsigned long ip, unsigned long parent_ip, do_for_each_ftrace_op(op, ftrace_control_list) { if (!(op->flags & FTRACE_OPS_FL_STUB) && !ftrace_function_local_disabled(op) && - ftrace_ops_test(op, ip, regs)) + ftrace_ops_test(op, ip)) op->func(ip, parent_ip, op, regs); } while_for_each_ftrace_op(op); trace_recursion_clear(TRACE_CONTROL_BIT); @@ -4307,7 +4244,7 @@ __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip, */ preempt_disable_notrace(); do_for_each_ftrace_op(op, ftrace_ops_list) { - if (ftrace_ops_test(op, ip, regs)) + if (ftrace_ops_test(op, ip)) op->func(ip, parent_ip, op, regs); } while_for_each_ftrace_op(op); preempt_enable_notrace(); diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 5546ae9c84f9..2e5ab7764a86 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -2837,17 +2837,6 @@ static int s_show(struct seq_file *m, void *v) return 0; } -/* - * Should be used after trace_array_get(), trace_types_lock - * ensures that i_cdev was already initialized. - */ -static inline int tracing_get_cpu(struct inode *inode) -{ - if (inode->i_cdev) /* See trace_create_cpu_file() */ - return (long)inode->i_cdev - 1; - return RING_BUFFER_ALL_CPUS; -} - static const struct seq_operations tracer_seq_ops = { .start = s_start, .next = s_next, @@ -2856,9 +2845,9 @@ static const struct seq_operations tracer_seq_ops = { }; static struct trace_iterator * -__tracing_open(struct inode *inode, struct file *file, bool snapshot) +__tracing_open(struct trace_array *tr, struct trace_cpu *tc, + struct inode *inode, struct file *file, bool snapshot) { - struct trace_array *tr = inode->i_private; struct trace_iterator *iter; int cpu; @@ -2899,8 +2888,8 @@ __tracing_open(struct inode *inode, struct file *file, bool snapshot) iter->trace_buffer = &tr->trace_buffer; iter->snapshot = snapshot; iter->pos = -1; - iter->cpu_file = tracing_get_cpu(inode); mutex_init(&iter->mutex); + iter->cpu_file = tc->cpu; /* Notify the tracer early; before we stop tracing. */ if (iter->trace && iter->trace->open) @@ -2976,22 +2965,44 @@ int tracing_open_generic_tr(struct inode *inode, struct file *filp) filp->private_data = inode->i_private; return 0; + +} + +int tracing_open_generic_tc(struct inode *inode, struct file *filp) +{ + struct trace_cpu *tc = inode->i_private; + struct trace_array *tr = tc->tr; + + if (tracing_disabled) + return -ENODEV; + + if (trace_array_get(tr) < 0) + return -ENODEV; + + filp->private_data = inode->i_private; + + return 0; + } static int tracing_release(struct inode *inode, struct file *file) { - struct trace_array *tr = inode->i_private; struct seq_file *m = file->private_data; struct trace_iterator *iter; + struct trace_array *tr; int cpu; + /* Writes do not use seq_file, need to grab tr from inode */ if (!(file->f_mode & FMODE_READ)) { - trace_array_put(tr); + struct trace_cpu *tc = inode->i_private; + + trace_array_put(tc->tr); return 0; } - /* Writes do not use seq_file */ iter = m->private; + tr = iter->tr; + mutex_lock(&trace_types_lock); for_each_tracing_cpu(cpu) { @@ -3027,6 +3038,15 @@ static int tracing_release_generic_tr(struct inode *inode, struct file *file) return 0; } +static int tracing_release_generic_tc(struct inode *inode, struct file *file) +{ + struct trace_cpu *tc = inode->i_private; + struct trace_array *tr = tc->tr; + + trace_array_put(tr); + return 0; +} + static int tracing_single_release_tr(struct inode *inode, struct file *file) { struct trace_array *tr = inode->i_private; @@ -3038,7 +3058,8 @@ static int tracing_single_release_tr(struct inode *inode, struct file *file) static int tracing_open(struct inode *inode, struct file *file) { - struct trace_array *tr = inode->i_private; + struct trace_cpu *tc = inode->i_private; + struct trace_array *tr = tc->tr; struct trace_iterator *iter; int ret = 0; @@ -3046,17 +3067,16 @@ static int tracing_open(struct inode *inode, struct file *file) return -ENODEV; /* If this file was open for write, then erase contents */ - if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) { - int cpu = tracing_get_cpu(inode); - - if (cpu == RING_BUFFER_ALL_CPUS) + if ((file->f_mode & FMODE_WRITE) && + (file->f_flags & O_TRUNC)) { + if (tc->cpu == RING_BUFFER_ALL_CPUS) tracing_reset_online_cpus(&tr->trace_buffer); else - tracing_reset(&tr->trace_buffer, cpu); + tracing_reset(&tr->trace_buffer, tc->cpu); } if (file->f_mode & FMODE_READ) { - iter = __tracing_open(inode, file, false); + iter = __tracing_open(tr, tc, inode, file, false); if (IS_ERR(iter)) ret = PTR_ERR(iter); else if (trace_flags & TRACE_ITER_LATENCY_FMT) @@ -3922,7 +3942,8 @@ tracing_max_lat_write(struct file *filp, const char __user *ubuf, static int tracing_open_pipe(struct inode *inode, struct file *filp) { - struct trace_array *tr = inode->i_private; + struct trace_cpu *tc = inode->i_private; + struct trace_array *tr = tc->tr; struct trace_iterator *iter; int ret = 0; @@ -3968,9 +3989,9 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp) if (trace_clocks[tr->clock_id].in_ns) iter->iter_flags |= TRACE_FILE_TIME_IN_NS; - iter->tr = tr; - iter->trace_buffer = &tr->trace_buffer; - iter->cpu_file = tracing_get_cpu(inode); + iter->cpu_file = tc->cpu; + iter->tr = tc->tr; + iter->trace_buffer = &tc->tr->trace_buffer; mutex_init(&iter->mutex); filp->private_data = iter; @@ -3993,7 +4014,8 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp) static int tracing_release_pipe(struct inode *inode, struct file *file) { struct trace_iterator *iter = file->private_data; - struct trace_array *tr = inode->i_private; + struct trace_cpu *tc = inode->i_private; + struct trace_array *tr = tc->tr; mutex_lock(&trace_types_lock); @@ -4347,16 +4369,15 @@ static ssize_t tracing_entries_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) { - struct inode *inode = file_inode(filp); - struct trace_array *tr = inode->i_private; - int cpu = tracing_get_cpu(inode); + struct trace_cpu *tc = filp->private_data; + struct trace_array *tr = tc->tr; char buf[64]; int r = 0; ssize_t ret; mutex_lock(&trace_types_lock); - if (cpu == RING_BUFFER_ALL_CPUS) { + if (tc->cpu == RING_BUFFER_ALL_CPUS) { int cpu, buf_size_same; unsigned long size; @@ -4383,7 +4404,7 @@ tracing_entries_read(struct file *filp, char __user *ubuf, } else r = sprintf(buf, "X\n"); } else - r = sprintf(buf, "%lu\n", per_cpu_ptr(tr->trace_buffer.data, cpu)->entries >> 10); + r = sprintf(buf, "%lu\n", per_cpu_ptr(tr->trace_buffer.data, tc->cpu)->entries >> 10); mutex_unlock(&trace_types_lock); @@ -4395,8 +4416,7 @@ static ssize_t tracing_entries_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { - struct inode *inode = file_inode(filp); - struct trace_array *tr = inode->i_private; + struct trace_cpu *tc = filp->private_data; unsigned long val; int ret; @@ -4410,7 +4430,8 @@ tracing_entries_write(struct file *filp, const char __user *ubuf, /* value is in KB */ val <<= 10; - ret = tracing_resize_ring_buffer(tr, val, tracing_get_cpu(inode)); + + ret = tracing_resize_ring_buffer(tc->tr, val, tc->cpu); if (ret < 0) return ret; @@ -4671,7 +4692,8 @@ struct ftrace_buffer_info { #ifdef CONFIG_TRACER_SNAPSHOT static int tracing_snapshot_open(struct inode *inode, struct file *file) { - struct trace_array *tr = inode->i_private; + struct trace_cpu *tc = inode->i_private; + struct trace_array *tr = tc->tr; struct trace_iterator *iter; struct seq_file *m; int ret = 0; @@ -4680,7 +4702,7 @@ static int tracing_snapshot_open(struct inode *inode, struct file *file) return -ENODEV; if (file->f_mode & FMODE_READ) { - iter = __tracing_open(inode, file, true); + iter = __tracing_open(tr, tc, inode, file, true); if (IS_ERR(iter)) ret = PTR_ERR(iter); } else { @@ -4697,8 +4719,8 @@ static int tracing_snapshot_open(struct inode *inode, struct file *file) ret = 0; iter->tr = tr; - iter->trace_buffer = &tr->max_buffer; - iter->cpu_file = tracing_get_cpu(inode); + iter->trace_buffer = &tc->tr->max_buffer; + iter->cpu_file = tc->cpu; m->private = iter; file->private_data = m; } @@ -4857,11 +4879,11 @@ static const struct file_operations tracing_pipe_fops = { }; static const struct file_operations tracing_entries_fops = { - .open = tracing_open_generic_tr, + .open = tracing_open_generic_tc, .read = tracing_entries_read, .write = tracing_entries_write, .llseek = generic_file_llseek, - .release = tracing_release_generic_tr, + .release = tracing_release_generic_tc, }; static const struct file_operations tracing_total_entries_fops = { @@ -4913,7 +4935,8 @@ static const struct file_operations snapshot_raw_fops = { static int tracing_buffers_open(struct inode *inode, struct file *filp) { - struct trace_array *tr = inode->i_private; + struct trace_cpu *tc = inode->i_private; + struct trace_array *tr = tc->tr; struct ftrace_buffer_info *info; int ret; @@ -4932,7 +4955,7 @@ static int tracing_buffers_open(struct inode *inode, struct file *filp) mutex_lock(&trace_types_lock); info->iter.tr = tr; - info->iter.cpu_file = tracing_get_cpu(inode); + info->iter.cpu_file = tc->cpu; info->iter.trace = tr->current_trace; info->iter.trace_buffer = &tr->trace_buffer; info->spare = NULL; @@ -5249,14 +5272,14 @@ static ssize_t tracing_stats_read(struct file *filp, char __user *ubuf, size_t count, loff_t *ppos) { - struct inode *inode = file_inode(filp); - struct trace_array *tr = inode->i_private; + struct trace_cpu *tc = filp->private_data; + struct trace_array *tr = tc->tr; struct trace_buffer *trace_buf = &tr->trace_buffer; - int cpu = tracing_get_cpu(inode); struct trace_seq *s; unsigned long cnt; unsigned long long t; unsigned long usec_rem; + int cpu = tc->cpu; s = kmalloc(sizeof(*s), GFP_KERNEL); if (!s) @@ -5309,10 +5332,10 @@ tracing_stats_read(struct file *filp, char __user *ubuf, } static const struct file_operations tracing_stats_fops = { - .open = tracing_open_generic_tr, + .open = tracing_open_generic_tc, .read = tracing_stats_read, .llseek = generic_file_llseek, - .release = tracing_release_generic_tr, + .release = tracing_release_generic_tc, }; #ifdef CONFIG_DYNAMIC_FTRACE @@ -5501,20 +5524,10 @@ static struct dentry *tracing_dentry_percpu(struct trace_array *tr, int cpu) return tr->percpu_dir; } -static struct dentry * -trace_create_cpu_file(const char *name, umode_t mode, struct dentry *parent, - void *data, long cpu, const struct file_operations *fops) -{ - struct dentry *ret = trace_create_file(name, mode, parent, data, fops); - - if (ret) /* See tracing_get_cpu() */ - ret->d_inode->i_cdev = (void *)(cpu + 1); - return ret; -} - static void tracing_init_debugfs_percpu(struct trace_array *tr, long cpu) { + struct trace_array_cpu *data = per_cpu_ptr(tr->trace_buffer.data, cpu); struct dentry *d_percpu = tracing_dentry_percpu(tr, cpu); struct dentry *d_cpu; char cpu_dir[30]; /* 30 characters should be more than enough */ @@ -5530,28 +5543,28 @@ tracing_init_debugfs_percpu(struct trace_array *tr, long cpu) } /* per cpu trace_pipe */ - trace_create_cpu_file("trace_pipe", 0444, d_cpu, - tr, cpu, &tracing_pipe_fops); + trace_create_file("trace_pipe", 0444, d_cpu, + (void *)&data->trace_cpu, &tracing_pipe_fops); /* per cpu trace */ - trace_create_cpu_file("trace", 0644, d_cpu, - tr, cpu, &tracing_fops); + trace_create_file("trace", 0644, d_cpu, + (void *)&data->trace_cpu, &tracing_fops); - trace_create_cpu_file("trace_pipe_raw", 0444, d_cpu, - tr, cpu, &tracing_buffers_fops); + trace_create_file("trace_pipe_raw", 0444, d_cpu, + (void *)&data->trace_cpu, &tracing_buffers_fops); - trace_create_cpu_file("stats", 0444, d_cpu, - tr, cpu, &tracing_stats_fops); + trace_create_file("stats", 0444, d_cpu, + (void *)&data->trace_cpu, &tracing_stats_fops); - trace_create_cpu_file("buffer_size_kb", 0444, d_cpu, - tr, cpu, &tracing_entries_fops); + trace_create_file("buffer_size_kb", 0444, d_cpu, + (void *)&data->trace_cpu, &tracing_entries_fops); #ifdef CONFIG_TRACER_SNAPSHOT - trace_create_cpu_file("snapshot", 0644, d_cpu, - tr, cpu, &snapshot_fops); + trace_create_file("snapshot", 0644, d_cpu, + (void *)&data->trace_cpu, &snapshot_fops); - trace_create_cpu_file("snapshot_raw", 0444, d_cpu, - tr, cpu, &snapshot_raw_fops); + trace_create_file("snapshot_raw", 0444, d_cpu, + (void *)&data->trace_cpu, &snapshot_raw_fops); #endif } @@ -6114,13 +6127,13 @@ init_tracer_debugfs(struct trace_array *tr, struct dentry *d_tracer) tr, &tracing_iter_fops); trace_create_file("trace", 0644, d_tracer, - tr, &tracing_fops); + (void *)&tr->trace_cpu, &tracing_fops); trace_create_file("trace_pipe", 0444, d_tracer, - tr, &tracing_pipe_fops); + (void *)&tr->trace_cpu, &tracing_pipe_fops); trace_create_file("buffer_size_kb", 0644, d_tracer, - tr, &tracing_entries_fops); + (void *)&tr->trace_cpu, &tracing_entries_fops); trace_create_file("buffer_total_size_kb", 0444, d_tracer, tr, &tracing_total_entries_fops); @@ -6135,11 +6148,11 @@ init_tracer_debugfs(struct trace_array *tr, struct dentry *d_tracer) &trace_clock_fops); trace_create_file("tracing_on", 0644, d_tracer, - tr, &rb_simple_fops); + tr, &rb_simple_fops); #ifdef CONFIG_TRACER_SNAPSHOT trace_create_file("snapshot", 0644, d_tracer, - tr, &snapshot_fops); + (void *)&tr->trace_cpu, &snapshot_fops); #endif for_each_tracing_cpu(cpu) diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 3d18aadef493..69532630a2d6 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -114,7 +114,7 @@ static int __trace_define_field(struct list_head *head, const char *type, field = kmem_cache_alloc(field_cachep, GFP_TRACE); if (!field) - return -ENOMEM; + goto err; field->name = name; field->type = type; @@ -131,6 +131,11 @@ static int __trace_define_field(struct list_head *head, const char *type, list_add(&field->link, head); return 0; + +err: + kmem_cache_free(field_cachep, field); + + return -ENOMEM; } int trace_define_field(struct ftrace_event_call *call, const char *type, @@ -407,42 +412,33 @@ static void put_system(struct ftrace_subsystem_dir *dir) mutex_unlock(&event_mutex); } -static void remove_subsystem(struct ftrace_subsystem_dir *dir) +/* + * Open and update trace_array ref count. + * Must have the current trace_array passed to it. + */ +static int tracing_open_generic_file(struct inode *inode, struct file *filp) { - if (!dir) - return; + struct ftrace_event_file *file = inode->i_private; + struct trace_array *tr = file->tr; + int ret; - if (!--dir->nr_events) { - debugfs_remove_recursive(dir->entry); - list_del(&dir->list); - __put_system_dir(dir); - } -} + if (trace_array_get(tr) < 0) + return -ENODEV; -static void *event_file_data(struct file *filp) -{ - return ACCESS_ONCE(file_inode(filp)->i_private); + ret = tracing_open_generic(inode, filp); + if (ret < 0) + trace_array_put(tr); + return ret; } -static void remove_event_file_dir(struct ftrace_event_file *file) +static int tracing_release_generic_file(struct inode *inode, struct file *filp) { - struct dentry *dir = file->dir; - struct dentry *child; - - if (dir) { - spin_lock(&dir->d_lock); /* probably unneeded */ - list_for_each_entry(child, &dir->d_subdirs, d_u.d_child) { - if (child->d_inode) /* probably unneeded */ - child->d_inode->i_private = NULL; - } - spin_unlock(&dir->d_lock); + struct ftrace_event_file *file = inode->i_private; + struct trace_array *tr = file->tr; - debugfs_remove_recursive(dir); - } + trace_array_put(tr); - list_del(&file->list); - remove_subsystem(file->system); - kmem_cache_free(file_cachep, file); + return 0; } /* @@ -686,23 +682,13 @@ static ssize_t event_enable_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) { - struct ftrace_event_file *file; - unsigned long flags; + struct ftrace_event_file *file = filp->private_data; char *buf; - mutex_lock(&event_mutex); - file = event_file_data(filp); - if (likely(file)) - flags = file->flags; - mutex_unlock(&event_mutex); - - if (!file) - return -ENODEV; - - if (flags & FTRACE_EVENT_FL_ENABLED) { - if (flags & FTRACE_EVENT_FL_SOFT_DISABLED) + if (file->flags & FTRACE_EVENT_FL_ENABLED) { + if (file->flags & FTRACE_EVENT_FL_SOFT_DISABLED) buf = "0*\n"; - else if (flags & FTRACE_EVENT_FL_SOFT_MODE) + else if (file->flags & FTRACE_EVENT_FL_SOFT_MODE) buf = "1*\n"; else buf = "1\n"; @@ -716,10 +702,13 @@ static ssize_t event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { - struct ftrace_event_file *file; + struct ftrace_event_file *file = filp->private_data; unsigned long val; int ret; + if (!file) + return -EINVAL; + ret = kstrtoul_from_user(ubuf, cnt, 10, &val); if (ret) return ret; @@ -731,11 +720,8 @@ event_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, switch (val) { case 0: case 1: - ret = -ENODEV; mutex_lock(&event_mutex); - file = event_file_data(filp); - if (likely(file)) - ret = ftrace_event_enable_disable(file, val); + ret = ftrace_event_enable_disable(file, val); mutex_unlock(&event_mutex); break; @@ -842,7 +828,7 @@ enum { static void *f_next(struct seq_file *m, void *v, loff_t *pos) { - struct ftrace_event_call *call = event_file_data(m->private); + struct ftrace_event_call *call = m->private; struct ftrace_event_field *field; struct list_head *common_head = &ftrace_common_fields; struct list_head *head = trace_get_fields(call); @@ -886,11 +872,6 @@ static void *f_start(struct seq_file *m, loff_t *pos) loff_t l = 0; void *p; - /* ->stop() is called even if ->start() fails */ - mutex_lock(&event_mutex); - if (!event_file_data(m->private)) - return ERR_PTR(-ENODEV); - /* Start by showing the header */ if (!*pos) return (void *)FORMAT_HEADER; @@ -905,7 +886,7 @@ static void *f_start(struct seq_file *m, loff_t *pos) static int f_show(struct seq_file *m, void *v) { - struct ftrace_event_call *call = event_file_data(m->private); + struct ftrace_event_call *call = m->private; struct ftrace_event_field *field; const char *array_descriptor; @@ -956,7 +937,6 @@ static int f_show(struct seq_file *m, void *v) static void f_stop(struct seq_file *m, void *p) { - mutex_unlock(&event_mutex); } static const struct seq_operations trace_format_seq_ops = { @@ -968,6 +948,7 @@ static const struct seq_operations trace_format_seq_ops = { static int trace_format_open(struct inode *inode, struct file *file) { + struct ftrace_event_call *call = inode->i_private; struct seq_file *m; int ret; @@ -976,7 +957,7 @@ static int trace_format_open(struct inode *inode, struct file *file) return ret; m = file->private_data; - m->private = file; + m->private = call; return 0; } @@ -984,22 +965,19 @@ static int trace_format_open(struct inode *inode, struct file *file) static ssize_t event_id_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) { - int id = (long)event_file_data(filp); + struct ftrace_event_call *call = filp->private_data; struct trace_seq *s; int r; if (*ppos) return 0; - if (unlikely(!id)) - return -ENODEV; - s = kmalloc(sizeof(*s), GFP_KERNEL); if (!s) return -ENOMEM; trace_seq_init(s); - trace_seq_printf(s, "%d\n", id); + trace_seq_printf(s, "%d\n", call->event.type); r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len); @@ -1011,28 +989,21 @@ static ssize_t event_filter_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) { - struct ftrace_event_call *call; + struct ftrace_event_call *call = filp->private_data; struct trace_seq *s; - int r = -ENODEV; + int r; if (*ppos) return 0; s = kmalloc(sizeof(*s), GFP_KERNEL); - if (!s) return -ENOMEM; trace_seq_init(s); - mutex_lock(&event_mutex); - call = event_file_data(filp); - if (call) - print_event_filter(call, s); - mutex_unlock(&event_mutex); - - if (call) - r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len); + print_event_filter(call, s); + r = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, s->len); kfree(s); @@ -1043,9 +1014,9 @@ static ssize_t event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { - struct ftrace_event_call *call; + struct ftrace_event_call *call = filp->private_data; char *buf; - int err = -ENODEV; + int err; if (cnt >= PAGE_SIZE) return -EINVAL; @@ -1060,12 +1031,7 @@ event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt, } buf[cnt] = '\0'; - mutex_lock(&event_mutex); - call = event_file_data(filp); - if (call) - err = apply_event_filter(call, buf); - mutex_unlock(&event_mutex); - + err = apply_event_filter(call, buf); free_page((unsigned long) buf); if (err < 0) return err; @@ -1287,9 +1253,10 @@ static const struct file_operations ftrace_set_event_fops = { }; static const struct file_operations ftrace_enable_fops = { - .open = tracing_open_generic, + .open = tracing_open_generic_file, .read = event_enable_read, .write = event_enable_write, + .release = tracing_release_generic_file, .llseek = default_llseek, }; @@ -1301,6 +1268,7 @@ static const struct file_operations ftrace_event_format_fops = { }; static const struct file_operations ftrace_event_id_fops = { + .open = tracing_open_generic, .read = event_id_read, .llseek = default_llseek, }; @@ -1548,8 +1516,8 @@ event_create_dir(struct dentry *parent, #ifdef CONFIG_PERF_EVENTS if (call->event.type && call->class->reg) - trace_create_file("id", 0444, file->dir, - (void *)(long)call->event.type, id); + trace_create_file("id", 0444, file->dir, call, + id); #endif /* @@ -1574,16 +1542,33 @@ event_create_dir(struct dentry *parent, return 0; } +static void remove_subsystem(struct ftrace_subsystem_dir *dir) +{ + if (!dir) + return; + + if (!--dir->nr_events) { + debugfs_remove_recursive(dir->entry); + list_del(&dir->list); + __put_system_dir(dir); + } +} + static void remove_event_from_tracers(struct ftrace_event_call *call) { struct ftrace_event_file *file; struct trace_array *tr; do_for_each_event_file_safe(tr, file) { + if (file->event_call != call) continue; - remove_event_file_dir(file); + list_del(&file->list); + debugfs_remove_recursive(file->dir); + remove_subsystem(file->system); + kmem_cache_free(file_cachep, file); + /* * The do_for_each_event_file_safe() is * a double loop. After finding the call for this @@ -1735,47 +1720,16 @@ static void __trace_remove_event_call(struct ftrace_event_call *call) destroy_preds(call); } -static int probe_remove_event_call(struct ftrace_event_call *call) -{ - struct trace_array *tr; - struct ftrace_event_file *file; - -#ifdef CONFIG_PERF_EVENTS - if (call->perf_refcount) - return -EBUSY; -#endif - do_for_each_event_file(tr, file) { - if (file->event_call != call) - continue; - /* - * We can't rely on ftrace_event_enable_disable(enable => 0) - * we are going to do, FTRACE_EVENT_FL_SOFT_MODE can suppress - * TRACE_REG_UNREGISTER. - */ - if (file->flags & FTRACE_EVENT_FL_ENABLED) - return -EBUSY; - break; - } while_for_each_event_file(); - - __trace_remove_event_call(call); - - return 0; -} - /* Remove an event_call */ -int trace_remove_event_call(struct ftrace_event_call *call) +void trace_remove_event_call(struct ftrace_event_call *call) { - int ret; - mutex_lock(&trace_types_lock); mutex_lock(&event_mutex); down_write(&trace_event_sem); - ret = probe_remove_event_call(call); + __trace_remove_event_call(call); up_write(&trace_event_sem); mutex_unlock(&event_mutex); mutex_unlock(&trace_types_lock); - - return ret; } #define for_each_event(event, start, end) \ @@ -2347,8 +2301,12 @@ __trace_remove_event_dirs(struct trace_array *tr) { struct ftrace_event_file *file, *next; - list_for_each_entry_safe(file, next, &tr->events, list) - remove_event_file_dir(file); + list_for_each_entry_safe(file, next, &tr->events, list) { + list_del(&file->list); + debugfs_remove_recursive(file->dir); + remove_subsystem(file->system); + kmem_cache_free(file_cachep, file); + } } static void diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 0a1edc694d67..e1b653f7e1ca 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c @@ -631,15 +631,17 @@ static void append_filter_err(struct filter_parse_state *ps, free_page((unsigned long) buf); } -/* caller must hold event_mutex */ void print_event_filter(struct ftrace_event_call *call, struct trace_seq *s) { - struct event_filter *filter = call->filter; + struct event_filter *filter; + mutex_lock(&event_mutex); + filter = call->filter; if (filter && filter->filter_string) trace_seq_printf(s, "%s\n", filter->filter_string); else trace_seq_printf(s, "none\n"); + mutex_unlock(&event_mutex); } void print_subsystem_event_filter(struct event_subsystem *system, @@ -1833,22 +1835,23 @@ static int create_system_filter(struct event_subsystem *system, return err; } -/* caller must hold event_mutex */ int apply_event_filter(struct ftrace_event_call *call, char *filter_string) { struct event_filter *filter; - int err; + int err = 0; + + mutex_lock(&event_mutex); if (!strcmp(strstrip(filter_string), "0")) { filter_disable(call); filter = call->filter; if (!filter) - return 0; + goto out_unlock; RCU_INIT_POINTER(call->filter, NULL); /* Make sure the filter is not being used */ synchronize_sched(); __free_filter(filter); - return 0; + goto out_unlock; } err = create_filter(call, filter_string, true, &filter); @@ -1875,6 +1878,8 @@ int apply_event_filter(struct ftrace_event_call *call, char *filter_string) __free_filter(tmp); } } +out_unlock: + mutex_unlock(&event_mutex); return err; } diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 64abc8ca928b..9f46e98ba8f2 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -90,7 +90,7 @@ static __kprobes bool trace_probe_is_on_module(struct trace_probe *tp) } static int register_probe_event(struct trace_probe *tp); -static int unregister_probe_event(struct trace_probe *tp); +static void unregister_probe_event(struct trace_probe *tp); static DEFINE_MUTEX(probe_lock); static LIST_HEAD(probe_list); @@ -281,8 +281,6 @@ trace_probe_file_index(struct trace_probe *tp, struct ftrace_event_file *file) static int disable_trace_probe(struct trace_probe *tp, struct ftrace_event_file *file) { - struct ftrace_event_file **old = NULL; - int wait = 0; int ret = 0; mutex_lock(&probe_enable_lock); @@ -316,7 +314,10 @@ disable_trace_probe(struct trace_probe *tp, struct ftrace_event_file *file) } rcu_assign_pointer(tp->files, new); - wait = 1; + + /* Make sure the probe is done with old files */ + synchronize_sched(); + kfree(old); } else tp->flags &= ~TP_FLAG_PROFILE; @@ -325,25 +326,11 @@ disable_trace_probe(struct trace_probe *tp, struct ftrace_event_file *file) disable_kretprobe(&tp->rp); else disable_kprobe(&tp->rp.kp); - wait = 1; } out_unlock: mutex_unlock(&probe_enable_lock); - if (wait) { - /* - * Synchronize with kprobe_trace_func/kretprobe_trace_func - * to ensure disabled (all running handlers are finished). - * This is not only for kfree(), but also the caller, - * trace_remove_event_call() supposes it for releasing - * event_call related objects, which will be accessed in - * the kprobe_trace_func/kretprobe_trace_func. - */ - synchronize_sched(); - kfree(old); /* Ignored if link == NULL */ - } - return ret; } @@ -411,12 +398,9 @@ static int unregister_trace_probe(struct trace_probe *tp) if (trace_probe_is_enabled(tp)) return -EBUSY; - /* Will fail if probe is being used by ftrace or perf */ - if (unregister_probe_event(tp)) - return -EBUSY; - __unregister_trace_probe(tp); list_del(&tp->list); + unregister_probe_event(tp); return 0; } @@ -695,9 +679,7 @@ static int release_all_trace_probes(void) /* TODO: Use batch unregistration */ while (!list_empty(&probe_list)) { tp = list_entry(probe_list.next, struct trace_probe, list); - ret = unregister_trace_probe(tp); - if (ret) - goto end; + unregister_trace_probe(tp); free_trace_probe(tp); } @@ -1330,15 +1312,11 @@ static int register_probe_event(struct trace_probe *tp) return ret; } -static int unregister_probe_event(struct trace_probe *tp) +static void unregister_probe_event(struct trace_probe *tp) { - int ret; - /* tp->event is unregistered in trace_remove_event_call() */ - ret = trace_remove_event_call(&tp->call); - if (!ret) - kfree(tp->call.print_fmt); - return ret; + trace_remove_event_call(&tp->call); + kfree(tp->call.print_fmt); } /* Make a debugfs interface for controlling probe points */ diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c index 6fd72b768522..d5d0cd368a56 100644 --- a/kernel/trace/trace_uprobe.c +++ b/kernel/trace/trace_uprobe.c @@ -70,7 +70,7 @@ struct trace_uprobe { (sizeof(struct probe_arg) * (n))) static int register_uprobe_event(struct trace_uprobe *tu); -static int unregister_uprobe_event(struct trace_uprobe *tu); +static void unregister_uprobe_event(struct trace_uprobe *tu); static DEFINE_MUTEX(uprobe_lock); static LIST_HEAD(uprobe_list); @@ -164,17 +164,11 @@ static struct trace_uprobe *find_probe_event(const char *event, const char *grou } /* Unregister a trace_uprobe and probe_event: call with locking uprobe_lock */ -static int unregister_trace_uprobe(struct trace_uprobe *tu) +static void unregister_trace_uprobe(struct trace_uprobe *tu) { - int ret; - - ret = unregister_uprobe_event(tu); - if (ret) - return ret; - list_del(&tu->list); + unregister_uprobe_event(tu); free_trace_uprobe(tu); - return 0; } /* Register a trace_uprobe and probe_event */ @@ -187,12 +181,9 @@ static int register_trace_uprobe(struct trace_uprobe *tu) /* register as an event */ old_tp = find_probe_event(tu->call.name, tu->call.class->system); - if (old_tp) { + if (old_tp) /* delete old event */ - ret = unregister_trace_uprobe(old_tp); - if (ret) - goto end; - } + unregister_trace_uprobe(old_tp); ret = register_uprobe_event(tu); if (ret) { @@ -265,8 +256,6 @@ static int create_trace_uprobe(int argc, char **argv) group = UPROBE_EVENT_SYSTEM; if (is_delete) { - int ret; - if (!event) { pr_info("Delete command needs an event name.\n"); return -EINVAL; @@ -280,9 +269,9 @@ static int create_trace_uprobe(int argc, char **argv) return -ENOENT; } /* delete an event */ - ret = unregister_trace_uprobe(tu); + unregister_trace_uprobe(tu); mutex_unlock(&uprobe_lock); - return ret; + return 0; } if (argc < 2) { @@ -419,20 +408,16 @@ static int create_trace_uprobe(int argc, char **argv) return ret; } -static int cleanup_all_probes(void) +static void cleanup_all_probes(void) { struct trace_uprobe *tu; - int ret = 0; mutex_lock(&uprobe_lock); while (!list_empty(&uprobe_list)) { tu = list_entry(uprobe_list.next, struct trace_uprobe, list); - ret = unregister_trace_uprobe(tu); - if (ret) - break; + unregister_trace_uprobe(tu); } mutex_unlock(&uprobe_lock); - return ret; } /* Probes listing interfaces */ @@ -477,13 +462,8 @@ static const struct seq_operations probes_seq_op = { static int probes_open(struct inode *inode, struct file *file) { - int ret; - - if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) { - ret = cleanup_all_probes(); - if (ret) - return ret; - } + if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) + cleanup_all_probes(); return seq_open(file, &probes_seq_op); } @@ -990,17 +970,12 @@ static int register_uprobe_event(struct trace_uprobe *tu) return ret; } -static int unregister_uprobe_event(struct trace_uprobe *tu) +static void unregister_uprobe_event(struct trace_uprobe *tu) { - int ret; - /* tu->event is unregistered in trace_remove_event_call() */ - ret = trace_remove_event_call(&tu->call); - if (ret) - return ret; + trace_remove_event_call(&tu->call); kfree(tu->call.print_fmt); tu->call.print_fmt = NULL; - return 0; } /* Make a trace interface for controling probe points */ diff --git a/kernel/workqueue.c b/kernel/workqueue.c index e52d002d3893..6f019219ddbd 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -2188,15 +2188,6 @@ __acquires(&pool->lock) dump_stack(); } - /* - * The following prevents a kworker from hogging CPU on !PREEMPT - * kernels, where a requeueing work item waiting for something to - * happen could deadlock with stop_machine as such work item could - * indefinitely requeue itself while all other CPUs are trapped in - * stop_machine. - */ - cond_resched(); - spin_lock_irq(&pool->lock); /* clear cpu intensive status */ diff --git a/mm/bounce.c b/mm/bounce.c index 5a7d58fb883b..c9f0a4339a7d 100644 --- a/mm/bounce.c +++ b/mm/bounce.c @@ -204,8 +204,6 @@ static void __blk_queue_bounce(struct request_queue *q, struct bio **bio_orig, struct bio_vec *to, *from; unsigned i; - if (force) - goto bounce; bio_for_each_segment(from, *bio_orig, i) if (page_to_pfn(from->bv_page) > queue_bounce_pfn(q)) goto bounce; diff --git a/mm/huge_memory.c b/mm/huge_memory.c index b92d0ce428b1..362c329b83fe 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -2286,8 +2286,6 @@ static void collapse_huge_page(struct mm_struct *mm, goto out; vma = find_vma(mm, address); - if (!vma) - goto out; hstart = (vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK; hend = vma->vm_end & HPAGE_PMD_MASK; if (address < hstart || address + HPAGE_PMD_SIZE > hend) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 905ce72c8c4e..82a187aea4c0 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -5584,13 +5584,7 @@ static int compare_thresholds(const void *a, const void *b) const struct mem_cgroup_threshold *_a = a; const struct mem_cgroup_threshold *_b = b; - if (_a->threshold > _b->threshold) - return 1; - - if (_a->threshold < _b->threshold) - return -1; - - return 0; + return _a->threshold - _b->threshold; } static int mem_cgroup_oom_notify_cb(struct mem_cgroup *memcg) diff --git a/mm/memory.c b/mm/memory.c index 265c29b513c6..5a35443c01ad 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -3461,8 +3461,6 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma, /* file_update_time outside page_lock */ if (vma->vm_file && !page_mkwrite) file_update_time(vma->vm_file); - if (vma->vm_prfile && !page_mkwrite) - file_update_time(vma->vm_prfile); } else { unlock_page(vmf.page); if (anon) diff --git a/mm/migrate.c b/mm/migrate.c index 25ca7caf9092..6f0c24438bba 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -103,7 +103,7 @@ void putback_movable_pages(struct list_head *l) list_del(&page->lru); dec_zone_page_state(page, NR_ISOLATED_ANON + page_is_file_cache(page)); - if (unlikely(isolated_balloon_page(page))) + if (unlikely(balloon_page_movable(page))) balloon_page_putback(page); else putback_lru_page(page); diff --git a/mm/mmap.c b/mm/mmap.c index d7bae856bb08..8d25fdc653be 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -251,8 +251,6 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma) vma->vm_ops->close(vma); if (vma->vm_file) fput(vma->vm_file); - if (vma->vm_prfile) - fput(vma->vm_prfile); mpol_put(vma_policy(vma)); kmem_cache_free(vm_area_cachep, vma); return next; @@ -863,8 +861,6 @@ again: remove_next = 1 + (end > next->vm_end); if (file) { uprobe_munmap(next, next->vm_start, next->vm_end); fput(file); - if (vma->vm_prfile) - fput(vma->vm_prfile); } if (next->anon_vma) anon_vma_merge(vma, next); @@ -2446,8 +2442,6 @@ static int __split_vma(struct mm_struct * mm, struct vm_area_struct * vma, if (new->vm_file) get_file(new->vm_file); - if (new->vm_prfile) - get_file(new->vm_prfile); if (new->vm_ops && new->vm_ops->open) new->vm_ops->open(new); @@ -2467,8 +2461,6 @@ static int __split_vma(struct mm_struct * mm, struct vm_area_struct * vma, new->vm_ops->close(new); if (new->vm_file) fput(new->vm_file); - if (new->vm_prfile) - fput(new->vm_prfile); unlink_anon_vmas(new); out_free_mpol: mpol_put(pol); @@ -2868,8 +2860,6 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap, goto out_free_mempol; if (new_vma->vm_file) get_file(new_vma->vm_file); - if (new_vma->vm_prfile) - get_file(new_vma->vm_prfile); if (new_vma->vm_ops && new_vma->vm_ops->open) new_vma->vm_ops->open(new_vma); vma_link(mm, new_vma, prev, rb_link, rb_parent); diff --git a/mm/nommu.c b/mm/nommu.c index d30faea1fc9b..298884dcd6e7 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -651,8 +651,6 @@ static void __put_nommu_region(struct vm_region *region) if (region->vm_file) fput(region->vm_file); - if (region->vm_prfile) - fput(region->vm_prfile); /* IO memory and memory shared directly out of the pagecache * from ramfs/tmpfs mustn't be released here */ @@ -811,8 +809,6 @@ static void delete_vma(struct mm_struct *mm, struct vm_area_struct *vma) vma->vm_ops->close(vma); if (vma->vm_file) fput(vma->vm_file); - if (vma->vm_prfile) - fput(vma->vm_prfile); put_nommu_region(vma->vm_region); kmem_cache_free(vm_area_cachep, vma); } @@ -1379,8 +1375,6 @@ unsigned long do_mmap_pgoff(struct file *file, } } fput(region->vm_file); - if (region->vm_prfile) - fput(region->vm_prfile); kmem_cache_free(vm_region_jar, region); region = pregion; result = start; @@ -1457,13 +1451,9 @@ unsigned long do_mmap_pgoff(struct file *file, error: if (region->vm_file) fput(region->vm_file); - if (region->vm_prfile) - fput(region->vm_prfile); kmem_cache_free(vm_region_jar, region); if (vma->vm_file) fput(vma->vm_file); - if (vma->vm_prfile) - fput(vma->vm_prfile); kmem_cache_free(vm_area_cachep, vma); kleave(" = %d", ret); return ret; diff --git a/mm/shmem.c b/mm/shmem.c index 509b393eceeb..5e6a8422658b 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2879,8 +2879,14 @@ EXPORT_SYMBOL_GPL(shmem_truncate_range); /* common code */ +static char *shmem_dname(struct dentry *dentry, char *buffer, int buflen) +{ + return dynamic_dname(dentry, buffer, buflen, "/%s (deleted)", + dentry->d_name.name); +} + static struct dentry_operations anon_ops = { - .d_dname = simple_dname + .d_dname = shmem_dname }; /** diff --git a/mm/slab.h b/mm/slab.h index 4d6d836247dd..f96b49e4704e 100644 --- a/mm/slab.h +++ b/mm/slab.h @@ -162,8 +162,6 @@ static inline const char *cache_name(struct kmem_cache *s) static inline struct kmem_cache *cache_from_memcg(struct kmem_cache *s, int idx) { - if (!s->memcg_params) - return NULL; return s->memcg_params->memcg_caches[idx]; } diff --git a/mm/swap.c b/mm/swap.c index 9f2225f2b5b0..dfd7d71d6841 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -31,7 +31,6 @@ #include #include #include -#include #include "internal.h" @@ -79,19 +78,6 @@ static void __put_compound_page(struct page *page) static void put_compound_page(struct page *page) { - /* - * hugetlbfs pages cannot be split from under us. If this is a - * hugetlbfs page, check refcount on head page and release the page if - * the refcount becomes zero. - */ - if (PageHuge(page)) { - page = compound_head(page); - if (put_page_testzero(page)) - __put_compound_page(page); - - return; - } - if (unlikely(PageTail(page))) { /* __split_huge_page_refcount can run under us */ struct page *page_head = compound_trans_head(page); @@ -195,51 +181,38 @@ bool __get_page_tail(struct page *page) * proper PT lock that already serializes against * split_huge_page(). */ + unsigned long flags; bool got = false; - struct page *page_head; + struct page *page_head = compound_trans_head(page); - /* - * If this is a hugetlbfs page it cannot be split under us. Simply - * increment refcount for the head page. - */ - if (PageHuge(page)) { - page_head = compound_head(page); - atomic_inc(&page_head->_count); - got = true; - } else { - unsigned long flags; - - page_head = compound_trans_head(page); - if (likely(page != page_head && - get_page_unless_zero(page_head))) { - - /* Ref to put_compound_page() comment. */ - if (PageSlab(page_head)) { - if (likely(PageTail(page))) { - __get_page_tail_foll(page, false); - return true; - } else { - put_page(page_head); - return false; - } - } + if (likely(page != page_head && get_page_unless_zero(page_head))) { - /* - * page_head wasn't a dangling pointer but it - * may not be a head page anymore by the time - * we obtain the lock. That is ok as long as it - * can't be freed from under us. - */ - flags = compound_lock_irqsave(page_head); - /* here __split_huge_page_refcount won't run anymore */ + /* Ref to put_compound_page() comment. */ + if (PageSlab(page_head)) { if (likely(PageTail(page))) { __get_page_tail_foll(page, false); - got = true; - } - compound_unlock_irqrestore(page_head, flags); - if (unlikely(!got)) + return true; + } else { put_page(page_head); + return false; + } + } + + /* + * page_head wasn't a dangling pointer but it + * may not be a head page anymore by the time + * we obtain the lock. That is ok as long as it + * can't be freed from under us. + */ + flags = compound_lock_irqsave(page_head); + /* here __split_huge_page_refcount won't run anymore */ + if (likely(PageTail(page))) { + __get_page_tail_foll(page, false); + got = true; } + compound_unlock_irqrestore(page_head, flags); + if (unlikely(!got)) + put_page(page_head); } return got; } diff --git a/mm/vmscan.c b/mm/vmscan.c index 7dbdb6afd101..fa6a85378ee4 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -48,7 +48,6 @@ #include #include -#include #include "internal.h" @@ -979,8 +978,7 @@ unsigned long reclaim_clean_pages_from_list(struct zone *zone, LIST_HEAD(clean_pages); list_for_each_entry_safe(page, next, page_list, lru) { - if (page_is_file_cache(page) && !PageDirty(page) && - !isolated_balloon_page(page)) { + if (page_is_file_cache(page) && !PageDirty(page)) { ClearPageActive(page); list_move(&page->lru, &clean_pages); } diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 7c88f5f83598..ace5e55fe5a3 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1123,11 +1123,7 @@ int hci_dev_open(__u16 dev) goto done; } - /* Check for rfkill but allow the HCI setup stage to proceed - * (which in itself doesn't cause any RF activity). - */ - if (test_bit(HCI_RFKILLED, &hdev->dev_flags) && - !test_bit(HCI_SETUP, &hdev->dev_flags)) { + if (hdev->rfkill && rfkill_blocked(hdev->rfkill)) { ret = -ERFKILL; goto done; } @@ -1549,13 +1545,10 @@ static int hci_rfkill_set_block(void *data, bool blocked) BT_DBG("%p name %s blocked %d", hdev, hdev->name, blocked); - if (blocked) { - set_bit(HCI_RFKILLED, &hdev->dev_flags); - if (!test_bit(HCI_SETUP, &hdev->dev_flags)) - hci_dev_do_close(hdev); - } else { - clear_bit(HCI_RFKILLED, &hdev->dev_flags); -} + if (!blocked) + return 0; + + hci_dev_do_close(hdev); return 0; } @@ -1577,13 +1570,9 @@ static void hci_power_on(struct work_struct *work) return; } - if (test_bit(HCI_RFKILLED, &hdev->dev_flags)) { - clear_bit(HCI_AUTO_OFF, &hdev->dev_flags); - hci_dev_do_close(hdev); - } else if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) { + if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) queue_delayed_work(hdev->req_workqueue, &hdev->power_off, HCI_AUTO_OFF_TIMEOUT); - } if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags)) mgmt_index_added(hdev); @@ -2252,9 +2241,6 @@ int hci_register_dev(struct hci_dev *hdev) } } - if (hdev->rfkill && rfkill_blocked(hdev->rfkill)) - set_bit(HCI_RFKILLED, &hdev->dev_flags); - set_bit(HCI_SETUP, &hdev->dev_flags); if (hdev->dev_type != HCI_AMP) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index dcaa6dbbab2c..b93cd2eb5d58 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3611,11 +3611,7 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb) cp.handle = cpu_to_le16(conn->handle); if (ltk->authenticated) - conn->pending_sec_level = BT_SECURITY_HIGH; - else - conn->pending_sec_level = BT_SECURITY_MEDIUM; - - conn->enc_key_size = ltk->enc_size; + conn->sec_level = BT_SECURITY_HIGH; hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp); diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index de030f50f72b..940f5acb6694 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -231,22 +231,17 @@ static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb) static int hidp_send_report(struct hidp_session *session, struct hid_report *report) { - unsigned char hdr; - u8 *buf; - int rsize, ret; + unsigned char buf[32], hdr; + int rsize; - buf = hid_alloc_report_buf(report, GFP_ATOMIC); - if (!buf) + rsize = ((report->size - 1) >> 3) + 1 + (report->id > 0); + if (rsize > sizeof(buf)) return -EIO; hid_output_report(report, buf); hdr = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT; - rsize = ((report->size - 1) >> 3) + 1 + (report->id > 0); - ret = hidp_send_intr_message(session, hdr, buf, rsize); - - kfree(buf); - return ret; + return hidp_send_intr_message(session, hdr, buf, rsize); } static int hidp_get_raw_report(struct hid_device *hid, diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 68843a28a7af..1d6f7902738e 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -58,6 +58,18 @@ static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err); static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control, struct sk_buff_head *skbs, u8 event); +static inline __u8 bdaddr_type(struct hci_conn *hcon, __u8 type) +{ + if (hcon->type == LE_LINK) { + if (type == ADDR_LE_DEV_PUBLIC) + return BDADDR_LE_PUBLIC; + else + return BDADDR_LE_RANDOM; + } + + return BDADDR_BREDR; +} + /* ---- L2CAP channels ---- */ static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, @@ -1364,6 +1376,8 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) bacpy(&bt_sk(sk)->src, conn->src); bacpy(&bt_sk(sk)->dst, conn->dst); + chan->src_type = bdaddr_type(conn->hcon, conn->hcon->src_type); + chan->dst_type = bdaddr_type(conn->hcon, conn->hcon->dst_type); l2cap_chan_add(conn, chan); @@ -1785,6 +1799,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, /* Set destination address and psm */ lock_sock(sk); bacpy(&bt_sk(sk)->dst, dst); + chan->dst_type = dst_type; release_sock(sk); chan->psm = psm; @@ -1792,7 +1807,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, auth_type = l2cap_get_auth_type(chan); - if (chan->dcid == L2CAP_CID_LE_DATA) + if (bdaddr_type_is_le(dst_type)) hcon = hci_connect(hdev, LE_LINK, dst, dst_type, chan->sec_level, auth_type); else @@ -1825,6 +1840,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, /* Update source addr of the socket */ bacpy(src, conn->src); + chan->src_type = bdaddr_type(hcon, hcon->src_type); l2cap_chan_unlock(chan); l2cap_chan_add(conn, chan); @@ -3755,6 +3771,8 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn, bacpy(&bt_sk(sk)->src, conn->src); bacpy(&bt_sk(sk)->dst, conn->dst); + chan->src_type = bdaddr_type(conn->hcon, conn->hcon->src_type); + chan->dst_type = bdaddr_type(conn->hcon, conn->hcon->dst_type); chan->psm = psm; chan->dcid = scid; chan->local_amp_id = amp_id; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 36fed40c162c..d0ff076f8f98 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -103,7 +103,8 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) __le16_to_cpu(la.l2_psm) == L2CAP_PSM_RFCOMM) chan->sec_level = BT_SECURITY_SDP; - bacpy(&bt_sk(sk)->src, &la.l2_bdaddr); + bacpy(&chan->src, &la.l2_bdaddr); + chan->src_type = la.l2_bdaddr_type; chan->state = BT_BOUND; sk->sk_state = BT_BOUND; @@ -267,10 +268,12 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, la->l2_psm = chan->psm; bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst); la->l2_cid = cpu_to_le16(chan->dcid); + la->l2_bdaddr_type = chan->dst_type; } else { la->l2_psm = chan->sport; bacpy(&la->l2_bdaddr, &bt_sk(sk)->src); la->l2_cid = cpu_to_le16(chan->scid); + la->l2_bdaddr_type = chan->src_type; } return 0; diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 84dd783abe5c..ebfa4443c69b 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -161,7 +161,7 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr) if (!pv) return; - for_each_set_bit_from(vid, pv->vlan_bitmap, VLAN_N_VID) { + for_each_set_bit_from(vid, pv->vlan_bitmap, BR_VLAN_BITMAP_LEN) { f = __br_fdb_get(br, br->dev->dev_addr, vid); if (f && f->is_local && !f->dst) fdb_delete(br, f); @@ -725,7 +725,7 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], /* VID was specified, so use it. */ err = __br_fdb_add(ndm, p, addr, nlh_flags, vid); } else { - if (!pv || bitmap_empty(pv->vlan_bitmap, VLAN_N_VID)) { + if (!pv || bitmap_empty(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN)) { err = __br_fdb_add(ndm, p, addr, nlh_flags, 0); goto out; } @@ -734,7 +734,7 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], * specify a VLAN. To be nice, add/update entry for every * vlan on this port. */ - for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) { + for_each_set_bit(vid, pv->vlan_bitmap, BR_VLAN_BITMAP_LEN) { err = __br_fdb_add(ndm, p, addr, nlh_flags, vid); if (err) goto out; @@ -812,7 +812,7 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], err = __br_fdb_delete(p, addr, vid); } else { - if (!pv || bitmap_empty(pv->vlan_bitmap, VLAN_N_VID)) { + if (!pv || bitmap_empty(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN)) { err = __br_fdb_delete(p, addr, 0); goto out; } @@ -822,7 +822,7 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], * vlan on this port. */ err = -ENOENT; - for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) { + for_each_set_bit(vid, pv->vlan_bitmap, BR_VLAN_BITMAP_LEN) { err &= __br_fdb_delete(p, addr, vid); } } diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index d82058f6fc79..d6448e35e027 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -1185,7 +1185,7 @@ static int br_ip6_multicast_query(struct net_bridge *br, max_delay = msecs_to_jiffies(ntohs(mld->mld_maxdelay)); if (max_delay) group = &mld->mld_mca; - } else { + } else if (skb->len >= sizeof(*mld2q)) { if (!pskb_may_pull(skb, sizeof(*mld2q))) { err = -EINVAL; goto out; @@ -1193,8 +1193,7 @@ static int br_ip6_multicast_query(struct net_bridge *br, mld2q = (struct mld2_query *)icmp6_hdr(skb); if (!mld2q->mld2q_nsrcs) group = &mld2q->mld2q_mca; - - max_delay = max(msecs_to_jiffies(MLDV2_MRC(ntohs(mld2q->mld2q_mrc))), 1UL); + max_delay = mld2q->mld2q_mrc ? MLDV2_MRC(ntohs(mld2q->mld2q_mrc)) : 1; } if (!group) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 06873e80a432..8e3abf564798 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -128,7 +128,7 @@ static int br_fill_ifinfo(struct sk_buff *skb, else pv = br_get_vlan_info(br); - if (!pv || bitmap_empty(pv->vlan_bitmap, VLAN_N_VID)) + if (!pv || bitmap_empty(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN)) goto done; af = nla_nest_start(skb, IFLA_AF_SPEC); @@ -136,7 +136,7 @@ static int br_fill_ifinfo(struct sk_buff *skb, goto nla_put_failure; pvid = br_get_pvid(pv); - for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) { + for_each_set_bit(vid, pv->vlan_bitmap, BR_VLAN_BITMAP_LEN) { vinfo.vid = vid; vinfo.flags = 0; if (vid == pvid) @@ -203,7 +203,7 @@ int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, struct net_device *dev, u32 filter_mask) { int err = 0; - struct net_bridge_port *port = br_port_get_rtnl(dev); + struct net_bridge_port *port = br_port_get_rcu(dev); /* not a bridge port and */ if (!port && !(filter_mask & RTEXT_FILTER_BRVLAN)) @@ -443,7 +443,7 @@ static size_t br_get_link_af_size(const struct net_device *dev) struct net_port_vlans *pv; if (br_port_exists(dev)) - pv = nbp_get_vlan_info(br_port_get_rtnl(dev)); + pv = nbp_get_vlan_info(br_port_get_rcu(dev)); else if (dev->priv_flags & IFF_EBRIDGE) pv = br_get_vlan_info((struct net_bridge *)netdev_priv(dev)); else diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index e696833a31b5..d2c043a857b6 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -183,10 +183,13 @@ struct net_bridge_port static inline struct net_bridge_port *br_port_get_rcu(const struct net_device *dev) { - return rcu_dereference(dev->rx_handler_data); + struct net_bridge_port *port = + rcu_dereference_rtnl(dev->rx_handler_data); + + return br_port_exists(dev) ? port : NULL; } -static inline struct net_bridge_port *br_port_get_rtnl(const struct net_device *dev) +static inline struct net_bridge_port *br_port_get_rtnl(struct net_device *dev) { return br_port_exists(dev) ? rtnl_dereference(dev->rx_handler_data) : NULL; @@ -711,7 +714,6 @@ extern struct net_bridge_port *br_get_port(struct net_bridge *br, extern void br_init_port(struct net_bridge_port *p); extern void br_become_designated_port(struct net_bridge_port *p); -extern void __br_set_forward_delay(struct net_bridge *br, unsigned long t); extern int br_set_forward_delay(struct net_bridge *br, unsigned long x); extern int br_set_hello_time(struct net_bridge *br, unsigned long x); extern int br_set_max_age(struct net_bridge *br, unsigned long x); diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index 3c86f0538cbb..1c0a50f13229 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -209,7 +209,7 @@ static void br_record_config_information(struct net_bridge_port *p, p->designated_age = jiffies - bpdu->message_age; mod_timer(&p->message_age_timer, jiffies - + (bpdu->max_age - bpdu->message_age)); + + (p->br->max_age - bpdu->message_age)); } /* called under bridge lock */ @@ -544,27 +544,18 @@ int br_set_max_age(struct net_bridge *br, unsigned long val) } -void __br_set_forward_delay(struct net_bridge *br, unsigned long t) -{ - br->bridge_forward_delay = t; - if (br_is_root_bridge(br)) - br->forward_delay = br->bridge_forward_delay; -} - int br_set_forward_delay(struct net_bridge *br, unsigned long val) { unsigned long t = clock_t_to_jiffies(val); - int err = -ERANGE; - spin_lock_bh(&br->lock); if (br->stp_enabled != BR_NO_STP && (t < BR_MIN_FORWARD_DELAY || t > BR_MAX_FORWARD_DELAY)) - goto unlock; - - __br_set_forward_delay(br, t); - err = 0; + return -ERANGE; -unlock: + spin_lock_bh(&br->lock); + br->bridge_forward_delay = t; + if (br_is_root_bridge(br)) + br->forward_delay = br->bridge_forward_delay; spin_unlock_bh(&br->lock); - return err; + return 0; } diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c index 108084a04671..d45e760141bb 100644 --- a/net/bridge/br_stp_if.c +++ b/net/bridge/br_stp_if.c @@ -129,14 +129,6 @@ static void br_stp_start(struct net_bridge *br) char *envp[] = { NULL }; r = call_usermodehelper(BR_STP_PROG, argv, envp, UMH_WAIT_PROC); - - spin_lock_bh(&br->lock); - - if (br->bridge_forward_delay < BR_MIN_FORWARD_DELAY) - __br_set_forward_delay(br, BR_MIN_FORWARD_DELAY); - else if (br->bridge_forward_delay < BR_MAX_FORWARD_DELAY) - __br_set_forward_delay(br, BR_MAX_FORWARD_DELAY); - if (r == 0) { br->stp_enabled = BR_USER_STP; br_debug(br, "userspace STP started\n"); @@ -145,10 +137,10 @@ static void br_stp_start(struct net_bridge *br) br_debug(br, "using kernel STP\n"); /* To start timers on any ports left in blocking */ + spin_lock_bh(&br->lock); br_port_state_selection(br); + spin_unlock_bh(&br->lock); } - - spin_unlock_bh(&br->lock); } static void br_stp_stop(struct net_bridge *br) diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c index 9a9ffe7e4019..bd58b45f5f90 100644 --- a/net/bridge/br_vlan.c +++ b/net/bridge/br_vlan.c @@ -108,7 +108,7 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid) clear_bit(vid, v->vlan_bitmap); v->num_vlans--; - if (bitmap_empty(v->vlan_bitmap, VLAN_N_VID)) { + if (bitmap_empty(v->vlan_bitmap, BR_VLAN_BITMAP_LEN)) { if (v->port_idx) rcu_assign_pointer(v->parent.port->vlan_info, NULL); else @@ -122,7 +122,7 @@ static void __vlan_flush(struct net_port_vlans *v) { smp_wmb(); v->pvid = 0; - bitmap_zero(v->vlan_bitmap, VLAN_N_VID); + bitmap_zero(v->vlan_bitmap, BR_VLAN_BITMAP_LEN); if (v->port_idx) rcu_assign_pointer(v->parent.port->vlan_info, NULL); else diff --git a/net/caif/cfctrl.c b/net/caif/cfctrl.c index 0f455227da83..2bd4b58f4372 100644 --- a/net/caif/cfctrl.c +++ b/net/caif/cfctrl.c @@ -293,10 +293,9 @@ int cfctrl_linkup_request(struct cflayer *layer, count = cfctrl_cancel_req(&cfctrl->serv.layer, user_layer); - if (count != 1) { + if (count != 1) pr_err("Could not remove request (%d)", count); return -ENODEV; - } } return 0; } diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 46ec7672cccc..3a246a6cab47 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -2130,8 +2130,6 @@ int ceph_osdc_start_request(struct ceph_osd_client *osdc, dout("osdc_start_request failed map, " " will retry %lld\n", req->r_tid); rc = 0; - } else { - __unregister_request(osdc, req); } goto out_unlock; } diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index dbd9a4792427..603ddd92db19 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -1129,7 +1129,7 @@ static int *calc_pg_raw(struct ceph_osdmap *osdmap, struct ceph_pg pgid, /* pg_temp? */ pgid.seed = ceph_stable_mod(pgid.seed, pool->pg_num, - pool->pg_num_mask); + pool->pgp_num_mask); pg = __lookup_pg_mapping(&osdmap->pg_temp, pgid); if (pg) { *num = pg->len; diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 44db78ae6a65..00ee068efc1c 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -149,8 +149,8 @@ bool skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow) if (poff >= 0) { __be32 *ports, _ports; - ports = skb_header_pointer(skb, nhoff + poff, - sizeof(_ports), &_ports); + nhoff += poff; + ports = skb_header_pointer(skb, nhoff, sizeof(_ports), &_ports); if (ports) flow->ports = *ports; } @@ -345,9 +345,14 @@ u16 __netdev_pick_tx(struct net_device *dev, struct sk_buff *skb) if (new_index < 0) new_index = skb_tx_hash(dev, skb); - if (queue_index != new_index && sk && - rcu_access_pointer(sk->sk_dst_cache)) - sk_tx_queue_set(sk, new_index); + if (queue_index != new_index && sk) { + struct dst_entry *dst = + rcu_dereference_check(sk->sk_dst_cache, 1); + + if (dst && skb_dst(skb) == dst) + sk_tx_queue_set(sk, queue_index); + + } queue_index = new_index; } diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 0034b611fa5e..ce90b0264db5 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1445,18 +1445,16 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev, atomic_set(&p->refcnt, 1); p->reachable_time = neigh_rand_reach_time(p->base_reachable_time); - dev_hold(dev); - p->dev = dev; - write_pnet(&p->net, hold_net(net)); - p->sysctl_table = NULL; if (ops->ndo_neigh_setup && ops->ndo_neigh_setup(dev, p)) { - release_net(net); - dev_put(dev); kfree(p); return NULL; } + dev_hold(dev); + p->dev = dev; + write_pnet(&p->net, hold_net(net)); + p->sysctl_table = NULL; write_lock_bh(&tbl->lock); p->next = tbl->parms.next; tbl->parms.next = p; diff --git a/net/core/netpoll.c b/net/core/netpoll.c index b04f73847eda..cec074be8c43 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -550,7 +550,7 @@ static void netpoll_neigh_reply(struct sk_buff *skb, struct netpoll_info *npinfo return; proto = ntohs(eth_hdr(skb)->h_proto); - if (proto == ETH_P_ARP) { + if (proto == ETH_P_IP) { struct arphdr *arp; unsigned char *arp_ptr; /* No arp on this interface */ @@ -1289,14 +1289,15 @@ EXPORT_SYMBOL_GPL(__netpoll_free_async); void netpoll_cleanup(struct netpoll *np) { - rtnl_lock(); if (!np->dev) - goto out; + return; + + rtnl_lock(); __netpoll_cleanup(np); + rtnl_unlock(); + dev_put(np->dev); np->dev = NULL; -out: - rtnl_unlock(); } EXPORT_SYMBOL(netpoll_cleanup); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index fd01eca52a13..a08bd2b7fe3f 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2142,7 +2142,7 @@ int ndo_dflt_fdb_del(struct ndmsg *ndm, /* If aging addresses are supported device will need to * implement its own handler for this. */ - if (!(ndm->ndm_state & NUD_PERMANENT)) { + if (ndm->ndm_state & NUD_PERMANENT) { pr_info("%s: FDB only supports static addresses\n", dev->name); return -EINVAL; } @@ -2374,7 +2374,7 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb) struct nlattr *extfilt; u32 filter_mask = 0; - extfilt = nlmsg_find_attr(cb->nlh, sizeof(struct ifinfomsg), + extfilt = nlmsg_find_attr(cb->nlh, sizeof(struct rtgenmsg), IFLA_EXT_MASK); if (extfilt) filter_mask = nla_get_u32(extfilt); diff --git a/net/core/scm.c b/net/core/scm.c index b4da80b1cc07..03795d0147f2 100644 --- a/net/core/scm.c +++ b/net/core/scm.c @@ -54,7 +54,7 @@ static __inline__ int scm_check_creds(struct ucred *creds) return -EINVAL; if ((creds->pid == task_tgid_vnr(current) || - ns_capable(task_active_pid_ns(current)->user_ns, CAP_SYS_ADMIN)) && + ns_capable(current->nsproxy->pid_ns->user_ns, CAP_SYS_ADMIN)) && ((uid_eq(uid, cred->uid) || uid_eq(uid, cred->euid) || uid_eq(uid, cred->suid)) || nsown_capable(CAP_SETUID)) && ((gid_eq(gid, cred->gid) || gid_eq(gid, cred->egid) || diff --git a/net/core/secure_seq.c b/net/core/secure_seq.c index 3f1ec1586ae1..6a2f13cee86a 100644 --- a/net/core/secure_seq.c +++ b/net/core/secure_seq.c @@ -10,24 +10,11 @@ #include -#define NET_SECRET_SIZE (MD5_MESSAGE_BYTES / 4) +static u32 net_secret[MD5_MESSAGE_BYTES / 4] ____cacheline_aligned; -static u32 net_secret[NET_SECRET_SIZE] ____cacheline_aligned; - -static void net_secret_init(void) +void net_secret_init(void) { - u32 tmp; - int i; - - if (likely(net_secret[0])) - return; - - for (i = NET_SECRET_SIZE; i > 0;) { - do { - get_random_bytes(&tmp, sizeof(tmp)); - } while (!tmp); - cmpxchg(&net_secret[--i], 0, tmp); - } + get_random_bytes(net_secret, sizeof(net_secret)); } #ifdef CONFIG_INET @@ -55,7 +42,6 @@ __u32 secure_tcpv6_sequence_number(const __be32 *saddr, const __be32 *daddr, u32 hash[MD5_DIGEST_WORDS]; u32 i; - net_secret_init(); memcpy(hash, saddr, 16); for (i = 0; i < 4; i++) secret[i] = net_secret[i] + (__force u32)daddr[i]; @@ -77,7 +63,6 @@ u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr, u32 hash[MD5_DIGEST_WORDS]; u32 i; - net_secret_init(); memcpy(hash, saddr, 16); for (i = 0; i < 4; i++) secret[i] = net_secret[i] + (__force u32) daddr[i]; @@ -97,7 +82,6 @@ __u32 secure_ip_id(__be32 daddr) { u32 hash[MD5_DIGEST_WORDS]; - net_secret_init(); hash[0] = (__force __u32) daddr; hash[1] = net_secret[13]; hash[2] = net_secret[14]; @@ -112,7 +96,6 @@ __u32 secure_ipv6_id(const __be32 daddr[4]) { __u32 hash[4]; - net_secret_init(); memcpy(hash, daddr, 16); md5_transform(hash, net_secret); @@ -124,7 +107,6 @@ __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, { u32 hash[MD5_DIGEST_WORDS]; - net_secret_init(); hash[0] = (__force u32)saddr; hash[1] = (__force u32)daddr; hash[2] = ((__force u16)sport << 16) + (__force u16)dport; @@ -139,7 +121,6 @@ u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport) { u32 hash[MD5_DIGEST_WORDS]; - net_secret_init(); hash[0] = (__force u32)saddr; hash[1] = (__force u32)daddr; hash[2] = (__force u32)dport ^ net_secret[14]; @@ -159,7 +140,6 @@ u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr, u32 hash[MD5_DIGEST_WORDS]; u64 seq; - net_secret_init(); hash[0] = (__force u32)saddr; hash[1] = (__force u32)daddr; hash[2] = ((__force u16)sport << 16) + (__force u16)dport; @@ -184,7 +164,6 @@ u64 secure_dccpv6_sequence_number(__be32 *saddr, __be32 *daddr, u64 seq; u32 i; - net_secret_init(); memcpy(hash, saddr, 16); for (i = 0; i < 4; i++) secret[i] = net_secret[i] + daddr[i]; diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index 2ff093b7c45e..cfdb46ab3a7f 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -20,9 +20,7 @@ #include #include -static int zero = 0; static int one = 1; -static int ushort_max = USHRT_MAX; #ifdef CONFIG_RPS static int rps_sock_flow_sysctl(ctl_table *table, int write, @@ -206,9 +204,7 @@ static struct ctl_table netns_core_table[] = { .data = &init_net.core.sysctl_somaxconn, .maxlen = sizeof(int), .mode = 0644, - .extra1 = &zero, - .extra2 = &ushort_max, - .proc_handler = proc_dointvec_minmax + .proc_handler = proc_dointvec }, { } }; diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 6cf9f7782ad4..9c61f9c02fdb 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -135,7 +135,6 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (dst) dst->ops->redirect(dst, sk, skb); - goto out; } if (type == ICMPV6_PKT_TOOBIG) { diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index c4adc319cc2e..d01be2a3ae53 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -263,8 +263,10 @@ void build_ehash_secret(void) get_random_bytes(&rnd, sizeof(rnd)); } while (rnd == 0); - if (cmpxchg(&inet_ehash_secret, 0, rnd) == 0) + if (cmpxchg(&inet_ehash_secret, 0, rnd) == 0) { get_random_bytes(&ipv6_hash_secret, sizeof(ipv6_hash_secret)); + net_secret_init(); + } } EXPORT_SYMBOL(build_ehash_secret); diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 791c19f6a497..247ec1951c35 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -912,14 +912,7 @@ static int arp_process(struct sk_buff *skb) agents are active. Taking the first reply prevents arp trashing and chooses the fastest router. */ - /* - * If n->updated is after jiffies, then the clock has wrapped and - * we are *well* past the locktime, so set the override flag - */ - if (time_after(n->updated, jiffies)) - override = 1; - else - override = time_after(jiffies, n->updated + n->parms->locktime); + override = time_after(jiffies, n->updated + n->parms->locktime); /* Broadcast replies and request packets do not assert neighbour reachability. diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 9e38217c3931..dfc39d4d48b7 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -771,7 +771,7 @@ static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh, ci = nla_data(tb[IFA_CACHEINFO]); if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) { err = -EINVAL; - goto errout_free; + goto errout; } *pvalid_lft = ci->ifa_valid; *pprefered_lft = ci->ifa_prefered; @@ -779,8 +779,6 @@ static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh, return ifa; -errout_free: - inet_free_ifa(ifa); errout: return ERR_PTR(err); } diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 6e8a13da6cbd..49616fed9340 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -71,6 +71,7 @@ #include #include #include +#include #include #include #include @@ -1760,8 +1761,10 @@ static struct leaf *leaf_walk_rcu(struct tnode *p, struct rt_trie_node *c) if (!c) continue; - if (IS_LEAF(c)) + if (IS_LEAF(c)) { + prefetch(rcu_dereference_rtnl(p->child[idx])); return (struct leaf *) c; + } /* Rescan start scanning in new node */ p = (struct tnode *) c; diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 089b4af4fecc..d8c232794bcb 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -343,7 +343,7 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) pip->saddr = fl4.saddr; pip->protocol = IPPROTO_IGMP; pip->tot_len = 0; /* filled in later */ - ip_select_ident(skb, &rt->dst, NULL); + ip_select_ident(pip, &rt->dst, NULL); ((u8 *)&pip[1])[0] = IPOPT_RA; ((u8 *)&pip[1])[1] = 4; ((u8 *)&pip[1])[2] = 0; @@ -687,7 +687,7 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc, iph->daddr = dst; iph->saddr = fl4.saddr; iph->protocol = IPPROTO_IGMP; - ip_select_ident(skb, &rt->dst, NULL); + ip_select_ident(iph, &rt->dst, NULL); ((u8 *)&iph[1])[0] = IPOPT_RA; ((u8 *)&iph[1])[1] = 4; ((u8 *)&iph[1])[2] = 0; @@ -709,7 +709,7 @@ static void igmp_gq_timer_expire(unsigned long data) in_dev->mr_gq_running = 0; igmpv3_send_report(in_dev, NULL); - in_dev_put(in_dev); + __in_dev_put(in_dev); } static void igmp_ifc_timer_expire(unsigned long data) @@ -721,7 +721,7 @@ static void igmp_ifc_timer_expire(unsigned long data) in_dev->mr_ifc_count--; igmp_ifc_start_timer(in_dev, IGMP_Unsolicited_Report_Interval); } - in_dev_put(in_dev); + __in_dev_put(in_dev); } static void igmp_ifc_event(struct in_device *in_dev) diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index 33d5537881ed..000e3d239d64 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -32,8 +32,8 @@ * At the moment of writing this notes identifier of IP packets is generated * to be unpredictable using this code only for packets subjected * (actually or potentially) to defragmentation. I.e. DF packets less than - * PMTU in size when local fragmentation is disabled use a constant ID and do - * not use this code (see ip_select_ident() in include/net/ip.h). + * PMTU in size uses a constant ID and do not use this code (see + * ip_select_ident() in include/net/ip.h). * * Route cache entries hold references to our nodes. * New cache entries get references via lookup by destination IP address in diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index c52fee0976da..855004f08325 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -572,7 +572,7 @@ static int ipgre_header(struct sk_buff *skb, struct net_device *dev, if (daddr) memcpy(&iph->daddr, daddr, 4); if (iph->daddr) - return t->hlen + sizeof(*iph); + return t->hlen; return -(t->hlen + sizeof(*iph)); } diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index ec2d430a6a55..4bcabf3ab4ca 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -148,7 +148,7 @@ int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk, iph->daddr = (opt && opt->opt.srr ? opt->opt.faddr : daddr); iph->saddr = saddr; iph->protocol = sk->sk_protocol; - ip_select_ident(skb, &rt->dst, sk); + ip_select_ident(iph, &rt->dst, sk); if (opt && opt->opt.optlen) { iph->ihl += opt->opt.optlen>>2; @@ -394,7 +394,7 @@ int ip_queue_xmit(struct sk_buff *skb, struct flowi *fl) ip_options_build(skb, &inet_opt->opt, inet->inet_daddr, rt, 0); } - ip_select_ident_more(skb, &rt->dst, sk, + ip_select_ident_more(iph, &rt->dst, sk, (skb_shinfo(skb)->gso_segs ?: 1) - 1); skb->priority = sk->sk_priority; @@ -1324,7 +1324,7 @@ struct sk_buff *__ip_make_skb(struct sock *sk, else ttl = ip_select_ttl(inet, &rt->dst); - iph = ip_hdr(skb); + iph = (struct iphdr *)skb->data; iph->version = 4; iph->ihl = 5; iph->tos = inet->tos; @@ -1332,7 +1332,7 @@ struct sk_buff *__ip_make_skb(struct sock *sk, iph->ttl = ttl; iph->protocol = sk->sk_protocol; ip_copy_addrs(iph, fl4); - ip_select_ident(skb, &rt->dst, sk); + ip_select_ident(iph, &rt->dst, sk); if (opt) { iph->ihl += opt->optlen>>2; diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 92d2f0f5d7bf..cbfc37f5f05a 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -659,13 +659,13 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, max_headroom = LL_RESERVED_SPACE(tdev) + sizeof(struct iphdr) + rt->dst.header_len; - if (max_headroom > dev->needed_headroom) + if (max_headroom > dev->needed_headroom) { dev->needed_headroom = max_headroom; - - if (skb_cow_head(skb, dev->needed_headroom)) { - dev->stats.tx_dropped++; - dev_kfree_skb(skb); - return; + if (skb_cow_head(skb, dev->needed_headroom)) { + dev->stats.tx_dropped++; + dev_kfree_skb(skb); + return; + } } skb_dst_drop(skb); @@ -686,7 +686,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, iph->daddr = fl4.daddr; iph->saddr = fl4.saddr; iph->ttl = ttl; - __ip_select_ident(iph, &rt->dst, (skb_shinfo(skb)->gso_segs ?: 1) - 1); + tunnel_ip_select_ident(skb, inner_iph, &rt->dst); iptunnel_xmit(skb, dev); return; diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 2c581da1fcab..9d9610ae7855 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -1658,7 +1658,7 @@ static void ip_encap(struct sk_buff *skb, __be32 saddr, __be32 daddr) iph->protocol = IPPROTO_IPIP; iph->ihl = 5; iph->tot_len = htons(skb->len); - ip_select_ident(skb, skb_dst(skb), NULL); + ip_select_ident(iph, skb_dst(skb), NULL); ip_send_check(iph); memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 6fb233772f79..dd44e0ab600c 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -387,7 +387,7 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4, iph->check = 0; iph->tot_len = htons(length); if (!iph->id) - ip_select_ident(skb, &rt->dst, NULL); + ip_select_ident(iph, &rt->dst, NULL); iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); } @@ -571,8 +571,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE, inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, - inet_sk_flowi_flags(sk) | FLOWI_FLAG_CAN_SLEEP | - (inet->hdrincl ? FLOWI_FLAG_KNOWN_NH : 0), + inet_sk_flowi_flags(sk) | FLOWI_FLAG_CAN_SLEEP, daddr, saddr, 0, 0); if (!inet->hdrincl) { diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 2b1b57f213b2..ab450c099aa4 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1117,13 +1117,6 @@ int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, if (!skb) goto wait_for_memory; - /* - * All packets are restored as if they have - * already been sent. - */ - if (tp->repair) - TCP_SKB_CB(skb)->when = tcp_time_stamp; - /* * Check whether we can use HW checksum. */ @@ -2447,11 +2440,10 @@ static int do_tcp_setsockopt(struct sock *sk, int level, case TCP_THIN_DUPACK: if (val < 0 || val > 1) err = -EINVAL; - else { + else tp->thin_dupack = val; if (tp->thin_dupack) tcp_disable_early_retrans(tp); - } break; case TCP_REPAIR: diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index b6ae92a51f58..a9077f441cb2 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -206,8 +206,8 @@ static u32 cubic_root(u64 a) */ static inline void bictcp_update(struct bictcp *ca, u32 cwnd) { - u32 delta, bic_target, max_cnt; - u64 offs, t; + u64 offs; + u32 delta, t, bic_target, max_cnt; ca->ack_cnt++; /* count the number of ACKs */ @@ -250,11 +250,9 @@ static inline void bictcp_update(struct bictcp *ca, u32 cwnd) * if the cwnd < 1 million packets !!! */ - t = (s32)(tcp_time_stamp - ca->epoch_start); - t += msecs_to_jiffies(ca->delay_min >> 3); /* change the unit from HZ to bictcp_HZ */ - t <<= BICTCP_HZ; - do_div(t, HZ); + t = ((tcp_time_stamp + msecs_to_jiffies(ca->delay_min>>3) + - ca->epoch_start) << BICTCP_HZ) / HZ; if (t < ca->bic_K) /* t - K */ offs = ca->bic_K - t; @@ -416,7 +414,7 @@ static void bictcp_acked(struct sock *sk, u32 cnt, s32 rtt_us) return; /* Discard delay samples right after fast recovery */ - if (ca->epoch_start && (s32)(tcp_time_stamp - ca->epoch_start) < HZ) + if ((s32)(tcp_time_stamp - ca->epoch_start) < HZ) return; delay = (rtt_us << 3) / USEC_PER_MSEC; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 4b75aad14b04..9c6225780bd5 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3598,10 +3598,7 @@ static bool tcp_parse_aligned_timestamp(struct tcp_sock *tp, const struct tcphdr ++ptr; tp->rx_opt.rcv_tsval = ntohl(*ptr); ++ptr; - if (*ptr) - tp->rx_opt.rcv_tsecr = ntohl(*ptr) - tp->tsoffset; - else - tp->rx_opt.rcv_tsecr = 0; + tp->rx_opt.rcv_tsecr = ntohl(*ptr) - tp->tsoffset; return true; } return false; @@ -3626,7 +3623,7 @@ static bool tcp_fast_parse_options(const struct sk_buff *skb, } tcp_parse_options(skb, &tp->rx_opt, 1, NULL); - if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr) + if (tp->rx_opt.saw_tstamp) tp->rx_opt.rcv_tsecr -= tp->tsoffset; return true; @@ -5379,7 +5376,7 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, int saved_clamp = tp->rx_opt.mss_clamp; tcp_parse_options(skb, &tp->rx_opt, 0, &foc); - if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr) + if (tp->rx_opt.saw_tstamp) tp->rx_opt.rcv_tsecr -= tp->tsoffset; if (th->ack) { diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 0145ce7e6098..ec335fabd5cc 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2664,7 +2664,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, int tcp_header_size; int mss; - skb = sock_wmalloc(sk, MAX_TCP_HEADER + 15, 1, GFP_ATOMIC); + skb = alloc_skb(MAX_TCP_HEADER + 15, sk_gfp_atomic(sk, GFP_ATOMIC)); if (unlikely(!skb)) { dst_release(dst); return NULL; @@ -2808,8 +2808,6 @@ void tcp_connect_init(struct sock *sk) if (likely(!tp->repair)) tp->rcv_nxt = 0; - else - tp->rcv_tstamp = tcp_time_stamp; tp->rcv_wup = tp->rcv_nxt; tp->copied_seq = tp->rcv_nxt; diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c index b5663c37f089..eb1dd4d643f2 100644 --- a/net/ipv4/xfrm4_mode_tunnel.c +++ b/net/ipv4/xfrm4_mode_tunnel.c @@ -117,7 +117,7 @@ static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb) top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ? 0 : (XFRM_MODE_SKB_CB(skb)->frag_off & htons(IP_DF)); - ip_select_ident(skb, dst->child, NULL); + ip_select_ident(top_iph, dst->child, NULL); top_iph->ttl = ip4_dst_hoplimit(dst->child); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index b78a3ee93d52..fb8c94c4ab86 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -1124,10 +1124,12 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i if (ifp->flags & IFA_F_OPTIMISTIC) addr_flags |= IFA_F_OPTIMISTIC; - ift = ipv6_add_addr(idev, &addr, tmp_plen, - ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, - addr_flags); - if (IS_ERR(ift)) { + ift = !max_addresses || + ipv6_count_addresses(idev) < max_addresses ? + ipv6_add_addr(idev, &addr, tmp_plen, + ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, + addr_flags) : NULL; + if (IS_ERR_OR_NULL(ift)) { in6_ifa_put(ifp); in6_dev_put(idev); pr_info("%s: retry temporary address regeneration\n", __func__); @@ -1533,33 +1535,6 @@ static bool ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr, return false; } -/* Compares an address/prefix_len with addresses on device @dev. - * If one is found it returns true. - */ -bool ipv6_chk_custom_prefix(const struct in6_addr *addr, - const unsigned int prefix_len, struct net_device *dev) -{ - struct inet6_dev *idev; - struct inet6_ifaddr *ifa; - bool ret = false; - - rcu_read_lock(); - idev = __in6_dev_get(dev); - if (idev) { - read_lock_bh(&idev->lock); - list_for_each_entry(ifa, &idev->addr_list, if_list) { - ret = ipv6_prefix_equal(addr, &ifa->addr, prefix_len); - if (ret) - break; - } - read_unlock_bh(&idev->lock); - } - rcu_read_unlock(); - - return ret; -} -EXPORT_SYMBOL(ipv6_chk_custom_prefix); - int ipv6_chk_prefix(const struct in6_addr *addr, struct net_device *dev) { struct inet6_dev *idev; diff --git a/net/ipv6/addrlabel.c b/net/ipv6/addrlabel.c index b30ad3741b46..f083a583a05c 100644 --- a/net/ipv6/addrlabel.c +++ b/net/ipv6/addrlabel.c @@ -251,36 +251,38 @@ static struct ip6addrlbl_entry *ip6addrlbl_alloc(struct net *net, /* add a label */ static int __ip6addrlbl_add(struct ip6addrlbl_entry *newp, int replace) { - struct hlist_node *n; - struct ip6addrlbl_entry *last = NULL, *p = NULL; int ret = 0; - ADDRLABEL(KERN_DEBUG "%s(newp=%p, replace=%d)\n", __func__, newp, - replace); + ADDRLABEL(KERN_DEBUG "%s(newp=%p, replace=%d)\n", + __func__, + newp, replace); - hlist_for_each_entry_safe(p, n, &ip6addrlbl_table.head, list) { - if (p->prefixlen == newp->prefixlen && - net_eq(ip6addrlbl_net(p), ip6addrlbl_net(newp)) && - p->ifindex == newp->ifindex && - ipv6_addr_equal(&p->prefix, &newp->prefix)) { - if (!replace) { - ret = -EEXIST; + if (hlist_empty(&ip6addrlbl_table.head)) { + hlist_add_head_rcu(&newp->list, &ip6addrlbl_table.head); + } else { + struct hlist_node *n; + struct ip6addrlbl_entry *p = NULL; + hlist_for_each_entry_safe(p, n, + &ip6addrlbl_table.head, list) { + if (p->prefixlen == newp->prefixlen && + net_eq(ip6addrlbl_net(p), ip6addrlbl_net(newp)) && + p->ifindex == newp->ifindex && + ipv6_addr_equal(&p->prefix, &newp->prefix)) { + if (!replace) { + ret = -EEXIST; + goto out; + } + hlist_replace_rcu(&p->list, &newp->list); + ip6addrlbl_put(p); + goto out; + } else if ((p->prefixlen == newp->prefixlen && !p->ifindex) || + (p->prefixlen < newp->prefixlen)) { + hlist_add_before_rcu(&newp->list, &p->list); goto out; } - hlist_replace_rcu(&p->list, &newp->list); - ip6addrlbl_put(p); - goto out; - } else if ((p->prefixlen == newp->prefixlen && !p->ifindex) || - (p->prefixlen < newp->prefixlen)) { - hlist_add_before_rcu(&newp->list, &p->list); - goto out; } - last = p; + hlist_add_after_rcu(&p->list, &newp->list); } - if (last) - hlist_add_after_rcu(&last->list, &newp->list); - else - hlist_add_head_rcu(&newp->list, &ip6addrlbl_table.head); out: if (!ret) ip6addrlbl_table.seq++; diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 8d67900aa003..07a7d65a7cb6 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -162,6 +162,12 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb) off += optlen; len -= optlen; } + /* This case will not be caught by above check since its padding + * length is smaller than 7: + * 1 byte NH + 1 byte Length + 6 bytes Padding + */ + if ((padlen == 6) && ((off - skb_network_header_len(skb)) == 8)) + goto bad; if (len == 0) return true; diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 70e704d49007..b4ff0a42b8c7 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -931,14 +931,6 @@ static const struct icmp6_err { .err = ECONNREFUSED, .fatal = 1, }, - { /* POLICY_FAIL */ - .err = EACCES, - .fatal = 1, - }, - { /* REJECT_ROUTE */ - .err = EACCES, - .fatal = 1, - }, }; int icmpv6_err_convert(u8 type, u8 code, int *err) @@ -950,7 +942,7 @@ int icmpv6_err_convert(u8 type, u8 code, int *err) switch (type) { case ICMPV6_DEST_UNREACH: fatal = 1; - if (code < ARRAY_SIZE(tab_unreach)) { + if (code <= ICMPV6_PORT_UNREACH) { *err = tab_unreach[code].err; fatal = tab_unreach[code].fatal; } diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 9c06ecb6556e..5fc9c7a68d8d 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -825,9 +825,9 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info) fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr), rt->rt6i_dst.plen, offsetof(struct rt6_info, rt6i_dst), allow_create, replace_required); + if (IS_ERR(fn)) { err = PTR_ERR(fn); - fn = NULL; goto out; } @@ -993,22 +993,14 @@ static struct fib6_node * fib6_lookup_1(struct fib6_node *root, if (ipv6_prefix_equal(&key->addr, args->addr, key->plen)) { #ifdef CONFIG_IPV6_SUBTREES - if (fn->subtree) { - struct fib6_node *sfn; - sfn = fib6_lookup_1(fn->subtree, - args + 1); - if (!sfn) - goto backtrack; - fn = sfn; - } + if (fn->subtree) + fn = fib6_lookup_1(fn->subtree, args + 1); #endif - if (fn->fn_flags & RTN_RTINFO) + if (!fn || fn->fn_flags & RTN_RTINFO) return fn; } } -#ifdef CONFIG_IPV6_SUBTREES -backtrack: -#endif + if (fn->fn_flags & RTN_ROOT) break; diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 1f9a1a5b61f4..ecd60733e5e2 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -620,7 +620,7 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb, struct ip6_tnl *tunnel = netdev_priv(dev); struct net_device *tdev; /* Device to other host */ struct ipv6hdr *ipv6h; /* Our new IP header */ - unsigned int max_headroom = 0; /* The extra header space needed */ + unsigned int max_headroom; /* The extra header space needed */ int gre_hlen; struct ipv6_tel_txoption opt; int mtu; @@ -693,7 +693,7 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb, tunnel->err_count = 0; } - max_headroom += LL_RESERVED_SPACE(tdev) + gre_hlen + dst->header_len; + max_headroom = LL_RESERVED_SPACE(tdev) + gre_hlen + dst->header_len; if (skb_headroom(skb) < max_headroom || skb_shared(skb) || (skb_cloned(skb) && !skb_clone_writable(skb, 0))) { diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 44ffdb99a62e..6e3ddf806ec2 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1039,8 +1039,6 @@ static inline int ip6_ufo_append_data(struct sock *sk, * udp datagram */ if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) { - struct frag_hdr fhdr; - skb = sock_alloc_send_skb(sk, hh_len + fragheaderlen + transhdrlen + 20, (flags & MSG_DONTWAIT), &err); @@ -1061,6 +1059,12 @@ static inline int ip6_ufo_append_data(struct sock *sk, skb->ip_summed = CHECKSUM_PARTIAL; skb->csum = 0; + } + + err = skb_append_datato_frags(sk,skb, getfrag, from, + (length - transhdrlen)); + if (!err) { + struct frag_hdr fhdr; /* Specify the length of each IPv6 datagram fragment. * It has to be a multiple of 8. @@ -1071,10 +1075,15 @@ static inline int ip6_ufo_append_data(struct sock *sk, ipv6_select_ident(&fhdr, rt); skb_shinfo(skb)->ip6_frag_id = fhdr.identification; __skb_queue_tail(&sk->sk_write_queue, skb); + + return 0; } + /* There is not enough support do UPD LSO, + * so follow normal path + */ + kfree_skb(skb); - return skb_append_datato_frags(sk, skb, getfrag, from, - (length - transhdrlen)); + return err; } static inline struct ipv6_opt_hdr *ip6_opt_dup(struct ipv6_opt_hdr *src, @@ -1241,27 +1250,27 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, * --yoshfuji */ - if ((length > mtu) && dontfrag && (sk->sk_protocol == IPPROTO_UDP || - sk->sk_protocol == IPPROTO_RAW)) { - ipv6_local_rxpmtu(sk, fl6, mtu-exthdrlen); - return -EMSGSIZE; - } - - skb = skb_peek_tail(&sk->sk_write_queue); cork->length += length; - if (((length > mtu) || - (skb && skb_is_gso(skb))) && - (sk->sk_protocol == IPPROTO_UDP) && - (rt->dst.dev->features & NETIF_F_UFO)) { - err = ip6_ufo_append_data(sk, getfrag, from, length, - hh_len, fragheaderlen, - transhdrlen, mtu, flags, rt); - if (err) - goto error; - return 0; + if (length > mtu) { + int proto = sk->sk_protocol; + if (dontfrag && (proto == IPPROTO_UDP || proto == IPPROTO_RAW)){ + ipv6_local_rxpmtu(sk, fl6, mtu-exthdrlen); + return -EMSGSIZE; + } + + if (proto == IPPROTO_UDP && + (rt->dst.dev->features & NETIF_F_UFO)) { + + err = ip6_ufo_append_data(sk, getfrag, from, length, + hh_len, fragheaderlen, + transhdrlen, mtu, flags, rt); + if (err) + goto error; + return 0; + } } - if (!skb) + if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) goto alloc_new_skb; while (length > 0) { diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 0516ebbea80b..1e55866cead7 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1646,9 +1646,9 @@ static int ip6_tnl_fill_info(struct sk_buff *skb, const struct net_device *dev) if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) || nla_put(skb, IFLA_IPTUN_LOCAL, sizeof(struct in6_addr), - &parm->laddr) || - nla_put(skb, IFLA_IPTUN_REMOTE, sizeof(struct in6_addr), &parm->raddr) || + nla_put(skb, IFLA_IPTUN_REMOTE, sizeof(struct in6_addr), + &parm->laddr) || nla_put_u8(skb, IFLA_IPTUN_TTL, parm->hop_limit) || nla_put_u8(skb, IFLA_IPTUN_ENCAP_LIMIT, parm->encap_limit) || nla_put_be32(skb, IFLA_IPTUN_FLOWINFO, parm->flowinfo) || @@ -1732,7 +1732,6 @@ static int __net_init ip6_tnl_init_net(struct net *net) if (!ip6n->fb_tnl_dev) goto err_alloc_dev; dev_net_set(ip6n->fb_tnl_dev, net); - ip6n->fb_tnl_dev->rtnl_link_ops = &ip6_link_ops; err = ip6_fb_tnl_dev_init(ip6n->fb_tnl_dev); if (err < 0) diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 952eaed38808..c3998c2bbc5a 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -2158,7 +2158,7 @@ static void mld_gq_timer_expire(unsigned long data) idev->mc_gq_running = 0; mld_send_report(idev, NULL); - in6_dev_put(idev); + __in6_dev_put(idev); } static void mld_ifc_timer_expire(unsigned long data) @@ -2171,7 +2171,7 @@ static void mld_ifc_timer_expire(unsigned long data) if (idev->mc_ifc_count) mld_ifc_start_timer(idev, idev->mc_maxdelay); } - in6_dev_put(idev); + __in6_dev_put(idev); } static void mld_ifc_event(struct inet6_dev *idev) diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 060a0449acaa..ca4ffcc287f1 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -372,11 +372,14 @@ static struct sk_buff *ndisc_alloc_skb(struct net_device *dev, int tlen = dev->needed_tailroom; struct sock *sk = dev_net(dev)->ipv6.ndisc_sk; struct sk_buff *skb; + int err; - skb = alloc_skb(hlen + sizeof(struct ipv6hdr) + len + tlen, GFP_ATOMIC); + skb = sock_alloc_send_skb(sk, + hlen + sizeof(struct ipv6hdr) + len + tlen, + 1, &err); if (!skb) { - ND_PRINTK(0, err, "ndisc: %s failed to allocate an skb\n", - __func__); + ND_PRINTK(0, err, "ndisc: %s failed to allocate an skb, err=%d\n", + __func__, err); return NULL; } @@ -386,11 +389,6 @@ static struct sk_buff *ndisc_alloc_skb(struct net_device *dev, skb_reserve(skb, hlen + sizeof(struct ipv6hdr)); skb_reset_transport_header(skb); - /* Manually assign socket ownership as we avoid calling - * sock_alloc_send_pskb() to bypass wmem buffer limits - */ - skb_set_owner_w(skb, sk); - return skb; } diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 1aeb473b2cc6..790d9f4b8b0b 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -490,7 +490,6 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev, ipv6_hdr(head)->payload_len = htons(payload_len); ipv6_change_dsfield(ipv6_hdr(head), 0xff, ecn); IP6CB(head)->nhoff = nhoff; - IP6CB(head)->flags |= IP6SKB_FRAGMENTED; /* Yes, and fold redundant checksum back. 8) */ if (head->ip_summed == CHECKSUM_COMPLETE) @@ -525,9 +524,6 @@ static int ipv6_frag_rcv(struct sk_buff *skb) struct net *net = dev_net(skb_dst(skb)->dev); int evicted; - if (IP6CB(skb)->flags & IP6SKB_FRAGMENTED) - goto fail_hdr; - IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_REASMREQDS); /* Jumbo payload inhibits frag. header */ @@ -548,7 +544,6 @@ static int ipv6_frag_rcv(struct sk_buff *skb) ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_REASMOKS); IP6CB(skb)->nhoff = (u8 *)fhdr - skb_network_header(skb); - IP6CB(skb)->flags |= IP6SKB_FRAGMENTED; return 1; } diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 0491264b8bfc..60df36d15390 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -566,70 +566,6 @@ static inline bool is_spoofed_6rd(struct ip_tunnel *tunnel, const __be32 v4addr, return false; } -/* Checks if an address matches an address on the tunnel interface. - * Used to detect the NAT of proto 41 packets and let them pass spoofing test. - * Long story: - * This function is called after we considered the packet as spoofed - * in is_spoofed_6rd. - * We may have a router that is doing NAT for proto 41 packets - * for an internal station. Destination a.a.a.a/PREFIX:bbbb:bbbb - * will be translated to n.n.n.n/PREFIX:bbbb:bbbb. And is_spoofed_6rd - * function will return true, dropping the packet. - * But, we can still check if is spoofed against the IP - * addresses associated with the interface. - */ -static bool only_dnatted(const struct ip_tunnel *tunnel, - const struct in6_addr *v6dst) -{ - int prefix_len; - -#ifdef CONFIG_IPV6_SIT_6RD - prefix_len = tunnel->ip6rd.prefixlen + 32 - - tunnel->ip6rd.relay_prefixlen; -#else - prefix_len = 48; -#endif - return ipv6_chk_custom_prefix(v6dst, prefix_len, tunnel->dev); -} - -/* Returns true if a packet is spoofed */ -static bool packet_is_spoofed(struct sk_buff *skb, - const struct iphdr *iph, - struct ip_tunnel *tunnel) -{ - const struct ipv6hdr *ipv6h; - - if (tunnel->dev->priv_flags & IFF_ISATAP) { - if (!isatap_chksrc(skb, iph, tunnel)) - return true; - - return false; - } - - if (tunnel->dev->flags & IFF_POINTOPOINT) - return false; - - ipv6h = ipv6_hdr(skb); - - if (unlikely(is_spoofed_6rd(tunnel, iph->saddr, &ipv6h->saddr))) { - net_warn_ratelimited("Src spoofed %pI4/%pI6c -> %pI4/%pI6c\n", - &iph->saddr, &ipv6h->saddr, - &iph->daddr, &ipv6h->daddr); - return true; - } - - if (likely(!is_spoofed_6rd(tunnel, iph->daddr, &ipv6h->daddr))) - return false; - - if (only_dnatted(tunnel, &ipv6h->daddr)) - return false; - - net_warn_ratelimited("Dst spoofed %pI4/%pI6c -> %pI4/%pI6c\n", - &iph->saddr, &ipv6h->saddr, - &iph->daddr, &ipv6h->daddr); - return true; -} - static int ipip6_rcv(struct sk_buff *skb) { const struct iphdr *iph = ip_hdr(skb); @@ -648,9 +584,19 @@ static int ipip6_rcv(struct sk_buff *skb) skb->protocol = htons(ETH_P_IPV6); skb->pkt_type = PACKET_HOST; - if (packet_is_spoofed(skb, iph, tunnel)) { - tunnel->dev->stats.rx_errors++; - goto out; + if (tunnel->dev->priv_flags & IFF_ISATAP) { + if (!isatap_chksrc(skb, iph, tunnel)) { + tunnel->dev->stats.rx_errors++; + goto out; + } + } else if (!(tunnel->dev->flags&IFF_POINTOPOINT)) { + if (is_spoofed_6rd(tunnel, iph->saddr, + &ipv6_hdr(skb)->saddr) || + is_spoofed_6rd(tunnel, iph->daddr, + &ipv6_hdr(skb)->daddr)) { + tunnel->dev->stats.rx_errors++; + goto out; + } } __skb_tunnel_rx(skb, tunnel->dev); @@ -767,7 +713,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, neigh = dst_neigh_lookup(skb_dst(skb), &iph6->daddr); if (neigh == NULL) { - net_dbg_ratelimited("nexthop == NULL\n"); + net_dbg_ratelimited("sit: nexthop == NULL\n"); goto tx_error; } @@ -796,7 +742,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, neigh = dst_neigh_lookup(skb_dst(skb), &iph6->daddr); if (neigh == NULL) { - net_dbg_ratelimited("nexthop == NULL\n"); + net_dbg_ratelimited("sit: nexthop == NULL\n"); goto tx_error; } @@ -919,7 +865,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, iph->ttl = iph6->hop_limit; skb->ip_summed = CHECKSUM_NONE; - ip_select_ident(skb, skb_dst(skb), NULL); + ip_select_ident(iph, skb_dst(skb), NULL); iptunnel_xmit(skb, dev); return NETDEV_TX_OK; @@ -1561,7 +1507,6 @@ static int __net_init sit_init_net(struct net *net) goto err_alloc_dev; } dev_net_set(sitn->fb_tunnel_dev, net); - sitn->fb_tunnel_dev->rtnl_link_ops = &sit_link_ops; err = ipip6_fb_tunnel_init(sitn->fb_tunnel_dev); if (err) diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 66c718854e5a..0a17ed9eaf39 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1426,7 +1426,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) if (np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) np->mcast_hops = ipv6_hdr(opt_skb)->hop_limit; if (np->rxopt.bits.rxtclass) - np->rcv_tclass = ipv6_get_dsfield(ipv6_hdr(opt_skb)); + np->rcv_tclass = ipv6_get_dsfield(ipv6_hdr(skb)); if (ipv6_opt_accepted(sk, opt_skb)) { skb_set_owner_r(opt_skb, sk); opt_skb = xchg(&np->pktoptions, opt_skb); diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 3052672e37f7..170f9a7fa319 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -1166,7 +1166,6 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_IBSS); - ieee80211_vif_release_channel(sdata); synchronize_rcu(); kfree(presp); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 5b4328dcbe4e..55a42f9c4f39 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -31,12 +31,10 @@ #include "led.h" #define IEEE80211_AUTH_TIMEOUT (HZ / 5) -#define IEEE80211_AUTH_TIMEOUT_LONG (HZ / 2) #define IEEE80211_AUTH_TIMEOUT_SHORT (HZ / 10) #define IEEE80211_AUTH_MAX_TRIES 3 #define IEEE80211_AUTH_WAIT_ASSOC (HZ * 5) #define IEEE80211_ASSOC_TIMEOUT (HZ / 5) -#define IEEE80211_ASSOC_TIMEOUT_LONG (HZ / 2) #define IEEE80211_ASSOC_TIMEOUT_SHORT (HZ / 10) #define IEEE80211_ASSOC_MAX_TRIES 3 @@ -3472,13 +3470,10 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) if (tx_flags == 0) { auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; - auth_data->timeout_started = true; + ifmgd->auth_data->timeout_started = true; run_again(ifmgd, auth_data->timeout); } else { - auth_data->timeout = - round_jiffies_up(jiffies + IEEE80211_AUTH_TIMEOUT_LONG); - auth_data->timeout_started = true; - run_again(ifmgd, auth_data->timeout); + auth_data->timeout_started = false; } return 0; @@ -3515,11 +3510,7 @@ static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata) assoc_data->timeout_started = true; run_again(&sdata->u.mgd, assoc_data->timeout); } else { - assoc_data->timeout = - round_jiffies_up(jiffies + - IEEE80211_ASSOC_TIMEOUT_LONG); - assoc_data->timeout_started = true; - run_again(&sdata->u.mgd, assoc_data->timeout); + assoc_data->timeout_started = false; } return 0; diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index f3bbea1eb9e7..f5aed963b22e 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -828,9 +828,6 @@ minstrel_ht_update_cck(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, if (sband->band != IEEE80211_BAND_2GHZ) return; - if (!(mp->hw->flags & IEEE80211_HW_SUPPORTS_HT_CCK_RATES)) - return; - mi->cck_supported = 0; mi->cck_supported_short = 0; for (i = 0; i < 4; i++) { diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 707bc520d629..57beb1762b2d 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -325,22 +325,18 @@ mtype_add_cidr(struct htype *h, u8 cidr, u8 nets_length) static void mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length) { - u8 i, j, net_end = nets_length - 1; - - for (i = 0; i < nets_length; i++) { - if (h->nets[i].cidr != cidr) - continue; - if (h->nets[i].nets > 1 || i == net_end || - h->nets[i + 1].nets == 0) { - h->nets[i].nets--; - return; - } - for (j = i; j < net_end && h->nets[j].nets; j++) { - h->nets[j].cidr = h->nets[j + 1].cidr; - h->nets[j].nets = h->nets[j + 1].nets; - } - h->nets[j].nets = 0; - return; + u8 i, j; + + for (i = 0; i < nets_length - 1 && h->nets[i].cidr != cidr; i++) + ; + h->nets[i].nets--; + + if (h->nets[i].nets != 0) + return; + + for (j = i; j < nets_length - 1 && h->nets[j].nets; j++) { + h->nets[j].cidr = h->nets[j + 1].cidr; + h->nets[j].nets = h->nets[j + 1].nets; } } #endif diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index c47444e4cf8c..b75ff6429a04 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -883,7 +883,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, iph->daddr = cp->daddr.ip; iph->saddr = saddr; iph->ttl = old_iph->ttl; - ip_select_ident(skb, &rt->dst, NULL); + ip_select_ident(iph, &rt->dst, NULL); /* Another hack: avoid icmp_send in ip_fragment */ skb->local_df = 1; diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 393f17eea1a2..1076fe16b122 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -364,7 +364,7 @@ int genl_unregister_ops(struct genl_family *family, struct genl_ops *ops) EXPORT_SYMBOL(genl_unregister_ops); /** - * __genl_register_family - register a generic netlink family + * genl_register_family - register a generic netlink family * @family: generic netlink family * * Registers the specified family after validating it first. Only one @@ -374,7 +374,7 @@ EXPORT_SYMBOL(genl_unregister_ops); * * Return 0 on success or a negative error code. */ -int __genl_register_family(struct genl_family *family) +int genl_register_family(struct genl_family *family) { int err = -EINVAL; @@ -430,10 +430,10 @@ int __genl_register_family(struct genl_family *family) errout: return err; } -EXPORT_SYMBOL(__genl_register_family); +EXPORT_SYMBOL(genl_register_family); /** - * __genl_register_family_with_ops - register a generic netlink family + * genl_register_family_with_ops - register a generic netlink family * @family: generic netlink family * @ops: operations to be registered * @n_ops: number of elements to register @@ -457,12 +457,12 @@ EXPORT_SYMBOL(__genl_register_family); * * Return 0 on success or a negative error code. */ -int __genl_register_family_with_ops(struct genl_family *family, +int genl_register_family_with_ops(struct genl_family *family, struct genl_ops *ops, size_t n_ops) { int err, i; - err = __genl_register_family(family); + err = genl_register_family(family); if (err) return err; @@ -476,7 +476,7 @@ int __genl_register_family_with_ops(struct genl_family *family, genl_unregister_family(family); return err; } -EXPORT_SYMBOL(__genl_register_family_with_ops); +EXPORT_SYMBOL(genl_register_family_with_ops); /** * genl_unregister_family - unregister generic netlink family @@ -544,30 +544,6 @@ void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, } EXPORT_SYMBOL(genlmsg_put); -static int genl_lock_dumpit(struct sk_buff *skb, struct netlink_callback *cb) -{ - struct genl_ops *ops = cb->data; - int rc; - - genl_lock(); - rc = ops->dumpit(skb, cb); - genl_unlock(); - return rc; -} - -static int genl_lock_done(struct netlink_callback *cb) -{ - struct genl_ops *ops = cb->data; - int rc = 0; - - if (ops->done) { - genl_lock(); - rc = ops->done(cb); - genl_unlock(); - } - return rc; -} - static int genl_family_rcv_msg(struct genl_family *family, struct sk_buff *skb, struct nlmsghdr *nlh) @@ -596,34 +572,15 @@ static int genl_family_rcv_msg(struct genl_family *family, return -EPERM; if (nlh->nlmsg_flags & NLM_F_DUMP) { - int rc; + struct netlink_dump_control c = { + .dump = ops->dumpit, + .done = ops->done, + }; if (ops->dumpit == NULL) return -EOPNOTSUPP; - if (!family->parallel_ops) { - struct netlink_dump_control c = { - .module = family->module, - .data = ops, - .dump = genl_lock_dumpit, - .done = genl_lock_done, - }; - - genl_unlock(); - rc = __netlink_dump_start(net->genl_sock, skb, nlh, &c); - genl_lock(); - - } else { - struct netlink_dump_control c = { - .module = family->module, - .dump = ops->dumpit, - .done = ops->done, - }; - - rc = __netlink_dump_start(net->genl_sock, skb, nlh, &c); - } - - return rc; + return netlink_dump_start(net->genl_sock, skb, nlh, &c); } if (ops->doit == NULL) diff --git a/net/nfc/llcp.h b/net/nfc/llcp.h index f924dd209b31..ff8c434f7df8 100644 --- a/net/nfc/llcp.h +++ b/net/nfc/llcp.h @@ -19,7 +19,6 @@ enum llcp_state { LLCP_CONNECTED = 1, /* wait_for_packet() wants that */ - LLCP_CONNECTING, LLCP_CLOSED, LLCP_BOUND, LLCP_LISTEN, diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c index 7522c3708723..380253eccb74 100644 --- a/net/nfc/llcp_sock.c +++ b/net/nfc/llcp_sock.c @@ -571,7 +571,7 @@ static unsigned int llcp_sock_poll(struct file *file, struct socket *sock, if (sk->sk_shutdown == SHUTDOWN_MASK) mask |= POLLHUP; - if (sock_writeable(sk) && sk->sk_state == LLCP_CONNECTED) + if (sock_writeable(sk)) mask |= POLLOUT | POLLWRNORM | POLLWRBAND; else set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); @@ -722,16 +722,14 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr *_addr, if (ret) goto sock_unlink; - sk->sk_state = LLCP_CONNECTING; - ret = sock_wait_state(sk, LLCP_CONNECTED, sock_sndtimeo(sk, flags & O_NONBLOCK)); - if (ret && ret != -EINPROGRESS) + if (ret) goto sock_unlink; release_sock(sk); - return ret; + return 0; sock_unlink: nfc_llcp_put_ssap(local, llcp_sock->ssap); diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index a6895ab597c2..20a1bd0e6549 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -3259,11 +3259,9 @@ static int packet_getsockopt(struct socket *sock, int level, int optname, if (po->tp_version == TPACKET_V3) { lv = sizeof(struct tpacket_stats_v3); - st.stats3.tp_packets += st.stats3.tp_drops; data = &st.stats3; } else { lv = sizeof(struct tpacket_stats); - st.stats1.tp_packets += st.stats1.tp_drops; data = &st.stats1; } diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 51b968d3febb..281c1bded1f6 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -285,45 +285,6 @@ static struct Qdisc_ops *qdisc_lookup_ops(struct nlattr *kind) return q; } -/* The linklayer setting were not transferred from iproute2, in older - * versions, and the rate tables lookup systems have been dropped in - * the kernel. To keep backward compatible with older iproute2 tc - * utils, we detect the linklayer setting by detecting if the rate - * table were modified. - * - * For linklayer ATM table entries, the rate table will be aligned to - * 48 bytes, thus some table entries will contain the same value. The - * mpu (min packet unit) is also encoded into the old rate table, thus - * starting from the mpu, we find low and high table entries for - * mapping this cell. If these entries contain the same value, when - * the rate tables have been modified for linklayer ATM. - * - * This is done by rounding mpu to the nearest 48 bytes cell/entry, - * and then roundup to the next cell, calc the table entry one below, - * and compare. - */ -static __u8 __detect_linklayer(struct tc_ratespec *r, __u32 *rtab) -{ - int low = roundup(r->mpu, 48); - int high = roundup(low+1, 48); - int cell_low = low >> r->cell_log; - int cell_high = (high >> r->cell_log) - 1; - - /* rtab is too inaccurate at rates > 100Mbit/s */ - if ((r->rate > (100000000/8)) || (rtab[0] == 0)) { - pr_debug("TC linklayer: Giving up ATM detection\n"); - return TC_LINKLAYER_ETHERNET; - } - - if ((cell_high > cell_low) && (cell_high < 256) - && (rtab[cell_low] == rtab[cell_high])) { - pr_debug("TC linklayer: Detected ATM, low(%d)=high(%d)=%u\n", - cell_low, cell_high, rtab[cell_high]); - return TC_LINKLAYER_ATM; - } - return TC_LINKLAYER_ETHERNET; -} - static struct qdisc_rate_table *qdisc_rtab_list; struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r, struct nlattr *tab) @@ -347,8 +308,6 @@ struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r, struct nlattr *ta rtab->rate = *r; rtab->refcnt = 1; memcpy(rtab->data, nla_data(tab), 1024); - if (r->linklayer == TC_LINKLAYER_UNAWARE) - r->linklayer = __detect_linklayer(r, rtab->data); rtab->next = qdisc_rtab_list; qdisc_rtab_list = rtab; } diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index a7f838b45dc8..20224086cc28 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -908,7 +908,6 @@ void psched_ratecfg_precompute(struct psched_ratecfg *r, memset(r, 0, sizeof(*r)); r->overhead = conf->overhead; r->rate_bps = (u64)conf->rate << 3; - r->linklayer = (conf->linklayer & TC_LINKLAYER_MASK); r->mult = 1; /* * Calibrate mult, shift so that token counting is accurate diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index e09b074bb8a7..adaedd79389c 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -87,7 +87,7 @@ struct htb_class { unsigned int children; struct htb_class *parent; /* parent class */ - u32 prio; /* these two are used only by leaves... */ + int prio; /* these two are used only by leaves... */ int quantum; /* but stored for parent-to-leaf return */ union { @@ -1312,7 +1312,6 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, struct htb_sched *q = qdisc_priv(sch); struct htb_class *cl = (struct htb_class *)*arg, *parent; struct nlattr *opt = tca[TCA_OPTIONS]; - struct qdisc_rate_table *rtab = NULL, *ctab = NULL; struct nlattr *tb[TCA_HTB_MAX + 1]; struct tc_htb_opt *hopt; @@ -1334,18 +1333,6 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, if (!hopt->rate.rate || !hopt->ceil.rate) goto failure; - /* Keeping backward compatible with rate_table based iproute2 tc */ - if (hopt->rate.linklayer == TC_LINKLAYER_UNAWARE) { - rtab = qdisc_get_rtab(&hopt->rate, tb[TCA_HTB_RTAB]); - if (rtab) - qdisc_put_rtab(rtab); - } - if (hopt->ceil.linklayer == TC_LINKLAYER_UNAWARE) { - ctab = qdisc_get_rtab(&hopt->ceil, tb[TCA_HTB_CTAB]); - if (ctab) - qdisc_put_rtab(ctab); - } - if (!cl) { /* new class */ struct Qdisc *new_q; int prio; @@ -1476,7 +1463,7 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, psched_ratecfg_precompute(&cl->ceil, &hopt->ceil); cl->buffer = PSCHED_TICKS2NS(hopt->buffer); - cl->cbuffer = PSCHED_TICKS2NS(hopt->cbuffer); + cl->cbuffer = PSCHED_TICKS2NS(hopt->buffer); sch_tree_unlock(sch); diff --git a/net/sctp/input.c b/net/sctp/input.c index bd4fb459c63b..4b2c83146aa7 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -648,7 +648,8 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info) break; case ICMP_REDIRECT: sctp_icmp_redirect(sk, transport, skb); - /* Fall through to out_unlock. */ + err = 0; + break; default: goto out_unlock; } diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 422d8bdacc0d..391a245d5203 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -189,7 +189,7 @@ SCTP_STATIC void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, break; case NDISC_REDIRECT: sctp_icmp_redirect(sk, transport, skb); - goto out_unlock; + break; default: break; } @@ -210,24 +210,45 @@ SCTP_STATIC void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, in6_dev_put(idev); } +/* Based on tcp_v6_xmit() in tcp_ipv6.c. */ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport) { struct sock *sk = skb->sk; struct ipv6_pinfo *np = inet6_sk(sk); - struct flowi6 *fl6 = &transport->fl.u.ip6; + struct flowi6 fl6; + + memset(&fl6, 0, sizeof(fl6)); + + fl6.flowi6_proto = sk->sk_protocol; + + /* Fill in the dest address from the route entry passed with the skb + * and the source address from the transport. + */ + fl6.daddr = transport->ipaddr.v6.sin6_addr; + fl6.saddr = transport->saddr.v6.sin6_addr; + + fl6.flowlabel = np->flow_label; + IP6_ECN_flow_xmit(sk, fl6.flowlabel); + if (ipv6_addr_type(&fl6.saddr) & IPV6_ADDR_LINKLOCAL) + fl6.flowi6_oif = transport->saddr.v6.sin6_scope_id; + else + fl6.flowi6_oif = sk->sk_bound_dev_if; + + if (np->opt && np->opt->srcrt) { + struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt; + fl6.daddr = *rt0->addr; + } SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, src:%pI6 dst:%pI6\n", __func__, skb, skb->len, - &fl6->saddr, &fl6->daddr); + &fl6.saddr, &fl6.daddr); - IP6_ECN_flow_xmit(sk, fl6->flowlabel); + SCTP_INC_STATS(sock_net(sk), SCTP_MIB_OUTSCTPPACKS); if (!(transport->param_flags & SPP_PMTUD_ENABLE)) skb->local_df = 1; - SCTP_INC_STATS(sock_net(sk), SCTP_MIB_OUTSCTPPACKS); - - return ip6_xmit(sk, skb, fl6, np->opt, np->tclass); + return ip6_xmit(sk, skb, &fl6, np->opt, np->tclass); } /* Returns the dst cache entry for the given source and destination ip @@ -240,12 +261,10 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, struct dst_entry *dst = NULL; struct flowi6 *fl6 = &fl->u.ip6; struct sctp_bind_addr *bp; - struct ipv6_pinfo *np = inet6_sk(sk); struct sctp_sockaddr_entry *laddr; union sctp_addr *baddr = NULL; union sctp_addr *daddr = &t->ipaddr; union sctp_addr dst_saddr; - struct in6_addr *final_p, final; __u8 matchlen = 0; __u8 bmatchlen; sctp_scope_t scope; @@ -268,8 +287,7 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, SCTP_DEBUG_PRINTK("SRC=%pI6 - ", &fl6->saddr); } - final_p = fl6_update_dst(fl6, np->opt, &final); - dst = ip6_dst_lookup_flow(sk, fl6, final_p, false); + dst = ip6_dst_lookup_flow(sk, fl6, NULL, false); if (!asoc || saddr) goto out; @@ -321,12 +339,10 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, } } rcu_read_unlock(); - if (baddr) { fl6->saddr = baddr->v6.sin6_addr; fl6->fl6_sport = baddr->v6.sin6_port; - final_p = fl6_update_dst(fl6, np->opt, &final); - dst = ip6_dst_lookup_flow(sk, fl6, final_p, false); + dst = ip6_dst_lookup_flow(sk, fl6, NULL, false); } out: diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 79bc251042ba..6abb1caf9836 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -820,9 +820,6 @@ static int sctp_send_asconf_del_ip(struct sock *sk, goto skip_mkasconf; } - if (laddr == NULL) - return -EINVAL; - /* We do not need RCU protection throughout this loop * because this is done under a socket lock from the * setsockopt call. @@ -6196,7 +6193,7 @@ unsigned int sctp_poll(struct file *file, struct socket *sock, poll_table *wait) /* Is there any exceptional events? */ if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue)) mask |= POLLERR | - (sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? POLLPRI : 0); + sock_flag(sk, SOCK_SELECT_ERR_QUEUE) ? POLLPRI : 0; if (sk->sk_shutdown & RCV_SHUTDOWN) mask |= POLLRDHUP | POLLIN | POLLRDNORM; if (sk->sk_shutdown == SHUTDOWN_MASK) diff --git a/net/sunrpc/auth_gss/gss_rpc_upcall.c b/net/sunrpc/auth_gss/gss_rpc_upcall.c index f1eb0d16666c..af7ffd447fee 100644 --- a/net/sunrpc/auth_gss/gss_rpc_upcall.c +++ b/net/sunrpc/auth_gss/gss_rpc_upcall.c @@ -213,26 +213,6 @@ static int gssp_call(struct net *net, struct rpc_message *msg) return status; } -static void gssp_free_receive_pages(struct gssx_arg_accept_sec_context *arg) -{ - int i; - - for (i = 0; i < arg->npages && arg->pages[i]; i++) - __free_page(arg->pages[i]); -} - -static int gssp_alloc_receive_pages(struct gssx_arg_accept_sec_context *arg) -{ - arg->npages = DIV_ROUND_UP(NGROUPS_MAX * 4, PAGE_SIZE); - arg->pages = kzalloc(arg->npages * sizeof(struct page *), GFP_KERNEL); - /* - * XXX: actual pages are allocated by xdr layer in - * xdr_partial_copy_from_skb. - */ - if (!arg->pages) - return -ENOMEM; - return 0; -} /* * Public functions @@ -281,16 +261,10 @@ int gssp_accept_sec_context_upcall(struct net *net, arg.context_handle = &ctxh; res.output_token->len = GSSX_max_output_token_sz; - ret = gssp_alloc_receive_pages(&arg); - if (ret) - return ret; - /* use nfs/ for targ_name ? */ ret = gssp_call(net, &msg); - gssp_free_receive_pages(&arg); - /* we need to fetch all data even in case of error so * that we can free special strctures is they have been allocated */ data->major_status = res.status.major_status; diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.c b/net/sunrpc/auth_gss/gss_rpc_xdr.c index f0f78c5f1c7d..3c85d1c8a028 100644 --- a/net/sunrpc/auth_gss/gss_rpc_xdr.c +++ b/net/sunrpc/auth_gss/gss_rpc_xdr.c @@ -166,15 +166,14 @@ static int dummy_dec_opt_array(struct xdr_stream *xdr, return 0; } -static int get_host_u32(struct xdr_stream *xdr, u32 *res) +static int get_s32(void **p, void *max, s32 *res) { - __be32 *p; - - p = xdr_inline_decode(xdr, 4); - if (!p) + void *base = *p; + void *next = (void *)((char *)base + sizeof(s32)); + if (unlikely(next > max || next < base)) return -EINVAL; - /* Contents of linux creds are all host-endian: */ - memcpy(res, p, sizeof(u32)); + memcpy(res, base, sizeof(s32)); + *p = next; return 0; } @@ -183,9 +182,9 @@ static int gssx_dec_linux_creds(struct xdr_stream *xdr, { u32 length; __be32 *p; - u32 tmp; - u32 N; - int i, err; + void *q, *end; + s32 tmp; + int N, i, err; p = xdr_inline_decode(xdr, 4); if (unlikely(p == NULL)) @@ -193,28 +192,33 @@ static int gssx_dec_linux_creds(struct xdr_stream *xdr, length = be32_to_cpup(p); - if (length > (3 + NGROUPS_MAX) * sizeof(u32)) + /* FIXME: we do not want to use the scratch buffer for this one + * may need to use functions that allows us to access an io vector + * directly */ + p = xdr_inline_decode(xdr, length); + if (unlikely(p == NULL)) return -ENOSPC; + q = p; + end = q + length; + /* uid */ - err = get_host_u32(xdr, &tmp); + err = get_s32(&q, end, &tmp); if (err) return err; creds->cr_uid = make_kuid(&init_user_ns, tmp); /* gid */ - err = get_host_u32(xdr, &tmp); + err = get_s32(&q, end, &tmp); if (err) return err; creds->cr_gid = make_kgid(&init_user_ns, tmp); /* number of additional gid's */ - err = get_host_u32(xdr, &tmp); + err = get_s32(&q, end, &tmp); if (err) return err; N = tmp; - if ((3 + N) * sizeof(u32) != length) - return -EINVAL; creds->cr_group_info = groups_alloc(N); if (creds->cr_group_info == NULL) return -ENOMEM; @@ -222,7 +226,7 @@ static int gssx_dec_linux_creds(struct xdr_stream *xdr, /* gid's */ for (i = 0; i < N; i++) { kgid_t kgid; - err = get_host_u32(xdr, &tmp); + err = get_s32(&q, end, &tmp); if (err) goto out_free_groups; err = -EINVAL; @@ -780,9 +784,6 @@ void gssx_enc_accept_sec_context(struct rpc_rqst *req, /* arg->options */ err = dummy_enc_opt_array(xdr, &arg->options); - xdr_inline_pages(&req->rq_rcv_buf, - PAGE_SIZE/2 /* pretty arbitrary */, - arg->pages, 0 /* page base */, arg->npages * PAGE_SIZE); done: if (err) dprintk("RPC: gssx_enc_accept_sec_context: %d\n", err); diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.h b/net/sunrpc/auth_gss/gss_rpc_xdr.h index 685a688f3d8a..1c98b27d870c 100644 --- a/net/sunrpc/auth_gss/gss_rpc_xdr.h +++ b/net/sunrpc/auth_gss/gss_rpc_xdr.h @@ -147,8 +147,6 @@ struct gssx_arg_accept_sec_context { struct gssx_cb *input_cb; u32 ret_deleg_cred; struct gssx_option_array options; - struct page **pages; - unsigned int npages; }; struct gssx_res_accept_sec_context { @@ -242,8 +240,7 @@ int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp, 2 * GSSX_max_princ_sz + \ 8 + 8 + 4 + 4 + 4) #define GSSX_max_output_token_sz 1024 -/* grouplist not included; we allocate separate pages for that: */ -#define GSSX_max_creds_sz (4 + 4 + 4 /* + NGROUPS_MAX*4 */) +#define GSSX_max_creds_sz (4 + 4 + 4 + NGROUPS_MAX * 4) #define GSSX_RES_accept_sec_context_sz (GSSX_default_status_sz + \ GSSX_default_ctx_sz + \ GSSX_max_output_token_sz + \ diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 1504bb11e4f3..75edcfad6e26 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -207,13 +207,10 @@ _shift_data_right_pages(struct page **pages, size_t pgto_base, pgfrom_base -= copy; vto = kmap_atomic(*pgto); - if (*pgto != *pgfrom) { - vfrom = kmap_atomic(*pgfrom); - memcpy(vto + pgto_base, vfrom + pgfrom_base, copy); - kunmap_atomic(vfrom); - } else - memmove(vto + pgto_base, vto + pgfrom_base, copy); + vfrom = kmap_atomic(*pgfrom); + memmove(vto + pgto_base, vfrom + pgfrom_base, copy); flush_dcache_page(*pgto); + kunmap_atomic(vfrom); kunmap_atomic(vto); } while ((len -= copy) != 0); diff --git a/net/sysctl_net.c b/net/sysctl_net.c index e7000be321b0..9bc6db04be3e 100644 --- a/net/sysctl_net.c +++ b/net/sysctl_net.c @@ -47,12 +47,12 @@ static int net_ctl_permissions(struct ctl_table_header *head, /* Allow network administrator to have same access as root. */ if (ns_capable(net->user_ns, CAP_NET_ADMIN) || - uid_eq(root_uid, current_euid())) { + uid_eq(root_uid, current_uid())) { int mode = (table->mode >> 6) & 7; return (mode << 6) | (mode << 3) | mode; } /* Allow netns root group to have the same access as the root group */ - if (in_egroup_p(root_gid)) { + if (gid_eq(root_gid, current_gid())) { int mode = (table->mode >> 3) & 7; return (mode << 3) | mode; } diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 7e26ad416af1..515ce38e4f4c 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -1179,7 +1179,7 @@ static u32 filter_connect(struct tipc_sock *tsock, struct sk_buff **buf) /* Accept only ACK or NACK message */ if (unlikely(msg_errcode(msg))) { sock->state = SS_DISCONNECTING; - sk->sk_err = ECONNREFUSED; + sk->sk_err = -ECONNREFUSED; retval = TIPC_OK; break; } @@ -1190,7 +1190,7 @@ static u32 filter_connect(struct tipc_sock *tsock, struct sk_buff **buf) res = auto_connect(sock, msg); if (res) { sock->state = SS_DISCONNECTING; - sk->sk_err = -res; + sk->sk_err = res; retval = TIPC_OK; break; } diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index bea100b245c8..fd3f0180e08f 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -525,7 +525,7 @@ static void get_prompt_str(struct gstr *r, struct property *prop, { int i, j; struct menu *submenu[8], *menu, *location = NULL; - struct jump_key *jump = NULL; + struct jump_key *jump; str_printf(r, _("Prompt: %s\n"), _(prop->text)); menu = prop->menu->parent; diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index b4c1161a373b..a4be8e112bb6 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1705,7 +1705,7 @@ static void read_symbols(char *modname) char *version; char *license; struct module *mod; - struct elf_info info = { .hdr = NULL }; + struct elf_info info = { }; Elf_Sym *sym; if (!parse_elf(&info, modname)) @@ -2118,7 +2118,7 @@ static int dump_sym(struct symbol *sym) static void write_dump(const char *fname) { - struct buffer buf = { NULL, 0, 0 }; + struct buffer buf = { }; struct symbol *symbol; int n; @@ -2144,7 +2144,7 @@ struct ext_sym_list { int main(int argc, char **argv) { struct module *mod; - struct buffer buf = { NULL, 0, 0 }; + struct buffer buf = { }; char *kernel_read = NULL, *module_read = NULL; char *dump_write = NULL, *files_source = NULL; int opt; diff --git a/security/commoncap.c b/security/commoncap.c index d78b003296a5..c44b6fe6648e 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -988,11 +988,9 @@ int cap_mmap_addr(unsigned long addr) } return ret; } -EXPORT_SYMBOL(cap_mmap_addr); int cap_mmap_file(struct file *file, unsigned long reqprot, unsigned long prot, unsigned long flags) { return 0; } -EXPORT_SYMBOL(cap_mmap_file); diff --git a/security/device_cgroup.c b/security/device_cgroup.c index 9760ecb6b052..dd0dc574d78d 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -790,7 +789,6 @@ int __devcgroup_inode_permission(struct inode *inode, int mask) return __devcgroup_check_permission(type, imajor(inode), iminor(inode), access); } -EXPORT_SYMBOL(__devcgroup_inode_permission); int devcgroup_inode_mknod(int mode, dev_t dev) { diff --git a/security/security.c b/security/security.c index 06a6ea69199b..a3dce87d1aef 100644 --- a/security/security.c +++ b/security/security.c @@ -396,7 +396,6 @@ int security_path_rmdir(struct path *dir, struct dentry *dentry) return 0; return security_ops->path_rmdir(dir, dentry); } -EXPORT_SYMBOL(security_path_rmdir); int security_path_unlink(struct path *dir, struct dentry *dentry) { @@ -413,7 +412,6 @@ int security_path_symlink(struct path *dir, struct dentry *dentry, return 0; return security_ops->path_symlink(dir, dentry, old_name); } -EXPORT_SYMBOL(security_path_symlink); int security_path_link(struct dentry *old_dentry, struct path *new_dir, struct dentry *new_dentry) @@ -422,7 +420,6 @@ int security_path_link(struct dentry *old_dentry, struct path *new_dir, return 0; return security_ops->path_link(old_dentry, new_dir, new_dentry); } -EXPORT_SYMBOL(security_path_link); int security_path_rename(struct path *old_dir, struct dentry *old_dentry, struct path *new_dir, struct dentry *new_dentry) @@ -441,7 +438,6 @@ int security_path_truncate(struct path *path) return 0; return security_ops->path_truncate(path); } -EXPORT_SYMBOL(security_path_truncate); int security_path_chmod(struct path *path, umode_t mode) { @@ -449,7 +445,6 @@ int security_path_chmod(struct path *path, umode_t mode) return 0; return security_ops->path_chmod(path, mode); } -EXPORT_SYMBOL(security_path_chmod); int security_path_chown(struct path *path, kuid_t uid, kgid_t gid) { @@ -457,7 +452,6 @@ int security_path_chown(struct path *path, kuid_t uid, kgid_t gid) return 0; return security_ops->path_chown(path, uid, gid); } -EXPORT_SYMBOL(security_path_chown); int security_path_chroot(struct path *path) { @@ -534,7 +528,6 @@ int security_inode_readlink(struct dentry *dentry) return 0; return security_ops->inode_readlink(dentry); } -EXPORT_SYMBOL(security_inode_readlink); int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd) { @@ -549,7 +542,6 @@ int security_inode_permission(struct inode *inode, int mask) return 0; return security_ops->inode_permission(inode, mask); } -EXPORT_SYMBOL(security_inode_permission); int security_inode_setattr(struct dentry *dentry, struct iattr *attr) { @@ -671,7 +663,6 @@ int security_file_permission(struct file *file, int mask) return fsnotify_perm(file, mask); } -EXPORT_SYMBOL(security_file_permission); int security_file_alloc(struct file *file) { @@ -732,7 +723,6 @@ int security_mmap_file(struct file *file, unsigned long prot, return ret; return ima_file_mmap(file, prot); } -EXPORT_SYMBOL(security_mmap_file); int security_mmap_addr(unsigned long addr) { diff --git a/sound/core/Kconfig b/sound/core/Kconfig index b413ed05e74d..27a27bd9e454 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -15,6 +15,9 @@ config SND_RAWMIDI config SND_COMPRESS_OFFLOAD tristate +config SND_EFFECTS_OFFLOAD + tristate + # To be effective this also requires INPUT - users should say: # select SND_JACK if INPUT=y || INPUT=SND # to avoid having to force INPUT on. diff --git a/sound/core/Makefile b/sound/core/Makefile index 43d4117428ac..9e37a2bff743 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -23,6 +23,7 @@ snd-rtctimer-objs := rtctimer.o snd-hwdep-objs := hwdep.o snd-compress-objs := compress_offload.o +snd-effects-objs := effects_offload.o obj-$(CONFIG_SND) += snd.o obj-$(CONFIG_SND_HWDEP) += snd-hwdep.o @@ -36,3 +37,4 @@ obj-$(CONFIG_SND_OSSEMUL) += oss/ obj-$(CONFIG_SND_SEQUENCER) += seq/ obj-$(CONFIG_SND_COMPRESS_OFFLOAD) += snd-compress.o +obj-$(CONFIG_SND_EFFECTS_OFFLOAD) += snd-effects.o diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 5863ba6dd12b..f22605763a3e 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -123,6 +123,7 @@ static int snd_compr_open(struct inode *inode, struct file *f) } runtime->state = SNDRV_PCM_STATE_OPEN; init_waitqueue_head(&runtime->sleep); + init_waitqueue_head(&runtime->wait); data->stream.runtime = runtime; f->private_data = (void *)data; mutex_lock(&compr->lock); @@ -139,6 +140,18 @@ static int snd_compr_open(struct inode *inode, struct file *f) 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; + + switch (runtime->state) { + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_DRAINING: + case SNDRV_PCM_STATE_PAUSED: + data->stream.ops->trigger(&data->stream, SNDRV_PCM_TRIGGER_STOP); + break; + default: + break; + } + data->stream.ops->free(&data->stream); kfree(data->stream.runtime->buffer); kfree(data->stream.runtime); @@ -256,16 +269,25 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf, struct snd_compr_file *data = f->private_data; struct snd_compr_stream *stream; size_t avail; - int retval; + int retval = 0; if (snd_BUG_ON(!data)) return -EFAULT; stream = &data->stream; mutex_lock(&stream->device->lock); - /* write is allowed when stream is running or has been steup */ + /* + * if the stream is in paused state, return the + * number of bytes consumed as 0 + */ + if (stream->runtime->state == SNDRV_PCM_STATE_PAUSED) { + mutex_unlock(&stream->device->lock); + return retval; + } + /* write is allowed when stream is running or prepared or in setup */ if (stream->runtime->state != SNDRV_PCM_STATE_SETUP && - stream->runtime->state != SNDRV_PCM_STATE_RUNNING) { + stream->runtime->state != SNDRV_PCM_STATE_RUNNING && + stream->runtime->state != SNDRV_PCM_STATE_PREPARED) { mutex_unlock(&stream->device->lock); return -EBADFD; } @@ -372,8 +394,7 @@ static unsigned int snd_compr_poll(struct file *f, poll_table *wait) return -EFAULT; mutex_lock(&stream->device->lock); - if (stream->runtime->state == SNDRV_PCM_STATE_PAUSED || - stream->runtime->state == SNDRV_PCM_STATE_OPEN) { + if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) { retval = -EBADFD; goto out; } @@ -397,6 +418,7 @@ static unsigned int snd_compr_poll(struct file *f, poll_table *wait) retval = snd_compr_get_poll(stream); break; default: + pr_err("poll returns err!...\n"); if (stream->direction == SND_COMPRESS_PLAYBACK) retval = POLLOUT | POLLWRNORM | POLLERR; else @@ -627,7 +649,8 @@ static int snd_compr_pause(struct snd_compr_stream *stream) { int retval; - if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) + if ((stream->runtime->state != SNDRV_PCM_STATE_RUNNING) && + (stream->runtime->state != SNDRV_PCM_STATE_DRAINING)) return -EPERM; retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH); if (!retval) @@ -642,8 +665,10 @@ static int snd_compr_resume(struct snd_compr_stream *stream) if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED) return -EPERM; retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE); - if (!retval) + if (!retval) { stream->runtime->state = SNDRV_PCM_STATE_RUNNING; + wake_up(&stream->runtime->sleep); + } return retval; } @@ -659,35 +684,71 @@ static int snd_compr_start(struct snd_compr_stream *stream) return retval; } -static int snd_compr_stop(struct snd_compr_stream *stream) +int snd_compr_stop(struct snd_compr_stream *stream) { - int retval; + int retval = 0; - if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || - stream->runtime->state == SNDRV_PCM_STATE_SETUP) + if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) return -EPERM; - retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); + if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED) + retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); if (!retval) { stream->runtime->state = SNDRV_PCM_STATE_SETUP; wake_up(&stream->runtime->sleep); + snd_compr_drain_notify(stream); stream->runtime->total_bytes_available = 0; stream->runtime->total_bytes_transferred = 0; } return retval; } +EXPORT_SYMBOL(snd_compr_stop); + +static int snd_compress_wait_for_drain(struct snd_compr_stream *stream) +{ + /* + * We are called with lock held. So drop the lock while we wait for + * drain complete notfication from the driver + * + * It is expected that driver will notify the drain completion and then + * stream will be moved to SETUP state, even if draining resulted in an + * error. We can trigger next track after this. + */ + stream->runtime->state = SNDRV_PCM_STATE_DRAINING; + mutex_unlock(&stream->device->lock); + + wait_event(stream->runtime->wait, stream->runtime->drain_wake); + + wake_up(&stream->runtime->sleep); + mutex_lock(&stream->device->lock); + + return 0; +} static int snd_compr_drain(struct snd_compr_stream *stream) { - int retval; + int retval = 0; - if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || - stream->runtime->state == SNDRV_PCM_STATE_SETUP) + if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) return -EPERM; - retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN); - if (!retval) { - stream->runtime->state = SNDRV_PCM_STATE_DRAINING; + + stream->runtime->drain_wake = 0; + + /* this is hackish for our tree but for now lets carry it while we fix + * usermode behaviour + */ + if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED) + retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN); + else + return 0; + + if (retval) { + pr_err("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval); wake_up(&stream->runtime->sleep); + return retval; } + + retval = snd_compress_wait_for_drain(stream); + stream->runtime->state = SNDRV_PCM_STATE_SETUP; return retval; } @@ -715,17 +776,30 @@ static int snd_compr_next_track(struct snd_compr_stream *stream) static int snd_compr_partial_drain(struct snd_compr_stream *stream) { - int retval; - if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || - stream->runtime->state == SNDRV_PCM_STATE_SETUP) + int retval = 0; + + /* agaain hackish changes */ + if (stream->runtime->state == SNDRV_PCM_STATE_SETUP) return -EPERM; /* stream can be drained only when next track has been signalled */ if (stream->next_track == false) return -EPERM; - retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN); + stream->runtime->drain_wake = 0; + if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED) + retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN); + else + return 0; + + if (retval) { + pr_err("Partial drain returned failure\n"); + wake_up(&stream->runtime->sleep); + return retval; + } stream->next_track = false; + retval = snd_compress_wait_for_drain(stream); + stream->runtime->state = SNDRV_PCM_STATE_SETUP; return retval; } @@ -804,6 +878,9 @@ static const struct file_operations snd_compr_file_ops = { .write = snd_compr_write, .read = snd_compr_read, .unlocked_ioctl = snd_compr_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = snd_compr_ioctl, +#endif .mmap = snd_compr_mmap, .poll = snd_compr_poll, }; @@ -818,7 +895,7 @@ static int snd_compress_dev_register(struct snd_device *device) return -EBADFD; compr = device->device_data; - sprintf(str, "comprC%iD%i", compr->card->number, compr->device); + snprintf(str, sizeof(str), "comprC%iD%i", compr->card->number, compr->device); pr_debug("reg %s for device %s, direction %d\n", str, compr->name, compr->direction); /* register compressed device */ @@ -837,8 +914,7 @@ static int snd_compress_dev_disconnect(struct snd_device *device) struct snd_compr *compr; compr = device->device_data; - snd_unregister_device(SNDRV_DEVICE_TYPE_COMPRESS, compr->card, - compr->device); + snd_unregister_device(compr->direction, compr->card, compr->device); return 0; } diff --git a/sound/core/effects_offload.c b/sound/core/effects_offload.c new file mode 100644 index 000000000000..def0a8906a46 --- /dev/null +++ b/sound/core/effects_offload.c @@ -0,0 +1,307 @@ +/* + * effect_offload.c - effects offload core + * + * Copyright (C) 2013 Intel Corporation + * Authors: Lakshmi N Vinnakota + * Vinod Koul + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ +#define FORMAT(fmt) "%s: %d: " fmt, __func__, __LINE__ +#define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt) + +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_MUTEX(effect_mutex); + +int snd_ctl_effect_create(struct snd_card *card, void *arg) +{ + int retval = 0; + struct snd_effect *effect; + + effect = kmalloc(sizeof(*effect), GFP_KERNEL); + if (!effect) + return -ENOMEM; + if (copy_from_user(effect, (void __user *)arg, sizeof(*effect))) { + retval = -EFAULT; + goto out; + } + pr_debug("effect_offload: device %d, pos %d, mode%d\n", + effect->device, effect->pos, effect->mode); + + mutex_lock(&card->effect_lock); + retval = card->effect_ops->create(card, effect); + mutex_unlock(&card->effect_lock); +out: + kfree(effect); + return retval; +} +EXPORT_SYMBOL_GPL(snd_ctl_effect_create); + +int snd_ctl_effect_destroy(struct snd_card *card, void *arg) +{ + int retval = 0; + struct snd_effect *effect; + + effect = kmalloc(sizeof(*effect), GFP_KERNEL); + if (!effect) + return -ENOMEM; + if (copy_from_user(effect, (void __user *)arg, sizeof(*effect))) { + retval = -EFAULT; + goto out; + } + mutex_lock(&card->effect_lock); + retval = card->effect_ops->destroy(card, effect); + mutex_unlock(&card->effect_lock); +out: + kfree(effect); + return retval; +} +EXPORT_SYMBOL_GPL(snd_ctl_effect_destroy); + +int snd_ctl_effect_set_params(struct snd_card *card, void *arg) +{ + int retval = 0; + struct snd_effect_params *params; + char __user *argp = (char __user *)arg; + char __user *bufp; + + params = kmalloc(sizeof(*params), GFP_KERNEL); + if (!params) + return -ENOMEM; + + if (copy_from_user(params, argp, sizeof(*params))) { + retval = -EFAULT; + goto out; + } + bufp = params->buffer; + params->buffer = kmalloc(params->size, GFP_KERNEL); + if (!params->buffer) { + retval = -ENOMEM; + goto out; + } + + if (copy_from_user((void *)params->buffer, bufp, params->size)) { + retval = -EFAULT; + goto free_buf; + } + + mutex_lock(&card->effect_lock); + retval = card->effect_ops->set_params(card, params); + mutex_unlock(&card->effect_lock); +free_buf: + kfree(params->buffer); +out: + kfree(params); + return retval; +} +EXPORT_SYMBOL_GPL(snd_ctl_effect_set_params); + +int snd_ctl_effect_get_params(struct snd_card *card, void *arg) +{ + int retval = 0; + struct snd_effect_params inparams; + struct snd_effect_params *outparams; + unsigned int offset; + char __user *argp = (char __user *)arg; + + if (copy_from_user((void *)&inparams, argp, sizeof(inparams))) + retval = -EFAULT; + + outparams = kmalloc(sizeof(*outparams), GFP_KERNEL); + if (!outparams) + return -ENOMEM; + + memcpy(outparams, &inparams, sizeof(inparams)); + outparams->buffer = kmalloc(inparams.size, GFP_KERNEL); + if (!outparams->buffer) { + retval = -ENOMEM; + goto free_out; + } + + if (copy_from_user((void *)outparams->buffer, inparams.buffer, + inparams.size)) { + retval = -EFAULT; + goto free_buf; + } + + mutex_lock(&card->effect_lock); + retval = card->effect_ops->get_params(card, outparams); + mutex_unlock(&card->effect_lock); + + if (retval) + goto free_buf; + + if (!outparams->size) + goto free_buf; + + if (outparams->size > inparams.size) { + pr_err("mem insufficient to copy\n"); + retval = -EMSGSIZE; + goto free_buf; + } else { + offset = offsetof(struct snd_effect_params, size); + if (copy_to_user((argp + offset), (void *)&outparams->size, + sizeof(u32))) + retval = -EFAULT; + + if (copy_to_user(inparams.buffer, outparams->buffer, + outparams->size)) + retval = -EFAULT; + } +free_buf: + kfree(outparams->buffer); +free_out: + kfree(outparams); + return retval; +} +EXPORT_SYMBOL_GPL(snd_ctl_effect_get_params); + +int snd_ctl_effect_query_num_effects(struct snd_card *card, void *arg) +{ + int retval = 0; + int __user *ip = arg; + + mutex_lock(&card->effect_lock); + retval = card->effect_ops->query_num_effects(card); + mutex_unlock(&card->effect_lock); + + if (retval < 0) + goto out; + retval = put_user(retval, ip) ? -EFAULT : 0; +out: + return retval; +} +EXPORT_SYMBOL_GPL(snd_ctl_effect_query_num_effects); + +int snd_ctl_effect_query_effect_caps(struct snd_card *card, void *arg) +{ + int retval = 0; + struct snd_effect_caps *caps; + unsigned int offset, insize; + char __user *argp = (char __user *)arg; + char __user *bufp; + + caps = kmalloc(sizeof(*caps), GFP_KERNEL); + if (!caps) + return -ENOMEM; + + if (copy_from_user(caps, argp, sizeof(*caps))) { + retval = -EFAULT; + goto out; + } + bufp = caps->buffer; + insize = caps->size; + caps->buffer = kmalloc(caps->size, GFP_KERNEL); + if (!caps->buffer) { + retval = -ENOMEM; + goto out; + } + + mutex_lock(&card->effect_lock); + retval = card->effect_ops->query_effect_caps(card, caps); + mutex_unlock(&card->effect_lock); + + if (retval) + goto free_buf; + + if (insize < caps->size) { + pr_err("mem insufficient to copy\n"); + retval = -EMSGSIZE; + goto free_buf; + } + + offset = offsetof(struct snd_effect_caps, size); + if (copy_to_user((argp + offset), (void *)&caps->size, sizeof(u32))) { + retval = -EFAULT; + goto free_buf; + } + + if (copy_to_user(bufp, caps->buffer, caps->size)) + retval = -EFAULT; + +free_buf: + kfree(caps->buffer); +out: + kfree(caps); + return retval; +} +EXPORT_SYMBOL_GPL(snd_ctl_effect_query_effect_caps); + +/** + * snd_effect_register - register compressed device + * + * @card : snd card to which the effect is registered + * @ops : effect_ops to register + */ +int snd_effect_register(struct snd_card *card, struct snd_effect_ops *ops) +{ + + if (card == NULL || ops == NULL) + return -EINVAL; + + if (snd_BUG_ON(!ops->create)) + return -EINVAL; + if (snd_BUG_ON(!ops->destroy)) + return -EINVAL; + if (snd_BUG_ON(!ops->set_params)) + return -EINVAL; + + mutex_init(&card->effect_lock); + + pr_debug("Registering Effects to card %s\n", card->shortname); + /* register the effect ops with the card */ + mutex_lock(&effect_mutex); + card->effect_ops = ops; + mutex_unlock(&effect_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(snd_effect_register); + +int snd_effect_deregister(struct snd_card *card) +{ + pr_debug("Removing effects for card %s\n", card->shortname); + mutex_lock(&effect_mutex); + card->effect_ops = NULL; + mutex_unlock(&effect_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(snd_effect_deregister); + +static int __init snd_effect_init(void) +{ + return 0; +} + +static void __exit snd_effect_exit(void) +{ +} + +module_init(snd_effect_init); +module_exit(snd_effect_exit); + +MODULE_DESCRIPTION("ALSA Effect offload framework"); +MODULE_AUTHOR("Lakshmi N Vinnakota "); +MODULE_AUTHOR("Vinod Koul "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c index e427dbf76368..b41ed8661b23 100644 --- a/sound/isa/opti9xx/opti92x-ad1848.c +++ b/sound/isa/opti9xx/opti92x-ad1848.c @@ -173,7 +173,11 @@ MODULE_DEVICE_TABLE(pnp_card, snd_opti9xx_pnpids); #endif /* CONFIG_PNP */ -#define DEV_NAME KBUILD_MODNAME +#ifdef OPTi93X +#define DEV_NAME "opti93x" +#else +#define DEV_NAME "opti92x" +#endif static char * snd_opti9xx_names[] = { "unknown", @@ -1164,7 +1168,7 @@ static int snd_opti9xx_pnp_resume(struct pnp_card_link *pcard) static struct pnp_card_driver opti9xx_pnpc_driver = { .flags = PNP_DRIVER_RES_DISABLE, - .name = DEV_NAME, + .name = "opti9xx", .id_table = snd_opti9xx_pnpids, .probe = snd_opti9xx_pnp_probe, .remove = snd_opti9xx_pnp_remove, diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 624e6c044a44..de18722c4873 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -3335,7 +3335,6 @@ static struct snd_pci_quirk msi_black_list[] = { SND_PCI_QUIRK(0x1043, 0x81f2, "ASUS", 0), /* Athlon64 X2 + nvidia */ SND_PCI_QUIRK(0x1043, 0x81f6, "ASUS", 0), /* nvidia */ SND_PCI_QUIRK(0x1043, 0x822d, "ASUS", 0), /* Athlon64 X2 + nvidia MCP55 */ - SND_PCI_QUIRK(0x1179, 0xfb44, "Toshiba Satellite C870", 0), /* AMD Hudson */ SND_PCI_QUIRK(0x1849, 0x0888, "ASRock", 0), /* Athlon64 X2 + nvidia */ SND_PCI_QUIRK(0xa0a0, 0x0575, "Aopen MZ915-M", 0), /* ICH6 */ {} diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index c96e1945059d..b314d3e6d7fa 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -3225,7 +3225,6 @@ enum { CXT_PINCFG_LEMOTE_A1205, CXT_FIXUP_STEREO_DMIC, CXT_FIXUP_INC_MIC_BOOST, - CXT_FIXUP_GPIO1, }; static void cxt_fixup_stereo_dmic(struct hda_codec *codec, @@ -3304,15 +3303,6 @@ static const struct hda_fixup cxt_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = cxt5066_increase_mic_boost, }, - [CXT_FIXUP_GPIO1] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x01, AC_VERB_SET_GPIO_MASK, 0x01 }, - { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01 }, - { 0x01, AC_VERB_SET_GPIO_DATA, 0x01 }, - { } - }, - }, }; static const struct snd_pci_quirk cxt5051_fixups[] = { @@ -3322,7 +3312,6 @@ static const struct snd_pci_quirk cxt5051_fixups[] = { static const struct snd_pci_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC), - SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT_FIXUP_GPIO1), SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410), diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index aecf088f40af..496d7f21d3e5 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -67,8 +67,6 @@ struct hdmi_spec_per_pin { struct delayed_work work; struct snd_kcontrol *eld_ctl; int repoll_count; - bool setup; /* the stream has been set up by prepare callback */ - int channels; /* current number of channels */ bool non_pcm; bool chmap_set; /* channel-map override by ALSA API? */ unsigned char chmap[8]; /* ALSA API channel-map */ @@ -553,17 +551,6 @@ static int hdmi_channel_allocation(struct hdmi_eld *eld, int channels) } } - if (!ca) { - /* if there was no match, select the regular ALSA channel - * allocation with the matching number of channels */ - for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { - if (channels == channel_allocations[i].channels) { - ca = channel_allocations[i].ca_index; - break; - } - } - } - snd_print_channel_allocation(eld->info.spk_alloc, buf, sizeof(buf)); snd_printdd("HDMI: select CA 0x%x for %d-channel allocation: %s\n", ca, channels, buf); @@ -881,19 +868,18 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, return true; } -static void hdmi_setup_audio_infoframe(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin, - bool non_pcm) +static void hdmi_setup_audio_infoframe(struct hda_codec *codec, int pin_idx, + bool non_pcm, + struct snd_pcm_substream *substream) { + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); hda_nid_t pin_nid = per_pin->pin_nid; - int channels = per_pin->channels; + int channels = substream->runtime->channels; struct hdmi_eld *eld; int ca; union audio_infoframe ai; - if (!channels) - return; - eld = &per_pin->sink_eld; if (!eld->monitor_present) return; @@ -929,14 +915,6 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, return; } - /* - * always configure channel mapping, it may have been changed by the - * user in the meantime - */ - hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca, - channels, per_pin->chmap, - per_pin->chmap_set); - /* * sizeof(ai) is used instead of sizeof(*hdmi_ai) or * sizeof(*dp_ai) to avoid partial match/update problems when @@ -948,10 +926,20 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, "pin=%d channels=%d\n", pin_nid, channels); + hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca, + channels, per_pin->chmap, + per_pin->chmap_set); hdmi_stop_infoframe_trans(codec, pin_nid); hdmi_fill_audio_infoframe(codec, pin_nid, ai.bytes, sizeof(ai)); hdmi_start_infoframe_trans(codec, pin_nid); + } else { + /* For non-pcm audio switch, setup new channel mapping + * accordingly */ + if (per_pin->non_pcm != non_pcm) + hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca, + channels, per_pin->chmap, + per_pin->chmap_set); } per_pin->non_pcm = non_pcm; @@ -1275,7 +1263,6 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) eld_changed = true; } if (update_eld) { - bool old_eld_valid = pin_eld->eld_valid; pin_eld->eld_valid = eld->eld_valid; eld_changed = pin_eld->eld_size != eld->eld_size || memcmp(pin_eld->eld_buffer, eld->eld_buffer, @@ -1285,18 +1272,6 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) eld->eld_size); pin_eld->eld_size = eld->eld_size; pin_eld->info = eld->info; - - /* Haswell-specific workaround: re-setup when the transcoder is - * changed during the stream playback - */ - if (codec->vendor_id == 0x80862807 && - eld->eld_valid && !old_eld_valid && per_pin->setup) { - snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); - hdmi_setup_audio_infoframe(codec, per_pin, - per_pin->non_pcm); - } } mutex_unlock(&pin_eld->lock); @@ -1469,17 +1444,14 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, hda_nid_t cvt_nid = hinfo->nid; struct hdmi_spec *spec = codec->spec; int pin_idx = hinfo_to_pin_index(spec, hinfo); - struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - hda_nid_t pin_nid = per_pin->pin_nid; + hda_nid_t pin_nid = get_pin(spec, pin_idx)->pin_nid; bool non_pcm; non_pcm = check_non_pcm_per_cvt(codec, cvt_nid); - per_pin->channels = substream->runtime->channels; - per_pin->setup = true; hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels); - hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); + hdmi_setup_audio_infoframe(codec, pin_idx, non_pcm, substream); return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); } @@ -1519,9 +1491,6 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, snd_hda_spdif_ctls_unassign(codec, pin_idx); per_pin->chmap_set = false; memset(per_pin->chmap, 0, sizeof(per_pin->chmap)); - - per_pin->setup = false; - per_pin->channels = 0; } return 0; @@ -1657,7 +1626,8 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, per_pin->chmap_set = true; memcpy(per_pin->chmap, chmap, sizeof(chmap)); if (prepared) - hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); + hdmi_setup_audio_infoframe(codec, pin_idx, per_pin->non_pcm, + substream); return 0; } @@ -1745,9 +1715,6 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) struct snd_pcm_chmap *chmap; struct snd_kcontrol *kctl; int i; - - if (!codec->pcm_info[pin_idx].pcm) - break; err = snd_pcm_add_chmap_ctls(codec->pcm_info[pin_idx].pcm, SNDRV_PCM_STREAM_PLAYBACK, NULL, 0, pin_idx, &chmap); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 21b6649c128e..57f9f2a12e85 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3200,15 +3200,6 @@ static void alc269_fixup_limit_int_mic_boost(struct hda_codec *codec, } } -static void alc290_fixup_mono_speakers(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - if (action == HDA_FIXUP_ACT_PRE_PROBE) - /* Remove DAC node 0x03, as it seems to be - giving mono output */ - snd_hda_override_wcaps(codec, 0x03, 0); -} - enum { ALC269_FIXUP_SONY_VAIO, ALC275_FIXUP_SONY_VAIO_GPIO2, @@ -3232,12 +3223,9 @@ enum { ALC269_FIXUP_HP_GPIO_LED, ALC269_FIXUP_INV_DMIC, ALC269_FIXUP_LENOVO_DOCK, - ALC286_FIXUP_SONY_MIC_NO_PRESENCE, ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT, ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, - ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, - ALC290_FIXUP_MONO_SPEAKERS, ALC269_FIXUP_HEADSET_MODE, ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, ALC269_FIXUP_ASUS_X101_FUNC, @@ -3424,15 +3412,6 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC }, - [ALC269_FIXUP_DELL3_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC - }, [ALC269_FIXUP_HEADSET_MODE] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_headset_mode, @@ -3441,13 +3420,6 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_headset_mode_no_hp_mic, }, - [ALC286_FIXUP_SONY_MIC_NO_PRESENCE] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ - { } - }, - }, [ALC269_FIXUP_ASUS_X101_FUNC] = { .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_x101_headset_mic, @@ -3505,12 +3477,6 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_limit_int_mic_boost, }, - [ALC290_FIXUP_MONO_SPEAKERS] = { - .type = HDA_FIXUP_FUNC, - .v.func = alc290_fixup_mono_speakers, - .chained = true, - .chain_id = ALC269_FIXUP_DELL3_MIC_NO_PRESENCE, - }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -3545,7 +3511,6 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0608, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0609, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0613, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), - SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_MONO_SPEAKERS), SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x1973, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1), @@ -3564,7 +3529,6 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x1043, 0x8516, "ASUS X101CH", ALC269_FIXUP_ASUS_X101), - SND_PCI_QUIRK(0x104d, 0x90b6, "Sony VAIO Pro 13", ALC286_FIXUP_SONY_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x104d, 0x9073, "Sony VAIO", ALC275_FIXUP_SONY_VAIO_GPIO2), SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), @@ -4243,7 +4207,6 @@ static const struct hda_fixup alc662_fixups[] = { static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2), - SND_PCI_QUIRK(0x1025, 0x022f, "Acer Aspire One", ALC662_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE), SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x1025, 0x0349, "eMachines eM250", ALC662_FIXUP_INV_DMIC), @@ -4252,7 +4215,6 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x05d8, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05db, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800), - SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_ASUS_MODE4), SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT), SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2), SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD), diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 9e675c76436c..23f39d25db87 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -45,7 +45,7 @@ source "sound/soc/jz4740/Kconfig" source "sound/soc/nuc900/Kconfig" source "sound/soc/omap/Kconfig" source "sound/soc/kirkwood/Kconfig" -source "sound/soc/mid-x86/Kconfig" +source "sound/soc/intel/Kconfig" source "sound/soc/mxs/Kconfig" source "sound/soc/pxa/Kconfig" source "sound/soc/samsung/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 197b6ae54c8d..25201f531c21 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -20,7 +20,7 @@ obj-$(CONFIG_SND_SOC) += davinci/ obj-$(CONFIG_SND_SOC) += dwc/ obj-$(CONFIG_SND_SOC) += fsl/ obj-$(CONFIG_SND_SOC) += jz4740/ -obj-$(CONFIG_SND_SOC) += mid-x86/ +obj-$(CONFIG_SND_SOC) += intel/ obj-$(CONFIG_SND_SOC) += mxs/ obj-$(CONFIG_SND_SOC) += nuc900/ obj-$(CONFIG_SND_SOC) += omap/ diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c index 6fd174be3bdf..60159c07448d 100644 --- a/sound/soc/codecs/88pm860x-codec.c +++ b/sound/soc/codecs/88pm860x-codec.c @@ -351,9 +351,6 @@ static int snd_soc_put_volsw_2r_st(struct snd_kcontrol *kcontrol, val = ucontrol->value.integer.value[0]; val2 = ucontrol->value.integer.value[1]; - if (val >= ARRAY_SIZE(st_table) || val2 >= ARRAY_SIZE(st_table)) - return -EINVAL; - err = snd_soc_update_bits(codec, reg, 0x3f, st_table[val].m); if (err < 0) return err; diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index bce45c197e1d..a153b168129b 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -1225,18 +1225,13 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol, struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev); struct device *dev = codec->dev; bool apply_fir, apply_iir; - unsigned int req; - int status; + int req, status; dev_dbg(dev, "%s: Enter.\n", __func__); mutex_lock(&drvdata->anc_lock); req = ucontrol->value.integer.value[0]; - if (req >= ARRAY_SIZE(enum_anc_state)) { - status = -EINVAL; - goto cleanup; - } if (req != ANC_APPLY_FIR_IIR && req != ANC_APPLY_FIR && req != ANC_APPLY_IIR) { dev_err(dev, "%s: ERROR: Unsupported status to set '%s'!\n", diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 8dbcacd44e6a..41cdd1642970 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -1863,7 +1863,7 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol, struct max98095_pdata *pdata = max98095->pdata; int channel = max98095_get_eq_channel(kcontrol->id.name); struct max98095_cdata *cdata; - unsigned int sel = ucontrol->value.integer.value[0]; + int sel = ucontrol->value.integer.value[0]; struct max98095_eq_cfg *coef_set; int fs, best, best_val, i; int regmask, regsave; @@ -2016,7 +2016,7 @@ static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol, struct max98095_pdata *pdata = max98095->pdata; int channel = max98095_get_bq_channel(codec, kcontrol->id.name); struct max98095_cdata *cdata; - unsigned int sel = ucontrol->value.integer.value[0]; + int sel = ucontrol->value.integer.value[0]; struct max98095_biquad_cfg *coef_set; int fs, best, best_val, i; int regmask, regsave; diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c index 8a8d9364e87f..5402dfbbb716 100644 --- a/sound/soc/codecs/mc13783.c +++ b/sound/soc/codecs/mc13783.c @@ -126,10 +126,6 @@ static int mc13783_write(struct snd_soc_codec *codec, ret = mc13xxx_reg_write(priv->mc13xxx, reg, value); - /* include errata fix for spi audio problems */ - if (reg == MC13783_AUDIO_CODEC || reg == MC13783_AUDIO_DAC) - ret = mc13xxx_reg_write(priv->mc13xxx, reg, value); - mc13xxx_unlock(priv->mc13xxx); return ret; diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 5e5af898f7f8..0a4ffdd1d2a7 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -857,9 +857,9 @@ static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, if (pll_div.k) { reg |= 0x20; - snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 16) & 0xff); - snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 8) & 0xff); - snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0xff); + snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 18) & 0x3f); + snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 9) & 0x1ff); + snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0x1ff); } snd_soc_write(codec, WM8960_PLL1, reg); diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 29e95f93d482..97b7b8fcfd52 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -93,9 +94,9 @@ static int wm8994_retune_mobile_base[] = { static const struct wm8958_micd_rate micdet_rates[] = { { 32768, true, 1, 4 }, - { 32768, false, 1, 1 }, + { 32768, false, 1, 0 }, { 44100 * 256, true, 7, 10 }, - { 44100 * 256, false, 7, 10 }, + { 44100 * 256, false, 7, 9 }, }; static const struct wm8958_micd_rate jackdet_rates[] = { @@ -539,13 +540,13 @@ static const struct soc_enum adc_osr = static const struct snd_kcontrol_new wm8994_snd_controls[] = { SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8994_AIF1_ADC1_LEFT_VOLUME, WM8994_AIF1_ADC1_RIGHT_VOLUME, - 1, 119, 0, digital_tlv), + 1, 120, 0, digital_tlv), SOC_DOUBLE_R_TLV("AIF1ADC2 Volume", WM8994_AIF1_ADC2_LEFT_VOLUME, WM8994_AIF1_ADC2_RIGHT_VOLUME, - 1, 119, 0, digital_tlv), + 1, 120, 0, digital_tlv), SOC_DOUBLE_R_TLV("AIF2ADC Volume", WM8994_AIF2_ADC_LEFT_VOLUME, WM8994_AIF2_ADC_RIGHT_VOLUME, - 1, 119, 0, digital_tlv), + 1, 120, 0, digital_tlv), SOC_ENUM("AIF1ADCL Source", aif1adcl_src), SOC_ENUM("AIF1ADCR Source", aif1adcr_src), @@ -607,12 +608,12 @@ SOC_ENUM("ADC OSR", adc_osr), SOC_ENUM("DAC OSR", dac_osr), SOC_DOUBLE_R_TLV("DAC1 Volume", WM8994_DAC1_LEFT_VOLUME, - WM8994_DAC1_RIGHT_VOLUME, 1, 96, 0, digital_tlv), + WM8994_DAC1_RIGHT_VOLUME, 1, 112, 0, digital_tlv), SOC_DOUBLE_R("DAC1 Switch", WM8994_DAC1_LEFT_VOLUME, WM8994_DAC1_RIGHT_VOLUME, 9, 1, 1), SOC_DOUBLE_R_TLV("DAC2 Volume", WM8994_DAC2_LEFT_VOLUME, - WM8994_DAC2_RIGHT_VOLUME, 1, 96, 0, digital_tlv), + WM8994_DAC2_RIGHT_VOLUME, 1, 112, 0, digital_tlv), SOC_DOUBLE_R("DAC2 Switch", WM8994_DAC2_LEFT_VOLUME, WM8994_DAC2_RIGHT_VOLUME, 9, 1, 1), @@ -638,6 +639,11 @@ SOC_SINGLE_TLV("AIF2DAC 3D Stereo Volume", WM8994_AIF2_DAC_FILTERS_2, 10, 15, 0, wm8994_3d_tlv), SOC_SINGLE("AIF2DAC 3D Stereo Switch", WM8994_AIF2_DAC_FILTERS_2, 8, 1, 0), + +SOC_SINGLE_TLV("MIXINL MIXOUTL Volume", WM8994_INPUT_MIXER_3, 0, 7, 0, + mixin_boost_tlv), +SOC_SINGLE_TLV("MIXINR MIXOUTR Volume", WM8994_INPUT_MIXER_4, 0, 7, 0, + mixin_boost_tlv), }; static const struct snd_kcontrol_new wm8994_eq_controls[] = { @@ -866,7 +872,7 @@ static void vmid_reference(struct snd_soc_codec *codec) WM8994_BIAS_SRC | WM8994_STARTUP_BIAS_ENA | WM8994_VMID_BUF_ENA | - (0x2 << WM8994_VMID_RAMP_SHIFT)); + (0x3 << WM8994_VMID_RAMP_SHIFT)); /* Main bias enable, VMID=2x40k */ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1, @@ -874,7 +880,14 @@ static void vmid_reference(struct snd_soc_codec *codec) WM8994_VMID_SEL_MASK, WM8994_BIAS_ENA | 0x2); - msleep(300); + /* The delay of 300ms was recommended to support pop + * free startup of the line output driver, as we don't use + * that feature reducing the delay to 50ms as recommended in + * the spec, Also changing VMID_RAMP to soft fast start + * accordingly Also applies for VMID_FORCE and + * vmid_dereference. + */ + msleep(50); snd_soc_update_bits(codec, WM8994_ANTIPOP_2, WM8994_VMID_RAMP_MASK | @@ -893,15 +906,14 @@ static void vmid_reference(struct snd_soc_codec *codec) WM8994_BIAS_SRC | WM8994_STARTUP_BIAS_ENA | WM8994_VMID_BUF_ENA | - (0x2 << WM8994_VMID_RAMP_SHIFT)); + (0x3 << WM8994_VMID_RAMP_SHIFT)); /* Main bias enable, VMID=2x40k */ snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1, WM8994_BIAS_ENA | WM8994_VMID_SEL_MASK, WM8994_BIAS_ENA | 0x2); - - msleep(400); + msleep(50); snd_soc_update_bits(codec, WM8994_ANTIPOP_2, WM8994_VMID_RAMP_MASK | @@ -946,7 +958,7 @@ static void vmid_dereference(struct snd_soc_codec *codec) snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1, WM8994_VMID_SEL_MASK, 0); - msleep(400); + msleep(50); /* Active discharge */ snd_soc_update_bits(codec, WM8994_ANTIPOP_1, @@ -969,7 +981,7 @@ static void vmid_dereference(struct snd_soc_codec *codec) WM8994_VMID_RAMP_MASK, 0); snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1, - WM8994_VMID_SEL_MASK, 0); + WM8994_BIAS_ENA | WM8994_VMID_SEL_MASK, 0); } pm_runtime_put(codec->dev); @@ -1055,7 +1067,7 @@ static int aif1clk_ev(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: /* Don't enable timeslot 2 if not in use */ - if (wm8994->channels[0] <= 2) + if ((wm8994->channels[0] <= 2) && (wm8994->slots <= 2)) mask &= ~(WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA); val = snd_soc_read(codec, WM8994_AIF1_CONTROL_1); @@ -1331,6 +1343,26 @@ static int dac_ev(struct snd_soc_dapm_widget *w, struct snd_soc_codec *codec = w->codec; unsigned int mask = 1 << w->shift; + /* Don't propagate FIFO errors unless the DAC is running */ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Clear FIFO error status */ + snd_soc_update_bits(codec, WM8994_INTERRUPT_STATUS_2, + WM8994_FIFOS_ERR_EINT_MASK, + 1 << WM8994_FIFOS_ERR_EINT_SHIFT); + /* Unmask FIFO error interrupts */ + snd_soc_update_bits(codec, WM8994_INTERRUPT_STATUS_2_MASK, + WM8994_IM_FIFOS_ERR_EINT_MASK, + 0 << WM8994_IM_FIFOS_ERR_EINT_SHIFT); + break; + case SND_SOC_DAPM_PRE_PMD: + /* Mask FIFO error interrupts */ + snd_soc_update_bits(codec, WM8994_INTERRUPT_STATUS_2_MASK, + WM8994_IM_FIFOS_ERR_EINT_MASK, + 1 << WM8994_IM_FIFOS_ERR_EINT_SHIFT); + break; + } + snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5, mask, mask); return 0; @@ -1498,6 +1530,24 @@ static const char *aif1dac_text[] = { "AIF1DACDAT", "AIF3DACDAT", }; +static const char *loopback_text[] = { + "None", "ADCDAT", +}; + +static const struct soc_enum aif1_loopback_enum = + SOC_ENUM_SINGLE(WM8994_AIF1_CONTROL_2, WM8994_AIF1_LOOPBACK_SHIFT, 2, + loopback_text); + +static const struct snd_kcontrol_new aif1_loopback = + SOC_DAPM_ENUM("AIF1 Loopback", aif1_loopback_enum); + +static const struct soc_enum aif2_loopback_enum = + SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_2, WM8994_AIF2_LOOPBACK_SHIFT, 2, + loopback_text); + +static const struct snd_kcontrol_new aif2_loopback = + SOC_DAPM_ENUM("AIF2 Loopback", aif2_loopback_enum); + static const struct soc_enum aif1dac_enum = SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 0, 2, aif1dac_text); @@ -1616,13 +1666,17 @@ SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &wm_hubs_hpr_mux), static const struct snd_soc_dapm_widget wm8994_dac_revd_widgets[] = { SND_SOC_DAPM_DAC_E("DAC2L", NULL, SND_SOC_NOPM, 3, 0, - dac_ev, SND_SOC_DAPM_PRE_PMU), + dac_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_DAC_E("DAC2R", NULL, SND_SOC_NOPM, 2, 0, - dac_ev, SND_SOC_DAPM_PRE_PMU), + dac_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_DAC_E("DAC1L", NULL, SND_SOC_NOPM, 1, 0, - dac_ev, SND_SOC_DAPM_PRE_PMU), + dac_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_DAC_E("DAC1R", NULL, SND_SOC_NOPM, 0, 0, - dac_ev, SND_SOC_DAPM_PRE_PMU), + dac_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD), }; static const struct snd_soc_dapm_widget wm8994_dac_widgets[] = { @@ -1633,15 +1687,15 @@ SND_SOC_DAPM_DAC("DAC1R", NULL, WM8994_POWER_MANAGEMENT_5, 0, 0), }; static const struct snd_soc_dapm_widget wm8994_adc_revd_widgets[] = { -SND_SOC_DAPM_VIRT_MUX_E("ADCL Mux", WM8994_POWER_MANAGEMENT_4, 1, 0, &adcl_mux, +SND_SOC_DAPM_VIRT_MUX_E("ADCL Mux", SND_SOC_NOPM, 1, 0, &adcl_mux, adc_mux_ev, SND_SOC_DAPM_PRE_PMU), -SND_SOC_DAPM_VIRT_MUX_E("ADCR Mux", WM8994_POWER_MANAGEMENT_4, 0, 0, &adcr_mux, +SND_SOC_DAPM_VIRT_MUX_E("ADCR Mux", SND_SOC_NOPM, 0, 0, &adcr_mux, adc_mux_ev, SND_SOC_DAPM_PRE_PMU), }; static const struct snd_soc_dapm_widget wm8994_adc_widgets[] = { -SND_SOC_DAPM_VIRT_MUX("ADCL Mux", WM8994_POWER_MANAGEMENT_4, 1, 0, &adcl_mux), -SND_SOC_DAPM_VIRT_MUX("ADCR Mux", WM8994_POWER_MANAGEMENT_4, 0, 0, &adcr_mux), +SND_SOC_DAPM_VIRT_MUX("ADCL Mux", SND_SOC_NOPM, 1, 0, &adcl_mux), +SND_SOC_DAPM_VIRT_MUX("ADCR Mux", SND_SOC_NOPM, 0, 0, &adcr_mux), }; static const struct snd_soc_dapm_widget wm8994_dapm_widgets[] = { @@ -1737,12 +1791,11 @@ SND_SOC_DAPM_ADC("DMIC2R", NULL, WM8994_POWER_MANAGEMENT_4, 4, 0), SND_SOC_DAPM_ADC("DMIC1L", NULL, WM8994_POWER_MANAGEMENT_4, 3, 0), SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8994_POWER_MANAGEMENT_4, 2, 0), -/* Power is done with the muxes since the ADC power also controls the - * downsampling chain, the chip will automatically manage the analogue - * specific portions. - */ -SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 1, 0), -SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_ADC("ADCL", NULL, WM8994_POWER_MANAGEMENT_4, 1, 0), +SND_SOC_DAPM_ADC("ADCR", NULL, WM8994_POWER_MANAGEMENT_4, 0, 0), + +SND_SOC_DAPM_MUX("AIF1 Loopback", SND_SOC_NOPM, 0, 0, &aif1_loopback), +SND_SOC_DAPM_MUX("AIF2 Loopback", SND_SOC_NOPM, 0, 0, &aif2_loopback), SND_SOC_DAPM_POST("Debug log", post_ev), }; @@ -1875,9 +1928,9 @@ static const struct snd_soc_dapm_route intercon[] = { { "AIF1DAC2L", NULL, "AIF1DAC Mux" }, { "AIF1DAC2R", NULL, "AIF1DAC Mux" }, - { "AIF1DAC Mux", "AIF1DACDAT", "AIF1DACDAT" }, + { "AIF1DAC Mux", "AIF1DACDAT", "AIF1 Loopback" }, { "AIF1DAC Mux", "AIF3DACDAT", "AIF3DACDAT" }, - { "AIF2DAC Mux", "AIF2DACDAT", "AIF2DACDAT" }, + { "AIF2DAC Mux", "AIF2DACDAT", "AIF2 Loopback" }, { "AIF2DAC Mux", "AIF3DACDAT", "AIF3DACDAT" }, { "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCL" }, { "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCR" }, @@ -1928,6 +1981,12 @@ static const struct snd_soc_dapm_route intercon[] = { { "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACL" }, { "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACR" }, + /* Loopback */ + { "AIF1 Loopback", "ADCDAT", "AIF1ADCDAT" }, + { "AIF1 Loopback", "None", "AIF1DACDAT" }, + { "AIF2 Loopback", "ADCDAT", "AIF2ADCDAT" }, + { "AIF2 Loopback", "None", "AIF2DACDAT" }, + /* Sidetone */ { "Left Sidetone", "ADC/DMIC1", "ADCL Mux" }, { "Left Sidetone", "DMIC2", "DMIC2L" }, @@ -2010,15 +2069,16 @@ struct fll_div { u16 outdiv; u16 n; u16 k; + u16 lambda; u16 clk_ref_div; u16 fll_fratio; }; -static int wm8994_get_fll_config(struct fll_div *fll, +static int wm8994_get_fll_config(struct wm8994 *control, struct fll_div *fll, int freq_in, int freq_out) { u64 Kpart; - unsigned int K, Ndiv, Nmod; + unsigned int K, Ndiv, Nmod, gcd_fll; pr_debug("FLL input=%dHz, output=%dHz\n", freq_in, freq_out); @@ -2067,20 +2127,32 @@ static int wm8994_get_fll_config(struct fll_div *fll, Nmod = freq_out % freq_in; pr_debug("Nmod=%d\n", Nmod); - /* Calculate fractional part - scale up so we can round. */ - Kpart = FIXED_FLL_SIZE * (long long)Nmod; + switch (control->type) { + case WM8994: + /* Calculate fractional part - scale up so we can round. */ + Kpart = FIXED_FLL_SIZE * (long long)Nmod; + + do_div(Kpart, freq_in); + + K = Kpart & 0xFFFFFFFF; - do_div(Kpart, freq_in); + if ((K % 10) >= 5) + K += 5; - K = Kpart & 0xFFFFFFFF; + /* Move down to proper range now rounding is done */ + fll->k = K / 10; + fll->lambda = 0; - if ((K % 10) >= 5) - K += 5; + pr_debug("N=%x K=%x\n", fll->n, fll->k); + break; - /* Move down to proper range now rounding is done */ - fll->k = K / 10; + default: + gcd_fll = gcd(freq_out, freq_in); - pr_debug("N=%x K=%x\n", fll->n, fll->k); + fll->k = (freq_out - (freq_in * fll->n)) / gcd_fll; + fll->lambda = freq_in / gcd_fll; + + } return 0; } @@ -2144,9 +2216,9 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src, * analysis bugs spewing warnings. */ if (freq_out) - ret = wm8994_get_fll_config(&fll, freq_in, freq_out); + ret = wm8994_get_fll_config(control, &fll, freq_in, freq_out); else - ret = wm8994_get_fll_config(&fll, wm8994->fll[id].in, + ret = wm8994_get_fll_config(control, &fll, wm8994->fll[id].in, wm8994->fll[id].out); if (ret < 0) return ret; @@ -2191,6 +2263,17 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src, WM8994_FLL1_N_MASK, fll.n << WM8994_FLL1_N_SHIFT); + if (fll.lambda) { + snd_soc_update_bits(codec, WM8958_FLL1_EFS_1 + reg_offset, + WM8958_FLL1_LAMBDA_MASK, + fll.lambda); + snd_soc_update_bits(codec, WM8958_FLL1_EFS_2 + reg_offset, + WM8958_FLL1_EFS_ENA, WM8958_FLL1_EFS_ENA); + } else { + snd_soc_update_bits(codec, WM8958_FLL1_EFS_2 + reg_offset, + WM8958_FLL1_EFS_ENA, 0); + } + snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset, WM8994_FLL1_FRC_NCO | WM8958_FLL1_BYP | WM8994_FLL1_REFCLK_DIV_MASK | @@ -2271,7 +2354,8 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src, * If SYSCLK will be less than 50kHz adjust AIFnCLK dividers * for detection. */ - if (max(wm8994->aifclk[0], wm8994->aifclk[1]) < 50000) { + if (max(wm8994->aifclk[0], wm8994->aifclk[1]) < 50000 && + !wm8994->aifdiv[0]) { dev_dbg(codec->dev, "Configuring AIFs for 128fs\n"); wm8994->aifdiv[0] = snd_soc_read(codec, WM8994_AIF1_RATE) @@ -2555,17 +2639,24 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) struct wm8994 *control = wm8994->wm8994; int ms_reg; int aif1_reg; + int dac_reg; + int adc_reg; int ms = 0; int aif1 = 0; + int lrclk = 0; switch (dai->id) { case 1: ms_reg = WM8994_AIF1_MASTER_SLAVE; aif1_reg = WM8994_AIF1_CONTROL_1; + dac_reg = WM8994_AIF1DAC_LRCLK; + adc_reg = WM8994_AIF1ADC_LRCLK; break; case 2: ms_reg = WM8994_AIF2_MASTER_SLAVE; aif1_reg = WM8994_AIF2_CONTROL_1; + dac_reg = WM8994_AIF1DAC_LRCLK; + adc_reg = WM8994_AIF1ADC_LRCLK; break; default: return -EINVAL; @@ -2584,6 +2675,7 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_B: aif1 |= WM8994_AIF1_LRCLK_INV; + lrclk |= WM8958_AIF1_LRCLK_INV; case SND_SOC_DAIFMT_DSP_A: aif1 |= 0x18; break; @@ -2622,12 +2714,14 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) break; case SND_SOC_DAIFMT_IB_IF: aif1 |= WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV; + lrclk |= WM8958_AIF1_LRCLK_INV; break; case SND_SOC_DAIFMT_IB_NF: aif1 |= WM8994_AIF1_BCLK_INV; break; case SND_SOC_DAIFMT_NB_IF: aif1 |= WM8994_AIF1_LRCLK_INV; + lrclk |= WM8958_AIF1_LRCLK_INV; break; default: return -EINVAL; @@ -2658,6 +2752,10 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) aif1); snd_soc_update_bits(codec, ms_reg, WM8994_AIF1_MSTR, ms); + snd_soc_update_bits(codec, dac_reg, + WM8958_AIF1_LRCLK_INV, lrclk); + snd_soc_update_bits(codec, adc_reg, + WM8958_AIF1_LRCLK_INV, lrclk); return 0; } @@ -2706,9 +2804,40 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, int lrclk = 0; int rate_val = 0; int id = dai->id - 1; + struct snd_pcm_hw_params hw_params; int i, cur_val, best_val, bclk_rate, best; + if (params) + memcpy(&hw_params, params, sizeof(*params)); + else + return -EINVAL; + + /* If custom params are there, override to custom params */ + if (pdata->custom_cfg) { + + dev_dbg(codec->dev, "%s: Overriding to custom params....\n", + __func__); + + snd_mask_none(hw_param_mask(&hw_params, + SNDRV_PCM_HW_PARAM_FORMAT)); + snd_mask_set(hw_param_mask(&hw_params, + SNDRV_PCM_HW_PARAM_FORMAT), + pdata->custom_cfg->format); + + hw_param_interval(&hw_params, SNDRV_PCM_HW_PARAM_RATE)->min = + pdata->custom_cfg->rate; + hw_param_interval(&hw_params, SNDRV_PCM_HW_PARAM_RATE)->max = + pdata->custom_cfg->rate; + + hw_param_interval(&hw_params, + SNDRV_PCM_HW_PARAM_CHANNELS)->min = + pdata->custom_cfg->channels; + hw_param_interval(&hw_params, + SNDRV_PCM_HW_PARAM_CHANNELS)->max = + pdata->custom_cfg->channels; + } + switch (dai->id) { case 1: aif1_reg = WM8994_AIF1_CONTROL_1; @@ -2740,8 +2869,9 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - bclk_rate = params_rate(params); - switch (params_format(params)) { + bclk_rate = params_rate(&hw_params); + + switch (params_format(&hw_params)) { case SNDRV_PCM_FORMAT_S16_LE: bclk_rate *= 16; break; @@ -2761,7 +2891,7 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - wm8994->channels[id] = params_channels(params); + wm8994->channels[id] = params_channels(&hw_params); if (pdata->max_channels_clocked[id] && wm8994->channels[id] > pdata->max_channels_clocked[id]) { dev_dbg(dai->dev, "Constraining channels to %d from %d\n", @@ -2781,7 +2911,7 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, /* Try to find an appropriate sample rate; look for an exact match. */ for (i = 0; i < ARRAY_SIZE(srs); i++) - if (srs[i].rate == params_rate(params)) + if (srs[i].rate == params_rate(&hw_params)) break; if (i == ARRAY_SIZE(srs)) return -EINVAL; @@ -2802,10 +2932,10 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, /* AIFCLK/fs ratio; look for a close match in either direction */ best = 0; - best_val = abs((fs_ratios[0] * params_rate(params)) + best_val = abs((fs_ratios[0] * params_rate(&hw_params)) - wm8994->aifclk[id]); for (i = 1; i < ARRAY_SIZE(fs_ratios); i++) { - cur_val = abs((fs_ratios[i] * params_rate(params)) + cur_val = abs((fs_ratios[i] * params_rate(&hw_params)) - wm8994->aifclk[id]); if (cur_val >= best_val) continue; @@ -2833,7 +2963,7 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, bclk_divs[best], bclk_rate); bclk |= best << WM8994_AIF1_BCLK_DIV_SHIFT; - lrclk = bclk_rate / params_rate(params); + lrclk = bclk_rate / params_rate(&hw_params); if (!lrclk) { dev_err(dai->dev, "Unable to generate LRCLK from %dHz BCLK\n", bclk_rate); @@ -2853,12 +2983,12 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { switch (dai->id) { case 1: - wm8994->dac_rates[0] = params_rate(params); + wm8994->dac_rates[0] = params_rate(&hw_params); wm8994_set_retune_mobile(codec, 0); wm8994_set_retune_mobile(codec, 1); break; case 2: - wm8994->dac_rates[1] = params_rate(params); + wm8994->dac_rates[1] = params_rate(&hw_params); wm8994_set_retune_mobile(codec, 2); break; } @@ -2911,6 +3041,13 @@ static int wm8994_aif3_hw_params(struct snd_pcm_substream *substream, return snd_soc_update_bits(codec, aif1_reg, WM8994_AIF1_WL_MASK, aif1); } +#if IS_ENABLED(CONFIG_SND_MRFLD_MACHINE) || \ + IS_ENABLED(CONFIG_SND_MOOR_MACHINE) +static int wm8994_aif_mute(struct snd_soc_dai *codec_dai, int mute) +{ + return 0; +} +#else static int wm8994_aif_mute(struct snd_soc_dai *codec_dai, int mute) { struct snd_soc_codec *codec = codec_dai->codec; @@ -2937,6 +3074,7 @@ static int wm8994_aif_mute(struct snd_soc_dai *codec_dai, int mute) return 0; } +#endif static int wm8994_set_tristate(struct snd_soc_dai *codec_dai, int tristate) { @@ -2964,6 +3102,25 @@ static int wm8994_set_tristate(struct snd_soc_dai *codec_dai, int tristate) return snd_soc_update_bits(codec, reg, mask, val); } +static int wm8994_set_tdm_slots(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 wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + struct wm8994 *control = wm8994->wm8994; + + switch (control->type) { + case WM8958: + wm8994->slots = slots; + break; + default: + pr_err("we dont support tdm for non 8958!"); + return -EINVAL; + break; + } + return 0; +} + static int wm8994_aif2_probe(struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; @@ -2991,6 +3148,7 @@ static const struct snd_soc_dai_ops wm8994_aif1_dai_ops = { .digital_mute = wm8994_aif_mute, .set_pll = wm8994_set_fll, .set_tristate = wm8994_set_tristate, + .set_tdm_slot = wm8994_set_tdm_slots, }; static const struct snd_soc_dai_ops wm8994_aif2_dai_ops = { @@ -3096,24 +3254,7 @@ static int wm8994_codec_suspend(struct snd_soc_codec *codec) static int wm8994_codec_resume(struct snd_soc_codec *codec) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); - struct wm8994 *control = wm8994->wm8994; int i, ret; - unsigned int val, mask; - - if (control->revision < 4) { - /* force a HW read */ - ret = regmap_read(control->regmap, - WM8994_POWER_MANAGEMENT_5, &val); - - /* modify the cache only */ - codec->cache_only = 1; - mask = WM8994_DAC1R_ENA | WM8994_DAC1L_ENA | - WM8994_DAC2R_ENA | WM8994_DAC2L_ENA; - val &= mask; - snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5, - mask, val); - codec->cache_only = 0; - } for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) { if (!wm8994->fll_suspend[i].out) @@ -3495,6 +3636,32 @@ static void wm8958_button_det(struct snd_soc_codec *codec, u16 status) wm8994->btn_mask); } +static void wm8958_open_circuit_work(struct work_struct *work) +{ + struct wm8994_priv *wm8994 = container_of(work, + struct wm8994_priv, + open_circuit_work.work); + struct device *dev = wm8994->wm8994->dev; + + wm1811_micd_stop(wm8994->hubs.codec); + + mutex_lock(&wm8994->accdet_lock); + + dev_dbg(dev, "Reporting open circuit\n"); + + wm8994->jack_mic = false; + wm8994->mic_detecting = true; + wm8994->headphone_detected = false; + + wm8958_micd_set_rate(wm8994->hubs.codec); + + snd_soc_jack_report(wm8994->micdet[0].jack, 0, + wm8994->btn_mask | + SND_JACK_HEADSET); + + mutex_unlock(&wm8994->accdet_lock); +} + static void wm8958_mic_id(void *data, u16 status) { struct snd_soc_codec *codec = data; @@ -3504,16 +3671,9 @@ static void wm8958_mic_id(void *data, u16 status) if (!(status & WM8958_MICD_STS)) { /* If nothing present then clear our statuses */ dev_dbg(codec->dev, "Detected open circuit\n"); - wm8994->jack_mic = false; - wm8994->mic_detecting = true; - - wm1811_micd_stop(codec); - wm8958_micd_set_rate(codec); - - snd_soc_jack_report(wm8994->micdet[0].jack, 0, - wm8994->btn_mask | - SND_JACK_HEADSET); + schedule_delayed_work(&wm8994->open_circuit_work, + msecs_to_jiffies(2500)); return; } @@ -3568,10 +3728,8 @@ static void wm1811_mic_work(struct work_struct *work) dev_dbg(codec->dev, "Starting mic detection\n"); - /* Use a user-supplied callback if we have one */ - if (wm8994->micd_cb) { - wm8994->micd_cb(wm8994->micd_cb_data); - } else { + /* If there's a callback it'll be called out of the lock */ + if (!wm8994->micd_cb) { /* * Start off measument of microphone impedence to find out * what's actually there. @@ -3585,6 +3743,10 @@ static void wm1811_mic_work(struct work_struct *work) mutex_unlock(&wm8994->accdet_lock); + /* Custom callbacks may reasonably wish to take the same locks */ + if (wm8994->micd_cb) + wm8994->micd_cb(wm8994->micd_cb_data); + pm_runtime_put(codec->dev); } @@ -3596,8 +3758,12 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data) int reg, delay; bool present; + cancel_delayed_work_sync(&wm8994->mic_work); + pm_runtime_get_sync(codec->dev); + cancel_delayed_work_sync(&wm8994->mic_complete_work); + mutex_lock(&wm8994->accdet_lock); reg = snd_soc_read(codec, WM1811_JACKDET_CTRL); @@ -3630,8 +3796,6 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data) } else { dev_dbg(codec->dev, "Jack not detected\n"); - cancel_delayed_work_sync(&wm8994->mic_work); - snd_soc_update_bits(codec, WM8958_MICBIAS2, WM8958_MICB2_DISCH, WM8958_MICB2_DISCH); @@ -3720,6 +3884,7 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, } else { wm8994->mic_detecting = true; wm8994->jack_mic = false; + wm8994->headphone_detected = false; } if (id_cb) { @@ -3780,11 +3945,70 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, } EXPORT_SYMBOL_GPL(wm8958_mic_detect); +int wm8958_micd_set_custom_rate(struct snd_soc_codec *codec, + wm8958_micd_set_custom_rate_cb micd_custom_rate_cb, + void *micd_custom_rate_cb_data) +{ + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + if (micd_custom_rate_cb) { + wm8994->micd_custom_rate_cb = micd_custom_rate_cb; + wm8994->micd_custom_rate_cb_data = micd_custom_rate_cb_data; + } + + return 0; +} +EXPORT_SYMBOL_GPL(wm8958_micd_set_custom_rate); + +static void wm8958_mic_work(struct work_struct *work) +{ + struct wm8994_priv *wm8994 = container_of(work, + struct wm8994_priv, + mic_complete_work.work); + struct snd_soc_codec *codec = wm8994->hubs.codec; + + dev_crit(codec->dev, "MIC WORK %x\n", wm8994->mic_status); + + pm_runtime_get_sync(codec->dev); + + mutex_lock(&wm8994->accdet_lock); + + wm8994->mic_id_cb(wm8994->mic_id_cb_data, wm8994->mic_status); + + mutex_unlock(&wm8994->accdet_lock); + + pm_runtime_put(codec->dev); + + dev_crit(codec->dev, "MIC WORK %x DONE\n", wm8994->mic_status); +} + +static void wm8958_micd_set_custom_rate_work(struct work_struct *work) +{ + struct wm8994_priv *wm8994 = container_of(work, + struct wm8994_priv, + micd_set_custom_rate_work.work); + struct snd_soc_codec *codec = wm8994->hubs.codec; + + dev_dbg(codec->dev, "%s: Set custom rates\n", __func__); + + pm_runtime_get_sync(codec->dev); + + mutex_lock(&wm8994->accdet_lock); + + wm8994->micd_custom_rate_cb(wm8994->micd_custom_rate_cb_data); + + mutex_unlock(&wm8994->accdet_lock); + + pm_runtime_put(codec->dev); + +} + static irqreturn_t wm8958_mic_irq(int irq, void *data) { struct wm8994_priv *wm8994 = data; struct snd_soc_codec *codec = wm8994->hubs.codec; - int reg, count, ret; + struct wm8994 *control = wm8994->wm8994; + int reg, count, ret, id_delay; /* * Jack detection may have detected a removal simulataneously @@ -3794,6 +4018,9 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data) if (!(snd_soc_read(codec, WM8958_MIC_DETECT_1) & WM8958_MICD_ENA)) return IRQ_HANDLED; + cancel_delayed_work_sync(&wm8994->mic_complete_work); + cancel_delayed_work_sync(&wm8994->open_circuit_work); + pm_runtime_get_sync(codec->dev); /* We may occasionally read a detection without an impedence @@ -3842,14 +4069,30 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data) snd_soc_jack_report(wm8994->micdet[0].jack, 0, SND_JACK_MECHANICAL | SND_JACK_HEADSET | wm8994->btn_mask); + wm8994->jack_mic = false; + wm8994->headphone_detected = false; wm8994->mic_detecting = true; goto out; } - if (wm8994->mic_detecting) - wm8994->mic_id_cb(wm8994->mic_id_cb_data, reg); - else + wm8994->mic_status = reg; + id_delay = wm8994->wm8994->pdata.mic_id_delay; + + if (wm8994->mic_detecting) { + if (control->type == WM8958) { + /* Set mic-bias high during detection phase (micb_en_delay) */ + /* 0 == Continuous */ + dev_dbg(codec->dev, "Set MICBIAS High, for micb_en_delay time\n"); + snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, + WM8958_MICD_BIAS_STARTTIME_MASK | + WM8958_MICD_RATE_MASK, 0); + } + + schedule_delayed_work(&wm8994->mic_complete_work, + msecs_to_jiffies(id_delay)); + } else { wm8958_button_det(codec, reg); + } out: pm_runtime_put(codec->dev); @@ -3888,6 +4131,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) struct wm8994 *control = dev_get_drvdata(codec->dev->parent); struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct snd_soc_dapm_context *dapm = &codec->dapm; + unsigned int dcs_done_irq; unsigned int reg; int ret, i; @@ -3899,6 +4143,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) mutex_init(&wm8994->accdet_lock); INIT_DELAYED_WORK(&wm8994->jackdet_bootstrap, wm1811_jackdet_bootstrap); + INIT_DELAYED_WORK(&wm8994->open_circuit_work, + wm8958_open_circuit_work); switch (control->type) { case WM8994: @@ -3907,10 +4153,16 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) case WM1811: INIT_DELAYED_WORK(&wm8994->mic_work, wm1811_mic_work); break; + case WM8958: + INIT_DELAYED_WORK(&wm8994->micd_set_custom_rate_work, + wm8958_micd_set_custom_rate_work); + break; default: break; } + INIT_DELAYED_WORK(&wm8994->mic_complete_work, wm8958_mic_work); + for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) init_completion(&wm8994->fll_locked[i]); @@ -3983,6 +4235,9 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_TEMP_SHUT, wm8994_temp_shut, "Thermal shutdown", codec); + dcs_done_irq = regmap_irq_get_virq(wm8994->wm8994->irq_data, + WM8994_IRQ_DCS_DONE); + irq_set_status_flags(dcs_done_irq, IRQ_NOAUTOEN); ret = wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_DCS_DONE, wm_hubs_dcs_done, "DC servo done", &wm8994->hubs); @@ -4089,7 +4344,6 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) } if ((reg & WM8994_GPN_FN_MASK) != WM8994_GP_FN_PIN_SPECIFIC) { wm8994->lrclk_shared[0] = 1; - wm8994_dai[0].symmetric_rates = 1; } else { wm8994->lrclk_shared[0] = 0; } @@ -4219,6 +4473,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) } wm_hubs_add_analogue_routes(codec, 0, 0); + enable_irq(dcs_done_irq); snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); switch (control->type) { @@ -4261,6 +4516,11 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) break; } + /* Make sure FIFO errors are masked */ + snd_soc_update_bits(codec, WM8994_INTERRUPT_STATUS_2_MASK, + WM8994_IM_FIFOS_ERR_EINT_MASK, + 1 << WM8994_IM_FIFOS_ERR_EINT_SHIFT); + return 0; err_irq: @@ -4365,6 +4625,11 @@ static int wm8994_remove(struct platform_device *pdev) static int wm8994_suspend(struct device *dev) { struct wm8994_priv *wm8994 = dev_get_drvdata(dev); + struct wm8994 *control = wm8994->wm8994; + struct snd_soc_codec *codec = wm8994->hubs.codec; + unsigned int reg; + int ret; + /* Drop down to power saving mode when system is suspended */ if (wm8994->jackdet && !wm8994->active_refcount) @@ -4372,18 +4637,62 @@ static int wm8994_suspend(struct device *dev) WM1811_JACKDET_MODE_MASK, wm8994->jackdet_mode); + /* Disable the MIC Detection when suspended */ + if ((control->type == WM8958) && wm8994->mic_id_cb) { + + reg = snd_soc_read(codec, WM8958_MIC_DETECT_3); + + dev_dbg(codec->dev, "%s: WM8958_MIC_DETECT_3 0x%x\n", __func__, reg); + dev_dbg(codec->dev, "mic_detect %d jack_mic %d headphone %d\n", + wm8994->mic_detecting, wm8994->jack_mic, + wm8994->headphone_detected); + + if (!(wm8994->jack_mic) && !(wm8994->headphone_detected)) { + + dev_dbg(codec->dev, "Jack not connected..Mask interrupt\n"); + snd_soc_write(codec, WM8994_INTERRUPT_CONTROL, 0x01); + + ret = regcache_sync_region(wm8994->wm8994->regmap, + WM8994_INTERRUPT_CONTROL, + WM8994_INTERRUPT_CONTROL); + if (ret != 0) + dev_err(dev, "Failed to sync register: %d\n", ret); + synchronize_irq(control->irq); + + dev_dbg(codec->dev, "Disable MIC Detection!!!\n"); + snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, + WM8958_MICD_ENA, 0); + + snd_soc_dapm_disable_pin(&codec->dapm, "CLK_SYS"); + snd_soc_dapm_sync(&codec->dapm); + } + } + return 0; } static int wm8994_resume(struct device *dev) { struct wm8994_priv *wm8994 = dev_get_drvdata(dev); + struct wm8994 *control = wm8994->wm8994; + struct snd_soc_codec *codec = wm8994->hubs.codec; if (wm8994->jackdet && wm8994->jackdet_mode) regmap_update_bits(wm8994->wm8994->regmap, WM8994_ANTIPOP_2, WM1811_JACKDET_MODE_MASK, WM1811_JACKDET_MODE_AUDIO); + /* Enable the MIC Detection when resumed */ + if ((control->type == WM8958) && wm8994->mic_id_cb) { + dev_dbg(codec->dev, "Enable MIC Detection!!!\n"); + snd_soc_dapm_force_enable_pin(&codec->dapm, "CLK_SYS"); + snd_soc_dapm_sync(&codec->dapm); + + snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, + WM8958_MICD_ENA, WM8958_MICD_ENA); + snd_soc_write(codec, WM8994_INTERRUPT_CONTROL, 0x00); + } + return 0; } #endif diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h index 55ddf4d57d9b..0a834df0fff0 100644 --- a/sound/soc/codecs/wm8994.h +++ b/sound/soc/codecs/wm8994.h @@ -41,6 +41,7 @@ enum wm8994_vmid_mode { typedef void (*wm1811_micdet_cb)(void *data); typedef void (*wm1811_mic_id_cb)(void *data, u16 status); +typedef void (*wm8958_micd_set_custom_rate_cb)(struct snd_soc_codec *codec); int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, int micbias); @@ -55,6 +56,10 @@ int wm8958_aif_ev(struct snd_soc_dapm_widget *w, void wm8958_dsp2_init(struct snd_soc_codec *codec); +int wm8958_micd_set_custom_rate(struct snd_soc_codec *codec, + wm8958_micd_set_custom_rate_cb micd_custom_rate_cb, + void *micd_custom_rate_cb_data); + struct wm8994_micdet { struct snd_soc_jack *jack; bool detecting; @@ -86,6 +91,7 @@ struct wm8994_priv { bool fll_locked_irq; bool fll_byp; bool clk_has_run; + int slots; int vmid_refcount; int active_refcount; @@ -134,8 +140,14 @@ struct wm8994_priv { struct mutex accdet_lock; struct wm8994_micdet micdet[2]; struct delayed_work mic_work; + struct delayed_work open_circuit_work; + struct delayed_work mic_complete_work; + struct delayed_work micd_set_custom_rate_work; + + u16 mic_status; bool mic_detecting; bool jack_mic; + bool headphone_detected; int btn_mask; bool jackdet; int jackdet_mode; @@ -146,6 +158,8 @@ struct wm8994_priv { void *micd_cb_data; wm1811_mic_id_cb mic_id_cb; void *mic_id_cb_data; + wm8958_micd_set_custom_rate_cb micd_custom_rate_cb; + void *micd_custom_rate_cb_data; unsigned int aif1clk_enable:1; unsigned int aif2clk_enable:1; diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index f5d81b948759..2874ec9a23ab 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -530,6 +530,7 @@ static int hp_supply_event(struct snd_soc_dapm_widget *w, hubs->hp_startup_mode); break; } + break; case SND_SOC_DAPM_PRE_PMD: snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1, @@ -1222,11 +1223,6 @@ int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *codec, WM8993_LINEOUT2_MODE, WM8993_LINEOUT2_MODE); - if (!lineout1_diff && !lineout2_diff) - snd_soc_update_bits(codec, WM8993_ANTIPOP1, - WM8993_LINEOUT_VMID_BUF_ENA, - WM8993_LINEOUT_VMID_BUF_ENA); - if (lineout1fb) snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL, WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB); @@ -1252,6 +1248,13 @@ void wm_hubs_vmid_ena(struct snd_soc_codec *codec) struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec); int val = 0; + if ((hubs->lineout1_se && hubs->lineout2_se) && + (hubs->lineout1n_ena || hubs->lineout1p_ena || + hubs->lineout2n_ena || hubs->lineout2p_ena)) + snd_soc_update_bits(codec, WM8993_ANTIPOP1, + WM8993_LINEOUT_VMID_BUF_ENA, + WM8993_LINEOUT_VMID_BUF_ENA); + if (hubs->lineout1_se) val |= WM8993_LINEOUT1N_ENA | WM8993_LINEOUT1P_ENA; @@ -1281,6 +1284,13 @@ void wm_hubs_set_bias_level(struct snd_soc_codec *codec, val = 0; mask = 0; + if ((hubs->lineout1_se && hubs->lineout2_se) && + (hubs->lineout1n_ena || hubs->lineout1p_ena || + hubs->lineout2n_ena || hubs->lineout2p_ena)) + snd_soc_update_bits(codec, WM8993_ANTIPOP1, + WM8993_LINEOUT_VMID_BUF_ENA, + WM8993_LINEOUT_VMID_BUF_ENA); + if (hubs->lineout1_se) mask |= WM8993_LINEOUT1N_ENA | WM8993_LINEOUT1P_ENA; diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig new file mode 100644 index 000000000000..b6efaacddd6d --- /dev/null +++ b/sound/soc/intel/Kconfig @@ -0,0 +1,58 @@ +config SND_MFLD_MACHINE + tristate "SOC Machine Audio driver for Intel Medfield MID platform" + depends on INTEL_SCU_IPC && INTEL_SCU_IPC_UTIL && X86 && GPIO_LANGWELL + depends on MSIC_GPADC + select SND_SOC_SN95031 + select SND_SST_PLATFORM + select SND_SST_MACHINE + select SND_INTEL_SST + default n + help + This adds support for ASoC machine driver for Intel(R) MID Medfield platform + used as alsa device in audio subsystem in Intel(R) MID devices + Say Y if you have such a device + If unsure select "N". + +config SND_MRFLD_MACHINE + tristate "SOC Machine Audio driver for Intel Merrifield MID platform" + depends on INTEL_SCU_IPC && X86 + select SND_SOC_LM49453 + select SND_SOC_WM8994 + select MFD_CORE + select MFD_WM8994 + select REGULATOR_WM8994 + select SND_SST_PLATFORM + select SND_SST_MACHINE + select SND_INTEL_SST + select SND_EFFECTS_OFFLOAD + default n + help + This adds support for ASoC machine driver for Intel(R) MID Merrifield platform + used as alsa device in audio substem in Intel(R) MID devices + Say Y if you have such a device + If unsure select "N". + +config SND_INTEL_SST + tristate + +config SND_SST_PLATFORM + tristate + +config SND_SOC_COMMS_SSP + depends on SND_INTEL_MID_I2S + tristate "Use ASOC framework to drive AudioComms SSP BT and Modem" + help + Sound SOC cards usually used for BT VOIP and MODEM MIXING use cases. + This will add devices for these uses cases in the list of alsa cards. + Say Y if you need these sound cards (BT chipset or Modem present). + Requires to enable the INTEL_MID_I2S low level SSP I2S driver. + +config SST_MRFLD_DPCM + bool "Use DPCM based Merrifield Machine Audio driver" + default n + help + This adds an option to enable the DPCM based MRFLD machine driver + +config SND_SST_MACHINE + tristate + diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile new file mode 100644 index 000000000000..02d512013c28 --- /dev/null +++ b/sound/soc/intel/Makefile @@ -0,0 +1,22 @@ +#EXTRA CFLAGS +ccflags-y += -Werror + +ifeq (${TARGET_BUILD_VARIANT},$(filter ${TARGET_BUILD_VARIANT}, eng)) +ccflags-y += -DCONFIG_SND_VERBOSE_PRINTK -DCONFIG_SND_DEBUG -DCONFIG_SND_DEBUG_VERBOSE +endif + +# SST Platform Driver +PLATFORM_LIBS = platform-libs/controls_v1.o platform-libs/controls_v2.o platform-libs/controls_v2_dpcm.o \ + platform-libs/ipc_lib_v2.o + +snd-soc-sst-platform-objs := pcm.o compress.o effects.o $(PLATFORM_LIBS) +obj-$(CONFIG_SND_SST_PLATFORM) += snd-soc-sst-platform.o + +# Relevant Machine driver +obj-$(CONFIG_SND_SST_MACHINE) += board/ + +# DSP driver +obj-$(CONFIG_SND_INTEL_SST) += sst/ + +# Audio Comms +obj-$(CONFIG_SND_SOC_COMMS_SSP) += ssp/ diff --git a/sound/soc/intel/board/Makefile b/sound/soc/intel/board/Makefile new file mode 100644 index 000000000000..3581ae123580 --- /dev/null +++ b/sound/soc/intel/board/Makefile @@ -0,0 +1,14 @@ +#EXTRA CFLAGS +ccflags-y += -Werror + +# Merrifield board +snd-merr-saltbay-wm8958-objs := merr_saltbay_wm8958.o +snd-merr-dpcm-wm8958-objs := merr_dpcm_wm8958.o +snd-merr-dpcm-dummy-objs := merr_dpcm_dummy.o + +ifdef CONFIG_SST_MRFLD_DPCM + obj-$(CONFIG_SND_MRFLD_MACHINE) += snd-merr-dpcm-wm8958.o + obj-$(CONFIG_SND_MRFLD_MACHINE) += snd-merr-dpcm-dummy.o +else + obj-$(CONFIG_SND_MRFLD_MACHINE) += snd-merr-saltbay-wm8958.o +endif diff --git a/sound/soc/intel/board/merr_dpcm_dummy.c b/sound/soc/intel/board/merr_dpcm_dummy.c new file mode 100644 index 000000000000..3cb879c73960 --- /dev/null +++ b/sound/soc/intel/board/merr_dpcm_dummy.c @@ -0,0 +1,233 @@ +/* + * ASoc Dummy DPCM Machine driver for Intel Edison MID platform + * + * Copyright (C) 2014 Intel Corp + * Author: Michael Soares + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include +#include +#include + +static bool wm_hardware_avail = true; +module_param(wm_hardware_avail, bool, S_IRUSR); +MODULE_PARM_DESC(wm_hardware_avail, "1 in cmdline to use dummy version"); + +#ifdef CONFIG_PM_SLEEP +static int snd_merr_dpcm_prepare(struct device *dev) +{ + pr_debug("In %s device name\n", __func__); + snd_soc_suspend(dev); + return 0; +} + +static void snd_merr_dpcm_complete(struct device *dev) +{ + pr_debug("In %s\n", __func__); + snd_soc_resume(dev); + return; +} + +static int snd_merr_dpcm_poweroff(struct device *dev) +{ + pr_debug("In %s\n", __func__); + snd_soc_poweroff(dev); + return 0; +} +#else +#define snd_merr_dpcm_prepare NULL +#define snd_merr_dpcm_complete NULL +#define snd_merr_dpcm_poweroff NULL +#endif + +static unsigned int rates_48000[] = { + 48000, +}; + +static struct snd_pcm_hw_constraint_list constraints_48000 = { + .count = ARRAY_SIZE(rates_48000), + .list = rates_48000, +}; + +static int merr_dummy_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_48000); +} + +static struct snd_soc_ops merr_dummy_ops = { + .startup = merr_dummy_startup, +}; + +static int merr_codec_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("Invoked %s for dailink %s\n", __func__, rtd->dai_link->name); + + /* The DSP will convert the FE rate to 48k, stereo, 24bits */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + /* set SSP2 to 24-bit */ + snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - + SNDRV_PCM_HW_PARAM_FIRST_MASK], + SNDRV_PCM_FORMAT_S24_LE); + return 0; +} + +struct snd_soc_dai_link merr_msic_dailink[] = { + [MERR_DPCM_AUDIO] = { + .name = "Media Audio Port", + .stream_name = "Edison Audio", + .cpu_dai_name = "Headset-cpu-dai", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "sst-platform", + .ignore_suspend = 1, + .dynamic = 1, + .ops = &merr_dummy_ops, + }, + /* back ends */ + { + .name = "SSP2-Codec", + .be_id = 1, + .cpu_dai_name = "ssp2-codec", + .platform_name = "sst-platform", + .no_pcm = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .be_hw_params_fixup = merr_codec_fixup, + .ignore_suspend = 1, + }, + { + .name = "SSP1-BT", + .be_id = 2, + .cpu_dai_name = "snd-soc-dummy-dai", + .platform_name = "snd-soc-dummy", + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .ignore_suspend = 1, + }, + +}; + +static const struct snd_soc_dapm_route map[] = { + { "Dummy Playback", NULL, "codec_out0" }, + { "Dummy Playback", NULL, "codec_out1" }, + { "codec_in0", NULL, "Dummy Capture" }, + { "codec_in1", NULL, "Dummy Capture" }, +}; + +/* SoC card */ +static struct snd_soc_card snd_soc_card_merr = { + .name = "dummy-audio", + .dai_link = merr_msic_dailink, + .num_links = ARRAY_SIZE(merr_msic_dailink), + .dapm_routes = map, + .num_dapm_routes = ARRAY_SIZE(map), +}; + +static int snd_merr_dpcm_probe(struct platform_device *pdev) +{ + int ret_val = 0; + pr_debug("%s enter\n", __func__); + + /* register the soc card */ + snd_soc_card_merr.dev = &pdev->dev; + ret_val = snd_soc_register_card(&snd_soc_card_merr); + if (ret_val) { + pr_err("snd_soc_register_card failed %d\n", ret_val); + return ret_val; + } + platform_set_drvdata(pdev, &snd_soc_card_merr); + pr_info("%s successful\n", __func__); + return ret_val; +} + +static int snd_merr_dpcm_remove(struct platform_device *pdev) +{ + struct snd_soc_card *soc_card = platform_get_drvdata(pdev); + pr_err("snd_merr_dpcm_remove"); + snd_soc_unregister_card(soc_card); + platform_set_drvdata(pdev, NULL); + return 0; +} + +const struct dev_pm_ops snd_merr_dpcm_mc_pm_ops = { + .prepare = snd_merr_dpcm_prepare, + .complete = snd_merr_dpcm_complete, + .poweroff = snd_merr_dpcm_poweroff, +}; + +static struct platform_driver snd_merr_dpcm_drv = { + .driver = { + .owner = THIS_MODULE, + .name = "merr_dpcm_dummy", + .pm = &snd_merr_dpcm_mc_pm_ops, + }, + .probe = snd_merr_dpcm_probe, + .remove = snd_merr_dpcm_remove, +}; + +static int __init snd_merr_dpcm_dummy_init(void) +{ + int err; + struct platform_device *device; + + /* Check from cmdline if hardware is plugged to the board */ + if (wm_hardware_avail) { + pr_info("dpcm dummy is not supported according to cmdline"); + return -EINVAL; + } + + err = platform_driver_register(&snd_merr_dpcm_drv); + if (err < 0) + return err; + + device = platform_device_register_simple("merr_dpcm_dummy", + 0, NULL, 0); + if (IS_ERR(device)) + return -ENODEV; + + return 0; +} +late_initcall(snd_merr_dpcm_dummy_init); + +static void __exit snd_merr_dpcm_dummy_exit(void) +{ + return platform_driver_unregister(&snd_merr_dpcm_drv); +} +module_exit(snd_merr_dpcm_dummy_exit); + +MODULE_DESCRIPTION("ASoC Intel(R) Edison dummy MID Machine driver"); +MODULE_AUTHOR("Michael Soares "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:merr_dpcm_dummy"); + diff --git a/sound/soc/intel/board/merr_dpcm_wm8958.c b/sound/soc/intel/board/merr_dpcm_wm8958.c new file mode 100644 index 000000000000..9d3aba8b7e79 --- /dev/null +++ b/sound/soc/intel/board/merr_dpcm_wm8958.c @@ -0,0 +1,932 @@ +/* + * merr_dpcm_wm8958.c - ASoc DPCM Machine driver for Intel Merrfield MID platform + * + * Copyright (C) 2013 Intel Corp + * Author: Vinod Koul + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "../../codecs/wm8994.h" + +/* Codec PLL output clk rate */ +#define CODEC_SYSCLK_RATE 24576000 +/* Input clock to codec at MCLK1 PIN */ +#define CODEC_IN_MCLK1_RATE 19200000 +/* Input clock to codec at MCLK2 PIN */ +#define CODEC_IN_MCLK2_RATE 32768 +/* define to select between MCLK1 and MCLK2 input to codec as its clock */ +#define CODEC_IN_MCLK1 1 +#define CODEC_IN_MCLK2 2 + +/* Register address for OSC Clock */ +#define MERR_OSC_CLKOUT_CTRL0_REG_ADDR 0xFF00BC04 +/* Size of osc clock register */ +#define MERR_OSC_CLKOUT_CTRL0_REG_SIZE 4 + +struct mrfld_8958_mc_private { + struct snd_soc_jack jack; + int jack_retry; + u8 pmic_id; + void __iomem *osc_clk0_reg; +}; + + +/* set_osc_clk0- enable/disables the osc clock0 + * addr: address of the register to write to + * enable: bool to enable or disable the clock + */ +static inline void set_soc_osc_clk0(void __iomem *addr, bool enable) +{ + u32 osc_clk_ctrl; + + osc_clk_ctrl = readl(addr); + if (enable) + osc_clk_ctrl |= BIT(31); + else + osc_clk_ctrl &= ~(BIT(31)); + + pr_debug("%s: enable:%d val 0x%x\n", __func__, enable, osc_clk_ctrl); + + writel(osc_clk_ctrl, addr); +} + +static inline struct snd_soc_codec *mrfld_8958_get_codec(struct snd_soc_card *card) +{ + bool found = false; + struct snd_soc_codec *codec; + + list_for_each_entry(codec, &card->codec_dev_list, card_list) { + if (!strstr(codec->name, "wm8994-codec")) { + pr_debug("codec was %s", codec->name); + continue; + } else { + found = true; + break; + } + } + if (found == false) { + pr_warn("%s: cant find codec", __func__); + return NULL; + } + return codec; +} + +/* TODO: find better way of doing this */ +static struct snd_soc_dai *find_codec_dai(struct snd_soc_card *card, const char *dai_name) +{ + int i; + for (i = 0; i < card->num_rtd; i++) { + if (!strcmp(card->rtd[i].codec_dai->name, dai_name)) + return card->rtd[i].codec_dai; + } + pr_err("%s: unable to find codec dai\n", __func__); + /* this should never occur */ + WARN_ON(1); + return NULL; +} + +/* Function to switch the input clock for codec, When audio is in + * progress input clock to codec will be through MCLK1 which is 19.2MHz + * while in off state input clock to codec will be through 32KHz through + * MCLK2 + * card : Sound card structure + * src : Input clock source to codec + */ +static int mrfld_8958_set_codec_clk(struct snd_soc_card *card, int src) +{ + struct snd_soc_dai *aif1_dai = find_codec_dai(card, "wm8994-aif1"); + int ret; + + if (!aif1_dai) + return -ENODEV; + + switch (src) { + case CODEC_IN_MCLK1: + /* Turn ON the PLL to generate required sysclk rate + * from MCLK1 */ + ret = snd_soc_dai_set_pll(aif1_dai, + WM8994_FLL1, WM8994_FLL_SRC_MCLK1, + CODEC_IN_MCLK1_RATE, CODEC_SYSCLK_RATE); + if (ret < 0) { + pr_err("Failed to start FLL: %d\n", ret); + return ret; + } + /* Switch to MCLK1 input */ + ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_FLL1, + CODEC_SYSCLK_RATE, SND_SOC_CLOCK_IN); + if (ret < 0) { + pr_err("Failed to set codec sysclk configuration %d\n", + ret); + return ret; + } + break; + case CODEC_IN_MCLK2: + /* Switch to MCLK2 */ + ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2, + 32768, SND_SOC_CLOCK_IN); + if (ret < 0) { + pr_err("Failed to switch to MCLK2: %d", ret); + return ret; + } + /* Turn off PLL for MCLK1 */ + ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1, 0, 0, 0); + if (ret < 0) { + pr_err("Failed to stop the FLL: %d", ret); + return ret; + } + break; + default: + return -EINVAL; + } + return 0; +} + +static int mrfld_wm8958_set_clk_fmt(struct snd_soc_dai *codec_dai) +{ + unsigned int fmt; + int ret = 0; + struct snd_soc_card *card = codec_dai->card; + struct mrfld_8958_mc_private *ctx = snd_soc_card_get_drvdata(card); + + /* Enable the osc clock at start so that it gets settling time */ + set_soc_osc_clk0(ctx->osc_clk0_reg, true); + + pr_err("setting snd_soc_dai_set_tdm_slot\n"); + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0, 0, 4, SNDRV_PCM_FORMAT_S24_LE); + if (ret < 0) { + pr_err("can't set codec pcm format %d\n", ret); + return ret; + } + + /* WM8958 slave Mode */ + fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF + | SND_SOC_DAIFMT_CBS_CFS; + ret = snd_soc_dai_set_fmt(codec_dai, fmt); + if (ret < 0) { + pr_err("can't set codec DAI configuration %d\n", ret); + return ret; + } + + /* FIXME: move this to SYS_CLOCK event handler when codec driver + * dependency is clean. + */ + /* Switch to 19.2MHz MCLK1 input clock for codec */ + ret = mrfld_8958_set_codec_clk(card, CODEC_IN_MCLK1); + + return ret; +} + +static int mrfld_8958_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; + + if (!strcmp(codec_dai->name, "wm8994-aif1")) + return mrfld_wm8958_set_clk_fmt(codec_dai); + return 0; +} + +static int mrfld_wm8958_compr_set_params(struct snd_compr_stream *cstream) +{ + return 0; +} + +static const struct snd_soc_pcm_stream mrfld_wm8958_dai_params = { + .formats = SNDRV_PCM_FMTBIT_S24_LE, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, +}; + +static int merr_codec_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + pr_debug("Invoked %s for dailink %s\n", __func__, rtd->dai_link->name); + + /* The DSP will covert the FE rate to 48k, stereo, 24bits */ + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + + /* set SSP2 to 24-bit */ + snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - + SNDRV_PCM_HW_PARAM_FIRST_MASK], + SNDRV_PCM_FORMAT_S24_LE); + return 0; +} + +static int mrfld_8958_set_bias_level(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) +{ + struct snd_soc_dai *aif1_dai = find_codec_dai(card, "wm8994-aif1"); + int ret = 0; + + if (!aif1_dai) + return -ENODEV; + + if (dapm->dev != aif1_dai->dev) + return 0; + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (card->dapm.bias_level == SND_SOC_BIAS_STANDBY) + + ret = mrfld_wm8958_set_clk_fmt(aif1_dai); + break; + default: + break; + } + pr_debug("%s card(%s)->bias_level %u\n", __func__, card->name, + card->dapm.bias_level); + return ret; +} + +static int mrfld_8958_set_bias_level_post(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) +{ + struct snd_soc_dai *aif1_dai = find_codec_dai(card, "wm8994-aif1"); + struct mrfld_8958_mc_private *ctx = snd_soc_card_get_drvdata(card); + int ret = 0; + + if (!aif1_dai) + return -ENODEV; + + if (dapm->dev != aif1_dai->dev) + return 0; + + switch (level) { + case SND_SOC_BIAS_STANDBY: + /* We are in stabdba down so */ + /* Switch to 32KHz MCLK2 input clock for codec + */ + ret = mrfld_8958_set_codec_clk(card, CODEC_IN_MCLK2); + /* Turn off 19.2MHz soc osc clock */ + set_soc_osc_clk0(ctx->osc_clk0_reg, false); + break; + default: + break; + } + card->dapm.bias_level = level; + pr_debug("%s card(%s)->bias_level %u\n", __func__, card->name, + card->dapm.bias_level); + return ret; +} + +#define PMIC_ID_ADDR 0x00 +#define PMIC_CHIP_ID_A0_VAL 0xC0 + +static int mrfld_8958_set_vflex_vsel(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ +#define VFLEXCNT 0xAB +#define VFLEXVSEL_5V 0x01 +#define VFLEXVSEL_B0_VSYS_PT 0x80 /* B0: Vsys pass-through */ +#define VFLEXVSEL_A0_4P5V 0x41 /* A0: 4.5V */ + + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct mrfld_8958_mc_private *ctx = snd_soc_card_get_drvdata(card); + + u8 vflexvsel, pmic_id = ctx->pmic_id; + int retval = 0; + + pr_debug("%s: ON? %d\n", __func__, SND_SOC_DAPM_EVENT_ON(event)); + + vflexvsel = (pmic_id == PMIC_CHIP_ID_A0_VAL) ? VFLEXVSEL_A0_4P5V : VFLEXVSEL_B0_VSYS_PT; + pr_debug("pmic_id %#x vflexvsel %#x\n", pmic_id, + SND_SOC_DAPM_EVENT_ON(event) ? VFLEXVSEL_5V : vflexvsel); + + if (SND_SOC_DAPM_EVENT_ON(event)) + retval = intel_scu_ipc_iowrite8(VFLEXCNT, VFLEXVSEL_5V); + else if (SND_SOC_DAPM_EVENT_OFF(event)) + retval = intel_scu_ipc_iowrite8(VFLEXCNT, vflexvsel); + if (retval) + pr_err("Error writing to VFLEXCNT register\n"); + + return retval; +} + +static const struct snd_soc_dapm_widget widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_MIC("AMIC", NULL), + SND_SOC_DAPM_MIC("DMIC", NULL), + SND_SOC_DAPM_SUPPLY("VFLEXCNT", SND_SOC_NOPM, 0, 0, + mrfld_8958_set_vflex_vsel, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route map[] = { + { "Headphones", NULL, "HPOUT1L" }, + { "Headphones", NULL, "HPOUT1R" }, + + /* saltbay uses 2 DMICs, other configs may use more so change below + * accordingly + */ + { "DMIC1DAT", NULL, "DMIC" }, + { "DMIC2DAT", NULL, "DMIC" }, + /*{ "DMIC3DAT", NULL, "DMIC" },*/ + /*{ "DMIC4DAT", NULL, "DMIC" },*/ + + /* MICBIAS2 is connected as Bias for AMIC so we link it + * here. Also AMIC wires up to IN1LP pin. + * DMIC is externally connected to 1.8V rail, so no link rqd. + */ + { "AMIC", NULL, "MICBIAS2" }, + { "IN1LP", NULL, "AMIC" }, + + /* SWM map link the SWM outs to codec AIF */ + { "AIF1 Playback", NULL, "codec_out0" }, + { "AIF1 Playback", NULL, "codec_out1" }, + { "codec_in0", NULL, "AIF1 Capture" }, + { "codec_in1", NULL, "AIF1 Capture" }, + + { "AIF1 Playback", NULL, "VFLEXCNT" }, + { "AIF1 Capture", NULL, "VFLEXCNT" }, +}; + +static const struct wm8958_micd_rate micdet_rates[] = { + { 32768, true, 1, 4 }, + { 32768, false, 1, 1 }, + { 44100 * 256, true, 7, 10 }, + { 44100 * 256, false, 7, 10 }, +}; + +static void wm8958_custom_micd_set_rate(struct snd_soc_codec *codec) +{ + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + struct wm8994 *control = dev_get_drvdata(codec->dev->parent); + int best, i, sysclk, val; + bool idle; + const struct wm8958_micd_rate *rates; + int num_rates; + + idle = !wm8994->jack_mic; + + sysclk = snd_soc_read(codec, WM8994_CLOCKING_1); + if (sysclk & WM8994_SYSCLK_SRC) + sysclk = wm8994->aifclk[1]; + else + sysclk = wm8994->aifclk[0]; + + if (control->pdata.micd_rates) { + rates = control->pdata.micd_rates; + num_rates = control->pdata.num_micd_rates; + } else { + rates = micdet_rates; + num_rates = ARRAY_SIZE(micdet_rates); + } + + best = 0; + for (i = 0; i < num_rates; i++) { + if (rates[i].idle != idle) + continue; + if (abs(rates[i].sysclk - sysclk) < + abs(rates[best].sysclk - sysclk)) + best = i; + else if (rates[best].idle != idle) + best = i; + } + + val = rates[best].start << WM8958_MICD_BIAS_STARTTIME_SHIFT + | rates[best].rate << WM8958_MICD_RATE_SHIFT; + + dev_dbg(codec->dev, "MICD rate %d,%d for %dHz %s\n", + rates[best].start, rates[best].rate, sysclk, + idle ? "idle" : "active"); + + snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, + WM8958_MICD_BIAS_STARTTIME_MASK | + WM8958_MICD_RATE_MASK, val); +} + +static void wm8958_custom_mic_id(void *data, u16 status) +{ + struct snd_soc_codec *codec = data; + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "wm8958 custom mic id called with status %x\n", + status); + + /* Either nothing present or just starting detection */ + if (!(status & WM8958_MICD_STS)) { + /* If nothing present then clear our statuses */ + dev_dbg(codec->dev, "Detected open circuit\n"); + + schedule_delayed_work(&wm8994->open_circuit_work, + msecs_to_jiffies(2500)); + return; + } + + schedule_delayed_work(&wm8994->micd_set_custom_rate_work, + msecs_to_jiffies(wm8994->wm8994->pdata.micb_en_delay)); + + /* If the measurement is showing a high impedence we've got a + * microphone. + */ + if (status & 0x600) { + dev_dbg(codec->dev, "Detected microphone\n"); + + wm8994->mic_detecting = false; + wm8994->jack_mic = true; + + snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADSET, + SND_JACK_HEADSET); + } + + + if (status & 0xfc) { + dev_dbg(codec->dev, "Detected headphone\n"); + + /* Partial inserts of headsets with complete insert + * after an indeterminate amount of time require + * continouous micdetect enabled (until open circuit + * or headset is detected) + * */ + wm8994->mic_detecting = true; + + snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADPHONE, + SND_JACK_HEADSET); + } +} + +static int mrfld_8958_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + unsigned int fmt; + struct snd_soc_codec *codec; + struct snd_soc_card *card = runtime->card; + struct snd_soc_dai *aif1_dai = find_codec_dai(card, "wm8994-aif1"); + struct mrfld_8958_mc_private *ctx = snd_soc_card_get_drvdata(card); + + if (!aif1_dai) + return -ENODEV; + + pr_debug("Entry %s\n", __func__); + + ret = snd_soc_dai_set_tdm_slot(aif1_dai, 0, 0, 4, SNDRV_PCM_FORMAT_S24_LE); + if (ret < 0) { + pr_err("can't set codec pcm format %d\n", ret); + return ret; + } + + /* WM8958 slave Mode */ + fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF + | SND_SOC_DAIFMT_CBS_CFS; + ret = snd_soc_dai_set_fmt(aif1_dai, fmt); + if (ret < 0) { + pr_err("can't set codec DAI configuration %d\n", ret); + return ret; + } + + mrfld_8958_set_bias_level(card, &card->dapm, SND_SOC_BIAS_OFF); + card->dapm.idle_bias_off = true; + + /* these pins are not used in SB config so mark as nc + * + * LINEOUT1, 2 + * IN1R + * DMICDAT2 + */ + snd_soc_dapm_nc_pin(&card->dapm, "DMIC2DAT"); + snd_soc_dapm_nc_pin(&card->dapm, "LINEOUT1P"); + snd_soc_dapm_nc_pin(&card->dapm, "LINEOUT1N"); + snd_soc_dapm_nc_pin(&card->dapm, "LINEOUT2P"); + snd_soc_dapm_nc_pin(&card->dapm, "LINEOUT2N"); + snd_soc_dapm_nc_pin(&card->dapm, "IN1RN"); + snd_soc_dapm_nc_pin(&card->dapm, "IN1RP"); + + snd_soc_dapm_sync(&card->dapm); + + codec = mrfld_8958_get_codec(card); + if (!codec) { + pr_err("%s: we didnt find the codec pointer!\n", __func__); + return 0; + } + + ctx->jack_retry = 0; + ret = snd_soc_jack_new(codec, "Intel MID Audio Jack", + SND_JACK_HEADSET | SND_JACK_HEADPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1, + &ctx->jack); + if (ret) { + pr_err("jack creation failed\n"); + return ret; + } + + snd_jack_set_key(ctx->jack.jack, SND_JACK_BTN_1, KEY_MEDIA); + snd_jack_set_key(ctx->jack.jack, SND_JACK_BTN_0, KEY_MEDIA); + + wm8958_mic_detect(codec, &ctx->jack, NULL, NULL, + wm8958_custom_mic_id, codec); + + wm8958_micd_set_custom_rate(codec, wm8958_custom_micd_set_rate, codec); + + snd_soc_update_bits(codec, WM8994_AIF1_DAC1_FILTERS_1, WM8994_AIF1DAC1_MUTE, 0); + snd_soc_update_bits(codec, WM8994_AIF1_DAC2_FILTERS_1, WM8994_AIF1DAC2_MUTE, 0); + + /* Micbias1 is always off, so for pm optimizations make sure the micbias1 + * discharge bit is set to floating to avoid discharge in disable state + */ + snd_soc_update_bits(codec, WM8958_MICBIAS1, WM8958_MICB1_DISCH, 0); + + return 0; +} + +static unsigned int rates_8000_16000[] = { + 8000, + 16000, +}; + +static struct snd_pcm_hw_constraint_list constraints_8000_16000 = { + .count = ARRAY_SIZE(rates_8000_16000), + .list = rates_8000_16000, +}; +static unsigned int rates_48000[] = { + 48000, +}; + +static struct snd_pcm_hw_constraint_list constraints_48000 = { + .count = ARRAY_SIZE(rates_48000), + .list = rates_48000, +}; +static int mrfld_8958_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_48000); +} + +static struct snd_soc_ops mrfld_8958_ops = { + .startup = mrfld_8958_startup, +}; +static int mrfld_8958_8k_16k_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_8000_16000); +} + +static struct snd_soc_ops mrfld_8958_8k_16k_ops = { + .startup = mrfld_8958_8k_16k_startup, + .hw_params = mrfld_8958_hw_params, +}; + +static struct snd_soc_ops mrfld_8958_be_ssp2_ops = { + .hw_params = mrfld_8958_hw_params, +}; +static struct snd_soc_compr_ops mrfld_compr_ops = { + .set_params = mrfld_wm8958_compr_set_params, +}; + +struct snd_soc_dai_link mrfld_8958_msic_dailink[] = { + [MERR_DPCM_AUDIO] = { + .name = "Merrifield Audio Port", + .stream_name = "Saltbay Audio", + .cpu_dai_name = "Headset-cpu-dai", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "sst-platform", + .init = mrfld_8958_init, + .ignore_suspend = 1, + .dynamic = 1, + .ops = &mrfld_8958_ops, + }, + [MERR_DPCM_DB] = { + .name = "Merrifield DB Audio Port", + .stream_name = "Deep Buffer Audio", + .cpu_dai_name = "Deepbuffer-cpu-dai", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "sst-platform", + .init = mrfld_8958_init, + .ignore_suspend = 1, + .dynamic = 1, + .ops = &mrfld_8958_ops, + }, + [MERR_DPCM_LL] = { + .name = "Merrifield LL Audio Port", + .stream_name = "Low Latency Audio", + .cpu_dai_name = "Lowlatency-cpu-dai", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "sst-platform", + .init = mrfld_8958_init, + .ignore_suspend = 1, + .dynamic = 1, + .ops = &mrfld_8958_ops, + }, + [MERR_DPCM_COMPR] = { + .name = "Merrifield Compress Port", + .stream_name = "Saltbay Compress", + .platform_name = "sst-platform", + .cpu_dai_name = "Compress-cpu-dai", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .dynamic = 1, + .init = mrfld_8958_init, + .compr_ops = &mrfld_compr_ops, + }, + [MERR_DPCM_VOIP] = { + .name = "Merrifield VOIP Port", + .stream_name = "Saltbay Voip", + .cpu_dai_name = "Voip-cpu-dai", + .platform_name = "sst-platform", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .init = NULL, + .ignore_suspend = 1, + .ops = &mrfld_8958_8k_16k_ops, + .dynamic = 1, + }, + [MERR_DPCM_PROBE] = { + .name = "Merrifield Probe Port", + .stream_name = "Saltbay Probe", + .cpu_dai_name = "Probe-cpu-dai", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .platform_name = "sst-platform", + .playback_count = 8, + .capture_count = 8, + }, + /* CODEC<->CODEC link */ + { + .name = "Merrifield Codec-Loop Port", + .stream_name = "Saltbay Codec-Loop", + .cpu_dai_name = "snd-soc-dummy-dai", + .codec_dai_name = "wm8994-aif1", + .codec_name = "wm8994-codec", + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF + | SND_SOC_DAIFMT_CBS_CFS, + .params = &mrfld_wm8958_dai_params, + }, + + /* back ends */ + { + .name = "SSP2-Codec", + .be_id = 1, + .cpu_dai_name = "ssp2-codec", + .platform_name = "sst-platform", + .no_pcm = 1, + .codec_dai_name = "wm8994-aif1", + .codec_name = "wm8994-codec", + .be_hw_params_fixup = merr_codec_fixup, + .ignore_suspend = 1, + .ops = &mrfld_8958_be_ssp2_ops, + }, + { + .name = "SSP1-BTFM", + .be_id = 2, + .cpu_dai_name = "snd-soc-dummy-dai", + .platform_name = "snd-soc-dummy", + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .ignore_suspend = 1, + }, + { + .name = "SSP0-Modem", + .be_id = 3, + .cpu_dai_name = "snd-soc-dummy-dai", + .platform_name = "snd-soc-dummy", + .no_pcm = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .ignore_suspend = 1, + }, +}; + +#ifdef CONFIG_PM_SLEEP +static int snd_mrfld_8958_prepare(struct device *dev) +{ + pr_debug("In %s device name\n", __func__); + snd_soc_suspend(dev); + return 0; +} + +static void snd_mrfld_8958_complete(struct device *dev) +{ + pr_debug("In %s\n", __func__); + snd_soc_resume(dev); + return; +} + +static int snd_mrfld_8958_poweroff(struct device *dev) +{ + pr_debug("In %s\n", __func__); + snd_soc_poweroff(dev); + return 0; +} +#else +#define snd_mrfld_8958_prepare NULL +#define snd_mrfld_8958_complete NULL +#define snd_mrfld_8958_poweroff NULL +#endif + +/* SoC card */ +static struct snd_soc_card snd_soc_card_mrfld = { + .name = "wm8958-audio", + .dai_link = mrfld_8958_msic_dailink, + .num_links = ARRAY_SIZE(mrfld_8958_msic_dailink), + .set_bias_level = mrfld_8958_set_bias_level, + .set_bias_level_post = mrfld_8958_set_bias_level_post, + .dapm_widgets = widgets, + .num_dapm_widgets = ARRAY_SIZE(widgets), + .dapm_routes = map, + .num_dapm_routes = ARRAY_SIZE(map), +}; + +static int snd_mrfld_8958_mc_probe(struct platform_device *pdev) +{ + int ret_val = 0; + struct mrfld_8958_mc_private *drv; + + pr_debug("Entry %s\n", __func__); + + drv = kzalloc(sizeof(*drv), GFP_ATOMIC); + if (!drv) { + pr_err("allocation failed\n"); + return -ENOMEM; + } + + /* ioremap the register */ + drv->osc_clk0_reg = devm_ioremap_nocache(&pdev->dev, + MERR_OSC_CLKOUT_CTRL0_REG_ADDR, + MERR_OSC_CLKOUT_CTRL0_REG_SIZE); + if (!drv->osc_clk0_reg) { + pr_err("osc clk0 ctrl ioremap failed\n"); + ret_val = -1; + goto unalloc; + } + + ret_val = intel_scu_ipc_ioread8(PMIC_ID_ADDR, &drv->pmic_id); + if (ret_val) { + pr_err("Error reading PMIC ID register\n"); + goto unalloc; + } + + /* register the soc card */ + snd_soc_card_mrfld.dev = &pdev->dev; + snd_soc_card_set_drvdata(&snd_soc_card_mrfld, drv); + ret_val = snd_soc_register_card(&snd_soc_card_mrfld); + if (ret_val) { + pr_err("snd_soc_register_card failed %d\n", ret_val); + goto unalloc; + } + platform_set_drvdata(pdev, &snd_soc_card_mrfld); + pr_info("%s successful\n", __func__); + return ret_val; + +unalloc: + kfree(drv); + return ret_val; +} + +static int snd_mrfld_8958_mc_remove(struct platform_device *pdev) +{ + struct snd_soc_card *soc_card = platform_get_drvdata(pdev); + struct mrfld_8958_mc_private *drv = snd_soc_card_get_drvdata(soc_card); + + pr_debug("In %s\n", __func__); + kfree(drv); + snd_soc_card_set_drvdata(soc_card, NULL); + snd_soc_unregister_card(soc_card); + platform_set_drvdata(pdev, NULL); + return 0; +} + +const struct dev_pm_ops snd_mrfld_8958_mc_pm_ops = { + .prepare = snd_mrfld_8958_prepare, + .complete = snd_mrfld_8958_complete, + .poweroff = snd_mrfld_8958_poweroff, +}; + +static struct platform_driver snd_mrfld_8958_mc_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "mrfld_wm8958", + .pm = &snd_mrfld_8958_mc_pm_ops, + }, + .probe = snd_mrfld_8958_mc_probe, + .remove = snd_mrfld_8958_mc_remove, +}; + +static int snd_mrfld_8958_driver_init(void) +{ + pr_info("Merrifield Machine Driver mrfld_wm8958 registerd\n"); + return platform_driver_register(&snd_mrfld_8958_mc_driver); +} + +static void snd_mrfld_8958_driver_exit(void) +{ + pr_debug("In %s\n", __func__); + platform_driver_unregister(&snd_mrfld_8958_mc_driver); +} + +static int snd_mrfld_8958_rpmsg_probe(struct rpmsg_channel *rpdev) +{ + int ret = 0; + + if (rpdev == NULL) { + pr_err("rpmsg channel not created\n"); + ret = -ENODEV; + goto out; + } + + dev_info(&rpdev->dev, "Probed snd_mrfld wm8958 rpmsg device\n"); + + ret = snd_mrfld_8958_driver_init(); + +out: + return ret; +} + +static void snd_mrfld_8958_rpmsg_remove(struct rpmsg_channel *rpdev) +{ + snd_mrfld_8958_driver_exit(); + dev_info(&rpdev->dev, "Removed snd_mrfld wm8958 rpmsg device\n"); +} + +static void snd_mrfld_8958_rpmsg_cb(struct rpmsg_channel *rpdev, void *data, + int len, void *priv, u32 src) +{ + dev_warn(&rpdev->dev, "unexpected, message\n"); + + print_hex_dump(KERN_DEBUG, __func__, DUMP_PREFIX_NONE, 16, 1, + data, len, true); +} + +static struct rpmsg_device_id snd_mrfld_8958_rpmsg_id_table[] = { + { .name = "rpmsg_mrfld_wm8958_audio" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, snd_mrfld_8958_rpmsg_id_table); + +static struct rpmsg_driver snd_mrfld_8958_rpmsg = { + .drv.name = KBUILD_MODNAME, + .drv.owner = THIS_MODULE, + .id_table = snd_mrfld_8958_rpmsg_id_table, + .probe = snd_mrfld_8958_rpmsg_probe, + .callback = snd_mrfld_8958_rpmsg_cb, + .remove = snd_mrfld_8958_rpmsg_remove, +}; + +static int __init snd_mrfld_8958_rpmsg_init(void) +{ + return register_rpmsg_driver(&snd_mrfld_8958_rpmsg); +} +late_initcall(snd_mrfld_8958_rpmsg_init); + +static void __exit snd_mrfld_8958_rpmsg_exit(void) +{ + return unregister_rpmsg_driver(&snd_mrfld_8958_rpmsg); +} +module_exit(snd_mrfld_8958_rpmsg_exit); + +MODULE_DESCRIPTION("ASoC Intel(R) Merrifield MID Machine driver"); +MODULE_AUTHOR("Vinod Koul "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:mrfld_wm8958"); diff --git a/sound/soc/intel/board/merr_saltbay_wm8958.c b/sound/soc/intel/board/merr_saltbay_wm8958.c new file mode 100644 index 000000000000..ef63154fa5a6 --- /dev/null +++ b/sound/soc/intel/board/merr_saltbay_wm8958.c @@ -0,0 +1,887 @@ +/* + * merr_saltbay_wm8958.c - ASoc Machine driver for Intel Merrfield MID platform + * + * Copyright (C) 2013 Intel Corp + * Author: Vinod Koul + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "../../codecs/wm8994.h" + +/* Codec PLL output clk rate */ +#define CODEC_SYSCLK_RATE 24576000 +/* Input clock to codec at MCLK1 PIN */ +#define CODEC_IN_MCLK1_RATE 19200000 +/* Input clock to codec at MCLK2 PIN */ +#define CODEC_IN_MCLK2_RATE 32768 +/* define to select between MCLK1 and MCLK2 input to codec as its clock */ +#define CODEC_IN_MCLK1 1 +#define CODEC_IN_MCLK2 2 + +/* Register address for OSC Clock */ +#define MERR_OSC_CLKOUT_CTRL0_REG_ADDR 0xFF00BC04 +/* Size of osc clock register */ +#define MERR_OSC_CLKOUT_CTRL0_REG_SIZE 4 + +struct mrfld_8958_mc_private { + struct snd_soc_jack jack; + int jack_retry; + u8 pmic_id; + void __iomem *osc_clk0_reg; +}; + + +/* set_osc_clk0- enable/disables the osc clock0 + * addr: address of the register to write to + * enable: bool to enable or disable the clock + */ +static inline void set_soc_osc_clk0(void __iomem *addr, bool enable) +{ + u32 osc_clk_ctrl; + + osc_clk_ctrl = readl(addr); + if (enable) + osc_clk_ctrl |= BIT(31); + else + osc_clk_ctrl &= ~(BIT(31)); + + pr_debug("%s: enable:%d val 0x%x\n", __func__, enable, osc_clk_ctrl); + + writel(osc_clk_ctrl, addr); +} + + +static inline struct snd_soc_codec *mrfld_8958_get_codec(struct snd_soc_card *card) +{ + bool found = false; + struct snd_soc_codec *codec; + + list_for_each_entry(codec, &card->codec_dev_list, card_list) { + if (!strstr(codec->name, "wm8994-codec")) { + pr_debug("codec was %s", codec->name); + continue; + } else { + found = true; + break; + } + } + if (found == false) { + pr_err("%s: cant find codec", __func__); + return NULL; + } + return codec; +} + +/* Function to switch the input clock for codec, When audio is in + * progress input clock to codec will be through MCLK1 which is 19.2MHz + * while in off state input clock to codec will be through 32KHz through + * MCLK2 + * card : Sound card structure + * src : Input clock source to codec + */ +static int mrfld_8958_set_codec_clk(struct snd_soc_card *card, int src) +{ + struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai; + int ret; + + switch (src) { + case CODEC_IN_MCLK1: + /* Turn ON the PLL to generate required sysclk rate + * from MCLK1 */ + ret = snd_soc_dai_set_pll(aif1_dai, + WM8994_FLL1, WM8994_FLL_SRC_MCLK1, + CODEC_IN_MCLK1_RATE, CODEC_SYSCLK_RATE); + if (ret < 0) { + pr_err("Failed to start FLL: %d\n", ret); + return ret; + } + /* Switch to MCLK1 input */ + ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_FLL1, + CODEC_SYSCLK_RATE, SND_SOC_CLOCK_IN); + if (ret < 0) { + pr_err("Failed to set codec sysclk configuration %d\n", + ret); + return ret; + } + break; + case CODEC_IN_MCLK2: + /* Switch to MCLK2 */ + ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2, + 32768, SND_SOC_CLOCK_IN); + if (ret < 0) { + pr_err("Failed to switch to MCLK2: %d", ret); + return ret; + } + /* Turn off PLL for MCLK1 */ + ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1, 0, 0, 0); + if (ret < 0) { + pr_err("Failed to stop the FLL: %d", ret); + return ret; + } + break; + default: + return -EINVAL; + } + return 0; +} + +static int mrfld_wm8958_set_clk_fmt(struct snd_soc_dai *codec_dai) +{ + unsigned int fmt; + int ret = 0; + struct snd_soc_card *card = codec_dai->card; + struct mrfld_8958_mc_private *ctx = snd_soc_card_get_drvdata(card); + + /* Enable the osc clock at start so that it gets settling time */ + set_soc_osc_clk0(ctx->osc_clk0_reg, true); + + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0, 0, 4, SNDRV_PCM_FORMAT_S24_LE); + if (ret < 0) { + pr_err("can't set codec pcm format %d\n", ret); + return ret; + } + + /* WM8958 slave Mode */ + fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF + | SND_SOC_DAIFMT_CBS_CFS; + ret = snd_soc_dai_set_fmt(codec_dai, fmt); + if (ret < 0) { + pr_err("can't set codec DAI configuration %d\n", ret); + return ret; + } + + /* FIXME: move this to SYS_CLOCK event handler when codec driver + * dependency is clean. + */ + /* Switch to 19.2MHz MCLK1 input clock for codec */ + ret = mrfld_8958_set_codec_clk(card, CODEC_IN_MCLK1); + + return ret; +} + +static int mrfld_8958_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; + + return mrfld_wm8958_set_clk_fmt(codec_dai); +} + +static int mrfld_wm8958_compr_set_params(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + return mrfld_wm8958_set_clk_fmt(codec_dai); +} +static int mrfld_8958_set_bias_level(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) +{ + struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai; + int ret = 0; + + if (dapm->dev != aif1_dai->dev) + return 0; + switch (level) { + case SND_SOC_BIAS_PREPARE: + if (card->dapm.bias_level == SND_SOC_BIAS_STANDBY) + + ret = mrfld_wm8958_set_clk_fmt(aif1_dai); + break; + default: + break; + } + pr_debug("%s card(%s)->bias_level %u\n", __func__, card->name, + card->dapm.bias_level); + return ret; +} +static int mrfld_8958_set_bias_level_post(struct snd_soc_card *card, + struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) +{ + struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai; + struct mrfld_8958_mc_private *ctx = snd_soc_card_get_drvdata(card); + int ret = 0; + + if (dapm->dev != aif1_dai->dev) + return 0; + + switch (level) { + case SND_SOC_BIAS_STANDBY: + /* We are in stabdba down so */ + /* Switch to 32KHz MCLK2 input clock for codec + */ + ret = mrfld_8958_set_codec_clk(card, CODEC_IN_MCLK2); + /* Turn off 19.2MHz soc osc clock */ + set_soc_osc_clk0(ctx->osc_clk0_reg, false); + break; + default: + break; + } + card->dapm.bias_level = level; + pr_debug("%s card(%s)->bias_level %u\n", __func__, card->name, + card->dapm.bias_level); + return ret; +} + +#define PMIC_ID_ADDR 0x00 +#define PMIC_CHIP_ID_A0_VAL 0xC0 + +static int mrfld_8958_set_vflex_vsel(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ +#define VFLEXCNT 0xAB +#define VFLEXVSEL_5V 0x01 +#define VFLEXVSEL_B0_VSYS_PT 0x80 /* B0: Vsys pass-through */ +#define VFLEXVSEL_A0_4P5V 0x41 /* A0: 4.5V */ + + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct mrfld_8958_mc_private *ctx = snd_soc_card_get_drvdata(card); + + u8 vflexvsel, pmic_id = ctx->pmic_id; + int retval = 0; + + pr_debug("%s: ON? %d\n", __func__, SND_SOC_DAPM_EVENT_ON(event)); + + vflexvsel = (pmic_id == PMIC_CHIP_ID_A0_VAL) ? VFLEXVSEL_A0_4P5V : VFLEXVSEL_B0_VSYS_PT; + pr_debug("pmic_id %#x vflexvsel %#x\n", pmic_id, + SND_SOC_DAPM_EVENT_ON(event) ? VFLEXVSEL_5V : vflexvsel); + + /*FIXME: seems to be issue with bypass mode in MOOR, for now + force the bias off volate as VFLEXVSEL_5V */ + if ((INTEL_MID_BOARD(1, PHONE, MOFD)) || + (INTEL_MID_BOARD(1, TABLET, MOFD))) + vflexvsel = VFLEXVSEL_5V; + + if (SND_SOC_DAPM_EVENT_ON(event)) + retval = intel_scu_ipc_iowrite8(VFLEXCNT, VFLEXVSEL_5V); + else if (SND_SOC_DAPM_EVENT_OFF(event)) + retval = intel_scu_ipc_iowrite8(VFLEXCNT, vflexvsel); + if (retval) + pr_err("Error writing to VFLEXCNT register\n"); + + return retval; +} + +static const struct snd_soc_dapm_widget widgets[] = { + SND_SOC_DAPM_HP("Headphones", NULL), + SND_SOC_DAPM_MIC("AMIC", NULL), + SND_SOC_DAPM_MIC("DMIC", NULL), + SND_SOC_DAPM_SUPPLY("VFLEXCNT", SND_SOC_NOPM, 0, 0, + mrfld_8958_set_vflex_vsel, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route map[] = { + { "Headphones", NULL, "HPOUT1L" }, + { "Headphones", NULL, "HPOUT1R" }, + + /* saltbay uses 2 DMICs, other configs may use more so change below + * accordingly + */ + { "DMIC1DAT", NULL, "DMIC" }, + { "DMIC2DAT", NULL, "DMIC" }, + /*{ "DMIC3DAT", NULL, "DMIC" },*/ + /*{ "DMIC4DAT", NULL, "DMIC" },*/ + + /* MICBIAS2 is connected as Bias for AMIC so we link it + * here. Also AMIC wires up to IN1LP pin. + * DMIC is externally connected to 1.8V rail, so no link rqd. + */ + { "AMIC", NULL, "MICBIAS2" }, + { "IN1LP", NULL, "AMIC" }, + + /* SWM map link the SWM outs to codec AIF */ + { "AIF1DAC1L", NULL, "Codec OUT0" }, + { "AIF1DAC1R", NULL, "Codec OUT0" }, + { "AIF1DAC2L", NULL, "Codec OUT1" }, + { "AIF1DAC2R", NULL, "Codec OUT1" }, + { "Codec IN0", NULL, "AIF1ADC1L" }, + { "Codec IN0", NULL, "AIF1ADC1R" }, + { "Codec IN1", NULL, "AIF1ADC1L" }, + { "Codec IN1", NULL, "AIF1ADC1R" }, + + { "AIF1DAC1L", NULL, "VFLEXCNT" }, + { "AIF1DAC1R", NULL, "VFLEXCNT" }, + { "AIF1DAC2L", NULL, "VFLEXCNT" }, + { "AIF1DAC2R", NULL, "VFLEXCNT" }, + + { "AIF1ADC1L", NULL, "VFLEXCNT" }, + { "AIF1ADC1R", NULL, "VFLEXCNT" }, + +}; + +static const struct wm8958_micd_rate micdet_rates[] = { + { 32768, true, 1, 4 }, + { 32768, false, 1, 1 }, + { 44100 * 256, true, 7, 10 }, + { 44100 * 256, false, 7, 10 }, +}; + +static void wm8958_custom_micd_set_rate(struct snd_soc_codec *codec) +{ + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + struct wm8994 *control = dev_get_drvdata(codec->dev->parent); + int best, i, sysclk, val; + bool idle; + const struct wm8958_micd_rate *rates; + int num_rates; + + idle = !wm8994->jack_mic; + + sysclk = snd_soc_read(codec, WM8994_CLOCKING_1); + if (sysclk & WM8994_SYSCLK_SRC) + sysclk = wm8994->aifclk[1]; + else + sysclk = wm8994->aifclk[0]; + + if (control->pdata.micd_rates) { + rates = control->pdata.micd_rates; + num_rates = control->pdata.num_micd_rates; + } else { + rates = micdet_rates; + num_rates = ARRAY_SIZE(micdet_rates); + } + + best = 0; + for (i = 0; i < num_rates; i++) { + if (rates[i].idle != idle) + continue; + if (abs(rates[i].sysclk - sysclk) < + abs(rates[best].sysclk - sysclk)) + best = i; + else if (rates[best].idle != idle) + best = i; + } + + val = rates[best].start << WM8958_MICD_BIAS_STARTTIME_SHIFT + | rates[best].rate << WM8958_MICD_RATE_SHIFT; + + dev_dbg(codec->dev, "MICD rate %d,%d for %dHz %s\n", + rates[best].start, rates[best].rate, sysclk, + idle ? "idle" : "active"); + + snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, + WM8958_MICD_BIAS_STARTTIME_MASK | + WM8958_MICD_RATE_MASK, val); +} + +static void wm8958_custom_mic_id(void *data, u16 status) +{ + struct snd_soc_codec *codec = data; + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "wm8958 custom mic id called with status %x\n", + status); + + /* Either nothing present or just starting detection */ + if (!(status & WM8958_MICD_STS)) { + /* If nothing present then clear our statuses */ + dev_dbg(codec->dev, "Detected open circuit\n"); + + schedule_delayed_work(&wm8994->open_circuit_work, + msecs_to_jiffies(2500)); + return; + } + + schedule_delayed_work(&wm8994->micd_set_custom_rate_work, + msecs_to_jiffies(wm8994->wm8994->pdata.micb_en_delay)); + + /* If the measurement is showing a high impedence we've got a + * microphone. + */ + if (status & 0x600) { + dev_dbg(codec->dev, "Detected microphone\n"); + + wm8994->mic_detecting = false; + wm8994->jack_mic = true; + wm8994->headphone_detected = false; + + snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADSET, + SND_JACK_HEADSET); + } + + + if (status & 0xfc) { + dev_dbg(codec->dev, "Detected headphone\n"); + + /* Partial inserts of headsets with complete insert + * after an indeterminate amount of time require + * continouous micdetect enabled (until open circuit + * or headset is detected) + * */ + wm8994->mic_detecting = true; + + wm8994->jack_mic = false; + wm8994->headphone_detected = true; + + snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADPHONE, + SND_JACK_HEADSET); + } +} + +static int mrfld_8958_init(struct snd_soc_pcm_runtime *runtime) +{ + int ret; + unsigned int fmt; + struct snd_soc_codec *codec = runtime->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + struct snd_soc_card *card = runtime->card; + struct snd_soc_dai *aif1_dai = card->rtd[0].codec_dai; + struct mrfld_8958_mc_private *ctx = snd_soc_card_get_drvdata(card); + + pr_debug("Entry %s\n", __func__); + + ret = snd_soc_dai_set_tdm_slot(aif1_dai, 0, 0, 4, SNDRV_PCM_FORMAT_S24_LE); + if (ret < 0) { + pr_err("can't set codec pcm format %d\n", ret); + return ret; + } + + /* WM8958 slave Mode */ + fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF + | SND_SOC_DAIFMT_CBS_CFS; + ret = snd_soc_dai_set_fmt(aif1_dai, fmt); + if (ret < 0) { + pr_err("can't set codec DAI configuration %d\n", ret); + return ret; + } + + mrfld_8958_set_bias_level(card, dapm, SND_SOC_BIAS_OFF); + card->dapm.idle_bias_off = true; + + /* these pins are not used in SB config so mark as nc + * + * LINEOUT1, 2 + * IN1R + * DMICDAT2 + */ + snd_soc_dapm_nc_pin(dapm, "DMIC2DAT"); + snd_soc_dapm_nc_pin(dapm, "LINEOUT1P"); + snd_soc_dapm_nc_pin(dapm, "LINEOUT1N"); + snd_soc_dapm_nc_pin(dapm, "LINEOUT2P"); + snd_soc_dapm_nc_pin(dapm, "LINEOUT2N"); + snd_soc_dapm_nc_pin(dapm, "IN1RN"); + snd_soc_dapm_nc_pin(dapm, "IN1RP"); + + /* Force enable VMID to avoid cold latency constraints */ + snd_soc_dapm_force_enable_pin(dapm, "VMID"); + snd_soc_dapm_sync(dapm); + + ctx->jack_retry = 0; + ret = snd_soc_jack_new(codec, "Intel MID Audio Jack", + SND_JACK_HEADSET | SND_JACK_HEADPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1, + &ctx->jack); + if (ret) { + pr_err("jack creation failed\n"); + return ret; + } + + snd_jack_set_key(ctx->jack.jack, SND_JACK_BTN_1, KEY_MEDIA); + snd_jack_set_key(ctx->jack.jack, SND_JACK_BTN_0, KEY_MEDIA); + + wm8958_mic_detect(codec, &ctx->jack, NULL, NULL, + wm8958_custom_mic_id, codec); + + wm8958_micd_set_custom_rate(codec, wm8958_custom_micd_set_rate, codec); + + snd_soc_update_bits(codec, WM8994_AIF1_DAC1_FILTERS_1, WM8994_AIF1DAC1_MUTE, 0); + snd_soc_update_bits(codec, WM8994_AIF1_DAC2_FILTERS_1, WM8994_AIF1DAC2_MUTE, 0); + + /* Micbias1 is always off, so for pm optimizations make sure the micbias1 + * discharge bit is set to floating to avoid discharge in disable state + */ + snd_soc_update_bits(codec, WM8958_MICBIAS1, WM8958_MICB1_DISCH, 0); + + return 0; +} + +static unsigned int rates_8000_16000[] = { + 8000, + 16000, +}; + +static struct snd_pcm_hw_constraint_list constraints_8000_16000 = { + .count = ARRAY_SIZE(rates_8000_16000), + .list = rates_8000_16000, +}; + +static unsigned int rates_48000[] = { + 48000, +}; + +static struct snd_pcm_hw_constraint_list constraints_48000 = { + .count = ARRAY_SIZE(rates_48000), + .list = rates_48000, +}; + +static int mrfld_8958_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_48000); +} + +static struct snd_soc_ops mrfld_8958_ops = { + .startup = mrfld_8958_startup, + .hw_params = mrfld_8958_hw_params, +}; + +static int mrfld_8958_8k_16k_startup(struct snd_pcm_substream *substream) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_8000_16000); +} + +static struct snd_soc_ops mrfld_8958_8k_16k_ops = { + .startup = mrfld_8958_8k_16k_startup, + .hw_params = mrfld_8958_hw_params, +}; + +static struct snd_soc_compr_ops mrfld_compr_ops = { + .set_params = mrfld_wm8958_compr_set_params, +}; + +struct snd_soc_dai_link mrfld_8958_msic_dailink[] = { + [MERR_SALTBAY_AUDIO] = { + .name = "Merrifield Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "Headset-cpu-dai", + .codec_dai_name = "wm8994-aif1", + .codec_name = "wm8994-codec", + .platform_name = "sst-platform", + .init = mrfld_8958_init, + .ignore_suspend = 1, + .ops = &mrfld_8958_ops, + .playback_count = 3, + }, + [MERR_SALTBAY_COMPR] = { + .name = "Merrifield Compress Port", + .stream_name = "Compress", + .platform_name = "sst-platform", + .cpu_dai_name = "Compress-cpu-dai", + .codec_dai_name = "wm8994-aif1", + .codec_name = "wm8994-codec", + .compr_ops = &mrfld_compr_ops, + }, + [MERR_SALTBAY_VOIP] = { + .name = "Merrifield VOIP Port", + .stream_name = "Voip", + .cpu_dai_name = "Voip-cpu-dai", + .codec_dai_name = "wm8994-aif1", + .codec_name = "wm8994-codec", + .platform_name = "sst-platform", + .init = NULL, + .ignore_suspend = 1, + .ops = &mrfld_8958_8k_16k_ops, + }, + [MERR_SALTBAY_PROBE] = { + .name = "Merrifield Probe Port", + .stream_name = "Probe", + .cpu_dai_name = "Probe-cpu-dai", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + .platform_name = "sst-platform", + .playback_count = 8, + .capture_count = 8, + }, + [MERR_SALTBAY_AWARE] = { + .name = "Merrifield Aware Port", + .stream_name = "Aware", + .cpu_dai_name = "Loopback-cpu-dai", + .codec_dai_name = "wm8994-aif1", + .codec_name = "wm8994-codec", + .platform_name = "sst-platform", + .init = NULL, + .ignore_suspend = 1, + .ops = &mrfld_8958_8k_16k_ops, + }, + [MERR_SALTBAY_VAD] = { + .name = "Merrifield VAD Port", + .stream_name = "Vad", + .cpu_dai_name = "Loopback-cpu-dai", + .codec_dai_name = "wm8994-aif1", + .codec_name = "wm8994-codec", + .platform_name = "sst-platform", + .init = NULL, + .ignore_suspend = 1, + .ops = &mrfld_8958_8k_16k_ops, + }, + [MERR_SALTBAY_POWER] = { + .name = "Virtual Power Port", + .stream_name = "Power", + .cpu_dai_name = "Power-cpu-dai", + .platform_name = "sst-platform", + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, +}; + +#ifdef CONFIG_PM_SLEEP +static int snd_mrfld_8958_prepare(struct device *dev) +{ + struct snd_soc_card *card = dev_get_drvdata(dev); + struct snd_soc_codec *codec; + struct snd_soc_dapm_context *dapm; + + pr_debug("In %s\n", __func__); + + codec = mrfld_8958_get_codec(card); + if (!codec) { + pr_err("%s: couldn't find the codec pointer!\n", __func__); + return -EAGAIN; + } + + pr_debug("found codec %s\n", codec->name); + dapm = &codec->dapm; + + snd_soc_dapm_disable_pin(dapm, "VMID"); + snd_soc_dapm_sync(dapm); + + snd_soc_suspend(dev); + return 0; +} + +static void snd_mrfld_8958_complete(struct device *dev) +{ + struct snd_soc_card *card = dev_get_drvdata(dev); + struct snd_soc_codec *codec; + struct snd_soc_dapm_context *dapm; + + pr_debug("In %s\n", __func__); + + codec = mrfld_8958_get_codec(card); + if (!codec) { + pr_err("%s: couldn't find the codec pointer!\n", __func__); + return; + } + + pr_debug("found codec %s\n", codec->name); + dapm = &codec->dapm; + + snd_soc_dapm_force_enable_pin(dapm, "VMID"); + snd_soc_dapm_sync(dapm); + + snd_soc_resume(dev); + return; +} + +static int snd_mrfld_8958_poweroff(struct device *dev) +{ + pr_debug("In %s\n", __func__); + snd_soc_poweroff(dev); + return 0; +} +#else +#define snd_mrfld_8958_prepare NULL +#define snd_mrfld_8958_complete NULL +#define snd_mrfld_8958_poweroff NULL +#endif + +/* SoC card */ +static struct snd_soc_card snd_soc_card_mrfld = { + .name = "wm8958-audio", + .dai_link = mrfld_8958_msic_dailink, + .num_links = ARRAY_SIZE(mrfld_8958_msic_dailink), + .set_bias_level = mrfld_8958_set_bias_level, + .set_bias_level_post = mrfld_8958_set_bias_level_post, + .dapm_widgets = widgets, + .num_dapm_widgets = ARRAY_SIZE(widgets), + .dapm_routes = map, + .num_dapm_routes = ARRAY_SIZE(map), +}; + +static int snd_mrfld_8958_mc_probe(struct platform_device *pdev) +{ + int ret_val = 0; + struct mrfld_8958_mc_private *drv; + + pr_debug("Entry %s\n", __func__); + + drv = kzalloc(sizeof(*drv), GFP_ATOMIC); + if (!drv) { + pr_err("allocation failed\n"); + return -ENOMEM; + } + + /* ioremap the register */ + drv->osc_clk0_reg = devm_ioremap_nocache(&pdev->dev, + MERR_OSC_CLKOUT_CTRL0_REG_ADDR, + MERR_OSC_CLKOUT_CTRL0_REG_SIZE); + if (!drv->osc_clk0_reg) { + pr_err("osc clk0 ctrl ioremap failed\n"); + ret_val = -1; + goto unalloc; + } + + ret_val = intel_scu_ipc_ioread8(PMIC_ID_ADDR, &drv->pmic_id); + if (ret_val) { + pr_err("Error reading PMIC ID register\n"); + goto unalloc; + } + + /* register the soc card */ + snd_soc_card_mrfld.dev = &pdev->dev; + snd_soc_card_set_drvdata(&snd_soc_card_mrfld, drv); + ret_val = snd_soc_register_card(&snd_soc_card_mrfld); + if (ret_val) { + pr_err("snd_soc_register_card failed %d\n", ret_val); + goto unalloc; + } + platform_set_drvdata(pdev, &snd_soc_card_mrfld); + pr_info("%s successful\n", __func__); + return ret_val; + +unalloc: + kfree(drv); + return ret_val; +} + +static int snd_mrfld_8958_mc_remove(struct platform_device *pdev) +{ + struct snd_soc_card *soc_card = platform_get_drvdata(pdev); + struct mrfld_8958_mc_private *drv = snd_soc_card_get_drvdata(soc_card); + + pr_debug("In %s\n", __func__); + kfree(drv); + snd_soc_card_set_drvdata(soc_card, NULL); + snd_soc_unregister_card(soc_card); + platform_set_drvdata(pdev, NULL); + return 0; +} + +const struct dev_pm_ops snd_mrfld_8958_mc_pm_ops = { + .prepare = snd_mrfld_8958_prepare, + .complete = snd_mrfld_8958_complete, + .poweroff = snd_mrfld_8958_poweroff, +}; + +static struct platform_driver snd_mrfld_8958_mc_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "mrfld_wm8958", + .pm = &snd_mrfld_8958_mc_pm_ops, + }, + .probe = snd_mrfld_8958_mc_probe, + .remove = snd_mrfld_8958_mc_remove, +}; + +static int snd_mrfld_8958_driver_init(void) +{ + pr_info("Merrifield Machine Driver mrfld_wm8958 registerd\n"); + return platform_driver_register(&snd_mrfld_8958_mc_driver); +} + +static void snd_mrfld_8958_driver_exit(void) +{ + pr_debug("In %s\n", __func__); + platform_driver_unregister(&snd_mrfld_8958_mc_driver); +} + +static int snd_mrfld_8958_rpmsg_probe(struct rpmsg_channel *rpdev) +{ + int ret = 0; + + if (rpdev == NULL) { + pr_err("rpmsg channel not created\n"); + ret = -ENODEV; + goto out; + } + + dev_info(&rpdev->dev, "Probed snd_mrfld wm8958 rpmsg device\n"); + + ret = snd_mrfld_8958_driver_init(); + +out: + return ret; +} + +static void snd_mrfld_8958_rpmsg_remove(struct rpmsg_channel *rpdev) +{ + snd_mrfld_8958_driver_exit(); + dev_info(&rpdev->dev, "Removed snd_mrfld wm8958 rpmsg device\n"); +} + +static void snd_mrfld_8958_rpmsg_cb(struct rpmsg_channel *rpdev, void *data, + int len, void *priv, u32 src) +{ + dev_warn(&rpdev->dev, "unexpected, message\n"); + + print_hex_dump(KERN_DEBUG, __func__, DUMP_PREFIX_NONE, 16, 1, + data, len, true); +} + +static struct rpmsg_device_id snd_mrfld_8958_rpmsg_id_table[] = { + { .name = "rpmsg_mrfld_wm8958_audio" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, snd_mrfld_8958_rpmsg_id_table); + +static struct rpmsg_driver snd_mrfld_8958_rpmsg = { + .drv.name = KBUILD_MODNAME, + .drv.owner = THIS_MODULE, + .id_table = snd_mrfld_8958_rpmsg_id_table, + .probe = snd_mrfld_8958_rpmsg_probe, + .callback = snd_mrfld_8958_rpmsg_cb, + .remove = snd_mrfld_8958_rpmsg_remove, +}; + +static int __init snd_mrfld_8958_rpmsg_init(void) +{ + return register_rpmsg_driver(&snd_mrfld_8958_rpmsg); +} +late_initcall(snd_mrfld_8958_rpmsg_init); + +static void __exit snd_mrfld_8958_rpmsg_exit(void) +{ + return unregister_rpmsg_driver(&snd_mrfld_8958_rpmsg); +} +module_exit(snd_mrfld_8958_rpmsg_exit); + +MODULE_DESCRIPTION("ASoC Intel(R) Merrifield MID Machine driver"); +MODULE_AUTHOR("Vinod Koul "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:mrfld_wm8958"); diff --git a/sound/soc/intel/compress.c b/sound/soc/intel/compress.c new file mode 100644 index 000000000000..f067468b320a --- /dev/null +++ b/sound/soc/intel/compress.c @@ -0,0 +1,254 @@ +/* + * compress.c - Intel MID Platform driver for Compress stream operations + * + * Copyright (C) 2010-2013 Intel Corp + * Author: Vinod Koul + * Author: Harsha Priya + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include "platform_ipc_v2.h" +#include "sst_platform.h" +#include "sst_platform_pvt.h" + +static void sst_compr_fragment_elapsed(void *arg) +{ + struct snd_compr_stream *cstream = (struct snd_compr_stream *)arg; + + pr_debug("fragment elapsed by driver\n"); + if (cstream) + snd_compr_fragment_elapsed(cstream); +} + +static void sst_drain_notify(void *arg) +{ + struct snd_compr_stream *cstream = (struct snd_compr_stream *)arg; + + pr_debug("drain notify by driver\n"); + if (cstream) + snd_compr_drain_notify(cstream); +} + +static int sst_platform_compr_open(struct snd_compr_stream *cstream) +{ + + int ret_val = 0; + struct snd_compr_runtime *runtime = cstream->runtime; + struct sst_runtime_stream *stream; + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + pr_debug("%s called:%s\n", __func__, dai_link->cpu_dai_name); + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; + + spin_lock_init(&stream->status_lock); + + /* get the sst ops */ + if (!sst_dsp || !try_module_get(sst_dsp->dev->driver->owner)) { + pr_err("no device available to run\n"); + ret_val = -ENODEV; + goto out_ops; + } + stream->compr_ops = sst_dsp->compr_ops; + + stream->id = 0; + sst_set_stream_status(stream, SST_PLATFORM_INIT); + runtime->private_data = stream; + return 0; +out_ops: + kfree(stream); + return ret_val; +} + +static int sst_platform_compr_free(struct snd_compr_stream *cstream) +{ + struct sst_runtime_stream *stream; + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + int ret_val = 0, str_id; + + stream = cstream->runtime->private_data; + /*need to check*/ + str_id = stream->id; + if (str_id) + ret_val = stream->compr_ops->close(str_id); + module_put(sst_dsp->dev->driver->owner); + kfree(stream); + pr_debug("%s called for dai %s: ret = %d\n", __func__, + dai_link->cpu_dai_name, ret_val); + return 0; +} + +static int sst_platform_compr_set_params(struct snd_compr_stream *cstream, + struct snd_compr_params *params) +{ + struct sst_runtime_stream *stream; + int retval = 0; + struct snd_sst_params str_params; + struct sst_compress_cb cb; + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_platform *platform = rtd->platform; + struct sst_data *ctx = snd_soc_platform_get_drvdata(platform); + + pr_debug("In function %s\n", __func__); + stream = cstream->runtime->private_data; + /* construct fw structure for this*/ + memset(&str_params, 0, sizeof(str_params)); + + /* fill the device type and stream id to pass to SST driver */ + retval = sst_fill_stream_params(cstream, ctx, &str_params, true); + pr_debug("compr_set_params: fill stream params ret_val = 0x%x\n", retval); + if (retval < 0) + return retval; + + switch (params->codec.id) { + case SND_AUDIOCODEC_MP3: { + str_params.codec = SST_CODEC_TYPE_MP3; + str_params.sparams.uc.mp3_params.num_chan = params->codec.ch_in; + str_params.sparams.uc.mp3_params.pcm_wd_sz = 16; + break; + } + + case SND_AUDIOCODEC_AAC: { + str_params.codec = SST_CODEC_TYPE_AAC; + str_params.sparams.uc.aac_params.num_chan = params->codec.ch_in; + str_params.sparams.uc.aac_params.pcm_wd_sz = 16; + if (params->codec.format == SND_AUDIOSTREAMFORMAT_MP4ADTS) + str_params.sparams.uc.aac_params.bs_format = + AAC_BIT_STREAM_ADTS; + else if (params->codec.format == SND_AUDIOSTREAMFORMAT_RAW) + str_params.sparams.uc.aac_params.bs_format = + AAC_BIT_STREAM_RAW; + else { + pr_err("Undefined format%d\n", params->codec.format); + return -EINVAL; + } + str_params.sparams.uc.aac_params.externalsr = + params->codec.sample_rate; + break; + } + + default: + pr_err("codec not supported, id =%d\n", params->codec.id); + return -EINVAL; + } + + str_params.aparams.ring_buf_info[0].addr = + virt_to_phys(cstream->runtime->buffer); + str_params.aparams.ring_buf_info[0].size = + cstream->runtime->buffer_size; + str_params.aparams.sg_count = 1; + str_params.aparams.frag_size = cstream->runtime->fragment_size; + + cb.param = cstream; + cb.compr_cb = sst_compr_fragment_elapsed; + cb.drain_cb_param = cstream; + cb.drain_notify = sst_drain_notify; + + retval = stream->compr_ops->open(&str_params, &cb); + if (retval < 0) { + pr_err("stream allocation failed %d\n", retval); + return retval; + } + + stream->id = retval; + return 0; +} + +static int sst_platform_compr_trigger(struct snd_compr_stream *cstream, int cmd) +{ + struct sst_runtime_stream *stream = + cstream->runtime->private_data; + + return stream->compr_ops->control(cmd, stream->id); +} + +static int sst_platform_compr_pointer(struct snd_compr_stream *cstream, + struct snd_compr_tstamp *tstamp) +{ + struct sst_runtime_stream *stream; + + stream = cstream->runtime->private_data; + stream->compr_ops->tstamp(stream->id, tstamp); + tstamp->byte_offset = tstamp->copied_total % + (u32)cstream->runtime->buffer_size; + pr_debug("calc bytes offset/copied bytes as %d\n", tstamp->byte_offset); + return 0; +} + +static int sst_platform_compr_ack(struct snd_compr_stream *cstream, + size_t bytes) +{ + struct sst_runtime_stream *stream; + + stream = cstream->runtime->private_data; + stream->compr_ops->ack(stream->id, (unsigned long)bytes); + stream->bytes_written += bytes; + + return 0; +} + +static int sst_platform_compr_get_caps(struct snd_compr_stream *cstream, + struct snd_compr_caps *caps) +{ + struct sst_runtime_stream *stream = + cstream->runtime->private_data; + + return stream->compr_ops->get_caps(caps); +} + +static int sst_platform_compr_get_codec_caps(struct snd_compr_stream *cstream, + struct snd_compr_codec_caps *codec) +{ + struct sst_runtime_stream *stream = + cstream->runtime->private_data; + + return stream->compr_ops->get_codec_caps(codec); +} + +static int sst_platform_compr_set_metadata(struct snd_compr_stream *cstream, + struct snd_compr_metadata *metadata) +{ + struct sst_runtime_stream *stream = + cstream->runtime->private_data; + + return stream->compr_ops->set_metadata(stream->id, metadata); +} + +struct snd_compr_ops sst_platform_compr_ops = { + + .open = sst_platform_compr_open, + .free = sst_platform_compr_free, + .set_params = sst_platform_compr_set_params, + .set_metadata = sst_platform_compr_set_metadata, + .trigger = sst_platform_compr_trigger, + .pointer = sst_platform_compr_pointer, + .ack = sst_platform_compr_ack, + .get_caps = sst_platform_compr_get_caps, + .get_codec_caps = sst_platform_compr_get_codec_caps, +}; diff --git a/sound/soc/intel/effects.c b/sound/soc/intel/effects.c new file mode 100644 index 000000000000..6bb4199d4726 --- /dev/null +++ b/sound/soc/intel/effects.c @@ -0,0 +1,405 @@ +/* + * effects.c - platform file for effects interface + * + * Copyright (C) 2013 Intel Corporation + * Authors: Samreen Nilofer + * Vinod Koul + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ +#include +#include +#include "platform_ipc_v2.h" +#include "sst_platform.h" +#include "sst_platform_pvt.h" + +extern struct sst_device *sst_dsp; +extern struct device *sst_pdev; + +struct effect_uuid { + uint32_t timeLow; + uint16_t timeMid; + uint16_t timeHiAndVersion; + uint16_t clockSeq; + uint8_t node[6]; +}; + +#define EFFECT_STRING_LEN_MAX 64 + +enum sst_effect { + EFFECTS_CREATE = 0, + EFFECTS_DESTROY, + EFFECTS_SET_PARAMS, + EFFECTS_GET_PARAMS, +}; + +enum sst_mixer_output_mode { + SST_MEDIA0_OUT, + SST_MEDIA1_OUT, +}; + +static inline void sst_fill_byte_stream(struct snd_sst_bytes_v2 *bytes, u8 type, + u8 msg, u8 block, u8 task, u8 pipe_id, u16 len, + struct ipc_effect_payload *payload) +{ + u32 size = sizeof(struct ipc_effect_dsp_hdr); + + bytes->type = type; + bytes->ipc_msg = msg; + bytes->block = block; + bytes->task_id = task; + bytes->pipe_id = pipe_id; + bytes->len = len; + + /* Copy the ipc_effect_dsp_hdr followed by the data */ + memcpy(bytes->bytes, payload, size); + memcpy(bytes->bytes + size, payload->data, len - size); +} + +static int sst_send_effects(struct ipc_effect_payload *dsp_payload, int data_len, + enum sst_effect effect_type) +{ + struct snd_sst_bytes_v2 *bytes; + u32 len; + int ret; + u8 type, msg = IPC_INVALID, pipe, payload_len; + struct sst_data *sst; + + if (!sst_pdev) + return -ENODEV; + sst = dev_get_drvdata(sst_pdev); + + len = sizeof(*bytes) + sizeof(struct ipc_effect_dsp_hdr) + data_len; + + bytes = kzalloc(len, GFP_KERNEL); + if (!bytes) { + pr_err("kzalloc failed allocate bytes\n"); + return -ENOMEM; + } + + switch (effect_type) { + case EFFECTS_CREATE: + case EFFECTS_DESTROY: + type = SND_SST_BYTES_SET; + msg = IPC_CMD; + break; + + case EFFECTS_SET_PARAMS: + type = SND_SST_BYTES_SET; + msg = IPC_SET_PARAMS; + break; + + case EFFECTS_GET_PARAMS: + type = SND_SST_BYTES_GET; + msg = IPC_GET_PARAMS; + break; + default: + pr_err("No such effect %#x", effect_type); + ret = -EINVAL; + goto free_bytes; + } + + pipe = dsp_payload->dsp_hdr.pipe_id; + payload_len = sizeof(struct ipc_effect_dsp_hdr) + data_len; + sst_fill_byte_stream(bytes, type, msg, 1, SST_TASK_ID_MEDIA, + pipe, payload_len, dsp_payload); + + mutex_lock(&sst->lock); + ret = sst_dsp->ops->set_generic_params(SST_SET_BYTE_STREAM, bytes); + mutex_unlock(&sst->lock); + + if (ret) { + pr_err("byte_stream failed err %d pipe_id %#x\n", ret, + dsp_payload->dsp_hdr.pipe_id); + goto free_bytes; + } + + /* Copy only the data - skip the dsp header */ + if (msg == IPC_GET_PARAMS) + memcpy(dsp_payload->data, bytes->bytes, data_len); + +free_bytes: + kfree(bytes); + return ret; +} + +static int sst_get_algo_id(const struct sst_dev_effects *pdev_effs, + char *uuid, u16 *algo_id) +{ + int i, len; + + len = pdev_effs->effs_num_map; + + for (i = 0; i < len; i++) { + if (!strncmp(pdev_effs->effs_map[i].uuid, uuid, sizeof(struct effect_uuid))) { + *algo_id = pdev_effs->effs_map[i].algo_id; + return 0; + } + } + pr_err("no such uuid\n"); + return -EINVAL; +} + +static int sst_fill_effects_info(const struct sst_dev_effects *pdev_effs, + char *uuid, u16 pos, + struct ipc_dsp_effects_info *effs_info, u16 cmd_id) +{ + int i, len; + + len = pdev_effs->effs_num_map; + + for (i = 0; i < len; i++) { + if (!strncmp(pdev_effs->effs_map[i].uuid, uuid, sizeof(struct effect_uuid))) { + + effs_info->cmd_id = cmd_id; + effs_info->length = (sizeof(struct ipc_dsp_effects_info) - + offsetof(struct ipc_dsp_effects_info, sel_pos)); + effs_info->sel_pos = pos; + effs_info->sel_algo_id = pdev_effs->effs_map[i].algo_id; + effs_info->cpu_load = pdev_effs->effs_res_map[i].cpuLoad; + effs_info->memory_usage = pdev_effs->effs_res_map[i].memoryUsage; + effs_info->flags = pdev_effs->effs_res_map[i].flags; + + return 0; + } + } + + pr_err("no such uuid\n"); + return -EINVAL; +} + +static inline void sst_fill_dsp_payload(struct ipc_effect_payload *dsp_payload, + u8 pipe_id, u16 mod_id, char *data) +{ + dsp_payload->dsp_hdr.mod_index_id = 0xFF; + dsp_payload->dsp_hdr.pipe_id = pipe_id; + dsp_payload->dsp_hdr.mod_id = mod_id; + dsp_payload->data = data; +} + +static int sst_get_pipe_id(struct sst_dev_stream_map *map, int map_size, + int dev, int mode, u8 *pipe_id) +{ + int index; + + if (map == NULL) + return -EINVAL; + + /* In case of global effects, dev will be 0xff */ + if (dev == 0xFF) { + *pipe_id = (mode == SST_MEDIA0_OUT) ? PIPE_MEDIA0_OUT : PIPE_MEDIA1_OUT; + return 0; + } + + for (index = 1; index < map_size; index++) { + if (map[index].dev_num == dev) { + *pipe_id = map[index].device_id; + break; + } + } + + if (index == map_size) { + pr_err("no such device %d\n", dev); + return -ENODEV; + } + return 0; +} + +static int sst_effects_create(struct snd_card *card, struct snd_effect *effect) +{ + int ret = 0; + u8 pipe_id; + struct ipc_effect_payload dsp_payload; + struct ipc_dsp_effects_info effects_info; + struct sst_data *sst; + + if (!sst_pdev) + return -ENODEV; + sst = dev_get_drvdata(sst_pdev); + + ret = sst_fill_effects_info(&sst->pdata->pdev_effs, effect->uuid, effect->pos, + &effects_info, IPC_EFFECTS_CREATE); + if (ret < 0) + return ret; + + ret = sst_get_pipe_id(sst->pdata->pdev_strm_map, + sst->pdata->strm_map_size, + effect->device, effect->mode, &pipe_id); + if (ret < 0) + return ret; + + sst_fill_dsp_payload(&dsp_payload, pipe_id, 0xFF, (char *)&effects_info); + + ret = sst_send_effects(&dsp_payload, sizeof(effects_info), EFFECTS_CREATE); + + if (ret < 0) + return ret; + + return 0; +} + +static int sst_effects_destroy(struct snd_card *card, struct snd_effect *effect) +{ + int ret = 0; + u8 pipe_id; + struct ipc_effect_payload dsp_payload; + struct ipc_dsp_effects_info effects_info; + struct sst_data *sst; + + if (!sst_pdev) + return -ENODEV; + sst = dev_get_drvdata(sst_pdev); + + ret = sst_fill_effects_info(&sst->pdata->pdev_effs, effect->uuid, effect->pos, + &effects_info, IPC_EFFECTS_DESTROY); + if (ret < 0) + return ret; + + ret = sst_get_pipe_id(sst->pdata->pdev_strm_map, + sst->pdata->strm_map_size, + effect->device, effect->mode, &pipe_id); + if (ret < 0) + return ret; + + sst_fill_dsp_payload(&dsp_payload, pipe_id, 0xFF, (char *)&effects_info); + + ret = sst_send_effects(&dsp_payload, sizeof(effects_info), EFFECTS_DESTROY); + + if (ret < 0) + return ret; + + return 0; +} + +static int sst_effects_set_params(struct snd_card *card, + struct snd_effect_params *params) +{ + int ret = 0; + u8 pipe_id; + u16 algo_id; + struct ipc_effect_payload dsp_payload; + struct sst_data *sst; + + if (!sst_pdev) + return -ENODEV; + sst = dev_get_drvdata(sst_pdev); + + ret = sst_get_algo_id(&sst->pdata->pdev_effs, params->uuid, &algo_id); + if (ret < 0) + return ret; + + ret = sst_get_pipe_id(sst->pdata->pdev_strm_map, + sst->pdata->strm_map_size, + params->device, SST_MEDIA0_OUT, &pipe_id); + if (ret < 0) + return ret; + + sst_fill_dsp_payload(&dsp_payload, pipe_id, algo_id, params->buffer); + + ret = sst_send_effects(&dsp_payload, params->size, EFFECTS_SET_PARAMS); + + if (ret < 0) + return ret; + + return 0; +} + +static int sst_effects_get_params(struct snd_card *card, + struct snd_effect_params *params) +{ + int ret = 0; + u8 pipe_id; + u16 algo_id; + struct ipc_effect_payload dsp_payload; + struct sst_data *sst; + + if (!sst_pdev) + return -ENODEV; + sst = dev_get_drvdata(sst_pdev); + + ret = sst_get_algo_id(&sst->pdata->pdev_effs, params->uuid, &algo_id); + if (ret < 0) + return ret; + + ret = sst_get_pipe_id(sst->pdata->pdev_strm_map, + sst->pdata->strm_map_size, + params->device, SST_MEDIA0_OUT, &pipe_id); + if (ret < 0) + return ret; + + sst_fill_dsp_payload(&dsp_payload, pipe_id, algo_id, params->buffer); + + ret = sst_send_effects(&dsp_payload, params->size, EFFECTS_GET_PARAMS); + + if (ret < 0) + return ret; + + return 0; +} + +static int sst_query_num_effects(struct snd_card *card) +{ + struct sst_data *sst; + + if (!sst_pdev) + return -ENODEV; + sst = dev_get_drvdata(sst_pdev); + + return sst->pdata->pdev_effs.effs_num_map; +} + +static int sst_query_effects_caps(struct snd_card *card, + struct snd_effect_caps *caps) +{ + struct sst_data *sst; + struct sst_dev_effects_map *effs_map; + unsigned int num_effects, offset = 0; + char *dstn; + int i; + + if (!sst_pdev) + return -ENODEV; + sst = dev_get_drvdata(sst_pdev); + + effs_map = sst->pdata->pdev_effs.effs_map; + num_effects = sst->pdata->pdev_effs.effs_num_map; + + if (caps->size < (num_effects * MAX_DESCRIPTOR_SIZE)) { + pr_err("buffer size is insufficient\n"); + return -ENOMEM; + } + + dstn = caps->buffer; + for (i = 0; i < num_effects; i++) { + memcpy(dstn + offset, effs_map[i].descriptor, MAX_DESCRIPTOR_SIZE); + offset += MAX_DESCRIPTOR_SIZE; + } + caps->size = offset; + + return 0; +} + +struct snd_effect_ops effects_ops = { + .create = sst_effects_create, + .destroy = sst_effects_destroy, + .set_params = sst_effects_set_params, + .get_params = sst_effects_get_params, + .query_num_effects = sst_query_num_effects, + .query_effect_caps = sst_query_effects_caps, +}; diff --git a/sound/soc/intel/pcm.c b/sound/soc/intel/pcm.c new file mode 100644 index 000000000000..860014723433 --- /dev/null +++ b/sound/soc/intel/pcm.c @@ -0,0 +1,1021 @@ +/* + * pcm.c - Intel MID Platform driver file implementing PCM functionality + * + * Copyright (C) 2010-2013 Intel Corp + * Author: Vinod Koul + * Author: Harsha Priya + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "platform_ipc_v2.h" +#include "sst_platform.h" +#include "sst_platform_pvt.h" + +struct device *sst_pdev; +struct sst_device *sst_dsp; +extern struct snd_compr_ops sst_platform_compr_ops; +extern struct snd_effect_ops effects_ops; + +static DEFINE_MUTEX(sst_dsp_lock); + +static struct snd_pcm_hardware sst_platform_pcm_hw = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_DOUBLE | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_MMAP| + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_SYNC_START), + .formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 | + SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24 | + SNDRV_PCM_FMTBIT_S32 | SNDRV_PCM_FMTBIT_U32), + .rates = (SNDRV_PCM_RATE_8000| + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000), + .rate_min = SST_MIN_RATE, + .rate_max = SST_MAX_RATE, + .channels_min = SST_MIN_CHANNEL, + .channels_max = SST_MAX_CHANNEL, + .buffer_bytes_max = SST_MAX_BUFFER, + .period_bytes_min = SST_MIN_PERIOD_BYTES, + .period_bytes_max = SST_MAX_PERIOD_BYTES, + .periods_min = SST_MIN_PERIODS, + .periods_max = SST_MAX_PERIODS, + .fifo_size = SST_FIFO_SIZE, +}; + +static int sst_platform_ihf_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) { + struct snd_sst_runtime_params params_data; + int channels = slots; + + /* registering with SST driver to get access to SST APIs to use */ + if (!sst_dsp) { + pr_err("sst: DSP not registered\n"); + return -EIO; + } + params_data.type = SST_SET_CHANNEL_INFO; + params_data.str_id = SND_SST_DEVICE_IHF; + params_data.size = sizeof(channels); + params_data.addr = &channels; + return sst_dsp->ops->set_generic_params(SST_SET_RUNTIME_PARAMS, + (void *)¶ms_data); +} + +static int sst_media_digital_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + + pr_debug("%s: enter, mute=%d dai-name=%s dir=%d\n", __func__, mute, dai->name, stream); + +#if IS_BUILTIN(CONFIG_SST_MRFLD_DPCM) + sst_send_pipe_gains(dai, stream, mute); +#endif + + return 0; +} + +/* helper functions */ +void sst_set_stream_status(struct sst_runtime_stream *stream, + int state) +{ + unsigned long flags; + spin_lock_irqsave(&stream->status_lock, flags); + stream->stream_status = state; + spin_unlock_irqrestore(&stream->status_lock, flags); +} + +static inline int sst_get_stream_status(struct sst_runtime_stream *stream) +{ + int state; + unsigned long flags; + + spin_lock_irqsave(&stream->status_lock, flags); + state = stream->stream_status; + spin_unlock_irqrestore(&stream->status_lock, flags); + return state; +} + +static void sst_fill_alloc_params(struct snd_pcm_substream *substream, + struct snd_sst_alloc_params_ext *alloc_param) +{ + unsigned int channels; + snd_pcm_uframes_t period_size; + ssize_t periodbytes; + ssize_t buffer_bytes = snd_pcm_lib_buffer_bytes(substream); + u32 buffer_addr = virt_to_phys(substream->dma_buffer.area); + + channels = substream->runtime->channels; + period_size = substream->runtime->period_size; + periodbytes = samples_to_bytes(substream->runtime, period_size); + alloc_param->ring_buf_info[0].addr = buffer_addr; + alloc_param->ring_buf_info[0].size = buffer_bytes; + alloc_param->sg_count = 1; + alloc_param->reserved = 0; + alloc_param->frag_size = periodbytes * channels; + + pr_debug("period_size = %d\n", alloc_param->frag_size); + pr_debug("ring_buf_addr = 0x%x\n", alloc_param->ring_buf_info[0].addr); +} +static void sst_fill_pcm_params(struct snd_pcm_substream *substream, + struct snd_sst_stream_params *param) +{ + param->uc.pcm_params.num_chan = (u8) substream->runtime->channels; + param->uc.pcm_params.pcm_wd_sz = substream->runtime->sample_bits; + param->uc.pcm_params.sfreq = substream->runtime->rate; + + /* PCM stream via ALSA interface */ + param->uc.pcm_params.use_offload_path = 0; + param->uc.pcm_params.reserved2 = 0; + memset(param->uc.pcm_params.channel_map, 0, sizeof(u8)); + pr_debug("sfreq= %d, wd_sz = %d\n", + param->uc.pcm_params.sfreq, param->uc.pcm_params.pcm_wd_sz); + +} + +#define ASSIGN_PIPE_ID(periodtime, lowlatency, deepbuffer) \ + ((periodtime) <= (lowlatency) ? PIPE_LOW_PCM0_IN : \ + ((periodtime) >= (deepbuffer) ? PIPE_MEDIA3_IN : PIPE_MEDIA1_IN)) + +static int sst_get_stream_mapping(int dev, int sdev, int dir, + struct sst_dev_stream_map *map, int size, u8 pipe_id, + const struct sst_lowlatency_deepbuff *ll_db) +{ + int index; + unsigned long pt = 0, ll = 0, db = 0; + + if (map == NULL) + return -EINVAL; + + pr_debug("dev %d sdev %d dir %d\n", dev, sdev, dir); + + /* index 0 is not used in stream map */ + for (index = 1; index < size; index++) { + if ((map[index].dev_num == dev) && + (map[index].subdev_num == sdev) && + (map[index].direction == dir)) { + /* device id for the probe is assigned dynamically */ + if (map[index].status == SST_DEV_MAP_IN_USE) { + return index; + } else if (map[index].status == SST_DEV_MAP_FREE) { + map[index].status = SST_DEV_MAP_IN_USE; + + if (map[index].dev_num == MERR_SALTBAY_PROBE) { + map[index].device_id = pipe_id; + + } else if (map[index].dev_num == MERR_SALTBAY_AUDIO) { + if (!ll_db->low_latency || !ll_db->deep_buffer) + return -EINVAL; + + pt = ll_db->period_time; + ll = *(ll_db->low_latency); + db = *(ll_db->deep_buffer); + + pr_debug("PT %lu LL %lu DB %lu\n", pt, ll, db); + + map[index].device_id = ASSIGN_PIPE_ID(pt, + ll, db); + } + pr_debug("%s: pipe_id 0%x index %d", __func__, + map[index].device_id, index); + + return index; + } + } + } + return 0; +} + +int sst_fill_stream_params(void *substream, + const struct sst_data *ctx, struct snd_sst_params *str_params, bool is_compress) +{ + int map_size; + int index; + struct sst_dev_stream_map *map; + struct snd_pcm_substream *pstream = NULL; + struct snd_compr_stream *cstream = NULL; + + map = ctx->pdata->pdev_strm_map; + map_size = ctx->pdata->strm_map_size; + + if (is_compress == true) + cstream = (struct snd_compr_stream *)substream; + else + pstream = (struct snd_pcm_substream *)substream; + + str_params->stream_type = SST_STREAM_TYPE_MUSIC; + + /* For pcm streams */ + if (pstream) { + index = sst_get_stream_mapping(pstream->pcm->device, + pstream->number, pstream->stream, + map, map_size, ctx->pipe_id, &ctx->ll_db); + if (index <= 0) + return -EINVAL; + + str_params->stream_id = index; + str_params->device_type = map[index].device_id; + str_params->task = map[index].task_id; + + if (str_params->device_type == SST_PROBE_IN) + str_params->stream_type = SST_STREAM_TYPE_PROBE; + + pr_debug("str_id = %d, device_type = 0x%x, task = %d", + str_params->stream_id, str_params->device_type, + str_params->task); + + str_params->ops = (u8)pstream->stream; + } + + if (cstream) { + /* FIXME: Add support for subdevice number in + * snd_compr_stream */ + index = sst_get_stream_mapping(cstream->device->device, + 0, cstream->direction, + map, map_size, ctx->pipe_id, &ctx->ll_db); + if (index <= 0) + return -EINVAL; + str_params->stream_id = index; + str_params->device_type = map[index].device_id; + str_params->task = map[index].task_id; + pr_debug("compress str_id = %d, device_type = 0x%x, task = %d", + str_params->stream_id, str_params->device_type, + str_params->task); + + str_params->ops = (u8)cstream->direction; + } + return 0; +} + +#define CALC_PERIODTIME(period_size, rate) (((period_size) * 1000) / (rate)) + +static int sst_platform_alloc_stream(struct snd_pcm_substream *substream, + struct snd_soc_platform *platform) +{ + struct sst_runtime_stream *stream = + substream->runtime->private_data; + struct snd_sst_stream_params param = {{{0,},},}; + struct snd_sst_params str_params = {0}; + struct snd_sst_alloc_params_ext alloc_params = {0}; + int ret_val = 0; + struct sst_data *ctx = snd_soc_platform_get_drvdata(platform); + + /* set codec params and inform SST driver the same */ + sst_fill_pcm_params(substream, ¶m); + sst_fill_alloc_params(substream, &alloc_params); + substream->runtime->dma_area = substream->dma_buffer.area; + str_params.sparams = param; + str_params.aparams = alloc_params; + str_params.codec = SST_CODEC_TYPE_PCM; + + ctx->ll_db.period_time = CALC_PERIODTIME(substream->runtime->period_size, + substream->runtime->rate); + + /* fill the device type and stream id to pass to SST driver */ + ret_val = sst_fill_stream_params(substream, ctx, &str_params, false); + pr_debug("platform prepare: fill stream params ret_val = 0x%x\n", ret_val); + if (ret_val < 0) + return ret_val; + + stream->stream_info.str_id = str_params.stream_id; + + ret_val = stream->ops->open(&str_params); + pr_debug("platform prepare: stream open ret_val = 0x%x\n", ret_val); + if (ret_val <= 0) + return ret_val; + + pr_debug("platform allocated strid: %d\n", stream->stream_info.str_id); + + return ret_val; +} + +static void sst_period_elapsed(void *mad_substream) +{ + struct snd_pcm_substream *substream = mad_substream; + struct sst_runtime_stream *stream; + int status; + + if (!substream || !substream->runtime) { + pr_debug("In %s : Null Substream pointer\n", __func__); + return; + } + stream = substream->runtime->private_data; + if (!stream) { + pr_debug("In %s : Null Stream pointer\n", __func__); + return; + } + status = sst_get_stream_status(stream); + if (status != SST_PLATFORM_RUNNING) { + pr_debug("In %s : Stream Status=%d\n", __func__, status); + return; + } + snd_pcm_period_elapsed(substream); +} + +static int sst_platform_init_stream(struct snd_pcm_substream *substream) +{ + struct sst_runtime_stream *stream = + substream->runtime->private_data; + int ret_val; + + pr_debug("setting buffer ptr param\n"); + sst_set_stream_status(stream, SST_PLATFORM_INIT); + stream->stream_info.period_elapsed = sst_period_elapsed; + stream->stream_info.mad_substream = substream; + stream->stream_info.buffer_ptr = 0; + stream->stream_info.sfreq = substream->runtime->rate; + pr_debug("pcm_substream %p, period_elapsed %p\n", + stream->stream_info.mad_substream, stream->stream_info.period_elapsed); + ret_val = stream->ops->device_control( + SST_SND_STREAM_INIT, &stream->stream_info); + if (ret_val) + pr_err("control_set ret error %d\n", ret_val); + return ret_val; + +} + +static inline int power_up_sst(struct sst_runtime_stream *sst) +{ + return sst->ops->power(true); +} + +static inline int power_down_sst(struct sst_runtime_stream *sst) +{ + return sst->ops->power(false); +} +/* end -- helper functions */ + +static int sst_media_open(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret_val = 0; + struct snd_pcm_runtime *runtime = substream->runtime; + struct sst_runtime_stream *stream; + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; + + spin_lock_init(&stream->status_lock); + + /* get the sst ops */ + mutex_lock(&sst_dsp_lock); + if (!sst_dsp || + !try_module_get(sst_dsp->dev->driver->owner)) { + pr_err("no device available to run\n"); + ret_val = -ENODEV; + goto out_ops; + } + stream->ops = sst_dsp->ops; + mutex_unlock(&sst_dsp_lock); + + stream->stream_info.str_id = 0; + sst_set_stream_status(stream, SST_PLATFORM_UNINIT); + stream->stream_info.mad_substream = substream; + runtime->private_data = stream; + + if (strstr(dai->name, "Power-cpu-dai")) + return power_up_sst(stream); + + /* Make sure, that the period size is always even */ + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIODS, 2); + + pr_debug("buf_ptr %llu\n", stream->stream_info.buffer_ptr); + return snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); +out_ops: + kfree(stream); + mutex_unlock(&sst_dsp_lock); + return ret_val; +} + +static void sst_free_stream_in_use(struct sst_dev_stream_map *map, int str_id) +{ +#if IS_BUILTIN(CONFIG_SST_MRFLD_DPCM) + return; +#else + if ((map[str_id].dev_num == MERR_SALTBAY_AUDIO) || + (map[str_id].dev_num == MERR_SALTBAY_PROBE)) { + + /* Do nothing in capture for audio device */ + if ((map[str_id].dev_num == MERR_SALTBAY_AUDIO) && + (map[str_id].direction == SNDRV_PCM_STREAM_CAPTURE)) + return; + if ((map[str_id].task_id == SST_TASK_ID_MEDIA) && + (map[str_id].status == SST_DEV_MAP_IN_USE)) { + pr_debug("str_id %d device_id 0x%x\n", str_id, map[str_id].device_id); + map[str_id].status = SST_DEV_MAP_FREE; + map[str_id].device_id = PIPE_RSVD; + } + } + return; +#endif +} + +static void sst_media_close(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sst_runtime_stream *stream; + int ret_val = 0, str_id; + struct sst_data *ctx = snd_soc_platform_get_drvdata(dai->platform); + + stream = substream->runtime->private_data; + if (strstr(dai->name, "Power-cpu-dai")) + ret_val = power_down_sst(stream); + + str_id = stream->stream_info.str_id; + if (str_id) + ret_val = stream->ops->close(str_id); + sst_free_stream_in_use(ctx->pdata->pdev_strm_map, str_id); + module_put(sst_dsp->dev->driver->owner); + kfree(stream); + pr_debug("%s: %d\n", __func__, ret_val); +} + +static int sst_dpcm_probe_cmd(struct snd_soc_platform *platform, + struct snd_pcm_substream *substream, u16 pipe_id, bool on) +{ + int ret = 0; +#if IS_BUILTIN(CONFIG_SST_MRFLD_DPCM) + if (substream->pcm->device == MERR_DPCM_PROBE) + ret = sst_dpcm_probe_send(platform, pipe_id, substream->number, + substream->stream, on); +#endif + return ret; +} + +static inline unsigned int get_current_pipe_id(struct snd_soc_platform *platform, + struct snd_pcm_substream *substream) +{ + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + struct sst_dev_stream_map *map = sst->pdata->pdev_strm_map; + struct sst_runtime_stream *stream = + substream->runtime->private_data; + u32 str_id = stream->stream_info.str_id; + unsigned int pipe_id; + pipe_id = map[str_id].device_id; + + pr_debug("%s: got pipe_id = %#x for str_id = %d\n", + __func__, pipe_id, str_id); + return pipe_id; +} + +static void sst_probe_close(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + u16 probe_pipe_id = get_current_pipe_id(dai->platform, substream); + + sst_dpcm_probe_cmd(dai->platform, substream, probe_pipe_id, false); + sst_media_close(substream, dai); +} + +static int sst_media_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sst_runtime_stream *stream; + int ret_val = 0, str_id; + + pr_debug("%s\n", __func__); + + stream = substream->runtime->private_data; + str_id = stream->stream_info.str_id; + if (stream->stream_info.str_id) + return ret_val; + + ret_val = sst_platform_alloc_stream(substream, dai->platform); + if (ret_val <= 0) + return ret_val; + snprintf(substream->pcm->id, sizeof(substream->pcm->id), + "%d", stream->stream_info.str_id); + + ret_val = sst_platform_init_stream(substream); + if (ret_val) + return ret_val; + substream->runtime->hw.info = SNDRV_PCM_INFO_BLOCK_TRANSFER; + + return ret_val; +} + +static int sst_probe_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + u16 probe_pipe_id; + + sst_media_prepare(substream, dai); + probe_pipe_id = get_current_pipe_id(dai->platform, substream); + + return sst_dpcm_probe_cmd(dai->platform, substream, probe_pipe_id, true); +} + +static int sst_media_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + pr_debug("%s\n", __func__); + + snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + memset(substream->runtime->dma_area, 0, params_buffer_bytes(params)); + return 0; +} + +static int sst_media_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return snd_pcm_lib_free_pages(substream); +} + +static struct snd_soc_dai_ops sst_media_dai_ops = { + .startup = sst_media_open, + .shutdown = sst_media_close, + .prepare = sst_media_prepare, + .hw_params = sst_media_hw_params, + .hw_free = sst_media_hw_free, + .set_tdm_slot = sst_platform_ihf_set_tdm_slot, + .mute_stream = sst_media_digital_mute, +}; + +static struct snd_soc_dai_ops sst_probe_dai_ops = { + .startup = sst_media_open, + .hw_params = sst_media_hw_params, + .hw_free = sst_media_hw_free, + .shutdown = sst_probe_close, + .prepare = sst_probe_prepare, +}; + +static struct snd_soc_dai_ops sst_loopback_dai_ops = { + .startup = sst_media_open, + .shutdown = sst_media_close, + .prepare = sst_media_prepare, +}; + +static struct snd_soc_dai_ops sst_compr_dai_ops = { + .mute_stream = sst_media_digital_mute, +}; + +static struct snd_soc_dai_driver sst_platform_dai[] = { +{ + .name = SST_HEADSET_DAI, + .ops = &sst_media_dai_ops, + .playback = { + .stream_name = "Headset Playback", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Headset Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = SST_DEEPBUFFER_DAI, + .ops = &sst_media_dai_ops, + .playback = { + .stream_name = "Deepbuffer Playback", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = SST_LOWLATENCY_DAI, + .ops = &sst_media_dai_ops, + .playback = { + .stream_name = "Low Latency Playback", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = SST_SPEAKER_DAI, + .ops = &sst_media_dai_ops, + .playback = { + .stream_name = "Speaker Playback", + .channels_min = SST_MONO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = SST_VOICE_DAI, + .playback = { + .stream_name = "Voice Downlink", + .channels_min = SST_MONO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Voice Uplink", + .channels_min = SST_MONO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = SST_COMPRESS_DAI, + .compress_dai = 1, + .ops = &sst_compr_dai_ops, + .playback = { + .stream_name = "Compress Playback", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = SST_VIRTUAL_DAI, + .playback = { + .stream_name = "Virtual Playback", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = SST_POWER_DAI, + .ops = &sst_media_dai_ops, + .playback = { + .stream_name = "Dummy Power Stream", + .channels_min = SST_MONO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = SST_PROBE_DAI, + .ops = &sst_probe_dai_ops, + .playback = { + .stream_name = "Probe Playback", + .channels_min = SST_MONO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, + .capture = { + .stream_name = "Probe Capture", + .channels_min = SST_MONO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = SST_VOIP_DAI, + .ops = &sst_media_dai_ops, + .playback = { + .stream_name = "VOIP Playback", + .channels_min = SST_MONO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "VOIP Capture", + .channels_min = SST_MONO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +{ + .name = SST_LOOPBACK_DAI, + .ops = &sst_loopback_dai_ops, + .capture = { + .stream_name = "Loopback Capture", + .channels_min = SST_MONO, + .channels_max = SST_MONO, + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +/*BE CPU Dais */ +{ + .name = "ssp2-codec", + .playback = { + .stream_name = "ssp2 playback", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "ssp2 Capture", + .channels_min = SST_STEREO, + .channels_max = SST_STEREO, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +}; + +static int sst_platform_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + + pr_debug("sst_platform_open called:%s\n", dai_link->cpu_dai_name); + if (substream->pcm->internal) + return 0; + runtime = substream->runtime; + runtime->hw = sst_platform_pcm_hw; + return 0; +} + +static int sst_platform_close(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + pr_debug("sst_platform_close called:%s\n", dai_link->cpu_dai_name); + return 0; +} + +static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + int ret_val = 0, str_id; + struct sst_runtime_stream *stream; + int str_cmd, status, alsa_state; + + if (substream->pcm->internal) + return 0; + pr_debug("sst_platform_pcm_trigger called\n"); + stream = substream->runtime->private_data; + str_id = stream->stream_info.str_id; + alsa_state = substream->runtime->status->state; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + pr_debug("Trigger Start\n"); + str_cmd = SST_SND_START; + status = SST_PLATFORM_RUNNING; + stream->stream_info.mad_substream = substream; + break; + case SNDRV_PCM_TRIGGER_STOP: + pr_debug("Trigger stop\n"); + str_cmd = SST_SND_DROP; + status = SST_PLATFORM_DROPPED; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + pr_debug("Trigger pause\n"); + str_cmd = SST_SND_PAUSE; + status = SST_PLATFORM_PAUSED; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + pr_debug("Trigger pause release\n"); + str_cmd = SST_SND_RESUME; + status = SST_PLATFORM_RUNNING; + break; + default: + return -EINVAL; + } + ret_val = stream->ops->device_control(str_cmd, &str_id); + if (!ret_val) + sst_set_stream_status(stream, status); + + return ret_val; +} + + +static snd_pcm_uframes_t sst_platform_pcm_pointer + (struct snd_pcm_substream *substream) +{ + struct sst_runtime_stream *stream; + int ret_val, status; + struct pcm_stream_info *str_info; + + stream = substream->runtime->private_data; + status = sst_get_stream_status(stream); + if (status == SST_PLATFORM_INIT) + return 0; + str_info = &stream->stream_info; + ret_val = stream->ops->device_control( + SST_SND_BUFFER_POINTER, str_info); + if (ret_val) { + pr_err("sst: error code = %d\n", ret_val); + return ret_val; + } + substream->runtime->soc_delay = str_info->pcm_delay; + return str_info->buffer_ptr; +} + +static struct snd_pcm_ops sst_platform_ops = { + .open = sst_platform_open, + .close = sst_platform_close, + .ioctl = snd_pcm_lib_ioctl, + .trigger = sst_platform_pcm_trigger, + .pointer = sst_platform_pcm_pointer, +}; + +static void sst_pcm_free(struct snd_pcm *pcm) +{ + pr_debug("sst_pcm_free called\n"); + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *dai = rtd->cpu_dai; + struct snd_pcm *pcm = rtd->pcm; + int retval = 0; + + pr_debug("sst_pcm_new called\n"); + if (dai->driver->playback.channels_min || + dai->driver->capture.channels_min) { + retval = snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_DMA), + SST_MAX_BUFFER, SST_MAX_BUFFER); + if (retval) { + pr_err("dma buffer allocationf fail\n"); + return retval; + } + } + return retval; +} + +static int sst_soc_probe(struct snd_soc_platform *platform) +{ + struct sst_data *ctx = snd_soc_platform_get_drvdata(platform); + struct soft_platform_id spid; + int ret = 0; + + memcpy(&spid, ctx->pdata->spid, sizeof(spid)); + pr_err("Enter:%s\n", __func__); + +#if IS_BUILTIN(CONFIG_SST_MRFLD_DPCM) + ret = sst_dsp_init_v2_dpcm(platform); +#else + ret = sst_dsp_init(platform); +#endif +// if (ret) +// return ret; +// ret = snd_soc_register_effect(platform->card, &effects_ops); + + return ret; +} + +static int sst_soc_remove(struct snd_soc_platform *platform) +{ + pr_debug("%s called\n", __func__); + return 0; +} + +static struct snd_soc_platform_driver sst_soc_platform_drv = { + .probe = sst_soc_probe, + .remove = sst_soc_remove, + .ops = &sst_platform_ops, + .compr_ops = &sst_platform_compr_ops, + .pcm_new = sst_pcm_new, + .pcm_free = sst_pcm_free, + .read = sst_soc_read, + .write = sst_soc_write, +}; + +int sst_register_dsp(struct sst_device *sst_dev) +{ + if (!sst_dev) + return -ENODEV; + mutex_lock(&sst_dsp_lock); + if (sst_dsp) { + pr_err("we already have a device %s\n", sst_dsp->name); + mutex_unlock(&sst_dsp_lock); + return -EEXIST; + } + pr_debug("registering device %s\n", sst_dev->name); + + sst_dsp = sst_dev; + mutex_unlock(&sst_dsp_lock); + return 0; +} +EXPORT_SYMBOL_GPL(sst_register_dsp); + +int sst_unregister_dsp(struct sst_device *dev) +{ + if (dev != sst_dsp) + return -EINVAL; + + mutex_lock(&sst_dsp_lock); + if (sst_dsp) { + pr_debug("unregister %s\n", sst_dsp->name); + mutex_unlock(&sst_dsp_lock); + return -EIO; + } + + sst_dsp = NULL; + mutex_unlock(&sst_dsp_lock); + return 0; +} +EXPORT_SYMBOL_GPL(sst_unregister_dsp); + +static const struct snd_soc_component_driver pcm_component = { + .name = "pcm", +}; + +static int sst_platform_probe(struct platform_device *pdev) +{ + struct sst_data *sst; + int ret; + struct sst_platform_data *pdata = pdev->dev.platform_data; + + pr_debug("sst_platform_probe called\n"); + sst = devm_kzalloc(&pdev->dev, sizeof(*sst), GFP_KERNEL); + if (sst == NULL) { + pr_err("kzalloc failed\n"); + return -ENOMEM; + } + + sst_pdev = &pdev->dev; + sst->pdata = pdata; + mutex_init(&sst->lock); + dev_set_drvdata(&pdev->dev, sst); + + ret = snd_soc_register_platform(&pdev->dev, + &sst_soc_platform_drv); + if (ret) { + pr_err("registering soc platform failed\n"); + return ret; + } + ret = snd_soc_register_component(&pdev->dev, &pcm_component, + sst_platform_dai, ARRAY_SIZE(sst_platform_dai)); + if (ret) { + pr_err("registering cpu dais failed\n"); + snd_soc_unregister_platform(&pdev->dev); + } + + return ret; +} + +static int sst_platform_remove(struct platform_device *pdev) +{ + + snd_soc_unregister_component(&pdev->dev); + snd_soc_unregister_platform(&pdev->dev); + pr_debug("sst_platform_remove success\n"); + return 0; +} + +static struct platform_driver sst_platform_driver = { + .driver = { + .name = "sst-platform", + .owner = THIS_MODULE, + }, + .probe = sst_platform_probe, + .remove = sst_platform_remove, +}; + +module_platform_driver(sst_platform_driver); + +MODULE_DESCRIPTION("ASoC Intel(R) MID Platform driver"); +MODULE_AUTHOR("Vinod Koul "); +MODULE_AUTHOR("Harsha Priya "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sst-platform"); diff --git a/sound/soc/intel/platform-libs/controls_v1.c b/sound/soc/intel/platform-libs/controls_v1.c new file mode 100644 index 000000000000..b6a5fa70f15c --- /dev/null +++ b/sound/soc/intel/platform-libs/controls_v1.c @@ -0,0 +1,184 @@ +/* + * controls_v1.c - Intel MID Platform driver ALSA controls for CTP + * + * Copyright (C) 2012 Intel Corp + * Author: Jeeja KP + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include +#include "../sst_platform.h" +#include "../sst_platform_pvt.h" + + +static int sst_set_mixer_param(unsigned int device_input_mixer) +{ + if (!sst_dsp) { + pr_err("sst: DSP not registered\n"); + return -ENODEV; + } + + /*allocate memory for params*/ + return sst_dsp->ops->set_generic_params(SST_SET_ALGO_PARAMS, + (void *)&device_input_mixer); +} +static int lpe_mixer_ihf_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + + ucontrol->value.integer.value[0] = sst->lpe_mixer_input_ihf; + return 0; +} + +static int lpe_mixer_ihf_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int device_input_mixer; + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + + switch (ucontrol->value.integer.value[0]) { + case 0: + pr_debug("input is None\n"); + device_input_mixer = SST_STREAM_DEVICE_IHF + | SST_INPUT_STREAM_NONE; + break; + case 1: + pr_debug("input is PCM stream\n"); + device_input_mixer = SST_STREAM_DEVICE_IHF + | SST_INPUT_STREAM_PCM; + break; + case 2: + pr_debug("input is Compress stream\n"); + device_input_mixer = SST_STREAM_DEVICE_IHF + | SST_INPUT_STREAM_COMPRESS; + break; + case 3: + pr_debug("input is Mixed stream\n"); + device_input_mixer = SST_STREAM_DEVICE_IHF + | SST_INPUT_STREAM_MIXED; + break; + default: + pr_err("Invalid Input:%ld\n", ucontrol->value.integer.value[0]); + return -EINVAL; + } + sst->lpe_mixer_input_ihf = ucontrol->value.integer.value[0]; + return sst_set_mixer_param(device_input_mixer); +} + +static int lpe_mixer_headset_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + + ucontrol->value.integer.value[0] = sst->lpe_mixer_input_hs; + return 0; +} + +static int lpe_mixer_headset_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int mixer_input_stream; + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + + switch (ucontrol->value.integer.value[0]) { + case 0: + pr_debug("input is None\n"); + mixer_input_stream = SST_STREAM_DEVICE_HS + | SST_INPUT_STREAM_NONE; + break; + case 1: + pr_debug("input is PCM stream\n"); + mixer_input_stream = SST_STREAM_DEVICE_HS + | SST_INPUT_STREAM_PCM; + break; + case 2: + pr_debug("input is Compress stream\n"); + mixer_input_stream = SST_STREAM_DEVICE_HS + | SST_INPUT_STREAM_COMPRESS; + break; + case 3: + pr_debug("input is Mixed stream\n"); + mixer_input_stream = SST_STREAM_DEVICE_HS + | SST_INPUT_STREAM_MIXED; + break; + default: + pr_err("Invalid Input:%ld\n", ucontrol->value.integer.value[0]); + return -EINVAL; + } + sst->lpe_mixer_input_hs = ucontrol->value.integer.value[0]; + return sst_set_mixer_param(mixer_input_stream); +} + +static int sst_probe_byte_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + if (!sst_dsp) { + pr_err("sst: DSP not registered\n"); + return -ENODEV; + } + + return sst_dsp->ops->set_generic_params(SST_GET_PROBE_BYTE_STREAM, + ucontrol->value.bytes.data); +} + +static int sst_probe_byte_control_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + if (!sst_dsp) { + pr_err("sst: DSP not registered\n"); + return -ENODEV; + } + + return sst_dsp->ops->set_generic_params(SST_SET_PROBE_BYTE_STREAM, + ucontrol->value.bytes.data); +} + +static const char *lpe_mixer_text[] = { + "None", "PCM", "Compressed", "Mixed", +}; + +static const struct soc_enum lpe_mixer_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lpe_mixer_text), lpe_mixer_text); + +static const struct snd_kcontrol_new sst_controls_clv[] = { + SOC_ENUM_EXT("LPE IHF mixer", lpe_mixer_enum, + lpe_mixer_ihf_get, lpe_mixer_ihf_set), + SOC_ENUM_EXT("LPE headset mixer", lpe_mixer_enum, + lpe_mixer_headset_get, lpe_mixer_headset_set), + SND_SOC_BYTES_EXT("SST Probe Byte Control", SST_MAX_BIN_BYTES, + sst_probe_byte_control_get, + sst_probe_byte_control_set), +}; + +int sst_platform_clv_init(struct snd_soc_platform *platform) +{ + struct sst_data *ctx = snd_soc_platform_get_drvdata(platform); + ctx->lpe_mixer_input_hs = 0; + ctx->lpe_mixer_input_ihf = 0; + snd_soc_add_platform_controls(platform, sst_controls_clv, + ARRAY_SIZE(sst_controls_clv)); + return 0; +} diff --git a/sound/soc/intel/platform-libs/controls_v2.c b/sound/soc/intel/platform-libs/controls_v2.c new file mode 100644 index 000000000000..3e80276698a3 --- /dev/null +++ b/sound/soc/intel/platform-libs/controls_v2.c @@ -0,0 +1,1770 @@ +/* + * controls_v2.c - Intel MID Platform driver ALSA controls for Mrfld + * + * Copyright (C) 2012 Intel Corp + * Author: Vinod Koul + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include "../platform_ipc_v2.h" +#include "../sst_platform.h" +#include "../sst_platform_pvt.h" +#include "ipc_lib.h" +#include "controls_v2.h" + + +#define SST_ALGO_KCONTROL_INT(xname, xreg, xshift, xmax, xinvert,\ + xhandler_get, xhandler_put, xmod, xpipe, xinstance, default_val) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = sst_algo_int_ctl_info, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct sst_algo_int_control_v2) \ + {.mc.reg = xreg, .mc.rreg = xreg, .mc.shift = xshift, \ + .mc.rshift = xshift, .mc.max = xmax, .mc.platform_max = xmax, \ + .mc.invert = xinvert, .module_id = xmod, .pipe_id = xpipe, \ + .instance_id = xinstance, .value = default_val } } +/* Thresholds for Low Latency & Deep Buffer*/ +#define DEFAULT_LOW_LATENCY 10 /* In Ms */ +#define DEFAULT_DEEP_BUFFER 96 + +unsigned long ll_threshold = DEFAULT_LOW_LATENCY; +unsigned long db_threshold = DEFAULT_DEEP_BUFFER; + +int sst_algo_int_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct sst_algo_int_control_v2 *amc = (void *)kcontrol->private_value; + struct soc_mixer_control *mc = &amc->mc; + int platform_max; + + if (!mc->platform_max) + mc->platform_max = mc->max; + platform_max = mc->platform_max; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = platform_max; + return 0; +} + +unsigned int sst_soc_read(struct snd_soc_platform *platform, + unsigned int reg) +{ + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + + pr_debug("%s for reg %d val=%d\n", __func__, reg, sst->widget[reg]); + BUG_ON(reg > (SST_NUM_WIDGETS - 1)); + return sst->widget[reg]; +} + +int sst_soc_write(struct snd_soc_platform *platform, + unsigned int reg, unsigned int val) +{ + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + + pr_debug("%s for reg %d val %d\n", __func__, reg, val); + BUG_ON(reg > (SST_NUM_WIDGETS - 1)); + sst->widget[reg] = val; + return 0; +} + +unsigned int sst_reg_read(struct sst_data *sst, unsigned int reg, + unsigned int shift, unsigned int max) +{ + unsigned int mask = (1 << fls(max)) - 1; + + return (sst->widget[reg] >> shift) & mask; +} + +unsigned int sst_reg_write(struct sst_data *sst, unsigned int reg, + unsigned int shift, unsigned int max, unsigned int val) +{ + unsigned int mask = (1 << fls(max)) - 1; + + val &= mask; + val <<= shift; + sst->widget[reg] &= ~(mask << shift); + sst->widget[reg] |= val; + return val; +} + +int sst_mix_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct sst_data *sst = snd_soc_platform_get_drvdata(widget->platform); + unsigned int mask = (1 << fls(mc->max)) - 1; + unsigned int val; + int connect; + struct snd_soc_dapm_update update; + + pr_debug("%s called set %ld for %s\n", __func__, + ucontrol->value.integer.value[0], widget->name); + val = sst_reg_write(sst, mc->reg, mc->shift, mc->max, ucontrol->value.integer.value[0]); + connect = !!val; + + widget->value = val; + update.kcontrol = kcontrol; + update.widget = widget; + update.reg = mc->reg; + update.mask = mask; + update.val = val; + + widget->dapm->update = &update; + snd_soc_dapm_mixer_update_power(widget, kcontrol, connect); + widget->dapm->update = NULL; + return 0; +} + +int sst_mix_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *w = wlist->widgets[0]; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct sst_data *sst = snd_soc_platform_get_drvdata(w->platform); + + pr_debug("%s called for %s\n", __func__, w->name); + ucontrol->value.integer.value[0] = !!sst_reg_read(sst, mc->reg, mc->shift, mc->max); + return 0; +} + +static const struct snd_kcontrol_new sst_mix_modem_controls[] = { + SOC_SINGLE_EXT("Modem", SST_MIX_MODEM, 0, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("BT", SST_MIX_MODEM, 1, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec0", SST_MIX_MODEM, 2, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec1", SST_MIX_MODEM, 3, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sprot_L0", SST_MIX_MODEM, 4, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L1", SST_MIX_MODEM, 5, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L2", SST_MIX_MODEM, 6, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Probe", SST_MIX_MODEM, 7, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sidetone", SST_MIX_MODEM, 8, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Tx", SST_MIX_MODEM, 9, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Rx", SST_MIX_MODEM, 10, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Tone", SST_MIX_MODEM, 11, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Voip", SST_MIX_MODEM, 12, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM0", SST_MIX_MODEM, 13, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM1", SST_MIX_MODEM, 14, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media0", SST_MIX_MODEM, 15, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media1", SST_MIX_MODEM, 16, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media2", SST_MIX_MODEM, 17, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("FM", SST_MIX_MODEM, 18, 1, 0, + sst_mix_get, sst_mix_put), +}; + +static const struct snd_kcontrol_new sst_mix_codec0_controls[] = { + SOC_SINGLE_EXT("Modem", SST_MIX_CODEC0, 0, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("BT", SST_MIX_CODEC0, 1, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec0", SST_MIX_CODEC0, 2, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec1", SST_MIX_CODEC0, 3, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sprot_L0", SST_MIX_CODEC0, 4, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L1", SST_MIX_CODEC0, 5, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L2", SST_MIX_CODEC0, 6, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Probe", SST_MIX_CODEC0, 7, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sidetone", SST_MIX_CODEC0, 8, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Tx", SST_MIX_CODEC0, 9, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Rx", SST_MIX_CODEC0, 10, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Tone", SST_MIX_CODEC0, 11, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Voip", SST_MIX_CODEC0, 12, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM0", SST_MIX_CODEC0, 13, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM1", SST_MIX_CODEC0, 14, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media0", SST_MIX_CODEC0, 15, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media1", SST_MIX_CODEC0, 16, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media2", SST_MIX_CODEC0, 17, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("FM", SST_MIX_CODEC0, 18, 1, 0, + sst_mix_get, sst_mix_put), +}; + +static const struct snd_kcontrol_new sst_mix_codec1_controls[] = { + SOC_SINGLE_EXT("Modem", SST_MIX_CODEC1, 0, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("BT", SST_MIX_CODEC1, 1, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec0", SST_MIX_CODEC1, 2, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec1", SST_MIX_CODEC1, 3, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sprot_L0", SST_MIX_CODEC1, 4, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L1", SST_MIX_CODEC1, 5, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L2", SST_MIX_CODEC1, 6, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Probe", SST_MIX_CODEC1, 7, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sidetone", SST_MIX_CODEC1, 8, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Tx", SST_MIX_CODEC1, 9, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Rx", SST_MIX_CODEC1, 10, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Tone", SST_MIX_CODEC1, 11, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Voip", SST_MIX_CODEC1, 12, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM0", SST_MIX_CODEC1, 13, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM1", SST_MIX_CODEC1, 14, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media0", SST_MIX_CODEC1, 15, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media1", SST_MIX_CODEC1, 16, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media2", SST_MIX_CODEC1, 17, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("FM", SST_MIX_CODEC1, 18, 1, 0, + sst_mix_get, sst_mix_put), +}; + +static const struct snd_kcontrol_new sst_mix_sprot_l0_controls[] = { + SOC_SINGLE_EXT("Modem", SST_MIX_LOOP0, 0, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("BT", SST_MIX_LOOP0, 1, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec0", SST_MIX_LOOP0, 2, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec1", SST_MIX_LOOP0, 3, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sprot_L0", SST_MIX_LOOP0, 4, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L1", SST_MIX_LOOP0, 5, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L2", SST_MIX_LOOP0, 6, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Probe", SST_MIX_LOOP0, 7, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sidetone", SST_MIX_LOOP0, 8, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Tx", SST_MIX_LOOP0, 9, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Rx", SST_MIX_LOOP0, 10, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Tone", SST_MIX_LOOP0, 11, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Voip", SST_MIX_LOOP0, 12, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM0", SST_MIX_LOOP0, 13, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM1", SST_MIX_LOOP0, 14, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media0", SST_MIX_LOOP0, 15, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media1", SST_MIX_LOOP0, 16, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media2", SST_MIX_LOOP0, 17, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("FM", SST_MIX_LOOP0, 18, 1, 0, + sst_mix_get, sst_mix_put), +}; + +static const struct snd_kcontrol_new sst_mix_media_l1_controls[] = { + SOC_SINGLE_EXT("Modem", SST_MIX_LOOP1, 0, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("BT", SST_MIX_LOOP1, 1, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec0", SST_MIX_LOOP1, 2, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec1", SST_MIX_LOOP1, 3, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sprot_L0", SST_MIX_LOOP1, 4, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L1", SST_MIX_LOOP1, 5, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L2", SST_MIX_LOOP1, 6, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Probe", SST_MIX_LOOP1, 7, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sidetone", SST_MIX_LOOP1, 8, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Tx", SST_MIX_LOOP1, 9, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Rx", SST_MIX_LOOP1, 10, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Tone", SST_MIX_LOOP1, 11, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Voip", SST_MIX_LOOP1, 12, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM0", SST_MIX_LOOP1, 13, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM1", SST_MIX_LOOP1, 14, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media0", SST_MIX_LOOP1, 15, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media1", SST_MIX_LOOP1, 16, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media2", SST_MIX_LOOP1, 17, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("FM", SST_MIX_LOOP1, 18, 1, 0, + sst_mix_get, sst_mix_put), +}; + +static const struct snd_kcontrol_new sst_mix_media_l2_controls[] = { + SOC_SINGLE_EXT("Modem", SST_MIX_LOOP2, 0, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("BT", SST_MIX_LOOP2, 1, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec0", SST_MIX_LOOP2, 2, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec1", SST_MIX_LOOP2, 3, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sprot_L0", SST_MIX_LOOP2, 4, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L1", SST_MIX_LOOP2, 5, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L2", SST_MIX_LOOP2, 6, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Probe", SST_MIX_LOOP2, 7, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sidetone", SST_MIX_LOOP2, 8, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Tx", SST_MIX_LOOP2, 9, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Rx", SST_MIX_LOOP2, 10, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Tone", SST_MIX_LOOP2, 11, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Voip", SST_MIX_LOOP2, 12, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM0", SST_MIX_LOOP2, 13, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM1", SST_MIX_LOOP2, 14, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media0", SST_MIX_LOOP2, 15, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media1", SST_MIX_LOOP2, 16, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media2", SST_MIX_LOOP2, 17, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("FM", SST_MIX_LOOP2, 18, 1, 0, + sst_mix_get, sst_mix_put), +}; + +static const struct snd_kcontrol_new sst_mix_speech_tx_controls[] = { + SOC_SINGLE_EXT("Modem", SST_MIX_SPEECH, 0, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("BT", SST_MIX_SPEECH, 1, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec0", SST_MIX_SPEECH, 2, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec1", SST_MIX_SPEECH, 3, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sprot_L0", SST_MIX_SPEECH, 4, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L1", SST_MIX_SPEECH, 5, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L2", SST_MIX_SPEECH, 6, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Probe", SST_MIX_SPEECH, 7, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sidetone", SST_MIX_SPEECH, 8, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Tx", SST_MIX_SPEECH, 9, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Rx", SST_MIX_SPEECH, 10, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Tone", SST_MIX_SPEECH, 11, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Voip", SST_MIX_SPEECH, 12, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM0", SST_MIX_SPEECH, 13, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM1", SST_MIX_SPEECH, 14, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media0", SST_MIX_SPEECH, 15, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media1", SST_MIX_SPEECH, 16, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media2", SST_MIX_SPEECH, 17, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("FM", SST_MIX_SPEECH, 18, 1, 0, + sst_mix_get, sst_mix_put), +}; + +static const struct snd_kcontrol_new sst_mix_speech_rx_controls[] = { + SOC_SINGLE_EXT("Modem", SST_MIX_RXSPEECH, 0, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("BT", SST_MIX_RXSPEECH, 1, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec0", SST_MIX_RXSPEECH, 2, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec1", SST_MIX_RXSPEECH, 3, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sprot_L0", SST_MIX_RXSPEECH, 4, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L1", SST_MIX_RXSPEECH, 5, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L2", SST_MIX_RXSPEECH, 6, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Probe", SST_MIX_RXSPEECH, 7, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sidetone", SST_MIX_RXSPEECH, 8, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Tx", SST_MIX_RXSPEECH, 9, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Rx", SST_MIX_RXSPEECH, 10, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Tone", SST_MIX_RXSPEECH, 11, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Voip", SST_MIX_RXSPEECH, 12, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM0", SST_MIX_RXSPEECH, 13, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM1", SST_MIX_RXSPEECH, 14, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media0", SST_MIX_RXSPEECH, 15, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media1", SST_MIX_RXSPEECH, 16, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media2", SST_MIX_RXSPEECH, 17, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("FM", SST_MIX_RXSPEECH, 18, 1, 0, + sst_mix_get, sst_mix_put), +}; + +static const struct snd_kcontrol_new sst_mix_voip_controls[] = { + SOC_SINGLE_EXT("Modem", SST_MIX_VOIP, 0, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("BT", SST_MIX_VOIP, 1, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec0", SST_MIX_VOIP, 2, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec1", SST_MIX_VOIP, 3, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sprot_L0", SST_MIX_VOIP, 4, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L1", SST_MIX_VOIP, 5, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L2", SST_MIX_VOIP, 6, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Probe", SST_MIX_VOIP, 7, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sidetone", SST_MIX_VOIP, 8, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Tx", SST_MIX_VOIP, 9, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Rx", SST_MIX_VOIP, 10, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Tone", SST_MIX_VOIP, 11, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Voip", SST_MIX_VOIP, 12, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM0", SST_MIX_VOIP, 13, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM1", SST_MIX_VOIP, 14, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media0", SST_MIX_VOIP, 15, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media1", SST_MIX_VOIP, 16, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media2", SST_MIX_VOIP, 17, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("FM", SST_MIX_VOIP, 18, 1, 0, + sst_mix_get, sst_mix_put), +}; + +static const struct snd_kcontrol_new sst_mix_pcm0_controls[] = { + SOC_SINGLE_EXT("Modem", SST_MIX_PCM0, 0, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("BT", SST_MIX_PCM0, 1, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec0", SST_MIX_PCM0, 2, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec1", SST_MIX_PCM0, 3, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sprot_L0", SST_MIX_PCM0, 4, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L1", SST_MIX_PCM0, 5, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L2", SST_MIX_PCM0, 6, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Probe", SST_MIX_PCM0, 7, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sidetone", SST_MIX_PCM0, 8, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Tx", SST_MIX_PCM0, 9, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Rx", SST_MIX_PCM0, 10, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Tone", SST_MIX_PCM0, 11, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Voip", SST_MIX_PCM0, 12, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM0", SST_MIX_PCM0, 13, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM1", SST_MIX_PCM0, 14, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media0", SST_MIX_PCM0, 15, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media1", SST_MIX_PCM0, 16, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media2", SST_MIX_PCM0, 17, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("FM", SST_MIX_PCM0, 18, 1, 0, + sst_mix_get, sst_mix_put), +}; + +static const struct snd_kcontrol_new sst_mix_pcm1_controls[] = { + SOC_SINGLE_EXT("Modem", SST_MIX_PCM1, 0, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("BT", SST_MIX_PCM1, 1, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec0", SST_MIX_PCM1, 2, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec1", SST_MIX_PCM1, 3, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sprot_L0", SST_MIX_PCM1, 4, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L1", SST_MIX_PCM1, 5, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L2", SST_MIX_PCM1, 6, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Probe", SST_MIX_PCM1, 7, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sidetone", SST_MIX_PCM1, 8, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Tx", SST_MIX_PCM1, 9, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Rx", SST_MIX_PCM1, 10, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Tone", SST_MIX_PCM1, 11, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Voip", SST_MIX_PCM1, 12, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM0", SST_MIX_PCM1, 13, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM1", SST_MIX_PCM1, 14, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media0", SST_MIX_PCM1, 15, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media1", SST_MIX_PCM1, 16, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media2", SST_MIX_PCM1, 17, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("FM", SST_MIX_PCM1, 18, 1, 0, + sst_mix_get, sst_mix_put), +}; + +static const struct snd_kcontrol_new sst_mix_pcm2_controls[] = { + SOC_SINGLE_EXT("Modem", SST_MIX_PCM2, 0, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("BT", SST_MIX_PCM2, 1, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec0", SST_MIX_PCM2, 2, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec1", SST_MIX_PCM2, 3, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sprot_L0", SST_MIX_PCM2, 4, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L1", SST_MIX_PCM2, 5, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L2", SST_MIX_PCM2, 6, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Probe", SST_MIX_PCM2, 7, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sidetone", SST_MIX_PCM2, 8, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Tx", SST_MIX_PCM2, 9, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Rx", SST_MIX_PCM2, 10, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Tone", SST_MIX_PCM2, 11, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Voip", SST_MIX_PCM2, 12, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM0", SST_MIX_PCM2, 13, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM1", SST_MIX_PCM2, 14, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media0", SST_MIX_PCM2, 15, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media1", SST_MIX_PCM2, 16, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media2", SST_MIX_PCM2, 17, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("FM", SST_MIX_PCM2, 18, 1, 0, + sst_mix_get, sst_mix_put), +}; + +static const struct snd_kcontrol_new sst_mix_aware_controls[] = { + SOC_SINGLE_EXT("Modem", SST_MIX_AWARE, 0, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("BT", SST_MIX_AWARE, 1, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec0", SST_MIX_AWARE, 2, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec1", SST_MIX_AWARE, 3, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sprot_L0", SST_MIX_AWARE, 4, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L1", SST_MIX_AWARE, 5, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L2", SST_MIX_AWARE, 6, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Probe", SST_MIX_AWARE, 7, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sidetone", SST_MIX_AWARE, 8, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Tx", SST_MIX_AWARE, 9, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Rx", SST_MIX_AWARE, 10, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Tone", SST_MIX_AWARE, 11, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Voip", SST_MIX_AWARE, 12, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM0", SST_MIX_AWARE, 13, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM1", SST_MIX_AWARE, 14, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media0", SST_MIX_AWARE, 15, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media1", SST_MIX_AWARE, 16, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media2", SST_MIX_AWARE, 17, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("FM", SST_MIX_AWARE, 18, 1, 0, + sst_mix_get, sst_mix_put), +}; + +static const struct snd_kcontrol_new sst_mix_vad_controls[] = { + SOC_SINGLE_EXT("Modem", SST_MIX_VAD, 0, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("BT", SST_MIX_VAD, 1, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec0", SST_MIX_VAD, 2, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec1", SST_MIX_VAD, 3, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sprot_L0", SST_MIX_VAD, 4, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L1", SST_MIX_VAD, 5, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L2", SST_MIX_VAD, 6, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Probe", SST_MIX_VAD, 7, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sidetone", SST_MIX_VAD, 8, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Tx", SST_MIX_VAD, 9, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Rx", SST_MIX_VAD, 10, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Tone", SST_MIX_VAD, 11, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Voip", SST_MIX_VAD, 12, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM0", SST_MIX_VAD, 13, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM1", SST_MIX_VAD, 14, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media0", SST_MIX_VAD, 15, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media1", SST_MIX_VAD, 16, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media2", SST_MIX_VAD, 17, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("FM", SST_MIX_VAD, 18, 1, 0, + sst_mix_get, sst_mix_put), +}; + +static const struct snd_kcontrol_new sst_mix_media0_controls[] = { + SOC_SINGLE_EXT("Modem", SST_MIX_MEDIA0, 0, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("BT", SST_MIX_MEDIA0, 1, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec0", SST_MIX_MEDIA0, 2, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec1", SST_MIX_MEDIA0, 3, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sprot_L0", SST_MIX_MEDIA0, 4, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L1", SST_MIX_MEDIA0, 5, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L2", SST_MIX_MEDIA0, 6, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Probe", SST_MIX_MEDIA0, 7, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sidetone", SST_MIX_MEDIA0, 8, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Tx", SST_MIX_MEDIA0, 9, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Rx", SST_MIX_MEDIA0, 10, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Tone", SST_MIX_MEDIA0, 11, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Voip", SST_MIX_MEDIA0, 12, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM0", SST_MIX_MEDIA0, 13, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM1", SST_MIX_MEDIA0, 14, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media0", SST_MIX_MEDIA0, 15, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media1", SST_MIX_MEDIA0, 16, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media2", SST_MIX_MEDIA0, 17, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("FM", SST_MIX_MEDIA0, 18, 1, 0, + sst_mix_get, sst_mix_put), +}; + +static const struct snd_kcontrol_new sst_mix_media1_controls[] = { + SOC_SINGLE_EXT("Modem", SST_MIX_MEDIA1, 0, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("BT", SST_MIX_MEDIA1, 1, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec0", SST_MIX_MEDIA1, 2, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec1", SST_MIX_MEDIA1, 3, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sprot_L0", SST_MIX_MEDIA1, 4, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L1", SST_MIX_MEDIA1, 5, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L2", SST_MIX_MEDIA1, 6, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Probe", SST_MIX_MEDIA1, 7, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sidetone", SST_MIX_MEDIA1, 8, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Tx", SST_MIX_MEDIA1, 9, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Rx", SST_MIX_MEDIA1, 10, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Tone", SST_MIX_MEDIA1, 11, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Voip", SST_MIX_MEDIA1, 12, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM0", SST_MIX_MEDIA1, 13, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM1", SST_MIX_MEDIA1, 14, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media0", SST_MIX_MEDIA1, 15, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media1", SST_MIX_MEDIA1, 16, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media2", SST_MIX_MEDIA1, 17, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("FM", SST_MIX_MEDIA1, 18, 1, 0, + sst_mix_get, sst_mix_put), +}; + +static const struct snd_kcontrol_new sst_mix_fm_controls[] = { + SOC_SINGLE_EXT("Modem", SST_MIX_FM, 0, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("BT", SST_MIX_FM, 1, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec0", SST_MIX_FM, 2, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Codec1", SST_MIX_FM, 3, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sprot_L0", SST_MIX_FM, 4, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L1", SST_MIX_FM, 5, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media_L2", SST_MIX_FM, 6, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Probe", SST_MIX_FM, 7, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Sidetone", SST_MIX_FM, 8, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Tx", SST_MIX_FM, 9, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Speech_Rx", SST_MIX_FM, 10, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Tone", SST_MIX_FM, 11, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Voip", SST_MIX_FM, 12, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM0", SST_MIX_FM, 13, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("PCM1", SST_MIX_FM, 14, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media0", SST_MIX_FM, 15, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media1", SST_MIX_FM, 16, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("Media2", SST_MIX_FM, 17, 1, 0, + sst_mix_get, sst_mix_put), + SOC_SINGLE_EXT("FM", SST_MIX_FM, 18, 1, 0, + sst_mix_get, sst_mix_put), +}; + +static const struct snd_kcontrol_new sst_mix_sw_modem = + SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 0, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_mix_sw_codec0 = + SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 1, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_mix_sw_codec1 = + SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 2, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_mix_sw_sprot_l0 = + SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 3, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_mix_sw_media_l1 = + SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 4, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_mix_sw_media_l2 = + SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 5, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_mix_sw_speech_tx = + SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 6, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_mix_sw_speech_rx = + SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 7, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_mix_sw_voip = + SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 8, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_mix_sw_pcm0 = + SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 9, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_mix_sw_pcm1 = + SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 10, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_mix_sw_pcm2 = + SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 11, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_mix_sw_aware = + SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 12, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_mix_sw_vad = + SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 13, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_mix_sw_media0 = + SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 14, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_mix_sw_media1 = + SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 15, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_mix_sw_fm = + SOC_SINGLE_EXT("Switch", SST_MIX_SWITCH, 16, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_out_sw_modem = + SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 0, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_out_sw_codec0 = + SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 1, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_out_sw_codec1 = + SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 2, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_out_sw_speech_tx = + SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 6, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_out_sw_speech_rx = + SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 7, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_out_sw_voip = + SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 8, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_out_sw_pcm0 = + SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 9, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_out_sw_pcm1 = + SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 10, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_out_sw_pcm2 = + SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 11, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_out_sw_aware = + SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 12, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_out_sw_vad = + SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 13, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_out_sw_media0 = + SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 14, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_out_sw_media1 = + SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 15, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_out_sw_fm = + SOC_SINGLE_EXT("Switch", SST_OUT_SWITCH, 16, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_in_sw_modem = + SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 0, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_in_sw_codec0 = + SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 1, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_in_sw_codec1 = + SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 2, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_in_sw_sidetone = + SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 3, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_in_sw_speech_tx = + SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 4, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_in_sw_speech_rx = + SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 5, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_in_sw_tone = + SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 6, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_in_sw_voip = + SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 7, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_in_sw_pcm0 = + SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 8, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_in_sw_pcm1 = + SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 9, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_in_sw_media0 = + SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 10, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_in_sw_media1 = + SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 11, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_in_sw_media2 = + SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 12, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_kcontrol_new sst_in_sw_fm = + SOC_SINGLE_EXT("Switch", SST_IN_SWITCH, 13, 1, 0, + sst_mix_get, sst_mix_put); + +static const struct snd_soc_dapm_widget sst_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("Modem IN"), + SND_SOC_DAPM_INPUT("Codec IN0"), + SND_SOC_DAPM_INPUT("Codec IN1"), + SND_SOC_DAPM_INPUT("Tone IN"), + SND_SOC_DAPM_INPUT("FM IN"), + SND_SOC_DAPM_OUTPUT("Modem OUT"), + SND_SOC_DAPM_OUTPUT("Codec OUT0"), + SND_SOC_DAPM_OUTPUT("Codec OUT1"), + SND_SOC_DAPM_OUTPUT("FM OUT"), + SND_SOC_DAPM_AIF_IN("Voip IN", "VoIP", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Media IN0", "Compress", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("Media IN1", "PCM", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("Voip OUT", "VoIP", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("PCM1 OUT", "Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("Aware OUT", "Aware", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("VAD OUT", "VAD", 0, SND_SOC_NOPM, 0, 0), + + /* output mixers */ + SND_SOC_DAPM_MIXER("MIX Modem", SND_SOC_NOPM, 0, 0, + sst_mix_modem_controls, ARRAY_SIZE(sst_mix_modem_controls)), + SND_SOC_DAPM_MIXER("MIX Codec0", SND_SOC_NOPM, 0, 0, + sst_mix_codec0_controls , ARRAY_SIZE(sst_mix_codec0_controls)), + SND_SOC_DAPM_MIXER("MIX Codec1", SND_SOC_NOPM, 0, 0, + sst_mix_codec1_controls, ARRAY_SIZE(sst_mix_codec1_controls)), + SND_SOC_DAPM_MIXER("MIX Sprot L0", SND_SOC_NOPM, 0, 0, + sst_mix_sprot_l0_controls, ARRAY_SIZE(sst_mix_sprot_l0_controls)), + SND_SOC_DAPM_MIXER("MIX Media L1", SND_SOC_NOPM, 0, 0, + sst_mix_media_l1_controls, ARRAY_SIZE(sst_mix_media_l1_controls)), + SND_SOC_DAPM_MIXER("MIX Media L2", SND_SOC_NOPM, 0, 0, + sst_mix_media_l2_controls, ARRAY_SIZE(sst_mix_media_l2_controls)), + SND_SOC_DAPM_MIXER("MIX Speech Tx", SND_SOC_NOPM, 0, 0, + sst_mix_speech_tx_controls, ARRAY_SIZE(sst_mix_speech_tx_controls)), + SND_SOC_DAPM_MIXER("MIX Speech Rx", SND_SOC_NOPM, 0, 0, + sst_mix_speech_rx_controls, ARRAY_SIZE(sst_mix_speech_rx_controls)), + SND_SOC_DAPM_MIXER("MIX Voip", SND_SOC_NOPM, 0, 0, + sst_mix_voip_controls, ARRAY_SIZE(sst_mix_voip_controls)), + SND_SOC_DAPM_MIXER("MIX PCM0", SND_SOC_NOPM, 0, 0, + sst_mix_pcm0_controls, ARRAY_SIZE(sst_mix_pcm0_controls)), + SND_SOC_DAPM_MIXER("MIX PCM1", SND_SOC_NOPM, 0, 0, + sst_mix_pcm1_controls, ARRAY_SIZE(sst_mix_pcm1_controls)), + SND_SOC_DAPM_MIXER("MIX PCM2", SND_SOC_NOPM, 0, 0, + sst_mix_pcm2_controls, ARRAY_SIZE(sst_mix_pcm2_controls)), + SND_SOC_DAPM_MIXER("MIX Aware", SND_SOC_NOPM, 0, 0, + sst_mix_aware_controls, ARRAY_SIZE(sst_mix_aware_controls)), + SND_SOC_DAPM_MIXER("MIX VAD", SND_SOC_NOPM, 0, 0, + sst_mix_vad_controls, ARRAY_SIZE(sst_mix_vad_controls)), + SND_SOC_DAPM_MIXER("MIX Media0", SND_SOC_NOPM, 0, 0, + sst_mix_media0_controls, ARRAY_SIZE(sst_mix_media0_controls)), + SND_SOC_DAPM_MIXER("MIX Media1", SND_SOC_NOPM, 0, 0, + sst_mix_media1_controls, ARRAY_SIZE(sst_mix_media1_controls)), + SND_SOC_DAPM_MIXER("MIX FM", SND_SOC_NOPM, 0, 0, + sst_mix_fm_controls, ARRAY_SIZE(sst_mix_fm_controls)), + + /* switches for mixer outputs */ + SND_SOC_DAPM_SWITCH("Mix Modem Switch", SND_SOC_NOPM, 0, 0, + &sst_mix_sw_modem), + SND_SOC_DAPM_SWITCH("Mix Codec0 Switch", SND_SOC_NOPM, 0, 0, + &sst_mix_sw_codec0), + SND_SOC_DAPM_SWITCH("Mix Codec1 Switch", SND_SOC_NOPM, 0, 0, + &sst_mix_sw_codec1), + SND_SOC_DAPM_SWITCH("Mix Sprot L0 Switch", SND_SOC_NOPM, 0, 0, + &sst_mix_sw_sprot_l0), + SND_SOC_DAPM_SWITCH("Mix Media L1 Switch", SND_SOC_NOPM, 0, 0, + &sst_mix_sw_media_l1), + SND_SOC_DAPM_SWITCH("Mix Media L2 Switch", SND_SOC_NOPM, 0, 0, + &sst_mix_sw_media_l2), + SND_SOC_DAPM_SWITCH("Mix Speech Tx Switch", SND_SOC_NOPM, 0, 0, + &sst_mix_sw_speech_tx), + SND_SOC_DAPM_SWITCH("Mix Speech Rx Switch", SND_SOC_NOPM, 0, 0, + &sst_mix_sw_speech_rx), + SND_SOC_DAPM_SWITCH("Mix Voip Switch", SND_SOC_NOPM, 0, 0, + &sst_mix_sw_voip), + SND_SOC_DAPM_SWITCH("Mix PCM0 Switch", SND_SOC_NOPM, 0, 0, + &sst_mix_sw_pcm0), + SND_SOC_DAPM_SWITCH("Mix PCM1 Switch", SND_SOC_NOPM, 0, 0, + &sst_mix_sw_pcm1), + SND_SOC_DAPM_SWITCH("Mix PCM2 Switch", SND_SOC_NOPM, 0, 0, + &sst_mix_sw_pcm2), + SND_SOC_DAPM_SWITCH("Mix Aware Switch", SND_SOC_NOPM, 0, 0, + &sst_mix_sw_aware), + SND_SOC_DAPM_SWITCH("Mix VAD Switch", SND_SOC_NOPM, 0, 0, + &sst_mix_sw_vad), + SND_SOC_DAPM_SWITCH("Mix Media0 Switch", SND_SOC_NOPM, 0, 0, + &sst_mix_sw_media0), + SND_SOC_DAPM_SWITCH("Mix Media1 Switch", SND_SOC_NOPM, 0, 0, + &sst_mix_sw_media1), + SND_SOC_DAPM_SWITCH("Mix FM Switch", SND_SOC_NOPM, 0, 0, + &sst_mix_sw_fm), + + /* output pipeline switches */ + SND_SOC_DAPM_SWITCH("Out Modem Switch", SND_SOC_NOPM, 0, 0, + &sst_out_sw_modem), + SND_SOC_DAPM_SWITCH("Out Codec0 Switch", SND_SOC_NOPM, 0, 0, + &sst_out_sw_codec0), + SND_SOC_DAPM_SWITCH("Out Codec1 Switch", SND_SOC_NOPM, 0, 0, + &sst_out_sw_codec1), + SND_SOC_DAPM_SWITCH("Out Speech Tx Switch", SND_SOC_NOPM, 0, 0, + &sst_out_sw_speech_tx), + SND_SOC_DAPM_SWITCH("Out Speech Rx Switch", SND_SOC_NOPM, 0, 0, + &sst_out_sw_speech_rx), + SND_SOC_DAPM_SWITCH("Out Voip Switch", SND_SOC_NOPM, 0, 0, + &sst_out_sw_voip), + SND_SOC_DAPM_SWITCH("Out PCM0 Switch", SND_SOC_NOPM, 0, 0, + &sst_out_sw_pcm0), + SND_SOC_DAPM_SWITCH("Out PCM1 Switch", SND_SOC_NOPM, 0, 0, + &sst_out_sw_pcm1), + SND_SOC_DAPM_SWITCH("Out PCM2 Switch", SND_SOC_NOPM, 0, 0, + &sst_out_sw_pcm2), + SND_SOC_DAPM_SWITCH("Out Aware Switch", SND_SOC_NOPM, 0, 0, + &sst_out_sw_aware), + SND_SOC_DAPM_SWITCH("Out VAD Switch", SND_SOC_NOPM, 0, 0, + &sst_out_sw_vad), + SND_SOC_DAPM_SWITCH("Out Media0 Switch", SND_SOC_NOPM, 0, 0, + &sst_out_sw_media0), + SND_SOC_DAPM_SWITCH("Out Media1 Switch", SND_SOC_NOPM, 0, 0, + &sst_out_sw_media1), + SND_SOC_DAPM_SWITCH("Out FM Switch", SND_SOC_NOPM, 0, 0, + &sst_out_sw_fm), + + /* Input pipeline switches */ + SND_SOC_DAPM_SWITCH("In Modem Switch", SND_SOC_NOPM, 0, 0, + &sst_in_sw_modem), + SND_SOC_DAPM_SWITCH("In Codec0 Switch", SND_SOC_NOPM, 0, 0, + &sst_in_sw_codec0), + SND_SOC_DAPM_SWITCH("In Codec1 Switch", SND_SOC_NOPM, 0, 0, + &sst_in_sw_codec1), + SND_SOC_DAPM_SWITCH("In Speech Tx Switch", SND_SOC_NOPM, 0, 0, + &sst_in_sw_speech_tx), + SND_SOC_DAPM_SWITCH("In Speech Rx Switch", SND_SOC_NOPM, 0, 0, + &sst_in_sw_speech_rx), + SND_SOC_DAPM_SWITCH("In Tone Switch", SND_SOC_NOPM, 0, 0, + &sst_in_sw_tone), + SND_SOC_DAPM_SWITCH("In Voip Switch", SND_SOC_NOPM, 0, 0, + &sst_in_sw_voip), + SND_SOC_DAPM_SWITCH("In PCM0 Switch", SND_SOC_NOPM, 0, 0, + &sst_in_sw_pcm0), + SND_SOC_DAPM_SWITCH("In PCM1 Switch", SND_SOC_NOPM, 0, 0, + &sst_in_sw_pcm1), + SND_SOC_DAPM_SWITCH("In Media0 Switch", SND_SOC_NOPM, 0, 0, + &sst_in_sw_media0), + SND_SOC_DAPM_SWITCH("In Media1 Switch", SND_SOC_NOPM, 0, 0, + &sst_in_sw_media1), + SND_SOC_DAPM_SWITCH("In Media2 Switch", SND_SOC_NOPM, 0, 0, + &sst_in_sw_media2), + SND_SOC_DAPM_SWITCH("In FM Switch", SND_SOC_NOPM, 0, 0, + &sst_in_sw_fm), +}; + +static const struct snd_soc_dapm_route intercon[] = { + /* media mixer settings */ + { "In Media0 Switch", "Switch", "Media IN0"}, + { "In Media1 Switch", "Switch", "Media IN1"}, + { "MIX Media0", "Media0", "In Media0 Switch"}, + { "MIX Media0", "Media1", "In Media1 Switch"}, + { "MIX Media0", "Media2", "In Media2 Switch"}, + { "MIX Media1", "Media0", "In Media0 Switch"}, + { "MIX Media1", "Media1", "In Media1 Switch"}, + { "MIX Media1", "Media2", "In Media2 Switch"}, + + /* media to main mixer intercon */ + /* two media paths from media to main */ + { "Mix Media0 Switch", "Switch", "MIX Media0"}, + { "Out Media0 Switch", "Switch", "Mix Media0 Switch"}, + { "In PCM0 Switch", "Switch", "Out Media0 Switch"}, + { "Mix Media1 Switch", "Switch", "MIX Media1"}, + { "Out Media1 Switch", "Switch", "Mix Media1 Switch"}, + { "In PCM1 Switch", "Switch", "Out Media1 Switch"}, + /* one back from main to media */ + { "Mix PCM0 Switch", "Switch", "MIX PCM0"}, + { "Out PCM0 Switch", "Switch", "Mix PCM0 Switch"}, + { "In Media2 Switch", "Switch", "Out PCM0 Switch"}, + + /* main mixer inputs - all inputs connect to mixer */ + { "MIX Modem", "Modem", "In Modem Switch"}, + { "MIX Modem", "Codec0", "In Codec0 Switch"}, + { "MIX Modem", "Codec1", "In Codec1 Switch"}, + { "MIX Modem", "Speech_Tx", "In Speech Tx Switch"}, + { "MIX Modem", "Speech_Rx", "In Speech Rx Switch"}, + { "MIX Modem", "Tone", "In Tone Switch"}, + { "MIX Modem", "Voip", "In Voip Switch"}, + { "MIX Modem", "PCM0", "In PCM0 Switch"}, + { "MIX Modem", "PCM1", "In PCM1 Switch"}, + { "MIX Modem", "FM", "In FM Switch"}, + /* loops have output switches coming back to mixers */ + { "MIX Modem", "Sprot_L0", "Mix Sprot L0 Switch"}, + { "MIX Modem", "Media_L1", "Mix Media L1 Switch"}, + { "MIX Modem", "Media_L2", "Mix Media L2 Switch"}, + /* sidetone comes from speech out */ + { "MIX Modem", "Sidetone", "Mix Speech Tx Switch"}, + + { "MIX Codec0", "Modem", "In Modem Switch"}, + { "MIX Codec0", "Codec0", "In Codec0 Switch"}, + { "MIX Codec0", "Codec1", "In Codec1 Switch"}, + { "MIX Codec0", "Speech_Tx", "In Speech Tx Switch"}, + { "MIX Codec0", "Speech_Rx", "In Speech Rx Switch"}, + { "MIX Codec0", "Tone", "In Tone Switch"}, + { "MIX Codec0", "Voip", "In Voip Switch"}, + { "MIX Codec0", "PCM0", "In PCM0 Switch"}, + { "MIX Codec0", "PCM1", "In PCM1 Switch"}, + { "MIX Codec0", "FM", "In FM Switch"}, + /* loops have output switches coming back to mixers */ + { "MIX Codec0", "Sprot_L0", "Mix Sprot L0 Switch"}, + { "MIX Codec0", "Media_L1", "Mix Media L1 Switch"}, + { "MIX Codec0", "Media_L2", "Mix Media L2 Switch"}, + /* sidetone comes from speech out */ + { "MIX Codec0", "Sidetone", "Mix Speech Tx Switch"}, + + { "MIX Codec1", "Modem", "In Modem Switch"}, + { "MIX Codec1", "Codec0", "In Codec0 Switch"}, + { "MIX Codec1", "Codec1", "In Codec1 Switch"}, + { "MIX Codec1", "Speech_Tx", "In Speech Tx Switch"}, + { "MIX Codec1", "Speech_Rx", "In Speech Rx Switch"}, + { "MIX Codec1", "Tone", "In Tone Switch"}, + { "MIX Codec1", "Voip", "In Voip Switch"}, + { "MIX Codec1", "PCM0", "In PCM0 Switch"}, + { "MIX Codec1", "PCM1", "In PCM1 Switch"}, + { "MIX Codec1", "FM", "In FM Switch"}, + /* loops have output switches coming back to mixers */ + { "MIX Codec1", "Sprot_L0", "Mix Sprot L0 Switch"}, + { "MIX Codec1", "Media_L1", "Mix Media L1 Switch"}, + { "MIX Codec1", "Media_L2", "Mix Media L2 Switch"}, + /* sidetone comes from speech out */ + { "MIX Codec1", "Sidetone", "Mix Speech Tx Switch"}, + + { "MIX Sprot L0", "Modem", "In Modem Switch"}, + { "MIX Sprot L0", "Codec0", "In Codec0 Switch"}, + { "MIX Sprot L0", "Codec1", "In Codec1 Switch"}, + { "MIX Sprot L0", "Speech_Tx", "In Speech Tx Switch"}, + { "MIX Sprot L0", "Speech_Rx", "In Speech Rx Switch"}, + { "MIX Sprot L0", "Tone", "In Tone Switch"}, + { "MIX Sprot L0", "Voip", "In Voip Switch"}, + { "MIX Sprot L0", "PCM0", "In PCM0 Switch"}, + { "MIX Sprot L0", "PCM1", "In PCM1 Switch"}, + { "MIX Sprot L0", "FM", "In FM Switch"}, + /* loops have output switches coming back to mixers */ + { "MIX Sprot L0", "Sprot_L0", "Mix Sprot L0 Switch"}, + { "MIX Sprot L0", "Media_L1", "Mix Media L1 Switch"}, + { "MIX Sprot L0", "Media_L2", "Mix Media L2 Switch"}, + /* sidetone comes from speech out */ + { "MIX Sprot L0", "Sidetone", "Mix Speech Tx Switch"}, + + { "MIX Media L1", "Modem", "In Modem Switch"}, + { "MIX Media L1", "Codec0", "In Codec0 Switch"}, + { "MIX Media L1", "Codec1", "In Codec1 Switch"}, + { "MIX Media L1", "Speech_Tx", "In Speech Tx Switch"}, + { "MIX Media L1", "Speech_Rx", "In Speech Rx Switch"}, + { "MIX Media L1", "Tone", "In Tone Switch"}, + { "MIX Media L1", "Voip", "In Voip Switch"}, + { "MIX Media L1", "PCM0", "In PCM0 Switch"}, + { "MIX Media L1", "PCM1", "In PCM1 Switch"}, + { "MIX Media L1", "FM", "In FM Switch"}, + /* loops have output switches coming back to mixers */ + { "MIX Media L1", "Sprot_L0", "Mix Sprot L0 Switch"}, + { "MIX Media L1", "Media_L1", "Mix Media L1 Switch"}, + { "MIX Media L1", "Media_L2", "Mix Media L2 Switch"}, + /* sidetone comes from speech out */ + { "MIX Media L1", "Sidetone", "Mix Speech Tx Switch"}, + + { "MIX Media L2", "Modem", "In Modem Switch"}, + { "MIX Media L2", "Codec0", "In Codec0 Switch"}, + { "MIX Media L2", "Codec1", "In Codec1 Switch"}, + { "MIX Media L2", "Speech_Tx", "In Speech Tx Switch"}, + { "MIX Media L2", "Speech_Rx", "In Speech Rx Switch"}, + { "MIX Media L2", "Tone", "In Tone Switch"}, + { "MIX Media L2", "Voip", "In Voip Switch"}, + { "MIX Media L2", "PCM0", "In PCM0 Switch"}, + { "MIX Media L2", "PCM1", "In PCM1 Switch"}, + { "MIX Media L2", "FM", "In FM Switch"}, + /* loops have output switches coming back to mixers */ + { "MIX Media L2", "Sprot_L0", "Mix Sprot L0 Switch"}, + { "MIX Media L2", "Media_L1", "Mix Media L1 Switch"}, + { "MIX Media L2", "Media_L2", "Mix Media L2 Switch"}, + /* sidetone comes from speech out */ + { "MIX Media L2", "Sidetone", "Mix Speech Tx Switch"}, + + { "MIX Speech Rx", "Modem", "In Modem Switch"}, + { "MIX Speech Rx", "Codec0", "In Codec0 Switch"}, + { "MIX Speech Rx", "Codec1", "In Codec1 Switch"}, + { "MIX Speech Rx", "Speech_Tx", "In Speech Tx Switch"}, + { "MIX Speech Rx", "Speech_Rx", "In Speech Rx Switch"}, + { "MIX Speech Rx", "Tone", "In Tone Switch"}, + { "MIX Speech Rx", "Voip", "In Voip Switch"}, + { "MIX Speech Rx", "PCM0", "In PCM0 Switch"}, + { "MIX Speech Rx", "PCM1", "In PCM1 Switch"}, + { "MIX Speech Rx", "FM", "In FM Switch"}, + /* loops have output switches coming back to mixers */ + { "MIX Speech Rx", "Sprot_L0", "Mix Sprot L0 Switch"}, + { "MIX Speech Rx", "Media_L1", "Mix Media L1 Switch"}, + { "MIX Speech Rx", "Media_L2", "Mix Media L2 Switch"}, + /* sidetone comes from speech out */ + { "MIX Speech Rx", "Sidetone", "Mix Speech Tx Switch"}, + + { "MIX Speech Tx", "Modem", "In Modem Switch"}, + { "MIX Speech Tx", "Codec0", "In Codec0 Switch"}, + { "MIX Speech Tx", "Codec1", "In Codec1 Switch"}, + { "MIX Speech Tx", "Speech_Tx", "In Speech Tx Switch"}, + { "MIX Speech Tx", "Speech_Rx", "In Speech Rx Switch"}, + { "MIX Speech Tx", "Tone", "In Tone Switch"}, + { "MIX Speech Tx", "Voip", "In Voip Switch"}, + { "MIX Speech Tx", "PCM0", "In PCM0 Switch"}, + { "MIX Speech Tx", "PCM1", "In PCM1 Switch"}, + { "MIX Speech Tx", "FM", "In FM Switch"}, + /* loops have output switches coming back to mixers */ + { "MIX Speech Tx", "Sprot_L0", "Mix Sprot L0 Switch"}, + { "MIX Speech Tx", "Media_L1", "Mix Media L1 Switch"}, + { "MIX Speech Tx", "Media_L2", "Mix Media L2 Switch"}, + /* sidetone comes from speech out */ + { "MIX Speech Tx", "Sidetone", "Mix Speech Tx Switch"}, + + { "MIX Voip", "Modem", "In Modem Switch"}, + { "MIX Voip", "Codec0", "In Codec0 Switch"}, + { "MIX Voip", "Codec1", "In Codec1 Switch"}, + { "MIX Voip", "Speech_Tx", "In Speech Tx Switch"}, + { "MIX Voip", "Speech_Rx", "In Speech Rx Switch"}, + { "MIX Voip", "Tone", "In Tone Switch"}, + { "MIX Voip", "Voip", "In Voip Switch"}, + { "MIX Voip", "PCM0", "In PCM0 Switch"}, + { "MIX Voip", "PCM1", "In PCM1 Switch"}, + { "MIX Voip", "FM", "In FM Switch"}, + /* loops have output switches coming back to mixers */ + { "MIX Voip", "Sprot_L0", "Mix Sprot L0 Switch"}, + { "MIX Voip", "Media_L1", "Mix Media L1 Switch"}, + { "MIX Voip", "Media_L2", "Mix Media L2 Switch"}, + /* sidetone comes from speech out */ + { "MIX Voip", "Sidetone", "Mix Speech Tx Switch"}, + + { "MIX PCM0", "Modem", "In Modem Switch"}, + { "MIX PCM0", "Codec0", "In Codec0 Switch"}, + { "MIX PCM0", "Codec1", "In Codec1 Switch"}, + { "MIX PCM0", "Speech_Tx", "In Speech Tx Switch"}, + { "MIX PCM0", "Speech_Rx", "In Speech Rx Switch"}, + { "MIX PCM0", "Tone", "In Tone Switch"}, + { "MIX PCM0", "Voip", "In Voip Switch"}, + { "MIX PCM0", "PCM0", "In PCM0 Switch"}, + { "MIX PCM0", "PCM1", "In PCM1 Switch"}, + { "MIX PCM0", "FM", "In FM Switch"}, + /* loops have output switches coming back to mixers */ + { "MIX PCM0", "Sprot_L0", "Mix Sprot L0 Switch"}, + { "MIX PCM0", "Media_L1", "Mix Media L1 Switch"}, + { "MIX PCM0", "Media_L2", "Mix Media L2 Switch"}, + /* sidetone comes from speech out */ + { "MIX PCM0", "Sidetone", "Mix Speech Tx Switch"}, + + { "MIX PCM1", "Modem", "In Modem Switch"}, + { "MIX PCM1", "Codec0", "In Codec0 Switch"}, + { "MIX PCM1", "Codec1", "In Codec1 Switch"}, + { "MIX PCM1", "Speech_Tx", "In Speech Tx Switch"}, + { "MIX PCM1", "Speech_Rx", "In Speech Rx Switch"}, + { "MIX PCM1", "Tone", "In Tone Switch"}, + { "MIX PCM1", "Voip", "In Voip Switch"}, + { "MIX PCM1", "PCM0", "In PCM0 Switch"}, + { "MIX PCM1", "PCM1", "In PCM1 Switch"}, + { "MIX PCM1", "FM", "In FM Switch"}, + /* loops have output switches coming back to mixers */ + { "MIX PCM1", "Sprot_L0", "Mix Sprot L0 Switch"}, + { "MIX PCM1", "Media_L1", "Mix Media L1 Switch"}, + { "MIX PCM1", "Media_L2", "Mix Media L2 Switch"}, + /* sidetone comes from speech out */ + { "MIX PCM1", "Sidetone", "Mix Speech Tx Switch"}, + + { "MIX PCM2", "Modem", "In Modem Switch"}, + { "MIX PCM2", "Codec0", "In Codec0 Switch"}, + { "MIX PCM2", "Codec1", "In Codec1 Switch"}, + { "MIX PCM2", "Speech_Tx", "In Speech Tx Switch"}, + { "MIX PCM2", "Speech_Rx", "In Speech Rx Switch"}, + { "MIX PCM2", "Tone", "In Tone Switch"}, + { "MIX PCM2", "Voip", "In Voip Switch"}, + { "MIX PCM2", "PCM0", "In PCM0 Switch"}, + { "MIX PCM2", "PCM1", "In PCM1 Switch"}, + { "MIX PCM2", "FM", "In FM Switch"}, + /* loops have output switches coming back to mixers */ + { "MIX PCM2", "Sprot_L0", "Mix Sprot L0 Switch"}, + { "MIX PCM2", "Media_L1", "Mix Media L1 Switch"}, + { "MIX PCM2", "Media_L2", "Mix Media L2 Switch"}, + /* sidetone comes from speech out */ + { "MIX PCM2", "Sidetone", "Mix Speech Tx Switch"}, + + { "MIX Aware", "Modem", "In Modem Switch"}, + { "MIX Aware", "Codec0", "In Codec0 Switch"}, + { "MIX Aware", "Codec1", "In Codec1 Switch"}, + { "MIX Aware", "Speech_Tx", "In Speech Tx Switch"}, + { "MIX Aware", "Speech_Rx", "In Speech Rx Switch"}, + { "MIX Aware", "Tone", "In Tone Switch"}, + { "MIX Aware", "Voip", "In Voip Switch"}, + { "MIX Aware", "PCM0", "In PCM0 Switch"}, + { "MIX Aware", "PCM1", "In PCM1 Switch"}, + { "MIX Aware", "FM", "In FM Switch"}, + /* loops have output switches coming back to mixers */ + { "MIX Aware", "Sprot_L0", "Mix Sprot L0 Switch"}, + { "MIX Aware", "Media_L1", "Mix Media L1 Switch"}, + { "MIX Aware", "Media_L2", "Mix Media L2 Switch"}, + /* sidetone comes from speech out */ + { "MIX Aware", "Sidetone", "Mix Speech Tx Switch"}, + + { "MIX VAD", "Modem", "In Modem Switch"}, + { "MIX VAD", "Codec0", "In Codec0 Switch"}, + { "MIX VAD", "Codec1", "In Codec1 Switch"}, + { "MIX VAD", "Speech_Tx", "In Speech Tx Switch"}, + { "MIX VAD", "Speech_Rx", "In Speech Rx Switch"}, + { "MIX VAD", "Tone", "In Tone Switch"}, + { "MIX VAD", "Voip", "In Voip Switch"}, + { "MIX VAD", "PCM0", "In PCM0 Switch"}, + { "MIX VAD", "PCM1", "In PCM1 Switch"}, + { "MIX VAD", "FM", "In FM Switch"}, + /* loops have output switches coming back to mixers */ + { "MIX VAD", "Sprot_L0", "Mix Sprot L0 Switch"}, + { "MIX VAD", "Media_L1", "Mix Media L1 Switch"}, + { "MIX VAD", "Media_L2", "Mix Media L2 Switch"}, + /* sidetone comes from speech out */ + { "MIX VAD", "Sidetone", "Mix Speech Tx Switch"}, + + { "MIX FM", "Modem", "In Modem Switch"}, + { "MIX FM", "Codec0", "In Codec0 Switch"}, + { "MIX FM", "Codec1", "In Codec1 Switch"}, + { "MIX FM", "Speech_Tx", "In Speech Tx Switch"}, + { "MIX FM", "Speech_Rx", "In Speech Rx Switch"}, + { "MIX FM", "Tone", "In Tone Switch"}, + { "MIX FM", "Voip", "In Voip Switch"}, + { "MIX FM", "PCM0", "In PCM0 Switch"}, + { "MIX FM", "PCM1", "In PCM1 Switch"}, + { "MIX FM", "FM", "In FM Switch"}, + /* loops have output switches coming back to mixers */ + { "MIX FM", "Sprot_L0", "Mix Sprot L0 Switch"}, + { "MIX FM", "Media_L1", "Mix Media L1 Switch"}, + { "MIX FM", "Media_L2", "Mix Media L2 Switch"}, + /* sidetone comes from speech out */ + { "MIX FM", "Sidetone", "Mix Speech Tx Switch"}, + + /* now connect the mixers to output switches */ + { "Mix Modem Switch", "Switch", "MIX Modem"}, + { "Out Modem Switch", "Switch", "Mix Modem Switch"}, + { "Mix Codec0 Switch", "Switch", "MIX Codec0"}, + { "Out Codec0 Switch", "Switch", "Mix Codec0 Switch"}, + { "Mix Codec1 Switch", "Switch", "MIX Codec1"}, + { "Out Codec1 Switch", "Switch", "Mix Codec1 Switch"}, + { "Mix Speech Tx Switch", "Switch", "MIX Speech Tx"}, + { "Out Speech Tx Switch", "Switch", "Mix Speech Tx Switch"}, + { "Mix Speech Rx Switch", "Switch", "MIX Speech Rx"}, + { "Out Speech Rx Switch", "Switch", "Mix Speech Rx Switch"}, + { "Mix Voip Switch", "Switch", "MIX Voip"}, + { "Out Voip Switch", "Switch", "Mix Voip Switch"}, + { "Mix Aware Switch", "Switch", "MIX Aware"}, + { "Out Aware Switch", "Switch", "Mix Aware Switch"}, + { "Mix VAD Switch", "Switch", "MIX VAD"}, + { "Out VAD Switch", "Switch", "Mix VAD Switch"}, + { "Mix FM Switch", "Switch", "MIX FM"}, + { "Out FM Switch", "Switch", "Mix FM Switch"}, + { "Mix PCM1 Switch", "Switch", "MIX PCM1"}, + { "Out PCM1 Switch", "Switch", "Mix PCM1 Switch"}, + { "Mix PCM2 Switch", "Switch", "MIX PCM2"}, + { "Out PCM2 Switch", "Switch", "Mix PCM2 Switch"}, + + /* the loops + * media loops dont have i/p o/p switches, just mixer enable + */ + { "Mix Sprot L0 Switch", "Switch", "MIX Sprot L0"}, + { "Mix Media L1 Switch", "Switch", "MIX Media L1"}, + { "Mix Media L2 Switch", "Switch", "MIX Media L2"}, + /* so no need as mixer switches are + * inputs to all mixers + * need to connect speech loops here + */ + { "In Speech Rx Switch", "Switch", "Out Speech Rx Switch"}, + { "In Speech Tx Switch", "Switch", "Out Speech Tx Switch"}, + /* last one, connect the output switches to ip's + * and op's. Also connect the AIFs + */ + { "In Modem Switch", "Switch", "Modem IN"}, + { "In Codec0 Switch", "Switch", "Codec IN0"}, + { "In Codec1 Switch", "Switch", "Codec IN1"}, + { "In Tone Switch", "Switch", "Tone IN"}, + { "In FM Switch", "Switch", "FM IN"}, + + { "Modem OUT", NULL, "Out Modem Switch"}, + { "Codec OUT0", NULL, "Out Codec0 Switch"}, + { "Codec OUT1", NULL, "Out Codec1 Switch"}, + { "FM OUT", NULL, "Out FM Switch"}, + + { "In Voip Switch", "Switch", "Voip IN"}, + + { "Voip OUT", NULL, "Out Voip Switch"}, + { "PCM1 OUT", NULL, "Out PCM1 Switch"}, + { "Aware OUT", NULL, "Out Aware Switch"}, + { "VAD OUT", NULL, "Out VAD Switch"}, +}; + +int sst_byte_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + + pr_debug("in %s\n", __func__); + memcpy(ucontrol->value.bytes.data, sst->byte_stream, SST_MAX_BIN_BYTES); + print_hex_dump_bytes(__func__, DUMP_PREFIX_OFFSET, + (const void *)sst->byte_stream, 32); + return 0; +} + +static int sst_check_binary_input(char *stream) +{ + struct snd_sst_bytes_v2 *bytes = (struct snd_sst_bytes_v2 *)stream; + + if (bytes->len == 0 || bytes->len > 1000) { + pr_err("length out of bounds %d\n", bytes->len); + return -EINVAL; + } + if (bytes->type == 0 || bytes->type > SND_SST_BYTES_GET) { + pr_err("type out of bounds: %d\n", bytes->type); + return -EINVAL; + } + if (bytes->block > 1) { + pr_err("block invalid %d\n", bytes->block); + return -EINVAL; + } + if (bytes->task_id == SST_TASK_ID_NONE || bytes->task_id > SST_TASK_ID_MAX) { + pr_err("taskid invalid %d\n", bytes->task_id); + return -EINVAL; + } + + return 0; +} + +int sst_byte_control_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + int ret = 0; + + pr_debug("in %s\n", __func__); + mutex_lock(&sst->lock); + memcpy(sst->byte_stream, ucontrol->value.bytes.data, SST_MAX_BIN_BYTES); + if (0 != sst_check_binary_input(sst->byte_stream)) { + mutex_unlock(&sst->lock); + return -EINVAL; + } + print_hex_dump_bytes(__func__, DUMP_PREFIX_OFFSET, + (const void *)sst->byte_stream, 32); + ret = sst_dsp->ops->set_generic_params(SST_SET_BYTE_STREAM, sst->byte_stream); + mutex_unlock(&sst->lock); + + return ret; +} + +static int sst_pipe_id_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + int ret = 0; + + ucontrol->value.integer.value[0] = sst->pipe_id; + + return ret; +} + +static int sst_pipe_id_control_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + int ret = 0; + + sst->pipe_id = ucontrol->value.integer.value[0]; + pr_debug("%s: pipe_id %d", __func__, sst->pipe_id); + + return ret; +} + +/* dB range for mrfld compress volume is -144dB to +36dB. + * Gain library expects user input in terms of 0.1dB, for example, + * 60 (in decimal) represents 6dB. + * MW will pass 2's complement value for negative dB values. + */ +static int sst_compr_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + struct sst_algo_int_control_v2 *amc = (void *)kcontrol->private_value; + u16 gain; + unsigned int gain_offset, ret; + + sst_create_compr_vol_ipc(sst->byte_stream, SND_SST_BYTES_GET, amc); + mutex_lock(&sst->lock); + ret = sst_dsp->ops->set_generic_params(SST_SET_BYTE_STREAM, + sst->byte_stream); + mutex_unlock(&sst->lock); + if (ret) { + pr_err("failed to get compress vol from fw: %d\n", ret); + return ret; + } + gain_offset = sizeof(struct snd_sst_bytes_v2) + + sizeof(struct ipc_dsp_hdr); + + /* Get params format for vol ctrl lib, size 6 bytes : + * u16 left_gain, u16 right_gain, u16 ramp + */ + memcpy(&gain, + (unsigned int *)(sst->byte_stream + gain_offset), + sizeof(u16)); + pr_debug("%s: cell_gain = %d\n", __func__, gain); + amc->value = gain; + ucontrol->value.integer.value[0] = gain; + return 0; +} + +static int sst_compr_vol_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + struct sst_algo_int_control_v2 *amc = (void *)kcontrol->private_value; + int ret = 0; + unsigned int old_val; + + pr_debug("%s: cell_gain = %ld\n", __func__,\ + ucontrol->value.integer.value[0]); + old_val = amc->value; + amc->value = ucontrol->value.integer.value[0]; + sst_create_compr_vol_ipc(sst->byte_stream, SND_SST_BYTES_SET, + amc); + + mutex_lock(&sst->lock); + ret = sst_dsp->ops->set_generic_params(SST_SET_BYTE_STREAM, + sst->byte_stream); + mutex_unlock(&sst->lock); + if (ret) { + pr_err("failed to set compress vol in fw: %d\n", ret); + amc->value = old_val; + return ret; + } + return 0; +} + +static int sst_vtsv_enroll_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + int ret = 0; + + sst->vtsv_enroll = ucontrol->value.integer.value[0]; + mutex_lock(&sst->lock); + if (sst->vtsv_enroll) + ret = sst_dsp->ops->set_generic_params(SST_SET_VTSV_INFO, + (void *)&sst->vtsv_enroll); + mutex_unlock(&sst->lock); + return ret; +} + +static int sst_vtsv_enroll_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + + ucontrol->value.integer.value[0] = sst->vtsv_enroll; + return 0; +} + +/* This value corresponds to two's complement value of -10 or -1dB */ +#define SST_COMPR_VOL_MAX_INTEG_GAIN 0xFFF6 +#define SST_COMPR_VOL_MUTE 0xFA60 /* 2's complement of -1440 or -144dB*/ + + +static const struct snd_kcontrol_new sst_mrfld_controls[] = { + SND_SOC_BYTES_EXT("SST Byte control", SST_MAX_BIN_BYTES, + sst_byte_control_get, sst_byte_control_set), + SOC_SINGLE_EXT("SST Pipe_id control", SST_PIPE_CONTROL, 0, 0x9A, 0, + sst_pipe_id_control_get, sst_pipe_id_control_set), + SST_ALGO_KCONTROL_INT("Compress Volume", SST_COMPRESS_VOL, + 0, SST_COMPR_VOL_MAX_INTEG_GAIN, 0, + sst_compr_vol_get, sst_compr_vol_set, + SST_ALGO_VOLUME_CONTROL, PIPE_MEDIA0_IN, 0, + SST_COMPR_VOL_MUTE), + SOC_SINGLE_BOOL_EXT("SST VTSV Enroll", 0, sst_vtsv_enroll_get, + sst_vtsv_enroll_set), +}; + +static DEVICE_ULONG_ATTR(low_latency_threshold, 0644, ll_threshold); +static DEVICE_ULONG_ATTR(deep_buffer_threshold, 0644, db_threshold); + +static struct attribute *device_sysfs_attrs[] = { + &dev_attr_low_latency_threshold.attr.attr, + &dev_attr_deep_buffer_threshold.attr.attr, + NULL, +}; + +static struct attribute_group attr_group = { + .attrs = device_sysfs_attrs, +}; + +int sst_dsp_init(struct snd_soc_platform *platform) +{ + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + int error = 0; + + sst->byte_stream = devm_kzalloc(platform->dev, + SST_MAX_BIN_BYTES, GFP_KERNEL); + if (sst->byte_stream == NULL) { + pr_err("kzalloc failed\n"); + return -ENOMEM; + } + + sst->widget = devm_kzalloc(platform->dev, + SST_NUM_WIDGETS * sizeof(*sst->widget), + GFP_KERNEL); + if (sst->widget == NULL) { + pr_err("kzalloc failed\n"); + return -ENOMEM; + } + + sst->vtsv_enroll = false; + /* Assign the pointer variables */ + sst->ll_db.low_latency = &ll_threshold; + sst->ll_db.deep_buffer = &db_threshold; + + pr_debug("Default ll thres %lu db thres %lu\n", ll_threshold, db_threshold); + + snd_soc_dapm_new_controls(&platform->dapm, sst_dapm_widgets, + ARRAY_SIZE(sst_dapm_widgets)); + snd_soc_dapm_add_routes(&platform->dapm, intercon, + ARRAY_SIZE(intercon)); + snd_soc_dapm_new_widgets(&platform->dapm); + snd_soc_add_platform_controls(platform, sst_mrfld_controls, + ARRAY_SIZE(sst_mrfld_controls)); + + error = sysfs_create_group(&platform->dev->kobj, &attr_group); + if (error) + pr_err("failed to create sysfs files %d\n", error); + + return error; +} diff --git a/sound/soc/intel/platform-libs/controls_v2.h b/sound/soc/intel/platform-libs/controls_v2.h new file mode 100644 index 000000000000..b54c947d0fe1 --- /dev/null +++ b/sound/soc/intel/platform-libs/controls_v2.h @@ -0,0 +1,708 @@ +/* + * controls_v2.h - Intel MID Platform driver header file + * + * Copyright (C) 2013 Intel Corp + * Author: Ramesh Babu + * Author: Omair M Abdullah + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#ifndef __SST_CONTROLS_V2_H__ +#define __SST_CONTROLS_V2_H__ + +/* + * This section defines the map for the mixer widgets. + * + * Each mixer will be represented by single value and that value will have each + * bit corresponding to one input + * + * Each out_id will correspond to one mixer and one path. Each input will be + * represented by single bit in the register. + */ + +/* mixer register ids here */ +#define SST_MIX(x) (x) + +#define SST_MIX_MODEM SST_MIX(0) +#define SST_MIX_BT SST_MIX(1) +#define SST_MIX_CODEC0 SST_MIX(2) +#define SST_MIX_CODEC1 SST_MIX(3) +#define SST_MIX_LOOP0 SST_MIX(4) +#define SST_MIX_LOOP1 SST_MIX(5) +#define SST_MIX_LOOP2 SST_MIX(6) +#define SST_MIX_PROBE SST_MIX(7) +#define SST_MIX_HF_SNS SST_MIX(8) +#define SST_MIX_HF SST_MIX(9) +#define SST_MIX_SPEECH SST_MIX(10) +#define SST_MIX_RXSPEECH SST_MIX(11) +#define SST_MIX_VOIP SST_MIX(12) +#define SST_MIX_PCM0 SST_MIX(13) +#define SST_MIX_PCM1 SST_MIX(14) +#define SST_MIX_PCM2 SST_MIX(15) +#define SST_MIX_AWARE SST_MIX(16) +#define SST_MIX_VAD SST_MIX(17) +#define SST_MIX_FM SST_MIX(18) + +#define SST_MIX_MEDIA0 SST_MIX(19) +#define SST_MIX_MEDIA1 SST_MIX(20) + +#define SST_NUM_MIX (SST_MIX_MEDIA1 + 1) + +#define SST_MIX_SWITCH (SST_NUM_MIX + 1) +#define SST_OUT_SWITCH (SST_NUM_MIX + 2) +#define SST_IN_SWITCH (SST_NUM_MIX + 3) +#define SST_MUX_REG (SST_NUM_MIX + 4) +#define SST_REG_LAST (SST_MUX_REG) + +/* last entry defines array size */ +#define SST_NUM_WIDGETS (SST_REG_LAST + 1) + +#define SST_BT_FM_MUX_SHIFT 0 +#define SST_VOICE_MODE_SHIFT 1 +#define SST_BT_MODE_SHIFT 2 + +/* in each mixer register we will define one bit for each input */ +#define SST_MIX_IP(x) (x) + +#define SST_IP_MODEM SST_MIX_IP(0) +#define SST_IP_BT SST_MIX_IP(1) +#define SST_IP_CODEC0 SST_MIX_IP(2) +#define SST_IP_CODEC1 SST_MIX_IP(3) +#define SST_IP_LOOP0 SST_MIX_IP(4) +#define SST_IP_LOOP1 SST_MIX_IP(5) +#define SST_IP_LOOP2 SST_MIX_IP(6) +#define SST_IP_PROBE SST_MIX_IP(7) +#define SST_IP_SIDETONE SST_MIX_IP(8) +#define SST_IP_TXSPEECH SST_MIX_IP(9) +#define SST_IP_SPEECH SST_MIX_IP(10) +#define SST_IP_TONE SST_MIX_IP(11) +#define SST_IP_VOIP SST_MIX_IP(12) +#define SST_IP_PCM0 SST_MIX_IP(13) +#define SST_IP_PCM1 SST_MIX_IP(14) +#define SST_IP_LOW_PCM0 SST_MIX_IP(15) +#define SST_IP_FM SST_MIX_IP(16) +#define SST_IP_MEDIA0 SST_MIX_IP(17) +#define SST_IP_MEDIA1 SST_MIX_IP(18) +#define SST_IP_MEDIA2 SST_MIX_IP(19) +#define SST_IP_MEDIA3 SST_MIX_IP(20) + +#define SST_IP_LAST SST_IP_MEDIA3 + +#define SST_SWM_INPUT_COUNT (SST_IP_LAST + 1) +#define SST_CMD_SWM_MAX_INPUTS 6 + +#define SST_PATH_ID_SHIFT 8 +#define SST_DEFAULT_LOCATION_ID 0xFFFF +#define SST_DEFAULT_CELL_NBR 0xFF +#define SST_DEFAULT_MODULE_ID 0xFFFF + +/* + * Audio DSP Path Ids. Specified by the audio DSP FW + */ +enum sst_path_index { + SST_PATH_INDEX_MODEM_OUT = (0x00 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_BT_OUT = (0x01 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_CODEC_OUT0 = (0x02 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_CODEC_OUT1 = (0x03 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_SPROT_LOOP_OUT = (0x04 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA_LOOP1_OUT = (0x05 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA_LOOP2_OUT = (0x06 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PROBE_OUT = (0x07 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_HF_SNS_OUT = (0x08 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_VOICE_UPLINK_REF2 = (0x08 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_HF_OUT = (0x09 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_VOICE_UPLINK_REF1 = (0x09 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_SPEECH_OUT = (0x0A << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_VOICE_UPLINK = (0x0A << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_RX_SPEECH_OUT = (0x0B << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_VOICE_DOWNLINK = (0x0B << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_VOIP_OUT = (0x0C << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PCM0_OUT = (0x0D << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PCM1_OUT = (0x0E << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PCM2_OUT = (0x0F << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_AWARE_OUT = (0x10 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_VAD_OUT = (0x11 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_MEDIA0_OUT = (0x12 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA1_OUT = (0x13 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_FM_OUT = (0x14 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_PROBE1_PIPE_OUT = (0x15 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PROBE2_PIPE_OUT = (0x16 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PROBE3_PIPE_OUT = (0x17 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PROBE4_PIPE_OUT = (0x18 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PROBE5_PIPE_OUT = (0x19 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PROBE6_PIPE_OUT = (0x1A << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PROBE7_PIPE_OUT = (0x1B << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PROBE8_PIPE_OUT = (0x1C << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_SIDETONE_OUT = (0x1D << SST_PATH_ID_SHIFT), + + /* Start of input paths */ + SST_PATH_INDEX_MODEM_IN = (0x80 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_BT_IN = (0x81 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_CODEC_IN0 = (0x82 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_CODEC_IN1 = (0x83 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_SPROT_LOOP_IN = (0x84 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA_LOOP1_IN = (0x85 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA_LOOP2_IN = (0x86 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_PROBE_IN = (0x87 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_SIDETONE_IN = (0x88 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_TX_SPEECH_IN = (0x89 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_SPEECH_IN = (0x8A << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_TONE_IN = (0x8B << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_VOIP_IN = (0x8C << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_PCM0_IN = (0x8D << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PCM1_IN = (0x8E << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_MEDIA0_IN = (0x8F << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA1_IN = (0x90 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_MEDIA2_IN = (0x91 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_FM_IN = (0x92 << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_PROBE1_PIPE_IN = (0x93 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PROBE2_PIPE_IN = (0x94 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PROBE3_PIPE_IN = (0x95 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PROBE4_PIPE_IN = (0x96 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PROBE5_PIPE_IN = (0x97 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PROBE6_PIPE_IN = (0x98 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PROBE7_PIPE_IN = (0x99 << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_PROBE8_PIPE_IN = (0x9A << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_MEDIA3_IN = (0x9C << SST_PATH_ID_SHIFT), + SST_PATH_INDEX_LOW_PCM0_IN = (0x9D << SST_PATH_ID_SHIFT), + + SST_PATH_INDEX_RESERVED = (0xFF << SST_PATH_ID_SHIFT), +}; + +/* + * switch matrix input path IDs + */ +enum sst_swm_inputs { + SST_SWM_IN_MODEM = (SST_PATH_INDEX_MODEM_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_BT = (SST_PATH_INDEX_BT_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_CODEC0 = (SST_PATH_INDEX_CODEC_IN0 | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_CODEC1 = (SST_PATH_INDEX_CODEC_IN1 | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_SPROT_LOOP = (SST_PATH_INDEX_SPROT_LOOP_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_MEDIA_LOOP1 = (SST_PATH_INDEX_MEDIA_LOOP1_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_MEDIA_LOOP2 = (SST_PATH_INDEX_MEDIA_LOOP2_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_PROBE = (SST_PATH_INDEX_PROBE_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_SIDETONE = (SST_PATH_INDEX_SIDETONE_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_TXSPEECH = (SST_PATH_INDEX_TX_SPEECH_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_SPEECH = (SST_PATH_INDEX_SPEECH_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_TONE = (SST_PATH_INDEX_TONE_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_VOIP = (SST_PATH_INDEX_VOIP_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_PCM0 = (SST_PATH_INDEX_PCM0_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_PCM1 = (SST_PATH_INDEX_PCM1_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_MEDIA0 = (SST_PATH_INDEX_MEDIA0_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ + SST_SWM_IN_MEDIA1 = (SST_PATH_INDEX_MEDIA1_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ + SST_SWM_IN_MEDIA2 = (SST_PATH_INDEX_MEDIA2_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ + SST_SWM_IN_FM = (SST_PATH_INDEX_FM_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_MEDIA3 = (SST_PATH_INDEX_MEDIA3_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ + SST_SWM_IN_LOW_PCM0 = (SST_PATH_INDEX_LOW_PCM0_IN | SST_DEFAULT_CELL_NBR), + SST_SWM_IN_END = (SST_PATH_INDEX_RESERVED | SST_DEFAULT_CELL_NBR) +}; + +/* + * switch matrix output path IDs + */ +enum sst_swm_outputs { + SST_SWM_OUT_MODEM = (SST_PATH_INDEX_MODEM_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_BT = (SST_PATH_INDEX_BT_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_CODEC0 = (SST_PATH_INDEX_CODEC_OUT0 | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_CODEC1 = (SST_PATH_INDEX_CODEC_OUT1 | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_SPROT_LOOP = (SST_PATH_INDEX_SPROT_LOOP_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_MEDIA_LOOP1 = (SST_PATH_INDEX_MEDIA_LOOP1_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_MEDIA_LOOP2 = (SST_PATH_INDEX_MEDIA_LOOP2_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_PROBE = (SST_PATH_INDEX_PROBE_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_HF_SNS = (SST_PATH_INDEX_HF_SNS_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_HF = (SST_PATH_INDEX_HF_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_SPEECH = (SST_PATH_INDEX_SPEECH_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_RXSPEECH = (SST_PATH_INDEX_RX_SPEECH_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_VOIP = (SST_PATH_INDEX_VOIP_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_PCM0 = (SST_PATH_INDEX_PCM0_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_PCM1 = (SST_PATH_INDEX_PCM1_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_PCM2 = (SST_PATH_INDEX_PCM2_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_AWARE = (SST_PATH_INDEX_AWARE_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_VAD = (SST_PATH_INDEX_VAD_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_MEDIA0 = (SST_PATH_INDEX_MEDIA0_OUT | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ + SST_SWM_OUT_MEDIA1 = (SST_PATH_INDEX_MEDIA1_OUT | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */ + SST_SWM_OUT_FM = (SST_PATH_INDEX_FM_OUT | SST_DEFAULT_CELL_NBR), + SST_SWM_OUT_END = (SST_PATH_INDEX_RESERVED | SST_DEFAULT_CELL_NBR), +}; + +enum sst_ipc_msg { + SST_IPC_IA_CMD = 1, + SST_IPC_IA_SET_PARAMS, + SST_IPC_IA_GET_PARAMS, +}; + +enum sst_cmd_type { + SST_CMD_BYTES_SET = 1, + SST_CMD_BYTES_GET = 2, +}; + +enum sst_task { + SST_TASK_SBA = 1, + SST_TASK_FBA_UL, + SST_TASK_MMX, + SST_TASK_AWARE, + SST_TASK_FBA_DL, +}; + +enum sst_type { + SST_TYPE_CMD = 1, + SST_TYPE_PARAMS, +}; + +enum sst_flag { + SST_FLAG_BLOCKED = 1, + SST_FLAG_NONBLOCK, +}; + +/* + * Enumeration for indexing the gain cells in VB_SET_GAIN DSP command + */ +enum sst_gain_index { + /* GAIN IDs for SB task start here */ + SST_GAIN_INDEX_MODEM_OUT, + SST_GAIN_INDEX_MODEM_IN, + SST_GAIN_INDEX_BT_OUT, + SST_GAIN_INDEX_BT_IN, + SST_GAIN_INDEX_FM_OUT, + + SST_GAIN_INDEX_FM_IN, + SST_GAIN_INDEX_CODEC_OUT0, + SST_GAIN_INDEX_CODEC_OUT1, + SST_GAIN_INDEX_CODEC_IN0, + SST_GAIN_INDEX_CODEC_IN1, + + SST_GAIN_INDEX_SPROT_LOOP_OUT, + SST_GAIN_INDEX_MEDIA_LOOP1_OUT, + SST_GAIN_INDEX_MEDIA_LOOP2_OUT, + SST_GAIN_INDEX_RX_SPEECH_OUT, + SST_GAIN_INDEX_TX_SPEECH_IN, + + SST_GAIN_INDEX_SPEECH_OUT, + SST_GAIN_INDEX_SPEECH_IN, + SST_GAIN_INDEX_HF_OUT, + SST_GAIN_INDEX_HF_SNS_OUT, + SST_GAIN_INDEX_TONE_IN, + + SST_GAIN_INDEX_SIDETONE_IN, + SST_GAIN_INDEX_PROBE_OUT, + SST_GAIN_INDEX_PROBE_IN, + SST_GAIN_INDEX_PCM0_IN_LEFT, + SST_GAIN_INDEX_PCM0_IN_RIGHT, + + SST_GAIN_INDEX_PCM1_OUT_LEFT, + SST_GAIN_INDEX_PCM1_OUT_RIGHT, + SST_GAIN_INDEX_PCM1_IN_LEFT, + SST_GAIN_INDEX_PCM1_IN_RIGHT, + SST_GAIN_INDEX_PCM2_OUT_LEFT, + + SST_GAIN_INDEX_PCM2_OUT_RIGHT, + SST_GAIN_INDEX_VOIP_OUT, + SST_GAIN_INDEX_VOIP_IN, + SST_GAIN_INDEX_AWARE_OUT, + SST_GAIN_INDEX_VAD_OUT, + + /* Gain IDs for FBA task start here */ + SST_GAIN_INDEX_VOICE_UL, + + /* Gain IDs for MMX task start here */ + SST_GAIN_INDEX_MEDIA0_IN_LEFT, + SST_GAIN_INDEX_MEDIA0_IN_RIGHT, + SST_GAIN_INDEX_MEDIA1_IN_LEFT, + SST_GAIN_INDEX_MEDIA1_IN_RIGHT, + + SST_GAIN_INDEX_MEDIA2_IN_LEFT, + SST_GAIN_INDEX_MEDIA2_IN_RIGHT, + + SST_GAIN_INDEX_GAIN_END +}; + +/* + * Audio DSP module IDs specified by FW spec + * TODO: Update with all modules + */ +enum sst_module_id { + SST_MODULE_ID_GAIN_CELL = 0x0067, + SST_MODULE_ID_SPROT = 0x006D, + SST_MODULE_ID_NR = 0x0076, + SST_MODULE_ID_BWX = 0x0077, + SST_MODULE_ID_DRP = 0x0078, + SST_MODULE_ID_MDRP = 0x0079, + + SST_MODULE_ID_ANA = 0x007A, + SST_MODULE_ID_AEC = 0x007B, + SST_MODULE_ID_NR_SNS = 0x007C, + SST_MODULE_ID_SER = 0x007D, + SST_MODULE_ID_AGC = 0x007E, + + SST_MODULE_ID_CNI = 0x007F, + SST_MODULE_ID_CONTEXT_ALGO_AWARE = 0x0080, + SST_MODULE_ID_FIR_24 = 0x0081, + SST_MODULE_ID_IIR_24 = 0x0082, + SST_MODULE_ID_FILT_DCR = 0x0082, + + SST_MODULE_ID_ASRC = 0x0083, + SST_MODULE_ID_TONE_GEN = 0x0084, + SST_MODULE_ID_BMF = 0x0086, + SST_MODULE_ID_EDL = 0x0087, + SST_MODULE_ID_GLC = 0x0088, + + SST_MODULE_ID_FIR_16 = 0x0089, + SST_MODULE_ID_IIR_16 = 0x008A, + SST_MODULE_ID_DNR = 0x008B, + + SST_MODULE_ID_CNI_TX = 0x0090, + SST_MODULE_ID_REF_LINE = 0x0091, + SST_MODULE_ID_VOLUME = 0x0092, + + SST_MODULE_ID_TASK = 0xFFFF +}; + +enum sst_cmd { + SBA_IDLE = 14, + SBA_VB_SET_SPEECH_PATH = 26, + MMX_SET_GAIN = 33, + SBA_VB_SET_GAIN = 33, + FBA_VB_RX_CNI = 35, + MMX_SET_GAIN_TIMECONST = 36, + SBA_VB_SET_TIMECONST = 36, + FBA_VB_ANA = 37, + FBA_VB_SET_FIR = 38, + FBA_VB_SET_IIR = 39, + FBA_VB_AEC = 47, + FBA_VB_NR_UL = 48, + FBA_VB_AGC = 49, + FBA_VB_NR_DL = 55, + SBA_PROBE = 66, + MMX_PROBE = 66, + FBA_VB_SET_BIQUAD_D_C = 69, + FBA_VB_DUAL_BAND_COMP = 70, + FBA_VB_SNS = 72, + FBA_VB_SER = 78, + FBA_VB_TX_CNI = 80, + SBA_VB_START = 85, + FBA_VB_SET_REF_LINE = 94, + FBA_VB_SET_DELAY_LINE = 95, + FBA_VB_BWX = 104, + FBA_VB_GMM = 105, + FBA_VB_GLC = 107, + FBA_VB_BMF = 111, + FBA_VB_DNR = 113, + MMX_SET_SWM = 114, + SBA_SET_SWM = 114, + SBA_SET_MDRP = 116, + SBA_HW_SET_SSP = 117, + SBA_SET_MEDIA_LOOP_MAP = 118, + SBA_SET_MEDIA_PATH = 119, + MMX_SET_MEDIA_PATH = 119, + SBA_VB_LPRO = 126, + SBA_VB_SET_FIR = 128, + SBA_VB_SET_IIR = 129, + SBA_SET_SSP_SLOT_MAP = 130, + AWARE_ENV_CLASS_PARAMS = 130, +}; + +enum sst_dsp_switch { + SST_SWITCH_OFF = 0, + SST_SWITCH_ON = 3, +}; + +enum sst_path_switch { + SST_PATH_OFF = 0, + SST_PATH_ON = 1, +}; + +enum sst_swm_state { + SST_SWM_OFF = 0, + SST_SWM_ON = 3, +}; + +#define SST_FILL_LOCATION_IDS(dst, cell_idx, pipe_id) do { \ + dst.location_id.p.cell_nbr_idx = (cell_idx); \ + dst.location_id.p.path_id = (pipe_id); \ + } while (0) +#define SST_FILL_LOCATION_ID(dst, loc_id) (\ + dst.location_id.f = (loc_id)) +#define SST_FILL_MODULE_ID(dst, mod_id) (\ + dst.module_id = (mod_id)) + +#define SST_FILL_DESTINATION1(dst, id) do { \ + SST_FILL_LOCATION_ID(dst, (id) & 0xFFFF); \ + SST_FILL_MODULE_ID(dst, ((id) & 0xFFFF0000) >> 16); \ + } while (0) +#define SST_FILL_DESTINATION2(dst, loc_id, mod_id) do { \ + SST_FILL_LOCATION_ID(dst, loc_id); \ + SST_FILL_MODULE_ID(dst, mod_id); \ + } while (0) +#define SST_FILL_DESTINATION3(dst, cell_idx, path_id, mod_id) do { \ + SST_FILL_LOCATION_IDS(dst, cell_idx, path_id); \ + SST_FILL_MODULE_ID(dst, mod_id); \ + } while (0) + +#define SST_FILL_DESTINATION(level, dst, ...) \ + SST_FILL_DESTINATION##level(dst, __VA_ARGS__) +#define SST_FILL_DEFAULT_DESTINATION(dst) \ + SST_FILL_DESTINATION(2, dst, SST_DEFAULT_LOCATION_ID, SST_DEFAULT_MODULE_ID) + +struct sst_destination_id { + union sst_location_id { + struct { + u8 cell_nbr_idx; /* module index */ + u8 path_id; /* pipe_id */ + } __packed p; /* part */ + u16 f; /* full */ + } __packed location_id; + u16 module_id; +} __packed; + +struct sst_dsp_header { + struct sst_destination_id dst; + u16 command_id; + u16 length; +} __packed; + +/* + * + * Common Commands + * + */ +struct sst_cmd_generic { + struct sst_dsp_header header; +} __packed; + +struct swm_input_ids { + struct sst_destination_id input_id; +} __packed; + +struct sst_cmd_set_swm { + struct sst_dsp_header header; + struct sst_destination_id output_id; + u16 switch_state; + u16 nb_inputs; + struct swm_input_ids input[SST_CMD_SWM_MAX_INPUTS]; +} __packed; + +struct sst_cmd_set_media_path { + struct sst_dsp_header header; + u16 switch_state; +} __packed; + +struct sst_cmd_set_speech_path { + struct sst_dsp_header header; + u16 switch_state; + struct { + u16 rsvd:8; + u16 sample_length:2; + u16 rate:3; + u16 format:3; + } config; +} __packed; + +struct gain_cell { + struct sst_destination_id dest; + s16 cell_gain_left; + s16 cell_gain_right; + u16 gain_time_constant; +} __packed; + +#define NUM_GAIN_CELLS 1 +struct sst_cmd_set_gain_dual { + struct sst_dsp_header header; + u16 gain_cell_num; + struct gain_cell cell_gains[NUM_GAIN_CELLS]; +} __packed; + +struct sst_cmd_set_params { + struct sst_destination_id dst; + u16 command_id; + char params[0]; +} __packed; + +/* + * + * Media (MMX) commands + * + */ + +/* + * + * SBA commands + * + */ +struct sst_cmd_sba_vb_start { + struct sst_dsp_header header; +} __packed; + +union sba_media_loop_params { + struct { + u16 rsvd:8; + u16 sample_length:2; + u16 rate:3; + u16 format:3; + } part; + u16 full; +} __packed; + +struct sst_cmd_sba_set_media_loop_map { + struct sst_dsp_header header; + u16 switch_state; + union sba_media_loop_params param; + u16 map; +} __packed; + +enum sst_ssp_mode { + SSP_MODE_MASTER = 0, + SSP_MODE_SLAVE = 1, +}; + +enum sst_ssp_pcm_mode { + SSP_PCM_MODE_NORMAL = 0, + SSP_PCM_MODE_NETWORK = 1, +}; + +enum sst_ssp_duplex { + SSP_DUPLEX = 0, + SSP_RX = 1, + SSP_TX = 2, +}; + +enum sst_ssp_fs_frequency { + SSP_FS_8_KHZ = 0, + SSP_FS_16_KHZ = 1, + SSP_FS_44_1_KHZ = 2, + SSP_FS_48_KHZ = 3, +}; + +enum sst_ssp_fs_polarity { + SSP_FS_ACTIVE_LOW = 0, + SSP_FS_ACTIVE_HIGH = 1, +}; + +enum sst_ssp_protocol { + SSP_MODE_PCM = 0, + SSP_MODE_I2S = 1, +}; + +enum sst_ssp_port_id { + SSP_MODEM = 0, + SSP_BT = 1, + SSP_FM = 2, + SSP_CODEC = 3, +}; + +struct sst_cmd_sba_hw_set_ssp { + struct sst_dsp_header header; + u16 selection; /* 0:SSP0(def), 1:SSP1, 2:SSP2 */ + + u16 switch_state; + + u16 nb_bits_per_slots:6; /* 0-32 bits, 24 (def) */ + u16 nb_slots:4; /* 0-8: slots per frame */ + u16 mode:3; /* 0:Master, 1: Slave */ + u16 duplex:3; + + u16 active_tx_slot_map:8; /* Bit map, 0:off, 1:on */ + u16 reserved1:8; + + u16 active_rx_slot_map:8; /* Bit map 0: Off, 1:On */ + u16 reserved2:8; + + u16 frame_sync_frequency; + + u16 frame_sync_polarity:8; + u16 data_polarity:8; + + u16 frame_sync_width; /* 1 to N clocks */ + u16 ssp_protocol:8; + u16 start_delay:8; /* Start delay in terms of clock ticks */ +} __packed; + +#define SST_MAX_TDM_SLOTS 8 + +struct sst_param_sba_ssp_slot_map { + struct sst_dsp_header header; + + u16 param_id; + u16 param_len; + u16 ssp_index; + + u8 rx_slot_map[SST_MAX_TDM_SLOTS]; + u8 tx_slot_map[SST_MAX_TDM_SLOTS]; +} __packed; + +enum { + SST_PROBE_EXTRACTOR = 0, + SST_PROBE_INJECTOR = 1, +}; + +struct sst_cmd_probe { + struct sst_dsp_header header; + + u16 switch_state; + struct sst_destination_id probe_dst; + + u16 shared_mem:1; + u16 probe_in:1; + u16 probe_out:1; + u16 rsvd_1:13; + + u16 rsvd_2:5; + u16 probe_mode:2; + u16 rsvd_3:1; + u16 sample_length:2; + u16 rate:3; + u16 format:3; + + u16 sm_buf_id; + + u16 gain[6]; + u16 rsvd_4[9]; +} __packed; + +struct sst_probe_config { + const char *name; + u16 loc_id; + u16 mod_id; + u8 task_id; + struct pcm_cfg { + u8 s_length:2; + u8 rate:3; + u8 format:3; + } cfg; +}; + +int sst_mix_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int sst_mix_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +#endif diff --git a/sound/soc/intel/platform-libs/controls_v2_dpcm.c b/sound/soc/intel/platform-libs/controls_v2_dpcm.c new file mode 100644 index 000000000000..311c04391a9b --- /dev/null +++ b/sound/soc/intel/platform-libs/controls_v2_dpcm.c @@ -0,0 +1,1803 @@ +/* + * controls_v2_dpcm.c - Intel MID Platform driver DPCM ALSA controls for Mrfld + * + * Copyright (C) 2013 Intel Corp + * Author: Omair Mohammed Abdullah + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include "../platform_ipc_v2.h" +#include "../sst_platform.h" +#include "../sst_platform_pvt.h" +#include "controls_v2.h" +#include "sst_widgets.h" + +static inline void sst_fill_byte_control(char *param, + u8 ipc_msg, u8 block, + u8 task_id, u8 pipe_id, + u16 len, void *cmd_data) +{ + + struct snd_sst_bytes_v2 *byte_data = (struct snd_sst_bytes_v2 *)param; + byte_data->type = SST_CMD_BYTES_SET; + byte_data->ipc_msg = ipc_msg; + byte_data->block = block; + byte_data->task_id = task_id; + byte_data->pipe_id = pipe_id; + + if (len > SST_MAX_BIN_BYTES - sizeof(*byte_data)) { + pr_err("%s: command length too big (%u)", __func__, len); + len = SST_MAX_BIN_BYTES - sizeof(*byte_data); + WARN_ON(1); /* this happens only if code is wrong */ + } + byte_data->len = len; + memcpy(byte_data->bytes, cmd_data, len); + print_hex_dump_bytes("writing to lpe: ", DUMP_PREFIX_OFFSET, + byte_data, len + sizeof(*byte_data)); +} + +static int sst_fill_and_send_cmd(struct sst_data *sst, + u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id, + void *cmd_data, u16 len) +{ + int ret = 0; + + mutex_lock(&sst->lock); + sst_fill_byte_control(sst->byte_stream, ipc_msg, block, task_id, pipe_id, + len, cmd_data); + ret = sst_dsp->ops->set_generic_params(SST_SET_BYTE_STREAM, + sst->byte_stream); + mutex_unlock(&sst->lock); + + return ret; +} + +static int sst_probe_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sst_probe_value *v = (void *)kcontrol->private_value; + + ucontrol->value.enumerated.item[0] = v->val; + return 0; +} + +static int sst_probe_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sst_probe_value *v = (void *)kcontrol->private_value; + const struct soc_enum *e = v->p_enum; + + if (ucontrol->value.enumerated.item[0] > e->max - 1) + return -EINVAL; + v->val = ucontrol->value.enumerated.item[0]; + return 0; +} + +int sst_probe_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct sst_probe_value *v = (void *)kcontrol->private_value; + const struct soc_enum *e = v->p_enum; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = e->max; + + if (uinfo->value.enumerated.item > e->max - 1) + uinfo->value.enumerated.item = e->max - 1; + strcpy(uinfo->value.enumerated.name, + e->texts[uinfo->value.enumerated.item]); + return 0; +} + +/* + * slot map value is a bitfield where each bit represents a FW channel + * + * 3 2 1 0 # 0 = codec0, 1 = codec1 + * RLRLRLRL # 3, 4 = reserved + * + * e.g. slot 0 rx map = 00001100b -> data from slot 0 goes into codec_in1 L,R + */ +static u8 sst_ssp_slot_map[SST_MAX_TDM_SLOTS] = { + 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default rx map */ +}; + +/* + * channel map value is a bitfield where each bit represents a slot + * + * 76543210 # 0 = slot 0, 1 = slot 1 + * + * e.g. codec1_0 tx map = 00000101b -> data from codec_out1_0 goes into slot 0, 2 + */ +static u8 sst_ssp_channel_map[SST_MAX_TDM_SLOTS] = { + 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default tx map */ +}; + +static int sst_slot_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *e = (void *)kcontrol->private_value; + unsigned int ctl_no = e->reg; + unsigned int is_tx = e->reg2; + unsigned int val, mux; + u8 *map = is_tx ? sst_ssp_channel_map : sst_ssp_slot_map; + + val = 1 << ctl_no; + /* search which slot/channel has this bit set - there should be only one */ + for (mux = e->max; mux > 0; mux--) + if (map[mux - 1] & val) + break; + + ucontrol->value.enumerated.item[0] = mux; + pr_debug("%s: %s - %s map = %#x\n", __func__, is_tx ? "tx channel" : "rx slot", + e->texts[mux], mux ? map[mux - 1] : -1); + return 0; +} + +/* + * (de)interleaver controls are defined in opposite sense to be user-friendly + * + * Instead of the enum value being the value set to the register, it is the + * register address; and the kcontrol_no is the value written to the register. + * + * This means that whenever an enum is set, we need to clear the bit + * for that kcontrol_no for all the interleaver OR deinterleaver registers + */ +static int sst_slot_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *e = (void *)kcontrol->private_value; + int i; + unsigned int ctl_no = e->reg; + unsigned int is_tx = e->reg2; + unsigned int slot_channel_no; + unsigned int val, mux; + u8 *map = is_tx ? sst_ssp_channel_map : sst_ssp_slot_map; + + val = 1 << ctl_no; + mux = ucontrol->value.enumerated.item[0]; + if (mux > e->max - 1) + return -EINVAL; + + /* first clear all registers of this bit */ + for (i = 0; i < e->max; i++) + map[i] &= ~val; + + if (mux == 0) /* kctl set to 'none' */ + return 0; + + /* offset by one to take "None" into account */ + slot_channel_no = mux - 1; + map[slot_channel_no] |= val; + + pr_debug("%s: %s %s map = %#x\n", __func__, is_tx ? "tx channel" : "rx slot", + e->texts[mux], map[slot_channel_no]); + return 0; +} + +/* assumes a boolean mux */ +static inline bool get_mux_state(struct sst_data *sst, unsigned int reg, unsigned int shift) +{ + return (sst_reg_read(sst, reg, shift, 1) == 1); +} + +static int sst_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct sst_data *sst = snd_soc_platform_get_drvdata(widget->platform); + struct soc_enum *e = (void *)kcontrol->private_value; + unsigned int max = e->max - 1; + + ucontrol->value.enumerated.item[0] = sst_reg_read(sst, e->reg, e->shift_l, max); + return 0; +} + +static int sst_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct sst_data *sst = snd_soc_platform_get_drvdata(widget->platform); + struct soc_enum *e = (void *)kcontrol->private_value; + struct snd_soc_dapm_update update; + unsigned int max = e->max - 1; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int mux, val; + + if (ucontrol->value.enumerated.item[0] > e->max - 1) + return -EINVAL; + + mux = ucontrol->value.enumerated.item[0]; + val = sst_reg_write(sst, e->reg, e->shift_l, max, mux); + + pr_debug("%s: reg[%d] = %#x\n", __func__, e->reg, val); + + widget->value = val; + update.kcontrol = kcontrol; + update.widget = widget; + update.reg = e->reg; + update.mask = mask; + update.val = val; + + widget->dapm->update = &update; + snd_soc_dapm_mux_update_power(widget, kcontrol, mux, e); + widget->dapm->update = NULL; + return 0; +} + +static int sst_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + struct soc_enum *e = (void *)kcontrol->private_value; + unsigned int max = e->max - 1; + + ucontrol->value.enumerated.item[0] = sst_reg_read(sst, e->reg, e->shift_l, max); + return 0; +} + +static int sst_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + struct soc_enum *e = (void *)kcontrol->private_value; + unsigned int max = e->max - 1; + unsigned int val; + + if (ucontrol->value.enumerated.item[0] > e->max - 1) + return -EINVAL; + + val = sst_reg_write(sst, e->reg, e->shift_l, max, ucontrol->value.enumerated.item[0]); + pr_debug("%s: reg[%d] - %#x\n", __func__, e->reg, val); + return 0; +} + +static void sst_send_algo_cmd(struct sst_data *sst, + struct sst_algo_control *bc) +{ + int len; + struct sst_cmd_set_params *cmd; + + if (bc->params == NULL) + return; + + len = sizeof(cmd->dst) + sizeof(cmd->command_id) + bc->max; + + cmd = kzalloc(len + bc->max, GFP_KERNEL); + if (cmd == NULL) { + pr_err("Failed to send cmd, kzalloc failed\n"); + return; + } + + SST_FILL_DESTINATION(2, cmd->dst, bc->pipe_id, bc->module_id); + cmd->command_id = bc->cmd_id; + memcpy(cmd->params, bc->params, bc->max); + + sst_fill_and_send_cmd(sst, SST_IPC_IA_SET_PARAMS, SST_FLAG_BLOCKED, + bc->task_id, 0, cmd, len); + kfree(cmd); + +} + +static void sst_find_and_send_pipe_algo(struct snd_soc_platform *platform, + struct snd_soc_dapm_widget *w) +{ + struct sst_algo_control *bc; + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + struct sst_ids *ids = w->priv; + struct module *algo = NULL; + + pr_debug("Enter:%s, widget=%s\n", __func__, w->name); + + list_for_each_entry(algo, &ids->algo_list, node) { + bc = (void *)algo->kctl->private_value; + + pr_debug("Found algo control name =%s pipe=%s\n", algo->kctl->id.name, w->name); + sst_send_algo_cmd(sst, bc); + } +} + +int sst_algo_bytes_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct sst_algo_control *bc = (void *)kcontrol->private_value; + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = bc->max; + + if (bc->params == NULL) { + bc->params = devm_kzalloc(platform->dev, bc->max, GFP_KERNEL); + if (bc->params == NULL) { + pr_err("kzalloc failed\n"); + return -ENOMEM; + } + } + return 0; +} + +static int sst_algo_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sst_algo_control *bc = (void *)kcontrol->private_value; + + pr_debug("in %s\n", __func__); + switch (bc->type) { + case SST_ALGO_PARAMS: + if (bc->params) + memcpy(ucontrol->value.bytes.data, bc->params, bc->max); + break; + case SST_ALGO_BYPASS: + ucontrol->value.integer.value[0] = bc->bypass ? 1 : 0; + pr_debug("%s: bypass %d\n", __func__, bc->bypass); + break; + default: + pr_err("Invalid Input- algo type:%d\n", bc->type); + return -EINVAL; + + } + return 0; +} + +static int sst_algo_control_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + struct sst_algo_control *bc = (void *)kcontrol->private_value; + + pr_debug("in %s control_name=%s\n", __func__, kcontrol->id.name); + switch (bc->type) { + case SST_ALGO_PARAMS: + if (bc->params) + memcpy(bc->params, ucontrol->value.bytes.data, bc->max); + break; + case SST_ALGO_BYPASS: + bc->bypass = !!ucontrol->value.integer.value[0]; + pr_debug("%s: Mute %d\n", __func__, bc->bypass); + break; + default: + pr_err("Invalid Input- algo type:%ld\n", ucontrol->value.integer.value[0]); + return -EINVAL; + } + /*if pipe is enabled, need to send the algo params from here */ + if (bc->w && bc->w->power) + sst_send_algo_cmd(sst, bc); + + return 0; +} + +static int sst_gain_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = mc->stereo ? 2 : 1; + uinfo->value.integer.min = mc->min; + uinfo->value.integer.max = mc->max; + return 0; +} + +static void sst_send_gain_cmd(struct sst_data *sst, struct sst_gain_value *gv, + u16 task_id, u16 loc_id, u16 module_id, int mute) +{ + struct sst_cmd_set_gain_dual cmd; + pr_debug("%s", __func__); + + cmd.header.command_id = MMX_SET_GAIN; + SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); + cmd.gain_cell_num = 1; + + if (mute || gv->mute) { + cmd.cell_gains[0].cell_gain_left = SST_GAIN_MIN_VALUE; + cmd.cell_gains[0].cell_gain_right = SST_GAIN_MIN_VALUE; + } else { + cmd.cell_gains[0].cell_gain_left = gv->l_gain; + cmd.cell_gains[0].cell_gain_right = gv->r_gain; + } + SST_FILL_DESTINATION(2, cmd.cell_gains[0].dest, + loc_id, module_id); + cmd.cell_gains[0].gain_time_constant = gv->ramp_duration; + + cmd.header.length = sizeof(struct sst_cmd_set_gain_dual) + - sizeof(struct sst_dsp_header); + + sst_fill_and_send_cmd(sst, SST_IPC_IA_SET_PARAMS, SST_FLAG_BLOCKED, + task_id, 0, &cmd, + sizeof(cmd.header) + cmd.header.length); +} + +static int sst_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value; + struct sst_gain_value *gv = mc->gain_val; + + switch (mc->type) { + case SST_GAIN_TLV: + ucontrol->value.integer.value[0] = gv->l_gain; + ucontrol->value.integer.value[1] = gv->r_gain; + pr_debug("%s: Volume %d, %d\n", __func__, gv->l_gain, gv->r_gain); + break; + case SST_GAIN_MUTE: + ucontrol->value.integer.value[0] = gv->mute ? 1 : 0; + pr_debug("%s: Mute %d\n", __func__, gv->mute); + break; + case SST_GAIN_RAMP_DURATION: + ucontrol->value.integer.value[0] = gv->ramp_duration; + pr_debug("%s: RampDuration %d\n", __func__, gv->ramp_duration); + break; + default: + pr_err("Invalid Input- gain type:%d\n", mc->type); + return -EINVAL; + }; + return 0; +} + +static int sst_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_kcontrol_chip(kcontrol); + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value; + struct sst_gain_value *gv = mc->gain_val; + + switch (mc->type) { + case SST_GAIN_TLV: + gv->l_gain = ucontrol->value.integer.value[0]; + gv->r_gain = ucontrol->value.integer.value[1]; + pr_debug("%s: Volume %d, %d\n", __func__, gv->l_gain, gv->r_gain); + break; + case SST_GAIN_MUTE: + gv->mute = !!ucontrol->value.integer.value[0]; + pr_debug("%s: Mute %d\n", __func__, gv->mute); + break; + case SST_GAIN_RAMP_DURATION: + gv->ramp_duration = ucontrol->value.integer.value[0]; + pr_debug("%s: RampDuration %d\n", __func__, gv->ramp_duration); + break; + default: + pr_err("Invalid Input- gain type:%d\n", mc->type); + return -EINVAL; + }; + + if (mc->w && mc->w->power) + sst_send_gain_cmd(sst, gv, mc->task_id, + mc->pipe_id | mc->instance_id, mc->module_id, 0); + return 0; +} + +static const DECLARE_TLV_DB_SCALE(sst_gain_tlv_common, SST_GAIN_MIN_VALUE * 10, 10, 0); + +/* Look up table to convert MIXER SW bit regs to SWM inputs */ +static const uint swm_mixer_input_ids[SST_SWM_INPUT_COUNT] = { + [SST_IP_MODEM] = SST_SWM_IN_MODEM, + [SST_IP_BT] = SST_SWM_IN_BT, + [SST_IP_CODEC0] = SST_SWM_IN_CODEC0, + [SST_IP_CODEC1] = SST_SWM_IN_CODEC1, + [SST_IP_LOOP0] = SST_SWM_IN_SPROT_LOOP, + [SST_IP_LOOP1] = SST_SWM_IN_MEDIA_LOOP1, + [SST_IP_LOOP2] = SST_SWM_IN_MEDIA_LOOP2, + [SST_IP_SIDETONE] = SST_SWM_IN_SIDETONE, + [SST_IP_TXSPEECH] = SST_SWM_IN_TXSPEECH, + [SST_IP_SPEECH] = SST_SWM_IN_SPEECH, + [SST_IP_TONE] = SST_SWM_IN_TONE, + [SST_IP_VOIP] = SST_SWM_IN_VOIP, + [SST_IP_PCM0] = SST_SWM_IN_PCM0, + [SST_IP_PCM1] = SST_SWM_IN_PCM1, + [SST_IP_LOW_PCM0] = SST_SWM_IN_LOW_PCM0, + [SST_IP_FM] = SST_SWM_IN_FM, + [SST_IP_MEDIA0] = SST_SWM_IN_MEDIA0, + [SST_IP_MEDIA1] = SST_SWM_IN_MEDIA1, + [SST_IP_MEDIA2] = SST_SWM_IN_MEDIA2, + [SST_IP_MEDIA3] = SST_SWM_IN_MEDIA3, +}; + +static int fill_swm_input(struct swm_input_ids *swm_input, unsigned int reg) +{ + uint i, is_set, nb_inputs = 0; + u16 input_loc_id; + + pr_debug("%s:reg value:%#x\n", __func__, reg); + for (i = 0; i < SST_SWM_INPUT_COUNT; i++) { + is_set = reg & BIT(i); + if (!is_set) + continue; + + input_loc_id = swm_mixer_input_ids[i]; + SST_FILL_DESTINATION(2, swm_input->input_id, + input_loc_id, SST_DEFAULT_MODULE_ID); + nb_inputs++; + swm_input++; + pr_debug("input id:%#x, nb_inputs:%d\n", input_loc_id, nb_inputs); + + if (nb_inputs == SST_CMD_SWM_MAX_INPUTS) { + pr_warn("%s: SET_SWM cmd max inputs reached", __func__); + break; + } + } + return nb_inputs; +} + +static void sst_set_pipe_gain(struct sst_ids *ids, struct sst_data *sst, int mute) +{ + struct sst_gain_mixer_control *mc; + struct sst_gain_value *gv; + struct module *gain = NULL; + + list_for_each_entry(gain, &ids->gain_list, node) { + struct snd_kcontrol *kctl = gain->kctl; + + pr_debug("control name=%s", kctl->id.name); + mc = (void *)kctl->private_value; + gv = mc->gain_val; + + sst_send_gain_cmd(sst, gv, mc->task_id, + mc->pipe_id | mc->instance_id, mc->module_id, mute); + } +} + +static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct sst_cmd_set_swm cmd; + struct sst_data *sst = snd_soc_platform_get_drvdata(w->platform); + struct sst_ids *ids = w->priv; + bool set_mixer = false; + int val = sst->widget[ids->reg]; + + pr_debug("%s: widget=%s\n", __func__, w->name); + pr_debug("%s: reg[%d] = %#x\n", __func__, ids->reg, val); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + case SND_SOC_DAPM_POST_PMD: + set_mixer = true; + break; + case SND_SOC_DAPM_POST_REG: + if (w->power) + set_mixer = true; + break; + default: + set_mixer = false; + } + + if (set_mixer == false) + return 0; + + if (SND_SOC_DAPM_EVENT_ON(event) || + event == SND_SOC_DAPM_POST_REG) + cmd.switch_state = SST_SWM_ON; + else + cmd.switch_state = SST_SWM_OFF; + + SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); + /* MMX_SET_SWM == SBA_SET_SWM */ + cmd.header.command_id = SBA_SET_SWM; + + SST_FILL_DESTINATION(2, cmd.output_id, + ids->location_id, SST_DEFAULT_MODULE_ID); + cmd.nb_inputs = fill_swm_input(&cmd.input[0], val); + cmd.header.length = offsetof(struct sst_cmd_set_swm, input) - sizeof(struct sst_dsp_header) + + (cmd.nb_inputs * sizeof(cmd.input[0])); + + sst_fill_and_send_cmd(sst, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, + ids->task_id, 0, &cmd, + sizeof(cmd.header) + cmd.header.length); + return 0; +} + +/* SBA mixers - 16 inputs */ +#define SST_SBA_DECLARE_MIX_CONTROLS(kctl_name, mixer_reg) \ + static const struct snd_kcontrol_new kctl_name[] = { \ + SOC_SINGLE_EXT("modem_in", mixer_reg, SST_IP_MODEM, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("bt_in", mixer_reg, SST_IP_BT, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("codec_in0", mixer_reg, SST_IP_CODEC0, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("codec_in1", mixer_reg, SST_IP_CODEC1, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("sprot_loop_in", mixer_reg, SST_IP_LOOP0, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("media_loop1_in", mixer_reg, SST_IP_LOOP1, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("media_loop2_in", mixer_reg, SST_IP_LOOP2, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("sidetone_in", mixer_reg, SST_IP_SIDETONE, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("txspeech_in", mixer_reg, SST_IP_TXSPEECH, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("speech_in", mixer_reg, SST_IP_SPEECH, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("tone_in", mixer_reg, SST_IP_TONE, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("voip_in", mixer_reg, SST_IP_VOIP, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("pcm0_in", mixer_reg, SST_IP_PCM0, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("pcm1_in", mixer_reg, SST_IP_PCM1, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("low_pcm0_in", mixer_reg, SST_IP_LOW_PCM0, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("fm_in", mixer_reg, SST_IP_FM, 1, 0, \ + sst_mix_get, sst_mix_put), \ + } + +#define SST_SBA_MIXER_GRAPH_MAP(mix_name) \ + { mix_name, "modem_in", "modem_in" }, \ + { mix_name, "bt_in", "bt_in" }, \ + { mix_name, "codec_in0", "codec_in0" }, \ + { mix_name, "codec_in1", "codec_in1" }, \ + { mix_name, "sprot_loop_in", "sprot_loop_in" }, \ + { mix_name, "media_loop1_in", "media_loop1_in" }, \ + { mix_name, "media_loop2_in", "media_loop2_in" }, \ + { mix_name, "sidetone_in", "sidetone_in" }, \ + { mix_name, "txspeech_in", "txspeech_in" }, \ + { mix_name, "speech_in", "speech_in" }, \ + { mix_name, "tone_in", "tone_in" }, \ + { mix_name, "voip_in", "voip_in" }, \ + { mix_name, "pcm0_in", "pcm0_in" }, \ + { mix_name, "pcm1_in", "pcm1_in" }, \ + { mix_name, "low_pcm0_in", "low_pcm0_in" }, \ + { mix_name, "fm_in", "fm_in" } + +#define SST_MMX_DECLARE_MIX_CONTROLS(kctl_name, mixer_reg) \ + static const struct snd_kcontrol_new kctl_name[] = { \ + SOC_SINGLE_EXT("media0_in", mixer_reg, SST_IP_MEDIA0, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("media1_in", mixer_reg, SST_IP_MEDIA1, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("media2_in", mixer_reg, SST_IP_MEDIA2, 1, 0, \ + sst_mix_get, sst_mix_put), \ + SOC_SINGLE_EXT("media3_in", mixer_reg, SST_IP_MEDIA3, 1, 0, \ + sst_mix_get, sst_mix_put), \ + } + +SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media0_controls, SST_MIX_MEDIA0); +SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media1_controls, SST_MIX_MEDIA1); + +/* 18 SBA mixers */ +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm0_controls, SST_MIX_PCM0); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm1_controls, SST_MIX_PCM1); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm2_controls, SST_MIX_PCM2); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_sprot_l0_controls, SST_MIX_LOOP0); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l1_controls, SST_MIX_LOOP1); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l2_controls, SST_MIX_LOOP2); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_voip_controls, SST_MIX_VOIP); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_aware_controls, SST_MIX_AWARE); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_vad_controls, SST_MIX_VAD); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_hf_sns_controls, SST_MIX_HF_SNS); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_hf_controls, SST_MIX_HF); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_speech_controls, SST_MIX_SPEECH); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_rxspeech_controls, SST_MIX_RXSPEECH); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec0_controls, SST_MIX_CODEC0); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec1_controls, SST_MIX_CODEC1); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_bt_controls, SST_MIX_BT); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_fm_controls, SST_MIX_FM); +SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_modem_controls, SST_MIX_MODEM); + +static int sst_vb_trigger_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct sst_cmd_generic cmd; + struct sst_data *sst = snd_soc_platform_get_drvdata(w->platform); + + pr_debug("Enter:%s, widget=%s\n", __func__, w->name); + if (SND_SOC_DAPM_EVENT_ON(event)) + cmd.header.command_id = SBA_VB_START; + else + cmd.header.command_id = SBA_IDLE; + + SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); + cmd.header.length = 0; + + if (SND_SOC_DAPM_EVENT_ON(event)) + sst_dsp->ops->power(true); + + sst_fill_and_send_cmd(sst, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, + SST_TASK_SBA, 0, &cmd, + sizeof(cmd.header) + cmd.header.length); + + if (!SND_SOC_DAPM_EVENT_ON(event)) + sst_dsp->ops->power(false); + return 0; +} + +static void sst_send_slot_map(struct sst_data *sst) +{ + struct sst_param_sba_ssp_slot_map cmd; + + pr_debug("Enter: %s", __func__); + + SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); + cmd.header.command_id = SBA_SET_SSP_SLOT_MAP; + cmd.header.length = sizeof(struct sst_param_sba_ssp_slot_map) + - sizeof(struct sst_dsp_header); + + cmd.param_id = SBA_SET_SSP_SLOT_MAP; + cmd.param_len = sizeof(cmd.rx_slot_map) + sizeof(cmd.tx_slot_map) + sizeof(cmd.ssp_index); + cmd.ssp_index = SSP_CODEC; + + memcpy(cmd.rx_slot_map, &sst_ssp_slot_map[0], sizeof(cmd.rx_slot_map)); + memcpy(cmd.tx_slot_map, &sst_ssp_channel_map[0], sizeof(cmd.tx_slot_map)); + + sst_fill_and_send_cmd(sst, SST_IPC_IA_SET_PARAMS, SST_FLAG_BLOCKED, + SST_TASK_SBA, 0, &cmd, + sizeof(cmd.header) + cmd.header.length); +} + +static int sst_ssp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct sst_cmd_sba_hw_set_ssp cmd; + struct sst_data *sst = snd_soc_platform_get_drvdata(w->platform); + struct sst_ids *ids = w->priv; + static int ssp_active[SST_NUM_SSPS]; + unsigned int domain, mux; + unsigned int ssp_no = ids->ssp->ssp_number; + int domain_shift, mux_shift; + const struct sst_ssp_config *config; + + pr_debug("Enter:%s, widget=%s\n", __func__, w->name); + + SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); + cmd.header.command_id = SBA_HW_SET_SSP; + cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp) + - sizeof(struct sst_dsp_header); + mux_shift = *ids->ssp->mux_shift; + mux = (mux_shift == -1) ? 0 : get_mux_state(sst, SST_MUX_REG, mux_shift); + domain_shift = (*ids->ssp->domain_shift)[mux]; + domain = (domain_shift == -1) ? 0 : get_mux_state(sst, SST_MUX_REG, domain_shift); + + config = &(*ids->ssp->ssp_config)[mux][domain]; + pr_debug("%s: ssp_id: %u, mux: %d, domain: %d\n", __func__, + config->ssp_id, mux, domain); + + if (SND_SOC_DAPM_EVENT_ON(event)) + ssp_active[ssp_no]++; + else + ssp_active[ssp_no]--; + + pr_debug("%s: ssp_no: %u ssp_active: %d", __func__, ssp_no, ssp_active[ssp_no]); + if (ssp_active[ssp_no]) + cmd.switch_state = SST_SWITCH_ON; + else + cmd.switch_state = SST_SWITCH_OFF; + + cmd.selection = config->ssp_id; + cmd.nb_bits_per_slots = config->bits_per_slot; + cmd.nb_slots = config->slots; + cmd.mode = config->ssp_mode | (config->pcm_mode << 1); + cmd.duplex = config->duplex; + cmd.active_tx_slot_map = config->active_slot_map; + cmd.active_rx_slot_map = config->active_slot_map; + cmd.frame_sync_frequency = config->fs_frequency; + cmd.frame_sync_polarity = SSP_FS_ACTIVE_HIGH; + cmd.data_polarity = 1; + cmd.frame_sync_width = config->fs_width; + cmd.ssp_protocol = config->ssp_protocol; + cmd.start_delay = config->start_delay; + cmd.reserved1 = cmd.reserved2 = 0xFF; + + sst_fill_and_send_cmd(sst, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, + SST_TASK_SBA, 0, &cmd, + sizeof(cmd.header) + cmd.header.length); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + sst_find_and_send_pipe_algo(w->platform, w); + sst_send_slot_map(sst); + sst_set_pipe_gain(ids, sst, 0); + } + return 0; +} + +static int sst_set_speech_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct sst_cmd_set_speech_path cmd; + struct sst_data *sst = snd_soc_platform_get_drvdata(w->platform); + struct sst_ids *ids = w->priv; + bool is_wideband; + static int speech_active; + + pr_debug("%s: widget=%s\n", __func__, w->name); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + speech_active++; + cmd.switch_state = SST_SWITCH_ON; + } else { + speech_active--; + cmd.switch_state = SST_SWITCH_OFF; + } + + SST_FILL_DEFAULT_DESTINATION(cmd.header.dst); + + cmd.header.command_id = SBA_VB_SET_SPEECH_PATH; + cmd.header.length = sizeof(struct sst_cmd_set_speech_path) + - sizeof(struct sst_dsp_header); + cmd.config.sample_length = 0; + cmd.config.rate = 0; /* 8 khz */ + cmd.config.format = 0; + + is_wideband = get_mux_state(sst, SST_MUX_REG, SST_VOICE_MODE_SHIFT); + if (is_wideband) + cmd.config.rate = 1; /* 16 khz */ + + if ((SND_SOC_DAPM_EVENT_ON(event) && (speech_active == 1)) || + (SND_SOC_DAPM_EVENT_OFF(event) && (speech_active == 0))) + sst_fill_and_send_cmd(sst, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, + SST_TASK_SBA, 0, &cmd, + sizeof(cmd.header) + cmd.header.length); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + sst_find_and_send_pipe_algo(w->platform, w); + sst_set_pipe_gain(ids, sst, 0); + } + + return 0; + +} + +static int sst_set_media_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct sst_cmd_set_media_path cmd; + struct sst_data *sst = snd_soc_platform_get_drvdata(w->platform); + struct sst_ids *ids = w->priv; + + pr_debug("%s: widget=%s\n", __func__, w->name); + pr_debug("%s: task=%u, location=%#x\n", __func__, + ids->task_id, ids->location_id); + + if (SND_SOC_DAPM_EVENT_ON(event)) + cmd.switch_state = SST_PATH_ON; + else + cmd.switch_state = SST_PATH_OFF; + + SST_FILL_DESTINATION(2, cmd.header.dst, + ids->location_id, SST_DEFAULT_MODULE_ID); + + /* MMX_SET_MEDIA_PATH == SBA_SET_MEDIA_PATH */ + cmd.header.command_id = MMX_SET_MEDIA_PATH; + cmd.header.length = sizeof(struct sst_cmd_set_media_path) + - sizeof(struct sst_dsp_header); + + sst_fill_and_send_cmd(sst, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, + ids->task_id, 0, &cmd, + sizeof(cmd.header) + cmd.header.length); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + sst_find_and_send_pipe_algo(w->platform, w); + sst_set_pipe_gain(ids, sst, 0); + } + + return 0; +} + +static int sst_set_media_loop(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct sst_cmd_sba_set_media_loop_map cmd; + struct sst_data *sst = snd_soc_platform_get_drvdata(w->platform); + struct sst_ids *ids = w->priv; + + pr_debug("Enter:%s, widget=%s\n", __func__, w->name); + if (SND_SOC_DAPM_EVENT_ON(event)) + cmd.switch_state = SST_SWITCH_ON; + else + cmd.switch_state = SST_SWITCH_OFF; + + SST_FILL_DESTINATION(2, cmd.header.dst, + ids->location_id, SST_DEFAULT_MODULE_ID); + + cmd.header.command_id = SBA_SET_MEDIA_LOOP_MAP; + cmd.header.length = sizeof(struct sst_cmd_sba_set_media_loop_map) + - sizeof(struct sst_dsp_header); + cmd.param.part.rate = 2; /* 48khz */ + + cmd.param.part.format = ids->format; /* stereo/Mono */ + cmd.param.part.sample_length = 1; /* 24bit left justified*/ + cmd.map = 0; /* Algo sequence: Gain - DRP - FIR - IIR */ + + sst_fill_and_send_cmd(sst, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, + SST_TASK_SBA, 0, &cmd, + sizeof(cmd.header) + cmd.header.length); + if (SND_SOC_DAPM_EVENT_ON(event)) { + sst_find_and_send_pipe_algo(w->platform, w); + sst_set_pipe_gain(ids, sst, 0); + } + return 0; +} + +static int sst_send_probe_cmd(struct sst_data *sst, u16 probe_pipe_id, + int mode, int switch_state, + const struct sst_probe_config *probe_cfg) +{ + struct sst_cmd_probe cmd; + + memset(&cmd, 0, sizeof(cmd)); + + SST_FILL_DESTINATION(3, cmd.header.dst, SST_DEFAULT_CELL_NBR, + probe_pipe_id, SST_DEFAULT_MODULE_ID); + cmd.header.command_id = SBA_PROBE; + cmd.header.length = sizeof(struct sst_cmd_probe) + - sizeof(struct sst_dsp_header); + cmd.switch_state = switch_state; + + SST_FILL_DESTINATION(2, cmd.probe_dst, + probe_cfg->loc_id, probe_cfg->mod_id); + + cmd.shared_mem = 1; + cmd.probe_in = 0; + cmd.probe_out = 0; + + cmd.probe_mode = mode; + cmd.sample_length = probe_cfg->cfg.s_length; + cmd.rate = probe_cfg->cfg.rate; + cmd.format = probe_cfg->cfg.format; + cmd.sm_buf_id = 1; + + return sst_fill_and_send_cmd(sst, SST_IPC_IA_CMD, SST_FLAG_BLOCKED, + probe_cfg->task_id, 0, &cmd, + sizeof(cmd.header) + cmd.header.length); +} + +static const struct snd_kcontrol_new sst_probe_controls[]; +static const struct sst_probe_config sst_probes[]; + +#define SST_MAX_PROBE_STREAMS 8 +int sst_dpcm_probe_send(struct snd_soc_platform *platform, u16 probe_pipe_id, + int substream, int direction, bool on) +{ + int switch_state = on ? SST_SWITCH_ON : SST_SWITCH_OFF; + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + const struct sst_probe_config *probe_cfg; + struct sst_probe_value *probe_val; + char *type; + int offset; + int mode; + + if (direction == SNDRV_PCM_STREAM_CAPTURE) { + mode = SST_PROBE_EXTRACTOR; + offset = 0; + type = "extractor"; + } else { + mode = SST_PROBE_INJECTOR; + offset = SST_MAX_PROBE_STREAMS; + type = "injector"; + } + /* get the value of the probe connection kcontrol */ + probe_val = (void *)sst_probe_controls[substream + offset].private_value; + probe_cfg = &sst_probes[probe_val->val]; + + pr_debug("%s: substream=%d, direction=%d\n", __func__, substream, direction); + pr_debug("%s: %s probe point at %s\n", __func__, type, probe_cfg->name); + + return sst_send_probe_cmd(sst, probe_pipe_id, mode, switch_state, probe_cfg); +} + +static const struct snd_kcontrol_new sst_mix_sw_aware = + SOC_SINGLE_EXT("switch", SST_MIX_SWITCH, 0, 1, 0, + sst_mix_get, sst_mix_put); + +static const char * const sst_bt_fm_texts[] = { + "fm", "bt", +}; + +static const struct snd_kcontrol_new sst_bt_fm_mux = + SST_SSP_MUX_CTL("ssp1_out", 0, SST_MUX_REG, SST_BT_FM_MUX_SHIFT, sst_bt_fm_texts, + sst_mux_get, sst_mux_put); + +#define SST_SSP_CODEC_MUX 0 +#define SST_SSP_CODEC_DOMAIN 0 +#define SST_SSP_MODEM_MUX 0 +#define SST_SSP_MODEM_DOMAIN 0 +#define SST_SSP_FM_MUX 0 +#define SST_SSP_FM_DOMAIN 0 +#define SST_SSP_BT_MUX 1 +#define SST_SSP_BT_NB_DOMAIN 0 +#define SST_SSP_BT_WB_DOMAIN 1 + +static const int sst_ssp_mux_shift[SST_NUM_SSPS] = { + [SST_SSP0] = -1, /* no register shift, i.e. single mux value */ + [SST_SSP1] = SST_BT_FM_MUX_SHIFT, + [SST_SSP2] = -1, +}; + +static const int sst_ssp_domain_shift[SST_NUM_SSPS][SST_MAX_SSP_MUX] = { + [SST_SSP0][0] = -1, /* no domain shift, i.e. single domain */ + [SST_SSP1] = { + [SST_SSP_FM_MUX] = -1, + [SST_SSP_BT_MUX] = SST_BT_MODE_SHIFT, + }, + [SST_SSP2][0] = -1, +}; + +static const struct sst_ssp_config +sst_ssp_configs[SST_NUM_SSPS][SST_MAX_SSP_MUX][SST_MAX_SSP_DOMAINS] = { + [SST_SSP0] = { + [SST_SSP_MODEM_MUX] = { + [SST_SSP_MODEM_DOMAIN] = { + .ssp_id = SSP_MODEM, + .bits_per_slot = 16, + .slots = 1, + .ssp_mode = SSP_MODE_MASTER, + .pcm_mode = SSP_PCM_MODE_NETWORK, + .duplex = SSP_DUPLEX, + .ssp_protocol = SSP_MODE_PCM, + .fs_width = 1, + .fs_frequency = SSP_FS_48_KHZ, + .active_slot_map = 0x1, + .start_delay = 1, + }, + }, + }, + [SST_SSP1] = { + [SST_SSP_FM_MUX] = { + [SST_SSP_FM_DOMAIN] = { + .ssp_id = SSP_FM, + .bits_per_slot = 16, + .slots = 2, + .ssp_mode = SSP_MODE_MASTER, + .pcm_mode = SSP_PCM_MODE_NORMAL, + .duplex = SSP_DUPLEX, + .ssp_protocol = SSP_MODE_I2S, + .fs_width = 32, + .fs_frequency = SSP_FS_48_KHZ, + .active_slot_map = 0x3, + .start_delay = 0, + }, + }, + [SST_SSP_BT_MUX] = { + [SST_SSP_BT_NB_DOMAIN] = { + .ssp_id = SSP_BT, + .bits_per_slot = 16, + .slots = 1, + .ssp_mode = SSP_MODE_MASTER, + .pcm_mode = SSP_PCM_MODE_NORMAL, + .duplex = SSP_DUPLEX, + .ssp_protocol = SSP_MODE_PCM, + .fs_width = 1, + .fs_frequency = SSP_FS_8_KHZ, + .active_slot_map = 0x1, + .start_delay = 1, + }, + [SST_SSP_BT_WB_DOMAIN] = { + .ssp_id = SSP_BT, + .bits_per_slot = 16, + .slots = 1, + .ssp_mode = SSP_MODE_MASTER, + .pcm_mode = SSP_PCM_MODE_NORMAL, + .duplex = SSP_DUPLEX, + .ssp_protocol = SSP_MODE_PCM, + .fs_width = 1, + .fs_frequency = SSP_FS_16_KHZ, + .active_slot_map = 0x1, + .start_delay = 1, + }, + }, + }, + [SST_SSP2] = { + [SST_SSP_CODEC_MUX] = { + [SST_SSP_CODEC_DOMAIN] = { + .ssp_id = SSP_CODEC, + .bits_per_slot = 24, + .slots = 4, + .ssp_mode = SSP_MODE_MASTER, + .pcm_mode = SSP_PCM_MODE_NETWORK, + .duplex = SSP_DUPLEX, + .ssp_protocol = SSP_MODE_PCM, + .fs_width = 1, + .fs_frequency = SSP_FS_48_KHZ, + .active_slot_map = 0xF, + .start_delay = 0, + }, + }, + }, +}; + +#define SST_SSP_CFG(wssp_no) \ + (const struct sst_ssp_cfg){ .ssp_config = &sst_ssp_configs[wssp_no], \ + .ssp_number = wssp_no, \ + .mux_shift = &sst_ssp_mux_shift[wssp_no], \ + .domain_shift = &sst_ssp_domain_shift[wssp_no], } + +static const struct snd_soc_dapm_widget sst_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("tone"), + SND_SOC_DAPM_OUTPUT("aware"), + SND_SOC_DAPM_OUTPUT("vad"), + SST_SSP_INPUT("modem_in", sst_ssp_event, SST_SSP_CFG(SST_SSP0)), + SST_SSP_AIF_IN("codec_in0", sst_ssp_event, SST_SSP_CFG(SST_SSP2)), + SST_SSP_AIF_IN("codec_in1", sst_ssp_event, SST_SSP_CFG(SST_SSP2)), + SST_SSP_INPUT("bt_fm_in", sst_ssp_event, SST_SSP_CFG(SST_SSP1)), + SST_SSP_OUTPUT("modem_out", sst_ssp_event, SST_SSP_CFG(SST_SSP0)), + SST_SSP_AIF_OUT("codec_out0", sst_ssp_event, SST_SSP_CFG(SST_SSP2)), + SST_SSP_AIF_OUT("codec_out1", sst_ssp_event, SST_SSP_CFG(SST_SSP2)), + SST_SSP_OUTPUT("bt_fm_out", sst_ssp_event, SST_SSP_CFG(SST_SSP1)), + + /* Media Paths */ + /* MediaX IN paths are set via ALLOC, so no SET_MEDIA_PATH command */ + SST_PATH_INPUT("media0_in", SST_TASK_MMX, SST_SWM_IN_MEDIA0, NULL), + SST_PATH_INPUT("media1_in", SST_TASK_MMX, SST_SWM_IN_MEDIA1, NULL), + SST_PATH_INPUT("media2_in", SST_TASK_MMX, SST_SWM_IN_MEDIA2, sst_set_media_path), + SST_PATH_INPUT("media3_in", SST_TASK_MMX, SST_SWM_IN_MEDIA3, NULL), + SST_PATH_OUTPUT("media0_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA0, sst_set_media_path), + SST_PATH_OUTPUT("media1_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA1, sst_set_media_path), + + /* SBA PCM Paths */ + SST_PATH_INPUT("pcm0_in", SST_TASK_SBA, SST_SWM_IN_PCM0, sst_set_media_path), + SST_PATH_INPUT("pcm1_in", SST_TASK_SBA, SST_SWM_IN_PCM1, sst_set_media_path), + SST_PATH_OUTPUT("pcm0_out", SST_TASK_SBA, SST_SWM_OUT_PCM0, sst_set_media_path), + SST_PATH_OUTPUT("pcm1_out", SST_TASK_SBA, SST_SWM_OUT_PCM1, sst_set_media_path), + SST_PATH_OUTPUT("pcm2_out", SST_TASK_SBA, SST_SWM_OUT_PCM2, sst_set_media_path), + /* TODO: check if this needs SET_MEDIA_PATH command*/ + SST_PATH_INPUT("low_pcm0_in", SST_TASK_SBA, SST_SWM_IN_LOW_PCM0, NULL), + + SST_PATH_INPUT("voip_in", SST_TASK_SBA, SST_SWM_IN_VOIP, sst_set_media_path), + SST_PATH_OUTPUT("voip_out", SST_TASK_SBA, SST_SWM_OUT_VOIP, sst_set_media_path), + SST_PATH_OUTPUT("aware_out", SST_TASK_SBA, SST_SWM_OUT_AWARE, sst_set_media_path), + SST_PATH_OUTPUT("vad_out", SST_TASK_SBA, SST_SWM_OUT_VAD, sst_set_media_path), + + /* SBA Loops */ + SST_PATH_INPUT("sprot_loop_in", SST_TASK_SBA, SST_SWM_IN_SPROT_LOOP, NULL), + SST_PATH_INPUT("media_loop1_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP1, NULL), + SST_PATH_INPUT("media_loop2_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP2, NULL), + SST_PATH_MEDIA_LOOP_OUTPUT("sprot_loop_out", SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, SST_FMT_MONO, sst_set_media_loop), + SST_PATH_MEDIA_LOOP_OUTPUT("media_loop1_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, SST_FMT_MONO, sst_set_media_loop), + SST_PATH_MEDIA_LOOP_OUTPUT("media_loop2_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, SST_FMT_STEREO, sst_set_media_loop), + + /* TODO: need to send command */ + SST_PATH_INPUT("sidetone_in", SST_TASK_SBA, SST_SWM_IN_SIDETONE, NULL), + SST_PATH_INPUT("tone_in", SST_TASK_SBA, SST_SWM_IN_TONE, NULL), + SST_PATH_INPUT("bt_in", SST_TASK_SBA, SST_SWM_IN_BT, NULL), + SST_PATH_INPUT("fm_in", SST_TASK_SBA, SST_SWM_IN_FM, NULL), + SST_PATH_OUTPUT("bt_out", SST_TASK_SBA, SST_SWM_OUT_BT, NULL), + SST_PATH_OUTPUT("fm_out", SST_TASK_SBA, SST_SWM_OUT_FM, NULL), + + /* SBA Voice Paths */ + SST_PATH_INPUT("speech_in", SST_TASK_SBA, SST_SWM_IN_SPEECH, sst_set_speech_path), + SST_PATH_INPUT("txspeech_in", SST_TASK_SBA, SST_SWM_IN_TXSPEECH, sst_set_speech_path), + SST_PATH_OUTPUT("hf_sns_out", SST_TASK_SBA, SST_SWM_OUT_HF_SNS, sst_set_speech_path), + SST_PATH_OUTPUT("hf_out", SST_TASK_SBA, SST_SWM_OUT_HF, sst_set_speech_path), + SST_PATH_OUTPUT("speech_out", SST_TASK_SBA, SST_SWM_OUT_SPEECH, sst_set_speech_path), + SST_PATH_OUTPUT("rxspeech_out", SST_TASK_SBA, SST_SWM_OUT_RXSPEECH, sst_set_speech_path), + + /* Media Mixers */ + SST_SWM_MIXER("media0_out mix 0", SST_MIX_MEDIA0, SST_TASK_MMX, SST_SWM_OUT_MEDIA0, + sst_mix_media0_controls, sst_swm_mixer_event), + SST_SWM_MIXER("media1_out mix 0", SST_MIX_MEDIA1, SST_TASK_MMX, SST_SWM_OUT_MEDIA1, + sst_mix_media1_controls, sst_swm_mixer_event), + + /* SBA PCM mixers */ + SST_SWM_MIXER("pcm0_out mix 0", SST_MIX_PCM0, SST_TASK_SBA, SST_SWM_OUT_PCM0, + sst_mix_pcm0_controls, sst_swm_mixer_event), + SST_SWM_MIXER("pcm1_out mix 0", SST_MIX_PCM1, SST_TASK_SBA, SST_SWM_OUT_PCM1, + sst_mix_pcm1_controls, sst_swm_mixer_event), + SST_SWM_MIXER("pcm2_out mix 0", SST_MIX_PCM2, SST_TASK_SBA, SST_SWM_OUT_PCM2, + sst_mix_pcm2_controls, sst_swm_mixer_event), + + /* SBA Loop mixers */ + SST_SWM_MIXER("sprot_loop_out mix 0", SST_MIX_LOOP0, SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, + sst_mix_sprot_l0_controls, sst_swm_mixer_event), + SST_SWM_MIXER("media_loop1_out mix 0", SST_MIX_LOOP1, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, + sst_mix_media_l1_controls, sst_swm_mixer_event), + SST_SWM_MIXER("media_loop2_out mix 0", SST_MIX_LOOP2, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, + sst_mix_media_l2_controls, sst_swm_mixer_event), + + SST_SWM_MIXER("voip_out mix 0", SST_MIX_VOIP, SST_TASK_SBA, SST_SWM_OUT_VOIP, + sst_mix_voip_controls, sst_swm_mixer_event), + SST_SWM_MIXER("aware_out mix 0", SST_MIX_AWARE, SST_TASK_SBA, SST_SWM_OUT_AWARE, + sst_mix_aware_controls, sst_swm_mixer_event), + SST_SWM_MIXER("vad_out mix 0", SST_MIX_VAD, SST_TASK_SBA, SST_SWM_OUT_VAD, + sst_mix_vad_controls, sst_swm_mixer_event), + + /* SBA Voice mixers */ + SST_SWM_MIXER("hf_sns_out mix 0", SST_MIX_HF_SNS, SST_TASK_SBA, SST_SWM_OUT_HF_SNS, + sst_mix_hf_sns_controls, sst_swm_mixer_event), + SST_SWM_MIXER("hf_out mix 0", SST_MIX_HF, SST_TASK_SBA, SST_SWM_OUT_HF, + sst_mix_hf_controls, sst_swm_mixer_event), + SST_SWM_MIXER("speech_out mix 0", SST_MIX_SPEECH, SST_TASK_SBA, SST_SWM_OUT_SPEECH, + sst_mix_speech_controls, sst_swm_mixer_event), + SST_SWM_MIXER("rxspeech_out mix 0", SST_MIX_RXSPEECH, SST_TASK_SBA, SST_SWM_OUT_RXSPEECH, + sst_mix_rxspeech_controls, sst_swm_mixer_event), + + /* SBA Backend mixers */ + SST_SWM_MIXER("codec_out0 mix 0", SST_MIX_CODEC0, SST_TASK_SBA, SST_SWM_OUT_CODEC0, + sst_mix_codec0_controls, sst_swm_mixer_event), + SST_SWM_MIXER("codec_out1 mix 0", SST_MIX_CODEC1, SST_TASK_SBA, SST_SWM_OUT_CODEC1, + sst_mix_codec1_controls, sst_swm_mixer_event), + SST_SWM_MIXER("bt_out mix 0", SST_MIX_BT, SST_TASK_SBA, SST_SWM_OUT_BT, + sst_mix_bt_controls, sst_swm_mixer_event), + SST_SWM_MIXER("fm_out mix 0", SST_MIX_FM, SST_TASK_SBA, SST_SWM_OUT_FM, + sst_mix_fm_controls, sst_swm_mixer_event), + SST_SWM_MIXER("modem_out mix 0", SST_MIX_MODEM, SST_TASK_SBA, SST_SWM_OUT_MODEM, + sst_mix_modem_controls, sst_swm_mixer_event), + + SND_SOC_DAPM_SWITCH("aware_out aware 0", SND_SOC_NOPM, 0, 0, &sst_mix_sw_aware), + SND_SOC_DAPM_MUX("ssp1_out mux 0", SND_SOC_NOPM, 0, 0, &sst_bt_fm_mux), + + SND_SOC_DAPM_SUPPLY("VBTimer", SND_SOC_NOPM, 0, 0, + sst_vb_trigger_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route intercon[] = { + {"media0_in", NULL, "Compress Playback"}, + {"media1_in", NULL, "Headset Playback"}, + {"media2_in", NULL, "pcm0_out"}, + {"media3_in", NULL, "Deepbuffer Playback"}, + + {"media0_out mix 0", "media0_in", "media0_in"}, + {"media0_out mix 0", "media1_in", "media1_in"}, + {"media0_out mix 0", "media2_in", "media2_in"}, + {"media0_out mix 0", "media3_in", "media3_in"}, + {"media1_out mix 0", "media0_in", "media0_in"}, + {"media1_out mix 0", "media1_in", "media1_in"}, + {"media1_out mix 0", "media2_in", "media2_in"}, + {"media1_out mix 0", "media3_in", "media3_in"}, + + {"media0_out", NULL, "media0_out mix 0"}, + {"media1_out", NULL, "media1_out mix 0"}, + {"pcm0_in", NULL, "media0_out"}, + {"pcm1_in", NULL, "media1_out"}, + + {"Headset Capture", NULL, "pcm1_out"}, + {"Headset Capture", NULL, "pcm2_out"}, + {"pcm0_out", NULL, "pcm0_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("pcm0_out mix 0"), + {"pcm1_out", NULL, "pcm1_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("pcm1_out mix 0"), + {"pcm2_out", NULL, "pcm2_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("pcm2_out mix 0"), + + {"media_loop1_in", NULL, "media_loop1_out"}, + {"media_loop1_out", NULL, "media_loop1_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("media_loop1_out mix 0"), + {"media_loop2_in", NULL, "media_loop2_out"}, + {"media_loop2_out", NULL, "media_loop2_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("media_loop2_out mix 0"), + {"sprot_loop_in", NULL, "sprot_loop_out"}, + {"sprot_loop_out", NULL, "sprot_loop_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("sprot_loop_out mix 0"), + + {"voip_in", NULL, "VOIP Playback"}, + {"VOIP Capture", NULL, "voip_out"}, + {"voip_out", NULL, "voip_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("voip_out mix 0"), + + {"aware", NULL, "aware_out"}, + {"aware_out", NULL, "aware_out aware 0"}, + {"aware_out aware 0", "switch", "aware_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("aware_out mix 0"), + {"vad", NULL, "vad_out"}, + {"vad_out", NULL, "vad_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("vad_out mix 0"), + + {"codec_out0", NULL, "codec_out0 mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("codec_out0 mix 0"), + {"codec_out1", NULL, "codec_out1 mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("codec_out1 mix 0"), + {"modem_out", NULL, "modem_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("modem_out mix 0"), + + {"bt_fm_out", NULL, "ssp1_out mux 0"}, + {"ssp1_out mux 0", "bt", "bt_out"}, + {"ssp1_out mux 0", "fm", "fm_out"}, + {"bt_out", NULL, "bt_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("bt_out mix 0"), + {"fm_out", NULL, "fm_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("fm_out mix 0"), + {"bt_in", NULL, "bt_fm_in"}, + {"fm_in", NULL, "bt_fm_in"}, + + /* Uplink processing */ + {"txspeech_in", NULL, "hf_sns_out"}, + {"txspeech_in", NULL, "hf_out"}, + {"txspeech_in", NULL, "speech_out"}, + + {"hf_sns_out", NULL, "hf_sns_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("hf_sns_out mix 0"), + {"hf_out", NULL, "hf_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("hf_out mix 0"), + {"speech_out", NULL, "speech_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("speech_out mix 0"), + + /* Downlink processing */ + {"speech_in", NULL, "rxspeech_out"}, + {"rxspeech_out", NULL, "rxspeech_out mix 0"}, + SST_SBA_MIXER_GRAPH_MAP("rxspeech_out mix 0"), + + /* TODO: add Tone inputs */ + /* TODO: add Low Latency stream support */ + + {"Headset Capture", NULL, "VBTimer"}, + {"Headset Playback", NULL, "VBTimer"}, + {"Deepbuffer Playback", NULL, "VBTimer"}, + {"Compress Playback", NULL, "VBTimer"}, + {"VOIP Playback", NULL, "VBTimer"}, + {"aware", NULL, "VBTimer"}, + {"modem_in", NULL, "VBTimer"}, + {"modem_out", NULL, "VBTimer"}, + {"bt_fm_in", NULL, "VBTimer"}, + {"bt_fm_out", NULL, "VBTimer"}, +}; + +static const char * const sst_nb_wb_texts[] = { + "narrowband", "wideband", +}; + +static const struct snd_kcontrol_new sst_mux_controls[] = { + SST_SSP_MUX_CTL("domain voice mode", 0, SST_MUX_REG, SST_VOICE_MODE_SHIFT, sst_nb_wb_texts, + sst_mode_get, sst_mode_put), + SST_SSP_MUX_CTL("domain bt mode", 0, SST_MUX_REG, SST_BT_MODE_SHIFT, sst_nb_wb_texts, + sst_mode_get, sst_mode_put), +}; + +static const char * const slot_names[] = { + "none", + "slot 0", "slot 1", "slot 2", "slot 3", + "slot 4", "slot 5", "slot 6", "slot 7", /* not supported by FW */ +}; + +static const char * const channel_names[] = { + "none", + "codec_out0_0", "codec_out0_1", "codec_out1_0", "codec_out1_1", + "codec_out2_0", "codec_out2_1", "codec_out3_0", "codec_out3_1", /* not supported by FW */ +}; + +#define SST_INTERLEAVER(xpname, slot_name, slotno) \ + SST_SSP_SLOT_CTL(xpname, "interleaver", slot_name, slotno, 1, \ + channel_names, sst_slot_get, sst_slot_put) + +#define SST_DEINTERLEAVER(xpname, channel_name, channel_no) \ + SST_SSP_SLOT_CTL(xpname, "deinterleaver", channel_name, channel_no, 0, \ + slot_names, sst_slot_get, sst_slot_put) + +static const struct snd_kcontrol_new sst_slot_controls[] = { + SST_INTERLEAVER("codec_out", "slot 0", 0), + SST_INTERLEAVER("codec_out", "slot 1", 1), + SST_INTERLEAVER("codec_out", "slot 2", 2), + SST_INTERLEAVER("codec_out", "slot 3", 3), + SST_DEINTERLEAVER("codec_in", "codec_in0_0", 0), + SST_DEINTERLEAVER("codec_in", "codec_in0_1", 1), + SST_DEINTERLEAVER("codec_in", "codec_in1_0", 2), + SST_DEINTERLEAVER("codec_in", "codec_in1_1", 3), +}; + +#define SST_NUM_PROBE_CONNECTION_PTS 31 +static const struct sst_probe_config sst_probes[SST_NUM_PROBE_CONNECTION_PTS] = { + /* TODO: get this struct from FW config data */ + /* TODO: only gain outputs supported currently */ + { "media0_in gain", SST_PATH_INDEX_MEDIA0_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_MMX, { 1, 2, 1 } }, + { "media1_in gain", SST_PATH_INDEX_MEDIA1_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_MMX, { 1, 2, 1 } }, + { "media2_in gain", SST_PATH_INDEX_MEDIA2_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_MMX, { 1, 2, 1 } }, + { "media3_in gain", SST_PATH_INDEX_MEDIA3_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_MMX, { 1, 2, 1 } }, + { "pcm0_in gain", SST_PATH_INDEX_PCM0_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "pcm1_in gain", SST_PATH_INDEX_PCM1_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "pcm1_out gain", SST_PATH_INDEX_PCM1_OUT, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "pcm2_out gain", SST_PATH_INDEX_PCM2_OUT, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "voip_in gain", SST_PATH_INDEX_VOIP_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "voip_out gain", SST_PATH_INDEX_VOIP_OUT, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "aware_out gain", SST_PATH_INDEX_AWARE_OUT, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "vad_out gain", SST_PATH_INDEX_VAD_OUT, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "hf_sns_out gain", SST_PATH_INDEX_HF_SNS_OUT, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "hf_out gain", SST_PATH_INDEX_HF_OUT, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "speech_out gain", SST_PATH_INDEX_SPEECH_OUT, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "txspeech_in gain", SST_PATH_INDEX_TX_SPEECH_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "rxspeech_out gain", SST_PATH_INDEX_RX_SPEECH_OUT, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "speech_in gain", SST_PATH_INDEX_SPEECH_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "media_loop1_out gain", SST_PATH_INDEX_MEDIA_LOOP1_OUT , SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "media_loop2_out gain", SST_PATH_INDEX_MEDIA_LOOP2_OUT , SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "tone_in gain", SST_PATH_INDEX_TONE_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "codec_out0 gain", SST_PATH_INDEX_CODEC_OUT0, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "codec_out1 gain", SST_PATH_INDEX_CODEC_OUT1, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "bt_out gain", SST_PATH_INDEX_BT_OUT, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "fm_out gain", SST_PATH_INDEX_FM_OUT, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "modem_out gain", SST_PATH_INDEX_MODEM_OUT, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "codec_in0 gain", SST_PATH_INDEX_CODEC_IN0, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "codec_in1 gain", SST_PATH_INDEX_CODEC_IN1, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "bt_in gain", SST_PATH_INDEX_BT_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "fm_in gain", SST_PATH_INDEX_FM_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, + { "modem_in gain", SST_PATH_INDEX_MODEM_IN, SST_MODULE_ID_GAIN_CELL, SST_TASK_SBA, { 1, 2, 1 } }, +}; + +/* initialized based on names in sst_probes array */ +static const char *sst_probe_enum_texts[SST_NUM_PROBE_CONNECTION_PTS]; +static const SOC_ENUM_SINGLE_EXT_DECL(sst_probe_enum, sst_probe_enum_texts); + +#define SST_PROBE_CTL(name, num) \ + SST_PROBE_ENUM(SST_PROBE_CTL_NAME(name, num, "connection"), \ + sst_probe_enum, sst_probe_get, sst_probe_put) + /* TODO: implement probe gains + SOC_SINGLE_EXT_TLV(SST_PROBE_CTL_NAME(name, num, "gains"), xreg, xshift, + xmax, xinv, xget, xput, sst_gain_tlv_common) + */ + +static const struct snd_kcontrol_new sst_probe_controls[] = { + SST_PROBE_CTL("probe out", 0), + SST_PROBE_CTL("probe out", 1), + SST_PROBE_CTL("probe out", 2), + SST_PROBE_CTL("probe out", 3), + SST_PROBE_CTL("probe out", 4), + SST_PROBE_CTL("probe out", 5), + SST_PROBE_CTL("probe out", 6), + SST_PROBE_CTL("probe out", 7), + SST_PROBE_CTL("probe in", 0), + SST_PROBE_CTL("probe in", 1), + SST_PROBE_CTL("probe in", 2), + SST_PROBE_CTL("probe in", 3), + SST_PROBE_CTL("probe in", 4), + SST_PROBE_CTL("probe in", 5), + SST_PROBE_CTL("probe in", 6), + SST_PROBE_CTL("probe in", 7), +}; + +/* Gain helper with min/max set */ +#define SST_GAIN(name, path_id, task_id, instance, gain_var) \ + SST_GAIN_KCONTROLS(name, "gain", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE, \ + SST_GAIN_TC_MIN, SST_GAIN_TC_MAX, \ + sst_gain_get, sst_gain_put, \ + SST_MODULE_ID_GAIN_CELL, path_id, instance, task_id, \ + sst_gain_tlv_common, gain_var) + +#define SST_VOLUME(name, path_id, task_id, instance, gain_var) \ + SST_GAIN_KCONTROLS(name, "volume", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE, \ + SST_GAIN_TC_MIN, SST_GAIN_TC_MAX, \ + sst_gain_get, sst_gain_put, \ + SST_MODULE_ID_VOLUME, path_id, instance, task_id, \ + sst_gain_tlv_common, gain_var) + +#define SST_NUM_GAINS 36 +static struct sst_gain_value sst_gains[SST_NUM_GAINS]; + +static const struct snd_kcontrol_new sst_gain_controls[] = { + SST_GAIN("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[0]), + SST_GAIN("media1_in", SST_PATH_INDEX_MEDIA1_IN, SST_TASK_MMX, 0, &sst_gains[1]), + SST_GAIN("media2_in", SST_PATH_INDEX_MEDIA2_IN, SST_TASK_MMX, 0, &sst_gains[2]), + SST_GAIN("media3_in", SST_PATH_INDEX_MEDIA3_IN, SST_TASK_MMX, 0, &sst_gains[3]), + + SST_GAIN("pcm0_in", SST_PATH_INDEX_PCM0_IN, SST_TASK_SBA, 0, &sst_gains[4]), + SST_GAIN("pcm1_in", SST_PATH_INDEX_PCM1_IN, SST_TASK_SBA, 0, &sst_gains[5]), + SST_GAIN("low_pcm0_in", SST_PATH_INDEX_LOW_PCM0_IN, SST_TASK_SBA, 0, &sst_gains[6]), + SST_GAIN("pcm1_out", SST_PATH_INDEX_PCM1_OUT, SST_TASK_SBA, 0, &sst_gains[7]), + SST_GAIN("pcm2_out", SST_PATH_INDEX_PCM1_OUT, SST_TASK_SBA, 0, &sst_gains[8]), + + SST_GAIN("voip_in", SST_PATH_INDEX_VOIP_IN, SST_TASK_SBA, 0, &sst_gains[9]), + SST_GAIN("voip_out", SST_PATH_INDEX_VOIP_OUT, SST_TASK_SBA, 0, &sst_gains[10]), + SST_GAIN("tone_in", SST_PATH_INDEX_TONE_IN, SST_TASK_SBA, 0, &sst_gains[11]), + + SST_GAIN("aware_out", SST_PATH_INDEX_AWARE_OUT, SST_TASK_SBA, 0, &sst_gains[12]), + SST_GAIN("vad_out", SST_PATH_INDEX_VAD_OUT, SST_TASK_SBA, 0, &sst_gains[13]), + + SST_GAIN("hf_sns_out", SST_PATH_INDEX_HF_SNS_OUT, SST_TASK_SBA, 0, &sst_gains[14]), + SST_GAIN("hf_out", SST_PATH_INDEX_HF_OUT, SST_TASK_SBA, 0, &sst_gains[15]), + SST_GAIN("speech_out", SST_PATH_INDEX_SPEECH_OUT, SST_TASK_SBA, 0, &sst_gains[16]), + SST_GAIN("txspeech_in", SST_PATH_INDEX_TX_SPEECH_IN, SST_TASK_SBA, 0, &sst_gains[17]), + SST_GAIN("rxspeech_out", SST_PATH_INDEX_RX_SPEECH_OUT, SST_TASK_SBA, 0, &sst_gains[18]), + SST_GAIN("speech_in", SST_PATH_INDEX_SPEECH_IN, SST_TASK_SBA, 0, &sst_gains[19]), + + SST_GAIN("codec_in0", SST_PATH_INDEX_CODEC_IN0, SST_TASK_SBA, 0, &sst_gains[20]), + SST_GAIN("codec_in1", SST_PATH_INDEX_CODEC_IN1, SST_TASK_SBA, 0, &sst_gains[21]), + SST_GAIN("codec_out0", SST_PATH_INDEX_CODEC_OUT0, SST_TASK_SBA, 0, &sst_gains[22]), + SST_GAIN("codec_out1", SST_PATH_INDEX_CODEC_OUT1, SST_TASK_SBA, 0, &sst_gains[23]), + SST_GAIN("bt_out", SST_PATH_INDEX_BT_OUT, SST_TASK_SBA, 0, &sst_gains[24]), + SST_GAIN("fm_out", SST_PATH_INDEX_FM_OUT, SST_TASK_SBA, 0, &sst_gains[25]), + SST_GAIN("bt_in", SST_PATH_INDEX_BT_IN, SST_TASK_SBA, 0, &sst_gains[26]), + SST_GAIN("fm_in", SST_PATH_INDEX_FM_IN, SST_TASK_SBA, 0, &sst_gains[27]), + SST_GAIN("modem_in", SST_PATH_INDEX_MODEM_IN, SST_TASK_SBA, 0, &sst_gains[28]), + SST_GAIN("modem_out", SST_PATH_INDEX_MODEM_OUT, SST_TASK_SBA, 0, &sst_gains[29]), + SST_GAIN("media_loop1_out", SST_PATH_INDEX_MEDIA_LOOP1_OUT, SST_TASK_SBA, 0, &sst_gains[30]), + SST_GAIN("media_loop2_out", SST_PATH_INDEX_MEDIA_LOOP2_OUT, SST_TASK_SBA, 0, &sst_gains[31]), + SST_GAIN("sprot_loop_out", SST_PATH_INDEX_SPROT_LOOP_OUT, SST_TASK_SBA, 0, &sst_gains[32]), + SST_VOLUME("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[33]), + SST_GAIN("sidetone_in", SST_PATH_INDEX_SIDETONE_IN, SST_TASK_SBA, 0, &sst_gains[34]), + SST_GAIN("speech_out", SST_PATH_INDEX_SPEECH_OUT, SST_TASK_FBA_UL, 1, &sst_gains[35]), +}; + +static const struct snd_kcontrol_new sst_algo_controls[] = { + SST_ALGO_KCONTROL_BYTES("media_loop1_out", "fir", 138, SST_MODULE_ID_FIR_24, + SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR), + SST_ALGO_KCONTROL_BYTES("media_loop1_out", "iir", 300, SST_MODULE_ID_IIR_24, + SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_IIR), + SST_ALGO_KCONTROL_BYTES("media_loop1_out", "mdrp", 76, SST_MODULE_ID_MDRP, + SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_SET_MDRP), + SST_ALGO_KCONTROL_BYTES("media_loop2_out", "fir", 272, SST_MODULE_ID_FIR_24, + SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR), + SST_ALGO_KCONTROL_BYTES("media_loop2_out", "iir", 300, SST_MODULE_ID_IIR_24, + SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_VB_SET_IIR), + SST_ALGO_KCONTROL_BYTES("media_loop2_out", "mdrp", 76, SST_MODULE_ID_MDRP, + SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_SET_MDRP), + SST_ALGO_KCONTROL_BYTES("aware_out", "fir", 272, SST_MODULE_ID_FIR_24, + SST_PATH_INDEX_AWARE_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR), + SST_ALGO_KCONTROL_BYTES("aware_out", "iir", 300, SST_MODULE_ID_IIR_24, + SST_PATH_INDEX_AWARE_OUT, 0, SST_TASK_SBA, SBA_VB_SET_IIR), + SST_ALGO_KCONTROL_BYTES("aware_out", "aware", 48, SST_MODULE_ID_CONTEXT_ALGO_AWARE, + SST_PATH_INDEX_AWARE_OUT, 0, SST_TASK_AWARE, AWARE_ENV_CLASS_PARAMS), + SST_ALGO_KCONTROL_BYTES("vad_out", "fir", 272, SST_MODULE_ID_FIR_24, + SST_PATH_INDEX_VAD_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR), + SST_ALGO_KCONTROL_BYTES("vad_out", "iir", 300, SST_MODULE_ID_IIR_24, + SST_PATH_INDEX_VAD_OUT, 0, SST_TASK_SBA, SBA_VB_SET_IIR), + SST_ALGO_KCONTROL_BYTES("sprot_loop_out", "lpro", 192, SST_MODULE_ID_SPROT, + SST_PATH_INDEX_SPROT_LOOP_OUT, 0, SST_TASK_SBA, SBA_VB_LPRO), + SST_ALGO_KCONTROL_BYTES("codec_in0", "dcr", 300, SST_MODULE_ID_FILT_DCR, + SST_PATH_INDEX_CODEC_IN0, 0, SST_TASK_SBA, SBA_VB_SET_IIR), + SST_ALGO_KCONTROL_BYTES("codec_in1", "dcr", 300, SST_MODULE_ID_FILT_DCR, + SST_PATH_INDEX_CODEC_IN1, 0, SST_TASK_SBA, SBA_VB_SET_IIR), + /* Uplink */ + SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "fir_speech", 136, SST_MODULE_ID_FIR_16, + SST_PATH_INDEX_SPEECH_OUT, 0, SST_TASK_FBA_UL, FBA_VB_SET_FIR), + SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "fir_hf_sns", 136, SST_MODULE_ID_FIR_16, + SST_PATH_INDEX_HF_SNS_OUT, 0, SST_TASK_FBA_UL, FBA_VB_SET_FIR), + SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "iir_speech", 48, SST_MODULE_ID_IIR_16, + SST_PATH_INDEX_SPEECH_OUT, 0, SST_TASK_FBA_UL, FBA_VB_SET_IIR), + SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "iir_hf_sns", 48, SST_MODULE_ID_IIR_16, + SST_PATH_INDEX_HF_SNS_OUT, 0, SST_TASK_FBA_UL, FBA_VB_SET_IIR), + SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "aec", 640, SST_MODULE_ID_AEC, + SST_PATH_INDEX_SPEECH_OUT, 0, SST_TASK_FBA_UL, FBA_VB_AEC), + SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "nr", 38, SST_MODULE_ID_NR, + SST_PATH_INDEX_SPEECH_OUT, 0, SST_TASK_FBA_UL, FBA_VB_NR_UL), + SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "agc", 58, SST_MODULE_ID_AGC, + SST_PATH_INDEX_SPEECH_OUT, 0, SST_TASK_FBA_UL, FBA_VB_AGC), + SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "biquad", 22, SST_MODULE_ID_DRP, + SST_PATH_INDEX_SPEECH_OUT, 0, SST_TASK_FBA_UL, FBA_VB_SET_BIQUAD_D_C), + SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "compr", 36, SST_MODULE_ID_DRP, + SST_PATH_INDEX_SPEECH_OUT, 0, SST_TASK_FBA_UL, FBA_VB_DUAL_BAND_COMP), + SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "sns", 324, SST_MODULE_ID_NR_SNS, + SST_PATH_INDEX_SPEECH_OUT, 0, SST_TASK_FBA_UL, FBA_VB_SNS), + SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "ser", 42, SST_MODULE_ID_SER, + SST_PATH_INDEX_SPEECH_OUT, 0, SST_TASK_FBA_UL, FBA_VB_SER), + SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "cni", 48, SST_MODULE_ID_CNI_TX, + SST_PATH_INDEX_SPEECH_OUT, 0, SST_TASK_FBA_UL, FBA_VB_TX_CNI), + SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "ref", 24, SST_MODULE_ID_REF_LINE, + SST_PATH_INDEX_HF_OUT, 0, SST_TASK_FBA_UL, FBA_VB_SET_REF_LINE), + SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "delay", 6, SST_MODULE_ID_EDL, + SST_PATH_INDEX_HF_OUT, 0, SST_TASK_FBA_UL, FBA_VB_SET_DELAY_LINE), + SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "bmf", 264, SST_MODULE_ID_BMF, + SST_PATH_INDEX_HF_SNS_OUT, 0, SST_TASK_FBA_UL, FBA_VB_BMF), + SST_COMBO_ALGO_KCONTROL_BYTES("speech_out", "ul_module", "dnr", 18, SST_MODULE_ID_DNR, + SST_PATH_INDEX_SPEECH_OUT, 0, SST_TASK_FBA_UL, FBA_VB_DNR), + /* Downlink */ + SST_COMBO_ALGO_KCONTROL_BYTES("speech_in", "dl_module", "ana", 52, SST_MODULE_ID_ANA, + SST_PATH_INDEX_SPEECH_IN, 0, SST_TASK_FBA_DL, FBA_VB_ANA), + SST_COMBO_ALGO_KCONTROL_BYTES("speech_in", "dl_module", "fir", 136, SST_MODULE_ID_FIR_16, + SST_PATH_INDEX_SPEECH_IN, 0, SST_TASK_FBA_DL, FBA_VB_SET_FIR), + SST_COMBO_ALGO_KCONTROL_BYTES("speech_in", "dl_module", "iir", 48, SST_MODULE_ID_IIR_16, + SST_PATH_INDEX_SPEECH_IN, 0, SST_TASK_FBA_DL, FBA_VB_SET_IIR), + SST_COMBO_ALGO_KCONTROL_BYTES("speech_in", "dl_module", "nr", 38, SST_MODULE_ID_NR, + SST_PATH_INDEX_SPEECH_IN, 0, SST_TASK_FBA_DL, FBA_VB_NR_DL), + SST_COMBO_ALGO_KCONTROL_BYTES("speech_in", "dl_module", "biquad", 22, SST_MODULE_ID_DRP, + SST_PATH_INDEX_SPEECH_IN, 0, SST_TASK_FBA_DL, FBA_VB_SET_BIQUAD_D_C), + SST_COMBO_ALGO_KCONTROL_BYTES("speech_in", "dl_module", "compr", 36, SST_MODULE_ID_DRP, + SST_PATH_INDEX_SPEECH_IN, 0, SST_TASK_FBA_DL, FBA_VB_DUAL_BAND_COMP), + SST_COMBO_ALGO_KCONTROL_BYTES("speech_in", "dl_module", "cni", 48, SST_MODULE_ID_CNI, + SST_PATH_INDEX_SPEECH_IN, 0, SST_TASK_FBA_DL, FBA_VB_RX_CNI), + SST_COMBO_ALGO_KCONTROL_BYTES("speech_in", "dl_module", "bwx", 54, SST_MODULE_ID_BWX, + SST_PATH_INDEX_SPEECH_IN, 0, SST_TASK_FBA_DL, FBA_VB_BWX), + SST_COMBO_ALGO_KCONTROL_BYTES("speech_in", "dl_module", "gmm", 586, SST_MODULE_ID_BWX, + SST_PATH_INDEX_SPEECH_IN, 0, SST_TASK_FBA_DL, FBA_VB_GMM), + SST_COMBO_ALGO_KCONTROL_BYTES("speech_in", "dl_module", "glc", 18, SST_MODULE_ID_GLC, + SST_PATH_INDEX_SPEECH_IN, 0, SST_TASK_FBA_DL, FBA_VB_GLC), +}; + +static const struct snd_kcontrol_new sst_debug_controls[] = { + SND_SOC_BYTES_EXT("sst debug byte control", SST_MAX_BIN_BYTES, + sst_byte_control_get, sst_byte_control_set), +}; + +static inline bool is_sst_dapm_widget(struct snd_soc_dapm_widget *w) +{ + if ((w->id == snd_soc_dapm_pga) || + (w->id == snd_soc_dapm_aif_in) || + (w->id == snd_soc_dapm_aif_out) || + (w->id == snd_soc_dapm_input) || + (w->id == snd_soc_dapm_output) || + (w->id == snd_soc_dapm_mixer)) + return true; + else + return false; +} + +int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute) +{ + struct snd_soc_platform *platform = dai->platform; + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_path *p = NULL; + + pr_debug("%s: enter, dai-name=%s dir=%d\n", __func__, dai->name, stream); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + pr_debug("Stream name=%s\n", dai->playback_widget->name); + w = dai->playback_widget; + list_for_each_entry(p, &w->sinks, list_source) { + if (p->connected && !p->connected(w, p->sink)) + continue; + + if (p->connect && p->sink->power && is_sst_dapm_widget(p->sink)) { + struct sst_ids *ids = p->sink->priv; + + pr_debug("send gains for widget=%s\n", p->sink->name); + sst_set_pipe_gain(ids, sst, mute); + } + } + } else { + pr_debug("Stream name=%s\n", dai->capture_widget->name); + w = dai->capture_widget; + list_for_each_entry(p, &w->sources, list_sink) { + if (p->connected && !p->connected(w, p->sink)) + continue; + + if (p->connect && p->source->power && is_sst_dapm_widget(p->source)) { + struct sst_ids *ids = p->source->priv; + + pr_debug("send gain for widget=%s\n", p->source->name); + sst_set_pipe_gain(ids, sst, mute); + } + } + } + return 0; +} + +static int sst_fill_module_list(struct snd_kcontrol *kctl, + struct snd_soc_dapm_widget *w, int type) +{ + struct module *module = NULL; + struct sst_ids *ids = w->priv; + + module = devm_kzalloc(w->platform->dev, sizeof(*module), GFP_KERNEL); + if (!module) { + pr_err("kzalloc block failed\n"); + return -ENOMEM; + } + + if (type == SST_MODULE_GAIN) { + struct sst_gain_mixer_control *mc = (void *)kctl->private_value; + + mc->w = w; + module->kctl = kctl; + list_add_tail(&module->node, &ids->gain_list); + } else if (type == SST_MODULE_ALGO) { + struct sst_algo_control *bc = (void *)kctl->private_value; + + bc->w = w; + module->kctl = kctl; + list_add_tail(&module->node, &ids->algo_list); + } + + return 0; +} + +static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w, + struct snd_soc_platform *platform) +{ + struct snd_kcontrol *kctl; + int index, ret = 0; + struct snd_card *card = platform->card->snd_card; + char *idx; + + down_read(&card->controls_rwsem); + + list_for_each_entry(kctl, &card->controls, list) { + idx = strstr(kctl->id.name, " "); + if (idx == NULL) + continue; + index = strlen(kctl->id.name) - strlen(idx); + if (strstr(kctl->id.name, "volume") && + !strncmp(kctl->id.name, w->name, index)) + ret = sst_fill_module_list(kctl, w, SST_MODULE_GAIN); + else if (strstr(kctl->id.name, "params") && + !strncmp(kctl->id.name, w->name, index)) + ret = sst_fill_module_list(kctl, w, SST_MODULE_ALGO); + else if (strstr(kctl->id.name, "mute") && + !strncmp(kctl->id.name, w->name, index)) { + struct sst_gain_mixer_control *mc = (void *)kctl->private_value; + mc->w = w; + } + if (ret < 0) { + up_read(&card->controls_rwsem); + return ret; + } + } + up_read(&card->controls_rwsem); + return 0; +} + +static int sst_map_modules_to_pipe(struct snd_soc_platform *platform) +{ + struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_context *dapm = &platform->dapm; + int ret = 0; + + list_for_each_entry(w, &dapm->card->widgets, list) { + if (w->platform && is_sst_dapm_widget(w) && (w->priv)) { + struct sst_ids *ids = w->priv; + + pr_debug("widget type=%d name=%s", w->id, w->name); + INIT_LIST_HEAD(&ids->algo_list); + INIT_LIST_HEAD(&ids->gain_list); + ret = sst_fill_widget_module_info(w, platform); + if (ret < 0) + return ret; + } + } + return 0; +} + +int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform) +{ + int i, ret = 0; + struct sst_data *sst = snd_soc_platform_get_drvdata(platform); + + sst->byte_stream = devm_kzalloc(platform->dev, + SST_MAX_BIN_BYTES, GFP_KERNEL); + if (!sst->byte_stream) { + pr_err("%s: kzalloc failed\n", __func__); + return -ENOMEM; + } + sst->widget = devm_kzalloc(platform->dev, + SST_NUM_WIDGETS * sizeof(*sst->widget), + GFP_KERNEL); + if (!sst->widget) { + pr_err("%s: kzalloc failed\n", __func__); + return -ENOMEM; + } + + snd_soc_dapm_new_controls(&platform->dapm, sst_dapm_widgets, + ARRAY_SIZE(sst_dapm_widgets)); + snd_soc_dapm_add_routes(&platform->dapm, intercon, + ARRAY_SIZE(intercon)); + snd_soc_dapm_new_widgets(&platform->dapm); + + for (i = 0; i < SST_NUM_GAINS; i++) { + sst_gains[i].mute = SST_GAIN_MUTE_DEFAULT; + sst_gains[i].l_gain = SST_GAIN_VOLUME_DEFAULT; + sst_gains[i].r_gain = SST_GAIN_VOLUME_DEFAULT; + sst_gains[i].ramp_duration = SST_GAIN_RAMP_DURATION_DEFAULT; + } + + snd_soc_add_platform_controls(platform, sst_gain_controls, + ARRAY_SIZE(sst_gain_controls)); + + snd_soc_add_platform_controls(platform, sst_algo_controls, + ARRAY_SIZE(sst_algo_controls)); + snd_soc_add_platform_controls(platform, sst_slot_controls, + ARRAY_SIZE(sst_slot_controls)); + snd_soc_add_platform_controls(platform, sst_mux_controls, + ARRAY_SIZE(sst_mux_controls)); + + /* initialize the names of the probe points */ + for (i = 0; i < SST_NUM_PROBE_CONNECTION_PTS; i++) + sst_probe_enum_texts[i] = sst_probes[i].name; + + snd_soc_add_platform_controls(platform, sst_probe_controls, + ARRAY_SIZE(sst_probe_controls)); + + ret = sst_map_modules_to_pipe(platform); + + return ret; +} diff --git a/sound/soc/intel/platform-libs/ipc_lib.h b/sound/soc/intel/platform-libs/ipc_lib.h new file mode 100644 index 000000000000..6ba50c41d342 --- /dev/null +++ b/sound/soc/intel/platform-libs/ipc_lib.h @@ -0,0 +1,33 @@ +/* + * ipc_lib.h - Intel MID Platform driver header file + * + * Copyright (C) 2013 Intel Corp + * Author: Lakshmi N Vinnakota + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * + */ + +#ifndef __PLATFORMDRV_IPC_LIB_H__ +#define __PLATFORMDRV_IPC_LIB_H__ + +struct sst_algo_int_control_v2; + +void sst_create_compr_vol_ipc(char *bytes, unsigned int type, + struct sst_algo_int_control_v2 *kdata); +#endif diff --git a/sound/soc/intel/platform-libs/ipc_lib_v2.c b/sound/soc/intel/platform-libs/ipc_lib_v2.c new file mode 100644 index 000000000000..105c7560c1eb --- /dev/null +++ b/sound/soc/intel/platform-libs/ipc_lib_v2.c @@ -0,0 +1,109 @@ +/* + * ipc_lib_v2.c - Intel MID Platform Driver IPC wrappers for mrfld + * + * Copyright (C) 2013 Intel Corp + * Author: Lakshmi N Vinnakota + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * + */ +#include +#include +#include "../platform_ipc_v2.h" +#include "../sst_platform.h" +#include "../sst_platform_pvt.h" + + +static inline void sst_fill_dsp_hdr(struct ipc_dsp_hdr *hdr, u8 index, u8 pipe, + u16 module, u16 cmd, u16 len) +{ + hdr->mod_index_id = index; + hdr->pipe_id = pipe; + hdr->mod_id = module; + hdr->cmd_id = cmd; + hdr->length = len; + +} + +static inline void sst_fill_byte_control_hdr(struct snd_sst_bytes_v2 *hdr, + u8 type, u8 msg, u8 block, u8 task, u8 pipe, u16 len) +{ + hdr->type = type; + hdr->ipc_msg = msg; + hdr->block = block; + hdr->task_id = task; + hdr->pipe_id = pipe; + hdr->rsvd = 0; + hdr->len = len; +} + +#define SST_GAIN_V2_TIME_CONST 50 + +void sst_create_compr_vol_ipc(char *bytes, unsigned int type, + struct sst_algo_int_control_v2 *kdata) +{ + struct snd_sst_gain_v2 gain1; + struct snd_sst_bytes_v2 byte_hdr; + struct ipc_dsp_hdr dsp_hdr; + char *tmp; + u16 len; + u8 ipc_msg; + + /* Fill gain params */ + gain1.gain_cell_num = 1; /* num of gain cells to modify*/ + gain1.cell_nbr_idx = kdata->instance_id; /* instance index */ + gain1.cell_path_idx = kdata->pipe_id; /* pipe id */ + gain1.module_id = kdata->module_id; /*module id */ + gain1.left_cell_gain = kdata->value; /* left gain value in dB*/ + gain1.right_cell_gain = kdata->value; /* same value as left in dB*/ + /* set to default recommended value*/ + gain1.gain_time_const = SST_GAIN_V2_TIME_CONST; + + /* fill dsp header */ + /* Get params format for vol ctrl lib, size 6 bytes : + * u16 left_gain, u16 right_gain, u16 ramp + */ + memset(&dsp_hdr, 0, sizeof(dsp_hdr)); + if (type == SND_SST_BYTES_GET) { + len = 6; + ipc_msg = IPC_GET_PARAMS; + } else { + len = sizeof(gain1); + ipc_msg = IPC_SET_PARAMS; + } + + sst_fill_dsp_hdr(&dsp_hdr, 0, kdata->pipe_id, kdata->module_id, + IPC_IA_SET_GAIN_MRFLD, len); + + /* fill byte control header */ + memset(&byte_hdr, 0, sizeof(byte_hdr)); + len = sizeof(dsp_hdr) + dsp_hdr.length; + sst_fill_byte_control_hdr(&byte_hdr, type, ipc_msg, 1, + SST_TASK_ID_MEDIA, kdata->pipe_id, len); + + /* fill complete byte stream as ipc payload */ + tmp = bytes; + memcpy(tmp, &byte_hdr, sizeof(byte_hdr)); + memcpy((tmp + sizeof(byte_hdr)), &dsp_hdr, sizeof(dsp_hdr)); + if (type != SND_SST_BYTES_GET) + memcpy((tmp + sizeof(byte_hdr) + sizeof(dsp_hdr)), &gain1, + sizeof(gain1)); +#ifdef DEBUG_HEX_DUMP_BYTES + print_hex_dump_bytes(__func__, DUMP_PREFIX_NONE, bytes, 32); +#endif +} diff --git a/sound/soc/intel/platform-libs/sst_widgets.h b/sound/soc/intel/platform-libs/sst_widgets.h new file mode 100644 index 000000000000..d39ec8937ca3 --- /dev/null +++ b/sound/soc/intel/platform-libs/sst_widgets.h @@ -0,0 +1,337 @@ +/* + * sst_widgets.h - Intel helpers to generate FW widgets + * + * Copyright (C) 2013 Intel Corp + * Author: Omair Mohammed Abdullah + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#ifndef __SST_WIDGETS_H__ +#define __SST_WIDGETS_H__ + +#include +#include + +#define SST_MODULE_GAIN 1 +#define SST_MODULE_ALGO 2 + +#define SST_FMT_MONO 0 +#define SST_FMT_STEREO 3 + +/* physical SSP numbers */ +enum { + SST_SSP0 = 0, + SST_SSP1, + SST_SSP2, + SST_SSP_LAST = SST_SSP2, +}; + +#define SST_NUM_SSPS (SST_SSP_LAST + 1) /* physical SSPs */ +#define SST_MAX_SSP_MUX 2 /* single SSP muxed between pipes */ +#define SST_MAX_SSP_DOMAINS 2 /* domains present in each pipe */ + +struct module { + struct snd_kcontrol *kctl; + struct list_head node; +}; + +struct sst_ssp_config { + u8 ssp_id; + u8 bits_per_slot; + u8 slots; + u8 ssp_mode; + u8 pcm_mode; + u8 duplex; + u8 ssp_protocol; + u8 fs_frequency; + u8 active_slot_map; + u8 start_delay; + u16 fs_width; +}; + +struct sst_ssp_cfg { + const u8 ssp_number; + const int *mux_shift; + const int (*domain_shift)[SST_MAX_SSP_MUX]; + const struct sst_ssp_config (*ssp_config)[SST_MAX_SSP_MUX][SST_MAX_SSP_DOMAINS]; +}; + +struct sst_ids { + u16 location_id; + u16 module_id; + u8 task_id; + u8 format; + u8 reg; + struct list_head algo_list; + struct list_head gain_list; + const struct sst_ssp_cfg *ssp; +}; + +#define SST_SSP_AIF_IN(wname, wevent, wssp_cfg) \ +{ .id = snd_soc_dapm_aif_in, .name = wname, .sname = NULL, \ + .reg = SND_SOC_NOPM, .shift = 0, .invert = 0, \ + .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ + .priv = (void *)&(struct sst_ids) { .ssp = &wssp_cfg, } \ +} + +#define SST_SSP_AIF_OUT(wname, wevent, wssp_cfg) \ +{ .id = snd_soc_dapm_aif_out, .name = wname, .sname = NULL, \ + .reg = SND_SOC_NOPM, .shift = 0, .invert = 0, \ + .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ + .priv = (void *)&(struct sst_ids) { .ssp = &wssp_cfg, } \ +} + +#define SST_SSP_INPUT(wname, wevent, wssp_cfg) \ +{ .id = snd_soc_dapm_input, .name = wname, .sname = NULL, \ + .reg = SND_SOC_NOPM, .shift = 0, .invert = 0, \ + .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ + .priv = (void *)&(struct sst_ids) { .ssp = &wssp_cfg, } \ +} + +#define SST_SSP_OUTPUT(wname, wevent, wssp_cfg) \ +{ .id = snd_soc_dapm_output, .name = wname, .sname = NULL, \ + .reg = SND_SOC_NOPM, .shift = 0, .invert = 0, \ + .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ + .priv = (void *)&(struct sst_ids) { .ssp = &wssp_cfg, } \ +} + +#define SST_PATH(wname, wtask, wloc_id, wevent, wflags) \ +{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \ + .invert = 0, .kcontrol_news = NULL, .num_kcontrols = 0, \ + .event = wevent, .event_flags = wflags, \ + .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, } \ +} + +#define SST_PATH_MEDIA_LOOP(wname, wtask, wloc_id, wformat, wevent, wflags) \ +{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \ + .invert = 0, .kcontrol_news = NULL, .num_kcontrols = 0, \ + .event = wevent, .event_flags = wflags, \ + .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \ + .format = wformat,} \ +} + +/* output is triggered before input */ +#define SST_PATH_INPUT(name, task_id, loc_id, event) \ + SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD) + +#define SST_PATH_OUTPUT(name, task_id, loc_id, event) \ + SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD) + +#define SST_PATH_MEDIA_LOOP_OUTPUT(name, task_id, loc_id, format, event) \ + SST_PATH_MEDIA_LOOP(name, task_id, loc_id, format, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD) + + +#define SST_SWM_MIXER(wname, wreg, wtask, wloc_id, wcontrols, wevent) \ +{ .id = snd_soc_dapm_mixer, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \ + .invert = 0, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols),\ + .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD | \ + SND_SOC_DAPM_POST_REG, \ + .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \ + .reg = wreg } \ +} + +enum sst_gain_kcontrol_type { + SST_GAIN_TLV, + SST_GAIN_MUTE, + SST_GAIN_RAMP_DURATION, +}; + +struct sst_gain_mixer_control { + bool stereo; + enum sst_gain_kcontrol_type type; + struct sst_gain_value *gain_val; + int max; + int min; + u16 instance_id; + u16 module_id; + u16 pipe_id; + u16 task_id; + char pname[44]; + struct snd_soc_dapm_widget *w; +}; + +struct sst_gain_value { + u16 ramp_duration; + s16 l_gain; + s16 r_gain; + bool mute; +}; + +#define SST_GAIN_VOLUME_DEFAULT (-1440) +#define SST_GAIN_RAMP_DURATION_DEFAULT 5 /* timeconstant */ +#define SST_GAIN_MUTE_DEFAULT true + +#define SST_GAIN_KCONTROL_TLV(xname, xhandler_get, xhandler_put, \ + xmod, xpipe, xinstance, xtask, tlv_array, xgain_val, \ + xmin, xmax, xpname) \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = sst_gain_ctl_info,\ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct sst_gain_mixer_control) \ + { .stereo = true, .max = xmax, .min = xmin, .type = SST_GAIN_TLV, \ + .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\ + .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname} + +#define SST_GAIN_KCONTROL_INT(xname, xhandler_get, xhandler_put, \ + xmod, xpipe, xinstance, xtask, xtype, xgain_val, \ + xmin, xmax, xpname) \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = sst_gain_ctl_info, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct sst_gain_mixer_control) \ + { .stereo = false, .max = xmax, .min = xmin, .type = xtype, \ + .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\ + .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname} + +#define SST_GAIN_KCONTROL_BOOL(xname, xhandler_get, xhandler_put,\ + xmod, xpipe, xinstance, xtask, xgain_val, xpname) \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_bool_ext, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct sst_gain_mixer_control) \ + { .stereo = false, .type = SST_GAIN_MUTE, \ + .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\ + .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname} + +#define SST_CONTROL_NAME(xpname, xmname, xinstance, xtype) \ + xpname " " xmname " " #xinstance " " xtype + +#define SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, xtype, xsubmodule) \ + xpname " " xmname " " #xinstance " " xtype " " xsubmodule + +/* + * 3 Controls for each Gain module + * e.g. - pcm0_in gain 0 volume + * - pcm0_in gain 0 rampduration + * - pcm0_in gain 0 mute + */ +#define SST_GAIN_KCONTROLS(xpname, xmname, xmin_gain, xmax_gain, xmin_tc, xmax_tc, \ + xhandler_get, xhandler_put, \ + xmod, xpipe, xinstance, xtask, tlv_array, xgain_val) \ + { SST_GAIN_KCONTROL_INT(SST_CONTROL_NAME(xpname, xmname, xinstance, "rampduration"), \ + xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, SST_GAIN_RAMP_DURATION, \ + xgain_val, xmin_tc, xmax_tc, xpname) }, \ + { SST_GAIN_KCONTROL_BOOL(SST_CONTROL_NAME(xpname, xmname, xinstance, "mute"), \ + xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, \ + xgain_val, xpname) } ,\ + { SST_GAIN_KCONTROL_TLV(SST_CONTROL_NAME(xpname, xmname, xinstance, "volume"), \ + xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, tlv_array, \ + xgain_val, xmin_gain, xmax_gain, xpname) } + +#define SST_GAIN_TC_MIN 5 +#define SST_GAIN_TC_MAX 5000 +#define SST_GAIN_MIN_VALUE -1440 /* in 0.1 DB units */ +#define SST_GAIN_MAX_VALUE 360 + +enum sst_algo_kcontrol_type { + SST_ALGO_PARAMS, + SST_ALGO_BYPASS, +}; + +struct sst_algo_control { + enum sst_algo_kcontrol_type type; + int max; + u16 module_id; + u16 pipe_id; + u16 instance_id; + u16 task_id; + u16 cmd_id; + bool bypass; + unsigned char *params; + char pname[44]; + struct snd_soc_dapm_widget *w; +}; + +#define SST_ALGO_KCONTROL_BYTES(xpname, xmname, xcount, xmod, \ + xpipe, xinstance, xtask, xcmd) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\ + .name = SST_CONTROL_NAME(xpname, xmname, xinstance, "params"), \ + .info = sst_algo_bytes_ctl_info, \ + .get = sst_algo_control_get, .put = sst_algo_control_set, \ + .private_value = (unsigned long)&(struct sst_algo_control) \ + {.max = xcount, .type = SST_ALGO_PARAMS, .module_id = xmod, .pname = xpname, \ + .pipe_id = xpipe, .instance_id = xinstance, .task_id = xtask, .cmd_id = xcmd} } + +#define SST_ALGO_KCONTROL_BOOL(xpname, xmname, xmod, xpipe, xinstance, xtask) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = SST_CONTROL_NAME(xpname, xmname, xinstance, "bypass"), \ + .info = snd_soc_info_bool_ext, \ + .get = sst_algo_control_get, .put = sst_algo_control_set, \ + .private_value = (unsigned long)&(struct sst_algo_control) \ + {.type = SST_ALGO_BYPASS, .module_id = xmod, .pipe_id = xpipe, .pname = xpname, \ + .task_id = xtask, .instance_id = xinstance, .bypass = 0 } } + +#define SST_ALGO_BYPASS_PARAMS(xpname, xmname, xcount, xmod, xpipe, \ + xinstance, xtask, xcmd) \ + SST_ALGO_KCONTROL_BOOL(xpname, xmname, xmod, xpipe, xinstance, xtask), \ + SST_ALGO_KCONTROL_BYTES(xpname, xmname, xcount, xmod, xpipe, xinstance, xtask, xcmd) + +#define SST_COMBO_ALGO_KCONTROL_BYTES(xpname, xmname, xsubmod, xcount, xmod, \ + xpipe, xinstance, xtask, xcmd) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\ + .name = SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, "params", xsubmod), \ + .info = sst_algo_bytes_ctl_info, \ + .get = sst_algo_control_get, .put = sst_algo_control_set, \ + .private_value = (unsigned long)&(struct sst_algo_control) \ + {.max = xcount, .type = SST_ALGO_PARAMS, .module_id = xmod, .pname = xpname, \ + .pipe_id = xpipe, .instance_id = xinstance, .task_id = xtask, .cmd_id = xcmd} } + + +/* only 4 slots/channels supported atm */ +#define SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts) \ + (struct soc_enum){ .reg = s_ch_no, .reg2 = is_tx, .max = 4+1, .texts = xtexts, } + +#define SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name) \ + xpname " " xmname " " s_ch_name + +#define SST_SSP_SLOT_CTL(xpname, xmname, s_ch_name, s_ch_no, is_tx, xtexts, xget, xput) \ + SOC_DAPM_ENUM_EXT(SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name), \ + SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts), \ + xget, xput) + +#define SST_MUX_CTL_NAME(xpname, xinstance) \ + xpname " " #xinstance + +#define SST_SSP_MUX_ENUM(xreg, xshift, xtexts) \ + (struct soc_enum){ .reg = xreg, .texts = xtexts, .shift_l = xshift, \ + .shift_r = xshift, .max = ARRAY_SIZE(xtexts), } + +#define SST_SSP_MUX_CTL(xpname, xinstance, xreg, xshift, xtexts, xget, xput) \ + SOC_DAPM_ENUM_EXT(SST_MUX_CTL_NAME(xpname, xinstance), \ + SST_SSP_MUX_ENUM(xreg, xshift, xtexts), \ + xget, xput) + +struct sst_probe_value { + unsigned int val; + const struct soc_enum *p_enum; +}; + +#define SST_PROBE_CTL_NAME(dir, num, type) \ + dir #num " " type + +#define SST_PROBE_ENUM(xname, xenum, xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = sst_probe_enum_info, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct sst_probe_value) \ + { .val = 0, .p_enum = &xenum } } + +#endif diff --git a/sound/soc/intel/platform_ipc_v2.h b/sound/soc/intel/platform_ipc_v2.h new file mode 100644 index 000000000000..a5db16b8c6f8 --- /dev/null +++ b/sound/soc/intel/platform_ipc_v2.h @@ -0,0 +1,693 @@ +/* +* platform_ipc_v2.h - Intel MID Platform driver FW IPC definitions +* +* Copyright (C) 2008-10 Intel Corporation +* Author: Vinod Koul +* Harsha Priya +* Dharageswari R +* KP Jeeja +* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; version 2 of the License. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +* +* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* +* This driver exposes the audio engine functionalities to the ALSA +* and middleware. +* This file has definitions shared between the firmware and driver +*/ +#ifndef __PLATFORM_IPC_V2_H__ +#define __PLATFORM_IPC_V2_H__ + +#define MAX_DBG_RW_BYTES 80 +#define MAX_NUM_SCATTER_BUFFERS 8 +#define MAX_LOOP_BACK_DWORDS 8 +/* IPC base address and mailbox, timestamp offsets */ +#define SST_MAILBOX_SIZE 0x0400 +#define SST_MAILBOX_SEND 0x0000 +#define SST_TIME_STAMP 0x1800 +#define SST_TIME_STAMP_MRFLD 0x680 +#define SST_TIME_STAMP_BYT 0x800 +#define SST_RESERVED_OFFSET 0x1A00 +#define SST_SCU_LPE_MAILBOX 0x1000 +#define SST_LPE_SCU_MAILBOX 0x1400 +#define SST_SCU_LPE_LOG_BUF (SST_SCU_LPE_MAILBOX+16) +#define PROCESS_MSG 0x80 + +/* Message ID's for IPC messages */ +/* Bits B7: SST or IA/SC ; B6-B4: Msg Category; B3-B0: Msg Type */ + +/* I2L Firmware/Codec Download msgs */ +#define IPC_IA_PREP_LIB_DNLD 0x01 +#define IPC_IA_LIB_DNLD_CMPLT 0x02 +#define IPC_IA_GET_FW_VERSION 0x04 +#define IPC_IA_GET_FW_BUILD_INF 0x05 +#define IPC_IA_GET_FW_INFO 0x06 +#define IPC_IA_GET_FW_CTXT 0x07 +#define IPC_IA_SET_FW_CTXT 0x08 +#define IPC_IA_PREPARE_SHUTDOWN 0x31 +/* I2L Codec Config/control msgs */ +#define IPC_PREP_D3 0x10 +#define IPC_IA_SET_CODEC_PARAMS 0x10 +#define IPC_IA_GET_CODEC_PARAMS 0x11 +#define IPC_IA_SET_PPP_PARAMS 0x12 +#define IPC_IA_GET_PPP_PARAMS 0x13 +#define IPC_SST_PERIOD_ELAPSED_MRFLD 0xA +#define IPC_IA_ALG_PARAMS 0x1A +#define IPC_IA_TUNING_PARAMS 0x1B +#define IPC_IA_SET_RUNTIME_PARAMS 0x1C +#define IPC_IA_SET_PARAMS 0x1 +#define IPC_IA_GET_PARAMS 0x2 + +#define IPC_EFFECTS_CREATE 0xE +#define IPC_EFFECTS_DESTROY 0xF + +/* I2L Stream config/control msgs */ +#define IPC_IA_ALLOC_STREAM_MRFLD 0x2 +#define IPC_IA_ALLOC_STREAM 0x20 /* Allocate a stream ID */ +#define IPC_IA_FREE_STREAM_MRFLD 0x03 +#define IPC_IA_FREE_STREAM 0x21 /* Free the stream ID */ +#define IPC_IA_SET_STREAM_PARAMS 0x22 +#define IPC_IA_SET_STREAM_PARAMS_MRFLD 0x12 +#define IPC_IA_GET_STREAM_PARAMS 0x23 +#define IPC_IA_PAUSE_STREAM 0x24 +#define IPC_IA_PAUSE_STREAM_MRFLD 0x4 +#define IPC_IA_RESUME_STREAM 0x25 +#define IPC_IA_RESUME_STREAM_MRFLD 0x5 +#define IPC_IA_DROP_STREAM 0x26 +#define IPC_IA_DROP_STREAM_MRFLD 0x07 +#define IPC_IA_DRAIN_STREAM 0x27 /* Short msg with str_id */ +#define IPC_IA_DRAIN_STREAM_MRFLD 0x8 +#define IPC_IA_CONTROL_ROUTING 0x29 +#define IPC_IA_VTSV_UPDATE_MODULES 0x20 +#define IPC_IA_VTSV_DETECTED 0x21 + +#define IPC_IA_START_STREAM_MRFLD 0X06 +#define IPC_IA_START_STREAM 0x30 /* Short msg with str_id */ + +#define IPC_IA_SET_GAIN_MRFLD 0x21 +/* Debug msgs */ +#define IPC_IA_DBG_MEM_READ 0x40 +#define IPC_IA_DBG_MEM_WRITE 0x41 +#define IPC_IA_DBG_LOOP_BACK 0x42 +#define IPC_IA_DBG_LOG_ENABLE 0x45 +#define IPC_IA_DBG_SET_PROBE_PARAMS 0x47 + +/* L2I Firmware/Codec Download msgs */ +#define IPC_IA_FW_INIT_CMPLT 0x81 +#define IPC_IA_FW_INIT_CMPLT_MRFLD 0x01 +#define IPC_IA_FW_ASYNC_ERR_MRFLD 0x11 + +/* L2I Codec Config/control msgs */ +#define IPC_SST_FRAGMENT_ELPASED 0x90 /* Request IA more data */ + +#define IPC_SST_BUF_UNDER_RUN 0x92 /* PB Under run and stopped */ +#define IPC_SST_BUF_OVER_RUN 0x93 /* CAP Under run and stopped */ +#define IPC_SST_DRAIN_END 0x94 /* PB Drain complete and stopped */ +#define IPC_SST_CHNGE_SSP_PARAMS 0x95 /* PB SSP parameters changed */ +#define IPC_SST_STREAM_PROCESS_FATAL_ERR 0x96/* error in processing a stream */ +#define IPC_SST_PERIOD_ELAPSED 0x97 /* period elapsed */ + +#define IPC_SST_ERROR_EVENT 0x99 /* Buffer over run occurred */ +/* L2S messages */ +#define IPC_SC_DDR_LINK_UP 0xC0 +#define IPC_SC_DDR_LINK_DOWN 0xC1 +#define IPC_SC_SET_LPECLK_REQ 0xC2 +#define IPC_SC_SSP_BIT_BANG 0xC3 + +/* L2I Error reporting msgs */ +#define IPC_IA_MEM_ALLOC_FAIL 0xE0 +#define IPC_IA_PROC_ERR 0xE1 /* error in processing a + stream can be used by playback and + capture modules */ + +/* L2I Debug msgs */ +#define IPC_IA_PRINT_STRING 0xF0 + +/* Buffer under-run */ +#define IPC_IA_BUF_UNDER_RUN_MRFLD 0x0B + +/* Mrfld specific defines: + * For asynchronous messages(INIT_CMPLT, PERIOD_ELAPSED, ASYNC_ERROR) + * received from FW, the format is: + * - IPC High: pvt_id is set to zero. Always short message. + * - msg_id is in lower 16-bits of IPC low payload. + * - pipe_id is in higher 16-bits of IPC low payload for period_elapsed. + * - error id is in higher 16-bits of IPC low payload for async errors. + */ +#define SST_ASYNC_DRV_ID 0 + +/* Command Response or Acknowledge message to any IPC message will have + * same message ID and stream ID information which is sent. + * There is no specific Ack message ID. The data field is used as response + * meaning. + */ + +/* SCU IPC for resetting & power gating the LPE through SCU */ +#define IPC_SCU_LPE_RESET 0xA3 + +enum ackData { + IPC_ACK_SUCCESS = 0, + IPC_ACK_FAILURE, +}; + +enum ipc_ia_msg_id { + IPC_CMD = 1, /*!< Task Control message ID */ + IPC_SET_PARAMS = 2,/*!< Task Set param message ID */ + IPC_GET_PARAMS = 3, /*!< Task Get param message ID */ + IPC_INVALID = 0xFF, /*! + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * + */ + +#define FORMAT(fmt) "%s: " fmt, __func__ +#define pr_fmt(fmt) KBUILD_MODNAME ": " FORMAT(fmt) + +#include +#include "mid_ssp.h" + + +/* + * Default I2S configuration + */ +/* + * TO BE DONE: use mixer to make it more flexible + */ +const struct intel_mid_i2s_settings ssp_platform_i2s_config = { + .master_mode_clk_selection = SSP_MASTER_CLOCK_UNDEFINED, + .master_mode_standard_freq = 0xFFFF, + .tx_tristate_phase = TXD_TRISTATE_LAST_PHASE_OFF, + .slave_clk_free_running_status = + SLAVE_SSPCLK_ON_DURING_TRANSFER_ONLY, + .ssp_duplex_mode = RX_AND_TX_MODE, + .ssp_trailing_byte_mode = SSP_TRAILING_BYTE_HDL_BY_IA, + .ssp_tx_dma = SSP_TX_DMA_ENABLE, + .ssp_rx_dma = SSP_RX_DMA_ENABLE, + .rx_fifo_interrupt = SSP_RX_FIFO_OVER_INT_ENABLE, + .tx_fifo_interrupt = SSP_TX_FIFO_UNDER_INT_ENABLE, + .ssp_rx_timeout_interrupt_status = SSP_RX_TIMEOUT_INT_DISABLE, + .ssp_trailing_byte_interrupt_status = + SSP_TRAILING_BYTE_INT_ENABLE, + .ssp_loopback_mode_status = SSP_LOOPBACK_OFF, + .ssp_rx_fifo_threshold = MID_SSP_RX_FIFO_THRESHOLD, + .ssp_tx_fifo_threshold = MID_SSP_TX_FIFO_THRESHOLD, + .ssp_frmsync_pol_bit = SSP_FRMS_ACTIVE_HIGH, + .ssp_end_transfer_state = + SSP_END_DATA_TRANSFER_STATE_LOW, + .ssp_psp_T1 = 0, + .ssp_psp_T2 = 0, + .ssp_psp_T4 = 0, + .ssp_psp_T5 = 0, + .ssp_psp_T6 = 1, +}; + +/* + * SSP DAI Internal functions + */ + +/** + * ssp_dma_req - This function programs a write or read request + * to the Intel I2S driver + * + * @param substream Pointer to stream structure + * return ret_val Status + */ +static int ssp_dma_req(struct snd_pcm_substream *substream) +{ + + struct intel_alsa_ssp_stream_info *str_info; + struct intel_ssp_config *ssp_config; + struct snd_pcm_runtime *pl_runtime; + int ret; +#ifdef _LLI_ENABLED_ + struct intel_mid_i2s_lli *sg_table = NULL; + int i; +#else + u32 *dma_addr; +#endif /* _LLI_ENABLED_ */ + + WARN(!substream, "SSP DAI: " + "ERROR NULL substream\n"); + if (!substream) + return -EINVAL; + + + pl_runtime = substream->runtime; + + str_info = pl_runtime->private_data; + + WARN(!str_info, "SSP DAI: " + "ERROR NULL str_info\n"); + if (!str_info) + return -EINVAL; + + ssp_config = str_info->ssp_config; + + WARN(!ssp_config, "SSP DAI: " + "ERROR NULL ssp_config\n"); + if (!ssp_config) + return -EINVAL; + + + WARN(!ssp_config->i2s_handle, "SSP DAI: " + "ERROR, trying to play a stream however " + "ssp_config->i2s_handle is NULL\n"); + + if (!ssp_config->i2s_handle) + return -EINVAL; + +#ifdef _LLI_ENABLED_ + if (!test_bit(INTEL_ALSA_SSP_STREAM_STARTED, + &str_info->stream_status)) { + pr_err("%s: Stream has been stopped before SSP DMA request has been taken into account", + __func__); + return 0; + } + + /* Will be executed once until next DAI shutdown */ + if (!test_bit(INTEL_ALSA_SSP_STREAM_INIT, + &str_info->stream_status)) { + + str_info->length = frames_to_bytes(pl_runtime, + pl_runtime->period_size); + + str_info->addr = substream->runtime->dma_area; + + sg_table = kzalloc(sizeof(struct intel_mid_i2s_lli) * + pl_runtime->periods, + GFP_KERNEL); + if (sg_table == NULL) { + pr_err("sg_table allocation failed!"); + return -EINVAL; + } + + for (i = 0; i < pl_runtime->periods; i++) { + sg_table[i].addr = (u32 *) (str_info->addr + + str_info->length * i); + sg_table[i].leng = (u32) str_info->length; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = intel_mid_i2s_lli_wr_req(ssp_config->i2s_handle, + sg_table, + pl_runtime->periods, + I2S_CIRCULAR_MODE, + substream); + else + ret = intel_mid_i2s_lli_rd_req(ssp_config->i2s_handle, + sg_table, + pl_runtime->periods, + I2S_CIRCULAR_MODE, + substream); + kfree(sg_table); + + if (ret != 0) { + pr_err("SSP DAI: %s request error", + (substream->stream == + SNDRV_PCM_STREAM_PLAYBACK) ? + "write" : "read"); + } + + set_bit(INTEL_ALSA_SSP_STREAM_INIT, &str_info->stream_status); + + intel_mid_i2s_command(ssp_config->i2s_handle, + SSP_CMD_ENABLE_SSP, NULL); + } + + /* Executed at each TRIGGER_START */ + intel_mid_i2s_command(ssp_config->i2s_handle, + (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + SSP_CMD_ENABLE_DMA_TX_INTR : + SSP_CMD_ENABLE_DMA_RX_INTR, NULL); + + return 0; +#else + str_info->length = frames_to_bytes(pl_runtime, pl_runtime->period_size); + + str_info->addr = substream->runtime->dma_area; + pr_debug("SSP DAI: FCT %s substream->runtime->dma_area = %p", + __func__, substream->runtime->dma_area); + + dma_addr = (u32 *)(str_info->addr + str_info->length + * str_info->period_req_index); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ret = intel_mid_i2s_wr_req(ssp_config->i2s_handle, dma_addr, + str_info->length, substream); + else + ret = intel_mid_i2s_rd_req(ssp_config->i2s_handle, dma_addr, + str_info->length, substream); + + if (ret == 0) { + intel_mid_i2s_command(ssp_config->i2s_handle, + SSP_CMD_ENABLE_SSP, NULL); + + if (test_and_set_bit(INTEL_ALSA_SSP_STREAM_RUNNING, + &str_info->stream_status)) { + pr_err("SSP DAI: ERROR previous request not handled\n"); + return -EBUSY; + } + + if (++(str_info->period_req_index) >= pl_runtime->periods) + str_info->period_req_index = 0; + return 0; + } else { + pr_err("SSP DAI: FCT %s read/write req ERROR\n", __func__); + return -EINVAL; + } +#endif /* _LLI_ENABLED_ */ +} /* ssp_dma_req */ + +/** + * ssp_dma_complete - End of capture or playback callback + * called in DMA Complete Tasklet context + * This Callback has in charge of re-programming a new read or write + * request to Intel MID I2S Driver if the stream has not been Closed. + * It calls also the snd_pcm_period_elapsed if the stream is not + * PAUSED or SUSPENDED to inform ALSA Kernel that the Ring Buffer + * period has been sent or received properly + * + * @param param Pointer to a user data + * return status + */ +static int ssp_dma_complete(void *param) +{ + struct snd_pcm_substream *substream; + struct intel_alsa_ssp_stream_info *str_info; + struct snd_pcm_runtime *pl_runtime; +#ifndef _LLI_ENABLED_ + bool call_back = false; + bool reset_index = false; +#endif /* _LLI_ENABLED_ */ + + substream = (struct snd_pcm_substream *)param; + pl_runtime = substream->runtime; + str_info = substream->runtime->private_data; + + WARN(!str_info, "SSP DAI: ERROR NULL str_info\n"); + if (str_info == NULL) + return -EINVAL; + +#ifdef _LLI_ENABLED_ + if (!test_bit(INTEL_ALSA_SSP_STREAM_INIT, + &str_info->stream_status)) { + pr_err("Stream already not initialized"); + return 0; + } + + if (++(str_info->period_cb_index) >= pl_runtime->periods) + str_info->period_cb_index = 0; + + if (test_bit(INTEL_ALSA_SSP_STREAM_STARTED, &str_info->stream_status)) + snd_pcm_period_elapsed(substream); + else + pr_debug("No call to snd_period_elapsed, stream is not started"); +#else + if (test_and_clear_bit(INTEL_ALSA_SSP_STREAM_RUNNING, + &str_info->stream_status)) { + bool dropped = test_and_clear_bit(INTEL_ALSA_SSP_STREAM_DROPPED, + &str_info->stream_status); + bool started = test_bit(INTEL_ALSA_SSP_STREAM_STARTED, + &str_info->stream_status); + + if (started) { + /* + * Whatever dropped or not, + * the stream is on going + */ + call_back = true; + } + if (started && dropped) { + /* + * the stream has been dropped and restarted + * before the callback occurs + * in this case the we have to reprogram the + * requests to SSP driver + * and reset the stream's indexes + */ + reset_index = true; + } + if (!started && !dropped) { + pr_err("SSP DAI: FCT %s neither started nor dropped", + __func__); + return -EBUSY; + } + } else { + pr_err("SSP DAI: FCT %s called while not running ", __func__); + return -EBUSY; + } + + if (call_back == true) { + pr_debug("SSP DAI: playback/capture (REQ=%d,CB=%d): DMA_REQ_COMPLETE\n", + str_info->period_req_index, + str_info->period_cb_index); + + if (reset_index) { + str_info->period_cb_index = 0; + str_info->period_req_index = 0; + } else if (++(str_info->period_cb_index) >= pl_runtime->periods) + str_info->period_cb_index = 0; + + /* + * Launch the next Capture/Playback request if + * no CLOSE has been requested + */ + ssp_dma_req(substream); + + /* + * Call the snd_pcm_period_elapsed to inform ALSA kernel + * that a ringbuffer period has been played + */ + snd_pcm_period_elapsed(substream); + } +#endif /* _LLI_ENABLED_ */ + + return 0; +} /* ssp_dma_complete */ + +/** + * intel_mid_ssp_transfer_data - send data buffers + * + * @param work Pointer to stream structure + * return void + */ +void intel_mid_ssp_transfer_data(struct work_struct *work) +{ + struct intel_alsa_ssp_stream_info *str_info; + struct snd_pcm_substream *substream; + + BUG_ON(!work); + + str_info = container_of(work, struct intel_alsa_ssp_stream_info, + ssp_ws); + + BUG_ON(!str_info); + + substream = str_info->substream; + + BUG_ON(!substream); + + ssp_dma_req(substream); + +} /* intel_mid_ssp_transfer_data */ + +/* + * SSP PLATFORM + */ + +/* + * SSP Platform functions + */ +static int ssp_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) +{ + int retval = 0; + struct snd_soc_dai *dai; + struct snd_pcm *pcm; + + pr_debug("SSP DAI: FCT %s enters\n", + __func__); + /* + * Do pre-allocation to all substreams of the given pcm for the + * specified DMA type. + * + */ + dai = soc_runtime->cpu_dai; + pcm = soc_runtime->pcm; + + if (dai->driver->playback.channels_min || + dai->driver->capture.channels_min) { + retval = snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + SSP_MIN_BUFFER, SSP_MAX_BUFFER); + + if (retval) { + pr_err("DMA buffer allocation fail\n"); + return retval; + } + } + return retval; +} /* ssp_platform_pcm_new */ + +static void ssp_platform_pcm_free(struct snd_pcm *pcm) +{ + pr_debug("SSP DAI: FCT %s enter\n", + __func__); + /* + * release all pre-allocated buffers on the pcm + * + */ + snd_pcm_lib_preallocate_free_for_all(pcm); + +} /* ssp_platform_pcm_free */ + +/** + * ssp_platform_hw_params - Allocate memory for Ring Buffer according + * to hw_params. + * It's called in a non-atomic context + * + * @param substream Substream for which the stream function is called + * @param hw_params Stream command thats requested from upper layer + * return status 0 ==> OK + * + */ +static int ssp_platform_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + int ret_val; + + /* + * Allocates the DMA buffer for the substream + * This callback could be called several time + * snd_pcm_lib_malloc_pages allows to avoid memory leak + * as it release already allocated memory when already allocated + */ + ret_val = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + + if (ret_val < 0) + return ret_val; + + memset(substream->runtime->dma_area, 0, params_buffer_bytes(hw_params)); + + return 0; +} /* ssp_platform_hw_params */ + +/* + * ssp_platform_pointer- to send the current buffer pointer + * processed by HW + * This function is called by ALSA framework to get the current HW buffer ptr + * to check the Ring Buffer Status + * + * @param substream Pointer to the substream for which the function + * is called + * + * return pcm_pointer Indicates the number of samples played + * + */ +static +snd_pcm_uframes_t ssp_platform_pointer(struct snd_pcm_substream *substream) +{ + struct intel_alsa_ssp_stream_info *str_info; + unsigned long pcm_pointer = 0; + + str_info = substream->runtime->private_data; + + WARN(!str_info, "SSP DAI: ERROR NULL str_info\n"); + if (!str_info) + return -EINVAL; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + pcm_pointer = (unsigned long) (str_info->period_cb_index + * substream->runtime->period_size); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + pcm_pointer = (unsigned long) (str_info->period_cb_index + * substream->runtime->period_size); + + pr_debug("SSP DAI: FCT %s Frame bits = %d, period_size = %d, periods = %d\n", + __func__, + (int) substream->runtime->frame_bits, + (int) substream->runtime->period_size, + (int) substream->runtime->periods); + + pr_debug("SSP DAI: FCT %s returns %ld\n", + __func__, pcm_pointer); + + return pcm_pointer; +} /* ssp_platform_pointer */ + +static struct snd_pcm_ops ssp_platform_ops = { + .open = NULL, + .close = NULL, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = ssp_platform_hw_params, + .hw_free = NULL, + .prepare = NULL, + .trigger = NULL, + .pointer = ssp_platform_pointer, +}; + +struct snd_soc_platform_driver soc_ssp_platform_drv = { + .ops = &ssp_platform_ops, + .probe = NULL, + .pcm_new = ssp_platform_pcm_new, + .pcm_free = ssp_platform_pcm_free, +}; + +/* + * SND SOC DAI OPs + */ +static int ssp_probe(struct snd_soc_dai *cpu_dai) +{ + struct intel_ssp_config *ssp_config; + + pr_info("SSP DAI: FCT %s enters for CPU_DAI %d\n", + __func__, cpu_dai->id); + + ssp_config = kzalloc(sizeof(struct intel_ssp_config), GFP_KERNEL); + + + if (ssp_config == NULL) { + pr_err("Unable to allocate ssp_config\n"); + return -ENOMEM; + } + +#ifndef _LLI_ENABLED_ + ssp_config->intel_mid_dma_alloc = false; +#endif /* _LLI_ENABLED_ */ + ssp_config->ssp_dai_tx_allocated = false; + ssp_config->ssp_dai_rx_allocated = false; + + ssp_config->i2s_settings = ssp_platform_i2s_config; + pr_info("SSP DAI: FCT %s ssp_config %p\n", + __func__, ssp_config); + + cpu_dai->playback_dma_data = cpu_dai->capture_dma_data = ssp_config; + + return 0; + +} /* ssp_probe */ + +static int ssp_remove(struct snd_soc_dai *cpu_dai) +{ + struct intel_ssp_config *ssp_config; + + WARN(!cpu_dai, "SSP DAI: " + "ERROR NULL cpu_dai\n"); + if (!cpu_dai) + return -EINVAL; + + ssp_config = cpu_dai->playback_dma_data; + + kfree(ssp_config); + + return 0; +} /* ssp_remove */ + +static int ssp_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct intel_ssp_config *ssp_config; + struct snd_pcm_runtime *pl_runtime; + struct intel_alsa_ssp_stream_info *str_info; + struct intel_ssp_info *ssp_info; + struct snd_soc_dai_driver *cpudai_drv = cpu_dai->driver; + unsigned int device; + int ret = 0; + + WARN(!cpu_dai->driver, "SSP DAI: " + "FCT %s ERROR NULL cpu_dai->driver\n", + __func__); + if (!cpu_dai->driver) + return -EINVAL; + + pr_info("SSP DAI: FCT %s enters for DAI Id = %d\n", + __func__, cpu_dai->driver->id); + + ssp_info = dev_get_drvdata(cpu_dai->dev); + + WARN(!ssp_info, "SSP DAI: ERROR NULL ssp_info\n"); + if (!ssp_info) + return -EINVAL; + + pl_runtime = substream->runtime; + + ssp_config = snd_soc_dai_get_dma_data(cpu_dai, substream); + + WARN(!ssp_config, "SSP DAI: " + "FCT %s ERROR NULL ssp_config\n", + __func__); + if (!ssp_config) + return -EINVAL; + + + device = cpu_dai->driver->id; + + /* + * setup the internal data structure stream pointers based on it being + * playback or capture stream + */ + str_info = kzalloc(sizeof(*str_info), GFP_KERNEL); + + if (!str_info) { + pr_err("SSP DAI: str_info alloc failure\n"); + return -EINVAL; + + } + str_info->substream = substream; + str_info->ssp_config = ssp_config; + str_info->stream_status = 0; + + INIT_WORK(&str_info->ssp_ws, intel_mid_ssp_transfer_data); + + /* + * Initialize SSPx [x=0,1] driver + * Store the Stream information + */ + pl_runtime->private_data = str_info; + + pr_debug("SSP DAI: FCT %s enters cpu_dai->card->name = %s\n", + __func__, cpu_dai->card->name); + + if (!cpu_dai->active) { + if (!strcmp(cpudai_drv->name, SSP_BT_DAI_NAME)) { + ssp_config->i2s_handle = + intel_mid_i2s_open(SSP_USAGE_BLUETOOTH_FM); + pr_debug("opening the CPU_DAI for "\ + "SSP_USAGE_BLUETOOTH_FM, i2s_handle = %p\n", + ssp_config->i2s_handle); + + } else if (!strcmp(cpudai_drv->name, SSP_MODEM_DAI_NAME)) { + ssp_config->i2s_handle = + intel_mid_i2s_open(SSP_USAGE_MODEM); + pr_debug("opening the CPU_DAI for "\ + "SSP_USAGE_MODEM, i2s_handle = %p\n", + ssp_config->i2s_handle); + + } else { + pr_err("non Valid SOC CARD\n"); + return -EINVAL; + } + + /* Set the Write Callback */ + ret = intel_mid_i2s_set_wr_cb(ssp_config->i2s_handle, + ssp_dma_complete); + if (ret) + return ret; + + /* Set the Default Read Callback */ + ret = intel_mid_i2s_set_rd_cb(ssp_config->i2s_handle, + ssp_dma_complete); + if (ret) + return ret; + + } else { + /* + * do nothing because already Open by sibling substream + */ + pr_debug("SSP DAI: FCT %s Open DO NOTHING\n", + __func__); + } + return 0; +} /* ssp_dai_startup */ + +static void ssp_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct intel_ssp_config *ssp_config; + struct intel_alsa_ssp_stream_info *str_info; + struct snd_pcm_runtime *runtime; + struct intel_ssp_info *ssp_info; + + ssp_config = snd_soc_dai_get_dma_data(cpu_dai, substream); + + BUG_ON(!ssp_config); + + runtime = substream->runtime; + BUG_ON(!runtime->private_data); + + str_info = runtime->private_data; + BUG_ON(!str_info); + + ssp_info = dev_get_drvdata(cpu_dai->dev); + BUG_ON(!ssp_info); + + /* Cancel pending work */ + cancel_work_sync(&str_info->ssp_ws); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + /* + * Only Free Tx channel if no playback streams are active + * Shutdown can be called right after a startup if something + * failed (as a concurrency issue + * so this case can happen + */ + if ((!cpu_dai->playback_active) && + (ssp_config->i2s_settings.ssp_active_tx_slots_map)) { + intel_mid_i2s_command(ssp_config->i2s_handle, + SSP_CMD_FREE_TX, NULL); + pr_debug("SSP DAI: FCT %s TX DMA Channel released\n", + __func__); + } + ssp_config->ssp_dai_tx_allocated = false; + break; + + case SNDRV_PCM_STREAM_CAPTURE: + /* + * Only Free Rx channel if no capture streams are active + */ + if ((!cpu_dai->capture_active) && + (ssp_config->i2s_settings.ssp_active_rx_slots_map)) { + intel_mid_i2s_command(ssp_config->i2s_handle, + SSP_CMD_FREE_RX, NULL); + pr_debug("SSP DAI: FCT %s RX DMA Channel released\n", + __func__); + } + ssp_config->ssp_dai_rx_allocated = false; + break; + + default: + pr_err("SSP DAI: FCT %s Bad stream_dir: %d\n", + __func__, substream->stream); + break; + } + +#ifdef _LLI_ENABLED_ + clear_bit(INTEL_ALSA_SSP_STREAM_INIT, &str_info->stream_status); +#endif /* _LLI_ENABLED_ */ + + kfree(str_info); + + if (!cpu_dai->active) { + pr_info("SSP DAI: FCT %s closing I2S\n", + __func__); + /* + * Close the Intel MID I2S connection + */ + intel_mid_i2s_close(ssp_config->i2s_handle); + + ssp_config->i2s_handle = NULL; +#ifndef _LLI_ENABLED_ + ssp_config->intel_mid_dma_alloc = false; +#endif /* _LLI_ENABLED_ */ + } + +} /* ssp_dai_shutdown */ + +static int ssp_set_dai_fmt(struct snd_soc_dai *cpu_dai, + unsigned int fmt) +{ + struct intel_ssp_config *ssp_config; + struct intel_mid_i2s_settings *i2s_config; + + ssp_config = cpu_dai->playback_dma_data; + + WARN(!ssp_config, "SSP DAI: FCT %s ssp_config=NULL\n", + __func__); + if (!ssp_config) + return -EINVAL; + + pr_debug("SSP DAI: FCT %s fmt = %d\n", + __func__, fmt); + + i2s_config = &(ssp_config->i2s_settings); + + /* + * SSP CLK Direction + * SSP FRMSYNC Direction + */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + i2s_config->sspslclk_direction = SSPSCLK_MASTER_MODE; + i2s_config->sspsfrm_direction = SSPSCLK_MASTER_MODE; + /* + * Mandatory to be able to perform only RX without TX + * in SSP CLK Master Mode + * + */ + i2s_config->ssp_duplex_mode = RX_WITHOUT_TX_MODE; + break; + case SND_SOC_DAIFMT_CBM_CFS: + i2s_config->sspslclk_direction = SSPSCLK_MASTER_MODE; + i2s_config->sspsfrm_direction = SSPSCLK_SLAVE_MODE; + /* + * Mandatory to be able to perform only RX without TX + * in SSP CLK Master Mode + * + */ + i2s_config->ssp_duplex_mode = RX_WITHOUT_TX_MODE; + break; + case SND_SOC_DAIFMT_CBS_CFS: + i2s_config->sspslclk_direction = SSPSCLK_SLAVE_MODE; + i2s_config->sspsfrm_direction = SSPSCLK_SLAVE_MODE; + i2s_config->ssp_duplex_mode = RX_AND_TX_MODE; + break; + case SND_SOC_DAIFMT_CBS_CFM: + i2s_config->sspslclk_direction = SSPSCLK_SLAVE_MODE; + i2s_config->sspsfrm_direction = SSPSCLK_MASTER_MODE; + i2s_config->ssp_duplex_mode = RX_AND_TX_MODE; + break; + default: + pr_err("SSP DAI: %s Bad DAI CLK/FS Mode=%d\n", + __func__, + (fmt & SND_SOC_DAIFMT_MASTER_MASK)); + return -EINVAL; + } + /* + * SSP Sgnal Inversion Mode + * Use clock gating bitfield for + * Serial bit-rate Clock Mode + */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) { + case SSP_DAI_SCMODE_0: + i2s_config->ssp_serial_clk_mode = SSP_CLK_MODE_0; + break; + case SSP_DAI_SCMODE_1: + i2s_config->ssp_serial_clk_mode = SSP_CLK_MODE_1; + break; + case SSP_DAI_SCMODE_2: + i2s_config->ssp_serial_clk_mode = SSP_CLK_MODE_2; + break; + case SSP_DAI_SCMODE_3: + i2s_config->ssp_serial_clk_mode = SSP_CLK_MODE_3; + break; + default: + pr_err("SSP DAI: %s Bad DAI Signal Inversion Mode=%d\n", + __func__, + (fmt & SND_SOC_DAIFMT_INV_MASK)); + return -EINVAL; + } + + /* + * SSP FS Inversion Mode + */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + case SND_SOC_DAIFMT_IB_NF: + i2s_config->ssp_frmsync_pol_bit = SSP_FRMS_ACTIVE_HIGH; + break; + case SND_SOC_DAIFMT_NB_IF: + case SND_SOC_DAIFMT_IB_IF: + i2s_config->ssp_frmsync_pol_bit = SSP_FRMS_ACTIVE_LOW; + break; + default: + pr_err("SSP DAI: %s Bad DAI FS Inversion Mode=%d\n", + __func__, + (fmt & SND_SOC_DAIFMT_INV_MASK)); + return -EINVAL; + } + + /* + * SSP Format Mode + */ + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + i2s_config->frame_format = PSP_FORMAT; + break; + + default: + pr_err("SSP DAI: %s Bad DAI format Mode=%d\n", + __func__, + (fmt & SND_SOC_DAIFMT_FORMAT_MASK)); + return -EINVAL; + } + return 0; +} /* ssp_set_dai_fmt */ + +static int ssp_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, + unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) +{ + struct intel_ssp_config *ssp_config; + struct intel_mid_i2s_settings *i2s_config; + + ssp_config = cpu_dai->playback_dma_data; + + WARN(!ssp_config, "SSP DAI: FCT %s ssp_config=NULL\n", + __func__); + if (!ssp_config) + return -EINVAL; + + + i2s_config = &(ssp_config->i2s_settings); + + i2s_config->frame_rate_divider_control = slots; + i2s_config->data_size = slot_width; + i2s_config->mode = SSP_IN_NETWORK_MODE; + i2s_config->ssp_active_tx_slots_map = tx_mask; + i2s_config->ssp_active_rx_slots_map = rx_mask; + + pr_debug("i2s_config->frame_rate_divider_control = %d\n", + i2s_config->frame_rate_divider_control); + pr_debug("i2s_config->data_size = %d\n", + i2s_config->data_size); + pr_debug("i2s_config->mode = %d\n", + i2s_config->mode); + pr_debug("i2s_config->ssp_active_tx_slots_map = %d\n", + i2s_config->ssp_active_tx_slots_map); + pr_debug("i2s_config->ssp_active_rx_slots_map = %d\n", + i2s_config->ssp_active_rx_slots_map); + + return 0; +} + +static int ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + struct intel_ssp_config *ssp_config; + struct intel_mid_i2s_settings *i2s_config; + + ssp_config = cpu_dai->playback_dma_data; + + BUG_ON(!ssp_config); + + i2s_config = &(ssp_config->i2s_settings); + + pr_debug("SSP DAI: FCT %s clk_id = %d\n", + __func__, clk_id); + + switch (clk_id) { + case SSP_CLK_ONCHIP: + i2s_config->master_mode_clk_selection = SSP_ONCHIP_CLOCK; + break; + case SSP_CLK_NET: + i2s_config->master_mode_clk_selection = SSP_NETWORK_CLOCK; + break; + case SSP_CLK_EXT: + i2s_config->master_mode_clk_selection = SSP_EXTERNAL_CLOCK; + break; + case SSP_CLK_AUDIO: + i2s_config->master_mode_clk_selection = SSP_ONCHIP_AUDIO_CLOCK; + break; + default: + i2s_config->master_mode_standard_freq = + SSP_MASTER_CLOCK_UNDEFINED; + pr_err("SSP DAI: %s Bad clk_id=%d\n", + __func__, + clk_id); + return -EINVAL; + } + + pr_debug("SSP DAI:FCT %s freq = %d\n", + __func__, freq); + + switch (freq) { + case 8000: + i2s_config->master_mode_standard_freq = SSP_FRM_FREQ_8_000; + i2s_config->ssp_psp_T1 = 0; + i2s_config->ssp_psp_T2 = 1; + i2s_config->ssp_psp_T4 = 0; + i2s_config->ssp_psp_T5 = 0; + i2s_config->ssp_psp_T6 = 1; + break; + + case 11025: + i2s_config->master_mode_standard_freq = SSP_FRM_FREQ_11_025; + pr_err("SSP DAI: %s Bad freq_out=%d\n", + __func__, + freq); + return -EINVAL; + + case 16000: + i2s_config->master_mode_standard_freq = SSP_FRM_FREQ_16_000; + i2s_config->ssp_psp_T1 = 6; + i2s_config->ssp_psp_T2 = 2; + i2s_config->ssp_psp_T4 = 0; + i2s_config->ssp_psp_T5 = 14; + i2s_config->ssp_psp_T6 = 16; + break; + + case 22050: + i2s_config->master_mode_standard_freq = SSP_FRM_FREQ_22_050; + pr_err("SSP DAI: %s Bad freq_out=%d\n", + __func__, + freq); + return -EINVAL; + + case 44100: + i2s_config->master_mode_standard_freq = SSP_FRM_FREQ_44_100; + pr_err("SSP DAI: %s Bad freq_out=%d\n", + __func__, + freq); + return -EINVAL; + + case 48000: + i2s_config->master_mode_standard_freq = SSP_FRM_FREQ_48_000; + i2s_config->ssp_psp_T1 = 6; + i2s_config->ssp_psp_T2 = 2; + i2s_config->ssp_psp_T4 = 0; + i2s_config->ssp_psp_T5 = 14; + i2s_config->ssp_psp_T6 = 16; + break; + + default: + i2s_config->master_mode_standard_freq = SSP_FRM_FREQ_UNDEFINED; + pr_err("SSP DAI: %s Bad freq_out=%d\n", + __func__, + freq); + return -EINVAL; + } + return 0; +} + +static int ssp_set_dai_tristate(struct snd_soc_dai *cpu_dai, + int tristate) +{ + struct intel_ssp_config *ssp_config; + struct intel_mid_i2s_settings *i2s_config; + + ssp_config = cpu_dai->playback_dma_data; + + BUG_ON(!ssp_config); + + i2s_config = &(ssp_config->i2s_settings); + + if (IS_TRISTATE_ENABLED(tristate)) + i2s_config->tx_tristate_enable = TXD_TRISTATE_ON; + else + i2s_config->tx_tristate_enable = TXD_TRISTATE_OFF; + + if (IS_NEXT_FRMS_ASSERTED_WITH_LSB_PREVIOUS_FRM(tristate)) + i2s_config->ssp_frmsync_timing_bit = + NEXT_FRMS_ASS_WITH_LSB_PREVIOUS_FRM; + else + i2s_config->ssp_frmsync_timing_bit = + NEXT_FRMS_ASS_AFTER_END_OF_T4; + + pr_debug("FCT %s tristate %x\n", __func__, tristate); + + return 0; +} + +/** + * ssp_dai_trigger- stream activities are handled here + * This function is called whenever a stream activity is invoked + * The Trigger function is called in an atomic context + * + * @param substream Substream for which the stream function is called + * @param cmd The stream command thats requested from upper layer + * return status 0 ==> OK + * + */ +static int ssp_dai_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *cpu_dai) +{ + int ret_val = 0; + struct intel_alsa_ssp_stream_info *str_info; + struct snd_pcm_runtime *pl_runtime; + struct intel_ssp_info *ssp_info; +#ifdef _LLI_ENABLED_ + struct intel_ssp_config *ssp_config; +#endif /* _LLI_ENABLED_ */ + + bool trigger_start = true; + int stream = 0; + + pr_debug("SSP DAI: FCT %s enters\n", + __func__); + + stream = substream->stream; + + + pl_runtime = substream->runtime; + + WARN(!pl_runtime->private_data, "SSP DAI: ERROR " + "NULL pl_runtime->private_data\n"); + if (!pl_runtime->private_data) + return -EINVAL; + + WARN(!cpu_dai, "SSP DAI: ERROR NULL cpu_dai\n"); + if (!cpu_dai) + return -EINVAL; + + WARN(!cpu_dai->dev, "SSP DAI: ERROR NULL cpu_dai->dev\n"); + if (!cpu_dai->dev) + return -EINVAL; + + ssp_info = dev_get_drvdata(cpu_dai->dev); + + + WARN(!ssp_info->ssp_dai_wq, "SSP DAI: ERROR NULL ssp_dai_wq\n"); + if (!ssp_info->ssp_dai_wq) + return -EINVAL; + + str_info = pl_runtime->private_data; + +#ifdef _LLI_ENABLED_ + ssp_config = str_info->ssp_config; + + WARN(!ssp_config, "SSP DAI: ERROR NULL ssp_config\n"); + if (!ssp_config) + return -EINVAL; +#endif /* _LLI_ENABLED_ */ + + pr_debug("SSP DAI: FCT %s CMD = 0x%04X\n", + __func__, cmd); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (!test_and_set_bit(INTEL_ALSA_SSP_STREAM_STARTED, + &str_info->stream_status)) { +#ifndef _LLI_ENABLED_ + if (test_bit(INTEL_ALSA_SSP_STREAM_DROPPED, + &str_info->stream_status)) { + pr_debug("SSP DAI: FCT %s do not restart the trigger stream running already\n", + __func__); + trigger_start = false; + } else +#endif /* _LLI_ENABLED_ */ + trigger_start = true; + } else { + pr_err("SSP DAI: ERROR 2 consecutive TRIGGER_START\n"); + return -EBUSY; + } + + /* Store the substream locally */ + if (trigger_start) { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + + pr_debug("SSP DAI: queue Playback Work\n"); + queue_work(ssp_info->ssp_dai_wq, + &str_info->ssp_ws); + } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { + + pr_debug("SSP DAI: queue Capture Work\n"); + queue_work(ssp_info->ssp_dai_wq, + &str_info->ssp_ws); + } else { + pr_err("SSP DAI: SNDRV_PCM_TRIGGER_START Bad Stream: %d\n", + substream->stream); + return -EINVAL; + } + } + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (test_and_clear_bit(INTEL_ALSA_SSP_STREAM_STARTED, + &str_info->stream_status)) { +#ifdef _LLI_ENABLED_ + intel_mid_i2s_command(ssp_config->i2s_handle, + (stream == SNDRV_PCM_STREAM_PLAYBACK) ? + SSP_CMD_DISABLE_DMA_TX_INTR : + SSP_CMD_DISABLE_DMA_RX_INTR, + NULL); +#else + set_bit(INTEL_ALSA_SSP_STREAM_DROPPED, + &str_info->stream_status); +#endif /* _LLI_ENABLED_ */ + } else { + pr_err("SSP DAI: trigger START/STOP mismatch\n"); + return -EBUSY; + } + break; + + default: + pr_err("SSP DAI: snd_i2s_alsa_pcm_trigger Bad Command\n"); + return -EINVAL; + } + return ret_val; +} /* ssp_dai_trigger */ + +/** + * ssp_dai_hw_params - Allocate memory for Ring Buffer according + * to hw_params. + * It's called in a non-atomic context + * + * @param substream Substream for which the stream function is called + * @param hw_params Stream command thats requested from upper layer + * @param cpu_dai Pointer to the CPU DAI that is used + * return status 0 ==> OK + * + */ +static int ssp_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params, + struct snd_soc_dai *cpu_dai) +{ + + return 0; +} + +static int ssp_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct intel_ssp_config *ssp_config; + struct intel_ssp_info *ssp_info; + + pr_debug("SSP DAI: FCT %s enters\n", + __func__); + + WARN(!cpu_dai, "SSP DAI: ERROR NULL cpu_dai\n"); + if (!cpu_dai) + return -EINVAL; + + ssp_info = dev_get_drvdata(cpu_dai->dev); + WARN(!ssp_info, "SSP DAI: ERROR NULL ssp_info\n"); + if (!ssp_info) + return -EINVAL; + + ssp_config = snd_soc_dai_get_dma_data(cpu_dai, substream); + pr_debug("SSP DAI: FCT %s ssp_dai_tx_allocated %d "\ + "ssp_dai_rx_allocated %d\n", + __func__, + ssp_config->ssp_dai_tx_allocated, + ssp_config->ssp_dai_rx_allocated); + + /* + * The set HW Config is only once for a CPU DAI + */ + + if (!ssp_config->ssp_dai_tx_allocated && + !ssp_config->ssp_dai_rx_allocated) { + intel_mid_i2s_command(ssp_config->i2s_handle, + SSP_CMD_SET_HW_CONFIG, + &(ssp_config->i2s_settings)); + } + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + if (!ssp_config->ssp_dai_tx_allocated) { + if (intel_mid_i2s_command(ssp_config->i2s_handle, + SSP_CMD_ALLOC_TX, NULL)) { + pr_err("can not alloc TX DMA Channel\n"); + return -EBUSY; + } + ssp_config->ssp_dai_tx_allocated = true; + } + break; + + case SNDRV_PCM_STREAM_CAPTURE: + if (!ssp_config->ssp_dai_rx_allocated) { + if (intel_mid_i2s_command(ssp_config->i2s_handle, + SSP_CMD_ALLOC_RX, NULL)) { + pr_err("can not alloc RX DMA Channel\n"); + return -EBUSY; + } + ssp_config->ssp_dai_rx_allocated = true; + } + break; + + default: + pr_err("SSP DAI: FCT %s Bad stream_dir: %d\n", + __func__, substream->stream); + return -EINVAL; + } + +#ifndef _LLI_ENABLED_ + ssp_config->intel_mid_dma_alloc = true; +#endif /* _LLI_ENABLED_ */ + + pr_debug("SSP DAI: FCT %s leaves\n", + __func__); + + return 0; +} + + + +/* BT/FM */ +static struct snd_soc_dai_ops ssp_dai_ops = { + .startup = ssp_dai_startup, + .shutdown = ssp_dai_shutdown, + .trigger = ssp_dai_trigger, + .hw_params = ssp_dai_hw_params, + .prepare = ssp_dai_prepare, + .set_sysclk = ssp_set_dai_sysclk, + .set_pll = NULL, + .set_fmt = ssp_set_dai_fmt, + .set_tdm_slot = ssp_set_dai_tdm_slot, + .set_tristate = ssp_set_dai_tristate, +}; + +#define SSP_SUPPORTED_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) + +#define SSP_SUPPORTED_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_U16_LE | \ + SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_U8) + +struct snd_soc_dai_driver intel_ssp_platform_dai[] = { +{ + .name = SSP_MODEM_DAI_NAME, + .id = 0, + .playback = { + .channels_min = 1, + .channels_max = 8, + .rates = SSP_SUPPORTED_RATES, + .formats = SSP_SUPPORTED_FORMATS, + }, + .capture = { + .channels_min = 1, + .channels_max = 8, + .rates = SSP_SUPPORTED_RATES, + .formats = SSP_SUPPORTED_FORMATS, + }, + .ops = &ssp_dai_ops, + .probe = ssp_probe, + .remove = ssp_remove, +}, +{ + .name = SSP_BT_DAI_NAME, + .id = 1, + .playback = { + .channels_min = 1, + .channels_max = 8, + .rates = SSP_SUPPORTED_RATES, + .formats = SSP_SUPPORTED_FORMATS, + }, + .capture = { + .channels_min = 1, + .channels_max = 8, + .rates = SSP_SUPPORTED_RATES, + .formats = SSP_SUPPORTED_FORMATS, + }, + .ops = &ssp_dai_ops, + .probe = ssp_probe, + .remove = ssp_remove, +}, +}; + +static const struct snd_soc_component_driver ssp_component = { + .name = "ssp", +}; + +static int ssp_dai_probe(struct platform_device *pdev) +{ + int ret; + struct intel_ssp_info *ssp_info; + + pr_info("SSP DAI: FCT %s enters\n", + __func__); + + ssp_info = kzalloc(sizeof(struct intel_ssp_info), GFP_KERNEL); + + if (ssp_info == NULL) { + pr_err("Unable to allocate ssp_info\n"); + return -ENOMEM; + } + pr_info("ssp_info address %p", ssp_info); + + ret = snd_soc_register_platform(&pdev->dev, + &soc_ssp_platform_drv); + if (ret) { + pr_err("registering SSP PLATFORM failed\n"); + snd_soc_unregister_component(&pdev->dev); + kfree(ssp_info); + return -EBUSY; + } + + ret = snd_soc_register_component(&pdev->dev, &ssp_component, + intel_ssp_platform_dai, + ARRAY_SIZE(intel_ssp_platform_dai)); + + if (ret) { + pr_err("registering cpu DAIs failed\n"); + snd_soc_unregister_component(&pdev->dev); + kfree(ssp_info); + return -EBUSY; + } + + ssp_info->ssp_dai_wq = create_workqueue("ssp_transfer_data"); + + if (!ssp_info->ssp_dai_wq) { + pr_err("work queue failed\n"); + snd_soc_unregister_component(&pdev->dev); + kfree(ssp_info); + return -ENOMEM; + } + + platform_set_drvdata(pdev, ssp_info); + + pr_info("SSP DAI: FCT %s leaves %d\n", + __func__, ret); + + return ret; +} + +static int ssp_dai_remove(struct platform_device *pdev) +{ + struct intel_ssp_info *ssp_info = platform_get_drvdata(pdev); + + pr_debug("SSP DAI: FCT %s enters\n", + __func__); + + if (ssp_info == NULL) { + pr_err("Unable to allocate ssp_info\n"); + return -ENOMEM; + } + pr_info("ssp_info address %p", ssp_info); + + flush_workqueue(ssp_info->ssp_dai_wq); + + destroy_workqueue(ssp_info->ssp_dai_wq); + + platform_set_drvdata(pdev, NULL); + + snd_soc_unregister_component(&pdev->dev); + + snd_soc_unregister_platform(&pdev->dev); + + pr_debug("SSP DAI: FCT %s leaves\n", + __func__); + + return 0; +} + +static struct platform_driver intel_ssp_dai_driver = { + .driver = { + .name = "mid-ssp-dai", + .owner = THIS_MODULE, + }, + .probe = ssp_dai_probe, + .remove = ssp_dai_remove, +}; + + +static int __init ssp_soc_dai_init(void) +{ + pr_info("SSP DAI: FCT %s called\n", + __func__); + + return platform_driver_register(&intel_ssp_dai_driver); +} +module_init(ssp_soc_dai_init); + +static void __exit ssp_soc_dai_exit(void) +{ + pr_debug("SSP DAI: FCT %s called\n", + __func__); + + platform_driver_unregister(&intel_ssp_dai_driver); + +} +module_exit(ssp_soc_dai_exit); + +MODULE_DESCRIPTION("ASoC Intel(R) MID Platform driver"); +MODULE_AUTHOR("Selma Bensaid"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:ssp-cpu-dai"); diff --git a/sound/soc/intel/ssp/mid_ssp.h b/sound/soc/intel/ssp/mid_ssp.h new file mode 100644 index 000000000000..4df4eac53337 --- /dev/null +++ b/sound/soc/intel/ssp/mid_ssp.h @@ -0,0 +1,124 @@ +/* + * mfld_ssp.h - ASoC CPU DAI driver for + * + * Copyright (C) 2011-12 Intel Corp + * Authors: Selma Bensaid + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#ifndef MID_SSP_H_ +#define MID_SSP_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define SSP_MODEM_DAI_NAME "ssp-modem-cpu-dai" +#define SSP_BT_DAI_NAME "ssp-bt-cpu-dai" + +#define SSP_MAX_BUFFER (640*1024) +#define SSP_MIN_BUFFER (640*1024) + +#define TRISTATE_BIT 0 +#define FRAME_SYNC_RELATIVE_TIMING_BIT 1 +#define DUMMY_START_ONE_PERIOD_OFFSET 2 +#define DUMMY_START_ONE_PERIOD_MASK 0x3 + +#define IS_TRISTATE_ENABLED(x) (x & BIT(TRISTATE_BIT)) +#define IS_NEXT_FRMS_ASSERTED_WITH_LSB_PREVIOUS_FRM(x) \ + ((x & BIT(FRAME_SYNC_RELATIVE_TIMING_BIT)) \ + >> FRAME_SYNC_RELATIVE_TIMING_BIT) +#define IS_DUMMY_START_ONE_PERIOD_OFFSET(x) \ + ((x >> DUMMY_START_ONE_PERIOD_OFFSET) \ + & DUMMY_START_ONE_PERIOD_MASK) + +#define MID_SSP_RX_FIFO_THRESHOLD 8 +#define MID_SSP_TX_FIFO_THRESHOLD 7 + + +/* data driven FALLING, data sampled RISING, idle LOW */ +#define SSP_DAI_SCMODE_0 (1 << 4) +/* data driven RISING, data sampled FALLING, idle LOW */ +#define SSP_DAI_SCMODE_1 (2 << 4) +/* data driven RISING, data sampled FALLING, idle HIGH */ +#define SSP_DAI_SCMODE_2 (3 << 4) +/* data driven FALLING, data sampled RISING, idle HIGH */ +#define SSP_DAI_SCMODE_3 (4 << 4) + + +/* + * Structures Definition + */ + + +struct intel_ssp_config { + struct intel_mid_i2s_hdl *i2s_handle; + struct intel_mid_i2s_settings i2s_settings; +#ifndef _LLI_ENABLED_ + bool intel_mid_dma_alloc; +#endif /* _LLI_ENABLED_ */ + bool ssp_dai_tx_allocated; + bool ssp_dai_rx_allocated; +}; + +struct intel_ssp_info { + struct workqueue_struct *ssp_dai_wq; +}; + +struct intel_alsa_ssp_stream_info { + struct snd_pcm_substream *substream; + struct work_struct ssp_ws; + struct intel_ssp_config *ssp_config; + unsigned long stream_status; + u32 period_req_index; + s32 period_cb_index; + u8 *addr; + int length; +}; + + +/* + * Enum Definition + */ + +enum intel_alsa_ssp_stream_status { + INTEL_ALSA_SSP_STREAM_INIT = 0, + INTEL_ALSA_SSP_STREAM_STARTED, + INTEL_ALSA_SSP_STREAM_RUNNING, + INTEL_ALSA_SSP_STREAM_PAUSED, + INTEL_ALSA_SSP_STREAM_DROPPED, +}; +enum ssp_clk_def { + SSP_CLK_ONCHIP = 0x0, + SSP_CLK_NET, + SSP_CLK_EXT, + SSP_CLK_AUDIO +}; + + +#endif /* MID_SSP_H_ */ diff --git a/sound/soc/intel/sst/Makefile b/sound/soc/intel/sst/Makefile new file mode 100644 index 000000000000..8b620d01bc17 --- /dev/null +++ b/sound/soc/intel/sst/Makefile @@ -0,0 +1,13 @@ +# Makefile for SST Audio driver +snd-intel-sst-objs := sst.o sst_ipc.o sst_stream.o sst_drv_interface.o sst_dsp.o sst_pvt.o sst_app_interface.o sst_acpi.o + +ifdef CONFIG_DEBUG_FS + snd-intel-sst-objs += sst_debug.o +endif + +obj-$(CONFIG_SND_INTEL_SST) += snd-intel-sst.o + + +CFLAGS_snd-intel-sst.o = -I$(src) + +ccflags-y += -DMRFLD_WORD_WA -Werror diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c new file mode 100644 index 000000000000..0f59e47e2ab8 --- /dev/null +++ b/sound/soc/intel/sst/sst.c @@ -0,0 +1,1255 @@ +/* + * sst.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-10 Intel Corp + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R + * KP Jeeja + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This driver enumerates the SST audio engine as a PCI or ACPI device and + * provides interface to the platform driver to interact with the SST audio + * Firmware. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sst_platform.h" +#include "../platform_ipc_v2.h" +#include "sst.h" + +#define CREATE_TRACE_POINTS +#include "sst_trace.h" + +MODULE_AUTHOR("Vinod Koul "); +MODULE_AUTHOR("Harsha Priya "); +MODULE_AUTHOR("Dharageswari R "); +MODULE_AUTHOR("KP Jeeja "); +MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(SST_DRIVER_VERSION); + +struct intel_sst_drv *sst_drv_ctx; +static struct mutex drv_ctx_lock; + +/* + * * ioctl32 compat + * */ +#ifdef CONFIG_COMPAT +#include "sst_app_compat_interface.c" +#else +#define intel_sst_ioctl_compat NULL +#endif + +static const struct file_operations intel_sst_fops_cntrl = { + .owner = THIS_MODULE, + .open = intel_sst_open_cntrl, + .release = intel_sst_release_cntrl, + .unlocked_ioctl = intel_sst_ioctl, + .compat_ioctl = intel_sst_ioctl_compat, +}; + +struct miscdevice lpe_ctrl = { + .minor = MISC_DYNAMIC_MINOR,/* dynamic allocation */ + .name = "intel_sst_ctrl",/* /dev/intel_sst_ctrl */ + .fops = &intel_sst_fops_cntrl +}; + +static inline void set_imr_interrupts(struct intel_sst_drv *ctx, bool enable) +{ + union interrupt_reg imr; + + spin_lock(&ctx->ipc_spin_lock); + imr.full = sst_shim_read(ctx->shim, SST_IMRX); + if (enable) { + imr.part.done_interrupt = 0; + imr.part.busy_interrupt = 0; + } else { + imr.part.done_interrupt = 1; + imr.part.busy_interrupt = 1; + } + sst_shim_write(ctx->shim, SST_IMRX, imr.full); + spin_unlock(&ctx->ipc_spin_lock); +} + +#define SST_IS_PROCESS_REPLY(header) ((header & PROCESS_MSG) ? true : false) +#define SST_VALIDATE_MAILBOX_SIZE(size) ((size <= SST_MAILBOX_SIZE) ? true : false) + +static irqreturn_t intel_sst_interrupt_mrfld(int irq, void *context) +{ + union interrupt_reg_mrfld isr; + union ipc_header_mrfld header; + union sst_imr_reg_mrfld imr; + struct ipc_post *msg = NULL; + unsigned int size = 0; + struct intel_sst_drv *drv = (struct intel_sst_drv *) context; + irqreturn_t retval = IRQ_HANDLED; + + /* Interrupt arrived, check src */ + isr.full = sst_shim_read64(drv->shim, SST_ISRX); + if (isr.part.done_interrupt) { + /* Clear done bit */ + spin_lock(&drv->ipc_spin_lock); + header.full = sst_shim_read64(drv->shim, + drv->ipc_reg.ipcx); + header.p.header_high.part.done = 0; + sst_shim_write64(drv->shim, drv->ipc_reg.ipcx, header.full); + /* write 1 to clear status register */; + isr.part.done_interrupt = 1; + sst_shim_write64(drv->shim, SST_ISRX, isr.full); + spin_unlock(&drv->ipc_spin_lock); + trace_sst_ipc("ACK <-", header.p.header_high.full, + header.p.header_low_payload, + header.p.header_high.part.drv_id); + queue_work(drv->post_msg_wq, &drv->ipc_post_msg.wq); + retval = IRQ_HANDLED; + } + if (isr.part.busy_interrupt) { + spin_lock(&drv->ipc_spin_lock); + imr.full = sst_shim_read64(drv->shim, SST_IMRX); + imr.part.busy_interrupt = 1; + sst_shim_write64(drv->shim, SST_IMRX, imr.full); + spin_unlock(&drv->ipc_spin_lock); + header.full = sst_shim_read64(drv->shim, drv->ipc_reg.ipcd); + if (sst_create_ipc_msg(&msg, header.p.header_high.part.large)) { + pr_err("No memory available\n"); + drv->ops->clear_interrupt(); + return IRQ_HANDLED; + } + if (header.p.header_high.part.large) { + size = header.p.header_low_payload; + if (SST_VALIDATE_MAILBOX_SIZE(size)) { + memcpy_fromio(msg->mailbox_data, + drv->mailbox + drv->mailbox_recv_offset, size); + } else { + pr_err("Mailbox not copied, payload siz is: %u\n", size); + header.p.header_low_payload = 0; + } + } + msg->mrfld_header = header; + msg->is_process_reply = + SST_IS_PROCESS_REPLY(header.p.header_high.part.msg_id); + trace_sst_ipc("REPLY <-", msg->mrfld_header.p.header_high.full, + msg->mrfld_header.p.header_low_payload, + msg->mrfld_header.p.header_high.part.drv_id); + spin_lock(&drv->rx_msg_lock); + list_add_tail(&msg->node, &drv->rx_list); + spin_unlock(&drv->rx_msg_lock); + drv->ops->clear_interrupt(); + retval = IRQ_WAKE_THREAD; + } + return retval; +} + +static irqreturn_t intel_sst_irq_thread_mfld(int irq, void *context) +{ + struct intel_sst_drv *drv = (struct intel_sst_drv *) context; + struct ipc_post *__msg, *msg = NULL; + unsigned long irq_flags; + + if (list_empty(&drv->rx_list)) + return IRQ_HANDLED; + + spin_lock_irqsave(&drv->rx_msg_lock, irq_flags); + list_for_each_entry_safe(msg, __msg, &drv->rx_list, node) { + + list_del(&msg->node); + spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags); + if (msg->is_process_reply) + drv->ops->process_message(msg); + else + drv->ops->process_reply(msg); + + if (msg->is_large) + kfree(msg->mailbox_data); + kfree(msg); + spin_lock_irqsave(&drv->rx_msg_lock, irq_flags); + } + spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags); + return IRQ_HANDLED; +} +/** +* intel_sst_interrupt - Interrupt service routine for SST +* +* @irq: irq number of interrupt +* @context: pointer to device structre +* +* This function is called by OS when SST device raises +* an interrupt. This will be result of write in IPC register +* Source can be busy or done interrupt +*/ +static irqreturn_t intel_sst_intr_mfld(int irq, void *context) +{ + union interrupt_reg isr; + union ipc_header header; + irqreturn_t retval = IRQ_HANDLED; + struct ipc_post *msg = NULL; + unsigned int size = 0; + struct intel_sst_drv *drv = (struct intel_sst_drv *) context; + + /* Interrupt arrived, check src */ + isr.full = sst_shim_read(drv->shim, SST_ISRX); + if (isr.part.done_interrupt) { + /* Mask all interrupts till this one is processsed */ + set_imr_interrupts(drv, false); + /* Clear done bit */ + spin_lock(&drv->ipc_spin_lock); + header.full = sst_shim_read(drv->shim, drv->ipc_reg.ipcx); + header.part.done = 0; + sst_shim_write(drv->shim, drv->ipc_reg.ipcx, header.full); + /* write 1 to clear status register */; + isr.part.done_interrupt = 1; + sst_shim_write(drv->shim, SST_ISRX, isr.full); + spin_unlock(&drv->ipc_spin_lock); + queue_work(drv->post_msg_wq, &sst_drv_ctx->ipc_post_msg.wq); + + /* Un mask done and busy intr */ + set_imr_interrupts(drv, true); + retval = IRQ_HANDLED; + } + if (isr.part.busy_interrupt) { + /* Mask all interrupts till we process it in bottom half */ + set_imr_interrupts(drv, false); + header.full = sst_shim_read(drv->shim, drv->ipc_reg.ipcd); + if (sst_create_ipc_msg(&msg, header.part.large)) { + pr_err("No memory available\n"); + drv->ops->clear_interrupt(); + return IRQ_HANDLED; + } + if (header.part.large) { + size = header.part.data; + if (SST_VALIDATE_MAILBOX_SIZE(size)) { + memcpy_fromio(msg->mailbox_data, + drv->mailbox + drv->mailbox_recv_offset + 4, size); + } else { + pr_err("Mailbox not copied, payload siz is: %u\n", size); + header.part.data = 0; + } + } + msg->header = header; + msg->is_process_reply = + SST_IS_PROCESS_REPLY(msg->header.part.msg_id); + spin_lock(&drv->rx_msg_lock); + list_add_tail(&msg->node, &drv->rx_list); + spin_unlock(&drv->rx_msg_lock); + drv->ops->clear_interrupt(); + retval = IRQ_WAKE_THREAD; + } + return retval; +} + +static int sst_save_dsp_context_v2(struct intel_sst_drv *sst) +{ + unsigned int pvt_id; + struct ipc_post *msg = NULL; + struct ipc_dsp_hdr dsp_hdr; + struct sst_block *block; + + /*send msg to fw*/ + pvt_id = sst_assign_pvt_id(sst); + if (sst_create_block_and_ipc_msg(&msg, true, sst, &block, + IPC_CMD, pvt_id)) { + pr_err("msg/block alloc failed. Not proceeding with context save\n"); + return 0; + } + + sst_fill_header_mrfld(&msg->mrfld_header, IPC_CMD, + SST_TASK_ID_MEDIA, 1, pvt_id); + msg->mrfld_header.p.header_low_payload = sizeof(dsp_hdr); + msg->mrfld_header.p.header_high.part.res_rqd = 1; + sst_fill_header_dsp(&dsp_hdr, IPC_PREP_D3, PIPE_RSVD, pvt_id); + memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr)); + + sst_add_to_dispatch_list_and_post(sst, msg); + /*wait for reply*/ + if (sst_wait_timeout(sst, block)) { + pr_err("sst: err fw context save timeout ...\n"); + pr_err("not suspending FW!!!"); + sst_free_block(sst, block); + return -EIO; + } + if (block->ret_code) { + pr_err("fw responded w/ error %d", block->ret_code); + sst_free_block(sst, block); + return -EIO; + } + + sst_free_block(sst, block); + return 0; +} + +static int sst_save_dsp_context(struct intel_sst_drv *sst) +{ + struct snd_sst_ctxt_params fw_context; + unsigned int pvt_id; + struct ipc_post *msg = NULL; + struct sst_block *block; + pr_debug("%s: Enter\n", __func__); + + /*send msg to fw*/ + pvt_id = sst_assign_pvt_id(sst_drv_ctx); + if (sst_create_block_and_ipc_msg(&msg, true, sst_drv_ctx, &block, + IPC_IA_GET_FW_CTXT, pvt_id)) { + pr_err("msg/block alloc failed. Not proceeding with context save\n"); + return -ENOMEM; + } + sst_fill_header(&msg->header, IPC_IA_GET_FW_CTXT, 1, pvt_id); + msg->header.part.data = sizeof(fw_context) + sizeof(u32); + fw_context.address = virt_to_phys((void *)sst_drv_ctx->fw_cntx); + fw_context.size = FW_CONTEXT_MEM; + memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); + memcpy(msg->mailbox_data + sizeof(u32), + &fw_context, sizeof(fw_context)); + sst_add_to_dispatch_list_and_post(sst, msg); + /*wait for reply*/ + if (sst_wait_timeout(sst_drv_ctx, block)) + pr_err("sst: err fw context save timeout ...\n"); + pr_debug("fw context saved ...\n"); + if (block->ret_code) + sst_drv_ctx->fw_cntx_size = 0; + else + sst_drv_ctx->fw_cntx_size = *sst_drv_ctx->fw_cntx; + pr_debug("fw copied data %x\n", sst_drv_ctx->fw_cntx_size); + sst_free_block(sst_drv_ctx, block); + return 0; +} + +static struct intel_sst_ops mrfld_ops = { + .interrupt = intel_sst_interrupt_mrfld, + .irq_thread = intel_sst_irq_thread_mfld, + .clear_interrupt = intel_sst_clear_intr_mrfld, + .start = sst_start_mrfld, + .reset = intel_sst_reset_dsp_mrfld, + .post_message = sst_post_message_mrfld, + .sync_post_message = sst_sync_post_message_mrfld, + .process_message = sst_process_message_mrfld, + .process_reply = sst_process_reply_mrfld, + .save_dsp_context = sst_save_dsp_context_v2, + .alloc_stream = sst_alloc_stream_mrfld, + .post_download = sst_post_download_mrfld, + .do_recovery = sst_do_recovery_mrfld, +}; + +static struct intel_sst_ops mrfld_32_ops = { + .interrupt = intel_sst_intr_mfld, + .irq_thread = intel_sst_irq_thread_mfld, + .clear_interrupt = intel_sst_clear_intr_mfld, + .start = sst_start_mrfld, + .reset = intel_sst_reset_dsp_mrfld, + .post_message = sst_post_message_mfld, + .sync_post_message = sst_sync_post_message_mfld, + .process_message = sst_process_message_mfld, + .process_reply = sst_process_reply_mfld, + .save_dsp_context = sst_save_dsp_context, + .restore_dsp_context = sst_restore_fw_context, + .alloc_stream = sst_alloc_stream_ctp, + .post_download = sst_post_download_byt, + .do_recovery = sst_do_recovery, +}; + +static struct intel_sst_ops ctp_ops = { + .interrupt = intel_sst_intr_mfld, + .irq_thread = intel_sst_irq_thread_mfld, + .clear_interrupt = intel_sst_clear_intr_mfld, + .start = sst_start_mfld, + .reset = intel_sst_reset_dsp_mfld, + .post_message = sst_post_message_mfld, + .sync_post_message = sst_sync_post_message_mfld, + .process_message = sst_process_message_mfld, + .process_reply = sst_process_reply_mfld, + .set_bypass = intel_sst_set_bypass_mfld, + .save_dsp_context = sst_save_dsp_context, + .restore_dsp_context = sst_restore_fw_context, + .alloc_stream = sst_alloc_stream_ctp, + .post_download = sst_post_download_ctp, + .do_recovery = sst_do_recovery, +}; + +int sst_driver_ops(struct intel_sst_drv *sst) +{ + + switch (sst->pci_id) { + case SST_MRFLD_PCI_ID: + sst->tstamp = SST_TIME_STAMP_MRFLD; + sst->ops = &mrfld_ops; + return 0; + case SST_BYT_PCI_ID: + sst->tstamp = SST_TIME_STAMP_BYT; + sst->ops = &mrfld_32_ops; + return 0; + case SST_CLV_PCI_ID: + sst->tstamp = SST_TIME_STAMP; + sst->ops = &ctp_ops; + return 0; + default: + pr_err("SST Driver capablities missing for pci_id: %x", sst->pci_id); + return -EINVAL; + }; +} + +int sst_alloc_drv_context(struct device *dev) +{ + struct intel_sst_drv *ctx; + mutex_lock(&drv_ctx_lock); + if (sst_drv_ctx) { + pr_err("Only one sst handle is supported\n"); + mutex_unlock(&drv_ctx_lock); + return -EBUSY; + } + pr_debug("%s: %d", __func__, __LINE__); + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) { + pr_err("malloc fail\n"); + mutex_unlock(&drv_ctx_lock); + return -ENOMEM; + } + sst_drv_ctx = ctx; + mutex_unlock(&drv_ctx_lock); + return 0; +} + +static ssize_t sst_sysfs_get_recovery(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", ctx->sst_state); +} + + +static ssize_t sst_sysfs_set_recovery(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + long val; + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + if (kstrtol(buf, 0, &val)) + return -EINVAL; + + if (val == 1) { + if (!atomic_read(&ctx->pm_usage_count)) { + pr_debug("%s: set sst state to uninit...\n", __func__); + sst_set_fw_state_locked(ctx, SST_UN_INIT); + } else { + pr_err("%s: not setting sst state... %d\n", __func__, + atomic_read(&ctx->pm_usage_count)); + pr_err("Unrecoverable state....\n"); + BUG(); + return -EPERM; + } + } + + return len; +} + +static DEVICE_ATTR(audio_recovery, S_IRUGO | S_IWUSR, + sst_sysfs_get_recovery, sst_sysfs_set_recovery); + +int sst_request_firmware_async(struct intel_sst_drv *ctx) +{ + int ret = 0; + + snprintf(ctx->firmware_name, sizeof(ctx->firmware_name), + "%s%04x%s", "fw_sst_", + ctx->pci_id, ".bin"); + pr_debug("Requesting FW %s now...\n", ctx->firmware_name); + + trace_sst_fw_download("Request firmware async", ctx->sst_state); + + ret = request_firmware_nowait(THIS_MODULE, 1, ctx->firmware_name, + ctx->dev, GFP_KERNEL, ctx, sst_firmware_load_cb); + if (ret) + pr_err("could not load firmware %s error %d\n", ctx->firmware_name, ret); + + return ret; +} +/* +* intel_sst_probe - PCI probe function +* +* @pci: PCI device structure +* @pci_id: PCI device ID structure +* +* This function is called by OS when a device is found +* This enables the device, interrupt etc +*/ +static int intel_sst_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + int i, ret = 0; + struct intel_sst_ops *ops; + struct sst_platform_info *sst_pdata = pci->dev.platform_data; + int ddr_base; + u32 ssp_base_add; + u32 dma_base_add; + u32 len; + + + + pr_debug("Probe for DID %x\n", pci->device); + ret = sst_alloc_drv_context(&pci->dev); + if (ret) + return ret; + + sst_drv_ctx->dev = &pci->dev; + sst_drv_ctx->pci_id = pci->device; + if (!sst_pdata) + return -EINVAL; + sst_drv_ctx->pdata = sst_pdata; + + if (!sst_drv_ctx->pdata->probe_data) + return -EINVAL; + memcpy(&sst_drv_ctx->info, sst_drv_ctx->pdata->probe_data, + sizeof(sst_drv_ctx->info)); + + sst_drv_ctx->use_32bit_ops = sst_drv_ctx->pdata->ipc_info->use_32bit_ops; + sst_drv_ctx->mailbox_recv_offset = sst_drv_ctx->pdata->ipc_info->mbox_recv_off; + + if (0 != sst_driver_ops(sst_drv_ctx)) + return -EINVAL; + ops = sst_drv_ctx->ops; + mutex_init(&sst_drv_ctx->stream_lock); + mutex_init(&sst_drv_ctx->sst_lock); + mutex_init(&sst_drv_ctx->mixer_ctrl_lock); + mutex_init(&sst_drv_ctx->csr_lock); + + sst_drv_ctx->stream_cnt = 0; + sst_drv_ctx->fw_in_mem = NULL; + sst_drv_ctx->vcache.file1_in_mem = NULL; + sst_drv_ctx->vcache.file2_in_mem = NULL; + sst_drv_ctx->vcache.size1 = 0; + sst_drv_ctx->vcache.size2 = 0; + + /* we don't use dma, so set to 0*/ + sst_drv_ctx->use_dma = 0; //1; + sst_drv_ctx->use_lli = 1; + + INIT_LIST_HEAD(&sst_drv_ctx->memcpy_list); + INIT_LIST_HEAD(&sst_drv_ctx->libmemcpy_list); + + INIT_LIST_HEAD(&sst_drv_ctx->ipc_dispatch_list); + INIT_LIST_HEAD(&sst_drv_ctx->block_list); + INIT_LIST_HEAD(&sst_drv_ctx->rx_list); + INIT_WORK(&sst_drv_ctx->ipc_post_msg.wq, ops->post_message); + init_waitqueue_head(&sst_drv_ctx->wait_queue); + + sst_drv_ctx->mad_wq = create_singlethread_workqueue("sst_mad_wq"); + if (!sst_drv_ctx->mad_wq) + goto do_free_drv_ctx; + sst_drv_ctx->post_msg_wq = + create_singlethread_workqueue("sst_post_msg_wq"); + if (!sst_drv_ctx->post_msg_wq) + goto free_mad_wq; + + spin_lock_init(&sst_drv_ctx->ipc_spin_lock); + spin_lock_init(&sst_drv_ctx->block_lock); + spin_lock_init(&sst_drv_ctx->pvt_id_lock); + spin_lock_init(&sst_drv_ctx->rx_msg_lock); + + sst_drv_ctx->ipc_reg.ipcx = SST_IPCX + sst_drv_ctx->pdata->ipc_info->ipc_offset; + sst_drv_ctx->ipc_reg.ipcd = SST_IPCD + sst_drv_ctx->pdata->ipc_info->ipc_offset; + pr_debug("ipcx 0x%x ipxd 0x%x", sst_drv_ctx->ipc_reg.ipcx, + sst_drv_ctx->ipc_reg.ipcd); + + pr_info("Got drv data max stream %d\n", + sst_drv_ctx->info.max_streams); + for (i = 1; i <= sst_drv_ctx->info.max_streams; i++) { + struct stream_info *stream = &sst_drv_ctx->streams[i]; + memset(stream, 0, sizeof(*stream)); + stream->pipe_id = PIPE_RSVD; + mutex_init(&stream->lock); + } + + ret = sst_request_firmware_async(sst_drv_ctx); + if (ret) { + pr_err("Firmware download failed:%d\n", ret); + goto do_free_mem; + } + /* Init the device */ + ret = pci_enable_device(pci); + if (ret) { + pr_err("device can't be enabled\n"); + goto do_free_mem; + } + sst_drv_ctx->pci = pci_dev_get(pci); + ret = pci_request_regions(pci, SST_DRV_NAME); + if (ret) + goto do_disable_device; + /* map registers */ + /* SST Shim */ + + if (sst_drv_ctx->pci_id == SST_MRFLD_PCI_ID) { + sst_drv_ctx->ddr_base = pci_resource_start(pci, 0); + /* + * check that the relocated IMR base matches with FW Binary + * put temporary check till better soln is available for FW + */ + ddr_base = relocate_imr_addr_mrfld(sst_drv_ctx->ddr_base); + if (!sst_drv_ctx->pdata->lib_info) { + pr_err("%s:lib_info pointer NULL\n", __func__); + ret = -EINVAL; + goto do_release_regions; + } + if (ddr_base != sst_drv_ctx->pdata->lib_info->mod_base) { + pr_err("FW LSP DDR BASE does not match with IFWI\n"); + ret = -EINVAL; + goto do_release_regions; + } + sst_drv_ctx->ddr_end = pci_resource_end(pci, 0); + + sst_drv_ctx->ddr = pci_ioremap_bar(pci, 0); + if (!sst_drv_ctx->ddr) + goto do_unmap_ddr; + pr_debug("sst: DDR Ptr %p\n", sst_drv_ctx->ddr); + } else { + sst_drv_ctx->ddr = NULL; + } + + /* SHIM */ + sst_drv_ctx->shim_phy_add = pci_resource_start(pci, 1); + sst_drv_ctx->shim = pci_ioremap_bar(pci, 1); + if (!sst_drv_ctx->shim) + goto do_release_regions; + pr_debug("SST Shim Ptr %p\n", sst_drv_ctx->shim); + + /* Shared SRAM */ + sst_drv_ctx->mailbox_add = pci_resource_start(pci, 2); + sst_drv_ctx->mailbox = pci_ioremap_bar(pci, 2); + if (!sst_drv_ctx->mailbox) + goto do_unmap_shim; + pr_debug("SRAM Ptr %p\n", sst_drv_ctx->mailbox); + + /* IRAM */ + sst_drv_ctx->iram_end = pci_resource_end(pci, 3); + sst_drv_ctx->iram_base = pci_resource_start(pci, 3); + sst_drv_ctx->iram = pci_ioremap_bar(pci, 3); + if (!sst_drv_ctx->iram) + goto do_unmap_sram; + pr_debug("IRAM Ptr %p\n", sst_drv_ctx->iram); + + /* DRAM */ + sst_drv_ctx->dram_end = pci_resource_end(pci, 4); + sst_drv_ctx->dram_base = pci_resource_start(pci, 4); + sst_drv_ctx->dram = pci_ioremap_bar(pci, 4); + if (!sst_drv_ctx->dram) + goto do_unmap_iram; + pr_debug("DRAM Ptr %p\n", sst_drv_ctx->dram); + + if ((sst_pdata->pdata != NULL) && + (sst_pdata->debugfs_data != NULL)) { + if (sst_pdata->ssp_data != NULL) { + /* SSP Register */ + ssp_base_add = sst_pdata->ssp_data->base_add; + len = sst_pdata->debugfs_data->ssp_reg_size; + for (i = 0; i < sst_pdata->debugfs_data->num_ssp; i++) { + sst_drv_ctx->debugfs.ssp[i] = + devm_ioremap(&pci->dev, + ssp_base_add + (len * i), len); + if (!sst_drv_ctx->debugfs.ssp[i]) { + pr_warn("ssp ioremap failed\n"); + continue; + } + + pr_debug("\n ssp io 0x%p ssp 0x%x size 0x%x", + sst_drv_ctx->debugfs.ssp[i], + ssp_base_add, len); + } + } + + /* DMA Register */ + dma_base_add = sst_pdata->pdata->sst_dma_base[0]; + len = sst_pdata->debugfs_data->dma_reg_size; + for (i = 0; i < sst_pdata->debugfs_data->num_dma; i++) { + sst_drv_ctx->debugfs.dma_reg[i] = + devm_ioremap(&pci->dev, + dma_base_add + (len * i), len); + if (!sst_drv_ctx->debugfs.dma_reg[i]) { + pr_warn("dma ioremap failed\n"); + continue; + } + + pr_debug("\n dma io 0x%p ssp 0x%x size 0x%x", + sst_drv_ctx->debugfs.dma_reg[i], + dma_base_add, len); + } + } + + /* Do not access iram/dram etc before LPE is reset */ + + sst_drv_ctx->dump_buf.iram_buf.size = pci_resource_len(pci, 3); + sst_drv_ctx->dump_buf.iram_buf.buf = kzalloc(sst_drv_ctx->dump_buf.iram_buf.size, + GFP_KERNEL); + if (!sst_drv_ctx->dump_buf.iram_buf.buf) { + pr_err("%s: no memory\n", __func__); + ret = -ENOMEM; + goto do_unmap_dram; + } + + sst_drv_ctx->dump_buf.dram_buf.size = pci_resource_len(pci, 4); + sst_drv_ctx->dump_buf.dram_buf.buf = kzalloc(sst_drv_ctx->dump_buf.dram_buf.size, + GFP_KERNEL); + if (!sst_drv_ctx->dump_buf.dram_buf.buf) { + pr_err("%s: no memory\n", __func__); + ret = -ENOMEM; + goto do_free_iram_buf; + } + + pr_debug("\niram len 0x%x dram len 0x%x", + sst_drv_ctx->dump_buf.iram_buf.size, + sst_drv_ctx->dump_buf.dram_buf.size); + + if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID) { + sst_drv_ctx->probe_bytes = kzalloc(SST_MAX_BIN_BYTES, GFP_KERNEL); + if (!sst_drv_ctx->probe_bytes) { + pr_err("%s: no memory\n", __func__); + ret = -ENOMEM; + goto do_free_dram_buf; + } + } + + sst_set_fw_state_locked(sst_drv_ctx, SST_UN_INIT); + sst_drv_ctx->irq_num = pci->irq; + /* Register the ISR */ + ret = request_threaded_irq(pci->irq, sst_drv_ctx->ops->interrupt, + sst_drv_ctx->ops->irq_thread, 0, SST_DRV_NAME, + sst_drv_ctx); + if (ret) + goto do_free_probe_bytes; + pr_debug("Registered IRQ 0x%x\n", pci->irq); + + /*Register LPE Control as misc driver*/ + ret = misc_register(&lpe_ctrl); + if (ret) { + pr_err("couldn't register control device\n"); + goto do_free_irq; + } + /* default intr are unmasked so set this as masked */ + if (sst_drv_ctx->pci_id == SST_MRFLD_PCI_ID) + sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, 0xFFFF0038); + + if (sst_drv_ctx->use_32bit_ops) { + pr_debug("allocate mem for context save/restore\n "); + /*allocate mem for fw context save during suspend*/ + sst_drv_ctx->fw_cntx = kzalloc(FW_CONTEXT_MEM, GFP_KERNEL); + if (!sst_drv_ctx->fw_cntx) { + ret = -ENOMEM; + goto do_free_misc; + } + /*setting zero as that is valid mem to restore*/ + sst_drv_ctx->fw_cntx_size = 0; + } + if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID) { + u32 csr; + u32 csr2; + u32 clkctl; + + /*set lpe start clock and ram size*/ + csr = sst_shim_read(sst_drv_ctx->shim, SST_CSR); + csr |= 0x30000; + /*make sure clksel set to OSC for SSP0,1 (default)*/ + csr &= 0xFFFFFFF3; + sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr); + + /*set clock output enable for SSP0,1,3*/ + clkctl = sst_shim_read(sst_drv_ctx->shim, SST_CLKCTL); + if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID) + clkctl |= (0x7 << 16); + else + clkctl |= ((1<<16)|(1<<17)); + sst_shim_write(sst_drv_ctx->shim, SST_CLKCTL, clkctl); + + /* set SSP0 & SSP1 disable DMA Finish*/ + csr2 = sst_shim_read(sst_drv_ctx->shim, SST_CSR2); + /*set SSP3 disable DMA finsh for SSSP3 */ + csr2 |= BIT(1)|BIT(2); + sst_shim_write(sst_drv_ctx->shim, SST_CSR2, csr2); + } + if (sst_drv_ctx->pdata->ssp_data) { + if (sst_drv_ctx->pdata->ssp_data->gpio_in_use) + sst_set_gpio_conf(&sst_drv_ctx->pdata->ssp_data->gpio); + } + pci_set_drvdata(pci, sst_drv_ctx); + pm_runtime_allow(sst_drv_ctx->dev); + pm_runtime_put_noidle(sst_drv_ctx->dev); + register_sst(sst_drv_ctx->dev); + sst_debugfs_init(sst_drv_ctx); + sst_drv_ctx->qos = kzalloc(sizeof(struct pm_qos_request), + GFP_KERNEL); + if (!sst_drv_ctx->qos) + goto do_free_misc; + pm_qos_add_request(sst_drv_ctx->qos, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + + ret = device_create_file(sst_drv_ctx->dev, &dev_attr_audio_recovery); + if (ret) { + pr_err("could not create sysfs %s file\n", + dev_attr_audio_recovery.attr.name); + goto do_free_qos; + } + + pr_info("%s successfully done!\n", __func__); + return ret; + +do_free_qos: + pm_qos_remove_request(sst_drv_ctx->qos); + kfree(sst_drv_ctx->qos); +do_free_misc: + misc_deregister(&lpe_ctrl); +do_free_irq: + free_irq(pci->irq, sst_drv_ctx); +do_free_probe_bytes: + if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID) + kfree(sst_drv_ctx->probe_bytes); +do_free_dram_buf: +#ifdef CONFIG_DEBUG_FS + if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID) + kfree(sst_drv_ctx->dump_buf.dram_buf.buf); +do_free_iram_buf: + if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID) + kfree(sst_drv_ctx->dump_buf.iram_buf.buf); +#endif +do_unmap_dram: + iounmap(sst_drv_ctx->dram); +do_unmap_iram: + iounmap(sst_drv_ctx->iram); +do_unmap_sram: + iounmap(sst_drv_ctx->mailbox); +do_unmap_shim: + iounmap(sst_drv_ctx->shim); + +do_unmap_ddr: + if (sst_drv_ctx->ddr) + iounmap(sst_drv_ctx->ddr); + +do_release_regions: + pci_release_regions(pci); +do_disable_device: + pci_disable_device(pci); +do_free_mem: + destroy_workqueue(sst_drv_ctx->post_msg_wq); +free_mad_wq: + destroy_workqueue(sst_drv_ctx->mad_wq); +do_free_drv_ctx: + sst_drv_ctx = NULL; + pr_err("Probe failed with %d\n", ret); + return ret; +} + +/** +* intel_sst_remove - PCI remove function +* +* @pci: PCI device structure +* +* This function is called by OS when a device is unloaded +* This frees the interrupt etc +*/ +static void intel_sst_remove(struct pci_dev *pci) +{ + struct intel_sst_drv *sst_drv_ctx = pci_get_drvdata(pci); + sst_debugfs_exit(sst_drv_ctx); + pm_runtime_get_noresume(sst_drv_ctx->dev); + pm_runtime_forbid(sst_drv_ctx->dev); + unregister_sst(sst_drv_ctx->dev); + pci_dev_put(sst_drv_ctx->pci); + sst_set_fw_state_locked(sst_drv_ctx, SST_UN_INIT); + misc_deregister(&lpe_ctrl); + free_irq(pci->irq, sst_drv_ctx); + + iounmap(sst_drv_ctx->dram); + iounmap(sst_drv_ctx->iram); + iounmap(sst_drv_ctx->mailbox); + iounmap(sst_drv_ctx->shim); +#ifdef CONFIG_DEBUG_FS + if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID) { + kfree(sst_drv_ctx->dump_buf.iram_buf.buf); + kfree(sst_drv_ctx->dump_buf.dram_buf.buf); + } +#endif + if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID) + kfree(sst_drv_ctx->probe_bytes); + + device_remove_file(sst_drv_ctx->dev, &dev_attr_audio_recovery); + kfree(sst_drv_ctx->fw_cntx); + kfree(sst_drv_ctx->runtime_param.param.addr); + flush_scheduled_work(); + destroy_workqueue(sst_drv_ctx->post_msg_wq); + destroy_workqueue(sst_drv_ctx->mad_wq); + pm_qos_remove_request(sst_drv_ctx->qos); + kfree(sst_drv_ctx->qos); + kfree(sst_drv_ctx->fw_sg_list.src); + kfree(sst_drv_ctx->fw_sg_list.dst); + sst_drv_ctx->fw_sg_list.list_len = 0; + kfree(sst_drv_ctx->fw_in_mem); + sst_drv_ctx->fw_in_mem = NULL; + sst_memcpy_free_resources(); + sst_drv_ctx = NULL; + pci_release_regions(pci); + pci_disable_device(pci); + pci_set_drvdata(pci, NULL); +} + +inline void sst_save_shim64(struct intel_sst_drv *ctx, + void __iomem *shim, + struct sst_shim_regs64 *shim_regs) +{ + unsigned long irq_flags; + spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags); + + shim_regs->csr = sst_shim_read64(shim, SST_CSR), + shim_regs->pisr = sst_shim_read64(shim, SST_PISR), + shim_regs->pimr = sst_shim_read64(shim, SST_PIMR), + shim_regs->isrx = sst_shim_read64(shim, SST_ISRX), + shim_regs->isrd = sst_shim_read64(shim, SST_ISRD), + shim_regs->imrx = sst_shim_read64(shim, SST_IMRX), + shim_regs->imrd = sst_shim_read64(shim, SST_IMRD), + shim_regs->ipcx = sst_shim_read64(shim, ctx->ipc_reg.ipcx), + shim_regs->ipcd = sst_shim_read64(shim, ctx->ipc_reg.ipcd), + shim_regs->isrsc = sst_shim_read64(shim, SST_ISRSC), + shim_regs->isrlpesc = sst_shim_read64(shim, SST_ISRLPESC), + shim_regs->imrsc = sst_shim_read64(shim, SST_IMRSC), + shim_regs->imrlpesc = sst_shim_read64(shim, SST_IMRLPESC), + shim_regs->ipcsc = sst_shim_read64(shim, SST_IPCSC), + shim_regs->ipclpesc = sst_shim_read64(shim, SST_IPCLPESC), + shim_regs->clkctl = sst_shim_read64(shim, SST_CLKCTL), + shim_regs->csr2 = sst_shim_read64(shim, SST_CSR2); + + spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags); +} + +static inline void sst_restore_shim64(struct intel_sst_drv *ctx, + void __iomem *shim, + struct sst_shim_regs64 *shim_regs) +{ + unsigned long irq_flags; + spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags); + sst_shim_write64(shim, SST_IMRX, shim_regs->imrx), + spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags); +} + +/* + * The runtime_suspend/resume is pretty much similar to the legacy + * suspend/resume with the noted exception below: The PCI core takes care of + * taking the system through D3hot and restoring it back to D0 and so there is + * no need to duplicate that here. + */ +static int intel_sst_runtime_suspend(struct device *dev) +{ + union config_status_reg csr; + int ret = 0; + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + pr_info("runtime_suspend called\n"); + if (ctx->sst_state == SST_UN_INIT) { + pr_debug("LPE is already in UNINIT state, No action"); + return 0; + } + /*save fw context*/ + if (ctx->ops->save_dsp_context(ctx)) + return -EBUSY; + + if (ctx->pci_id == SST_CLV_PCI_ID) { + /*Assert RESET on LPE Processor*/ + csr.full = sst_shim_read(ctx->shim, SST_CSR); + ctx->csr_value = csr.full; + csr.full = csr.full | 0x2; + sst_shim_write(ctx->shim, SST_CSR, csr.full); + } + + /* Move the SST state to Suspended */ + sst_set_fw_state_locked(ctx, SST_SUSPENDED); + + flush_workqueue(ctx->post_msg_wq); + synchronize_irq(ctx->irq_num); + + if (ctx->pci_id == SST_BYT_PCI_ID || ctx->pci_id == SST_CHT_PCI_ID) { + /* save the shim registers because PMC doesn't save state */ + sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64); + } + return ret; +} + +static int intel_sst_runtime_resume(struct device *dev) +{ + u32 csr; + int ret = 0; + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + pr_info("runtime_resume called\n"); + + if (ctx->pci_id == SST_BYT_PCI_ID || ctx->pci_id == SST_CHT_PCI_ID) { + /* wait for device power up a/c to PCI spec */ + usleep_range(10000, 11000); + sst_restore_shim64(ctx, ctx->shim, ctx->shim_regs64); + } + + if (ctx->pci_id == SST_CLV_PCI_ID) { + csr = sst_shim_read(ctx->shim, SST_CSR); + /* + * To restore the csr_value after S0ix and S3 states. + * The value 0x30000 is to enable LPE dram high and low addresses. + * Reference: + * Penwell Audio Voice Module HAS 1.61 Section - 13.12.1 - + * CSR - Configuration and Status Register. + */ + csr |= (ctx->csr_value | 0x30000); + sst_shim_write(ctx->shim, SST_CSR, csr); + if (sst_drv_ctx->pdata->ssp_data) { + if (ctx->pdata->ssp_data->gpio_in_use) + sst_set_gpio_conf(&ctx->pdata->ssp_data->gpio); + } + } + /* When fw_clear_cache is set, clear the cached firmware copy */ + /* fw_clear_cache is set through debugfs support */ + if (atomic_read(&ctx->fw_clear_cache) && ctx->fw_in_mem) { + pr_debug("Clearing the cached firmware\n"); + kfree(ctx->fw_in_mem); + ctx->fw_in_mem = NULL; + atomic_set(&ctx->fw_clear_cache, 0); + } + + sst_set_fw_state_locked(ctx, SST_UN_INIT); + return ret; +} + +static int intel_sst_suspend(struct device *dev) +{ + int retval = 0, usage_count; + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + usage_count = atomic_read(&ctx->pm_usage_count); + if (usage_count) { + pr_err("Ret error for suspend:%d\n", usage_count); + return -EBUSY; + } + retval = intel_sst_runtime_suspend(dev); + + return retval; +} + +static int intel_sst_runtime_idle(struct device *dev) +{ + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + pr_info("runtime_idle called\n"); + if (ctx->sst_state != SST_UN_INIT) { + pm_schedule_suspend(dev, SST_SUSPEND_DELAY); + return -EBUSY; + } else { + return 0; + } + return -EBUSY; + +} + +static void sst_do_shutdown(struct intel_sst_drv *ctx) +{ + int retval = 0; + unsigned int pvt_id; + struct ipc_post *msg = NULL; + struct sst_block *block = NULL; + + pr_debug(" %s called\n", __func__); + if (ctx->sst_state == SST_SUSPENDED || + ctx->sst_state == SST_UN_INIT) { + sst_set_fw_state_locked(ctx, SST_SHUTDOWN); + pr_debug("sst is already in suspended/un-int state\n"); + return; + } + if (!ctx->use_32bit_ops) + return; + + sst_set_fw_state_locked(ctx, SST_SHUTDOWN); + flush_workqueue(ctx->post_msg_wq); + pvt_id = sst_assign_pvt_id(ctx); + retval = sst_create_block_and_ipc_msg(&msg, false, + ctx, &block, + IPC_IA_PREPARE_SHUTDOWN, pvt_id); + if (retval) { + pr_err("sst_create_block returned error!\n"); + return; + } + sst_fill_header(&msg->header, IPC_IA_PREPARE_SHUTDOWN, 0, pvt_id); + sst_add_to_dispatch_list_and_post(ctx, msg); + sst_wait_timeout(ctx, block); + sst_free_block(ctx, block); +} + + +/** +* sst_pci_shutdown - PCI shutdown function +* +* @pci: PCI device structure +* +* This function is called by OS when a device is shutdown/reboot +* +*/ + +static void sst_pci_shutdown(struct pci_dev *pci) +{ + struct intel_sst_drv *ctx = pci_get_drvdata(pci); + + pr_debug(" %s called\n", __func__); + + sst_do_shutdown(ctx); + disable_irq_nosync(pci->irq); +} + +/** +* sst_acpi_shutdown - platform shutdown function +* +* @pci: Platform device structure +* +* This function is called by OS when a device is shutdown/reboot +* +*/ +static void sst_acpi_shutdown(struct platform_device *pdev) +{ + struct intel_sst_drv *ctx = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + pr_debug(" %s called\n", __func__); + + sst_do_shutdown(ctx); + disable_irq_nosync(irq); +} + +static const struct dev_pm_ops intel_sst_pm = { + .suspend = intel_sst_suspend, + .resume = intel_sst_runtime_resume, + .runtime_suspend = intel_sst_runtime_suspend, + .runtime_resume = intel_sst_runtime_resume, + .runtime_idle = intel_sst_runtime_idle, +}; + +static const struct acpi_device_id sst_acpi_ids[]; + +struct sst_platform_info *sst_get_acpi_driver_data(const char *hid) +{ + const struct acpi_device_id *id; + + pr_debug("%s", __func__); + for (id = sst_acpi_ids; id->id[0]; id++) + if (!strncmp(id->id, hid, 16)) + return (struct sst_platform_info *)id->driver_data; + return NULL; +} + +/* PCI Routines */ +static DEFINE_PCI_DEVICE_TABLE(intel_sst_ids) = { + { PCI_VDEVICE(INTEL, SST_CLV_PCI_ID), 0}, + { PCI_VDEVICE(INTEL, SST_MRFLD_PCI_ID), 0}, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, intel_sst_ids); + +static const struct acpi_device_id sst_acpi_ids[] = { + { "LPE0F28", (kernel_ulong_t) &byt_rvp_platform_data }, + { "LPE0F281", (kernel_ulong_t) &byt_ffrd8_platform_data }, + { "80860F28", (kernel_ulong_t) &byt_ffrd8_platform_data }, + { "808622A8", (kernel_ulong_t) &cht_platform_data }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, sst_acpi_ids); + +static struct pci_driver driver = { + .name = SST_DRV_NAME, + .id_table = intel_sst_ids, + .probe = intel_sst_probe, + .remove = intel_sst_remove, + .shutdown = sst_pci_shutdown, +#ifdef CONFIG_PM + .driver = { + .pm = &intel_sst_pm, + }, +#endif +}; + +static struct platform_driver sst_acpi_driver = { + .driver = { + .name = "intel_sst_acpi", + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(sst_acpi_ids), + .pm = &intel_sst_pm, + }, + .probe = sst_acpi_probe, + .remove = sst_acpi_remove, + .shutdown = sst_acpi_shutdown, +}; + + +/** +* intel_sst_init - Module init function +* +* Registers with PCI +* Registers with /dev +* Init all data strutures +*/ +static int __init intel_sst_init(void) +{ + /* Init all variables, data structure etc....*/ + int ret = 0; + pr_info("INFO: ******** SST DRIVER loading.. Ver: %s\n", + SST_DRIVER_VERSION); + + mutex_init(&drv_ctx_lock); + /* Register with PCI */ + ret = pci_register_driver(&driver); + if (ret) + pr_err("PCI register failed\n"); + + ret = platform_driver_register(&sst_acpi_driver); + if (ret) + pr_err("ACPI register failed\n"); + return ret; +} + +/** +* intel_sst_exit - Module exit function +* +* Unregisters with PCI +* Unregisters with /dev +* Frees all data strutures +*/ +static void __exit intel_sst_exit(void) +{ + pci_unregister_driver(&driver); + platform_driver_unregister(&sst_acpi_driver); + + pr_debug("driver unloaded\n"); + sst_drv_ctx = NULL; + return; +} + +module_init(intel_sst_init); +module_exit(intel_sst_exit); diff --git a/sound/soc/intel/sst/sst.h b/sound/soc/intel/sst/sst.h new file mode 100644 index 000000000000..4ef3efa59db5 --- /dev/null +++ b/sound/soc/intel/sst/sst.h @@ -0,0 +1,932 @@ +/* + * sst.h - Intel SST Driver for audio engine + * + * Copyright (C) 2008-10 Intel Corporation + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R + * KP Jeeja + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Common private declarations for SST + */ +#ifndef __SST_H__ +#define __SST_H__ + +#include +#include +#include +#include +#include +#include +#include + +#define SST_DRIVER_VERSION "3.0.8" + +/* driver names */ +#define SST_DRV_NAME "intel_sst_driver" +#define SST_CLV_PCI_ID 0x08E7 +#define SST_MRFLD_PCI_ID 0x119A +#define SST_BYT_PCI_ID 0x0F28 +#define SST_CHT_PCI_ID 0x22A8 + +#define SST_SUSPEND_DELAY 2000 +#define FW_CONTEXT_MEM (64*1024) +#define SST_ICCM_BOUNDARY 4 +#define SST_CONFIG_SSP_SIGN 0x7ffe8001 + +/* FIXME: All this info should come from platform data + * move this when the base framework is ready to pass + * platform data to SST driver + */ +#define MRFLD_FW_VIRTUAL_BASE 0xC0000000 +#define MRFLD_FW_DDR_BASE_OFFSET 0x0 +#define MRFLD_FW_FEATURE_BASE_OFFSET 0x4 +#define MRFLD_FW_BSS_RESET_BIT 0 +extern struct intel_sst_drv *sst_drv_ctx; +enum sst_states { + SST_FW_LOADED = 1, + SST_FW_RUNNING, + SST_START_INIT, + SST_UN_INIT, + SST_ERROR, + SST_SUSPENDED, + SST_FW_CTXT_RESTORE, + SST_SHUTDOWN, + SST_FW_LIB_LOAD, +}; + +enum sst_algo_ops { + SST_SET_ALGO = 0, + SST_GET_ALGO = 1, +}; + +#define SST_BLOCK_TIMEOUT 1000 + +/* SST register map */ +#define SST_CSR 0x00 +#define SST_PISR 0x08 +#define SST_PIMR 0x10 +#define SST_ISRX 0x18 +#define SST_ISRD 0x20 +#define SST_IMRX 0x28 +#define SST_IMRD 0x30 +#define SST_IPCX 0x38 /* IPC IA-SST */ +#define SST_IPCD 0x40 /* IPC SST-IA */ +#define SST_ISRSC 0x48 +#define SST_ISRLPESC 0x50 +#define SST_IMRSC 0x58 +#define SST_IMRLPESC 0x60 +#define SST_IPCSC 0x68 +#define SST_IPCLPESC 0x70 +#define SST_CLKCTL 0x78 +#define SST_CSR2 0x80 + +#define SST_SHIM_BEGIN SST_CSR +#define SST_SHIM_END SST_CSR2 +#define SST_SHIM_SIZE 0x88 + +#define FW_SIGNATURE_SIZE 4 + +/* stream states */ +enum sst_stream_states { + STREAM_UN_INIT = 0, /* Freed/Not used stream */ + STREAM_RUNNING = 1, /* Running */ + STREAM_PAUSED = 2, /* Paused stream */ + STREAM_DECODE = 3, /* stream is in decoding only state */ + STREAM_INIT = 4, /* stream init, waiting for data */ + STREAM_RESET = 5, /* force reset on recovery */ +}; + +enum sst_ram_type { + SST_IRAM = 1, + SST_DRAM = 2, +}; + +/* SST shim registers to structure mapping */ +union config_status_reg { + struct { + u32 mfld_strb:1; + u32 sst_reset:1; + u32 clk_sel:3; + u32 sst_clk:2; + u32 bypass:3; + u32 run_stall:1; + u32 rsvd1:2; + u32 strb_cntr_rst:1; + u32 rsvd:18; + } part; + u32 full; +}; + +union interrupt_reg { + struct { + u64 done_interrupt:1; + u64 busy_interrupt:1; + u64 rsvd:62; + } part; + u64 full; +}; + +union sst_imr_reg { + struct { + u32 done_interrupt:1; + u32 busy_interrupt:1; + u32 rsvd:30; + } part; + u32 full; +}; + +union sst_pisr_reg { + struct { + u32 pssp0:1; + u32 pssp1:1; + u32 rsvd0:3; + u32 dmac:1; + u32 rsvd1:26; + } part; + u32 full; +}; + +union sst_pimr_reg { + struct { + u32 ssp0:1; + u32 ssp1:1; + u32 rsvd0:3; + u32 dmac:1; + u32 rsvd1:10; + u32 ssp0_sc:1; + u32 ssp1_sc:1; + u32 rsvd2:3; + u32 dmac_sc:1; + u32 rsvd3:10; + } part; + u32 full; +}; + +union config_status_reg_mrfld { + struct { + u64 lpe_reset:1; + u64 lpe_reset_vector:1; + u64 runstall:1; + u64 pwaitmode:1; + u64 clk_sel:3; + u64 rsvd2:1; + u64 sst_clk:3; + u64 xt_snoop:1; + u64 rsvd3:4; + u64 clk_sel1:6; + u64 clk_enable:3; + u64 rsvd4:6; + u64 slim0baseclk:1; + u64 rsvd:32; + } part; + u64 full; +}; + +union interrupt_reg_mrfld { + struct { + u64 done_interrupt:1; + u64 busy_interrupt:1; + u64 rsvd:62; + } part; + u64 full; +}; + +union sst_imr_reg_mrfld { + struct { + u64 done_interrupt:1; + u64 busy_interrupt:1; + u64 rsvd:62; + } part; + u64 full; +}; + +/*This structure is used to block a user/fw data call to another +fw/user call +*/ +struct sst_block { + bool condition; /* condition for blocking check */ + int ret_code; /* ret code when block is released */ + void *data; /* data to be appsed for block if any */ + u32 size; + bool on; + u32 msg_id; /*msg_id = msgid in mfld/ctp, mrfld = 0 */ + u32 drv_id; /* = str_id in mfld/ctp, = drv_id in mrfld*/ + struct list_head node; +}; + +/** + * struct stream_info - structure that holds the stream information + * + * @status : stream current state + * @prev : stream prev state + * @ops : stream operation pb/cp/drm... + * @bufs: stream buffer list + * @lock : stream mutex for protecting state + * @pcm_substream : PCM substream + * @period_elapsed : PCM period elapsed callback + * @sfreq : stream sampling freq + * @str_type : stream type + * @cumm_bytes : cummulative bytes decoded + * @str_type : stream type + * @src : stream source + * @device : output device type (medfield only) + */ +struct stream_info { + unsigned int status; + unsigned int prev; + unsigned int ops; + struct mutex lock; /* mutex */ + void *pcm_substream; + void (*period_elapsed) (void *pcm_substream); + unsigned int sfreq; + u32 cumm_bytes; + void *compr_cb_param; + void (*compr_cb) (void *compr_cb_param); + void *drain_cb_param; + void (*drain_notify) (void *drain_cb_param); + + unsigned int num_ch; + unsigned int pipe_id; + unsigned int str_id; + unsigned int task_id; +}; + +#define SST_FW_SIGN "$SST" +#define SST_FW_LIB_SIGN "$LIB" + +/* + * struct fw_header - FW file headers + * + * @signature : FW signature + * @modules : # of modules + * @file_format : version of header format + * @reserved : reserved fields + */ +struct fw_header { + unsigned char signature[FW_SIGNATURE_SIZE]; /* FW signature */ + u32 file_size; /* size of fw minus this header */ + u32 modules; /* # of modules */ + u32 file_format; /* version of header format */ + u32 reserved[4]; +}; + +struct fw_module_header { + unsigned char signature[FW_SIGNATURE_SIZE]; /* module signature */ + u32 mod_size; /* size of module */ + u32 blocks; /* # of blocks */ + u32 type; /* codec type, pp lib */ + u32 entry_point; +}; + +struct fw_block_info { + enum sst_ram_type type; /* IRAM/DRAM */ + u32 size; /* Bytes */ + u32 ram_offset; /* Offset in I/DRAM */ + u32 rsvd; /* Reserved field */ +}; + +struct sst_ipc_msg_wq { + union ipc_header_mrfld mrfld_header; + struct ipc_dsp_hdr dsp_hdr; + char mailbox[SST_MAILBOX_SIZE]; + struct work_struct wq; + union ipc_header header; +}; + +struct sst_dma { + struct dma_chan *ch; + struct intel_mid_dma_slave slave; + struct device *dev; +}; + +struct sst_runtime_param { + struct snd_sst_runtime_params param; +}; + +struct sst_sg_list { + struct scatterlist *src; + struct scatterlist *dst; + int list_len; + unsigned int sg_idx; +}; + +struct sst_memcpy_list { + struct list_head memcpylist; + void *dstn; + const void *src; + u32 size; + bool is_io; +}; + +struct sst_debugfs { +#ifdef CONFIG_DEBUG_FS + struct dentry *root; +#endif + int runtime_pm_status; + void __iomem *ssp[SST_MAX_SSP_PORTS]; + void __iomem *dma_reg[SST_MAX_DMA]; + unsigned char get_params_data[1024]; + ssize_t get_params_len; +}; + +struct lpe_log_buf_hdr { + u32 base_addr; + u32 end_addr; + u32 rd_addr; + u32 wr_addr; +}; + +struct snd_ssp_config { + int size; + char bytes[0]; +}; + +struct snd_sst_probe_bytes { + u16 len; + char bytes[0]; +}; + +#define PCI_DMAC_CLV_ID 0x08F0 +#define PCI_DMAC_MRFLD_ID 0x119B + +struct sst_ram_buf { + u32 size; + char *buf; +}; + +/* Firmware Module Information*/ + +enum sst_lib_dwnld_status { + SST_LIB_NOT_FOUND = 0, + SST_LIB_FOUND, + SST_LIB_DOWNLOADED, +}; + +struct sst_module_info { + const char *name; /* Library name */ + u32 id; /* Module ID */ + u32 entry_pt; /* Module entry point */ + u8 status; /* module status*/ + u8 rsvd1; + u16 rsvd2; +}; + +/* Structure for managing the Library Region(1.5MB) + * in DDR in Merrifield + */ +struct sst_mem_mgr { + phys_addr_t current_base; + int avail; + unsigned int count; +}; + +struct sst_dump_buf { + /* buffers for iram-dram dump crash */ + struct sst_ram_buf iram_buf; + struct sst_ram_buf dram_buf; +}; + +struct sst_ipc_reg { + int ipcx; + int ipcd; +}; + +struct sst_shim_regs64 { + u64 csr; + u64 pisr; + u64 pimr; + u64 isrx; + u64 isrd; + u64 imrx; + u64 imrd; + u64 ipcx; + u64 ipcd; + u64 isrsc; + u64 isrlpesc; + u64 imrsc; + u64 imrlpesc; + u64 ipcsc; + u64 ipclpesc; + u64 clkctl; + u64 csr2; +}; + +struct sst_vtsv_cache { + void *file1_in_mem; + u32 size1; + void *file2_in_mem; + u32 size2; +}; + +/*** + * + * struct intel_sst_drv - driver ops + * + * @sst_state : current sst device state + * @pci_id : PCI device id loaded + * @shim : SST shim pointer + * @mailbox : SST mailbox pointer + * @iram : SST IRAM pointer + * @dram : SST DRAM pointer + * @pdata : SST info passed as a part of pci platform data + * @shim_phy_add : SST shim phy addr + * @shim_regs64: Struct to save shim registers + * @ipc_dispatch_list : ipc messages dispatched + * @rx_list : to copy the process_reply/process_msg from DSP + * @ipc_post_msg_wq : wq to post IPC messages context + * @ipc_post_msg : wq to post reply from FW context + * @mad_ops : MAD driver operations registered + * @mad_wq : MAD driver wq + * @post_msg_wq : wq to post IPC messages + * @streams : sst stream contexts + * @list_lock : sst driver list lock (deprecated) + * @ipc_spin_lock : spin lock to handle audio shim access and ipc queue + * @rx_msg_lock : spin lock to handle the rx messages from the DSP + * @scard_ops : sst card ops + * @pci : sst pci device struture + * @dev : pointer to current device struct + * @sst_lock : sst device lock + * @stream_lock : sst stream lock + * @pvt_id : sst private id + * @stream_cnt : total sst active stream count + * @pb_streams : total active pb streams + * @cp_streams : total active cp streams + * @audio_start : audio status + * @qos : PM Qos struct + * firmware_name : Firmware / Library name + */ +struct intel_sst_drv { + int sst_state; + int irq_num; + unsigned int pci_id; + bool use_32bit_ops; + void __iomem *ddr; + void __iomem *shim; + void __iomem *mailbox; + void __iomem *iram; + void __iomem *dram; + unsigned int mailbox_add; + unsigned int iram_base; + unsigned int dram_base; + unsigned int shim_phy_add; + unsigned int iram_end; + unsigned int dram_end; + unsigned int ddr_end; + unsigned int ddr_base; + unsigned int mailbox_recv_offset; + atomic_t pm_usage_count; + struct sst_shim_regs64 *shim_regs64; + struct list_head block_list; + struct list_head ipc_dispatch_list; + struct sst_platform_info *pdata; + struct sst_ipc_msg_wq ipc_post_msg; + struct list_head rx_list; + struct work_struct ipc_post_msg_wq; + wait_queue_head_t wait_queue; + struct workqueue_struct *mad_wq; + struct workqueue_struct *post_msg_wq; + unsigned int tstamp; + struct stream_info streams[MAX_NUM_STREAMS+1]; /*str_id 0 is not used*/ + spinlock_t ipc_spin_lock; /* lock for Shim reg access and ipc queue */ + spinlock_t block_lock; /* lock for adding block to block_list */ + spinlock_t pvt_id_lock; /* lock for allocating private id */ + spinlock_t rx_msg_lock; + struct pci_dev *pci; + struct device *dev; + unsigned int pvt_id; + struct mutex sst_lock; + struct mutex stream_lock; + unsigned int stream_cnt; + unsigned int *fw_cntx; + unsigned int fw_cntx_size; + unsigned int csr_value; + struct sst_dma dma; + void *fw_in_mem; + struct sst_runtime_param runtime_param; + unsigned int device_input_mixer; + struct mutex mixer_ctrl_lock; + struct dma_async_tx_descriptor *desc; + struct sst_sg_list fw_sg_list, library_list; + struct intel_sst_ops *ops; + struct sst_debugfs debugfs; + struct pm_qos_request *qos; + struct sst_info info; + unsigned int use_dma; + unsigned int use_lli; + atomic_t fw_clear_context; + atomic_t fw_clear_cache; + bool lib_dwnld_reqd; + /* list used during FW download in memcpy mode */ + struct list_head memcpy_list; + /* list used during LIB download in memcpy mode */ + struct list_head libmemcpy_list; + /* holds the stucts of iram/dram local buffers for dump*/ + struct sst_dump_buf dump_buf; + /* Lock for CSR register change */ + struct mutex csr_lock; + /* byte control to set the probe stream */ + struct snd_sst_probe_bytes *probe_bytes; + /* contains the ipc registers */ + struct sst_ipc_reg ipc_reg; + /* IMR region Library space memory manager */ + struct sst_mem_mgr lib_mem_mgr; + /* Contains the cached vtsv files*/ + struct sst_vtsv_cache vcache; + /* Pointer to device ID, now for same PCI_ID, HID will be + * will be different for FDK and EDK2. This will be used + * for devices where PCI or ACPI id is same but HID is + * different + */ + const char *hid; + /* Holder for firmware name. Due to async call it needs to be + * persistent till worker thread gets called + */ + char firmware_name[20]; +}; + +extern struct intel_sst_drv *sst_drv_ctx; +extern struct sst_platform_info byt_rvp_platform_data; +extern struct sst_platform_info byt_ffrd8_platform_data; +extern struct sst_platform_info cht_platform_data; + +/* misc definitions */ +#define FW_DWNL_ID 0xFF + +struct sst_fill_config { + u32 sign; + struct sst_board_config_data sst_bdata; + struct sst_platform_config_data sst_pdata; + u32 shim_phy_add; + u32 mailbox_add; +} __packed; + +struct intel_sst_ops { + irqreturn_t (*interrupt) (int, void *); + irqreturn_t (*irq_thread) (int, void *); + void (*clear_interrupt) (void); + int (*start) (void); + int (*reset) (void); + void (*process_reply) (struct ipc_post *msg); + void (*post_message) (struct work_struct *work); + int (*sync_post_message) (struct ipc_post *msg); + void (*process_message) (struct ipc_post *msg); + void (*set_bypass)(bool set); + int (*save_dsp_context) (struct intel_sst_drv *sst); + void (*restore_dsp_context) (void); + int (*alloc_stream) (char *params, struct sst_block *block); + void (*post_download)(struct intel_sst_drv *sst); + void (*do_recovery)(struct intel_sst_drv *sst); +}; + +int sst_alloc_stream(char *params, struct sst_block *block); +int sst_pause_stream(int id); +int sst_resume_stream(int id); +int sst_drop_stream(int id); +int sst_next_track(void); +int sst_free_stream(int id); +int sst_start_stream(int str_id); +int sst_send_byte_stream_mrfld(void *sbytes); +int sst_send_probe_bytes(struct intel_sst_drv *sst); +int sst_set_stream_param(int str_id, struct snd_sst_params *str_param); +int sst_set_metadata(int str_id, char *params); +int sst_get_stream(struct snd_sst_params *str_param); +int sst_get_stream_allocated(struct snd_sst_params *str_param, + struct snd_sst_lib_download **lib_dnld); +int sst_drain_stream(int str_id, bool partial_drain); + + +int sst_sync_post_message_mfld(struct ipc_post *msg); +void sst_post_message_mfld(struct work_struct *work); +void sst_process_message_mfld(struct ipc_post *msg); +void sst_process_reply_mfld(struct ipc_post *msg); +int sst_start_mfld(void); +int intel_sst_reset_dsp_mfld(void); +void intel_sst_clear_intr_mfld(void); +void intel_sst_set_bypass_mfld(bool set); + +int sst_sync_post_message_mrfld(struct ipc_post *msg); +void sst_post_message_mrfld(struct work_struct *work); +void sst_process_message_mrfld(struct ipc_post *msg); +void sst_process_reply_mrfld(struct ipc_post *msg); +int sst_start_mrfld(void); +int intel_sst_reset_dsp_mrfld(void); +void intel_sst_clear_intr_mrfld(void); +void sst_process_mad_ops(struct work_struct *work); + +long intel_sst_ioctl(struct file *file_ptr, unsigned int cmd, + unsigned long arg); +int intel_sst_open_cntrl(struct inode *i_node, struct file *file_ptr); +int intel_sst_release_cntrl(struct inode *i_node, struct file *file_ptr); + +int sst_load_fw(void); +int sst_load_library(struct snd_sst_lib_download *lib, u8 ops); +int sst_load_all_modules_elf(struct intel_sst_drv *ctx, + struct sst_module_info *mod_table, int mod_table_size); +int sst_get_next_lib_mem(struct sst_mem_mgr *mgr, int size, + unsigned long *lib_base); +void sst_post_download_ctp(struct intel_sst_drv *ctx); +void sst_post_download_mrfld(struct intel_sst_drv *ctx); +void sst_post_download_byt(struct intel_sst_drv *ctx); +int sst_get_block_stream(struct intel_sst_drv *sst_drv_ctx); +void sst_memcpy_free_resources(void); + +int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx, + struct sst_block *block); +int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx, + struct sst_block *block); +int sst_create_ipc_msg(struct ipc_post **arg, bool large); +int sst_download_fw(void); +int free_stream_context(unsigned int str_id); +void sst_clean_stream(struct stream_info *stream); +int intel_sst_register_compress(struct intel_sst_drv *sst); +int intel_sst_remove_compress(struct intel_sst_drv *sst); +void sst_cdev_fragment_elapsed(int str_id); +int sst_send_sync_msg(int ipc, int str_id); +int sst_get_num_channel(struct snd_sst_params *str_param); +int sst_get_sfreq(struct snd_sst_params *str_param); +int intel_sst_check_device(void); +int sst_alloc_stream_ctp(char *params, struct sst_block *block); +int sst_alloc_stream_mrfld(char *params, struct sst_block *block); +void sst_restore_fw_context(void); +struct sst_block *sst_create_block(struct intel_sst_drv *ctx, + u32 msg_id, u32 drv_id); +int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large, + struct intel_sst_drv *sst_drv_ctx, struct sst_block **block, + u32 msg_id, u32 drv_id); +int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed); +int sst_wake_up_block(struct intel_sst_drv *ctx, int result, + u32 drv_id, u32 ipc, void *data, u32 size); +int sst_alloc_drv_context(struct device *dev); +int sst_request_firmware_async(struct intel_sst_drv *ctx); +int sst_driver_ops(struct intel_sst_drv *sst); +struct sst_platform_info *sst_get_acpi_driver_data(const char *hid); +int sst_acpi_probe(struct platform_device *pdev); +int sst_acpi_remove(struct platform_device *pdev); +void sst_save_shim64(struct intel_sst_drv *ctx, void __iomem *shim, + struct sst_shim_regs64 *shim_regs); +void sst_firmware_load_cb(const struct firmware *fw, void *context); +int sst_send_vtsv_data_to_fw(struct intel_sst_drv *ctx); + +void sst_do_recovery_mrfld(struct intel_sst_drv *sst); +void sst_do_recovery(struct intel_sst_drv *sst); +long intel_sst_ioctl_dsp(unsigned int cmd, + struct snd_ppp_params *algo_params, unsigned long arg); + +void sst_dump_to_buffer(const void *from, size_t from_len, char *buf); + +extern int intel_scu_ipc_simple_command(int, int); + +static inline int sst_pm_runtime_put(struct intel_sst_drv *sst_drv) +{ + int ret; + + ret = pm_runtime_put_sync(sst_drv->dev); + if (ret < 0) + return ret; + atomic_dec(&sst_drv->pm_usage_count); + + pr_debug("%s: count is %d now..\n", __func__, + atomic_read(&sst_drv->pm_usage_count)); + return 0; +} +/* + * sst_fill_header - inline to fill sst header + * + * @header : ipc header + * @msg : IPC message to be sent + * @large : is ipc large msg + * @str_id : stream id + * + * this function is an inline function that sets the headers before + * sending a message + */ +static inline void sst_fill_header(union ipc_header *header, + int msg, int large, int str_id) +{ + header->part.msg_id = msg; + header->part.str_id = str_id; + header->part.large = large; + header->part.done = 0; + header->part.busy = 1; + header->part.data = 0; +} + + +static inline void sst_fill_header_mrfld(union ipc_header_mrfld *header, + int msg, int task_id, int large, int drv_id) +{ + header->full = 0; + header->p.header_high.part.msg_id = msg; + header->p.header_high.part.task_id = task_id; + header->p.header_high.part.large = large; + header->p.header_high.part.drv_id = drv_id; + header->p.header_high.part.done = 0; + header->p.header_high.part.busy = 1; + header->p.header_high.part.res_rqd = 1; +} + +static inline void sst_fill_header_dsp(struct ipc_dsp_hdr *dsp, int msg, + int pipe_id, int len) +{ + dsp->cmd_id = msg; + dsp->mod_index_id = 0xff; + dsp->pipe_id = pipe_id; + dsp->length = len; + dsp->mod_id = 0; +} + +#define MAX_BLOCKS 15 +/* sst_assign_pvt_id - assign a pvt id for stream + * + * @sst_drv_ctx : driver context + * + * this inline function assigns a private id for calls that dont have stream + * context yet, should be called with lock held + */ +static inline unsigned int sst_assign_pvt_id(struct intel_sst_drv *sst_drv_ctx) +{ + unsigned int local; + + spin_lock(&sst_drv_ctx->pvt_id_lock); + sst_drv_ctx->pvt_id++; + if (sst_drv_ctx->pvt_id > MAX_BLOCKS) + sst_drv_ctx->pvt_id = 1; + local = sst_drv_ctx->pvt_id; + spin_unlock(&sst_drv_ctx->pvt_id_lock); + return local; +} + + +/* + * sst_init_stream - this function initialzes stream context + * + * @stream : stream struture + * @codec : codec for stream + * @sst_id : stream id + * @ops : stream operation + * @slot : stream pcm slot + * @device : device type + * + * this inline function initialzes stream context for allocated stream + */ +static inline void sst_init_stream(struct stream_info *stream, + int codec, int sst_id, int ops, u8 slot) +{ + stream->status = STREAM_INIT; + stream->prev = STREAM_UN_INIT; + stream->ops = ops; +} + +static inline void sst_set_gpio_conf(const struct sst_gpio_config *gpio_conf) +{ + lnw_gpio_set_alt(gpio_conf->i2s_rx_alt, gpio_conf->alt_function); + lnw_gpio_set_alt(gpio_conf->i2s_tx_alt, gpio_conf->alt_function); + lnw_gpio_set_alt(gpio_conf->i2s_frame, gpio_conf->alt_function); + lnw_gpio_set_alt(gpio_conf->i2s_clock, gpio_conf->alt_function); +} + + +/* + * sst_validate_strid - this function validates the stream id + * + * @str_id : stream id to be validated + * + * returns 0 if valid stream + */ +static inline int sst_validate_strid(int str_id) +{ + if (str_id <= 0 || str_id > sst_drv_ctx->info.max_streams) { + pr_err("SST ERR: invalid stream id : %d, max %d\n", + str_id, sst_drv_ctx->info.max_streams); + return -EINVAL; + } else + return 0; +} + +static inline int sst_shim_write(void __iomem *addr, int offset, int value) +{ + writel(value, addr + offset); + return 0; +} + +static inline u32 sst_shim_read(void __iomem *addr, int offset) +{ + + return readl(addr + offset); +} + +static inline u32 sst_reg_read(void __iomem *addr, int offset) +{ + + return readl(addr + offset); +} + +static inline u64 sst_reg_read64(void __iomem *addr, int offset) +{ + u64 val = 0; + + memcpy_fromio(&val, addr + offset, sizeof(val)); + + return val; +} + +static inline int sst_shim_write64(void __iomem *addr, int offset, u64 value) +{ + memcpy_toio(addr + offset, &value, sizeof(value)); + return 0; +} + +static inline u64 sst_shim_read64(void __iomem *addr, int offset) +{ + u64 val = 0; + + memcpy_fromio(&val, addr + offset, sizeof(val)); + return val; +} + +static inline void +sst_set_fw_state_locked(struct intel_sst_drv *sst_drv_ctx, int sst_state) +{ + mutex_lock(&sst_drv_ctx->sst_lock); + sst_drv_ctx->sst_state = sst_state; + mutex_unlock(&sst_drv_ctx->sst_lock); +} + +static inline struct stream_info *get_stream_info(int str_id) +{ + if (sst_validate_strid(str_id)) + return NULL; + return &sst_drv_ctx->streams[str_id]; +} + +static inline int get_stream_id_mrfld(u32 pipe_id) +{ + int i; + + for (i = 1; i <= sst_drv_ctx->info.max_streams; i++) + if (pipe_id == sst_drv_ctx->streams[i].pipe_id) + return i; + + pr_debug("%s: no such pipe_id(%u)", __func__, pipe_id); + return -1; +} + +int register_sst(struct device *); +int unregister_sst(struct device *); + +#ifdef CONFIG_DEBUG_FS +void sst_debugfs_init(struct intel_sst_drv *sst); +void sst_debugfs_exit(struct intel_sst_drv *sst); +#else +static inline void sst_debugfs_init(struct intel_sst_drv *sst) +{ +} + +static inline void sst_debugfs_exit(struct intel_sst_drv *sst) +{ +} +#endif /* CONFIG_DEBUG_FS */ + +/* + * FW should use virtual address 0xC000_0000 to map to the DDR + * reserved 2MB region at 512MB boundary. Currently the address of + * DDR region allocated by IA FW is not 512MB aligned. So FW is + * statically linking the DDR region at 0xDF600000. So we need to + * use the translated address to identify the DDR regions in the FW + * ELF binary. + */ +static inline u32 relocate_imr_addr_mrfld(u32 base_addr) +{ + /* Get the difference from 512MB aligned base addr */ + /* relocate the base */ + base_addr = MRFLD_FW_VIRTUAL_BASE + (base_addr % (512 * 1024 * 1024)); + return base_addr; +} + +static inline void sst_add_to_dispatch_list_and_post(struct intel_sst_drv *sst, + struct ipc_post *msg) +{ + unsigned long irq_flags; + spin_lock_irqsave(&sst->ipc_spin_lock, irq_flags); + list_add_tail(&msg->node, &sst->ipc_dispatch_list); + spin_unlock_irqrestore(&sst->ipc_spin_lock, irq_flags); + sst->ops->post_message(&sst->ipc_post_msg_wq); +} +#endif diff --git a/sound/soc/intel/sst/sst_acpi.c b/sound/soc/intel/sst/sst_acpi.c new file mode 100644 index 000000000000..a898c2a15f98 --- /dev/null +++ b/sound/soc/intel/sst/sst_acpi.c @@ -0,0 +1,669 @@ +/* sst_acpi.c - SST (LPE) driver init file for ACPI enumeration. + * + * Copyright (c) 2013, Intel Corporation. + * + * Authors: Ramesh Babu K V + * Authors: Omair Mohammed Abdullah + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sst_platform.h" +#include "../platform_ipc_v2.h" +#include "sst.h" + +extern struct miscdevice lpe_ctrl; + +static const struct sst_platform_config_data sst_byt_pdata = { + .sst_sram_buff_base = 0xffffffff, + .sst_dma_base[0] = SST_BYT_DMA0_PHY_ADDR, + .sst_dma_base[1] = SST_BYT_DMA1_PHY_ADDR, +}; + +/* use array[0] for ssp_platform_data even though SSP2 is used */ +static const struct sst_board_config_data sst_byt_rvp_bdata = { + .active_ssp_ports = 1, + .platform_id = 3, + .board_id = 1, + .ihf_num_chan = 2, + .osc_clk_freq = 25000000, + .ssp_platform_data = { + [0] = { + .ssp_cfg_sst = 1, + .port_number = 2, + .is_master = 1, + .pack_mode = 1, + .num_slots_per_frame = 2, + .num_bits_per_slot = 24, + .active_tx_map = 3, + .active_rx_map = 3, + .ssp_frame_format = 3, + .frame_polarity = 1, + .serial_bitrate_clk_mode = 0, + .frame_sync_width = 24, + .dma_handshake_interface_tx = 5, + .dma_handshake_interface_rx = 4, + .network_mode = 0, + .start_delay = 1, + .ssp_base_add = SST_BYT_SSP2_PHY_ADDR, + }, + }, +}; + +static const struct sst_board_config_data sst_byt_ffrd8_bdata = { + .active_ssp_ports = 1, + .platform_id = 3, + .board_id = 1, + .ihf_num_chan = 2, + .osc_clk_freq = 25000000, + .ssp_platform_data = { + [0] = { + .ssp_cfg_sst = 1, + .port_number = 0, + .is_master = 1, + .pack_mode = 1, + .num_slots_per_frame = 2, + .num_bits_per_slot = 24, + .active_tx_map = 3, + .active_rx_map = 3, + .ssp_frame_format = 3, + .frame_polarity = 1, + .serial_bitrate_clk_mode = 0, + .frame_sync_width = 24, + .dma_handshake_interface_tx = 1, + .dma_handshake_interface_rx = 0, + .network_mode = 0, + .start_delay = 1, + .ssp_base_add = SST_BYT_SSP0_PHY_ADDR, + }, + }, +}; + +static const struct sst_board_config_data sst_byt_crv2_bdata = { + .active_ssp_ports = 1, + .platform_id = 3, + .board_id = 1, + .ihf_num_chan = 1, + .osc_clk_freq = 25000000, + .ssp_platform_data = { + [0] = { + .ssp_cfg_sst = 1, + .port_number = 0, + .is_master = 1, + .pack_mode = 1, + .num_slots_per_frame = 2, + .num_bits_per_slot = 24, + .active_tx_map = 3, + .active_rx_map = 3, + .ssp_frame_format = 3, + .frame_polarity = 1, + .serial_bitrate_clk_mode = 0, + .frame_sync_width = 24, + .dma_handshake_interface_tx = 1, + .dma_handshake_interface_rx = 0, + .network_mode = 0, + .start_delay = 1, + .ssp_base_add = SST_BYT_SSP0_PHY_ADDR, + }, + }, +}; + +static const struct sst_info byt_fwparse_info = { + .use_elf = true, + .max_streams = 4, + .dma_max_len = SST_MAX_DMA_LEN_MRFLD, + .iram_start = SST_BYT_IRAM_PHY_START, + .iram_end = SST_BYT_IRAM_PHY_END, + .iram_use = true, + .dram_start = SST_BYT_DRAM_PHY_START, + .dram_end = SST_BYT_DRAM_PHY_END, + .dram_use = true, + .imr_start = SST_BYT_IMR_VIRT_START, + .imr_end = SST_BYT_IMR_VIRT_END, + .imr_use = true, + .mailbox_start = SST_BYT_MBOX_PHY_ADDR, + .num_probes = 0, + .lpe_viewpt_rqd = true, +}; + + +static const struct sst_info cht_fwparse_info = { + .use_elf = true, + .max_streams = MAX_NUM_STREAMS_MRFLD, + .dma_max_len = SST_MAX_DMA_LEN_MRFLD, + .iram_start = SST_BYT_IRAM_PHY_START, + .iram_end = SST_BYT_IRAM_PHY_END, + .iram_use = true, + .dram_start = SST_BYT_DRAM_PHY_START, + .dram_end = SST_BYT_DRAM_PHY_END, + .dram_use = true, + .imr_start = SST_BYT_IMR_VIRT_START, + .imr_end = SST_BYT_IMR_VIRT_END, + .imr_use = true, + .mailbox_start = SST_BYT_MBOX_PHY_ADDR, + .num_probes = 0, + .lpe_viewpt_rqd = true, +}; + +static const struct sst_ipc_info byt_ipc_info = { + .use_32bit_ops = true, + .ipc_offset = 4, + .mbox_recv_off = 0x400, +}; + +static const struct sst_lib_dnld_info byt_lib_dnld_info = { + .mod_base = SST_BYT_IMR_VIRT_START, + .mod_end = SST_BYT_IMR_VIRT_END, + .mod_table_offset = BYT_FW_MOD_TABLE_OFFSET, + .mod_table_size = BYT_FW_MOD_TABLE_SIZE, + .mod_ddr_dnld = true, +}; + +static const struct sst_ipc_info cht_ipc_info = { + .use_32bit_ops = false, + .ipc_offset = 0, + .mbox_recv_off = 0x400, +}; + +struct sst_platform_info cht_platform_data = { + .probe_data = &cht_fwparse_info, + .ssp_data = NULL, + .bdata = NULL, + .pdata = NULL, + .ipc_info = &cht_ipc_info, + .lib_info = NULL, +}; + +struct sst_platform_info byt_rvp_platform_data = { + .probe_data = &byt_fwparse_info, + .ssp_data = NULL, + .bdata = &sst_byt_rvp_bdata, + .pdata = &sst_byt_pdata, + .ipc_info = &byt_ipc_info, + .lib_info = &byt_lib_dnld_info, +}; + +struct sst_platform_info byt_ffrd8_platform_data = { + .probe_data = &byt_fwparse_info, + .ssp_data = NULL, + .bdata = &sst_byt_ffrd8_bdata, + .pdata = &sst_byt_pdata, + .ipc_info = &byt_ipc_info, + .lib_info = &byt_lib_dnld_info, +}; + +int sst_workqueue_init(struct intel_sst_drv *ctx) +{ + pr_debug("%s", __func__); + + INIT_LIST_HEAD(&ctx->memcpy_list); + INIT_LIST_HEAD(&ctx->libmemcpy_list); + INIT_LIST_HEAD(&sst_drv_ctx->rx_list); + INIT_LIST_HEAD(&ctx->ipc_dispatch_list); + INIT_LIST_HEAD(&ctx->block_list); + INIT_WORK(&ctx->ipc_post_msg.wq, ctx->ops->post_message); + init_waitqueue_head(&ctx->wait_queue); + + ctx->mad_wq = create_singlethread_workqueue("sst_mad_wq"); + if (!ctx->mad_wq) + goto err_wq; + ctx->post_msg_wq = + create_singlethread_workqueue("sst_post_msg_wq"); + if (!ctx->post_msg_wq) + goto err_wq; + return 0; +err_wq: + return -EBUSY; +} + +void sst_init_locks(struct intel_sst_drv *ctx) +{ + mutex_init(&ctx->stream_lock); + mutex_init(&ctx->sst_lock); + mutex_init(&ctx->mixer_ctrl_lock); + mutex_init(&ctx->csr_lock); + spin_lock_init(&sst_drv_ctx->rx_msg_lock); + spin_lock_init(&ctx->ipc_spin_lock); + spin_lock_init(&ctx->block_lock); + spin_lock_init(&ctx->pvt_id_lock); +} + +int sst_destroy_workqueue(struct intel_sst_drv *ctx) +{ + pr_debug("%s", __func__); + if (ctx->mad_wq) + destroy_workqueue(ctx->mad_wq); + if (ctx->post_msg_wq) + destroy_workqueue(ctx->post_msg_wq); + return 0; +} + +#if IS_ENABLED(CONFIG_ACPI) +static int sst_platform_get_resources_fdk(struct intel_sst_drv *ctx, + struct platform_device *pdev) +{ + struct resource *rsrc; + + pr_debug("%s", __func__); + + /* All ACPI resource request here */ + /* Get DDR addr from platform resource table */ + rsrc = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!rsrc) { + pr_err("Invalid DDR base from IFWI"); + return -EIO; + } + ctx->ddr_base = rsrc->start; + ctx->ddr_end = rsrc->end; + pr_debug("DDR base: %#x", ctx->ddr_base); + ctx->ddr = devm_ioremap_nocache(ctx->dev, ctx->ddr_base, + resource_size(rsrc)); + if (!ctx->ddr) { + pr_err("unable to map DDR"); + return -EIO; + } + + /* Get Shim addr from platform resource table */ + rsrc = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!rsrc) { + pr_err("Invalid SHIM base from IFWI"); + return -EIO; + } + ctx->shim_phy_add = rsrc->start; + pr_debug("SHIM base: %#x", ctx->shim_phy_add); + ctx->shim = devm_ioremap_nocache(ctx->dev, ctx->shim_phy_add, + resource_size(rsrc)); + if (!ctx->shim) { + pr_err("unable to map SHIM"); + return -EIO; + } + /* reassign physical address to LPE viewpoint address */ + ctx->shim_phy_add = SST_BYT_SHIM_PHY_ADDR; + + /* Get mailbox addr from platform resource table */ + rsrc = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (!rsrc) { + pr_err("Invalid Mailbox base from IFWI"); + return -EIO; + } + ctx->mailbox_add = rsrc->start; + pr_debug("Mailbox base: %#x", ctx->mailbox_add); + ctx->mailbox = devm_ioremap_nocache(ctx->dev, ctx->mailbox_add, + resource_size(rsrc)); + if (!ctx->mailbox) { + pr_err("unable to map mailbox"); + return -EIO; + } + /* reassign physical address to LPE viewpoint address */ + ctx->mailbox_add = sst_drv_ctx->info.mailbox_start; + + /* Get iram/iccm addr from platform resource table */ + rsrc = platform_get_resource(pdev, IORESOURCE_MEM, 3); + if (!rsrc) { + pr_err("Invalid IRAM base from IFWI"); + return -EIO; + } + ctx->iram_base = rsrc->start; + ctx->iram_end = rsrc->end; + pr_debug("IRAM base: %#x", ctx->iram_base); + ctx->iram = devm_ioremap_nocache(ctx->dev, ctx->iram_base, + resource_size(rsrc)); + if (!ctx->iram) { + pr_err("unable to map IRAM"); + return -EIO; + } + + /* Get dram/dccm addr from platform resource table */ + rsrc = platform_get_resource(pdev, IORESOURCE_MEM, 4); + if (!rsrc) { + pr_err("Invalid DRAM base from IFWI"); + return -EIO; + } + ctx->dram_base = rsrc->start; + ctx->dram_end = rsrc->end; + pr_debug("DRAM base: %#x", ctx->dram_base); + ctx->dram = devm_ioremap_nocache(ctx->dev, ctx->dram_base, + resource_size(rsrc)); + if (!ctx->dram) { + pr_err("unable to map DRAM"); + return -EIO; + } + + /* Register the ISR */ + ctx->irq_num = platform_get_irq(pdev, 0); + pr_debug("irq from pdev is:%d", ctx->irq_num); + return 0; +} + +#define LPE_IRAM_OFFSET 0x0C0000 +#define LPE_IRAM_SIZE 0x040000 +#define LPE_DRAM_OFFSET 0x100000 +#define LPE_DRAM_SIZE 0x040000 +#define LPE_SHIM_OFFSET 0x140000 +#define LPE_SHIM_SIZE 0x004000 +#define LPE_MBOX_OFFSET 0x144000 +#define LPE_MBOX_SIZE 0x004000 + +static int sst_platform_get_resources_edk(struct intel_sst_drv *ctx, + struct platform_device *pdev) +{ + struct resource *rsrc; + + pr_debug("%s", __func__); + + /* All ACPI resource request here */ + /* Get Shim addr */ + rsrc = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!rsrc) { + pr_err("Invalid SHIM base from IFWI"); + return -EIO; + } + pr_debug("LPE base: %#x size:%#x", (unsigned int) rsrc->start, + (unsigned int)resource_size(rsrc)); + ctx->iram_base = rsrc->start + LPE_IRAM_OFFSET; + ctx->iram_end = ctx->iram_base + LPE_IRAM_SIZE - 1; + pr_debug("IRAM base: %#x", ctx->iram_base); + ctx->iram = devm_ioremap_nocache(ctx->dev, ctx->iram_base, + LPE_IRAM_SIZE); + if (!ctx->iram) { + pr_err("unable to map IRAM"); + return -EIO; + } + + ctx->dram_base = rsrc->start + LPE_DRAM_OFFSET; + ctx->dram_end = ctx->dram_base + LPE_DRAM_SIZE - 1; + pr_debug("DRAM base: %#x", ctx->dram_base); + ctx->dram = devm_ioremap_nocache(ctx->dev, ctx->dram_base, + LPE_DRAM_SIZE); + if (!ctx->dram) { + pr_err("unable to map DRAM"); + return -EIO; + } + + ctx->shim_phy_add = rsrc->start + LPE_SHIM_OFFSET; + pr_debug("SHIM base: %#x", ctx->shim_phy_add); + ctx->shim = devm_ioremap_nocache(ctx->dev, ctx->shim_phy_add, + LPE_SHIM_SIZE); + if (!ctx->shim) { + pr_err("unable to map SHIM"); + return -EIO; + } + /* reassign physical address to LPE viewpoint address */ + ctx->shim_phy_add = SST_BYT_SHIM_PHY_ADDR; + + /* Get mailbox addr */ + ctx->mailbox_add = rsrc->start + LPE_MBOX_OFFSET; + pr_debug("Mailbox base: %#x", ctx->mailbox_add); + ctx->mailbox = devm_ioremap_nocache(ctx->dev, ctx->mailbox_add, + LPE_MBOX_SIZE); + if (!ctx->mailbox) { + pr_err("unable to map mailbox"); + return -EIO; + } + + /* reassign physical address to LPE viewpoint address */ + ctx->mailbox_add = sst_drv_ctx->info.mailbox_start; + + rsrc = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (!rsrc) { + pr_err("Invalid DDR base from IFWI"); + return -EIO; + } + ctx->ddr_base = rsrc->start; + ctx->ddr_end = rsrc->end; + pr_debug("DDR base: %#x", ctx->ddr_base); + ctx->ddr = devm_ioremap_nocache(ctx->dev, ctx->ddr_base, + resource_size(rsrc)); + if (!ctx->ddr) { + pr_err("unable to map DDR"); + return -EIO; + } + /* Register the ISR */ + if (!strncmp(ctx->hid, "80860F28", 8)) + ctx->irq_num = platform_get_irq(pdev, 0); + else if (!strncmp(ctx->hid, "808622A8", 8)) { + /* FIXME: IRQ number will be moved to 0 once the BIOS fix is done */ + ctx->irq_num = platform_get_irq(pdev, 5); + } else + return -EINVAL; + return 0; +} + +static int sst_platform_get_resources(const char *hid, + struct intel_sst_drv *ctx, struct platform_device *pdev) +{ + + pr_debug("%s", __func__); + + if (!strncmp(hid, "LPE0F281", 8)) { + ctx->pci_id = SST_BYT_PCI_ID; + return sst_platform_get_resources_fdk(ctx, pdev); + } + if (!strncmp(hid, "808622A8", 8)) { + ctx->pci_id = SST_CHT_PCI_ID; + return sst_platform_get_resources_edk(ctx, pdev); + } + if (!strncmp(hid, "80860F28", 8)) { + ctx->pci_id = SST_BYT_PCI_ID; + return sst_platform_get_resources_edk(ctx, pdev); + } else if (!strncmp(hid, "LPE0F28", 7)) { + ctx->pci_id = SST_BYT_PCI_ID; + return sst_platform_get_resources_fdk(ctx, pdev); + } else { + pr_err("Invalid device\n"); + return -EINVAL; + } +} + +int sst_acpi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + acpi_handle handle = ACPI_HANDLE(dev); + struct acpi_device *device; + const char *hid; + int i, ret = 0; + struct intel_sst_drv *ctx; + + ret = acpi_bus_get_device(handle, &device); + if (ret) { + pr_err("%s: could not get acpi device - %d\n", __func__, ret); + return -ENODEV; + } + + if (acpi_bus_get_status(device) || !device->status.present) { + pr_err("%s: device has invalid status", __func__); + return -ENODEV; + } + + hid = acpi_device_hid(device); + pr_debug("%s for %s", __func__, hid); + ret = sst_alloc_drv_context(dev); + if (ret) + return ret; + ctx = sst_drv_ctx; + ctx->dev = dev; + ctx->hid = hid; + + ret = sst_platform_get_resources(hid, ctx, pdev); + if (ret) + return ret; + /* need to save shim registers in BYT */ + ctx->shim_regs64 = devm_kzalloc(dev, sizeof(*ctx->shim_regs64), + GFP_KERNEL); + if (!ctx->shim_regs64) + return -ENOMEM; + + ret = sst_driver_ops(ctx); + if (ret != 0) + return -EINVAL; + + sst_init_locks(ctx); + + ctx->stream_cnt = 0; + ctx->fw_in_mem = NULL; + ctx->use_dma = 1; + ctx->use_lli = 1; + + if (sst_workqueue_init(ctx)) + goto do_free_wq; + + ctx->pdata = sst_get_acpi_driver_data(hid); + if (!ctx->pdata) + return -EINVAL; + if (INTEL_MID_BOARD(3, TABLET, BYT, BLK, PRO, CRV2)) { + /* BYT-CR V2 has only mono speaker, while + * byt has stereo speaker, for both + * HID is same, so platform data also is + * same, hence overriding bdata based on spid + */ + ctx->pdata->bdata = &sst_byt_crv2_bdata; + pr_info("Overriding bdata for byt-crv2\n"); + } + + ctx->use_32bit_ops = ctx->pdata->ipc_info->use_32bit_ops; + ctx->mailbox_recv_offset = ctx->pdata->ipc_info->mbox_recv_off; + + memcpy(&ctx->info, ctx->pdata->probe_data, sizeof(ctx->info)); + + ctx->ipc_reg.ipcx = SST_IPCX + ctx->pdata->ipc_info->ipc_offset; + ctx->ipc_reg.ipcd = SST_IPCD + ctx->pdata->ipc_info->ipc_offset; + + pr_debug("Got drv data max stream %d\n", + ctx->info.max_streams); + for (i = 1; i <= ctx->info.max_streams; i++) { + struct stream_info *stream = &ctx->streams[i]; + mutex_init(&stream->lock); + } + ret = sst_request_firmware_async(ctx); + if (ret) { + pr_err("Firmware download failed:%d\n", ret); + goto do_free_wq; + } + + ret = devm_request_threaded_irq(ctx->dev, ctx->irq_num, ctx->ops->interrupt, + ctx->ops->irq_thread, 0, SST_DRV_NAME, + ctx); + if (ret) + return ret; + pr_debug("Registered IRQ %#x\n", ctx->irq_num); + + /*Register LPE Control as misc driver*/ + ret = misc_register(&lpe_ctrl); + if (ret) { + pr_err("couldn't register control device\n"); + goto do_free_wq; + } + /* mask all SSP and DMA irq to IA - enabled in acpi kernel driver */ + sst_shim_write64(ctx->shim, SST_IMRX, 0xFFFF0038); + + if (ctx->use_32bit_ops) { + pr_debug("allocate mem for context save/restore\n "); + /*allocate mem for fw context save during suspend*/ + ctx->fw_cntx = devm_kzalloc(ctx->dev, FW_CONTEXT_MEM, GFP_KERNEL); + if (!ctx->fw_cntx) { + ret = -ENOMEM; + goto do_free_misc; + } + /*setting zero as that is valid mem to restore*/ + ctx->fw_cntx_size = 0; + } + + platform_set_drvdata(pdev, ctx); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + register_sst(dev); + sst_debugfs_init(ctx); + sst_set_fw_state_locked(ctx, SST_UN_INIT); + sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64); + pr_info("%s successfully done!\n", __func__); + return ret; + +do_free_misc: + misc_deregister(&lpe_ctrl); +do_free_wq: + sst_destroy_workqueue(ctx); + + sst_drv_ctx = NULL; + platform_set_drvdata(pdev, NULL); + pr_err("%s: failed with %d\n", __func__, ret); + return ret; +} + +/** +* intel_sst_remove - remove function +* +* @pdev: platform device structure +* +* This function is called by OS when a device is unloaded +* This frees the interrupt etc +*/ +int sst_acpi_remove(struct platform_device *pdev) +{ + struct intel_sst_drv *ctx; + + ctx = platform_get_drvdata(pdev); + sst_debugfs_exit(ctx); + pm_runtime_get_noresume(ctx->dev); + pm_runtime_disable(ctx->dev); + unregister_sst(ctx->dev); + sst_set_fw_state_locked(ctx, SST_UN_INIT); + misc_deregister(&lpe_ctrl); + kfree(ctx->runtime_param.param.addr); + flush_scheduled_work(); + sst_destroy_workqueue(ctx); + kfree(ctx->fw_sg_list.src); + kfree(ctx->fw_sg_list.dst); + ctx->fw_sg_list.list_len = 0; + kfree(ctx->fw_in_mem); + ctx->fw_in_mem = NULL; + sst_memcpy_free_resources(); + sst_drv_ctx = NULL; + platform_set_drvdata(pdev, NULL); + return 0; +} + +#else +int sst_acpi_probe(struct platform_device *pdev) +{ + return -EINVAL; +} + +int sst_acpi_remove(struct platform_device *pdev) +{ + return -EINVAL; +} +#endif + +MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine ACPI Driver"); +MODULE_AUTHOR("Ramesh Babu K V"); +MODULE_AUTHOR("Omair Mohammed Abdullah"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("sst"); diff --git a/sound/soc/intel/sst/sst_app_compat_interface.c b/sound/soc/intel/sst/sst_app_compat_interface.c new file mode 100644 index 000000000000..4babd7631e90 --- /dev/null +++ b/sound/soc/intel/sst/sst_app_compat_interface.c @@ -0,0 +1,85 @@ + +/* + * sst_app_compat_interface.c - Intel SST Driver for audio engine + * + * Copyright (C) 2013-14 Intel Corp + * Authors: Subhransu S. Prusty + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This driver exposes the audio engine functionalities to the ALSA + * and middleware. + */ + +/* This file is included from sst.c */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include "sst.h" + +struct snd_ppp_params32 { + __u8 algo_id;/* Post/Pre processing algorithm ID */ + __u8 str_id; /*Only 5 bits used 0 - 31 are valid*/ + __u8 enable; /* 0= disable, 1= enable*/ + __u8 operation; + __u32 size; /*Size of parameters for all blocks*/ + __u32 params; +} __packed; + +enum { +SNDRV_SST_SET_ALGO32 = _IOW('L', 0x30, struct snd_ppp_params32), +SNDRV_SST_GET_ALGO32 = _IOWR('L', 0x31, struct snd_ppp_params32), +}; + +static long sst_algo_compat(unsigned int cmd, + struct snd_ppp_params32 __user *arg32) +{ + int retval = 0; + struct snd_ppp_params32 algo_params32; + struct snd_ppp_params algo_params; + + if (copy_from_user(&algo_params32, arg32, sizeof(algo_params32))) { + pr_debug("%s: copy from user failed: %d\n", __func__, retval); + return -EINVAL; + } + + memcpy(&algo_params, &algo_params32, sizeof(algo_params32)-sizeof(__u32)); + algo_params.params = compat_ptr(algo_params32.params); + retval = intel_sst_ioctl_dsp(cmd, &algo_params, (unsigned long)arg32); + return retval; +} + +static long intel_sst_ioctl_compat(struct file *file_ptr, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = compat_ptr(arg); + + switch (_IOC_NR(cmd)) { + case _IOC_NR(SNDRV_SST_DRIVER_INFO): + case _IOC_NR(SNDRV_SST_TUNING_PARAMS): + return intel_sst_ioctl(file_ptr, cmd, (unsigned long)argp); + case _IOC_NR(SNDRV_SST_SET_ALGO32): + return sst_algo_compat(SNDRV_SST_SET_ALGO, argp); + case _IOC_NR(SNDRV_SST_GET_ALGO32): + return sst_algo_compat(SNDRV_SST_GET_ALGO, argp); + + default: + return -ENOTTY; + } + return 0; +} diff --git a/sound/soc/intel/sst/sst_app_interface.c b/sound/soc/intel/sst/sst_app_interface.c new file mode 100644 index 000000000000..cfbbf4f0db11 --- /dev/null +++ b/sound/soc/intel/sst/sst_app_interface.c @@ -0,0 +1,342 @@ +/* + * sst_app_interface.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-10 Intel Corp + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R + * Jeeja KP + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This driver exposes the audio engine functionalities to the ALSA + * and middleware. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sst_platform.h" +#include "../platform_ipc_v2.h" +#include "sst.h" + +#define AM_MODULE 1 + +/** + * intel_sst_open_cntrl - opens a handle to driver + * + * @i_node: inode structure + * @file_ptr:pointer to file + * + * This function is called by OS when a user space component + * tries to get a driver handle to /dev/intel_sst_control. + * Only one handle at a time will be allowed + * This is for control operations only + */ +int intel_sst_open_cntrl(struct inode *i_node, struct file *file_ptr) +{ + unsigned int retval; + + /* audio manager open */ + mutex_lock(&sst_drv_ctx->stream_lock); + retval = intel_sst_check_device(); + if (retval) { + mutex_unlock(&sst_drv_ctx->stream_lock); + return retval; + } + pr_debug("AM handle opened\n"); + + mutex_unlock(&sst_drv_ctx->stream_lock); + return retval; +} + + +int intel_sst_release_cntrl(struct inode *i_node, struct file *file_ptr) +{ + /* audio manager close */ + mutex_lock(&sst_drv_ctx->stream_lock); + sst_pm_runtime_put(sst_drv_ctx); + mutex_unlock(&sst_drv_ctx->stream_lock); + pr_debug("AM handle closed\n"); + return 0; +} + +/** + * sst_get_max_streams - Function to populate the drv info structure + * with the max streams + * @info: the out params that holds the drv info + * + * This function is called when max streams count is required +**/ +void sst_get_max_streams(struct snd_sst_driver_info *info) +{ + pr_debug("info.max_streams %d num_probes %d\n", sst_drv_ctx->info.max_streams, + sst_drv_ctx->info.num_probes); + info->max_streams = sst_drv_ctx->info.max_streams - sst_drv_ctx->info.num_probes; +} + +/** + * sst_create_algo_ipc - create ipc msg for algorithm parameters + * + * @algo_params: Algorithm parameters + * @msg: post msg pointer + * @pvt_id: Checked by wake_up_block + * + * This function is called to create ipc msg + * For copying the mailbox data the function returns offset in bytes to mailbox + * memory where the mailbox data should be copied after msg header + */ +static int sst_create_algo_ipc(struct snd_ppp_params *algo_params, + struct ipc_post **msg, int pvt_id) +{ + u32 header_size = 0; + u32 ipc_msg_size = sizeof(u32) + sizeof(*algo_params) + - sizeof(algo_params->params) + algo_params->size; + u32 offset = 0; + + if (ipc_msg_size > SST_MAILBOX_SIZE) + return -ENOMEM; + if (sst_create_ipc_msg(msg, true)) + return -ENOMEM; + sst_fill_header(&(*msg)->header, + IPC_IA_ALG_PARAMS, 1, pvt_id); + (*msg)->header.part.data = ipc_msg_size; + memcpy((*msg)->mailbox_data, &(*msg)->header, sizeof(u32)); + offset = sizeof(u32); + header_size = sizeof(*algo_params) - sizeof(algo_params->params); + memcpy((*msg)->mailbox_data + offset, algo_params, header_size); + offset += header_size; + return offset; +} + +static long sst_send_algo(struct snd_ppp_params *algo_params, + struct sst_block *block, enum sst_algo_ops algo) +{ + struct ipc_post *msg; + int retval; + int offset; + + pr_debug("Algo ID %d Str id %d Enable %d Size %d\n", + algo_params->algo_id, algo_params->str_id, + algo_params->enable, algo_params->size); + + algo_params->operation = algo; + + offset = sst_create_algo_ipc(algo_params, &msg, block->drv_id); + if (offset < 0) + return offset; + + if (copy_from_user(msg->mailbox_data + offset, + algo_params->params, algo_params->size)) { + kfree(msg); + return -EFAULT; + } + + sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); + retval = sst_wait_timeout(sst_drv_ctx, block); + if (retval) { + pr_debug("%s: failed for algo ops %s with retval %d\n", + __func__, algo ? "SST_GET_ALGO" : "SST_SET_ALGO", retval); + return -EIO; + } + return 0; +} + +/** + * intel_sst_ioctl_dsp - receives the device ioctl's + * + * @cmd:Ioctl cmd + * @arg:data + * + * This function is called when a user space component + * sends a DSP Ioctl to SST driver + */ +long intel_sst_ioctl_dsp(unsigned int cmd, + struct snd_ppp_params *algo_params, unsigned long arg) +{ + int retval = 0; + struct snd_ppp_params *algo_params_copied; + struct sst_block *block; + int pvt_id; + + pvt_id = sst_assign_pvt_id(sst_drv_ctx); + block = sst_create_block(sst_drv_ctx, IPC_IA_ALG_PARAMS, pvt_id); + if (block == NULL) + return -ENOMEM; + + switch (_IOC_NR(cmd)) { + case _IOC_NR(SNDRV_SST_SET_ALGO): + retval = sst_send_algo(algo_params, block, SST_SET_ALGO); + break; + + case _IOC_NR(SNDRV_SST_GET_ALGO): + retval = sst_send_algo(algo_params, block, SST_GET_ALGO); + if (retval) + break; + algo_params_copied = (struct snd_ppp_params *)block->data; + + if (algo_params_copied->size > algo_params->size) { + pr_debug("mem insufficient to copy\n"); + retval = -EMSGSIZE; + break; + } else { + char __user *tmp; + struct snd_ppp_params *get_params; + char *pp; + + tmp = (char __user *)arg + offsetof( + struct snd_ppp_params, size); + if (copy_to_user(tmp, &algo_params_copied->size, + sizeof(u32))) { + retval = -EFAULT; + break; + } + tmp = (char __user *)arg + offsetof( + struct snd_ppp_params, enable); + if (copy_to_user(tmp, &algo_params_copied->enable, + sizeof(u8))) { + retval = -EFAULT; + break; + } + if (algo_params_copied->size == 0) + break; + + get_params = kmalloc(sizeof(*get_params), GFP_KERNEL); + if (!get_params) { + pr_err("sst: mem alloc failed\n"); + break; + } + memcpy(get_params, algo_params_copied, + sizeof(*get_params)); + + get_params->params = kmalloc(get_params->size, GFP_KERNEL); + if (!get_params->params) { + pr_err("sst: mem alloc failed\n"); + goto free_mem; + } + pp = (char *)algo_params_copied; + pp = pp + sizeof(*get_params) - + sizeof(get_params->params); + memcpy(get_params->params, pp, get_params->size); + if (copy_to_user(algo_params->params, + get_params->params, + get_params->size)) { + retval = -EFAULT; + } + kfree(get_params->params); + +free_mem: + kfree(get_params); + + } + break; + } + sst_free_block(sst_drv_ctx, block); + pr_debug("ioctl dsp return = %d, for cmd = %x\n", retval, cmd); + return retval; +} + +static long sst_ioctl_tuning_params(unsigned int cmd, unsigned long arg) +{ + struct snd_sst_tuning_params params; + struct ipc_post *msg; + unsigned long address; + + if (copy_from_user(¶ms, (void __user *)arg, sizeof(params))) + return -EFAULT; + pr_debug("sst: Parameter %d, Stream %d, Size %d\n", params.type, + params.str_id, params.size); + if (sst_create_ipc_msg(&msg, true)) + return -ENOMEM; + address = (unsigned long)params.addr; + + switch (_IOC_NR(cmd)) { + case _IOC_NR(SNDRV_SST_TUNING_PARAMS): + sst_fill_header(&msg->header, IPC_IA_TUNING_PARAMS, 1, + params.str_id); + break; + } + msg->header.part.data = sizeof(u32) + sizeof(params) + params.size; + memcpy(msg->mailbox_data, &msg->header.full, sizeof(u32)); + memcpy(msg->mailbox_data + sizeof(u32), ¶ms, sizeof(params)); + /* driver doesn't need to send address, so overwrite addr with data */ + if (copy_from_user(msg->mailbox_data + sizeof(u32) + + sizeof(params) - sizeof(params.addr), + (void __user *)address, params.size)) { + kfree(msg->mailbox_data); + kfree(msg); + return -EFAULT; + } + + sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); + return 0; +} +/** + * intel_sst_ioctl - receives the device ioctl's + * @file_ptr:pointer to file + * @cmd:Ioctl cmd + * @arg:data + * + * This function is called by OS when a user space component + * sends an Ioctl to SST driver + */ +long intel_sst_ioctl(struct file *file_ptr, unsigned int cmd, unsigned long arg) +{ + int retval = 0; + struct snd_ppp_params algo_params; + + if (sst_drv_ctx->sst_state != SST_FW_RUNNING) + return -EBUSY; + + switch (_IOC_NR(cmd)) { + case _IOC_NR(SNDRV_SST_DRIVER_INFO): { + struct snd_sst_driver_info info; + + pr_debug("SNDRV_SST_DRIVER_INFO received\n"); + sst_get_max_streams(&info); + + if (copy_to_user((void __user *)arg, &info, + sizeof(info))) + retval = -EFAULT; + break; + } + case _IOC_NR(SNDRV_SST_GET_ALGO): + case _IOC_NR(SNDRV_SST_SET_ALGO): + if (copy_from_user(&algo_params, (void __user *)arg, + sizeof(algo_params))) { + return -EFAULT; + } + retval = intel_sst_ioctl_dsp(cmd, &algo_params, arg); + break; + + case _IOC_NR(SNDRV_SST_TUNING_PARAMS): + retval = sst_ioctl_tuning_params(cmd, arg); + break; + + default: + retval = -EINVAL; + } + pr_debug("intel_sst_ioctl:complete ret code = %d for command = %x\n", retval, cmd); + return retval; +} + diff --git a/sound/soc/intel/sst/sst_debug.c b/sound/soc/intel/sst/sst_debug.c new file mode 100644 index 000000000000..ce771c553e64 --- /dev/null +++ b/sound/soc/intel/sst/sst_debug.c @@ -0,0 +1,1328 @@ +/* + * sst_debug.c - Intel SST Driver debugfs support + * + * Copyright (C) 2012 Intel Corp + * Authors: Vinod Koul + * Omair Mohammed Abdullah + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This file contains all debugfs functions + * Support includes: + * - Disabling/Enabling runtime PM for SST + * - Reading/Writing SST SHIM registers + * - Reading/Enabling Input OSC Clock + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": debugfs: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sst_platform.h" +#include "../platform_ipc_v2.h" +#include "sst.h" + +#define DMA_NUM_CH 8 +#define DEBUGFS_SSP_BUF_SIZE 300 /* 22 chars * 12 reg*/ +#define DEBUGFS_DMA_BUF_SIZE 2500 /* 32 chars * 78 regs*/ + +/* Register Offsets of SSP3 and LPE DMA */ +u32 ssp_reg_off[] = {0x0, 0x4, 0x8, 0xC, 0x10, 0x28, 0x2C, 0x30, 0x34, 0x38, + 0x3C, 0x40}; +/* Excludes the channel registers */ +u32 dma_reg_off[] = {0x2C0, 0x2C8, 0x2D0, 0x2D8, 0x2E0, 0x2E8, + 0x2F0, 0x2F8, 0x300, 0x308, 0x310, 0x318, 0x320, 0x328, 0x330, + 0x338, 0x340, 0x348, 0x350, 0x358, 0x360, 0x368, 0x370, 0x378, + 0x380, 0x388, 0x390, 0x398, 0x3A0, 0x3A8, 0x3B0, 0x3C8, 0x3D0, + 0x3D8, 0x3E0, 0x3E8, 0x3F0, 0x3F8}; + +static ssize_t sst_debug_shim_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct intel_sst_drv *drv = file->private_data; + unsigned long long val = 0; + unsigned int addr; + char buf[512]; + char name[8]; + int pos = 0; + + buf[0] = 0; + if (drv->sst_state == SST_SUSPENDED) { + pr_err("FW suspended, cannot read SHIM registers\n"); + return -EFAULT; + } + + for (addr = SST_SHIM_BEGIN; addr <= SST_SHIM_END; addr += 8) { + switch (drv->pci_id) { + case SST_CLV_PCI_ID: + val = sst_shim_read(drv->shim, addr); + break; + case SST_MRFLD_PCI_ID: + case SST_BYT_PCI_ID: + case SST_CHT_PCI_ID: + val = sst_shim_read64(drv->shim, addr); + break; + } + + name[0] = 0; + switch (addr) { + case SST_ISRX: + strcpy(name, "ISRX"); break; + case SST_ISRD: + strcpy(name, "ISRD"); break; + case SST_IPCX: + strcpy(name, "IPCX"); break; + case SST_IPCD: + strcpy(name, "IPCD"); break; + case SST_IMRX: + strcpy(name, "IMRX"); break; + case SST_IMRD: + strcpy(name, "IMRD"); break; + } + pos += sprintf(buf + pos, "0x%.2x: %.8llx %s\n", addr, val, name); + } + + return simple_read_from_buffer(user_buf, count, ppos, + buf, strlen(buf)); +} + +static ssize_t sst_debug_shim_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct intel_sst_drv *drv = file->private_data; + char buf[32]; + char *start = buf, *end; + unsigned long long value; + unsigned long reg_addr; + int ret_val; + size_t buf_size = min(count, sizeof(buf)-1); + + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + if (drv->sst_state == SST_SUSPENDED) { + pr_err("FW suspended, cannot write SHIM registers\n"); + return -EFAULT; + } + + while (*start == ' ') + start++; + end = start; + while (isalnum(*end)) + end++; + *end = 0; + + ret_val = kstrtoul(start, 16, ®_addr); + if (ret_val) { + pr_err("kstrtoul failed, ret_val = %d\n", ret_val); + return ret_val; + } + if (!(SST_SHIM_BEGIN < reg_addr && reg_addr < SST_SHIM_END)) { + pr_err("invalid shim address: 0x%lx\n", reg_addr); + return -EINVAL; + } + + start = end + 1; + while (*start == ' ') + start++; + + ret_val = kstrtoull(start, 16, &value); + if (ret_val) { + pr_err("kstrtoul failed, ret_val = %d\n", ret_val); + return ret_val; + } + + pr_debug("writing shim: 0x%.2lx=0x%.8llx", reg_addr, value); + + if (drv->pci_id == SST_CLV_PCI_ID) + sst_shim_write(drv->shim, reg_addr, (u32) value); + else if (drv->pci_id == SST_MRFLD_PCI_ID) + sst_shim_write64(drv->shim, reg_addr, (u64) value); + + /* Userspace has been fiddling around behind the kernel's back */ + add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); + return buf_size; +} + +static const struct file_operations sst_debug_shim_ops = { + .open = simple_open, + .read = sst_debug_shim_read, + .write = sst_debug_shim_write, + .llseek = default_llseek, +}; + +#define RESVD_DUMP_SZ 40 +#define IA_LPE_MAILBOX_DUMP_SZ 100 +#define LPE_IA_MAILBOX_DUMP_SZ 100 +#define SCU_LPE_MAILBOX_DUMP_SZ 256 +#define LPE_SCU_MAILBOX_DUMP_SZ 256 + +static inline int is_fw_running(struct intel_sst_drv *drv) +{ + pm_runtime_get_sync(drv->dev); + atomic_inc(&drv->pm_usage_count); + if (drv->sst_state != SST_FW_RUNNING) { + pr_err("FW not running\n"); + sst_pm_runtime_put(drv); + return -EFAULT; + } + return 0; +} + +static inline int read_buffer_fromio(char *dest, unsigned int sz, + const u32 __iomem *from, + unsigned int num_dwords) +{ + int i; + const unsigned int rowsz = 16, groupsz = 4; + const unsigned int size = num_dwords * sizeof(u32); + unsigned int linelen, printed = 0, remaining = size; + + u8 *tmp = kmalloc(size, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + memcpy_fromio(tmp, from, size); + for (i = 0; i < size; i += rowsz) { + linelen = min(remaining, rowsz); + remaining -= rowsz; + hex_dump_to_buffer(tmp + i, linelen, rowsz, groupsz, + dest + printed, sz - printed, false); + printed += linelen * 2 + linelen / groupsz - 1; + *(dest + printed++) = '\n'; + *(dest + printed) = 0; + } + kfree(tmp); + return 0; +} + +static inline int copy_sram_to_user_buffer(char __user *user_buf, size_t count, loff_t *ppos, + unsigned int num_dwords, const u32 __iomem *from, + u32 offset) +{ + ssize_t bytes_read; + char *buf; + int pos; + unsigned int bufsz = 48 + sizeof(u32) * num_dwords * (2 + 1) + 1; + + buf = kmalloc(bufsz, GFP_KERNEL); + if (!buf) { + pr_err("%s: no memory\n", __func__); + return -ENOMEM; + } + *buf = 0; + pos = scnprintf(buf, 48, "Reading %u dwords from offset %#x\n", + num_dwords, offset); + read_buffer_fromio(buf + pos, bufsz - pos, from, num_dwords); + bytes_read = simple_read_from_buffer(user_buf, count, ppos, + buf, strlen(buf)); + kfree(buf); + return bytes_read; +} + +static ssize_t sst_debug_sram_lpe_debug_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + + struct intel_sst_drv *drv = file->private_data; + int ret = 0; + + ret = is_fw_running(drv); + if (ret) + return ret; + + ret = copy_sram_to_user_buffer(user_buf, count, ppos, RESVD_DUMP_SZ, + (u32 *)(drv->mailbox + SST_RESERVED_OFFSET), + SST_RESERVED_OFFSET); + sst_pm_runtime_put(drv); + return ret; +} + +static const struct file_operations sst_debug_sram_lpe_debug_ops = { + .open = simple_open, + .read = sst_debug_sram_lpe_debug_read, + .llseek = default_llseek, +}; + +static ssize_t sst_debug_sram_lpe_checkpoint_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + + struct intel_sst_drv *drv = file->private_data; + int ret = 0; + u32 offset; + + ret = is_fw_running(drv); + if (ret) + return ret; + + offset = sst_drv_ctx->pdata->debugfs_data->checkpoint_offset; + + ret = copy_sram_to_user_buffer(user_buf, count, ppos, + sst_drv_ctx->pdata->debugfs_data->checkpoint_size, + (u32 *)(drv->mailbox + offset), offset); + sst_pm_runtime_put(drv); + return ret; +} + +static const struct file_operations sst_debug_sram_lpe_checkpoint_ops = { + .open = simple_open, + .read = sst_debug_sram_lpe_checkpoint_read, + .llseek = default_llseek, +}; + +static ssize_t sst_debug_sram_ia_lpe_mbox_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + + struct intel_sst_drv *drv = file->private_data; + int ret = 0; + + ret = is_fw_running(drv); + if (ret) + return ret; + ret = copy_sram_to_user_buffer(user_buf, count, ppos, IA_LPE_MAILBOX_DUMP_SZ, + (u32 *)(drv->mailbox + SST_MAILBOX_SEND), + SST_MAILBOX_SEND); + sst_pm_runtime_put(drv); + return ret; +} + +static const struct file_operations sst_debug_sram_ia_lpe_mbox_ops = { + .open = simple_open, + .read = sst_debug_sram_ia_lpe_mbox_read, + .llseek = default_llseek, +}; + +static ssize_t sst_debug_sram_lpe_ia_mbox_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + + struct intel_sst_drv *drv = file->private_data; + int ret = 0; + + ret = is_fw_running(drv); + if (ret) + return ret; + + ret = copy_sram_to_user_buffer(user_buf, count, ppos, LPE_IA_MAILBOX_DUMP_SZ, + (u32 *)(drv->mailbox + drv->mailbox_recv_offset), + drv->mailbox_recv_offset); + sst_pm_runtime_put(drv); + return ret; +} + +static const struct file_operations sst_debug_sram_lpe_ia_mbox_ops = { + .open = simple_open, + .read = sst_debug_sram_lpe_ia_mbox_read, + .llseek = default_llseek, +}; + +static ssize_t sst_debug_sram_lpe_scu_mbox_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct intel_sst_drv *drv = file->private_data; + int ret = 0; + + ret = is_fw_running(drv); + if (ret) + return ret; + ret = copy_sram_to_user_buffer(user_buf, count, ppos, LPE_SCU_MAILBOX_DUMP_SZ, + (u32 *)(drv->mailbox + SST_LPE_SCU_MAILBOX), + SST_LPE_SCU_MAILBOX); + sst_pm_runtime_put(drv); + return ret; +} + +static const struct file_operations sst_debug_sram_lpe_scu_mbox_ops = { + .open = simple_open, + .read = sst_debug_sram_lpe_scu_mbox_read, + .llseek = default_llseek, +}; + +static ssize_t sst_debug_sram_scu_lpe_mbox_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ struct intel_sst_drv *drv = file->private_data; + int ret = 0; + + ret = is_fw_running(drv); + if (ret) + return ret; + ret = copy_sram_to_user_buffer(user_buf, count, ppos, SCU_LPE_MAILBOX_DUMP_SZ, + (u32 *)(drv->mailbox + SST_SCU_LPE_MAILBOX), + SST_SCU_LPE_MAILBOX); + sst_pm_runtime_put(drv); + return ret; +} + +static const struct file_operations sst_debug_sram_scu_lpe_mbox_ops = { + .open = simple_open, + .read = sst_debug_sram_scu_lpe_mbox_read, + .llseek = default_llseek, +}; + +static ssize_t sst_debug_lpe_log_enable_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct intel_sst_drv *drv = file->private_data; + struct ipc_post *msg = NULL; + char buf[32]; + int str_id = 0; /* DUMMY, required by post message */ + struct snd_sst_lpe_log_params params; + int ret_val = 0; + char *start = buf, *end; + int i = 0; + u8 *addr; + unsigned long tmp; + + size_t buf_size = min(count, sizeof(buf)-1); + memset(¶ms, 0, sizeof(params)); + + ret_val = is_fw_running(drv); + if (ret_val) + return ret_val; + + if (copy_from_user(buf, user_buf, buf_size)) { + ret_val = -EFAULT; + goto put_pm_runtime; + } + + buf[buf_size] = 0; + + addr = ¶ms.dbg_type; + for (i = 0; i < (sizeof(params) - sizeof(u8)); i++) { + while (*start == ' ') + start++; + end = start; + while (isalnum(*end)) + end++; + *end = 0; + ret_val = kstrtoul(start, 16, &tmp); + if (ret_val) { + pr_err("kstrtoul failed, ret_val = %d\n", ret_val); + goto put_pm_runtime; + } + *addr++ = (u8)tmp; + start = end + 1; + } + + pr_debug("dbg_type = %d module_id = %d log_level = %d\n", + params.dbg_type, params.module_id, params.log_level); + + if (params.dbg_type < NO_DEBUG || params.dbg_type > PTI_DEBUG) { + ret_val = -EINVAL; + goto put_pm_runtime; + } + + ret_val = sst_create_ipc_msg(&msg, true); + if (ret_val != 0) + goto put_pm_runtime; + + if (sst_drv_ctx->pci_id != SST_MRFLD_PCI_ID) { + sst_fill_header(&msg->header, IPC_IA_DBG_LOG_ENABLE, 1, + str_id); + msg->header.part.data = sizeof(u32) + sizeof(params); + memcpy(msg->mailbox_data, &msg->header.full, sizeof(u32)); + memcpy(msg->mailbox_data + sizeof(u32), ¶ms, + sizeof(params)); + } + drv->ops->sync_post_message(msg); + ret_val = buf_size; +put_pm_runtime: + sst_pm_runtime_put(drv); + return ret_val; +} + +/* + * Circular buffer hdr -> 0x1000 + * log data starts at 0x1010 + */ +static ssize_t sst_debug_lpe_log_enable_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct intel_sst_drv *drv = file->private_data; + struct lpe_log_buf_hdr buf_hdr; + size_t size1, size2, offset, bytes_read; + char *buf = NULL; + int ret; + + ret = is_fw_running(drv); + if (ret) + return ret; + + /* Get the sram lpe log buffer header */ + memcpy_fromio(&buf_hdr, (u32 *)(drv->mailbox + SST_SCU_LPE_MAILBOX), + sizeof(buf_hdr)); + if (buf_hdr.rd_addr == buf_hdr.wr_addr) { + pr_err("SRAM emptry\n"); + ret = -ENODATA; + goto put_pm_runtime; + } else if (buf_hdr.rd_addr < buf_hdr.wr_addr) { + size1 = buf_hdr.wr_addr - buf_hdr.rd_addr; + offset = (buf_hdr.rd_addr - buf_hdr.base_addr) + + SST_SCU_LPE_LOG_BUF; + pr_debug("Size = %zu, offset = %zx\n", size1, offset); + buf = vmalloc(size1); + if (buf == NULL) { + pr_err("Not enough memory to allocate\n"); + ret = -ENOMEM; + goto put_pm_runtime; + } + memcpy_fromio(buf, (u32 *)(drv->mailbox + offset), size1); + bytes_read = simple_read_from_buffer(user_buf, count, ppos, + buf, size1); + + buf_hdr.rd_addr = buf_hdr.rd_addr + bytes_read; + + } else { + /* Read including the end address as well */ + size1 = buf_hdr.end_addr - buf_hdr.rd_addr + 1; + offset = (buf_hdr.rd_addr - buf_hdr.base_addr) + + SST_SCU_LPE_LOG_BUF; + pr_debug("Size = %zu, offset = %zx\n", size1, offset); + buf = vmalloc(size1); + if (buf == NULL) { + pr_err("Not enough memory to allocate\n"); + ret = -ENOMEM; + goto put_pm_runtime; + } + memcpy_fromio(buf, (u32 *)(drv->mailbox + offset), size1); + bytes_read = simple_read_from_buffer(user_buf, count, ppos, + buf, size1); + if (bytes_read != size1) { + buf_hdr.rd_addr = buf_hdr.rd_addr + bytes_read; + goto update_rd_ptr; + } + + /* Wrap around lpe log buffer here */ + vfree(buf); + buf = NULL; + size2 = (buf_hdr.wr_addr - buf_hdr.base_addr); + offset = SST_SCU_LPE_LOG_BUF; + pr_debug("Size = %zu, offset = %zx\n", size2, offset); + buf = vmalloc(size2); + if (buf == NULL) { + pr_err("Not enough memory to allocate\n"); + ret = -ENOMEM; + goto put_pm_runtime; + } + memcpy_fromio(buf, (u32 *)(drv->mailbox + offset), size2); + bytes_read += simple_read_from_buffer(user_buf, + (count - bytes_read), ppos, buf, size2); + buf_hdr.rd_addr = buf_hdr.base_addr + bytes_read - size1; + + } +update_rd_ptr: + if (bytes_read != 0) { + memcpy_toio((u32 *)(drv->mailbox + SST_SCU_LPE_MAILBOX + + 2 * sizeof(u32)), &(buf_hdr.rd_addr), sizeof(u32)); + pr_debug("read pointer restored\n"); + } + vfree(buf); + buf = NULL; + ret = bytes_read; +put_pm_runtime: + sst_pm_runtime_put(drv); + return ret; +} + +static const struct file_operations sst_debug_lpe_log_enable_ops = { + .open = simple_open, + .write = sst_debug_lpe_log_enable_write, + .read = sst_debug_lpe_log_enable_read, + .llseek = default_llseek, +}; + +static ssize_t sst_debug_rtpm_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct intel_sst_drv *drv = file->private_data; + char *status; + + int usage = atomic_read(&drv->pm_usage_count); + + pr_debug("RTPM usage: %d\n", usage); + status = drv->debugfs.runtime_pm_status ? "enabled\n" : "disabled\n"; + return simple_read_from_buffer(user_buf, count, ppos, + status, strlen(status)); +} + +static ssize_t sst_debug_rtpm_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct intel_sst_drv *drv = file->private_data; + char buf[16]; + int sz = min(count, sizeof(buf)-1); + + int usage = atomic_read(&drv->pm_usage_count); + + pr_debug("RTPM Usage: %d\n", usage); + if (copy_from_user(buf, user_buf, sz)) + return -EFAULT; + buf[sz] = 0; + + if (!strncmp(buf, "enable\n", sz)) { + /* already enabled? */ + if (drv->debugfs.runtime_pm_status) + return -EINVAL; + drv->debugfs.runtime_pm_status = 1; + pm_runtime_allow(drv->dev); + sz = 6; /* strlen("enable") */ + } else if (!strncmp(buf, "disable\n", sz)) { + if (!drv->debugfs.runtime_pm_status) + return -EINVAL; + drv->debugfs.runtime_pm_status = 0; + pm_runtime_forbid(drv->dev); + sz = 7; /* strlen("disable") */ + } else + return -EINVAL; + return sz; +} + +static const struct file_operations sst_debug_rtpm_ops = { + .open = simple_open, + .read = sst_debug_rtpm_read, + .write = sst_debug_rtpm_write, + .llseek = default_llseek, +}; + + +static ssize_t sst_debug_readme_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = + "\nAll files can be read using 'cat'\n" + "1. 'echo disable > runtime_pm' disables runtime PM and will prevent SST from suspending.\n" + "To enable runtime PM, echo 'enable' to runtime_pm. Dmesg will print the runtime pm usage\n" + "if logs are enabled.\n" + "2. Write to shim register using 'echo > shim_dump'.\n" + "Valid address range is between 0x00 to 0x80 in increments of 8.\n" + "3. echo 1 > fw_clear_context , This sets the flag to skip the context restore\n" + "4. echo 1 > fw_clear_cache , This sets the flag to clear the cached copy of firmware\n" + "5. echo 1 > fw_reset_state ,This sets the fw state to uninit\n" + "6. echo memcpy > fw_dwnld_mode, This will set the firmware download mode to memcpy\n" + " echo lli > fw_dwnld_mode, This will set the firmware download mode to\n" + "dma lli mode\n" + " echo dma > fw_dwnld_mode, This will set the firmware download mode to\n" + "dma single block mode\n" + "7. iram_dump, dram_dump, interfaces provide mmap support to\n" + "get the iram and dram dump, these buffers will have data only\n" + "after the recovery is triggered\n"; + + const char *ctp_buf = + "8. Enable input clock by 'echo enable > osc_clk0'.\n" + "This prevents the input OSC clock from switching off till it is disabled by\n" + "'echo disable > osc_clk0'. The status of the clock indicated who are using it.\n" + "9. lpe_log_enable usage:\n" + " echo > lpe_log_enable.\n" + "10. cat fw_ssp_reg,This will dump the ssp register contents\n" + "11. cat fw_dma_reg,This will dump the dma register contents\n"; + + const char *mrfld_buf = + "8. lpe_log_enable usage:\n" + " echo > lpe_log_enable.\n" + "9. cat fw_ssp_reg,This will dump the ssp register contents\n" + "10. cat fw_dma_reg,This will dump the dma register contents\n" + "11. ddr_imr_dump interface provides mmap support to get the imr dump,\n" + "this buffer will have data only after the recovery is triggered\n" + "12. ipc usage:\n" + "\t ipc file works only in binary mode. The ipc format is .\n" + "\t drv_id in the ipc header will be overwritten with unique driver id in the driver\n"; + + char *readme = NULL; + const char *buf2 = NULL; + int size, ret = 0; + + switch (sst_drv_ctx->pci_id) { + case SST_CLV_PCI_ID: + size = strlen(buf) + strlen(ctp_buf) + 2; + buf2 = ctp_buf; + break; + case SST_MRFLD_PCI_ID: + size = strlen(buf) + strlen(mrfld_buf) + 2; + buf2 = mrfld_buf; + break; + default: + size = strlen(buf) + 1; + }; + + readme = kmalloc(size, GFP_KERNEL); + if (readme == NULL) { + pr_err("%s: no memory\n", __func__); + return -ENOMEM; + } + + if (buf2) + sprintf(readme, "%s%s\n", buf, buf2); + else + sprintf(readme, "%s\n", buf); + + ret = simple_read_from_buffer(user_buf, count, ppos, + readme, strlen(readme)); + kfree(readme); + return ret; +} + +static const struct file_operations sst_debug_readme_ops = { + .open = simple_open, + .read = sst_debug_readme_read, +}; + +static ssize_t sst_debug_osc_clk0_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + char status[16]; + int mode = -1; +#ifdef CONFIG_INTEL_SCU_IPC_UTIL + mode = intel_scu_ipc_set_osc_clk0(0, CLK0_QUERY); +#endif + + snprintf(status, 16, "0x%x\n", mode); + return simple_read_from_buffer(user_buf, count, ppos, + status, strlen(status)); +} + +static ssize_t sst_debug_osc_clk0_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + char buf[16]; + int sz = min(count, sizeof(buf)-1); + + if (copy_from_user(buf, user_buf, sz)) + return -EFAULT; + buf[sz] = 0; + +#ifdef CONFIG_INTEL_SCU_IPC_UTIL + if (!strncmp(buf, "enable\n", sz)) { + intel_scu_ipc_set_osc_clk0(true, CLK0_DEBUG); + sz = 6; /* strlen("enable") */ + } else if (!strncmp(buf, "disable\n", sz)) { + intel_scu_ipc_set_osc_clk0(false, CLK0_DEBUG); + sz = 7; /* strlen("disable") */ + } else + return -EINVAL; +#endif + return sz; +} + +static const struct file_operations sst_debug_osc_clk0_ops = { + .open = simple_open, + .read = sst_debug_osc_clk0_read, + .write = sst_debug_osc_clk0_write, +}; + +static ssize_t sst_debug_fw_clear_cntx_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char *status; + + status = atomic_read(&sst_drv_ctx->fw_clear_context) ? \ + "clear fw cntx\n" : "do not clear fw cntx\n"; + + return simple_read_from_buffer(user_buf, count, ppos, + status, strlen(status)); + +} + +static ssize_t sst_debug_fw_clear_cntx_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) + +{ + char buf[16]; + int sz = min(count, sizeof(buf)-1); + + if (copy_from_user(buf, user_buf, sz)) + return -EFAULT; + buf[sz] = 0; + + if (!strncmp(buf, "1\n", sz)) + atomic_set(&sst_drv_ctx->fw_clear_context, 1); + else + atomic_set(&sst_drv_ctx->fw_clear_context, 0); + + return sz; + +} + +static const struct file_operations sst_debug_fw_clear_cntx = { + .open = simple_open, + .read = sst_debug_fw_clear_cntx_read, + .write = sst_debug_fw_clear_cntx_write, +}; + +static ssize_t sst_debug_fw_clear_cache_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char *status; + + status = atomic_read(&sst_drv_ctx->fw_clear_cache) ? \ + "cache clear flag set\n" : "cache clear flag not set\n"; + + return simple_read_from_buffer(user_buf, count, ppos, + status, strlen(status)); + +} + +static ssize_t sst_debug_fw_clear_cache_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) + +{ + char buf[16]; + int sz = min(count, sizeof(buf)-1); + + if (copy_from_user(buf, user_buf, sz)) + return -EFAULT; + buf[sz] = 0; + + if (!strncmp(buf, "1\n", sz)) + atomic_set(&sst_drv_ctx->fw_clear_cache, 1); + else + return -EINVAL; + + return sz; +} + +static const struct file_operations sst_debug_fw_clear_cache = { + .open = simple_open, + .read = sst_debug_fw_clear_cache_read, + .write = sst_debug_fw_clear_cache_write, +}; + +static ssize_t sst_debug_fw_reset_state_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char state[16]; + + sprintf(state, "%d\n", sst_drv_ctx->sst_state); + + return simple_read_from_buffer(user_buf, count, ppos, + state, strlen(state)); + +} + +static ssize_t sst_debug_fw_reset_state_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) + +{ + char buf[16]; + int sz = min(count, sizeof(buf)-1); + + if (copy_from_user(buf, user_buf, sz)) + return -EFAULT; + buf[sz] = 0; + + if (!strncmp(buf, "1\n", sz)) + sst_set_fw_state_locked(sst_drv_ctx, SST_UN_INIT); + else + return -EINVAL; + + return sz; + +} + +static const struct file_operations sst_debug_fw_reset_state = { + .open = simple_open, + .read = sst_debug_fw_reset_state_read, + .write = sst_debug_fw_reset_state_write, +}; + +static ssize_t sst_debug_dwnld_mode_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char *state = "error\n"; + + if (sst_drv_ctx->use_dma == 0) { + state = "memcpy\n"; + } else if (sst_drv_ctx->use_dma == 1) { + state = sst_drv_ctx->use_lli ? \ + "lli\n" : "dma\n"; + + } + + return simple_read_from_buffer(user_buf, count, ppos, + state, strlen(state)); + +} + +static ssize_t sst_debug_dwnld_mode_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) + +{ + char buf[16]; + int sz = min(count, sizeof(buf)-1); + + if (sst_drv_ctx->sst_state != SST_SUSPENDED && + sst_drv_ctx->sst_state != SST_UN_INIT) { + pr_err("FW should be in suspended/uninit state\n"); + return -EFAULT; + } + + if (copy_from_user(buf, user_buf, sz)) + return -EFAULT; + buf[sz] = '\0'; + + /* Firmware needs to be downloaded again to populate the lists */ + atomic_set(&sst_drv_ctx->fw_clear_cache, 1); + + if (!strncmp(buf, "memcpy\n", sz)) { + sst_drv_ctx->use_dma = 0; + } else if (!strncmp(buf, "lli\n", sz)) { + sst_drv_ctx->use_dma = 1; + sst_drv_ctx->use_lli = 1; + } else if (!strncmp(buf, "dma\n", sz)) { + sst_drv_ctx->use_dma = 1; + sst_drv_ctx->use_lli = 0; + } + return sz; + +} + +static const struct file_operations sst_debug_dwnld_mode = { + .open = simple_open, + .read = sst_debug_dwnld_mode_read, + .write = sst_debug_dwnld_mode_write, +}; + +static int dump_ssp_port(void __iomem *ssp_base, char *buf, int pos) +{ + int index = 0; + + while (index < ARRAY_SIZE(ssp_reg_off)) { + pos += sprintf(buf + pos, "Reg: 0x%x: 0x%x\n", ssp_reg_off[index], + sst_reg_read(ssp_base, ssp_reg_off[index])); + index++; + } + return pos; +} + +static ssize_t sst_debug_ssp_reg_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char *buf; + int i, pos = 0, off = 0; + struct intel_sst_drv *drv = file->private_data; + int num_ssp, buf_size, ret; + + num_ssp = sst_drv_ctx->pdata->debugfs_data->num_ssp; + buf_size = DEBUGFS_SSP_BUF_SIZE * num_ssp; + + buf = kmalloc(buf_size, GFP_KERNEL); + if (!buf) { + pr_err("%s: no memory\n", __func__); + return -ENOMEM; + } + + ret = is_fw_running(drv); + if (ret) + goto err; + + buf[0] = 0; + + for (i = 0; i < num_ssp ; i++) { + if (!sst_drv_ctx->debugfs.ssp[i]) { + pr_err("ssp %d port not mapped\n", i); + continue; + } + off = sst_drv_ctx->pdata->debugfs_data->ssp_reg_size * i; + pos = dump_ssp_port((sst_drv_ctx->debugfs.ssp[i]), buf, pos); + } + sst_pm_runtime_put(drv); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); +err: + kfree(buf); + return ret; +} + +static const struct file_operations sst_debug_ssp_reg = { + .open = simple_open, + .read = sst_debug_ssp_reg_read, +}; + +static int dump_dma_reg(char *buf, int pos, int dma) +{ + int i, index = 0; + int off = 0 ; + void __iomem *dma_reg; + + if (!sst_drv_ctx->debugfs.dma_reg[dma]) { + pr_err("dma %d not mapped\n", dma); + return pos; + } + + pos += sprintf(buf + pos, "\nDump DMA%d Reg\n\n", dma); + + dma_reg = sst_drv_ctx->debugfs.dma_reg[dma]; + + /* Dump the DMA channel registers */ + for (i = 0; i < DMA_NUM_CH; i++) { + pos += sprintf(buf + pos, "SAR%d: 0x%x: 0x%llx\n", i, off, + sst_reg_read64(dma_reg, off)); + off += 8; + + pos += sprintf(buf + pos, "DAR%d: 0x%x: 0x%llx\n", i, off, + sst_reg_read64(dma_reg, off)); + off += 8; + + pos += sprintf(buf + pos, "LLP%d: 0x%x: 0x%llx\n", i, off, + sst_reg_read64(dma_reg, off)); + off += 8; + + pos += sprintf(buf + pos, "CTL%d: 0x%x: 0x%llx\n", i, off, + sst_reg_read64(dma_reg, off)); + off += 0x28; + + pos += sprintf(buf + pos, "CFG%d: 0x%x: 0x%llx\n", i, off, + sst_reg_read64(dma_reg, off)); + off += 0x18; + } + + /* Dump the remaining DMA registers */ + while (index < ARRAY_SIZE(dma_reg_off)) { + pos += sprintf(buf + pos, "Reg: 0x%x: 0x%llx\n", dma_reg_off[index], + sst_reg_read64(dma_reg, dma_reg_off[index])); + index++; + } + return pos; +} + +static ssize_t sst_debug_dma_reg_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char *buf; + int pos = 0; + int ret, i; + struct intel_sst_drv *drv = file->private_data; + int num_dma, buf_size; + + num_dma = sst_drv_ctx->pdata->debugfs_data->num_dma; + buf_size = DEBUGFS_DMA_BUF_SIZE * num_dma; + + buf = kmalloc(buf_size, GFP_KERNEL); + if (!buf) { + pr_err("%s: no memory\n", __func__); + return -ENOMEM; + } + + ret = is_fw_running(drv); + if (ret) + goto err; + + buf[0] = 0; + + for (i = 0; i < num_dma; i++) + pos = dump_dma_reg(buf, pos, i); + + sst_pm_runtime_put(drv); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); +err: + kfree(buf); + return ret; +} + +static const struct file_operations sst_debug_dma_reg = { + .open = simple_open, + .read = sst_debug_dma_reg_read, +}; + +/** + * sst_debug_remap - function remaps the iram/dram buff to userspace + * + * @vma: vm_area_struct passed from userspace + * @buf: Physical addr of the pointer to be remapped + * @type: type of the buffer + * + * Remaps the kernel buffer to the userspace + */ +static int sst_debug_remap(struct vm_area_struct *vma, char *buf, + enum sst_ram_type type) +{ + int retval, length; + void *mem_area; + + if (!buf) + return -EIO; + + length = vma->vm_end - vma->vm_start; + pr_debug("iram length 0x%x\n", length); + + /* round it up to the page bondary */ + mem_area = (void *)PAGE_ALIGN((unsigned long)buf); + + /* map the whole physically contiguous area in one piece */ + retval = remap_pfn_range(vma, + vma->vm_start, + virt_to_phys((void *)mem_area) >> PAGE_SHIFT, + length, + vma->vm_page_prot); + if (retval) + pr_err("mapping failed %d ", retval); + return retval; +} + +int sst_debug_iram_dump_mmap(struct file *file, struct vm_area_struct *vma) +{ + int retval; + struct intel_sst_drv *sst = sst_drv_ctx; + + retval = sst_debug_remap(vma, sst->dump_buf.iram_buf.buf, SST_IRAM); + + return retval; +} + +static const struct file_operations sst_debug_iram_dump = { + .open = simple_open, + .mmap = sst_debug_iram_dump_mmap, +}; + +int sst_debug_dram_dump_mmap(struct file *file, struct vm_area_struct *vma) +{ + int retval; + struct intel_sst_drv *sst = sst_drv_ctx; + + retval = sst_debug_remap(vma, sst->dump_buf.dram_buf.buf, SST_DRAM); + + return retval; +} + +static const struct file_operations sst_debug_dram_dump = { + .open = simple_open, + .mmap = sst_debug_dram_dump_mmap, +}; + +int sst_debug_ddr_imr_dump_mmap(struct file *file, struct vm_area_struct *vma) +{ + int retval; + struct intel_sst_drv *sst = sst_drv_ctx; + + retval = sst_debug_remap(vma, sst->ddr, 0); + + return retval; +} + +static const struct file_operations sst_debug_ddr_imr_dump = { + .open = simple_open, + .mmap = sst_debug_ddr_imr_dump_mmap, +}; + +static ssize_t sst_debug_ipc_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct intel_sst_drv *ctx = (struct intel_sst_drv *)file->private_data; + unsigned char *buf; + struct sst_block *block = NULL; + struct ipc_dsp_hdr *dsp_hdr; + struct ipc_post *msg = NULL; + int ret, res_rqd, msg_id, drv_id; + u32 low_payload; + + if (count > 1024) + return -EINVAL; + + ret = is_fw_running(ctx); + if (ret) + return ret; + + buf = kzalloc((sizeof(unsigned char) * (count)), GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto put_pm_runtime; + } + if (copy_from_user(buf, user_buf, count)) { + ret = -EFAULT; + goto free_mem; + } + + if (sst_create_ipc_msg(&msg, true)) { + ret = -ENOMEM; + goto free_mem; + } + + msg->mrfld_header.full = *((u64 *)buf); + pr_debug("ipc hdr: %llx\n", msg->mrfld_header.full); + + /* Override the drv id with unique drv id */ + drv_id = sst_assign_pvt_id(ctx); + msg->mrfld_header.p.header_high.part.drv_id = drv_id; + + res_rqd = msg->mrfld_header.p.header_high.part.res_rqd; + msg_id = msg->mrfld_header.p.header_high.part.msg_id; + pr_debug("res_rqd: %d, msg_id: %d, drv_id: %d\n", + res_rqd, msg_id, drv_id); + if (res_rqd) { + block = sst_create_block(ctx, msg_id, drv_id); + if (block == NULL) { + ret = -ENOMEM; + kfree(msg); + goto free_mem; + } + } + + dsp_hdr = (struct ipc_dsp_hdr *)(buf + 8); + pr_debug("dsp hdr: %llx\n", *((u64 *)(dsp_hdr))); + low_payload = msg->mrfld_header.p.header_low_payload; + if (low_payload > (1024 - sizeof(union ipc_header_mrfld))) { + pr_err("Invalid low payload length: %x\n", low_payload); + ret = -EINVAL; + kfree(msg); + goto free_block; + } + + memcpy(msg->mailbox_data, (buf+(sizeof(union ipc_header_mrfld))), + low_payload); + sst_add_to_dispatch_list_and_post(ctx, msg); + if (res_rqd) { + ret = sst_wait_timeout(ctx, block); + if (ret) { + pr_err("%s: fw returned err %d\n", __func__, ret); + goto free_block; + } + + if (msg_id == IPC_GET_PARAMS) { + unsigned char *r = block->data; + memcpy(ctx->debugfs.get_params_data, r, dsp_hdr->length); + ctx->debugfs.get_params_len = dsp_hdr->length; + } + + } + ret = count; +free_block: + if (res_rqd) + sst_free_block(sst_drv_ctx, block); +free_mem: + kfree(buf); +put_pm_runtime: + sst_pm_runtime_put(ctx); + return ret; +} + +static ssize_t sst_debug_ipc_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct intel_sst_drv *ctx = (struct intel_sst_drv *)file->private_data; + return simple_read_from_buffer(user_buf, count, ppos, + ctx->debugfs.get_params_data, + ctx->debugfs.get_params_len); +} + +static const struct file_operations sst_debug_ipc_ops = { + .open = simple_open, + .write = sst_debug_ipc_write, + .read = sst_debug_ipc_read, +}; + +struct sst_debug { + const char *name; + const struct file_operations *fops; + umode_t mode; +}; + +static const struct sst_debug sst_common_dbg_entries[] = { + {"runtime_pm", &sst_debug_rtpm_ops, 0600}, + {"shim_dump", &sst_debug_shim_ops, 0600}, + {"fw_clear_context", &sst_debug_fw_clear_cntx, 0600}, + {"fw_clear_cache", &sst_debug_fw_clear_cache, 0600}, + {"fw_reset_state", &sst_debug_fw_reset_state, 0600}, + {"fw_dwnld_mode", &sst_debug_dwnld_mode, 0600}, + {"iram_dump", &sst_debug_iram_dump, 0400}, + {"dram_dump", &sst_debug_dram_dump, 0400}, + {"sram_ia_lpe_mailbox", &sst_debug_sram_ia_lpe_mbox_ops, 0400}, + {"sram_lpe_ia_mailbox", &sst_debug_sram_lpe_ia_mbox_ops, 0400}, + {"README", &sst_debug_readme_ops, 0400}, +}; + +static const struct sst_debug ctp_dbg_entries[] = { + {"sram_lpe_debug", &sst_debug_sram_lpe_debug_ops, 0400}, + {"sram_lpe_checkpoint", &sst_debug_sram_lpe_checkpoint_ops, 0400}, + {"sram_lpe_scu_mailbox", &sst_debug_sram_lpe_scu_mbox_ops, 0400}, + {"sram_scu_lpe_mailbox", &sst_debug_sram_scu_lpe_mbox_ops, 0400}, + {"lpe_log_enable", &sst_debug_lpe_log_enable_ops, 0400}, + {"fw_ssp_reg", &sst_debug_ssp_reg, 0400}, + {"fw_dma_reg", &sst_debug_dma_reg, 0400}, + {"osc_clk0", &sst_debug_osc_clk0_ops, 0600}, +}; + +static const struct sst_debug mrfld_dbg_entries[] = { + {"sram_lpe_checkpoint", &sst_debug_sram_lpe_checkpoint_ops, 0400}, + {"fw_ssp_reg", &sst_debug_ssp_reg, 0400}, + {"fw_dma_reg", &sst_debug_dma_reg, 0400}, + {"ddr_imr_dump", &sst_debug_ddr_imr_dump, 0400}, + {"ipc", &sst_debug_ipc_ops, 0400}, +}; + +void sst_debugfs_create_files(struct intel_sst_drv *sst, + const struct sst_debug *entries, int size) +{ + int i; + + for (i = 0; i < size; i++) { + struct dentry *dentry; + const struct sst_debug *entry = &entries[i]; + + dentry = debugfs_create_file(entry->name, entry->mode, + sst->debugfs.root, sst, entry->fops); + if (dentry == NULL) { + pr_err("Failed to create %s file\n", entry->name); + return; + } + } +} + +void sst_debugfs_init(struct intel_sst_drv *sst) +{ + int size = 0; + const struct sst_debug *debug = NULL; + + sst->debugfs.root = debugfs_create_dir("sst", NULL); + if (IS_ERR(sst->debugfs.root) || !sst->debugfs.root) { + pr_err("Failed to create debugfs directory\n"); + return; + } + + sst_debugfs_create_files(sst, sst_common_dbg_entries, + ARRAY_SIZE(sst_common_dbg_entries)); + + /* Initial status is enabled */ + sst->debugfs.runtime_pm_status = 1; + + if (sst->pci_id == SST_MRFLD_PCI_ID) { + debug = mrfld_dbg_entries; + size = ARRAY_SIZE(mrfld_dbg_entries); + } else if (sst->pci_id == SST_CLV_PCI_ID) { + debug = ctp_dbg_entries; + size = ARRAY_SIZE(ctp_dbg_entries); + } + + if (debug) + sst_debugfs_create_files(sst, debug, size); + +} + +void sst_debugfs_exit(struct intel_sst_drv *sst) +{ + if (sst->debugfs.runtime_pm_status) + pm_runtime_allow(sst->dev); + debugfs_remove_recursive(sst->debugfs.root); +} diff --git a/sound/soc/intel/sst/sst_drv_interface.c b/sound/soc/intel/sst/sst_drv_interface.c new file mode 100644 index 000000000000..8250ccb23590 --- /dev/null +++ b/sound/soc/intel/sst/sst_drv_interface.c @@ -0,0 +1,1111 @@ +/* + * sst_drv_interface.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-10 Intel Corp + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sst_platform.h" +#include "../platform_ipc_v2.h" +#include "sst.h" + +#define NUM_CODEC 2 +#define MIN_FRAGMENT 2 +#define MAX_FRAGMENT 4 +#define MIN_FRAGMENT_SIZE (50 * 1024) +#define MAX_FRAGMENT_SIZE (1024 * 1024) +#define SST_GET_BYTES_PER_SAMPLE(pcm_wd_sz) (((pcm_wd_sz + 15) >> 4) << 1) + +void sst_restore_fw_context(void) +{ + struct snd_sst_ctxt_params fw_context; + struct ipc_post *msg = NULL; + int retval = 0; + struct sst_block *block; + + /* Skip the context restore, when fw_clear_context is set */ + /* fw_clear_context set through debugfs support */ + if (atomic_read(&sst_drv_ctx->fw_clear_context)) { + pr_debug("Skipping restore_fw_context\n"); + atomic_set(&sst_drv_ctx->fw_clear_context, 0); + return; + } + + pr_debug("restore_fw_context\n"); + /*nothing to restore*/ + if (!sst_drv_ctx->fw_cntx_size) + return; + pr_debug("restoring context......\n"); + /*send msg to fw*/ + retval = sst_create_block_and_ipc_msg(&msg, true, sst_drv_ctx, &block, + IPC_IA_SET_FW_CTXT, 0); + if (retval) { + pr_err("Can't allocate block/msg. No restore fw_context\n"); + return; + } + + sst_drv_ctx->sst_state = SST_FW_CTXT_RESTORE; + sst_fill_header(&msg->header, IPC_IA_SET_FW_CTXT, 1, 0); + + msg->header.part.data = sizeof(fw_context) + sizeof(u32); + fw_context.address = virt_to_phys((void *)sst_drv_ctx->fw_cntx); + fw_context.size = sst_drv_ctx->fw_cntx_size; + memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); + memcpy(msg->mailbox_data + sizeof(u32), + &fw_context, sizeof(fw_context)); + sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); + retval = sst_wait_timeout(sst_drv_ctx, block); + sst_free_block(sst_drv_ctx, block); + if (retval) + pr_err("sst_restore_fw_context..timeout!\n"); + return; +} + +/* + * sst_download_fw - download the audio firmware to DSP + * + * This function is called when the FW needs to be downloaded to SST DSP engine + */ +int sst_download_fw(void) +{ + int retval = 0; + + retval = sst_load_fw(); + if (retval) + return retval; + pr_debug("fw loaded successful!!!\n"); + + if (sst_drv_ctx->ops->restore_dsp_context) + sst_drv_ctx->ops->restore_dsp_context(); + sst_drv_ctx->sst_state = SST_FW_RUNNING; + return retval; +} + +int free_stream_context(unsigned int str_id) +{ + struct stream_info *stream; + int ret = 0; + + stream = get_stream_info(str_id); + if (stream) { + /* str_id is valid, so stream is alloacted */ + ret = sst_free_stream(str_id); + if (ret) + sst_clean_stream(&sst_drv_ctx->streams[str_id]); + return ret; + } + return ret; +} + +/* + * sst_send_algo_param - send LPE Mixer param to SST + * + * this function sends the algo parameter to sst dsp engine + */ +static int sst_send_algo_param(struct snd_ppp_params *algo_params) +{ + u32 header_size = 0; + struct ipc_post *msg = NULL; + u32 ipc_msg_size = sizeof(u32) + sizeof(*algo_params) + - sizeof(algo_params->params) + algo_params->size; + u32 offset = 0; + + if (ipc_msg_size > SST_MAILBOX_SIZE) + return -ENOMEM; + if (sst_create_ipc_msg(&msg, true)) + return -ENOMEM; + sst_fill_header(&msg->header, + IPC_IA_ALG_PARAMS, 1, algo_params->str_id); + msg->header.part.data = sizeof(u32) + sizeof(*algo_params) + - sizeof(algo_params->params) + algo_params->size; + memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); + offset = sizeof(u32); + header_size = sizeof(*algo_params) - sizeof(algo_params->params); + memcpy(msg->mailbox_data + sizeof(u32), algo_params, + sizeof(*algo_params) - sizeof(algo_params->params)); + offset += header_size; + memcpy(msg->mailbox_data + offset , algo_params->params, + algo_params->size); + sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); + return 0; +} + +static int sst_send_lpe_mixer_algo_params(void) +{ + struct snd_ppp_params algo_param; + struct snd_ppp_mixer_params mixer_param; + unsigned int input_mixer, stream_device_id; + int retval = 0; + + retval = intel_sst_check_device(); + if (retval) { + pr_err("sst_check_device failed %d\n", retval); + return retval; + } + + mutex_lock(&sst_drv_ctx->mixer_ctrl_lock); + input_mixer = (sst_drv_ctx->device_input_mixer) + & SST_INPUT_STREAM_MIXED; + pr_debug("Input Mixer settings %d", input_mixer); + stream_device_id = sst_drv_ctx->device_input_mixer - input_mixer; + algo_param.algo_id = SST_ALGO_MIXER; + algo_param.str_id = stream_device_id; + algo_param.enable = 1; + algo_param.operation = SST_SET_ALGO; + algo_param.size = sizeof(mixer_param); + mixer_param.type = SST_ALGO_PARAM_MIXER_STREAM_CFG; + mixer_param.input_stream_bitmap = input_mixer; + mixer_param.size = sizeof(input_mixer); + algo_param.params = &mixer_param; + mutex_unlock(&sst_drv_ctx->mixer_ctrl_lock); + pr_debug("setting pp param\n"); + pr_debug("Algo ID %d Str id %d Enable %d Size %d\n", + algo_param.algo_id, algo_param.str_id, + algo_param.enable, algo_param.size); + sst_send_algo_param(&algo_param); + sst_pm_runtime_put(sst_drv_ctx); + return retval; +} + +/* + * sst_get_stream_allocated - this function gets a stream allocated with + * the given params + * + * @str_param : stream params + * @lib_dnld : pointer to pointer of lib downlaod struct + * + * This creates new stream id for a stream, in case lib is to be downloaded to + * DSP, it downloads that + */ +int sst_get_stream_allocated(struct snd_sst_params *str_param, + struct snd_sst_lib_download **lib_dnld) +{ + int retval, str_id; + struct sst_block *block; + struct snd_sst_alloc_response *response; + struct stream_info *str_info; + + pr_debug("In %s\n", __func__); + block = sst_create_block(sst_drv_ctx, 0, 0); + if (block == NULL) + return -ENOMEM; + + retval = sst_drv_ctx->ops->alloc_stream((char *) str_param, block); + str_id = retval; + if (retval < 0) { + pr_err("sst_alloc_stream failed %d\n", retval); + goto free_block; + } + pr_debug("Stream allocated %d\n", retval); + str_info = get_stream_info(str_id); + if (str_info == NULL) { + pr_err("get stream info returned null\n"); + str_id = -EINVAL; + goto free_block; + } + + /* Block the call for reply */ + retval = sst_wait_timeout(sst_drv_ctx, block); + if (block->data) { + response = (struct snd_sst_alloc_response *)block->data; + retval = response->str_type.result; + if (!retval) + goto free_block; + + pr_err("sst: FW alloc failed retval %d\n", retval); + if (retval == SST_ERR_STREAM_IN_USE) { + pr_err("sst:FW not in clean state, send free for:%d\n", + str_id); + sst_free_stream(str_id); + *lib_dnld = NULL; + } + if (retval == SST_LIB_ERR_LIB_DNLD_REQUIRED) { + *lib_dnld = kzalloc(sizeof(**lib_dnld), GFP_KERNEL); + if (*lib_dnld == NULL) { + str_id = -ENOMEM; + goto free_block; + } + memcpy(*lib_dnld, &response->lib_dnld, sizeof(**lib_dnld)); + sst_clean_stream(str_info); + } else { + *lib_dnld = NULL; + } + str_id = -retval; + } else if (retval != 0) { + pr_err("sst: FW alloc failed retval %d\n", retval); + /* alloc failed, so reset the state to uninit */ + str_info->status = STREAM_UN_INIT; + str_id = retval; + } +free_block: + sst_free_block(sst_drv_ctx, block); + return str_id; /*will ret either error (in above if) or correct str id*/ +} + +/* + * sst_get_sfreq - this function returns the frequency of the stream + * + * @str_param : stream params + */ +int sst_get_sfreq(struct snd_sst_params *str_param) +{ + switch (str_param->codec) { + case SST_CODEC_TYPE_PCM: + return str_param->sparams.uc.pcm_params.sfreq; + case SST_CODEC_TYPE_AAC: + return str_param->sparams.uc.aac_params.externalsr; + case SST_CODEC_TYPE_MP3: + return 0; + default: + return -EINVAL; + } +} + +/* + * sst_get_sfreq - this function returns the frequency of the stream + * + * @str_param : stream params + */ +int sst_get_num_channel(struct snd_sst_params *str_param) +{ + switch (str_param->codec) { + case SST_CODEC_TYPE_PCM: + return str_param->sparams.uc.pcm_params.num_chan; + case SST_CODEC_TYPE_MP3: + return str_param->sparams.uc.mp3_params.num_chan; + case SST_CODEC_TYPE_AAC: + return str_param->sparams.uc.aac_params.num_chan; + default: + return -EINVAL; + } +} + +/* + * sst_get_stream - this function prepares for stream allocation + * + * @str_param : stream param + */ +int sst_get_stream(struct snd_sst_params *str_param) +{ + int retval; + struct stream_info *str_info; + struct snd_sst_lib_download *lib_dnld; + + pr_debug("In %s\n", __func__); + /* stream is not allocated, we are allocating */ + retval = sst_get_stream_allocated(str_param, &lib_dnld); + + if (retval == -(SST_LIB_ERR_LIB_DNLD_REQUIRED)) { + /* codec download is required */ + + pr_debug("Codec is required.... trying that\n"); + if (lib_dnld == NULL) { + pr_err("lib download null!!! abort\n"); + return -EIO; + } + + retval = sst_load_library(lib_dnld, str_param->ops); + kfree(lib_dnld); + + if (!retval) { + pr_debug("codec was downloaded successfully\n"); + + retval = sst_get_stream_allocated(str_param, &lib_dnld); + if (retval <= 0) { + retval = -EIO; + goto err; + } + + pr_debug("Alloc done stream id %d\n", retval); + } else { + pr_debug("codec download failed\n"); + retval = -EIO; + goto err; + } + } else if (retval <= 0) { + retval = -EIO; + goto err; + } + /* store sampling freq */ + str_info = &sst_drv_ctx->streams[retval]; + str_info->sfreq = sst_get_sfreq(str_param); + +err: + return retval; +} + +/** +* intel_sst_check_device - checks SST device +* +* This utility function checks the state of SST device and downlaods FW if +* not done, or resumes the device if suspended +*/ +int intel_sst_check_device(void) +{ + int retval = 0; + + pr_debug("In %s\n", __func__); + + pm_runtime_get_sync(sst_drv_ctx->dev); + atomic_inc(&sst_drv_ctx->pm_usage_count); + + pr_debug("%s: count is %d now\n", __func__, + atomic_read(&sst_drv_ctx->pm_usage_count)); + + mutex_lock(&sst_drv_ctx->sst_lock); + if (sst_drv_ctx->sst_state == SST_UN_INIT) + sst_drv_ctx->sst_state = SST_START_INIT; + + if (sst_drv_ctx->sst_state == SST_START_INIT || + sst_drv_ctx->sst_state == SST_FW_LIB_LOAD) { + + /* FW is not downloaded */ + pr_debug("DSP Downloading FW now...\n"); + retval = sst_download_fw(); + if (retval) { + pr_err("FW download fail %x\n", retval); + sst_drv_ctx->sst_state = SST_UN_INIT; + mutex_unlock(&sst_drv_ctx->sst_lock); + sst_pm_runtime_put(sst_drv_ctx); + return retval; + } + } + mutex_unlock(&sst_drv_ctx->sst_lock); + return retval; +} + +void sst_process_mad_ops(struct work_struct *work) +{ + + struct mad_ops_wq *mad_ops = + container_of(work, struct mad_ops_wq, wq); + int retval = 0; + + switch (mad_ops->control_op) { + case SST_SND_PAUSE: + retval = sst_pause_stream(mad_ops->stream_id); + break; + case SST_SND_RESUME: + retval = sst_resume_stream(mad_ops->stream_id); + break; + default: + pr_err(" wrong control_ops reported\n"); + } + if (retval) + pr_err("%s(): op: %d, retval: %d\n", + __func__, mad_ops->control_op, retval); + kfree(mad_ops); + return; +} + +static int sst_power_control(bool state) +{ + pr_debug("%s for %d", __func__, state); + + /* should we do ref count here, or rely on pcm handle?? */ + if (state == true) + return intel_sst_check_device(); + else + return sst_pm_runtime_put(sst_drv_ctx); +} +/* + * sst_open_pcm_stream - Open PCM interface + * + * @str_param: parameters of pcm stream + * + * This function is called by MID sound card driver to open + * a new pcm interface + */ +static int sst_open_pcm_stream(struct snd_sst_params *str_param) +{ + int retval; + + if (!str_param) + return -EINVAL; + + pr_debug("%s: doing rtpm_get\n", __func__); + + retval = intel_sst_check_device(); + + if (retval) + return retval; + retval = sst_get_stream(str_param); + if (retval > 0) { + sst_drv_ctx->stream_cnt++; + } else { + pr_err("sst_get_stream returned err %d\n", retval); + sst_pm_runtime_put(sst_drv_ctx); + } + + return retval; +} + +static int sst_cdev_open(struct snd_sst_params *str_params, + struct sst_compress_cb *cb) +{ + int str_id, retval; + struct stream_info *stream; + + pr_debug("%s: doing rtpm_get\n", __func__); + + retval = intel_sst_check_device(); + if (retval) + return retval; + + str_id = sst_get_stream(str_params); + if (str_id > 0) { + pr_debug("stream allocated in sst_cdev_open %d\n", str_id); + stream = &sst_drv_ctx->streams[str_id]; + stream->compr_cb = cb->compr_cb; + stream->compr_cb_param = cb->param; + stream->drain_notify = cb->drain_notify; + stream->drain_cb_param = cb->drain_cb_param; + } else { + pr_err("stream encountered error during alloc %d\n", str_id); + str_id = -EINVAL; + sst_pm_runtime_put(sst_drv_ctx); + } + return str_id; +} + +static int sst_cdev_close(unsigned int str_id) +{ + int retval; + struct stream_info *stream; + + pr_debug("%s: Entry\n", __func__); + stream = get_stream_info(str_id); + if (!stream) { + pr_err("stream info is NULL for str %d!!!\n", str_id); + return -EINVAL; + } + + if (stream->status == STREAM_RESET) { + /* silently fail here as we have cleaned the stream */ + pr_debug("stream in reset state...\n"); + stream->status = STREAM_UN_INIT; + + retval = 0; + goto put; + } + + retval = sst_free_stream(str_id); +put: + stream->compr_cb_param = NULL; + stream->compr_cb = NULL; + + /* The free_stream will return a error if there is no stream to free, + (i.e. the alloc failure case). And in this case the open does a put in + the error scenario, so skip in this case. + In the close we need to handle put in the success scenario and + the timeout error(EBUSY) scenario. */ + if (!retval || (retval == -EBUSY)) + sst_pm_runtime_put(sst_drv_ctx); + else + pr_err("%s: free stream returned err %d\n", __func__, retval); + + pr_debug("%s: End\n", __func__); + return retval; + +} + +static int sst_cdev_ack(unsigned int str_id, unsigned long bytes) +{ + struct stream_info *stream; + struct snd_sst_tstamp fw_tstamp = {0,}; + int offset; + void __iomem *addr; + + pr_debug("sst: ackfor %d\n", str_id); + stream = get_stream_info(str_id); + if (!stream) + return -EINVAL; + + /* update bytes sent */ + stream->cumm_bytes += bytes; + pr_debug("bytes copied %d inc by %ld\n", stream->cumm_bytes, bytes); + + memcpy_fromio(&fw_tstamp, + ((void *)(sst_drv_ctx->mailbox + sst_drv_ctx->tstamp) + +(str_id * sizeof(fw_tstamp))), + sizeof(fw_tstamp)); + + fw_tstamp.bytes_copied = stream->cumm_bytes; + pr_debug("bytes sent to fw %llu inc by %ld\n", fw_tstamp.bytes_copied, + bytes); + + addr = ((void *)(sst_drv_ctx->mailbox + sst_drv_ctx->tstamp)) + + (str_id * sizeof(fw_tstamp)); + offset = offsetof(struct snd_sst_tstamp, bytes_copied); + sst_shim_write(addr, offset, fw_tstamp.bytes_copied); + return 0; + +} + +static int sst_cdev_set_metadata(unsigned int str_id, + struct snd_compr_metadata *metadata) +{ + int retval = 0, pvt_id, len; + struct ipc_post *msg = NULL; + struct stream_info *str_info; + struct ipc_dsp_hdr dsp_hdr; + + pr_debug("set metadata for stream %d\n", str_id); + + str_info = get_stream_info(str_id); + if (!str_info) + return -EINVAL; + + if (sst_create_ipc_msg(&msg, 1)) + return -ENOMEM; + + if (!sst_drv_ctx->use_32bit_ops) { + pvt_id = sst_assign_pvt_id(sst_drv_ctx); + pr_debug("pvt id = %d\n", pvt_id); + pr_debug("pipe id = %d\n", str_info->pipe_id); + sst_fill_header_mrfld(&msg->mrfld_header, + IPC_CMD, str_info->task_id, 1, pvt_id); + + len = sizeof(*metadata) + sizeof(dsp_hdr); + msg->mrfld_header.p.header_low_payload = len; + sst_fill_header_dsp(&dsp_hdr, IPC_IA_SET_STREAM_PARAMS_MRFLD, + str_info->pipe_id, sizeof(*metadata)); + memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr)); + memcpy(msg->mailbox_data + sizeof(dsp_hdr), + metadata, sizeof(*metadata)); + } else { + sst_fill_header(&msg->header, IPC_IA_SET_STREAM_PARAMS, + 1, str_id); + msg->header.part.data = sizeof(u32) + sizeof(*metadata); + memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); + memcpy(msg->mailbox_data + sizeof(u32), + metadata, sizeof(*metadata)); + } + + sst_drv_ctx->ops->sync_post_message(msg); + return retval; +} + +static int sst_cdev_control(unsigned int cmd, unsigned int str_id) +{ + pr_debug("recieved cmd %d on stream %d\n", cmd, str_id); + + if (sst_drv_ctx->sst_state == SST_UN_INIT) + return 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + return sst_pause_stream(str_id); + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + return sst_resume_stream(str_id); + case SNDRV_PCM_TRIGGER_START: { + struct stream_info *str_info; + str_info = get_stream_info(str_id); + if (!str_info) + return -EINVAL; + str_info->prev = str_info->status; + str_info->status = STREAM_RUNNING; + return sst_start_stream(str_id); + } + case SNDRV_PCM_TRIGGER_STOP: + return sst_drop_stream(str_id); + case SND_COMPR_TRIGGER_DRAIN: + return sst_drain_stream(str_id, false); + case SND_COMPR_TRIGGER_NEXT_TRACK: + return sst_next_track(); + case SND_COMPR_TRIGGER_PARTIAL_DRAIN: + return sst_drain_stream(str_id, true); + default: + return -EINVAL; + } +} + +static int sst_cdev_tstamp(unsigned int str_id, struct snd_compr_tstamp *tstamp) +{ + struct snd_sst_tstamp fw_tstamp = {0,}; + struct stream_info *stream; + + memcpy_fromio(&fw_tstamp, + ((void *)(sst_drv_ctx->mailbox + sst_drv_ctx->tstamp) + +(str_id * sizeof(fw_tstamp))), + sizeof(fw_tstamp)); + + stream = get_stream_info(str_id); + if (!stream) + return -EINVAL; + pr_debug("rb_counter %llu in bytes\n", fw_tstamp.ring_buffer_counter); + + tstamp->copied_total = fw_tstamp.ring_buffer_counter; + tstamp->pcm_frames = fw_tstamp.frames_decoded; + tstamp->pcm_io_frames = div_u64(fw_tstamp.hardware_counter, + (u64)((stream->num_ch) * SST_GET_BYTES_PER_SAMPLE(24))); + tstamp->sampling_rate = fw_tstamp.sampling_frequency; + pr_debug("PCM = %u\n", tstamp->pcm_io_frames); + pr_debug("Pointer Query on strid = %d copied_total %d, decodec %d\n", + str_id, tstamp->copied_total, tstamp->pcm_frames); + pr_debug("rendered %d\n", tstamp->pcm_io_frames); + return 0; +} + +static int sst_cdev_caps(struct snd_compr_caps *caps) +{ + caps->num_codecs = NUM_CODEC; + caps->min_fragment_size = MIN_FRAGMENT_SIZE; /* 50KB */ + caps->max_fragment_size = MAX_FRAGMENT_SIZE; /* 1024KB */ + caps->min_fragments = MIN_FRAGMENT; + caps->max_fragments = MAX_FRAGMENT; + caps->codecs[0] = SND_AUDIOCODEC_MP3; + caps->codecs[1] = SND_AUDIOCODEC_AAC; + return 0; +} + +static int sst_cdev_codec_caps(struct snd_compr_codec_caps *codec) +{ + + if (codec->codec == SND_AUDIOCODEC_MP3) { + codec->num_descriptors = 2; + codec->descriptor[0].max_ch = 2; + codec->descriptor[0].sample_rates = SNDRV_PCM_RATE_8000_48000; + codec->descriptor[0].bit_rate[0] = 320; /* 320kbps */ + codec->descriptor[0].bit_rate[1] = 192; + codec->descriptor[0].num_bitrates = 2; + codec->descriptor[0].profiles = 0; + codec->descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO; + codec->descriptor[0].formats = 0; + } else if (codec->codec == SND_AUDIOCODEC_AAC) { + codec->num_descriptors = 2; + codec->descriptor[1].max_ch = 2; + codec->descriptor[1].sample_rates = SNDRV_PCM_RATE_8000_48000; + codec->descriptor[1].bit_rate[0] = 320; /* 320kbps */ + codec->descriptor[1].bit_rate[1] = 192; + codec->descriptor[1].num_bitrates = 2; + codec->descriptor[1].profiles = 0; + codec->descriptor[1].modes = 0; + codec->descriptor[1].formats = + (SND_AUDIOSTREAMFORMAT_MP4ADTS | + SND_AUDIOSTREAMFORMAT_RAW); + } else { + return -EINVAL; + } + + return 0; +} + +void sst_cdev_fragment_elapsed(int str_id) +{ + struct stream_info *stream; + + pr_debug("fragment elapsed from firmware for str_id %d\n", str_id); + stream = &sst_drv_ctx->streams[str_id]; + if (stream->compr_cb) + stream->compr_cb(stream->compr_cb_param); +} + +/* + * sst_close_pcm_stream - Close PCM interface + * + * @str_id: stream id to be closed + * + * This function is called by MID sound card driver to close + * an existing pcm interface + */ +static int sst_close_pcm_stream(unsigned int str_id) +{ + struct stream_info *stream; + int retval = 0; + + pr_debug("%s: Entry\n", __func__); + stream = get_stream_info(str_id); + if (!stream) { + pr_err("stream info is NULL for str %d!!!\n", str_id); + return -EINVAL; + } + + if (stream->status == STREAM_RESET) { + /* silently fail here as we have cleaned the stream */ + pr_debug("stream in reset state...\n"); + + retval = 0; + goto put; + } + + retval = free_stream_context(str_id); +put: + stream->pcm_substream = NULL; + stream->status = STREAM_UN_INIT; + stream->period_elapsed = NULL; + sst_drv_ctx->stream_cnt--; + + /* The free_stream will return a error if there is no stream to free, + (i.e. the alloc failure case). And in this case the open does a put in + the error scenario, so skip in this case. + In the close we need to handle put in the success scenario and + the timeout error(EBUSY) scenario. */ + if (!retval || (retval == -EBUSY)) + sst_pm_runtime_put(sst_drv_ctx); + else + pr_err("%s: free stream returned err %d\n", __func__, retval); + + pr_debug("%s: Exit\n", __func__); + return 0; +} + +int sst_send_sync_msg(int ipc, int str_id) +{ + struct ipc_post *msg = NULL; + + if (sst_create_ipc_msg(&msg, false)) + return -ENOMEM; + sst_fill_header(&msg->header, ipc, 0, str_id); + return sst_drv_ctx->ops->sync_post_message(msg); +} + +static inline int sst_calc_tstamp(struct pcm_stream_info *info, + struct snd_pcm_substream *substream, + struct snd_sst_tstamp *fw_tstamp) +{ + size_t delay_bytes, delay_frames; + size_t buffer_sz; + u32 pointer_bytes, pointer_samples; + + pr_debug("mrfld ring_buffer_counter %llu in bytes\n", + fw_tstamp->ring_buffer_counter); + pr_debug("mrfld hardware_counter %llu in bytes\n", + fw_tstamp->hardware_counter); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + delay_bytes = (size_t) (fw_tstamp->ring_buffer_counter - + fw_tstamp->hardware_counter); + else + delay_bytes = (size_t) (fw_tstamp->hardware_counter - + fw_tstamp->ring_buffer_counter); + delay_frames = bytes_to_frames(substream->runtime, delay_bytes); + buffer_sz = snd_pcm_lib_buffer_bytes(substream); + div_u64_rem(fw_tstamp->ring_buffer_counter, buffer_sz, &pointer_bytes); + pointer_samples = bytes_to_samples(substream->runtime, pointer_bytes); + + pr_debug("pcm delay %zu in bytes\n", delay_bytes); + + info->buffer_ptr = pointer_samples / substream->runtime->channels; + + info->pcm_delay = delay_frames / substream->runtime->channels; + pr_debug("buffer ptr %llu pcm_delay rep: %llu\n", + info->buffer_ptr, info->pcm_delay); + return 0; +} + +static int sst_read_timestamp(struct pcm_stream_info *info) +{ + struct stream_info *stream; + struct snd_pcm_substream *substream; + struct snd_sst_tstamp fw_tstamp; + unsigned int str_id; + + str_id = info->str_id; + stream = get_stream_info(str_id); + if (!stream) + return -EINVAL; + + if (!stream->pcm_substream) + return -EINVAL; + substream = stream->pcm_substream; + + memcpy_fromio(&fw_tstamp, + ((void *)(sst_drv_ctx->mailbox + sst_drv_ctx->tstamp) + + (str_id * sizeof(fw_tstamp))), + sizeof(fw_tstamp)); + return sst_calc_tstamp(info, substream, &fw_tstamp); +} + +/* + * sst_device_control - Set Control params + * + * @cmd: control cmd to be set + * @arg: command argument + * + * This function is called by MID sound card driver to set + * SST/Sound card controls for an opened stream. + * This is registered with MID driver + */ +static int sst_device_control(int cmd, void *arg) +{ + int retval = 0, str_id = 0; + + if (sst_drv_ctx->sst_state == SST_UN_INIT) + return 0; + + switch (cmd) { + case SST_SND_PAUSE: + case SST_SND_RESUME: { + struct mad_ops_wq *work = kzalloc(sizeof(*work), GFP_ATOMIC); + if (!work) + return -ENOMEM; + INIT_WORK(&work->wq, sst_process_mad_ops); + work->control_op = cmd; + work->stream_id = *(int *)arg; + queue_work(sst_drv_ctx->mad_wq, &work->wq); + break; + } + case SST_SND_START: { + struct stream_info *str_info; + int ipc; + str_id = *(int *)arg; + str_info = get_stream_info(str_id); + if (!str_info) + return -EINVAL; + ipc = IPC_IA_START_STREAM; + str_info->prev = str_info->status; + str_info->status = STREAM_RUNNING; + sst_start_stream(str_id); + break; + } + case SST_SND_DROP: { + struct stream_info *str_info; + int ipc; + str_id = *(int *)arg; + str_info = get_stream_info(str_id); + if (!str_info) + return -EINVAL; + ipc = IPC_IA_DROP_STREAM; + str_info->prev = STREAM_UN_INIT; + str_info->status = STREAM_INIT; + if (sst_drv_ctx->use_32bit_ops) + retval = sst_send_sync_msg(ipc, str_id); + else + retval = sst_drop_stream(str_id); + break; + } + case SST_SND_STREAM_INIT: { + struct pcm_stream_info *str_info; + struct stream_info *stream; + + pr_debug("stream init called\n"); + str_info = (struct pcm_stream_info *)arg; + str_id = str_info->str_id; + stream = get_stream_info(str_id); + if (!stream) { + retval = -EINVAL; + break; + } + pr_debug("setting the period ptrs\n"); + stream->pcm_substream = str_info->mad_substream; + stream->period_elapsed = str_info->period_elapsed; + stream->sfreq = str_info->sfreq; + stream->prev = stream->status; + stream->status = STREAM_INIT; + pr_debug("pcm_substream %p, period_elapsed %p, sfreq %d, status %d\n", + stream->pcm_substream, stream->period_elapsed, stream->sfreq, stream->status); + break; + } + + case SST_SND_BUFFER_POINTER: { + struct pcm_stream_info *stream_info; + + stream_info = (struct pcm_stream_info *)arg; + retval = sst_read_timestamp(stream_info); + pr_debug("pointer %llu, delay %llu\n", + stream_info->buffer_ptr, stream_info->pcm_delay); + break; + } + default: + /* Illegal case */ + pr_warn("illegal req\n"); + return -EINVAL; + } + + return retval; +} + +/* + * sst_copy_runtime_param - copy runtime params from src to dst + * structure. + * + *@dst: destination runtime structure + *@src: source runtime structure + * + * This helper function is called to copy the runtime parameter + * structure. +*/ +static int sst_copy_runtime_param(struct snd_sst_runtime_params *dst, + struct snd_sst_runtime_params *src) +{ + dst->type = src->type; + dst->str_id = src->str_id; + dst->size = src->size; + if (dst->addr) { + pr_err("mem allocated in prev setting, use the same memory\n"); + return -EINVAL; + } + dst->addr = kzalloc(dst->size, GFP_KERNEL); + if (!dst->addr) + return -ENOMEM; + memcpy(dst->addr, src->addr, dst->size); + return 0; +} +/* + * sst_set_generic_params - Set generic params + * + * @cmd: control cmd to be set + * @arg: command argument + * + * This function is called by MID sound card driver to configure + * SST runtime params. + */ +static int sst_set_generic_params(enum sst_controls cmd, void *arg) +{ + int ret_val = 0; + pr_debug("Enter:%s, cmd:%d\n", __func__, cmd); + + if (NULL == arg) + return -EINVAL; + + switch (cmd) { + case SST_SET_RUNTIME_PARAMS: { + struct snd_sst_runtime_params *src; + struct snd_sst_runtime_params *dst; + + src = (struct snd_sst_runtime_params *)arg; + dst = &(sst_drv_ctx->runtime_param.param); + ret_val = sst_copy_runtime_param(dst, src); + break; + } + case SST_SET_ALGO_PARAMS: { + unsigned int device_input_mixer = *((unsigned int *)arg); + pr_debug("LPE mixer algo param set %x\n", device_input_mixer); + mutex_lock(&sst_drv_ctx->mixer_ctrl_lock); + sst_drv_ctx->device_input_mixer = device_input_mixer; + mutex_unlock(&sst_drv_ctx->mixer_ctrl_lock); + ret_val = sst_send_lpe_mixer_algo_params(); + break; + } + case SST_SET_BYTE_STREAM: { + ret_val = intel_sst_check_device(); + if (ret_val) + return ret_val; + + ret_val = sst_send_byte_stream_mrfld(arg); + sst_pm_runtime_put(sst_drv_ctx); + break; + } + case SST_GET_PROBE_BYTE_STREAM: { + struct snd_sst_probe_bytes *prb_bytes = (struct snd_sst_probe_bytes *)arg; + + if (sst_drv_ctx->probe_bytes) { + prb_bytes->len = sst_drv_ctx->probe_bytes->len; + memcpy(prb_bytes->bytes, &sst_drv_ctx->probe_bytes->bytes, prb_bytes->len); + } + break; + } + case SST_SET_PROBE_BYTE_STREAM: { + struct snd_sst_probe_bytes *prb_bytes = (struct snd_sst_probe_bytes *)arg; + + if (sst_drv_ctx->probe_bytes) { + sst_drv_ctx->probe_bytes->len = prb_bytes->len; + memcpy(&sst_drv_ctx->probe_bytes->bytes, prb_bytes->bytes, prb_bytes->len); + } + + ret_val = intel_sst_check_device(); + if (ret_val) + return ret_val; + + ret_val = sst_send_probe_bytes(sst_drv_ctx); + break; + } + case SST_SET_VTSV_INFO: { + ret_val = intel_sst_check_device(); + if (ret_val) + return ret_val; + + ret_val = sst_send_vtsv_data_to_fw(sst_drv_ctx); + if (ret_val) + pr_err("vtsv data send failed\n"); + sst_pm_runtime_put(sst_drv_ctx); + break; + } + default: + pr_err("Invalid cmd request:%d\n", cmd); + ret_val = -EINVAL; + } + return ret_val; +} + +static struct sst_ops pcm_ops = { + .open = sst_open_pcm_stream, + .device_control = sst_device_control, + .set_generic_params = sst_set_generic_params, + .close = sst_close_pcm_stream, + .power = sst_power_control, +}; + +static struct compress_sst_ops compr_ops = { + .open = sst_cdev_open, + .close = sst_cdev_close, + .control = sst_cdev_control, + .tstamp = sst_cdev_tstamp, + .ack = sst_cdev_ack, + .get_caps = sst_cdev_caps, + .get_codec_caps = sst_cdev_codec_caps, + .set_metadata = sst_cdev_set_metadata, +}; + + +static struct sst_device sst_dsp_device = { + .name = "Intel(R) SST LPE", + .dev = NULL, + .ops = &pcm_ops, + .compr_ops = &compr_ops, +}; + +/* + * register_sst - function to register DSP + * + * This functions registers DSP with the platform driver + */ +int register_sst(struct device *dev) +{ + int ret_val; + sst_dsp_device.dev = dev; + ret_val = sst_register_dsp(&sst_dsp_device); + if (ret_val) + pr_err("Unable to register DSP with platform driver\n"); + + return ret_val; +} + +int unregister_sst(struct device *dev) +{ + return sst_unregister_dsp(&sst_dsp_device); +} diff --git a/sound/soc/intel/sst/sst_dsp.c b/sound/soc/intel/sst/sst_dsp.c new file mode 100644 index 000000000000..edfe4715ad0a --- /dev/null +++ b/sound/soc/intel/sst/sst_dsp.c @@ -0,0 +1,1995 @@ +/* + * sst_dsp.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-10 Intel Corp + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R + * KP Jeeja + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This file contains all dsp controlling functions like firmware download, + * setting/resetting dsp cores, etc + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sst_platform.h" +#include "../platform_ipc_v2.h" +#include "sst.h" +#include "sst_trace.h" + +#ifndef CONFIG_X86_64 +#define MEMCPY_TOIO memcpy_toio +#else +#define MEMCPY_TOIO memcpy32_toio +#endif + +static struct sst_module_info sst_modules_mrfld[] = { + {"mp3_dec", SST_CODEC_TYPE_MP3, 0, SST_LIB_NOT_FOUND}, + {"aac_dec", SST_CODEC_TYPE_AAC, 0, SST_LIB_NOT_FOUND}, + {"audclass_lib", SST_ALGO_AUDCLASSIFIER, 0, SST_LIB_NOT_FOUND}, + {"vtsv_lib", SST_ALGO_VTSV, 0, SST_LIB_NOT_FOUND}, + {"geq_lib", SST_ALGO_GEQ, 0, SST_LIB_NOT_FOUND}, +}; + +static struct sst_module_info sst_modules_byt[] = { + {"mp3_dec", SST_CODEC_TYPE_MP3, 0, SST_LIB_NOT_FOUND}, + {"aac_dec", SST_CODEC_TYPE_AAC, 0, SST_LIB_NOT_FOUND}, +}; + +/** + * memcpy32_toio: Copy using writel commands + * + * This is needed because the hardware does not support + * 64-bit moveq insructions while writing to PCI MMIO + */ +void memcpy32_toio(void *dst, const void *src, int count) +{ + int i; + const u32 *src_32 = src; + u32 *dst_32 = dst; + + for (i = 0; i < count/sizeof(u32); i++) + writel(*src_32++, dst_32++); +} + +/** + * intel_sst_reset_dsp_medfield - Resetting SST DSP + * + * This resets DSP in case of Medfield platfroms + */ +int intel_sst_reset_dsp_mfld(void) +{ + union config_status_reg csr; + + pr_debug("Resetting the DSP in medfield\n"); + mutex_lock(&sst_drv_ctx->csr_lock); + csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); + csr.full |= 0x382; + csr.part.run_stall = 0x1; + sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); + mutex_unlock(&sst_drv_ctx->csr_lock); + + return 0; +} + +/** + * sst_start_medfield - Start the SST DSP processor + * + * This starts the DSP in MRST platfroms + */ +int sst_start_mfld(void) +{ + union config_status_reg csr; + + mutex_lock(&sst_drv_ctx->csr_lock); + csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); + csr.part.bypass = 0; + sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); + csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); + csr.part.mfld_strb = 1; + sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); + csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); + csr.part.run_stall = 0; + csr.part.sst_reset = 0; + pr_debug("Starting the DSP_medfld %x\n", csr.full); + sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); + pr_debug("Starting the DSP_medfld\n"); + mutex_unlock(&sst_drv_ctx->csr_lock); + + return 0; +} +/** + * intel_sst_reset_dsp_mrfld - Resetting SST DSP + * + * This resets DSP in case of MRFLD platfroms + */ +int intel_sst_reset_dsp_mrfld(void) +{ + union config_status_reg_mrfld csr; + + pr_debug("sst: Resetting the DSP in mrfld\n"); + mutex_lock(&sst_drv_ctx->csr_lock); + csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); + + pr_debug("value:0x%llx\n", csr.full); + + csr.full |= 0x7; + sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full); + csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); + + pr_debug("value:0x%llx\n", csr.full); + + csr.full &= ~(0x1); + sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full); + + csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); + pr_debug("value:0x%llx\n", csr.full); + mutex_unlock(&sst_drv_ctx->csr_lock); + return 0; +} + +/** + * sst_start_merrifield - Start the SST DSP processor + * + * This starts the DSP in MERRIFIELD platfroms + */ +int sst_start_mrfld(void) +{ + union config_status_reg_mrfld csr; + + pr_debug("sst: Starting the DSP in mrfld LALALALA\n"); + mutex_lock(&sst_drv_ctx->csr_lock); + csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); + pr_debug("value:0x%llx\n", csr.full); + + csr.full |= 0x7; + sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full); + + csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); + pr_debug("value:0x%llx\n", csr.full); + + csr.part.xt_snoop = 1; + csr.full &= ~(0x5); + sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full); + + csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); + pr_debug("sst: Starting the DSP_merrifield:%llx\n", csr.full); + mutex_unlock(&sst_drv_ctx->csr_lock); + return 0; +} + +/** + * intel_sst_set_bypass - Sets/clears the bypass bits + * + * This sets/clears the bypass bits + */ +void intel_sst_set_bypass_mfld(bool set) +{ + union config_status_reg csr; + + mutex_lock(&sst_drv_ctx->csr_lock); + csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); + if (set == true) + csr.full |= 0x380; + else + csr.part.bypass = 0; + pr_debug("SetupByPass set %d Val 0x%x\n", set, csr.full); + sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); + mutex_unlock(&sst_drv_ctx->csr_lock); + +} +#define SST_CALC_DMA_DSTN(lpe_viewpt_rqd, ia_viewpt_addr, elf_paddr, \ + lpe_viewpt_addr) ((lpe_viewpt_rqd) ? \ + elf_paddr : (ia_viewpt_addr + elf_paddr - lpe_viewpt_addr)) + +static int sst_fill_dstn(struct intel_sst_drv *sst, struct sst_info info, + Elf32_Phdr *pr, void **dstn, unsigned int *dstn_phys, int *mem_type) +{ +#ifdef MRFLD_WORD_WA + /* work arnd-since only 4 byte align copying is only allowed for ICCM */ + if ((pr->p_paddr >= info.iram_start) && (pr->p_paddr < info.iram_end)) { + size_t data_size = pr->p_filesz % SST_ICCM_BOUNDARY; + + if (data_size) + pr->p_filesz += 4 - data_size; + *dstn = sst->iram + (pr->p_paddr - info.iram_start); + *dstn_phys = SST_CALC_DMA_DSTN(info.lpe_viewpt_rqd, + sst->iram_base, pr->p_paddr, info.iram_start); + *mem_type = 1; + } +#else + if ((pr->p_paddr >= info.iram_start) && + (pr->p_paddr < info.iram_end)) { + + *dstn = sst->iram + (pr->p_paddr - info.iram_start); + *dstn_phys = SST_CALC_DMA_DSTN(info.lpe_viewpt_rqd, + sst->iram_base, pr->p_paddr, info.iram_start); + *mem_type = 1; + } +#endif + else if ((pr->p_paddr >= info.dram_start) && + (pr->p_paddr < info.dram_end)) { + + *dstn = sst->dram + (pr->p_paddr - info.dram_start); + *dstn_phys = SST_CALC_DMA_DSTN(info.lpe_viewpt_rqd, + sst->dram_base, pr->p_paddr, info.dram_start); + *mem_type = 1; + } else if ((pr->p_paddr >= info.imr_start) && + (pr->p_paddr < info.imr_end)) { + + *dstn = sst->ddr + (pr->p_paddr - info.imr_start); + *dstn_phys = sst->ddr_base + pr->p_paddr - info.imr_start; + *mem_type = 0; + } else { + return -EINVAL; + } + return 0; +} + +static void sst_fill_info(struct intel_sst_drv *sst, + struct sst_info *info) +{ + /* first we setup addresses to be used for elf sections */ + if (sst->info.iram_use) { + info->iram_start = sst->info.iram_start; + info->iram_end = sst->info.iram_end; + } else { + info->iram_start = sst->iram_base; + info->iram_end = sst->iram_end; + } + if (sst->info.dram_use) { + info->dram_start = sst->info.dram_start; + info->dram_end = sst->info.dram_end; + } else { + info->dram_start = sst->dram_base; + info->dram_end = sst->dram_end; + } + if (sst->info.imr_use) { + info->imr_start = sst->info.imr_start; + info->imr_end = sst->info.imr_end; + } else { + info->imr_start = relocate_imr_addr_mrfld(sst->ddr_base); + info->imr_end = relocate_imr_addr_mrfld(sst->ddr_end); + } + + info->lpe_viewpt_rqd = sst->info.lpe_viewpt_rqd; + info->dma_max_len = sst->info.dma_max_len; + pr_debug("%s: dma_max_len 0x%x", __func__, info->dma_max_len); +} + +static inline int sst_validate_elf(const struct firmware *sst_bin, bool dynamic) +{ + Elf32_Ehdr *elf; + + BUG_ON(!sst_bin); + + pr_debug("IN %s\n", __func__); + + elf = (Elf32_Ehdr *)sst_bin->data; + + if ((elf->e_ident[0] != 0x7F) || (elf->e_ident[1] != 'E') || + (elf->e_ident[2] != 'L') || (elf->e_ident[3] != 'F')) { + pr_debug("ELF Header Not found!%zu\n", sst_bin->size); + return -EINVAL; + } + + if (dynamic == true) { + if (elf->e_type != ET_DYN) { + pr_err("Not a dynamic loadable library\n"); + return -EINVAL; + } + } + pr_debug("Valid ELF Header...%zu\n", sst_bin->size); + return 0; +} + +/** + * sst_validate_fw_image - validates the firmware signature + * + * @sst_fw_in_mem : pointer to audio FW + * @size : size of the firmware + * @module : points to the FW modules + * @num_modules : points to the num of modules + * This function validates the header signature in the FW image + */ +static int sst_validate_fw_image(const void *sst_fw_in_mem, unsigned long size, + struct fw_module_header **module, u32 *num_modules) +{ + struct fw_header *header; + + pr_debug("%s\n", __func__); + + /* Read the header information from the data pointer */ + header = (struct fw_header *)sst_fw_in_mem; + pr_debug("header sign=%s size=%x modules=%x fmt=%x size=%zx\n", + header->signature, header->file_size, header->modules, + header->file_format, sizeof(*header)); + + /* verify FW */ + if ((strncmp(header->signature, SST_FW_SIGN, 4) != 0) || + (size != header->file_size + sizeof(*header))) { + /* Invalid FW signature */ + pr_err("InvalidFW sign/filesize mismatch\n"); + return -EINVAL; + } + *num_modules = header->modules; + *module = (void *)sst_fw_in_mem + sizeof(*header); + + return 0; +} + +/** + * sst_validate_library - validates the library signature + * + * @fw_lib : pointer to FW library + * @slot : pointer to the lib slot info + * @entry_point : out param, which contains the module entry point + * This function is called before downloading the codec/postprocessing + * library + */ +static int sst_validate_library(const struct firmware *fw_lib, + struct lib_slot_info *slot, + u32 *entry_point) +{ + struct fw_header *header; + struct fw_module_header *module; + struct fw_block_info *block; + unsigned int n_blk, isize = 0, dsize = 0; + int err = 0; + + header = (struct fw_header *)fw_lib->data; + if (header->modules != 1) { + pr_err("Module no mismatch found\n"); + err = -EINVAL; + goto exit; + } + module = (void *)fw_lib->data + sizeof(*header); + *entry_point = module->entry_point; + pr_debug("Module entry point 0x%x\n", *entry_point); + pr_debug("Module Sign %s, Size 0x%x, Blocks 0x%x Type 0x%x\n", + module->signature, module->mod_size, + module->blocks, module->type); + + block = (void *)module + sizeof(*module); + for (n_blk = 0; n_blk < module->blocks; n_blk++) { + switch (block->type) { + case SST_IRAM: + isize += block->size; + break; + case SST_DRAM: + dsize += block->size; + break; + default: + pr_err("Invalid block type for 0x%x\n", n_blk); + err = -EINVAL; + goto exit; + } + block = (void *)block + sizeof(*block) + block->size; + } + if (isize > slot->iram_size || dsize > slot->dram_size) { + pr_err("library exceeds size allocated\n"); + err = -EINVAL; + goto exit; + } else + pr_debug("Library is safe for download...\n"); + + pr_debug("iram 0x%x, dram 0x%x, iram 0x%x, dram 0x%x\n", + isize, dsize, slot->iram_size, slot->dram_size); +exit: + return err; + +} + +static bool chan_filter(struct dma_chan *chan, void *param) +{ + struct sst_dma *dma = (struct sst_dma *)param; + + /* we only need MID_DMAC1 as that can access DSP RAMs*/ + if (chan->device->dev == dma->dev) + return true; + + return false; +} + +static unsigned int +sst_get_elf_sg_len(struct intel_sst_drv *sst, Elf32_Ehdr *elf, Elf32_Phdr *pr, + struct sst_info info) +{ + unsigned int i = 0, count = 0; + + pr_debug("in %s: dma_max_len 0x%x\n", __func__, info.dma_max_len); + + while (i < elf->e_phnum) { + if (pr[i].p_type == PT_LOAD) { + + if ((pr[i].p_paddr >= info.iram_start) && + (pr[i].p_paddr < info.iram_end && + pr[i].p_filesz)) { + count += (pr[i].p_filesz) / info.dma_max_len; + + if ((pr[i].p_filesz) % info.dma_max_len) + count++; + + } else if ((pr[i].p_paddr >= info.dram_start) && + (pr[i].p_paddr < info.dram_end && + pr[i].p_filesz)) { + count += (pr[i].p_filesz) / info.dma_max_len; + + if ((pr[i].p_filesz) % info.dma_max_len) + count++; + + } else if ((pr[i].p_paddr >= info.imr_start) && + (pr[i].p_paddr < info.imr_end && + pr[i].p_filesz)) { + count += (pr[i].p_filesz) / info.dma_max_len; + + if ((pr[i].p_filesz) % info.dma_max_len) + count++; + } + } + i++; + } + + pr_debug("gotcha count %d\n", count); + return count; +} + +static int +sst_init_dma_sg_list(struct intel_sst_drv *sst, unsigned int len, + struct scatterlist **src, struct scatterlist **dstn) +{ + struct scatterlist *sg_src = NULL, *sg_dst = NULL; + + sg_src = kzalloc(sizeof(*sg_src)*(len), GFP_KERNEL); + if (NULL == sg_src) + return -ENOMEM; + sg_init_table(sg_src, len); + sg_dst = kzalloc(sizeof(*sg_dst)*(len), GFP_KERNEL); + if (NULL == sg_dst) { + kfree(sg_src); + return -ENOMEM; + } + sg_init_table(sg_dst, len); + *src = sg_src; + *dstn = sg_dst; + + return 0; +} + +static int sst_alloc_dma_chan(struct sst_dma *dma) +{ + dma_cap_mask_t mask; + struct intel_mid_dma_slave *slave = &dma->slave; + int retval; + struct pci_dev *dmac = NULL; + const char *hid; + + pr_debug("%s\n", __func__); + dma->dev = NULL; + dma_cap_zero(mask); + dma_cap_set(DMA_MEMCPY, mask); + + if (sst_drv_ctx->pci_id == SST_CLV_PCI_ID) + dmac = pci_get_device(PCI_VENDOR_ID_INTEL, + PCI_DMAC_CLV_ID, NULL); + else if (sst_drv_ctx->pci_id == SST_MRFLD_PCI_ID) + dmac = pci_get_device(PCI_VENDOR_ID_INTEL, + PCI_DMAC_MRFLD_ID, NULL); + else if (sst_drv_ctx->pci_id == SST_BYT_PCI_ID) { + hid = sst_drv_ctx->hid; + if (!strncmp(hid, "LPE0F281", 8)) + dma->dev = intel_mid_get_acpi_dma("DMA0F28"); + else if (!strncmp(hid, "80860F28", 8)) + dma->dev = intel_mid_get_acpi_dma("ADMA0F28"); + else if (!strncmp(hid, "808622A8", 8)) + dma->dev = intel_mid_get_acpi_dma("ADMA22A8"); + else if (!strncmp(hid, "LPE0F28", 7)) + dma->dev = intel_mid_get_acpi_dma("DMA0F28"); + } + + if (!dmac && !dma->dev) { + pr_err("Can't find DMAC\n"); + return -ENODEV; + } + if (dmac) + dma->dev = &dmac->dev; + + dma->ch = dma_request_channel(mask, chan_filter, dma); + if (!dma->ch) { + pr_err("unable to request dma channel\n"); + return -EIO; + } + + slave->dma_slave.direction = DMA_MEM_TO_MEM; + slave->hs_mode = 0; + slave->cfg_mode = LNW_DMA_MEM_TO_MEM; + slave->dma_slave.src_addr_width = slave->dma_slave.dst_addr_width = + DMA_SLAVE_BUSWIDTH_4_BYTES; + slave->dma_slave.src_maxburst = slave->dma_slave.dst_maxburst = + LNW_DMA_MSIZE_16; + + retval = dmaengine_slave_config(dma->ch, &slave->dma_slave); + if (retval) { + pr_err("unable to set slave config, err %d\n", retval); + dma_release_channel(dma->ch); + return -EIO; + } + return retval; +} + +static void sst_dma_transfer_complete(void *arg) +{ + sst_drv_ctx = (struct intel_sst_drv *)arg; + pr_debug(" sst_dma_transfer_complete\n"); + sst_wake_up_block(sst_drv_ctx, 0, FW_DWNL_ID, FW_DWNL_ID, NULL, 0); +} + +static inline int sst_dma_wait_for_completion(struct intel_sst_drv *sst) +{ + int ret = 0; + struct sst_block *block; + /* call prep and wait */ + sst->desc->callback = sst_dma_transfer_complete; + sst->desc->callback_param = sst; + + block = sst_create_block(sst, FW_DWNL_ID, FW_DWNL_ID); + if (block == NULL) + return -ENOMEM; + + sst->desc->tx_submit(sst_drv_ctx->desc); + ret = sst_wait_timeout(sst, block); + if (ret) + dma_wait_for_async_tx(sst_drv_ctx->desc); + sst_free_block(sst, block); + return ret; +} + +static int sst_dma_firmware(struct sst_dma *dma, struct sst_sg_list *sg_list) +{ + int retval = 0; + enum dma_ctrl_flags flag = DMA_CTRL_ACK; + struct scatterlist *sg_src_list, *sg_dst_list; + int length; + pr_debug("%s: use_lli %d\n", __func__, sst_drv_ctx->use_lli); + + sg_src_list = sg_list->src; + sg_dst_list = sg_list->dst; + length = sg_list->list_len; + + /* BY default PIMR is unsmasked + * FW gets unmaksed dma intr too, so mask it for FW to execute on mrfld + */ + if (sst_drv_ctx->pci_id == SST_MRFLD_PCI_ID || + sst_drv_ctx->pci_id == SST_BYT_PCI_ID) + sst_shim_write(sst_drv_ctx->shim, SST_PIMR, 0xFFFF0034); + + if (sst_drv_ctx->use_lli) { + sst_drv_ctx->desc = dma->ch->device->device_prep_dma_sg(dma->ch, + sg_dst_list, length, + sg_src_list, length, flag); + if (!sst_drv_ctx->desc) + return -EFAULT; + retval = sst_dma_wait_for_completion(sst_drv_ctx); + if (retval) + pr_err("sst_dma_firmware..timeout!\n"); + } else { + struct scatterlist *sg; + dma_addr_t src_addr, dstn_addr; + int i = 0; + + /* dma single block mode */ + for_each_sg(sg_src_list, sg, length, i) { + pr_debug("dma desc %d, length %d\n", i, sg->length); + src_addr = sg_phys(sg); + dstn_addr = sg_phys(sg_dst_list); + if (sg_dst_list) + sg_dst_list = sg_next(sg_dst_list); + sst_drv_ctx->desc = dma->ch->device->device_prep_dma_memcpy( + dma->ch, dstn_addr, src_addr, sg->length, flag); + if (!sst_drv_ctx->desc) + return -EFAULT; + retval = sst_dma_wait_for_completion(sst_drv_ctx); + if (retval) + pr_err("sst_dma_firmware..timeout!\n"); + + } + } + + return retval; +} + +/* + * sst_fill_sglist - Fill the sg list + * + * @from: src address of the fw + * @to: virtual address of IRAM/DRAM + * @block_size: size of the block + * @sg_src: source scatterlist pointer + * @sg_dst: Destination scatterlist pointer + * @fw_sg_list: Pointer to the sg_list + * @dma_max_len: maximum len of the DMA block + * + * Parses modules that need to be placed in SST IRAM and DRAM + * and stores them in a sg list for transfer + * returns error or 0 if list creation fails or pass. + */ +static int sst_fill_sglist(unsigned long from, unsigned long to, + u32 block_size, struct scatterlist **sg_src, struct scatterlist **sg_dstn, + struct sst_sg_list *fw_sg_list, u32 dma_max_len) +{ + u32 offset = 0; + int len = 0; + unsigned long dstn, src; + + pr_debug("%s entry", __func__); + if (!sg_src || !sg_dstn) + return -EINVAL; + + do { + dstn = (unsigned long) (to + offset); + src = (unsigned long) (from + offset); + + /* split blocks to dma_max_len */ + + len = block_size - offset; + pr_debug("DMA blk src %lx,dstn %lx,len %d,offset %d, size %d\n", + src, dstn, len, offset, block_size); + if (len > dma_max_len) { + pr_debug("block size exceeds %d\n", dma_max_len); + len = dma_max_len; + offset += len; + } else { + pr_debug("Node length less that %d\n", dma_max_len); + offset = 0; + } + + if (!(*sg_src) || !(*sg_dstn)) + return -ENOMEM; + + sg_set_page(*sg_src, virt_to_page((void *) src), len, + offset_in_page((void *) src)); + sg_set_page(*sg_dstn, virt_to_page((void *) dstn), len, + offset_in_page((void *) dstn)); + + *sg_src = sg_next(*sg_src); + *sg_dstn = sg_next(*sg_dstn); + + /* TODO: is sg_idx required? */ + if (sst_drv_ctx->info.use_elf == true) + fw_sg_list->sg_idx++; + } while (offset > 0); + + return 0; +} + +static int sst_parse_elf_module_dma(struct intel_sst_drv *sst, const void *fw, + struct sst_info info, Elf32_Phdr *pr, + struct scatterlist **sg_src, struct scatterlist **sg_dstn, + struct sst_sg_list *fw_sg_list) +{ + unsigned long dstn, src; + unsigned int dstn_phys; + int ret_val = 0; + int mem_type; + + ret_val = sst_fill_dstn(sst, info, pr, (void *)&dstn, &dstn_phys, &mem_type); + if (ret_val) + return ret_val; + + dstn = (unsigned long) phys_to_virt(dstn_phys); + src = (unsigned long) (fw + pr->p_offset); + + ret_val = sst_fill_sglist(src, dstn, pr->p_filesz, + sg_src, sg_dstn, fw_sg_list, sst->info.dma_max_len); + + return ret_val; +} + +static int +sst_parse_elf_fw_dma(struct intel_sst_drv *sst, const void *fw_in_mem, + struct sst_sg_list *fw_sg_list) +{ + int i = 0, ret = 0; + Elf32_Ehdr *elf; + Elf32_Phdr *pr; + struct sst_info info; + struct scatterlist *sg_src = NULL, *sg_dst = NULL; + unsigned int sg_len; + + BUG_ON(!fw_in_mem); + + elf = (Elf32_Ehdr *)fw_in_mem; + pr = (Elf32_Phdr *) (fw_in_mem + elf->e_phoff); + pr_debug("%s entry\n", __func__); + + sst_fill_info(sst, &info); + + sg_len = sst_get_elf_sg_len(sst, elf, pr, info); + if (sg_len == 0) { + pr_err("we got NULL sz ELF, abort\n"); + return -EIO; + } + + if (sst_init_dma_sg_list(sst, sg_len, &sg_src, &sg_dst)) { + ret = -ENOMEM; + goto err1; + } + + fw_sg_list->src = sg_src; + fw_sg_list->dst = sg_dst; + fw_sg_list->list_len = sg_len; + fw_sg_list->sg_idx = 0; + + while (i < elf->e_phnum) { + if ((pr[i].p_type == PT_LOAD) && (pr[i].p_filesz)) { + ret = sst_parse_elf_module_dma(sst, fw_in_mem, info, + &pr[i], &sg_src, &sg_dst, fw_sg_list); + if (ret) + goto err; + } + i++; + } + return 0; +err: + kfree(fw_sg_list->src); + kfree(fw_sg_list->dst); +err1: + fw_sg_list->src = NULL; + fw_sg_list->dst = NULL; + fw_sg_list->list_len = 0; + fw_sg_list->sg_idx = 0; + + return ret; +} + +/** + * sst_parse_module_dma - Parse audio FW modules and populate the dma list + * + * @sst_ctx : sst driver context + * @module : FW module header + * @sg_list : Pointer to the sg_list to be populated + * Count the length for scattergather list + * and create the scattergather list of same length + * returns error or 0 if module sizes are proper + */ +static int sst_parse_module_dma(struct intel_sst_drv *sst_ctx, + struct fw_module_header *module, + struct sst_sg_list *sg_list) +{ + struct fw_block_info *block; + u32 count; + unsigned long ram, src; + int retval, sg_len = 0; + struct scatterlist *sg_src, *sg_dst; + + pr_debug("module sign %s size %x blocks %x type %x\n", + module->signature, module->mod_size, + module->blocks, module->type); + pr_debug("module entrypoint 0x%x\n", module->entry_point); + + block = (void *)module + sizeof(*module); + + for (count = 0; count < module->blocks; count++) { + sg_len += (block->size) / sst_drv_ctx->info.dma_max_len; + + if (block->size % sst_drv_ctx->info.dma_max_len) + sg_len = sg_len + 1; + block = (void *)block + sizeof(*block) + block->size; + } + + if (sst_init_dma_sg_list(sst_ctx, sg_len, &sg_src, &sg_dst)) { + retval = -ENOMEM; + goto err1; + } + + sg_list->src = sg_src; + sg_list->dst = sg_dst; + sg_list->list_len = sg_len; + + block = (void *)module + sizeof(*module); + + for (count = 0; count < module->blocks; count++) { + if (block->size <= 0) { + pr_err("block size invalid\n"); + retval = -EINVAL; + goto err; + } + switch (block->type) { + case SST_IRAM: + ram = sst_ctx->iram_base; + break; + case SST_DRAM: + ram = sst_ctx->dram_base; + break; + default: + pr_err("wrong ram type0x%x in block0x%x\n", + block->type, count); + retval = -EINVAL; + goto err; + } + + /*converting from physical to virtual because + scattergather list works on virtual pointers*/ + ram = (unsigned long) phys_to_virt(ram); + ram = (unsigned long)(ram + block->ram_offset); + src = (unsigned long) (void *)block + sizeof(*block); + + retval = sst_fill_sglist(src, ram, + block->size, &sg_src, &sg_dst, + sg_list, sst_ctx->info.dma_max_len); + if (retval) + goto err; + + block = (void *)block + sizeof(*block) + block->size; + } + return 0; +err: + kfree(sg_list->src); + kfree(sg_list->dst); +err1: + sg_list->src = NULL; + sg_list->dst = NULL; + sg_list->list_len = 0; + + return retval; +} + +/** + * sst_parse_fw_dma - parse the firmware image & populate the list for dma + * + * @sst_fw_in_mem : pointer to audio fw + * @size : size of the firmware + * @fw_list : pointer to sst_sg_list to be populated + * This function parses the FW image and saves the parsed image in the list + * for dma + */ +static int sst_parse_fw_dma(const void *sst_fw_in_mem, unsigned long size, + struct sst_sg_list *fw_list) +{ + struct fw_module_header *module; + u32 count, num_modules; + int ret_val; + + ret_val = sst_validate_fw_image(sst_fw_in_mem, size, + &module, &num_modules); + if (ret_val) + return ret_val; + + for (count = 0; count < num_modules; count++) { + /* module */ + ret_val = sst_parse_module_dma(sst_drv_ctx, module, fw_list); + if (ret_val) + return ret_val; + module = (void *)module + sizeof(*module) + module->mod_size ; + } + + return 0; +} + +static void sst_dma_free_resources(struct sst_dma *dma) +{ + pr_debug("entry:%s\n", __func__); + + dma_release_channel(dma->ch); +} + +void sst_fill_config(struct intel_sst_drv *sst_ctx, unsigned int offset) +{ + struct sst_fill_config sst_config; + + if (!(sst_ctx->pdata->bdata && sst_ctx->pdata->pdata)) + return; + + sst_config.sign = SST_CONFIG_SSP_SIGN; + memcpy(&sst_config.sst_bdata, sst_ctx->pdata->bdata, sizeof(struct sst_board_config_data)); + memcpy(&sst_config.sst_pdata, sst_ctx->pdata->pdata, sizeof(struct sst_platform_config_data)); + sst_config.shim_phy_add = sst_ctx->shim_phy_add; + sst_config.mailbox_add = sst_ctx->mailbox_add; + MEMCPY_TOIO(sst_ctx->dram + offset, &sst_config, sizeof(sst_config)); + +} + +/** + * sst_do_dma - function allocs and initiates the DMA + * + * @sg_list: Pointer to dma list on which the dma needs to be initiated + * + * Triggers the DMA + */ +static int sst_do_dma(struct sst_sg_list *sg_list) +{ + int ret_val; + + /* get a dmac channel */ + ret_val = sst_alloc_dma_chan(&sst_drv_ctx->dma); + if (ret_val) + return ret_val; + + /* allocate desc for transfer and submit */ + ret_val = sst_dma_firmware(&sst_drv_ctx->dma, sg_list); + + sst_dma_free_resources(&sst_drv_ctx->dma); + + return ret_val; +} + +/* + * sst_fill_memcpy_list - Fill the memcpy list + * + * @memcpy_list: List to be filled + * @destn: Destination addr to be filled in the list + * @src: Source addr to be filled in the list + * @size: Size to be filled in the list + * + * Adds the node to the list after required fields + * are populated in the node + */ + +static int sst_fill_memcpy_list(struct list_head *memcpy_list, + void *destn, const void *src, u32 size, bool is_io) +{ + struct sst_memcpy_list *listnode; + + listnode = kzalloc(sizeof(*listnode), GFP_KERNEL); + if (listnode == NULL) + return -ENOMEM; + listnode->dstn = destn; + listnode->src = src; + listnode->size = size; + listnode->is_io = is_io; + list_add_tail(&listnode->memcpylist, memcpy_list); + + return 0; +} + +static int sst_parse_elf_module_memcpy(struct intel_sst_drv *sst, + const void *fw, struct sst_info info, Elf32_Phdr *pr, + struct list_head *memcpy_list) +{ + void *dstn; + unsigned int dstn_phys; + int ret_val = 0; + int mem_type; + + ret_val = sst_fill_dstn(sst, info, pr, &dstn, &dstn_phys, &mem_type); + if (ret_val) + return ret_val; + + ret_val = sst_fill_memcpy_list(memcpy_list, dstn, + (void *)fw + pr->p_offset, pr->p_filesz, mem_type); + if (ret_val) + return ret_val; + + return 0; +} + +static int +sst_parse_elf_fw_memcpy(struct intel_sst_drv *sst, const void *fw_in_mem, + struct list_head *memcpy_list) +{ + int i = 0; + + Elf32_Ehdr *elf; + Elf32_Phdr *pr; + struct sst_info info; + + BUG_ON(!fw_in_mem); + + elf = (Elf32_Ehdr *)fw_in_mem; + pr = (Elf32_Phdr *) (fw_in_mem + elf->e_phoff); + pr_debug("%s entry\n", __func__); + + sst_fill_info(sst, &info); + + while (i < elf->e_phnum) { + if (pr[i].p_type == PT_LOAD) + sst_parse_elf_module_memcpy(sst, fw_in_mem, info, + &pr[i], memcpy_list); + i++; + } + return 0; +} + +/** + * sst_parse_module_memcpy - Parse audio FW modules and populate the memcpy list + * + * @module : FW module header + * @memcpy_list : Pointer to the list to be populated + * Create the memcpy list as the number of block to be copied + * returns error or 0 if module sizes are proper + */ +static int sst_parse_module_memcpy(struct fw_module_header *module, + struct list_head *memcpy_list) +{ + struct fw_block_info *block; + u32 count; + int ret_val = 0; + void __iomem *ram_iomem; + + pr_debug("module sign %s size %x blocks %x type %x\n", + module->signature, module->mod_size, + module->blocks, module->type); + pr_debug("module entrypoint 0x%x\n", module->entry_point); + + block = (void *)module + sizeof(*module); + + for (count = 0; count < module->blocks; count++) { + if (block->size <= 0) { + pr_err("block size invalid\n"); + return -EINVAL; + } + switch (block->type) { + case SST_IRAM: + ram_iomem = sst_drv_ctx->iram; + break; + case SST_DRAM: + ram_iomem = sst_drv_ctx->dram; + break; + default: + pr_err("wrong ram type0x%x in block0x%x\n", + block->type, count); + return -EINVAL; + } + + ret_val = sst_fill_memcpy_list(memcpy_list, + ram_iomem + block->ram_offset, + (void *)block + sizeof(*block), block->size, 1); + if (ret_val) + return ret_val; + + block = (void *)block + sizeof(*block) + block->size; + } + return 0; +} + +/** + * sst_parse_fw_memcpy - parse the firmware image & populate the list for memcpy + * + * @sst_fw_in_mem : pointer to audio fw + * @size : size of the firmware + * @fw_list : pointer to list_head to be populated + * This function parses the FW image and saves the parsed image in the list + * for memcpy + */ +static int sst_parse_fw_memcpy(const void *sst_fw_in_mem, unsigned long size, + struct list_head *fw_list) +{ + struct fw_module_header *module; + u32 count, num_modules; + int ret_val; + + ret_val = sst_validate_fw_image(sst_fw_in_mem, size, + &module, &num_modules); + if (ret_val) + return ret_val; + + for (count = 0; count < num_modules; count++) { + /* module */ + ret_val = sst_parse_module_memcpy(module, fw_list); + if (ret_val) + return ret_val; + module = (void *)module + sizeof(*module) + module->mod_size ; + } + + return 0; +} + +/** + * sst_do_memcpy - function initiates the memcpy + * + * @memcpy_list: Pter to memcpy list on which the memcpy needs to be initiated + * + * Triggers the memcpy + */ +static void sst_do_memcpy(struct list_head *memcpy_list) +{ + struct sst_memcpy_list *listnode; + + list_for_each_entry(listnode, memcpy_list, memcpylist) { + if (listnode->is_io == true) + MEMCPY_TOIO((void __iomem *)listnode->dstn, listnode->src, + listnode->size); + else + memcpy(listnode->dstn, listnode->src, listnode->size); + } +} + +static void sst_memcpy_free_lib_resources(void) +{ + struct sst_memcpy_list *listnode, *tmplistnode; + + pr_debug("entry:%s\n", __func__); + + /*Free the list*/ + if (!list_empty(&sst_drv_ctx->libmemcpy_list)) { + list_for_each_entry_safe(listnode, tmplistnode, + &sst_drv_ctx->libmemcpy_list, memcpylist) { + list_del(&listnode->memcpylist); + kfree(listnode); + } + } +} + +void sst_memcpy_free_resources(void) +{ + struct sst_memcpy_list *listnode, *tmplistnode; + + pr_debug("entry:%s\n", __func__); + + /*Free the list*/ + if (!list_empty(&sst_drv_ctx->memcpy_list)) { + list_for_each_entry_safe(listnode, tmplistnode, + &sst_drv_ctx->memcpy_list, memcpylist) { + list_del(&listnode->memcpylist); + kfree(listnode); + } + } + sst_memcpy_free_lib_resources(); +} + +void sst_firmware_load_cb(const struct firmware *fw, void *context) +{ + struct intel_sst_drv *ctx = context; + int ret = 0; + + pr_debug("In %s\n", __func__); + + if (fw == NULL) { + pr_err("request fw failed\n"); + goto out; + } + + if (sst_drv_ctx->sst_state != SST_UN_INIT || + ctx->fw_in_mem != NULL) + goto exit; + + pr_debug("Request Fw completed\n"); + trace_sst_fw_download("End of FW request", ctx->sst_state); + + if (ctx->info.use_elf == true) + ret = sst_validate_elf(fw, false); + + if (ret != 0) { + pr_err("FW image invalid...\n"); + goto out; + } + + ctx->fw_in_mem = kzalloc(fw->size, GFP_KERNEL); + if (!ctx->fw_in_mem) { + pr_err("%s unable to allocate memory\n", __func__); + goto out; + } + + pr_debug("copied fw to %p", ctx->fw_in_mem); + pr_debug("phys: %lx", (unsigned long)virt_to_phys(ctx->fw_in_mem)); + memcpy(ctx->fw_in_mem, fw->data, fw->size); + + trace_sst_fw_download("Start FW parsing", ctx->sst_state); + if (ctx->use_dma) { + if (ctx->info.use_elf == true) + ret = sst_parse_elf_fw_dma(ctx, ctx->fw_in_mem, + &ctx->fw_sg_list); + else + ret = sst_parse_fw_dma(ctx->fw_in_mem, fw->size, + &ctx->fw_sg_list); + } else { + if (ctx->info.use_elf == true) + ret = sst_parse_elf_fw_memcpy(ctx, ctx->fw_in_mem, + &ctx->memcpy_list); + else + ret = sst_parse_fw_memcpy(ctx->fw_in_mem, fw->size, + &ctx->memcpy_list); + } + trace_sst_fw_download("End FW parsing", ctx->sst_state); + if (ret) { + kfree(ctx->fw_in_mem); + ctx->fw_in_mem = NULL; + goto out; + } + + /* If static module download(download at boot time) is supported, + * set the flag to indicate lib download is to be done + */ + if (ctx->pdata->lib_info) + if (ctx->pdata->lib_info->mod_ddr_dnld) + ctx->lib_dwnld_reqd = true; + + sst_set_fw_state_locked(sst_drv_ctx, SST_FW_LIB_LOAD); + goto exit; +out: + sst_set_fw_state_locked(sst_drv_ctx, SST_UN_INIT); +exit: + if (fw != NULL) + release_firmware(fw); +} + +/* + * sst_request_fw - requests audio fw from kernel and saves a copy + * + * This function requests the SST FW from the kernel, parses it and + * saves a copy in the driver context + */ +static int sst_request_fw(struct intel_sst_drv *sst) +{ + int retval = 0; + char name[20]; + const struct firmware *fw; + + snprintf(name, sizeof(name), "%s%04x%s", "fw_sst_", + sst->pci_id, ".bin"); + pr_debug("Requesting FW %s now...\n", name); + + retval = request_firmware(&fw, name, sst->dev); + if (fw == NULL) { + pr_err("fw is returning as null\n"); + return -EINVAL; + } + if (retval) { + pr_err("request fw failed %d\n", retval); + return retval; + } + trace_sst_fw_download("End of FW request", sst->sst_state); + if (sst->info.use_elf == true) + retval = sst_validate_elf(fw, false); + if (retval != 0) { + pr_err("FW image invalid...\n"); + goto end_release; + } + sst->fw_in_mem = kzalloc(fw->size, GFP_KERNEL); + if (!sst->fw_in_mem) { + pr_err("%s unable to allocate memory\n", __func__); + retval = -ENOMEM; + goto end_release; + } + pr_debug("copied fw to %p", sst->fw_in_mem); + pr_debug("phys: %lx", (unsigned long)virt_to_phys(sst->fw_in_mem)); + memcpy(sst->fw_in_mem, fw->data, fw->size); + trace_sst_fw_download("Start FW parsing", sst->sst_state); + if (sst->use_dma) { + if (sst->info.use_elf == true) + retval = sst_parse_elf_fw_dma(sst, sst->fw_in_mem, + &sst->fw_sg_list); + else + retval = sst_parse_fw_dma(sst->fw_in_mem, fw->size, + &sst->fw_sg_list); + } else { + if (sst->info.use_elf == true) + retval = sst_parse_elf_fw_memcpy(sst, sst->fw_in_mem, + &sst->memcpy_list); + else + retval = sst_parse_fw_memcpy(sst->fw_in_mem, fw->size, + &sst->memcpy_list); + } + trace_sst_fw_download("End FW parsing", sst->sst_state); + if (retval) { + kfree(sst->fw_in_mem); + sst->fw_in_mem = NULL; + } + + /* If static module download(download at boot time) is supported, + * set the flag to indicate lib download is to be done + */ + if (sst->pdata->lib_info) + if (sst->pdata->lib_info->mod_ddr_dnld) + sst->lib_dwnld_reqd = true; +end_release: + release_firmware(fw); + return retval; +} + +static inline void print_lib_info(struct snd_sst_lib_download_info *resp) +{ + pr_debug("codec Type %d Ver %d Built %s: %s\n", + resp->dload_lib.lib_info.lib_type, + resp->dload_lib.lib_info.lib_version, + resp->dload_lib.lib_info.b_date, + resp->dload_lib.lib_info.b_time); +} + +/* sst_download_library - This function is called when any + codec/post processing library needs to be downloaded */ +static int sst_download_library(const struct firmware *fw_lib, + struct snd_sst_lib_download_info *lib) +{ + int ret_val = 0; + + /* send IPC message and wait */ + u8 pvt_id; + struct ipc_post *msg = NULL; + union config_status_reg csr; + struct snd_sst_str_type str_type = {0}; + int retval = 0; + void *codec_fw; + struct sst_block *block; + + pvt_id = sst_assign_pvt_id(sst_drv_ctx); + ret_val = sst_create_block_and_ipc_msg(&msg, true, sst_drv_ctx, &block, + IPC_IA_PREP_LIB_DNLD, pvt_id); + if (ret_val) { + pr_err("library download failed\n"); + return ret_val; + } + + sst_fill_header(&msg->header, IPC_IA_PREP_LIB_DNLD, 1, pvt_id); + msg->header.part.data = sizeof(u32) + sizeof(str_type); + str_type.codec_type = lib->dload_lib.lib_info.lib_type; + /*str_type.pvt_id = pvt_id;*/ + memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); + memcpy(msg->mailbox_data + sizeof(u32), &str_type, sizeof(str_type)); + sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); + retval = sst_wait_timeout(sst_drv_ctx, block); + if (block->data) { + struct snd_sst_str_type *str_type = + (struct snd_sst_str_type *)block->data; + if (str_type->result) { + /* error */ + pr_err("Prep codec downloaded failed %d\n", + str_type->result); + retval = -EIO; + goto free_block; + } + kfree(block->data); + } else if (retval != 0) { + retval = -EIO; + goto free_block; + } + pr_debug("FW responded, ready for download now...\n"); + codec_fw = kzalloc(fw_lib->size, GFP_KERNEL); + if (!codec_fw) { + memset(lib, 0, sizeof(*lib)); + retval = -ENOMEM; + goto send_ipc; + } + memcpy(codec_fw, fw_lib->data, fw_lib->size); + + if (sst_drv_ctx->use_dma) + retval = sst_parse_fw_dma(codec_fw, fw_lib->size, + &sst_drv_ctx->library_list); + else + retval = sst_parse_fw_memcpy(codec_fw, fw_lib->size, + &sst_drv_ctx->libmemcpy_list); + + if (retval) { + memset(lib, 0, sizeof(*lib)); + goto send_ipc; + } + + /* downloading on success */ + mutex_lock(&sst_drv_ctx->sst_lock); + sst_drv_ctx->sst_state = SST_FW_LOADED; + mutex_lock(&sst_drv_ctx->csr_lock); + csr.full = readl(sst_drv_ctx->shim + SST_CSR); + csr.part.run_stall = 1; + sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); + + csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); + csr.part.bypass = 0x7; + sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); + mutex_unlock(&sst_drv_ctx->csr_lock); + + if (sst_drv_ctx->use_dma) { + ret_val = sst_do_dma(&sst_drv_ctx->library_list); + if (ret_val) { + pr_err("sst_do_dma failed, abort\n"); + memset(lib, 0, sizeof(*lib)); + } + } else + sst_do_memcpy(&sst_drv_ctx->libmemcpy_list); + /* set the FW to running again */ + mutex_lock(&sst_drv_ctx->csr_lock); + csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); + csr.part.bypass = 0x0; + sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); + + csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); + csr.part.run_stall = 0; + sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); + mutex_unlock(&sst_drv_ctx->csr_lock); +send_ipc: + /* send download complete and wait */ + if (sst_create_ipc_msg(&msg, true)) { + retval = -ENOMEM; + goto free_resources; + } + + block->condition = false; + block->msg_id = IPC_IA_LIB_DNLD_CMPLT; + sst_fill_header(&msg->header, IPC_IA_LIB_DNLD_CMPLT, 1, pvt_id); + msg->header.part.data = sizeof(u32) + sizeof(*lib); + lib->pvt_id = pvt_id; + memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); + memcpy(msg->mailbox_data + sizeof(u32), lib, sizeof(*lib)); + sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); + pr_debug("Waiting for FW response Download complete\n"); + retval = sst_wait_timeout(sst_drv_ctx, block); + sst_drv_ctx->sst_state = SST_FW_RUNNING; + if (block->data) { + struct snd_sst_lib_download_info *resp = block->data; + retval = resp->result; + if (retval) { + pr_err("err in lib dload %x\n", resp->result); + goto free_resources; + } else { + pr_debug("Codec download complete...\n"); + print_lib_info(resp); + } + } else if (retval) { + /* error */ + retval = -EIO; + goto free_resources; + } + + pr_debug("FW success on Download complete\n"); + +free_resources: + if (sst_drv_ctx->use_dma) { + kfree(sst_drv_ctx->library_list.src); + kfree(sst_drv_ctx->library_list.dst); + sst_drv_ctx->library_list.list_len = 0; + } + + kfree(codec_fw); + mutex_unlock(&sst_drv_ctx->sst_lock); +free_block: + sst_free_block(sst_drv_ctx, block); + return retval; +} + +/* + * Writing the DDR physical base to DCCM offset + * so that FW can use it to setup TLB + */ +static void sst_dccm_config_write(void __iomem *dram_base, unsigned int ddr_base) +{ + void __iomem *addr; + u32 bss_reset = 0; + + addr = (void __iomem *)(dram_base + MRFLD_FW_DDR_BASE_OFFSET); + MEMCPY_TOIO(addr, (void *)&ddr_base, sizeof(u32)); + bss_reset |= (1 << MRFLD_FW_BSS_RESET_BIT); + addr = (void __iomem *)(dram_base + MRFLD_FW_FEATURE_BASE_OFFSET); + MEMCPY_TOIO(addr, &bss_reset, sizeof(u32)); + pr_debug("%s: config written to DCCM\n", __func__); +} + +void sst_post_download_mrfld(struct intel_sst_drv *ctx) +{ + sst_dccm_config_write(ctx->dram, ctx->ddr_base); + /* For mrfld, download all libraries the first time fw is + * downloaded */ + pr_debug("%s: lib_dwnld = %u\n", __func__, ctx->lib_dwnld_reqd); + if (ctx->lib_dwnld_reqd) { + sst_load_all_modules_elf(ctx, sst_modules_mrfld, ARRAY_SIZE(sst_modules_mrfld)); + ctx->lib_dwnld_reqd = false; + } +} + +void sst_post_download_ctp(struct intel_sst_drv *ctx) +{ + sst_fill_config(ctx, 0); +} + +void sst_post_download_byt(struct intel_sst_drv *ctx) +{ + sst_dccm_config_write(ctx->dram, ctx->ddr_base); + sst_fill_config(ctx, 2 * sizeof(u32)); + + pr_debug("%s: lib_dwnld = %u\n", __func__, ctx->lib_dwnld_reqd); + if (ctx->lib_dwnld_reqd) { + sst_load_all_modules_elf(ctx, sst_modules_byt, + ARRAY_SIZE(sst_modules_byt)); + ctx->lib_dwnld_reqd = false; + } +} + +static void sst_init_lib_mem_mgr(struct intel_sst_drv *ctx) +{ + struct sst_mem_mgr *mgr = &ctx->lib_mem_mgr; + const struct sst_lib_dnld_info *lib_info = ctx->pdata->lib_info; + + memset(mgr, 0, sizeof(*mgr)); + mgr->current_base = lib_info->mod_base + lib_info->mod_table_offset + + lib_info->mod_table_size; + mgr->avail = lib_info->mod_end - mgr->current_base + 1; + + pr_debug("current base = 0x%lx , avail = 0x%x\n", + (unsigned long)mgr->current_base, mgr->avail); +} + +/** + * sst_load_fw - function to load FW into DSP + * + * + * Transfers the FW to DSP using dma/memcpy + */ +int sst_load_fw(void) +{ + int ret_val = 0; + struct sst_block *block; + + pr_debug("sst_load_fw\n"); + + if ((sst_drv_ctx->sst_state != SST_START_INIT && + sst_drv_ctx->sst_state != SST_FW_LIB_LOAD) || + sst_drv_ctx->sst_state == SST_SHUTDOWN) + return -EAGAIN; + + if (!sst_drv_ctx->fw_in_mem) { + if (sst_drv_ctx->sst_state != SST_START_INIT) { + /* even wake*/ + pr_err("sst : wait for FW to be downloaded\n"); + return -EBUSY; + } else { + trace_sst_fw_download("Req FW sent in check device", + sst_drv_ctx->sst_state); + pr_debug("sst: FW not in memory retry to download\n"); + ret_val = sst_request_fw(sst_drv_ctx); + if (ret_val) + return ret_val; + } + } + + BUG_ON(!sst_drv_ctx->fw_in_mem); + block = sst_create_block(sst_drv_ctx, 0, FW_DWNL_ID); + if (block == NULL) + return -ENOMEM; + + /* Prevent C-states beyond C6 */ + pm_qos_update_request(sst_drv_ctx->qos, CSTATE_EXIT_LATENCY_S0i1 - 1); + + ret_val = sst_drv_ctx->ops->reset(); + if (ret_val) + goto restore; + + trace_sst_fw_download("Start FW copy", sst_drv_ctx->sst_state); + if (sst_drv_ctx->use_dma) { + ret_val = sst_do_dma(&sst_drv_ctx->fw_sg_list); + if (ret_val) { + pr_err("sst_do_dma failed, abort\n"); + goto restore; + } + } else { + sst_do_memcpy(&sst_drv_ctx->memcpy_list); + } + + trace_sst_fw_download("Post download for Lib start", + sst_drv_ctx->sst_state); + /* Write the DRAM/DCCM config before enabling FW */ + if (sst_drv_ctx->ops->post_download) + sst_drv_ctx->ops->post_download(sst_drv_ctx); + trace_sst_fw_download("Post download for Lib end", + sst_drv_ctx->sst_state); + sst_drv_ctx->sst_state = SST_FW_LOADED; + + /* bring sst out of reset */ + ret_val = sst_drv_ctx->ops->start(); + if (ret_val) + goto restore; + trace_sst_fw_download("DSP reset done", + sst_drv_ctx->sst_state); + + ret_val = sst_wait_timeout(sst_drv_ctx, block); + if (ret_val) { + pr_err("fw download failed %d\n" , ret_val); + /* assume FW d/l failed due to timeout*/ + ret_val = -EBUSY; + + } + +restore: + /* Re-enable Deeper C-states beyond C6 */ + pm_qos_update_request(sst_drv_ctx->qos, PM_QOS_DEFAULT_VALUE); + sst_free_block(sst_drv_ctx, block); + + return ret_val; +} + +/** + * sst_load_library - function to load FW into DSP + * + * @lib: Pointer to the lib download structure + * @ops: Contains the stream ops + * This function is called when FW requests for a particular library download + * This function prepares & downloads the library + */ +int sst_load_library(struct snd_sst_lib_download *lib, u8 ops) +{ + char buf[20]; + const char *type, *dir; + int len = 0, error = 0; + u32 entry_point; + const struct firmware *fw_lib; + struct snd_sst_lib_download_info dload_info = {{{0},},}; + + memset(buf, 0, sizeof(buf)); + + pr_debug("Lib Type 0x%x, Slot 0x%x, ops 0x%x\n", + lib->lib_info.lib_type, lib->slot_info.slot_num, ops); + pr_debug("Version 0x%x, name %s, caps 0x%x media type 0x%x\n", + lib->lib_info.lib_version, lib->lib_info.lib_name, + lib->lib_info.lib_caps, lib->lib_info.media_type); + + pr_debug("IRAM Size 0x%x, offset 0x%x\n", + lib->slot_info.iram_size, lib->slot_info.iram_offset); + pr_debug("DRAM Size 0x%x, offset 0x%x\n", + lib->slot_info.dram_size, lib->slot_info.dram_offset); + + switch (lib->lib_info.lib_type) { + case SST_CODEC_TYPE_MP3: + type = "mp3_"; + break; + case SST_CODEC_TYPE_AAC: + type = "aac_"; + break; + case SST_CODEC_TYPE_AACP: + type = "aac_v1_"; + break; + case SST_CODEC_TYPE_eAACP: + type = "aac_v2_"; + break; + case SST_CODEC_TYPE_WMA9: + type = "wma9_"; + break; + default: + pr_err("Invalid codec type\n"); + error = -EINVAL; + goto wake; + } + + if (ops == STREAM_OPS_CAPTURE) + dir = "enc_"; + else + dir = "dec_"; + len = strlen(type) + strlen(dir); + strncpy(buf, type, sizeof(buf)-1); + strncpy(buf + strlen(type), dir, sizeof(buf)-strlen(type)-1); + len += snprintf(buf + len, sizeof(buf) - len, "%d", + lib->slot_info.slot_num); + len += snprintf(buf + len, sizeof(buf) - len, ".bin"); + + pr_debug("Requesting %s\n", buf); + + error = request_firmware(&fw_lib, buf, sst_drv_ctx->dev); + if (fw_lib == NULL) { + pr_err("fw_lib pointer is returning null\n"); + return -EINVAL; + } + if (error) { + pr_err("library load failed %d\n", error); + goto wake; + } + error = sst_validate_library(fw_lib, &lib->slot_info, &entry_point); + if (error) + goto wake_free; + + lib->mod_entry_pt = entry_point; + memcpy(&dload_info.dload_lib, lib, sizeof(*lib)); + /* Prevent C-states beyond C6 */ + pm_qos_update_request(sst_drv_ctx->qos, CSTATE_EXIT_LATENCY_S0i1 - 1); + error = sst_download_library(fw_lib, &dload_info); + /* Re-enable Deeper C-states beyond C6 */ + pm_qos_update_request(sst_drv_ctx->qos, PM_QOS_DEFAULT_VALUE); + if (error) + goto wake_free; + + /* lib is downloaded and init send alloc again */ + pr_debug("Library is downloaded now...\n"); +wake_free: + /* sst_wake_up_alloc_block(sst_drv_ctx, pvt_id, error, NULL); */ + release_firmware(fw_lib); +wake: + return error; +} + +/* In relocatable elf file, there can be relocatable variables and functions. + * Variables are kept in Global Address Offset Table (GOT) and functions in + * Procedural Linkage Table (PLT). In current codec binaries only relocatable + * variables are seen. So we use the GOT table. + */ +static int sst_find_got_table(Elf32_Shdr *shdr, int nsec, char *in_elf, + Elf32_Rela **got, unsigned int *cnt) +{ + int i = 0; + while (i < nsec) { + if (shdr[i].sh_type == SHT_RELA) { + *got = (Elf32_Rela *)(in_elf + shdr[i].sh_offset); + *cnt = shdr[i].sh_size / sizeof(Elf32_Rela); + break; + } + i++; + } + if (i == nsec) + return -EINVAL; + + return 0; +} + +/* For each entry in the GOT table, find the unrelocated offset. Then + * add the relocation base to the offset and write back the new address to the + * original variable location. + */ +static int sst_relocate_got_entries(Elf32_Rela *table, unsigned int size, + char *in_elf, int elf_size, u32 rel_base) +{ + int i; + Elf32_Rela *entry; + Elf32_Addr *target_addr, unreloc_addr; + + for (i = 0; i < size; i++) { + entry = &table[i]; + if (ELF32_R_SYM(entry->r_info) != 0) { + return -EINVAL; + } else { + if (entry->r_offset > elf_size) { + pr_err("GOT table target addr out of range\n"); + return -EINVAL; + } + target_addr = (Elf32_Addr *)(in_elf + entry->r_offset); + unreloc_addr = *target_addr + entry->r_addend; + if (unreloc_addr > elf_size) { + pr_err("GOT table entry invalid\n"); + continue; + } + *target_addr = unreloc_addr + rel_base; + } + } + return 0; +} + +static int sst_relocate_elf(char *in_elf, int elf_size, phys_addr_t rel_base, + Elf32_Addr *entry_pt) +{ + int retval = 0; + Elf32_Ehdr *ehdr = (Elf32_Ehdr *)in_elf; + Elf32_Shdr *shdr = (Elf32_Shdr *) (in_elf + ehdr->e_shoff); + Elf32_Phdr *phdr = (Elf32_Phdr *) (in_elf + ehdr->e_phoff); + int i, num_sec; + Elf32_Rela *rel_table = NULL; + unsigned int rela_cnt = 0; + u32 rbase; + + BUG_ON(rel_base > (u32)(-1)); + rbase = (u32) (rel_base & (u32)(~0)); + + /* relocate the entry_pt */ + *entry_pt = (Elf32_Addr)(ehdr->e_entry + rbase); + num_sec = ehdr->e_shnum; + + /* Find the relocation(GOT) table through the section header */ + retval = sst_find_got_table(shdr, num_sec, in_elf, + &rel_table, &rela_cnt); + if (retval < 0) + return retval; + + /* Relocate all the entries in the GOT */ + retval = sst_relocate_got_entries(rel_table, rela_cnt, in_elf, + elf_size, rbase); + if (retval < 0) + return retval; + + pr_debug("GOT entries relocated\n"); + + /* Update the program headers in the ELF */ + for (i = 0; i < ehdr->e_phnum; i++) { + if (phdr[i].p_type == PT_LOAD) { + phdr[i].p_vaddr += rbase; + phdr[i].p_paddr += rbase; + } + } + pr_debug("program header entries updated\n"); + + return retval; +} + +#define ALIGN_256 0x100 + +int sst_get_next_lib_mem(struct sst_mem_mgr *mgr, int size, + unsigned long *lib_base) +{ + int retval = 0; + + pr_debug("library orig size = 0x%x", size); + if (size % ALIGN_256) + size += (ALIGN_256 - (size % ALIGN_256)); + if (size > mgr->avail) + return -ENOMEM; + + *lib_base = mgr->current_base; + mgr->current_base += size; + mgr->avail -= size; + mgr->count++; + pr_debug("library base = 0x%lx", *lib_base); + pr_debug("library aligned size = 0x%x", size); + pr_debug("lib count = %d\n", mgr->count); + return retval; + +} + +static int sst_download_lib_elf(struct intel_sst_drv *sst, const void *lib, + int size) +{ + int retval = 0; + + pr_debug("In %s\n", __func__); + + if (sst->use_dma) { + retval = sst_parse_elf_fw_dma(sst, lib, + &sst->library_list); + if (retval) + goto free_dma_res; + retval = sst_do_dma(&sst->library_list); + if (retval) + pr_err("sst_do_dma failed, abort\n"); +free_dma_res: + kfree(sst->library_list.src); + kfree(sst->library_list.dst); + sst->library_list.list_len = 0; + } else { + retval = sst_parse_elf_fw_memcpy(sst, lib, + &sst->libmemcpy_list); + if (retval) + return retval; + sst_do_memcpy(&sst->libmemcpy_list); + sst_memcpy_free_lib_resources(); + } + pr_debug("download lib complete"); + return retval; +} + +static void sst_fill_fw_module_table(struct sst_module_info *mod_list, + int list_size, unsigned long ddr_base) +{ + int i; + u32 *write_ptr = (u32 *)ddr_base; + + pr_debug("In %s\n", __func__); + + for (i = 0; i < list_size; i++) { + if (mod_list[i].status == SST_LIB_DOWNLOADED) { + pr_debug("status dnwld for %d\n", i); + pr_debug("module id %d\n", mod_list[i].id); + pr_debug("entry pt 0x%x\n", mod_list[i].entry_pt); + + *write_ptr++ = mod_list[i].id; + *write_ptr++ = mod_list[i].entry_pt; + } + } +} + +static int sst_request_lib_elf(struct sst_module_info *mod_entry, + const struct firmware **fw_lib, int pci_id, struct device *dev) +{ + char name[25]; + int retval = 0; + + snprintf(name, sizeof(name), "%s%s%04x%s", mod_entry->name, + "_", pci_id, ".bin"); + pr_debug("Requesting %s\n", name); + + retval = request_firmware(fw_lib, name, dev); + if (retval) { + pr_err("%s library load failed %d\n", name, retval); + return retval; + } + pr_debug("got lib\n"); + mod_entry->status = SST_LIB_FOUND; + return 0; +} + +static int sst_allocate_lib_mem(const struct firmware *lib, int size, + struct sst_mem_mgr *mem_mgr, char **out_elf, unsigned long *lib_start) +{ + int retval = 0; + + *out_elf = kzalloc(size, GFP_KERNEL); + if (!*out_elf) { + pr_err("cannot alloc mem for elf copy %d\n", retval); + goto mem_error; + } + + memcpy(*out_elf, lib->data, size); + retval = sst_get_next_lib_mem(mem_mgr, size, lib_start); + if (retval < 0) { + pr_err("cannot alloc ddr mem for lib: %d\n", retval); + kfree(*out_elf); + goto mem_error; + } + return 0; + +mem_error: + release_firmware(lib); + return -ENOMEM; +} + +int sst_load_all_modules_elf(struct intel_sst_drv *ctx, struct sst_module_info *mod_table, + int num_modules) +{ + int retval = 0; + int i; + const struct firmware *fw_lib; + struct sst_module_info *mod = NULL; + char *out_elf; + unsigned int lib_size = 0; + unsigned int mod_table_offset = ctx->pdata->lib_info->mod_table_offset; + unsigned long lib_base; + + pr_debug("In %s", __func__); + + sst_init_lib_mem_mgr(ctx); + + for (i = 0; i < num_modules; i++) { + mod = &mod_table[i]; + trace_sst_lib_download("Start of Request Lib", mod->name); + retval = sst_request_lib_elf(mod, &fw_lib, + ctx->pci_id, ctx->dev); + if (retval < 0) + continue; + lib_size = fw_lib->size; + + trace_sst_lib_download("End of Request Lib", mod->name); + retval = sst_validate_elf(fw_lib, true); + if (retval < 0) { + pr_err("library is not valid elf %d\n", retval); + release_firmware(fw_lib); + continue; + } + pr_debug("elf validated\n"); + retval = sst_allocate_lib_mem(fw_lib, lib_size, + &ctx->lib_mem_mgr, &out_elf, &lib_base); + if (retval < 0) { + pr_err("lib mem allocation failed: %d\n", retval); + continue; + } + pr_debug("lib space allocated\n"); + + /* relocate in place */ + retval = sst_relocate_elf(out_elf, lib_size, + lib_base, &mod->entry_pt); + if (retval < 0) { + pr_err("lib elf relocation failed: %d\n", retval); + release_firmware(fw_lib); + kfree(out_elf); + continue; + } + pr_debug("relocation done\n"); + release_firmware(fw_lib); + trace_sst_lib_download("Start of download Lib", mod->name); + /* write to ddr imr region,use memcpy method */ + retval = sst_download_lib_elf(ctx, out_elf, lib_size); + trace_sst_lib_download("End of download Lib", mod->name); + mod->status = SST_LIB_DOWNLOADED; + kfree(out_elf); + } + + /* write module table to DDR */ + sst_fill_fw_module_table(mod_table, num_modules, + (unsigned long)(ctx->ddr + mod_table_offset)); + return retval; +} diff --git a/sound/soc/intel/sst/sst_ipc.c b/sound/soc/intel/sst/sst_ipc.c new file mode 100644 index 000000000000..097c7155ad55 --- /dev/null +++ b/sound/soc/intel/sst/sst_ipc.c @@ -0,0 +1,768 @@ +/* + * sst_ipc.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-10 Intel Corporation + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R + * KP Jeeja + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This file defines all ipc functions + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include "../sst_platform.h" +#include "../platform_ipc_v2.h" +#include "sst.h" +#include "sst_trace.h" + +void sst_dump_to_buffer(const void *from, size_t len, char *buf) +{ + int i, end; + const unsigned char *cmd = from; + + if (len == 0) { + buf[0] = '\0'; + return; + } + + for (end = len - 1; end >= 0; end--) + if (cmd[end]) + break; + end++; + + buf += snprintf(buf, 3, "%02x", cmd[0]); + for (i = 1; i < len; i++) { + buf += snprintf(buf, 4, " %02x", cmd[i]); + if (i == end && end != len - 1) { + sprintf(buf, "..."); + break; + } + } +} + +struct sst_block *sst_create_block(struct intel_sst_drv *ctx, + u32 msg_id, u32 drv_id) +{ + struct sst_block *msg = NULL; + + pr_debug("in %s\n", __func__); + msg = kzalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) { + pr_err("kzalloc block failed\n"); + return NULL; + } + msg->condition = false; + msg->on = true; + msg->msg_id = msg_id; + msg->drv_id = drv_id; + spin_lock_bh(&ctx->block_lock); + list_add_tail(&msg->node, &ctx->block_list); + spin_unlock_bh(&ctx->block_lock); + + return msg; +} + +int sst_wake_up_block(struct intel_sst_drv *ctx, int result, + u32 drv_id, u32 ipc, void *data, u32 size) +{ + struct sst_block *block = NULL; + + pr_debug("in %s\n", __func__); + spin_lock_bh(&ctx->block_lock); + list_for_each_entry(block, &ctx->block_list, node) { + pr_debug("Block ipc %d, drv_id %d\n", block->msg_id, + block->drv_id); + if (block->msg_id == ipc && block->drv_id == drv_id) { + pr_debug("free up the block\n"); + block->ret_code = result; + block->data = data; + block->size = size; + block->condition = true; + spin_unlock_bh(&ctx->block_lock); + wake_up(&ctx->wait_queue); + return 0; + } + } + spin_unlock_bh(&ctx->block_lock); + pr_debug("Block not found or a response is received for a short message for ipc %d, drv_id %d\n", + ipc, drv_id); + return -EINVAL; +} + +int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed) +{ + struct sst_block *block = NULL, *__block; + + pr_debug("in %s\n", __func__); + spin_lock_bh(&ctx->block_lock); + list_for_each_entry_safe(block, __block, &ctx->block_list, node) { + if (block == freed) { + list_del(&freed->node); + kfree(freed->data); + freed->data = NULL; + kfree(freed); + spin_unlock_bh(&ctx->block_lock); + return 0; + } + } + spin_unlock_bh(&ctx->block_lock); + return -EINVAL; +} + +/* + * sst_send_runtime_param - send runtime param to SST + * + * this function sends the runtime parameter to sst dsp engine + */ +static int sst_send_runtime_param(struct snd_sst_runtime_params *params) +{ + struct ipc_post *msg = NULL; + int ret_val; + + pr_debug("Enter:%s\n", __func__); + ret_val = sst_create_ipc_msg(&msg, true); + if (ret_val) + return ret_val; + sst_fill_header(&msg->header, IPC_IA_SET_RUNTIME_PARAMS, 1, + params->str_id); + msg->header.part.data = sizeof(u32) + sizeof(*params) - sizeof(params->addr) + + params->size; + memcpy(msg->mailbox_data, &msg->header.full, sizeof(u32)); + memcpy(msg->mailbox_data + sizeof(u32), params, sizeof(*params) + - sizeof(params->addr)); + /* driver doesn't need to send address, so overwrite addr with data */ + memcpy(msg->mailbox_data + sizeof(u32) + sizeof(*params) + - sizeof(params->addr), + params->addr, params->size); + sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); + return 0; +} + +void sst_post_message_mrfld(struct work_struct *work) +{ + struct ipc_post *msg; + union ipc_header_mrfld header; + unsigned long irq_flags; + + pr_debug("Enter:%s\n", __func__); + spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); + /* check list */ + if (list_empty(&sst_drv_ctx->ipc_dispatch_list)) { + /* queue is empty, nothing to send */ + spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); + pr_debug("Empty msg queue... NO Action\n"); + return; + } + + /* check busy bit */ + header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX); + if (header.p.header_high.part.busy) { + spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); + pr_debug("Busy not free... post later\n"); + return; + } + /* copy msg from list */ + msg = list_entry(sst_drv_ctx->ipc_dispatch_list.next, + struct ipc_post, node); + list_del(&msg->node); + pr_debug("sst: size: = %x\n", msg->mrfld_header.p.header_low_payload); + if (msg->mrfld_header.p.header_high.part.large) + memcpy_toio(sst_drv_ctx->mailbox + SST_MAILBOX_SEND, + msg->mailbox_data, msg->mrfld_header.p.header_low_payload); + + trace_sst_ipc("POST ->", msg->mrfld_header.p.header_high.full, + msg->mrfld_header.p.header_low_payload, + msg->mrfld_header.p.header_high.part.drv_id); + trace_sst_ipc_mailbox(msg->mailbox_data, msg->mrfld_header.p.header_low_payload); + sst_shim_write64(sst_drv_ctx->shim, SST_IPCX, msg->mrfld_header.full); + spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); + pr_debug("sst: Post message: header = %x\n", + msg->mrfld_header.p.header_high.full); + kfree(msg->mailbox_data); + kfree(msg); + return; +} + +/** +* sst_post_message - Posts message to SST +* +* @work: Pointer to work structure +* +* This function is called by any component in driver which +* wants to send an IPC message. This will post message only if +* busy bit is free +*/ +void sst_post_message_mfld(struct work_struct *work) +{ + struct ipc_post *msg; + union ipc_header header; + unsigned long irq_flags; + + pr_debug("Enter:%s\n", __func__); + + spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); + /* check list */ + if (list_empty(&sst_drv_ctx->ipc_dispatch_list)) { + /* queue is empty, nothing to send */ + spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); + pr_debug("Empty msg queue... NO Action\n"); + return; + } + + /* check busy bit */ + header.full = sst_shim_read(sst_drv_ctx->shim, sst_drv_ctx->ipc_reg.ipcx); + if (header.part.busy) { + spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); + pr_debug("Busy not free... Post later\n"); + return; + } + /* copy msg from list */ + msg = list_entry(sst_drv_ctx->ipc_dispatch_list.next, + struct ipc_post, node); + list_del(&msg->node); + pr_debug("size: = %x\n", msg->header.part.data); + if (msg->header.part.large) + memcpy_toio(sst_drv_ctx->mailbox + SST_MAILBOX_SEND, + msg->mailbox_data, msg->header.part.data); + + sst_shim_write(sst_drv_ctx->shim, sst_drv_ctx->ipc_reg.ipcx, msg->header.full); + spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); + pr_debug("Posted message: header = %x\n", msg->header.full); + + kfree(msg->mailbox_data); + kfree(msg); + return; +} + +int sst_sync_post_message_mrfld(struct ipc_post *msg) +{ + union ipc_header_mrfld header; + unsigned int loop_count = 0; + int retval = 0; + unsigned long irq_flags; + + pr_debug("Enter:%s\n", __func__); + spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); + + /* check busy bit */ + header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX); + while (header.p.header_high.part.busy) { + if (loop_count > 10) { + pr_err("sst: Busy wait failed, cant send this msg\n"); + retval = -EBUSY; + goto out; + } + udelay(500); + loop_count++; + header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX); + } + pr_debug("sst: Post message: header = %x\n", + msg->mrfld_header.p.header_high.full); + pr_debug("sst: size = 0x%x\n", msg->mrfld_header.p.header_low_payload); + if (msg->mrfld_header.p.header_high.part.large) + memcpy_toio(sst_drv_ctx->mailbox + SST_MAILBOX_SEND, + msg->mailbox_data, msg->mrfld_header.p.header_low_payload); + + trace_sst_ipc("POST ->", msg->mrfld_header.p.header_high.full, + msg->mrfld_header.p.header_low_payload, + msg->mrfld_header.p.header_high.part.drv_id); + trace_sst_ipc_mailbox(msg->mailbox_data, msg->mrfld_header.p.header_low_payload); + sst_shim_write64(sst_drv_ctx->shim, SST_IPCX, msg->mrfld_header.full); + +out: + spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); + kfree(msg->mailbox_data); + kfree(msg); + return retval; +} + +/* use this for trigger ops to post syncronous msgs + */ +int sst_sync_post_message_mfld(struct ipc_post *msg) +{ + union ipc_header header; + unsigned int loop_count = 0; + int retval = 0; + unsigned long irq_flags; + + pr_debug("Enter:%s\n", __func__); + spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); + + /* check busy bit */ + header.full = sst_shim_read(sst_drv_ctx->shim, sst_drv_ctx->ipc_reg.ipcx); + while (header.part.busy) { + if (loop_count > 10) { + pr_err("busy wait failed, cant send this msg\n"); + retval = -EBUSY; + goto out; + } + udelay(500); + loop_count++; + header.full = sst_shim_read(sst_drv_ctx->shim, sst_drv_ctx->ipc_reg.ipcx); + } + pr_debug("sst: Post message: header = %x\n", msg->header.full); + if (msg->header.part.large) + memcpy_toio(sst_drv_ctx->mailbox + SST_MAILBOX_SEND, + msg->mailbox_data, msg->header.part.data); + sst_shim_write(sst_drv_ctx->shim, sst_drv_ctx->ipc_reg.ipcx, msg->header.full); + +out: + spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); + kfree(msg->mailbox_data); + kfree(msg); + + return retval; +} + +/* + * sst_clear_interrupt - clear the SST FW interrupt + * + * This function clears the interrupt register after the interrupt + * bottom half is complete allowing next interrupt to arrive + */ +void intel_sst_clear_intr_mfld(void) +{ + union interrupt_reg isr; + union interrupt_reg imr; + union ipc_header clear_ipc; + unsigned long irq_flags; + + spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); + imr.full = sst_shim_read(sst_drv_ctx->shim, SST_IMRX); + isr.full = sst_shim_read(sst_drv_ctx->shim, SST_ISRX); + /* write 1 to clear */; + isr.part.busy_interrupt = 1; + sst_shim_write(sst_drv_ctx->shim, SST_ISRX, isr.full); + /* Set IA done bit */ + clear_ipc.full = sst_shim_read(sst_drv_ctx->shim, sst_drv_ctx->ipc_reg.ipcd); + clear_ipc.part.busy = 0; + clear_ipc.part.done = 1; + clear_ipc.part.data = IPC_ACK_SUCCESS; + sst_shim_write(sst_drv_ctx->shim, sst_drv_ctx->ipc_reg.ipcd, clear_ipc.full); + /* un mask busy interrupt */ + imr.part.busy_interrupt = 0; + imr.part.done_interrupt = 0; + sst_shim_write(sst_drv_ctx->shim, SST_IMRX, imr.full); + spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); +} + + +void intel_sst_clear_intr_mrfld(void) +{ + union interrupt_reg_mrfld isr; + union interrupt_reg_mrfld imr; + union ipc_header_mrfld clear_ipc; + unsigned long irq_flags; + + spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); + imr.full = sst_shim_read64(sst_drv_ctx->shim, SST_IMRX); + isr.full = sst_shim_read64(sst_drv_ctx->shim, SST_ISRX); + + /* write 1 to clear */ + isr.part.busy_interrupt = 1; + sst_shim_write64(sst_drv_ctx->shim, SST_ISRX, isr.full); + + /* Set IA done bit */ + clear_ipc.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCD); + + clear_ipc.p.header_high.part.busy = 0; + clear_ipc.p.header_high.part.done = 1; + clear_ipc.p.header_low_payload = IPC_ACK_SUCCESS; + sst_shim_write64(sst_drv_ctx->shim, SST_IPCD, clear_ipc.full); + /* un mask busy interrupt */ + imr.part.busy_interrupt = 0; + sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, imr.full); + spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); +} + + +/* + * process_fw_init - process the FW init msg + * + * @msg: IPC message mailbox data from FW + * + * This function processes the FW init msg from FW + * marks FW state and prints debug info of loaded FW + */ +static void process_fw_init(void *msg) +{ + struct ipc_header_fw_init *init = + (struct ipc_header_fw_init *)msg; + int retval = 0; + + pr_debug("*** FW Init msg came***\n"); + if (init->result) { + sst_drv_ctx->sst_state = SST_ERROR; + pr_debug("FW Init failed, Error %x\n", init->result); + pr_err("FW Init failed, Error %x\n", init->result); + retval = init->result; + goto ret; + } + pr_info("FW Version %02x.%02x.%02x.%02x\n", + init->fw_version.type, init->fw_version.major, + init->fw_version.minor, init->fw_version.build); + pr_info("Build date %s Time %s\n", + init->build_info.date, init->build_info.time); + + /* If there any runtime parameter to set, send it */ + if (sst_drv_ctx->runtime_param.param.addr) + sst_send_runtime_param(&(sst_drv_ctx->runtime_param.param)); + +ret: + sst_wake_up_block(sst_drv_ctx, retval, FW_DWNL_ID, 0 , NULL, 0); +} +/** +* sst_process_message_mfld - Processes message from SST +* +* @work: Pointer to work structure +* +* This function is scheduled by ISR +* It take a msg from process_queue and does action based on msg +*/ +void sst_process_message_mfld(struct ipc_post *msg) +{ + int str_id; + struct stream_info *stream; + + str_id = msg->header.part.str_id; + pr_debug("IPC process for %x\n", msg->header.full); + /* based on msg in list call respective handler */ + switch (msg->header.part.msg_id) { + case IPC_SST_PERIOD_ELAPSED: + if (sst_validate_strid(str_id)) { + pr_err("stream id %d invalid\n", str_id); + break; + } + stream = &sst_drv_ctx->streams[str_id]; + if (stream->period_elapsed) + stream->period_elapsed(stream->pcm_substream); + break; + case IPC_SST_BUF_UNDER_RUN: + case IPC_SST_BUF_OVER_RUN: + if (sst_validate_strid(str_id)) { + pr_err("stream id %d invalid\n", str_id); + break; + } + pr_err("Buffer under/overrun for %d\n", + msg->header.part.str_id); + pr_err("Got Underrun & not to send data...ignore\n"); + break; + + case IPC_SST_FRAGMENT_ELPASED: { + pr_debug("IPC_SST_FRAGMENT_ELPASED for %d", str_id); + sst_cdev_fragment_elapsed(str_id); + break; + } + + case IPC_IA_PRINT_STRING: + pr_debug("been asked to print something by fw\n"); + /* TBD */ + break; + + case IPC_IA_FW_INIT_CMPLT: { + /* send next data to FW */ + process_fw_init(msg->mailbox_data); + break; + } + + case IPC_SST_STREAM_PROCESS_FATAL_ERR: + if (sst_validate_strid(str_id)) { + pr_err("stream id %d invalid\n", str_id); + break; + } + pr_err("codec fatal error %x stream %d...\n", + msg->header.full, msg->header.part.str_id); + pr_err("Dropping the stream\n"); + sst_drop_stream(msg->header.part.str_id); + break; + default: + /* Illegal case */ + pr_err("Unhandled msg %x header %x\n", + msg->header.part.msg_id, msg->header.full); + } + return; +} + +/** +* sst_process_message - Processes message from SST +* +* @work: Pointer to work structure +* +* This function is scheduled by ISR +* It take a msg from process_queue and does action based on msg +*/ + +void sst_process_message_mrfld(struct ipc_post *msg) +{ + int str_id; + + str_id = msg->mrfld_header.p.header_high.part.drv_id; + + pr_debug("IPC process message header %x payload %x\n", + msg->mrfld_header.p.header_high.full, + msg->mrfld_header.p.header_low_payload); + + return; +} + +#define VTSV_MAX_NUM_RESULTS 6 +#define VTSV_SIZE_PER_RESULT 7 /* 7 16 bit words */ +/* Max 6 results each of size 7 words + 1 num results word */ +#define VTSV_MAX_TOTAL_RESULT_SIZE \ + (VTSV_MAX_NUM_RESULTS*VTSV_SIZE_PER_RESULT + 1) +/* Each data word in the result is sent as a string in the format: +DATAn=d, where n is the data word index varying from 0 to + VTSV_MAX_TOTAL_RESULT_SIZE-1 +d = string representation of data in decimal format; + unsigned 16bit data needs max 5 chars +So total data string size = 4("DATA")+2("n")+1("=") + +5("d")+1(null)+5(reserved) = 18 */ +#define VTSV_DATA_STRING_SIZE 18 + +static int send_vtsv_result_event(void *data, int size) +{ + char *envp[VTSV_MAX_TOTAL_RESULT_SIZE+3]; + char res_size[30]; + char ev_type[30]; + char result[VTSV_MAX_TOTAL_RESULT_SIZE][VTSV_DATA_STRING_SIZE]; + int offset = 0; + u16 *tmp; + int i; + int ret; + + if (!data) { + pr_err("Data pointer Null into %s\n", __func__); + return -EINVAL; + } + size = size / (sizeof(u16)); /* Number of 16 bit data words*/ + if (size > VTSV_MAX_TOTAL_RESULT_SIZE) { + pr_err("VTSV result size exceeds expected value, no uevent sent\n"); + return -EINVAL; + } + + snprintf(ev_type, sizeof(res_size), "EVENT_TYPE=SST_VTSV"); + envp[offset++] = ev_type; + snprintf(res_size, sizeof(ev_type), "VTSV_RESULT_SIZE=%u", size); + envp[offset++] = res_size; + tmp = (u16 *)(data); + for (i = 0; i < size; i++) { + /* Driver assumes all data to be u16; The VTSV service + layer will type cast to u16 or s16 as appropriate for + a given data word*/ + snprintf(result[i], VTSV_DATA_STRING_SIZE, + "DATA%u=%u", i, *tmp++); + envp[offset++] = result[i]; + } + envp[offset] = NULL; + ret = kobject_uevent_env(&sst_drv_ctx->dev->kobj, KOBJ_CHANGE, envp); + if (ret) + pr_err("VTSV event send failed: ret = %d\n", ret); + return ret; +} + +static void process_fw_async_msg(struct ipc_post *msg) +{ + u32 msg_id; + int str_id; + int res_size, ret; + u32 data_size, i; + void *data_offset; + struct stream_info *stream; + union ipc_header_high msg_high; + u32 msg_low, pipe_id; + + msg_high = msg->mrfld_header.p.header_high; + msg_low = msg->mrfld_header.p.header_low_payload; + msg_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->cmd_id; + data_offset = (msg->mailbox_data + sizeof(struct ipc_dsp_hdr)); + data_size = msg_low - (sizeof(struct ipc_dsp_hdr)); + + switch (msg_id) { + case IPC_SST_PERIOD_ELAPSED_MRFLD: + pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id; + str_id = get_stream_id_mrfld(pipe_id); + if (str_id > 0) { + pr_debug("Period elapsed rcvd for pipe id 0x%x\n", pipe_id); + stream = &sst_drv_ctx->streams[str_id]; + if (stream->period_elapsed) + stream->period_elapsed(stream->pcm_substream); + if (stream->compr_cb) + stream->compr_cb(stream->compr_cb_param); + } + break; + + case IPC_IA_DRAIN_STREAM_MRFLD: + pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id; + str_id = get_stream_id_mrfld(pipe_id); + if (str_id > 0) { + stream = &sst_drv_ctx->streams[str_id]; + if (stream->drain_notify) + stream->drain_notify(stream->drain_cb_param); + } + break; + + case IPC_IA_FW_ASYNC_ERR_MRFLD: + pr_err("FW sent async error msg:\n"); + for (i = 0; i < (data_size/4); i++) + pr_err("0x%x\n", (*((unsigned int *)data_offset + i))); + break; + + case IPC_IA_VTSV_DETECTED: + res_size = data_size; + ret = send_vtsv_result_event(data_offset, res_size); + if (ret) + pr_err("VTSV uevent send failed: %d\n", ret); + else + pr_debug("VTSV uevent sent\n"); + break; + + case IPC_IA_FW_INIT_CMPLT_MRFLD: + process_fw_init(data_offset); + break; + + case IPC_IA_BUF_UNDER_RUN_MRFLD: + pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id; + str_id = get_stream_id_mrfld(pipe_id); + if (str_id > 0) + pr_err("Buffer under-run for pipe:%#x str_id:%d\n", + pipe_id, str_id); + break; + + default: + pr_err("Unrecognized async msg from FW msg_id %#x\n", msg_id); + } +} + +void sst_process_reply_mrfld(struct ipc_post *msg) +{ + unsigned int drv_id; + void *data; + union ipc_header_high msg_high; + u32 msg_low; + + msg_high = msg->mrfld_header.p.header_high; + msg_low = msg->mrfld_header.p.header_low_payload; + + pr_debug("IPC process message header %x payload %x\n", + msg->mrfld_header.p.header_high.full, + msg->mrfld_header.p.header_low_payload); + + drv_id = msg_high.part.drv_id; + + /* Check for async messages */ + if (drv_id == SST_ASYNC_DRV_ID) { + /* FW sent async large message */ + process_fw_async_msg(msg); + goto end; + } + + /* FW sent short error response for an IPC */ + if (msg_high.part.result && drv_id && !msg_high.part.large) { + /* 32-bit FW error code in msg_low */ + pr_err("FW sent error response 0x%x", msg_low); + sst_wake_up_block(sst_drv_ctx, msg_high.part.result, + msg_high.part.drv_id, + msg_high.part.msg_id, NULL, 0); + goto end; + } + + /* Process all valid responses */ + /* if it is a large message, the payload contains the size to + * copy from mailbox */ + if (msg_high.part.large) { + data = kzalloc(msg_low, GFP_KERNEL); + if (!data) + goto end; + memcpy(data, (void *) msg->mailbox_data, msg_low); + if (sst_wake_up_block(sst_drv_ctx, msg_high.part.result, + msg_high.part.drv_id, + msg_high.part.msg_id, data, msg_low)) + kfree(data); + } else { + sst_wake_up_block(sst_drv_ctx, msg_high.part.result, + msg_high.part.drv_id, + msg_high.part.msg_id, NULL, 0); + } + +end: + return; +} + +/** +* sst_process_reply - Processes reply message from SST +* +* @work: Pointer to work structure +* +* This function is scheduled by ISR +* It take a reply msg from response_queue and +* does action based on msg +*/ +void sst_process_reply_mfld(struct ipc_post *msg) +{ + void *data; + int str_id; + struct stream_info *stream; + + + str_id = msg->header.part.str_id; + + pr_debug("sst: IPC process reply for %x\n", msg->header.full); + + /* handle drain notify first */ + if (msg->header.part.msg_id == IPC_IA_DRAIN_STREAM) { + pr_debug("drain message notify\n"); + if (str_id > 0) { + stream = &sst_drv_ctx->streams[str_id]; + if (stream->drain_notify) + stream->drain_notify(stream->drain_cb_param); + } + return; + } + + + if (!msg->header.part.large) { + if (!msg->header.part.data) + pr_debug("Success\n"); + else + pr_err("Error from firmware: %d\n", msg->header.part.data); + sst_wake_up_block(sst_drv_ctx, msg->header.part.data, + str_id, msg->header.part.msg_id, NULL, 0); + } else { + pr_debug("Allocating %d\n", msg->header.part.data); + data = kzalloc(msg->header.part.data, GFP_KERNEL); + if (!data) { + pr_err("sst: mem alloc failed\n"); + return; + } + + memcpy(data, (void *)msg->mailbox_data, msg->header.part.data); + if (sst_wake_up_block(sst_drv_ctx, 0, str_id, + msg->header.part.msg_id, data, + msg->header.part.data)) + kfree(data); + } + return; +} diff --git a/sound/soc/intel/sst/sst_pvt.c b/sound/soc/intel/sst/sst_pvt.c new file mode 100644 index 000000000000..7638cb81776a --- /dev/null +++ b/sound/soc/intel/sst/sst_pvt.c @@ -0,0 +1,577 @@ +/* + * sst_pvt.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-10 Intel Corp + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R + * KP Jeeja + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This file contains all private functions + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../sst_platform.h" +#include "../platform_ipc_v2.h" +#include "sst.h" + +#define SST_EXCE_DUMP_BASE 0xFFFF2c00 +#define SST_EXCE_DUMP_WORD 4 +#define SST_EXCE_DUMP_LEN 32 +#define SST_EXCE_DUMP_SIZE ((SST_EXCE_DUMP_LEN)*(SST_EXCE_DUMP_WORD)) +#define SST_EXCE_DUMP_OFFSET 0xA00 +/* + * sst_wait_interruptible - wait on event + * + * @sst_drv_ctx: Driver context + * @block: Driver block to wait on + * + * This function waits without a timeout (and is interruptable) for a + * given block event + */ +int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx, + struct sst_block *block) +{ + int retval = 0; + + if (!wait_event_interruptible(sst_drv_ctx->wait_queue, + block->condition)) { + /* event wake */ + if (block->ret_code < 0) { + pr_err("stream failed %d\n", block->ret_code); + retval = -EBUSY; + } else { + pr_debug("event up\n"); + retval = 0; + } + } else { + pr_err("signal interrupted\n"); + retval = -EINTR; + } + return retval; + +} + +unsigned long long read_shim_data(struct intel_sst_drv *sst, int addr) +{ + unsigned long long val = 0; + + switch (sst->pci_id) { + case SST_CLV_PCI_ID: + val = sst_shim_read(sst->shim, addr); + break; + case SST_MRFLD_PCI_ID: + case SST_BYT_PCI_ID: + val = sst_shim_read64(sst->shim, addr); + break; + } + return val; +} + +void write_shim_data(struct intel_sst_drv *sst, int addr, + unsigned long long data) +{ + switch (sst->pci_id) { + case SST_CLV_PCI_ID: + sst_shim_write(sst->shim, addr, (u32) data); + break; + case SST_MRFLD_PCI_ID: + case SST_BYT_PCI_ID: + sst_shim_write64(sst->shim, addr, (u64) data); + break; + } +} + + +void dump_sst_shim(struct intel_sst_drv *sst) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&sst->ipc_spin_lock, irq_flags); + pr_err("audio shim registers:\n" + "CSR: %.8llx\n" + "PISR: %.8llx\n" + "PIMR: %.8llx\n" + "ISRX: %.8llx\n" + "ISRD: %.8llx\n" + "IMRX: %.8llx\n" + "IMRD: %.8llx\n" + "IPCX: %.8llx\n" + "IPCD: %.8llx\n" + "ISRSC: %.8llx\n" + "ISRLPESC: %.8llx\n" + "IMRSC: %.8llx\n" + "IMRLPESC: %.8llx\n" + "IPCSC: %.8llx\n" + "IPCLPESC: %.8llx\n" + "CLKCTL: %.8llx\n" + "CSR2: %.8llx\n", + read_shim_data(sst, SST_CSR), + read_shim_data(sst, SST_PISR), + read_shim_data(sst, SST_PIMR), + read_shim_data(sst, SST_ISRX), + read_shim_data(sst, SST_ISRD), + read_shim_data(sst, SST_IMRX), + read_shim_data(sst, SST_IMRD), + read_shim_data(sst, sst->ipc_reg.ipcx), + read_shim_data(sst, sst->ipc_reg.ipcd), + read_shim_data(sst, SST_ISRSC), + read_shim_data(sst, SST_ISRLPESC), + read_shim_data(sst, SST_IMRSC), + read_shim_data(sst, SST_IMRLPESC), + read_shim_data(sst, SST_IPCSC), + read_shim_data(sst, SST_IPCLPESC), + read_shim_data(sst, SST_CLKCTL), + read_shim_data(sst, SST_CSR2)); + spin_unlock_irqrestore(&sst->ipc_spin_lock, irq_flags); +} + +void reset_sst_shim(struct intel_sst_drv *sst) +{ + union config_status_reg_mrfld csr; + + pr_err("Resetting few Shim registers\n"); + write_shim_data(sst, sst->ipc_reg.ipcx, 0x0); + write_shim_data(sst, sst->ipc_reg.ipcd, 0x0); + write_shim_data(sst, SST_ISRX, 0x0); + write_shim_data(sst, SST_ISRD, 0x0); + write_shim_data(sst, SST_IPCSC, 0x0); + write_shim_data(sst, SST_IPCLPESC, 0x0); + write_shim_data(sst, SST_ISRSC, 0x0); + write_shim_data(sst, SST_ISRLPESC, 0x0); + write_shim_data(sst, SST_PISR, 0x0); + + /* Reset the CSR value to the default value. i.e 0x1e40001*/ + csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR); + csr.part.xt_snoop = 0; + csr.full &= ~(0xf); + csr.full |= 0x01; + sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full); +} + +static void dump_sst_crash_area(void) +{ + void __iomem *fw_dump_area; + u32 dump_word; + u8 i; + + /* dump the firmware SRAM where the exception details are stored */ + fw_dump_area = ioremap_nocache(SST_EXCE_DUMP_BASE, SST_EXCE_DUMP_SIZE); + + pr_err("Firmware exception dump begins:\n"); + pr_err("Exception start signature:%#x\n", readl(fw_dump_area + SST_EXCE_DUMP_WORD)); + pr_err("EXCCAUSE:\t\t\t%#x\n", readl(fw_dump_area + SST_EXCE_DUMP_WORD*2)); + pr_err("EXCVADDR:\t\t\t%#x\n", readl(fw_dump_area + (SST_EXCE_DUMP_WORD*3))); + pr_err("Firmware additional data:\n"); + + /* dump remaining FW debug data */ + for (i = 1; i < (SST_EXCE_DUMP_LEN-4+1); i++) { + dump_word = readl(fw_dump_area + (SST_EXCE_DUMP_WORD*3) + + (i*SST_EXCE_DUMP_WORD)); + pr_err("Data[%d]=%#x\n", i, dump_word); + } + iounmap(fw_dump_area); + pr_err("Firmware exception dump ends\n"); +} + +/** + * dump_ram_area - dumps the iram/dram into a local buff + * + * @sst : pointer to driver context + * @recovery : pointer to the struct containing buffers + * @iram : true if iram dump else false + * This function dumps the iram dram data into the respective buffers + */ +static void dump_ram_area(struct intel_sst_drv *sst, + struct sst_dump_buf *dump_buf, enum sst_ram_type type) +{ + if (type == SST_IRAM) { + pr_err("Iram dumped in buffer\n"); + memcpy_fromio(dump_buf->iram_buf.buf, sst->iram, + dump_buf->iram_buf.size); + } else { + pr_err("Dram dumped in buffer\n"); + memcpy_fromio(dump_buf->dram_buf.buf, sst->dram, + dump_buf->dram_buf.size); + } +} + +/*FIXME Disabling IRAM/DRAM dump for timeout issues */ +static void sst_stream_recovery(struct intel_sst_drv *sst) +{ + struct stream_info *str_info; + u8 i; + for (i = 1; i <= sst->info.max_streams; i++) { + pr_err("Audio: Stream %d, state %d\n", i, sst->streams[i].status); + if (sst->streams[i].status != STREAM_UN_INIT) { + str_info = &sst_drv_ctx->streams[i]; + if (str_info->pcm_substream) + snd_pcm_stop(str_info->pcm_substream, SNDRV_PCM_STATE_SETUP); + else if (str_info->compr_cb_param) + snd_compr_stop(str_info->compr_cb_param); + sst->streams[i].status = STREAM_RESET; + } + } +} + +static void sst_dump_ipc_dispatch_lists(struct intel_sst_drv *sst) +{ + struct ipc_post *m, *_m; + unsigned long irq_flags; + + spin_lock_irqsave(&sst->ipc_spin_lock, irq_flags); + if (list_empty(&sst->ipc_dispatch_list)) + pr_err("ipc dispatch list is Empty\n"); + + list_for_each_entry_safe(m, _m, &sst->ipc_dispatch_list, node) { + pr_err("ipc-dispatch:pending msg header %#x\n", m->header.full); + list_del(&m->node); + kfree(m->mailbox_data); + kfree(m); + } + spin_unlock_irqrestore(&sst->ipc_spin_lock, irq_flags); +} + +static void sst_dump_rx_lists(struct intel_sst_drv *sst) +{ + struct ipc_post *m, *_m; + unsigned long irq_flags; + + spin_lock_irqsave(&sst->rx_msg_lock, irq_flags); + if (list_empty(&sst->rx_list)) + pr_err("rx msg list is empty\n"); + + list_for_each_entry_safe(m, _m, &sst->rx_list, node) { + pr_err("rx: pending msg header %#x\n", m->header.full); + list_del(&m->node); + kfree(m->mailbox_data); + kfree(m); + } + spin_unlock_irqrestore(&sst->rx_msg_lock, irq_flags); +} + +/* num_dwords: should be multiple of 4 */ +static void dump_buffer_fromio(void __iomem *from, + unsigned int num_dwords) +{ + int i; + u32 val[4]; + + if (num_dwords % 4) { + pr_err("%s: num_dwords %d not multiple of 4\n", + __func__, num_dwords); + return; + } + + pr_err("****** Start *******\n"); + pr_err("Dump %d dwords, from location %p\n", num_dwords, from); + + for (i = 0; i < num_dwords; ) { + val[0] = ioread32(from + (i++ * 4)); + val[1] = ioread32(from + (i++ * 4)); + val[2] = ioread32(from + (i++ * 4)); + val[3] = ioread32(from + (i++ * 4)); + pr_err("%.8x %.8x %.8x %.8x\n", val[0], val[1], val[2], val[3]); + } + pr_err("****** End *********\n\n\n"); +} + +static void sst_stall_lpe_n_wait(struct intel_sst_drv *sst) +{ + union config_status_reg_mrfld csr; + void __iomem *dma_reg0 = sst->debugfs.dma_reg[0]; + void __iomem *dma_reg1 = sst->debugfs.dma_reg[1]; + int offset = 0x3A0; /* ChEnReg of DMA */ + + + pr_err("Before stall: DMA_0 Ch_EN %#llx DMA_1 Ch_EN %#llx\n", + sst_reg_read64(dma_reg0, offset), + sst_reg_read64(dma_reg1, offset)); + + /* Stall LPE */ + csr.full = sst_shim_read64(sst->shim, SST_CSR); + csr.part.runstall = 1; + sst_shim_write64(sst->shim, SST_CSR, csr.full); + + /* A 5ms delay, before resetting the LPE */ + usleep_range(5000, 5100); + + pr_err("After stall: DMA_0 Ch_EN %#llx DMA_1 Ch_EN %#llx\n", + sst_reg_read64(dma_reg0, offset), + sst_reg_read64(dma_reg1, offset)); +} + +#if IS_ENABLED(CONFIG_INTEL_SCU_IPC) +static void sst_send_scu_reset_ipc(struct intel_sst_drv *sst) +{ + int ret = 0; + + /* Reset and power gate the LPE */ + ret = intel_scu_ipc_simple_command(IPC_SCU_LPE_RESET, 0); + if (ret) { + pr_err("Power gating LPE failed %d\n", ret); + reset_sst_shim(sst); + } else { + pr_err("LPE reset via SCU is success!!\n"); + pr_err("dump after LPE power cycle\n"); + dump_sst_shim(sst); + + /* Mask the DMA & SSP interrupts */ + sst_shim_write64(sst->shim, SST_IMRX, 0xFFFF0038); + } +} +#else +static void sst_send_scu_reset_ipc(struct intel_sst_drv *sst) +{ + pr_debug("%s: do nothing, just return\n", __func__); +} +#endif + +#define SRAM_OFFSET_MRFLD 0xc00 +#define NUM_DWORDS 256 +void sst_do_recovery_mrfld(struct intel_sst_drv *sst) +{ + char iram_event[30], dram_event[30], ddr_imr_event[65], event_type[30]; + char *envp[5]; + int env_offset = 0; + + /* + * setting firmware state as uninit so that the firmware will get + * redownloaded on next request.This is because firmare not responding + * for 1 sec is equalant to some unrecoverable error of FW. + */ + pr_err("Audio: Intel SST engine encountered an unrecoverable error\n"); + pr_err("Audio: trying to reset the dsp now\n"); + + mutex_lock(&sst->sst_lock); + sst->sst_state = SST_UN_INIT; + sst_stream_recovery(sst); + mutex_unlock(&sst->sst_lock); + + dump_stack(); + dump_sst_shim(sst); + + sst_stall_lpe_n_wait(sst); + + /* dump mailbox and sram */ + pr_err("Dumping Mailbox...\n"); + dump_buffer_fromio(sst->mailbox, NUM_DWORDS); + pr_err("Dumping SRAM...\n"); + dump_buffer_fromio(sst->mailbox + SRAM_OFFSET_MRFLD, NUM_DWORDS); + + if (sst_drv_ctx->ops->set_bypass) { + + sst_drv_ctx->ops->set_bypass(true); + dump_ram_area(sst, &(sst->dump_buf), SST_IRAM); + dump_ram_area(sst, &(sst->dump_buf), SST_DRAM); + sst_drv_ctx->ops->set_bypass(false); + + } + + snprintf(event_type, sizeof(event_type), "EVENT_TYPE=SST_RECOVERY"); + envp[env_offset++] = event_type; + snprintf(iram_event, sizeof(iram_event), "IRAM_DUMP_SIZE=%d", + sst->dump_buf.iram_buf.size); + envp[env_offset++] = iram_event; + snprintf(dram_event, sizeof(dram_event), "DRAM_DUMP_SIZE=%d", + sst->dump_buf.dram_buf.size); + envp[env_offset++] = dram_event; + + if (sst->ddr != NULL) { + snprintf(ddr_imr_event, sizeof(ddr_imr_event), + "DDR_IMR_DUMP_SIZE=%d DDR_IMR_ADDRESS=%p", (sst->ddr_end - sst->ddr_base), sst->ddr); + envp[env_offset++] = ddr_imr_event; + } + envp[env_offset] = NULL; + kobject_uevent_env(&sst->dev->kobj, KOBJ_CHANGE, envp); + pr_err("Recovery Uevent Sent!!\n"); + + /* Send IPC to SCU to power gate and reset the LPE */ + sst_send_scu_reset_ipc(sst); + + pr_err("reset the pvt id from val %d\n", sst_drv_ctx->pvt_id); + spin_lock(&sst_drv_ctx->pvt_id_lock); + sst_drv_ctx->pvt_id = 0; + spin_unlock(&sst_drv_ctx->pvt_id_lock); + sst_dump_ipc_dispatch_lists(sst_drv_ctx); + sst_dump_rx_lists(sst_drv_ctx); + + if (sst_drv_ctx->fw_in_mem) { + pr_err("Clearing the cached FW copy...\n"); + kfree(sst_drv_ctx->fw_in_mem); + sst_drv_ctx->fw_in_mem = NULL; + } +} + +void sst_do_recovery(struct intel_sst_drv *sst) +{ + pr_err("Audio: Intel SST engine encountered an unrecoverable error\n"); + + dump_stack(); + dump_sst_shim(sst); + + if (sst->sst_state == SST_FW_RUNNING && + sst_drv_ctx->pci_id == SST_CLV_PCI_ID) + dump_sst_crash_area(); + + sst_dump_ipc_dispatch_lists(sst_drv_ctx); + +} + +/* + * sst_wait_timeout - wait on event for timeout + * + * @sst_drv_ctx: Driver context + * @block: Driver block to wait on + * + * This function waits with a timeout value (and is not interruptible) on a + * given block event + */ +int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx, struct sst_block *block) +{ + int retval = 0; + + /* NOTE: + Observed that FW processes the alloc msg and replies even + before the alloc thread has finished execution */ + pr_debug("sst: waiting for condition %x ipc %d drv_id %d\n", + block->condition, block->msg_id, block->drv_id); + if (wait_event_timeout(sst_drv_ctx->wait_queue, + block->condition, + msecs_to_jiffies(SST_BLOCK_TIMEOUT))) { + /* event wake */ + pr_debug("sst: Event wake %x\n", block->condition); + pr_debug("sst: message ret: %d\n", block->ret_code); + retval = -block->ret_code; + } else { + block->on = false; + pr_err("sst: Wait timed-out condition:%#x, msg_id:%#x fw_state %#x\n", + block->condition, block->msg_id, sst_drv_ctx->sst_state); + + if (sst_drv_ctx->sst_state == SST_FW_LOADED || + sst_drv_ctx->sst_state == SST_START_INIT) { + pr_err("Can't recover as timedout while downloading the FW\n"); + pr_err("reseting fw state to unint from %d ...\n", sst_drv_ctx->sst_state); + sst_drv_ctx->sst_state = SST_UN_INIT; + + dump_sst_shim(sst_drv_ctx); + + /* Reset & Power Off the LPE only for MRFLD */ + if (sst_drv_ctx->pci_id == SST_MRFLD_PCI_ID) { + sst_stall_lpe_n_wait(sst_drv_ctx); + + /* Send IPC to SCU to power gate and reset the LPE */ + sst_send_scu_reset_ipc(sst_drv_ctx); + } + + } else { + if (sst_drv_ctx->ops->do_recovery) + sst_drv_ctx->ops->do_recovery(sst_drv_ctx); + } + + retval = -EBUSY; + } + return retval; +} + +/* + * sst_create_ipc_msg - create a IPC message + * + * @arg: ipc message + * @large: large or short message + * + * this function allocates structures to send a large or short + * message to the firmware + */ +int sst_create_ipc_msg(struct ipc_post **arg, bool large) +{ + struct ipc_post *msg; + + msg = kzalloc(sizeof(struct ipc_post), GFP_ATOMIC); + if (!msg) { + pr_err("kzalloc ipc msg failed\n"); + return -ENOMEM; + } + if (large) { + msg->mailbox_data = kzalloc(SST_MAILBOX_SIZE, GFP_ATOMIC); + if (!msg->mailbox_data) { + kfree(msg); + pr_err("kzalloc mailbox_data failed"); + return -ENOMEM; + } + } else { + msg->mailbox_data = NULL; + } + msg->is_large = large; + *arg = msg; + return 0; +} + +/* + * sst_create_block_and_ipc_msg - Creates IPC message and sst block + * @arg: passed to sst_create_ipc_message API + * @large: large or short message + * @sst_drv_ctx: sst driver context + * @block: return block allocated + * @msg_id: IPC + * @drv_id: stream id or private id + */ +int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large, + struct intel_sst_drv *sst_drv_ctx, struct sst_block **block, + u32 msg_id, u32 drv_id) +{ + int retval = 0; + retval = sst_create_ipc_msg(arg, large); + if (retval) + return retval; + *block = sst_create_block(sst_drv_ctx, msg_id, drv_id); + if (*block == NULL) { + kfree(*arg); + return -ENOMEM; + } + return retval; +} + +/* + * sst_clean_stream - clean the stream context + * + * @stream: stream structure + * + * this function resets the stream contexts + * should be called in free + */ +void sst_clean_stream(struct stream_info *stream) +{ + stream->status = STREAM_UN_INIT; + stream->prev = STREAM_UN_INIT; + mutex_lock(&stream->lock); + stream->cumm_bytes = 0; + mutex_unlock(&stream->lock); +} + diff --git a/sound/soc/intel/sst/sst_stream.c b/sound/soc/intel/sst/sst_stream.c new file mode 100644 index 000000000000..9e71b444fc85 --- /dev/null +++ b/sound/soc/intel/sst/sst_stream.c @@ -0,0 +1,831 @@ +/* + * sst_stream.c - Intel SST Driver for audio engine + * + * Copyright (C) 2008-10 Intel Corp + * Authors: Vinod Koul + * Harsha Priya + * Dharageswari R + * KP Jeeja + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This file contains the stream operations of SST driver + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include "../sst_platform.h" +#include "../platform_ipc_v2.h" +#include "sst.h" +#include "sst_trace.h" + +/** + * sst_alloc_stream - Send msg for a new stream ID + * + * @params: stream params + * @stream_ops: operation of stream PB/capture + * @codec: codec for stream + * @device: device stream to be allocated for + * + * This function is called by any function which wants to start + * a new stream. This also check if a stream exists which is idle + * it initializes idle stream id to this request + */ +int sst_alloc_stream_ctp(char *params, struct sst_block *block) +{ + struct ipc_post *msg = NULL; + struct snd_sst_alloc_params alloc_param; + unsigned int pcm_slot = 0x03, num_ch; + int str_id; + struct snd_sst_params *str_params; + struct snd_sst_stream_params *sparams; + struct snd_sst_alloc_params_ext *aparams; + struct stream_info *str_info; + unsigned int stream_ops, device; + u8 codec; + + pr_debug("In %s\n", __func__); + + BUG_ON(!params); + str_params = (struct snd_sst_params *)params; + stream_ops = str_params->ops; + codec = str_params->codec; + device = str_params->device_type; + sparams = &str_params->sparams; + aparams = &str_params->aparams; + num_ch = sst_get_num_channel(str_params); + + pr_debug("period_size = %d\n", aparams->frag_size); + pr_debug("ring_buf_addr = 0x%x\n", aparams->ring_buf_info[0].addr); + pr_debug("ring_buf_size = %d\n", aparams->ring_buf_info[0].size); + pr_debug("In alloc device_type=%d\n", str_params->device_type); + pr_debug("In alloc sg_count =%d\n", aparams->sg_count); + + str_id = str_params->stream_id; + if (str_id <= 0) + return -EBUSY; + + /*allocate device type context*/ + sst_init_stream(&sst_drv_ctx->streams[str_id], codec, + str_id, stream_ops, pcm_slot); + /* send msg to FW to allocate a stream */ + if (sst_create_ipc_msg(&msg, true)) + return -ENOMEM; + + alloc_param.str_type.codec_type = codec; + alloc_param.str_type.str_type = str_params->stream_type; + alloc_param.str_type.operation = stream_ops; + alloc_param.str_type.protected_str = 0; /* non drm */ + alloc_param.str_type.time_slots = pcm_slot; + alloc_param.str_type.reserved = 0; + alloc_param.str_type.result = 0; + memcpy(&alloc_param.stream_params, sparams, + sizeof(struct snd_sst_stream_params)); + memcpy(&alloc_param.alloc_params, aparams, + sizeof(struct snd_sst_alloc_params_ext)); + block->drv_id = str_id; + block->msg_id = IPC_IA_ALLOC_STREAM; + sst_fill_header(&msg->header, IPC_IA_ALLOC_STREAM, 1, str_id); + msg->header.part.data = sizeof(alloc_param) + sizeof(u32); + memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); + memcpy(msg->mailbox_data + sizeof(u32), &alloc_param, + sizeof(alloc_param)); + str_info = &sst_drv_ctx->streams[str_id]; + str_info->num_ch = num_ch; + sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); + return str_id; +} + +int sst_alloc_stream_mrfld(char *params, struct sst_block *block) +{ + struct ipc_post *msg = NULL; + struct snd_sst_alloc_mrfld alloc_param; + struct ipc_dsp_hdr dsp_hdr; + struct snd_sst_params *str_params; + struct snd_sst_tstamp fw_tstamp; + unsigned int str_id, pipe_id, pvt_id, task_id; + u32 len = 0; + struct stream_info *str_info; + int i, num_ch; + + pr_debug("In %s\n", __func__); + BUG_ON(!params); + + str_params = (struct snd_sst_params *)params; + memset(&alloc_param, 0, sizeof(alloc_param)); + alloc_param.operation = str_params->ops; + alloc_param.codec_type = str_params->codec; + alloc_param.sg_count = str_params->aparams.sg_count; + alloc_param.ring_buf_info[0].addr = str_params->aparams.ring_buf_info[0].addr; + alloc_param.ring_buf_info[0].size = str_params->aparams.ring_buf_info[0].size; + alloc_param.frag_size = str_params->aparams.frag_size; + + memcpy(&alloc_param.codec_params, &str_params->sparams, + sizeof(struct snd_sst_stream_params)); + + /* fill channel map params for multichannel support. + * Ideally channel map should be received from upper layers + * for multichannel support. + * Currently hardcoding as per FW reqm. + */ + num_ch = sst_get_num_channel(str_params); + for (i = 0; i < 8; i++) { + if (i < num_ch) + alloc_param.codec_params.uc.pcm_params.channel_map[i] = i; + else + alloc_param.codec_params.uc.pcm_params.channel_map[i] = 0xFF; + } + + str_id = str_params->stream_id; + pipe_id = str_params->device_type; + task_id = str_params->task; + sst_drv_ctx->streams[str_id].pipe_id = pipe_id; + sst_drv_ctx->streams[str_id].task_id = task_id; + sst_drv_ctx->streams[str_id].num_ch = num_ch; + + pvt_id = sst_assign_pvt_id(sst_drv_ctx); + if (sst_drv_ctx->info.lpe_viewpt_rqd) + alloc_param.ts = sst_drv_ctx->info.mailbox_start + + sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp)); + else + alloc_param.ts = sst_drv_ctx->mailbox_add + + sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp)); + + pr_debug("alloc tstamp location = 0x%x\n", alloc_param.ts); + pr_debug("assigned pipe id 0x%x to task %d\n", pipe_id, task_id); + + /*allocate device type context*/ + sst_init_stream(&sst_drv_ctx->streams[str_id], alloc_param.codec_type, + str_id, alloc_param.operation, 0); + /* send msg to FW to allocate a stream */ + if (sst_create_ipc_msg(&msg, true)) + return -ENOMEM; + + block->drv_id = pvt_id; + block->msg_id = IPC_CMD; + + sst_fill_header_mrfld(&msg->mrfld_header, IPC_CMD, + task_id, 1, pvt_id); + pr_debug("header:%x\n", msg->mrfld_header.p.header_high.full); + msg->mrfld_header.p.header_high.part.res_rqd = 1; + + len = msg->mrfld_header.p.header_low_payload = sizeof(alloc_param) + sizeof(dsp_hdr); + sst_fill_header_dsp(&dsp_hdr, IPC_IA_ALLOC_STREAM_MRFLD, pipe_id, sizeof(alloc_param)); + memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr)); + memcpy(msg->mailbox_data + sizeof(dsp_hdr), &alloc_param, + sizeof(alloc_param)); + trace_sst_stream("ALLOC ->", str_id, pipe_id); + str_info = &sst_drv_ctx->streams[str_id]; + pr_debug("header:%x\n", msg->mrfld_header.p.header_high.full); + pr_debug("response rqd: %x", msg->mrfld_header.p.header_high.part.res_rqd); + pr_debug("calling post_message\n"); + pr_info("Alloc for str %d pipe %#x\n", str_id, pipe_id); + + sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); + return str_id; +} + +/** +* sst_stream_stream - Send msg for a pausing stream +* @str_id: stream ID +* +* This function is called by any function which wants to start +* a stream. +*/ +int sst_start_stream(int str_id) +{ + int retval = 0, pvt_id; + u32 len = 0; + struct ipc_post *msg = NULL; + struct ipc_dsp_hdr dsp_hdr; + struct stream_info *str_info; + + pr_debug("sst_start_stream for %d\n", str_id); + str_info = get_stream_info(str_id); + if (!str_info) + return -EINVAL; + if (str_info->status != STREAM_RUNNING) + return -EBADRQC; + + if (sst_create_ipc_msg(&msg, true)) + return -ENOMEM; + + if (!sst_drv_ctx->use_32bit_ops) { + pvt_id = sst_assign_pvt_id(sst_drv_ctx); + pr_debug("pvt_id = %d, pipe id = %d, task = %d\n", + pvt_id, str_info->pipe_id, str_info->task_id); + sst_fill_header_mrfld(&msg->mrfld_header, IPC_CMD, + str_info->task_id, 1, pvt_id); + + len = sizeof(u16) + sizeof(dsp_hdr); + msg->mrfld_header.p.header_low_payload = len; + sst_fill_header_dsp(&dsp_hdr, IPC_IA_START_STREAM_MRFLD, + str_info->pipe_id, sizeof(u16)); + memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr)); + memset(msg->mailbox_data + sizeof(dsp_hdr), 0, sizeof(u16)); + trace_sst_stream("START ->", str_id, str_info->pipe_id); + pr_info("Start for str %d pipe %#x\n", str_id, str_info->pipe_id); + + } else { + pr_debug("fill START_STREAM for CTP\n"); + sst_fill_header(&msg->header, IPC_IA_START_STREAM, 1, str_id); + msg->header.part.data = sizeof(u32) + sizeof(u32); + memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); + memset(msg->mailbox_data + sizeof(u32), 0, sizeof(u32)); + } + sst_drv_ctx->ops->sync_post_message(msg); + return retval; +} + +int sst_send_byte_stream_mrfld(void *sbytes) +{ + struct ipc_post *msg = NULL; + struct snd_sst_bytes_v2 *bytes = (struct snd_sst_bytes_v2 *) sbytes; + u32 length; + int pvt_id, ret = 0; + struct sst_block *block = NULL; + + pr_debug("%s: type:%u ipc_msg:%u block:%u task_id:%u pipe: %#x length:%#x\n", + __func__, bytes->type, bytes->ipc_msg, + bytes->block, bytes->task_id, + bytes->pipe_id, bytes->len); + + /* need some err check as this is user data, perhpas move this to the + * platform driver and pass the struct + */ + if (sst_create_ipc_msg(&msg, true)) + return -ENOMEM; + + pvt_id = sst_assign_pvt_id(sst_drv_ctx); + sst_fill_header_mrfld(&msg->mrfld_header, bytes->ipc_msg, bytes->task_id, + 1, pvt_id); + msg->mrfld_header.p.header_high.part.res_rqd = bytes->block; + length = bytes->len; + msg->mrfld_header.p.header_low_payload = length; + pr_debug("length is %d\n", length); + memcpy(msg->mailbox_data, &bytes->bytes, bytes->len); + trace_sst_stream("BYTES ->", bytes->type, bytes->pipe_id); + if (bytes->block) { + block = sst_create_block(sst_drv_ctx, bytes->ipc_msg, pvt_id); + if (block == NULL) { + kfree(msg); + return -ENOMEM; + } + } + sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); + pr_debug("msg->mrfld_header.p.header_low_payload:%d", msg->mrfld_header.p.header_low_payload); + if (bytes->block) { + ret = sst_wait_timeout(sst_drv_ctx, block); + if (ret) { + pr_err("%s: fw returned err %d\n", __func__, ret); + sst_free_block(sst_drv_ctx, block); + return ret; + } + } + if (bytes->type == SND_SST_BYTES_GET) { + /* copy the reply and send back + * we need to update only sz and payload + */ + if (bytes->block) { + unsigned char *r = block->data; + pr_debug("read back %d bytes", bytes->len); + memcpy(bytes->bytes, r, bytes->len); + trace_sst_stream("BYTES <-", bytes->type, bytes->pipe_id); + } + } + if (bytes->block) + sst_free_block(sst_drv_ctx, block); + return 0; +} + +int sst_send_probe_bytes(struct intel_sst_drv *sst) +{ + struct ipc_post *msg = NULL; + struct sst_block *block; + int ret_val = 0; + + ret_val = sst_create_block_and_ipc_msg(&msg, true, sst, + &block, IPC_IA_DBG_SET_PROBE_PARAMS, 0); + if (ret_val) { + pr_err("Can't allocate block/msg: Probe Byte Stream\n"); + return ret_val; + } + + sst_fill_header(&msg->header, IPC_IA_DBG_SET_PROBE_PARAMS, 1, 0); + + msg->header.part.data = sizeof(u32) + sst->probe_bytes->len; + memcpy(msg->mailbox_data, &msg->header.full, sizeof(u32)); + memcpy(msg->mailbox_data + sizeof(u32), sst->probe_bytes->bytes, + sst->probe_bytes->len); + + sst_add_to_dispatch_list_and_post(sst, msg); + ret_val = sst_wait_timeout(sst, block); + sst_free_block(sst, block); + if (ret_val) + pr_err("set probe stream param..timeout!\n"); + return ret_val; +} + +/* + * sst_pause_stream - Send msg for a pausing stream + * @str_id: stream ID + * + * This function is called by any function which wants to pause + * an already running stream. + */ +int sst_pause_stream(int str_id) +{ + int retval = 0, pvt_id, len; + struct ipc_post *msg = NULL; + struct stream_info *str_info; + struct intel_sst_ops *ops; + struct sst_block *block; + struct ipc_dsp_hdr dsp_hdr; + + pr_debug("SST DBG:sst_pause_stream for %d\n", str_id); + str_info = get_stream_info(str_id); + if (!str_info) + return -EINVAL; + ops = sst_drv_ctx->ops; + if (str_info->status == STREAM_PAUSED) + return 0; + if (str_info->status == STREAM_RUNNING || + str_info->status == STREAM_INIT) { + if (str_info->prev == STREAM_UN_INIT) + return -EBADRQC; + if (!sst_drv_ctx->use_32bit_ops) { + pvt_id = sst_assign_pvt_id(sst_drv_ctx); + retval = sst_create_block_and_ipc_msg(&msg, true, + sst_drv_ctx, &block, IPC_CMD, pvt_id); + if (retval) + return retval; + sst_fill_header_mrfld(&msg->mrfld_header, IPC_CMD, + str_info->task_id, 1, pvt_id); + msg->mrfld_header.p.header_high.part.res_rqd = 1; + len = sizeof(dsp_hdr); + msg->mrfld_header.p.header_low_payload = len; + sst_fill_header_dsp(&dsp_hdr, IPC_IA_PAUSE_STREAM_MRFLD, + str_info->pipe_id, 0); + memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr)); + trace_sst_stream("PAUSE ->", str_id, str_info->pipe_id); + } else { + retval = sst_create_block_and_ipc_msg(&msg, false, + sst_drv_ctx, &block, + IPC_IA_PAUSE_STREAM, str_id); + if (retval) + return retval; + sst_fill_header(&msg->header, IPC_IA_PAUSE_STREAM, + 0, str_id); + } + sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); + retval = sst_wait_timeout(sst_drv_ctx, block); + sst_free_block(sst_drv_ctx, block); + if (retval == 0) { + str_info->prev = str_info->status; + str_info->status = STREAM_PAUSED; + } else if (retval == SST_ERR_INVALID_STREAM_ID) { + retval = -EINVAL; + mutex_lock(&sst_drv_ctx->stream_lock); + sst_clean_stream(str_info); + mutex_unlock(&sst_drv_ctx->stream_lock); + } + } else { + retval = -EBADRQC; + pr_debug("SST DBG:BADRQC for stream\n "); + } + + return retval; +} + +/** + * sst_resume_stream - Send msg for resuming stream + * @str_id: stream ID + * + * This function is called by any function which wants to resume + * an already paused stream. + */ +int sst_resume_stream(int str_id) +{ + int retval = 0; + struct ipc_post *msg = NULL; + struct stream_info *str_info; + struct intel_sst_ops *ops; + struct sst_block *block = NULL; + int pvt_id, len; + struct ipc_dsp_hdr dsp_hdr; + + pr_debug("SST DBG:sst_resume_stream for %d\n", str_id); + str_info = get_stream_info(str_id); + if (!str_info) + return -EINVAL; + ops = sst_drv_ctx->ops; + if (str_info->status == STREAM_RUNNING) + return 0; + if (str_info->status == STREAM_PAUSED) { + if (!sst_drv_ctx->use_32bit_ops) { + pvt_id = sst_assign_pvt_id(sst_drv_ctx); + retval = sst_create_block_and_ipc_msg(&msg, true, + sst_drv_ctx, &block, IPC_CMD, pvt_id); + if (retval) + return retval; + sst_fill_header_mrfld(&msg->mrfld_header, IPC_CMD, + str_info->task_id, 1, pvt_id); + msg->mrfld_header.p.header_high.part.res_rqd = 1; + len = sizeof(dsp_hdr); + msg->mrfld_header.p.header_low_payload = len; + sst_fill_header_dsp(&dsp_hdr, + IPC_IA_RESUME_STREAM_MRFLD, + str_info->pipe_id, 0); + memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr)); + trace_sst_stream("RESUME->", str_id, str_info->pipe_id); + } else { + retval = sst_create_block_and_ipc_msg(&msg, false, + sst_drv_ctx, &block, + IPC_IA_RESUME_STREAM, str_id); + if (retval) + return retval; + sst_fill_header(&msg->header, IPC_IA_RESUME_STREAM, + 0, str_id); + } + sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); + retval = sst_wait_timeout(sst_drv_ctx, block); + sst_free_block(sst_drv_ctx, block); + if (!retval) { + if (str_info->prev == STREAM_RUNNING) + str_info->status = STREAM_RUNNING; + else + str_info->status = STREAM_INIT; + str_info->prev = STREAM_PAUSED; + } else if (retval == -SST_ERR_INVALID_STREAM_ID) { + retval = -EINVAL; + mutex_lock(&sst_drv_ctx->stream_lock); + sst_clean_stream(str_info); + mutex_unlock(&sst_drv_ctx->stream_lock); + } + } else { + retval = -EBADRQC; + pr_err("SST ERR: BADQRC for stream\n"); + } + + return retval; +} + + +/** + * sst_drop_stream - Send msg for stopping stream + * @str_id: stream ID + * + * This function is called by any function which wants to stop + * a stream. + */ +int sst_drop_stream(int str_id) +{ + int retval = 0, pvt_id; + struct stream_info *str_info; + struct ipc_post *msg = NULL; + struct ipc_dsp_hdr dsp_hdr; + + pr_debug("SST DBG:sst_drop_stream for %d\n", str_id); + str_info = get_stream_info(str_id); + if (!str_info) + return -EINVAL; + + if (str_info->status != STREAM_UN_INIT) { + + if (sst_drv_ctx->use_32bit_ops == true) { + str_info->prev = STREAM_UN_INIT; + str_info->status = STREAM_INIT; + str_info->cumm_bytes = 0; + sst_send_sync_msg(IPC_IA_DROP_STREAM, str_id); + } else { + if (sst_create_ipc_msg(&msg, true)) + return -ENOMEM; + str_info->prev = STREAM_UN_INIT; + str_info->status = STREAM_INIT; + str_info->cumm_bytes = 0; + pvt_id = sst_assign_pvt_id(sst_drv_ctx); + sst_fill_header_mrfld(&msg->mrfld_header, IPC_CMD, + str_info->task_id, 1, pvt_id); + + msg->mrfld_header.p.header_low_payload = sizeof(dsp_hdr); + sst_fill_header_dsp(&dsp_hdr, IPC_IA_DROP_STREAM_MRFLD, + str_info->pipe_id, 0); + memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr)); + trace_sst_stream("STOP ->", str_id, str_info->pipe_id); + pr_info("Stop for str %d pipe %#x\n", str_id, str_info->pipe_id); + + sst_drv_ctx->ops->sync_post_message(msg); + } + } else { + retval = -EBADRQC; + pr_debug("BADQRC for stream, state %x\n", str_info->status); + } + return retval; +} + +/** + * sst_next_track: notify next track + * @str_id: stream ID + * + * This function is called by any function which wants to + * set next track. Current this is NOP as FW doest care + */ +int sst_next_track(void) +{ + pr_debug("SST DBG: next_track"); + return 0; +} + +/** +* sst_drain_stream - Send msg for draining stream +* @str_id: stream ID +* +* This function is called by any function which wants to drain +* a stream. +*/ +int sst_drain_stream(int str_id, bool partial_drain) +{ + int retval = 0, pvt_id, len; + struct ipc_post *msg = NULL; + struct stream_info *str_info; + struct intel_sst_ops *ops; + struct sst_block *block = NULL; + struct ipc_dsp_hdr dsp_hdr; + + pr_debug("SST DBG:sst_drain_stream for %d\n", str_id); + str_info = get_stream_info(str_id); + if (!str_info) + return -EINVAL; + ops = sst_drv_ctx->ops; + if (str_info->status != STREAM_RUNNING && + str_info->status != STREAM_INIT && + str_info->status != STREAM_PAUSED) { + pr_err("SST ERR: BADQRC for stream = %d\n", + str_info->status); + return -EBADRQC; + } + + if (!sst_drv_ctx->use_32bit_ops) { + pvt_id = sst_assign_pvt_id(sst_drv_ctx); + retval = sst_create_block_and_ipc_msg(&msg, true, + sst_drv_ctx, &block, IPC_CMD, pvt_id); + if (retval) + return retval; + sst_fill_header_mrfld(&msg->mrfld_header, IPC_CMD, + str_info->task_id, 1, pvt_id); + pr_debug("header:%x\n", + (unsigned int)msg->mrfld_header.p.header_high.full); + msg->mrfld_header.p.header_high.part.res_rqd = 1; + + len = sizeof(u8) + sizeof(dsp_hdr); + msg->mrfld_header.p.header_low_payload = len; + sst_fill_header_dsp(&dsp_hdr, IPC_IA_DRAIN_STREAM_MRFLD, + str_info->pipe_id, sizeof(u8)); + memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr)); + memcpy(msg->mailbox_data + sizeof(dsp_hdr), + &partial_drain, sizeof(u8)); + trace_sst_stream("DRAIN ->", str_id, str_info->pipe_id); + } else { + retval = sst_create_block_and_ipc_msg(&msg, false, + sst_drv_ctx, &block, + IPC_IA_DRAIN_STREAM, str_id); + if (retval) + return retval; + sst_fill_header(&msg->header, IPC_IA_DRAIN_STREAM, 0, str_id); + msg->header.part.data = partial_drain; + } + sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg); + /* with new non blocked drain implementation in core we dont need to + * wait for respsonse, and need to only invoke callback for drain + * complete + */ + + sst_free_block(sst_drv_ctx, block); + return retval; +} + +/** + * sst_free_stream - Frees a stream + * @str_id: stream ID + * + * This function is called by any function which wants to free + * a stream. + */ +int sst_free_stream(int str_id) +{ + int retval = 0; + unsigned int pvt_id; + struct ipc_post *msg = NULL; + struct stream_info *str_info; + struct intel_sst_ops *ops; + unsigned long irq_flags; + struct ipc_dsp_hdr dsp_hdr; + struct sst_block *block; + + pr_debug("SST DBG:sst_free_stream for %d\n", str_id); + + mutex_lock(&sst_drv_ctx->sst_lock); + if (sst_drv_ctx->sst_state == SST_UN_INIT) { + mutex_unlock(&sst_drv_ctx->sst_lock); + return -ENODEV; + } + mutex_unlock(&sst_drv_ctx->sst_lock); + str_info = get_stream_info(str_id); + if (!str_info) + return -EINVAL; + ops = sst_drv_ctx->ops; + + mutex_lock(&str_info->lock); + if (str_info->status != STREAM_UN_INIT) { + str_info->prev = str_info->status; + str_info->status = STREAM_UN_INIT; + mutex_unlock(&str_info->lock); + + if (!sst_drv_ctx->use_32bit_ops) { + pvt_id = sst_assign_pvt_id(sst_drv_ctx); + retval = sst_create_block_and_ipc_msg(&msg, true, + sst_drv_ctx, &block, IPC_CMD, pvt_id); + if (retval) + return retval; + + sst_fill_header_mrfld(&msg->mrfld_header, IPC_CMD, + str_info->task_id, 1, pvt_id); + msg->mrfld_header.p.header_low_payload = + sizeof(dsp_hdr); + sst_fill_header_dsp(&dsp_hdr, IPC_IA_FREE_STREAM_MRFLD, + str_info->pipe_id, 0); + memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr)); + trace_sst_stream("FREE ->", str_id, str_info->pipe_id); + pr_info("Free for str %d pipe %#x\n", str_id, str_info->pipe_id); + + } else { + retval = sst_create_block_and_ipc_msg(&msg, false, + sst_drv_ctx, &block, + IPC_IA_FREE_STREAM, str_id); + if (retval) + return retval; + sst_fill_header(&msg->header, IPC_IA_FREE_STREAM, + 0, str_id); + } + spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags); + list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); + spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags); + if (!sst_drv_ctx->use_32bit_ops) { + /*FIXME: do we need to wake up drain stream here, + * how to get the pvt_id and msg_id + */ + } else { + sst_wake_up_block(sst_drv_ctx, 0, str_id, + IPC_IA_DRAIN_STREAM, NULL, 0); + } + ops->post_message(&sst_drv_ctx->ipc_post_msg_wq); + retval = sst_wait_timeout(sst_drv_ctx, block); + pr_debug("sst: wait for free returned %d\n", retval); + mutex_lock(&sst_drv_ctx->stream_lock); + sst_clean_stream(str_info); + mutex_unlock(&sst_drv_ctx->stream_lock); + pr_debug("SST DBG:Stream freed\n"); + sst_free_block(sst_drv_ctx, block); + } else { + mutex_unlock(&str_info->lock); + retval = -EBADRQC; + pr_debug("SST DBG:BADQRC for stream\n"); + } + + return retval; +} + +int sst_request_vtsv_file(char *fname, struct intel_sst_drv *ctx, + void **out_file, u32 *out_size) +{ + int retval = 0; + const struct firmware *file; + void *ddr_virt_addr; + unsigned long file_base; + + if (!ctx->pdata->lib_info) { + pr_err("lib_info pointer NULL\n"); + return -EINVAL; + } + + pr_debug("Requesting VTSV file %s now...\n", fname); + retval = request_firmware(&file, fname, ctx->dev); + if (file == NULL) { + pr_err("VTSV file is returning as null\n"); + return -EINVAL; + } + if (retval) { + pr_err("request fw failed %d\n", retval); + return retval; + } + + if ((*out_file == NULL) || (*out_size < file->size)) { + retval = sst_get_next_lib_mem(&ctx->lib_mem_mgr, file->size, + &file_base); + *out_file = (void *)file_base; + } + ddr_virt_addr = (unsigned char *)ctx->ddr + + (unsigned long)(*out_file - ctx->pdata->lib_info->mod_base); + memcpy(ddr_virt_addr, file->data, file->size); + + *out_size = file->size; + release_firmware(file); + return 0; +} + +int sst_format_vtsv_message(struct intel_sst_drv *ctx, + struct ipc_post **msgptr, struct sst_block **block) +{ + int retval = 0, pvt_id, len; + struct ipc_dsp_hdr dsp_hdr; + struct snd_sst_vtsv_info vinfo; + struct ipc_post *msg; + + BUG_ON((unsigned long)(ctx->vcache.file1_in_mem) & 0xffffffff00000000ULL); + BUG_ON((unsigned long)(ctx->vcache.file2_in_mem) & 0xffffffff00000000ULL); + + vinfo.vfiles[0].addr = (u32)((unsigned long)ctx->vcache.file1_in_mem + & 0xffffffff); + vinfo.vfiles[0].size = ctx->vcache.size1; + vinfo.vfiles[1].addr = (u32)((unsigned long)ctx->vcache.file2_in_mem + & 0xffffffff); + vinfo.vfiles[1].size = ctx->vcache.size2; + + /* Create the vtsv message */ + pvt_id = sst_assign_pvt_id(ctx); + retval = sst_create_block_and_ipc_msg(msgptr, true, + ctx, block, IPC_CMD, pvt_id); + if (retval) + return retval; + msg = *msgptr; + sst_fill_header_mrfld(&msg->mrfld_header, IPC_CMD, + SST_TASK_ID_AWARE, 1, pvt_id); + pr_debug("header:%x\n", + (unsigned int)msg->mrfld_header.p.header_high.full); + msg->mrfld_header.p.header_high.part.res_rqd = 1; + + len = sizeof(vinfo) + sizeof(dsp_hdr); + msg->mrfld_header.p.header_low_payload = len; + sst_fill_header_dsp(&dsp_hdr, IPC_IA_VTSV_UPDATE_MODULES, + PIPE_VAD_OUT, sizeof(u8)); + dsp_hdr.mod_id = SST_ALGO_VTSV; + memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr)); + memcpy(msg->mailbox_data + sizeof(dsp_hdr), + &vinfo, sizeof(vinfo)); + return 0; +} + +int sst_send_vtsv_data_to_fw(struct intel_sst_drv *ctx) +{ + int retval = 0; + struct ipc_post *msg = NULL; + struct sst_block *block = NULL; + + /* Download both the data files */ + retval = sst_request_vtsv_file("vtsv_net.bin", ctx, + &ctx->vcache.file1_in_mem, &ctx->vcache.size1); + if (retval) { + pr_err("vtsv data file1 request failed %d\n", retval); + return retval; + } + + retval = sst_request_vtsv_file("vtsv_grammar.bin", ctx, + &ctx->vcache.file2_in_mem, &ctx->vcache.size2); + if (retval) { + pr_err("vtsv data file2 request failed %d\n", retval); + return retval; + } + + retval = sst_format_vtsv_message(ctx, &msg, &block); + if (retval) { + pr_err("vtsv msg format failed %d\n", retval); + return retval; + } + sst_add_to_dispatch_list_and_post(ctx, msg); + retval = sst_wait_timeout(ctx, block); + if (retval) + pr_err("vtsv msg send to fw failed %d\n", retval); + + sst_free_block(ctx, block); + return retval; +} diff --git a/sound/soc/intel/sst/sst_trace.h b/sound/soc/intel/sst/sst_trace.h new file mode 100644 index 000000000000..8d34ef3575e5 --- /dev/null +++ b/sound/soc/intel/sst/sst_trace.h @@ -0,0 +1,147 @@ +/* + * sst_trace.h - Intel SST Driver tracing support + * + * Copyright (C) 2013 Intel Corp + * Authors: Omair Mohammed Abdullah + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM sst + +#if !defined(_TRACE_SST_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_SST_H + +#include +#include +#include + +TRACE_EVENT(sst_ipc, + + TP_PROTO(const char *msg, u32 header_high, u32 header_low, int pvt_id), + + TP_ARGS(msg, header_high, header_low, pvt_id), + + TP_STRUCT__entry( + __string(info_msg, msg) + __field(unsigned int, val_l) + __field(unsigned int, val_h) + __field(unsigned int, id) + ), + + TP_fast_assign( + __assign_str(info_msg, msg); + __entry->val_l = header_low; + __entry->val_h = header_high; + __entry->id = pvt_id; + ), + + TP_printk("\t%s\t [%2u] = %#8.8x:%.4x", __get_str(info_msg), + (unsigned int)__entry->id, + (unsigned int)__entry->val_h, (unsigned int)__entry->val_l) + +); + +TRACE_EVENT(sst_stream, + + TP_PROTO(const char *msg, int str_id, int pipe_id), + + TP_ARGS(msg, str_id, pipe_id), + + TP_STRUCT__entry( + __string(info_msg, msg) + __field(unsigned int, str_id) + __field(unsigned int, pipe_id) + ), + + TP_fast_assign( + __assign_str(info_msg, msg); + __entry->str_id = str_id; + __entry->pipe_id = pipe_id; + ), + + TP_printk("\t%s\t str = %2u, pipe = %#x", __get_str(info_msg), + (unsigned int)__entry->str_id, (unsigned int)__entry->pipe_id) +); + +TRACE_EVENT(sst_ipc_mailbox, + + TP_PROTO(const char *mailbox, int mbox_len), + + TP_ARGS(mailbox, mbox_len), + + TP_STRUCT__entry( + __dynamic_array(char, mbox, (3 * mbox_len)) + ), + + TP_fast_assign( + sst_dump_to_buffer(mailbox, mbox_len, + __get_dynamic_array(mbox)); + ), + + TP_printk(" %s", __get_str(mbox)) + +); + +TRACE_EVENT(sst_lib_download, + + TP_PROTO(const char *msg, const char *lib_name), + + TP_ARGS(msg, lib_name), + + TP_STRUCT__entry( + __string(info_msg, msg) + __string(info_lib_name, lib_name) + ), + + TP_fast_assign( + __assign_str(info_msg, msg); + __assign_str(info_lib_name, lib_name); + ), + + TP_printk("\t%s %s", __get_str(info_msg), + __get_str(info_lib_name)) +); + +TRACE_EVENT(sst_fw_download, + + TP_PROTO(const char *msg, int fw_state), + + TP_ARGS(msg, fw_state), + + TP_STRUCT__entry( + __string(info_msg, msg) + __field(unsigned int, fw_state) + ), + + TP_fast_assign( + __assign_str(info_msg, msg); + __entry->fw_state = fw_state; + ), + + TP_printk("\t%s\tFW state = %d", __get_str(info_msg), + (unsigned int)__entry->fw_state) +); + +#endif /* _TRACE_SST_H */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE sst_trace +#include diff --git a/sound/soc/intel/sst_platform.h b/sound/soc/intel/sst_platform.h new file mode 100644 index 000000000000..5d231b5e5e6f --- /dev/null +++ b/sound/soc/intel/sst_platform.h @@ -0,0 +1,155 @@ +/* + * sst_platform.h - Intel MID Platform driver header file + * + * Copyright (C) 2010 Intel Corp + * Author: Vinod Koul + * Author: Harsha Priya + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * + */ + +#ifndef __SST_PLATFORM_H__ +#define __SST_PLATFORM_H__ + +#include + +#define SST_MAX_BIN_BYTES 1024 + +struct sst_data; + +enum sst_audio_device_type { + SND_SST_DEVICE_HEADSET = 1, + SND_SST_DEVICE_IHF, + SND_SST_DEVICE_VIBRA, + SND_SST_DEVICE_HAPTIC, + SND_SST_DEVICE_CAPTURE, + SND_SST_DEVICE_COMPRESS, +}; + +enum snd_sst_input_stream { + SST_INPUT_STREAM_NONE = 0x0, + SST_INPUT_STREAM_PCM = 0x6, + SST_INPUT_STREAM_COMPRESS = 0x8, + SST_INPUT_STREAM_MIXED = 0xE, +}; + +enum sst_stream_ops { + STREAM_OPS_PLAYBACK = 0, /* Decode */ + STREAM_OPS_CAPTURE, /* Encode */ + STREAM_OPS_COMPRESSED_PATH, /* Offload playback/capture */ + +}; +enum snd_sst_stream_type { + SST_STREAM_DEVICE_HS = 32, + SST_STREAM_DEVICE_IHF = 33, + SST_STREAM_DEVICE_MIC0 = 34, + SST_STREAM_DEVICE_MIC1 = 35, +}; + +enum sst_controls { + SST_SND_ALLOC = 0x1000, + SST_SND_PAUSE = 0x1001, + SST_SND_RESUME = 0x1002, + SST_SND_DROP = 0x1003, + SST_SND_FREE = 0x1004, + SST_SND_BUFFER_POINTER = 0x1005, + SST_SND_STREAM_INIT = 0x1006, + SST_SND_START = 0x1007, + SST_SET_RUNTIME_PARAMS = 0x1008, + SST_SET_ALGO_PARAMS = 0x1009, + SST_SET_BYTE_STREAM = 0x100A, + SST_GET_BYTE_STREAM = 0x100B, + SST_SET_SSP_CONFIG = 0x100C, + SST_SET_PROBE_BYTE_STREAM = 0x100D, + SST_GET_PROBE_BYTE_STREAM = 0x100E, + SST_SET_VTSV_INFO = 0x100F, +}; + +struct pcm_stream_info { + int str_id; + void *mad_substream; + void (*period_elapsed) (void *mad_substream); + unsigned long long buffer_ptr; + unsigned long long pcm_delay; + int sfreq; +}; + +struct sst_compress_cb { + void *param; + void (*compr_cb)(void *param); + void *drain_cb_param; + void (*drain_notify)(void *param); + +}; + +struct snd_sst_params; + +struct compress_sst_ops { + const char *name; + int (*open) (struct snd_sst_params *str_params, + struct sst_compress_cb *cb); + int (*control) (unsigned int cmd, unsigned int str_id); + int (*tstamp) (unsigned int str_id, struct snd_compr_tstamp *tstamp); + int (*ack) (unsigned int str_id, unsigned long bytes); + int (*close) (unsigned int str_id); + int (*get_caps) (struct snd_compr_caps *caps); + int (*get_codec_caps) (struct snd_compr_codec_caps *codec); + int (*set_metadata) (unsigned int str_id, struct snd_compr_metadata *metadata); + +}; + +enum lpe_param_types_mixer { + SST_ALGO_PARAM_MIXER_STREAM_CFG = 0x801, +}; + +struct mad_ops_wq { + int stream_id; + enum sst_controls control_op; + struct work_struct wq; +}; + +struct sst_ops { + int (*open) (struct snd_sst_params *str_param); + int (*device_control) (int cmd, void *arg); + int (*set_generic_params) (enum sst_controls cmd, void *arg); + int (*close) (unsigned int str_id); + int (*power) (bool state); +}; + +struct sst_runtime_stream { + int stream_status; + unsigned int id; + size_t bytes_written; + struct pcm_stream_info stream_info; + struct sst_ops *ops; + struct compress_sst_ops *compr_ops; + spinlock_t status_lock; +}; + +struct sst_device { + char *name; + struct device *dev; + struct sst_ops *ops; + struct platform_device *pdev; + struct compress_sst_ops *compr_ops; +}; + +int sst_register_dsp(struct sst_device *sst); +int sst_unregister_dsp(struct sst_device *sst); +#endif diff --git a/sound/soc/intel/sst_platform_pvt.h b/sound/soc/intel/sst_platform_pvt.h new file mode 100644 index 000000000000..f74dd9ca61fb --- /dev/null +++ b/sound/soc/intel/sst_platform_pvt.h @@ -0,0 +1,127 @@ +/* + * sst_platform_pvt.h - Intel MID Platform driver header file + * + * Copyright (C) 2010 Intel Corp + * Author: Vinod Koul + * Author: Harsha Priya + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * + */ + +#ifndef __SST_PLATFORM_PVT_H__ +#define __SST_PLATFORM_PVT_H__ + +/* TODO rmv this global */ +extern struct sst_device *sst_dsp; + +#define SST_MONO 1 +#define SST_STEREO 2 + +#define SST_MIN_RATE 8000 +#define SST_MAX_RATE 48000 +#define SST_MIN_CHANNEL 1 +#define SST_MAX_CHANNEL 2 + +#define SST_MAX_BUFFER 96000 /*500ms@48K,16bit,2ch - CLV*/ +#define SST_MIN_PERIOD_BYTES 1536 /*24ms@16K,16bit,2ch - For VoIP on Mrfld*/ +#define SST_MAX_PERIOD_BYTES 48000 /*250ms@48K,16bit,2ch - CLV*/ + +#define SST_MIN_PERIODS 2 +#define SST_MAX_PERIODS 50 +#define SST_FIFO_SIZE 0 +#define SST_CODEC_TYPE_PCM 1 + +#define SST_HEADSET_DAI "Headset-cpu-dai" +#define SST_SPEAKER_DAI "Speaker-cpu-dai" +#define SST_VOICE_DAI "Voice-cpu-dai" +#define SST_VIRTUAL_DAI "Virtual-cpu-dai" +#define SST_LOOPBACK_DAI "Loopback-cpu-dai" +#define SST_POWER_DAI "Power-cpu-dai" +#define SST_COMPRESS_DAI "Compress-cpu-dai" +#define SST_PROBE_DAI "Probe-cpu-dai" +#define SST_VOIP_DAI "Voip-cpu-dai" +#define SST_DEEPBUFFER_DAI "Deepbuffer-cpu-dai" +#define SST_LOWLATENCY_DAI "Lowlatency-cpu-dai" + +struct sst_device; + +enum sst_drv_status { + SST_PLATFORM_UNINIT, + SST_PLATFORM_INIT, + SST_PLATFORM_RUNNING, + SST_PLATFORM_PAUSED, + SST_PLATFORM_DROPPED, +}; + +#define SST_PIPE_CONTROL 0x0 +#define SST_COMPRESS_VOL 0x01 + +int sst_platform_clv_init(struct snd_soc_platform *platform); +int sst_dsp_init(struct snd_soc_platform *platform); +int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform); +int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute); + +unsigned int sst_soc_read(struct snd_soc_platform *platform, unsigned int reg); +int sst_soc_write(struct snd_soc_platform *platform, unsigned int reg, unsigned int val); +unsigned int sst_reg_read(struct sst_data *sst, unsigned int reg, + unsigned int shift, unsigned int max); +unsigned int sst_reg_write(struct sst_data *sst, unsigned int reg, + unsigned int shift, unsigned int max, unsigned int val); + +int sst_algo_int_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +void sst_set_stream_status(struct sst_runtime_stream *stream, int state); +int sst_fill_stream_params(void *substream, const struct sst_data *ctx, + struct snd_sst_params *str_params, bool is_compress); +int sst_dpcm_probe_send(struct snd_soc_platform *platform, u16 probe_pipe, + int substream, int direction, bool on); +int sst_byte_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int sst_byte_control_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +struct sst_algo_int_control_v2 { + struct soc_mixer_control mc; + u16 module_id; /* module identifieer */ + u16 pipe_id; /* location info: pipe_id + instance_id */ + u16 instance_id; + unsigned int value; /* Value received is stored here */ +}; + +struct sst_lowlatency_deepbuff { + /* Thresholds for low latency & deep buffer */ + unsigned long *low_latency; + unsigned long *deep_buffer; + unsigned long period_time; +}; + +struct sst_data { + struct platform_device *pdev; + struct sst_platform_data *pdata; + unsigned int lpe_mixer_input_ihf; + unsigned int lpe_mixer_input_hs; + u32 *widget; + char *byte_stream; + struct mutex lock; + /* Pipe_id for probe_stream to be saved in stream map */ + u8 pipe_id; + bool vtsv_enroll; + struct sst_lowlatency_deepbuff ll_db; +}; +#endif diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 06a8000aa07b..7201146ad201 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -24,6 +24,7 @@ #include #include #include +#include static int soc_compr_open(struct snd_compr_stream *cstream) { @@ -75,6 +76,98 @@ static int soc_compr_open(struct snd_compr_stream *cstream) return ret; } +static int soc_compr_open_fe(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *fe = cstream->private_data; + struct snd_pcm_substream *fe_substream = fe->pcm->streams[0].substream; + struct snd_soc_platform *platform = fe->platform; + struct snd_soc_dai *cpu_dai = fe->cpu_dai; + struct snd_soc_dai *codec_dai = fe->codec_dai; + struct snd_soc_dpcm *dpcm; + struct snd_soc_dapm_widget_list *list; + int stream; + int ret = 0; + + if (cstream->direction == SND_COMPRESS_PLAYBACK) + stream = SNDRV_PCM_STREAM_PLAYBACK; + else + stream = SNDRV_PCM_STREAM_CAPTURE; + + mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + + if (platform->driver->compr_ops && platform->driver->compr_ops->open) { + ret = platform->driver->compr_ops->open(cstream); + if (ret < 0) { + pr_err("compress asoc: can't open platform %s\n", platform->name); + goto out; + } + } + + if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->startup) { + ret = fe->dai_link->compr_ops->startup(cstream); + if (ret < 0) { + pr_err("compress asoc: %s startup failed\n", fe->dai_link->name); + goto machine_err; + } + } + + fe->dpcm[stream].runtime = fe_substream->runtime; + + if (dpcm_path_get(fe, stream, &list) <= 0) { + dev_dbg(fe->dev, "ASoC: %s no valid %s route\n", + fe->dai_link->name, stream ? "capture" : "playback"); + } + + /* calculate valid and active FE <-> BE dpcms */ + dpcm_process_paths(fe, stream, &list, 1); + + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + + ret = dpcm_be_dai_startup(fe, stream); + if (ret < 0) { + /* clean up all links */ + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) + dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; + + dpcm_be_disconnect(fe, stream); + fe->dpcm[stream].runtime = NULL; + goto fe_err; + } + + dpcm_clear_pending_state(fe, stream); + dpcm_path_put(&list); + + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + + if (cstream->direction == SND_COMPRESS_PLAYBACK) { + cpu_dai->playback_active++; + codec_dai->playback_active++; + } else { + cpu_dai->capture_active++; + codec_dai->capture_active++; + } + + cpu_dai->active++; + codec_dai->active++; + fe->codec->active++; + + mutex_unlock(&fe->card->mutex); + + return 0; + +fe_err: + if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->shutdown) + fe->dai_link->compr_ops->shutdown(cstream); +machine_err: + if (platform->driver->compr_ops && platform->driver->compr_ops->free) + platform->driver->compr_ops->free(cstream); +out: + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + mutex_unlock(&fe->card->mutex); + return ret; +} + /* * Power down the audio subsystem pmdown_time msecs after close is called. * This is to ensure there are no pops or clicks in between any music tracks @@ -88,18 +181,17 @@ static void close_delayed_work(struct work_struct *work) mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - dev_dbg(rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n", - codec_dai->driver->playback.stream_name, - codec_dai->playback_active ? "active" : "inactive", - rtd->pop_wait ? "yes" : "no"); + dev_dbg (rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n", + codec_dai->driver->playback.stream_name, + codec_dai->playback_active ? "active" : "inactive", + rtd->pop_wait ? "yes" : "no"); /* are we waiting on this codec DAI stream */ - if (rtd->pop_wait == 1) { + if (rtd->pop_wait == 1 && !codec_dai->active) { rtd->pop_wait = 0; snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, SND_SOC_DAPM_STREAM_STOP); } - mutex_unlock(&rtd->pcm_mutex); } @@ -120,8 +212,8 @@ static int soc_compr_free(struct snd_compr_stream *cstream) cpu_dai->capture_active--; codec_dai->capture_active--; } - - snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction); + if (!codec_dai->playback_active) + snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction); cpu_dai->active--; codec_dai->active--; @@ -133,7 +225,6 @@ static int soc_compr_free(struct snd_compr_stream *cstream) if (!codec_dai->active) codec_dai->rate = 0; - if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->shutdown) rtd->dai_link->compr_ops->shutdown(cstream); @@ -141,7 +232,8 @@ static int soc_compr_free(struct snd_compr_stream *cstream) platform->driver->compr_ops->free(cstream); cpu_dai->runtime = NULL; - if (cstream->direction == SND_COMPRESS_PLAYBACK) { + if (cstream->direction == SND_COMPRESS_PLAYBACK + && !codec_dai->playback_active) { if (!rtd->pmdown_time || codec->ignore_pmdown_time || rtd->dai_link->ignore_pmdown_time) { snd_soc_dapm_stream_event(rtd, @@ -152,7 +244,8 @@ static int soc_compr_free(struct snd_compr_stream *cstream) schedule_delayed_work(&rtd->delayed_work, msecs_to_jiffies(rtd->pmdown_time)); } - } else { + } else if (cstream->direction == SND_COMPRESS_CAPTURE + && !codec_dai->capture_active) { /* capture streams can be powered down now */ snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE, @@ -163,15 +256,97 @@ static int soc_compr_free(struct snd_compr_stream *cstream) return 0; } +static int soc_compr_free_fe(struct snd_compr_stream *cstream) +{ + struct snd_soc_pcm_runtime *fe = cstream->private_data; + struct snd_soc_platform *platform = fe->platform; + struct snd_soc_dai *cpu_dai = fe->cpu_dai; + struct snd_soc_dai *codec_dai = fe->codec_dai; + struct snd_soc_dpcm *dpcm; + int stream, ret; + + mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + + if (cstream->direction == SND_COMPRESS_PLAYBACK) { + stream = SNDRV_PCM_STREAM_PLAYBACK; + cpu_dai->playback_active--; + codec_dai->playback_active--; + } else { + stream = SNDRV_PCM_STREAM_CAPTURE; + cpu_dai->capture_active--; + codec_dai->capture_active--; + } + + cpu_dai->active--; + codec_dai->active--; + fe->codec->active--; + + snd_soc_dai_digital_mute(cpu_dai, 1, stream); + + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + + ret = dpcm_be_dai_hw_free(fe, stream); + if (ret < 0) + dev_err(fe->dev, "compressed hw_free failed %d\n", ret); + + ret = dpcm_be_dai_shutdown(fe, stream); + + /* mark FE's links ready to prune */ + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) + dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); + else + dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP); + + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + + dpcm_be_disconnect(fe, stream); + + fe->dpcm[stream].runtime = NULL; + + if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->shutdown) + fe->dai_link->compr_ops->shutdown(cstream); + + if (platform->driver->compr_ops && platform->driver->compr_ops->free) + platform->driver->compr_ops->free(cstream); + + mutex_unlock(&fe->card->mutex); + return 0; +} + static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; - struct snd_soc_dai *codec_dai = rtd->codec_dai; int ret = 0; - mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) { + ret = platform->driver->compr_ops->trigger(cstream, cmd); + if (ret < 0) + goto out; + } + +out: + return ret; +} + +static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) +{ + struct snd_soc_pcm_runtime *fe = cstream->private_data; + struct snd_soc_platform *platform = fe->platform; + int ret = 0, stream; + + if (cstream->direction == SND_COMPRESS_PLAYBACK) + stream = SNDRV_PCM_STREAM_PLAYBACK; + else + stream = SNDRV_PCM_STREAM_CAPTURE; + + + mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) { ret = platform->driver->compr_ops->trigger(cstream, cmd); @@ -179,17 +354,28 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) goto out; } + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + + ret = dpcm_be_dai_trigger(fe, stream, cmd); + switch (cmd) { case SNDRV_PCM_TRIGGER_START: - snd_soc_dai_digital_mute(codec_dai, 0, cstream->direction); + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_START; break; case SNDRV_PCM_TRIGGER_STOP: - snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction); + case SNDRV_PCM_TRIGGER_SUSPEND: + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_STOP; + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PAUSED; break; } out: - mutex_unlock(&rtd->pcm_mutex); + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + mutex_unlock(&fe->card->mutex); return ret; } @@ -220,23 +406,88 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, goto err; } - if (cstream->direction == SND_COMPRESS_PLAYBACK) - snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, - SND_SOC_DAPM_STREAM_START); - else - snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE, + /* cancel any delayed stream shutdown that is pending */ + if (cstream->direction == SND_COMPRESS_PLAYBACK + && rtd->pop_wait) { + rtd->pop_wait = 0; + cancel_delayed_work(&rtd->delayed_work); + } + + snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, SND_SOC_DAPM_STREAM_START); - /* cancel any delayed stream shutdown that is pending */ - rtd->pop_wait = 0; + snd_soc_dai_digital_mute(rtd->codec_dai, 0, cstream->direction); +err: mutex_unlock(&rtd->pcm_mutex); + return ret; +} - cancel_delayed_work_sync(&rtd->delayed_work); +static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, + struct snd_compr_params *params) +{ + struct snd_soc_pcm_runtime *fe = cstream->private_data; + struct snd_pcm_substream *fe_substream = fe->pcm->streams[0].substream; + struct snd_soc_platform *platform = fe->platform; + struct snd_soc_dai *cpu_dai = fe->cpu_dai; + struct snd_soc_dai *codec_dai = fe->codec_dai; - return ret; + struct snd_pcm_hw_params *hw_params; + int ret = 0, stream; -err: - mutex_unlock(&rtd->pcm_mutex); + if (cstream->direction == SND_COMPRESS_PLAYBACK) + stream = SNDRV_PCM_STREAM_PLAYBACK; + else + stream = SNDRV_PCM_STREAM_CAPTURE; + + hw_params = kzalloc(sizeof(*hw_params), GFP_KERNEL); + if (hw_params == NULL) + return -ENOMEM; + + mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + + /* first we call set_params for the platform driver + * this should configure the soc side + * if the machine has compressed ops then we call that as well + * expectation is that platform and machine will configure everything + * for this compress path, like configuring pcm port for codec + */ + if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) { + ret = platform->driver->compr_ops->set_params(cstream, params); + if (ret < 0) + goto out; + } + + if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->set_params) { + ret = fe->dai_link->compr_ops->set_params(cstream); + if (ret < 0) + goto out; + } + + memcpy(&fe->dpcm[fe_substream->stream].hw_params, params, + sizeof(struct snd_pcm_hw_params)); + + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; + + ret = dpcm_be_dai_hw_params(fe, stream); + if (ret < 0) + goto out; + + ret = dpcm_be_dai_prepare(fe, stream); + if (ret < 0) + goto out; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); + else + dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START); + + fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE; + + snd_soc_dai_digital_mute(cpu_dai, 0, stream); + +out: + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO; + mutex_unlock(&fe->card->mutex); return ret; } @@ -359,6 +610,7 @@ static int sst_compr_get_metadata(struct snd_compr_stream *cstream, return ret; } + /* ASoC Compress operations */ static struct snd_compr_ops soc_compr_ops = { .open = soc_compr_open, @@ -374,6 +626,21 @@ static struct snd_compr_ops soc_compr_ops = { .get_codec_caps = soc_compr_get_codec_caps }; +/* ASoC Dynamic Compress operations */ +static struct snd_compr_ops soc_compr_dyn_ops = { + .open = soc_compr_open_fe, + .free = soc_compr_free_fe, + .set_params = soc_compr_set_params_fe, + .get_params = soc_compr_get_params, + .set_metadata = sst_compr_set_metadata, + .get_metadata = sst_compr_get_metadata, + .trigger = soc_compr_trigger_fe, + .pointer = soc_compr_pointer, + .ack = soc_compr_ack, + .get_caps = soc_compr_get_caps, + .get_codec_caps = soc_compr_get_codec_caps +}; + /* create a new compress */ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) { @@ -382,6 +649,7 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_compr *compr; + struct snd_pcm *be_pcm; char new_name[64]; int ret = 0, direction = 0; @@ -409,7 +677,26 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) ret = -ENOMEM; goto compr_err; } - memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops)); + + if (rtd->dai_link->dynamic) { + snprintf(new_name, sizeof(new_name), "(%s)", + rtd->dai_link->stream_name); + + ret = snd_pcm_new_internal(rtd->card->snd_card, new_name, num, + 1, 0, &be_pcm); + if (ret < 0) { + dev_err(rtd->card->dev, "ASoC: can't create compressed for %s\n", + rtd->dai_link->name); + goto compr_err; + } + + rtd->pcm = be_pcm; + rtd->fe_compr = 1; + be_pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd; + /*be_pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd;*/ + memcpy(compr->ops, &soc_compr_dyn_ops, sizeof(soc_compr_dyn_ops)); + } else + memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops)); /* Add copy callback for not memory mapped DSPs */ if (platform->driver->compr_ops && platform->driver->compr_ops->copy) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index d56bbea6e75e..717e6e971884 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -530,6 +530,15 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec) } #endif +static void codec2codec_close_delayed_work(struct work_struct *work) +{ + /* Currently nothing to do for c2c links + * Since c2c links are internal nodes in the DAPM graph and + * don't interface with the outside world or application layer + * we don't have to do any special handling on close. + */ +} + #ifdef CONFIG_PM_SLEEP /* powers down audio subsystem for suspend */ int snd_soc_suspend(struct device *dev) @@ -1163,6 +1172,11 @@ static int soc_probe_platform(struct snd_soc_card *card, if (dai->dev != platform->dev) continue; + /* dummy platform doesn't have and DAIs, don't add dummy-codec + * widgets here (since dev is the same) + */ + if (!strcmp(dai->name, "snd-soc-dummy-dai")) + continue; snd_soc_dapm_new_dai_widgets(&platform->dapm, dai); } @@ -1224,9 +1238,6 @@ static int soc_post_component_init(struct snd_soc_card *card, } rtd->card = card; - /* Make sure all DAPM widgets are instantiated */ - snd_soc_dapm_new_widgets(&codec->dapm); - /* machine controls, routes and widgets are not prefixed */ temp = codec->name_prefix; codec->name_prefix = NULL; @@ -1362,7 +1373,6 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) return -ENODEV; list_add(&cpu_dai->dapm.list, &card->dapm_list); - snd_soc_dapm_new_dai_widgets(&cpu_dai->dapm, cpu_dai); } if (cpu_dai->driver->probe) { @@ -1429,6 +1439,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) return ret; } } else { + INIT_DELAYED_WORK(&rtd->delayed_work, + codec2codec_close_delayed_work); + /* link the DAI widgets */ play_w = codec_dai->playback_widget; capture_w = cpu_dai->capture_widget; @@ -1717,8 +1730,6 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, card->num_dapm_routes); - snd_soc_dapm_new_widgets(&card->dapm); - for (i = 0; i < card->num_links; i++) { dai_link = &card->dai_link[i]; dai_fmt = dai_link->dai_fmt; @@ -3174,6 +3185,19 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_bytes_put); +int snd_soc_info_bytes_ext(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_bytes_ext *params = (void *)kcontrol->private_value; + + ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES; + ucontrol->count = params->max; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_info_bytes_ext); + /** * snd_soc_info_xr_sx - signed multi register info callback * @kcontrol: mreg control diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 360638362e98..86891a3e0748 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -172,6 +172,17 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL); } +/** + * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol + * @kcontrol: The kcontrol + */ +struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol) +{ + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + return wlist->widgets[0]->codec; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_codec); + /* get snd_card from DAPM context */ static inline struct snd_card *dapm_get_snd_card( struct snd_soc_dapm_context *dapm) @@ -3440,7 +3451,7 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) break; } - if (!w->sname) + if (!w->sname || !strstr(w->sname, dai_w->name)) continue; if (dai->driver->playback.stream_name && diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index 0bb5cccd7766..9fd4ed846289 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -65,6 +65,7 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) struct snd_soc_codec *codec; struct snd_soc_dapm_context *dapm; struct snd_soc_jack_pin *pin; + unsigned int sync = 0; int enable; trace_snd_soc_jack_report(jack, mask, status); @@ -92,12 +93,16 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) snd_soc_dapm_enable_pin(dapm, pin->pin); else snd_soc_dapm_disable_pin(dapm, pin->pin); + + /* we need to sync for this case only */ + sync = 1; } /* Report before the DAPM sync to help users updating micbias status */ blocking_notifier_call_chain(&jack->notifier, jack->status, jack); - snd_soc_dapm_sync(dapm); + if (sync) + snd_soc_dapm_sync(dapm); snd_jack_report(jack->jack, jack->status); @@ -318,10 +323,13 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, INIT_DELAYED_WORK(&gpios[i].work, gpio_work); gpios[i].jack = jack; + if (!gpios[i].irq_flags) + gpios[i].irq_flags = + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; + ret = request_any_context_irq(gpio_to_irq(gpios[i].gpio), gpio_handler, - IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING, + gpios[i].irq_flags, gpios[i].name, &gpios[i]); if (ret < 0) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index ccb6be4d658d..fc27d27850e4 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -34,7 +34,7 @@ #define DPCM_MAX_BE_USERS 8 /* DPCM stream event, send event to FE and all active BEs. */ -static int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, +int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, int event) { struct snd_soc_dpcm *dpcm; @@ -337,7 +337,7 @@ static void close_delayed_work(struct work_struct *work) rtd->pop_wait ? "yes" : "no"); /* are we waiting on this codec DAI stream */ - if (rtd->pop_wait == 1) { + if (rtd->pop_wait == 1 && !codec_dai->active) { rtd->pop_wait = 0; snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, SND_SOC_DAPM_STREAM_STOP); @@ -383,7 +383,10 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) /* Muting the DAC suppresses artifacts caused during digital * shutdown, for example from stopping clocks. */ - snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); + if (!codec_dai->playback_active) + snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); + + snd_soc_dai_digital_mute(cpu_dai, 1, substream->stream); if (cpu_dai->driver->ops->shutdown) cpu_dai->driver->ops->shutdown(substream, cpu_dai); @@ -398,7 +401,8 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) platform->driver->ops->close(substream); cpu_dai->runtime = NULL; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK + && !codec_dai->playback_active) { if (!rtd->pmdown_time || codec->ignore_pmdown_time || rtd->dai_link->ignore_pmdown_time) { /* powered down playback stream now */ @@ -411,14 +415,14 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) schedule_delayed_work(&rtd->delayed_work, msecs_to_jiffies(rtd->pmdown_time)); } - } else { + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE + && !codec_dai->capture_active) { /* capture streams can be powered down now */ snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_CAPTURE, SND_SOC_DAPM_STREAM_STOP); } mutex_unlock(&rtd->pcm_mutex); - pm_runtime_put(platform->dev); pm_runtime_put(codec_dai->dev); pm_runtime_put(cpu_dai->dev); @@ -488,6 +492,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) SND_SOC_DAPM_STREAM_START); snd_soc_dai_digital_mute(codec_dai, 0, substream->stream); + snd_soc_dai_digital_mute(cpu_dai, 0, substream->stream); out: mutex_unlock(&rtd->pcm_mutex); @@ -757,7 +762,7 @@ static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe, } /* disconnect a BE and FE */ -static void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream) +void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm, *d; @@ -853,7 +858,7 @@ static int widget_in_list(struct snd_soc_dapm_widget_list *list, return 0; } -static int dpcm_path_get(struct snd_soc_pcm_runtime *fe, +int dpcm_path_get(struct snd_soc_pcm_runtime *fe, int stream, struct snd_soc_dapm_widget_list **list_) { struct snd_soc_dai *cpu_dai = fe->cpu_dai; @@ -875,11 +880,6 @@ static int dpcm_path_get(struct snd_soc_pcm_runtime *fe, return paths; } -static inline void dpcm_path_put(struct snd_soc_dapm_widget_list **list) -{ - kfree(*list); -} - static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream, struct snd_soc_dapm_widget_list **list_) { @@ -949,7 +949,7 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, continue; /* don't connect if FE is not running */ - if (!fe->dpcm[stream].runtime) + if (!fe->dpcm[stream].runtime && !fe->fe_compr) continue; /* newly connected FE and BE */ @@ -974,7 +974,7 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, * Find the corresponding BE DAIs that source or sink audio to this * FE substream. */ -static int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, +int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, int stream, struct snd_soc_dapm_widget_list **list, int new) { if (new) @@ -983,7 +983,7 @@ static int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, return dpcm_prune_paths(fe, stream, list); } -static void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream) +void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; @@ -1021,7 +1021,7 @@ static void dpcm_be_dai_startup_unwind(struct snd_soc_pcm_runtime *fe, } } -static int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) +int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; int err, count = 0; @@ -1149,7 +1149,6 @@ static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream) } fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; - dpcm_set_fe_runtime(fe_substream); snd_pcm_limit_hw_rates(runtime); @@ -1163,7 +1162,7 @@ static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream) return ret; } -static int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream) +int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; @@ -1224,7 +1223,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream) return 0; } -static int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) +int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; @@ -1289,7 +1288,7 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream) return 0; } -static int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) +int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; int ret; @@ -1395,7 +1394,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream, if (ret < 0) { dev_err(fe->dev,"ASoC: hw_params FE failed %d\n", ret); dpcm_be_dai_hw_free(fe, stream); - } else + } else fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS; out: @@ -1419,7 +1418,7 @@ static int dpcm_do_trigger(struct snd_soc_dpcm *dpcm, return ret; } -static int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, +int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, int cmd) { struct snd_soc_dpcm *dpcm; @@ -1511,7 +1510,6 @@ static int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, return ret; } -EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger); static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd) { @@ -1587,7 +1585,7 @@ static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd) return ret; } -static int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) +int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_dpcm *dpcm; int ret = 0; @@ -2011,17 +2009,36 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) int ret = 0, playback = 0, capture = 0; if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) { - if (cpu_dai->driver->playback.channels_min) - playback = 1; - if (cpu_dai->driver->capture.channels_min) - capture = 1; + if (cpu_dai->driver->playback.channels_min) { + if (rtd->dai_link->playback_count) + playback = rtd->dai_link->playback_count; + else + playback = 1; + } + + if (cpu_dai->driver->capture.channels_min) { + if (rtd->dai_link->capture_count) + capture = rtd->dai_link->capture_count; + else + capture = 1; + } } else { if (codec_dai->driver->playback.channels_min && - cpu_dai->driver->playback.channels_min) - playback = 1; + cpu_dai->driver->playback.channels_min) { + if (rtd->dai_link->playback_count) + playback = rtd->dai_link->playback_count; + else + playback = 1; + } + if (codec_dai->driver->capture.channels_min && - cpu_dai->driver->capture.channels_min) - capture = 1; + cpu_dai->driver->capture.channels_min) { + if (rtd->dai_link->capture_count) + capture = rtd->dai_link->capture_count; + else + capture = 1; + } + } /* create the PCM */ diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index 4b3be6c3c91e..8ecf448c73a3 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -75,7 +75,11 @@ static const struct snd_pcm_hardware dummy_dma_hardware = { static int dummy_dma_open(struct snd_pcm_substream *substream) { - snd_soc_set_runtime_hwparams(substream, &dummy_dma_hardware); + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + /* BE's dont need dummy params */ + if (!rtd->dai_link->no_pcm) + snd_soc_set_runtime_hwparams(substream, &dummy_dma_hardware); return 0; } @@ -89,7 +93,28 @@ static struct snd_soc_platform_driver dummy_platform = { .ops = &dummy_dma_ops, }; -static struct snd_soc_codec_driver dummy_codec; +static struct snd_soc_dapm_widget dapm_widgets[] = { + SND_SOC_DAPM_INPUT("Dummy Input"), + SND_SOC_DAPM_OUTPUT("Dummy Output"), +}; + +static struct snd_soc_dapm_route intercon[] = { + { "Dummy Output", NULL, "Dummy Playback"}, + { "Dummy Capture", NULL, "Dummy Input"}, +}; + +static int dummy_codec_probe(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = &codec->dapm; + snd_soc_dapm_new_controls(dapm, dapm_widgets, + ARRAY_SIZE(dapm_widgets)); + snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon)); + return 0; +} + +static struct snd_soc_codec_driver dummy_codec = { + .probe = dummy_codec_probe, +}; #define STUB_RATES SNDRV_PCM_RATE_8000_192000 #define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ @@ -101,17 +126,18 @@ static struct snd_soc_codec_driver dummy_codec; SNDRV_PCM_FMTBIT_S32_LE | \ SNDRV_PCM_FMTBIT_U32_LE | \ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) + static struct snd_soc_dai_driver dummy_dai = { .name = "snd-soc-dummy-dai", .playback = { - .stream_name = "Playback", + .stream_name = "Dummy Playback", .channels_min = 1, .channels_max = 384, .rates = STUB_RATES, .formats = STUB_FORMATS, }, .capture = { - .stream_name = "Capture", + .stream_name = "Dummy Capture", .channels_min = 1, .channels_max = 384, .rates = STUB_RATES, diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c index cd69a80b5ca9..0ce903375eaf 100644 --- a/sound/usb/usx2y/usbusx2yaudio.c +++ b/sound/usb/usx2y/usbusx2yaudio.c @@ -299,6 +299,19 @@ static void usX2Y_error_urb_status(struct usX2Ydev *usX2Y, usX2Y_clients_stop(usX2Y); } +static void usX2Y_error_sequence(struct usX2Ydev *usX2Y, + struct snd_usX2Y_substream *subs, struct urb *urb) +{ + snd_printk(KERN_ERR +"Sequence Error!(hcd_frame=%i ep=%i%s;wait=%i,frame=%i).\n" +"Most probably some urb of usb-frame %i is still missing.\n" +"Cause could be too long delays in usb-hcd interrupt handling.\n", + usb_get_current_frame_number(usX2Y->dev), + subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out", + usX2Y->wait_iso_frame, urb->start_frame, usX2Y->wait_iso_frame); + usX2Y_clients_stop(usX2Y); +} + static void i_usX2Y_urb_complete(struct urb *urb) { struct snd_usX2Y_substream *subs = urb->context; @@ -315,9 +328,12 @@ static void i_usX2Y_urb_complete(struct urb *urb) usX2Y_error_urb_status(usX2Y, subs, urb); return; } - - subs->completed_urb = urb; - + if (likely((urb->start_frame & 0xFFFF) == (usX2Y->wait_iso_frame & 0xFFFF))) + subs->completed_urb = urb; + else { + usX2Y_error_sequence(usX2Y, subs, urb); + return; + } { struct snd_usX2Y_substream *capsubs = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE], *playbacksubs = usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK]; diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c index 814d0e887c62..f2a1acdc4d83 100644 --- a/sound/usb/usx2y/usx2yhwdeppcm.c +++ b/sound/usb/usx2y/usx2yhwdeppcm.c @@ -244,8 +244,13 @@ static void i_usX2Y_usbpcm_urb_complete(struct urb *urb) usX2Y_error_urb_status(usX2Y, subs, urb); return; } + if (likely((urb->start_frame & 0xFFFF) == (usX2Y->wait_iso_frame & 0xFFFF))) + subs->completed_urb = urb; + else { + usX2Y_error_sequence(usX2Y, subs, urb); + return; + } - subs->completed_urb = urb; capsubs = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE]; capsubs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2]; playbacksubs = usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK]; diff --git a/tools/lib/lk/debugfs.c b/tools/lib/lk/debugfs.c index 7c4347962353..099e7cd022e4 100644 --- a/tools/lib/lk/debugfs.c +++ b/tools/lib/lk/debugfs.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "debugfs.h" diff --git a/tools/perf/Makefile b/tools/perf/Makefile index ecdd885d4f84..b0f164b133d9 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -691,10 +691,9 @@ ifndef NO_SLANG ifneq ($(call try-cc,$(SOURCE_SLANG),$(FLAGS_SLANG),libslang),y) msg := $(warning slang not found, disables TUI support. Please install slang-devel or libslang-dev); else - # Some releases like Fedora has /usr/include/slang/slang.h other than /usr/include/slang.h - SLANG_INC ?= -idirafter =/usr/include/slang + # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h + BASIC_CFLAGS += -I/usr/include/slang BASIC_CFLAGS += -DSLANG_SUPPORT - BASIC_CFLAGS += $(SLANG_INC) EXTLIBS += -lslang LIB_OBJS += $(OUTPUT)ui/browser.o LIB_OBJS += $(OUTPUT)ui/browsers/annotate.o @@ -743,7 +742,6 @@ else FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) ifneq ($(call try-cc,$(SOURCE_PERL_EMBED),$(FLAGS_PERL_EMBED),perl),y) - msg := $(warning libperl not found, disables Perl scripting support. Please install libperl-dev or perl-devel); BASIC_CFLAGS += -DNO_LIBPERL else ALL_LDFLAGS += $(PERL_EMBED_LDFLAGS) @@ -782,10 +780,10 @@ else PYTHON_CONFIG_SQ := $(call shell-sq,$(PYTHON_CONFIG)) - PYTHON_EMBED_LDOPTS := $(shell pkg-config --libs python 2>/dev/null) + PYTHON_EMBED_LDOPTS := $(shell $(PYTHON_CONFIG_SQ) --ldflags 2>/dev/null) PYTHON_EMBED_LDFLAGS := $(call strip-libs,$(PYTHON_EMBED_LDOPTS)) PYTHON_EMBED_LIBADD := $(call grep-libs,$(PYTHON_EMBED_LDOPTS)) - PYTHON_EMBED_CCOPTS := $(shell pkg-config --cflags python 2>/dev/null) + PYTHON_EMBED_CCOPTS := $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null) FLAGS_PYTHON_EMBED := $(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS) ifneq ($(call try-cc,$(SOURCE_PYTHON_EMBED),$(FLAGS_PYTHON_EMBED),python),y) @@ -1196,7 +1194,7 @@ install-bin: all install: install-bin try-install-man install-python_ext: - $(PYTHON_WORD) util/setup.py --quiet install --prefix='$(DESTDIR_SQ)/usr' + $(PYTHON_WORD) util/setup.py --quiet install --root='/$(DESTDIR_SQ)' # 'make install-doc' should call 'make -C Documentation install' $(INSTALL_DOC_TARGETS): diff --git a/tools/perf/bench/numa.c b/tools/perf/bench/numa.c index f5c08346c599..30d1c3225b46 100644 --- a/tools/perf/bench/numa.c +++ b/tools/perf/bench/numa.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -1111,7 +1110,7 @@ static void *worker_thread(void *__tdata) /* Check whether our max runtime timed out: */ if (g->p.nr_secs) { timersub(&stop, &start0, &diff); - if (diff.tv_sec >= (long int)g->p.nr_secs) { + if (diff.tv_sec >= g->p.nr_secs) { g->stop_work = true; break; } @@ -1158,7 +1157,7 @@ static void *worker_thread(void *__tdata) runtime_ns_max += diff.tv_usec * 1000; if (details >= 0) { - printf(" #%2d / %2d: %14.2lf nsecs/op [val: %016"PRIu64"x]\n", + printf(" #%2d / %2d: %14.2lf nsecs/op [val: %016lx]\n", process_nr, thread_nr, runtime_ns_max / bytes_done, val); } fflush(stdout); diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 2eddd71baa94..2da2a6ca22bf 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1663,29 +1663,28 @@ static int __cmd_record(int argc, const char **argv) return cmd_record(i, rec_argv, NULL); } -static const char default_sort_order[] = "avg, max, switch, runtime"; -static struct perf_sched sched = { - .tool = { - .sample = perf_sched__process_tracepoint_sample, - .comm = perf_event__process_comm, - .lost = perf_event__process_lost, - .fork = perf_event__process_fork, - .ordered_samples = true, - }, - .cmp_pid = LIST_HEAD_INIT(sched.cmp_pid), - .sort_list = LIST_HEAD_INIT(sched.sort_list), - .start_work_mutex = PTHREAD_MUTEX_INITIALIZER, - .work_done_wait_mutex = PTHREAD_MUTEX_INITIALIZER, - .curr_pid = { [0 ... MAX_CPUS - 1] = -1 }, - .sort_order = default_sort_order, - .replay_repeat = 10, - .profile_cpu = -1, - .next_shortname1 = 'A', - .next_shortname2 = '0', -}; - int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) { + const char default_sort_order[] = "avg, max, switch, runtime"; + struct perf_sched sched = { + .tool = { + .sample = perf_sched__process_tracepoint_sample, + .comm = perf_event__process_comm, + .lost = perf_event__process_lost, + .fork = perf_event__process_fork, + .ordered_samples = true, + }, + .cmp_pid = LIST_HEAD_INIT(sched.cmp_pid), + .sort_list = LIST_HEAD_INIT(sched.sort_list), + .start_work_mutex = PTHREAD_MUTEX_INITIALIZER, + .work_done_wait_mutex = PTHREAD_MUTEX_INITIALIZER, + .curr_pid = { [0 ... MAX_CPUS - 1] = -1 }, + .sort_order = default_sort_order, + .replay_repeat = 10, + .profile_cpu = -1, + .next_shortname1 = 'A', + .next_shortname2 = '0', + }; const struct option latency_options[] = { OPT_STRING('s', "sort", &sched.sort_order, "key[,key2...]", "sort by key(s): runtime, switch, avg, max"), diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 6128be7513d9..32bd102c32b6 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -3,8 +3,6 @@ #include -#include - #if defined(__i386__) #define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") #define cpu_relax() asm volatile("rep; nop" ::: "memory"); diff --git a/tools/perf/scripts/python/Perf-Trace-Util/Context.c b/tools/perf/scripts/python/Perf-Trace-Util/Context.c index 57d3aa36630c..315067b8f552 100644 --- a/tools/perf/scripts/python/Perf-Trace-Util/Context.c +++ b/tools/perf/scripts/python/Perf-Trace-Util/Context.c @@ -19,10 +19,6 @@ * */ -#ifdef __mips__ -#include -#endif - #include #include "../../../perf.h" #include "../../../util/trace-event.h" diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c index f045c2c7e59c..00218f503b2e 100644 --- a/tools/perf/tests/attr.c +++ b/tools/perf/tests/attr.c @@ -71,7 +71,7 @@ static int store_event(struct perf_event_attr *attr, pid_t pid, int cpu, char path[PATH_MAX]; snprintf(path, PATH_MAX, "%s/event-%d-%llu-%d", dir, - attr->type, (unsigned long long)attr->config, fd); + attr->type, attr->config, fd); file = fopen(path, "w+"); if (!file) { @@ -80,7 +80,7 @@ static int store_event(struct perf_event_attr *attr, pid_t pid, int cpu, } if (fprintf(file, "[event-%d-%llu-%d]\n", - attr->type, (unsigned long long)attr->config, fd) < 0) { + attr->type, attr->config, fd) < 0) { perror("test attr - failed to write event file"); fclose(file); return -1; @@ -96,10 +96,10 @@ static int store_event(struct perf_event_attr *attr, pid_t pid, int cpu, /* struct perf_event_attr */ WRITE_ASS(type, PRIu32); WRITE_ASS(size, PRIu32); - __WRITE_ASS(config, "llu", (unsigned long long)attr->config); - __WRITE_ASS(sample_period, "llu", (unsigned long long)attr->sample_period); - __WRITE_ASS(sample_type, "llu", (unsigned long long)attr->sample_type); - __WRITE_ASS(read_format, "llu", (unsigned long long)attr->read_format); + WRITE_ASS(config, "llu"); + WRITE_ASS(sample_period, "llu"); + WRITE_ASS(sample_type, "llu"); + WRITE_ASS(read_format, "llu"); WRITE_ASS(disabled, "d"); WRITE_ASS(inherit, "d"); WRITE_ASS(pinned, "d"); @@ -124,10 +124,10 @@ static int store_event(struct perf_event_attr *attr, pid_t pid, int cpu, WRITE_ASS(exclude_callchain_user, "d"); WRITE_ASS(wakeup_events, PRIu32); WRITE_ASS(bp_type, PRIu32); - __WRITE_ASS(config1, "llu", (unsigned long long)attr->config1); - __WRITE_ASS(config2, "llu", (unsigned long long)attr->config2); - __WRITE_ASS(branch_sample_type, "llu", (unsigned long long)attr->branch_sample_type); - __WRITE_ASS(sample_regs_user, "llu", (unsigned long long)attr->sample_regs_user); + WRITE_ASS(config1, "llu"); + WRITE_ASS(config2, "llu"); + WRITE_ASS(branch_sample_type, "llu"); + WRITE_ASS(sample_regs_user, "llu"); WRITE_ASS(sample_stack_user, PRIu32); fclose(file); diff --git a/tools/perf/tests/bp_signal.c b/tools/perf/tests/bp_signal.c index bf3a094c4a87..68daa289e94c 100644 --- a/tools/perf/tests/bp_signal.c +++ b/tools/perf/tests/bp_signal.c @@ -74,7 +74,7 @@ static int bp_event(void *fn, int setup_signal) fd = sys_perf_event_open(&pe, 0, -1, -1, 0); if (fd < 0) { - pr_debug("failed opening event %llx\n", (unsigned long long)pe.config); + pr_debug("failed opening event %llx\n", pe.config); return TEST_FAIL; } diff --git a/tools/perf/tests/bp_signal_overflow.c b/tools/perf/tests/bp_signal_overflow.c index 3662b15ab2bd..fe7ed28815f8 100644 --- a/tools/perf/tests/bp_signal_overflow.c +++ b/tools/perf/tests/bp_signal_overflow.c @@ -87,7 +87,7 @@ int test__bp_signal_overflow(void) fd = sys_perf_event_open(&pe, 0, -1, -1, 0); if (fd < 0) { - pr_debug("failed opening event %llx\n", (unsigned long long)pe.config); + pr_debug("failed opening event %llx\n", pe.config); return TEST_FAIL; } diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index c0ec9f53728d..d102716c43a1 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -898,7 +898,7 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) snprintf(command, sizeof(command), "%s %s%s --start-address=0x%016" PRIx64 " --stop-address=0x%016" PRIx64 - " -d %s %s -C %s|grep -v %s|sed 's/\t/ /g'", + " -d %s %s -C %s|grep -v %s|expand", objdump_path ? objdump_path : "objdump", disassembler_style ? "-M " : "", disassembler_style ? disassembler_style : "", diff --git a/tools/perf/util/include/linux/compiler.h b/tools/perf/util/include/linux/compiler.h index d0c6cd6a3fad..96b919dae11c 100644 --- a/tools/perf/util/include/linux/compiler.h +++ b/tools/perf/util/include/linux/compiler.h @@ -4,7 +4,6 @@ #ifndef __always_inline #define __always_inline inline #endif -#undef __user #define __user #ifndef __attribute_const__ #define __attribute_const__ diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 8bcdf9e54089..6fcb9de62340 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -21,7 +21,6 @@ const char *map_type__name[MAP__NR_TYPES] = { static inline int is_anon_memory(const char *filename) { return !strcmp(filename, "//anon") || - !strcmp(filename, "/dev/zero (deleted)") || !strcmp(filename, "/anon_hugepage (deleted)"); } diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 0aa746fb33f2..e87aa5d9696b 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -19,10 +19,6 @@ * */ -#ifdef __mips__ -#include -#endif - #include #include diff --git a/usr/Makefile b/usr/Makefile index ddb506bf5cff..029ffe6cd0d8 100644 --- a/usr/Makefile +++ b/usr/Makefile @@ -35,7 +35,7 @@ $(obj)/initramfs_data.o: $(obj)/initramfs_data.cpio$(suffix_y) FORCE # Generate the initramfs cpio archive hostprogs-y := gen_init_cpio -initramfs := $(INITRAMFS_WRAPPER) $(CONFIG_SHELL) $(srctree)/scripts/gen_initramfs_list.sh +initramfs := $(CONFIG_SHELL) $(srctree)/scripts/gen_initramfs_list.sh ramfs-input := $(if $(filter-out "",$(CONFIG_INITRAMFS_SOURCE)), \ $(shell echo $(CONFIG_INITRAMFS_SOURCE)),-d) ramfs-args := \