aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Takashi Iwai <tiwai@suse.de> 2014-05-27 17:38:08 +0200
committerGravatar Takashi Iwai <tiwai@suse.de> 2014-05-27 17:38:08 +0200
commita58bdba749b36069ec372da9c9fd16017b6c0b47 (patch)
tree95c79448427425d1c05712a7c7fb98ed42e41539
parentALSA: sound/aoa/codecs/onyx.c: use static const for texts (diff)
parentALSA: bebob: Improve comments about stream format (diff)
downloadlinux-a58bdba749b36069ec372da9c9fd16017b6c0b47.tar.gz
linux-a58bdba749b36069ec372da9c9fd16017b6c0b47.tar.bz2
linux-a58bdba749b36069ec372da9c9fd16017b6c0b47.zip
Merge branch 'topic/firewire' into for-next
This is a merge of big firewire audio stack updates by Takashi Sakamoto.
-rw-r--r--Documentation/ABI/testing/sysfs-bus-pci2
-rw-r--r--Documentation/devicetree/bindings/clock/at91-clock.txt2
-rw-r--r--Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt2
-rw-r--r--Documentation/devicetree/bindings/dma/ti-edma.txt4
-rw-r--r--Documentation/kernel-parameters.txt8
-rw-r--r--MAINTAINERS24
-rw-r--r--Makefile2
-rw-r--r--arch/arm/boot/dts/am33xx.dtsi2
-rw-r--r--arch/arm/boot/dts/am3517.dtsi16
-rw-r--r--arch/arm/boot/dts/am437x-gp-evm.dts5
-rw-r--r--arch/arm/boot/dts/armada-370-db.dts1
-rw-r--r--arch/arm/boot/dts/armada-375-db.dts5
-rw-r--r--arch/arm/boot/dts/armada-xp-db.dts2
-rw-r--r--arch/arm/boot/dts/armada-xp-gp.dts10
-rw-r--r--arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts2
-rw-r--r--arch/arm/boot/dts/at91-sama5d3_xplained.dts4
-rw-r--r--arch/arm/boot/dts/at91sam9261.dtsi2
-rw-r--r--arch/arm/boot/dts/at91sam9rl.dtsi2
-rw-r--r--arch/arm/boot/dts/imx53-mba53.dts2
-rw-r--r--arch/arm/boot/dts/imx53.dtsi2
-rw-r--r--arch/arm/boot/dts/kirkwood-mv88f6281gtw-ge.dts18
-rw-r--r--arch/arm/boot/dts/kirkwood-nsa310-common.dtsi18
-rw-r--r--arch/arm/boot/dts/kirkwood-t5325.dts5
-rw-r--r--arch/arm/boot/dts/omap-gpmc-smsc911x.dtsi19
-rw-r--r--arch/arm/boot/dts/omap2.dtsi7
-rw-r--r--arch/arm/boot/dts/omap2420.dtsi8
-rw-r--r--arch/arm/boot/dts/omap2430.dtsi7
-rw-r--r--arch/arm/boot/dts/omap3-cm-t3x30.dtsi66
-rw-r--r--arch/arm/boot/dts/omap3-igep.dtsi2
-rw-r--r--arch/arm/boot/dts/omap3-igep0020.dts4
-rw-r--r--arch/arm/boot/dts/omap3-sb-t35.dtsi37
-rw-r--r--arch/arm/boot/dts/omap3-sbc-t3517.dts13
-rw-r--r--arch/arm/boot/dts/omap3.dtsi2
-rw-r--r--arch/arm/boot/dts/omap5.dtsi7
-rw-r--r--arch/arm/boot/dts/sama5d3.dtsi2
-rw-r--r--arch/arm/boot/dts/sama5d3_mci2.dtsi2
-rw-r--r--arch/arm/boot/dts/sama5d3_tcb1.dtsi2
-rw-r--r--arch/arm/boot/dts/sama5d3_uart.dtsi2
-rw-r--r--arch/arm/boot/dts/ste-ccu8540.dts1
-rw-r--r--arch/arm/boot/dts/sun7i-a20.dtsi14
-rw-r--r--arch/arm/common/edma.c48
-rw-r--r--arch/arm/configs/sunxi_defconfig2
-rw-r--r--arch/arm/include/asm/xen/page.h1
-rw-r--r--arch/arm/mach-omap2/omap-headsmp.S8
-rw-r--r--arch/arm/mach-orion5x/common.h2
-rw-r--r--arch/arm64/include/asm/memory.h1
-rw-r--r--arch/arm64/kernel/irq.c10
-rw-r--r--arch/arm64/mm/hugetlbpage.c4
-rw-r--r--arch/ia64/include/asm/unistd.h2
-rw-r--r--arch/ia64/include/uapi/asm/unistd.h1
-rw-r--r--arch/ia64/kernel/entry.S1
-rw-r--r--arch/m68k/include/asm/unistd.h2
-rw-r--r--arch/m68k/include/uapi/asm/unistd.h1
-rw-r--r--arch/m68k/kernel/syscalltable.S1
-rw-r--r--arch/metag/include/asm/barrier.h3
-rw-r--r--arch/metag/include/asm/processor.h2
-rw-r--r--arch/metag/include/uapi/asm/Kbuild2
-rw-r--r--arch/metag/include/uapi/asm/resource.h7
-rw-r--r--arch/mips/dec/ecc-berr.c1
-rw-r--r--arch/mips/dec/kn02xa-berr.c1
-rw-r--r--arch/mips/dec/prom/Makefile1
-rw-r--r--arch/mips/dec/prom/call_o32.S89
-rw-r--r--arch/mips/fw/lib/call_o32.S57
-rw-r--r--arch/mips/fw/sni/sniprom.c3
-rw-r--r--arch/mips/include/asm/dec/prom.h48
-rw-r--r--arch/mips/include/asm/rm9k-ocd.h56
-rw-r--r--arch/mips/include/asm/syscall.h2
-rw-r--r--arch/mips/include/uapi/asm/inst.h398
-rw-r--r--arch/mips/include/uapi/asm/unistd.h9
-rw-r--r--arch/mips/kernel/proc.c9
-rw-r--r--arch/mips/kernel/scall32-o32.S1
-rw-r--r--arch/mips/kernel/scall64-64.S1
-rw-r--r--arch/mips/kernel/scall64-n32.S1
-rw-r--r--arch/mips/kernel/scall64-o32.S1
-rw-r--r--arch/mips/lantiq/dts/easy50712.dts1
-rw-r--r--arch/mips/lib/csum_partial.S9
-rw-r--r--arch/mips/lib/delay.c14
-rw-r--r--arch/mips/lib/strncpy_user.S13
-rw-r--r--arch/mips/loongson/Kconfig1
-rw-r--r--arch/mips/loongson/lemote-2f/clock.c5
-rw-r--r--arch/mips/mm/tlb-funcs.S4
-rw-r--r--arch/mips/mm/tlbex.c7
-rw-r--r--arch/mips/ralink/dts/mt7620a_eval.dts1
-rw-r--r--arch/mips/ralink/dts/rt2880_eval.dts1
-rw-r--r--arch/mips/ralink/dts/rt3052_eval.dts1
-rw-r--r--arch/mips/ralink/dts/rt3883_eval.dts1
-rw-r--r--arch/parisc/Kconfig1
-rw-r--r--arch/parisc/include/asm/processor.h5
-rw-r--r--arch/parisc/include/uapi/asm/unistd.h3
-rw-r--r--arch/parisc/kernel/sys_parisc.c6
-rw-r--r--arch/parisc/kernel/syscall.S12
-rw-r--r--arch/parisc/kernel/syscall_table.S1
-rw-r--r--arch/parisc/kernel/traps.c54
-rw-r--r--arch/parisc/mm/fault.c44
-rw-r--r--arch/powerpc/kernel/time.c3
-rw-r--r--arch/powerpc/platforms/powernv/eeh-ioda.c3
-rw-r--r--arch/s390/crypto/aes_s390.c3
-rw-r--r--arch/s390/crypto/des_s390.c3
-rw-r--r--arch/x86/include/asm/hugetlb.h1
-rw-r--r--arch/x86/kernel/cpu/rdrand.c1
-rw-r--r--arch/x86/kernel/ldt.c4
-rw-r--r--arch/x86/vdso/vdso32-setup.c8
-rw-r--r--block/blk-cgroup.c15
-rw-r--r--drivers/Makefile2
-rw-r--r--drivers/acpi/Kconfig17
-rw-r--r--drivers/acpi/Makefile1
-rw-r--r--drivers/acpi/ac.c117
-rw-r--r--drivers/acpi/acpi_platform.c1
-rw-r--r--drivers/acpi/acpi_processor.c1
-rw-r--r--drivers/acpi/acpica/acglobal.h4
-rw-r--r--drivers/acpi/acpica/tbutils.c7
-rw-r--r--drivers/acpi/battery.c329
-rw-r--r--drivers/acpi/blacklist.c21
-rw-r--r--drivers/acpi/cm_sbs.c105
-rw-r--r--drivers/acpi/video.c16
-rw-r--r--drivers/ata/Kconfig2
-rw-r--r--drivers/ata/ahci.c15
-rw-r--r--drivers/ata/ahci.h1
-rw-r--r--drivers/ata/ahci_imx.c179
-rw-r--r--drivers/ata/libahci.c7
-rw-r--r--drivers/ata/libata-core.c9
-rw-r--r--drivers/bus/mvebu-mbus.c22
-rw-r--r--drivers/char/random.c7
-rw-r--r--drivers/char/tpm/tpm_ppi.c8
-rw-r--r--drivers/clk/bcm/clk-kona-setup.c33
-rw-r--r--drivers/clk/bcm/clk-kona.c64
-rw-r--r--drivers/clk/bcm/clk-kona.h28
-rw-r--r--drivers/clk/clk-divider.c37
-rw-r--r--drivers/clk/clk.c74
-rw-r--r--drivers/clk/shmobile/clk-mstp.c9
-rw-r--r--drivers/clk/socfpga/clk-pll.c7
-rw-r--r--drivers/clk/socfpga/clk.c23
-rw-r--r--drivers/clk/tegra/clk-pll.c2
-rw-r--r--drivers/cpufreq/intel_pstate.c34
-rw-r--r--drivers/cpufreq/loongson2_cpufreq.c4
-rw-r--r--drivers/crypto/caam/error.c10
-rw-r--r--drivers/firewire/core.h1
-rw-r--r--drivers/firmware/iscsi_ibft.c1
-rw-r--r--drivers/gpio/gpio-ich.c4
-rw-r--r--drivers/gpio/gpio-mcp23s08.c12
-rw-r--r--drivers/gpu/drm/i915/intel_bios.c52
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c55
-rw-r--r--drivers/gpu/drm/i915/intel_fbdev.c9
-rw-r--r--drivers/gpu/drm/i915/intel_panel.c8
-rw-r--r--drivers/gpu/drm/i915/intel_pm.c40
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo.c4
-rw-r--r--drivers/gpu/drm/i915/intel_uncore.c2
-rw-r--r--drivers/hwmon/emc1403.c10
-rw-r--r--drivers/i2c/busses/i2c-designware-core.c3
-rw-r--r--drivers/i2c/busses/i2c-nomadik.c2
-rw-r--r--drivers/i2c/busses/i2c-qup.c2
-rw-r--r--drivers/i2c/busses/i2c-rcar.c9
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c2
-rw-r--r--drivers/infiniband/ulp/isert/ib_isert.c38
-rw-r--r--drivers/infiniband/ulp/isert/ib_isert.h2
-rw-r--r--drivers/iommu/amd_iommu.c2
-rw-r--r--drivers/iommu/amd_iommu_init.c2
-rw-r--r--drivers/iommu/amd_iommu_v2.c2
-rw-r--r--drivers/md/dm-crypt.c61
-rw-r--r--drivers/md/dm-mpath.c2
-rw-r--r--drivers/md/dm-thin.c23
-rw-r--r--drivers/md/md.c3
-rw-r--r--drivers/md/raid10.c13
-rw-r--r--drivers/media/i2c/ov7670.c2
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-core.c2
-rw-r--r--drivers/media/media-device.c1
-rw-r--r--drivers/media/platform/davinci/vpbe_display.c16
-rw-r--r--drivers/media/platform/davinci/vpfe_capture.c2
-rw-r--r--drivers/media/platform/davinci/vpif_capture.c34
-rw-r--r--drivers/media/platform/davinci/vpif_display.c35
-rw-r--r--drivers/media/platform/exynos4-is/fimc-core.c2
-rw-r--r--drivers/media/tuners/fc2580.c6
-rw-r--r--drivers/media/tuners/fc2580_priv.h1
-rw-r--r--drivers/media/usb/dvb-usb-v2/Makefile1
-rw-r--r--drivers/media/usb/dvb-usb-v2/rtl28xxu.c48
-rw-r--r--drivers/media/usb/gspca/sonixb.c2
-rw-r--r--drivers/media/v4l2-core/v4l2-compat-ioctl32.c12
-rw-r--r--drivers/memory/mvebu-devbus.c15
-rw-r--r--drivers/of/base.c4
-rw-r--r--drivers/pci/host/pci-mvebu.c92
-rw-r--r--drivers/pci/hotplug/shpchp_ctrl.c4
-rw-r--r--drivers/pci/pci.c5
-rw-r--r--drivers/rtc/rtc-hym8563.c3
-rw-r--r--drivers/sh/Makefile14
-rw-r--r--drivers/sh/pm_runtime.c20
-rw-r--r--drivers/spi/spi-pxa2xx-dma.c16
-rw-r--r--drivers/spi/spi-qup.c2
-rw-r--r--drivers/spi/spi.c124
-rw-r--r--drivers/staging/imx-drm/imx-drm-core.c7
-rw-r--r--drivers/staging/imx-drm/imx-tve.c2
-rw-r--r--drivers/staging/media/davinci_vpfe/vpfe_video.c13
-rw-r--r--drivers/staging/media/sn9c102/sn9c102_devtable.h2
-rw-r--r--drivers/staging/rtl8723au/os_dep/os_intfs.c2
-rw-r--r--drivers/staging/rtl8723au/os_dep/usb_ops_linux.c2
-rw-r--r--drivers/target/iscsi/iscsi_target.c4
-rw-r--r--drivers/target/iscsi/iscsi_target_core.h1
-rw-r--r--drivers/target/iscsi/iscsi_target_login.c28
-rw-r--r--drivers/target/iscsi/iscsi_target_tpg.c1
-rw-r--r--drivers/target/target_core_device.c12
-rw-r--r--drivers/target/target_core_transport.c2
-rw-r--r--drivers/target/tcm_fc/tfc_cmd.c8
-rw-r--r--drivers/xen/events/events_fifo.c41
-rw-r--r--fs/btrfs/ioctl.c6
-rw-r--r--fs/btrfs/send.c2
-rw-r--r--fs/cifs/inode.c3
-rw-r--r--fs/exec.c6
-rw-r--r--fs/kernfs/file.c17
-rw-r--r--fs/locks.c36
-rw-r--r--fs/nfsd/nfs4acl.c17
-rw-r--r--fs/nfsd/nfs4state.c25
-rw-r--r--fs/sysfs/file.c3
-rw-r--r--fs/sysfs/mount.c3
-rw-r--r--fs/xfs/xfs_export.c2
-rw-r--r--fs/xfs/xfs_file.c8
-rw-r--r--fs/xfs/xfs_iops.c12
-rw-r--r--fs/xfs/xfs_qm.c26
-rw-r--r--fs/xfs/xfs_super.c4
-rw-r--r--include/asm-generic/resource.h2
-rw-r--r--include/dt-bindings/clock/at91.h (renamed from include/dt-bindings/clk/at91.h)0
-rw-r--r--include/linux/cgroup.h15
-rw-r--r--include/linux/firewire.h3
-rw-r--r--include/linux/interrupt.h5
-rw-r--r--include/linux/kernfs.h19
-rw-r--r--include/linux/of.h2
-rw-r--r--include/uapi/asm-generic/resource.h7
-rw-r--r--include/uapi/asm-generic/unistd.h4
-rw-r--r--include/uapi/linux/audit.h12
-rw-r--r--include/uapi/sound/asound.h4
-rw-r--r--include/uapi/sound/firewire.h23
-rw-r--r--kernel/cgroup.c10
-rw-r--r--kernel/cgroup_freezer.c116
-rw-r--r--kernel/hrtimer.c8
-rw-r--r--kernel/workqueue.c36
-rw-r--r--mm/Kconfig15
-rw-r--r--mm/kmemleak.c4
-rw-r--r--mm/mremap.c9
-rw-r--r--mm/percpu.c2
-rw-r--r--net/ceph/messenger.c20
-rw-r--r--net/ceph/osdmap.c5
-rwxr-xr-xscripts/checksyscalls.sh5
-rw-r--r--security/device_cgroup.c202
-rw-r--r--sound/firewire/Kconfig59
-rw-r--r--sound/firewire/Makefile2
-rw-r--r--sound/firewire/amdtp.c797
-rw-r--r--sound/firewire/amdtp.h200
-rw-r--r--sound/firewire/bebob/Makefile4
-rw-r--r--sound/firewire/bebob/bebob.c471
-rw-r--r--sound/firewire/bebob/bebob.h257
-rw-r--r--sound/firewire/bebob/bebob_command.c282
-rw-r--r--sound/firewire/bebob/bebob_focusrite.c279
-rw-r--r--sound/firewire/bebob/bebob_hwdep.c199
-rw-r--r--sound/firewire/bebob/bebob_maudio.c792
-rw-r--r--sound/firewire/bebob/bebob_midi.c168
-rw-r--r--sound/firewire/bebob/bebob_pcm.c378
-rw-r--r--sound/firewire/bebob/bebob_proc.c196
-rw-r--r--sound/firewire/bebob/bebob_stream.c1021
-rw-r--r--sound/firewire/bebob/bebob_terratec.c68
-rw-r--r--sound/firewire/bebob/bebob_yamaha.c50
-rw-r--r--sound/firewire/cmp.c205
-rw-r--r--sound/firewire/cmp.h14
-rw-r--r--sound/firewire/dice.c88
-rw-r--r--sound/firewire/fcp.c189
-rw-r--r--sound/firewire/fcp.h21
-rw-r--r--sound/firewire/fireworks/Makefile4
-rw-r--r--sound/firewire/fireworks/fireworks.c357
-rw-r--r--sound/firewire/fireworks/fireworks.h233
-rw-r--r--sound/firewire/fireworks/fireworks_command.c372
-rw-r--r--sound/firewire/fireworks/fireworks_hwdep.c298
-rw-r--r--sound/firewire/fireworks/fireworks_midi.c168
-rw-r--r--sound/firewire/fireworks/fireworks_pcm.c403
-rw-r--r--sound/firewire/fireworks/fireworks_proc.c232
-rw-r--r--sound/firewire/fireworks/fireworks_stream.c372
-rw-r--r--sound/firewire/fireworks/fireworks_transaction.c326
-rw-r--r--sound/firewire/speakers.c100
-rw-r--r--sound/isa/sb/sb_mixer.c14
-rw-r--r--sound/pci/hda/hda_intel.c6
-rw-r--r--sound/pci/hda/patch_hdmi.c2
-rw-r--r--sound/pci/hda/patch_realtek.c3
278 files changed, 10913 insertions, 1899 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci
index a3c5a6685036..ab8d76dfaa80 100644
--- a/Documentation/ABI/testing/sysfs-bus-pci
+++ b/Documentation/ABI/testing/sysfs-bus-pci
@@ -117,7 +117,7 @@ Description:
What: /sys/bus/pci/devices/.../vpd
Date: February 2008
-Contact: Ben Hutchings <bhutchings@solarflare.com>
+Contact: Ben Hutchings <bwh@kernel.org>
Description:
A file named vpd in a device directory will be a
binary file containing the Vital Product Data for the
diff --git a/Documentation/devicetree/bindings/clock/at91-clock.txt b/Documentation/devicetree/bindings/clock/at91-clock.txt
index cd5e23912888..6794cdc96d8f 100644
--- a/Documentation/devicetree/bindings/clock/at91-clock.txt
+++ b/Documentation/devicetree/bindings/clock/at91-clock.txt
@@ -62,7 +62,7 @@ Required properties for PMC node:
- interrupt-controller : tell that the PMC is an interrupt controller.
- #interrupt-cells : must be set to 1. The first cell encodes the interrupt id,
and reflect the bit position in the PMC_ER/DR/SR registers.
- You can use the dt macros defined in dt-bindings/clk/at91.h.
+ You can use the dt macros defined in dt-bindings/clock/at91.h.
0 (AT91_PMC_MOSCS) -> main oscillator ready
1 (AT91_PMC_LOCKA) -> PLL A ready
2 (AT91_PMC_LOCKB) -> PLL B ready
diff --git a/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt
index 5992dceec7af..02a25d99ca61 100644
--- a/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt
+++ b/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt
@@ -43,7 +43,7 @@ Example
clock-output-names =
"tpu0", "mmcif1", "sdhi3", "sdhi2",
"sdhi1", "sdhi0", "mmcif0";
- renesas,clock-indices = <
+ clock-indices = <
R8A7790_CLK_TPU0 R8A7790_CLK_MMCIF1 R8A7790_CLK_SDHI3
R8A7790_CLK_SDHI2 R8A7790_CLK_SDHI1 R8A7790_CLK_SDHI0
R8A7790_CLK_MMCIF0
diff --git a/Documentation/devicetree/bindings/dma/ti-edma.txt b/Documentation/devicetree/bindings/dma/ti-edma.txt
index 9fbbdb783a72..68ff2137bae7 100644
--- a/Documentation/devicetree/bindings/dma/ti-edma.txt
+++ b/Documentation/devicetree/bindings/dma/ti-edma.txt
@@ -29,6 +29,6 @@ edma: edma@49000000 {
dma-channels = <64>;
ti,edma-regions = <4>;
ti,edma-slots = <256>;
- ti,edma-xbar-event-map = <1 12
- 2 13>;
+ ti,edma-xbar-event-map = /bits/ 16 <1 12
+ 2 13>;
};
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 43842177b771..30a8ad0dae53 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -2218,10 +2218,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
noreplace-smp [X86-32,SMP] Don't replace SMP instructions
with UP alternatives
- nordrand [X86] Disable the direct use of the RDRAND
- instruction even if it is supported by the
- processor. RDRAND is still available to user
- space applications.
+ nordrand [X86] Disable kernel use of the RDRAND and
+ RDSEED instructions even if they are supported
+ by the processor. RDRAND and RDSEED are still
+ available to user space applications.
noresume [SWSUSP] Disables resume and restores original swap
space.
diff --git a/MAINTAINERS b/MAINTAINERS
index 2c4cc2251693..8779dae65f63 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1893,14 +1893,15 @@ L: netdev@vger.kernel.org
S: Supported
F: drivers/net/ethernet/broadcom/bnx2x/
-BROADCOM BCM281XX/BCM11XXX ARM ARCHITECTURE
+BROADCOM BCM281XX/BCM11XXX/BCM216XX ARM ARCHITECTURE
M: Christian Daudt <bcm@fixthebug.org>
M: Matt Porter <mporter@linaro.org>
L: bcm-kernel-feedback-list@broadcom.com
-T: git git://git.github.com/broadcom/bcm11351
+T: git git://github.com/broadcom/mach-bcm
S: Maintained
F: arch/arm/mach-bcm/
F: arch/arm/boot/dts/bcm113*
+F: arch/arm/boot/dts/bcm216*
F: arch/arm/boot/dts/bcm281*
F: arch/arm/configs/bcm_defconfig
F: drivers/mmc/host/sdhci_bcm_kona.c
@@ -4812,6 +4813,14 @@ L: linux-kernel@vger.kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/core
F: kernel/irq/
+
+IRQCHIP DRIVERS
+M: Thomas Gleixner <tglx@linutronix.de>
+M: Jason Cooper <jason@lakedaemon.net>
+L: linux-kernel@vger.kernel.org
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/core
+T: git git://git.infradead.org/users/jcooper/linux.git irqchip/core
F: drivers/irqchip/
IRQ DOMAINS (IRQ NUMBER MAPPING LIBRARY)
@@ -5484,15 +5493,15 @@ F: Documentation/hwmon/ltc4261
F: drivers/hwmon/ltc4261.c
LTP (Linux Test Project)
-M: Shubham Goyal <shubham@linux.vnet.ibm.com>
M: Mike Frysinger <vapier@gentoo.org>
M: Cyril Hrubis <chrubis@suse.cz>
-M: Caspar Zhang <caspar@casparzhang.com>
M: Wanlong Gao <gaowanlong@cn.fujitsu.com>
+M: Jan Stancek <jstancek@redhat.com>
+M: Stanislav Kholmanskikh <stanislav.kholmanskikh@oracle.com>
+M: Alexey Kodanev <alexey.kodanev@oracle.com>
L: ltp-list@lists.sourceforge.net (subscribers-only)
-W: http://ltp.sourceforge.net/
+W: http://linux-test-project.github.io/
T: git git://github.com/linux-test-project/ltp.git
-T: git git://ltp.git.sourceforge.net/gitroot/ltp/ltp-dev
S: Maintained
M32R ARCHITECTURE
@@ -9102,6 +9111,9 @@ F: arch/um/os-Linux/drivers/
TURBOCHANNEL SUBSYSTEM
M: "Maciej W. Rozycki" <macro@linux-mips.org>
+M: Ralf Baechle <ralf@linux-mips.org>
+L: linux-mips@linux-mips.org
+Q: http://patchwork.linux-mips.org/project/linux-mips/list/
S: Maintained
F: drivers/tc/
F: include/linux/tc.h
diff --git a/Makefile b/Makefile
index 8a8440a3578e..9d993787afe0 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
VERSION = 3
PATCHLEVEL = 15
SUBLEVEL = 0
-EXTRAVERSION = -rc5
+EXTRAVERSION = -rc6
NAME = Shuffling Zombie Juror
# *DOCUMENTATION*
diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
index cb6811e5ae5a..7ad75b4e0663 100644
--- a/arch/arm/boot/dts/am33xx.dtsi
+++ b/arch/arm/boot/dts/am33xx.dtsi
@@ -144,7 +144,7 @@
compatible = "ti,edma3";
ti,hwmods = "tpcc", "tptc0", "tptc1", "tptc2";
reg = <0x49000000 0x10000>,
- <0x44e10f90 0x10>;
+ <0x44e10f90 0x40>;
interrupts = <12 13 14>;
#dma-cells = <1>;
dma-channels = <64>;
diff --git a/arch/arm/boot/dts/am3517.dtsi b/arch/arm/boot/dts/am3517.dtsi
index 788391f91684..5a452fdd7c5d 100644
--- a/arch/arm/boot/dts/am3517.dtsi
+++ b/arch/arm/boot/dts/am3517.dtsi
@@ -62,5 +62,21 @@
};
};
+&iva {
+ status = "disabled";
+};
+
+&mailbox {
+ status = "disabled";
+};
+
+&mmu_isp {
+ status = "disabled";
+};
+
+&smartreflex_mpu_iva {
+ status = "disabled";
+};
+
/include/ "am35xx-clocks.dtsi"
/include/ "omap36xx-am35xx-omap3430es2plus-clocks.dtsi"
diff --git a/arch/arm/boot/dts/am437x-gp-evm.dts b/arch/arm/boot/dts/am437x-gp-evm.dts
index df8798e8bd25..a055f7f0f14a 100644
--- a/arch/arm/boot/dts/am437x-gp-evm.dts
+++ b/arch/arm/boot/dts/am437x-gp-evm.dts
@@ -117,6 +117,11 @@
status = "okay";
};
+&gpio5 {
+ status = "okay";
+ ti,no-reset-on-init;
+};
+
&mmc1 {
status = "okay";
vmmc-supply = <&vmmcsd_fixed>;
diff --git a/arch/arm/boot/dts/armada-370-db.dts b/arch/arm/boot/dts/armada-370-db.dts
index 82f238a9063f..3383c4b66803 100644
--- a/arch/arm/boot/dts/armada-370-db.dts
+++ b/arch/arm/boot/dts/armada-370-db.dts
@@ -67,6 +67,7 @@
i2c@11000 {
pinctrl-0 = <&i2c0_pins>;
pinctrl-names = "default";
+ clock-frequency = <100000>;
status = "okay";
audio_codec: audio-codec@4a {
compatible = "cirrus,cs42l51";
diff --git a/arch/arm/boot/dts/armada-375-db.dts b/arch/arm/boot/dts/armada-375-db.dts
index 9378d3136b41..0451124e8ebf 100644
--- a/arch/arm/boot/dts/armada-375-db.dts
+++ b/arch/arm/boot/dts/armada-375-db.dts
@@ -79,6 +79,11 @@
};
};
+ sata@a0000 {
+ status = "okay";
+ nr-ports = <2>;
+ };
+
nand: nand@d0000 {
pinctrl-0 = <&nand_pins>;
pinctrl-names = "default";
diff --git a/arch/arm/boot/dts/armada-xp-db.dts b/arch/arm/boot/dts/armada-xp-db.dts
index 448373c4b0e5..90f0bf6f9271 100644
--- a/arch/arm/boot/dts/armada-xp-db.dts
+++ b/arch/arm/boot/dts/armada-xp-db.dts
@@ -49,7 +49,7 @@
/* Device Bus parameters are required */
/* Read parameters */
- devbus,bus-width = <8>;
+ devbus,bus-width = <16>;
devbus,turn-off-ps = <60000>;
devbus,badr-skew-ps = <0>;
devbus,acc-first-ps = <124000>;
diff --git a/arch/arm/boot/dts/armada-xp-gp.dts b/arch/arm/boot/dts/armada-xp-gp.dts
index 61bda687f782..0c756421ae6a 100644
--- a/arch/arm/boot/dts/armada-xp-gp.dts
+++ b/arch/arm/boot/dts/armada-xp-gp.dts
@@ -59,7 +59,7 @@
/* Device Bus parameters are required */
/* Read parameters */
- devbus,bus-width = <8>;
+ devbus,bus-width = <16>;
devbus,turn-off-ps = <60000>;
devbus,badr-skew-ps = <0>;
devbus,acc-first-ps = <124000>;
@@ -146,22 +146,22 @@
ethernet@70000 {
status = "okay";
phy = <&phy0>;
- phy-mode = "rgmii-id";
+ phy-mode = "qsgmii";
};
ethernet@74000 {
status = "okay";
phy = <&phy1>;
- phy-mode = "rgmii-id";
+ phy-mode = "qsgmii";
};
ethernet@30000 {
status = "okay";
phy = <&phy2>;
- phy-mode = "rgmii-id";
+ phy-mode = "qsgmii";
};
ethernet@34000 {
status = "okay";
phy = <&phy3>;
- phy-mode = "rgmii-id";
+ phy-mode = "qsgmii";
};
/* Front-side USB slot */
diff --git a/arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts b/arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts
index 985948ce67b3..5d42feb31049 100644
--- a/arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts
+++ b/arch/arm/boot/dts/armada-xp-openblocks-ax3-4.dts
@@ -39,7 +39,7 @@
/* Device Bus parameters are required */
/* Read parameters */
- devbus,bus-width = <8>;
+ devbus,bus-width = <16>;
devbus,turn-off-ps = <60000>;
devbus,badr-skew-ps = <0>;
devbus,acc-first-ps = <124000>;
diff --git a/arch/arm/boot/dts/at91-sama5d3_xplained.dts b/arch/arm/boot/dts/at91-sama5d3_xplained.dts
index ce1375595e5f..4537259ce529 100644
--- a/arch/arm/boot/dts/at91-sama5d3_xplained.dts
+++ b/arch/arm/boot/dts/at91-sama5d3_xplained.dts
@@ -34,7 +34,7 @@
};
spi0: spi@f0004000 {
- cs-gpios = <&pioD 13 0>;
+ cs-gpios = <&pioD 13 0>, <0>, <0>, <&pioD 16 0>;
status = "okay";
};
@@ -79,7 +79,7 @@
};
spi1: spi@f8008000 {
- cs-gpios = <&pioC 25 0>, <0>, <0>, <&pioD 16 0>;
+ cs-gpios = <&pioC 25 0>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/at91sam9261.dtsi b/arch/arm/boot/dts/at91sam9261.dtsi
index e21dda0e8986..3be973e9889a 100644
--- a/arch/arm/boot/dts/at91sam9261.dtsi
+++ b/arch/arm/boot/dts/at91sam9261.dtsi
@@ -10,7 +10,7 @@
#include <dt-bindings/pinctrl/at91.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/gpio/gpio.h>
-#include <dt-bindings/clk/at91.h>
+#include <dt-bindings/clock/at91.h>
/ {
model = "Atmel AT91SAM9261 family SoC";
diff --git a/arch/arm/boot/dts/at91sam9rl.dtsi b/arch/arm/boot/dts/at91sam9rl.dtsi
index 63e1784d272c..92a52faebef7 100644
--- a/arch/arm/boot/dts/at91sam9rl.dtsi
+++ b/arch/arm/boot/dts/at91sam9rl.dtsi
@@ -8,7 +8,7 @@
#include "skeleton.dtsi"
#include <dt-bindings/pinctrl/at91.h>
-#include <dt-bindings/clk/at91.h>
+#include <dt-bindings/clock/at91.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/gpio/gpio.h>
diff --git a/arch/arm/boot/dts/imx53-mba53.dts b/arch/arm/boot/dts/imx53-mba53.dts
index 7c8c12969892..a3431d784870 100644
--- a/arch/arm/boot/dts/imx53-mba53.dts
+++ b/arch/arm/boot/dts/imx53-mba53.dts
@@ -244,7 +244,7 @@
&tve {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_vga_sync_1>;
- i2c-ddc-bus = <&i2c3>;
+ ddc-i2c-bus = <&i2c3>;
fsl,tve-mode = "vga";
fsl,hsync-pin = <4>;
fsl,vsync-pin = <6>;
diff --git a/arch/arm/boot/dts/imx53.dtsi b/arch/arm/boot/dts/imx53.dtsi
index 9c2bff2252d0..6a1bf4ff83d5 100644
--- a/arch/arm/boot/dts/imx53.dtsi
+++ b/arch/arm/boot/dts/imx53.dtsi
@@ -115,7 +115,7 @@
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx53-ipu";
- reg = <0x18000000 0x080000000>;
+ reg = <0x18000000 0x08000000>;
interrupts = <11 10>;
clocks = <&clks IMX5_CLK_IPU_GATE>,
<&clks IMX5_CLK_IPU_DI0_GATE>,
diff --git a/arch/arm/boot/dts/kirkwood-mv88f6281gtw-ge.dts b/arch/arm/boot/dts/kirkwood-mv88f6281gtw-ge.dts
index 32c6fb4a1162..b939f4f52d16 100644
--- a/arch/arm/boot/dts/kirkwood-mv88f6281gtw-ge.dts
+++ b/arch/arm/boot/dts/kirkwood-mv88f6281gtw-ge.dts
@@ -30,6 +30,16 @@
bootargs = "console=ttyS0,115200n8 earlyprintk";
};
+ mbus {
+ pcie-controller {
+ status = "okay";
+
+ pcie@1,0 {
+ status = "okay";
+ };
+ };
+ };
+
ocp@f1000000 {
pinctrl@10000 {
pmx_usb_led: pmx-usb-led {
@@ -73,14 +83,6 @@
ehci@50000 {
status = "okay";
};
-
- pcie-controller {
- status = "okay";
-
- pcie@1,0 {
- status = "okay";
- };
- };
};
gpio-leds {
diff --git a/arch/arm/boot/dts/kirkwood-nsa310-common.dtsi b/arch/arm/boot/dts/kirkwood-nsa310-common.dtsi
index aa78c2d11fe7..e2cc85cc3b87 100644
--- a/arch/arm/boot/dts/kirkwood-nsa310-common.dtsi
+++ b/arch/arm/boot/dts/kirkwood-nsa310-common.dtsi
@@ -4,6 +4,16 @@
/ {
model = "ZyXEL NSA310";
+ mbus {
+ pcie-controller {
+ status = "okay";
+
+ pcie@1,0 {
+ status = "okay";
+ };
+ };
+ };
+
ocp@f1000000 {
pinctrl: pinctrl@10000 {
@@ -26,14 +36,6 @@
status = "okay";
nr-ports = <2>;
};
-
- pcie-controller {
- status = "okay";
-
- pcie@1,0 {
- status = "okay";
- };
- };
};
gpio_poweroff {
diff --git a/arch/arm/boot/dts/kirkwood-t5325.dts b/arch/arm/boot/dts/kirkwood-t5325.dts
index 7d1c7677a18f..0bd70d928c69 100644
--- a/arch/arm/boot/dts/kirkwood-t5325.dts
+++ b/arch/arm/boot/dts/kirkwood-t5325.dts
@@ -127,11 +127,6 @@
i2c@11000 {
status = "okay";
-
- alc5621: alc5621@1a {
- compatible = "realtek,alc5621";
- reg = <0x1a>;
- };
};
serial@12000 {
diff --git a/arch/arm/boot/dts/omap-gpmc-smsc911x.dtsi b/arch/arm/boot/dts/omap-gpmc-smsc911x.dtsi
index f577b7df9a29..521c587acaee 100644
--- a/arch/arm/boot/dts/omap-gpmc-smsc911x.dtsi
+++ b/arch/arm/boot/dts/omap-gpmc-smsc911x.dtsi
@@ -24,11 +24,10 @@
compatible = "smsc,lan9221", "smsc,lan9115";
bank-width = <2>;
gpmc,mux-add-data;
- gpmc,cs-on-ns = <0>;
- gpmc,cs-rd-off-ns = <186>;
- gpmc,cs-wr-off-ns = <186>;
- gpmc,adv-on-ns = <12>;
- gpmc,adv-rd-off-ns = <48>;
+ gpmc,cs-on-ns = <1>;
+ gpmc,cs-rd-off-ns = <180>;
+ gpmc,cs-wr-off-ns = <180>;
+ gpmc,adv-rd-off-ns = <18>;
gpmc,adv-wr-off-ns = <48>;
gpmc,oe-on-ns = <54>;
gpmc,oe-off-ns = <168>;
@@ -36,12 +35,10 @@
gpmc,we-off-ns = <168>;
gpmc,rd-cycle-ns = <186>;
gpmc,wr-cycle-ns = <186>;
- gpmc,access-ns = <114>;
- gpmc,page-burst-access-ns = <6>;
- gpmc,bus-turnaround-ns = <12>;
- gpmc,cycle2cycle-delay-ns = <18>;
- gpmc,wr-data-mux-bus-ns = <90>;
- gpmc,wr-access-ns = <186>;
+ gpmc,access-ns = <144>;
+ gpmc,page-burst-access-ns = <24>;
+ gpmc,bus-turnaround-ns = <90>;
+ gpmc,cycle2cycle-delay-ns = <90>;
gpmc,cycle2cycle-samecsen;
gpmc,cycle2cycle-diffcsen;
vddvario-supply = <&vddvario>;
diff --git a/arch/arm/boot/dts/omap2.dtsi b/arch/arm/boot/dts/omap2.dtsi
index 22f35ea142c1..8f8c07da4ac1 100644
--- a/arch/arm/boot/dts/omap2.dtsi
+++ b/arch/arm/boot/dts/omap2.dtsi
@@ -71,13 +71,6 @@
interrupts = <58>;
};
- mailbox: mailbox@48094000 {
- compatible = "ti,omap2-mailbox";
- ti,hwmods = "mailbox";
- reg = <0x48094000 0x200>;
- interrupts = <26>;
- };
-
intc: interrupt-controller@1 {
compatible = "ti,omap2-intc";
interrupt-controller;
diff --git a/arch/arm/boot/dts/omap2420.dtsi b/arch/arm/boot/dts/omap2420.dtsi
index 85b1fb014c43..2d9979835f24 100644
--- a/arch/arm/boot/dts/omap2420.dtsi
+++ b/arch/arm/boot/dts/omap2420.dtsi
@@ -125,6 +125,14 @@
dma-names = "tx", "rx";
};
+ mailbox: mailbox@48094000 {
+ compatible = "ti,omap2-mailbox";
+ reg = <0x48094000 0x200>;
+ interrupts = <26>, <34>;
+ interrupt-names = "dsp", "iva";
+ ti,hwmods = "mailbox";
+ };
+
timer1: timer@48028000 {
compatible = "ti,omap2420-timer";
reg = <0x48028000 0x400>;
diff --git a/arch/arm/boot/dts/omap2430.dtsi b/arch/arm/boot/dts/omap2430.dtsi
index d09697dab55e..42d2c61c9e2d 100644
--- a/arch/arm/boot/dts/omap2430.dtsi
+++ b/arch/arm/boot/dts/omap2430.dtsi
@@ -216,6 +216,13 @@
dma-names = "tx", "rx";
};
+ mailbox: mailbox@48094000 {
+ compatible = "ti,omap2-mailbox";
+ reg = <0x48094000 0x200>;
+ interrupts = <26>;
+ ti,hwmods = "mailbox";
+ };
+
timer1: timer@49018000 {
compatible = "ti,omap2420-timer";
reg = <0x49018000 0x400>;
diff --git a/arch/arm/boot/dts/omap3-cm-t3x30.dtsi b/arch/arm/boot/dts/omap3-cm-t3x30.dtsi
index d00055809e31..25ba08331d88 100644
--- a/arch/arm/boot/dts/omap3-cm-t3x30.dtsi
+++ b/arch/arm/boot/dts/omap3-cm-t3x30.dtsi
@@ -10,18 +10,6 @@
cpu0-supply = <&vcc>;
};
};
-
- vddvario: regulator-vddvario {
- compatible = "regulator-fixed";
- regulator-name = "vddvario";
- regulator-always-on;
- };
-
- vdd33a: regulator-vdd33a {
- compatible = "regulator-fixed";
- regulator-name = "vdd33a";
- regulator-always-on;
- };
};
&omap3_pmx_core {
@@ -35,58 +23,34 @@
hsusb0_pins: pinmux_hsusb0_pins {
pinctrl-single,pins = <
- OMAP3_CORE1_IOPAD(0x21a0, PIN_OUTPUT | MUX_MODE0) /* hsusb0_clk.hsusb0_clk */
- OMAP3_CORE1_IOPAD(0x21a2, PIN_OUTPUT | MUX_MODE0) /* hsusb0_stp.hsusb0_stp */
- OMAP3_CORE1_IOPAD(0x21a4, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_dir.hsusb0_dir */
- OMAP3_CORE1_IOPAD(0x21a6, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_nxt.hsusb0_nxt */
- OMAP3_CORE1_IOPAD(0x21a8, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data0.hsusb2_data0 */
- OMAP3_CORE1_IOPAD(0x21aa, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data1.hsusb0_data1 */
- OMAP3_CORE1_IOPAD(0x21ac, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data2.hsusb0_data2 */
- OMAP3_CORE1_IOPAD(0x21ae, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data3 */
- OMAP3_CORE1_IOPAD(0x21b0, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data4 */
- OMAP3_CORE1_IOPAD(0x21b2, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data5 */
- OMAP3_CORE1_IOPAD(0x21b4, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data6 */
- OMAP3_CORE1_IOPAD(0x21b6, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data7 */
+ OMAP3_CORE1_IOPAD(0x21a2, PIN_OUTPUT | MUX_MODE0) /* hsusb0_clk.hsusb0_clk */
+ OMAP3_CORE1_IOPAD(0x21a4, PIN_OUTPUT | MUX_MODE0) /* hsusb0_stp.hsusb0_stp */
+ OMAP3_CORE1_IOPAD(0x21a6, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_dir.hsusb0_dir */
+ OMAP3_CORE1_IOPAD(0x21a8, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_nxt.hsusb0_nxt */
+ OMAP3_CORE1_IOPAD(0x21aa, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data0.hsusb2_data0 */
+ OMAP3_CORE1_IOPAD(0x21ac, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data1.hsusb0_data1 */
+ OMAP3_CORE1_IOPAD(0x21ae, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data2.hsusb0_data2 */
+ OMAP3_CORE1_IOPAD(0x21b0, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data3 */
+ OMAP3_CORE1_IOPAD(0x21b2, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data4 */
+ OMAP3_CORE1_IOPAD(0x21b4, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data5 */
+ OMAP3_CORE1_IOPAD(0x21b6, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data6 */
+ OMAP3_CORE1_IOPAD(0x21b8, PIN_INPUT_PULLDOWN | MUX_MODE0) /* hsusb0_data7.hsusb0_data7 */
>;
};
};
+#include "omap-gpmc-smsc911x.dtsi"
+
&gpmc {
ranges = <5 0 0x2c000000 0x01000000>;
- smsc1: ethernet@5,0 {
+ smsc1: ethernet@gpmc {
compatible = "smsc,lan9221", "smsc,lan9115";
pinctrl-names = "default";
pinctrl-0 = <&smsc1_pins>;
interrupt-parent = <&gpio6>;
interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
reg = <5 0 0xff>;
- bank-width = <2>;
- gpmc,mux-add-data;
- gpmc,cs-on-ns = <0>;
- gpmc,cs-rd-off-ns = <186>;
- gpmc,cs-wr-off-ns = <186>;
- gpmc,adv-on-ns = <12>;
- gpmc,adv-rd-off-ns = <48>;
- gpmc,adv-wr-off-ns = <48>;
- gpmc,oe-on-ns = <54>;
- gpmc,oe-off-ns = <168>;
- gpmc,we-on-ns = <54>;
- gpmc,we-off-ns = <168>;
- gpmc,rd-cycle-ns = <186>;
- gpmc,wr-cycle-ns = <186>;
- gpmc,access-ns = <114>;
- gpmc,page-burst-access-ns = <6>;
- gpmc,bus-turnaround-ns = <12>;
- gpmc,cycle2cycle-delay-ns = <18>;
- gpmc,wr-data-mux-bus-ns = <90>;
- gpmc,wr-access-ns = <186>;
- gpmc,cycle2cycle-samecsen;
- gpmc,cycle2cycle-diffcsen;
- vddvario-supply = <&vddvario>;
- vdd33a-supply = <&vdd33a>;
- reg-io-width = <4>;
- smsc,save-mac-address;
};
};
diff --git a/arch/arm/boot/dts/omap3-igep.dtsi b/arch/arm/boot/dts/omap3-igep.dtsi
index b97736d98a64..e2d163bf0619 100644
--- a/arch/arm/boot/dts/omap3-igep.dtsi
+++ b/arch/arm/boot/dts/omap3-igep.dtsi
@@ -107,7 +107,7 @@
>;
};
- smsc911x_pins: pinmux_smsc911x_pins {
+ smsc9221_pins: pinmux_smsc9221_pins {
pinctrl-single,pins = <
0x1a2 (PIN_INPUT | MUX_MODE4) /* mcspi1_cs2.gpio_176 */
>;
diff --git a/arch/arm/boot/dts/omap3-igep0020.dts b/arch/arm/boot/dts/omap3-igep0020.dts
index 7abd64f6ae21..b22caaaf774b 100644
--- a/arch/arm/boot/dts/omap3-igep0020.dts
+++ b/arch/arm/boot/dts/omap3-igep0020.dts
@@ -10,7 +10,7 @@
*/
#include "omap3-igep.dtsi"
-#include "omap-gpmc-smsc911x.dtsi"
+#include "omap-gpmc-smsc9221.dtsi"
/ {
model = "IGEPv2 (TI OMAP AM/DM37x)";
@@ -248,7 +248,7 @@
ethernet@gpmc {
pinctrl-names = "default";
- pinctrl-0 = <&smsc911x_pins>;
+ pinctrl-0 = <&smsc9221_pins>;
reg = <5 0 0xff>;
interrupt-parent = <&gpio6>;
interrupts = <16 IRQ_TYPE_LEVEL_LOW>;
diff --git a/arch/arm/boot/dts/omap3-sb-t35.dtsi b/arch/arm/boot/dts/omap3-sb-t35.dtsi
index 7909c51b05a5..d59e3de1441e 100644
--- a/arch/arm/boot/dts/omap3-sb-t35.dtsi
+++ b/arch/arm/boot/dts/omap3-sb-t35.dtsi
@@ -2,20 +2,6 @@
* Common support for CompuLab SB-T35 used on SBC-T3530, SBC-T3517 and SBC-T3730
*/
-/ {
- vddvario_sb_t35: regulator-vddvario-sb-t35 {
- compatible = "regulator-fixed";
- regulator-name = "vddvario";
- regulator-always-on;
- };
-
- vdd33a_sb_t35: regulator-vdd33a-sb-t35 {
- compatible = "regulator-fixed";
- regulator-name = "vdd33a";
- regulator-always-on;
- };
-};
-
&omap3_pmx_core {
smsc2_pins: pinmux_smsc2_pins {
pinctrl-single,pins = <
@@ -37,11 +23,10 @@
reg = <4 0 0xff>;
bank-width = <2>;
gpmc,mux-add-data;
- gpmc,cs-on-ns = <0>;
- gpmc,cs-rd-off-ns = <186>;
- gpmc,cs-wr-off-ns = <186>;
- gpmc,adv-on-ns = <12>;
- gpmc,adv-rd-off-ns = <48>;
+ gpmc,cs-on-ns = <1>;
+ gpmc,cs-rd-off-ns = <180>;
+ gpmc,cs-wr-off-ns = <180>;
+ gpmc,adv-rd-off-ns = <18>;
gpmc,adv-wr-off-ns = <48>;
gpmc,oe-on-ns = <54>;
gpmc,oe-off-ns = <168>;
@@ -49,16 +34,14 @@
gpmc,we-off-ns = <168>;
gpmc,rd-cycle-ns = <186>;
gpmc,wr-cycle-ns = <186>;
- gpmc,access-ns = <114>;
- gpmc,page-burst-access-ns = <6>;
- gpmc,bus-turnaround-ns = <12>;
- gpmc,cycle2cycle-delay-ns = <18>;
- gpmc,wr-data-mux-bus-ns = <90>;
- gpmc,wr-access-ns = <186>;
+ gpmc,access-ns = <144>;
+ gpmc,page-burst-access-ns = <24>;
+ gpmc,bus-turnaround-ns = <90>;
+ gpmc,cycle2cycle-delay-ns = <90>;
gpmc,cycle2cycle-samecsen;
gpmc,cycle2cycle-diffcsen;
- vddvario-supply = <&vddvario_sb_t35>;
- vdd33a-supply = <&vdd33a_sb_t35>;
+ vddvario-supply = <&vddvario>;
+ vdd33a-supply = <&vdd33a>;
reg-io-width = <4>;
smsc,save-mac-address;
};
diff --git a/arch/arm/boot/dts/omap3-sbc-t3517.dts b/arch/arm/boot/dts/omap3-sbc-t3517.dts
index 024c9c6c682d..42189b65d393 100644
--- a/arch/arm/boot/dts/omap3-sbc-t3517.dts
+++ b/arch/arm/boot/dts/omap3-sbc-t3517.dts
@@ -8,6 +8,19 @@
/ {
model = "CompuLab SBC-T3517 with CM-T3517";
compatible = "compulab,omap3-sbc-t3517", "compulab,omap3-cm-t3517", "ti,am3517", "ti,omap3";
+
+ /* Only one GPMC smsc9220 on SBC-T3517, CM-T3517 uses am35x Ethernet */
+ vddvario: regulator-vddvario-sb-t35 {
+ compatible = "regulator-fixed";
+ regulator-name = "vddvario";
+ regulator-always-on;
+ };
+
+ vdd33a: regulator-vdd33a-sb-t35 {
+ compatible = "regulator-fixed";
+ regulator-name = "vdd33a";
+ regulator-always-on;
+ };
};
&omap3_pmx_core {
diff --git a/arch/arm/boot/dts/omap3.dtsi b/arch/arm/boot/dts/omap3.dtsi
index acb9019dc437..4231191ade06 100644
--- a/arch/arm/boot/dts/omap3.dtsi
+++ b/arch/arm/boot/dts/omap3.dtsi
@@ -61,7 +61,7 @@
ti,hwmods = "mpu";
};
- iva {
+ iva: iva {
compatible = "ti,iva2.2";
ti,hwmods = "iva";
diff --git a/arch/arm/boot/dts/omap5.dtsi b/arch/arm/boot/dts/omap5.dtsi
index f8c9855ce587..36b4312a5e0d 100644
--- a/arch/arm/boot/dts/omap5.dtsi
+++ b/arch/arm/boot/dts/omap5.dtsi
@@ -630,6 +630,13 @@
status = "disabled";
};
+ mailbox: mailbox@4a0f4000 {
+ compatible = "ti,omap4-mailbox";
+ reg = <0x4a0f4000 0x200>;
+ interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
+ ti,hwmods = "mailbox";
+ };
+
timer1: timer@4ae18000 {
compatible = "ti,omap5430-timer";
reg = <0x4ae18000 0x80>;
diff --git a/arch/arm/boot/dts/sama5d3.dtsi b/arch/arm/boot/dts/sama5d3.dtsi
index eabcfdbb403a..a106b0872910 100644
--- a/arch/arm/boot/dts/sama5d3.dtsi
+++ b/arch/arm/boot/dts/sama5d3.dtsi
@@ -13,7 +13,7 @@
#include <dt-bindings/pinctrl/at91.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/gpio/gpio.h>
-#include <dt-bindings/clk/at91.h>
+#include <dt-bindings/clock/at91.h>
/ {
model = "Atmel SAMA5D3 family SoC";
diff --git a/arch/arm/boot/dts/sama5d3_mci2.dtsi b/arch/arm/boot/dts/sama5d3_mci2.dtsi
index b029fe7ef17a..1b02208ea6ff 100644
--- a/arch/arm/boot/dts/sama5d3_mci2.dtsi
+++ b/arch/arm/boot/dts/sama5d3_mci2.dtsi
@@ -9,7 +9,7 @@
#include <dt-bindings/pinctrl/at91.h>
#include <dt-bindings/interrupt-controller/irq.h>
-#include <dt-bindings/clk/at91.h>
+#include <dt-bindings/clock/at91.h>
/ {
ahb {
diff --git a/arch/arm/boot/dts/sama5d3_tcb1.dtsi b/arch/arm/boot/dts/sama5d3_tcb1.dtsi
index 382b04431f66..02848453ca0c 100644
--- a/arch/arm/boot/dts/sama5d3_tcb1.dtsi
+++ b/arch/arm/boot/dts/sama5d3_tcb1.dtsi
@@ -9,7 +9,7 @@
#include <dt-bindings/pinctrl/at91.h>
#include <dt-bindings/interrupt-controller/irq.h>
-#include <dt-bindings/clk/at91.h>
+#include <dt-bindings/clock/at91.h>
/ {
aliases {
diff --git a/arch/arm/boot/dts/sama5d3_uart.dtsi b/arch/arm/boot/dts/sama5d3_uart.dtsi
index a9fa75e41652..7a8d4c6115f7 100644
--- a/arch/arm/boot/dts/sama5d3_uart.dtsi
+++ b/arch/arm/boot/dts/sama5d3_uart.dtsi
@@ -9,7 +9,7 @@
#include <dt-bindings/pinctrl/at91.h>
#include <dt-bindings/interrupt-controller/irq.h>
-#include <dt-bindings/clk/at91.h>
+#include <dt-bindings/clock/at91.h>
/ {
aliases {
diff --git a/arch/arm/boot/dts/ste-ccu8540.dts b/arch/arm/boot/dts/ste-ccu8540.dts
index 7f3baf51a3a9..32dd55e5f4e6 100644
--- a/arch/arm/boot/dts/ste-ccu8540.dts
+++ b/arch/arm/boot/dts/ste-ccu8540.dts
@@ -18,6 +18,7 @@
compatible = "st-ericsson,ccu8540", "st-ericsson,u8540";
memory@0 {
+ device_type = "memory";
reg = <0x20000000 0x1f000000>, <0xc0000000 0x3f000000>;
};
diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index 32efc105df83..aba1c8a3f388 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -87,7 +87,7 @@
pll4: clk@01c20018 {
#clock-cells = <0>;
- compatible = "allwinner,sun4i-a10-pll1-clk";
+ compatible = "allwinner,sun7i-a20-pll4-clk";
reg = <0x01c20018 0x4>;
clocks = <&osc24M>;
clock-output-names = "pll4";
@@ -109,6 +109,14 @@
clock-output-names = "pll6_sata", "pll6_other", "pll6";
};
+ pll8: clk@01c20040 {
+ #clock-cells = <0>;
+ compatible = "allwinner,sun7i-a20-pll4-clk";
+ reg = <0x01c20040 0x4>;
+ clocks = <&osc24M>;
+ clock-output-names = "pll8";
+ };
+
cpu: cpu@01c20054 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-a10-cpu-clk";
@@ -805,9 +813,9 @@
status = "disabled";
};
- i2c4: i2c@01c2bc00 {
+ i2c4: i2c@01c2c000 {
compatible = "allwinner,sun4i-i2c";
- reg = <0x01c2bc00 0x400>;
+ reg = <0x01c2c000 0x400>;
interrupts = <0 89 4>;
clocks = <&apb1_gates 15>;
clock-frequency = <100000>;
diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c
index 41bca32409fc..5339009b3c0c 100644
--- a/arch/arm/common/edma.c
+++ b/arch/arm/common/edma.c
@@ -1423,55 +1423,38 @@ EXPORT_SYMBOL(edma_clear_event);
#if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DMADEVICES)
-static int edma_of_read_u32_to_s16_array(const struct device_node *np,
- const char *propname, s16 *out_values,
- size_t sz)
+static int edma_xbar_event_map(struct device *dev, struct device_node *node,
+ struct edma_soc_info *pdata, size_t sz)
{
- int ret;
-
- ret = of_property_read_u16_array(np, propname, out_values, sz);
- if (ret)
- return ret;
-
- /* Terminate it */
- *out_values++ = -1;
- *out_values++ = -1;
-
- return 0;
-}
-
-static int edma_xbar_event_map(struct device *dev,
- struct device_node *node,
- struct edma_soc_info *pdata, int len)
-{
- int ret, i;
+ const char pname[] = "ti,edma-xbar-event-map";
struct resource res;
void __iomem *xbar;
- const s16 (*xbar_chans)[2];
+ s16 (*xbar_chans)[2];
+ size_t nelm = sz / sizeof(s16);
u32 shift, offset, mux;
+ int ret, i;
- xbar_chans = devm_kzalloc(dev,
- len/sizeof(s16) + 2*sizeof(s16),
- GFP_KERNEL);
+ xbar_chans = devm_kzalloc(dev, (nelm + 2) * sizeof(s16), GFP_KERNEL);
if (!xbar_chans)
return -ENOMEM;
ret = of_address_to_resource(node, 1, &res);
if (ret)
- return -EIO;
+ return -ENOMEM;
xbar = devm_ioremap(dev, res.start, resource_size(&res));
if (!xbar)
return -ENOMEM;
- ret = edma_of_read_u32_to_s16_array(node,
- "ti,edma-xbar-event-map",
- (s16 *)xbar_chans,
- len/sizeof(u32));
+ ret = of_property_read_u16_array(node, pname, (u16 *)xbar_chans, nelm);
if (ret)
return -EIO;
- for (i = 0; xbar_chans[i][0] != -1; i++) {
+ /* Invalidate last entry for the other user of this mess */
+ nelm >>= 1;
+ xbar_chans[nelm][0] = xbar_chans[nelm][1] = -1;
+
+ for (i = 0; i < nelm; i++) {
shift = (xbar_chans[i][1] & 0x03) << 3;
offset = xbar_chans[i][1] & 0xfffffffc;
mux = readl(xbar + offset);
@@ -1480,8 +1463,7 @@ static int edma_xbar_event_map(struct device *dev,
writel(mux, (xbar + offset));
}
- pdata->xbar_chans = xbar_chans;
-
+ pdata->xbar_chans = (const s16 (*)[2]) xbar_chans;
return 0;
}
diff --git a/arch/arm/configs/sunxi_defconfig b/arch/arm/configs/sunxi_defconfig
index b5df4a511b0a..81ba78eaf54a 100644
--- a/arch/arm/configs/sunxi_defconfig
+++ b/arch/arm/configs/sunxi_defconfig
@@ -37,7 +37,7 @@ CONFIG_SUN4I_EMAC=y
# CONFIG_NET_VENDOR_NATSEMI is not set
# CONFIG_NET_VENDOR_SEEQ is not set
# CONFIG_NET_VENDOR_SMSC is not set
-# CONFIG_NET_VENDOR_STMICRO is not set
+CONFIG_STMMAC_ETH=y
# CONFIG_NET_VENDOR_WIZNET is not set
# CONFIG_WLAN is not set
CONFIG_SERIAL_8250=y
diff --git a/arch/arm/include/asm/xen/page.h b/arch/arm/include/asm/xen/page.h
index cf4f3e867395..ded062f9b358 100644
--- a/arch/arm/include/asm/xen/page.h
+++ b/arch/arm/include/asm/xen/page.h
@@ -77,7 +77,6 @@ static inline xpaddr_t machine_to_phys(xmaddr_t machine)
}
/* VIRT <-> MACHINE conversion */
#define virt_to_machine(v) (phys_to_machine(XPADDR(__pa(v))))
-#define virt_to_pfn(v) (PFN_DOWN(__pa(v)))
#define virt_to_mfn(v) (pfn_to_mfn(virt_to_pfn(v)))
#define mfn_to_virt(m) (__va(mfn_to_pfn(m) << PAGE_SHIFT))
diff --git a/arch/arm/mach-omap2/omap-headsmp.S b/arch/arm/mach-omap2/omap-headsmp.S
index 75e92952c18e..40c5d5f1451c 100644
--- a/arch/arm/mach-omap2/omap-headsmp.S
+++ b/arch/arm/mach-omap2/omap-headsmp.S
@@ -1,7 +1,7 @@
/*
* Secondary CPU startup routine source file.
*
- * Copyright (C) 2009 Texas Instruments, Inc.
+ * Copyright (C) 2009-2014 Texas Instruments, Inc.
*
* Author:
* Santosh Shilimkar <santosh.shilimkar@ti.com>
@@ -28,9 +28,13 @@
* code. This routine also provides a holding flag into which
* secondary core is held until we're ready for it to initialise.
* The primary core will update this flag using a hardware
-+ * register AuxCoreBoot0.
+ * register AuxCoreBoot0.
*/
ENTRY(omap5_secondary_startup)
+.arm
+THUMB( adr r9, BSYM(wait) ) @ CPU may be entered in ARM mode.
+THUMB( bx r9 ) @ If this is a Thumb-2 kernel,
+THUMB( .thumb ) @ switch to Thumb now.
wait: ldr r2, =AUX_CORE_BOOT0_PA @ read from AuxCoreBoot0
ldr r0, [r2]
mov r0, r0, lsr #5
diff --git a/arch/arm/mach-orion5x/common.h b/arch/arm/mach-orion5x/common.h
index f565f9944af2..7548db2bfb8a 100644
--- a/arch/arm/mach-orion5x/common.h
+++ b/arch/arm/mach-orion5x/common.h
@@ -21,7 +21,7 @@ struct mv_sata_platform_data;
#define ORION_MBUS_DEVBUS_BOOT_ATTR 0x0f
#define ORION_MBUS_DEVBUS_TARGET(cs) 0x01
#define ORION_MBUS_DEVBUS_ATTR(cs) (~(1 << cs))
-#define ORION_MBUS_SRAM_TARGET 0x00
+#define ORION_MBUS_SRAM_TARGET 0x09
#define ORION_MBUS_SRAM_ATTR 0x00
/*
diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
index e94f9458aa6f..993bce527b85 100644
--- a/arch/arm64/include/asm/memory.h
+++ b/arch/arm64/include/asm/memory.h
@@ -138,6 +138,7 @@ static inline void *phys_to_virt(phys_addr_t x)
#define __pa(x) __virt_to_phys((unsigned long)(x))
#define __va(x) ((void *)__phys_to_virt((phys_addr_t)(x)))
#define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT)
+#define virt_to_pfn(x) __phys_to_pfn(__virt_to_phys(x))
/*
* virt_to_page(k) convert a _valid_ virtual address to struct page *
diff --git a/arch/arm64/kernel/irq.c b/arch/arm64/kernel/irq.c
index 473e5dbf8f39..0f08dfd69ebc 100644
--- a/arch/arm64/kernel/irq.c
+++ b/arch/arm64/kernel/irq.c
@@ -97,11 +97,15 @@ static bool migrate_one_irq(struct irq_desc *desc)
if (irqd_is_per_cpu(d) || !cpumask_test_cpu(smp_processor_id(), affinity))
return false;
- if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids) {
- affinity = cpu_online_mask;
+ if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids)
ret = true;
- }
+ /*
+ * when using forced irq_set_affinity we must ensure that the cpu
+ * being offlined is not present in the affinity mask, it may be
+ * selected as the target CPU otherwise
+ */
+ affinity = cpu_online_mask;
c = irq_data_get_irq_chip(d);
if (!c->irq_set_affinity)
pr_debug("IRQ%u: unable to set affinity\n", d->irq);
diff --git a/arch/arm64/mm/hugetlbpage.c b/arch/arm64/mm/hugetlbpage.c
index 5e9aec358306..31eb959e9aa8 100644
--- a/arch/arm64/mm/hugetlbpage.c
+++ b/arch/arm64/mm/hugetlbpage.c
@@ -51,7 +51,11 @@ int pmd_huge(pmd_t pmd)
int pud_huge(pud_t pud)
{
+#ifndef __PAGETABLE_PMD_FOLDED
return !(pud_val(pud) & PUD_TABLE_BIT);
+#else
+ return 0;
+#endif
}
int pmd_huge_support(void)
diff --git a/arch/ia64/include/asm/unistd.h b/arch/ia64/include/asm/unistd.h
index ae763d8bf55a..fb13dc5e8f8c 100644
--- a/arch/ia64/include/asm/unistd.h
+++ b/arch/ia64/include/asm/unistd.h
@@ -11,7 +11,7 @@
-#define NR_syscalls 314 /* length of syscall table */
+#define NR_syscalls 315 /* length of syscall table */
/*
* The following defines stop scripts/checksyscalls.sh from complaining about
diff --git a/arch/ia64/include/uapi/asm/unistd.h b/arch/ia64/include/uapi/asm/unistd.h
index 715e85f858de..7de0a2d65da4 100644
--- a/arch/ia64/include/uapi/asm/unistd.h
+++ b/arch/ia64/include/uapi/asm/unistd.h
@@ -327,5 +327,6 @@
#define __NR_finit_module 1335
#define __NR_sched_setattr 1336
#define __NR_sched_getattr 1337
+#define __NR_renameat2 1338
#endif /* _UAPI_ASM_IA64_UNISTD_H */
diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S
index fa8d61a312a7..ba3d03503e84 100644
--- a/arch/ia64/kernel/entry.S
+++ b/arch/ia64/kernel/entry.S
@@ -1775,6 +1775,7 @@ sys_call_table:
data8 sys_finit_module // 1335
data8 sys_sched_setattr
data8 sys_sched_getattr
+ data8 sys_renameat2
.org sys_call_table + 8*NR_syscalls // guard against failures to increase NR_syscalls
#endif /* __IA64_ASM_PARAVIRTUALIZED_NATIVE */
diff --git a/arch/m68k/include/asm/unistd.h b/arch/m68k/include/asm/unistd.h
index 9d38b73989eb..33afa56ad47a 100644
--- a/arch/m68k/include/asm/unistd.h
+++ b/arch/m68k/include/asm/unistd.h
@@ -4,7 +4,7 @@
#include <uapi/asm/unistd.h>
-#define NR_syscalls 351
+#define NR_syscalls 352
#define __ARCH_WANT_OLD_READDIR
#define __ARCH_WANT_OLD_STAT
diff --git a/arch/m68k/include/uapi/asm/unistd.h b/arch/m68k/include/uapi/asm/unistd.h
index b932dd470041..9cd82fbc7817 100644
--- a/arch/m68k/include/uapi/asm/unistd.h
+++ b/arch/m68k/include/uapi/asm/unistd.h
@@ -356,5 +356,6 @@
#define __NR_finit_module 348
#define __NR_sched_setattr 349
#define __NR_sched_getattr 350
+#define __NR_renameat2 351
#endif /* _UAPI_ASM_M68K_UNISTD_H_ */
diff --git a/arch/m68k/kernel/syscalltable.S b/arch/m68k/kernel/syscalltable.S
index b6223dc41d82..501e10212789 100644
--- a/arch/m68k/kernel/syscalltable.S
+++ b/arch/m68k/kernel/syscalltable.S
@@ -371,4 +371,5 @@ ENTRY(sys_call_table)
.long sys_finit_module
.long sys_sched_setattr
.long sys_sched_getattr /* 350 */
+ .long sys_renameat2
diff --git a/arch/metag/include/asm/barrier.h b/arch/metag/include/asm/barrier.h
index 5d6b4b407dda..2d6f0de77325 100644
--- a/arch/metag/include/asm/barrier.h
+++ b/arch/metag/include/asm/barrier.h
@@ -15,6 +15,7 @@ static inline void wr_fence(void)
volatile int *flushptr = (volatile int *) LINSYSEVENT_WR_FENCE;
barrier();
*flushptr = 0;
+ barrier();
}
#else /* CONFIG_METAG_META21 */
@@ -35,6 +36,7 @@ static inline void wr_fence(void)
*flushptr = 0;
*flushptr = 0;
*flushptr = 0;
+ barrier();
}
#endif /* !CONFIG_METAG_META21 */
@@ -68,6 +70,7 @@ static inline void fence(void)
volatile int *flushptr = (volatile int *) LINSYSEVENT_WR_ATOMIC_UNLOCK;
barrier();
*flushptr = 0;
+ barrier();
}
#define smp_mb() fence()
#define smp_rmb() fence()
diff --git a/arch/metag/include/asm/processor.h b/arch/metag/include/asm/processor.h
index f16477d1f571..a8a37477c66e 100644
--- a/arch/metag/include/asm/processor.h
+++ b/arch/metag/include/asm/processor.h
@@ -22,6 +22,8 @@
/* Add an extra page of padding at the top of the stack for the guard page. */
#define STACK_TOP (TASK_SIZE - PAGE_SIZE)
#define STACK_TOP_MAX STACK_TOP
+/* Maximum virtual space for stack */
+#define STACK_SIZE_MAX (CONFIG_MAX_STACK_SIZE_MB*1024*1024)
/* This decides where the kernel will search for a free chunk of vm
* space during mmap's.
diff --git a/arch/metag/include/uapi/asm/Kbuild b/arch/metag/include/uapi/asm/Kbuild
index 84e09feb4d54..ab78be2b6eb0 100644
--- a/arch/metag/include/uapi/asm/Kbuild
+++ b/arch/metag/include/uapi/asm/Kbuild
@@ -4,11 +4,11 @@ include include/uapi/asm-generic/Kbuild.asm
header-y += byteorder.h
header-y += ech.h
header-y += ptrace.h
-header-y += resource.h
header-y += sigcontext.h
header-y += siginfo.h
header-y += swab.h
header-y += unistd.h
generic-y += mman.h
+generic-y += resource.h
generic-y += setup.h
diff --git a/arch/metag/include/uapi/asm/resource.h b/arch/metag/include/uapi/asm/resource.h
deleted file mode 100644
index 526d23cc3054..000000000000
--- a/arch/metag/include/uapi/asm/resource.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef _UAPI_METAG_RESOURCE_H
-#define _UAPI_METAG_RESOURCE_H
-
-#define _STK_LIM_MAX (1 << 28)
-#include <asm-generic/resource.h>
-
-#endif /* _UAPI_METAG_RESOURCE_H */
diff --git a/arch/mips/dec/ecc-berr.c b/arch/mips/dec/ecc-berr.c
index 5abf4e894216..2a66e908f6a9 100644
--- a/arch/mips/dec/ecc-berr.c
+++ b/arch/mips/dec/ecc-berr.c
@@ -21,6 +21,7 @@
#include <asm/addrspace.h>
#include <asm/bootinfo.h>
#include <asm/cpu.h>
+#include <asm/cpu-type.h>
#include <asm/irq_regs.h>
#include <asm/processor.h>
#include <asm/ptrace.h>
diff --git a/arch/mips/dec/kn02xa-berr.c b/arch/mips/dec/kn02xa-berr.c
index f434b759e3b9..ec606363b806 100644
--- a/arch/mips/dec/kn02xa-berr.c
+++ b/arch/mips/dec/kn02xa-berr.c
@@ -19,6 +19,7 @@
#include <linux/types.h>
#include <asm/addrspace.h>
+#include <asm/cpu-type.h>
#include <asm/irq_regs.h>
#include <asm/ptrace.h>
#include <asm/traps.h>
diff --git a/arch/mips/dec/prom/Makefile b/arch/mips/dec/prom/Makefile
index 064ae7a76bdc..ae73e42ac20b 100644
--- a/arch/mips/dec/prom/Makefile
+++ b/arch/mips/dec/prom/Makefile
@@ -6,4 +6,3 @@
lib-y += init.o memory.o cmdline.o identify.o console.o
lib-$(CONFIG_32BIT) += locore.o
-lib-$(CONFIG_64BIT) += call_o32.o
diff --git a/arch/mips/dec/prom/call_o32.S b/arch/mips/dec/prom/call_o32.S
deleted file mode 100644
index 8c8498159e43..000000000000
--- a/arch/mips/dec/prom/call_o32.S
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * O32 interface for the 64 (or N32) ABI.
- *
- * Copyright (C) 2002 Maciej W. Rozycki
- *
- * 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 <asm/asm.h>
-#include <asm/regdef.h>
-
-/* Maximum number of arguments supported. Must be even! */
-#define O32_ARGC 32
-/* Number of static registers we save. */
-#define O32_STATC 11
-/* Frame size for both of the above. */
-#define O32_FRAMESZ (4 * O32_ARGC + SZREG * O32_STATC)
-
- .text
-
-/*
- * O32 function call dispatcher, for interfacing 32-bit ROM routines.
- *
- * The standard 64 (N32) calling sequence is supported, with a0
- * holding a function pointer, a1-a7 -- its first seven arguments
- * and the stack -- remaining ones (up to O32_ARGC, including a1-a7).
- * Static registers, gp and fp are preserved, v0 holds a result.
- * This code relies on the called o32 function for sp and ra
- * restoration and thus both this dispatcher and the current stack
- * have to be placed in a KSEGx (or KUSEG) address space. Any
- * pointers passed have to point to addresses within one of these
- * spaces as well.
- */
-NESTED(call_o32, O32_FRAMESZ, ra)
- REG_SUBU sp,O32_FRAMESZ
-
- REG_S ra,O32_FRAMESZ-1*SZREG(sp)
- REG_S fp,O32_FRAMESZ-2*SZREG(sp)
- REG_S gp,O32_FRAMESZ-3*SZREG(sp)
- REG_S s7,O32_FRAMESZ-4*SZREG(sp)
- REG_S s6,O32_FRAMESZ-5*SZREG(sp)
- REG_S s5,O32_FRAMESZ-6*SZREG(sp)
- REG_S s4,O32_FRAMESZ-7*SZREG(sp)
- REG_S s3,O32_FRAMESZ-8*SZREG(sp)
- REG_S s2,O32_FRAMESZ-9*SZREG(sp)
- REG_S s1,O32_FRAMESZ-10*SZREG(sp)
- REG_S s0,O32_FRAMESZ-11*SZREG(sp)
-
- move jp,a0
-
- sll a0,a1,zero
- sll a1,a2,zero
- sll a2,a3,zero
- sll a3,a4,zero
- sw a5,0x10(sp)
- sw a6,0x14(sp)
- sw a7,0x18(sp)
-
- PTR_LA t0,O32_FRAMESZ(sp)
- PTR_LA t1,0x1c(sp)
- li t2,O32_ARGC-7
-1:
- lw t3,(t0)
- REG_ADDU t0,SZREG
- sw t3,(t1)
- REG_SUBU t2,1
- REG_ADDU t1,4
- bnez t2,1b
-
- jalr jp
-
- REG_L s0,O32_FRAMESZ-11*SZREG(sp)
- REG_L s1,O32_FRAMESZ-10*SZREG(sp)
- REG_L s2,O32_FRAMESZ-9*SZREG(sp)
- REG_L s3,O32_FRAMESZ-8*SZREG(sp)
- REG_L s4,O32_FRAMESZ-7*SZREG(sp)
- REG_L s5,O32_FRAMESZ-6*SZREG(sp)
- REG_L s6,O32_FRAMESZ-5*SZREG(sp)
- REG_L s7,O32_FRAMESZ-4*SZREG(sp)
- REG_L gp,O32_FRAMESZ-3*SZREG(sp)
- REG_L fp,O32_FRAMESZ-2*SZREG(sp)
- REG_L ra,O32_FRAMESZ-1*SZREG(sp)
-
- REG_ADDU sp,O32_FRAMESZ
- jr ra
-END(call_o32)
diff --git a/arch/mips/fw/lib/call_o32.S b/arch/mips/fw/lib/call_o32.S
index b308b2a0613e..4703fe4dbd9a 100644
--- a/arch/mips/fw/lib/call_o32.S
+++ b/arch/mips/fw/lib/call_o32.S
@@ -1,7 +1,7 @@
/*
* O32 interface for the 64 (or N32) ABI.
*
- * Copyright (C) 2002 Maciej W. Rozycki
+ * Copyright (C) 2002, 2014 Maciej W. Rozycki
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -12,28 +12,37 @@
#include <asm/asm.h>
#include <asm/regdef.h>
+/* O32 register size. */
+#define O32_SZREG 4
/* Maximum number of arguments supported. Must be even! */
#define O32_ARGC 32
-/* Number of static registers we save. */
+/* Number of static registers we save. */
#define O32_STATC 11
-/* Frame size for static register */
-#define O32_FRAMESZ (SZREG * O32_STATC)
-/* Frame size on new stack */
-#define O32_FRAMESZ_NEW (SZREG + 4 * O32_ARGC)
+/* Argument area frame size. */
+#define O32_ARGSZ (O32_SZREG * O32_ARGC)
+/* Static register save area frame size. */
+#define O32_STATSZ (SZREG * O32_STATC)
+/* Stack pointer register save area frame size. */
+#define O32_SPSZ SZREG
+/* Combined area frame size. */
+#define O32_FRAMESZ (O32_ARGSZ + O32_SPSZ + O32_STATSZ)
+/* Switched stack frame size. */
+#define O32_NFRAMESZ (O32_ARGSZ + O32_SPSZ)
.text
/*
* O32 function call dispatcher, for interfacing 32-bit ROM routines.
*
- * The standard 64 (N32) calling sequence is supported, with a0
- * holding a function pointer, a1 a new stack pointer, a2-a7 -- its
- * first six arguments and the stack -- remaining ones (up to O32_ARGC,
- * including a2-a7). Static registers, gp and fp are preserved, v0 holds
- * a result. This code relies on the called o32 function for sp and ra
- * restoration and this dispatcher has to be placed in a KSEGx (or KUSEG)
- * address space. Any pointers passed have to point to addresses within
- * one of these spaces as well.
+ * The standard 64 (N32) calling sequence is supported, with a0 holding
+ * a function pointer, a1 a pointer to the new stack to call the
+ * function with or 0 if no stack switching is requested, a2-a7 -- the
+ * function call's first six arguments, and the stack -- the remaining
+ * arguments (up to O32_ARGC, including a2-a7). Static registers, gp
+ * and fp are preserved, v0 holds the result. This code relies on the
+ * called o32 function for sp and ra restoration and this dispatcher has
+ * to be placed in a KSEGx (or KUSEG) address space. Any pointers
+ * passed have to point to addresses within one of these spaces as well.
*/
NESTED(call_o32, O32_FRAMESZ, ra)
REG_SUBU sp,O32_FRAMESZ
@@ -51,32 +60,36 @@ NESTED(call_o32, O32_FRAMESZ, ra)
REG_S s0,O32_FRAMESZ-11*SZREG(sp)
move jp,a0
- REG_SUBU s0,a1,O32_FRAMESZ_NEW
- REG_S sp,O32_FRAMESZ_NEW-1*SZREG(s0)
+
+ move fp,sp
+ beqz a1,0f
+ REG_SUBU fp,a1,O32_NFRAMESZ
+0:
+ REG_S sp,O32_NFRAMESZ-1*SZREG(fp)
sll a0,a2,zero
sll a1,a3,zero
sll a2,a4,zero
sll a3,a5,zero
- sw a6,0x10(s0)
- sw a7,0x14(s0)
+ sw a6,4*O32_SZREG(fp)
+ sw a7,5*O32_SZREG(fp)
PTR_LA t0,O32_FRAMESZ(sp)
- PTR_LA t1,0x18(s0)
+ PTR_LA t1,6*O32_SZREG(fp)
li t2,O32_ARGC-6
1:
lw t3,(t0)
REG_ADDU t0,SZREG
sw t3,(t1)
REG_SUBU t2,1
- REG_ADDU t1,4
+ REG_ADDU t1,O32_SZREG
bnez t2,1b
- move sp,s0
+ move sp,fp
jalr jp
- REG_L sp,O32_FRAMESZ_NEW-1*SZREG(sp)
+ REG_L sp,O32_NFRAMESZ-1*SZREG(sp)
REG_L s0,O32_FRAMESZ-11*SZREG(sp)
REG_L s1,O32_FRAMESZ-10*SZREG(sp)
diff --git a/arch/mips/fw/sni/sniprom.c b/arch/mips/fw/sni/sniprom.c
index 2c2cb182af4e..6aa264b9856a 100644
--- a/arch/mips/fw/sni/sniprom.c
+++ b/arch/mips/fw/sni/sniprom.c
@@ -40,7 +40,8 @@
#ifdef CONFIG_64BIT
-static u8 o32_stk[16384];
+/* O32 stack has to be 8-byte aligned. */
+static u64 o32_stk[4096];
#define O32_STK &o32_stk[sizeof(o32_stk)]
#define __PROM_O32(fun, arg) fun arg __asm__(#fun); \
diff --git a/arch/mips/include/asm/dec/prom.h b/arch/mips/include/asm/dec/prom.h
index c0ead6313845..b59a2103b61a 100644
--- a/arch/mips/include/asm/dec/prom.h
+++ b/arch/mips/include/asm/dec/prom.h
@@ -113,31 +113,31 @@ extern int (*__pmax_close)(int);
#define __DEC_PROM_O32(fun, arg) fun arg __asm__(#fun); \
__asm__(#fun " = call_o32")
-int __DEC_PROM_O32(_rex_bootinit, (int (*)(void)));
-int __DEC_PROM_O32(_rex_bootread, (int (*)(void)));
-int __DEC_PROM_O32(_rex_getbitmap, (int (*)(memmap *), memmap *));
+int __DEC_PROM_O32(_rex_bootinit, (int (*)(void), void *));
+int __DEC_PROM_O32(_rex_bootread, (int (*)(void), void *));
+int __DEC_PROM_O32(_rex_getbitmap, (int (*)(memmap *), void *, memmap *));
unsigned long *__DEC_PROM_O32(_rex_slot_address,
- (unsigned long *(*)(int), int));
-void *__DEC_PROM_O32(_rex_gettcinfo, (void *(*)(void)));
-int __DEC_PROM_O32(_rex_getsysid, (int (*)(void)));
-void __DEC_PROM_O32(_rex_clear_cache, (void (*)(void)));
-
-int __DEC_PROM_O32(_prom_getchar, (int (*)(void)));
-char *__DEC_PROM_O32(_prom_getenv, (char *(*)(char *), char *));
-int __DEC_PROM_O32(_prom_printf, (int (*)(char *, ...), char *, ...));
-
-
-#define rex_bootinit() _rex_bootinit(__rex_bootinit)
-#define rex_bootread() _rex_bootread(__rex_bootread)
-#define rex_getbitmap(x) _rex_getbitmap(__rex_getbitmap, x)
-#define rex_slot_address(x) _rex_slot_address(__rex_slot_address, x)
-#define rex_gettcinfo() _rex_gettcinfo(__rex_gettcinfo)
-#define rex_getsysid() _rex_getsysid(__rex_getsysid)
-#define rex_clear_cache() _rex_clear_cache(__rex_clear_cache)
-
-#define prom_getchar() _prom_getchar(__prom_getchar)
-#define prom_getenv(x) _prom_getenv(__prom_getenv, x)
-#define prom_printf(x...) _prom_printf(__prom_printf, x)
+ (unsigned long *(*)(int), void *, int));
+void *__DEC_PROM_O32(_rex_gettcinfo, (void *(*)(void), void *));
+int __DEC_PROM_O32(_rex_getsysid, (int (*)(void), void *));
+void __DEC_PROM_O32(_rex_clear_cache, (void (*)(void), void *));
+
+int __DEC_PROM_O32(_prom_getchar, (int (*)(void), void *));
+char *__DEC_PROM_O32(_prom_getenv, (char *(*)(char *), void *, char *));
+int __DEC_PROM_O32(_prom_printf, (int (*)(char *, ...), void *, char *, ...));
+
+
+#define rex_bootinit() _rex_bootinit(__rex_bootinit, NULL)
+#define rex_bootread() _rex_bootread(__rex_bootread, NULL)
+#define rex_getbitmap(x) _rex_getbitmap(__rex_getbitmap, NULL, x)
+#define rex_slot_address(x) _rex_slot_address(__rex_slot_address, NULL, x)
+#define rex_gettcinfo() _rex_gettcinfo(__rex_gettcinfo, NULL)
+#define rex_getsysid() _rex_getsysid(__rex_getsysid, NULL)
+#define rex_clear_cache() _rex_clear_cache(__rex_clear_cache, NULL)
+
+#define prom_getchar() _prom_getchar(__prom_getchar, NULL)
+#define prom_getenv(x) _prom_getenv(__prom_getenv, NULL, x)
+#define prom_printf(x...) _prom_printf(__prom_printf, NULL, x)
#else /* !CONFIG_64BIT */
diff --git a/arch/mips/include/asm/rm9k-ocd.h b/arch/mips/include/asm/rm9k-ocd.h
deleted file mode 100644
index b0b80d9ecf96..000000000000
--- a/arch/mips/include/asm/rm9k-ocd.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2004 by Basler Vision Technologies AG
- * Author: Thomas Koeller <thomas.koeller@baslerweb.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * 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
- */
-
-#if !defined(_ASM_RM9K_OCD_H)
-#define _ASM_RM9K_OCD_H
-
-#include <linux/types.h>
-#include <linux/spinlock.h>
-#include <asm/io.h>
-
-extern volatile void __iomem * const ocd_base;
-extern volatile void __iomem * const titan_base;
-
-#define ocd_addr(__x__) (ocd_base + (__x__))
-#define titan_addr(__x__) (titan_base + (__x__))
-#define scram_addr(__x__) (scram_base + (__x__))
-
-/* OCD register access */
-#define ocd_readl(__offs__) __raw_readl(ocd_addr(__offs__))
-#define ocd_readw(__offs__) __raw_readw(ocd_addr(__offs__))
-#define ocd_readb(__offs__) __raw_readb(ocd_addr(__offs__))
-#define ocd_writel(__val__, __offs__) \
- __raw_writel((__val__), ocd_addr(__offs__))
-#define ocd_writew(__val__, __offs__) \
- __raw_writew((__val__), ocd_addr(__offs__))
-#define ocd_writeb(__val__, __offs__) \
- __raw_writeb((__val__), ocd_addr(__offs__))
-
-/* TITAN register access - 32 bit-wide only */
-#define titan_readl(__offs__) __raw_readl(titan_addr(__offs__))
-#define titan_writel(__val__, __offs__) \
- __raw_writel((__val__), titan_addr(__offs__))
-
-/* Protect access to shared TITAN registers */
-extern spinlock_t titan_lock;
-extern int titan_irqflags;
-#define lock_titan_regs() spin_lock_irqsave(&titan_lock, titan_irqflags)
-#define unlock_titan_regs() spin_unlock_irqrestore(&titan_lock, titan_irqflags)
-
-#endif /* !defined(_ASM_RM9K_OCD_H) */
diff --git a/arch/mips/include/asm/syscall.h b/arch/mips/include/asm/syscall.h
index c6e9cd2bca8d..17960fe7a8ce 100644
--- a/arch/mips/include/asm/syscall.h
+++ b/arch/mips/include/asm/syscall.h
@@ -133,6 +133,8 @@ static inline int syscall_get_arch(void)
#ifdef CONFIG_64BIT
if (!test_thread_flag(TIF_32BIT_REGS))
arch |= __AUDIT_ARCH_64BIT;
+ if (test_thread_flag(TIF_32BIT_ADDR))
+ arch |= __AUDIT_ARCH_CONVENTION_MIPS64_N32;
#endif
#if defined(__LITTLE_ENDIAN)
arch |= __AUDIT_ARCH_LE;
diff --git a/arch/mips/include/uapi/asm/inst.h b/arch/mips/include/uapi/asm/inst.h
index df6e775f3fef..3125797f2a88 100644
--- a/arch/mips/include/uapi/asm/inst.h
+++ b/arch/mips/include/uapi/asm/inst.h
@@ -484,13 +484,13 @@ enum MIPS6e_i8_func {
* Damn ... bitfields depend from byteorder :-(
*/
#ifdef __MIPSEB__
-#define BITFIELD_FIELD(field, more) \
+#define __BITFIELD_FIELD(field, more) \
field; \
more
#elif defined(__MIPSEL__)
-#define BITFIELD_FIELD(field, more) \
+#define __BITFIELD_FIELD(field, more) \
more \
field;
@@ -499,112 +499,112 @@ enum MIPS6e_i8_func {
#endif
struct j_format {
- BITFIELD_FIELD(unsigned int opcode : 6, /* Jump format */
- BITFIELD_FIELD(unsigned int target : 26,
+ __BITFIELD_FIELD(unsigned int opcode : 6, /* Jump format */
+ __BITFIELD_FIELD(unsigned int target : 26,
;))
};
struct i_format { /* signed immediate format */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int rs : 5,
- BITFIELD_FIELD(unsigned int rt : 5,
- BITFIELD_FIELD(signed int simmediate : 16,
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int rs : 5,
+ __BITFIELD_FIELD(unsigned int rt : 5,
+ __BITFIELD_FIELD(signed int simmediate : 16,
;))))
};
struct u_format { /* unsigned immediate format */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int rs : 5,
- BITFIELD_FIELD(unsigned int rt : 5,
- BITFIELD_FIELD(unsigned int uimmediate : 16,
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int rs : 5,
+ __BITFIELD_FIELD(unsigned int rt : 5,
+ __BITFIELD_FIELD(unsigned int uimmediate : 16,
;))))
};
struct c_format { /* Cache (>= R6000) format */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int rs : 5,
- BITFIELD_FIELD(unsigned int c_op : 3,
- BITFIELD_FIELD(unsigned int cache : 2,
- BITFIELD_FIELD(unsigned int simmediate : 16,
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int rs : 5,
+ __BITFIELD_FIELD(unsigned int c_op : 3,
+ __BITFIELD_FIELD(unsigned int cache : 2,
+ __BITFIELD_FIELD(unsigned int simmediate : 16,
;)))))
};
struct r_format { /* Register format */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int rs : 5,
- BITFIELD_FIELD(unsigned int rt : 5,
- BITFIELD_FIELD(unsigned int rd : 5,
- BITFIELD_FIELD(unsigned int re : 5,
- BITFIELD_FIELD(unsigned int func : 6,
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int rs : 5,
+ __BITFIELD_FIELD(unsigned int rt : 5,
+ __BITFIELD_FIELD(unsigned int rd : 5,
+ __BITFIELD_FIELD(unsigned int re : 5,
+ __BITFIELD_FIELD(unsigned int func : 6,
;))))))
};
struct p_format { /* Performance counter format (R10000) */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int rs : 5,
- BITFIELD_FIELD(unsigned int rt : 5,
- BITFIELD_FIELD(unsigned int rd : 5,
- BITFIELD_FIELD(unsigned int re : 5,
- BITFIELD_FIELD(unsigned int func : 6,
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int rs : 5,
+ __BITFIELD_FIELD(unsigned int rt : 5,
+ __BITFIELD_FIELD(unsigned int rd : 5,
+ __BITFIELD_FIELD(unsigned int re : 5,
+ __BITFIELD_FIELD(unsigned int func : 6,
;))))))
};
struct f_format { /* FPU register format */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int : 1,
- BITFIELD_FIELD(unsigned int fmt : 4,
- BITFIELD_FIELD(unsigned int rt : 5,
- BITFIELD_FIELD(unsigned int rd : 5,
- BITFIELD_FIELD(unsigned int re : 5,
- BITFIELD_FIELD(unsigned int func : 6,
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int : 1,
+ __BITFIELD_FIELD(unsigned int fmt : 4,
+ __BITFIELD_FIELD(unsigned int rt : 5,
+ __BITFIELD_FIELD(unsigned int rd : 5,
+ __BITFIELD_FIELD(unsigned int re : 5,
+ __BITFIELD_FIELD(unsigned int func : 6,
;)))))))
};
struct ma_format { /* FPU multiply and add format (MIPS IV) */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int fr : 5,
- BITFIELD_FIELD(unsigned int ft : 5,
- BITFIELD_FIELD(unsigned int fs : 5,
- BITFIELD_FIELD(unsigned int fd : 5,
- BITFIELD_FIELD(unsigned int func : 4,
- BITFIELD_FIELD(unsigned int fmt : 2,
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int fr : 5,
+ __BITFIELD_FIELD(unsigned int ft : 5,
+ __BITFIELD_FIELD(unsigned int fs : 5,
+ __BITFIELD_FIELD(unsigned int fd : 5,
+ __BITFIELD_FIELD(unsigned int func : 4,
+ __BITFIELD_FIELD(unsigned int fmt : 2,
;)))))))
};
struct b_format { /* BREAK and SYSCALL */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int code : 20,
- BITFIELD_FIELD(unsigned int func : 6,
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int code : 20,
+ __BITFIELD_FIELD(unsigned int func : 6,
;)))
};
struct ps_format { /* MIPS-3D / paired single format */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int rs : 5,
- BITFIELD_FIELD(unsigned int ft : 5,
- BITFIELD_FIELD(unsigned int fs : 5,
- BITFIELD_FIELD(unsigned int fd : 5,
- BITFIELD_FIELD(unsigned int func : 6,
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int rs : 5,
+ __BITFIELD_FIELD(unsigned int ft : 5,
+ __BITFIELD_FIELD(unsigned int fs : 5,
+ __BITFIELD_FIELD(unsigned int fd : 5,
+ __BITFIELD_FIELD(unsigned int func : 6,
;))))))
};
struct v_format { /* MDMX vector format */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int sel : 4,
- BITFIELD_FIELD(unsigned int fmt : 1,
- BITFIELD_FIELD(unsigned int vt : 5,
- BITFIELD_FIELD(unsigned int vs : 5,
- BITFIELD_FIELD(unsigned int vd : 5,
- BITFIELD_FIELD(unsigned int func : 6,
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int sel : 4,
+ __BITFIELD_FIELD(unsigned int fmt : 1,
+ __BITFIELD_FIELD(unsigned int vt : 5,
+ __BITFIELD_FIELD(unsigned int vs : 5,
+ __BITFIELD_FIELD(unsigned int vd : 5,
+ __BITFIELD_FIELD(unsigned int func : 6,
;)))))))
};
struct spec3_format { /* SPEC3 */
- BITFIELD_FIELD(unsigned int opcode:6,
- BITFIELD_FIELD(unsigned int rs:5,
- BITFIELD_FIELD(unsigned int rt:5,
- BITFIELD_FIELD(signed int simmediate:9,
- BITFIELD_FIELD(unsigned int func:7,
+ __BITFIELD_FIELD(unsigned int opcode:6,
+ __BITFIELD_FIELD(unsigned int rs:5,
+ __BITFIELD_FIELD(unsigned int rt:5,
+ __BITFIELD_FIELD(signed int simmediate:9,
+ __BITFIELD_FIELD(unsigned int func:7,
;)))))
};
@@ -616,141 +616,141 @@ struct spec3_format { /* SPEC3 */
* if it is MIPS32 instruction re-encoded for use in the microMIPS ASE.
*/
struct fb_format { /* FPU branch format (MIPS32) */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int bc : 5,
- BITFIELD_FIELD(unsigned int cc : 3,
- BITFIELD_FIELD(unsigned int flag : 2,
- BITFIELD_FIELD(signed int simmediate : 16,
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int bc : 5,
+ __BITFIELD_FIELD(unsigned int cc : 3,
+ __BITFIELD_FIELD(unsigned int flag : 2,
+ __BITFIELD_FIELD(signed int simmediate : 16,
;)))))
};
struct fp0_format { /* FPU multiply and add format (MIPS32) */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int fmt : 5,
- BITFIELD_FIELD(unsigned int ft : 5,
- BITFIELD_FIELD(unsigned int fs : 5,
- BITFIELD_FIELD(unsigned int fd : 5,
- BITFIELD_FIELD(unsigned int func : 6,
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int fmt : 5,
+ __BITFIELD_FIELD(unsigned int ft : 5,
+ __BITFIELD_FIELD(unsigned int fs : 5,
+ __BITFIELD_FIELD(unsigned int fd : 5,
+ __BITFIELD_FIELD(unsigned int func : 6,
;))))))
};
struct mm_fp0_format { /* FPU multipy and add format (microMIPS) */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int ft : 5,
- BITFIELD_FIELD(unsigned int fs : 5,
- BITFIELD_FIELD(unsigned int fd : 5,
- BITFIELD_FIELD(unsigned int fmt : 3,
- BITFIELD_FIELD(unsigned int op : 2,
- BITFIELD_FIELD(unsigned int func : 6,
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int ft : 5,
+ __BITFIELD_FIELD(unsigned int fs : 5,
+ __BITFIELD_FIELD(unsigned int fd : 5,
+ __BITFIELD_FIELD(unsigned int fmt : 3,
+ __BITFIELD_FIELD(unsigned int op : 2,
+ __BITFIELD_FIELD(unsigned int func : 6,
;)))))))
};
struct fp1_format { /* FPU mfc1 and cfc1 format (MIPS32) */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int op : 5,
- BITFIELD_FIELD(unsigned int rt : 5,
- BITFIELD_FIELD(unsigned int fs : 5,
- BITFIELD_FIELD(unsigned int fd : 5,
- BITFIELD_FIELD(unsigned int func : 6,
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int op : 5,
+ __BITFIELD_FIELD(unsigned int rt : 5,
+ __BITFIELD_FIELD(unsigned int fs : 5,
+ __BITFIELD_FIELD(unsigned int fd : 5,
+ __BITFIELD_FIELD(unsigned int func : 6,
;))))))
};
struct mm_fp1_format { /* FPU mfc1 and cfc1 format (microMIPS) */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int rt : 5,
- BITFIELD_FIELD(unsigned int fs : 5,
- BITFIELD_FIELD(unsigned int fmt : 2,
- BITFIELD_FIELD(unsigned int op : 8,
- BITFIELD_FIELD(unsigned int func : 6,
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int rt : 5,
+ __BITFIELD_FIELD(unsigned int fs : 5,
+ __BITFIELD_FIELD(unsigned int fmt : 2,
+ __BITFIELD_FIELD(unsigned int op : 8,
+ __BITFIELD_FIELD(unsigned int func : 6,
;))))))
};
struct mm_fp2_format { /* FPU movt and movf format (microMIPS) */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int fd : 5,
- BITFIELD_FIELD(unsigned int fs : 5,
- BITFIELD_FIELD(unsigned int cc : 3,
- BITFIELD_FIELD(unsigned int zero : 2,
- BITFIELD_FIELD(unsigned int fmt : 2,
- BITFIELD_FIELD(unsigned int op : 3,
- BITFIELD_FIELD(unsigned int func : 6,
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int fd : 5,
+ __BITFIELD_FIELD(unsigned int fs : 5,
+ __BITFIELD_FIELD(unsigned int cc : 3,
+ __BITFIELD_FIELD(unsigned int zero : 2,
+ __BITFIELD_FIELD(unsigned int fmt : 2,
+ __BITFIELD_FIELD(unsigned int op : 3,
+ __BITFIELD_FIELD(unsigned int func : 6,
;))))))))
};
struct mm_fp3_format { /* FPU abs and neg format (microMIPS) */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int rt : 5,
- BITFIELD_FIELD(unsigned int fs : 5,
- BITFIELD_FIELD(unsigned int fmt : 3,
- BITFIELD_FIELD(unsigned int op : 7,
- BITFIELD_FIELD(unsigned int func : 6,
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int rt : 5,
+ __BITFIELD_FIELD(unsigned int fs : 5,
+ __BITFIELD_FIELD(unsigned int fmt : 3,
+ __BITFIELD_FIELD(unsigned int op : 7,
+ __BITFIELD_FIELD(unsigned int func : 6,
;))))))
};
struct mm_fp4_format { /* FPU c.cond format (microMIPS) */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int rt : 5,
- BITFIELD_FIELD(unsigned int fs : 5,
- BITFIELD_FIELD(unsigned int cc : 3,
- BITFIELD_FIELD(unsigned int fmt : 3,
- BITFIELD_FIELD(unsigned int cond : 4,
- BITFIELD_FIELD(unsigned int func : 6,
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int rt : 5,
+ __BITFIELD_FIELD(unsigned int fs : 5,
+ __BITFIELD_FIELD(unsigned int cc : 3,
+ __BITFIELD_FIELD(unsigned int fmt : 3,
+ __BITFIELD_FIELD(unsigned int cond : 4,
+ __BITFIELD_FIELD(unsigned int func : 6,
;)))))))
};
struct mm_fp5_format { /* FPU lwxc1 and swxc1 format (microMIPS) */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int index : 5,
- BITFIELD_FIELD(unsigned int base : 5,
- BITFIELD_FIELD(unsigned int fd : 5,
- BITFIELD_FIELD(unsigned int op : 5,
- BITFIELD_FIELD(unsigned int func : 6,
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int index : 5,
+ __BITFIELD_FIELD(unsigned int base : 5,
+ __BITFIELD_FIELD(unsigned int fd : 5,
+ __BITFIELD_FIELD(unsigned int op : 5,
+ __BITFIELD_FIELD(unsigned int func : 6,
;))))))
};
struct fp6_format { /* FPU madd and msub format (MIPS IV) */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int fr : 5,
- BITFIELD_FIELD(unsigned int ft : 5,
- BITFIELD_FIELD(unsigned int fs : 5,
- BITFIELD_FIELD(unsigned int fd : 5,
- BITFIELD_FIELD(unsigned int func : 6,
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int fr : 5,
+ __BITFIELD_FIELD(unsigned int ft : 5,
+ __BITFIELD_FIELD(unsigned int fs : 5,
+ __BITFIELD_FIELD(unsigned int fd : 5,
+ __BITFIELD_FIELD(unsigned int func : 6,
;))))))
};
struct mm_fp6_format { /* FPU madd and msub format (microMIPS) */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int ft : 5,
- BITFIELD_FIELD(unsigned int fs : 5,
- BITFIELD_FIELD(unsigned int fd : 5,
- BITFIELD_FIELD(unsigned int fr : 5,
- BITFIELD_FIELD(unsigned int func : 6,
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int ft : 5,
+ __BITFIELD_FIELD(unsigned int fs : 5,
+ __BITFIELD_FIELD(unsigned int fd : 5,
+ __BITFIELD_FIELD(unsigned int fr : 5,
+ __BITFIELD_FIELD(unsigned int func : 6,
;))))))
};
struct mm_i_format { /* Immediate format (microMIPS) */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int rt : 5,
- BITFIELD_FIELD(unsigned int rs : 5,
- BITFIELD_FIELD(signed int simmediate : 16,
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int rt : 5,
+ __BITFIELD_FIELD(unsigned int rs : 5,
+ __BITFIELD_FIELD(signed int simmediate : 16,
;))))
};
struct mm_m_format { /* Multi-word load/store format (microMIPS) */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int rd : 5,
- BITFIELD_FIELD(unsigned int base : 5,
- BITFIELD_FIELD(unsigned int func : 4,
- BITFIELD_FIELD(signed int simmediate : 12,
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int rd : 5,
+ __BITFIELD_FIELD(unsigned int base : 5,
+ __BITFIELD_FIELD(unsigned int func : 4,
+ __BITFIELD_FIELD(signed int simmediate : 12,
;)))))
};
struct mm_x_format { /* Scaled indexed load format (microMIPS) */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int index : 5,
- BITFIELD_FIELD(unsigned int base : 5,
- BITFIELD_FIELD(unsigned int rd : 5,
- BITFIELD_FIELD(unsigned int func : 11,
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int index : 5,
+ __BITFIELD_FIELD(unsigned int base : 5,
+ __BITFIELD_FIELD(unsigned int rd : 5,
+ __BITFIELD_FIELD(unsigned int func : 11,
;)))))
};
@@ -758,51 +758,51 @@ struct mm_x_format { /* Scaled indexed load format (microMIPS) */
* microMIPS instruction formats (16-bit length)
*/
struct mm_b0_format { /* Unconditional branch format (microMIPS) */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(signed int simmediate : 10,
- BITFIELD_FIELD(unsigned int : 16, /* Ignored */
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(signed int simmediate : 10,
+ __BITFIELD_FIELD(unsigned int : 16, /* Ignored */
;)))
};
struct mm_b1_format { /* Conditional branch format (microMIPS) */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int rs : 3,
- BITFIELD_FIELD(signed int simmediate : 7,
- BITFIELD_FIELD(unsigned int : 16, /* Ignored */
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int rs : 3,
+ __BITFIELD_FIELD(signed int simmediate : 7,
+ __BITFIELD_FIELD(unsigned int : 16, /* Ignored */
;))))
};
struct mm16_m_format { /* Multi-word load/store format */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int func : 4,
- BITFIELD_FIELD(unsigned int rlist : 2,
- BITFIELD_FIELD(unsigned int imm : 4,
- BITFIELD_FIELD(unsigned int : 16, /* Ignored */
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int func : 4,
+ __BITFIELD_FIELD(unsigned int rlist : 2,
+ __BITFIELD_FIELD(unsigned int imm : 4,
+ __BITFIELD_FIELD(unsigned int : 16, /* Ignored */
;)))))
};
struct mm16_rb_format { /* Signed immediate format */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int rt : 3,
- BITFIELD_FIELD(unsigned int base : 3,
- BITFIELD_FIELD(signed int simmediate : 4,
- BITFIELD_FIELD(unsigned int : 16, /* Ignored */
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int rt : 3,
+ __BITFIELD_FIELD(unsigned int base : 3,
+ __BITFIELD_FIELD(signed int simmediate : 4,
+ __BITFIELD_FIELD(unsigned int : 16, /* Ignored */
;)))))
};
struct mm16_r3_format { /* Load from global pointer format */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int rt : 3,
- BITFIELD_FIELD(signed int simmediate : 7,
- BITFIELD_FIELD(unsigned int : 16, /* Ignored */
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int rt : 3,
+ __BITFIELD_FIELD(signed int simmediate : 7,
+ __BITFIELD_FIELD(unsigned int : 16, /* Ignored */
;))))
};
struct mm16_r5_format { /* Load/store from stack pointer format */
- BITFIELD_FIELD(unsigned int opcode : 6,
- BITFIELD_FIELD(unsigned int rt : 5,
- BITFIELD_FIELD(signed int simmediate : 5,
- BITFIELD_FIELD(unsigned int : 16, /* Ignored */
+ __BITFIELD_FIELD(unsigned int opcode : 6,
+ __BITFIELD_FIELD(unsigned int rt : 5,
+ __BITFIELD_FIELD(signed int simmediate : 5,
+ __BITFIELD_FIELD(unsigned int : 16, /* Ignored */
;))))
};
@@ -810,57 +810,57 @@ struct mm16_r5_format { /* Load/store from stack pointer format */
* MIPS16e instruction formats (16-bit length)
*/
struct m16e_rr {
- BITFIELD_FIELD(unsigned int opcode : 5,
- BITFIELD_FIELD(unsigned int rx : 3,
- BITFIELD_FIELD(unsigned int nd : 1,
- BITFIELD_FIELD(unsigned int l : 1,
- BITFIELD_FIELD(unsigned int ra : 1,
- BITFIELD_FIELD(unsigned int func : 5,
+ __BITFIELD_FIELD(unsigned int opcode : 5,
+ __BITFIELD_FIELD(unsigned int rx : 3,
+ __BITFIELD_FIELD(unsigned int nd : 1,
+ __BITFIELD_FIELD(unsigned int l : 1,
+ __BITFIELD_FIELD(unsigned int ra : 1,
+ __BITFIELD_FIELD(unsigned int func : 5,
;))))))
};
struct m16e_jal {
- BITFIELD_FIELD(unsigned int opcode : 5,
- BITFIELD_FIELD(unsigned int x : 1,
- BITFIELD_FIELD(unsigned int imm20_16 : 5,
- BITFIELD_FIELD(signed int imm25_21 : 5,
+ __BITFIELD_FIELD(unsigned int opcode : 5,
+ __BITFIELD_FIELD(unsigned int x : 1,
+ __BITFIELD_FIELD(unsigned int imm20_16 : 5,
+ __BITFIELD_FIELD(signed int imm25_21 : 5,
;))))
};
struct m16e_i64 {
- BITFIELD_FIELD(unsigned int opcode : 5,
- BITFIELD_FIELD(unsigned int func : 3,
- BITFIELD_FIELD(unsigned int imm : 8,
+ __BITFIELD_FIELD(unsigned int opcode : 5,
+ __BITFIELD_FIELD(unsigned int func : 3,
+ __BITFIELD_FIELD(unsigned int imm : 8,
;)))
};
struct m16e_ri64 {
- BITFIELD_FIELD(unsigned int opcode : 5,
- BITFIELD_FIELD(unsigned int func : 3,
- BITFIELD_FIELD(unsigned int ry : 3,
- BITFIELD_FIELD(unsigned int imm : 5,
+ __BITFIELD_FIELD(unsigned int opcode : 5,
+ __BITFIELD_FIELD(unsigned int func : 3,
+ __BITFIELD_FIELD(unsigned int ry : 3,
+ __BITFIELD_FIELD(unsigned int imm : 5,
;))))
};
struct m16e_ri {
- BITFIELD_FIELD(unsigned int opcode : 5,
- BITFIELD_FIELD(unsigned int rx : 3,
- BITFIELD_FIELD(unsigned int imm : 8,
+ __BITFIELD_FIELD(unsigned int opcode : 5,
+ __BITFIELD_FIELD(unsigned int rx : 3,
+ __BITFIELD_FIELD(unsigned int imm : 8,
;)))
};
struct m16e_rri {
- BITFIELD_FIELD(unsigned int opcode : 5,
- BITFIELD_FIELD(unsigned int rx : 3,
- BITFIELD_FIELD(unsigned int ry : 3,
- BITFIELD_FIELD(unsigned int imm : 5,
+ __BITFIELD_FIELD(unsigned int opcode : 5,
+ __BITFIELD_FIELD(unsigned int rx : 3,
+ __BITFIELD_FIELD(unsigned int ry : 3,
+ __BITFIELD_FIELD(unsigned int imm : 5,
;))))
};
struct m16e_i8 {
- BITFIELD_FIELD(unsigned int opcode : 5,
- BITFIELD_FIELD(unsigned int func : 3,
- BITFIELD_FIELD(unsigned int imm : 8,
+ __BITFIELD_FIELD(unsigned int opcode : 5,
+ __BITFIELD_FIELD(unsigned int func : 3,
+ __BITFIELD_FIELD(unsigned int imm : 8,
;)))
};
diff --git a/arch/mips/include/uapi/asm/unistd.h b/arch/mips/include/uapi/asm/unistd.h
index d6e154a9e6a5..2692abb28e36 100644
--- a/arch/mips/include/uapi/asm/unistd.h
+++ b/arch/mips/include/uapi/asm/unistd.h
@@ -371,11 +371,12 @@
#define __NR_finit_module (__NR_Linux + 348)
#define __NR_sched_setattr (__NR_Linux + 349)
#define __NR_sched_getattr (__NR_Linux + 350)
+#define __NR_renameat2 (__NR_Linux + 351)
/*
* Offset of the last Linux o32 flavoured syscall
*/
-#define __NR_Linux_syscalls 350
+#define __NR_Linux_syscalls 351
#endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */
@@ -699,11 +700,12 @@
#define __NR_getdents64 (__NR_Linux + 308)
#define __NR_sched_setattr (__NR_Linux + 309)
#define __NR_sched_getattr (__NR_Linux + 310)
+#define __NR_renameat2 (__NR_Linux + 311)
/*
* Offset of the last Linux 64-bit flavoured syscall
*/
-#define __NR_Linux_syscalls 310
+#define __NR_Linux_syscalls 311
#endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */
@@ -1031,11 +1033,12 @@
#define __NR_finit_module (__NR_Linux + 312)
#define __NR_sched_setattr (__NR_Linux + 313)
#define __NR_sched_getattr (__NR_Linux + 314)
+#define __NR_renameat2 (__NR_Linux + 315)
/*
* Offset of the last N32 flavoured syscall
*/
-#define __NR_Linux_syscalls 314
+#define __NR_Linux_syscalls 315
#endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */
diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c
index e40971b51d2f..037a44d962f3 100644
--- a/arch/mips/kernel/proc.c
+++ b/arch/mips/kernel/proc.c
@@ -124,14 +124,7 @@ static int show_cpuinfo(struct seq_file *m, void *v)
seq_printf(m, "kscratch registers\t: %d\n",
hweight8(cpu_data[n].kscratch_mask));
seq_printf(m, "core\t\t\t: %d\n", cpu_data[n].core);
-#if defined(CONFIG_MIPS_MT_SMP) || defined(CONFIG_MIPS_MT_SMTC)
- if (cpu_has_mipsmt) {
- seq_printf(m, "VPE\t\t\t: %d\n", cpu_data[n].vpe_id);
-#if defined(CONFIG_MIPS_MT_SMTC)
- seq_printf(m, "TC\t\t\t: %d\n", cpu_data[n].tc_id);
-#endif
- }
-#endif
+
sprintf(fmt, "VCE%%c exceptions\t\t: %s\n",
cpu_has_vce ? "%u" : "not available");
seq_printf(m, fmt, 'D', vced_count);
diff --git a/arch/mips/kernel/scall32-o32.S b/arch/mips/kernel/scall32-o32.S
index fdc70b400442..3245474f19d5 100644
--- a/arch/mips/kernel/scall32-o32.S
+++ b/arch/mips/kernel/scall32-o32.S
@@ -577,3 +577,4 @@ EXPORT(sys_call_table)
PTR sys_finit_module
PTR sys_sched_setattr
PTR sys_sched_getattr /* 4350 */
+ PTR sys_renameat2
diff --git a/arch/mips/kernel/scall64-64.S b/arch/mips/kernel/scall64-64.S
index dd99c3285aea..be2fedd4ae33 100644
--- a/arch/mips/kernel/scall64-64.S
+++ b/arch/mips/kernel/scall64-64.S
@@ -430,4 +430,5 @@ EXPORT(sys_call_table)
PTR sys_getdents64
PTR sys_sched_setattr
PTR sys_sched_getattr /* 5310 */
+ PTR sys_renameat2
.size sys_call_table,.-sys_call_table
diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S
index f68d2f4f0090..c1dbcda4b816 100644
--- a/arch/mips/kernel/scall64-n32.S
+++ b/arch/mips/kernel/scall64-n32.S
@@ -423,4 +423,5 @@ EXPORT(sysn32_call_table)
PTR sys_finit_module
PTR sys_sched_setattr
PTR sys_sched_getattr
+ PTR sys_renameat2 /* 6315 */
.size sysn32_call_table,.-sysn32_call_table
diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S
index 70f6acecd928..f1343ccd7ed7 100644
--- a/arch/mips/kernel/scall64-o32.S
+++ b/arch/mips/kernel/scall64-o32.S
@@ -556,4 +556,5 @@ EXPORT(sys32_call_table)
PTR sys_finit_module
PTR sys_sched_setattr
PTR sys_sched_getattr /* 4350 */
+ PTR sys_renameat2
.size sys32_call_table,.-sys32_call_table
diff --git a/arch/mips/lantiq/dts/easy50712.dts b/arch/mips/lantiq/dts/easy50712.dts
index fac1f5b178eb..143b8a37b5e4 100644
--- a/arch/mips/lantiq/dts/easy50712.dts
+++ b/arch/mips/lantiq/dts/easy50712.dts
@@ -8,6 +8,7 @@
};
memory@0 {
+ device_type = "memory";
reg = <0x0 0x2000000>;
};
diff --git a/arch/mips/lib/csum_partial.S b/arch/mips/lib/csum_partial.S
index 2e4825e48388..9901237563c5 100644
--- a/arch/mips/lib/csum_partial.S
+++ b/arch/mips/lib/csum_partial.S
@@ -56,14 +56,20 @@
#define UNIT(unit) ((unit)*NBYTES)
#define ADDC(sum,reg) \
+ .set push; \
+ .set noat; \
ADD sum, reg; \
sltu v1, sum, reg; \
ADD sum, v1; \
+ .set pop
#define ADDC32(sum,reg) \
+ .set push; \
+ .set noat; \
addu sum, reg; \
sltu v1, sum, reg; \
addu sum, v1; \
+ .set pop
#define CSUM_BIGCHUNK1(src, offset, sum, _t0, _t1, _t2, _t3) \
LOAD _t0, (offset + UNIT(0))(src); \
@@ -710,6 +716,8 @@ LEAF(csum_partial)
ADDC(sum, t2)
.Ldone\@:
/* fold checksum */
+ .set push
+ .set noat
#ifdef USE_DOUBLE
dsll32 v1, sum, 0
daddu sum, v1
@@ -732,6 +740,7 @@ LEAF(csum_partial)
or sum, sum, t0
1:
#endif
+ .set pop
.set reorder
ADDC32(sum, psum)
jr ra
diff --git a/arch/mips/lib/delay.c b/arch/mips/lib/delay.c
index 44713af15a62..705cfb7c1a74 100644
--- a/arch/mips/lib/delay.c
+++ b/arch/mips/lib/delay.c
@@ -6,7 +6,7 @@
* Copyright (C) 1994 by Waldorf Electronics
* Copyright (C) 1995 - 2000, 01, 03 by Ralf Baechle
* Copyright (C) 1999, 2000 Silicon Graphics, Inc.
- * Copyright (C) 2007 Maciej W. Rozycki
+ * Copyright (C) 2007, 2014 Maciej W. Rozycki
*/
#include <linux/module.h>
#include <linux/param.h>
@@ -15,6 +15,12 @@
#include <asm/compiler.h>
#include <asm/war.h>
+#ifndef CONFIG_CPU_DADDI_WORKAROUNDS
+#define GCC_DADDI_IMM_ASM() "I"
+#else
+#define GCC_DADDI_IMM_ASM() "r"
+#endif
+
void __delay(unsigned long loops)
{
__asm__ __volatile__ (
@@ -22,13 +28,13 @@ void __delay(unsigned long loops)
" .align 3 \n"
"1: bnez %0, 1b \n"
#if BITS_PER_LONG == 32
- " subu %0, 1 \n"
+ " subu %0, %1 \n"
#else
- " dsubu %0, 1 \n"
+ " dsubu %0, %1 \n"
#endif
" .set reorder \n"
: "=r" (loops)
- : "0" (loops));
+ : GCC_DADDI_IMM_ASM() (1), "0" (loops));
}
EXPORT_SYMBOL(__delay);
diff --git a/arch/mips/lib/strncpy_user.S b/arch/mips/lib/strncpy_user.S
index d3301cd1e9a5..3c32baf8b494 100644
--- a/arch/mips/lib/strncpy_user.S
+++ b/arch/mips/lib/strncpy_user.S
@@ -35,7 +35,6 @@ LEAF(__strncpy_from_\func\()_asm)
bnez v0, .Lfault\@
FEXPORT(__strncpy_from_\func\()_nocheck_asm)
- .set noreorder
move t0, zero
move v1, a1
.ifeqs "\func","kernel"
@@ -45,21 +44,21 @@ FEXPORT(__strncpy_from_\func\()_nocheck_asm)
.endif
PTR_ADDIU v1, 1
R10KCBARRIER(0(ra))
+ sb v0, (a0)
beqz v0, 2f
- sb v0, (a0)
PTR_ADDIU t0, 1
+ PTR_ADDIU a0, 1
bne t0, a2, 1b
- PTR_ADDIU a0, 1
2: PTR_ADDU v0, a1, t0
xor v0, a1
bltz v0, .Lfault\@
- nop
+ move v0, t0
jr ra # return n
- move v0, t0
END(__strncpy_from_\func\()_asm)
-.Lfault\@: jr ra
- li v0, -EFAULT
+.Lfault\@:
+ li v0, -EFAULT
+ jr ra
.section __ex_table,"a"
PTR 1b, .Lfault\@
diff --git a/arch/mips/loongson/Kconfig b/arch/mips/loongson/Kconfig
index 7397be226a06..603d79a95f47 100644
--- a/arch/mips/loongson/Kconfig
+++ b/arch/mips/loongson/Kconfig
@@ -64,7 +64,6 @@ config LEMOTE_MACH3A
bool "Lemote Loongson 3A family machines"
select ARCH_SPARSEMEM_ENABLE
select GENERIC_ISA_DMA_SUPPORT_BROKEN
- select GENERIC_HARDIRQS_NO__DO_IRQ
select BOOT_ELF32
select BOARD_SCACHE
select CSRC_R4K
diff --git a/arch/mips/loongson/lemote-2f/clock.c b/arch/mips/loongson/lemote-2f/clock.c
index e1f427f4f5f3..67dd94ef28e6 100644
--- a/arch/mips/loongson/lemote-2f/clock.c
+++ b/arch/mips/loongson/lemote-2f/clock.c
@@ -91,6 +91,7 @@ EXPORT_SYMBOL(clk_put);
int clk_set_rate(struct clk *clk, unsigned long rate)
{
+ unsigned int rate_khz = rate / 1000;
int ret = 0;
int regval;
int i;
@@ -111,10 +112,10 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
if (loongson2_clockmod_table[i].frequency ==
CPUFREQ_ENTRY_INVALID)
continue;
- if (rate == loongson2_clockmod_table[i].frequency)
+ if (rate_khz == loongson2_clockmod_table[i].frequency)
break;
}
- if (rate != loongson2_clockmod_table[i].frequency)
+ if (rate_khz != loongson2_clockmod_table[i].frequency)
return -ENOTSUPP;
clk->rate = rate;
diff --git a/arch/mips/mm/tlb-funcs.S b/arch/mips/mm/tlb-funcs.S
index 30a494db99c2..a5427c6e9757 100644
--- a/arch/mips/mm/tlb-funcs.S
+++ b/arch/mips/mm/tlb-funcs.S
@@ -16,8 +16,10 @@
#define FASTPATH_SIZE 128
+EXPORT(tlbmiss_handler_setup_pgd_start)
LEAF(tlbmiss_handler_setup_pgd)
- .space 16 * 4
+1: j 1b /* Dummy, will be replaced. */
+ .space 64
END(tlbmiss_handler_setup_pgd)
EXPORT(tlbmiss_handler_setup_pgd_end)
diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
index ee88367ab3ad..f99ec587b151 100644
--- a/arch/mips/mm/tlbex.c
+++ b/arch/mips/mm/tlbex.c
@@ -1422,16 +1422,17 @@ static void build_r4000_tlb_refill_handler(void)
extern u32 handle_tlbl[], handle_tlbl_end[];
extern u32 handle_tlbs[], handle_tlbs_end[];
extern u32 handle_tlbm[], handle_tlbm_end[];
-extern u32 tlbmiss_handler_setup_pgd[], tlbmiss_handler_setup_pgd_end[];
+extern u32 tlbmiss_handler_setup_pgd_start[], tlbmiss_handler_setup_pgd[];
+extern u32 tlbmiss_handler_setup_pgd_end[];
static void build_setup_pgd(void)
{
const int a0 = 4;
const int __maybe_unused a1 = 5;
const int __maybe_unused a2 = 6;
- u32 *p = tlbmiss_handler_setup_pgd;
+ u32 *p = tlbmiss_handler_setup_pgd_start;
const int tlbmiss_handler_setup_pgd_size =
- tlbmiss_handler_setup_pgd_end - tlbmiss_handler_setup_pgd;
+ tlbmiss_handler_setup_pgd_end - tlbmiss_handler_setup_pgd_start;
#ifndef CONFIG_MIPS_PGD_C0_CONTEXT
long pgdc = (long)pgd_current;
#endif
diff --git a/arch/mips/ralink/dts/mt7620a_eval.dts b/arch/mips/ralink/dts/mt7620a_eval.dts
index 35eb874ab7f1..709f58132f5c 100644
--- a/arch/mips/ralink/dts/mt7620a_eval.dts
+++ b/arch/mips/ralink/dts/mt7620a_eval.dts
@@ -7,6 +7,7 @@
model = "Ralink MT7620A evaluation board";
memory@0 {
+ device_type = "memory";
reg = <0x0 0x2000000>;
};
diff --git a/arch/mips/ralink/dts/rt2880_eval.dts b/arch/mips/ralink/dts/rt2880_eval.dts
index 322d7002595b..0a685db093d4 100644
--- a/arch/mips/ralink/dts/rt2880_eval.dts
+++ b/arch/mips/ralink/dts/rt2880_eval.dts
@@ -7,6 +7,7 @@
model = "Ralink RT2880 evaluation board";
memory@0 {
+ device_type = "memory";
reg = <0x8000000 0x2000000>;
};
diff --git a/arch/mips/ralink/dts/rt3052_eval.dts b/arch/mips/ralink/dts/rt3052_eval.dts
index 0ac73ea28198..ec9e9a035541 100644
--- a/arch/mips/ralink/dts/rt3052_eval.dts
+++ b/arch/mips/ralink/dts/rt3052_eval.dts
@@ -7,6 +7,7 @@
model = "Ralink RT3052 evaluation board";
memory@0 {
+ device_type = "memory";
reg = <0x0 0x2000000>;
};
diff --git a/arch/mips/ralink/dts/rt3883_eval.dts b/arch/mips/ralink/dts/rt3883_eval.dts
index 2fa6b330bf4f..e8df21a5d10d 100644
--- a/arch/mips/ralink/dts/rt3883_eval.dts
+++ b/arch/mips/ralink/dts/rt3883_eval.dts
@@ -7,6 +7,7 @@
model = "Ralink RT3883 evaluation board";
memory@0 {
+ device_type = "memory";
reg = <0x0 0x2000000>;
};
diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig
index 1faefed32749..108d48e652af 100644
--- a/arch/parisc/Kconfig
+++ b/arch/parisc/Kconfig
@@ -22,6 +22,7 @@ config PARISC
select GENERIC_SMP_IDLE_THREAD
select GENERIC_STRNCPY_FROM_USER
select SYSCTL_ARCH_UNALIGN_ALLOW
+ select SYSCTL_EXCEPTION_TRACE
select HAVE_MOD_ARCH_SPECIFIC
select VIRT_TO_BUS
select MODULES_USE_ELF_RELA
diff --git a/arch/parisc/include/asm/processor.h b/arch/parisc/include/asm/processor.h
index 198a86feb574..d951c9681ab3 100644
--- a/arch/parisc/include/asm/processor.h
+++ b/arch/parisc/include/asm/processor.h
@@ -55,6 +55,11 @@
#define STACK_TOP TASK_SIZE
#define STACK_TOP_MAX DEFAULT_TASK_SIZE
+/* Allow bigger stacks for 64-bit processes */
+#define STACK_SIZE_MAX (USER_WIDE_MODE \
+ ? (1 << 30) /* 1 GB */ \
+ : (CONFIG_MAX_STACK_SIZE_MB*1024*1024))
+
#endif
#ifndef __ASSEMBLY__
diff --git a/arch/parisc/include/uapi/asm/unistd.h b/arch/parisc/include/uapi/asm/unistd.h
index 265ae5190b0a..47e0e21d2272 100644
--- a/arch/parisc/include/uapi/asm/unistd.h
+++ b/arch/parisc/include/uapi/asm/unistd.h
@@ -829,8 +829,9 @@
#define __NR_sched_setattr (__NR_Linux + 334)
#define __NR_sched_getattr (__NR_Linux + 335)
#define __NR_utimes (__NR_Linux + 336)
+#define __NR_renameat2 (__NR_Linux + 337)
-#define __NR_Linux_syscalls (__NR_utimes + 1)
+#define __NR_Linux_syscalls (__NR_renameat2 + 1)
#define __IGNORE_select /* newselect */
diff --git a/arch/parisc/kernel/sys_parisc.c b/arch/parisc/kernel/sys_parisc.c
index 31ffa9b55322..e1ffea2f9a0b 100644
--- a/arch/parisc/kernel/sys_parisc.c
+++ b/arch/parisc/kernel/sys_parisc.c
@@ -72,10 +72,10 @@ static unsigned long mmap_upper_limit(void)
{
unsigned long stack_base;
- /* Limit stack size to 1GB - see setup_arg_pages() in fs/exec.c */
+ /* Limit stack size - see setup_arg_pages() in fs/exec.c */
stack_base = rlimit_max(RLIMIT_STACK);
- if (stack_base > (1 << 30))
- stack_base = 1 << 30;
+ if (stack_base > STACK_SIZE_MAX)
+ stack_base = STACK_SIZE_MAX;
return PAGE_ALIGN(STACK_TOP - stack_base);
}
diff --git a/arch/parisc/kernel/syscall.S b/arch/parisc/kernel/syscall.S
index a63bb179f79a..838786011037 100644
--- a/arch/parisc/kernel/syscall.S
+++ b/arch/parisc/kernel/syscall.S
@@ -589,10 +589,13 @@ cas_nocontend:
# endif
/* ENABLE_LWS_DEBUG */
+ rsm PSW_SM_I, %r0 /* Disable interrupts */
+ /* COW breaks can cause contention on UP systems */
LDCW 0(%sr2,%r20), %r28 /* Try to acquire the lock */
cmpb,<>,n %r0, %r28, cas_action /* Did we get it? */
cas_wouldblock:
ldo 2(%r0), %r28 /* 2nd case */
+ ssm PSW_SM_I, %r0
b lws_exit /* Contended... */
ldo -EAGAIN(%r0), %r21 /* Spin in userspace */
@@ -619,15 +622,17 @@ cas_action:
stw %r1, 4(%sr2,%r20)
#endif
/* The load and store could fail */
-1: ldw 0(%sr3,%r26), %r28
+1: ldw,ma 0(%sr3,%r26), %r28
sub,<> %r28, %r25, %r0
-2: stw %r24, 0(%sr3,%r26)
+2: stw,ma %r24, 0(%sr3,%r26)
/* Free lock */
- stw %r20, 0(%sr2,%r20)
+ stw,ma %r20, 0(%sr2,%r20)
#if ENABLE_LWS_DEBUG
/* Clear thread register indicator */
stw %r0, 4(%sr2,%r20)
#endif
+ /* Enable interrupts */
+ ssm PSW_SM_I, %r0
/* Return to userspace, set no error */
b lws_exit
copy %r0, %r21
@@ -639,6 +644,7 @@ cas_action:
#if ENABLE_LWS_DEBUG
stw %r0, 4(%sr2,%r20)
#endif
+ ssm PSW_SM_I, %r0
b lws_exit
ldo -EFAULT(%r0),%r21 /* set errno */
nop
diff --git a/arch/parisc/kernel/syscall_table.S b/arch/parisc/kernel/syscall_table.S
index 83ead0ea127d..f1432da7b4c0 100644
--- a/arch/parisc/kernel/syscall_table.S
+++ b/arch/parisc/kernel/syscall_table.S
@@ -432,6 +432,7 @@
ENTRY_SAME(sched_setattr)
ENTRY_SAME(sched_getattr) /* 335 */
ENTRY_COMP(utimes)
+ ENTRY_COMP(renameat2)
/* Nothing yet */
diff --git a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c
index 1cd1d0c83b6d..47ee620d15d2 100644
--- a/arch/parisc/kernel/traps.c
+++ b/arch/parisc/kernel/traps.c
@@ -25,6 +25,7 @@
#include <linux/interrupt.h>
#include <linux/console.h>
#include <linux/bug.h>
+#include <linux/ratelimit.h>
#include <asm/assembly.h>
#include <asm/uaccess.h>
@@ -42,9 +43,6 @@
#include "../math-emu/math-emu.h" /* for handle_fpe() */
-#define PRINT_USER_FAULTS /* (turn this on if you want user faults to be */
- /* dumped to the console via printk) */
-
#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK)
DEFINE_SPINLOCK(pa_dbit_lock);
#endif
@@ -160,6 +158,17 @@ void show_regs(struct pt_regs *regs)
}
}
+static DEFINE_RATELIMIT_STATE(_hppa_rs,
+ DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST);
+
+#define parisc_printk_ratelimited(critical, regs, fmt, ...) { \
+ if ((critical || show_unhandled_signals) && __ratelimit(&_hppa_rs)) { \
+ printk(fmt, ##__VA_ARGS__); \
+ show_regs(regs); \
+ } \
+}
+
+
static void do_show_stack(struct unwind_frame_info *info)
{
int i = 1;
@@ -229,12 +238,10 @@ void die_if_kernel(char *str, struct pt_regs *regs, long err)
if (err == 0)
return; /* STFU */
- printk(KERN_CRIT "%s (pid %d): %s (code %ld) at " RFMT "\n",
+ parisc_printk_ratelimited(1, regs,
+ KERN_CRIT "%s (pid %d): %s (code %ld) at " RFMT "\n",
current->comm, task_pid_nr(current), str, err, regs->iaoq[0]);
-#ifdef PRINT_USER_FAULTS
- /* XXX for debugging only */
- show_regs(regs);
-#endif
+
return;
}
@@ -321,14 +328,11 @@ static void handle_break(struct pt_regs *regs)
(tt == BUG_TRAP_TYPE_NONE) ? 9 : 0);
}
-#ifdef PRINT_USER_FAULTS
- if (unlikely(iir != GDB_BREAK_INSN)) {
- printk(KERN_DEBUG "break %d,%d: pid=%d command='%s'\n",
+ if (unlikely(iir != GDB_BREAK_INSN))
+ parisc_printk_ratelimited(0, regs,
+ KERN_DEBUG "break %d,%d: pid=%d command='%s'\n",
iir & 31, (iir>>13) & ((1<<13)-1),
task_pid_nr(current), current->comm);
- show_regs(regs);
- }
-#endif
/* send standard GDB signal */
handle_gdb_break(regs, TRAP_BRKPT);
@@ -758,11 +762,9 @@ void notrace handle_interruption(int code, struct pt_regs *regs)
default:
if (user_mode(regs)) {
-#ifdef PRINT_USER_FAULTS
- printk(KERN_DEBUG "\nhandle_interruption() pid=%d command='%s'\n",
- task_pid_nr(current), current->comm);
- show_regs(regs);
-#endif
+ parisc_printk_ratelimited(0, regs, KERN_DEBUG
+ "handle_interruption() pid=%d command='%s'\n",
+ task_pid_nr(current), current->comm);
/* SIGBUS, for lack of a better one. */
si.si_signo = SIGBUS;
si.si_code = BUS_OBJERR;
@@ -779,16 +781,10 @@ void notrace handle_interruption(int code, struct pt_regs *regs)
if (user_mode(regs)) {
if ((fault_space >> SPACEID_SHIFT) != (regs->sr[7] >> SPACEID_SHIFT)) {
-#ifdef PRINT_USER_FAULTS
- if (fault_space == 0)
- printk(KERN_DEBUG "User Fault on Kernel Space ");
- else
- printk(KERN_DEBUG "User Fault (long pointer) (fault %d) ",
- code);
- printk(KERN_CONT "pid=%d command='%s'\n",
- task_pid_nr(current), current->comm);
- show_regs(regs);
-#endif
+ parisc_printk_ratelimited(0, regs, KERN_DEBUG
+ "User fault %d on space 0x%08lx, pid=%d command='%s'\n",
+ code, fault_space,
+ task_pid_nr(current), current->comm);
si.si_signo = SIGSEGV;
si.si_errno = 0;
si.si_code = SEGV_MAPERR;
diff --git a/arch/parisc/mm/fault.c b/arch/parisc/mm/fault.c
index 747550762f3c..3ca9c1131cfe 100644
--- a/arch/parisc/mm/fault.c
+++ b/arch/parisc/mm/fault.c
@@ -19,10 +19,6 @@
#include <asm/uaccess.h>
#include <asm/traps.h>
-#define PRINT_USER_FAULTS /* (turn this on if you want user faults to be */
- /* dumped to the console via printk) */
-
-
/* Various important other fields */
#define bit22set(x) (x & 0x00000200)
#define bits23_25set(x) (x & 0x000001c0)
@@ -34,6 +30,8 @@
DEFINE_PER_CPU(struct exception_data, exception_data);
+int show_unhandled_signals = 1;
+
/*
* parisc_acctyp(unsigned int inst) --
* Given a PA-RISC memory access instruction, determine if the
@@ -173,6 +171,32 @@ int fixup_exception(struct pt_regs *regs)
return 0;
}
+/*
+ * Print out info about fatal segfaults, if the show_unhandled_signals
+ * sysctl is set:
+ */
+static inline void
+show_signal_msg(struct pt_regs *regs, unsigned long code,
+ unsigned long address, struct task_struct *tsk,
+ struct vm_area_struct *vma)
+{
+ if (!unhandled_signal(tsk, SIGSEGV))
+ return;
+
+ if (!printk_ratelimit())
+ return;
+
+ pr_warn("\n");
+ pr_warn("do_page_fault() command='%s' type=%lu address=0x%08lx",
+ tsk->comm, code, address);
+ print_vma_addr(KERN_CONT " in ", regs->iaoq[0]);
+ if (vma)
+ pr_warn(" vm_start = 0x%08lx, vm_end = 0x%08lx\n",
+ vma->vm_start, vma->vm_end);
+
+ show_regs(regs);
+}
+
void do_page_fault(struct pt_regs *regs, unsigned long code,
unsigned long address)
{
@@ -270,16 +294,8 @@ bad_area:
if (user_mode(regs)) {
struct siginfo si;
-#ifdef PRINT_USER_FAULTS
- printk(KERN_DEBUG "\n");
- printk(KERN_DEBUG "do_page_fault() pid=%d command='%s' type=%lu address=0x%08lx\n",
- task_pid_nr(tsk), tsk->comm, code, address);
- if (vma) {
- printk(KERN_DEBUG "vm_start = 0x%08lx, vm_end = 0x%08lx\n",
- vma->vm_start, vma->vm_end);
- }
- show_regs(regs);
-#endif
+ show_signal_msg(regs, code, address, tsk, vma);
+
switch (code) {
case 15: /* Data TLB miss fault/Data page fault */
/* send SIGSEGV when outside of vma */
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index 122a580f7322..7e711bdcc6da 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -813,9 +813,6 @@ static void __init clocksource_init(void)
static int decrementer_set_next_event(unsigned long evt,
struct clock_event_device *dev)
{
- /* Don't adjust the decrementer if some irq work is pending */
- if (test_irq_work_pending())
- return 0;
__get_cpu_var(decrementers_next_tb) = get_tb_or_rtc() + evt;
set_dec(evt);
diff --git a/arch/powerpc/platforms/powernv/eeh-ioda.c b/arch/powerpc/platforms/powernv/eeh-ioda.c
index 253fefe3d1a0..5b51079f3e3b 100644
--- a/arch/powerpc/platforms/powernv/eeh-ioda.c
+++ b/arch/powerpc/platforms/powernv/eeh-ioda.c
@@ -549,7 +549,8 @@ static int ioda_eeh_reset(struct eeh_pe *pe, int option)
ret = ioda_eeh_phb_reset(hose, option);
} else {
bus = eeh_pe_bus_get(pe);
- if (pci_is_root_bus(bus))
+ if (pci_is_root_bus(bus) ||
+ pci_is_root_bus(bus->parent))
ret = ioda_eeh_root_reset(hose, option);
else
ret = ioda_eeh_bridge_reset(hose, bus->self, option);
diff --git a/arch/s390/crypto/aes_s390.c b/arch/s390/crypto/aes_s390.c
index cf3c0089bef2..23223cd63e54 100644
--- a/arch/s390/crypto/aes_s390.c
+++ b/arch/s390/crypto/aes_s390.c
@@ -820,6 +820,9 @@ static int ctr_aes_crypt(struct blkcipher_desc *desc, long func,
else
memcpy(walk->iv, ctrptr, AES_BLOCK_SIZE);
spin_unlock(&ctrblk_lock);
+ } else {
+ if (!nbytes)
+ memcpy(walk->iv, ctrptr, AES_BLOCK_SIZE);
}
/*
* final block may be < AES_BLOCK_SIZE, copy only nbytes
diff --git a/arch/s390/crypto/des_s390.c b/arch/s390/crypto/des_s390.c
index 0a5aac8a9412..7acb77f7ef1a 100644
--- a/arch/s390/crypto/des_s390.c
+++ b/arch/s390/crypto/des_s390.c
@@ -429,6 +429,9 @@ static int ctr_desall_crypt(struct blkcipher_desc *desc, long func,
else
memcpy(walk->iv, ctrptr, DES_BLOCK_SIZE);
spin_unlock(&ctrblk_lock);
+ } else {
+ if (!nbytes)
+ memcpy(walk->iv, ctrptr, DES_BLOCK_SIZE);
}
/* final block may be < DES_BLOCK_SIZE, copy only nbytes */
if (nbytes) {
diff --git a/arch/x86/include/asm/hugetlb.h b/arch/x86/include/asm/hugetlb.h
index a8091216963b..68c05398bba9 100644
--- a/arch/x86/include/asm/hugetlb.h
+++ b/arch/x86/include/asm/hugetlb.h
@@ -52,6 +52,7 @@ static inline pte_t huge_ptep_get_and_clear(struct mm_struct *mm,
static inline void huge_ptep_clear_flush(struct vm_area_struct *vma,
unsigned long addr, pte_t *ptep)
{
+ ptep_clear_flush(vma, addr, ptep);
}
static inline int huge_pte_none(pte_t pte)
diff --git a/arch/x86/kernel/cpu/rdrand.c b/arch/x86/kernel/cpu/rdrand.c
index 384df5105fbc..136ac74dee82 100644
--- a/arch/x86/kernel/cpu/rdrand.c
+++ b/arch/x86/kernel/cpu/rdrand.c
@@ -27,6 +27,7 @@
static int __init x86_rdrand_setup(char *s)
{
setup_clear_cpu_cap(X86_FEATURE_RDRAND);
+ setup_clear_cpu_cap(X86_FEATURE_RDSEED);
return 1;
}
__setup("nordrand", x86_rdrand_setup);
diff --git a/arch/x86/kernel/ldt.c b/arch/x86/kernel/ldt.c
index af1d14a9ebda..dcbbaa165bde 100644
--- a/arch/x86/kernel/ldt.c
+++ b/arch/x86/kernel/ldt.c
@@ -20,6 +20,8 @@
#include <asm/mmu_context.h>
#include <asm/syscalls.h>
+int sysctl_ldt16 = 0;
+
#ifdef CONFIG_SMP
static void flush_ldt(void *current_mm)
{
@@ -234,7 +236,7 @@ static int write_ldt(void __user *ptr, unsigned long bytecount, int oldmode)
* IRET leaking the high bits of the kernel stack address.
*/
#ifdef CONFIG_X86_64
- if (!ldt_info.seg_32bit) {
+ if (!ldt_info.seg_32bit && !sysctl_ldt16) {
error = -EINVAL;
goto out_unlock;
}
diff --git a/arch/x86/vdso/vdso32-setup.c b/arch/x86/vdso/vdso32-setup.c
index 00348980a3a6..e1f220e3ca68 100644
--- a/arch/x86/vdso/vdso32-setup.c
+++ b/arch/x86/vdso/vdso32-setup.c
@@ -39,6 +39,7 @@
#ifdef CONFIG_X86_64
#define vdso_enabled sysctl_vsyscall32
#define arch_setup_additional_pages syscall32_setup_pages
+extern int sysctl_ldt16;
#endif
/*
@@ -249,6 +250,13 @@ static struct ctl_table abi_table2[] = {
.mode = 0644,
.proc_handler = proc_dointvec
},
+ {
+ .procname = "ldt16",
+ .data = &sysctl_ldt16,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec
+ },
{}
};
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index e4a4145926f6..1039fb9ff5f5 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -451,7 +451,20 @@ static int blkcg_reset_stats(struct cgroup_subsys_state *css,
struct blkcg_gq *blkg;
int i;
- mutex_lock(&blkcg_pol_mutex);
+ /*
+ * XXX: We invoke cgroup_add/rm_cftypes() under blkcg_pol_mutex
+ * which ends up putting cgroup's internal cgroup_tree_mutex under
+ * it; however, cgroup_tree_mutex is nested above cgroup file
+ * active protection and grabbing blkcg_pol_mutex from a cgroup
+ * file operation creates a possible circular dependency. cgroup
+ * internal locking is planned to go through further simplification
+ * and this issue should go away soon. For now, let's trylock
+ * blkcg_pol_mutex and restart the write on failure.
+ *
+ * http://lkml.kernel.org/g/5363C04B.4010400@oracle.com
+ */
+ if (!mutex_trylock(&blkcg_pol_mutex))
+ return restart_syscall();
spin_lock_irq(&blkcg->lock);
/*
diff --git a/drivers/Makefile b/drivers/Makefile
index d05d81b19b50..7183b6af5dac 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -119,7 +119,7 @@ obj-$(CONFIG_SGI_SN) += sn/
obj-y += firmware/
obj-$(CONFIG_CRYPTO) += crypto/
obj-$(CONFIG_SUPERH) += sh/
-obj-$(CONFIG_ARCH_SHMOBILE_LEGACY) += sh/
+obj-$(CONFIG_ARCH_SHMOBILE) += sh/
ifndef CONFIG_ARCH_USES_GETTIMEOFFSET
obj-y += clocksource/
endif
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index ab686b310100..a34a22841002 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -47,6 +47,23 @@ config ACPI_SLEEP
depends on SUSPEND || HIBERNATION
default y
+config ACPI_PROCFS_POWER
+ bool "Deprecated power /proc/acpi directories"
+ depends on PROC_FS
+ help
+ For backwards compatibility, this option allows
+ deprecated power /proc/acpi/ directories to exist, even when
+ they have been replaced by functions in /sys.
+ The deprecated directories (and their replacements) include:
+ /proc/acpi/battery/* (/sys/class/power_supply/*)
+ /proc/acpi/ac_adapter/* (sys/class/power_supply/*)
+ This option has no effect on /proc/acpi/ directories
+ and functions, which do not yet exist in /sys
+ This option, together with the proc directories, will be
+ deleted in the future.
+
+ Say N to delete power /proc/acpi/ directories that have moved to /sys/
+
config ACPI_EC_DEBUGFS
tristate "EC read/write access through /sys/kernel/debug/ec"
default n
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 0331f91d56e6..bce34afadcd0 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -47,6 +47,7 @@ acpi-y += sysfs.o
acpi-$(CONFIG_X86) += acpi_cmos_rtc.o
acpi-$(CONFIG_DEBUG_FS) += debugfs.o
acpi-$(CONFIG_ACPI_NUMA) += numa.o
+acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o
ifdef CONFIG_ACPI_VIDEO
acpi-y += video_detect.o
endif
diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c
index 2c01c1da29ce..c67f6f5ad611 100644
--- a/drivers/acpi/ac.c
+++ b/drivers/acpi/ac.c
@@ -52,11 +52,39 @@ MODULE_AUTHOR("Paul Diefenbaugh");
MODULE_DESCRIPTION("ACPI AC Adapter Driver");
MODULE_LICENSE("GPL");
+static int acpi_ac_add(struct acpi_device *device);
+static int acpi_ac_remove(struct acpi_device *device);
+static void acpi_ac_notify(struct acpi_device *device, u32 event);
+
+static const struct acpi_device_id ac_device_ids[] = {
+ {"ACPI0003", 0},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, ac_device_ids);
+
+#ifdef CONFIG_PM_SLEEP
+static int acpi_ac_resume(struct device *dev);
+#endif
+static SIMPLE_DEV_PM_OPS(acpi_ac_pm, NULL, acpi_ac_resume);
+
static int ac_sleep_before_get_state_ms;
+static struct acpi_driver acpi_ac_driver = {
+ .name = "ac",
+ .class = ACPI_AC_CLASS,
+ .ids = ac_device_ids,
+ .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
+ .ops = {
+ .add = acpi_ac_add,
+ .remove = acpi_ac_remove,
+ .notify = acpi_ac_notify,
+ },
+ .drv.pm = &acpi_ac_pm,
+};
+
struct acpi_ac {
struct power_supply charger;
- struct platform_device *pdev;
+ struct acpi_device * device;
unsigned long long state;
struct notifier_block battery_nb;
};
@@ -69,10 +97,12 @@ struct acpi_ac {
static int acpi_ac_get_state(struct acpi_ac *ac)
{
- acpi_status status;
- acpi_handle handle = ACPI_HANDLE(&ac->pdev->dev);
+ acpi_status status = AE_OK;
+
+ if (!ac)
+ return -EINVAL;
- status = acpi_evaluate_integer(handle, "_PSR", NULL,
+ status = acpi_evaluate_integer(ac->device->handle, "_PSR", NULL,
&ac->state);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status,
@@ -117,10 +147,9 @@ static enum power_supply_property ac_props[] = {
Driver Model
-------------------------------------------------------------------------- */
-static void acpi_ac_notify_handler(acpi_handle handle, u32 event, void *data)
+static void acpi_ac_notify(struct acpi_device *device, u32 event)
{
- struct acpi_ac *ac = data;
- struct acpi_device *adev;
+ struct acpi_ac *ac = acpi_driver_data(device);
if (!ac)
return;
@@ -143,11 +172,10 @@ static void acpi_ac_notify_handler(acpi_handle handle, u32 event, void *data)
msleep(ac_sleep_before_get_state_ms);
acpi_ac_get_state(ac);
- adev = ACPI_COMPANION(&ac->pdev->dev);
- acpi_bus_generate_netlink_event(adev->pnp.device_class,
- dev_name(&ac->pdev->dev),
- event, (u32) ac->state);
- acpi_notifier_call_chain(adev, event, (u32) ac->state);
+ acpi_bus_generate_netlink_event(device->pnp.device_class,
+ dev_name(&device->dev), event,
+ (u32) ac->state);
+ acpi_notifier_call_chain(device, event, (u32) ac->state);
kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE);
}
@@ -192,49 +220,39 @@ static struct dmi_system_id ac_dmi_table[] = {
{},
};
-static int acpi_ac_probe(struct platform_device *pdev)
+static int acpi_ac_add(struct acpi_device *device)
{
int result = 0;
struct acpi_ac *ac = NULL;
- struct acpi_device *adev;
- if (!pdev)
- return -EINVAL;
- adev = ACPI_COMPANION(&pdev->dev);
- if (!adev)
- return -ENODEV;
+ if (!device)
+ return -EINVAL;
ac = kzalloc(sizeof(struct acpi_ac), GFP_KERNEL);
if (!ac)
return -ENOMEM;
- strcpy(acpi_device_name(adev), ACPI_AC_DEVICE_NAME);
- strcpy(acpi_device_class(adev), ACPI_AC_CLASS);
- ac->pdev = pdev;
- platform_set_drvdata(pdev, ac);
+ ac->device = device;
+ strcpy(acpi_device_name(device), ACPI_AC_DEVICE_NAME);
+ strcpy(acpi_device_class(device), ACPI_AC_CLASS);
+ device->driver_data = ac;
result = acpi_ac_get_state(ac);
if (result)
goto end;
- ac->charger.name = acpi_device_bid(adev);
+ ac->charger.name = acpi_device_bid(device);
ac->charger.type = POWER_SUPPLY_TYPE_MAINS;
ac->charger.properties = ac_props;
ac->charger.num_properties = ARRAY_SIZE(ac_props);
ac->charger.get_property = get_ac_property;
- result = power_supply_register(&pdev->dev, &ac->charger);
+ result = power_supply_register(&ac->device->dev, &ac->charger);
if (result)
goto end;
- result = acpi_install_notify_handler(ACPI_HANDLE(&pdev->dev),
- ACPI_ALL_NOTIFY, acpi_ac_notify_handler, ac);
- if (result) {
- power_supply_unregister(&ac->charger);
- goto end;
- }
printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
- acpi_device_name(adev), acpi_device_bid(adev),
+ acpi_device_name(device), acpi_device_bid(device),
ac->state ? "on-line" : "off-line");
ac->battery_nb.notifier_call = acpi_ac_battery_notify;
@@ -256,7 +274,7 @@ static int acpi_ac_resume(struct device *dev)
if (!dev)
return -EINVAL;
- ac = platform_get_drvdata(to_platform_device(dev));
+ ac = acpi_driver_data(to_acpi_device(dev));
if (!ac)
return -EINVAL;
@@ -270,19 +288,17 @@ static int acpi_ac_resume(struct device *dev)
#else
#define acpi_ac_resume NULL
#endif
-static SIMPLE_DEV_PM_OPS(acpi_ac_pm_ops, NULL, acpi_ac_resume);
-static int acpi_ac_remove(struct platform_device *pdev)
+static int acpi_ac_remove(struct acpi_device *device)
{
- struct acpi_ac *ac;
+ struct acpi_ac *ac = NULL;
+
- if (!pdev)
+ if (!device || !acpi_driver_data(device))
return -EINVAL;
- acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev),
- ACPI_ALL_NOTIFY, acpi_ac_notify_handler);
+ ac = acpi_driver_data(device);
- ac = platform_get_drvdata(pdev);
if (ac->charger.dev)
power_supply_unregister(&ac->charger);
unregister_acpi_notifier(&ac->battery_nb);
@@ -292,23 +308,6 @@ static int acpi_ac_remove(struct platform_device *pdev)
return 0;
}
-static const struct acpi_device_id acpi_ac_match[] = {
- { "ACPI0003", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(acpi, acpi_ac_match);
-
-static struct platform_driver acpi_ac_driver = {
- .probe = acpi_ac_probe,
- .remove = acpi_ac_remove,
- .driver = {
- .name = "acpi-ac",
- .owner = THIS_MODULE,
- .pm = &acpi_ac_pm_ops,
- .acpi_match_table = ACPI_PTR(acpi_ac_match),
- },
-};
-
static int __init acpi_ac_init(void)
{
int result;
@@ -316,7 +315,7 @@ static int __init acpi_ac_init(void)
if (acpi_disabled)
return -ENODEV;
- result = platform_driver_register(&acpi_ac_driver);
+ result = acpi_bus_register_driver(&acpi_ac_driver);
if (result < 0)
return -ENODEV;
@@ -325,7 +324,7 @@ static int __init acpi_ac_init(void)
static void __exit acpi_ac_exit(void)
{
- platform_driver_unregister(&acpi_ac_driver);
+ acpi_bus_unregister_driver(&acpi_ac_driver);
}
module_init(acpi_ac_init);
module_exit(acpi_ac_exit);
diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c
index dbfe49e5fd63..1d4950388fa1 100644
--- a/drivers/acpi/acpi_platform.c
+++ b/drivers/acpi/acpi_platform.c
@@ -29,7 +29,6 @@ ACPI_MODULE_NAME("platform");
static const struct acpi_device_id acpi_platform_device_ids[] = {
{ "PNP0D40" },
- { "ACPI0003" },
{ "VPC2004" },
{ "BCM4752" },
diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c
index b06f5f55ada9..52c81c49cc7d 100644
--- a/drivers/acpi/acpi_processor.c
+++ b/drivers/acpi/acpi_processor.c
@@ -405,7 +405,6 @@ static int acpi_processor_add(struct acpi_device *device,
goto err;
pr->dev = dev;
- dev->offline = pr->flags.need_hotplug_init;
/* Trigger the processor driver's .probe() if present. */
if (device_attach(dev) >= 0)
diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h
index 49bbc71fad54..a08a448068dd 100644
--- a/drivers/acpi/acpica/acglobal.h
+++ b/drivers/acpi/acpica/acglobal.h
@@ -141,9 +141,9 @@ ACPI_INIT_GLOBAL(u8, acpi_gbl_do_not_use_xsdt, FALSE);
* address. Although ACPICA adheres to the ACPI specification which
* requires the use of the corresponding 64-bit address if it is non-zero,
* some machines have been found to have a corrupted non-zero 64-bit
- * address. Default is FALSE, do not favor the 32-bit addresses.
+ * address. Default is TRUE, favor the 32-bit addresses.
*/
-ACPI_INIT_GLOBAL(u8, acpi_gbl_use32_bit_fadt_addresses, FALSE);
+ACPI_INIT_GLOBAL(u8, acpi_gbl_use32_bit_fadt_addresses, TRUE);
/*
* Optionally truncate I/O addresses to 16 bits. Provides compatibility
diff --git a/drivers/acpi/acpica/tbutils.c b/drivers/acpi/acpica/tbutils.c
index a4702eee91a8..9fb85f38de90 100644
--- a/drivers/acpi/acpica/tbutils.c
+++ b/drivers/acpi/acpica/tbutils.c
@@ -461,6 +461,7 @@ acpi_status __init acpi_tb_parse_root_table(acpi_physical_address rsdp_address)
u32 table_count;
struct acpi_table_header *table;
acpi_physical_address address;
+ acpi_physical_address rsdt_address;
u32 length;
u8 *table_entry;
acpi_status status;
@@ -488,11 +489,14 @@ acpi_status __init acpi_tb_parse_root_table(acpi_physical_address rsdp_address)
* as per the ACPI specification.
*/
address = (acpi_physical_address) rsdp->xsdt_physical_address;
+ rsdt_address =
+ (acpi_physical_address) rsdp->rsdt_physical_address;
table_entry_size = ACPI_XSDT_ENTRY_SIZE;
} else {
/* Root table is an RSDT (32-bit physical addresses) */
address = (acpi_physical_address) rsdp->rsdt_physical_address;
+ rsdt_address = address;
table_entry_size = ACPI_RSDT_ENTRY_SIZE;
}
@@ -515,8 +519,7 @@ acpi_status __init acpi_tb_parse_root_table(acpi_physical_address rsdp_address)
/* Fall back to the RSDT */
- address =
- (acpi_physical_address) rsdp->rsdt_physical_address;
+ address = rsdt_address;
table_entry_size = ACPI_RSDT_ENTRY_SIZE;
}
}
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index 9a2c63b20050..6e7b2a12860d 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -36,6 +36,12 @@
#include <linux/suspend.h>
#include <asm/unaligned.h>
+#ifdef CONFIG_ACPI_PROCFS_POWER
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <asm/uaccess.h>
+#endif
+
#include <linux/acpi.h>
#include <linux/power_supply.h>
@@ -64,6 +70,19 @@ static unsigned int cache_time = 1000;
module_param(cache_time, uint, 0644);
MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
+#ifdef CONFIG_ACPI_PROCFS_POWER
+extern struct proc_dir_entry *acpi_lock_battery_dir(void);
+extern void *acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir);
+
+enum acpi_battery_files {
+ info_tag = 0,
+ state_tag,
+ alarm_tag,
+ ACPI_BATTERY_NUMFILES,
+};
+
+#endif
+
static const struct acpi_device_id battery_device_ids[] = {
{"PNP0C0A", 0},
{"", 0},
@@ -299,6 +318,14 @@ static enum power_supply_property energy_battery_props[] = {
POWER_SUPPLY_PROP_SERIAL_NUMBER,
};
+#ifdef CONFIG_ACPI_PROCFS_POWER
+inline char *acpi_battery_units(struct acpi_battery *battery)
+{
+ return (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) ?
+ "mA" : "mW";
+}
+#endif
+
/* --------------------------------------------------------------------------
Battery Management
-------------------------------------------------------------------------- */
@@ -717,6 +744,279 @@ static void acpi_battery_refresh(struct acpi_battery *battery)
}
/* --------------------------------------------------------------------------
+ FS Interface (/proc)
+ -------------------------------------------------------------------------- */
+
+#ifdef CONFIG_ACPI_PROCFS_POWER
+static struct proc_dir_entry *acpi_battery_dir;
+
+static int acpi_battery_print_info(struct seq_file *seq, int result)
+{
+ struct acpi_battery *battery = seq->private;
+
+ if (result)
+ goto end;
+
+ seq_printf(seq, "present: %s\n",
+ acpi_battery_present(battery) ? "yes" : "no");
+ if (!acpi_battery_present(battery))
+ goto end;
+ if (battery->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
+ seq_printf(seq, "design capacity: unknown\n");
+ else
+ seq_printf(seq, "design capacity: %d %sh\n",
+ battery->design_capacity,
+ acpi_battery_units(battery));
+
+ if (battery->full_charge_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
+ seq_printf(seq, "last full capacity: unknown\n");
+ else
+ seq_printf(seq, "last full capacity: %d %sh\n",
+ battery->full_charge_capacity,
+ acpi_battery_units(battery));
+
+ seq_printf(seq, "battery technology: %srechargeable\n",
+ (!battery->technology)?"non-":"");
+
+ if (battery->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
+ seq_printf(seq, "design voltage: unknown\n");
+ else
+ seq_printf(seq, "design voltage: %d mV\n",
+ battery->design_voltage);
+ seq_printf(seq, "design capacity warning: %d %sh\n",
+ battery->design_capacity_warning,
+ acpi_battery_units(battery));
+ seq_printf(seq, "design capacity low: %d %sh\n",
+ battery->design_capacity_low,
+ acpi_battery_units(battery));
+ seq_printf(seq, "cycle count: %i\n", battery->cycle_count);
+ seq_printf(seq, "capacity granularity 1: %d %sh\n",
+ battery->capacity_granularity_1,
+ acpi_battery_units(battery));
+ seq_printf(seq, "capacity granularity 2: %d %sh\n",
+ battery->capacity_granularity_2,
+ acpi_battery_units(battery));
+ seq_printf(seq, "model number: %s\n", battery->model_number);
+ seq_printf(seq, "serial number: %s\n", battery->serial_number);
+ seq_printf(seq, "battery type: %s\n", battery->type);
+ seq_printf(seq, "OEM info: %s\n", battery->oem_info);
+ end:
+ if (result)
+ seq_printf(seq, "ERROR: Unable to read battery info\n");
+ return result;
+}
+
+static int acpi_battery_print_state(struct seq_file *seq, int result)
+{
+ struct acpi_battery *battery = seq->private;
+
+ if (result)
+ goto end;
+
+ seq_printf(seq, "present: %s\n",
+ acpi_battery_present(battery) ? "yes" : "no");
+ if (!acpi_battery_present(battery))
+ goto end;
+
+ seq_printf(seq, "capacity state: %s\n",
+ (battery->state & 0x04) ? "critical" : "ok");
+ if ((battery->state & 0x01) && (battery->state & 0x02))
+ seq_printf(seq,
+ "charging state: charging/discharging\n");
+ else if (battery->state & 0x01)
+ seq_printf(seq, "charging state: discharging\n");
+ else if (battery->state & 0x02)
+ seq_printf(seq, "charging state: charging\n");
+ else
+ seq_printf(seq, "charging state: charged\n");
+
+ if (battery->rate_now == ACPI_BATTERY_VALUE_UNKNOWN)
+ seq_printf(seq, "present rate: unknown\n");
+ else
+ seq_printf(seq, "present rate: %d %s\n",
+ battery->rate_now, acpi_battery_units(battery));
+
+ if (battery->capacity_now == ACPI_BATTERY_VALUE_UNKNOWN)
+ seq_printf(seq, "remaining capacity: unknown\n");
+ else
+ seq_printf(seq, "remaining capacity: %d %sh\n",
+ battery->capacity_now, acpi_battery_units(battery));
+ if (battery->voltage_now == ACPI_BATTERY_VALUE_UNKNOWN)
+ seq_printf(seq, "present voltage: unknown\n");
+ else
+ seq_printf(seq, "present voltage: %d mV\n",
+ battery->voltage_now);
+ end:
+ if (result)
+ seq_printf(seq, "ERROR: Unable to read battery state\n");
+
+ return result;
+}
+
+static int acpi_battery_print_alarm(struct seq_file *seq, int result)
+{
+ struct acpi_battery *battery = seq->private;
+
+ if (result)
+ goto end;
+
+ if (!acpi_battery_present(battery)) {
+ seq_printf(seq, "present: no\n");
+ goto end;
+ }
+ seq_printf(seq, "alarm: ");
+ if (!battery->alarm)
+ seq_printf(seq, "unsupported\n");
+ else
+ seq_printf(seq, "%u %sh\n", battery->alarm,
+ acpi_battery_units(battery));
+ end:
+ if (result)
+ seq_printf(seq, "ERROR: Unable to read battery alarm\n");
+ return result;
+}
+
+static ssize_t acpi_battery_write_alarm(struct file *file,
+ const char __user * buffer,
+ size_t count, loff_t * ppos)
+{
+ int result = 0;
+ char alarm_string[12] = { '\0' };
+ struct seq_file *m = file->private_data;
+ struct acpi_battery *battery = m->private;
+
+ if (!battery || (count > sizeof(alarm_string) - 1))
+ return -EINVAL;
+ if (!acpi_battery_present(battery)) {
+ result = -ENODEV;
+ goto end;
+ }
+ if (copy_from_user(alarm_string, buffer, count)) {
+ result = -EFAULT;
+ goto end;
+ }
+ alarm_string[count] = '\0';
+ battery->alarm = simple_strtol(alarm_string, NULL, 0);
+ result = acpi_battery_set_alarm(battery);
+ end:
+ if (!result)
+ return count;
+ return result;
+}
+
+typedef int(*print_func)(struct seq_file *seq, int result);
+
+static print_func acpi_print_funcs[ACPI_BATTERY_NUMFILES] = {
+ acpi_battery_print_info,
+ acpi_battery_print_state,
+ acpi_battery_print_alarm,
+};
+
+static int acpi_battery_read(int fid, struct seq_file *seq)
+{
+ struct acpi_battery *battery = seq->private;
+ int result = acpi_battery_update(battery);
+ return acpi_print_funcs[fid](seq, result);
+}
+
+#define DECLARE_FILE_FUNCTIONS(_name) \
+static int acpi_battery_read_##_name(struct seq_file *seq, void *offset) \
+{ \
+ return acpi_battery_read(_name##_tag, seq); \
+} \
+static int acpi_battery_##_name##_open_fs(struct inode *inode, struct file *file) \
+{ \
+ return single_open(file, acpi_battery_read_##_name, PDE_DATA(inode)); \
+}
+
+DECLARE_FILE_FUNCTIONS(info);
+DECLARE_FILE_FUNCTIONS(state);
+DECLARE_FILE_FUNCTIONS(alarm);
+
+#undef DECLARE_FILE_FUNCTIONS
+
+#define FILE_DESCRIPTION_RO(_name) \
+ { \
+ .name = __stringify(_name), \
+ .mode = S_IRUGO, \
+ .ops = { \
+ .open = acpi_battery_##_name##_open_fs, \
+ .read = seq_read, \
+ .llseek = seq_lseek, \
+ .release = single_release, \
+ .owner = THIS_MODULE, \
+ }, \
+ }
+
+#define FILE_DESCRIPTION_RW(_name) \
+ { \
+ .name = __stringify(_name), \
+ .mode = S_IFREG | S_IRUGO | S_IWUSR, \
+ .ops = { \
+ .open = acpi_battery_##_name##_open_fs, \
+ .read = seq_read, \
+ .llseek = seq_lseek, \
+ .write = acpi_battery_write_##_name, \
+ .release = single_release, \
+ .owner = THIS_MODULE, \
+ }, \
+ }
+
+static const struct battery_file {
+ struct file_operations ops;
+ umode_t mode;
+ const char *name;
+} acpi_battery_file[] = {
+ FILE_DESCRIPTION_RO(info),
+ FILE_DESCRIPTION_RO(state),
+ FILE_DESCRIPTION_RW(alarm),
+};
+
+#undef FILE_DESCRIPTION_RO
+#undef FILE_DESCRIPTION_RW
+
+static int acpi_battery_add_fs(struct acpi_device *device)
+{
+ struct proc_dir_entry *entry = NULL;
+ int i;
+
+ printk(KERN_WARNING PREFIX "Deprecated procfs I/F for battery is loaded,"
+ " please retry with CONFIG_ACPI_PROCFS_POWER cleared\n");
+ if (!acpi_device_dir(device)) {
+ acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
+ acpi_battery_dir);
+ if (!acpi_device_dir(device))
+ return -ENODEV;
+ }
+
+ for (i = 0; i < ACPI_BATTERY_NUMFILES; ++i) {
+ entry = proc_create_data(acpi_battery_file[i].name,
+ acpi_battery_file[i].mode,
+ acpi_device_dir(device),
+ &acpi_battery_file[i].ops,
+ acpi_driver_data(device));
+ if (!entry)
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static void acpi_battery_remove_fs(struct acpi_device *device)
+{
+ int i;
+ if (!acpi_device_dir(device))
+ return;
+ for (i = 0; i < ACPI_BATTERY_NUMFILES; ++i)
+ remove_proc_entry(acpi_battery_file[i].name,
+ acpi_device_dir(device));
+
+ remove_proc_entry(acpi_device_bid(device), acpi_battery_dir);
+ acpi_device_dir(device) = NULL;
+}
+
+#endif
+
+/* --------------------------------------------------------------------------
Driver Interface
-------------------------------------------------------------------------- */
@@ -790,6 +1090,15 @@ static int acpi_battery_add(struct acpi_device *device)
result = acpi_battery_update(battery);
if (result)
goto fail;
+#ifdef CONFIG_ACPI_PROCFS_POWER
+ result = acpi_battery_add_fs(device);
+#endif
+ if (result) {
+#ifdef CONFIG_ACPI_PROCFS_POWER
+ acpi_battery_remove_fs(device);
+#endif
+ goto fail;
+ }
printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
@@ -816,6 +1125,9 @@ static int acpi_battery_remove(struct acpi_device *device)
return -EINVAL;
battery = acpi_driver_data(device);
unregister_pm_notifier(&battery->pm_nb);
+#ifdef CONFIG_ACPI_PROCFS_POWER
+ acpi_battery_remove_fs(device);
+#endif
sysfs_remove_battery(battery);
mutex_destroy(&battery->lock);
mutex_destroy(&battery->sysfs_lock);
@@ -866,7 +1178,19 @@ static void __init acpi_battery_init_async(void *unused, async_cookie_t cookie)
if (dmi_check_system(bat_dmi_table))
battery_bix_broken_package = 1;
- acpi_bus_register_driver(&acpi_battery_driver);
+
+#ifdef CONFIG_ACPI_PROCFS_POWER
+ acpi_battery_dir = acpi_lock_battery_dir();
+ if (!acpi_battery_dir)
+ return;
+#endif
+ if (acpi_bus_register_driver(&acpi_battery_driver) < 0) {
+#ifdef CONFIG_ACPI_PROCFS_POWER
+ acpi_unlock_battery_dir(acpi_battery_dir);
+#endif
+ return;
+ }
+ return;
}
static int __init acpi_battery_init(void)
@@ -878,6 +1202,9 @@ static int __init acpi_battery_init(void)
static void __exit acpi_battery_exit(void)
{
acpi_bus_unregister_driver(&acpi_battery_driver);
+#ifdef CONFIG_ACPI_PROCFS_POWER
+ acpi_unlock_battery_dir(acpi_battery_dir);
+#endif
}
module_init(acpi_battery_init);
diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c
index afec4526c48a..3d8413d02a97 100644
--- a/drivers/acpi/blacklist.c
+++ b/drivers/acpi/blacklist.c
@@ -314,6 +314,14 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
DMI_MATCH(DMI_PRODUCT_VERSION, "2349D15"),
},
},
+ {
+ .callback = dmi_disable_osi_win8,
+ .ident = "Dell Inspiron 7737",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7737"),
+ },
+ },
/*
* BIOS invocation of _OSI(Linux) is almost always a BIOS bug.
@@ -374,6 +382,19 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T500"),
},
},
+ /*
+ * Without this this EEEpc exports a non working WMI interface, with
+ * this it exports a working "good old" eeepc_laptop interface, fixing
+ * both brightness control, and rfkill not working.
+ */
+ {
+ .callback = dmi_enable_osi_linux,
+ .ident = "Asus EEE PC 1015PX",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "1015PX"),
+ },
+ },
{}
};
diff --git a/drivers/acpi/cm_sbs.c b/drivers/acpi/cm_sbs.c
new file mode 100644
index 000000000000..6c9ee68e46fb
--- /dev/null
+++ b/drivers/acpi/cm_sbs.c
@@ -0,0 +1,105 @@
+/*
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+
+#define PREFIX "ACPI: "
+
+ACPI_MODULE_NAME("cm_sbs");
+#define ACPI_AC_CLASS "ac_adapter"
+#define ACPI_BATTERY_CLASS "battery"
+#define _COMPONENT ACPI_SBS_COMPONENT
+static struct proc_dir_entry *acpi_ac_dir;
+static struct proc_dir_entry *acpi_battery_dir;
+
+static DEFINE_MUTEX(cm_sbs_mutex);
+
+static int lock_ac_dir_cnt;
+static int lock_battery_dir_cnt;
+
+struct proc_dir_entry *acpi_lock_ac_dir(void)
+{
+ mutex_lock(&cm_sbs_mutex);
+ if (!acpi_ac_dir)
+ acpi_ac_dir = proc_mkdir(ACPI_AC_CLASS, acpi_root_dir);
+ if (acpi_ac_dir) {
+ lock_ac_dir_cnt++;
+ } else {
+ printk(KERN_ERR PREFIX
+ "Cannot create %s\n", ACPI_AC_CLASS);
+ }
+ mutex_unlock(&cm_sbs_mutex);
+ return acpi_ac_dir;
+}
+EXPORT_SYMBOL(acpi_lock_ac_dir);
+
+void acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir_param)
+{
+ mutex_lock(&cm_sbs_mutex);
+ if (acpi_ac_dir_param)
+ lock_ac_dir_cnt--;
+ if (lock_ac_dir_cnt == 0 && acpi_ac_dir_param && acpi_ac_dir) {
+ remove_proc_entry(ACPI_AC_CLASS, acpi_root_dir);
+ acpi_ac_dir = NULL;
+ }
+ mutex_unlock(&cm_sbs_mutex);
+}
+EXPORT_SYMBOL(acpi_unlock_ac_dir);
+
+struct proc_dir_entry *acpi_lock_battery_dir(void)
+{
+ mutex_lock(&cm_sbs_mutex);
+ if (!acpi_battery_dir) {
+ acpi_battery_dir =
+ proc_mkdir(ACPI_BATTERY_CLASS, acpi_root_dir);
+ }
+ if (acpi_battery_dir) {
+ lock_battery_dir_cnt++;
+ } else {
+ printk(KERN_ERR PREFIX
+ "Cannot create %s\n", ACPI_BATTERY_CLASS);
+ }
+ mutex_unlock(&cm_sbs_mutex);
+ return acpi_battery_dir;
+}
+EXPORT_SYMBOL(acpi_lock_battery_dir);
+
+void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir_param)
+{
+ mutex_lock(&cm_sbs_mutex);
+ if (acpi_battery_dir_param)
+ lock_battery_dir_cnt--;
+ if (lock_battery_dir_cnt == 0 && acpi_battery_dir_param
+ && acpi_battery_dir) {
+ remove_proc_entry(ACPI_BATTERY_CLASS, acpi_root_dir);
+ acpi_battery_dir = NULL;
+ }
+ mutex_unlock(&cm_sbs_mutex);
+ return;
+}
+EXPORT_SYMBOL(acpi_unlock_battery_dir);
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
index 8b6990e417ec..f8bc5a755dda 100644
--- a/drivers/acpi/video.c
+++ b/drivers/acpi/video.c
@@ -457,10 +457,10 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
},
{
.callback = video_set_use_native_backlight,
- .ident = "ThinkPad T430s",
+ .ident = "ThinkPad T430 and T430s",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T430s"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T430"),
},
},
{
@@ -472,7 +472,7 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
},
},
{
- .callback = video_set_use_native_backlight,
+ .callback = video_set_use_native_backlight,
.ident = "ThinkPad X1 Carbon",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
@@ -500,7 +500,7 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
.ident = "Dell Inspiron 7520",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_VERSION, "Inspiron 7520"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7520"),
},
},
{
@@ -513,6 +513,14 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
},
{
.callback = video_set_use_native_backlight,
+ .ident = "Acer Aspire 5742G",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5742G"),
+ },
+ },
+ {
+ .callback = video_set_use_native_backlight,
.ident = "Acer Aspire V5-431",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index c2706047337f..0033fafc470b 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -815,7 +815,7 @@ config PATA_AT32
config PATA_AT91
tristate "PATA support for AT91SAM9260"
- depends on ARM && ARCH_AT91
+ depends on ARM && SOC_AT91SAM9
help
This option enables support for IDE devices on the Atmel AT91SAM9260 SoC.
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 71e15b73513d..60707814a84b 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -1115,6 +1115,17 @@ static bool ahci_broken_online(struct pci_dev *pdev)
return pdev->bus->number == (val >> 8) && pdev->devfn == (val & 0xff);
}
+static bool ahci_broken_devslp(struct pci_dev *pdev)
+{
+ /* device with broken DEVSLP but still showing SDS capability */
+ static const struct pci_device_id ids[] = {
+ { PCI_VDEVICE(INTEL, 0x0f23)}, /* Valleyview SoC */
+ {}
+ };
+
+ return pci_match_id(ids, pdev);
+}
+
#ifdef CONFIG_ATA_ACPI
static void ahci_gtf_filter_workaround(struct ata_host *host)
{
@@ -1364,6 +1375,10 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
hpriv->mmio = pcim_iomap_table(pdev)[ahci_pci_bar];
+ /* must set flag prior to save config in order to take effect */
+ if (ahci_broken_devslp(pdev))
+ hpriv->flags |= AHCI_HFLAG_NO_DEVSLP;
+
/* save initial config */
ahci_pci_save_initial_config(pdev, hpriv);
diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
index b5eb886da226..af63c75c2001 100644
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -236,6 +236,7 @@ enum {
port start (wait until
error-handling stage) */
AHCI_HFLAG_MULTI_MSI = (1 << 16), /* multiple PCI MSIs */
+ AHCI_HFLAG_NO_DEVSLP = (1 << 17), /* no device sleep */
/* ap->flags bits */
diff --git a/drivers/ata/ahci_imx.c b/drivers/ata/ahci_imx.c
index 497c7abe1c7d..8befeb69eeb1 100644
--- a/drivers/ata/ahci_imx.c
+++ b/drivers/ata/ahci_imx.c
@@ -29,9 +29,25 @@
#include "ahci.h"
enum {
- PORT_PHY_CTL = 0x178, /* Port0 PHY Control */
- PORT_PHY_CTL_PDDQ_LOC = 0x100000, /* PORT_PHY_CTL bits */
- HOST_TIMER1MS = 0xe0, /* Timer 1-ms */
+ /* Timer 1-ms Register */
+ IMX_TIMER1MS = 0x00e0,
+ /* Port0 PHY Control Register */
+ IMX_P0PHYCR = 0x0178,
+ IMX_P0PHYCR_TEST_PDDQ = 1 << 20,
+ IMX_P0PHYCR_CR_READ = 1 << 19,
+ IMX_P0PHYCR_CR_WRITE = 1 << 18,
+ IMX_P0PHYCR_CR_CAP_DATA = 1 << 17,
+ IMX_P0PHYCR_CR_CAP_ADDR = 1 << 16,
+ /* Port0 PHY Status Register */
+ IMX_P0PHYSR = 0x017c,
+ IMX_P0PHYSR_CR_ACK = 1 << 18,
+ IMX_P0PHYSR_CR_DATA_OUT = 0xffff << 0,
+ /* Lane0 Output Status Register */
+ IMX_LANE0_OUT_STAT = 0x2003,
+ IMX_LANE0_OUT_STAT_RX_PLL_STATE = 1 << 1,
+ /* Clock Reset Register */
+ IMX_CLOCK_RESET = 0x7f3f,
+ IMX_CLOCK_RESET_RESET = 1 << 0,
};
enum ahci_imx_type {
@@ -54,9 +70,149 @@ MODULE_PARM_DESC(hotplug, "AHCI IMX hot-plug support (0=Don't support, 1=support
static void ahci_imx_host_stop(struct ata_host *host);
+static int imx_phy_crbit_assert(void __iomem *mmio, u32 bit, bool assert)
+{
+ int timeout = 10;
+ u32 crval;
+ u32 srval;
+
+ /* Assert or deassert the bit */
+ crval = readl(mmio + IMX_P0PHYCR);
+ if (assert)
+ crval |= bit;
+ else
+ crval &= ~bit;
+ writel(crval, mmio + IMX_P0PHYCR);
+
+ /* Wait for the cr_ack signal */
+ do {
+ srval = readl(mmio + IMX_P0PHYSR);
+ if ((assert ? srval : ~srval) & IMX_P0PHYSR_CR_ACK)
+ break;
+ usleep_range(100, 200);
+ } while (--timeout);
+
+ return timeout ? 0 : -ETIMEDOUT;
+}
+
+static int imx_phy_reg_addressing(u16 addr, void __iomem *mmio)
+{
+ u32 crval = addr;
+ int ret;
+
+ /* Supply the address on cr_data_in */
+ writel(crval, mmio + IMX_P0PHYCR);
+
+ /* Assert the cr_cap_addr signal */
+ ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_CAP_ADDR, true);
+ if (ret)
+ return ret;
+
+ /* Deassert cr_cap_addr */
+ ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_CAP_ADDR, false);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int imx_phy_reg_write(u16 val, void __iomem *mmio)
+{
+ u32 crval = val;
+ int ret;
+
+ /* Supply the data on cr_data_in */
+ writel(crval, mmio + IMX_P0PHYCR);
+
+ /* Assert the cr_cap_data signal */
+ ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_CAP_DATA, true);
+ if (ret)
+ return ret;
+
+ /* Deassert cr_cap_data */
+ ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_CAP_DATA, false);
+ if (ret)
+ return ret;
+
+ if (val & IMX_CLOCK_RESET_RESET) {
+ /*
+ * In case we're resetting the phy, it's unable to acknowledge,
+ * so we return immediately here.
+ */
+ crval |= IMX_P0PHYCR_CR_WRITE;
+ writel(crval, mmio + IMX_P0PHYCR);
+ goto out;
+ }
+
+ /* Assert the cr_write signal */
+ ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_WRITE, true);
+ if (ret)
+ return ret;
+
+ /* Deassert cr_write */
+ ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_WRITE, false);
+ if (ret)
+ return ret;
+
+out:
+ return 0;
+}
+
+static int imx_phy_reg_read(u16 *val, void __iomem *mmio)
+{
+ int ret;
+
+ /* Assert the cr_read signal */
+ ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_READ, true);
+ if (ret)
+ return ret;
+
+ /* Capture the data from cr_data_out[] */
+ *val = readl(mmio + IMX_P0PHYSR) & IMX_P0PHYSR_CR_DATA_OUT;
+
+ /* Deassert cr_read */
+ ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_READ, false);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int imx_sata_phy_reset(struct ahci_host_priv *hpriv)
+{
+ void __iomem *mmio = hpriv->mmio;
+ int timeout = 10;
+ u16 val;
+ int ret;
+
+ /* Reset SATA PHY by setting RESET bit of PHY register CLOCK_RESET */
+ ret = imx_phy_reg_addressing(IMX_CLOCK_RESET, mmio);
+ if (ret)
+ return ret;
+ ret = imx_phy_reg_write(IMX_CLOCK_RESET_RESET, mmio);
+ if (ret)
+ return ret;
+
+ /* Wait for PHY RX_PLL to be stable */
+ do {
+ usleep_range(100, 200);
+ ret = imx_phy_reg_addressing(IMX_LANE0_OUT_STAT, mmio);
+ if (ret)
+ return ret;
+ ret = imx_phy_reg_read(&val, mmio);
+ if (ret)
+ return ret;
+ if (val & IMX_LANE0_OUT_STAT_RX_PLL_STATE)
+ break;
+ } while (--timeout);
+
+ return timeout ? 0 : -ETIMEDOUT;
+}
+
static int imx_sata_enable(struct ahci_host_priv *hpriv)
{
struct imx_ahci_priv *imxpriv = hpriv->plat_data;
+ struct device *dev = &imxpriv->ahci_pdev->dev;
int ret;
if (imxpriv->no_device)
@@ -101,6 +257,14 @@ static int imx_sata_enable(struct ahci_host_priv *hpriv)
regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13,
IMX6Q_GPR13_SATA_MPLL_CLK_EN,
IMX6Q_GPR13_SATA_MPLL_CLK_EN);
+
+ usleep_range(100, 200);
+
+ ret = imx_sata_phy_reset(hpriv);
+ if (ret) {
+ dev_err(dev, "failed to reset phy: %d\n", ret);
+ goto disable_regulator;
+ }
}
usleep_range(1000, 2000);
@@ -156,8 +320,8 @@ static void ahci_imx_error_handler(struct ata_port *ap)
* without full reset once the pddq mode is enabled making it
* impossible to use as part of libata LPM.
*/
- reg_val = readl(mmio + PORT_PHY_CTL);
- writel(reg_val | PORT_PHY_CTL_PDDQ_LOC, mmio + PORT_PHY_CTL);
+ reg_val = readl(mmio + IMX_P0PHYCR);
+ writel(reg_val | IMX_P0PHYCR_TEST_PDDQ, mmio + IMX_P0PHYCR);
imx_sata_disable(hpriv);
imxpriv->no_device = true;
}
@@ -217,6 +381,7 @@ static int imx_ahci_probe(struct platform_device *pdev)
if (!imxpriv)
return -ENOMEM;
+ imxpriv->ahci_pdev = pdev;
imxpriv->no_device = false;
imxpriv->first_time = true;
imxpriv->type = (enum ahci_imx_type)of_id->data;
@@ -248,7 +413,7 @@ static int imx_ahci_probe(struct platform_device *pdev)
/*
* Configure the HWINIT bits of the HOST_CAP and HOST_PORTS_IMPL,
- * and IP vendor specific register HOST_TIMER1MS.
+ * and IP vendor specific register IMX_TIMER1MS.
* Configure CAP_SSS (support stagered spin up).
* Implement the port0.
* Get the ahb clock rate, and configure the TIMER1MS register.
@@ -265,7 +430,7 @@ static int imx_ahci_probe(struct platform_device *pdev)
}
reg_val = clk_get_rate(imxpriv->ahb_clk) / 1000;
- writel(reg_val, hpriv->mmio + HOST_TIMER1MS);
+ writel(reg_val, hpriv->mmio + IMX_TIMER1MS);
ret = ahci_platform_init_host(pdev, hpriv, &ahci_imx_port_info, 0, 0);
if (ret)
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index 6bd4f660b4e1..b9861453fc81 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -452,6 +452,13 @@ void ahci_save_initial_config(struct device *dev,
cap &= ~HOST_CAP_SNTF;
}
+ if ((cap2 & HOST_CAP2_SDS) && (hpriv->flags & AHCI_HFLAG_NO_DEVSLP)) {
+ dev_info(dev,
+ "controller can't do DEVSLP, turning off\n");
+ cap2 &= ~HOST_CAP2_SDS;
+ cap2 &= ~HOST_CAP2_SADM;
+ }
+
if (!(cap & HOST_CAP_FBS) && (hpriv->flags & AHCI_HFLAG_YES_FBS)) {
dev_info(dev, "controller can do FBS, turning on CAP_FBS\n");
cap |= HOST_CAP_FBS;
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 943cc8b83e59..ea83828bfea9 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -6314,6 +6314,8 @@ int ata_host_activate(struct ata_host *host, int irq,
static void ata_port_detach(struct ata_port *ap)
{
unsigned long flags;
+ struct ata_link *link;
+ struct ata_device *dev;
if (!ap->ops->error_handler)
goto skip_eh;
@@ -6333,6 +6335,13 @@ static void ata_port_detach(struct ata_port *ap)
cancel_delayed_work_sync(&ap->hotplug_task);
skip_eh:
+ /* clean up zpodd on port removal */
+ ata_for_each_link(link, ap, HOST_FIRST) {
+ ata_for_each_dev(dev, link, ALL) {
+ if (zpodd_dev_enabled(dev))
+ zpodd_exit(dev);
+ }
+ }
if (ap->pmp_link) {
int i;
for (i = 0; i < SATA_PMP_MAX_PORTS; i++)
diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c
index 293e2e0a0a87..00b73448b22e 100644
--- a/drivers/bus/mvebu-mbus.c
+++ b/drivers/bus/mvebu-mbus.c
@@ -56,6 +56,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/debugfs.h>
+#include <linux/log2.h>
/*
* DDR target is the same on all platforms.
@@ -222,12 +223,6 @@ static int mvebu_mbus_window_conflicts(struct mvebu_mbus_state *mbus,
*/
if ((u64)base < wend && end > wbase)
return 0;
-
- /*
- * Check if target/attribute conflicts
- */
- if (target == wtarget && attr == wattr)
- return 0;
}
return 1;
@@ -266,6 +261,17 @@ static int mvebu_mbus_setup_window(struct mvebu_mbus_state *mbus,
mbus->soc->win_cfg_offset(win);
u32 ctrl, remap_addr;
+ if (!is_power_of_2(size)) {
+ WARN(true, "Invalid MBus window size: 0x%zx\n", size);
+ return -EINVAL;
+ }
+
+ if ((base & (phys_addr_t)(size - 1)) != 0) {
+ WARN(true, "Invalid MBus base/size: %pa len 0x%zx\n", &base,
+ size);
+ return -EINVAL;
+ }
+
ctrl = ((size - 1) & WIN_CTRL_SIZE_MASK) |
(attr << WIN_CTRL_ATTR_SHIFT) |
(target << WIN_CTRL_TGT_SHIFT) |
@@ -413,6 +419,10 @@ static int mvebu_devs_debug_show(struct seq_file *seq, void *v)
win, (unsigned long long)wbase,
(unsigned long long)(wbase + wsize), wtarget, wattr);
+ if (!is_power_of_2(wsize) ||
+ ((wbase & (u64)(wsize - 1)) != 0))
+ seq_puts(seq, " (Invalid base/size!!)");
+
if (win < mbus->soc->num_remappable_wins) {
seq_printf(seq, " (remap %016llx)\n",
(unsigned long long)wremap);
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 6b75713d953a..102c50d38902 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -995,8 +995,11 @@ retry:
ibytes = min_t(size_t, ibytes, have_bytes - reserved);
if (ibytes < min)
ibytes = 0;
- entropy_count = max_t(int, 0,
- entropy_count - (ibytes << (ENTROPY_SHIFT + 3)));
+ if (have_bytes >= ibytes + reserved)
+ entropy_count -= ibytes << (ENTROPY_SHIFT + 3);
+ else
+ entropy_count = reserved << (ENTROPY_SHIFT + 3);
+
if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig)
goto retry;
diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c
index b3ea223585bd..61dcc8011ec7 100644
--- a/drivers/char/tpm/tpm_ppi.c
+++ b/drivers/char/tpm/tpm_ppi.c
@@ -328,13 +328,11 @@ int tpm_add_ppi(struct kobject *parent)
/* Cache TPM ACPI handle and version string */
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX,
ppi_callback, NULL, NULL, &tpm_ppi_handle);
- if (tpm_ppi_handle == NULL)
- return -ENODEV;
-
- return sysfs_create_group(parent, &ppi_attr_grp);
+ return tpm_ppi_handle ? sysfs_create_group(parent, &ppi_attr_grp) : 0;
}
void tpm_remove_ppi(struct kobject *parent)
{
- sysfs_remove_group(parent, &ppi_attr_grp);
+ if (tpm_ppi_handle)
+ sysfs_remove_group(parent, &ppi_attr_grp);
}
diff --git a/drivers/clk/bcm/clk-kona-setup.c b/drivers/clk/bcm/clk-kona-setup.c
index c7607feb18dd..54a06526f64f 100644
--- a/drivers/clk/bcm/clk-kona-setup.c
+++ b/drivers/clk/bcm/clk-kona-setup.c
@@ -27,7 +27,7 @@ LIST_HEAD(ccu_list); /* The list of set up CCUs */
static bool clk_requires_trigger(struct kona_clk *bcm_clk)
{
- struct peri_clk_data *peri = bcm_clk->peri;
+ struct peri_clk_data *peri = bcm_clk->u.peri;
struct bcm_clk_sel *sel;
struct bcm_clk_div *div;
@@ -63,7 +63,7 @@ static bool peri_clk_data_offsets_valid(struct kona_clk *bcm_clk)
u32 limit;
BUG_ON(bcm_clk->type != bcm_clk_peri);
- peri = bcm_clk->peri;
+ peri = bcm_clk->u.peri;
name = bcm_clk->name;
range = bcm_clk->ccu->range;
@@ -81,19 +81,19 @@ static bool peri_clk_data_offsets_valid(struct kona_clk *bcm_clk)
div = &peri->div;
if (divider_exists(div)) {
- if (div->offset > limit) {
+ if (div->u.s.offset > limit) {
pr_err("%s: bad divider offset for %s (%u > %u)\n",
- __func__, name, div->offset, limit);
+ __func__, name, div->u.s.offset, limit);
return false;
}
}
div = &peri->pre_div;
if (divider_exists(div)) {
- if (div->offset > limit) {
+ if (div->u.s.offset > limit) {
pr_err("%s: bad pre-divider offset for %s "
"(%u > %u)\n",
- __func__, name, div->offset, limit);
+ __func__, name, div->u.s.offset, limit);
return false;
}
}
@@ -249,21 +249,22 @@ static bool div_valid(struct bcm_clk_div *div, const char *field_name,
{
if (divider_is_fixed(div)) {
/* Any fixed divider value but 0 is OK */
- if (div->fixed == 0) {
+ if (div->u.fixed == 0) {
pr_err("%s: bad %s fixed value 0 for %s\n", __func__,
field_name, clock_name);
return false;
}
return true;
}
- if (!bitfield_valid(div->shift, div->width, field_name, clock_name))
+ if (!bitfield_valid(div->u.s.shift, div->u.s.width,
+ field_name, clock_name))
return false;
if (divider_has_fraction(div))
- if (div->frac_width > div->width) {
+ if (div->u.s.frac_width > div->u.s.width) {
pr_warn("%s: bad %s fraction width for %s (%u > %u)\n",
__func__, field_name, clock_name,
- div->frac_width, div->width);
+ div->u.s.frac_width, div->u.s.width);
return false;
}
@@ -278,7 +279,7 @@ static bool div_valid(struct bcm_clk_div *div, const char *field_name,
*/
static bool kona_dividers_valid(struct kona_clk *bcm_clk)
{
- struct peri_clk_data *peri = bcm_clk->peri;
+ struct peri_clk_data *peri = bcm_clk->u.peri;
struct bcm_clk_div *div;
struct bcm_clk_div *pre_div;
u32 limit;
@@ -295,7 +296,7 @@ static bool kona_dividers_valid(struct kona_clk *bcm_clk)
limit = BITS_PER_BYTE * sizeof(u32);
- return div->frac_width + pre_div->frac_width <= limit;
+ return div->u.s.frac_width + pre_div->u.s.frac_width <= limit;
}
@@ -328,7 +329,7 @@ peri_clk_data_valid(struct kona_clk *bcm_clk)
if (!peri_clk_data_offsets_valid(bcm_clk))
return false;
- peri = bcm_clk->peri;
+ peri = bcm_clk->u.peri;
name = bcm_clk->name;
gate = &peri->gate;
if (gate_exists(gate) && !gate_valid(gate, "gate", name))
@@ -588,12 +589,12 @@ static void bcm_clk_teardown(struct kona_clk *bcm_clk)
{
switch (bcm_clk->type) {
case bcm_clk_peri:
- peri_clk_teardown(bcm_clk->data, &bcm_clk->init_data);
+ peri_clk_teardown(bcm_clk->u.data, &bcm_clk->init_data);
break;
default:
break;
}
- bcm_clk->data = NULL;
+ bcm_clk->u.data = NULL;
bcm_clk->type = bcm_clk_none;
}
@@ -644,7 +645,7 @@ struct clk *kona_clk_setup(struct ccu_data *ccu, const char *name,
break;
}
bcm_clk->type = type;
- bcm_clk->data = data;
+ bcm_clk->u.data = data;
/* Make sure everything makes sense before we set it up */
if (!kona_clk_valid(bcm_clk)) {
diff --git a/drivers/clk/bcm/clk-kona.c b/drivers/clk/bcm/clk-kona.c
index e3d339e08309..db11a87449f2 100644
--- a/drivers/clk/bcm/clk-kona.c
+++ b/drivers/clk/bcm/clk-kona.c
@@ -61,7 +61,7 @@ u64 do_div_round_closest(u64 dividend, unsigned long divisor)
/* Convert a divider into the scaled divisor value it represents. */
static inline u64 scaled_div_value(struct bcm_clk_div *div, u32 reg_div)
{
- return (u64)reg_div + ((u64)1 << div->frac_width);
+ return (u64)reg_div + ((u64)1 << div->u.s.frac_width);
}
/*
@@ -77,7 +77,7 @@ u64 scaled_div_build(struct bcm_clk_div *div, u32 div_value, u32 billionths)
BUG_ON(billionths >= BILLION);
combined = (u64)div_value * BILLION + billionths;
- combined <<= div->frac_width;
+ combined <<= div->u.s.frac_width;
return do_div_round_closest(combined, BILLION);
}
@@ -87,7 +87,7 @@ static inline u64
scaled_div_min(struct bcm_clk_div *div)
{
if (divider_is_fixed(div))
- return (u64)div->fixed;
+ return (u64)div->u.fixed;
return scaled_div_value(div, 0);
}
@@ -98,9 +98,9 @@ u64 scaled_div_max(struct bcm_clk_div *div)
u32 reg_div;
if (divider_is_fixed(div))
- return (u64)div->fixed;
+ return (u64)div->u.fixed;
- reg_div = ((u32)1 << div->width) - 1;
+ reg_div = ((u32)1 << div->u.s.width) - 1;
return scaled_div_value(div, reg_div);
}
@@ -115,7 +115,7 @@ divider(struct bcm_clk_div *div, u64 scaled_div)
BUG_ON(scaled_div < scaled_div_min(div));
BUG_ON(scaled_div > scaled_div_max(div));
- return (u32)(scaled_div - ((u64)1 << div->frac_width));
+ return (u32)(scaled_div - ((u64)1 << div->u.s.frac_width));
}
/* Return a rate scaled for use when dividing by a scaled divisor. */
@@ -125,7 +125,7 @@ scale_rate(struct bcm_clk_div *div, u32 rate)
if (divider_is_fixed(div))
return (u64)rate;
- return (u64)rate << div->frac_width;
+ return (u64)rate << div->u.s.frac_width;
}
/* CCU access */
@@ -398,14 +398,14 @@ static u64 divider_read_scaled(struct ccu_data *ccu, struct bcm_clk_div *div)
u32 reg_div;
if (divider_is_fixed(div))
- return (u64)div->fixed;
+ return (u64)div->u.fixed;
flags = ccu_lock(ccu);
- reg_val = __ccu_read(ccu, div->offset);
+ reg_val = __ccu_read(ccu, div->u.s.offset);
ccu_unlock(ccu, flags);
/* Extract the full divider field from the register value */
- reg_div = bitfield_extract(reg_val, div->shift, div->width);
+ reg_div = bitfield_extract(reg_val, div->u.s.shift, div->u.s.width);
/* Return the scaled divisor value it represents */
return scaled_div_value(div, reg_div);
@@ -433,16 +433,17 @@ static int __div_commit(struct ccu_data *ccu, struct bcm_clk_gate *gate,
* state was defined in the device tree, we just find out
* what its current value is rather than updating it.
*/
- if (div->scaled_div == BAD_SCALED_DIV_VALUE) {
- reg_val = __ccu_read(ccu, div->offset);
- reg_div = bitfield_extract(reg_val, div->shift, div->width);
- div->scaled_div = scaled_div_value(div, reg_div);
+ if (div->u.s.scaled_div == BAD_SCALED_DIV_VALUE) {
+ reg_val = __ccu_read(ccu, div->u.s.offset);
+ reg_div = bitfield_extract(reg_val, div->u.s.shift,
+ div->u.s.width);
+ div->u.s.scaled_div = scaled_div_value(div, reg_div);
return 0;
}
/* Convert the scaled divisor to the value we need to record */
- reg_div = divider(div, div->scaled_div);
+ reg_div = divider(div, div->u.s.scaled_div);
/* Clock needs to be enabled before changing the rate */
enabled = __is_clk_gate_enabled(ccu, gate);
@@ -452,9 +453,10 @@ static int __div_commit(struct ccu_data *ccu, struct bcm_clk_gate *gate,
}
/* Replace the divider value and record the result */
- reg_val = __ccu_read(ccu, div->offset);
- reg_val = bitfield_replace(reg_val, div->shift, div->width, reg_div);
- __ccu_write(ccu, div->offset, reg_val);
+ reg_val = __ccu_read(ccu, div->u.s.offset);
+ reg_val = bitfield_replace(reg_val, div->u.s.shift, div->u.s.width,
+ reg_div);
+ __ccu_write(ccu, div->u.s.offset, reg_val);
/* If the trigger fails we still want to disable the gate */
if (!__clk_trigger(ccu, trig))
@@ -490,11 +492,11 @@ static int divider_write(struct ccu_data *ccu, struct bcm_clk_gate *gate,
BUG_ON(divider_is_fixed(div));
- previous = div->scaled_div;
+ previous = div->u.s.scaled_div;
if (previous == scaled_div)
return 0; /* No change */
- div->scaled_div = scaled_div;
+ div->u.s.scaled_div = scaled_div;
flags = ccu_lock(ccu);
__ccu_write_enable(ccu);
@@ -505,7 +507,7 @@ static int divider_write(struct ccu_data *ccu, struct bcm_clk_gate *gate,
ccu_unlock(ccu, flags);
if (ret)
- div->scaled_div = previous; /* Revert the change */
+ div->u.s.scaled_div = previous; /* Revert the change */
return ret;
@@ -802,7 +804,7 @@ static int selector_write(struct ccu_data *ccu, struct bcm_clk_gate *gate,
static int kona_peri_clk_enable(struct clk_hw *hw)
{
struct kona_clk *bcm_clk = to_kona_clk(hw);
- struct bcm_clk_gate *gate = &bcm_clk->peri->gate;
+ struct bcm_clk_gate *gate = &bcm_clk->u.peri->gate;
return clk_gate(bcm_clk->ccu, bcm_clk->name, gate, true);
}
@@ -810,7 +812,7 @@ static int kona_peri_clk_enable(struct clk_hw *hw)
static void kona_peri_clk_disable(struct clk_hw *hw)
{
struct kona_clk *bcm_clk = to_kona_clk(hw);
- struct bcm_clk_gate *gate = &bcm_clk->peri->gate;
+ struct bcm_clk_gate *gate = &bcm_clk->u.peri->gate;
(void)clk_gate(bcm_clk->ccu, bcm_clk->name, gate, false);
}
@@ -818,7 +820,7 @@ static void kona_peri_clk_disable(struct clk_hw *hw)
static int kona_peri_clk_is_enabled(struct clk_hw *hw)
{
struct kona_clk *bcm_clk = to_kona_clk(hw);
- struct bcm_clk_gate *gate = &bcm_clk->peri->gate;
+ struct bcm_clk_gate *gate = &bcm_clk->u.peri->gate;
return is_clk_gate_enabled(bcm_clk->ccu, gate) ? 1 : 0;
}
@@ -827,7 +829,7 @@ static unsigned long kona_peri_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct kona_clk *bcm_clk = to_kona_clk(hw);
- struct peri_clk_data *data = bcm_clk->peri;
+ struct peri_clk_data *data = bcm_clk->u.peri;
return clk_recalc_rate(bcm_clk->ccu, &data->div, &data->pre_div,
parent_rate);
@@ -837,20 +839,20 @@ static long kona_peri_clk_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct kona_clk *bcm_clk = to_kona_clk(hw);
- struct bcm_clk_div *div = &bcm_clk->peri->div;
+ struct bcm_clk_div *div = &bcm_clk->u.peri->div;
if (!divider_exists(div))
return __clk_get_rate(hw->clk);
/* Quietly avoid a zero rate */
- return round_rate(bcm_clk->ccu, div, &bcm_clk->peri->pre_div,
+ return round_rate(bcm_clk->ccu, div, &bcm_clk->u.peri->pre_div,
rate ? rate : 1, *parent_rate, NULL);
}
static int kona_peri_clk_set_parent(struct clk_hw *hw, u8 index)
{
struct kona_clk *bcm_clk = to_kona_clk(hw);
- struct peri_clk_data *data = bcm_clk->peri;
+ struct peri_clk_data *data = bcm_clk->u.peri;
struct bcm_clk_sel *sel = &data->sel;
struct bcm_clk_trig *trig;
int ret;
@@ -884,7 +886,7 @@ static int kona_peri_clk_set_parent(struct clk_hw *hw, u8 index)
static u8 kona_peri_clk_get_parent(struct clk_hw *hw)
{
struct kona_clk *bcm_clk = to_kona_clk(hw);
- struct peri_clk_data *data = bcm_clk->peri;
+ struct peri_clk_data *data = bcm_clk->u.peri;
u8 index;
index = selector_read_index(bcm_clk->ccu, &data->sel);
@@ -897,7 +899,7 @@ static int kona_peri_clk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct kona_clk *bcm_clk = to_kona_clk(hw);
- struct peri_clk_data *data = bcm_clk->peri;
+ struct peri_clk_data *data = bcm_clk->u.peri;
struct bcm_clk_div *div = &data->div;
u64 scaled_div = 0;
int ret;
@@ -958,7 +960,7 @@ struct clk_ops kona_peri_clk_ops = {
static bool __peri_clk_init(struct kona_clk *bcm_clk)
{
struct ccu_data *ccu = bcm_clk->ccu;
- struct peri_clk_data *peri = bcm_clk->peri;
+ struct peri_clk_data *peri = bcm_clk->u.peri;
const char *name = bcm_clk->name;
struct bcm_clk_trig *trig;
diff --git a/drivers/clk/bcm/clk-kona.h b/drivers/clk/bcm/clk-kona.h
index 5e139adc3dc5..dee690951bb6 100644
--- a/drivers/clk/bcm/clk-kona.h
+++ b/drivers/clk/bcm/clk-kona.h
@@ -57,7 +57,7 @@
#define divider_exists(div) FLAG_TEST(div, DIV, EXISTS)
#define divider_is_fixed(div) FLAG_TEST(div, DIV, FIXED)
#define divider_has_fraction(div) (!divider_is_fixed(div) && \
- (div)->frac_width > 0)
+ (div)->u.s.frac_width > 0)
#define selector_exists(sel) ((sel)->width != 0)
#define trigger_exists(trig) FLAG_TEST(trig, TRIG, EXISTS)
@@ -244,9 +244,9 @@ struct bcm_clk_div {
u32 frac_width; /* field fraction width */
u64 scaled_div; /* scaled divider value */
- };
+ } s;
u32 fixed; /* non-zero fixed divider value */
- };
+ } u;
u32 flags; /* BCM_CLK_DIV_FLAGS_* below */
};
@@ -263,28 +263,28 @@ struct bcm_clk_div {
/* A fixed (non-zero) divider */
#define FIXED_DIVIDER(_value) \
{ \
- .fixed = (_value), \
+ .u.fixed = (_value), \
.flags = FLAG(DIV, EXISTS)|FLAG(DIV, FIXED), \
}
/* A divider with an integral divisor */
#define DIVIDER(_offset, _shift, _width) \
{ \
- .offset = (_offset), \
- .shift = (_shift), \
- .width = (_width), \
- .scaled_div = BAD_SCALED_DIV_VALUE, \
+ .u.s.offset = (_offset), \
+ .u.s.shift = (_shift), \
+ .u.s.width = (_width), \
+ .u.s.scaled_div = BAD_SCALED_DIV_VALUE, \
.flags = FLAG(DIV, EXISTS), \
}
/* A divider whose divisor has an integer and fractional part */
#define FRAC_DIVIDER(_offset, _shift, _width, _frac_width) \
{ \
- .offset = (_offset), \
- .shift = (_shift), \
- .width = (_width), \
- .frac_width = (_frac_width), \
- .scaled_div = BAD_SCALED_DIV_VALUE, \
+ .u.s.offset = (_offset), \
+ .u.s.shift = (_shift), \
+ .u.s.width = (_width), \
+ .u.s.frac_width = (_frac_width), \
+ .u.s.scaled_div = BAD_SCALED_DIV_VALUE, \
.flags = FLAG(DIV, EXISTS), \
}
@@ -380,7 +380,7 @@ struct kona_clk {
union {
void *data;
struct peri_clk_data *peri;
- };
+ } u;
};
#define to_kona_clk(_hw) \
container_of(_hw, struct kona_clk, hw)
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index ec22112e569f..4637697c139f 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -144,6 +144,37 @@ static bool _is_valid_div(struct clk_divider *divider, unsigned int div)
return true;
}
+static int _round_up_table(const struct clk_div_table *table, int div)
+{
+ const struct clk_div_table *clkt;
+ int up = _get_table_maxdiv(table);
+
+ for (clkt = table; clkt->div; clkt++) {
+ if (clkt->div == div)
+ return clkt->div;
+ else if (clkt->div < div)
+ continue;
+
+ if ((clkt->div - div) < (up - div))
+ up = clkt->div;
+ }
+
+ return up;
+}
+
+static int _div_round_up(struct clk_divider *divider,
+ unsigned long parent_rate, unsigned long rate)
+{
+ int div = DIV_ROUND_UP(parent_rate, rate);
+
+ if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
+ div = __roundup_pow_of_two(div);
+ if (divider->table)
+ div = _round_up_table(divider->table, div);
+
+ return div;
+}
+
static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
unsigned long *best_parent_rate)
{
@@ -159,7 +190,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) {
parent_rate = *best_parent_rate;
- bestdiv = DIV_ROUND_UP(parent_rate, rate);
+ bestdiv = _div_round_up(divider, parent_rate, rate);
bestdiv = bestdiv == 0 ? 1 : bestdiv;
bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv;
return bestdiv;
@@ -219,6 +250,10 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
u32 val;
div = DIV_ROUND_UP(parent_rate, rate);
+
+ if (!_is_valid_div(divider, div))
+ return -EINVAL;
+
value = _get_val(divider, div);
if (value > div_mask(divider))
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index dff0373f53c1..7cf2c093cc54 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1984,9 +1984,28 @@ struct clk *__clk_register(struct device *dev, struct clk_hw *hw)
}
EXPORT_SYMBOL_GPL(__clk_register);
-static int _clk_register(struct device *dev, struct clk_hw *hw, struct clk *clk)
+/**
+ * clk_register - allocate a new clock, register it and return an opaque cookie
+ * @dev: device that is registering this clock
+ * @hw: link to hardware-specific clock data
+ *
+ * clk_register is the primary interface for populating the clock tree with new
+ * clock nodes. It returns a pointer to the newly allocated struct clk which
+ * cannot be dereferenced by driver code but may be used in conjuction with the
+ * rest of the clock API. In the event of an error clk_register will return an
+ * error code; drivers must test for an error code after calling clk_register.
+ */
+struct clk *clk_register(struct device *dev, struct clk_hw *hw)
{
int i, ret;
+ struct clk *clk;
+
+ clk = kzalloc(sizeof(*clk), GFP_KERNEL);
+ if (!clk) {
+ pr_err("%s: could not allocate clk\n", __func__);
+ ret = -ENOMEM;
+ goto fail_out;
+ }
clk->name = kstrdup(hw->init->name, GFP_KERNEL);
if (!clk->name) {
@@ -2026,7 +2045,7 @@ static int _clk_register(struct device *dev, struct clk_hw *hw, struct clk *clk)
ret = __clk_init(dev, clk);
if (!ret)
- return 0;
+ return clk;
fail_parent_names_copy:
while (--i >= 0)
@@ -2035,36 +2054,6 @@ fail_parent_names_copy:
fail_parent_names:
kfree(clk->name);
fail_name:
- return ret;
-}
-
-/**
- * clk_register - allocate a new clock, register it and return an opaque cookie
- * @dev: device that is registering this clock
- * @hw: link to hardware-specific clock data
- *
- * clk_register is the primary interface for populating the clock tree with new
- * clock nodes. It returns a pointer to the newly allocated struct clk which
- * cannot be dereferenced by driver code but may be used in conjuction with the
- * rest of the clock API. In the event of an error clk_register will return an
- * error code; drivers must test for an error code after calling clk_register.
- */
-struct clk *clk_register(struct device *dev, struct clk_hw *hw)
-{
- int ret;
- struct clk *clk;
-
- clk = kzalloc(sizeof(*clk), GFP_KERNEL);
- if (!clk) {
- pr_err("%s: could not allocate clk\n", __func__);
- ret = -ENOMEM;
- goto fail_out;
- }
-
- ret = _clk_register(dev, hw, clk);
- if (!ret)
- return clk;
-
kfree(clk);
fail_out:
return ERR_PTR(ret);
@@ -2151,9 +2140,10 @@ void clk_unregister(struct clk *clk)
if (!hlist_empty(&clk->children)) {
struct clk *child;
+ struct hlist_node *t;
/* Reparent all children to the orphan list. */
- hlist_for_each_entry(child, &clk->children, child_node)
+ hlist_for_each_entry_safe(child, t, &clk->children, child_node)
clk_set_parent(child, NULL);
}
@@ -2173,7 +2163,7 @@ EXPORT_SYMBOL_GPL(clk_unregister);
static void devm_clk_release(struct device *dev, void *res)
{
- clk_unregister(res);
+ clk_unregister(*(struct clk **)res);
}
/**
@@ -2188,18 +2178,18 @@ static void devm_clk_release(struct device *dev, void *res)
struct clk *devm_clk_register(struct device *dev, struct clk_hw *hw)
{
struct clk *clk;
- int ret;
+ struct clk **clkp;
- clk = devres_alloc(devm_clk_release, sizeof(*clk), GFP_KERNEL);
- if (!clk)
+ clkp = devres_alloc(devm_clk_release, sizeof(*clkp), GFP_KERNEL);
+ if (!clkp)
return ERR_PTR(-ENOMEM);
- ret = _clk_register(dev, hw, clk);
- if (!ret) {
- devres_add(dev, clk);
+ clk = clk_register(dev, hw);
+ if (!IS_ERR(clk)) {
+ *clkp = clk;
+ devres_add(dev, clkp);
} else {
- devres_free(clk);
- clk = ERR_PTR(ret);
+ devres_free(clkp);
}
return clk;
diff --git a/drivers/clk/shmobile/clk-mstp.c b/drivers/clk/shmobile/clk-mstp.c
index 2e5810c88d11..1f6324e29a80 100644
--- a/drivers/clk/shmobile/clk-mstp.c
+++ b/drivers/clk/shmobile/clk-mstp.c
@@ -156,6 +156,7 @@ cpg_mstp_clock_register(const char *name, const char *parent_name,
static void __init cpg_mstp_clocks_init(struct device_node *np)
{
struct mstp_clock_group *group;
+ const char *idxname;
struct clk **clks;
unsigned int i;
@@ -184,6 +185,11 @@ static void __init cpg_mstp_clocks_init(struct device_node *np)
for (i = 0; i < MSTP_MAX_CLOCKS; ++i)
clks[i] = ERR_PTR(-ENOENT);
+ if (of_find_property(np, "clock-indices", &i))
+ idxname = "clock-indices";
+ else
+ idxname = "renesas,clock-indices";
+
for (i = 0; i < MSTP_MAX_CLOCKS; ++i) {
const char *parent_name;
const char *name;
@@ -197,8 +203,7 @@ static void __init cpg_mstp_clocks_init(struct device_node *np)
continue;
parent_name = of_clk_get_parent_name(np, i);
- ret = of_property_read_u32_index(np, "renesas,clock-indices", i,
- &clkidx);
+ ret = of_property_read_u32_index(np, idxname, i, &clkidx);
if (parent_name == NULL || ret < 0)
break;
diff --git a/drivers/clk/socfpga/clk-pll.c b/drivers/clk/socfpga/clk-pll.c
index 88dafb5e9627..de6da957a09d 100644
--- a/drivers/clk/socfpga/clk-pll.c
+++ b/drivers/clk/socfpga/clk-pll.c
@@ -20,6 +20,7 @@
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/of.h>
+#include <linux/of_address.h>
#include "clk.h"
@@ -43,6 +44,8 @@
#define to_socfpga_clk(p) container_of(p, struct socfpga_pll, hw.hw)
+void __iomem *clk_mgr_base_addr;
+
static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk,
unsigned long parent_rate)
{
@@ -87,6 +90,7 @@ static __init struct clk *__socfpga_pll_init(struct device_node *node,
const char *clk_name = node->name;
const char *parent_name[SOCFPGA_MAX_PARENTS];
struct clk_init_data init;
+ struct device_node *clkmgr_np;
int rc;
int i = 0;
@@ -96,6 +100,9 @@ static __init struct clk *__socfpga_pll_init(struct device_node *node,
if (WARN_ON(!pll_clk))
return NULL;
+ clkmgr_np = of_find_compatible_node(NULL, NULL, "altr,clk-mgr");
+ clk_mgr_base_addr = of_iomap(clkmgr_np, 0);
+ BUG_ON(!clk_mgr_base_addr);
pll_clk->hw.reg = clk_mgr_base_addr + reg;
of_property_read_string(node, "clock-output-names", &clk_name);
diff --git a/drivers/clk/socfpga/clk.c b/drivers/clk/socfpga/clk.c
index 35a960a993f9..43db947e5f0e 100644
--- a/drivers/clk/socfpga/clk.c
+++ b/drivers/clk/socfpga/clk.c
@@ -17,28 +17,11 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <linux/clk.h>
-#include <linux/clkdev.h>
-#include <linux/clk-provider.h>
-#include <linux/io.h>
#include <linux/of.h>
-#include <linux/of_address.h>
#include "clk.h"
-void __iomem *clk_mgr_base_addr;
-
-static const struct of_device_id socfpga_child_clocks[] __initconst = {
- { .compatible = "altr,socfpga-pll-clock", socfpga_pll_init, },
- { .compatible = "altr,socfpga-perip-clk", socfpga_periph_init, },
- { .compatible = "altr,socfpga-gate-clk", socfpga_gate_init, },
- {},
-};
-
-static void __init socfpga_clkmgr_init(struct device_node *node)
-{
- clk_mgr_base_addr = of_iomap(node, 0);
- of_clk_init(socfpga_child_clocks);
-}
-CLK_OF_DECLARE(socfpga_mgr, "altr,clk-mgr", socfpga_clkmgr_init);
+CLK_OF_DECLARE(socfpga_pll_clk, "altr,socfpga-pll-clock", socfpga_pll_init);
+CLK_OF_DECLARE(socfpga_perip_clk, "altr,socfpga-perip-clk", socfpga_periph_init);
+CLK_OF_DECLARE(socfpga_gate_clk, "altr,socfpga-gate-clk", socfpga_gate_init);
diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c
index 0d20241e0770..e1769addf435 100644
--- a/drivers/clk/tegra/clk-pll.c
+++ b/drivers/clk/tegra/clk-pll.c
@@ -1718,7 +1718,7 @@ struct clk *tegra_clk_register_plle_tegra114(const char *name,
"pll_re_vco");
} else {
val_aux &= ~(PLLE_AUX_PLLRE_SEL | PLLE_AUX_PLLP_SEL);
- pll_writel(val, pll_params->aux_reg, pll);
+ pll_writel(val_aux, pll_params->aux_reg, pll);
}
clk = _tegra_clk_register_pll(pll, name, parent_name, flags,
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index 099967302bf2..eab8ccfe6beb 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -37,6 +37,7 @@
#define BYT_RATIOS 0x66a
#define BYT_VIDS 0x66b
#define BYT_TURBO_RATIOS 0x66c
+#define BYT_TURBO_VIDS 0x66d
#define FRAC_BITS 6
@@ -70,8 +71,9 @@ struct pstate_data {
};
struct vid_data {
- int32_t min;
- int32_t max;
+ int min;
+ int max;
+ int turbo;
int32_t ratio;
};
@@ -359,14 +361,14 @@ static int byt_get_min_pstate(void)
{
u64 value;
rdmsrl(BYT_RATIOS, value);
- return (value >> 8) & 0xFF;
+ return (value >> 8) & 0x3F;
}
static int byt_get_max_pstate(void)
{
u64 value;
rdmsrl(BYT_RATIOS, value);
- return (value >> 16) & 0xFF;
+ return (value >> 16) & 0x3F;
}
static int byt_get_turbo_pstate(void)
@@ -393,6 +395,9 @@ static void byt_set_pstate(struct cpudata *cpudata, int pstate)
vid_fp = clamp_t(int32_t, vid_fp, cpudata->vid.min, cpudata->vid.max);
vid = fp_toint(vid_fp);
+ if (pstate > cpudata->pstate.max_pstate)
+ vid = cpudata->vid.turbo;
+
val |= vid;
wrmsrl(MSR_IA32_PERF_CTL, val);
@@ -402,13 +407,17 @@ static void byt_get_vid(struct cpudata *cpudata)
{
u64 value;
+
rdmsrl(BYT_VIDS, value);
- cpudata->vid.min = int_tofp((value >> 8) & 0x7f);
- cpudata->vid.max = int_tofp((value >> 16) & 0x7f);
+ cpudata->vid.min = int_tofp((value >> 8) & 0x3f);
+ cpudata->vid.max = int_tofp((value >> 16) & 0x3f);
cpudata->vid.ratio = div_fp(
cpudata->vid.max - cpudata->vid.min,
int_tofp(cpudata->pstate.max_pstate -
cpudata->pstate.min_pstate));
+
+ rdmsrl(BYT_TURBO_VIDS, value);
+ cpudata->vid.turbo = value & 0x7f;
}
@@ -545,12 +554,7 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu)
if (pstate_funcs.get_vid)
pstate_funcs.get_vid(cpu);
-
- /*
- * goto max pstate so we don't slow up boot if we are built-in if we are
- * a module we will take care of it during normal operation
- */
- intel_pstate_set_pstate(cpu, cpu->pstate.max_pstate);
+ intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate);
}
static inline void intel_pstate_calc_busy(struct cpudata *cpu,
@@ -695,11 +699,6 @@ static int intel_pstate_init_cpu(unsigned int cpunum)
cpu = all_cpu_data[cpunum];
intel_pstate_get_cpu_pstates(cpu);
- if (!cpu->pstate.current_pstate) {
- all_cpu_data[cpunum] = NULL;
- kfree(cpu);
- return -ENODATA;
- }
cpu->cpu = cpunum;
@@ -710,7 +709,6 @@ static int intel_pstate_init_cpu(unsigned int cpunum)
cpu->timer.expires = jiffies + HZ/100;
intel_pstate_busy_pid_reset(cpu);
intel_pstate_sample(cpu);
- intel_pstate_set_pstate(cpu, cpu->pstate.max_pstate);
add_timer_on(&cpu->timer, cpunum);
diff --git a/drivers/cpufreq/loongson2_cpufreq.c b/drivers/cpufreq/loongson2_cpufreq.c
index f0bc31f5db27..d4add8621944 100644
--- a/drivers/cpufreq/loongson2_cpufreq.c
+++ b/drivers/cpufreq/loongson2_cpufreq.c
@@ -62,7 +62,7 @@ static int loongson2_cpufreq_target(struct cpufreq_policy *policy,
set_cpus_allowed_ptr(current, &cpus_allowed);
/* setting the cpu frequency */
- clk_set_rate(policy->clk, freq);
+ clk_set_rate(policy->clk, freq * 1000);
return 0;
}
@@ -92,7 +92,7 @@ static int loongson2_cpufreq_cpu_init(struct cpufreq_policy *policy)
i++)
loongson2_clockmod_table[i].frequency = (rate * i) / 8;
- ret = clk_set_rate(cpuclk, rate);
+ ret = clk_set_rate(cpuclk, rate * 1000);
if (ret) {
clk_put(cpuclk);
return ret;
diff --git a/drivers/crypto/caam/error.c b/drivers/crypto/caam/error.c
index 9f25f5296029..0eabd81e1a90 100644
--- a/drivers/crypto/caam/error.c
+++ b/drivers/crypto/caam/error.c
@@ -16,9 +16,13 @@
char *tmp; \
\
tmp = kmalloc(sizeof(format) + max_alloc, GFP_ATOMIC); \
- sprintf(tmp, format, param); \
- strcat(str, tmp); \
- kfree(tmp); \
+ if (likely(tmp)) { \
+ sprintf(tmp, format, param); \
+ strcat(str, tmp); \
+ kfree(tmp); \
+ } else { \
+ strcat(str, "kmalloc failure in SPRINTFCAT"); \
+ } \
}
static void report_jump_idx(u32 status, char *outstr)
diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h
index c98764aeeec6..870044e82316 100644
--- a/drivers/firewire/core.h
+++ b/drivers/firewire/core.h
@@ -118,7 +118,6 @@ int fw_card_add(struct fw_card *card,
u32 max_receive, u32 link_speed, u64 guid);
void fw_core_remove_card(struct fw_card *card);
int fw_compute_block_crc(__be32 *block);
-void fw_schedule_bus_reset(struct fw_card *card, bool delayed, bool short_reset);
void fw_schedule_bm_work(struct fw_card *card, unsigned long delay);
/* -cdev */
diff --git a/drivers/firmware/iscsi_ibft.c b/drivers/firmware/iscsi_ibft.c
index 3ee852c9925b..071c2c969eec 100644
--- a/drivers/firmware/iscsi_ibft.c
+++ b/drivers/firmware/iscsi_ibft.c
@@ -756,6 +756,7 @@ static const struct {
*/
{ ACPI_SIG_IBFT },
{ "iBFT" },
+ { "BIFT" }, /* Broadcom iSCSI Offload */
};
static void __init acpi_find_ibft_region(void)
diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c
index e73c6755a5eb..70304220a479 100644
--- a/drivers/gpio/gpio-ich.c
+++ b/drivers/gpio/gpio-ich.c
@@ -305,6 +305,8 @@ static struct ichx_desc ich6_desc = {
.ngpio = 50,
.have_blink = true,
+ .regs = ichx_regs,
+ .reglen = ichx_reglen,
};
/* Intel 3100 */
@@ -324,6 +326,8 @@ static struct ichx_desc i3100_desc = {
.uses_gpe0 = true,
.ngpio = 50,
+ .regs = ichx_regs,
+ .reglen = ichx_reglen,
};
/* ICH7 and ICH8-based */
diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c
index 99a68310e7c0..3d53fd6880d1 100644
--- a/drivers/gpio/gpio-mcp23s08.c
+++ b/drivers/gpio/gpio-mcp23s08.c
@@ -894,9 +894,11 @@ static int mcp23s08_probe(struct spi_device *spi)
dev_err(&spi->dev, "invalid spi-present-mask\n");
return -ENODEV;
}
-
- for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++)
+ for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) {
+ if ((spi_present_mask & (1 << addr)))
+ chips++;
pullups[addr] = 0;
+ }
} else {
type = spi_get_device_id(spi)->driver_data;
pdata = dev_get_platdata(&spi->dev);
@@ -919,12 +921,12 @@ static int mcp23s08_probe(struct spi_device *spi)
pullups[addr] = pdata->chip[addr].pullups;
}
- if (!chips)
- return -ENODEV;
-
base = pdata->base;
}
+ if (!chips)
+ return -ENODEV;
+
data = kzalloc(sizeof(*data) + chips * sizeof(struct mcp23s08),
GFP_KERNEL);
if (!data)
diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
index fa486c5fbb02..aff4a113cda3 100644
--- a/drivers/gpu/drm/i915/intel_bios.c
+++ b/drivers/gpu/drm/i915/intel_bios.c
@@ -560,47 +560,71 @@ parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
dev_priv->vbt.edp_pps = *edp_pps;
- dev_priv->vbt.edp_rate = edp_link_params->rate ? DP_LINK_BW_2_7 :
- DP_LINK_BW_1_62;
+ switch (edp_link_params->rate) {
+ case EDP_RATE_1_62:
+ dev_priv->vbt.edp_rate = DP_LINK_BW_1_62;
+ break;
+ case EDP_RATE_2_7:
+ dev_priv->vbt.edp_rate = DP_LINK_BW_2_7;
+ break;
+ default:
+ DRM_DEBUG_KMS("VBT has unknown eDP link rate value %u\n",
+ edp_link_params->rate);
+ break;
+ }
+
switch (edp_link_params->lanes) {
- case 0:
+ case EDP_LANE_1:
dev_priv->vbt.edp_lanes = 1;
break;
- case 1:
+ case EDP_LANE_2:
dev_priv->vbt.edp_lanes = 2;
break;
- case 3:
- default:
+ case EDP_LANE_4:
dev_priv->vbt.edp_lanes = 4;
break;
+ default:
+ DRM_DEBUG_KMS("VBT has unknown eDP lane count value %u\n",
+ edp_link_params->lanes);
+ break;
}
+
switch (edp_link_params->preemphasis) {
- case 0:
+ case EDP_PREEMPHASIS_NONE:
dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_0;
break;
- case 1:
+ case EDP_PREEMPHASIS_3_5dB:
dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_3_5;
break;
- case 2:
+ case EDP_PREEMPHASIS_6dB:
dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_6;
break;
- case 3:
+ case EDP_PREEMPHASIS_9_5dB:
dev_priv->vbt.edp_preemphasis = DP_TRAIN_PRE_EMPHASIS_9_5;
break;
+ default:
+ DRM_DEBUG_KMS("VBT has unknown eDP pre-emphasis value %u\n",
+ edp_link_params->preemphasis);
+ break;
}
+
switch (edp_link_params->vswing) {
- case 0:
+ case EDP_VSWING_0_4V:
dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_400;
break;
- case 1:
+ case EDP_VSWING_0_6V:
dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_600;
break;
- case 2:
+ case EDP_VSWING_0_8V:
dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_800;
break;
- case 3:
+ case EDP_VSWING_1_2V:
dev_priv->vbt.edp_vswing = DP_TRAIN_VOLTAGE_SWING_1200;
break;
+ default:
+ DRM_DEBUG_KMS("VBT has unknown eDP voltage swing value %u\n",
+ edp_link_params->vswing);
+ break;
}
}
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 5ca68aa9f237..2a00cb828d20 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -121,6 +121,22 @@ intel_dp_max_link_bw(struct intel_dp *intel_dp)
return max_link_bw;
}
+static u8 intel_dp_max_lane_count(struct intel_dp *intel_dp)
+{
+ struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+ struct drm_device *dev = intel_dig_port->base.base.dev;
+ u8 source_max, sink_max;
+
+ source_max = 4;
+ if (HAS_DDI(dev) && intel_dig_port->port == PORT_A &&
+ (intel_dig_port->saved_port_bits & DDI_A_4_LANES) == 0)
+ source_max = 2;
+
+ sink_max = drm_dp_max_lane_count(intel_dp->dpcd);
+
+ return min(source_max, sink_max);
+}
+
/*
* The units on the numbers in the next two are... bizarre. Examples will
* make it clearer; this one parallels an example in the eDP spec.
@@ -171,7 +187,7 @@ intel_dp_mode_valid(struct drm_connector *connector,
}
max_link_clock = drm_dp_bw_code_to_link_rate(intel_dp_max_link_bw(intel_dp));
- max_lanes = drm_dp_max_lane_count(intel_dp->dpcd);
+ max_lanes = intel_dp_max_lane_count(intel_dp);
max_rate = intel_dp_max_data_rate(max_link_clock, max_lanes);
mode_rate = intel_dp_link_required(target_clock, 18);
@@ -751,8 +767,10 @@ intel_dp_compute_config(struct intel_encoder *encoder,
struct intel_crtc *intel_crtc = encoder->new_crtc;
struct intel_connector *intel_connector = intel_dp->attached_connector;
int lane_count, clock;
- int max_lane_count = drm_dp_max_lane_count(intel_dp->dpcd);
+ int min_lane_count = 1;
+ int max_lane_count = intel_dp_max_lane_count(intel_dp);
/* Conveniently, the link BW constants become indices with a shift...*/
+ int min_clock = 0;
int max_clock = intel_dp_max_link_bw(intel_dp) >> 3;
int bpp, mode_rate;
static int bws[] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7, DP_LINK_BW_5_4 };
@@ -785,19 +803,38 @@ intel_dp_compute_config(struct intel_encoder *encoder,
/* Walk through all bpp values. Luckily they're all nicely spaced with 2
* bpc in between. */
bpp = pipe_config->pipe_bpp;
- if (is_edp(intel_dp) && dev_priv->vbt.edp_bpp &&
- dev_priv->vbt.edp_bpp < bpp) {
- DRM_DEBUG_KMS("clamping bpp for eDP panel to BIOS-provided %i\n",
- dev_priv->vbt.edp_bpp);
- bpp = dev_priv->vbt.edp_bpp;
+ if (is_edp(intel_dp)) {
+ if (dev_priv->vbt.edp_bpp && dev_priv->vbt.edp_bpp < bpp) {
+ DRM_DEBUG_KMS("clamping bpp for eDP panel to BIOS-provided %i\n",
+ dev_priv->vbt.edp_bpp);
+ bpp = dev_priv->vbt.edp_bpp;
+ }
+
+ if (IS_BROADWELL(dev)) {
+ /* Yes, it's an ugly hack. */
+ min_lane_count = max_lane_count;
+ DRM_DEBUG_KMS("forcing lane count to max (%u) on BDW\n",
+ min_lane_count);
+ } else if (dev_priv->vbt.edp_lanes) {
+ min_lane_count = min(dev_priv->vbt.edp_lanes,
+ max_lane_count);
+ DRM_DEBUG_KMS("using min %u lanes per VBT\n",
+ min_lane_count);
+ }
+
+ if (dev_priv->vbt.edp_rate) {
+ min_clock = min(dev_priv->vbt.edp_rate >> 3, max_clock);
+ DRM_DEBUG_KMS("using min %02x link bw per VBT\n",
+ bws[min_clock]);
+ }
}
for (; bpp >= 6*3; bpp -= 2*3) {
mode_rate = intel_dp_link_required(adjusted_mode->crtc_clock,
bpp);
- for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) {
- for (clock = 0; clock <= max_clock; clock++) {
+ for (lane_count = min_lane_count; lane_count <= max_lane_count; lane_count <<= 1) {
+ for (clock = min_clock; clock <= max_clock; clock++) {
link_clock = drm_dp_bw_code_to_link_rate(bws[clock]);
link_avail = intel_dp_max_data_rate(link_clock,
lane_count);
diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
index fce4a0d93c0b..f73ba5e6b7a8 100644
--- a/drivers/gpu/drm/i915/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/intel_fbdev.c
@@ -387,6 +387,15 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
height);
}
+ /* No preferred mode marked by the EDID? Are there any modes? */
+ if (!modes[i] && !list_empty(&connector->modes)) {
+ DRM_DEBUG_KMS("using first mode listed on connector %s\n",
+ drm_get_connector_name(connector));
+ modes[i] = list_first_entry(&connector->modes,
+ struct drm_display_mode,
+ head);
+ }
+
/* last resort: use current mode */
if (!modes[i]) {
/*
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
index 0eead16aeda7..cb8cfb7e0974 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -492,6 +492,7 @@ void intel_panel_set_backlight(struct intel_connector *connector, u32 level,
enum pipe pipe = intel_get_pipe_from_connector(connector);
u32 freq;
unsigned long flags;
+ u64 n;
if (!panel->backlight.present || pipe == INVALID_PIPE)
return;
@@ -502,10 +503,9 @@ void intel_panel_set_backlight(struct intel_connector *connector, u32 level,
/* scale to hardware max, but be careful to not overflow */
freq = panel->backlight.max;
- if (freq < max)
- level = level * freq / max;
- else
- level = freq / max * level;
+ n = (u64)level * freq;
+ do_div(n, max);
+ level = n;
panel->backlight.level = level;
if (panel->backlight.device)
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 19e94c3edc19..d93dcf683e8c 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -2095,6 +2095,43 @@ static void intel_print_wm_latency(struct drm_device *dev,
}
}
+static bool ilk_increase_wm_latency(struct drm_i915_private *dev_priv,
+ uint16_t wm[5], uint16_t min)
+{
+ int level, max_level = ilk_wm_max_level(dev_priv->dev);
+
+ if (wm[0] >= min)
+ return false;
+
+ wm[0] = max(wm[0], min);
+ for (level = 1; level <= max_level; level++)
+ wm[level] = max_t(uint16_t, wm[level], DIV_ROUND_UP(min, 5));
+
+ return true;
+}
+
+static void snb_wm_latency_quirk(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ bool changed;
+
+ /*
+ * The BIOS provided WM memory latency values are often
+ * inadequate for high resolution displays. Adjust them.
+ */
+ changed = ilk_increase_wm_latency(dev_priv, dev_priv->wm.pri_latency, 12) |
+ ilk_increase_wm_latency(dev_priv, dev_priv->wm.spr_latency, 12) |
+ ilk_increase_wm_latency(dev_priv, dev_priv->wm.cur_latency, 12);
+
+ if (!changed)
+ return;
+
+ DRM_DEBUG_KMS("WM latency values increased to avoid potential underruns\n");
+ intel_print_wm_latency(dev, "Primary", dev_priv->wm.pri_latency);
+ intel_print_wm_latency(dev, "Sprite", dev_priv->wm.spr_latency);
+ intel_print_wm_latency(dev, "Cursor", dev_priv->wm.cur_latency);
+}
+
static void ilk_setup_wm_latency(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2112,6 +2149,9 @@ static void ilk_setup_wm_latency(struct drm_device *dev)
intel_print_wm_latency(dev, "Primary", dev_priv->wm.pri_latency);
intel_print_wm_latency(dev, "Sprite", dev_priv->wm.spr_latency);
intel_print_wm_latency(dev, "Cursor", dev_priv->wm.cur_latency);
+
+ if (IS_GEN6(dev))
+ snb_wm_latency_quirk(dev);
}
static void ilk_compute_wm_parameters(struct drm_crtc *crtc,
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index d27155adf5db..46be00d66df3 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -2424,8 +2424,8 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector,
if (ret < 0)
goto err1;
- ret = sysfs_create_link(&encoder->ddc.dev.kobj,
- &drm_connector->kdev->kobj,
+ ret = sysfs_create_link(&drm_connector->kdev->kobj,
+ &encoder->ddc.dev.kobj,
encoder->ddc.dev.kobj.name);
if (ret < 0)
goto err2;
diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c
index f729dc71d5be..d0c75779d3f6 100644
--- a/drivers/gpu/drm/i915/intel_uncore.c
+++ b/drivers/gpu/drm/i915/intel_uncore.c
@@ -185,6 +185,8 @@ static void vlv_force_wake_reset(struct drm_i915_private *dev_priv)
{
__raw_i915_write32(dev_priv, FORCEWAKE_VLV,
_MASKED_BIT_DISABLE(0xffff));
+ __raw_i915_write32(dev_priv, FORCEWAKE_MEDIA_VLV,
+ _MASKED_BIT_DISABLE(0xffff));
/* something from same cacheline, but !FORCEWAKE_VLV */
__raw_posting_read(dev_priv, FORCEWAKE_ACK_VLV);
}
diff --git a/drivers/hwmon/emc1403.c b/drivers/hwmon/emc1403.c
index 90ec1173b8a1..01723f04fe45 100644
--- a/drivers/hwmon/emc1403.c
+++ b/drivers/hwmon/emc1403.c
@@ -163,7 +163,7 @@ static ssize_t store_hyst(struct device *dev,
if (retval < 0)
goto fail;
- hyst = val - retval * 1000;
+ hyst = retval * 1000 - val;
hyst = DIV_ROUND_CLOSEST(hyst, 1000);
if (hyst < 0 || hyst > 255) {
retval = -ERANGE;
@@ -330,7 +330,7 @@ static int emc1403_detect(struct i2c_client *client,
}
id = i2c_smbus_read_byte_data(client, THERMAL_REVISION_REG);
- if (id != 0x01)
+ if (id < 0x01 || id > 0x04)
return -ENODEV;
return 0;
@@ -355,9 +355,9 @@ static int emc1403_probe(struct i2c_client *client,
if (id->driver_data)
data->groups[1] = &emc1404_group;
- hwmon_dev = hwmon_device_register_with_groups(&client->dev,
- client->name, data,
- data->groups);
+ hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
+ client->name, data,
+ data->groups);
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c
index 22e92c3d3d07..3c20e4bd6dd1 100644
--- a/drivers/i2c/busses/i2c-designware-core.c
+++ b/drivers/i2c/busses/i2c-designware-core.c
@@ -422,6 +422,9 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
*/
dw_writel(dev, msgs[dev->msg_write_idx].addr | ic_tar, DW_IC_TAR);
+ /* enforce disabled interrupts (due to HW issues) */
+ i2c_dw_disable_int(dev);
+
/* Enable the adapter */
__i2c_dw_enable(dev, true);
diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c
index 28cbe1b2a2ec..32c85e9ecdae 100644
--- a/drivers/i2c/busses/i2c-nomadik.c
+++ b/drivers/i2c/busses/i2c-nomadik.c
@@ -999,7 +999,7 @@ static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id)
dev->virtbase = devm_ioremap(&adev->dev, adev->res.start,
resource_size(&adev->res));
- if (IS_ERR(dev->virtbase)) {
+ if (!dev->virtbase) {
ret = -ENOMEM;
goto err_no_mem;
}
diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index 1b4cf14f1106..2a5efb5b487c 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -479,7 +479,7 @@ static int qup_i2c_xfer(struct i2c_adapter *adap,
int ret, idx;
ret = pm_runtime_get_sync(qup->dev);
- if (ret)
+ if (ret < 0)
goto out;
writel(1, qup->base + QUP_SW_RESET);
diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c
index d4fa8eba6e9d..06d47aafbb79 100644
--- a/drivers/i2c/busses/i2c-rcar.c
+++ b/drivers/i2c/busses/i2c-rcar.c
@@ -561,6 +561,12 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
ret = -EINVAL;
for (i = 0; i < num; i++) {
+ /* This HW can't send STOP after address phase */
+ if (msgs[i].len == 0) {
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
/*-------------- spin lock -----------------*/
spin_lock_irqsave(&priv->lock, flags);
@@ -625,7 +631,8 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
static u32 rcar_i2c_func(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+ /* This HW can't do SMBUS_QUICK and NOSTART */
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
}
static const struct i2c_algorithm rcar_i2c_algo = {
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index ae4491062e41..bb3a9964f7e0 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -1276,10 +1276,10 @@ static int s3c24xx_i2c_resume(struct device *dev)
struct platform_device *pdev = to_platform_device(dev);
struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);
- i2c->suspended = 0;
clk_prepare_enable(i2c->clk);
s3c24xx_i2c_init(i2c);
clk_disable_unprepare(i2c->clk);
+ i2c->suspended = 0;
return 0;
}
diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c
index c98fdb185931..a1710465faaf 100644
--- a/drivers/infiniband/ulp/isert/ib_isert.c
+++ b/drivers/infiniband/ulp/isert/ib_isert.c
@@ -28,6 +28,7 @@
#include <target/target_core_base.h>
#include <target/target_core_fabric.h>
#include <target/iscsi/iscsi_transport.h>
+#include <linux/semaphore.h>
#include "isert_proto.h"
#include "ib_isert.h"
@@ -561,7 +562,15 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
struct isert_device *device;
struct ib_device *ib_dev = cma_id->device;
int ret = 0;
- u8 pi_support = np->tpg_np->tpg->tpg_attrib.t10_pi;
+ u8 pi_support;
+
+ spin_lock_bh(&np->np_thread_lock);
+ if (!np->enabled) {
+ spin_unlock_bh(&np->np_thread_lock);
+ pr_debug("iscsi_np is not enabled, reject connect request\n");
+ return rdma_reject(cma_id, NULL, 0);
+ }
+ spin_unlock_bh(&np->np_thread_lock);
pr_debug("Entering isert_connect_request cma_id: %p, context: %p\n",
cma_id, cma_id->context);
@@ -652,6 +661,7 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
goto out_mr;
}
+ pi_support = np->tpg_np->tpg->tpg_attrib.t10_pi;
if (pi_support && !device->pi_capable) {
pr_err("Protection information requested but not supported\n");
ret = -EINVAL;
@@ -663,11 +673,11 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
goto out_conn_dev;
mutex_lock(&isert_np->np_accept_mutex);
- list_add_tail(&isert_np->np_accept_list, &isert_conn->conn_accept_node);
+ list_add_tail(&isert_conn->conn_accept_node, &isert_np->np_accept_list);
mutex_unlock(&isert_np->np_accept_mutex);
- pr_debug("isert_connect_request() waking up np_accept_wq: %p\n", np);
- wake_up(&isert_np->np_accept_wq);
+ pr_debug("isert_connect_request() up np_sem np: %p\n", np);
+ up(&isert_np->np_sem);
return 0;
out_conn_dev:
@@ -2999,7 +3009,7 @@ isert_setup_np(struct iscsi_np *np,
pr_err("Unable to allocate struct isert_np\n");
return -ENOMEM;
}
- init_waitqueue_head(&isert_np->np_accept_wq);
+ sema_init(&isert_np->np_sem, 0);
mutex_init(&isert_np->np_accept_mutex);
INIT_LIST_HEAD(&isert_np->np_accept_list);
init_completion(&isert_np->np_login_comp);
@@ -3048,18 +3058,6 @@ out:
}
static int
-isert_check_accept_queue(struct isert_np *isert_np)
-{
- int empty;
-
- mutex_lock(&isert_np->np_accept_mutex);
- empty = list_empty(&isert_np->np_accept_list);
- mutex_unlock(&isert_np->np_accept_mutex);
-
- return empty;
-}
-
-static int
isert_rdma_accept(struct isert_conn *isert_conn)
{
struct rdma_cm_id *cm_id = isert_conn->conn_cm_id;
@@ -3151,16 +3149,14 @@ isert_accept_np(struct iscsi_np *np, struct iscsi_conn *conn)
int max_accept = 0, ret;
accept_wait:
- ret = wait_event_interruptible(isert_np->np_accept_wq,
- !isert_check_accept_queue(isert_np) ||
- np->np_thread_state == ISCSI_NP_THREAD_RESET);
+ ret = down_interruptible(&isert_np->np_sem);
if (max_accept > 5)
return -ENODEV;
spin_lock_bh(&np->np_thread_lock);
if (np->np_thread_state == ISCSI_NP_THREAD_RESET) {
spin_unlock_bh(&np->np_thread_lock);
- pr_err("ISCSI_NP_THREAD_RESET for isert_accept_np\n");
+ pr_debug("ISCSI_NP_THREAD_RESET for isert_accept_np\n");
return -ENODEV;
}
spin_unlock_bh(&np->np_thread_lock);
diff --git a/drivers/infiniband/ulp/isert/ib_isert.h b/drivers/infiniband/ulp/isert/ib_isert.h
index 4c072ae34c01..da6612e68000 100644
--- a/drivers/infiniband/ulp/isert/ib_isert.h
+++ b/drivers/infiniband/ulp/isert/ib_isert.h
@@ -182,7 +182,7 @@ struct isert_device {
};
struct isert_np {
- wait_queue_head_t np_accept_wq;
+ struct semaphore np_sem;
struct rdma_cm_id *np_cm_id;
struct mutex np_accept_mutex;
struct list_head np_accept_list;
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index c949520bd196..57068e8035b5 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -3999,7 +3999,7 @@ static struct irq_remap_table *get_irq_table(u16 devid, bool ioapic)
iommu_flush_dte(iommu, devid);
if (devid != alias) {
irq_lookup_table[alias] = table;
- set_dte_irq_entry(devid, table);
+ set_dte_irq_entry(alias, table);
iommu_flush_dte(iommu, alias);
}
diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c
index b76c58dbe30c..0e08545d7298 100644
--- a/drivers/iommu/amd_iommu_init.c
+++ b/drivers/iommu/amd_iommu_init.c
@@ -788,7 +788,7 @@ static void __init set_device_exclusion_range(u16 devid, struct ivmd_header *m)
* per device. But we can enable the exclusion range per
* device. This is done here
*/
- set_dev_entry_bit(m->devid, DEV_ENTRY_EX);
+ set_dev_entry_bit(devid, DEV_ENTRY_EX);
iommu->exclusion_start = m->range_start;
iommu->exclusion_length = m->range_length;
}
diff --git a/drivers/iommu/amd_iommu_v2.c b/drivers/iommu/amd_iommu_v2.c
index 5208828792e6..203b2e6a91cf 100644
--- a/drivers/iommu/amd_iommu_v2.c
+++ b/drivers/iommu/amd_iommu_v2.c
@@ -504,8 +504,10 @@ static void do_fault(struct work_struct *work)
write = !!(fault->flags & PPR_FAULT_WRITE);
+ down_read(&fault->state->mm->mmap_sem);
npages = get_user_pages(fault->state->task, fault->state->mm,
fault->address, 1, write, 0, &page, NULL);
+ up_read(&fault->state->mm->mmap_sem);
if (npages == 1) {
put_page(page);
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 784695d22fde..53b213226c01 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -19,7 +19,6 @@
#include <linux/crypto.h>
#include <linux/workqueue.h>
#include <linux/backing-dev.h>
-#include <linux/percpu.h>
#include <linux/atomic.h>
#include <linux/scatterlist.h>
#include <asm/page.h>
@@ -43,6 +42,7 @@ struct convert_context {
struct bvec_iter iter_out;
sector_t cc_sector;
atomic_t cc_pending;
+ struct ablkcipher_request *req;
};
/*
@@ -111,15 +111,7 @@ struct iv_tcw_private {
enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID };
/*
- * Duplicated per-CPU state for cipher.
- */
-struct crypt_cpu {
- struct ablkcipher_request *req;
-};
-
-/*
- * The fields in here must be read only after initialization,
- * changing state should be in crypt_cpu.
+ * The fields in here must be read only after initialization.
*/
struct crypt_config {
struct dm_dev *dev;
@@ -150,12 +142,6 @@ struct crypt_config {
sector_t iv_offset;
unsigned int iv_size;
- /*
- * Duplicated per cpu state. Access through
- * per_cpu_ptr() only.
- */
- struct crypt_cpu __percpu *cpu;
-
/* ESSIV: struct crypto_cipher *essiv_tfm */
void *iv_private;
struct crypto_ablkcipher **tfms;
@@ -192,11 +178,6 @@ static void clone_init(struct dm_crypt_io *, struct bio *);
static void kcryptd_queue_crypt(struct dm_crypt_io *io);
static u8 *iv_of_dmreq(struct crypt_config *cc, struct dm_crypt_request *dmreq);
-static struct crypt_cpu *this_crypt_config(struct crypt_config *cc)
-{
- return this_cpu_ptr(cc->cpu);
-}
-
/*
* Use this to access cipher attributes that are the same for each CPU.
*/
@@ -903,16 +884,15 @@ static void kcryptd_async_done(struct crypto_async_request *async_req,
static void crypt_alloc_req(struct crypt_config *cc,
struct convert_context *ctx)
{
- struct crypt_cpu *this_cc = this_crypt_config(cc);
unsigned key_index = ctx->cc_sector & (cc->tfms_count - 1);
- if (!this_cc->req)
- this_cc->req = mempool_alloc(cc->req_pool, GFP_NOIO);
+ if (!ctx->req)
+ ctx->req = mempool_alloc(cc->req_pool, GFP_NOIO);
- ablkcipher_request_set_tfm(this_cc->req, cc->tfms[key_index]);
- ablkcipher_request_set_callback(this_cc->req,
+ ablkcipher_request_set_tfm(ctx->req, cc->tfms[key_index]);
+ ablkcipher_request_set_callback(ctx->req,
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
- kcryptd_async_done, dmreq_of_req(cc, this_cc->req));
+ kcryptd_async_done, dmreq_of_req(cc, ctx->req));
}
/*
@@ -921,7 +901,6 @@ static void crypt_alloc_req(struct crypt_config *cc,
static int crypt_convert(struct crypt_config *cc,
struct convert_context *ctx)
{
- struct crypt_cpu *this_cc = this_crypt_config(cc);
int r;
atomic_set(&ctx->cc_pending, 1);
@@ -932,7 +911,7 @@ static int crypt_convert(struct crypt_config *cc,
atomic_inc(&ctx->cc_pending);
- r = crypt_convert_block(cc, ctx, this_cc->req);
+ r = crypt_convert_block(cc, ctx, ctx->req);
switch (r) {
/* async */
@@ -941,7 +920,7 @@ static int crypt_convert(struct crypt_config *cc,
reinit_completion(&ctx->restart);
/* fall through*/
case -EINPROGRESS:
- this_cc->req = NULL;
+ ctx->req = NULL;
ctx->cc_sector++;
continue;
@@ -1040,6 +1019,7 @@ static struct dm_crypt_io *crypt_io_alloc(struct crypt_config *cc,
io->sector = sector;
io->error = 0;
io->base_io = NULL;
+ io->ctx.req = NULL;
atomic_set(&io->io_pending, 0);
return io;
@@ -1065,6 +1045,8 @@ static void crypt_dec_pending(struct dm_crypt_io *io)
if (!atomic_dec_and_test(&io->io_pending))
return;
+ if (io->ctx.req)
+ mempool_free(io->ctx.req, cc->req_pool);
mempool_free(io, cc->io_pool);
if (likely(!base_io))
@@ -1492,8 +1474,6 @@ static int crypt_wipe_key(struct crypt_config *cc)
static void crypt_dtr(struct dm_target *ti)
{
struct crypt_config *cc = ti->private;
- struct crypt_cpu *cpu_cc;
- int cpu;
ti->private = NULL;
@@ -1505,13 +1485,6 @@ static void crypt_dtr(struct dm_target *ti)
if (cc->crypt_queue)
destroy_workqueue(cc->crypt_queue);
- if (cc->cpu)
- for_each_possible_cpu(cpu) {
- cpu_cc = per_cpu_ptr(cc->cpu, cpu);
- if (cpu_cc->req)
- mempool_free(cpu_cc->req, cc->req_pool);
- }
-
crypt_free_tfms(cc);
if (cc->bs)
@@ -1530,9 +1503,6 @@ static void crypt_dtr(struct dm_target *ti)
if (cc->dev)
dm_put_device(ti, cc->dev);
- if (cc->cpu)
- free_percpu(cc->cpu);
-
kzfree(cc->cipher);
kzfree(cc->cipher_string);
@@ -1588,13 +1558,6 @@ static int crypt_ctr_cipher(struct dm_target *ti,
if (tmp)
DMWARN("Ignoring unexpected additional cipher options");
- cc->cpu = __alloc_percpu(sizeof(*(cc->cpu)),
- __alignof__(struct crypt_cpu));
- if (!cc->cpu) {
- ti->error = "Cannot allocate per cpu state";
- goto bad_mem;
- }
-
/*
* For compatibility with the original dm-crypt mapping format, if
* only the cipher name is supplied, use cbc-plain.
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index aa009e865871..fa0f6cbd6a41 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -1566,8 +1566,8 @@ static int multipath_ioctl(struct dm_target *ti, unsigned int cmd,
}
if (m->pg_init_required)
__pg_init_all_paths(m);
- spin_unlock_irqrestore(&m->lock, flags);
dm_table_run_md_queue_async(m->ti->table);
+ spin_unlock_irqrestore(&m->lock, flags);
}
return r ? : __blkdev_driver_ioctl(bdev, mode, cmd, arg);
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index 13abade76ad9..2e71de8e0048 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -27,6 +27,7 @@
#define MAPPING_POOL_SIZE 1024
#define PRISON_CELLS 1024
#define COMMIT_PERIOD HZ
+#define NO_SPACE_TIMEOUT (HZ * 60)
DECLARE_DM_KCOPYD_THROTTLE_WITH_MODULE_PARM(snapshot_copy_throttle,
"A percentage of time allocated for copy on write");
@@ -175,6 +176,7 @@ struct pool {
struct workqueue_struct *wq;
struct work_struct worker;
struct delayed_work waker;
+ struct delayed_work no_space_timeout;
unsigned long last_commit_jiffies;
unsigned ref_count;
@@ -935,7 +937,7 @@ static int commit(struct pool *pool)
{
int r;
- if (get_pool_mode(pool) != PM_WRITE)
+ if (get_pool_mode(pool) >= PM_READ_ONLY)
return -EINVAL;
r = dm_pool_commit_metadata(pool->pmd);
@@ -1590,6 +1592,20 @@ static void do_waker(struct work_struct *ws)
queue_delayed_work(pool->wq, &pool->waker, COMMIT_PERIOD);
}
+/*
+ * We're holding onto IO to allow userland time to react. After the
+ * timeout either the pool will have been resized (and thus back in
+ * PM_WRITE mode), or we degrade to PM_READ_ONLY and start erroring IO.
+ */
+static void do_no_space_timeout(struct work_struct *ws)
+{
+ struct pool *pool = container_of(to_delayed_work(ws), struct pool,
+ no_space_timeout);
+
+ if (get_pool_mode(pool) == PM_OUT_OF_DATA_SPACE && !pool->pf.error_if_no_space)
+ set_pool_mode(pool, PM_READ_ONLY);
+}
+
/*----------------------------------------------------------------*/
struct noflush_work {
@@ -1715,6 +1731,9 @@ static void set_pool_mode(struct pool *pool, enum pool_mode new_mode)
pool->process_discard = process_discard;
pool->process_prepared_mapping = process_prepared_mapping;
pool->process_prepared_discard = process_prepared_discard_passdown;
+
+ if (!pool->pf.error_if_no_space)
+ queue_delayed_work(pool->wq, &pool->no_space_timeout, NO_SPACE_TIMEOUT);
break;
case PM_WRITE:
@@ -2100,6 +2119,7 @@ static struct pool *pool_create(struct mapped_device *pool_md,
INIT_WORK(&pool->worker, do_worker);
INIT_DELAYED_WORK(&pool->waker, do_waker);
+ INIT_DELAYED_WORK(&pool->no_space_timeout, do_no_space_timeout);
spin_lock_init(&pool->lock);
bio_list_init(&pool->deferred_flush_bios);
INIT_LIST_HEAD(&pool->prepared_mappings);
@@ -2662,6 +2682,7 @@ static void pool_postsuspend(struct dm_target *ti)
struct pool *pool = pt->pool;
cancel_delayed_work(&pool->waker);
+ cancel_delayed_work(&pool->no_space_timeout);
flush_workqueue(pool->wq);
(void) commit(pool);
}
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 8fda38d23e38..237b7e0ddc7a 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -8516,7 +8516,8 @@ static int md_notify_reboot(struct notifier_block *this,
if (mddev_trylock(mddev)) {
if (mddev->pers)
__md_stop_writes(mddev);
- mddev->safemode = 2;
+ if (mddev->persistent)
+ mddev->safemode = 2;
mddev_unlock(mddev);
}
need_delay = 1;
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 33fc408e5eac..cb882aae9e20 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1172,6 +1172,13 @@ static void __make_request(struct mddev *mddev, struct bio *bio)
int max_sectors;
int sectors;
+ /*
+ * Register the new request and wait if the reconstruction
+ * thread has put up a bar for new requests.
+ * Continue immediately if no resync is active currently.
+ */
+ wait_barrier(conf);
+
sectors = bio_sectors(bio);
while (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) &&
bio->bi_iter.bi_sector < conf->reshape_progress &&
@@ -1552,12 +1559,6 @@ static void make_request(struct mddev *mddev, struct bio *bio)
md_write_start(mddev, bio);
- /*
- * Register the new request and wait if the reconstruction
- * thread has put up a bar for new requests.
- * Continue immediately if no resync is active currently.
- */
- wait_barrier(conf);
do {
diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c
index e8a1ce204036..cdd7c1b7259b 100644
--- a/drivers/media/i2c/ov7670.c
+++ b/drivers/media/i2c/ov7670.c
@@ -1109,7 +1109,7 @@ static int ov7670_enum_framesizes(struct v4l2_subdev *sd,
* windows that fall outside that.
*/
for (i = 0; i < n_win_sizes; i++) {
- struct ov7670_win_size *win = &info->devtype->win_sizes[index];
+ struct ov7670_win_size *win = &info->devtype->win_sizes[i];
if (info->min_width && win->width < info->min_width)
continue;
if (info->min_height && win->height < info->min_height)
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
index a4459301b5f8..ee0f57e01b56 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
@@ -1616,7 +1616,7 @@ static int s5c73m3_get_platform_data(struct s5c73m3 *state)
if (ret < 0)
return -EINVAL;
- node_ep = v4l2_of_get_next_endpoint(node, NULL);
+ node_ep = of_graph_get_next_endpoint(node, NULL);
if (!node_ep) {
dev_warn(dev, "no endpoint defined for node: %s\n",
node->full_name);
diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
index d5a7a135f75d..703560fa5e73 100644
--- a/drivers/media/media-device.c
+++ b/drivers/media/media-device.c
@@ -93,6 +93,7 @@ static long media_device_enum_entities(struct media_device *mdev,
struct media_entity *ent;
struct media_entity_desc u_ent;
+ memset(&u_ent, 0, sizeof(u_ent));
if (copy_from_user(&u_ent.id, &uent->id, sizeof(u_ent.id)))
return -EFAULT;
diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c
index b4f12d00be05..656708252962 100644
--- a/drivers/media/platform/davinci/vpbe_display.c
+++ b/drivers/media/platform/davinci/vpbe_display.c
@@ -372,18 +372,32 @@ static int vpbe_stop_streaming(struct vb2_queue *vq)
{
struct vpbe_fh *fh = vb2_get_drv_priv(vq);
struct vpbe_layer *layer = fh->layer;
+ struct vpbe_display *disp = fh->disp_dev;
+ unsigned long flags;
if (!vb2_is_streaming(vq))
return 0;
/* release all active buffers */
+ spin_lock_irqsave(&disp->dma_queue_lock, flags);
+ if (layer->cur_frm == layer->next_frm) {
+ vb2_buffer_done(&layer->cur_frm->vb, VB2_BUF_STATE_ERROR);
+ } else {
+ if (layer->cur_frm != NULL)
+ vb2_buffer_done(&layer->cur_frm->vb,
+ VB2_BUF_STATE_ERROR);
+ if (layer->next_frm != NULL)
+ vb2_buffer_done(&layer->next_frm->vb,
+ VB2_BUF_STATE_ERROR);
+ }
+
while (!list_empty(&layer->dma_queue)) {
layer->next_frm = list_entry(layer->dma_queue.next,
struct vpbe_disp_buffer, list);
list_del(&layer->next_frm->list);
vb2_buffer_done(&layer->next_frm->vb, VB2_BUF_STATE_ERROR);
}
-
+ spin_unlock_irqrestore(&disp->dma_queue_lock, flags);
return 0;
}
diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c
index d762246eabf5..0379cb9f9a9c 100644
--- a/drivers/media/platform/davinci/vpfe_capture.c
+++ b/drivers/media/platform/davinci/vpfe_capture.c
@@ -734,6 +734,8 @@ static int vpfe_release(struct file *file)
}
vpfe_dev->io_usrs = 0;
vpfe_dev->numbuffers = config_params.numbuffers;
+ videobuf_stop(&vpfe_dev->buffer_queue);
+ videobuf_mmap_free(&vpfe_dev->buffer_queue);
}
/* Decrement device usrs counter */
diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c
index 756da78bac23..8dea0b84a3ad 100644
--- a/drivers/media/platform/davinci/vpif_capture.c
+++ b/drivers/media/platform/davinci/vpif_capture.c
@@ -358,8 +358,31 @@ static int vpif_stop_streaming(struct vb2_queue *vq)
common = &ch->common[VPIF_VIDEO_INDEX];
+ /* Disable channel as per its device type and channel id */
+ if (VPIF_CHANNEL0_VIDEO == ch->channel_id) {
+ enable_channel0(0);
+ channel0_intr_enable(0);
+ }
+ if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) ||
+ (2 == common->started)) {
+ enable_channel1(0);
+ channel1_intr_enable(0);
+ }
+ common->started = 0;
+
/* release all active buffers */
spin_lock_irqsave(&common->irqlock, flags);
+ if (common->cur_frm == common->next_frm) {
+ vb2_buffer_done(&common->cur_frm->vb, VB2_BUF_STATE_ERROR);
+ } else {
+ if (common->cur_frm != NULL)
+ vb2_buffer_done(&common->cur_frm->vb,
+ VB2_BUF_STATE_ERROR);
+ if (common->next_frm != NULL)
+ vb2_buffer_done(&common->next_frm->vb,
+ VB2_BUF_STATE_ERROR);
+ }
+
while (!list_empty(&common->dma_queue)) {
common->next_frm = list_entry(common->dma_queue.next,
struct vpif_cap_buffer, list);
@@ -933,17 +956,6 @@ static int vpif_release(struct file *filep)
if (fh->io_allowed[VPIF_VIDEO_INDEX]) {
/* Reset io_usrs member of channel object */
common->io_usrs = 0;
- /* Disable channel as per its device type and channel id */
- if (VPIF_CHANNEL0_VIDEO == ch->channel_id) {
- enable_channel0(0);
- channel0_intr_enable(0);
- }
- if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) ||
- (2 == common->started)) {
- enable_channel1(0);
- channel1_intr_enable(0);
- }
- common->started = 0;
/* Free buffers allocated */
vb2_queue_release(&common->buffer_queue);
vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c
index 0ac841e35aa4..aed41edd0501 100644
--- a/drivers/media/platform/davinci/vpif_display.c
+++ b/drivers/media/platform/davinci/vpif_display.c
@@ -320,8 +320,31 @@ static int vpif_stop_streaming(struct vb2_queue *vq)
common = &ch->common[VPIF_VIDEO_INDEX];
+ /* Disable channel */
+ if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
+ enable_channel2(0);
+ channel2_intr_enable(0);
+ }
+ if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) ||
+ (2 == common->started)) {
+ enable_channel3(0);
+ channel3_intr_enable(0);
+ }
+ common->started = 0;
+
/* release all active buffers */
spin_lock_irqsave(&common->irqlock, flags);
+ if (common->cur_frm == common->next_frm) {
+ vb2_buffer_done(&common->cur_frm->vb, VB2_BUF_STATE_ERROR);
+ } else {
+ if (common->cur_frm != NULL)
+ vb2_buffer_done(&common->cur_frm->vb,
+ VB2_BUF_STATE_ERROR);
+ if (common->next_frm != NULL)
+ vb2_buffer_done(&common->next_frm->vb,
+ VB2_BUF_STATE_ERROR);
+ }
+
while (!list_empty(&common->dma_queue)) {
common->next_frm = list_entry(common->dma_queue.next,
struct vpif_disp_buffer, list);
@@ -773,18 +796,6 @@ static int vpif_release(struct file *filep)
if (fh->io_allowed[VPIF_VIDEO_INDEX]) {
/* Reset io_usrs member of channel object */
common->io_usrs = 0;
- /* Disable channel */
- if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
- enable_channel2(0);
- channel2_intr_enable(0);
- }
- if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) ||
- (2 == common->started)) {
- enable_channel3(0);
- channel3_intr_enable(0);
- }
- common->started = 0;
-
/* Free buffers allocated */
vb2_queue_release(&common->buffer_queue);
vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c
index da2fc86cc524..25dbf5b05a96 100644
--- a/drivers/media/platform/exynos4-is/fimc-core.c
+++ b/drivers/media/platform/exynos4-is/fimc-core.c
@@ -122,7 +122,7 @@ static struct fimc_fmt fimc_formats[] = {
}, {
.name = "YUV 4:2:2 planar, Y/Cb/Cr",
.fourcc = V4L2_PIX_FMT_YUV422P,
- .depth = { 12 },
+ .depth = { 16 },
.color = FIMC_FMT_YCBYCR422,
.memplanes = 1,
.colplanes = 3,
diff --git a/drivers/media/tuners/fc2580.c b/drivers/media/tuners/fc2580.c
index 3aecaf465094..f0c9c42867de 100644
--- a/drivers/media/tuners/fc2580.c
+++ b/drivers/media/tuners/fc2580.c
@@ -195,7 +195,7 @@ static int fc2580_set_params(struct dvb_frontend *fe)
f_ref = 2UL * priv->cfg->clock / r_val;
n_val = div_u64_rem(f_vco, f_ref, &k_val);
- k_val_reg = 1UL * k_val * (1 << 20) / f_ref;
+ k_val_reg = div_u64(1ULL * k_val * (1 << 20), f_ref);
ret = fc2580_wr_reg(priv, 0x18, r18_val | ((k_val_reg >> 16) & 0xff));
if (ret < 0)
@@ -348,8 +348,8 @@ static int fc2580_set_params(struct dvb_frontend *fe)
if (ret < 0)
goto err;
- ret = fc2580_wr_reg(priv, 0x37, 1UL * priv->cfg->clock * \
- fc2580_if_filter_lut[i].mul / 1000000000);
+ ret = fc2580_wr_reg(priv, 0x37, div_u64(1ULL * priv->cfg->clock *
+ fc2580_if_filter_lut[i].mul, 1000000000));
if (ret < 0)
goto err;
diff --git a/drivers/media/tuners/fc2580_priv.h b/drivers/media/tuners/fc2580_priv.h
index be38a9e637e0..646c99452136 100644
--- a/drivers/media/tuners/fc2580_priv.h
+++ b/drivers/media/tuners/fc2580_priv.h
@@ -22,6 +22,7 @@
#define FC2580_PRIV_H
#include "fc2580.h"
+#include <linux/math64.h>
struct fc2580_reg_val {
u8 reg;
diff --git a/drivers/media/usb/dvb-usb-v2/Makefile b/drivers/media/usb/dvb-usb-v2/Makefile
index 7407b8338ccf..bc38f03394cd 100644
--- a/drivers/media/usb/dvb-usb-v2/Makefile
+++ b/drivers/media/usb/dvb-usb-v2/Makefile
@@ -41,4 +41,3 @@ ccflags-y += -I$(srctree)/drivers/media/dvb-core
ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
ccflags-y += -I$(srctree)/drivers/media/tuners
ccflags-y += -I$(srctree)/drivers/media/common
-ccflags-y += -I$(srctree)/drivers/staging/media/rtl2832u_sdr
diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
index 61d196e8b3ab..dcbd392e6efc 100644
--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
@@ -24,7 +24,6 @@
#include "rtl2830.h"
#include "rtl2832.h"
-#include "rtl2832_sdr.h"
#include "qt1010.h"
#include "mt2060.h"
@@ -36,6 +35,45 @@
#include "tua9001.h"
#include "r820t.h"
+/*
+ * RTL2832_SDR module is in staging. That logic is added in order to avoid any
+ * hard dependency to drivers/staging/ directory as we want compile mainline
+ * driver even whole staging directory is missing.
+ */
+#include <media/v4l2-subdev.h>
+
+#if IS_ENABLED(CONFIG_DVB_RTL2832_SDR)
+struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, const struct rtl2832_config *cfg,
+ struct v4l2_subdev *sd);
+#else
+static inline struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, const struct rtl2832_config *cfg,
+ struct v4l2_subdev *sd)
+{
+ return NULL;
+}
+#endif
+
+#ifdef CONFIG_MEDIA_ATTACH
+#define dvb_attach_sdr(FUNCTION, ARGS...) ({ \
+ void *__r = NULL; \
+ typeof(&FUNCTION) __a = symbol_request(FUNCTION); \
+ if (__a) { \
+ __r = (void *) __a(ARGS); \
+ if (__r == NULL) \
+ symbol_put(FUNCTION); \
+ } \
+ __r; \
+})
+
+#else
+#define dvb_attach_sdr(FUNCTION, ARGS...) ({ \
+ FUNCTION(ARGS); \
+})
+
+#endif
+
static int rtl28xxu_disable_rc;
module_param_named(disable_rc, rtl28xxu_disable_rc, int, 0644);
MODULE_PARM_DESC(disable_rc, "disable RTL2832U remote controller");
@@ -908,7 +946,7 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
adap->fe[0]->ops.tuner_ops.get_rf_strength;
/* attach SDR */
- dvb_attach(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap,
+ dvb_attach_sdr(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap,
&rtl28xxu_rtl2832_fc0012_config, NULL);
break;
case TUNER_RTL2832_FC0013:
@@ -920,7 +958,7 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
adap->fe[0]->ops.tuner_ops.get_rf_strength;
/* attach SDR */
- dvb_attach(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap,
+ dvb_attach_sdr(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap,
&rtl28xxu_rtl2832_fc0013_config, NULL);
break;
case TUNER_RTL2832_E4000: {
@@ -951,7 +989,7 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
i2c_set_adapdata(i2c_adap_internal, d);
/* attach SDR */
- dvb_attach(rtl2832_sdr_attach, adap->fe[0],
+ dvb_attach_sdr(rtl2832_sdr_attach, adap->fe[0],
i2c_adap_internal,
&rtl28xxu_rtl2832_e4000_config, sd);
}
@@ -982,7 +1020,7 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
adap->fe[0]->ops.tuner_ops.get_rf_strength;
/* attach SDR */
- dvb_attach(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap,
+ dvb_attach_sdr(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap,
&rtl28xxu_rtl2832_r820t_config, NULL);
break;
case TUNER_RTL2832_R828D:
diff --git a/drivers/media/usb/gspca/sonixb.c b/drivers/media/usb/gspca/sonixb.c
index 7277dbd2afcd..ecbcb39feb71 100644
--- a/drivers/media/usb/gspca/sonixb.c
+++ b/drivers/media/usb/gspca/sonixb.c
@@ -1430,10 +1430,8 @@ static const struct usb_device_id device_table[] = {
{USB_DEVICE(0x0c45, 0x600d), SB(PAS106, 101)},
{USB_DEVICE(0x0c45, 0x6011), SB(OV6650, 101)},
{USB_DEVICE(0x0c45, 0x6019), SB(OV7630, 101)},
-#if !IS_ENABLED(CONFIG_USB_SN9C102)
{USB_DEVICE(0x0c45, 0x6024), SB(TAS5130CXX, 102)},
{USB_DEVICE(0x0c45, 0x6025), SB(TAS5130CXX, 102)},
-#endif
{USB_DEVICE(0x0c45, 0x6027), SB(OV7630, 101)}, /* Genius Eye 310 */
{USB_DEVICE(0x0c45, 0x6028), SB(PAS202, 102)},
{USB_DEVICE(0x0c45, 0x6029), SB(PAS106, 102)},
diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index 04b2daf567be..7e2411c36419 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -178,6 +178,9 @@ struct v4l2_create_buffers32 {
static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
{
+ if (get_user(kp->type, &up->type))
+ return -EFAULT;
+
switch (kp->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
@@ -204,17 +207,16 @@ static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __us
static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
{
- if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)) ||
- get_user(kp->type, &up->type))
- return -EFAULT;
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)))
+ return -EFAULT;
return __get_v4l2_format32(kp, up);
}
static int get_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
{
if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_create_buffers32)) ||
- copy_from_user(kp, up, offsetof(struct v4l2_create_buffers32, format.fmt)))
- return -EFAULT;
+ copy_from_user(kp, up, offsetof(struct v4l2_create_buffers32, format)))
+ return -EFAULT;
return __get_v4l2_format32(&kp->format, &up->format);
}
diff --git a/drivers/memory/mvebu-devbus.c b/drivers/memory/mvebu-devbus.c
index 110c03627051..b59a17fb7c3e 100644
--- a/drivers/memory/mvebu-devbus.c
+++ b/drivers/memory/mvebu-devbus.c
@@ -108,8 +108,19 @@ static int devbus_set_timing_params(struct devbus *devbus,
node->full_name);
return err;
}
- /* Convert bit width to byte width */
- r.bus_width /= 8;
+
+ /*
+ * The bus width is encoded into the register as 0 for 8 bits,
+ * and 1 for 16 bits, so we do the necessary conversion here.
+ */
+ if (r.bus_width == 8)
+ r.bus_width = 0;
+ else if (r.bus_width == 16)
+ r.bus_width = 1;
+ else {
+ dev_err(devbus->dev, "invalid bus width %d\n", r.bus_width);
+ return -EINVAL;
+ }
err = get_timing_param_ps(devbus, node, "devbus,badr-skew-ps",
&r.badr_skew);
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 6d4ee22708c9..32e969d95319 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -1831,6 +1831,10 @@ int of_update_property(struct device_node *np, struct property *newprop)
if (!found)
return -ENODEV;
+ /* At early boot, bail out and defer setup to of_init() */
+ if (!of_kset)
+ return found ? 0 : -ENODEV;
+
/* Update the sysfs attribute */
sysfs_remove_bin_file(&np->kobj, &oldprop->attr);
__of_add_property_sysfs(np, newprop);
diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c
index d3d1cfd51e09..e384e2534594 100644
--- a/drivers/pci/host/pci-mvebu.c
+++ b/drivers/pci/host/pci-mvebu.c
@@ -293,6 +293,58 @@ static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port,
return PCIBIOS_SUCCESSFUL;
}
+/*
+ * Remove windows, starting from the largest ones to the smallest
+ * ones.
+ */
+static void mvebu_pcie_del_windows(struct mvebu_pcie_port *port,
+ phys_addr_t base, size_t size)
+{
+ while (size) {
+ size_t sz = 1 << (fls(size) - 1);
+
+ mvebu_mbus_del_window(base, sz);
+ base += sz;
+ size -= sz;
+ }
+}
+
+/*
+ * MBus windows can only have a power of two size, but PCI BARs do not
+ * have this constraint. Therefore, we have to split the PCI BAR into
+ * areas each having a power of two size. We start from the largest
+ * one (i.e highest order bit set in the size).
+ */
+static void mvebu_pcie_add_windows(struct mvebu_pcie_port *port,
+ unsigned int target, unsigned int attribute,
+ phys_addr_t base, size_t size,
+ phys_addr_t remap)
+{
+ size_t size_mapped = 0;
+
+ while (size) {
+ size_t sz = 1 << (fls(size) - 1);
+ int ret;
+
+ ret = mvebu_mbus_add_window_remap_by_id(target, attribute, base,
+ sz, remap);
+ if (ret) {
+ dev_err(&port->pcie->pdev->dev,
+ "Could not create MBus window at 0x%x, size 0x%x: %d\n",
+ base, sz, ret);
+ mvebu_pcie_del_windows(port, base - size_mapped,
+ size_mapped);
+ return;
+ }
+
+ size -= sz;
+ size_mapped += sz;
+ base += sz;
+ if (remap != MVEBU_MBUS_NO_REMAP)
+ remap += sz;
+ }
+}
+
static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
{
phys_addr_t iobase;
@@ -304,8 +356,8 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
/* If a window was configured, remove it */
if (port->iowin_base) {
- mvebu_mbus_del_window(port->iowin_base,
- port->iowin_size);
+ mvebu_pcie_del_windows(port, port->iowin_base,
+ port->iowin_size);
port->iowin_base = 0;
port->iowin_size = 0;
}
@@ -331,11 +383,11 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
port->iowin_base = port->pcie->io.start + iobase;
port->iowin_size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) |
(port->bridge.iolimitupper << 16)) -
- iobase);
+ iobase) + 1;
- mvebu_mbus_add_window_remap_by_id(port->io_target, port->io_attr,
- port->iowin_base, port->iowin_size,
- iobase);
+ mvebu_pcie_add_windows(port, port->io_target, port->io_attr,
+ port->iowin_base, port->iowin_size,
+ iobase);
}
static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
@@ -346,8 +398,8 @@ static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
/* If a window was configured, remove it */
if (port->memwin_base) {
- mvebu_mbus_del_window(port->memwin_base,
- port->memwin_size);
+ mvebu_pcie_del_windows(port, port->memwin_base,
+ port->memwin_size);
port->memwin_base = 0;
port->memwin_size = 0;
}
@@ -364,10 +416,11 @@ static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
port->memwin_base = ((port->bridge.membase & 0xFFF0) << 16);
port->memwin_size =
(((port->bridge.memlimit & 0xFFF0) << 16) | 0xFFFFF) -
- port->memwin_base;
+ port->memwin_base + 1;
- mvebu_mbus_add_window_by_id(port->mem_target, port->mem_attr,
- port->memwin_base, port->memwin_size);
+ mvebu_pcie_add_windows(port, port->mem_target, port->mem_attr,
+ port->memwin_base, port->memwin_size,
+ MVEBU_MBUS_NO_REMAP);
}
/*
@@ -743,14 +796,21 @@ static resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev,
/*
* On the PCI-to-PCI bridge side, the I/O windows must have at
- * least a 64 KB size and be aligned on their size, and the
- * memory windows must have at least a 1 MB size and be
- * aligned on their size
+ * least a 64 KB size and the memory windows must have at
+ * least a 1 MB size. Moreover, MBus windows need to have a
+ * base address aligned on their size, and their size must be
+ * a power of two. This means that if the BAR doesn't have a
+ * power of two size, several MBus windows will actually be
+ * created. We need to ensure that the biggest MBus window
+ * (which will be the first one) is aligned on its size, which
+ * explains the rounddown_pow_of_two() being done here.
*/
if (res->flags & IORESOURCE_IO)
- return round_up(start, max_t(resource_size_t, SZ_64K, size));
+ return round_up(start, max_t(resource_size_t, SZ_64K,
+ rounddown_pow_of_two(size)));
else if (res->flags & IORESOURCE_MEM)
- return round_up(start, max_t(resource_size_t, SZ_1M, size));
+ return round_up(start, max_t(resource_size_t, SZ_1M,
+ rounddown_pow_of_two(size)));
else
return start;
}
diff --git a/drivers/pci/hotplug/shpchp_ctrl.c b/drivers/pci/hotplug/shpchp_ctrl.c
index 58499277903a..6efc2ec5e4db 100644
--- a/drivers/pci/hotplug/shpchp_ctrl.c
+++ b/drivers/pci/hotplug/shpchp_ctrl.c
@@ -282,8 +282,8 @@ static int board_added(struct slot *p_slot)
return WRONG_BUS_FREQUENCY;
}
- bsp = ctrl->pci_dev->bus->cur_bus_speed;
- msp = ctrl->pci_dev->bus->max_bus_speed;
+ bsp = ctrl->pci_dev->subordinate->cur_bus_speed;
+ msp = ctrl->pci_dev->subordinate->max_bus_speed;
/* Check if there are other slots or devices on the same bus */
if (!list_empty(&ctrl->pci_dev->subordinate->devices))
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 7325d43bf030..759475ef6ff3 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -3067,7 +3067,8 @@ int pci_wait_for_pending_transaction(struct pci_dev *dev)
if (!pci_is_pcie(dev))
return 1;
- return pci_wait_for_pending(dev, PCI_EXP_DEVSTA, PCI_EXP_DEVSTA_TRPND);
+ return pci_wait_for_pending(dev, pci_pcie_cap(dev) + PCI_EXP_DEVSTA,
+ PCI_EXP_DEVSTA_TRPND);
}
EXPORT_SYMBOL(pci_wait_for_pending_transaction);
@@ -3109,7 +3110,7 @@ static int pci_af_flr(struct pci_dev *dev, int probe)
return 0;
/* Wait for Transaction Pending bit clean */
- if (pci_wait_for_pending(dev, PCI_AF_STATUS, PCI_AF_STATUS_TP))
+ if (pci_wait_for_pending(dev, pos + PCI_AF_STATUS, PCI_AF_STATUS_TP))
goto clear;
dev_err(&dev->dev, "transaction is not cleared; "
diff --git a/drivers/rtc/rtc-hym8563.c b/drivers/rtc/rtc-hym8563.c
index bd628a6f981d..e5f13c4310fe 100644
--- a/drivers/rtc/rtc-hym8563.c
+++ b/drivers/rtc/rtc-hym8563.c
@@ -569,6 +569,9 @@ static int hym8563_probe(struct i2c_client *client,
if (IS_ERR(hym8563->rtc))
return PTR_ERR(hym8563->rtc);
+ /* the hym8563 alarm only supports a minute accuracy */
+ hym8563->rtc->uie_unsupported = 1;
+
#ifdef CONFIG_COMMON_CLK
hym8563_clkout_register_clk(hym8563);
#endif
diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile
index fc67f564f02c..788ed9b59b4e 100644
--- a/drivers/sh/Makefile
+++ b/drivers/sh/Makefile
@@ -1,10 +1,12 @@
#
# Makefile for the SuperH specific drivers.
#
-obj-y := intc/
+obj-$(CONFIG_SUPERH) += intc/
+obj-$(CONFIG_ARCH_SHMOBILE_LEGACY) += intc/
+ifneq ($(CONFIG_COMMON_CLK),y)
+obj-$(CONFIG_HAVE_CLK) += clk/
+endif
+obj-$(CONFIG_MAPLE) += maple/
+obj-$(CONFIG_SUPERHYWAY) += superhyway/
-obj-$(CONFIG_HAVE_CLK) += clk/
-obj-$(CONFIG_MAPLE) += maple/
-obj-$(CONFIG_SUPERHYWAY) += superhyway/
-
-obj-y += pm_runtime.o
+obj-y += pm_runtime.o
diff --git a/drivers/sh/pm_runtime.c b/drivers/sh/pm_runtime.c
index 8afa5a4589f2..10c65eb51f85 100644
--- a/drivers/sh/pm_runtime.c
+++ b/drivers/sh/pm_runtime.c
@@ -50,8 +50,25 @@ static struct pm_clk_notifier_block platform_bus_notifier = {
.con_ids = { NULL, },
};
+static bool default_pm_on;
+
static int __init sh_pm_runtime_init(void)
{
+ if (IS_ENABLED(CONFIG_ARCH_SHMOBILE_MULTI)) {
+ if (!of_machine_is_compatible("renesas,emev2") &&
+ !of_machine_is_compatible("renesas,r7s72100") &&
+ !of_machine_is_compatible("renesas,r8a73a4") &&
+ !of_machine_is_compatible("renesas,r8a7740") &&
+ !of_machine_is_compatible("renesas,r8a7778") &&
+ !of_machine_is_compatible("renesas,r8a7779") &&
+ !of_machine_is_compatible("renesas,r8a7790") &&
+ !of_machine_is_compatible("renesas,r8a7791") &&
+ !of_machine_is_compatible("renesas,sh7372") &&
+ !of_machine_is_compatible("renesas,sh73a0"))
+ return 0;
+ }
+
+ default_pm_on = true;
pm_clk_add_notifier(&platform_bus_type, &platform_bus_notifier);
return 0;
}
@@ -59,7 +76,8 @@ core_initcall(sh_pm_runtime_init);
static int __init sh_pm_runtime_late_init(void)
{
- pm_genpd_poweroff_unused();
+ if (default_pm_on)
+ pm_genpd_poweroff_unused();
return 0;
}
late_initcall(sh_pm_runtime_late_init);
diff --git a/drivers/spi/spi-pxa2xx-dma.c b/drivers/spi/spi-pxa2xx-dma.c
index 713af4806f26..f6759dc0153b 100644
--- a/drivers/spi/spi-pxa2xx-dma.c
+++ b/drivers/spi/spi-pxa2xx-dma.c
@@ -29,18 +29,6 @@ static int pxa2xx_spi_map_dma_buffer(struct driver_data *drv_data,
struct sg_table *sgt;
void *buf, *pbuf;
- /*
- * Some DMA controllers have problems transferring buffers that are
- * not multiple of 4 bytes. So we truncate the transfer so that it
- * is suitable for such controllers, and handle the trailing bytes
- * manually after the DMA completes.
- *
- * REVISIT: It would be better if this information could be
- * retrieved directly from the DMA device in a similar way than
- * ->copy_align etc. is done.
- */
- len = ALIGN(drv_data->len, 4);
-
if (dir == DMA_TO_DEVICE) {
dmadev = drv_data->tx_chan->device->dev;
sgt = &drv_data->tx_sgt;
@@ -144,12 +132,8 @@ static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data,
if (!error) {
pxa2xx_spi_unmap_dma_buffers(drv_data);
- /* Handle the last bytes of unaligned transfer */
drv_data->tx += drv_data->tx_map_len;
- drv_data->write(drv_data);
-
drv_data->rx += drv_data->rx_map_len;
- drv_data->read(drv_data);
msg->actual_length += drv_data->len;
msg->state = pxa2xx_spi_next_transfer(drv_data);
diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
index b032e8885e24..78c66e3c53ed 100644
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -734,7 +734,7 @@ static int spi_qup_remove(struct platform_device *pdev)
int ret;
ret = pm_runtime_get_sync(&pdev->dev);
- if (ret)
+ if (ret < 0)
return ret;
ret = spi_qup_set_state(controller, QUP_STATE_RESET);
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 4eb9bf02996c..939edf473235 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -580,6 +580,7 @@ static void spi_set_cs(struct spi_device *spi, bool enable)
spi->master->set_cs(spi, !enable);
}
+#ifdef CONFIG_HAS_DMA
static int spi_map_buf(struct spi_master *master, struct device *dev,
struct sg_table *sgt, void *buf, size_t len,
enum dma_data_direction dir)
@@ -637,55 +638,12 @@ static void spi_unmap_buf(struct spi_master *master, struct device *dev,
}
}
-static int spi_map_msg(struct spi_master *master, struct spi_message *msg)
+static int __spi_map_msg(struct spi_master *master, struct spi_message *msg)
{
struct device *tx_dev, *rx_dev;
struct spi_transfer *xfer;
- void *tmp;
- unsigned int max_tx, max_rx;
int ret;
- if (master->flags & (SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX)) {
- max_tx = 0;
- max_rx = 0;
-
- list_for_each_entry(xfer, &msg->transfers, transfer_list) {
- if ((master->flags & SPI_MASTER_MUST_TX) &&
- !xfer->tx_buf)
- max_tx = max(xfer->len, max_tx);
- if ((master->flags & SPI_MASTER_MUST_RX) &&
- !xfer->rx_buf)
- max_rx = max(xfer->len, max_rx);
- }
-
- if (max_tx) {
- tmp = krealloc(master->dummy_tx, max_tx,
- GFP_KERNEL | GFP_DMA);
- if (!tmp)
- return -ENOMEM;
- master->dummy_tx = tmp;
- memset(tmp, 0, max_tx);
- }
-
- if (max_rx) {
- tmp = krealloc(master->dummy_rx, max_rx,
- GFP_KERNEL | GFP_DMA);
- if (!tmp)
- return -ENOMEM;
- master->dummy_rx = tmp;
- }
-
- if (max_tx || max_rx) {
- list_for_each_entry(xfer, &msg->transfers,
- transfer_list) {
- if (!xfer->tx_buf)
- xfer->tx_buf = master->dummy_tx;
- if (!xfer->rx_buf)
- xfer->rx_buf = master->dummy_rx;
- }
- }
- }
-
if (!master->can_dma)
return 0;
@@ -742,6 +700,69 @@ static int spi_unmap_msg(struct spi_master *master, struct spi_message *msg)
return 0;
}
+#else /* !CONFIG_HAS_DMA */
+static inline int __spi_map_msg(struct spi_master *master,
+ struct spi_message *msg)
+{
+ return 0;
+}
+
+static inline int spi_unmap_msg(struct spi_master *master,
+ struct spi_message *msg)
+{
+ return 0;
+}
+#endif /* !CONFIG_HAS_DMA */
+
+static int spi_map_msg(struct spi_master *master, struct spi_message *msg)
+{
+ struct spi_transfer *xfer;
+ void *tmp;
+ unsigned int max_tx, max_rx;
+
+ if (master->flags & (SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX)) {
+ max_tx = 0;
+ max_rx = 0;
+
+ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+ if ((master->flags & SPI_MASTER_MUST_TX) &&
+ !xfer->tx_buf)
+ max_tx = max(xfer->len, max_tx);
+ if ((master->flags & SPI_MASTER_MUST_RX) &&
+ !xfer->rx_buf)
+ max_rx = max(xfer->len, max_rx);
+ }
+
+ if (max_tx) {
+ tmp = krealloc(master->dummy_tx, max_tx,
+ GFP_KERNEL | GFP_DMA);
+ if (!tmp)
+ return -ENOMEM;
+ master->dummy_tx = tmp;
+ memset(tmp, 0, max_tx);
+ }
+
+ if (max_rx) {
+ tmp = krealloc(master->dummy_rx, max_rx,
+ GFP_KERNEL | GFP_DMA);
+ if (!tmp)
+ return -ENOMEM;
+ master->dummy_rx = tmp;
+ }
+
+ if (max_tx || max_rx) {
+ list_for_each_entry(xfer, &msg->transfers,
+ transfer_list) {
+ if (!xfer->tx_buf)
+ xfer->tx_buf = master->dummy_tx;
+ if (!xfer->rx_buf)
+ xfer->rx_buf = master->dummy_rx;
+ }
+ }
+ }
+
+ return __spi_map_msg(master, msg);
+}
/*
* spi_transfer_one_message - Default implementation of transfer_one_message()
@@ -1151,7 +1172,6 @@ static int spi_master_initialize_queue(struct spi_master *master)
{
int ret;
- master->queued = true;
master->transfer = spi_queued_transfer;
if (!master->transfer_one_message)
master->transfer_one_message = spi_transfer_one_message;
@@ -1162,6 +1182,7 @@ static int spi_master_initialize_queue(struct spi_master *master)
dev_err(&master->dev, "problem initializing queue\n");
goto err_init_queue;
}
+ master->queued = true;
ret = spi_start_queue(master);
if (ret) {
dev_err(&master->dev, "problem starting queue\n");
@@ -1171,8 +1192,8 @@ static int spi_master_initialize_queue(struct spi_master *master)
return 0;
err_start_queue:
-err_init_queue:
spi_destroy_queue(master);
+err_init_queue:
return ret;
}
@@ -1756,7 +1777,7 @@ EXPORT_SYMBOL_GPL(spi_busnum_to_master);
*/
int spi_setup(struct spi_device *spi)
{
- unsigned bad_bits;
+ unsigned bad_bits, ugly_bits;
int status = 0;
/* check mode to prevent that DUAL and QUAD set at the same time
@@ -1776,6 +1797,15 @@ int spi_setup(struct spi_device *spi)
* that aren't supported with their current master
*/
bad_bits = spi->mode & ~spi->master->mode_bits;
+ ugly_bits = bad_bits &
+ (SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD);
+ if (ugly_bits) {
+ dev_warn(&spi->dev,
+ "setup: ignoring unsupported mode bits %x\n",
+ ugly_bits);
+ spi->mode &= ~ugly_bits;
+ bad_bits &= ~ugly_bits;
+ }
if (bad_bits) {
dev_err(&spi->dev, "setup: unsupported mode bits %x\n",
bad_bits);
diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c
index 4144a75e5f71..c270c9ae6d27 100644
--- a/drivers/staging/imx-drm/imx-drm-core.c
+++ b/drivers/staging/imx-drm/imx-drm-core.c
@@ -517,7 +517,7 @@ int imx_drm_encoder_get_mux_id(struct device_node *node,
of_node_put(port);
if (port == imx_crtc->port) {
ret = of_graph_parse_endpoint(ep, &endpoint);
- return ret ? ret : endpoint.id;
+ return ret ? ret : endpoint.port;
}
} while (ep);
@@ -675,6 +675,11 @@ static int imx_drm_platform_probe(struct platform_device *pdev)
if (!remote || !of_device_is_available(remote)) {
of_node_put(remote);
continue;
+ } else if (!of_device_is_available(remote->parent)) {
+ dev_warn(&pdev->dev, "parent device of %s is not available\n",
+ remote->full_name);
+ of_node_put(remote);
+ continue;
}
ret = imx_drm_add_component(&pdev->dev, remote);
diff --git a/drivers/staging/imx-drm/imx-tve.c b/drivers/staging/imx-drm/imx-tve.c
index 575533f4fd64..a23f4f773146 100644
--- a/drivers/staging/imx-drm/imx-tve.c
+++ b/drivers/staging/imx-drm/imx-tve.c
@@ -582,7 +582,7 @@ static int imx_tve_bind(struct device *dev, struct device *master, void *data)
tve->dev = dev;
spin_lock_init(&tve->lock);
- ddc_node = of_parse_phandle(np, "i2c-ddc-bus", 0);
+ ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0);
if (ddc_node) {
tve->ddc = of_find_i2c_adapter_by_node(ddc_node);
of_node_put(ddc_node);
diff --git a/drivers/staging/media/davinci_vpfe/vpfe_video.c b/drivers/staging/media/davinci_vpfe/vpfe_video.c
index 8c101cbbee97..acc8184c46cd 100644
--- a/drivers/staging/media/davinci_vpfe/vpfe_video.c
+++ b/drivers/staging/media/davinci_vpfe/vpfe_video.c
@@ -1247,9 +1247,18 @@ static int vpfe_stop_streaming(struct vb2_queue *vq)
struct vpfe_fh *fh = vb2_get_drv_priv(vq);
struct vpfe_video_device *video = fh->video;
- if (!vb2_is_streaming(vq))
- return 0;
/* release all active buffers */
+ if (video->cur_frm == video->next_frm) {
+ vb2_buffer_done(&video->cur_frm->vb, VB2_BUF_STATE_ERROR);
+ } else {
+ if (video->cur_frm != NULL)
+ vb2_buffer_done(&video->cur_frm->vb,
+ VB2_BUF_STATE_ERROR);
+ if (video->next_frm != NULL)
+ vb2_buffer_done(&video->next_frm->vb,
+ VB2_BUF_STATE_ERROR);
+ }
+
while (!list_empty(&video->dma_queue)) {
video->next_frm = list_entry(video->dma_queue.next,
struct vpfe_cap_buffer, list);
diff --git a/drivers/staging/media/sn9c102/sn9c102_devtable.h b/drivers/staging/media/sn9c102/sn9c102_devtable.h
index b3d2cc729657..4ba569258498 100644
--- a/drivers/staging/media/sn9c102/sn9c102_devtable.h
+++ b/drivers/staging/media/sn9c102/sn9c102_devtable.h
@@ -48,10 +48,8 @@ static const struct usb_device_id sn9c102_id_table[] = {
{ SN9C102_USB_DEVICE(0x0c45, 0x600d, BRIDGE_SN9C102), },
/* { SN9C102_USB_DEVICE(0x0c45, 0x6011, BRIDGE_SN9C102), }, OV6650 */
{ SN9C102_USB_DEVICE(0x0c45, 0x6019, BRIDGE_SN9C102), },
-#endif
{ SN9C102_USB_DEVICE(0x0c45, 0x6024, BRIDGE_SN9C102), },
{ SN9C102_USB_DEVICE(0x0c45, 0x6025, BRIDGE_SN9C102), },
-#if !defined CONFIG_USB_GSPCA_SONIXB && !defined CONFIG_USB_GSPCA_SONIXB_MODULE
{ SN9C102_USB_DEVICE(0x0c45, 0x6028, BRIDGE_SN9C102), },
{ SN9C102_USB_DEVICE(0x0c45, 0x6029, BRIDGE_SN9C102), },
{ SN9C102_USB_DEVICE(0x0c45, 0x602a, BRIDGE_SN9C102), },
diff --git a/drivers/staging/rtl8723au/os_dep/os_intfs.c b/drivers/staging/rtl8723au/os_dep/os_intfs.c
index 57eca7a45672..4fe751f7c2bf 100644
--- a/drivers/staging/rtl8723au/os_dep/os_intfs.c
+++ b/drivers/staging/rtl8723au/os_dep/os_intfs.c
@@ -953,8 +953,6 @@ static int netdev_close(struct net_device *pnetdev)
#endif /* CONFIG_8723AU_P2P */
rtw_scan_abort23a(padapter);
- /* set this at the end */
- padapter->rtw_wdev->iftype = NL80211_IFTYPE_MONITOR;
RT_TRACE(_module_os_intfs_c_, _drv_info_, ("-871x_drv - drv_close\n"));
DBG_8723A("-871x_drv - drv_close, bup =%d\n", padapter->bup);
diff --git a/drivers/staging/rtl8723au/os_dep/usb_ops_linux.c b/drivers/staging/rtl8723au/os_dep/usb_ops_linux.c
index c49160e477d8..07e542e5d156 100644
--- a/drivers/staging/rtl8723au/os_dep/usb_ops_linux.c
+++ b/drivers/staging/rtl8723au/os_dep/usb_ops_linux.c
@@ -26,7 +26,7 @@ unsigned int ffaddr2pipehdl23a(struct dvobj_priv *pdvobj, u32 addr)
if (addr == RECV_BULK_IN_ADDR) {
pipe = usb_rcvbulkpipe(pusbd, pdvobj->RtInPipe[0]);
} else if (addr == RECV_INT_IN_ADDR) {
- pipe = usb_rcvbulkpipe(pusbd, pdvobj->RtInPipe[1]);
+ pipe = usb_rcvintpipe(pusbd, pdvobj->RtInPipe[1]);
} else if (addr < HW_QUEUE_ENTRY) {
ep_num = pdvobj->Queue2Pipe[addr];
pipe = usb_sndbulkpipe(pusbd, ep_num);
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index 78cab13bbb1b..46588c85d39b 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -1593,7 +1593,9 @@ int iscsit_process_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
* Initiator is expecting a NopIN ping reply..
*/
if (hdr->itt != RESERVED_ITT) {
- BUG_ON(!cmd);
+ if (!cmd)
+ return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
+ (unsigned char *)hdr);
spin_lock_bh(&conn->cmd_lock);
list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h
index 6960f22909ae..302eb3b78715 100644
--- a/drivers/target/iscsi/iscsi_target_core.h
+++ b/drivers/target/iscsi/iscsi_target_core.h
@@ -775,6 +775,7 @@ struct iscsi_np {
int np_ip_proto;
int np_sock_type;
enum np_thread_state_table np_thread_state;
+ bool enabled;
enum iscsi_timer_flags_table np_login_timer_flags;
u32 np_exports;
enum np_flags_table np_flags;
diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c
index 8739b98f6f93..ca31fa1b8a4b 100644
--- a/drivers/target/iscsi/iscsi_target_login.c
+++ b/drivers/target/iscsi/iscsi_target_login.c
@@ -436,7 +436,7 @@ static int iscsi_login_zero_tsih_s2(
}
off = mrdsl % PAGE_SIZE;
if (!off)
- return 0;
+ goto check_prot;
if (mrdsl < PAGE_SIZE)
mrdsl = PAGE_SIZE;
@@ -452,6 +452,31 @@ static int iscsi_login_zero_tsih_s2(
ISCSI_LOGIN_STATUS_NO_RESOURCES);
return -1;
}
+ /*
+ * ISER currently requires that ImmediateData + Unsolicited
+ * Data be disabled when protection / signature MRs are enabled.
+ */
+check_prot:
+ if (sess->se_sess->sup_prot_ops &
+ (TARGET_PROT_DOUT_STRIP | TARGET_PROT_DOUT_PASS |
+ TARGET_PROT_DOUT_INSERT)) {
+
+ sprintf(buf, "ImmediateData=No");
+ if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) {
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+ ISCSI_LOGIN_STATUS_NO_RESOURCES);
+ return -1;
+ }
+
+ sprintf(buf, "InitialR2T=Yes");
+ if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) {
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+ ISCSI_LOGIN_STATUS_NO_RESOURCES);
+ return -1;
+ }
+ pr_debug("Forcing ImmediateData=No + InitialR2T=Yes for"
+ " T10-PI enabled ISER session\n");
+ }
}
return 0;
@@ -984,6 +1009,7 @@ int iscsi_target_setup_login_socket(
}
np->np_transport = t;
+ np->enabled = true;
return 0;
}
diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c
index eb96b20dc09e..ca1811858afd 100644
--- a/drivers/target/iscsi/iscsi_target_tpg.c
+++ b/drivers/target/iscsi/iscsi_target_tpg.c
@@ -184,6 +184,7 @@ static void iscsit_clear_tpg_np_login_thread(
return;
}
+ tpg_np->tpg_np->enabled = false;
iscsit_reset_np_thread(tpg_np->tpg_np, tpg_np, tpg, shutdown);
}
diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c
index 65001e133670..26416c15d65c 100644
--- a/drivers/target/target_core_device.c
+++ b/drivers/target/target_core_device.c
@@ -798,10 +798,10 @@ int se_dev_set_emulate_write_cache(struct se_device *dev, int flag)
pr_err("emulate_write_cache not supported for pSCSI\n");
return -EINVAL;
}
- if (dev->transport->get_write_cache) {
- pr_warn("emulate_write_cache cannot be changed when underlying"
- " HW reports WriteCacheEnabled, ignoring request\n");
- return 0;
+ if (flag &&
+ dev->transport->get_write_cache) {
+ pr_err("emulate_write_cache not supported for this device\n");
+ return -EINVAL;
}
dev->dev_attrib.emulate_write_cache = flag;
@@ -936,6 +936,10 @@ int se_dev_set_pi_prot_type(struct se_device *dev, int flag)
return 0;
}
if (!dev->transport->init_prot || !dev->transport->free_prot) {
+ /* 0 is only allowed value for non-supporting backends */
+ if (flag == 0)
+ return 0;
+
pr_err("DIF protection not supported by backend: %s\n",
dev->transport->name);
return -ENOSYS;
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index d4b98690a736..789aa9eb0a1e 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -1113,6 +1113,7 @@ void transport_init_se_cmd(
init_completion(&cmd->cmd_wait_comp);
init_completion(&cmd->task_stop_comp);
spin_lock_init(&cmd->t_state_lock);
+ kref_init(&cmd->cmd_kref);
cmd->transport_state = CMD_T_DEV_ACTIVE;
cmd->se_tfo = tfo;
@@ -2357,7 +2358,6 @@ int target_get_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd,
unsigned long flags;
int ret = 0;
- kref_init(&se_cmd->cmd_kref);
/*
* Add a second kref if the fabric caller is expecting to handle
* fabric acknowledgement that requires two target_put_sess_cmd()
diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c
index 01cf37f212c3..f5fd515b2bee 100644
--- a/drivers/target/tcm_fc/tfc_cmd.c
+++ b/drivers/target/tcm_fc/tfc_cmd.c
@@ -90,18 +90,18 @@ static void ft_free_cmd(struct ft_cmd *cmd)
{
struct fc_frame *fp;
struct fc_lport *lport;
- struct se_session *se_sess;
+ struct ft_sess *sess;
if (!cmd)
return;
- se_sess = cmd->sess->se_sess;
+ sess = cmd->sess;
fp = cmd->req_frame;
lport = fr_dev(fp);
if (fr_seq(fp))
lport->tt.seq_release(fr_seq(fp));
fc_frame_free(fp);
- percpu_ida_free(&se_sess->sess_tag_pool, cmd->se_cmd.map_tag);
- ft_sess_put(cmd->sess); /* undo get from lookup at recv */
+ percpu_ida_free(&sess->se_sess->sess_tag_pool, cmd->se_cmd.map_tag);
+ ft_sess_put(sess); /* undo get from lookup at recv */
}
void ft_release_cmd(struct se_cmd *se_cmd)
diff --git a/drivers/xen/events/events_fifo.c b/drivers/xen/events/events_fifo.c
index 96109a9972b6..84b4bfb84344 100644
--- a/drivers/xen/events/events_fifo.c
+++ b/drivers/xen/events/events_fifo.c
@@ -66,7 +66,22 @@ static DEFINE_PER_CPU(struct evtchn_fifo_queue, cpu_queue);
static event_word_t *event_array[MAX_EVENT_ARRAY_PAGES] __read_mostly;
static unsigned event_array_pages __read_mostly;
+/*
+ * sync_set_bit() and friends must be unsigned long aligned on non-x86
+ * platforms.
+ */
+#if !defined(CONFIG_X86) && BITS_PER_LONG > 32
+
+#define BM(w) (unsigned long *)((unsigned long)w & ~0x7UL)
+#define EVTCHN_FIFO_BIT(b, w) \
+ (((unsigned long)w & 0x4UL) ? (EVTCHN_FIFO_ ##b + 32) : EVTCHN_FIFO_ ##b)
+
+#else
+
#define BM(w) ((unsigned long *)(w))
+#define EVTCHN_FIFO_BIT(b, w) EVTCHN_FIFO_ ##b
+
+#endif
static inline event_word_t *event_word_from_port(unsigned port)
{
@@ -161,33 +176,38 @@ static void evtchn_fifo_bind_to_cpu(struct irq_info *info, unsigned cpu)
static void evtchn_fifo_clear_pending(unsigned port)
{
event_word_t *word = event_word_from_port(port);
- sync_clear_bit(EVTCHN_FIFO_PENDING, BM(word));
+ sync_clear_bit(EVTCHN_FIFO_BIT(PENDING, word), BM(word));
}
static void evtchn_fifo_set_pending(unsigned port)
{
event_word_t *word = event_word_from_port(port);
- sync_set_bit(EVTCHN_FIFO_PENDING, BM(word));
+ sync_set_bit(EVTCHN_FIFO_BIT(PENDING, word), BM(word));
}
static bool evtchn_fifo_is_pending(unsigned port)
{
event_word_t *word = event_word_from_port(port);
- return sync_test_bit(EVTCHN_FIFO_PENDING, BM(word));
+ return sync_test_bit(EVTCHN_FIFO_BIT(PENDING, word), BM(word));
}
static bool evtchn_fifo_test_and_set_mask(unsigned port)
{
event_word_t *word = event_word_from_port(port);
- return sync_test_and_set_bit(EVTCHN_FIFO_MASKED, BM(word));
+ return sync_test_and_set_bit(EVTCHN_FIFO_BIT(MASKED, word), BM(word));
}
static void evtchn_fifo_mask(unsigned port)
{
event_word_t *word = event_word_from_port(port);
- sync_set_bit(EVTCHN_FIFO_MASKED, BM(word));
+ sync_set_bit(EVTCHN_FIFO_BIT(MASKED, word), BM(word));
}
+static bool evtchn_fifo_is_masked(unsigned port)
+{
+ event_word_t *word = event_word_from_port(port);
+ return sync_test_bit(EVTCHN_FIFO_BIT(MASKED, word), BM(word));
+}
/*
* Clear MASKED, spinning if BUSY is set.
*/
@@ -211,7 +231,7 @@ static void evtchn_fifo_unmask(unsigned port)
BUG_ON(!irqs_disabled());
clear_masked(word);
- if (sync_test_bit(EVTCHN_FIFO_PENDING, BM(word))) {
+ if (evtchn_fifo_is_pending(port)) {
struct evtchn_unmask unmask = { .port = port };
(void)HYPERVISOR_event_channel_op(EVTCHNOP_unmask, &unmask);
}
@@ -243,7 +263,7 @@ static void handle_irq_for_port(unsigned port)
static void consume_one_event(unsigned cpu,
struct evtchn_fifo_control_block *control_block,
- unsigned priority, uint32_t *ready)
+ unsigned priority, unsigned long *ready)
{
struct evtchn_fifo_queue *q = &per_cpu(cpu_queue, cpu);
uint32_t head;
@@ -273,10 +293,9 @@ static void consume_one_event(unsigned cpu,
* copy of the ready word.
*/
if (head == 0)
- clear_bit(priority, BM(ready));
+ clear_bit(priority, ready);
- if (sync_test_bit(EVTCHN_FIFO_PENDING, BM(word))
- && !sync_test_bit(EVTCHN_FIFO_MASKED, BM(word)))
+ if (evtchn_fifo_is_pending(port) && !evtchn_fifo_is_masked(port))
handle_irq_for_port(port);
q->head[priority] = head;
@@ -285,7 +304,7 @@ static void consume_one_event(unsigned cpu,
static void evtchn_fifo_handle_events(unsigned cpu)
{
struct evtchn_fifo_control_block *control_block;
- uint32_t ready;
+ unsigned long ready;
unsigned q;
control_block = per_cpu(cpu_control_block, cpu);
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 2ad7de94efef..2f6d7b13b5bd 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -3120,6 +3120,8 @@ process_slot:
} else if (type == BTRFS_FILE_EXTENT_INLINE) {
u64 skip = 0;
u64 trim = 0;
+ u64 aligned_end = 0;
+
if (off > key.offset) {
skip = off - key.offset;
new_key.offset += skip;
@@ -3136,9 +3138,11 @@ process_slot:
size -= skip + trim;
datal -= skip + trim;
+ aligned_end = ALIGN(new_key.offset + datal,
+ root->sectorsize);
ret = btrfs_drop_extents(trans, root, inode,
new_key.offset,
- new_key.offset + datal,
+ aligned_end,
1);
if (ret) {
if (ret != -EOPNOTSUPP)
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index eb6537a08c1b..fd38b5053479 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -1668,7 +1668,7 @@ static int get_first_ref(struct btrfs_root *root, u64 ino,
goto out;
}
- if (key.type == BTRFS_INODE_REF_KEY) {
+ if (found_key.type == BTRFS_INODE_REF_KEY) {
struct btrfs_inode_ref *iref;
iref = btrfs_item_ptr(path->nodes[0], path->slots[0],
struct btrfs_inode_ref);
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index aadc2b68678b..a22d667f1069 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -1737,6 +1737,9 @@ cifs_inode_needs_reval(struct inode *inode)
if (cifs_i->time == 0)
return true;
+ if (!cifs_sb->actimeo)
+ return true;
+
if (!time_in_range(jiffies, cifs_i->time,
cifs_i->time + cifs_sb->actimeo))
return true;
diff --git a/fs/exec.c b/fs/exec.c
index 476f3ebf437e..238b7aa26f68 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -657,10 +657,10 @@ int setup_arg_pages(struct linux_binprm *bprm,
unsigned long rlim_stack;
#ifdef CONFIG_STACK_GROWSUP
- /* Limit stack size to 1GB */
+ /* Limit stack size */
stack_base = rlimit_max(RLIMIT_STACK);
- if (stack_base > (1 << 30))
- stack_base = 1 << 30;
+ if (stack_base > STACK_SIZE_MAX)
+ stack_base = STACK_SIZE_MAX;
/* Make sure we didn't let the argument array grow too large. */
if (vma->vm_end - vma->vm_start > stack_base)
diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c
index e01ea4a14a01..5e9a80cfc3d8 100644
--- a/fs/kernfs/file.c
+++ b/fs/kernfs/file.c
@@ -610,6 +610,7 @@ static void kernfs_put_open_node(struct kernfs_node *kn,
static int kernfs_fop_open(struct inode *inode, struct file *file)
{
struct kernfs_node *kn = file->f_path.dentry->d_fsdata;
+ struct kernfs_root *root = kernfs_root(kn);
const struct kernfs_ops *ops;
struct kernfs_open_file *of;
bool has_read, has_write, has_mmap;
@@ -624,14 +625,16 @@ static int kernfs_fop_open(struct inode *inode, struct file *file)
has_write = ops->write || ops->mmap;
has_mmap = ops->mmap;
- /* check perms and supported operations */
- if ((file->f_mode & FMODE_WRITE) &&
- (!(inode->i_mode & S_IWUGO) || !has_write))
- goto err_out;
+ /* see the flag definition for details */
+ if (root->flags & KERNFS_ROOT_EXTRA_OPEN_PERM_CHECK) {
+ if ((file->f_mode & FMODE_WRITE) &&
+ (!(inode->i_mode & S_IWUGO) || !has_write))
+ goto err_out;
- if ((file->f_mode & FMODE_READ) &&
- (!(inode->i_mode & S_IRUGO) || !has_read))
- goto err_out;
+ if ((file->f_mode & FMODE_READ) &&
+ (!(inode->i_mode & S_IRUGO) || !has_read))
+ goto err_out;
+ }
/* allocate a kernfs_open_file for the file */
error = -ENOMEM;
diff --git a/fs/locks.c b/fs/locks.c
index e663aeac579e..e390bd9ae068 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -389,18 +389,6 @@ static int flock64_to_posix_lock(struct file *filp, struct file_lock *fl,
fl->fl_ops = NULL;
fl->fl_lmops = NULL;
- /* Ensure that fl->fl_filp has compatible f_mode */
- switch (l->l_type) {
- case F_RDLCK:
- if (!(filp->f_mode & FMODE_READ))
- return -EBADF;
- break;
- case F_WRLCK:
- if (!(filp->f_mode & FMODE_WRITE))
- return -EBADF;
- break;
- }
-
return assign_type(fl, l->l_type);
}
@@ -2034,6 +2022,22 @@ static int do_lock_file_wait(struct file *filp, unsigned int cmd,
return error;
}
+/* Ensure that fl->fl_filp has compatible f_mode for F_SETLK calls */
+static int
+check_fmode_for_setlk(struct file_lock *fl)
+{
+ switch (fl->fl_type) {
+ case F_RDLCK:
+ if (!(fl->fl_file->f_mode & FMODE_READ))
+ return -EBADF;
+ break;
+ case F_WRLCK:
+ if (!(fl->fl_file->f_mode & FMODE_WRITE))
+ return -EBADF;
+ }
+ return 0;
+}
+
/* Apply the lock described by l to an open file descriptor.
* This implements both the F_SETLK and F_SETLKW commands of fcntl().
*/
@@ -2071,6 +2075,10 @@ again:
if (error)
goto out;
+ error = check_fmode_for_setlk(file_lock);
+ if (error)
+ goto out;
+
/*
* If the cmd is requesting file-private locks, then set the
* FL_OFDLCK flag and override the owner.
@@ -2206,6 +2214,10 @@ again:
if (error)
goto out;
+ error = check_fmode_for_setlk(file_lock);
+ if (error)
+ goto out;
+
/*
* If the cmd is requesting file-private locks, then set the
* FL_OFDLCK flag and override the owner.
diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c
index 6f3f392d48af..b6f46013dddf 100644
--- a/fs/nfsd/nfs4acl.c
+++ b/fs/nfsd/nfs4acl.c
@@ -402,8 +402,10 @@ sort_pacl(struct posix_acl *pacl)
* by uid/gid. */
int i, j;
- if (pacl->a_count <= 4)
- return; /* no users or groups */
+ /* no users or groups */
+ if (!pacl || pacl->a_count <= 4)
+ return;
+
i = 1;
while (pacl->a_entries[i].e_tag == ACL_USER)
i++;
@@ -530,13 +532,12 @@ posix_state_to_acl(struct posix_acl_state *state, unsigned int flags)
/*
* ACLs with no ACEs are treated differently in the inheritable
- * and effective cases: when there are no inheritable ACEs, we
- * set a zero-length default posix acl:
+ * and effective cases: when there are no inheritable ACEs,
+ * calls ->set_acl with a NULL ACL structure.
*/
- if (state->empty && (flags & NFS4_ACL_TYPE_DEFAULT)) {
- pacl = posix_acl_alloc(0, GFP_KERNEL);
- return pacl ? pacl : ERR_PTR(-ENOMEM);
- }
+ if (state->empty && (flags & NFS4_ACL_TYPE_DEFAULT))
+ return NULL;
+
/*
* When there are no effective ACEs, the following will end
* up setting a 3-element effective posix ACL with all
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 3ba65979a3cd..32b699bebb9c 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1078,6 +1078,18 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name)
return NULL;
}
clp->cl_name.len = name.len;
+ INIT_LIST_HEAD(&clp->cl_sessions);
+ idr_init(&clp->cl_stateids);
+ atomic_set(&clp->cl_refcount, 0);
+ clp->cl_cb_state = NFSD4_CB_UNKNOWN;
+ INIT_LIST_HEAD(&clp->cl_idhash);
+ INIT_LIST_HEAD(&clp->cl_openowners);
+ INIT_LIST_HEAD(&clp->cl_delegations);
+ INIT_LIST_HEAD(&clp->cl_lru);
+ INIT_LIST_HEAD(&clp->cl_callbacks);
+ INIT_LIST_HEAD(&clp->cl_revoked);
+ spin_lock_init(&clp->cl_lock);
+ rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table");
return clp;
}
@@ -1095,6 +1107,7 @@ free_client(struct nfs4_client *clp)
WARN_ON_ONCE(atomic_read(&ses->se_ref));
free_session(ses);
}
+ rpc_destroy_wait_queue(&clp->cl_cb_waitq);
free_svc_cred(&clp->cl_cred);
kfree(clp->cl_name.data);
idr_destroy(&clp->cl_stateids);
@@ -1347,7 +1360,6 @@ static struct nfs4_client *create_client(struct xdr_netobj name,
if (clp == NULL)
return NULL;
- INIT_LIST_HEAD(&clp->cl_sessions);
ret = copy_cred(&clp->cl_cred, &rqstp->rq_cred);
if (ret) {
spin_lock(&nn->client_lock);
@@ -1355,20 +1367,9 @@ static struct nfs4_client *create_client(struct xdr_netobj name,
spin_unlock(&nn->client_lock);
return NULL;
}
- idr_init(&clp->cl_stateids);
- atomic_set(&clp->cl_refcount, 0);
- clp->cl_cb_state = NFSD4_CB_UNKNOWN;
- INIT_LIST_HEAD(&clp->cl_idhash);
- INIT_LIST_HEAD(&clp->cl_openowners);
- INIT_LIST_HEAD(&clp->cl_delegations);
- INIT_LIST_HEAD(&clp->cl_lru);
- INIT_LIST_HEAD(&clp->cl_callbacks);
- INIT_LIST_HEAD(&clp->cl_revoked);
- spin_lock_init(&clp->cl_lock);
nfsd4_init_callback(&clp->cl_cb_null);
clp->cl_time = get_seconds();
clear_bit(0, &clp->cl_cb_slot_busy);
- rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table");
copy_verf(clp, verf);
rpc_copy_addr((struct sockaddr *) &clp->cl_addr, sa);
gen_confirm(clp);
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
index 28cc1acd5439..e9ef59b3abb1 100644
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -47,12 +47,13 @@ static int sysfs_kf_seq_show(struct seq_file *sf, void *v)
ssize_t count;
char *buf;
- /* acquire buffer and ensure that it's >= PAGE_SIZE */
+ /* acquire buffer and ensure that it's >= PAGE_SIZE and clear */
count = seq_get_buf(sf, &buf);
if (count < PAGE_SIZE) {
seq_commit(sf, -1);
return 0;
}
+ memset(buf, 0, PAGE_SIZE);
/*
* Invoke show(). Control may reach here via seq file lseek even
diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c
index a66ad6196f59..8794423f7efb 100644
--- a/fs/sysfs/mount.c
+++ b/fs/sysfs/mount.c
@@ -63,7 +63,8 @@ int __init sysfs_init(void)
{
int err;
- sysfs_root = kernfs_create_root(NULL, 0, NULL);
+ sysfs_root = kernfs_create_root(NULL, KERNFS_ROOT_EXTRA_OPEN_PERM_CHECK,
+ NULL);
if (IS_ERR(sysfs_root))
return PTR_ERR(sysfs_root);
diff --git a/fs/xfs/xfs_export.c b/fs/xfs/xfs_export.c
index 1399e187d425..753e467aa1a5 100644
--- a/fs/xfs/xfs_export.c
+++ b/fs/xfs/xfs_export.c
@@ -237,7 +237,7 @@ xfs_fs_nfs_commit_metadata(
if (!lsn)
return 0;
- return _xfs_log_force_lsn(mp, lsn, XFS_LOG_SYNC, NULL);
+ return -_xfs_log_force_lsn(mp, lsn, XFS_LOG_SYNC, NULL);
}
const struct export_operations xfs_export_operations = {
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index 951a2321ee01..830c1c937b88 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -155,7 +155,7 @@ xfs_dir_fsync(
if (!lsn)
return 0;
- return _xfs_log_force_lsn(mp, lsn, XFS_LOG_SYNC, NULL);
+ return -_xfs_log_force_lsn(mp, lsn, XFS_LOG_SYNC, NULL);
}
STATIC int
@@ -295,7 +295,7 @@ xfs_file_aio_read(
xfs_rw_ilock(ip, XFS_IOLOCK_EXCL);
if (inode->i_mapping->nrpages) {
- ret = -filemap_write_and_wait_range(
+ ret = filemap_write_and_wait_range(
VFS_I(ip)->i_mapping,
pos, -1);
if (ret) {
@@ -837,7 +837,7 @@ xfs_file_fallocate(
unsigned blksize_mask = (1 << inode->i_blkbits) - 1;
if (offset & blksize_mask || len & blksize_mask) {
- error = -EINVAL;
+ error = EINVAL;
goto out_unlock;
}
@@ -846,7 +846,7 @@ xfs_file_fallocate(
* in which case it is effectively a truncate operation
*/
if (offset + len >= i_size_read(inode)) {
- error = -EINVAL;
+ error = EINVAL;
goto out_unlock;
}
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index 301ecbfcc0be..36d630319a27 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -72,8 +72,8 @@ xfs_initxattrs(
int error = 0;
for (xattr = xattr_array; xattr->name != NULL; xattr++) {
- error = xfs_attr_set(ip, xattr->name, xattr->value,
- xattr->value_len, ATTR_SECURE);
+ error = -xfs_attr_set(ip, xattr->name, xattr->value,
+ xattr->value_len, ATTR_SECURE);
if (error < 0)
break;
}
@@ -93,8 +93,8 @@ xfs_init_security(
struct inode *dir,
const struct qstr *qstr)
{
- return security_inode_init_security(inode, dir, qstr,
- &xfs_initxattrs, NULL);
+ return -security_inode_init_security(inode, dir, qstr,
+ &xfs_initxattrs, NULL);
}
static void
@@ -173,12 +173,12 @@ xfs_generic_create(
#ifdef CONFIG_XFS_POSIX_ACL
if (default_acl) {
- error = xfs_set_acl(inode, default_acl, ACL_TYPE_DEFAULT);
+ error = -xfs_set_acl(inode, default_acl, ACL_TYPE_DEFAULT);
if (error)
goto out_cleanup_inode;
}
if (acl) {
- error = xfs_set_acl(inode, acl, ACL_TYPE_ACCESS);
+ error = -xfs_set_acl(inode, acl, ACL_TYPE_ACCESS);
if (error)
goto out_cleanup_inode;
}
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
index 348e4d2ed6e6..dc977b6e6a36 100644
--- a/fs/xfs/xfs_qm.c
+++ b/fs/xfs/xfs_qm.c
@@ -843,22 +843,17 @@ xfs_qm_init_quotainfo(
qinf = mp->m_quotainfo = kmem_zalloc(sizeof(xfs_quotainfo_t), KM_SLEEP);
- if ((error = list_lru_init(&qinf->qi_lru))) {
- kmem_free(qinf);
- mp->m_quotainfo = NULL;
- return error;
- }
+ error = -list_lru_init(&qinf->qi_lru);
+ if (error)
+ goto out_free_qinf;
/*
* See if quotainodes are setup, and if not, allocate them,
* and change the superblock accordingly.
*/
- if ((error = xfs_qm_init_quotainos(mp))) {
- list_lru_destroy(&qinf->qi_lru);
- kmem_free(qinf);
- mp->m_quotainfo = NULL;
- return error;
- }
+ error = xfs_qm_init_quotainos(mp);
+ if (error)
+ goto out_free_lru;
INIT_RADIX_TREE(&qinf->qi_uquota_tree, GFP_NOFS);
INIT_RADIX_TREE(&qinf->qi_gquota_tree, GFP_NOFS);
@@ -918,7 +913,7 @@ xfs_qm_init_quotainfo(
qinf->qi_isoftlimit = be64_to_cpu(ddqp->d_ino_softlimit);
qinf->qi_rtbhardlimit = be64_to_cpu(ddqp->d_rtb_hardlimit);
qinf->qi_rtbsoftlimit = be64_to_cpu(ddqp->d_rtb_softlimit);
-
+
xfs_qm_dqdestroy(dqp);
} else {
qinf->qi_btimelimit = XFS_QM_BTIMELIMIT;
@@ -935,6 +930,13 @@ xfs_qm_init_quotainfo(
qinf->qi_shrinker.flags = SHRINKER_NUMA_AWARE;
register_shrinker(&qinf->qi_shrinker);
return 0;
+
+out_free_lru:
+ list_lru_destroy(&qinf->qi_lru);
+out_free_qinf:
+ kmem_free(qinf);
+ mp->m_quotainfo = NULL;
+ return error;
}
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index 205376776377..3494eff8e4eb 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -1433,11 +1433,11 @@ xfs_fs_fill_super(
if (error)
goto out_free_fsname;
- error = xfs_init_mount_workqueues(mp);
+ error = -xfs_init_mount_workqueues(mp);
if (error)
goto out_close_devices;
- error = xfs_icsb_init_counters(mp);
+ error = -xfs_icsb_init_counters(mp);
if (error)
goto out_destroy_workqueues;
diff --git a/include/asm-generic/resource.h b/include/asm-generic/resource.h
index b4ea8f50fc65..5e752b959054 100644
--- a/include/asm-generic/resource.h
+++ b/include/asm-generic/resource.h
@@ -12,7 +12,7 @@
[RLIMIT_CPU] = { RLIM_INFINITY, RLIM_INFINITY }, \
[RLIMIT_FSIZE] = { RLIM_INFINITY, RLIM_INFINITY }, \
[RLIMIT_DATA] = { RLIM_INFINITY, RLIM_INFINITY }, \
- [RLIMIT_STACK] = { _STK_LIM, _STK_LIM_MAX }, \
+ [RLIMIT_STACK] = { _STK_LIM, RLIM_INFINITY }, \
[RLIMIT_CORE] = { 0, RLIM_INFINITY }, \
[RLIMIT_RSS] = { RLIM_INFINITY, RLIM_INFINITY }, \
[RLIMIT_NPROC] = { 0, 0 }, \
diff --git a/include/dt-bindings/clk/at91.h b/include/dt-bindings/clock/at91.h
index 0b4cb999a3f7..0b4cb999a3f7 100644
--- a/include/dt-bindings/clk/at91.h
+++ b/include/dt-bindings/clock/at91.h
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h
index c2515851c1aa..d60904b9e505 100644
--- a/include/linux/cgroup.h
+++ b/include/linux/cgroup.h
@@ -473,6 +473,7 @@ struct cftype {
};
extern struct cgroup_root cgrp_dfl_root;
+extern struct css_set init_css_set;
static inline bool cgroup_on_dfl(const struct cgroup *cgrp)
{
@@ -700,6 +701,20 @@ static inline struct cgroup_subsys_state *task_css(struct task_struct *task,
return task_css_check(task, subsys_id, false);
}
+/**
+ * task_css_is_root - test whether a task belongs to the root css
+ * @task: the target task
+ * @subsys_id: the target subsystem ID
+ *
+ * Test whether @task belongs to the root css on the specified subsystem.
+ * May be invoked in any context.
+ */
+static inline bool task_css_is_root(struct task_struct *task, int subsys_id)
+{
+ return task_css_check(task, subsys_id, true) ==
+ init_css_set.subsys[subsys_id];
+}
+
static inline struct cgroup *task_cgroup(struct task_struct *task,
int subsys_id)
{
diff --git a/include/linux/firewire.h b/include/linux/firewire.h
index c3683bdf28fe..d4b7683c722d 100644
--- a/include/linux/firewire.h
+++ b/include/linux/firewire.h
@@ -367,6 +367,9 @@ static inline int fw_stream_packet_destination_id(int tag, int channel, int sy)
return tag << 14 | channel << 8 | sy;
}
+void fw_schedule_bus_reset(struct fw_card *card, bool delayed,
+ bool short_reset);
+
struct fw_descriptor {
struct list_head link;
size_t length;
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index 97ac926c78a7..051c85032f48 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -272,6 +272,11 @@ static inline int irq_set_affinity(unsigned int irq, const struct cpumask *m)
return -EINVAL;
}
+static inline int irq_force_affinity(unsigned int irq, const struct cpumask *cpumask)
+{
+ return 0;
+}
+
static inline int irq_can_set_affinity(unsigned int irq)
{
return 0;
diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h
index b0122dc6f96a..ca1be5c9136c 100644
--- a/include/linux/kernfs.h
+++ b/include/linux/kernfs.h
@@ -50,7 +50,24 @@ enum kernfs_node_flag {
/* @flags for kernfs_create_root() */
enum kernfs_root_flag {
- KERNFS_ROOT_CREATE_DEACTIVATED = 0x0001,
+ /*
+ * kernfs_nodes are created in the deactivated state and invisible.
+ * They require explicit kernfs_activate() to become visible. This
+ * can be used to make related nodes become visible atomically
+ * after all nodes are created successfully.
+ */
+ KERNFS_ROOT_CREATE_DEACTIVATED = 0x0001,
+
+ /*
+ * For regular flies, if the opener has CAP_DAC_OVERRIDE, open(2)
+ * succeeds regardless of the RW permissions. sysfs had an extra
+ * layer of enforcement where open(2) fails with -EACCES regardless
+ * of CAP_DAC_OVERRIDE if the permission doesn't have the
+ * respective read or write access at all (none of S_IRUGO or
+ * S_IWUGO) or the respective operation isn't implemented. The
+ * following flag enables that behavior.
+ */
+ KERNFS_ROOT_EXTRA_OPEN_PERM_CHECK = 0x0002,
};
/* type-specific structures for kernfs_node union members */
diff --git a/include/linux/of.h b/include/linux/of.h
index 3bad8d106e0e..e6f0988c1c68 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -349,7 +349,7 @@ int of_device_is_stdout_path(struct device_node *dn);
#else /* CONFIG_OF */
-static inline const char* of_node_full_name(struct device_node *np)
+static inline const char* of_node_full_name(const struct device_node *np)
{
return "<no-node>";
}
diff --git a/include/uapi/asm-generic/resource.h b/include/uapi/asm-generic/resource.h
index f863428796d5..c6d10af50123 100644
--- a/include/uapi/asm-generic/resource.h
+++ b/include/uapi/asm-generic/resource.h
@@ -57,12 +57,5 @@
# define RLIM_INFINITY (~0UL)
#endif
-/*
- * RLIMIT_STACK default maximum - some architectures override it:
- */
-#ifndef _STK_LIM_MAX
-# define _STK_LIM_MAX RLIM_INFINITY
-#endif
-
#endif /* _UAPI_ASM_GENERIC_RESOURCE_H */
diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h
index 6db66783d268..333640608087 100644
--- a/include/uapi/asm-generic/unistd.h
+++ b/include/uapi/asm-generic/unistd.h
@@ -697,9 +697,11 @@ __SYSCALL(__NR_finit_module, sys_finit_module)
__SYSCALL(__NR_sched_setattr, sys_sched_setattr)
#define __NR_sched_getattr 275
__SYSCALL(__NR_sched_getattr, sys_sched_getattr)
+#define __NR_renameat2 276
+__SYSCALL(__NR_renameat2, sys_renameat2)
#undef __NR_syscalls
-#define __NR_syscalls 276
+#define __NR_syscalls 277
/*
* All syscalls below here should go away really,
diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
index 11917f747cb4..1b1efddb91cd 100644
--- a/include/uapi/linux/audit.h
+++ b/include/uapi/linux/audit.h
@@ -331,9 +331,17 @@ enum {
#define AUDIT_FAIL_PRINTK 1
#define AUDIT_FAIL_PANIC 2
+/*
+ * These bits disambiguate different calling conventions that share an
+ * ELF machine type, bitness, and endianness
+ */
+#define __AUDIT_ARCH_CONVENTION_MASK 0x30000000
+#define __AUDIT_ARCH_CONVENTION_MIPS64_N32 0x20000000
+
/* distinguish syscall tables */
#define __AUDIT_ARCH_64BIT 0x80000000
#define __AUDIT_ARCH_LE 0x40000000
+
#define AUDIT_ARCH_ALPHA (EM_ALPHA|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
#define AUDIT_ARCH_ARM (EM_ARM|__AUDIT_ARCH_LE)
#define AUDIT_ARCH_ARMEB (EM_ARM)
@@ -346,7 +354,11 @@ enum {
#define AUDIT_ARCH_MIPS (EM_MIPS)
#define AUDIT_ARCH_MIPSEL (EM_MIPS|__AUDIT_ARCH_LE)
#define AUDIT_ARCH_MIPS64 (EM_MIPS|__AUDIT_ARCH_64BIT)
+#define AUDIT_ARCH_MIPS64N32 (EM_MIPS|__AUDIT_ARCH_64BIT|\
+ __AUDIT_ARCH_CONVENTION_MIPS64_N32)
#define AUDIT_ARCH_MIPSEL64 (EM_MIPS|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
+#define AUDIT_ARCH_MIPSEL64N32 (EM_MIPS|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE\
+ __AUDIT_ARCH_CONVENTION_MIPS64_N32)
#define AUDIT_ARCH_OPENRISC (EM_OPENRISC)
#define AUDIT_ARCH_PARISC (EM_PARISC)
#define AUDIT_ARCH_PARISC64 (EM_PARISC|__AUDIT_ARCH_64BIT)
diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 9fc6219d3848..224948342f14 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -94,9 +94,11 @@ enum {
SNDRV_HWDEP_IFACE_HDA, /* HD-audio */
SNDRV_HWDEP_IFACE_USB_STREAM, /* direct access to usb stream */
SNDRV_HWDEP_IFACE_FW_DICE, /* TC DICE FireWire device */
+ SNDRV_HWDEP_IFACE_FW_FIREWORKS, /* Echo Audio Fireworks based device */
+ SNDRV_HWDEP_IFACE_FW_BEBOB, /* BridgeCo BeBoB based device */
/* Don't forget to change the following: */
- SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_DICE
+ SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_BEBOB
};
struct snd_hwdep_info {
diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h
index 59f5961302bf..af4bd136c75d 100644
--- a/include/uapi/sound/firewire.h
+++ b/include/uapi/sound/firewire.h
@@ -2,11 +2,13 @@
#define _UAPI_SOUND_FIREWIRE_H_INCLUDED
#include <linux/ioctl.h>
+#include <linux/types.h>
/* events can be read() from the hwdep device */
#define SNDRV_FIREWIRE_EVENT_LOCK_STATUS 0x000010cc
#define SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION 0xd1ce004e
+#define SNDRV_FIREWIRE_EVENT_EFW_RESPONSE 0x4e617475
struct snd_firewire_event_common {
unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */
@@ -22,10 +24,27 @@ struct snd_firewire_event_dice_notification {
unsigned int notification; /* DICE-specific bits */
};
+#define SND_EFW_TRANSACTION_USER_SEQNUM_MAX ((__u32)((__u16)~0) - 1)
+/* each field should be in big endian */
+struct snd_efw_transaction {
+ __be32 length;
+ __be32 version;
+ __be32 seqnum;
+ __be32 category;
+ __be32 command;
+ __be32 status;
+ __be32 params[0];
+};
+struct snd_firewire_event_efw_response {
+ unsigned int type;
+ __be32 response[0]; /* some responses */
+};
+
union snd_firewire_event {
struct snd_firewire_event_common common;
struct snd_firewire_event_lock_status lock_status;
struct snd_firewire_event_dice_notification dice_notification;
+ struct snd_firewire_event_efw_response efw_response;
};
@@ -34,7 +53,9 @@ union snd_firewire_event {
#define SNDRV_FIREWIRE_IOCTL_UNLOCK _IO('H', 0xfa)
#define SNDRV_FIREWIRE_TYPE_DICE 1
-/* Fireworks, AV/C, RME, MOTU, ... */
+#define SNDRV_FIREWIRE_TYPE_FIREWORKS 2
+#define SNDRV_FIREWIRE_TYPE_BEBOB 3
+/* AV/C, RME, MOTU, ... */
struct snd_firewire_get_info {
unsigned int type; /* SNDRV_FIREWIRE_TYPE_xxx */
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 9fcdaa705b6c..3f1ca934a237 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -348,7 +348,7 @@ struct cgrp_cset_link {
* reference-counted, to improve performance when child cgroups
* haven't been created.
*/
-static struct css_set init_css_set = {
+struct css_set init_css_set = {
.refcount = ATOMIC_INIT(1),
.cgrp_links = LIST_HEAD_INIT(init_css_set.cgrp_links),
.tasks = LIST_HEAD_INIT(init_css_set.tasks),
@@ -1495,7 +1495,7 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type,
*/
if (!use_task_css_set_links)
cgroup_enable_task_cg_lists();
-retry:
+
mutex_lock(&cgroup_tree_mutex);
mutex_lock(&cgroup_mutex);
@@ -1503,7 +1503,7 @@ retry:
ret = parse_cgroupfs_options(data, &opts);
if (ret)
goto out_unlock;
-
+retry:
/* look for a matching existing root */
if (!opts.subsys_mask && !opts.none && !opts.name) {
cgrp_dfl_root_visible = true;
@@ -1562,9 +1562,9 @@ retry:
if (!atomic_inc_not_zero(&root->cgrp.refcnt)) {
mutex_unlock(&cgroup_mutex);
mutex_unlock(&cgroup_tree_mutex);
- kfree(opts.release_agent);
- kfree(opts.name);
msleep(10);
+ mutex_lock(&cgroup_tree_mutex);
+ mutex_lock(&cgroup_mutex);
goto retry;
}
diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c
index 2bc4a2256444..345628c78b5b 100644
--- a/kernel/cgroup_freezer.c
+++ b/kernel/cgroup_freezer.c
@@ -21,6 +21,7 @@
#include <linux/uaccess.h>
#include <linux/freezer.h>
#include <linux/seq_file.h>
+#include <linux/mutex.h>
/*
* A cgroup is freezing if any FREEZING flags are set. FREEZING_SELF is
@@ -42,9 +43,10 @@ enum freezer_state_flags {
struct freezer {
struct cgroup_subsys_state css;
unsigned int state;
- spinlock_t lock;
};
+static DEFINE_MUTEX(freezer_mutex);
+
static inline struct freezer *css_freezer(struct cgroup_subsys_state *css)
{
return css ? container_of(css, struct freezer, css) : NULL;
@@ -93,7 +95,6 @@ freezer_css_alloc(struct cgroup_subsys_state *parent_css)
if (!freezer)
return ERR_PTR(-ENOMEM);
- spin_lock_init(&freezer->lock);
return &freezer->css;
}
@@ -110,14 +111,7 @@ static int freezer_css_online(struct cgroup_subsys_state *css)
struct freezer *freezer = css_freezer(css);
struct freezer *parent = parent_freezer(freezer);
- /*
- * The following double locking and freezing state inheritance
- * guarantee that @cgroup can never escape ancestors' freezing
- * states. See css_for_each_descendant_pre() for details.
- */
- if (parent)
- spin_lock_irq(&parent->lock);
- spin_lock_nested(&freezer->lock, SINGLE_DEPTH_NESTING);
+ mutex_lock(&freezer_mutex);
freezer->state |= CGROUP_FREEZER_ONLINE;
@@ -126,10 +120,7 @@ static int freezer_css_online(struct cgroup_subsys_state *css)
atomic_inc(&system_freezing_cnt);
}
- spin_unlock(&freezer->lock);
- if (parent)
- spin_unlock_irq(&parent->lock);
-
+ mutex_unlock(&freezer_mutex);
return 0;
}
@@ -144,14 +135,14 @@ static void freezer_css_offline(struct cgroup_subsys_state *css)
{
struct freezer *freezer = css_freezer(css);
- spin_lock_irq(&freezer->lock);
+ mutex_lock(&freezer_mutex);
if (freezer->state & CGROUP_FREEZING)
atomic_dec(&system_freezing_cnt);
freezer->state = 0;
- spin_unlock_irq(&freezer->lock);
+ mutex_unlock(&freezer_mutex);
}
static void freezer_css_free(struct cgroup_subsys_state *css)
@@ -175,7 +166,7 @@ static void freezer_attach(struct cgroup_subsys_state *new_css,
struct task_struct *task;
bool clear_frozen = false;
- spin_lock_irq(&freezer->lock);
+ mutex_lock(&freezer_mutex);
/*
* Make the new tasks conform to the current state of @new_css.
@@ -197,21 +188,13 @@ static void freezer_attach(struct cgroup_subsys_state *new_css,
}
}
- spin_unlock_irq(&freezer->lock);
-
- /*
- * Propagate FROZEN clearing upwards. We may race with
- * update_if_frozen(), but as long as both work bottom-up, either
- * update_if_frozen() sees child's FROZEN cleared or we clear the
- * parent's FROZEN later. No parent w/ !FROZEN children can be
- * left FROZEN.
- */
+ /* propagate FROZEN clearing upwards */
while (clear_frozen && (freezer = parent_freezer(freezer))) {
- spin_lock_irq(&freezer->lock);
freezer->state &= ~CGROUP_FROZEN;
clear_frozen = freezer->state & CGROUP_FREEZING;
- spin_unlock_irq(&freezer->lock);
}
+
+ mutex_unlock(&freezer_mutex);
}
/**
@@ -228,9 +211,6 @@ static void freezer_fork(struct task_struct *task)
{
struct freezer *freezer;
- rcu_read_lock();
- freezer = task_freezer(task);
-
/*
* The root cgroup is non-freezable, so we can skip locking the
* freezer. This is safe regardless of race with task migration.
@@ -238,24 +218,18 @@ static void freezer_fork(struct task_struct *task)
* to do. If we lost and root is the new cgroup, noop is still the
* right thing to do.
*/
- if (!parent_freezer(freezer))
- goto out;
+ if (task_css_is_root(task, freezer_cgrp_id))
+ return;
- /*
- * Grab @freezer->lock and freeze @task after verifying @task still
- * belongs to @freezer and it's freezing. The former is for the
- * case where we have raced against task migration and lost and
- * @task is already in a different cgroup which may not be frozen.
- * This isn't strictly necessary as freeze_task() is allowed to be
- * called spuriously but let's do it anyway for, if nothing else,
- * documentation.
- */
- spin_lock_irq(&freezer->lock);
- if (freezer == task_freezer(task) && (freezer->state & CGROUP_FREEZING))
+ mutex_lock(&freezer_mutex);
+ rcu_read_lock();
+
+ freezer = task_freezer(task);
+ if (freezer->state & CGROUP_FREEZING)
freeze_task(task);
- spin_unlock_irq(&freezer->lock);
-out:
+
rcu_read_unlock();
+ mutex_unlock(&freezer_mutex);
}
/**
@@ -281,22 +255,24 @@ static void update_if_frozen(struct cgroup_subsys_state *css)
struct css_task_iter it;
struct task_struct *task;
- WARN_ON_ONCE(!rcu_read_lock_held());
-
- spin_lock_irq(&freezer->lock);
+ lockdep_assert_held(&freezer_mutex);
if (!(freezer->state & CGROUP_FREEZING) ||
(freezer->state & CGROUP_FROZEN))
- goto out_unlock;
+ return;
/* are all (live) children frozen? */
+ rcu_read_lock();
css_for_each_child(pos, css) {
struct freezer *child = css_freezer(pos);
if ((child->state & CGROUP_FREEZER_ONLINE) &&
- !(child->state & CGROUP_FROZEN))
- goto out_unlock;
+ !(child->state & CGROUP_FROZEN)) {
+ rcu_read_unlock();
+ return;
+ }
}
+ rcu_read_unlock();
/* are all tasks frozen? */
css_task_iter_start(css, &it);
@@ -317,21 +293,29 @@ static void update_if_frozen(struct cgroup_subsys_state *css)
freezer->state |= CGROUP_FROZEN;
out_iter_end:
css_task_iter_end(&it);
-out_unlock:
- spin_unlock_irq(&freezer->lock);
}
static int freezer_read(struct seq_file *m, void *v)
{
struct cgroup_subsys_state *css = seq_css(m), *pos;
+ mutex_lock(&freezer_mutex);
rcu_read_lock();
/* update states bottom-up */
- css_for_each_descendant_post(pos, css)
+ css_for_each_descendant_post(pos, css) {
+ if (!css_tryget(pos))
+ continue;
+ rcu_read_unlock();
+
update_if_frozen(pos);
+ rcu_read_lock();
+ css_put(pos);
+ }
+
rcu_read_unlock();
+ mutex_unlock(&freezer_mutex);
seq_puts(m, freezer_state_strs(css_freezer(css)->state));
seq_putc(m, '\n');
@@ -373,7 +357,7 @@ static void freezer_apply_state(struct freezer *freezer, bool freeze,
unsigned int state)
{
/* also synchronizes against task migration, see freezer_attach() */
- lockdep_assert_held(&freezer->lock);
+ lockdep_assert_held(&freezer_mutex);
if (!(freezer->state & CGROUP_FREEZER_ONLINE))
return;
@@ -414,31 +398,29 @@ static void freezer_change_state(struct freezer *freezer, bool freeze)
* descendant will try to inherit its parent's FREEZING state as
* CGROUP_FREEZING_PARENT.
*/
+ mutex_lock(&freezer_mutex);
rcu_read_lock();
css_for_each_descendant_pre(pos, &freezer->css) {
struct freezer *pos_f = css_freezer(pos);
struct freezer *parent = parent_freezer(pos_f);
- spin_lock_irq(&pos_f->lock);
+ if (!css_tryget(pos))
+ continue;
+ rcu_read_unlock();
- if (pos_f == freezer) {
+ if (pos_f == freezer)
freezer_apply_state(pos_f, freeze,
CGROUP_FREEZING_SELF);
- } else {
- /*
- * Our update to @parent->state is already visible
- * which is all we need. No need to lock @parent.
- * For more info on synchronization, see
- * freezer_post_create().
- */
+ else
freezer_apply_state(pos_f,
parent->state & CGROUP_FREEZING,
CGROUP_FREEZING_PARENT);
- }
- spin_unlock_irq(&pos_f->lock);
+ rcu_read_lock();
+ css_put(pos);
}
rcu_read_unlock();
+ mutex_unlock(&freezer_mutex);
}
static int freezer_write(struct cgroup_subsys_state *css, struct cftype *cft,
diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c
index 6b715c0af1b1..e0501fe7140d 100644
--- a/kernel/hrtimer.c
+++ b/kernel/hrtimer.c
@@ -990,11 +990,8 @@ int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
/* Remove an active timer from the queue: */
ret = remove_hrtimer(timer, base);
- /* Switch the timer base, if necessary: */
- new_base = switch_hrtimer_base(timer, base, mode & HRTIMER_MODE_PINNED);
-
if (mode & HRTIMER_MODE_REL) {
- tim = ktime_add_safe(tim, new_base->get_time());
+ tim = ktime_add_safe(tim, base->get_time());
/*
* CONFIG_TIME_LOW_RES is a temporary way for architectures
* to signal that they simply return xtime in
@@ -1009,6 +1006,9 @@ int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
hrtimer_set_expires_range_ns(timer, tim, delta_ns);
+ /* Switch the timer base, if necessary: */
+ new_base = switch_hrtimer_base(timer, base, mode & HRTIMER_MODE_PINNED);
+
timer_stats_hrtimer_set_start_info(timer);
leftmost = enqueue_hrtimer(timer, new_base);
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 0ee63af30bd1..8edc87185427 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -1916,6 +1916,12 @@ static void send_mayday(struct work_struct *work)
/* mayday mayday mayday */
if (list_empty(&pwq->mayday_node)) {
+ /*
+ * If @pwq is for an unbound wq, its base ref may be put at
+ * any time due to an attribute change. Pin @pwq until the
+ * rescuer is done with it.
+ */
+ get_pwq(pwq);
list_add_tail(&pwq->mayday_node, &wq->maydays);
wake_up_process(wq->rescuer->task);
}
@@ -2398,6 +2404,7 @@ static int rescuer_thread(void *__rescuer)
struct worker *rescuer = __rescuer;
struct workqueue_struct *wq = rescuer->rescue_wq;
struct list_head *scheduled = &rescuer->scheduled;
+ bool should_stop;
set_user_nice(current, RESCUER_NICE_LEVEL);
@@ -2409,11 +2416,15 @@ static int rescuer_thread(void *__rescuer)
repeat:
set_current_state(TASK_INTERRUPTIBLE);
- if (kthread_should_stop()) {
- __set_current_state(TASK_RUNNING);
- rescuer->task->flags &= ~PF_WQ_WORKER;
- return 0;
- }
+ /*
+ * By the time the rescuer is requested to stop, the workqueue
+ * shouldn't have any work pending, but @wq->maydays may still have
+ * pwq(s) queued. This can happen by non-rescuer workers consuming
+ * all the work items before the rescuer got to them. Go through
+ * @wq->maydays processing before acting on should_stop so that the
+ * list is always empty on exit.
+ */
+ should_stop = kthread_should_stop();
/* see whether any pwq is asking for help */
spin_lock_irq(&wq_mayday_lock);
@@ -2445,6 +2456,12 @@ repeat:
process_scheduled_works(rescuer);
/*
+ * Put the reference grabbed by send_mayday(). @pool won't
+ * go away while we're holding its lock.
+ */
+ put_pwq(pwq);
+
+ /*
* Leave this pool. If keep_working() is %true, notify a
* regular worker; otherwise, we end up with 0 concurrency
* and stalling the execution.
@@ -2459,6 +2476,12 @@ repeat:
spin_unlock_irq(&wq_mayday_lock);
+ if (should_stop) {
+ __set_current_state(TASK_RUNNING);
+ rescuer->task->flags &= ~PF_WQ_WORKER;
+ return 0;
+ }
+
/* rescuers should never participate in concurrency management */
WARN_ON_ONCE(!(rescuer->flags & WORKER_NOT_RUNNING));
schedule();
@@ -4100,7 +4123,8 @@ static void wq_update_unbound_numa(struct workqueue_struct *wq, int cpu,
if (!pwq) {
pr_warning("workqueue: allocation failed while updating NUMA affinity of \"%s\"\n",
wq->name);
- goto out_unlock;
+ mutex_lock(&wq->mutex);
+ goto use_dfl_pwq;
}
/*
diff --git a/mm/Kconfig b/mm/Kconfig
index ebe5880c29d6..1b5a95f0fa01 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -581,3 +581,18 @@ config PGTABLE_MAPPING
config GENERIC_EARLY_IOREMAP
bool
+
+config MAX_STACK_SIZE_MB
+ int "Maximum user stack size for 32-bit processes (MB)"
+ default 80
+ range 8 256 if METAG
+ range 8 2048
+ depends on STACK_GROWSUP && (!64BIT || COMPAT)
+ help
+ This is the maximum stack size in Megabytes in the VM layout of 32-bit
+ user processes when the stack grows upwards (currently only on parisc
+ and metag arch). The stack will be located at the highest memory
+ address minus the given value, unless the RLIMIT_STACK hard limit is
+ changed to a smaller value in which case that is used.
+
+ A sane initial value is 80 MB.
diff --git a/mm/kmemleak.c b/mm/kmemleak.c
index 91d67eaee050..8d2fcdfeff7f 100644
--- a/mm/kmemleak.c
+++ b/mm/kmemleak.c
@@ -1775,10 +1775,9 @@ void __init kmemleak_init(void)
int i;
unsigned long flags;
- kmemleak_early_log = 0;
-
#ifdef CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF
if (!kmemleak_skip_disable) {
+ kmemleak_early_log = 0;
kmemleak_disable();
return;
}
@@ -1796,6 +1795,7 @@ void __init kmemleak_init(void)
/* the kernel is still in UP mode, so disabling the IRQs is enough */
local_irq_save(flags);
+ kmemleak_early_log = 0;
if (kmemleak_error) {
local_irq_restore(flags);
return;
diff --git a/mm/mremap.c b/mm/mremap.c
index 0843feb66f3d..05f1180e9f21 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -194,10 +194,17 @@ unsigned long move_page_tables(struct vm_area_struct *vma,
break;
if (pmd_trans_huge(*old_pmd)) {
int err = 0;
- if (extent == HPAGE_PMD_SIZE)
+ if (extent == HPAGE_PMD_SIZE) {
+ VM_BUG_ON(vma->vm_file || !vma->anon_vma);
+ /* See comment in move_ptes() */
+ if (need_rmap_locks)
+ anon_vma_lock_write(vma->anon_vma);
err = move_huge_pmd(vma, new_vma, old_addr,
new_addr, old_end,
old_pmd, new_pmd);
+ if (need_rmap_locks)
+ anon_vma_unlock_write(vma->anon_vma);
+ }
if (err > 0) {
need_flush = true;
continue;
diff --git a/mm/percpu.c b/mm/percpu.c
index 63e24fb4387b..2ddf9a990dbd 100644
--- a/mm/percpu.c
+++ b/mm/percpu.c
@@ -610,7 +610,7 @@ static struct pcpu_chunk *pcpu_alloc_chunk(void)
chunk->map = pcpu_mem_zalloc(PCPU_DFL_MAP_ALLOC *
sizeof(chunk->map[0]));
if (!chunk->map) {
- kfree(chunk);
+ pcpu_mem_free(chunk, pcpu_chunk_struct_size);
return NULL;
}
diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c
index dac7f9b98687..1948d592aa54 100644
--- a/net/ceph/messenger.c
+++ b/net/ceph/messenger.c
@@ -557,7 +557,7 @@ static int ceph_tcp_sendmsg(struct socket *sock, struct kvec *iov,
return r;
}
-static int ceph_tcp_sendpage(struct socket *sock, struct page *page,
+static int __ceph_tcp_sendpage(struct socket *sock, struct page *page,
int offset, size_t size, bool more)
{
int flags = MSG_DONTWAIT | MSG_NOSIGNAL | (more ? MSG_MORE : MSG_EOR);
@@ -570,6 +570,24 @@ static int ceph_tcp_sendpage(struct socket *sock, struct page *page,
return ret;
}
+static int ceph_tcp_sendpage(struct socket *sock, struct page *page,
+ int offset, size_t size, bool more)
+{
+ int ret;
+ struct kvec iov;
+
+ /* sendpage cannot properly handle pages with page_count == 0,
+ * we need to fallback to sendmsg if that's the case */
+ if (page_count(page) >= 1)
+ return __ceph_tcp_sendpage(sock, page, offset, size, more);
+
+ iov.iov_base = kmap(page) + offset;
+ iov.iov_len = size;
+ ret = ceph_tcp_sendmsg(sock, &iov, 1, size, more);
+ kunmap(page);
+
+ return ret;
+}
/*
* Shutdown/close the socket for the given connection.
diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c
index 8b8a5a24b223..c547e46084d3 100644
--- a/net/ceph/osdmap.c
+++ b/net/ceph/osdmap.c
@@ -329,6 +329,11 @@ static struct crush_map *crush_decode(void *pbyval, void *end)
dout("crush decode tunable chooseleaf_descend_once = %d",
c->chooseleaf_descend_once);
+ ceph_decode_need(p, end, sizeof(u8), done);
+ c->chooseleaf_vary_r = ceph_decode_8(p);
+ dout("crush decode tunable chooseleaf_vary_r = %d",
+ c->chooseleaf_vary_r);
+
done:
dout("crush_decode success\n");
return c;
diff --git a/scripts/checksyscalls.sh b/scripts/checksyscalls.sh
index fd8fa9aa7c4e..5b3add31f9f1 100755
--- a/scripts/checksyscalls.sh
+++ b/scripts/checksyscalls.sh
@@ -25,7 +25,7 @@ cat << EOF
#define __IGNORE_rmdir /* unlinkat */
#define __IGNORE_lchown /* fchownat */
#define __IGNORE_access /* faccessat */
-#define __IGNORE_rename /* renameat */
+#define __IGNORE_rename /* renameat2 */
#define __IGNORE_readlink /* readlinkat */
#define __IGNORE_symlink /* symlinkat */
#define __IGNORE_utimes /* futimesat */
@@ -37,6 +37,9 @@ cat << EOF
#define __IGNORE_lstat64 /* fstatat64 */
#endif
+/* Missing flags argument */
+#define __IGNORE_renameat /* renameat2 */
+
/* CLOEXEC flag */
#define __IGNORE_pipe /* pipe2 */
#define __IGNORE_dup2 /* dup3 */
diff --git a/security/device_cgroup.c b/security/device_cgroup.c
index 8365909f5f8c..9134dbf70d3e 100644
--- a/security/device_cgroup.c
+++ b/security/device_cgroup.c
@@ -306,57 +306,138 @@ static int devcgroup_seq_show(struct seq_file *m, void *v)
}
/**
- * may_access - verifies if a new exception is part of what is allowed
- * by a dev cgroup based on the default policy +
- * exceptions. This is used to make sure a child cgroup
- * won't have more privileges than its parent or to
- * verify if a certain access is allowed.
- * @dev_cgroup: dev cgroup to be tested against
- * @refex: new exception
- * @behavior: behavior of the exception
+ * match_exception - iterates the exception list trying to find a complete match
+ * @exceptions: list of exceptions
+ * @type: device type (DEV_BLOCK or DEV_CHAR)
+ * @major: device file major number, ~0 to match all
+ * @minor: device file minor number, ~0 to match all
+ * @access: permission mask (ACC_READ, ACC_WRITE, ACC_MKNOD)
+ *
+ * It is considered a complete match if an exception is found that will
+ * contain the entire range of provided parameters.
+ *
+ * Return: true in case it matches an exception completely
*/
-static bool may_access(struct dev_cgroup *dev_cgroup,
- struct dev_exception_item *refex,
- enum devcg_behavior behavior)
+static bool match_exception(struct list_head *exceptions, short type,
+ u32 major, u32 minor, short access)
{
struct dev_exception_item *ex;
- bool match = false;
- rcu_lockdep_assert(rcu_read_lock_held() ||
- lockdep_is_held(&devcgroup_mutex),
- "device_cgroup::may_access() called without proper synchronization");
+ list_for_each_entry_rcu(ex, exceptions, list) {
+ if ((type & DEV_BLOCK) && !(ex->type & DEV_BLOCK))
+ continue;
+ if ((type & DEV_CHAR) && !(ex->type & DEV_CHAR))
+ continue;
+ if (ex->major != ~0 && ex->major != major)
+ continue;
+ if (ex->minor != ~0 && ex->minor != minor)
+ continue;
+ /* provided access cannot have more than the exception rule */
+ if (access & (~ex->access))
+ continue;
+ return true;
+ }
+ return false;
+}
+
+/**
+ * match_exception_partial - iterates the exception list trying to find a partial match
+ * @exceptions: list of exceptions
+ * @type: device type (DEV_BLOCK or DEV_CHAR)
+ * @major: device file major number, ~0 to match all
+ * @minor: device file minor number, ~0 to match all
+ * @access: permission mask (ACC_READ, ACC_WRITE, ACC_MKNOD)
+ *
+ * It is considered a partial match if an exception's range is found to
+ * contain *any* of the devices specified by provided parameters. This is
+ * used to make sure no extra access is being granted that is forbidden by
+ * any of the exception list.
+ *
+ * Return: true in case the provided range mat matches an exception completely
+ */
+static bool match_exception_partial(struct list_head *exceptions, short type,
+ u32 major, u32 minor, short access)
+{
+ struct dev_exception_item *ex;
- list_for_each_entry_rcu(ex, &dev_cgroup->exceptions, list) {
- if ((refex->type & DEV_BLOCK) && !(ex->type & DEV_BLOCK))
+ list_for_each_entry_rcu(ex, exceptions, list) {
+ if ((type & DEV_BLOCK) && !(ex->type & DEV_BLOCK))
continue;
- if ((refex->type & DEV_CHAR) && !(ex->type & DEV_CHAR))
+ if ((type & DEV_CHAR) && !(ex->type & DEV_CHAR))
continue;
- if (ex->major != ~0 && ex->major != refex->major)
+ /*
+ * We must be sure that both the exception and the provided
+ * range aren't masking all devices
+ */
+ if (ex->major != ~0 && major != ~0 && ex->major != major)
continue;
- if (ex->minor != ~0 && ex->minor != refex->minor)
+ if (ex->minor != ~0 && minor != ~0 && ex->minor != minor)
continue;
- if (refex->access & (~ex->access))
+ /*
+ * In order to make sure the provided range isn't matching
+ * an exception, all its access bits shouldn't match the
+ * exception's access bits
+ */
+ if (!(access & ex->access))
continue;
- match = true;
- break;
+ return true;
}
+ return false;
+}
+
+/**
+ * verify_new_ex - verifies if a new exception is allowed by parent cgroup's permissions
+ * @dev_cgroup: dev cgroup to be tested against
+ * @refex: new exception
+ * @behavior: behavior of the exception's dev_cgroup
+ *
+ * This is used to make sure a child cgroup won't have more privileges
+ * than its parent
+ */
+static bool verify_new_ex(struct dev_cgroup *dev_cgroup,
+ struct dev_exception_item *refex,
+ enum devcg_behavior behavior)
+{
+ bool match = false;
+
+ rcu_lockdep_assert(rcu_read_lock_held() ||
+ lockdep_is_held(&devcgroup_mutex),
+ "device_cgroup:verify_new_ex called without proper synchronization");
if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW) {
if (behavior == DEVCG_DEFAULT_ALLOW) {
- /* the exception will deny access to certain devices */
+ /*
+ * new exception in the child doesn't matter, only
+ * adding extra restrictions
+ */
return true;
} else {
- /* the exception will allow access to certain devices */
+ /*
+ * new exception in the child will add more devices
+ * that can be acessed, so it can't match any of
+ * parent's exceptions, even slightly
+ */
+ match = match_exception_partial(&dev_cgroup->exceptions,
+ refex->type,
+ refex->major,
+ refex->minor,
+ refex->access);
+
if (match)
- /*
- * a new exception allowing access shouldn't
- * match an parent's exception
- */
return false;
return true;
}
} else {
- /* only behavior == DEVCG_DEFAULT_DENY allowed here */
+ /*
+ * Only behavior == DEVCG_DEFAULT_DENY allowed here, therefore
+ * the new exception will add access to more devices and must
+ * be contained completely in an parent's exception to be
+ * allowed
+ */
+ match = match_exception(&dev_cgroup->exceptions, refex->type,
+ refex->major, refex->minor,
+ refex->access);
+
if (match)
/* parent has an exception that matches the proposed */
return true;
@@ -378,7 +459,38 @@ static int parent_has_perm(struct dev_cgroup *childcg,
if (!parent)
return 1;
- return may_access(parent, ex, childcg->behavior);
+ return verify_new_ex(parent, ex, childcg->behavior);
+}
+
+/**
+ * parent_allows_removal - verify if it's ok to remove an exception
+ * @childcg: child cgroup from where the exception will be removed
+ * @ex: exception being removed
+ *
+ * When removing an exception in cgroups with default ALLOW policy, it must
+ * be checked if removing it will give the child cgroup more access than the
+ * parent.
+ *
+ * Return: true if it's ok to remove exception, false otherwise
+ */
+static bool parent_allows_removal(struct dev_cgroup *childcg,
+ struct dev_exception_item *ex)
+{
+ struct dev_cgroup *parent = css_to_devcgroup(css_parent(&childcg->css));
+
+ if (!parent)
+ return true;
+
+ /* It's always allowed to remove access to devices */
+ if (childcg->behavior == DEVCG_DEFAULT_DENY)
+ return true;
+
+ /*
+ * Make sure you're not removing part or a whole exception existing in
+ * the parent cgroup
+ */
+ return !match_exception_partial(&parent->exceptions, ex->type,
+ ex->major, ex->minor, ex->access);
}
/**
@@ -616,17 +728,21 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup,
switch (filetype) {
case DEVCG_ALLOW:
- if (!parent_has_perm(devcgroup, &ex))
- return -EPERM;
/*
* If the default policy is to allow by default, try to remove
* an matching exception instead. And be silent about it: we
* don't want to break compatibility
*/
if (devcgroup->behavior == DEVCG_DEFAULT_ALLOW) {
+ /* Check if the parent allows removing it first */
+ if (!parent_allows_removal(devcgroup, &ex))
+ return -EPERM;
dev_exception_rm(devcgroup, &ex);
- return 0;
+ break;
}
+
+ if (!parent_has_perm(devcgroup, &ex))
+ return -EPERM;
rc = dev_exception_add(devcgroup, &ex);
break;
case DEVCG_DENY:
@@ -704,18 +820,18 @@ static int __devcgroup_check_permission(short type, u32 major, u32 minor,
short access)
{
struct dev_cgroup *dev_cgroup;
- struct dev_exception_item ex;
- int rc;
-
- memset(&ex, 0, sizeof(ex));
- ex.type = type;
- ex.major = major;
- ex.minor = minor;
- ex.access = access;
+ bool rc;
rcu_read_lock();
dev_cgroup = task_devcgroup(current);
- rc = may_access(dev_cgroup, &ex, dev_cgroup->behavior);
+ if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW)
+ /* Can't match any of the exceptions, even partially */
+ rc = !match_exception_partial(&dev_cgroup->exceptions,
+ type, major, minor, access);
+ else
+ /* Need to match completely one exception to be allowed */
+ rc = match_exception(&dev_cgroup->exceptions, type, major,
+ minor, access);
rcu_read_unlock();
if (!rc)
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index b3e274fe4a77..9f363fac7688 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -61,4 +61,63 @@ config SND_SCS1X
To compile this driver as a module, choose M here: the module
will be called snd-scs1x.
+config SND_FIREWORKS
+ tristate "Echo Fireworks board module support"
+ select SND_FIREWIRE_LIB
+ select SND_RAWMIDI
+ select SND_PCM
+ select SND_HWDEP
+ help
+ Say Y here to include support for FireWire devices based
+ on Echo Digital Audio Fireworks board:
+ * Mackie Onyx 400F/1200F
+ * Echo AudioFire12/8(until 2009 July)
+ * Echo AudioFire2/4/Pre8/8(since 2009 July)
+ * Echo Fireworks 8/HDMI
+ * Gibson Robot Interface Pack/GoldTop
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-fireworks.
+
+config SND_BEBOB
+ tristate "BridgeCo DM1000/DM1100/DM1500 with BeBoB firmware"
+ select SND_FIREWIRE_LIB
+ select SND_RAWMIDI
+ select SND_PCM
+ select SND_HWDEP
+ help
+ Say Y here to include support for FireWire devices based
+ on BridgeCo DM1000/DM1100/DM1500 with BeBoB firmware:
+ * Edirol FA-66/FA-101
+ * PreSonus FIREBOX/FIREPOD/FP10/Inspire1394
+ * BridgeCo RDAudio1/Audio5
+ * Mackie Onyx 1220/1620/1640 (Firewire I/O Card)
+ * Mackie d.2 (Firewire Option)
+ * Stanton FinalScratch 2 (ScratchAmp)
+ * Tascam IF-FW/DM
+ * Behringer XENIX UFX 1204/1604
+ * Behringer Digital Mixer X32 series (X-UF Card)
+ * Apogee Rosetta 200/400 (X-FireWire card)
+ * Apogee DA/AD/DD-16X (X-FireWire card)
+ * Apogee Ensemble
+ * ESI Quotafire610
+ * AcousticReality eARMasterOne
+ * CME MatrixKFW
+ * Phonic Helix Board 12 MkII/18 MkII/24 MkII
+ * Phonic Helix Board 12 Universal/18 Universal/24 Universal
+ * Lynx Aurora 8/16 (LT-FW)
+ * ICON FireXon
+ * PrismSound Orpheus/ADA-8XR
+ * TerraTec PHASE 24 FW/PHASE X24 FW/PHASE 88 Rack FW
+ * Terratec EWS MIC2/EWS MIC4
+ * Terratec Aureon 7.1 Firewire
+ * Yamaha GO44/GO46
+ * Focusrite Saffire/Saffire LE/SaffirePro10 IO/SaffirePro26 IO
+ * M-Audio Firewire410/AudioPhile/Solo
+ * M-Audio Ozonic/NRV10/ProfireLightBridge
+ * M-Audio Firewire 1814/ProjectMix IO
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-bebob.
+
endif # SND_FIREWIRE
diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile
index 509955061d30..fad8d49306ab 100644
--- a/sound/firewire/Makefile
+++ b/sound/firewire/Makefile
@@ -10,3 +10,5 @@ obj-$(CONFIG_SND_DICE) += snd-dice.o
obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o
obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
+obj-$(CONFIG_SND_FIREWORKS) += fireworks/
+obj-$(CONFIG_SND_BEBOB) += bebob/
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index 9048777228e2..31dd1cfc79b3 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -11,7 +11,10 @@
#include <linux/firewire.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/sched.h>
#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/rawmidi.h>
#include "amdtp.h"
#define TICKS_PER_CYCLE 3072
@@ -20,50 +23,78 @@
#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 µs */
+/* isochronous header parameters */
+#define ISO_DATA_LENGTH_SHIFT 16
#define TAG_CIP 1
+/* common isochronous packet header parameters */
#define CIP_EOH (1u << 31)
+#define CIP_EOH_MASK 0x80000000
#define CIP_FMT_AM (0x10 << 24)
-#define AMDTP_FDF_AM824 (0 << 19)
-#define AMDTP_FDF_SFC_SHIFT 16
+#define CIP_FMT_MASK 0x3f000000
+#define CIP_SYT_MASK 0x0000ffff
+#define CIP_SYT_NO_INFO 0xffff
+#define CIP_FDF_MASK 0x00ff0000
+#define CIP_FDF_SFC_SHIFT 16
+
+/*
+ * Audio and Music transfer protocol specific parameters
+ * only "Clock-based rate control mode" is supported
+ */
+#define AMDTP_FDF_AM824 (0 << (CIP_FDF_SFC_SHIFT + 3))
+#define AMDTP_FDF_NO_DATA 0xff
+#define AMDTP_DBS_MASK 0x00ff0000
+#define AMDTP_DBS_SHIFT 16
+#define AMDTP_DBC_MASK 0x000000ff
/* TODO: make these configurable */
#define INTERRUPT_INTERVAL 16
#define QUEUE_LENGTH 48
+#define IN_PACKET_HEADER_SIZE 4
+#define OUT_PACKET_HEADER_SIZE 0
+
static void pcm_period_tasklet(unsigned long data);
/**
- * amdtp_out_stream_init - initialize an AMDTP output stream structure
- * @s: the AMDTP output stream to initialize
+ * amdtp_stream_init - initialize an AMDTP stream structure
+ * @s: the AMDTP stream to initialize
* @unit: the target of the stream
+ * @dir: the direction of stream
* @flags: the packet transmission method to use
*/
-int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit,
- enum cip_out_flags flags)
+int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir, enum cip_flags flags)
{
s->unit = fw_unit_get(unit);
+ s->direction = dir;
s->flags = flags;
s->context = ERR_PTR(-1);
mutex_init(&s->mutex);
tasklet_init(&s->period_tasklet, pcm_period_tasklet, (unsigned long)s);
s->packet_index = 0;
+ init_waitqueue_head(&s->callback_wait);
+ s->callbacked = false;
+ s->sync_slave = NULL;
+
+ s->rx_blocks_for_midi = UINT_MAX;
+
return 0;
}
-EXPORT_SYMBOL(amdtp_out_stream_init);
+EXPORT_SYMBOL(amdtp_stream_init);
/**
- * amdtp_out_stream_destroy - free stream resources
- * @s: the AMDTP output stream to destroy
+ * amdtp_stream_destroy - free stream resources
+ * @s: the AMDTP stream to destroy
*/
-void amdtp_out_stream_destroy(struct amdtp_out_stream *s)
+void amdtp_stream_destroy(struct amdtp_stream *s)
{
- WARN_ON(amdtp_out_stream_running(s));
+ WARN_ON(amdtp_stream_running(s));
mutex_destroy(&s->mutex);
fw_unit_put(s->unit);
}
-EXPORT_SYMBOL(amdtp_out_stream_destroy);
+EXPORT_SYMBOL(amdtp_stream_destroy);
const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = {
[CIP_SFC_32000] = 8,
@@ -76,9 +107,75 @@ const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = {
};
EXPORT_SYMBOL(amdtp_syt_intervals);
+const unsigned int amdtp_rate_table[CIP_SFC_COUNT] = {
+ [CIP_SFC_32000] = 32000,
+ [CIP_SFC_44100] = 44100,
+ [CIP_SFC_48000] = 48000,
+ [CIP_SFC_88200] = 88200,
+ [CIP_SFC_96000] = 96000,
+ [CIP_SFC_176400] = 176400,
+ [CIP_SFC_192000] = 192000,
+};
+EXPORT_SYMBOL(amdtp_rate_table);
+
+/**
+ * amdtp_stream_add_pcm_hw_constraints - add hw constraints for PCM substream
+ * @s: the AMDTP stream, which must be initialized.
+ * @runtime: the PCM substream runtime
+ */
+int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime)
+{
+ int err;
+
+ /* AM824 in IEC 61883-6 can deliver 24bit data */
+ err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ if (err < 0)
+ goto end;
+
+ /*
+ * Currently firewire-lib processes 16 packets in one software
+ * interrupt callback. This equals to 2msec but actually the
+ * interval of the interrupts has a jitter.
+ * Additionally, even if adding a constraint to fit period size to
+ * 2msec, actual calculated frames per period doesn't equal to 2msec,
+ * depending on sampling rate.
+ * Anyway, the interval to call snd_pcm_period_elapsed() cannot 2msec.
+ * Here let us use 5msec for safe period interrupt.
+ */
+ err = snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+ 5000, UINT_MAX);
+ if (err < 0)
+ goto end;
+
+ /* Non-Blocking stream has no more constraints */
+ if (!(s->flags & CIP_BLOCKING))
+ goto end;
+
+ /*
+ * One AMDTP packet can include some frames. In blocking mode, the
+ * number equals to SYT_INTERVAL. So the number is 8, 16 or 32,
+ * depending on its sampling rate. For accurate period interrupt, it's
+ * preferrable to aligh period/buffer sizes to current SYT_INTERVAL.
+ *
+ * TODO: These constraints can be improved with propper rules.
+ * Currently apply LCM of SYT_INTEVALs.
+ */
+ err = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
+ if (err < 0)
+ goto end;
+ err = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
+end:
+ return err;
+}
+EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints);
+
/**
- * amdtp_out_stream_set_parameters - set stream parameters
- * @s: the AMDTP output stream to configure
+ * amdtp_stream_set_parameters - set stream parameters
+ * @s: the AMDTP stream to configure
* @rate: the sample rate
* @pcm_channels: the number of PCM samples in each data block, to be encoded
* as AM824 multi-bit linear audio
@@ -87,41 +184,30 @@ EXPORT_SYMBOL(amdtp_syt_intervals);
* The parameters must be set before the stream is started, and must not be
* changed while the stream is running.
*/
-void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s,
- unsigned int rate,
- unsigned int pcm_channels,
- unsigned int midi_ports)
+void amdtp_stream_set_parameters(struct amdtp_stream *s,
+ unsigned int rate,
+ unsigned int pcm_channels,
+ unsigned int midi_ports)
{
- static const unsigned int rates[] = {
- [CIP_SFC_32000] = 32000,
- [CIP_SFC_44100] = 44100,
- [CIP_SFC_48000] = 48000,
- [CIP_SFC_88200] = 88200,
- [CIP_SFC_96000] = 96000,
- [CIP_SFC_176400] = 176400,
- [CIP_SFC_192000] = 192000,
- };
- unsigned int sfc;
+ unsigned int i, sfc, midi_channels;
+
+ midi_channels = DIV_ROUND_UP(midi_ports, 8);
- if (WARN_ON(amdtp_out_stream_running(s)))
+ if (WARN_ON(amdtp_stream_running(s)) |
+ WARN_ON(pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM) |
+ WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI))
return;
- for (sfc = 0; sfc < CIP_SFC_COUNT; ++sfc)
- if (rates[sfc] == rate)
+ for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc)
+ if (amdtp_rate_table[sfc] == rate)
goto sfc_found;
WARN_ON(1);
return;
sfc_found:
- s->dual_wire = (s->flags & CIP_HI_DUALWIRE) && sfc > CIP_SFC_96000;
- if (s->dual_wire) {
- sfc -= 2;
- rate /= 2;
- pcm_channels *= 2;
- }
- s->sfc = sfc;
- s->data_block_quadlets = pcm_channels + DIV_ROUND_UP(midi_ports, 8);
s->pcm_channels = pcm_channels;
+ s->sfc = sfc;
+ s->data_block_quadlets = s->pcm_channels + midi_channels;
s->midi_ports = midi_ports;
s->syt_interval = amdtp_syt_intervals[sfc];
@@ -131,48 +217,50 @@ sfc_found:
if (s->flags & CIP_BLOCKING)
/* additional buffering needed to adjust for no-data packets */
s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate;
+
+ /* init the position map for PCM and MIDI channels */
+ for (i = 0; i < pcm_channels; i++)
+ s->pcm_positions[i] = i;
+ s->midi_position = s->pcm_channels;
}
-EXPORT_SYMBOL(amdtp_out_stream_set_parameters);
+EXPORT_SYMBOL(amdtp_stream_set_parameters);
/**
- * amdtp_out_stream_get_max_payload - get the stream's packet size
- * @s: the AMDTP output stream
+ * amdtp_stream_get_max_payload - get the stream's packet size
+ * @s: the AMDTP stream
*
* This function must not be called before the stream has been configured
- * with amdtp_out_stream_set_parameters().
+ * with amdtp_stream_set_parameters().
*/
-unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s)
+unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
{
return 8 + s->syt_interval * s->data_block_quadlets * 4;
}
-EXPORT_SYMBOL(amdtp_out_stream_get_max_payload);
+EXPORT_SYMBOL(amdtp_stream_get_max_payload);
-static void amdtp_write_s16(struct amdtp_out_stream *s,
+static void amdtp_write_s16(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames);
-static void amdtp_write_s32(struct amdtp_out_stream *s,
+static void amdtp_write_s32(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames);
-static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames);
-static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames);
+static void amdtp_read_s32(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames);
/**
- * amdtp_out_stream_set_pcm_format - set the PCM format
- * @s: the AMDTP output stream to configure
+ * amdtp_stream_set_pcm_format - set the PCM format
+ * @s: the AMDTP stream to configure
* @format: the format of the ALSA PCM device
*
* The sample format must be set after the other paramters (rate/PCM channels/
* MIDI) and before the stream is started, and must not be changed while the
* stream is running.
*/
-void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
- snd_pcm_format_t format)
+void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
+ snd_pcm_format_t format)
{
- if (WARN_ON(amdtp_out_stream_running(s)))
+ if (WARN_ON(amdtp_stream_pcm_running(s)))
return;
switch (format) {
@@ -180,41 +268,44 @@ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
WARN_ON(1);
/* fall through */
case SNDRV_PCM_FORMAT_S16:
- if (s->dual_wire)
- s->transfer_samples = amdtp_write_s16_dualwire;
- else
+ if (s->direction == AMDTP_OUT_STREAM) {
s->transfer_samples = amdtp_write_s16;
- break;
+ break;
+ }
+ WARN_ON(1);
+ /* fall through */
case SNDRV_PCM_FORMAT_S32:
- if (s->dual_wire)
- s->transfer_samples = amdtp_write_s32_dualwire;
- else
+ if (s->direction == AMDTP_OUT_STREAM)
s->transfer_samples = amdtp_write_s32;
+ else
+ s->transfer_samples = amdtp_read_s32;
break;
}
}
-EXPORT_SYMBOL(amdtp_out_stream_set_pcm_format);
+EXPORT_SYMBOL(amdtp_stream_set_pcm_format);
/**
- * amdtp_out_stream_pcm_prepare - prepare PCM device for running
- * @s: the AMDTP output stream
+ * amdtp_stream_pcm_prepare - prepare PCM device for running
+ * @s: the AMDTP stream
*
* This function should be called from the PCM device's .prepare callback.
*/
-void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s)
+void amdtp_stream_pcm_prepare(struct amdtp_stream *s)
{
tasklet_kill(&s->period_tasklet);
s->pcm_buffer_pointer = 0;
s->pcm_period_pointer = 0;
s->pointer_flush = true;
}
-EXPORT_SYMBOL(amdtp_out_stream_pcm_prepare);
+EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
-static unsigned int calculate_data_blocks(struct amdtp_out_stream *s)
+static unsigned int calculate_data_blocks(struct amdtp_stream *s)
{
unsigned int phase, data_blocks;
- if (!cip_sfc_is_base_44100(s->sfc)) {
+ if (s->flags & CIP_BLOCKING)
+ data_blocks = s->syt_interval;
+ else if (!cip_sfc_is_base_44100(s->sfc)) {
/* Sample_rate / 8000 is an integer, and precomputed. */
data_blocks = s->data_block_state;
} else {
@@ -243,7 +334,7 @@ static unsigned int calculate_data_blocks(struct amdtp_out_stream *s)
return data_blocks;
}
-static unsigned int calculate_syt(struct amdtp_out_stream *s,
+static unsigned int calculate_syt(struct amdtp_stream *s,
unsigned int cycle)
{
unsigned int syt_offset, phase, index, syt;
@@ -280,175 +371,228 @@ static unsigned int calculate_syt(struct amdtp_out_stream *s,
syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12;
syt += syt_offset % TICKS_PER_CYCLE;
- return syt & 0xffff;
+ return syt & CIP_SYT_MASK;
} else {
- return 0xffff; /* no info */
+ return CIP_SYT_NO_INFO;
}
}
-static void amdtp_write_s32(struct amdtp_out_stream *s,
+static void amdtp_write_s32(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames)
{
struct snd_pcm_runtime *runtime = pcm->runtime;
- unsigned int channels, remaining_frames, frame_step, i, c;
+ unsigned int channels, remaining_frames, i, c;
const u32 *src;
channels = s->pcm_channels;
src = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
- frame_step = s->data_block_quadlets - channels;
for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
- *buffer = cpu_to_be32((*src >> 8) | 0x40000000);
+ buffer[s->pcm_positions[c]] =
+ cpu_to_be32((*src >> 8) | 0x40000000);
src++;
- buffer++;
}
- buffer += frame_step;
+ buffer += s->data_block_quadlets;
if (--remaining_frames == 0)
src = (void *)runtime->dma_area;
}
}
-static void amdtp_write_s16(struct amdtp_out_stream *s,
+static void amdtp_write_s16(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames)
{
struct snd_pcm_runtime *runtime = pcm->runtime;
- unsigned int channels, remaining_frames, frame_step, i, c;
+ unsigned int channels, remaining_frames, i, c;
const u16 *src;
channels = s->pcm_channels;
src = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
- frame_step = s->data_block_quadlets - channels;
for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
- *buffer = cpu_to_be32((*src << 8) | 0x40000000);
+ buffer[s->pcm_positions[c]] =
+ cpu_to_be32((*src << 8) | 0x40000000);
src++;
- buffer++;
}
- buffer += frame_step;
+ buffer += s->data_block_quadlets;
if (--remaining_frames == 0)
src = (void *)runtime->dma_area;
}
}
-static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames)
+static void amdtp_read_s32(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __be32 *buffer, unsigned int frames)
{
struct snd_pcm_runtime *runtime = pcm->runtime;
- unsigned int channels, frame_adjust_1, frame_adjust_2, i, c;
- const u32 *src;
+ unsigned int channels, remaining_frames, i, c;
+ u32 *dst;
channels = s->pcm_channels;
- src = (void *)runtime->dma_area +
- s->pcm_buffer_pointer * (runtime->frame_bits / 8);
- frame_adjust_1 = channels - 1;
- frame_adjust_2 = 1 - (s->data_block_quadlets - channels);
+ dst = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
- channels /= 2;
for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
- *buffer = cpu_to_be32((*src >> 8) | 0x40000000);
- src++;
- buffer += 2;
- }
- buffer -= frame_adjust_1;
- for (c = 0; c < channels; ++c) {
- *buffer = cpu_to_be32((*src >> 8) | 0x40000000);
- src++;
- buffer += 2;
+ *dst = be32_to_cpu(buffer[s->pcm_positions[c]]) << 8;
+ dst++;
}
- buffer -= frame_adjust_2;
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ dst = (void *)runtime->dma_area;
}
}
-static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s,
- struct snd_pcm_substream *pcm,
- __be32 *buffer, unsigned int frames)
+static void amdtp_fill_pcm_silence(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int frames)
{
- struct snd_pcm_runtime *runtime = pcm->runtime;
- unsigned int channels, frame_adjust_1, frame_adjust_2, i, c;
- const u16 *src;
-
- channels = s->pcm_channels;
- src = (void *)runtime->dma_area +
- s->pcm_buffer_pointer * (runtime->frame_bits / 8);
- frame_adjust_1 = channels - 1;
- frame_adjust_2 = 1 - (s->data_block_quadlets - channels);
+ unsigned int i, c;
- channels /= 2;
for (i = 0; i < frames; ++i) {
- for (c = 0; c < channels; ++c) {
- *buffer = cpu_to_be32((*src << 8) | 0x40000000);
- src++;
- buffer += 2;
- }
- buffer -= frame_adjust_1;
- for (c = 0; c < channels; ++c) {
- *buffer = cpu_to_be32((*src << 8) | 0x40000000);
- src++;
- buffer += 2;
- }
- buffer -= frame_adjust_2;
+ for (c = 0; c < s->pcm_channels; ++c)
+ buffer[s->pcm_positions[c]] = cpu_to_be32(0x40000000);
+ buffer += s->data_block_quadlets;
}
}
-static void amdtp_fill_pcm_silence(struct amdtp_out_stream *s,
- __be32 *buffer, unsigned int frames)
+static void amdtp_fill_midi(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int frames)
{
- unsigned int i, c;
+ unsigned int f, port;
+ u8 *b;
+
+ for (f = 0; f < frames; f++) {
+ buffer[s->midi_position] = 0;
+ b = (u8 *)&buffer[s->midi_position];
+
+ port = (s->data_block_counter + f) % 8;
+ if ((f >= s->rx_blocks_for_midi) ||
+ (s->midi[port] == NULL) ||
+ (snd_rawmidi_transmit(s->midi[port], b + 1, 1) <= 0))
+ b[0] = 0x80;
+ else
+ b[0] = 0x81;
- for (i = 0; i < frames; ++i) {
- for (c = 0; c < s->pcm_channels; ++c)
- buffer[c] = cpu_to_be32(0x40000000);
buffer += s->data_block_quadlets;
}
}
-static void amdtp_fill_midi(struct amdtp_out_stream *s,
+static void amdtp_pull_midi(struct amdtp_stream *s,
__be32 *buffer, unsigned int frames)
{
- unsigned int i;
+ unsigned int f, port;
+ int len;
+ u8 *b;
+
+ for (f = 0; f < frames; f++) {
+ port = (s->data_block_counter + f) % 8;
+ b = (u8 *)&buffer[s->midi_position];
+
+ len = b[0] - 0x80;
+ if ((1 <= len) && (len <= 3) && (s->midi[port]))
+ snd_rawmidi_receive(s->midi[port], b + 1, len);
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+static void update_pcm_pointers(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ unsigned int frames)
+{ unsigned int ptr;
+
+ ptr = s->pcm_buffer_pointer + frames;
+ if (ptr >= pcm->runtime->buffer_size)
+ ptr -= pcm->runtime->buffer_size;
+ ACCESS_ONCE(s->pcm_buffer_pointer) = ptr;
+
+ s->pcm_period_pointer += frames;
+ if (s->pcm_period_pointer >= pcm->runtime->period_size) {
+ s->pcm_period_pointer -= pcm->runtime->period_size;
+ s->pointer_flush = false;
+ tasklet_hi_schedule(&s->period_tasklet);
+ }
+}
+
+static void pcm_period_tasklet(unsigned long data)
+{
+ struct amdtp_stream *s = (void *)data;
+ struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
- for (i = 0; i < frames; ++i)
- buffer[s->pcm_channels + i * s->data_block_quadlets] =
- cpu_to_be32(0x80000000);
+ if (pcm)
+ snd_pcm_period_elapsed(pcm);
}
-static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
+static int queue_packet(struct amdtp_stream *s,
+ unsigned int header_length,
+ unsigned int payload_length, bool skip)
+{
+ struct fw_iso_packet p = {0};
+ int err = 0;
+
+ if (IS_ERR(s->context))
+ goto end;
+
+ p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL);
+ p.tag = TAG_CIP;
+ p.header_length = header_length;
+ p.payload_length = (!skip) ? payload_length : 0;
+ p.skip = skip;
+ err = fw_iso_context_queue(s->context, &p, &s->buffer.iso_buffer,
+ s->buffer.packets[s->packet_index].offset);
+ if (err < 0) {
+ dev_err(&s->unit->device, "queueing error: %d\n", err);
+ goto end;
+ }
+
+ if (++s->packet_index >= QUEUE_LENGTH)
+ s->packet_index = 0;
+end:
+ return err;
+}
+
+static inline int queue_out_packet(struct amdtp_stream *s,
+ unsigned int payload_length, bool skip)
+{
+ return queue_packet(s, OUT_PACKET_HEADER_SIZE,
+ payload_length, skip);
+}
+
+static inline int queue_in_packet(struct amdtp_stream *s)
+{
+ return queue_packet(s, IN_PACKET_HEADER_SIZE,
+ amdtp_stream_get_max_payload(s), false);
+}
+
+static void handle_out_packet(struct amdtp_stream *s, unsigned int syt)
{
__be32 *buffer;
- unsigned int index, data_blocks, syt, ptr;
+ unsigned int data_blocks, payload_length;
struct snd_pcm_substream *pcm;
- struct fw_iso_packet packet;
- int err;
if (s->packet_index < 0)
return;
- index = s->packet_index;
/* this module generate empty packet for 'no data' */
- syt = calculate_syt(s, cycle);
- if (!(s->flags & CIP_BLOCKING))
+ if (!(s->flags & CIP_BLOCKING) || (syt != CIP_SYT_NO_INFO))
data_blocks = calculate_data_blocks(s);
- else if (syt != 0xffff)
- data_blocks = s->syt_interval;
else
data_blocks = 0;
- buffer = s->buffer.packets[index].buffer;
+ buffer = s->buffer.packets[s->packet_index].buffer;
buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
- (s->data_block_quadlets << 16) |
+ (s->data_block_quadlets << AMDTP_DBS_SHIFT) |
s->data_block_counter);
buffer[1] = cpu_to_be32(CIP_EOH | CIP_FMT_AM | AMDTP_FDF_AM824 |
- (s->sfc << AMDTP_FDF_SFC_SHIFT) | syt);
+ (s->sfc << CIP_FDF_SFC_SHIFT) | syt);
buffer += 2;
pcm = ACCESS_ONCE(s->pcm);
@@ -461,58 +605,127 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
- packet.payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
- packet.interrupt = IS_ALIGNED(index + 1, INTERRUPT_INTERVAL);
- packet.skip = 0;
- packet.tag = TAG_CIP;
- packet.sy = 0;
- packet.header_length = 0;
-
- err = fw_iso_context_queue(s->context, &packet, &s->buffer.iso_buffer,
- s->buffer.packets[index].offset);
- if (err < 0) {
- dev_err(&s->unit->device, "queueing error: %d\n", err);
+ payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
+ if (queue_out_packet(s, payload_length, false) < 0) {
s->packet_index = -1;
- amdtp_out_stream_pcm_abort(s);
+ amdtp_stream_pcm_abort(s);
return;
}
- if (++index >= QUEUE_LENGTH)
- index = 0;
- s->packet_index = index;
+ if (pcm)
+ update_pcm_pointers(s, pcm, data_blocks);
+}
- if (pcm) {
- if (s->dual_wire)
- data_blocks *= 2;
-
- ptr = s->pcm_buffer_pointer + data_blocks;
- if (ptr >= pcm->runtime->buffer_size)
- ptr -= pcm->runtime->buffer_size;
- ACCESS_ONCE(s->pcm_buffer_pointer) = ptr;
-
- s->pcm_period_pointer += data_blocks;
- if (s->pcm_period_pointer >= pcm->runtime->period_size) {
- s->pcm_period_pointer -= pcm->runtime->period_size;
- s->pointer_flush = false;
- tasklet_hi_schedule(&s->period_tasklet);
+static void handle_in_packet(struct amdtp_stream *s,
+ unsigned int payload_quadlets,
+ __be32 *buffer)
+{
+ u32 cip_header[2];
+ unsigned int data_blocks, data_block_quadlets, data_block_counter,
+ dbc_interval;
+ struct snd_pcm_substream *pcm = NULL;
+ bool lost;
+
+ cip_header[0] = be32_to_cpu(buffer[0]);
+ cip_header[1] = be32_to_cpu(buffer[1]);
+
+ /*
+ * This module supports 'Two-quadlet CIP header with SYT field'.
+ * For convenience, also check FMT field is AM824 or not.
+ */
+ if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
+ ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH) ||
+ ((cip_header[1] & CIP_FMT_MASK) != CIP_FMT_AM)) {
+ dev_info_ratelimited(&s->unit->device,
+ "Invalid CIP header for AMDTP: %08X:%08X\n",
+ cip_header[0], cip_header[1]);
+ goto end;
+ }
+
+ /* Calculate data blocks */
+ if (payload_quadlets < 3 ||
+ ((cip_header[1] & CIP_FDF_MASK) ==
+ (AMDTP_FDF_NO_DATA << CIP_FDF_SFC_SHIFT))) {
+ data_blocks = 0;
+ } else {
+ data_block_quadlets =
+ (cip_header[0] & AMDTP_DBS_MASK) >> AMDTP_DBS_SHIFT;
+ /* avoid division by zero */
+ if (data_block_quadlets == 0) {
+ dev_info_ratelimited(&s->unit->device,
+ "Detect invalid value in dbs field: %08X\n",
+ cip_header[0]);
+ goto err;
}
+ if (s->flags & CIP_WRONG_DBS)
+ data_block_quadlets = s->data_block_quadlets;
+
+ data_blocks = (payload_quadlets - 2) / data_block_quadlets;
}
-}
-static void pcm_period_tasklet(unsigned long data)
-{
- struct amdtp_out_stream *s = (void *)data;
- struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
+ /* Check data block counter continuity */
+ data_block_counter = cip_header[0] & AMDTP_DBC_MASK;
+ if (data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
+ s->data_block_counter != UINT_MAX)
+ data_block_counter = s->data_block_counter;
+
+ if (((s->flags & CIP_SKIP_DBC_ZERO_CHECK) && data_block_counter == 0) ||
+ (s->data_block_counter == UINT_MAX)) {
+ lost = false;
+ } else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
+ lost = data_block_counter != s->data_block_counter;
+ } else {
+ if ((data_blocks > 0) && (s->tx_dbc_interval > 0))
+ dbc_interval = s->tx_dbc_interval;
+ else
+ dbc_interval = data_blocks;
+
+ lost = data_block_counter !=
+ ((s->data_block_counter + dbc_interval) & 0xff);
+ }
+
+ if (lost) {
+ dev_info(&s->unit->device,
+ "Detect discontinuity of CIP: %02X %02X\n",
+ s->data_block_counter, data_block_counter);
+ goto err;
+ }
+
+ if (data_blocks > 0) {
+ buffer += 2;
+
+ pcm = ACCESS_ONCE(s->pcm);
+ if (pcm)
+ s->transfer_samples(s, pcm, buffer, data_blocks);
+
+ if (s->midi_ports)
+ amdtp_pull_midi(s, buffer, data_blocks);
+ }
+
+ if (s->flags & CIP_DBC_IS_END_EVENT)
+ s->data_block_counter = data_block_counter;
+ else
+ s->data_block_counter =
+ (data_block_counter + data_blocks) & 0xff;
+end:
+ if (queue_in_packet(s) < 0)
+ goto err;
if (pcm)
- snd_pcm_period_elapsed(pcm);
+ update_pcm_pointers(s, pcm, data_blocks);
+
+ return;
+err:
+ s->packet_index = -1;
+ amdtp_stream_pcm_abort(s);
}
-static void out_packet_callback(struct fw_iso_context *context, u32 cycle,
- size_t header_length, void *header, void *data)
+static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
+ size_t header_length, void *header,
+ void *private_data)
{
- struct amdtp_out_stream *s = data;
- unsigned int i, packets = header_length / 4;
+ struct amdtp_stream *s = private_data;
+ unsigned int i, syt, packets = header_length / 4;
/*
* Compute the cycle of the last queued packet.
@@ -521,43 +734,102 @@ static void out_packet_callback(struct fw_iso_context *context, u32 cycle,
*/
cycle += QUEUE_LENGTH - packets;
- for (i = 0; i < packets; ++i)
- queue_out_packet(s, ++cycle);
+ for (i = 0; i < packets; ++i) {
+ syt = calculate_syt(s, ++cycle);
+ handle_out_packet(s, syt);
+ }
fw_iso_context_queue_flush(s->context);
}
-static int queue_initial_skip_packets(struct amdtp_out_stream *s)
+static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
+ size_t header_length, void *header,
+ void *private_data)
{
- struct fw_iso_packet skip_packet = {
- .skip = 1,
- };
- unsigned int i;
- int err;
+ struct amdtp_stream *s = private_data;
+ unsigned int p, syt, packets, payload_quadlets;
+ __be32 *buffer, *headers = header;
- for (i = 0; i < QUEUE_LENGTH; ++i) {
- skip_packet.interrupt = IS_ALIGNED(s->packet_index + 1,
- INTERRUPT_INTERVAL);
- err = fw_iso_context_queue(s->context, &skip_packet, NULL, 0);
- if (err < 0)
- return err;
- if (++s->packet_index >= QUEUE_LENGTH)
- s->packet_index = 0;
+ /* The number of packets in buffer */
+ packets = header_length / IN_PACKET_HEADER_SIZE;
+
+ for (p = 0; p < packets; p++) {
+ if (s->packet_index < 0)
+ break;
+
+ buffer = s->buffer.packets[s->packet_index].buffer;
+
+ /* Process sync slave stream */
+ if (s->sync_slave && s->sync_slave->callbacked) {
+ syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
+ handle_out_packet(s->sync_slave, syt);
+ }
+
+ /* The number of quadlets in this packet */
+ payload_quadlets =
+ (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4;
+ handle_in_packet(s, payload_quadlets, buffer);
}
- return 0;
+ /* Queueing error or detecting discontinuity */
+ if (s->packet_index < 0) {
+ /* Abort sync slave. */
+ if (s->sync_slave) {
+ s->sync_slave->packet_index = -1;
+ amdtp_stream_pcm_abort(s->sync_slave);
+ }
+ return;
+ }
+
+ /* when sync to device, flush the packets for slave stream */
+ if (s->sync_slave && s->sync_slave->callbacked)
+ fw_iso_context_queue_flush(s->sync_slave->context);
+
+ fw_iso_context_queue_flush(s->context);
+}
+
+/* processing is done by master callback */
+static void slave_stream_callback(struct fw_iso_context *context, u32 cycle,
+ size_t header_length, void *header,
+ void *private_data)
+{
+ return;
+}
+
+/* this is executed one time */
+static void amdtp_stream_first_callback(struct fw_iso_context *context,
+ u32 cycle, size_t header_length,
+ void *header, void *private_data)
+{
+ struct amdtp_stream *s = private_data;
+
+ /*
+ * For in-stream, first packet has come.
+ * For out-stream, prepared to transmit first packet
+ */
+ s->callbacked = true;
+ wake_up(&s->callback_wait);
+
+ if (s->direction == AMDTP_IN_STREAM)
+ context->callback.sc = in_stream_callback;
+ else if ((s->flags & CIP_BLOCKING) && (s->flags & CIP_SYNC_TO_DEVICE))
+ context->callback.sc = slave_stream_callback;
+ else
+ context->callback.sc = out_stream_callback;
+
+ context->callback.sc(context, cycle, header_length, header, s);
}
/**
- * amdtp_out_stream_start - start sending packets
- * @s: the AMDTP output stream to start
+ * amdtp_stream_start - start transferring packets
+ * @s: the AMDTP stream to start
* @channel: the isochronous channel on the bus
* @speed: firewire speed code
*
* The stream cannot be started until it has been configured with
- * amdtp_out_stream_set_parameters() and amdtp_out_stream_set_pcm_format(),
- * and it must be started before any PCM or MIDI device can be started.
+ * amdtp_stream_set_parameters() and it must be started before any PCM or MIDI
+ * device can be started.
*/
-int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed)
+int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
{
static const struct {
unsigned int data_block;
@@ -571,47 +843,72 @@ int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed)
[CIP_SFC_88200] = { 0, 67 },
[CIP_SFC_176400] = { 0, 67 },
};
- int err;
+ unsigned int header_size;
+ enum dma_data_direction dir;
+ int type, tag, err;
mutex_lock(&s->mutex);
- if (WARN_ON(amdtp_out_stream_running(s) ||
- (!s->pcm_channels && !s->midi_ports))) {
+ if (WARN_ON(amdtp_stream_running(s) ||
+ (s->data_block_quadlets < 1))) {
err = -EBADFD;
goto err_unlock;
}
+ if (s->direction == AMDTP_IN_STREAM &&
+ s->flags & CIP_SKIP_INIT_DBC_CHECK)
+ s->data_block_counter = UINT_MAX;
+ else
+ s->data_block_counter = 0;
s->data_block_state = initial_state[s->sfc].data_block;
s->syt_offset_state = initial_state[s->sfc].syt_offset;
s->last_syt_offset = TICKS_PER_CYCLE;
+ /* initialize packet buffer */
+ if (s->direction == AMDTP_IN_STREAM) {
+ dir = DMA_FROM_DEVICE;
+ type = FW_ISO_CONTEXT_RECEIVE;
+ header_size = IN_PACKET_HEADER_SIZE;
+ } else {
+ dir = DMA_TO_DEVICE;
+ type = FW_ISO_CONTEXT_TRANSMIT;
+ header_size = OUT_PACKET_HEADER_SIZE;
+ }
err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH,
- amdtp_out_stream_get_max_payload(s),
- DMA_TO_DEVICE);
+ amdtp_stream_get_max_payload(s), dir);
if (err < 0)
goto err_unlock;
s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
- FW_ISO_CONTEXT_TRANSMIT,
- channel, speed, 0,
- out_packet_callback, s);
+ type, channel, speed, header_size,
+ amdtp_stream_first_callback, s);
if (IS_ERR(s->context)) {
err = PTR_ERR(s->context);
if (err == -EBUSY)
dev_err(&s->unit->device,
- "no free output stream on this controller\n");
+ "no free stream on this controller\n");
goto err_buffer;
}
- amdtp_out_stream_update(s);
+ amdtp_stream_update(s);
s->packet_index = 0;
- s->data_block_counter = 0;
- err = queue_initial_skip_packets(s);
- if (err < 0)
- goto err_context;
+ do {
+ if (s->direction == AMDTP_IN_STREAM)
+ err = queue_in_packet(s);
+ else
+ err = queue_out_packet(s, 0, true);
+ if (err < 0)
+ goto err_context;
+ } while (s->packet_index > 0);
- err = fw_iso_context_start(s->context, -1, 0, 0);
+ /* NOTE: TAG1 matches CIP. This just affects in stream. */
+ tag = FW_ISO_CONTEXT_MATCH_TAG1;
+ if (s->flags & CIP_EMPTY_WITH_TAG0)
+ tag |= FW_ISO_CONTEXT_MATCH_TAG0;
+
+ s->callbacked = false;
+ err = fw_iso_context_start(s->context, -1, 0, tag);
if (err < 0)
goto err_context;
@@ -629,49 +926,49 @@ err_unlock:
return err;
}
-EXPORT_SYMBOL(amdtp_out_stream_start);
+EXPORT_SYMBOL(amdtp_stream_start);
/**
- * amdtp_out_stream_pcm_pointer - get the PCM buffer position
- * @s: the AMDTP output stream that transports the PCM data
+ * amdtp_stream_pcm_pointer - get the PCM buffer position
+ * @s: the AMDTP stream that transports the PCM data
*
* Returns the current buffer position, in frames.
*/
-unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s)
+unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s)
{
/* this optimization is allowed to be racy */
- if (s->pointer_flush)
+ if (s->pointer_flush && amdtp_stream_running(s))
fw_iso_context_flush_completions(s->context);
else
s->pointer_flush = true;
return ACCESS_ONCE(s->pcm_buffer_pointer);
}
-EXPORT_SYMBOL(amdtp_out_stream_pcm_pointer);
+EXPORT_SYMBOL(amdtp_stream_pcm_pointer);
/**
- * amdtp_out_stream_update - update the stream after a bus reset
- * @s: the AMDTP output stream
+ * amdtp_stream_update - update the stream after a bus reset
+ * @s: the AMDTP stream
*/
-void amdtp_out_stream_update(struct amdtp_out_stream *s)
+void amdtp_stream_update(struct amdtp_stream *s)
{
ACCESS_ONCE(s->source_node_id_field) =
(fw_parent_device(s->unit)->card->node_id & 0x3f) << 24;
}
-EXPORT_SYMBOL(amdtp_out_stream_update);
+EXPORT_SYMBOL(amdtp_stream_update);
/**
- * amdtp_out_stream_stop - stop sending packets
- * @s: the AMDTP output stream to stop
+ * amdtp_stream_stop - stop sending packets
+ * @s: the AMDTP stream to stop
*
* All PCM and MIDI devices of the stream must be stopped before the stream
* itself can be stopped.
*/
-void amdtp_out_stream_stop(struct amdtp_out_stream *s)
+void amdtp_stream_stop(struct amdtp_stream *s)
{
mutex_lock(&s->mutex);
- if (!amdtp_out_stream_running(s)) {
+ if (!amdtp_stream_running(s)) {
mutex_unlock(&s->mutex);
return;
}
@@ -682,18 +979,20 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s)
s->context = ERR_PTR(-1);
iso_packets_buffer_destroy(&s->buffer, s->unit);
+ s->callbacked = false;
+
mutex_unlock(&s->mutex);
}
-EXPORT_SYMBOL(amdtp_out_stream_stop);
+EXPORT_SYMBOL(amdtp_stream_stop);
/**
- * amdtp_out_stream_pcm_abort - abort the running PCM device
+ * amdtp_stream_pcm_abort - abort the running PCM device
* @s: the AMDTP stream about to be stopped
*
* If the isochronous stream needs to be stopped asynchronously, call this
* function first to stop the PCM device.
*/
-void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s)
+void amdtp_stream_pcm_abort(struct amdtp_stream *s)
{
struct snd_pcm_substream *pcm;
@@ -705,4 +1004,4 @@ void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s)
snd_pcm_stream_unlock_irq(pcm);
}
}
-EXPORT_SYMBOL(amdtp_out_stream_pcm_abort);
+EXPORT_SYMBOL(amdtp_stream_pcm_abort);
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
index 2746ecd291af..d8ee7b0e9386 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp.h
@@ -8,7 +8,7 @@
#include "packets-buffer.h"
/**
- * enum cip_out_flags - describes details of the streaming protocol
+ * enum cip_flags - describes details of the streaming protocol
* @CIP_NONBLOCKING: In non-blocking mode, each packet contains
* sample_rate/8000 samples, with rounding up or down to adjust
* for clock skew and left-over fractional samples. This should
@@ -16,15 +16,30 @@
* @CIP_BLOCKING: In blocking mode, each packet contains either zero or
* SYT_INTERVAL samples, with these two types alternating so that
* the overall sample rate comes out right.
- * @CIP_HI_DUALWIRE: At rates above 96 kHz, pretend that the stream runs
- * at half the actual sample rate with twice the number of channels;
- * two samples of a channel are stored consecutively in the packet.
- * Requires blocking mode and SYT_INTERVAL-aligned PCM buffer size.
+ * @CIP_SYNC_TO_DEVICE: In sync to device mode, time stamp in out packets is
+ * generated by in packets. Defaultly this driver generates timestamp.
+ * @CIP_EMPTY_WITH_TAG0: Only for in-stream. Empty in-packets have TAG0.
+ * @CIP_DBC_IS_END_EVENT: Only for in-stream. The value of dbc in an in-packet
+ * corresponds to the end of event in the packet. Out of IEC 61883.
+ * @CIP_WRONG_DBS: Only for in-stream. The value of dbs is wrong in in-packets.
+ * The value of data_block_quadlets is used instead of reported value.
+ * @SKIP_DBC_ZERO_CHECK: Only for in-stream. Packets with zero in dbc is
+ * skipped for detecting discontinuity.
+ * @CIP_SKIP_INIT_DBC_CHECK: Only for in-stream. The value of dbc in first
+ * packet is not continuous from an initial value.
+ * @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty
+ * packet is wrong but the others are correct.
*/
-enum cip_out_flags {
- CIP_NONBLOCKING = 0x00,
- CIP_BLOCKING = 0x01,
- CIP_HI_DUALWIRE = 0x02,
+enum cip_flags {
+ CIP_NONBLOCKING = 0x00,
+ CIP_BLOCKING = 0x01,
+ CIP_SYNC_TO_DEVICE = 0x02,
+ CIP_EMPTY_WITH_TAG0 = 0x04,
+ CIP_DBC_IS_END_EVENT = 0x08,
+ CIP_WRONG_DBS = 0x10,
+ CIP_SKIP_DBC_ZERO_CHECK = 0x20,
+ CIP_SKIP_INIT_DBC_CHECK = 0x40,
+ CIP_EMPTY_HAS_WRONG_DBC = 0x80,
};
/**
@@ -41,27 +56,55 @@ enum cip_sfc {
CIP_SFC_COUNT
};
+#define AMDTP_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32
+
#define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \
SNDRV_PCM_FMTBIT_S32)
+
+/*
+ * This module supports maximum 64 PCM channels for one PCM stream
+ * This is for our convenience.
+ */
+#define AMDTP_MAX_CHANNELS_FOR_PCM 64
+
+/*
+ * AMDTP packet can include channels for MIDI conformant data.
+ * Each MIDI conformant data channel includes 8 MPX-MIDI data stream.
+ * Each MPX-MIDI data stream includes one data stream from/to MIDI ports.
+ *
+ * This module supports maximum 1 MIDI conformant data channels.
+ * Then this AMDTP packets can transfer maximum 8 MIDI data streams.
+ */
+#define AMDTP_MAX_CHANNELS_FOR_MIDI 1
+
struct fw_unit;
struct fw_iso_context;
struct snd_pcm_substream;
+struct snd_pcm_runtime;
+struct snd_rawmidi_substream;
-struct amdtp_out_stream {
+enum amdtp_stream_direction {
+ AMDTP_OUT_STREAM = 0,
+ AMDTP_IN_STREAM
+};
+
+struct amdtp_stream {
struct fw_unit *unit;
- enum cip_out_flags flags;
+ enum cip_flags flags;
+ enum amdtp_stream_direction direction;
struct fw_iso_context *context;
struct mutex mutex;
enum cip_sfc sfc;
- bool dual_wire;
unsigned int data_block_quadlets;
unsigned int pcm_channels;
unsigned int midi_ports;
- void (*transfer_samples)(struct amdtp_out_stream *s,
+ void (*transfer_samples)(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__be32 *buffer, unsigned int frames);
+ u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM];
+ u8 midi_position;
unsigned int syt_interval;
unsigned int transfer_delay;
@@ -82,65 +125,148 @@ struct amdtp_out_stream {
unsigned int pcm_buffer_pointer;
unsigned int pcm_period_pointer;
bool pointer_flush;
+
+ struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8];
+
+ /* quirk: fixed interval of dbc between previos/current packets. */
+ unsigned int tx_dbc_interval;
+
+ /* quirk: the first count of data blocks in an rx packet for MIDI */
+ unsigned int rx_blocks_for_midi;
+
+ bool callbacked;
+ wait_queue_head_t callback_wait;
+ struct amdtp_stream *sync_slave;
};
-int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit,
- enum cip_out_flags flags);
-void amdtp_out_stream_destroy(struct amdtp_out_stream *s);
+int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir,
+ enum cip_flags flags);
+void amdtp_stream_destroy(struct amdtp_stream *s);
-void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s,
- unsigned int rate,
- unsigned int pcm_channels,
- unsigned int midi_ports);
-unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s);
+void amdtp_stream_set_parameters(struct amdtp_stream *s,
+ unsigned int rate,
+ unsigned int pcm_channels,
+ unsigned int midi_ports);
+unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s);
-int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed);
-void amdtp_out_stream_update(struct amdtp_out_stream *s);
-void amdtp_out_stream_stop(struct amdtp_out_stream *s);
+int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed);
+void amdtp_stream_update(struct amdtp_stream *s);
+void amdtp_stream_stop(struct amdtp_stream *s);
-void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
- snd_pcm_format_t format);
-void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s);
-unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s);
-void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s);
+int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime);
+void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
+ snd_pcm_format_t format);
+void amdtp_stream_pcm_prepare(struct amdtp_stream *s);
+unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s);
+void amdtp_stream_pcm_abort(struct amdtp_stream *s);
extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT];
+extern const unsigned int amdtp_rate_table[CIP_SFC_COUNT];
-static inline bool amdtp_out_stream_running(struct amdtp_out_stream *s)
+/**
+ * amdtp_stream_running - check stream is running or not
+ * @s: the AMDTP stream
+ *
+ * If this function returns true, the stream is running.
+ */
+static inline bool amdtp_stream_running(struct amdtp_stream *s)
{
return !IS_ERR(s->context);
}
/**
- * amdtp_out_streaming_error - check for streaming error
- * @s: the AMDTP output stream
+ * amdtp_streaming_error - check for streaming error
+ * @s: the AMDTP stream
*
* If this function returns true, the stream's packet queue has stopped due to
* an asynchronous error.
*/
-static inline bool amdtp_out_streaming_error(struct amdtp_out_stream *s)
+static inline bool amdtp_streaming_error(struct amdtp_stream *s)
{
return s->packet_index < 0;
}
/**
- * amdtp_out_stream_pcm_trigger - start/stop playback from a PCM device
- * @s: the AMDTP output stream
+ * amdtp_stream_pcm_running - check PCM substream is running or not
+ * @s: the AMDTP stream
+ *
+ * If this function returns true, PCM substream in the AMDTP stream is running.
+ */
+static inline bool amdtp_stream_pcm_running(struct amdtp_stream *s)
+{
+ return !!s->pcm;
+}
+
+/**
+ * amdtp_stream_pcm_trigger - start/stop playback from a PCM device
+ * @s: the AMDTP stream
* @pcm: the PCM device to be started, or %NULL to stop the current device
*
* Call this function on a running isochronous stream to enable the actual
* transmission of PCM data. This function should be called from the PCM
* device's .trigger callback.
*/
-static inline void amdtp_out_stream_pcm_trigger(struct amdtp_out_stream *s,
- struct snd_pcm_substream *pcm)
+static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm)
{
ACCESS_ONCE(s->pcm) = pcm;
}
+/**
+ * amdtp_stream_midi_trigger - start/stop playback/capture with a MIDI device
+ * @s: the AMDTP stream
+ * @port: index of MIDI port
+ * @midi: the MIDI device to be started, or %NULL to stop the current device
+ *
+ * Call this function on a running isochronous stream to enable the actual
+ * transmission of MIDI data. This function should be called from the MIDI
+ * device's .trigger callback.
+ */
+static inline void amdtp_stream_midi_trigger(struct amdtp_stream *s,
+ unsigned int port,
+ struct snd_rawmidi_substream *midi)
+{
+ if (port < s->midi_ports)
+ ACCESS_ONCE(s->midi[port]) = midi;
+}
+
static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
{
return sfc & 1;
}
+static inline void amdtp_stream_set_sync(enum cip_flags sync_mode,
+ struct amdtp_stream *master,
+ struct amdtp_stream *slave)
+{
+ if (sync_mode == CIP_SYNC_TO_DEVICE) {
+ master->flags |= CIP_SYNC_TO_DEVICE;
+ slave->flags |= CIP_SYNC_TO_DEVICE;
+ master->sync_slave = slave;
+ } else {
+ master->flags &= ~CIP_SYNC_TO_DEVICE;
+ slave->flags &= ~CIP_SYNC_TO_DEVICE;
+ master->sync_slave = NULL;
+ }
+
+ slave->sync_slave = NULL;
+}
+
+/**
+ * amdtp_stream_wait_callback - sleep till callbacked or timeout
+ * @s: the AMDTP stream
+ * @timeout: msec till timeout
+ *
+ * If this function return false, the AMDTP stream should be stopped.
+ */
+static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s,
+ unsigned int timeout)
+{
+ return wait_event_timeout(s->callback_wait,
+ s->callbacked == true,
+ msecs_to_jiffies(timeout)) > 0;
+}
+
#endif
diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile
new file mode 100644
index 000000000000..6cf470c80d1f
--- /dev/null
+++ b/sound/firewire/bebob/Makefile
@@ -0,0 +1,4 @@
+snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \
+ bebob_pcm.o bebob_hwdep.o bebob_terratec.o bebob_yamaha.o \
+ bebob_focusrite.o bebob_maudio.o bebob.o
+obj-m += snd-bebob.o
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
new file mode 100644
index 000000000000..fc19c99654aa
--- /dev/null
+++ b/sound/firewire/bebob/bebob.c
@@ -0,0 +1,471 @@
+/*
+ * bebob.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * BeBoB is 'BridgeCo enhanced Breakout Box'. This is installed to firewire
+ * devices with DM1000/DM1100/DM1500 chipset. It gives common way for host
+ * system to handle BeBoB based devices.
+ */
+
+#include "bebob.h"
+
+MODULE_DESCRIPTION("BridgeCo BeBoB driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "card index");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "enable BeBoB sound card");
+
+static DEFINE_MUTEX(devices_mutex);
+static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
+
+/* Offsets from information register. */
+#define INFO_OFFSET_GUID 0x10
+#define INFO_OFFSET_HW_MODEL_ID 0x18
+#define INFO_OFFSET_HW_MODEL_REVISION 0x1c
+
+#define VEN_EDIROL 0x000040ab
+#define VEN_PRESONUS 0x00000a92
+#define VEN_BRIDGECO 0x000007f5
+#define VEN_MACKIE 0x0000000f
+#define VEN_STANTON 0x00001260
+#define VEN_TASCAM 0x0000022e
+#define VEN_BEHRINGER 0x00001564
+#define VEN_APOGEE 0x000003db
+#define VEN_ESI 0x00000f1b
+#define VEN_ACOUSTIC 0x00000002
+#define VEN_CME 0x0000000a
+#define VEN_PHONIC 0x00001496
+#define VEN_LYNX 0x000019e5
+#define VEN_ICON 0x00001a9e
+#define VEN_PRISMSOUND 0x00001198
+#define VEN_TERRATEC 0x00000aac
+#define VEN_YAMAHA 0x0000a0de
+#define VEN_FOCUSRITE 0x0000130e
+#define VEN_MAUDIO1 0x00000d6c
+#define VEN_MAUDIO2 0x000007f5
+
+#define MODEL_FOCUSRITE_SAFFIRE_BOTH 0x00000000
+#define MODEL_MAUDIO_AUDIOPHILE_BOTH 0x00010060
+#define MODEL_MAUDIO_FW1814 0x00010071
+#define MODEL_MAUDIO_PROJECTMIX 0x00010091
+
+static int
+name_device(struct snd_bebob *bebob, unsigned int vendor_id)
+{
+ struct fw_device *fw_dev = fw_parent_device(bebob->unit);
+ char vendor[24] = {0};
+ char model[32] = {0};
+ u32 hw_id;
+ u32 data[2] = {0};
+ u32 revision;
+ int err;
+
+ /* get vendor name from root directory */
+ err = fw_csr_string(fw_dev->config_rom + 5, CSR_VENDOR,
+ vendor, sizeof(vendor));
+ if (err < 0)
+ goto end;
+
+ /* get model name from unit directory */
+ err = fw_csr_string(bebob->unit->directory, CSR_MODEL,
+ model, sizeof(model));
+ if (err < 0)
+ goto end;
+
+ /* get hardware id */
+ err = snd_bebob_read_quad(bebob->unit, INFO_OFFSET_HW_MODEL_ID,
+ &hw_id);
+ if (err < 0)
+ goto end;
+
+ /* get hardware revision */
+ err = snd_bebob_read_quad(bebob->unit, INFO_OFFSET_HW_MODEL_REVISION,
+ &revision);
+ if (err < 0)
+ goto end;
+
+ /* get GUID */
+ err = snd_bebob_read_block(bebob->unit, INFO_OFFSET_GUID,
+ data, sizeof(data));
+ if (err < 0)
+ goto end;
+
+ strcpy(bebob->card->driver, "BeBoB");
+ strcpy(bebob->card->shortname, model);
+ strcpy(bebob->card->mixername, model);
+ snprintf(bebob->card->longname, sizeof(bebob->card->longname),
+ "%s %s (id:%d, rev:%d), GUID %08x%08x at %s, S%d",
+ vendor, model, hw_id, revision,
+ data[0], data[1], dev_name(&bebob->unit->device),
+ 100 << fw_dev->max_speed);
+end:
+ return err;
+}
+
+static void
+bebob_card_free(struct snd_card *card)
+{
+ struct snd_bebob *bebob = card->private_data;
+
+ if (bebob->card_index >= 0) {
+ mutex_lock(&devices_mutex);
+ clear_bit(bebob->card_index, devices_used);
+ mutex_unlock(&devices_mutex);
+ }
+
+ mutex_destroy(&bebob->mutex);
+}
+
+static const struct snd_bebob_spec *
+get_saffire_spec(struct fw_unit *unit)
+{
+ char name[24] = {0};
+
+ if (fw_csr_string(unit->directory, CSR_MODEL, name, sizeof(name)) < 0)
+ return NULL;
+
+ if (strcmp(name, "SaffireLE") == 0)
+ return &saffire_le_spec;
+ else
+ return &saffire_spec;
+}
+
+static bool
+check_audiophile_booted(struct fw_unit *unit)
+{
+ char name[24] = {0};
+
+ if (fw_csr_string(unit->directory, CSR_MODEL, name, sizeof(name)) < 0)
+ return false;
+
+ return strncmp(name, "FW Audiophile Bootloader", 15) != 0;
+}
+
+static int
+bebob_probe(struct fw_unit *unit,
+ const struct ieee1394_device_id *entry)
+{
+ struct snd_card *card;
+ struct snd_bebob *bebob;
+ const struct snd_bebob_spec *spec;
+ unsigned int card_index;
+ int err;
+
+ mutex_lock(&devices_mutex);
+
+ for (card_index = 0; card_index < SNDRV_CARDS; card_index++) {
+ if (!test_bit(card_index, devices_used) && enable[card_index])
+ break;
+ }
+ if (card_index >= SNDRV_CARDS) {
+ err = -ENOENT;
+ goto end;
+ }
+
+ if ((entry->vendor_id == VEN_FOCUSRITE) &&
+ (entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH))
+ spec = get_saffire_spec(unit);
+ else if ((entry->vendor_id == VEN_MAUDIO1) &&
+ (entry->model_id == MODEL_MAUDIO_AUDIOPHILE_BOTH) &&
+ !check_audiophile_booted(unit))
+ spec = NULL;
+ else
+ spec = (const struct snd_bebob_spec *)entry->driver_data;
+
+ if (spec == NULL) {
+ if ((entry->vendor_id == VEN_MAUDIO1) ||
+ (entry->vendor_id == VEN_MAUDIO2))
+ err = snd_bebob_maudio_load_firmware(unit);
+ else
+ err = -ENOSYS;
+ goto end;
+ }
+
+ err = snd_card_new(&unit->device, index[card_index], id[card_index],
+ THIS_MODULE, sizeof(struct snd_bebob), &card);
+ if (err < 0)
+ goto end;
+ bebob = card->private_data;
+ bebob->card_index = card_index;
+ set_bit(card_index, devices_used);
+ card->private_free = bebob_card_free;
+
+ bebob->card = card;
+ bebob->unit = unit;
+ bebob->spec = spec;
+ mutex_init(&bebob->mutex);
+ spin_lock_init(&bebob->lock);
+ init_waitqueue_head(&bebob->hwdep_wait);
+
+ err = name_device(bebob, entry->vendor_id);
+ if (err < 0)
+ goto error;
+
+ if ((entry->vendor_id == VEN_MAUDIO1) &&
+ (entry->model_id == MODEL_MAUDIO_FW1814))
+ err = snd_bebob_maudio_special_discover(bebob, true);
+ else if ((entry->vendor_id == VEN_MAUDIO1) &&
+ (entry->model_id == MODEL_MAUDIO_PROJECTMIX))
+ err = snd_bebob_maudio_special_discover(bebob, false);
+ else
+ err = snd_bebob_stream_discover(bebob);
+ if (err < 0)
+ goto error;
+
+ snd_bebob_proc_init(bebob);
+
+ if ((bebob->midi_input_ports > 0) ||
+ (bebob->midi_output_ports > 0)) {
+ err = snd_bebob_create_midi_devices(bebob);
+ if (err < 0)
+ goto error;
+ }
+
+ err = snd_bebob_create_pcm_devices(bebob);
+ if (err < 0)
+ goto error;
+
+ err = snd_bebob_create_hwdep_device(bebob);
+ if (err < 0)
+ goto error;
+
+ err = snd_bebob_stream_init_duplex(bebob);
+ if (err < 0)
+ goto error;
+
+ if (!bebob->maudio_special_quirk) {
+ err = snd_card_register(card);
+ if (err < 0) {
+ snd_bebob_stream_destroy_duplex(bebob);
+ goto error;
+ }
+ } else {
+ /*
+ * This is a workaround. This bus reset seems to have an effect
+ * to make devices correctly handling transactions. Without
+ * this, the devices have gap_count mismatch. This causes much
+ * failure of transaction.
+ *
+ * Just after registration, user-land application receive
+ * signals from dbus and starts I/Os. To avoid I/Os till the
+ * future bus reset, registration is done in next update().
+ */
+ bebob->deferred_registration = true;
+ fw_schedule_bus_reset(fw_parent_device(bebob->unit)->card,
+ false, true);
+ }
+
+ dev_set_drvdata(&unit->device, bebob);
+end:
+ mutex_unlock(&devices_mutex);
+ return err;
+error:
+ mutex_unlock(&devices_mutex);
+ snd_card_free(card);
+ return err;
+}
+
+static void
+bebob_update(struct fw_unit *unit)
+{
+ struct snd_bebob *bebob = dev_get_drvdata(&unit->device);
+
+ if (bebob == NULL)
+ return;
+
+ fcp_bus_reset(bebob->unit);
+ snd_bebob_stream_update_duplex(bebob);
+
+ if (bebob->deferred_registration) {
+ if (snd_card_register(bebob->card) < 0) {
+ snd_bebob_stream_destroy_duplex(bebob);
+ snd_card_free(bebob->card);
+ }
+ bebob->deferred_registration = false;
+ }
+}
+
+static void bebob_remove(struct fw_unit *unit)
+{
+ struct snd_bebob *bebob = dev_get_drvdata(&unit->device);
+
+ if (bebob == NULL)
+ return;
+
+ kfree(bebob->maudio_special_quirk);
+
+ snd_bebob_stream_destroy_duplex(bebob);
+ snd_card_disconnect(bebob->card);
+ snd_card_free_when_closed(bebob->card);
+}
+
+static struct snd_bebob_rate_spec normal_rate_spec = {
+ .get = &snd_bebob_stream_get_rate,
+ .set = &snd_bebob_stream_set_rate
+};
+static const struct snd_bebob_spec spec_normal = {
+ .clock = NULL,
+ .rate = &normal_rate_spec,
+ .meter = NULL
+};
+
+static const struct ieee1394_device_id bebob_id_table[] = {
+ /* Edirol, FA-66 */
+ SND_BEBOB_DEV_ENTRY(VEN_EDIROL, 0x00010049, &spec_normal),
+ /* Edirol, FA-101 */
+ SND_BEBOB_DEV_ENTRY(VEN_EDIROL, 0x00010048, &spec_normal),
+ /* Presonus, FIREBOX */
+ SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010000, &spec_normal),
+ /* PreSonus, FIREPOD/FP10 */
+ SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010066, &spec_normal),
+ /* PreSonus, Inspire1394 */
+ SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010001, &spec_normal),
+ /* BridgeCo, RDAudio1 */
+ SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010048, &spec_normal),
+ /* BridgeCo, Audio5 */
+ SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010049, &spec_normal),
+ /* Mackie, Onyx 1220/1620/1640 (Firewire I/O Card) */
+ SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010065, &spec_normal),
+ /* Mackie, d.2 (Firewire Option) */
+ SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010067, &spec_normal),
+ /* Stanton, ScratchAmp */
+ SND_BEBOB_DEV_ENTRY(VEN_STANTON, 0x00000001, &spec_normal),
+ /* Tascam, IF-FW DM */
+ SND_BEBOB_DEV_ENTRY(VEN_TASCAM, 0x00010067, &spec_normal),
+ /* Behringer, XENIX UFX 1204 */
+ SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001204, &spec_normal),
+ /* Behringer, XENIX UFX 1604 */
+ SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001604, &spec_normal),
+ /* Behringer, Digital Mixer X32 series (X-UF Card) */
+ SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00000006, &spec_normal),
+ /* Apogee Electronics, Rosetta 200/400 (X-FireWire card) */
+ /* Apogee Electronics, DA/AD/DD-16X (X-FireWire card) */
+ SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00010048, &spec_normal),
+ /* Apogee Electronics, Ensemble */
+ SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00001eee, &spec_normal),
+ /* ESI, Quatafire610 */
+ SND_BEBOB_DEV_ENTRY(VEN_ESI, 0x00010064, &spec_normal),
+ /* AcousticReality, eARMasterOne */
+ SND_BEBOB_DEV_ENTRY(VEN_ACOUSTIC, 0x00000002, &spec_normal),
+ /* CME, MatrixKFW */
+ SND_BEBOB_DEV_ENTRY(VEN_CME, 0x00030000, &spec_normal),
+ /* Phonic, Helix Board 12 MkII */
+ SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00050000, &spec_normal),
+ /* Phonic, Helix Board 18 MkII */
+ SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00060000, &spec_normal),
+ /* Phonic, Helix Board 24 MkII */
+ SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00070000, &spec_normal),
+ /* Phonic, Helix Board 12 Universal/18 Universal/24 Universal */
+ SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00000000, &spec_normal),
+ /* Lynx, Aurora 8/16 (LT-FW) */
+ SND_BEBOB_DEV_ENTRY(VEN_LYNX, 0x00000001, &spec_normal),
+ /* ICON, FireXon */
+ SND_BEBOB_DEV_ENTRY(VEN_ICON, 0x00000001, &spec_normal),
+ /* PrismSound, Orpheus */
+ SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x00010048, &spec_normal),
+ /* PrismSound, ADA-8XR */
+ SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x0000ada8, &spec_normal),
+ /* TerraTec Electronic GmbH, PHASE 88 Rack FW */
+ SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000003, &phase88_rack_spec),
+ /* TerraTec Electronic GmbH, PHASE 24 FW */
+ SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000004, &phase24_series_spec),
+ /* TerraTec Electronic GmbH, Phase X24 FW */
+ SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000007, &phase24_series_spec),
+ /* TerraTec Electronic GmbH, EWS MIC2/MIC8 */
+ SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000005, &spec_normal),
+ /* Terratec Electronic GmbH, Aureon 7.1 Firewire */
+ SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000002, &spec_normal),
+ /* Yamaha, GO44 */
+ SND_BEBOB_DEV_ENTRY(VEN_YAMAHA, 0x0010000b, &yamaha_go_spec),
+ /* YAMAHA, GO46 */
+ SND_BEBOB_DEV_ENTRY(VEN_YAMAHA, 0x0010000c, &yamaha_go_spec),
+ /* Focusrite, SaffirePro 26 I/O */
+ SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, 0x00000003, &saffirepro_26_spec),
+ /* Focusrite, SaffirePro 10 I/O */
+ SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, 0x00000006, &saffirepro_10_spec),
+ /* Focusrite, Saffire(no label and LE) */
+ SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, MODEL_FOCUSRITE_SAFFIRE_BOTH,
+ &saffire_spec),
+ /* M-Audio, Firewire 410 */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO2, 0x00010058, NULL), /* bootloader */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO2, 0x00010046, &maudio_fw410_spec),
+ /* M-Audio, Firewire Audiophile */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, MODEL_MAUDIO_AUDIOPHILE_BOTH,
+ &maudio_audiophile_spec),
+ /* M-Audio, Firewire Solo */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x00010062, &maudio_solo_spec),
+ /* M-Audio, Ozonic */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x0000000a, &maudio_ozonic_spec),
+ /* M-Audio NRV10 */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x00010081, &maudio_nrv10_spec),
+ /* M-Audio, ProFireLightbridge */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x000100a1, &spec_normal),
+ /* Firewire 1814 */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x00010070, NULL), /* bootloader */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, MODEL_MAUDIO_FW1814,
+ &maudio_special_spec),
+ /* M-Audio ProjectMix */
+ SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, MODEL_MAUDIO_PROJECTMIX,
+ &maudio_special_spec),
+ /* IDs are unknown but able to be supported */
+ /* Apogee, Mini-ME Firewire */
+ /* Apogee, Mini-DAC Firewire */
+ /* Behringer, F-Control Audio 1616 */
+ /* Behringer, F-Control Audio 610 */
+ /* Cakawalk, Sonar Power Studio 66 */
+ /* CME, UF400e */
+ /* ESI, Quotafire XL */
+ /* Infrasonic, DewX */
+ /* Infrasonic, Windy6 */
+ /* Mackie, Digital X Bus x.200 */
+ /* Mackie, Digital X Bus x.400 */
+ /* Phonic, HB 12 */
+ /* Phonic, HB 24 */
+ /* Phonic, HB 18 */
+ /* Phonic, FireFly 202 */
+ /* Phonic, FireFly 302 */
+ /* Rolf Spuler, Firewire Guitar */
+ {}
+};
+MODULE_DEVICE_TABLE(ieee1394, bebob_id_table);
+
+static struct fw_driver bebob_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "snd-bebob",
+ .bus = &fw_bus_type,
+ },
+ .probe = bebob_probe,
+ .update = bebob_update,
+ .remove = bebob_remove,
+ .id_table = bebob_id_table,
+};
+
+static int __init
+snd_bebob_init(void)
+{
+ return driver_register(&bebob_driver.driver);
+}
+
+static void __exit
+snd_bebob_exit(void)
+{
+ driver_unregister(&bebob_driver.driver);
+}
+
+module_init(snd_bebob_init);
+module_exit(snd_bebob_exit);
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
new file mode 100644
index 000000000000..d1c93a1e0978
--- /dev/null
+++ b/sound/firewire/bebob/bebob.h
@@ -0,0 +1,257 @@
+/*
+ * bebob.h - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#ifndef SOUND_BEBOB_H_INCLUDED
+#define SOUND_BEBOB_H_INCLUDED
+
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/rawmidi.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+
+#include "../lib.h"
+#include "../fcp.h"
+#include "../packets-buffer.h"
+#include "../iso-resources.h"
+#include "../amdtp.h"
+#include "../cmp.h"
+
+/* basic register addresses on DM1000/DM1100/DM1500 */
+#define BEBOB_ADDR_REG_INFO 0xffffc8020000ULL
+#define BEBOB_ADDR_REG_REQ 0xffffc8021000ULL
+
+struct snd_bebob;
+
+#define SND_BEBOB_STRM_FMT_ENTRIES 7
+struct snd_bebob_stream_formation {
+ unsigned int pcm;
+ unsigned int midi;
+};
+/* this is a lookup table for index of stream formations */
+extern const unsigned int snd_bebob_rate_table[SND_BEBOB_STRM_FMT_ENTRIES];
+
+/* device specific operations */
+#define SND_BEBOB_CLOCK_INTERNAL "Internal"
+struct snd_bebob_clock_spec {
+ unsigned int num;
+ char *const *labels;
+ int (*get)(struct snd_bebob *bebob, unsigned int *id);
+};
+struct snd_bebob_rate_spec {
+ int (*get)(struct snd_bebob *bebob, unsigned int *rate);
+ int (*set)(struct snd_bebob *bebob, unsigned int rate);
+};
+struct snd_bebob_meter_spec {
+ unsigned int num;
+ char *const *labels;
+ int (*get)(struct snd_bebob *bebob, u32 *target, unsigned int size);
+};
+struct snd_bebob_spec {
+ struct snd_bebob_clock_spec *clock;
+ struct snd_bebob_rate_spec *rate;
+ struct snd_bebob_meter_spec *meter;
+};
+
+struct snd_bebob {
+ struct snd_card *card;
+ struct fw_unit *unit;
+ int card_index;
+
+ struct mutex mutex;
+ spinlock_t lock;
+
+ const struct snd_bebob_spec *spec;
+
+ unsigned int midi_input_ports;
+ unsigned int midi_output_ports;
+
+ /* for bus reset quirk */
+ struct completion bus_reset;
+ bool connected;
+
+ struct amdtp_stream *master;
+ struct amdtp_stream tx_stream;
+ struct amdtp_stream rx_stream;
+ struct cmp_connection out_conn;
+ struct cmp_connection in_conn;
+ atomic_t capture_substreams;
+ atomic_t playback_substreams;
+
+ struct snd_bebob_stream_formation
+ tx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES];
+ struct snd_bebob_stream_formation
+ rx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES];
+
+ int sync_input_plug;
+
+ /* for uapi */
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+
+ /* for M-Audio special devices */
+ void *maudio_special_quirk;
+ bool deferred_registration;
+};
+
+static inline int
+snd_bebob_read_block(struct fw_unit *unit, u64 addr, void *buf, int size)
+{
+ return snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST,
+ BEBOB_ADDR_REG_INFO + addr,
+ buf, size, 0);
+}
+
+static inline int
+snd_bebob_read_quad(struct fw_unit *unit, u64 addr, u32 *buf)
+{
+ return snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
+ BEBOB_ADDR_REG_INFO + addr,
+ (void *)buf, sizeof(u32), 0);
+}
+
+/* AV/C Audio Subunit Specification 1.0 (Oct 2000, 1394TA) */
+int avc_audio_set_selector(struct fw_unit *unit, unsigned int subunit_id,
+ unsigned int fb_id, unsigned int num);
+int avc_audio_get_selector(struct fw_unit *unit, unsigned int subunit_id,
+ unsigned int fb_id, unsigned int *num);
+
+/*
+ * AVC command extensions, AV/C Unit and Subunit, Revision 17
+ * (Nov 2003, BridgeCo)
+ */
+#define AVC_BRIDGECO_ADDR_BYTES 6
+enum avc_bridgeco_plug_dir {
+ AVC_BRIDGECO_PLUG_DIR_IN = 0x00,
+ AVC_BRIDGECO_PLUG_DIR_OUT = 0x01
+};
+enum avc_bridgeco_plug_mode {
+ AVC_BRIDGECO_PLUG_MODE_UNIT = 0x00,
+ AVC_BRIDGECO_PLUG_MODE_SUBUNIT = 0x01,
+ AVC_BRIDGECO_PLUG_MODE_FUNCTION_BLOCK = 0x02
+};
+enum avc_bridgeco_plug_unit {
+ AVC_BRIDGECO_PLUG_UNIT_ISOC = 0x00,
+ AVC_BRIDGECO_PLUG_UNIT_EXT = 0x01,
+ AVC_BRIDGECO_PLUG_UNIT_ASYNC = 0x02
+};
+enum avc_bridgeco_plug_type {
+ AVC_BRIDGECO_PLUG_TYPE_ISOC = 0x00,
+ AVC_BRIDGECO_PLUG_TYPE_ASYNC = 0x01,
+ AVC_BRIDGECO_PLUG_TYPE_MIDI = 0x02,
+ AVC_BRIDGECO_PLUG_TYPE_SYNC = 0x03,
+ AVC_BRIDGECO_PLUG_TYPE_ANA = 0x04,
+ AVC_BRIDGECO_PLUG_TYPE_DIG = 0x05
+};
+static inline void
+avc_bridgeco_fill_unit_addr(u8 buf[AVC_BRIDGECO_ADDR_BYTES],
+ enum avc_bridgeco_plug_dir dir,
+ enum avc_bridgeco_plug_unit unit,
+ unsigned int pid)
+{
+ buf[0] = 0xff; /* Unit */
+ buf[1] = dir;
+ buf[2] = AVC_BRIDGECO_PLUG_MODE_UNIT;
+ buf[3] = unit;
+ buf[4] = 0xff & pid;
+ buf[5] = 0xff; /* reserved */
+}
+static inline void
+avc_bridgeco_fill_msu_addr(u8 buf[AVC_BRIDGECO_ADDR_BYTES],
+ enum avc_bridgeco_plug_dir dir,
+ unsigned int pid)
+{
+ buf[0] = 0x60; /* Music subunit */
+ buf[1] = dir;
+ buf[2] = AVC_BRIDGECO_PLUG_MODE_SUBUNIT;
+ buf[3] = 0xff & pid;
+ buf[4] = 0xff; /* reserved */
+ buf[5] = 0xff; /* reserved */
+}
+int avc_bridgeco_get_plug_ch_pos(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ u8 *buf, unsigned int len);
+int avc_bridgeco_get_plug_type(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ enum avc_bridgeco_plug_type *type);
+int avc_bridgeco_get_plug_section_type(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ unsigned int id, u8 *type);
+int avc_bridgeco_get_plug_input(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ u8 input[7]);
+int avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 *buf,
+ unsigned int *len, unsigned int eid);
+
+/* for AMDTP streaming */
+int snd_bebob_stream_get_rate(struct snd_bebob *bebob, unsigned int *rate);
+int snd_bebob_stream_set_rate(struct snd_bebob *bebob, unsigned int rate);
+int snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob,
+ bool *internal);
+int snd_bebob_stream_discover(struct snd_bebob *bebob);
+int snd_bebob_stream_map(struct snd_bebob *bebob,
+ struct amdtp_stream *stream);
+int snd_bebob_stream_init_duplex(struct snd_bebob *bebob);
+int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate);
+void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob);
+void snd_bebob_stream_update_duplex(struct snd_bebob *bebob);
+void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob);
+
+void snd_bebob_stream_lock_changed(struct snd_bebob *bebob);
+int snd_bebob_stream_lock_try(struct snd_bebob *bebob);
+void snd_bebob_stream_lock_release(struct snd_bebob *bebob);
+
+void snd_bebob_proc_init(struct snd_bebob *bebob);
+
+int snd_bebob_create_midi_devices(struct snd_bebob *bebob);
+
+int snd_bebob_create_pcm_devices(struct snd_bebob *bebob);
+
+int snd_bebob_create_hwdep_device(struct snd_bebob *bebob);
+
+/* model specific operations */
+extern struct snd_bebob_spec phase88_rack_spec;
+extern struct snd_bebob_spec phase24_series_spec;
+extern struct snd_bebob_spec yamaha_go_spec;
+extern struct snd_bebob_spec saffirepro_26_spec;
+extern struct snd_bebob_spec saffirepro_10_spec;
+extern struct snd_bebob_spec saffire_le_spec;
+extern struct snd_bebob_spec saffire_spec;
+extern struct snd_bebob_spec maudio_fw410_spec;
+extern struct snd_bebob_spec maudio_audiophile_spec;
+extern struct snd_bebob_spec maudio_solo_spec;
+extern struct snd_bebob_spec maudio_ozonic_spec;
+extern struct snd_bebob_spec maudio_nrv10_spec;
+extern struct snd_bebob_spec maudio_special_spec;
+int snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814);
+int snd_bebob_maudio_load_firmware(struct fw_unit *unit);
+
+#define SND_BEBOB_DEV_ENTRY(vendor, model, data) \
+{ \
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | \
+ IEEE1394_MATCH_MODEL_ID, \
+ .vendor_id = vendor, \
+ .model_id = model, \
+ .driver_data = (kernel_ulong_t)data \
+}
+
+#endif
diff --git a/sound/firewire/bebob/bebob_command.c b/sound/firewire/bebob/bebob_command.c
new file mode 100644
index 000000000000..9402cc15dbc1
--- /dev/null
+++ b/sound/firewire/bebob/bebob_command.c
@@ -0,0 +1,282 @@
+/*
+ * bebob_command.c - driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./bebob.h"
+
+int avc_audio_set_selector(struct fw_unit *unit, unsigned int subunit_id,
+ unsigned int fb_id, unsigned int num)
+{
+ u8 *buf;
+ int err;
+
+ buf = kzalloc(12, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x00; /* AV/C CONTROL */
+ buf[1] = 0x08 | (0x07 & subunit_id); /* AUDIO SUBUNIT ID */
+ buf[2] = 0xb8; /* FUNCTION BLOCK */
+ buf[3] = 0x80; /* type is 'selector'*/
+ buf[4] = 0xff & fb_id; /* function block id */
+ buf[5] = 0x10; /* control attribute is CURRENT */
+ buf[6] = 0x02; /* selector length is 2 */
+ buf[7] = 0xff & num; /* input function block plug number */
+ buf[8] = 0x01; /* control selector is SELECTOR_CONTROL */
+
+ err = fcp_avc_transaction(unit, buf, 12, buf, 12,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7) | BIT(8));
+ if (err > 0 && err < 9)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (err > 0)
+ err = 0;
+
+ kfree(buf);
+ return err;
+}
+
+int avc_audio_get_selector(struct fw_unit *unit, unsigned int subunit_id,
+ unsigned int fb_id, unsigned int *num)
+{
+ u8 *buf;
+ int err;
+
+ buf = kzalloc(12, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x01; /* AV/C STATUS */
+ buf[1] = 0x08 | (0x07 & subunit_id); /* AUDIO SUBUNIT ID */
+ buf[2] = 0xb8; /* FUNCTION BLOCK */
+ buf[3] = 0x80; /* type is 'selector'*/
+ buf[4] = 0xff & fb_id; /* function block id */
+ buf[5] = 0x10; /* control attribute is CURRENT */
+ buf[6] = 0x02; /* selector length is 2 */
+ buf[7] = 0xff; /* input function block plug number */
+ buf[8] = 0x01; /* control selector is SELECTOR_CONTROL */
+
+ err = fcp_avc_transaction(unit, buf, 12, buf, 12,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(8));
+ if (err > 0 && err < 9)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ if (err < 0)
+ goto end;
+
+ *num = buf[7];
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+
+static inline void
+avc_bridgeco_fill_extension_addr(u8 *buf, u8 *addr)
+{
+ buf[1] = addr[0];
+ memcpy(buf + 4, addr + 1, 5);
+}
+
+static inline void
+avc_bridgeco_fill_plug_info_extension_command(u8 *buf, u8 *addr,
+ unsigned int itype)
+{
+ buf[0] = 0x01; /* AV/C STATUS */
+ buf[2] = 0x02; /* AV/C GENERAL PLUG INFO */
+ buf[3] = 0xc0; /* BridgeCo extension */
+ avc_bridgeco_fill_extension_addr(buf, addr);
+ buf[9] = itype; /* info type */
+}
+
+int avc_bridgeco_get_plug_type(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ enum avc_bridgeco_plug_type *type)
+{
+ u8 *buf;
+ int err;
+
+ buf = kzalloc(12, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ /* Info type is 'plug type'. */
+ avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x00);
+
+ err = fcp_avc_transaction(unit, buf, 12, buf, 12,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7) | BIT(9));
+ if ((err >= 0) && (err < 8))
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ if (err < 0)
+ goto end;
+
+ *type = buf[10];
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+
+int avc_bridgeco_get_plug_ch_pos(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ u8 *buf, unsigned int len)
+{
+ int err;
+
+ /* Info type is 'channel position'. */
+ avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x03);
+
+ err = fcp_avc_transaction(unit, buf, 12, buf, 256,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) |
+ BIT(5) | BIT(6) | BIT(7) | BIT(9));
+ if ((err >= 0) && (err < 8))
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ if (err < 0)
+ goto end;
+
+ /* Pick up specific data. */
+ memmove(buf, buf + 10, err - 10);
+ err = 0;
+end:
+ return err;
+}
+
+int avc_bridgeco_get_plug_section_type(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+ unsigned int id, u8 *type)
+{
+ u8 *buf;
+ int err;
+
+ /* section info includes charactors but this module don't need it */
+ buf = kzalloc(12, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ /* Info type is 'section info'. */
+ avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x07);
+ buf[10] = 0xff & ++id; /* section id */
+
+ err = fcp_avc_transaction(unit, buf, 12, buf, 12,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7) | BIT(9) | BIT(10));
+ if ((err >= 0) && (err < 8))
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ if (err < 0)
+ goto end;
+
+ *type = buf[11];
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+
+int avc_bridgeco_get_plug_input(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 input[7])
+{
+ int err;
+ u8 *buf;
+
+ buf = kzalloc(18, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ /* Info type is 'plug input'. */
+ avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x05);
+
+ err = fcp_avc_transaction(unit, buf, 16, buf, 16,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7));
+ if ((err >= 0) && (err < 8))
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ if (err < 0)
+ goto end;
+
+ memcpy(input, buf + 10, 5);
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+
+int avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit,
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 *buf,
+ unsigned int *len, unsigned int eid)
+{
+ int err;
+
+ /* check given buffer */
+ if ((buf == NULL) || (*len < 12)) {
+ err = -EINVAL;
+ goto end;
+ }
+
+ buf[0] = 0x01; /* AV/C STATUS */
+ buf[2] = 0x2f; /* AV/C STREAM FORMAT SUPPORT */
+ buf[3] = 0xc1; /* Bridgeco extension - List Request */
+ avc_bridgeco_fill_extension_addr(buf, addr);
+ buf[10] = 0xff & eid; /* Entry ID */
+
+ err = fcp_avc_transaction(unit, buf, 12, buf, *len,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+ BIT(6) | BIT(7) | BIT(10));
+ if ((err >= 0) && (err < 12))
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ else if (buf[10] != eid)
+ err = -EIO;
+ if (err < 0)
+ goto end;
+
+ /* Pick up 'stream format info'. */
+ memmove(buf, buf + 11, err - 11);
+ *len = err - 11;
+ err = 0;
+end:
+ return err;
+}
diff --git a/sound/firewire/bebob/bebob_focusrite.c b/sound/firewire/bebob/bebob_focusrite.c
new file mode 100644
index 000000000000..45a0eed6d5b1
--- /dev/null
+++ b/sound/firewire/bebob/bebob_focusrite.c
@@ -0,0 +1,279 @@
+/*
+ * bebob_focusrite.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./bebob.h"
+
+#define ANA_IN "Analog In"
+#define DIG_IN "Digital In"
+#define ANA_OUT "Analog Out"
+#define DIG_OUT "Digital Out"
+#define STM_IN "Stream In"
+
+#define SAFFIRE_ADDRESS_BASE 0x000100000000ULL
+
+#define SAFFIRE_OFFSET_CLOCK_SOURCE 0x00f8
+#define SAFFIREPRO_OFFSET_CLOCK_SOURCE 0x0174
+
+/* whether sync to external device or not */
+#define SAFFIRE_OFFSET_CLOCK_SYNC_EXT 0x013c
+#define SAFFIRE_LE_OFFSET_CLOCK_SYNC_EXT 0x0432
+#define SAFFIREPRO_OFFSET_CLOCK_SYNC_EXT 0x0164
+
+#define SAFFIRE_CLOCK_SOURCE_INTERNAL 0
+#define SAFFIRE_CLOCK_SOURCE_SPDIF 1
+
+/* '1' is absent, why... */
+#define SAFFIREPRO_CLOCK_SOURCE_INTERNAL 0
+#define SAFFIREPRO_CLOCK_SOURCE_SPDIF 2
+#define SAFFIREPRO_CLOCK_SOURCE_ADAT1 3
+#define SAFFIREPRO_CLOCK_SOURCE_ADAT2 4
+#define SAFFIREPRO_CLOCK_SOURCE_WORDCLOCK 5
+
+/* S/PDIF, ADAT1, ADAT2 is enabled or not. three quadlets */
+#define SAFFIREPRO_ENABLE_DIG_IFACES 0x01a4
+
+/* saffirepro has its own parameter for sampling frequency */
+#define SAFFIREPRO_RATE_NOREBOOT 0x01cc
+/* index is the value for this register */
+static const unsigned int rates[] = {
+ [0] = 0,
+ [1] = 44100,
+ [2] = 48000,
+ [3] = 88200,
+ [4] = 96000,
+ [5] = 176400,
+ [6] = 192000
+};
+
+/* saffire(no label)/saffire LE has metering */
+#define SAFFIRE_OFFSET_METER 0x0100
+#define SAFFIRE_LE_OFFSET_METER 0x0168
+
+static inline int
+saffire_read_block(struct snd_bebob *bebob, u64 offset,
+ u32 *buf, unsigned int size)
+{
+ unsigned int i;
+ int err;
+ __be32 *tmp = (__be32 *)buf;
+
+ err = snd_fw_transaction(bebob->unit, TCODE_READ_BLOCK_REQUEST,
+ SAFFIRE_ADDRESS_BASE + offset,
+ tmp, size, 0);
+ if (err < 0)
+ goto end;
+
+ for (i = 0; i < size / sizeof(u32); i++)
+ buf[i] = be32_to_cpu(tmp[i]);
+end:
+ return err;
+}
+
+static inline int
+saffire_read_quad(struct snd_bebob *bebob, u64 offset, u32 *value)
+{
+ int err;
+ __be32 tmp;
+
+ err = snd_fw_transaction(bebob->unit, TCODE_READ_QUADLET_REQUEST,
+ SAFFIRE_ADDRESS_BASE + offset,
+ &tmp, sizeof(__be32), 0);
+ if (err < 0)
+ goto end;
+
+ *value = be32_to_cpu(tmp);
+end:
+ return err;
+}
+
+static inline int
+saffire_write_quad(struct snd_bebob *bebob, u64 offset, u32 value)
+{
+ __be32 data = cpu_to_be32(value);
+
+ return snd_fw_transaction(bebob->unit, TCODE_WRITE_QUADLET_REQUEST,
+ SAFFIRE_ADDRESS_BASE + offset,
+ &data, sizeof(__be32), 0);
+}
+
+static char *const saffirepro_26_clk_src_labels[] = {
+ SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "ADAT1", "ADAT2", "Word Clock"
+};
+
+static char *const saffirepro_10_clk_src_labels[] = {
+ SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "Word Clock"
+};
+static int
+saffirepro_both_clk_freq_get(struct snd_bebob *bebob, unsigned int *rate)
+{
+ u32 id;
+ int err;
+
+ err = saffire_read_quad(bebob, SAFFIREPRO_RATE_NOREBOOT, &id);
+ if (err < 0)
+ goto end;
+ if (id >= ARRAY_SIZE(rates))
+ err = -EIO;
+ else
+ *rate = rates[id];
+end:
+ return err;
+}
+static int
+saffirepro_both_clk_freq_set(struct snd_bebob *bebob, unsigned int rate)
+{
+ u32 id;
+
+ for (id = 0; id < ARRAY_SIZE(rates); id++) {
+ if (rates[id] == rate)
+ break;
+ }
+ if (id == ARRAY_SIZE(rates))
+ return -EINVAL;
+
+ return saffire_write_quad(bebob, SAFFIREPRO_RATE_NOREBOOT, id);
+}
+static int
+saffirepro_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
+{
+ int err;
+ u32 value;
+
+ err = saffire_read_quad(bebob, SAFFIREPRO_OFFSET_CLOCK_SOURCE, &value);
+ if (err < 0)
+ goto end;
+
+ if (bebob->spec->clock->labels == saffirepro_10_clk_src_labels) {
+ if (value == SAFFIREPRO_CLOCK_SOURCE_WORDCLOCK)
+ *id = 2;
+ else if (value == SAFFIREPRO_CLOCK_SOURCE_SPDIF)
+ *id = 1;
+ } else if (value > 1) {
+ *id = value - 1;
+ }
+end:
+ return err;
+}
+
+struct snd_bebob_spec saffire_le_spec;
+static char *const saffire_both_clk_src_labels[] = {
+ SND_BEBOB_CLOCK_INTERNAL, "S/PDIF"
+};
+static int
+saffire_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
+{
+ int err;
+ u32 value;
+
+ err = saffire_read_quad(bebob, SAFFIRE_OFFSET_CLOCK_SOURCE, &value);
+ if (err >= 0)
+ *id = 0xff & value;
+
+ return err;
+};
+static char *const saffire_le_meter_labels[] = {
+ ANA_IN, ANA_IN, DIG_IN,
+ ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT,
+ STM_IN, STM_IN
+};
+static char *const saffire_meter_labels[] = {
+ ANA_IN, ANA_IN,
+ STM_IN, STM_IN, STM_IN, STM_IN, STM_IN,
+};
+static int
+saffire_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
+{
+ struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+ unsigned int channels;
+ u64 offset;
+ int err;
+
+ if (spec->labels == saffire_le_meter_labels)
+ offset = SAFFIRE_LE_OFFSET_METER;
+ else
+ offset = SAFFIRE_OFFSET_METER;
+
+ channels = spec->num * 2;
+ if (size < channels * sizeof(u32))
+ return -EIO;
+
+ err = saffire_read_block(bebob, offset, buf, size);
+ if (err >= 0 && spec->labels == saffire_le_meter_labels) {
+ swap(buf[1], buf[3]);
+ swap(buf[2], buf[3]);
+ swap(buf[3], buf[4]);
+
+ swap(buf[7], buf[10]);
+ swap(buf[8], buf[10]);
+ swap(buf[9], buf[11]);
+ swap(buf[11], buf[12]);
+
+ swap(buf[15], buf[16]);
+ }
+
+ return err;
+}
+
+static struct snd_bebob_rate_spec saffirepro_both_rate_spec = {
+ .get = &saffirepro_both_clk_freq_get,
+ .set = &saffirepro_both_clk_freq_set,
+};
+/* Saffire Pro 26 I/O */
+static struct snd_bebob_clock_spec saffirepro_26_clk_spec = {
+ .num = ARRAY_SIZE(saffirepro_26_clk_src_labels),
+ .labels = saffirepro_26_clk_src_labels,
+ .get = &saffirepro_both_clk_src_get,
+};
+struct snd_bebob_spec saffirepro_26_spec = {
+ .clock = &saffirepro_26_clk_spec,
+ .rate = &saffirepro_both_rate_spec,
+ .meter = NULL
+};
+/* Saffire Pro 10 I/O */
+static struct snd_bebob_clock_spec saffirepro_10_clk_spec = {
+ .num = ARRAY_SIZE(saffirepro_10_clk_src_labels),
+ .labels = saffirepro_10_clk_src_labels,
+ .get = &saffirepro_both_clk_src_get,
+};
+struct snd_bebob_spec saffirepro_10_spec = {
+ .clock = &saffirepro_10_clk_spec,
+ .rate = &saffirepro_both_rate_spec,
+ .meter = NULL
+};
+
+static struct snd_bebob_rate_spec saffire_both_rate_spec = {
+ .get = &snd_bebob_stream_get_rate,
+ .set = &snd_bebob_stream_set_rate,
+};
+static struct snd_bebob_clock_spec saffire_both_clk_spec = {
+ .num = ARRAY_SIZE(saffire_both_clk_src_labels),
+ .labels = saffire_both_clk_src_labels,
+ .get = &saffire_both_clk_src_get,
+};
+/* Saffire LE */
+static struct snd_bebob_meter_spec saffire_le_meter_spec = {
+ .num = ARRAY_SIZE(saffire_le_meter_labels),
+ .labels = saffire_le_meter_labels,
+ .get = &saffire_meter_get,
+};
+struct snd_bebob_spec saffire_le_spec = {
+ .clock = &saffire_both_clk_spec,
+ .rate = &saffire_both_rate_spec,
+ .meter = &saffire_le_meter_spec
+};
+/* Saffire */
+static struct snd_bebob_meter_spec saffire_meter_spec = {
+ .num = ARRAY_SIZE(saffire_meter_labels),
+ .labels = saffire_meter_labels,
+ .get = &saffire_meter_get,
+};
+struct snd_bebob_spec saffire_spec = {
+ .clock = &saffire_both_clk_spec,
+ .rate = &saffire_both_rate_spec,
+ .meter = &saffire_meter_spec
+};
diff --git a/sound/firewire/bebob/bebob_hwdep.c b/sound/firewire/bebob/bebob_hwdep.c
new file mode 100644
index 000000000000..ce731f4d8b4f
--- /dev/null
+++ b/sound/firewire/bebob/bebob_hwdep.c
@@ -0,0 +1,199 @@
+/*
+ * bebob_hwdep.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node infomation
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ */
+
+#include "bebob.h"
+
+static long
+hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_bebob *bebob = hwdep->private_data;
+ DEFINE_WAIT(wait);
+ union snd_firewire_event event;
+
+ spin_lock_irq(&bebob->lock);
+
+ while (!bebob->dev_lock_changed) {
+ prepare_to_wait(&bebob->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&bebob->lock);
+ schedule();
+ finish_wait(&bebob->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&bebob->lock);
+ }
+
+ memset(&event, 0, sizeof(event));
+ if (bebob->dev_lock_changed) {
+ event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+ event.lock_status.status = (bebob->dev_lock_count > 0);
+ bebob->dev_lock_changed = false;
+
+ count = min_t(long, count, sizeof(event.lock_status));
+ }
+
+ spin_unlock_irq(&bebob->lock);
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static unsigned int
+hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
+{
+ struct snd_bebob *bebob = hwdep->private_data;
+ unsigned int events;
+
+ poll_wait(file, &bebob->hwdep_wait, wait);
+
+ spin_lock_irq(&bebob->lock);
+ if (bebob->dev_lock_changed)
+ events = POLLIN | POLLRDNORM;
+ else
+ events = 0;
+ spin_unlock_irq(&bebob->lock);
+
+ return events;
+}
+
+static int
+hwdep_get_info(struct snd_bebob *bebob, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(bebob->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_BEBOB;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strlcpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int
+hwdep_lock(struct snd_bebob *bebob)
+{
+ int err;
+
+ spin_lock_irq(&bebob->lock);
+
+ if (bebob->dev_lock_count == 0) {
+ bebob->dev_lock_count = -1;
+ err = 0;
+ } else {
+ err = -EBUSY;
+ }
+
+ spin_unlock_irq(&bebob->lock);
+
+ return err;
+}
+
+static int
+hwdep_unlock(struct snd_bebob *bebob)
+{
+ int err;
+
+ spin_lock_irq(&bebob->lock);
+
+ if (bebob->dev_lock_count == -1) {
+ bebob->dev_lock_count = 0;
+ err = 0;
+ } else {
+ err = -EBADFD;
+ }
+
+ spin_unlock_irq(&bebob->lock);
+
+ return err;
+}
+
+static int
+hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_bebob *bebob = hwdep->private_data;
+
+ spin_lock_irq(&bebob->lock);
+ if (bebob->dev_lock_count == -1)
+ bebob->dev_lock_count = 0;
+ spin_unlock_irq(&bebob->lock);
+
+ return 0;
+}
+
+static int
+hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_bebob *bebob = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(bebob, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(bebob);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(bebob);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int
+hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+static const struct snd_hwdep_ops hwdep_ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+};
+
+int snd_bebob_create_hwdep_device(struct snd_bebob *bebob)
+{
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(bebob->card, "BeBoB", 0, &hwdep);
+ if (err < 0)
+ goto end;
+ strcpy(hwdep->name, "BeBoB");
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_BEBOB;
+ hwdep->ops = hwdep_ops;
+ hwdep->private_data = bebob;
+ hwdep->exclusive = true;
+end:
+ return err;
+}
+
diff --git a/sound/firewire/bebob/bebob_maudio.c b/sound/firewire/bebob/bebob_maudio.c
new file mode 100644
index 000000000000..6af50eb80ea7
--- /dev/null
+++ b/sound/firewire/bebob/bebob_maudio.c
@@ -0,0 +1,792 @@
+/*
+ * bebob_maudio.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./bebob.h"
+#include <sound/control.h>
+
+/*
+ * Just powering on, Firewire 410/Audiophile/1814 and ProjectMix I/O wait to
+ * download firmware blob. To enable these devices, drivers should upload
+ * firmware blob and send a command to initialize configuration to factory
+ * settings when completing uploading. Then these devices generate bus reset
+ * and are recognized as new devices with the firmware.
+ *
+ * But with firmware version 5058 or later, the firmware is stored to flash
+ * memory in the device and drivers can tell bootloader to load the firmware
+ * by sending a cue. This cue must be sent one time.
+ *
+ * For streaming, both of output and input streams are needed for Firewire 410
+ * and Ozonic. The single stream is OK for the other devices even if the clock
+ * source is not SYT-Match (I note no devices use SYT-Match).
+ *
+ * Without streaming, the devices except for Firewire Audiophile can mix any
+ * input and output. For this reason, Audiophile cannot be used as standalone
+ * mixer.
+ *
+ * Firewire 1814 and ProjectMix I/O uses special firmware. It will be freezed
+ * when receiving any commands which the firmware can't understand. These
+ * devices utilize completely different system to control. It is some
+ * write-transaction directly into a certain address. All of addresses for mixer
+ * functionality is between 0xffc700700000 to 0xffc70070009c.
+ */
+
+/* Offset from information register */
+#define INFO_OFFSET_SW_DATE 0x20
+
+/* Bootloader Protocol Version 1 */
+#define MAUDIO_BOOTLOADER_CUE1 0x00000001
+/*
+ * Initializing configuration to factory settings (= 0x1101), (swapped in line),
+ * Command code is zero (= 0x00),
+ * the number of operands is zero (= 0x00)(at least significant byte)
+ */
+#define MAUDIO_BOOTLOADER_CUE2 0x01110000
+/* padding */
+#define MAUDIO_BOOTLOADER_CUE3 0x00000000
+
+#define MAUDIO_SPECIFIC_ADDRESS 0xffc700000000ULL
+
+#define METER_OFFSET 0x00600000
+
+/* some device has sync info after metering data */
+#define METER_SIZE_SPECIAL 84 /* with sync info */
+#define METER_SIZE_FW410 76 /* with sync info */
+#define METER_SIZE_AUDIOPHILE 60 /* with sync info */
+#define METER_SIZE_SOLO 52 /* with sync info */
+#define METER_SIZE_OZONIC 48
+#define METER_SIZE_NRV10 80
+
+/* labels for metering */
+#define ANA_IN "Analog In"
+#define ANA_OUT "Analog Out"
+#define DIG_IN "Digital In"
+#define SPDIF_IN "S/PDIF In"
+#define ADAT_IN "ADAT In"
+#define DIG_OUT "Digital Out"
+#define SPDIF_OUT "S/PDIF Out"
+#define ADAT_OUT "ADAT Out"
+#define STRM_IN "Stream In"
+#define AUX_OUT "Aux Out"
+#define HP_OUT "HP Out"
+/* for NRV */
+#define UNKNOWN_METER "Unknown"
+
+struct special_params {
+ bool is1814;
+ unsigned int clk_src;
+ unsigned int dig_in_fmt;
+ unsigned int dig_out_fmt;
+ unsigned int clk_lock;
+ struct snd_ctl_elem_id *ctl_id_sync;
+};
+
+/*
+ * For some M-Audio devices, this module just send cue to load firmware. After
+ * loading, the device generates bus reset and newly detected.
+ *
+ * If we make any transactions to load firmware, the operation may failed.
+ */
+int snd_bebob_maudio_load_firmware(struct fw_unit *unit)
+{
+ struct fw_device *device = fw_parent_device(unit);
+ int err, rcode;
+ u64 date;
+ __be32 cues[3] = {
+ MAUDIO_BOOTLOADER_CUE1,
+ MAUDIO_BOOTLOADER_CUE2,
+ MAUDIO_BOOTLOADER_CUE3
+ };
+
+ /* check date of software used to build */
+ err = snd_bebob_read_block(unit, INFO_OFFSET_SW_DATE,
+ &date, sizeof(u64));
+ if (err < 0)
+ goto end;
+ /*
+ * firmware version 5058 or later has date later than "20070401", but
+ * 'date' is not null-terminated.
+ */
+ if (date < 0x3230303730343031LL) {
+ dev_err(&unit->device,
+ "Use firmware version 5058 or later\n");
+ err = -ENOSYS;
+ goto end;
+ }
+
+ rcode = fw_run_transaction(device->card, TCODE_WRITE_BLOCK_REQUEST,
+ device->node_id, device->generation,
+ device->max_speed, BEBOB_ADDR_REG_REQ,
+ cues, sizeof(cues));
+ if (rcode != RCODE_COMPLETE) {
+ dev_err(&unit->device,
+ "Failed to send a cue to load firmware\n");
+ err = -EIO;
+ }
+end:
+ return err;
+}
+
+static inline int
+get_meter(struct snd_bebob *bebob, void *buf, unsigned int size)
+{
+ return snd_fw_transaction(bebob->unit, TCODE_READ_BLOCK_REQUEST,
+ MAUDIO_SPECIFIC_ADDRESS + METER_OFFSET,
+ buf, size, 0);
+}
+
+static int
+check_clk_sync(struct snd_bebob *bebob, unsigned int size, bool *sync)
+{
+ int err;
+ u8 *buf;
+
+ buf = kmalloc(size, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ err = get_meter(bebob, buf, size);
+ if (err < 0)
+ goto end;
+
+ /* if synced, this value is the same as SFC of FDF in CIP header */
+ *sync = (buf[size - 2] != 0xff);
+end:
+ kfree(buf);
+ return err;
+}
+
+/*
+ * dig_fmt: 0x00:S/PDIF, 0x01:ADAT
+ * clk_lock: 0x00:unlock, 0x01:lock
+ */
+static int
+avc_maudio_set_special_clk(struct snd_bebob *bebob, unsigned int clk_src,
+ unsigned int dig_in_fmt, unsigned int dig_out_fmt,
+ unsigned int clk_lock)
+{
+ struct special_params *params = bebob->maudio_special_quirk;
+ int err;
+ u8 *buf;
+
+ if (amdtp_stream_running(&bebob->rx_stream) ||
+ amdtp_stream_running(&bebob->tx_stream))
+ return -EBUSY;
+
+ buf = kmalloc(12, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x00; /* CONTROL */
+ buf[1] = 0xff; /* UNIT */
+ buf[2] = 0x00; /* vendor dependent */
+ buf[3] = 0x04; /* company ID high */
+ buf[4] = 0x00; /* company ID middle */
+ buf[5] = 0x04; /* company ID low */
+ buf[6] = 0xff & clk_src; /* clock source */
+ buf[7] = 0xff & dig_in_fmt; /* input digital format */
+ buf[8] = 0xff & dig_out_fmt; /* output digital format */
+ buf[9] = 0xff & clk_lock; /* lock these settings */
+ buf[10] = 0x00; /* padding */
+ buf[11] = 0x00; /* padding */
+
+ err = fcp_avc_transaction(bebob->unit, buf, 12, buf, 12,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) |
+ BIT(5) | BIT(6) | BIT(7) | BIT(8) |
+ BIT(9));
+ if ((err > 0) && (err < 10))
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ if (err < 0)
+ goto end;
+
+ params->clk_src = buf[6];
+ params->dig_in_fmt = buf[7];
+ params->dig_out_fmt = buf[8];
+ params->clk_lock = buf[9];
+
+ if (params->ctl_id_sync)
+ snd_ctl_notify(bebob->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ params->ctl_id_sync);
+
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+static void
+special_stream_formation_set(struct snd_bebob *bebob)
+{
+ static const unsigned int ch_table[2][2][3] = {
+ /* AMDTP_OUT_STREAM */
+ { { 6, 6, 4 }, /* SPDIF */
+ { 12, 8, 4 } }, /* ADAT */
+ /* AMDTP_IN_STREAM */
+ { { 10, 10, 2 }, /* SPDIF */
+ { 16, 12, 2 } } /* ADAT */
+ };
+ struct special_params *params = bebob->maudio_special_quirk;
+ unsigned int i, max;
+
+ max = SND_BEBOB_STRM_FMT_ENTRIES - 1;
+ if (!params->is1814)
+ max -= 2;
+
+ for (i = 0; i < max; i++) {
+ bebob->tx_stream_formations[i + 1].pcm =
+ ch_table[AMDTP_IN_STREAM][params->dig_in_fmt][i / 2];
+ bebob->tx_stream_formations[i + 1].midi = 1;
+
+ bebob->rx_stream_formations[i + 1].pcm =
+ ch_table[AMDTP_OUT_STREAM][params->dig_out_fmt][i / 2];
+ bebob->rx_stream_formations[i + 1].midi = 1;
+ }
+}
+
+static int add_special_controls(struct snd_bebob *bebob);
+int
+snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814)
+{
+ struct special_params *params;
+ int err;
+
+ params = kzalloc(sizeof(struct special_params), GFP_KERNEL);
+ if (params == NULL)
+ return -ENOMEM;
+
+ mutex_lock(&bebob->mutex);
+
+ bebob->maudio_special_quirk = (void *)params;
+ params->is1814 = is1814;
+
+ /* initialize these parameters because driver is not allowed to ask */
+ bebob->rx_stream.context = ERR_PTR(-1);
+ bebob->tx_stream.context = ERR_PTR(-1);
+ err = avc_maudio_set_special_clk(bebob, 0x03, 0x00, 0x00, 0x00);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to initialize clock params: %d\n", err);
+ goto end;
+ }
+
+ err = add_special_controls(bebob);
+ if (err < 0)
+ goto end;
+
+ special_stream_formation_set(bebob);
+
+ if (params->is1814) {
+ bebob->midi_input_ports = 1;
+ bebob->midi_output_ports = 1;
+ } else {
+ bebob->midi_input_ports = 2;
+ bebob->midi_output_ports = 2;
+ }
+end:
+ if (err < 0) {
+ kfree(params);
+ bebob->maudio_special_quirk = NULL;
+ }
+ mutex_unlock(&bebob->mutex);
+ return err;
+}
+
+/* Input plug shows actual rate. Output plug is needless for this purpose. */
+static int special_get_rate(struct snd_bebob *bebob, unsigned int *rate)
+{
+ int err, trials;
+
+ trials = 0;
+ do {
+ err = avc_general_get_sig_fmt(bebob->unit, rate,
+ AVC_GENERAL_PLUG_DIR_IN, 0);
+ } while (err == -EAGAIN && ++trials < 3);
+
+ return err;
+}
+static int special_set_rate(struct snd_bebob *bebob, unsigned int rate)
+{
+ struct special_params *params = bebob->maudio_special_quirk;
+ int err;
+
+ err = avc_general_set_sig_fmt(bebob->unit, rate,
+ AVC_GENERAL_PLUG_DIR_OUT, 0);
+ if (err < 0)
+ goto end;
+
+ /*
+ * Just after changing sampling rate for output, a followed command
+ * for input is easy to fail. This is a workaround fot this issue.
+ */
+ msleep(100);
+
+ err = avc_general_set_sig_fmt(bebob->unit, rate,
+ AVC_GENERAL_PLUG_DIR_IN, 0);
+ if (err < 0)
+ goto end;
+
+ if (params->ctl_id_sync)
+ snd_ctl_notify(bebob->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ params->ctl_id_sync);
+end:
+ return err;
+}
+
+/* Clock source control for special firmware */
+static char *const special_clk_labels[] = {
+ SND_BEBOB_CLOCK_INTERNAL " with Digital Mute", "Digital",
+ "Word Clock", SND_BEBOB_CLOCK_INTERNAL};
+static int special_clk_get(struct snd_bebob *bebob, unsigned int *id)
+{
+ struct special_params *params = bebob->maudio_special_quirk;
+ *id = params->clk_src;
+ return 0;
+}
+static int special_clk_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *einf)
+{
+ einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ einf->count = 1;
+ einf->value.enumerated.items = ARRAY_SIZE(special_clk_labels);
+
+ if (einf->value.enumerated.item >= einf->value.enumerated.items)
+ einf->value.enumerated.item = einf->value.enumerated.items - 1;
+
+ strcpy(einf->value.enumerated.name,
+ special_clk_labels[einf->value.enumerated.item]);
+
+ return 0;
+}
+static int special_clk_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uval)
+{
+ struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+ struct special_params *params = bebob->maudio_special_quirk;
+ uval->value.enumerated.item[0] = params->clk_src;
+ return 0;
+}
+static int special_clk_ctl_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uval)
+{
+ struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+ struct special_params *params = bebob->maudio_special_quirk;
+ int err, id;
+
+ mutex_lock(&bebob->mutex);
+
+ id = uval->value.enumerated.item[0];
+ if (id >= ARRAY_SIZE(special_clk_labels))
+ return 0;
+
+ err = avc_maudio_set_special_clk(bebob, id,
+ params->dig_in_fmt,
+ params->dig_out_fmt,
+ params->clk_lock);
+ mutex_unlock(&bebob->mutex);
+
+ return err >= 0;
+}
+static struct snd_kcontrol_new special_clk_ctl = {
+ .name = "Clock Source",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = special_clk_ctl_info,
+ .get = special_clk_ctl_get,
+ .put = special_clk_ctl_put
+};
+
+/* Clock synchronization control for special firmware */
+static int special_sync_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *einf)
+{
+ einf->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ einf->count = 1;
+ einf->value.integer.min = 0;
+ einf->value.integer.max = 1;
+
+ return 0;
+}
+static int special_sync_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uval)
+{
+ struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+ int err;
+ bool synced = 0;
+
+ err = check_clk_sync(bebob, METER_SIZE_SPECIAL, &synced);
+ if (err >= 0)
+ uval->value.integer.value[0] = synced;
+
+ return 0;
+}
+static struct snd_kcontrol_new special_sync_ctl = {
+ .name = "Sync Status",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ,
+ .info = special_sync_ctl_info,
+ .get = special_sync_ctl_get,
+};
+
+/* Digital interface control for special firmware */
+static char *const special_dig_iface_labels[] = {
+ "S/PDIF Optical", "S/PDIF Coaxial", "ADAT Optical"
+};
+static int special_dig_in_iface_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *einf)
+{
+ einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ einf->count = 1;
+ einf->value.enumerated.items = ARRAY_SIZE(special_dig_iface_labels);
+
+ if (einf->value.enumerated.item >= einf->value.enumerated.items)
+ einf->value.enumerated.item = einf->value.enumerated.items - 1;
+
+ strcpy(einf->value.enumerated.name,
+ special_dig_iface_labels[einf->value.enumerated.item]);
+
+ return 0;
+}
+static int special_dig_in_iface_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uval)
+{
+ struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+ struct special_params *params = bebob->maudio_special_quirk;
+ unsigned int dig_in_iface;
+ int err, val;
+
+ mutex_lock(&bebob->mutex);
+
+ err = avc_audio_get_selector(bebob->unit, 0x00, 0x04,
+ &dig_in_iface);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get digital input interface: %d\n", err);
+ goto end;
+ }
+
+ /* encoded id for user value */
+ val = (params->dig_in_fmt << 1) | (dig_in_iface & 0x01);
+
+ /* for ADAT Optical */
+ if (val > 2)
+ val = 2;
+
+ uval->value.enumerated.item[0] = val;
+end:
+ mutex_unlock(&bebob->mutex);
+ return err;
+}
+static int special_dig_in_iface_ctl_set(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uval)
+{
+ struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+ struct special_params *params = bebob->maudio_special_quirk;
+ unsigned int id, dig_in_fmt, dig_in_iface;
+ int err;
+
+ mutex_lock(&bebob->mutex);
+
+ id = uval->value.enumerated.item[0];
+
+ /* decode user value */
+ dig_in_fmt = (id >> 1) & 0x01;
+ dig_in_iface = id & 0x01;
+
+ err = avc_maudio_set_special_clk(bebob,
+ params->clk_src,
+ dig_in_fmt,
+ params->dig_out_fmt,
+ params->clk_lock);
+ if ((err < 0) || (params->dig_in_fmt > 0)) /* ADAT */
+ goto end;
+
+ err = avc_audio_set_selector(bebob->unit, 0x00, 0x04, dig_in_iface);
+ if (err < 0)
+ dev_err(&bebob->unit->device,
+ "fail to set digital input interface: %d\n", err);
+end:
+ special_stream_formation_set(bebob);
+ mutex_unlock(&bebob->mutex);
+ return err;
+}
+static struct snd_kcontrol_new special_dig_in_iface_ctl = {
+ .name = "Digital Input Interface",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = special_dig_in_iface_ctl_info,
+ .get = special_dig_in_iface_ctl_get,
+ .put = special_dig_in_iface_ctl_set
+};
+
+static int special_dig_out_iface_ctl_info(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_info *einf)
+{
+ einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ einf->count = 1;
+ einf->value.enumerated.items = ARRAY_SIZE(special_dig_iface_labels) - 1;
+
+ if (einf->value.enumerated.item >= einf->value.enumerated.items)
+ einf->value.enumerated.item = einf->value.enumerated.items - 1;
+
+ strcpy(einf->value.enumerated.name,
+ special_dig_iface_labels[einf->value.enumerated.item + 1]);
+
+ return 0;
+}
+static int special_dig_out_iface_ctl_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uval)
+{
+ struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+ struct special_params *params = bebob->maudio_special_quirk;
+ mutex_lock(&bebob->mutex);
+ uval->value.enumerated.item[0] = params->dig_out_fmt;
+ mutex_unlock(&bebob->mutex);
+ return 0;
+}
+static int special_dig_out_iface_ctl_set(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uval)
+{
+ struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+ struct special_params *params = bebob->maudio_special_quirk;
+ unsigned int id;
+ int err;
+
+ mutex_lock(&bebob->mutex);
+
+ id = uval->value.enumerated.item[0];
+
+ err = avc_maudio_set_special_clk(bebob,
+ params->clk_src,
+ params->dig_in_fmt,
+ id, params->clk_lock);
+ if (err >= 0)
+ special_stream_formation_set(bebob);
+
+ mutex_unlock(&bebob->mutex);
+ return err;
+}
+static struct snd_kcontrol_new special_dig_out_iface_ctl = {
+ .name = "Digital Output Interface",
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .info = special_dig_out_iface_ctl_info,
+ .get = special_dig_out_iface_ctl_get,
+ .put = special_dig_out_iface_ctl_set
+};
+
+static int add_special_controls(struct snd_bebob *bebob)
+{
+ struct snd_kcontrol *kctl;
+ struct special_params *params = bebob->maudio_special_quirk;
+ int err;
+
+ kctl = snd_ctl_new1(&special_clk_ctl, bebob);
+ err = snd_ctl_add(bebob->card, kctl);
+ if (err < 0)
+ goto end;
+
+ kctl = snd_ctl_new1(&special_sync_ctl, bebob);
+ err = snd_ctl_add(bebob->card, kctl);
+ if (err < 0)
+ goto end;
+ params->ctl_id_sync = &kctl->id;
+
+ kctl = snd_ctl_new1(&special_dig_in_iface_ctl, bebob);
+ err = snd_ctl_add(bebob->card, kctl);
+ if (err < 0)
+ goto end;
+
+ kctl = snd_ctl_new1(&special_dig_out_iface_ctl, bebob);
+ err = snd_ctl_add(bebob->card, kctl);
+end:
+ return err;
+}
+
+/* Hardware metering for special firmware */
+static char *const special_meter_labels[] = {
+ ANA_IN, ANA_IN, ANA_IN, ANA_IN,
+ SPDIF_IN,
+ ADAT_IN, ADAT_IN, ADAT_IN, ADAT_IN,
+ ANA_OUT, ANA_OUT,
+ SPDIF_OUT,
+ ADAT_OUT, ADAT_OUT, ADAT_OUT, ADAT_OUT,
+ HP_OUT, HP_OUT,
+ AUX_OUT
+};
+static int
+special_meter_get(struct snd_bebob *bebob, u32 *target, unsigned int size)
+{
+ u16 *buf;
+ unsigned int i, c, channels;
+ int err;
+
+ channels = ARRAY_SIZE(special_meter_labels) * 2;
+ if (size < channels * sizeof(u32))
+ return -EINVAL;
+
+ /* omit last 4 bytes because it's clock info. */
+ buf = kmalloc(METER_SIZE_SPECIAL - 4, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ err = get_meter(bebob, (void *)buf, METER_SIZE_SPECIAL - 4);
+ if (err < 0)
+ goto end;
+
+ /* Its format is u16 and some channels are unknown. */
+ i = 0;
+ for (c = 2; c < channels + 2; c++)
+ target[i++] = be16_to_cpu(buf[c]) << 16;
+end:
+ kfree(buf);
+ return err;
+}
+
+/* last 4 bytes are omitted because it's clock info. */
+static char *const fw410_meter_labels[] = {
+ ANA_IN, DIG_IN,
+ ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT, DIG_OUT,
+ HP_OUT
+};
+static char *const audiophile_meter_labels[] = {
+ ANA_IN, DIG_IN,
+ ANA_OUT, ANA_OUT, DIG_OUT,
+ HP_OUT, AUX_OUT,
+};
+static char *const solo_meter_labels[] = {
+ ANA_IN, DIG_IN,
+ STRM_IN, STRM_IN,
+ ANA_OUT, DIG_OUT
+};
+
+/* no clock info */
+static char *const ozonic_meter_labels[] = {
+ ANA_IN, ANA_IN,
+ STRM_IN, STRM_IN,
+ ANA_OUT, ANA_OUT
+};
+/* TODO: need testers. these positions are based on authour's assumption */
+static char *const nrv10_meter_labels[] = {
+ ANA_IN, ANA_IN, ANA_IN, ANA_IN,
+ DIG_IN,
+ ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT,
+ DIG_IN
+};
+static int
+normal_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
+{
+ struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+ unsigned int c, channels;
+ int err;
+
+ channels = spec->num * 2;
+ if (size < channels * sizeof(u32))
+ return -EINVAL;
+
+ err = get_meter(bebob, (void *)buf, size);
+ if (err < 0)
+ goto end;
+
+ for (c = 0; c < channels; c++)
+ be32_to_cpus(&buf[c]);
+
+ /* swap stream channels because inverted */
+ if (spec->labels == solo_meter_labels) {
+ swap(buf[4], buf[6]);
+ swap(buf[5], buf[7]);
+ }
+end:
+ return err;
+}
+
+/* for special customized devices */
+static struct snd_bebob_rate_spec special_rate_spec = {
+ .get = &special_get_rate,
+ .set = &special_set_rate,
+};
+static struct snd_bebob_clock_spec special_clk_spec = {
+ .num = ARRAY_SIZE(special_clk_labels),
+ .labels = special_clk_labels,
+ .get = &special_clk_get,
+};
+static struct snd_bebob_meter_spec special_meter_spec = {
+ .num = ARRAY_SIZE(special_meter_labels),
+ .labels = special_meter_labels,
+ .get = &special_meter_get
+};
+struct snd_bebob_spec maudio_special_spec = {
+ .clock = &special_clk_spec,
+ .rate = &special_rate_spec,
+ .meter = &special_meter_spec
+};
+
+/* Firewire 410 specification */
+static struct snd_bebob_rate_spec usual_rate_spec = {
+ .get = &snd_bebob_stream_get_rate,
+ .set = &snd_bebob_stream_set_rate,
+};
+static struct snd_bebob_meter_spec fw410_meter_spec = {
+ .num = ARRAY_SIZE(fw410_meter_labels),
+ .labels = fw410_meter_labels,
+ .get = &normal_meter_get
+};
+struct snd_bebob_spec maudio_fw410_spec = {
+ .clock = NULL,
+ .rate = &usual_rate_spec,
+ .meter = &fw410_meter_spec
+};
+
+/* Firewire Audiophile specification */
+static struct snd_bebob_meter_spec audiophile_meter_spec = {
+ .num = ARRAY_SIZE(audiophile_meter_labels),
+ .labels = audiophile_meter_labels,
+ .get = &normal_meter_get
+};
+struct snd_bebob_spec maudio_audiophile_spec = {
+ .clock = NULL,
+ .rate = &usual_rate_spec,
+ .meter = &audiophile_meter_spec
+};
+
+/* Firewire Solo specification */
+static struct snd_bebob_meter_spec solo_meter_spec = {
+ .num = ARRAY_SIZE(solo_meter_labels),
+ .labels = solo_meter_labels,
+ .get = &normal_meter_get
+};
+struct snd_bebob_spec maudio_solo_spec = {
+ .clock = NULL,
+ .rate = &usual_rate_spec,
+ .meter = &solo_meter_spec
+};
+
+/* Ozonic specification */
+static struct snd_bebob_meter_spec ozonic_meter_spec = {
+ .num = ARRAY_SIZE(ozonic_meter_labels),
+ .labels = ozonic_meter_labels,
+ .get = &normal_meter_get
+};
+struct snd_bebob_spec maudio_ozonic_spec = {
+ .clock = NULL,
+ .rate = &usual_rate_spec,
+ .meter = &ozonic_meter_spec
+};
+
+/* NRV10 specification */
+static struct snd_bebob_meter_spec nrv10_meter_spec = {
+ .num = ARRAY_SIZE(nrv10_meter_labels),
+ .labels = nrv10_meter_labels,
+ .get = &normal_meter_get
+};
+struct snd_bebob_spec maudio_nrv10_spec = {
+ .clock = NULL,
+ .rate = &usual_rate_spec,
+ .meter = &nrv10_meter_spec
+};
diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c
new file mode 100644
index 000000000000..63343d578df3
--- /dev/null
+++ b/sound/firewire/bebob/bebob_midi.c
@@ -0,0 +1,168 @@
+/*
+ * bebob_midi.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "bebob.h"
+
+static int midi_capture_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_bebob *bebob = substream->rmidi->private_data;
+ int err;
+
+ err = snd_bebob_stream_lock_try(bebob);
+ if (err < 0)
+ goto end;
+
+ atomic_inc(&bebob->capture_substreams);
+ err = snd_bebob_stream_start_duplex(bebob, 0);
+ if (err < 0)
+ snd_bebob_stream_lock_release(bebob);
+end:
+ return err;
+}
+
+static int midi_playback_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_bebob *bebob = substream->rmidi->private_data;
+ int err;
+
+ err = snd_bebob_stream_lock_try(bebob);
+ if (err < 0)
+ goto end;
+
+ atomic_inc(&bebob->playback_substreams);
+ err = snd_bebob_stream_start_duplex(bebob, 0);
+ if (err < 0)
+ snd_bebob_stream_lock_release(bebob);
+end:
+ return err;
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_bebob *bebob = substream->rmidi->private_data;
+
+ atomic_dec(&bebob->capture_substreams);
+ snd_bebob_stream_stop_duplex(bebob);
+
+ snd_bebob_stream_lock_release(bebob);
+ return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_bebob *bebob = substream->rmidi->private_data;
+
+ atomic_dec(&bebob->playback_substreams);
+ snd_bebob_stream_stop_duplex(bebob);
+
+ snd_bebob_stream_lock_release(bebob);
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_bebob *bebob = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bebob->lock, flags);
+
+ if (up)
+ amdtp_stream_midi_trigger(&bebob->tx_stream,
+ substrm->number, substrm);
+ else
+ amdtp_stream_midi_trigger(&bebob->tx_stream,
+ substrm->number, NULL);
+
+ spin_unlock_irqrestore(&bebob->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_bebob *bebob = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bebob->lock, flags);
+
+ if (up)
+ amdtp_stream_midi_trigger(&bebob->rx_stream,
+ substrm->number, substrm);
+ else
+ amdtp_stream_midi_trigger(&bebob->rx_stream,
+ substrm->number, NULL);
+
+ spin_unlock_irqrestore(&bebob->lock, flags);
+}
+
+static struct snd_rawmidi_ops midi_capture_ops = {
+ .open = midi_capture_open,
+ .close = midi_capture_close,
+ .trigger = midi_capture_trigger,
+};
+
+static struct snd_rawmidi_ops midi_playback_ops = {
+ .open = midi_playback_open,
+ .close = midi_playback_close,
+ .trigger = midi_playback_trigger,
+};
+
+static void set_midi_substream_names(struct snd_bebob *bebob,
+ struct snd_rawmidi_str *str)
+{
+ struct snd_rawmidi_substream *subs;
+
+ list_for_each_entry(subs, &str->substreams, list) {
+ snprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ bebob->card->shortname, subs->number + 1);
+ }
+}
+
+int snd_bebob_create_midi_devices(struct snd_bebob *bebob)
+{
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_str *str;
+ int err;
+
+ /* create midi ports */
+ err = snd_rawmidi_new(bebob->card, bebob->card->driver, 0,
+ bebob->midi_output_ports, bebob->midi_input_ports,
+ &rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", bebob->card->shortname);
+ rmidi->private_data = bebob;
+
+ if (bebob->midi_input_ports > 0) {
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &midi_capture_ops);
+
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+ set_midi_substream_names(bebob, str);
+ }
+
+ if (bebob->midi_output_ports > 0) {
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &midi_playback_ops);
+
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+ set_midi_substream_names(bebob, str);
+ }
+
+ if ((bebob->midi_output_ports > 0) && (bebob->midi_input_ports > 0))
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ return 0;
+}
diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c
new file mode 100644
index 000000000000..4a55561ed4ec
--- /dev/null
+++ b/sound/firewire/bebob/bebob_pcm.c
@@ -0,0 +1,378 @@
+/*
+ * bebob_pcm.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./bebob.h"
+
+static int
+hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
+{
+ struct snd_bebob_stream_formation *formations = rule->private;
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ const struct snd_interval *c =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int i;
+
+ for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+ /* entry is invalid */
+ if (formations[i].pcm == 0)
+ continue;
+
+ if (!snd_interval_test(c, formations[i].pcm))
+ continue;
+
+ t.min = min(t.min, snd_bebob_rate_table[i]);
+ t.max = max(t.max, snd_bebob_rate_table[i]);
+
+ }
+ return snd_interval_refine(r, &t);
+}
+
+static int
+hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
+{
+ struct snd_bebob_stream_formation *formations = rule->private;
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+
+ unsigned int i;
+
+ for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+ /* entry is invalid */
+ if (formations[i].pcm == 0)
+ continue;
+
+ if (!snd_interval_test(r, snd_bebob_rate_table[i]))
+ continue;
+
+ t.min = min(t.min, formations[i].pcm);
+ t.max = max(t.max, formations[i].pcm);
+ }
+
+ return snd_interval_refine(c, &t);
+}
+
+static void
+limit_channels_and_rates(struct snd_pcm_hardware *hw,
+ struct snd_bebob_stream_formation *formations)
+{
+ unsigned int i;
+
+ hw->channels_min = UINT_MAX;
+ hw->channels_max = 0;
+
+ hw->rate_min = UINT_MAX;
+ hw->rate_max = 0;
+ hw->rates = 0;
+
+ for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+ /* entry has no PCM channels */
+ if (formations[i].pcm == 0)
+ continue;
+
+ hw->channels_min = min(hw->channels_min, formations[i].pcm);
+ hw->channels_max = max(hw->channels_max, formations[i].pcm);
+
+ hw->rate_min = min(hw->rate_min, snd_bebob_rate_table[i]);
+ hw->rate_max = max(hw->rate_max, snd_bebob_rate_table[i]);
+ hw->rates |= snd_pcm_rate_to_rate_bit(snd_bebob_rate_table[i]);
+ }
+}
+
+static void
+limit_period_and_buffer(struct snd_pcm_hardware *hw)
+{
+ hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */
+ hw->periods_max = UINT_MAX;
+
+ hw->period_bytes_min = 4 * hw->channels_max; /* bytes for a frame */
+
+ /* Just to prevent from allocating much pages. */
+ hw->period_bytes_max = hw->period_bytes_min * 2048;
+ hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
+}
+
+static int
+pcm_init_hw_params(struct snd_bebob *bebob,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct amdtp_stream *s;
+ struct snd_bebob_stream_formation *formations;
+ int err;
+
+ runtime->hw.info = SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_JOINT_DUPLEX |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
+ s = &bebob->tx_stream;
+ formations = bebob->tx_stream_formations;
+ } else {
+ runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
+ s = &bebob->rx_stream;
+ formations = bebob->rx_stream_formations;
+ }
+
+ limit_channels_and_rates(&runtime->hw, formations);
+ limit_period_and_buffer(&runtime->hw);
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_channels, formations,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ goto end;
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ hw_rule_rate, formations,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ goto end;
+
+ err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
+end:
+ return err;
+}
+
+static int
+pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_bebob *bebob = substream->private_data;
+ struct snd_bebob_rate_spec *spec = bebob->spec->rate;
+ unsigned int sampling_rate;
+ bool internal;
+ int err;
+
+ err = snd_bebob_stream_lock_try(bebob);
+ if (err < 0)
+ goto end;
+
+ err = pcm_init_hw_params(bebob, substream);
+ if (err < 0)
+ goto err_locked;
+
+ err = snd_bebob_stream_check_internal_clock(bebob, &internal);
+ if (err < 0)
+ goto err_locked;
+
+ /*
+ * When source of clock is internal or any PCM stream are running,
+ * the available sampling rate is limited at current sampling rate.
+ */
+ if (!internal ||
+ amdtp_stream_pcm_running(&bebob->tx_stream) ||
+ amdtp_stream_pcm_running(&bebob->rx_stream)) {
+ err = spec->get(bebob, &sampling_rate);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get sampling rate: %d\n", err);
+ goto err_locked;
+ }
+
+ substream->runtime->hw.rate_min = sampling_rate;
+ substream->runtime->hw.rate_max = sampling_rate;
+ }
+
+ snd_pcm_set_sync(substream);
+end:
+ return err;
+err_locked:
+ snd_bebob_stream_lock_release(bebob);
+ return err;
+}
+
+static int
+pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_bebob *bebob = substream->private_data;
+ snd_bebob_stream_lock_release(bebob);
+ return 0;
+}
+
+static int
+pcm_capture_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_bebob *bebob = substream->private_data;
+
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ atomic_inc(&bebob->capture_substreams);
+ amdtp_stream_set_pcm_format(&bebob->tx_stream,
+ params_format(hw_params));
+ return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+}
+static int
+pcm_playback_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_bebob *bebob = substream->private_data;
+
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ atomic_inc(&bebob->playback_substreams);
+ amdtp_stream_set_pcm_format(&bebob->rx_stream,
+ params_format(hw_params));
+ return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+}
+
+static int
+pcm_capture_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_bebob *bebob = substream->private_data;
+
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ atomic_dec(&bebob->capture_substreams);
+
+ snd_bebob_stream_stop_duplex(bebob);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+static int
+pcm_playback_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_bebob *bebob = substream->private_data;
+
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ atomic_dec(&bebob->playback_substreams);
+
+ snd_bebob_stream_stop_duplex(bebob);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int
+pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_bebob *bebob = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ err = snd_bebob_stream_start_duplex(bebob, runtime->rate);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&bebob->tx_stream);
+
+ return err;
+}
+static int
+pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_bebob *bebob = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ err = snd_bebob_stream_start_duplex(bebob, runtime->rate);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&bebob->rx_stream);
+
+ return err;
+}
+
+static int
+pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_bebob *bebob = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&bebob->tx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&bebob->tx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+static int
+pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_bebob *bebob = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&bebob->rx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&bebob->rx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t
+pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_bebob *bebob = sbstrm->private_data;
+ return amdtp_stream_pcm_pointer(&bebob->tx_stream);
+}
+static snd_pcm_uframes_t
+pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_bebob *bebob = sbstrm->private_data;
+ return amdtp_stream_pcm_pointer(&bebob->rx_stream);
+}
+
+static const struct snd_pcm_ops pcm_capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pcm_capture_hw_params,
+ .hw_free = pcm_capture_hw_free,
+ .prepare = pcm_capture_prepare,
+ .trigger = pcm_capture_trigger,
+ .pointer = pcm_capture_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+};
+static const struct snd_pcm_ops pcm_playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pcm_playback_hw_params,
+ .hw_free = pcm_playback_hw_free,
+ .prepare = pcm_playback_prepare,
+ .trigger = pcm_playback_trigger,
+ .pointer = pcm_playback_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+};
+
+int snd_bebob_create_pcm_devices(struct snd_bebob *bebob)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(bebob->card, bebob->card->driver, 0, 1, 1, &pcm);
+ if (err < 0)
+ goto end;
+
+ pcm->private_data = bebob;
+ snprintf(pcm->name, sizeof(pcm->name),
+ "%s PCM", bebob->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
+end:
+ return err;
+}
diff --git a/sound/firewire/bebob/bebob_proc.c b/sound/firewire/bebob/bebob_proc.c
new file mode 100644
index 000000000000..335da64506e0
--- /dev/null
+++ b/sound/firewire/bebob/bebob_proc.c
@@ -0,0 +1,196 @@
+/*
+ * bebob_proc.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./bebob.h"
+
+/* contents of information register */
+struct hw_info {
+ u64 manufacturer;
+ u32 protocol_ver;
+ u32 bld_ver;
+ u32 guid[2];
+ u32 model_id;
+ u32 model_rev;
+ u64 fw_date;
+ u64 fw_time;
+ u32 fw_id;
+ u32 fw_ver;
+ u32 base_addr;
+ u32 max_size;
+ u64 bld_date;
+ u64 bld_time;
+/* may not used in product
+ u64 dbg_date;
+ u64 dbg_time;
+ u32 dbg_id;
+ u32 dbg_version;
+*/
+} __packed;
+
+static void
+proc_read_hw_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_bebob *bebob = entry->private_data;
+ struct hw_info *info;
+
+ info = kzalloc(sizeof(struct hw_info), GFP_KERNEL);
+ if (info == NULL)
+ return;
+
+ if (snd_bebob_read_block(bebob->unit, 0,
+ info, sizeof(struct hw_info)) < 0)
+ goto end;
+
+ snd_iprintf(buffer, "Manufacturer:\t%.8s\n",
+ (char *)&info->manufacturer);
+ snd_iprintf(buffer, "Protocol Ver:\t%d\n", info->protocol_ver);
+ snd_iprintf(buffer, "Build Ver:\t%d\n", info->bld_ver);
+ snd_iprintf(buffer, "GUID:\t\t0x%.8X%.8X\n",
+ info->guid[0], info->guid[1]);
+ snd_iprintf(buffer, "Model ID:\t0x%02X\n", info->model_id);
+ snd_iprintf(buffer, "Model Rev:\t%d\n", info->model_rev);
+ snd_iprintf(buffer, "Firmware Date:\t%.8s\n", (char *)&info->fw_date);
+ snd_iprintf(buffer, "Firmware Time:\t%.8s\n", (char *)&info->fw_time);
+ snd_iprintf(buffer, "Firmware ID:\t0x%X\n", info->fw_id);
+ snd_iprintf(buffer, "Firmware Ver:\t%d\n", info->fw_ver);
+ snd_iprintf(buffer, "Base Addr:\t0x%X\n", info->base_addr);
+ snd_iprintf(buffer, "Max Size:\t%d\n", info->max_size);
+ snd_iprintf(buffer, "Loader Date:\t%.8s\n", (char *)&info->bld_date);
+ snd_iprintf(buffer, "Loader Time:\t%.8s\n", (char *)&info->bld_time);
+
+end:
+ kfree(info);
+}
+
+static void
+proc_read_meters(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_bebob *bebob = entry->private_data;
+ struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+ u32 *buf;
+ unsigned int i, c, channels, size;
+
+ if (spec == NULL)
+ return;
+
+ channels = spec->num * 2;
+ size = channels * sizeof(u32);
+ buf = kmalloc(size, GFP_KERNEL);
+ if (buf == NULL)
+ return;
+
+ if (spec->get(bebob, buf, size) < 0)
+ goto end;
+
+ for (i = 0, c = 1; i < channels; i++) {
+ snd_iprintf(buffer, "%s %d:\t%d\n",
+ spec->labels[i / 2], c++, buf[i]);
+ if ((i + 1 < channels - 1) &&
+ (strcmp(spec->labels[i / 2],
+ spec->labels[(i + 1) / 2]) != 0))
+ c = 1;
+ }
+end:
+ kfree(buf);
+}
+
+static void
+proc_read_formation(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_bebob *bebob = entry->private_data;
+ struct snd_bebob_stream_formation *formation;
+ unsigned int i;
+
+ snd_iprintf(buffer, "Output Stream from device:\n");
+ snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n");
+ formation = bebob->tx_stream_formations;
+ for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+ snd_iprintf(buffer,
+ "\t%d\t%d\t%d\n", snd_bebob_rate_table[i],
+ formation[i].pcm, formation[i].midi);
+ }
+
+ snd_iprintf(buffer, "Input Stream to device:\n");
+ snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n");
+ formation = bebob->rx_stream_formations;
+ for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+ snd_iprintf(buffer,
+ "\t%d\t%d\t%d\n", snd_bebob_rate_table[i],
+ formation[i].pcm, formation[i].midi);
+ }
+}
+
+static void
+proc_read_clock(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_bebob *bebob = entry->private_data;
+ struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
+ struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+ unsigned int rate, id;
+ bool internal;
+
+ if (rate_spec->get(bebob, &rate) >= 0)
+ snd_iprintf(buffer, "Sampling rate: %d\n", rate);
+
+ if (clk_spec) {
+ if (clk_spec->get(bebob, &id) >= 0)
+ snd_iprintf(buffer, "Clock Source: %s\n",
+ clk_spec->labels[id]);
+ } else {
+ if (snd_bebob_stream_check_internal_clock(bebob,
+ &internal) >= 0)
+ snd_iprintf(buffer, "Clock Source: %s (MSU-dest: %d)\n",
+ (internal) ? "Internal" : "External",
+ bebob->sync_input_plug);
+ }
+}
+
+static void
+add_node(struct snd_bebob *bebob, struct snd_info_entry *root, const char *name,
+ void (*op)(struct snd_info_entry *e, struct snd_info_buffer *b))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(bebob->card, name, root);
+ if (entry == NULL)
+ return;
+
+ snd_info_set_text_ops(entry, bebob, op);
+ if (snd_info_register(entry) < 0)
+ snd_info_free_entry(entry);
+}
+
+void snd_bebob_proc_init(struct snd_bebob *bebob)
+{
+ struct snd_info_entry *root;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(bebob->card, "firewire",
+ bebob->card->proc_root);
+ if (root == NULL)
+ return;
+ root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ if (snd_info_register(root) < 0) {
+ snd_info_free_entry(root);
+ return;
+ }
+
+ add_node(bebob, root, "clock", proc_read_clock);
+ add_node(bebob, root, "firmware", proc_read_hw_info);
+ add_node(bebob, root, "formation", proc_read_formation);
+
+ if (bebob->spec->meter != NULL)
+ add_node(bebob, root, "meter", proc_read_meters);
+}
diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c
new file mode 100644
index 000000000000..514c7c9fa1cd
--- /dev/null
+++ b/sound/firewire/bebob/bebob_stream.c
@@ -0,0 +1,1021 @@
+/*
+ * bebob_stream.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./bebob.h"
+
+#define CALLBACK_TIMEOUT 1000
+#define FW_ISO_RESOURCE_DELAY 1000
+
+/*
+ * NOTE;
+ * For BeBoB streams, Both of input and output CMP connection are important.
+ *
+ * For most devices, each CMP connection starts to transmit/receive a
+ * corresponding stream. But for a few devices, both of CMP connection needs
+ * to start transmitting stream. An example is 'M-Audio Firewire 410'.
+ */
+
+/* 128 is an arbitrary length but it seems to be enough */
+#define FORMAT_MAXIMUM_LENGTH 128
+
+const unsigned int snd_bebob_rate_table[SND_BEBOB_STRM_FMT_ENTRIES] = {
+ [0] = 32000,
+ [1] = 44100,
+ [2] = 48000,
+ [3] = 88200,
+ [4] = 96000,
+ [5] = 176400,
+ [6] = 192000,
+};
+
+/*
+ * See: Table 51: Extended Stream Format Info ‘Sampling Frequency’
+ * in Additional AVC commands (Nov 2003, BridgeCo)
+ */
+static const unsigned int bridgeco_freq_table[] = {
+ [0] = 0x02,
+ [1] = 0x03,
+ [2] = 0x04,
+ [3] = 0x0a,
+ [4] = 0x05,
+ [5] = 0x06,
+ [6] = 0x07,
+};
+
+static unsigned int
+get_formation_index(unsigned int rate)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(snd_bebob_rate_table); i++) {
+ if (snd_bebob_rate_table[i] == rate)
+ return i;
+ }
+ return -EINVAL;
+}
+
+int
+snd_bebob_stream_get_rate(struct snd_bebob *bebob, unsigned int *curr_rate)
+{
+ unsigned int tx_rate, rx_rate, trials;
+ int err;
+
+ trials = 0;
+ do {
+ err = avc_general_get_sig_fmt(bebob->unit, &tx_rate,
+ AVC_GENERAL_PLUG_DIR_OUT, 0);
+ } while (err == -EAGAIN && ++trials < 3);
+ if (err < 0)
+ goto end;
+
+ trials = 0;
+ do {
+ err = avc_general_get_sig_fmt(bebob->unit, &rx_rate,
+ AVC_GENERAL_PLUG_DIR_IN, 0);
+ } while (err == -EAGAIN && ++trials < 3);
+ if (err < 0)
+ goto end;
+
+ *curr_rate = rx_rate;
+ if (rx_rate == tx_rate)
+ goto end;
+
+ /* synchronize receive stream rate to transmit stream rate */
+ err = avc_general_set_sig_fmt(bebob->unit, rx_rate,
+ AVC_GENERAL_PLUG_DIR_IN, 0);
+end:
+ return err;
+}
+
+int
+snd_bebob_stream_set_rate(struct snd_bebob *bebob, unsigned int rate)
+{
+ int err;
+
+ err = avc_general_set_sig_fmt(bebob->unit, rate,
+ AVC_GENERAL_PLUG_DIR_OUT, 0);
+ if (err < 0)
+ goto end;
+
+ err = avc_general_set_sig_fmt(bebob->unit, rate,
+ AVC_GENERAL_PLUG_DIR_IN, 0);
+ if (err < 0)
+ goto end;
+
+ /*
+ * Some devices need a bit time for transition.
+ * 300msec is got by some experiments.
+ */
+ msleep(300);
+end:
+ return err;
+}
+
+int
+snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, bool *internal)
+{
+ struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES], input[7];
+ unsigned int id;
+ int err = 0;
+
+ *internal = false;
+
+ /* 1.The device has its own operation to switch source of clock */
+ if (clk_spec) {
+ err = clk_spec->get(bebob, &id);
+ if (err < 0)
+ dev_err(&bebob->unit->device,
+ "fail to get clock source: %d\n", err);
+ else if (strncmp(clk_spec->labels[id], SND_BEBOB_CLOCK_INTERNAL,
+ strlen(SND_BEBOB_CLOCK_INTERNAL)) == 0)
+ *internal = true;
+ goto end;
+ }
+
+ /*
+ * 2.The device don't support to switch source of clock then assumed
+ * to use internal clock always
+ */
+ if (bebob->sync_input_plug < 0) {
+ *internal = true;
+ goto end;
+ }
+
+ /*
+ * 3.The device supports to switch source of clock by an usual way.
+ * Let's check input for 'Music Sub Unit Sync Input' plug.
+ */
+ avc_bridgeco_fill_msu_addr(addr, AVC_BRIDGECO_PLUG_DIR_IN,
+ bebob->sync_input_plug);
+ err = avc_bridgeco_get_plug_input(bebob->unit, addr, input);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get an input for MSU in plug %d: %d\n",
+ bebob->sync_input_plug, err);
+ goto end;
+ }
+
+ /*
+ * If there are no input plugs, all of fields are 0xff.
+ * Here check the first field. This field is used for direction.
+ */
+ if (input[0] == 0xff) {
+ *internal = true;
+ goto end;
+ }
+
+ /*
+ * If source of clock is internal CSR, Music Sub Unit Sync Input is
+ * a destination of Music Sub Unit Sync Output.
+ */
+ *internal = ((input[0] == AVC_BRIDGECO_PLUG_DIR_OUT) &&
+ (input[1] == AVC_BRIDGECO_PLUG_MODE_SUBUNIT) &&
+ (input[2] == 0x0c) &&
+ (input[3] == 0x00));
+end:
+ return err;
+}
+
+static unsigned int
+map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
+{
+ unsigned int sec, sections, ch, channels;
+ unsigned int pcm, midi, location;
+ unsigned int stm_pos, sec_loc, pos;
+ u8 *buf, addr[AVC_BRIDGECO_ADDR_BYTES], type;
+ enum avc_bridgeco_plug_dir dir;
+ int err;
+
+ /*
+ * The length of return value of this command cannot be expected. Here
+ * use the maximum length of FCP.
+ */
+ buf = kzalloc(256, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ if (s == &bebob->tx_stream)
+ dir = AVC_BRIDGECO_PLUG_DIR_OUT;
+ else
+ dir = AVC_BRIDGECO_PLUG_DIR_IN;
+
+ avc_bridgeco_fill_unit_addr(addr, dir, AVC_BRIDGECO_PLUG_UNIT_ISOC, 0);
+ err = avc_bridgeco_get_plug_ch_pos(bebob->unit, addr, buf, 256);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get channel position for isoc %s plug 0: %d\n",
+ (dir == AVC_BRIDGECO_PLUG_DIR_IN) ? "in" : "out",
+ err);
+ goto end;
+ }
+ pos = 0;
+
+ /* positions in I/O buffer */
+ pcm = 0;
+ midi = 0;
+
+ /* the number of sections in AMDTP packet */
+ sections = buf[pos++];
+
+ for (sec = 0; sec < sections; sec++) {
+ /* type of this section */
+ avc_bridgeco_fill_unit_addr(addr, dir,
+ AVC_BRIDGECO_PLUG_UNIT_ISOC, 0);
+ err = avc_bridgeco_get_plug_section_type(bebob->unit, addr,
+ sec, &type);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get section type for isoc %s plug 0: %d\n",
+ (dir == AVC_BRIDGECO_PLUG_DIR_IN) ? "in" :
+ "out",
+ err);
+ goto end;
+ }
+ /* NoType */
+ if (type == 0xff) {
+ err = -ENOSYS;
+ goto end;
+ }
+
+ /* the number of channels in this section */
+ channels = buf[pos++];
+
+ for (ch = 0; ch < channels; ch++) {
+ /* position of this channel in AMDTP packet */
+ stm_pos = buf[pos++] - 1;
+ /* location of this channel in this section */
+ sec_loc = buf[pos++] - 1;
+
+ /*
+ * Basically the number of location is within the
+ * number of channels in this section. But some models
+ * of M-Audio don't follow this. Its location for MIDI
+ * is the position of MIDI channels in AMDTP packet.
+ */
+ if (sec_loc >= channels)
+ sec_loc = ch;
+
+ switch (type) {
+ /* for MIDI conformant data channel */
+ case 0x0a:
+ /* AMDTP_MAX_CHANNELS_FOR_MIDI is 1. */
+ if ((midi > 0) && (stm_pos != midi)) {
+ err = -ENOSYS;
+ goto end;
+ }
+ s->midi_position = stm_pos;
+ midi = stm_pos;
+ break;
+ /* for PCM data channel */
+ case 0x01: /* Headphone */
+ case 0x02: /* Microphone */
+ case 0x03: /* Line */
+ case 0x04: /* SPDIF */
+ case 0x05: /* ADAT */
+ case 0x06: /* TDIF */
+ case 0x07: /* MADI */
+ /* for undefined/changeable signal */
+ case 0x08: /* Analog */
+ case 0x09: /* Digital */
+ default:
+ location = pcm + sec_loc;
+ if (location >= AMDTP_MAX_CHANNELS_FOR_PCM) {
+ err = -ENOSYS;
+ goto end;
+ }
+ s->pcm_positions[location] = stm_pos;
+ break;
+ }
+ }
+
+ if (type != 0x0a)
+ pcm += channels;
+ else
+ midi += channels;
+ }
+end:
+ kfree(buf);
+ return err;
+}
+
+static int
+init_both_connections(struct snd_bebob *bebob)
+{
+ int err;
+
+ err = cmp_connection_init(&bebob->in_conn,
+ bebob->unit, CMP_INPUT, 0);
+ if (err < 0)
+ goto end;
+
+ err = cmp_connection_init(&bebob->out_conn,
+ bebob->unit, CMP_OUTPUT, 0);
+ if (err < 0)
+ cmp_connection_destroy(&bebob->in_conn);
+end:
+ return err;
+}
+
+static int
+check_connection_used_by_others(struct snd_bebob *bebob, struct amdtp_stream *s)
+{
+ struct cmp_connection *conn;
+ bool used;
+ int err;
+
+ if (s == &bebob->tx_stream)
+ conn = &bebob->out_conn;
+ else
+ conn = &bebob->in_conn;
+
+ err = cmp_connection_check_used(conn, &used);
+ if ((err >= 0) && used && !amdtp_stream_running(s)) {
+ dev_err(&bebob->unit->device,
+ "Connection established by others: %cPCR[%d]\n",
+ (conn->direction == CMP_OUTPUT) ? 'o' : 'i',
+ conn->pcr_index);
+ err = -EBUSY;
+ }
+
+ return err;
+}
+
+static int
+make_both_connections(struct snd_bebob *bebob, unsigned int rate)
+{
+ int index, pcm_channels, midi_channels, err = 0;
+
+ if (bebob->connected)
+ goto end;
+
+ /* confirm params for both streams */
+ index = get_formation_index(rate);
+ pcm_channels = bebob->tx_stream_formations[index].pcm;
+ midi_channels = bebob->tx_stream_formations[index].midi;
+ amdtp_stream_set_parameters(&bebob->tx_stream,
+ rate, pcm_channels, midi_channels * 8);
+ pcm_channels = bebob->rx_stream_formations[index].pcm;
+ midi_channels = bebob->rx_stream_formations[index].midi;
+ amdtp_stream_set_parameters(&bebob->rx_stream,
+ rate, pcm_channels, midi_channels * 8);
+
+ /* establish connections for both streams */
+ err = cmp_connection_establish(&bebob->out_conn,
+ amdtp_stream_get_max_payload(&bebob->tx_stream));
+ if (err < 0)
+ goto end;
+ err = cmp_connection_establish(&bebob->in_conn,
+ amdtp_stream_get_max_payload(&bebob->rx_stream));
+ if (err < 0) {
+ cmp_connection_break(&bebob->out_conn);
+ goto end;
+ }
+
+ bebob->connected = true;
+end:
+ return err;
+}
+
+static void
+break_both_connections(struct snd_bebob *bebob)
+{
+ cmp_connection_break(&bebob->in_conn);
+ cmp_connection_break(&bebob->out_conn);
+
+ bebob->connected = false;
+
+ /* These models seems to be in transition state for a longer time. */
+ if (bebob->maudio_special_quirk != NULL)
+ msleep(200);
+}
+
+static void
+destroy_both_connections(struct snd_bebob *bebob)
+{
+ break_both_connections(bebob);
+
+ cmp_connection_destroy(&bebob->in_conn);
+ cmp_connection_destroy(&bebob->out_conn);
+}
+
+static int
+get_sync_mode(struct snd_bebob *bebob, enum cip_flags *sync_mode)
+{
+ /* currently this module doesn't support SYT-Match mode */
+ *sync_mode = CIP_SYNC_TO_DEVICE;
+ return 0;
+}
+
+static int
+start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream,
+ unsigned int rate)
+{
+ struct cmp_connection *conn;
+ int err = 0;
+
+ if (stream == &bebob->rx_stream)
+ conn = &bebob->in_conn;
+ else
+ conn = &bebob->out_conn;
+
+ /* channel mapping */
+ if (bebob->maudio_special_quirk == NULL) {
+ err = map_data_channels(bebob, stream);
+ if (err < 0)
+ goto end;
+ }
+
+ /* start amdtp stream */
+ err = amdtp_stream_start(stream,
+ conn->resources.channel,
+ conn->speed);
+end:
+ return err;
+}
+
+int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
+{
+ int err;
+
+ err = init_both_connections(bebob);
+ if (err < 0)
+ goto end;
+
+ err = amdtp_stream_init(&bebob->tx_stream, bebob->unit,
+ AMDTP_IN_STREAM, CIP_BLOCKING);
+ if (err < 0) {
+ amdtp_stream_destroy(&bebob->tx_stream);
+ destroy_both_connections(bebob);
+ goto end;
+ }
+ /* See comments in next function */
+ init_completion(&bebob->bus_reset);
+ bebob->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
+ /*
+ * At high sampling rate, M-Audio special firmware transmits empty
+ * packet with the value of dbc incremented by 8 but the others are
+ * valid to IEC 61883-1.
+ */
+ if (bebob->maudio_special_quirk)
+ bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC;
+
+ err = amdtp_stream_init(&bebob->rx_stream, bebob->unit,
+ AMDTP_OUT_STREAM, CIP_BLOCKING);
+ if (err < 0) {
+ amdtp_stream_destroy(&bebob->tx_stream);
+ amdtp_stream_destroy(&bebob->rx_stream);
+ destroy_both_connections(bebob);
+ }
+ /*
+ * The firmware for these devices ignore MIDI messages in more than
+ * first 8 data blocks of an received AMDTP packet.
+ */
+ if (bebob->spec == &maudio_fw410_spec ||
+ bebob->spec == &maudio_special_spec)
+ bebob->rx_stream.rx_blocks_for_midi = 8;
+end:
+ return err;
+}
+
+int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
+{
+ struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
+ struct amdtp_stream *master, *slave;
+ atomic_t *slave_substreams;
+ enum cip_flags sync_mode;
+ unsigned int curr_rate;
+ bool updated = false;
+ int err = 0;
+
+ /*
+ * Normal BeBoB firmware has a quirk at bus reset to transmits packets
+ * with discontinuous value in dbc field.
+ *
+ * This 'struct completion' is used to call .update() at first to update
+ * connections/streams. Next following codes handle streaming error.
+ */
+ if (amdtp_streaming_error(&bebob->tx_stream)) {
+ if (completion_done(&bebob->bus_reset))
+ reinit_completion(&bebob->bus_reset);
+
+ updated = (wait_for_completion_interruptible_timeout(
+ &bebob->bus_reset,
+ msecs_to_jiffies(FW_ISO_RESOURCE_DELAY)) > 0);
+ }
+
+ mutex_lock(&bebob->mutex);
+
+ /* Need no substreams */
+ if (atomic_read(&bebob->playback_substreams) == 0 &&
+ atomic_read(&bebob->capture_substreams) == 0)
+ goto end;
+
+ err = get_sync_mode(bebob, &sync_mode);
+ if (err < 0)
+ goto end;
+ if (sync_mode == CIP_SYNC_TO_DEVICE) {
+ master = &bebob->tx_stream;
+ slave = &bebob->rx_stream;
+ slave_substreams = &bebob->playback_substreams;
+ } else {
+ master = &bebob->rx_stream;
+ slave = &bebob->tx_stream;
+ slave_substreams = &bebob->capture_substreams;
+ }
+
+ /*
+ * Considering JACK/FFADO streaming:
+ * TODO: This can be removed hwdep functionality becomes popular.
+ */
+ err = check_connection_used_by_others(bebob, master);
+ if (err < 0)
+ goto end;
+
+ /*
+ * packet queueing error or detecting discontinuity
+ *
+ * At bus reset, connections should not be broken here. So streams need
+ * to be re-started. This is a reason to use SKIP_INIT_DBC_CHECK flag.
+ */
+ if (amdtp_streaming_error(master))
+ amdtp_stream_stop(master);
+ if (amdtp_streaming_error(slave))
+ amdtp_stream_stop(slave);
+ if (!updated &&
+ !amdtp_stream_running(master) && !amdtp_stream_running(slave))
+ break_both_connections(bebob);
+
+ /* stop streams if rate is different */
+ err = rate_spec->get(bebob, &curr_rate);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get sampling rate: %d\n", err);
+ goto end;
+ }
+ if (rate == 0)
+ rate = curr_rate;
+ if (rate != curr_rate) {
+ amdtp_stream_stop(master);
+ amdtp_stream_stop(slave);
+ break_both_connections(bebob);
+ }
+
+ /* master should be always running */
+ if (!amdtp_stream_running(master)) {
+ amdtp_stream_set_sync(sync_mode, master, slave);
+ bebob->master = master;
+
+ /*
+ * NOTE:
+ * If establishing connections at first, Yamaha GO46
+ * (and maybe Terratec X24) don't generate sound.
+ *
+ * For firmware customized by M-Audio, refer to next NOTE.
+ */
+ if (bebob->maudio_special_quirk == NULL) {
+ err = rate_spec->set(bebob, rate);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to set sampling rate: %d\n",
+ err);
+ goto end;
+ }
+ }
+
+ err = make_both_connections(bebob, rate);
+ if (err < 0)
+ goto end;
+
+ err = start_stream(bebob, master, rate);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to run AMDTP master stream:%d\n", err);
+ break_both_connections(bebob);
+ goto end;
+ }
+
+ /*
+ * NOTE:
+ * The firmware customized by M-Audio uses these commands to
+ * start transmitting stream. This is not usual way.
+ */
+ if (bebob->maudio_special_quirk != NULL) {
+ err = rate_spec->set(bebob, rate);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to ensure sampling rate: %d\n",
+ err);
+ amdtp_stream_stop(master);
+ break_both_connections(bebob);
+ goto end;
+ }
+ }
+
+ /* wait first callback */
+ if (!amdtp_stream_wait_callback(master, CALLBACK_TIMEOUT)) {
+ amdtp_stream_stop(master);
+ break_both_connections(bebob);
+ err = -ETIMEDOUT;
+ goto end;
+ }
+ }
+
+ /* start slave if needed */
+ if (atomic_read(slave_substreams) > 0 && !amdtp_stream_running(slave)) {
+ err = start_stream(bebob, slave, rate);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to run AMDTP slave stream:%d\n", err);
+ amdtp_stream_stop(master);
+ break_both_connections(bebob);
+ goto end;
+ }
+
+ /* wait first callback */
+ if (!amdtp_stream_wait_callback(slave, CALLBACK_TIMEOUT)) {
+ amdtp_stream_stop(slave);
+ amdtp_stream_stop(master);
+ break_both_connections(bebob);
+ err = -ETIMEDOUT;
+ }
+ }
+end:
+ mutex_unlock(&bebob->mutex);
+ return err;
+}
+
+void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
+{
+ struct amdtp_stream *master, *slave;
+ atomic_t *master_substreams, *slave_substreams;
+
+ mutex_lock(&bebob->mutex);
+
+ if (bebob->master == &bebob->rx_stream) {
+ slave = &bebob->tx_stream;
+ master = &bebob->rx_stream;
+ slave_substreams = &bebob->capture_substreams;
+ master_substreams = &bebob->playback_substreams;
+ } else {
+ slave = &bebob->rx_stream;
+ master = &bebob->tx_stream;
+ slave_substreams = &bebob->playback_substreams;
+ master_substreams = &bebob->capture_substreams;
+ }
+
+ if (atomic_read(slave_substreams) == 0) {
+ amdtp_stream_pcm_abort(slave);
+ amdtp_stream_stop(slave);
+
+ if (atomic_read(master_substreams) == 0) {
+ amdtp_stream_pcm_abort(master);
+ amdtp_stream_stop(master);
+ break_both_connections(bebob);
+ }
+ }
+
+ mutex_unlock(&bebob->mutex);
+}
+
+void snd_bebob_stream_update_duplex(struct snd_bebob *bebob)
+{
+ /* vs. XRUN recovery due to discontinuity at bus reset */
+ mutex_lock(&bebob->mutex);
+
+ if ((cmp_connection_update(&bebob->in_conn) < 0) ||
+ (cmp_connection_update(&bebob->out_conn) < 0)) {
+ amdtp_stream_pcm_abort(&bebob->rx_stream);
+ amdtp_stream_pcm_abort(&bebob->tx_stream);
+ amdtp_stream_stop(&bebob->rx_stream);
+ amdtp_stream_stop(&bebob->tx_stream);
+ break_both_connections(bebob);
+ } else {
+ amdtp_stream_update(&bebob->rx_stream);
+ amdtp_stream_update(&bebob->tx_stream);
+ }
+
+ /* wake up stream_start_duplex() */
+ if (!completion_done(&bebob->bus_reset))
+ complete_all(&bebob->bus_reset);
+
+ mutex_unlock(&bebob->mutex);
+}
+
+void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob)
+{
+ mutex_lock(&bebob->mutex);
+
+ amdtp_stream_pcm_abort(&bebob->rx_stream);
+ amdtp_stream_pcm_abort(&bebob->tx_stream);
+
+ amdtp_stream_stop(&bebob->rx_stream);
+ amdtp_stream_stop(&bebob->tx_stream);
+
+ amdtp_stream_destroy(&bebob->rx_stream);
+ amdtp_stream_destroy(&bebob->tx_stream);
+
+ destroy_both_connections(bebob);
+
+ mutex_unlock(&bebob->mutex);
+}
+
+/*
+ * See: Table 50: Extended Stream Format Info Format Hierarchy Level 2’
+ * in Additional AVC commands (Nov 2003, BridgeCo)
+ * Also 'Clause 12 AM824 sequence adaption layers' in IEC 61883-6:2005
+ */
+static int
+parse_stream_formation(u8 *buf, unsigned int len,
+ struct snd_bebob_stream_formation *formation)
+{
+ unsigned int i, e, channels, format;
+
+ /*
+ * this module can support a hierarchy combination that:
+ * Root: Audio and Music (0x90)
+ * Level 1: AM824 Compound (0x40)
+ */
+ if ((buf[0] != 0x90) || (buf[1] != 0x40))
+ return -ENOSYS;
+
+ /* check sampling rate */
+ for (i = 0; i < ARRAY_SIZE(bridgeco_freq_table); i++) {
+ if (buf[2] == bridgeco_freq_table[i])
+ break;
+ }
+ if (i == sizeof(bridgeco_freq_table))
+ return -ENOSYS;
+
+ /* Avoid double count by different entries for the same rate. */
+ memset(&formation[i], 0, sizeof(struct snd_bebob_stream_formation));
+
+ for (e = 0; e < buf[4]; e++) {
+ channels = buf[5 + e * 2];
+ format = buf[6 + e * 2];
+
+ switch (format) {
+ /* IEC 60958 Conformant, currently handled as MBLA */
+ case 0x00:
+ /* Multi bit linear audio */
+ case 0x06: /* Raw */
+ formation[i].pcm += channels;
+ break;
+ /* MIDI Conformant */
+ case 0x0d:
+ formation[i].midi += channels;
+ break;
+ /* IEC 61937-3 to 7 */
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ /* Multi bit linear audio */
+ case 0x07: /* DVD-Audio */
+ case 0x0c: /* High Precision */
+ /* One Bit Audio */
+ case 0x08: /* (Plain) Raw */
+ case 0x09: /* (Plain) SACD */
+ case 0x0a: /* (Encoded) Raw */
+ case 0x0b: /* (Encoded) SACD */
+ /* Synchronization Stream (Stereo Raw audio) */
+ case 0x40:
+ /* Don't care */
+ case 0xff:
+ default:
+ return -ENOSYS; /* not supported */
+ }
+ }
+
+ if (formation[i].pcm > AMDTP_MAX_CHANNELS_FOR_PCM ||
+ formation[i].midi > AMDTP_MAX_CHANNELS_FOR_MIDI)
+ return -ENOSYS;
+
+ return 0;
+}
+
+static int
+fill_stream_formations(struct snd_bebob *bebob, enum avc_bridgeco_plug_dir dir,
+ unsigned short pid)
+{
+ u8 *buf;
+ struct snd_bebob_stream_formation *formations;
+ unsigned int len, eid;
+ u8 addr[AVC_BRIDGECO_ADDR_BYTES];
+ int err;
+
+ buf = kmalloc(FORMAT_MAXIMUM_LENGTH, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ if (dir == AVC_BRIDGECO_PLUG_DIR_IN)
+ formations = bebob->rx_stream_formations;
+ else
+ formations = bebob->tx_stream_formations;
+
+ for (eid = 0; eid < SND_BEBOB_STRM_FMT_ENTRIES; eid++) {
+ len = FORMAT_MAXIMUM_LENGTH;
+ avc_bridgeco_fill_unit_addr(addr, dir,
+ AVC_BRIDGECO_PLUG_UNIT_ISOC, pid);
+ err = avc_bridgeco_get_plug_strm_fmt(bebob->unit, addr, buf,
+ &len, eid);
+ /* No entries remained. */
+ if (err == -EINVAL && eid > 0) {
+ err = 0;
+ break;
+ } else if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get stream format %d for isoc %s plug %d:%d\n",
+ eid,
+ (dir == AVC_BRIDGECO_PLUG_DIR_IN) ? "in" :
+ "out",
+ pid, err);
+ break;
+ }
+
+ err = parse_stream_formation(buf, len, formations);
+ if (err < 0)
+ break;
+ }
+
+ kfree(buf);
+ return err;
+}
+
+static int
+seek_msu_sync_input_plug(struct snd_bebob *bebob)
+{
+ u8 plugs[AVC_PLUG_INFO_BUF_BYTES], addr[AVC_BRIDGECO_ADDR_BYTES];
+ unsigned int i;
+ enum avc_bridgeco_plug_type type;
+ int err;
+
+ /* Get the number of Music Sub Unit for both direction. */
+ err = avc_general_get_plug_info(bebob->unit, 0x0c, 0x00, 0x00, plugs);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get info for MSU in/out plugs: %d\n",
+ err);
+ goto end;
+ }
+
+ /* seek destination plugs for 'MSU sync input' */
+ bebob->sync_input_plug = -1;
+ for (i = 0; i < plugs[0]; i++) {
+ avc_bridgeco_fill_msu_addr(addr, AVC_BRIDGECO_PLUG_DIR_IN, i);
+ err = avc_bridgeco_get_plug_type(bebob->unit, addr, &type);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get type for MSU in plug %d: %d\n",
+ i, err);
+ goto end;
+ }
+
+ if (type == AVC_BRIDGECO_PLUG_TYPE_SYNC) {
+ bebob->sync_input_plug = i;
+ break;
+ }
+ }
+end:
+ return err;
+}
+
+int snd_bebob_stream_discover(struct snd_bebob *bebob)
+{
+ struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+ u8 plugs[AVC_PLUG_INFO_BUF_BYTES], addr[AVC_BRIDGECO_ADDR_BYTES];
+ enum avc_bridgeco_plug_type type;
+ unsigned int i;
+ int err;
+
+ /* the number of plugs for isoc in/out, ext in/out */
+ err = avc_general_get_plug_info(bebob->unit, 0x1f, 0x07, 0x00, plugs);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get info for isoc/external in/out plugs: %d\n",
+ err);
+ goto end;
+ }
+
+ /*
+ * This module supports at least one isoc input plug and one isoc
+ * output plug.
+ */
+ if ((plugs[0] == 0) || (plugs[1] == 0)) {
+ err = -ENOSYS;
+ goto end;
+ }
+
+ avc_bridgeco_fill_unit_addr(addr, AVC_BRIDGECO_PLUG_DIR_IN,
+ AVC_BRIDGECO_PLUG_UNIT_ISOC, 0);
+ err = avc_bridgeco_get_plug_type(bebob->unit, addr, &type);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get type for isoc in plug 0: %d\n", err);
+ goto end;
+ } else if (type != AVC_BRIDGECO_PLUG_TYPE_ISOC) {
+ err = -ENOSYS;
+ goto end;
+ }
+ err = fill_stream_formations(bebob, AVC_BRIDGECO_PLUG_DIR_IN, 0);
+ if (err < 0)
+ goto end;
+
+ avc_bridgeco_fill_unit_addr(addr, AVC_BRIDGECO_PLUG_DIR_OUT,
+ AVC_BRIDGECO_PLUG_UNIT_ISOC, 0);
+ err = avc_bridgeco_get_plug_type(bebob->unit, addr, &type);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get type for isoc out plug 0: %d\n", err);
+ goto end;
+ } else if (type != AVC_BRIDGECO_PLUG_TYPE_ISOC) {
+ err = -ENOSYS;
+ goto end;
+ }
+ err = fill_stream_formations(bebob, AVC_BRIDGECO_PLUG_DIR_OUT, 0);
+ if (err < 0)
+ goto end;
+
+ /* count external input plugs for MIDI */
+ bebob->midi_input_ports = 0;
+ for (i = 0; i < plugs[2]; i++) {
+ avc_bridgeco_fill_unit_addr(addr, AVC_BRIDGECO_PLUG_DIR_IN,
+ AVC_BRIDGECO_PLUG_UNIT_EXT, i);
+ err = avc_bridgeco_get_plug_type(bebob->unit, addr, &type);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get type for external in plug %d: %d\n",
+ i, err);
+ goto end;
+ } else if (type == AVC_BRIDGECO_PLUG_TYPE_MIDI) {
+ bebob->midi_input_ports++;
+ }
+ }
+
+ /* count external output plugs for MIDI */
+ bebob->midi_output_ports = 0;
+ for (i = 0; i < plugs[3]; i++) {
+ avc_bridgeco_fill_unit_addr(addr, AVC_BRIDGECO_PLUG_DIR_OUT,
+ AVC_BRIDGECO_PLUG_UNIT_EXT, i);
+ err = avc_bridgeco_get_plug_type(bebob->unit, addr, &type);
+ if (err < 0) {
+ dev_err(&bebob->unit->device,
+ "fail to get type for external out plug %d: %d\n",
+ i, err);
+ goto end;
+ } else if (type == AVC_BRIDGECO_PLUG_TYPE_MIDI) {
+ bebob->midi_output_ports++;
+ }
+ }
+
+ /* for check source of clock later */
+ if (!clk_spec)
+ err = seek_msu_sync_input_plug(bebob);
+end:
+ return err;
+}
+
+void snd_bebob_stream_lock_changed(struct snd_bebob *bebob)
+{
+ bebob->dev_lock_changed = true;
+ wake_up(&bebob->hwdep_wait);
+}
+
+int snd_bebob_stream_lock_try(struct snd_bebob *bebob)
+{
+ int err;
+
+ spin_lock_irq(&bebob->lock);
+
+ /* user land lock this */
+ if (bebob->dev_lock_count < 0) {
+ err = -EBUSY;
+ goto end;
+ }
+
+ /* this is the first time */
+ if (bebob->dev_lock_count++ == 0)
+ snd_bebob_stream_lock_changed(bebob);
+ err = 0;
+end:
+ spin_unlock_irq(&bebob->lock);
+ return err;
+}
+
+void snd_bebob_stream_lock_release(struct snd_bebob *bebob)
+{
+ spin_lock_irq(&bebob->lock);
+
+ if (WARN_ON(bebob->dev_lock_count <= 0))
+ goto end;
+ if (--bebob->dev_lock_count == 0)
+ snd_bebob_stream_lock_changed(bebob);
+end:
+ spin_unlock_irq(&bebob->lock);
+}
diff --git a/sound/firewire/bebob/bebob_terratec.c b/sound/firewire/bebob/bebob_terratec.c
new file mode 100644
index 000000000000..eef8ea7d9b97
--- /dev/null
+++ b/sound/firewire/bebob/bebob_terratec.c
@@ -0,0 +1,68 @@
+/*
+ * bebob_terratec.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./bebob.h"
+
+static char *const phase88_rack_clk_src_labels[] = {
+ SND_BEBOB_CLOCK_INTERNAL, "Digital In", "Word Clock"
+};
+static int
+phase88_rack_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
+{
+ unsigned int enable_ext, enable_word;
+ int err;
+
+ err = avc_audio_get_selector(bebob->unit, 0, 0, &enable_ext);
+ if (err < 0)
+ goto end;
+ err = avc_audio_get_selector(bebob->unit, 0, 0, &enable_word);
+ if (err < 0)
+ goto end;
+
+ *id = (enable_ext & 0x01) | ((enable_word & 0x01) << 1);
+end:
+ return err;
+}
+
+static char *const phase24_series_clk_src_labels[] = {
+ SND_BEBOB_CLOCK_INTERNAL, "Digital In"
+};
+static int
+phase24_series_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
+{
+ return avc_audio_get_selector(bebob->unit, 0, 4, id);
+}
+
+static struct snd_bebob_rate_spec phase_series_rate_spec = {
+ .get = &snd_bebob_stream_get_rate,
+ .set = &snd_bebob_stream_set_rate,
+};
+
+/* PHASE 88 Rack FW */
+static struct snd_bebob_clock_spec phase88_rack_clk = {
+ .num = ARRAY_SIZE(phase88_rack_clk_src_labels),
+ .labels = phase88_rack_clk_src_labels,
+ .get = &phase88_rack_clk_src_get,
+};
+struct snd_bebob_spec phase88_rack_spec = {
+ .clock = &phase88_rack_clk,
+ .rate = &phase_series_rate_spec,
+ .meter = NULL
+};
+
+/* 'PHASE 24 FW' and 'PHASE X24 FW' */
+static struct snd_bebob_clock_spec phase24_series_clk = {
+ .num = ARRAY_SIZE(phase24_series_clk_src_labels),
+ .labels = phase24_series_clk_src_labels,
+ .get = &phase24_series_clk_src_get,
+};
+struct snd_bebob_spec phase24_series_spec = {
+ .clock = &phase24_series_clk,
+ .rate = &phase_series_rate_spec,
+ .meter = NULL
+};
diff --git a/sound/firewire/bebob/bebob_yamaha.c b/sound/firewire/bebob/bebob_yamaha.c
new file mode 100644
index 000000000000..9b7e798180ff
--- /dev/null
+++ b/sound/firewire/bebob/bebob_yamaha.c
@@ -0,0 +1,50 @@
+/*
+ * bebob_yamaha.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./bebob.h"
+
+/*
+ * NOTE:
+ * Yamaha GO44 is not designed to be used as stand-alone mixer. So any streams
+ * must be accompanied. If changing the state, a LED on the device starts to
+ * blink and its sync status is false. In this state, the device sounds nothing
+ * even if streaming. To start streaming at the current sampling rate is only
+ * way to revocer this state. GO46 is better for stand-alone mixer.
+ *
+ * Both of them have a capability to change its sampling rate up to 192.0kHz.
+ * At 192.0kHz, the device reports 4 PCM-in, 1 MIDI-in, 6 PCM-out, 1 MIDI-out.
+ * But Yamaha's driver reduce 2 PCM-in, 1 MIDI-in, 2 PCM-out, 1 MIDI-out to use
+ * 'Extended Stream Format Information Command - Single Request' in 'Additional
+ * AVC commands' defined by BridgeCo.
+ * This ALSA driver don't do this because a bit tiresome. Then isochronous
+ * streaming with many asynchronous transactions brings sounds with noises.
+ * Unfortunately current 'ffado-mixer' generated many asynchronous transaction
+ * to observe device's state, mainly check cmp connection and signal format. I
+ * reccomend users to close ffado-mixer at 192.0kHz if mixer is needless.
+ */
+
+static char *const clk_src_labels[] = {SND_BEBOB_CLOCK_INTERNAL, "SPDIF"};
+static int
+clk_src_get(struct snd_bebob *bebob, unsigned int *id)
+{
+ return avc_audio_get_selector(bebob->unit, 0, 4, id);
+}
+static struct snd_bebob_clock_spec clock_spec = {
+ .num = ARRAY_SIZE(clk_src_labels),
+ .labels = clk_src_labels,
+ .get = &clk_src_get,
+};
+static struct snd_bebob_rate_spec rate_spec = {
+ .get = &snd_bebob_stream_get_rate,
+ .set = &snd_bebob_stream_set_rate,
+};
+struct snd_bebob_spec yamaha_go_spec = {
+ .clock = &clock_spec,
+ .rate = &rate_spec,
+ .meter = NULL
+};
diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c
index efdbf585e404..ba8df5a1be39 100644
--- a/sound/firewire/cmp.c
+++ b/sound/firewire/cmp.c
@@ -14,18 +14,28 @@
#include "iso-resources.h"
#include "cmp.h"
-#define IMPR_SPEED_MASK 0xc0000000
-#define IMPR_SPEED_SHIFT 30
-#define IMPR_XSPEED_MASK 0x00000060
-#define IMPR_XSPEED_SHIFT 5
-#define IMPR_PLUGS_MASK 0x0000001f
-
-#define IPCR_ONLINE 0x80000000
-#define IPCR_BCAST_CONN 0x40000000
-#define IPCR_P2P_CONN_MASK 0x3f000000
-#define IPCR_P2P_CONN_SHIFT 24
-#define IPCR_CHANNEL_MASK 0x003f0000
-#define IPCR_CHANNEL_SHIFT 16
+/* MPR common fields */
+#define MPR_SPEED_MASK 0xc0000000
+#define MPR_SPEED_SHIFT 30
+#define MPR_XSPEED_MASK 0x00000060
+#define MPR_XSPEED_SHIFT 5
+#define MPR_PLUGS_MASK 0x0000001f
+
+/* PCR common fields */
+#define PCR_ONLINE 0x80000000
+#define PCR_BCAST_CONN 0x40000000
+#define PCR_P2P_CONN_MASK 0x3f000000
+#define PCR_P2P_CONN_SHIFT 24
+#define PCR_CHANNEL_MASK 0x003f0000
+#define PCR_CHANNEL_SHIFT 16
+
+/* oPCR specific fields */
+#define OPCR_XSPEED_MASK 0x00C00000
+#define OPCR_XSPEED_SHIFT 22
+#define OPCR_SPEED_MASK 0x0000C000
+#define OPCR_SPEED_SHIFT 14
+#define OPCR_OVERHEAD_ID_MASK 0x00003C00
+#define OPCR_OVERHEAD_ID_SHIFT 10
enum bus_reset_handling {
ABORT_ON_BUS_RESET,
@@ -39,10 +49,27 @@ void cmp_error(struct cmp_connection *c, const char *fmt, ...)
va_start(va, fmt);
dev_err(&c->resources.unit->device, "%cPCR%u: %pV",
- 'i', c->pcr_index, &(struct va_format){ fmt, &va });
+ (c->direction == CMP_INPUT) ? 'i' : 'o',
+ c->pcr_index, &(struct va_format){ fmt, &va });
va_end(va);
}
+static u64 mpr_address(struct cmp_connection *c)
+{
+ if (c->direction == CMP_INPUT)
+ return CSR_REGISTER_BASE + CSR_IMPR;
+ else
+ return CSR_REGISTER_BASE + CSR_OMPR;
+}
+
+static u64 pcr_address(struct cmp_connection *c)
+{
+ if (c->direction == CMP_INPUT)
+ return CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index);
+ else
+ return CSR_REGISTER_BASE + CSR_OPCR(c->pcr_index);
+}
+
static int pcr_modify(struct cmp_connection *c,
__be32 (*modify)(struct cmp_connection *c, __be32 old),
int (*check)(struct cmp_connection *c, __be32 pcr),
@@ -58,8 +85,7 @@ static int pcr_modify(struct cmp_connection *c,
err = snd_fw_transaction(
c->resources.unit, TCODE_LOCK_COMPARE_SWAP,
- CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index),
- buffer, 8,
+ pcr_address(c), buffer, 8,
FW_FIXED_GENERATION | c->resources.generation);
if (err < 0) {
@@ -88,24 +114,25 @@ static int pcr_modify(struct cmp_connection *c,
* cmp_connection_init - initializes a connection manager
* @c: the connection manager to initialize
* @unit: a unit of the target device
- * @ipcr_index: the index of the iPCR on the target device
+ * @pcr_index: the index of the iPCR/oPCR on the target device
*/
int cmp_connection_init(struct cmp_connection *c,
struct fw_unit *unit,
- unsigned int ipcr_index)
+ enum cmp_direction direction,
+ unsigned int pcr_index)
{
- __be32 impr_be;
- u32 impr;
+ __be32 mpr_be;
+ u32 mpr;
int err;
+ c->direction = direction;
err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
- CSR_REGISTER_BASE + CSR_IMPR,
- &impr_be, 4, 0);
+ mpr_address(c), &mpr_be, 4, 0);
if (err < 0)
return err;
- impr = be32_to_cpu(impr_be);
+ mpr = be32_to_cpu(mpr_be);
- if (ipcr_index >= (impr & IMPR_PLUGS_MASK))
+ if (pcr_index >= (mpr & MPR_PLUGS_MASK))
return -EINVAL;
err = fw_iso_resources_init(&c->resources, unit);
@@ -115,16 +142,36 @@ int cmp_connection_init(struct cmp_connection *c,
c->connected = false;
mutex_init(&c->mutex);
c->last_pcr_value = cpu_to_be32(0x80000000);
- c->pcr_index = ipcr_index;
- c->max_speed = (impr & IMPR_SPEED_MASK) >> IMPR_SPEED_SHIFT;
+ c->pcr_index = pcr_index;
+ c->max_speed = (mpr & MPR_SPEED_MASK) >> MPR_SPEED_SHIFT;
if (c->max_speed == SCODE_BETA)
- c->max_speed += (impr & IMPR_XSPEED_MASK) >> IMPR_XSPEED_SHIFT;
+ c->max_speed += (mpr & MPR_XSPEED_MASK) >> MPR_XSPEED_SHIFT;
return 0;
}
EXPORT_SYMBOL(cmp_connection_init);
/**
+ * cmp_connection_check_used - check connection is already esablished or not
+ * @c: the connection manager to be checked
+ */
+int cmp_connection_check_used(struct cmp_connection *c, bool *used)
+{
+ __be32 pcr;
+ int err;
+
+ err = snd_fw_transaction(
+ c->resources.unit, TCODE_READ_QUADLET_REQUEST,
+ pcr_address(c), &pcr, 4, 0);
+ if (err >= 0)
+ *used = !!(pcr & cpu_to_be32(PCR_BCAST_CONN |
+ PCR_P2P_CONN_MASK));
+
+ return err;
+}
+EXPORT_SYMBOL(cmp_connection_check_used);
+
+/**
* cmp_connection_destroy - free connection manager resources
* @c: the connection manager
*/
@@ -139,23 +186,70 @@ EXPORT_SYMBOL(cmp_connection_destroy);
static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr)
{
- ipcr &= ~cpu_to_be32(IPCR_BCAST_CONN |
- IPCR_P2P_CONN_MASK |
- IPCR_CHANNEL_MASK);
- ipcr |= cpu_to_be32(1 << IPCR_P2P_CONN_SHIFT);
- ipcr |= cpu_to_be32(c->resources.channel << IPCR_CHANNEL_SHIFT);
+ ipcr &= ~cpu_to_be32(PCR_BCAST_CONN |
+ PCR_P2P_CONN_MASK |
+ PCR_CHANNEL_MASK);
+ ipcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
+ ipcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
return ipcr;
}
-static int ipcr_set_check(struct cmp_connection *c, __be32 ipcr)
+static int get_overhead_id(struct cmp_connection *c)
{
- if (ipcr & cpu_to_be32(IPCR_BCAST_CONN |
- IPCR_P2P_CONN_MASK)) {
+ int id;
+
+ /*
+ * apply "oPCR overhead ID encoding"
+ * the encoding table can convert up to 512.
+ * here the value over 512 is converted as the same way as 512.
+ */
+ for (id = 1; id < 16; id++) {
+ if (c->resources.bandwidth_overhead < (id << 5))
+ break;
+ }
+ if (id == 16)
+ id = 0;
+
+ return id;
+}
+
+static __be32 opcr_set_modify(struct cmp_connection *c, __be32 opcr)
+{
+ unsigned int spd, xspd;
+
+ /* generate speed and extended speed field value */
+ if (c->speed > SCODE_400) {
+ spd = SCODE_800;
+ xspd = c->speed - SCODE_800;
+ } else {
+ spd = c->speed;
+ xspd = 0;
+ }
+
+ opcr &= ~cpu_to_be32(PCR_BCAST_CONN |
+ PCR_P2P_CONN_MASK |
+ OPCR_XSPEED_MASK |
+ PCR_CHANNEL_MASK |
+ OPCR_SPEED_MASK |
+ OPCR_OVERHEAD_ID_MASK);
+ opcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
+ opcr |= cpu_to_be32(xspd << OPCR_XSPEED_SHIFT);
+ opcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
+ opcr |= cpu_to_be32(spd << OPCR_SPEED_SHIFT);
+ opcr |= cpu_to_be32(get_overhead_id(c) << OPCR_OVERHEAD_ID_SHIFT);
+
+ return opcr;
+}
+
+static int pcr_set_check(struct cmp_connection *c, __be32 pcr)
+{
+ if (pcr & cpu_to_be32(PCR_BCAST_CONN |
+ PCR_P2P_CONN_MASK)) {
cmp_error(c, "plug is already in use\n");
return -EBUSY;
}
- if (!(ipcr & cpu_to_be32(IPCR_ONLINE))) {
+ if (!(pcr & cpu_to_be32(PCR_ONLINE))) {
cmp_error(c, "plug is not on-line\n");
return -ECONNREFUSED;
}
@@ -170,9 +264,9 @@ static int ipcr_set_check(struct cmp_connection *c, __be32 ipcr)
*
* This function establishes a point-to-point connection from the local
* computer to the target by allocating isochronous resources (channel and
- * bandwidth) and setting the target's input plug control register. When this
- * function succeeds, the caller is responsible for starting transmitting
- * packets.
+ * bandwidth) and setting the target's input/output plug control register.
+ * When this function succeeds, the caller is responsible for starting
+ * transmitting packets.
*/
int cmp_connection_establish(struct cmp_connection *c,
unsigned int max_payload_bytes)
@@ -193,8 +287,13 @@ retry_after_bus_reset:
if (err < 0)
goto err_mutex;
- err = pcr_modify(c, ipcr_set_modify, ipcr_set_check,
- ABORT_ON_BUS_RESET);
+ if (c->direction == CMP_OUTPUT)
+ err = pcr_modify(c, opcr_set_modify, pcr_set_check,
+ ABORT_ON_BUS_RESET);
+ else
+ err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
+ ABORT_ON_BUS_RESET);
+
if (err == -EAGAIN) {
fw_iso_resources_free(&c->resources);
goto retry_after_bus_reset;
@@ -221,8 +320,8 @@ EXPORT_SYMBOL(cmp_connection_establish);
* cmp_connection_update - update the connection after a bus reset
* @c: the connection manager
*
- * This function must be called from the driver's .update handler to reestablish
- * any connection that might have been active.
+ * This function must be called from the driver's .update handler to
+ * reestablish any connection that might have been active.
*
* Returns zero on success, or a negative error code. On an error, the
* connection is broken and the caller must stop transmitting iso packets.
@@ -242,8 +341,13 @@ int cmp_connection_update(struct cmp_connection *c)
if (err < 0)
goto err_unconnect;
- err = pcr_modify(c, ipcr_set_modify, ipcr_set_check,
- SUCCEED_ON_BUS_RESET);
+ if (c->direction == CMP_OUTPUT)
+ err = pcr_modify(c, opcr_set_modify, pcr_set_check,
+ SUCCEED_ON_BUS_RESET);
+ else
+ err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
+ SUCCEED_ON_BUS_RESET);
+
if (err < 0)
goto err_resources;
@@ -261,19 +365,18 @@ err_unconnect:
}
EXPORT_SYMBOL(cmp_connection_update);
-
-static __be32 ipcr_break_modify(struct cmp_connection *c, __be32 ipcr)
+static __be32 pcr_break_modify(struct cmp_connection *c, __be32 pcr)
{
- return ipcr & ~cpu_to_be32(IPCR_BCAST_CONN | IPCR_P2P_CONN_MASK);
+ return pcr & ~cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK);
}
/**
* cmp_connection_break - break the connection to the target
* @c: the connection manager
*
- * This function deactives the connection in the target's input plug control
- * register, and frees the isochronous resources of the connection. Before
- * calling this function, the caller should cease transmitting packets.
+ * This function deactives the connection in the target's input/output plug
+ * control register, and frees the isochronous resources of the connection.
+ * Before calling this function, the caller should cease transmitting packets.
*/
void cmp_connection_break(struct cmp_connection *c)
{
@@ -286,7 +389,7 @@ void cmp_connection_break(struct cmp_connection *c)
return;
}
- err = pcr_modify(c, ipcr_break_modify, NULL, SUCCEED_ON_BUS_RESET);
+ err = pcr_modify(c, pcr_break_modify, NULL, SUCCEED_ON_BUS_RESET);
if (err < 0)
cmp_error(c, "plug is still connected\n");
diff --git a/sound/firewire/cmp.h b/sound/firewire/cmp.h
index f47de08feb12..ebcb48484fca 100644
--- a/sound/firewire/cmp.h
+++ b/sound/firewire/cmp.h
@@ -7,12 +7,17 @@
struct fw_unit;
+enum cmp_direction {
+ CMP_INPUT = 0,
+ CMP_OUTPUT,
+};
+
/**
* struct cmp_connection - manages an isochronous connection to a device
* @speed: the connection's actual speed
*
- * This structure manages (using CMP) an isochronous stream from the local
- * computer to a device's input plug (iPCR).
+ * This structure manages (using CMP) an isochronous stream between the local
+ * computer and a device's input plug (iPCR) and output plug (oPCR).
*
* There is no corresponding oPCR created on the local computer, so it is not
* possible to overlay connections on top of this one.
@@ -26,11 +31,14 @@ struct cmp_connection {
__be32 last_pcr_value;
unsigned int pcr_index;
unsigned int max_speed;
+ enum cmp_direction direction;
};
int cmp_connection_init(struct cmp_connection *connection,
struct fw_unit *unit,
- unsigned int ipcr_index);
+ enum cmp_direction direction,
+ unsigned int pcr_index);
+int cmp_connection_check_used(struct cmp_connection *connection, bool *used);
void cmp_connection_destroy(struct cmp_connection *connection);
int cmp_connection_establish(struct cmp_connection *connection,
diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index 0c3948630cf7..a9a30c0161f1 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -51,7 +51,7 @@ struct dice {
wait_queue_head_t hwdep_wait;
u32 notification_bits;
struct fw_iso_resources resources;
- struct amdtp_out_stream stream;
+ struct amdtp_stream stream;
};
MODULE_DESCRIPTION("DICE driver");
@@ -420,22 +420,7 @@ static int dice_open(struct snd_pcm_substream *substream)
if (err < 0)
goto err_lock;
- err = snd_pcm_hw_constraint_step(runtime, 0,
- SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
- if (err < 0)
- goto err_lock;
- err = snd_pcm_hw_constraint_step(runtime, 0,
- SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
- if (err < 0)
- goto err_lock;
-
- err = snd_pcm_hw_constraint_minmax(runtime,
- SNDRV_PCM_HW_PARAM_PERIOD_TIME,
- 5000, UINT_MAX);
- if (err < 0)
- goto err_lock;
-
- err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ err = amdtp_stream_add_pcm_hw_constraints(&dice->stream, runtime);
if (err < 0)
goto err_lock;
@@ -460,17 +445,17 @@ static int dice_stream_start_packets(struct dice *dice)
{
int err;
- if (amdtp_out_stream_running(&dice->stream))
+ if (amdtp_stream_running(&dice->stream))
return 0;
- err = amdtp_out_stream_start(&dice->stream, dice->resources.channel,
- fw_parent_device(dice->unit)->max_speed);
+ err = amdtp_stream_start(&dice->stream, dice->resources.channel,
+ fw_parent_device(dice->unit)->max_speed);
if (err < 0)
return err;
err = dice_enable_set(dice);
if (err < 0) {
- amdtp_out_stream_stop(&dice->stream);
+ amdtp_stream_stop(&dice->stream);
return err;
}
@@ -484,7 +469,7 @@ static int dice_stream_start(struct dice *dice)
if (!dice->resources.allocated) {
err = fw_iso_resources_allocate(&dice->resources,
- amdtp_out_stream_get_max_payload(&dice->stream),
+ amdtp_stream_get_max_payload(&dice->stream),
fw_parent_device(dice->unit)->max_speed);
if (err < 0)
goto error;
@@ -516,9 +501,9 @@ error:
static void dice_stream_stop_packets(struct dice *dice)
{
- if (amdtp_out_stream_running(&dice->stream)) {
+ if (amdtp_stream_running(&dice->stream)) {
dice_enable_clear(dice);
- amdtp_out_stream_stop(&dice->stream);
+ amdtp_stream_stop(&dice->stream);
}
}
@@ -563,7 +548,7 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct dice *dice = substream->private_data;
- unsigned int rate_index, mode;
+ unsigned int rate_index, mode, rate, channels, i;
int err;
mutex_lock(&dice->mutex);
@@ -575,18 +560,39 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
if (err < 0)
return err;
- rate_index = rate_to_index(params_rate(hw_params));
+ rate = params_rate(hw_params);
+ rate_index = rate_to_index(rate);
err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT);
if (err < 0)
return err;
+ /*
+ * At rates above 96 kHz, pretend that the stream runs at half the
+ * actual sample rate with twice the number of channels; two samples
+ * of a channel are stored consecutively in the packet. Requires
+ * blocking mode and PCM buffer size should be aligned to SYT_INTERVAL.
+ */
+ channels = params_channels(hw_params);
+ if (rate_index > 4) {
+ if (channels > AMDTP_MAX_CHANNELS_FOR_PCM / 2) {
+ err = -ENOSYS;
+ return err;
+ }
+
+ for (i = 0; i < channels; i++) {
+ dice->stream.pcm_positions[i * 2] = i;
+ dice->stream.pcm_positions[i * 2 + 1] = i + channels;
+ }
+
+ rate /= 2;
+ channels *= 2;
+ }
+
mode = rate_index_to_mode(rate_index);
- amdtp_out_stream_set_parameters(&dice->stream,
- params_rate(hw_params),
- params_channels(hw_params),
- dice->rx_midi_ports[mode]);
- amdtp_out_stream_set_pcm_format(&dice->stream,
- params_format(hw_params));
+ amdtp_stream_set_parameters(&dice->stream, rate, channels,
+ dice->rx_midi_ports[mode]);
+ amdtp_stream_set_pcm_format(&dice->stream,
+ params_format(hw_params));
return 0;
}
@@ -609,7 +615,7 @@ static int dice_prepare(struct snd_pcm_substream *substream)
mutex_lock(&dice->mutex);
- if (amdtp_out_streaming_error(&dice->stream))
+ if (amdtp_streaming_error(&dice->stream))
dice_stream_stop_packets(dice);
err = dice_stream_start(dice);
@@ -620,7 +626,7 @@ static int dice_prepare(struct snd_pcm_substream *substream)
mutex_unlock(&dice->mutex);
- amdtp_out_stream_pcm_prepare(&dice->stream);
+ amdtp_stream_pcm_prepare(&dice->stream);
return 0;
}
@@ -640,7 +646,7 @@ static int dice_trigger(struct snd_pcm_substream *substream, int cmd)
default:
return -EINVAL;
}
- amdtp_out_stream_pcm_trigger(&dice->stream, pcm);
+ amdtp_stream_pcm_trigger(&dice->stream, pcm);
return 0;
}
@@ -649,7 +655,7 @@ static snd_pcm_uframes_t dice_pointer(struct snd_pcm_substream *substream)
{
struct dice *dice = substream->private_data;
- return amdtp_out_stream_pcm_pointer(&dice->stream);
+ return amdtp_stream_pcm_pointer(&dice->stream);
}
static int dice_create_pcm(struct dice *dice)
@@ -1104,7 +1110,7 @@ static void dice_card_free(struct snd_card *card)
{
struct dice *dice = card->private_data;
- amdtp_out_stream_destroy(&dice->stream);
+ amdtp_stream_destroy(&dice->stream);
fw_core_remove_address_handler(&dice->notification_handler);
mutex_destroy(&dice->mutex);
}
@@ -1360,8 +1366,8 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
goto err_owner;
dice->resources.channels_mask = 0x00000000ffffffffuLL;
- err = amdtp_out_stream_init(&dice->stream, unit,
- CIP_BLOCKING | CIP_HI_DUALWIRE);
+ err = amdtp_stream_init(&dice->stream, unit, AMDTP_OUT_STREAM,
+ CIP_BLOCKING);
if (err < 0)
goto err_resources;
@@ -1417,7 +1423,7 @@ static void dice_remove(struct fw_unit *unit)
{
struct dice *dice = dev_get_drvdata(&unit->device);
- amdtp_out_stream_pcm_abort(&dice->stream);
+ amdtp_stream_pcm_abort(&dice->stream);
snd_card_disconnect(dice->card);
@@ -1443,7 +1449,7 @@ static void dice_bus_reset(struct fw_unit *unit)
* to stop so that the application can restart them in an orderly
* manner.
*/
- amdtp_out_stream_pcm_abort(&dice->stream);
+ amdtp_stream_pcm_abort(&dice->stream);
mutex_lock(&dice->mutex);
diff --git a/sound/firewire/fcp.c b/sound/firewire/fcp.c
index 860c08073c59..d48e326567c8 100644
--- a/sound/firewire/fcp.c
+++ b/sound/firewire/fcp.c
@@ -10,12 +10,14 @@
#include <linux/firewire-constants.h>
#include <linux/list.h>
#include <linux/module.h>
+#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include "fcp.h"
#include "lib.h"
+#include "amdtp.h"
#define CTS_AVC 0x00
@@ -23,6 +25,158 @@
#define ERROR_DELAY_MS 5
#define FCP_TIMEOUT_MS 125
+int avc_general_set_sig_fmt(struct fw_unit *unit, unsigned int rate,
+ enum avc_general_plug_dir dir,
+ unsigned short pid)
+{
+ unsigned int sfc;
+ u8 *buf;
+ bool flag;
+ int err;
+
+ flag = false;
+ for (sfc = 0; sfc < CIP_SFC_COUNT; sfc++) {
+ if (amdtp_rate_table[sfc] == rate) {
+ flag = true;
+ break;
+ }
+ }
+ if (!flag)
+ return -EINVAL;
+
+ buf = kzalloc(8, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x00; /* AV/C CONTROL */
+ buf[1] = 0xff; /* UNIT */
+ if (dir == AVC_GENERAL_PLUG_DIR_IN)
+ buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */
+ else
+ buf[2] = 0x18; /* OUTPUT PLUG SIGNAL FORMAT */
+ buf[3] = 0xff & pid; /* plug id */
+ buf[4] = 0x90; /* EOH_1, Form_1, FMT. AM824 */
+ buf[5] = 0x07 & sfc; /* FDF-hi. AM824, frequency */
+ buf[6] = 0xff; /* FDF-mid. AM824, SYT hi (not used)*/
+ buf[7] = 0xff; /* FDF-low. AM824, SYT lo (not used) */
+
+ /* do transaction and check buf[1-5] are the same against command */
+ err = fcp_avc_transaction(unit, buf, 8, buf, 8,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5));
+ if (err >= 0 && err < 8)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ if (err < 0)
+ goto end;
+
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+EXPORT_SYMBOL(avc_general_set_sig_fmt);
+
+int avc_general_get_sig_fmt(struct fw_unit *unit, unsigned int *rate,
+ enum avc_general_plug_dir dir,
+ unsigned short pid)
+{
+ unsigned int sfc;
+ u8 *buf;
+ int err;
+
+ buf = kzalloc(8, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x01; /* AV/C STATUS */
+ buf[1] = 0xff; /* Unit */
+ if (dir == AVC_GENERAL_PLUG_DIR_IN)
+ buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */
+ else
+ buf[2] = 0x18; /* OUTPUT PLUG SIGNAL FORMAT */
+ buf[3] = 0xff & pid; /* plug id */
+ buf[4] = 0x90; /* EOH_1, Form_1, FMT. AM824 */
+ buf[5] = 0xff; /* FDF-hi. AM824, frequency */
+ buf[6] = 0xff; /* FDF-mid. AM824, SYT hi (not used) */
+ buf[7] = 0xff; /* FDF-low. AM824, SYT lo (not used) */
+
+ /* do transaction and check buf[1-4] are the same against command */
+ err = fcp_avc_transaction(unit, buf, 8, buf, 8,
+ BIT(1) | BIT(2) | BIT(3) | BIT(4));
+ if (err >= 0 && err < 8)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ if (err < 0)
+ goto end;
+
+ /* check sfc field and pick up rate */
+ sfc = 0x07 & buf[5];
+ if (sfc >= CIP_SFC_COUNT) {
+ err = -EAGAIN; /* also in transition */
+ goto end;
+ }
+
+ *rate = amdtp_rate_table[sfc];
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+EXPORT_SYMBOL(avc_general_get_sig_fmt);
+
+int avc_general_get_plug_info(struct fw_unit *unit, unsigned int subunit_type,
+ unsigned int subunit_id, unsigned int subfunction,
+ u8 info[AVC_PLUG_INFO_BUF_BYTES])
+{
+ u8 *buf;
+ int err;
+
+ /* extended subunit in spec.4.2 is not supported */
+ if ((subunit_type == 0x1E) || (subunit_id == 5))
+ return -EINVAL;
+
+ buf = kzalloc(8, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x01; /* AV/C STATUS */
+ /* UNIT or Subunit, Functionblock */
+ buf[1] = ((subunit_type & 0x1f) << 3) | (subunit_id & 0x7);
+ buf[2] = 0x02; /* PLUG INFO */
+ buf[3] = 0xff & subfunction;
+
+ err = fcp_avc_transaction(unit, buf, 8, buf, 8, BIT(1) | BIT(2));
+ if (err >= 0 && err < 8)
+ err = -EIO;
+ else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
+ err = -ENOSYS;
+ else if (buf[0] == 0x0a) /* REJECTED */
+ err = -EINVAL;
+ else if (buf[0] == 0x0b) /* IN TRANSITION */
+ err = -EAGAIN;
+ if (err < 0)
+ goto end;
+
+ info[0] = buf[4];
+ info[1] = buf[5];
+ info[2] = buf[6];
+ info[3] = buf[7];
+
+ err = 0;
+end:
+ kfree(buf);
+ return err;
+}
+EXPORT_SYMBOL(avc_general_get_plug_info);
+
static DEFINE_SPINLOCK(transactions_lock);
static LIST_HEAD(transactions);
@@ -30,6 +184,7 @@ enum fcp_state {
STATE_PENDING,
STATE_BUS_RESET,
STATE_COMPLETE,
+ STATE_DEFERRED,
};
struct fcp_transaction {
@@ -40,6 +195,7 @@ struct fcp_transaction {
unsigned int response_match_bytes;
enum fcp_state state;
wait_queue_head_t wait;
+ bool deferrable;
};
/**
@@ -81,6 +237,9 @@ int fcp_avc_transaction(struct fw_unit *unit,
t.state = STATE_PENDING;
init_waitqueue_head(&t.wait);
+ if (*(const u8 *)command == 0x00 || *(const u8 *)command == 0x03)
+ t.deferrable = true;
+
spin_lock_irq(&transactions_lock);
list_add_tail(&t.list, &transactions);
spin_unlock_irq(&transactions_lock);
@@ -93,11 +252,21 @@ int fcp_avc_transaction(struct fw_unit *unit,
(void *)command, command_size, 0);
if (ret < 0)
break;
-
+deferred:
wait_event_timeout(t.wait, t.state != STATE_PENDING,
msecs_to_jiffies(FCP_TIMEOUT_MS));
- if (t.state == STATE_COMPLETE) {
+ if (t.state == STATE_DEFERRED) {
+ /*
+ * 'AV/C General Specification' define no time limit
+ * on command completion once an INTERIM response has
+ * been sent. but we promise to finish this function
+ * for a caller. Here we use FCP_TIMEOUT_MS for next
+ * interval. This is not in the specification.
+ */
+ t.state = STATE_PENDING;
+ goto deferred;
+ } else if (t.state == STATE_COMPLETE) {
ret = t.response_size;
break;
} else if (t.state == STATE_BUS_RESET) {
@@ -132,7 +301,8 @@ void fcp_bus_reset(struct fw_unit *unit)
spin_lock_irq(&transactions_lock);
list_for_each_entry(t, &transactions, list) {
if (t->unit == unit &&
- t->state == STATE_PENDING) {
+ (t->state == STATE_PENDING ||
+ t->state == STATE_DEFERRED)) {
t->state = STATE_BUS_RESET;
wake_up(&t->wait);
}
@@ -186,10 +356,15 @@ static void fcp_response(struct fw_card *card, struct fw_request *request,
if (t->state == STATE_PENDING &&
is_matching_response(t, data, length)) {
- t->state = STATE_COMPLETE;
- t->response_size = min((unsigned int)length,
- t->response_size);
- memcpy(t->response_buffer, data, t->response_size);
+ if (t->deferrable && *(const u8 *)data == 0x0f) {
+ t->state = STATE_DEFERRED;
+ } else {
+ t->state = STATE_COMPLETE;
+ t->response_size = min_t(unsigned int, length,
+ t->response_size);
+ memcpy(t->response_buffer, data,
+ t->response_size);
+ }
wake_up(&t->wait);
}
}
diff --git a/sound/firewire/fcp.h b/sound/firewire/fcp.h
index 86595688bd91..63ae4f7ce3af 100644
--- a/sound/firewire/fcp.h
+++ b/sound/firewire/fcp.h
@@ -1,8 +1,29 @@
#ifndef SOUND_FIREWIRE_FCP_H_INCLUDED
#define SOUND_FIREWIRE_FCP_H_INCLUDED
+#define AVC_PLUG_INFO_BUF_BYTES 4
+
struct fw_unit;
+/*
+ * AV/C Digital Interface Command Set General Specification 4.2
+ * (Sep 2004, 1394TA)
+ */
+enum avc_general_plug_dir {
+ AVC_GENERAL_PLUG_DIR_IN = 0,
+ AVC_GENERAL_PLUG_DIR_OUT = 1,
+ AVC_GENERAL_PLUG_DIR_COUNT
+};
+int avc_general_set_sig_fmt(struct fw_unit *unit, unsigned int rate,
+ enum avc_general_plug_dir dir,
+ unsigned short plug);
+int avc_general_get_sig_fmt(struct fw_unit *unit, unsigned int *rate,
+ enum avc_general_plug_dir dir,
+ unsigned short plug);
+int avc_general_get_plug_info(struct fw_unit *unit, unsigned int subunit_type,
+ unsigned int subunit_id, unsigned int subfunction,
+ u8 info[AVC_PLUG_INFO_BUF_BYTES]);
+
int fcp_avc_transaction(struct fw_unit *unit,
const void *command, unsigned int command_size,
void *response, unsigned int response_size,
diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile
new file mode 100644
index 000000000000..0c7440826db8
--- /dev/null
+++ b/sound/firewire/fireworks/Makefile
@@ -0,0 +1,4 @@
+snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \
+ fireworks_stream.o fireworks_proc.o fireworks_midi.o \
+ fireworks_pcm.o fireworks_hwdep.o fireworks.o
+obj-m += snd-fireworks.o
diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
new file mode 100644
index 000000000000..a354d26afe9e
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks.c
@@ -0,0 +1,357 @@
+/*
+ * fireworks.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * Fireworks is a board module which Echo Audio produced. This module consists
+ * of three chipsets:
+ * - Communication chipset for IEEE1394 PHY/Link and IEC 61883-1/6
+ * - DSP or/and FPGA for signal processing
+ * - Flash Memory to store firmwares
+ */
+
+#include "fireworks.h"
+
+MODULE_DESCRIPTION("Echo Fireworks driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+unsigned int snd_efw_resp_buf_size = 1024;
+bool snd_efw_resp_buf_debug = false;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "card index");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "enable Fireworks sound card");
+module_param_named(resp_buf_size, snd_efw_resp_buf_size, uint, 0444);
+MODULE_PARM_DESC(resp_buf_size,
+ "response buffer size (max 4096, default 1024)");
+module_param_named(resp_buf_debug, snd_efw_resp_buf_debug, bool, 0444);
+MODULE_PARM_DESC(resp_buf_debug, "store all responses to buffer");
+
+static DEFINE_MUTEX(devices_mutex);
+static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
+
+#define VENDOR_LOUD 0x000ff2
+#define MODEL_MACKIE_400F 0x00400f
+#define MODEL_MACKIE_1200F 0x01200f
+
+#define VENDOR_ECHO 0x001486
+#define MODEL_ECHO_AUDIOFIRE_12 0x00af12
+#define MODEL_ECHO_AUDIOFIRE_12HD 0x0af12d
+#define MODEL_ECHO_AUDIOFIRE_12_APPLE 0x0af12a
+/* This is applied for AudioFire8 (until 2009 July) */
+#define MODEL_ECHO_AUDIOFIRE_8 0x000af8
+#define MODEL_ECHO_AUDIOFIRE_2 0x000af2
+#define MODEL_ECHO_AUDIOFIRE_4 0x000af4
+/* AudioFire9 is applied for AudioFire8(since 2009 July) and AudioFirePre8 */
+#define MODEL_ECHO_AUDIOFIRE_9 0x000af9
+/* unknown as product */
+#define MODEL_ECHO_FIREWORKS_8 0x0000f8
+#define MODEL_ECHO_FIREWORKS_HDMI 0x00afd1
+
+#define VENDOR_GIBSON 0x00075b
+/* for Robot Interface Pack of Dark Fire, Dusk Tiger, Les Paul Standard 2010 */
+#define MODEL_GIBSON_RIP 0x00afb2
+/* unknown as product */
+#define MODEL_GIBSON_GOLDTOP 0x00afb9
+
+/* part of hardware capability flags */
+#define FLAG_RESP_ADDR_CHANGABLE 0
+
+static int
+get_hardware_info(struct snd_efw *efw)
+{
+ struct fw_device *fw_dev = fw_parent_device(efw->unit);
+ struct snd_efw_hwinfo *hwinfo;
+ char version[12] = {0};
+ int err;
+
+ hwinfo = kzalloc(sizeof(struct snd_efw_hwinfo), GFP_KERNEL);
+ if (hwinfo == NULL)
+ return -ENOMEM;
+
+ err = snd_efw_command_get_hwinfo(efw, hwinfo);
+ if (err < 0)
+ goto end;
+
+ /* firmware version for communication chipset */
+ snprintf(version, sizeof(version), "%u.%u",
+ (hwinfo->arm_version >> 24) & 0xff,
+ (hwinfo->arm_version >> 16) & 0xff);
+ if (err < 0)
+ goto end;
+ efw->firmware_version = hwinfo->arm_version;
+
+ strcpy(efw->card->driver, "Fireworks");
+ strcpy(efw->card->shortname, hwinfo->model_name);
+ strcpy(efw->card->mixername, hwinfo->model_name);
+ snprintf(efw->card->longname, sizeof(efw->card->longname),
+ "%s %s v%s, GUID %08x%08x at %s, S%d",
+ hwinfo->vendor_name, hwinfo->model_name, version,
+ hwinfo->guid_hi, hwinfo->guid_lo,
+ dev_name(&efw->unit->device), 100 << fw_dev->max_speed);
+ if (err < 0)
+ goto end;
+
+ if (hwinfo->flags & BIT(FLAG_RESP_ADDR_CHANGABLE))
+ efw->resp_addr_changable = true;
+
+ efw->supported_sampling_rate = 0;
+ if ((hwinfo->min_sample_rate <= 22050)
+ && (22050 <= hwinfo->max_sample_rate))
+ efw->supported_sampling_rate |= SNDRV_PCM_RATE_22050;
+ if ((hwinfo->min_sample_rate <= 32000)
+ && (32000 <= hwinfo->max_sample_rate))
+ efw->supported_sampling_rate |= SNDRV_PCM_RATE_32000;
+ if ((hwinfo->min_sample_rate <= 44100)
+ && (44100 <= hwinfo->max_sample_rate))
+ efw->supported_sampling_rate |= SNDRV_PCM_RATE_44100;
+ if ((hwinfo->min_sample_rate <= 48000)
+ && (48000 <= hwinfo->max_sample_rate))
+ efw->supported_sampling_rate |= SNDRV_PCM_RATE_48000;
+ if ((hwinfo->min_sample_rate <= 88200)
+ && (88200 <= hwinfo->max_sample_rate))
+ efw->supported_sampling_rate |= SNDRV_PCM_RATE_88200;
+ if ((hwinfo->min_sample_rate <= 96000)
+ && (96000 <= hwinfo->max_sample_rate))
+ efw->supported_sampling_rate |= SNDRV_PCM_RATE_96000;
+ if ((hwinfo->min_sample_rate <= 176400)
+ && (176400 <= hwinfo->max_sample_rate))
+ efw->supported_sampling_rate |= SNDRV_PCM_RATE_176400;
+ if ((hwinfo->min_sample_rate <= 192000)
+ && (192000 <= hwinfo->max_sample_rate))
+ efw->supported_sampling_rate |= SNDRV_PCM_RATE_192000;
+
+ /* the number of MIDI ports, not of MIDI conformant data channels */
+ if (hwinfo->midi_out_ports > SND_EFW_MAX_MIDI_OUT_PORTS ||
+ hwinfo->midi_in_ports > SND_EFW_MAX_MIDI_IN_PORTS) {
+ err = -EIO;
+ goto end;
+ }
+ efw->midi_out_ports = hwinfo->midi_out_ports;
+ efw->midi_in_ports = hwinfo->midi_in_ports;
+
+ if (hwinfo->amdtp_tx_pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_tx_pcm_channels_2x > AMDTP_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_tx_pcm_channels_4x > AMDTP_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_rx_pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_rx_pcm_channels_2x > AMDTP_MAX_CHANNELS_FOR_PCM ||
+ hwinfo->amdtp_rx_pcm_channels_4x > AMDTP_MAX_CHANNELS_FOR_PCM) {
+ err = -ENOSYS;
+ goto end;
+ }
+ efw->pcm_capture_channels[0] = hwinfo->amdtp_tx_pcm_channels;
+ efw->pcm_capture_channels[1] = hwinfo->amdtp_tx_pcm_channels_2x;
+ efw->pcm_capture_channels[2] = hwinfo->amdtp_tx_pcm_channels_4x;
+ efw->pcm_playback_channels[0] = hwinfo->amdtp_rx_pcm_channels;
+ efw->pcm_playback_channels[1] = hwinfo->amdtp_rx_pcm_channels_2x;
+ efw->pcm_playback_channels[2] = hwinfo->amdtp_rx_pcm_channels_4x;
+
+ /* Hardware metering. */
+ if (hwinfo->phys_in_grp_count > HWINFO_MAX_CAPS_GROUPS ||
+ hwinfo->phys_out_grp_count > HWINFO_MAX_CAPS_GROUPS) {
+ return -EIO;
+ goto end;
+ }
+ efw->phys_in = hwinfo->phys_in;
+ efw->phys_out = hwinfo->phys_out;
+ efw->phys_in_grp_count = hwinfo->phys_in_grp_count;
+ efw->phys_out_grp_count = hwinfo->phys_out_grp_count;
+ memcpy(&efw->phys_in_grps, hwinfo->phys_in_grps,
+ sizeof(struct snd_efw_phys_grp) * hwinfo->phys_in_grp_count);
+ memcpy(&efw->phys_out_grps, hwinfo->phys_out_grps,
+ sizeof(struct snd_efw_phys_grp) * hwinfo->phys_out_grp_count);
+end:
+ kfree(hwinfo);
+ return err;
+}
+
+static void
+efw_card_free(struct snd_card *card)
+{
+ struct snd_efw *efw = card->private_data;
+
+ if (efw->card_index >= 0) {
+ mutex_lock(&devices_mutex);
+ clear_bit(efw->card_index, devices_used);
+ mutex_unlock(&devices_mutex);
+ }
+
+ mutex_destroy(&efw->mutex);
+ kfree(efw->resp_buf);
+}
+
+static int
+efw_probe(struct fw_unit *unit,
+ const struct ieee1394_device_id *entry)
+{
+ struct snd_card *card;
+ struct snd_efw *efw;
+ int card_index, err;
+
+ mutex_lock(&devices_mutex);
+
+ /* check registered cards */
+ for (card_index = 0; card_index < SNDRV_CARDS; ++card_index) {
+ if (!test_bit(card_index, devices_used) && enable[card_index])
+ break;
+ }
+ if (card_index >= SNDRV_CARDS) {
+ err = -ENOENT;
+ goto end;
+ }
+
+ err = snd_card_new(&unit->device, index[card_index], id[card_index],
+ THIS_MODULE, sizeof(struct snd_efw), &card);
+ if (err < 0)
+ goto end;
+ efw = card->private_data;
+ efw->card_index = card_index;
+ set_bit(card_index, devices_used);
+ card->private_free = efw_card_free;
+
+ efw->card = card;
+ efw->unit = unit;
+ mutex_init(&efw->mutex);
+ spin_lock_init(&efw->lock);
+ init_waitqueue_head(&efw->hwdep_wait);
+
+ /* prepare response buffer */
+ snd_efw_resp_buf_size = clamp(snd_efw_resp_buf_size,
+ SND_EFW_RESPONSE_MAXIMUM_BYTES, 4096U);
+ efw->resp_buf = kzalloc(snd_efw_resp_buf_size, GFP_KERNEL);
+ if (efw->resp_buf == NULL) {
+ err = -ENOMEM;
+ goto error;
+ }
+ efw->pull_ptr = efw->push_ptr = efw->resp_buf;
+ snd_efw_transaction_add_instance(efw);
+
+ err = get_hardware_info(efw);
+ if (err < 0)
+ goto error;
+ if (entry->model_id == MODEL_ECHO_AUDIOFIRE_9)
+ efw->is_af9 = true;
+
+ snd_efw_proc_init(efw);
+
+ if (efw->midi_out_ports || efw->midi_in_ports) {
+ err = snd_efw_create_midi_devices(efw);
+ if (err < 0)
+ goto error;
+ }
+
+ err = snd_efw_create_pcm_devices(efw);
+ if (err < 0)
+ goto error;
+
+ err = snd_efw_create_hwdep_device(efw);
+ if (err < 0)
+ goto error;
+
+ err = snd_efw_stream_init_duplex(efw);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(card);
+ if (err < 0) {
+ snd_efw_stream_destroy_duplex(efw);
+ goto error;
+ }
+
+ dev_set_drvdata(&unit->device, efw);
+end:
+ mutex_unlock(&devices_mutex);
+ return err;
+error:
+ snd_efw_transaction_remove_instance(efw);
+ mutex_unlock(&devices_mutex);
+ snd_card_free(card);
+ return err;
+}
+
+static void efw_update(struct fw_unit *unit)
+{
+ struct snd_efw *efw = dev_get_drvdata(&unit->device);
+
+ snd_efw_transaction_bus_reset(efw->unit);
+ snd_efw_stream_update_duplex(efw);
+}
+
+static void efw_remove(struct fw_unit *unit)
+{
+ struct snd_efw *efw = dev_get_drvdata(&unit->device);
+
+ snd_efw_stream_destroy_duplex(efw);
+ snd_efw_transaction_remove_instance(efw);
+
+ snd_card_disconnect(efw->card);
+ snd_card_free_when_closed(efw->card);
+}
+
+static const struct ieee1394_device_id efw_id_table[] = {
+ SND_EFW_DEV_ENTRY(VENDOR_LOUD, MODEL_MACKIE_400F),
+ SND_EFW_DEV_ENTRY(VENDOR_LOUD, MODEL_MACKIE_1200F),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_8),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_12),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_12HD),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_12_APPLE),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_2),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_4),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_9),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_FIREWORKS_8),
+ SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_FIREWORKS_HDMI),
+ SND_EFW_DEV_ENTRY(VENDOR_GIBSON, MODEL_GIBSON_RIP),
+ SND_EFW_DEV_ENTRY(VENDOR_GIBSON, MODEL_GIBSON_GOLDTOP),
+ {}
+};
+MODULE_DEVICE_TABLE(ieee1394, efw_id_table);
+
+static struct fw_driver efw_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "snd-fireworks",
+ .bus = &fw_bus_type,
+ },
+ .probe = efw_probe,
+ .update = efw_update,
+ .remove = efw_remove,
+ .id_table = efw_id_table,
+};
+
+static int __init snd_efw_init(void)
+{
+ int err;
+
+ err = snd_efw_transaction_register();
+ if (err < 0)
+ goto end;
+
+ err = driver_register(&efw_driver.driver);
+ if (err < 0)
+ snd_efw_transaction_unregister();
+
+end:
+ return err;
+}
+
+static void __exit snd_efw_exit(void)
+{
+ snd_efw_transaction_unregister();
+ driver_unregister(&efw_driver.driver);
+ mutex_destroy(&devices_mutex);
+}
+
+module_init(snd_efw_init);
+module_exit(snd_efw_exit);
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
new file mode 100644
index 000000000000..d2b36be4d2f8
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks.h
@@ -0,0 +1,233 @@
+/*
+ * fireworks.h - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+#ifndef SOUND_FIREWORKS_H_INCLUDED
+#define SOUND_FIREWORKS_H_INCLUDED
+
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/info.h>
+#include <sound/rawmidi.h>
+#include <sound/pcm_params.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+
+#include "../packets-buffer.h"
+#include "../iso-resources.h"
+#include "../amdtp.h"
+#include "../cmp.h"
+#include "../lib.h"
+
+#define SND_EFW_MAX_MIDI_OUT_PORTS 2
+#define SND_EFW_MAX_MIDI_IN_PORTS 2
+
+#define SND_EFW_MULTIPLIER_MODES 3
+#define HWINFO_NAME_SIZE_BYTES 32
+#define HWINFO_MAX_CAPS_GROUPS 8
+
+/*
+ * This should be greater than maximum bytes for EFW response content.
+ * Currently response against command for isochronous channel mapping is
+ * confirmed to be the maximum one. But for flexibility, use maximum data
+ * payload for asynchronous primary packets at S100 (Cable base rate) in
+ * IEEE Std 1394-1995.
+ */
+#define SND_EFW_RESPONSE_MAXIMUM_BYTES 0x200U
+
+extern unsigned int snd_efw_resp_buf_size;
+extern bool snd_efw_resp_buf_debug;
+
+struct snd_efw_phys_grp {
+ u8 type; /* see enum snd_efw_grp_type */
+ u8 count;
+} __packed;
+
+struct snd_efw {
+ struct snd_card *card;
+ struct fw_unit *unit;
+ int card_index;
+
+ struct mutex mutex;
+ spinlock_t lock;
+
+ /* for transaction */
+ u32 seqnum;
+ bool resp_addr_changable;
+
+ /* for quirks */
+ bool is_af9;
+ u32 firmware_version;
+
+ unsigned int midi_in_ports;
+ unsigned int midi_out_ports;
+
+ unsigned int supported_sampling_rate;
+ unsigned int pcm_capture_channels[SND_EFW_MULTIPLIER_MODES];
+ unsigned int pcm_playback_channels[SND_EFW_MULTIPLIER_MODES];
+
+ struct amdtp_stream *master;
+ struct amdtp_stream tx_stream;
+ struct amdtp_stream rx_stream;
+ struct cmp_connection out_conn;
+ struct cmp_connection in_conn;
+ atomic_t capture_substreams;
+ atomic_t playback_substreams;
+
+ /* hardware metering parameters */
+ unsigned int phys_out;
+ unsigned int phys_in;
+ unsigned int phys_out_grp_count;
+ unsigned int phys_in_grp_count;
+ struct snd_efw_phys_grp phys_out_grps[HWINFO_MAX_CAPS_GROUPS];
+ struct snd_efw_phys_grp phys_in_grps[HWINFO_MAX_CAPS_GROUPS];
+
+ /* for uapi */
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+
+ /* response queue */
+ u8 *resp_buf;
+ u8 *pull_ptr;
+ u8 *push_ptr;
+ unsigned int resp_queues;
+};
+
+int snd_efw_transaction_cmd(struct fw_unit *unit,
+ const void *cmd, unsigned int size);
+int snd_efw_transaction_run(struct fw_unit *unit,
+ const void *cmd, unsigned int cmd_size,
+ void *resp, unsigned int resp_size);
+int snd_efw_transaction_register(void);
+void snd_efw_transaction_unregister(void);
+void snd_efw_transaction_bus_reset(struct fw_unit *unit);
+void snd_efw_transaction_add_instance(struct snd_efw *efw);
+void snd_efw_transaction_remove_instance(struct snd_efw *efw);
+
+struct snd_efw_hwinfo {
+ u32 flags;
+ u32 guid_hi;
+ u32 guid_lo;
+ u32 type;
+ u32 version;
+ char vendor_name[HWINFO_NAME_SIZE_BYTES];
+ char model_name[HWINFO_NAME_SIZE_BYTES];
+ u32 supported_clocks;
+ u32 amdtp_rx_pcm_channels;
+ u32 amdtp_tx_pcm_channels;
+ u32 phys_out;
+ u32 phys_in;
+ u32 phys_out_grp_count;
+ struct snd_efw_phys_grp phys_out_grps[HWINFO_MAX_CAPS_GROUPS];
+ u32 phys_in_grp_count;
+ struct snd_efw_phys_grp phys_in_grps[HWINFO_MAX_CAPS_GROUPS];
+ u32 midi_out_ports;
+ u32 midi_in_ports;
+ u32 max_sample_rate;
+ u32 min_sample_rate;
+ u32 dsp_version;
+ u32 arm_version;
+ u32 mixer_playback_channels;
+ u32 mixer_capture_channels;
+ u32 fpga_version;
+ u32 amdtp_rx_pcm_channels_2x;
+ u32 amdtp_tx_pcm_channels_2x;
+ u32 amdtp_rx_pcm_channels_4x;
+ u32 amdtp_tx_pcm_channels_4x;
+ u32 reserved[16];
+} __packed;
+enum snd_efw_grp_type {
+ SND_EFW_CH_TYPE_ANALOG = 0,
+ SND_EFW_CH_TYPE_SPDIF = 1,
+ SND_EFW_CH_TYPE_ADAT = 2,
+ SND_EFW_CH_TYPE_SPDIF_OR_ADAT = 3,
+ SND_EFW_CH_TYPE_ANALOG_MIRRORING = 4,
+ SND_EFW_CH_TYPE_HEADPHONES = 5,
+ SND_EFW_CH_TYPE_I2S = 6,
+ SND_EFW_CH_TYPE_GUITAR = 7,
+ SND_EFW_CH_TYPE_PIEZO_GUITAR = 8,
+ SND_EFW_CH_TYPE_GUITAR_STRING = 9,
+ SND_EFW_CH_TYPE_VIRTUAL = 0x10000,
+ SND_EFW_CH_TYPE_DUMMY
+};
+struct snd_efw_phys_meters {
+ u32 status; /* guitar state/midi signal/clock input detect */
+ u32 reserved0;
+ u32 reserved1;
+ u32 reserved2;
+ u32 reserved3;
+ u32 out_meters;
+ u32 in_meters;
+ u32 reserved4;
+ u32 reserved5;
+ u32 values[0];
+} __packed;
+enum snd_efw_clock_source {
+ SND_EFW_CLOCK_SOURCE_INTERNAL = 0,
+ SND_EFW_CLOCK_SOURCE_SYTMATCH = 1,
+ SND_EFW_CLOCK_SOURCE_WORDCLOCK = 2,
+ SND_EFW_CLOCK_SOURCE_SPDIF = 3,
+ SND_EFW_CLOCK_SOURCE_ADAT_1 = 4,
+ SND_EFW_CLOCK_SOURCE_ADAT_2 = 5,
+ SND_EFW_CLOCK_SOURCE_CONTINUOUS = 6 /* internal variable clock */
+};
+enum snd_efw_transport_mode {
+ SND_EFW_TRANSPORT_MODE_WINDOWS = 0,
+ SND_EFW_TRANSPORT_MODE_IEC61883 = 1,
+};
+int snd_efw_command_set_resp_addr(struct snd_efw *efw,
+ u16 addr_high, u32 addr_low);
+int snd_efw_command_set_tx_mode(struct snd_efw *efw,
+ enum snd_efw_transport_mode mode);
+int snd_efw_command_get_hwinfo(struct snd_efw *efw,
+ struct snd_efw_hwinfo *hwinfo);
+int snd_efw_command_get_phys_meters(struct snd_efw *efw,
+ struct snd_efw_phys_meters *meters,
+ unsigned int len);
+int snd_efw_command_get_clock_source(struct snd_efw *efw,
+ enum snd_efw_clock_source *source);
+int snd_efw_command_get_sampling_rate(struct snd_efw *efw, unsigned int *rate);
+int snd_efw_command_set_sampling_rate(struct snd_efw *efw, unsigned int rate);
+
+int snd_efw_stream_init_duplex(struct snd_efw *efw);
+int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate);
+void snd_efw_stream_stop_duplex(struct snd_efw *efw);
+void snd_efw_stream_update_duplex(struct snd_efw *efw);
+void snd_efw_stream_destroy_duplex(struct snd_efw *efw);
+void snd_efw_stream_lock_changed(struct snd_efw *efw);
+int snd_efw_stream_lock_try(struct snd_efw *efw);
+void snd_efw_stream_lock_release(struct snd_efw *efw);
+
+void snd_efw_proc_init(struct snd_efw *efw);
+
+int snd_efw_create_midi_devices(struct snd_efw *efw);
+
+int snd_efw_create_pcm_devices(struct snd_efw *efw);
+int snd_efw_get_multiplier_mode(unsigned int sampling_rate, unsigned int *mode);
+
+int snd_efw_create_hwdep_device(struct snd_efw *efw);
+
+#define SND_EFW_DEV_ENTRY(vendor, model) \
+{ \
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | \
+ IEEE1394_MATCH_MODEL_ID, \
+ .vendor_id = vendor,\
+ .model_id = model \
+}
+
+#endif
diff --git a/sound/firewire/fireworks/fireworks_command.c b/sound/firewire/fireworks/fireworks_command.c
new file mode 100644
index 000000000000..166f80584c2a
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_command.c
@@ -0,0 +1,372 @@
+/*
+ * fireworks_command.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./fireworks.h"
+
+/*
+ * This driver uses transaction version 1 or later to use extended hardware
+ * information. Then too old devices are not available.
+ *
+ * Each commands are not required to have continuous sequence numbers. This
+ * number is just used to match command and response.
+ *
+ * This module support a part of commands. Please see FFADO if you want to see
+ * whole commands. But there are some commands which FFADO don't implement.
+ *
+ * Fireworks also supports AV/C general commands and AV/C Stream Format
+ * Information commands. But this module don't use them.
+ */
+
+#define KERNEL_SEQNUM_MIN (SND_EFW_TRANSACTION_USER_SEQNUM_MAX + 2)
+#define KERNEL_SEQNUM_MAX ((u32)~0)
+
+/* for clock source and sampling rate */
+struct efc_clock {
+ u32 source;
+ u32 sampling_rate;
+ u32 index;
+};
+
+/* command categories */
+enum efc_category {
+ EFC_CAT_HWINFO = 0,
+ EFC_CAT_TRANSPORT = 2,
+ EFC_CAT_HWCTL = 3,
+};
+
+/* hardware info category commands */
+enum efc_cmd_hwinfo {
+ EFC_CMD_HWINFO_GET_CAPS = 0,
+ EFC_CMD_HWINFO_GET_POLLED = 1,
+ EFC_CMD_HWINFO_SET_RESP_ADDR = 2
+};
+
+enum efc_cmd_transport {
+ EFC_CMD_TRANSPORT_SET_TX_MODE = 0
+};
+
+/* hardware control category commands */
+enum efc_cmd_hwctl {
+ EFC_CMD_HWCTL_SET_CLOCK = 0,
+ EFC_CMD_HWCTL_GET_CLOCK = 1,
+ EFC_CMD_HWCTL_IDENTIFY = 5
+};
+
+/* return values in response */
+enum efr_status {
+ EFR_STATUS_OK = 0,
+ EFR_STATUS_BAD = 1,
+ EFR_STATUS_BAD_COMMAND = 2,
+ EFR_STATUS_COMM_ERR = 3,
+ EFR_STATUS_BAD_QUAD_COUNT = 4,
+ EFR_STATUS_UNSUPPORTED = 5,
+ EFR_STATUS_1394_TIMEOUT = 6,
+ EFR_STATUS_DSP_TIMEOUT = 7,
+ EFR_STATUS_BAD_RATE = 8,
+ EFR_STATUS_BAD_CLOCK = 9,
+ EFR_STATUS_BAD_CHANNEL = 10,
+ EFR_STATUS_BAD_PAN = 11,
+ EFR_STATUS_FLASH_BUSY = 12,
+ EFR_STATUS_BAD_MIRROR = 13,
+ EFR_STATUS_BAD_LED = 14,
+ EFR_STATUS_BAD_PARAMETER = 15,
+ EFR_STATUS_INCOMPLETE = 0x80000000
+};
+
+static const char *const efr_status_names[] = {
+ [EFR_STATUS_OK] = "OK",
+ [EFR_STATUS_BAD] = "bad",
+ [EFR_STATUS_BAD_COMMAND] = "bad command",
+ [EFR_STATUS_COMM_ERR] = "comm err",
+ [EFR_STATUS_BAD_QUAD_COUNT] = "bad quad count",
+ [EFR_STATUS_UNSUPPORTED] = "unsupported",
+ [EFR_STATUS_1394_TIMEOUT] = "1394 timeout",
+ [EFR_STATUS_DSP_TIMEOUT] = "DSP timeout",
+ [EFR_STATUS_BAD_RATE] = "bad rate",
+ [EFR_STATUS_BAD_CLOCK] = "bad clock",
+ [EFR_STATUS_BAD_CHANNEL] = "bad channel",
+ [EFR_STATUS_BAD_PAN] = "bad pan",
+ [EFR_STATUS_FLASH_BUSY] = "flash busy",
+ [EFR_STATUS_BAD_MIRROR] = "bad mirror",
+ [EFR_STATUS_BAD_LED] = "bad LED",
+ [EFR_STATUS_BAD_PARAMETER] = "bad parameter",
+ [EFR_STATUS_BAD_PARAMETER + 1] = "incomplete"
+};
+
+static int
+efw_transaction(struct snd_efw *efw, unsigned int category,
+ unsigned int command,
+ const __be32 *params, unsigned int param_bytes,
+ const __be32 *resp, unsigned int resp_bytes)
+{
+ struct snd_efw_transaction *header;
+ __be32 *buf;
+ u32 seqnum;
+ unsigned int buf_bytes, cmd_bytes;
+ int err;
+
+ /* calculate buffer size*/
+ buf_bytes = sizeof(struct snd_efw_transaction) +
+ max(param_bytes, resp_bytes);
+
+ /* keep buffer */
+ buf = kzalloc(buf_bytes, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ /* to keep consistency of sequence number */
+ spin_lock(&efw->lock);
+ if ((efw->seqnum < KERNEL_SEQNUM_MIN) ||
+ (efw->seqnum >= KERNEL_SEQNUM_MAX - 2))
+ efw->seqnum = KERNEL_SEQNUM_MIN;
+ else
+ efw->seqnum += 2;
+ seqnum = efw->seqnum;
+ spin_unlock(&efw->lock);
+
+ /* fill transaction header fields */
+ cmd_bytes = sizeof(struct snd_efw_transaction) + param_bytes;
+ header = (struct snd_efw_transaction *)buf;
+ header->length = cpu_to_be32(cmd_bytes / sizeof(__be32));
+ header->version = cpu_to_be32(1);
+ header->seqnum = cpu_to_be32(seqnum);
+ header->category = cpu_to_be32(category);
+ header->command = cpu_to_be32(command);
+ header->status = 0;
+
+ /* fill transaction command parameters */
+ memcpy(header->params, params, param_bytes);
+
+ err = snd_efw_transaction_run(efw->unit, buf, cmd_bytes,
+ buf, buf_bytes);
+ if (err < 0)
+ goto end;
+
+ /* check transaction header fields */
+ if ((be32_to_cpu(header->version) < 1) ||
+ (be32_to_cpu(header->category) != category) ||
+ (be32_to_cpu(header->command) != command) ||
+ (be32_to_cpu(header->status) != EFR_STATUS_OK)) {
+ dev_err(&efw->unit->device, "EFW command failed [%u/%u]: %s\n",
+ be32_to_cpu(header->category),
+ be32_to_cpu(header->command),
+ efr_status_names[be32_to_cpu(header->status)]);
+ err = -EIO;
+ goto end;
+ }
+
+ if (resp == NULL)
+ goto end;
+
+ /* fill transaction response parameters */
+ memset((void *)resp, 0, resp_bytes);
+ resp_bytes = min_t(unsigned int, resp_bytes,
+ be32_to_cpu(header->length) * sizeof(__be32) -
+ sizeof(struct snd_efw_transaction));
+ memcpy((void *)resp, &buf[6], resp_bytes);
+end:
+ kfree(buf);
+ return err;
+}
+
+/*
+ * The address in host system for transaction response is changable when the
+ * device supports. struct hwinfo.flags includes its flag. The default is
+ * MEMORY_SPACE_EFW_RESPONSE.
+ */
+int snd_efw_command_set_resp_addr(struct snd_efw *efw,
+ u16 addr_high, u32 addr_low)
+{
+ __be32 addr[2];
+
+ addr[0] = cpu_to_be32(addr_high);
+ addr[1] = cpu_to_be32(addr_low);
+
+ if (!efw->resp_addr_changable)
+ return -ENOSYS;
+
+ return efw_transaction(efw, EFC_CAT_HWCTL,
+ EFC_CMD_HWINFO_SET_RESP_ADDR,
+ addr, sizeof(addr), NULL, 0);
+}
+
+/*
+ * This is for timestamp processing. In Windows mode, all 32bit fields of second
+ * CIP header in AMDTP transmit packet is used for 'presentation timestamp'. In
+ * 'no data' packet the value of this field is 0x90ffffff.
+ */
+int snd_efw_command_set_tx_mode(struct snd_efw *efw,
+ enum snd_efw_transport_mode mode)
+{
+ __be32 param = cpu_to_be32(mode);
+ return efw_transaction(efw, EFC_CAT_TRANSPORT,
+ EFC_CMD_TRANSPORT_SET_TX_MODE,
+ &param, sizeof(param), NULL, 0);
+}
+
+int snd_efw_command_get_hwinfo(struct snd_efw *efw,
+ struct snd_efw_hwinfo *hwinfo)
+{
+ int err;
+
+ err = efw_transaction(efw, EFC_CAT_HWINFO,
+ EFC_CMD_HWINFO_GET_CAPS,
+ NULL, 0, (__be32 *)hwinfo, sizeof(*hwinfo));
+ if (err < 0)
+ goto end;
+
+ be32_to_cpus(&hwinfo->flags);
+ be32_to_cpus(&hwinfo->guid_hi);
+ be32_to_cpus(&hwinfo->guid_lo);
+ be32_to_cpus(&hwinfo->type);
+ be32_to_cpus(&hwinfo->version);
+ be32_to_cpus(&hwinfo->supported_clocks);
+ be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels);
+ be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels);
+ be32_to_cpus(&hwinfo->phys_out);
+ be32_to_cpus(&hwinfo->phys_in);
+ be32_to_cpus(&hwinfo->phys_out_grp_count);
+ be32_to_cpus(&hwinfo->phys_in_grp_count);
+ be32_to_cpus(&hwinfo->midi_out_ports);
+ be32_to_cpus(&hwinfo->midi_in_ports);
+ be32_to_cpus(&hwinfo->max_sample_rate);
+ be32_to_cpus(&hwinfo->min_sample_rate);
+ be32_to_cpus(&hwinfo->dsp_version);
+ be32_to_cpus(&hwinfo->arm_version);
+ be32_to_cpus(&hwinfo->mixer_playback_channels);
+ be32_to_cpus(&hwinfo->mixer_capture_channels);
+ be32_to_cpus(&hwinfo->fpga_version);
+ be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels_2x);
+ be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels_2x);
+ be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels_4x);
+ be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels_4x);
+
+ /* ensure terminated */
+ hwinfo->vendor_name[HWINFO_NAME_SIZE_BYTES - 1] = '\0';
+ hwinfo->model_name[HWINFO_NAME_SIZE_BYTES - 1] = '\0';
+end:
+ return err;
+}
+
+int snd_efw_command_get_phys_meters(struct snd_efw *efw,
+ struct snd_efw_phys_meters *meters,
+ unsigned int len)
+{
+ __be32 *buf = (__be32 *)meters;
+ unsigned int i;
+ int err;
+
+ err = efw_transaction(efw, EFC_CAT_HWINFO,
+ EFC_CMD_HWINFO_GET_POLLED,
+ NULL, 0, (__be32 *)meters, len);
+ if (err >= 0)
+ for (i = 0; i < len / sizeof(u32); i++)
+ be32_to_cpus(&buf[i]);
+
+ return err;
+}
+
+static int
+command_get_clock(struct snd_efw *efw, struct efc_clock *clock)
+{
+ int err;
+
+ err = efw_transaction(efw, EFC_CAT_HWCTL,
+ EFC_CMD_HWCTL_GET_CLOCK,
+ NULL, 0,
+ (__be32 *)clock, sizeof(struct efc_clock));
+ if (err >= 0) {
+ be32_to_cpus(&clock->source);
+ be32_to_cpus(&clock->sampling_rate);
+ be32_to_cpus(&clock->index);
+ }
+
+ return err;
+}
+
+/* give UINT_MAX if set nothing */
+static int
+command_set_clock(struct snd_efw *efw,
+ unsigned int source, unsigned int rate)
+{
+ struct efc_clock clock = {0};
+ int err;
+
+ /* check arguments */
+ if ((source == UINT_MAX) && (rate == UINT_MAX)) {
+ err = -EINVAL;
+ goto end;
+ }
+
+ /* get current status */
+ err = command_get_clock(efw, &clock);
+ if (err < 0)
+ goto end;
+
+ /* no need */
+ if ((clock.source == source) && (clock.sampling_rate == rate))
+ goto end;
+
+ /* set params */
+ if ((source != UINT_MAX) && (clock.source != source))
+ clock.source = source;
+ if ((rate != UINT_MAX) && (clock.sampling_rate != rate))
+ clock.sampling_rate = rate;
+ clock.index = 0;
+
+ cpu_to_be32s(&clock.source);
+ cpu_to_be32s(&clock.sampling_rate);
+ cpu_to_be32s(&clock.index);
+
+ err = efw_transaction(efw, EFC_CAT_HWCTL,
+ EFC_CMD_HWCTL_SET_CLOCK,
+ (__be32 *)&clock, sizeof(struct efc_clock),
+ NULL, 0);
+ if (err < 0)
+ goto end;
+
+ /*
+ * With firmware version 5.8, just after changing clock state, these
+ * parameters are not immediately retrieved by get command. In my
+ * trial, there needs to be 100msec to get changed parameters.
+ */
+ msleep(150);
+end:
+ return err;
+}
+
+int snd_efw_command_get_clock_source(struct snd_efw *efw,
+ enum snd_efw_clock_source *source)
+{
+ int err;
+ struct efc_clock clock = {0};
+
+ err = command_get_clock(efw, &clock);
+ if (err >= 0)
+ *source = clock.source;
+
+ return err;
+}
+
+int snd_efw_command_get_sampling_rate(struct snd_efw *efw, unsigned int *rate)
+{
+ int err;
+ struct efc_clock clock = {0};
+
+ err = command_get_clock(efw, &clock);
+ if (err >= 0)
+ *rate = clock.sampling_rate;
+
+ return err;
+}
+
+int snd_efw_command_set_sampling_rate(struct snd_efw *efw, unsigned int rate)
+{
+ return command_set_clock(efw, UINT_MAX, rate);
+}
+
diff --git a/sound/firewire/fireworks/fireworks_hwdep.c b/sound/firewire/fireworks/fireworks_hwdep.c
new file mode 100644
index 000000000000..4f8216fb6b62
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_hwdep.c
@@ -0,0 +1,298 @@
+/*
+ * fireworks_hwdep.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * This codes have five functionalities.
+ *
+ * 1.get information about firewire node
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock streaming
+ * 4.transmit command of EFW transaction
+ * 5.receive response of EFW transaction
+ *
+ */
+
+#include "fireworks.h"
+
+static long
+hwdep_read_resp_buf(struct snd_efw *efw, char __user *buf, long remained,
+ loff_t *offset)
+{
+ unsigned int length, till_end, type;
+ struct snd_efw_transaction *t;
+ long count = 0;
+
+ if (remained < sizeof(type) + sizeof(struct snd_efw_transaction))
+ return -ENOSPC;
+
+ /* data type is SNDRV_FIREWIRE_EVENT_EFW_RESPONSE */
+ type = SNDRV_FIREWIRE_EVENT_EFW_RESPONSE;
+ if (copy_to_user(buf, &type, sizeof(type)))
+ return -EFAULT;
+ remained -= sizeof(type);
+ buf += sizeof(type);
+
+ /* write into buffer as many responses as possible */
+ while (efw->resp_queues > 0) {
+ t = (struct snd_efw_transaction *)(efw->pull_ptr);
+ length = be32_to_cpu(t->length) * sizeof(__be32);
+
+ /* confirm enough space for this response */
+ if (remained < length)
+ break;
+
+ /* copy from ring buffer to user buffer */
+ while (length > 0) {
+ till_end = snd_efw_resp_buf_size -
+ (unsigned int)(efw->pull_ptr - efw->resp_buf);
+ till_end = min_t(unsigned int, length, till_end);
+
+ if (copy_to_user(buf, efw->pull_ptr, till_end))
+ return -EFAULT;
+
+ efw->pull_ptr += till_end;
+ if (efw->pull_ptr >= efw->resp_buf +
+ snd_efw_resp_buf_size)
+ efw->pull_ptr = efw->resp_buf;
+
+ length -= till_end;
+ buf += till_end;
+ count += till_end;
+ remained -= till_end;
+ }
+
+ efw->resp_queues--;
+ }
+
+ return count;
+}
+
+static long
+hwdep_read_locked(struct snd_efw *efw, char __user *buf, long count,
+ loff_t *offset)
+{
+ union snd_firewire_event event;
+
+ memset(&event, 0, sizeof(event));
+
+ event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+ event.lock_status.status = (efw->dev_lock_count > 0);
+ efw->dev_lock_changed = false;
+
+ count = min_t(long, count, sizeof(event.lock_status));
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static long
+hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_efw *efw = hwdep->private_data;
+ DEFINE_WAIT(wait);
+
+ spin_lock_irq(&efw->lock);
+
+ while ((!efw->dev_lock_changed) && (efw->resp_queues == 0)) {
+ prepare_to_wait(&efw->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&efw->lock);
+ schedule();
+ finish_wait(&efw->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&efw->lock);
+ }
+
+ if (efw->dev_lock_changed)
+ count = hwdep_read_locked(efw, buf, count, offset);
+ else if (efw->resp_queues > 0)
+ count = hwdep_read_resp_buf(efw, buf, count, offset);
+
+ spin_unlock_irq(&efw->lock);
+
+ return count;
+}
+
+static long
+hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count,
+ loff_t *offset)
+{
+ struct snd_efw *efw = hwdep->private_data;
+ u32 seqnum;
+ u8 *buf;
+
+ if (count < sizeof(struct snd_efw_transaction) ||
+ SND_EFW_RESPONSE_MAXIMUM_BYTES < count)
+ return -EINVAL;
+
+ buf = memdup_user(data, count);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ /* check seqnum is not for kernel-land */
+ seqnum = be32_to_cpu(((struct snd_efw_transaction *)buf)->seqnum);
+ if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX) {
+ count = -EINVAL;
+ goto end;
+ }
+
+ if (snd_efw_transaction_cmd(efw->unit, buf, count) < 0)
+ count = -EIO;
+end:
+ kfree(buf);
+ return count;
+}
+
+static unsigned int
+hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
+{
+ struct snd_efw *efw = hwdep->private_data;
+ unsigned int events;
+
+ poll_wait(file, &efw->hwdep_wait, wait);
+
+ spin_lock_irq(&efw->lock);
+ if (efw->dev_lock_changed || (efw->resp_queues > 0))
+ events = POLLIN | POLLRDNORM;
+ else
+ events = 0;
+ spin_unlock_irq(&efw->lock);
+
+ return events | POLLOUT;
+}
+
+static int
+hwdep_get_info(struct snd_efw *efw, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(efw->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_FIREWORKS;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strlcpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int
+hwdep_lock(struct snd_efw *efw)
+{
+ int err;
+
+ spin_lock_irq(&efw->lock);
+
+ if (efw->dev_lock_count == 0) {
+ efw->dev_lock_count = -1;
+ err = 0;
+ } else {
+ err = -EBUSY;
+ }
+
+ spin_unlock_irq(&efw->lock);
+
+ return err;
+}
+
+static int
+hwdep_unlock(struct snd_efw *efw)
+{
+ int err;
+
+ spin_lock_irq(&efw->lock);
+
+ if (efw->dev_lock_count == -1) {
+ efw->dev_lock_count = 0;
+ err = 0;
+ } else {
+ err = -EBADFD;
+ }
+
+ spin_unlock_irq(&efw->lock);
+
+ return err;
+}
+
+static int
+hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_efw *efw = hwdep->private_data;
+
+ spin_lock_irq(&efw->lock);
+ if (efw->dev_lock_count == -1)
+ efw->dev_lock_count = 0;
+ spin_unlock_irq(&efw->lock);
+
+ return 0;
+}
+
+static int
+hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_efw *efw = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(efw, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(efw);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(efw);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int
+hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+static const struct snd_hwdep_ops hwdep_ops = {
+ .read = hwdep_read,
+ .write = hwdep_write,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+};
+
+int snd_efw_create_hwdep_device(struct snd_efw *efw)
+{
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(efw->card, "Fireworks", 0, &hwdep);
+ if (err < 0)
+ goto end;
+ strcpy(hwdep->name, "Fireworks");
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREWORKS;
+ hwdep->ops = hwdep_ops;
+ hwdep->private_data = efw;
+ hwdep->exclusive = true;
+end:
+ return err;
+}
+
diff --git a/sound/firewire/fireworks/fireworks_midi.c b/sound/firewire/fireworks/fireworks_midi.c
new file mode 100644
index 000000000000..cf9c65260439
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_midi.c
@@ -0,0 +1,168 @@
+/*
+ * fireworks_midi.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+#include "fireworks.h"
+
+static int midi_capture_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_efw *efw = substream->rmidi->private_data;
+ int err;
+
+ err = snd_efw_stream_lock_try(efw);
+ if (err < 0)
+ goto end;
+
+ atomic_inc(&efw->capture_substreams);
+ err = snd_efw_stream_start_duplex(efw, 0);
+ if (err < 0)
+ snd_efw_stream_lock_release(efw);
+
+end:
+ return err;
+}
+
+static int midi_playback_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_efw *efw = substream->rmidi->private_data;
+ int err;
+
+ err = snd_efw_stream_lock_try(efw);
+ if (err < 0)
+ goto end;
+
+ atomic_inc(&efw->playback_substreams);
+ err = snd_efw_stream_start_duplex(efw, 0);
+ if (err < 0)
+ snd_efw_stream_lock_release(efw);
+end:
+ return err;
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_efw *efw = substream->rmidi->private_data;
+
+ atomic_dec(&efw->capture_substreams);
+ snd_efw_stream_stop_duplex(efw);
+
+ snd_efw_stream_lock_release(efw);
+ return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_efw *efw = substream->rmidi->private_data;
+
+ atomic_dec(&efw->playback_substreams);
+ snd_efw_stream_stop_duplex(efw);
+
+ snd_efw_stream_lock_release(efw);
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_efw *efw = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&efw->lock, flags);
+
+ if (up)
+ amdtp_stream_midi_trigger(&efw->tx_stream,
+ substrm->number, substrm);
+ else
+ amdtp_stream_midi_trigger(&efw->tx_stream,
+ substrm->number, NULL);
+
+ spin_unlock_irqrestore(&efw->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_efw *efw = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&efw->lock, flags);
+
+ if (up)
+ amdtp_stream_midi_trigger(&efw->rx_stream,
+ substrm->number, substrm);
+ else
+ amdtp_stream_midi_trigger(&efw->rx_stream,
+ substrm->number, NULL);
+
+ spin_unlock_irqrestore(&efw->lock, flags);
+}
+
+static struct snd_rawmidi_ops midi_capture_ops = {
+ .open = midi_capture_open,
+ .close = midi_capture_close,
+ .trigger = midi_capture_trigger,
+};
+
+static struct snd_rawmidi_ops midi_playback_ops = {
+ .open = midi_playback_open,
+ .close = midi_playback_close,
+ .trigger = midi_playback_trigger,
+};
+
+static void set_midi_substream_names(struct snd_efw *efw,
+ struct snd_rawmidi_str *str)
+{
+ struct snd_rawmidi_substream *subs;
+
+ list_for_each_entry(subs, &str->substreams, list) {
+ snprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d", efw->card->shortname, subs->number + 1);
+ }
+}
+
+int snd_efw_create_midi_devices(struct snd_efw *efw)
+{
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_str *str;
+ int err;
+
+ /* create midi ports */
+ err = snd_rawmidi_new(efw->card, efw->card->driver, 0,
+ efw->midi_out_ports, efw->midi_in_ports,
+ &rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", efw->card->shortname);
+ rmidi->private_data = efw;
+
+ if (efw->midi_in_ports > 0) {
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &midi_capture_ops);
+
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+ set_midi_substream_names(efw, str);
+ }
+
+ if (efw->midi_out_ports > 0) {
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &midi_playback_ops);
+
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+ set_midi_substream_names(efw, str);
+ }
+
+ if ((efw->midi_out_ports > 0) && (efw->midi_in_ports > 0))
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ return 0;
+}
diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c
new file mode 100644
index 000000000000..8a34753de210
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_pcm.c
@@ -0,0 +1,403 @@
+/*
+ * fireworks_pcm.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+#include "./fireworks.h"
+
+/*
+ * NOTE:
+ * Fireworks changes its AMDTP channels for PCM data according to its sampling
+ * rate. There are three modes. Here _XX is either _rx or _tx.
+ * 0: 32.0- 48.0 kHz then snd_efw_hwinfo.amdtp_XX_pcm_channels applied
+ * 1: 88.2- 96.0 kHz then snd_efw_hwinfo.amdtp_XX_pcm_channels_2x applied
+ * 2: 176.4-192.0 kHz then snd_efw_hwinfo.amdtp_XX_pcm_channels_4x applied
+ *
+ * The number of PCM channels for analog input and output are always fixed but
+ * the number of PCM channels for digital input and output are differed.
+ *
+ * Additionally, according to "AudioFire Owner's Manual Version 2.2", in some
+ * model, the number of PCM channels for digital input has more restriction
+ * depending on which digital interface is selected.
+ * - S/PDIF coaxial and optical : use input 1-2
+ * - ADAT optical at 32.0-48.0 kHz : use input 1-8
+ * - ADAT optical at 88.2-96.0 kHz : use input 1-4 (S/MUX format)
+ *
+ * The data in AMDTP channels for blank PCM channels are zero.
+ */
+static const unsigned int freq_table[] = {
+ /* multiplier mode 0 */
+ [0] = 32000,
+ [1] = 44100,
+ [2] = 48000,
+ /* multiplier mode 1 */
+ [3] = 88200,
+ [4] = 96000,
+ /* multiplier mode 2 */
+ [5] = 176400,
+ [6] = 192000,
+};
+
+static inline unsigned int
+get_multiplier_mode_with_index(unsigned int index)
+{
+ return ((int)index - 1) / 2;
+}
+
+int snd_efw_get_multiplier_mode(unsigned int sampling_rate, unsigned int *mode)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
+ if (freq_table[i] == sampling_rate) {
+ *mode = get_multiplier_mode_with_index(i);
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int
+hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
+{
+ unsigned int *pcm_channels = rule->private;
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ const struct snd_interval *c =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int i, mode;
+
+ for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
+ mode = get_multiplier_mode_with_index(i);
+ if (!snd_interval_test(c, pcm_channels[mode]))
+ continue;
+
+ t.min = min(t.min, freq_table[i]);
+ t.max = max(t.max, freq_table[i]);
+ }
+
+ return snd_interval_refine(r, &t);
+}
+
+static int
+hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
+{
+ unsigned int *pcm_channels = rule->private;
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int i, mode;
+
+ for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
+ mode = get_multiplier_mode_with_index(i);
+ if (!snd_interval_test(r, freq_table[i]))
+ continue;
+
+ t.min = min(t.min, pcm_channels[mode]);
+ t.max = max(t.max, pcm_channels[mode]);
+ }
+
+ return snd_interval_refine(c, &t);
+}
+
+static void
+limit_channels(struct snd_pcm_hardware *hw, unsigned int *pcm_channels)
+{
+ unsigned int i, mode;
+
+ hw->channels_min = UINT_MAX;
+ hw->channels_max = 0;
+
+ for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
+ mode = get_multiplier_mode_with_index(i);
+ if (pcm_channels[mode] == 0)
+ continue;
+
+ hw->channels_min = min(hw->channels_min, pcm_channels[mode]);
+ hw->channels_max = max(hw->channels_max, pcm_channels[mode]);
+ }
+}
+
+static void
+limit_period_and_buffer(struct snd_pcm_hardware *hw)
+{
+ hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */
+ hw->periods_max = UINT_MAX;
+
+ hw->period_bytes_min = 4 * hw->channels_max; /* bytes for a frame */
+
+ /* Just to prevent from allocating much pages. */
+ hw->period_bytes_max = hw->period_bytes_min * 2048;
+ hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
+}
+
+static int
+pcm_init_hw_params(struct snd_efw *efw,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct amdtp_stream *s;
+ unsigned int *pcm_channels;
+ int err;
+
+ runtime->hw.info = SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_JOINT_DUPLEX |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
+ s = &efw->tx_stream;
+ pcm_channels = efw->pcm_capture_channels;
+ } else {
+ runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
+ s = &efw->rx_stream;
+ pcm_channels = efw->pcm_playback_channels;
+ }
+
+ /* limit rates */
+ runtime->hw.rates = efw->supported_sampling_rate,
+ snd_pcm_limit_hw_rates(runtime);
+
+ limit_channels(&runtime->hw, pcm_channels);
+ limit_period_and_buffer(&runtime->hw);
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_channels, pcm_channels,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ goto end;
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ hw_rule_rate, pcm_channels,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ goto end;
+
+ err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
+end:
+ return err;
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_efw *efw = substream->private_data;
+ unsigned int sampling_rate;
+ enum snd_efw_clock_source clock_source;
+ int err;
+
+ err = snd_efw_stream_lock_try(efw);
+ if (err < 0)
+ goto end;
+
+ err = pcm_init_hw_params(efw, substream);
+ if (err < 0)
+ goto err_locked;
+
+ err = snd_efw_command_get_clock_source(efw, &clock_source);
+ if (err < 0)
+ goto err_locked;
+
+ /*
+ * When source of clock is not internal or any PCM streams are running,
+ * available sampling rate is limited at current sampling rate.
+ */
+ if ((clock_source != SND_EFW_CLOCK_SOURCE_INTERNAL) ||
+ amdtp_stream_pcm_running(&efw->tx_stream) ||
+ amdtp_stream_pcm_running(&efw->rx_stream)) {
+ err = snd_efw_command_get_sampling_rate(efw, &sampling_rate);
+ if (err < 0)
+ goto err_locked;
+ substream->runtime->hw.rate_min = sampling_rate;
+ substream->runtime->hw.rate_max = sampling_rate;
+ }
+
+ snd_pcm_set_sync(substream);
+end:
+ return err;
+err_locked:
+ snd_efw_stream_lock_release(efw);
+ return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_efw *efw = substream->private_data;
+ snd_efw_stream_lock_release(efw);
+ return 0;
+}
+
+static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_efw *efw = substream->private_data;
+
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ atomic_inc(&efw->capture_substreams);
+ amdtp_stream_set_pcm_format(&efw->tx_stream, params_format(hw_params));
+
+ return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+}
+static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_efw *efw = substream->private_data;
+
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
+ atomic_inc(&efw->playback_substreams);
+ amdtp_stream_set_pcm_format(&efw->rx_stream, params_format(hw_params));
+
+ return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+}
+
+static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_efw *efw = substream->private_data;
+
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ atomic_dec(&efw->capture_substreams);
+
+ snd_efw_stream_stop_duplex(efw);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_efw *efw = substream->private_data;
+
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ atomic_dec(&efw->playback_substreams);
+
+ snd_efw_stream_stop_duplex(efw);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_efw *efw = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ err = snd_efw_stream_start_duplex(efw, runtime->rate);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&efw->tx_stream);
+
+ return err;
+}
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_efw *efw = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ err = snd_efw_stream_start_duplex(efw, runtime->rate);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&efw->rx_stream);
+
+ return err;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_efw *efw = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&efw->tx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&efw->tx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_efw *efw = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&efw->rx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&efw->rx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_efw *efw = sbstrm->private_data;
+ return amdtp_stream_pcm_pointer(&efw->tx_stream);
+}
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_efw *efw = sbstrm->private_data;
+ return amdtp_stream_pcm_pointer(&efw->rx_stream);
+}
+
+static const struct snd_pcm_ops pcm_capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pcm_capture_hw_params,
+ .hw_free = pcm_capture_hw_free,
+ .prepare = pcm_capture_prepare,
+ .trigger = pcm_capture_trigger,
+ .pointer = pcm_capture_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static const struct snd_pcm_ops pcm_playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pcm_playback_hw_params,
+ .hw_free = pcm_playback_hw_free,
+ .prepare = pcm_playback_prepare,
+ .trigger = pcm_playback_trigger,
+ .pointer = pcm_playback_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+};
+
+int snd_efw_create_pcm_devices(struct snd_efw *efw)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(efw->card, efw->card->driver, 0, 1, 1, &pcm);
+ if (err < 0)
+ goto end;
+
+ pcm->private_data = efw;
+ snprintf(pcm->name, sizeof(pcm->name), "%s PCM", efw->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
+end:
+ return err;
+}
+
diff --git a/sound/firewire/fireworks/fireworks_proc.c b/sound/firewire/fireworks/fireworks_proc.c
new file mode 100644
index 000000000000..f29d4aaf56a1
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_proc.c
@@ -0,0 +1,232 @@
+/*
+ * fireworks_proc.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./fireworks.h"
+
+static inline const char*
+get_phys_name(struct snd_efw_phys_grp *grp, bool input)
+{
+ const char *const ch_type[] = {
+ "Analog", "S/PDIF", "ADAT", "S/PDIF or ADAT", "Mirroring",
+ "Headphones", "I2S", "Guitar", "Pirzo Guitar", "Guitar String",
+ };
+
+ if (grp->type < ARRAY_SIZE(ch_type))
+ return ch_type[grp->type];
+ else if (input)
+ return "Input";
+ else
+ return "Output";
+}
+
+static void
+proc_read_hwinfo(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+{
+ struct snd_efw *efw = entry->private_data;
+ unsigned short i;
+ struct snd_efw_hwinfo *hwinfo;
+
+ hwinfo = kmalloc(sizeof(struct snd_efw_hwinfo), GFP_KERNEL);
+ if (hwinfo == NULL)
+ return;
+
+ if (snd_efw_command_get_hwinfo(efw, hwinfo) < 0)
+ goto end;
+
+ snd_iprintf(buffer, "guid_hi: 0x%X\n", hwinfo->guid_hi);
+ snd_iprintf(buffer, "guid_lo: 0x%X\n", hwinfo->guid_lo);
+ snd_iprintf(buffer, "type: 0x%X\n", hwinfo->type);
+ snd_iprintf(buffer, "version: 0x%X\n", hwinfo->version);
+ snd_iprintf(buffer, "vendor_name: %s\n", hwinfo->vendor_name);
+ snd_iprintf(buffer, "model_name: %s\n", hwinfo->model_name);
+
+ snd_iprintf(buffer, "dsp_version: 0x%X\n", hwinfo->dsp_version);
+ snd_iprintf(buffer, "arm_version: 0x%X\n", hwinfo->arm_version);
+ snd_iprintf(buffer, "fpga_version: 0x%X\n", hwinfo->fpga_version);
+
+ snd_iprintf(buffer, "flags: 0x%X\n", hwinfo->flags);
+
+ snd_iprintf(buffer, "max_sample_rate: 0x%X\n", hwinfo->max_sample_rate);
+ snd_iprintf(buffer, "min_sample_rate: 0x%X\n", hwinfo->min_sample_rate);
+ snd_iprintf(buffer, "supported_clock: 0x%X\n",
+ hwinfo->supported_clocks);
+
+ snd_iprintf(buffer, "phys out: 0x%X\n", hwinfo->phys_out);
+ snd_iprintf(buffer, "phys in: 0x%X\n", hwinfo->phys_in);
+
+ snd_iprintf(buffer, "phys in grps: 0x%X\n",
+ hwinfo->phys_in_grp_count);
+ for (i = 0; i < hwinfo->phys_in_grp_count; i++) {
+ snd_iprintf(buffer,
+ "phys in grp[0x%d]: type 0x%d, count 0x%d\n",
+ i, hwinfo->phys_out_grps[i].type,
+ hwinfo->phys_out_grps[i].count);
+ }
+
+ snd_iprintf(buffer, "phys out grps: 0x%X\n",
+ hwinfo->phys_out_grp_count);
+ for (i = 0; i < hwinfo->phys_out_grp_count; i++) {
+ snd_iprintf(buffer,
+ "phys out grps[0x%d]: type 0x%d, count 0x%d\n",
+ i, hwinfo->phys_out_grps[i].type,
+ hwinfo->phys_out_grps[i].count);
+ }
+
+ snd_iprintf(buffer, "amdtp rx pcm channels 1x: 0x%X\n",
+ hwinfo->amdtp_rx_pcm_channels);
+ snd_iprintf(buffer, "amdtp tx pcm channels 1x: 0x%X\n",
+ hwinfo->amdtp_tx_pcm_channels);
+ snd_iprintf(buffer, "amdtp rx pcm channels 2x: 0x%X\n",
+ hwinfo->amdtp_rx_pcm_channels_2x);
+ snd_iprintf(buffer, "amdtp tx pcm channels 2x: 0x%X\n",
+ hwinfo->amdtp_tx_pcm_channels_2x);
+ snd_iprintf(buffer, "amdtp rx pcm channels 4x: 0x%X\n",
+ hwinfo->amdtp_rx_pcm_channels_4x);
+ snd_iprintf(buffer, "amdtp tx pcm channels 4x: 0x%X\n",
+ hwinfo->amdtp_tx_pcm_channels_4x);
+
+ snd_iprintf(buffer, "midi out ports: 0x%X\n", hwinfo->midi_out_ports);
+ snd_iprintf(buffer, "midi in ports: 0x%X\n", hwinfo->midi_in_ports);
+
+ snd_iprintf(buffer, "mixer playback channels: 0x%X\n",
+ hwinfo->mixer_playback_channels);
+ snd_iprintf(buffer, "mixer capture channels: 0x%X\n",
+ hwinfo->mixer_capture_channels);
+end:
+ kfree(hwinfo);
+}
+
+static void
+proc_read_clock(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+{
+ struct snd_efw *efw = entry->private_data;
+ enum snd_efw_clock_source clock_source;
+ unsigned int sampling_rate;
+
+ if (snd_efw_command_get_clock_source(efw, &clock_source) < 0)
+ return;
+
+ if (snd_efw_command_get_sampling_rate(efw, &sampling_rate) < 0)
+ return;
+
+ snd_iprintf(buffer, "Clock Source: %d\n", clock_source);
+ snd_iprintf(buffer, "Sampling Rate: %d\n", sampling_rate);
+}
+
+/*
+ * NOTE:
+ * dB = 20 * log10(linear / 0x01000000)
+ * -144.0 dB when linear is 0
+ */
+static void
+proc_read_phys_meters(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_efw *efw = entry->private_data;
+ struct snd_efw_phys_meters *meters;
+ unsigned int g, c, m, max, size;
+ const char *name;
+ u32 *linear;
+ int err;
+
+ size = sizeof(struct snd_efw_phys_meters) +
+ (efw->phys_in + efw->phys_out) * sizeof(u32);
+ meters = kzalloc(size, GFP_KERNEL);
+ if (meters == NULL)
+ return;
+
+ err = snd_efw_command_get_phys_meters(efw, meters, size);
+ if (err < 0)
+ goto end;
+
+ snd_iprintf(buffer, "Physical Meters:\n");
+
+ m = 0;
+ max = min(efw->phys_out, meters->out_meters);
+ linear = meters->values;
+ snd_iprintf(buffer, " %d Outputs:\n", max);
+ for (g = 0; g < efw->phys_out_grp_count; g++) {
+ name = get_phys_name(&efw->phys_out_grps[g], false);
+ for (c = 0; c < efw->phys_out_grps[g].count; c++) {
+ if (m < max)
+ snd_iprintf(buffer, "\t%s [%d]: %d\n",
+ name, c, linear[m++]);
+ }
+ }
+
+ m = 0;
+ max = min(efw->phys_in, meters->in_meters);
+ linear = meters->values + meters->out_meters;
+ snd_iprintf(buffer, " %d Inputs:\n", max);
+ for (g = 0; g < efw->phys_in_grp_count; g++) {
+ name = get_phys_name(&efw->phys_in_grps[g], true);
+ for (c = 0; c < efw->phys_in_grps[g].count; c++)
+ if (m < max)
+ snd_iprintf(buffer, "\t%s [%d]: %d\n",
+ name, c, linear[m++]);
+ }
+end:
+ kfree(meters);
+}
+
+static void
+proc_read_queues_state(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_efw *efw = entry->private_data;
+ unsigned int consumed;
+
+ if (efw->pull_ptr > efw->push_ptr)
+ consumed = snd_efw_resp_buf_size -
+ (unsigned int)(efw->pull_ptr - efw->push_ptr);
+ else
+ consumed = (unsigned int)(efw->push_ptr - efw->pull_ptr);
+
+ snd_iprintf(buffer, "%d %d/%d\n",
+ efw->resp_queues, consumed, snd_efw_resp_buf_size);
+}
+
+static void
+add_node(struct snd_efw *efw, struct snd_info_entry *root, const char *name,
+ void (*op)(struct snd_info_entry *e, struct snd_info_buffer *b))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(efw->card, name, root);
+ if (entry == NULL)
+ return;
+
+ snd_info_set_text_ops(entry, efw, op);
+ if (snd_info_register(entry) < 0)
+ snd_info_free_entry(entry);
+}
+
+void snd_efw_proc_init(struct snd_efw *efw)
+{
+ struct snd_info_entry *root;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(efw->card, "firewire",
+ efw->card->proc_root);
+ if (root == NULL)
+ return;
+ root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ if (snd_info_register(root) < 0) {
+ snd_info_free_entry(root);
+ return;
+ }
+
+ add_node(efw, root, "clock", proc_read_clock);
+ add_node(efw, root, "firmware", proc_read_hwinfo);
+ add_node(efw, root, "meters", proc_read_phys_meters);
+ add_node(efw, root, "queues", proc_read_queues_state);
+}
diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c
new file mode 100644
index 000000000000..541569022a7c
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_stream.c
@@ -0,0 +1,372 @@
+/*
+ * fireworks_stream.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+#include "./fireworks.h"
+
+#define CALLBACK_TIMEOUT 100
+
+static int
+init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
+{
+ struct cmp_connection *conn;
+ enum cmp_direction c_dir;
+ enum amdtp_stream_direction s_dir;
+ int err;
+
+ if (stream == &efw->tx_stream) {
+ conn = &efw->out_conn;
+ c_dir = CMP_OUTPUT;
+ s_dir = AMDTP_IN_STREAM;
+ } else {
+ conn = &efw->in_conn;
+ c_dir = CMP_INPUT;
+ s_dir = AMDTP_OUT_STREAM;
+ }
+
+ err = cmp_connection_init(conn, efw->unit, c_dir, 0);
+ if (err < 0)
+ goto end;
+
+ err = amdtp_stream_init(stream, efw->unit, s_dir, CIP_BLOCKING);
+ if (err < 0) {
+ amdtp_stream_destroy(stream);
+ cmp_connection_destroy(conn);
+ }
+end:
+ return err;
+}
+
+static void
+stop_stream(struct snd_efw *efw, struct amdtp_stream *stream)
+{
+ amdtp_stream_pcm_abort(stream);
+ amdtp_stream_stop(stream);
+
+ if (stream == &efw->tx_stream)
+ cmp_connection_break(&efw->out_conn);
+ else
+ cmp_connection_break(&efw->in_conn);
+}
+
+static int
+start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
+ unsigned int sampling_rate)
+{
+ struct cmp_connection *conn;
+ unsigned int mode, pcm_channels, midi_ports;
+ int err;
+
+ err = snd_efw_get_multiplier_mode(sampling_rate, &mode);
+ if (err < 0)
+ goto end;
+ if (stream == &efw->tx_stream) {
+ conn = &efw->out_conn;
+ pcm_channels = efw->pcm_capture_channels[mode];
+ midi_ports = efw->midi_out_ports;
+ } else {
+ conn = &efw->in_conn;
+ pcm_channels = efw->pcm_playback_channels[mode];
+ midi_ports = efw->midi_in_ports;
+ }
+
+ amdtp_stream_set_parameters(stream, sampling_rate,
+ pcm_channels, midi_ports);
+
+ /* establish connection via CMP */
+ err = cmp_connection_establish(conn,
+ amdtp_stream_get_max_payload(stream));
+ if (err < 0)
+ goto end;
+
+ /* start amdtp stream */
+ err = amdtp_stream_start(stream,
+ conn->resources.channel,
+ conn->speed);
+ if (err < 0) {
+ stop_stream(efw, stream);
+ goto end;
+ }
+
+ /* wait first callback */
+ if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
+ stop_stream(efw, stream);
+ err = -ETIMEDOUT;
+ }
+end:
+ return err;
+}
+
+static void
+destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)
+{
+ stop_stream(efw, stream);
+
+ amdtp_stream_destroy(stream);
+
+ if (stream == &efw->tx_stream)
+ cmp_connection_destroy(&efw->out_conn);
+ else
+ cmp_connection_destroy(&efw->in_conn);
+}
+
+static int
+get_sync_mode(struct snd_efw *efw, enum cip_flags *sync_mode)
+{
+ enum snd_efw_clock_source clock_source;
+ int err;
+
+ err = snd_efw_command_get_clock_source(efw, &clock_source);
+ if (err < 0)
+ return err;
+
+ if (clock_source == SND_EFW_CLOCK_SOURCE_SYTMATCH)
+ return -ENOSYS;
+
+ *sync_mode = CIP_SYNC_TO_DEVICE;
+ return 0;
+}
+
+static int
+check_connection_used_by_others(struct snd_efw *efw, struct amdtp_stream *s)
+{
+ struct cmp_connection *conn;
+ bool used;
+ int err;
+
+ if (s == &efw->tx_stream)
+ conn = &efw->out_conn;
+ else
+ conn = &efw->in_conn;
+
+ err = cmp_connection_check_used(conn, &used);
+ if ((err >= 0) && used && !amdtp_stream_running(s)) {
+ dev_err(&efw->unit->device,
+ "Connection established by others: %cPCR[%d]\n",
+ (conn->direction == CMP_OUTPUT) ? 'o' : 'i',
+ conn->pcr_index);
+ err = -EBUSY;
+ }
+
+ return err;
+}
+
+int snd_efw_stream_init_duplex(struct snd_efw *efw)
+{
+ int err;
+
+ err = init_stream(efw, &efw->tx_stream);
+ if (err < 0)
+ goto end;
+ /* Fireworks transmits NODATA packets with TAG0. */
+ efw->tx_stream.flags |= CIP_EMPTY_WITH_TAG0;
+ /* Fireworks has its own meaning for dbc. */
+ efw->tx_stream.flags |= CIP_DBC_IS_END_EVENT;
+ /* Fireworks reset dbc at bus reset. */
+ efw->tx_stream.flags |= CIP_SKIP_DBC_ZERO_CHECK;
+ /* AudioFire9 always reports wrong dbs. */
+ if (efw->is_af9)
+ efw->tx_stream.flags |= CIP_WRONG_DBS;
+ /* Firmware version 5.5 reports fixed interval for dbc. */
+ if (efw->firmware_version == 0x5050000)
+ efw->tx_stream.tx_dbc_interval = 8;
+
+ err = init_stream(efw, &efw->rx_stream);
+ if (err < 0) {
+ destroy_stream(efw, &efw->tx_stream);
+ goto end;
+ }
+ /*
+ * Fireworks ignores MIDI messages in more than first 8 data
+ * blocks of an received AMDTP packet.
+ */
+ efw->rx_stream.rx_blocks_for_midi = 8;
+
+ /* set IEC61883 compliant mode (actually not fully compliant...) */
+ err = snd_efw_command_set_tx_mode(efw, SND_EFW_TRANSPORT_MODE_IEC61883);
+ if (err < 0) {
+ destroy_stream(efw, &efw->tx_stream);
+ destroy_stream(efw, &efw->rx_stream);
+ }
+end:
+ return err;
+}
+
+int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
+{
+ struct amdtp_stream *master, *slave;
+ atomic_t *slave_substreams;
+ enum cip_flags sync_mode;
+ unsigned int curr_rate;
+ int err = 0;
+
+ mutex_lock(&efw->mutex);
+
+ /* Need no substreams */
+ if ((atomic_read(&efw->playback_substreams) == 0) &&
+ (atomic_read(&efw->capture_substreams) == 0))
+ goto end;
+
+ err = get_sync_mode(efw, &sync_mode);
+ if (err < 0)
+ goto end;
+ if (sync_mode == CIP_SYNC_TO_DEVICE) {
+ master = &efw->tx_stream;
+ slave = &efw->rx_stream;
+ slave_substreams = &efw->playback_substreams;
+ } else {
+ master = &efw->rx_stream;
+ slave = &efw->tx_stream;
+ slave_substreams = &efw->capture_substreams;
+ }
+
+ /*
+ * Considering JACK/FFADO streaming:
+ * TODO: This can be removed hwdep functionality becomes popular.
+ */
+ err = check_connection_used_by_others(efw, master);
+ if (err < 0)
+ goto end;
+
+ /* packet queueing error */
+ if (amdtp_streaming_error(slave))
+ stop_stream(efw, slave);
+ if (amdtp_streaming_error(master))
+ stop_stream(efw, master);
+
+ /* stop streams if rate is different */
+ err = snd_efw_command_get_sampling_rate(efw, &curr_rate);
+ if (err < 0)
+ goto end;
+ if (rate == 0)
+ rate = curr_rate;
+ if (rate != curr_rate) {
+ stop_stream(efw, slave);
+ stop_stream(efw, master);
+ }
+
+ /* master should be always running */
+ if (!amdtp_stream_running(master)) {
+ amdtp_stream_set_sync(sync_mode, master, slave);
+ efw->master = master;
+
+ err = snd_efw_command_set_sampling_rate(efw, rate);
+ if (err < 0)
+ goto end;
+
+ err = start_stream(efw, master, rate);
+ if (err < 0) {
+ dev_err(&efw->unit->device,
+ "fail to start AMDTP master stream:%d\n", err);
+ goto end;
+ }
+ }
+
+ /* start slave if needed */
+ if (atomic_read(slave_substreams) > 0 && !amdtp_stream_running(slave)) {
+ err = start_stream(efw, slave, rate);
+ if (err < 0) {
+ dev_err(&efw->unit->device,
+ "fail to start AMDTP slave stream:%d\n", err);
+ stop_stream(efw, master);
+ }
+ }
+end:
+ mutex_unlock(&efw->mutex);
+ return err;
+}
+
+void snd_efw_stream_stop_duplex(struct snd_efw *efw)
+{
+ struct amdtp_stream *master, *slave;
+ atomic_t *master_substreams, *slave_substreams;
+
+ mutex_lock(&efw->mutex);
+
+ if (efw->master == &efw->rx_stream) {
+ slave = &efw->tx_stream;
+ master = &efw->rx_stream;
+ slave_substreams = &efw->capture_substreams;
+ master_substreams = &efw->playback_substreams;
+ } else {
+ slave = &efw->rx_stream;
+ master = &efw->tx_stream;
+ slave_substreams = &efw->playback_substreams;
+ master_substreams = &efw->capture_substreams;
+ }
+
+ if (atomic_read(slave_substreams) == 0) {
+ stop_stream(efw, slave);
+
+ if (atomic_read(master_substreams) == 0)
+ stop_stream(efw, master);
+ }
+
+ mutex_unlock(&efw->mutex);
+}
+
+void snd_efw_stream_update_duplex(struct snd_efw *efw)
+{
+ if ((cmp_connection_update(&efw->out_conn) < 0) ||
+ (cmp_connection_update(&efw->in_conn) < 0)) {
+ mutex_lock(&efw->mutex);
+ stop_stream(efw, &efw->rx_stream);
+ stop_stream(efw, &efw->tx_stream);
+ mutex_unlock(&efw->mutex);
+ } else {
+ amdtp_stream_update(&efw->rx_stream);
+ amdtp_stream_update(&efw->tx_stream);
+ }
+}
+
+void snd_efw_stream_destroy_duplex(struct snd_efw *efw)
+{
+ mutex_lock(&efw->mutex);
+
+ destroy_stream(efw, &efw->rx_stream);
+ destroy_stream(efw, &efw->tx_stream);
+
+ mutex_unlock(&efw->mutex);
+}
+
+void snd_efw_stream_lock_changed(struct snd_efw *efw)
+{
+ efw->dev_lock_changed = true;
+ wake_up(&efw->hwdep_wait);
+}
+
+int snd_efw_stream_lock_try(struct snd_efw *efw)
+{
+ int err;
+
+ spin_lock_irq(&efw->lock);
+
+ /* user land lock this */
+ if (efw->dev_lock_count < 0) {
+ err = -EBUSY;
+ goto end;
+ }
+
+ /* this is the first time */
+ if (efw->dev_lock_count++ == 0)
+ snd_efw_stream_lock_changed(efw);
+ err = 0;
+end:
+ spin_unlock_irq(&efw->lock);
+ return err;
+}
+
+void snd_efw_stream_lock_release(struct snd_efw *efw)
+{
+ spin_lock_irq(&efw->lock);
+
+ if (WARN_ON(efw->dev_lock_count <= 0))
+ goto end;
+ if (--efw->dev_lock_count == 0)
+ snd_efw_stream_lock_changed(efw);
+end:
+ spin_unlock_irq(&efw->lock);
+}
diff --git a/sound/firewire/fireworks/fireworks_transaction.c b/sound/firewire/fireworks/fireworks_transaction.c
new file mode 100644
index 000000000000..aa56b8ac537c
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_transaction.c
@@ -0,0 +1,326 @@
+/*
+ * fireworks_transaction.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2013-2014 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * Fireworks have its own transaction. The transaction can be delivered by AV/C
+ * Vendor Specific command. But at least Windows driver and firmware version 5.5
+ * or later don't use it.
+ *
+ * Transaction substance:
+ * At first, 6 data exist. Following to the 6 data, parameters for each
+ * commands exists. All of parameters are 32 bit alighed to big endian.
+ * data[0]: Length of transaction substance
+ * data[1]: Transaction version
+ * data[2]: Sequence number. This is incremented by the device
+ * data[3]: transaction category
+ * data[4]: transaction command
+ * data[5]: return value in response.
+ * data[6-]: parameters
+ *
+ * Transaction address:
+ * command: 0xecc000000000
+ * response: 0xecc080000000 (default)
+ *
+ * I note that the address for response can be changed by command. But this
+ * module uses the default address.
+ */
+#include "./fireworks.h"
+
+#define MEMORY_SPACE_EFW_COMMAND 0xecc000000000ULL
+#define MEMORY_SPACE_EFW_RESPONSE 0xecc080000000ULL
+
+#define ERROR_RETRIES 3
+#define ERROR_DELAY_MS 5
+#define EFC_TIMEOUT_MS 125
+
+static DEFINE_SPINLOCK(instances_lock);
+static struct snd_efw *instances[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
+static DEFINE_SPINLOCK(transaction_queues_lock);
+static LIST_HEAD(transaction_queues);
+
+enum transaction_queue_state {
+ STATE_PENDING,
+ STATE_BUS_RESET,
+ STATE_COMPLETE
+};
+
+struct transaction_queue {
+ struct list_head list;
+ struct fw_unit *unit;
+ void *buf;
+ unsigned int size;
+ u32 seqnum;
+ enum transaction_queue_state state;
+ wait_queue_head_t wait;
+};
+
+int snd_efw_transaction_cmd(struct fw_unit *unit,
+ const void *cmd, unsigned int size)
+{
+ return snd_fw_transaction(unit, TCODE_WRITE_BLOCK_REQUEST,
+ MEMORY_SPACE_EFW_COMMAND,
+ (void *)cmd, size, 0);
+}
+
+int snd_efw_transaction_run(struct fw_unit *unit,
+ const void *cmd, unsigned int cmd_size,
+ void *resp, unsigned int resp_size)
+{
+ struct transaction_queue t;
+ unsigned int tries;
+ int ret;
+
+ t.unit = unit;
+ t.buf = resp;
+ t.size = resp_size;
+ t.seqnum = be32_to_cpu(((struct snd_efw_transaction *)cmd)->seqnum) + 1;
+ t.state = STATE_PENDING;
+ init_waitqueue_head(&t.wait);
+
+ spin_lock_irq(&transaction_queues_lock);
+ list_add_tail(&t.list, &transaction_queues);
+ spin_unlock_irq(&transaction_queues_lock);
+
+ tries = 0;
+ do {
+ ret = snd_efw_transaction_cmd(t.unit, (void *)cmd, cmd_size);
+ if (ret < 0)
+ break;
+
+ wait_event_timeout(t.wait, t.state != STATE_PENDING,
+ msecs_to_jiffies(EFC_TIMEOUT_MS));
+
+ if (t.state == STATE_COMPLETE) {
+ ret = t.size;
+ break;
+ } else if (t.state == STATE_BUS_RESET) {
+ msleep(ERROR_DELAY_MS);
+ } else if (++tries >= ERROR_RETRIES) {
+ dev_err(&t.unit->device, "EFW transaction timed out\n");
+ ret = -EIO;
+ break;
+ }
+ } while (1);
+
+ spin_lock_irq(&transaction_queues_lock);
+ list_del(&t.list);
+ spin_unlock_irq(&transaction_queues_lock);
+
+ return ret;
+}
+
+static void
+copy_resp_to_buf(struct snd_efw *efw, void *data, size_t length, int *rcode)
+{
+ size_t capacity, till_end;
+ struct snd_efw_transaction *t;
+
+ spin_lock_irq(&efw->lock);
+
+ t = (struct snd_efw_transaction *)data;
+ length = min_t(size_t, t->length * sizeof(t->length), length);
+
+ if (efw->push_ptr < efw->pull_ptr)
+ capacity = (unsigned int)(efw->pull_ptr - efw->push_ptr);
+ else
+ capacity = snd_efw_resp_buf_size -
+ (unsigned int)(efw->push_ptr - efw->pull_ptr);
+
+ /* confirm enough space for this response */
+ if (capacity < length) {
+ *rcode = RCODE_CONFLICT_ERROR;
+ goto end;
+ }
+
+ /* copy to ring buffer */
+ while (length > 0) {
+ till_end = snd_efw_resp_buf_size -
+ (unsigned int)(efw->push_ptr - efw->resp_buf);
+ till_end = min_t(unsigned int, length, till_end);
+
+ memcpy(efw->push_ptr, data, till_end);
+
+ efw->push_ptr += till_end;
+ if (efw->push_ptr >= efw->resp_buf + snd_efw_resp_buf_size)
+ efw->push_ptr = efw->resp_buf;
+
+ length -= till_end;
+ data += till_end;
+ }
+
+ /* for hwdep */
+ efw->resp_queues++;
+ wake_up(&efw->hwdep_wait);
+
+ *rcode = RCODE_COMPLETE;
+end:
+ spin_unlock_irq(&efw->lock);
+}
+
+static void
+handle_resp_for_user(struct fw_card *card, int generation, int source,
+ void *data, size_t length, int *rcode)
+{
+ struct fw_device *device;
+ struct snd_efw *efw;
+ unsigned int i;
+
+ spin_lock_irq(&instances_lock);
+
+ for (i = 0; i < SNDRV_CARDS; i++) {
+ efw = instances[i];
+ if (efw == NULL)
+ continue;
+ device = fw_parent_device(efw->unit);
+ if ((device->card != card) ||
+ (device->generation != generation))
+ continue;
+ smp_rmb(); /* node id vs. generation */
+ if (device->node_id != source)
+ continue;
+
+ break;
+ }
+ if (i == SNDRV_CARDS)
+ goto end;
+
+ copy_resp_to_buf(efw, data, length, rcode);
+end:
+ spin_unlock_irq(&instances_lock);
+}
+
+static void
+handle_resp_for_kernel(struct fw_card *card, int generation, int source,
+ void *data, size_t length, int *rcode, u32 seqnum)
+{
+ struct fw_device *device;
+ struct transaction_queue *t;
+ unsigned long flags;
+
+ spin_lock_irqsave(&transaction_queues_lock, flags);
+ list_for_each_entry(t, &transaction_queues, list) {
+ device = fw_parent_device(t->unit);
+ if ((device->card != card) ||
+ (device->generation != generation))
+ continue;
+ smp_rmb(); /* node_id vs. generation */
+ if (device->node_id != source)
+ continue;
+
+ if ((t->state == STATE_PENDING) && (t->seqnum == seqnum)) {
+ t->state = STATE_COMPLETE;
+ t->size = min_t(unsigned int, length, t->size);
+ memcpy(t->buf, data, t->size);
+ wake_up(&t->wait);
+ *rcode = RCODE_COMPLETE;
+ }
+ }
+ spin_unlock_irqrestore(&transaction_queues_lock, flags);
+}
+
+static void
+efw_response(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ int rcode, dummy;
+ u32 seqnum;
+
+ rcode = RCODE_TYPE_ERROR;
+ if (length < sizeof(struct snd_efw_transaction)) {
+ rcode = RCODE_DATA_ERROR;
+ goto end;
+ } else if (offset != MEMORY_SPACE_EFW_RESPONSE) {
+ rcode = RCODE_ADDRESS_ERROR;
+ goto end;
+ }
+
+ seqnum = be32_to_cpu(((struct snd_efw_transaction *)data)->seqnum);
+ if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX + 1) {
+ handle_resp_for_kernel(card, generation, source,
+ data, length, &rcode, seqnum);
+ if (snd_efw_resp_buf_debug)
+ handle_resp_for_user(card, generation, source,
+ data, length, &dummy);
+ } else {
+ handle_resp_for_user(card, generation, source,
+ data, length, &rcode);
+ }
+end:
+ fw_send_response(card, request, rcode);
+}
+
+void snd_efw_transaction_add_instance(struct snd_efw *efw)
+{
+ unsigned int i;
+
+ spin_lock_irq(&instances_lock);
+
+ for (i = 0; i < SNDRV_CARDS; i++) {
+ if (instances[i] != NULL)
+ continue;
+ instances[i] = efw;
+ break;
+ }
+
+ spin_unlock_irq(&instances_lock);
+}
+
+void snd_efw_transaction_remove_instance(struct snd_efw *efw)
+{
+ unsigned int i;
+
+ spin_lock_irq(&instances_lock);
+
+ for (i = 0; i < SNDRV_CARDS; i++) {
+ if (instances[i] != efw)
+ continue;
+ instances[i] = NULL;
+ }
+
+ spin_unlock_irq(&instances_lock);
+}
+
+void snd_efw_transaction_bus_reset(struct fw_unit *unit)
+{
+ struct transaction_queue *t;
+
+ spin_lock_irq(&transaction_queues_lock);
+ list_for_each_entry(t, &transaction_queues, list) {
+ if ((t->unit == unit) &&
+ (t->state == STATE_PENDING)) {
+ t->state = STATE_BUS_RESET;
+ wake_up(&t->wait);
+ }
+ }
+ spin_unlock_irq(&transaction_queues_lock);
+}
+
+static struct fw_address_handler resp_register_handler = {
+ .length = SND_EFW_RESPONSE_MAXIMUM_BYTES,
+ .address_callback = efw_response
+};
+
+int snd_efw_transaction_register(void)
+{
+ static const struct fw_address_region resp_register_region = {
+ .start = MEMORY_SPACE_EFW_RESPONSE,
+ .end = MEMORY_SPACE_EFW_RESPONSE +
+ SND_EFW_RESPONSE_MAXIMUM_BYTES
+ };
+ return fw_core_add_address_handler(&resp_register_handler,
+ &resp_register_region);
+}
+
+void snd_efw_transaction_unregister(void)
+{
+ WARN_ON(!list_empty(&transaction_queues));
+ fw_core_remove_address_handler(&resp_register_handler);
+}
diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c
index 9f7ef219b109..768d40ddfebb 100644
--- a/sound/firewire/speakers.c
+++ b/sound/firewire/speakers.c
@@ -51,7 +51,7 @@ struct fwspk {
const struct device_info *device_info;
struct mutex mutex;
struct cmp_connection connection;
- struct amdtp_out_stream stream;
+ struct amdtp_stream stream;
bool mute;
s16 volume[6];
s16 volume_min;
@@ -167,13 +167,7 @@ static int fwspk_open(struct snd_pcm_substream *substream)
if (err < 0)
return err;
- err = snd_pcm_hw_constraint_minmax(runtime,
- SNDRV_PCM_HW_PARAM_PERIOD_TIME,
- 5000, UINT_MAX);
- if (err < 0)
- return err;
-
- err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ err = amdtp_stream_add_pcm_hw_constraints(&fwspk->stream, runtime);
if (err < 0)
return err;
@@ -187,48 +181,12 @@ static int fwspk_close(struct snd_pcm_substream *substream)
static void fwspk_stop_stream(struct fwspk *fwspk)
{
- if (amdtp_out_stream_running(&fwspk->stream)) {
- amdtp_out_stream_stop(&fwspk->stream);
+ if (amdtp_stream_running(&fwspk->stream)) {
+ amdtp_stream_stop(&fwspk->stream);
cmp_connection_break(&fwspk->connection);
}
}
-static int fwspk_set_rate(struct fwspk *fwspk, unsigned int sfc)
-{
- u8 *buf;
- int err;
-
- buf = kmalloc(8, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- buf[0] = 0x00; /* AV/C, CONTROL */
- buf[1] = 0xff; /* unit */
- buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */
- buf[3] = 0x00; /* plug 0 */
- buf[4] = 0x90; /* format: audio */
- buf[5] = 0x00 | sfc; /* AM824, frequency */
- buf[6] = 0xff; /* SYT (not used) */
- buf[7] = 0xff;
-
- err = fcp_avc_transaction(fwspk->unit, buf, 8, buf, 8,
- BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5));
- if (err < 0)
- goto error;
- if (err < 6 || buf[0] != 0x09 /* ACCEPTED */) {
- dev_err(&fwspk->unit->device, "failed to set sample rate\n");
- err = -EIO;
- goto error;
- }
-
- err = 0;
-
-error:
- kfree(buf);
-
- return err;
-}
-
static int fwspk_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
@@ -244,17 +202,20 @@ static int fwspk_hw_params(struct snd_pcm_substream *substream,
if (err < 0)
goto error;
- amdtp_out_stream_set_parameters(&fwspk->stream,
- params_rate(hw_params),
- params_channels(hw_params),
- 0);
+ amdtp_stream_set_parameters(&fwspk->stream,
+ params_rate(hw_params),
+ params_channels(hw_params),
+ 0);
- amdtp_out_stream_set_pcm_format(&fwspk->stream,
- params_format(hw_params));
+ amdtp_stream_set_pcm_format(&fwspk->stream,
+ params_format(hw_params));
- err = fwspk_set_rate(fwspk, fwspk->stream.sfc);
- if (err < 0)
+ err = avc_general_set_sig_fmt(fwspk->unit, params_rate(hw_params),
+ AVC_GENERAL_PLUG_DIR_IN, 0);
+ if (err < 0) {
+ dev_err(&fwspk->unit->device, "failed to set sample rate\n");
goto err_buffer;
+ }
return 0;
@@ -282,25 +243,25 @@ static int fwspk_prepare(struct snd_pcm_substream *substream)
mutex_lock(&fwspk->mutex);
- if (amdtp_out_streaming_error(&fwspk->stream))
+ if (amdtp_streaming_error(&fwspk->stream))
fwspk_stop_stream(fwspk);
- if (!amdtp_out_stream_running(&fwspk->stream)) {
+ if (!amdtp_stream_running(&fwspk->stream)) {
err = cmp_connection_establish(&fwspk->connection,
- amdtp_out_stream_get_max_payload(&fwspk->stream));
+ amdtp_stream_get_max_payload(&fwspk->stream));
if (err < 0)
goto err_mutex;
- err = amdtp_out_stream_start(&fwspk->stream,
- fwspk->connection.resources.channel,
- fwspk->connection.speed);
+ err = amdtp_stream_start(&fwspk->stream,
+ fwspk->connection.resources.channel,
+ fwspk->connection.speed);
if (err < 0)
goto err_connection;
}
mutex_unlock(&fwspk->mutex);
- amdtp_out_stream_pcm_prepare(&fwspk->stream);
+ amdtp_stream_pcm_prepare(&fwspk->stream);
return 0;
@@ -327,7 +288,7 @@ static int fwspk_trigger(struct snd_pcm_substream *substream, int cmd)
default:
return -EINVAL;
}
- amdtp_out_stream_pcm_trigger(&fwspk->stream, pcm);
+ amdtp_stream_pcm_trigger(&fwspk->stream, pcm);
return 0;
}
@@ -335,7 +296,7 @@ static snd_pcm_uframes_t fwspk_pointer(struct snd_pcm_substream *substream)
{
struct fwspk *fwspk = substream->private_data;
- return amdtp_out_stream_pcm_pointer(&fwspk->stream);
+ return amdtp_stream_pcm_pointer(&fwspk->stream);
}
static int fwspk_create_pcm(struct fwspk *fwspk)
@@ -653,7 +614,7 @@ static void fwspk_card_free(struct snd_card *card)
{
struct fwspk *fwspk = card->private_data;
- amdtp_out_stream_destroy(&fwspk->stream);
+ amdtp_stream_destroy(&fwspk->stream);
cmp_connection_destroy(&fwspk->connection);
fw_unit_put(fwspk->unit);
mutex_destroy(&fwspk->mutex);
@@ -679,11 +640,12 @@ static int fwspk_probe(struct fw_unit *unit,
fwspk->unit = fw_unit_get(unit);
fwspk->device_info = (const struct device_info *)id->driver_data;
- err = cmp_connection_init(&fwspk->connection, unit, 0);
+ err = cmp_connection_init(&fwspk->connection, unit, CMP_INPUT, 0);
if (err < 0)
goto err_unit;
- err = amdtp_out_stream_init(&fwspk->stream, unit, CIP_NONBLOCKING);
+ err = amdtp_stream_init(&fwspk->stream, unit, AMDTP_OUT_STREAM,
+ CIP_NONBLOCKING);
if (err < 0)
goto err_connection;
@@ -733,21 +695,21 @@ static void fwspk_bus_reset(struct fw_unit *unit)
fcp_bus_reset(fwspk->unit);
if (cmp_connection_update(&fwspk->connection) < 0) {
- amdtp_out_stream_pcm_abort(&fwspk->stream);
+ amdtp_stream_pcm_abort(&fwspk->stream);
mutex_lock(&fwspk->mutex);
fwspk_stop_stream(fwspk);
mutex_unlock(&fwspk->mutex);
return;
}
- amdtp_out_stream_update(&fwspk->stream);
+ amdtp_stream_update(&fwspk->stream);
}
static void fwspk_remove(struct fw_unit *unit)
{
struct fwspk *fwspk = dev_get_drvdata(&unit->device);
- amdtp_out_stream_pcm_abort(&fwspk->stream);
+ amdtp_stream_pcm_abort(&fwspk->stream);
snd_card_disconnect(fwspk->card);
mutex_lock(&fwspk->mutex);
diff --git a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c
index 6496822c1808..1ff78ec9f0ac 100644
--- a/sound/isa/sb/sb_mixer.c
+++ b/sound/isa/sb/sb_mixer.c
@@ -818,12 +818,14 @@ int snd_sbmixer_new(struct snd_sb *chip)
return err;
break;
case SB_HW_DT019X:
- if ((err = snd_sbmixer_init(chip,
- snd_dt019x_controls,
- ARRAY_SIZE(snd_dt019x_controls),
- snd_dt019x_init_values,
- ARRAY_SIZE(snd_dt019x_init_values),
- "DT019X")) < 0)
+ err = snd_sbmixer_init(chip,
+ snd_dt019x_controls,
+ ARRAY_SIZE(snd_dt019x_controls),
+ snd_dt019x_init_values,
+ ARRAY_SIZE(snd_dt019x_init_values),
+ "DT019X");
+ if (err < 0)
+ return err;
break;
default:
strcpy(card->mixername, "???");
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index d3eaac020a38..ab0fd6d4961b 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -1367,6 +1367,12 @@ static int azx_first_init(struct azx *chip)
/* initialize streams */
azx_init_stream(chip);
+ /* workaround for Broadwell HDMI: the first stream is broken,
+ * so mask it by keeping it as if opened
+ */
+ if (pci->vendor == 0x8086 && pci->device == 0x160c)
+ chip->azx_dev[0].opened = 1;
+
/* initialize chip */
azx_init_pci(chip);
azx_init_chip(chip, (probe_only[dev] & 2) == 0);
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 11d1c5d303f8..be0a9ee0b804 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -3329,6 +3329,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
{ .id = 0x10de0051, .name = "GPU 51 HDMI/DP", .patch = patch_nvhdmi },
{ .id = 0x10de0060, .name = "GPU 60 HDMI/DP", .patch = patch_nvhdmi },
{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
+{ .id = 0x10de0071, .name = "GPU 71 HDMI/DP", .patch = patch_nvhdmi },
{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
{ .id = 0x11069f80, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi },
{ .id = 0x11069f81, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi },
@@ -3385,6 +3386,7 @@ MODULE_ALIAS("snd-hda-codec-id:10de0044");
MODULE_ALIAS("snd-hda-codec-id:10de0051");
MODULE_ALIAS("snd-hda-codec-id:10de0060");
MODULE_ALIAS("snd-hda-codec-id:10de0067");
+MODULE_ALIAS("snd-hda-codec-id:10de0071");
MODULE_ALIAS("snd-hda-codec-id:10de8001");
MODULE_ALIAS("snd-hda-codec-id:11069f80");
MODULE_ALIAS("snd-hda-codec-id:11069f81");
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 7a800a8ee215..08bb06969a57 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -4760,6 +4760,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x0653, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0657, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0658, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x065c, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x065f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0662, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0667, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
@@ -4768,6 +4769,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x0674, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x067e, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x067f, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0680, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0684, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x15cc, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x15cd, "Dell X5 Precision", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),