--- /dev/null
+Code of Conflict
+----------------
+
+The Linux kernel development effort is a very personal process compared
+to "traditional" ways of developing software. Your code and ideas
+behind it will be carefully reviewed, often resulting in critique and
+criticism. The review will almost always require improvements to the
+code before it can be included in the kernel. Know that this happens
+because everyone involved wants to see the best possible solution for
+the overall success of Linux. This development process has been proven
+to create the most robust operating system kernel ever, and we do not
+want to do anything to cause the quality of submission and eventual
+result to ever decrease.
+
+If however, anyone feels personally abused, threatened, or otherwise
+uncomfortable due to this process, that is not acceptable. If so,
+please contact the Linux Foundation's Technical Advisory Board at
+<tab@lists.linux-foundation.org>, or the individual members, and they
+will work to resolve the issue to the best of their ability. For more
+information on who is on the Technical Advisory Board and what their
+role is, please see:
+ http://www.linuxfoundation.org/programs/advisory-councils/tab
+
+As a reviewer of code, please strive to keep things civil and focused on
+the technical issues involved. We are all humans, and frustrations can
+be high on both sides of the process. Try to keep in mind the immortal
+words of Bill and Ted, "Be excellent to each other."
be understood as an underflow into the highest possible value, -2 or
-10M etc. do not work, so it's not consistent.
- memory.low, memory.high, and memory.max will use the string
- "infinity" to indicate and set the highest possible value.
+ memory.low, memory.high, and memory.max will use the string "max" to
+ indicate and set the highest possible value.
5. Planned Changes
- pclkN, clkN: Pairs of parent of input clock and input clock to the
devices in this power domain. Maximum of 4 pairs (N = 0 to 3)
are supported currently.
+- power-domains: phandle pointing to the parent power domain, for more details
+ see Documentation/devicetree/bindings/power/power_domain.txt
Node of a device using power domains must have a power-domains property
defined with a phandle to respective power domain.
Required root node property:
compatible = "st,stih407";
+Boards with the ST STiH410 SoC shall have the following properties:
+Required root node property:
+compatible = "st,stih410";
+
Boards with the ST STiH418 SoC shall have the following properties:
Required root node property:
compatible = "st,stih418";
- "fsl,vf610-i2c" for I2C compatible with the one integrated on Vybrid vf610 SoC
- reg : Should contain I2C/HS-I2C registers location and length
- interrupts : Should contain I2C/HS-I2C interrupt
+- clocks : Should contain the I2C/HS-I2C clock specifier
Optional properties:
- clock-frequency : Constains desired I2C/HS-I2C bus clock frequency in Hz.
- amd,serdes-cdr-rate: CDR rate speed selection
- amd,serdes-pq-skew: PQ (data sampling) skew
- amd,serdes-tx-amp: TX amplitude boost
+- amd,serdes-dfe-tap-config: DFE taps available to run
+- amd,serdes-dfe-tap-enable: DFE taps to enable
Example:
xgbe_phy@e1240800 {
amd,serdes-cdr-rate = <2>, <2>, <7>;
amd,serdes-pq-skew = <10>, <10>, <30>;
amd,serdes-tx-amp = <15>, <15>, <10>;
+ amd,serdes-dfe-tap-config = <3>, <3>, <1>;
+ amd,serdes-dfe-tap-enable = <0>, <0>, <127>;
};
APM X-Gene SoC.
Required properties for all the ethernet interfaces:
-- compatible: Should be "apm,xgene-enet"
+- compatible: Should state binding information from the following list,
+ - "apm,xgene-enet": RGMII based 1G interface
+ - "apm,xgene1-sgenet": SGMII based 1G interface
+ - "apm,xgene1-xgenet": XFI based 10G interface
- reg: Address and length of the register set for the device. It contains the
information of registers in the same order as described by reg-names
- reg-names: Should contain the register set names
(DSA_MAX_SWITCHES).
Each of these switch child nodes should have the following required properties:
-- reg : Describes the switch address on the MII bus
+- reg : Contains two fields. The first one describes the
+ address on the MII bus. The second is the switch
+ number that must be unique in cascaded configurations
- #address-cells : Must be 1
- #size-cells : Must be 0
providing multiple PM domains (e.g. power controllers), but can be any value
as specified by device tree binding documentation of particular provider.
+Optional properties:
+ - power-domains : A phandle and PM domain specifier as defined by bindings of
+ the power controller specified by phandle.
+ Some power domains might be powered from another power domain (or have
+ other hardware specific dependencies). For representing such dependency
+ a standard PM domain consumer binding is used. When provided, all domains
+ created by the given provider should be subdomains of the domain
+ specified by this binding. More details about power domain specifier are
+ available in the next section.
+
Example:
power: power-controller@12340000 {
The node above defines a power controller that is a PM domain provider and
expects one cell as its phandle argument.
+Example 2:
+
+ parent: power-controller@12340000 {
+ compatible = "foo,power-controller";
+ reg = <0x12340000 0x1000>;
+ #power-domain-cells = <1>;
+ };
+
+ child: power-controller@12340000 {
+ compatible = "foo,power-controller";
+ reg = <0x12341000 0x1000>;
+ power-domains = <&parent 0>;
+ #power-domain-cells = <1>;
+ };
+
+The nodes above define two power controllers: 'parent' and 'child'.
+Domains created by the 'child' power controller are subdomains of '0' power
+domain provided by the 'parent' power controller.
+
==PM domain consumers==
Required properties:
--- /dev/null
+* UART (Universal Asynchronous Receiver/Transmitter)
+
+Required properties:
+- compatible : one of:
+ - "ns8250"
+ - "ns16450"
+ - "ns16550a"
+ - "ns16550"
+ - "ns16750"
+ - "ns16850"
+ - For Tegra20, must contain "nvidia,tegra20-uart"
+ - For other Tegra, must contain '"nvidia,<chip>-uart",
+ "nvidia,tegra20-uart"' where <chip> is tegra30, tegra114, tegra124,
+ tegra132, or tegra210.
+ - "nxp,lpc3220-uart"
+ - "ralink,rt2880-uart"
+ - "ibm,qpace-nwp-serial"
+ - "altr,16550-FIFO32"
+ - "altr,16550-FIFO64"
+ - "altr,16550-FIFO128"
+ - "fsl,16550-FIFO64"
+ - "fsl,ns16550"
+ - "serial" if the port type is unknown.
+- reg : offset and length of the register set for the device.
+- interrupts : should contain uart interrupt.
+- clock-frequency : the input clock frequency for the UART
+ or
+ clocks phandle to refer to the clk used as per Documentation/devicetree
+ /bindings/clock/clock-bindings.txt
+
+Optional properties:
+- current-speed : the current active speed of the UART.
+- reg-offset : offset to apply to the mapbase from the start of the registers.
+- reg-shift : quantity to shift the register offsets by.
+- reg-io-width : the size (in bytes) of the IO accesses that should be
+ performed on the device. There are some systems that require 32-bit
+ accesses to the UART (e.g. TI davinci).
+- used-by-rtas : set to indicate that the port is in use by the OpenFirmware
+ RTAS and should not be registered.
+- no-loopback-test: set to indicate that the port does not implements loopback
+ test mode
+- fifo-size: the fifo size of the UART.
+- auto-flow-control: one way to enable automatic flow control support. The
+ driver is allowed to detect support for the capability even without this
+ property.
+
+Note:
+* fsl,ns16550:
+ ------------
+ Freescale DUART is very similar to the PC16552D (and to a
+ pair of NS16550A), albeit with some nonstandard behavior such as
+ erratum A-004737 (relating to incorrect BRK handling).
+
+ Represents a single port that is compatible with the DUART found
+ on many Freescale chips (examples include mpc8349, mpc8548,
+ mpc8641d, p4080 and ls2085a).
+
+Example:
+
+ uart@80230000 {
+ compatible = "ns8250";
+ reg = <0x80230000 0x100>;
+ clock-frequency = <3686400>;
+ interrupts = <10>;
+ reg-shift = <2>;
+ };
--- /dev/null
+ETRAX FS UART
+
+Required properties:
+- compatible : "axis,etraxfs-uart"
+- reg: offset and length of the register set for the device.
+- interrupts: device interrupt
+
+Optional properties:
+- {dtr,dsr,ri,cd}-gpios: specify a GPIO for DTR/DSR/RI/CD
+ line respectively.
+
+Example:
+
+serial@b00260000 {
+ compatible = "axis,etraxfs-uart";
+ reg = <0xb0026000 0x1000>;
+ interrupts = <68>;
+ status = "disabled";
+};
+++ /dev/null
-* UART (Universal Asynchronous Receiver/Transmitter)
-
-Required properties:
-- compatible : one of:
- - "ns8250"
- - "ns16450"
- - "ns16550a"
- - "ns16550"
- - "ns16750"
- - "ns16850"
- - For Tegra20, must contain "nvidia,tegra20-uart"
- - For other Tegra, must contain '"nvidia,<chip>-uart",
- "nvidia,tegra20-uart"' where <chip> is tegra30, tegra114, tegra124,
- tegra132, or tegra210.
- - "nxp,lpc3220-uart"
- - "ralink,rt2880-uart"
- - "ibm,qpace-nwp-serial"
- - "altr,16550-FIFO32"
- - "altr,16550-FIFO64"
- - "altr,16550-FIFO128"
- - "fsl,16550-FIFO64"
- - "fsl,ns16550"
- - "serial" if the port type is unknown.
-- reg : offset and length of the register set for the device.
-- interrupts : should contain uart interrupt.
-- clock-frequency : the input clock frequency for the UART
- or
- clocks phandle to refer to the clk used as per Documentation/devicetree
- /bindings/clock/clock-bindings.txt
-
-Optional properties:
-- current-speed : the current active speed of the UART.
-- reg-offset : offset to apply to the mapbase from the start of the registers.
-- reg-shift : quantity to shift the register offsets by.
-- reg-io-width : the size (in bytes) of the IO accesses that should be
- performed on the device. There are some systems that require 32-bit
- accesses to the UART (e.g. TI davinci).
-- used-by-rtas : set to indicate that the port is in use by the OpenFirmware
- RTAS and should not be registered.
-- no-loopback-test: set to indicate that the port does not implements loopback
- test mode
-- fifo-size: the fifo size of the UART.
-- auto-flow-control: one way to enable automatic flow control support. The
- driver is allowed to detect support for the capability even without this
- property.
-
-Note:
-* fsl,ns16550:
- ------------
- Freescale DUART is very similar to the PC16552D (and to a
- pair of NS16550A), albeit with some nonstandard behavior such as
- erratum A-004737 (relating to incorrect BRK handling).
-
- Represents a single port that is compatible with the DUART found
- on many Freescale chips (examples include mpc8349, mpc8548,
- mpc8641d, p4080 and ls2085a).
-
-Example:
-
- uart@80230000 {
- compatible = "ns8250";
- reg = <0x80230000 0x100>;
- clock-frequency = <3686400>;
- interrupts = <10>;
- reg-shift = <2>;
- };
- reg-io-width : the size (in bytes) of the IO accesses that should be
performed on the device. If this property is not present then single byte
accesses are used.
+- dcd-override : Override the DCD modem status signal. This signal will always
+ be reported as active instead of being obtained from the modem status
+ register. Define this if your serial port does not use this pin.
+- dsr-override : Override the DTS modem status signal. This signal will always
+ be reported as active instead of being obtained from the modem status
+ register. Define this if your serial port does not use this pin.
+- cts-override : Override the CTS modem status signal. This signal will always
+ be reported as active instead of being obtained from the modem status
+ register. Define this if your serial port does not use this pin.
+- ri-override : Override the RI modem status signal. This signal will always be
+ reported as inactive instead of being obtained from the modem status register.
+ Define this if your serial port does not use this pin.
Example:
interrupts = <10>;
reg-shift = <2>;
reg-io-width = <4>;
+ dcd-override;
+ dsr-override;
+ cts-override;
+ ri-override;
};
Example with one clock:
* Headphones
* Speakers
* Mic Jack
+ * Int Mic
- nvidia,i2s-controller : The phandle of the Tegra I2S controller that's
connected to the CODEC.
devicetree@vger.kernel.org
+ and Cc: the DT maintainers. Use scripts/get_maintainer.pl to identify
+ all of the DT maintainers.
+
3) The Documentation/ portion of the patch should come in the series before
the code implementing the binding.
ams AMS AG
amstaos AMS-Taos Inc.
apm Applied Micro Circuits Corporation (APM)
+arasan Arasan Chip Systems
arm ARM Ltd.
armadeus ARMadeus Systems SARL
asahi-kasei Asahi Kasei Corp.
auo AU Optronics Corporation
avago Avago Technologies
avic Shanghai AVIC Optoelectronics Co., Ltd.
+axis Axis Communications AB
bosch Bosch Sensortec GmbH
brcm Broadcom Corporation
buffalo Buffalo, Inc.
- atmel,disable : Should be present if you want to disable the watchdog.
- atmel,idle-halt : Should be present if you want to stop the watchdog when
entering idle state.
+ CAUTION: This property should be used with care, it actually makes the
+ watchdog not counting when the CPU is in idle state, therefore the
+ watchdog reset time depends on mean CPU usage and will not reset at all
+ if the CPU stop working while it is in idle state, which is probably
+ not what you want.
- atmel,dbg-halt : Should be present if you want to stop the watchdog when
entering debug state.
dlmfs is built with OCFS2 as it requires most of its infrastructure.
-Project web page: http://oss.oracle.com/projects/ocfs2
-Tools web page: http://oss.oracle.com/projects/ocfs2-tools
+Project web page: http://ocfs2.wiki.kernel.org
+Tools web page: https://github.com/markfasheh/ocfs2-tools
OCFS2 mailing lists: http://oss.oracle.com/projects/ocfs2/mailman/
All code copyright 2005 Oracle except when otherwise noted.
You'll want to install the ocfs2-tools package in order to at least
get "mount.ocfs2" and "ocfs2_hb_ctl".
-Project web page: http://oss.oracle.com/projects/ocfs2
-Tools web page: http://oss.oracle.com/projects/ocfs2-tools
+Project web page: http://ocfs2.wiki.kernel.org
+Tools git tree: https://github.com/markfasheh/ocfs2-tools
OCFS2 mailing lists: http://oss.oracle.com/projects/ocfs2/mailman/
All code copyright 2005 Oracle except when otherwise noted.
byte 4: 0 y6 y5 y4 y3 y2 y1 y0
byte 5: 0 z6 z5 z4 z3 z2 z1 z0
+Protocol Version 2 DualPoint devices send standard PS/2 mouse packets for
+the DualPoint Stick.
+
Dualpoint device -- interleaved packet format
---------------------------------------------
byte 7: 0 y6 y5 y4 y3 y2 y1 y0
byte 8: 0 z6 z5 z4 z3 z2 z1 z0
+Devices which use the interleaving format normally send standard PS/2 mouse
+packets for the DualPoint Stick + ALPS Absolute Mode packets for the
+touchpad, switching to the interleaved packet format when both the stick and
+the touchpad are used at the same time.
+
ALPS Absolute Mode - Protocol Version 3
---------------------------------------
The kernel does not provide button emulation for such devices but treats
them as any other INPUT_PROP_BUTTONPAD device.
+INPUT_PROP_ACCELEROMETER
+-------------------------
+Directional axes on this device (absolute and/or relative x, y, z) represent
+accelerometer data. All other axes retain their meaning. A device must not mix
+regular directional axes and accelerometer axes on the same event node.
+
Guidelines:
==========
The guidelines below ensure proper single-touch and multi-finger functionality.
The type of approaching tool. A lot of kernel drivers cannot distinguish
between different tool types, such as a finger or a pen. In such cases, the
-event should be omitted. The protocol currently supports MT_TOOL_FINGER and
-MT_TOOL_PEN [2]. For type B devices, this event is handled by input core;
-drivers should instead use input_mt_report_slot_state().
+event should be omitted. The protocol currently supports MT_TOOL_FINGER,
+MT_TOOL_PEN, and MT_TOOL_PALM [2]. For type B devices, this event is handled
+by input core; drivers should instead use input_mt_report_slot_state().
+A contact's ABS_MT_TOOL_TYPE may change over time while still touching the
+device, because the firmware may not be able to determine which tool is being
+used when it first appears.
ABS_MT_BLOB_ID
The IRQF_NO_SUSPEND flag is used to indicate that to the IRQ subsystem when
requesting a special-purpose interrupt. It causes suspend_device_irqs() to
-leave the corresponding IRQ enabled so as to allow the interrupt to work all
-the time as expected.
+leave the corresponding IRQ enabled so as to allow the interrupt to work as
+expected during the suspend-resume cycle, but does not guarantee that the
+interrupt will wake the system from a suspended state -- for such cases it is
+necessary to use enable_irq_wake().
Note that the IRQF_NO_SUSPEND flag affects the entire IRQ and not just one
user of it. Thus, if the IRQ is shared, all of the interrupt handlers installed
IRQF_NO_SUSPEND and enable_irq_wake()
-------------------------------------
-There are no valid reasons to use both enable_irq_wake() and the IRQF_NO_SUSPEND
-flag on the same IRQ.
+There are very few valid reasons to use both enable_irq_wake() and the
+IRQF_NO_SUSPEND flag on the same IRQ, and it is never valid to use both for the
+same device.
First of all, if the IRQ is not shared, the rules for handling IRQF_NO_SUSPEND
interrupts (interrupt handlers are invoked after suspend_device_irqs()) are
Second, both enable_irq_wake() and IRQF_NO_SUSPEND apply to entire IRQs and not
to individual interrupt handlers, so sharing an IRQ between a system wakeup
-interrupt source and an IRQF_NO_SUSPEND interrupt source does not make sense.
+interrupt source and an IRQF_NO_SUSPEND interrupt source does not generally
+make sense.
+
+In rare cases an IRQ can be shared between a wakeup device driver and an
+IRQF_NO_SUSPEND user. In order for this to be safe, the wakeup device driver
+must be able to discern spurious IRQs from genuine wakeup events (signalling
+the latter to the core with pm_system_wakeup()), must use enable_irq_wake() to
+ensure that the IRQ will function as a wakeup source, and must request the IRQ
+with IRQF_COND_SUSPEND to tell the core that it meets these requirements. If
+these requirements are not met, it is not valid to use IRQF_COND_SUSPEND.
HDMI/DP (either HDMI or DisplayPort)
Exceptions (deprecated):
- [Digital] Capture Source
- [Digital] Capture Switch (aka input gain switch)
- [Digital] Capture Volume (aka input gain volume)
- [Digital] Playback Switch (aka output gain switch)
- [Digital] Playback Volume (aka output gain volume)
+ [Analogue|Digital] Capture Source
+ [Analogue|Digital] Capture Switch (aka input gain switch)
+ [Analogue|Digital] Capture Volume (aka input gain volume)
+ [Analogue|Digital] Playback Switch (aka output gain switch)
+ [Analogue|Digital] Playback Volume (aka output gain volume)
Tone Control - Switch
Tone Control - Bass
Tone Control - Treble
F: include/uapi/linux/kfd_ioctl.h
AMD MICROCODE UPDATE SUPPORT
-M: Andreas Herrmann <herrmann.der.user@googlemail.com>
-L: amd64-microcode@amd64.org
+M: Borislav Petkov <bp@alien8.de>
S: Maintained
F: arch/x86/kernel/cpu/microcode/amd*
F: arch/arm/boot/dts/imx*
F: arch/arm/configs/imx*_defconfig
+ARM/FREESCALE VYBRID ARM ARCHITECTURE
+M: Shawn Guo <shawn.guo@linaro.org>
+M: Sascha Hauer <kernel@pengutronix.de>
+R: Stefan Agner <stefan@agner.ch>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux.git
+F: arch/arm/mach-imx/*vf610*
+F: arch/arm/boot/dts/vf*
+
ARM/GLOMATION GESBC9312SX MACHINE SUPPORT
M: Lennert Buytenhek <kernel@wantstofly.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: arch/arm/mach-mvebu/
-F: drivers/rtc/armada38x-rtc
+F: drivers/rtc/rtc-armada38x.c
ARM/Marvell Berlin SoC support
M: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
M: Jason Cooper <jason@lakedaemon.net>
M: Andrew Lunn <andrew@lunn.ch>
M: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+M: Gregory Clement <gregory.clement@free-electrons.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: arch/arm/mach-dove/
F: drivers/*/*rockchip*
F: drivers/*/*/*rockchip*
F: sound/soc/rockchip/
+N: rockchip
ARM/SAMSUNG EXYNOS ARM ARCHITECTURES
M: Kukjin Kim <kgene@kernel.org>
F: include/linux/platform_data/at24.h
ATA OVER ETHERNET (AOE) DRIVER
-M: "Ed L. Cashin" <ecashin@coraid.com>
-W: http://support.coraid.com/support/linux
+M: "Ed L. Cashin" <ed.cashin@acm.org>
+W: http://www.openaoe.org/
S: Supported
F: Documentation/aoe/
F: drivers/block/aoe/
F: drivers/net/ethernet/atheros/
ATM
-M: Chas Williams <chas@cmf.nrl.navy.mil>
+M: Chas Williams <3chas3@gmail.com>
L: linux-atm-general@lists.sourceforge.net (moderated for non-subscribers)
L: netdev@vger.kernel.org
W: http://linux-atm.sourceforge.net
F: drivers/tty/serial/atmel_serial.c
ATMEL Audio ALSA driver
-M: Bo Shen <voice.shen@atmel.com>
+M: Nicolas Ferre <nicolas.ferre@atmel.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
S: Supported
F: sound/soc/atmel
BONDING DRIVER
M: Jay Vosburgh <j.vosburgh@gmail.com>
M: Veaceslav Falico <vfalico@gmail.com>
-M: Andy Gospodarek <andy@greyhouse.net>
+M: Andy Gospodarek <gospo@cumulusnetworks.com>
L: netdev@vger.kernel.org
W: http://sourceforge.net/projects/bonding/
S: Supported
BROADCOM BCM281XX/BCM11XXX/BCM216XX ARM ARCHITECTURE
M: Christian Daudt <bcm@fixthebug.org>
-M: Matt Porter <mporter@linaro.org>
M: Florian Fainelli <f.fainelli@gmail.com>
L: bcm-kernel-feedback-list@broadcom.com
T: git git://github.com/broadcom/mach-bcm
CAN NETWORK LAYER
M: Oliver Hartkopp <socketcan@hartkopp.net>
+M: Marc Kleine-Budde <mkl@pengutronix.de>
L: linux-can@vger.kernel.org
-W: http://gitorious.org/linux-can
+W: https://github.com/linux-can
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can.git
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next.git
S: Maintained
M: Wolfgang Grandegger <wg@grandegger.com>
M: Marc Kleine-Budde <mkl@pengutronix.de>
L: linux-can@vger.kernel.org
-W: http://gitorious.org/linux-can
+W: https://github.com/linux-can
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can.git
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next.git
S: Maintained
F: Documentation/hwmon/dme1737
F: drivers/hwmon/dme1737.c
+DMI/SMBIOS SUPPORT
+M: Jean Delvare <jdelvare@suse.de>
+S: Maintained
+F: drivers/firmware/dmi-id.c
+F: drivers/firmware/dmi_scan.c
+F: include/linux/dmi.h
+
DOCKING STATION DRIVER
M: Shaohua Li <shaohua.li@intel.com>
L: linux-acpi@vger.kernel.org
F: drivers/platform/x86/intel_menlow.c
INTEL IA32 MICROCODE UPDATE SUPPORT
-M: Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
+M: Borislav Petkov <bp@alien8.de>
S: Maintained
F: arch/x86/kernel/cpu/microcode/core*
F: arch/x86/kernel/cpu/microcode/intel*
S: Maintained
F: drivers/char/hw_random/ixp4xx-rng.c
-INTEL ETHERNET DRIVERS (e100/e1000/e1000e/fm10k/igb/igbvf/ixgb/ixgbe/ixgbevf/i40e/i40evf)
+INTEL ETHERNET DRIVERS
M: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
-M: Jesse Brandeburg <jesse.brandeburg@intel.com>
-M: Bruce Allan <bruce.w.allan@intel.com>
-M: Carolyn Wyborny <carolyn.wyborny@intel.com>
-M: Don Skidmore <donald.c.skidmore@intel.com>
-M: Greg Rose <gregory.v.rose@intel.com>
-M: Matthew Vick <matthew.vick@intel.com>
-M: John Ronciak <john.ronciak@intel.com>
-M: Mitch Williams <mitch.a.williams@intel.com>
-M: Linux NICS <linux.nics@intel.com>
-L: e1000-devel@lists.sourceforge.net
+R: Jesse Brandeburg <jesse.brandeburg@intel.com>
+R: Shannon Nelson <shannon.nelson@intel.com>
+R: Carolyn Wyborny <carolyn.wyborny@intel.com>
+R: Don Skidmore <donald.c.skidmore@intel.com>
+R: Matthew Vick <matthew.vick@intel.com>
+R: John Ronciak <john.ronciak@intel.com>
+R: Mitch Williams <mitch.a.williams@intel.com>
+L: intel-wired-lan@lists.osuosl.org
W: http://www.intel.com/support/feedback.htm
W: http://e1000.sourceforge.net/
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/net.git
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/net-next.git
+Q: http://patchwork.ozlabs.org/project/intel-wired-lan/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/net-queue.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/next-queue.git
S: Supported
F: Documentation/networking/e100.txt
F: Documentation/networking/e1000.txt
M: Mark Fasheh <mfasheh@suse.com>
M: Joel Becker <jlbec@evilplan.org>
L: ocfs2-devel@oss.oracle.com (moderated for non-subscribers)
-W: http://oss.oracle.com/projects/ocfs2/
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/jlbec/ocfs2.git
+W: http://ocfs2.wiki.kernel.org
S: Supported
F: Documentation/filesystems/ocfs2.txt
F: Documentation/filesystems/dlmfs.txt
L: netdev@vger.kernel.org
F: drivers/net/ethernet/samsung/sxgbe/
+SAMSUNG THERMAL DRIVER
+M: Lukasz Majewski <l.majewski@samsung.com>
+L: linux-pm@vger.kernel.org
+L: linux-samsung-soc@vger.kernel.org
+S: Supported
+T: https://github.com/lmajewski/linux-samsung-thermal.git
+F: drivers/thermal/samsung/
+
SAMSUNG USB2 PHY DRIVER
M: Kamil Debski <k.debski@samsung.com>
L: linux-kernel@vger.kernel.org
F: Documentation/usb/ohci.txt
F: drivers/usb/host/ohci*
+USB OTG FSM (Finite State Machine)
+M: Peter Chen <Peter.Chen@freescale.com>
+T: git git://github.com/hzpeterchen/linux-usb.git
+L: linux-usb@vger.kernel.org
+S: Maintained
+F: drivers/usb/common/usb-otg-fsm.c
+
USB OVER IP DRIVER
M: Valentina Manea <valentina.manea.m@gmail.com>
M: Shuah Khan <shuah.kh@samsung.com>
VERSION = 4
PATCHLEVEL = 0
SUBLEVEL = 0
-EXTRAVERSION = -rc1
+EXTRAVERSION = -rc7
NAME = Hurr durr I'ma sheep
# *DOCUMENTATION*
/* Forward declaration, a strange C thing */
struct task_struct;
-/* Return saved PC of a blocked thread */
-unsigned long thread_saved_pc(struct task_struct *t);
-
#define task_pt_regs(p) \
((struct pt_regs *)(THREAD_SIZE + (void *)task_stack_page(p)) - 1)
#define release_segments(mm) do { } while (0)
#define KSTK_EIP(tsk) (task_pt_regs(tsk)->ret)
+#define KSTK_ESP(tsk) (task_pt_regs(tsk)->sp)
/*
* Where abouts of Task's sp, fp, blink when it was last seen in kernel mode.
* Look in process.c for details of kernel stack layout
*/
-#define KSTK_ESP(tsk) (tsk->thread.ksp)
+#define TSK_K_ESP(tsk) (tsk->thread.ksp)
-#define KSTK_REG(tsk, off) (*((unsigned int *)(KSTK_ESP(tsk) + \
+#define TSK_K_REG(tsk, off) (*((unsigned int *)(TSK_K_ESP(tsk) + \
sizeof(struct callee_regs) + off)))
-#define KSTK_BLINK(tsk) KSTK_REG(tsk, 4)
-#define KSTK_FP(tsk) KSTK_REG(tsk, 0)
+#define TSK_K_BLINK(tsk) TSK_K_REG(tsk, 4)
+#define TSK_K_FP(tsk) TSK_K_REG(tsk, 0)
+
+#define thread_saved_pc(tsk) TSK_K_BLINK(tsk)
extern void start_thread(struct pt_regs * regs, unsigned long pc,
unsigned long usp);
--- /dev/null
+/*
+ * Copyright (C) 2014-15 Synopsys, Inc. (www.synopsys.com)
+ * Copyright (C) 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __ASM_STACKTRACE_H
+#define __ASM_STACKTRACE_H
+
+#include <linux/sched.h>
+
+/**
+ * arc_unwind_core - Unwind the kernel mode stack for an execution context
+ * @tsk: NULL for current task, specific task otherwise
+ * @regs: pt_regs used to seed the unwinder {SP, FP, BLINK, PC}
+ * If NULL, use pt_regs of @tsk (if !NULL) otherwise
+ * use the current values of {SP, FP, BLINK, PC}
+ * @consumer_fn: Callback invoked for each frame unwound
+ * Returns 0 to continue unwinding, -1 to stop
+ * @arg: Arg to callback
+ *
+ * Returns the address of first function in stack
+ *
+ * Semantics:
+ * - synchronous unwinding (e.g. dump_stack): @tsk NULL, @regs NULL
+ * - Asynchronous unwinding of sleeping task: @tsk !NULL, @regs NULL
+ * - Asynchronous unwinding of intr/excp etc: @tsk !NULL, @regs !NULL
+ */
+notrace noinline unsigned int arc_unwind_core(
+ struct task_struct *tsk, struct pt_regs *regs,
+ int (*consumer_fn) (unsigned int, void *),
+ void *arg);
+
+#endif /* __ASM_STACKTRACE_H */
return 0;
}
-/*
- * API: expected by schedular Code: If thread is sleeping where is that.
- * What is this good for? it will be always the scheduler or ret_from_fork.
- * So we hard code that anyways.
- */
-unsigned long thread_saved_pc(struct task_struct *t)
-{
- struct pt_regs *regs = task_pt_regs(t);
- unsigned long blink = 0;
-
- /*
- * If the thread being queried for in not itself calling this, then it
- * implies it is not executing, which in turn implies it is sleeping,
- * which in turn implies it got switched OUT by the schedular.
- * In that case, it's kernel mode blink can reliably retrieved as per
- * the picture above (right above pt_regs).
- */
- if (t != current && t->state != TASK_RUNNING)
- blink = *((unsigned int *)regs - 1);
-
- return blink;
-}
-
int elf_check_arch(const struct elf32_hdr *x)
{
unsigned int eflags;
sigset_t *set)
{
int err;
- err = __copy_to_user(&(sf->uc.uc_mcontext.regs), regs,
+ err = __copy_to_user(&(sf->uc.uc_mcontext.regs.scratch), regs,
sizeof(sf->uc.uc_mcontext.regs.scratch));
err |= __copy_to_user(&sf->uc.uc_sigmask, set, sizeof(sigset_t));
if (!err)
set_current_blocked(&set);
- err |= __copy_from_user(regs, &(sf->uc.uc_mcontext.regs),
+ err |= __copy_from_user(regs, &(sf->uc.uc_mcontext.regs.scratch),
sizeof(sf->uc.uc_mcontext.regs.scratch));
return err;
/* Don't restart from sigreturn */
syscall_wont_restart(regs);
+ /*
+ * Ensure that sigreturn always returns to user mode (in case the
+ * regs saved on user stack got fudged between save and sigreturn)
+ * Otherwise it is easy to panic the kernel with a custom
+ * signal handler and/or restorer which clobberes the status32/ret
+ * to return to a bogus location in kernel mode.
+ */
+ regs->status32 |= STATUS_U_MASK;
+
return regs->r0;
badframe:
/*
* handler returns using sigreturn stub provided already by userpsace
+ * If not, nuke the process right away
*/
- BUG_ON(!(ksig->ka.sa.sa_flags & SA_RESTORER));
+ if(!(ksig->ka.sa.sa_flags & SA_RESTORER))
+ return 1;
+
regs->blink = (unsigned long)ksig->ka.sa.sa_restorer;
/* User Stack for signal handler will be above the frame just carved */
handle_signal(struct ksignal *ksig, struct pt_regs *regs)
{
sigset_t *oldset = sigmask_to_save();
- int ret;
+ int failed;
/* Set up the stack frame */
- ret = setup_rt_frame(ksig, oldset, regs);
+ failed = setup_rt_frame(ksig, oldset, regs);
- signal_setup_done(ret, ksig, 0);
+ signal_setup_done(failed, ksig, 0);
}
void do_signal(struct pt_regs *regs)
struct pt_regs *regs,
struct unwind_frame_info *frame_info)
{
+ /*
+ * synchronous unwinding (e.g. dump_stack)
+ * - uses current values of SP and friends
+ */
if (tsk == NULL && regs == NULL) {
unsigned long fp, sp, blink, ret;
frame_info->task = current;
frame_info->regs.r63 = ret;
frame_info->call_frame = 0;
} else if (regs == NULL) {
+ /*
+ * Asynchronous unwinding of sleeping task
+ * - Gets SP etc from task's pt_regs (saved bottom of kernel
+ * mode stack of task)
+ */
frame_info->task = tsk;
- frame_info->regs.r27 = KSTK_FP(tsk);
- frame_info->regs.r28 = KSTK_ESP(tsk);
- frame_info->regs.r31 = KSTK_BLINK(tsk);
+ frame_info->regs.r27 = TSK_K_FP(tsk);
+ frame_info->regs.r28 = TSK_K_ESP(tsk);
+ frame_info->regs.r31 = TSK_K_BLINK(tsk);
frame_info->regs.r63 = (unsigned int)__switch_to;
/* In the prologue of __switch_to, first FP is saved on stack
frame_info->call_frame = 0;
} else {
+ /*
+ * Asynchronous unwinding of intr/exception
+ * - Just uses the pt_regs passed
+ */
frame_info->task = tsk;
frame_info->regs.r27 = regs->fp;
#endif
-static noinline unsigned int
+notrace noinline unsigned int
arc_unwind_core(struct task_struct *tsk, struct pt_regs *regs,
int (*consumer_fn) (unsigned int, void *), void *arg)
{
*/
#include <linux/types.h>
+#include <linux/perf_event.h>
#include <linux/ptrace.h>
#include <linux/uaccess.h>
#include <asm/disasm.h>
}
}
+ perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, address);
return 0;
fault:
#include <linux/ptrace.h>
#include <linux/uaccess.h>
#include <linux/kdebug.h>
+#include <linux/perf_event.h>
#include <asm/pgalloc.h>
#include <asm/mmu.h>
return;
}
+ perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
+
if (likely(!(fault & VM_FAULT_ERROR))) {
if (flags & FAULT_FLAG_ALLOW_RETRY) {
/* To avoid updating stats twice for retry case */
- if (fault & VM_FAULT_MAJOR)
+ if (fault & VM_FAULT_MAJOR) {
tsk->maj_flt++;
- else
+ perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1,
+ regs, address);
+ } else {
tsk->min_flt++;
+ perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1,
+ regs, address);
+ }
if (fault & VM_FAULT_RETRY) {
flags &= ~FAULT_FLAG_ALLOW_RETRY;
select GENERIC_CLOCKEVENTS
select GPIO_PXA
select HAVE_IDE
+ select IRQ_DOMAIN
select MULTI_IRQ_HANDLER
select PLAT_PXA
select SPARSE_IRQ
machine-$(CONFIG_ARCH_CLPS711X) += clps711x
machine-$(CONFIG_ARCH_CNS3XXX) += cns3xxx
machine-$(CONFIG_ARCH_DAVINCI) += davinci
+machine-$(CONFIG_ARCH_DIGICOLOR) += digicolor
machine-$(CONFIG_ARCH_DOVE) += dove
machine-$(CONFIG_ARCH_EBSA110) += ebsa110
machine-$(CONFIG_ARCH_EFM32) += efm32
&usb0 {
status = "okay";
+ dr_mode = "peripheral";
};
&usb1 {
cd-gpios = <&gpio0 6 GPIO_ACTIVE_HIGH>;
cd-inverted;
};
+
+&aes {
+ status = "okay";
+};
+
+&sham {
+ status = "okay";
+};
&mmc1 {
vmmc-supply = <&ldo3_reg>;
};
-
-&sham {
- status = "okay";
-};
-
-&aes {
- status = "okay";
-};
dual_emac_res_vlan = <3>;
};
+&phy_sel {
+ rmii-clock-ext;
+};
+
&mac {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&cpsw_default>;
ehrpwm0_tbclk: ehrpwm0_tbclk@44e10664 {
#clock-cells = <0>;
compatible = "ti,gate-clock";
- clocks = <&dpll_per_m2_ck>;
+ clocks = <&l4ls_gclk>;
ti,bit-shift = <0>;
reg = <0x0664>;
};
ehrpwm1_tbclk: ehrpwm1_tbclk@44e10664 {
#clock-cells = <0>;
compatible = "ti,gate-clock";
- clocks = <&dpll_per_m2_ck>;
+ clocks = <&l4ls_gclk>;
ti,bit-shift = <1>;
reg = <0x0664>;
};
ehrpwm2_tbclk: ehrpwm2_tbclk@44e10664 {
#clock-cells = <0>;
compatible = "ti,gate-clock";
- clocks = <&dpll_per_m2_ck>;
+ clocks = <&l4ls_gclk>;
ti,bit-shift = <2>;
reg = <0x0664>;
};
>;
};
- i2c1_pins_default: i2c1_pins_default {
- pinctrl-single,pins = <
- 0x15c (PIN_INPUT | SLEWCTRL_FAST | MUX_MODE2) /* spi0_cs0.i2c1_scl */
- 0x158 (PIN_INPUT | SLEWCTRL_FAST | MUX_MODE2) /* spi0_d1.i2c1_sda */
- >;
- };
-
- i2c1_pins_sleep: i2c1_pins_sleep {
- pinctrl-single,pins = <
- 0x15c (PIN_INPUT_PULLDOWN | MUX_MODE7) /* spi0_cs0.i2c1_scl */
- 0x158 (PIN_INPUT_PULLDOWN | MUX_MODE7) /* spi0_d1.i2c1_sda */
- >;
- };
-
mmc1_pins_default: pinmux_mmc1_pins_default {
pinctrl-single,pins = <
0x100 (PIN_INPUT | MUX_MODE0) /* mmc0_clk.mmc0_clk */
status = "okay";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&i2c0_pins_default>;
- pinctrl-1 = <&i2c0_pins_default>;
+ pinctrl-1 = <&i2c0_pins_sleep>;
clock-frequency = <400000>;
at24@50 {
pagesize = <64>;
reg = <0x50>;
};
-};
-
-&i2c1 {
- status = "okay";
- pinctrl-names = "default", "sleep";
- pinctrl-0 = <&i2c1_pins_default>;
- pinctrl-1 = <&i2c1_pins_default>;
- clock-frequency = <400000>;
tps: tps62362@60 {
compatible = "ti,tps62362";
+ reg = <0x60>;
regulator-name = "VDD_MPU";
regulator-min-microvolt = <950000>;
regulator-max-microvolt = <1330000>;
ehrpwm0_tbclk: ehrpwm0_tbclk {
#clock-cells = <0>;
compatible = "ti,gate-clock";
- clocks = <&dpll_per_m2_ck>;
+ clocks = <&l4ls_gclk>;
ti,bit-shift = <0>;
reg = <0x0664>;
};
ehrpwm1_tbclk: ehrpwm1_tbclk {
#clock-cells = <0>;
compatible = "ti,gate-clock";
- clocks = <&dpll_per_m2_ck>;
+ clocks = <&l4ls_gclk>;
ti,bit-shift = <1>;
reg = <0x0664>;
};
ehrpwm2_tbclk: ehrpwm2_tbclk {
#clock-cells = <0>;
compatible = "ti,gate-clock";
- clocks = <&dpll_per_m2_ck>;
+ clocks = <&l4ls_gclk>;
ti,bit-shift = <2>;
reg = <0x0664>;
};
ehrpwm3_tbclk: ehrpwm3_tbclk {
#clock-cells = <0>;
compatible = "ti,gate-clock";
- clocks = <&dpll_per_m2_ck>;
+ clocks = <&l4ls_gclk>;
ti,bit-shift = <4>;
reg = <0x0664>;
};
ehrpwm4_tbclk: ehrpwm4_tbclk {
#clock-cells = <0>;
compatible = "ti,gate-clock";
- clocks = <&dpll_per_m2_ck>;
+ clocks = <&l4ls_gclk>;
ti,bit-shift = <5>;
reg = <0x0664>;
};
ehrpwm5_tbclk: ehrpwm5_tbclk {
#clock-cells = <0>;
compatible = "ti,gate-clock";
- clocks = <&dpll_per_m2_ck>;
+ clocks = <&l4ls_gclk>;
ti,bit-shift = <6>;
reg = <0x0664>;
};
pinctrl-0 = <&usb1_pins>;
};
-&omap_dwc3_1 {
- extcon = <&extcon_usb1>;
-};
-
-&omap_dwc3_2 {
- extcon = <&extcon_usb2>;
-};
-
&usb2 {
dr_mode = "peripheral";
};
pinctrl_usart3_rts: usart3_rts-0 {
atmel,pins =
- <AT91_PIOB 8 AT91_PERIPH_B AT91_PINCTRL_NONE>; /* PC8 periph B */
+ <AT91_PIOC 8 AT91_PERIPH_B AT91_PINCTRL_NONE>;
};
pinctrl_usart3_cts: usart3_cts-0 {
atmel,pins =
- <AT91_PIOB 10 AT91_PERIPH_B AT91_PINCTRL_NONE>; /* PC10 periph B */
+ <AT91_PIOC 10 AT91_PERIPH_B AT91_PINCTRL_NONE>;
};
};
};
usb1: gadget@fffa4000 {
- compatible = "atmel,at91rm9200-udc";
+ compatible = "atmel,at91sam9260-udc";
reg = <0xfffa4000 0x4000>;
interrupts = <10 IRQ_TYPE_LEVEL_HIGH 2>;
clocks = <&udc_clk>, <&udpck>;
atmel,watchdog-type = "hardware";
atmel,reset-type = "all";
atmel,dbg-halt;
- atmel,idle-halt;
status = "disabled";
};
};
usb1: gadget@fffa4000 {
- compatible = "atmel,at91rm9200-udc";
+ compatible = "atmel,at91sam9261-udc";
reg = <0xfffa4000 0x4000>;
interrupts = <10 IRQ_TYPE_LEVEL_HIGH 2>;
- clocks = <&usb>, <&udc_clk>, <&udpck>;
- clock-names = "usb_clk", "udc_clk", "udpck";
+ clocks = <&udc_clk>, <&udpck>;
+ clock-names = "pclk", "hclk";
+ atmel,matrix = <&matrix>;
status = "disabled";
};
};
matrix: matrix@ffffee00 {
- compatible = "atmel,at91sam9260-bus-matrix";
+ compatible = "atmel,at91sam9260-bus-matrix", "syscon";
reg = <0xffffee00 0x200>;
};
sram1: sram@00500000 {
compatible = "mmio-sram";
- reg = <0x00300000 0x4000>;
+ reg = <0x00500000 0x4000>;
};
ahb {
};
usb1: gadget@fff78000 {
- compatible = "atmel,at91rm9200-udc";
+ compatible = "atmel,at91sam9263-udc";
reg = <0xfff78000 0x4000>;
interrupts = <24 IRQ_TYPE_LEVEL_HIGH 2>;
clocks = <&udc_clk>, <&udpck>;
atmel,watchdog-type = "hardware";
atmel,reset-type = "all";
atmel,dbg-halt;
- atmel,idle-halt;
status = "disabled";
};
atmel,watchdog-type = "hardware";
atmel,reset-type = "all";
atmel,dbg-halt;
- atmel,idle-halt;
status = "disabled";
};
compatible = "atmel,at91sam9g45-ehci", "usb-ehci";
reg = <0x00800000 0x100000>;
interrupts = <22 IRQ_TYPE_LEVEL_HIGH 2>;
- clocks = <&usb>, <&uhphs_clk>, <&uhphs_clk>, <&uhpck>;
+ clocks = <&utmi>, <&uhphs_clk>, <&uhphs_clk>, <&uhpck>;
clock-names = "usb_clk", "ehci_clk", "hclk", "uhpck";
status = "disabled";
};
atmel,watchdog-type = "hardware";
atmel,reset-type = "all";
atmel,dbg-halt;
- atmel,idle-halt;
status = "disabled";
};
reg = <0x00500000 0x80000
0xf803c000 0x400>;
interrupts = <23 IRQ_TYPE_LEVEL_HIGH 0>;
- clocks = <&usb>, <&udphs_clk>;
+ clocks = <&utmi>, <&udphs_clk>;
clock-names = "hclk", "pclk";
status = "disabled";
atmel,watchdog-type = "hardware";
atmel,reset-type = "all";
atmel,dbg-halt;
- atmel,idle-halt;
status = "disabled";
};
compatible = "atmel,at91sam9g45-ehci", "usb-ehci";
reg = <0x00700000 0x100000>;
interrupts = <22 IRQ_TYPE_LEVEL_HIGH 2>;
- clocks = <&usb>, <&uhphs_clk>, <&uhpck>;
+ clocks = <&utmi>, <&uhphs_clk>, <&uhpck>;
clock-names = "usb_clk", "ehci_clk", "uhpck";
status = "disabled";
};
DM816X_IOPAD(0x0aac, PIN_INPUT | MUX_MODE0) /* SPI_D1 */
>;
};
+
+ mmc_pins: pinmux_mmc_pins {
+ pinctrl-single,pins = <
+ DM816X_IOPAD(0x0a70, MUX_MODE0) /* SD_POW */
+ DM816X_IOPAD(0x0a74, MUX_MODE0) /* SD_CLK */
+ DM816X_IOPAD(0x0a78, MUX_MODE0) /* SD_CMD */
+ DM816X_IOPAD(0x0a7C, MUX_MODE0) /* SD_DAT0 */
+ DM816X_IOPAD(0x0a80, MUX_MODE0) /* SD_DAT1 */
+ DM816X_IOPAD(0x0a84, MUX_MODE0) /* SD_DAT2 */
+ DM816X_IOPAD(0x0a88, MUX_MODE0) /* SD_DAT2 */
+ DM816X_IOPAD(0x0a8c, MUX_MODE2) /* GP1[7] */
+ DM816X_IOPAD(0x0a90, MUX_MODE2) /* GP1[8] */
+ >;
+ };
+
+ usb0_pins: pinmux_usb0_pins {
+ pinctrl-single,pins = <
+ DM816X_IOPAD(0x0d00, MUX_MODE0) /* USB0_DRVVBUS */
+ >;
+ };
+
+ usb1_pins: pinmux_usb0_pins {
+ pinctrl-single,pins = <
+ DM816X_IOPAD(0x0d04, MUX_MODE0) /* USB1_DRVVBUS */
+ >;
+ };
};
&i2c1 {
};
&mmc1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc_pins>;
vmmc-supply = <&vmmcsd_fixed>;
+ bus-width = <4>;
+ cd-gpios = <&gpio2 7 GPIO_ACTIVE_LOW>;
+ wp-gpios = <&gpio2 8 GPIO_ACTIVE_LOW>;
+};
+
+/* At least dm8168-evm rev c won't support multipoint, later may */
+&usb0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&usb0_pins>;
+ mentor,multipoint = <0>;
+};
+
+&usb1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&usb1_pins>;
+ mentor,multipoint = <0>;
};
/* Device Configuration Registers */
scm_conf: syscon@600 {
- compatible = "syscon";
+ compatible = "syscon", "simple-bus";
reg = <0x600 0x110>;
#address-cells = <1>;
#size-cells = <1>;
+ ranges = <0 0x600 0x110>;
+
+ usb_phy0: usb-phy@20 {
+ compatible = "ti,dm8168-usb-phy";
+ reg = <0x20 0x8>;
+ reg-names = "phy";
+ clocks = <&main_fapll 6>;
+ clock-names = "refclk";
+ #phy-cells = <0>;
+ syscon = <&scm_conf>;
+ };
+
+ usb_phy1: usb-phy@28 {
+ compatible = "ti,dm8168-usb-phy";
+ reg = <0x28 0x8>;
+ reg-names = "phy";
+ clocks = <&main_fapll 6>;
+ clock-names = "refclk";
+ #phy-cells = <0>;
+ syscon = <&scm_conf>;
+ };
};
scrm_clocks: clocks {
};
gpio1: gpio@48032000 {
- compatible = "ti,omap3-gpio";
+ compatible = "ti,omap4-gpio";
ti,hwmods = "gpio1";
+ ti,gpio-always-on;
reg = <0x48032000 0x1000>;
- interrupts = <97>;
+ interrupts = <96>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
};
gpio2: gpio@4804c000 {
- compatible = "ti,omap3-gpio";
+ compatible = "ti,omap4-gpio";
ti,hwmods = "gpio2";
+ ti,gpio-always-on;
reg = <0x4804c000 0x1000>;
- interrupts = <99>;
+ interrupts = <98>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
};
gpmc: gpmc@50000000 {
reg-names = "mc", "control";
interrupts = <18>;
interrupt-names = "mc";
- dr_mode = "otg";
+ dr_mode = "host";
+ interface-type = <0>;
+ phys = <&usb_phy0>;
+ phy-names = "usb2-phy";
mentor,multipoint = <1>;
mentor,num-eps = <16>;
mentor,ram-bits = <12>;
usb1: usb@47401800 {
compatible = "ti,musb-am33xx";
- status = "disabled";
reg = <0x47401c00 0x400
0x47401800 0x200>;
reg-names = "mc", "control";
interrupts = <19>;
interrupt-names = "mc";
- dr_mode = "otg";
+ dr_mode = "host";
+ interface-type = <0>;
+ phys = <&usb_phy1>;
+ phy-names = "usb2-phy";
mentor,multipoint = <1>;
mentor,num-eps = <16>;
mentor,ram-bits = <12>;
dcan1_pins_default: dcan1_pins_default {
pinctrl-single,pins = <
- 0x3d0 (PIN_OUTPUT | MUX_MODE0) /* dcan1_tx */
- 0x3d4 (MUX_MODE15) /* dcan1_rx.off */
- 0x418 (PULL_DIS | MUX_MODE1) /* wakeup0.dcan1_rx */
+ 0x3d0 (PIN_OUTPUT_PULLUP | MUX_MODE0) /* dcan1_tx */
+ 0x418 (PULL_UP | MUX_MODE1) /* wakeup0.dcan1_rx */
>;
};
dcan1_pins_sleep: dcan1_pins_sleep {
pinctrl-single,pins = <
- 0x3d0 (MUX_MODE15) /* dcan1_tx.off */
- 0x3d4 (MUX_MODE15) /* dcan1_rx.off */
- 0x418 (MUX_MODE15) /* wakeup0.off */
+ 0x3d0 (MUX_MODE15 | PULL_UP) /* dcan1_tx.off */
+ 0x418 (MUX_MODE15 | PULL_UP) /* wakeup0.off */
>;
};
};
};
};
-&omap_dwc3_1 {
- extcon = <&extcon_usb1>;
-};
-
-&omap_dwc3_2 {
- extcon = <&extcon_usb2>;
-};
-
&usb1 {
dr_mode = "peripheral";
pinctrl-names = "default";
<GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>;
#dma-cells = <1>;
- #dma-channels = <32>;
- #dma-requests = <127>;
+ dma-channels = <32>;
+ dma-requests = <127>;
};
gpio1: gpio@4ae10000 {
<0x4A096800 0x40>; /* pll_ctrl */
reg-names = "phy_rx", "phy_tx", "pll_ctrl";
ctrl-module = <&omap_control_sata>;
- clocks = <&sys_clkin1>;
- clock-names = "sysclk";
+ clocks = <&sys_clkin1>, <&sata_ref_clk>;
+ clock-names = "sysclk", "refclk";
#phy-cells = <0>;
};
"wkupclk", "refclk",
"div-clk", "phy-div";
#phy-cells = <0>;
- ti,hwmods = "pcie1-phy";
};
pcie2_phy: pciephy@4a095000 {
"wkupclk", "refclk",
"div-clk", "phy-div";
#phy-cells = <0>;
- ti,hwmods = "pcie2-phy";
status = "disabled";
};
};
dcan1_pins_default: dcan1_pins_default {
pinctrl-single,pins = <
- 0x3d0 (PIN_OUTPUT | MUX_MODE0) /* dcan1_tx */
- 0x3d4 (MUX_MODE15) /* dcan1_rx.off */
- 0x418 (PULL_DIS | MUX_MODE1) /* wakeup0.dcan1_rx */
+ 0x3d0 (PIN_OUTPUT_PULLUP | MUX_MODE0) /* dcan1_tx */
+ 0x418 (PULL_UP | MUX_MODE1) /* wakeup0.dcan1_rx */
>;
};
dcan1_pins_sleep: dcan1_pins_sleep {
pinctrl-single,pins = <
- 0x3d0 (MUX_MODE15) /* dcan1_tx.off */
- 0x3d4 (MUX_MODE15) /* dcan1_rx.off */
- 0x418 (MUX_MODE15) /* wakeup0.off */
+ 0x3d0 (MUX_MODE15 | PULL_UP) /* dcan1_tx.off */
+ 0x418 (MUX_MODE15 | PULL_UP) /* wakeup0.off */
>;
};
phy-supply = <&ldo4_reg>;
};
-&omap_dwc3_1 {
- extcon = <&extcon_usb1>;
-};
-
-&omap_dwc3_2 {
- extcon = <&extcon_usb2>;
-};
-
&usb1 {
dr_mode = "peripheral";
pinctrl-names = "default";
ti,invert-autoidle-bit;
};
+ dpll_core_byp_mux: dpll_core_byp_mux {
+ #clock-cells = <0>;
+ compatible = "ti,mux-clock";
+ clocks = <&sys_clkin1>, <&dpll_abe_m3x2_ck>;
+ ti,bit-shift = <23>;
+ reg = <0x012c>;
+ };
+
dpll_core_ck: dpll_core_ck {
#clock-cells = <0>;
compatible = "ti,omap4-dpll-core-clock";
- clocks = <&sys_clkin1>, <&dpll_abe_m3x2_ck>;
+ clocks = <&sys_clkin1>, <&dpll_core_byp_mux>;
reg = <0x0120>, <0x0124>, <0x012c>, <0x0128>;
};
clock-div = <1>;
};
+ dpll_dsp_byp_mux: dpll_dsp_byp_mux {
+ #clock-cells = <0>;
+ compatible = "ti,mux-clock";
+ clocks = <&sys_clkin1>, <&dsp_dpll_hs_clk_div>;
+ ti,bit-shift = <23>;
+ reg = <0x0240>;
+ };
+
dpll_dsp_ck: dpll_dsp_ck {
#clock-cells = <0>;
compatible = "ti,omap4-dpll-clock";
- clocks = <&sys_clkin1>, <&dsp_dpll_hs_clk_div>;
+ clocks = <&sys_clkin1>, <&dpll_dsp_byp_mux>;
reg = <0x0234>, <0x0238>, <0x0240>, <0x023c>;
};
clock-div = <1>;
};
+ dpll_iva_byp_mux: dpll_iva_byp_mux {
+ #clock-cells = <0>;
+ compatible = "ti,mux-clock";
+ clocks = <&sys_clkin1>, <&iva_dpll_hs_clk_div>;
+ ti,bit-shift = <23>;
+ reg = <0x01ac>;
+ };
+
dpll_iva_ck: dpll_iva_ck {
#clock-cells = <0>;
compatible = "ti,omap4-dpll-clock";
- clocks = <&sys_clkin1>, <&iva_dpll_hs_clk_div>;
+ clocks = <&sys_clkin1>, <&dpll_iva_byp_mux>;
reg = <0x01a0>, <0x01a4>, <0x01ac>, <0x01a8>;
};
clock-div = <1>;
};
+ dpll_gpu_byp_mux: dpll_gpu_byp_mux {
+ #clock-cells = <0>;
+ compatible = "ti,mux-clock";
+ clocks = <&sys_clkin1>, <&dpll_abe_m3x2_ck>;
+ ti,bit-shift = <23>;
+ reg = <0x02e4>;
+ };
+
dpll_gpu_ck: dpll_gpu_ck {
#clock-cells = <0>;
compatible = "ti,omap4-dpll-clock";
- clocks = <&sys_clkin1>, <&dpll_abe_m3x2_ck>;
+ clocks = <&sys_clkin1>, <&dpll_gpu_byp_mux>;
reg = <0x02d8>, <0x02dc>, <0x02e4>, <0x02e0>;
};
clock-div = <1>;
};
+ dpll_ddr_byp_mux: dpll_ddr_byp_mux {
+ #clock-cells = <0>;
+ compatible = "ti,mux-clock";
+ clocks = <&sys_clkin1>, <&dpll_abe_m3x2_ck>;
+ ti,bit-shift = <23>;
+ reg = <0x021c>;
+ };
+
dpll_ddr_ck: dpll_ddr_ck {
#clock-cells = <0>;
compatible = "ti,omap4-dpll-clock";
- clocks = <&sys_clkin1>, <&dpll_abe_m3x2_ck>;
+ clocks = <&sys_clkin1>, <&dpll_ddr_byp_mux>;
reg = <0x0210>, <0x0214>, <0x021c>, <0x0218>;
};
ti,invert-autoidle-bit;
};
+ dpll_gmac_byp_mux: dpll_gmac_byp_mux {
+ #clock-cells = <0>;
+ compatible = "ti,mux-clock";
+ clocks = <&sys_clkin1>, <&dpll_abe_m3x2_ck>;
+ ti,bit-shift = <23>;
+ reg = <0x02b4>;
+ };
+
dpll_gmac_ck: dpll_gmac_ck {
#clock-cells = <0>;
compatible = "ti,omap4-dpll-clock";
- clocks = <&sys_clkin1>, <&dpll_abe_m3x2_ck>;
+ clocks = <&sys_clkin1>, <&dpll_gmac_byp_mux>;
reg = <0x02a8>, <0x02ac>, <0x02b4>, <0x02b0>;
};
clock-div = <1>;
};
+ dpll_eve_byp_mux: dpll_eve_byp_mux {
+ #clock-cells = <0>;
+ compatible = "ti,mux-clock";
+ clocks = <&sys_clkin1>, <&eve_dpll_hs_clk_div>;
+ ti,bit-shift = <23>;
+ reg = <0x0290>;
+ };
+
dpll_eve_ck: dpll_eve_ck {
#clock-cells = <0>;
compatible = "ti,omap4-dpll-clock";
- clocks = <&sys_clkin1>, <&eve_dpll_hs_clk_div>;
+ clocks = <&sys_clkin1>, <&dpll_eve_byp_mux>;
reg = <0x0284>, <0x0288>, <0x0290>, <0x028c>;
};
clock-div = <1>;
};
+ dpll_per_byp_mux: dpll_per_byp_mux {
+ #clock-cells = <0>;
+ compatible = "ti,mux-clock";
+ clocks = <&sys_clkin1>, <&per_dpll_hs_clk_div>;
+ ti,bit-shift = <23>;
+ reg = <0x014c>;
+ };
+
dpll_per_ck: dpll_per_ck {
#clock-cells = <0>;
compatible = "ti,omap4-dpll-clock";
- clocks = <&sys_clkin1>, <&per_dpll_hs_clk_div>;
+ clocks = <&sys_clkin1>, <&dpll_per_byp_mux>;
reg = <0x0140>, <0x0144>, <0x014c>, <0x0148>;
};
clock-div = <1>;
};
+ dpll_usb_byp_mux: dpll_usb_byp_mux {
+ #clock-cells = <0>;
+ compatible = "ti,mux-clock";
+ clocks = <&sys_clkin1>, <&usb_dpll_hs_clk_div>;
+ ti,bit-shift = <23>;
+ reg = <0x018c>;
+ };
+
dpll_usb_ck: dpll_usb_ck {
#clock-cells = <0>;
compatible = "ti,omap4-dpll-j-type-clock";
- clocks = <&sys_clkin1>, <&usb_dpll_hs_clk_div>;
+ clocks = <&sys_clkin1>, <&dpll_usb_byp_mux>;
reg = <0x0180>, <0x0184>, <0x018c>, <0x0188>;
};
*/
#include "skeleton.dtsi"
+#include "exynos4-cpu-thermal.dtsi"
#include <dt-bindings/clock/exynos3250.h>
/ {
interrupts = <0 216 0>;
clocks = <&cmu CLK_TMU_APBIF>;
clock-names = "tmu_apbif";
+ #include "exynos4412-tmu-sensor-conf.dtsi"
status = "disabled";
};
--- /dev/null
+/*
+ * Device tree sources for Exynos4 thermal zone
+ *
+ * Copyright (c) 2014 Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <dt-bindings/thermal/thermal.h>
+
+/ {
+thermal-zones {
+ cpu_thermal: cpu-thermal {
+ thermal-sensors = <&tmu 0>;
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ trips {
+ cpu_alert0: cpu-alert-0 {
+ temperature = <70000>; /* millicelsius */
+ hysteresis = <10000>; /* millicelsius */
+ type = "active";
+ };
+ cpu_alert1: cpu-alert-1 {
+ temperature = <95000>; /* millicelsius */
+ hysteresis = <10000>; /* millicelsius */
+ type = "active";
+ };
+ cpu_alert2: cpu-alert-2 {
+ temperature = <110000>; /* millicelsius */
+ hysteresis = <10000>; /* millicelsius */
+ type = "active";
+ };
+ cpu_crit0: cpu-crit-0 {
+ temperature = <120000>; /* millicelsius */
+ hysteresis = <0>; /* millicelsius */
+ type = "critical";
+ };
+ };
+ cooling-maps {
+ map0 {
+ trip = <&cpu_alert0>;
+ };
+ map1 {
+ trip = <&cpu_alert1>;
+ };
+ };
+ };
+};
+};
i2c5 = &i2c_5;
i2c6 = &i2c_6;
i2c7 = &i2c_7;
+ i2c8 = &i2c_8;
csis0 = &csis_0;
csis1 = &csis_1;
fimc0 = &fimc_0;
compatible = "samsung,exynos4210-pd";
reg = <0x10023C20 0x20>;
#power-domain-cells = <0>;
+ power-domains = <&pd_lcd0>;
};
pd_cam: cam-power-domain@10023C00 {
status = "disabled";
};
+ i2c_8: i2c@138E0000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "samsung,s3c2440-hdmiphy-i2c";
+ reg = <0x138E0000 0x100>;
+ interrupts = <0 93 0>;
+ clocks = <&clock CLK_I2C_HDMI>;
+ clock-names = "i2c";
+ status = "disabled";
+
+ hdmi_i2c_phy: hdmiphy@38 {
+ compatible = "exynos4210-hdmiphy";
+ reg = <0x38>;
+ };
+ };
+
spi_0: spi@13920000 {
compatible = "samsung,exynos4210-spi";
reg = <0x13920000 0x100>;
status = "disabled";
};
+ tmu: tmu@100C0000 {
+ #include "exynos4412-tmu-sensor-conf.dtsi"
+ };
+
+ hdmi: hdmi@12D00000 {
+ compatible = "samsung,exynos4210-hdmi";
+ reg = <0x12D00000 0x70000>;
+ interrupts = <0 92 0>;
+ clock-names = "hdmi", "sclk_hdmi", "sclk_pixel", "sclk_hdmiphy",
+ "mout_hdmi";
+ clocks = <&clock CLK_HDMI>, <&clock CLK_SCLK_HDMI>,
+ <&clock CLK_SCLK_PIXEL>, <&clock CLK_SCLK_HDMIPHY>,
+ <&clock CLK_MOUT_HDMI>;
+ phy = <&hdmi_i2c_phy>;
+ power-domains = <&pd_tv>;
+ samsung,syscon-phandle = <&pmu_system_controller>;
+ status = "disabled";
+ };
+
+ mixer: mixer@12C10000 {
+ compatible = "samsung,exynos4210-mixer";
+ interrupts = <0 91 0>;
+ reg = <0x12C10000 0x2100>, <0x12c00000 0x300>;
+ power-domains = <&pd_tv>;
+ status = "disabled";
+ };
+
ppmu_dmc0: ppmu_dmc0@106a0000 {
compatible = "samsung,exynos-ppmu";
reg = <0x106a0000 0x2000>;
status = "okay";
};
+ tmu@100C0000 {
+ status = "okay";
+ };
+
+ thermal-zones {
+ cpu_thermal: cpu-thermal {
+ cooling-maps {
+ map0 {
+ /* Corresponds to 800MHz at freq_table */
+ cooling-device = <&cpu0 2 2>;
+ };
+ map1 {
+ /* Corresponds to 200MHz at freq_table */
+ cooling-device = <&cpu0 4 4>;
+ };
+ };
+ };
+ };
+
camera {
pinctrl-names = "default";
pinctrl-0 = <>;
assigned-clock-rates = <0>, <160000000>;
};
};
+
+ hdmi_en: voltage-regulator-hdmi-5v {
+ compatible = "regulator-fixed";
+ regulator-name = "HDMI_5V";
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+ gpio = <&gpe0 1 0>;
+ enable-active-high;
+ };
+
+ hdmi_ddc: i2c-ddc {
+ compatible = "i2c-gpio";
+ gpios = <&gpe4 2 0 &gpe4 3 0>;
+ i2c-gpio,delay-us = <100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pinctrl-0 = <&i2c_ddc_bus>;
+ pinctrl-names = "default";
+ status = "okay";
+ };
+
+ mixer@12C10000 {
+ status = "okay";
+ };
+
+ hdmi@12D00000 {
+ hpd-gpio = <&gpx3 7 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&hdmi_hpd>;
+ hdmi-en-supply = <&hdmi_en>;
+ vdd-supply = <&ldo3_reg>;
+ vdd_osc-supply = <&ldo4_reg>;
+ vdd_pll-supply = <&ldo3_reg>;
+ ddc = <&hdmi_ddc>;
+ status = "okay";
+ };
+
+ i2c@138E0000 {
+ status = "okay";
+ };
+};
+
+&pinctrl_1 {
+ hdmi_hpd: hdmi-hpd {
+ samsung,pins = "gpx3-7";
+ samsung,pin-pud = <0>;
+ };
+};
+
+&pinctrl_0 {
+ i2c_ddc_bus: i2c-ddc-bus {
+ samsung,pins = "gpe4-2", "gpe4-3";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
};
&mdma1 {
#include "exynos4.dtsi"
#include "exynos4210-pinctrl.dtsi"
+#include "exynos4-cpu-thermal.dtsi"
/ {
compatible = "samsung,exynos4210", "samsung,exynos4";
#address-cells = <1>;
#size-cells = <0>;
- cpu@900 {
+ cpu0: cpu@900 {
device_type = "cpu";
compatible = "arm,cortex-a9";
reg = <0x900>;
+ cooling-min-level = <4>;
+ cooling-max-level = <2>;
+ #cooling-cells = <2>; /* min followed by max */
};
cpu@901 {
reg = <0x03860000 0x1000>;
};
- tmu@100C0000 {
+ tmu: tmu@100C0000 {
compatible = "samsung,exynos4210-tmu";
interrupt-parent = <&combiner>;
reg = <0x100C0000 0x100>;
interrupts = <2 4>;
clocks = <&clock CLK_TMU_APBIF>;
clock-names = "tmu_apbif";
+ samsung,tmu_gain = <15>;
+ samsung,tmu_reference_voltage = <7>;
status = "disabled";
};
+ thermal-zones {
+ cpu_thermal: cpu-thermal {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tmu 0>;
+
+ trips {
+ cpu_alert0: cpu-alert-0 {
+ temperature = <85000>; /* millicelsius */
+ };
+ cpu_alert1: cpu-alert-1 {
+ temperature = <100000>; /* millicelsius */
+ };
+ cpu_alert2: cpu-alert-2 {
+ temperature = <110000>; /* millicelsius */
+ };
+ };
+ };
+ };
+
g2d@12800000 {
compatible = "samsung,s5pv210-g2d";
reg = <0x12800000 0x1000>;
};
};
+ mixer: mixer@12C10000 {
+ clock-names = "mixer", "hdmi", "sclk_hdmi", "vp", "mout_mixer",
+ "sclk_mixer";
+ clocks = <&clock CLK_MIXER>, <&clock CLK_HDMI>,
+ <&clock CLK_SCLK_HDMI>, <&clock CLK_VP>,
+ <&clock CLK_MOUT_MIXER>, <&clock CLK_SCLK_MIXER>;
+ };
+
ppmu_lcd1: ppmu_lcd1@12240000 {
compatible = "samsung,exynos-ppmu";
reg = <0x12240000 0x2000>;
#address-cells = <1>;
#size-cells = <0>;
- cpu@A00 {
+ cpu0: cpu@A00 {
device_type = "cpu";
compatible = "arm,cortex-a9";
reg = <0xA00>;
+ cooling-min-level = <13>;
+ cooling-max-level = <7>;
+ #cooling-cells = <2>; /* min followed by max */
};
cpu@A01 {
regulator-always-on;
};
+ ldo8_reg: ldo@8 {
+ regulator-compatible = "LDO8";
+ regulator-name = "VDD10_HDMI_1.0V";
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ };
+
+ ldo10_reg: ldo@10 {
+ regulator-compatible = "LDO10";
+ regulator-name = "VDDQ_MIPIHSI_1.8V";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
ldo11_reg: LDO11 {
regulator-name = "VDD18_ABB1_1.8V";
regulator-min-microvolt = <1800000>;
ehci: ehci@12580000 {
status = "okay";
};
+
+ tmu@100C0000 {
+ vtmu-supply = <&ldo10_reg>;
+ status = "okay";
+ };
+
+ thermal-zones {
+ cpu_thermal: cpu-thermal {
+ cooling-maps {
+ map0 {
+ /* Corresponds to 800MHz at freq_table */
+ cooling-device = <&cpu0 7 7>;
+ };
+ map1 {
+ /* Corresponds to 200MHz at freq_table */
+ cooling-device = <&cpu0 13 13>;
+ };
+ };
+ };
+ };
+
+ mixer: mixer@12C10000 {
+ status = "okay";
+ };
+
+ hdmi@12D00000 {
+ hpd-gpio = <&gpx3 7 0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&hdmi_hpd>;
+ vdd-supply = <&ldo8_reg>;
+ vdd_osc-supply = <&ldo10_reg>;
+ vdd_pll-supply = <&ldo8_reg>;
+ ddc = <&hdmi_ddc>;
+ status = "okay";
+ };
+
+ hdmi_ddc: i2c@13880000 {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c2_bus>;
+ };
+
+ i2c@138E0000 {
+ status = "okay";
+ };
};
&pinctrl_1 {
samsung,pin-pud = <0>;
samsung,pin-drv = <0>;
};
+
+ hdmi_hpd: hdmi-hpd {
+ samsung,pins = "gpx3-7";
+ samsung,pin-pud = <1>;
+ };
};
--- /dev/null
+/*
+ * Device tree sources for Exynos4412 TMU sensor configuration
+ *
+ * Copyright (c) 2014 Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <dt-bindings/thermal/thermal_exynos.h>
+
+#thermal-sensor-cells = <0>;
+samsung,tmu_gain = <8>;
+samsung,tmu_reference_voltage = <16>;
+samsung,tmu_noise_cancel_mode = <4>;
+samsung,tmu_efuse_value = <55>;
+samsung,tmu_min_efuse_value = <40>;
+samsung,tmu_max_efuse_value = <100>;
+samsung,tmu_first_point_trim = <25>;
+samsung,tmu_second_point_trim = <85>;
+samsung,tmu_default_temp_offset = <50>;
+samsung,tmu_cal_type = <TYPE_ONE_POINT_TRIMMING>;
pulldown-ohm = <100000>; /* 100K */
io-channels = <&adc 2>; /* Battery temperature */
};
+
+ thermal-zones {
+ cpu_thermal: cpu-thermal {
+ cooling-maps {
+ map0 {
+ /* Corresponds to 800MHz at freq_table */
+ cooling-device = <&cpu0 7 7>;
+ };
+ map1 {
+ /* Corresponds to 200MHz at freq_table */
+ cooling-device = <&cpu0 13 13>;
+ };
+ };
+ };
+ };
};
&pmu_system_controller {
#address-cells = <1>;
#size-cells = <0>;
- cpu@A00 {
+ cpu0: cpu@A00 {
device_type = "cpu";
compatible = "arm,cortex-a9";
reg = <0xA00>;
+ cooling-min-level = <13>;
+ cooling-max-level = <7>;
+ #cooling-cells = <2>; /* min followed by max */
};
cpu@A01 {
#include "exynos4.dtsi"
#include "exynos4x12-pinctrl.dtsi"
+#include "exynos4-cpu-thermal.dtsi"
/ {
aliases {
clock-names = "tmu_apbif";
status = "disabled";
};
+
+ hdmi: hdmi@12D00000 {
+ compatible = "samsung,exynos4212-hdmi";
+ };
+
+ mixer: mixer@12C10000 {
+ compatible = "samsung,exynos4212-mixer";
+ clock-names = "mixer", "hdmi", "sclk_hdmi", "vp";
+ clocks = <&clock CLK_MIXER>, <&clock CLK_HDMI>,
+ <&clock CLK_SCLK_HDMI>, <&clock CLK_VP>;
+ };
};
#include <dt-bindings/clock/exynos5250.h>
#include "exynos5.dtsi"
#include "exynos5250-pinctrl.dtsi"
-
+#include "exynos4-cpu-thermal.dtsi"
#include <dt-bindings/clock/exynos-audss-clk.h>
/ {
#address-cells = <1>;
#size-cells = <0>;
- cpu@0 {
+ cpu0: cpu@0 {
device_type = "cpu";
compatible = "arm,cortex-a15";
reg = <0>;
clock-frequency = <1700000000>;
+ cooling-min-level = <15>;
+ cooling-max-level = <9>;
+ #cooling-cells = <2>; /* min followed by max */
};
cpu@1 {
device_type = "cpu";
#power-domain-cells = <0>;
};
+ pd_disp1: disp1-power-domain@100440A0 {
+ compatible = "samsung,exynos4210-pd";
+ reg = <0x100440A0 0x20>;
+ #power-domain-cells = <0>;
+ };
+
clock: clock-controller@10010000 {
compatible = "samsung,exynos5250-clock";
reg = <0x10010000 0x30000>;
status = "disabled";
};
- tmu@10060000 {
+ tmu: tmu@10060000 {
compatible = "samsung,exynos5250-tmu";
reg = <0x10060000 0x100>;
interrupts = <0 65 0>;
clocks = <&clock CLK_TMU>;
clock-names = "tmu_apbif";
+ #include "exynos4412-tmu-sensor-conf.dtsi"
+ };
+
+ thermal-zones {
+ cpu_thermal: cpu-thermal {
+ polling-delay-passive = <0>;
+ polling-delay = <0>;
+ thermal-sensors = <&tmu 0>;
+
+ cooling-maps {
+ map0 {
+ /* Corresponds to 800MHz at freq_table */
+ cooling-device = <&cpu0 9 9>;
+ };
+ map1 {
+ /* Corresponds to 200MHz at freq_table */
+ cooling-device = <&cpu0 15 15>;
+ };
+ };
+ };
};
serial@12C00000 {
hdmi: hdmi {
compatible = "samsung,exynos4212-hdmi";
reg = <0x14530000 0x70000>;
+ power-domains = <&pd_disp1>;
interrupts = <0 95 0>;
clocks = <&clock CLK_HDMI>, <&clock CLK_SCLK_HDMI>,
<&clock CLK_SCLK_PIXEL>, <&clock CLK_SCLK_HDMIPHY>,
mixer {
compatible = "samsung,exynos5250-mixer";
reg = <0x14450000 0x10000>;
+ power-domains = <&pd_disp1>;
interrupts = <0 94 0>;
- clocks = <&clock CLK_MIXER>, <&clock CLK_SCLK_HDMI>;
- clock-names = "mixer", "sclk_hdmi";
+ clocks = <&clock CLK_MIXER>, <&clock CLK_HDMI>,
+ <&clock CLK_SCLK_HDMI>;
+ clock-names = "mixer", "hdmi", "sclk_hdmi";
};
dp_phy: video-phy@10040720 {
};
dp: dp-controller@145B0000 {
+ power-domains = <&pd_disp1>;
clocks = <&clock CLK_DP>;
clock-names = "dp";
phys = <&dp_phy>;
};
fimd: fimd@14400000 {
+ power-domains = <&pd_disp1>;
clocks = <&clock CLK_SCLK_FIMD1>, <&clock CLK_FIMD1>;
clock-names = "sclk_fimd", "fimd";
};
--- /dev/null
+/*
+ * Device tree sources for default Exynos5420 thermal zone definition
+ *
+ * Copyright (c) 2014 Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+polling-delay-passive = <0>;
+polling-delay = <0>;
+trips {
+ cpu-alert-0 {
+ temperature = <85000>; /* millicelsius */
+ hysteresis = <10000>; /* millicelsius */
+ type = "active";
+ };
+ cpu-alert-1 {
+ temperature = <103000>; /* millicelsius */
+ hysteresis = <10000>; /* millicelsius */
+ type = "active";
+ };
+ cpu-alert-2 {
+ temperature = <110000>; /* millicelsius */
+ hysteresis = <10000>; /* millicelsius */
+ type = "active";
+ };
+ cpu-crit-0 {
+ temperature = <1200000>; /* millicelsius */
+ hysteresis = <0>; /* millicelsius */
+ type = "critical";
+ };
+};
compatible = "samsung,exynos5420-mixer";
reg = <0x14450000 0x10000>;
interrupts = <0 94 0>;
- clocks = <&clock CLK_MIXER>, <&clock CLK_SCLK_HDMI>;
- clock-names = "mixer", "sclk_hdmi";
+ clocks = <&clock CLK_MIXER>, <&clock CLK_HDMI>,
+ <&clock CLK_SCLK_HDMI>;
+ clock-names = "mixer", "hdmi", "sclk_hdmi";
power-domains = <&disp_pd>;
};
interrupts = <0 65 0>;
clocks = <&clock CLK_TMU>;
clock-names = "tmu_apbif";
+ #include "exynos4412-tmu-sensor-conf.dtsi"
};
tmu_cpu1: tmu@10064000 {
interrupts = <0 183 0>;
clocks = <&clock CLK_TMU>;
clock-names = "tmu_apbif";
+ #include "exynos4412-tmu-sensor-conf.dtsi"
};
tmu_cpu2: tmu@10068000 {
interrupts = <0 184 0>;
clocks = <&clock CLK_TMU>, <&clock CLK_TMU>;
clock-names = "tmu_apbif", "tmu_triminfo_apbif";
+ #include "exynos4412-tmu-sensor-conf.dtsi"
};
tmu_cpu3: tmu@1006c000 {
interrupts = <0 185 0>;
clocks = <&clock CLK_TMU>, <&clock CLK_TMU_GPU>;
clock-names = "tmu_apbif", "tmu_triminfo_apbif";
+ #include "exynos4412-tmu-sensor-conf.dtsi"
};
tmu_gpu: tmu@100a0000 {
interrupts = <0 215 0>;
clocks = <&clock CLK_TMU_GPU>, <&clock CLK_TMU>;
clock-names = "tmu_apbif", "tmu_triminfo_apbif";
+ #include "exynos4412-tmu-sensor-conf.dtsi"
+ };
+
+ thermal-zones {
+ cpu0_thermal: cpu0-thermal {
+ thermal-sensors = <&tmu_cpu0>;
+ #include "exynos5420-trip-points.dtsi"
+ };
+ cpu1_thermal: cpu1-thermal {
+ thermal-sensors = <&tmu_cpu1>;
+ #include "exynos5420-trip-points.dtsi"
+ };
+ cpu2_thermal: cpu2-thermal {
+ thermal-sensors = <&tmu_cpu2>;
+ #include "exynos5420-trip-points.dtsi"
+ };
+ cpu3_thermal: cpu3-thermal {
+ thermal-sensors = <&tmu_cpu3>;
+ #include "exynos5420-trip-points.dtsi"
+ };
+ gpu_thermal: gpu-thermal {
+ thermal-sensors = <&tmu_gpu>;
+ #include "exynos5420-trip-points.dtsi"
+ };
};
watchdog: watchdog@101D0000 {
--- /dev/null
+/*
+ * Device tree sources for Exynos5440 TMU sensor configuration
+ *
+ * Copyright (c) 2014 Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <dt-bindings/thermal/thermal_exynos.h>
+
+#thermal-sensor-cells = <0>;
+samsung,tmu_gain = <5>;
+samsung,tmu_reference_voltage = <16>;
+samsung,tmu_noise_cancel_mode = <4>;
+samsung,tmu_efuse_value = <0x5d2d>;
+samsung,tmu_min_efuse_value = <16>;
+samsung,tmu_max_efuse_value = <76>;
+samsung,tmu_first_point_trim = <25>;
+samsung,tmu_second_point_trim = <70>;
+samsung,tmu_default_temp_offset = <25>;
+samsung,tmu_cal_type = <TYPE_ONE_POINT_TRIMMING>;
--- /dev/null
+/*
+ * Device tree sources for default Exynos5440 thermal zone definition
+ *
+ * Copyright (c) 2014 Lukasz Majewski <l.majewski@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+polling-delay-passive = <0>;
+polling-delay = <0>;
+trips {
+ cpu-alert-0 {
+ temperature = <100000>; /* millicelsius */
+ hysteresis = <0>; /* millicelsius */
+ type = "active";
+ };
+ cpu-crit-0 {
+ temperature = <1050000>; /* millicelsius */
+ hysteresis = <0>; /* millicelsius */
+ type = "critical";
+ };
+};
interrupts = <0 58 0>;
clocks = <&clock CLK_B_125>;
clock-names = "tmu_apbif";
+ #include "exynos5440-tmu-sensor-conf.dtsi"
};
tmuctrl_1: tmuctrl@16011C {
interrupts = <0 58 0>;
clocks = <&clock CLK_B_125>;
clock-names = "tmu_apbif";
+ #include "exynos5440-tmu-sensor-conf.dtsi"
};
tmuctrl_2: tmuctrl@160120 {
interrupts = <0 58 0>;
clocks = <&clock CLK_B_125>;
clock-names = "tmu_apbif";
+ #include "exynos5440-tmu-sensor-conf.dtsi"
+ };
+
+ thermal-zones {
+ cpu0_thermal: cpu0-thermal {
+ thermal-sensors = <&tmuctrl_0>;
+ #include "exynos5440-trip-points.dtsi"
+ };
+ cpu1_thermal: cpu1-thermal {
+ thermal-sensors = <&tmuctrl_1>;
+ #include "exynos5440-trip-points.dtsi"
+ };
+ cpu2_thermal: cpu2-thermal {
+ thermal-sensors = <&tmuctrl_2>;
+ #include "exynos5440-trip-points.dtsi"
+ };
};
sata@210000 {
regulator-max-microvolt = <5000000>;
gpio = <&gpio3 22 0>;
enable-active-high;
+ vin-supply = <&swbst_reg>;
};
reg_usb_h1_vbus: regulator@1 {
regulator-max-microvolt = <5000000>;
gpio = <&gpio1 29 0>;
enable-active-high;
+ vin-supply = <&swbst_reg>;
};
reg_audio: regulator@2 {
regulator-max-microvolt = <5000000>;
gpio = <&gpio4 0 0>;
enable-active-high;
+ vin-supply = <&swbst_reg>;
};
reg_usb_otg2_vbus: regulator@1 {
regulator-max-microvolt = <5000000>;
gpio = <&gpio4 2 0>;
enable-active-high;
+ vin-supply = <&swbst_reg>;
};
reg_aud3v: regulator@2 {
<14>,
<15>;
#dma-cells = <1>;
- #dma-channels = <32>;
- #dma-requests = <64>;
+ dma-channels = <32>;
+ dma-requests = <64>;
};
i2c1: i2c@48070000 {
model = "Nokia N900";
compatible = "nokia,omap3-n900", "ti,omap3430", "ti,omap3";
+ aliases {
+ i2c0;
+ i2c1 = &i2c1;
+ i2c2 = &i2c2;
+ i2c3 = &i2c3;
+ };
+
cpus {
cpu@0 {
cpu0-supply = <&vcc>;
compatible = "smsc,lan91c94";
interrupt-parent = <&gpio2>;
interrupts = <22 IRQ_TYPE_LEVEL_HIGH>; /* gpio54 */
- reg = <1 0x300 0xf>; /* 16 byte IO range at offset 0x300 */
+ reg = <1 0 0xf>; /* 16 byte IO range */
bank-width = <2>;
pinctrl-names = "default";
pinctrl-0 = <ðernet_pins>;
ti,hwmods = "aes";
reg = <0x480c5000 0x50>;
interrupts = <0>;
+ dmas = <&sdma 65 &sdma 66>;
+ dma-names = "tx", "rx";
};
prm: prm@48306000 {
<14>,
<15>;
#dma-cells = <1>;
- #dma-channels = <32>;
- #dma-requests = <96>;
+ dma-channels = <32>;
+ dma-requests = <96>;
};
omap3_pmx_core: pinmux@48002030 {
ti,hwmods = "sham";
reg = <0x480c3000 0x64>;
interrupts = <49>;
+ dmas = <&sdma 69>;
+ dma-names = "rx";
};
smartreflex_core: smartreflex@480cb000 {
<GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
#dma-cells = <1>;
- #dma-channels = <32>;
- #dma-requests = <127>;
+ dma-channels = <32>;
+ dma-requests = <127>;
};
gpio1: gpio@4a310000 {
core_thermal: core_thermal {
polling-delay-passive = <250>; /* milliseconds */
- polling-delay = <1000>; /* milliseconds */
+ polling-delay = <500>; /* milliseconds */
/* sensor ID */
thermal-sensors = <&bandgap 2>;
gpu_thermal: gpu_thermal {
polling-delay-passive = <250>; /* milliseconds */
- polling-delay = <1000>; /* milliseconds */
+ polling-delay = <500>; /* milliseconds */
/* sensor ID */
thermal-sensors = <&bandgap 1>;
<GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
#dma-cells = <1>;
- #dma-channels = <32>;
- #dma-requests = <127>;
+ dma-channels = <32>;
+ dma-requests = <127>;
};
gpio1: gpio@4ae10000 {
<0x4A096800 0x40>; /* pll_ctrl */
reg-names = "phy_rx", "phy_tx", "pll_ctrl";
ctrl-module = <&omap_control_sata>;
- clocks = <&sys_clkin>;
- clock-names = "sysclk";
+ clocks = <&sys_clkin>, <&sata_ref_clk>;
+ clock-names = "sysclk", "refclk";
#phy-cells = <0>;
};
};
};
};
+&cpu_thermal {
+ polling-delay = <500>; /* milliseconds */
+};
+
/include/ "omap54xx-clocks.dtsi"
ti,index-starts-at-one;
};
+ dpll_core_byp_mux: dpll_core_byp_mux {
+ #clock-cells = <0>;
+ compatible = "ti,mux-clock";
+ clocks = <&sys_clkin>, <&dpll_abe_m3x2_ck>;
+ ti,bit-shift = <23>;
+ reg = <0x012c>;
+ };
+
dpll_core_ck: dpll_core_ck {
#clock-cells = <0>;
compatible = "ti,omap4-dpll-core-clock";
- clocks = <&sys_clkin>, <&dpll_abe_m3x2_ck>;
+ clocks = <&sys_clkin>, <&dpll_core_byp_mux>;
reg = <0x0120>, <0x0124>, <0x012c>, <0x0128>;
};
clock-div = <1>;
};
+ dpll_iva_byp_mux: dpll_iva_byp_mux {
+ #clock-cells = <0>;
+ compatible = "ti,mux-clock";
+ clocks = <&sys_clkin>, <&iva_dpll_hs_clk_div>;
+ ti,bit-shift = <23>;
+ reg = <0x01ac>;
+ };
+
dpll_iva_ck: dpll_iva_ck {
#clock-cells = <0>;
compatible = "ti,omap4-dpll-clock";
- clocks = <&sys_clkin>, <&iva_dpll_hs_clk_div>;
+ clocks = <&sys_clkin>, <&dpll_iva_byp_mux>;
reg = <0x01a0>, <0x01a4>, <0x01ac>, <0x01a8>;
};
};
};
&cm_core_clocks {
+
+ dpll_per_byp_mux: dpll_per_byp_mux {
+ #clock-cells = <0>;
+ compatible = "ti,mux-clock";
+ clocks = <&sys_clkin>, <&per_dpll_hs_clk_div>;
+ ti,bit-shift = <23>;
+ reg = <0x014c>;
+ };
+
dpll_per_ck: dpll_per_ck {
#clock-cells = <0>;
compatible = "ti,omap4-dpll-clock";
- clocks = <&sys_clkin>, <&per_dpll_hs_clk_div>;
+ clocks = <&sys_clkin>, <&dpll_per_byp_mux>;
reg = <0x0140>, <0x0144>, <0x014c>, <0x0148>;
};
ti,index-starts-at-one;
};
+ dpll_usb_byp_mux: dpll_usb_byp_mux {
+ #clock-cells = <0>;
+ compatible = "ti,mux-clock";
+ clocks = <&sys_clkin>, <&usb_dpll_hs_clk_div>;
+ ti,bit-shift = <23>;
+ reg = <0x018c>;
+ };
+
dpll_usb_ck: dpll_usb_ck {
#clock-cells = <0>;
compatible = "ti,omap4-dpll-j-type-clock";
- clocks = <&sys_clkin>, <&usb_dpll_hs_clk_div>;
+ clocks = <&sys_clkin>, <&dpll_usb_byp_mux>;
reg = <0x0180>, <0x0184>, <0x018c>, <0x0188>;
};
"mac_clk_rx", "mac_clk_tx",
"clk_mac_ref", "clk_mac_refout",
"aclk_mac", "pclk_mac";
+ status = "disabled";
};
usb_host0_ehci: usb@ff500000 {
atmel,watchdog-type = "hardware";
atmel,reset-type = "all";
atmel,dbg-halt;
- atmel,idle-halt;
status = "disabled";
};
compatible = "atmel,at91sam9g45-ehci", "usb-ehci";
reg = <0x00700000 0x100000>;
interrupts = <32 IRQ_TYPE_LEVEL_HIGH 2>;
- clocks = <&usb>, <&uhphs_clk>, <&uhpck>;
+ clocks = <&utmi>, <&uhphs_clk>, <&uhpck>;
clock-names = "usb_clk", "ehci_clk", "uhpck";
status = "disabled";
};
gpio4 = &pioE;
tcb0 = &tcb0;
tcb1 = &tcb1;
+ i2c0 = &i2c0;
i2c2 = &i2c2;
};
cpus {
compatible = "atmel,at91sam9g45-ehci", "usb-ehci";
reg = <0x00600000 0x100000>;
interrupts = <46 IRQ_TYPE_LEVEL_HIGH 2>;
- clocks = <&usb>, <&uhphs_clk>, <&uhpck>;
+ clocks = <&utmi>, <&uhphs_clk>, <&uhpck>;
clock-names = "usb_clk", "ehci_clk", "uhpck";
status = "disabled";
};
lcdck: lcdck {
#clock-cells = <0>;
- reg = <4>;
- clocks = <&smd>;
+ reg = <3>;
+ clocks = <&mck>;
};
smdck: smdck {
reg = <50>;
};
- lcd_clk: lcd_clk {
+ lcdc_clk: lcdc_clk {
#clock-cells = <0>;
reg = <51>;
};
#address-cells = <1>;
#size-cells = <0>;
reg = <0xfff01000 0x1000>;
- interrupts = <0 156 4>;
+ interrupts = <0 155 4>;
num-cs = <4>;
clocks = <&spi_m_clk>;
status = "disabled";
reg-shift = <2>;
reg-io-width = <4>;
clocks = <&l4_sp_clk>;
+ dmas = <&pdma 28>,
+ <&pdma 29>;
+ dma-names = "tx", "rx";
};
uart1: serial1@ffc03000 {
reg-shift = <2>;
reg-io-width = <4>;
clocks = <&l4_sp_clk>;
+ dmas = <&pdma 30>,
+ <&pdma 31>;
+ dma-names = "tx", "rx";
};
rst: rstmgr@ffd05000 {
model = "Olimex A10-OLinuXino-LIME";
compatible = "olimex,a10-olinuxino-lime", "allwinner,sun4i-a10";
+ cpus {
+ cpu0: cpu@0 {
+ /*
+ * The A10-Lime is known to be unstable
+ * when running at 1008 MHz
+ */
+ operating-points = <
+ /* kHz uV */
+ 912000 1350000
+ 864000 1300000
+ 624000 1250000
+ >;
+ cooling-max-level = <2>;
+ };
+ };
+
soc@01c00000 {
emac: ethernet@01c0b000 {
pinctrl-names = "default";
clock-latency = <244144>; /* 8 32k periods */
operating-points = <
/* kHz uV */
- 1056000 1500000
1008000 1400000
912000 1350000
864000 1300000
>;
#cooling-cells = <2>;
cooling-min-level = <0>;
- cooling-max-level = <4>;
+ cooling-max-level = <3>;
};
};
clock-latency = <244144>; /* 8 32k periods */
operating-points = <
/* kHz uV */
- 1104000 1500000
1008000 1400000
912000 1350000
864000 1300000
>;
#cooling-cells = <2>;
cooling-min-level = <0>;
- cooling-max-level = <6>;
+ cooling-max-level = <5>;
};
};
clock-latency = <244144>; /* 8 32k periods */
operating-points = <
/* kHz uV */
- 1008000 1450000
960000 1400000
912000 1400000
864000 1300000
>;
#cooling-cells = <2>;
cooling-min-level = <0>;
- cooling-max-level = <7>;
+ cooling-max-level = <6>;
};
cpu@1 {
CONFIG_BLK_DEV_SD=y
# CONFIG_SCSI_LOWLEVEL is not set
CONFIG_NETDEVICES=y
+CONFIG_ARM_AT91_ETHER=y
CONFIG_MACB=y
# CONFIG_NET_VENDOR_BROADCOM is not set
CONFIG_DM9000=y
CONFIG_ARCH_STI=y
CONFIG_ARCH_EXYNOS=y
CONFIG_EXYNOS5420_MCPM=y
+CONFIG_ARCH_SHMOBILE_MULTI=y
+CONFIG_ARCH_EMEV2=y
+CONFIG_ARCH_R7S72100=y
+CONFIG_ARCH_R8A73A4=y
+CONFIG_ARCH_R8A7740=y
+CONFIG_ARCH_R8A7779=y
+CONFIG_ARCH_R8A7790=y
+CONFIG_ARCH_R8A7791=y
+CONFIG_ARCH_R8A7794=y
+CONFIG_ARCH_SH73A0=y
+CONFIG_MACH_MARZEN=y
CONFIG_ARCH_SUNXI=y
CONFIG_ARCH_SIRF=y
CONFIG_ARCH_TEGRA=y
CONFIG_PCI_MSI=y
CONFIG_PCI_MVEBU=y
CONFIG_PCI_TEGRA=y
+CONFIG_PCI_RCAR_GEN2=y
+CONFIG_PCI_RCAR_GEN2_PCIE=y
CONFIG_PCIEPORTBUS=y
CONFIG_SMP=y
-CONFIG_NR_CPUS=8
+CONFIG_NR_CPUS=16
CONFIG_HIGHPTE=y
CONFIG_CMA=y
CONFIG_ARM_APPENDED_DTB=y
CONFIG_DMA_CMA=y
CONFIG_CMA_SIZE_MBYTES=64
CONFIG_OMAP_OCP2SCP=y
+CONFIG_SIMPLE_PM_BUS=y
CONFIG_MTD=y
CONFIG_MTD_CMDLINE_PARTS=y
CONFIG_MTD_BLOCK=y
CONFIG_AHCI_TEGRA=y
CONFIG_SATA_HIGHBANK=y
CONFIG_SATA_MV=y
+CONFIG_SATA_RCAR=y
CONFIG_NETDEVICES=y
CONFIG_HIX5HD2_GMAC=y
CONFIG_SUN4I_EMAC=y
CONFIG_MVNETA=y
CONFIG_KS8851=y
CONFIG_R8169=y
+CONFIG_SH_ETH=y
CONFIG_SMSC911X=y
CONFIG_STMMAC_ETH=y
CONFIG_TI_CPSW=y
CONFIG_XILINX_EMACLITE=y
CONFIG_AT803X_PHY=y
CONFIG_MARVELL_PHY=y
+CONFIG_SMSC_PHY=y
CONFIG_BROADCOM_PHY=y
CONFIG_ICPLUS_PHY=y
+CONFIG_MICREL_PHY=y
CONFIG_USB_PEGASUS=y
CONFIG_USB_USBNET=y
CONFIG_USB_NET_SMSC75XX=y
CONFIG_MOUSE_PS2_ELANTECH=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_ATMEL_MXT=y
+CONFIG_TOUCHSCREEN_ST1232=m
CONFIG_TOUCHSCREEN_STMPE=y
CONFIG_TOUCHSCREEN_SUN4I=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_MPU3050=y
CONFIG_INPUT_AXP20X_PEK=y
+CONFIG_INPUT_ADXL34X=m
CONFIG_SERIO_AMBAKMI=y
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_DW=y
+CONFIG_SERIAL_8250_EM=y
CONFIG_SERIAL_8250_MT6577=y
CONFIG_SERIAL_AMBA_PL011=y
CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
CONFIG_SERIAL_TEGRA=y
CONFIG_SERIAL_IMX=y
CONFIG_SERIAL_IMX_CONSOLE=y
+CONFIG_SERIAL_SH_SCI=y
+CONFIG_SERIAL_SH_SCI_NR_UARTS=20
+CONFIG_SERIAL_SH_SCI_CONSOLE=y
CONFIG_SERIAL_MSM=y
CONFIG_SERIAL_MSM_CONSOLE=y
CONFIG_SERIAL_VT8500=y
CONFIG_I2C_MUX_PINCTRL=y
CONFIG_I2C_CADENCE=y
CONFIG_I2C_DESIGNWARE_PLATFORM=y
+CONFIG_I2C_GPIO=m
CONFIG_I2C_EXYNOS5=y
CONFIG_I2C_MV64XXX=y
+CONFIG_I2C_RIIC=y
CONFIG_I2C_S3C2410=y
+CONFIG_I2C_SH_MOBILE=y
CONFIG_I2C_SIRF=y
-CONFIG_I2C_TEGRA=y
CONFIG_I2C_ST=y
-CONFIG_SPI=y
+CONFIG_I2C_TEGRA=y
CONFIG_I2C_XILINX=y
-CONFIG_SPI_DAVINCI=y
+CONFIG_I2C_RCAR=y
+CONFIG_SPI=y
CONFIG_SPI_CADENCE=y
+CONFIG_SPI_DAVINCI=y
CONFIG_SPI_OMAP24XX=y
CONFIG_SPI_ORION=y
CONFIG_SPI_PL022=y
+CONFIG_SPI_RSPI=y
+CONFIG_SPI_SH_MSIOF=m
+CONFIG_SPI_SH_HSPI=y
CONFIG_SPI_SIRF=y
CONFIG_SPI_SUN4I=y
CONFIG_SPI_SUN6I=y
CONFIG_PINCTRL_APQ8084=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_GENERIC_PLATFORM=y
-CONFIG_GPIO_DWAPB=y
CONFIG_GPIO_DAVINCI=y
+CONFIG_GPIO_DWAPB=y
+CONFIG_GPIO_EM=y
+CONFIG_GPIO_RCAR=y
CONFIG_GPIO_XILINX=y
CONFIG_GPIO_ZYNQ=y
CONFIG_GPIO_PCA953X=y
CONFIG_GPIO_PCA953X_IRQ=y
+CONFIG_GPIO_PCF857X=y
CONFIG_GPIO_TWL4030=y
CONFIG_GPIO_PALMAS=y
CONFIG_GPIO_SYSCON=y
CONFIG_POWER_RESET_GPIO=y
CONFIG_POWER_RESET_KEYSTONE=y
CONFIG_POWER_RESET_SUN6I=y
+CONFIG_POWER_RESET_RMOBILE=y
CONFIG_SENSORS_LM90=y
CONFIG_SENSORS_LM95245=y
CONFIG_THERMAL=y
CONFIG_CPU_THERMAL=y
+CONFIG_RCAR_THERMAL=y
CONFIG_ARMADA_THERMAL=y
CONFIG_DAVINCI_WATCHDOG
CONFIG_ST_THERMAL_SYSCFG=y
CONFIG_ORION_WATCHDOG=y
CONFIG_SUNXI_WATCHDOG=y
CONFIG_MESON_WATCHDOG=y
+CONFIG_MFD_AS3711=y
CONFIG_MFD_AS3722=y
CONFIG_MFD_BCM590XX=y
CONFIG_MFD_AXP20X=y
CONFIG_MFD_TPS6586X=y
CONFIG_MFD_TPS65910=y
CONFIG_REGULATOR_AB8500=y
+CONFIG_REGULATOR_AS3711=y
CONFIG_REGULATOR_AS3722=y
CONFIG_REGULATOR_AXP20X=y
CONFIG_REGULATOR_BCM590XX=y
+CONFIG_REGULATOR_DA9210=y
CONFIG_REGULATOR_GPIO=y
CONFIG_MFD_SYSCON=y
CONFIG_POWER_RESET_SYSCON=y
CONFIG_REGULATOR_MAX8907=y
+CONFIG_REGULATOR_MAX8973=y
CONFIG_REGULATOR_MAX77686=y
CONFIG_REGULATOR_PALMAS=y
CONFIG_REGULATOR_S2MPS11=y
CONFIG_REGULATOR_VEXPRESS=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_CONTROLLER=y
+CONFIG_VIDEO_V4L2_SUBDEV_API=y
CONFIG_MEDIA_USB_SUPPORT=y
CONFIG_USB_VIDEO_CLASS=y
CONFIG_USB_GSPCA=y
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_SOC_CAMERA=m
+CONFIG_SOC_CAMERA_PLATFORM=m
+CONFIG_VIDEO_RCAR_VIN=m
+CONFIG_V4L_MEM2MEM_DRIVERS=y
+CONFIG_VIDEO_RENESAS_VSP1=m
+# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set
+CONFIG_VIDEO_ADV7180=m
CONFIG_DRM=y
+CONFIG_DRM_RCAR_DU=m
CONFIG_DRM_TEGRA=y
CONFIG_DRM_PANEL_SIMPLE=y
CONFIG_FB_ARMCLCD=y
CONFIG_FB_WM8505=y
+CONFIG_FB_SH_MOBILE_LCDC=y
CONFIG_FB_SIMPLE=y
+CONFIG_FB_SH_MOBILE_MERAM=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_BACKLIGHT_CLASS_DEVICE=y
CONFIG_BACKLIGHT_PWM=y
+CONFIG_BACKLIGHT_AS3711=y
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
CONFIG_SOUND=y
CONFIG_SND_DYNAMIC_MINORS=y
CONFIG_SND_USB_AUDIO=y
CONFIG_SND_SOC=y
+CONFIG_SND_SOC_SH4_FSI=m
+CONFIG_SND_SOC_RCAR=m
CONFIG_SND_SOC_TEGRA=y
CONFIG_SND_SOC_TEGRA_RT5640=y
CONFIG_SND_SOC_TEGRA_WM8753=y
CONFIG_SND_SOC_TEGRA_TRIMSLICE=y
CONFIG_SND_SOC_TEGRA_ALC5632=y
CONFIG_SND_SOC_TEGRA_MAX98090=y
+CONFIG_SND_SOC_AK4642=m
+CONFIG_SND_SOC_WM8978=m
CONFIG_USB=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_XHCI_MVEBU=y
CONFIG_USB_OHCI_HCD=y
CONFIG_USB_OHCI_HCD_STI=y
CONFIG_USB_OHCI_HCD_PLATFORM=y
+CONFIG_USB_R8A66597_HCD=m
+CONFIG_USB_RENESAS_USBHS=m
CONFIG_USB_STORAGE=y
CONFIG_USB_DWC3=y
CONFIG_USB_CHIPIDEA=y
CONFIG_USB_GPIO_VBUS=y
CONFIG_USB_ISP1301=y
CONFIG_USB_MXS_PHY=y
+CONFIG_USB_RCAR_PHY=m
+CONFIG_USB_RCAR_GEN2_PHY=m
+CONFIG_USB_GADGET=y
+CONFIG_USB_RENESAS_USBHS_UDC=m
CONFIG_MMC=y
CONFIG_MMC_BLOCK_MINORS=16
CONFIG_MMC_ARMMMCI=y
CONFIG_MMC_OMAP=y
CONFIG_MMC_OMAP_HS=y
CONFIG_MMC_MVSDIO=y
-CONFIG_MMC_SUNXI=y
+CONFIG_MMC_SDHI=y
CONFIG_MMC_DW=y
CONFIG_MMC_DW_IDMAC=y
CONFIG_MMC_DW_PLTFM=y
CONFIG_MMC_DW_EXYNOS=y
CONFIG_MMC_DW_ROCKCHIP=y
+CONFIG_MMC_SH_MMCIF=y
+CONFIG_MMC_SUNXI=y
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_GPIO=y
CONFIG_RTC_DRV_DS1307=y
CONFIG_RTC_DRV_MAX8907=y
CONFIG_RTC_DRV_MAX77686=y
+CONFIG_RTC_DRV_RS5C372=m
CONFIG_RTC_DRV_PALMAS=y
CONFIG_RTC_DRV_TWL4030=y
CONFIG_RTC_DRV_TPS6586X=y
CONFIG_RTC_DRV_TPS65910=y
+CONFIG_RTC_DRV_S35390A=m
CONFIG_RTC_DRV_EM3027=y
CONFIG_RTC_DRV_PL031=y
CONFIG_RTC_DRV_VT8500=y
CONFIG_DW_DMAC=y
CONFIG_MV_XOR=y
CONFIG_TEGRA20_APB_DMA=y
+CONFIG_SH_DMAE=y
+CONFIG_RCAR_AUDMAC_PP=m
+CONFIG_RCAR_DMAC=y
CONFIG_STE_DMA40=y
CONFIG_SIRF_DMA=y
CONFIG_TI_EDMA=y
CONFIG_XILINX_XADC=y
CONFIG_AK8975=y
CONFIG_PWM=y
+CONFIG_PWM_RENESAS_TPU=y
CONFIG_PWM_TEGRA=y
CONFIG_PWM_VT8500=y
CONFIG_PHY_HIX5HD2_SATA=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_ECC_BCH=y
CONFIG_MTD_NAND_OMAP2=y
+CONFIG_MTD_NAND_OMAP_BCH=y
CONFIG_MTD_ONENAND=y
CONFIG_MTD_ONENAND_VERIFY_WRITE=y
CONFIG_MTD_ONENAND_OMAP2=y
CONFIG_REGULATOR_PALMAS=y
CONFIG_REGULATOR_PBIAS=y
CONFIG_REGULATOR_TI_ABB=y
+CONFIG_REGULATOR_TPS62360=m
CONFIG_REGULATOR_TPS65023=y
CONFIG_REGULATOR_TPS6507X=y
CONFIG_REGULATOR_TPS65217=y
CONFIG_PWM_TWL=m
CONFIG_PWM_TWL_LED=m
CONFIG_OMAP_USB2=m
-CONFIG_TI_PIPE3=m
+CONFIG_TI_PIPE3=y
+CONFIG_TWL4030_USB=m
CONFIG_EXT2_FS=y
CONFIG_EXT3_FS=y
# CONFIG_EXT3_FS_XATTR is not set
CONFIG_SYSVIPC=y
CONFIG_IRQ_DOMAIN_DEBUG=y
CONFIG_LOG_BUF_SHIFT=14
-CONFIG_SYSFS_DEPRECATED=y
-CONFIG_SYSFS_DEPRECATED_V2=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_EMBEDDED=y
CONFIG_SLAB=y
CONFIG_PERF_EVENTS=y
CONFIG_ARCH_SUNXI=y
CONFIG_SMP=y
+CONFIG_NR_CPUS=8
CONFIG_AEABI=y
CONFIG_HIGHMEM=y
CONFIG_HIGHPTE=y
CONFIG_USB=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_MON=y
-CONFIG_USB_ISP1760_HCD=y
CONFIG_USB_STORAGE=y
+CONFIG_USB_ISP1760=y
CONFIG_MMC=y
CONFIG_MMC_ARMMMCI=y
CONFIG_NEW_LEDS=y
# define VFP_ABI_FRAME 0
# define BSAES_ASM_EXTENDED_KEY
# define XTS_CHAIN_TWEAK
-# define __ARM_ARCH__ 7
+# define __ARM_ARCH__ __LINUX_ARM_ARCH__
+# define __ARM_MAX_ARCH__ 7
#endif
#ifdef __thumb__
# define adrl adr
#endif
-#if __ARM_ARCH__>=7
+#if __ARM_MAX_ARCH__>=7
+.arch armv7-a
+.fpu neon
+
.text
.syntax unified @ ARMv7-capable assembler is expected to handle this
#ifdef __thumb2__
.code 32
#endif
-.fpu neon
-
.type _bsaes_decrypt8,%function
.align 4
_bsaes_decrypt8:
vld1.8 {q8}, [r0] @ initial tweak
adr r2, .Lxts_magic
+#ifndef XTS_CHAIN_TWEAK
tst r9, #0xf @ if not multiple of 16
it ne @ Thumb2 thing, sanity check in ARM
subne r9, #0x10 @ subtract another 16 bytes
+#endif
subs r9, #0x80
blo .Lxts_dec_short
# define VFP_ABI_FRAME 0
# define BSAES_ASM_EXTENDED_KEY
# define XTS_CHAIN_TWEAK
-# define __ARM_ARCH__ 7
+# define __ARM_ARCH__ __LINUX_ARM_ARCH__
+# define __ARM_MAX_ARCH__ 7
#endif
#ifdef __thumb__
# define adrl adr
#endif
-#if __ARM_ARCH__>=7
+#if __ARM_MAX_ARCH__>=7
+.arch armv7-a
+.fpu neon
+
.text
.syntax unified @ ARMv7-capable assembler is expected to handle this
#ifdef __thumb2__
.code 32
#endif
-.fpu neon
-
.type _bsaes_decrypt8,%function
.align 4
_bsaes_decrypt8:
vld1.8 {@XMM[8]}, [r0] @ initial tweak
adr $magic, .Lxts_magic
+#ifndef XTS_CHAIN_TWEAK
tst $len, #0xf @ if not multiple of 16
it ne @ Thumb2 thing, sanity check in ARM
subne $len, #0x10 @ subtract another 16 bytes
+#endif
subs $len, #0x80
blo .Lxts_dec_short
(__boundary - 1 < (end) - 1)? __boundary: (end); \
})
+#define kvm_pgd_index(addr) pgd_index(addr)
+
static inline bool kvm_page_empty(void *ptr)
{
struct page *ptr_page = virt_to_page(ptr);
return page_count(ptr_page) == 1;
}
-
#define kvm_pte_table_empty(kvm, ptep) kvm_page_empty(ptep)
#define kvm_pmd_table_empty(kvm, pmdp) kvm_page_empty(pmdp)
#define kvm_pud_table_empty(kvm, pudp) (0)
#define KVM_PREALLOC_LEVEL 0
-static inline int kvm_prealloc_hwpgd(struct kvm *kvm, pgd_t *pgd)
+static inline void *kvm_get_hwpgd(struct kvm *kvm)
{
- return 0;
+ return kvm->arch.pgd;
}
-static inline void kvm_free_hwpgd(struct kvm *kvm) { }
-
-static inline void *kvm_get_hwpgd(struct kvm *kvm)
+static inline unsigned int kvm_get_hwpgd_size(void)
{
- return kvm->arch.pgd;
+ return PTRS_PER_S2_PGD * sizeof(pgd_t);
}
struct kvm;
bool need_flush = !vcpu_has_cache_enabled(vcpu) || ipa_uncached;
- VM_BUG_ON(size & PAGE_MASK);
+ VM_BUG_ON(size & ~PAGE_MASK);
if (!need_flush && !icache_is_pipt())
goto vipt_cache;
#define AT91_DBGU 0xfc00c000 /* SAMA5D4_BASE_USART3 */
#endif
-/* Keep in sync with mach-at91/include/mach/hardware.h */
+#ifdef CONFIG_MMU
#define AT91_IO_P2V(x) ((x) - 0x01000000)
+#else
+#define AT91_IO_P2V(x) (x)
+#endif
#define AT91_DBGU_SR (0x14) /* Status Register */
#define AT91_DBGU_THR (0x1c) /* Transmitter Holding Register */
if (cpu_arch)
cpu_arch += CPU_ARCH_ARMv3;
} else if ((read_cpuid_id() & 0x000f0000) == 0x000f0000) {
- unsigned int mmfr0;
-
/* Revised CPUID format. Read the Memory Model Feature
* Register 0 and check for VMSAv7 or PMSAv7 */
- asm("mrc p15, 0, %0, c0, c1, 4"
- : "=r" (mmfr0));
+ unsigned int mmfr0 = read_cpuid_ext(CPUID_EXT_MMFR0);
if ((mmfr0 & 0x0000000f) >= 0x00000003 ||
(mmfr0 & 0x000000f0) >= 0x00000030)
cpu_arch = CPU_ARCH_ARMv7;
vcpu->mode = OUTSIDE_GUEST_MODE;
kvm_guest_exit();
- trace_kvm_exit(*vcpu_pc(vcpu));
+ trace_kvm_exit(kvm_vcpu_trap_get_class(vcpu), *vcpu_pc(vcpu));
/*
* We may have taken a host interrupt in HYP mode (ie
* while executing the guest). This interrupt is still
phys_addr_t addr = start, end = start + size;
phys_addr_t next;
- pgd = pgdp + pgd_index(addr);
+ pgd = pgdp + kvm_pgd_index(addr);
do {
next = kvm_pgd_addr_end(addr, end);
if (!pgd_none(*pgd))
phys_addr_t next;
pgd_t *pgd;
- pgd = kvm->arch.pgd + pgd_index(addr);
+ pgd = kvm->arch.pgd + kvm_pgd_index(addr);
do {
next = kvm_pgd_addr_end(addr, end);
stage2_flush_puds(kvm, pgd, addr, next);
__phys_to_pfn(phys_addr), PAGE_HYP_DEVICE);
}
+/* Free the HW pgd, one page at a time */
+static void kvm_free_hwpgd(void *hwpgd)
+{
+ free_pages_exact(hwpgd, kvm_get_hwpgd_size());
+}
+
+/* Allocate the HW PGD, making sure that each page gets its own refcount */
+static void *kvm_alloc_hwpgd(void)
+{
+ unsigned int size = kvm_get_hwpgd_size();
+
+ return alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO);
+}
+
/**
* kvm_alloc_stage2_pgd - allocate level-1 table for stage-2 translation.
* @kvm: The KVM struct pointer for the VM.
*/
int kvm_alloc_stage2_pgd(struct kvm *kvm)
{
- int ret;
pgd_t *pgd;
+ void *hwpgd;
if (kvm->arch.pgd != NULL) {
kvm_err("kvm_arch already initialized?\n");
return -EINVAL;
}
+ hwpgd = kvm_alloc_hwpgd();
+ if (!hwpgd)
+ return -ENOMEM;
+
+ /* When the kernel uses more levels of page tables than the
+ * guest, we allocate a fake PGD and pre-populate it to point
+ * to the next-level page table, which will be the real
+ * initial page table pointed to by the VTTBR.
+ *
+ * When KVM_PREALLOC_LEVEL==2, we allocate a single page for
+ * the PMD and the kernel will use folded pud.
+ * When KVM_PREALLOC_LEVEL==1, we allocate 2 consecutive PUD
+ * pages.
+ */
if (KVM_PREALLOC_LEVEL > 0) {
+ int i;
+
/*
* Allocate fake pgd for the page table manipulation macros to
* work. This is not used by the hardware and we have no
*/
pgd = (pgd_t *)kmalloc(PTRS_PER_S2_PGD * sizeof(pgd_t),
GFP_KERNEL | __GFP_ZERO);
+
+ if (!pgd) {
+ kvm_free_hwpgd(hwpgd);
+ return -ENOMEM;
+ }
+
+ /* Plug the HW PGD into the fake one. */
+ for (i = 0; i < PTRS_PER_S2_PGD; i++) {
+ if (KVM_PREALLOC_LEVEL == 1)
+ pgd_populate(NULL, pgd + i,
+ (pud_t *)hwpgd + i * PTRS_PER_PUD);
+ else if (KVM_PREALLOC_LEVEL == 2)
+ pud_populate(NULL, pud_offset(pgd, 0) + i,
+ (pmd_t *)hwpgd + i * PTRS_PER_PMD);
+ }
} else {
/*
* Allocate actual first-level Stage-2 page table used by the
* hardware for Stage-2 page table walks.
*/
- pgd = (pgd_t *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, S2_PGD_ORDER);
+ pgd = (pgd_t *)hwpgd;
}
- if (!pgd)
- return -ENOMEM;
-
- ret = kvm_prealloc_hwpgd(kvm, pgd);
- if (ret)
- goto out_err;
-
kvm_clean_pgd(pgd);
kvm->arch.pgd = pgd;
return 0;
-out_err:
- if (KVM_PREALLOC_LEVEL > 0)
- kfree(pgd);
- else
- free_pages((unsigned long)pgd, S2_PGD_ORDER);
- return ret;
}
/**
return;
unmap_stage2_range(kvm, 0, KVM_PHYS_SIZE);
- kvm_free_hwpgd(kvm);
+ kvm_free_hwpgd(kvm_get_hwpgd(kvm));
if (KVM_PREALLOC_LEVEL > 0)
kfree(kvm->arch.pgd);
- else
- free_pages((unsigned long)kvm->arch.pgd, S2_PGD_ORDER);
+
kvm->arch.pgd = NULL;
}
pgd_t *pgd;
pud_t *pud;
- pgd = kvm->arch.pgd + pgd_index(addr);
+ pgd = kvm->arch.pgd + kvm_pgd_index(addr);
if (WARN_ON(pgd_none(*pgd))) {
if (!cache)
return NULL;
pgd_t *pgd;
phys_addr_t next;
- pgd = kvm->arch.pgd + pgd_index(addr);
+ pgd = kvm->arch.pgd + kvm_pgd_index(addr);
do {
/*
* Release kvm_mmu_lock periodically if the memory region is
);
TRACE_EVENT(kvm_exit,
- TP_PROTO(unsigned long vcpu_pc),
- TP_ARGS(vcpu_pc),
+ TP_PROTO(unsigned int exit_reason, unsigned long vcpu_pc),
+ TP_ARGS(exit_reason, vcpu_pc),
TP_STRUCT__entry(
+ __field( unsigned int, exit_reason )
__field( unsigned long, vcpu_pc )
),
TP_fast_assign(
+ __entry->exit_reason = exit_reason;
__entry->vcpu_pc = vcpu_pc;
),
- TP_printk("PC: 0x%08lx", __entry->vcpu_pc)
+ TP_printk("HSR_EC: 0x%04x, PC: 0x%08lx",
+ __entry->exit_reason,
+ __entry->vcpu_pc)
);
TRACE_EVENT(kvm_guest_fault,
bool "Alphascale ASM9260"
depends on ARCH_MULTI_V5
select CPU_ARM926T
+ select ASM9260_TIMER
+ select GENERIC_CLOCKEVENTS
help
Support for Alphascale ASM9260 based platform.
phys_addr_t sram_pbase;
unsigned long sram_base;
struct device_node *node;
- struct platform_device *pdev;
+ struct platform_device *pdev = NULL;
- node = of_find_compatible_node(NULL, NULL, "mmio-sram");
- if (!node) {
- pr_warn("%s: failed to find sram node!\n", __func__);
- return;
+ for_each_compatible_node(node, NULL, "mmio-sram") {
+ pdev = of_find_device_by_node(node);
+ if (pdev) {
+ of_node_put(node);
+ break;
+ }
}
- pdev = of_find_device_by_node(node);
if (!pdev) {
pr_warn("%s: failed to find sram device!\n", __func__);
- goto put_node;
+ return;
}
sram_pool = dev_get_gen_pool(&pdev->dev);
if (!sram_pool) {
pr_warn("%s: sram pool unavailable!\n", __func__);
- goto put_node;
+ return;
}
sram_base = gen_pool_alloc(sram_pool, at91_slow_clock_sz);
if (!sram_base) {
pr_warn("%s: unable to alloc ocram!\n", __func__);
- goto put_node;
+ return;
}
sram_pbase = gen_pool_virt_to_phys(sram_pool, sram_base);
slow_clock = __arm_ioremap_exec(sram_pbase, at91_slow_clock_sz, false);
-
-put_node:
- of_node_put(node);
}
#endif
" mcr p15, 0, %0, c7, c0, 4\n\t"
" str %5, [%1, %2]"
:
- : "r" (0), "r" (AT91_BASE_SYS), "r" (AT91RM9200_SDRAMC_LPR),
+ : "r" (0), "r" (at91_ramc_base[0]), "r" (AT91RM9200_SDRAMC_LPR),
"r" (1), "r" (AT91RM9200_SDRAMC_SRR),
"r" (lpr));
}
*/
#undef SLOWDOWN_MASTER_CLOCK
-#define MCKRDY_TIMEOUT 1000
-#define MOSCRDY_TIMEOUT 1000
-#define PLLALOCK_TIMEOUT 1000
-#define PLLBLOCK_TIMEOUT 1000
-
pmc .req r0
sdramc .req r1
ramc1 .req r2
* Wait until master clock is ready (after switching master clock source)
*/
.macro wait_mckrdy
- mov tmp2, #MCKRDY_TIMEOUT
-1: sub tmp2, tmp2, #1
- cmp tmp2, #0
- beq 2f
- ldr tmp1, [pmc, #AT91_PMC_SR]
+1: ldr tmp1, [pmc, #AT91_PMC_SR]
tst tmp1, #AT91_PMC_MCKRDY
beq 1b
-2:
.endm
/*
* Wait until master oscillator has stabilized.
*/
.macro wait_moscrdy
- mov tmp2, #MOSCRDY_TIMEOUT
-1: sub tmp2, tmp2, #1
- cmp tmp2, #0
- beq 2f
- ldr tmp1, [pmc, #AT91_PMC_SR]
+1: ldr tmp1, [pmc, #AT91_PMC_SR]
tst tmp1, #AT91_PMC_MOSCS
beq 1b
-2:
.endm
/*
* Wait until PLLA has locked.
*/
.macro wait_pllalock
- mov tmp2, #PLLALOCK_TIMEOUT
-1: sub tmp2, tmp2, #1
- cmp tmp2, #0
- beq 2f
- ldr tmp1, [pmc, #AT91_PMC_SR]
+1: ldr tmp1, [pmc, #AT91_PMC_SR]
tst tmp1, #AT91_PMC_LOCKA
beq 1b
-2:
.endm
/*
* Wait until PLLB has locked.
*/
.macro wait_pllblock
- mov tmp2, #PLLBLOCK_TIMEOUT
-1: sub tmp2, tmp2, #1
- cmp tmp2, #0
- beq 2f
- ldr tmp1, [pmc, #AT91_PMC_SR]
+1: ldr tmp1, [pmc, #AT91_PMC_SR]
tst tmp1, #AT91_PMC_LOCKB
beq 1b
-2:
.endm
.text
+ .arm
+
/* void at91_slow_clock(void __iomem *pmc, void __iomem *sdramc,
* void __iomem *ramc1, int memctrl)
*/
cmp memctrl, #AT91_MEMCTRL_DDRSDR
bne sdr_sr_enable
+ /* LPDDR1 --> force DDR2 mode during self-refresh */
+ ldr tmp1, [sdramc, #AT91_DDRSDRC_MDR]
+ str tmp1, .saved_sam9_mdr
+ bic tmp1, tmp1, #~AT91_DDRSDRC_MD
+ cmp tmp1, #AT91_DDRSDRC_MD_LOW_POWER_DDR
+ ldreq tmp1, [sdramc, #AT91_DDRSDRC_MDR]
+ biceq tmp1, tmp1, #AT91_DDRSDRC_MD
+ orreq tmp1, tmp1, #AT91_DDRSDRC_MD_DDR2
+ streq tmp1, [sdramc, #AT91_DDRSDRC_MDR]
+
/* prepare for DDRAM self-refresh mode */
ldr tmp1, [sdramc, #AT91_DDRSDRC_LPR]
str tmp1, .saved_sam9_lpr
/* figure out if we use the second ram controller */
cmp ramc1, #0
- ldrne tmp2, [ramc1, #AT91_DDRSDRC_LPR]
- strne tmp2, .saved_sam9_lpr1
- bicne tmp2, #AT91_DDRSDRC_LPCB
- orrne tmp2, #AT91_DDRSDRC_LPCB_SELF_REFRESH
+ beq ddr_no_2nd_ctrl
+
+ ldr tmp2, [ramc1, #AT91_DDRSDRC_MDR]
+ str tmp2, .saved_sam9_mdr1
+ bic tmp2, tmp2, #~AT91_DDRSDRC_MD
+ cmp tmp2, #AT91_DDRSDRC_MD_LOW_POWER_DDR
+ ldreq tmp2, [ramc1, #AT91_DDRSDRC_MDR]
+ biceq tmp2, tmp2, #AT91_DDRSDRC_MD
+ orreq tmp2, tmp2, #AT91_DDRSDRC_MD_DDR2
+ streq tmp2, [ramc1, #AT91_DDRSDRC_MDR]
+
+ ldr tmp2, [ramc1, #AT91_DDRSDRC_LPR]
+ str tmp2, .saved_sam9_lpr1
+ bic tmp2, #AT91_DDRSDRC_LPCB
+ orr tmp2, #AT91_DDRSDRC_LPCB_SELF_REFRESH
/* Enable DDRAM self-refresh mode */
+ str tmp2, [ramc1, #AT91_DDRSDRC_LPR]
+ddr_no_2nd_ctrl:
str tmp1, [sdramc, #AT91_DDRSDRC_LPR]
- strne tmp2, [ramc1, #AT91_DDRSDRC_LPR]
b sdr_sr_done
/* Turn off the main oscillator */
ldr tmp1, [pmc, #AT91_CKGR_MOR]
bic tmp1, tmp1, #AT91_PMC_MOSCEN
+ orr tmp1, tmp1, #AT91_PMC_KEY
str tmp1, [pmc, #AT91_CKGR_MOR]
/* Wait for interrupt */
/* Turn on the main oscillator */
ldr tmp1, [pmc, #AT91_CKGR_MOR]
orr tmp1, tmp1, #AT91_PMC_MOSCEN
+ orr tmp1, tmp1, #AT91_PMC_KEY
str tmp1, [pmc, #AT91_CKGR_MOR]
wait_moscrdy
*/
cmp memctrl, #AT91_MEMCTRL_DDRSDR
bne sdr_en_restore
+ /* Restore MDR in case of LPDDR1 */
+ ldr tmp1, .saved_sam9_mdr
+ str tmp1, [sdramc, #AT91_DDRSDRC_MDR]
/* Restore LPR on AT91 with DDRAM */
ldr tmp1, .saved_sam9_lpr
str tmp1, [sdramc, #AT91_DDRSDRC_LPR]
/* if we use the second ram controller */
cmp ramc1, #0
+ ldrne tmp2, .saved_sam9_mdr1
+ strne tmp2, [ramc1, #AT91_DDRSDRC_MDR]
ldrne tmp2, .saved_sam9_lpr1
strne tmp2, [ramc1, #AT91_DDRSDRC_LPR]
.saved_sam9_lpr1:
.word 0
+.saved_sam9_mdr:
+ .word 0
+
+.saved_sam9_mdr1:
+ .word 0
+
ENTRY(at91_slow_clock_sz)
.word .-at91_slow_clock
*/
void exynos_cpu_power_down(int cpu)
{
- if (cpu == 0 && (of_machine_is_compatible("samsung,exynos5420") ||
- of_machine_is_compatible("samsung,exynos5800"))) {
+ if (cpu == 0 && (soc_is_exynos5420() || soc_is_exynos5800())) {
/*
* Bypass power down for CPU0 during suspend. Check for
* the SYS_PWR_REG value to decide if we are suspending
of_genpd_add_provider_simple(np, &pd->pd);
}
+ /* Assign the child power domains to their parents */
+ for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") {
+ struct generic_pm_domain *child_domain, *parent_domain;
+ struct of_phandle_args args;
+
+ args.np = np;
+ args.args_count = 0;
+ child_domain = of_genpd_get_from_provider(&args);
+ if (!child_domain)
+ continue;
+
+ if (of_parse_phandle_with_args(np, "power-domains",
+ "#power-domain-cells", 0, &args) != 0)
+ continue;
+
+ parent_domain = of_genpd_get_from_provider(&args);
+ if (!parent_domain)
+ continue;
+
+ if (pm_genpd_add_subdomain(parent_domain, child_domain))
+ pr_warn("%s failed to add subdomain: %s\n",
+ parent_domain->name, child_domain->name);
+ else
+ pr_info("%s has as child subdomain: %s.\n",
+ parent_domain->name, child_domain->name);
+ of_node_put(np);
+ }
+
return 0;
}
arch_initcall(exynos4_pm_init_power_domain);
static u32 exynos_irqwake_intmask = 0xffffffff;
static const struct exynos_wkup_irq exynos3250_wkup_irq[] = {
- { 73, BIT(1) }, /* RTC alarm */
- { 74, BIT(2) }, /* RTC tick */
+ { 105, BIT(1) }, /* RTC alarm */
+ { 106, BIT(2) }, /* RTC tick */
{ /* sentinel */ },
};
* set bit IOMUXC_GPR1[21]. Or the PTP clock must be from pad
* (external OSC), and we need to clear the bit.
*/
- clksel = ptp_clk == enet_ref ? IMX6Q_GPR1_ENET_CLK_SEL_ANATOP :
- IMX6Q_GPR1_ENET_CLK_SEL_PAD;
+ clksel = clk_is_match(ptp_clk, enet_ref) ?
+ IMX6Q_GPR1_ENET_CLK_SEL_ANATOP :
+ IMX6Q_GPR1_ENET_CLK_SEL_PAD;
gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
if (!IS_ERR(gpr))
regmap_update_bits(gpr, IOMUXC_GPR1,
#include <linux/input.h>
#include <linux/io.h>
#include <linux/delay.h>
+#include <linux/smc91x.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
[1] = {
.start = MSM_GPIO_TO_INT(49),
.end = MSM_GPIO_TO_INT(49),
- .flags = IORESOURCE_IRQ,
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
},
};
+static struct smc91x_platdata smc91x_platdata = {
+ .flags = SMC91X_USE_16BIT | SMC91X_NOWAIT,
+};
+
static struct platform_device smc91x_device = {
.name = "smc91x",
.id = 0,
.num_resources = ARRAY_SIZE(smc91x_resources),
.resource = smc91x_resources,
+ .dev.platform_data = &smc91x_platdata,
};
static struct platform_device *devices[] __initdata = {
#include <linux/usb/msm_hsusb.h>
#include <linux/err.h>
#include <linux/clkdev.h>
+#include <linux/smc91x.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
.flags = IORESOURCE_MEM,
},
[1] = {
- .flags = IORESOURCE_IRQ,
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
},
};
+static struct smc91x_platdata smc91x_platdata = {
+ .flags = SMC91X_USE_16BIT | SMC91X_NOWAIT,
+};
+
static struct platform_device smc91x_device = {
.name = "smc91x",
.id = 0,
.num_resources = ARRAY_SIZE(smc91x_resources),
.resource = smc91x_resources,
+ .dev.platform_data = &smc91x_platdata,
};
static int __init msm_init_smc91x(void)
return kasprintf(GFP_KERNEL, "OMAP4");
else if (soc_is_omap54xx())
return kasprintf(GFP_KERNEL, "OMAP5");
+ else if (soc_is_am33xx() || soc_is_am335x())
+ return kasprintf(GFP_KERNEL, "AM33xx");
else if (soc_is_am43xx())
return kasprintf(GFP_KERNEL, "AM43xx");
else if (soc_is_dra7xx())
if (ret == -EBUSY)
pr_warn("omap_hwmod: %s: failed to hardreset\n", oh->name);
- if (!ret) {
+ if (oh->clkdm) {
/*
* Set the clockdomain to HW_AUTO, assuming that the
* previous state was HW_AUTO.
*/
- if (oh->clkdm && hwsup)
+ if (hwsup)
clkdm_allow_idle(oh->clkdm);
- } else {
- if (oh->clkdm)
- clkdm_hwmod_disable(oh->clkdm, oh);
+
+ clkdm_hwmod_disable(oh->clkdm, oh);
}
return ret;
INIT_LIST_HEAD(&oh->master_ports);
INIT_LIST_HEAD(&oh->slave_ports);
spin_lock_init(&oh->_lock);
+ lockdep_set_class(&oh->_lock, &oh->hwmod_key);
oh->_state = _HWMOD_STATE_REGISTERED;
u32 _sysc_cache;
void __iomem *_mpu_rt_va;
spinlock_t _lock;
+ struct lock_class_key hwmod_key; /* unique lock class */
struct list_head node;
struct omap_hwmod_ocp_if *_mpu_port;
unsigned int (*xlate_irq)(unsigned int);
*
*/
-static struct omap_hwmod_class dra7xx_pcie_hwmod_class = {
+static struct omap_hwmod_class dra7xx_pciess_hwmod_class = {
.name = "pcie",
};
/* pcie1 */
-static struct omap_hwmod dra7xx_pcie1_hwmod = {
+static struct omap_hwmod dra7xx_pciess1_hwmod = {
.name = "pcie1",
- .class = &dra7xx_pcie_hwmod_class,
+ .class = &dra7xx_pciess_hwmod_class,
.clkdm_name = "pcie_clkdm",
.main_clk = "l4_root_clk_div",
- .prcm = {
- .omap4 = {
- .clkctrl_offs = DRA7XX_CM_PCIE_CLKSTCTRL_OFFSET,
- .modulemode = MODULEMODE_SWCTRL,
- },
- },
-};
-
-/* pcie2 */
-static struct omap_hwmod dra7xx_pcie2_hwmod = {
- .name = "pcie2",
- .class = &dra7xx_pcie_hwmod_class,
- .clkdm_name = "pcie_clkdm",
- .main_clk = "l4_root_clk_div",
- .prcm = {
- .omap4 = {
- .clkctrl_offs = DRA7XX_CM_PCIE_CLKSTCTRL_OFFSET,
- .modulemode = MODULEMODE_SWCTRL,
- },
- },
-};
-
-/*
- * 'PCIE PHY' class
- *
- */
-
-static struct omap_hwmod_class dra7xx_pcie_phy_hwmod_class = {
- .name = "pcie-phy",
-};
-
-/* pcie1 phy */
-static struct omap_hwmod dra7xx_pcie1_phy_hwmod = {
- .name = "pcie1-phy",
- .class = &dra7xx_pcie_phy_hwmod_class,
- .clkdm_name = "l3init_clkdm",
- .main_clk = "l4_root_clk_div",
.prcm = {
.omap4 = {
.clkctrl_offs = DRA7XX_CM_L3INIT_PCIESS1_CLKCTRL_OFFSET,
},
};
-/* pcie2 phy */
-static struct omap_hwmod dra7xx_pcie2_phy_hwmod = {
- .name = "pcie2-phy",
- .class = &dra7xx_pcie_phy_hwmod_class,
- .clkdm_name = "l3init_clkdm",
+/* pcie2 */
+static struct omap_hwmod dra7xx_pciess2_hwmod = {
+ .name = "pcie2",
+ .class = &dra7xx_pciess_hwmod_class,
+ .clkdm_name = "pcie_clkdm",
.main_clk = "l4_root_clk_div",
.prcm = {
.omap4 = {
.user = OCP_USER_MPU | OCP_USER_SDMA,
};
-/* l3_main_1 -> pcie1 */
-static struct omap_hwmod_ocp_if dra7xx_l3_main_1__pcie1 = {
+/* l3_main_1 -> pciess1 */
+static struct omap_hwmod_ocp_if dra7xx_l3_main_1__pciess1 = {
.master = &dra7xx_l3_main_1_hwmod,
- .slave = &dra7xx_pcie1_hwmod,
+ .slave = &dra7xx_pciess1_hwmod,
.clk = "l3_iclk_div",
.user = OCP_USER_MPU | OCP_USER_SDMA,
};
-/* l4_cfg -> pcie1 */
-static struct omap_hwmod_ocp_if dra7xx_l4_cfg__pcie1 = {
+/* l4_cfg -> pciess1 */
+static struct omap_hwmod_ocp_if dra7xx_l4_cfg__pciess1 = {
.master = &dra7xx_l4_cfg_hwmod,
- .slave = &dra7xx_pcie1_hwmod,
+ .slave = &dra7xx_pciess1_hwmod,
.clk = "l4_root_clk_div",
.user = OCP_USER_MPU | OCP_USER_SDMA,
};
-/* l3_main_1 -> pcie2 */
-static struct omap_hwmod_ocp_if dra7xx_l3_main_1__pcie2 = {
+/* l3_main_1 -> pciess2 */
+static struct omap_hwmod_ocp_if dra7xx_l3_main_1__pciess2 = {
.master = &dra7xx_l3_main_1_hwmod,
- .slave = &dra7xx_pcie2_hwmod,
+ .slave = &dra7xx_pciess2_hwmod,
.clk = "l3_iclk_div",
.user = OCP_USER_MPU | OCP_USER_SDMA,
};
-/* l4_cfg -> pcie2 */
-static struct omap_hwmod_ocp_if dra7xx_l4_cfg__pcie2 = {
- .master = &dra7xx_l4_cfg_hwmod,
- .slave = &dra7xx_pcie2_hwmod,
- .clk = "l4_root_clk_div",
- .user = OCP_USER_MPU | OCP_USER_SDMA,
-};
-
-/* l4_cfg -> pcie1 phy */
-static struct omap_hwmod_ocp_if dra7xx_l4_cfg__pcie1_phy = {
- .master = &dra7xx_l4_cfg_hwmod,
- .slave = &dra7xx_pcie1_phy_hwmod,
- .clk = "l4_root_clk_div",
- .user = OCP_USER_MPU | OCP_USER_SDMA,
-};
-
-/* l4_cfg -> pcie2 phy */
-static struct omap_hwmod_ocp_if dra7xx_l4_cfg__pcie2_phy = {
+/* l4_cfg -> pciess2 */
+static struct omap_hwmod_ocp_if dra7xx_l4_cfg__pciess2 = {
.master = &dra7xx_l4_cfg_hwmod,
- .slave = &dra7xx_pcie2_phy_hwmod,
+ .slave = &dra7xx_pciess2_hwmod,
.clk = "l4_root_clk_div",
.user = OCP_USER_MPU | OCP_USER_SDMA,
};
&dra7xx_l4_cfg__mpu,
&dra7xx_l4_cfg__ocp2scp1,
&dra7xx_l4_cfg__ocp2scp3,
- &dra7xx_l3_main_1__pcie1,
- &dra7xx_l4_cfg__pcie1,
- &dra7xx_l3_main_1__pcie2,
- &dra7xx_l4_cfg__pcie2,
- &dra7xx_l4_cfg__pcie1_phy,
- &dra7xx_l4_cfg__pcie2_phy,
+ &dra7xx_l3_main_1__pciess1,
+ &dra7xx_l4_cfg__pciess1,
+ &dra7xx_l3_main_1__pciess2,
+ &dra7xx_l4_cfg__pciess2,
&dra7xx_l3_main_1__qspi,
&dra7xx_l4_per3__rtcss,
&dra7xx_l4_cfg__sata,
static void __init omap3_evm_legacy_init(void)
{
+ hsmmc2_internal_input_clk();
legacy_init_wl12xx(WL12XX_REFCLOCK_38, 0, 149);
}
{
saved_mask[0] =
omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST,
- OMAP4_PRM_IRQSTATUS_MPU_OFFSET);
+ OMAP4_PRM_IRQENABLE_MPU_OFFSET);
saved_mask[1] =
omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST,
- OMAP4_PRM_IRQSTATUS_MPU_2_OFFSET);
+ OMAP4_PRM_IRQENABLE_MPU_2_OFFSET);
omap4_prm_write_inst_reg(0, OMAP4430_PRM_OCP_SOCKET_INST,
OMAP4_PRM_IRQENABLE_MPU_OFFSET);
#include <linux/platform_data/video-pxafb.h>
#include <mach/bitfield.h>
#include <linux/platform_data/mmc-pxamci.h>
+#include <linux/smc91x.h>
#include "generic.h"
#include "devices.h"
}
};
+static struct smc91x_platdata smc91x_platdata = {
+ .flags = SMC91X_USE_32BIT | SMC91X_USE_DMA | SMC91X_NOWAIT,
+};
+
static struct platform_device smc91x_device = {
.name = "smc91x",
.id = 0,
.num_resources = ARRAY_SIZE(smc91x_resources),
.resource = smc91x_resources,
+ .dev.platform_data = &smc91x_platdata,
};
static void idp_backlight_power(int on)
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#include <linux/bitops.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#define ICHP_VAL_IRQ (1 << 31)
#define ICHP_IRQ(i) (((i) >> 16) & 0x7fff)
#define IPR_VALID (1 << 31)
-#define IRQ_BIT(n) (((n) - PXA_IRQ(0)) & 0x1f)
#define MAX_INTERNAL_IRQS 128
static void __iomem *pxa_irq_base;
static int pxa_internal_irq_nr;
static bool cpu_has_ipr;
+static struct irq_domain *pxa_irq_domain;
static inline void __iomem *irq_base(int i)
{
void pxa_mask_irq(struct irq_data *d)
{
void __iomem *base = irq_data_get_irq_chip_data(d);
+ irq_hw_number_t irq = irqd_to_hwirq(d);
uint32_t icmr = __raw_readl(base + ICMR);
- icmr &= ~(1 << IRQ_BIT(d->irq));
+ icmr &= ~BIT(irq & 0x1f);
__raw_writel(icmr, base + ICMR);
}
void pxa_unmask_irq(struct irq_data *d)
{
void __iomem *base = irq_data_get_irq_chip_data(d);
+ irq_hw_number_t irq = irqd_to_hwirq(d);
uint32_t icmr = __raw_readl(base + ICMR);
- icmr |= 1 << IRQ_BIT(d->irq);
+ icmr |= BIT(irq & 0x1f);
__raw_writel(icmr, base + ICMR);
}
} while (1);
}
-void __init pxa_init_irq(int irq_nr, int (*fn)(struct irq_data *, unsigned int))
+static int pxa_irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
{
- int irq, i, n;
+ void __iomem *base = irq_base(hw / 32);
- BUG_ON(irq_nr > MAX_INTERNAL_IRQS);
+ /* initialize interrupt priority */
+ if (cpu_has_ipr)
+ __raw_writel(hw | IPR_VALID, pxa_irq_base + IPR(hw));
+
+ irq_set_chip_and_handler(virq, &pxa_internal_irq_chip,
+ handle_level_irq);
+ irq_set_chip_data(virq, base);
+ set_irq_flags(virq, IRQF_VALID);
+
+ return 0;
+}
+
+static struct irq_domain_ops pxa_irq_ops = {
+ .map = pxa_irq_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static __init void
+pxa_init_irq_common(struct device_node *node, int irq_nr,
+ int (*fn)(struct irq_data *, unsigned int))
+{
+ int n;
pxa_internal_irq_nr = irq_nr;
- cpu_has_ipr = !cpu_is_pxa25x();
- pxa_irq_base = io_p2v(0x40d00000);
+ pxa_irq_domain = irq_domain_add_legacy(node, irq_nr,
+ PXA_IRQ(0), 0,
+ &pxa_irq_ops, NULL);
+ if (!pxa_irq_domain)
+ panic("Unable to add PXA IRQ domain\n");
+ irq_set_default_host(pxa_irq_domain);
for (n = 0; n < irq_nr; n += 32) {
void __iomem *base = irq_base(n >> 5);
__raw_writel(0, base + ICMR); /* disable all IRQs */
__raw_writel(0, base + ICLR); /* all IRQs are IRQ, not FIQ */
- for (i = n; (i < (n + 32)) && (i < irq_nr); i++) {
- /* initialize interrupt priority */
- if (cpu_has_ipr)
- __raw_writel(i | IPR_VALID, pxa_irq_base + IPR(i));
-
- irq = PXA_IRQ(i);
- irq_set_chip_and_handler(irq, &pxa_internal_irq_chip,
- handle_level_irq);
- irq_set_chip_data(irq, base);
- set_irq_flags(irq, IRQF_VALID);
- }
}
-
/* only unmasked interrupts kick us out of idle */
__raw_writel(1, irq_base(0) + ICCR);
pxa_internal_irq_chip.irq_set_wake = fn;
}
+void __init pxa_init_irq(int irq_nr, int (*fn)(struct irq_data *, unsigned int))
+{
+ BUG_ON(irq_nr > MAX_INTERNAL_IRQS);
+
+ pxa_irq_base = io_p2v(0x40d00000);
+ cpu_has_ipr = !cpu_is_pxa25x();
+ pxa_init_irq_common(NULL, irq_nr, fn);
+}
+
#ifdef CONFIG_PM
static unsigned long saved_icmr[MAX_INTERNAL_IRQS/32];
static unsigned long saved_ipr[MAX_INTERNAL_IRQS];
};
#ifdef CONFIG_OF
-static struct irq_domain *pxa_irq_domain;
-
-static int pxa_irq_map(struct irq_domain *h, unsigned int virq,
- irq_hw_number_t hw)
-{
- void __iomem *base = irq_base(hw / 32);
-
- /* initialize interrupt priority */
- if (cpu_has_ipr)
- __raw_writel(hw | IPR_VALID, pxa_irq_base + IPR(hw));
-
- irq_set_chip_and_handler(hw, &pxa_internal_irq_chip,
- handle_level_irq);
- irq_set_chip_data(hw, base);
- set_irq_flags(hw, IRQF_VALID);
-
- return 0;
-}
-
-static struct irq_domain_ops pxa_irq_ops = {
- .map = pxa_irq_map,
- .xlate = irq_domain_xlate_onecell,
-};
-
static const struct of_device_id intc_ids[] __initconst = {
{ .compatible = "marvell,pxa-intc", },
{}
{
struct device_node *node;
struct resource res;
- int n, ret;
+ int ret;
node = of_find_matching_node(NULL, intc_ids);
if (!node) {
return;
}
- pxa_irq_domain = irq_domain_add_legacy(node, pxa_internal_irq_nr, 0, 0,
- &pxa_irq_ops, NULL);
- if (!pxa_irq_domain)
- panic("Unable to add PXA IRQ domain\n");
-
- irq_set_default_host(pxa_irq_domain);
-
- for (n = 0; n < pxa_internal_irq_nr; n += 32) {
- void __iomem *base = irq_base(n >> 5);
-
- __raw_writel(0, base + ICMR); /* disable all IRQs */
- __raw_writel(0, base + ICLR); /* all IRQs are IRQ, not FIQ */
- }
-
- /* only unmasked interrupts kick us out of idle */
- __raw_writel(1, irq_base(0) + ICCR);
-
- pxa_internal_irq_chip.irq_set_wake = fn;
+ pxa_init_irq_common(node, pxa_internal_irq_nr, fn);
}
#endif /* CONFIG_OF */
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/pwm_backlight.h>
+#include <linux/smc91x.h>
#include <asm/types.h>
#include <asm/setup.h>
[1] = {
.start = LPD270_ETHERNET_IRQ,
.end = LPD270_ETHERNET_IRQ,
- .flags = IORESOURCE_IRQ,
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
},
};
+struct smc91x_platdata smc91x_platdata = {
+ .flags = SMC91X_USE_16BIT | SMC91X_NOWAIT,
+};
+
static struct platform_device smc91x_device = {
.name = "smc91x",
.id = 0,
.num_resources = ARRAY_SIZE(smc91x_resources),
.resource = smc91x_resources,
+ .dev.platform_data = &smc91x_platdata,
};
static struct resource lpd270_flash_resources[] = {
};
static struct platform_device can_regulator_device = {
- .name = "reg-fixed-volage",
+ .name = "reg-fixed-voltage",
.id = 0,
.dev = {
.platform_data = &can_regulator_pdata,
#include <linux/platform_data/video-clcd-versatile.h>
#include <linux/io.h>
#include <linux/smsc911x.h>
+#include <linux/smc91x.h>
#include <linux/ata_platform.h>
#include <linux/amba/mmci.h>
#include <linux/gfp.h>
.phy_interface = PHY_INTERFACE_MODE_MII,
};
+static struct smc91x_platdata smc91x_platdata = {
+ .flags = SMC91X_USE_32BIT | SMC91X_NOWAIT,
+};
+
static struct platform_device realview_eth_device = {
.name = "smsc911x",
.id = 0,
realview_eth_device.resource = res;
if (strcmp(realview_eth_device.name, "smsc911x") == 0)
realview_eth_device.dev.platform_data = &smsc911x_config;
+ else
+ realview_eth_device.dev.platform_data = &smc91x_platdata;
return platform_device_register(&realview_eth_device);
}
[1] = {
.start = IRQ_EB_ETH,
.end = IRQ_EB_ETH,
- .flags = IORESOURCE_IRQ,
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
},
};
#include <linux/pm.h>
#include <linux/serial_core.h>
#include <linux/slab.h>
+#include <linux/smc91x.h>
#include <asm/mach-types.h>
#include <asm/mach/map.h>
0x02000000, "smc91x-attrib"),
{ .flags = IORESOURCE_IRQ },
};
+ struct smc91x_platdata smc91x_platdata = {
+ .flags = SMC91X_USE_8BIT | SMC91X_IO_SHIFT_2 | SMC91X_NOWAIT,
+ };
struct platform_device_info smc91x_devinfo = {
.parent = &dev->dev,
.name = "smc91x",
.id = 0,
.res = smc91x_resources,
.num_res = ARRAY_SIZE(smc91x_resources),
+ .data = &smc91x_platdata,
+ .size_data = sizeof(smc91x_platdata),
};
int ret, irq;
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/mtd/partitions.h>
+#include <linux/smc91x.h>
#include <mach/hardware.h>
#include <asm/setup.h>
#endif
};
+static struct smc91x_platdata smc91x_platdata = {
+ .flags = SMC91X_USE_16BIT | SMC91X_NOWAIT,
+};
static struct platform_device smc91x_device = {
.name = "smc91x",
.id = 0,
.num_resources = ARRAY_SIZE(smc91x_resources),
.resource = smc91x_resources,
+ .dev = {
+ .platform_data = &smc91x_platdata,
+ },
};
static struct platform_device *devices[] __initdata = {
extern unsigned long socfpga_cpu1start_addr;
-#define SOCFPGA_SCU_VIRT_BASE 0xfffec000
+#define SOCFPGA_SCU_VIRT_BASE 0xfee00000
#endif
#include <asm/hardware/cache-l2x0.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
+#include <asm/cacheflush.h>
#include "core.h"
(u32 *) &socfpga_cpu1start_addr))
pr_err("SMP: Need cpu1-start-addr in device tree.\n");
+ /* Ensure that socfpga_cpu1start_addr is visible to other CPUs */
+ smp_wmb();
+ sync_cache_w(&socfpga_cpu1start_addr);
+
sys_manager_base_addr = of_iomap(np, 0);
np = of_find_compatible_node(NULL, NULL, "altr,rst-mgr");
"st,stih415",
"st,stih416",
"st,stih407",
+ "st,stih410",
"st,stih418",
NULL
};
menuconfig ARCH_SUNXI
bool "Allwinner SoCs" if ARCH_MULTI_V7
select ARCH_REQUIRE_GPIOLIB
+ select ARCH_HAS_RESET_CONTROLLER
select CLKSRC_MMIO
select GENERIC_IRQ_CHIP
select PINCTRL
select SUN4I_TIMER
+ select RESET_CONTROLLER
if ARCH_SUNXI
config MACH_SUN6I
bool "Allwinner A31 (sun6i) SoCs support"
default ARCH_SUNXI
- select ARCH_HAS_RESET_CONTROLLER
select ARM_GIC
select MFD_SUN6I_PRCM
- select RESET_CONTROLLER
select SUN5I_HSTIMER
config MACH_SUN7I
config MACH_SUN8I
bool "Allwinner A23 (sun8i) SoCs support"
default ARCH_SUNXI
- select ARCH_HAS_RESET_CONTROLLER
select ARM_GIC
select MFD_SUN6I_PRCM
- select RESET_CONTROLLER
config MACH_SUN9I
bool "Allwinner (sun9i) SoCs support"
default ARCH_SUNXI
- select ARCH_HAS_RESET_CONTROLLER
select ARM_GIC
- select RESET_CONTROLLER
endif
}
ret = l2x0_cache_size_of_parse(np, aux_val, aux_mask, &assoc, SZ_512K);
- if (ret)
- return;
-
- switch (assoc) {
- case 16:
- *aux_val &= ~L2X0_AUX_CTRL_ASSOC_MASK;
- *aux_val |= L310_AUX_CTRL_ASSOCIATIVITY_16;
- *aux_mask &= ~L2X0_AUX_CTRL_ASSOC_MASK;
- break;
- case 8:
- *aux_val &= ~L2X0_AUX_CTRL_ASSOC_MASK;
- *aux_mask &= ~L2X0_AUX_CTRL_ASSOC_MASK;
- break;
- default:
- pr_err("L2C-310 OF cache associativity %d invalid, only 8 or 16 permitted\n",
- assoc);
- break;
+ if (!ret) {
+ switch (assoc) {
+ case 16:
+ *aux_val &= ~L2X0_AUX_CTRL_ASSOC_MASK;
+ *aux_val |= L310_AUX_CTRL_ASSOCIATIVITY_16;
+ *aux_mask &= ~L2X0_AUX_CTRL_ASSOC_MASK;
+ break;
+ case 8:
+ *aux_val &= ~L2X0_AUX_CTRL_ASSOC_MASK;
+ *aux_mask &= ~L2X0_AUX_CTRL_ASSOC_MASK;
+ break;
+ default:
+ pr_err("L2C-310 OF cache associativity %d invalid, only 8 or 16 permitted\n",
+ assoc);
+ break;
+ }
}
prefetch = l2x0_saved_regs.prefetch_ctrl;
*/
if (sizeof(mask) != sizeof(dma_addr_t) &&
mask > (dma_addr_t)~0 &&
- dma_to_pfn(dev, ~0) < max_pfn) {
+ dma_to_pfn(dev, ~0) < max_pfn - 1) {
if (warn) {
dev_warn(dev, "Coherent DMA mask %#llx is larger than dma_addr_t allows\n",
mask);
pr_alert("Unhandled fault: %s (0x%03x) at 0x%08lx\n",
inf->name, fsr, addr);
+ show_pte(current->mm, addr);
info.si_signo = inf->sig;
info.si_errno = 0;
WARN_ON_ONCE(1);
}
- if (!is_module_address(start) || !is_module_address(end - 1))
+ if (start < MODULES_VADDR || start >= MODULES_END)
+ return -EINVAL;
+
+ if (end < MODULES_VADDR || start >= MODULES_END)
return -EINVAL;
data.set_mask = set_mask;
struct device *dev = &pdev->dev;
const struct of_device_id *match;
const struct dmtimer_platform_data *pdata;
+ int ret;
match = of_match_device(of_match_ptr(omap_timer_match), dev);
pdata = match ? match->data : dev->platform_data;
}
if (!timer->reserved) {
- pm_runtime_get_sync(dev);
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ dev_err(dev, "%s: pm_runtime_get_sync failed!\n",
+ __func__);
+ goto err_get_sync;
+ }
__omap_dm_timer_init_regs(timer);
pm_runtime_put(dev);
}
dev_dbg(dev, "Device Probed.\n");
return 0;
+
+err_get_sync:
+ pm_runtime_put_noidle(dev);
+ pm_runtime_disable(dev);
+ return ret;
}
/**
}
spin_unlock_irqrestore(&dm_timer_lock, flags);
+ pm_runtime_disable(&pdev->dev);
+
return ret;
}
};
sgenet0: ethernet@1f210000 {
- compatible = "apm,xgene-enet";
+ compatible = "apm,xgene1-sgenet";
status = "disabled";
reg = <0x0 0x1f210000 0x0 0xd100>,
<0x0 0x1f200000 0x0 0Xc300>,
};
xgenet: ethernet@1f610000 {
- compatible = "apm,xgene-enet";
+ compatible = "apm,xgene1-xgenet";
status = "disabled";
reg = <0x0 0x1f610000 0x0 0xd100>,
<0x0 0x1f600000 0x0 0Xc300>,
reg = <0x0 0x0>;
enable-method = "spin-table";
cpu-release-addr = <0x0 0x8000fff8>;
+ next-level-cache = <&L2_0>;
};
cpu@1 {
device_type = "cpu";
reg = <0x0 0x1>;
enable-method = "spin-table";
cpu-release-addr = <0x0 0x8000fff8>;
+ next-level-cache = <&L2_0>;
};
cpu@2 {
device_type = "cpu";
reg = <0x0 0x2>;
enable-method = "spin-table";
cpu-release-addr = <0x0 0x8000fff8>;
+ next-level-cache = <&L2_0>;
};
cpu@3 {
device_type = "cpu";
reg = <0x0 0x3>;
enable-method = "spin-table";
cpu-release-addr = <0x0 0x8000fff8>;
+ next-level-cache = <&L2_0>;
+ };
+
+ L2_0: l2-cache0 {
+ compatible = "cache";
};
};
*/
/* SoC fixed clocks */
- soc_uartclk: refclk72738khz {
+ soc_uartclk: refclk7273800hz {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <7273800>;
reg = <0x0 0x0>;
device_type = "cpu";
enable-method = "psci";
+ next-level-cache = <&A57_L2>;
};
A57_1: cpu@1 {
reg = <0x0 0x1>;
device_type = "cpu";
enable-method = "psci";
+ next-level-cache = <&A57_L2>;
};
A53_0: cpu@100 {
reg = <0x0 0x100>;
device_type = "cpu";
enable-method = "psci";
+ next-level-cache = <&A53_L2>;
};
A53_1: cpu@101 {
reg = <0x0 0x101>;
device_type = "cpu";
enable-method = "psci";
+ next-level-cache = <&A53_L2>;
};
A53_2: cpu@102 {
reg = <0x0 0x102>;
device_type = "cpu";
enable-method = "psci";
+ next-level-cache = <&A53_L2>;
};
A53_3: cpu@103 {
reg = <0x0 0x103>;
device_type = "cpu";
enable-method = "psci";
+ next-level-cache = <&A53_L2>;
+ };
+
+ A57_L2: l2-cache0 {
+ compatible = "cache";
+ };
+
+ A53_L2: l2-cache1 {
+ compatible = "cache";
};
};
reg = <0x0 0x0>;
enable-method = "spin-table";
cpu-release-addr = <0x0 0x8000fff8>;
+ next-level-cache = <&L2_0>;
};
cpu@1 {
device_type = "cpu";
reg = <0x0 0x1>;
enable-method = "spin-table";
cpu-release-addr = <0x0 0x8000fff8>;
+ next-level-cache = <&L2_0>;
};
cpu@2 {
device_type = "cpu";
reg = <0x0 0x2>;
enable-method = "spin-table";
cpu-release-addr = <0x0 0x8000fff8>;
+ next-level-cache = <&L2_0>;
};
cpu@3 {
device_type = "cpu";
reg = <0x0 0x3>;
enable-method = "spin-table";
cpu-release-addr = <0x0 0x8000fff8>;
+ next-level-cache = <&L2_0>;
+ };
+
+ L2_0: l2-cache0 {
+ compatible = "cache";
};
};
obj-$(CONFIG_CRYPTO_AES_ARM64_NEON_BLK) += aes-neon-blk.o
aes-neon-blk-y := aes-glue-neon.o aes-neon.o
-AFLAGS_aes-ce.o := -DINTERLEAVE=2 -DINTERLEAVE_INLINE
+AFLAGS_aes-ce.o := -DINTERLEAVE=4
AFLAGS_aes-neon.o := -DINTERLEAVE=4
CFLAGS_aes-glue-ce.o := -DUSE_V8_CRYPTO_EXTENSIONS
#error "Only include this from assembly code"
#endif
+#ifndef __ASM_ASSEMBLER_H
+#define __ASM_ASSEMBLER_H
+
#include <asm/ptrace.h>
#include <asm/thread_info.h>
#endif
orr \rd, \lbits, \hbits, lsl #32
.endm
+
+#endif /* __ASM_ASSEMBLER_H */
__ret; \
})
-#define this_cpu_cmpxchg_1(ptr, o, n) cmpxchg_local(raw_cpu_ptr(&(ptr)), o, n)
-#define this_cpu_cmpxchg_2(ptr, o, n) cmpxchg_local(raw_cpu_ptr(&(ptr)), o, n)
-#define this_cpu_cmpxchg_4(ptr, o, n) cmpxchg_local(raw_cpu_ptr(&(ptr)), o, n)
-#define this_cpu_cmpxchg_8(ptr, o, n) cmpxchg_local(raw_cpu_ptr(&(ptr)), o, n)
-
-#define this_cpu_cmpxchg_double_8(ptr1, ptr2, o1, o2, n1, n2) \
- cmpxchg_double_local(raw_cpu_ptr(&(ptr1)), raw_cpu_ptr(&(ptr2)), \
- o1, o2, n1, n2)
+#define _protect_cmpxchg_local(pcp, o, n) \
+({ \
+ typeof(*raw_cpu_ptr(&(pcp))) __ret; \
+ preempt_disable(); \
+ __ret = cmpxchg_local(raw_cpu_ptr(&(pcp)), o, n); \
+ preempt_enable(); \
+ __ret; \
+})
+
+#define this_cpu_cmpxchg_1(ptr, o, n) _protect_cmpxchg_local(ptr, o, n)
+#define this_cpu_cmpxchg_2(ptr, o, n) _protect_cmpxchg_local(ptr, o, n)
+#define this_cpu_cmpxchg_4(ptr, o, n) _protect_cmpxchg_local(ptr, o, n)
+#define this_cpu_cmpxchg_8(ptr, o, n) _protect_cmpxchg_local(ptr, o, n)
+
+#define this_cpu_cmpxchg_double_8(ptr1, ptr2, o1, o2, n1, n2) \
+({ \
+ int __ret; \
+ preempt_disable(); \
+ __ret = cmpxchg_double_local( raw_cpu_ptr(&(ptr1)), \
+ raw_cpu_ptr(&(ptr2)), \
+ o1, o2, n1, n2); \
+ preempt_enable(); \
+ __ret; \
+})
#define cmpxchg64(ptr,o,n) cmpxchg((ptr),(o),(n))
#define cmpxchg64_local(ptr,o,n) cmpxchg_local((ptr),(o),(n))
#ifndef __ASM_CPUIDLE_H
#define __ASM_CPUIDLE_H
+#include <asm/proc-fns.h>
+
#ifdef CONFIG_CPU_IDLE
extern int cpu_init_idle(unsigned int cpu);
extern int cpu_suspend(unsigned long arg);
__AARCH64_INSN_FUNCS(bics, 0x7F200000, 0x6A200000)
__AARCH64_INSN_FUNCS(b, 0xFC000000, 0x14000000)
__AARCH64_INSN_FUNCS(bl, 0xFC000000, 0x94000000)
-__AARCH64_INSN_FUNCS(cbz, 0xFE000000, 0x34000000)
-__AARCH64_INSN_FUNCS(cbnz, 0xFE000000, 0x35000000)
+__AARCH64_INSN_FUNCS(cbz, 0x7F000000, 0x34000000)
+__AARCH64_INSN_FUNCS(cbnz, 0x7F000000, 0x35000000)
+__AARCH64_INSN_FUNCS(tbz, 0x7F000000, 0x36000000)
+__AARCH64_INSN_FUNCS(tbnz, 0x7F000000, 0x37000000)
__AARCH64_INSN_FUNCS(bcond, 0xFF000010, 0x54000000)
__AARCH64_INSN_FUNCS(svc, 0xFFE0001F, 0xD4000001)
__AARCH64_INSN_FUNCS(hvc, 0xFFE0001F, 0xD4000002)
* 40 bits wide (T0SZ = 24). Systems with a PARange smaller than 40 bits are
* not known to exist and will break with this configuration.
*
+ * VTCR_EL2.PS is extracted from ID_AA64MMFR0_EL1.PARange at boot time
+ * (see hyp-init.S).
+ *
* Note that when using 4K pages, we concatenate two first level page tables
* together.
*
#ifdef CONFIG_ARM64_64K_PAGES
/*
* Stage2 translation configuration:
- * 40bits output (PS = 2)
* 40bits input (T0SZ = 24)
* 64kB pages (TG0 = 1)
* 2 level page tables (SL = 1)
#else
/*
* Stage2 translation configuration:
- * 40bits output (PS = 2)
* 40bits input (T0SZ = 24)
* 4kB pages (TG0 = 0)
* 3 level page tables (SL = 1)
#define PTRS_PER_S2_PGD (1 << PTRS_PER_S2_PGD_SHIFT)
#define S2_PGD_ORDER get_order(PTRS_PER_S2_PGD * sizeof(pgd_t))
+#define kvm_pgd_index(addr) (((addr) >> PGDIR_SHIFT) & (PTRS_PER_S2_PGD - 1))
+
/*
* If we are concatenating first level stage-2 page tables, we would have less
* than or equal to 16 pointers in the fake PGD, because that's what the
#define KVM_PREALLOC_LEVEL (0)
#endif
-/**
- * kvm_prealloc_hwpgd - allocate inital table for VTTBR
- * @kvm: The KVM struct pointer for the VM.
- * @pgd: The kernel pseudo pgd
- *
- * When the kernel uses more levels of page tables than the guest, we allocate
- * a fake PGD and pre-populate it to point to the next-level page table, which
- * will be the real initial page table pointed to by the VTTBR.
- *
- * When KVM_PREALLOC_LEVEL==2, we allocate a single page for the PMD and
- * the kernel will use folded pud. When KVM_PREALLOC_LEVEL==1, we
- * allocate 2 consecutive PUD pages.
- */
-static inline int kvm_prealloc_hwpgd(struct kvm *kvm, pgd_t *pgd)
-{
- unsigned int i;
- unsigned long hwpgd;
-
- if (KVM_PREALLOC_LEVEL == 0)
- return 0;
-
- hwpgd = __get_free_pages(GFP_KERNEL | __GFP_ZERO, PTRS_PER_S2_PGD_SHIFT);
- if (!hwpgd)
- return -ENOMEM;
-
- for (i = 0; i < PTRS_PER_S2_PGD; i++) {
- if (KVM_PREALLOC_LEVEL == 1)
- pgd_populate(NULL, pgd + i,
- (pud_t *)hwpgd + i * PTRS_PER_PUD);
- else if (KVM_PREALLOC_LEVEL == 2)
- pud_populate(NULL, pud_offset(pgd, 0) + i,
- (pmd_t *)hwpgd + i * PTRS_PER_PMD);
- }
-
- return 0;
-}
-
static inline void *kvm_get_hwpgd(struct kvm *kvm)
{
pgd_t *pgd = kvm->arch.pgd;
return pmd_offset(pud, 0);
}
-static inline void kvm_free_hwpgd(struct kvm *kvm)
+static inline unsigned int kvm_get_hwpgd_size(void)
{
- if (KVM_PREALLOC_LEVEL > 0) {
- unsigned long hwpgd = (unsigned long)kvm_get_hwpgd(kvm);
- free_pages(hwpgd, PTRS_PER_S2_PGD_SHIFT);
- }
+ if (KVM_PREALLOC_LEVEL > 0)
+ return PTRS_PER_S2_PGD * PAGE_SIZE;
+ return PTRS_PER_S2_PGD * sizeof(pgd_t);
}
static inline bool kvm_page_empty(void *ptr)
{
unsigned int cpu = smp_processor_id();
+ /*
+ * init_mm.pgd does not contain any user mappings and it is always
+ * active for kernel addresses in TTBR1. Just set the reserved TTBR0.
+ */
+ if (next == &init_mm) {
+ cpu_set_reserved_ttbr0();
+ return;
+ }
+
if (!cpumask_test_and_set_cpu(cpu, mm_cpumask(next)) || prev != next)
check_and_switch_context(next, tsk);
}
return ret;
}
+#define _percpu_read(pcp) \
+({ \
+ typeof(pcp) __retval; \
+ preempt_disable(); \
+ __retval = (typeof(pcp))__percpu_read(raw_cpu_ptr(&(pcp)), \
+ sizeof(pcp)); \
+ preempt_enable(); \
+ __retval; \
+})
+
+#define _percpu_write(pcp, val) \
+do { \
+ preempt_disable(); \
+ __percpu_write(raw_cpu_ptr(&(pcp)), (unsigned long)(val), \
+ sizeof(pcp)); \
+ preempt_enable(); \
+} while(0) \
+
+#define _pcp_protect(operation, pcp, val) \
+({ \
+ typeof(pcp) __retval; \
+ preempt_disable(); \
+ __retval = (typeof(pcp))operation(raw_cpu_ptr(&(pcp)), \
+ (val), sizeof(pcp)); \
+ preempt_enable(); \
+ __retval; \
+})
+
#define _percpu_add(pcp, val) \
- __percpu_add(raw_cpu_ptr(&(pcp)), val, sizeof(pcp))
+ _pcp_protect(__percpu_add, pcp, val)
-#define _percpu_add_return(pcp, val) (typeof(pcp)) (_percpu_add(pcp, val))
+#define _percpu_add_return(pcp, val) _percpu_add(pcp, val)
#define _percpu_and(pcp, val) \
- __percpu_and(raw_cpu_ptr(&(pcp)), val, sizeof(pcp))
+ _pcp_protect(__percpu_and, pcp, val)
#define _percpu_or(pcp, val) \
- __percpu_or(raw_cpu_ptr(&(pcp)), val, sizeof(pcp))
-
-#define _percpu_read(pcp) (typeof(pcp)) \
- (__percpu_read(raw_cpu_ptr(&(pcp)), sizeof(pcp)))
-
-#define _percpu_write(pcp, val) \
- __percpu_write(raw_cpu_ptr(&(pcp)), (unsigned long)(val), sizeof(pcp))
+ _pcp_protect(__percpu_or, pcp, val)
#define _percpu_xchg(pcp, val) (typeof(pcp)) \
- (__percpu_xchg(raw_cpu_ptr(&(pcp)), (unsigned long)(val), sizeof(pcp)))
+ _pcp_protect(__percpu_xchg, pcp, (unsigned long)(val))
#define this_cpu_add_1(pcp, val) _percpu_add(pcp, val)
#define this_cpu_add_2(pcp, val) _percpu_add(pcp, val)
static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
{
const pteval_t mask = PTE_USER | PTE_PXN | PTE_UXN | PTE_RDONLY |
- PTE_PROT_NONE | PTE_VALID | PTE_WRITE;
+ PTE_PROT_NONE | PTE_WRITE | PTE_TYPE_MASK;
pte_val(pte) = (pte_val(pte) & ~mask) | (pgprot_val(newprot) & mask);
return pte;
}
#include <asm/memory.h>
-#define cpu_switch_mm(pgd,mm) cpu_do_switch_mm(virt_to_phys(pgd),mm)
+#define cpu_switch_mm(pgd,mm) \
+do { \
+ BUG_ON(pgd == swapper_pg_dir); \
+ cpu_do_switch_mm(virt_to_phys(pgd),mm); \
+} while (0)
#define cpu_get_pgd() \
({ \
#define STACK_TOP STACK_TOP_MAX
#endif /* CONFIG_COMPAT */
-#define ARCH_LOW_ADDRESS_LIMIT PHYS_MASK
+extern phys_addr_t arm64_dma_phys_limit;
+#define ARCH_LOW_ADDRESS_LIMIT (arm64_dma_phys_limit - 1)
#endif /* __KERNEL__ */
struct debug_info {
static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
unsigned long addr)
{
+ __flush_tlb_pgtable(tlb->mm, addr);
pgtable_page_dtor(pte);
tlb_remove_entry(tlb, pte);
}
static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp,
unsigned long addr)
{
+ __flush_tlb_pgtable(tlb->mm, addr);
tlb_remove_entry(tlb, virt_to_page(pmdp));
}
#endif
static inline void __pud_free_tlb(struct mmu_gather *tlb, pud_t *pudp,
unsigned long addr)
{
+ __flush_tlb_pgtable(tlb->mm, addr);
tlb_remove_entry(tlb, virt_to_page(pudp));
}
#endif
#include <linux/sched.h>
#include <asm/cputype.h>
-extern void __cpu_flush_user_tlb_range(unsigned long, unsigned long, struct vm_area_struct *);
-extern void __cpu_flush_kern_tlb_range(unsigned long, unsigned long);
-
-extern struct cpu_tlb_fns cpu_tlb;
-
/*
* TLB Management
* ==============
flush_tlb_all();
}
+/*
+ * Used to invalidate the TLB (walk caches) corresponding to intermediate page
+ * table levels (pgd/pud/pmd).
+ */
+static inline void __flush_tlb_pgtable(struct mm_struct *mm,
+ unsigned long uaddr)
+{
+ unsigned long addr = uaddr >> 12 | ((unsigned long)ASID(mm) << 48);
+
+ dsb(ishst);
+ asm("tlbi vae1is, %0" : : "r" (addr));
+ dsb(ish);
+}
/*
* On AArch64, the cache coherency is handled via the set_pte_at() function.
*/
arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \
entry-fpsimd.o process.o ptrace.o setup.o signal.o \
sys.o stacktrace.o time.o traps.o io.o vdso.o \
- hyp-stub.o psci.o cpu_ops.o insn.o return_address.o \
- cpuinfo.o cpu_errata.o alternative.o cacheinfo.o
+ hyp-stub.o psci.o psci-call.o cpu_ops.o insn.o \
+ return_address.o cpuinfo.o cpu_errata.o \
+ alternative.o cacheinfo.o
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \
sys_compat.o entry32.o \
static void efi_set_pgd(struct mm_struct *mm)
{
- cpu_switch_mm(mm->pgd, mm);
+ if (mm == &init_mm)
+ cpu_set_reserved_ttbr0();
+ else
+ cpu_switch_mm(mm->pgd, mm);
+
flush_tlb_all();
if (icache_is_aivivt())
__flush_icache_all();
efi_set_pgd(current->active_mm);
preempt_enable();
}
+
+/*
+ * UpdateCapsule() depends on the system being shutdown via
+ * ResetSystem().
+ */
+bool efi_poweroff_required(void)
+{
+ return efi_enabled(EFI_RUNTIME_SERVICES);
+}
branch = aarch64_insn_gen_branch_imm(pc,
(unsigned long)ftrace_graph_caller,
- AARCH64_INSN_BRANCH_LINK);
+ AARCH64_INSN_BRANCH_NOLINK);
nop = aarch64_insn_gen_nop();
if (enable)
* zeroing of .bss would clobber it.
*/
.pushsection .data..cacheline_aligned
-ENTRY(__boot_cpu_mode)
.align L1_CACHE_SHIFT
+ENTRY(__boot_cpu_mode)
.long BOOT_CPU_MODE_EL2
.long 0
.popsection
if (module && IS_ENABLED(CONFIG_DEBUG_SET_MODULE_RONX))
page = vmalloc_to_page(addr);
- else
+ else if (!module && IS_ENABLED(CONFIG_DEBUG_RODATA))
page = virt_to_page(addr);
+ else
+ return addr;
BUG_ON(!page);
set_fixmap(fixmap, page_to_phys(page));
#include <stdarg.h>
#include <linux/compat.h>
+#include <linux/efi.h>
#include <linux/export.h>
#include <linux/sched.h>
#include <linux/kernel.h>
local_irq_disable();
smp_send_stop();
+ /*
+ * UpdateCapsule() depends on the system being reset via
+ * ResetSystem().
+ */
+ if (efi_enabled(EFI_RUNTIME_SERVICES))
+ efi_reboot(reboot_mode, NULL);
+
/* Now call the architecture specific reboot code. */
if (arm_pm_restart)
arm_pm_restart(reboot_mode, cmd);
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Copyright (C) 2015 ARM Limited
+ *
+ * Author: Will Deacon <will.deacon@arm.com>
+ */
+
+#include <linux/linkage.h>
+
+/* int __invoke_psci_fn_hvc(u64 function_id, u64 arg0, u64 arg1, u64 arg2) */
+ENTRY(__invoke_psci_fn_hvc)
+ hvc #0
+ ret
+ENDPROC(__invoke_psci_fn_hvc)
+
+/* int __invoke_psci_fn_smc(u64 function_id, u64 arg0, u64 arg1, u64 arg2) */
+ENTRY(__invoke_psci_fn_smc)
+ smc #0
+ ret
+ENDPROC(__invoke_psci_fn_smc)
static int (*invoke_psci_fn)(u64, u64, u64, u64);
typedef int (*psci_initcall_t)(const struct device_node *);
+asmlinkage int __invoke_psci_fn_hvc(u64, u64, u64, u64);
+asmlinkage int __invoke_psci_fn_smc(u64, u64, u64, u64);
+
enum psci_function {
PSCI_FN_CPU_SUSPEND,
PSCI_FN_CPU_ON,
PSCI_0_2_POWER_STATE_AFFL_SHIFT;
}
-/*
- * The following two functions are invoked via the invoke_psci_fn pointer
- * and will not be inlined, allowing us to piggyback on the AAPCS.
- */
-static noinline int __invoke_psci_fn_hvc(u64 function_id, u64 arg0, u64 arg1,
- u64 arg2)
-{
- asm volatile(
- __asmeq("%0", "x0")
- __asmeq("%1", "x1")
- __asmeq("%2", "x2")
- __asmeq("%3", "x3")
- "hvc #0\n"
- : "+r" (function_id)
- : "r" (arg0), "r" (arg1), "r" (arg2));
-
- return function_id;
-}
-
-static noinline int __invoke_psci_fn_smc(u64 function_id, u64 arg0, u64 arg1,
- u64 arg2)
-{
- asm volatile(
- __asmeq("%0", "x0")
- __asmeq("%1", "x1")
- __asmeq("%2", "x2")
- __asmeq("%3", "x3")
- "smc #0\n"
- : "+r" (function_id)
- : "r" (arg0), "r" (arg1), "r" (arg2));
-
- return function_id;
-}
-
static int psci_get_version(void)
{
int err;
case __SI_TIMER:
err |= __put_user(from->si_tid, &to->si_tid);
err |= __put_user(from->si_overrun, &to->si_overrun);
- err |= __put_user((compat_uptr_t)(unsigned long)from->si_ptr,
- &to->si_ptr);
+ err |= __put_user(from->si_int, &to->si_int);
break;
case __SI_POLL:
err |= __put_user(from->si_band, &to->si_band);
case __SI_MESGQ: /* But this is */
err |= __put_user(from->si_pid, &to->si_pid);
err |= __put_user(from->si_uid, &to->si_uid);
- err |= __put_user((compat_uptr_t)(unsigned long)from->si_ptr, &to->si_ptr);
+ err |= __put_user(from->si_int, &to->si_int);
break;
case __SI_SYS:
err |= __put_user((compat_uptr_t)(unsigned long)
/* int __kernel_clock_getres(clockid_t clock_id, struct timespec *res); */
ENTRY(__kernel_clock_getres)
.cfi_startproc
- cbz w1, 3f
-
cmp w0, #CLOCK_REALTIME
ccmp w0, #CLOCK_MONOTONIC, #0x4, ne
b.ne 1f
b.ne 4f
ldr x2, 6f
2:
+ cbz w1, 3f
stp xzr, x2, [x1]
3: /* res == NULL. */
}
early_param("coherent_pool", early_coherent_pool);
-static void *__alloc_from_pool(size_t size, struct page **ret_page)
+static void *__alloc_from_pool(size_t size, struct page **ret_page, gfp_t flags)
{
unsigned long val;
void *ptr = NULL;
*ret_page = phys_to_page(phys);
ptr = (void *)val;
+ if (flags & __GFP_ZERO)
+ memset(ptr, 0, size);
}
return ptr;
flags |= GFP_DMA;
if (IS_ENABLED(CONFIG_DMA_CMA) && (flags & __GFP_WAIT)) {
struct page *page;
+ void *addr;
size = PAGE_ALIGN(size);
page = dma_alloc_from_contiguous(dev, size >> PAGE_SHIFT,
return NULL;
*dma_handle = phys_to_dma(dev, page_to_phys(page));
- return page_address(page);
+ addr = page_address(page);
+ if (flags & __GFP_ZERO)
+ memset(addr, 0, size);
+ return addr;
} else {
return swiotlb_alloc_coherent(dev, size, dma_handle, flags);
}
if (!coherent && !(flags & __GFP_WAIT)) {
struct page *page = NULL;
- void *addr = __alloc_from_pool(size, &page);
+ void *addr = __alloc_from_pool(size, &page, flags);
if (addr)
*dma_handle = phys_to_dma(dev, page_to_phys(page));
.mapping_error = swiotlb_dma_mapping_error,
};
-extern int swiotlb_late_init_with_default_size(size_t default_size);
-
static int __init atomic_pool_init(void)
{
pgprot_t prot = __pgprot(PROT_NORMAL_NC);
return -ENOMEM;
}
-static int __init swiotlb_late_init(void)
+static int __init arm64_dma_init(void)
{
- size_t swiotlb_size = min(SZ_64M, MAX_ORDER_NR_PAGES << PAGE_SHIFT);
+ int ret;
dma_ops = &swiotlb_dma_ops;
- return swiotlb_late_init_with_default_size(swiotlb_size);
-}
-
-static int __init arm64_dma_init(void)
-{
- int ret = 0;
-
- ret |= swiotlb_late_init();
- ret |= atomic_pool_init();
+ ret = atomic_pool_init();
return ret;
}
#include <linux/dma-mapping.h>
#include <linux/dma-contiguous.h>
#include <linux/efi.h>
+#include <linux/swiotlb.h>
#include <asm/fixmap.h>
#include <asm/memory.h>
#include "mm.h"
phys_addr_t memstart_addr __read_mostly = 0;
+phys_addr_t arm64_dma_phys_limit __read_mostly;
#ifdef CONFIG_BLK_DEV_INITRD
static int __init early_initrd(char *p)
/* 4GB maximum for 32-bit only capable devices */
if (IS_ENABLED(CONFIG_ZONE_DMA)) {
- max_dma = PFN_DOWN(max_zone_dma_phys());
+ max_dma = PFN_DOWN(arm64_dma_phys_limit);
zone_size[ZONE_DMA] = max_dma - min;
}
zone_size[ZONE_NORMAL] = max - max_dma;
void __init arm64_memblock_init(void)
{
- phys_addr_t dma_phys_limit = 0;
-
memblock_enforce_memory_limit(memory_limit);
/*
/* 4GB maximum for 32-bit only capable devices */
if (IS_ENABLED(CONFIG_ZONE_DMA))
- dma_phys_limit = max_zone_dma_phys();
- dma_contiguous_reserve(dma_phys_limit);
+ arm64_dma_phys_limit = max_zone_dma_phys();
+ else
+ arm64_dma_phys_limit = PHYS_MASK + 1;
+ dma_contiguous_reserve(arm64_dma_phys_limit);
memblock_allow_resize();
memblock_dump_all();
*/
void __init mem_init(void)
{
+ swiotlb_init(1);
+
set_max_mapnr(pfn_to_page(max_pfn) - mem_map);
#ifndef CONFIG_SPARSEMEM_VMEMMAP
WARN_ON_ONCE(1);
}
- if (!is_module_address(start) || !is_module_address(end - 1))
+ if (start < MODULES_VADDR || start >= MODULES_END)
+ return -EINVAL;
+
+ if (end < MODULES_VADDR || end >= MODULES_END)
return -EINVAL;
data.set_mask = set_mask;
*/
#define pgtable_cache_init() do { } while (0)
+/*
+ * c6x is !MMU, so define the simpliest implementation
+ */
+#define pgprot_writecombine pgprot_noncached
+
#include <asm-generic/pgtable.h>
#endif /* _ASM_C6X_PGTABLE_H */
#define PGDIR_MASK (~(PGDIR_SIZE - 1))
#define PTRS_PER_PGD 64
+#define __PAGETABLE_PUD_FOLDED
#define PUD_SHIFT 26
#define PTRS_PER_PUD 1
#define PUD_SIZE (1UL << PUD_SHIFT)
#define PUD_MASK (~(PUD_SIZE - 1))
#define PUE_SIZE 256
+#define __PAGETABLE_PMD_FOLDED
#define PMD_SHIFT 26
#define PMD_SIZE (1UL << PMD_SHIFT)
#define PMD_MASK (~(PMD_SIZE - 1))
* the M32R is two-level, so we don't really have any
* PMD directory physically.
*/
+#define __PAGETABLE_PMD_FOLDED
#define PMD_SHIFT 22
#define PTRS_PER_PMD 1
*/
#ifdef CONFIG_SUN3
#define PTRS_PER_PTE 16
+#define __PAGETABLE_PMD_FOLDED
#define PTRS_PER_PMD 1
#define PTRS_PER_PGD 2048
#elif defined(CONFIG_COLDFIRE)
#define PTRS_PER_PTE 512
+#define __PAGETABLE_PMD_FOLDED
#define PTRS_PER_PMD 1
#define PTRS_PER_PGD 1024
#else
#define _ASM_METAG_IO_H
#include <linux/types.h>
+#include <asm/pgtable-bits.h>
#define IO_SPACE_LIMIT 0
--- /dev/null
+/*
+ * Meta page table definitions.
+ */
+
+#ifndef _METAG_PGTABLE_BITS_H
+#define _METAG_PGTABLE_BITS_H
+
+#include <asm/metag_mem.h>
+
+/*
+ * Definitions for MMU descriptors
+ *
+ * These are the hardware bits in the MMCU pte entries.
+ * Derived from the Meta toolkit headers.
+ */
+#define _PAGE_PRESENT MMCU_ENTRY_VAL_BIT
+#define _PAGE_WRITE MMCU_ENTRY_WR_BIT
+#define _PAGE_PRIV MMCU_ENTRY_PRIV_BIT
+/* Write combine bit - this can cause writes to occur out of order */
+#define _PAGE_WR_COMBINE MMCU_ENTRY_WRC_BIT
+/* Sys coherent bit - this bit is never used by Linux */
+#define _PAGE_SYS_COHERENT MMCU_ENTRY_SYS_BIT
+#define _PAGE_ALWAYS_ZERO_1 0x020
+#define _PAGE_CACHE_CTRL0 0x040
+#define _PAGE_CACHE_CTRL1 0x080
+#define _PAGE_ALWAYS_ZERO_2 0x100
+#define _PAGE_ALWAYS_ZERO_3 0x200
+#define _PAGE_ALWAYS_ZERO_4 0x400
+#define _PAGE_ALWAYS_ZERO_5 0x800
+
+/* These are software bits that we stuff into the gaps in the hardware
+ * pte entries that are not used. Note, these DO get stored in the actual
+ * hardware, but the hardware just does not use them.
+ */
+#define _PAGE_ACCESSED _PAGE_ALWAYS_ZERO_1
+#define _PAGE_DIRTY _PAGE_ALWAYS_ZERO_2
+
+/* Pages owned, and protected by, the kernel. */
+#define _PAGE_KERNEL _PAGE_PRIV
+
+/* No cacheing of this page */
+#define _PAGE_CACHE_WIN0 (MMCU_CWIN_UNCACHED << MMCU_ENTRY_CWIN_S)
+/* burst cacheing - good for data streaming */
+#define _PAGE_CACHE_WIN1 (MMCU_CWIN_BURST << MMCU_ENTRY_CWIN_S)
+/* One cache way per thread */
+#define _PAGE_CACHE_WIN2 (MMCU_CWIN_C1SET << MMCU_ENTRY_CWIN_S)
+/* Full on cacheing */
+#define _PAGE_CACHE_WIN3 (MMCU_CWIN_CACHED << MMCU_ENTRY_CWIN_S)
+
+#define _PAGE_CACHEABLE (_PAGE_CACHE_WIN3 | _PAGE_WR_COMBINE)
+
+/* which bits are used for cache control ... */
+#define _PAGE_CACHE_MASK (_PAGE_CACHE_CTRL0 | _PAGE_CACHE_CTRL1 | \
+ _PAGE_WR_COMBINE)
+
+/* This is a mask of the bits that pte_modify is allowed to change. */
+#define _PAGE_CHG_MASK (PAGE_MASK)
+
+#define _PAGE_SZ_SHIFT 1
+#define _PAGE_SZ_4K (0x0)
+#define _PAGE_SZ_8K (0x1 << _PAGE_SZ_SHIFT)
+#define _PAGE_SZ_16K (0x2 << _PAGE_SZ_SHIFT)
+#define _PAGE_SZ_32K (0x3 << _PAGE_SZ_SHIFT)
+#define _PAGE_SZ_64K (0x4 << _PAGE_SZ_SHIFT)
+#define _PAGE_SZ_128K (0x5 << _PAGE_SZ_SHIFT)
+#define _PAGE_SZ_256K (0x6 << _PAGE_SZ_SHIFT)
+#define _PAGE_SZ_512K (0x7 << _PAGE_SZ_SHIFT)
+#define _PAGE_SZ_1M (0x8 << _PAGE_SZ_SHIFT)
+#define _PAGE_SZ_2M (0x9 << _PAGE_SZ_SHIFT)
+#define _PAGE_SZ_4M (0xa << _PAGE_SZ_SHIFT)
+#define _PAGE_SZ_MASK (0xf << _PAGE_SZ_SHIFT)
+
+#if defined(CONFIG_PAGE_SIZE_4K)
+#define _PAGE_SZ (_PAGE_SZ_4K)
+#elif defined(CONFIG_PAGE_SIZE_8K)
+#define _PAGE_SZ (_PAGE_SZ_8K)
+#elif defined(CONFIG_PAGE_SIZE_16K)
+#define _PAGE_SZ (_PAGE_SZ_16K)
+#endif
+#define _PAGE_TABLE (_PAGE_SZ | _PAGE_PRESENT)
+
+#if defined(CONFIG_HUGETLB_PAGE_SIZE_8K)
+# define _PAGE_SZHUGE (_PAGE_SZ_8K)
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_16K)
+# define _PAGE_SZHUGE (_PAGE_SZ_16K)
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_32K)
+# define _PAGE_SZHUGE (_PAGE_SZ_32K)
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_64K)
+# define _PAGE_SZHUGE (_PAGE_SZ_64K)
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_128K)
+# define _PAGE_SZHUGE (_PAGE_SZ_128K)
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_256K)
+# define _PAGE_SZHUGE (_PAGE_SZ_256K)
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_512K)
+# define _PAGE_SZHUGE (_PAGE_SZ_512K)
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_1M)
+# define _PAGE_SZHUGE (_PAGE_SZ_1M)
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_2M)
+# define _PAGE_SZHUGE (_PAGE_SZ_2M)
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_4M)
+# define _PAGE_SZHUGE (_PAGE_SZ_4M)
+#endif
+
+#endif /* _METAG_PGTABLE_BITS_H */
#ifndef _METAG_PGTABLE_H
#define _METAG_PGTABLE_H
+#include <asm/pgtable-bits.h>
#include <asm-generic/pgtable-nopmd.h>
/* Invalid regions on Meta: 0x00000000-0x001FFFFF and 0xFFFF0000-0xFFFFFFFF */
#define VMALLOC_END 0x7FFFFFFF
#endif
-/*
- * Definitions for MMU descriptors
- *
- * These are the hardware bits in the MMCU pte entries.
- * Derived from the Meta toolkit headers.
- */
-#define _PAGE_PRESENT MMCU_ENTRY_VAL_BIT
-#define _PAGE_WRITE MMCU_ENTRY_WR_BIT
-#define _PAGE_PRIV MMCU_ENTRY_PRIV_BIT
-/* Write combine bit - this can cause writes to occur out of order */
-#define _PAGE_WR_COMBINE MMCU_ENTRY_WRC_BIT
-/* Sys coherent bit - this bit is never used by Linux */
-#define _PAGE_SYS_COHERENT MMCU_ENTRY_SYS_BIT
-#define _PAGE_ALWAYS_ZERO_1 0x020
-#define _PAGE_CACHE_CTRL0 0x040
-#define _PAGE_CACHE_CTRL1 0x080
-#define _PAGE_ALWAYS_ZERO_2 0x100
-#define _PAGE_ALWAYS_ZERO_3 0x200
-#define _PAGE_ALWAYS_ZERO_4 0x400
-#define _PAGE_ALWAYS_ZERO_5 0x800
-
-/* These are software bits that we stuff into the gaps in the hardware
- * pte entries that are not used. Note, these DO get stored in the actual
- * hardware, but the hardware just does not use them.
- */
-#define _PAGE_ACCESSED _PAGE_ALWAYS_ZERO_1
-#define _PAGE_DIRTY _PAGE_ALWAYS_ZERO_2
-
-/* Pages owned, and protected by, the kernel. */
-#define _PAGE_KERNEL _PAGE_PRIV
-
-/* No cacheing of this page */
-#define _PAGE_CACHE_WIN0 (MMCU_CWIN_UNCACHED << MMCU_ENTRY_CWIN_S)
-/* burst cacheing - good for data streaming */
-#define _PAGE_CACHE_WIN1 (MMCU_CWIN_BURST << MMCU_ENTRY_CWIN_S)
-/* One cache way per thread */
-#define _PAGE_CACHE_WIN2 (MMCU_CWIN_C1SET << MMCU_ENTRY_CWIN_S)
-/* Full on cacheing */
-#define _PAGE_CACHE_WIN3 (MMCU_CWIN_CACHED << MMCU_ENTRY_CWIN_S)
-
-#define _PAGE_CACHEABLE (_PAGE_CACHE_WIN3 | _PAGE_WR_COMBINE)
-
-/* which bits are used for cache control ... */
-#define _PAGE_CACHE_MASK (_PAGE_CACHE_CTRL0 | _PAGE_CACHE_CTRL1 | \
- _PAGE_WR_COMBINE)
-
-/* This is a mask of the bits that pte_modify is allowed to change. */
-#define _PAGE_CHG_MASK (PAGE_MASK)
-
-#define _PAGE_SZ_SHIFT 1
-#define _PAGE_SZ_4K (0x0)
-#define _PAGE_SZ_8K (0x1 << _PAGE_SZ_SHIFT)
-#define _PAGE_SZ_16K (0x2 << _PAGE_SZ_SHIFT)
-#define _PAGE_SZ_32K (0x3 << _PAGE_SZ_SHIFT)
-#define _PAGE_SZ_64K (0x4 << _PAGE_SZ_SHIFT)
-#define _PAGE_SZ_128K (0x5 << _PAGE_SZ_SHIFT)
-#define _PAGE_SZ_256K (0x6 << _PAGE_SZ_SHIFT)
-#define _PAGE_SZ_512K (0x7 << _PAGE_SZ_SHIFT)
-#define _PAGE_SZ_1M (0x8 << _PAGE_SZ_SHIFT)
-#define _PAGE_SZ_2M (0x9 << _PAGE_SZ_SHIFT)
-#define _PAGE_SZ_4M (0xa << _PAGE_SZ_SHIFT)
-#define _PAGE_SZ_MASK (0xf << _PAGE_SZ_SHIFT)
-
-#if defined(CONFIG_PAGE_SIZE_4K)
-#define _PAGE_SZ (_PAGE_SZ_4K)
-#elif defined(CONFIG_PAGE_SIZE_8K)
-#define _PAGE_SZ (_PAGE_SZ_8K)
-#elif defined(CONFIG_PAGE_SIZE_16K)
-#define _PAGE_SZ (_PAGE_SZ_16K)
-#endif
-#define _PAGE_TABLE (_PAGE_SZ | _PAGE_PRESENT)
-
-#if defined(CONFIG_HUGETLB_PAGE_SIZE_8K)
-# define _PAGE_SZHUGE (_PAGE_SZ_8K)
-#elif defined(CONFIG_HUGETLB_PAGE_SIZE_16K)
-# define _PAGE_SZHUGE (_PAGE_SZ_16K)
-#elif defined(CONFIG_HUGETLB_PAGE_SIZE_32K)
-# define _PAGE_SZHUGE (_PAGE_SZ_32K)
-#elif defined(CONFIG_HUGETLB_PAGE_SIZE_64K)
-# define _PAGE_SZHUGE (_PAGE_SZ_64K)
-#elif defined(CONFIG_HUGETLB_PAGE_SIZE_128K)
-# define _PAGE_SZHUGE (_PAGE_SZ_128K)
-#elif defined(CONFIG_HUGETLB_PAGE_SIZE_256K)
-# define _PAGE_SZHUGE (_PAGE_SZ_256K)
-#elif defined(CONFIG_HUGETLB_PAGE_SIZE_512K)
-# define _PAGE_SZHUGE (_PAGE_SZ_512K)
-#elif defined(CONFIG_HUGETLB_PAGE_SIZE_1M)
-# define _PAGE_SZHUGE (_PAGE_SZ_1M)
-#elif defined(CONFIG_HUGETLB_PAGE_SIZE_2M)
-# define _PAGE_SZHUGE (_PAGE_SZ_2M)
-#elif defined(CONFIG_HUGETLB_PAGE_SIZE_4M)
-# define _PAGE_SZHUGE (_PAGE_SZ_4M)
-#endif
-
/*
* The Linux memory management assumes a three-level page table setup. On
* Meta, we use that, but "fold" the mid level into the top-level page
unsigned long get_wchan(struct task_struct *p);
-#define KSTK_EIP(tsk) ((tsk)->thread.kernel_context->CurrPC)
-#define KSTK_ESP(tsk) ((tsk)->thread.kernel_context->AX[0].U0)
+#define KSTK_EIP(tsk) (task_pt_regs(tsk)->ctx.CurrPC)
+#define KSTK_ESP(tsk) (task_pt_regs(tsk)->ctx.AX[0].U0)
#define user_stack_pointer(regs) ((regs)->ctx.AX[0].U0)
* The LP register should point to the location where the called function
* should return. [note that MAKE_SYS_CALL uses label 1] */
/* See if the system call number is valid */
+ blti r12, 5f
addi r11, r12, -__NR_syscalls;
- bgei r11,5f;
+ bgei r11, 5f;
/* Figure out which function to use for this system call. */
/* Note Microblaze barrel shift is optional, so don't rely on it */
add r12, r12, r12; /* convert num -> ptr */
/* The syscall number is invalid, return an error. */
5:
- rtsd r15, 8; /* looks like a normal subroutine return */
+ braid ret_from_trap
addi r3, r0, -ENOSYS;
/* Entry point used to return from a syscall/trap */
bri 1b
/* Maybe handle a signal */
-5:
+5:
andi r11, r19, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME;
beqi r11, 4f; /* Signals to handle, handle them */
if (idx > current_cpu_data.tlbsize) {
kvm_err("%s: Invalid Index: %d\n", __func__, idx);
kvm_mips_dump_host_tlbs();
+ local_irq_restore(flags);
return -1;
}
TP_PROTO(struct kvm_vcpu *vcpu, unsigned int reason),
TP_ARGS(vcpu, reason),
TP_STRUCT__entry(
- __field(struct kvm_vcpu *, vcpu)
+ __field(unsigned long, pc)
__field(unsigned int, reason)
),
TP_fast_assign(
- __entry->vcpu = vcpu;
+ __entry->pc = vcpu->arch.pc;
__entry->reason = reason;
),
TP_printk("[%s]PC: 0x%08lx",
kvm_mips_exit_types_str[__entry->reason],
- __entry->vcpu->arch.pc)
+ __entry->pc)
);
#endif /* _TRACE_KVM_H */
#define PGDIR_SHIFT 22
#define PTRS_PER_PGD 1024
#define PTRS_PER_PUD 1 /* we don't really have any PUD physically */
+#define __PAGETABLE_PUD_FOLDED
#define PTRS_PER_PMD 1 /* we don't really have any PMD physically */
+#define __PAGETABLE_PMD_FOLDED
#define PTRS_PER_PTE 1024
#define PGD_SIZE PAGE_SIZE
#include <uapi/asm/ptrace.h>
+/* This struct defines the way the registers are stored on the
+ stack during a system call. */
+
#ifndef __ASSEMBLY__
+struct pt_regs {
+ unsigned long r8; /* r8-r15 Caller-saved GP registers */
+ unsigned long r9;
+ unsigned long r10;
+ unsigned long r11;
+ unsigned long r12;
+ unsigned long r13;
+ unsigned long r14;
+ unsigned long r15;
+ unsigned long r1; /* Assembler temporary */
+ unsigned long r2; /* Retval LS 32bits */
+ unsigned long r3; /* Retval MS 32bits */
+ unsigned long r4; /* r4-r7 Register arguments */
+ unsigned long r5;
+ unsigned long r6;
+ unsigned long r7;
+ unsigned long orig_r2; /* Copy of r2 ?? */
+ unsigned long ra; /* Return address */
+ unsigned long fp; /* Frame pointer */
+ unsigned long sp; /* Stack pointer */
+ unsigned long gp; /* Global pointer */
+ unsigned long estatus;
+ unsigned long ea; /* Exception return address (pc) */
+ unsigned long orig_r7;
+};
+
+/*
+ * This is the extended stack used by signal handlers and the context
+ * switcher: it's pushed after the normal "struct pt_regs".
+ */
+struct switch_stack {
+ unsigned long r16; /* r16-r23 Callee-saved GP registers */
+ unsigned long r17;
+ unsigned long r18;
+ unsigned long r19;
+ unsigned long r20;
+ unsigned long r21;
+ unsigned long r22;
+ unsigned long r23;
+ unsigned long fp;
+ unsigned long gp;
+ unsigned long ra;
+};
+
#define user_mode(regs) (((regs)->estatus & ESTATUS_EU))
#define instruction_pointer(regs) ((regs)->ra)
+++ /dev/null
-/*
- * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
- * Copyright (C) 2004 Microtronix Datacom Ltd
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-
-#ifndef _ASM_NIOS2_UCONTEXT_H
-#define _ASM_NIOS2_UCONTEXT_H
-
-typedef int greg_t;
-#define NGREG 32
-typedef greg_t gregset_t[NGREG];
-
-struct mcontext {
- int version;
- gregset_t gregs;
-};
-
-#define MCONTEXT_VERSION 2
-
-struct ucontext {
- unsigned long uc_flags;
- struct ucontext *uc_link;
- stack_t uc_stack;
- struct mcontext uc_mcontext;
- sigset_t uc_sigmask; /* mask last for extensibility */
-};
-
-#endif
include include/uapi/asm-generic/Kbuild.asm
header-y += elf.h
-header-y += ucontext.h
+
+generic-y += ucontext.h
typedef unsigned long elf_greg_t;
-#define ELF_NGREG \
- ((sizeof(struct pt_regs) + sizeof(struct switch_stack)) / \
- sizeof(elf_greg_t))
+#define ELF_NGREG 49
typedef elf_greg_t elf_gregset_t[ELF_NGREG];
typedef unsigned long elf_fpregset_t;
#define NUM_PTRACE_REG (PTR_TLBMISC + 1)
-/* this struct defines the way the registers are stored on the
- stack during a system call.
-
- There is a fake_regs in setup.c that has to match pt_regs.*/
-
-struct pt_regs {
- unsigned long r8; /* r8-r15 Caller-saved GP registers */
- unsigned long r9;
- unsigned long r10;
- unsigned long r11;
- unsigned long r12;
- unsigned long r13;
- unsigned long r14;
- unsigned long r15;
- unsigned long r1; /* Assembler temporary */
- unsigned long r2; /* Retval LS 32bits */
- unsigned long r3; /* Retval MS 32bits */
- unsigned long r4; /* r4-r7 Register arguments */
- unsigned long r5;
- unsigned long r6;
- unsigned long r7;
- unsigned long orig_r2; /* Copy of r2 ?? */
- unsigned long ra; /* Return address */
- unsigned long fp; /* Frame pointer */
- unsigned long sp; /* Stack pointer */
- unsigned long gp; /* Global pointer */
- unsigned long estatus;
- unsigned long ea; /* Exception return address (pc) */
- unsigned long orig_r7;
-};
-
-/*
- * This is the extended stack used by signal handlers and the context
- * switcher: it's pushed after the normal "struct pt_regs".
- */
-struct switch_stack {
- unsigned long r16; /* r16-r23 Callee-saved GP registers */
- unsigned long r17;
- unsigned long r18;
- unsigned long r19;
- unsigned long r20;
- unsigned long r21;
- unsigned long r22;
- unsigned long r23;
- unsigned long fp;
- unsigned long gp;
- unsigned long ra;
+/* User structures for general purpose registers. */
+struct user_pt_regs {
+ __u32 regs[49];
};
#endif /* __ASSEMBLY__ */
* details.
*/
-#ifndef _ASM_NIOS2_SIGCONTEXT_H
-#define _ASM_NIOS2_SIGCONTEXT_H
+#ifndef _UAPI__ASM_SIGCONTEXT_H
+#define _UAPI__ASM_SIGCONTEXT_H
-#include <asm/ptrace.h>
+#include <linux/types.h>
+
+#define MCONTEXT_VERSION 2
struct sigcontext {
- struct pt_regs regs;
- unsigned long sc_mask; /* old sigmask */
+ int version;
+ unsigned long gregs[32];
};
#endif
struct ucontext *uc, int *pr2)
{
int temp;
- greg_t *gregs = uc->uc_mcontext.gregs;
+ unsigned long *gregs = uc->uc_mcontext.gregs;
int err;
/* Always make any pending restarted system calls return -EINTR */
static inline int rt_setup_ucontext(struct ucontext *uc, struct pt_regs *regs)
{
struct switch_stack *sw = (struct switch_stack *)regs - 1;
- greg_t *gregs = uc->uc_mcontext.gregs;
+ unsigned long *gregs = uc->uc_mcontext.gregs;
int err = 0;
err |= __put_user(MCONTEXT_VERSION, &uc->uc_mcontext.version);
break;
}
-survive:
/*
* If for any reason at all we couldn't handle the fault,
* make sure we exit gracefully rather than endlessly redo
*/
out_of_memory:
up_read(&mm->mmap_sem);
- if (is_global_init(tsk)) {
- yield();
- down_read(&mm->mmap_sem);
- goto survive;
- }
if (!user_mode(regs))
goto no_context;
pagefault_out_of_memory();
if (likely(pgd != NULL)) {
memset(pgd, 0, PAGE_SIZE<<PGD_ALLOC_ORDER);
-#ifdef CONFIG_64BIT
+#if PT_NLEVELS == 3
actual_pgd += PTRS_PER_PGD;
/* Populate first pmd with allocated memory. We mark it
* with PxD_FLAG_ATTACHED as a signal to the system that this
static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd)
{
-#ifdef CONFIG_64BIT
+#if PT_NLEVELS == 3
pgd -= PTRS_PER_PGD;
#endif
free_pages((unsigned long)pgd, PGD_ALLOC_ORDER);
static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd)
{
-#ifdef CONFIG_64BIT
if(pmd_flag(*pmd) & PxD_FLAG_ATTACHED)
- /* This is the permanent pmd attached to the pgd;
- * cannot free it */
+ /*
+ * This is the permanent pmd attached to the pgd;
+ * cannot free it.
+ * Increment the counter to compensate for the decrement
+ * done by generic mm code.
+ */
+ mm_inc_nr_pmds(mm);
return;
-#endif
free_pages((unsigned long)pmd, PMD_ORDER);
}
static inline void
pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, pte_t *pte)
{
-#ifdef CONFIG_64BIT
+#if PT_NLEVELS == 3
/* preserve the gateway marker if this is the beginning of
* the permanent pmd */
if(pmd_flag(*pmd) & PxD_FLAG_ATTACHED)
#if PT_NLEVELS == 3
#define BITS_PER_PMD (PAGE_SHIFT + PMD_ORDER - BITS_PER_PMD_ENTRY)
#else
+#define __PAGETABLE_PMD_FOLDED
#define BITS_PER_PMD 0
#endif
#define PTRS_PER_PMD (1UL << BITS_PER_PMD)
#define ENTRY_COMP(_name_) .word sys_##_name_
#endif
- ENTRY_SAME(restart_syscall) /* 0 */
- ENTRY_SAME(exit)
+90: ENTRY_SAME(restart_syscall) /* 0 */
+91: ENTRY_SAME(exit)
ENTRY_SAME(fork_wrapper)
ENTRY_SAME(read)
ENTRY_SAME(write)
ENTRY_SAME(bpf)
ENTRY_COMP(execveat)
- /* Nothing yet */
+
+.ifne (. - 90b) - (__NR_Linux_syscalls * (91b - 90b))
+.error "size of syscall table does not fit value of __NR_Linux_syscalls"
+.endif
#undef ENTRY_SAME
#undef ENTRY_DIFF
static inline int cpu_nr_cores(void)
{
- return NR_CPUS >> threads_shift;
+ return nr_cpu_ids >> threads_shift;
}
static inline cpumask_t cpu_online_cores_map(void)
int pci_domain_number, unsigned long pe_num);
extern int iommu_add_device(struct device *dev);
extern void iommu_del_device(struct device *dev);
+extern int __init tce_iommu_bus_notifier_init(void);
#else
static inline void iommu_register_group(struct iommu_table *tbl,
int pci_domain_number,
static inline void iommu_del_device(struct device *dev)
{
}
+
+static inline int __init tce_iommu_bus_notifier_init(void)
+{
+ return 0;
+}
#endif /* !CONFIG_IOMMU_API */
static inline void set_iommu_table_base_and_group(struct device *dev,
--- /dev/null
+#ifndef _ASM_POWERPC_IRQ_WORK_H
+#define _ASM_POWERPC_IRQ_WORK_H
+
+static inline bool arch_irq_work_has_interrupt(void)
+{
+ return true;
+}
+
+#endif /* _ASM_POWERPC_IRQ_WORK_H */
#define PPC_INST_MFSPR_PVR_MASK 0xfc1fffff
#define PPC_INST_MFTMR 0x7c0002dc
#define PPC_INST_MSGSND 0x7c00019c
+#define PPC_INST_MSGCLR 0x7c0001dc
#define PPC_INST_MSGSNDP 0x7c00011c
#define PPC_INST_MTTMR 0x7c0003dc
#define PPC_INST_NOP 0x60000000
___PPC_RB(b) | __PPC_EH(eh))
#define PPC_MSGSND(b) stringify_in_c(.long PPC_INST_MSGSND | \
___PPC_RB(b))
+#define PPC_MSGCLR(b) stringify_in_c(.long PPC_INST_MSGCLR | \
+ ___PPC_RB(b))
#define PPC_MSGSNDP(b) stringify_in_c(.long PPC_INST_MSGSNDP | \
___PPC_RB(b))
#define PPC_POPCNTB(a, s) stringify_in_c(.long PPC_INST_POPCNTB | \
#define SRR1_ISI_N_OR_G 0x10000000 /* ISI: Access is no-exec or G */
#define SRR1_ISI_PROT 0x08000000 /* ISI: Other protection fault */
#define SRR1_WAKEMASK 0x00380000 /* reason for wakeup */
+#define SRR1_WAKEMASK_P8 0x003c0000 /* reason for wakeup on POWER8 */
#define SRR1_WAKESYSERR 0x00300000 /* System error */
#define SRR1_WAKEEE 0x00200000 /* External interrupt */
#define SRR1_WAKEMT 0x00280000 /* mtctrl */
#define SRR1_WAKEHMI 0x00280000 /* Hypervisor maintenance */
#define SRR1_WAKEDEC 0x00180000 /* Decrementer interrupt */
+#define SRR1_WAKEDBELL 0x00140000 /* Privileged doorbell on P8 */
#define SRR1_WAKETHERM 0x00100000 /* Thermal management interrupt */
#define SRR1_WAKERESET 0x00100000 /* System reset */
+#define SRR1_WAKEHDBELL 0x000c0000 /* Hypervisor doorbell on P8 */
#define SRR1_WAKESTATE 0x00030000 /* Powersave exit mask [46:47] */
#define SRR1_WS_DEEPEST 0x00030000 /* Some resources not maintained,
* may not be recoverable */
.machine_check_early = __machine_check_early_realmode_p8,
.platform = "power8",
},
+ { /* Power8NVL */
+ .pvr_mask = 0xffff0000,
+ .pvr_value = 0x004c0000,
+ .cpu_name = "POWER8NVL (raw)",
+ .cpu_features = CPU_FTRS_POWER8,
+ .cpu_user_features = COMMON_USER_POWER8,
+ .cpu_user_features2 = COMMON_USER2_POWER8,
+ .mmu_features = MMU_FTRS_POWER8,
+ .icache_bsize = 128,
+ .dcache_bsize = 128,
+ .num_pmcs = 6,
+ .pmc_type = PPC_PMC_IBM,
+ .oprofile_cpu_type = "ppc64/power8",
+ .oprofile_type = PPC_OPROFILE_INVALID,
+ .cpu_setup = __setup_cpu_power8,
+ .cpu_restore = __restore_cpu_power8,
+ .flush_tlb = __flush_tlb_power8,
+ .machine_check_early = __machine_check_early_realmode_p8,
+ .platform = "power8",
+ },
{ /* Power8 DD1: Does not support doorbell IPIs */
.pvr_mask = 0xffffff00,
.pvr_value = 0x004d0100,
#include <asm/dbell.h>
#include <asm/irq_regs.h>
+#include <asm/kvm_ppc.h>
#ifdef CONFIG_SMP
void doorbell_setup_this_cpu(void)
may_hard_irq_enable();
+ kvmppc_set_host_ipi(smp_processor_id(), 0);
__this_cpu_inc(irq_stat.doorbell_irqs);
smp_ipi_demux();
bne 9f /* continue in V mode if we are. */
5:
-#ifdef CONFIG_KVM_BOOK3S_64_HV
+#ifdef CONFIG_KVM_BOOK3S_64_HANDLER
/*
* We are coming from kernel context. Check if we are coming from
* guest. if yes, then we can continue. We will fall through
}
EXPORT_SYMBOL_GPL(iommu_del_device);
+static int tce_iommu_bus_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct device *dev = data;
+
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ return iommu_add_device(dev);
+ case BUS_NOTIFY_DEL_DEVICE:
+ if (dev->iommu_group)
+ iommu_del_device(dev);
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+static struct notifier_block tce_iommu_bus_nb = {
+ .notifier_call = tce_iommu_bus_notifier,
+};
+
+int __init tce_iommu_bus_notifier_init(void)
+{
+ bus_register_notifier(&pci_bus_type, &tce_iommu_bus_nb);
+ return 0;
+}
#endif /* CONFIG_IOMMU_API */
if (smp_ops->give_timebase)
smp_ops->give_timebase();
- /* Wait until cpu puts itself in the online map */
- while (!cpu_online(cpu))
+ /* Wait until cpu puts itself in the online & active maps */
+ while (!cpu_online(cpu) || !cpu_active(cpu))
cpu_relax();
return 0;
spin_lock(&vcpu->arch.vpa_update_lock);
lppaca = (struct lppaca *)vcpu->arch.vpa.pinned_addr;
if (lppaca)
- yield_count = lppaca->yield_count;
+ yield_count = be32_to_cpu(lppaca->yield_count);
spin_unlock(&vcpu->arch.vpa_update_lock);
return yield_count;
}
static void kvmppc_set_lpcr(struct kvm_vcpu *vcpu, u64 new_lpcr,
bool preserve_top32)
{
+ struct kvm *kvm = vcpu->kvm;
struct kvmppc_vcore *vc = vcpu->arch.vcore;
u64 mask;
+ mutex_lock(&kvm->lock);
spin_lock(&vc->lock);
/*
* If ILE (interrupt little-endian) has changed, update the
* MSR_LE bit in the intr_msr for each vcpu in this vcore.
*/
if ((new_lpcr & LPCR_ILE) != (vc->lpcr & LPCR_ILE)) {
- struct kvm *kvm = vcpu->kvm;
struct kvm_vcpu *vcpu;
int i;
- mutex_lock(&kvm->lock);
kvm_for_each_vcpu(i, vcpu, kvm) {
if (vcpu->arch.vcore != vc)
continue;
else
vcpu->arch.intr_msr &= ~MSR_LE;
}
- mutex_unlock(&kvm->lock);
}
/*
mask &= 0xFFFFFFFF;
vc->lpcr = (vc->lpcr & ~mask) | (new_lpcr & mask);
spin_unlock(&vc->lock);
+ mutex_unlock(&kvm->lock);
}
static int kvmppc_get_one_reg_hv(struct kvm_vcpu *vcpu, u64 id,
/* Save HEIR (HV emulation assist reg) in emul_inst
if this is an HEI (HV emulation interrupt, e40) */
li r3,KVM_INST_FETCH_FAILED
+ stw r3,VCPU_LAST_INST(r9)
cmpwi r12,BOOK3S_INTERRUPT_H_EMUL_ASSIST
bne 11f
mfspr r3,SPRN_HEIR
#endif
}
-static int tce_iommu_bus_notifier(struct notifier_block *nb,
- unsigned long action, void *data)
-{
- struct device *dev = data;
-
- switch (action) {
- case BUS_NOTIFY_ADD_DEVICE:
- return iommu_add_device(dev);
- case BUS_NOTIFY_DEL_DEVICE:
- if (dev->iommu_group)
- iommu_del_device(dev);
- return 0;
- default:
- return 0;
- }
-}
-
-static struct notifier_block tce_iommu_bus_nb = {
- .notifier_call = tce_iommu_bus_notifier,
-};
-
-static int __init tce_iommu_bus_notifier_init(void)
-{
- bus_register_notifier(&pci_bus_type, &tce_iommu_bus_nb);
- return 0;
-}
machine_subsys_initcall_sync(powernv, tce_iommu_bus_notifier_init);
#include <asm/runlatch.h>
#include <asm/code-patching.h>
#include <asm/dbell.h>
+#include <asm/kvm_ppc.h>
+#include <asm/ppc-opcode.h>
#include "powernv.h"
static void pnv_smp_cpu_kill_self(void)
{
unsigned int cpu;
- unsigned long srr1;
+ unsigned long srr1, wmask;
u32 idle_states;
/* Standard hot unplug procedure */
generic_set_cpu_dead(cpu);
smp_wmb();
+ wmask = SRR1_WAKEMASK;
+ if (cpu_has_feature(CPU_FTR_ARCH_207S))
+ wmask = SRR1_WAKEMASK_P8;
+
idle_states = pnv_get_supported_cpuidle_states();
/* We don't want to take decrementer interrupts while we are offline,
* so clear LPCR:PECE1. We keep PECE2 enabled.
* having finished executing in a KVM guest, then srr1
* contains 0.
*/
- if ((srr1 & SRR1_WAKEMASK) == SRR1_WAKEEE) {
+ if ((srr1 & wmask) == SRR1_WAKEEE) {
icp_native_flush_interrupt();
local_paca->irq_happened &= PACA_IRQ_HARD_DIS;
smp_mb();
+ } else if ((srr1 & wmask) == SRR1_WAKEHDBELL) {
+ unsigned long msg = PPC_DBELL_TYPE(PPC_DBELL_SERVER);
+ asm volatile(PPC_MSGCLR(%0) : : "r" (msg));
+ kvmppc_set_host_ipi(cpu, 0);
}
if (cpu_core_split_required())
}
__setup("multitce=", disable_multitce);
+
+machine_subsys_initcall_sync(pseries, tce_iommu_bus_notifier_init);
static struct kobject *mobility_kobj;
struct update_props_workarea {
- u32 phandle;
- u32 state;
- u64 reserved;
- u32 nprops;
+ __be32 phandle;
+ __be32 state;
+ __be64 reserved;
+ __be32 nprops;
} __packed;
#define NODE_ACTION_MASK 0xff000000
return rc;
}
-static int delete_dt_node(u32 phandle)
+static int delete_dt_node(__be32 phandle)
{
struct device_node *dn;
- dn = of_find_node_by_phandle(phandle);
+ dn = of_find_node_by_phandle(be32_to_cpu(phandle));
if (!dn)
return -ENOENT;
return 0;
}
-static int update_dt_node(u32 phandle, s32 scope)
+static int update_dt_node(__be32 phandle, s32 scope)
{
struct update_props_workarea *upwa;
struct device_node *dn;
char *prop_data;
char *rtas_buf;
int update_properties_token;
+ u32 nprops;
u32 vd;
update_properties_token = rtas_token("ibm,update-properties");
if (!rtas_buf)
return -ENOMEM;
- dn = of_find_node_by_phandle(phandle);
+ dn = of_find_node_by_phandle(be32_to_cpu(phandle));
if (!dn) {
kfree(rtas_buf);
return -ENOENT;
break;
prop_data = rtas_buf + sizeof(*upwa);
+ nprops = be32_to_cpu(upwa->nprops);
/* On the first call to ibm,update-properties for a node the
* the first property value descriptor contains an empty
*/
if (*prop_data == 0) {
prop_data++;
- vd = *(u32 *)prop_data;
+ vd = be32_to_cpu(*(__be32 *)prop_data);
prop_data += vd + sizeof(vd);
- upwa->nprops--;
+ nprops--;
}
- for (i = 0; i < upwa->nprops; i++) {
+ for (i = 0; i < nprops; i++) {
char *prop_name;
prop_name = prop_data;
prop_data += strlen(prop_name) + 1;
- vd = *(u32 *)prop_data;
+ vd = be32_to_cpu(*(__be32 *)prop_data);
prop_data += sizeof(vd);
switch (vd) {
return 0;
}
-static int add_dt_node(u32 parent_phandle, u32 drc_index)
+static int add_dt_node(__be32 parent_phandle, __be32 drc_index)
{
struct device_node *dn;
struct device_node *parent_dn;
int rc;
- parent_dn = of_find_node_by_phandle(parent_phandle);
+ parent_dn = of_find_node_by_phandle(be32_to_cpu(parent_phandle));
if (!parent_dn)
return -ENOENT;
int pseries_devicetree_update(s32 scope)
{
char *rtas_buf;
- u32 *data;
+ __be32 *data;
int update_nodes_token;
int rc;
if (rc && rc != 1)
break;
- data = (u32 *)rtas_buf + 4;
- while (*data & NODE_ACTION_MASK) {
+ data = (__be32 *)rtas_buf + 4;
+ while (be32_to_cpu(*data) & NODE_ACTION_MASK) {
int i;
- u32 action = *data & NODE_ACTION_MASK;
- int node_count = *data & NODE_COUNT_MASK;
+ u32 action = be32_to_cpu(*data) & NODE_ACTION_MASK;
+ u32 node_count = be32_to_cpu(*data) & NODE_COUNT_MASK;
data++;
for (i = 0; i < node_count; i++) {
- u32 phandle = *data++;
- u32 drc_index;
+ __be32 phandle = *data++;
+ __be32 drc_index;
switch (action) {
case DELETE_DT_NODE:
extern unsigned long mmap_rnd_mask;
-#define STACK_RND_MASK (mmap_rnd_mask)
+#define STACK_RND_MASK (test_thread_flag(TIF_31BIT) ? 0x7ff : mmap_rnd_mask)
#define ARCH_DLINFO \
do { \
#define S390_ARCH_FAC_MASK_SIZE_U64 \
(S390_ARCH_FAC_MASK_SIZE_BYTE / sizeof(u64))
-struct s390_model_fac {
- /* facilities used in SIE context */
- __u64 sie[S390_ARCH_FAC_LIST_SIZE_U64];
- /* subset enabled by kvm */
- __u64 kvm[S390_ARCH_FAC_LIST_SIZE_U64];
+struct kvm_s390_fac {
+ /* facility list requested by guest */
+ __u64 list[S390_ARCH_FAC_LIST_SIZE_U64];
+ /* facility mask supported by kvm & hosting machine */
+ __u64 mask[S390_ARCH_FAC_LIST_SIZE_U64];
};
struct kvm_s390_cpu_model {
- struct s390_model_fac *fac;
+ struct kvm_s390_fac *fac;
struct cpuid cpu_id;
unsigned short ibc;
};
{
int cpu = smp_processor_id();
+ S390_lowcore.user_asce = next->context.asce_bits | __pa(next->pgd);
if (prev == next)
return;
if (MACHINE_HAS_TLB_LC)
atomic_dec(&prev->context.attach_count);
if (MACHINE_HAS_TLB_LC)
cpumask_clear_cpu(cpu, &prev->context.cpu_attach_mask);
- S390_lowcore.user_asce = next->context.asce_bits | __pa(next->pgd);
}
#define finish_arch_post_lock_switch finish_arch_post_lock_switch
#endif
}
-static inline void clear_page(void *page)
-{
- register unsigned long reg1 asm ("1") = 0;
- register void *reg2 asm ("2") = page;
- register unsigned long reg3 asm ("3") = 4096;
- asm volatile(
- " mvcl 2,0"
- : "+d" (reg2), "+d" (reg3) : "d" (reg1)
- : "memory", "cc");
-}
+#define clear_page(page) memset((page), 0, PAGE_SIZE)
/*
* copy_page uses the mvcl instruction with 0xb0 padding byte in order to
*/
#define PTRS_PER_PTE 256
#ifndef CONFIG_64BIT
+#define __PAGETABLE_PUD_FOLDED
#define PTRS_PER_PMD 1
+#define __PAGETABLE_PMD_FOLDED
#define PTRS_PER_PUD 1
#else /* CONFIG_64BIT */
#define PTRS_PER_PMD 2048
unsigned long ftrace_plt;
+static inline void ftrace_generate_orig_insn(struct ftrace_insn *insn)
+{
+#ifdef CC_USING_HOTPATCH
+ /* brcl 0,0 */
+ insn->opc = 0xc004;
+ insn->disp = 0;
+#else
+ /* stg r14,8(r15) */
+ insn->opc = 0xe3e0;
+ insn->disp = 0xf0080024;
+#endif
+}
+
+static inline int is_kprobe_on_ftrace(struct ftrace_insn *insn)
+{
+#ifdef CONFIG_KPROBES
+ if (insn->opc == BREAKPOINT_INSTRUCTION)
+ return 1;
+#endif
+ return 0;
+}
+
+static inline void ftrace_generate_kprobe_nop_insn(struct ftrace_insn *insn)
+{
+#ifdef CONFIG_KPROBES
+ insn->opc = BREAKPOINT_INSTRUCTION;
+ insn->disp = KPROBE_ON_FTRACE_NOP;
+#endif
+}
+
+static inline void ftrace_generate_kprobe_call_insn(struct ftrace_insn *insn)
+{
+#ifdef CONFIG_KPROBES
+ insn->opc = BREAKPOINT_INSTRUCTION;
+ insn->disp = KPROBE_ON_FTRACE_CALL;
+#endif
+}
+
int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
unsigned long addr)
{
return -EFAULT;
if (addr == MCOUNT_ADDR) {
/* Initial code replacement */
-#ifdef CC_USING_HOTPATCH
- /* We expect to see brcl 0,0 */
- ftrace_generate_nop_insn(&orig);
-#else
- /* We expect to see stg r14,8(r15) */
- orig.opc = 0xe3e0;
- orig.disp = 0xf0080024;
-#endif
+ ftrace_generate_orig_insn(&orig);
ftrace_generate_nop_insn(&new);
- } else if (old.opc == BREAKPOINT_INSTRUCTION) {
+ } else if (is_kprobe_on_ftrace(&old)) {
/*
* If we find a breakpoint instruction, a kprobe has been
* placed at the beginning of the function. We write the
* bytes of the original instruction so that the kprobes
* handler can execute a nop, if it reaches this breakpoint.
*/
- new.opc = orig.opc = BREAKPOINT_INSTRUCTION;
- orig.disp = KPROBE_ON_FTRACE_CALL;
- new.disp = KPROBE_ON_FTRACE_NOP;
+ ftrace_generate_kprobe_call_insn(&orig);
+ ftrace_generate_kprobe_nop_insn(&new);
} else {
/* Replace ftrace call with a nop. */
ftrace_generate_call_insn(&orig, rec->ip);
if (probe_kernel_read(&old, (void *) rec->ip, sizeof(old)))
return -EFAULT;
- if (old.opc == BREAKPOINT_INSTRUCTION) {
+ if (is_kprobe_on_ftrace(&old)) {
/*
* If we find a breakpoint instruction, a kprobe has been
* placed at the beginning of the function. We write the
* bytes of the original instruction so that the kprobes
* handler can execute a brasl if it reaches this breakpoint.
*/
- new.opc = orig.opc = BREAKPOINT_INSTRUCTION;
- orig.disp = KPROBE_ON_FTRACE_NOP;
- new.disp = KPROBE_ON_FTRACE_CALL;
+ ftrace_generate_kprobe_nop_insn(&orig);
+ ftrace_generate_kprobe_call_insn(&new);
} else {
/* Replace nop with an ftrace call. */
ftrace_generate_nop_insn(&orig);
insn->offset = (entry->target - entry->code) >> 1;
}
-static void jump_label_bug(struct jump_entry *entry, struct insn *insn)
+static void jump_label_bug(struct jump_entry *entry, struct insn *expected,
+ struct insn *new)
{
unsigned char *ipc = (unsigned char *)entry->code;
- unsigned char *ipe = (unsigned char *)insn;
+ unsigned char *ipe = (unsigned char *)expected;
+ unsigned char *ipn = (unsigned char *)new;
pr_emerg("Jump label code mismatch at %pS [%p]\n", ipc, ipc);
pr_emerg("Found: %02x %02x %02x %02x %02x %02x\n",
ipc[0], ipc[1], ipc[2], ipc[3], ipc[4], ipc[5]);
pr_emerg("Expected: %02x %02x %02x %02x %02x %02x\n",
ipe[0], ipe[1], ipe[2], ipe[3], ipe[4], ipe[5]);
+ pr_emerg("New: %02x %02x %02x %02x %02x %02x\n",
+ ipn[0], ipn[1], ipn[2], ipn[3], ipn[4], ipn[5]);
panic("Corrupted kernel text");
}
}
if (init) {
if (memcmp((void *)entry->code, &orignop, sizeof(orignop)))
- jump_label_bug(entry, &old);
+ jump_label_bug(entry, &orignop, &new);
} else {
if (memcmp((void *)entry->code, &old, sizeof(old)))
- jump_label_bug(entry, &old);
+ jump_label_bug(entry, &old, &new);
}
probe_kernel_write((void *)entry->code, &new, sizeof(new));
}
const Elf_Shdr *sechdrs,
struct module *me)
{
+ jump_label_apply_nops(me);
vfree(me->arch.syminfo);
me->arch.syminfo = NULL;
return 0;
static struct attribute *cpumsf_pmu_events_attr[] = {
CPUMF_EVENT_PTR(SF, SF_CYCLES_BASIC),
- CPUMF_EVENT_PTR(SF, SF_CYCLES_BASIC_DIAG),
+ NULL,
NULL,
};
return -EINVAL;
}
- if (si.ad)
+ if (si.ad) {
sfb_set_limits(CPUM_SF_MIN_SDB, CPUM_SF_MAX_SDB);
+ cpumsf_pmu_events_attr[1] =
+ CPUMF_EVENT_PTR(SF, SF_CYCLES_BASIC_DIAG);
+ }
sfdbg = debug_register(KMSG_COMPONENT, 2, 1, 80);
if (!sfdbg)
static DEFINE_PER_CPU(struct cpuid, cpu_id);
-void cpu_relax(void)
+void notrace cpu_relax(void)
{
if (!smp_cpu_mtid && MACHINE_HAS_DIAG44)
asm volatile("diag 0,0,0x44");
lhi %r1,1
sigp %r1,%r0,SIGP_SET_ARCHITECTURE
sam64
+#ifdef CONFIG_SMP
+ larl %r1,smp_cpu_mt_shift
+ icm %r1,15,0(%r1)
+ jz smt_done
+ llgfr %r1,%r1
+smt_loop:
+ sigp %r1,%r0,SIGP_SET_MULTI_THREADING
+ brc 8,smt_done /* accepted */
+ brc 2,smt_loop /* busy, try again */
+smt_done:
+#endif
larl %r1,.Lnew_pgm_check_psw
lpswe 0(%r1)
pgm_check_entry:
case KVM_CAP_ONE_REG:
case KVM_CAP_ENABLE_CAP:
case KVM_CAP_S390_CSS_SUPPORT:
- case KVM_CAP_IRQFD:
case KVM_CAP_IOEVENTFD:
case KVM_CAP_DEVICE_CTRL:
case KVM_CAP_ENABLE_CAP_VM:
memcpy(&kvm->arch.model.cpu_id, &proc->cpuid,
sizeof(struct cpuid));
kvm->arch.model.ibc = proc->ibc;
- memcpy(kvm->arch.model.fac->kvm, proc->fac_list,
+ memcpy(kvm->arch.model.fac->list, proc->fac_list,
S390_ARCH_FAC_LIST_SIZE_BYTE);
} else
ret = -EFAULT;
}
memcpy(&proc->cpuid, &kvm->arch.model.cpu_id, sizeof(struct cpuid));
proc->ibc = kvm->arch.model.ibc;
- memcpy(&proc->fac_list, kvm->arch.model.fac->kvm, S390_ARCH_FAC_LIST_SIZE_BYTE);
+ memcpy(&proc->fac_list, kvm->arch.model.fac->list, S390_ARCH_FAC_LIST_SIZE_BYTE);
if (copy_to_user((void __user *)attr->addr, proc, sizeof(*proc)))
ret = -EFAULT;
kfree(proc);
}
get_cpu_id((struct cpuid *) &mach->cpuid);
mach->ibc = sclp_get_ibc();
- memcpy(&mach->fac_mask, kvm_s390_fac_list_mask,
- kvm_s390_fac_list_mask_size() * sizeof(u64));
+ memcpy(&mach->fac_mask, kvm->arch.model.fac->mask,
+ S390_ARCH_FAC_LIST_SIZE_BYTE);
memcpy((unsigned long *)&mach->fac_list, S390_lowcore.stfle_fac_list,
- S390_ARCH_FAC_LIST_SIZE_U64);
+ S390_ARCH_FAC_LIST_SIZE_BYTE);
if (copy_to_user((void __user *)attr->addr, mach, sizeof(*mach)))
ret = -EFAULT;
kfree(mach);
static int kvm_s390_query_ap_config(u8 *config)
{
u32 fcn_code = 0x04000000UL;
- u32 cc;
+ u32 cc = 0;
+ memset(config, 0, 128);
asm volatile(
"lgr 0,%1\n"
"lgr 2,%2\n"
".long 0xb2af0000\n" /* PQAP(QCI) */
- "ipm %0\n"
+ "0: ipm %0\n"
"srl %0,28\n"
- : "=r" (cc)
+ "1:\n"
+ EX_TABLE(0b, 1b)
+ : "+r" (cc)
: "r" (fcn_code), "r" (config)
: "cc", "0", "2", "memory"
);
kvm_s390_set_crycb_format(kvm);
- /* Disable AES/DEA protected key functions by default */
- kvm->arch.crypto.aes_kw = 0;
- kvm->arch.crypto.dea_kw = 0;
+ /* Enable AES/DEA protected key functions by default */
+ kvm->arch.crypto.aes_kw = 1;
+ kvm->arch.crypto.dea_kw = 1;
+ get_random_bytes(kvm->arch.crypto.crycb->aes_wrapping_key_mask,
+ sizeof(kvm->arch.crypto.crycb->aes_wrapping_key_mask));
+ get_random_bytes(kvm->arch.crypto.crycb->dea_wrapping_key_mask,
+ sizeof(kvm->arch.crypto.crycb->dea_wrapping_key_mask));
return 0;
}
/*
* The architectural maximum amount of facilities is 16 kbit. To store
* this amount, 2 kbyte of memory is required. Thus we need a full
- * page to hold the active copy (arch.model.fac->sie) and the current
- * facilities set (arch.model.fac->kvm). Its address size has to be
+ * page to hold the guest facility list (arch.model.fac->list) and the
+ * facility mask (arch.model.fac->mask). Its address size has to be
* 31 bits and word aligned.
*/
kvm->arch.model.fac =
- (struct s390_model_fac *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ (struct kvm_s390_fac *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!kvm->arch.model.fac)
goto out_nofac;
- memcpy(kvm->arch.model.fac->kvm, S390_lowcore.stfle_fac_list,
- S390_ARCH_FAC_LIST_SIZE_U64);
-
- /*
- * If this KVM host runs *not* in a LPAR, relax the facility bits
- * of the kvm facility mask by all missing facilities. This will allow
- * to determine the right CPU model by means of the remaining facilities.
- * Live guest migration must prohibit the migration of KVMs running in
- * a LPAR to non LPAR hosts.
- */
- if (!MACHINE_IS_LPAR)
- for (i = 0; i < kvm_s390_fac_list_mask_size(); i++)
- kvm_s390_fac_list_mask[i] &= kvm->arch.model.fac->kvm[i];
-
- /*
- * Apply the kvm facility mask to limit the kvm supported/tolerated
- * facility list.
- */
+ /* Populate the facility mask initially. */
+ memcpy(kvm->arch.model.fac->mask, S390_lowcore.stfle_fac_list,
+ S390_ARCH_FAC_LIST_SIZE_BYTE);
for (i = 0; i < S390_ARCH_FAC_LIST_SIZE_U64; i++) {
if (i < kvm_s390_fac_list_mask_size())
- kvm->arch.model.fac->kvm[i] &= kvm_s390_fac_list_mask[i];
+ kvm->arch.model.fac->mask[i] &= kvm_s390_fac_list_mask[i];
else
- kvm->arch.model.fac->kvm[i] = 0UL;
+ kvm->arch.model.fac->mask[i] = 0UL;
}
+ /* Populate the facility list initially. */
+ memcpy(kvm->arch.model.fac->list, kvm->arch.model.fac->mask,
+ S390_ARCH_FAC_LIST_SIZE_BYTE);
+
kvm_s390_get_cpu_id(&kvm->arch.model.cpu_id);
kvm->arch.model.ibc = sclp_get_ibc() & 0x0fff;
mutex_lock(&vcpu->kvm->lock);
vcpu->arch.cpu_id = vcpu->kvm->arch.model.cpu_id;
- memcpy(vcpu->kvm->arch.model.fac->sie, vcpu->kvm->arch.model.fac->kvm,
- S390_ARCH_FAC_LIST_SIZE_BYTE);
vcpu->arch.sie_block->ibc = vcpu->kvm->arch.model.ibc;
mutex_unlock(&vcpu->kvm->lock);
vcpu->arch.sie_block->scaol = (__u32)(__u64)kvm->arch.sca;
set_bit(63 - id, (unsigned long *) &kvm->arch.sca->mcn);
}
- vcpu->arch.sie_block->fac = (int) (long) kvm->arch.model.fac->sie;
+ vcpu->arch.sie_block->fac = (int) (long) kvm->arch.model.fac->list;
spin_lock_init(&vcpu->arch.local_int.lock);
vcpu->arch.local_int.float_int = &kvm->arch.float_int;
/* test availability of facility in a kvm intance */
static inline int test_kvm_facility(struct kvm *kvm, unsigned long nr)
{
- return __test_facility(nr, kvm->arch.model.fac->kvm);
+ return __test_facility(nr, kvm->arch.model.fac->mask) &&
+ __test_facility(nr, kvm->arch.model.fac->list);
}
/* are cpu states controlled by user space */
* We need to shift the lower 32 facility bits (bit 0-31) from a u64
* into a u32 memory representation. They will remain bits 0-31.
*/
- fac = *vcpu->kvm->arch.model.fac->sie >> 32;
+ fac = *vcpu->kvm->arch.model.fac->list >> 32;
rc = write_guest_lc(vcpu, offsetof(struct _lowcore, stfl_fac_list),
&fac, sizeof(fac));
if (rc)
addr = ZPCI_IOMAP_ADDR_BASE | ((u64) idx << 48);
return (void __iomem *) addr + offset;
}
-EXPORT_SYMBOL_GPL(pci_iomap_range);
+EXPORT_SYMBOL(pci_iomap_range);
void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen)
{
}
spin_unlock(&zpci_iomap_lock);
}
-EXPORT_SYMBOL_GPL(pci_iounmap);
+EXPORT_SYMBOL(pci_iounmap);
static int pci_read(struct pci_bus *bus, unsigned int devfn, int where,
int size, u32 *val)
airq_iv_free_bit(zpci_aisb_iv, zdev->aisb);
}
-static void zpci_map_resources(struct zpci_dev *zdev)
+static void zpci_map_resources(struct pci_dev *pdev)
{
- struct pci_dev *pdev = zdev->pdev;
resource_size_t len;
int i;
}
}
-static void zpci_unmap_resources(struct zpci_dev *zdev)
+static void zpci_unmap_resources(struct pci_dev *pdev)
{
- struct pci_dev *pdev = zdev->pdev;
resource_size_t len;
int i;
zdev->pdev = pdev;
pdev->dev.groups = zpci_attr_groups;
- zpci_map_resources(zdev);
+ zpci_map_resources(pdev);
for (i = 0; i < PCI_BAR_COUNT; i++) {
res = &pdev->resource[i];
return 0;
}
+void pcibios_release_device(struct pci_dev *pdev)
+{
+ zpci_unmap_resources(pdev);
+}
+
int pcibios_enable_device(struct pci_dev *pdev, int mask)
{
struct zpci_dev *zdev = get_zdev(pdev);
zdev->pdev = pdev;
zpci_debug_init_device(zdev);
zpci_fmb_enable_device(zdev);
- zpci_map_resources(zdev);
return pci_enable_resources(pdev, mask);
}
{
struct zpci_dev *zdev = get_zdev(pdev);
- zpci_unmap_resources(zdev);
zpci_fmb_disable_device(zdev);
zpci_debug_exit_device(zdev);
zdev->pdev = NULL;
#ifdef CONFIG_HIBERNATE_CALLBACKS
static int zpci_restore(struct device *dev)
{
- struct zpci_dev *zdev = get_zdev(to_pci_dev(dev));
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct zpci_dev *zdev = get_zdev(pdev);
int ret = 0;
if (zdev->state != ZPCI_FN_STATE_ONLINE)
if (ret)
goto out;
- zpci_map_resources(zdev);
+ zpci_map_resources(pdev);
zpci_register_ioat(zdev, 0, zdev->start_dma + PAGE_OFFSET,
zdev->start_dma + zdev->iommu_size - 1,
(u64) zdev->dma_table);
static int zpci_freeze(struct device *dev)
{
- struct zpci_dev *zdev = get_zdev(to_pci_dev(dev));
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct zpci_dev *zdev = get_zdev(pdev);
if (zdev->state != ZPCI_FN_STATE_ONLINE)
return 0;
zpci_unregister_ioat(zdev, 0);
+ zpci_unmap_resources(pdev);
return clp_disable_fh(zdev);
}
if (copy_from_user(buf, user_buffer, length))
goto out;
- memcpy_toio(io_addr, buf, length);
- ret = 0;
+ ret = zpci_memcpy_toio(io_addr, buf, length);
out:
if (buf != local_buf)
kfree(buf);
goto out;
io_addr = (void __iomem *)((pfn << PAGE_SHIFT) | (mmio_addr & ~PAGE_MASK));
- ret = -EFAULT;
- if ((unsigned long) io_addr < ZPCI_IOMAP_ADDR_BASE)
+ if ((unsigned long) io_addr < ZPCI_IOMAP_ADDR_BASE) {
+ ret = -EFAULT;
goto out;
-
- memcpy_fromio(buf, io_addr, length);
-
- if (copy_to_user(user_buffer, buf, length))
+ }
+ ret = zpci_memcpy_fromio(buf, io_addr, length);
+ if (ret)
goto out;
+ if (copy_to_user(user_buffer, buf, length))
+ ret = -EFAULT;
- ret = 0;
out:
if (buf != local_buf)
kfree(buf);
default "arch/sparc/configs/sparc32_defconfig" if SPARC32
default "arch/sparc/configs/sparc64_defconfig" if SPARC64
+config ARCH_PROC_KCORE_TEXT
+ def_bool y
+
config IOMMU_HELPER
bool
default y if SPARC64
unsigned long reg_val);
#endif
+
+#define HV_FAST_M7_GET_PERFREG 0x43
+#define HV_FAST_M7_SET_PERFREG 0x44
+
+#ifndef __ASSEMBLY__
+unsigned long sun4v_m7_get_perfreg(unsigned long reg_num,
+ unsigned long *reg_val);
+unsigned long sun4v_m7_set_perfreg(unsigned long reg_num,
+ unsigned long reg_val);
+#endif
+
/* Function numbers for HV_CORE_TRAP. */
#define HV_CORE_SET_VER 0x00
#define HV_CORE_PUTCHAR 0x01
#define HV_GRP_SDIO 0x0108
#define HV_GRP_SDIO_ERR 0x0109
#define HV_GRP_REBOOT_DATA 0x0110
+#define HV_GRP_M7_PERF 0x0114
#define HV_GRP_NIAG_PERF 0x0200
#define HV_GRP_FIRE_PERF 0x0201
#define HV_GRP_N2_CPU 0x0202
{
}
-#define ioread8(X) readb(X)
-#define ioread16(X) readw(X)
-#define ioread16be(X) __raw_readw(X)
-#define ioread32(X) readl(X)
-#define ioread32be(X) __raw_readl(X)
-#define iowrite8(val,X) writeb(val,X)
-#define iowrite16(val,X) writew(val,X)
-#define iowrite16be(val,X) __raw_writew(val,X)
-#define iowrite32(val,X) writel(val,X)
-#define iowrite32be(val,X) __raw_writel(val,X)
+#define ioread8 readb
+#define ioread16 readw
+#define ioread16be __raw_readw
+#define ioread32 readl
+#define ioread32be __raw_readl
+#define iowrite8 writeb
+#define iowrite16 writew
+#define iowrite16be __raw_writew
+#define iowrite32 writel
+#define iowrite32be __raw_writel
/* Create a virtual mapping cookie for an IO port range */
void __iomem *ioport_map(unsigned long port, unsigned int nr);
extern int this_is_starfire;
void check_if_starfire(void);
-int starfire_hard_smp_processor_id(void);
void starfire_hookup(int);
unsigned int starfire_translate(unsigned long imap, unsigned int upaid);
void do_privop(struct pt_regs *regs);
void do_privact(struct pt_regs *regs);
void do_cee(struct pt_regs *regs);
-void do_cee_tl1(struct pt_regs *regs);
-void do_dae_tl1(struct pt_regs *regs);
-void do_iae_tl1(struct pt_regs *regs);
void do_div0_tl1(struct pt_regs *regs);
-void do_fpdis_tl1(struct pt_regs *regs);
void do_fpieee_tl1(struct pt_regs *regs);
void do_fpother_tl1(struct pt_regs *regs);
void do_ill_tl1(struct pt_regs *regs);
{ .group = HV_GRP_VT_CPU, },
{ .group = HV_GRP_T5_CPU, },
{ .group = HV_GRP_DIAG, .flags = FLAG_PRE_API },
+ { .group = HV_GRP_M7_PERF, },
};
static DEFINE_SPINLOCK(hvapi_lock);
retl
nop
ENDPROC(sun4v_t5_set_perfreg)
+
+ENTRY(sun4v_m7_get_perfreg)
+ mov %o1, %o4
+ mov HV_FAST_M7_GET_PERFREG, %o5
+ ta HV_FAST_TRAP
+ stx %o1, [%o4]
+ retl
+ nop
+ENDPROC(sun4v_m7_get_perfreg)
+
+ENTRY(sun4v_m7_set_perfreg)
+ mov HV_FAST_M7_SET_PERFREG, %o5
+ ta HV_FAST_TRAP
+ retl
+ nop
+ENDPROC(sun4v_m7_set_perfreg)
.pcr_nmi_disable = PCR_N4_PICNPT,
};
+static u64 m7_pcr_read(unsigned long reg_num)
+{
+ unsigned long val;
+
+ (void) sun4v_m7_get_perfreg(reg_num, &val);
+
+ return val;
+}
+
+static void m7_pcr_write(unsigned long reg_num, u64 val)
+{
+ (void) sun4v_m7_set_perfreg(reg_num, val);
+}
+
+static const struct pcr_ops m7_pcr_ops = {
+ .read_pcr = m7_pcr_read,
+ .write_pcr = m7_pcr_write,
+ .read_pic = n4_pic_read,
+ .write_pic = n4_pic_write,
+ .nmi_picl_value = n4_picl_value,
+ .pcr_nmi_enable = (PCR_N4_PICNPT | PCR_N4_STRACE |
+ PCR_N4_UTRACE | PCR_N4_TOE |
+ (26 << PCR_N4_SL_SHIFT)),
+ .pcr_nmi_disable = PCR_N4_PICNPT,
+};
static unsigned long perf_hsvc_group;
static unsigned long perf_hsvc_major;
perf_hsvc_group = HV_GRP_T5_CPU;
break;
+ case SUN4V_CHIP_SPARC_M7:
+ perf_hsvc_group = HV_GRP_M7_PERF;
+ break;
+
default:
return -ENODEV;
}
pcr_ops = &n5_pcr_ops;
break;
+ case SUN4V_CHIP_SPARC_M7:
+ pcr_ops = &m7_pcr_ops;
+ break;
+
default:
ret = -ENODEV;
break;
.num_pic_regs = 4,
};
+static void sparc_m7_write_pmc(int idx, u64 val)
+{
+ u64 pcr;
+
+ pcr = pcr_ops->read_pcr(idx);
+ /* ensure ov and ntc are reset */
+ pcr &= ~(PCR_N4_OV | PCR_N4_NTC);
+
+ pcr_ops->write_pic(idx, val & 0xffffffff);
+
+ pcr_ops->write_pcr(idx, pcr);
+}
+
+static const struct sparc_pmu sparc_m7_pmu = {
+ .event_map = niagara4_event_map,
+ .cache_map = &niagara4_cache_map,
+ .max_events = ARRAY_SIZE(niagara4_perfmon_event_map),
+ .read_pmc = sparc_vt_read_pmc,
+ .write_pmc = sparc_m7_write_pmc,
+ .upper_shift = 5,
+ .lower_shift = 5,
+ .event_mask = 0x7ff,
+ .user_bit = PCR_N4_UTRACE,
+ .priv_bit = PCR_N4_STRACE,
+
+ /* We explicitly don't support hypervisor tracing. */
+ .hv_bit = 0,
+
+ .irq_bit = PCR_N4_TOE,
+ .upper_nop = 0,
+ .lower_nop = 0,
+ .flags = 0,
+ .max_hw_events = 4,
+ .num_pcrs = 4,
+ .num_pic_regs = 4,
+};
static const struct sparc_pmu *sparc_pmu __read_mostly;
static u64 event_encoding(u64 event_id, int idx)
cpuc->pcr[0] |= cpuc->event[0]->hw.config_base;
}
+static void sparc_pmu_start(struct perf_event *event, int flags);
+
/* On this PMU each PIC has it's own PCR control register. */
static void calculate_multiple_pcrs(struct cpu_hw_events *cpuc)
{
struct perf_event *cp = cpuc->event[i];
struct hw_perf_event *hwc = &cp->hw;
int idx = hwc->idx;
- u64 enc;
if (cpuc->current_idx[i] != PIC_NO_INDEX)
continue;
- sparc_perf_event_set_period(cp, hwc, idx);
cpuc->current_idx[i] = idx;
- enc = perf_event_get_enc(cpuc->events[i]);
- cpuc->pcr[idx] &= ~mask_for_index(idx);
- if (hwc->state & PERF_HES_STOPPED)
- cpuc->pcr[idx] |= nop_for_index(idx);
- else
- cpuc->pcr[idx] |= event_encoding(enc, idx);
+ sparc_pmu_start(cp, PERF_EF_RELOAD);
}
out:
for (i = 0; i < cpuc->n_events; i++) {
int i;
local_irq_save(flags);
- perf_pmu_disable(event->pmu);
for (i = 0; i < cpuc->n_events; i++) {
if (event == cpuc->event[i]) {
}
}
- perf_pmu_enable(event->pmu);
local_irq_restore(flags);
}
unsigned long flags;
local_irq_save(flags);
- perf_pmu_disable(event->pmu);
n0 = cpuc->n_events;
if (n0 >= sparc_pmu->max_hw_events)
ret = 0;
out:
- perf_pmu_enable(event->pmu);
local_irq_restore(flags);
return ret;
}
sparc_pmu = &niagara4_pmu;
return true;
}
+ if (!strcmp(sparc_pmu_type, "sparc-m7")) {
+ sparc_pmu = &sparc_m7_pmu;
+ return true;
+ }
return false;
}
printk(" TPC[%lx] O7[%lx] I7[%lx] RPC[%lx]\n",
gp->tpc, gp->o7, gp->i7, gp->rpc);
}
+
+ touch_nmi_watchdog();
}
memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot));
(cpu == this_cpu ? '*' : ' '), cpu,
pp->pcr[0], pp->pcr[1], pp->pcr[2], pp->pcr[3],
pp->pic[0], pp->pic[1], pp->pic[2], pp->pic[3]);
+
+ touch_nmi_watchdog();
}
memset(global_cpu_snapshot, 0, sizeof(global_cpu_snapshot));
scheduler_ipi();
}
-/* This is a nop because we capture all other cpus
- * anyways when making the PROM active.
- */
+static void stop_this_cpu(void *dummy)
+{
+ prom_stopself();
+}
+
void smp_send_stop(void)
{
+ int cpu;
+
+ if (tlb_type == hypervisor) {
+ for_each_online_cpu(cpu) {
+ if (cpu == smp_processor_id())
+ continue;
+#ifdef CONFIG_SUN_LDOMS
+ if (ldom_domaining_enabled) {
+ unsigned long hv_err;
+ hv_err = sun4v_cpu_stop(cpu);
+ if (hv_err)
+ printk(KERN_ERR "sun4v_cpu_stop() "
+ "failed err=%lu\n", hv_err);
+ } else
+#endif
+ prom_stopcpu_cpuid(cpu);
+ }
+ } else
+ smp_call_function(stop_this_cpu, NULL, 0);
}
/**
this_is_starfire = 1;
}
-int starfire_hard_smp_processor_id(void)
-{
- return upa_readl(0x1fff40000d0UL);
-}
-
/*
* Each Starfire board has 32 registers which perform translation
* and delivery of traditional interrupt packets into the extended
long err;
/* No need for backward compatibility. We can start fresh... */
- if (call <= SEMCTL) {
+ if (call <= SEMTIMEDOP) {
switch (call) {
case SEMOP:
err = sys_semtimedop(first, ptr,
}
user_instruction_dump ((unsigned int __user *) regs->tpc);
}
+ if (panic_on_oops)
+ panic("Fatal exception");
if (regs->tstate & TSTATE_PRIV)
do_exit(SIGKILL);
do_exit(SIGSEGV);
die_if_kernel("TL0: Cache Error Exception", regs);
}
-void do_cee_tl1(struct pt_regs *regs)
-{
- exception_enter();
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
- die_if_kernel("TL1: Cache Error Exception", regs);
-}
-
-void do_dae_tl1(struct pt_regs *regs)
-{
- exception_enter();
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
- die_if_kernel("TL1: Data Access Exception", regs);
-}
-
-void do_iae_tl1(struct pt_regs *regs)
-{
- exception_enter();
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
- die_if_kernel("TL1: Instruction Access Exception", regs);
-}
-
void do_div0_tl1(struct pt_regs *regs)
{
exception_enter();
die_if_kernel("TL1: DIV0 Exception", regs);
}
-void do_fpdis_tl1(struct pt_regs *regs)
-{
- exception_enter();
- dump_tl1_traplog((struct tl1_traplog *)(regs + 1));
- die_if_kernel("TL1: FPU Disabled", regs);
-}
-
void do_fpieee_tl1(struct pt_regs *regs)
{
exception_enter();
.text
ENTRY(memmove) /* o0=dst o1=src o2=len */
- mov %o0, %g1
+ brz,pn %o2, 99f
+ mov %o0, %g1
+
cmp %o0, %o1
- bleu,pt %xcc, memcpy
+ bleu,pt %xcc, 2f
add %o1, %o2, %g7
cmp %g7, %o0
bleu,pt %xcc, memcpy
stb %g7, [%o0]
bne,pt %icc, 1b
sub %o0, 1, %o0
-
+99:
retl
mov %g1, %o0
+
+ /* We can't just call memcpy for these memmove cases. On some
+ * chips the memcpy uses cache initializing stores and when dst
+ * and src are close enough, those can clobber the source data
+ * before we've loaded it in.
+ */
+2: or %o0, %o1, %g7
+ or %o2, %g7, %g7
+ andcc %g7, 0x7, %g0
+ bne,pn %xcc, 4f
+ nop
+
+3: ldx [%o1], %g7
+ add %o1, 8, %o1
+ subcc %o2, 8, %o2
+ add %o0, 8, %o0
+ bne,pt %icc, 3b
+ stx %g7, [%o0 - 0x8]
+ ba,a,pt %xcc, 99b
+
+4: ldub [%o1], %g7
+ add %o1, 1, %o1
+ subcc %o2, 1, %o2
+ add %o0, 1, %o0
+ bne,pt %icc, 4b
+ stb %g7, [%o0 - 0x1]
+ ba,a,pt %xcc, 99b
ENDPROC(memmove)
return 0;
}
-device_initcall(report_memory);
+arch_initcall(report_memory);
#ifdef CONFIG_SMP
#define do_flush_tlb_kernel_range smp_flush_tlb_kernel_range
depends on X86_IO_APIC
select IOSF_MBI
select INTEL_IMR
+ select COMMON_CLK
---help---
Select to include support for Quark X1000 SoC.
Say Y here if you have a Quark based system such as the Arduino
static const char build_str[] = UTS_RELEASE " (" LINUX_COMPILE_BY "@"
LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION;
-struct kaslr_setup_data {
- __u64 next;
- __u32 type;
- __u32 len;
- __u8 data[1];
-} kaslr_setup_data;
-
#define I8254_PORT_CONTROL 0x43
#define I8254_PORT_COUNTER0 0x40
#define I8254_CMD_READBACK 0xC0
return slots_fetch_random();
}
-static void add_kaslr_setup_data(struct boot_params *params, __u8 enabled)
-{
- struct setup_data *data;
-
- kaslr_setup_data.type = SETUP_KASLR;
- kaslr_setup_data.len = 1;
- kaslr_setup_data.next = 0;
- kaslr_setup_data.data[0] = enabled;
-
- data = (struct setup_data *)(unsigned long)params->hdr.setup_data;
-
- while (data && data->next)
- data = (struct setup_data *)(unsigned long)data->next;
-
- if (data)
- data->next = (unsigned long)&kaslr_setup_data;
- else
- params->hdr.setup_data = (unsigned long)&kaslr_setup_data;
-
-}
-
-unsigned char *choose_kernel_location(struct boot_params *params,
- unsigned char *input,
+unsigned char *choose_kernel_location(unsigned char *input,
unsigned long input_size,
unsigned char *output,
unsigned long output_size)
#ifdef CONFIG_HIBERNATION
if (!cmdline_find_option_bool("kaslr")) {
debug_putstr("KASLR disabled by default...\n");
- add_kaslr_setup_data(params, 0);
goto out;
}
#else
if (cmdline_find_option_bool("nokaslr")) {
debug_putstr("KASLR disabled by cmdline...\n");
- add_kaslr_setup_data(params, 0);
goto out;
}
#endif
- add_kaslr_setup_data(params, 1);
/* Record the various known unsafe memory ranges. */
mem_avoid_init((unsigned long)input, input_size,
* the entire decompressed kernel plus relocation table, or the
* entire decompressed kernel plus .bss and .brk sections.
*/
- output = choose_kernel_location(real_mode, input_data, input_len,
- output,
+ output = choose_kernel_location(input_data, input_len, output,
output_len > run_size ? output_len
: run_size);
#if CONFIG_RANDOMIZE_BASE
/* aslr.c */
-unsigned char *choose_kernel_location(struct boot_params *params,
- unsigned char *input,
+unsigned char *choose_kernel_location(unsigned char *input,
unsigned long input_size,
unsigned char *output,
unsigned long output_size);
bool has_cpuflag(int flag);
#else
static inline
-unsigned char *choose_kernel_location(struct boot_params *params,
- unsigned char *input,
+unsigned char *choose_kernel_location(unsigned char *input,
unsigned long input_size,
unsigned char *output,
unsigned long output_size)
src = kmalloc(req->cryptlen + req->assoclen, GFP_ATOMIC);
if (!src)
return -ENOMEM;
- assoc = (src + req->cryptlen + auth_tag_len);
+ assoc = (src + req->cryptlen);
scatterwalk_map_and_copy(src, req->src, 0, req->cryptlen, 0);
scatterwalk_map_and_copy(assoc, req->assoc, 0,
req->assoclen, 0);
scatterwalk_done(&src_sg_walk, 0, 0);
scatterwalk_done(&assoc_sg_walk, 0, 0);
} else {
- scatterwalk_map_and_copy(dst, req->dst, 0, req->cryptlen, 1);
+ scatterwalk_map_and_copy(dst, req->dst, 0, tempCipherLen, 1);
kfree(src);
}
return retval;
preempt_disable();
tsk->thread.fpu_counter = 0;
__drop_fpu(tsk);
- clear_used_math();
+ clear_stopped_child_used_math(tsk);
preempt_enable();
}
extern unsigned long max_low_pfn_mapped;
extern unsigned long max_pfn_mapped;
-extern bool kaslr_enabled;
-
static inline phys_addr_t get_max_mapped(void)
{
return (phys_addr_t)max_pfn_mapped << PAGE_SHIFT;
extern int (*pcibios_enable_irq)(struct pci_dev *dev);
extern void (*pcibios_disable_irq)(struct pci_dev *dev);
+extern bool mp_should_keep_irq(struct device *dev);
+
struct pci_raw_ops {
int (*read)(unsigned int domain, unsigned int bus, unsigned int devfn,
int reg, int len, u32 *val);
if (boot_cpu_has(X86_FEATURE_XSAVES))
asm volatile("1:"XSAVES"\n\t"
"2:\n\t"
- : : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
+ xstate_fault
+ : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
: "memory");
else
asm volatile("1:"XSAVE"\n\t"
"2:\n\t"
- : : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
+ xstate_fault
+ : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
: "memory");
-
- asm volatile(xstate_fault
- : "0" (0)
- : "memory");
-
return err;
}
if (boot_cpu_has(X86_FEATURE_XSAVES))
asm volatile("1:"XRSTORS"\n\t"
"2:\n\t"
- : : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
+ xstate_fault
+ : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
: "memory");
else
asm volatile("1:"XRSTOR"\n\t"
"2:\n\t"
- : : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
+ xstate_fault
+ : "D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
: "memory");
-
- asm volatile(xstate_fault
- : "0" (0)
- : "memory");
-
return err;
}
*/
alternative_input_2(
"1:"XSAVE,
- "1:"XSAVEOPT,
+ XSAVEOPT,
X86_FEATURE_XSAVEOPT,
- "1:"XSAVES,
+ XSAVES,
X86_FEATURE_XSAVES,
[fx] "D" (fx), "a" (lmask), "d" (hmask) :
"memory");
*/
alternative_input(
"1: " XRSTOR,
- "1: " XRSTORS,
+ XRSTORS,
X86_FEATURE_XSAVES,
"D" (fx), "m" (*fx), "a" (lmask), "d" (hmask)
: "memory");
#define SETUP_DTB 2
#define SETUP_PCI 3
#define SETUP_EFI 4
-#define SETUP_KASLR 5
/* ram_size flags */
#define RAMDISK_IMAGE_START_MASK 0x07FF
return 0;
}
+/*
+ * ACPI offers an alternative platform interface model that removes
+ * ACPI hardware requirements for platforms that do not implement
+ * the PC Architecture.
+ *
+ * We initialize the Hardware-reduced ACPI model here:
+ */
+static void __init acpi_reduced_hw_init(void)
+{
+ if (acpi_gbl_reduced_hardware) {
+ /*
+ * Override x86_init functions and bypass legacy pic
+ * in Hardware-reduced ACPI mode
+ */
+ x86_init.timers.timer_init = x86_init_noop;
+ x86_init.irqs.pre_vector_init = x86_init_noop;
+ legacy_pic = &null_legacy_pic;
+ }
+}
+
/*
* If your system is blacklisted here, but you find that acpi=force
* works for you, please contact linux-acpi@vger.kernel.org
*/
early_acpi_process_madt();
+ /*
+ * Hardware-reduced ACPI mode initialization:
+ */
+ acpi_reduced_hw_init();
+
return 0;
}
static unsigned int get_apic_id(unsigned long x)
{
unsigned long value;
- unsigned int id;
+ unsigned int id = (x >> 24) & 0xff;
- rdmsrl(MSR_FAM10H_NODE_ID, value);
- id = ((x >> 24) & 0xffU) | ((value << 2) & 0xff00U);
+ if (static_cpu_has_safe(X86_FEATURE_NODEID_MSR)) {
+ rdmsrl(MSR_FAM10H_NODE_ID, value);
+ id |= (value << 2) & 0xff00;
+ }
return id;
}
static void fixup_cpu_id(struct cpuinfo_x86 *c, int node)
{
- if (c->phys_proc_id != node) {
- c->phys_proc_id = node;
- per_cpu(cpu_llc_id, smp_processor_id()) = node;
+ u64 val;
+ u32 nodes = 1;
+
+ this_cpu_write(cpu_llc_id, node);
+
+ /* Account for nodes per socket in multi-core-module processors */
+ if (static_cpu_has_safe(X86_FEATURE_NODEID_MSR)) {
+ rdmsrl(MSR_FAM10H_NODE_ID, val);
+ nodes = ((val >> 3) & 7) + 1;
}
+
+ c->phys_proc_id = node / nodes;
}
static int __init numachip_system_init(void)
wait_for_master_cpu(cpu);
+ /*
+ * Initialize the CR4 shadow before doing anything that could
+ * try to read it.
+ */
+ cr4_init_shadow();
+
show_ucode_info_early();
printk(KERN_INFO "Initializing CPU#%d\n", cpu);
{ 0xb2, TLB_INST_4K, 64, " TLB_INST 4KByte pages, 4-way set associative" },
{ 0xb3, TLB_DATA_4K, 128, " TLB_DATA 4 KByte pages, 4-way set associative" },
{ 0xb4, TLB_DATA_4K, 256, " TLB_DATA 4 KByte pages, 4-way associative" },
- { 0xb5, TLB_INST_4K, 64, " TLB_INST 4 KByte pages, 8-way set ssociative" },
- { 0xb6, TLB_INST_4K, 128, " TLB_INST 4 KByte pages, 8-way set ssociative" },
+ { 0xb5, TLB_INST_4K, 64, " TLB_INST 4 KByte pages, 8-way set associative" },
+ { 0xb6, TLB_INST_4K, 128, " TLB_INST 4 KByte pages, 8-way set associative" },
{ 0xba, TLB_DATA_4K, 64, " TLB_DATA 4 KByte pages, 4-way associative" },
{ 0xc0, TLB_DATA_4K_4M, 8, " TLB_DATA 4 KByte and 4 MByte pages, 4-way associative" },
{ 0xc1, STLB_4K_2M, 1024, " STLB 4 KByte and 2 MByte pages, 8-way associative" },
INTEL_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PREC_DIST */
INTEL_EVENT_CONSTRAINT(0xcd, 0x8), /* MEM_TRANS_RETIRED.LOAD_LATENCY */
/* CYCLE_ACTIVITY.CYCLES_L1D_PENDING */
- INTEL_EVENT_CONSTRAINT(0x08a3, 0x4),
+ INTEL_UEVENT_CONSTRAINT(0x08a3, 0x4),
/* CYCLE_ACTIVITY.STALLS_L1D_PENDING */
- INTEL_EVENT_CONSTRAINT(0x0ca3, 0x4),
+ INTEL_UEVENT_CONSTRAINT(0x0ca3, 0x4),
/* CYCLE_ACTIVITY.CYCLES_NO_EXECUTE */
- INTEL_EVENT_CONSTRAINT(0x04a3, 0xf),
+ INTEL_UEVENT_CONSTRAINT(0x04a3, 0xf),
EVENT_CONSTRAINT_END
};
if (c)
return c;
- c = intel_pebs_constraints(event);
+ c = intel_shared_regs_constraints(cpuc, event);
if (c)
return c;
- c = intel_shared_regs_constraints(cpuc, event);
+ c = intel_pebs_constraints(event);
if (c)
return c;
ENTRY(xen_do_upcall)
1: mov %esp, %eax
call xen_evtchn_do_upcall
+#ifndef CONFIG_PREEMPT
+ call xen_maybe_preempt_hcall
+#endif
jmp ret_from_intr
CFI_ENDPROC
ENDPROC(xen_hypervisor_callback)
testl $3, CS-ARGOFFSET(%rsp) # from kernel_thread?
jz 1f
- testl $_TIF_IA32, TI_flags(%rcx) # 32-bit compat task needs IRET
- jnz int_ret_from_sys_call
-
- RESTORE_TOP_OF_STACK %rdi, -ARGOFFSET
- jmp ret_from_sys_call # go to the SYSRET fastpath
+ /*
+ * By the time we get here, we have no idea whether our pt_regs,
+ * ti flags, and ti status came from the 64-bit SYSCALL fast path,
+ * the slow path, or one of the ia32entry paths.
+ * Use int_ret_from_sys_call to return, since it can safely handle
+ * all of the above.
+ */
+ jmp int_ret_from_sys_call
1:
subq $REST_SKIP, %rsp # leave space for volatiles
* Has incomplete stack frame and undefined top of stack.
*/
ret_from_sys_call:
- testl $_TIF_ALLWORK_MASK,TI_flags+THREAD_INFO(%rsp,RIP-ARGOFFSET)
- jnz int_ret_from_sys_call_fixup /* Go the the slow path */
-
LOCKDEP_SYS_EXIT
DISABLE_INTERRUPTS(CLBR_NONE)
TRACE_IRQS_OFF
+
+ /*
+ * We must check ti flags with interrupts (or at least preemption)
+ * off because we must *never* return to userspace without
+ * processing exit work that is enqueued if we're preempted here.
+ * In particular, returning to userspace with any of the one-shot
+ * flags (TIF_NOTIFY_RESUME, TIF_USER_RETURN_NOTIFY, etc) set is
+ * very bad.
+ */
+ testl $_TIF_ALLWORK_MASK,TI_flags+THREAD_INFO(%rsp,RIP-ARGOFFSET)
+ jnz int_ret_from_sys_call_fixup /* Go the the slow path */
+
CFI_REMEMBER_STATE
/*
* sysretq will re-enable interrupts:
int_ret_from_sys_call_fixup:
FIXUP_TOP_OF_STACK %r11, -ARGOFFSET
- jmp int_ret_from_sys_call
+ jmp int_ret_from_sys_call_irqs_off
/* Do syscall tracing */
tracesys:
GLOBAL(int_ret_from_sys_call)
DISABLE_INTERRUPTS(CLBR_NONE)
TRACE_IRQS_OFF
+int_ret_from_sys_call_irqs_off:
movl $_TIF_ALLWORK_MASK,%edi
/* edi: mask to check */
GLOBAL(int_with_check)
cmpq %r11,(EFLAGS-ARGOFFSET)(%rsp) /* R11 == RFLAGS */
jne opportunistic_sysret_failed
- testq $X86_EFLAGS_RF,%r11 /* sysret can't restore RF */
+ /*
+ * SYSRET can't restore RF. SYSRET can restore TF, but unlike IRET,
+ * restoring TF results in a trap from userspace immediately after
+ * SYSRET. This would cause an infinite loop whenever #DB happens
+ * with register state that satisfies the opportunistic SYSRET
+ * conditions. For example, single-stepping this user code:
+ *
+ * movq $stuck_here,%rcx
+ * pushfq
+ * popq %r11
+ * stuck_here:
+ *
+ * would never get past 'stuck_here'.
+ */
+ testq $(X86_EFLAGS_RF|X86_EFLAGS_TF), %r11
jnz opportunistic_sysret_failed
/* nothing to check for RSP */
popq %rsp
CFI_DEF_CFA_REGISTER rsp
decl PER_CPU_VAR(irq_count)
+#ifndef CONFIG_PREEMPT
+ call xen_maybe_preempt_hcall
+#endif
jmp error_exit
CFI_ENDPROC
END(xen_do_hypervisor_callback)
{ "bx", 8, offsetof(struct pt_regs, bx) },
{ "cx", 8, offsetof(struct pt_regs, cx) },
{ "dx", 8, offsetof(struct pt_regs, dx) },
- { "si", 8, offsetof(struct pt_regs, dx) },
+ { "si", 8, offsetof(struct pt_regs, si) },
{ "di", 8, offsetof(struct pt_regs, di) },
{ "bp", 8, offsetof(struct pt_regs, bp) },
{ "sp", 8, offsetof(struct pt_regs, sp) },
__recover_probed_insn(kprobe_opcode_t *buf, unsigned long addr)
{
struct kprobe *kp;
+ unsigned long faddr;
kp = get_kprobe((void *)addr);
- /* There is no probe, return original address */
- if (!kp)
+ faddr = ftrace_location(addr);
+ /*
+ * Addresses inside the ftrace location are refused by
+ * arch_check_ftrace_location(). Something went terribly wrong
+ * if such an address is checked here.
+ */
+ if (WARN_ON(faddr && faddr != addr))
+ return 0UL;
+ /*
+ * Use the current code if it is not modified by Kprobe
+ * and it cannot be modified by ftrace.
+ */
+ if (!kp && !faddr)
return addr;
/*
- * Basically, kp->ainsn.insn has an original instruction.
- * However, RIP-relative instruction can not do single-stepping
- * at different place, __copy_instruction() tweaks the displacement of
- * that instruction. In that case, we can't recover the instruction
- * from the kp->ainsn.insn.
+ * Basically, kp->ainsn.insn has an original instruction.
+ * However, RIP-relative instruction can not do single-stepping
+ * at different place, __copy_instruction() tweaks the displacement of
+ * that instruction. In that case, we can't recover the instruction
+ * from the kp->ainsn.insn.
*
- * On the other hand, kp->opcode has a copy of the first byte of
- * the probed instruction, which is overwritten by int3. And
- * the instruction at kp->addr is not modified by kprobes except
- * for the first byte, we can recover the original instruction
- * from it and kp->opcode.
+ * On the other hand, in case on normal Kprobe, kp->opcode has a copy
+ * of the first byte of the probed instruction, which is overwritten
+ * by int3. And the instruction at kp->addr is not modified by kprobes
+ * except for the first byte, we can recover the original instruction
+ * from it and kp->opcode.
+ *
+ * In case of Kprobes using ftrace, we do not have a copy of
+ * the original instruction. In fact, the ftrace location might
+ * be modified at anytime and even could be in an inconsistent state.
+ * Fortunately, we know that the original code is the ideal 5-byte
+ * long NOP.
*/
- memcpy(buf, kp->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
- buf[0] = kp->opcode;
+ memcpy(buf, (void *)addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
+ if (faddr)
+ memcpy(buf, ideal_nops[NOP_ATOMIC5], 5);
+ else
+ buf[0] = kp->opcode;
return (unsigned long)buf;
}
* Recover the probed instruction at addr for further analysis.
* Caller must lock kprobes by kprobe_mutex, or disable preemption
* for preventing to release referencing kprobes.
+ * Returns zero if the instruction can not get recovered.
*/
unsigned long recover_probed_instruction(kprobe_opcode_t *buf, unsigned long addr)
{
* normally used, we just go through if there is no kprobe.
*/
__addr = recover_probed_instruction(buf, addr);
+ if (!__addr)
+ return 0;
kernel_insn_init(&insn, (void *)__addr, MAX_INSN_SIZE);
insn_get_length(&insn);
unsigned long recovered_insn =
recover_probed_instruction(buf, (unsigned long)src);
+ if (!recovered_insn)
+ return 0;
kernel_insn_init(&insn, (void *)recovered_insn, MAX_INSN_SIZE);
insn_get_length(&insn);
/* Another subsystem puts a breakpoint, failed to recover */
*/
return 0;
recovered_insn = recover_probed_instruction(buf, addr);
+ if (!recovered_insn)
+ return 0;
kernel_insn_init(&insn, (void *)recovered_insn, MAX_INSN_SIZE);
insn_get_length(&insn);
/* Another subsystem puts a breakpoint */
#ifdef CONFIG_RANDOMIZE_BASE
static unsigned long module_load_offset;
+static int randomize_modules = 1;
/* Mutex protects the module_load_offset. */
static DEFINE_MUTEX(module_kaslr_mutex);
+static int __init parse_nokaslr(char *p)
+{
+ randomize_modules = 0;
+ return 0;
+}
+early_param("nokaslr", parse_nokaslr);
+
static unsigned long int get_module_load_offset(void)
{
- if (kaslr_enabled) {
+ if (randomize_modules) {
mutex_lock(&module_kaslr_mutex);
/*
* Calculate the module_load_offset the first time this
},
},
+ /* ASRock */
+ { /* Handle problems with rebooting on ASRock Q1900DC-ITX */
+ .callback = set_pci_reboot,
+ .ident = "ASRock Q1900DC-ITX",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASRock"),
+ DMI_MATCH(DMI_BOARD_NAME, "Q1900DC-ITX"),
+ },
+ },
+
/* ASUS */
{ /* Handle problems with rebooting on ASUS P4S800 */
.callback = set_bios_reboot,
unsigned long max_low_pfn_mapped;
unsigned long max_pfn_mapped;
-bool __read_mostly kaslr_enabled = false;
-
#ifdef CONFIG_DMI
RESERVE_BRK(dmi_alloc, 65536);
#endif
}
#endif /* CONFIG_BLK_DEV_INITRD */
-static void __init parse_kaslr_setup(u64 pa_data, u32 data_len)
-{
- kaslr_enabled = (bool)(pa_data + sizeof(struct setup_data));
-}
-
static void __init parse_setup_data(void)
{
struct setup_data *data;
case SETUP_EFI:
parse_efi_setup(pa_data, data_len);
break;
- case SETUP_KASLR:
- parse_kaslr_setup(pa_data, data_len);
- break;
default:
break;
}
static int
dump_kernel_offset(struct notifier_block *self, unsigned long v, void *p)
{
- if (kaslr_enabled)
- pr_emerg("Kernel Offset: 0x%lx from 0x%lx (relocation range: 0x%lx-0x%lx)\n",
- (unsigned long)&_text - __START_KERNEL,
- __START_KERNEL,
- __START_KERNEL_map,
- MODULES_VADDR-1);
- else
- pr_emerg("Kernel Offset: disabled\n");
+ pr_emerg("Kernel Offset: 0x%lx from 0x%lx "
+ "(relocation range: 0x%lx-0x%lx)\n",
+ (unsigned long)&_text - __START_KERNEL, __START_KERNEL,
+ __START_KERNEL_map, MODULES_VADDR-1);
return 0;
}
goto exit;
conditional_sti(regs);
- if (!user_mode(regs))
+ if (!user_mode_vm(regs))
die("bounds", regs, error_code);
if (!cpu_feature_enabled(X86_FEATURE_MPX)) {
* then it's very likely the result of an icebp/int01 trap.
* User wants a sigtrap for that.
*/
- if (!dr6 && user_mode(regs))
+ if (!dr6 && user_mode_vm(regs))
user_icebp = 1;
/* Catch kmemcheck conditions first of all! */
* thread's fpu state, reconstruct fxstate from the fsave
* header. Sanitize the copied state etc.
*/
- struct xsave_struct *xsave = &tsk->thread.fpu.state->xsave;
+ struct fpu *fpu = &tsk->thread.fpu;
struct user_i387_ia32_struct env;
int err = 0;
*/
drop_fpu(tsk);
- if (__copy_from_user(xsave, buf_fx, state_size) ||
+ if (__copy_from_user(&fpu->state->xsave, buf_fx, state_size) ||
__copy_from_user(&env, buf, sizeof(env))) {
+ fpu_finit(fpu);
err = -1;
} else {
sanitize_restored_xstate(tsk, &env, xstate_bv, fx_only);
- set_used_math();
}
+ set_used_math();
if (use_eager_fpu()) {
preempt_disable();
math_state_restore();
goto done;
}
}
- ctxt->dst.orig_val = ctxt->dst.val;
+ /* Copy full 64-bit value for CMPXCHG8B. */
+ ctxt->dst.orig_val64 = ctxt->dst.val64;
special_insn:
return -EOPNOTSUPP;
if (len != 1) {
+ memset(val, 0, len);
pr_pic_unimpl("non byte read\n");
return 0;
}
struct kvm_ioapic *ioapic, int vector, int trigger_mode)
{
int i;
+ struct kvm_lapic *apic = vcpu->arch.apic;
for (i = 0; i < IOAPIC_NUM_PINS; i++) {
union kvm_ioapic_redirect_entry *ent = &ioapic->redirtbl[i];
kvm_notify_acked_irq(ioapic->kvm, KVM_IRQCHIP_IOAPIC, i);
spin_lock(&ioapic->lock);
- if (trigger_mode != IOAPIC_LEVEL_TRIG)
+ if (trigger_mode != IOAPIC_LEVEL_TRIG ||
+ kvm_apic_get_reg(apic, APIC_SPIV) & APIC_SPIV_DIRECTED_EOI)
continue;
ASSERT(ent->fields.trig_mode == IOAPIC_LEVEL_TRIG);
static void kvm_ioapic_send_eoi(struct kvm_lapic *apic, int vector)
{
- if (!(kvm_apic_get_reg(apic, APIC_SPIV) & APIC_SPIV_DIRECTED_EOI) &&
- kvm_ioapic_handles_vector(apic->vcpu->kvm, vector)) {
+ if (kvm_ioapic_handles_vector(apic->vcpu->kvm, vector)) {
int trigger_mode;
if (apic_test_vector(vector, apic->regs + APIC_TMR))
trigger_mode = IOAPIC_LEVEL_TRIG;
apic_set_reg(apic, APIC_TMR + 0x10 * i, 0);
}
apic->irr_pending = kvm_apic_vid_enabled(vcpu->kvm);
- apic->isr_count = kvm_apic_vid_enabled(vcpu->kvm);
+ apic->isr_count = kvm_x86_ops->hwapic_isr_update ? 1 : 0;
apic->highest_isr_cache = -1;
update_divide_count(apic);
atomic_set(&apic->lapic_timer.pending, 0);
update_divide_count(apic);
start_apic_timer(apic);
apic->irr_pending = true;
- apic->isr_count = kvm_apic_vid_enabled(vcpu->kvm) ?
+ apic->isr_count = kvm_x86_ops->hwapic_isr_update ?
1 : count_vectors(apic->regs + APIC_ISR);
apic->highest_isr_cache = -1;
if (kvm_x86_ops->hwapic_irr_update)
return;
}
-static void svm_hwapic_isr_update(struct kvm *kvm, int isr)
-{
- return;
-}
-
static void svm_sync_pir_to_irr(struct kvm_vcpu *vcpu)
{
return;
.set_virtual_x2apic_mode = svm_set_virtual_x2apic_mode,
.vm_has_apicv = svm_vm_has_apicv,
.load_eoi_exitmap = svm_load_eoi_exitmap,
- .hwapic_isr_update = svm_hwapic_isr_update,
.sync_pir_to_irr = svm_sync_pir_to_irr,
.set_tss_addr = svm_set_tss_addr,
{
unsigned long *msr_bitmap;
- if (irqchip_in_kernel(vcpu->kvm) && apic_x2apic_mode(vcpu->arch.apic)) {
+ if (is_guest_mode(vcpu))
+ msr_bitmap = vmx_msr_bitmap_nested;
+ else if (irqchip_in_kernel(vcpu->kvm) &&
+ apic_x2apic_mode(vcpu->arch.apic)) {
if (is_long_mode(vcpu))
msr_bitmap = vmx_msr_bitmap_longmode_x2apic;
else
if (enable_ept) {
/* nested EPT: emulate EPT also to L1 */
vmx->nested.nested_vmx_secondary_ctls_high |=
- SECONDARY_EXEC_ENABLE_EPT |
- SECONDARY_EXEC_UNRESTRICTED_GUEST;
+ SECONDARY_EXEC_ENABLE_EPT;
vmx->nested.nested_vmx_ept_caps = VMX_EPT_PAGE_WALK_4_BIT |
VMX_EPTP_WB_BIT | VMX_EPT_2MB_PAGE_BIT |
VMX_EPT_INVEPT_BIT;
} else
vmx->nested.nested_vmx_ept_caps = 0;
+ if (enable_unrestricted_guest)
+ vmx->nested.nested_vmx_secondary_ctls_high |=
+ SECONDARY_EXEC_UNRESTRICTED_GUEST;
+
/* miscellaneous data */
rdmsr(MSR_IA32_VMX_MISC,
vmx->nested.nested_vmx_misc_low,
return 0;
}
+static inline bool kvm_vcpu_trigger_posted_interrupt(struct kvm_vcpu *vcpu)
+{
+#ifdef CONFIG_SMP
+ if (vcpu->mode == IN_GUEST_MODE) {
+ apic->send_IPI_mask(get_cpu_mask(vcpu->cpu),
+ POSTED_INTR_VECTOR);
+ return true;
+ }
+#endif
+ return false;
+}
+
static int vmx_deliver_nested_posted_interrupt(struct kvm_vcpu *vcpu,
int vector)
{
if (is_guest_mode(vcpu) &&
vector == vmx->nested.posted_intr_nv) {
/* the PIR and ON have been set by L1. */
- if (vcpu->mode == IN_GUEST_MODE)
- apic->send_IPI_mask(get_cpu_mask(vcpu->cpu),
- POSTED_INTR_VECTOR);
+ kvm_vcpu_trigger_posted_interrupt(vcpu);
/*
* If a posted intr is not recognized by hardware,
* we will accomplish it in the next vmentry.
r = pi_test_and_set_on(&vmx->pi_desc);
kvm_make_request(KVM_REQ_EVENT, vcpu);
-#ifdef CONFIG_SMP
- if (!r && (vcpu->mode == IN_GUEST_MODE))
- apic->send_IPI_mask(get_cpu_mask(vcpu->cpu),
- POSTED_INTR_VECTOR);
- else
-#endif
+ if (r || !kvm_vcpu_trigger_posted_interrupt(vcpu))
kvm_vcpu_kick(vcpu);
}
}
if (cpu_has_vmx_msr_bitmap() &&
- exec_control & CPU_BASED_USE_MSR_BITMAPS &&
- nested_vmx_merge_msr_bitmap(vcpu, vmcs12)) {
- vmcs_write64(MSR_BITMAP, __pa(vmx_msr_bitmap_nested));
+ exec_control & CPU_BASED_USE_MSR_BITMAPS) {
+ nested_vmx_merge_msr_bitmap(vcpu, vmcs12);
+ /* MSR_BITMAP will be set by following vmx_set_efer. */
} else
exec_control &= ~CPU_BASED_USE_MSR_BITMAPS;
case KVM_CAP_USER_NMI:
case KVM_CAP_REINJECT_CONTROL:
case KVM_CAP_IRQ_INJECT_STATUS:
- case KVM_CAP_IRQFD:
case KVM_CAP_IOEVENTFD:
case KVM_CAP_IOEVENTFD_NO_LENGTH:
case KVM_CAP_PIT2:
config LGUEST_GUEST
bool "Lguest guest support"
- depends on X86_32 && PARAVIRT
+ depends on X86_32 && PARAVIRT && PCI
select TTY
select VIRTUALIZATION
select VIRTIO
help
Lguest is a tiny in-kernel hypervisor. Selecting this will
allow your kernel to boot under lguest. This option will increase
- your kernel size by about 6k. If in doubt, say N.
+ your kernel size by about 10k. If in doubt, say N.
If you say Y here, make sure you say Y (or M) to the virtio block
and net drivers which lguest needs.
struct list_head *list)
{
int ret;
- struct resource_entry *entry;
+ struct resource_entry *entry, *tmp;
sprintf(info->name, "PCI Bus %04x:%02x", domain, busnum);
info->bridge = device;
dev_dbg(&device->dev,
"no IO and memory resources present in _CRS\n");
else
- resource_list_for_each_entry(entry, list)
- entry->res->name = info->name;
+ resource_list_for_each_entry_safe(entry, tmp, list) {
+ if ((entry->res->flags & IORESOURCE_WINDOW) == 0 ||
+ (entry->res->flags & IORESOURCE_DISABLED))
+ resource_list_destroy_entry(entry);
+ else
+ entry->res->name = info->name;
+ }
}
struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
}
}
-/*
- * Some device drivers assume dev->irq won't change after calling
- * pci_disable_device(). So delay releasing of IRQ resource to driver
- * unbinding time. Otherwise it will break PM subsystem and drivers
- * like xen-pciback etc.
- */
-static int pci_irq_notifier(struct notifier_block *nb, unsigned long action,
- void *data)
-{
- struct pci_dev *dev = to_pci_dev(data);
-
- if (action != BUS_NOTIFY_UNBOUND_DRIVER)
- return NOTIFY_DONE;
-
- if (pcibios_disable_irq)
- pcibios_disable_irq(dev);
-
- return NOTIFY_OK;
-}
-
-static struct notifier_block pci_irq_nb = {
- .notifier_call = pci_irq_notifier,
- .priority = INT_MIN,
-};
-
int __init pcibios_init(void)
{
if (!raw_pci_ops) {
if (pci_bf_sort >= pci_force_bf)
pci_sort_breadthfirst();
-
- bus_register_notifier(&pci_bus_type, &pci_irq_nb);
-
return 0;
}
return 0;
}
+void pcibios_disable_device (struct pci_dev *dev)
+{
+ if (!pci_dev_msi_enabled(dev) && pcibios_disable_irq)
+ pcibios_disable_irq(dev);
+}
+
int pci_ext_cfg_avail(void)
{
if (raw_pci_ext_ops)
static void intel_mid_pci_irq_disable(struct pci_dev *dev)
{
- if (dev->irq_managed && dev->irq > 0) {
+ if (!mp_should_keep_irq(&dev->dev) && dev->irq_managed &&
+ dev->irq > 0) {
mp_unmap_irq(dev->irq);
dev->irq_managed = 0;
- dev->irq = 0;
}
}
return 0;
}
+bool mp_should_keep_irq(struct device *dev)
+{
+ if (dev->power.is_prepared)
+ return true;
+#ifdef CONFIG_PM
+ if (dev->power.runtime_status == RPM_SUSPENDING)
+ return true;
+#endif
+
+ return false;
+}
+
static void pirq_disable_irq(struct pci_dev *dev)
{
- if (io_apic_assign_pci_irqs && dev->irq_managed && dev->irq) {
+ if (io_apic_assign_pci_irqs && !mp_should_keep_irq(&dev->dev) &&
+ dev->irq_managed && dev->irq) {
mp_unmap_irq(dev->irq);
dev->irq = 0;
dev->irq_managed = 0;
intel_mid_ops = get_intel_mid_ops[__intel_mid_cpu_chip]();
else {
intel_mid_ops = get_intel_mid_ops[INTEL_MID_CPU_CHIP_PENWELL]();
- pr_info("ARCH: Uknown SoC, assuming PENWELL!\n");
+ pr_info("ARCH: Unknown SoC, assuming PENWELL!\n");
}
out:
.text
.globl __kernel_sigreturn
.type __kernel_sigreturn,@function
+ nop /* this guy is needed for .LSTARTFDEDLSI1 below (watch for HACK) */
ALIGN
__kernel_sigreturn:
.LSTART_sigreturn:
BUG_ON(val);
}
#endif
+
+static u64 xen_read_msr_safe(unsigned int msr, int *err)
+{
+ u64 val;
+
+ val = native_read_msr_safe(msr, err);
+ switch (msr) {
+ case MSR_IA32_APICBASE:
+#ifdef CONFIG_X86_X2APIC
+ if (!(cpuid_ecx(1) & (1 << (X86_FEATURE_X2APIC & 31))))
+#endif
+ val &= ~X2APIC_ENABLE;
+ break;
+ }
+ return val;
+}
+
static int xen_write_msr_safe(unsigned int msr, unsigned low, unsigned high)
{
int ret;
.wbinvd = native_wbinvd,
- .read_msr = native_read_msr_safe,
+ .read_msr = xen_read_msr_safe,
.write_msr = xen_write_msr_safe,
.read_tsc = native_read_tsc,
#ifdef CONFIG_X86_32
i386_start_kernel();
#else
+ cr4_init_shadow(); /* 32b kernel does this in i386_start_kernel() */
x86_64_start_reservations((char *)__pa_symbol(&boot_params));
#endif
}
unsigned long xen_max_p2m_pfn __read_mostly;
EXPORT_SYMBOL_GPL(xen_max_p2m_pfn);
+#ifdef CONFIG_XEN_BALLOON_MEMORY_HOTPLUG_LIMIT
+#define P2M_LIMIT CONFIG_XEN_BALLOON_MEMORY_HOTPLUG_LIMIT
+#else
+#define P2M_LIMIT 0
+#endif
+
static DEFINE_SPINLOCK(p2m_update_lock);
static unsigned long *p2m_mid_missing_mfn;
void __init xen_vmalloc_p2m_tree(void)
{
static struct vm_struct vm;
+ unsigned long p2m_limit;
+ p2m_limit = (phys_addr_t)P2M_LIMIT * 1024 * 1024 * 1024 / PAGE_SIZE;
vm.flags = VM_ALLOC;
- vm.size = ALIGN(sizeof(unsigned long) * xen_max_p2m_pfn,
+ vm.size = ALIGN(sizeof(unsigned long) * max(xen_max_p2m_pfn, p2m_limit),
PMD_SIZE * PMDS_PER_MID_PAGE);
vm_area_register_early(&vm, PMD_SIZE * PMDS_PER_MID_PAGE);
pr_notice("p2m virtual area at %p, size is %lx\n", vm.addr, vm.size);
if (p2m_pfn == PFN_DOWN(__pa(p2m_missing)))
p2m_init(p2m);
else
- p2m_init_identity(p2m, pfn);
+ p2m_init_identity(p2m, pfn & ~(P2M_PER_PAGE - 1));
spin_lock_irqsave(&p2m_update_lock, flags);
if (q->queue_flags & (1 << QUEUE_FLAG_SG_GAPS)) {
struct bio_vec *bprev;
- bprev = &rq->biotail->bi_io_vec[bio->bi_vcnt - 1];
+ bprev = &rq->biotail->bi_io_vec[rq->biotail->bi_vcnt - 1];
if (bvec_gap_to_prev(bprev, bio->bi_io_vec[0].bv_offset))
return false;
}
/*
* We're out of tags on this hardware queue, kick any
* pending IO submits before going to sleep waiting for
- * some to complete.
+ * some to complete. Note that hctx can be NULL here for
+ * reserved tag allocation.
*/
- blk_mq_run_hw_queue(hctx, false);
+ if (hctx)
+ blk_mq_run_hw_queue(hctx, false);
/*
* Retry tag allocation after running the hardware queue,
*/
if (percpu_ref_init(&q->mq_usage_counter, blk_mq_usage_counter_release,
PERCPU_REF_INIT_ATOMIC, GFP_KERNEL))
- goto err_map;
+ goto err_mq_usage;
setup_timer(&q->timeout, blk_mq_rq_timer, (unsigned long) q);
blk_queue_rq_timeout(q, 30000);
blk_mq_init_cpu_queues(q, set->nr_hw_queues);
if (blk_mq_init_hw_queues(q, set))
- goto err_hw;
+ goto err_mq_usage;
mutex_lock(&all_q_mutex);
list_add_tail(&q->all_q_node, &all_q_list);
return q;
-err_hw:
+err_mq_usage:
blk_cleanup_queue(q);
err_hctxs:
kfree(map);
b->physical_block_size);
t->io_min = max(t->io_min, b->io_min);
- t->io_opt = lcm(t->io_opt, b->io_opt);
+ t->io_opt = lcm_not_zero(t->io_opt, b->io_opt);
t->cluster &= b->cluster;
t->discard_zeroes_data &= b->discard_zeroes_data;
b->raid_partial_stripes_expensive);
/* Find lowest common alignment_offset */
- t->alignment_offset = lcm(t->alignment_offset, alignment)
+ t->alignment_offset = lcm_not_zero(t->alignment_offset, alignment)
% max(t->physical_block_size, t->io_min);
/* Verify that new alignment_offset is on a logical block boundary */
b->max_discard_sectors);
t->discard_granularity = max(t->discard_granularity,
b->discard_granularity);
- t->discard_alignment = lcm(t->discard_alignment, alignment) %
+ t->discard_alignment = lcm_not_zero(t->discard_alignment, alignment) %
t->discard_granularity;
}
struct lpss_device_desc {
unsigned int flags;
+ const char *clk_con_id;
unsigned int prv_offset;
size_t prv_size_override;
void (*setup)(struct lpss_private_data *pdata);
static struct lpss_device_desc lpt_uart_dev_desc = {
.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR,
+ .clk_con_id = "baudclk",
.prv_offset = 0x800,
.setup = lpss_uart_setup,
};
static struct lpss_device_desc byt_uart_dev_desc = {
.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX,
+ .clk_con_id = "baudclk",
.prv_offset = 0x800,
.setup = lpss_uart_setup,
};
return PTR_ERR(clk);
pdata->clk = clk;
- clk_register_clkdev(clk, NULL, devname);
+ clk_register_clkdev(clk, dev_desc->clk_con_id, devname);
return 0;
}
if (!pin || !dev->irq_managed || dev->irq <= 0)
return;
+ /* Keep IOAPIC pin configuration when suspending */
+ if (dev->dev.power.is_prepared)
+ return;
+#ifdef CONFIG_PM
+ if (dev->dev.power.runtime_status == RPM_SUSPENDING)
+ return;
+#endif
+
entry = acpi_pci_irq_lookup(dev, pin);
if (!entry)
return;
if (gsi >= 0) {
acpi_unregister_gsi(gsi);
dev->irq_managed = 0;
- dev->irq = 0;
}
}
* CHECKME: len might be required to check versus a minimum
* length as well. 1 for io is fine, but for memory it does
* not make any sense at all.
+ * Note: some BIOSes report incorrect length for ACPI address space
+ * descriptor, so remove check of 'reslen == len' to avoid regression.
*/
- if (len && reslen && reslen == len && start <= end)
+ if (len && reslen && start <= end)
return true;
pr_debug("ACPI: invalid or unassigned resource %s [%016llx - %016llx] length [%016llx]\n",
int acpi_video_register(void)
{
- int result = 0;
+ int ret;
+
if (register_count) {
/*
* if the function of acpi_video_register is already called,
mutex_init(&video_list_lock);
INIT_LIST_HEAD(&video_bus_head);
- result = acpi_bus_register_driver(&acpi_video_bus);
- if (result < 0)
- return -ENODEV;
+ ret = acpi_bus_register_driver(&acpi_video_bus);
+ if (ret)
+ return ret;
/*
* When the acpi_video_bus is loaded successfully, increase
static int __init acpi_video_init(void)
{
+ /*
+ * Let the module load even if ACPI is disabled (e.g. due to
+ * a broken BIOS) so that i915.ko can still be loaded on such
+ * old systems without an AcpiOpRegion.
+ *
+ * acpi_video_register() will report -ENODEV later as well due
+ * to acpi_disabled when i915.ko tries to register itself afterwards.
+ */
+ if (acpi_disabled)
+ return 0;
+
dmi_check_system(video_dmi_table);
if (intel_opregion_present())
{
void *page_addr;
unsigned long user_page_addr;
- struct vm_struct tmp_area;
struct page **page;
struct mm_struct *mm;
proc->pid, page_addr);
goto err_alloc_page_failed;
}
- tmp_area.addr = page_addr;
- tmp_area.size = PAGE_SIZE + PAGE_SIZE /* guard page? */;
- ret = map_vm_area(&tmp_area, PAGE_KERNEL, page);
- if (ret) {
+ ret = map_kernel_range_noflush((unsigned long)page_addr,
+ PAGE_SIZE, PAGE_KERNEL, page);
+ flush_cache_vmap((unsigned long)page_addr,
+ (unsigned long)page_addr + PAGE_SIZE);
+ if (ret != 1) {
pr_err("%d: binder_alloc_buf failed to map page at %p in kernel\n",
proc->pid, page_addr);
goto err_map_kernel_failed;
{ "PIONEER DVD-RW DVR-216D", NULL, ATA_HORKAGE_NOSETXFER },
/* devices that don't properly handle queued TRIM commands */
- { "Micron_M[56]*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
+ { "Micron_M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
+ ATA_HORKAGE_ZERO_AFTER_TRIM, },
+ { "Crucial_CT*M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
+ ATA_HORKAGE_ZERO_AFTER_TRIM, },
+ { "Micron_M5[15]0*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM |
+ ATA_HORKAGE_ZERO_AFTER_TRIM, },
+ { "Crucial_CT*M550*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM |
+ ATA_HORKAGE_ZERO_AFTER_TRIM, },
+ { "Crucial_CT*MX100*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM |
+ ATA_HORKAGE_ZERO_AFTER_TRIM, },
+ { "Samsung SSD 850 PRO*", NULL, ATA_HORKAGE_NO_NCQ_TRIM |
ATA_HORKAGE_ZERO_AFTER_TRIM, },
- { "Crucial_CT*SSD*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, },
/*
* As defined, the DRAT (Deterministic Read After Trim) and RZAT
*/
{ "INTEL*SSDSC2MH*", NULL, 0, },
+ { "Micron*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, },
+ { "Crucial*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "INTEL*SSD*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "SSD*INTEL*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, },
{ "Samsung*SSD*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, },
return NULL;
/* libsas case */
- if (!ap->scsi_host) {
+ if (ap->flags & ATA_FLAG_SAS_HOST) {
tag = ata_sas_allocate_tag(ap);
if (tag < 0)
return NULL;
tag = qc->tag;
if (likely(ata_tag_valid(tag))) {
qc->tag = ATA_TAG_POISON;
- if (!ap->scsi_host)
+ if (ap->flags & ATA_FLAG_SAS_HOST)
ata_sas_free_tag(tag, ap);
}
}
*/
ata_msleep(ap, 1);
+ sata_set_spd(link);
+
/*
* Now, bring the host controller online again, this can take time
* as PHY reset and communication establishment, 1st D2H FIS and
}
static int pm_genpd_summary_one(struct seq_file *s,
- struct generic_pm_domain *gpd)
+ struct generic_pm_domain *genpd)
{
static const char * const status_lookup[] = {
[GPD_STATE_ACTIVE] = "on",
struct gpd_link *link;
int ret;
- ret = mutex_lock_interruptible(&gpd->lock);
+ ret = mutex_lock_interruptible(&genpd->lock);
if (ret)
return -ERESTARTSYS;
- if (WARN_ON(gpd->status >= ARRAY_SIZE(status_lookup)))
+ if (WARN_ON(genpd->status >= ARRAY_SIZE(status_lookup)))
goto exit;
- seq_printf(s, "%-30s %-15s ", gpd->name, status_lookup[gpd->status]);
+ seq_printf(s, "%-30s %-15s ", genpd->name, status_lookup[genpd->status]);
/*
* Modifications on the list require holding locks on both
* master and slave, so we are safe.
- * Also gpd->name is immutable.
+ * Also genpd->name is immutable.
*/
- list_for_each_entry(link, &gpd->master_links, master_node) {
+ list_for_each_entry(link, &genpd->master_links, master_node) {
seq_printf(s, "%s", link->slave->name);
- if (!list_is_last(&link->master_node, &gpd->master_links))
+ if (!list_is_last(&link->master_node, &genpd->master_links))
seq_puts(s, ", ");
}
- list_for_each_entry(pm_data, &gpd->dev_list, list_node) {
+ list_for_each_entry(pm_data, &genpd->dev_list, list_node) {
kobj_path = kobject_get_path(&pm_data->dev->kobj, GFP_KERNEL);
if (kobj_path == NULL)
continue;
seq_puts(s, "\n");
exit:
- mutex_unlock(&gpd->lock);
+ mutex_unlock(&genpd->lock);
return 0;
}
static int pm_genpd_summary_show(struct seq_file *s, void *data)
{
- struct generic_pm_domain *gpd;
+ struct generic_pm_domain *genpd;
int ret = 0;
seq_puts(s, " domain status slaves\n");
if (ret)
return -ERESTARTSYS;
- list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
- ret = pm_genpd_summary_one(s, gpd);
+ list_for_each_entry(genpd, &gpd_list, gpd_list_node) {
+ ret = pm_genpd_summary_one(s, genpd);
if (ret)
break;
}
pm_abort_suspend = true;
freeze_wake();
}
+EXPORT_SYMBOL_GPL(pm_system_wakeup);
void pm_wakeup_clear(void)
{
extern struct regcache_ops regcache_lzo_ops;
extern struct regcache_ops regcache_flat_ops;
+static inline const char *regmap_name(const struct regmap *map)
+{
+ if (map->dev)
+ return dev_name(map->dev);
+
+ return map->name;
+}
+
#endif
if (pos == 0) {
memmove(blk + offset * map->cache_word_size,
blk, rbnode->blklen * map->cache_word_size);
- bitmap_shift_right(present, present, offset, blklen);
+ bitmap_shift_left(present, present, offset, blklen);
}
/* update the rbnode block, its size and the base register */
ret = map->cache_ops->read(map, reg, value);
if (ret == 0)
- trace_regmap_reg_read_cache(map->dev, reg, *value);
+ trace_regmap_reg_read_cache(map, reg, *value);
return ret;
}
dev_dbg(map->dev, "Syncing %s cache\n",
map->cache_ops->name);
name = map->cache_ops->name;
- trace_regcache_sync(map->dev, name, "start");
+ trace_regcache_sync(map, name, "start");
if (!map->cache_dirty)
goto out;
regmap_async_complete(map);
- trace_regcache_sync(map->dev, name, "stop");
+ trace_regcache_sync(map, name, "stop");
return ret;
}
name = map->cache_ops->name;
dev_dbg(map->dev, "Syncing %s cache from %d-%d\n", name, min, max);
- trace_regcache_sync(map->dev, name, "start region");
+ trace_regcache_sync(map, name, "start region");
if (!map->cache_dirty)
goto out;
regmap_async_complete(map);
- trace_regcache_sync(map->dev, name, "stop region");
+ trace_regcache_sync(map, name, "stop region");
return ret;
}
map->lock(map->lock_arg);
- trace_regcache_drop_region(map->dev, min, max);
+ trace_regcache_drop_region(map, min, max);
ret = map->cache_ops->drop(map, min, max);
map->lock(map->lock_arg);
WARN_ON(map->cache_bypass && enable);
map->cache_only = enable;
- trace_regmap_cache_only(map->dev, enable);
+ trace_regmap_cache_only(map, enable);
map->unlock(map->lock_arg);
}
EXPORT_SYMBOL_GPL(regcache_cache_only);
map->lock(map->lock_arg);
WARN_ON(map->cache_only && enable);
map->cache_bypass = enable;
- trace_regmap_cache_bypass(map->dev, enable);
+ trace_regmap_cache_bypass(map, enable);
map->unlock(map->lock_arg);
}
EXPORT_SYMBOL_GPL(regcache_cache_bypass);
for (i = start; i < end; i++) {
regtmp = block_base + (i * map->reg_stride);
- if (!regcache_reg_present(cache_present, i))
+ if (!regcache_reg_present(cache_present, i) ||
+ !regmap_writeable(map, regtmp))
continue;
val = regcache_get_val(map, block, i);
for (i = start; i < end; i++) {
regtmp = block_base + (i * map->reg_stride);
- if (!regcache_reg_present(cache_present, i)) {
+ if (!regcache_reg_present(cache_present, i) ||
+ !regmap_writeable(map, regtmp)) {
ret = regcache_sync_block_raw_flush(map, &data,
base, regtmp);
if (ret != 0)
goto err_alloc;
}
- ret = request_threaded_irq(irq, NULL, regmap_irq_thread, irq_flags,
+ ret = request_threaded_irq(irq, NULL, regmap_irq_thread,
+ irq_flags | IRQF_ONESHOT,
chip->name, d);
if (ret != 0) {
dev_err(map->dev, "Failed to request IRQ %d for %s: %d\n",
if (map->async && map->bus->async_write) {
struct regmap_async *async;
- trace_regmap_async_write_start(map->dev, reg, val_len);
+ trace_regmap_async_write_start(map, reg, val_len);
spin_lock_irqsave(&map->async_lock, flags);
async = list_first_entry_or_null(&map->async_free,
return ret;
}
- trace_regmap_hw_write_start(map->dev, reg,
- val_len / map->format.val_bytes);
+ trace_regmap_hw_write_start(map, reg, val_len / map->format.val_bytes);
/* If we're doing a single register write we can probably just
* send the work_buf directly, otherwise try to do a gather
kfree(buf);
}
- trace_regmap_hw_write_done(map->dev, reg,
- val_len / map->format.val_bytes);
+ trace_regmap_hw_write_done(map, reg, val_len / map->format.val_bytes);
return ret;
}
map->format.format_write(map, reg, val);
- trace_regmap_hw_write_start(map->dev, reg, 1);
+ trace_regmap_hw_write_start(map, reg, 1);
ret = map->bus->write(map->bus_context, map->work_buf,
map->format.buf_size);
- trace_regmap_hw_write_done(map->dev, reg, 1);
+ trace_regmap_hw_write_done(map, reg, 1);
return ret;
}
dev_info(map->dev, "%x <= %x\n", reg, val);
#endif
- trace_regmap_reg_write(map->dev, reg, val);
+ trace_regmap_reg_write(map, reg, val);
return map->reg_write(context, reg, val);
}
for (i = 0; i < num_regs; i++) {
int reg = regs[i].reg;
int val = regs[i].def;
- trace_regmap_hw_write_start(map->dev, reg, 1);
+ trace_regmap_hw_write_start(map, reg, 1);
map->format.format_reg(u8, reg, map->reg_shift);
u8 += reg_bytes + pad_bytes;
map->format.format_val(u8, val, 0);
for (i = 0; i < num_regs; i++) {
int reg = regs[i].reg;
- trace_regmap_hw_write_done(map->dev, reg, 1);
+ trace_regmap_hw_write_done(map, reg, 1);
}
return ret;
}
*/
u8[0] |= map->read_flag_mask;
- trace_regmap_hw_read_start(map->dev, reg,
- val_len / map->format.val_bytes);
+ trace_regmap_hw_read_start(map, reg, val_len / map->format.val_bytes);
ret = map->bus->read(map->bus_context, map->work_buf,
map->format.reg_bytes + map->format.pad_bytes,
val, val_len);
- trace_regmap_hw_read_done(map->dev, reg,
- val_len / map->format.val_bytes);
+ trace_regmap_hw_read_done(map, reg, val_len / map->format.val_bytes);
return ret;
}
dev_info(map->dev, "%x => %x\n", reg, *val);
#endif
- trace_regmap_reg_read(map->dev, reg, *val);
+ trace_regmap_reg_read(map, reg, *val);
if (!map->cache_bypass)
regcache_write(map, reg, *val);
struct regmap *map = async->map;
bool wake;
- trace_regmap_async_io_complete(map->dev);
+ trace_regmap_async_io_complete(map);
spin_lock(&map->async_lock);
list_move(&async->list, &map->async_free);
if (!map->bus || !map->bus->async_write)
return 0;
- trace_regmap_async_complete_start(map->dev);
+ trace_regmap_async_complete_start(map);
wait_event(map->async_waitq, regmap_async_is_done(map));
map->async_ret = 0;
spin_unlock_irqrestore(&map->async_lock, flags);
- trace_regmap_async_complete_done(map->dev);
+ trace_regmap_async_complete_done(map);
return ret;
}
return -EINVAL;
}
- nbd_dev = kcalloc(nbds_max, sizeof(*nbd_dev), GFP_KERNEL);
- if (!nbd_dev)
- return -ENOMEM;
-
part_shift = 0;
if (max_part > 0) {
part_shift = fls(max_part);
if (nbds_max > 1UL << (MINORBITS - part_shift))
return -EINVAL;
+ nbd_dev = kcalloc(nbds_max, sizeof(*nbd_dev), GFP_KERNEL);
+ if (!nbd_dev)
+ return -ENOMEM;
+
for (i = 0; i < nbds_max; i++) {
struct gendisk *disk = alloc_disk(1 << part_shift);
if (!disk)
}
}
+#ifdef CONFIG_BLK_DEV_INTEGRITY
static void nvme_dif_prep(u32 p, u32 v, struct t10_pi_tuple *pi)
{
if (be32_to_cpu(pi->ref_tag) == v)
kunmap_atomic(pmap);
}
+static int nvme_noop_verify(struct blk_integrity_iter *iter)
+{
+ return 0;
+}
+
+static int nvme_noop_generate(struct blk_integrity_iter *iter)
+{
+ return 0;
+}
+
+struct blk_integrity nvme_meta_noop = {
+ .name = "NVME_META_NOOP",
+ .generate_fn = nvme_noop_generate,
+ .verify_fn = nvme_noop_verify,
+};
+
+static void nvme_init_integrity(struct nvme_ns *ns)
+{
+ struct blk_integrity integrity;
+
+ switch (ns->pi_type) {
+ case NVME_NS_DPS_PI_TYPE3:
+ integrity = t10_pi_type3_crc;
+ break;
+ case NVME_NS_DPS_PI_TYPE1:
+ case NVME_NS_DPS_PI_TYPE2:
+ integrity = t10_pi_type1_crc;
+ break;
+ default:
+ integrity = nvme_meta_noop;
+ break;
+ }
+ integrity.tuple_size = ns->ms;
+ blk_integrity_register(ns->disk, &integrity);
+ blk_queue_max_integrity_segments(ns->queue, 1);
+}
+#else /* CONFIG_BLK_DEV_INTEGRITY */
+static void nvme_dif_remap(struct request *req,
+ void (*dif_swap)(u32 p, u32 v, struct t10_pi_tuple *pi))
+{
+}
+static void nvme_dif_prep(u32 p, u32 v, struct t10_pi_tuple *pi)
+{
+}
+static void nvme_dif_complete(u32 p, u32 v, struct t10_pi_tuple *pi)
+{
+}
+static void nvme_init_integrity(struct nvme_ns *ns)
+{
+}
+#endif
+
static void req_completion(struct nvme_queue *nvmeq, void *ctx,
struct nvme_completion *cqe)
{
queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, ns->queue);
}
-static int nvme_noop_verify(struct blk_integrity_iter *iter)
-{
- return 0;
-}
-
-static int nvme_noop_generate(struct blk_integrity_iter *iter)
-{
- return 0;
-}
-
-struct blk_integrity nvme_meta_noop = {
- .name = "NVME_META_NOOP",
- .generate_fn = nvme_noop_generate,
- .verify_fn = nvme_noop_verify,
-};
-
-static void nvme_init_integrity(struct nvme_ns *ns)
-{
- struct blk_integrity integrity;
-
- switch (ns->pi_type) {
- case NVME_NS_DPS_PI_TYPE3:
- integrity = t10_pi_type3_crc;
- break;
- case NVME_NS_DPS_PI_TYPE1:
- case NVME_NS_DPS_PI_TYPE2:
- integrity = t10_pi_type1_crc;
- break;
- default:
- integrity = nvme_meta_noop;
- break;
- }
- integrity.tuple_size = ns->ms;
- blk_integrity_register(ns->disk, &integrity);
- blk_queue_max_integrity_segments(ns->queue, 1);
-}
-
static int nvme_revalidate_disk(struct gendisk *disk)
{
struct nvme_ns *ns = disk->private_data;
pi_type = ns->ms == sizeof(struct t10_pi_tuple) ?
id->dps & NVME_NS_DPS_PI_MASK : 0;
- if (disk->integrity && (ns->pi_type != pi_type || ns->ms != old_ms ||
+ if (blk_get_integrity(disk) && (ns->pi_type != pi_type ||
+ ns->ms != old_ms ||
bs != queue_logical_block_size(disk->queue) ||
(ns->ms && id->flbas & NVME_NS_FLBAS_META_EXT)))
blk_integrity_unregister(disk);
ns->pi_type = pi_type;
blk_queue_logical_block_size(ns->queue, bs);
- if (ns->ms && !disk->integrity && (disk->flags & GENHD_FL_UP) &&
+ if (ns->ms && !blk_get_integrity(disk) && (disk->flags & GENHD_FL_UP) &&
!(id->flbas & NVME_NS_FLBAS_META_EXT))
nvme_init_integrity(ns);
- if (id->ncap == 0 || (ns->ms && !disk->integrity))
+ if (id->ncap == 0 || (ns->ms && !blk_get_integrity(disk)))
set_capacity(disk, 0);
else
set_capacity(disk, le64_to_cpup(&id->nsze) << (ns->lba_shift - 9));
list_for_each_entry(ns, &dev->namespaces, list) {
if (ns->disk->flags & GENHD_FL_UP) {
- if (ns->disk->integrity)
+ if (blk_get_integrity(ns->disk))
blk_integrity_unregister(ns->disk);
del_gendisk(ns->disk);
}
}
get_device(dev->device);
+ INIT_LIST_HEAD(&dev->node);
INIT_WORK(&dev->probe_work, nvme_async_probe);
schedule_work(&dev->probe_work);
return 0;
static inline void update_used_max(struct zram *zram,
const unsigned long pages)
{
- int old_max, cur_max;
+ unsigned long old_max, cur_max;
old_max = atomic_long_read(&zram->stats.max_used_pages);
{ USB_DEVICE(0x1286, 0x2046), .driver_info = BTUSB_MARVELL },
/* Intel Bluetooth devices */
+ { USB_DEVICE(0x8087, 0x07da), .driver_info = BTUSB_CSR },
{ USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL },
{ USB_DEVICE(0x8087, 0x0a2a), .driver_info = BTUSB_INTEL },
{ USB_DEVICE(0x8087, 0x0a2b), .driver_info = BTUSB_INTEL_NEW },
{
int rc;
- rc = device_add(&chip->dev);
+ rc = cdev_add(&chip->cdev, chip->dev.devt, 1);
if (rc) {
dev_err(&chip->dev,
- "unable to device_register() %s, major %d, minor %d, err=%d\n",
+ "unable to cdev_add() %s, major %d, minor %d, err=%d\n",
chip->devname, MAJOR(chip->dev.devt),
MINOR(chip->dev.devt), rc);
+ device_unregister(&chip->dev);
return rc;
}
- rc = cdev_add(&chip->cdev, chip->dev.devt, 1);
+ rc = device_add(&chip->dev);
if (rc) {
dev_err(&chip->dev,
- "unable to cdev_add() %s, major %d, minor %d, err=%d\n",
+ "unable to device_register() %s, major %d, minor %d, err=%d\n",
chip->devname, MAJOR(chip->dev.devt),
MINOR(chip->dev.devt), rc);
- device_unregister(&chip->dev);
return rc;
}
* tpm_chip_register() - create a character device for the TPM chip
* @chip: TPM chip to use.
*
- * Creates a character device for the TPM chip and adds sysfs interfaces for
- * the device, PPI and TCPA. As the last step this function adds the
- * chip to the list of TPM chips available for use.
+ * Creates a character device for the TPM chip and adds sysfs attributes for
+ * the device. As the last step this function adds the chip to the list of TPM
+ * chips available for in-kernel use.
*
- * NOTE: This function should be only called after the chip initialization
- * is complete.
- *
- * Called from tpm_<specific>.c probe function only for devices
- * the driver has determined it should claim. Prior to calling
- * this function the specific probe function has called pci_enable_device
- * upon errant exit from this function specific probe function should call
- * pci_disable_device
+ * This function should be only called after the chip initialization is
+ * complete.
*/
int tpm_chip_register(struct tpm_chip *chip)
{
int rc;
- rc = tpm_dev_add_device(chip);
- if (rc)
- return rc;
-
/* Populate sysfs for TPM1 devices. */
if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) {
rc = tpm_sysfs_add_device(chip);
chip->bios_dir = tpm_bios_log_setup(chip->devname);
}
+ rc = tpm_dev_add_device(chip);
+ if (rc)
+ return rc;
+
/* Make the chip available. */
spin_lock(&driver_lock);
list_add_rcu(&chip->list, &tpm_chip_list);
{
struct ibmvtpm_dev *ibmvtpm;
struct ibmvtpm_crq crq;
- u64 *word = (u64 *) &crq;
+ __be64 *word = (__be64 *)&crq;
int rc;
ibmvtpm = (struct ibmvtpm_dev *)TPM_VPRIV(chip);
memcpy((void *)ibmvtpm->rtce_buf, (void *)buf, count);
crq.valid = (u8)IBMVTPM_VALID_CMD;
crq.msg = (u8)VTPM_TPM_COMMAND;
- crq.len = (u16)count;
- crq.data = ibmvtpm->rtce_dma_handle;
+ crq.len = cpu_to_be16(count);
+ crq.data = cpu_to_be32(ibmvtpm->rtce_dma_handle);
- rc = ibmvtpm_send_crq(ibmvtpm->vdev, cpu_to_be64(word[0]),
- cpu_to_be64(word[1]));
+ rc = ibmvtpm_send_crq(ibmvtpm->vdev, be64_to_cpu(word[0]),
+ be64_to_cpu(word[1]));
if (rc != H_SUCCESS) {
dev_err(ibmvtpm->dev, "tpm_ibmvtpm_send failed rc=%d\n", rc);
rc = 0;
struct ibmvtpm_crq {
u8 valid;
u8 msg;
- u16 len;
- u32 data;
- u64 reserved;
+ __be16 len;
+ __be32 data;
+ __be64 reserved;
} __attribute__((packed, aligned(8)));
struct ibmvtpm_crq_queue {
* notification
*/
struct work_struct control_work;
+ struct work_struct config_work;
struct list_head ports;
portdev = vdev->priv;
+ if (!use_multiport(portdev))
+ schedule_work(&portdev->config_work);
+}
+
+static void config_work_handler(struct work_struct *work)
+{
+ struct ports_device *portdev;
+
+ portdev = container_of(work, struct ports_device, control_work);
if (!use_multiport(portdev)) {
+ struct virtio_device *vdev;
struct port *port;
u16 rows, cols;
+ vdev = portdev->vdev;
virtio_cread(vdev, struct virtio_console_config, cols, &cols);
virtio_cread(vdev, struct virtio_console_config, rows, &rows);
virtio_device_ready(portdev->vdev);
+ INIT_WORK(&portdev->config_work, &config_work_handler);
+ INIT_WORK(&portdev->control_work, &control_work_handler);
+
if (multiport) {
unsigned int nr_added_bufs;
spin_lock_init(&portdev->c_ivq_lock);
spin_lock_init(&portdev->c_ovq_lock);
- INIT_WORK(&portdev->control_work, &control_work_handler);
nr_added_bufs = fill_queue(portdev->c_ivq,
&portdev->c_ivq_lock);
/* Finish up work that's lined up */
if (use_multiport(portdev))
cancel_work_sync(&portdev->control_work);
+ else
+ cancel_work_sync(&portdev->config_work);
list_for_each_entry_safe(port, port2, &portdev->ports, list)
unplug_port(port);
virtqueue_disable_cb(portdev->c_ivq);
cancel_work_sync(&portdev->control_work);
+ cancel_work_sync(&portdev->config_work);
/*
* Once more: if control_work_handler() was running, it would
* enable the cb as the last step.
return 0;
}
+static void pmc_irq_suspend(struct irq_data *d)
+{
+ struct at91_pmc *pmc = irq_data_get_irq_chip_data(d);
+
+ pmc->imr = pmc_read(pmc, AT91_PMC_IMR);
+ pmc_write(pmc, AT91_PMC_IDR, pmc->imr);
+}
+
+static void pmc_irq_resume(struct irq_data *d)
+{
+ struct at91_pmc *pmc = irq_data_get_irq_chip_data(d);
+
+ pmc_write(pmc, AT91_PMC_IER, pmc->imr);
+}
+
static struct irq_chip pmc_irq = {
.name = "PMC",
.irq_disable = pmc_irq_mask,
.irq_mask = pmc_irq_mask,
.irq_unmask = pmc_irq_unmask,
.irq_set_type = pmc_irq_set_type,
+ .irq_suspend = pmc_irq_suspend,
+ .irq_resume = pmc_irq_resume,
};
static struct lock_class_key pmc_lock_class;
goto out_free_pmc;
pmc_write(pmc, AT91_PMC_IDR, 0xffffffff);
- if (request_irq(pmc->virq, pmc_irq_handler, IRQF_SHARED, "pmc", pmc))
+ if (request_irq(pmc->virq, pmc_irq_handler,
+ IRQF_SHARED | IRQF_COND_SUSPEND, "pmc", pmc))
goto out_remove_irqdomain;
return pmc;
spinlock_t lock;
const struct at91_pmc_caps *caps;
struct irq_domain *irqdomain;
+ u32 imr;
};
static inline void pmc_lock(struct at91_pmc *pmc)
divider->flags);
}
-/*
- * The reverse of DIV_ROUND_UP: The maximum number which
- * divided by m is r
- */
-#define MULT_ROUND_UP(r, m) ((r) * (m) + (m) - 1)
-
static bool _is_valid_table_div(const struct clk_div_table *table,
unsigned int div)
{
unsigned long parent_rate, unsigned long rate,
unsigned long flags)
{
- int up, down, div;
+ int up, down;
+ unsigned long up_rate, down_rate;
- up = down = div = DIV_ROUND_CLOSEST(parent_rate, rate);
+ up = DIV_ROUND_UP(parent_rate, rate);
+ down = parent_rate / rate;
if (flags & CLK_DIVIDER_POWER_OF_TWO) {
- up = __roundup_pow_of_two(div);
- down = __rounddown_pow_of_two(div);
+ up = __roundup_pow_of_two(up);
+ down = __rounddown_pow_of_two(down);
} else if (table) {
- up = _round_up_table(table, div);
- down = _round_down_table(table, div);
+ up = _round_up_table(table, up);
+ down = _round_down_table(table, down);
}
- return (up - div) <= (div - down) ? up : down;
+ up_rate = DIV_ROUND_UP(parent_rate, up);
+ down_rate = DIV_ROUND_UP(parent_rate, down);
+
+ return (rate - up_rate) <= (down_rate - rate) ? up : down;
}
static int _div_round(const struct clk_div_table *table,
return i;
}
parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
- MULT_ROUND_UP(rate, i));
+ rate * i);
now = DIV_ROUND_UP(parent_rate, i);
if (_is_best_div(rate, now, best, flags)) {
bestdiv = i;
bestdiv = readl(divider->reg) >> divider->shift;
bestdiv &= div_mask(divider->width);
bestdiv = _get_div(divider->table, bestdiv, divider->flags);
- return bestdiv;
+ return DIV_ROUND_UP(*prate, bestdiv);
}
return divider_round_rate(hw, rate, prate, divider->table,
return rate;
}
-EXPORT_SYMBOL_GPL(clk_core_get_rate);
/**
* clk_get_rate - return the rate of clk
return clk_core_get_phase(clk->core);
}
+/**
+ * clk_is_match - check if two clk's point to the same hardware clock
+ * @p: clk compared against q
+ * @q: clk compared against p
+ *
+ * Returns true if the two struct clk pointers both point to the same hardware
+ * clock node. Put differently, returns true if struct clk *p and struct clk *q
+ * share the same struct clk_core object.
+ *
+ * Returns false otherwise. Note that two NULL clks are treated as matching.
+ */
+bool clk_is_match(const struct clk *p, const struct clk *q)
+{
+ /* trivial case: identical struct clk's or both NULL */
+ if (p == q)
+ return true;
+
+ /* true if clk->core pointers match. Avoid derefing garbage */
+ if (!IS_ERR_OR_NULL(p) && !IS_ERR_OR_NULL(q))
+ if (p->core == q->core)
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(clk_is_match);
+
/**
* __clk_init - initialize the data structures in a struct clk
* @dev: device initializing this clk, placeholder for now
},
};
+static struct clk_regmap pll4_vote = {
+ .enable_reg = 0x34c0,
+ .enable_mask = BIT(4),
+ .hw.init = &(struct clk_init_data){
+ .name = "pll4_vote",
+ .parent_names = (const char *[]){ "pll4" },
+ .num_parents = 1,
+ .ops = &clk_pll_vote_ops,
+ },
+};
+
static struct clk_pll pll8 = {
.l_reg = 0x3144,
.m_reg = 0x3148,
static struct clk_regmap *gcc_msm8960_clks[] = {
[PLL3] = &pll3.clkr,
+ [PLL4_VOTE] = &pll4_vote,
[PLL8] = &pll8.clkr,
[PLL8_VOTE] = &pll8_vote,
[PLL14] = &pll14.clkr,
static struct clk_regmap *gcc_apq8064_clks[] = {
[PLL3] = &pll3.clkr,
+ [PLL4_VOTE] = &pll4_vote,
[PLL8] = &pll8.clkr,
[PLL8_VOTE] = &pll8_vote,
[PLL14] = &pll14.clkr,
.remove = lcc_ipq806x_remove,
.driver = {
.name = "lcc-ipq806x",
- .owner = THIS_MODULE,
.of_match_table = lcc_ipq806x_match_table,
},
};
.mnctr_en_bit = 8,
.mnctr_reset_bit = 7,
.mnctr_mode_shift = 5,
- .n_val_shift = 16,
- .m_val_shift = 16,
+ .n_val_shift = 24,
+ .m_val_shift = 8,
.width = 8,
},
.p = {
return PTR_ERR(regmap);
/* Use the correct frequency plan depending on speed of PLL4 */
- val = regmap_read(regmap, 0x4, &val);
+ regmap_read(regmap, 0x4, &val);
if (val == 0x12) {
slimbus_src.freq_tbl = clk_tbl_aif_osr_492;
mi2s_osr_src.freq_tbl = clk_tbl_aif_osr_492;
.remove = lcc_msm8960_remove,
.driver = {
.name = "lcc-msm8960",
- .owner = THIS_MODULE,
.of_match_table = lcc_msm8960_match_table,
},
};
struct fapll_data *fd = to_fapll(hw);
u32 v = readl_relaxed(fd->base);
- v |= (1 << FAPLL_MAIN_PLLEN);
+ v |= FAPLL_MAIN_PLLEN;
writel_relaxed(v, fd->base);
return 0;
struct fapll_data *fd = to_fapll(hw);
u32 v = readl_relaxed(fd->base);
- v &= ~(1 << FAPLL_MAIN_PLLEN);
+ v &= ~FAPLL_MAIN_PLLEN;
writel_relaxed(v, fd->base);
}
struct fapll_data *fd = to_fapll(hw);
u32 v = readl_relaxed(fd->base);
- return v & (1 << FAPLL_MAIN_PLLEN);
+ return v & FAPLL_MAIN_PLLEN;
}
static unsigned long ti_fapll_recalc_rate(struct clk_hw *hw,
config CADENCE_TTC_TIMER
bool
+config ASM9260_TIMER
+ bool
+ select CLKSRC_MMIO
+ select CLKSRC_OF
+
config CLKSRC_NOMADIK_MTU
bool
depends on (ARCH_NOMADIK || ARCH_U8500)
config SH_TIMER_CMT
bool "Renesas CMT timer driver" if COMPILE_TEST
depends on GENERIC_CLOCKEVENTS
+ depends on HAS_IOMEM
default SYS_SUPPORTS_SH_CMT
help
This enables build of a clocksource and clockevent driver for
config SH_TIMER_MTU2
bool "Renesas MTU2 timer driver" if COMPILE_TEST
depends on GENERIC_CLOCKEVENTS
+ depends on HAS_IOMEM
default SYS_SUPPORTS_SH_MTU2
help
This enables build of a clockevent driver for the Multi-Function
config SH_TIMER_TMU
bool "Renesas TMU timer driver" if COMPILE_TEST
depends on GENERIC_CLOCKEVENTS
+ depends on HAS_IOMEM
default SYS_SUPPORTS_SH_TMU
help
This enables build of a clocksource and clockevent driver for
help
This enables OST0 support available on PXA and SA-11x0
platforms.
-
-config ASM9260_TIMER
- bool "Alphascale ASM9260 timer driver"
- depends on GENERIC_CLOCKEVENTS
- select CLKSRC_MMIO
- select CLKSRC_OF
- default y if MACH_ASM9260
- help
- This enables build of a clocksource and clockevent driver for
- the 32-bit System Timer hardware available on a Alphascale ASM9260.
-
endmenu
}
rate = clk_get_rate(clk);
+ mtk_timer_global_reset(evt);
+
if (request_irq(evt->dev.irq, mtk_timer_interrupt,
IRQF_TIMER | IRQF_IRQPOLL, "mtk_timer", evt)) {
pr_warn("failed to setup irq %d\n", evt->dev.irq);
evt->ticks_per_jiffy = DIV_ROUND_UP(rate, HZ);
- mtk_timer_global_reset(evt);
-
/* Configure clock source */
mtk_timer_setup(evt, GPT_CLK_SRC, TIMER_CTRL_OP_FREERUN);
clocksource_mmio_init(evt->gpt_base + TIMER_CNT_REG(GPT_CLK_SRC),
/* Configure clock event */
mtk_timer_setup(evt, GPT_CLK_EVT, TIMER_CTRL_OP_REPEAT);
- mtk_timer_enable_irq(evt, GPT_CLK_EVT);
-
clockevents_config_and_register(&evt->dev, rate, 0x3,
0xffffffff);
+
+ mtk_timer_enable_irq(evt, GPT_CLK_EVT);
+
return;
err_clk_disable:
.dev_id = &ckevt_pxa_osmr0,
};
-static void pxa_timer_common_init(int irq, unsigned long clock_tick_rate)
+static void __init pxa_timer_common_init(int irq, unsigned long clock_tick_rate)
{
timer_writel(0, OIER);
timer_writel(OSSR_M0 | OSSR_M1 | OSSR_M2 | OSSR_M3, OSSR);
clock_event_ddata.base = base;
clock_event_ddata.periodic_top = DIV_ROUND_CLOSEST(rate, 1024 * HZ);
- setup_irq(irq, &efm32_clock_event_irq);
-
clockevents_config_and_register(&clock_event_ddata.evtdev,
DIV_ROUND_CLOSEST(rate, 1024),
0xf, 0xffff);
+ setup_irq(irq, &efm32_clock_event_irq);
+
return 0;
err_get_irq:
#include <linux/irq.h>
#include <linux/irqreturn.h>
#include <linux/reset.h>
-#include <linux/sched_clock.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
.dev_id = &sun5i_clockevent,
};
-static u64 sun5i_timer_sched_read(void)
-{
- return ~readl(timer_base + TIMER_CNTVAL_LO_REG(1));
-}
-
static void __init sun5i_timer_init(struct device_node *node)
{
struct reset_control *rstc;
writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
timer_base + TIMER_CTL_REG(1));
- sched_clock_register(sun5i_timer_sched_read, 32, rate);
clocksource_mmio_init(timer_base + TIMER_CNTVAL_LO_REG(1), node->name,
rate, 340, 32, clocksource_mmio_readl_down);
ticks_per_jiffy = DIV_ROUND_UP(rate, HZ);
- ret = setup_irq(irq, &sun5i_timer_irq);
- if (ret)
- pr_warn("failed to setup irq %d\n", irq);
-
/* Enable timer0 interrupt */
val = readl(timer_base + TIMER_IRQ_EN_REG);
writel(val | TIMER_IRQ_EN(0), timer_base + TIMER_IRQ_EN_REG);
clockevents_config_and_register(&sun5i_clockevent, rate,
TIMER_SYNC_TICKS, 0xffffffff);
+
+ ret = setup_irq(irq, &sun5i_timer_irq);
+ if (ret)
+ pr_warn("failed to setup irq %d\n", irq);
}
CLOCKSOURCE_OF_DECLARE(sun5i_a13, "allwinner,sun5i-a13-hstimer",
sun5i_timer_init);
static int exynos_cpufreq_probe(struct platform_device *pdev)
{
- struct device_node *cpus, *np;
+ struct device_node *cpu0;
int ret = -EINVAL;
exynos_info = kzalloc(sizeof(*exynos_info), GFP_KERNEL);
if (ret)
goto err_cpufreq_reg;
- cpus = of_find_node_by_path("/cpus");
- if (!cpus) {
- pr_err("failed to find cpus node\n");
+ cpu0 = of_get_cpu_node(0, NULL);
+ if (!cpu0) {
+ pr_err("failed to find cpu0 node\n");
return 0;
}
- np = of_get_next_child(cpus, NULL);
- if (!np) {
- pr_err("failed to find cpus child node\n");
- of_node_put(cpus);
- return 0;
- }
-
- if (of_find_property(np, "#cooling-cells", NULL)) {
- cdev = of_cpufreq_cooling_register(np,
+ if (of_find_property(cpu0, "#cooling-cells", NULL)) {
+ cdev = of_cpufreq_cooling_register(cpu0,
cpu_present_mask);
if (IS_ERR(cdev))
pr_err("running cpufreq without cooling device: %ld\n",
PTR_ERR(cdev));
}
- of_node_put(np);
- of_node_put(cpus);
return 0;
#include <linux/smp.h>
#include <sysdev/fsl_soc.h>
+#include <asm/smp.h> /* for get_hard_smp_processor_id() in UP configs */
+
/**
* struct cpu_data - per CPU data struct
* @parent: the parent node of cpu clock
deepidle = true;
ret = mvebu_v7_cpu_suspend(deepidle);
+ cpu_pm_exit();
+
if (ret)
return ret;
- cpu_pm_exit();
-
return index;
}
.states[0] = ARM_CPUIDLE_WFI_STATE,
.states[1] = {
.enter = mvebu_v7_enter_idle,
- .exit_latency = 10,
+ .exit_latency = 100,
.power_usage = 50,
- .target_residency = 100,
+ .target_residency = 1000,
.name = "MV CPU IDLE",
.desc = "CPU power down",
},
.states[2] = {
.enter = mvebu_v7_enter_idle,
- .exit_latency = 100,
+ .exit_latency = 1000,
.power_usage = 5,
- .target_residency = 1000,
+ .target_residency = 10000,
.flags = MVEBU_V7_FLAG_DEEP_IDLE,
.name = "MV CPU DEEP IDLE",
.desc = "CPU and L2 Fabric power down",
off = 1;
}
+bool cpuidle_not_available(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev)
+{
+ return off || !initialized || !drv || !dev || !dev->enabled;
+}
+
/**
* cpuidle_play_dead - cpu off-lining
*
return -ENODEV;
}
-/**
- * cpuidle_find_deepest_state - Find deepest state meeting specific conditions.
- * @drv: cpuidle driver for the given CPU.
- * @dev: cpuidle device for the given CPU.
- * @freeze: Whether or not the state should be suitable for suspend-to-idle.
- */
-static int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
- struct cpuidle_device *dev, bool freeze)
+static int find_deepest_state(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev, bool freeze)
{
unsigned int latency_req = 0;
int i, ret = freeze ? -1 : CPUIDLE_DRIVER_STATE_START - 1;
return ret;
}
+/**
+ * cpuidle_find_deepest_state - Find the deepest available idle state.
+ * @drv: cpuidle driver for the given CPU.
+ * @dev: cpuidle device for the given CPU.
+ */
+int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev)
+{
+ return find_deepest_state(drv, dev, false);
+}
+
static void enter_freeze_proper(struct cpuidle_driver *drv,
struct cpuidle_device *dev, int index)
{
/**
* cpuidle_enter_freeze - Enter an idle state suitable for suspend-to-idle.
+ * @drv: cpuidle driver for the given CPU.
+ * @dev: cpuidle device for the given CPU.
*
* If there are states with the ->enter_freeze callback, find the deepest of
- * them and enter it with frozen tick. Otherwise, find the deepest state
- * available and enter it normally.
+ * them and enter it with frozen tick.
*/
-void cpuidle_enter_freeze(void)
+int cpuidle_enter_freeze(struct cpuidle_driver *drv, struct cpuidle_device *dev)
{
- struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices);
- struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);
int index;
/*
* that interrupts won't be enabled when it exits and allows the tick to
* be frozen safely.
*/
- index = cpuidle_find_deepest_state(drv, dev, true);
- if (index >= 0) {
- enter_freeze_proper(drv, dev, index);
- return;
- }
-
- /*
- * It is not safe to freeze the tick, find the deepest state available
- * at all and try to enter it normally.
- */
- index = cpuidle_find_deepest_state(drv, dev, false);
+ index = find_deepest_state(drv, dev, true);
if (index >= 0)
- cpuidle_enter(drv, dev, index);
- else
- arch_cpu_idle();
+ enter_freeze_proper(drv, dev, index);
- /* Interrupts are enabled again here. */
- local_irq_disable();
+ return index;
}
/**
*/
int cpuidle_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
{
- if (off || !initialized)
- return -ENODEV;
-
- if (!drv || !dev || !dev->enabled)
- return -EBUSY;
-
return cpuidle_curr_governor->select(drv, dev);
}
if (WARN_ON(timeout < 0))
return -EINVAL;
+ if (timeout == 0)
+ return fence_is_signaled(fence);
+
trace_fence_wait_start(fence);
ret = fence->ops->wait(fence, intr, timeout);
trace_fence_wait_end(fence);
unsigned seq, shared_count, i = 0;
long ret = timeout;
+ if (!timeout)
+ return reservation_object_test_signaled_rcu(obj, wait_all);
+
retry:
fence = NULL;
shared_count = 0;
int ret = 1;
if (!test_bit(FENCE_FLAG_SIGNALED_BIT, &lfence->flags)) {
- int ret;
-
fence = fence_get_rcu(lfence);
if (!fence)
return -1;
#define DRIVER_NAME "pl08xdmac"
+#define PL80X_DMA_BUSWIDTHS \
+ BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED) | \
+ BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
+ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
+ BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)
+
static struct amba_driver pl08x_amba_driver;
struct pl08x_driver_data;
pl08x->memcpy.device_pause = pl08x_pause;
pl08x->memcpy.device_resume = pl08x_resume;
pl08x->memcpy.device_terminate_all = pl08x_terminate_all;
+ pl08x->memcpy.src_addr_widths = PL80X_DMA_BUSWIDTHS;
+ pl08x->memcpy.dst_addr_widths = PL80X_DMA_BUSWIDTHS;
+ pl08x->memcpy.directions = BIT(DMA_MEM_TO_MEM);
+ pl08x->memcpy.residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;
/* Initialize slave engine */
dma_cap_set(DMA_SLAVE, pl08x->slave.cap_mask);
pl08x->slave.device_pause = pl08x_pause;
pl08x->slave.device_resume = pl08x_resume;
pl08x->slave.device_terminate_all = pl08x_terminate_all;
+ pl08x->slave.src_addr_widths = PL80X_DMA_BUSWIDTHS;
+ pl08x->slave.dst_addr_widths = PL80X_DMA_BUSWIDTHS;
+ pl08x->slave.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
+ pl08x->slave.residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;
/* Get the platform data */
pl08x->pd = dev_get_platdata(&adev->dev);
}
/*
- * atc_get_current_descriptors -
- * locate the descriptor which equal to physical address in DSCR
- * @atchan: the channel we want to start
- * @dscr_addr: physical descriptor address in DSCR
+ * atc_get_desc_by_cookie - get the descriptor of a cookie
+ * @atchan: the DMA channel
+ * @cookie: the cookie to get the descriptor for
*/
-static struct at_desc *atc_get_current_descriptors(struct at_dma_chan *atchan,
- u32 dscr_addr)
+static struct at_desc *atc_get_desc_by_cookie(struct at_dma_chan *atchan,
+ dma_cookie_t cookie)
{
- struct at_desc *desc, *_desc, *child, *desc_cur = NULL;
+ struct at_desc *desc, *_desc;
- list_for_each_entry_safe(desc, _desc, &atchan->active_list, desc_node) {
- if (desc->lli.dscr == dscr_addr) {
- desc_cur = desc;
- break;
- }
+ list_for_each_entry_safe(desc, _desc, &atchan->queue, desc_node) {
+ if (desc->txd.cookie == cookie)
+ return desc;
+ }
- list_for_each_entry(child, &desc->tx_list, desc_node) {
- if (child->lli.dscr == dscr_addr) {
- desc_cur = child;
- break;
- }
- }
+ list_for_each_entry_safe(desc, _desc, &atchan->active_list, desc_node) {
+ if (desc->txd.cookie == cookie)
+ return desc;
}
- return desc_cur;
+ return NULL;
}
-/*
- * atc_get_bytes_left -
- * Get the number of bytes residue in dma buffer,
- * @chan: the channel we want to start
+/**
+ * atc_calc_bytes_left - calculates the number of bytes left according to the
+ * value read from CTRLA.
+ *
+ * @current_len: the number of bytes left before reading CTRLA
+ * @ctrla: the value of CTRLA
+ * @desc: the descriptor containing the transfer width
+ */
+static inline int atc_calc_bytes_left(int current_len, u32 ctrla,
+ struct at_desc *desc)
+{
+ return current_len - ((ctrla & ATC_BTSIZE_MAX) << desc->tx_width);
+}
+
+/**
+ * atc_calc_bytes_left_from_reg - calculates the number of bytes left according
+ * to the current value of CTRLA.
+ *
+ * @current_len: the number of bytes left before reading CTRLA
+ * @atchan: the channel to read CTRLA for
+ * @desc: the descriptor containing the transfer width
+ */
+static inline int atc_calc_bytes_left_from_reg(int current_len,
+ struct at_dma_chan *atchan, struct at_desc *desc)
+{
+ u32 ctrla = channel_readl(atchan, CTRLA);
+
+ return atc_calc_bytes_left(current_len, ctrla, desc);
+}
+
+/**
+ * atc_get_bytes_left - get the number of bytes residue for a cookie
+ * @chan: DMA channel
+ * @cookie: transaction identifier to check status of
*/
-static int atc_get_bytes_left(struct dma_chan *chan)
+static int atc_get_bytes_left(struct dma_chan *chan, dma_cookie_t cookie)
{
struct at_dma_chan *atchan = to_at_dma_chan(chan);
- struct at_dma *atdma = to_at_dma(chan->device);
- int chan_id = atchan->chan_common.chan_id;
struct at_desc *desc_first = atc_first_active(atchan);
- struct at_desc *desc_cur;
- int ret = 0, count = 0;
+ struct at_desc *desc;
+ int ret;
+ u32 ctrla, dscr;
/*
- * Initialize necessary values in the first time.
- * remain_desc record remain desc length.
+ * If the cookie doesn't match to the currently running transfer then
+ * we can return the total length of the associated DMA transfer,
+ * because it is still queued.
*/
- if (atchan->remain_desc == 0)
- /* First descriptor embedds the transaction length */
- atchan->remain_desc = desc_first->len;
+ desc = atc_get_desc_by_cookie(atchan, cookie);
+ if (desc == NULL)
+ return -EINVAL;
+ else if (desc != desc_first)
+ return desc->total_len;
- /*
- * This happens when current descriptor transfer complete.
- * The residual buffer size should reduce current descriptor length.
- */
- if (unlikely(test_bit(ATC_IS_BTC, &atchan->status))) {
- clear_bit(ATC_IS_BTC, &atchan->status);
- desc_cur = atc_get_current_descriptors(atchan,
- channel_readl(atchan, DSCR));
- if (!desc_cur) {
- ret = -EINVAL;
- goto out;
- }
+ /* cookie matches to the currently running transfer */
+ ret = desc_first->total_len;
- count = (desc_cur->lli.ctrla & ATC_BTSIZE_MAX)
- << desc_first->tx_width;
- if (atchan->remain_desc < count) {
- ret = -EINVAL;
- goto out;
+ if (desc_first->lli.dscr) {
+ /* hardware linked list transfer */
+
+ /*
+ * Calculate the residue by removing the length of the child
+ * descriptors already transferred from the total length.
+ * To get the current child descriptor we can use the value of
+ * the channel's DSCR register and compare it against the value
+ * of the hardware linked list structure of each child
+ * descriptor.
+ */
+
+ ctrla = channel_readl(atchan, CTRLA);
+ rmb(); /* ensure CTRLA is read before DSCR */
+ dscr = channel_readl(atchan, DSCR);
+
+ /* for the first descriptor we can be more accurate */
+ if (desc_first->lli.dscr == dscr)
+ return atc_calc_bytes_left(ret, ctrla, desc_first);
+
+ ret -= desc_first->len;
+ list_for_each_entry(desc, &desc_first->tx_list, desc_node) {
+ if (desc->lli.dscr == dscr)
+ break;
+
+ ret -= desc->len;
}
- atchan->remain_desc -= count;
- ret = atchan->remain_desc;
- } else {
/*
- * Get residual bytes when current
- * descriptor transfer in progress.
+ * For the last descriptor in the chain we can calculate
+ * the remaining bytes using the channel's register.
+ * Note that the transfer width of the first and last
+ * descriptor may differ.
*/
- count = (channel_readl(atchan, CTRLA) & ATC_BTSIZE_MAX)
- << (desc_first->tx_width);
- ret = atchan->remain_desc - count;
+ if (!desc->lli.dscr)
+ ret = atc_calc_bytes_left_from_reg(ret, atchan, desc);
+ } else {
+ /* single transfer */
+ ret = atc_calc_bytes_left_from_reg(ret, atchan, desc_first);
}
- /*
- * Check fifo empty.
- */
- if (!(dma_readl(atdma, CHSR) & AT_DMA_EMPT(chan_id)))
- atc_issue_pending(chan);
-out:
return ret;
}
/* Give information to tasklet */
set_bit(ATC_IS_ERROR, &atchan->status);
}
- if (pending & AT_DMA_BTC(i))
- set_bit(ATC_IS_BTC, &atchan->status);
tasklet_schedule(&atchan->tasklet);
ret = IRQ_HANDLED;
}
desc->lli.ctrlb = ctrlb;
desc->txd.cookie = 0;
+ desc->len = xfer_count << src_width;
atc_desc_chain(&first, &prev, desc);
}
/* First descriptor of the chain embedds additional information */
first->txd.cookie = -EBUSY;
- first->len = len;
+ first->total_len = len;
+
+ /* set transfer width for the calculation of the residue */
first->tx_width = src_width;
+ prev->tx_width = src_width;
/* set end-of-link to the last link descriptor of list*/
set_desc_eol(desc);
| ATC_SRC_WIDTH(mem_width)
| len >> mem_width;
desc->lli.ctrlb = ctrlb;
+ desc->len = len;
atc_desc_chain(&first, &prev, desc);
total_len += len;
| ATC_DST_WIDTH(mem_width)
| len >> reg_width;
desc->lli.ctrlb = ctrlb;
+ desc->len = len;
atc_desc_chain(&first, &prev, desc);
total_len += len;
/* First descriptor of the chain embedds additional information */
first->txd.cookie = -EBUSY;
- first->len = total_len;
+ first->total_len = total_len;
+
+ /* set transfer width for the calculation of the residue */
first->tx_width = reg_width;
+ prev->tx_width = reg_width;
/* first link descriptor of list is responsible of flags */
first->txd.flags = flags; /* client is in control of this ack */
| ATC_FC_MEM2PER
| ATC_SIF(atchan->mem_if)
| ATC_DIF(atchan->per_if);
+ desc->len = period_len;
break;
case DMA_DEV_TO_MEM:
| ATC_FC_PER2MEM
| ATC_SIF(atchan->per_if)
| ATC_DIF(atchan->mem_if);
+ desc->len = period_len;
break;
default:
/* First descriptor of the chain embedds additional information */
first->txd.cookie = -EBUSY;
- first->len = buf_len;
+ first->total_len = buf_len;
first->tx_width = reg_width;
return &first->txd;
spin_lock_irqsave(&atchan->lock, flags);
/* Get number of bytes left in the active transactions */
- bytes = atc_get_bytes_left(chan);
+ bytes = atc_get_bytes_left(chan, cookie);
spin_unlock_irqrestore(&atchan->lock, flags);
spin_lock_irqsave(&atchan->lock, flags);
atchan->descs_allocated = i;
- atchan->remain_desc = 0;
list_splice(&tmp_list, &atchan->free_list);
dma_cookie_init(chan);
spin_unlock_irqrestore(&atchan->lock, flags);
list_splice_init(&atchan->free_list, &list);
atchan->descs_allocated = 0;
atchan->status = 0;
- atchan->remain_desc = 0;
dev_vdbg(chan2dev(chan), "free_chan_resources: done\n");
}
* @at_lli: hardware lli structure
* @txd: support for the async_tx api
* @desc_node: node on the channed descriptors list
- * @len: total transaction bytecount
+ * @len: descriptor byte count
* @tx_width: transfer width
+ * @total_len: total transaction byte count
*/
struct at_desc {
/* FIRST values the hardware uses */
struct list_head desc_node;
size_t len;
u32 tx_width;
+ size_t total_len;
};
static inline struct at_desc *
enum atc_status {
ATC_IS_ERROR = 0,
ATC_IS_PAUSED = 1,
- ATC_IS_BTC = 2,
ATC_IS_CYCLIC = 24,
};
* @save_cfg: configuration register that is saved on suspend/resume cycle
* @save_dscr: for cyclic operations, preserve next descriptor address in
* the cyclic list on suspend/resume cycle
- * @remain_desc: to save remain desc length
* @dma_sconfig: configuration for slave transfers, passed via
* .device_config
* @lock: serializes enqueue/dequeue operations to descriptors lists
struct tasklet_struct tasklet;
u32 save_cfg;
u32 save_dscr;
- u32 remain_desc;
struct dma_slave_config dma_sconfig;
spinlock_t lock;
struct at_xdmac_desc *first = NULL, *prev = NULL;
unsigned int periods = buf_len / period_len;
int i;
- u32 cfg;
dev_dbg(chan2dev(chan), "%s: buf_addr=%pad, buf_len=%zd, period_len=%zd, dir=%s, flags=0x%lx\n",
__func__, &buf_addr, buf_len, period_len,
if (direction == DMA_DEV_TO_MEM) {
desc->lld.mbr_sa = atchan->per_src_addr;
desc->lld.mbr_da = buf_addr + i * period_len;
- cfg = atchan->cfg[AT_XDMAC_DEV_TO_MEM_CFG];
+ desc->lld.mbr_cfg = atchan->cfg[AT_XDMAC_DEV_TO_MEM_CFG];
} else {
desc->lld.mbr_sa = buf_addr + i * period_len;
desc->lld.mbr_da = atchan->per_dst_addr;
- cfg = atchan->cfg[AT_XDMAC_MEM_TO_DEV_CFG];
+ desc->lld.mbr_cfg = atchan->cfg[AT_XDMAC_MEM_TO_DEV_CFG];
}
desc->lld.mbr_ubc = AT_XDMAC_MBR_UBC_NDV1
| AT_XDMAC_MBR_UBC_NDEN
| AT_XDMAC_MBR_UBC_NSEN
| AT_XDMAC_MBR_UBC_NDE
- | period_len >> at_xdmac_get_dwidth(cfg);
+ | period_len >> at_xdmac_get_dwidth(desc->lld.mbr_cfg);
dev_dbg(chan2dev(chan),
"%s: lld: mbr_sa=%pad, mbr_da=%pad, mbr_ubc=0x%08x\n",
* c->desc is NULL and exit.)
*/
if (c->desc) {
+ bcm2835_dma_desc_free(&c->desc->vd);
c->desc = NULL;
bcm2835_dma_abort(c->chan_base);
kfree(container_of(vdesc, struct jz4740_dma_desc, vdesc));
}
+#define JZ4740_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
+ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | BIT(DMA_SLAVE_BUSWIDTH_4_BYTES))
+
static int jz4740_dma_probe(struct platform_device *pdev)
{
struct jz4740_dmaengine_chan *chan;
dd->device_prep_dma_cyclic = jz4740_dma_prep_dma_cyclic;
dd->device_config = jz4740_dma_slave_config;
dd->device_terminate_all = jz4740_dma_terminate_all;
+ dd->src_addr_widths = JZ4740_DMA_BUSWIDTHS;
+ dd->dst_addr_widths = JZ4740_DMA_BUSWIDTHS;
+ dd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
+ dd->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
dd->dev = &pdev->dev;
INIT_LIST_HEAD(&dd->channels);
dev_vdbg(dw->dma.dev, "%s: status=0x%x\n", __func__, status);
/* Check if we have any interrupt from the DMAC */
- if (!status)
+ if (!status || !dw->in_use)
return IRQ_NONE;
/*
#include "internal.h"
+#define DRV_NAME "dw_dmac"
+
static struct dma_chan *dw_dma_of_xlate(struct of_phandle_args *dma_spec,
struct of_dma *ofdma)
{
.remove = dw_remove,
.shutdown = dw_shutdown,
.driver = {
- .name = "dw_dmac",
+ .name = DRV_NAME,
.pm = &dw_dev_pm_ops,
.of_match_table = of_match_ptr(dw_dma_of_id_table),
.acpi_match_table = ACPI_PTR(dw_dma_acpi_id_table),
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller platform driver");
+MODULE_ALIAS("platform:" DRV_NAME);
*/
if (echan->edesc) {
int cyclic = echan->edesc->cyclic;
+
+ /*
+ * free the running request descriptor
+ * since it is not in any of the vdesc lists
+ */
+ edma_desc_free(&echan->edesc->vdesc);
+
echan->edesc = NULL;
edma_stop(echan->ch_num);
/* Move the cyclic channel back to default queue */
dev_err(sdma->dev, "Timeout waiting for CH0 ready\n");
}
+ /* Set bits of CONFIG register with dynamic context switching */
+ if (readl(sdma->regs + SDMA_H_CONFIG) == 0)
+ writel_relaxed(SDMA_H_CONFIG_CSM, sdma->regs + SDMA_H_CONFIG);
+
return ret ? 0 : -ETIMEDOUT;
}
writel_relaxed(ccb_phys, sdma->regs + SDMA_H_C0PTR);
- /* Set bits of CONFIG register with given context switching mode */
- writel_relaxed(SDMA_H_CONFIG_CSM, sdma->regs + SDMA_H_CONFIG);
-
/* Initializes channel's priorities */
sdma_set_channel_priority(&sdma->channel[0], 7);
switch (pdev->device) {
case PCI_DEVICE_ID_INTEL_IOAT_BWD2:
case PCI_DEVICE_ID_INTEL_IOAT_BWD3:
+ case PCI_DEVICE_ID_INTEL_IOAT_BDXDE0:
+ case PCI_DEVICE_ID_INTEL_IOAT_BDXDE1:
+ case PCI_DEVICE_ID_INTEL_IOAT_BDXDE2:
+ case PCI_DEVICE_ID_INTEL_IOAT_BDXDE3:
return true;
default:
return false;
while (dint) {
i = __ffs(dint);
+ /* only handle interrupts belonging to pdma driver*/
+ if (i >= pdev->dma_channels)
+ break;
dint &= (dint - 1);
phy = &pdev->phy[i];
ret = mmp_pdma_chan_handler(irq, phy);
struct resource *iores;
int i, ret, irq = 0;
int dma_channels = 0, irq_num = 0;
+ const enum dma_slave_buswidth widths =
+ DMA_SLAVE_BUSWIDTH_1_BYTE | DMA_SLAVE_BUSWIDTH_2_BYTES |
+ DMA_SLAVE_BUSWIDTH_4_BYTES;
pdev = devm_kzalloc(&op->dev, sizeof(*pdev), GFP_KERNEL);
if (!pdev)
pdev->device.device_config = mmp_pdma_config;
pdev->device.device_terminate_all = mmp_pdma_terminate_all;
pdev->device.copy_align = PDMA_ALIGNMENT;
+ pdev->device.src_addr_widths = widths;
+ pdev->device.dst_addr_widths = widths;
+ pdev->device.directions = BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM);
+ pdev->device.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
if (pdev->dev->coherent_dma_mask)
dma_set_mask(pdev->dev, pdev->dev->coherent_dma_mask);
struct tasklet_struct tasklet;
struct mmp_tdma_desc *desc_arr;
- phys_addr_t desc_arr_phys;
+ dma_addr_t desc_arr_phys;
int desc_num;
enum dma_transfer_direction dir;
dma_addr_t dev_addr;
static int mmp_tdma_disable_chan(struct dma_chan *chan)
{
struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan);
+ u32 tdcr;
- writel(readl(tdmac->reg_base + TDCR) & ~TDCR_CHANEN,
- tdmac->reg_base + TDCR);
+ tdcr = readl(tdmac->reg_base + TDCR);
+ tdcr |= TDCR_ABR;
+ tdcr &= ~TDCR_CHANEN;
+ writel(tdcr, tdmac->reg_base + TDCR);
tdmac->status = DMA_COMPLETE;
return -EAGAIN;
}
+static size_t mmp_tdma_get_pos(struct mmp_tdma_chan *tdmac)
+{
+ size_t reg;
+
+ if (tdmac->idx == 0) {
+ reg = __raw_readl(tdmac->reg_base + TDSAR);
+ reg -= tdmac->desc_arr[0].src_addr;
+ } else if (tdmac->idx == 1) {
+ reg = __raw_readl(tdmac->reg_base + TDDAR);
+ reg -= tdmac->desc_arr[0].dst_addr;
+ } else
+ return -EINVAL;
+
+ return reg;
+}
+
static irqreturn_t mmp_tdma_chan_handler(int irq, void *dev_id)
{
struct mmp_tdma_chan *tdmac = dev_id;
if (mmp_tdma_clear_chan_irq(tdmac) == 0) {
- tdmac->pos = (tdmac->pos + tdmac->period_len) % tdmac->buf_len;
tasklet_schedule(&tdmac->tasklet);
return IRQ_HANDLED;
} else
int size = tdmac->desc_num * sizeof(struct mmp_tdma_desc);
gpool = tdmac->pool;
- if (tdmac->desc_arr)
+ if (gpool && tdmac->desc_arr)
gen_pool_free(gpool, (unsigned long)tdmac->desc_arr,
size);
tdmac->desc_arr = NULL;
{
struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan);
+ tdmac->pos = mmp_tdma_get_pos(tdmac);
dma_set_tx_state(txstate, chan->completed_cookie, chan->cookie,
tdmac->buf_len - tdmac->pos);
int i, ret;
int irq = 0, irq_num = 0;
int chan_num = TDMA_CHANNEL_NUM;
- struct gen_pool *pool;
+ struct gen_pool *pool = NULL;
of_id = of_match_device(mmp_tdma_dt_ids, &pdev->dev);
if (of_id)
spin_lock_irqsave(&ch->vc.lock, flags);
- if (ch->desc)
+ if (ch->desc) {
+ moxart_dma_desc_free(&ch->desc->vd);
ch->desc = NULL;
+ }
ctrl = readl(ch->base + REG_OFF_CTRL);
ctrl &= ~(APB_DMA_ENABLE | APB_DMA_FIN_INT_EN | APB_DMA_ERR_INT_EN);
* c->desc is NULL and exit.)
*/
if (c->desc) {
+ omap_dma_desc_free(&c->desc->vd);
c->desc = NULL;
/* Avoid stopping the dma twice */
if (!c->paused)
[BAM_P_IRQ_STTS] = { 0x1010, 0x1000, 0x00, 0x00 },
[BAM_P_IRQ_CLR] = { 0x1014, 0x1000, 0x00, 0x00 },
[BAM_P_IRQ_EN] = { 0x1018, 0x1000, 0x00, 0x00 },
- [BAM_P_EVNT_DEST_ADDR] = { 0x102C, 0x00, 0x1000, 0x00 },
- [BAM_P_EVNT_REG] = { 0x1018, 0x00, 0x1000, 0x00 },
- [BAM_P_SW_OFSTS] = { 0x1000, 0x00, 0x1000, 0x00 },
+ [BAM_P_EVNT_DEST_ADDR] = { 0x182C, 0x00, 0x1000, 0x00 },
+ [BAM_P_EVNT_REG] = { 0x1818, 0x00, 0x1000, 0x00 },
+ [BAM_P_SW_OFSTS] = { 0x1800, 0x00, 0x1000, 0x00 },
[BAM_P_DATA_FIFO_ADDR] = { 0x1824, 0x00, 0x1000, 0x00 },
[BAM_P_DESC_FIFO_ADDR] = { 0x181C, 0x00, 0x1000, 0x00 },
[BAM_P_EVNT_GEN_TRSHLD] = { 0x1828, 0x00, 0x1000, 0x00 },
dma_cap_set(DMA_SLAVE, bdev->common.cap_mask);
/* initialize dmaengine apis */
+ bdev->common.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
+ bdev->common.residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;
+ bdev->common.src_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ bdev->common.dst_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES;
bdev->common.device_alloc_chan_resources = bam_alloc_chan;
bdev->common.device_free_chan_resources = bam_free_chan;
bdev->common.device_prep_slave_sg = bam_prep_slave_sg;
}
}
-static void sh_dmae_shutdown(struct platform_device *pdev)
-{
- struct sh_dmae_device *shdev = platform_get_drvdata(pdev);
- sh_dmae_ctl_stop(shdev);
-}
-
#ifdef CONFIG_PM
static int sh_dmae_runtime_suspend(struct device *dev)
{
+ struct sh_dmae_device *shdev = dev_get_drvdata(dev);
+
+ sh_dmae_ctl_stop(shdev);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int sh_dmae_suspend(struct device *dev)
{
+ struct sh_dmae_device *shdev = dev_get_drvdata(dev);
+
+ sh_dmae_ctl_stop(shdev);
return 0;
}
}
static struct platform_driver sh_dmae_driver = {
- .driver = {
+ .driver = {
.pm = &sh_dmae_pm,
.name = SH_DMAE_DRV_NAME,
.of_match_table = sh_dmae_of_match,
},
.remove = sh_dmae_remove,
- .shutdown = sh_dmae_shutdown,
};
static int __init sh_dmae_init(void)
* We have to be cautious here. We have seen BIOSes with DMI pointers
* pointing to completely the wrong place for example
*/
-static void dmi_table(u8 *buf, int len, int num,
+static void dmi_table(u8 *buf, u32 len, int num,
void (*decode)(const struct dmi_header *, void *),
void *private_data)
{
int i = 0;
/*
- * Stop when we see all the items the table claimed to have
- * OR we run off the end of the table (also happens)
+ * Stop when we have seen all the items the table claimed to have
+ * (SMBIOS < 3.0 only) OR we reach an end-of-table marker OR we run
+ * off the end of the table (should never happen but sometimes does
+ * on bogus implementations.)
*/
- while ((i < num) && (data - buf + sizeof(struct dmi_header)) <= len) {
+ while ((!num || i < num) &&
+ (data - buf + sizeof(struct dmi_header)) <= len) {
const struct dmi_header *dm = (const struct dmi_header *)data;
- /*
- * 7.45 End-of-Table (Type 127) [SMBIOS reference spec v3.0.0]
- */
- if (dm->type == DMI_ENTRY_END_OF_TABLE)
- break;
-
/*
* We want to know the total length (formatted area and
* strings) before decoding to make sure we won't run off the
data++;
if (data - buf < len - 1)
decode(dm, private_data);
+
+ /*
+ * 7.45 End-of-Table (Type 127) [SMBIOS reference spec v3.0.0]
+ */
+ if (dm->type == DMI_ENTRY_END_OF_TABLE)
+ break;
+
data += 2;
i++;
}
}
static phys_addr_t dmi_base;
-static u16 dmi_len;
+static u32 dmi_len;
static u16 dmi_num;
static int __init dmi_walk_early(void (*decode)(const struct dmi_header *,
if (memcmp(buf, "_SM3_", 5) == 0 &&
buf[6] < 32 && dmi_checksum(buf, buf[6])) {
dmi_ver = get_unaligned_be16(buf + 7);
+ dmi_num = 0; /* No longer specified */
dmi_len = get_unaligned_le32(buf + 12);
dmi_base = get_unaligned_le64(buf + 16);
- /*
- * The 64-bit SMBIOS 3.0 entry point no longer has a field
- * containing the number of structures present in the table.
- * Instead, it defines the table size as a maximum size, and
- * relies on the end-of-table structure type (#127) to be used
- * to signal the end of the table.
- * So let's define dmi_num as an upper bound as well: each
- * structure has a 4 byte header, so dmi_len / 4 is an upper
- * bound for the number of structures in the table.
- */
- dmi_num = dmi_len / 4;
-
if (dmi_walk_early(dmi_decode) == 0) {
pr_info("SMBIOS %d.%d present.\n",
dmi_ver >> 8, dmi_ver & 0xFF);
start = desc->phys_addr;
end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT);
- if ((start + size) > end || (start + size) > max)
- continue;
-
- if (end - size > max)
+ if (end > max)
end = max;
+ if ((start + size) > end)
+ continue;
+
if (round_down(end - size, align) < start)
continue;
.xlate = irq_domain_xlate_twocell,
};
-static struct of_device_id mpc8xxx_gpio_ids[] __initdata = {
+static struct of_device_id mpc8xxx_gpio_ids[] = {
{ .compatible = "fsl,mpc8349-gpio", },
{ .compatible = "fsl,mpc8572-gpio", },
{ .compatible = "fsl,mpc8610-gpio", },
ret = of_property_read_u32_index(np, "gpio,syscon-dev", 2,
&priv->dir_reg_offset);
if (ret)
- dev_err(dev, "can't read the dir register offset!\n");
+ dev_dbg(dev, "can't read the dir register offset!\n");
priv->dir_reg_offset <<= 3;
}
struct gpio_chip gpio_chip;
};
+#define to_tgd(gc) container_of(gc, struct tps65912_gpio_data, gpio_chip)
+
static int tps65912_gpio_get(struct gpio_chip *gc, unsigned offset)
{
- struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio);
+ struct tps65912_gpio_data *tps65912_gpio = to_tgd(gc);
+ struct tps65912 *tps65912 = tps65912_gpio->tps65912;
int val;
val = tps65912_reg_read(tps65912, TPS65912_GPIO1 + offset);
static void tps65912_gpio_set(struct gpio_chip *gc, unsigned offset,
int value)
{
- struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio);
+ struct tps65912_gpio_data *tps65912_gpio = to_tgd(gc);
+ struct tps65912 *tps65912 = tps65912_gpio->tps65912;
if (value)
tps65912_set_bits(tps65912, TPS65912_GPIO1 + offset,
static int tps65912_gpio_output(struct gpio_chip *gc, unsigned offset,
int value)
{
- struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio);
+ struct tps65912_gpio_data *tps65912_gpio = to_tgd(gc);
+ struct tps65912 *tps65912 = tps65912_gpio->tps65912;
/* Set the initial value */
tps65912_gpio_set(gc, offset, value);
static int tps65912_gpio_input(struct gpio_chip *gc, unsigned offset)
{
- struct tps65912 *tps65912 = container_of(gc, struct tps65912, gpio);
+ struct tps65912_gpio_data *tps65912_gpio = to_tgd(gc);
+ struct tps65912 *tps65912 = tps65912_gpio->tps65912;
return tps65912_clear_bits(tps65912, TPS65912_GPIO1 + offset,
GPIO_CFG_MASK);
if (!handler)
return AE_BAD_PARAMETER;
+ pin = acpi_gpiochip_pin_to_gpio_offset(chip, pin);
+ if (pin < 0)
+ return AE_BAD_PARAMETER;
+
desc = gpiochip_request_own_desc(chip, pin, "ACPI:Event");
if (IS_ERR(desc)) {
dev_err(chip->dev, "Failed to request GPIO\n");
struct gpio_desc *desc;
bool found;
+ pin = acpi_gpiochip_pin_to_gpio_offset(chip, pin);
+ if (pin < 0) {
+ status = AE_BAD_PARAMETER;
+ goto out;
+ }
+
mutex_lock(&achip->conn_lock);
found = false;
ret = gc->of_xlate(gc, &gg_data->gpiospec, gg_data->flags);
if (ret < 0) {
- /* We've found the gpio chip, but the translation failed.
- * Return true to stop looking and return the translation
- * error via out_gpio
+ /* We've found a gpio chip, but the translation failed.
+ * Store translation error in out_gpio.
+ * Return false to keep looking, as more than one gpio chip
+ * could be registered per of-node.
*/
gg_data->out_gpio = ERR_PTR(ret);
- return true;
+ return false;
}
gg_data->out_gpio = gpiochip_get_desc(gc, ret);
return KFD_MQD_TYPE_CP;
}
-static inline unsigned int get_first_pipe(struct device_queue_manager *dqm)
+unsigned int get_first_pipe(struct device_queue_manager *dqm)
{
- BUG_ON(!dqm);
+ BUG_ON(!dqm || !dqm->dev);
return dqm->dev->shared_resources.first_compute_pipe;
}
+unsigned int get_pipes_num(struct device_queue_manager *dqm)
+{
+ BUG_ON(!dqm || !dqm->dev);
+ return dqm->dev->shared_resources.compute_pipe_count;
+}
+
static inline unsigned int get_pipes_num_cpsch(void)
{
return PIPE_PER_ME_CP_SCHEDULING;
pr_debug(" sdma queue id: %d\n", q->properties.sdma_queue_id);
pr_debug(" sdma engine id: %d\n", q->properties.sdma_engine_id);
+ init_sdma_vm(dqm, q, qpd);
retval = mqd->init_mqd(mqd, &q->mqd, &q->mqd_mem_obj,
&q->gart_mqd_addr, &q->properties);
if (retval != 0) {
return retval;
}
- init_sdma_vm(dqm, q, qpd);
+ retval = mqd->load_mqd(mqd, q->mqd, 0,
+ 0, NULL);
+ if (retval != 0) {
+ deallocate_sdma_queue(dqm, q->sdma_id);
+ mqd->uninit_mqd(mqd, q->mqd, q->mqd_mem_obj);
+ return retval;
+ }
+
return 0;
}
struct qcm_process_device *qpd);
int init_pipelines(struct device_queue_manager *dqm,
unsigned int pipes_num, unsigned int first_pipe);
+unsigned int get_first_pipe(struct device_queue_manager *dqm);
+unsigned int get_pipes_num(struct device_queue_manager *dqm);
extern inline unsigned int get_sh_mem_bases_32(struct kfd_process_device *pdd)
{
return (pdd->lds_base >> 60) & 0x0E;
}
-extern inline unsigned int get_pipes_num(struct device_queue_manager *dqm)
-{
- BUG_ON(!dqm || !dqm->dev);
- return dqm->dev->shared_resources.compute_pipe_count;
-}
-
#endif /* KFD_DEVICE_QUEUE_MANAGER_H_ */
static int initialize_cpsch_cik(struct device_queue_manager *dqm)
{
- return init_pipelines(dqm, get_pipes_num(dqm), 0);
+ return init_pipelines(dqm, get_pipes_num(dqm), get_first_pipe(dqm));
}
BUG_ON(!kq || !dev);
BUG_ON(type != KFD_QUEUE_TYPE_DIQ && type != KFD_QUEUE_TYPE_HIQ);
- pr_debug("kfd: In func %s initializing queue type %d size %d\n",
+ pr_debug("amdkfd: In func %s initializing queue type %d size %d\n",
__func__, KFD_QUEUE_TYPE_HIQ, queue_size);
nop.opcode = IT_NOP;
prop.doorbell_ptr = kfd_get_kernel_doorbell(dev, &prop.doorbell_off);
- if (prop.doorbell_ptr == NULL)
+ if (prop.doorbell_ptr == NULL) {
+ pr_err("amdkfd: error init doorbell");
goto err_get_kernel_doorbell;
+ }
retval = kfd_gtt_sa_allocate(dev, queue_size, &kq->pq);
- if (retval != 0)
+ if (retval != 0) {
+ pr_err("amdkfd: error init pq queues size (%d)\n", queue_size);
goto err_pq_allocate_vidmem;
+ }
kq->pq_kernel_addr = kq->pq->cpu_ptr;
kq->pq_gpu_addr = kq->pq->gpu_addr;
err_eop_allocate_vidmem:
kfd_gtt_sa_free(dev, kq->pq);
err_pq_allocate_vidmem:
- pr_err("kfd: error init pq\n");
kfd_release_kernel_doorbell(dev, prop.doorbell_ptr);
err_get_kernel_doorbell:
- pr_err("kfd: error init doorbell");
return false;
}
else if (kq->queue->properties.type == KFD_QUEUE_TYPE_DIQ)
kfd_gtt_sa_free(kq->dev, kq->fence_mem_obj);
+ kq->mqd->uninit_mqd(kq->mqd, kq->queue->mqd, kq->queue->mqd_mem_obj);
+
kfd_gtt_sa_free(kq->dev, kq->rptr_mem);
kfd_gtt_sa_free(kq->dev, kq->wptr_mem);
kq->ops_asic_specific.uninitialize(kq);
queue_address = (unsigned int *)kq->pq_kernel_addr;
queue_size_dwords = kq->queue->properties.queue_size / sizeof(uint32_t);
- pr_debug("kfd: In func %s\nrptr: %d\nwptr: %d\nqueue_address 0x%p\n",
+ pr_debug("amdkfd: In func %s\nrptr: %d\nwptr: %d\nqueue_address 0x%p\n",
__func__, rptr, wptr, queue_address);
available_size = (rptr - 1 - wptr + queue_size_dwords) %
}
if (kq->ops.initialize(kq, dev, type, KFD_KERNEL_QUEUE_SIZE) == false) {
- pr_err("kfd: failed to init kernel queue\n");
+ pr_err("amdkfd: failed to init kernel queue\n");
kfree(kq);
return NULL;
}
BUG_ON(!dev);
- pr_err("kfd: starting kernel queue test\n");
+ pr_err("amdkfd: starting kernel queue test\n");
kq = kernel_queue_init(dev, KFD_QUEUE_TYPE_HIQ);
BUG_ON(!kq);
buffer[i] = kq->nop_packet;
kq->ops.submit_packet(kq);
- pr_err("kfd: ending kernel queue test\n");
+ pr_err("amdkfd: ending kernel queue test\n");
}
(adj->crtc_hdisplay - 1) |
((adj->crtc_vdisplay - 1) << 16));
- cfg = ATMEL_HLCDC_CLKPOL;
+ cfg = 0;
prate = clk_get_rate(crtc->dc->hlcdc->sys_clk);
mode_rate = mode->crtc_clock * 1000;
pm_runtime_enable(dev->dev);
- pm_runtime_put_sync(dev->dev);
-
ret = atmel_hlcdc_dc_modeset_init(dev);
if (ret < 0) {
dev_err(dev->dev, "failed to initialize mode setting\n");
/* Disable the layer */
regmap_write(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_CHDR,
- ATMEL_HLCDC_LAYER_RST);
+ ATMEL_HLCDC_LAYER_RST | ATMEL_HLCDC_LAYER_A2Q |
+ ATMEL_HLCDC_LAYER_UPDATE);
/* Clear all pending interrupts */
regmap_read(regmap, desc->regs_offset + ATMEL_HLCDC_LAYER_ISR, &isr);
#include "drm_crtc_internal.h"
#include "drm_internal.h"
-static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev,
- struct drm_mode_fb_cmd2 *r,
- struct drm_file *file_priv);
+static struct drm_framebuffer *
+internal_framebuffer_create(struct drm_device *dev,
+ struct drm_mode_fb_cmd2 *r,
+ struct drm_file *file_priv);
/* Avoid boilerplate. I'm tired of typing. */
#define DRM_ENUM_NAME_FN(fnname, list) \
}
EXPORT_SYMBOL(drm_framebuffer_reference);
-static void drm_framebuffer_free_bug(struct kref *kref)
-{
- BUG();
-}
-
-static void __drm_framebuffer_unreference(struct drm_framebuffer *fb)
-{
- DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount));
- kref_put(&fb->refcount, drm_framebuffer_free_bug);
-}
-
/**
* drm_framebuffer_unregister_private - unregister a private fb from the lookup idr
* @fb: fb to unregister
return;
}
/* disconnect the plane from the fb and crtc: */
- __drm_framebuffer_unreference(plane->old_fb);
+ drm_framebuffer_unreference(plane->old_fb);
plane->old_fb = NULL;
plane->fb = NULL;
plane->crtc = NULL;
DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id);
mutex_lock(&dev->mode_config.mutex);
- drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
connector = drm_connector_find(dev, out_resp->connector_id);
if (!connector) {
out_resp->mm_height = connector->display_info.height_mm;
out_resp->subpixel = connector->display_info.subpixel_order;
out_resp->connection = connector->status;
+
+ drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
encoder = drm_connector_get_encoder(connector);
if (encoder)
out_resp->encoder_id = encoder->base.id;
*/
if (req->flags & DRM_MODE_CURSOR_BO) {
if (req->handle) {
- fb = add_framebuffer_internal(dev, &fbreq, file_priv);
+ fb = internal_framebuffer_create(dev, &fbreq, file_priv);
if (IS_ERR(fb)) {
DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n");
return PTR_ERR(fb);
}
-
- drm_framebuffer_reference(fb);
} else {
fb = NULL;
}
return 0;
}
-static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev,
- struct drm_mode_fb_cmd2 *r,
- struct drm_file *file_priv)
+static struct drm_framebuffer *
+internal_framebuffer_create(struct drm_device *dev,
+ struct drm_mode_fb_cmd2 *r,
+ struct drm_file *file_priv)
{
struct drm_mode_config *config = &dev->mode_config;
struct drm_framebuffer *fb;
return fb;
}
- mutex_lock(&file_priv->fbs_lock);
- r->fb_id = fb->base.id;
- list_add(&fb->filp_head, &file_priv->fbs);
- DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id);
- mutex_unlock(&file_priv->fbs_lock);
-
return fb;
}
int drm_mode_addfb2(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
+ struct drm_mode_fb_cmd2 *r = data;
struct drm_framebuffer *fb;
if (!drm_core_check_feature(dev, DRIVER_MODESET))
return -EINVAL;
- fb = add_framebuffer_internal(dev, data, file_priv);
+ fb = internal_framebuffer_create(dev, r, file_priv);
if (IS_ERR(fb))
return PTR_ERR(fb);
+ /* Transfer ownership to the filp for reaping on close */
+
+ DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id);
+ mutex_lock(&file_priv->fbs_lock);
+ r->fb_id = fb->base.id;
+ list_add(&fb->filp_head, &file_priv->fbs);
+ mutex_unlock(&file_priv->fbs_lock);
+
return 0;
}
struct drm_dp_sideband_msg_tx *txmsg)
{
bool ret;
- mutex_lock(&mgr->qlock);
+
+ /*
+ * All updates to txmsg->state are protected by mgr->qlock, and the two
+ * cases we check here are terminal states. For those the barriers
+ * provided by the wake_up/wait_event pair are enough.
+ */
ret = (txmsg->state == DRM_DP_SIDEBAND_TX_RX ||
txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT);
- mutex_unlock(&mgr->qlock);
return ret;
}
return 0;
}
-/* must be called holding qlock */
static void process_single_down_tx_qlock(struct drm_dp_mst_topology_mgr *mgr)
{
struct drm_dp_sideband_msg_tx *txmsg;
int ret;
+ WARN_ON(!mutex_is_locked(&mgr->qlock));
+
/* construct a chunk from the first msg in the tx_msg queue */
if (list_empty(&mgr->tx_msg_downq)) {
mgr->tx_down_in_progress = false;
drm_mode_connector_update_edid_property(connector, edid);
ret = drm_add_edid_modes(connector, edid);
+ drm_edid_to_eld(connector, edid);
kfree(edid);
return ret;
*/
static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm,
- unsigned long size,
+ u64 size,
unsigned alignment,
unsigned long color,
enum drm_mm_search_flags flags);
static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm,
- unsigned long size,
+ u64 size,
unsigned alignment,
unsigned long color,
- unsigned long start,
- unsigned long end,
+ u64 start,
+ u64 end,
enum drm_mm_search_flags flags);
static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
struct drm_mm_node *node,
- unsigned long size, unsigned alignment,
+ u64 size, unsigned alignment,
unsigned long color,
enum drm_mm_allocator_flags flags)
{
struct drm_mm *mm = hole_node->mm;
- unsigned long hole_start = drm_mm_hole_node_start(hole_node);
- unsigned long hole_end = drm_mm_hole_node_end(hole_node);
- unsigned long adj_start = hole_start;
- unsigned long adj_end = hole_end;
+ u64 hole_start = drm_mm_hole_node_start(hole_node);
+ u64 hole_end = drm_mm_hole_node_end(hole_node);
+ u64 adj_start = hole_start;
+ u64 adj_end = hole_end;
BUG_ON(node->allocated);
adj_start = adj_end - size;
if (alignment) {
- unsigned tmp = adj_start % alignment;
- if (tmp) {
+ u64 tmp = adj_start;
+ unsigned rem;
+
+ rem = do_div(tmp, alignment);
+ if (rem) {
if (flags & DRM_MM_CREATE_TOP)
- adj_start -= tmp;
+ adj_start -= rem;
else
- adj_start += alignment - tmp;
+ adj_start += alignment - rem;
}
}
int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node)
{
struct drm_mm_node *hole;
- unsigned long end = node->start + node->size;
- unsigned long hole_start;
- unsigned long hole_end;
+ u64 end = node->start + node->size;
+ u64 hole_start;
+ u64 hole_end;
BUG_ON(node == NULL);
* 0 on success, -ENOSPC if there's no suitable hole.
*/
int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node,
- unsigned long size, unsigned alignment,
+ u64 size, unsigned alignment,
unsigned long color,
enum drm_mm_search_flags sflags,
enum drm_mm_allocator_flags aflags)
static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
struct drm_mm_node *node,
- unsigned long size, unsigned alignment,
+ u64 size, unsigned alignment,
unsigned long color,
- unsigned long start, unsigned long end,
+ u64 start, u64 end,
enum drm_mm_allocator_flags flags)
{
struct drm_mm *mm = hole_node->mm;
- unsigned long hole_start = drm_mm_hole_node_start(hole_node);
- unsigned long hole_end = drm_mm_hole_node_end(hole_node);
- unsigned long adj_start = hole_start;
- unsigned long adj_end = hole_end;
+ u64 hole_start = drm_mm_hole_node_start(hole_node);
+ u64 hole_end = drm_mm_hole_node_end(hole_node);
+ u64 adj_start = hole_start;
+ u64 adj_end = hole_end;
BUG_ON(!hole_node->hole_follows || node->allocated);
mm->color_adjust(hole_node, color, &adj_start, &adj_end);
if (alignment) {
- unsigned tmp = adj_start % alignment;
- if (tmp) {
+ u64 tmp = adj_start;
+ unsigned rem;
+
+ rem = do_div(tmp, alignment);
+ if (rem) {
if (flags & DRM_MM_CREATE_TOP)
- adj_start -= tmp;
+ adj_start -= rem;
else
- adj_start += alignment - tmp;
+ adj_start += alignment - rem;
}
}
* 0 on success, -ENOSPC if there's no suitable hole.
*/
int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *node,
- unsigned long size, unsigned alignment,
+ u64 size, unsigned alignment,
unsigned long color,
- unsigned long start, unsigned long end,
+ u64 start, u64 end,
enum drm_mm_search_flags sflags,
enum drm_mm_allocator_flags aflags)
{
}
EXPORT_SYMBOL(drm_mm_remove_node);
-static int check_free_hole(unsigned long start, unsigned long end,
- unsigned long size, unsigned alignment)
+static int check_free_hole(u64 start, u64 end, u64 size, unsigned alignment)
{
if (end - start < size)
return 0;
if (alignment) {
- unsigned tmp = start % alignment;
- if (tmp)
- start += alignment - tmp;
+ u64 tmp = start;
+ unsigned rem;
+
+ rem = do_div(tmp, alignment);
+ if (rem)
+ start += alignment - rem;
}
return end >= start + size;
}
static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm,
- unsigned long size,
+ u64 size,
unsigned alignment,
unsigned long color,
enum drm_mm_search_flags flags)
{
struct drm_mm_node *entry;
struct drm_mm_node *best;
- unsigned long adj_start;
- unsigned long adj_end;
- unsigned long best_size;
+ u64 adj_start;
+ u64 adj_end;
+ u64 best_size;
BUG_ON(mm->scanned_blocks);
__drm_mm_for_each_hole(entry, mm, adj_start, adj_end,
flags & DRM_MM_SEARCH_BELOW) {
- unsigned long hole_size = adj_end - adj_start;
+ u64 hole_size = adj_end - adj_start;
if (mm->color_adjust) {
mm->color_adjust(entry, color, &adj_start, &adj_end);
}
static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm,
- unsigned long size,
+ u64 size,
unsigned alignment,
unsigned long color,
- unsigned long start,
- unsigned long end,
+ u64 start,
+ u64 end,
enum drm_mm_search_flags flags)
{
struct drm_mm_node *entry;
struct drm_mm_node *best;
- unsigned long adj_start;
- unsigned long adj_end;
- unsigned long best_size;
+ u64 adj_start;
+ u64 adj_end;
+ u64 best_size;
BUG_ON(mm->scanned_blocks);
__drm_mm_for_each_hole(entry, mm, adj_start, adj_end,
flags & DRM_MM_SEARCH_BELOW) {
- unsigned long hole_size = adj_end - adj_start;
+ u64 hole_size = adj_end - adj_start;
if (adj_start < start)
adj_start = start;
* adding/removing nodes to/from the scan list are allowed.
*/
void drm_mm_init_scan(struct drm_mm *mm,
- unsigned long size,
+ u64 size,
unsigned alignment,
unsigned long color)
{
* adding/removing nodes to/from the scan list are allowed.
*/
void drm_mm_init_scan_with_range(struct drm_mm *mm,
- unsigned long size,
+ u64 size,
unsigned alignment,
unsigned long color,
- unsigned long start,
- unsigned long end)
+ u64 start,
+ u64 end)
{
mm->scan_color = color;
mm->scan_alignment = alignment;
{
struct drm_mm *mm = node->mm;
struct drm_mm_node *prev_node;
- unsigned long hole_start, hole_end;
- unsigned long adj_start, adj_end;
+ u64 hole_start, hole_end;
+ u64 adj_start, adj_end;
mm->scanned_blocks++;
*
* Note that @mm must be cleared to 0 before calling this function.
*/
-void drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size)
+void drm_mm_init(struct drm_mm * mm, u64 start, u64 size)
{
INIT_LIST_HEAD(&mm->hole_stack);
mm->scanned_blocks = 0;
}
EXPORT_SYMBOL(drm_mm_takedown);
-static unsigned long drm_mm_debug_hole(struct drm_mm_node *entry,
- const char *prefix)
+static u64 drm_mm_debug_hole(struct drm_mm_node *entry,
+ const char *prefix)
{
- unsigned long hole_start, hole_end, hole_size;
+ u64 hole_start, hole_end, hole_size;
if (entry->hole_follows) {
hole_start = drm_mm_hole_node_start(entry);
hole_end = drm_mm_hole_node_end(entry);
hole_size = hole_end - hole_start;
- printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: free\n",
- prefix, hole_start, hole_end,
- hole_size);
+ pr_debug("%s %#llx-%#llx: %llu: free\n", prefix, hole_start,
+ hole_end, hole_size);
return hole_size;
}
void drm_mm_debug_table(struct drm_mm *mm, const char *prefix)
{
struct drm_mm_node *entry;
- unsigned long total_used = 0, total_free = 0, total = 0;
+ u64 total_used = 0, total_free = 0, total = 0;
total_free += drm_mm_debug_hole(&mm->head_node, prefix);
drm_mm_for_each_node(entry, mm) {
- printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: used\n",
- prefix, entry->start, entry->start + entry->size,
- entry->size);
+ pr_debug("%s %#llx-%#llx: %llu: used\n", prefix, entry->start,
+ entry->start + entry->size, entry->size);
total_used += entry->size;
total_free += drm_mm_debug_hole(entry, prefix);
}
total = total_free + total_used;
- printk(KERN_DEBUG "%s total: %lu, used %lu free %lu\n", prefix, total,
- total_used, total_free);
+ pr_debug("%s total: %llu, used %llu free %llu\n", prefix, total,
+ total_used, total_free);
}
EXPORT_SYMBOL(drm_mm_debug_table);
#if defined(CONFIG_DEBUG_FS)
-static unsigned long drm_mm_dump_hole(struct seq_file *m, struct drm_mm_node *entry)
+static u64 drm_mm_dump_hole(struct seq_file *m, struct drm_mm_node *entry)
{
- unsigned long hole_start, hole_end, hole_size;
+ u64 hole_start, hole_end, hole_size;
if (entry->hole_follows) {
hole_start = drm_mm_hole_node_start(entry);
hole_end = drm_mm_hole_node_end(entry);
hole_size = hole_end - hole_start;
- seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: free\n",
- hole_start, hole_end, hole_size);
+ seq_printf(m, "%#llx-%#llx: %llu: free\n", hole_start,
+ hole_end, hole_size);
return hole_size;
}
int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm)
{
struct drm_mm_node *entry;
- unsigned long total_used = 0, total_free = 0, total = 0;
+ u64 total_used = 0, total_free = 0, total = 0;
total_free += drm_mm_dump_hole(m, &mm->head_node);
drm_mm_for_each_node(entry, mm) {
- seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: used\n",
- entry->start, entry->start + entry->size,
- entry->size);
+ seq_printf(m, "%#016llx-%#016llx: %llu: used\n", entry->start,
+ entry->start + entry->size, entry->size);
total_used += entry->size;
total_free += drm_mm_dump_hole(m, entry);
}
total = total_free + total_used;
- seq_printf(m, "total: %lu, used %lu free %lu\n", total, total_used, total_free);
+ seq_printf(m, "total: %llu, used %llu free %llu\n", total,
+ total_used, total_free);
return 0;
}
EXPORT_SYMBOL(drm_mm_dump_table);
struct edid *edid = (struct edid *) connector->edid_blob_ptr->data;
count = drm_add_edid_modes(connector, edid);
+ drm_edid_to_eld(connector, edid);
} else
count = (*connector_funcs->get_modes)(connector);
}
config DRM_EXYNOS_DP
bool "EXYNOS DRM DP driver support"
- depends on (DRM_EXYNOS_FIMD || DRM_EXYNOS7DECON) && ARCH_EXYNOS && (DRM_PTN3460=n || DRM_PTN3460=y || DRM_PTN3460=DRM_EXYNOS)
+ depends on (DRM_EXYNOS_FIMD || DRM_EXYNOS7_DECON) && ARCH_EXYNOS && (DRM_PTN3460=n || DRM_PTN3460=y || DRM_PTN3460=DRM_EXYNOS)
default DRM_EXYNOS
select DRM_PANEL
help
of_node_put(i80_if_timings);
ctx->regs = of_iomap(dev->of_node, 0);
- if (IS_ERR(ctx->regs)) {
- ret = PTR_ERR(ctx->regs);
+ if (!ctx->regs) {
+ ret = -ENOMEM;
goto err_del_component;
}
+++ /dev/null
-/*
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * Authors:
- * Inki Dae <inki.dae@samsung.com>
- * Joonyoung Shim <jy0922.shim@samsung.com>
- * Seung-Woo Kim <sw0312.kim@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#include <drm/drmP.h>
-#include <drm/drm_crtc_helper.h>
-
-#include <drm/exynos_drm.h>
-#include "exynos_drm_drv.h"
-#include "exynos_drm_encoder.h"
-#include "exynos_drm_connector.h"
-
-#define to_exynos_connector(x) container_of(x, struct exynos_drm_connector,\
- drm_connector)
-
-struct exynos_drm_connector {
- struct drm_connector drm_connector;
- uint32_t encoder_id;
- struct exynos_drm_display *display;
-};
-
-static int exynos_drm_connector_get_modes(struct drm_connector *connector)
-{
- struct exynos_drm_connector *exynos_connector =
- to_exynos_connector(connector);
- struct exynos_drm_display *display = exynos_connector->display;
- struct edid *edid = NULL;
- unsigned int count = 0;
- int ret;
-
- /*
- * if get_edid() exists then get_edid() callback of hdmi side
- * is called to get edid data through i2c interface else
- * get timing from the FIMD driver(display controller).
- *
- * P.S. in case of lcd panel, count is always 1 if success
- * because lcd panel has only one mode.
- */
- if (display->ops->get_edid) {
- edid = display->ops->get_edid(display, connector);
- if (IS_ERR_OR_NULL(edid)) {
- ret = PTR_ERR(edid);
- edid = NULL;
- DRM_ERROR("Panel operation get_edid failed %d\n", ret);
- goto out;
- }
-
- count = drm_add_edid_modes(connector, edid);
- if (!count) {
- DRM_ERROR("Add edid modes failed %d\n", count);
- goto out;
- }
-
- drm_mode_connector_update_edid_property(connector, edid);
- } else {
- struct exynos_drm_panel_info *panel;
- struct drm_display_mode *mode = drm_mode_create(connector->dev);
- if (!mode) {
- DRM_ERROR("failed to create a new display mode.\n");
- return 0;
- }
-
- if (display->ops->get_panel)
- panel = display->ops->get_panel(display);
- else {
- drm_mode_destroy(connector->dev, mode);
- return 0;
- }
-
- drm_display_mode_from_videomode(&panel->vm, mode);
- mode->width_mm = panel->width_mm;
- mode->height_mm = panel->height_mm;
- connector->display_info.width_mm = mode->width_mm;
- connector->display_info.height_mm = mode->height_mm;
-
- mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
- drm_mode_set_name(mode);
- drm_mode_probed_add(connector, mode);
-
- count = 1;
- }
-
-out:
- kfree(edid);
- return count;
-}
-
-static int exynos_drm_connector_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
-{
- struct exynos_drm_connector *exynos_connector =
- to_exynos_connector(connector);
- struct exynos_drm_display *display = exynos_connector->display;
- int ret = MODE_BAD;
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- if (display->ops->check_mode)
- if (!display->ops->check_mode(display, mode))
- ret = MODE_OK;
-
- return ret;
-}
-
-static struct drm_encoder *exynos_drm_best_encoder(
- struct drm_connector *connector)
-{
- struct drm_device *dev = connector->dev;
- struct exynos_drm_connector *exynos_connector =
- to_exynos_connector(connector);
- return drm_encoder_find(dev, exynos_connector->encoder_id);
-}
-
-static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
- .get_modes = exynos_drm_connector_get_modes,
- .mode_valid = exynos_drm_connector_mode_valid,
- .best_encoder = exynos_drm_best_encoder,
-};
-
-static int exynos_drm_connector_fill_modes(struct drm_connector *connector,
- unsigned int max_width, unsigned int max_height)
-{
- struct exynos_drm_connector *exynos_connector =
- to_exynos_connector(connector);
- struct exynos_drm_display *display = exynos_connector->display;
- unsigned int width, height;
-
- width = max_width;
- height = max_height;
-
- /*
- * if specific driver want to find desired_mode using maxmum
- * resolution then get max width and height from that driver.
- */
- if (display->ops->get_max_resol)
- display->ops->get_max_resol(display, &width, &height);
-
- return drm_helper_probe_single_connector_modes(connector, width,
- height);
-}
-
-/* get detection status of display device. */
-static enum drm_connector_status
-exynos_drm_connector_detect(struct drm_connector *connector, bool force)
-{
- struct exynos_drm_connector *exynos_connector =
- to_exynos_connector(connector);
- struct exynos_drm_display *display = exynos_connector->display;
- enum drm_connector_status status = connector_status_disconnected;
-
- if (display->ops->is_connected) {
- if (display->ops->is_connected(display))
- status = connector_status_connected;
- else
- status = connector_status_disconnected;
- }
-
- return status;
-}
-
-static void exynos_drm_connector_destroy(struct drm_connector *connector)
-{
- struct exynos_drm_connector *exynos_connector =
- to_exynos_connector(connector);
-
- drm_connector_unregister(connector);
- drm_connector_cleanup(connector);
- kfree(exynos_connector);
-}
-
-static struct drm_connector_funcs exynos_connector_funcs = {
- .dpms = drm_helper_connector_dpms,
- .fill_modes = exynos_drm_connector_fill_modes,
- .detect = exynos_drm_connector_detect,
- .destroy = exynos_drm_connector_destroy,
-};
-
-struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
- struct drm_encoder *encoder)
-{
- struct exynos_drm_connector *exynos_connector;
- struct exynos_drm_display *display = exynos_drm_get_display(encoder);
- struct drm_connector *connector;
- int type;
- int err;
-
- exynos_connector = kzalloc(sizeof(*exynos_connector), GFP_KERNEL);
- if (!exynos_connector)
- return NULL;
-
- connector = &exynos_connector->drm_connector;
-
- switch (display->type) {
- case EXYNOS_DISPLAY_TYPE_HDMI:
- type = DRM_MODE_CONNECTOR_HDMIA;
- connector->interlace_allowed = true;
- connector->polled = DRM_CONNECTOR_POLL_HPD;
- break;
- case EXYNOS_DISPLAY_TYPE_VIDI:
- type = DRM_MODE_CONNECTOR_VIRTUAL;
- connector->polled = DRM_CONNECTOR_POLL_HPD;
- break;
- default:
- type = DRM_MODE_CONNECTOR_Unknown;
- break;
- }
-
- drm_connector_init(dev, connector, &exynos_connector_funcs, type);
- drm_connector_helper_add(connector, &exynos_connector_helper_funcs);
-
- err = drm_connector_register(connector);
- if (err)
- goto err_connector;
-
- exynos_connector->encoder_id = encoder->base.id;
- exynos_connector->display = display;
- connector->dpms = DRM_MODE_DPMS_OFF;
- connector->encoder = encoder;
-
- err = drm_mode_connector_attach_encoder(connector, encoder);
- if (err) {
- DRM_ERROR("failed to attach a connector to a encoder\n");
- goto err_sysfs;
- }
-
- DRM_DEBUG_KMS("connector has been created\n");
-
- return connector;
-
-err_sysfs:
- drm_connector_unregister(connector);
-err_connector:
- drm_connector_cleanup(connector);
- kfree(exynos_connector);
- return NULL;
-}
+++ /dev/null
-/*
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * Authors:
- * Inki Dae <inki.dae@samsung.com>
- * Joonyoung Shim <jy0922.shim@samsung.com>
- * Seung-Woo Kim <sw0312.kim@samsung.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.
- */
-
-#ifndef _EXYNOS_DRM_CONNECTOR_H_
-#define _EXYNOS_DRM_CONNECTOR_H_
-
-struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
- struct drm_encoder *encoder);
-
-#endif
unsigned int ovl_height;
unsigned int fb_width;
unsigned int fb_height;
+ unsigned int fb_pitch;
unsigned int bpp;
unsigned int pixel_format;
dma_addr_t dma_addr;
}
}
-static int fimd_ctx_initialize(struct fimd_context *ctx,
+static int fimd_iommu_attach_devices(struct fimd_context *ctx,
struct drm_device *drm_dev)
{
- struct exynos_drm_private *priv;
- priv = drm_dev->dev_private;
-
- ctx->drm_dev = drm_dev;
- ctx->pipe = priv->pipe++;
/* attach this sub driver to iommu mapping if supported. */
if (is_drm_iommu_supported(ctx->drm_dev)) {
return 0;
}
-static void fimd_ctx_remove(struct fimd_context *ctx)
+static void fimd_iommu_detach_devices(struct fimd_context *ctx)
{
/* detach this sub driver from iommu mapping if supported. */
if (is_drm_iommu_supported(ctx->drm_dev))
win_data->offset_y = plane->crtc_y;
win_data->ovl_width = plane->crtc_width;
win_data->ovl_height = plane->crtc_height;
+ win_data->fb_pitch = plane->pitch;
win_data->fb_width = plane->fb_width;
win_data->fb_height = plane->fb_height;
win_data->dma_addr = plane->dma_addr[0] + offset;
win_data->bpp = plane->bpp;
win_data->pixel_format = plane->pixel_format;
- win_data->buf_offsize = (plane->fb_width - plane->crtc_width) *
- (plane->bpp >> 3);
+ win_data->buf_offsize =
+ plane->pitch - (plane->crtc_width * (plane->bpp >> 3));
win_data->line_size = plane->crtc_width * (plane->bpp >> 3);
DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n",
writel(val, ctx->regs + VIDWx_BUF_START(win, 0));
/* buffer end address */
- size = win_data->fb_width * win_data->ovl_height * (win_data->bpp >> 3);
+ size = win_data->fb_pitch * win_data->ovl_height * (win_data->bpp >> 3);
val = (unsigned long)(win_data->dma_addr + size);
writel(val, ctx->regs + VIDWx_BUF_END(win, 0));
{
struct fimd_context *ctx = dev_get_drvdata(dev);
struct drm_device *drm_dev = data;
+ struct exynos_drm_private *priv = drm_dev->dev_private;
int ret;
- ret = fimd_ctx_initialize(ctx, drm_dev);
- if (ret) {
- DRM_ERROR("fimd_ctx_initialize failed.\n");
- return ret;
- }
+ ctx->drm_dev = drm_dev;
+ ctx->pipe = priv->pipe++;
ctx->crtc = exynos_drm_crtc_create(drm_dev, ctx->pipe,
EXYNOS_DISPLAY_TYPE_LCD,
&fimd_crtc_ops, ctx);
- if (IS_ERR(ctx->crtc)) {
- fimd_ctx_remove(ctx);
- return PTR_ERR(ctx->crtc);
- }
if (ctx->display)
exynos_drm_create_enc_conn(drm_dev, ctx->display);
+ ret = fimd_iommu_attach_devices(ctx, drm_dev);
+ if (ret)
+ return ret;
+
return 0;
}
fimd_dpms(ctx->crtc, DRM_MODE_DPMS_OFF);
+ fimd_iommu_detach_devices(ctx);
+
if (ctx->display)
exynos_dpi_remove(ctx->display);
-
- fimd_ctx_remove(ctx);
}
static const struct component_ops fimd_component_ops = {
struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(plane->crtc);
- if (exynos_crtc->ops->win_disable)
+ if (exynos_crtc && exynos_crtc->ops->win_disable)
exynos_crtc->ops->win_disable(exynos_crtc,
exynos_plane->zpos);
unsigned int fb_x;
unsigned int fb_y;
unsigned int fb_width;
+ unsigned int fb_pitch;
unsigned int fb_height;
unsigned int src_width;
unsigned int src_height;
} else {
luma_addr[0] = win_data->dma_addr;
chroma_addr[0] = win_data->dma_addr
- + (win_data->fb_width * win_data->fb_height);
+ + (win_data->fb_pitch * win_data->fb_height);
}
if (win_data->scan_flags & DRM_MODE_FLAG_INTERLACE) {
luma_addr[1] = luma_addr[0] + 0x40;
chroma_addr[1] = chroma_addr[0] + 0x40;
} else {
- luma_addr[1] = luma_addr[0] + win_data->fb_width;
- chroma_addr[1] = chroma_addr[0] + win_data->fb_width;
+ luma_addr[1] = luma_addr[0] + win_data->fb_pitch;
+ chroma_addr[1] = chroma_addr[0] + win_data->fb_pitch;
}
} else {
ctx->interlace = false;
vp_reg_writemask(res, VP_MODE, val, VP_MODE_FMT_MASK);
/* setting size of input image */
- vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(win_data->fb_width) |
+ vp_reg_write(res, VP_IMG_SIZE_Y, VP_IMG_HSIZE(win_data->fb_pitch) |
VP_IMG_VSIZE(win_data->fb_height));
/* chroma height has to reduced by 2 to avoid chroma distorions */
- vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(win_data->fb_width) |
+ vp_reg_write(res, VP_IMG_SIZE_C, VP_IMG_HSIZE(win_data->fb_pitch) |
VP_IMG_VSIZE(win_data->fb_height / 2));
vp_reg_write(res, VP_SRC_WIDTH, win_data->src_width);
/* converting dma address base and source offset */
dma_addr = win_data->dma_addr
+ (win_data->fb_x * win_data->bpp >> 3)
- + (win_data->fb_y * win_data->fb_width * win_data->bpp >> 3);
+ + (win_data->fb_y * win_data->fb_pitch);
src_x_offset = 0;
src_y_offset = 0;
MXR_GRP_CFG_FORMAT_VAL(fmt), MXR_GRP_CFG_FORMAT_MASK);
/* setup geometry */
- mixer_reg_write(res, MXR_GRAPHIC_SPAN(win), win_data->fb_width);
+ mixer_reg_write(res, MXR_GRAPHIC_SPAN(win),
+ win_data->fb_pitch / (win_data->bpp >> 3));
/* setup display size */
if (ctx->mxr_ver == MXR_VER_128_0_0_184 &&
win_data->fb_y = plane->fb_y;
win_data->fb_width = plane->fb_width;
win_data->fb_height = plane->fb_height;
+ win_data->fb_pitch = plane->pitch;
win_data->src_width = plane->src_width;
win_data->src_height = plane->src_height;
seq_puts(m, " (pp");
else
seq_puts(m, " (g");
- seq_printf(m, "gtt offset: %08lx, size: %08lx, type: %u)",
+ seq_printf(m, "gtt offset: %08llx, size: %08llx, type: %u)",
vma->node.start, vma->node.size,
vma->ggtt_view.type);
}
if (obj->stolen)
- seq_printf(m, " (stolen: %08lx)", obj->stolen->start);
+ seq_printf(m, " (stolen: %08llx)", obj->stolen->start);
if (obj->pin_mappable || obj->fault_mappable) {
char s[3], *t = s;
if (obj->pin_mappable)
return 0;
}
-static int i915_drm_suspend_late(struct drm_device *drm_dev)
+static int i915_drm_suspend_late(struct drm_device *drm_dev, bool hibernation)
{
struct drm_i915_private *dev_priv = drm_dev->dev_private;
int ret;
}
pci_disable_device(drm_dev->pdev);
- pci_set_power_state(drm_dev->pdev, PCI_D3hot);
+ /*
+ * During hibernation on some GEN4 platforms the BIOS may try to access
+ * the device even though it's already in D3 and hang the machine. So
+ * leave the device in D0 on those platforms and hope the BIOS will
+ * power down the device properly. Platforms where this was seen:
+ * Lenovo Thinkpad X301, X61s
+ */
+ if (!(hibernation &&
+ drm_dev->pdev->subsystem_vendor == PCI_VENDOR_ID_LENOVO &&
+ INTEL_INFO(dev_priv)->gen == 4))
+ pci_set_power_state(drm_dev->pdev, PCI_D3hot);
return 0;
}
if (error)
return error;
- return i915_drm_suspend_late(dev);
+ return i915_drm_suspend_late(dev, false);
}
static int i915_drm_resume(struct drm_device *dev)
if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF)
return 0;
- return i915_drm_suspend_late(drm_dev);
+ return i915_drm_suspend_late(drm_dev, false);
+}
+
+static int i915_pm_poweroff_late(struct device *dev)
+{
+ struct drm_device *drm_dev = dev_to_i915(dev)->dev;
+
+ if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF)
+ return 0;
+
+ return i915_drm_suspend_late(drm_dev, true);
}
static int i915_pm_resume_early(struct device *dev)
.thaw_early = i915_pm_resume_early,
.thaw = i915_pm_resume,
.poweroff = i915_pm_suspend,
- .poweroff_late = i915_pm_suspend_late,
+ .poweroff_late = i915_pm_poweroff_late,
.restore_early = i915_pm_resume_early,
.restore = i915_pm_resume,
* number comparisons on buffer last_read|write_seqno. It also allows an
* emission time to be associated with the request for tracking how far ahead
* of the GPU the submission is.
+ *
+ * The requests are reference counted, so upon creation they should have an
+ * initial reference taken using kref_init
*/
struct drm_i915_gem_request {
struct kref ref;
/** Position in the ringbuffer of the end of the whole request */
u32 tail;
- /** Context related to this request */
+ /**
+ * Context related to this request
+ * Contexts are refcounted, so when this request is associated with a
+ * context, we must increment the context's refcount, to guarantee that
+ * it persists while any request is linked to it. Requests themselves
+ * are also refcounted, so the request will only be freed when the last
+ * reference to it is dismissed, and the code in
+ * i915_gem_request_free() will then decrement the refcount on the
+ * context.
+ */
struct intel_context *ctx;
/** Batch buffer related to this request if any */
(INTEL_DEVID(dev) & 0xFF00) == 0x0C00)
#define IS_BDW_ULT(dev) (IS_BROADWELL(dev) && \
((INTEL_DEVID(dev) & 0xf) == 0x6 || \
+ (INTEL_DEVID(dev) & 0xf) == 0xb || \
(INTEL_DEVID(dev) & 0xf) == 0xe))
#define IS_BDW_GT3(dev) (IS_BROADWELL(dev) && \
(INTEL_DEVID(dev) & 0x00F0) == 0x0020)
if (submit_req->ctx != ring->default_context)
intel_lr_context_unpin(ring, submit_req->ctx);
- i915_gem_context_unreference(submit_req->ctx);
- kfree(submit_req);
+ i915_gem_request_unreference(submit_req);
}
/*
WARN_ON(i915_verify_lists(ring->dev));
- /* Move any buffers on the active list that are no longer referenced
- * by the ringbuffer to the flushing/inactive lists as appropriate,
- * before we free the context associated with the requests.
+ /* Retire requests first as we use it above for the early return.
+ * If we retire requests last, we may use a later seqno and so clear
+ * the requests lists without clearing the active list, leading to
+ * confusion.
*/
- while (!list_empty(&ring->active_list)) {
- struct drm_i915_gem_object *obj;
-
- obj = list_first_entry(&ring->active_list,
- struct drm_i915_gem_object,
- ring_list);
-
- if (!i915_gem_request_completed(obj->last_read_req, true))
- break;
-
- i915_gem_object_move_to_inactive(obj);
- }
-
-
while (!list_empty(&ring->request_list)) {
struct drm_i915_gem_request *request;
struct intel_ringbuffer *ringbuf;
i915_gem_free_request(request);
}
+ /* Move any buffers on the active list that are no longer referenced
+ * by the ringbuffer to the flushing/inactive lists as appropriate,
+ * before we free the context associated with the requests.
+ */
+ while (!list_empty(&ring->active_list)) {
+ struct drm_i915_gem_object *obj;
+
+ obj = list_first_entry(&ring->active_list,
+ struct drm_i915_gem_object,
+ ring_list);
+
+ if (!i915_gem_request_completed(obj->last_read_req, true))
+ break;
+
+ i915_gem_object_move_to_inactive(obj);
+ }
+
if (unlikely(ring->trace_irq_req &&
i915_gem_request_completed(ring->trace_irq_req, true))) {
ring->irq_put(ring);
req = obj->last_read_req;
/* Do this after OLR check to make sure we make forward progress polling
- * on this IOCTL with a timeout <=0 (like busy ioctl)
+ * on this IOCTL with a timeout == 0 (like busy ioctl)
*/
- if (args->timeout_ns <= 0) {
+ if (args->timeout_ns == 0) {
ret = -ETIME;
goto out;
}
i915_gem_request_reference(req);
mutex_unlock(&dev->struct_mutex);
- ret = __i915_wait_request(req, reset_counter, true, &args->timeout_ns,
+ ret = __i915_wait_request(req, reset_counter, true,
+ args->timeout_ns > 0 ? &args->timeout_ns : NULL,
file->driver_priv);
mutex_lock(&dev->struct_mutex);
i915_gem_request_unreference(req);
if (INTEL_INFO(dev)->gen < 6 && !intel_enable_gtt())
return -EIO;
+ /* Double layer security blanket, see i915_gem_init() */
+ intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
+
if (dev_priv->ellc_size)
I915_WRITE(HSW_IDICR, I915_READ(HSW_IDICR) | IDIHASHMSK(0xf));
for_each_ring(ring, dev_priv, i) {
ret = ring->init_hw(ring);
if (ret)
- return ret;
+ goto out;
}
for (i = 0; i < NUM_L3_SLICES(dev); i++)
DRM_ERROR("Context enable failed %d\n", ret);
i915_gem_cleanup_ringbuffer(dev);
- return ret;
+ goto out;
}
+out:
+ intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
return ret;
}
dev_priv->gt.stop_ring = intel_logical_ring_stop;
}
+ /* This is just a security blanket to placate dragons.
+ * On some systems, we very sporadically observe that the first TLBs
+ * used by the CS may be stale, despite us poking the TLB reset. If
+ * we hold the forcewake during initialisation these problems
+ * just magically go away.
+ */
+ intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
+
ret = i915_gem_init_userptr(dev);
if (ret)
goto out_unlock;
}
out_unlock:
+ intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
mutex_unlock(&dev->struct_mutex);
return ret;
goto err;
}
- if (i915_needs_cmd_parser(ring)) {
+ if (i915_needs_cmd_parser(ring) && args->batch_len) {
batch_obj = i915_gem_execbuffer_parse(ring,
&shadow_exec_entry,
eb,
ppgtt->base.clear_range(&ppgtt->base, 0, ppgtt->base.total, true);
- DRM_DEBUG_DRIVER("Allocated pde space (%ldM) at GTT entry: %lx\n",
+ DRM_DEBUG_DRIVER("Allocated pde space (%lldM) at GTT entry: %llx\n",
ppgtt->node.size >> 20,
ppgtt->node.start / PAGE_SIZE);
static void i915_gtt_color_adjust(struct drm_mm_node *node,
unsigned long color,
- unsigned long *start,
- unsigned long *end)
+ u64 *start,
+ u64 *end)
{
if (node->color != color)
*start += 4096;
stolen_offset, gtt_offset, size);
/* KISS and expect everything to be page-aligned */
- BUG_ON(stolen_offset & 4095);
- BUG_ON(size & 4095);
-
- if (WARN_ON(size == 0))
+ if (WARN_ON(size == 0) || WARN_ON(size & 4095) ||
+ WARN_ON(stolen_offset & 4095))
return NULL;
stolen = kzalloc(sizeof(*stolen), GFP_KERNEL);
return -EINVAL;
}
+ mutex_lock(&dev->struct_mutex);
if (i915_gem_obj_is_pinned(obj) || obj->framebuffer_references) {
- drm_gem_object_unreference_unlocked(&obj->base);
- return -EBUSY;
+ ret = -EBUSY;
+ goto err;
}
if (args->tiling_mode == I915_TILING_NONE) {
}
}
- mutex_lock(&dev->struct_mutex);
if (args->tiling_mode != obj->tiling_mode ||
args->stride != obj->stride) {
/* We need to rebind the object if its current allocation
obj->bit_17 = NULL;
}
+err:
drm_gem_object_unreference(&obj->base);
mutex_unlock(&dev->struct_mutex);
u32 iir, gt_iir, pm_iir;
irqreturn_t ret = IRQ_NONE;
+ if (!intel_irqs_enabled(dev_priv))
+ return IRQ_NONE;
+
while (true) {
/* Find, clear, then process each source of interrupt */
u32 master_ctl, iir;
irqreturn_t ret = IRQ_NONE;
+ if (!intel_irqs_enabled(dev_priv))
+ return IRQ_NONE;
+
for (;;) {
master_ctl = I915_READ(GEN8_MASTER_IRQ) & ~GEN8_MASTER_IRQ_CONTROL;
iir = I915_READ(VLV_IIR);
u32 de_iir, gt_iir, de_ier, sde_ier = 0;
irqreturn_t ret = IRQ_NONE;
+ if (!intel_irqs_enabled(dev_priv))
+ return IRQ_NONE;
+
/* We get interrupts on unclaimed registers, so check for this before we
* do any I915_{READ,WRITE}. */
intel_uncore_check_errors(dev);
enum pipe pipe;
u32 aux_mask = GEN8_AUX_CHANNEL_A;
+ if (!intel_irqs_enabled(dev_priv))
+ return IRQ_NONE;
+
if (IS_GEN9(dev))
aux_mask |= GEN9_AUX_CHANNEL_B | GEN9_AUX_CHANNEL_C |
GEN9_AUX_CHANNEL_D;
I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT |
I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT;
+ if (!intel_irqs_enabled(dev_priv))
+ return IRQ_NONE;
+
iir = I915_READ16(IIR);
if (iir == 0)
return IRQ_NONE;
I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT;
int pipe, ret = IRQ_NONE;
+ if (!intel_irqs_enabled(dev_priv))
+ return IRQ_NONE;
+
iir = I915_READ(IIR);
do {
bool irq_received = (iir & ~flip_mask) != 0;
I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT |
I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT;
+ if (!intel_irqs_enabled(dev_priv))
+ return IRQ_NONE;
+
iir = I915_READ(IIR);
for (;;) {
{
dev_priv->dev->driver->irq_uninstall(dev_priv->dev);
dev_priv->pm.irqs_enabled = false;
+ synchronize_irq(dev_priv->dev->irq);
}
/**
#include <drm/i915_drm.h>
#include "i915_drv.h"
#include "i915_trace.h"
+#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_dp_helper.h>
#include <drm/drm_crtc_helper.h>
struct drm_device *dev = crtc->base.dev;
struct drm_i915_gem_object *obj = NULL;
struct drm_mode_fb_cmd2 mode_cmd = { 0 };
- u32 base = plane_config->base;
+ u32 base_aligned = round_down(plane_config->base, PAGE_SIZE);
+ u32 size_aligned = round_up(plane_config->base + plane_config->size,
+ PAGE_SIZE);
+
+ size_aligned -= base_aligned;
if (plane_config->size == 0)
return false;
- obj = i915_gem_object_create_stolen_for_preallocated(dev, base, base,
- plane_config->size);
+ obj = i915_gem_object_create_stolen_for_preallocated(dev,
+ base_aligned,
+ base_aligned,
+ size_aligned);
if (!obj)
return false;
return false;
}
+/* Update plane->state->fb to match plane->fb after driver-internal updates */
+static void
+update_state_fb(struct drm_plane *plane)
+{
+ if (plane->fb != plane->state->fb)
+ drm_atomic_set_fb_for_plane(plane->state, plane->fb);
+}
+
static void
intel_find_plane_obj(struct intel_crtc *intel_crtc,
struct intel_initial_plane_config *plane_config)
if (!intel_crtc->base.primary->fb)
return;
- if (intel_alloc_plane_obj(intel_crtc, plane_config))
+ if (intel_alloc_plane_obj(intel_crtc, plane_config)) {
+ struct drm_plane *primary = intel_crtc->base.primary;
+
+ primary->state->crtc = &intel_crtc->base;
+ primary->crtc = &intel_crtc->base;
+ update_state_fb(primary);
+
return;
+ }
kfree(intel_crtc->base.primary->fb);
intel_crtc->base.primary->fb = NULL;
continue;
if (i915_gem_obj_ggtt_offset(obj) == plane_config->base) {
+ struct drm_plane *primary = intel_crtc->base.primary;
+
if (obj->tiling_mode != I915_TILING_NONE)
dev_priv->preserve_bios_swizzle = true;
drm_framebuffer_reference(c->primary->fb);
- intel_crtc->base.primary->fb = c->primary->fb;
+ primary->fb = c->primary->fb;
+ primary->state->crtc = &intel_crtc->base;
+ primary->crtc = &intel_crtc->base;
obj->frontbuffer_bits |= INTEL_FRONTBUFFER_PRIMARY(intel_crtc->pipe);
break;
}
}
+
+ update_state_fb(intel_crtc->base.primary);
}
static void i9xx_update_primary_plane(struct drm_crtc *crtc,
case DRM_FORMAT_XRGB8888:
plane_ctl |= PLANE_CTL_FORMAT_XRGB_8888;
break;
+ case DRM_FORMAT_ARGB8888:
+ plane_ctl |= PLANE_CTL_FORMAT_XRGB_8888;
+ plane_ctl |= PLANE_CTL_ALPHA_SW_PREMULTIPLY;
+ break;
case DRM_FORMAT_XBGR8888:
plane_ctl |= PLANE_CTL_ORDER_RGBX;
plane_ctl |= PLANE_CTL_FORMAT_XRGB_8888;
break;
+ case DRM_FORMAT_ABGR8888:
+ plane_ctl |= PLANE_CTL_ORDER_RGBX;
+ plane_ctl |= PLANE_CTL_FORMAT_XRGB_8888;
+ plane_ctl |= PLANE_CTL_ALPHA_SW_PREMULTIPLY;
+ break;
case DRM_FORMAT_XRGB2101010:
plane_ctl |= PLANE_CTL_FORMAT_XRGB_2101010;
break;
struct drm_framebuffer *fb;
struct intel_framebuffer *intel_fb;
+ val = I915_READ(DSPCNTR(plane));
+ if (!(val & DISPLAY_PLANE_ENABLE))
+ return;
+
intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL);
if (!intel_fb) {
DRM_DEBUG_KMS("failed to alloc fb\n");
fb = &intel_fb->base;
- val = I915_READ(DSPCNTR(plane));
-
if (INTEL_INFO(dev)->gen >= 4)
if (val & DISPPLANE_TILED)
plane_config->tiling = I915_TILING_X;
aligned_height = intel_fb_align_height(dev, fb->height,
plane_config->tiling);
- plane_config->size = PAGE_ALIGN(fb->pitches[0] * aligned_height);
+ plane_config->size = fb->pitches[0] * aligned_height;
DRM_DEBUG_KMS("pipe/plane %c/%d with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n",
pipe_name(pipe), plane, fb->width, fb->height,
fb = &intel_fb->base;
val = I915_READ(PLANE_CTL(pipe, 0));
+ if (!(val & PLANE_CTL_ENABLE))
+ goto error;
+
if (val & PLANE_CTL_TILED_MASK)
plane_config->tiling = I915_TILING_X;
aligned_height = intel_fb_align_height(dev, fb->height,
plane_config->tiling);
- plane_config->size = ALIGN(fb->pitches[0] * aligned_height, PAGE_SIZE);
+ plane_config->size = fb->pitches[0] * aligned_height;
DRM_DEBUG_KMS("pipe %c with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n",
pipe_name(pipe), fb->width, fb->height,
struct drm_framebuffer *fb;
struct intel_framebuffer *intel_fb;
+ val = I915_READ(DSPCNTR(pipe));
+ if (!(val & DISPLAY_PLANE_ENABLE))
+ return;
+
intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL);
if (!intel_fb) {
DRM_DEBUG_KMS("failed to alloc fb\n");
fb = &intel_fb->base;
- val = I915_READ(DSPCNTR(pipe));
-
if (INTEL_INFO(dev)->gen >= 4)
if (val & DISPPLANE_TILED)
plane_config->tiling = I915_TILING_X;
aligned_height = intel_fb_align_height(dev, fb->height,
plane_config->tiling);
- plane_config->size = PAGE_ALIGN(fb->pitches[0] * aligned_height);
+ plane_config->size = fb->pitches[0] * aligned_height;
DRM_DEBUG_KMS("pipe %c with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n",
pipe_name(pipe), fb->width, fb->height,
old->release_fb->funcs->destroy(old->release_fb);
goto fail;
}
+ crtc->primary->crtc = crtc;
/* let the connector get through one full cycle before testing */
intel_wait_for_vblank(dev, intel_crtc->pipe);
struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
- WARN_ON(!in_irq());
+ WARN_ON(!in_interrupt());
if (crtc == NULL)
return;
drm_gem_object_reference(&obj->base);
crtc->primary->fb = fb;
+ update_state_fb(crtc->primary);
work->pending_flip_obj = obj;
cleanup_pending:
atomic_dec(&intel_crtc->unpin_work_count);
crtc->primary->fb = old_fb;
+ update_state_fb(crtc->primary);
drm_gem_object_unreference(&work->old_fb_obj->base);
drm_gem_object_unreference(&obj->base);
mutex_unlock(&dev->struct_mutex);
return -ENOMEM;
}
- if (fb == crtc->cursor->fb)
- return 0;
-
/* we only need to pin inside GTT if cursor is non-phy */
mutex_lock(&dev->struct_mutex);
if (!INTEL_INFO(dev)->cursor_needs_physical && obj->tiling_mode) {
/* HP Chromebook 14 (Celeron 2955U) */
{ 0x0a06, 0x103c, 0x21ed, quirk_backlight_present },
+
+ /* Dell Chromebook 11 */
+ { 0x0a06, 0x1028, 0x0a35, quirk_backlight_present },
};
static void intel_init_quirks(struct drm_device *dev)
to_intel_crtc(c)->pipe);
drm_framebuffer_unreference(c->primary->fb);
c->primary->fb = NULL;
+ update_state_fb(c->primary);
}
}
mutex_unlock(&dev->struct_mutex);
return ret;
}
-static bool
-__cpu_fifo_underrun_reporting_enabled(struct drm_i915_private *dev_priv,
- enum pipe pipe)
-{
- struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
- struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-
- return !intel_crtc->cpu_fifo_underrun_disabled;
-}
-
/**
* intel_set_pch_fifo_underrun_reporting - set PCH fifo underrun reporting state
* @dev_priv: i915 device instance
void intel_cpu_fifo_underrun_irq_handler(struct drm_i915_private *dev_priv,
enum pipe pipe)
{
+ struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
+
+ /* We may be called too early in init, thanks BIOS! */
+ if (crtc == NULL)
+ return;
+
/* GMCH can't disable fifo underruns, filter them. */
if (HAS_GMCH_DISPLAY(dev_priv->dev) &&
- !__cpu_fifo_underrun_reporting_enabled(dev_priv, pipe))
+ to_intel_crtc(crtc)->cpu_fifo_underrun_disabled)
return;
if (intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, false))
* If there isn't a request associated with this submission,
* create one as a temporary holder.
*/
- WARN(1, "execlist context submission without request");
request = kzalloc(sizeof(*request), GFP_KERNEL);
if (request == NULL)
return -ENOMEM;
request->ring = ring;
request->ctx = to;
+ kref_init(&request->ref);
+ request->uniq = dev_priv->request_uniq++;
+ i915_gem_context_reference(request->ctx);
} else {
+ i915_gem_request_reference(request);
WARN_ON(to != request->ctx);
}
request->tail = tail;
- i915_gem_request_reference(request);
- i915_gem_context_reference(request->ctx);
intel_runtime_pm_get(dev_priv);
if (ctx_obj && (ctx != ring->default_context))
intel_lr_context_unpin(ring, ctx);
intel_runtime_pm_put(dev_priv);
- i915_gem_context_unreference(ctx);
list_del(&req->execlist_link);
i915_gem_request_unreference(req);
}
drm_modeset_lock_all(dev);
plane = drm_plane_find(dev, set->plane_id);
- if (!plane) {
+ if (!plane || plane->type != DRM_PLANE_TYPE_OVERLAY) {
ret = -ENOENT;
goto out_unlock;
}
drm_modeset_lock_all(dev);
plane = drm_plane_find(dev, get->plane_id);
- if (!plane) {
+ if (!plane || plane->type != DRM_PLANE_TYPE_OVERLAY) {
ret = -ENOENT;
goto out_unlock;
}
/* We need to init first for ECOBUS access and then
* determine later if we want to reinit, in case of MT access is
- * not working
+ * not working. In this stage we don't know which flavour this
+ * ivb is, so it is better to reset also the gen6 fw registers
+ * before the ecobus check.
*/
+
+ __raw_i915_write32(dev_priv, FORCEWAKE, 0);
+ __raw_posting_read(dev_priv, ECOBUS);
+
fw_domain_init(dev_priv, FW_DOMAIN_ID_RENDER,
FORCEWAKE_MT, FORCEWAKE_MT_ACK);
118800000, { 0x091c, 0x091c, 0x06dc },
}, {
216000000, { 0x06dc, 0x0b5c, 0x091c },
- }
+ }, {
+ ~0UL, { 0x0000, 0x0000, 0x0000 },
+ },
};
static const struct dw_hdmi_sym_term imx_sym_term[] = {
.destroy = drm_encoder_cleanup,
};
+static enum drm_mode_status imx6q_hdmi_mode_valid(struct drm_connector *con,
+ struct drm_display_mode *mode)
+{
+ if (mode->clock < 13500)
+ return MODE_CLOCK_LOW;
+ if (mode->clock > 266000)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
+static enum drm_mode_status imx6dl_hdmi_mode_valid(struct drm_connector *con,
+ struct drm_display_mode *mode)
+{
+ if (mode->clock < 13500)
+ return MODE_CLOCK_LOW;
+ if (mode->clock > 270000)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
static struct dw_hdmi_plat_data imx6q_hdmi_drv_data = {
- .mpll_cfg = imx_mpll_cfg,
- .cur_ctr = imx_cur_ctr,
- .sym_term = imx_sym_term,
- .dev_type = IMX6Q_HDMI,
+ .mpll_cfg = imx_mpll_cfg,
+ .cur_ctr = imx_cur_ctr,
+ .sym_term = imx_sym_term,
+ .dev_type = IMX6Q_HDMI,
+ .mode_valid = imx6q_hdmi_mode_valid,
};
static struct dw_hdmi_plat_data imx6dl_hdmi_drv_data = {
.cur_ctr = imx_cur_ctr,
.sym_term = imx_sym_term,
.dev_type = IMX6DL_HDMI,
+ .mode_valid = imx6dl_hdmi_mode_valid,
};
static const struct of_device_id dw_hdmi_imx_dt_ids[] = {
{
struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
struct imx_ldb *ldb = imx_ldb_ch->ldb;
- struct drm_display_mode *mode = &encoder->crtc->hwmode;
u32 pixel_fmt;
- unsigned long serial_clk;
- unsigned long di_clk = mode->clock * 1000;
- int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->child, encoder);
-
- if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) {
- /* dual channel LVDS mode */
- serial_clk = 3500UL * mode->clock;
- imx_ldb_set_clock(ldb, mux, 0, serial_clk, di_clk);
- imx_ldb_set_clock(ldb, mux, 1, serial_clk, di_clk);
- } else {
- serial_clk = 7000UL * mode->clock;
- imx_ldb_set_clock(ldb, mux, imx_ldb_ch->chno, serial_clk,
- di_clk);
- }
switch (imx_ldb_ch->chno) {
case 0:
struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
struct imx_ldb *ldb = imx_ldb_ch->ldb;
int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN;
+ unsigned long serial_clk;
+ unsigned long di_clk = mode->clock * 1000;
+ int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->child, encoder);
if (mode->clock > 170000) {
dev_warn(ldb->dev,
"%s: mode exceeds 85 MHz pixel clock\n", __func__);
}
+ if (dual) {
+ serial_clk = 3500UL * mode->clock;
+ imx_ldb_set_clock(ldb, mux, 0, serial_clk, di_clk);
+ imx_ldb_set_clock(ldb, mux, 1, serial_clk, di_clk);
+ } else {
+ serial_clk = 7000UL * mode->clock;
+ imx_ldb_set_clock(ldb, mux, imx_ldb_ch->chno, serial_clk,
+ di_clk);
+ }
+
/* FIXME - assumes straight connections DI0 --> CH0, DI1 --> CH1 */
if (imx_ldb_ch == &ldb->channel[0]) {
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
}
panel_node = of_parse_phandle(np, "fsl,panel", 0);
- if (panel_node)
+ if (panel_node) {
imxpd->panel = of_drm_find_panel(panel_node);
+ if (!imxpd->panel)
+ return -EPROBE_DEFER;
+ }
imxpd->dev = dev;
void mdp4_irq_preinstall(struct msm_kms *kms)
{
struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms));
+ mdp4_enable(mdp4_kms);
mdp4_write(mdp4_kms, REG_MDP4_INTR_CLEAR, 0xffffffff);
+ mdp4_write(mdp4_kms, REG_MDP4_INTR_ENABLE, 0x00000000);
+ mdp4_disable(mdp4_kms);
}
int mdp4_irq_postinstall(struct msm_kms *kms)
void mdp4_irq_uninstall(struct msm_kms *kms)
{
struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms));
+ mdp4_enable(mdp4_kms);
mdp4_write(mdp4_kms, REG_MDP4_INTR_ENABLE, 0x00000000);
+ mdp4_disable(mdp4_kms);
}
irqreturn_t mdp4_irq(struct msm_kms *kms)
git clone https://github.com/freedreno/envytools.git
The rules-ng-ng source files this header was generated from are:
-- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2014-12-05 15:34:49)
-- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20908 bytes, from 2014-12-08 16:13:00)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2357 bytes, from 2014-12-08 16:13:00)
-- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 27208 bytes, from 2015-01-13 23:56:11)
-- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 11712 bytes, from 2013-08-17 17:13:43)
-- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2013-08-11 19:26:32)
-- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2014-10-31 16:48:57)
-- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2013-07-05 19:21:12)
-- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 26848 bytes, from 2015-01-13 23:55:57)
-- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 8253 bytes, from 2014-12-08 16:13:00)
+- /local/mnt2/workspace2/sviau/envytools/rnndb/mdp/mdp5.xml ( 27229 bytes, from 2015-02-10 17:00:41)
+- /local/mnt2/workspace2/sviau/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2014-06-02 18:31:15)
+- /local/mnt2/workspace2/sviau/envytools/rnndb/mdp/mdp_common.xml ( 2357 bytes, from 2015-01-23 16:20:19)
Copyright (C) 2013-2015 by the following authors:
- Rob Clark <robdclark@gmail.com> (robclark)
case 2: return (mdp5_cfg->lm.base[2]);
case 3: return (mdp5_cfg->lm.base[3]);
case 4: return (mdp5_cfg->lm.base[4]);
+ case 5: return (mdp5_cfg->lm.base[5]);
default: return INVALID_IDX(idx);
}
}
/* current cursor being scanned out: */
struct drm_gem_object *scanout_bo;
- uint32_t width;
- uint32_t height;
+ uint32_t width, height;
+ uint32_t x, y;
} cursor;
};
#define to_mdp5_crtc(x) container_of(x, struct mdp5_crtc, base)
struct drm_plane *plane;
uint32_t flush_mask = 0;
- /* we could have already released CTL in the disable path: */
- if (!mdp5_crtc->ctl)
+ /* this should not happen: */
+ if (WARN_ON(!mdp5_crtc->ctl))
return;
drm_atomic_crtc_for_each_plane(plane, crtc) {
drm_atomic_crtc_for_each_plane(plane, crtc) {
mdp5_plane_complete_flip(plane);
}
+
+ if (mdp5_crtc->ctl && !crtc->state->enable) {
+ mdp5_ctl_release(mdp5_crtc->ctl);
+ mdp5_crtc->ctl = NULL;
+ }
}
static void unref_cursor_worker(struct drm_flip_work *work, void *val)
mdp5_crtc->event = crtc->state->event;
spin_unlock_irqrestore(&dev->event_lock, flags);
+ /*
+ * If no CTL has been allocated in mdp5_crtc_atomic_check(),
+ * it means we are trying to flush a CRTC whose state is disabled:
+ * nothing else needs to be done.
+ */
+ if (unlikely(!mdp5_crtc->ctl))
+ return;
+
blend_setup(crtc);
crtc_flush_all(crtc);
request_pending(crtc, PENDING_FLIP);
-
- if (mdp5_crtc->ctl && !crtc->state->enable) {
- mdp5_ctl_release(mdp5_crtc->ctl);
- mdp5_crtc->ctl = NULL;
- }
}
static int mdp5_crtc_set_property(struct drm_crtc *crtc,
return -EINVAL;
}
+static void get_roi(struct drm_crtc *crtc, uint32_t *roi_w, uint32_t *roi_h)
+{
+ struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
+ uint32_t xres = crtc->mode.hdisplay;
+ uint32_t yres = crtc->mode.vdisplay;
+
+ /*
+ * Cursor Region Of Interest (ROI) is a plane read from cursor
+ * buffer to render. The ROI region is determined by the visibility of
+ * the cursor point. In the default Cursor image the cursor point will
+ * be at the top left of the cursor image, unless it is specified
+ * otherwise using hotspot feature.
+ *
+ * If the cursor point reaches the right (xres - x < cursor.width) or
+ * bottom (yres - y < cursor.height) boundary of the screen, then ROI
+ * width and ROI height need to be evaluated to crop the cursor image
+ * accordingly.
+ * (xres-x) will be new cursor width when x > (xres - cursor.width)
+ * (yres-y) will be new cursor height when y > (yres - cursor.height)
+ */
+ *roi_w = min(mdp5_crtc->cursor.width, xres -
+ mdp5_crtc->cursor.x);
+ *roi_h = min(mdp5_crtc->cursor.height, yres -
+ mdp5_crtc->cursor.y);
+}
+
static int mdp5_crtc_cursor_set(struct drm_crtc *crtc,
struct drm_file *file, uint32_t handle,
uint32_t width, uint32_t height)
unsigned int depth;
enum mdp5_cursor_alpha cur_alpha = CURSOR_ALPHA_PER_PIXEL;
uint32_t flush_mask = mdp_ctl_flush_mask_cursor(0);
+ uint32_t roi_w, roi_h;
unsigned long flags;
if ((width > CURSOR_WIDTH) || (height > CURSOR_HEIGHT)) {
spin_lock_irqsave(&mdp5_crtc->cursor.lock, flags);
old_bo = mdp5_crtc->cursor.scanout_bo;
+ mdp5_crtc->cursor.scanout_bo = cursor_bo;
+ mdp5_crtc->cursor.width = width;
+ mdp5_crtc->cursor.height = height;
+
+ get_roi(crtc, &roi_w, &roi_h);
+
mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_STRIDE(lm), stride);
mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_FORMAT(lm),
MDP5_LM_CURSOR_FORMAT_FORMAT(CURSOR_FMT_ARGB8888));
MDP5_LM_CURSOR_IMG_SIZE_SRC_H(height) |
MDP5_LM_CURSOR_IMG_SIZE_SRC_W(width));
mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_SIZE(lm),
- MDP5_LM_CURSOR_SIZE_ROI_H(height) |
- MDP5_LM_CURSOR_SIZE_ROI_W(width));
+ MDP5_LM_CURSOR_SIZE_ROI_H(roi_h) |
+ MDP5_LM_CURSOR_SIZE_ROI_W(roi_w));
mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_BASE_ADDR(lm), cursor_addr);
-
blendcfg = MDP5_LM_CURSOR_BLEND_CONFIG_BLEND_EN;
- blendcfg |= MDP5_LM_CURSOR_BLEND_CONFIG_BLEND_TRANSP_EN;
blendcfg |= MDP5_LM_CURSOR_BLEND_CONFIG_BLEND_ALPHA_SEL(cur_alpha);
mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_BLEND_CONFIG(lm), blendcfg);
- mdp5_crtc->cursor.scanout_bo = cursor_bo;
- mdp5_crtc->cursor.width = width;
- mdp5_crtc->cursor.height = height;
spin_unlock_irqrestore(&mdp5_crtc->cursor.lock, flags);
ret = mdp5_ctl_set_cursor(mdp5_crtc->ctl, true);
struct mdp5_kms *mdp5_kms = get_kms(crtc);
struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
uint32_t flush_mask = mdp_ctl_flush_mask_cursor(0);
- uint32_t xres = crtc->mode.hdisplay;
- uint32_t yres = crtc->mode.vdisplay;
uint32_t roi_w;
uint32_t roi_h;
unsigned long flags;
- x = (x > 0) ? x : 0;
- y = (y > 0) ? y : 0;
+ /* In case the CRTC is disabled, just drop the cursor update */
+ if (unlikely(!crtc->state->enable))
+ return 0;
- /*
- * Cursor Region Of Interest (ROI) is a plane read from cursor
- * buffer to render. The ROI region is determined by the visiblity of
- * the cursor point. In the default Cursor image the cursor point will
- * be at the top left of the cursor image, unless it is specified
- * otherwise using hotspot feature.
- *
- * If the cursor point reaches the right (xres - x < cursor.width) or
- * bottom (yres - y < cursor.height) boundary of the screen, then ROI
- * width and ROI height need to be evaluated to crop the cursor image
- * accordingly.
- * (xres-x) will be new cursor width when x > (xres - cursor.width)
- * (yres-y) will be new cursor height when y > (yres - cursor.height)
- */
- roi_w = min(mdp5_crtc->cursor.width, xres - x);
- roi_h = min(mdp5_crtc->cursor.height, yres - y);
+ mdp5_crtc->cursor.x = x = max(x, 0);
+ mdp5_crtc->cursor.y = y = max(y, 0);
+
+ get_roi(crtc, &roi_w, &roi_h);
spin_lock_irqsave(&mdp5_crtc->cursor.lock, flags);
mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_SIZE(mdp5_crtc->lm),
static const struct drm_crtc_helper_funcs mdp5_crtc_helper_funcs = {
.mode_fixup = mdp5_crtc_mode_fixup,
.mode_set_nofb = mdp5_crtc_mode_set_nofb,
- .prepare = mdp5_crtc_disable,
- .commit = mdp5_crtc_enable,
+ .disable = mdp5_crtc_disable,
+ .enable = mdp5_crtc_enable,
.atomic_check = mdp5_crtc_atomic_check,
.atomic_begin = mdp5_crtc_atomic_begin,
.atomic_flush = mdp5_crtc_atomic_flush,
mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(intf), 1);
spin_unlock_irqrestore(&mdp5_encoder->intf_lock, flags);
- mdp5_encoder->enabled = false;
+ mdp5_encoder->enabled = true;
}
static const struct drm_encoder_helper_funcs mdp5_encoder_helper_funcs = {
.mode_fixup = mdp5_encoder_mode_fixup,
.mode_set = mdp5_encoder_mode_set,
- .prepare = mdp5_encoder_disable,
- .commit = mdp5_encoder_enable,
+ .disable = mdp5_encoder_disable,
+ .enable = mdp5_encoder_enable,
};
/* initialize encoder */
void mdp5_irq_preinstall(struct msm_kms *kms)
{
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
+ mdp5_enable(mdp5_kms);
mdp5_write(mdp5_kms, REG_MDP5_INTR_CLEAR, 0xffffffff);
+ mdp5_write(mdp5_kms, REG_MDP5_INTR_EN, 0x00000000);
+ mdp5_disable(mdp5_kms);
}
int mdp5_irq_postinstall(struct msm_kms *kms)
void mdp5_irq_uninstall(struct msm_kms *kms)
{
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
+ mdp5_enable(mdp5_kms);
mdp5_write(mdp5_kms, REG_MDP5_INTR_EN, 0x00000000);
+ mdp5_disable(mdp5_kms);
}
static void mdp5_irq_mdp(struct mdp_kms *mdp_kms)
* mark our set of crtc's as busy:
*/
ret = start_atomic(dev->dev_private, c->crtc_mask);
- if (ret)
+ if (ret) {
+ kfree(c);
return ret;
+ }
/*
* This is the point of no return - everything below never fails except
nouveau_fbcon_zfill(dev, fbcon);
/* To allow resizeing without swapping buffers */
- NV_INFO(drm, "allocated %dx%d fb: 0x%lx, bo %p\n",
+ NV_INFO(drm, "allocated %dx%d fb: 0x%llx, bo %p\n",
nouveau_fb->base.width, nouveau_fb->base.height,
nvbo->bo.offset, nvbo);
/* switch mmio to cpu's native endianness */
#ifndef __BIG_ENDIAN
- if (ioread32_native(map + 0x000004) != 0x00000000)
+ if (ioread32_native(map + 0x000004) != 0x00000000) {
#else
- if (ioread32_native(map + 0x000004) == 0x00000000)
+ if (ioread32_native(map + 0x000004) == 0x00000000) {
#endif
iowrite32_native(0x01000001, map + 0x000004);
+ ioread32_native(map);
+ }
/* read boot0 and strapping information */
boot0 = ioread32_native(map + 0x000000);
device->oclass[NVDEV_ENGINE_MSVLD ] = &gk104_msvld_oclass;
device->oclass[NVDEV_ENGINE_MSPDEC ] = &gk104_mspdec_oclass;
device->oclass[NVDEV_ENGINE_MSPPP ] = &gf100_msppp_oclass;
+#endif
+ break;
+ case 0x126:
+ device->cname = "GM206";
+ device->oclass[NVDEV_SUBDEV_VBIOS ] = &nvkm_bios_oclass;
+ device->oclass[NVDEV_SUBDEV_GPIO ] = gk104_gpio_oclass;
+ device->oclass[NVDEV_SUBDEV_I2C ] = gm204_i2c_oclass;
+ device->oclass[NVDEV_SUBDEV_FUSE ] = &gm107_fuse_oclass;
+#if 0
+ /* looks to be some non-trivial changes */
+ device->oclass[NVDEV_SUBDEV_CLK ] = &gk104_clk_oclass;
+ /* priv ring says no to 0x10eb14 writes */
+ device->oclass[NVDEV_SUBDEV_THERM ] = &gm107_therm_oclass;
+#endif
+ device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass;
+ device->oclass[NVDEV_SUBDEV_DEVINIT] = gm204_devinit_oclass;
+ device->oclass[NVDEV_SUBDEV_MC ] = gk20a_mc_oclass;
+ device->oclass[NVDEV_SUBDEV_BUS ] = gf100_bus_oclass;
+ device->oclass[NVDEV_SUBDEV_TIMER ] = &gk20a_timer_oclass;
+ device->oclass[NVDEV_SUBDEV_FB ] = gm107_fb_oclass;
+ device->oclass[NVDEV_SUBDEV_LTC ] = gm107_ltc_oclass;
+ device->oclass[NVDEV_SUBDEV_IBUS ] = &gk104_ibus_oclass;
+ device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass;
+ device->oclass[NVDEV_SUBDEV_MMU ] = &gf100_mmu_oclass;
+ device->oclass[NVDEV_SUBDEV_BAR ] = &gf100_bar_oclass;
+ device->oclass[NVDEV_SUBDEV_PMU ] = gk208_pmu_oclass;
+#if 0
+ device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass;
+#endif
+ device->oclass[NVDEV_ENGINE_DMAOBJ ] = gf110_dmaeng_oclass;
+#if 0
+ device->oclass[NVDEV_ENGINE_FIFO ] = gk208_fifo_oclass;
+ device->oclass[NVDEV_ENGINE_SW ] = gf100_sw_oclass;
+ device->oclass[NVDEV_ENGINE_GR ] = gm107_gr_oclass;
+#endif
+ device->oclass[NVDEV_ENGINE_DISP ] = gm204_disp_oclass;
+#if 0
+ device->oclass[NVDEV_ENGINE_CE0 ] = &gm204_ce0_oclass;
+ device->oclass[NVDEV_ENGINE_CE1 ] = &gm204_ce1_oclass;
+ device->oclass[NVDEV_ENGINE_CE2 ] = &gm204_ce2_oclass;
+ device->oclass[NVDEV_ENGINE_MSVLD ] = &gk104_msvld_oclass;
+ device->oclass[NVDEV_ENGINE_MSPDEC ] = &gk104_mspdec_oclass;
+ device->oclass[NVDEV_ENGINE_MSPPP ] = &gf100_msppp_oclass;
#endif
break;
default:
{
struct nvkm_device *device = nv_device(subdev);
struct nv04_fifo_priv *priv = (void *)subdev;
- uint32_t status, reassign;
- int cnt = 0;
+ u32 mask = nv_rd32(priv, NV03_PFIFO_INTR_EN_0);
+ u32 stat = nv_rd32(priv, NV03_PFIFO_INTR_0) & mask;
+ u32 reassign, chid, get, sem;
reassign = nv_rd32(priv, NV03_PFIFO_CACHES) & 1;
- while ((status = nv_rd32(priv, NV03_PFIFO_INTR_0)) && (cnt++ < 100)) {
- uint32_t chid, get;
-
- nv_wr32(priv, NV03_PFIFO_CACHES, 0);
-
- chid = nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH1) & priv->base.max;
- get = nv_rd32(priv, NV03_PFIFO_CACHE1_GET);
+ nv_wr32(priv, NV03_PFIFO_CACHES, 0);
- if (status & NV_PFIFO_INTR_CACHE_ERROR) {
- nv04_fifo_cache_error(device, priv, chid, get);
- status &= ~NV_PFIFO_INTR_CACHE_ERROR;
- }
+ chid = nv_rd32(priv, NV03_PFIFO_CACHE1_PUSH1) & priv->base.max;
+ get = nv_rd32(priv, NV03_PFIFO_CACHE1_GET);
- if (status & NV_PFIFO_INTR_DMA_PUSHER) {
- nv04_fifo_dma_pusher(device, priv, chid);
- status &= ~NV_PFIFO_INTR_DMA_PUSHER;
- }
+ if (stat & NV_PFIFO_INTR_CACHE_ERROR) {
+ nv04_fifo_cache_error(device, priv, chid, get);
+ stat &= ~NV_PFIFO_INTR_CACHE_ERROR;
+ }
- if (status & NV_PFIFO_INTR_SEMAPHORE) {
- uint32_t sem;
+ if (stat & NV_PFIFO_INTR_DMA_PUSHER) {
+ nv04_fifo_dma_pusher(device, priv, chid);
+ stat &= ~NV_PFIFO_INTR_DMA_PUSHER;
+ }
- status &= ~NV_PFIFO_INTR_SEMAPHORE;
- nv_wr32(priv, NV03_PFIFO_INTR_0,
- NV_PFIFO_INTR_SEMAPHORE);
+ if (stat & NV_PFIFO_INTR_SEMAPHORE) {
+ stat &= ~NV_PFIFO_INTR_SEMAPHORE;
+ nv_wr32(priv, NV03_PFIFO_INTR_0, NV_PFIFO_INTR_SEMAPHORE);
- sem = nv_rd32(priv, NV10_PFIFO_CACHE1_SEMAPHORE);
- nv_wr32(priv, NV10_PFIFO_CACHE1_SEMAPHORE, sem | 0x1);
+ sem = nv_rd32(priv, NV10_PFIFO_CACHE1_SEMAPHORE);
+ nv_wr32(priv, NV10_PFIFO_CACHE1_SEMAPHORE, sem | 0x1);
- nv_wr32(priv, NV03_PFIFO_CACHE1_GET, get + 4);
- nv_wr32(priv, NV04_PFIFO_CACHE1_PULL0, 1);
- }
+ nv_wr32(priv, NV03_PFIFO_CACHE1_GET, get + 4);
+ nv_wr32(priv, NV04_PFIFO_CACHE1_PULL0, 1);
+ }
- if (device->card_type == NV_50) {
- if (status & 0x00000010) {
- status &= ~0x00000010;
- nv_wr32(priv, 0x002100, 0x00000010);
- }
-
- if (status & 0x40000000) {
- nv_wr32(priv, 0x002100, 0x40000000);
- nvkm_fifo_uevent(&priv->base);
- status &= ~0x40000000;
- }
+ if (device->card_type == NV_50) {
+ if (stat & 0x00000010) {
+ stat &= ~0x00000010;
+ nv_wr32(priv, 0x002100, 0x00000010);
}
- if (status) {
- nv_warn(priv, "unknown intr 0x%08x, ch %d\n",
- status, chid);
- nv_wr32(priv, NV03_PFIFO_INTR_0, status);
- status = 0;
+ if (stat & 0x40000000) {
+ nv_wr32(priv, 0x002100, 0x40000000);
+ nvkm_fifo_uevent(&priv->base);
+ stat &= ~0x40000000;
}
-
- nv_wr32(priv, NV03_PFIFO_CACHES, reassign);
}
- if (status) {
- nv_error(priv, "still angry after %d spins, halt\n", cnt);
- nv_wr32(priv, 0x002140, 0);
- nv_wr32(priv, 0x000140, 0);
+ if (stat) {
+ nv_warn(priv, "unknown intr 0x%08x\n", stat);
+ nv_mask(priv, NV03_PFIFO_INTR_EN_0, stat, 0x00000000);
+ nv_wr32(priv, NV03_PFIFO_INTR_0, stat);
}
- nv_wr32(priv, 0x000100, 0x00000100);
+ nv_wr32(priv, NV03_PFIFO_CACHES, reassign);
}
static int
const int s = 8;
const int b = mmio_vram(info, impl->bundle_size, (1 << s), access);
mmio_refn(info, 0x408004, 0x00000000, s, b);
- mmio_refn(info, 0x408008, 0x80000000 | (impl->bundle_size >> s), 0, b);
+ mmio_wr32(info, 0x408008, 0x80000000 | (impl->bundle_size >> s));
mmio_refn(info, 0x418808, 0x00000000, s, b);
- mmio_refn(info, 0x41880c, 0x80000000 | (impl->bundle_size >> s), 0, b);
+ mmio_wr32(info, 0x41880c, 0x80000000 | (impl->bundle_size >> s));
}
void
const int s = 8;
const int b = mmio_vram(info, impl->bundle_size, (1 << s), access);
mmio_refn(info, 0x408004, 0x00000000, s, b);
- mmio_refn(info, 0x408008, 0x80000000 | (impl->bundle_size >> s), 0, b);
+ mmio_wr32(info, 0x408008, 0x80000000 | (impl->bundle_size >> s));
mmio_refn(info, 0x418808, 0x00000000, s, b);
- mmio_refn(info, 0x41880c, 0x80000000 | (impl->bundle_size >> s), 0, b);
+ mmio_wr32(info, 0x41880c, 0x80000000 | (impl->bundle_size >> s));
mmio_wr32(info, 0x4064c8, (state_limit << 16) | token_limit);
}
const int s = 8;
const int b = mmio_vram(info, impl->bundle_size, (1 << s), access);
mmio_refn(info, 0x408004, 0x00000000, s, b);
- mmio_refn(info, 0x408008, 0x80000000 | (impl->bundle_size >> s), 0, b);
+ mmio_wr32(info, 0x408008, 0x80000000 | (impl->bundle_size >> s));
mmio_refn(info, 0x418e24, 0x00000000, s, b);
- mmio_refn(info, 0x418e28, 0x80000000 | (impl->bundle_size >> s), 0, b);
+ mmio_wr32(info, 0x418e28, 0x80000000 | (impl->bundle_size >> s));
mmio_wr32(info, 0x4064c8, (state_limit << 16) | token_limit);
}
u16 ent = dcb_i2c_entry(bios, idx, &ver, &len);
if (ent) {
if (ver >= 0x41) {
- if (!(nv_ro32(bios, ent) & 0x80000000))
+ u32 ent_value = nv_ro32(bios, ent);
+ u8 i2c_port = (ent_value >> 27) & 0x1f;
+ u8 dpaux_port = (ent_value >> 22) & 0x1f;
+ /* value 0x1f means unused according to DCB 4.x spec */
+ if (i2c_port == 0x1f && dpaux_port == 0x1f)
info->type = DCB_I2C_UNUSED;
else
info->type = DCB_I2C_PMGR;
(x << 16) | y);
viewport_w = crtc->mode.hdisplay;
viewport_h = (crtc->mode.vdisplay + 1) & ~1;
+ if ((rdev->family >= CHIP_BONAIRE) &&
+ (crtc->mode.flags & DRM_MODE_FLAG_INTERLACE))
+ viewport_h *= 2;
WREG32(EVERGREEN_VIEWPORT_SIZE + radeon_crtc->crtc_offset,
(viewport_w << 16) | viewport_h);
switch (msg->request & ~DP_AUX_I2C_MOT) {
case DP_AUX_NATIVE_WRITE:
case DP_AUX_I2C_WRITE:
+ /* The atom implementation only supports writes with a max payload of
+ * 12 bytes since it uses 4 bits for the total count (header + payload)
+ * in the parameter space. The atom interface supports 16 byte
+ * payloads for reads. The hw itself supports up to 16 bytes of payload.
+ */
+ if (WARN_ON_ONCE(msg->size > 12))
+ return -E2BIG;
/* tx_size needs to be 4 even for bare address packets since the atom
* table needs the info in tx_buf[3].
*/
dig_connector = radeon_connector->con_priv;
if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
(dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) {
- if (radeon_audio != 0 && ASIC_IS_DCE4(rdev) && !ASIC_IS_DCE5(rdev))
+ if (radeon_audio != 0 &&
+ drm_detect_monitor_audio(radeon_connector_edid(connector)) &&
+ ASIC_IS_DCE4(rdev) && !ASIC_IS_DCE5(rdev))
return ATOM_ENCODER_MODE_DP_AUDIO;
return ATOM_ENCODER_MODE_DP;
} else if (radeon_audio != 0) {
}
break;
case DRM_MODE_CONNECTOR_eDP:
- if (radeon_audio != 0 && ASIC_IS_DCE4(rdev) && !ASIC_IS_DCE5(rdev))
+ if (radeon_audio != 0 &&
+ drm_detect_monitor_audio(radeon_connector_edid(connector)) &&
+ ASIC_IS_DCE4(rdev) && !ASIC_IS_DCE5(rdev))
return ATOM_ENCODER_MODE_DP_AUDIO;
return ATOM_ENCODER_MODE_DP;
case DRM_MODE_CONNECTOR_DVIA:
struct radeon_connector *radeon_connector = NULL;
struct radeon_connector_atom_dig *radeon_dig_connector = NULL;
bool travis_quirk = false;
- int encoder_mode;
if (connector) {
radeon_connector = to_radeon_connector(connector);
}
break;
}
-
- encoder_mode = atombios_get_encoder_mode(encoder);
- if (radeon_audio != 0 &&
- (encoder_mode == ATOM_ENCODER_MODE_HDMI || ENCODER_MODE_IS_DP(encoder_mode)))
- radeon_audio_dpms(encoder, mode);
}
static void
struct drm_device *dev = encoder->dev;
struct radeon_device *rdev = dev->dev_private;
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
+ int encoder_mode = atombios_get_encoder_mode(encoder);
DRM_DEBUG_KMS("encoder dpms %d to mode %d, devices %08x, active_devices %08x\n",
radeon_encoder->encoder_id, mode, radeon_encoder->devices,
radeon_encoder->active_device);
+
+ if (connector && (radeon_audio != 0) &&
+ ((encoder_mode == ATOM_ENCODER_MODE_HDMI) ||
+ (ENCODER_MODE_IS_DP(encoder_mode) &&
+ drm_detect_monitor_audio(radeon_connector_edid(connector)))))
+ radeon_audio_dpms(encoder, mode);
+
switch (radeon_encoder->encoder_id) {
case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
struct drm_device *dev = encoder->dev;
struct radeon_device *rdev = dev->dev_private;
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
int encoder_mode;
radeon_encoder->pixel_clock = adjusted_mode->clock;
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3:
case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
/* handled in dpms */
- encoder_mode = atombios_get_encoder_mode(encoder);
- if (radeon_audio != 0 &&
- (encoder_mode == ATOM_ENCODER_MODE_HDMI || ENCODER_MODE_IS_DP(encoder_mode)))
- radeon_audio_mode_set(encoder, adjusted_mode);
break;
case ENCODER_OBJECT_ID_INTERNAL_DDI:
case ENCODER_OBJECT_ID_INTERNAL_DVO1:
}
atombios_apply_encoder_quirks(encoder, adjusted_mode);
+
+ encoder_mode = atombios_get_encoder_mode(encoder);
+ if (connector && (radeon_audio != 0) &&
+ ((encoder_mode == ATOM_ENCODER_MODE_HDMI) ||
+ (ENCODER_MODE_IS_DP(encoder_mode) &&
+ drm_detect_monitor_audio(radeon_connector_edid(connector)))))
+ radeon_audio_mode_set(encoder, adjusted_mode);
}
static bool
}
WREG32(GRBM_CNTL, GRBM_READ_TIMEOUT(0xff));
+ WREG32(SRBM_INT_CNTL, 0x1);
+ WREG32(SRBM_INT_ACK, 0x1);
WREG32(BIF_FB_EN, FB_READ_EN | FB_WRITE_EN);
WREG32(CP_ME2_PIPE3_INT_CNTL, 0);
/* grbm */
WREG32(GRBM_INT_CNTL, 0);
+ /* SRBM */
+ WREG32(SRBM_INT_CNTL, 0);
/* vline/vblank, etc. */
WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC0_REGISTER_OFFSET, 0);
WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC1_REGISTER_OFFSET, 0);
WREG32(DC_HPD5_INT_CONTROL, hpd5);
WREG32(DC_HPD6_INT_CONTROL, hpd6);
+ /* posting read */
+ RREG32(SRBM_STATUS);
+
return 0;
}
break;
}
break;
+ case 96:
+ DRM_ERROR("SRBM_READ_ERROR: 0x%x\n", RREG32(SRBM_READ_ERROR));
+ WREG32(SRBM_INT_ACK, 0x1);
+ break;
case 124: /* UVD */
DRM_DEBUG("IH: UVD int: 0x%08x\n", src_data);
radeon_fence_process(rdev, R600_RING_TYPE_UVD_INDEX);
#define SOFT_RESET_ORB (1 << 23)
#define SOFT_RESET_VCE (1 << 24)
+#define SRBM_READ_ERROR 0xE98
+#define SRBM_INT_CNTL 0xEA0
+#define SRBM_INT_ACK 0xEA8
+
#define VM_L2_CNTL 0x1400
#define ENABLE_L2_CACHE (1 << 0)
#define ENABLE_L2_FRAGMENT_PROCESSING (1 << 1)
#define VCE_UENC_REG_CLOCK_GATING 0x207c0
#define VCE_SYS_INT_EN 0x21300
# define VCE_SYS_INT_TRAP_INTERRUPT_EN (1 << 3)
+#define VCE_LMI_VCPU_CACHE_40BIT_BAR 0x2145c
#define VCE_LMI_CTRL2 0x21474
#define VCE_LMI_CTRL 0x21498
#define VCE_LMI_VM_CTRL 0x214a0
#include "radeon_audio.h"
#include "sid.h"
+#define DCE8_DCCG_AUDIO_DTO1_PHASE 0x05b8
+#define DCE8_DCCG_AUDIO_DTO1_MODULE 0x05bc
+
u32 dce6_endpoint_rreg(struct radeon_device *rdev,
u32 block_offset, u32 reg)
{
void dce6_hdmi_audio_set_dto(struct radeon_device *rdev,
struct radeon_crtc *crtc, unsigned int clock)
{
- /* Two dtos; generally use dto0 for HDMI */
+ /* Two dtos; generally use dto0 for HDMI */
u32 value = 0;
- if (crtc)
+ if (crtc)
value |= DCCG_AUDIO_DTO0_SOURCE_SEL(crtc->crtc_id);
WREG32(DCCG_AUDIO_DTO_SOURCE, value);
- /* Express [24MHz / target pixel clock] as an exact rational
- * number (coefficient of two integer numbers. DCCG_AUDIO_DTOx_PHASE
- * is the numerator, DCCG_AUDIO_DTOx_MODULE is the denominator
- */
- WREG32(DCCG_AUDIO_DTO0_PHASE, 24000);
- WREG32(DCCG_AUDIO_DTO0_MODULE, clock);
+ /* Express [24MHz / target pixel clock] as an exact rational
+ * number (coefficient of two integer numbers. DCCG_AUDIO_DTOx_PHASE
+ * is the numerator, DCCG_AUDIO_DTOx_MODULE is the denominator
+ */
+ WREG32(DCCG_AUDIO_DTO0_PHASE, 24000);
+ WREG32(DCCG_AUDIO_DTO0_MODULE, clock);
}
void dce6_dp_audio_set_dto(struct radeon_device *rdev,
struct radeon_crtc *crtc, unsigned int clock)
{
- /* Two dtos; generally use dto1 for DP */
+ /* Two dtos; generally use dto1 for DP */
u32 value = 0;
value |= DCCG_AUDIO_DTO_SEL;
- if (crtc)
+ if (crtc)
value |= DCCG_AUDIO_DTO0_SOURCE_SEL(crtc->crtc_id);
WREG32(DCCG_AUDIO_DTO_SOURCE, value);
- /* Express [24MHz / target pixel clock] as an exact rational
- * number (coefficient of two integer numbers. DCCG_AUDIO_DTOx_PHASE
- * is the numerator, DCCG_AUDIO_DTOx_MODULE is the denominator
- */
- WREG32(DCCG_AUDIO_DTO1_PHASE, 24000);
- WREG32(DCCG_AUDIO_DTO1_MODULE, clock);
+ /* Express [24MHz / target pixel clock] as an exact rational
+ * number (coefficient of two integer numbers. DCCG_AUDIO_DTOx_PHASE
+ * is the numerator, DCCG_AUDIO_DTOx_MODULE is the denominator
+ */
+ if (ASIC_IS_DCE8(rdev)) {
+ WREG32(DCE8_DCCG_AUDIO_DTO1_PHASE, 24000);
+ WREG32(DCE8_DCCG_AUDIO_DTO1_MODULE, clock);
+ } else {
+ WREG32(DCCG_AUDIO_DTO1_PHASE, 24000);
+ WREG32(DCCG_AUDIO_DTO1_MODULE, clock);
+ }
}
-void dce6_enable_dp_audio_packets(struct drm_encoder *encoder, bool enable)
+void dce6_dp_enable(struct drm_encoder *encoder, bool enable)
{
struct drm_device *dev = encoder->dev;
struct radeon_device *rdev = dev->dev_private;
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
- uint32_t offset;
if (!dig || !dig->afmt)
return;
- offset = dig->afmt->offset;
-
if (enable) {
- if (dig->afmt->enabled)
- return;
-
- WREG32(EVERGREEN_DP_SEC_TIMESTAMP + offset, EVERGREEN_DP_SEC_TIMESTAMP_MODE(1));
- WREG32(EVERGREEN_DP_SEC_CNTL + offset,
- EVERGREEN_DP_SEC_ASP_ENABLE | /* Audio packet transmission */
- EVERGREEN_DP_SEC_ATP_ENABLE | /* Audio timestamp packet transmission */
- EVERGREEN_DP_SEC_AIP_ENABLE | /* Audio infoframe packet transmission */
- EVERGREEN_DP_SEC_STREAM_ENABLE); /* Master enable for secondary stream engine */
- radeon_audio_enable(rdev, dig->afmt->pin, true);
+ WREG32(EVERGREEN_DP_SEC_TIMESTAMP + dig->afmt->offset,
+ EVERGREEN_DP_SEC_TIMESTAMP_MODE(1));
+ WREG32(EVERGREEN_DP_SEC_CNTL + dig->afmt->offset,
+ EVERGREEN_DP_SEC_ASP_ENABLE | /* Audio packet transmission */
+ EVERGREEN_DP_SEC_ATP_ENABLE | /* Audio timestamp packet transmission */
+ EVERGREEN_DP_SEC_AIP_ENABLE | /* Audio infoframe packet transmission */
+ EVERGREEN_DP_SEC_STREAM_ENABLE); /* Master enable for secondary stream engine */
} else {
- if (!dig->afmt->enabled)
- return;
-
- WREG32(EVERGREEN_DP_SEC_CNTL + offset, 0);
- radeon_audio_enable(rdev, dig->afmt->pin, false);
+ WREG32(EVERGREEN_DP_SEC_CNTL + dig->afmt->offset, 0);
}
dig->afmt->enabled = enable;
}
WREG32(GRBM_CNTL, GRBM_READ_TIMEOUT(0xff));
+ WREG32(SRBM_INT_CNTL, 0x1);
+ WREG32(SRBM_INT_ACK, 0x1);
evergreen_fix_pci_max_read_req_size(rdev);
tmp = RREG32(DMA_CNTL) & ~TRAP_ENABLE;
WREG32(DMA_CNTL, tmp);
WREG32(GRBM_INT_CNTL, 0);
+ WREG32(SRBM_INT_CNTL, 0);
WREG32(INT_MASK + EVERGREEN_CRTC0_REGISTER_OFFSET, 0);
WREG32(INT_MASK + EVERGREEN_CRTC1_REGISTER_OFFSET, 0);
if (rdev->num_crtc >= 4) {
WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, afmt5);
WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, afmt6);
+ /* posting read */
+ RREG32(SRBM_STATUS);
+
return 0;
}
DRM_ERROR("Unhandled interrupt: %d %d\n", src_id, src_data);
break;
}
+ case 96:
+ DRM_ERROR("SRBM_READ_ERROR: 0x%x\n", RREG32(SRBM_READ_ERROR));
+ WREG32(SRBM_INT_ACK, 0x1);
+ break;
case 124: /* UVD */
DRM_DEBUG("IH: UVD int: 0x%08x\n", src_data);
radeon_fence_process(rdev, R600_RING_TYPE_UVD_INDEX);
}
void dce4_dp_audio_set_dto(struct radeon_device *rdev,
- struct radeon_crtc *crtc, unsigned int clock)
+ struct radeon_crtc *crtc, unsigned int clock)
{
u32 value;
* is the numerator, DCCG_AUDIO_DTOx_MODULE is the denominator
*/
WREG32(DCCG_AUDIO_DTO1_PHASE, 24000);
- WREG32(DCCG_AUDIO_DTO1_MODULE, rdev->clock.max_pixel_clock * 10);
+ WREG32(DCCG_AUDIO_DTO1_MODULE, clock);
}
void dce4_set_vbi_packet(struct drm_encoder *encoder, u32 offset)
struct drm_device *dev = encoder->dev;
struct radeon_device *rdev = dev->dev_private;
- WREG32(HDMI_INFOFRAME_CONTROL0 + offset,
- HDMI_AUDIO_INFO_SEND | /* enable audio info frames (frames won't be set until audio is enabled) */
- HDMI_AUDIO_INFO_CONT); /* required for audio info values to be updated */
-
WREG32(AFMT_INFOFRAME_CONTROL0 + offset,
AFMT_AUDIO_INFO_UPDATE); /* required for audio info values to be updated */
- WREG32(HDMI_INFOFRAME_CONTROL1 + offset,
- HDMI_AUDIO_INFO_LINE(2)); /* anything other than 0 */
-
- WREG32(HDMI_AUDIO_PACKET_CONTROL + offset,
- HDMI_AUDIO_DELAY_EN(1) | /* set the default audio delay */
- HDMI_AUDIO_PACKETS_PER_LINE(3)); /* should be suffient for all audio modes and small enough for all hblanks */
-
WREG32(AFMT_60958_0 + offset,
AFMT_60958_CS_CHANNEL_NUMBER_L(1));
if (!dig || !dig->afmt)
return;
- /* Silent, r600_hdmi_enable will raise WARN for us */
- if (enable && dig->afmt->enabled)
- return;
- if (!enable && !dig->afmt->enabled)
- return;
+ if (enable) {
+ WREG32(HDMI_INFOFRAME_CONTROL1 + dig->afmt->offset,
+ HDMI_AUDIO_INFO_LINE(2)); /* anything other than 0 */
+
+ WREG32(HDMI_AUDIO_PACKET_CONTROL + dig->afmt->offset,
+ HDMI_AUDIO_DELAY_EN(1) | /* set the default audio delay */
+ HDMI_AUDIO_PACKETS_PER_LINE(3)); /* should be suffient for all audio modes and small enough for all hblanks */
- if (!enable && dig->afmt->pin) {
- radeon_audio_enable(rdev, dig->afmt->pin, 0);
- dig->afmt->pin = NULL;
+ WREG32(HDMI_INFOFRAME_CONTROL0 + dig->afmt->offset,
+ HDMI_AUDIO_INFO_SEND | /* enable audio info frames (frames won't be set until audio is enabled) */
+ HDMI_AUDIO_INFO_CONT); /* required for audio info values to be updated */
+ } else {
+ WREG32(HDMI_INFOFRAME_CONTROL0 + dig->afmt->offset, 0);
}
dig->afmt->enabled = enable;
enable ? "En" : "Dis", dig->afmt->offset, radeon_encoder->encoder_id);
}
-void evergreen_enable_dp_audio_packets(struct drm_encoder *encoder, bool enable)
+void evergreen_dp_enable(struct drm_encoder *encoder, bool enable)
{
struct drm_device *dev = encoder->dev;
struct radeon_device *rdev = dev->dev_private;
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
- uint32_t offset;
if (!dig || !dig->afmt)
return;
- offset = dig->afmt->offset;
-
if (enable) {
struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
struct radeon_connector_atom_dig *dig_connector;
uint32_t val;
- if (dig->afmt->enabled)
- return;
-
- WREG32(EVERGREEN_DP_SEC_TIMESTAMP + offset, EVERGREEN_DP_SEC_TIMESTAMP_MODE(1));
+ WREG32(EVERGREEN_DP_SEC_TIMESTAMP + dig->afmt->offset,
+ EVERGREEN_DP_SEC_TIMESTAMP_MODE(1));
if (radeon_connector->con_priv) {
dig_connector = radeon_connector->con_priv;
- val = RREG32(EVERGREEN_DP_SEC_AUD_N + offset);
+ val = RREG32(EVERGREEN_DP_SEC_AUD_N + dig->afmt->offset);
val &= ~EVERGREEN_DP_SEC_N_BASE_MULTIPLE(0xf);
if (dig_connector->dp_clock == 162000)
else
val |= EVERGREEN_DP_SEC_N_BASE_MULTIPLE(5);
- WREG32(EVERGREEN_DP_SEC_AUD_N + offset, val);
+ WREG32(EVERGREEN_DP_SEC_AUD_N + dig->afmt->offset, val);
}
- WREG32(EVERGREEN_DP_SEC_CNTL + offset,
+ WREG32(EVERGREEN_DP_SEC_CNTL + dig->afmt->offset,
EVERGREEN_DP_SEC_ASP_ENABLE | /* Audio packet transmission */
EVERGREEN_DP_SEC_ATP_ENABLE | /* Audio timestamp packet transmission */
EVERGREEN_DP_SEC_AIP_ENABLE | /* Audio infoframe packet transmission */
EVERGREEN_DP_SEC_STREAM_ENABLE); /* Master enable for secondary stream engine */
- radeon_audio_enable(rdev, dig->afmt->pin, 0xf);
} else {
- if (!dig->afmt->enabled)
- return;
-
- WREG32(EVERGREEN_DP_SEC_CNTL + offset, 0);
- radeon_audio_enable(rdev, dig->afmt->pin, 0);
+ WREG32(EVERGREEN_DP_SEC_CNTL + dig->afmt->offset, 0);
}
dig->afmt->enabled = enable;
#define SOFT_RESET_REGBB (1 << 22)
#define SOFT_RESET_ORB (1 << 23)
+#define SRBM_READ_ERROR 0xE98
+#define SRBM_INT_CNTL 0xEA0
+#define SRBM_INT_ACK 0xEA8
+
/* display watermarks */
#define DC_LB_MEMORY_SPLIT 0x6b0c
#define PRIORITY_A_CNT 0x6b18
}
WREG32(GRBM_CNTL, GRBM_READ_TIMEOUT(0xff));
+ WREG32(SRBM_INT_CNTL, 0x1);
+ WREG32(SRBM_INT_ACK, 0x1);
evergreen_fix_pci_max_read_req_size(rdev);
if ((rdev->config.cayman.max_backends_per_se == 1) &&
(rdev->flags & RADEON_IS_IGP)) {
- if ((disabled_rb_mask & 3) == 1) {
- /* RB0 disabled, RB1 enabled */
- tmp = 0x11111111;
- } else {
+ if ((disabled_rb_mask & 3) == 2) {
/* RB1 disabled, RB0 enabled */
tmp = 0x00000000;
+ } else {
+ /* RB0 disabled, RB1 enabled */
+ tmp = 0x11111111;
}
} else {
tmp = gb_addr_config & NUM_PIPES_MASK;
#define SOFT_RESET_REGBB (1 << 22)
#define SOFT_RESET_ORB (1 << 23)
+#define SRBM_READ_ERROR 0xE98
+#define SRBM_INT_CNTL 0xEA0
+#define SRBM_INT_ACK 0xEA8
+
#define SRBM_STATUS2 0x0EC4
#define DMA_BUSY (1 << 5)
#define DMA1_BUSY (1 << 6)
tmp |= RADEON_FP2_DETECT_MASK;
}
WREG32(RADEON_GEN_INT_CNTL, tmp);
+
+ /* read back to post the write */
+ RREG32(RADEON_GEN_INT_CNTL);
+
return 0;
}
WREG32(RV770_CG_THERMAL_INT, thermal_int);
}
+ /* posting read */
+ RREG32(R_000E50_SRBM_STATUS);
+
return 0;
}
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
radeon_crtc = to_radeon_crtc(crtc);
if (crtc->enabled && radeon_crtc->enabled && radeon_crtc->hw_mode.clock) {
- vrefresh = radeon_crtc->hw_mode.vrefresh;
+ vrefresh = drm_mode_vrefresh(&radeon_crtc->hw_mode);
break;
}
}
if (!dig || !dig->afmt)
return;
- /* Silent, r600_hdmi_enable will raise WARN for us */
- if (enable && dig->afmt->enabled)
- return;
- if (!enable && !dig->afmt->enabled)
- return;
-
- if (!enable && dig->afmt->pin) {
- radeon_audio_enable(rdev, dig->afmt->pin, 0);
- dig->afmt->pin = NULL;
- }
-
/* Older chipsets require setting HDMI and routing manually */
if (!ASIC_IS_DCE3(rdev)) {
if (enable)
int new_active_crtc_count;
u32 current_active_crtcs;
int current_active_crtc_count;
+ bool single_display;
struct radeon_dpm_dynamic_state dyn_state;
struct radeon_dpm_fan fan;
u32 tdp_limit;
struct drm_display_mode *mode);
void r600_hdmi_enable(struct drm_encoder *encoder, bool enable);
void evergreen_hdmi_enable(struct drm_encoder *encoder, bool enable);
-void evergreen_enable_dp_audio_packets(struct drm_encoder *encoder, bool enable);
-void dce6_enable_dp_audio_packets(struct drm_encoder *encoder, bool enable);
+void evergreen_dp_enable(struct drm_encoder *encoder, bool enable);
+void dce6_dp_enable(struct drm_encoder *encoder, bool enable);
static const u32 pin_offsets[7] =
{
.set_avi_packet = evergreen_set_avi_packet,
.set_audio_packet = dce4_set_audio_packet,
.mode_set = radeon_audio_dp_mode_set,
- .dpms = evergreen_enable_dp_audio_packets,
+ .dpms = evergreen_dp_enable,
};
static struct radeon_audio_funcs dce6_hdmi_funcs = {
.set_avi_packet = evergreen_set_avi_packet,
.set_audio_packet = dce4_set_audio_packet,
.mode_set = radeon_audio_dp_mode_set,
- .dpms = dce6_enable_dp_audio_packets,
+ .dpms = dce6_dp_enable,
};
static void radeon_audio_interface_init(struct radeon_device *rdev)
}
void radeon_audio_detect(struct drm_connector *connector,
- enum drm_connector_status status)
+ enum drm_connector_status status)
{
struct radeon_device *rdev;
struct radeon_encoder *radeon_encoder;
else
radeon_encoder->audio = rdev->audio.hdmi_funcs;
- radeon_audio_write_speaker_allocation(connector->encoder);
- radeon_audio_write_sad_regs(connector->encoder);
- if (connector->encoder->crtc)
- radeon_audio_write_latency_fields(connector->encoder,
- &connector->encoder->crtc->mode);
+ dig->afmt->pin = radeon_audio_get_pin(connector->encoder);
radeon_audio_enable(rdev, dig->afmt->pin, 0xf);
} else {
radeon_audio_enable(rdev, dig->afmt->pin, 0);
+ dig->afmt->pin = NULL;
}
}
* update the info frames with the data from the current display mode
*/
static void radeon_audio_hdmi_mode_set(struct drm_encoder *encoder,
- struct drm_display_mode *mode)
+ struct drm_display_mode *mode)
{
- struct radeon_device *rdev = encoder->dev->dev_private;
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
if (!dig || !dig->afmt)
return;
- /* disable audio prior to setting up hw */
- dig->afmt->pin = radeon_audio_get_pin(encoder);
- radeon_audio_enable(rdev, dig->afmt->pin, 0);
+ radeon_audio_set_mute(encoder, true);
+ radeon_audio_write_speaker_allocation(encoder);
+ radeon_audio_write_sad_regs(encoder);
+ radeon_audio_write_latency_fields(encoder, mode);
radeon_audio_set_dto(encoder, mode->clock);
radeon_audio_set_vbi_packet(encoder);
radeon_hdmi_set_color_depth(encoder);
- radeon_audio_set_mute(encoder, false);
radeon_audio_update_acr(encoder, mode->clock);
radeon_audio_set_audio_packet(encoder);
radeon_audio_select_pin(encoder);
if (radeon_audio_set_avi_packet(encoder, mode) < 0)
return;
- /* enable audio after to setting up hw */
- radeon_audio_enable(rdev, dig->afmt->pin, 0xf);
+ radeon_audio_set_mute(encoder, false);
}
static void radeon_audio_dp_mode_set(struct drm_encoder *encoder,
struct radeon_device *rdev = dev->dev_private;
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
+ struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+ struct radeon_connector_atom_dig *dig_connector =
+ radeon_connector->con_priv;
if (!dig || !dig->afmt)
return;
- /* disable audio prior to setting up hw */
- dig->afmt->pin = radeon_audio_get_pin(encoder);
- radeon_audio_enable(rdev, dig->afmt->pin, 0);
-
- radeon_audio_set_dto(encoder, rdev->clock.default_dispclk * 10);
+ radeon_audio_write_speaker_allocation(encoder);
+ radeon_audio_write_sad_regs(encoder);
+ radeon_audio_write_latency_fields(encoder, mode);
+ if (rdev->clock.dp_extclk || ASIC_IS_DCE5(rdev))
+ radeon_audio_set_dto(encoder, rdev->clock.default_dispclk * 10);
+ else
+ radeon_audio_set_dto(encoder, dig_connector->dp_clock);
radeon_audio_set_audio_packet(encoder);
radeon_audio_select_pin(encoder);
if (radeon_audio_set_avi_packet(encoder, mode) < 0)
return;
-
- /* enable audio after to setting up hw */
- radeon_audio_enable(rdev, dig->afmt->pin, 0xf);
}
void radeon_audio_mode_set(struct drm_encoder *encoder,
static bool radeon_read_bios(struct radeon_device *rdev)
{
- uint8_t __iomem *bios;
+ uint8_t __iomem *bios, val1, val2;
size_t size;
rdev->bios = NULL;
return false;
}
- if (size == 0 || bios[0] != 0x55 || bios[1] != 0xaa) {
+ val1 = readb(&bios[0]);
+ val2 = readb(&bios[1]);
+
+ if (size == 0 || val1 != 0x55 || val2 != 0xaa) {
pci_unmap_rom(rdev->pdev, bios);
return false;
}
- rdev->bios = kmemdup(bios, size, GFP_KERNEL);
+ rdev->bios = kzalloc(size, GFP_KERNEL);
if (rdev->bios == NULL) {
pci_unmap_rom(rdev->pdev, bios);
return false;
}
+ memcpy_fromio(rdev->bios, bios, size);
pci_unmap_rom(rdev->pdev, bios);
return true;
}
u32 ring = RADEON_CS_RING_GFX;
s32 priority = 0;
+ INIT_LIST_HEAD(&p->validated);
+
if (!cs->num_chunks) {
return 0;
}
+
/* get chunks */
- INIT_LIST_HEAD(&p->validated);
p->idx = 0;
p->ib.sa_bo = NULL;
p->const_ib.sa_bo = NULL;
struct radeon_cs_chunk *ib_chunk = p->chunk_ib;
struct radeon_device *rdev = p->rdev;
uint32_t header;
+ int ret = 0, i;
if (idx >= ib_chunk->length_dw) {
DRM_ERROR("Can not parse packet at %d after CS end %d !\n",
break;
default:
DRM_ERROR("Unknown packet type %d at %d !\n", pkt->type, idx);
- return -EINVAL;
+ ret = -EINVAL;
+ goto dump_ib;
}
if ((pkt->count + 1 + pkt->idx) >= ib_chunk->length_dw) {
DRM_ERROR("Packet (%d:%d:%d) end after CS buffer (%d) !\n",
pkt->idx, pkt->type, pkt->count, ib_chunk->length_dw);
- return -EINVAL;
+ ret = -EINVAL;
+ goto dump_ib;
}
return 0;
+
+dump_ib:
+ for (i = 0; i < ib_chunk->length_dw; i++) {
+ if (i == idx)
+ printk("\t0x%08x <---\n", radeon_get_ib_value(p, i));
+ else
+ printk("\t0x%08x\n", radeon_get_ib_value(p, i));
+ }
+ return ret;
}
/**
(rdev->pdev->subsystem_vendor == 0x1734) &&
(rdev->pdev->subsystem_device == 0x1107))
use_bl = false;
+/* Older PPC macs use on-GPU backlight controller */
+#ifndef CONFIG_PPC_PMAC
/* disable native backlight control on older asics */
else if (rdev->family < CHIP_R600)
use_bl = false;
+#endif
else
use_bl = true;
}
return test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags);
}
+struct radeon_wait_cb {
+ struct fence_cb base;
+ struct task_struct *task;
+};
+
+static void
+radeon_fence_wait_cb(struct fence *fence, struct fence_cb *cb)
+{
+ struct radeon_wait_cb *wait =
+ container_of(cb, struct radeon_wait_cb, base);
+
+ wake_up_process(wait->task);
+}
+
static signed long radeon_fence_default_wait(struct fence *f, bool intr,
signed long t)
{
struct radeon_fence *fence = to_radeon_fence(f);
struct radeon_device *rdev = fence->rdev;
- bool signaled;
+ struct radeon_wait_cb cb;
- fence_enable_sw_signaling(&fence->base);
+ cb.task = current;
- /*
- * This function has to return -EDEADLK, but cannot hold
- * exclusive_lock during the wait because some callers
- * may already hold it. This means checking needs_reset without
- * lock, and not fiddling with any gpu internals.
- *
- * The callback installed with fence_enable_sw_signaling will
- * run before our wait_event_*timeout call, so we will see
- * both the signaled fence and the changes to needs_reset.
- */
+ if (fence_add_callback(f, &cb.base, radeon_fence_wait_cb))
+ return t;
+
+ while (t > 0) {
+ if (intr)
+ set_current_state(TASK_INTERRUPTIBLE);
+ else
+ set_current_state(TASK_UNINTERRUPTIBLE);
+
+ /*
+ * radeon_test_signaled must be called after
+ * set_current_state to prevent a race with wake_up_process
+ */
+ if (radeon_test_signaled(fence))
+ break;
+
+ if (rdev->needs_reset) {
+ t = -EDEADLK;
+ break;
+ }
+
+ t = schedule_timeout(t);
+
+ if (t > 0 && intr && signal_pending(current))
+ t = -ERESTARTSYS;
+ }
+
+ __set_current_state(TASK_RUNNING);
+ fence_remove_callback(f, &cb.base);
- if (intr)
- t = wait_event_interruptible_timeout(rdev->fence_queue,
- ((signaled = radeon_test_signaled(fence)) ||
- rdev->needs_reset), t);
- else
- t = wait_event_timeout(rdev->fence_queue,
- ((signaled = radeon_test_signaled(fence)) ||
- rdev->needs_reset), t);
-
- if (t > 0 && !signaled)
- return -EDEADLK;
return t;
}
.compute_vmid_bitmap = 0xFF00,
.first_compute_pipe = 1,
- .compute_pipe_count = 8 - 1,
+ .compute_pipe_count = 4 - 1,
};
radeon_doorbell_get_kfd_info(rdev,
it = interval_tree_iter_first(&rmn->objects, start, end);
while (it) {
struct radeon_bo *bo;
- struct fence *fence;
int r;
bo = container_of(it, struct radeon_bo, mn_it);
continue;
}
- fence = reservation_object_get_excl(bo->tbo.resv);
- if (fence) {
- r = radeon_fence_wait((struct radeon_fence *)fence, false);
- if (r)
- DRM_ERROR("(%d) failed to wait for user bo\n", r);
- }
+ r = reservation_object_wait_timeout_rcu(bo->tbo.resv, true,
+ false, MAX_SCHEDULE_TIMEOUT);
+ if (r)
+ DRM_ERROR("(%d) failed to wait for user bo\n", r);
radeon_ttm_placement_from_domain(bo, RADEON_GEM_DOMAIN_CPU);
r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false);
else
rbo->placements[i].lpfn = 0;
}
-
- /*
- * Use two-ended allocation depending on the buffer size to
- * improve fragmentation quality.
- * 512kb was measured as the most optimal number.
- */
- if (rbo->tbo.mem.size > 512 * 1024) {
- for (i = 0; i < c; i++) {
- rbo->placements[i].flags |= TTM_PL_FLAG_TOPDOWN;
- }
- }
}
int radeon_bo_create(struct radeon_device *rdev,
radeon_pm_compute_clocks(rdev);
}
-static struct radeon_ps *radeon_dpm_pick_power_state(struct radeon_device *rdev,
- enum radeon_pm_state_type dpm_state)
+static bool radeon_dpm_single_display(struct radeon_device *rdev)
{
- int i;
- struct radeon_ps *ps;
- u32 ui_class;
bool single_display = (rdev->pm.dpm.new_active_crtc_count < 2) ?
true : false;
single_display = false;
}
+ /* 120hz tends to be problematic even if they are under the
+ * vblank limit.
+ */
+ if (single_display && (r600_dpm_get_vrefresh(rdev) >= 120))
+ single_display = false;
+
+ return single_display;
+}
+
+static struct radeon_ps *radeon_dpm_pick_power_state(struct radeon_device *rdev,
+ enum radeon_pm_state_type dpm_state)
+{
+ int i;
+ struct radeon_ps *ps;
+ u32 ui_class;
+ bool single_display = radeon_dpm_single_display(rdev);
+
/* certain older asics have a separare 3D performance state,
* so try that first if the user selected performance
*/
struct radeon_ps *ps;
enum radeon_pm_state_type dpm_state;
int ret;
+ bool single_display = radeon_dpm_single_display(rdev);
/* if dpm init failed */
if (!rdev->pm.dpm_enabled)
/* vce just modifies an existing state so force a change */
if (ps->vce_active != rdev->pm.dpm.vce_active)
goto force;
+ /* user has made a display change (such as timing) */
+ if (rdev->pm.dpm.single_display != single_display)
+ goto force;
if ((rdev->family < CHIP_BARTS) || (rdev->flags & RADEON_IS_IGP)) {
/* for pre-BTC and APUs if the num crtcs changed but state is the same,
* all we need to do is update the display configuration.
rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs;
rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count;
+ rdev->pm.dpm.single_display = single_display;
/* wait for the rings to drain */
for (i = 0; i < RADEON_NUM_RINGS; i++) {
seq_printf(m, "%u free dwords in ring\n", ring->ring_free_dw);
seq_printf(m, "%u dwords in ring\n", count);
- if (!ring->ready)
+ if (!ring->ring)
return 0;
/* print 8 dw before current rptr as often it's the last executed
enum dma_data_direction direction = write ?
DMA_BIDIRECTIONAL : DMA_TO_DEVICE;
+ /* double check that we don't free the table twice */
+ if (!ttm->sg->sgl)
+ return;
+
/* free the sg table and pages again */
dma_unmap_sg(rdev->dev, ttm->sg->sgl, ttm->sg->nents, direction);
WREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL, hpd2);
if (ASIC_IS_DCE2(rdev))
WREG32(R_007408_HDMI0_AUDIO_PACKET_CONTROL, hdmi0);
+
+ /* posting read */
+ RREG32(R_000040_GEN_INT_CNTL);
+
return 0;
}
}
WREG32(GRBM_CNTL, GRBM_READ_TIMEOUT(0xff));
+ WREG32(SRBM_INT_CNTL, 1);
+ WREG32(SRBM_INT_ACK, 1);
evergreen_fix_pci_max_read_req_size(rdev);
switch (pkt.type) {
case RADEON_PACKET_TYPE0:
dev_err(rdev->dev, "Packet0 not allowed!\n");
- for (i = 0; i < ib->length_dw; i++) {
- if (i == idx)
- printk("\t0x%08x <---\n", ib->ptr[i]);
- else
- printk("\t0x%08x\n", ib->ptr[i]);
- }
ret = -EINVAL;
break;
case RADEON_PACKET_TYPE2:
ret = -EINVAL;
break;
}
- if (ret)
+ if (ret) {
+ for (i = 0; i < ib->length_dw; i++) {
+ if (i == idx)
+ printk("\t0x%08x <---\n", ib->ptr[i]);
+ else
+ printk("\t0x%08x\n", ib->ptr[i]);
+ }
break;
+ }
} while (idx < ib->length_dw);
return ret;
tmp = RREG32(DMA_CNTL + DMA1_REGISTER_OFFSET) & ~TRAP_ENABLE;
WREG32(DMA_CNTL + DMA1_REGISTER_OFFSET, tmp);
WREG32(GRBM_INT_CNTL, 0);
+ WREG32(SRBM_INT_CNTL, 0);
if (rdev->num_crtc >= 2) {
WREG32(INT_MASK + EVERGREEN_CRTC0_REGISTER_OFFSET, 0);
WREG32(INT_MASK + EVERGREEN_CRTC1_REGISTER_OFFSET, 0);
WREG32(CG_THERMAL_INT, thermal_int);
+ /* posting read */
+ RREG32(SRBM_STATUS);
+
return 0;
}
break;
}
break;
+ case 96:
+ DRM_ERROR("SRBM_READ_ERROR: 0x%x\n", RREG32(SRBM_READ_ERROR));
+ WREG32(SRBM_INT_ACK, 0x1);
+ break;
case 124: /* UVD */
DRM_DEBUG("IH: UVD int: 0x%08x\n", src_data);
radeon_fence_process(rdev, R600_RING_TYPE_UVD_INDEX);
WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~UPLL_BYPASS_EN_MASK);
if (!vclk || !dclk) {
- /* keep the Bypass mode, put PLL to sleep */
- WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_SLEEP_MASK, ~UPLL_SLEEP_MASK);
+ /* keep the Bypass mode */
return 0;
}
/* set VCO_MODE to 1 */
WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_VCO_MODE_MASK, ~UPLL_VCO_MODE_MASK);
- /* toggle UPLL_SLEEP to 1 then back to 0 */
- WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_SLEEP_MASK, ~UPLL_SLEEP_MASK);
+ /* disable sleep mode */
WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_SLEEP_MASK);
/* deassert UPLL_RESET */
#define CC_SYS_RB_BACKEND_DISABLE 0xe80
#define GC_USER_SYS_RB_BACKEND_DISABLE 0xe84
+#define SRBM_READ_ERROR 0xE98
+#define SRBM_INT_CNTL 0xEA0
+#define SRBM_INT_ACK 0xEA8
+
#define SRBM_STATUS2 0x0EC4
#define DMA_BUSY (1 << 5)
#define DMA1_BUSY (1 << 6)
#define DCCG_AUDIO_DTO0_PHASE 0x05b0
#define DCCG_AUDIO_DTO0_MODULE 0x05b4
-#define DCCG_AUDIO_DTO1_PHASE 0x05b8
-#define DCCG_AUDIO_DTO1_MODULE 0x05bc
+#define DCCG_AUDIO_DTO1_PHASE 0x05c0
+#define DCCG_AUDIO_DTO1_MODULE 0x05c4
#define AFMT_AUDIO_SRC_CONTROL 0x713c
#define AFMT_AUDIO_SRC_SELECT(x) (((x) & 7) << 0)
WREG32(VCE_LMI_SWAP_CNTL1, 0);
WREG32(VCE_LMI_VM_CTRL, 0);
+ WREG32(VCE_LMI_VCPU_CACHE_40BIT_BAR, addr >> 8);
+
+ addr &= 0xff;
size = RADEON_GPU_PAGE_ALIGN(rdev->vce_fw->size);
WREG32(VCE_VCPU_CACHE_OFFSET0, addr & 0x7fffffff);
WREG32(VCE_VCPU_CACHE_SIZE0, size);
crtc->state = NULL;
state = kzalloc(sizeof(*state), GFP_KERNEL);
- if (state)
+ if (state) {
crtc->state = &state->base;
+ crtc->state->crtc = crtc;
+ }
}
static struct drm_crtc_state *
return NULL;
copy->base.mode_changed = false;
+ copy->base.active_changed = false;
copy->base.planes_changed = false;
copy->base.event = NULL;
/* program display mode */
tegra_dc_set_timings(dc, mode);
- if (dc->soc->supports_border_color)
- tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR);
-
/* interlacing isn't supported yet, so disable it */
if (dc->soc->supports_interlacing) {
value = tegra_dc_readl(dc, DC_DISP_INTERLACE_CONTROL);
static void tegra_crtc_prepare(struct drm_crtc *crtc)
{
- struct tegra_dc *dc = to_tegra_dc(crtc);
- unsigned int syncpt;
- unsigned long value;
-
drm_crtc_vblank_off(crtc);
-
- if (dc->pipe)
- syncpt = SYNCPT_VBLANK1;
- else
- syncpt = SYNCPT_VBLANK0;
-
- /* initialize display controller */
- tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
- tegra_dc_writel(dc, 0x100 | syncpt, DC_CMD_CONT_SYNCPT_VSYNC);
-
- value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | WIN_A_OF_INT;
- tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);
-
- value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
- WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
- tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);
-
- /* initialize timer */
- value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) |
- WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20);
- tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY);
-
- value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) |
- WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1);
- tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER);
-
- value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT;
- tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
-
- value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT;
- tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
}
static void tegra_crtc_commit(struct drm_crtc *crtc)
struct tegra_drm *tegra = drm->dev_private;
struct drm_plane *primary = NULL;
struct drm_plane *cursor = NULL;
+ unsigned int syncpt;
+ u32 value;
int err;
if (tegra->domain) {
goto cleanup;
}
+ /* initialize display controller */
+ if (dc->pipe)
+ syncpt = SYNCPT_VBLANK1;
+ else
+ syncpt = SYNCPT_VBLANK0;
+
+ tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
+ tegra_dc_writel(dc, 0x100 | syncpt, DC_CMD_CONT_SYNCPT_VSYNC);
+
+ value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT | WIN_A_OF_INT;
+ tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);
+
+ value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
+ WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
+ tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);
+
+ /* initialize timer */
+ value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) |
+ WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20);
+ tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY);
+
+ value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) |
+ WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1);
+ tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER);
+
+ value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT;
+ tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
+
+ value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT;
+ tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
+
+ if (dc->soc->supports_border_color)
+ tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR);
+
return 0;
cleanup:
h_back_porch = mode->htotal - mode->hsync_end;
h_front_porch = mode->hsync_start - mode->hdisplay;
+ err = clk_set_rate(hdmi->clk, pclk);
+ if (err < 0) {
+ dev_err(hdmi->dev, "failed to set HDMI clock frequency: %d\n",
+ err);
+ }
+
+ DRM_DEBUG_KMS("HDMI clock rate: %lu Hz\n", clk_get_rate(hdmi->clk));
+
/* power up sequence */
value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_PLL0);
value &= ~SOR_PLL_PDBG;
pr_err(" has_type: %d\n", man->has_type);
pr_err(" use_type: %d\n", man->use_type);
pr_err(" flags: 0x%08X\n", man->flags);
- pr_err(" gpu_offset: 0x%08lX\n", man->gpu_offset);
+ pr_err(" gpu_offset: 0x%08llX\n", man->gpu_offset);
pr_err(" size: %llu\n", man->size);
pr_err(" available_caching: 0x%08X\n", man->available_caching);
pr_err(" default_caching: 0x%08X\n", man->default_caching);
goto out_err1;
}
- ret = ttm_bo_init_mm(&dev_priv->bdev, TTM_PL_VRAM,
- (dev_priv->vram_size >> PAGE_SHIFT));
- if (unlikely(ret != 0)) {
- DRM_ERROR("Failed initializing memory manager for VRAM.\n");
- goto out_err2;
- }
-
- dev_priv->has_gmr = true;
- if (((dev_priv->capabilities & (SVGA_CAP_GMR | SVGA_CAP_GMR2)) == 0) ||
- refuse_dma || ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_GMR,
- VMW_PL_GMR) != 0) {
- DRM_INFO("No GMR memory available. "
- "Graphics memory resources are very limited.\n");
- dev_priv->has_gmr = false;
- }
-
- if (dev_priv->capabilities & SVGA_CAP_GBOBJECTS) {
- dev_priv->has_mob = true;
- if (ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_MOB,
- VMW_PL_MOB) != 0) {
- DRM_INFO("No MOB memory available. "
- "3D will be disabled.\n");
- dev_priv->has_mob = false;
- }
- }
-
dev_priv->mmio_mtrr = arch_phys_wc_add(dev_priv->mmio_start,
dev_priv->mmio_size);
goto out_no_fman;
}
+
+ ret = ttm_bo_init_mm(&dev_priv->bdev, TTM_PL_VRAM,
+ (dev_priv->vram_size >> PAGE_SHIFT));
+ if (unlikely(ret != 0)) {
+ DRM_ERROR("Failed initializing memory manager for VRAM.\n");
+ goto out_no_vram;
+ }
+
+ dev_priv->has_gmr = true;
+ if (((dev_priv->capabilities & (SVGA_CAP_GMR | SVGA_CAP_GMR2)) == 0) ||
+ refuse_dma || ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_GMR,
+ VMW_PL_GMR) != 0) {
+ DRM_INFO("No GMR memory available. "
+ "Graphics memory resources are very limited.\n");
+ dev_priv->has_gmr = false;
+ }
+
+ if (dev_priv->capabilities & SVGA_CAP_GBOBJECTS) {
+ dev_priv->has_mob = true;
+ if (ttm_bo_init_mm(&dev_priv->bdev, VMW_PL_MOB,
+ VMW_PL_MOB) != 0) {
+ DRM_INFO("No MOB memory available. "
+ "3D will be disabled.\n");
+ dev_priv->has_mob = false;
+ }
+ }
+
vmw_kms_save_vga(dev_priv);
/* Start kms and overlay systems, needs fifo. */
vmw_kms_close(dev_priv);
out_no_kms:
vmw_kms_restore_vga(dev_priv);
+ if (dev_priv->has_mob)
+ (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB);
+ if (dev_priv->has_gmr)
+ (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR);
+ (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM);
+out_no_vram:
vmw_fence_manager_takedown(dev_priv->fman);
out_no_fman:
if (dev_priv->capabilities & SVGA_CAP_IRQMASK)
iounmap(dev_priv->mmio_virt);
out_err3:
arch_phys_wc_del(dev_priv->mmio_mtrr);
- if (dev_priv->has_mob)
- (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB);
- if (dev_priv->has_gmr)
- (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR);
- (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM);
-out_err2:
(void)ttm_bo_device_release(&dev_priv->bdev);
out_err1:
vmw_ttm_global_release(dev_priv);
}
vmw_kms_close(dev_priv);
vmw_overlay_close(dev_priv);
+
+ if (dev_priv->has_mob)
+ (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB);
+ if (dev_priv->has_gmr)
+ (void)ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR);
+ (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM);
+
vmw_fence_manager_takedown(dev_priv->fman);
if (dev_priv->capabilities & SVGA_CAP_IRQMASK)
drm_irq_uninstall(dev_priv->dev);
ttm_object_device_release(&dev_priv->tdev);
iounmap(dev_priv->mmio_virt);
arch_phys_wc_del(dev_priv->mmio_mtrr);
- if (dev_priv->has_mob)
- (void) ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_MOB);
- if (dev_priv->has_gmr)
- (void)ttm_bo_clean_mm(&dev_priv->bdev, VMW_PL_GMR);
- (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM);
(void)ttm_bo_device_release(&dev_priv->bdev);
vmw_ttm_global_release(dev_priv);
{
struct drm_device *dev = pci_get_drvdata(pdev);
+ pci_disable_device(pdev);
drm_put_dev(dev);
}
ret = vmw_user_dmabuf_lookup(sw_context->fp->tfile, handle, &vmw_bo);
if (unlikely(ret != 0)) {
DRM_ERROR("Could not find or use MOB buffer.\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out_no_reloc;
}
bo = &vmw_bo->base;
out_no_reloc:
vmw_dmabuf_unreference(&vmw_bo);
- vmw_bo_p = NULL;
+ *vmw_bo_p = NULL;
return ret;
}
ret = vmw_user_dmabuf_lookup(sw_context->fp->tfile, handle, &vmw_bo);
if (unlikely(ret != 0)) {
DRM_ERROR("Could not find or use GMR region.\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out_no_reloc;
}
bo = &vmw_bo->base;
out_no_reloc:
vmw_dmabuf_unreference(&vmw_bo);
- vmw_bo_p = NULL;
+ *vmw_bo_p = NULL;
return ret;
}
NULL, arg->command_size, arg->throttle_us,
(void __user *)(unsigned long)arg->fence_rep,
NULL);
-
+ ttm_read_unlock(&dev_priv->reservation_sem);
if (unlikely(ret != 0))
- goto out_unlock;
+ return ret;
vmw_kms_cursor_post_execbuf(dev_priv);
-out_unlock:
- ttm_read_unlock(&dev_priv->reservation_sem);
- return ret;
+ return 0;
}
int i;
struct drm_mode_config *mode_config = &dev->mode_config;
- ret = ttm_read_lock(&dev_priv->reservation_sem, true);
- if (unlikely(ret != 0))
- return ret;
-
if (!arg->num_outputs) {
struct drm_vmw_rect def_rect = {0, 0, 800, 600};
vmw_du_update_layout(dev_priv, 1, &def_rect);
- goto out_unlock;
+ return 0;
}
rects_size = arg->num_outputs * sizeof(struct drm_vmw_rect);
rects = kcalloc(arg->num_outputs, sizeof(struct drm_vmw_rect),
GFP_KERNEL);
- if (unlikely(!rects)) {
- ret = -ENOMEM;
- goto out_unlock;
- }
+ if (unlikely(!rects))
+ return -ENOMEM;
user_rects = (void __user *)(unsigned long)arg->rects;
ret = copy_from_user(rects, user_rects, rects_size);
out_free:
kfree(rects);
-out_unlock:
- ttm_read_unlock(&dev_priv->reservation_sem);
return ret;
}
clkrate = clk_get_rate(di->clk_ipu);
div = DIV_ROUND_CLOSEST(clkrate, sig->mode.pixelclock);
+ if (div == 0)
+ div = 1;
rate = clkrate / div;
error = rate / (sig->mode.pixelclock / 1000);
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K_JP) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE7K) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K) },
#endif
#if IS_ENABLED(CONFIG_HID_SAITEK)
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7_OLD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT9) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb65a) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE_BT) },
{ HID_USB_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE_PRO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED2, USB_DEVICE_ID_TOPSEED2_RF_COMBO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_TWINHAN, USB_DEVICE_ID_TWINHAN_IR_REMOTE) },
#define USB_VENDOR_ID_LOGITECH 0x046d
#define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e
#define USB_DEVICE_ID_LOGITECH_T651 0xb00c
+#define USB_DEVICE_ID_LOGITECH_C077 0xc007
#define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101
#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110
#define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f
#define USB_DEVICE_ID_MS_LK6K 0x00f9
#define USB_DEVICE_ID_MS_PRESENTER_8K_BT 0x0701
#define USB_DEVICE_ID_MS_PRESENTER_8K_USB 0x0713
+#define USB_DEVICE_ID_MS_NE7K 0x071d
#define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K 0x0730
#define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500 0x076c
#define USB_DEVICE_ID_MS_SURFACE_PRO_2 0x0799
#define USB_VENDOR_ID_SAITEK 0x06a3
#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17
#define USB_DEVICE_ID_SAITEK_PS1000 0x0621
+#define USB_DEVICE_ID_SAITEK_RAT7_OLD 0x0ccb
#define USB_DEVICE_ID_SAITEK_RAT7 0x0cd7
#define USB_DEVICE_ID_SAITEK_MMO7 0x0cd0
#define USB_VENDOR_ID_TIVO 0x150a
#define USB_DEVICE_ID_TIVO_SLIDE_BT 0x1200
#define USB_DEVICE_ID_TIVO_SLIDE 0x1201
+#define USB_DEVICE_ID_TIVO_SLIDE_PRO 0x1203
#define USB_VENDOR_ID_TOPSEED 0x0766
#define USB_DEVICE_ID_TOPSEED_CYBERLINK 0x0204
.driver_data = MS_ERGONOMY },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K_JP),
.driver_data = MS_ERGONOMY },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE7K),
+ .driver_data = MS_ERGONOMY },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_LK6K),
.driver_data = MS_ERGONOMY | MS_RDESC },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB),
static const struct hid_device_id saitek_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000),
.driver_data = SAITEK_FIX_PS1000 },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7_OLD),
+ .driver_data = SAITEK_RELEASE_MODE_RAT7 },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7),
.driver_data = SAITEK_RELEASE_MODE_RAT7 },
{ HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT9),
{
struct hid_sensor_hub_callbacks_list *callback;
struct sensor_hub_data *pdata = hid_get_drvdata(hdev);
+ unsigned long flags;
- spin_lock(&pdata->dyn_callback_lock);
+ spin_lock_irqsave(&pdata->dyn_callback_lock, flags);
list_for_each_entry(callback, &pdata->dyn_callback_list, list)
if (callback->usage_id == usage_id &&
(collection_index >=
callback->hsdev->end_collection_index)) {
*priv = callback->priv;
*hsdev = callback->hsdev;
- spin_unlock(&pdata->dyn_callback_lock);
+ spin_unlock_irqrestore(&pdata->dyn_callback_lock,
+ flags);
return callback->usage_callback;
}
- spin_unlock(&pdata->dyn_callback_lock);
+ spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags);
return NULL;
}
#define DS4_REPORT_0x81_SIZE 7
#define SIXAXIS_REPORT_0xF2_SIZE 18
-static spinlock_t sony_dev_list_lock;
+static DEFINE_SPINLOCK(sony_dev_list_lock);
static LIST_HEAD(sony_device_list);
static DEFINE_IDA(sony_device_id_allocator);
return -ENOMEM;
}
+ spin_lock_init(&sc->lock);
+
sc->quirks = quirks;
hid_set_drvdata(hdev, sc);
sc->hdev = hdev;
{
dbg_hid("Sony:%s\n", __func__);
- ida_destroy(&sony_device_id_allocator);
hid_unregister_driver(&sony_driver);
+ ida_destroy(&sony_device_id_allocator);
}
module_init(sony_init);
module_exit(sony_exit);
/* TiVo Slide Bluetooth remote, pairs with a Broadcom dongle */
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE_BT) },
{ HID_USB_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE_PRO) },
{ }
};
MODULE_DEVICE_TABLE(hid, tivo_devices);
static void i2c_hid_get_input(struct i2c_hid *ihid)
{
int ret, ret_size;
- int size = ihid->bufsize;
+ int size = le16_to_cpu(ihid->hdesc.wMaxInputLength);
+
+ if (size > ihid->bufsize)
+ size = ihid->bufsize;
ret = i2c_master_recv(ihid->client, ihid->inbuf, size);
if (ret != size) {
dev_dbg(&client->dev, "Requesting IRQ: %d\n", client->irq);
ret = request_threaded_irq(client->irq, NULL, i2c_hid_irq,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
client->name, ihid);
if (ret < 0) {
dev_warn(&client->dev,
{ USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_C077, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3_JP, HID_QUIRK_NO_INIT_REPORTS },
(features->type == CINTIQ && !(data[1] & 0x40)))
return 1;
- if (features->quirks & WACOM_QUIRK_MULTI_INPUT)
+ if (wacom->shared) {
wacom->shared->stylus_in_proximity = true;
+ if (wacom->shared->touch_down)
+ return 1;
+ }
+
/* in Range while exiting */
if (((data[1] & 0xfe) == 0x20) && wacom->reporting_data) {
input_report_key(input, BTN_TOUCH, 0);
input_report_abs(input, ABS_X, be16_to_cpup((__be16 *)&data[4]));
input_report_abs(input, ABS_Y, be16_to_cpup((__be16 *)&data[6]));
input_report_abs(input, ABS_Z, be16_to_cpup((__be16 *)&data[8]));
+ if ((data[2] & 0x07) | data[4] | data[5] | data[6] | data[7] | data[8] | data[9]) {
+ input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
+ } else {
+ input_report_abs(input, ABS_MISC, 0);
+ }
} else if (features->type == CINTIQ_HYBRID) {
/*
* Do not send hardware buttons under Android. They
struct input_dev *input = wacom->input;
unsigned char *data = wacom->data;
int i;
- int current_num_contacts = 0;
+ int current_num_contacts = data[61];
int contacts_to_send = 0;
int num_contacts_left = 4; /* maximum contacts per packet */
int byte_per_packet = WACOM_BYTES_PER_24HDT_PACKET;
int y_offset = 2;
+ static int contact_with_no_pen_down_count = 0;
if (wacom->features.type == WACOM_27QHDT) {
current_num_contacts = data[63];
num_contacts_left = 10;
byte_per_packet = WACOM_BYTES_PER_QHDTHID_PACKET;
y_offset = 0;
- } else {
- current_num_contacts = data[61];
}
/*
* First packet resets the counter since only the first
* packet in series will have non-zero current_num_contacts.
*/
- if (current_num_contacts)
+ if (current_num_contacts) {
wacom->num_contacts_left = current_num_contacts;
+ contact_with_no_pen_down_count = 0;
+ }
contacts_to_send = min(num_contacts_left, wacom->num_contacts_left);
input_report_abs(input, ABS_MT_WIDTH_MINOR, min(w, h));
input_report_abs(input, ABS_MT_ORIENTATION, w > h);
}
+ contact_with_no_pen_down_count++;
}
}
input_mt_report_pointer_emulation(input, true);
wacom->num_contacts_left -= contacts_to_send;
- if (wacom->num_contacts_left <= 0)
+ if (wacom->num_contacts_left <= 0) {
wacom->num_contacts_left = 0;
-
- wacom->shared->touch_down = (wacom->num_contacts_left > 0);
+ wacom->shared->touch_down = (contact_with_no_pen_down_count > 0);
+ }
return 1;
}
int current_num_contacts = data[2];
int contacts_to_send = 0;
int x_offset = 0;
+ static int contact_with_no_pen_down_count = 0;
/* MTTPC does not support Height and Width */
if (wacom->features.type == MTTPC || wacom->features.type == MTTPC_B)
* First packet resets the counter since only the first
* packet in series will have non-zero current_num_contacts.
*/
- if (current_num_contacts)
+ if (current_num_contacts) {
wacom->num_contacts_left = current_num_contacts;
+ contact_with_no_pen_down_count = 0;
+ }
/* There are at most 5 contacts per packet */
contacts_to_send = min(5, wacom->num_contacts_left);
int y = get_unaligned_le16(&data[offset + x_offset + 9]);
input_report_abs(input, ABS_MT_POSITION_X, x);
input_report_abs(input, ABS_MT_POSITION_Y, y);
+ contact_with_no_pen_down_count++;
}
}
input_mt_report_pointer_emulation(input, true);
wacom->num_contacts_left -= contacts_to_send;
- if (wacom->num_contacts_left < 0)
+ if (wacom->num_contacts_left <= 0) {
wacom->num_contacts_left = 0;
-
- wacom->shared->touch_down = (wacom->num_contacts_left > 0);
+ wacom->shared->touch_down = (contact_with_no_pen_down_count > 0);
+ }
return 1;
}
{
unsigned char *data = wacom->data;
struct input_dev *input = wacom->input;
- bool prox;
+ bool prox = !wacom->shared->stylus_in_proximity;
int x = 0, y = 0;
if (wacom->features.touch_max > 1 || len > WACOM_PKGLEN_TPC2FG)
return 0;
- if (!wacom->shared->stylus_in_proximity) {
- if (len == WACOM_PKGLEN_TPC1FG) {
- prox = data[0] & 0x01;
- x = get_unaligned_le16(&data[1]);
- y = get_unaligned_le16(&data[3]);
- } else if (len == WACOM_PKGLEN_TPC1FG_B) {
- prox = data[2] & 0x01;
- x = get_unaligned_le16(&data[3]);
- y = get_unaligned_le16(&data[5]);
- } else {
- prox = data[1] & 0x01;
- x = le16_to_cpup((__le16 *)&data[2]);
- y = le16_to_cpup((__le16 *)&data[4]);
- }
- } else
- /* force touch out when pen is in prox */
- prox = 0;
+ if (len == WACOM_PKGLEN_TPC1FG) {
+ prox = prox && (data[0] & 0x01);
+ x = get_unaligned_le16(&data[1]);
+ y = get_unaligned_le16(&data[3]);
+ } else if (len == WACOM_PKGLEN_TPC1FG_B) {
+ prox = prox && (data[2] & 0x01);
+ x = get_unaligned_le16(&data[3]);
+ y = get_unaligned_le16(&data[5]);
+ } else {
+ prox = prox && (data[1] & 0x01);
+ x = le16_to_cpup((__le16 *)&data[2]);
+ y = le16_to_cpup((__le16 *)&data[4]);
+ }
if (prox) {
input_report_abs(input, ABS_X, x);
struct input_dev *pad_input = wacom->pad_input;
unsigned char *data = wacom->data;
int i;
+ int contact_with_no_pen_down_count = 0;
if (data[0] != 0x02)
return 0;
}
input_report_abs(input, ABS_MT_POSITION_X, x);
input_report_abs(input, ABS_MT_POSITION_Y, y);
+ contact_with_no_pen_down_count++;
}
}
input_report_key(pad_input, BTN_FORWARD, (data[1] & 0x04) != 0);
input_report_key(pad_input, BTN_BACK, (data[1] & 0x02) != 0);
input_report_key(pad_input, BTN_RIGHT, (data[1] & 0x01) != 0);
+ wacom->shared->touch_down = (contact_with_no_pen_down_count > 0);
return 1;
}
-static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data)
+static int wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data, int last_touch_count)
{
struct wacom_features *features = &wacom->features;
struct input_dev *input = wacom->input;
int slot = input_mt_get_slot_by_key(input, data[0]);
if (slot < 0)
- return;
+ return 0;
touch = touch && !wacom->shared->stylus_in_proximity;
input_report_abs(input, ABS_MT_POSITION_Y, y);
input_report_abs(input, ABS_MT_TOUCH_MAJOR, width);
input_report_abs(input, ABS_MT_TOUCH_MINOR, height);
+ last_touch_count++;
}
+ return last_touch_count;
}
static void wacom_bpt3_button_msg(struct wacom_wac *wacom, unsigned char *data)
unsigned char *data = wacom->data;
int count = data[1] & 0x07;
int i;
+ int contact_with_no_pen_down_count = 0;
if (data[0] != 0x02)
return 0;
int msg_id = data[offset];
if (msg_id >= 2 && msg_id <= 17)
- wacom_bpt3_touch_msg(wacom, data + offset);
+ contact_with_no_pen_down_count =
+ wacom_bpt3_touch_msg(wacom, data + offset,
+ contact_with_no_pen_down_count);
else if (msg_id == 128)
wacom_bpt3_button_msg(wacom, data + offset);
}
input_mt_report_pointer_emulation(input, true);
+ wacom->shared->touch_down = (contact_with_no_pen_down_count > 0);
return 1;
}
return 0;
}
+ if (wacom->shared->touch_down)
+ return 0;
+
prox = (data[1] & 0x20) == 0x20;
/*
.oVid = USB_VENDOR_ID_WACOM, .oPid = 0xf8, .touch_max = 10,
.check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE };
static const struct wacom_features wacom_features_0x32A =
- { "Wacom Cintiq 27QHD", 119740, 67520, 2047,
- 63, WACOM_27QHD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
- WACOM_27QHD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+ { "Wacom Cintiq 27QHD", 119740, 67520, 2047, 63,
+ WACOM_27QHD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
+ WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET };
static const struct wacom_features wacom_features_0x32B =
{ "Wacom Cintiq 27QHD touch", 119740, 67520, 2047, 63,
WACOM_27QHD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
&ads2830_regmap_config);
}
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
data->cmd_byte = ext_vref ? ADS7828_CMD_PD1 : ADS7828_CMD_PD3;
if (!diff_input)
data->cmd_byte |= ADS7828_CMD_SD_SE;
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
+
#include <asm/iosf_mbi.h>
+
#include "i2c-designware-core.h"
#define SEMAPHORE_TIMEOUT 100
#define PUNIT_SEMAPHORE 0x7
+#define PUNIT_SEMAPHORE_BIT BIT(0)
+#define PUNIT_SEMAPHORE_ACQUIRE BIT(1)
static unsigned long acquired;
static int get_sem(struct device *dev, u32 *sem)
{
- u32 reg_val;
+ u32 data;
int ret;
ret = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, PUNIT_SEMAPHORE,
- ®_val);
+ &data);
if (ret) {
dev_err(dev, "iosf failed to read punit semaphore\n");
return ret;
}
- *sem = reg_val & 0x1;
+ *sem = data & PUNIT_SEMAPHORE_BIT;
return 0;
}
return;
}
- data = data & 0xfffffffe;
+ data &= ~PUNIT_SEMAPHORE_BIT;
if (iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
- PUNIT_SEMAPHORE, data))
+ PUNIT_SEMAPHORE, data))
dev_err(dev, "iosf failed to reset punit semaphore during write\n");
}
-int baytrail_i2c_acquire(struct dw_i2c_dev *dev)
+static int baytrail_i2c_acquire(struct dw_i2c_dev *dev)
{
- u32 sem = 0;
+ u32 sem;
int ret;
unsigned long start, end;
+ might_sleep();
+
if (!dev || !dev->dev)
return -ENODEV;
- if (!dev->acquire_lock)
+ if (!dev->release_lock)
return 0;
- /* host driver writes 0x2 to side band semaphore register */
+ /* host driver writes to side band semaphore register */
ret = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
- PUNIT_SEMAPHORE, 0x2);
+ PUNIT_SEMAPHORE, PUNIT_SEMAPHORE_ACQUIRE);
if (ret) {
dev_err(dev->dev, "iosf punit semaphore request failed\n");
return ret;
/* host driver waits for bit 0 to be set in semaphore register */
start = jiffies;
end = start + msecs_to_jiffies(SEMAPHORE_TIMEOUT);
- while (!time_after(jiffies, end)) {
+ do {
ret = get_sem(dev->dev, &sem);
if (!ret && sem) {
acquired = jiffies;
}
usleep_range(1000, 2000);
- }
+ } while (time_before(jiffies, end));
dev_err(dev->dev, "punit semaphore timed out, resetting\n");
reset_semaphore(dev->dev);
ret = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
- PUNIT_SEMAPHORE, &sem);
- if (!ret)
+ PUNIT_SEMAPHORE, &sem);
+ if (ret)
dev_err(dev->dev, "iosf failed to read punit semaphore\n");
else
dev_err(dev->dev, "PUNIT SEM: %d\n", sem);
return -ETIMEDOUT;
}
-EXPORT_SYMBOL(baytrail_i2c_acquire);
-void baytrail_i2c_release(struct dw_i2c_dev *dev)
+static void baytrail_i2c_release(struct dw_i2c_dev *dev)
{
if (!dev || !dev->dev)
return;
dev_dbg(dev->dev, "punit semaphore held for %ums\n",
jiffies_to_msecs(jiffies - acquired));
}
-EXPORT_SYMBOL(baytrail_i2c_release);
int i2c_dw_eval_lock_support(struct dw_i2c_dev *dev)
{
return 0;
status = acpi_evaluate_integer(handle, "_SEM", NULL, &shared_host);
-
if (ACPI_FAILURE(status))
return 0;
return 0;
}
-EXPORT_SYMBOL(i2c_dw_eval_lock_support);
MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
MODULE_DESCRIPTION("Baytrail I2C Semaphore driver");
status = driver->remove(client);
}
- if (dev->of_node)
- irq_dispose_mapping(client->irq);
-
dev_pm_domain_detach(&client->dev, true);
return status;
}
tape->best_dsc_rw_freq = clamp_t(unsigned long, t, IDETAPE_DSC_RW_MIN,
IDETAPE_DSC_RW_MAX);
printk(KERN_INFO "ide-tape: %s <-> %s: %dKBps, %d*%dkB buffer, "
- "%lums tDSC%s\n",
+ "%ums tDSC%s\n",
drive->name, tape->name, *(u16 *)&tape->caps[14],
(*(u16 *)&tape->caps[16] * 512) / tape->buffer_size,
tape->buffer_size / 1024,
- tape->best_dsc_rw_freq * 1000 / HZ,
+ jiffies_to_msecs(tape->best_dsc_rw_freq),
(drive->dev_flags & IDE_DFLAG_USING_DMA) ? ", DMA" : "");
ide_proc_register_driver(drive, tape->driver);
mutex_lock(&data->mutex);
- for_each_set_bit(bit, indio_dev->buffer->scan_mask,
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
ret = bma180_get_data_reg(data, bit);
if (ret < 0) {
int val;
int val2;
u8 bw_bits;
-} bmc150_accel_samp_freq_table[] = { {7, 810000, 0x08},
- {15, 630000, 0x09},
- {31, 250000, 0x0A},
- {62, 500000, 0x0B},
- {125, 0, 0x0C},
- {250, 0, 0x0D},
- {500, 0, 0x0E},
- {1000, 0, 0x0F} };
+} bmc150_accel_samp_freq_table[] = { {15, 620000, 0x08},
+ {31, 260000, 0x09},
+ {62, 500000, 0x0A},
+ {125, 0, 0x0B},
+ {250, 0, 0x0C},
+ {500, 0, 0x0D},
+ {1000, 0, 0x0E},
+ {2000, 0, 0x0F} };
static const struct {
int bw_bits;
}
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
- "7.810000 15.630000 31.250000 62.500000 125 250 500 1000");
+ "15.620000 31.260000 62.50000 125 250 500 1000 2000");
static struct attribute *bmc150_accel_attributes[] = {
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
int bit, ret, i = 0;
mutex_lock(&data->mutex);
- for_each_set_bit(bit, indio_dev->buffer->scan_mask,
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
ret = i2c_smbus_read_word_data(data->client,
BMC150_ACCEL_AXIS_TO_REG(bit));
mutex_lock(&data->mutex);
- for_each_set_bit(bit, indio_dev->buffer->scan_mask,
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
ret = kxcjk1013_get_acc_reg(data, bit);
if (ret < 0) {
config CC10001_ADC
tristate "Cosmic Circuits 10001 ADC driver"
- depends on HAS_IOMEM || HAVE_CLK || REGULATOR
+ depends on HAVE_CLK || REGULATOR
+ depends on HAS_IOMEM
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
{
struct iio_dev *idev = iio_trigger_get_drvdata(trig);
struct at91_adc_state *st = iio_priv(idev);
- struct iio_buffer *buffer = idev->buffer;
struct at91_adc_reg_desc *reg = st->registers;
u32 status = at91_adc_readl(st, reg->trigger_register);
int value;
at91_adc_writel(st, reg->trigger_register,
status | value);
- for_each_set_bit(bit, buffer->scan_mask,
+ for_each_set_bit(bit, idev->active_scan_mask,
st->num_channels) {
struct iio_chan_spec const *chan = idev->channels + bit;
at91_adc_writel(st, AT91_ADC_CHER,
at91_adc_writel(st, reg->trigger_register,
status & ~value);
- for_each_set_bit(bit, buffer->scan_mask,
+ for_each_set_bit(bit, idev->active_scan_mask,
st->num_channels) {
struct iio_chan_spec const *chan = idev->channels + bit;
at91_adc_writel(st, AT91_ADC_CHDR,
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
}
-/* LSB is in nV to eliminate floating point */
-static const u32 rates_to_lsb[] = {1000000, 250000, 62500, 15625};
-
-/*
- * scales calculated as:
- * rates_to_lsb[sample_rate] / (1 << pga);
- * pga is 1 for 0, 2
- */
-
static const int mcp3422_scales[4][4] = {
- { 1000000, 250000, 62500, 15625 },
- { 500000 , 125000, 31250, 7812 },
- { 250000 , 62500 , 15625, 3906 },
- { 125000 , 31250 , 7812 , 1953 } };
+ { 1000000, 500000, 250000, 125000 },
+ { 250000 , 125000, 62500 , 31250 },
+ { 62500 , 31250 , 15625 , 7812 },
+ { 15625 , 7812 , 3906 , 1953 } };
/* Constant msleep times for data acquisitions */
static const int mcp3422_read_times[4] = {
if (iadc->poll_eoc) {
ret = iadc_poll_wait_eoc(iadc, wait);
} else {
- ret = wait_for_completion_timeout(&iadc->complete, wait);
+ ret = wait_for_completion_timeout(&iadc->complete,
+ usecs_to_jiffies(wait));
if (!ret)
ret = -ETIMEDOUT;
else
static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
- struct iio_buffer *buffer = indio_dev->buffer;
unsigned int enb = 0;
u8 bit;
tiadc_step_config(indio_dev);
- for_each_set_bit(bit, buffer->scan_mask, adc_dev->channels)
+ for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels)
enb |= (get_adc_step_bit(adc_dev, bit) << 1);
adc_dev->buffer_en_ch_steps = enb;
struct regulator *vref;
struct vf610_adc_feature adc_feature;
+ u32 sample_freq_avail[5];
+
struct completion completion;
};
+static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 };
+
#define VF610_ADC_CHAN(_idx, _chan_type) { \
.type = (_chan_type), \
.indexed = 1, \
/* sentinel */
};
-/*
- * ADC sample frequency, unit is ADCK cycles.
- * ADC clk source is ipg clock, which is the same as bus clock.
- *
- * ADC conversion time = SFCAdder + AverageNum x (BCT + LSTAdder)
- * SFCAdder: fixed to 6 ADCK cycles
- * AverageNum: 1, 4, 8, 16, 32 samples for hardware average.
- * BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode
- * LSTAdder(Long Sample Time): fixed to 3 ADCK cycles
- *
- * By default, enable 12 bit resolution mode, clock source
- * set to ipg clock, So get below frequency group:
- */
-static const u32 vf610_sample_freq_avail[5] =
-{1941176, 559332, 286957, 145374, 73171};
+static inline void vf610_adc_calculate_rates(struct vf610_adc *info)
+{
+ unsigned long adck_rate, ipg_rate = clk_get_rate(info->clk);
+ int i;
+
+ /*
+ * Calculate ADC sample frequencies
+ * Sample time unit is ADCK cycles. ADCK clk source is ipg clock,
+ * which is the same as bus clock.
+ *
+ * ADC conversion time = SFCAdder + AverageNum x (BCT + LSTAdder)
+ * SFCAdder: fixed to 6 ADCK cycles
+ * AverageNum: 1, 4, 8, 16, 32 samples for hardware average.
+ * BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode
+ * LSTAdder(Long Sample Time): fixed to 3 ADCK cycles
+ */
+ adck_rate = ipg_rate / info->adc_feature.clk_div;
+ for (i = 0; i < ARRAY_SIZE(vf610_hw_avgs); i++)
+ info->sample_freq_avail[i] =
+ adck_rate / (6 + vf610_hw_avgs[i] * (25 + 3));
+}
static inline void vf610_adc_cfg_init(struct vf610_adc *info)
{
+ struct vf610_adc_feature *adc_feature = &info->adc_feature;
+
/* set default Configuration for ADC controller */
- info->adc_feature.clk_sel = VF610_ADCIOC_BUSCLK_SET;
- info->adc_feature.vol_ref = VF610_ADCIOC_VR_VREF_SET;
+ adc_feature->clk_sel = VF610_ADCIOC_BUSCLK_SET;
+ adc_feature->vol_ref = VF610_ADCIOC_VR_VREF_SET;
+
+ adc_feature->calibration = true;
+ adc_feature->ovwren = true;
+
+ adc_feature->res_mode = 12;
+ adc_feature->sample_rate = 1;
+ adc_feature->lpm = true;
- info->adc_feature.calibration = true;
- info->adc_feature.ovwren = true;
+ /* Use a save ADCK which is below 20MHz on all devices */
+ adc_feature->clk_div = 8;
- info->adc_feature.clk_div = 1;
- info->adc_feature.res_mode = 12;
- info->adc_feature.sample_rate = 1;
- info->adc_feature.lpm = true;
+ vf610_adc_calculate_rates(info);
}
static void vf610_adc_cfg_post_set(struct vf610_adc *info)
cfg_data = readl(info->regs + VF610_REG_ADC_CFG);
- /* low power configuration */
cfg_data &= ~VF610_ADC_ADLPC_EN;
if (adc_feature->lpm)
cfg_data |= VF610_ADC_ADLPC_EN;
- /* disable high speed */
cfg_data &= ~VF610_ADC_ADHSC_EN;
writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
return IRQ_HANDLED;
}
-static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1941176, 559332, 286957, 145374, 73171");
+static ssize_t vf610_show_samp_freq_avail(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct vf610_adc *info = iio_priv(dev_to_iio_dev(dev));
+ size_t len = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(info->sample_freq_avail); i++)
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "%u ", info->sample_freq_avail[i]);
+
+ /* replace trailing space by newline */
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(vf610_show_samp_freq_avail);
static struct attribute *vf610_attributes[] = {
- &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
NULL
};
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_SAMP_FREQ:
- *val = vf610_sample_freq_avail[info->adc_feature.sample_rate];
+ *val = info->sample_freq_avail[info->adc_feature.sample_rate];
*val2 = 0;
return IIO_VAL_INT;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
for (i = 0;
- i < ARRAY_SIZE(vf610_sample_freq_avail);
+ i < ARRAY_SIZE(info->sample_freq_avail);
i++)
- if (val == vf610_sample_freq_avail[i]) {
+ if (val == info->sample_freq_avail[i]) {
info->adc_feature.sample_rate = i;
vf610_adc_sample_set(info);
return 0;
return 0;
}
+#ifdef CONFIG_PM_SLEEP
static int ssp_suspend(struct device *dev)
{
int ret;
return 0;
}
+#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops ssp_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(ssp_suspend, ssp_resume)
st = iio_priv(indio_dev);
spi_set_drvdata(spi, indio_dev);
- st->reg = devm_regulator_get(&spi->dev, "vcc");
+ st->reg = devm_regulator_get_optional(&spi->dev, "vcc");
if (!IS_ERR(st->reg)) {
ret = regulator_enable(st->reg);
if (ret)
int bit, ret, i = 0;
mutex_lock(&data->mutex);
- for_each_set_bit(bit, indio_dev->buffer->scan_mask,
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
ret = i2c_smbus_read_word_data(data->client,
BMG160_AXIS_TO_REG(bit));
#include <linux/wait.h>
#include <linux/bitops.h>
#include <linux/completion.h>
+#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#define DHT11_DATA_VALID_TIME 2000000000 /* 2s in ns */
-#define DHT11_EDGES_PREAMBLE 4
+#define DHT11_EDGES_PREAMBLE 2
#define DHT11_BITS_PER_READ 40
+/*
+ * Note that when reading the sensor actually 84 edges are detected, but
+ * since the last edge is not significant, we only store 83:
+ */
#define DHT11_EDGES_PER_READ (2*DHT11_BITS_PER_READ + DHT11_EDGES_PREAMBLE + 1)
/* Data transmission timing (nano seconds) */
int irq;
struct completion completion;
+ struct mutex lock;
s64 timestamp;
int temperature;
unsigned char temp_int, temp_dec, hum_int, hum_dec, checksum;
/* Calculate timestamp resolution */
- for (i = 0; i < dht11->num_edges; ++i) {
+ for (i = 1; i < dht11->num_edges; ++i) {
t = dht11->edges[i].ts - dht11->edges[i-1].ts;
if (t > 0 && t < timeres)
timeres = t;
return 0;
}
+/*
+ * IRQ handler called on GPIO edges
+ */
+static irqreturn_t dht11_handle_irq(int irq, void *data)
+{
+ struct iio_dev *iio = data;
+ struct dht11 *dht11 = iio_priv(iio);
+
+ /* TODO: Consider making the handler safe for IRQ sharing */
+ if (dht11->num_edges < DHT11_EDGES_PER_READ && dht11->num_edges >= 0) {
+ dht11->edges[dht11->num_edges].ts = iio_get_time_ns();
+ dht11->edges[dht11->num_edges++].value =
+ gpio_get_value(dht11->gpio);
+
+ if (dht11->num_edges >= DHT11_EDGES_PER_READ)
+ complete(&dht11->completion);
+ }
+
+ return IRQ_HANDLED;
+}
+
static int dht11_read_raw(struct iio_dev *iio_dev,
const struct iio_chan_spec *chan,
int *val, int *val2, long m)
struct dht11 *dht11 = iio_priv(iio_dev);
int ret;
+ mutex_lock(&dht11->lock);
if (dht11->timestamp + DHT11_DATA_VALID_TIME < iio_get_time_ns()) {
reinit_completion(&dht11->completion);
if (ret)
goto err;
+ ret = request_irq(dht11->irq, dht11_handle_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ iio_dev->name, iio_dev);
+ if (ret)
+ goto err;
+
ret = wait_for_completion_killable_timeout(&dht11->completion,
HZ);
+
+ free_irq(dht11->irq, iio_dev);
+
if (ret == 0 && dht11->num_edges < DHT11_EDGES_PER_READ - 1) {
dev_err(&iio_dev->dev,
"Only %d signal edges detected\n",
ret = -EINVAL;
err:
dht11->num_edges = -1;
+ mutex_unlock(&dht11->lock);
return ret;
}
.read_raw = dht11_read_raw,
};
-/*
- * IRQ handler called on GPIO edges
-*/
-static irqreturn_t dht11_handle_irq(int irq, void *data)
-{
- struct iio_dev *iio = data;
- struct dht11 *dht11 = iio_priv(iio);
-
- /* TODO: Consider making the handler safe for IRQ sharing */
- if (dht11->num_edges < DHT11_EDGES_PER_READ && dht11->num_edges >= 0) {
- dht11->edges[dht11->num_edges].ts = iio_get_time_ns();
- dht11->edges[dht11->num_edges++].value =
- gpio_get_value(dht11->gpio);
-
- if (dht11->num_edges >= DHT11_EDGES_PER_READ)
- complete(&dht11->completion);
- }
-
- return IRQ_HANDLED;
-}
-
static const struct iio_chan_spec dht11_chan_spec[] = {
{ .type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), },
dev_err(dev, "GPIO %d has no interrupt\n", dht11->gpio);
return -EINVAL;
}
- ret = devm_request_irq(dev, dht11->irq, dht11_handle_irq,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- pdev->name, iio);
- if (ret)
- return ret;
dht11->timestamp = iio_get_time_ns() - DHT11_DATA_VALID_TIME - 1;
dht11->num_edges = -1;
platform_set_drvdata(pdev, iio);
init_completion(&dht11->completion);
+ mutex_init(&dht11->lock);
iio->name = pdev->name;
iio->dev.parent = &pdev->dev;
iio->info = &dht11_iio_info;
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
- struct i2c_client *client = iio_priv(indio_dev);
+ struct i2c_client **client = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
- ret = i2c_smbus_read_word_data(client,
+ ret = i2c_smbus_read_word_data(*client,
chan->type == IIO_TEMP ?
SI7020CMD_TEMP_HOLD :
SI7020CMD_RH_HOLD);
/* Wait the maximum power-up time after software reset. */
msleep(15);
- indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*client));
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
#include <linux/list.h>
#include <linux/module.h>
#include <linux/debugfs.h>
+#include <linux/bitops.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
mutex_unlock(&indio_dev->mlock);
if (ret)
return ret;
- val16 = ((val16 & 0xFFF) << 4) >> 4;
+ val16 = sign_extend32(val16, 11);
*val = val16;
return IIO_VAL_INT;
case IIO_CHAN_INFO_OFFSET:
iio_trigger_set_drvdata(adis->trig, adis);
ret = iio_trigger_register(adis->trig);
- indio_dev->trig = adis->trig;
+ indio_dev->trig = iio_trigger_get(adis->trig);
if (ret)
goto error_free_irq;
}
}
-static int inv_mpu6050_write_fsr(struct inv_mpu6050_state *st, int fsr)
+static int inv_mpu6050_write_gyro_scale(struct inv_mpu6050_state *st, int val)
{
- int result;
+ int result, i;
u8 d;
- if (fsr < 0 || fsr > INV_MPU6050_MAX_GYRO_FS_PARAM)
- return -EINVAL;
- if (fsr == st->chip_config.fsr)
- return 0;
+ for (i = 0; i < ARRAY_SIZE(gyro_scale_6050); ++i) {
+ if (gyro_scale_6050[i] == val) {
+ d = (i << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT);
+ result = inv_mpu6050_write_reg(st,
+ st->reg->gyro_config, d);
+ if (result)
+ return result;
- d = (fsr << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT);
- result = inv_mpu6050_write_reg(st, st->reg->gyro_config, d);
- if (result)
- return result;
- st->chip_config.fsr = fsr;
+ st->chip_config.fsr = i;
+ return 0;
+ }
+ }
- return 0;
+ return -EINVAL;
}
-static int inv_mpu6050_write_accel_fs(struct inv_mpu6050_state *st, int fs)
+static int inv_mpu6050_write_accel_scale(struct inv_mpu6050_state *st, int val)
{
- int result;
+ int result, i;
u8 d;
- if (fs < 0 || fs > INV_MPU6050_MAX_ACCL_FS_PARAM)
- return -EINVAL;
- if (fs == st->chip_config.accl_fs)
- return 0;
+ for (i = 0; i < ARRAY_SIZE(accel_scale); ++i) {
+ if (accel_scale[i] == val) {
+ d = (i << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT);
+ result = inv_mpu6050_write_reg(st,
+ st->reg->accl_config, d);
+ if (result)
+ return result;
- d = (fs << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT);
- result = inv_mpu6050_write_reg(st, st->reg->accl_config, d);
- if (result)
- return result;
- st->chip_config.accl_fs = fs;
+ st->chip_config.accl_fs = i;
+ return 0;
+ }
+ }
- return 0;
+ return -EINVAL;
}
static int inv_mpu6050_write_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_ANGL_VEL:
- result = inv_mpu6050_write_fsr(st, val);
+ result = inv_mpu6050_write_gyro_scale(st, val2);
break;
case IIO_ACCEL:
- result = inv_mpu6050_write_accel_fs(st, val);
+ result = inv_mpu6050_write_accel_scale(st, val2);
break;
default:
result = -EINVAL;
i2c_set_clientdata(client, indio_dev);
indio_dev->dev.parent = &client->dev;
- indio_dev->name = id->name;
+ /* id will be NULL when enumerated via ACPI */
+ if (id)
+ indio_dev->name = (char *)id->name;
+ else
+ indio_dev->name = (char *)dev_name(&client->dev);
indio_dev->channels = inv_mpu_channels;
indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
#include <linux/poll.h>
#include "inv_mpu_iio.h"
+static void inv_clear_kfifo(struct inv_mpu6050_state *st)
+{
+ unsigned long flags;
+
+ /* take the spin lock sem to avoid interrupt kick in */
+ spin_lock_irqsave(&st->time_stamp_lock, flags);
+ kfifo_reset(&st->timestamps);
+ spin_unlock_irqrestore(&st->time_stamp_lock, flags);
+}
+
int inv_reset_fifo(struct iio_dev *indio_dev)
{
int result;
INV_MPU6050_BIT_FIFO_RST);
if (result)
goto reset_fifo_fail;
+
+ /* clear timestamps fifo */
+ inv_clear_kfifo(st);
+
/* enable interrupt */
if (st->chip_config.accl_fifo_enable ||
st->chip_config.gyro_fifo_enable) {
return result;
}
-static void inv_clear_kfifo(struct inv_mpu6050_state *st)
-{
- unsigned long flags;
-
- /* take the spin lock sem to avoid interrupt kick in */
- spin_lock_irqsave(&st->time_stamp_lock, flags);
- kfifo_reset(&st->timestamps);
- spin_unlock_irqrestore(&st->time_stamp_lock, flags);
-}
-
/**
* inv_mpu6050_irq_handler() - Cache a timestamp at each data ready interrupt.
*/
flush_fifo:
/* Flush HW and SW FIFOs. */
inv_reset_fifo(indio_dev);
- inv_clear_kfifo(st);
mutex_unlock(&indio_dev->mlock);
iio_trigger_notify_done(indio_dev->trig);
base = KMX61_MAG_XOUT_L;
mutex_lock(&data->lock);
- for_each_set_bit(bit, indio_dev->buffer->scan_mask,
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
ret = kmx61_read_measurement(data, base, bit);
if (ret < 0) {
* @attr_list: List of IIO device attributes
*
* This function frees the memory allocated for each of the IIO device
- * attributes in the list. Note: if you want to reuse the list after calling
- * this function you have to reinitialize it using INIT_LIST_HEAD().
+ * attributes in the list.
*/
void iio_free_chan_devattr_list(struct list_head *attr_list)
{
list_for_each_entry_safe(p, n, attr_list, l) {
kfree(p->dev_attr.attr.name);
+ list_del(&p->l);
kfree(p);
}
}
iio_free_chan_devattr_list(&indio_dev->channel_attr_list);
kfree(indio_dev->chan_attr_group.attrs);
+ indio_dev->chan_attr_group.attrs = NULL;
}
static void iio_dev_release(struct device *device)
error_free_setup_event_lines:
iio_free_chan_devattr_list(&indio_dev->event_interface->dev_attr_list);
kfree(indio_dev->event_interface);
+ indio_dev->event_interface = NULL;
return ret;
}
config GP2AP020A00F
tristate "Sharp GP2AP020A00F Proximity/ALS sensor"
depends on I2C
+ select REGMAP_I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select IRQ_WORK
config JSA1212
tristate "JSA1212 ALS and proximity sensor driver"
depends on I2C
+ select REGMAP_I2C
help
Say Y here if you want to build a IIO driver for JSA1212
proximity & ALS sensor device.
config AK09911
tristate "Asahi Kasei AK09911 3-axis Compass"
+ depends on I2C
+ depends on GPIOLIB
select AK8975
help
Deprecated: AK09911 is now supported by AK8975 driver.
mutex_lock(&data->mutex);
- for_each_set_bit(bit, indio_dev->buffer->scan_mask,
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
ret = sx9500_read_proximity(data, &indio_dev->channels[bit],
&val);
if (dmasync)
dma_set_attr(DMA_ATTR_WRITE_BARRIER, &attrs);
+ /*
+ * If the combination of the addr and size requested for this memory
+ * region causes an integer overflow, return error.
+ */
+ if ((PAGE_ALIGN(addr + size) <= size) ||
+ (PAGE_ALIGN(addr + size) <= addr))
+ return ERR_PTR(-EINVAL);
+
if (!can_do_mlock())
return ERR_PTR(-EPERM);
#define GUID_TBL_BLK_NUM_ENTRIES 8
#define GUID_TBL_BLK_SIZE (GUID_TBL_ENTRY_SIZE * GUID_TBL_BLK_NUM_ENTRIES)
+/* Counters should be saturate once they reach their maximum value */
+#define ASSIGN_32BIT_COUNTER(counter, value) do {\
+ if ((value) > U32_MAX) \
+ counter = cpu_to_be32(U32_MAX); \
+ else \
+ counter = cpu_to_be32(value); \
+} while (0)
+
struct mlx4_mad_rcv_buf {
struct ib_grh grh;
u8 payload[256];
static void edit_counter(struct mlx4_counter *cnt,
struct ib_pma_portcounters *pma_cnt)
{
- pma_cnt->port_xmit_data = cpu_to_be32((be64_to_cpu(cnt->tx_bytes)>>2));
- pma_cnt->port_rcv_data = cpu_to_be32((be64_to_cpu(cnt->rx_bytes)>>2));
- pma_cnt->port_xmit_packets = cpu_to_be32(be64_to_cpu(cnt->tx_frames));
- pma_cnt->port_rcv_packets = cpu_to_be32(be64_to_cpu(cnt->rx_frames));
+ ASSIGN_32BIT_COUNTER(pma_cnt->port_xmit_data,
+ (be64_to_cpu(cnt->tx_bytes) >> 2));
+ ASSIGN_32BIT_COUNTER(pma_cnt->port_rcv_data,
+ (be64_to_cpu(cnt->rx_bytes) >> 2));
+ ASSIGN_32BIT_COUNTER(pma_cnt->port_xmit_packets,
+ be64_to_cpu(cnt->tx_frames));
+ ASSIGN_32BIT_COUNTER(pma_cnt->port_rcv_packets,
+ be64_to_cpu(cnt->rx_frames));
}
static int iboe_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num,
spin_lock_bh(&ibdev->iboe.lock);
for (i = 0; i < MLX4_MAX_PORTS; ++i) {
struct net_device *curr_netdev = ibdev->iboe.netdevs[i];
+ enum ib_port_state curr_port_state;
- enum ib_port_state curr_port_state =
+ if (!curr_netdev)
+ continue;
+
+ curr_port_state =
(netif_running(curr_netdev) &&
netif_carrier_ok(curr_netdev)) ?
IB_PORT_ACTIVE : IB_PORT_DOWN;
input_set_drvdata(input, keypad);
- error = request_threaded_irq(irq, NULL,
- tc3589x_keypad_irq, plat->irqtype,
- "tc3589x-keypad", keypad);
+ error = request_threaded_irq(irq, NULL, tc3589x_keypad_irq,
+ plat->irqtype | IRQF_ONESHOT,
+ "tc3589x-keypad", keypad);
if (error < 0) {
dev_err(&pdev->dev,
"Could not allocate irq %d,error %d\n",
idev->private = m;
idev->input->name = MMA8450_DRV_NAME;
idev->input->id.bustype = BUS_I2C;
+ idev->input->dev.parent = &c->dev;
idev->poll = mma8450_poll;
idev->poll_interval = POLL_INTERVAL;
idev->poll_interval_max = POLL_INTERVAL_MAX;
mutex_unlock(&alps_mutex);
}
-static void alps_report_bare_ps2_packet(struct input_dev *dev,
+static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
unsigned char packet[],
bool report_buttons)
{
+ struct alps_data *priv = psmouse->private;
+ struct input_dev *dev;
+
+ /* Figure out which device to use to report the bare packet */
+ if (priv->proto_version == ALPS_PROTO_V2 &&
+ (priv->flags & ALPS_DUALPOINT)) {
+ /* On V2 devices the DualPoint Stick reports bare packets */
+ dev = priv->dev2;
+ } else if (unlikely(IS_ERR_OR_NULL(priv->dev3))) {
+ /* Register dev3 mouse if we received PS/2 packet first time */
+ if (!IS_ERR(priv->dev3))
+ psmouse_queue_work(psmouse, &priv->dev3_register_work,
+ 0);
+ return;
+ } else {
+ dev = priv->dev3;
+ }
+
if (report_buttons)
alps_report_buttons(dev, NULL,
packet[0] & 1, packet[0] & 2, packet[0] & 4);
* de-synchronization.
*/
- alps_report_bare_ps2_packet(priv->dev2,
- &psmouse->packet[3], false);
+ alps_report_bare_ps2_packet(psmouse, &psmouse->packet[3],
+ false);
/*
* Continue with the standard ALPS protocol handling,
* properly we only do this if the device is fully synchronized.
*/
if (!psmouse->out_of_sync_cnt && (psmouse->packet[0] & 0xc8) == 0x08) {
-
- /* Register dev3 mouse if we received PS/2 packet first time */
- if (unlikely(!priv->dev3))
- psmouse_queue_work(psmouse,
- &priv->dev3_register_work, 0);
-
if (psmouse->pktcnt == 3) {
- /* Once dev3 mouse device is registered report data */
- if (likely(!IS_ERR_OR_NULL(priv->dev3)))
- alps_report_bare_ps2_packet(priv->dev3,
- psmouse->packet,
- true);
+ alps_report_bare_ps2_packet(psmouse, psmouse->packet,
+ true);
return PSMOUSE_FULL_PACKET;
}
return PSMOUSE_GOOD_DATA;
priv->set_abs_params = alps_set_abs_params_mt;
priv->nibble_commands = alps_v3_nibble_commands;
priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
- priv->x_max = 1360;
- priv->y_max = 660;
priv->x_bits = 23;
priv->y_bits = 12;
+
+ if (alps_dolphin_get_device_area(psmouse, priv))
+ return -EIO;
+
break;
case ALPS_PROTO_V6:
priv->set_abs_params = alps_set_abs_params_mt;
priv->nibble_commands = alps_v3_nibble_commands;
priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
-
- if (alps_dolphin_get_device_area(psmouse, priv))
- return -EIO;
+ priv->x_max = 0xfff;
+ priv->y_max = 0x7ff;
if (priv->fw_ver[1] != 0xba)
priv->flags |= ALPS_BUTTONPAD;
return -ENOMEM;
error = alps_identify(psmouse, priv);
- if (error)
+ if (error) {
+ kfree(priv);
return error;
+ }
if (set_properties) {
psmouse->vendor = "ALPS";
#include <linux/input/mt.h>
#include <linux/module.h>
#include <linux/slab.h>
-#include <linux/unaligned/access_ok.h>
+#include <asm/unaligned.h>
#include "cyapa.h"
#include <linux/mutex.h>
#include <linux/completion.h>
#include <linux/slab.h>
-#include <linux/unaligned/access_ok.h>
+#include <asm/unaligned.h>
#include <linux/crc-itu-t.h>
#include "cyapa.h"
electrodes_tx = cyapa->electrodes_x;
max_element_cnt = ((cyapa->aligned_electrodes_rx + 7) &
~7u) * electrodes_tx;
- } else if (idac_data_type == GEN5_RETRIEVE_SELF_CAP_PWC_DATA) {
+ } else {
offset = 2;
max_element_cnt = cyapa->electrodes_x +
cyapa->electrodes_y;
#define FOC_MAX_FINGERS 5
-#define FOC_MAX_X 2431
-#define FOC_MAX_Y 1663
-
/*
* Current state of a single finger on the touchpad.
*/
input_mt_slot(dev, i);
input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
if (active) {
- input_report_abs(dev, ABS_MT_POSITION_X, finger->x);
+ unsigned int clamped_x, clamped_y;
+ /*
+ * The touchpad might report invalid data, so we clamp
+ * the resulting values so that we do not confuse
+ * userspace.
+ */
+ clamped_x = clamp(finger->x, 0U, priv->x_max);
+ clamped_y = clamp(finger->y, 0U, priv->y_max);
+ input_report_abs(dev, ABS_MT_POSITION_X, clamped_x);
input_report_abs(dev, ABS_MT_POSITION_Y,
- FOC_MAX_Y - finger->y);
+ priv->y_max - clamped_y);
}
}
input_mt_report_pointer_emulation(dev, true);
state->pressed = (packet[0] >> 4) & 1;
- /*
- * packet[5] contains some kind of tool size in the most
- * significant nibble. 0xff is a special value (latching) that
- * signals a large contact area.
- */
- if (packet[5] == 0xff) {
- state->fingers[finger].valid = false;
- return;
- }
-
state->fingers[finger].x = ((packet[1] & 0xf) << 8) | packet[2];
state->fingers[finger].y = (packet[3] << 8) | packet[4];
state->fingers[finger].valid = true;
return 0;
}
+
+void focaltech_set_resolution(struct psmouse *psmouse, unsigned int resolution)
+{
+ /* not supported yet */
+}
+
+static void focaltech_set_rate(struct psmouse *psmouse, unsigned int rate)
+{
+ /* not supported yet */
+}
+
+static void focaltech_set_scale(struct psmouse *psmouse,
+ enum psmouse_scale scale)
+{
+ /* not supported yet */
+}
+
int focaltech_init(struct psmouse *psmouse)
{
struct focaltech_data *priv;
psmouse->cleanup = focaltech_reset;
/* resync is not supported yet */
psmouse->resync_time = 0;
+ /*
+ * rate/resolution/scale changes are not supported yet, and
+ * the generic implementations of these functions seem to
+ * confuse some touchpads
+ */
+ psmouse->set_resolution = focaltech_set_resolution;
+ psmouse->set_rate = focaltech_set_rate;
+ psmouse->set_scale = focaltech_set_scale;
return 0;
psmouse->rate = r;
}
+/*
+ * Here we set the mouse scaling.
+ */
+
+static void psmouse_set_scale(struct psmouse *psmouse, enum psmouse_scale scale)
+{
+ ps2_command(&psmouse->ps2dev, NULL,
+ scale == PSMOUSE_SCALE21 ? PSMOUSE_CMD_SETSCALE21 :
+ PSMOUSE_CMD_SETSCALE11);
+}
+
/*
* psmouse_poll() - default poll handler. Everyone except for ALPS uses it.
*/
psmouse->set_rate = psmouse_set_rate;
psmouse->set_resolution = psmouse_set_resolution;
+ psmouse->set_scale = psmouse_set_scale;
psmouse->poll = psmouse_poll;
psmouse->protocol_handler = psmouse_process_byte;
psmouse->pktsize = 3;
if (psmouse_max_proto != PSMOUSE_PS2) {
psmouse->set_rate(psmouse, psmouse->rate);
psmouse->set_resolution(psmouse, psmouse->resolution);
- ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
+ psmouse->set_scale(psmouse, PSMOUSE_SCALE11);
}
}
PSMOUSE_FULL_PACKET
} psmouse_ret_t;
+enum psmouse_scale {
+ PSMOUSE_SCALE11,
+ PSMOUSE_SCALE21
+};
+
struct psmouse {
void *private;
struct input_dev *dev;
psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse);
void (*set_rate)(struct psmouse *psmouse, unsigned int rate);
void (*set_resolution)(struct psmouse *psmouse, unsigned int resolution);
+ void (*set_scale)(struct psmouse *psmouse, enum psmouse_scale scale);
int (*reconnect)(struct psmouse *psmouse);
void (*disconnect)(struct psmouse *psmouse);
#define X_MAX_POSITIVE 8176
#define Y_MAX_POSITIVE 8176
-/* maximum ABS_MT_POSITION displacement (in mm) */
-#define DMAX 10
-
/*****************************************************************************
* Stuff we need even when we do not want native Synaptics support
****************************************************************************/
static bool cr48_profile_sensor;
+#define ANY_BOARD_ID 0
struct min_max_quirk {
const char * const *pnp_ids;
+ struct {
+ unsigned long int min, max;
+ } board_id;
int x_min, x_max, y_min, y_max;
};
static const struct min_max_quirk min_max_pnpid_table[] = {
{
(const char * const []){"LEN0033", NULL},
+ {ANY_BOARD_ID, ANY_BOARD_ID},
1024, 5052, 2258, 4832
},
{
- (const char * const []){"LEN0035", "LEN0042", NULL},
+ (const char * const []){"LEN0042", NULL},
+ {ANY_BOARD_ID, ANY_BOARD_ID},
1232, 5710, 1156, 4696
},
{
(const char * const []){"LEN0034", "LEN0036", "LEN0037",
"LEN0039", "LEN2002", "LEN2004",
NULL},
+ {ANY_BOARD_ID, 2961},
1024, 5112, 2024, 4832
},
{
(const char * const []){"LEN2001", NULL},
+ {ANY_BOARD_ID, ANY_BOARD_ID},
1024, 5022, 2508, 4832
},
{
(const char * const []){"LEN2006", NULL},
+ {2691, 2691},
+ 1024, 5045, 2457, 4832
+ },
+ {
+ (const char * const []){"LEN2006", NULL},
+ {ANY_BOARD_ID, ANY_BOARD_ID},
1264, 5675, 1171, 4688
},
{ }
"LEN0041",
"LEN0042", /* Yoga */
"LEN0045",
- "LEN0046",
"LEN0047",
- "LEN0048",
"LEN0049",
"LEN2000",
"LEN2001", /* Edge E431 */
"LEN2003",
"LEN2004", /* L440 */
"LEN2005",
- "LEN2006",
+ "LEN2006", /* Edge E440/E540 */
"LEN2007",
"LEN2008",
"LEN2009",
return 0;
}
+static int synaptics_more_extended_queries(struct psmouse *psmouse)
+{
+ struct synaptics_data *priv = psmouse->private;
+ unsigned char buf[3];
+
+ if (synaptics_send_cmd(psmouse, SYN_QUE_MEXT_CAPAB_10, buf))
+ return -1;
+
+ priv->ext_cap_10 = (buf[0]<<16) | (buf[1]<<8) | buf[2];
+
+ return 0;
+}
+
/*
- * Read the board id from the touchpad
+ * Read the board id and the "More Extended Queries" from the touchpad
* The board id is encoded in the "QUERY MODES" response
*/
-static int synaptics_board_id(struct psmouse *psmouse)
+static int synaptics_query_modes(struct psmouse *psmouse)
{
struct synaptics_data *priv = psmouse->private;
unsigned char bid[3];
+ /* firmwares prior 7.5 have no board_id encoded */
+ if (SYN_ID_FULL(priv->identity) < 0x705)
+ return 0;
+
if (synaptics_send_cmd(psmouse, SYN_QUE_MODES, bid))
return -1;
priv->board_id = ((bid[0] & 0xfc) << 6) | bid[1];
+
+ if (SYN_MEXT_CAP_BIT(bid[0]))
+ return synaptics_more_extended_queries(psmouse);
+
return 0;
}
{
struct synaptics_data *priv = psmouse->private;
unsigned char resp[3];
- int i;
if (SYN_ID_MAJOR(priv->identity) < 4)
return 0;
}
}
- for (i = 0; min_max_pnpid_table[i].pnp_ids; i++) {
- if (psmouse_matches_pnp_id(psmouse,
- min_max_pnpid_table[i].pnp_ids)) {
- priv->x_min = min_max_pnpid_table[i].x_min;
- priv->x_max = min_max_pnpid_table[i].x_max;
- priv->y_min = min_max_pnpid_table[i].y_min;
- priv->y_max = min_max_pnpid_table[i].y_max;
- return 0;
- }
- }
-
if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 5 &&
SYN_CAP_MAX_DIMENSIONS(priv->ext_cap_0c)) {
if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MAX_COORDS, resp)) {
} else {
priv->x_max = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
priv->y_max = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
+ psmouse_info(psmouse,
+ "queried max coordinates: x [..%d], y [..%d]\n",
+ priv->x_max, priv->y_max);
}
}
- if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 7 &&
- SYN_CAP_MIN_DIMENSIONS(priv->ext_cap_0c)) {
+ if (SYN_CAP_MIN_DIMENSIONS(priv->ext_cap_0c) &&
+ (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 7 ||
+ /*
+ * Firmware v8.1 does not report proper number of extended
+ * capabilities, but has been proven to report correct min
+ * coordinates.
+ */
+ SYN_ID_FULL(priv->identity) == 0x801)) {
if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MIN_COORDS, resp)) {
psmouse_warn(psmouse,
"device claims to have min coordinates query, but I'm not able to read it.\n");
} else {
priv->x_min = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
priv->y_min = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
+ psmouse_info(psmouse,
+ "queried min coordinates: x [%d..], y [%d..]\n",
+ priv->x_min, priv->y_min);
}
}
return 0;
}
+/*
+ * Apply quirk(s) if the hardware matches
+ */
+
+static void synaptics_apply_quirks(struct psmouse *psmouse)
+{
+ struct synaptics_data *priv = psmouse->private;
+ int i;
+
+ for (i = 0; min_max_pnpid_table[i].pnp_ids; i++) {
+ if (!psmouse_matches_pnp_id(psmouse,
+ min_max_pnpid_table[i].pnp_ids))
+ continue;
+
+ if (min_max_pnpid_table[i].board_id.min != ANY_BOARD_ID &&
+ priv->board_id < min_max_pnpid_table[i].board_id.min)
+ continue;
+
+ if (min_max_pnpid_table[i].board_id.max != ANY_BOARD_ID &&
+ priv->board_id > min_max_pnpid_table[i].board_id.max)
+ continue;
+
+ priv->x_min = min_max_pnpid_table[i].x_min;
+ priv->x_max = min_max_pnpid_table[i].x_max;
+ priv->y_min = min_max_pnpid_table[i].y_min;
+ priv->y_max = min_max_pnpid_table[i].y_max;
+ psmouse_info(psmouse,
+ "quirked min/max coordinates: x [%d..%d], y [%d..%d]\n",
+ priv->x_min, priv->x_max,
+ priv->y_min, priv->y_max);
+ break;
+ }
+}
+
static int synaptics_query_hardware(struct psmouse *psmouse)
{
if (synaptics_identify(psmouse))
return -1;
if (synaptics_firmware_id(psmouse))
return -1;
- if (synaptics_board_id(psmouse))
+ if (synaptics_query_modes(psmouse))
return -1;
if (synaptics_capability(psmouse))
return -1;
if (synaptics_resolution(psmouse))
return -1;
+ synaptics_apply_quirks(psmouse);
+
return 0;
}
return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4;
}
-static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet)
+static void synaptics_pass_pt_packet(struct psmouse *psmouse,
+ struct serio *ptport,
+ unsigned char *packet)
{
+ struct synaptics_data *priv = psmouse->private;
struct psmouse *child = serio_get_drvdata(ptport);
if (child && child->state == PSMOUSE_ACTIVATED) {
- serio_interrupt(ptport, packet[1], 0);
+ serio_interrupt(ptport, packet[1] | priv->pt_buttons, 0);
serio_interrupt(ptport, packet[4], 0);
serio_interrupt(ptport, packet[5], 0);
if (child->pktsize == 4)
serio_interrupt(ptport, packet[2], 0);
- } else
+ } else {
serio_interrupt(ptport, packet[1], 0);
+ }
}
static void synaptics_pt_activate(struct psmouse *psmouse)
}
}
+static void synaptics_parse_ext_buttons(const unsigned char buf[],
+ struct synaptics_data *priv,
+ struct synaptics_hw_state *hw)
+{
+ unsigned int ext_bits =
+ (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1;
+ unsigned int ext_mask = GENMASK(ext_bits - 1, 0);
+
+ hw->ext_buttons = buf[4] & ext_mask;
+ hw->ext_buttons |= (buf[5] & ext_mask) << ext_bits;
+}
+
static bool is_forcepad;
static int synaptics_parse_hw_state(const unsigned char buf[],
hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0;
}
- if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) &&
+ if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 0 &&
((buf[0] ^ buf[3]) & 0x02)) {
- switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) {
- default:
- /*
- * if nExtBtn is greater than 8 it should be
- * considered invalid and treated as 0
- */
- break;
- case 8:
- hw->ext_buttons |= ((buf[5] & 0x08)) ? 0x80 : 0;
- hw->ext_buttons |= ((buf[4] & 0x08)) ? 0x40 : 0;
- case 6:
- hw->ext_buttons |= ((buf[5] & 0x04)) ? 0x20 : 0;
- hw->ext_buttons |= ((buf[4] & 0x04)) ? 0x10 : 0;
- case 4:
- hw->ext_buttons |= ((buf[5] & 0x02)) ? 0x08 : 0;
- hw->ext_buttons |= ((buf[4] & 0x02)) ? 0x04 : 0;
- case 2:
- hw->ext_buttons |= ((buf[5] & 0x01)) ? 0x02 : 0;
- hw->ext_buttons |= ((buf[4] & 0x01)) ? 0x01 : 0;
- }
+ synaptics_parse_ext_buttons(buf, priv, hw);
}
} else {
hw->x = (((buf[1] & 0x1f) << 8) | buf[2]);
}
}
+static void synaptics_report_ext_buttons(struct psmouse *psmouse,
+ const struct synaptics_hw_state *hw)
+{
+ struct input_dev *dev = psmouse->dev;
+ struct synaptics_data *priv = psmouse->private;
+ int ext_bits = (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1;
+ char buf[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ int i;
+
+ if (!SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap))
+ return;
+
+ /* Bug in FW 8.1, buttons are reported only when ExtBit is 1 */
+ if (SYN_ID_FULL(priv->identity) == 0x801 &&
+ !((psmouse->packet[0] ^ psmouse->packet[3]) & 0x02))
+ return;
+
+ if (!SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10)) {
+ for (i = 0; i < ext_bits; i++) {
+ input_report_key(dev, BTN_0 + 2 * i,
+ hw->ext_buttons & (1 << i));
+ input_report_key(dev, BTN_1 + 2 * i,
+ hw->ext_buttons & (1 << (i + ext_bits)));
+ }
+ return;
+ }
+
+ /*
+ * This generation of touchpads has the trackstick buttons
+ * physically wired to the touchpad. Re-route them through
+ * the pass-through interface.
+ */
+ if (!priv->pt_port)
+ return;
+
+ /* The trackstick expects at most 3 buttons */
+ priv->pt_buttons = SYN_CAP_EXT_BUTTON_STICK_L(hw->ext_buttons) |
+ SYN_CAP_EXT_BUTTON_STICK_R(hw->ext_buttons) << 1 |
+ SYN_CAP_EXT_BUTTON_STICK_M(hw->ext_buttons) << 2;
+
+ synaptics_pass_pt_packet(psmouse, priv->pt_port, buf);
+}
+
static void synaptics_report_buttons(struct psmouse *psmouse,
const struct synaptics_hw_state *hw)
{
struct input_dev *dev = psmouse->dev;
struct synaptics_data *priv = psmouse->private;
- int i;
input_report_key(dev, BTN_LEFT, hw->left);
input_report_key(dev, BTN_RIGHT, hw->right);
input_report_key(dev, BTN_BACK, hw->down);
}
- for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
- input_report_key(dev, BTN_0 + i, hw->ext_buttons & (1 << i));
+ synaptics_report_ext_buttons(psmouse, hw);
}
static void synaptics_report_mt_data(struct psmouse *psmouse,
pos[i].y = synaptics_invert_y(hw[i]->y);
}
- input_mt_assign_slots(dev, slot, pos, nsemi, DMAX * priv->x_res);
+ input_mt_assign_slots(dev, slot, pos, nsemi, 0);
for (i = 0; i < nsemi; i++) {
input_mt_slot(dev, slot[i]);
if (SYN_CAP_PASS_THROUGH(priv->capabilities) &&
synaptics_is_pt_packet(psmouse->packet)) {
if (priv->pt_port)
- synaptics_pass_pt_packet(priv->pt_port, psmouse->packet);
+ synaptics_pass_pt_packet(psmouse, priv->pt_port,
+ psmouse->packet);
} else
synaptics_process_packet(psmouse);
__set_bit(BTN_BACK, dev->keybit);
}
- for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
- __set_bit(BTN_0 + i, dev->keybit);
+ if (!SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10))
+ for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
+ __set_bit(BTN_0 + i, dev->keybit);
__clear_bit(EV_REL, dev->evbit);
__clear_bit(REL_X, dev->relbit);
if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) {
__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
- if (psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids))
+ if (psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) &&
+ !SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10))
__set_bit(INPUT_PROP_TOPBUTTONPAD, dev->propbit);
/* Clickpads report only left button */
__clear_bit(BTN_RIGHT, dev->keybit);
#define SYN_QUE_EXT_CAPAB_0C 0x0c
#define SYN_QUE_EXT_MAX_COORDS 0x0d
#define SYN_QUE_EXT_MIN_COORDS 0x0f
+#define SYN_QUE_MEXT_CAPAB_10 0x10
/* synatics modes */
#define SYN_BIT_ABSOLUTE_MODE (1 << 7)
#define SYN_EXT_CAP_REQUESTS(c) (((c) & 0x700000) >> 20)
#define SYN_CAP_MULTI_BUTTON_NO(ec) (((ec) & 0x00f000) >> 12)
#define SYN_CAP_PRODUCT_ID(ec) (((ec) & 0xff0000) >> 16)
+#define SYN_MEXT_CAP_BIT(m) ((m) & (1 << 1))
/*
* The following describes response for the 0x0c query.
#define SYN_CAP_REDUCED_FILTERING(ex0c) ((ex0c) & 0x000400)
#define SYN_CAP_IMAGE_SENSOR(ex0c) ((ex0c) & 0x000800)
+/*
+ * The following descibes response for the 0x10 query.
+ *
+ * byte mask name meaning
+ * ---- ---- ------- ------------
+ * 1 0x01 ext buttons are stick buttons exported in the extended
+ * capability are actually meant to be used
+ * by the tracktick (pass-through).
+ * 1 0x02 SecurePad the touchpad is a SecurePad, so it
+ * contains a built-in fingerprint reader.
+ * 1 0xe0 more ext count how many more extented queries are
+ * available after this one.
+ * 2 0xff SecurePad width the width of the SecurePad fingerprint
+ * reader.
+ * 3 0xff SecurePad height the height of the SecurePad fingerprint
+ * reader.
+ */
+#define SYN_CAP_EXT_BUTTONS_STICK(ex10) ((ex10) & 0x010000)
+#define SYN_CAP_SECUREPAD(ex10) ((ex10) & 0x020000)
+
+#define SYN_CAP_EXT_BUTTON_STICK_L(eb) (!!((eb) & 0x01))
+#define SYN_CAP_EXT_BUTTON_STICK_M(eb) (!!((eb) & 0x02))
+#define SYN_CAP_EXT_BUTTON_STICK_R(eb) (!!((eb) & 0x04))
+
/* synaptics modes query bits */
#define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7))
#define SYN_MODE_RATE(m) ((m) & (1 << 6))
unsigned long int capabilities; /* Capabilities */
unsigned long int ext_cap; /* Extended Capabilities */
unsigned long int ext_cap_0c; /* Ext Caps from 0x0c query */
+ unsigned long int ext_cap_10; /* Ext Caps from 0x10 query */
unsigned long int identity; /* Identification */
unsigned int x_res, y_res; /* X/Y resolution in units/mm */
unsigned int x_max, y_max; /* Max coordinates (from FW) */
bool disable_gesture; /* disable gestures */
struct serio *pt_port; /* Pass-through serio port */
+ unsigned char pt_buttons; /* Pass-through buttons */
/*
* Last received Advanced Gesture Mode (AGM) packet. An AGM packet
tristate "Allwinner sun4i resistive touchscreen controller support"
depends on ARCH_SUNXI || COMPILE_TEST
depends on HWMON
+ depends on THERMAL || !THERMAL_OF
help
This selects support for the resistive touchscreen controller
found on Allwinner sunxi SoCs.
config IOMMU_IO_PGTABLE_LPAE
bool "ARMv7/v8 Long Descriptor Format"
select IOMMU_IO_PGTABLE
+ depends on ARM || ARM64 || COMPILE_TEST
help
Enable support for the ARM long descriptor pagetable format.
This allocator supports 4K/2M/1G, 16K/32M and 64K/512M page
bool "MSM IOMMU Support"
depends on ARM
depends on ARCH_MSM8X60 || ARCH_MSM8960 || COMPILE_TEST
+ depends on BROKEN
select IOMMU_API
help
Support for the IOMMUs found on certain Qualcomm SOCs.
return 0;
spin_lock_irqsave(&smmu_domain->pgtbl_lock, flags);
- if (smmu_domain->smmu->features & ARM_SMMU_FEAT_TRANS_OPS)
+ if (smmu_domain->smmu->features & ARM_SMMU_FEAT_TRANS_OPS &&
+ smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
ret = arm_smmu_iova_to_phys_hard(domain, iova);
- else
+ } else {
ret = ops->iova_to_phys(ops, iova);
+ }
+
spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags);
return ret;
return -ENODEV;
}
- if (smmu->version == 1 || (!(id & ID0_ATOSNS) && (id & ID0_S1TS))) {
+ if ((id & ID0_S1TS) && ((smmu->version == 1) || (id & ID0_ATOSNS))) {
smmu->features |= ARM_SMMU_FEAT_TRANS_OPS;
dev_notice(smmu->dev, "\taddress translation ops\n");
}
static int __init exynos_iommu_init(void)
{
+ struct device_node *np;
int ret;
+ np = of_find_matching_node(NULL, sysmmu_of_match);
+ if (!np)
+ return 0;
+
+ of_node_put(np);
+
lv2table_kmem_cache = kmem_cache_create("exynos-iommu-lv2table",
LV2TABLE_SIZE, LV2TABLE_SIZE, 0, NULL);
if (!lv2table_kmem_cache) {
static void domain_exit(struct dmar_domain *domain)
{
- struct dmar_drhd_unit *drhd;
- struct intel_iommu *iommu;
struct page *freelist = NULL;
+ int i;
/* Domain 0 is reserved, so dont process it */
if (!domain)
/* clear attached or cached domains */
rcu_read_lock();
- for_each_active_iommu(iommu, drhd)
- iommu_detach_domain(domain, iommu);
+ for_each_set_bit(i, domain->iommu_bmp, g_num_of_iommus)
+ iommu_detach_domain(domain, g_iommus[i]);
rcu_read_unlock();
dma_free_pagelist(freelist);
((((d)->levels - ((l) - ARM_LPAE_START_LVL(d) + 1)) \
* (d)->bits_per_level) + (d)->pg_shift)
-#define ARM_LPAE_PAGES_PER_PGD(d) ((d)->pgd_size >> (d)->pg_shift)
+#define ARM_LPAE_PAGES_PER_PGD(d) \
+ DIV_ROUND_UP((d)->pgd_size, 1UL << (d)->pg_shift)
/*
* Calculate the index at level l used to map virtual address a using the
((l) == ARM_LPAE_START_LVL(d) ? ilog2(ARM_LPAE_PAGES_PER_PGD(d)) : 0)
#define ARM_LPAE_LVL_IDX(a,l,d) \
- (((a) >> ARM_LPAE_LVL_SHIFT(l,d)) & \
+ (((u64)(a) >> ARM_LPAE_LVL_SHIFT(l,d)) & \
((1 << ((d)->bits_per_level + ARM_LPAE_PGD_IDX(l,d))) - 1))
/* Calculate the block/page mapping size at level l for pagetable in d. */
static const struct of_device_id ipmmu_of_ids[] = {
{ .compatible = "renesas,ipmmu-vmsa", },
+ { }
};
static struct platform_driver ipmmu_driver = {
struct kmem_cache *p;
const unsigned long flags = SLAB_HWCACHE_ALIGN;
size_t align = 1 << 10; /* L2 pagetable alignement */
+ struct device_node *np;
+
+ np = of_find_matching_node(NULL, omap_iommu_of_match);
+ if (!np)
+ return 0;
+
+ of_node_put(np);
p = kmem_cache_create("iopte_cache", IOPTE_TABLE_SIZE, align, flags,
iopte_cachep_ctor);
static int __init rk_iommu_init(void)
{
+ struct device_node *np;
int ret;
+ np = of_find_matching_node(NULL, rk_iommu_dt_ids);
+ if (!np)
+ return 0;
+
+ of_node_put(np);
+
ret = bus_set_iommu(&platform_bus_type, &rk_iommu_ops);
if (ret)
return ret;
static void __iomem *main_int_base;
static struct irq_domain *armada_370_xp_mpic_domain;
static u32 doorbell_mask_reg;
+static int parent_irq;
#ifdef CONFIG_PCI_MSI
static struct irq_domain *armada_370_xp_msi_domain;
static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR);
{
if (action == CPU_STARTING || action == CPU_STARTING_FROZEN)
armada_xp_mpic_smp_cpu_init();
+
return NOTIFY_OK;
}
.priority = 100,
};
+static int mpic_cascaded_secondary_init(struct notifier_block *nfb,
+ unsigned long action, void *hcpu)
+{
+ if (action == CPU_STARTING || action == CPU_STARTING_FROZEN)
+ enable_percpu_irq(parent_irq, IRQ_TYPE_NONE);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block mpic_cascaded_cpu_notifier = {
+ .notifier_call = mpic_cascaded_secondary_init,
+ .priority = 100,
+};
+
#endif /* CONFIG_SMP */
static struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
struct device_node *parent)
{
struct resource main_int_res, per_cpu_int_res;
- int parent_irq, nr_irqs, i;
+ int nr_irqs, i;
u32 control;
BUG_ON(of_address_to_resource(node, 0, &main_int_res));
register_cpu_notifier(&armada_370_xp_mpic_cpu_notifier);
#endif
} else {
+#ifdef CONFIG_SMP
+ register_cpu_notifier(&mpic_cascaded_cpu_notifier);
+#endif
irq_set_chained_handler(parent_irq,
armada_370_xp_mpic_handle_cascade_irq);
}
static void its_encode_devid(struct its_cmd_block *cmd, u32 devid)
{
- cmd->raw_cmd[0] &= ~(0xffffUL << 32);
+ cmd->raw_cmd[0] &= BIT_ULL(32) - 1;
cmd->raw_cmd[0] |= ((u64)devid) << 32;
}
{
struct its_cmd_block *cmd, *sync_cmd, *next_cmd;
struct its_collection *sync_col;
+ unsigned long flags;
- raw_spin_lock(&its->lock);
+ raw_spin_lock_irqsave(&its->lock, flags);
cmd = its_allocate_entry(its);
if (!cmd) { /* We're soooooo screewed... */
pr_err_ratelimited("ITS can't allocate, dropping command\n");
- raw_spin_unlock(&its->lock);
+ raw_spin_unlock_irqrestore(&its->lock, flags);
return;
}
sync_col = builder(cmd, desc);
post:
next_cmd = its_post_commands(its);
- raw_spin_unlock(&its->lock);
+ raw_spin_unlock_irqrestore(&its->lock, flags);
its_wait_for_range_completion(its, cmd, next_cmd);
}
{
int err;
int i;
- int psz = PAGE_SIZE;
+ int psz = SZ_64K;
u64 shr = GITS_BASER_InnerShareable;
+ u64 cache = GITS_BASER_WaWb;
for (i = 0; i < GITS_BASER_NR_REGS; i++) {
u64 val = readq_relaxed(its->base + GITS_BASER + i * 8);
u64 type = GITS_BASER_TYPE(val);
u64 entry_size = GITS_BASER_ENTRY_SIZE(val);
+ int order = get_order(psz);
+ int alloc_size;
u64 tmp;
void *base;
if (type == GITS_BASER_TYPE_NONE)
continue;
- /* We're lazy and only allocate a single page for now */
- base = (void *)get_zeroed_page(GFP_KERNEL);
+ /*
+ * Allocate as many entries as required to fit the
+ * range of device IDs that the ITS can grok... The ID
+ * space being incredibly sparse, this results in a
+ * massive waste of memory.
+ *
+ * For other tables, only allocate a single page.
+ */
+ if (type == GITS_BASER_TYPE_DEVICE) {
+ u64 typer = readq_relaxed(its->base + GITS_TYPER);
+ u32 ids = GITS_TYPER_DEVBITS(typer);
+
+ order = get_order((1UL << ids) * entry_size);
+ if (order >= MAX_ORDER) {
+ order = MAX_ORDER - 1;
+ pr_warn("%s: Device Table too large, reduce its page order to %u\n",
+ its->msi_chip.of_node->full_name, order);
+ }
+ }
+
+ alloc_size = (1 << order) * PAGE_SIZE;
+ base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, order);
if (!base) {
err = -ENOMEM;
goto out_free;
val = (virt_to_phys(base) |
(type << GITS_BASER_TYPE_SHIFT) |
((entry_size - 1) << GITS_BASER_ENTRY_SIZE_SHIFT) |
- GITS_BASER_WaWb |
+ cache |
shr |
GITS_BASER_VALID);
break;
}
- val |= (PAGE_SIZE / psz) - 1;
+ val |= (alloc_size / psz) - 1;
writeq_relaxed(val, its->base + GITS_BASER + i * 8);
tmp = readq_relaxed(its->base + GITS_BASER + i * 8);
* Shareability didn't stick. Just use
* whatever the read reported, which is likely
* to be the only thing this redistributor
- * supports.
+ * supports. If that's zero, make it
+ * non-cacheable as well.
*/
shr = tmp & GITS_BASER_SHAREABILITY_MASK;
+ if (!shr)
+ cache = GITS_BASER_nC;
goto retry_baser;
}
}
pr_info("ITS: allocated %d %s @%lx (psz %dK, shr %d)\n",
- (int)(PAGE_SIZE / entry_size),
+ (int)(alloc_size / entry_size),
its_base_type_string[type],
(unsigned long)virt_to_phys(base),
psz / SZ_1K, (int)shr >> GITS_BASER_SHAREABILITY_SHIFT);
tmp = readq_relaxed(rbase + GICR_PROPBASER);
if ((tmp ^ val) & GICR_PROPBASER_SHAREABILITY_MASK) {
+ if (!(tmp & GICR_PROPBASER_SHAREABILITY_MASK)) {
+ /*
+ * The HW reports non-shareable, we must
+ * remove the cacheability attributes as
+ * well.
+ */
+ val &= ~(GICR_PROPBASER_SHAREABILITY_MASK |
+ GICR_PROPBASER_CACHEABILITY_MASK);
+ val |= GICR_PROPBASER_nC;
+ writeq_relaxed(val, rbase + GICR_PROPBASER);
+ }
pr_info_once("GIC: using cache flushing for LPI property table\n");
gic_rdists->flags |= RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING;
}
/* set PENDBASE */
val = (page_to_phys(pend_page) |
- GICR_PROPBASER_InnerShareable |
- GICR_PROPBASER_WaWb);
+ GICR_PENDBASER_InnerShareable |
+ GICR_PENDBASER_WaWb);
writeq_relaxed(val, rbase + GICR_PENDBASER);
+ tmp = readq_relaxed(rbase + GICR_PENDBASER);
+
+ if (!(tmp & GICR_PENDBASER_SHAREABILITY_MASK)) {
+ /*
+ * The HW reports non-shareable, we must remove the
+ * cacheability attributes as well.
+ */
+ val &= ~(GICR_PENDBASER_SHAREABILITY_MASK |
+ GICR_PENDBASER_CACHEABILITY_MASK);
+ val |= GICR_PENDBASER_nC;
+ writeq_relaxed(val, rbase + GICR_PENDBASER);
+ }
/* Enable LPIs */
val = readl_relaxed(rbase + GICR_CTLR);
* This ITS wants a linear CPU number.
*/
target = readq_relaxed(gic_data_rdist_rd_base() + GICR_TYPER);
- target = GICR_TYPER_CPU_NUMBER(target);
+ target = GICR_TYPER_CPU_NUMBER(target) << 16;
}
/* Perform collection mapping */
static struct its_device *its_find_device(struct its_node *its, u32 dev_id)
{
struct its_device *its_dev = NULL, *tmp;
+ unsigned long flags;
- raw_spin_lock(&its->lock);
+ raw_spin_lock_irqsave(&its->lock, flags);
list_for_each_entry(tmp, &its->its_device_list, entry) {
if (tmp->device_id == dev_id) {
}
}
- raw_spin_unlock(&its->lock);
+ raw_spin_unlock_irqrestore(&its->lock, flags);
return its_dev;
}
{
struct its_device *dev;
unsigned long *lpi_map;
+ unsigned long flags;
void *itt;
int lpi_base;
int nr_lpis;
nr_ites = max(2UL, roundup_pow_of_two(nvecs));
sz = nr_ites * its->ite_size;
sz = max(sz, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1;
- itt = kmalloc(sz, GFP_KERNEL);
+ itt = kzalloc(sz, GFP_KERNEL);
lpi_map = its_lpi_alloc_chunks(nvecs, &lpi_base, &nr_lpis);
if (!dev || !itt || !lpi_map) {
dev->device_id = dev_id;
INIT_LIST_HEAD(&dev->entry);
- raw_spin_lock(&its->lock);
+ raw_spin_lock_irqsave(&its->lock, flags);
list_add(&dev->entry, &its->its_device_list);
- raw_spin_unlock(&its->lock);
+ raw_spin_unlock_irqrestore(&its->lock, flags);
/* Bind the device to the first possible CPU */
cpu = cpumask_first(cpu_online_mask);
static void its_free_device(struct its_device *its_dev)
{
- raw_spin_lock(&its_dev->its->lock);
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&its_dev->its->lock, flags);
list_del(&its_dev->entry);
- raw_spin_unlock(&its_dev->its->lock);
+ raw_spin_unlock_irqrestore(&its_dev->its->lock, flags);
kfree(its_dev->itt);
kfree(its_dev);
}
return 0;
}
+struct its_pci_alias {
+ struct pci_dev *pdev;
+ u32 dev_id;
+ u32 count;
+};
+
+static int its_pci_msi_vec_count(struct pci_dev *pdev)
+{
+ int msi, msix;
+
+ msi = max(pci_msi_vec_count(pdev), 0);
+ msix = max(pci_msix_vec_count(pdev), 0);
+
+ return max(msi, msix);
+}
+
+static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data)
+{
+ struct its_pci_alias *dev_alias = data;
+
+ dev_alias->dev_id = alias;
+ if (pdev != dev_alias->pdev)
+ dev_alias->count += its_pci_msi_vec_count(dev_alias->pdev);
+
+ return 0;
+}
+
static int its_msi_prepare(struct irq_domain *domain, struct device *dev,
int nvec, msi_alloc_info_t *info)
{
struct pci_dev *pdev;
struct its_node *its;
- u32 dev_id;
struct its_device *its_dev;
+ struct its_pci_alias dev_alias;
if (!dev_is_pci(dev))
return -EINVAL;
pdev = to_pci_dev(dev);
- dev_id = PCI_DEVID(pdev->bus->number, pdev->devfn);
+ dev_alias.pdev = pdev;
+ dev_alias.count = nvec;
+
+ pci_for_each_dma_alias(pdev, its_get_pci_alias, &dev_alias);
its = domain->parent->host_data;
- its_dev = its_find_device(its, dev_id);
- if (WARN_ON(its_dev))
- return -EINVAL;
+ its_dev = its_find_device(its, dev_alias.dev_id);
+ if (its_dev) {
+ /*
+ * We already have seen this ID, probably through
+ * another alias (PCI bridge of some sort). No need to
+ * create the device.
+ */
+ dev_dbg(dev, "Reusing ITT for devID %x\n", dev_alias.dev_id);
+ goto out;
+ }
- its_dev = its_create_device(its, dev_id, nvec);
+ its_dev = its_create_device(its, dev_alias.dev_id, dev_alias.count);
if (!its_dev)
return -ENOMEM;
- dev_dbg(&pdev->dev, "ITT %d entries, %d bits\n", nvec, ilog2(nvec));
-
+ dev_dbg(&pdev->dev, "ITT %d entries, %d bits\n",
+ dev_alias.count, ilog2(dev_alias.count));
+out:
info->scratchpad[0].ptr = its_dev;
info->scratchpad[1].ptr = dev;
return 0;
.deactivate = its_irq_domain_deactivate,
};
+static int its_force_quiescent(void __iomem *base)
+{
+ u32 count = 1000000; /* 1s */
+ u32 val;
+
+ val = readl_relaxed(base + GITS_CTLR);
+ if (val & GITS_CTLR_QUIESCENT)
+ return 0;
+
+ /* Disable the generation of all interrupts to this ITS */
+ val &= ~GITS_CTLR_ENABLE;
+ writel_relaxed(val, base + GITS_CTLR);
+
+ /* Poll GITS_CTLR and wait until ITS becomes quiescent */
+ while (1) {
+ val = readl_relaxed(base + GITS_CTLR);
+ if (val & GITS_CTLR_QUIESCENT)
+ return 0;
+
+ count--;
+ if (!count)
+ return -EBUSY;
+
+ cpu_relax();
+ udelay(1);
+ }
+}
+
static int its_probe(struct device_node *node, struct irq_domain *parent)
{
struct resource res;
goto out_unmap;
}
+ err = its_force_quiescent(its_base);
+ if (err) {
+ pr_warn("%s: failed to quiesce, giving up\n",
+ node->full_name);
+ goto out_unmap;
+ }
+
pr_info("ITS: %s\n", node->full_name);
its = kzalloc(sizeof(*its), GFP_KERNEL);
writeq_relaxed(baser, its->base + GITS_CBASER);
tmp = readq_relaxed(its->base + GITS_CBASER);
- writeq_relaxed(0, its->base + GITS_CWRITER);
- writel_relaxed(1, its->base + GITS_CTLR);
- if ((tmp ^ baser) & GITS_BASER_SHAREABILITY_MASK) {
+ if ((tmp ^ baser) & GITS_CBASER_SHAREABILITY_MASK) {
+ if (!(tmp & GITS_CBASER_SHAREABILITY_MASK)) {
+ /*
+ * The HW reports non-shareable, we must
+ * remove the cacheability attributes as
+ * well.
+ */
+ baser &= ~(GITS_CBASER_SHAREABILITY_MASK |
+ GITS_CBASER_CACHEABILITY_MASK);
+ baser |= GITS_CBASER_nC;
+ writeq_relaxed(baser, its->base + GITS_CBASER);
+ }
pr_info("ITS: using cache flushing for cmd queue\n");
its->flags |= ITS_FLAGS_CMDQ_NEEDS_FLUSHING;
}
+ writeq_relaxed(0, its->base + GITS_CWRITER);
+ writel_relaxed(GITS_CTLR_ENABLE, its->base + GITS_CTLR);
+
if (of_property_read_bool(its->msi_chip.of_node, "msi-controller")) {
its->domain = irq_domain_add_tree(NULL, &its_domain_ops, its);
if (!its->domain) {
int its_cpu_init(void)
{
- if (!gic_rdists_supports_plpis()) {
- pr_info("CPU%d: LPIs not supported\n", smp_processor_id());
- return -ENXIO;
- }
-
if (!list_empty(&its_nodes)) {
+ if (!gic_rdists_supports_plpis()) {
+ pr_info("CPU%d: LPIs not supported\n", smp_processor_id());
+ return -ENXIO;
+ }
its_cpu_init_lpis();
its_cpu_init_collection();
}
tlist |= 1 << (mpidr & 0xf);
cpu = cpumask_next(cpu, mask);
- if (cpu == nr_cpu_ids)
+ if (cpu >= nr_cpu_ids)
goto out;
mpidr = cpu_logical_map(cpu);
static void gic_mask_irq(struct irq_data *d)
{
u32 mask = 1 << (gic_irq(d) % 32);
+ unsigned long flags;
- raw_spin_lock(&irq_controller_lock);
+ raw_spin_lock_irqsave(&irq_controller_lock, flags);
writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_CLEAR + (gic_irq(d) / 32) * 4);
if (gic_arch_extn.irq_mask)
gic_arch_extn.irq_mask(d);
- raw_spin_unlock(&irq_controller_lock);
+ raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
}
static void gic_unmask_irq(struct irq_data *d)
{
u32 mask = 1 << (gic_irq(d) % 32);
+ unsigned long flags;
- raw_spin_lock(&irq_controller_lock);
+ raw_spin_lock_irqsave(&irq_controller_lock, flags);
if (gic_arch_extn.irq_unmask)
gic_arch_extn.irq_unmask(d);
writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_SET + (gic_irq(d) / 32) * 4);
- raw_spin_unlock(&irq_controller_lock);
+ raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
}
static void gic_eoi_irq(struct irq_data *d)
{
void __iomem *base = gic_dist_base(d);
unsigned int gicirq = gic_irq(d);
+ unsigned long flags;
int ret;
/* Interrupt configuration for SGIs can't be changed */
type != IRQ_TYPE_EDGE_RISING)
return -EINVAL;
- raw_spin_lock(&irq_controller_lock);
+ raw_spin_lock_irqsave(&irq_controller_lock, flags);
if (gic_arch_extn.irq_set_type)
gic_arch_extn.irq_set_type(d, type);
ret = gic_configure_irq(gicirq, type, base, NULL);
- raw_spin_unlock(&irq_controller_lock);
+ raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
return ret;
}
void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3);
unsigned int cpu, shift = (gic_irq(d) % 4) * 8;
u32 val, mask, bit;
+ unsigned long flags;
if (!force)
cpu = cpumask_any_and(mask_val, cpu_online_mask);
if (cpu >= NR_GIC_CPU_IF || cpu >= nr_cpu_ids)
return -EINVAL;
- raw_spin_lock(&irq_controller_lock);
+ raw_spin_lock_irqsave(&irq_controller_lock, flags);
mask = 0xff << shift;
bit = gic_cpu_map[cpu] << shift;
val = readl_relaxed(reg) & ~mask;
writel_relaxed(val | bit, reg);
- raw_spin_unlock(&irq_controller_lock);
+ raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
return IRQ_SET_MASK_OK;
}
enable_hwirq(hc);
spin_unlock_irqrestore(&hc->lock, flags);
/* Timeout 80ms */
- current->state = TASK_UNINTERRUPTIBLE;
+ set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout((80 * HZ) / 1000);
printk(KERN_INFO "HFC PCI: IRQ %d count %d\n",
hc->irq, hc->irqcnt);
if (ints[0] > 1)
membase = (unsigned long)ints[2];
if (str && *str) {
- strcpy(sid, str);
+ strlcpy(sid, str, sizeof(sid));
icn_id = sid;
if ((p = strchr(sid, ','))) {
*p++ = 0;
config LGUEST
tristate "Linux hypervisor example code"
- depends on X86_32 && EVENTFD && TTY
+ depends on X86_32 && EVENTFD && TTY && PCI_DIRECT
select HVC_DRIVER
---help---
This is a very simple module which allows you to run
struct request_queue *q = bdev_get_queue(where->bdev);
unsigned short logical_block_size = queue_logical_block_size(q);
sector_t num_sectors;
+ unsigned int uninitialized_var(special_cmd_max_sectors);
- /* Reject unsupported discard requests */
- if ((rw & REQ_DISCARD) && !blk_queue_discard(q)) {
+ /*
+ * Reject unsupported discard and write same requests.
+ */
+ if (rw & REQ_DISCARD)
+ special_cmd_max_sectors = q->limits.max_discard_sectors;
+ else if (rw & REQ_WRITE_SAME)
+ special_cmd_max_sectors = q->limits.max_write_same_sectors;
+ if ((rw & (REQ_DISCARD | REQ_WRITE_SAME)) && special_cmd_max_sectors == 0) {
dec_count(io, region, -EOPNOTSUPP);
return;
}
store_io_and_region_in_bio(bio, io, region);
if (rw & REQ_DISCARD) {
- num_sectors = min_t(sector_t, q->limits.max_discard_sectors, remaining);
+ num_sectors = min_t(sector_t, special_cmd_max_sectors, remaining);
bio->bi_iter.bi_size = num_sectors << SECTOR_SHIFT;
remaining -= num_sectors;
} else if (rw & REQ_WRITE_SAME) {
*/
dp->get_page(dp, &page, &len, &offset);
bio_add_page(bio, page, logical_block_size, offset);
- num_sectors = min_t(sector_t, q->limits.max_write_same_sectors, remaining);
+ num_sectors = min_t(sector_t, special_cmd_max_sectors, remaining);
bio->bi_iter.bi_size = num_sectors << SECTOR_SHIFT;
offset = 0;
#include <linux/log2.h>
#include <linux/dm-kcopyd.h>
+#include "dm.h"
+
#include "dm-exception-store.h"
#define DM_MSG_PREFIX "snapshots"
struct list_head snapshots;
};
+/*
+ * This structure is allocated for each origin target
+ */
+struct dm_origin {
+ struct dm_dev *dev;
+ struct dm_target *ti;
+ unsigned split_boundary;
+ struct list_head hash_list;
+};
+
/*
* Size of the hash table for origin volumes. If we make this
* the size of the minors list then it should be nearly perfect
#define ORIGIN_HASH_SIZE 256
#define ORIGIN_MASK 0xFF
static struct list_head *_origins;
+static struct list_head *_dm_origins;
static struct rw_semaphore _origins_lock;
static DECLARE_WAIT_QUEUE_HEAD(_pending_exceptions_done);
_origins = kmalloc(ORIGIN_HASH_SIZE * sizeof(struct list_head),
GFP_KERNEL);
if (!_origins) {
- DMERR("unable to allocate memory");
+ DMERR("unable to allocate memory for _origins");
return -ENOMEM;
}
-
for (i = 0; i < ORIGIN_HASH_SIZE; i++)
INIT_LIST_HEAD(_origins + i);
+
+ _dm_origins = kmalloc(ORIGIN_HASH_SIZE * sizeof(struct list_head),
+ GFP_KERNEL);
+ if (!_dm_origins) {
+ DMERR("unable to allocate memory for _dm_origins");
+ kfree(_origins);
+ return -ENOMEM;
+ }
+ for (i = 0; i < ORIGIN_HASH_SIZE; i++)
+ INIT_LIST_HEAD(_dm_origins + i);
+
init_rwsem(&_origins_lock);
return 0;
static void exit_origin_hash(void)
{
kfree(_origins);
+ kfree(_dm_origins);
}
static unsigned origin_hash(struct block_device *bdev)
list_add_tail(&o->hash_list, sl);
}
+static struct dm_origin *__lookup_dm_origin(struct block_device *origin)
+{
+ struct list_head *ol;
+ struct dm_origin *o;
+
+ ol = &_dm_origins[origin_hash(origin)];
+ list_for_each_entry (o, ol, hash_list)
+ if (bdev_equal(o->dev->bdev, origin))
+ return o;
+
+ return NULL;
+}
+
+static void __insert_dm_origin(struct dm_origin *o)
+{
+ struct list_head *sl = &_dm_origins[origin_hash(o->dev->bdev)];
+ list_add_tail(&o->hash_list, sl);
+}
+
+static void __remove_dm_origin(struct dm_origin *o)
+{
+ list_del(&o->hash_list);
+}
+
/*
* _origins_lock must be held when calling this function.
* Returns number of snapshots registered using the supplied cow device, plus:
static void snapshot_resume(struct dm_target *ti)
{
struct dm_snapshot *s = ti->private;
- struct dm_snapshot *snap_src = NULL, *snap_dest = NULL;
+ struct dm_snapshot *snap_src = NULL, *snap_dest = NULL, *snap_merging = NULL;
+ struct dm_origin *o;
+ struct mapped_device *origin_md = NULL;
+ bool must_restart_merging = false;
down_read(&_origins_lock);
+
+ o = __lookup_dm_origin(s->origin->bdev);
+ if (o)
+ origin_md = dm_table_get_md(o->ti->table);
+ if (!origin_md) {
+ (void) __find_snapshots_sharing_cow(s, NULL, NULL, &snap_merging);
+ if (snap_merging)
+ origin_md = dm_table_get_md(snap_merging->ti->table);
+ }
+ if (origin_md == dm_table_get_md(ti->table))
+ origin_md = NULL;
+ if (origin_md) {
+ if (dm_hold(origin_md))
+ origin_md = NULL;
+ }
+
+ up_read(&_origins_lock);
+
+ if (origin_md) {
+ dm_internal_suspend_fast(origin_md);
+ if (snap_merging && test_bit(RUNNING_MERGE, &snap_merging->state_bits)) {
+ must_restart_merging = true;
+ stop_merge(snap_merging);
+ }
+ }
+
+ down_read(&_origins_lock);
+
(void) __find_snapshots_sharing_cow(s, &snap_src, &snap_dest, NULL);
if (snap_src && snap_dest) {
down_write(&snap_src->lock);
up_write(&snap_dest->lock);
up_write(&snap_src->lock);
}
+
up_read(&_origins_lock);
+ if (origin_md) {
+ if (must_restart_merging)
+ start_merge(snap_merging);
+ dm_internal_resume_fast(origin_md);
+ dm_put(origin_md);
+ }
+
/* Now we have correct chunk size, reregister */
reregister_snapshot(s);
* Origin: maps a linear range of a device, with hooks for snapshotting.
*/
-struct dm_origin {
- struct dm_dev *dev;
- unsigned split_boundary;
-};
-
/*
* Construct an origin mapping: <dev_path>
* The context for an origin is merely a 'struct dm_dev *'
goto bad_open;
}
+ o->ti = ti;
ti->private = o;
ti->num_flush_bios = 1;
static void origin_dtr(struct dm_target *ti)
{
struct dm_origin *o = ti->private;
+
dm_put_device(ti, o->dev);
kfree(o);
}
struct dm_origin *o = ti->private;
o->split_boundary = get_origin_minimum_chunksize(o->dev->bdev);
+
+ down_write(&_origins_lock);
+ __insert_dm_origin(o);
+ up_write(&_origins_lock);
+}
+
+static void origin_postsuspend(struct dm_target *ti)
+{
+ struct dm_origin *o = ti->private;
+
+ down_write(&_origins_lock);
+ __remove_dm_origin(o);
+ up_write(&_origins_lock);
}
static void origin_status(struct dm_target *ti, status_type_t type,
static struct target_type origin_target = {
.name = "snapshot-origin",
- .version = {1, 8, 1},
+ .version = {1, 9, 0},
.module = THIS_MODULE,
.ctr = origin_ctr,
.dtr = origin_dtr,
.map = origin_map,
.resume = origin_resume,
+ .postsuspend = origin_postsuspend,
.status = origin_status,
.merge = origin_merge,
.iterate_devices = origin_iterate_devices,
static struct target_type snapshot_target = {
.name = "snapshot",
- .version = {1, 12, 0},
+ .version = {1, 13, 0},
.module = THIS_MODULE,
.ctr = snapshot_ctr,
.dtr = snapshot_dtr,
static struct target_type merge_target = {
.name = dm_snapshot_merge_target_name,
- .version = {1, 2, 0},
+ .version = {1, 3, 0},
.module = THIS_MODULE,
.ctr = snapshot_ctr,
.dtr = snapshot_dtr,
return DM_MAPIO_REMAPPED;
case -ENODATA:
- if (get_pool_mode(tc->pool) == PM_READ_ONLY) {
- /*
- * This block isn't provisioned, and we have no way
- * of doing so.
- */
- handle_unserviceable_bio(tc->pool, bio);
- cell_defer_no_holder(tc, virt_cell);
- return DM_MAPIO_SUBMITTED;
- }
- /* fall through */
-
case -EWOULDBLOCK:
thin_defer_cell(tc, virt_cell);
return DM_MAPIO_SUBMITTED;
dm_get(md);
atomic_inc(&md->open_count);
-
out:
spin_unlock(&_minor_lock);
static void dm_blk_close(struct gendisk *disk, fmode_t mode)
{
- struct mapped_device *md = disk->private_data;
+ struct mapped_device *md;
spin_lock(&_minor_lock);
+ md = disk->private_data;
+ if (WARN_ON(!md))
+ goto out;
+
if (atomic_dec_and_test(&md->open_count) &&
(test_bit(DMF_DEFERRED_REMOVE, &md->flags)))
queue_work(deferred_remove_workqueue, &deferred_remove_work);
dm_put(md);
-
+out:
spin_unlock(&_minor_lock);
}
int minor = MINOR(disk_devt(md->disk));
unlock_fs(md);
- bdput(md->bdev);
destroy_workqueue(md->wq);
if (md->kworker_task)
mempool_destroy(md->rq_pool);
if (md->bs)
bioset_free(md->bs);
- blk_integrity_unregister(md->disk);
- del_gendisk(md->disk);
+
cleanup_srcu_struct(&md->io_barrier);
free_table_devices(&md->table_devices);
- free_minor(minor);
+ dm_stats_cleanup(&md->stats);
spin_lock(&_minor_lock);
md->disk->private_data = NULL;
spin_unlock(&_minor_lock);
-
+ if (blk_get_integrity(md->disk))
+ blk_integrity_unregister(md->disk);
+ del_gendisk(md->disk);
put_disk(md->disk);
blk_cleanup_queue(md->queue);
- dm_stats_cleanup(&md->stats);
+ bdput(md->bdev);
+ free_minor(minor);
+
module_put(THIS_MODULE);
kfree(md);
}
BUG_ON(test_bit(DMF_FREEING, &md->flags));
}
+int dm_hold(struct mapped_device *md)
+{
+ spin_lock(&_minor_lock);
+ if (test_bit(DMF_FREEING, &md->flags)) {
+ spin_unlock(&_minor_lock);
+ return -EBUSY;
+ }
+ dm_get(md);
+ spin_unlock(&_minor_lock);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dm_hold);
+
const char *dm_device_name(struct mapped_device *md)
{
return md->name;
might_sleep();
- spin_lock(&_minor_lock);
map = dm_get_live_table(md, &srcu_idx);
+
+ spin_lock(&_minor_lock);
idr_replace(&_minor_idr, MINOR_ALLOCED, MINOR(disk_devt(dm_disk(md))));
set_bit(DMF_FREEING, &md->flags);
spin_unlock(&_minor_lock);
if (dm_request_based(md))
flush_kthread_worker(&md->kworker);
+ /*
+ * Take suspend_lock so that presuspend and postsuspend methods
+ * do not race with internal suspend.
+ */
+ mutex_lock(&md->suspend_lock);
if (!dm_suspended_md(md)) {
dm_table_presuspend_targets(map);
dm_table_postsuspend_targets(map);
}
+ mutex_unlock(&md->suspend_lock);
/* dm_put_live_table must be before msleep, otherwise deadlock is possible */
dm_put_live_table(md, srcu_idx);
flush_workqueue(md->wq);
dm_wait_for_completion(md, TASK_UNINTERRUPTIBLE);
}
+EXPORT_SYMBOL_GPL(dm_internal_suspend_fast);
void dm_internal_resume_fast(struct mapped_device *md)
{
done:
mutex_unlock(&md->suspend_lock);
}
+EXPORT_SYMBOL_GPL(dm_internal_resume_fast);
/*-----------------------------------------------------------------
* Event notification.
return err ? err : len;
}
static struct rdev_sysfs_entry rdev_state =
-__ATTR(state, S_IRUGO|S_IWUSR, state_show, state_store);
+__ATTR_PREALLOC(state, S_IRUGO|S_IWUSR, state_show, state_store);
static ssize_t
errors_show(struct md_rdev *rdev, char *page)
return err ?: len;
}
static struct md_sysfs_entry md_resync_start =
-__ATTR(resync_start, S_IRUGO|S_IWUSR, resync_start_show, resync_start_store);
+__ATTR_PREALLOC(resync_start, S_IRUGO|S_IWUSR,
+ resync_start_show, resync_start_store);
/*
* The array state can be:
return err ?: len;
}
static struct md_sysfs_entry md_array_state =
-__ATTR(array_state, S_IRUGO|S_IWUSR, array_state_show, array_state_store);
+__ATTR_PREALLOC(array_state, S_IRUGO|S_IWUSR, array_state_show, array_state_store);
static ssize_t
max_corrected_read_errors_show(struct mddev *mddev, char *page) {
}
static struct md_sysfs_entry md_metadata =
-__ATTR(metadata_version, S_IRUGO|S_IWUSR, metadata_show, metadata_store);
+__ATTR_PREALLOC(metadata_version, S_IRUGO|S_IWUSR, metadata_show, metadata_store);
static ssize_t
action_show(struct mddev *mddev, char *page)
}
static struct md_sysfs_entry md_scan_mode =
-__ATTR(sync_action, S_IRUGO|S_IWUSR, action_show, action_store);
+__ATTR_PREALLOC(sync_action, S_IRUGO|S_IWUSR, action_show, action_store);
static ssize_t
last_sync_action_show(struct mddev *mddev, char *page)
return sprintf(page, "%llu / %llu\n", resync, max_sectors);
}
-static struct md_sysfs_entry md_sync_completed = __ATTR_RO(sync_completed);
+static struct md_sysfs_entry md_sync_completed =
+ __ATTR_PREALLOC(sync_completed, S_IRUGO, sync_completed_show, NULL);
static ssize_t
min_sync_show(struct mddev *mddev, char *page)
}
if (err) {
mddev_detach(mddev);
- pers->free(mddev, mddev->private);
+ if (mddev->private)
+ pers->free(mddev, mddev->private);
module_put(pers->owner);
bitmap_destroy(mddev);
return err;
dump_zones(mddev);
ret = md_integrity_register(mddev);
- if (ret)
- raid0_free(mddev, conf);
return ret;
}
if (test_bit(WriteMostly, &rdev->flags)) {
/* Don't balance among write-mostly, just
* use the first as a last resort */
- if (best_disk < 0) {
+ if (best_dist_disk < 0) {
if (is_badblock(rdev, this_sector, sectors,
&first_bad, &bad_sectors)) {
if (first_bad < this_sector)
best_good_sectors = first_bad - this_sector;
} else
best_good_sectors = sectors;
- best_disk = disk;
+ best_dist_disk = disk;
+ best_pending_disk = disk;
}
continue;
}
schedule_timeout_uninterruptible(1);
}
/* Need to check if array will still be degraded after recovery/resync
- * We don't need to check the 'failed' flag as when that gets set,
- * recovery aborts.
+ * Note in case of > 1 drive failures it's possible we're rebuilding
+ * one drive while leaving another faulty drive in array.
*/
- for (i = 0; i < conf->raid_disks; i++)
- if (conf->disks[i].rdev == NULL)
+ rcu_read_lock();
+ for (i = 0; i < conf->raid_disks; i++) {
+ struct md_rdev *rdev = ACCESS_ONCE(conf->disks[i].rdev);
+
+ if (rdev == NULL || test_bit(Faulty, &rdev->flags))
still_degraded = 1;
+ }
+ rcu_read_unlock();
bitmap_start_sync(mddev->bitmap, sector_nr, &sync_blocks, still_degraded);
for (id = kempld_dmi_table;
id->matches[0].slot != DMI_NONE; id++)
if (strstr(id->ident, force_device_id))
- if (id->callback && id->callback(id))
+ if (id->callback && !id->callback(id))
break;
if (id->matches[0].slot == DMI_NONE)
return -ENODEV;
int rtsx_usb_ep0_read_register(struct rtsx_ucr *ucr, u16 addr, u8 *data)
{
u16 value;
+ u8 *buf;
+ int ret;
if (!data)
return -EINVAL;
- *data = 0;
+
+ buf = kzalloc(sizeof(u8), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
addr |= EP0_READ_REG_CMD << EP0_OP_SHIFT;
value = swab16(addr);
- return usb_control_msg(ucr->pusb_dev,
+ ret = usb_control_msg(ucr->pusb_dev,
usb_rcvctrlpipe(ucr->pusb_dev, 0), RTSX_USB_REQ_REG_OP,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- value, 0, data, 1, 100);
+ value, 0, buf, 1, 100);
+ *data = *buf;
+
+ kfree(buf);
+ return ret;
}
EXPORT_SYMBOL_GPL(rtsx_usb_ep0_read_register);
int rtsx_usb_get_card_status(struct rtsx_ucr *ucr, u16 *status)
{
int ret;
+ u16 *buf;
if (!status)
return -EINVAL;
- if (polling_pipe == 0)
+ if (polling_pipe == 0) {
+ buf = kzalloc(sizeof(u16), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
ret = usb_control_msg(ucr->pusb_dev,
usb_rcvctrlpipe(ucr->pusb_dev, 0),
RTSX_USB_REQ_POLL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, 0, status, 2, 100);
- else
+ 0, 0, buf, 2, 100);
+ *status = *buf;
+
+ kfree(buf);
+ } else {
ret = rtsx_usb_get_status_with_bulk(ucr, status);
+ }
/* usb_control_msg may return positive when success */
if (ret < 0)
dev->dev_state = MEI_DEV_POWER_DOWN;
mei_reset(dev);
+ /* move device to disabled state unconditionally */
+ dev->dev_state = MEI_DEV_DISABLED;
mutex_unlock(&dev->device_lock);
PTR_ERR(pwrseq->reset_gpios[i]) != -ENOSYS) {
ret = PTR_ERR(pwrseq->reset_gpios[i]);
- while (--i)
+ while (i--)
gpiod_put(pwrseq->reset_gpios[i]);
goto clk_put;
config MTD_NAND_HISI504
tristate "Support for NAND controller on Hisilicon SoC Hip04"
+ depends on HAS_DMA
help
Enables support for NAND controller on Hisilicon SoC Hip04.
nand_writel(info, NDCR, ndcr | int_mask);
}
+static void drain_fifo(struct pxa3xx_nand_info *info, void *data, int len)
+{
+ if (info->ecc_bch) {
+ int timeout;
+
+ /*
+ * According to the datasheet, when reading from NDDB
+ * with BCH enabled, after each 32 bytes reads, we
+ * have to make sure that the NDSR.RDDREQ bit is set.
+ *
+ * Drain the FIFO 8 32 bits reads at a time, and skip
+ * the polling on the last read.
+ */
+ while (len > 8) {
+ __raw_readsl(info->mmio_base + NDDB, data, 8);
+
+ for (timeout = 0;
+ !(nand_readl(info, NDSR) & NDSR_RDDREQ);
+ timeout++) {
+ if (timeout >= 5) {
+ dev_err(&info->pdev->dev,
+ "Timeout on RDDREQ while draining the FIFO\n");
+ return;
+ }
+
+ mdelay(1);
+ }
+
+ data += 32;
+ len -= 8;
+ }
+ }
+
+ __raw_readsl(info->mmio_base + NDDB, data, len);
+}
+
static void handle_data_pio(struct pxa3xx_nand_info *info)
{
unsigned int do_bytes = min(info->data_size, info->chunk_size);
DIV_ROUND_UP(info->oob_size, 4));
break;
case STATE_PIO_READING:
- __raw_readsl(info->mmio_base + NDDB,
- info->data_buff + info->data_buff_pos,
- DIV_ROUND_UP(do_bytes, 4));
+ drain_fifo(info,
+ info->data_buff + info->data_buff_pos,
+ DIV_ROUND_UP(do_bytes, 4));
if (info->oob_size > 0)
- __raw_readsl(info->mmio_base + NDDB,
- info->oob_buff + info->oob_buff_pos,
- DIV_ROUND_UP(info->oob_size, 4));
+ drain_fifo(info,
+ info->oob_buff + info->oob_buff_pos,
+ DIV_ROUND_UP(info->oob_size, 4));
break;
default:
dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
int ret, irq, cs;
pdata = dev_get_platdata(&pdev->dev);
+ if (pdata->num_cs <= 0)
+ return -ENODEV;
info = devm_kzalloc(&pdev->dev, sizeof(*info) + (sizeof(*mtd) +
sizeof(*host)) * pdata->num_cs, GFP_KERNEL);
if (!info)
ubi_warn(ubi, "corrupted VID header at PEB %d, LEB %d:%d",
pnum, vol_id, lnum);
err = -EBADMSG;
- } else
+ } else {
err = -EINVAL;
ubi_ro_mode(ubi);
+ }
}
goto out_free;
} else if (err == UBI_IO_BITFLIPS)
making it transparent to the connected L2 switch.
Ipvlan devices can be added using the "ip" command from the
- iproute2 package starting with the iproute2-X.Y.ZZ release:
+ iproute2 package starting with the iproute2-3.19 release:
"ip link add link <main-dev> [ NAME ] type ipvlan"
config LTPC
tristate "Apple/Farallon LocalTalk PC support"
- depends on DEV_APPLETALK && (ISA || EISA) && ISA_DMA_API
+ depends on DEV_APPLETALK && (ISA || EISA) && ISA_DMA_API && VIRT_TO_BUS
help
This allows you to use the AppleTalk PC card to connect to LocalTalk
networks. The card is also known as the Farallon PhoneNet PC card.
/* Find out if any slaves have the same mapping as this skb. */
bond_for_each_slave_rcu(bond, slave, iter) {
if (slave->queue_id == skb->queue_mapping) {
- if (bond_slave_can_tx(slave)) {
+ if (bond_slave_is_up(slave) &&
+ slave->link == BOND_LINK_UP) {
bond_dev_queue_xmit(bond, skb, slave->dev);
return 0;
}
config CAN_XILINXCAN
tristate "Xilinx CAN"
- depends on ARCH_ZYNQ || MICROBLAZE || COMPILE_TEST
+ depends on ARCH_ZYNQ || ARM64 || MICROBLAZE || COMPILE_TEST
depends on COMMON_CLK && HAS_IOMEM
---help---
Xilinx CAN driver. This driver supports both soft AXI CAN IP and
skb->pkt_type = PACKET_BROADCAST;
skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb_reset_mac_header(skb);
+ skb_reset_network_header(skb);
+ skb_reset_transport_header(skb);
+
can_skb_reserve(skb);
can_skb_prv(skb)->ifindex = dev->ifindex;
skb->pkt_type = PACKET_BROADCAST;
skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb_reset_mac_header(skb);
+ skb_reset_network_header(skb);
+ skb_reset_transport_header(skb);
+
can_skb_reserve(skb);
can_skb_prv(skb)->ifindex = dev->ifindex;
rx_state = unlikely(reg_esr & FLEXCAN_ESR_RX_WRN) ?
CAN_STATE_ERROR_WARNING : CAN_STATE_ERROR_ACTIVE;
new_state = max(tx_state, rx_state);
- } else if (unlikely(flt == FLEXCAN_ESR_FLT_CONF_PASSIVE)) {
+ } else {
__flexcan_get_berr_counter(dev, &bec);
- new_state = CAN_STATE_ERROR_PASSIVE;
+ new_state = flt == FLEXCAN_ESR_FLT_CONF_PASSIVE ?
+ CAN_STATE_ERROR_PASSIVE : CAN_STATE_BUS_OFF;
rx_state = bec.rxerr >= bec.txerr ? new_state : 0;
tx_state = bec.rxerr <= bec.txerr ? new_state : 0;
- } else {
- new_state = CAN_STATE_BUS_OFF;
}
/* state hasn't changed */
const struct flexcan_devtype_data *devtype_data;
struct net_device *dev;
struct flexcan_priv *priv;
+ struct regulator *reg_xceiver;
struct resource *mem;
struct clk *clk_ipg = NULL, *clk_per = NULL;
void __iomem *base;
int err, irq;
u32 clock_freq = 0;
+ reg_xceiver = devm_regulator_get(&pdev->dev, "xceiver");
+ if (PTR_ERR(reg_xceiver) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ else if (IS_ERR(reg_xceiver))
+ reg_xceiver = NULL;
+
if (pdev->dev.of_node)
of_property_read_u32(pdev->dev.of_node,
"clock-frequency", &clock_freq);
priv->pdata = dev_get_platdata(&pdev->dev);
priv->devtype_data = devtype_data;
- priv->reg_xceiver = devm_regulator_get(&pdev->dev, "xceiver");
- if (IS_ERR(priv->reg_xceiver))
- priv->reg_xceiver = NULL;
+ priv->reg_xceiver = reg_xceiver;
netif_napi_add(dev, &priv->napi, flexcan_poll, FLEXCAN_NAPI_WEIGHT);
}
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
init_usb_anchor(&dev->rx_submitted);
atomic_set(&dev->active_channels, 0);
* Copyright (C) 2015 Valeo S.A.
*/
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
#include <linux/completion.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/can/dev.h>
#include <linux/can/error.h>
-#define MAX_TX_URBS 16
#define MAX_RX_URBS 4
#define START_TIMEOUT 1000 /* msecs */
#define STOP_TIMEOUT 1000 /* msecs */
};
};
+/* Context for an outstanding, not yet ACKed, transmission */
struct kvaser_usb_tx_urb_context {
struct kvaser_usb_net_priv *priv;
u32 echo_index;
struct usb_endpoint_descriptor *bulk_in, *bulk_out;
struct usb_anchor rx_submitted;
+ /* @max_tx_urbs: Firmware-reported maximum number of oustanding,
+ * not yet ACKed, transmissions on this device. This value is
+ * also used as a sentinel for marking free tx contexts.
+ */
u32 fw_version;
unsigned int nchannels;
+ unsigned int max_tx_urbs;
enum kvaser_usb_family family;
bool rxinitdone;
struct kvaser_usb_net_priv {
struct can_priv can;
-
- atomic_t active_tx_urbs;
- struct usb_anchor tx_submitted;
- struct kvaser_usb_tx_urb_context tx_contexts[MAX_TX_URBS];
-
- struct completion start_comp, stop_comp;
+ struct can_berr_counter bec;
struct kvaser_usb *dev;
struct net_device *netdev;
int channel;
- struct can_berr_counter bec;
+ struct completion start_comp, stop_comp;
+ struct usb_anchor tx_submitted;
+
+ spinlock_t tx_contexts_lock;
+ int active_tx_contexts;
+ struct kvaser_usb_tx_urb_context tx_contexts[];
};
static const struct usb_device_id kvaser_usb_table[] = {
while (pos <= actual_len - MSG_HEADER_LEN) {
tmp = buf + pos;
- if (!tmp->len)
- break;
+ /* Handle messages crossing the USB endpoint max packet
+ * size boundary. Check kvaser_usb_read_bulk_callback()
+ * for further details.
+ */
+ if (tmp->len == 0) {
+ pos = round_up(pos, le16_to_cpu(dev->bulk_in->
+ wMaxPacketSize));
+ continue;
+ }
if (pos + tmp->len > actual_len) {
dev_err(dev->udev->dev.parent,
switch (dev->family) {
case KVASER_LEAF:
dev->fw_version = le32_to_cpu(msg.u.leaf.softinfo.fw_version);
+ dev->max_tx_urbs =
+ le16_to_cpu(msg.u.leaf.softinfo.max_outstanding_tx);
break;
case KVASER_USBCAN:
dev->fw_version = le32_to_cpu(msg.u.usbcan.softinfo.fw_version);
+ dev->max_tx_urbs =
+ le16_to_cpu(msg.u.usbcan.softinfo.max_outstanding_tx);
break;
}
struct kvaser_usb_net_priv *priv;
struct sk_buff *skb;
struct can_frame *cf;
+ unsigned long flags;
u8 channel, tid;
channel = msg->u.tx_acknowledge_header.channel;
stats = &priv->netdev->stats;
- context = &priv->tx_contexts[tid % MAX_TX_URBS];
+ context = &priv->tx_contexts[tid % dev->max_tx_urbs];
/* Sometimes the state change doesn't come after a bus-off event */
if (priv->can.restart_ms &&
stats->tx_packets++;
stats->tx_bytes += context->dlc;
- can_get_echo_skb(priv->netdev, context->echo_index);
- context->echo_index = MAX_TX_URBS;
- atomic_dec(&priv->active_tx_urbs);
+ spin_lock_irqsave(&priv->tx_contexts_lock, flags);
+ can_get_echo_skb(priv->netdev, context->echo_index);
+ context->echo_index = dev->max_tx_urbs;
+ --priv->active_tx_contexts;
netif_wake_queue(priv->netdev);
+
+ spin_unlock_irqrestore(&priv->tx_contexts_lock, flags);
}
static void kvaser_usb_simple_msg_callback(struct urb *urb)
netdev_err(netdev, "Error transmitting URB\n");
usb_unanchor_urb(urb);
usb_free_urb(urb);
- kfree(buf);
return err;
}
return 0;
}
-static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv)
-{
- int i;
-
- usb_kill_anchored_urbs(&priv->tx_submitted);
- atomic_set(&priv->active_tx_urbs, 0);
-
- for (i = 0; i < MAX_TX_URBS; i++)
- priv->tx_contexts[i].echo_index = MAX_TX_URBS;
-}
-
static void kvaser_usb_rx_error_update_can_state(struct kvaser_usb_net_priv *priv,
const struct kvaser_usb_error_summary *es,
struct can_frame *cf)
while (pos <= urb->actual_length - MSG_HEADER_LEN) {
msg = urb->transfer_buffer + pos;
- if (!msg->len)
- break;
+ /* The Kvaser firmware can only read and write messages that
+ * does not cross the USB's endpoint wMaxPacketSize boundary.
+ * If a follow-up command crosses such boundary, firmware puts
+ * a placeholder zero-length command in its place then aligns
+ * the real command to the next max packet size.
+ *
+ * Handle such cases or we're going to miss a significant
+ * number of events in case of a heavy rx load on the bus.
+ */
+ if (msg->len == 0) {
+ pos = round_up(pos, le16_to_cpu(dev->bulk_in->
+ wMaxPacketSize));
+ continue;
+ }
if (pos + msg->len > urb->actual_length) {
dev_err(dev->udev->dev.parent, "Format error\n");
}
kvaser_usb_handle_message(dev, msg);
-
pos += msg->len;
}
return err;
}
+static void kvaser_usb_reset_tx_urb_contexts(struct kvaser_usb_net_priv *priv)
+{
+ int i, max_tx_urbs;
+
+ max_tx_urbs = priv->dev->max_tx_urbs;
+
+ priv->active_tx_contexts = 0;
+ for (i = 0; i < max_tx_urbs; i++)
+ priv->tx_contexts[i].echo_index = max_tx_urbs;
+}
+
+/* This method might sleep. Do not call it in the atomic context
+ * of URB completions.
+ */
+static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv)
+{
+ usb_kill_anchored_urbs(&priv->tx_submitted);
+ kvaser_usb_reset_tx_urb_contexts(priv);
+}
+
static void kvaser_usb_unlink_all_urbs(struct kvaser_usb *dev)
{
int i;
struct urb *urb;
void *buf;
struct kvaser_msg *msg;
- int i, err;
- int ret = NETDEV_TX_OK;
+ int i, err, ret = NETDEV_TX_OK;
u8 *msg_tx_can_flags = NULL; /* GCC */
+ unsigned long flags;
if (can_dropped_invalid_skb(netdev, skb))
return NETDEV_TX_OK;
if (!buf) {
stats->tx_dropped++;
dev_kfree_skb(skb);
- goto nobufmem;
+ goto freeurb;
}
msg = buf;
if (cf->can_id & CAN_RTR_FLAG)
*msg_tx_can_flags |= MSG_FLAG_REMOTE_FRAME;
- for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) {
- if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
+ spin_lock_irqsave(&priv->tx_contexts_lock, flags);
+ for (i = 0; i < dev->max_tx_urbs; i++) {
+ if (priv->tx_contexts[i].echo_index == dev->max_tx_urbs) {
context = &priv->tx_contexts[i];
+
+ context->echo_index = i;
+ can_put_echo_skb(skb, netdev, context->echo_index);
+ ++priv->active_tx_contexts;
+ if (priv->active_tx_contexts >= dev->max_tx_urbs)
+ netif_stop_queue(netdev);
+
break;
}
}
+ spin_unlock_irqrestore(&priv->tx_contexts_lock, flags);
/* This should never happen; it implies a flow control bug */
if (!context) {
netdev_warn(netdev, "cannot find free context\n");
+
+ kfree(buf);
ret = NETDEV_TX_BUSY;
- goto releasebuf;
+ goto freeurb;
}
context->priv = priv;
- context->echo_index = i;
context->dlc = cf->can_dlc;
msg->u.tx_can.tid = context->echo_index;
kvaser_usb_write_bulk_callback, context);
usb_anchor_urb(urb, &priv->tx_submitted);
- can_put_echo_skb(skb, netdev, context->echo_index);
-
- atomic_inc(&priv->active_tx_urbs);
-
- if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS)
- netif_stop_queue(netdev);
-
err = usb_submit_urb(urb, GFP_ATOMIC);
if (unlikely(err)) {
+ spin_lock_irqsave(&priv->tx_contexts_lock, flags);
+
can_free_echo_skb(netdev, context->echo_index);
+ context->echo_index = dev->max_tx_urbs;
+ --priv->active_tx_contexts;
+ netif_wake_queue(netdev);
+
+ spin_unlock_irqrestore(&priv->tx_contexts_lock, flags);
- atomic_dec(&priv->active_tx_urbs);
usb_unanchor_urb(urb);
stats->tx_dropped++;
else
netdev_warn(netdev, "Failed tx_urb %d\n", err);
- goto releasebuf;
+ goto freeurb;
}
- usb_free_urb(urb);
-
- return NETDEV_TX_OK;
+ ret = NETDEV_TX_OK;
-releasebuf:
- kfree(buf);
-nobufmem:
+freeurb:
usb_free_urb(urb);
return ret;
}
struct kvaser_usb *dev = usb_get_intfdata(intf);
struct net_device *netdev;
struct kvaser_usb_net_priv *priv;
- int i, err;
+ int err;
err = kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, channel);
if (err)
return err;
- netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS);
+ netdev = alloc_candev(sizeof(*priv) +
+ dev->max_tx_urbs * sizeof(*priv->tx_contexts),
+ dev->max_tx_urbs);
if (!netdev) {
dev_err(&intf->dev, "Cannot alloc candev\n");
return -ENOMEM;
priv = netdev_priv(netdev);
+ init_usb_anchor(&priv->tx_submitted);
init_completion(&priv->start_comp);
init_completion(&priv->stop_comp);
- init_usb_anchor(&priv->tx_submitted);
- atomic_set(&priv->active_tx_urbs, 0);
-
- for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++)
- priv->tx_contexts[i].echo_index = MAX_TX_URBS;
-
priv->dev = dev;
priv->netdev = netdev;
priv->channel = channel;
+ spin_lock_init(&priv->tx_contexts_lock);
+ kvaser_usb_reset_tx_urb_contexts(priv);
+
priv->can.state = CAN_STATE_STOPPED;
priv->can.clock.freq = CAN_USB_CLOCK;
priv->can.bittiming_const = &kvaser_usb_bittiming_const;
return err;
}
+ dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n",
+ ((dev->fw_version >> 24) & 0xff),
+ ((dev->fw_version >> 16) & 0xff),
+ (dev->fw_version & 0xffff));
+
+ dev_dbg(&intf->dev, "Max oustanding tx = %d URBs\n", dev->max_tx_urbs);
+
err = kvaser_usb_get_card_info(dev);
if (err) {
dev_err(&intf->dev,
return err;
}
- dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n",
- ((dev->fw_version >> 24) & 0xff),
- ((dev->fw_version >> 16) & 0xff),
- (dev->fw_version & 0xffff));
-
for (i = 0; i < dev->nchannels; i++) {
err = kvaser_usb_init_one(intf, id, i);
if (err) {
#define PUCAN_CMD_FILTER_STD 0x008
#define PUCAN_CMD_TX_ABORT 0x009
#define PUCAN_CMD_WR_ERR_CNT 0x00a
-#define PUCAN_CMD_RX_FRAME_ENABLE 0x00b
-#define PUCAN_CMD_RX_FRAME_DISABLE 0x00c
+#define PUCAN_CMD_SET_EN_OPTION 0x00b
+#define PUCAN_CMD_CLR_DIS_OPTION 0x00c
#define PUCAN_CMD_END_OF_COLLECTION 0x3ff
/* uCAN received messages list */
u16 unused;
};
-/* uCAN RX_FRAME_ENABLE command fields */
-#define PUCAN_FLTEXT_ERROR 0x0001
-#define PUCAN_FLTEXT_BUSLOAD 0x0002
+/* uCAN SET_EN/CLR_DIS _OPTION command fields */
+#define PUCAN_OPTION_ERROR 0x0001
+#define PUCAN_OPTION_BUSLOAD 0x0002
+#define PUCAN_OPTION_CANDFDISO 0x0004
-struct __packed pucan_filter_ext {
+struct __packed pucan_options {
__le16 opcode_channel;
- __le16 ext_mask;
+ __le16 options;
u32 unused;
};
u8 unused[5];
};
-/* Extended usage of uCAN commands CMD_RX_FRAME_xxxABLE for PCAN-USB Pro FD */
+/* Extended usage of uCAN commands CMD_xxx_xx_OPTION for PCAN-USB Pro FD */
#define PCAN_UFD_FLTEXT_CALIBRATION 0x8000
-struct __packed pcan_ufd_filter_ext {
+struct __packed pcan_ufd_options {
__le16 opcode_channel;
- __le16 ext_mask;
+ __le16 ucan_mask;
u16 unused;
__le16 usb_mask;
};
/* moves the pointer forward */
pc += sizeof(struct pucan_wr_err_cnt);
+ /* add command to switch from ISO to non-ISO mode, if fw allows it */
+ if (dev->can.ctrlmode_supported & CAN_CTRLMODE_FD_NON_ISO) {
+ struct pucan_options *puo = (struct pucan_options *)pc;
+
+ puo->opcode_channel =
+ (dev->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) ?
+ pucan_cmd_opcode_channel(dev,
+ PUCAN_CMD_CLR_DIS_OPTION) :
+ pucan_cmd_opcode_channel(dev, PUCAN_CMD_SET_EN_OPTION);
+
+ puo->options = cpu_to_le16(PUCAN_OPTION_CANDFDISO);
+
+ /* to be sure that no other extended bits will be taken into
+ * account
+ */
+ puo->unused = 0;
+
+ /* moves the pointer forward */
+ pc += sizeof(struct pucan_options);
+ }
+
/* next, go back to operational mode */
cmd = (struct pucan_command *)pc;
cmd->opcode_channel = pucan_cmd_opcode_channel(dev,
return pcan_usb_fd_send_cmd(dev, cmd);
}
-/* set/unset notifications filter:
+/* set/unset options
*
- * onoff sets(1)/unset(0) notifications
- * mask each bit defines a kind of notification to set/unset
+ * onoff set(1)/unset(0) options
+ * mask each bit defines a kind of options to set/unset
*/
-static int pcan_usb_fd_set_filter_ext(struct peak_usb_device *dev,
- bool onoff, u16 ext_mask, u16 usb_mask)
+static int pcan_usb_fd_set_options(struct peak_usb_device *dev,
+ bool onoff, u16 ucan_mask, u16 usb_mask)
{
- struct pcan_ufd_filter_ext *cmd = pcan_usb_fd_cmd_buffer(dev);
+ struct pcan_ufd_options *cmd = pcan_usb_fd_cmd_buffer(dev);
cmd->opcode_channel = pucan_cmd_opcode_channel(dev,
- (onoff) ? PUCAN_CMD_RX_FRAME_ENABLE :
- PUCAN_CMD_RX_FRAME_DISABLE);
+ (onoff) ? PUCAN_CMD_SET_EN_OPTION :
+ PUCAN_CMD_CLR_DIS_OPTION);
- cmd->ext_mask = cpu_to_le16(ext_mask);
+ cmd->ucan_mask = cpu_to_le16(ucan_mask);
cmd->usb_mask = cpu_to_le16(usb_mask);
/* send the command */
&pcan_usb_pro_fd);
/* enable USB calibration messages */
- err = pcan_usb_fd_set_filter_ext(dev, 1,
- PUCAN_FLTEXT_ERROR,
- PCAN_UFD_FLTEXT_CALIBRATION);
+ err = pcan_usb_fd_set_options(dev, 1,
+ PUCAN_OPTION_ERROR,
+ PCAN_UFD_FLTEXT_CALIBRATION);
}
pdev->usb_if->dev_opened_count++;
/* turn off special msgs for that interface if no other dev opened */
if (pdev->usb_if->dev_opened_count == 1)
- pcan_usb_fd_set_filter_ext(dev, 0,
- PUCAN_FLTEXT_ERROR,
- PCAN_UFD_FLTEXT_CALIBRATION);
+ pcan_usb_fd_set_options(dev, 0,
+ PUCAN_OPTION_ERROR,
+ PCAN_UFD_FLTEXT_CALIBRATION);
pdev->usb_if->dev_opened_count--;
return 0;
pdev->usb_if->fw_info.fw_version[2],
dev->adapter->ctrl_count);
- /* the currently supported hw is non-ISO */
- dev->can.ctrlmode = CAN_CTRLMODE_FD_NON_ISO;
+ /* check for ability to switch between ISO/non-ISO modes */
+ if (pdev->usb_if->fw_info.fw_version[0] >= 2) {
+ /* firmware >= 2.x supports ISO/non-ISO switching */
+ dev->can.ctrlmode_supported |= CAN_CTRLMODE_FD_NON_ISO;
+ } else {
+ /* firmware < 2.x only supports fixed(!) non-ISO */
+ dev->can.ctrlmode |= CAN_CTRLMODE_FD_NON_ISO;
+ }
/* tell the hardware the can driver is running */
err = pcan_usb_fd_drv_loaded(dev, 1);
pdev->usb_if = ppdev->usb_if;
pdev->cmd_buffer_addr = ppdev->cmd_buffer_addr;
+
+ /* do a copy of the ctrlmode[_supported] too */
+ dev->can.ctrlmode = ppdev->dev.can.ctrlmode;
+ dev->can.ctrlmode_supported = ppdev->dev.can.ctrlmode_supported;
}
pdev->usb_if->dev[dev->ctrl_idx] = dev;
if (dev->ctrl_idx == 0) {
/* turn off calibration message if any device were opened */
if (pdev->usb_if->dev_opened_count > 0)
- pcan_usb_fd_set_filter_ext(dev, 0,
- PUCAN_FLTEXT_ERROR,
- PCAN_UFD_FLTEXT_CALIBRATION);
+ pcan_usb_fd_set_options(dev, 0,
+ PUCAN_OPTION_ERROR,
+ PCAN_UFD_FLTEXT_CALIBRATION);
/* tell USB adapter that the driver is being unloaded */
pcan_usb_fd_drv_loaded(dev, 0);
{ \
u32 indir, dir; \
spin_lock(&priv->indir_lock); \
- indir = reg_readl(priv, REG_DIR_DATA_READ); \
dir = __raw_readl(priv->name + off); \
+ indir = reg_readl(priv, REG_DIR_DATA_READ); \
spin_unlock(&priv->indir_lock); \
return (u64)indir << 32 | dir; \
} \
link->open++;
info->link_status = 0x00;
- init_timer(&info->watchdog);
- info->watchdog.function = ei_watchdog;
- info->watchdog.data = (u_long)dev;
- info->watchdog.expires = jiffies + HZ;
- add_timer(&info->watchdog);
+ setup_timer(&info->watchdog, ei_watchdog, (u_long)dev);
+ mod_timer(&info->watchdog, jiffies + HZ);
return ax_open(dev);
} /* axnet_open */
info->phy_id = info->eth_phy;
info->link_status = 0x00;
- init_timer(&info->watchdog);
- info->watchdog.function = ei_watchdog;
- info->watchdog.data = (u_long)dev;
- info->watchdog.expires = jiffies + HZ;
- add_timer(&info->watchdog);
+ setup_timer(&info->watchdog, ei_watchdog, (u_long)dev);
+ mod_timer(&info->watchdog, jiffies + HZ);
return ei_open(dev);
} /* pcnet_open */
u16 pktlength;
u16 pktstatus;
- while ((rxstatus = priv->dmaops->get_rx_status(priv)) != 0) {
+ while (((rxstatus = priv->dmaops->get_rx_status(priv)) != 0) &&
+ (count < limit)) {
pktstatus = rxstatus >> 16;
pktlength = rxstatus & 0xffff;
struct altera_tse_private *priv =
container_of(napi, struct altera_tse_private, napi);
int rxcomplete = 0;
- int txcomplete = 0;
unsigned long int flags;
- txcomplete = tse_tx_complete(priv);
+ tse_tx_complete(priv);
rxcomplete = tse_rx(priv, budget);
- if (rxcomplete >= budget || txcomplete > 0)
- return rxcomplete;
+ if (rxcomplete < budget) {
- napi_gro_flush(napi, false);
- __napi_complete(napi);
+ napi_gro_flush(napi, false);
+ __napi_complete(napi);
- netdev_dbg(priv->dev,
- "NAPI Complete, did %d packets with budget %d\n",
- txcomplete+rxcomplete, budget);
+ netdev_dbg(priv->dev,
+ "NAPI Complete, did %d packets with budget %d\n",
+ rxcomplete, budget);
- spin_lock_irqsave(&priv->rxdma_irq_lock, flags);
- priv->dmaops->enable_rxirq(priv);
- priv->dmaops->enable_txirq(priv);
- spin_unlock_irqrestore(&priv->rxdma_irq_lock, flags);
- return rxcomplete + txcomplete;
+ spin_lock_irqsave(&priv->rxdma_irq_lock, flags);
+ priv->dmaops->enable_rxirq(priv);
+ priv->dmaops->enable_txirq(priv);
+ spin_unlock_irqrestore(&priv->rxdma_irq_lock, flags);
+ }
+ return rxcomplete;
}
/* DMA TX & RX FIFO interrupt routing
{
struct net_device *dev = dev_id;
struct altera_tse_private *priv;
- unsigned long int flags;
if (unlikely(!dev)) {
pr_err("%s: invalid dev pointer\n", __func__);
}
priv = netdev_priv(dev);
- /* turn off desc irqs and enable napi rx */
- spin_lock_irqsave(&priv->rxdma_irq_lock, flags);
+ spin_lock(&priv->rxdma_irq_lock);
+ /* reset IRQs */
+ priv->dmaops->clear_rxirq(priv);
+ priv->dmaops->clear_txirq(priv);
+ spin_unlock(&priv->rxdma_irq_lock);
if (likely(napi_schedule_prep(&priv->napi))) {
+ spin_lock(&priv->rxdma_irq_lock);
priv->dmaops->disable_rxirq(priv);
priv->dmaops->disable_txirq(priv);
+ spin_unlock(&priv->rxdma_irq_lock);
__napi_schedule(&priv->napi);
}
- /* reset IRQs */
- priv->dmaops->clear_rxirq(priv);
- priv->dmaops->clear_txirq(priv);
-
- spin_unlock_irqrestore(&priv->rxdma_irq_lock, flags);
return IRQ_HANDLED;
}
}
if (of_property_read_u32(pdev->dev.of_node, "tx-fifo-depth",
- &priv->rx_fifo_depth)) {
+ &priv->tx_fifo_depth)) {
dev_err(&pdev->dev, "cannot obtain tx-fifo-depth\n");
ret = -ENXIO;
goto err_free_netdev;
{
struct pcnet32_private *lp;
int i, media;
- int fdx, mii, fset, dxsuflo;
+ int fdx, mii, fset, dxsuflo, sram;
int chip_version;
char *chipname;
struct net_device *dev;
}
/* initialize variables */
- fdx = mii = fset = dxsuflo = 0;
+ fdx = mii = fset = dxsuflo = sram = 0;
chip_version = (chip_version >> 12) & 0xffff;
switch (chip_version) {
chipname = "PCnet/FAST III 79C973"; /* PCI */
fdx = 1;
mii = 1;
+ sram = 1;
break;
case 0x2626:
chipname = "PCnet/Home 79C978"; /* PCI */
chipname = "PCnet/FAST III 79C975"; /* PCI */
fdx = 1;
mii = 1;
+ sram = 1;
break;
case 0x2628:
chipname = "PCnet/PRO 79C976";
dxsuflo = 1;
}
+ /*
+ * The Am79C973/Am79C975 controllers come with 12K of SRAM
+ * which we can use for the Tx/Rx buffers but most importantly,
+ * the use of SRAM allow us to use the BCR18:NOUFLO bit to avoid
+ * Tx fifo underflows.
+ */
+ if (sram) {
+ /*
+ * The SRAM is being configured in two steps. First we
+ * set the SRAM size in the BCR25:SRAM_SIZE bits. According
+ * to the datasheet, each bit corresponds to a 512-byte
+ * page so we can have at most 24 pages. The SRAM_SIZE
+ * holds the value of the upper 8 bits of the 16-bit SRAM size.
+ * The low 8-bits start at 0x00 and end at 0xff. So the
+ * address range is from 0x0000 up to 0x17ff. Therefore,
+ * the SRAM_SIZE is set to 0x17. The next step is to set
+ * the BCR26:SRAM_BND midway through so the Tx and Rx
+ * buffers can share the SRAM equally.
+ */
+ a->write_bcr(ioaddr, 25, 0x17);
+ a->write_bcr(ioaddr, 26, 0xc);
+ /* And finally enable the NOUFLO bit */
+ a->write_bcr(ioaddr, 18, a->read_bcr(ioaddr, 18) | (1 << 11));
+ }
+
dev = alloc_etherdev(sizeof(*lp));
if (!dev) {
ret = -ENOMEM;
}
}
+static int xgbe_request_irqs(struct xgbe_prv_data *pdata)
+{
+ struct xgbe_channel *channel;
+ struct net_device *netdev = pdata->netdev;
+ unsigned int i;
+ int ret;
+
+ ret = devm_request_irq(pdata->dev, pdata->dev_irq, xgbe_isr, 0,
+ netdev->name, pdata);
+ if (ret) {
+ netdev_alert(netdev, "error requesting irq %d\n",
+ pdata->dev_irq);
+ return ret;
+ }
+
+ if (!pdata->per_channel_irq)
+ return 0;
+
+ channel = pdata->channel;
+ for (i = 0; i < pdata->channel_count; i++, channel++) {
+ snprintf(channel->dma_irq_name,
+ sizeof(channel->dma_irq_name) - 1,
+ "%s-TxRx-%u", netdev_name(netdev),
+ channel->queue_index);
+
+ ret = devm_request_irq(pdata->dev, channel->dma_irq,
+ xgbe_dma_isr, 0,
+ channel->dma_irq_name, channel);
+ if (ret) {
+ netdev_alert(netdev, "error requesting irq %d\n",
+ channel->dma_irq);
+ goto err_irq;
+ }
+ }
+
+ return 0;
+
+err_irq:
+ /* Using an unsigned int, 'i' will go to UINT_MAX and exit */
+ for (i--, channel--; i < pdata->channel_count; i--, channel--)
+ devm_free_irq(pdata->dev, channel->dma_irq, channel);
+
+ devm_free_irq(pdata->dev, pdata->dev_irq, pdata);
+
+ return ret;
+}
+
+static void xgbe_free_irqs(struct xgbe_prv_data *pdata)
+{
+ struct xgbe_channel *channel;
+ unsigned int i;
+
+ devm_free_irq(pdata->dev, pdata->dev_irq, pdata);
+
+ if (!pdata->per_channel_irq)
+ return;
+
+ channel = pdata->channel;
+ for (i = 0; i < pdata->channel_count; i++, channel++)
+ devm_free_irq(pdata->dev, channel->dma_irq, channel);
+}
+
void xgbe_init_tx_coalesce(struct xgbe_prv_data *pdata)
{
struct xgbe_hw_if *hw_if = &pdata->hw_if;
return -EINVAL;
}
- phy_stop(pdata->phydev);
-
spin_lock_irqsave(&pdata->lock, flags);
if (caller == XGMAC_DRIVER_CONTEXT)
netif_device_detach(netdev);
netif_tx_stop_all_queues(netdev);
- xgbe_napi_disable(pdata, 0);
- /* Powerdown Tx/Rx */
hw_if->powerdown_tx(pdata);
hw_if->powerdown_rx(pdata);
+ xgbe_napi_disable(pdata, 0);
+
+ phy_stop(pdata->phydev);
+
pdata->power_down = 1;
spin_unlock_irqrestore(&pdata->lock, flags);
phy_start(pdata->phydev);
- /* Enable Tx/Rx */
+ xgbe_napi_enable(pdata, 0);
+
hw_if->powerup_tx(pdata);
hw_if->powerup_rx(pdata);
if (caller == XGMAC_DRIVER_CONTEXT)
netif_device_attach(netdev);
- xgbe_napi_enable(pdata, 0);
netif_tx_start_all_queues(netdev);
spin_unlock_irqrestore(&pdata->lock, flags);
{
struct xgbe_hw_if *hw_if = &pdata->hw_if;
struct net_device *netdev = pdata->netdev;
+ int ret;
DBGPR("-->xgbe_start\n");
phy_start(pdata->phydev);
+ xgbe_napi_enable(pdata, 1);
+
+ ret = xgbe_request_irqs(pdata);
+ if (ret)
+ goto err_napi;
+
hw_if->enable_tx(pdata);
hw_if->enable_rx(pdata);
xgbe_init_tx_timers(pdata);
- xgbe_napi_enable(pdata, 1);
netif_tx_start_all_queues(netdev);
DBGPR("<--xgbe_start\n");
return 0;
+
+err_napi:
+ xgbe_napi_disable(pdata, 1);
+
+ phy_stop(pdata->phydev);
+
+ hw_if->exit(pdata);
+
+ return ret;
}
static void xgbe_stop(struct xgbe_prv_data *pdata)
DBGPR("-->xgbe_stop\n");
- phy_stop(pdata->phydev);
-
netif_tx_stop_all_queues(netdev);
- xgbe_napi_disable(pdata, 1);
xgbe_stop_tx_timers(pdata);
hw_if->disable_tx(pdata);
hw_if->disable_rx(pdata);
+ xgbe_free_irqs(pdata);
+
+ xgbe_napi_disable(pdata, 1);
+
+ phy_stop(pdata->phydev);
+
+ hw_if->exit(pdata);
+
channel = pdata->channel;
for (i = 0; i < pdata->channel_count; i++, channel++) {
if (!channel->tx_ring)
static void xgbe_restart_dev(struct xgbe_prv_data *pdata)
{
- struct xgbe_channel *channel;
- struct xgbe_hw_if *hw_if = &pdata->hw_if;
- unsigned int i;
-
DBGPR("-->xgbe_restart_dev\n");
/* If not running, "restart" will happen on open */
return;
xgbe_stop(pdata);
- synchronize_irq(pdata->dev_irq);
- if (pdata->per_channel_irq) {
- channel = pdata->channel;
- for (i = 0; i < pdata->channel_count; i++, channel++)
- synchronize_irq(channel->dma_irq);
- }
xgbe_free_tx_data(pdata);
xgbe_free_rx_data(pdata);
- /* Issue software reset to device */
- hw_if->exit(pdata);
-
xgbe_start(pdata);
DBGPR("<--xgbe_restart_dev\n");
static int xgbe_open(struct net_device *netdev)
{
struct xgbe_prv_data *pdata = netdev_priv(netdev);
- struct xgbe_hw_if *hw_if = &pdata->hw_if;
struct xgbe_desc_if *desc_if = &pdata->desc_if;
- struct xgbe_channel *channel = NULL;
- unsigned int i = 0;
int ret;
DBGPR("-->xgbe_open\n");
INIT_WORK(&pdata->restart_work, xgbe_restart);
INIT_WORK(&pdata->tx_tstamp_work, xgbe_tx_tstamp);
- /* Request interrupts */
- ret = devm_request_irq(pdata->dev, pdata->dev_irq, xgbe_isr, 0,
- netdev->name, pdata);
- if (ret) {
- netdev_alert(netdev, "error requesting irq %d\n",
- pdata->dev_irq);
- goto err_rings;
- }
-
- if (pdata->per_channel_irq) {
- channel = pdata->channel;
- for (i = 0; i < pdata->channel_count; i++, channel++) {
- snprintf(channel->dma_irq_name,
- sizeof(channel->dma_irq_name) - 1,
- "%s-TxRx-%u", netdev_name(netdev),
- channel->queue_index);
-
- ret = devm_request_irq(pdata->dev, channel->dma_irq,
- xgbe_dma_isr, 0,
- channel->dma_irq_name, channel);
- if (ret) {
- netdev_alert(netdev,
- "error requesting irq %d\n",
- channel->dma_irq);
- goto err_irq;
- }
- }
- }
-
ret = xgbe_start(pdata);
if (ret)
- goto err_start;
+ goto err_rings;
DBGPR("<--xgbe_open\n");
return 0;
-err_start:
- hw_if->exit(pdata);
-
-err_irq:
- if (pdata->per_channel_irq) {
- /* Using an unsigned int, 'i' will go to UINT_MAX and exit */
- for (i--, channel--; i < pdata->channel_count; i--, channel--)
- devm_free_irq(pdata->dev, channel->dma_irq, channel);
- }
-
- devm_free_irq(pdata->dev, pdata->dev_irq, pdata);
-
err_rings:
desc_if->free_ring_resources(pdata);
static int xgbe_close(struct net_device *netdev)
{
struct xgbe_prv_data *pdata = netdev_priv(netdev);
- struct xgbe_hw_if *hw_if = &pdata->hw_if;
struct xgbe_desc_if *desc_if = &pdata->desc_if;
- struct xgbe_channel *channel;
- unsigned int i;
DBGPR("-->xgbe_close\n");
/* Stop the device */
xgbe_stop(pdata);
- /* Issue software reset to device */
- hw_if->exit(pdata);
-
/* Free the ring descriptors and buffers */
desc_if->free_ring_resources(pdata);
- /* Release the interrupts */
- devm_free_irq(pdata->dev, pdata->dev_irq, pdata);
- if (pdata->per_channel_irq) {
- channel = pdata->channel;
- for (i = 0; i < pdata->channel_count; i++, channel++)
- devm_free_irq(pdata->dev, channel->dma_irq, channel);
- }
-
/* Free the channel and ring structures */
xgbe_free_channels(pdata);
if (!xgene_ring_mgr_init(pdata))
return -ENODEV;
- if (!efi_enabled(EFI_BOOT)) {
+ if (pdata->clk) {
clk_prepare_enable(pdata->clk);
clk_disable_unprepare(pdata->clk);
clk_prepare_enable(pdata->clk);
#ifdef CONFIG_ACPI
static const struct acpi_device_id xgene_enet_acpi_match[] = {
{ "APMC0D05", },
+ { "APMC0D30", },
+ { "APMC0D31", },
{ }
};
MODULE_DEVICE_TABLE(acpi, xgene_enet_acpi_match);
#ifdef CONFIG_OF
static struct of_device_id xgene_enet_of_match[] = {
{.compatible = "apm,xgene-enet",},
+ {.compatible = "apm,xgene1-sgenet",},
+ {.compatible = "apm,xgene1-xgenet",},
{},
};
{
struct bcm_enet_priv *priv;
struct net_device *dev;
- int tx_work_done, rx_work_done;
+ int rx_work_done;
priv = container_of(napi, struct bcm_enet_priv, napi);
dev = priv->net_dev;
ENETDMAC_IR, priv->tx_chan);
/* reclaim sent skb */
- tx_work_done = bcm_enet_tx_reclaim(dev, 0);
+ bcm_enet_tx_reclaim(dev, 0);
spin_lock(&priv->rx_lock);
rx_work_done = bcm_enet_receive_queue(dev, budget);
spin_unlock(&priv->rx_lock);
- if (rx_work_done >= budget || tx_work_done > 0) {
- /* rx/tx queue is not yet empty/clean */
+ if (rx_work_done >= budget) {
+ /* rx queue is not yet empty/clean */
return rx_work_done;
}
/* RBUF misc statistics */
STAT_RBUF("rbuf_ovflow_cnt", mib.rbuf_ovflow_cnt, RBUF_OVFL_DISC_CNTR),
STAT_RBUF("rbuf_err_cnt", mib.rbuf_err_cnt, RBUF_ERR_PKT_CNTR),
- STAT_MIB_RX("alloc_rx_buff_failed", mib.alloc_rx_buff_failed),
- STAT_MIB_RX("rx_dma_failed", mib.rx_dma_failed),
- STAT_MIB_TX("tx_dma_failed", mib.tx_dma_failed),
+ STAT_MIB_SOFT("alloc_rx_buff_failed", mib.alloc_rx_buff_failed),
+ STAT_MIB_SOFT("rx_dma_failed", mib.rx_dma_failed),
+ STAT_MIB_SOFT("tx_dma_failed", mib.tx_dma_failed),
};
#define BCM_SYSPORT_STATS_LEN ARRAY_SIZE(bcm_sysport_gstrings_stats)
s = &bcm_sysport_gstrings_stats[i];
switch (s->type) {
case BCM_SYSPORT_STAT_NETDEV:
+ case BCM_SYSPORT_STAT_SOFT:
continue;
case BCM_SYSPORT_STAT_MIB_RX:
case BCM_SYSPORT_STAT_MIB_TX:
BCM_SYSPORT_STAT_RUNT,
BCM_SYSPORT_STAT_RXCHK,
BCM_SYSPORT_STAT_RBUF,
+ BCM_SYSPORT_STAT_SOFT,
};
/* Macros to help define ethtool statistics */
#define STAT_MIB_RX(str, m) STAT_MIB(str, m, BCM_SYSPORT_STAT_MIB_RX)
#define STAT_MIB_TX(str, m) STAT_MIB(str, m, BCM_SYSPORT_STAT_MIB_TX)
#define STAT_RUNT(str, m) STAT_MIB(str, m, BCM_SYSPORT_STAT_RUNT)
+#define STAT_MIB_SOFT(str, m) STAT_MIB(str, m, BCM_SYSPORT_STAT_SOFT)
#define STAT_RXCHK(str, m, ofs) { \
.stat_string = str, \
slot->skb = skb;
slot->dma_addr = dma_addr;
- if (slot->dma_addr & 0xC0000000)
- bgmac_warn(bgmac, "DMA address using 0xC0000000 bit(s), it may need translation trick\n");
-
return 0;
}
ring->mmio_base);
goto err_dma_free;
}
- if (ring->dma_base & 0xC0000000)
- bgmac_warn(bgmac, "DMA address using 0xC0000000 bit(s), it may need translation trick\n");
ring->unaligned = bgmac_dma_unaligned(bgmac, ring,
BGMAC_DMA_RING_TX);
err = -ENOMEM;
goto err_dma_free;
}
- if (ring->dma_base & 0xC0000000)
- bgmac_warn(bgmac, "DMA address using 0xC0000000 bit(s), it may need translation trick\n");
ring->unaligned = bgmac_dma_unaligned(bgmac, ring,
BGMAC_DMA_RING_RX);
int stats_state;
/* used for synchronization of concurrent threads statistics handling */
- spinlock_t stats_lock;
+ struct mutex stats_lock;
/* used by dmae command loader */
struct dmae_command stats_dmae;
int fp_array_size;
u32 dump_preset_idx;
- bool stats_started;
- struct semaphore stats_sema;
u8 phys_port_id[ETH_ALEN];
u32 xmac_val;
u32 emac_addr;
u32 emac_val;
- u32 umac_addr;
- u32 umac_val;
+ u32 umac_addr[2];
+ u32 umac_val[2];
u32 bmac_addr;
u32 bmac_val[2];
};
return 0;
}
+/* previous driver DMAE transaction may have occurred when pre-boot stage ended
+ * and boot began, or when kdump kernel was loaded. Either case would invalidate
+ * the addresses of the transaction, resulting in was-error bit set in the pci
+ * causing all hw-to-host pcie transactions to timeout. If this happened we want
+ * to clear the interrupt which detected this from the pglueb and the was done
+ * bit
+ */
+static void bnx2x_clean_pglue_errors(struct bnx2x *bp)
+{
+ if (!CHIP_IS_E1x(bp))
+ REG_WR(bp, PGLUE_B_REG_WAS_ERROR_PF_7_0_CLR,
+ 1 << BP_ABS_FUNC(bp));
+}
+
static int bnx2x_init_hw_func(struct bnx2x *bp)
{
int port = BP_PORT(bp);
bnx2x_init_block(bp, BLOCK_PGLUE_B, init_phase);
- if (!CHIP_IS_E1x(bp))
- REG_WR(bp, PGLUE_B_REG_WAS_ERROR_PF_7_0_CLR, func);
+ bnx2x_clean_pglue_errors(bp);
bnx2x_init_block(bp, BLOCK_ATC, init_phase);
bnx2x_init_block(bp, BLOCK_DMAE, init_phase);
return base + (BP_ABS_FUNC(bp)) * stride;
}
+static bool bnx2x_prev_unload_close_umac(struct bnx2x *bp,
+ u8 port, u32 reset_reg,
+ struct bnx2x_mac_vals *vals)
+{
+ u32 mask = MISC_REGISTERS_RESET_REG_2_UMAC0 << port;
+ u32 base_addr;
+
+ if (!(mask & reset_reg))
+ return false;
+
+ BNX2X_DEV_INFO("Disable umac Rx %02x\n", port);
+ base_addr = port ? GRCBASE_UMAC1 : GRCBASE_UMAC0;
+ vals->umac_addr[port] = base_addr + UMAC_REG_COMMAND_CONFIG;
+ vals->umac_val[port] = REG_RD(bp, vals->umac_addr[port]);
+ REG_WR(bp, vals->umac_addr[port], 0);
+
+ return true;
+}
+
static void bnx2x_prev_unload_close_mac(struct bnx2x *bp,
struct bnx2x_mac_vals *vals)
{
u8 port = BP_PORT(bp);
/* reset addresses as they also mark which values were changed */
- vals->bmac_addr = 0;
- vals->umac_addr = 0;
- vals->xmac_addr = 0;
- vals->emac_addr = 0;
+ memset(vals, 0, sizeof(*vals));
reset_reg = REG_RD(bp, MISC_REG_RESET_REG_2);
REG_WR(bp, vals->xmac_addr, 0);
mac_stopped = true;
}
- mask = MISC_REGISTERS_RESET_REG_2_UMAC0 << port;
- if (mask & reset_reg) {
- BNX2X_DEV_INFO("Disable umac Rx\n");
- base_addr = BP_PORT(bp) ? GRCBASE_UMAC1 : GRCBASE_UMAC0;
- vals->umac_addr = base_addr + UMAC_REG_COMMAND_CONFIG;
- vals->umac_val = REG_RD(bp, vals->umac_addr);
- REG_WR(bp, vals->umac_addr, 0);
- mac_stopped = true;
- }
+
+ mac_stopped |= bnx2x_prev_unload_close_umac(bp, 0,
+ reset_reg, vals);
+ mac_stopped |= bnx2x_prev_unload_close_umac(bp, 1,
+ reset_reg, vals);
}
if (mac_stopped)
/* Close the MAC Rx to prevent BRB from filling up */
bnx2x_prev_unload_close_mac(bp, &mac_vals);
- /* close LLH filters towards the BRB */
+ /* close LLH filters for both ports towards the BRB */
+ bnx2x_set_rx_filter(&bp->link_params, 0);
+ bp->link_params.port ^= 1;
bnx2x_set_rx_filter(&bp->link_params, 0);
+ bp->link_params.port ^= 1;
/* Check if the UNDI driver was previously loaded */
if (bnx2x_prev_is_after_undi(bp)) {
if (mac_vals.xmac_addr)
REG_WR(bp, mac_vals.xmac_addr, mac_vals.xmac_val);
- if (mac_vals.umac_addr)
- REG_WR(bp, mac_vals.umac_addr, mac_vals.umac_val);
+ if (mac_vals.umac_addr[0])
+ REG_WR(bp, mac_vals.umac_addr[0], mac_vals.umac_val[0]);
+ if (mac_vals.umac_addr[1])
+ REG_WR(bp, mac_vals.umac_addr[1], mac_vals.umac_val[1]);
if (mac_vals.emac_addr)
REG_WR(bp, mac_vals.emac_addr, mac_vals.emac_val);
if (mac_vals.bmac_addr) {
return bnx2x_prev_mcp_done(bp);
}
-/* previous driver DMAE transaction may have occurred when pre-boot stage ended
- * and boot began, or when kdump kernel was loaded. Either case would invalidate
- * the addresses of the transaction, resulting in was-error bit set in the pci
- * causing all hw-to-host pcie transactions to timeout. If this happened we want
- * to clear the interrupt which detected this from the pglueb and the was done
- * bit
- */
-static void bnx2x_prev_interrupted_dmae(struct bnx2x *bp)
-{
- if (!CHIP_IS_E1x(bp)) {
- u32 val = REG_RD(bp, PGLUE_B_REG_PGLUE_B_INT_STS);
- if (val & PGLUE_B_PGLUE_B_INT_STS_REG_WAS_ERROR_ATTN) {
- DP(BNX2X_MSG_SP,
- "'was error' bit was found to be set in pglueb upon startup. Clearing\n");
- REG_WR(bp, PGLUE_B_REG_WAS_ERROR_PF_7_0_CLR,
- 1 << BP_FUNC(bp));
- }
- }
-}
-
static int bnx2x_prev_unload(struct bnx2x *bp)
{
int time_counter = 10;
/* clear hw from errors which may have resulted from an interrupted
* dmae transaction.
*/
- bnx2x_prev_interrupted_dmae(bp);
+ bnx2x_clean_pglue_errors(bp);
/* Release previously held locks */
hw_lock_reg = (BP_FUNC(bp) <= 5) ?
mutex_init(&bp->port.phy_mutex);
mutex_init(&bp->fw_mb_mutex);
mutex_init(&bp->drv_info_mutex);
+ mutex_init(&bp->stats_lock);
bp->drv_info_mng_owner = false;
- spin_lock_init(&bp->stats_lock);
- sema_init(&bp->stats_sema, 1);
INIT_DELAYED_WORK(&bp->sp_task, bnx2x_sp_task);
INIT_DELAYED_WORK(&bp->sp_rtnl_task, bnx2x_sp_rtnl_task);
pci_write_config_dword(bp->pdev, PCICFG_GRC_ADDRESS,
PCICFG_VENDOR_ID_OFFSET);
+ /* Set PCIe reset type to fundamental for EEH recovery */
+ pdev->needs_freset = 1;
+
/* AER (Advanced Error reporting) configuration */
rc = pci_enable_pcie_error_reporting(pdev);
if (!rc)
NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6 |
NETIF_F_RXCSUM | NETIF_F_LRO | NETIF_F_GRO |
NETIF_F_RXHASH | NETIF_F_HW_VLAN_CTAG_TX;
- if (!CHIP_IS_E1x(bp)) {
+ if (!chip_is_e1x) {
dev->hw_features |= NETIF_F_GSO_GRE | NETIF_F_GSO_UDP_TUNNEL |
NETIF_F_GSO_IPIP | NETIF_F_GSO_SIT;
dev->hw_enc_features =
cancel_delayed_work_sync(&bp->sp_task);
cancel_delayed_work_sync(&bp->period_task);
- spin_lock_bh(&bp->stats_lock);
+ mutex_lock(&bp->stats_lock);
bp->stats_state = STATS_STATE_DISABLED;
- spin_unlock_bh(&bp->stats_lock);
+ mutex_unlock(&bp->stats_lock);
bnx2x_save_statistics(bp);
cookie.vf = vf;
cookie.state = VF_ACQUIRED;
- bnx2x_stats_safe_exec(bp, bnx2x_set_vf_state, &cookie);
+ rc = bnx2x_stats_safe_exec(bp, bnx2x_set_vf_state, &cookie);
+ if (rc)
+ goto op_err;
}
DP(BNX2X_MSG_IOV, "set state to acquired\n");
*/
static void bnx2x_storm_stats_post(struct bnx2x *bp)
{
- if (!bp->stats_pending) {
- int rc;
+ int rc;
- spin_lock_bh(&bp->stats_lock);
-
- if (bp->stats_pending) {
- spin_unlock_bh(&bp->stats_lock);
- return;
- }
-
- bp->fw_stats_req->hdr.drv_stats_counter =
- cpu_to_le16(bp->stats_counter++);
+ if (bp->stats_pending)
+ return;
- DP(BNX2X_MSG_STATS, "Sending statistics ramrod %d\n",
- le16_to_cpu(bp->fw_stats_req->hdr.drv_stats_counter));
+ bp->fw_stats_req->hdr.drv_stats_counter =
+ cpu_to_le16(bp->stats_counter++);
- /* adjust the ramrod to include VF queues statistics */
- bnx2x_iov_adjust_stats_req(bp);
- bnx2x_dp_stats(bp);
+ DP(BNX2X_MSG_STATS, "Sending statistics ramrod %d\n",
+ le16_to_cpu(bp->fw_stats_req->hdr.drv_stats_counter));
- /* send FW stats ramrod */
- rc = bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_STAT_QUERY, 0,
- U64_HI(bp->fw_stats_req_mapping),
- U64_LO(bp->fw_stats_req_mapping),
- NONE_CONNECTION_TYPE);
- if (rc == 0)
- bp->stats_pending = 1;
+ /* adjust the ramrod to include VF queues statistics */
+ bnx2x_iov_adjust_stats_req(bp);
+ bnx2x_dp_stats(bp);
- spin_unlock_bh(&bp->stats_lock);
- }
+ /* send FW stats ramrod */
+ rc = bnx2x_sp_post(bp, RAMROD_CMD_ID_COMMON_STAT_QUERY, 0,
+ U64_HI(bp->fw_stats_req_mapping),
+ U64_LO(bp->fw_stats_req_mapping),
+ NONE_CONNECTION_TYPE);
+ if (rc == 0)
+ bp->stats_pending = 1;
}
static void bnx2x_hw_stats_post(struct bnx2x *bp)
*/
/* should be called under stats_sema */
-static void __bnx2x_stats_pmf_update(struct bnx2x *bp)
+static void bnx2x_stats_pmf_update(struct bnx2x *bp)
{
struct dmae_command *dmae;
u32 opcode;
}
/* should be called under stats_sema */
-static void __bnx2x_stats_start(struct bnx2x *bp)
+static void bnx2x_stats_start(struct bnx2x *bp)
{
if (IS_PF(bp)) {
if (bp->port.pmf)
bnx2x_hw_stats_post(bp);
bnx2x_storm_stats_post(bp);
}
-
- bp->stats_started = true;
-}
-
-static void bnx2x_stats_start(struct bnx2x *bp)
-{
- if (down_timeout(&bp->stats_sema, HZ/10))
- BNX2X_ERR("Unable to acquire stats lock\n");
- __bnx2x_stats_start(bp);
- up(&bp->stats_sema);
}
static void bnx2x_stats_pmf_start(struct bnx2x *bp)
{
- if (down_timeout(&bp->stats_sema, HZ/10))
- BNX2X_ERR("Unable to acquire stats lock\n");
bnx2x_stats_comp(bp);
- __bnx2x_stats_pmf_update(bp);
- __bnx2x_stats_start(bp);
- up(&bp->stats_sema);
-}
-
-static void bnx2x_stats_pmf_update(struct bnx2x *bp)
-{
- if (down_timeout(&bp->stats_sema, HZ/10))
- BNX2X_ERR("Unable to acquire stats lock\n");
- __bnx2x_stats_pmf_update(bp);
- up(&bp->stats_sema);
+ bnx2x_stats_pmf_update(bp);
+ bnx2x_stats_start(bp);
}
static void bnx2x_stats_restart(struct bnx2x *bp)
*/
if (IS_VF(bp))
return;
- if (down_timeout(&bp->stats_sema, HZ/10))
- BNX2X_ERR("Unable to acquire stats lock\n");
+
bnx2x_stats_comp(bp);
- __bnx2x_stats_start(bp);
- up(&bp->stats_sema);
+ bnx2x_stats_start(bp);
}
static void bnx2x_bmac_stats_update(struct bnx2x *bp)
{
u32 *stats_comp = bnx2x_sp(bp, stats_comp);
- /* we run update from timer context, so give up
- * if somebody is in the middle of transition
- */
- if (down_trylock(&bp->stats_sema))
+ if (bnx2x_edebug_stats_stopped(bp))
return;
- if (bnx2x_edebug_stats_stopped(bp) || !bp->stats_started)
- goto out;
-
if (IS_PF(bp)) {
if (*stats_comp != DMAE_COMP_VAL)
- goto out;
+ return;
if (bp->port.pmf)
bnx2x_hw_stats_update(bp);
BNX2X_ERR("storm stats were not updated for 3 times\n");
bnx2x_panic();
}
- goto out;
+ return;
}
} else {
/* vf doesn't collect HW statistics, and doesn't get completions
/* vf is done */
if (IS_VF(bp))
- goto out;
+ return;
if (netif_msg_timer(bp)) {
struct bnx2x_eth_stats *estats = &bp->eth_stats;
bnx2x_hw_stats_post(bp);
bnx2x_storm_stats_post(bp);
-
-out:
- up(&bp->stats_sema);
}
static void bnx2x_port_stats_stop(struct bnx2x *bp)
static void bnx2x_stats_stop(struct bnx2x *bp)
{
- int update = 0;
-
- if (down_timeout(&bp->stats_sema, HZ/10))
- BNX2X_ERR("Unable to acquire stats lock\n");
-
- bp->stats_started = false;
+ bool update = false;
bnx2x_stats_comp(bp);
bnx2x_hw_stats_post(bp);
bnx2x_stats_comp(bp);
}
-
- up(&bp->stats_sema);
}
static void bnx2x_stats_do_nothing(struct bnx2x *bp)
void bnx2x_stats_handle(struct bnx2x *bp, enum bnx2x_stats_event event)
{
- enum bnx2x_stats_state state;
- void (*action)(struct bnx2x *bp);
+ enum bnx2x_stats_state state = bp->stats_state;
+
if (unlikely(bp->panic))
return;
- spin_lock_bh(&bp->stats_lock);
- state = bp->stats_state;
+ /* Statistics update run from timer context, and we don't want to stop
+ * that context in case someone is in the middle of a transition.
+ * For other events, wait a bit until lock is taken.
+ */
+ if (!mutex_trylock(&bp->stats_lock)) {
+ if (event == STATS_EVENT_UPDATE)
+ return;
+
+ DP(BNX2X_MSG_STATS,
+ "Unlikely stats' lock contention [event %d]\n", event);
+ mutex_lock(&bp->stats_lock);
+ }
+
+ bnx2x_stats_stm[state][event].action(bp);
bp->stats_state = bnx2x_stats_stm[state][event].next_state;
- action = bnx2x_stats_stm[state][event].action;
- spin_unlock_bh(&bp->stats_lock);
- action(bp);
+ mutex_unlock(&bp->stats_lock);
if ((event != STATS_EVENT_UPDATE) || netif_msg_timer(bp))
DP(BNX2X_MSG_STATS, "state %d -> event %d -> state %d\n",
}
}
-void bnx2x_stats_safe_exec(struct bnx2x *bp,
- void (func_to_exec)(void *cookie),
- void *cookie){
- if (down_timeout(&bp->stats_sema, HZ/10))
- BNX2X_ERR("Unable to acquire stats lock\n");
+int bnx2x_stats_safe_exec(struct bnx2x *bp,
+ void (func_to_exec)(void *cookie),
+ void *cookie)
+{
+ int cnt = 10, rc = 0;
+
+ /* Wait for statistics to end [while blocking further requests],
+ * then run supplied function 'safely'.
+ */
+ mutex_lock(&bp->stats_lock);
+
bnx2x_stats_comp(bp);
+ while (bp->stats_pending && cnt--)
+ if (bnx2x_storm_stats_update(bp))
+ usleep_range(1000, 2000);
+ if (bp->stats_pending) {
+ BNX2X_ERR("Failed to wait for stats pending to clear [possibly FW is stuck]\n");
+ rc = -EBUSY;
+ goto out;
+ }
+
func_to_exec(cookie);
- __bnx2x_stats_start(bp);
- up(&bp->stats_sema);
+
+out:
+ /* No need to restart statistics - if they're enabled, the timer
+ * will restart the statistics.
+ */
+ mutex_unlock(&bp->stats_lock);
+
+ return rc;
}
void bnx2x_memset_stats(struct bnx2x *bp);
void bnx2x_stats_init(struct bnx2x *bp);
void bnx2x_stats_handle(struct bnx2x *bp, enum bnx2x_stats_event event);
-void bnx2x_stats_safe_exec(struct bnx2x *bp,
- void (func_to_exec)(void *cookie),
- void *cookie);
+int bnx2x_stats_safe_exec(struct bnx2x *bp,
+ void (func_to_exec)(void *cookie),
+ void *cookie);
/**
* bnx2x_save_statistics - save statistics when unloading.
BCMGENET_STAT_MIB_TX,
BCMGENET_STAT_RUNT,
BCMGENET_STAT_MISC,
+ BCMGENET_STAT_SOFT,
};
struct bcmgenet_stats {
#define STAT_GENET_MIB_RX(str, m) STAT_GENET_MIB(str, m, BCMGENET_STAT_MIB_RX)
#define STAT_GENET_MIB_TX(str, m) STAT_GENET_MIB(str, m, BCMGENET_STAT_MIB_TX)
#define STAT_GENET_RUNT(str, m) STAT_GENET_MIB(str, m, BCMGENET_STAT_RUNT)
+#define STAT_GENET_SOFT_MIB(str, m) STAT_GENET_MIB(str, m, BCMGENET_STAT_SOFT)
#define STAT_GENET_MISC(str, m, offset) { \
.stat_string = str, \
UMAC_RBUF_OVFL_CNT),
STAT_GENET_MISC("rbuf_err_cnt", mib.rbuf_err_cnt, UMAC_RBUF_ERR_CNT),
STAT_GENET_MISC("mdf_err_cnt", mib.mdf_err_cnt, UMAC_MDF_ERR_CNT),
- STAT_GENET_MIB_RX("alloc_rx_buff_failed", mib.alloc_rx_buff_failed),
- STAT_GENET_MIB_RX("rx_dma_failed", mib.rx_dma_failed),
- STAT_GENET_MIB_TX("tx_dma_failed", mib.tx_dma_failed),
+ STAT_GENET_SOFT_MIB("alloc_rx_buff_failed", mib.alloc_rx_buff_failed),
+ STAT_GENET_SOFT_MIB("rx_dma_failed", mib.rx_dma_failed),
+ STAT_GENET_SOFT_MIB("tx_dma_failed", mib.tx_dma_failed),
};
#define BCMGENET_STATS_LEN ARRAY_SIZE(bcmgenet_gstrings_stats)
s = &bcmgenet_gstrings_stats[i];
switch (s->type) {
case BCMGENET_STAT_NETDEV:
+ case BCMGENET_STAT_SOFT:
continue;
case BCMGENET_STAT_MIB_RX:
case BCMGENET_STAT_MIB_TX:
}
/* Unlocked version of the reclaim routine */
-static void __bcmgenet_tx_reclaim(struct net_device *dev,
- struct bcmgenet_tx_ring *ring)
+static unsigned int __bcmgenet_tx_reclaim(struct net_device *dev,
+ struct bcmgenet_tx_ring *ring)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
int last_tx_cn, last_c_index, num_tx_bds;
struct enet_cb *tx_cb_ptr;
struct netdev_queue *txq;
+ unsigned int pkts_compl = 0;
unsigned int bds_compl;
unsigned int c_index;
tx_cb_ptr = ring->cbs + last_c_index;
bds_compl = 0;
if (tx_cb_ptr->skb) {
+ pkts_compl++;
bds_compl = skb_shinfo(tx_cb_ptr->skb)->nr_frags + 1;
dev->stats.tx_bytes += tx_cb_ptr->skb->len;
dma_unmap_single(&dev->dev,
last_c_index &= (num_tx_bds - 1);
}
- if (ring->free_bds > (MAX_SKB_FRAGS + 1))
- ring->int_disable(priv, ring);
-
- if (netif_tx_queue_stopped(txq))
- netif_tx_wake_queue(txq);
+ if (ring->free_bds > (MAX_SKB_FRAGS + 1)) {
+ if (netif_tx_queue_stopped(txq))
+ netif_tx_wake_queue(txq);
+ }
ring->c_index = c_index;
+
+ return pkts_compl;
}
-static void bcmgenet_tx_reclaim(struct net_device *dev,
+static unsigned int bcmgenet_tx_reclaim(struct net_device *dev,
struct bcmgenet_tx_ring *ring)
{
+ unsigned int released;
unsigned long flags;
spin_lock_irqsave(&ring->lock, flags);
- __bcmgenet_tx_reclaim(dev, ring);
+ released = __bcmgenet_tx_reclaim(dev, ring);
spin_unlock_irqrestore(&ring->lock, flags);
+
+ return released;
+}
+
+static int bcmgenet_tx_poll(struct napi_struct *napi, int budget)
+{
+ struct bcmgenet_tx_ring *ring =
+ container_of(napi, struct bcmgenet_tx_ring, napi);
+ unsigned int work_done = 0;
+
+ work_done = bcmgenet_tx_reclaim(ring->priv->dev, ring);
+
+ if (work_done == 0) {
+ napi_complete(napi);
+ ring->int_enable(ring->priv, ring);
+
+ return 0;
+ }
+
+ return budget;
}
static void bcmgenet_tx_reclaim_all(struct net_device *dev)
bcmgenet_tdma_ring_writel(priv, ring->index,
ring->prod_index, TDMA_PROD_INDEX);
- if (ring->free_bds <= (MAX_SKB_FRAGS + 1)) {
+ if (ring->free_bds <= (MAX_SKB_FRAGS + 1))
netif_tx_stop_queue(txq);
- ring->int_enable(priv, ring);
- }
out:
spin_unlock_irqrestore(&ring->lock, flags);
struct device *kdev = &priv->pdev->dev;
int ret;
u32 reg, cpu_mask_clear;
+ int index;
dev_dbg(&priv->pdev->dev, "bcmgenet: init_umac\n");
bcmgenet_intr_disable(priv);
- cpu_mask_clear = UMAC_IRQ_RXDMA_BDONE;
+ cpu_mask_clear = UMAC_IRQ_RXDMA_BDONE | UMAC_IRQ_TXDMA_BDONE;
dev_dbg(kdev, "%s:Enabling RXDMA_BDONE interrupt\n", __func__);
bcmgenet_intrl2_0_writel(priv, cpu_mask_clear, INTRL2_CPU_MASK_CLEAR);
+ for (index = 0; index < priv->hw_params->tx_queues; index++)
+ bcmgenet_intrl2_1_writel(priv, (1 << index),
+ INTRL2_CPU_MASK_CLEAR);
+
/* Enable rx/tx engine.*/
dev_dbg(kdev, "done init umac\n");
unsigned int first_bd;
spin_lock_init(&ring->lock);
+ ring->priv = priv;
+ netif_napi_add(priv->dev, &ring->napi, bcmgenet_tx_poll, 64);
ring->index = index;
if (index == DESC_INDEX) {
ring->queue = 0;
TDMA_WRITE_PTR);
bcmgenet_tdma_ring_writel(priv, index, end_ptr * words_per_bd - 1,
DMA_END_ADDR);
+
+ napi_enable(&ring->napi);
+}
+
+static void bcmgenet_fini_tx_ring(struct bcmgenet_priv *priv,
+ unsigned int index)
+{
+ struct bcmgenet_tx_ring *ring = &priv->tx_rings[index];
+
+ napi_disable(&ring->napi);
+ netif_napi_del(&ring->napi);
}
/* Initialize a RDMA ring */
return ret;
}
-static void bcmgenet_fini_dma(struct bcmgenet_priv *priv)
+static void __bcmgenet_fini_dma(struct bcmgenet_priv *priv)
{
int i;
kfree(priv->tx_cbs);
}
+static void bcmgenet_fini_dma(struct bcmgenet_priv *priv)
+{
+ int i;
+
+ bcmgenet_fini_tx_ring(priv, DESC_INDEX);
+
+ for (i = 0; i < priv->hw_params->tx_queues; i++)
+ bcmgenet_fini_tx_ring(priv, i);
+
+ __bcmgenet_fini_dma(priv);
+}
+
/* init_edma: Initialize DMA control register */
static int bcmgenet_init_dma(struct bcmgenet_priv *priv)
{
priv->tx_cbs = kcalloc(priv->num_tx_bds, sizeof(struct enet_cb),
GFP_KERNEL);
if (!priv->tx_cbs) {
- bcmgenet_fini_dma(priv);
+ __bcmgenet_fini_dma(priv);
return -ENOMEM;
}
struct bcmgenet_priv, napi);
unsigned int work_done;
- /* tx reclaim */
- bcmgenet_tx_reclaim(priv->dev, &priv->tx_rings[DESC_INDEX]);
-
work_done = bcmgenet_desc_rx(priv, budget);
/* Advancing our consumer index*/
static irqreturn_t bcmgenet_isr1(int irq, void *dev_id)
{
struct bcmgenet_priv *priv = dev_id;
+ struct bcmgenet_tx_ring *ring;
unsigned int index;
/* Save irq status for bottom-half processing. */
priv->irq1_stat =
bcmgenet_intrl2_1_readl(priv, INTRL2_CPU_STAT) &
- ~priv->int1_mask;
+ ~bcmgenet_intrl2_1_readl(priv, INTRL2_CPU_MASK_STATUS);
/* clear interrupts */
bcmgenet_intrl2_1_writel(priv, priv->irq1_stat, INTRL2_CPU_CLEAR);
netif_dbg(priv, intr, priv->dev,
"%s: IRQ=0x%x\n", __func__, priv->irq1_stat);
+
/* Check the MBDONE interrupts.
* packet is done, reclaim descriptors
*/
- if (priv->irq1_stat & 0x0000ffff) {
- index = 0;
- for (index = 0; index < 16; index++) {
- if (priv->irq1_stat & (1 << index))
- bcmgenet_tx_reclaim(priv->dev,
- &priv->tx_rings[index]);
+ for (index = 0; index < priv->hw_params->tx_queues; index++) {
+ if (!(priv->irq1_stat & BIT(index)))
+ continue;
+
+ ring = &priv->tx_rings[index];
+
+ if (likely(napi_schedule_prep(&ring->napi))) {
+ ring->int_disable(priv, ring);
+ __napi_schedule(&ring->napi);
}
}
+
return IRQ_HANDLED;
}
}
if (priv->irq0_stat &
(UMAC_IRQ_TXDMA_BDONE | UMAC_IRQ_TXDMA_PDONE)) {
- /* Tx reclaim */
- bcmgenet_tx_reclaim(priv->dev, &priv->tx_rings[DESC_INDEX]);
+ struct bcmgenet_tx_ring *ring = &priv->tx_rings[DESC_INDEX];
+
+ if (likely(napi_schedule_prep(&ring->napi))) {
+ ring->int_disable(priv, ring);
+ __napi_schedule(&ring->napi);
+ }
}
if (priv->irq0_stat & (UMAC_IRQ_PHY_DET_R |
UMAC_IRQ_PHY_DET_F |
struct bcmgenet_tx_ring {
spinlock_t lock; /* ring lock */
+ struct napi_struct napi; /* NAPI per tx queue */
unsigned int index; /* ring index */
unsigned int queue; /* queue index */
struct enet_cb *cbs; /* tx ring buffer control block*/
struct bcmgenet_tx_ring *);
void (*int_disable)(struct bcmgenet_priv *priv,
struct bcmgenet_tx_ring *);
+ struct bcmgenet_priv *priv;
};
/* device context */
if (wol->wolopts & ~(WAKE_MAGIC | WAKE_MAGICSECURE))
return -EINVAL;
+ reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
if (wol->wolopts & WAKE_MAGICSECURE) {
bcmgenet_umac_writel(priv, get_unaligned_be16(&wol->sopass[0]),
UMAC_MPD_PW_MS);
bcmgenet_umac_writel(priv, get_unaligned_be32(&wol->sopass[2]),
UMAC_MPD_PW_LS);
- reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL);
reg |= MPD_PW_EN;
- bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
+ } else {
+ reg &= ~MPD_PW_EN;
}
+ bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL);
/* Flag the device and relevant IRQ as wakeup capable */
if (wol->wolopts) {
};
#if defined(CONFIG_OF)
-static struct macb_config pc302gem_config = {
+static const struct macb_config pc302gem_config = {
.caps = MACB_CAPS_SG_DISABLED | MACB_CAPS_GIGABIT_MODE_AVAILABLE,
.dma_burst_length = 16,
};
-static struct macb_config sama5d3_config = {
+static const struct macb_config sama5d3_config = {
.caps = MACB_CAPS_SG_DISABLED | MACB_CAPS_GIGABIT_MODE_AVAILABLE,
.dma_burst_length = 16,
};
-static struct macb_config sama5d4_config = {
+static const struct macb_config sama5d4_config = {
.caps = 0,
.dma_burst_length = 4,
};
if (bp->pdev->dev.of_node) {
match = of_match_node(macb_dt_ids, bp->pdev->dev.of_node);
if (match && match->data) {
- config = (const struct macb_config *)match->data;
+ config = match->data;
bp->caps = config->caps;
/*
/* Bitfields in MID */
#define MACB_IDNUM_OFFSET 16
-#define MACB_IDNUM_SIZE 16
+#define MACB_IDNUM_SIZE 12
#define MACB_REV_OFFSET 0
#define MACB_REV_SIZE 16
}
static unsigned int clip_addr_hash(struct clip_tbl *ctbl, const u32 *addr,
- int addr_len)
+ u8 v6)
{
- return addr_len == 4 ? ipv4_clip_hash(ctbl, addr) :
- ipv6_clip_hash(ctbl, addr);
+ return v6 ? ipv6_clip_hash(ctbl, addr) :
+ ipv4_clip_hash(ctbl, addr);
}
static int clip6_get_mbox(const struct net_device *dev,
struct clip_entry *ce, *cte;
u32 *addr = (u32 *)lip;
int hash;
- int addr_len;
- int ret = 0;
+ int ret = -1;
if (!ctbl)
return 0;
- if (v6)
- addr_len = 16;
- else
- addr_len = 4;
-
- hash = clip_addr_hash(ctbl, addr, addr_len);
+ hash = clip_addr_hash(ctbl, addr, v6);
read_lock_bh(&ctbl->lock);
list_for_each_entry(cte, &ctbl->hash_list[hash], list) {
- if (addr_len == cte->addr_len &&
- memcmp(lip, cte->addr, cte->addr_len) == 0) {
+ if (cte->addr6.sin6_family == AF_INET6 && v6)
+ ret = memcmp(lip, cte->addr6.sin6_addr.s6_addr,
+ sizeof(struct in6_addr));
+ else if (cte->addr.sin_family == AF_INET && !v6)
+ ret = memcmp(lip, (char *)(&cte->addr.sin_addr),
+ sizeof(struct in_addr));
+ if (!ret) {
ce = cte;
read_unlock_bh(&ctbl->lock);
goto found;
spin_lock_init(&ce->lock);
atomic_set(&ce->refcnt, 0);
atomic_dec(&ctbl->nfree);
- ce->addr_len = addr_len;
- memcpy(ce->addr, lip, addr_len);
list_add_tail(&ce->list, &ctbl->hash_list[hash]);
if (v6) {
+ ce->addr6.sin6_family = AF_INET6;
+ memcpy(ce->addr6.sin6_addr.s6_addr,
+ lip, sizeof(struct in6_addr));
ret = clip6_get_mbox(dev, (const struct in6_addr *)lip);
if (ret) {
write_unlock_bh(&ctbl->lock);
return ret;
}
+ } else {
+ ce->addr.sin_family = AF_INET;
+ memcpy((char *)(&ce->addr.sin_addr), lip,
+ sizeof(struct in_addr));
}
} else {
write_unlock_bh(&ctbl->lock);
struct clip_entry *ce, *cte;
u32 *addr = (u32 *)lip;
int hash;
- int addr_len;
-
- if (v6)
- addr_len = 16;
- else
- addr_len = 4;
+ int ret = -1;
- hash = clip_addr_hash(ctbl, addr, addr_len);
+ hash = clip_addr_hash(ctbl, addr, v6);
read_lock_bh(&ctbl->lock);
list_for_each_entry(cte, &ctbl->hash_list[hash], list) {
- if (addr_len == cte->addr_len &&
- memcmp(lip, cte->addr, cte->addr_len) == 0) {
+ if (cte->addr6.sin6_family == AF_INET6 && v6)
+ ret = memcmp(lip, cte->addr6.sin6_addr.s6_addr,
+ sizeof(struct in6_addr));
+ else if (cte->addr.sin_family == AF_INET && !v6)
+ ret = memcmp(lip, (char *)(&cte->addr.sin_addr),
+ sizeof(struct in_addr));
+ if (!ret) {
ce = cte;
read_unlock_bh(&ctbl->lock);
goto found;
for (i = 0 ; i < ctbl->clipt_size; ++i) {
list_for_each_entry(ce, &ctbl->hash_list[i], list) {
ip[0] = '\0';
- if (ce->addr_len == 16)
- sprintf(ip, "%pI6c", ce->addr);
- else
- sprintf(ip, "%pI4c", ce->addr);
+ sprintf(ip, "%pISc", &ce->addr);
seq_printf(seq, "%-25s %u\n", ip,
atomic_read(&ce->refcnt));
}
spinlock_t lock; /* Hold while modifying clip reference */
atomic_t refcnt;
struct list_head list;
- u32 addr[4];
- int addr_len;
+ union {
+ struct sockaddr_in addr;
+ struct sockaddr_in6 addr6;
+ };
};
struct clip_tbl {
enum {
INGQ_EXTRAS = 2, /* firmware event queue and */
/* forwarded interrupts */
- MAX_EGRQ = MAX_ETH_QSETS*2 + MAX_OFLD_QSETS*2
- + MAX_CTRL_QUEUES + MAX_RDMA_QUEUES + MAX_ISCSI_QUEUES,
MAX_INGQ = MAX_ETH_QSETS + MAX_OFLD_QSETS + MAX_RDMA_QUEUES
+ MAX_RDMA_CIQS + MAX_ISCSI_QUEUES + INGQ_EXTRAS,
};
unsigned int idma_qid[2]; /* SGE IDMA Hung Ingress Queue ID */
unsigned int egr_start;
+ unsigned int egr_sz;
unsigned int ingr_start;
- void *egr_map[MAX_EGRQ]; /* qid->queue egress queue map */
- struct sge_rspq *ingr_map[MAX_INGQ]; /* qid->queue ingress queue map */
- DECLARE_BITMAP(starving_fl, MAX_EGRQ);
- DECLARE_BITMAP(txq_maperr, MAX_EGRQ);
+ unsigned int ingr_sz;
+ void **egr_map; /* qid->queue egress queue map */
+ struct sge_rspq **ingr_map; /* qid->queue ingress queue map */
+ unsigned long *starving_fl;
+ unsigned long *txq_maperr;
struct timer_list rx_timer; /* refills starving FLs */
struct timer_list tx_timer; /* checks Tx queues */
};
#define T4_MEMORY_WRITE 0
#define T4_MEMORY_READ 1
int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr, u32 len,
- __be32 *buf, int dir);
+ void *buf, int dir);
static inline int t4_memory_write(struct adapter *adap, int mtype, u32 addr,
u32 len, __be32 *buf)
{
unsigned int qtimer_val(const struct adapter *adap,
const struct sge_rspq *q);
+
+int t4_init_devlog_params(struct adapter *adapter);
int t4_init_sge_params(struct adapter *adapter);
int t4_init_tp_params(struct adapter *adap);
int t4_filter_field_shift(const struct adapter *adap, int filter_sel);
"0.9375" };
int i;
- u16 incr[NMTUS][NCCTRL_WIN];
+ u16 (*incr)[NCCTRL_WIN];
struct adapter *adap = seq->private;
+ incr = kmalloc(sizeof(*incr) * NMTUS, GFP_KERNEL);
+ if (!incr)
+ return -ENOMEM;
+
t4_read_cong_tbl(adap, incr);
for (i = 0; i < NCCTRL_WIN; ++i) {
adap->params.a_wnd[i],
dec_fac[adap->params.b_wnd[i]]);
}
+
+ kfree(incr);
return 0;
}
{
int i;
- for (i = 0; i < ARRAY_SIZE(adap->sge.ingr_map); i++) {
+ for (i = 0; i < adap->sge.ingr_sz; i++) {
struct sge_rspq *q = adap->sge.ingr_map[i];
if (q && q->handler) {
}
}
+/* Disable interrupt and napi handler */
+static void disable_interrupts(struct adapter *adap)
+{
+ if (adap->flags & FULL_INIT_DONE) {
+ t4_intr_disable(adap);
+ if (adap->flags & USING_MSIX) {
+ free_msix_queue_irqs(adap);
+ free_irq(adap->msix_info[0].vec, adap);
+ } else {
+ free_irq(adap->pdev->irq, adap);
+ }
+ quiesce_rx(adap);
+ }
+}
+
/*
* Enable NAPI scheduling and interrupt generation for all Rx queues.
*/
{
int i;
- for (i = 0; i < ARRAY_SIZE(adap->sge.ingr_map); i++) {
+ for (i = 0; i < adap->sge.ingr_sz; i++) {
struct sge_rspq *q = adap->sge.ingr_map[i];
if (!q)
int err, msi_idx, i, j;
struct sge *s = &adap->sge;
- bitmap_zero(s->starving_fl, MAX_EGRQ);
- bitmap_zero(s->txq_maperr, MAX_EGRQ);
+ bitmap_zero(s->starving_fl, s->egr_sz);
+ bitmap_zero(s->txq_maperr, s->egr_sz);
if (adap->flags & USING_MSIX)
msi_idx = 1; /* vector 0 is for non-queue interrupts */
msi_idx = -((int)s->intrq.abs_id + 1);
}
+ /* NOTE: If you add/delete any Ingress/Egress Queue allocations in here,
+ * don't forget to update the following which need to be
+ * synchronized to and changes here.
+ *
+ * 1. The calculations of MAX_INGQ in cxgb4.h.
+ *
+ * 2. Update enable_msix/name_msix_vecs/request_msix_queue_irqs
+ * to accommodate any new/deleted Ingress Queues
+ * which need MSI-X Vectors.
+ *
+ * 3. Update sge_qinfo_show() to include information on the
+ * new/deleted queues.
+ */
err = t4_sge_alloc_rxq(adap, &s->fw_evtq, true, adap->port[0],
msi_idx, NULL, fwevtq_handler);
if (err) {
static void cxgb_down(struct adapter *adapter)
{
- t4_intr_disable(adapter);
cancel_work_sync(&adapter->tid_release_task);
cancel_work_sync(&adapter->db_full_task);
cancel_work_sync(&adapter->db_drop_task);
adapter->tid_release_task_busy = false;
adapter->tid_release_head = NULL;
- if (adapter->flags & USING_MSIX) {
- free_msix_queue_irqs(adapter);
- free_irq(adapter->msix_info[0].vec, adapter);
- } else
- free_irq(adapter->pdev->irq, adapter);
- quiesce_rx(adapter);
t4_sge_stop(adapter);
t4_free_sge_resources(adapter);
adapter->flags &= ~FULL_INIT_DONE;
if (ret < 0)
return ret;
- ret = t4_cfg_pfvf(adap, adap->fn, adap->fn, 0, MAX_EGRQ, 64, MAX_INGQ,
- 0, 0, 4, 0xf, 0xf, 16, FW_CMD_CAP_PF, FW_CMD_CAP_PF);
+ ret = t4_cfg_pfvf(adap, adap->fn, adap->fn, 0, adap->sge.egr_sz, 64,
+ MAX_INGQ, 0, 0, 4, 0xf, 0xf, 16, FW_CMD_CAP_PF,
+ FW_CMD_CAP_PF);
if (ret < 0)
return ret;
enum dev_state state;
u32 params[7], val[7];
struct fw_caps_config_cmd caps_cmd;
- struct fw_devlog_cmd devlog_cmd;
- u32 devlog_meminfo;
int reset = 1;
+ /* Grab Firmware Device Log parameters as early as possible so we have
+ * access to it for debugging, etc.
+ */
+ ret = t4_init_devlog_params(adap);
+ if (ret < 0)
+ return ret;
+
/* Contact FW, advertising Master capability */
ret = t4_fw_hello(adap, adap->mbox, adap->mbox, MASTER_MAY, &state);
if (ret < 0) {
if (ret < 0)
goto bye;
- /* Read firmware device log parameters. We really need to find a way
- * to get these parameters initialized with some default values (which
- * are likely to be correct) for the case where we either don't
- * attache to the firmware or it's crashed when we probe the adapter.
- * That way we'll still be able to perform early firmware startup
- * debugging ... If the request to get the Firmware's Device Log
- * parameters fails, we'll live so we don't make that a fatal error.
- */
- memset(&devlog_cmd, 0, sizeof(devlog_cmd));
- devlog_cmd.op_to_write = htonl(FW_CMD_OP_V(FW_DEVLOG_CMD) |
- FW_CMD_REQUEST_F | FW_CMD_READ_F);
- devlog_cmd.retval_len16 = htonl(FW_LEN16(devlog_cmd));
- ret = t4_wr_mbox(adap, adap->mbox, &devlog_cmd, sizeof(devlog_cmd),
- &devlog_cmd);
- if (ret == 0) {
- devlog_meminfo =
- ntohl(devlog_cmd.memtype_devlog_memaddr16_devlog);
- adap->params.devlog.memtype =
- FW_DEVLOG_CMD_MEMTYPE_DEVLOG_G(devlog_meminfo);
- adap->params.devlog.start =
- FW_DEVLOG_CMD_MEMADDR16_DEVLOG_G(devlog_meminfo) << 4;
- adap->params.devlog.size = ntohl(devlog_cmd.memsize_devlog);
- }
-
/*
* Find out what ports are available to us. Note that we need to do
* this before calling adap_init0_no_config() since it needs nports
adap->tids.nftids = val[4] - val[3] + 1;
adap->sge.ingr_start = val[5];
+ /* qids (ingress/egress) returned from firmware can be anywhere
+ * in the range from EQ(IQFLINT)_START to EQ(IQFLINT)_END.
+ * Hence driver needs to allocate memory for this range to
+ * store the queue info. Get the highest IQFLINT/EQ index returned
+ * in FW_EQ_*_CMD.alloc command.
+ */
+ params[0] = FW_PARAM_PFVF(EQ_END);
+ params[1] = FW_PARAM_PFVF(IQFLINT_END);
+ ret = t4_query_params(adap, adap->mbox, adap->fn, 0, 2, params, val);
+ if (ret < 0)
+ goto bye;
+ adap->sge.egr_sz = val[0] - adap->sge.egr_start + 1;
+ adap->sge.ingr_sz = val[1] - adap->sge.ingr_start + 1;
+
+ adap->sge.egr_map = kcalloc(adap->sge.egr_sz,
+ sizeof(*adap->sge.egr_map), GFP_KERNEL);
+ if (!adap->sge.egr_map) {
+ ret = -ENOMEM;
+ goto bye;
+ }
+
+ adap->sge.ingr_map = kcalloc(adap->sge.ingr_sz,
+ sizeof(*adap->sge.ingr_map), GFP_KERNEL);
+ if (!adap->sge.ingr_map) {
+ ret = -ENOMEM;
+ goto bye;
+ }
+
+ /* Allocate the memory for the vaious egress queue bitmaps
+ * ie starving_fl and txq_maperr.
+ */
+ adap->sge.starving_fl = kcalloc(BITS_TO_LONGS(adap->sge.egr_sz),
+ sizeof(long), GFP_KERNEL);
+ if (!adap->sge.starving_fl) {
+ ret = -ENOMEM;
+ goto bye;
+ }
+
+ adap->sge.txq_maperr = kcalloc(BITS_TO_LONGS(adap->sge.egr_sz),
+ sizeof(long), GFP_KERNEL);
+ if (!adap->sge.txq_maperr) {
+ ret = -ENOMEM;
+ goto bye;
+ }
+
params[0] = FW_PARAM_PFVF(CLIP_START);
params[1] = FW_PARAM_PFVF(CLIP_END);
ret = t4_query_params(adap, adap->mbox, adap->fn, 0, 2, params, val);
* happened to HW/FW, stop issuing commands.
*/
bye:
+ kfree(adap->sge.egr_map);
+ kfree(adap->sge.ingr_map);
+ kfree(adap->sge.starving_fl);
+ kfree(adap->sge.txq_maperr);
if (ret != -ETIMEDOUT && ret != -EIO)
t4_fw_bye(adap, adap->mbox);
return ret;
netif_carrier_off(dev);
}
spin_unlock(&adap->stats_lock);
+ disable_interrupts(adap);
if (adap->flags & FULL_INIT_DONE)
cxgb_down(adap);
rtnl_unlock();
t4_free_mem(adapter->l2t);
t4_free_mem(adapter->tids.tid_tab);
+ kfree(adapter->sge.egr_map);
+ kfree(adapter->sge.ingr_map);
+ kfree(adapter->sge.starving_fl);
+ kfree(adapter->sge.txq_maperr);
disable_msi(adapter);
for_each_port(adapter, i)
if (is_offload(adapter))
detach_ulds(adapter);
+ disable_interrupts(adapter);
+
for_each_port(adapter, i)
if (adapter->port[i]->reg_state == NETREG_REGISTERED)
unregister_netdev(adapter->port[i]);
struct adapter *adap = (struct adapter *)data;
struct sge *s = &adap->sge;
- for (i = 0; i < ARRAY_SIZE(s->starving_fl); i++)
+ for (i = 0; i < BITS_TO_LONGS(s->egr_sz); i++)
for (m = s->starving_fl[i]; m; m &= m - 1) {
struct sge_eth_rxq *rxq;
unsigned int id = __ffs(m) + i * BITS_PER_LONG;
struct adapter *adap = (struct adapter *)data;
struct sge *s = &adap->sge;
- for (i = 0; i < ARRAY_SIZE(s->txq_maperr); i++)
+ for (i = 0; i < BITS_TO_LONGS(s->egr_sz); i++)
for (m = s->txq_maperr[i]; m; m &= m - 1) {
unsigned long id = __ffs(m) + i * BITS_PER_LONG;
struct sge_ofld_txq *txq = s->egr_map[id];
free_rspq_fl(adap, &adap->sge.intrq, NULL);
/* clear the reverse egress queue map */
- memset(adap->sge.egr_map, 0, sizeof(adap->sge.egr_map));
+ memset(adap->sge.egr_map, 0,
+ adap->sge.egr_sz * sizeof(*adap->sge.egr_map));
}
void t4_sge_start(struct adapter *adap)
* @mtype: memory type: MEM_EDC0, MEM_EDC1 or MEM_MC
* @addr: address within indicated memory type
* @len: amount of memory to transfer
- * @buf: host memory buffer
+ * @hbuf: host memory buffer
* @dir: direction of transfer T4_MEMORY_READ (1) or T4_MEMORY_WRITE (0)
*
* Reads/writes an [almost] arbitrary memory region in the firmware: the
* caller's responsibility to perform appropriate byte order conversions.
*/
int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr,
- u32 len, __be32 *buf, int dir)
+ u32 len, void *hbuf, int dir)
{
u32 pos, offset, resid, memoffset;
u32 edc_size, mc_size, win_pf, mem_reg, mem_aperture, mem_base;
+ u32 *buf;
/* Argument sanity checks ...
*/
- if (addr & 0x3)
+ if (addr & 0x3 || (uintptr_t)hbuf & 0x3)
return -EINVAL;
+ buf = (u32 *)hbuf;
/* It's convenient to be able to handle lengths which aren't a
* multiple of 32-bits because we often end up transferring files to
/* Transfer data to/from the adapter as long as there's an integral
* number of 32-bit transfers to complete.
+ *
+ * A note on Endianness issues:
+ *
+ * The "register" reads and writes below from/to the PCI-E Memory
+ * Window invoke the standard adapter Big-Endian to PCI-E Link
+ * Little-Endian "swizzel." As a result, if we have the following
+ * data in adapter memory:
+ *
+ * Memory: ... | b0 | b1 | b2 | b3 | ...
+ * Address: i+0 i+1 i+2 i+3
+ *
+ * Then a read of the adapter memory via the PCI-E Memory Window
+ * will yield:
+ *
+ * x = readl(i)
+ * 31 0
+ * [ b3 | b2 | b1 | b0 ]
+ *
+ * If this value is stored into local memory on a Little-Endian system
+ * it will show up correctly in local memory as:
+ *
+ * ( ..., b0, b1, b2, b3, ... )
+ *
+ * But on a Big-Endian system, the store will show up in memory
+ * incorrectly swizzled as:
+ *
+ * ( ..., b3, b2, b1, b0, ... )
+ *
+ * So we need to account for this in the reads and writes to the
+ * PCI-E Memory Window below by undoing the register read/write
+ * swizzels.
*/
while (len > 0) {
if (dir == T4_MEMORY_READ)
- *buf++ = (__force __be32) t4_read_reg(adap,
- mem_base + offset);
+ *buf++ = le32_to_cpu((__force __le32)t4_read_reg(adap,
+ mem_base + offset));
else
t4_write_reg(adap, mem_base + offset,
- (__force u32) *buf++);
+ (__force u32)cpu_to_le32(*buf++));
offset += sizeof(__be32);
len -= sizeof(__be32);
*/
if (resid) {
union {
- __be32 word;
+ u32 word;
char byte[4];
} last;
unsigned char *bp;
int i;
if (dir == T4_MEMORY_READ) {
- last.word = (__force __be32) t4_read_reg(adap,
- mem_base + offset);
+ last.word = le32_to_cpu(
+ (__force __le32)t4_read_reg(adap,
+ mem_base + offset));
for (bp = (unsigned char *)buf, i = resid; i < 4; i++)
bp[i] = last.byte[i];
} else {
for (i = resid; i < 4; i++)
last.byte[i] = 0;
t4_write_reg(adap, mem_base + offset,
- (__force u32) last.word);
+ (__force u32)cpu_to_le32(last.word));
}
}
}
/* Installed successfully, update the cached header too. */
- memcpy(card_fw, fs_fw, sizeof(*card_fw));
+ *card_fw = *fs_fw;
card_fw_usable = 1;
*reset = 0; /* already reset as part of load_fw */
}
return 0;
}
+/**
+ * t4_init_devlog_params - initialize adapter->params.devlog
+ * @adap: the adapter
+ *
+ * Initialize various fields of the adapter's Firmware Device Log
+ * Parameters structure.
+ */
+int t4_init_devlog_params(struct adapter *adap)
+{
+ struct devlog_params *dparams = &adap->params.devlog;
+ u32 pf_dparams;
+ unsigned int devlog_meminfo;
+ struct fw_devlog_cmd devlog_cmd;
+ int ret;
+
+ /* If we're dealing with newer firmware, the Device Log Paramerters
+ * are stored in a designated register which allows us to access the
+ * Device Log even if we can't talk to the firmware.
+ */
+ pf_dparams =
+ t4_read_reg(adap, PCIE_FW_REG(PCIE_FW_PF_A, PCIE_FW_PF_DEVLOG));
+ if (pf_dparams) {
+ unsigned int nentries, nentries128;
+
+ dparams->memtype = PCIE_FW_PF_DEVLOG_MEMTYPE_G(pf_dparams);
+ dparams->start = PCIE_FW_PF_DEVLOG_ADDR16_G(pf_dparams) << 4;
+
+ nentries128 = PCIE_FW_PF_DEVLOG_NENTRIES128_G(pf_dparams);
+ nentries = (nentries128 + 1) * 128;
+ dparams->size = nentries * sizeof(struct fw_devlog_e);
+
+ return 0;
+ }
+
+ /* Otherwise, ask the firmware for it's Device Log Parameters.
+ */
+ memset(&devlog_cmd, 0, sizeof(devlog_cmd));
+ devlog_cmd.op_to_write = htonl(FW_CMD_OP_V(FW_DEVLOG_CMD) |
+ FW_CMD_REQUEST_F | FW_CMD_READ_F);
+ devlog_cmd.retval_len16 = htonl(FW_LEN16(devlog_cmd));
+ ret = t4_wr_mbox(adap, adap->mbox, &devlog_cmd, sizeof(devlog_cmd),
+ &devlog_cmd);
+ if (ret)
+ return ret;
+
+ devlog_meminfo = ntohl(devlog_cmd.memtype_devlog_memaddr16_devlog);
+ dparams->memtype = FW_DEVLOG_CMD_MEMTYPE_DEVLOG_G(devlog_meminfo);
+ dparams->start = FW_DEVLOG_CMD_MEMADDR16_DEVLOG_G(devlog_meminfo) << 4;
+ dparams->size = ntohl(devlog_cmd.memsize_devlog);
+
+ return 0;
+}
+
/**
* t4_init_sge_params - initialize adap->params.sge
* @adapter: the adapter
#define MC_BIST_STATUS_REG(reg_addr, idx) ((reg_addr) + (idx) * 4)
#define EDC_BIST_STATUS_REG(reg_addr, idx) ((reg_addr) + (idx) * 4)
+#define PCIE_FW_REG(reg_addr, idx) ((reg_addr) + (idx) * 4)
+
#define SGE_PF_KDOORBELL_A 0x0
#define QID_S 15
#define PFNUM_V(x) ((x) << PFNUM_S)
#define PCIE_FW_A 0x30b8
+#define PCIE_FW_PF_A 0x30bc
#define PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS_A 0x5908
FW_RI_BIND_MW_WR = 0x18,
FW_RI_FR_NSMR_WR = 0x19,
FW_RI_INV_LSTAG_WR = 0x1a,
- FW_LASTC2E_WR = 0x40
+ FW_LASTC2E_WR = 0x70
};
struct fw_wr_hdr {
FW_MEMTYPE_CF_EXTMEM = 0x2,
FW_MEMTYPE_CF_FLASH = 0x4,
FW_MEMTYPE_CF_INTERNAL = 0x5,
+ FW_MEMTYPE_CF_EXTMEM1 = 0x6,
};
struct fw_caps_config_cmd {
FW_PARAMS_MNEM_PFVF = 2, /* function params */
FW_PARAMS_MNEM_REG = 3, /* limited register access */
FW_PARAMS_MNEM_DMAQ = 4, /* dma queue params */
+ FW_PARAMS_MNEM_CHNET = 5, /* chnet params */
FW_PARAMS_MNEM_LAST
};
FW_DEVLOG_FACILITY_FCOE = 0x2E,
FW_DEVLOG_FACILITY_FOISCSI = 0x30,
FW_DEVLOG_FACILITY_FOFCOE = 0x32,
- FW_DEVLOG_FACILITY_MAX = 0x32,
+ FW_DEVLOG_FACILITY_CHNET = 0x34,
+ FW_DEVLOG_FACILITY_MAX = 0x34,
};
/* log message format */
(((x) >> FW_DEVLOG_CMD_MEMADDR16_DEVLOG_S) & \
FW_DEVLOG_CMD_MEMADDR16_DEVLOG_M)
+/* P C I E F W P F 7 R E G I S T E R */
+
+/* PF7 stores the Firmware Device Log parameters which allows Host Drivers to
+ * access the "devlog" which needing to contact firmware. The encoding is
+ * mostly the same as that returned by the DEVLOG command except for the size
+ * which is encoded as the number of entries in multiples-1 of 128 here rather
+ * than the memory size as is done in the DEVLOG command. Thus, 0 means 128
+ * and 15 means 2048. This of course in turn constrains the allowed values
+ * for the devlog size ...
+ */
+#define PCIE_FW_PF_DEVLOG 7
+
+#define PCIE_FW_PF_DEVLOG_NENTRIES128_S 28
+#define PCIE_FW_PF_DEVLOG_NENTRIES128_M 0xf
+#define PCIE_FW_PF_DEVLOG_NENTRIES128_V(x) \
+ ((x) << PCIE_FW_PF_DEVLOG_NENTRIES128_S)
+#define PCIE_FW_PF_DEVLOG_NENTRIES128_G(x) \
+ (((x) >> PCIE_FW_PF_DEVLOG_NENTRIES128_S) & \
+ PCIE_FW_PF_DEVLOG_NENTRIES128_M)
+
+#define PCIE_FW_PF_DEVLOG_ADDR16_S 4
+#define PCIE_FW_PF_DEVLOG_ADDR16_M 0xffffff
+#define PCIE_FW_PF_DEVLOG_ADDR16_V(x) ((x) << PCIE_FW_PF_DEVLOG_ADDR16_S)
+#define PCIE_FW_PF_DEVLOG_ADDR16_G(x) \
+ (((x) >> PCIE_FW_PF_DEVLOG_ADDR16_S) & PCIE_FW_PF_DEVLOG_ADDR16_M)
+
+#define PCIE_FW_PF_DEVLOG_MEMTYPE_S 0
+#define PCIE_FW_PF_DEVLOG_MEMTYPE_M 0xf
+#define PCIE_FW_PF_DEVLOG_MEMTYPE_V(x) ((x) << PCIE_FW_PF_DEVLOG_MEMTYPE_S)
+#define PCIE_FW_PF_DEVLOG_MEMTYPE_G(x) \
+ (((x) >> PCIE_FW_PF_DEVLOG_MEMTYPE_S) & PCIE_FW_PF_DEVLOG_MEMTYPE_M)
+
#endif /* _T4FW_INTERFACE_H_ */
#define __T4FW_VERSION_H__
#define T4FW_VERSION_MAJOR 0x01
-#define T4FW_VERSION_MINOR 0x0C
-#define T4FW_VERSION_MICRO 0x19
+#define T4FW_VERSION_MINOR 0x0D
+#define T4FW_VERSION_MICRO 0x20
#define T4FW_VERSION_BUILD 0x00
#define T5FW_VERSION_MAJOR 0x01
-#define T5FW_VERSION_MINOR 0x0C
-#define T5FW_VERSION_MICRO 0x19
+#define T5FW_VERSION_MINOR 0x0D
+#define T5FW_VERSION_MICRO 0x20
#define T5FW_VERSION_BUILD 0x00
#endif
? (tq->pidx - 1)
: (tq->size - 1));
__be64 *src = (__be64 *)&tq->desc[index];
- __be64 __iomem *dst = (__be64 *)(tq->bar2_addr +
+ __be64 __iomem *dst = (__be64 __iomem *)(tq->bar2_addr +
SGE_UDB_WCDOORBELL);
unsigned int count = EQ_UNIT / sizeof(__be64);
* DMA.
*/
while (count) {
- writeq(*src, dst);
+ /* the (__force u64) is because the compiler
+ * doesn't understand the endian swizzling
+ * going on
+ */
+ writeq((__force u64)*src, dst);
src++;
dst++;
count--;
BUG_ON(DIV_ROUND_UP(ETHTXQ_MAX_HDR, TXD_PER_EQ_UNIT) > 1);
wr = (void *)&txq->q.desc[txq->q.pidx];
wr->equiq_to_len16 = cpu_to_be32(wr_mid);
- wr->r3[0] = cpu_to_be64(0);
- wr->r3[1] = cpu_to_be64(0);
+ wr->r3[0] = cpu_to_be32(0);
+ wr->r3[1] = cpu_to_be32(0);
skb_copy_from_linear_data(skb, (void *)wr->ethmacdst, fw_hdr_copy_len);
end = (u64 *)wr + flits;
if (rpl) {
/* request bit in high-order BE word */
- WARN_ON((be32_to_cpu(*(const u32 *)cmd)
+ WARN_ON((be32_to_cpu(*(const __be32 *)cmd)
& FW_CMD_REQUEST_F) == 0);
get_mbox_rpl(adapter, rpl, size, mbox_data);
- WARN_ON((be32_to_cpu(*(u32 *)rpl)
+ WARN_ON((be32_to_cpu(*(__be32 *)rpl)
& FW_CMD_REQUEST_F) != 0);
}
t4_write_reg(adapter, mbox_ctl,
* o The BAR2 Queue ID.
* o The BAR2 Queue ID Offset into the BAR2 page.
*/
- bar2_page_offset = ((qid >> qpp_shift) << page_shift);
+ bar2_page_offset = ((u64)(qid >> qpp_shift) << page_shift);
bar2_qid = qid & qpp_mask;
bar2_qid_offset = bar2_qid * SGE_UDB_SIZE;
}
if (ENIC_TEST_INTR(pba, notify_intr)) {
- vnic_intr_return_all_credits(&enic->intr[notify_intr]);
enic_notify_check(enic);
+ vnic_intr_return_all_credits(&enic->intr[notify_intr]);
}
if (ENIC_TEST_INTR(pba, err_intr)) {
struct enic *enic = data;
unsigned int intr = enic_msix_notify_intr(enic);
- vnic_intr_return_all_credits(&enic->intr[intr]);
enic_notify_check(enic);
+ vnic_intr_return_all_credits(&enic->intr[intr]);
return IRQ_HANDLED;
}
(unsigned int)tp->rx_ring[i].buffer1,
(unsigned int)tp->rx_ring[i].buffer2,
buf[0], buf[1], buf[2]);
- for (j = 0; buf[j] != 0xee && j < 1600; j++)
+ for (j = 0; ((j < 1600) && buf[j] != 0xee); j++)
if (j < 100)
pr_cont(" %02x", buf[j]);
pr_cont(" j=%d\n", j);
u16 vlan_tag;
u32 tx_rate;
u32 plink_tracking;
+ u32 privileges;
};
enum vf_state {
u8 __iomem *csr; /* CSR BAR used only for BE2/3 */
u8 __iomem *db; /* Door Bell */
+ u8 __iomem *pcicfg; /* On SH,BEx only. Shadow of PCI config space */
struct mutex mbox_lock; /* For serializing mbox cmds to BE card */
struct be_dma_mem mbox_mem;
{
int num_eqs, i = 0;
- if (lancer_chip(adapter) && num > 8) {
- while (num) {
- num_eqs = min(num, 8);
- __be_cmd_modify_eqd(adapter, &set_eqd[i], num_eqs);
- i += num_eqs;
- num -= num_eqs;
- }
- } else {
- __be_cmd_modify_eqd(adapter, set_eqd, num);
+ while (num) {
+ num_eqs = min(num, 8);
+ __be_cmd_modify_eqd(adapter, &set_eqd[i], num_eqs);
+ i += num_eqs;
+ num -= num_eqs;
}
return 0;
/* Uses sycnhronous mcc */
int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, u16 *vtag_array,
- u32 num)
+ u32 num, u32 domain)
{
struct be_mcc_wrb *wrb;
struct be_cmd_req_vlan_config *req;
be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
OPCODE_COMMON_NTWK_VLAN_CONFIG, sizeof(*req),
wrb, NULL);
+ req->hdr.domain = domain;
req->interface_id = if_id;
req->untagged = BE_IF_FLAGS_UNTAGGED & be_if_cap_flags(adapter) ? 1 : 0;
int be_cmd_get_fw_ver(struct be_adapter *adapter);
int be_cmd_modify_eqd(struct be_adapter *adapter, struct be_set_eqd *, int num);
int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, u16 *vtag_array,
- u32 num);
+ u32 num, u32 domain);
int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 status);
int be_cmd_set_flow_control(struct be_adapter *adapter, u32 tx_fc, u32 rx_fc);
int be_cmd_get_flow_control(struct be_adapter *adapter, u32 *tx_fc, u32 *rx_fc);
for_each_set_bit(i, adapter->vids, VLAN_N_VID)
vids[num++] = cpu_to_le16(i);
- status = be_cmd_vlan_config(adapter, adapter->if_handle, vids, num);
+ status = be_cmd_vlan_config(adapter, adapter->if_handle, vids, num, 0);
if (status) {
dev_err(dev, "Setting HW VLAN filtering failed\n");
/* Set to VLAN promisc mode as setting VLAN filter failed */
return 0;
}
+static int be_set_vf_tvt(struct be_adapter *adapter, int vf, u16 vlan)
+{
+ struct be_vf_cfg *vf_cfg = &adapter->vf_cfg[vf];
+ u16 vids[BE_NUM_VLANS_SUPPORTED];
+ int vf_if_id = vf_cfg->if_handle;
+ int status;
+
+ /* Enable Transparent VLAN Tagging */
+ status = be_cmd_set_hsw_config(adapter, vlan, vf + 1, vf_if_id, 0);
+ if (status)
+ return status;
+
+ /* Clear pre-programmed VLAN filters on VF if any, if TVT is enabled */
+ vids[0] = 0;
+ status = be_cmd_vlan_config(adapter, vf_if_id, vids, 1, vf + 1);
+ if (!status)
+ dev_info(&adapter->pdev->dev,
+ "Cleared guest VLANs on VF%d", vf);
+
+ /* After TVT is enabled, disallow VFs to program VLAN filters */
+ if (vf_cfg->privileges & BE_PRIV_FILTMGMT) {
+ status = be_cmd_set_fn_privileges(adapter, vf_cfg->privileges &
+ ~BE_PRIV_FILTMGMT, vf + 1);
+ if (!status)
+ vf_cfg->privileges &= ~BE_PRIV_FILTMGMT;
+ }
+ return 0;
+}
+
+static int be_clear_vf_tvt(struct be_adapter *adapter, int vf)
+{
+ struct be_vf_cfg *vf_cfg = &adapter->vf_cfg[vf];
+ struct device *dev = &adapter->pdev->dev;
+ int status;
+
+ /* Reset Transparent VLAN Tagging. */
+ status = be_cmd_set_hsw_config(adapter, BE_RESET_VLAN_TAG_ID, vf + 1,
+ vf_cfg->if_handle, 0);
+ if (status)
+ return status;
+
+ /* Allow VFs to program VLAN filtering */
+ if (!(vf_cfg->privileges & BE_PRIV_FILTMGMT)) {
+ status = be_cmd_set_fn_privileges(adapter, vf_cfg->privileges |
+ BE_PRIV_FILTMGMT, vf + 1);
+ if (!status) {
+ vf_cfg->privileges |= BE_PRIV_FILTMGMT;
+ dev_info(dev, "VF%d: FILTMGMT priv enabled", vf);
+ }
+ }
+
+ dev_info(dev,
+ "Disable/re-enable i/f in VM to clear Transparent VLAN tag");
+ return 0;
+}
+
static int be_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos)
{
struct be_adapter *adapter = netdev_priv(netdev);
struct be_vf_cfg *vf_cfg = &adapter->vf_cfg[vf];
- int status = 0;
+ int status;
if (!sriov_enabled(adapter))
return -EPERM;
if (vlan || qos) {
vlan |= qos << VLAN_PRIO_SHIFT;
- if (vf_cfg->vlan_tag != vlan)
- status = be_cmd_set_hsw_config(adapter, vlan, vf + 1,
- vf_cfg->if_handle, 0);
+ status = be_set_vf_tvt(adapter, vf, vlan);
} else {
- /* Reset Transparent Vlan Tagging. */
- status = be_cmd_set_hsw_config(adapter, BE_RESET_VLAN_TAG_ID,
- vf + 1, vf_cfg->if_handle, 0);
+ status = be_clear_vf_tvt(adapter, vf);
}
if (status) {
dev_err(&adapter->pdev->dev,
- "VLAN %d config on VF %d failed : %#x\n", vlan,
- vf, status);
+ "VLAN %d config on VF %d failed : %#x\n", vlan, vf,
+ status);
return be_cmd_status(status);
}
vf_cfg->vlan_tag = vlan;
-
return 0;
}
}
}
} else {
- pci_read_config_dword(adapter->pdev,
- PCICFG_UE_STATUS_LOW, &ue_lo);
- pci_read_config_dword(adapter->pdev,
- PCICFG_UE_STATUS_HIGH, &ue_hi);
- pci_read_config_dword(adapter->pdev,
- PCICFG_UE_STATUS_LOW_MASK, &ue_lo_mask);
- pci_read_config_dword(adapter->pdev,
- PCICFG_UE_STATUS_HI_MASK, &ue_hi_mask);
+ ue_lo = ioread32(adapter->pcicfg + PCICFG_UE_STATUS_LOW);
+ ue_hi = ioread32(adapter->pcicfg + PCICFG_UE_STATUS_HIGH);
+ ue_lo_mask = ioread32(adapter->pcicfg +
+ PCICFG_UE_STATUS_LOW_MASK);
+ ue_hi_mask = ioread32(adapter->pcicfg +
+ PCICFG_UE_STATUS_HI_MASK);
ue_lo = (ue_lo & ~ue_lo_mask);
ue_hi = (ue_hi & ~ue_hi_mask);
u32 cap_flags, u32 vf)
{
u32 en_flags;
- int status;
en_flags = BE_IF_FLAGS_UNTAGGED | BE_IF_FLAGS_BROADCAST |
BE_IF_FLAGS_MULTICAST | BE_IF_FLAGS_PASS_L3L4_ERRORS |
en_flags &= cap_flags;
- status = be_cmd_if_create(adapter, cap_flags, en_flags,
- if_handle, vf);
-
- return status;
+ return be_cmd_if_create(adapter, cap_flags, en_flags, if_handle, vf);
}
static int be_vfs_if_create(struct be_adapter *adapter)
if (!BE3_chip(adapter)) {
status = be_cmd_get_profile_config(adapter, &res,
vf + 1);
- if (!status)
+ if (!status) {
cap_flags = res.if_cap_flags;
+ /* Prevent VFs from enabling VLAN promiscuous
+ * mode
+ */
+ cap_flags &= ~BE_IF_FLAGS_VLAN_PROMISCUOUS;
+ }
}
status = be_if_create(adapter, &vf_cfg->if_handle,
struct device *dev = &adapter->pdev->dev;
struct be_vf_cfg *vf_cfg;
int status, old_vfs, vf;
- u32 privileges;
old_vfs = pci_num_vf(adapter->pdev);
for_all_vfs(adapter, vf_cfg, vf) {
/* Allow VFs to programs MAC/VLAN filters */
- status = be_cmd_get_fn_privileges(adapter, &privileges, vf + 1);
- if (!status && !(privileges & BE_PRIV_FILTMGMT)) {
+ status = be_cmd_get_fn_privileges(adapter, &vf_cfg->privileges,
+ vf + 1);
+ if (!status && !(vf_cfg->privileges & BE_PRIV_FILTMGMT)) {
status = be_cmd_set_fn_privileges(adapter,
- privileges |
+ vf_cfg->privileges |
BE_PRIV_FILTMGMT,
vf + 1);
- if (!status)
+ if (!status) {
+ vf_cfg->privileges |= BE_PRIV_FILTMGMT;
dev_info(dev, "VF%d has FILTMGMT privilege\n",
vf);
+ }
}
/* Allow full available bandwidth */
static int be_map_pci_bars(struct be_adapter *adapter)
{
+ struct pci_dev *pdev = adapter->pdev;
u8 __iomem *addr;
if (BEx_chip(adapter) && be_physfn(adapter)) {
- adapter->csr = pci_iomap(adapter->pdev, 2, 0);
+ adapter->csr = pci_iomap(pdev, 2, 0);
if (!adapter->csr)
return -ENOMEM;
}
- addr = pci_iomap(adapter->pdev, db_bar(adapter), 0);
+ addr = pci_iomap(pdev, db_bar(adapter), 0);
if (!addr)
goto pci_map_err;
adapter->db = addr;
+ if (skyhawk_chip(adapter) || BEx_chip(adapter)) {
+ if (be_physfn(adapter)) {
+ /* PCICFG is the 2nd BAR in BE2 */
+ addr = pci_iomap(pdev, BE2_chip(adapter) ? 1 : 0, 0);
+ if (!addr)
+ goto pci_map_err;
+ adapter->pcicfg = addr;
+ } else {
+ adapter->pcicfg = adapter->db + SRIOV_VF_PCICFG_OFFSET;
+ }
+ }
+
be_roce_map_pci_bars(adapter);
return 0;
pci_map_err:
- dev_err(&adapter->pdev->dev, "Error in mapping PCI BARs\n");
+ dev_err(&pdev->dev, "Error in mapping PCI BARs\n");
be_unmap_pci_bars(adapter);
return -ENOMEM;
}
fec_enet_tx_queue(struct net_device *ndev, u16 queue_id)
{
struct fec_enet_private *fep;
- struct bufdesc *bdp, *bdp_t;
+ struct bufdesc *bdp;
unsigned short status;
struct sk_buff *skb;
struct fec_enet_priv_tx_q *txq;
struct netdev_queue *nq;
int index = 0;
- int i, bdnum;
int entries_free;
fep = netdev_priv(ndev);
if (bdp == txq->cur_tx)
break;
- bdp_t = bdp;
- bdnum = 1;
- index = fec_enet_get_bd_index(txq->tx_bd_base, bdp_t, fep);
- skb = txq->tx_skbuff[index];
- while (!skb) {
- bdp_t = fec_enet_get_nextdesc(bdp_t, fep, queue_id);
- index = fec_enet_get_bd_index(txq->tx_bd_base, bdp_t, fep);
- skb = txq->tx_skbuff[index];
- bdnum++;
- }
- if (skb_shinfo(skb)->nr_frags &&
- (status = bdp_t->cbd_sc) & BD_ENET_TX_READY)
- break;
+ index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep);
- for (i = 0; i < bdnum; i++) {
- if (!IS_TSO_HEADER(txq, bdp->cbd_bufaddr))
- dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
- bdp->cbd_datlen, DMA_TO_DEVICE);
- bdp->cbd_bufaddr = 0;
- if (i < bdnum - 1)
- bdp = fec_enet_get_nextdesc(bdp, fep, queue_id);
- }
+ skb = txq->tx_skbuff[index];
txq->tx_skbuff[index] = NULL;
+ if (!IS_TSO_HEADER(txq, bdp->cbd_bufaddr))
+ dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr,
+ bdp->cbd_datlen, DMA_TO_DEVICE);
+ bdp->cbd_bufaddr = 0;
+ if (!skb) {
+ bdp = fec_enet_get_nextdesc(bdp, fep, queue_id);
+ continue;
+ }
/* Check for errors. */
if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC |
vlan_packet_rcvd = true;
- skb_copy_to_linear_data_offset(skb, VLAN_HLEN,
- data, (2 * ETH_ALEN));
+ memmove(skb->data + VLAN_HLEN, data, ETH_ALEN * 2);
skb_pull(skb, VLAN_HLEN);
}
writel(int_events, fep->hwp + FEC_IEVENT);
fec_enet_collect_events(fep, int_events);
- if (fep->work_tx || fep->work_rx) {
+ if ((fep->work_tx || fep->work_rx) && fep->link) {
ret = IRQ_HANDLED;
if (napi_schedule_prep(&fep->napi)) {
struct fec_enet_private *fep = netdev_priv(ndev);
struct device_node *node;
int err = -ENXIO, i;
+ u32 mii_speed, holdtime;
/*
* The i.MX28 dual fec interfaces are not equal.
* Reference Manual has an error on this, and gets fixed on i.MX6Q
* document.
*/
- fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 5000000);
+ mii_speed = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 5000000);
if (fep->quirks & FEC_QUIRK_ENET_MAC)
- fep->phy_speed--;
- fep->phy_speed <<= 1;
+ mii_speed--;
+ if (mii_speed > 63) {
+ dev_err(&pdev->dev,
+ "fec clock (%lu) to fast to get right mii speed\n",
+ clk_get_rate(fep->clk_ipg));
+ err = -EINVAL;
+ goto err_out;
+ }
+
+ /*
+ * The i.MX28 and i.MX6 types have another filed in the MSCR (aka
+ * MII_SPEED) register that defines the MDIO output hold time. Earlier
+ * versions are RAZ there, so just ignore the difference and write the
+ * register always.
+ * The minimal hold time according to IEE802.3 (clause 22) is 10 ns.
+ * HOLDTIME + 1 is the number of clk cycles the fec is holding the
+ * output.
+ * The HOLDTIME bitfield takes values between 0 and 7 (inclusive).
+ * Given that ceil(clkrate / 5000000) <= 64, the calculation for
+ * holdtime cannot result in a value greater than 3.
+ */
+ holdtime = DIV_ROUND_UP(clk_get_rate(fep->clk_ipg), 100000000) - 1;
+
+ fep->phy_speed = mii_speed << 1 | holdtime << 8;
+
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
fep->mii_bus = mdiobus_alloc();
regulator_disable(fep->reg_phy);
if (fep->ptp_clock)
ptp_clock_unregister(fep->ptp_clock);
- fec_enet_clk_enable(ndev, false);
of_node_put(fep->phy_node);
free_netdev(ndev);
return 0;
}
+static int gfar_of_group_count(struct device_node *np)
+{
+ struct device_node *child;
+ int num = 0;
+
+ for_each_available_child_of_node(np, child)
+ if (!of_node_cmp(child->name, "queue-group"))
+ num++;
+
+ return num;
+}
+
static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
{
const char *model;
num_rx_qs = 1;
} else { /* MQ_MG_MODE */
/* get the actual number of supported groups */
- unsigned int num_grps = of_get_available_child_count(np);
+ unsigned int num_grps = gfar_of_group_count(np);
if (num_grps == 0 || num_grps > MAXGROUPS) {
dev_err(&ofdev->dev, "Invalid # of int groups(%d)\n",
/* Parse and initialize group specific information */
if (priv->mode == MQ_MG_MODE) {
- for_each_child_of_node(np, child) {
+ for_each_available_child_of_node(np, child) {
+ if (of_node_cmp(child->name, "queue-group"))
+ continue;
+
err = gfar_parse_group(child, priv, model);
if (err)
goto err_grp_init;
struct phy_device *phydev = priv->phydev;
if (unlikely(phydev->link != priv->oldlink ||
- phydev->duplex != priv->oldduplex ||
- phydev->speed != priv->oldspeed))
+ (phydev->link && (phydev->duplex != priv->oldduplex ||
+ phydev->speed != priv->oldspeed))))
gfar_update_link_state(priv);
}
ugeth->phy_interface = phy_interface;
ugeth->max_speed = max_speed;
+ /* Carrier starts down, phylib will bring it up */
+ netif_carrier_off(dev);
+
err = register_netdev(dev);
if (err) {
if (netif_msg_probe(ugeth))
device_remove_file(&dev->dev, &dev_attr_remove_port);
}
+static int ehea_reboot_notifier(struct notifier_block *nb,
+ unsigned long action, void *unused)
+{
+ if (action == SYS_RESTART) {
+ pr_info("Reboot: freeing all eHEA resources\n");
+ ibmebus_unregister_driver(&ehea_driver);
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block ehea_reboot_nb = {
+ .notifier_call = ehea_reboot_notifier,
+};
+
+static int ehea_mem_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ int ret = NOTIFY_BAD;
+ struct memory_notify *arg = data;
+
+ mutex_lock(&dlpar_mem_lock);
+
+ switch (action) {
+ case MEM_CANCEL_OFFLINE:
+ pr_info("memory offlining canceled");
+ /* Fall through: re-add canceled memory block */
+
+ case MEM_ONLINE:
+ pr_info("memory is going online");
+ set_bit(__EHEA_STOP_XFER, &ehea_driver_flags);
+ if (ehea_add_sect_bmap(arg->start_pfn, arg->nr_pages))
+ goto out_unlock;
+ ehea_rereg_mrs();
+ break;
+
+ case MEM_GOING_OFFLINE:
+ pr_info("memory is going offline");
+ set_bit(__EHEA_STOP_XFER, &ehea_driver_flags);
+ if (ehea_rem_sect_bmap(arg->start_pfn, arg->nr_pages))
+ goto out_unlock;
+ ehea_rereg_mrs();
+ break;
+
+ default:
+ break;
+ }
+
+ ehea_update_firmware_handles();
+ ret = NOTIFY_OK;
+
+out_unlock:
+ mutex_unlock(&dlpar_mem_lock);
+ return ret;
+}
+
+static struct notifier_block ehea_mem_nb = {
+ .notifier_call = ehea_mem_notifier,
+};
+
+static void ehea_crash_handler(void)
+{
+ int i;
+
+ if (ehea_fw_handles.arr)
+ for (i = 0; i < ehea_fw_handles.num_entries; i++)
+ ehea_h_free_resource(ehea_fw_handles.arr[i].adh,
+ ehea_fw_handles.arr[i].fwh,
+ FORCE_FREE);
+
+ if (ehea_bcmc_regs.arr)
+ for (i = 0; i < ehea_bcmc_regs.num_entries; i++)
+ ehea_h_reg_dereg_bcmc(ehea_bcmc_regs.arr[i].adh,
+ ehea_bcmc_regs.arr[i].port_id,
+ ehea_bcmc_regs.arr[i].reg_type,
+ ehea_bcmc_regs.arr[i].macaddr,
+ 0, H_DEREG_BCMC);
+}
+
+static atomic_t ehea_memory_hooks_registered;
+
+/* Register memory hooks on probe of first adapter */
+static int ehea_register_memory_hooks(void)
+{
+ int ret = 0;
+
+ if (atomic_inc_and_test(&ehea_memory_hooks_registered))
+ return 0;
+
+ ret = ehea_create_busmap();
+ if (ret) {
+ pr_info("ehea_create_busmap failed\n");
+ goto out;
+ }
+
+ ret = register_reboot_notifier(&ehea_reboot_nb);
+ if (ret) {
+ pr_info("register_reboot_notifier failed\n");
+ goto out;
+ }
+
+ ret = register_memory_notifier(&ehea_mem_nb);
+ if (ret) {
+ pr_info("register_memory_notifier failed\n");
+ goto out2;
+ }
+
+ ret = crash_shutdown_register(ehea_crash_handler);
+ if (ret) {
+ pr_info("crash_shutdown_register failed\n");
+ goto out3;
+ }
+
+ return 0;
+
+out3:
+ unregister_memory_notifier(&ehea_mem_nb);
+out2:
+ unregister_reboot_notifier(&ehea_reboot_nb);
+out:
+ return ret;
+}
+
+static void ehea_unregister_memory_hooks(void)
+{
+ if (atomic_read(&ehea_memory_hooks_registered))
+ return;
+
+ unregister_reboot_notifier(&ehea_reboot_nb);
+ if (crash_shutdown_unregister(ehea_crash_handler))
+ pr_info("failed unregistering crash handler\n");
+ unregister_memory_notifier(&ehea_mem_nb);
+}
+
static int ehea_probe_adapter(struct platform_device *dev)
{
struct ehea_adapter *adapter;
int ret;
int i;
+ ret = ehea_register_memory_hooks();
+ if (ret)
+ return ret;
+
if (!dev || !dev->dev.of_node) {
pr_err("Invalid ibmebus device probed\n");
return -EINVAL;
return 0;
}
-static void ehea_crash_handler(void)
-{
- int i;
-
- if (ehea_fw_handles.arr)
- for (i = 0; i < ehea_fw_handles.num_entries; i++)
- ehea_h_free_resource(ehea_fw_handles.arr[i].adh,
- ehea_fw_handles.arr[i].fwh,
- FORCE_FREE);
-
- if (ehea_bcmc_regs.arr)
- for (i = 0; i < ehea_bcmc_regs.num_entries; i++)
- ehea_h_reg_dereg_bcmc(ehea_bcmc_regs.arr[i].adh,
- ehea_bcmc_regs.arr[i].port_id,
- ehea_bcmc_regs.arr[i].reg_type,
- ehea_bcmc_regs.arr[i].macaddr,
- 0, H_DEREG_BCMC);
-}
-
-static int ehea_mem_notifier(struct notifier_block *nb,
- unsigned long action, void *data)
-{
- int ret = NOTIFY_BAD;
- struct memory_notify *arg = data;
-
- mutex_lock(&dlpar_mem_lock);
-
- switch (action) {
- case MEM_CANCEL_OFFLINE:
- pr_info("memory offlining canceled");
- /* Readd canceled memory block */
- case MEM_ONLINE:
- pr_info("memory is going online");
- set_bit(__EHEA_STOP_XFER, &ehea_driver_flags);
- if (ehea_add_sect_bmap(arg->start_pfn, arg->nr_pages))
- goto out_unlock;
- ehea_rereg_mrs();
- break;
- case MEM_GOING_OFFLINE:
- pr_info("memory is going offline");
- set_bit(__EHEA_STOP_XFER, &ehea_driver_flags);
- if (ehea_rem_sect_bmap(arg->start_pfn, arg->nr_pages))
- goto out_unlock;
- ehea_rereg_mrs();
- break;
- default:
- break;
- }
-
- ehea_update_firmware_handles();
- ret = NOTIFY_OK;
-
-out_unlock:
- mutex_unlock(&dlpar_mem_lock);
- return ret;
-}
-
-static struct notifier_block ehea_mem_nb = {
- .notifier_call = ehea_mem_notifier,
-};
-
-static int ehea_reboot_notifier(struct notifier_block *nb,
- unsigned long action, void *unused)
-{
- if (action == SYS_RESTART) {
- pr_info("Reboot: freeing all eHEA resources\n");
- ibmebus_unregister_driver(&ehea_driver);
- }
- return NOTIFY_DONE;
-}
-
-static struct notifier_block ehea_reboot_nb = {
- .notifier_call = ehea_reboot_notifier,
-};
-
static int check_module_parm(void)
{
int ret = 0;
if (ret)
goto out;
- ret = ehea_create_busmap();
- if (ret)
- goto out;
-
- ret = register_reboot_notifier(&ehea_reboot_nb);
- if (ret)
- pr_info("failed registering reboot notifier\n");
-
- ret = register_memory_notifier(&ehea_mem_nb);
- if (ret)
- pr_info("failed registering memory remove notifier\n");
-
- ret = crash_shutdown_register(ehea_crash_handler);
- if (ret)
- pr_info("failed registering crash handler\n");
-
ret = ibmebus_register_driver(&ehea_driver);
if (ret) {
pr_err("failed registering eHEA device driver on ebus\n");
- goto out2;
+ goto out;
}
ret = driver_create_file(&ehea_driver.driver,
if (ret) {
pr_err("failed to register capabilities attribute, ret=%d\n",
ret);
- goto out3;
+ goto out2;
}
return ret;
-out3:
- ibmebus_unregister_driver(&ehea_driver);
out2:
- unregister_memory_notifier(&ehea_mem_nb);
- unregister_reboot_notifier(&ehea_reboot_nb);
- crash_shutdown_unregister(ehea_crash_handler);
+ ibmebus_unregister_driver(&ehea_driver);
out:
return ret;
}
static void __exit ehea_module_exit(void)
{
- int ret;
-
driver_remove_file(&ehea_driver.driver, &driver_attr_capabilities);
ibmebus_unregister_driver(&ehea_driver);
- unregister_reboot_notifier(&ehea_reboot_nb);
- ret = crash_shutdown_unregister(ehea_crash_handler);
- if (ret)
- pr_info("failed unregistering crash handler\n");
- unregister_memory_notifier(&ehea_mem_nb);
+ ehea_unregister_memory_hooks();
kfree(ehea_fw_handles.arr);
kfree(ehea_bcmc_regs.arr);
ehea_destroy_busmap();
ibmveth_replenish_task(adapter);
if (frames_processed < budget) {
+ napi_complete(napi);
+
/* We think we are done - reenable interrupts,
* then check once more to make sure we are done.
*/
BUG_ON(lpar_rc != H_SUCCESS);
- napi_complete(napi);
-
if (ibmveth_rxq_pending_buffer(adapter) &&
napi_reschedule(napi)) {
lpar_rc = h_vio_signal(adapter->vdev->unit_address,
return ret;
}
+static int ibmveth_set_mac_addr(struct net_device *dev, void *p)
+{
+ struct ibmveth_adapter *adapter = netdev_priv(dev);
+ struct sockaddr *addr = p;
+ u64 mac_address;
+ int rc;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ mac_address = ibmveth_encode_mac_addr(addr->sa_data);
+ rc = h_change_logical_lan_mac(adapter->vdev->unit_address, mac_address);
+ if (rc) {
+ netdev_err(adapter->netdev, "h_change_logical_lan_mac failed with rc=%d\n", rc);
+ return rc;
+ }
+
+ ether_addr_copy(dev->dev_addr, addr->sa_data);
+
+ return 0;
+}
+
static const struct net_device_ops ibmveth_netdev_ops = {
.ndo_open = ibmveth_open,
.ndo_stop = ibmveth_close,
.ndo_fix_features = ibmveth_fix_features,
.ndo_set_features = ibmveth_set_features,
.ndo_validate_addr = eth_validate_addr,
- .ndo_set_mac_address = eth_mac_addr,
+ .ndo_set_mac_address = ibmveth_set_mac_addr,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = ibmveth_poll_controller,
#endif
* The grst delay value is in 100ms units, and we'll wait a
* couple counts longer to be sure we don't just miss the end.
*/
- grst_del = rd32(hw, I40E_GLGEN_RSTCTL) & I40E_GLGEN_RSTCTL_GRSTDEL_MASK
- >> I40E_GLGEN_RSTCTL_GRSTDEL_SHIFT;
+ grst_del = (rd32(hw, I40E_GLGEN_RSTCTL) &
+ I40E_GLGEN_RSTCTL_GRSTDEL_MASK) >>
+ I40E_GLGEN_RSTCTL_GRSTDEL_SHIFT;
for (cnt = 0; cnt < grst_del + 2; cnt++) {
reg = rd32(hw, I40E_GLGEN_RSTAT);
if (!(reg & I40E_GLGEN_RSTAT_DEVSTATE_MASK))
status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details);
- if (!status)
+ if (!status && filter_index)
*filter_index = resp->index;
return status;
u32 val;
val = rd32(hw, I40E_PRTDCB_GENC);
- *delay = (u16)(val & I40E_PRTDCB_GENC_PFCLDA_MASK >>
+ *delay = (u16)((val & I40E_PRTDCB_GENC_PFCLDA_MASK) >>
I40E_PRTDCB_GENC_PFCLDA_SHIFT);
}
if (!cmd_buf)
return count;
bytes_not_copied = copy_from_user(cmd_buf, buffer, count);
- if (bytes_not_copied < 0)
+ if (bytes_not_copied < 0) {
+ kfree(cmd_buf);
return bytes_not_copied;
+ }
if (bytes_not_copied > 0)
count -= bytes_not_copied;
cmd_buf[count] = '\0';
vsi->tc_config.numtc = numtc;
vsi->tc_config.enabled_tc = enabled_tc ? enabled_tc : 1;
/* Number of queues per enabled TC */
- num_tc_qps = vsi->alloc_queue_pairs/numtc;
+ /* In MFP case we can have a much lower count of MSIx
+ * vectors available and so we need to lower the used
+ * q count.
+ */
+ qcount = min_t(int, vsi->alloc_queue_pairs, pf->num_lan_msix);
+ num_tc_qps = qcount / numtc;
num_tc_qps = min_t(int, num_tc_qps, I40E_MAX_QUEUES_PER_TC);
/* Setup queue offset/count for all TCs for given VSI */
u16 qoffset, qcount;
int i, n;
- if (!(vsi->back->flags & I40E_FLAG_DCB_ENABLED))
- return;
+ if (!(vsi->back->flags & I40E_FLAG_DCB_ENABLED)) {
+ /* Reset the TC information */
+ for (i = 0; i < vsi->num_queue_pairs; i++) {
+ rx_ring = vsi->rx_rings[i];
+ tx_ring = vsi->tx_rings[i];
+ rx_ring->dcb_tc = 0;
+ tx_ring->dcb_tc = 0;
+ }
+ }
for (n = 0; n < I40E_MAX_TRAFFIC_CLASS; n++) {
if (!(vsi->tc_config.enabled_tc & (1 << n)))
{
int i;
+ i40e_stop_misc_vector(pf);
+ if (pf->flags & I40E_FLAG_MSIX_ENABLED) {
+ synchronize_irq(pf->msix_entries[0].vector);
+ free_irq(pf->msix_entries[0].vector, pf);
+ }
+
i40e_put_lump(pf->irq_pile, 0, I40E_PILE_VALID_BIT-1);
for (i = 0; i < pf->num_alloc_vsi; i++)
if (pf->vsi[i])
/* Wait for the PF's Tx queues to be disabled */
ret = i40e_pf_wait_txq_disabled(pf);
- if (!ret)
+ if (ret) {
+ /* Schedule PF reset to recover */
+ set_bit(__I40E_PF_RESET_REQUESTED, &pf->state);
+ i40e_service_event_schedule(pf);
+ } else {
i40e_pf_unquiesce_all_vsi(pf);
+ }
+
exit:
return ret;
}
int i, v;
/* If we're down or resetting, just bail */
- if (test_bit(__I40E_CONFIG_BUSY, &pf->state))
+ if (test_bit(__I40E_DOWN, &pf->state) ||
+ test_bit(__I40E_CONFIG_BUSY, &pf->state))
return;
/* for each VSI/netdev
set_bit(__I40E_DOWN, &pf->state);
del_timer_sync(&pf->service_timer);
cancel_work_sync(&pf->service_task);
+ i40e_fdir_teardown(pf);
if (pf->flags & I40E_FLAG_SRIOV_ENABLED) {
i40e_free_vfs(pf);
if (pf->vsi[pf->lan_vsi])
i40e_vsi_release(pf->vsi[pf->lan_vsi]);
- i40e_stop_misc_vector(pf);
- if (pf->flags & I40E_FLAG_MSIX_ENABLED) {
- synchronize_irq(pf->msix_entries[0].vector);
- free_irq(pf->msix_entries[0].vector, pf);
- }
-
/* shutdown and destroy the HMC */
if (pf->hw.hmc.hmc_obj) {
ret_code = i40e_shutdown_lan_hmc(&pf->hw);
wr32(hw, I40E_PFPM_APM, (pf->wol_en ? I40E_PFPM_APM_APME_MASK : 0));
wr32(hw, I40E_PFPM_WUFC, (pf->wol_en ? I40E_PFPM_WUFC_MAG_MASK : 0));
+ i40e_clear_interrupt_scheme(pf);
+
if (system_state == SYSTEM_POWER_OFF) {
pci_wake_from_d3(pdev, pf->wol_en);
pci_set_power_state(pdev, PCI_D3hot);
{
i40e_status status;
enum i40e_nvmupd_cmd upd_cmd;
+ bool retry_attempt = false;
upd_cmd = i40e_nvmupd_validate_command(hw, cmd, errno);
+retry:
switch (upd_cmd) {
case I40E_NVMUPD_WRITE_CON:
status = i40e_nvmupd_nvm_write(hw, cmd, bytes, errno);
*errno = -ESRCH;
break;
}
+
+ /* In some circumstances, a multi-write transaction takes longer
+ * than the default 3 minute timeout on the write semaphore. If
+ * the write failed with an EBUSY status, this is likely the problem,
+ * so here we try to reacquire the semaphore then retry the write.
+ * We only do one retry, then give up.
+ */
+ if (status && (hw->aq.asq_last_status == I40E_AQ_RC_EBUSY) &&
+ !retry_attempt) {
+ i40e_status old_status = status;
+ u32 old_asq_status = hw->aq.asq_last_status;
+ u32 gtime;
+
+ gtime = rd32(hw, I40E_GLVFGEN_TIMER);
+ if (gtime >= hw->nvm.hw_semaphore_timeout) {
+ i40e_debug(hw, I40E_DEBUG_ALL,
+ "NVMUPD: write semaphore expired (%d >= %lld), retrying\n",
+ gtime, hw->nvm.hw_semaphore_timeout);
+ i40e_release_nvm(hw);
+ status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
+ if (status) {
+ i40e_debug(hw, I40E_DEBUG_ALL,
+ "NVMUPD: write semaphore reacquire failed aq_err = %d\n",
+ hw->aq.asq_last_status);
+ status = old_status;
+ hw->aq.asq_last_status = old_asq_status;
+ } else {
+ retry_attempt = true;
+ goto retry;
+ }
+ }
+ }
+
return status;
}
}
}
+/**
+ * i40e_get_head - Retrieve head from head writeback
+ * @tx_ring: tx ring to fetch head of
+ *
+ * Returns value of Tx ring head based on value stored
+ * in head write-back location
+ **/
+static inline u32 i40e_get_head(struct i40e_ring *tx_ring)
+{
+ void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count;
+
+ return le32_to_cpu(*(volatile __le32 *)head);
+}
+
/**
* i40e_get_tx_pending - how many tx descriptors not processed
* @tx_ring: the ring of descriptors
**/
static u32 i40e_get_tx_pending(struct i40e_ring *ring)
{
- u32 ntu = ((ring->next_to_clean <= ring->next_to_use)
- ? ring->next_to_use
- : ring->next_to_use + ring->count);
- return ntu - ring->next_to_clean;
+ u32 head, tail;
+
+ head = i40e_get_head(ring);
+ tail = readl(ring->tail);
+
+ if (head != tail)
+ return (head < tail) ?
+ tail - head : (tail + ring->count - head);
+
+ return 0;
}
/**
**/
static bool i40e_check_tx_hang(struct i40e_ring *tx_ring)
{
+ u32 tx_done = tx_ring->stats.packets;
+ u32 tx_done_old = tx_ring->tx_stats.tx_done_old;
u32 tx_pending = i40e_get_tx_pending(tx_ring);
struct i40e_pf *pf = tx_ring->vsi->back;
bool ret = false;
* run the check_tx_hang logic with a transmit completion
* pending but without time to complete it yet.
*/
- if ((tx_ring->tx_stats.tx_done_old == tx_ring->stats.packets) &&
- (tx_pending >= I40E_MIN_DESC_PENDING)) {
+ if ((tx_done_old == tx_done) && tx_pending) {
/* make sure it is true for two checks in a row */
ret = test_and_set_bit(__I40E_HANG_CHECK_ARMED,
&tx_ring->state);
- } else if ((tx_ring->tx_stats.tx_done_old == tx_ring->stats.packets) &&
- (tx_pending < I40E_MIN_DESC_PENDING) &&
- (tx_pending > 0)) {
+ } else if (tx_done_old == tx_done &&
+ (tx_pending < I40E_MIN_DESC_PENDING) && (tx_pending > 0)) {
if (I40E_DEBUG_FLOW & pf->hw.debug_mask)
dev_info(tx_ring->dev, "HW needs some more descs to do a cacheline flush. tx_pending %d, queue %d",
tx_pending, tx_ring->queue_index);
pf->tx_sluggish_count++;
} else {
/* update completed stats and disarm the hang check */
- tx_ring->tx_stats.tx_done_old = tx_ring->stats.packets;
+ tx_ring->tx_stats.tx_done_old = tx_done;
clear_bit(__I40E_HANG_CHECK_ARMED, &tx_ring->state);
}
return ret;
}
-/**
- * i40e_get_head - Retrieve head from head writeback
- * @tx_ring: tx ring to fetch head of
- *
- * Returns value of Tx ring head based on value stored
- * in head write-back location
- **/
-static inline u32 i40e_get_head(struct i40e_ring *tx_ring)
-{
- void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count;
-
- return le32_to_cpu(*(volatile __le32 *)head);
-}
-
#define WB_STRIDE 0x3
/**
return __i40e_maybe_stop_tx(tx_ring, size);
}
+/**
+ * i40e_chk_linearize - Check if there are more than 8 fragments per packet
+ * @skb: send buffer
+ * @tx_flags: collected send information
+ * @hdr_len: size of the packet header
+ *
+ * Note: Our HW can't scatter-gather more than 8 fragments to build
+ * a packet on the wire and so we need to figure out the cases where we
+ * need to linearize the skb.
+ **/
+static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags,
+ const u8 hdr_len)
+{
+ struct skb_frag_struct *frag;
+ bool linearize = false;
+ unsigned int size = 0;
+ u16 num_frags;
+ u16 gso_segs;
+
+ num_frags = skb_shinfo(skb)->nr_frags;
+ gso_segs = skb_shinfo(skb)->gso_segs;
+
+ if (tx_flags & (I40E_TX_FLAGS_TSO | I40E_TX_FLAGS_FSO)) {
+ u16 j = 1;
+
+ if (num_frags < (I40E_MAX_BUFFER_TXD))
+ goto linearize_chk_done;
+ /* try the simple math, if we have too many frags per segment */
+ if (DIV_ROUND_UP((num_frags + gso_segs), gso_segs) >
+ I40E_MAX_BUFFER_TXD) {
+ linearize = true;
+ goto linearize_chk_done;
+ }
+ frag = &skb_shinfo(skb)->frags[0];
+ size = hdr_len;
+ /* we might still have more fragments per segment */
+ do {
+ size += skb_frag_size(frag);
+ frag++; j++;
+ if (j == I40E_MAX_BUFFER_TXD) {
+ if (size < skb_shinfo(skb)->gso_size) {
+ linearize = true;
+ break;
+ }
+ j = 1;
+ size -= skb_shinfo(skb)->gso_size;
+ if (size)
+ j++;
+ size += hdr_len;
+ }
+ num_frags--;
+ } while (num_frags);
+ } else {
+ if (num_frags >= I40E_MAX_BUFFER_TXD)
+ linearize = true;
+ }
+
+linearize_chk_done:
+ return linearize;
+}
+
/**
* i40e_tx_map - Build the Tx descriptor
* @tx_ring: ring to send buffer on
if (tsyn)
tx_flags |= I40E_TX_FLAGS_TSYN;
+ if (i40e_chk_linearize(skb, tx_flags, hdr_len))
+ if (skb_linearize(skb))
+ goto out_drop;
+
skb_tx_timestamp(skb);
/* always enable CRC insertion offload */
#define i40e_rx_desc i40e_32byte_rx_desc
+#define I40E_MAX_BUFFER_TXD 8
#define I40E_MIN_TX_LEN 17
#define I40E_MAX_DATA_PER_TXD 8192
}
}
+/**
+ * i40e_get_head - Retrieve head from head writeback
+ * @tx_ring: tx ring to fetch head of
+ *
+ * Returns value of Tx ring head based on value stored
+ * in head write-back location
+ **/
+static inline u32 i40e_get_head(struct i40e_ring *tx_ring)
+{
+ void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count;
+
+ return le32_to_cpu(*(volatile __le32 *)head);
+}
+
/**
* i40e_get_tx_pending - how many tx descriptors not processed
* @tx_ring: the ring of descriptors
**/
static u32 i40e_get_tx_pending(struct i40e_ring *ring)
{
- u32 ntu = ((ring->next_to_clean <= ring->next_to_use)
- ? ring->next_to_use
- : ring->next_to_use + ring->count);
- return ntu - ring->next_to_clean;
+ u32 head, tail;
+
+ head = i40e_get_head(ring);
+ tail = readl(ring->tail);
+
+ if (head != tail)
+ return (head < tail) ?
+ tail - head : (tail + ring->count - head);
+
+ return 0;
}
/**
**/
static bool i40e_check_tx_hang(struct i40e_ring *tx_ring)
{
+ u32 tx_done = tx_ring->stats.packets;
+ u32 tx_done_old = tx_ring->tx_stats.tx_done_old;
u32 tx_pending = i40e_get_tx_pending(tx_ring);
bool ret = false;
* run the check_tx_hang logic with a transmit completion
* pending but without time to complete it yet.
*/
- if ((tx_ring->tx_stats.tx_done_old == tx_ring->stats.packets) &&
- (tx_pending >= I40E_MIN_DESC_PENDING)) {
+ if ((tx_done_old == tx_done) && tx_pending) {
/* make sure it is true for two checks in a row */
ret = test_and_set_bit(__I40E_HANG_CHECK_ARMED,
&tx_ring->state);
- } else if (!(tx_ring->tx_stats.tx_done_old == tx_ring->stats.packets) ||
- !(tx_pending < I40E_MIN_DESC_PENDING) ||
- !(tx_pending > 0)) {
+ } else if (tx_done_old == tx_done &&
+ (tx_pending < I40E_MIN_DESC_PENDING) && (tx_pending > 0)) {
/* update completed stats and disarm the hang check */
- tx_ring->tx_stats.tx_done_old = tx_ring->stats.packets;
+ tx_ring->tx_stats.tx_done_old = tx_done;
clear_bit(__I40E_HANG_CHECK_ARMED, &tx_ring->state);
}
return ret;
}
-/**
- * i40e_get_head - Retrieve head from head writeback
- * @tx_ring: tx ring to fetch head of
- *
- * Returns value of Tx ring head based on value stored
- * in head write-back location
- **/
-static inline u32 i40e_get_head(struct i40e_ring *tx_ring)
-{
- void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count;
-
- return le32_to_cpu(*(volatile __le32 *)head);
-}
-
#define WB_STRIDE 0x3
/**
if (err < 0)
return err;
- if (protocol == htons(ETH_P_IP)) {
- iph = skb->encapsulation ? inner_ip_hdr(skb) : ip_hdr(skb);
+ iph = skb->encapsulation ? inner_ip_hdr(skb) : ip_hdr(skb);
+ ipv6h = skb->encapsulation ? inner_ipv6_hdr(skb) : ipv6_hdr(skb);
+
+ if (iph->version == 4) {
tcph = skb->encapsulation ? inner_tcp_hdr(skb) : tcp_hdr(skb);
iph->tot_len = 0;
iph->check = 0;
tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
0, IPPROTO_TCP, 0);
- } else if (skb_is_gso_v6(skb)) {
-
- ipv6h = skb->encapsulation ? inner_ipv6_hdr(skb)
- : ipv6_hdr(skb);
+ } else if (ipv6h->version == 6) {
tcph = skb->encapsulation ? inner_tcp_hdr(skb) : tcp_hdr(skb);
ipv6h->payload_len = 0;
tcph->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
I40E_TX_CTX_EXT_IP_IPV4_NO_CSUM;
}
} else if (tx_flags & I40E_TX_FLAGS_IPV6) {
- if (tx_flags & I40E_TX_FLAGS_TSO) {
- *cd_tunneling |= I40E_TX_CTX_EXT_IP_IPV6;
+ *cd_tunneling |= I40E_TX_CTX_EXT_IP_IPV6;
+ if (tx_flags & I40E_TX_FLAGS_TSO)
ip_hdr(skb)->check = 0;
- } else {
- *cd_tunneling |=
- I40E_TX_CTX_EXT_IP_IPV4_NO_CSUM;
- }
}
/* Now set the ctx descriptor fields */
((skb_inner_network_offset(skb) -
skb_transport_offset(skb)) >> 1) <<
I40E_TXD_CTX_QW0_NATLEN_SHIFT;
+ if (this_ip_hdr->version == 6) {
+ tx_flags &= ~I40E_TX_FLAGS_IPV4;
+ tx_flags |= I40E_TX_FLAGS_IPV6;
+ }
+
} else {
network_hdr_len = skb_network_header_len(skb);
context_desc->type_cmd_tso_mss = cpu_to_le64(cd_type_cmd_tso_mss);
}
+ /**
+ * i40e_chk_linearize - Check if there are more than 8 fragments per packet
+ * @skb: send buffer
+ * @tx_flags: collected send information
+ * @hdr_len: size of the packet header
+ *
+ * Note: Our HW can't scatter-gather more than 8 fragments to build
+ * a packet on the wire and so we need to figure out the cases where we
+ * need to linearize the skb.
+ **/
+static bool i40e_chk_linearize(struct sk_buff *skb, u32 tx_flags,
+ const u8 hdr_len)
+{
+ struct skb_frag_struct *frag;
+ bool linearize = false;
+ unsigned int size = 0;
+ u16 num_frags;
+ u16 gso_segs;
+
+ num_frags = skb_shinfo(skb)->nr_frags;
+ gso_segs = skb_shinfo(skb)->gso_segs;
+
+ if (tx_flags & (I40E_TX_FLAGS_TSO | I40E_TX_FLAGS_FSO)) {
+ u16 j = 1;
+
+ if (num_frags < (I40E_MAX_BUFFER_TXD))
+ goto linearize_chk_done;
+ /* try the simple math, if we have too many frags per segment */
+ if (DIV_ROUND_UP((num_frags + gso_segs), gso_segs) >
+ I40E_MAX_BUFFER_TXD) {
+ linearize = true;
+ goto linearize_chk_done;
+ }
+ frag = &skb_shinfo(skb)->frags[0];
+ size = hdr_len;
+ /* we might still have more fragments per segment */
+ do {
+ size += skb_frag_size(frag);
+ frag++; j++;
+ if (j == I40E_MAX_BUFFER_TXD) {
+ if (size < skb_shinfo(skb)->gso_size) {
+ linearize = true;
+ break;
+ }
+ j = 1;
+ size -= skb_shinfo(skb)->gso_size;
+ if (size)
+ j++;
+ size += hdr_len;
+ }
+ num_frags--;
+ } while (num_frags);
+ } else {
+ if (num_frags >= I40E_MAX_BUFFER_TXD)
+ linearize = true;
+ }
+
+linearize_chk_done:
+ return linearize;
+}
+
/**
* i40e_tx_map - Build the Tx descriptor
* @tx_ring: ring to send buffer on
else if (tso)
tx_flags |= I40E_TX_FLAGS_TSO;
+ if (i40e_chk_linearize(skb, tx_flags, hdr_len))
+ if (skb_linearize(skb))
+ goto out_drop;
+
skb_tx_timestamp(skb);
/* always enable CRC insertion offload */
#define i40e_rx_desc i40e_32byte_rx_desc
+#define I40E_MAX_BUFFER_TXD 8
#define I40E_MIN_TX_LEN 17
#define I40E_MAX_DATA_PER_TXD 8192
static int mvneta_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct mvneta_port *pp = netdev_priv(dev);
- int ret;
if (!pp->phy_dev)
return -ENOTSUPP;
- ret = phy_mii_ioctl(pp->phy_dev, ifr, cmd);
- if (!ret)
- mvneta_adjust_link(dev);
-
- return ret;
+ return phy_mii_ioctl(pp->phy_dev, ifr, cmd);
}
/* Ethtool methods */
* on the host, we deprecate the error message for this
* specific command/input_mod/opcode_mod/fw-status to be debug.
*/
- if (op == MLX4_CMD_SET_PORT && in_modifier == 1 &&
+ if (op == MLX4_CMD_SET_PORT &&
+ (in_modifier == 1 || in_modifier == 2) &&
op_modifier == 0 && context->fw_status == CMD_STAT_BAD_SIZE)
mlx4_dbg(dev, "command 0x%x failed: fw status = 0x%x\n",
op, context->fw_status);
goto reset_slave;
slave_state[slave].vhcr_dma = ((u64) param) << 48;
priv->mfunc.master.slave_state[slave].cookie = 0;
- mutex_init(&priv->mfunc.master.gen_eqe_mutex[slave]);
break;
case MLX4_COMM_CMD_VHCR1:
if (slave_state[slave].last_cmd != MLX4_COMM_CMD_VHCR0)
for (i = 0; i < dev->num_slaves; ++i) {
s_state = &priv->mfunc.master.slave_state[i];
s_state->last_cmd = MLX4_COMM_CMD_RESET;
+ mutex_init(&priv->mfunc.master.gen_eqe_mutex[i]);
for (j = 0; j < MLX4_EVENT_TYPES_NUM; ++j)
s_state->event_eq[j].eqn = -1;
__raw_writel((__force u32) 0,
/* Schedule multicast task to populate multicast list */
queue_work(mdev->workqueue, &priv->rx_mode_task);
- mlx4_set_stats_bitmap(mdev->dev, &priv->stats_bitmap);
-
#ifdef CONFIG_MLX4_EN_VXLAN
if (priv->mdev->dev->caps.tunnel_offload_mode == MLX4_TUNNEL_OFFLOAD_MODE_VXLAN)
vxlan_get_rx_port(dev);
netif_carrier_off(dev);
mlx4_en_set_default_moderation(priv);
- err = register_netdev(dev);
- if (err) {
- en_err(priv, "Netdev registration failed for port %d\n", port);
- goto out;
- }
- priv->registered = 1;
-
en_warn(priv, "Using %d TX rings\n", prof->tx_ring_num);
en_warn(priv, "Using %d RX rings\n", prof->rx_ring_num);
queue_delayed_work(mdev->workqueue, &priv->service_task,
SERVICE_TASK_DELAY);
+ mlx4_set_stats_bitmap(mdev->dev, &priv->stats_bitmap);
+
+ err = register_netdev(dev);
+ if (err) {
+ en_err(priv, "Netdev registration failed for port %d\n", port);
+ goto out;
+ }
+
+ priv->registered = 1;
+
return 0;
out:
{
u32 loopback_ok = 0;
int i;
-
+ bool gro_enabled;
priv->loopback_ok = 0;
priv->validate_loopback = 1;
+ gro_enabled = priv->dev->features & NETIF_F_GRO;
mlx4_en_update_loopback_state(priv->dev, priv->dev->features);
+ priv->dev->features &= ~NETIF_F_GRO;
/* xmit */
if (mlx4_en_test_loopback_xmit(priv)) {
mlx4_en_test_loopback_exit:
priv->validate_loopback = 0;
+
+ if (gro_enabled)
+ priv->dev->features |= NETIF_F_GRO;
+
mlx4_en_update_loopback_state(priv->dev, priv->dev->features);
return !loopback_ok;
}
/* All active slaves need to receive the event */
if (slave == ALL_SLAVES) {
- for (i = 0; i < dev->num_slaves; i++) {
- if (i != dev->caps.function &&
- master->slave_state[i].active)
- if (mlx4_GEN_EQE(dev, i, eqe))
- mlx4_warn(dev, "Failed to generate event for slave %d\n",
- i);
+ for (i = 0; i <= dev->persist->num_vfs; i++) {
+ if (mlx4_GEN_EQE(dev, i, eqe))
+ mlx4_warn(dev, "Failed to generate event for slave %d\n",
+ i);
}
} else {
if (mlx4_GEN_EQE(dev, slave, eqe))
struct mlx4_eqe *eqe)
{
struct mlx4_priv *priv = mlx4_priv(dev);
- struct mlx4_slave_state *s_slave =
- &priv->mfunc.master.slave_state[slave];
- if (!s_slave->active) {
- /*mlx4_warn(dev, "Trying to pass event to inactive slave\n");*/
+ if (slave < 0 || slave > dev->persist->num_vfs ||
+ slave == dev->caps.function ||
+ !priv->mfunc.master.slave_state[slave].active)
return;
- }
slave_event(dev, slave, eqe);
}
unsigned long rx_chksum_none;
unsigned long rx_chksum_complete;
unsigned long tx_chksum_offload;
-#define NUM_PORT_STATS 9
+#define NUM_PORT_STATS 10
};
struct mlx4_en_perf_stats {
EXPORT_SYMBOL_GPL(mlx4_qp_alloc);
-#define MLX4_UPDATE_QP_SUPPORTED_ATTRS MLX4_UPDATE_QP_SMAC
int mlx4_update_qp(struct mlx4_dev *dev, u32 qpn,
enum mlx4_update_qp_attr attr,
struct mlx4_update_qp_params *params)
struct mlx4_vport_oper_state *vp_oper;
struct mlx4_priv *priv;
u32 qp_type;
- int port;
+ int port, err = 0;
port = (qpc->pri_path.sched_queue & 0x40) ? 2 : 1;
priv = mlx4_priv(dev);
} else {
struct mlx4_update_qp_params params = {.flags = 0};
- mlx4_update_qp(dev, qpn, MLX4_UPDATE_QP_VSD, ¶ms);
+ err = mlx4_update_qp(dev, qpn, MLX4_UPDATE_QP_VSD, ¶ms);
+ if (err)
+ goto out;
}
}
qpc->pri_path.feup |= MLX4_FSM_FORCE_ETH_SRC_MAC;
qpc->pri_path.grh_mylmc = (0x80 & qpc->pri_path.grh_mylmc) + vp_oper->mac_idx;
}
- return 0;
+out:
+ return err;
}
static int mpt_mask(struct mlx4_dev *dev)
if (!priv->mfunc.master.slave_state)
return -EINVAL;
+ /* check for slave valid, slave not PF, and slave active */
+ if (slave < 0 || slave > dev->persist->num_vfs ||
+ slave == dev->caps.function ||
+ !priv->mfunc.master.slave_state[slave].active)
+ return 0;
+
event_eq = &priv->mfunc.master.slave_state[slave].event_eq[eqe->type];
/* Create the event only if the slave is registered */
if (mac->phydev)
phy_start(mac->phydev);
- init_timer(&mac->tx->clean_timer);
- mac->tx->clean_timer.function = pasemi_mac_tx_timer;
- mac->tx->clean_timer.data = (unsigned long)mac->tx;
- mac->tx->clean_timer.expires = jiffies+HZ;
- add_timer(&mac->tx->clean_timer);
+ setup_timer(&mac->tx->clean_timer, pasemi_mac_tx_timer,
+ (unsigned long)mac->tx);
+ mod_timer(&mac->tx->clean_timer, jiffies + HZ);
return 0;
} __attribute__ ((aligned(64)));
-/* Note: sizeof(rcv_desc) should always be a mutliple of 2 */
+/* Note: sizeof(rcv_desc) should always be a multiple of 2 */
struct rcv_desc {
__le16 reference_handle;
__le16 reserved;
#define NETXEN_IMAGE_START 0x43000 /* compressed image */
#define NETXEN_SECONDARY_START 0x200000 /* backup images */
#define NETXEN_PXE_START 0x3E0000 /* PXE boot rom */
-#define NETXEN_USER_START 0x3E8000 /* Firmare info */
+#define NETXEN_USER_START 0x3E8000 /* Firmware info */
#define NETXEN_FIXED_START 0x3F0000 /* backup of crbinit */
#define NETXEN_USER_START_OLD NETXEN_PXE_START /* very old flash */
#define QLCNIC_BRDCFG_START 0x4000 /* board config */
#define QLCNIC_BOOTLD_START 0x10000 /* bootld */
#define QLCNIC_IMAGE_START 0x43000 /* compressed image */
-#define QLCNIC_USER_START 0x3E8000 /* Firmare info */
+#define QLCNIC_USER_START 0x3E8000 /* Firmware info */
#define QLCNIC_FW_VERSION_OFFSET (QLCNIC_USER_START+0x408)
#define QLCNIC_FW_SIZE_OFFSET (QLCNIC_USER_START+0x40c)
int rc = -EINVAL;
if (!rtl_fw_format_ok(tp, rtl_fw)) {
- netif_err(tp, ifup, dev, "invalid firwmare\n");
+ netif_err(tp, ifup, dev, "invalid firmware\n");
goto out;
}
RTL_W8(ChipCmd, CmdReset);
rtl_udelay_loop_wait_low(tp, &rtl_chipcmd_cond, 100, 100);
-
- netdev_reset_queue(tp->dev);
}
static void rtl_request_uncached_firmware(struct rtl8169_private *tp)
u32 status, len;
u32 opts[2];
int frags;
- bool stop_queue;
if (unlikely(!TX_FRAGS_READY_FOR(tp, skb_shinfo(skb)->nr_frags))) {
netif_err(tp, drv, dev, "BUG! Tx Ring full when queue awake!\n");
txd->opts2 = cpu_to_le32(opts[1]);
- netdev_sent_queue(dev, skb->len);
-
skb_tx_timestamp(skb);
/* Force memory writes to complete before releasing descriptor */
tp->cur_tx += frags + 1;
- stop_queue = !TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS);
+ RTL_W8(TxPoll, NPQ);
- if (!skb->xmit_more || stop_queue ||
- netif_xmit_stopped(netdev_get_tx_queue(dev, 0))) {
- RTL_W8(TxPoll, NPQ);
-
- mmiowb();
- }
+ mmiowb();
- if (stop_queue) {
+ if (!TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS)) {
/* Avoid wrongly optimistic queue wake-up: rtl_tx thread must
* not miss a ring update when it notices a stopped queue.
*/
static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp)
{
unsigned int dirty_tx, tx_left;
- unsigned int bytes_compl = 0, pkts_compl = 0;
dirty_tx = tp->dirty_tx;
smp_rmb();
rtl8169_unmap_tx_skb(&tp->pci_dev->dev, tx_skb,
tp->TxDescArray + entry);
if (status & LastFrag) {
- pkts_compl++;
- bytes_compl += tx_skb->skb->len;
+ u64_stats_update_begin(&tp->tx_stats.syncp);
+ tp->tx_stats.packets++;
+ tp->tx_stats.bytes += tx_skb->skb->len;
+ u64_stats_update_end(&tp->tx_stats.syncp);
dev_kfree_skb_any(tx_skb->skb);
tx_skb->skb = NULL;
}
}
if (tp->dirty_tx != dirty_tx) {
- netdev_completed_queue(tp->dev, pkts_compl, bytes_compl);
-
- u64_stats_update_begin(&tp->tx_stats.syncp);
- tp->tx_stats.packets += pkts_compl;
- tp->tx_stats.bytes += bytes_compl;
- u64_stats_update_end(&tp->tx_stats.syncp);
-
tp->dirty_tx = dirty_tx;
/* Sync with rtl8169_start_xmit:
* - publish dirty_tx ring index (write barrier)
.tpauser = 1,
.hw_swap = 1,
.rmiimode = 1,
- .shift_rd0 = 1,
};
static void sh_eth_set_rate_sh7724(struct net_device *ndev)
msleep(2); /* max frame time at 10 Mbps < 1250 us */
sh_eth_get_stats(ndev);
sh_eth_reset(ndev);
+
+ /* Set MAC address again */
+ update_mac_address(ndev);
}
/* free Tx skb function */
txdesc = &mdp->tx_ring[entry];
if (txdesc->status & cpu_to_edmac(mdp, TD_TACT))
break;
+ /* TACT bit must be checked before all the following reads */
+ rmb();
/* Free the original skb. */
if (mdp->tx_skbuff[entry]) {
dma_unmap_single(&ndev->dev, txdesc->addr,
limit = boguscnt;
rxdesc = &mdp->rx_ring[entry];
while (!(rxdesc->status & cpu_to_edmac(mdp, RD_RACT))) {
+ /* RACT bit must be checked before all the following reads */
+ rmb();
desc_status = edmac_to_cpu(mdp, rxdesc->status);
pkt_len = rxdesc->frame_length;
/* In case of almost all GETHER/ETHERs, the Receive Frame State
* (RFS) bits in the Receive Descriptor 0 are from bit 9 to
- * bit 0. However, in case of the R8A7740, R8A779x, and
- * R7S72100 the RFS bits are from bit 25 to bit 16. So, the
+ * bit 0. However, in case of the R8A7740 and R7S72100
+ * the RFS bits are from bit 25 to bit 16. So, the
* driver needs right shifting by 16.
*/
if (mdp->cd->shift_rd0)
skb_checksum_none_assert(skb);
rxdesc->addr = dma_addr;
}
+ wmb(); /* RACT bit must be set after all the above writes */
if (entry >= mdp->num_rx_ring - 1)
rxdesc->status |=
cpu_to_edmac(mdp, RD_RACT | RD_RFP | RD_RDEL);
/* If we don't need to check status, don't. -KDU */
if (!(sh_eth_read(ndev, EDRRR) & EDRRR_R)) {
/* fix the values for the next receiving if RDE is set */
- if (intr_status & EESR_RDE) {
+ if (intr_status & EESR_RDE && mdp->reg_offset[RDFAR] != 0) {
u32 count = (sh_eth_read(ndev, RDFAR) -
sh_eth_read(ndev, RDLAR)) >> 4;
}
spin_unlock_irqrestore(&mdp->lock, flags);
- if (skb_padto(skb, ETH_ZLEN))
+ if (skb_put_padto(skb, ETH_ZLEN))
return NETDEV_TX_OK;
entry = mdp->cur_tx % mdp->num_tx_ring;
}
txdesc->buffer_length = skb->len;
+ wmb(); /* TACT bit must be set after all the above writes */
if (entry >= mdp->num_tx_ring - 1)
txdesc->status |= cpu_to_edmac(mdp, TD_TACT | TD_TDLE);
else
u64 val = rocker_read64(rocker_port->rocker, PORT_PHYS_ENABLE);
if (enable)
- val |= 1 << rocker_port->lport;
+ val |= 1ULL << rocker_port->lport;
else
- val &= ~(1 << rocker_port->lport);
+ val &= ~(1ULL << rocker_port->lport);
rocker_write64(rocker_port->rocker, PORT_PHYS_ENABLE, val);
}
alloc_size = sizeof(struct rocker_port *) * rocker->port_count;
rocker->ports = kmalloc(alloc_size, GFP_KERNEL);
+ if (!rocker->ports)
+ return -ENOMEM;
for (i = 0; i < rocker->port_count; i++) {
err = rocker_probe_port(rocker, i);
if (err)
struct net_device *master = netdev_master_upper_dev_get(dev);
int err = 0;
+ /* There are currently three cases handled here:
+ * 1. Joining a bridge
+ * 2. Leaving a previously joined bridge
+ * 3. Other, e.g. being added to or removed from a bond or openvswitch,
+ * in which case nothing is done
+ */
if (master && master->rtnl_link_ops &&
!strcmp(master->rtnl_link_ops->kind, "bridge"))
err = rocker_port_bridge_join(rocker_port, master);
- else
+ else if (rocker_port_is_bridged(rocker_port))
err = rocker_port_bridge_leave(rocker_port);
return err;
smc->packets_waiting = 0;
smc_reset(dev);
- init_timer(&smc->media);
- smc->media.function = media_check;
- smc->media.data = (u_long) dev;
- smc->media.expires = jiffies + HZ;
- add_timer(&smc->media);
+ setup_timer(&smc->media, media_check, (u_long)dev);
+ mod_timer(&smc->media, jiffies + HZ);
return 0;
} /* smc_open */
#include "smc91x.h"
+#if defined(CONFIG_ASSABET_NEPONSET)
+#include <mach/assabet.h>
+#include <mach/neponset.h>
+#endif
+
#ifndef SMC_NOWAIT
# define SMC_NOWAIT 0
#endif
const struct of_device_id *match = NULL;
struct smc_local *lp;
struct net_device *ndev;
- struct resource *res;
+ struct resource *res, *ires;
unsigned int __iomem *addr;
unsigned long irq_flags = SMC_IRQ_FLAGS;
- unsigned long irq_resflags;
int ret;
ndev = alloc_etherdev(sizeof(struct smc_local));
goto out_free_netdev;
}
- ndev->irq = platform_get_irq(pdev, 0);
- if (ndev->irq <= 0) {
+ ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!ires) {
ret = -ENODEV;
goto out_release_io;
}
- /*
- * If this platform does not specify any special irqflags, or if
- * the resource supplies a trigger, override the irqflags with
- * the trigger flags from the resource.
- */
- irq_resflags = irqd_get_trigger_type(irq_get_irq_data(ndev->irq));
- if (irq_flags == -1 || irq_resflags & IRQF_TRIGGER_MASK)
- irq_flags = irq_resflags & IRQF_TRIGGER_MASK;
+
+ ndev->irq = ires->start;
+
+ if (irq_flags == -1 || ires->flags & IRQF_TRIGGER_MASK)
+ irq_flags = ires->flags & IRQF_TRIGGER_MASK;
ret = smc_request_attrib(pdev, ndev);
if (ret)
goto out_release_io;
-#if defined(CONFIG_SA1100_ASSABET)
- neponset_ncr_set(NCR_ENET_OSC_EN);
+#if defined(CONFIG_ASSABET_NEPONSET)
+ if (machine_is_assabet() && machine_has_neponset())
+ neponset_ncr_set(NCR_ENET_OSC_EN);
#endif
platform_set_drvdata(pdev, ndev);
ret = smc_enable_device(pdev);
* Define your architecture specific bus configuration parameters here.
*/
-#if defined(CONFIG_ARCH_LUBBOCK) ||\
- defined(CONFIG_MACH_MAINSTONE) ||\
- defined(CONFIG_MACH_ZYLONITE) ||\
- defined(CONFIG_MACH_LITTLETON) ||\
- defined(CONFIG_MACH_ZYLONITE2) ||\
- defined(CONFIG_ARCH_VIPER) ||\
- defined(CONFIG_MACH_STARGATE2) ||\
- defined(CONFIG_ARCH_VERSATILE)
+#if defined(CONFIG_ARM)
#include <asm/mach-types.h>
/* We actually can't write halfwords properly if not word aligned */
static inline void SMC_outw(u16 val, void __iomem *ioaddr, int reg)
{
- if ((machine_is_mainstone() || machine_is_stargate2()) && reg & 2) {
- unsigned int v = val << 16;
- v |= readl(ioaddr + (reg & ~2)) & 0xffff;
- writel(v, ioaddr + (reg & ~2));
- } else {
- writew(val, ioaddr + reg);
- }
-}
-
-#elif defined(CONFIG_SA1100_PLEB)
-/* We can only do 16-bit reads and writes in the static memory space. */
-#define SMC_CAN_USE_8BIT 1
-#define SMC_CAN_USE_16BIT 1
-#define SMC_CAN_USE_32BIT 0
-#define SMC_IO_SHIFT 0
-#define SMC_NOWAIT 1
-
-#define SMC_inb(a, r) readb((a) + (r))
-#define SMC_insb(a, r, p, l) readsb((a) + (r), p, (l))
-#define SMC_inw(a, r) readw((a) + (r))
-#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l)
-#define SMC_outb(v, a, r) writeb(v, (a) + (r))
-#define SMC_outsb(a, r, p, l) writesb((a) + (r), p, (l))
-#define SMC_outw(v, a, r) writew(v, (a) + (r))
-#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l)
-
-#define SMC_IRQ_FLAGS (-1)
-
-#elif defined(CONFIG_SA1100_ASSABET)
-
-#include <mach/neponset.h>
-
-/* We can only do 8-bit reads and writes in the static memory space. */
-#define SMC_CAN_USE_8BIT 1
-#define SMC_CAN_USE_16BIT 0
-#define SMC_CAN_USE_32BIT 0
-#define SMC_NOWAIT 1
-
-/* The first two address lines aren't connected... */
-#define SMC_IO_SHIFT 2
-
-#define SMC_inb(a, r) readb((a) + (r))
-#define SMC_outb(v, a, r) writeb(v, (a) + (r))
-#define SMC_insb(a, r, p, l) readsb((a) + (r), p, (l))
-#define SMC_outsb(a, r, p, l) writesb((a) + (r), p, (l))
-#define SMC_IRQ_FLAGS (-1) /* from resource */
-
-#elif defined(CONFIG_MACH_LOGICPD_PXA270) || \
- defined(CONFIG_MACH_NOMADIK_8815NHK)
-
-#define SMC_CAN_USE_8BIT 0
-#define SMC_CAN_USE_16BIT 1
-#define SMC_CAN_USE_32BIT 0
-#define SMC_IO_SHIFT 0
-#define SMC_NOWAIT 1
-
-#define SMC_inw(a, r) readw((a) + (r))
-#define SMC_outw(v, a, r) writew(v, (a) + (r))
-#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l)
-#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l)
-
-#elif defined(CONFIG_ARCH_INNOKOM) || \
- defined(CONFIG_ARCH_PXA_IDP) || \
- defined(CONFIG_ARCH_RAMSES) || \
- defined(CONFIG_ARCH_PCM027)
-
-#define SMC_CAN_USE_8BIT 1
-#define SMC_CAN_USE_16BIT 1
-#define SMC_CAN_USE_32BIT 1
-#define SMC_IO_SHIFT 0
-#define SMC_NOWAIT 1
-#define SMC_USE_PXA_DMA 1
-
-#define SMC_inb(a, r) readb((a) + (r))
-#define SMC_inw(a, r) readw((a) + (r))
-#define SMC_inl(a, r) readl((a) + (r))
-#define SMC_outb(v, a, r) writeb(v, (a) + (r))
-#define SMC_outl(v, a, r) writel(v, (a) + (r))
-#define SMC_insl(a, r, p, l) readsl((a) + (r), p, l)
-#define SMC_outsl(a, r, p, l) writesl((a) + (r), p, l)
-#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l)
-#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l)
-#define SMC_IRQ_FLAGS (-1) /* from resource */
-
-/* We actually can't write halfwords properly if not word aligned */
-static inline void
-SMC_outw(u16 val, void __iomem *ioaddr, int reg)
-{
- if (reg & 2) {
+ if ((machine_is_mainstone() || machine_is_stargate2() ||
+ machine_is_pxa_idp()) && reg & 2) {
unsigned int v = val << 16;
v |= readl(ioaddr + (reg & ~2)) & 0xffff;
writel(v, ioaddr + (reg & ~2));
#define RPC_LSA_DEFAULT RPC_LED_100_10
#define RPC_LSB_DEFAULT RPC_LED_TX_RX
-#elif defined(CONFIG_ARCH_MSM)
-
-#define SMC_CAN_USE_8BIT 0
-#define SMC_CAN_USE_16BIT 1
-#define SMC_CAN_USE_32BIT 0
-#define SMC_NOWAIT 1
-
-#define SMC_inw(a, r) readw((a) + (r))
-#define SMC_outw(v, a, r) writew(v, (a) + (r))
-#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l)
-#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l)
-
-#define SMC_IRQ_FLAGS IRQF_TRIGGER_HIGH
-
#elif defined(CONFIG_COLDFIRE)
#define SMC_CAN_USE_8BIT 0
spin_lock_irqsave(&priv->lock, flags);
if (!priv->eee_active) {
priv->eee_active = 1;
- init_timer(&priv->eee_ctrl_timer);
- priv->eee_ctrl_timer.function = stmmac_eee_ctrl_timer;
- priv->eee_ctrl_timer.data = (unsigned long)priv;
- priv->eee_ctrl_timer.expires = STMMAC_LPI_T(eee_timer);
- add_timer(&priv->eee_ctrl_timer);
+ setup_timer(&priv->eee_ctrl_timer,
+ stmmac_eee_ctrl_timer,
+ (unsigned long)priv);
+ mod_timer(&priv->eee_ctrl_timer,
+ STMMAC_LPI_T(eee_timer));
priv->hw->mac->set_eee_timer(priv->hw,
STMMAC_DEFAULT_LIT_LS,
struct stmmac_priv *priv = NULL;
struct plat_stmmacenet_data *plat_dat = NULL;
const char *mac = NULL;
+ int irq, wol_irq, lpi_irq;
+
+ /* Get IRQ information early to have an ability to ask for deferred
+ * probe if needed before we went too far with resource allocation.
+ */
+ irq = platform_get_irq_byname(pdev, "macirq");
+ if (irq < 0) {
+ if (irq != -EPROBE_DEFER) {
+ dev_err(dev,
+ "MAC IRQ configuration information not found\n");
+ }
+ return irq;
+ }
+
+ /* On some platforms e.g. SPEAr the wake up irq differs from the mac irq
+ * The external wake up irq can be passed through the platform code
+ * named as "eth_wake_irq"
+ *
+ * In case the wake up interrupt is not passed from the platform
+ * so the driver will continue to use the mac irq (ndev->irq)
+ */
+ wol_irq = platform_get_irq_byname(pdev, "eth_wake_irq");
+ if (wol_irq < 0) {
+ if (wol_irq == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ wol_irq = irq;
+ }
+
+ lpi_irq = platform_get_irq_byname(pdev, "eth_lpi");
+ if (lpi_irq == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
addr = devm_ioremap_resource(dev, res);
return PTR_ERR(priv);
}
+ /* Copy IRQ values to priv structure which is now avaialble */
+ priv->dev->irq = irq;
+ priv->wol_irq = wol_irq;
+ priv->lpi_irq = lpi_irq;
+
/* Get MAC address if available (DT) */
if (mac)
memcpy(priv->dev->dev_addr, mac, ETH_ALEN);
- /* Get the MAC information */
- priv->dev->irq = platform_get_irq_byname(pdev, "macirq");
- if (priv->dev->irq < 0) {
- if (priv->dev->irq != -EPROBE_DEFER) {
- netdev_err(priv->dev,
- "MAC IRQ configuration information not found\n");
- }
- return priv->dev->irq;
- }
-
- /*
- * On some platforms e.g. SPEAr the wake up irq differs from the mac irq
- * The external wake up irq can be passed through the platform code
- * named as "eth_wake_irq"
- *
- * In case the wake up interrupt is not passed from the platform
- * so the driver will continue to use the mac irq (ndev->irq)
- */
- priv->wol_irq = platform_get_irq_byname(pdev, "eth_wake_irq");
- if (priv->wol_irq < 0) {
- if (priv->wol_irq == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- priv->wol_irq = priv->dev->irq;
- }
-
- priv->lpi_irq = platform_get_irq_byname(pdev, "eth_lpi");
- if (priv->lpi_irq == -EPROBE_DEFER)
- return -EPROBE_DEFER;
-
platform_set_drvdata(pdev, priv->dev);
pr_debug("STMMAC platform driver registration completed");
*flow_type = IP_USER_FLOW;
break;
default:
- return 0;
+ return -EINVAL;
}
- return 1;
+ return 0;
}
static int niu_ethflow_to_class(int flow_type, u64 *class)
class = (tp->key[0] & TCAM_V4KEY0_CLASS_CODE) >>
TCAM_V4KEY0_CLASS_CODE_SHIFT;
ret = niu_class_to_ethflow(class, &fsp->flow_type);
-
if (ret < 0) {
netdev_info(np->dev, "niu%d: niu_class_to_ethflow failed\n",
parent->index);
- ret = -EINVAL;
goto out;
}
cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast,
port_mask, ALE_VLAN, slave->port_vlan, 0);
cpsw_ale_add_ucast(priv->ale, priv->mac_addr,
- priv->host_port, ALE_VLAN, slave->port_vlan);
+ priv->host_port, ALE_VLAN | ALE_SECURE, slave->port_vlan);
}
static void soft_reset_slave(struct cpsw_slave *slave)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
static int cpsw_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
}
return 0;
}
+#endif
-static const struct dev_pm_ops cpsw_pm_ops = {
- .suspend = cpsw_suspend,
- .resume = cpsw_resume,
-};
+static SIMPLE_DEV_PM_OPS(cpsw_pm_ops, cpsw_suspend, cpsw_resume);
static const struct of_device_id cpsw_of_mtable[] = {
{ .compatible = "ti,cpsw", },
return 0;
}
+#ifdef CONFIG_PM_SLEEP
static int davinci_mdio_suspend(struct device *dev)
{
struct davinci_mdio_data *data = dev_get_drvdata(dev);
return 0;
}
+#endif
static const struct dev_pm_ops davinci_mdio_pm_ops = {
- .suspend_late = davinci_mdio_suspend,
- .resume_early = davinci_mdio_resume,
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(davinci_mdio_suspend, davinci_mdio_resume)
};
#if IS_ENABLED(CONFIG_OF)
}
if (rx_count < budget) {
+ napi_complete(napi);
w5100_write(priv, W5100_IMR, IR_S0);
mmiowb();
- napi_complete(napi);
}
return rx_count;
}
if (rx_count < budget) {
+ napi_complete(napi);
w5300_write(priv, W5300_IMR, IR_S0);
mmiowb();
- napi_complete(napi);
}
return rx_count;
int i;
static const u8 allmulti[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
- if (dev->flags & IFF_ALLMULTI) {
+ if ((dev->flags & IFF_ALLMULTI) && !(dev->flags & IFF_PROMISC)) {
for (i = 0; i < ETH_ALEN; i++) {
__raw_writel(allmulti[i], &port->regs->mcast_addr[i]);
__raw_writel(allmulti[i], &port->regs->mcast_mask[i]);
rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb);
int ipvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev);
void ipvlan_ht_addr_add(struct ipvl_dev *ipvlan, struct ipvl_addr *addr);
-bool ipvlan_addr_busy(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6);
+struct ipvl_addr *ipvlan_find_addr(const struct ipvl_dev *ipvlan,
+ const void *iaddr, bool is_v6);
+bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6);
struct ipvl_addr *ipvlan_ht_addr_lookup(const struct ipvl_port *port,
const void *iaddr, bool is_v6);
void ipvlan_ht_addr_del(struct ipvl_addr *addr, bool sync);
hash = (addr->atype == IPVL_IPV6) ?
ipvlan_get_v6_hash(&addr->ip6addr) :
ipvlan_get_v4_hash(&addr->ip4addr);
- hlist_add_head_rcu(&addr->hlnode, &port->hlhead[hash]);
+ if (hlist_unhashed(&addr->hlnode))
+ hlist_add_head_rcu(&addr->hlnode, &port->hlhead[hash]);
}
void ipvlan_ht_addr_del(struct ipvl_addr *addr, bool sync)
{
- hlist_del_rcu(&addr->hlnode);
+ hlist_del_init_rcu(&addr->hlnode);
if (sync)
synchronize_rcu();
}
-bool ipvlan_addr_busy(struct ipvl_dev *ipvlan, void *iaddr, bool is_v6)
+struct ipvl_addr *ipvlan_find_addr(const struct ipvl_dev *ipvlan,
+ const void *iaddr, bool is_v6)
{
- struct ipvl_port *port = ipvlan->port;
struct ipvl_addr *addr;
list_for_each_entry(addr, &ipvlan->addrs, anode) {
ipv6_addr_equal(&addr->ip6addr, iaddr)) ||
(!is_v6 && addr->atype == IPVL_IPV4 &&
addr->ip4addr.s_addr == ((struct in_addr *)iaddr)->s_addr))
- return true;
+ return addr;
}
+ return NULL;
+}
+
+bool ipvlan_addr_busy(struct ipvl_port *port, void *iaddr, bool is_v6)
+{
+ struct ipvl_dev *ipvlan;
- if (ipvlan_ht_addr_lookup(port, iaddr, is_v6))
- return true;
+ ASSERT_RTNL();
+ list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
+ if (ipvlan_find_addr(ipvlan, iaddr, is_v6))
+ return true;
+ }
return false;
}
if (skb->protocol == htons(ETH_P_PAUSE))
return;
- list_for_each_entry(ipvlan, &port->ipvlans, pnode) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(ipvlan, &port->ipvlans, pnode) {
if (local && (ipvlan == in_dev))
continue;
mcast_acct:
ipvlan_count_rx(ipvlan, len, ret == NET_RX_SUCCESS, true);
}
+ rcu_read_unlock();
/* Locally generated? ...Forward a copy to the main-device as
* well. On the RX side we'll ignore it (wont give it to any
if (ipvlan->ipv6cnt > 0 || ipvlan->ipv4cnt > 0) {
list_for_each_entry_safe(addr, next, &ipvlan->addrs, anode) {
ipvlan_ht_addr_del(addr, !dev->dismantle);
- list_del_rcu(&addr->anode);
+ list_del(&addr->anode);
}
}
list_del_rcu(&ipvlan->pnode);
{
struct ipvl_addr *addr;
- if (ipvlan_addr_busy(ipvlan, ip6_addr, true)) {
+ if (ipvlan_addr_busy(ipvlan->port, ip6_addr, true)) {
netif_err(ipvlan, ifup, ipvlan->dev,
"Failed to add IPv6=%pI6c addr for %s intf\n",
ip6_addr, ipvlan->dev->name);
addr->master = ipvlan;
memcpy(&addr->ip6addr, ip6_addr, sizeof(struct in6_addr));
addr->atype = IPVL_IPV6;
- list_add_tail_rcu(&addr->anode, &ipvlan->addrs);
+ list_add_tail(&addr->anode, &ipvlan->addrs);
ipvlan->ipv6cnt++;
- ipvlan_ht_addr_add(ipvlan, addr);
+ /* If the interface is not up, the address will be added to the hash
+ * list by ipvlan_open.
+ */
+ if (netif_running(ipvlan->dev))
+ ipvlan_ht_addr_add(ipvlan, addr);
return 0;
}
{
struct ipvl_addr *addr;
- addr = ipvlan_ht_addr_lookup(ipvlan->port, ip6_addr, true);
+ addr = ipvlan_find_addr(ipvlan, ip6_addr, true);
if (!addr)
return;
ipvlan_ht_addr_del(addr, true);
- list_del_rcu(&addr->anode);
+ list_del(&addr->anode);
ipvlan->ipv6cnt--;
WARN_ON(ipvlan->ipv6cnt < 0);
kfree_rcu(addr, rcu);
{
struct ipvl_addr *addr;
- if (ipvlan_addr_busy(ipvlan, ip4_addr, false)) {
+ if (ipvlan_addr_busy(ipvlan->port, ip4_addr, false)) {
netif_err(ipvlan, ifup, ipvlan->dev,
"Failed to add IPv4=%pI4 on %s intf.\n",
ip4_addr, ipvlan->dev->name);
addr->master = ipvlan;
memcpy(&addr->ip4addr, ip4_addr, sizeof(struct in_addr));
addr->atype = IPVL_IPV4;
- list_add_tail_rcu(&addr->anode, &ipvlan->addrs);
+ list_add_tail(&addr->anode, &ipvlan->addrs);
ipvlan->ipv4cnt++;
- ipvlan_ht_addr_add(ipvlan, addr);
+ /* If the interface is not up, the address will be added to the hash
+ * list by ipvlan_open.
+ */
+ if (netif_running(ipvlan->dev))
+ ipvlan_ht_addr_add(ipvlan, addr);
ipvlan_set_broadcast_mac_filter(ipvlan, true);
return 0;
{
struct ipvl_addr *addr;
- addr = ipvlan_ht_addr_lookup(ipvlan->port, ip4_addr, false);
+ addr = ipvlan_find_addr(ipvlan, ip4_addr, false);
if (!addr)
return;
ipvlan_ht_addr_del(addr, true);
- list_del_rcu(&addr->anode);
+ list_del(&addr->anode);
ipvlan->ipv4cnt--;
WARN_ON(ipvlan->ipv4cnt < 0);
if (!ipvlan->ipv4cnt)
} /* else everything is zero */
}
+/* Neighbour code has some assumptions on HH_DATA_MOD alignment */
+#define MACVTAP_RESERVE HH_DATA_OFF(ETH_HLEN)
+
/* Get packet from user space buffer */
static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
struct iov_iter *from, int noblock)
{
- int good_linear = SKB_MAX_HEAD(NET_IP_ALIGN);
+ int good_linear = SKB_MAX_HEAD(MACVTAP_RESERVE);
struct sk_buff *skb;
struct macvlan_dev *vlan;
unsigned long total_len = iov_iter_count(from);
linear = macvtap16_to_cpu(q, vnet_hdr.hdr_len);
}
- skb = macvtap_alloc_skb(&q->sk, NET_IP_ALIGN, copylen,
+ skb = macvtap_alloc_skb(&q->sk, MACVTAP_RESERVE, copylen,
linear, noblock, &err);
if (!skb)
goto err;
#define XGBE_PHY_CDR_RATE_PROPERTY "amd,serdes-cdr-rate"
#define XGBE_PHY_PQ_SKEW_PROPERTY "amd,serdes-pq-skew"
#define XGBE_PHY_TX_AMP_PROPERTY "amd,serdes-tx-amp"
+#define XGBE_PHY_DFE_CFG_PROPERTY "amd,serdes-dfe-tap-config"
+#define XGBE_PHY_DFE_ENA_PROPERTY "amd,serdes-dfe-tap-enable"
#define XGBE_PHY_SPEEDS 3
#define XGBE_PHY_SPEED_1000 0
#define SPEED_10000_BLWC 0
#define SPEED_10000_CDR 0x7
#define SPEED_10000_PLL 0x1
-#define SPEED_10000_PQ 0x1e
+#define SPEED_10000_PQ 0x12
#define SPEED_10000_RATE 0x0
#define SPEED_10000_TXAMP 0xa
#define SPEED_10000_WORD 0x7
+#define SPEED_10000_DFE_TAP_CONFIG 0x1
+#define SPEED_10000_DFE_TAP_ENABLE 0x7f
#define SPEED_2500_BLWC 1
#define SPEED_2500_CDR 0x2
#define SPEED_2500_RATE 0x1
#define SPEED_2500_TXAMP 0xf
#define SPEED_2500_WORD 0x1
+#define SPEED_2500_DFE_TAP_CONFIG 0x3
+#define SPEED_2500_DFE_TAP_ENABLE 0x0
#define SPEED_1000_BLWC 1
#define SPEED_1000_CDR 0x2
#define SPEED_1000_RATE 0x3
#define SPEED_1000_TXAMP 0xf
#define SPEED_1000_WORD 0x1
+#define SPEED_1000_DFE_TAP_CONFIG 0x3
+#define SPEED_1000_DFE_TAP_ENABLE 0x0
/* SerDes RxTx register offsets */
+#define RXTX_REG6 0x0018
#define RXTX_REG20 0x0050
+#define RXTX_REG22 0x0058
#define RXTX_REG114 0x01c8
+#define RXTX_REG129 0x0204
/* SerDes RxTx register entry bit positions and sizes */
+#define RXTX_REG6_RESETB_RXD_INDEX 8
+#define RXTX_REG6_RESETB_RXD_WIDTH 1
#define RXTX_REG20_BLWC_ENA_INDEX 2
#define RXTX_REG20_BLWC_ENA_WIDTH 1
#define RXTX_REG114_PQ_REG_INDEX 9
#define RXTX_REG114_PQ_REG_WIDTH 7
+#define RXTX_REG129_RXDFE_CONFIG_INDEX 14
+#define RXTX_REG129_RXDFE_CONFIG_WIDTH 2
/* Bit setting and getting macros
* The get macro will extract the current bit field value from within
SPEED_10000_TXAMP,
};
+static const u32 amd_xgbe_phy_serdes_dfe_tap_cfg[] = {
+ SPEED_1000_DFE_TAP_CONFIG,
+ SPEED_2500_DFE_TAP_CONFIG,
+ SPEED_10000_DFE_TAP_CONFIG,
+};
+
+static const u32 amd_xgbe_phy_serdes_dfe_tap_ena[] = {
+ SPEED_1000_DFE_TAP_ENABLE,
+ SPEED_2500_DFE_TAP_ENABLE,
+ SPEED_10000_DFE_TAP_ENABLE,
+};
+
enum amd_xgbe_phy_an {
AMD_XGBE_AN_READY = 0,
AMD_XGBE_AN_PAGE_RECEIVED,
u32 serdes_cdr_rate[XGBE_PHY_SPEEDS];
u32 serdes_pq_skew[XGBE_PHY_SPEEDS];
u32 serdes_tx_amp[XGBE_PHY_SPEEDS];
+ u32 serdes_dfe_tap_cfg[XGBE_PHY_SPEEDS];
+ u32 serdes_dfe_tap_ena[XGBE_PHY_SPEEDS];
/* Auto-negotiation state machine support */
struct mutex an_mutex;
status = XSIR0_IOREAD(priv, SIR0_STATUS);
if (XSIR_GET_BITS(status, SIR0_STATUS, RX_READY) &&
XSIR_GET_BITS(status, SIR0_STATUS, TX_READY))
- return;
+ goto rx_reset;
}
netdev_dbg(phydev->attached_dev, "SerDes rx/tx not ready (%#hx)\n",
status);
+
+rx_reset:
+ /* Perform Rx reset for the DFE changes */
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG6, RESETB_RXD, 0);
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG6, RESETB_RXD, 1);
}
static int amd_xgbe_phy_xgmii_mode(struct phy_device *phydev)
priv->serdes_blwc[XGBE_PHY_SPEED_10000]);
XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG,
priv->serdes_pq_skew[XGBE_PHY_SPEED_10000]);
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG129, RXDFE_CONFIG,
+ priv->serdes_dfe_tap_cfg[XGBE_PHY_SPEED_10000]);
+ XRXTX_IOWRITE(priv, RXTX_REG22,
+ priv->serdes_dfe_tap_ena[XGBE_PHY_SPEED_10000]);
amd_xgbe_phy_serdes_complete_ratechange(phydev);
priv->serdes_blwc[XGBE_PHY_SPEED_2500]);
XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG,
priv->serdes_pq_skew[XGBE_PHY_SPEED_2500]);
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG129, RXDFE_CONFIG,
+ priv->serdes_dfe_tap_cfg[XGBE_PHY_SPEED_2500]);
+ XRXTX_IOWRITE(priv, RXTX_REG22,
+ priv->serdes_dfe_tap_ena[XGBE_PHY_SPEED_2500]);
amd_xgbe_phy_serdes_complete_ratechange(phydev);
priv->serdes_blwc[XGBE_PHY_SPEED_1000]);
XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG,
priv->serdes_pq_skew[XGBE_PHY_SPEED_1000]);
+ XRXTX_IOWRITE_BITS(priv, RXTX_REG129, RXDFE_CONFIG,
+ priv->serdes_dfe_tap_cfg[XGBE_PHY_SPEED_1000]);
+ XRXTX_IOWRITE(priv, RXTX_REG22,
+ priv->serdes_dfe_tap_ena[XGBE_PHY_SPEED_1000]);
amd_xgbe_phy_serdes_complete_ratechange(phydev);
sizeof(priv->serdes_tx_amp));
}
+ if (device_property_present(phy_dev, XGBE_PHY_DFE_CFG_PROPERTY)) {
+ ret = device_property_read_u32_array(phy_dev,
+ XGBE_PHY_DFE_CFG_PROPERTY,
+ priv->serdes_dfe_tap_cfg,
+ XGBE_PHY_SPEEDS);
+ if (ret) {
+ dev_err(dev, "invalid %s property\n",
+ XGBE_PHY_DFE_CFG_PROPERTY);
+ goto err_sir1;
+ }
+ } else {
+ memcpy(priv->serdes_dfe_tap_cfg,
+ amd_xgbe_phy_serdes_dfe_tap_cfg,
+ sizeof(priv->serdes_dfe_tap_cfg));
+ }
+
+ if (device_property_present(phy_dev, XGBE_PHY_DFE_ENA_PROPERTY)) {
+ ret = device_property_read_u32_array(phy_dev,
+ XGBE_PHY_DFE_ENA_PROPERTY,
+ priv->serdes_dfe_tap_ena,
+ XGBE_PHY_SPEEDS);
+ if (ret) {
+ dev_err(dev, "invalid %s property\n",
+ XGBE_PHY_DFE_ENA_PROPERTY);
+ goto err_sir1;
+ }
+ } else {
+ memcpy(priv->serdes_dfe_tap_ena,
+ amd_xgbe_phy_serdes_dfe_tap_ena,
+ sizeof(priv->serdes_dfe_tap_ena));
+ }
+
phydev->priv = priv;
if (!priv->adev || acpi_disabled)
return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1;
}
+/**
+ * phy_check_valid - check if there is a valid PHY setting which matches
+ * speed, duplex, and feature mask
+ * @speed: speed to match
+ * @duplex: duplex to match
+ * @features: A mask of the valid settings
+ *
+ * Description: Returns true if there is a valid setting, false otherwise.
+ */
+static inline bool phy_check_valid(int speed, int duplex, u32 features)
+{
+ unsigned int idx;
+
+ idx = phy_find_valid(phy_find_setting(speed, duplex), features);
+
+ return settings[idx].speed == speed && settings[idx].duplex == duplex &&
+ (settings[idx].setting & features);
+}
+
/**
* phy_sanitize_settings - make sure the PHY is set to supported speed and duplex
* @phydev: the target phy_device struct
int eee_lp, eee_cap, eee_adv;
u32 lp, cap, adv;
int status;
- unsigned int idx;
/* Read phy status to properly get the right settings */
status = phy_read_status(phydev);
adv = mmd_eee_adv_to_ethtool_adv_t(eee_adv);
lp = mmd_eee_adv_to_ethtool_adv_t(eee_lp);
- idx = phy_find_setting(phydev->speed, phydev->duplex);
- if (!(lp & adv & settings[idx].setting))
+ if (!phy_check_valid(phydev->speed, phydev->duplex, lp & adv))
goto eee_exit_err;
if (clk_stop_enable) {
static struct team_port *team_port_get_rcu(const struct net_device *dev)
{
- struct team_port *port = rcu_dereference(dev->rx_handler_data);
-
- return team_port_exists(dev) ? port : NULL;
+ return rcu_dereference(dev->rx_handler_data);
}
static struct team_port *team_port_get_rtnl(const struct net_device *dev)
if (dev->type == ARPHRD_ETHER && !is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
- rcu_read_lock();
- list_for_each_entry_rcu(port, &team->port_list, list)
+ mutex_lock(&team->lock);
+ list_for_each_entry(port, &team->port_list, list)
if (team->ops.port_change_dev_addr)
team->ops.port_change_dev_addr(team, port);
- rcu_read_unlock();
+ mutex_unlock(&team->lock);
return 0;
}
* Linksys USB200M
* Netgear FA120
* Sitecom LN-029
+ * Sitecom LN-028
* Intellinet USB 2.0 Ethernet
* ST Lab USB 2.0 Ethernet
* TrendNet TU2-ET100
memcpy(skb_tail_pointer(skb), &padbytes, sizeof(padbytes));
skb_put(skb, sizeof(padbytes));
}
+
+ usbnet_set_skb_tx_stats(skb, 1, 0);
return skb;
}
// Sitecom LN-031 "USB 2.0 10/100/1000 Ethernet adapter"
USB_DEVICE (0x0df6, 0x0056),
.driver_info = (unsigned long) &ax88178_info,
+}, {
+ // Sitecom LN-028 "USB 2.0 10/100/1000 Ethernet adapter"
+ USB_DEVICE (0x0df6, 0x061c),
+ .driver_info = (unsigned long) &ax88178_info,
}, {
// corega FEther USB2-TX
USB_DEVICE (0x07aa, 0x0017),
#define DELL_VENDOR_ID 0x413C
#define REALTEK_VENDOR_ID 0x0bda
#define SAMSUNG_VENDOR_ID 0x04e8
+#define LENOVO_VENDOR_ID 0x17ef
static const struct usb_device_id products[] = {
/* BLACKLIST !!
.driver_info = 0,
},
+/* Lenovo Thinkpad USB 3.0 Ethernet Adapters (based on Realtek RTL8153) */
+{
+ USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x7205, USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+ .driver_info = 0,
+},
+
/* WHITELIST!!!
*
* CDC Ether uses two interfaces, not necessarily consecutive.
/* return skb */
ctx->tx_curr_skb = NULL;
- dev->net->stats.tx_packets += ctx->tx_curr_frame_num;
/* keep private stats: framing overhead and number of NTBs */
ctx->tx_overhead += skb_out->len - ctx->tx_curr_frame_payload;
ctx->tx_ntbs++;
- /* usbnet has already counted all the framing overhead.
+ /* usbnet will count all the framing overhead by default.
* Adjust the stats so that the tx_bytes counter show real
* payload data instead.
*/
- dev->net->stats.tx_bytes -= skb_out->len - ctx->tx_curr_frame_payload;
+ usbnet_set_skb_tx_stats(skb_out, n,
+ ctx->tx_curr_frame_payload - skb_out->len);
return skb_out;
};
#define CMD_PACKET_SIZE 64
-/* first command after power on can take around 8 seconds */
-#define CMD_TIMEOUT 15000
+#define CMD_TIMEOUT 100
#define CMD_REPLY_RETRY 5
#define CX82310_MTU 1514
ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, CMD_EP), buf,
CMD_PACKET_SIZE, &actual_len, CMD_TIMEOUT);
if (ret < 0) {
- dev_err(&dev->udev->dev, "send command %#x: error %d\n",
- cmd, ret);
+ if (cmd != CMD_GET_LINK_STATUS)
+ dev_err(&dev->udev->dev, "send command %#x: error %d\n",
+ cmd, ret);
goto end;
}
buf, CMD_PACKET_SIZE, &actual_len,
CMD_TIMEOUT);
if (ret < 0) {
- dev_err(&dev->udev->dev,
- "reply receive error %d\n", ret);
+ if (cmd != CMD_GET_LINK_STATUS)
+ dev_err(&dev->udev->dev,
+ "reply receive error %d\n",
+ ret);
goto end;
}
if (actual_len > 0)
int ret;
char buf[15];
struct usb_device *udev = dev->udev;
+ u8 link[3];
+ int timeout = 50;
/* avoid ADSL modems - continue only if iProduct is "USB NET CARD" */
if (usb_string(udev, udev->descriptor.iProduct, buf, sizeof(buf)) > 0
if (!dev->partial_data)
return -ENOMEM;
+ /* wait for firmware to become ready (indicated by the link being up) */
+ while (--timeout) {
+ ret = cx82310_cmd(dev, CMD_GET_LINK_STATUS, true, NULL, 0,
+ link, sizeof(link));
+ /* the command can time out during boot - it's not an error */
+ if (!ret && link[0] == 1 && link[2] == 1)
+ break;
+ msleep(500);
+ };
+ if (!timeout) {
+ dev_err(&udev->dev, "firmware not ready in time\n");
+ return -ETIMEDOUT;
+ }
+
/* enable ethernet mode (?) */
ret = cx82310_cmd(dev, CMD_ETHERNET_MODE, true, "\x01", 1, NULL, 0);
if (ret) {
.tx_fixup = cx82310_tx_fixup,
};
+#define USB_DEVICE_CLASS(vend, prod, cl, sc, pr) \
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
+ USB_DEVICE_ID_MATCH_DEV_INFO, \
+ .idVendor = (vend), \
+ .idProduct = (prod), \
+ .bDeviceClass = (cl), \
+ .bDeviceSubClass = (sc), \
+ .bDeviceProtocol = (pr)
+
static const struct usb_device_id products[] = {
{
- USB_DEVICE_AND_INTERFACE_INFO(0x0572, 0xcb01, 0xff, 0, 0),
+ USB_DEVICE_CLASS(0x0572, 0xcb01, 0xff, 0, 0),
.driver_info = (unsigned long) &cx82310_info
},
{ },
}
cprev = cnow;
}
- current->state = TASK_RUNNING;
+ __set_current_state(TASK_RUNNING);
remove_wait_queue(&tiocmget->waitq, &wait);
return ret;
}, {
USB_DEVICE(0x050d, 0x258a), /* Belkin F5U258/F5U279 (PL-25A1) */
.driver_info = (unsigned long) &prolific_info,
+}, {
+ USB_DEVICE(0x3923, 0x7825), /* National Instruments USB
+ * Host-to-Host Cable
+ */
+ .driver_info = (unsigned long) &prolific_info,
},
{ }, // END
/* Define these values to match your device */
#define VENDOR_ID_REALTEK 0x0bda
#define VENDOR_ID_SAMSUNG 0x04e8
+#define VENDOR_ID_LENOVO 0x17ef
#define MCU_TYPE_PLA 0x0100
#define MCU_TYPE_USB 0x0000
{REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8152)},
{REALTEK_USB_DEVICE(VENDOR_ID_REALTEK, 0x8153)},
{REALTEK_USB_DEVICE(VENDOR_ID_SAMSUNG, 0xa101)},
+ {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x7205)},
{}
};
skb_put(skb, sizeof(padbytes));
}
+ usbnet_set_skb_tx_stats(skb, 1, 0);
return skb;
}
struct usbnet *dev = entry->dev;
if (urb->status == 0) {
- if (!(dev->driver_info->flags & FLAG_MULTI_PACKET))
- dev->net->stats.tx_packets++;
+ dev->net->stats.tx_packets += entry->packets;
dev->net->stats.tx_bytes += entry->length;
} else {
dev->net->stats.tx_errors++;
} else
urb->transfer_flags |= URB_ZERO_PACKET;
}
- entry->length = urb->transfer_buffer_length = length;
+ urb->transfer_buffer_length = length;
+
+ if (info->flags & FLAG_MULTI_PACKET) {
+ /* Driver has set number of packets and a length delta.
+ * Calculate the complete length and ensure that it's
+ * positive.
+ */
+ entry->length += length;
+ if (WARN_ON_ONCE(entry->length <= 0))
+ entry->length = length;
+ } else {
+ usbnet_set_skb_tx_stats(skb, 1, length);
+ }
spin_lock_irqsave(&dev->txq.lock, flags);
retval = usb_autopm_get_interface_async(dev->intf);
{
int i;
- for (i = 0; i < vi->max_queue_pairs; i++)
+ for (i = 0; i < vi->max_queue_pairs; i++) {
+ napi_hash_del(&vi->rq[i].napi);
netif_napi_del(&vi->rq[i].napi);
+ }
kfree(vi->rq);
kfree(vi->sq);
cancel_delayed_work_sync(&vi->refill);
if (netif_running(vi->dev)) {
- for (i = 0; i < vi->max_queue_pairs; i++) {
+ for (i = 0; i < vi->max_queue_pairs; i++)
napi_disable(&vi->rq[i].napi);
- napi_hash_del(&vi->rq[i].napi);
- netif_napi_del(&vi->rq[i].napi);
- }
}
remove_vq_common(vi);
goto drop;
flags &= ~VXLAN_HF_RCO;
- vni &= VXLAN_VID_MASK;
+ vni &= VXLAN_VNI_MASK;
}
/* For backwards compatibility, only allow reserved fields to be
flags &= ~VXLAN_GBP_USED_BITS;
}
- if (flags || (vni & ~VXLAN_VID_MASK)) {
+ if (flags || vni & ~VXLAN_VNI_MASK) {
/* If there are any unprocessed flags remaining treat
* this as a malformed packet. This behavior diverges from
* VXLAN RFC (RFC7348) which stipulates that bits in reserved
spin_lock_irqsave(&cosa->lock, flags);
add_wait_queue(&chan->rxwaitq, &wait);
while (!chan->rx_status) {
- current->state = TASK_INTERRUPTIBLE;
+ set_current_state(TASK_INTERRUPTIBLE);
spin_unlock_irqrestore(&cosa->lock, flags);
schedule();
spin_lock_irqsave(&cosa->lock, flags);
if (signal_pending(current) && chan->rx_status == 0) {
chan->rx_status = 1;
remove_wait_queue(&chan->rxwaitq, &wait);
- current->state = TASK_RUNNING;
+ __set_current_state(TASK_RUNNING);
spin_unlock_irqrestore(&cosa->lock, flags);
mutex_unlock(&chan->rlock);
return -ERESTARTSYS;
}
}
remove_wait_queue(&chan->rxwaitq, &wait);
- current->state = TASK_RUNNING;
+ __set_current_state(TASK_RUNNING);
kbuf = chan->rxdata;
count = chan->rxsize;
spin_unlock_irqrestore(&cosa->lock, flags);
spin_lock_irqsave(&cosa->lock, flags);
add_wait_queue(&chan->txwaitq, &wait);
while (!chan->tx_status) {
- current->state = TASK_INTERRUPTIBLE;
+ set_current_state(TASK_INTERRUPTIBLE);
spin_unlock_irqrestore(&cosa->lock, flags);
schedule();
spin_lock_irqsave(&cosa->lock, flags);
if (signal_pending(current) && chan->tx_status == 0) {
chan->tx_status = 1;
remove_wait_queue(&chan->txwaitq, &wait);
- current->state = TASK_RUNNING;
+ __set_current_state(TASK_RUNNING);
chan->tx_status = 1;
spin_unlock_irqrestore(&cosa->lock, flags);
up(&chan->wsem);
}
}
remove_wait_queue(&chan->txwaitq, &wait);
- current->state = TASK_RUNNING;
+ __set_current_state(TASK_RUNNING);
up(&chan->wsem);
spin_unlock_irqrestore(&cosa->lock, flags);
kfree(kbuf);
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_vif *avp = (void *)vif->drv_priv;
struct ath_buf *bf = avp->av_bcbuf;
+ struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
ath_dbg(common, CONFIG, "Removing interface at beacon slot: %d\n",
avp->av_bslot);
tasklet_disable(&sc->bcon_tasklet);
+ cur_conf->enable_beacon &= ~BIT(avp->av_bslot);
+
if (bf && bf->bf_mpdu) {
struct sk_buff *skb = bf->bf_mpdu;
dma_unmap_single(sc->dev, bf->bf_buf_addr,
}
if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
- if ((vif->type != NL80211_IFTYPE_AP) ||
- (sc->nbcnvifs > 1)) {
+ if (vif->type != NL80211_IFTYPE_AP) {
ath_dbg(common, CONFIG,
"An AP interface is already present !\n");
return false;
* enabling/disabling SWBA.
*/
if (changed & BSS_CHANGED_BEACON_ENABLED) {
- if (!bss_conf->enable_beacon &&
- (sc->nbcnvifs <= 1)) {
- cur_conf->enable_beacon = false;
- } else if (bss_conf->enable_beacon) {
- cur_conf->enable_beacon = true;
- ath9k_cache_beacon_config(sc, ctx, bss_conf);
+ bool enabled = cur_conf->enable_beacon;
+
+ if (!bss_conf->enable_beacon) {
+ cur_conf->enable_beacon &= ~BIT(avp->av_bslot);
+ } else {
+ cur_conf->enable_beacon |= BIT(avp->av_bslot);
+ if (!enabled)
+ ath9k_cache_beacon_config(sc, ctx, bss_conf);
}
}
u16 dtim_period;
u16 bmiss_timeout;
u8 dtim_count;
- bool enable_beacon;
+ u8 enable_beacon;
bool ibss_creator;
u32 nexttbtt;
u32 intval;
ah->power_mode = ATH9K_PM_UNDEFINED;
ah->htc_reset_init = true;
- ah->tpc_enabled = true;
+ ah->tpc_enabled = false;
ah->ani_function = ATH9K_ANI_ALL;
if (!AR_SREV_9300_20_OR_LATER(ah))
case 0x432a: /* BCM4321 */
case 0x432d: /* BCM4322 */
case 0x4352: /* BCM43222 */
+ case 0x435a: /* BCM43228 */
case 0x4333: /* BCM4331 */
case 0x43a2: /* BCM4360 */
case 0x43b3: /* BCM4352 */
brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan");
if (drvr->bus_if->wowl_supported)
brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl");
- brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0);
+ if (drvr->bus_if->chip != BRCM_CC_43362_CHIP_ID)
+ brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0);
/* set chip related quirks */
switch (drvr->bus_if->chip) {
void *dcmd_buf = NULL, *wr_pointer;
u16 msglen, maxmsglen = PAGE_SIZE - 0x100;
- brcmf_dbg(TRACE, "cmd %x set %d len %d\n", cmdhdr->cmd, cmdhdr->set,
- cmdhdr->len);
+ if (len < sizeof(*cmdhdr)) {
+ brcmf_err("vendor command too short: %d\n", len);
+ return -EINVAL;
+ }
vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
ifp = vif->ifp;
- len -= sizeof(struct brcmf_vndr_dcmd_hdr);
+ brcmf_dbg(TRACE, "ifidx=%d, cmd=%d\n", ifp->ifidx, cmdhdr->cmd);
+
+ if (cmdhdr->offset > len) {
+ brcmf_err("bad buffer offset %d > %d\n", cmdhdr->offset, len);
+ return -EINVAL;
+ }
+
+ len -= cmdhdr->offset;
ret_len = cmdhdr->len;
if (ret_len > 0 || len > 0) {
if (len > BRCMF_DCMD_MAXLEN) {
unsigned long reload_jiffies;
int reload_count;
bool ucode_loaded;
- bool init_ucode_run; /* Don't run init uCode again */
u8 plcp_delta_threshold;
scd_queues &= ~(BIT(IWL_IPAN_CMD_QUEUE_NUM) |
BIT(IWL_DEFAULT_CMD_QUEUE_NUM));
- if (vif)
- scd_queues &= ~BIT(vif->hw_queue[IEEE80211_AC_VO]);
-
- IWL_DEBUG_TX_QUEUES(priv, "Flushing SCD queues: 0x%x\n", scd_queues);
- if (iwlagn_txfifo_flush(priv, scd_queues)) {
- IWL_ERR(priv, "flush request fail\n");
- goto done;
+ if (drop) {
+ IWL_DEBUG_TX_QUEUES(priv, "Flushing SCD queues: 0x%x\n",
+ scd_queues);
+ if (iwlagn_txfifo_flush(priv, scd_queues)) {
+ IWL_ERR(priv, "flush request fail\n");
+ goto done;
+ }
}
+
IWL_DEBUG_TX_QUEUES(priv, "wait transmit/flush all frames\n");
- iwl_trans_wait_tx_queue_empty(priv->trans, 0xffffffff);
+ iwl_trans_wait_tx_queue_empty(priv->trans, scd_queues);
done:
mutex_unlock(&priv->mutex);
IWL_DEBUG_MAC80211(priv, "leave\n");
if (!priv->fw->img[IWL_UCODE_INIT].sec[0].len)
return 0;
- if (priv->init_ucode_run)
- return 0;
-
iwl_init_notification_wait(&priv->notif_wait, &calib_wait,
calib_complete, ARRAY_SIZE(calib_complete),
iwlagn_wait_calib, priv);
*/
ret = iwl_wait_notification(&priv->notif_wait, &calib_wait,
UCODE_CALIB_TIMEOUT);
- if (!ret)
- priv->init_ucode_run = true;
goto out;
.nvm_calib_ver = EEPROM_1000_TX_POWER_VERSION, \
.base_params = &iwl1000_base_params, \
.eeprom_params = &iwl1000_eeprom_params, \
- .led_mode = IWL_LED_BLINK
+ .led_mode = IWL_LED_BLINK, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
const struct iwl_cfg iwl1000_bgn_cfg = {
.name = "Intel(R) Centrino(R) Wireless-N 1000 BGN",
.base_params = &iwl1000_base_params, \
.eeprom_params = &iwl1000_eeprom_params, \
.led_mode = IWL_LED_RF_STATE, \
- .rx_with_siso_diversity = true
+ .rx_with_siso_diversity = true, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
const struct iwl_cfg iwl100_bgn_cfg = {
.name = "Intel(R) Centrino(R) Wireless-N 100 BGN",
.nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
.base_params = &iwl2000_base_params, \
.eeprom_params = &iwl20x0_eeprom_params, \
- .led_mode = IWL_LED_RF_STATE
+ .led_mode = IWL_LED_RF_STATE, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
+
const struct iwl_cfg iwl2000_2bgn_cfg = {
.name = "Intel(R) Centrino(R) Wireless-N 2200 BGN",
.nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \
.base_params = &iwl2030_base_params, \
.eeprom_params = &iwl20x0_eeprom_params, \
- .led_mode = IWL_LED_RF_STATE
+ .led_mode = IWL_LED_RF_STATE, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
const struct iwl_cfg iwl2030_2bgn_cfg = {
.name = "Intel(R) Centrino(R) Wireless-N 2230 BGN",
.base_params = &iwl2000_base_params, \
.eeprom_params = &iwl20x0_eeprom_params, \
.led_mode = IWL_LED_RF_STATE, \
- .rx_with_siso_diversity = true
+ .rx_with_siso_diversity = true, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
const struct iwl_cfg iwl105_bgn_cfg = {
.name = "Intel(R) Centrino(R) Wireless-N 105 BGN",
.base_params = &iwl2030_base_params, \
.eeprom_params = &iwl20x0_eeprom_params, \
.led_mode = IWL_LED_RF_STATE, \
- .rx_with_siso_diversity = true
+ .rx_with_siso_diversity = true, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
const struct iwl_cfg iwl135_bgn_cfg = {
.name = "Intel(R) Centrino(R) Wireless-N 135 BGN",
.nvm_calib_ver = EEPROM_5000_TX_POWER_VERSION, \
.base_params = &iwl5000_base_params, \
.eeprom_params = &iwl5000_eeprom_params, \
- .led_mode = IWL_LED_BLINK
+ .led_mode = IWL_LED_BLINK, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
const struct iwl_cfg iwl5300_agn_cfg = {
.name = "Intel(R) Ultimate N WiFi Link 5300 AGN",
.base_params = &iwl5000_base_params, \
.eeprom_params = &iwl5000_eeprom_params, \
.led_mode = IWL_LED_BLINK, \
- .internal_wimax_coex = true
+ .internal_wimax_coex = true, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
const struct iwl_cfg iwl5150_agn_cfg = {
.name = "Intel(R) WiMAX/WiFi Link 5150 AGN",
.nvm_calib_ver = EEPROM_6005_TX_POWER_VERSION, \
.base_params = &iwl6000_g2_base_params, \
.eeprom_params = &iwl6000_eeprom_params, \
- .led_mode = IWL_LED_RF_STATE
+ .led_mode = IWL_LED_RF_STATE, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
const struct iwl_cfg iwl6005_2agn_cfg = {
.name = "Intel(R) Centrino(R) Advanced-N 6205 AGN",
.nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \
.base_params = &iwl6000_g2_base_params, \
.eeprom_params = &iwl6000_eeprom_params, \
- .led_mode = IWL_LED_RF_STATE
+ .led_mode = IWL_LED_RF_STATE, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
const struct iwl_cfg iwl6030_2agn_cfg = {
.name = "Intel(R) Centrino(R) Advanced-N 6230 AGN",
.nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \
.base_params = &iwl6000_g2_base_params, \
.eeprom_params = &iwl6000_eeprom_params, \
- .led_mode = IWL_LED_RF_STATE
+ .led_mode = IWL_LED_RF_STATE, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
const struct iwl_cfg iwl6035_2agn_cfg = {
.name = "Intel(R) Centrino(R) Advanced-N 6235 AGN",
.nvm_calib_ver = EEPROM_6000_TX_POWER_VERSION, \
.base_params = &iwl6000_base_params, \
.eeprom_params = &iwl6000_eeprom_params, \
- .led_mode = IWL_LED_BLINK
+ .led_mode = IWL_LED_BLINK, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
const struct iwl_cfg iwl6000i_2agn_cfg = {
.name = "Intel(R) Centrino(R) Advanced-N 6200 AGN",
.base_params = &iwl6050_base_params, \
.eeprom_params = &iwl6000_eeprom_params, \
.led_mode = IWL_LED_BLINK, \
- .internal_wimax_coex = true
+ .internal_wimax_coex = true, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
const struct iwl_cfg iwl6050_2agn_cfg = {
.name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 AGN",
.base_params = &iwl6050_base_params, \
.eeprom_params = &iwl6000_eeprom_params, \
.led_mode = IWL_LED_BLINK, \
- .internal_wimax_coex = true
+ .internal_wimax_coex = true, \
+ .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K
const struct iwl_cfg iwl6150_bgn_cfg = {
.name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BGN",
op->name, err);
#endif
}
+ kfree(pieces);
return;
try_again:
if (!vif->bss_conf.assoc)
smps_mode = IEEE80211_SMPS_AUTOMATIC;
- if (IWL_COEX_IS_RRC_ON(mvm->last_bt_notif.ttc_rrc_status,
+ if (mvmvif->phy_ctxt &&
+ IWL_COEX_IS_RRC_ON(mvm->last_bt_notif.ttc_rrc_status,
mvmvif->phy_ctxt->id))
smps_mode = IEEE80211_SMPS_AUTOMATIC;
if (!vif->bss_conf.assoc)
smps_mode = IEEE80211_SMPS_AUTOMATIC;
- if (data->notif->rrc_enabled & BIT(mvmvif->phy_ctxt->id))
+ if (mvmvif->phy_ctxt &&
+ data->notif->rrc_enabled & BIT(mvmvif->phy_ctxt->id))
smps_mode = IEEE80211_SMPS_AUTOMATIC;
IWL_DEBUG_COEX(data->mvm,
hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
&mvm->nvm_data->bands[IEEE80211_BAND_5GHZ];
- if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_BEAMFORMER)
+ if ((mvm->fw->ucode_capa.capa[0] &
+ IWL_UCODE_TLV_CAPA_BEAMFORMER) &&
+ (mvm->fw->ucode_capa.api[0] &
+ IWL_UCODE_TLV_API_LQ_SS_PARAMS))
hw->wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap.cap |=
IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
}
mutex_lock(&mvm->mutex);
- iwl_mvm_cancel_scan(mvm);
+ /* Due to a race condition, it's possible that mac80211 asks
+ * us to stop a hw_scan when it's already stopped. This can
+ * happen, for instance, if we stopped the scan ourselves,
+ * called ieee80211_scan_completed() and the userspace called
+ * cancel scan scan before ieee80211_scan_work() could run.
+ * To handle that, simply return if the scan is not running.
+ */
+ /* FIXME: for now, we ignore this race for UMAC scans, since
+ * they don't set the scan_status.
+ */
+ if ((mvm->scan_status == IWL_MVM_SCAN_OS) ||
+ (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN))
+ iwl_mvm_cancel_scan(mvm);
mutex_unlock(&mvm->mutex);
}
int ret;
mutex_lock(&mvm->mutex);
+
+ /* Due to a race condition, it's possible that mac80211 asks
+ * us to stop a sched_scan when it's already stopped. This
+ * can happen, for instance, if we stopped the scan ourselves,
+ * called ieee80211_sched_scan_stopped() and the userspace called
+ * stop sched scan scan before ieee80211_sched_scan_stopped_work()
+ * could run. To handle this, simply return if the scan is
+ * not running.
+ */
+ /* FIXME: for now, we ignore this race for UMAC scans, since
+ * they don't set the scan_status.
+ */
+ if (mvm->scan_status != IWL_MVM_SCAN_SCHED &&
+ !(mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
+ mutex_unlock(&mvm->mutex);
+ return 0;
+ }
+
ret = iwl_mvm_scan_offload_stop(mvm, false);
mutex_unlock(&mvm->mutex);
iwl_mvm_wait_for_async_handlers(mvm);
return ret;
-
}
static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
#define MAX_NEXT_COLUMNS 7
#define MAX_COLUMN_CHECKS 3
+struct rs_tx_column;
+
typedef bool (*allow_column_func_t) (struct iwl_mvm *mvm,
struct ieee80211_sta *sta,
- struct iwl_scale_tbl_info *tbl);
+ struct iwl_scale_tbl_info *tbl,
+ const struct rs_tx_column *next_col);
struct rs_tx_column {
enum rs_column_mode mode;
};
static bool rs_ant_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
- struct iwl_scale_tbl_info *tbl)
+ struct iwl_scale_tbl_info *tbl,
+ const struct rs_tx_column *next_col)
{
- return iwl_mvm_bt_coex_is_ant_avail(mvm, tbl->rate.ant);
+ return iwl_mvm_bt_coex_is_ant_avail(mvm, next_col->ant);
}
static bool rs_mimo_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
- struct iwl_scale_tbl_info *tbl)
+ struct iwl_scale_tbl_info *tbl,
+ const struct rs_tx_column *next_col)
{
if (!sta->ht_cap.ht_supported)
return false;
}
static bool rs_siso_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
- struct iwl_scale_tbl_info *tbl)
+ struct iwl_scale_tbl_info *tbl,
+ const struct rs_tx_column *next_col)
{
if (!sta->ht_cap.ht_supported)
return false;
}
static bool rs_sgi_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
- struct iwl_scale_tbl_info *tbl)
+ struct iwl_scale_tbl_info *tbl,
+ const struct rs_tx_column *next_col)
{
struct rs_rate *rate = &tbl->rate;
struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ if (!iwl_mvm_sta_from_mac80211(sta)->vif)
+ return;
+
if (!ieee80211_is_data(hdr->frame_control) ||
info->flags & IEEE80211_TX_CTL_NO_ACK)
return;
for (j = 0; j < MAX_COLUMN_CHECKS; j++) {
allow_func = next_col->checks[j];
- if (allow_func && !allow_func(mvm, sta, tbl))
+ if (allow_func && !allow_func(mvm, sta, tbl, next_col))
break;
}
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct iwl_lq_sta *lq_sta = mvm_sta;
+ if (sta && !iwl_mvm_sta_from_mac80211(sta)->vif) {
+ /* if vif isn't initialized mvm doesn't know about
+ * this station, so don't do anything with the it
+ */
+ sta = NULL;
+ mvm_sta = NULL;
+ }
+
/* TODO: handle rate_idx_mask and rate_idx_mcs_mask */
/* Treat uninitialized rate scaling data same as non-existing. */
(struct iwl_op_mode *)mvm_r;
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+ if (!iwl_mvm_sta_from_mac80211(sta)->vif)
+ return;
+
/* Stop any ongoing aggregations as rs starts off assuming no agg */
for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++)
ieee80211_stop_tx_ba_session(sta, tid);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(ss_force, 32);
-static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir)
+static void rs_add_debugfs(void *mvm, void *priv_sta, struct dentry *dir)
{
- struct iwl_lq_sta *lq_sta = mvm_sta;
+ struct iwl_lq_sta *lq_sta = priv_sta;
+ struct iwl_mvm_sta *mvmsta;
+
+ mvmsta = container_of(lq_sta, struct iwl_mvm_sta, lq_sta);
+
+ if (!mvmsta->vif)
+ return;
debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir,
lq_sta, &rs_sta_dbgfs_scale_table_ops);
if (mvm->scan_status == IWL_MVM_SCAN_NONE)
return 0;
- if (iwl_mvm_is_radio_killed(mvm))
+ if (iwl_mvm_is_radio_killed(mvm)) {
+ ret = 0;
goto out;
+ }
if (mvm->scan_status != IWL_MVM_SCAN_SCHED &&
(!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) ||
IWL_DEBUG_SCAN(mvm, "Send stop %sscan failed %d\n",
sched ? "offloaded " : "", ret);
iwl_remove_notification(&mvm->notif_wait, &wait_scan_done);
- return ret;
+ goto out;
}
IWL_DEBUG_SCAN(mvm, "Successfully sent stop %sscan\n",
sched ? "offloaded " : "");
ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_done, 1 * HZ);
- if (ret)
- return ret;
-
+out:
/*
* Clear the scan status so the next scan requests will succeed. This
* also ensures the Rx handler doesn't do anything, as the scan was
if (mvm->scan_status == IWL_MVM_SCAN_OS)
iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
-out:
mvm->scan_status = IWL_MVM_SCAN_NONE;
if (notify) {
ieee80211_scan_completed(mvm->hw, true);
}
- return 0;
+ return ret;
}
static void iwl_mvm_unified_scan_fill_tx_cmd(struct iwl_mvm *mvm,
struct iwl_time_event_notif *notif)
{
if (!le32_to_cpu(notif->status)) {
+ if (te_data->vif->type == NL80211_IFTYPE_STATION)
+ ieee80211_connection_loss(te_data->vif);
IWL_DEBUG_TE(mvm, "CSA time event failed to start\n");
iwl_mvm_te_clear_data(mvm, te_data);
return;
* request
*/
list_for_each_entry(te_data, &mvm->time_event_list, list) {
- if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE &&
- te_data->running) {
+ if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
is_p2p = true;
goto remove_te;
* request
*/
list_for_each_entry(te_data, &mvm->aux_roc_te_list, list) {
- if (te_data->running) {
- mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
- goto remove_te;
- }
+ mvmvif = iwl_mvm_vif_from_mac80211(te_data->vif);
+ goto remove_te;
}
remove_te:
mvmsta = iwl_mvm_sta_from_mac80211(sta);
tid_data = &mvmsta->tid_data[tid];
- if (WARN_ONCE(tid_data->txq_id != scd_flow, "Q %d, tid %d, flow %d",
- tid_data->txq_id, tid, scd_flow)) {
+ if (tid_data->txq_id != scd_flow) {
+ IWL_ERR(mvm,
+ "invalid BA notification: Q %d, tid %d, flow %d\n",
+ tid_data->txq_id, tid, scd_flow);
rcu_read_unlock();
return 0;
}
/* 3165 Series */
{IWL_PCI_DEVICE(0x3165, 0x4010, iwl3165_2ac_cfg)},
{IWL_PCI_DEVICE(0x3165, 0x4012, iwl3165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x3165, 0x4110, iwl3165_2ac_cfg)},
- {IWL_PCI_DEVICE(0x3165, 0x4210, iwl3165_2ac_cfg)},
{IWL_PCI_DEVICE(0x3165, 0x4410, iwl3165_2ac_cfg)},
{IWL_PCI_DEVICE(0x3165, 0x4510, iwl3165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x3165, 0x4110, iwl3165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x3166, 0x4310, iwl3165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x3166, 0x4210, iwl3165_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x3165, 0x8010, iwl3165_2ac_cfg)},
/* 7265 Series */
{IWL_PCI_DEVICE(0x095A, 0x5010, iwl7265_2ac_cfg)},
goto nla_put_failure;
genlmsg_end(skb, msg_head);
- genlmsg_unicast(&init_net, skb, dst_portid);
+ if (genlmsg_unicast(&init_net, skb, dst_portid))
+ goto err_free_txskb;
/* Enqueue the packet */
skb_queue_tail(&data->pending, my_skb);
return;
nla_put_failure:
+ nlmsg_free(skb);
+err_free_txskb:
printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__);
ieee80211_free_txskb(hw, my_skb);
data->tx_failed++;
}
return true;
- } else if (0x86DD == ether_type) {
- return true;
+ } else if (ETH_P_IPV6 == ether_type) {
+ /* TODO: Handle any IPv6 cases that need special handling.
+ * For now, always return false
+ */
+ goto end;
}
end:
/*This is for new trx flow*/
struct rtl_tx_buffer_desc *pbuffer_desc = NULL;
u8 temp_one = 1;
+ u8 *entry;
memset(&tcb_desc, 0, sizeof(struct rtl_tcb_desc));
ring = &rtlpci->tx_ring[BEACON_QUEUE];
pskb = __skb_dequeue(&ring->queue);
- if (pskb)
+ if (rtlpriv->use_new_trx_flow)
+ entry = (u8 *)(&ring->buffer_desc[ring->idx]);
+ else
+ entry = (u8 *)(&ring->desc[ring->idx]);
+ if (pskb) {
+ pci_unmap_single(rtlpci->pdev,
+ rtlpriv->cfg->ops->get_desc(
+ (u8 *)entry, true, HW_DESC_TXBUFF_ADDR),
+ pskb->len, PCI_DMA_TODEVICE);
kfree_skb(pskb);
+ }
/*NB: the beacon data buffer must be 32-bit aligned. */
pskb = ieee80211_beacon_get(hw, mac->vif);
unsigned int num_queues = vif->num_queues;
int i;
unsigned int queue_index;
- struct xenvif_stats *vif_stats;
for (i = 0; i < ARRAY_SIZE(xenvif_stats); i++) {
unsigned long accum = 0;
for (queue_index = 0; queue_index < num_queues; ++queue_index) {
- vif_stats = &vif->queues[queue_index].stats;
+ void *vif_stats = &vif->queues[queue_index].stats;
accum += *(unsigned long *)(vif_stats + xenvif_stats[i].offset);
}
data[i] = accum;
static void make_tx_response(struct xenvif_queue *queue,
struct xen_netif_tx_request *txp,
s8 st);
+static void push_tx_responses(struct xenvif_queue *queue);
static inline int tx_work_todo(struct xenvif_queue *queue);
do {
spin_lock_irqsave(&queue->response_lock, flags);
make_tx_response(queue, txp, XEN_NETIF_RSP_ERROR);
+ push_tx_responses(queue);
spin_unlock_irqrestore(&queue->response_lock, flags);
if (cons == end)
break;
{
unsigned int offset = skb_headlen(skb);
skb_frag_t frags[MAX_SKB_FRAGS];
- int i;
+ int i, f;
struct ubuf_info *uarg;
struct sk_buff *nskb = skb_shinfo(skb)->frag_list;
frags[i].page_offset = 0;
skb_frag_size_set(&frags[i], len);
}
- /* swap out with old one */
- memcpy(skb_shinfo(skb)->frags,
- frags,
- i * sizeof(skb_frag_t));
- skb_shinfo(skb)->nr_frags = i;
- skb->truesize += i * PAGE_SIZE;
- /* remove traces of mapped pages and frag_list */
+ /* Copied all the bits from the frag list -- free it. */
skb_frag_list_init(skb);
+ xenvif_skb_zerocopy_prepare(queue, nskb);
+ kfree_skb(nskb);
+
+ /* Release all the original (foreign) frags. */
+ for (f = 0; f < skb_shinfo(skb)->nr_frags; f++)
+ skb_frag_unref(skb, f);
uarg = skb_shinfo(skb)->destructor_arg;
/* increase inflight counter to offset decrement in callback */
atomic_inc(&queue->inflight_packets);
uarg->callback(uarg, true);
skb_shinfo(skb)->destructor_arg = NULL;
- xenvif_skb_zerocopy_prepare(queue, nskb);
- kfree_skb(nskb);
+ /* Fill the skb with the new (local) frags. */
+ memcpy(skb_shinfo(skb)->frags, frags, i * sizeof(skb_frag_t));
+ skb_shinfo(skb)->nr_frags = i;
+ skb->truesize += i * PAGE_SIZE;
return 0;
}
unsigned long flags;
pending_tx_info = &queue->pending_tx_info[pending_idx];
+
spin_lock_irqsave(&queue->response_lock, flags);
+
make_tx_response(queue, &pending_tx_info->req, status);
- index = pending_index(queue->pending_prod);
+
+ /* Release the pending index before pusing the Tx response so
+ * its available before a new Tx request is pushed by the
+ * frontend.
+ */
+ index = pending_index(queue->pending_prod++);
queue->pending_ring[index] = pending_idx;
- /* TX shouldn't use the index before we give it back here */
- mb();
- queue->pending_prod++;
+
+ push_tx_responses(queue);
+
spin_unlock_irqrestore(&queue->response_lock, flags);
}
{
RING_IDX i = queue->tx.rsp_prod_pvt;
struct xen_netif_tx_response *resp;
- int notify;
resp = RING_GET_RESPONSE(&queue->tx, i);
resp->id = txp->id;
RING_GET_RESPONSE(&queue->tx, ++i)->status = XEN_NETIF_RSP_NULL;
queue->tx.rsp_prod_pvt = ++i;
+}
+
+static void push_tx_responses(struct xenvif_queue *queue)
+{
+ int notify;
+
RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&queue->tx, notify);
if (notify)
notify_remote_via_irq(queue->tx_irq);
static int xennet_change_mtu(struct net_device *dev, int mtu)
{
- int max = xennet_can_sg(dev) ?
- XEN_NETIF_MAX_TX_SIZE - MAX_TCP_HEADER : ETH_DATA_LEN;
+ int max = xennet_can_sg(dev) ? XEN_NETIF_MAX_TX_SIZE : ETH_DATA_LEN;
if (mtu > max)
return -EINVAL;
netdev->ethtool_ops = &xennet_ethtool_ops;
SET_NETDEV_DEV(netdev, &dev->dev);
- netif_set_gso_max_size(netdev, XEN_NETIF_MAX_TX_SIZE - MAX_TCP_HEADER);
-
np->netdev = netdev;
netif_carrier_off(netdev);
bool
config OF_OVERLAY
- bool
- depends on OF
+ bool "Device Tree overlays"
select OF_DYNAMIC
select OF_RESOLVE
return NULL;
}
-static int of_empty_ranges_quirk(void)
+static int of_empty_ranges_quirk(struct device_node *np)
{
if (IS_ENABLED(CONFIG_PPC)) {
- /* To save cycles, we cache the result */
+ /* To save cycles, we cache the result for global "Mac" setting */
static int quirk_state = -1;
+ /* PA-SEMI sdc DT bug */
+ if (of_device_is_compatible(np, "1682m-sdc"))
+ return true;
+
+ /* Make quirk cached */
if (quirk_state < 0)
quirk_state =
of_machine_is_compatible("Power Macintosh") ||
* This code is only enabled on powerpc. --gcl
*/
ranges = of_get_property(parent, rprop, &rlen);
- if (ranges == NULL && !of_empty_ranges_quirk()) {
+ if (ranges == NULL && !of_empty_ranges_quirk(parent)) {
pr_debug("OF: no ranges; cannot translate\n");
return 1;
}
const char *path)
{
struct device_node *child;
- int len = strchrnul(path, '/') - path;
- int term;
+ int len;
+ len = strcspn(path, "/:");
if (!len)
return NULL;
- term = strchrnul(path, ':') - path;
- if (term < len)
- len = term;
-
__for_each_child_of_node(parent, child) {
const char *name = strrchr(child->full_name, '/');
if (WARN(!name, "malformed device_node %s\n", child->full_name))
/* The path could begin with an alias */
if (*path != '/') {
- char *p = strchrnul(path, '/');
- int len = separator ? separator - path : p - path;
+ int len;
+ const char *p = separator;
+
+ if (!p)
+ p = strchrnul(path, '/');
+ len = p - path;
/* of_aliases must not be NULL */
if (!of_aliases)
path++; /* Increment past '/' delimiter */
np = __of_find_node_by_path(np, path);
path = strchrnul(path, '/');
+ if (separator && separator < path)
+ break;
}
raw_spin_unlock_irqrestore(&devtree_lock, flags);
return np;
struct device_node *p;
const __be32 *intspec, *tmp, *addr;
u32 intsize, intlen;
- int i, res = -EINVAL;
+ int i, res;
pr_debug("of_irq_parse_one: dev=%s, index=%d\n", of_node_full_name(device), index);
/* Get size of interrupt specifier */
tmp = of_get_property(p, "#interrupt-cells", NULL);
- if (tmp == NULL)
+ if (tmp == NULL) {
+ res = -EINVAL;
goto out;
+ }
intsize = be32_to_cpu(*tmp);
pr_debug(" intsize=%d intlen=%d\n", intsize, intlen);
/* Check index */
- if ((index + 1) * intsize > intlen)
+ if ((index + 1) * intsize > intlen) {
+ res = -EINVAL;
goto out;
+ }
/* Copy intspec into irq structure */
intspec += index * intsize;
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/err.h>
+#include <linux/idr.h>
#include "of_private.h"
struct device_node *target, struct device_node *child)
{
const char *cname;
- struct device_node *tchild, *grandchild;
+ struct device_node *tchild;
int ret = 0;
cname = kbasename(child->full_name);
"option path test failed\n");
of_node_put(np);
+ np = of_find_node_opts_by_path("/testcase-data:test/option", &options);
+ selftest(np && !strcmp("test/option", options),
+ "option path test, subcase #1 failed\n");
+ of_node_put(np);
+
+ np = of_find_node_opts_by_path("/testcase-data/testcase-device1:test/option", &options);
+ selftest(np && !strcmp("test/option", options),
+ "option path test, subcase #2 failed\n");
+ of_node_put(np);
+
np = of_find_node_opts_by_path("/testcase-data:testoption", NULL);
selftest(np, "NULL option path test failed\n");
of_node_put(np);
"option alias path test failed\n");
of_node_put(np);
+ np = of_find_node_opts_by_path("testcase-alias:test/alias/option",
+ &options);
+ selftest(np && !strcmp("test/alias/option", options),
+ "option alias path test, subcase #1 failed\n");
+ of_node_put(np);
+
np = of_find_node_opts_by_path("testcase-alias:testaliasoption", NULL);
selftest(np, "NULL option alias path test failed\n");
of_node_put(np);
rc = of_property_match_string(np, "phandle-list-names", "first");
selftest(rc == 0, "first expected:0 got:%i\n", rc);
rc = of_property_match_string(np, "phandle-list-names", "second");
- selftest(rc == 1, "second expected:0 got:%i\n", rc);
+ selftest(rc == 1, "second expected:1 got:%i\n", rc);
rc = of_property_match_string(np, "phandle-list-names", "third");
- selftest(rc == 2, "third expected:0 got:%i\n", rc);
+ selftest(rc == 2, "third expected:2 got:%i\n", rc);
rc = of_property_match_string(np, "phandle-list-names", "fourth");
selftest(rc == -ENODATA, "unmatched string; rc=%i\n", rc);
rc = of_property_match_string(np, "missing-property", "blah");
struct device_node *n1, *n2, *n21, *nremove, *parent, *np;
struct of_changeset chgset;
- of_changeset_init(&chgset);
n1 = __of_node_dup(NULL, "/testcase-data/changeset/n1");
selftest(n1, "testcase setup failure\n");
n2 = __of_node_dup(NULL, "/testcase-data/changeset/n2");
return pdev != NULL;
}
-#if IS_ENABLED(CONFIG_I2C)
+#if IS_BUILTIN(CONFIG_I2C)
/* get the i2c client device instantiated at the path */
static struct i2c_client *of_path_to_i2c_client(const char *path)
return;
}
-#if IS_ENABLED(CONFIG_I2C) && IS_ENABLED(CONFIG_OF_OVERLAY)
+#if IS_BUILTIN(CONFIG_I2C) && IS_ENABLED(CONFIG_OF_OVERLAY)
struct selftest_i2c_bus_data {
struct platform_device *pdev;
.id_table = selftest_i2c_dev_id,
};
-#if IS_ENABLED(CONFIG_I2C_MUX)
+#if IS_BUILTIN(CONFIG_I2C_MUX)
struct selftest_i2c_mux_data {
int nchans;
"could not register selftest i2c bus driver\n"))
return ret;
-#if IS_ENABLED(CONFIG_I2C_MUX)
+#if IS_BUILTIN(CONFIG_I2C_MUX)
ret = i2c_add_driver(&selftest_i2c_mux_driver);
if (selftest(ret == 0,
"could not register selftest i2c mux driver\n"))
static void of_selftest_overlay_i2c_cleanup(void)
{
-#if IS_ENABLED(CONFIG_I2C_MUX)
+#if IS_BUILTIN(CONFIG_I2C_MUX)
i2c_del_driver(&selftest_i2c_mux_driver);
#endif
platform_driver_unregister(&selftest_i2c_bus_driver);
of_selftest_overlay_10();
of_selftest_overlay_11();
-#if IS_ENABLED(CONFIG_I2C)
+#if IS_BUILTIN(CONFIG_I2C)
if (selftest(of_selftest_overlay_i2c_init() == 0, "i2c init failed\n"))
goto out;
if (err)
return err;
- resource_list_for_each_entry(win, res, list) {
+ resource_list_for_each_entry(win, res) {
struct resource *parent, *res = win->res;
switch (resource_type(res)) {
return false;
}
-static int xgene_pcie_map_bus(struct pci_bus *bus, unsigned int devfn,
+static void __iomem *xgene_pcie_map_bus(struct pci_bus *bus, unsigned int devfn,
int offset)
{
struct xgene_pcie_port *port = bus->sysdata;
return NULL;
xgene_pcie_set_rtdid_reg(bus, devfn);
- return xgene_pcie_get_cfg_base(bus);
+ return xgene_pcie_get_cfg_base(bus) + offset;
}
static struct pci_ops xgene_pcie_ops = {
struct pci_dev *pdev = to_pci_dev(dev);
char *driver_override, *old = pdev->driver_override, *cp;
- if (count > PATH_MAX)
+ /* We need to keep extra room for a newline */
+ if (count >= (PAGE_SIZE - 1))
return -EINVAL;
driver_override = kstrndup(buf, count, GFP_KERNEL);
{
struct pci_dev *pdev = to_pci_dev(dev);
- return sprintf(buf, "%s\n", pdev->driver_override);
+ return snprintf(buf, PAGE_SIZE, "%s\n", pdev->driver_override);
}
static DEVICE_ATTR_RW(driver_override);
tristate "CardBus yenta-compatible bridge support"
depends on PCI
select CARDBUS if !EXPERT
- select PCCARD_NONSTATIC if PCMCIA != n && ISA
- select PCCARD_PCI if PCMCIA !=n && !ISA
+ select PCCARD_NONSTATIC if PCMCIA != n
---help---
This option enables support for CardBus host bridges. Virtually
all modern PCMCIA bridges are CardBus compatible. A "bridge" is
config PD6729
tristate "Cirrus PD6729 compatible bridge support"
depends on PCMCIA && PCI
- select PCCARD_NONSTATIC if PCMCIA != n && ISA
- select PCCARD_PCI if PCMCIA !=n && !ISA
+ select PCCARD_NONSTATIC
help
This provides support for the Cirrus PD6729 PCI-to-PCMCIA bridge
device, found in some older laptops and PCMCIA card readers.
config I82092
tristate "i82092 compatible bridge support"
depends on PCMCIA && PCI
- select PCCARD_NONSTATIC if PCMCIA != n && ISA
- select PCCARD_PCI if PCMCIA !=n && !ISA
+ select PCCARD_NONSTATIC
help
This provides support for the Intel I82092AA PCI-to-PCMCIA bridge device,
found in some older laptops and more commonly in evaluation boards for the
Say Y here to support the CompactFlash controller on the
PA Semi Electra eval board.
-config PCCARD_PCI
- bool
-
config PCCARD_NONSTATIC
bool
pcmcia_rsrc-y += rsrc_mgr.o
pcmcia_rsrc-$(CONFIG_PCCARD_NONSTATIC) += rsrc_nonstatic.o
pcmcia_rsrc-$(CONFIG_PCCARD_IODYN) += rsrc_iodyn.o
-pcmcia_rsrc-$(CONFIG_PCCARD_PCI) += rsrc_pci.o
obj-$(CONFIG_PCCARD) += pcmcia_rsrc.o
+++ /dev/null
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/pci.h>
-
-#include <pcmcia/ss.h>
-#include <pcmcia/cistpl.h>
-#include "cs_internal.h"
-
-
-struct pcmcia_align_data {
- unsigned long mask;
- unsigned long offset;
-};
-
-static resource_size_t pcmcia_align(void *align_data,
- const struct resource *res,
- resource_size_t size, resource_size_t align)
-{
- struct pcmcia_align_data *data = align_data;
- resource_size_t start;
-
- start = (res->start & ~data->mask) + data->offset;
- if (start < res->start)
- start += data->mask + 1;
- return start;
-}
-
-static struct resource *find_io_region(struct pcmcia_socket *s,
- unsigned long base, int num,
- unsigned long align)
-{
- struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_IO,
- dev_name(&s->dev));
- struct pcmcia_align_data data;
- int ret;
-
- data.mask = align - 1;
- data.offset = base & data.mask;
-
- ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
- base, 0, pcmcia_align, &data);
- if (ret != 0) {
- kfree(res);
- res = NULL;
- }
- return res;
-}
-
-static int res_pci_find_io(struct pcmcia_socket *s, unsigned int attr,
- unsigned int *base, unsigned int num,
- unsigned int align, struct resource **parent)
-{
- int i, ret = 0;
-
- /* Check for an already-allocated window that must conflict with
- * what was asked for. It is a hack because it does not catch all
- * potential conflicts, just the most obvious ones.
- */
- for (i = 0; i < MAX_IO_WIN; i++) {
- if (!s->io[i].res)
- continue;
-
- if (!*base)
- continue;
-
- if ((s->io[i].res->start & (align-1)) == *base)
- return -EBUSY;
- }
-
- for (i = 0; i < MAX_IO_WIN; i++) {
- struct resource *res = s->io[i].res;
- unsigned int try;
-
- if (res && (res->flags & IORESOURCE_BITS) !=
- (attr & IORESOURCE_BITS))
- continue;
-
- if (!res) {
- if (align == 0)
- align = 0x10000;
-
- res = s->io[i].res = find_io_region(s, *base, num,
- align);
- if (!res)
- return -EINVAL;
-
- *base = res->start;
- s->io[i].res->flags =
- ((res->flags & ~IORESOURCE_BITS) |
- (attr & IORESOURCE_BITS));
- s->io[i].InUse = num;
- *parent = res;
- return 0;
- }
-
- /* Try to extend top of window */
- try = res->end + 1;
- if ((*base == 0) || (*base == try)) {
- ret = adjust_resource(s->io[i].res, res->start,
- resource_size(res) + num);
- if (ret)
- continue;
- *base = try;
- s->io[i].InUse += num;
- *parent = res;
- return 0;
- }
-
- /* Try to extend bottom of window */
- try = res->start - num;
- if ((*base == 0) || (*base == try)) {
- ret = adjust_resource(s->io[i].res,
- res->start - num,
- resource_size(res) + num);
- if (ret)
- continue;
- *base = try;
- s->io[i].InUse += num;
- *parent = res;
- return 0;
- }
- }
- return -EINVAL;
-}
-
-static struct resource *res_pci_find_mem(u_long base, u_long num,
- u_long align, int low, struct pcmcia_socket *s)
-{
- struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_MEM,
- dev_name(&s->dev));
- struct pcmcia_align_data data;
- unsigned long min;
- int ret;
-
- if (align < 0x20000)
- align = 0x20000;
- data.mask = align - 1;
- data.offset = base & data.mask;
-
- min = 0;
- if (!low)
- min = 0x100000UL;
-
- ret = pci_bus_alloc_resource(s->cb_dev->bus,
- res, num, 1, min, 0,
- pcmcia_align, &data);
-
- if (ret != 0) {
- kfree(res);
- res = NULL;
- }
- return res;
-}
-
-
-static int res_pci_init(struct pcmcia_socket *s)
-{
- if (!s->cb_dev || !(s->features & SS_CAP_PAGE_REGS)) {
- dev_err(&s->dev, "not supported by res_pci\n");
- return -EOPNOTSUPP;
- }
- return 0;
-}
-
-struct pccard_resource_ops pccard_nonstatic_ops = {
- .validate_mem = NULL,
- .find_io = res_pci_find_io,
- .find_mem = res_pci_find_mem,
- .init = res_pci_init,
- .exit = NULL,
-};
-EXPORT_SYMBOL(pccard_nonstatic_ops);
struct armada375_cluster_phy *cluster_phy;
u32 reg;
- cluster_phy = dev_get_drvdata(phy->dev.parent);
+ cluster_phy = phy_get_drvdata(phy);
if (!cluster_phy)
return -ENODEV;
cluster_phy->reg = usb_cluster_base;
dev_set_drvdata(dev, cluster_phy);
+ phy_set_drvdata(phy, cluster_phy);
phy_provider = devm_of_phy_provider_register(&pdev->dev,
armada375_usb_phy_xlate);
static int devm_phy_match(struct device *dev, void *res, void *match_data)
{
- return res == match_data;
+ struct phy **phy = res;
+
+ return *phy == match_data;
}
/**
ret = phy_pm_runtime_get_sync(phy);
if (ret < 0 && ret != -ENOTSUPP)
return ret;
+ ret = 0; /* Override possible ret == -ENOTSUPP */
mutex_lock(&phy->mutex);
if (phy->init_count == 0 && phy->ops->init) {
dev_err(&phy->dev, "phy init failed --> %d\n", ret);
goto out;
}
- } else {
- ret = 0; /* Override possible ret == -ENOTSUPP */
}
++phy->init_count;
ret = phy_pm_runtime_get_sync(phy);
if (ret < 0 && ret != -ENOTSUPP)
return ret;
+ ret = 0; /* Override possible ret == -ENOTSUPP */
mutex_lock(&phy->mutex);
if (phy->init_count == 1 && phy->ops->exit) {
ret = phy_pm_runtime_get_sync(phy);
if (ret < 0 && ret != -ENOTSUPP)
return ret;
+ ret = 0; /* Override possible ret == -ENOTSUPP */
mutex_lock(&phy->mutex);
if (phy->power_count == 0 && phy->ops->power_on) {
dev_err(&phy->dev, "phy poweron failed --> %d\n", ret);
goto out;
}
- } else {
- ret = 0; /* Override possible ret == -ENOTSUPP */
}
++phy->power_count;
mutex_unlock(&phy->mutex);
const struct exynos_dp_video_phy_drvdata *drvdata;
};
-static void exynos_dp_video_phy_pwr_isol(struct exynos_dp_video_phy *state,
- unsigned int on)
-{
- unsigned int val;
-
- if (IS_ERR(state->regs))
- return;
-
- val = on ? 0 : EXYNOS5_PHY_ENABLE;
-
- regmap_update_bits(state->regs, state->drvdata->phy_ctrl_offset,
- EXYNOS5_PHY_ENABLE, val);
-}
-
static int exynos_dp_video_phy_power_on(struct phy *phy)
{
struct exynos_dp_video_phy *state = phy_get_drvdata(phy);
/* Disable power isolation on DP-PHY */
- exynos_dp_video_phy_pwr_isol(state, 0);
-
- return 0;
+ return regmap_update_bits(state->regs, state->drvdata->phy_ctrl_offset,
+ EXYNOS5_PHY_ENABLE, EXYNOS5_PHY_ENABLE);
}
static int exynos_dp_video_phy_power_off(struct phy *phy)
struct exynos_dp_video_phy *state = phy_get_drvdata(phy);
/* Enable power isolation on DP-PHY */
- exynos_dp_video_phy_pwr_isol(state, 1);
-
- return 0;
+ return regmap_update_bits(state->regs, state->drvdata->phy_ctrl_offset,
+ EXYNOS5_PHY_ENABLE, 0);
}
static struct phy_ops exynos_dp_video_phy_ops = {
} phys[EXYNOS_MIPI_PHYS_NUM];
spinlock_t slock;
void __iomem *regs;
- struct mutex mutex;
struct regmap *regmap;
};
else
reset = EXYNOS4_MIPI_PHY_SRESETN;
- if (state->regmap) {
- mutex_lock(&state->mutex);
+ spin_lock(&state->slock);
+
+ if (!IS_ERR(state->regmap)) {
regmap_read(state->regmap, offset, &val);
if (on)
val |= reset;
else if (!(val & EXYNOS4_MIPI_PHY_RESET_MASK))
val &= ~EXYNOS4_MIPI_PHY_ENABLE;
regmap_write(state->regmap, offset, val);
- mutex_unlock(&state->mutex);
} else {
addr = state->regs + EXYNOS_MIPI_PHY_CONTROL(id / 2);
- spin_lock(&state->slock);
val = readl(addr);
if (on)
val |= reset;
val &= ~EXYNOS4_MIPI_PHY_ENABLE;
writel(val, addr);
- spin_unlock(&state->slock);
}
+ spin_unlock(&state->slock);
return 0;
}
dev_set_drvdata(dev, state);
spin_lock_init(&state->slock);
- mutex_init(&state->mutex);
for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) {
struct phy *phy = devm_phy_create(dev, NULL,
.power_on = exynos4210_power_on,
.power_off = exynos4210_power_off,
},
- {},
};
const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
.power_on = exynos4x12_power_on,
.power_off = exynos4x12_power_off,
},
- {},
};
const struct samsung_usb2_phy_config exynos3250_usb2_phy_config = {
{
struct exynos5_usbdrd_phy *phy_drd = dev_get_drvdata(dev);
- if (WARN_ON(args->args[0] > EXYNOS5_DRDPHYS_NUM))
+ if (WARN_ON(args->args[0] >= EXYNOS5_DRDPHYS_NUM))
return ERR_PTR(-ENODEV);
return phy_drd->phys[args->args[0]].phy;
.power_on = exynos5250_power_on,
.power_off = exynos5250_power_off,
},
- {},
};
const struct samsung_usb2_phy_config exynos5250_usb2_phy_config = {
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
priv->base = devm_ioremap(dev, res->start, resource_size(res));
if (!priv->base)
return -ENOMEM;
struct regmap *regmap;
struct mutex miphy_mutex;
struct miphy28lp_phy **phys;
+ int nphys;
};
struct miphy_initval {
return ERR_PTR(-EINVAL);
}
- for (index = 0; index < of_get_child_count(dev->of_node); index++)
+ for (index = 0; index < miphy_dev->nphys; index++)
if (phynode == miphy_dev->phys[index]->phy->dev.of_node) {
miphy_phy = miphy_dev->phys[index];
break;
static struct phy_ops miphy28lp_ops = {
.init = miphy28lp_init,
+ .owner = THIS_MODULE,
};
static int miphy28lp_probe_resets(struct device_node *node,
struct miphy28lp_dev *miphy_dev;
struct phy_provider *provider;
struct phy *phy;
- int chancount, port = 0;
- int ret;
+ int ret, port = 0;
miphy_dev = devm_kzalloc(&pdev->dev, sizeof(*miphy_dev), GFP_KERNEL);
if (!miphy_dev)
return -ENOMEM;
- chancount = of_get_child_count(np);
- miphy_dev->phys = devm_kzalloc(&pdev->dev, sizeof(phy) * chancount,
- GFP_KERNEL);
+ miphy_dev->nphys = of_get_child_count(np);
+ miphy_dev->phys = devm_kcalloc(&pdev->dev, miphy_dev->nphys,
+ sizeof(*miphy_dev->phys), GFP_KERNEL);
if (!miphy_dev->phys)
return -ENOMEM;
struct regmap *regmap;
struct mutex miphy_mutex;
struct miphy365x_phy **phys;
+ int nphys;
};
/*
return ERR_PTR(-EINVAL);
}
- for (index = 0; index < of_get_child_count(dev->of_node); index++)
+ for (index = 0; index < miphy_dev->nphys; index++)
if (phynode == miphy_dev->phys[index]->phy->dev.of_node) {
miphy_phy = miphy_dev->phys[index];
break;
struct miphy365x_dev *miphy_dev;
struct phy_provider *provider;
struct phy *phy;
- int chancount, port = 0;
- int ret;
+ int ret, port = 0;
miphy_dev = devm_kzalloc(&pdev->dev, sizeof(*miphy_dev), GFP_KERNEL);
if (!miphy_dev)
return -ENOMEM;
- chancount = of_get_child_count(np);
- miphy_dev->phys = devm_kzalloc(&pdev->dev, sizeof(phy) * chancount,
- GFP_KERNEL);
+ miphy_dev->nphys = of_get_child_count(np);
+ miphy_dev->phys = devm_kcalloc(&pdev->dev, miphy_dev->nphys,
+ sizeof(*miphy_dev->phys), GFP_KERNEL);
if (!miphy_dev->phys)
return -ENOMEM;
}
module_exit(omap_control_phy_exit);
-MODULE_ALIAS("platform: omap_control_phy");
+MODULE_ALIAS("platform:omap_control_phy");
MODULE_AUTHOR("Texas Instruments Inc.");
MODULE_DESCRIPTION("OMAP Control Module PHY Driver");
MODULE_LICENSE("GPL v2");
dev_warn(&pdev->dev,
"found usb_otg_ss_refclk960m, please fix DTS\n");
}
- } else {
- clk_prepare(phy->optclk);
}
+ if (!IS_ERR(phy->optclk))
+ clk_prepare(phy->optclk);
+
usb_add_phy_dev(&phy->phy);
return 0;
module_platform_driver(omap_usb2_driver);
-MODULE_ALIAS("platform: omap_usb2");
+MODULE_ALIAS("platform:omap_usb2");
MODULE_AUTHOR("Texas Instruments Inc.");
MODULE_DESCRIPTION("OMAP USB2 phy driver");
MODULE_LICENSE("GPL v2");
return ret;
clk_disable_unprepare(phy->clk);
- if (ret)
- return ret;
return 0;
}
/* Power up usb phy analog blocks by set siddq 0 */
ret = rockchip_usb_phy_power(phy, 0);
- if (ret)
+ if (ret) {
+ clk_disable_unprepare(phy->clk);
return ret;
+ }
return 0;
}
cpu_relax();
val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS);
if (val & PLL_LOCK)
- break;
+ return 0;
} while (!time_after(jiffies, timeout));
- if (!(val & PLL_LOCK)) {
- dev_err(phy->dev, "DPLL failed to lock\n");
- return -EBUSY;
- }
-
- return 0;
+ dev_err(phy->dev, "DPLL failed to lock\n");
+ return -EBUSY;
}
static int ti_pipe3_dpll_program(struct ti_pipe3 *phy)
module_platform_driver(ti_pipe3_driver);
-MODULE_ALIAS("platform: ti_pipe3");
+MODULE_ALIAS("platform:ti_pipe3");
MODULE_AUTHOR("Texas Instruments Inc.");
MODULE_DESCRIPTION("TI PIPE3 phy driver");
MODULE_LICENSE("GPL v2");
twl->dev = &pdev->dev;
twl->irq = platform_get_irq(pdev, 0);
twl->vbus_supplied = false;
- twl->linkstat = -EINVAL;
twl->linkstat = OMAP_MUSB_UNKNOWN;
twl->phy.dev = twl->dev;
for (i = 0; i < MAX_LANE; i++)
ctx->sata_param.speed[i] = 2; /* Default to Gen3 */
- ctx->dev = &pdev->dev;
platform_set_drvdata(pdev, ctx);
ctx->phy = devm_phy_create(ctx->dev, NULL, &xgene_phy_ops);
#define BYT_DIR_MASK (BIT(1) | BIT(2))
#define BYT_TRIG_MASK (BIT(26) | BIT(25) | BIT(24))
+#define BYT_CONF0_RESTORE_MASK (BYT_DIRECT_IRQ_EN | BYT_TRIG_MASK | \
+ BYT_PIN_MUX)
+#define BYT_VAL_RESTORE_MASK (BYT_DIR_MASK | BYT_LEVEL)
+
#define BYT_NGPIO_SCORE 102
#define BYT_NGPIO_NCORE 28
#define BYT_NGPIO_SUS 44
},
};
+struct byt_gpio_pin_context {
+ u32 conf0;
+ u32 val;
+};
+
struct byt_gpio {
struct gpio_chip chip;
struct platform_device *pdev;
spinlock_t lock;
void __iomem *reg_base;
struct pinctrl_gpio_range *range;
+ struct byt_gpio_pin_context *saved_context;
};
#define to_byt_gpio(c) container_of(c, struct byt_gpio, chip)
return vg->reg_base + reg_offset + reg;
}
-static bool is_special_pin(struct byt_gpio *vg, unsigned offset)
+static void byt_gpio_clear_triggering(struct byt_gpio *vg, unsigned offset)
+{
+ void __iomem *reg = byt_gpio_reg(&vg->chip, offset, BYT_CONF0_REG);
+ unsigned long flags;
+ u32 value;
+
+ spin_lock_irqsave(&vg->lock, flags);
+ value = readl(reg);
+ value &= ~(BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL);
+ writel(value, reg);
+ spin_unlock_irqrestore(&vg->lock, flags);
+}
+
+static u32 byt_get_gpio_mux(struct byt_gpio *vg, unsigned offset)
{
/* SCORE pin 92-93 */
if (!strcmp(vg->range->name, BYT_SCORE_ACPI_UID) &&
offset >= 92 && offset <= 93)
- return true;
+ return 1;
/* SUS pin 11-21 */
if (!strcmp(vg->range->name, BYT_SUS_ACPI_UID) &&
offset >= 11 && offset <= 21)
- return true;
+ return 1;
- return false;
+ return 0;
}
static int byt_gpio_request(struct gpio_chip *chip, unsigned offset)
{
struct byt_gpio *vg = to_byt_gpio(chip);
void __iomem *reg = byt_gpio_reg(chip, offset, BYT_CONF0_REG);
- u32 value;
- bool special;
+ u32 value, gpio_mux;
/*
* In most cases, func pin mux 000 means GPIO function.
* But, some pins may have func pin mux 001 represents
- * GPIO function. Only allow user to export pin with
- * func pin mux preset as GPIO function by BIOS/FW.
+ * GPIO function.
+ *
+ * Because there are devices out there where some pins were not
+ * configured correctly we allow changing the mux value from
+ * request (but print out warning about that).
*/
value = readl(reg) & BYT_PIN_MUX;
- special = is_special_pin(vg, offset);
- if ((special && value != 1) || (!special && value)) {
- dev_err(&vg->pdev->dev,
- "pin %u cannot be used as GPIO.\n", offset);
- return -EINVAL;
+ gpio_mux = byt_get_gpio_mux(vg, offset);
+ if (WARN_ON(gpio_mux != value)) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&vg->lock, flags);
+ value = readl(reg) & ~BYT_PIN_MUX;
+ value |= gpio_mux;
+ writel(value, reg);
+ spin_unlock_irqrestore(&vg->lock, flags);
+
+ dev_warn(&vg->pdev->dev,
+ "pin %u forcibly re-configured as GPIO\n", offset);
}
pm_runtime_get(&vg->pdev->dev);
static void byt_gpio_free(struct gpio_chip *chip, unsigned offset)
{
struct byt_gpio *vg = to_byt_gpio(chip);
- void __iomem *reg = byt_gpio_reg(&vg->chip, offset, BYT_CONF0_REG);
- u32 value;
-
- /* clear interrupt triggering */
- value = readl(reg);
- value &= ~(BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL);
- writel(value, reg);
+ byt_gpio_clear_triggering(vg, offset);
pm_runtime_put(&vg->pdev->dev);
}
value &= ~(BYT_DIRECT_IRQ_EN | BYT_TRIG_POS | BYT_TRIG_NEG |
BYT_TRIG_LVL);
- switch (type) {
- case IRQ_TYPE_LEVEL_HIGH:
- value |= BYT_TRIG_LVL;
- case IRQ_TYPE_EDGE_RISING:
- value |= BYT_TRIG_POS;
- break;
- case IRQ_TYPE_LEVEL_LOW:
- value |= BYT_TRIG_LVL;
- case IRQ_TYPE_EDGE_FALLING:
- value |= BYT_TRIG_NEG;
- break;
- case IRQ_TYPE_EDGE_BOTH:
- value |= (BYT_TRIG_NEG | BYT_TRIG_POS);
- break;
- }
writel(value, reg);
+ if (type & IRQ_TYPE_EDGE_BOTH)
+ __irq_set_handler_locked(d->irq, handle_edge_irq);
+ else if (type & IRQ_TYPE_LEVEL_MASK)
+ __irq_set_handler_locked(d->irq, handle_level_irq);
+
spin_unlock_irqrestore(&vg->lock, flags);
return 0;
struct irq_data *data = irq_desc_get_irq_data(desc);
struct byt_gpio *vg = to_byt_gpio(irq_desc_get_handler_data(desc));
struct irq_chip *chip = irq_data_get_irq_chip(data);
- u32 base, pin, mask;
+ u32 base, pin;
void __iomem *reg;
- u32 pending;
+ unsigned long pending;
unsigned virq;
- int looplimit = 0;
/* check from GPIO controller which pin triggered the interrupt */
for (base = 0; base < vg->chip.ngpio; base += 32) {
-
reg = byt_gpio_reg(&vg->chip, base, BYT_INT_STAT_REG);
-
- while ((pending = readl(reg))) {
- pin = __ffs(pending);
- mask = BIT(pin);
- /* Clear before handling so we can't lose an edge */
- writel(mask, reg);
-
+ pending = readl(reg);
+ for_each_set_bit(pin, &pending, 32) {
virq = irq_find_mapping(vg->chip.irqdomain, base + pin);
generic_handle_irq(virq);
-
- /* In case bios or user sets triggering incorretly a pin
- * might remain in "interrupt triggered" state.
- */
- if (looplimit++ > 32) {
- dev_err(&vg->pdev->dev,
- "Gpio %d interrupt flood, disabling\n",
- base + pin);
-
- reg = byt_gpio_reg(&vg->chip, base + pin,
- BYT_CONF0_REG);
- mask = readl(reg);
- mask &= ~(BYT_TRIG_NEG | BYT_TRIG_POS |
- BYT_TRIG_LVL);
- writel(mask, reg);
- mask = readl(reg); /* flush */
- break;
- }
}
}
chip->irq_eoi(data);
}
+static void byt_irq_ack(struct irq_data *d)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct byt_gpio *vg = to_byt_gpio(gc);
+ unsigned offset = irqd_to_hwirq(d);
+ void __iomem *reg;
+
+ reg = byt_gpio_reg(&vg->chip, offset, BYT_INT_STAT_REG);
+ writel(BIT(offset % 32), reg);
+}
+
static void byt_irq_unmask(struct irq_data *d)
{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct byt_gpio *vg = to_byt_gpio(gc);
+ unsigned offset = irqd_to_hwirq(d);
+ unsigned long flags;
+ void __iomem *reg;
+ u32 value;
+
+ spin_lock_irqsave(&vg->lock, flags);
+
+ reg = byt_gpio_reg(&vg->chip, offset, BYT_CONF0_REG);
+ value = readl(reg);
+
+ switch (irqd_get_trigger_type(d)) {
+ case IRQ_TYPE_LEVEL_HIGH:
+ value |= BYT_TRIG_LVL;
+ case IRQ_TYPE_EDGE_RISING:
+ value |= BYT_TRIG_POS;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ value |= BYT_TRIG_LVL;
+ case IRQ_TYPE_EDGE_FALLING:
+ value |= BYT_TRIG_NEG;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ value |= (BYT_TRIG_NEG | BYT_TRIG_POS);
+ break;
+ }
+
+ writel(value, reg);
+
+ spin_unlock_irqrestore(&vg->lock, flags);
}
static void byt_irq_mask(struct irq_data *d)
{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+ struct byt_gpio *vg = to_byt_gpio(gc);
+
+ byt_gpio_clear_triggering(vg, irqd_to_hwirq(d));
}
static struct irq_chip byt_irqchip = {
.name = "BYT-GPIO",
+ .irq_ack = byt_irq_ack,
.irq_mask = byt_irq_mask,
.irq_unmask = byt_irq_unmask,
.irq_set_type = byt_irq_type,
{
void __iomem *reg;
u32 base, value;
+ int i;
+
+ /*
+ * Clear interrupt triggers for all pins that are GPIOs and
+ * do not use direct IRQ mode. This will prevent spurious
+ * interrupts from misconfigured pins.
+ */
+ for (i = 0; i < vg->chip.ngpio; i++) {
+ value = readl(byt_gpio_reg(&vg->chip, i, BYT_CONF0_REG));
+ if ((value & BYT_PIN_MUX) == byt_get_gpio_mux(vg, i) &&
+ !(value & BYT_DIRECT_IRQ_EN)) {
+ byt_gpio_clear_triggering(vg, i);
+ dev_dbg(&vg->pdev->dev, "disabling GPIO %d\n", i);
+ }
+ }
/* clear interrupt status trigger registers */
for (base = 0; base < vg->chip.ngpio; base += 32) {
gc->can_sleep = false;
gc->dev = dev;
+#ifdef CONFIG_PM_SLEEP
+ vg->saved_context = devm_kcalloc(&pdev->dev, gc->ngpio,
+ sizeof(*vg->saved_context), GFP_KERNEL);
+#endif
+
ret = gpiochip_add(gc);
if (ret) {
dev_err(&pdev->dev, "failed adding byt-gpio chip\n");
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int byt_gpio_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct byt_gpio *vg = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < vg->chip.ngpio; i++) {
+ void __iomem *reg;
+ u32 value;
+
+ reg = byt_gpio_reg(&vg->chip, i, BYT_CONF0_REG);
+ value = readl(reg) & BYT_CONF0_RESTORE_MASK;
+ vg->saved_context[i].conf0 = value;
+
+ reg = byt_gpio_reg(&vg->chip, i, BYT_VAL_REG);
+ value = readl(reg) & BYT_VAL_RESTORE_MASK;
+ vg->saved_context[i].val = value;
+ }
+
+ return 0;
+}
+
+static int byt_gpio_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct byt_gpio *vg = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < vg->chip.ngpio; i++) {
+ void __iomem *reg;
+ u32 value;
+
+ reg = byt_gpio_reg(&vg->chip, i, BYT_CONF0_REG);
+ value = readl(reg);
+ if ((value & BYT_CONF0_RESTORE_MASK) !=
+ vg->saved_context[i].conf0) {
+ value &= ~BYT_CONF0_RESTORE_MASK;
+ value |= vg->saved_context[i].conf0;
+ writel(value, reg);
+ dev_info(dev, "restored pin %d conf0 %#08x", i, value);
+ }
+
+ reg = byt_gpio_reg(&vg->chip, i, BYT_VAL_REG);
+ value = readl(reg);
+ if ((value & BYT_VAL_RESTORE_MASK) !=
+ vg->saved_context[i].val) {
+ u32 v;
+
+ v = value & ~BYT_VAL_RESTORE_MASK;
+ v |= vg->saved_context[i].val;
+ if (v != value) {
+ writel(v, reg);
+ dev_dbg(dev, "restored pin %d val %#08x\n",
+ i, v);
+ }
+ }
+ }
+
+ return 0;
+}
+#endif
+
static int byt_gpio_runtime_suspend(struct device *dev)
{
return 0;
}
static const struct dev_pm_ops byt_gpio_pm_ops = {
- .runtime_suspend = byt_gpio_runtime_suspend,
- .runtime_resume = byt_gpio_runtime_resume,
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(byt_gpio_suspend, byt_gpio_resume)
+ SET_RUNTIME_PM_OPS(byt_gpio_runtime_suspend, byt_gpio_runtime_resume,
+ NULL)
};
static const struct acpi_device_id byt_gpio_acpi_match[] = {
static int chv_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
int value)
{
+ chv_gpio_set(chip, offset, value);
return pinctrl_gpio_direction_output(chip->base + offset);
}
/* the interrupt is already cleared before by reading ISR */
}
-static unsigned int gpio_irq_startup(struct irq_data *d)
+static int gpio_irq_request_res(struct irq_data *d)
{
struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d);
unsigned pin = d->hwirq;
int ret;
ret = gpiochip_lock_as_irq(&at91_gpio->chip, pin);
- if (ret) {
+ if (ret)
dev_err(at91_gpio->chip.dev, "unable to lock pind %lu IRQ\n",
d->hwirq);
- return ret;
- }
- gpio_irq_unmask(d);
- return 0;
+
+ return ret;
}
-static void gpio_irq_shutdown(struct irq_data *d)
+static void gpio_irq_release_res(struct irq_data *d)
{
struct at91_gpio_chip *at91_gpio = irq_data_get_irq_chip_data(d);
unsigned pin = d->hwirq;
- gpio_irq_mask(d);
gpiochip_unlock_as_irq(&at91_gpio->chip, pin);
}
static struct irq_chip gpio_irqchip = {
.name = "GPIO",
.irq_ack = gpio_irq_ack,
- .irq_startup = gpio_irq_startup,
- .irq_shutdown = gpio_irq_shutdown,
+ .irq_request_resources = gpio_irq_request_res,
+ .irq_release_resources = gpio_irq_release_res,
.irq_disable = gpio_irq_mask,
.irq_mask = gpio_irq_mask,
.irq_unmask = gpio_irq_unmask,
.pins = sun4i_a10_pins,
.npins = ARRAY_SIZE(sun4i_a10_pins),
.irq_banks = 1,
+ .irq_read_needs_mux = true,
};
static int sun4i_a10_pinctrl_probe(struct platform_device *pdev)
#include <linux/slab.h>
#include "../core.h"
+#include "../../gpio/gpiolib.h"
#include "pinctrl-sunxi.h"
static struct irq_chip sunxi_pinctrl_edge_irq_chip;
static int sunxi_pinctrl_gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct sunxi_pinctrl *pctl = dev_get_drvdata(chip->dev);
-
u32 reg = sunxi_data_reg(offset);
u8 index = sunxi_data_offset(offset);
- u32 val = (readl(pctl->membase + reg) >> index) & DATA_PINS_MASK;
+ u32 set_mux = pctl->desc->irq_read_needs_mux &&
+ test_bit(FLAG_USED_AS_IRQ, &chip->desc[offset].flags);
+ u32 val;
+
+ if (set_mux)
+ sunxi_pmx_set(pctl->pctl_dev, offset, SUN4I_FUNC_INPUT);
+
+ val = (readl(pctl->membase + reg) >> index) & DATA_PINS_MASK;
+
+ if (set_mux)
+ sunxi_pmx_set(pctl->pctl_dev, offset, SUN4I_FUNC_IRQ);
return val;
}
#define IRQ_LEVEL_LOW 0x03
#define IRQ_EDGE_BOTH 0x04
+#define SUN4I_FUNC_INPUT 0
+#define SUN4I_FUNC_IRQ 6
+
struct sunxi_desc_function {
const char *name;
u8 muxval;
int npins;
unsigned pin_base;
unsigned irq_banks;
+ bool irq_read_needs_mux;
};
struct sunxi_pinctrl_function {
#define TIME_WINDOW_MAX_MSEC 40000
#define TIME_WINDOW_MIN_MSEC 250
-
+#define ENERGY_UNIT_SCALE 1000 /* scale from driver unit to powercap unit */
enum unit_type {
ARBITRARY_UNIT, /* no translation */
POWER_UNIT,
struct rapl_power_limit rpl[NR_POWER_LIMITS];
u64 attr_map; /* track capabilities */
unsigned int state;
+ unsigned int domain_energy_unit;
int package_id;
};
#define power_zone_to_rapl_domain(_zone) \
void (*set_floor_freq)(struct rapl_domain *rd, bool mode);
u64 (*compute_time_window)(struct rapl_package *rp, u64 val,
bool to_raw);
+ unsigned int dram_domain_energy_unit;
};
static struct rapl_defaults *rapl_defaults;
static int rapl_write_data_raw(struct rapl_domain *rd,
enum rapl_primitives prim,
unsigned long long value);
-static u64 rapl_unit_xlate(int package, enum unit_type type, u64 value,
+static u64 rapl_unit_xlate(struct rapl_domain *rd, int package,
+ enum unit_type type, u64 value,
int to_raw);
static void package_power_limit_irq_save(int package_id);
static int get_max_energy_counter(struct powercap_zone *pcd_dev, u64 *energy)
{
- *energy = rapl_unit_xlate(0, ENERGY_UNIT, ENERGY_STATUS_MASK, 0);
+ struct rapl_domain *rd = power_zone_to_rapl_domain(pcd_dev);
+
+ *energy = rapl_unit_xlate(rd, 0, ENERGY_UNIT, ENERGY_STATUS_MASK, 0);
return 0;
}
rd->msrs[4] = MSR_DRAM_POWER_INFO;
rd->rpl[0].prim_id = PL1_ENABLE;
rd->rpl[0].name = pl1_name;
+ rd->domain_energy_unit =
+ rapl_defaults->dram_domain_energy_unit;
+ if (rd->domain_energy_unit)
+ pr_info("DRAM domain energy unit %dpj\n",
+ rd->domain_energy_unit);
break;
}
if (mask) {
}
}
-static u64 rapl_unit_xlate(int package, enum unit_type type, u64 value,
+static u64 rapl_unit_xlate(struct rapl_domain *rd, int package,
+ enum unit_type type, u64 value,
int to_raw)
{
u64 units = 1;
struct rapl_package *rp;
+ u64 scale = 1;
rp = find_package_by_id(package);
if (!rp)
units = rp->power_unit;
break;
case ENERGY_UNIT:
- units = rp->energy_unit;
+ scale = ENERGY_UNIT_SCALE;
+ /* per domain unit takes precedence */
+ if (rd && rd->domain_energy_unit)
+ units = rd->domain_energy_unit;
+ else
+ units = rp->energy_unit;
break;
case TIME_UNIT:
return rapl_defaults->compute_time_window(rp, value, to_raw);
};
if (to_raw)
- return div64_u64(value, units);
+ return div64_u64(value, units) * scale;
value *= units;
- return value;
+ return div64_u64(value, scale);
}
/* in the order of enum rapl_primitives */
final = value & rp->mask;
final = final >> rp->shift;
if (xlate)
- *data = rapl_unit_xlate(rd->package_id, rp->unit, final, 0);
+ *data = rapl_unit_xlate(rd, rd->package_id, rp->unit, final, 0);
else
*data = final;
"failed to read msr 0x%x on cpu %d\n", msr, cpu);
return -EIO;
}
- value = rapl_unit_xlate(rd->package_id, rp->unit, value, 1);
+ value = rapl_unit_xlate(rd, rd->package_id, rp->unit, value, 1);
msr_val &= ~rp->mask;
msr_val |= value << rp->shift;
if (wrmsrl_safe_on_cpu(cpu, msr, msr_val)) {
* calculate units differ on different CPUs.
* We convert the units to below format based on CPUs.
* i.e.
- * energy unit: microJoules : Represented in microJoules by default
+ * energy unit: picoJoules : Represented in picoJoules by default
* power unit : microWatts : Represented in milliWatts by default
* time unit : microseconds: Represented in seconds by default
*/
}
value = (msr_val & ENERGY_UNIT_MASK) >> ENERGY_UNIT_OFFSET;
- rp->energy_unit = 1000000 / (1 << value);
+ rp->energy_unit = ENERGY_UNIT_SCALE * 1000000 / (1 << value);
value = (msr_val & POWER_UNIT_MASK) >> POWER_UNIT_OFFSET;
rp->power_unit = 1000000 / (1 << value);
value = (msr_val & TIME_UNIT_MASK) >> TIME_UNIT_OFFSET;
rp->time_unit = 1000000 / (1 << value);
- pr_debug("Core CPU package %d energy=%duJ, time=%dus, power=%duW\n",
+ pr_debug("Core CPU package %d energy=%dpJ, time=%dus, power=%duW\n",
rp->id, rp->energy_unit, rp->time_unit, rp->power_unit);
return 0;
return -ENODEV;
}
value = (msr_val & ENERGY_UNIT_MASK) >> ENERGY_UNIT_OFFSET;
- rp->energy_unit = 1 << value;
+ rp->energy_unit = ENERGY_UNIT_SCALE * 1 << value;
value = (msr_val & POWER_UNIT_MASK) >> POWER_UNIT_OFFSET;
rp->power_unit = (1 << value) * 1000;
value = (msr_val & TIME_UNIT_MASK) >> TIME_UNIT_OFFSET;
rp->time_unit = 1000000 / (1 << value);
- pr_debug("Atom package %d energy=%duJ, time=%dus, power=%duW\n",
+ pr_debug("Atom package %d energy=%dpJ, time=%dus, power=%duW\n",
rp->id, rp->energy_unit, rp->time_unit, rp->power_unit);
return 0;
.compute_time_window = rapl_compute_time_window_core,
};
+static const struct rapl_defaults rapl_defaults_hsw_server = {
+ .check_unit = rapl_check_unit_core,
+ .set_floor_freq = set_floor_freq_default,
+ .compute_time_window = rapl_compute_time_window_core,
+ .dram_domain_energy_unit = 15300,
+};
+
static const struct rapl_defaults rapl_defaults_atom = {
.check_unit = rapl_check_unit_atom,
.set_floor_freq = set_floor_freq_atom,
RAPL_CPU(0x3a, rapl_defaults_core),/* Ivy Bridge */
RAPL_CPU(0x3c, rapl_defaults_core),/* Haswell */
RAPL_CPU(0x3d, rapl_defaults_core),/* Broadwell */
- RAPL_CPU(0x3f, rapl_defaults_core),/* Haswell */
+ RAPL_CPU(0x3f, rapl_defaults_hsw_server),/* Haswell servers */
RAPL_CPU(0x45, rapl_defaults_core),/* Haswell ULT */
RAPL_CPU(0x4C, rapl_defaults_atom),/* Braswell */
RAPL_CPU(0x4A, rapl_defaults_atom),/* Tangier */
}
if (rdev->ena_pin) {
- ret = regulator_ena_gpio_ctrl(rdev, true);
- if (ret < 0)
- return ret;
- rdev->ena_gpio_state = 1;
+ if (!rdev->ena_gpio_state) {
+ ret = regulator_ena_gpio_ctrl(rdev, true);
+ if (ret < 0)
+ return ret;
+ rdev->ena_gpio_state = 1;
+ }
} else if (rdev->desc->ops->enable) {
ret = rdev->desc->ops->enable(rdev);
if (ret < 0)
trace_regulator_disable(rdev_get_name(rdev));
if (rdev->ena_pin) {
- ret = regulator_ena_gpio_ctrl(rdev, false);
- if (ret < 0)
- return ret;
- rdev->ena_gpio_state = 0;
+ if (rdev->ena_gpio_state) {
+ ret = regulator_ena_gpio_ctrl(rdev, false);
+ if (ret < 0)
+ return ret;
+ rdev->ena_gpio_state = 0;
+ }
} else if (rdev->desc->ops->disable) {
ret = rdev->desc->ops->disable(rdev);
if (attr == &dev_attr_requested_microamps.attr)
return rdev->desc->type == REGULATOR_CURRENT ? mode : 0;
- /* all the other attributes exist to support constraints;
- * don't show them if there are no constraints, or if the
- * relevant supporting methods are missing.
- */
- if (!rdev->constraints)
- return 0;
-
/* constraints need specific supporting methods */
if (attr == &dev_attr_min_microvolts.attr ||
attr == &dev_attr_max_microvolts.attr)
config->ena_gpio, ret);
goto wash;
}
-
- if (config->ena_gpio_flags & GPIOF_OUT_INIT_HIGH)
- rdev->ena_gpio_state = 1;
-
- if (config->ena_gpio_invert)
- rdev->ena_gpio_state = !rdev->ena_gpio_state;
}
/* set regulator constraints */
list_for_each_entry(rdev, ®ulator_list, list) {
mutex_lock(&rdev->mutex);
if (rdev->use_count > 0 || rdev->constraints->always_on) {
- error = _regulator_do_enable(rdev);
- if (error)
- ret = error;
+ if (!_regulator_is_enabled(rdev)) {
+ error = _regulator_do_enable(rdev);
+ if (error)
+ ret = error;
+ }
} else {
if (!have_full_constraints())
goto unlock;
config.regmap = chip->regmap;
config.of_node = dev->of_node;
+ /* Mask all interrupt sources to deassert interrupt line */
+ error = regmap_write(chip->regmap, DA9210_REG_MASK_A, ~0);
+ if (!error)
+ error = regmap_write(chip->regmap, DA9210_REG_MASK_B, ~0);
+ if (error) {
+ dev_err(&i2c->dev, "Failed to write to mask reg: %d\n", error);
+ return error;
+ }
+
rdev = devm_regulator_register(&i2c->dev, &da9210_reg, &config);
if (IS_ERR(rdev)) {
dev_err(&i2c->dev, "Failed to register DA9210 regulator\n");
if (!pmic)
return -ENOMEM;
+ if (of_device_is_compatible(node, "ti,tps659038-pmic"))
+ palmas_generic_regs_info[PALMAS_REG_REGEN2].ctrl_addr =
+ TPS659038_REGEN2_CTRL;
+
pmic->dev = &pdev->dev;
pmic->palmas = palmas;
palmas->pmic = pmic;
.vsel_mask = RK808_LDO_VSEL_MASK,
.enable_reg = RK808_LDO_EN_REG,
.enable_mask = BIT(0),
+ .enable_time = 400,
.owner = THIS_MODULE,
}, {
.name = "LDO_REG2",
.vsel_mask = RK808_LDO_VSEL_MASK,
.enable_reg = RK808_LDO_EN_REG,
.enable_mask = BIT(1),
+ .enable_time = 400,
.owner = THIS_MODULE,
}, {
.name = "LDO_REG3",
.vsel_mask = RK808_BUCK4_VSEL_MASK,
.enable_reg = RK808_LDO_EN_REG,
.enable_mask = BIT(2),
+ .enable_time = 400,
.owner = THIS_MODULE,
}, {
.name = "LDO_REG4",
.vsel_mask = RK808_LDO_VSEL_MASK,
.enable_reg = RK808_LDO_EN_REG,
.enable_mask = BIT(3),
+ .enable_time = 400,
.owner = THIS_MODULE,
}, {
.name = "LDO_REG5",
.vsel_mask = RK808_LDO_VSEL_MASK,
.enable_reg = RK808_LDO_EN_REG,
.enable_mask = BIT(4),
+ .enable_time = 400,
.owner = THIS_MODULE,
}, {
.name = "LDO_REG6",
.vsel_mask = RK808_LDO_VSEL_MASK,
.enable_reg = RK808_LDO_EN_REG,
.enable_mask = BIT(5),
+ .enable_time = 400,
.owner = THIS_MODULE,
}, {
.name = "LDO_REG7",
.vsel_mask = RK808_LDO_VSEL_MASK,
.enable_reg = RK808_LDO_EN_REG,
.enable_mask = BIT(6),
+ .enable_time = 400,
.owner = THIS_MODULE,
}, {
.name = "LDO_REG8",
.vsel_mask = RK808_LDO_VSEL_MASK,
.enable_reg = RK808_LDO_EN_REG,
.enable_mask = BIT(7),
+ .enable_time = 400,
.owner = THIS_MODULE,
}, {
.name = "SWITCH_REG1",
#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
void *bufs_va;
int err = 0, i;
size_t total_buf_space;
+ bool notify;
vrp = kzalloc(sizeof(*vrp), GFP_KERNEL);
if (!vrp)
}
}
+ /*
+ * Prepare to kick but don't notify yet - we can't do this before
+ * device is ready.
+ */
+ notify = virtqueue_kick_prepare(vrp->rvq);
+
+ /* From this point on, we can notify and get callbacks. */
+ virtio_device_ready(vdev);
+
/* tell the remote processor it can start sending messages */
- virtqueue_kick(vrp->rvq);
+ /*
+ * this might be concurrent with callbacks, but we are only
+ * doing notify, not a full kick here, so that's ok.
+ */
+ if (notify)
+ virtqueue_notify(vrp->rvq);
dev_info(&vdev->dev, "rpmsg host is online\n");
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/suspend.h>
#include <linux/uaccess.h>
#include "rtc-at91rm9200.h"
static int irq;
static DEFINE_SPINLOCK(at91_rtc_lock);
static u32 at91_rtc_shadow_imr;
+static bool suspended;
+static DEFINE_SPINLOCK(suspended_lock);
+static unsigned long cached_events;
+static u32 at91_rtc_imr;
static void at91_rtc_write_ier(u32 mask)
{
struct rtc_device *rtc = platform_get_drvdata(pdev);
unsigned int rtsr;
unsigned long events = 0;
+ int ret = IRQ_NONE;
+ spin_lock(&suspended_lock);
rtsr = at91_rtc_read(AT91_RTC_SR) & at91_rtc_read_imr();
if (rtsr) { /* this interrupt is shared! Is it ours? */
if (rtsr & AT91_RTC_ALARM)
at91_rtc_write(AT91_RTC_SCCR, rtsr); /* clear status reg */
- rtc_update_irq(rtc, 1, events);
+ if (!suspended) {
+ rtc_update_irq(rtc, 1, events);
- dev_dbg(&pdev->dev, "%s(): num=%ld, events=0x%02lx\n", __func__,
- events >> 8, events & 0x000000FF);
+ dev_dbg(&pdev->dev, "%s(): num=%ld, events=0x%02lx\n",
+ __func__, events >> 8, events & 0x000000FF);
+ } else {
+ cached_events |= events;
+ at91_rtc_write_idr(at91_rtc_imr);
+ pm_system_wakeup();
+ }
- return IRQ_HANDLED;
+ ret = IRQ_HANDLED;
}
- return IRQ_NONE; /* not handled */
+ spin_unlock(&suspended_lock);
+
+ return ret;
}
static const struct at91_rtc_config at91rm9200_config = {
AT91_RTC_CALEV);
ret = devm_request_irq(&pdev->dev, irq, at91_rtc_interrupt,
- IRQF_SHARED,
- "at91_rtc", pdev);
+ IRQF_SHARED | IRQF_COND_SUSPEND,
+ "at91_rtc", pdev);
if (ret) {
dev_err(&pdev->dev, "IRQ %d already in use.\n", irq);
return ret;
/* AT91RM9200 RTC Power management control */
-static u32 at91_rtc_imr;
-
static int at91_rtc_suspend(struct device *dev)
{
/* this IRQ is shared with DBGU and other hardware which isn't
at91_rtc_imr = at91_rtc_read_imr()
& (AT91_RTC_ALARM|AT91_RTC_SECEV);
if (at91_rtc_imr) {
- if (device_may_wakeup(dev))
+ if (device_may_wakeup(dev)) {
+ unsigned long flags;
+
enable_irq_wake(irq);
- else
+
+ spin_lock_irqsave(&suspended_lock, flags);
+ suspended = true;
+ spin_unlock_irqrestore(&suspended_lock, flags);
+ } else {
at91_rtc_write_idr(at91_rtc_imr);
+ }
}
return 0;
}
static int at91_rtc_resume(struct device *dev)
{
+ struct rtc_device *rtc = dev_get_drvdata(dev);
+
if (at91_rtc_imr) {
- if (device_may_wakeup(dev))
+ if (device_may_wakeup(dev)) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&suspended_lock, flags);
+
+ if (cached_events) {
+ rtc_update_irq(rtc, 1, cached_events);
+ cached_events = 0;
+ }
+
+ suspended = false;
+ spin_unlock_irqrestore(&suspended_lock, flags);
+
disable_irq_wake(irq);
- else
- at91_rtc_write_ier(at91_rtc_imr);
+ }
+ at91_rtc_write_ier(at91_rtc_imr);
}
return 0;
}
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
+#include <linux/suspend.h>
#include <linux/clk.h>
/*
unsigned int gpbr_offset;
int irq;
struct clk *sclk;
+ bool suspended;
+ unsigned long events;
+ spinlock_t lock;
};
#define rtt_readl(rtc, field) \
return 0;
}
-/*
- * IRQ handler for the RTC
- */
-static irqreturn_t at91_rtc_interrupt(int irq, void *_rtc)
+static irqreturn_t at91_rtc_cache_events(struct sam9_rtc *rtc)
{
- struct sam9_rtc *rtc = _rtc;
u32 sr, mr;
- unsigned long events = 0;
/* Shared interrupt may be for another device. Note: reading
* SR clears it, so we must only read it in this irq handler!
/* alarm status */
if (sr & AT91_RTT_ALMS)
- events |= (RTC_AF | RTC_IRQF);
+ rtc->events |= (RTC_AF | RTC_IRQF);
/* timer update/increment */
if (sr & AT91_RTT_RTTINC)
- events |= (RTC_UF | RTC_IRQF);
+ rtc->events |= (RTC_UF | RTC_IRQF);
+
+ return IRQ_HANDLED;
+}
+
+static void at91_rtc_flush_events(struct sam9_rtc *rtc)
+{
+ if (!rtc->events)
+ return;
- rtc_update_irq(rtc->rtcdev, 1, events);
+ rtc_update_irq(rtc->rtcdev, 1, rtc->events);
+ rtc->events = 0;
pr_debug("%s: num=%ld, events=0x%02lx\n", __func__,
- events >> 8, events & 0x000000FF);
+ rtc->events >> 8, rtc->events & 0x000000FF);
+}
- return IRQ_HANDLED;
+/*
+ * IRQ handler for the RTC
+ */
+static irqreturn_t at91_rtc_interrupt(int irq, void *_rtc)
+{
+ struct sam9_rtc *rtc = _rtc;
+ int ret;
+
+ spin_lock(&rtc->lock);
+
+ ret = at91_rtc_cache_events(rtc);
+
+ /* We're called in suspended state */
+ if (rtc->suspended) {
+ /* Mask irqs coming from this peripheral */
+ rtt_writel(rtc, MR,
+ rtt_readl(rtc, MR) &
+ ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN));
+ /* Trigger a system wakeup */
+ pm_system_wakeup();
+ } else {
+ at91_rtc_flush_events(rtc);
+ }
+
+ spin_unlock(&rtc->lock);
+
+ return ret;
}
static const struct rtc_class_ops at91_rtc_ops = {
/* register irq handler after we know what name we'll use */
ret = devm_request_irq(&pdev->dev, rtc->irq, at91_rtc_interrupt,
- IRQF_SHARED, dev_name(&rtc->rtcdev->dev), rtc);
+ IRQF_SHARED | IRQF_COND_SUSPEND,
+ dev_name(&rtc->rtcdev->dev), rtc);
if (ret) {
dev_dbg(&pdev->dev, "can't share IRQ %d?\n", rtc->irq);
return ret;
rtc->imr = mr & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN);
if (rtc->imr) {
if (device_may_wakeup(dev) && (mr & AT91_RTT_ALMIEN)) {
+ unsigned long flags;
+
enable_irq_wake(rtc->irq);
+ spin_lock_irqsave(&rtc->lock, flags);
+ rtc->suspended = true;
+ spin_unlock_irqrestore(&rtc->lock, flags);
/* don't let RTTINC cause wakeups */
if (mr & AT91_RTT_RTTINCIEN)
rtt_writel(rtc, MR, mr & ~AT91_RTT_RTTINCIEN);
u32 mr;
if (rtc->imr) {
+ unsigned long flags;
+
if (device_may_wakeup(dev))
disable_irq_wake(rtc->irq);
mr = rtt_readl(rtc, MR);
rtt_writel(rtc, MR, mr | rtc->imr);
+
+ spin_lock_irqsave(&rtc->lock, flags);
+ rtc->suspended = false;
+ at91_rtc_cache_events(rtc);
+ at91_rtc_flush_events(rtc);
+ spin_unlock_irqrestore(&rtc->lock, flags);
}
return 0;
* of this RTC chip. We check for it anyways in case support is
* added in the future.
*/
- if (unlikely((seconds >= 0xc0) && (seconds <= 0xff)))
+ if (unlikely(seconds >= 0xc0))
alrm->time.tm_sec = -1;
else
alrm->time.tm_sec = ds1685_rtc_bcd2bin(rtc, seconds,
RTC_SECS_BCD_MASK,
RTC_SECS_BIN_MASK);
- if (unlikely((minutes >= 0xc0) && (minutes <= 0xff)))
+ if (unlikely(minutes >= 0xc0))
alrm->time.tm_min = -1;
else
alrm->time.tm_min = ds1685_rtc_bcd2bin(rtc, minutes,
RTC_MINS_BCD_MASK,
RTC_MINS_BIN_MASK);
- if (unlikely((hours >= 0xc0) && (hours <= 0xff)))
+ if (unlikely(hours >= 0xc0))
alrm->time.tm_hour = -1;
else
alrm->time.tm_hour = ds1685_rtc_bcd2bin(rtc, hours,
* field, and we only support four fields. We put the support
* here anyways for the future.
*/
- if (unlikely((seconds >= 0xc0) && (seconds <= 0xff)))
+ if (unlikely(seconds >= 0xc0))
seconds = 0xff;
- if (unlikely((minutes >= 0xc0) && (minutes <= 0xff)))
+ if (unlikely(minutes >= 0xc0))
minutes = 0xff;
- if (unlikely((hours >= 0xc0) && (hours <= 0xff)))
+ if (unlikely(hours >= 0xc0))
hours = 0xff;
alrm->time.tm_mon = -1;
/* ----------------------------------------------------------------------- */
/* /dev/rtcX Interface functions */
-#ifdef CONFIG_RTC_INTF_DEV
/**
* ds1685_rtc_alarm_irq_enable - replaces ioctl() RTC_AIE on/off.
* @dev: pointer to device structure.
return 0;
}
-#endif
/* ----------------------------------------------------------------------- */
ds1685_rtc_sysfs_time_regs_lookup(attr->attr.name, false);
/* Make sure we actually matched something. */
- if (!bcd_reg_info && !bin_reg_info)
+ if (!bcd_reg_info || !bin_reg_info)
return -EINVAL;
/* bcd_reg_info->reg == bin_reg_info->reg. */
return -EINVAL;
/* Make sure we actually matched something. */
- if (!bcd_reg_info && !bin_reg_info)
+ if (!bcd_reg_info || !bin_reg_info)
return -EINVAL;
/* Check for a valid range. */
mrst->dev = NULL;
}
-#ifdef CONFIG_PM
-static int mrst_suspend(struct device *dev, pm_message_t mesg)
+#ifdef CONFIG_PM_SLEEP
+static int mrst_suspend(struct device *dev)
{
struct mrst_rtc *mrst = dev_get_drvdata(dev);
unsigned char tmp;
*/
static inline int mrst_poweroff(struct device *dev)
{
- return mrst_suspend(dev, PMSG_HIBERNATE);
+ return mrst_suspend(dev);
}
static int mrst_resume(struct device *dev)
return 0;
}
+static SIMPLE_DEV_PM_OPS(mrst_pm_ops, mrst_suspend, mrst_resume);
+#define MRST_PM_OPS (&mrst_pm_ops)
+
#else
-#define mrst_suspend NULL
-#define mrst_resume NULL
+#define MRST_PM_OPS NULL
static inline int mrst_poweroff(struct device *dev)
{
.remove = vrtc_mrst_platform_remove,
.shutdown = vrtc_mrst_platform_shutdown,
.driver = {
- .name = (char *) driver_name,
- .suspend = mrst_suspend,
- .resume = mrst_resume,
+ .name = driver_name,
+ .pm = MRST_PM_OPS,
}
};
static struct s3c_rtc_data const s3c6410_rtc_data = {
.max_user_freq = 32768,
+ .needs_src_clk = true,
.irq_handler = s3c6410_rtc_irq,
.set_freq = s3c6410_rtc_setfreq,
.enable_tick = s3c6410_rtc_enable_tick,
* parse input
*/
num_of_segments = 0;
- for (i = 0; ((buf[i] != '\0') && (buf[i] != '\n') && i < count); i++) {
+ for (i = 0; (i < count && (buf[i] != '\0') && (buf[i] != '\n')); i++) {
for (j = i; (buf[j] != ':') &&
(buf[j] != '\0') &&
(buf[j] != '\n') &&
add = 0;
continue;
}
- for (pos = 0; pos <= iter->aob->request.msb_count; pos++) {
+ for (pos = 0; pos < iter->aob->request.msb_count; pos++) {
if (clusters_intersect(req, iter->request[pos]) &&
(rq_data_dir(req) == WRITE ||
rq_data_dir(iter->request[pos]) == WRITE)) {
};
static struct ata_port_info sata_port_info = {
- .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA,
+ .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA |
+ ATA_FLAG_SAS_HOST,
.pio_mask = ATA_PIO4_ONLY,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA6,
};
static struct ata_port_info sata_port_info = {
- .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ,
+ .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ |
+ ATA_FLAG_SAS_HOST,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA6,
struct sas_discovery_event *ev = to_sas_discovery_event(work);
struct asd_sas_port *port = ev->port;
struct sas_ha_struct *ha = port->ha;
+ struct domain_device *ddev = port->port_dev;
/* prevent revalidation from finding sata links in recovery */
mutex_lock(&ha->disco_mutex);
SAS_DPRINTK("REVALIDATING DOMAIN on port %d, pid:%d\n", port->id,
task_pid_nr(current));
- if (port->port_dev)
- res = sas_ex_revalidate_domain(port->port_dev);
+ if (ddev && (ddev->dev_type == SAS_FANOUT_EXPANDER_DEVICE ||
+ ddev->dev_type == SAS_EDGE_EXPANDER_DEVICE))
+ res = sas_ex_revalidate_domain(ddev);
SAS_DPRINTK("done REVALIDATING DOMAIN on port %d, pid:%d, res 0x%x\n",
port->id, task_pid_nr(current), res);
/*
* Finally register the new FC Nexus with TCM
*/
- __transport_register_session(se_nacl->se_tpg, se_nacl, se_sess, sess);
+ transport_register_session(se_nacl->se_tpg, se_nacl, se_sess, sess);
return 0;
}
if (!of_machine_is_compatible("renesas,emev2") &&
!of_machine_is_compatible("renesas,r7s72100") &&
!of_machine_is_compatible("renesas,r8a73a4") &&
+#ifndef CONFIG_PM_GENERIC_DOMAINS_OF
!of_machine_is_compatible("renesas,r8a7740") &&
+#endif
!of_machine_is_compatible("renesas,r8a7778") &&
!of_machine_is_compatible("renesas,r8a7779") &&
!of_machine_is_compatible("renesas,r8a7790") &&
(unsigned long long)xfer->rx_dma);
}
- /* REVISIT: We're waiting for ENDRX before we start the next
+ /* REVISIT: We're waiting for RXBUFF before we start the next
* transfer because we need to handle some difficult timing
- * issues otherwise. If we wait for ENDTX in one transfer and
- * then starts waiting for ENDRX in the next, it's difficult
- * to tell the difference between the ENDRX interrupt we're
- * actually waiting for and the ENDRX interrupt of the
+ * issues otherwise. If we wait for TXBUFE in one transfer and
+ * then starts waiting for RXBUFF in the next, it's difficult
+ * to tell the difference between the RXBUFF interrupt we're
+ * actually waiting for and the RXBUFF interrupt of the
* previous transfer.
*
* It should be doable, though. Just not now...
*/
- spi_writel(as, IER, SPI_BIT(ENDRX) | SPI_BIT(OVRES));
+ spi_writel(as, IER, SPI_BIT(RXBUFF) | SPI_BIT(OVRES));
spi_writel(as, PTCR, SPI_BIT(TXTEN) | SPI_BIT(RXTEN));
}
{
struct dw_spi *dws = arg;
- if (test_and_clear_bit(TX_BUSY, &dws->dma_chan_busy) & BIT(RX_BUSY))
+ clear_bit(TX_BUSY, &dws->dma_chan_busy);
+ if (test_bit(RX_BUSY, &dws->dma_chan_busy))
return;
dw_spi_xfer_done(dws);
}
1,
DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!txdesc)
+ return NULL;
+
txdesc->callback = dw_spi_dma_tx_done;
txdesc->callback_param = dws;
{
struct dw_spi *dws = arg;
- if (test_and_clear_bit(RX_BUSY, &dws->dma_chan_busy) & BIT(TX_BUSY))
+ clear_bit(RX_BUSY, &dws->dma_chan_busy);
+ if (test_bit(TX_BUSY, &dws->dma_chan_busy))
return;
dw_spi_xfer_done(dws);
}
1,
DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!rxdesc)
+ return NULL;
+
rxdesc->callback = dw_spi_dma_rx_done;
rxdesc->callback_param = dws;
static struct spi_pci_desc spi_pci_mid_desc_1 = {
.setup = dw_spi_mid_init,
- .num_cs = 32,
+ .num_cs = 5,
.bus_num = 0,
};
static struct spi_pci_desc spi_pci_mid_desc_2 = {
.setup = dw_spi_mid_init,
- .num_cs = 4,
+ .num_cs = 2,
.bus_num = 1,
};
if (!dws->fifo_len) {
u32 fifo;
- for (fifo = 2; fifo <= 256; fifo++) {
+ for (fifo = 1; fifo < 256; fifo++) {
dw_writew(dws, DW_SPI_TXFLTR, fifo);
if (fifo != dw_readw(dws, DW_SPI_TXFLTR))
break;
}
dw_writew(dws, DW_SPI_TXFLTR, 0);
- dws->fifo_len = (fifo == 2) ? 0 : fifo - 1;
+ dws->fifo_len = (fifo == 1) ? 0 : fifo;
dev_dbg(dev, "Detected FIFO size: %u bytes\n", dws->fifo_len);
}
}
unsigned long flags;
int ret;
+ if (xfer->len > SPFI_TRANSACTION_TSIZE_MASK) {
+ dev_err(spfi->dev,
+ "Transfer length (%d) is greater than the max supported (%d)",
+ xfer->len, SPFI_TRANSACTION_TSIZE_MASK);
+ return -EINVAL;
+ }
+
/*
* Stop all DMA and reset the controller if the previous transaction
* timed-out and never completed it's DMA.
pl022->cur_msg = NULL;
pl022->cur_transfer = NULL;
pl022->cur_chip = NULL;
- spi_finalize_current_message(pl022->master);
/* disable the SPI/SSP operation */
writew((readw(SSP_CR1(pl022->virtbase)) &
(~SSP_CR1_MASK_SSE)), SSP_CR1(pl022->virtbase));
+ spi_finalize_current_message(pl022->master);
}
/**
struct resource *res;
struct device *dev;
void __iomem *base;
- u32 max_freq, iomode;
+ u32 max_freq, iomode, num_cs;
int ret, irq, size;
dev = &pdev->dev;
}
/* use num-cs unless not present or out of range */
- if (of_property_read_u16(dev->of_node, "num-cs",
- &master->num_chipselect) ||
- (master->num_chipselect > SPI_NUM_CHIPSELECTS))
+ if (of_property_read_u32(dev->of_node, "num-cs", &num_cs) ||
+ num_cs > SPI_NUM_CHIPSELECTS)
master->num_chipselect = SPI_NUM_CHIPSELECTS;
+ else
+ master->num_chipselect = num_cs;
master->bus_num = pdev->id;
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP;
#define QSPI_FLEN(n) ((n - 1) << 0)
/* STATUS REGISTER */
+#define BUSY 0x01
#define WC 0x02
/* INTERRUPT REGISTER */
ti_qspi_write(qspi, ctx_reg->clkctrl, QSPI_SPI_CLOCK_CNTRL_REG);
}
+static inline u32 qspi_is_busy(struct ti_qspi *qspi)
+{
+ u32 stat;
+ unsigned long timeout = jiffies + QSPI_COMPLETION_TIMEOUT;
+
+ stat = ti_qspi_read(qspi, QSPI_SPI_STATUS_REG);
+ while ((stat & BUSY) && time_after(timeout, jiffies)) {
+ cpu_relax();
+ stat = ti_qspi_read(qspi, QSPI_SPI_STATUS_REG);
+ }
+
+ WARN(stat & BUSY, "qspi busy\n");
+ return stat & BUSY;
+}
+
static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t)
{
int wlen, count;
wlen = t->bits_per_word >> 3; /* in bytes */
while (count) {
+ if (qspi_is_busy(qspi))
+ return -EBUSY;
+
switch (wlen) {
case 1:
dev_dbg(qspi->dev, "tx cmd %08x dc %08x data %02x\n",
while (count) {
dev_dbg(qspi->dev, "rx cmd %08x dc %08x\n", cmd, qspi->dc);
+ if (qspi_is_busy(qspi))
+ return -EBUSY;
+
ti_qspi_write(qspi, cmd, QSPI_SPI_CMD_REG);
if (!wait_for_completion_timeout(&qspi->transfer_complete,
QSPI_COMPLETION_TIMEOUT)) {
"failed to unprepare message: %d\n", ret);
}
}
+
+ trace_spi_message_done(mesg);
+
master->cur_msg_prepared = false;
mesg->state = NULL;
if (mesg->complete)
mesg->complete(mesg->context);
-
- trace_spi_message_done(mesg);
}
EXPORT_SYMBOL_GPL(spi_finalize_current_message);
unsigned int *data)
{
struct pci1710_private *devpriv = dev->private;
- unsigned int chan = CR_CHAN(insn->chanspec);
int ret = 0;
int i;
if (ret)
break;
- ret = pci171x_ai_read_sample(dev, s, chan, &val);
+ ret = pci171x_ai_read_sample(dev, s, 0, &val);
if (ret)
break;
stalled++;
if (stalled > 10)
break;
+ } else {
+ residue = new_residue;
+ stalled = 0;
}
- residue = new_residue;
- stalled = 0;
}
return residue;
}
VMK8061_MODEL
};
-struct firmware_version {
- unsigned char ic3_vers[32]; /* USB-Controller */
- unsigned char ic6_vers[32]; /* CPU */
-};
-
static const struct comedi_lrange vmk8061_range = {
2, {
UNI_RANGE(5),
struct vmk80xx_private {
struct usb_endpoint_descriptor *ep_rx;
struct usb_endpoint_descriptor *ep_tx;
- struct firmware_version fw;
struct semaphore limit_sem;
unsigned char *usb_rx_buf;
unsigned char *usb_tx_buf;
enum vmk80xx_model model;
};
-static int vmk80xx_check_data_link(struct comedi_device *dev)
-{
- struct vmk80xx_private *devpriv = dev->private;
- struct usb_device *usb = comedi_to_usb_dev(dev);
- unsigned int tx_pipe;
- unsigned int rx_pipe;
- unsigned char tx[1];
- unsigned char rx[2];
-
- tx_pipe = usb_sndbulkpipe(usb, 0x01);
- rx_pipe = usb_rcvbulkpipe(usb, 0x81);
-
- tx[0] = VMK8061_CMD_RD_PWR_STAT;
-
- /*
- * Check that IC6 (PIC16F871) is powered and
- * running and the data link between IC3 and
- * IC6 is working properly
- */
- usb_bulk_msg(usb, tx_pipe, tx, 1, NULL, devpriv->ep_tx->bInterval);
- usb_bulk_msg(usb, rx_pipe, rx, 2, NULL, HZ * 10);
-
- return (int)rx[1];
-}
-
-static void vmk80xx_read_eeprom(struct comedi_device *dev, int flag)
-{
- struct vmk80xx_private *devpriv = dev->private;
- struct usb_device *usb = comedi_to_usb_dev(dev);
- unsigned int tx_pipe;
- unsigned int rx_pipe;
- unsigned char tx[1];
- unsigned char rx[64];
- int cnt;
-
- tx_pipe = usb_sndbulkpipe(usb, 0x01);
- rx_pipe = usb_rcvbulkpipe(usb, 0x81);
-
- tx[0] = VMK8061_CMD_RD_VERSION;
-
- /*
- * Read the firmware version info of IC3 and
- * IC6 from the internal EEPROM of the IC
- */
- usb_bulk_msg(usb, tx_pipe, tx, 1, NULL, devpriv->ep_tx->bInterval);
- usb_bulk_msg(usb, rx_pipe, rx, 64, &cnt, HZ * 10);
-
- rx[cnt] = '\0';
-
- if (flag & IC3_VERSION)
- strncpy(devpriv->fw.ic3_vers, rx + 1, 24);
- else /* IC6_VERSION */
- strncpy(devpriv->fw.ic6_vers, rx + 25, 24);
-}
-
static void vmk80xx_do_bulk_msg(struct comedi_device *dev)
{
struct vmk80xx_private *devpriv = dev->private;
usb_set_intfdata(intf, devpriv);
- if (devpriv->model == VMK8061_MODEL) {
- vmk80xx_read_eeprom(dev, IC3_VERSION);
- dev_info(&intf->dev, "%s\n", devpriv->fw.ic3_vers);
-
- if (vmk80xx_check_data_link(dev)) {
- vmk80xx_read_eeprom(dev, IC6_VERSION);
- dev_info(&intf->dev, "%s\n", devpriv->fw.ic6_vers);
- }
- }
-
if (devpriv->model == VMK8055_MODEL)
vmk80xx_reset_device(dev);
config IIO_SIMPLE_DUMMY_BUFFER
bool "Buffered capture support"
select IIO_BUFFER
+ select IIO_TRIGGER
select IIO_KFIFO_BUF
help
Add buffered data capture to the simple dummy driver.
unsigned long is_divided;
/*
- * Touchscreen LRADC channels receives a private slot in the CTRL4
- * register, the slot #7. Therefore only 7 slots instead of 8 in the
- * CTRL4 register can be mapped to LRADC channels when using the
- * touchscreen.
- *
+ * When the touchscreen is enabled, we give it two private virtual
+ * channels: #6 and #7. This means that only 6 virtual channels (instead
+ * of 8) will be available for buffered capture.
+ */
+#define TOUCHSCREEN_VCHANNEL1 7
+#define TOUCHSCREEN_VCHANNEL2 6
+#define BUFFER_VCHANS_LIMITED 0x3f
+#define BUFFER_VCHANS_ALL 0xff
+ u8 buffer_vchans;
+
+ /*
* Furthermore, certain LRADC channels are shared between touchscreen
* and/or touch-buttons and generic LRADC block. Therefore when using
* either of these, these channels are not available for the regular
#define LRADC_CTRL4 0x140
#define LRADC_CTRL4_LRADCSELECT_MASK(n) (0xf << ((n) * 4))
#define LRADC_CTRL4_LRADCSELECT_OFFSET(n) ((n) * 4)
+#define LRADC_CTRL4_LRADCSELECT(n, x) \
+ (((x) << LRADC_CTRL4_LRADCSELECT_OFFSET(n)) & \
+ LRADC_CTRL4_LRADCSELECT_MASK(n))
#define LRADC_RESOLUTION 12
#define LRADC_SINGLE_SAMPLE_MASK ((1 << LRADC_RESOLUTION) - 1)
LRADC_STATUS_TOUCH_DETECT_RAW);
}
+static void mxs_lradc_map_channel(struct mxs_lradc *lradc, unsigned vch,
+ unsigned ch)
+{
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(vch),
+ LRADC_CTRL4);
+ mxs_lradc_reg_set(lradc, LRADC_CTRL4_LRADCSELECT(vch, ch), LRADC_CTRL4);
+}
+
static void mxs_lradc_setup_ts_channel(struct mxs_lradc *lradc, unsigned ch)
{
/*
LRADC_DELAY_DELAY(lradc->over_sample_delay - 1),
LRADC_DELAY(3));
- mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(2) |
- LRADC_CTRL1_LRADC_IRQ(3) | LRADC_CTRL1_LRADC_IRQ(4) |
- LRADC_CTRL1_LRADC_IRQ(5), LRADC_CTRL1);
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch), LRADC_CTRL1);
- /* wake us again, when the complete conversion is done */
- mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(ch), LRADC_CTRL1);
/*
* after changing the touchscreen plates setting
* the signals need some initial time to settle. Start the
LRADC_DELAY_DELAY(lradc->over_sample_delay - 1),
LRADC_DELAY(3));
- mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(2) |
- LRADC_CTRL1_LRADC_IRQ(3) | LRADC_CTRL1_LRADC_IRQ(4) |
- LRADC_CTRL1_LRADC_IRQ(5), LRADC_CTRL1);
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch2), LRADC_CTRL1);
- /* wake us again, when the conversions are done */
- mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(ch2), LRADC_CTRL1);
/*
* after changing the touchscreen plates setting
* the signals need some initial time to settle. Start the
#define TS_CH_XM 4
#define TS_CH_YM 5
-static int mxs_lradc_read_ts_channel(struct mxs_lradc *lradc)
-{
- u32 reg;
- int val;
-
- reg = readl(lradc->base + LRADC_CTRL1);
-
- /* only channels 3 to 5 are of interest here */
- if (reg & LRADC_CTRL1_LRADC_IRQ(TS_CH_YP)) {
- mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(TS_CH_YP) |
- LRADC_CTRL1_LRADC_IRQ(TS_CH_YP), LRADC_CTRL1);
- val = mxs_lradc_read_raw_channel(lradc, TS_CH_YP);
- } else if (reg & LRADC_CTRL1_LRADC_IRQ(TS_CH_XM)) {
- mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(TS_CH_XM) |
- LRADC_CTRL1_LRADC_IRQ(TS_CH_XM), LRADC_CTRL1);
- val = mxs_lradc_read_raw_channel(lradc, TS_CH_XM);
- } else if (reg & LRADC_CTRL1_LRADC_IRQ(TS_CH_YM)) {
- mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(TS_CH_YM) |
- LRADC_CTRL1_LRADC_IRQ(TS_CH_YM), LRADC_CTRL1);
- val = mxs_lradc_read_raw_channel(lradc, TS_CH_YM);
- } else {
- return -EIO;
- }
-
- mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
- mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
-
- return val;
-}
-
/*
* YP(open)--+-------------+
* | |--+
mxs_lradc_reg_set(lradc, mxs_lradc_drive_x_plate(lradc), LRADC_CTRL0);
lradc->cur_plate = LRADC_SAMPLE_X;
- mxs_lradc_setup_ts_channel(lradc, TS_CH_YP);
+ mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YP);
+ mxs_lradc_setup_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1);
}
/*
mxs_lradc_reg_set(lradc, mxs_lradc_drive_y_plate(lradc), LRADC_CTRL0);
lradc->cur_plate = LRADC_SAMPLE_Y;
- mxs_lradc_setup_ts_channel(lradc, TS_CH_XM);
+ mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_XM);
+ mxs_lradc_setup_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1);
}
/*
mxs_lradc_reg_set(lradc, mxs_lradc_drive_pressure(lradc), LRADC_CTRL0);
lradc->cur_plate = LRADC_SAMPLE_PRESSURE;
- mxs_lradc_setup_ts_pressure(lradc, TS_CH_XP, TS_CH_YM);
+ mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YM);
+ mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL2, TS_CH_XP);
+ mxs_lradc_setup_ts_pressure(lradc, TOUCHSCREEN_VCHANNEL2,
+ TOUCHSCREEN_VCHANNEL1);
}
static void mxs_lradc_enable_touch_detection(struct mxs_lradc *lradc)
mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
}
+static void mxs_lradc_start_touch_event(struct mxs_lradc *lradc)
+{
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
+ LRADC_CTRL1);
+ mxs_lradc_reg_set(lradc,
+ LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1), LRADC_CTRL1);
+ /*
+ * start with the Y-pos, because it uses nearly the same plate
+ * settings like the touch detection
+ */
+ mxs_lradc_prepare_y_pos(lradc);
+}
+
static void mxs_lradc_report_ts_event(struct mxs_lradc *lradc)
{
input_report_abs(lradc->ts_input, ABS_X, lradc->ts_x_pos);
* start a dummy conversion to burn time to settle the signals
* note: we are not interested in the conversion's value
*/
- mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(5));
- mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(5), LRADC_CTRL1);
- mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(5), LRADC_CTRL1);
- mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << 5) |
+ mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(TOUCHSCREEN_VCHANNEL1));
+ mxs_lradc_reg_clear(lradc,
+ LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
+ LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2), LRADC_CTRL1);
+ mxs_lradc_reg_wrt(lradc,
+ LRADC_DELAY_TRIGGER(1 << TOUCHSCREEN_VCHANNEL1) |
LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10), /* waste 5 ms */
LRADC_DELAY(2));
}
/* if it is released, wait for the next touch via IRQ */
lradc->cur_plate = LRADC_TOUCH;
- mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ, LRADC_CTRL1);
+ mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
+ mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ |
+ LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
+ LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1), LRADC_CTRL1);
mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
}
/* touchscreen's state machine */
static void mxs_lradc_handle_touch(struct mxs_lradc *lradc)
{
- int val;
-
switch (lradc->cur_plate) {
case LRADC_TOUCH:
- /*
- * start with the Y-pos, because it uses nearly the same plate
- * settings like the touch detection
- */
- if (mxs_lradc_check_touch_event(lradc)) {
- mxs_lradc_reg_clear(lradc,
- LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
- LRADC_CTRL1);
- mxs_lradc_prepare_y_pos(lradc);
- }
+ if (mxs_lradc_check_touch_event(lradc))
+ mxs_lradc_start_touch_event(lradc);
mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ,
LRADC_CTRL1);
return;
case LRADC_SAMPLE_Y:
- val = mxs_lradc_read_ts_channel(lradc);
- if (val < 0) {
- mxs_lradc_enable_touch_detection(lradc); /* re-start */
- return;
- }
- lradc->ts_y_pos = val;
+ lradc->ts_y_pos = mxs_lradc_read_raw_channel(lradc,
+ TOUCHSCREEN_VCHANNEL1);
mxs_lradc_prepare_x_pos(lradc);
return;
case LRADC_SAMPLE_X:
- val = mxs_lradc_read_ts_channel(lradc);
- if (val < 0) {
- mxs_lradc_enable_touch_detection(lradc); /* re-start */
- return;
- }
- lradc->ts_x_pos = val;
+ lradc->ts_x_pos = mxs_lradc_read_raw_channel(lradc,
+ TOUCHSCREEN_VCHANNEL1);
mxs_lradc_prepare_pressure(lradc);
return;
case LRADC_SAMPLE_PRESSURE:
- lradc->ts_pressure =
- mxs_lradc_read_ts_pressure(lradc, TS_CH_XP, TS_CH_YM);
+ lradc->ts_pressure = mxs_lradc_read_ts_pressure(lradc,
+ TOUCHSCREEN_VCHANNEL2,
+ TOUCHSCREEN_VCHANNEL1);
mxs_lradc_complete_touch_event(lradc);
return;
case LRADC_SAMPLE_VALID:
- val = mxs_lradc_read_ts_channel(lradc); /* ignore the value */
mxs_lradc_finish_touch_event(lradc, 1);
break;
}
* used if doing raw sampling.
*/
if (lradc->soc == IMX28_LRADC)
- mxs_lradc_reg_clear(lradc, LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK,
+ mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0),
LRADC_CTRL1);
- mxs_lradc_reg_clear(lradc, 0xff, LRADC_CTRL0);
+ mxs_lradc_reg_clear(lradc, 0x1, LRADC_CTRL0);
/* Enable / disable the divider per requirement */
if (test_bit(chan, &lradc->is_divided))
{
/* stop all interrupts from firing */
mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN |
- LRADC_CTRL1_LRADC_IRQ_EN(2) | LRADC_CTRL1_LRADC_IRQ_EN(3) |
- LRADC_CTRL1_LRADC_IRQ_EN(4) | LRADC_CTRL1_LRADC_IRQ_EN(5),
- LRADC_CTRL1);
+ LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
+ LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL2), LRADC_CTRL1);
/* Power-down touchscreen touch-detect circuitry. */
mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
struct iio_dev *iio = data;
struct mxs_lradc *lradc = iio_priv(iio);
unsigned long reg = readl(lradc->base + LRADC_CTRL1);
+ uint32_t clr_irq = mxs_lradc_irq_mask(lradc);
const uint32_t ts_irq_mask =
LRADC_CTRL1_TOUCH_DETECT_IRQ |
- LRADC_CTRL1_LRADC_IRQ(2) |
- LRADC_CTRL1_LRADC_IRQ(3) |
- LRADC_CTRL1_LRADC_IRQ(4) |
- LRADC_CTRL1_LRADC_IRQ(5);
+ LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
+ LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2);
if (!(reg & mxs_lradc_irq_mask(lradc)))
return IRQ_NONE;
- if (lradc->use_touchscreen && (reg & ts_irq_mask))
+ if (lradc->use_touchscreen && (reg & ts_irq_mask)) {
mxs_lradc_handle_touch(lradc);
- if (iio_buffer_enabled(iio))
- iio_trigger_poll(iio->trig);
- else if (reg & LRADC_CTRL1_LRADC_IRQ(0))
+ /* Make sure we don't clear the next conversion's interrupt. */
+ clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
+ LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2));
+ }
+
+ if (iio_buffer_enabled(iio)) {
+ if (reg & lradc->buffer_vchans)
+ iio_trigger_poll(iio->trig);
+ } else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) {
complete(&lradc->completion);
+ }
- mxs_lradc_reg_clear(lradc, reg & mxs_lradc_irq_mask(lradc),
- LRADC_CTRL1);
+ mxs_lradc_reg_clear(lradc, reg & clr_irq, LRADC_CTRL1);
return IRQ_HANDLED;
}
}
if (lradc->soc == IMX28_LRADC)
- mxs_lradc_reg_clear(lradc, LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK,
- LRADC_CTRL1);
- mxs_lradc_reg_clear(lradc, 0xff, LRADC_CTRL0);
+ mxs_lradc_reg_clear(lradc,
+ lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
+ LRADC_CTRL1);
+ mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0);
for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs);
mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
LRADC_DELAY_KICK, LRADC_DELAY(0));
- mxs_lradc_reg_clear(lradc, 0xff, LRADC_CTRL0);
+ mxs_lradc_reg_clear(lradc, lradc->buffer_vchans, LRADC_CTRL0);
if (lradc->soc == IMX28_LRADC)
- mxs_lradc_reg_clear(lradc, LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK,
- LRADC_CTRL1);
+ mxs_lradc_reg_clear(lradc,
+ lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
+ LRADC_CTRL1);
kfree(lradc->buffer);
mutex_unlock(&lradc->lock);
if (lradc->use_touchbutton)
rsvd_chans++;
if (lradc->use_touchscreen)
- rsvd_chans++;
+ rsvd_chans += 2;
/* Test for attempts to map channels with special mode of operation. */
if (bitmap_intersects(mask, &rsvd_mask, LRADC_MAX_TOTAL_CHANS))
.channel = 8,
.scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
},
+ /* Hidden channel to keep indexes */
+ {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .scan_index = -1,
+ .channel = 9,
+ },
MXS_ADC_CHAN(10, IIO_VOLTAGE), /* VDDIO */
MXS_ADC_CHAN(11, IIO_VOLTAGE), /* VTH */
MXS_ADC_CHAN(12, IIO_VOLTAGE), /* VDDA */
touch_ret = mxs_lradc_probe_touchscreen(lradc, node);
+ if (touch_ret == 0)
+ lradc->buffer_vchans = BUFFER_VCHANS_LIMITED;
+ else
+ lradc->buffer_vchans = BUFFER_VCHANS_ALL;
+
/* Grab all IRQ sources */
for (i = 0; i < of_cfg->irq_count; i++) {
lradc->irq[i] = platform_get_irq(pdev, i);
mutex_init(&data->lock);
indio_dev->dev.parent = dev;
+ indio_dev->name = dev->driver->name;
indio_dev->info = &hmc5843_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = data->variant->channels;
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/module.h>
+#include <linux/bitops.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
break;
case IIO_ANGL_VEL:
vel = (((s16)(st->rx[0])) << 4) | ((st->rx[1] & 0xF0) >> 4);
- vel = (vel << 4) >> 4;
+ vel = sign_extend32(vel, 11);
*val = vel;
break;
default:
/* zonetype initial */
pDevice->byOriginalZonetype = pDevice->abyEEPROM[EEP_OFS_ZONETYPE];
- /* Get RFType */
- pDevice->byRFType = SROMbyReadEmbedded(pDevice->PortOffset, EEP_OFS_RFTYPE);
-
- /* force change RevID for VT3253 emu */
- if ((pDevice->byRFType & RF_EMU) != 0)
- pDevice->byRevId = 0x80;
-
- pDevice->byRFType &= RF_MASK;
- pr_debug("pDevice->byRFType = %x\n", pDevice->byRFType);
-
if (!pDevice->bZoneRegExist)
pDevice->byZoneType = pDevice->abyEEPROM[EEP_OFS_ZONETYPE];
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
PSTxDesc head_td;
- u32 dma_idx = TYPE_AC0DMA;
+ u32 dma_idx;
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
- if (!ieee80211_is_data(hdr->frame_control))
+ if (ieee80211_is_data(hdr->frame_control))
+ dma_idx = TYPE_AC0DMA;
+ else
dma_idx = TYPE_TXDMA0;
if (AVAIL_TD(priv, dma_idx) < 1) {
head_td->pTDInfo->skb = skb;
+ if (dma_idx == TYPE_AC0DMA)
+ head_td->pTDInfo->byFlags = TD_FLAGS_NETIF_SKB;
+
priv->iTDUsed[dma_idx]++;
/* Take ownership */
head_td->buff_addr = cpu_to_le32(head_td->pTDInfo->skb_dma);
- if (dma_idx == TYPE_AC0DMA) {
- head_td->pTDInfo->byFlags = TD_FLAGS_NETIF_SKB;
-
+ if (head_td->pTDInfo->byFlags & TD_FLAGS_NETIF_SKB)
MACvTransmitAC0(priv->PortOffset);
- } else {
+ else
MACvTransmit0(priv->PortOffset);
- }
spin_unlock_irqrestore(&priv->lock, flags);
MACvInitialize(priv->PortOffset);
MACvReadEtherAddress(priv->PortOffset, priv->abyCurrentNetAddr);
+ /* Get RFType */
+ priv->byRFType = SROMbyReadEmbedded(priv->PortOffset, EEP_OFS_RFTYPE);
+ priv->byRFType &= RF_MASK;
+
+ dev_dbg(&pcid->dev, "RF Type = %x\n", priv->byRFType);
+
device_get_options(priv);
device_set_options(priv);
/* Mask out the options cannot be set to the chip */
break;
case RATE_6M:
case RATE_9M:
+ case RATE_12M:
case RATE_18M:
byPwr = priv->abyOFDMPwrTbl[uCH];
if (priv->byRFType == RF_UW2452)
break;
case RATE_6M:
case RATE_9M:
+ case RATE_12M:
case RATE_18M:
case RATE_24M:
case RATE_36M:
pr_debug("Closing iSCSI connection CID %hu on SID:"
" %u\n", conn->cid, sess->sid);
/*
- * Always up conn_logout_comp just in case the RX Thread is sleeping
- * and the logout response never got sent because the connection
- * failed.
+ * Always up conn_logout_comp for the traditional TCP case just in case
+ * the RX Thread in iscsi_target_rx_opcode() is sleeping and the logout
+ * response never got sent because the connection failed.
+ *
+ * However for iser-target, isert_wait4logout() is using conn_logout_comp
+ * to signal logout response TX interrupt completion. Go ahead and skip
+ * this for iser since isert_rx_opcode() does not wait on logout failure,
+ * and to avoid iscsi_conn pointer dereference in iser-target code.
*/
- complete(&conn->conn_logout_comp);
+ if (conn->conn_transport->transport_type == ISCSI_TCP)
+ complete(&conn->conn_logout_comp);
iscsi_release_thread_set(conn);
#include <target/target_core_fabric.h>
#include <target/iscsi/iscsi_target_core.h>
-#include <target/iscsi/iscsi_transport.h>
#include "iscsi_target_seq_pdu_list.h"
#include "iscsi_target_tq.h"
#include "iscsi_target_erl0.h"
if (conn->conn_state == TARG_CONN_STATE_IN_LOGOUT) {
spin_unlock_bh(&conn->state_lock);
- if (conn->conn_transport->transport_type == ISCSI_TCP)
- iscsit_close_connection(conn);
+ iscsit_close_connection(conn);
return;
}
transport_free_session(tl_nexus->se_sess);
goto out;
}
- /*
- * Now, register the SAS I_T Nexus as active with the call to
- * transport_register_session()
- */
- __transport_register_session(se_tpg, tl_nexus->se_sess->se_node_acl,
+ /* Now, register the SAS I_T Nexus as active. */
+ transport_register_session(se_tpg, tl_nexus->se_sess->se_node_acl,
tl_nexus->se_sess, tl_nexus);
tl_tpg->tl_nexus = tl_nexus;
pr_debug("TCM_Loop_ConfigFS: Established I_T Nexus to emulated"
return aligned_max_sectors;
}
+bool se_dev_check_wce(struct se_device *dev)
+{
+ bool wce = false;
+
+ if (dev->transport->get_write_cache)
+ wce = dev->transport->get_write_cache(dev);
+ else if (dev->dev_attrib.emulate_write_cache > 0)
+ wce = true;
+
+ return wce;
+}
+
int se_dev_set_max_unmap_lba_count(
struct se_device *dev,
u32 max_unmap_lba_count)
pr_err("Illegal value %d\n", flag);
return -EINVAL;
}
+ if (flag &&
+ dev->transport->get_write_cache) {
+ pr_err("emulate_fua_write not supported for this device\n");
+ return -EINVAL;
+ }
+ if (dev->export_count) {
+ pr_err("emulate_fua_write cannot be changed with active"
+ " exports: %d\n", dev->export_count);
+ return -EINVAL;
+ }
dev->dev_attrib.emulate_fua_write = flag;
pr_debug("dev[%p]: SE Device Forced Unit Access WRITEs: %d\n",
dev, dev->dev_attrib.emulate_fua_write);
pr_err("emulate_write_cache not supported for this device\n");
return -EINVAL;
}
-
+ if (dev->export_count) {
+ pr_err("emulate_write_cache cannot be changed with active"
+ " exports: %d\n", dev->export_count);
+ return -EINVAL;
+ }
dev->dev_attrib.emulate_write_cache = flag;
pr_debug("dev[%p]: SE Device WRITE_CACHE_EMULATION flag: %d\n",
dev, dev->dev_attrib.emulate_write_cache);
ret = dev->transport->configure_device(dev);
if (ret)
goto out;
- dev->dev_flags |= DF_CONFIGURED;
-
/*
* XXX: there is not much point to have two different values here..
*/
list_add_tail(&dev->g_dev_node, &g_device_list);
mutex_unlock(&g_device_mutex);
+ dev->dev_flags |= DF_CONFIGURED;
+
return 0;
out_free_alua:
struct pscsi_dev_virt *pdv = PSCSI_DEV(dev);
struct scsi_device *sd = pdv->pdv_sd;
- return sd->type;
+ return (sd) ? sd->type : TYPE_NO_LUN;
}
static sector_t pscsi_get_blocks(struct se_device *dev)
}
}
if (cdb[1] & 0x8) {
- if (!dev->dev_attrib.emulate_fua_write ||
- !dev->dev_attrib.emulate_write_cache) {
+ if (!dev->dev_attrib.emulate_fua_write || !se_dev_check_wce(dev)) {
pr_err("Got CDB: 0x%02x with FUA bit set, but device"
" does not advertise support for FUA write\n",
cdb[0]);
}
EXPORT_SYMBOL(spc_emulate_evpd_83);
-static bool
-spc_check_dev_wce(struct se_device *dev)
-{
- bool wce = false;
-
- if (dev->transport->get_write_cache)
- wce = dev->transport->get_write_cache(dev);
- else if (dev->dev_attrib.emulate_write_cache > 0)
- wce = true;
-
- return wce;
-}
-
/* Extended INQUIRY Data VPD Page */
static sense_reason_t
spc_emulate_evpd_86(struct se_cmd *cmd, unsigned char *buf)
buf[5] = 0x07;
/* If WriteCache emulation is enabled, set V_SUP */
- if (spc_check_dev_wce(dev))
+ if (se_dev_check_wce(dev))
buf[6] = 0x01;
/* If an LBA map is present set R_SUP */
spin_lock(&cmd->se_dev->t10_alua.lba_map_lock);
if (pc == 1)
goto out;
- if (spc_check_dev_wce(dev))
+ if (se_dev_check_wce(dev))
p[2] = 0x04; /* Write Cache Enable */
p[12] = 0x20; /* Disabled Read Ahead */
(cmd->se_deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY)))
spc_modesense_write_protect(&buf[length], type);
- if ((spc_check_dev_wce(dev)) &&
+ if ((se_dev_check_wce(dev)) &&
(dev->dev_attrib.emulate_fua_write > 0))
spc_modesense_dpofua(&buf[length], type);
list_add_tail(&se_cmd->se_cmd_list, &se_sess->sess_cmd_list);
out:
spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
+
+ if (ret && ack_kref)
+ target_put_sess_cmd(se_sess, se_cmd);
+
return ret;
}
EXPORT_SYMBOL(target_get_sess_cmd);
ep = fc_seq_exch(seq);
if (ep) {
lport = ep->lp;
- if (lport && (ep->xid <= lport->lro_xid))
+ if (lport && (ep->xid <= lport->lro_xid)) {
/*
* "ddp_done" trigger invalidation of HW
* specific DDP context
* identified using ep->xid)
*/
cmd->was_ddp_setup = 0;
+ }
}
}
}
result = acpi_parse_art(priv->adev->handle, &priv->art_count,
&priv->arts, true);
if (result)
- goto free_priv;
-
+ dev_dbg(&pdev->dev, "_ART table parsing error\n");
result = acpi_parse_trt(priv->adev->handle, &priv->trt_count,
&priv->trts, true);
if (result)
- goto free_art;
+ dev_dbg(&pdev->dev, "_TRT table parsing error\n");
platform_set_drvdata(pdev, priv);
&int3400_thermal_params, 0, 0);
if (IS_ERR(priv->thermal)) {
result = PTR_ERR(priv->thermal);
- goto free_trt;
+ goto free_art_trt;
}
priv->rel_misc_dev_res = acpi_thermal_rel_misc_device_add(
free_zone:
thermal_zone_device_unregister(priv->thermal);
-free_trt:
+free_art_trt:
kfree(priv->trts);
-free_art:
kfree(priv->arts);
free_priv:
kfree(priv);
trip_cnt, GFP_KERNEL);
if (!int34x_thermal_zone->aux_trips) {
ret = -ENOMEM;
- goto free_mem;
+ goto err_trip_alloc;
}
trip_mask = BIT(trip_cnt) - 1;
int34x_thermal_zone->aux_trip_nr = trip_cnt;
0, 0);
if (IS_ERR(int34x_thermal_zone->zone)) {
ret = PTR_ERR(int34x_thermal_zone->zone);
- goto free_lpat;
+ goto err_thermal_zone;
}
return int34x_thermal_zone;
-free_lpat:
+err_thermal_zone:
acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
-free_mem:
+ kfree(int34x_thermal_zone->aux_trips);
+err_trip_alloc:
kfree(int34x_thermal_zone);
return ERR_PTR(ret);
}
{
thermal_zone_device_unregister(int34x_thermal_zone->zone);
acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
+ kfree(int34x_thermal_zone->aux_trips);
kfree(int34x_thermal_zone);
}
EXPORT_SYMBOL_GPL(int340x_thermal_zone_remove);
{ X86_VENDOR_INTEL, 6, 0x45},
{ X86_VENDOR_INTEL, 6, 0x46},
{ X86_VENDOR_INTEL, 6, 0x4c},
+ { X86_VENDOR_INTEL, 6, 0x4d},
{ X86_VENDOR_INTEL, 6, 0x56},
{}
};
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (irq) {
- int ret;
-
/*
* platform has IRQ support.
* Then, driver uses common registers
- */
-
- ret = devm_request_irq(dev, irq->start, rcar_thermal_irq, 0,
- dev_name(dev), common);
- if (ret) {
- dev_err(dev, "irq request failed\n ");
- return ret;
- }
-
- /*
* rcar_has_irq_support() will be enabled
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, mres++);
}
/* enable temperature comparation */
- if (irq)
+ if (irq) {
+ ret = devm_request_irq(dev, irq->start, rcar_thermal_irq, 0,
+ dev_name(dev), common);
+ if (ret) {
+ dev_err(dev, "irq request failed\n ");
+ goto error_unregister;
+ }
+
rcar_thermal_common_write(common, ENR, enr_bits);
+ }
platform_set_drvdata(pdev, common);
error_unregister:
rcar_thermal_for_each_priv(priv, common) {
- thermal_zone_device_unregister(priv->zone);
if (rcar_has_irq_support(priv))
rcar_thermal_irq_disable(priv);
+ thermal_zone_device_unregister(priv->zone);
}
pm_runtime_put(dev);
struct rcar_thermal_priv *priv;
rcar_thermal_for_each_priv(priv, common) {
- thermal_zone_device_unregister(priv->zone);
if (rcar_has_irq_support(priv))
rcar_thermal_irq_disable(priv);
+ thermal_zone_device_unregister(priv->zone);
}
pm_runtime_put(dev);
if (on) {
con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT);
+ con |= (1 << EXYNOS7_PD_DET_EN_SHIFT);
interrupt_en =
(of_thermal_is_trip_valid(tz, 7)
<< EXYNOS7_TMU_INTEN_RISE7_SHIFT) |
interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT;
} else {
con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT);
+ con &= ~(1 << EXYNOS7_PD_DET_EN_SHIFT);
interrupt_en = 0; /* Disable all interrupts */
}
- con |= 1 << EXYNOS7_PD_DET_EN_SHIFT;
writel(interrupt_en, data->base + EXYNOS7_TMU_REG_INTEN);
writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
}
static const struct of_device_id exynos_tmu_match[] = {
- {
- .compatible = "samsung,exynos3250-tmu",
- },
- {
- .compatible = "samsung,exynos4210-tmu",
- },
- {
- .compatible = "samsung,exynos4412-tmu",
- },
- {
- .compatible = "samsung,exynos5250-tmu",
- },
- {
- .compatible = "samsung,exynos5260-tmu",
- },
- {
- .compatible = "samsung,exynos5420-tmu",
- },
- {
- .compatible = "samsung,exynos5420-tmu-ext-triminfo",
- },
- {
- .compatible = "samsung,exynos5440-tmu",
- },
- {
- .compatible = "samsung,exynos7-tmu",
- },
- {},
+ { .compatible = "samsung,exynos3250-tmu", },
+ { .compatible = "samsung,exynos4210-tmu", },
+ { .compatible = "samsung,exynos4412-tmu", },
+ { .compatible = "samsung,exynos5250-tmu", },
+ { .compatible = "samsung,exynos5260-tmu", },
+ { .compatible = "samsung,exynos5420-tmu", },
+ { .compatible = "samsung,exynos5420-tmu-ext-triminfo", },
+ { .compatible = "samsung,exynos5440-tmu", },
+ { .compatible = "samsung,exynos7-tmu", },
+ { /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, exynos_tmu_match);
return sprintf(buf, "%d\n", instance->trip);
}
+static struct attribute *cooling_device_attrs[] = {
+ &dev_attr_cdev_type.attr,
+ &dev_attr_max_state.attr,
+ &dev_attr_cur_state.attr,
+ NULL,
+};
+
+static const struct attribute_group cooling_device_attr_group = {
+ .attrs = cooling_device_attrs,
+};
+
+static const struct attribute_group *cooling_device_attr_groups[] = {
+ &cooling_device_attr_group,
+ NULL,
+};
+
/* Device management */
/**
cdev->ops = ops;
cdev->updated = false;
cdev->device.class = &thermal_class;
+ cdev->device.groups = cooling_device_attr_groups;
cdev->devdata = devdata;
dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
result = device_register(&cdev->device);
return ERR_PTR(result);
}
- /* sys I/F */
- if (type) {
- result = device_create_file(&cdev->device, &dev_attr_cdev_type);
- if (result)
- goto unregister;
- }
-
- result = device_create_file(&cdev->device, &dev_attr_max_state);
- if (result)
- goto unregister;
-
- result = device_create_file(&cdev->device, &dev_attr_cur_state);
- if (result)
- goto unregister;
-
/* Add 'this' new cdev to the global cdev list */
mutex_lock(&thermal_list_lock);
list_add(&cdev->node, &thermal_cdev_list);
bind_cdev(cdev);
return cdev;
-
-unregister:
- release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id);
- device_unregister(&cdev->device);
- return ERR_PTR(result);
}
/**
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int ti_bandgap_save_ctxt(struct ti_bandgap *bgp)
{
int i;
data = ti_bandgap_get_sensor_data(bgp, id);
- if (data && data->cool_dev)
+ if (data)
cpufreq_cooling_unregister(data->cool_dev);
return 0;
return circ_cnt(&bfin_jc_write_buf);
}
-static void
-bfin_jc_wait_until_sent(struct tty_struct *tty, int timeout)
-{
- unsigned long expire = jiffies + timeout;
- while (!circ_empty(&bfin_jc_write_buf)) {
- if (signal_pending(current))
- break;
- if (time_after(jiffies, expire))
- break;
- }
-}
-
static const struct tty_operations bfin_jc_ops = {
.open = bfin_jc_open,
.close = bfin_jc_close,
.flush_chars = bfin_jc_flush_chars,
.write_room = bfin_jc_write_room,
.chars_in_buffer = bfin_jc_chars_in_buffer,
- .wait_until_sent = bfin_jc_wait_until_sent,
};
static int __init bfin_jc_init(void)
/*
* Clear the interrupt registers.
*/
- if (serial_port_in(port, UART_LSR) & UART_LSR_DR)
- serial_port_in(port, UART_RX);
+ serial_port_in(port, UART_LSR);
+ serial_port_in(port, UART_RX);
serial_port_in(port, UART_IIR);
serial_port_in(port, UART_MSR);
* saved flags to avoid getting false values from polling
* routines or the previous session.
*/
- if (serial_port_in(port, UART_LSR) & UART_LSR_DR)
- serial_port_in(port, UART_RX);
+ serial_port_in(port, UART_LSR);
+ serial_port_in(port, UART_RX);
serial_port_in(port, UART_IIR);
serial_port_in(port, UART_MSR);
up->lsr_saved_flags = 0;
* Read data port to reset things, and then unlink from
* the IRQ chain.
*/
- if (serial_port_in(port, UART_LSR) & UART_LSR_DR)
- serial_port_in(port, UART_RX);
+ serial_port_in(port, UART_RX);
serial8250_rpm_put(up);
del_timer_sync(&up->timer);
u8 usr_reg;
int last_mcr;
int line;
+ int msr_mask_on;
+ int msr_mask_off;
struct clk *clk;
struct clk *pclk;
struct reset_control *rst;
value &= ~UART_MSR_DCTS;
}
+ /* Override any modem control signals if needed */
+ if (offset == UART_MSR) {
+ value |= d->msr_mask_on;
+ value &= ~d->msr_mask_off;
+ }
+
return value;
}
dw8250_force_idle(p);
writeb(value, p->membase + (UART_LCR << p->regshift));
}
- dev_err(p->dev, "Couldn't set LCR to %d\n", value);
+ /*
+ * FIXME: this deadlocks if port->lock is already held
+ * dev_err(p->dev, "Couldn't set LCR to %d\n", value);
+ */
}
}
__raw_writeq(value & 0xff,
p->membase + (UART_LCR << p->regshift));
}
- dev_err(p->dev, "Couldn't set LCR to %d\n", value);
+ /*
+ * FIXME: this deadlocks if port->lock is already held
+ * dev_err(p->dev, "Couldn't set LCR to %d\n", value);
+ */
}
}
#endif /* CONFIG_64BIT */
dw8250_force_idle(p);
writel(value, p->membase + (UART_LCR << p->regshift));
}
- dev_err(p->dev, "Couldn't set LCR to %d\n", value);
+ /*
+ * FIXME: this deadlocks if port->lock is already held
+ * dev_err(p->dev, "Couldn't set LCR to %d\n", value);
+ */
}
}
if (id >= 0)
p->line = id;
+ if (of_property_read_bool(np, "dcd-override")) {
+ /* Always report DCD as active */
+ data->msr_mask_on |= UART_MSR_DCD;
+ data->msr_mask_off |= UART_MSR_DDCD;
+ }
+
+ if (of_property_read_bool(np, "dsr-override")) {
+ /* Always report DSR as active */
+ data->msr_mask_on |= UART_MSR_DSR;
+ data->msr_mask_off |= UART_MSR_DDSR;
+ }
+
+ if (of_property_read_bool(np, "cts-override")) {
+ /* Always report DSR as active */
+ data->msr_mask_on |= UART_MSR_DSR;
+ data->msr_mask_off |= UART_MSR_DDSR;
+ }
+
+ if (of_property_read_bool(np, "ri-override")) {
+ /* Always report Ring indicator as inactive */
+ data->msr_mask_off |= UART_MSR_RI;
+ data->msr_mask_off |= UART_MSR_TERI;
+ }
+
/* clock got configured through clk api, all done */
if (p->uartclk)
return 0;
"Please send the output of lspci -vv, this\n"
"message (0x%04x,0x%04x,0x%04x,0x%04x), the\n"
"manufacturer and name of serial board or\n"
- "modem board to rmk+serial@arm.linux.org.uk.\n",
+ "modem board to <linux-serial@vger.kernel.org>.\n",
pci_name(dev), str, dev->vendor, dev->device,
dev->subsystem_vendor, dev->subsystem_device);
}
.subdevice = PCI_ANY_ID,
.setup = byt_serial_setup,
},
- {
- .vendor = PCI_VENDOR_ID_INTEL,
- .device = PCI_DEVICE_ID_INTEL_QRK_UART,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .setup = pci_default_setup,
- },
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_BSW_UART1,
/*
* PLX
*/
- {
- .vendor = PCI_VENDOR_ID_PLX,
- .device = PCI_DEVICE_ID_PLX_9030,
- .subvendor = PCI_SUBVENDOR_ID_PERLE,
- .subdevice = PCI_ANY_ID,
- .setup = pci_default_setup,
- },
{
.vendor = PCI_VENDOR_ID_PLX,
.device = PCI_DEVICE_ID_PLX_9050,
PCI_ANY_ID, PCI_ANY_ID,
0, 0, pbn_b0_bt_2_115200 },
- { PCI_VENDOR_ID_WCH, PCI_DEVICE_ID_WCH_CH352_2S,
- PCI_ANY_ID, PCI_ANY_ID,
- 0, 0, pbn_b0_bt_2_115200 },
-
{ PCIE_VENDOR_ID_WCH, PCIE_DEVICE_ID_WCH_CH384_4S,
PCI_ANY_ID, PCI_ANY_ID,
0, 0, pbn_wch384_4 },
#include <linux/gpio/consumer.h>
#include <linux/err.h>
#include <linux/irq.h>
+#include <linux/suspend.h>
#include <asm/io.h>
#include <asm/ioctls.h>
bool ms_irq_enabled;
bool is_usart; /* usart or uart */
struct timer_list uart_timer; /* uart timer */
+
+ bool suspended;
+ unsigned int pending;
+ unsigned int pending_status;
+ spinlock_t lock_suspended;
+
int (*prepare_rx)(struct uart_port *port);
int (*prepare_tx)(struct uart_port *port);
void (*schedule_rx)(struct uart_port *port);
{
struct uart_port *port = dev_id;
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- unsigned int status, pending, pass_counter = 0;
+ unsigned int status, pending, mask, pass_counter = 0;
bool gpio_handled = false;
+ spin_lock(&atmel_port->lock_suspended);
+
do {
status = atmel_get_lines_status(port);
- pending = status & UART_GET_IMR(port);
+ mask = UART_GET_IMR(port);
+ pending = status & mask;
if (!gpio_handled) {
/*
* Dealing with GPIO interrupt
if (!pending)
break;
+ if (atmel_port->suspended) {
+ atmel_port->pending |= pending;
+ atmel_port->pending_status = status;
+ UART_PUT_IDR(port, mask);
+ pm_system_wakeup();
+ break;
+ }
+
atmel_handle_receive(port, pending);
atmel_handle_status(port, pending, status);
atmel_handle_transmit(port, pending);
} while (pass_counter++ < ATMEL_ISR_PASS_LIMIT);
+ spin_unlock(&atmel_port->lock_suspended);
+
return pass_counter ? IRQ_HANDLED : IRQ_NONE;
}
/*
* Allocate the IRQ
*/
- retval = request_irq(port->irq, atmel_interrupt, IRQF_SHARED,
+ retval = request_irq(port->irq, atmel_interrupt,
+ IRQF_SHARED | IRQF_COND_SUSPEND,
tty ? tty->name : "atmel_serial", port);
if (retval) {
dev_err(port->dev, "atmel_startup - Can't get irq\n");
/* we can not wake up if we're running on slow clock */
atmel_port->may_wakeup = device_may_wakeup(&pdev->dev);
- if (atmel_serial_clk_will_stop())
+ if (atmel_serial_clk_will_stop()) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&atmel_port->lock_suspended, flags);
+ atmel_port->suspended = true;
+ spin_unlock_irqrestore(&atmel_port->lock_suspended, flags);
device_set_wakeup_enable(&pdev->dev, 0);
+ }
uart_suspend_port(&atmel_uart, port);
{
struct uart_port *port = platform_get_drvdata(pdev);
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ unsigned long flags;
+
+ spin_lock_irqsave(&atmel_port->lock_suspended, flags);
+ if (atmel_port->pending) {
+ atmel_handle_receive(port, atmel_port->pending);
+ atmel_handle_status(port, atmel_port->pending,
+ atmel_port->pending_status);
+ atmel_handle_transmit(port, atmel_port->pending);
+ atmel_port->pending = 0;
+ }
+ atmel_port->suspended = false;
+ spin_unlock_irqrestore(&atmel_port->lock_suspended, flags);
uart_resume_port(&atmel_uart, port);
device_set_wakeup_enable(&pdev->dev, atmel_port->may_wakeup);
port->backup_imr = 0;
port->uart.line = ret;
+ spin_lock_init(&port->lock_suspended);
+
ret = atmel_init_gpios(port, &pdev->dev);
if (ret < 0)
dev_err(&pdev->dev, "%s",
writeb(val | UARTPFIFO_TXFE | UARTPFIFO_RXFE,
sport->port.membase + UARTPFIFO);
+ /* explicitly clear RDRF */
+ readb(sport->port.membase + UARTSR1);
+
/* flush Tx and Rx FIFO */
writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH,
sport->port.membase + UARTCFIFO);
sport->txfifo_size = 0x1 << (((temp >> UARTPFIFO_TXSIZE_OFF) &
UARTPFIFO_FIFOSIZE_MASK) + 1);
+ sport->port.fifosize = sport->txfifo_size;
+
sport->rxfifo_size = 0x1 << (((temp >> UARTPFIFO_RXSIZE_OFF) &
UARTPFIFO_FIFOSIZE_MASK) + 1);
if (of_find_property(np, "no-loopback-test", NULL))
port->flags |= UPF_SKIP_TEST;
- ret = of_alias_get_id(np, "serial");
- if (ret >= 0)
- port->line = ret;
-
port->dev = &ofdev->dev;
switch (type) {
free_irq(ourport->tx_irq, ourport);
tx_enabled(port) = 0;
ourport->tx_claimed = 0;
+ ourport->tx_mode = 0;
}
if (ourport->rx_claimed) {
ims = serial_in(port, SPRD_IMSR);
- if (!ims)
+ if (!ims) {
+ spin_unlock(&port->lock);
return IRQ_NONE;
+ }
serial_out(port, SPRD_ICLR, ~0);
/* We limit tty time update visibility to every 8 seconds or so. */
static void tty_update_time(struct timespec *time)
{
- unsigned long sec = get_seconds() & ~7;
- if ((long)(sec - time->tv_sec) > 0)
+ unsigned long sec = get_seconds();
+ if (abs(sec - time->tv_sec) & ~7)
time->tv_sec = sec;
}
#endif
if (!timeout)
timeout = MAX_SCHEDULE_TIMEOUT;
- if (wait_event_interruptible_timeout(tty->write_wait,
- !tty_chars_in_buffer(tty), timeout) >= 0) {
- if (tty->ops->wait_until_sent)
- tty->ops->wait_until_sent(tty, timeout);
- }
+
+ timeout = wait_event_interruptible_timeout(tty->write_wait,
+ !tty_chars_in_buffer(tty), timeout);
+ if (timeout <= 0)
+ return;
+
+ if (timeout == MAX_SCHEDULE_TIMEOUT)
+ timeout = 0;
+
+ if (tty->ops->wait_until_sent)
+ tty->ops->wait_until_sent(tty, timeout);
}
EXPORT_SYMBOL(tty_wait_until_sent);
return retval;
}
+static int otg_a_alt_hnp_support(struct ci_hdrc *ci)
+{
+ dev_warn(&ci->gadget.dev,
+ "connect the device to an alternate port if you want HNP\n");
+ return isr_setup_status_phase(ci);
+}
+
/**
* isr_setup_packet_handler: setup packet handler
* @ci: UDC descriptor
ci);
}
break;
+ case USB_DEVICE_A_ALT_HNP_SUPPORT:
+ if (ci_otg_is_fsm_mode(ci))
+ err = otg_a_alt_hnp_support(ci);
+ break;
default:
goto delegate;
}
static const struct usb_device_id acm_ids[] = {
/* quirky and broken devices */
+ { USB_DEVICE(0x076d, 0x0006), /* Denso Cradle CU-321 */
+ .driver_info = NO_UNION_NORMAL, },/* has no union descriptor */
{ USB_DEVICE(0x17ef, 0x7000), /* Lenovo USB modem */
.driver_info = NO_UNION_NORMAL, },/* has no union descriptor */
{ USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
break;
case OTG_STATE_B_PERIPHERAL:
otg_chrg_vbus(fsm, 0);
- otg_loc_conn(fsm, 1);
otg_loc_sof(fsm, 0);
otg_set_protocol(fsm, PROTO_GADGET);
+ otg_loc_conn(fsm, 1);
break;
case OTG_STATE_B_WAIT_ACON:
otg_chrg_vbus(fsm, 0);
break;
case OTG_STATE_A_PERIPHERAL:
- otg_loc_conn(fsm, 1);
otg_loc_sof(fsm, 0);
otg_set_protocol(fsm, PROTO_GADGET);
otg_drv_vbus(fsm, 1);
+ otg_loc_conn(fsm, 1);
otg_add_timer(fsm, A_BIDL_ADIS);
break;
case OTG_STATE_A_WAIT_VFALL:
as->status = urb->status;
signr = as->signr;
if (signr) {
+ memset(&sinfo, 0, sizeof(sinfo));
sinfo.si_signo = as->signr;
sinfo.si_errno = as->status;
sinfo.si_code = SI_ASYNCIO;
wake_up_all(&ps->wait);
list_del_init(&ps->list);
if (ps->discsignr) {
+ memset(&sinfo, 0, sizeof(sinfo));
sinfo.si_signo = ps->discsignr;
sinfo.si_errno = EPIPE;
sinfo.si_code = SI_ASYNCIO;
dwc2_is_host_mode(hsotg) ? "Host" : "Device",
dwc2_op_state_str(hsotg));
+ if (hsotg->op_state == OTG_STATE_A_HOST)
+ dwc2_hcd_disconnect(hsotg);
+
/* Change to L3 (OFF) state */
hsotg->lx_state = DWC2_L3;
omap->irq0_offset, value);
}
+static void dwc3_omap_write_irqmisc_clr(struct dwc3_omap *omap, u32 value)
+{
+ dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_CLR_MISC +
+ omap->irqmisc_offset, value);
+}
+
+static void dwc3_omap_write_irq0_clr(struct dwc3_omap *omap, u32 value)
+{
+ dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_CLR_0 -
+ omap->irq0_offset, value);
+}
+
static void dwc3_omap_set_mailbox(struct dwc3_omap *omap,
enum omap_dwc3_vbus_id_status status)
{
static void dwc3_omap_disable_irqs(struct dwc3_omap *omap)
{
+ u32 reg;
+
/* disable all IRQs */
- dwc3_omap_write_irqmisc_set(omap, 0x00);
- dwc3_omap_write_irq0_set(omap, 0x00);
+ reg = USBOTGSS_IRQO_COREIRQ_ST;
+ dwc3_omap_write_irq0_clr(omap, reg);
+
+ reg = (USBOTGSS_IRQMISC_OEVT |
+ USBOTGSS_IRQMISC_DRVVBUS_RISE |
+ USBOTGSS_IRQMISC_CHRGVBUS_RISE |
+ USBOTGSS_IRQMISC_DISCHRGVBUS_RISE |
+ USBOTGSS_IRQMISC_IDPULLUP_RISE |
+ USBOTGSS_IRQMISC_DRVVBUS_FALL |
+ USBOTGSS_IRQMISC_CHRGVBUS_FALL |
+ USBOTGSS_IRQMISC_DISCHRGVBUS_FALL |
+ USBOTGSS_IRQMISC_IDPULLUP_FALL);
+
+ dwc3_omap_write_irqmisc_clr(omap, reg);
}
static u64 dwc3_omap_dma_mask = DMA_BIT_MASK(32);
if (desc->opts_mutex)
mutex_lock(desc->opts_mutex);
memcpy(desc->ext_compat_id, page, l);
- desc->ext_compat_id[l] = '\0';
if (desc->opts_mutex)
mutex_unlock(desc->opts_mutex);
if (desc->opts_mutex)
mutex_lock(desc->opts_mutex);
memcpy(desc->ext_compat_id + 8, page, l);
- desc->ext_compat_id[l + 8] = '\0';
if (desc->opts_mutex)
mutex_unlock(desc->opts_mutex);
bool read;
struct kiocb *kiocb;
- const struct iovec *iovec;
- unsigned long nr_segs;
- char __user *buf;
- size_t len;
+ struct iov_iter data;
+ const void *to_free;
+ char *buf;
struct mm_struct *mm;
struct work_struct work;
io_data->req->actual;
if (io_data->read && ret > 0) {
- int i;
- size_t pos = 0;
-
- /*
- * Since req->length may be bigger than io_data->len (after
- * being rounded up to maxpacketsize), we may end up with more
- * data then user space has space for.
- */
- ret = min_t(int, ret, io_data->len);
-
use_mm(io_data->mm);
- for (i = 0; i < io_data->nr_segs; i++) {
- size_t len = min_t(size_t, ret - pos,
- io_data->iovec[i].iov_len);
- if (!len)
- break;
- if (unlikely(copy_to_user(io_data->iovec[i].iov_base,
- &io_data->buf[pos], len))) {
- ret = -EFAULT;
- break;
- }
- pos += len;
- }
+ ret = copy_to_iter(io_data->buf, ret, &io_data->data);
+ if (iov_iter_count(&io_data->data))
+ ret = -EFAULT;
unuse_mm(io_data->mm);
}
io_data->kiocb->private = NULL;
if (io_data->read)
- kfree(io_data->iovec);
+ kfree(io_data->to_free);
kfree(io_data->buf);
kfree(io_data);
}
* before the waiting completes, so do not assign to 'gadget' earlier
*/
struct usb_gadget *gadget = epfile->ffs->gadget;
+ size_t copied;
spin_lock_irq(&epfile->ffs->eps_lock);
/* In the meantime, endpoint got disabled or changed. */
spin_unlock_irq(&epfile->ffs->eps_lock);
return -ESHUTDOWN;
}
+ data_len = iov_iter_count(&io_data->data);
/*
* Controller may require buffer size to be aligned to
* maxpacketsize of an out endpoint.
*/
- data_len = io_data->read ?
- usb_ep_align_maybe(gadget, ep->ep, io_data->len) :
- io_data->len;
+ if (io_data->read)
+ data_len = usb_ep_align_maybe(gadget, ep->ep, data_len);
spin_unlock_irq(&epfile->ffs->eps_lock);
data = kmalloc(data_len, GFP_KERNEL);
if (unlikely(!data))
return -ENOMEM;
- if (io_data->aio && !io_data->read) {
- int i;
- size_t pos = 0;
- for (i = 0; i < io_data->nr_segs; i++) {
- if (unlikely(copy_from_user(&data[pos],
- io_data->iovec[i].iov_base,
- io_data->iovec[i].iov_len))) {
- ret = -EFAULT;
- goto error;
- }
- pos += io_data->iovec[i].iov_len;
- }
- } else {
- if (!io_data->read &&
- unlikely(__copy_from_user(data, io_data->buf,
- io_data->len))) {
+ if (!io_data->read) {
+ copied = copy_from_iter(data, data_len, &io_data->data);
+ if (copied != data_len) {
ret = -EFAULT;
goto error;
}
*/
ret = ep->status;
if (io_data->read && ret > 0) {
- ret = min_t(size_t, ret, io_data->len);
-
- if (unlikely(copy_to_user(io_data->buf,
- data, ret)))
+ ret = copy_to_iter(data, ret, &io_data->data);
+ if (unlikely(iov_iter_count(&io_data->data)))
ret = -EFAULT;
}
}
return ret;
}
-static ssize_t
-ffs_epfile_write(struct file *file, const char __user *buf, size_t len,
- loff_t *ptr)
-{
- struct ffs_io_data io_data;
-
- ENTER();
-
- io_data.aio = false;
- io_data.read = false;
- io_data.buf = (char * __user)buf;
- io_data.len = len;
-
- return ffs_epfile_io(file, &io_data);
-}
-
-static ssize_t
-ffs_epfile_read(struct file *file, char __user *buf, size_t len, loff_t *ptr)
-{
- struct ffs_io_data io_data;
-
- ENTER();
-
- io_data.aio = false;
- io_data.read = true;
- io_data.buf = buf;
- io_data.len = len;
-
- return ffs_epfile_io(file, &io_data);
-}
-
static int
ffs_epfile_open(struct inode *inode, struct file *file)
{
return value;
}
-static ssize_t ffs_epfile_aio_write(struct kiocb *kiocb,
- const struct iovec *iovec,
- unsigned long nr_segs, loff_t loff)
+static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from)
{
- struct ffs_io_data *io_data;
+ struct ffs_io_data io_data, *p = &io_data;
+ ssize_t res;
ENTER();
- io_data = kmalloc(sizeof(*io_data), GFP_KERNEL);
- if (unlikely(!io_data))
- return -ENOMEM;
+ if (!is_sync_kiocb(kiocb)) {
+ p = kmalloc(sizeof(io_data), GFP_KERNEL);
+ if (unlikely(!p))
+ return -ENOMEM;
+ p->aio = true;
+ } else {
+ p->aio = false;
+ }
- io_data->aio = true;
- io_data->read = false;
- io_data->kiocb = kiocb;
- io_data->iovec = iovec;
- io_data->nr_segs = nr_segs;
- io_data->len = kiocb->ki_nbytes;
- io_data->mm = current->mm;
+ p->read = false;
+ p->kiocb = kiocb;
+ p->data = *from;
+ p->mm = current->mm;
- kiocb->private = io_data;
+ kiocb->private = p;
kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
- return ffs_epfile_io(kiocb->ki_filp, io_data);
+ res = ffs_epfile_io(kiocb->ki_filp, p);
+ if (res == -EIOCBQUEUED)
+ return res;
+ if (p->aio)
+ kfree(p);
+ else
+ *from = p->data;
+ return res;
}
-static ssize_t ffs_epfile_aio_read(struct kiocb *kiocb,
- const struct iovec *iovec,
- unsigned long nr_segs, loff_t loff)
+static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to)
{
- struct ffs_io_data *io_data;
- struct iovec *iovec_copy;
+ struct ffs_io_data io_data, *p = &io_data;
+ ssize_t res;
ENTER();
- iovec_copy = kmalloc_array(nr_segs, sizeof(*iovec_copy), GFP_KERNEL);
- if (unlikely(!iovec_copy))
- return -ENOMEM;
-
- memcpy(iovec_copy, iovec, sizeof(struct iovec)*nr_segs);
-
- io_data = kmalloc(sizeof(*io_data), GFP_KERNEL);
- if (unlikely(!io_data)) {
- kfree(iovec_copy);
- return -ENOMEM;
+ if (!is_sync_kiocb(kiocb)) {
+ p = kmalloc(sizeof(io_data), GFP_KERNEL);
+ if (unlikely(!p))
+ return -ENOMEM;
+ p->aio = true;
+ } else {
+ p->aio = false;
}
- io_data->aio = true;
- io_data->read = true;
- io_data->kiocb = kiocb;
- io_data->iovec = iovec_copy;
- io_data->nr_segs = nr_segs;
- io_data->len = kiocb->ki_nbytes;
- io_data->mm = current->mm;
+ p->read = true;
+ p->kiocb = kiocb;
+ if (p->aio) {
+ p->to_free = dup_iter(&p->data, to, GFP_KERNEL);
+ if (!p->to_free) {
+ kfree(p);
+ return -ENOMEM;
+ }
+ } else {
+ p->data = *to;
+ p->to_free = NULL;
+ }
+ p->mm = current->mm;
- kiocb->private = io_data;
+ kiocb->private = p;
kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
- return ffs_epfile_io(kiocb->ki_filp, io_data);
+ res = ffs_epfile_io(kiocb->ki_filp, p);
+ if (res == -EIOCBQUEUED)
+ return res;
+
+ if (p->aio) {
+ kfree(p->to_free);
+ kfree(p);
+ } else {
+ *to = p->data;
+ }
+ return res;
}
static int
.llseek = no_llseek,
.open = ffs_epfile_open,
- .write = ffs_epfile_write,
- .read = ffs_epfile_read,
- .aio_write = ffs_epfile_aio_write,
- .aio_read = ffs_epfile_aio_read,
+ .write = new_sync_write,
+ .read = new_sync_read,
+ .write_iter = ffs_epfile_write_iter,
+ .read_iter = ffs_epfile_read_iter,
.release = ffs_epfile_release,
.unlocked_ioctl = ffs_epfile_ioctl,
};
return status;
}
-const struct file_operations f_hidg_fops = {
+static const struct file_operations f_hidg_fops = {
.owner = THIS_MODULE,
.open = f_hidg_open,
.release = f_hidg_release,
struct usb_composite_dev *cdev;
cdev = loop->function.config->cdev;
- disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL, NULL,
- NULL);
+ disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL);
VDBG(cdev, "%s disabled\n", loop->function.name);
}
return -EINVAL;
spin_lock(&port->lock);
- __pn_reset(f);
+
+ if (fp->in_ep->driver_data)
+ __pn_reset(f);
+
if (alt == 1) {
int i;
#include "gadget_chips.h"
#include "u_f.h"
-#define USB_MS_TO_SS_INTERVAL(x) USB_MS_TO_HS_INTERVAL(x)
-
-enum eptype {
- EP_CONTROL = 0,
- EP_BULK,
- EP_ISOC,
- EP_INTERRUPT,
-};
-
/*
* SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral
* controller drivers.
struct usb_ep *out_ep;
struct usb_ep *iso_in_ep;
struct usb_ep *iso_out_ep;
- struct usb_ep *int_in_ep;
- struct usb_ep *int_out_ep;
int cur_alt;
};
static unsigned isoc_maxpacket;
static unsigned isoc_mult;
static unsigned isoc_maxburst;
-static unsigned int_interval; /* In ms */
-static unsigned int_maxpacket;
-static unsigned int_mult;
-static unsigned int_maxburst;
static unsigned buflen;
/*-------------------------------------------------------------------------*/
/* .iInterface = DYNAMIC */
};
-static struct usb_interface_descriptor source_sink_intf_alt2 = {
- .bLength = USB_DT_INTERFACE_SIZE,
- .bDescriptorType = USB_DT_INTERFACE,
-
- .bAlternateSetting = 2,
- .bNumEndpoints = 2,
- .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
- /* .iInterface = DYNAMIC */
-};
-
/* full speed support: */
static struct usb_endpoint_descriptor fs_source_desc = {
.bInterval = 4,
};
-static struct usb_endpoint_descriptor fs_int_source_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bEndpointAddress = USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = cpu_to_le16(64),
- .bInterval = GZERO_INT_INTERVAL,
-};
-
-static struct usb_endpoint_descriptor fs_int_sink_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bEndpointAddress = USB_DIR_OUT,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = cpu_to_le16(64),
- .bInterval = GZERO_INT_INTERVAL,
-};
-
static struct usb_descriptor_header *fs_source_sink_descs[] = {
(struct usb_descriptor_header *) &source_sink_intf_alt0,
(struct usb_descriptor_header *) &fs_sink_desc,
(struct usb_descriptor_header *) &fs_source_desc,
(struct usb_descriptor_header *) &fs_iso_sink_desc,
(struct usb_descriptor_header *) &fs_iso_source_desc,
- (struct usb_descriptor_header *) &source_sink_intf_alt2,
-#define FS_ALT_IFC_2_OFFSET 8
- (struct usb_descriptor_header *) &fs_int_sink_desc,
- (struct usb_descriptor_header *) &fs_int_source_desc,
NULL,
};
.bInterval = 4,
};
-static struct usb_endpoint_descriptor hs_int_source_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = cpu_to_le16(1024),
- .bInterval = USB_MS_TO_HS_INTERVAL(GZERO_INT_INTERVAL),
-};
-
-static struct usb_endpoint_descriptor hs_int_sink_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = cpu_to_le16(1024),
- .bInterval = USB_MS_TO_HS_INTERVAL(GZERO_INT_INTERVAL),
-};
-
static struct usb_descriptor_header *hs_source_sink_descs[] = {
(struct usb_descriptor_header *) &source_sink_intf_alt0,
(struct usb_descriptor_header *) &hs_source_desc,
(struct usb_descriptor_header *) &hs_sink_desc,
(struct usb_descriptor_header *) &hs_iso_source_desc,
(struct usb_descriptor_header *) &hs_iso_sink_desc,
- (struct usb_descriptor_header *) &source_sink_intf_alt2,
-#define HS_ALT_IFC_2_OFFSET 8
- (struct usb_descriptor_header *) &hs_int_source_desc,
- (struct usb_descriptor_header *) &hs_int_sink_desc,
NULL,
};
.wBytesPerInterval = cpu_to_le16(1024),
};
-static struct usb_endpoint_descriptor ss_int_source_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = cpu_to_le16(1024),
- .bInterval = USB_MS_TO_SS_INTERVAL(GZERO_INT_INTERVAL),
-};
-
-struct usb_ss_ep_comp_descriptor ss_int_source_comp_desc = {
- .bLength = USB_DT_SS_EP_COMP_SIZE,
- .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
-
- .bMaxBurst = 0,
- .bmAttributes = 0,
- .wBytesPerInterval = cpu_to_le16(1024),
-};
-
-static struct usb_endpoint_descriptor ss_int_sink_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = cpu_to_le16(1024),
- .bInterval = USB_MS_TO_SS_INTERVAL(GZERO_INT_INTERVAL),
-};
-
-struct usb_ss_ep_comp_descriptor ss_int_sink_comp_desc = {
- .bLength = USB_DT_SS_EP_COMP_SIZE,
- .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
-
- .bMaxBurst = 0,
- .bmAttributes = 0,
- .wBytesPerInterval = cpu_to_le16(1024),
-};
-
static struct usb_descriptor_header *ss_source_sink_descs[] = {
(struct usb_descriptor_header *) &source_sink_intf_alt0,
(struct usb_descriptor_header *) &ss_source_desc,
(struct usb_descriptor_header *) &ss_iso_source_comp_desc,
(struct usb_descriptor_header *) &ss_iso_sink_desc,
(struct usb_descriptor_header *) &ss_iso_sink_comp_desc,
- (struct usb_descriptor_header *) &source_sink_intf_alt2,
-#define SS_ALT_IFC_2_OFFSET 14
- (struct usb_descriptor_header *) &ss_int_source_desc,
- (struct usb_descriptor_header *) &ss_int_source_comp_desc,
- (struct usb_descriptor_header *) &ss_int_sink_desc,
- (struct usb_descriptor_header *) &ss_int_sink_comp_desc,
NULL,
};
};
/*-------------------------------------------------------------------------*/
-static const char *get_ep_string(enum eptype ep_type)
-{
- switch (ep_type) {
- case EP_ISOC:
- return "ISOC-";
- case EP_INTERRUPT:
- return "INTERRUPT-";
- case EP_CONTROL:
- return "CTRL-";
- case EP_BULK:
- return "BULK-";
- default:
- return "UNKNOWN-";
- }
-}
static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len)
{
void disable_endpoints(struct usb_composite_dev *cdev,
struct usb_ep *in, struct usb_ep *out,
- struct usb_ep *iso_in, struct usb_ep *iso_out,
- struct usb_ep *int_in, struct usb_ep *int_out)
+ struct usb_ep *iso_in, struct usb_ep *iso_out)
{
disable_ep(cdev, in);
disable_ep(cdev, out);
disable_ep(cdev, iso_in);
if (iso_out)
disable_ep(cdev, iso_out);
- if (int_in)
- disable_ep(cdev, int_in);
- if (int_out)
- disable_ep(cdev, int_out);
}
static int
return id;
source_sink_intf_alt0.bInterfaceNumber = id;
source_sink_intf_alt1.bInterfaceNumber = id;
- source_sink_intf_alt2.bInterfaceNumber = id;
/* allocate bulk endpoints */
ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
if (isoc_maxpacket > 1024)
isoc_maxpacket = 1024;
- /* sanity check the interrupt module parameters */
- if (int_interval < 1)
- int_interval = 1;
- if (int_interval > 4096)
- int_interval = 4096;
- if (int_mult > 2)
- int_mult = 2;
- if (int_maxburst > 15)
- int_maxburst = 15;
-
- /* fill in the FS interrupt descriptors from the module parameters */
- fs_int_source_desc.wMaxPacketSize = int_maxpacket > 64 ?
- 64 : int_maxpacket;
- fs_int_source_desc.bInterval = int_interval > 255 ?
- 255 : int_interval;
- fs_int_sink_desc.wMaxPacketSize = int_maxpacket > 64 ?
- 64 : int_maxpacket;
- fs_int_sink_desc.bInterval = int_interval > 255 ?
- 255 : int_interval;
-
- /* allocate int endpoints */
- ss->int_in_ep = usb_ep_autoconfig(cdev->gadget, &fs_int_source_desc);
- if (!ss->int_in_ep)
- goto no_int;
- ss->int_in_ep->driver_data = cdev; /* claim */
-
- ss->int_out_ep = usb_ep_autoconfig(cdev->gadget, &fs_int_sink_desc);
- if (ss->int_out_ep) {
- ss->int_out_ep->driver_data = cdev; /* claim */
- } else {
- ss->int_in_ep->driver_data = NULL;
- ss->int_in_ep = NULL;
-no_int:
- fs_source_sink_descs[FS_ALT_IFC_2_OFFSET] = NULL;
- hs_source_sink_descs[HS_ALT_IFC_2_OFFSET] = NULL;
- ss_source_sink_descs[SS_ALT_IFC_2_OFFSET] = NULL;
- }
-
- if (int_maxpacket > 1024)
- int_maxpacket = 1024;
-
/* support high speed hardware */
hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress;
hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress;
/*
- * Fill in the HS isoc and interrupt descriptors from the module
- * parameters. We assume that the user knows what they are doing and
- * won't give parameters that their UDC doesn't support.
+ * Fill in the HS isoc descriptors from the module parameters.
+ * We assume that the user knows what they are doing and won't
+ * give parameters that their UDC doesn't support.
*/
hs_iso_source_desc.wMaxPacketSize = isoc_maxpacket;
hs_iso_source_desc.wMaxPacketSize |= isoc_mult << 11;
hs_iso_sink_desc.bInterval = isoc_interval;
hs_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress;
- hs_int_source_desc.wMaxPacketSize = int_maxpacket;
- hs_int_source_desc.wMaxPacketSize |= int_mult << 11;
- hs_int_source_desc.bInterval = USB_MS_TO_HS_INTERVAL(int_interval);
- hs_int_source_desc.bEndpointAddress =
- fs_int_source_desc.bEndpointAddress;
-
- hs_int_sink_desc.wMaxPacketSize = int_maxpacket;
- hs_int_sink_desc.wMaxPacketSize |= int_mult << 11;
- hs_int_sink_desc.bInterval = USB_MS_TO_HS_INTERVAL(int_interval);
- hs_int_sink_desc.bEndpointAddress = fs_int_sink_desc.bEndpointAddress;
-
/* support super speed hardware */
ss_source_desc.bEndpointAddress =
fs_source_desc.bEndpointAddress;
fs_sink_desc.bEndpointAddress;
/*
- * Fill in the SS isoc and interrupt descriptors from the module
- * parameters. We assume that the user knows what they are doing and
- * won't give parameters that their UDC doesn't support.
+ * Fill in the SS isoc descriptors from the module parameters.
+ * We assume that the user knows what they are doing and won't
+ * give parameters that their UDC doesn't support.
*/
ss_iso_source_desc.wMaxPacketSize = isoc_maxpacket;
ss_iso_source_desc.bInterval = isoc_interval;
isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1);
ss_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress;
- ss_int_source_desc.wMaxPacketSize = int_maxpacket;
- ss_int_source_desc.bInterval = USB_MS_TO_SS_INTERVAL(int_interval);
- ss_int_source_comp_desc.bmAttributes = int_mult;
- ss_int_source_comp_desc.bMaxBurst = int_maxburst;
- ss_int_source_comp_desc.wBytesPerInterval =
- int_maxpacket * (int_mult + 1) * (int_maxburst + 1);
- ss_int_source_desc.bEndpointAddress =
- fs_int_source_desc.bEndpointAddress;
-
- ss_int_sink_desc.wMaxPacketSize = int_maxpacket;
- ss_int_sink_desc.bInterval = USB_MS_TO_SS_INTERVAL(int_interval);
- ss_int_sink_comp_desc.bmAttributes = int_mult;
- ss_int_sink_comp_desc.bMaxBurst = int_maxburst;
- ss_int_sink_comp_desc.wBytesPerInterval =
- int_maxpacket * (int_mult + 1) * (int_maxburst + 1);
- ss_int_sink_desc.bEndpointAddress = fs_int_sink_desc.bEndpointAddress;
-
ret = usb_assign_descriptors(f, fs_source_sink_descs,
hs_source_sink_descs, ss_source_sink_descs);
if (ret)
return ret;
- DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s, "
- "INT-IN/%s, INT-OUT/%s\n",
+ DBG(cdev, "%s speed %s: IN/%s, OUT/%s, ISO-IN/%s, ISO-OUT/%s\n",
(gadget_is_superspeed(c->cdev->gadget) ? "super" :
(gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
f->name, ss->in_ep->name, ss->out_ep->name,
ss->iso_in_ep ? ss->iso_in_ep->name : "<none>",
- ss->iso_out_ep ? ss->iso_out_ep->name : "<none>",
- ss->int_in_ep ? ss->int_in_ep->name : "<none>",
- ss->int_out_ep ? ss->int_out_ep->name : "<none>");
+ ss->iso_out_ep ? ss->iso_out_ep->name : "<none>");
return 0;
}
}
static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
- enum eptype ep_type, int speed)
+ bool is_iso, int speed)
{
struct usb_ep *ep;
struct usb_request *req;
int i, size, status;
for (i = 0; i < 8; i++) {
- switch (ep_type) {
- case EP_ISOC:
+ if (is_iso) {
switch (speed) {
case USB_SPEED_SUPER:
size = isoc_maxpacket * (isoc_mult + 1) *
}
ep = is_in ? ss->iso_in_ep : ss->iso_out_ep;
req = ss_alloc_ep_req(ep, size);
- break;
- case EP_INTERRUPT:
- switch (speed) {
- case USB_SPEED_SUPER:
- size = int_maxpacket * (int_mult + 1) *
- (int_maxburst + 1);
- break;
- case USB_SPEED_HIGH:
- size = int_maxpacket * (int_mult + 1);
- break;
- default:
- size = int_maxpacket > 1023 ?
- 1023 : int_maxpacket;
- break;
- }
- ep = is_in ? ss->int_in_ep : ss->int_out_ep;
- req = ss_alloc_ep_req(ep, size);
- break;
- default:
+ } else {
ep = is_in ? ss->in_ep : ss->out_ep;
req = ss_alloc_ep_req(ep, 0);
- break;
}
if (!req)
cdev = ss->function.config->cdev;
ERROR(cdev, "start %s%s %s --> %d\n",
- get_ep_string(ep_type), is_in ? "IN" : "OUT",
- ep->name, status);
+ is_iso ? "ISO-" : "", is_in ? "IN" : "OUT",
+ ep->name, status);
free_ep_req(ep, req);
}
- if (!(ep_type == EP_ISOC))
+ if (!is_iso)
break;
}
cdev = ss->function.config->cdev;
disable_endpoints(cdev, ss->in_ep, ss->out_ep, ss->iso_in_ep,
- ss->iso_out_ep, ss->int_in_ep, ss->int_out_ep);
+ ss->iso_out_ep);
VDBG(cdev, "%s disabled\n", ss->function.name);
}
int speed = cdev->gadget->speed;
struct usb_ep *ep;
- if (alt == 2) {
- /* Configure for periodic interrupt endpoint */
- ep = ss->int_in_ep;
- if (ep) {
- result = config_ep_by_speed(cdev->gadget,
- &(ss->function), ep);
- if (result)
- return result;
-
- result = usb_ep_enable(ep);
- if (result < 0)
- return result;
-
- ep->driver_data = ss;
- result = source_sink_start_ep(ss, true, EP_INTERRUPT,
- speed);
- if (result < 0) {
-fail1:
- ep = ss->int_in_ep;
- if (ep) {
- usb_ep_disable(ep);
- ep->driver_data = NULL;
- }
- return result;
- }
- }
-
- /*
- * one interrupt endpoint reads (sinks) anything OUT (from the
- * host)
- */
- ep = ss->int_out_ep;
- if (ep) {
- result = config_ep_by_speed(cdev->gadget,
- &(ss->function), ep);
- if (result)
- goto fail1;
-
- result = usb_ep_enable(ep);
- if (result < 0)
- goto fail1;
-
- ep->driver_data = ss;
- result = source_sink_start_ep(ss, false, EP_INTERRUPT,
- speed);
- if (result < 0) {
- ep = ss->int_out_ep;
- usb_ep_disable(ep);
- ep->driver_data = NULL;
- goto fail1;
- }
- }
-
- goto out;
- }
-
/* one bulk endpoint writes (sources) zeroes IN (to the host) */
ep = ss->in_ep;
result = config_ep_by_speed(cdev->gadget, &(ss->function), ep);
return result;
ep->driver_data = ss;
- result = source_sink_start_ep(ss, true, EP_BULK, speed);
+ result = source_sink_start_ep(ss, true, false, speed);
if (result < 0) {
fail:
ep = ss->in_ep;
goto fail;
ep->driver_data = ss;
- result = source_sink_start_ep(ss, false, EP_BULK, speed);
+ result = source_sink_start_ep(ss, false, false, speed);
if (result < 0) {
fail2:
ep = ss->out_ep;
goto fail2;
ep->driver_data = ss;
- result = source_sink_start_ep(ss, true, EP_ISOC, speed);
+ result = source_sink_start_ep(ss, true, true, speed);
if (result < 0) {
fail3:
ep = ss->iso_in_ep;
goto fail3;
ep->driver_data = ss;
- result = source_sink_start_ep(ss, false, EP_ISOC, speed);
+ result = source_sink_start_ep(ss, false, true, speed);
if (result < 0) {
usb_ep_disable(ep);
ep->driver_data = NULL;
goto fail3;
}
}
-
out:
ss->cur_alt = alt;
if (ss->in_ep->driver_data)
disable_source_sink(ss);
- else if (alt == 2 && ss->int_in_ep->driver_data)
- disable_source_sink(ss);
return enable_source_sink(cdev, ss, alt);
}
isoc_maxpacket = ss_opts->isoc_maxpacket;
isoc_mult = ss_opts->isoc_mult;
isoc_maxburst = ss_opts->isoc_maxburst;
- int_interval = ss_opts->int_interval;
- int_maxpacket = ss_opts->int_maxpacket;
- int_mult = ss_opts->int_mult;
- int_maxburst = ss_opts->int_maxburst;
buflen = ss_opts->bulk_buflen;
ss->function.name = "source/sink";
f_ss_opts_bulk_buflen_show,
f_ss_opts_bulk_buflen_store);
-static ssize_t f_ss_opts_int_interval_show(struct f_ss_opts *opts, char *page)
-{
- int result;
-
- mutex_lock(&opts->lock);
- result = sprintf(page, "%u", opts->int_interval);
- mutex_unlock(&opts->lock);
-
- return result;
-}
-
-static ssize_t f_ss_opts_int_interval_store(struct f_ss_opts *opts,
- const char *page, size_t len)
-{
- int ret;
- u32 num;
-
- mutex_lock(&opts->lock);
- if (opts->refcnt) {
- ret = -EBUSY;
- goto end;
- }
-
- ret = kstrtou32(page, 0, &num);
- if (ret)
- goto end;
-
- if (num > 4096) {
- ret = -EINVAL;
- goto end;
- }
-
- opts->int_interval = num;
- ret = len;
-end:
- mutex_unlock(&opts->lock);
- return ret;
-}
-
-static struct f_ss_opts_attribute f_ss_opts_int_interval =
- __CONFIGFS_ATTR(int_interval, S_IRUGO | S_IWUSR,
- f_ss_opts_int_interval_show,
- f_ss_opts_int_interval_store);
-
-static ssize_t f_ss_opts_int_maxpacket_show(struct f_ss_opts *opts, char *page)
-{
- int result;
-
- mutex_lock(&opts->lock);
- result = sprintf(page, "%u", opts->int_maxpacket);
- mutex_unlock(&opts->lock);
-
- return result;
-}
-
-static ssize_t f_ss_opts_int_maxpacket_store(struct f_ss_opts *opts,
- const char *page, size_t len)
-{
- int ret;
- u16 num;
-
- mutex_lock(&opts->lock);
- if (opts->refcnt) {
- ret = -EBUSY;
- goto end;
- }
-
- ret = kstrtou16(page, 0, &num);
- if (ret)
- goto end;
-
- if (num > 1024) {
- ret = -EINVAL;
- goto end;
- }
-
- opts->int_maxpacket = num;
- ret = len;
-end:
- mutex_unlock(&opts->lock);
- return ret;
-}
-
-static struct f_ss_opts_attribute f_ss_opts_int_maxpacket =
- __CONFIGFS_ATTR(int_maxpacket, S_IRUGO | S_IWUSR,
- f_ss_opts_int_maxpacket_show,
- f_ss_opts_int_maxpacket_store);
-
-static ssize_t f_ss_opts_int_mult_show(struct f_ss_opts *opts, char *page)
-{
- int result;
-
- mutex_lock(&opts->lock);
- result = sprintf(page, "%u", opts->int_mult);
- mutex_unlock(&opts->lock);
-
- return result;
-}
-
-static ssize_t f_ss_opts_int_mult_store(struct f_ss_opts *opts,
- const char *page, size_t len)
-{
- int ret;
- u8 num;
-
- mutex_lock(&opts->lock);
- if (opts->refcnt) {
- ret = -EBUSY;
- goto end;
- }
-
- ret = kstrtou8(page, 0, &num);
- if (ret)
- goto end;
-
- if (num > 2) {
- ret = -EINVAL;
- goto end;
- }
-
- opts->int_mult = num;
- ret = len;
-end:
- mutex_unlock(&opts->lock);
- return ret;
-}
-
-static struct f_ss_opts_attribute f_ss_opts_int_mult =
- __CONFIGFS_ATTR(int_mult, S_IRUGO | S_IWUSR,
- f_ss_opts_int_mult_show,
- f_ss_opts_int_mult_store);
-
-static ssize_t f_ss_opts_int_maxburst_show(struct f_ss_opts *opts, char *page)
-{
- int result;
-
- mutex_lock(&opts->lock);
- result = sprintf(page, "%u", opts->int_maxburst);
- mutex_unlock(&opts->lock);
-
- return result;
-}
-
-static ssize_t f_ss_opts_int_maxburst_store(struct f_ss_opts *opts,
- const char *page, size_t len)
-{
- int ret;
- u8 num;
-
- mutex_lock(&opts->lock);
- if (opts->refcnt) {
- ret = -EBUSY;
- goto end;
- }
-
- ret = kstrtou8(page, 0, &num);
- if (ret)
- goto end;
-
- if (num > 15) {
- ret = -EINVAL;
- goto end;
- }
-
- opts->int_maxburst = num;
- ret = len;
-end:
- mutex_unlock(&opts->lock);
- return ret;
-}
-
-static struct f_ss_opts_attribute f_ss_opts_int_maxburst =
- __CONFIGFS_ATTR(int_maxburst, S_IRUGO | S_IWUSR,
- f_ss_opts_int_maxburst_show,
- f_ss_opts_int_maxburst_store);
-
static struct configfs_attribute *ss_attrs[] = {
&f_ss_opts_pattern.attr,
&f_ss_opts_isoc_interval.attr,
&f_ss_opts_isoc_mult.attr,
&f_ss_opts_isoc_maxburst.attr,
&f_ss_opts_bulk_buflen.attr,
- &f_ss_opts_int_interval.attr,
- &f_ss_opts_int_maxpacket.attr,
- &f_ss_opts_int_mult.attr,
- &f_ss_opts_int_maxburst.attr,
NULL,
};
ss_opts->isoc_interval = GZERO_ISOC_INTERVAL;
ss_opts->isoc_maxpacket = GZERO_ISOC_MAXPACKET;
ss_opts->bulk_buflen = GZERO_BULK_BUFLEN;
- ss_opts->int_interval = GZERO_INT_INTERVAL;
- ss_opts->int_maxpacket = GZERO_INT_MAXPACKET;
config_group_init_type_name(&ss_opts->func_inst.group, "",
&ss_func_type);
#define UNFLW_CTRL 8
#define OVFLW_CTRL 10
-const char *uac2_name = "snd_uac2";
+static const char *uac2_name = "snd_uac2";
struct uac2_req {
struct uac2_rtd_params *pp; /* parent param */
};
/* Clock source for IN traffic */
-struct uac_clock_source_descriptor in_clk_src_desc = {
+static struct uac_clock_source_descriptor in_clk_src_desc = {
.bLength = sizeof in_clk_src_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
};
/* Clock source for OUT traffic */
-struct uac_clock_source_descriptor out_clk_src_desc = {
+static struct uac_clock_source_descriptor out_clk_src_desc = {
.bLength = sizeof out_clk_src_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
};
/* Input Terminal for USB_OUT */
-struct uac2_input_terminal_descriptor usb_out_it_desc = {
+static struct uac2_input_terminal_descriptor usb_out_it_desc = {
.bLength = sizeof usb_out_it_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
};
/* Input Terminal for I/O-In */
-struct uac2_input_terminal_descriptor io_in_it_desc = {
+static struct uac2_input_terminal_descriptor io_in_it_desc = {
.bLength = sizeof io_in_it_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
};
/* Ouput Terminal for USB_IN */
-struct uac2_output_terminal_descriptor usb_in_ot_desc = {
+static struct uac2_output_terminal_descriptor usb_in_ot_desc = {
.bLength = sizeof usb_in_ot_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
};
/* Ouput Terminal for I/O-Out */
-struct uac2_output_terminal_descriptor io_out_ot_desc = {
+static struct uac2_output_terminal_descriptor io_out_ot_desc = {
.bLength = sizeof io_out_ot_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bmControls = (CONTROL_RDWR << COPY_CTRL),
};
-struct uac2_ac_header_descriptor ac_hdr_desc = {
+static struct uac2_ac_header_descriptor ac_hdr_desc = {
.bLength = sizeof ac_hdr_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
};
/* Audio Stream OUT Intface Desc */
-struct uac2_as_header_descriptor as_out_hdr_desc = {
+static struct uac2_as_header_descriptor as_out_hdr_desc = {
.bLength = sizeof as_out_hdr_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
};
/* Audio USB_OUT Format */
-struct uac2_format_type_i_descriptor as_out_fmt1_desc = {
+static struct uac2_format_type_i_descriptor as_out_fmt1_desc = {
.bLength = sizeof as_out_fmt1_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_FORMAT_TYPE,
};
/* STD AS ISO OUT Endpoint */
-struct usb_endpoint_descriptor fs_epout_desc = {
+static struct usb_endpoint_descriptor fs_epout_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bInterval = 1,
};
-struct usb_endpoint_descriptor hs_epout_desc = {
+static struct usb_endpoint_descriptor hs_epout_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
};
/* Audio Stream IN Intface Desc */
-struct uac2_as_header_descriptor as_in_hdr_desc = {
+static struct uac2_as_header_descriptor as_in_hdr_desc = {
.bLength = sizeof as_in_hdr_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
};
/* Audio USB_IN Format */
-struct uac2_format_type_i_descriptor as_in_fmt1_desc = {
+static struct uac2_format_type_i_descriptor as_in_fmt1_desc = {
.bLength = sizeof as_in_fmt1_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_FORMAT_TYPE,
};
/* STD AS ISO IN Endpoint */
-struct usb_endpoint_descriptor fs_epin_desc = {
+static struct usb_endpoint_descriptor fs_epin_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bInterval = 1,
};
-struct usb_endpoint_descriptor hs_epin_desc = {
+static struct usb_endpoint_descriptor hs_epin_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
agdev->out_ep->driver_data = NULL;
}
-struct usb_function *afunc_alloc(struct usb_function_instance *fi)
+static struct usb_function *afunc_alloc(struct usb_function_instance *fi)
{
struct audio_dev *agdev;
struct f_uac2_opts *opts;
#define GZERO_QLEN 32
#define GZERO_ISOC_INTERVAL 4
#define GZERO_ISOC_MAXPACKET 1024
-#define GZERO_INT_INTERVAL 1 /* Default interrupt interval = 1 ms */
-#define GZERO_INT_MAXPACKET 1024
struct usb_zero_options {
unsigned pattern;
unsigned isoc_maxpacket;
unsigned isoc_mult;
unsigned isoc_maxburst;
- unsigned int_interval; /* In ms */
- unsigned int_maxpacket;
- unsigned int_mult;
- unsigned int_maxburst;
unsigned bulk_buflen;
unsigned qlen;
};
unsigned isoc_maxpacket;
unsigned isoc_mult;
unsigned isoc_maxburst;
- unsigned int_interval; /* In ms */
- unsigned int_maxpacket;
- unsigned int_mult;
- unsigned int_maxburst;
unsigned bulk_buflen;
/*
void free_ep_req(struct usb_ep *ep, struct usb_request *req);
void disable_endpoints(struct usb_composite_dev *cdev,
struct usb_ep *in, struct usb_ep *out,
- struct usb_ep *iso_in, struct usb_ep *iso_out,
- struct usb_ep *int_in, struct usb_ep *int_out);
+ struct usb_ep *iso_in, struct usb_ep *iso_out);
#endif /* __G_ZERO_H */
#include "uvc.h"
#include "uvc_queue.h"
#include "uvc_video.h"
+#include "uvc_v4l2.h"
/* --------------------------------------------------------------------------
* Requests handling
#include "uvc.h"
#include "uvc_queue.h"
+#include "uvc_video.h"
/* --------------------------------------------------------------------------
* Video codecs
struct usb_configuration c;
int (*eth)(struct usb_configuration *c);
int num;
-} gfs_configurations[] = {
+};
+
+static struct gfs_configuration gfs_configurations[] = {
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
{
.eth = bind_rndis_config,
if (!try_module_get(THIS_MODULE))
return ERR_PTR(-ENOENT);
- return 0;
+ return NULL;
}
static void functionfs_release_dev(struct ffs_dev *dev)
MODULE_AUTHOR ("David Brownell");
MODULE_LICENSE ("GPL");
+static int ep_open(struct inode *, struct file *);
+
/*----------------------------------------------------------------------*/
* still need dev->lock to use epdata->ep.
*/
static int
-get_ready_ep (unsigned f_flags, struct ep_data *epdata)
+get_ready_ep (unsigned f_flags, struct ep_data *epdata, bool is_write)
{
int val;
if (f_flags & O_NONBLOCK) {
if (!mutex_trylock(&epdata->lock))
goto nonblock;
- if (epdata->state != STATE_EP_ENABLED) {
+ if (epdata->state != STATE_EP_ENABLED &&
+ (!is_write || epdata->state != STATE_EP_READY)) {
mutex_unlock(&epdata->lock);
nonblock:
val = -EAGAIN;
switch (epdata->state) {
case STATE_EP_ENABLED:
+ return 0;
+ case STATE_EP_READY: /* not configured yet */
+ if (is_write)
+ return 0;
+ // FALLTHRU
+ case STATE_EP_UNBOUND: /* clean disconnect */
break;
// case STATE_EP_DISABLED: /* "can't happen" */
- // case STATE_EP_READY: /* "can't happen" */
default: /* error! */
pr_debug ("%s: ep %p not available, state %d\n",
shortname, epdata, epdata->state);
- // FALLTHROUGH
- case STATE_EP_UNBOUND: /* clean disconnect */
- val = -ENODEV;
- mutex_unlock(&epdata->lock);
}
- return val;
+ mutex_unlock(&epdata->lock);
+ return -ENODEV;
}
static ssize_t
return value;
}
-
-/* handle a synchronous OUT bulk/intr/iso transfer */
-static ssize_t
-ep_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
-{
- struct ep_data *data = fd->private_data;
- void *kbuf;
- ssize_t value;
-
- if ((value = get_ready_ep (fd->f_flags, data)) < 0)
- return value;
-
- /* halt any endpoint by doing a "wrong direction" i/o call */
- if (usb_endpoint_dir_in(&data->desc)) {
- if (usb_endpoint_xfer_isoc(&data->desc)) {
- mutex_unlock(&data->lock);
- return -EINVAL;
- }
- DBG (data->dev, "%s halt\n", data->name);
- spin_lock_irq (&data->dev->lock);
- if (likely (data->ep != NULL))
- usb_ep_set_halt (data->ep);
- spin_unlock_irq (&data->dev->lock);
- mutex_unlock(&data->lock);
- return -EBADMSG;
- }
-
- /* FIXME readahead for O_NONBLOCK and poll(); careful with ZLPs */
-
- value = -ENOMEM;
- kbuf = kmalloc (len, GFP_KERNEL);
- if (unlikely (!kbuf))
- goto free1;
-
- value = ep_io (data, kbuf, len);
- VDEBUG (data->dev, "%s read %zu OUT, status %d\n",
- data->name, len, (int) value);
- if (value >= 0 && copy_to_user (buf, kbuf, value))
- value = -EFAULT;
-
-free1:
- mutex_unlock(&data->lock);
- kfree (kbuf);
- return value;
-}
-
-/* handle a synchronous IN bulk/intr/iso transfer */
-static ssize_t
-ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
-{
- struct ep_data *data = fd->private_data;
- void *kbuf;
- ssize_t value;
-
- if ((value = get_ready_ep (fd->f_flags, data)) < 0)
- return value;
-
- /* halt any endpoint by doing a "wrong direction" i/o call */
- if (!usb_endpoint_dir_in(&data->desc)) {
- if (usb_endpoint_xfer_isoc(&data->desc)) {
- mutex_unlock(&data->lock);
- return -EINVAL;
- }
- DBG (data->dev, "%s halt\n", data->name);
- spin_lock_irq (&data->dev->lock);
- if (likely (data->ep != NULL))
- usb_ep_set_halt (data->ep);
- spin_unlock_irq (&data->dev->lock);
- mutex_unlock(&data->lock);
- return -EBADMSG;
- }
-
- /* FIXME writebehind for O_NONBLOCK and poll(), qlen = 1 */
-
- value = -ENOMEM;
- kbuf = memdup_user(buf, len);
- if (IS_ERR(kbuf)) {
- value = PTR_ERR(kbuf);
- kbuf = NULL;
- goto free1;
- }
-
- value = ep_io (data, kbuf, len);
- VDEBUG (data->dev, "%s write %zu IN, status %d\n",
- data->name, len, (int) value);
-free1:
- mutex_unlock(&data->lock);
- kfree (kbuf);
- return value;
-}
-
static int
ep_release (struct inode *inode, struct file *fd)
{
struct ep_data *data = fd->private_data;
int status;
- if ((status = get_ready_ep (fd->f_flags, data)) < 0)
+ if ((status = get_ready_ep (fd->f_flags, data, false)) < 0)
return status;
spin_lock_irq (&data->dev->lock);
struct mm_struct *mm;
struct work_struct work;
void *buf;
- const struct iovec *iv;
- unsigned long nr_segs;
+ struct iov_iter to;
+ const void *to_free;
unsigned actual;
};
return value;
}
-static ssize_t ep_copy_to_user(struct kiocb_priv *priv)
-{
- ssize_t len, total;
- void *to_copy;
- int i;
-
- /* copy stuff into user buffers */
- total = priv->actual;
- len = 0;
- to_copy = priv->buf;
- for (i=0; i < priv->nr_segs; i++) {
- ssize_t this = min((ssize_t)(priv->iv[i].iov_len), total);
-
- if (copy_to_user(priv->iv[i].iov_base, to_copy, this)) {
- if (len == 0)
- len = -EFAULT;
- break;
- }
-
- total -= this;
- len += this;
- to_copy += this;
- if (total == 0)
- break;
- }
-
- return len;
-}
-
static void ep_user_copy_worker(struct work_struct *work)
{
struct kiocb_priv *priv = container_of(work, struct kiocb_priv, work);
size_t ret;
use_mm(mm);
- ret = ep_copy_to_user(priv);
+ ret = copy_to_iter(priv->buf, priv->actual, &priv->to);
unuse_mm(mm);
+ if (!ret)
+ ret = -EFAULT;
/* completing the iocb can drop the ctx and mm, don't touch mm after */
aio_complete(iocb, ret, ret);
kfree(priv->buf);
+ kfree(priv->to_free);
kfree(priv);
}
* don't need to copy anything to userspace, so we can
* complete the aio request immediately.
*/
- if (priv->iv == NULL || unlikely(req->actual == 0)) {
+ if (priv->to_free == NULL || unlikely(req->actual == 0)) {
kfree(req->buf);
+ kfree(priv->to_free);
kfree(priv);
iocb->private = NULL;
/* aio_complete() reports bytes-transferred _and_ faults */
priv->buf = req->buf;
priv->actual = req->actual;
+ INIT_WORK(&priv->work, ep_user_copy_worker);
schedule_work(&priv->work);
}
spin_unlock(&epdata->dev->lock);
put_ep(epdata);
}
-static ssize_t
-ep_aio_rwtail(
- struct kiocb *iocb,
- char *buf,
- size_t len,
- struct ep_data *epdata,
- const struct iovec *iv,
- unsigned long nr_segs
-)
+static ssize_t ep_aio(struct kiocb *iocb,
+ struct kiocb_priv *priv,
+ struct ep_data *epdata,
+ char *buf,
+ size_t len)
{
- struct kiocb_priv *priv;
- struct usb_request *req;
- ssize_t value;
+ struct usb_request *req;
+ ssize_t value;
- priv = kmalloc(sizeof *priv, GFP_KERNEL);
- if (!priv) {
- value = -ENOMEM;
-fail:
- kfree(buf);
- return value;
- }
iocb->private = priv;
priv->iocb = iocb;
- priv->iv = iv;
- priv->nr_segs = nr_segs;
- INIT_WORK(&priv->work, ep_user_copy_worker);
-
- value = get_ready_ep(iocb->ki_filp->f_flags, epdata);
- if (unlikely(value < 0)) {
- kfree(priv);
- goto fail;
- }
kiocb_set_cancel_fn(iocb, ep_aio_cancel);
get_ep(epdata);
* allocate or submit those if the host disconnected.
*/
spin_lock_irq(&epdata->dev->lock);
- if (likely(epdata->ep)) {
- req = usb_ep_alloc_request(epdata->ep, GFP_ATOMIC);
- if (likely(req)) {
- priv->req = req;
- req->buf = buf;
- req->length = len;
- req->complete = ep_aio_complete;
- req->context = iocb;
- value = usb_ep_queue(epdata->ep, req, GFP_ATOMIC);
- if (unlikely(0 != value))
- usb_ep_free_request(epdata->ep, req);
- } else
- value = -EAGAIN;
- } else
- value = -ENODEV;
- spin_unlock_irq(&epdata->dev->lock);
+ value = -ENODEV;
+ if (unlikely(epdata->ep))
+ goto fail;
- mutex_unlock(&epdata->lock);
+ req = usb_ep_alloc_request(epdata->ep, GFP_ATOMIC);
+ value = -ENOMEM;
+ if (unlikely(!req))
+ goto fail;
- if (unlikely(value)) {
- kfree(priv);
- put_ep(epdata);
- } else
- value = -EIOCBQUEUED;
+ priv->req = req;
+ req->buf = buf;
+ req->length = len;
+ req->complete = ep_aio_complete;
+ req->context = iocb;
+ value = usb_ep_queue(epdata->ep, req, GFP_ATOMIC);
+ if (unlikely(0 != value)) {
+ usb_ep_free_request(epdata->ep, req);
+ goto fail;
+ }
+ spin_unlock_irq(&epdata->dev->lock);
+ return -EIOCBQUEUED;
+
+fail:
+ spin_unlock_irq(&epdata->dev->lock);
+ kfree(priv->to_free);
+ kfree(priv);
+ put_ep(epdata);
return value;
}
static ssize_t
-ep_aio_read(struct kiocb *iocb, const struct iovec *iov,
- unsigned long nr_segs, loff_t o)
+ep_read_iter(struct kiocb *iocb, struct iov_iter *to)
{
- struct ep_data *epdata = iocb->ki_filp->private_data;
- char *buf;
+ struct file *file = iocb->ki_filp;
+ struct ep_data *epdata = file->private_data;
+ size_t len = iov_iter_count(to);
+ ssize_t value;
+ char *buf;
- if (unlikely(usb_endpoint_dir_in(&epdata->desc)))
- return -EINVAL;
+ if ((value = get_ready_ep(file->f_flags, epdata, false)) < 0)
+ return value;
- buf = kmalloc(iocb->ki_nbytes, GFP_KERNEL);
- if (unlikely(!buf))
- return -ENOMEM;
+ /* halt any endpoint by doing a "wrong direction" i/o call */
+ if (usb_endpoint_dir_in(&epdata->desc)) {
+ if (usb_endpoint_xfer_isoc(&epdata->desc) ||
+ !is_sync_kiocb(iocb)) {
+ mutex_unlock(&epdata->lock);
+ return -EINVAL;
+ }
+ DBG (epdata->dev, "%s halt\n", epdata->name);
+ spin_lock_irq(&epdata->dev->lock);
+ if (likely(epdata->ep != NULL))
+ usb_ep_set_halt(epdata->ep);
+ spin_unlock_irq(&epdata->dev->lock);
+ mutex_unlock(&epdata->lock);
+ return -EBADMSG;
+ }
- return ep_aio_rwtail(iocb, buf, iocb->ki_nbytes, epdata, iov, nr_segs);
+ buf = kmalloc(len, GFP_KERNEL);
+ if (unlikely(!buf)) {
+ mutex_unlock(&epdata->lock);
+ return -ENOMEM;
+ }
+ if (is_sync_kiocb(iocb)) {
+ value = ep_io(epdata, buf, len);
+ if (value >= 0 && copy_to_iter(buf, value, to))
+ value = -EFAULT;
+ } else {
+ struct kiocb_priv *priv = kzalloc(sizeof *priv, GFP_KERNEL);
+ value = -ENOMEM;
+ if (!priv)
+ goto fail;
+ priv->to_free = dup_iter(&priv->to, to, GFP_KERNEL);
+ if (!priv->to_free) {
+ kfree(priv);
+ goto fail;
+ }
+ value = ep_aio(iocb, priv, epdata, buf, len);
+ if (value == -EIOCBQUEUED)
+ buf = NULL;
+ }
+fail:
+ kfree(buf);
+ mutex_unlock(&epdata->lock);
+ return value;
}
+static ssize_t ep_config(struct ep_data *, const char *, size_t);
+
static ssize_t
-ep_aio_write(struct kiocb *iocb, const struct iovec *iov,
- unsigned long nr_segs, loff_t o)
+ep_write_iter(struct kiocb *iocb, struct iov_iter *from)
{
- struct ep_data *epdata = iocb->ki_filp->private_data;
- char *buf;
- size_t len = 0;
- int i = 0;
+ struct file *file = iocb->ki_filp;
+ struct ep_data *epdata = file->private_data;
+ size_t len = iov_iter_count(from);
+ bool configured;
+ ssize_t value;
+ char *buf;
+
+ if ((value = get_ready_ep(file->f_flags, epdata, true)) < 0)
+ return value;
- if (unlikely(!usb_endpoint_dir_in(&epdata->desc)))
- return -EINVAL;
+ configured = epdata->state == STATE_EP_ENABLED;
- buf = kmalloc(iocb->ki_nbytes, GFP_KERNEL);
- if (unlikely(!buf))
+ /* halt any endpoint by doing a "wrong direction" i/o call */
+ if (configured && !usb_endpoint_dir_in(&epdata->desc)) {
+ if (usb_endpoint_xfer_isoc(&epdata->desc) ||
+ !is_sync_kiocb(iocb)) {
+ mutex_unlock(&epdata->lock);
+ return -EINVAL;
+ }
+ DBG (epdata->dev, "%s halt\n", epdata->name);
+ spin_lock_irq(&epdata->dev->lock);
+ if (likely(epdata->ep != NULL))
+ usb_ep_set_halt(epdata->ep);
+ spin_unlock_irq(&epdata->dev->lock);
+ mutex_unlock(&epdata->lock);
+ return -EBADMSG;
+ }
+
+ buf = kmalloc(len, GFP_KERNEL);
+ if (unlikely(!buf)) {
+ mutex_unlock(&epdata->lock);
return -ENOMEM;
+ }
- for (i=0; i < nr_segs; i++) {
- if (unlikely(copy_from_user(&buf[len], iov[i].iov_base,
- iov[i].iov_len) != 0)) {
- kfree(buf);
- return -EFAULT;
+ if (unlikely(copy_from_iter(buf, len, from) != len)) {
+ value = -EFAULT;
+ goto out;
+ }
+
+ if (unlikely(!configured)) {
+ value = ep_config(epdata, buf, len);
+ } else if (is_sync_kiocb(iocb)) {
+ value = ep_io(epdata, buf, len);
+ } else {
+ struct kiocb_priv *priv = kzalloc(sizeof *priv, GFP_KERNEL);
+ value = -ENOMEM;
+ if (priv) {
+ value = ep_aio(iocb, priv, epdata, buf, len);
+ if (value == -EIOCBQUEUED)
+ buf = NULL;
}
- len += iov[i].iov_len;
}
- return ep_aio_rwtail(iocb, buf, len, epdata, NULL, 0);
+out:
+ kfree(buf);
+ mutex_unlock(&epdata->lock);
+ return value;
}
/*----------------------------------------------------------------------*/
/* used after endpoint configuration */
static const struct file_operations ep_io_operations = {
.owner = THIS_MODULE,
- .llseek = no_llseek,
- .read = ep_read,
- .write = ep_write,
- .unlocked_ioctl = ep_ioctl,
+ .open = ep_open,
.release = ep_release,
-
- .aio_read = ep_aio_read,
- .aio_write = ep_aio_write,
+ .llseek = no_llseek,
+ .read = new_sync_read,
+ .write = new_sync_write,
+ .unlocked_ioctl = ep_ioctl,
+ .read_iter = ep_read_iter,
+ .write_iter = ep_write_iter,
};
/* ENDPOINT INITIALIZATION
* speed descriptor, then optional high speed descriptor.
*/
static ssize_t
-ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
+ep_config (struct ep_data *data, const char *buf, size_t len)
{
- struct ep_data *data = fd->private_data;
struct usb_ep *ep;
u32 tag;
int value, length = len;
- value = mutex_lock_interruptible(&data->lock);
- if (value < 0)
- return value;
-
if (data->state != STATE_EP_READY) {
value = -EL2HLT;
goto fail;
goto fail0;
/* we might need to change message format someday */
- if (copy_from_user (&tag, buf, 4)) {
- goto fail1;
- }
+ memcpy(&tag, buf, 4);
if (tag != 1) {
DBG(data->dev, "config %s, bad tag %d\n", data->name, tag);
goto fail0;
*/
/* full/low speed descriptor, then high speed */
- if (copy_from_user (&data->desc, buf, USB_DT_ENDPOINT_SIZE)) {
- goto fail1;
- }
+ memcpy(&data->desc, buf, USB_DT_ENDPOINT_SIZE);
if (data->desc.bLength != USB_DT_ENDPOINT_SIZE
|| data->desc.bDescriptorType != USB_DT_ENDPOINT)
goto fail0;
if (len != USB_DT_ENDPOINT_SIZE) {
if (len != 2 * USB_DT_ENDPOINT_SIZE)
goto fail0;
- if (copy_from_user (&data->hs_desc, buf + USB_DT_ENDPOINT_SIZE,
- USB_DT_ENDPOINT_SIZE)) {
- goto fail1;
- }
+ memcpy(&data->hs_desc, buf + USB_DT_ENDPOINT_SIZE,
+ USB_DT_ENDPOINT_SIZE);
if (data->hs_desc.bLength != USB_DT_ENDPOINT_SIZE
|| data->hs_desc.bDescriptorType
!= USB_DT_ENDPOINT) {
case USB_SPEED_LOW:
case USB_SPEED_FULL:
ep->desc = &data->desc;
- value = usb_ep_enable(ep);
- if (value == 0)
- data->state = STATE_EP_ENABLED;
break;
case USB_SPEED_HIGH:
/* fails if caller didn't provide that descriptor... */
ep->desc = &data->hs_desc;
- value = usb_ep_enable(ep);
- if (value == 0)
- data->state = STATE_EP_ENABLED;
break;
default:
DBG(data->dev, "unconnected, %s init abandoned\n",
data->name);
value = -EINVAL;
+ goto gone;
}
+ value = usb_ep_enable(ep);
if (value == 0) {
- fd->f_op = &ep_io_operations;
+ data->state = STATE_EP_ENABLED;
value = length;
}
gone:
data->desc.bDescriptorType = 0;
data->hs_desc.bDescriptorType = 0;
}
- mutex_unlock(&data->lock);
return value;
fail0:
value = -EINVAL;
goto fail;
-fail1:
- value = -EFAULT;
- goto fail;
}
static int
return value;
}
-/* used before endpoint configuration */
-static const struct file_operations ep_config_operations = {
- .llseek = no_llseek,
-
- .open = ep_open,
- .write = ep_config,
- .release = ep_release,
-};
-
/*----------------------------------------------------------------------*/
/* EP0 IMPLEMENTATION can be partly in userspace.
enum ep0_state state;
spin_lock_irq (&dev->lock);
+ if (dev->state <= STATE_DEV_OPENED) {
+ retval = -EINVAL;
+ goto done;
+ }
/* report fd mode change before acting on it */
if (dev->setup_abort) {
struct dev_data *dev = fd->private_data;
ssize_t retval = -ESRCH;
- spin_lock_irq (&dev->lock);
-
/* report fd mode change before acting on it */
if (dev->setup_abort) {
dev->setup_abort = 0;
} else
DBG (dev, "fail %s, state %d\n", __func__, dev->state);
- spin_unlock_irq (&dev->lock);
return retval;
}
struct dev_data *dev = fd->private_data;
int mask = 0;
+ if (dev->state <= STATE_DEV_OPENED)
+ return DEFAULT_POLLMASK;
+
poll_wait(fd, &dev->wait, wait);
spin_lock_irq (&dev->lock);
return ret;
}
-/* used after device configuration */
-static const struct file_operations ep0_io_operations = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
-
- .read = ep0_read,
- .write = ep0_write,
- .fasync = ep0_fasync,
- .poll = ep0_poll,
- .unlocked_ioctl = dev_ioctl,
- .release = dev_release,
-};
-
/*----------------------------------------------------------------------*/
/* The in-kernel gadget driver handles most ep0 issues, in particular
goto enomem1;
data->dentry = gadgetfs_create_file (dev->sb, data->name,
- data, &ep_config_operations);
+ data, &ep_io_operations);
if (!data->dentry)
goto enomem2;
list_add_tail (&data->epfiles, &dev->epfiles);
u32 tag;
char *kbuf;
+ spin_lock_irq(&dev->lock);
+ if (dev->state > STATE_DEV_OPENED) {
+ value = ep0_write(fd, buf, len, ptr);
+ spin_unlock_irq(&dev->lock);
+ return value;
+ }
+ spin_unlock_irq(&dev->lock);
+
if (len < (USB_DT_CONFIG_SIZE + USB_DT_DEVICE_SIZE + 4))
return -EINVAL;
* on, they can work ... except in cleanup paths that
* kick in after the ep0 descriptor is closed.
*/
- fd->f_op = &ep0_io_operations;
value = len;
}
return value;
return value;
}
-static const struct file_operations dev_init_operations = {
+static const struct file_operations ep0_operations = {
.llseek = no_llseek,
.open = dev_open,
+ .read = ep0_read,
.write = dev_config,
.fasync = ep0_fasync,
+ .poll = ep0_poll,
.unlocked_ioctl = dev_ioctl,
.release = dev_release,
};
goto Enomem;
dev->sb = sb;
- dev->dentry = gadgetfs_create_file(sb, CHIP, dev, &dev_init_operations);
+ dev->dentry = gadgetfs_create_file(sb, CHIP, dev, &ep0_operations);
if (!dev->dentry) {
put_dev(dev);
goto Enomem;
goto err_session;
}
/*
- * Now register the TCM vHost virtual I_T Nexus as active with the
- * call to __transport_register_session()
+ * Now register the TCM vHost virtual I_T Nexus as active.
*/
- __transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl,
+ transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl,
tv_nexus->tvn_se_sess, tv_nexus);
tpg->tpg_nexus = tv_nexus;
mutex_unlock(&tpg->tpg_mutex);
.isoc_maxpacket = GZERO_ISOC_MAXPACKET,
.bulk_buflen = GZERO_BULK_BUFLEN,
.qlen = GZERO_QLEN,
- .int_interval = GZERO_INT_INTERVAL,
- .int_maxpacket = GZERO_INT_MAXPACKET,
};
/*-------------------------------------------------------------------------*/
S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)");
-module_param_named(int_interval, gzero_options.int_interval, uint,
- S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(int_interval, "1 - 16");
-
-module_param_named(int_maxpacket, gzero_options.int_maxpacket, uint,
- S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(int_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)");
-
-module_param_named(int_mult, gzero_options.int_mult, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(int_mult, "0 - 2 (hs/ss only)");
-
-module_param_named(int_maxburst, gzero_options.int_maxburst, uint,
- S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(int_maxburst, "0 - 15 (ss only)");
-
static struct usb_function *func_lb;
static struct usb_function_instance *func_inst_lb;
ss_opts->isoc_maxpacket = gzero_options.isoc_maxpacket;
ss_opts->isoc_mult = gzero_options.isoc_mult;
ss_opts->isoc_maxburst = gzero_options.isoc_maxburst;
- ss_opts->int_interval = gzero_options.int_interval;
- ss_opts->int_maxpacket = gzero_options.int_maxpacket;
- ss_opts->int_mult = gzero_options.int_mult;
- ss_opts->int_maxburst = gzero_options.int_maxburst;
ss_opts->bulk_buflen = gzero_options.bulk_buflen;
func_ss = usb_get_function(func_inst_ss);
struct atmel_ehci_priv {
struct clk *iclk;
- struct clk *fclk;
struct clk *uclk;
bool clocked;
};
{
if (atmel_ehci->clocked)
return;
- if (IS_ENABLED(CONFIG_COMMON_CLK)) {
- clk_set_rate(atmel_ehci->uclk, 48000000);
- clk_prepare_enable(atmel_ehci->uclk);
- }
+
+ clk_prepare_enable(atmel_ehci->uclk);
clk_prepare_enable(atmel_ehci->iclk);
- clk_prepare_enable(atmel_ehci->fclk);
atmel_ehci->clocked = true;
}
{
if (!atmel_ehci->clocked)
return;
- clk_disable_unprepare(atmel_ehci->fclk);
+
clk_disable_unprepare(atmel_ehci->iclk);
- if (IS_ENABLED(CONFIG_COMMON_CLK))
- clk_disable_unprepare(atmel_ehci->uclk);
+ clk_disable_unprepare(atmel_ehci->uclk);
atmel_ehci->clocked = false;
}
retval = -ENOENT;
goto fail_request_resource;
}
- atmel_ehci->fclk = devm_clk_get(&pdev->dev, "uhpck");
- if (IS_ERR(atmel_ehci->fclk)) {
- dev_err(&pdev->dev, "Error getting function clock\n");
- retval = -ENOENT;
+
+ atmel_ehci->uclk = devm_clk_get(&pdev->dev, "usb_clk");
+ if (IS_ERR(atmel_ehci->uclk)) {
+ dev_err(&pdev->dev, "failed to get uclk\n");
+ retval = PTR_ERR(atmel_ehci->uclk);
goto fail_request_resource;
}
- if (IS_ENABLED(CONFIG_COMMON_CLK)) {
- atmel_ehci->uclk = devm_clk_get(&pdev->dev, "usb_clk");
- if (IS_ERR(atmel_ehci->uclk)) {
- dev_err(&pdev->dev, "failed to get uclk\n");
- retval = PTR_ERR(atmel_ehci->uclk);
- goto fail_request_resource;
- }
- }
ehci = hcd_to_ehci(hcd);
/* registers start at offset 0x0 */
status = PORT_PLC;
port_change_bit = "link state";
break;
+ case USB_PORT_FEAT_C_PORT_CONFIG_ERROR:
+ status = PORT_CEC;
+ port_change_bit = "config error";
+ break;
default:
/* Should never happen */
return;
status |= USB_PORT_STAT_C_LINK_STATE << 16;
if ((raw_port_status & PORT_WRC))
status |= USB_PORT_STAT_C_BH_RESET << 16;
+ if ((raw_port_status & PORT_CEC))
+ status |= USB_PORT_STAT_C_CONFIG_ERROR << 16;
}
if (hcd->speed != HCD_USB3) {
case USB_PORT_FEAT_C_OVER_CURRENT:
case USB_PORT_FEAT_C_ENABLE:
case USB_PORT_FEAT_C_PORT_LINK_STATE:
+ case USB_PORT_FEAT_C_PORT_CONFIG_ERROR:
xhci_clear_port_change_bit(xhci, wValue, wIndex,
port_array[wIndex], temp);
break;
*/
status = bus_state->resuming_ports;
- mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC | PORT_WRC;
+ mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC | PORT_WRC | PORT_CEC;
spin_lock_irqsave(&xhci->lock, flags);
/* For each port, did anything change? If so, set that bit in buf. */
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
+#define PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI 0x22b5
+#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI 0xa12f
+#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI 0x9d2f
static const char hcd_name[] = "xhci_hcd";
if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
xhci->quirks |= XHCI_LPM_SUPPORT;
xhci->quirks |= XHCI_INTEL_HOST;
+ xhci->quirks |= XHCI_AVOID_BEI;
}
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
pdev->device == PCI_DEVICE_ID_INTEL_PANTHERPOINT_XHCI) {
* PPT chipsets.
*/
xhci->quirks |= XHCI_SPURIOUS_REBOOT;
- xhci->quirks |= XHCI_AVOID_BEI;
}
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI) {
xhci->quirks |= XHCI_SPURIOUS_REBOOT;
}
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
+ (pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI)) {
+ xhci->quirks |= XHCI_PME_STUCK_QUIRK;
+ }
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
pdev->device == PCI_DEVICE_ID_EJ168) {
xhci->quirks |= XHCI_RESET_ON_RESUME;
"QUIRK: Resetting on resume");
}
+/*
+ * Make sure PME works on some Intel xHCI controllers by writing 1 to clear
+ * the Internal PME flag bit in vendor specific PMCTRL register at offset 0x80a4
+ */
+static void xhci_pme_quirk(struct xhci_hcd *xhci)
+{
+ u32 val;
+ void __iomem *reg;
+
+ reg = (void __iomem *) xhci->cap_regs + 0x80a4;
+ val = readl(reg);
+ writel(val | BIT(28), reg);
+ readl(reg);
+}
+
/* called during probe() after chip reset completes */
static int xhci_pci_setup(struct usb_hcd *hcd)
{
if (xhci->quirks & XHCI_COMP_MODE_QUIRK)
pdev->no_d3cold = true;
+ if (xhci->quirks & XHCI_PME_STUCK_QUIRK)
+ xhci_pme_quirk(xhci);
+
return xhci_suspend(xhci, do_wakeup);
}
if (pdev->vendor == PCI_VENDOR_ID_INTEL)
usb_enable_intel_xhci_ports(pdev);
+ if (xhci->quirks & XHCI_PME_STUCK_QUIRK)
+ xhci_pme_quirk(xhci);
+
retval = xhci_resume(xhci, hibernated);
return retval;
}
if (irq < 0)
return -ENODEV;
-
- if (of_device_is_compatible(pdev->dev.of_node,
- "marvell,armada-375-xhci") ||
- of_device_is_compatible(pdev->dev.of_node,
- "marvell,armada-380-xhci")) {
- ret = xhci_mvebu_mbus_init_quirk(pdev);
- if (ret)
- return ret;
- }
-
/* Initialize dma_mask and coherent_dma_mask to 32-bits */
ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
if (ret)
goto put_hcd;
}
+ if (of_device_is_compatible(pdev->dev.of_node,
+ "marvell,armada-375-xhci") ||
+ of_device_is_compatible(pdev->dev.of_node,
+ "marvell,armada-380-xhci")) {
+ ret = xhci_mvebu_mbus_init_quirk(pdev);
+ if (ret)
+ goto disable_clk;
+ }
+
ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (ret)
goto disable_clk;
if (event_trb != ep_ring->dequeue) {
/* The event was for the status stage */
if (event_trb == td->last_trb) {
- if (td->urb->actual_length != 0) {
+ if (td->urb_length_set) {
/* Don't overwrite a previously set error code
*/
if ((*status == -EINPROGRESS || *status == 0) &&
td->urb->transfer_buffer_length;
}
} else {
- /* Maybe the event was for the data stage? */
+ /*
+ * Maybe the event was for the data stage? If so, update
+ * already the actual_length of the URB and flag it as
+ * set, so that it is not overwritten in the event for
+ * the last TRB.
+ */
+ td->urb_length_set = true;
td->urb->actual_length =
td->urb->transfer_buffer_length -
EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
+
/*
* xHCI host controller driver
*
#define HCS_IST(p) (((p) >> 0) & 0xf)
/* bits 4:7, max number of Event Ring segments */
#define HCS_ERST_MAX(p) (((p) >> 4) & 0xf)
+/* bits 21:25 Hi 5 bits of Scratchpad buffers SW must allocate for the HW */
/* bit 26 Scratchpad restore - for save/restore HW state - not used yet */
-/* bits 27:31 number of Scratchpad buffers SW must allocate for the HW */
-#define HCS_MAX_SCRATCHPAD(p) (((p) >> 27) & 0x1f)
+/* bits 27:31 Lo 5 bits of Scratchpad buffers SW must allocate for the HW */
+#define HCS_MAX_SCRATCHPAD(p) ((((p) >> 16) & 0x3e0) | (((p) >> 27) & 0x1f))
/* HCSPARAMS3 - hcs_params3 - bitmasks */
/* bits 0:7, Max U1 to U0 latency for the roothub ports */
struct xhci_segment *start_seg;
union xhci_trb *first_trb;
union xhci_trb *last_trb;
+ /* actual_length of the URB has already been set */
+ bool urb_length_set;
};
/* xHCI command default timeout value */
#define XHCI_SPURIOUS_WAKEUP (1 << 18)
/* For controllers with a broken beyond repair streams implementation */
#define XHCI_BROKEN_STREAMS (1 << 19)
+#define XHCI_PME_STUCK_QUIRK (1 << 20)
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */
}
if (IS_ENABLED(CONFIG_USB_ISP1761_UDC) && !udc_disabled) {
- ret = isp1760_udc_register(isp, irq, irqflags | IRQF_SHARED |
- IRQF_DISABLED);
+ ret = isp1760_udc_register(isp, irq, irqflags);
if (ret < 0) {
isp1760_hcd_unregister(&isp->hcd);
return ret;
for (slot = 0; slot < 32; slot++)
if (priv->atl_slots[slot].qh && time_after(jiffies,
priv->atl_slots[slot].timestamp +
- SLOT_TIMEOUT * HZ / 1000)) {
+ msecs_to_jiffies(SLOT_TIMEOUT))) {
ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd);
if (!FROM_DW0_VALID(ptd.dw0) &&
!FROM_DW3_ACTIVE(ptd.dw3))
spin_unlock_irqrestore(&priv->lock, spinflags);
- errata2_timer.expires = jiffies + SLOT_CHECK_PERIOD * HZ / 1000;
+ errata2_timer.expires = jiffies + msecs_to_jiffies(SLOT_CHECK_PERIOD);
add_timer(&errata2_timer);
}
return retval;
setup_timer(&errata2_timer, errata2_function, (unsigned long)hcd);
- errata2_timer.expires = jiffies + SLOT_CHECK_PERIOD * HZ / 1000;
+ errata2_timer.expires = jiffies + msecs_to_jiffies(SLOT_CHECK_PERIOD);
add_timer(&errata2_timer);
chipid = reg_read32(hcd->regs, HC_CHIP_ID_REG);
struct usb_gadget_driver *driver)
{
struct isp1760_udc *udc = gadget_to_udc(gadget);
+ unsigned long flags;
/* The hardware doesn't support low speed. */
if (driver->max_speed < USB_SPEED_FULL) {
return -EINVAL;
}
- spin_lock(&udc->lock);
+ spin_lock_irqsave(&udc->lock, flags);
if (udc->driver) {
dev_err(udc->isp->dev, "UDC already has a gadget driver\n");
- spin_unlock(&udc->lock);
+ spin_unlock_irqrestore(&udc->lock, flags);
return -EBUSY;
}
udc->driver = driver;
- spin_unlock(&udc->lock);
+ spin_unlock_irqrestore(&udc->lock, flags);
dev_dbg(udc->isp->dev, "starting UDC with driver %s\n",
driver->function);
static int isp1760_udc_stop(struct usb_gadget *gadget)
{
struct isp1760_udc *udc = gadget_to_udc(gadget);
+ unsigned long flags;
dev_dbg(udc->isp->dev, "%s\n", __func__);
isp1760_udc_write(udc, DC_MODE, 0);
- spin_lock(&udc->lock);
+ spin_lock_irqsave(&udc->lock, flags);
udc->driver = NULL;
- spin_unlock(&udc->lock);
+ spin_unlock_irqrestore(&udc->lock, flags);
return 0;
}
return -ENODEV;
}
- if (chipid != 0x00011582) {
+ if (chipid != 0x00011582 && chipid != 0x00158210) {
dev_err(udc->isp->dev, "udc: invalid chip ID 0x%08x\n", chipid);
return -ENODEV;
}
sprintf(udc->irqname, "%s (udc)", devname);
- ret = request_irq(irq, isp1760_udc_irq, IRQF_SHARED | IRQF_DISABLED |
- irqflags, udc->irqname, udc);
+ ret = request_irq(irq, isp1760_udc_irq, IRQF_SHARED | irqflags,
+ udc->irqname, udc);
if (ret < 0)
goto error;
config USB_MUSB_OMAP2PLUS
tristate "OMAP2430 and onwards"
- depends on ARCH_OMAP2PLUS && USB && OMAP_CONTROL_PHY
+ depends on ARCH_OMAP2PLUS && USB
+ depends on OMAP_CONTROL_PHY || !OMAP_CONTROL_PHY
select GENERIC_PHY
config USB_MUSB_AM35X
goto fail0;
}
- pm_runtime_use_autosuspend(musb->controller);
- pm_runtime_set_autosuspend_delay(musb->controller, 200);
- pm_runtime_enable(musb->controller);
-
spin_lock_init(&musb->lock);
musb->board_set_power = plat->set_power;
musb->min_power = plat->min_power;
musb_readl = musb_default_readl;
musb_writel = musb_default_writel;
+ /* We need musb_read/write functions initialized for PM */
+ pm_runtime_use_autosuspend(musb->controller);
+ pm_runtime_set_autosuspend_delay(musb->controller, 200);
+ pm_runtime_irq_safe(musb->controller);
+ pm_runtime_enable(musb->controller);
+
/* The musb_platform_init() call:
* - adjusts musb->mregs
* - sets the musb->isr
if (IS_ERR(musb->xceiv))
return PTR_ERR(musb->xceiv);
+ musb->phy = devm_phy_get(dev->parent, "usb2-phy");
+
/* Returns zero if e.g. not clocked */
rev = dsps_readl(reg_base, wrp->revision);
if (!rev)
return -ENODEV;
usb_phy_init(musb->xceiv);
+ if (IS_ERR(musb->phy)) {
+ musb->phy = NULL;
+ } else {
+ ret = phy_init(musb->phy);
+ if (ret < 0)
+ return ret;
+ ret = phy_power_on(musb->phy);
+ if (ret) {
+ phy_exit(musb->phy);
+ return ret;
+ }
+ }
+
setup_timer(&glue->timer, otg_timer, (unsigned long) musb);
/* Reset the musb */
del_timer_sync(&glue->timer);
usb_phy_shutdown(musb->xceiv);
+ phy_power_off(musb->phy);
+ phy_exit(musb->phy);
debugfs_remove_recursive(glue->dbgfs_root);
return 0;
struct device *dev = musb->controller;
struct dsps_glue *glue = dev_get_drvdata(dev->parent);
const struct dsps_musb_wrapper *wrp = glue->wrp;
- int session_restart = 0;
+ int session_restart = 0, error;
if (glue->sw_babble_enabled)
session_restart = sw_babble_control(musb);
dsps_writel(musb->ctrl_base, wrp->control, (1 << wrp->reset));
usleep_range(100, 200);
usb_phy_shutdown(musb->xceiv);
+ error = phy_power_off(musb->phy);
+ if (error)
+ dev_err(dev, "phy shutdown failed: %i\n", error);
usleep_range(100, 200);
usb_phy_init(musb->xceiv);
+ error = phy_power_on(musb->phy);
+ if (error)
+ dev_err(dev, "phy powerup failed: %i\n", error);
session_restart = 1;
}
struct musb_hdrc_config *config;
struct platform_device *musb;
struct device_node *dn = parent->dev.of_node;
- int ret;
+ int ret, val;
memset(resources, 0, sizeof(resources));
res = platform_get_resource_byname(parent, IORESOURCE_MEM, "mc");
pdata.mode = get_musb_port_mode(dev);
/* DT keeps this entry in mA, musb expects it as per USB spec */
pdata.power = get_int_prop(dn, "mentor,power") / 2;
- config->multipoint = of_property_read_bool(dn, "mentor,multipoint");
+
+ ret = of_property_read_u32(dn, "mentor,multipoint", &val);
+ if (!ret && val)
+ config->multipoint = true;
ret = platform_device_add_data(musb, &pdata, sizeof(pdata));
if (ret) {
.description = "musb-hcd",
.product_desc = "MUSB HDRC host driver",
.hcd_priv_size = sizeof(struct musb *),
- .flags = HCD_USB2 | HCD_MEMORY,
+ .flags = HCD_USB2 | HCD_MEMORY | HCD_BH,
/* not using irq handler or reset hooks from usbcore, since
* those must be shared with peripheral code for OTG configs
struct omap2430_glue *glue;
struct device_node *np = pdev->dev.of_node;
struct musb_hdrc_config *config;
- int ret = -ENOMEM;
+ int ret = -ENOMEM, val;
glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
if (!glue)
of_property_read_u32(np, "num-eps", (u32 *)&config->num_eps);
of_property_read_u32(np, "ram-bits", (u32 *)&config->ram_bits);
of_property_read_u32(np, "power", (u32 *)&pdata->power);
- config->multipoint = of_property_read_bool(np, "multipoint");
+
+ ret = of_property_read_u32(np, "multipoint", &val);
+ if (!ret && val)
+ config->multipoint = true;
pdata->board_data = data;
pdata->config = config;
return NULL;
dev = bus_find_device(&platform_bus_type, NULL, node, match);
+ if (!dev)
+ return NULL;
+
ctrl_usb = dev_get_drvdata(dev);
if (!ctrl_usb)
return NULL;
tristate 'Renesas USBHS controller'
depends on USB_GADGET
depends on ARCH_SHMOBILE || SUPERH || COMPILE_TEST
+ depends on EXTCON || !EXTCON # if EXTCON=m, USBHS cannot be built-in
default n
help
Renesas USBHS is a discrete USB host and peripheral controller chip
return 0;
}
-static ssize_t port_number_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct usb_serial_port *port = to_usb_serial_port(dev);
-
- return sprintf(buf, "%d\n", port->port_number);
-}
-static DEVICE_ATTR_RO(port_number);
-
static int usb_serial_device_probe(struct device *dev)
{
struct usb_serial_driver *driver;
struct usb_serial_port *port;
+ struct device *tty_dev;
int retval = 0;
int minor;
port = to_usb_serial_port(dev);
- if (!port) {
- retval = -ENODEV;
- goto exit;
- }
+ if (!port)
+ return -ENODEV;
/* make sure suspend/resume doesn't race against port_probe */
retval = usb_autopm_get_interface(port->serial->interface);
if (retval)
- goto exit;
+ return retval;
driver = port->serial->type;
if (driver->port_probe) {
retval = driver->port_probe(port);
if (retval)
- goto exit_with_autopm;
+ goto err_autopm_put;
}
- retval = device_create_file(dev, &dev_attr_port_number);
- if (retval) {
- if (driver->port_remove)
- retval = driver->port_remove(port);
- goto exit_with_autopm;
+ minor = port->minor;
+ tty_dev = tty_register_device(usb_serial_tty_driver, minor, dev);
+ if (IS_ERR(tty_dev)) {
+ retval = PTR_ERR(tty_dev);
+ goto err_port_remove;
}
- minor = port->minor;
- tty_register_device(usb_serial_tty_driver, minor, dev);
+ usb_autopm_put_interface(port->serial->interface);
+
dev_info(&port->serial->dev->dev,
"%s converter now attached to ttyUSB%d\n",
driver->description, minor);
-exit_with_autopm:
+ return 0;
+
+err_port_remove:
+ if (driver->port_remove)
+ driver->port_remove(port);
+err_autopm_put:
usb_autopm_put_interface(port->serial->interface);
-exit:
+
return retval;
}
minor = port->minor;
tty_unregister_device(usb_serial_tty_driver, minor);
- device_remove_file(&port->dev, &dev_attr_port_number);
-
driver = port->serial->type;
if (driver->port_remove)
retval = driver->port_remove(port);
u8 line_status; /* active status of modem control inputs */
};
+static void ch341_set_termios(struct tty_struct *tty,
+ struct usb_serial_port *port,
+ struct ktermios *old_termios);
+
static int ch341_control_out(struct usb_device *dev, u8 request,
u16 value, u16 index)
{
struct ch341_private *priv = usb_get_serial_port_data(port);
int r;
- priv->baud_rate = DEFAULT_BAUD_RATE;
-
r = ch341_configure(serial->dev, priv);
if (r)
goto out;
- r = ch341_set_handshake(serial->dev, priv->line_control);
- if (r)
- goto out;
-
- r = ch341_set_baudrate(serial->dev, priv);
- if (r)
- goto out;
+ if (tty)
+ ch341_set_termios(tty, port, NULL);
dev_dbg(&port->dev, "%s - submitting interrupt urb\n", __func__);
r = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/console.h>
init_ldsem(&tty->ldisc_sem);
INIT_LIST_HEAD(&tty->tty_files);
kref_get(&tty->driver->kref);
+ __module_get(tty->driver->owner);
tty->ops = &usb_console_fake_tty_ops;
if (tty_init_termios(tty)) {
retval = -ENOMEM;
{ USB_DEVICE(0x166A, 0x0305) }, /* Clipsal C-5000CT2 C-Bus Spectrum Colour Touchscreen */
{ USB_DEVICE(0x166A, 0x0401) }, /* Clipsal L51xx C-Bus Architectural Dimmer */
{ USB_DEVICE(0x166A, 0x0101) }, /* Clipsal 5560884 C-Bus Multi-room Audio Matrix Switcher */
+ { USB_DEVICE(0x16C0, 0x09B0) }, /* Lunatico Seletek */
+ { USB_DEVICE(0x16C0, 0x09B1) }, /* Lunatico Seletek */
{ USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */
{ USB_DEVICE(0x16DC, 0x0010) }, /* W-IE-NE-R Plein & Baus GmbH PL512 Power Supply */
{ USB_DEVICE(0x16DC, 0x0011) }, /* W-IE-NE-R Plein & Baus GmbH RCM Remote Control for MARATON Power Supply */
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(FTDI_VID, FTDI_NT_ORIONLXM_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+ { USB_DEVICE(FTDI_VID, FTDI_SYNAPSE_SS200_PID) },
/*
* ELV devices:
*/
{ USB_DEVICE(FTDI_VID, FTDI_ELSTER_UNICOM_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_PROPOX_JTAGCABLEII_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_PROPOX_ISPCABLEIII_PID) },
+ { USB_DEVICE(FTDI_VID, CYBER_CORTEX_AV_PID),
+ .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_H_PID),
{ USB_DEVICE_INTERFACE_NUMBER(INFINEON_VID, INFINEON_TRIBOARD_PID, 1) },
/* GE Healthcare devices */
{ USB_DEVICE(GE_HEALTHCARE_VID, GE_HEALTHCARE_NEMO_TRACKER_PID) },
+ /* Active Research (Actisense) devices */
+ { USB_DEVICE(FTDI_VID, ACTISENSE_NDC_PID) },
+ { USB_DEVICE(FTDI_VID, ACTISENSE_USG_PID) },
+ { USB_DEVICE(FTDI_VID, ACTISENSE_NGT_PID) },
+ { USB_DEVICE(FTDI_VID, ACTISENSE_NGW_PID) },
+ { USB_DEVICE(FTDI_VID, ACTISENSE_D9AC_PID) },
+ { USB_DEVICE(FTDI_VID, ACTISENSE_D9AD_PID) },
+ { USB_DEVICE(FTDI_VID, ACTISENSE_D9AE_PID) },
+ { USB_DEVICE(FTDI_VID, ACTISENSE_D9AF_PID) },
+ { USB_DEVICE(FTDI_VID, CHETCO_SEAGAUGE_PID) },
+ { USB_DEVICE(FTDI_VID, CHETCO_SEASWITCH_PID) },
+ { USB_DEVICE(FTDI_VID, CHETCO_SEASMART_NMEA2000_PID) },
+ { USB_DEVICE(FTDI_VID, CHETCO_SEASMART_ETHERNET_PID) },
+ { USB_DEVICE(FTDI_VID, CHETCO_SEASMART_WIFI_PID) },
+ { USB_DEVICE(FTDI_VID, CHETCO_SEASMART_DISPLAY_PID) },
+ { USB_DEVICE(FTDI_VID, CHETCO_SEASMART_LITE_PID) },
+ { USB_DEVICE(FTDI_VID, CHETCO_SEASMART_ANALOG_PID) },
{ } /* Terminating entry */
};
{
struct usb_device *udev = serial->dev;
- if ((udev->manufacturer && !strcmp(udev->manufacturer, "CALAO Systems")) ||
- (udev->product && !strcmp(udev->product, "BeagleBone/XDS100V2")))
+ if (udev->manufacturer && !strcmp(udev->manufacturer, "CALAO Systems"))
+ return ftdi_jtag_probe(serial);
+
+ if (udev->product &&
+ (!strcmp(udev->product, "BeagleBone/XDS100V2") ||
+ !strcmp(udev->product, "SNAP Connect E10")))
return ftdi_jtag_probe(serial);
return 0;
#define FTDI_LUMEL_PD12_PID 0x6002
+/* Cyber Cortex AV by Fabulous Silicon (http://fabuloussilicon.com) */
+#define CYBER_CORTEX_AV_PID 0x8698
+
/*
* Marvell OpenRD Base, Client
* http://www.open-rd.org
*/
#define FTDI_NT_ORIONLXM_PID 0x7c90 /* OrionLXm Substation Automation Platform */
+/*
+ * Synapse Wireless product ids (FTDI_VID)
+ * http://www.synapse-wireless.com
+ */
+#define FTDI_SYNAPSE_SS200_PID 0x9090 /* SS200 - SNAP Stick 200 */
+
/********************************/
/** third-party VID/PID combos **/
*/
#define GE_HEALTHCARE_VID 0x1901
#define GE_HEALTHCARE_NEMO_TRACKER_PID 0x0015
+
+/*
+ * Active Research (Actisense) devices
+ */
+#define ACTISENSE_NDC_PID 0xD9A8 /* NDC USB Serial Adapter */
+#define ACTISENSE_USG_PID 0xD9A9 /* USG USB Serial Adapter */
+#define ACTISENSE_NGT_PID 0xD9AA /* NGT NMEA2000 Interface */
+#define ACTISENSE_NGW_PID 0xD9AB /* NGW NMEA2000 Gateway */
+#define ACTISENSE_D9AC_PID 0xD9AC /* Actisense Reserved */
+#define ACTISENSE_D9AD_PID 0xD9AD /* Actisense Reserved */
+#define ACTISENSE_D9AE_PID 0xD9AE /* Actisense Reserved */
+#define ACTISENSE_D9AF_PID 0xD9AF /* Actisense Reserved */
+#define CHETCO_SEAGAUGE_PID 0xA548 /* SeaGauge USB Adapter */
+#define CHETCO_SEASWITCH_PID 0xA549 /* SeaSwitch USB Adapter */
+#define CHETCO_SEASMART_NMEA2000_PID 0xA54A /* SeaSmart NMEA2000 Gateway */
+#define CHETCO_SEASMART_ETHERNET_PID 0xA54B /* SeaSmart Ethernet Gateway */
+#define CHETCO_SEASMART_WIFI_PID 0xA5AC /* SeaSmart Wifi Gateway */
+#define CHETCO_SEASMART_DISPLAY_PID 0xA5AD /* SeaSmart NMEA2000 Display */
+#define CHETCO_SEASMART_LITE_PID 0xA5AE /* SeaSmart Lite USB Adapter */
+#define CHETCO_SEASMART_ANALOG_PID 0xA5AF /* SeaSmart Analog Adapter */
* character or at least one jiffy.
*/
period = max_t(unsigned long, (10 * HZ / bps), 1);
- period = min_t(unsigned long, period, timeout);
+ if (timeout)
+ period = min_t(unsigned long, period, timeout);
dev_dbg(&port->dev, "%s - timeout = %u ms, period = %u ms\n",
__func__, jiffies_to_msecs(timeout),
schedule_timeout_interruptible(period);
if (signal_pending(current))
break;
- if (time_after(jiffies, expire))
+ if (timeout && time_after(jiffies, expire))
break;
}
}
/* For Xircom PGSDB9 and older Entrega version of the same device */
#define XIRCOM_VENDOR_ID 0x085a
#define XIRCOM_FAKE_ID 0x8027
+#define XIRCOM_FAKE_ID_2 0x8025 /* "PGMFHUB" serial */
#define ENTREGA_VENDOR_ID 0x1645
#define ENTREGA_FAKE_ID 0x8093
#endif
#ifdef XIRCOM
{ USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID) },
+ { USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID_2) },
{ USB_DEVICE(ENTREGA_VENDOR_ID, ENTREGA_FAKE_ID) },
#endif
{ USB_DEVICE(KEYSPAN_VENDOR_ID, KEYSPAN_PDA_ID) },
#ifdef XIRCOM
static const struct usb_device_id id_table_fake_xircom[] = {
{ USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID) },
+ { USB_DEVICE(XIRCOM_VENDOR_ID, XIRCOM_FAKE_ID_2) },
{ USB_DEVICE(ENTREGA_VENDOR_ID, ENTREGA_FAKE_ID) },
{ }
};
}
/* Initial port termios */
- mxuport_set_termios(tty, port, NULL);
+ if (tty)
+ mxuport_set_termios(tty, port, NULL);
/*
* TODO: use RQ_VENDOR_GET_MSR, once we know what it
#define UART_OVERRUN_ERROR 0x40
#define UART_CTS 0x80
+static void pl2303_set_break(struct usb_serial_port *port, bool enable);
enum pl2303_type {
TYPE_01, /* Type 0 and 1 (difference unknown) */
{
usb_serial_generic_close(port);
usb_kill_urb(port->interrupt_in_urb);
+ pl2303_set_break(port, false);
}
static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port)
return -ENOIOCTLCMD;
}
-static void pl2303_break_ctl(struct tty_struct *tty, int break_state)
+static void pl2303_set_break(struct usb_serial_port *port, bool enable)
{
- struct usb_serial_port *port = tty->driver_data;
struct usb_serial *serial = port->serial;
u16 state;
int result;
- if (break_state == 0)
- state = BREAK_OFF;
- else
+ if (enable)
state = BREAK_ON;
+ else
+ state = BREAK_OFF;
dev_dbg(&port->dev, "%s - turning break %s\n", __func__,
state == BREAK_OFF ? "off" : "on");
dev_err(&port->dev, "error sending break = %d\n", result);
}
+static void pl2303_break_ctl(struct tty_struct *tty, int state)
+{
+ struct usb_serial_port *port = tty->driver_data;
+
+ pl2303_set_break(port, state);
+}
+
static void pl2303_update_line_status(struct usb_serial_port *port,
unsigned char *data,
unsigned int actual_length)
drv->dtr_rts(p, on);
}
+static ssize_t port_number_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_serial_port *port = to_usb_serial_port(dev);
+
+ return sprintf(buf, "%u\n", port->port_number);
+}
+static DEVICE_ATTR_RO(port_number);
+
+static struct attribute *usb_serial_port_attrs[] = {
+ &dev_attr_port_number.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(usb_serial_port);
+
static const struct tty_port_operations serial_port_ops = {
.carrier_raised = serial_port_carrier_raised,
.dtr_rts = serial_port_dtr_rts,
port->dev.driver = NULL;
port->dev.bus = &usb_serial_bus_type;
port->dev.release = &usb_serial_port_release;
+ port->dev.groups = usb_serial_port_groups;
device_initialize(&port->dev);
}
port = serial->port[i];
if (kfifo_alloc(&port->write_fifo, PAGE_SIZE, GFP_KERNEL))
goto probe_error;
- buffer_size = max_t(int, serial->type->bulk_out_size,
- usb_endpoint_maxp(endpoint));
+ buffer_size = serial->type->bulk_out_size;
+ if (!buffer_size)
+ buffer_size = usb_endpoint_maxp(endpoint);
port->bulk_out_size = buffer_size;
port->bulk_out_endpointAddress = endpoint->bEndpointAddress;
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_NO_ATA_1X),
+/* Reported-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> */
+UNUSUAL_DEV(0x13fd, 0x3940, 0x0000, 0x9999,
+ "Initio Corporation",
+ "",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_NO_ATA_1X),
+
+/* Reported-by: Tom Arild Naess <tanaess@gmail.com> */
+UNUSUAL_DEV(0x152d, 0x0539, 0x0000, 0x9999,
+ "JMicron",
+ "JMS539",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_NO_REPORT_OPCODES),
+
/* Reported-by: Claudio Bizzarri <claudio.bizzarri@gmail.com> */
UNUSUAL_DEV(0x152d, 0x0567, 0x0000, 0x9999,
"JMicron",
!(us->fflags & US_FL_SCM_MULT_TARG)) {
mutex_lock(&us->dev_mutex);
us->max_lun = usb_stor_Bulk_max_lun(us);
+ /*
+ * Allow proper scanning of devices that present more than 8 LUNs
+ * While not affecting other devices that may need the previous behavior
+ */
+ if (us->max_lun >= 8)
+ us_to_host(us)->max_lun = us->max_lun+1;
mutex_unlock(&us->dev_mutex);
}
scsi_scan_host(us_to_host(us));
func = vfio_pci_set_err_trigger;
break;
}
+ break;
case VFIO_PCI_REQ_IRQ_INDEX:
switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
case VFIO_IRQ_SET_ACTION_TRIGGER:
func = vfio_pci_set_req_trigger;
break;
}
+ break;
}
if (!func)
* TODO: support TSO.
*/
iov_iter_advance(&msg.msg_iter, vhost_hlen);
- } else {
- /* It'll come from socket; we'll need to patch
- * ->num_buffers over if VIRTIO_NET_F_MRG_RXBUF
- */
- iov_iter_advance(&fixup, sizeof(hdr));
}
err = sock->ops->recvmsg(NULL, sock, &msg,
sock_len, MSG_DONTWAIT | MSG_TRUNC);
continue;
}
/* Supply virtio_net_hdr if VHOST_NET_F_VIRTIO_NET_HDR */
- if (unlikely(vhost_hlen) &&
- copy_to_iter(&hdr, sizeof(hdr), &fixup) != sizeof(hdr)) {
- vq_err(vq, "Unable to write vnet_hdr at addr %p\n",
- vq->iov->iov_base);
- break;
+ if (unlikely(vhost_hlen)) {
+ if (copy_to_iter(&hdr, sizeof(hdr),
+ &fixup) != sizeof(hdr)) {
+ vq_err(vq, "Unable to write vnet_hdr "
+ "at addr %p\n", vq->iov->iov_base);
+ break;
+ }
+ } else {
+ /* Header came from socket; we'll need to patch
+ * ->num_buffers over if VIRTIO_NET_F_MRG_RXBUF
+ */
+ iov_iter_advance(&fixup, sizeof(hdr));
}
/* TODO: Should check and handle checksum. */
num_buffers = cpu_to_vhost16(vq, headcount);
if (likely(mergeable) &&
- copy_to_iter(&num_buffers, 2, &fixup) != 2) {
+ copy_to_iter(&num_buffers, sizeof num_buffers,
+ &fixup) != sizeof num_buffers) {
vq_err(vq, "Failed num_buffers write");
vhost_discard_vq_desc(vq, headcount);
break;
goto out;
}
/*
- * Now register the TCM vhost virtual I_T Nexus as active with the
- * call to __transport_register_session()
+ * Now register the TCM vhost virtual I_T Nexus as active.
*/
- __transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl,
+ transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl,
tv_nexus->tvn_se_sess, tv_nexus);
tpg->tpg_nexus = tv_nexus;
len = clcdfb_snprintf_mode(NULL, 0, mode);
name = devm_kzalloc(dev, len + 1, GFP_KERNEL);
+ if (!name)
+ return -ENOMEM;
+
clcdfb_snprintf_mode(name, len + 1, mode);
mode->name = name;
int num = 0, i, first = 1;
int ver, rev;
- ver = edid[EDID_STRUCT_VERSION];
- rev = edid[EDID_STRUCT_REVISION];
-
mode = kzalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL);
if (mode == NULL)
return NULL;
return NULL;
}
+ ver = edid[EDID_STRUCT_VERSION];
+ rev = edid[EDID_STRUCT_REVISION];
+
*dbsize = 0;
DPRINTK(" Detailed Timings\n");
#include <video/omapdss.h>
#include "dss.h"
-static struct omap_dss_device *to_dss_device_sysfs(struct device *dev)
+static ssize_t display_name_show(struct omap_dss_device *dssdev, char *buf)
{
- struct omap_dss_device *dssdev = NULL;
-
- for_each_dss_dev(dssdev) {
- if (dssdev->dev == dev) {
- omap_dss_put_device(dssdev);
- return dssdev;
- }
- }
-
- return NULL;
-}
-
-static ssize_t display_name_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
-
return snprintf(buf, PAGE_SIZE, "%s\n",
dssdev->name ?
dssdev->name : "");
}
-static ssize_t display_enabled_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t display_enabled_show(struct omap_dss_device *dssdev, char *buf)
{
- struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
-
return snprintf(buf, PAGE_SIZE, "%d\n",
omapdss_device_is_enabled(dssdev));
}
-static ssize_t display_enabled_store(struct device *dev,
- struct device_attribute *attr,
+static ssize_t display_enabled_store(struct omap_dss_device *dssdev,
const char *buf, size_t size)
{
- struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
int r;
bool enable;
return size;
}
-static ssize_t display_tear_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t display_tear_show(struct omap_dss_device *dssdev, char *buf)
{
- struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
return snprintf(buf, PAGE_SIZE, "%d\n",
dssdev->driver->get_te ?
dssdev->driver->get_te(dssdev) : 0);
}
-static ssize_t display_tear_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
+static ssize_t display_tear_store(struct omap_dss_device *dssdev,
+ const char *buf, size_t size)
{
- struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
int r;
bool te;
return size;
}
-static ssize_t display_timings_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t display_timings_show(struct omap_dss_device *dssdev, char *buf)
{
- struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
struct omap_video_timings t;
if (!dssdev->driver->get_timings)
t.y_res, t.vfp, t.vbp, t.vsw);
}
-static ssize_t display_timings_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
+static ssize_t display_timings_store(struct omap_dss_device *dssdev,
+ const char *buf, size_t size)
{
- struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
struct omap_video_timings t = dssdev->panel.timings;
int r, found;
return size;
}
-static ssize_t display_rotate_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t display_rotate_show(struct omap_dss_device *dssdev, char *buf)
{
- struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
int rotate;
if (!dssdev->driver->get_rotate)
return -ENOENT;
return snprintf(buf, PAGE_SIZE, "%u\n", rotate);
}
-static ssize_t display_rotate_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
+static ssize_t display_rotate_store(struct omap_dss_device *dssdev,
+ const char *buf, size_t size)
{
- struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
int rot, r;
if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate)
return size;
}
-static ssize_t display_mirror_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t display_mirror_show(struct omap_dss_device *dssdev, char *buf)
{
- struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
int mirror;
if (!dssdev->driver->get_mirror)
return -ENOENT;
return snprintf(buf, PAGE_SIZE, "%u\n", mirror);
}
-static ssize_t display_mirror_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
+static ssize_t display_mirror_store(struct omap_dss_device *dssdev,
+ const char *buf, size_t size)
{
- struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
int r;
bool mirror;
return size;
}
-static ssize_t display_wss_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t display_wss_show(struct omap_dss_device *dssdev, char *buf)
{
- struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
unsigned int wss;
if (!dssdev->driver->get_wss)
return snprintf(buf, PAGE_SIZE, "0x%05x\n", wss);
}
-static ssize_t display_wss_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
+static ssize_t display_wss_store(struct omap_dss_device *dssdev,
+ const char *buf, size_t size)
{
- struct omap_dss_device *dssdev = to_dss_device_sysfs(dev);
u32 wss;
int r;
return size;
}
-static DEVICE_ATTR(display_name, S_IRUGO, display_name_show, NULL);
-static DEVICE_ATTR(enabled, S_IRUGO|S_IWUSR,
+struct display_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct omap_dss_device *, char *);
+ ssize_t (*store)(struct omap_dss_device *, const char *, size_t);
+};
+
+#define DISPLAY_ATTR(_name, _mode, _show, _store) \
+ struct display_attribute display_attr_##_name = \
+ __ATTR(_name, _mode, _show, _store)
+
+static DISPLAY_ATTR(name, S_IRUGO, display_name_show, NULL);
+static DISPLAY_ATTR(display_name, S_IRUGO, display_name_show, NULL);
+static DISPLAY_ATTR(enabled, S_IRUGO|S_IWUSR,
display_enabled_show, display_enabled_store);
-static DEVICE_ATTR(tear_elim, S_IRUGO|S_IWUSR,
+static DISPLAY_ATTR(tear_elim, S_IRUGO|S_IWUSR,
display_tear_show, display_tear_store);
-static DEVICE_ATTR(timings, S_IRUGO|S_IWUSR,
+static DISPLAY_ATTR(timings, S_IRUGO|S_IWUSR,
display_timings_show, display_timings_store);
-static DEVICE_ATTR(rotate, S_IRUGO|S_IWUSR,
+static DISPLAY_ATTR(rotate, S_IRUGO|S_IWUSR,
display_rotate_show, display_rotate_store);
-static DEVICE_ATTR(mirror, S_IRUGO|S_IWUSR,
+static DISPLAY_ATTR(mirror, S_IRUGO|S_IWUSR,
display_mirror_show, display_mirror_store);
-static DEVICE_ATTR(wss, S_IRUGO|S_IWUSR,
+static DISPLAY_ATTR(wss, S_IRUGO|S_IWUSR,
display_wss_show, display_wss_store);
-static const struct attribute *display_sysfs_attrs[] = {
- &dev_attr_display_name.attr,
- &dev_attr_enabled.attr,
- &dev_attr_tear_elim.attr,
- &dev_attr_timings.attr,
- &dev_attr_rotate.attr,
- &dev_attr_mirror.attr,
- &dev_attr_wss.attr,
+static struct attribute *display_sysfs_attrs[] = {
+ &display_attr_name.attr,
+ &display_attr_display_name.attr,
+ &display_attr_enabled.attr,
+ &display_attr_tear_elim.attr,
+ &display_attr_timings.attr,
+ &display_attr_rotate.attr,
+ &display_attr_mirror.attr,
+ &display_attr_wss.attr,
NULL
};
+static ssize_t display_attr_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct omap_dss_device *dssdev;
+ struct display_attribute *display_attr;
+
+ dssdev = container_of(kobj, struct omap_dss_device, kobj);
+ display_attr = container_of(attr, struct display_attribute, attr);
+
+ if (!display_attr->show)
+ return -ENOENT;
+
+ return display_attr->show(dssdev, buf);
+}
+
+static ssize_t display_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t size)
+{
+ struct omap_dss_device *dssdev;
+ struct display_attribute *display_attr;
+
+ dssdev = container_of(kobj, struct omap_dss_device, kobj);
+ display_attr = container_of(attr, struct display_attribute, attr);
+
+ if (!display_attr->store)
+ return -ENOENT;
+
+ return display_attr->store(dssdev, buf, size);
+}
+
+static const struct sysfs_ops display_sysfs_ops = {
+ .show = display_attr_show,
+ .store = display_attr_store,
+};
+
+static struct kobj_type display_ktype = {
+ .sysfs_ops = &display_sysfs_ops,
+ .default_attrs = display_sysfs_attrs,
+};
+
int display_init_sysfs(struct platform_device *pdev)
{
struct omap_dss_device *dssdev = NULL;
int r;
for_each_dss_dev(dssdev) {
- struct kobject *kobj = &dssdev->dev->kobj;
-
- r = sysfs_create_files(kobj, display_sysfs_attrs);
+ r = kobject_init_and_add(&dssdev->kobj, &display_ktype,
+ &pdev->dev.kobj, dssdev->alias);
if (r) {
DSSERR("failed to create sysfs files\n");
- goto err;
- }
-
- r = sysfs_create_link(&pdev->dev.kobj, kobj, dssdev->alias);
- if (r) {
- sysfs_remove_files(kobj, display_sysfs_attrs);
-
- DSSERR("failed to create sysfs display link\n");
+ omap_dss_put_device(dssdev);
goto err;
}
}
struct omap_dss_device *dssdev = NULL;
for_each_dss_dev(dssdev) {
- sysfs_remove_link(&pdev->dev.kobj, dssdev->alias);
- sysfs_remove_files(&dssdev->dev->kobj,
- display_sysfs_attrs);
+ if (kobject_name(&dssdev->kobj) == NULL)
+ continue;
+
+ kobject_del(&dssdev->kobj);
+ kobject_put(&dssdev->kobj);
+
+ memset(&dssdev->kobj, 0, sizeof(dssdev->kobj));
}
}
#include <linux/module.h>
#include <linux/balloon_compaction.h>
#include <linux/oom.h>
+#include <linux/wait.h>
/*
* Balloon device works in 4K page units. So each page is pointed to by
static int balloon(void *_vballoon)
{
struct virtio_balloon *vb = _vballoon;
+ DEFINE_WAIT_FUNC(wait, woken_wake_function);
set_freezable();
while (!kthread_should_stop()) {
s64 diff;
try_to_freeze();
- wait_event_interruptible(vb->config_change,
- (diff = towards_target(vb)) != 0
- || vb->need_stats_update
- || kthread_should_stop()
- || freezing(current));
+
+ add_wait_queue(&vb->config_change, &wait);
+ for (;;) {
+ if ((diff = towards_target(vb)) != 0 ||
+ vb->need_stats_update ||
+ kthread_should_stop() ||
+ freezing(current))
+ break;
+ wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
+ }
+ remove_wait_queue(&vb->config_change, &wait);
+
if (vb->need_stats_update)
stats_handle_request(vb);
if (diff > 0)
if (err < 0)
goto out_oom_notify;
+ virtio_device_ready(vdev);
+
vb->thread = kthread_run(balloon, vb, "vballoon");
if (IS_ERR(vb->thread)) {
err = PTR_ERR(vb->thread);
void *buf, unsigned len)
{
struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
- u8 *ptr = buf;
- int i;
+ void __iomem *base = vm_dev->base + VIRTIO_MMIO_CONFIG;
+ u8 b;
+ __le16 w;
+ __le32 l;
- for (i = 0; i < len; i++)
- ptr[i] = readb(vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i);
+ if (vm_dev->version == 1) {
+ u8 *ptr = buf;
+ int i;
+
+ for (i = 0; i < len; i++)
+ ptr[i] = readb(base + offset + i);
+ return;
+ }
+
+ switch (len) {
+ case 1:
+ b = readb(base + offset);
+ memcpy(buf, &b, sizeof b);
+ break;
+ case 2:
+ w = cpu_to_le16(readw(base + offset));
+ memcpy(buf, &w, sizeof w);
+ break;
+ case 4:
+ l = cpu_to_le32(readl(base + offset));
+ memcpy(buf, &l, sizeof l);
+ break;
+ case 8:
+ l = cpu_to_le32(readl(base + offset));
+ memcpy(buf, &l, sizeof l);
+ l = cpu_to_le32(ioread32(base + offset + sizeof l));
+ memcpy(buf + sizeof l, &l, sizeof l);
+ break;
+ default:
+ BUG();
+ }
}
static void vm_set(struct virtio_device *vdev, unsigned offset,
const void *buf, unsigned len)
{
struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
- const u8 *ptr = buf;
- int i;
+ void __iomem *base = vm_dev->base + VIRTIO_MMIO_CONFIG;
+ u8 b;
+ __le16 w;
+ __le32 l;
- for (i = 0; i < len; i++)
- writeb(ptr[i], vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i);
+ if (vm_dev->version == 1) {
+ const u8 *ptr = buf;
+ int i;
+
+ for (i = 0; i < len; i++)
+ writeb(ptr[i], base + offset + i);
+
+ return;
+ }
+
+ switch (len) {
+ case 1:
+ memcpy(&b, buf, sizeof b);
+ writeb(b, base + offset);
+ break;
+ case 2:
+ memcpy(&w, buf, sizeof w);
+ writew(le16_to_cpu(w), base + offset);
+ break;
+ case 4:
+ memcpy(&l, buf, sizeof l);
+ writel(le32_to_cpu(l), base + offset);
+ break;
+ case 8:
+ memcpy(&l, buf, sizeof l);
+ writel(le32_to_cpu(l), base + offset);
+ memcpy(&l, buf + sizeof l, sizeof l);
+ writel(le32_to_cpu(l), base + offset + sizeof l);
+ break;
+ default:
+ BUG();
+ }
+}
+
+static u32 vm_generation(struct virtio_device *vdev)
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+
+ if (vm_dev->version == 1)
+ return 0;
+ else
+ return readl(vm_dev->base + VIRTIO_MMIO_CONFIG_GENERATION);
}
static u8 vm_get_status(struct virtio_device *vdev)
static const struct virtio_config_ops virtio_mmio_config_ops = {
.get = vm_get,
.set = vm_set,
+ .generation = vm_generation,
.get_status = vm_get_status,
.set_status = vm_set_status,
.reset = vm_reset,
if ((tmp & AT91_WDT_WDFIEN) && wdt->irq) {
err = request_irq(wdt->irq, wdt_interrupt,
- IRQF_SHARED | IRQF_IRQPOLL,
+ IRQF_SHARED | IRQF_IRQPOLL |
+ IRQF_NO_SUSPEND,
pdev->name, wdt);
if (err)
return err;
#define PDC_WDT_MIN_TIMEOUT 1
#define PDC_WDT_DEF_TIMEOUT 64
-static int heartbeat;
+static int heartbeat = PDC_WDT_DEF_TIMEOUT;
module_param(heartbeat, int, 0);
-MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
- "(default = " __MODULE_STRING(PDC_WDT_DEF_TIMEOUT) ")");
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds "
+ "(default=" __MODULE_STRING(PDC_WDT_DEF_TIMEOUT) ")");
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0);
pdc_wdt->wdt_dev.ops = &pdc_wdt_ops;
pdc_wdt->wdt_dev.max_timeout = 1 << PDC_WDT_CONFIG_DELAY_MASK;
pdc_wdt->wdt_dev.parent = &pdev->dev;
+ watchdog_set_drvdata(&pdc_wdt->wdt_dev, pdc_wdt);
ret = watchdog_init_timeout(&pdc_wdt->wdt_dev, heartbeat, &pdev->dev);
if (ret < 0) {
watchdog_set_nowayout(&pdc_wdt->wdt_dev, nowayout);
platform_set_drvdata(pdev, pdc_wdt);
- watchdog_set_drvdata(&pdc_wdt->wdt_dev, pdc_wdt);
ret = watchdog_register_device(&pdc_wdt->wdt_dev);
if (ret)
u32 reg;
struct mtk_wdt_dev *mtk_wdt = watchdog_get_drvdata(wdt_dev);
void __iomem *wdt_base = mtk_wdt->wdt_base;
- u32 ret;
+ int ret;
ret = mtk_wdt_set_timeout(wdt_dev, wdt_dev->timeout);
if (ret < 0)
In that case step 3 should be omitted.
+config XEN_BALLOON_MEMORY_HOTPLUG_LIMIT
+ int "Hotplugged memory limit (in GiB) for a PV guest"
+ default 512 if X86_64
+ default 4 if X86_32
+ range 0 64 if X86_32
+ depends on XEN_HAVE_PVMMU
+ depends on XEN_BALLOON_MEMORY_HOTPLUG
+ help
+ Maxmium amount of memory (in GiB) that a PV guest can be
+ expanded to when using memory hotplug.
+
+ A PV guest can have more memory than this limit if is
+ started with a larger maximum.
+
+ This value is used to allocate enough space in internal
+ tables needed for physical memory administration.
+
config XEN_SCRUB_PAGES
bool "Scrub pages before returning them to system"
depends on XEN_BALLOON
obj-$(CONFIG_HOTPLUG_CPU) += cpu_hotplug.o
endif
obj-$(CONFIG_X86) += fallback.o
-obj-y += grant-table.o features.o balloon.o manage.o
+obj-y += grant-table.o features.o balloon.o manage.o preempt.o
obj-y += events/
obj-y += xenbus/
balloon_hotplug = round_up(balloon_hotplug, PAGES_PER_SECTION);
nid = memory_add_physaddr_to_nid(hotplug_start_paddr);
+#ifdef CONFIG_XEN_HAVE_PVMMU
+ /*
+ * add_memory() will build page tables for the new memory so
+ * the p2m must contain invalid entries so the correct
+ * non-present PTEs will be written.
+ *
+ * If a failure occurs, the original (identity) p2m entries
+ * are not restored since this region is now known not to
+ * conflict with any devices.
+ */
+ if (!xen_feature(XENFEAT_auto_translated_physmap)) {
+ unsigned long pfn, i;
+
+ pfn = PFN_DOWN(hotplug_start_paddr);
+ for (i = 0; i < balloon_hotplug; i++) {
+ if (!set_phys_to_machine(pfn + i, INVALID_P2M_ENTRY)) {
+ pr_warn("set_phys_to_machine() failed, no memory added\n");
+ return BP_ECANCELED;
+ }
+ }
+ }
+#endif
+
rc = add_memory(nid, hotplug_start_paddr, balloon_hotplug << PAGE_SHIFT);
if (rc) {
pirq_query_unmask(irq);
rc = set_evtchn_to_irq(evtchn, irq);
- if (rc != 0) {
- pr_err("irq%d: Failed to set port to irq mapping (%d)\n",
- irq, rc);
- xen_evtchn_close(evtchn);
- return 0;
- }
+ if (rc)
+ goto err;
+
bind_evtchn_to_cpu(evtchn, 0);
info->evtchn = evtchn;
+ rc = xen_evtchn_port_setup(info);
+ if (rc)
+ goto err;
+
out:
unmask_evtchn(evtchn);
eoi_pirq(irq_get_irq_data(irq));
return 0;
+
+err:
+ pr_err("irq%d: Failed to set port to irq mapping (%d)\n", irq, rc);
+ xen_evtchn_close(evtchn);
+ return 0;
}
static unsigned int startup_pirq(struct irq_data *data)
--- /dev/null
+/*
+ * Preemptible hypercalls
+ *
+ * Copyright (C) 2014 Citrix Systems R&D ltd.
+ *
+ * This source code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ */
+
+#include <linux/sched.h>
+#include <xen/xen-ops.h>
+
+#ifndef CONFIG_PREEMPT
+
+/*
+ * Some hypercalls issued by the toolstack can take many 10s of
+ * seconds. Allow tasks running hypercalls via the privcmd driver to
+ * be voluntarily preempted even if full kernel preemption is
+ * disabled.
+ *
+ * Such preemptible hypercalls are bracketed by
+ * xen_preemptible_hcall_begin() and xen_preemptible_hcall_end()
+ * calls.
+ */
+
+DEFINE_PER_CPU(bool, xen_in_preemptible_hcall);
+EXPORT_SYMBOL_GPL(xen_in_preemptible_hcall);
+
+asmlinkage __visible void xen_maybe_preempt_hcall(void)
+{
+ if (unlikely(__this_cpu_read(xen_in_preemptible_hcall)
+ && should_resched())) {
+ /*
+ * Clear flag as we may be rescheduled on a different
+ * cpu.
+ */
+ __this_cpu_write(xen_in_preemptible_hcall, false);
+ _cond_resched();
+ __this_cpu_write(xen_in_preemptible_hcall, true);
+ }
+}
+#endif /* CONFIG_PREEMPT */
if (copy_from_user(&hypercall, udata, sizeof(hypercall)))
return -EFAULT;
+ xen_preemptible_hcall_begin();
ret = privcmd_call(hypercall.op,
hypercall.arg[0], hypercall.arg[1],
hypercall.arg[2], hypercall.arg[3],
hypercall.arg[4]);
+ xen_preemptible_hcall_end();
return ret;
}
#include "conf_space.h"
#include "conf_space_quirks.h"
-static bool permissive;
+bool permissive;
module_param(permissive, bool, 0644);
/* This is where xen_pcibk_read_config_byte, xen_pcibk_read_config_word,
void *data;
};
+extern bool permissive;
+
#define OFFSET(cfg_entry) ((cfg_entry)->base_offset+(cfg_entry)->field->offset)
/* Add fields to a device - the add_fields macro expects to get a pointer to
#include "pciback.h"
#include "conf_space.h"
+struct pci_cmd_info {
+ u16 val;
+};
+
struct pci_bar_info {
u32 val;
u32 len_val;
#define is_enable_cmd(value) ((value)&(PCI_COMMAND_MEMORY|PCI_COMMAND_IO))
#define is_master_cmd(value) ((value)&PCI_COMMAND_MASTER)
-static int command_read(struct pci_dev *dev, int offset, u16 *value, void *data)
+/* Bits guests are allowed to control in permissive mode. */
+#define PCI_COMMAND_GUEST (PCI_COMMAND_MASTER|PCI_COMMAND_SPECIAL| \
+ PCI_COMMAND_INVALIDATE|PCI_COMMAND_VGA_PALETTE| \
+ PCI_COMMAND_WAIT|PCI_COMMAND_FAST_BACK)
+
+static void *command_init(struct pci_dev *dev, int offset)
{
- int i;
- int ret;
-
- ret = xen_pcibk_read_config_word(dev, offset, value, data);
- if (!pci_is_enabled(dev))
- return ret;
-
- for (i = 0; i < PCI_ROM_RESOURCE; i++) {
- if (dev->resource[i].flags & IORESOURCE_IO)
- *value |= PCI_COMMAND_IO;
- if (dev->resource[i].flags & IORESOURCE_MEM)
- *value |= PCI_COMMAND_MEMORY;
+ struct pci_cmd_info *cmd = kmalloc(sizeof(*cmd), GFP_KERNEL);
+ int err;
+
+ if (!cmd)
+ return ERR_PTR(-ENOMEM);
+
+ err = pci_read_config_word(dev, PCI_COMMAND, &cmd->val);
+ if (err) {
+ kfree(cmd);
+ return ERR_PTR(err);
}
+ return cmd;
+}
+
+static int command_read(struct pci_dev *dev, int offset, u16 *value, void *data)
+{
+ int ret = pci_read_config_word(dev, offset, value);
+ const struct pci_cmd_info *cmd = data;
+
+ *value &= PCI_COMMAND_GUEST;
+ *value |= cmd->val & ~PCI_COMMAND_GUEST;
+
return ret;
}
{
struct xen_pcibk_dev_data *dev_data;
int err;
+ u16 val;
+ struct pci_cmd_info *cmd = data;
dev_data = pci_get_drvdata(dev);
if (!pci_is_enabled(dev) && is_enable_cmd(value)) {
}
}
+ cmd->val = value;
+
+ if (!permissive && (!dev_data || !dev_data->permissive))
+ return 0;
+
+ /* Only allow the guest to control certain bits. */
+ err = pci_read_config_word(dev, offset, &val);
+ if (err || val == value)
+ return err;
+
+ value &= PCI_COMMAND_GUEST;
+ value |= val & ~PCI_COMMAND_GUEST;
+
return pci_write_config_word(dev, offset, value);
}
{
.offset = PCI_COMMAND,
.size = 2,
+ .init = command_init,
+ .release = bar_release,
.u.w.read = command_read,
.u.w.write = command_write,
},
static int scsiback_do_cmd_fn(struct vscsibk_info *info)
{
struct vscsiif_back_ring *ring = &info->ring;
- struct vscsiif_request *ring_req;
+ struct vscsiif_request ring_req;
struct vscsibk_pend *pending_req;
RING_IDX rc, rp;
int err, more_to_do;
uint32_t result;
- uint8_t act;
rc = ring->req_cons;
rp = ring->sring->req_prod;
if (!pending_req)
return 1;
- ring_req = RING_GET_REQUEST(ring, rc);
+ ring_req = *RING_GET_REQUEST(ring, rc);
ring->req_cons = ++rc;
- act = ring_req->act;
- err = prepare_pending_reqs(info, ring_req, pending_req);
+ err = prepare_pending_reqs(info, &ring_req, pending_req);
if (err) {
switch (err) {
case -ENODEV:
return 1;
}
- switch (act) {
+ switch (ring_req.act) {
case VSCSIIF_ACT_SCSI_CDB:
- if (scsiback_gnttab_data_map(ring_req, pending_req)) {
+ if (scsiback_gnttab_data_map(&ring_req, pending_req)) {
scsiback_fast_flush_area(pending_req);
scsiback_do_resp_with_sense(NULL,
DRIVER_ERROR << 24, 0, pending_req);
break;
case VSCSIIF_ACT_SCSI_ABORT:
scsiback_device_action(pending_req, TMR_ABORT_TASK,
- ring_req->ref_rqid);
+ ring_req.ref_rqid);
break;
case VSCSIIF_ACT_SCSI_RESET:
scsiback_device_action(pending_req, TMR_LUN_RESET, 0);
name);
goto out;
}
- /*
- * Now register the TCM pvscsi virtual I_T Nexus as active with the
- * call to __transport_register_session()
- */
- __transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl,
+ /* Now register the TCM pvscsi virtual I_T Nexus as active. */
+ transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl,
tv_nexus->tvn_se_sess, tv_nexus);
tpg->tpg_nexus = tv_nexus;
boff = tmp % bsize;
if (boff) {
bh = affs_bread_ino(inode, bidx, 0);
- if (IS_ERR(bh))
- return PTR_ERR(bh);
+ if (IS_ERR(bh)) {
+ written = PTR_ERR(bh);
+ goto err_first_bh;
+ }
tmp = min(bsize - boff, to - from);
BUG_ON(boff + tmp > bsize || tmp > bsize);
memcpy(AFFS_DATA(bh) + boff, data + from, tmp);
bidx++;
} else if (bidx) {
bh = affs_bread_ino(inode, bidx - 1, 0);
- if (IS_ERR(bh))
- return PTR_ERR(bh);
+ if (IS_ERR(bh)) {
+ written = PTR_ERR(bh);
+ goto err_first_bh;
+ }
}
while (from + bsize <= to) {
prev_bh = bh;
bh = affs_getemptyblk_ino(inode, bidx);
if (IS_ERR(bh))
- goto out;
+ goto err_bh;
memcpy(AFFS_DATA(bh), data + from, bsize);
if (buffer_new(bh)) {
AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA);
prev_bh = bh;
bh = affs_bread_ino(inode, bidx, 1);
if (IS_ERR(bh))
- goto out;
+ goto err_bh;
tmp = min(bsize, to - from);
BUG_ON(tmp > bsize);
memcpy(AFFS_DATA(bh), data + from, tmp);
if (tmp > inode->i_size)
inode->i_size = AFFS_I(inode)->mmu_private = tmp;
+err_first_bh:
unlock_page(page);
page_cache_release(page);
return written;
-out:
+err_bh:
bh = prev_bh;
if (!written)
written = PTR_ERR(bh);
parent_nritems = btrfs_header_nritems(parent);
blocksize = root->nodesize;
- end_slot = parent_nritems;
+ end_slot = parent_nritems - 1;
- if (parent_nritems == 1)
+ if (parent_nritems <= 1)
return 0;
btrfs_set_lock_blocking(parent);
- for (i = start_slot; i < end_slot; i++) {
+ for (i = start_slot; i <= end_slot; i++) {
int close = 1;
btrfs_node_key(parent, &disk_key, i);
other = btrfs_node_blockptr(parent, i - 1);
close = close_blocks(blocknr, other, blocksize);
}
- if (!close && i < end_slot - 2) {
+ if (!close && i < end_slot) {
other = btrfs_node_blockptr(parent, i + 1);
close = close_blocks(blocknr, other, blocksize);
}
int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans,
struct btrfs_root *root);
+int btrfs_setup_space_cache(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root);
int btrfs_extent_readonly(struct btrfs_root *root, u64 bytenr);
int btrfs_free_block_groups(struct btrfs_fs_info *info);
int btrfs_read_block_groups(struct btrfs_root *root);
loff_t actual_len, u64 *alloc_hint);
int btrfs_inode_check_errors(struct inode *inode);
extern const struct dentry_operations btrfs_dentry_operations;
+#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
+void btrfs_test_inode_set_ops(struct inode *inode);
+#endif
/* ioctl.c */
long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
}
if (btrfs_super_sys_array_size(sb) < sizeof(struct btrfs_disk_key)
+ sizeof(struct btrfs_chunk)) {
- printk(KERN_ERR "BTRFS: system chunk array too small %u < %lu\n",
+ printk(KERN_ERR "BTRFS: system chunk array too small %u < %zu\n",
btrfs_super_sys_array_size(sb),
sizeof(struct btrfs_disk_key)
+ sizeof(struct btrfs_chunk));
return 0;
}
+ if (trans->aborted)
+ return 0;
again:
inode = lookup_free_space_inode(root, block_group, path);
if (IS_ERR(inode) && PTR_ERR(inode) != -ENOENT) {
*/
BTRFS_I(inode)->generation = 0;
ret = btrfs_update_inode(trans, root, inode);
+ if (ret) {
+ /*
+ * So theoretically we could recover from this, simply set the
+ * super cache generation to 0 so we know to invalidate the
+ * cache, but then we'd have to keep track of the block groups
+ * that fail this way so we know we _have_ to reset this cache
+ * before the next commit or risk reading stale cache. So to
+ * limit our exposure to horrible edge cases lets just abort the
+ * transaction, this only happens in really bad situations
+ * anyway.
+ */
+ btrfs_abort_transaction(trans, root, ret);
+ goto out_put;
+ }
WARN_ON(ret);
if (i_size_read(inode) > 0) {
return ret;
}
+int btrfs_setup_space_cache(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root)
+{
+ struct btrfs_block_group_cache *cache, *tmp;
+ struct btrfs_transaction *cur_trans = trans->transaction;
+ struct btrfs_path *path;
+
+ if (list_empty(&cur_trans->dirty_bgs) ||
+ !btrfs_test_opt(root, SPACE_CACHE))
+ return 0;
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+
+ /* Could add new block groups, use _safe just in case */
+ list_for_each_entry_safe(cache, tmp, &cur_trans->dirty_bgs,
+ dirty_list) {
+ if (cache->disk_cache_state == BTRFS_DC_CLEAR)
+ cache_save_setup(cache, trans, path);
+ }
+
+ btrfs_free_path(path);
+ return 0;
+}
+
int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans,
struct btrfs_root *root)
{
num_bytes = ALIGN(num_bytes, root->sectorsize);
spin_lock(&BTRFS_I(inode)->lock);
- BTRFS_I(inode)->outstanding_extents++;
+ nr_extents = (unsigned)div64_u64(num_bytes +
+ BTRFS_MAX_EXTENT_SIZE - 1,
+ BTRFS_MAX_EXTENT_SIZE);
+ BTRFS_I(inode)->outstanding_extents += nr_extents;
+ nr_extents = 0;
if (BTRFS_I(inode)->outstanding_extents >
BTRFS_I(inode)->reserved_extents)
if (dropped > 0)
to_free += btrfs_calc_trans_metadata_size(root, dropped);
+ if (btrfs_test_is_dummy_root(root))
+ return;
+
trace_btrfs_space_reservation(root->fs_info, "delalloc",
btrfs_ino(inode), to_free, 0);
if (root->fs_info->quota_enabled) {
/* Should be safe to release our pages at this point */
btrfs_release_extent_buffer_page(eb);
+#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
+ if (unlikely(test_bit(EXTENT_BUFFER_DUMMY, &eb->bflags))) {
+ __free_extent_buffer(eb);
+ return 1;
+ }
+#endif
call_rcu(&eb->rcu_head, btrfs_release_extent_buffer_rcu);
return 1;
}
mutex_unlock(&inode->i_mutex);
/*
- * we want to make sure fsync finds this change
- * but we haven't joined a transaction running right now.
- *
- * Later on, someone is sure to update the inode and get the
- * real transid recorded.
- *
- * We set last_trans now to the fs_info generation + 1,
- * this will either be one more than the running transaction
- * or the generation used for the next transaction if there isn't
- * one running right now.
- *
* We also have to set last_sub_trans to the current log transid,
* otherwise subsequent syncs to a file that's been synced in this
* transaction will appear to have already occured.
*/
- BTRFS_I(inode)->last_trans = root->fs_info->generation + 1;
BTRFS_I(inode)->last_sub_trans = root->log_transid;
if (num_written > 0) {
err = generic_write_sync(file, pos, num_written);
atomic_inc(&root->log_batch);
/*
- * check the transaction that last modified this inode
- * and see if its already been committed
- */
- if (!BTRFS_I(inode)->last_trans) {
- mutex_unlock(&inode->i_mutex);
- goto out;
- }
-
- /*
- * if the last transaction that changed this file was before
- * the current transaction, we can bail out now without any
- * syncing
+ * If the last transaction that changed this file was before the current
+ * transaction and we have the full sync flag set in our inode, we can
+ * bail out now without any syncing.
+ *
+ * Note that we can't bail out if the full sync flag isn't set. This is
+ * because when the full sync flag is set we start all ordered extents
+ * and wait for them to fully complete - when they complete they update
+ * the inode's last_trans field through:
+ *
+ * btrfs_finish_ordered_io() ->
+ * btrfs_update_inode_fallback() ->
+ * btrfs_update_inode() ->
+ * btrfs_set_inode_last_trans()
+ *
+ * So we are sure that last_trans is up to date and can do this check to
+ * bail out safely. For the fast path, when the full sync flag is not
+ * set in our inode, we can not do it because we start only our ordered
+ * extents and don't wait for them to complete (that is when
+ * btrfs_finish_ordered_io runs), so here at this point their last_trans
+ * value might be less than or equals to fs_info->last_trans_committed,
+ * and setting a speculative last_trans for an inode when a buffered
+ * write is made (such as fs_info->generation + 1 for example) would not
+ * be reliable since after setting the value and before fsync is called
+ * any number of transactions can start and commit (transaction kthread
+ * commits the current transaction periodically), and a transaction
+ * commit does not start nor waits for ordered extents to complete.
*/
smp_mb();
if (btrfs_inode_in_log(inode, root->fs_info->generation) ||
- BTRFS_I(inode)->last_trans <=
- root->fs_info->last_trans_committed) {
- BTRFS_I(inode)->last_trans = 0;
-
+ (full_sync && BTRFS_I(inode)->last_trans <=
+ root->fs_info->last_trans_committed)) {
/*
* We'v had everything committed since the last time we were
* modified so clear this flag in case it was set for whatever
bool same_page;
bool no_holes = btrfs_fs_incompat(root->fs_info, NO_HOLES);
u64 ino_size;
+ bool truncated_page = false;
+ bool updated_inode = false;
ret = btrfs_wait_ordered_range(inode, offset, len);
if (ret)
* entire page.
*/
if (same_page && len < PAGE_CACHE_SIZE) {
- if (offset < ino_size)
+ if (offset < ino_size) {
+ truncated_page = true;
ret = btrfs_truncate_page(inode, offset, len, 0);
+ } else {
+ ret = 0;
+ }
goto out_only_mutex;
}
/* zero back part of the first page */
if (offset < ino_size) {
+ truncated_page = true;
ret = btrfs_truncate_page(inode, offset, 0, 0);
if (ret) {
mutex_unlock(&inode->i_mutex);
if (!ret) {
/* zero the front end of the last page */
if (tail_start + tail_len < ino_size) {
+ truncated_page = true;
ret = btrfs_truncate_page(inode,
tail_start + tail_len, 0, 1);
if (ret)
}
if (lockend < lockstart) {
- mutex_unlock(&inode->i_mutex);
- return 0;
+ ret = 0;
+ goto out_only_mutex;
}
while (1) {
trans->block_rsv = &root->fs_info->trans_block_rsv;
ret = btrfs_update_inode(trans, root, inode);
+ updated_inode = true;
btrfs_end_transaction(trans, root);
btrfs_btree_balance_dirty(root);
out_free:
unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart, lockend,
&cached_state, GFP_NOFS);
out_only_mutex:
+ if (!updated_inode && truncated_page && !ret && !err) {
+ /*
+ * If we only end up zeroing part of a page, we still need to
+ * update the inode item, so that all the time fields are
+ * updated as well as the necessary btrfs inode in memory fields
+ * for detecting, at fsync time, if the inode isn't yet in the
+ * log tree or it's there but not up to date.
+ */
+ trans = btrfs_start_transaction(root, 1);
+ if (IS_ERR(trans)) {
+ err = PTR_ERR(trans);
+ } else {
+ err = btrfs_update_inode(trans, root, inode);
+ ret = btrfs_end_transaction(trans, root);
+ }
+ }
mutex_unlock(&inode->i_mutex);
if (ret && !err)
err = ret;
static int btrfs_dirty_inode(struct inode *inode);
+#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
+void btrfs_test_inode_set_ops(struct inode *inode)
+{
+ BTRFS_I(inode)->io_tree.ops = &btrfs_extent_io_ops;
+}
+#endif
+
static int btrfs_init_inode_security(struct btrfs_trans_handle *trans,
struct inode *inode, struct inode *dir,
const struct qstr *qstr)
u64 new_size;
/*
- * We need the largest size of the remaining extent to see if we
- * need to add a new outstanding extent. Think of the following
- * case
- *
- * [MEAX_EXTENT_SIZEx2 - 4k][4k]
- *
- * The new_size would just be 4k and we'd think we had enough
- * outstanding extents for this if we only took one side of the
- * split, same goes for the other direction. We need to see if
- * the larger size still is the same amount of extents as the
- * original size, because if it is we need to add a new
- * outstanding extent. But if we split up and the larger size
- * is less than the original then we are good to go since we've
- * already accounted for the extra extent in our original
- * accounting.
+ * See the explanation in btrfs_merge_extent_hook, the same
+ * applies here, just in reverse.
*/
new_size = orig->end - split + 1;
- if ((split - orig->start) > new_size)
- new_size = split - orig->start;
-
- num_extents = div64_u64(size + BTRFS_MAX_EXTENT_SIZE - 1,
+ num_extents = div64_u64(new_size + BTRFS_MAX_EXTENT_SIZE - 1,
BTRFS_MAX_EXTENT_SIZE);
- if (div64_u64(new_size + BTRFS_MAX_EXTENT_SIZE - 1,
- BTRFS_MAX_EXTENT_SIZE) < num_extents)
+ new_size = split - orig->start;
+ num_extents += div64_u64(new_size + BTRFS_MAX_EXTENT_SIZE - 1,
+ BTRFS_MAX_EXTENT_SIZE);
+ if (div64_u64(size + BTRFS_MAX_EXTENT_SIZE - 1,
+ BTRFS_MAX_EXTENT_SIZE) >= num_extents)
return;
}
if (!(other->state & EXTENT_DELALLOC))
return;
- old_size = other->end - other->start + 1;
- new_size = old_size + (new->end - new->start + 1);
+ if (new->start > other->start)
+ new_size = new->end - other->start + 1;
+ else
+ new_size = other->end - new->start + 1;
/* we're not bigger than the max, unreserve the space and go */
if (new_size <= BTRFS_MAX_EXTENT_SIZE) {
}
/*
- * If we grew by another max_extent, just return, we want to keep that
- * reserved amount.
+ * We have to add up either side to figure out how many extents were
+ * accounted for before we merged into one big extent. If the number of
+ * extents we accounted for is <= the amount we need for the new range
+ * then we can return, otherwise drop. Think of it like this
+ *
+ * [ 4k][MAX_SIZE]
+ *
+ * So we've grown the extent by a MAX_SIZE extent, this would mean we
+ * need 2 outstanding extents, on one side we have 1 and the other side
+ * we have 1 so they are == and we can return. But in this case
+ *
+ * [MAX_SIZE+4k][MAX_SIZE+4k]
+ *
+ * Each range on their own accounts for 2 extents, but merged together
+ * they are only 3 extents worth of accounting, so we need to drop in
+ * this case.
*/
+ old_size = other->end - other->start + 1;
num_extents = div64_u64(old_size + BTRFS_MAX_EXTENT_SIZE - 1,
BTRFS_MAX_EXTENT_SIZE);
+ old_size = new->end - new->start + 1;
+ num_extents += div64_u64(old_size + BTRFS_MAX_EXTENT_SIZE - 1,
+ BTRFS_MAX_EXTENT_SIZE);
+
if (div64_u64(new_size + BTRFS_MAX_EXTENT_SIZE - 1,
- BTRFS_MAX_EXTENT_SIZE) > num_extents)
+ BTRFS_MAX_EXTENT_SIZE) >= num_extents)
return;
spin_lock(&BTRFS_I(inode)->lock);
spin_unlock(&BTRFS_I(inode)->lock);
}
+ /* For sanity tests */
+ if (btrfs_test_is_dummy_root(root))
+ return;
+
__percpu_counter_add(&root->fs_info->delalloc_bytes, len,
root->fs_info->delalloc_batch);
spin_lock(&BTRFS_I(inode)->lock);
root != root->fs_info->tree_root)
btrfs_delalloc_release_metadata(inode, len);
+ /* For sanity tests. */
+ if (btrfs_test_is_dummy_root(root))
+ return;
+
if (root->root_key.objectid != BTRFS_DATA_RELOC_TREE_OBJECTID
&& do_list && !(state->state & EXTENT_NORESERVE))
btrfs_free_reserved_data_space(inode, len);
u64 start = iblock << inode->i_blkbits;
u64 lockstart, lockend;
u64 len = bh_result->b_size;
- u64 orig_len = len;
+ u64 *outstanding_extents = NULL;
int unlock_bits = EXTENT_LOCKED;
int ret = 0;
lockstart = start;
lockend = start + len - 1;
+ if (current->journal_info) {
+ /*
+ * Need to pull our outstanding extents and set journal_info to NULL so
+ * that anything that needs to check if there's a transction doesn't get
+ * confused.
+ */
+ outstanding_extents = current->journal_info;
+ current->journal_info = NULL;
+ }
+
/*
* If this errors out it's because we couldn't invalidate pagecache for
* this range and we need to fallback to buffered.
((BTRFS_I(inode)->flags & BTRFS_INODE_NODATACOW) &&
em->block_start != EXTENT_MAP_HOLE)) {
int type;
- int ret;
u64 block_start, orig_start, orig_block_len, ram_bytes;
if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags))
if (start + len > i_size_read(inode))
i_size_write(inode, start + len);
- if (len < orig_len) {
+ /*
+ * If we have an outstanding_extents count still set then we're
+ * within our reservation, otherwise we need to adjust our inode
+ * counter appropriately.
+ */
+ if (*outstanding_extents) {
+ (*outstanding_extents)--;
+ } else {
spin_lock(&BTRFS_I(inode)->lock);
BTRFS_I(inode)->outstanding_extents++;
spin_unlock(&BTRFS_I(inode)->lock);
}
+
+ current->journal_info = outstanding_extents;
btrfs_free_reserved_data_space(inode, len);
}
unlock_err:
clear_extent_bit(&BTRFS_I(inode)->io_tree, lockstart, lockend,
unlock_bits, 1, 0, &cached_state, GFP_NOFS);
+ if (outstanding_extents)
+ current->journal_info = outstanding_extents;
return ret;
}
{
struct file *file = iocb->ki_filp;
struct inode *inode = file->f_mapping->host;
+ u64 outstanding_extents = 0;
size_t count = 0;
int flags = 0;
bool wakeup = true;
ret = btrfs_delalloc_reserve_space(inode, count);
if (ret)
goto out;
+ outstanding_extents = div64_u64(count +
+ BTRFS_MAX_EXTENT_SIZE - 1,
+ BTRFS_MAX_EXTENT_SIZE);
+
+ /*
+ * We need to know how many extents we reserved so that we can
+ * do the accounting properly if we go over the number we
+ * originally calculated. Abuse current->journal_info for this.
+ */
+ current->journal_info = &outstanding_extents;
} else if (test_bit(BTRFS_INODE_READDIO_NEED_LOCK,
&BTRFS_I(inode)->runtime_flags)) {
inode_dio_done(inode);
iter, offset, btrfs_get_blocks_direct, NULL,
btrfs_submit_direct, flags);
if (rw & WRITE) {
+ current->journal_info = NULL;
if (ret < 0 && ret != -EIOCBQUEUED)
btrfs_delalloc_release_space(inode, count);
else if (ret >= 0 && (size_t)ret < count)
continue;
if (entry_end(ordered) <= start)
break;
- if (!list_empty(&ordered->log_list))
- continue;
- if (test_bit(BTRFS_ORDERED_LOGGED, &ordered->flags))
+ if (test_and_set_bit(BTRFS_ORDERED_LOGGED, &ordered->flags))
continue;
list_add(&ordered->log_list, logged_list);
atomic_inc(&ordered->refs);
wait_event(ordered->wait, test_bit(BTRFS_ORDERED_IO_DONE,
&ordered->flags));
- if (!test_and_set_bit(BTRFS_ORDERED_LOGGED, &ordered->flags))
- list_add_tail(&ordered->trans_list, &trans->ordered);
+ list_add_tail(&ordered->trans_list, &trans->ordered);
spin_lock_irq(&log->log_extents_lock[index]);
}
spin_unlock_irq(&log->log_extents_lock[index]);
if (oper1->seq < oper2->seq)
return -1;
if (oper1->seq > oper2->seq)
- return -1;
+ return 1;
if (oper1->ref_root < oper2->ref_root)
return -1;
if (oper1->ref_root > oper2->ref_root)
u64 parent_ino;
u64 ino;
u64 gen;
+ bool is_orphan;
struct list_head update_refs;
};
u64 ino_gen,
u64 parent_ino,
struct list_head *new_refs,
- struct list_head *deleted_refs)
+ struct list_head *deleted_refs,
+ const bool is_orphan)
{
struct rb_node **p = &sctx->pending_dir_moves.rb_node;
struct rb_node *parent = NULL;
pm->parent_ino = parent_ino;
pm->ino = ino;
pm->gen = ino_gen;
+ pm->is_orphan = is_orphan;
INIT_LIST_HEAD(&pm->list);
INIT_LIST_HEAD(&pm->update_refs);
RB_CLEAR_NODE(&pm->node);
rmdir_ino = dm->rmdir_ino;
free_waiting_dir_move(sctx, dm);
- ret = get_first_ref(sctx->parent_root, pm->ino,
- &parent_ino, &parent_gen, name);
- if (ret < 0)
- goto out;
-
- ret = get_cur_path(sctx, parent_ino, parent_gen,
- from_path);
- if (ret < 0)
- goto out;
- ret = fs_path_add_path(from_path, name);
+ if (pm->is_orphan) {
+ ret = gen_unique_name(sctx, pm->ino,
+ pm->gen, from_path);
+ } else {
+ ret = get_first_ref(sctx->parent_root, pm->ino,
+ &parent_ino, &parent_gen, name);
+ if (ret < 0)
+ goto out;
+ ret = get_cur_path(sctx, parent_ino, parent_gen,
+ from_path);
+ if (ret < 0)
+ goto out;
+ ret = fs_path_add_path(from_path, name);
+ }
if (ret < 0)
goto out;
LIST_HEAD(deleted_refs);
ASSERT(ancestor > BTRFS_FIRST_FREE_OBJECTID);
ret = add_pending_dir_move(sctx, pm->ino, pm->gen, ancestor,
- &pm->update_refs, &deleted_refs);
+ &pm->update_refs, &deleted_refs,
+ pm->is_orphan);
if (ret < 0)
goto out;
if (rmdir_ino) {
return ret;
}
+/*
+ * We might need to delay a directory rename even when no ancestor directory
+ * (in the send root) with a higher inode number than ours (sctx->cur_ino) was
+ * renamed. This happens when we rename a directory to the old name (the name
+ * in the parent root) of some other unrelated directory that got its rename
+ * delayed due to some ancestor with higher number that got renamed.
+ *
+ * Example:
+ *
+ * Parent snapshot:
+ * . (ino 256)
+ * |---- a/ (ino 257)
+ * | |---- file (ino 260)
+ * |
+ * |---- b/ (ino 258)
+ * |---- c/ (ino 259)
+ *
+ * Send snapshot:
+ * . (ino 256)
+ * |---- a/ (ino 258)
+ * |---- x/ (ino 259)
+ * |---- y/ (ino 257)
+ * |----- file (ino 260)
+ *
+ * Here we can not rename 258 from 'b' to 'a' without the rename of inode 257
+ * from 'a' to 'x/y' happening first, which in turn depends on the rename of
+ * inode 259 from 'c' to 'x'. So the order of rename commands the send stream
+ * must issue is:
+ *
+ * 1 - rename 259 from 'c' to 'x'
+ * 2 - rename 257 from 'a' to 'x/y'
+ * 3 - rename 258 from 'b' to 'a'
+ *
+ * Returns 1 if the rename of sctx->cur_ino needs to be delayed, 0 if it can
+ * be done right away and < 0 on error.
+ */
+static int wait_for_dest_dir_move(struct send_ctx *sctx,
+ struct recorded_ref *parent_ref,
+ const bool is_orphan)
+{
+ struct btrfs_path *path;
+ struct btrfs_key key;
+ struct btrfs_key di_key;
+ struct btrfs_dir_item *di;
+ u64 left_gen;
+ u64 right_gen;
+ int ret = 0;
+
+ if (RB_EMPTY_ROOT(&sctx->waiting_dir_moves))
+ return 0;
+
+ path = alloc_path_for_send();
+ if (!path)
+ return -ENOMEM;
+
+ key.objectid = parent_ref->dir;
+ key.type = BTRFS_DIR_ITEM_KEY;
+ key.offset = btrfs_name_hash(parent_ref->name, parent_ref->name_len);
+
+ ret = btrfs_search_slot(NULL, sctx->parent_root, &key, path, 0, 0);
+ if (ret < 0) {
+ goto out;
+ } else if (ret > 0) {
+ ret = 0;
+ goto out;
+ }
+
+ di = btrfs_match_dir_item_name(sctx->parent_root, path,
+ parent_ref->name, parent_ref->name_len);
+ if (!di) {
+ ret = 0;
+ goto out;
+ }
+ /*
+ * di_key.objectid has the number of the inode that has a dentry in the
+ * parent directory with the same name that sctx->cur_ino is being
+ * renamed to. We need to check if that inode is in the send root as
+ * well and if it is currently marked as an inode with a pending rename,
+ * if it is, we need to delay the rename of sctx->cur_ino as well, so
+ * that it happens after that other inode is renamed.
+ */
+ btrfs_dir_item_key_to_cpu(path->nodes[0], di, &di_key);
+ if (di_key.type != BTRFS_INODE_ITEM_KEY) {
+ ret = 0;
+ goto out;
+ }
+
+ ret = get_inode_info(sctx->parent_root, di_key.objectid, NULL,
+ &left_gen, NULL, NULL, NULL, NULL);
+ if (ret < 0)
+ goto out;
+ ret = get_inode_info(sctx->send_root, di_key.objectid, NULL,
+ &right_gen, NULL, NULL, NULL, NULL);
+ if (ret < 0) {
+ if (ret == -ENOENT)
+ ret = 0;
+ goto out;
+ }
+
+ /* Different inode, no need to delay the rename of sctx->cur_ino */
+ if (right_gen != left_gen) {
+ ret = 0;
+ goto out;
+ }
+
+ if (is_waiting_for_move(sctx, di_key.objectid)) {
+ ret = add_pending_dir_move(sctx,
+ sctx->cur_ino,
+ sctx->cur_inode_gen,
+ di_key.objectid,
+ &sctx->new_refs,
+ &sctx->deleted_refs,
+ is_orphan);
+ if (!ret)
+ ret = 1;
+ }
+out:
+ btrfs_free_path(path);
+ return ret;
+}
+
static int wait_for_parent_move(struct send_ctx *sctx,
struct recorded_ref *parent_ref)
{
sctx->cur_inode_gen,
ino,
&sctx->new_refs,
- &sctx->deleted_refs);
+ &sctx->deleted_refs,
+ false);
if (!ret)
ret = 1;
}
int did_overwrite = 0;
int is_orphan = 0;
u64 last_dir_ino_rm = 0;
+ bool can_rename = true;
verbose_printk("btrfs: process_recorded_refs %llu\n", sctx->cur_ino);
}
}
+ if (S_ISDIR(sctx->cur_inode_mode) && sctx->parent_root) {
+ ret = wait_for_dest_dir_move(sctx, cur, is_orphan);
+ if (ret < 0)
+ goto out;
+ if (ret == 1) {
+ can_rename = false;
+ *pending_move = 1;
+ }
+ }
+
/*
* link/move the ref to the new place. If we have an orphan
* inode, move it and update valid_path. If not, link or move
* it depending on the inode mode.
*/
- if (is_orphan) {
+ if (is_orphan && can_rename) {
ret = send_rename(sctx, valid_path, cur->full_path);
if (ret < 0)
goto out;
ret = fs_path_copy(valid_path, cur->full_path);
if (ret < 0)
goto out;
- } else {
+ } else if (can_rename) {
if (S_ISDIR(sctx->cur_inode_mode)) {
/*
* Dirs can't be linked, so move it. For moved
return ret;
}
+static int test_extent_accounting(void)
+{
+ struct inode *inode = NULL;
+ struct btrfs_root *root = NULL;
+ int ret = -ENOMEM;
+
+ inode = btrfs_new_test_inode();
+ if (!inode) {
+ test_msg("Couldn't allocate inode\n");
+ return ret;
+ }
+
+ root = btrfs_alloc_dummy_root();
+ if (IS_ERR(root)) {
+ test_msg("Couldn't allocate root\n");
+ goto out;
+ }
+
+ root->fs_info = btrfs_alloc_dummy_fs_info();
+ if (!root->fs_info) {
+ test_msg("Couldn't allocate dummy fs info\n");
+ goto out;
+ }
+
+ BTRFS_I(inode)->root = root;
+ btrfs_test_inode_set_ops(inode);
+
+ /* [BTRFS_MAX_EXTENT_SIZE] */
+ BTRFS_I(inode)->outstanding_extents++;
+ ret = btrfs_set_extent_delalloc(inode, 0, BTRFS_MAX_EXTENT_SIZE - 1,
+ NULL);
+ if (ret) {
+ test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
+ goto out;
+ }
+ if (BTRFS_I(inode)->outstanding_extents != 1) {
+ ret = -EINVAL;
+ test_msg("Miscount, wanted 1, got %u\n",
+ BTRFS_I(inode)->outstanding_extents);
+ goto out;
+ }
+
+ /* [BTRFS_MAX_EXTENT_SIZE][4k] */
+ BTRFS_I(inode)->outstanding_extents++;
+ ret = btrfs_set_extent_delalloc(inode, BTRFS_MAX_EXTENT_SIZE,
+ BTRFS_MAX_EXTENT_SIZE + 4095, NULL);
+ if (ret) {
+ test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
+ goto out;
+ }
+ if (BTRFS_I(inode)->outstanding_extents != 2) {
+ ret = -EINVAL;
+ test_msg("Miscount, wanted 2, got %u\n",
+ BTRFS_I(inode)->outstanding_extents);
+ goto out;
+ }
+
+ /* [BTRFS_MAX_EXTENT_SIZE/2][4K HOLE][the rest] */
+ ret = clear_extent_bit(&BTRFS_I(inode)->io_tree,
+ BTRFS_MAX_EXTENT_SIZE >> 1,
+ (BTRFS_MAX_EXTENT_SIZE >> 1) + 4095,
+ EXTENT_DELALLOC | EXTENT_DIRTY |
+ EXTENT_UPTODATE | EXTENT_DO_ACCOUNTING, 0, 0,
+ NULL, GFP_NOFS);
+ if (ret) {
+ test_msg("clear_extent_bit returned %d\n", ret);
+ goto out;
+ }
+ if (BTRFS_I(inode)->outstanding_extents != 2) {
+ ret = -EINVAL;
+ test_msg("Miscount, wanted 2, got %u\n",
+ BTRFS_I(inode)->outstanding_extents);
+ goto out;
+ }
+
+ /* [BTRFS_MAX_EXTENT_SIZE][4K] */
+ BTRFS_I(inode)->outstanding_extents++;
+ ret = btrfs_set_extent_delalloc(inode, BTRFS_MAX_EXTENT_SIZE >> 1,
+ (BTRFS_MAX_EXTENT_SIZE >> 1) + 4095,
+ NULL);
+ if (ret) {
+ test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
+ goto out;
+ }
+ if (BTRFS_I(inode)->outstanding_extents != 2) {
+ ret = -EINVAL;
+ test_msg("Miscount, wanted 2, got %u\n",
+ BTRFS_I(inode)->outstanding_extents);
+ goto out;
+ }
+
+ /*
+ * [BTRFS_MAX_EXTENT_SIZE+4K][4K HOLE][BTRFS_MAX_EXTENT_SIZE+4K]
+ *
+ * I'm artificially adding 2 to outstanding_extents because in the
+ * buffered IO case we'd add things up as we go, but I don't feel like
+ * doing that here, this isn't the interesting case we want to test.
+ */
+ BTRFS_I(inode)->outstanding_extents += 2;
+ ret = btrfs_set_extent_delalloc(inode, BTRFS_MAX_EXTENT_SIZE + 8192,
+ (BTRFS_MAX_EXTENT_SIZE << 1) + 12287,
+ NULL);
+ if (ret) {
+ test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
+ goto out;
+ }
+ if (BTRFS_I(inode)->outstanding_extents != 4) {
+ ret = -EINVAL;
+ test_msg("Miscount, wanted 4, got %u\n",
+ BTRFS_I(inode)->outstanding_extents);
+ goto out;
+ }
+
+ /* [BTRFS_MAX_EXTENT_SIZE+4k][4k][BTRFS_MAX_EXTENT_SIZE+4k] */
+ BTRFS_I(inode)->outstanding_extents++;
+ ret = btrfs_set_extent_delalloc(inode, BTRFS_MAX_EXTENT_SIZE+4096,
+ BTRFS_MAX_EXTENT_SIZE+8191, NULL);
+ if (ret) {
+ test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
+ goto out;
+ }
+ if (BTRFS_I(inode)->outstanding_extents != 3) {
+ ret = -EINVAL;
+ test_msg("Miscount, wanted 3, got %u\n",
+ BTRFS_I(inode)->outstanding_extents);
+ goto out;
+ }
+
+ /* [BTRFS_MAX_EXTENT_SIZE+4k][4K HOLE][BTRFS_MAX_EXTENT_SIZE+4k] */
+ ret = clear_extent_bit(&BTRFS_I(inode)->io_tree,
+ BTRFS_MAX_EXTENT_SIZE+4096,
+ BTRFS_MAX_EXTENT_SIZE+8191,
+ EXTENT_DIRTY | EXTENT_DELALLOC |
+ EXTENT_DO_ACCOUNTING | EXTENT_UPTODATE, 0, 0,
+ NULL, GFP_NOFS);
+ if (ret) {
+ test_msg("clear_extent_bit returned %d\n", ret);
+ goto out;
+ }
+ if (BTRFS_I(inode)->outstanding_extents != 4) {
+ ret = -EINVAL;
+ test_msg("Miscount, wanted 4, got %u\n",
+ BTRFS_I(inode)->outstanding_extents);
+ goto out;
+ }
+
+ /*
+ * Refill the hole again just for good measure, because I thought it
+ * might fail and I'd rather satisfy my paranoia at this point.
+ */
+ BTRFS_I(inode)->outstanding_extents++;
+ ret = btrfs_set_extent_delalloc(inode, BTRFS_MAX_EXTENT_SIZE+4096,
+ BTRFS_MAX_EXTENT_SIZE+8191, NULL);
+ if (ret) {
+ test_msg("btrfs_set_extent_delalloc returned %d\n", ret);
+ goto out;
+ }
+ if (BTRFS_I(inode)->outstanding_extents != 3) {
+ ret = -EINVAL;
+ test_msg("Miscount, wanted 3, got %u\n",
+ BTRFS_I(inode)->outstanding_extents);
+ goto out;
+ }
+
+ /* Empty */
+ ret = clear_extent_bit(&BTRFS_I(inode)->io_tree, 0, (u64)-1,
+ EXTENT_DIRTY | EXTENT_DELALLOC |
+ EXTENT_DO_ACCOUNTING | EXTENT_UPTODATE, 0, 0,
+ NULL, GFP_NOFS);
+ if (ret) {
+ test_msg("clear_extent_bit returned %d\n", ret);
+ goto out;
+ }
+ if (BTRFS_I(inode)->outstanding_extents) {
+ ret = -EINVAL;
+ test_msg("Miscount, wanted 0, got %u\n",
+ BTRFS_I(inode)->outstanding_extents);
+ goto out;
+ }
+ ret = 0;
+out:
+ if (ret)
+ clear_extent_bit(&BTRFS_I(inode)->io_tree, 0, (u64)-1,
+ EXTENT_DIRTY | EXTENT_DELALLOC |
+ EXTENT_DO_ACCOUNTING | EXTENT_UPTODATE, 0, 0,
+ NULL, GFP_NOFS);
+ iput(inode);
+ btrfs_free_dummy_root(root);
+ return ret;
+}
+
int btrfs_test_inodes(void)
{
int ret;
if (ret)
return ret;
test_msg("Running hole first btrfs_get_extent test\n");
- return test_hole_first();
+ ret = test_hole_first();
+ if (ret)
+ return ret;
+ test_msg("Running outstanding_extents tests\n");
+ return test_extent_accounting();
}
u64 old_root_bytenr;
u64 old_root_used;
struct btrfs_root *tree_root = root->fs_info->tree_root;
- bool extent_root = (root->objectid == BTRFS_EXTENT_TREE_OBJECTID);
old_root_used = btrfs_root_used(&root->root_item);
- btrfs_write_dirty_block_groups(trans, root);
while (1) {
old_root_bytenr = btrfs_root_bytenr(&root->root_item);
if (old_root_bytenr == root->node->start &&
- old_root_used == btrfs_root_used(&root->root_item) &&
- (!extent_root ||
- list_empty(&trans->transaction->dirty_bgs)))
+ old_root_used == btrfs_root_used(&root->root_item))
break;
btrfs_set_root_node(&root->root_item, root->node);
return ret;
old_root_used = btrfs_root_used(&root->root_item);
- if (extent_root) {
- ret = btrfs_write_dirty_block_groups(trans, root);
- if (ret)
- return ret;
- }
- ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
- if (ret)
- return ret;
- ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
- if (ret)
- return ret;
}
return 0;
struct btrfs_root *root)
{
struct btrfs_fs_info *fs_info = root->fs_info;
+ struct list_head *dirty_bgs = &trans->transaction->dirty_bgs;
struct list_head *next;
struct extent_buffer *eb;
int ret;
if (ret)
return ret;
+ ret = btrfs_setup_space_cache(trans, root);
+ if (ret)
+ return ret;
+
/* run_qgroups might have added some more refs */
ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
if (ret)
return ret;
-
+again:
while (!list_empty(&fs_info->dirty_cowonly_roots)) {
next = fs_info->dirty_cowonly_roots.next;
list_del_init(next);
ret = update_cowonly_root(trans, root);
if (ret)
return ret;
+ ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
+ if (ret)
+ return ret;
}
+ while (!list_empty(dirty_bgs)) {
+ ret = btrfs_write_dirty_block_groups(trans, root);
+ if (ret)
+ return ret;
+ ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
+ if (ret)
+ return ret;
+ }
+
+ if (!list_empty(&fs_info->dirty_cowonly_roots))
+ goto again;
+
list_add_tail(&fs_info->extent_root->dirty_list,
&trans->transaction->switch_commits);
btrfs_after_dev_replace_commit(fs_info);
wait_for_commit(root, cur_trans);
+ if (unlikely(cur_trans->aborted))
+ ret = cur_trans->aborted;
+
btrfs_put_transaction(cur_trans);
return ret;
base = btrfs_item_ptr_offset(leaf, path->slots[0]);
while (cur_offset < item_size) {
- extref = (struct btrfs_inode_extref *)base + cur_offset;
+ extref = (struct btrfs_inode_extref *)(base + cur_offset);
victim_name_len = btrfs_inode_extref_name_len(leaf, extref);
static struct btrfs_bio *alloc_btrfs_bio(int total_stripes, int real_stripes)
{
struct btrfs_bio *bbio = kzalloc(
+ /* the size of the btrfs_bio */
sizeof(struct btrfs_bio) +
+ /* plus the variable array for the stripes */
sizeof(struct btrfs_bio_stripe) * (total_stripes) +
+ /* plus the variable array for the tgt dev */
sizeof(int) * (real_stripes) +
- sizeof(u64) * (real_stripes),
+ /*
+ * plus the raid_map, which includes both the tgt dev
+ * and the stripes
+ */
+ sizeof(u64) * (total_stripes),
GFP_NOFS);
if (!bbio)
return NULL;
name, name_len, -1);
if (!di && (flags & XATTR_REPLACE))
ret = -ENODATA;
+ else if (IS_ERR(di))
+ ret = PTR_ERR(di);
else if (di)
ret = btrfs_delete_one_dir_name(trans, root, path, di);
goto out;
ASSERT(mutex_is_locked(&inode->i_mutex));
di = btrfs_lookup_xattr(NULL, root, path, btrfs_ino(inode),
name, name_len, 0);
- if (!di) {
+ if (!di)
ret = -ENODATA;
+ else if (IS_ERR(di))
+ ret = PTR_ERR(di);
+ if (ret)
goto out;
- }
btrfs_release_path(path);
di = NULL;
}
/*
* fs/cifs/cifsencrypt.c
*
+ * Encryption and hashing operations relating to NTLM, NTLMv2. See MS-NLMP
+ * for more detailed information
+ *
* Copyright (C) International Business Machines Corp., 2005,2013
* Author(s): Steve French (sfrench@us.ibm.com)
*
__func__);
return rc;
}
- } else if (ses->serverName) {
+ } else {
+ /* We use ses->serverName if no domain name available */
len = strlen(ses->serverName);
server = kmalloc(2 + (len * 2), GFP_KERNEL);
pr_warn("CIFS: username too long\n");
goto cifs_parse_mount_err;
}
+
+ kfree(vol->username);
vol->username = kstrdup(string, GFP_KERNEL);
if (!vol->username)
goto cifs_parse_mount_err;
goto cifs_parse_mount_err;
}
+ kfree(vol->domainname);
vol->domainname = kstrdup(string, GFP_KERNEL);
if (!vol->domainname) {
pr_warn("CIFS: no memory for domainname\n");
}
if (strncasecmp(string, "default", 7) != 0) {
+ kfree(vol->iocharset);
vol->iocharset = kstrdup(string,
GFP_KERNEL);
if (!vol->iocharset) {
* calling name ends in null (byte 16) from old smb
* convention.
*/
- if (server->workstation_RFC1001_name &&
- server->workstation_RFC1001_name[0] != 0)
+ if (server->workstation_RFC1001_name[0] != 0)
rfc1002mangle(ses_init_buf->trailer.
session_req.calling_name,
server->workstation_RFC1001_name,
#endif /* CIFS_WEAK_PW_HASH */
rc = SMBNTencrypt(tcon->password, ses->server->cryptkey,
bcc_ptr, nls_codepage);
+ if (rc) {
+ cifs_dbg(FYI, "%s Can't generate NTLM rsp. Error: %d\n",
+ __func__, rc);
+ cifs_buf_release(smb_buffer);
+ return rc;
+ }
bcc_ptr += CIFS_AUTH_RESP_SIZE;
if (ses->capabilities & CAP_UNICODE) {
cifsFileInfo_put(inv_file);
spin_lock(&cifs_file_list_lock);
++refind;
+ inv_file = NULL;
goto refind_writable;
}
}
cifs_buf_release(srchinf->ntwrk_buf_start);
}
kfree(srchinf);
+ if (rc)
+ goto cgii_exit;
} else
goto cgii_exit;
/* return pointer to beginning of data area, ie offset from SMB start */
if ((*off != 0) && (*len != 0))
- return hdr->ProtocolId + *off;
+ return (char *)(&hdr->ProtocolId[0]) + *off;
else
return NULL;
}
/* No need to change MaxChunks since already set to 1 */
chunk_sizes_updated = true;
- }
+ } else
+ goto cchunk_out;
}
cchunk_out:
struct smb2_ioctl_req *req;
struct smb2_ioctl_rsp *rsp;
struct TCP_Server_Info *server;
- struct cifs_ses *ses = tcon->ses;
+ struct cifs_ses *ses;
struct kvec iov[2];
int resp_buftype;
int num_iovecs;
if (plen)
*plen = 0;
+ if (tcon)
+ ses = tcon->ses;
+ else
+ return -EIO;
+
if (ses && (ses->server))
server = ses->server;
else
rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base;
if ((rc != 0) && (rc != -EINVAL)) {
- if (tcon)
- cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
+ cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
goto ioctl_exit;
} else if (rc == -EINVAL) {
if ((opcode != FSCTL_SRV_COPYCHUNK_WRITE) &&
(opcode != FSCTL_SRV_COPYCHUNK)) {
- if (tcon)
- cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
+ cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
goto ioctl_exit;
}
}
rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, 0);
- if ((rc != 0) && tcon)
+ if (rc != 0)
cifs_stats_fail_inc(tcon, SMB2_FLUSH_HE);
free_rsp_buf(resp_buftype, iov[0].iov_base);
struct kvec iov[2];
int rc = 0;
int len;
- int resp_buftype;
+ int resp_buftype = CIFS_NO_BUFFER;
unsigned char *bufptr;
struct TCP_Server_Info *server;
struct cifs_ses *ses = tcon->ses;
}
#define ECRYPTFS_MAX_KEYSET_SIZE 1024
-#define ECRYPTFS_MAX_CIPHER_NAME_SIZE 32
+#define ECRYPTFS_MAX_CIPHER_NAME_SIZE 31
#define ECRYPTFS_MAX_NUM_ENC_KEYS 64
#define ECRYPTFS_MAX_IV_BYTES 16 /* 128 bits */
#define ECRYPTFS_SALT_BYTES 2
struct crypto_ablkcipher *tfm;
struct crypto_hash *hash_tfm; /* Crypto context for generating
* the initialization vectors */
- unsigned char cipher[ECRYPTFS_MAX_CIPHER_NAME_SIZE];
+ unsigned char cipher[ECRYPTFS_MAX_CIPHER_NAME_SIZE + 1];
unsigned char key[ECRYPTFS_MAX_KEY_BYTES];
unsigned char root_iv[ECRYPTFS_MAX_IV_BYTES];
struct list_head keysig_list;
struct file *lower_file = ecryptfs_file_to_lower(file);
long rc = -ENOTTY;
- if (lower_file->f_op->unlocked_ioctl)
+ if (!lower_file->f_op->unlocked_ioctl)
+ return rc;
+
+ switch (cmd) {
+ case FITRIM:
+ case FS_IOC_GETFLAGS:
+ case FS_IOC_SETFLAGS:
+ case FS_IOC_GETVERSION:
+ case FS_IOC_SETVERSION:
rc = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg);
- return rc;
+ fsstack_copy_attr_all(file_inode(file), file_inode(lower_file));
+
+ return rc;
+ default:
+ return rc;
+ }
}
#ifdef CONFIG_COMPAT
struct file *lower_file = ecryptfs_file_to_lower(file);
long rc = -ENOIOCTLCMD;
- if (lower_file->f_op->compat_ioctl)
+ if (!lower_file->f_op->compat_ioctl)
+ return rc;
+
+ switch (cmd) {
+ case FITRIM:
+ case FS_IOC32_GETFLAGS:
+ case FS_IOC32_SETFLAGS:
+ case FS_IOC32_GETVERSION:
+ case FS_IOC32_SETVERSION:
rc = lower_file->f_op->compat_ioctl(lower_file, cmd, arg);
- return rc;
+ fsstack_copy_attr_all(file_inode(file), file_inode(lower_file));
+
+ return rc;
+ default:
+ return rc;
+ }
}
#endif
struct blkcipher_desc desc;
char fnek_sig_hex[ECRYPTFS_SIG_SIZE_HEX + 1];
char iv[ECRYPTFS_MAX_IV_BYTES];
- char cipher_string[ECRYPTFS_MAX_CIPHER_NAME_SIZE];
+ char cipher_string[ECRYPTFS_MAX_CIPHER_NAME_SIZE + 1];
};
/**
if (!cipher_name_set) {
int cipher_name_len = strlen(ECRYPTFS_DEFAULT_CIPHER);
- BUG_ON(cipher_name_len >= ECRYPTFS_MAX_CIPHER_NAME_SIZE);
+ BUG_ON(cipher_name_len > ECRYPTFS_MAX_CIPHER_NAME_SIZE);
strcpy(mount_crypt_stat->global_default_cipher_name,
ECRYPTFS_DEFAULT_CIPHER);
}
struct completion *done; /* set if the caller waits */
};
+/*
+ * If an inode is constantly having its pages dirtied, but then the
+ * updates stop dirtytime_expire_interval seconds in the past, it's
+ * possible for the worst case time between when an inode has its
+ * timestamps updated and when they finally get written out to be two
+ * dirtytime_expire_intervals. We set the default to 12 hours (in
+ * seconds), which means most of the time inodes will have their
+ * timestamps written to disk after 12 hours, but in the worst case a
+ * few inodes might not their timestamps updated for 24 hours.
+ */
+unsigned int dirtytime_expire_interval = 12 * 60 * 60;
+
/**
* writeback_in_progress - determine whether there is writeback in progress
* @bdi: the device's backing_dev_info structure.
if ((flags & EXPIRE_DIRTY_ATIME) == 0)
older_than_this = work->older_than_this;
- else if ((work->reason == WB_REASON_SYNC) == 0) {
- expire_time = jiffies - (HZ * 86400);
+ else if (!work->for_sync) {
+ expire_time = jiffies - (dirtytime_expire_interval * HZ);
older_than_this = &expire_time;
}
while (!list_empty(delaying_queue)) {
*/
redirty_tail(inode, wb);
} else if (inode->i_state & I_DIRTY_TIME) {
+ inode->dirtied_when = jiffies;
list_move(&inode->i_wb_list, &wb->b_dirty_time);
} else {
/* The inode is clean. Remove from writeback lists. */
spin_lock(&inode->i_lock);
dirty = inode->i_state & I_DIRTY;
- if (((dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) &&
- (inode->i_state & I_DIRTY_TIME)) ||
- (inode->i_state & I_DIRTY_TIME_EXPIRED)) {
- dirty |= I_DIRTY_TIME | I_DIRTY_TIME_EXPIRED;
- trace_writeback_lazytime(inode);
- }
+ if (inode->i_state & I_DIRTY_TIME) {
+ if ((dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) ||
+ unlikely(inode->i_state & I_DIRTY_TIME_EXPIRED) ||
+ unlikely(time_after(jiffies,
+ (inode->dirtied_time_when +
+ dirtytime_expire_interval * HZ)))) {
+ dirty |= I_DIRTY_TIME | I_DIRTY_TIME_EXPIRED;
+ trace_writeback_lazytime(inode);
+ }
+ } else
+ inode->i_state &= ~I_DIRTY_TIME_EXPIRED;
inode->i_state &= ~dirty;
/*
rcu_read_unlock();
}
+/*
+ * Wake up bdi's periodically to make sure dirtytime inodes gets
+ * written back periodically. We deliberately do *not* check the
+ * b_dirtytime list in wb_has_dirty_io(), since this would cause the
+ * kernel to be constantly waking up once there are any dirtytime
+ * inodes on the system. So instead we define a separate delayed work
+ * function which gets called much more rarely. (By default, only
+ * once every 12 hours.)
+ *
+ * If there is any other write activity going on in the file system,
+ * this function won't be necessary. But if the only thing that has
+ * happened on the file system is a dirtytime inode caused by an atime
+ * update, we need this infrastructure below to make sure that inode
+ * eventually gets pushed out to disk.
+ */
+static void wakeup_dirtytime_writeback(struct work_struct *w);
+static DECLARE_DELAYED_WORK(dirtytime_work, wakeup_dirtytime_writeback);
+
+static void wakeup_dirtytime_writeback(struct work_struct *w)
+{
+ struct backing_dev_info *bdi;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(bdi, &bdi_list, bdi_list) {
+ if (list_empty(&bdi->wb.b_dirty_time))
+ continue;
+ bdi_wakeup_thread(bdi);
+ }
+ rcu_read_unlock();
+ schedule_delayed_work(&dirtytime_work, dirtytime_expire_interval * HZ);
+}
+
+static int __init start_dirtytime_writeback(void)
+{
+ schedule_delayed_work(&dirtytime_work, dirtytime_expire_interval * HZ);
+ return 0;
+}
+__initcall(start_dirtytime_writeback);
+
+int dirtytime_interval_handler(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ int ret;
+
+ ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+ if (ret == 0 && write)
+ mod_delayed_work(system_wq, &dirtytime_work, 0);
+ return ret;
+}
+
static noinline void block_dump___mark_inode_dirty(struct inode *inode)
{
if (inode->i_ino || strcmp(inode->i_sb->s_id, "bdev")) {
}
inode->dirtied_when = jiffies;
- list_move(&inode->i_wb_list, dirtytime ?
- &bdi->wb.b_dirty_time : &bdi->wb.b_dirty);
+ if (dirtytime)
+ inode->dirtied_time_when = jiffies;
+ if (inode->i_state & (I_DIRTY_INODE | I_DIRTY_PAGES))
+ list_move(&inode->i_wb_list, &bdi->wb.b_dirty);
+ else
+ list_move(&inode->i_wb_list,
+ &bdi->wb.b_dirty_time);
spin_unlock(&bdi->wb.list_lock);
trace_writeback_dirty_inode_enqueue(inode);
newpage = buf->page;
- if (WARN_ON(!PageUptodate(newpage)))
- return -EIO;
+ if (!PageUptodate(newpage))
+ SetPageUptodate(newpage);
ClearPageMappedToDisk(newpage);
return err;
}
+static int fuse_dev_open(struct inode *inode, struct file *file)
+{
+ /*
+ * The fuse device's file's private_data is used to hold
+ * the fuse_conn(ection) when it is mounted, and is used to
+ * keep track of whether the file has been mounted already.
+ */
+ file->private_data = NULL;
+ return 0;
+}
+
static ssize_t fuse_dev_read(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,
unsigned int size, struct fuse_copy_state *cs)
{
+ /* Don't try to move pages (yet) */
+ cs->move_pages = 0;
+
switch (code) {
case FUSE_NOTIFY_POLL:
return fuse_notify_poll(fc, size, cs);
const struct file_operations fuse_dev_operations = {
.owner = THIS_MODULE,
+ .open = fuse_dev_open,
.llseek = no_llseek,
.read = do_sync_read,
.aio_read = fuse_dev_read,
hfs_bnode_write(node, entry, data_off + key_len, entry_len);
hfs_bnode_dump(node);
- if (new_node) {
- /* update parent key if we inserted a key
- * at the start of the first node
- */
- if (!rec && new_node != node)
- hfs_brec_update_parent(fd);
+ /*
+ * update parent key if we inserted a key
+ * at the start of the node and it is not the new node
+ */
+ if (!rec && new_node != node) {
+ hfs_bnode_read_key(node, fd->search_key, data_off + size);
+ hfs_brec_update_parent(fd);
+ }
+ if (new_node) {
hfs_bnode_put(fd->bnode);
if (!new_node->parent) {
hfs_btree_inc_height(tree);
goto again;
}
- if (!rec)
- hfs_brec_update_parent(fd);
-
return 0;
}
if (IS_ERR(parent))
return PTR_ERR(parent);
__hfs_brec_find(parent, fd, hfs_find_rec_by_key);
+ if (fd->record < 0)
+ return -ENOENT;
hfs_bnode_dump(parent);
rec = fd->record;
goto out_free;
}
+ of->event = atomic_read(&of->kn->attr.open->event);
ops = kernfs_ops(of->kn);
if (ops->read)
len = ops->read(of, buf, len, *ppos);
int __break_lease(struct inode *inode, unsigned int mode, unsigned int type)
{
int error = 0;
- struct file_lock *new_fl;
struct file_lock_context *ctx = inode->i_flctx;
- struct file_lock *fl;
+ struct file_lock *new_fl, *fl, *tmp;
unsigned long break_time;
int want_write = (mode & O_ACCMODE) != O_RDONLY;
LIST_HEAD(dispose);
break_time++; /* so that 0 means no break time */
}
- list_for_each_entry(fl, &ctx->flc_lease, fl_list) {
+ list_for_each_entry_safe(fl, tmp, &ctx->flc_lease, fl_list) {
if (!leases_conflict(fl, new_fl))
continue;
if (want_write) {
}
if (my_fl != NULL) {
- error = lease->fl_lmops->lm_change(my_fl, arg, &dispose);
+ lease = my_fl;
+ error = lease->fl_lmops->lm_change(lease, arg, &dispose);
if (error)
goto out;
goto out_setup;
break;
}
}
- trace_generic_delete_lease(inode, fl);
+ trace_generic_delete_lease(inode, victim);
if (victim)
error = fl->fl_lmops->lm_change(victim, F_UNLCK, &dispose);
spin_unlock(&ctx->flc_lock);
static bool nfs_client_init_is_complete(const struct nfs_client *clp)
{
- return clp->cl_cons_state != NFS_CS_INITING;
+ return clp->cl_cons_state <= NFS_CS_READY;
}
int nfs_wait_client_init_complete(const struct nfs_client *clp)
clear_bit(NFS_DELEGATION_NEED_RECLAIM,
&delegation->flags);
spin_unlock(&delegation->lock);
- put_rpccred(oldcred);
rcu_read_unlock();
+ put_rpccred(oldcred);
trace_nfs4_reclaim_delegation(inode, res->delegation_type);
} else {
/* We appear to have raced with a delegation return. */
delegation = NULL;
goto out;
}
- freeme = nfs_detach_delegation_locked(nfsi,
+ if (test_and_set_bit(NFS_DELEGATION_RETURNING,
+ &old_delegation->flags))
+ goto out;
+ freeme = nfs_detach_delegation_locked(nfsi,
old_delegation, clp);
if (freeme == NULL)
goto out;
{
bool ret = false;
+ if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
+ goto out;
if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags))
ret = true;
if (test_and_clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) && !ret) {
ret = true;
spin_unlock(&delegation->lock);
}
+out:
return ret;
}
super_list) {
if (!nfs_delegation_need_return(delegation))
continue;
- inode = nfs_delegation_grab_inode(delegation);
- if (inode == NULL)
+ if (!nfs_sb_active(server->super))
continue;
+ inode = nfs_delegation_grab_inode(delegation);
+ if (inode == NULL) {
+ rcu_read_unlock();
+ nfs_sb_deactive(server->super);
+ goto restart;
+ }
delegation = nfs_start_delegation_return_locked(NFS_I(inode));
rcu_read_unlock();
err = nfs_end_delegation_return(inode, delegation, 0);
iput(inode);
+ nfs_sb_deactive(server->super);
if (!err)
goto restart;
set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
list_for_each_entry_rcu(delegation, &server->delegations,
super_list) {
+ if (test_bit(NFS_DELEGATION_RETURNING,
+ &delegation->flags))
+ continue;
if (test_bit(NFS_DELEGATION_NEED_RECLAIM,
&delegation->flags) == 0)
continue;
- inode = nfs_delegation_grab_inode(delegation);
- if (inode == NULL)
+ if (!nfs_sb_active(server->super))
continue;
- delegation = nfs_detach_delegation(NFS_I(inode),
- delegation, server);
+ inode = nfs_delegation_grab_inode(delegation);
+ if (inode == NULL) {
+ rcu_read_unlock();
+ nfs_sb_deactive(server->super);
+ goto restart;
+ }
+ delegation = nfs_start_delegation_return_locked(NFS_I(inode));
rcu_read_unlock();
-
- if (delegation != NULL)
- nfs_free_delegation(delegation);
+ if (delegation != NULL) {
+ delegation = nfs_detach_delegation(NFS_I(inode),
+ delegation, server);
+ if (delegation != NULL)
+ nfs_free_delegation(delegation);
+ }
iput(inode);
+ nfs_sb_deactive(server->super);
goto restart;
}
}
return 0;
}
+/* Match file and dirent using either filehandle or fileid
+ * Note: caller is responsible for checking the fsid
+ */
static
int nfs_same_file(struct dentry *dentry, struct nfs_entry *entry)
{
+ struct nfs_inode *nfsi;
+
if (dentry->d_inode == NULL)
goto different;
- if (nfs_compare_fh(entry->fh, NFS_FH(dentry->d_inode)) != 0)
- goto different;
- return 1;
+
+ nfsi = NFS_I(dentry->d_inode);
+ if (entry->fattr->fileid == nfsi->fileid)
+ return 1;
+ if (nfs_compare_fh(entry->fh, &nfsi->fh) == 0)
+ return 1;
different:
return 0;
}
struct inode *inode;
int status;
+ if (!(entry->fattr->valid & NFS_ATTR_FATTR_FILEID))
+ return;
+ if (!(entry->fattr->valid & NFS_ATTR_FATTR_FSID))
+ return;
if (filename.name[0] == '.') {
if (filename.len == 1)
return;
dentry = d_lookup(parent, &filename);
if (dentry != NULL) {
+ /* Is there a mountpoint here? If so, just exit */
+ if (!nfs_fsid_equal(&NFS_SB(dentry->d_sb)->fsid,
+ &entry->fattr->fsid))
+ goto out;
if (nfs_same_file(dentry, entry)) {
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
status = nfs_refresh_inode(dentry->d_inode, entry->fattr);
iocb->ki_filp,
iov_iter_count(to), (unsigned long) iocb->ki_pos);
- result = nfs_revalidate_mapping(inode, iocb->ki_filp->f_mapping);
+ result = nfs_revalidate_mapping_protected(inode, iocb->ki_filp->f_mapping);
if (!result) {
result = generic_file_read_iter(iocb, to);
if (result > 0)
dprintk("NFS: splice_read(%pD2, %lu@%Lu)\n",
filp, (unsigned long) count, (unsigned long long) *ppos);
- res = nfs_revalidate_mapping(inode, filp->f_mapping);
+ res = nfs_revalidate_mapping_protected(inode, filp->f_mapping);
if (!res) {
res = generic_file_splice_read(filp, ppos, pipe, count, flags);
if (res > 0)
nfs_wait_bit_killable, TASK_KILLABLE);
if (ret)
return ret;
+ /*
+ * Wait for O_DIRECT to complete
+ */
+ nfs_inode_dio_wait(mapping->host);
page = grab_cache_page_write_begin(mapping, index, flags);
if (!page)
/* make sure the cache has finished storing the page */
nfs_fscache_wait_on_page_write(NFS_I(inode), page);
+ wait_on_bit_action(&NFS_I(inode)->flags, NFS_INO_INVALIDATING,
+ nfs_wait_bit_killable, TASK_KILLABLE);
+
lock_page(page);
mapping = page_file_mapping(page);
if (mapping != inode->i_mapping)
* This is a copy of the common vmtruncate, but with the locking
* corrected to take into account the fact that NFS requires
* inode->i_size to be updated under the inode->i_lock.
+ * Note: must be called with inode->i_lock held!
*/
static int nfs_vmtruncate(struct inode * inode, loff_t offset)
{
if (err)
goto out;
- spin_lock(&inode->i_lock);
i_size_write(inode, offset);
/* Optimisation */
if (offset == 0)
NFS_I(inode)->cache_validity &= ~NFS_INO_INVALID_DATA;
- spin_unlock(&inode->i_lock);
+ spin_unlock(&inode->i_lock);
truncate_pagecache(inode, offset);
+ spin_lock(&inode->i_lock);
out:
return err;
}
* Note: we do this in the *proc.c in order to ensure that
* it works for things like exclusive creates too.
*/
-void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr)
+void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr,
+ struct nfs_fattr *fattr)
{
+ /* Barrier: bump the attribute generation count. */
+ nfs_fattr_set_barrier(fattr);
+
+ spin_lock(&inode->i_lock);
+ NFS_I(inode)->attr_gencount = fattr->gencount;
if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0) {
- spin_lock(&inode->i_lock);
if ((attr->ia_valid & ATTR_MODE) != 0) {
int mode = attr->ia_mode & S_IALLUGO;
mode |= inode->i_mode & ~S_IALLUGO;
inode->i_gid = attr->ia_gid;
nfs_set_cache_invalid(inode, NFS_INO_INVALID_ACCESS
| NFS_INO_INVALID_ACL);
- spin_unlock(&inode->i_lock);
}
if ((attr->ia_valid & ATTR_SIZE) != 0) {
nfs_inc_stats(inode, NFSIOS_SETATTRTRUNC);
nfs_vmtruncate(inode, attr->ia_size);
}
+ nfs_update_inode(inode, fattr);
+ spin_unlock(&inode->i_lock);
}
EXPORT_SYMBOL_GPL(nfs_setattr_update_inode);
if (mapping->nrpages != 0) {
if (S_ISREG(inode->i_mode)) {
+ unmap_mapping_range(mapping, 0, 0, 0);
ret = nfs_sync_mapping(mapping);
if (ret < 0)
return ret;
}
/**
- * nfs_revalidate_mapping - Revalidate the pagecache
+ * __nfs_revalidate_mapping - Revalidate the pagecache
* @inode - pointer to host inode
* @mapping - pointer to mapping
+ * @may_lock - take inode->i_mutex?
*/
-int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
+static int __nfs_revalidate_mapping(struct inode *inode,
+ struct address_space *mapping,
+ bool may_lock)
{
struct nfs_inode *nfsi = NFS_I(inode);
unsigned long *bitlock = &nfsi->flags;
nfsi->cache_validity &= ~NFS_INO_INVALID_DATA;
spin_unlock(&inode->i_lock);
trace_nfs_invalidate_mapping_enter(inode);
- ret = nfs_invalidate_mapping(inode, mapping);
+ if (may_lock) {
+ mutex_lock(&inode->i_mutex);
+ ret = nfs_invalidate_mapping(inode, mapping);
+ mutex_unlock(&inode->i_mutex);
+ } else
+ ret = nfs_invalidate_mapping(inode, mapping);
trace_nfs_invalidate_mapping_exit(inode, ret);
clear_bit_unlock(NFS_INO_INVALIDATING, bitlock);
return ret;
}
+/**
+ * nfs_revalidate_mapping - Revalidate the pagecache
+ * @inode - pointer to host inode
+ * @mapping - pointer to mapping
+ */
+int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
+{
+ return __nfs_revalidate_mapping(inode, mapping, false);
+}
+
+/**
+ * nfs_revalidate_mapping_protected - Revalidate the pagecache
+ * @inode - pointer to host inode
+ * @mapping - pointer to mapping
+ *
+ * Differs from nfs_revalidate_mapping() in that it grabs the inode->i_mutex
+ * while invalidating the mapping.
+ */
+int nfs_revalidate_mapping_protected(struct inode *inode, struct address_space *mapping)
+{
+ return __nfs_revalidate_mapping(inode, mapping, true);
+}
+
static unsigned long nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr)
{
struct nfs_inode *nfsi = NFS_I(inode);
return timespec_compare(&fattr->ctime, &inode->i_ctime) > 0;
}
-static int nfs_size_need_update(const struct inode *inode, const struct nfs_fattr *fattr)
-{
- if (!(fattr->valid & NFS_ATTR_FATTR_SIZE))
- return 0;
- return nfs_size_to_loff_t(fattr->size) > i_size_read(inode);
-}
-
static atomic_long_t nfs_attr_generation_counter;
static unsigned long nfs_read_attr_generation_counter(void)
{
return atomic_long_inc_return(&nfs_attr_generation_counter);
}
+EXPORT_SYMBOL_GPL(nfs_inc_attr_generation_counter);
void nfs_fattr_init(struct nfs_fattr *fattr)
{
}
EXPORT_SYMBOL_GPL(nfs_fattr_init);
+/**
+ * nfs_fattr_set_barrier
+ * @fattr: attributes
+ *
+ * Used to set a barrier after an attribute was updated. This
+ * barrier ensures that older attributes from RPC calls that may
+ * have raced with our update cannot clobber these new values.
+ * Note that you are still responsible for ensuring that other
+ * operations which change the attribute on the server do not
+ * collide.
+ */
+void nfs_fattr_set_barrier(struct nfs_fattr *fattr)
+{
+ fattr->gencount = nfs_inc_attr_generation_counter();
+}
+
struct nfs_fattr *nfs_alloc_fattr(void)
{
struct nfs_fattr *fattr;
return ((long)fattr->gencount - (long)nfsi->attr_gencount) > 0 ||
nfs_ctime_need_update(inode, fattr) ||
- nfs_size_need_update(inode, fattr) ||
((long)nfsi->attr_gencount - (long)nfs_read_attr_generation_counter() > 0);
}
int status;
spin_lock(&inode->i_lock);
+ nfs_fattr_set_barrier(fattr);
status = nfs_post_op_update_inode_locked(inode, fattr);
spin_unlock(&inode->i_lock);
EXPORT_SYMBOL_GPL(nfs_post_op_update_inode);
/**
- * nfs_post_op_update_inode_force_wcc - try to update the inode attribute cache
+ * nfs_post_op_update_inode_force_wcc_locked - update the inode attribute cache
* @inode - pointer to inode
* @fattr - updated attributes
*
*
* This function is mainly designed to be used by the ->write_done() functions.
*/
-int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fattr)
+int nfs_post_op_update_inode_force_wcc_locked(struct inode *inode, struct nfs_fattr *fattr)
{
int status;
- spin_lock(&inode->i_lock);
/* Don't do a WCC update if these attributes are already stale */
if ((fattr->valid & NFS_ATTR_FATTR) == 0 ||
!nfs_inode_attrs_need_update(inode, fattr)) {
}
out_noforce:
status = nfs_post_op_update_inode_locked(inode, fattr);
+ return status;
+}
+
+/**
+ * nfs_post_op_update_inode_force_wcc - try to update the inode attribute cache
+ * @inode - pointer to inode
+ * @fattr - updated attributes
+ *
+ * After an operation that has changed the inode metadata, mark the
+ * attribute cache as being invalid, then try to update it. Fake up
+ * weak cache consistency data, if none exist.
+ *
+ * This function is mainly designed to be used by the ->write_done() functions.
+ */
+int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fattr)
+{
+ int status;
+
+ spin_lock(&inode->i_lock);
+ nfs_fattr_set_barrier(fattr);
+ status = nfs_post_op_update_inode_force_wcc_locked(inode, fattr);
spin_unlock(&inode->i_lock);
return status;
}
nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);
nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
nfsi->attrtimeo_timestamp = now;
+ /* Set barrier to be more recent than all outstanding updates */
nfsi->attr_gencount = nfs_inc_attr_generation_counter();
} else {
if (!time_in_range_open(now, nfsi->attrtimeo_timestamp, nfsi->attrtimeo_timestamp + nfsi->attrtimeo)) {
nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode);
nfsi->attrtimeo_timestamp = now;
}
+ /* Set the barrier to be more recent than this fattr */
+ if ((long)fattr->gencount - (long)nfsi->attr_gencount > 0)
+ nfsi->attr_gencount = fattr->gencount;
}
invalid &= ~NFS_INO_INVALID_ATTR;
/* Don't invalidate the data if we were to blame */
struct nfs_commit_info *cinfo,
u32 ds_commit_idx);
int nfs_write_need_commit(struct nfs_pgio_header *);
+void nfs_writeback_update_inode(struct nfs_pgio_header *hdr);
int nfs_generic_commit_list(struct inode *inode, struct list_head *head,
int how, struct nfs_commit_info *cinfo);
void nfs_retry_commit(struct list_head *page_list,
nfs_fattr_init(fattr);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
if (status == 0)
- nfs_setattr_update_inode(inode, sattr);
+ nfs_setattr_update_inode(inode, sattr, fattr);
dprintk("NFS reply setattr: %d\n", status);
return status;
}
if (nfs3_async_handle_jukebox(task, inode))
return -EAGAIN;
if (task->tk_status >= 0)
- nfs_post_op_update_inode_force_wcc(inode, hdr->res.fattr);
+ nfs_writeback_update_inode(hdr);
return 0;
}
if (entry->fattr->valid & NFS_ATTR_FATTR_V3)
entry->d_type = nfs_umode_to_dtype(entry->fattr->mode);
+ if (entry->fattr->fileid != entry->ino) {
+ entry->fattr->mounted_on_fileid = entry->ino;
+ entry->fattr->valid |= NFS_ATTR_FATTR_MOUNTED_ON_FILEID;
+ }
+
/* In fact, a post_op_fh3: */
p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL))
spin_lock(&nn->nfs_client_lock);
list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) {
+ if (pos == new)
+ goto found;
+
if (pos->rpc_ops != new->rpc_ops)
continue;
prev = pos;
status = nfs_wait_client_init_complete(pos);
- if (pos->cl_cons_state == NFS_CS_SESSION_INITING) {
- nfs4_schedule_lease_recovery(pos);
- status = nfs4_wait_clnt_recover(pos);
- }
spin_lock(&nn->nfs_client_lock);
if (status < 0)
break;
*/
if (!nfs4_match_client_owner_id(pos, new))
continue;
-
+found:
atomic_inc(&pos->cl_count);
*result = pos;
status = 0;
if (!cinfo->atomic || cinfo->before != dir->i_version)
nfs_force_lookup_revalidate(dir);
dir->i_version = cinfo->after;
+ nfsi->attr_gencount = nfs_inc_attr_generation_counter();
nfs_fscache_invalidate(dir);
spin_unlock(&dir->i_lock);
}
opendata->o_arg.open_flags = 0;
opendata->o_arg.fmode = fmode;
+ opendata->o_arg.share_access = nfs4_map_atomic_open_share(
+ NFS_SB(opendata->dentry->d_sb),
+ fmode, 0);
memset(&opendata->o_res, 0, sizeof(opendata->o_res));
memset(&opendata->c_res, 0, sizeof(opendata->c_res));
nfs4_init_opendata_res(opendata);
opendata->o_res.f_attr, sattr,
state, label, olabel);
if (status == 0) {
- nfs_setattr_update_inode(state->inode, sattr);
- nfs_post_op_update_inode(state->inode, opendata->o_res.f_attr);
+ nfs_setattr_update_inode(state->inode, sattr,
+ opendata->o_res.f_attr);
nfs_setsecurity(state->inode, opendata->o_res.f_attr, olabel);
}
}
case -NFS4ERR_BAD_STATEID:
case -NFS4ERR_EXPIRED:
if (!nfs4_stateid_match(&calldata->arg.stateid,
- &state->stateid)) {
+ &state->open_stateid)) {
rpc_restart_call_prepare(task);
goto out_release;
}
is_rdwr = test_bit(NFS_O_RDWR_STATE, &state->flags);
is_rdonly = test_bit(NFS_O_RDONLY_STATE, &state->flags);
is_wronly = test_bit(NFS_O_WRONLY_STATE, &state->flags);
- nfs4_stateid_copy(&calldata->arg.stateid, &state->stateid);
+ nfs4_stateid_copy(&calldata->arg.stateid, &state->open_stateid);
/* Calculate the change in open mode */
calldata->arg.fmode = 0;
if (state->n_rdwr == 0) {
status = nfs4_do_setattr(inode, cred, fattr, sattr, state, NULL, label);
if (status == 0) {
- nfs_setattr_update_inode(inode, sattr);
+ nfs_setattr_update_inode(inode, sattr, fattr);
nfs_setsecurity(inode, fattr, label);
}
nfs4_label_free(label);
}
if (task->tk_status >= 0) {
renew_lease(NFS_SERVER(inode), hdr->timestamp);
- nfs_post_op_update_inode_force_wcc(inode, &hdr->fattr);
+ nfs_writeback_update_inode(hdr);
}
return 0;
}
if (status == 0) {
clp->cl_clientid = res.clientid;
- clp->cl_exchange_flags = (res.flags & ~EXCHGID4_FLAG_CONFIRMED_R);
- if (!(res.flags & EXCHGID4_FLAG_CONFIRMED_R))
+ clp->cl_exchange_flags = res.flags;
+ /* Client ID is not confirmed */
+ if (!(res.flags & EXCHGID4_FLAG_CONFIRMED_R)) {
+ clear_bit(NFS4_SESSION_ESTABLISHED,
+ &clp->cl_session->session_state);
clp->cl_seqid = res.seqid;
+ }
kfree(clp->cl_serverowner);
clp->cl_serverowner = res.server_owner;
struct nfs41_create_session_res *res)
{
nfs4_copy_sessionid(&session->sess_id, &res->sessionid);
+ /* Mark client id and session as being confirmed */
+ session->clp->cl_exchange_flags |= EXCHGID4_FLAG_CONFIRMED_R;
+ set_bit(NFS4_SESSION_ESTABLISHED, &session->session_state);
session->flags = res->flags;
memcpy(&session->fc_attrs, &res->fc_attrs, sizeof(session->fc_attrs));
if (res->flags & SESSION4_BACK_CHAN)
dprintk("--> nfs4_proc_destroy_session\n");
/* session is still being setup */
- if (session->clp->cl_cons_state != NFS_CS_READY)
- return status;
+ if (!test_and_clear_bit(NFS4_SESSION_ESTABLISHED, &session->session_state))
+ return 0;
status = rpc_call_sync(session->clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
trace_nfs4_destroy_session(session->clp, status);
enum nfs4_session_state {
NFS4_SESSION_INITING,
+ NFS4_SESSION_ESTABLISHED,
};
extern int nfs4_setup_slot_table(struct nfs4_slot_table *tbl,
status = nfs4_proc_exchange_id(clp, cred);
if (status != NFS4_OK)
return status;
- set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
- return nfs41_walk_client_list(clp, result, cred);
+ status = nfs41_walk_client_list(clp, result, cred);
+ if (status < 0)
+ return status;
+ if (clp != *result)
+ return 0;
+
+ /* Purge state if the client id was established in a prior instance */
+ if (clp->cl_exchange_flags & EXCHGID4_FLAG_CONFIRMED_R)
+ set_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state);
+ else
+ set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
+ nfs4_schedule_state_manager(clp);
+ status = nfs_wait_client_init_complete(clp);
+ if (status < 0)
+ nfs_put_client(clp);
+ return status;
}
#endif /* CONFIG_NFS_V4_1 */
nfs_fattr_init(fattr);
status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
if (status == 0)
- nfs_setattr_update_inode(inode, sattr);
+ nfs_setattr_update_inode(inode, sattr, fattr);
dprintk("NFS reply setattr: %d\n", status);
return status;
}
static int nfs_write_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
{
- struct inode *inode = hdr->inode;
-
if (task->tk_status >= 0)
- nfs_post_op_update_inode_force_wcc(inode, hdr->res.fattr);
+ nfs_writeback_update_inode(hdr);
return 0;
}
return 0;
}
+static void nfs_writeback_check_extend(struct nfs_pgio_header *hdr,
+ struct nfs_fattr *fattr)
+{
+ struct nfs_pgio_args *argp = &hdr->args;
+ struct nfs_pgio_res *resp = &hdr->res;
+
+ if (!(fattr->valid & NFS_ATTR_FATTR_SIZE))
+ return;
+ if (argp->offset + resp->count != fattr->size)
+ return;
+ if (nfs_size_to_loff_t(fattr->size) < i_size_read(hdr->inode))
+ return;
+ /* Set attribute barrier */
+ nfs_fattr_set_barrier(fattr);
+}
+
+void nfs_writeback_update_inode(struct nfs_pgio_header *hdr)
+{
+ struct nfs_fattr *fattr = hdr->res.fattr;
+ struct inode *inode = hdr->inode;
+
+ if (fattr == NULL)
+ return;
+ spin_lock(&inode->i_lock);
+ nfs_writeback_check_extend(hdr, fattr);
+ nfs_post_op_update_inode_force_wcc_locked(inode, fattr);
+ spin_unlock(&inode->i_lock);
+}
+EXPORT_SYMBOL_GPL(nfs_writeback_update_inode);
+
/*
* This function is called when the WRITE call is complete.
*/
seg->offset = iomap.offset;
seg->length = iomap.length;
- dprintk("GET: %lld:%lld %d\n", bex->foff, bex->len, bex->es);
+ dprintk("GET: 0x%llx:0x%llx %d\n", bex->foff, bex->len, bex->es);
return 0;
out_error:
p = xdr_decode_hyper(p, &bex.foff);
if (bex.foff & (block_size - 1)) {
- dprintk("%s: unaligned offset %lld\n",
+ dprintk("%s: unaligned offset 0x%llx\n",
__func__, bex.foff);
goto fail;
}
p = xdr_decode_hyper(p, &bex.len);
if (bex.len & (block_size - 1)) {
- dprintk("%s: unaligned length %lld\n",
+ dprintk("%s: unaligned length 0x%llx\n",
__func__, bex.foff);
goto fail;
}
p = xdr_decode_hyper(p, &bex.soff);
if (bex.soff & (block_size - 1)) {
- dprintk("%s: unaligned disk offset %lld\n",
+ dprintk("%s: unaligned disk offset 0x%llx\n",
__func__, bex.soff);
goto fail;
}
{
struct super_block *sb = exp->ex_path.mnt->mnt_sb;
- if (exp->ex_flags & NFSEXP_NOPNFS)
+ if (!(exp->ex_flags & NFSEXP_PNFS))
return;
if (sb->s_export_op->get_uuid &&
list_move_tail(&lp->lo_perstate, reaplist);
return;
}
- end = seg->offset;
+ lo->offset = layout_end(seg);
} else {
/* retain the whole layout segment on a split. */
if (layout_end(seg) < end) {
dprintk("%s: split not supported\n", __func__);
return;
}
-
- lo->offset = layout_end(seg);
+ end = seg->offset;
}
layout_update_len(lo, end);
spin_lock(&clp->cl_lock);
list_for_each_entry_safe(ls, n, &clp->cl_lo_states, ls_perclnt) {
+ if (ls->ls_layout_type != lrp->lr_layout_type)
+ continue;
+
if (lrp->lr_return_type == RETURN_FSID &&
!fh_fsid_match(&ls->ls_stid.sc_file->fi_fhandle,
&cstate->current_fh.fh_handle))
rpc_ntop((struct sockaddr *)&clp->cl_addr, addr_str, sizeof(addr_str));
- nfsd4_cb_layout_fail(ls);
+ trace_layout_recall_fail(&ls->ls_stid.sc_stateid);
printk(KERN_WARNING
"nfsd: client %s failed to respond to layout recall. "
nfserr = ops->proc_getdeviceinfo(exp->ex_path.mnt->mnt_sb, gdp);
gdp->gd_notify_types &= ops->notify_types;
- exp_put(exp);
out:
+ exp_put(exp);
return nfserr;
}
nfs4_put_stid(&dp->dl_stid);
}
while (!list_empty(&clp->cl_revoked)) {
- dp = list_entry(reaplist.next, struct nfs4_delegation, dl_recall_lru);
+ dp = list_entry(clp->cl_revoked.next, struct nfs4_delegation, dl_recall_lru);
list_del_init(&dp->dl_recall_lru);
nfs4_put_stid(&dp->dl_stid);
}
} else
nfs4_free_openowner(&oo->oo_owner);
spin_unlock(&clp->cl_lock);
- return oo;
+ return ret;
}
static void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *open) {
} else
nfs4_free_lockowner(&lo->lo_owner);
spin_unlock(&clp->cl_lock);
- return lo;
+ return ret;
}
static void
p = xdr_decode_hyper(p, &lgp->lg_seg.offset);
p = xdr_decode_hyper(p, &lgp->lg_seg.length);
p = xdr_decode_hyper(p, &lgp->lg_minlength);
- nfsd4_decode_stateid(argp, &lgp->lg_sid);
+
+ status = nfsd4_decode_stateid(argp, &lgp->lg_sid);
+ if (status)
+ return status;
+
READ_BUF(4);
lgp->lg_maxcount = be32_to_cpup(p++);
p = xdr_decode_hyper(p, &lcp->lc_seg.offset);
p = xdr_decode_hyper(p, &lcp->lc_seg.length);
lcp->lc_reclaim = be32_to_cpup(p++);
- nfsd4_decode_stateid(argp, &lcp->lc_sid);
+
+ status = nfsd4_decode_stateid(argp, &lcp->lc_sid);
+ if (status)
+ return status;
+
READ_BUF(4);
lcp->lc_newoffset = be32_to_cpup(p++);
if (lcp->lc_newoffset) {
READ_BUF(16);
p = xdr_decode_hyper(p, &lrp->lr_seg.offset);
p = xdr_decode_hyper(p, &lrp->lr_seg.length);
- nfsd4_decode_stateid(argp, &lrp->lr_sid);
+
+ status = nfsd4_decode_stateid(argp, &lrp->lr_sid);
+ if (status)
+ return status;
+
READ_BUF(4);
lrp->lrf_body_len = be32_to_cpup(p++);
if (lrp->lrf_body_len > 0) {
return nfserr_resource;
*p++ = cpu_to_be32(lrp->lrs_present);
if (lrp->lrs_present)
- nfsd4_encode_stateid(xdr, &lrp->lr_sid);
+ return nfsd4_encode_stateid(xdr, &lrp->lr_sid);
return nfs_ok;
}
#endif /* CONFIG_NFSD_PNFS */
{
unsigned int hashsize;
unsigned int i;
+ int status = 0;
max_drc_entries = nfsd_cache_size_limit();
atomic_set(&num_drc_entries, 0);
hashsize = nfsd_hashsize(max_drc_entries);
maskbits = ilog2(hashsize);
- register_shrinker(&nfsd_reply_cache_shrinker);
+ status = register_shrinker(&nfsd_reply_cache_shrinker);
+ if (status)
+ return status;
+
drc_slab = kmem_cache_create("nfsd_drc", sizeof(struct svc_cacherep),
0, 0, NULL);
if (!drc_slab)
#include "alloc.h"
#include "dat.h"
+static void __nilfs_btree_init(struct nilfs_bmap *bmap);
+
static struct nilfs_btree_path *nilfs_btree_alloc_path(void)
{
struct nilfs_btree_path *path;
return ret;
}
+/**
+ * nilfs_btree_root_broken - verify consistency of btree root node
+ * @node: btree root node to be examined
+ * @ino: inode number
+ *
+ * Return Value: If node is broken, 1 is returned. Otherwise, 0 is returned.
+ */
+static int nilfs_btree_root_broken(const struct nilfs_btree_node *node,
+ unsigned long ino)
+{
+ int level, flags, nchildren;
+ int ret = 0;
+
+ level = nilfs_btree_node_get_level(node);
+ flags = nilfs_btree_node_get_flags(node);
+ nchildren = nilfs_btree_node_get_nchildren(node);
+
+ if (unlikely(level < NILFS_BTREE_LEVEL_NODE_MIN ||
+ level > NILFS_BTREE_LEVEL_MAX ||
+ nchildren < 0 ||
+ nchildren > NILFS_BTREE_ROOT_NCHILDREN_MAX)) {
+ pr_crit("NILFS: bad btree root (inode number=%lu): level = %d, flags = 0x%x, nchildren = %d\n",
+ ino, level, flags, nchildren);
+ ret = 1;
+ }
+ return ret;
+}
+
int nilfs_btree_broken_node_block(struct buffer_head *bh)
{
int ret;
/* convert and insert */
dat = NILFS_BMAP_USE_VBN(btree) ? nilfs_bmap_get_dat(btree) : NULL;
- nilfs_btree_init(btree);
+ __nilfs_btree_init(btree);
if (nreq != NULL) {
nilfs_bmap_commit_alloc_ptr(btree, dreq, dat);
nilfs_bmap_commit_alloc_ptr(btree, nreq, dat);
.bop_gather_data = NULL,
};
-int nilfs_btree_init(struct nilfs_bmap *bmap)
+static void __nilfs_btree_init(struct nilfs_bmap *bmap)
{
bmap->b_ops = &nilfs_btree_ops;
bmap->b_nchildren_per_block =
NILFS_BTREE_NODE_NCHILDREN_MAX(nilfs_btree_node_size(bmap));
- return 0;
+}
+
+int nilfs_btree_init(struct nilfs_bmap *bmap)
+{
+ int ret = 0;
+
+ __nilfs_btree_init(bmap);
+
+ if (nilfs_btree_root_broken(nilfs_btree_get_root(bmap),
+ bmap->b_inode->i_ino))
+ ret = -EIO;
+ return ret;
}
void nilfs_btree_init_gc(struct nilfs_bmap *bmap)
struct the_nilfs *nilfs)
{
struct nilfs_inode_info *ii, *n;
+ int during_mount = !(sci->sc_super->s_flags & MS_ACTIVE);
int defer_iput = false;
spin_lock(&nilfs->ns_inode_lock);
brelse(ii->i_bh);
ii->i_bh = NULL;
list_del_init(&ii->i_dirty);
- if (!ii->vfs_inode.i_nlink) {
+ if (!ii->vfs_inode.i_nlink || during_mount) {
/*
- * Defer calling iput() to avoid a deadlock
- * over I_SYNC flag for inodes with i_nlink == 0
+ * Defer calling iput() to avoid deadlocks if
+ * i_nlink == 0 or mount is not yet finished.
*/
list_add_tail(&ii->i_dirty, &sci->sc_iput_queue);
defer_iput = true;
!(marks_mask & FS_ISDIR & ~marks_ignored_mask))
return false;
- if (event_mask & marks_mask & ~marks_ignored_mask)
+ if (event_mask & FAN_ALL_OUTGOING_EVENTS & marks_mask &
+ ~marks_ignored_mask)
return true;
return false;
static inline int ocfs2_supports_append_dio(struct ocfs2_super *osb)
{
- if (osb->s_feature_ro_compat & OCFS2_FEATURE_RO_COMPAT_APPEND_DIO)
+ if (osb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_APPEND_DIO)
return 1;
return 0;
}
| OCFS2_FEATURE_INCOMPAT_INDEXED_DIRS \
| OCFS2_FEATURE_INCOMPAT_REFCOUNT_TREE \
| OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG \
- | OCFS2_FEATURE_INCOMPAT_CLUSTERINFO)
+ | OCFS2_FEATURE_INCOMPAT_CLUSTERINFO \
+ | OCFS2_FEATURE_INCOMPAT_APPEND_DIO)
#define OCFS2_FEATURE_RO_COMPAT_SUPP (OCFS2_FEATURE_RO_COMPAT_UNWRITTEN \
| OCFS2_FEATURE_RO_COMPAT_USRQUOTA \
- | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA \
- | OCFS2_FEATURE_RO_COMPAT_APPEND_DIO)
+ | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)
/*
* Heartbeat-only devices are missing journals and other files. The
*/
#define OCFS2_FEATURE_INCOMPAT_CLUSTERINFO 0x4000
+/*
+ * Append Direct IO support
+ */
+#define OCFS2_FEATURE_INCOMPAT_APPEND_DIO 0x8000
+
/*
* backup superblock flag is used to indicate that this volume
* has backup superblocks.
#define OCFS2_FEATURE_RO_COMPAT_USRQUOTA 0x0002
#define OCFS2_FEATURE_RO_COMPAT_GRPQUOTA 0x0004
-/*
- * Append Direct IO support
- */
-#define OCFS2_FEATURE_RO_COMPAT_APPEND_DIO 0x0008
/* The byte offset of the first backup block will be 1G.
* The following will be 4G, 16G, 64G, 256G and 1T.
{
struct ovl_fs *ufs = sb->s_fs_info;
- if (!(*flags & MS_RDONLY) &&
- (!ufs->upper_mnt || (ufs->upper_mnt->mnt_sb->s_flags & MS_RDONLY)))
+ if (!(*flags & MS_RDONLY) && !ufs->upper_mnt)
return -EROFS;
return 0;
break;
default:
+ pr_err("overlayfs: unrecognized mount option \"%s\" or missing value\n", p);
return -EINVAL;
}
}
+
+ /* Workdir is useless in non-upper mount */
+ if (!config->upperdir && config->workdir) {
+ pr_info("overlayfs: option \"workdir=%s\" is useless in a non-upper mount, ignore\n",
+ config->workdir);
+ kfree(config->workdir);
+ config->workdir = NULL;
+ }
+
return 0;
}
sb->s_stack_depth = 0;
if (ufs->config.upperdir) {
- /* FIXME: workdir is not needed for a R/O mount */
if (!ufs->config.workdir) {
pr_err("overlayfs: missing 'workdir'\n");
goto out_free_config;
if (err)
goto out_free_config;
+ /* Upper fs should not be r/o */
+ if (upperpath.mnt->mnt_sb->s_flags & MS_RDONLY) {
+ pr_err("overlayfs: upper fs is r/o, try multi-lower layers mount\n");
+ err = -EINVAL;
+ goto out_put_upperpath;
+ }
+
err = ovl_mount_dir(ufs->config.workdir, &workpath);
if (err)
goto out_put_upperpath;
err = -EINVAL;
stacklen = ovl_split_lowerdirs(lowertmp);
- if (stacklen > OVL_MAX_STACK)
+ if (stacklen > OVL_MAX_STACK) {
+ pr_err("overlayfs: too many lower directries, limit is %d\n",
+ OVL_MAX_STACK);
goto out_free_lowertmp;
+ } else if (!ufs->config.upperdir && stacklen == 1) {
+ pr_err("overlayfs: at least 2 lowerdir are needed while upperdir nonexistent\n");
+ goto out_free_lowertmp;
+ }
stack = kcalloc(stacklen, sizeof(struct path), GFP_KERNEL);
if (!stack)
ufs->numlower++;
}
- /* If the upper fs is r/o or nonexistent, we mark overlayfs r/o too */
- if (!ufs->upper_mnt || (ufs->upper_mnt->mnt_sb->s_flags & MS_RDONLY))
+ /* If the upper fs is nonexistent, we mark overlayfs r/o too */
+ if (!ufs->upper_mnt)
sb->s_flags |= MS_RDONLY;
sb->s_d_op = &ovl_dentry_operations;
static int pagemap_open(struct inode *inode, struct file *file)
{
+ /* do not disclose physical addresses: attack vector */
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
pr_warn_once("Bits 55-60 of /proc/PID/pagemap entries are about "
"to stop being page-shift some time soon. See the "
"linux/Documentation/vm/pagemap.txt for details.\n");
xfs_zero_last_block(
struct xfs_inode *ip,
xfs_fsize_t offset,
- xfs_fsize_t isize)
+ xfs_fsize_t isize,
+ bool *did_zeroing)
{
struct xfs_mount *mp = ip->i_mount;
xfs_fileoff_t last_fsb = XFS_B_TO_FSBT(mp, isize);
zero_len = mp->m_sb.sb_blocksize - zero_offset;
if (isize + zero_len > offset)
zero_len = offset - isize;
+ *did_zeroing = true;
return xfs_iozero(ip, isize, zero_len);
}
xfs_zero_eof(
struct xfs_inode *ip,
xfs_off_t offset, /* starting I/O offset */
- xfs_fsize_t isize) /* current inode size */
+ xfs_fsize_t isize, /* current inode size */
+ bool *did_zeroing)
{
struct xfs_mount *mp = ip->i_mount;
xfs_fileoff_t start_zero_fsb;
* We only zero a part of that block so it is handled specially.
*/
if (XFS_B_FSB_OFFSET(mp, isize) != 0) {
- error = xfs_zero_last_block(ip, offset, isize);
+ error = xfs_zero_last_block(ip, offset, isize, did_zeroing);
if (error)
return error;
}
if (error)
return error;
+ *did_zeroing = true;
start_zero_fsb = imap.br_startoff + imap.br_blockcount;
ASSERT(start_zero_fsb <= (end_zero_fsb + 1));
}
* having to redo all checks before.
*/
if (*pos > i_size_read(inode)) {
+ bool zero = false;
+
if (*iolock == XFS_IOLOCK_SHARED) {
xfs_rw_iunlock(ip, *iolock);
*iolock = XFS_IOLOCK_EXCL;
xfs_rw_ilock(ip, *iolock);
goto restart;
}
- error = xfs_zero_eof(ip, *pos, i_size_read(inode));
+ error = xfs_zero_eof(ip, *pos, i_size_read(inode), &zero);
if (error)
return error;
}
* Handle RENAME_EXCHANGE flags
*/
if (flags & RENAME_EXCHANGE) {
+ if (target_ip == NULL) {
+ error = -EINVAL;
+ goto error_return;
+ }
error = xfs_cross_rename(tp, src_dp, src_name, src_ip,
target_dp, target_name, target_ip,
&free_list, &first_block, spaceres);
XFS_PREALLOC_INVISIBLE = (1 << 4),
};
-int xfs_update_prealloc_flags(struct xfs_inode *,
- enum xfs_prealloc_flags);
-int xfs_zero_eof(struct xfs_inode *, xfs_off_t, xfs_fsize_t);
-int xfs_iozero(struct xfs_inode *, loff_t, size_t);
+int xfs_update_prealloc_flags(struct xfs_inode *ip,
+ enum xfs_prealloc_flags flags);
+int xfs_zero_eof(struct xfs_inode *ip, xfs_off_t offset,
+ xfs_fsize_t isize, bool *did_zeroing);
+int xfs_iozero(struct xfs_inode *ip, loff_t pos, size_t count);
#define IHOLD(ip) \
int error;
uint lock_flags = 0;
uint commit_flags = 0;
+ bool did_zeroing = false;
trace_xfs_setattr(ip);
return error;
/*
- * Now we can make the changes. Before we join the inode to the
- * transaction, take care of the part of the truncation that must be
- * done without the inode lock. This needs to be done before joining
- * the inode to the transaction, because the inode cannot be unlocked
- * once it is a part of the transaction.
+ * File data changes must be complete before we start the transaction to
+ * modify the inode. This needs to be done before joining the inode to
+ * the transaction because the inode cannot be unlocked once it is a
+ * part of the transaction.
+ *
+ * Start with zeroing any data block beyond EOF that we may expose on
+ * file extension.
*/
if (newsize > oldsize) {
- /*
- * Do the first part of growing a file: zero any data in the
- * last block that is beyond the old EOF. We need to do this
- * before the inode is joined to the transaction to modify
- * i_size.
- */
- error = xfs_zero_eof(ip, newsize, oldsize);
+ error = xfs_zero_eof(ip, newsize, oldsize, &did_zeroing);
if (error)
return error;
}
* any previous writes that are beyond the on disk EOF and the new
* EOF that have not been written out need to be written here. If we
* do not write the data out, we expose ourselves to the null files
- * problem.
- *
- * Only flush from the on disk size to the smaller of the in memory
- * file size or the new size as that's the range we really care about
- * here and prevents waiting for other data not within the range we
- * care about here.
+ * problem. Note that this includes any block zeroing we did above;
+ * otherwise those blocks may not be zeroed after a crash.
*/
- if (oldsize != ip->i_d.di_size && newsize > ip->i_d.di_size) {
+ if (newsize > ip->i_d.di_size &&
+ (oldsize != ip->i_d.di_size || did_zeroing)) {
error = filemap_write_and_wait_range(VFS_I(ip)->i_mapping,
ip->i_d.di_size, newsize);
if (error)
return error;
}
- /*
- * Wait for all direct I/O to complete.
- */
+ /* Now wait for all direct I/O to complete. */
inode_dio_wait(inode);
/*
tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE);
error = xfs_trans_reserve(tp, &M_RES(mp)->tr_ichange, 0, 0);
- if (error)
+ if (error) {
+ xfs_trans_cancel(tp, 0);
goto out_drop_iolock;
+ }
xfs_ilock(ip, XFS_ILOCK_EXCL);
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
*/
xfs_dqcheck(mp, ddq, id+j, type, XFS_QMOPT_DQREPAIR,
"xfs_quotacheck");
+ /*
+ * Reset type in case we are reusing group quota file for
+ * project quotas or vice versa
+ */
+ ddq->d_flags = type;
ddq->d_bcount = 0;
ddq->d_icount = 0;
ddq->d_rtbcount = 0;
unsigned scanned_preceeds_hole : 1;
unsigned allocated : 1;
unsigned long color;
- unsigned long start;
- unsigned long size;
+ u64 start;
+ u64 size;
struct drm_mm *mm;
};
unsigned int scan_check_range : 1;
unsigned scan_alignment;
unsigned long scan_color;
- unsigned long scan_size;
- unsigned long scan_hit_start;
- unsigned long scan_hit_end;
+ u64 scan_size;
+ u64 scan_hit_start;
+ u64 scan_hit_end;
unsigned scanned_blocks;
- unsigned long scan_start;
- unsigned long scan_end;
+ u64 scan_start;
+ u64 scan_end;
struct drm_mm_node *prev_scanned_node;
void (*color_adjust)(struct drm_mm_node *node, unsigned long color,
- unsigned long *start, unsigned long *end);
+ u64 *start, u64 *end);
};
/**
return mm->hole_stack.next;
}
-static inline unsigned long __drm_mm_hole_node_start(struct drm_mm_node *hole_node)
+static inline u64 __drm_mm_hole_node_start(struct drm_mm_node *hole_node)
{
return hole_node->start + hole_node->size;
}
* Returns:
* Start of the subsequent hole.
*/
-static inline unsigned long drm_mm_hole_node_start(struct drm_mm_node *hole_node)
+static inline u64 drm_mm_hole_node_start(struct drm_mm_node *hole_node)
{
BUG_ON(!hole_node->hole_follows);
return __drm_mm_hole_node_start(hole_node);
}
-static inline unsigned long __drm_mm_hole_node_end(struct drm_mm_node *hole_node)
+static inline u64 __drm_mm_hole_node_end(struct drm_mm_node *hole_node)
{
return list_entry(hole_node->node_list.next,
struct drm_mm_node, node_list)->start;
* Returns:
* End of the subsequent hole.
*/
-static inline unsigned long drm_mm_hole_node_end(struct drm_mm_node *hole_node)
+static inline u64 drm_mm_hole_node_end(struct drm_mm_node *hole_node)
{
return __drm_mm_hole_node_end(hole_node);
}
int drm_mm_insert_node_generic(struct drm_mm *mm,
struct drm_mm_node *node,
- unsigned long size,
+ u64 size,
unsigned alignment,
unsigned long color,
enum drm_mm_search_flags sflags,
*/
static inline int drm_mm_insert_node(struct drm_mm *mm,
struct drm_mm_node *node,
- unsigned long size,
+ u64 size,
unsigned alignment,
enum drm_mm_search_flags flags)
{
int drm_mm_insert_node_in_range_generic(struct drm_mm *mm,
struct drm_mm_node *node,
- unsigned long size,
+ u64 size,
unsigned alignment,
unsigned long color,
- unsigned long start,
- unsigned long end,
+ u64 start,
+ u64 end,
enum drm_mm_search_flags sflags,
enum drm_mm_allocator_flags aflags);
/**
*/
static inline int drm_mm_insert_node_in_range(struct drm_mm *mm,
struct drm_mm_node *node,
- unsigned long size,
+ u64 size,
unsigned alignment,
- unsigned long start,
- unsigned long end,
+ u64 start,
+ u64 end,
enum drm_mm_search_flags flags)
{
return drm_mm_insert_node_in_range_generic(mm, node, size, alignment,
void drm_mm_remove_node(struct drm_mm_node *node);
void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new);
void drm_mm_init(struct drm_mm *mm,
- unsigned long start,
- unsigned long size);
+ u64 start,
+ u64 size);
void drm_mm_takedown(struct drm_mm *mm);
bool drm_mm_clean(struct drm_mm *mm);
void drm_mm_init_scan(struct drm_mm *mm,
- unsigned long size,
+ u64 size,
unsigned alignment,
unsigned long color);
void drm_mm_init_scan_with_range(struct drm_mm *mm,
- unsigned long size,
+ u64 size,
unsigned alignment,
unsigned long color,
- unsigned long start,
- unsigned long end);
+ u64 start,
+ u64 end);
bool drm_mm_scan_add_block(struct drm_mm_node *node);
bool drm_mm_scan_remove_block(struct drm_mm_node *node);
INTEL_VGA_DEVICE((((gt) - 1) << 4) | (id), info)
#define _INTEL_BDW_M_IDS(gt, info) \
- _INTEL_BDW_M(gt, 0x1602, info), /* ULT */ \
+ _INTEL_BDW_M(gt, 0x1602, info), /* Halo */ \
_INTEL_BDW_M(gt, 0x1606, info), /* ULT */ \
- _INTEL_BDW_M(gt, 0x160B, info), /* Iris */ \
+ _INTEL_BDW_M(gt, 0x160B, info), /* ULT */ \
_INTEL_BDW_M(gt, 0x160E, info) /* ULX */
#define _INTEL_BDW_D_IDS(gt, info) \
* either of these locks held.
*/
- unsigned long offset;
+ uint64_t offset; /* GPU address space is independent of CPU word size */
uint32_t cur_placement;
struct sg_table *sg;
bool has_type;
bool use_type;
uint32_t flags;
- unsigned long gpu_offset;
+ uint64_t gpu_offset; /* GPU address space is independent of CPU word size */
uint64_t size;
uint32_t available_caching;
uint32_t default_caching;
#define PULL_DISABLE (1 << 3)
#define INPUT_EN (1 << 5)
-#define SLEWCTRL_FAST (1 << 6)
+#define SLEWCTRL_SLOW (1 << 6)
+#define SLEWCTRL_FAST 0
/* update macro depending on INPUT_EN and PULL_ENA */
#undef PIN_OUTPUT
#define PULL_DISABLE (1 << 16)
#define PULL_UP (1 << 17)
#define INPUT_EN (1 << 18)
-#define SLEWCTRL_FAST (1 << 19)
+#define SLEWCTRL_SLOW (1 << 19)
+#define SLEWCTRL_FAST 0
#define DS0_PULL_UP_DOWN_EN (1 << 27)
#define PIN_OUTPUT (PULL_DISABLE)
void (*sync_lr_elrsr)(struct kvm_vcpu *, int, struct vgic_lr);
u64 (*get_elrsr)(const struct kvm_vcpu *vcpu);
u64 (*get_eisr)(const struct kvm_vcpu *vcpu);
+ void (*clear_eisr)(struct kvm_vcpu *vcpu);
u32 (*get_interrupt_status)(const struct kvm_vcpu *vcpu);
void (*enable_underflow)(struct kvm_vcpu *vcpu);
void (*disable_underflow)(struct kvm_vcpu *vcpu);
*/
int clk_get_phase(struct clk *clk);
+/**
+ * clk_is_match - check if two clk's point to the same hardware clock
+ * @p: clk compared against q
+ * @q: clk compared against p
+ *
+ * Returns true if the two struct clk pointers both point to the same hardware
+ * clock node. Put differently, returns true if struct clk *p and struct clk *q
+ * share the same struct clk_core object.
+ *
+ * Returns false otherwise. Note that two NULL clks are treated as matching.
+ */
+bool clk_is_match(const struct clk *p, const struct clk *q);
+
#else
static inline long clk_get_accuracy(struct clk *clk)
return -ENOTSUPP;
}
+static inline bool clk_is_match(const struct clk *p, const struct clk *q)
+{
+ return p == q;
+}
+
#endif
/**
#ifdef CONFIG_CPU_IDLE
extern void disable_cpuidle(void);
+extern bool cpuidle_not_available(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev);
extern int cpuidle_select(struct cpuidle_driver *drv,
struct cpuidle_device *dev);
extern int cpuidle_enable_device(struct cpuidle_device *dev);
extern void cpuidle_disable_device(struct cpuidle_device *dev);
extern int cpuidle_play_dead(void);
-extern void cpuidle_enter_freeze(void);
+extern int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev);
+extern int cpuidle_enter_freeze(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev);
extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev);
#else
static inline void disable_cpuidle(void) { }
+static inline bool cpuidle_not_available(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev)
+{return true; }
static inline int cpuidle_select(struct cpuidle_driver *drv,
struct cpuidle_device *dev)
{return -ENODEV; }
{return -ENODEV; }
static inline void cpuidle_disable_device(struct cpuidle_device *dev) { }
static inline int cpuidle_play_dead(void) {return -ENODEV; }
-static inline void cpuidle_enter_freeze(void) { }
+static inline int cpuidle_find_deepest_state(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev)
+{return -ENODEV; }
+static inline int cpuidle_enter_freeze(struct cpuidle_driver *drv,
+ struct cpuidle_device *dev)
+{return -ENODEV; }
static inline struct cpuidle_driver *cpuidle_get_cpu_driver(
struct cpuidle_device *dev) {return NULL; }
#endif
*/
struct mapped_device *dm_get_md(dev_t dev);
void dm_get(struct mapped_device *md);
+int dm_hold(struct mapped_device *md);
void dm_put(struct mapped_device *md);
/*
struct mutex i_mutex;
unsigned long dirtied_when; /* jiffies of first dirtying */
+ unsigned long dirtied_time_when;
struct hlist_node i_hash;
struct list_head i_wb_list; /* backing dev IO list */
* @units: Measurment unit for this attribute.
* @unit_expo: Exponent used in the data.
* @size: Size in bytes for data size.
+ * @logical_minimum: Logical minimum value for this attribute.
+ * @logical_maximum: Logical maximum value for this attribute.
*/
struct hid_sensor_hub_attribute_info {
u32 usage_id;
/**
* sensor_hub_input_attr_get_raw_value() - Synchronous read request
+* @hsdev: Hub device instance.
* @usage_id: Attribute usage id of parent physical device as per spec
* @attr_usage_id: Attribute usage id as per spec
* @report_id: Report id to look for
u32 attr_usage_id, u32 report_id);
/**
* sensor_hub_set_feature() - Feature set request
+* @hsdev: Hub device instance.
* @report_id: Report id to look for
* @field_index: Field index inside a report
* @value: Value to set
/**
* sensor_hub_get_feature() - Feature get request
+* @hsdev: Hub device instance.
* @report_id: Report id to look for
* @field_index: Field index inside a report
* @value: Place holder for return value
* IRQF_ONESHOT - Interrupt is not reenabled after the hardirq handler finished.
* Used by threaded interrupts which need to keep the
* irq line disabled until the threaded handler has been run.
- * IRQF_NO_SUSPEND - Do not disable this IRQ during suspend
+ * IRQF_NO_SUSPEND - Do not disable this IRQ during suspend. Does not guarantee
+ * that this interrupt will wake the system from a suspended
+ * state. See Documentation/power/suspend-and-interrupts.txt
* IRQF_FORCE_RESUME - Force enable it on resume even if IRQF_NO_SUSPEND is set
* IRQF_NO_THREAD - Interrupt cannot be threaded
* IRQF_EARLY_RESUME - Resume IRQ early during syscore instead of at device
* resume time.
+ * IRQF_COND_SUSPEND - If the IRQ is shared with a NO_SUSPEND user, execute this
+ * interrupt handler after suspending interrupts. For system
+ * wakeup devices users need to implement wakeup detection in
+ * their interrupt handlers.
*/
#define IRQF_DISABLED 0x00000020
#define IRQF_SHARED 0x00000080
#define IRQF_FORCE_RESUME 0x00008000
#define IRQF_NO_THREAD 0x00010000
#define IRQF_EARLY_RESUME 0x00020000
+#define IRQF_COND_SUSPEND 0x00040000
#define IRQF_TIMER (__IRQF_TIMER | IRQF_NO_SUSPEND | IRQF_NO_THREAD)
#define GICR_PROPBASER_WaWb (5U << 7)
#define GICR_PROPBASER_RaWaWt (6U << 7)
#define GICR_PROPBASER_RaWaWb (7U << 7)
+#define GICR_PROPBASER_CACHEABILITY_MASK (7U << 7)
#define GICR_PROPBASER_IDBITS_MASK (0x1f)
+#define GICR_PENDBASER_NonShareable (0U << 10)
+#define GICR_PENDBASER_InnerShareable (1U << 10)
+#define GICR_PENDBASER_OuterShareable (2U << 10)
+#define GICR_PENDBASER_SHAREABILITY_MASK (3UL << 10)
+#define GICR_PENDBASER_nCnB (0U << 7)
+#define GICR_PENDBASER_nC (1U << 7)
+#define GICR_PENDBASER_RaWt (2U << 7)
+#define GICR_PENDBASER_RaWb (3U << 7)
+#define GICR_PENDBASER_WaWt (4U << 7)
+#define GICR_PENDBASER_WaWb (5U << 7)
+#define GICR_PENDBASER_RaWaWt (6U << 7)
+#define GICR_PENDBASER_RaWaWb (7U << 7)
+#define GICR_PENDBASER_CACHEABILITY_MASK (7U << 7)
+
/*
* Re-Distributor registers, offsets from SGI_base
*/
#define GITS_TRANSLATER 0x10040
+#define GITS_CTLR_ENABLE (1U << 0)
+#define GITS_CTLR_QUIESCENT (1U << 31)
+
+#define GITS_TYPER_DEVBITS_SHIFT 13
+#define GITS_TYPER_DEVBITS(r) ((((r) >> GITS_TYPER_DEVBITS_SHIFT) & 0x1f) + 1)
#define GITS_TYPER_PTA (1UL << 19)
#define GITS_CBASER_VALID (1UL << 63)
#define GITS_CBASER_WaWb (5UL << 59)
#define GITS_CBASER_RaWaWt (6UL << 59)
#define GITS_CBASER_RaWaWb (7UL << 59)
+#define GITS_CBASER_CACHEABILITY_MASK (7UL << 59)
#define GITS_CBASER_NonShareable (0UL << 10)
#define GITS_CBASER_InnerShareable (1UL << 10)
#define GITS_CBASER_OuterShareable (2UL << 10)
#define GITS_BASER_WaWb (5UL << 59)
#define GITS_BASER_RaWaWt (6UL << 59)
#define GITS_BASER_RaWaWb (7UL << 59)
+#define GITS_BASER_CACHEABILITY_MASK (7UL << 59)
#define GITS_BASER_TYPE_SHIFT (56)
#define GITS_BASER_TYPE(r) (((r) >> GITS_BASER_TYPE_SHIFT) & 7)
#define GITS_BASER_ENTRY_SIZE_SHIFT (48)
#ifdef CONFIG_PM_SLEEP
unsigned int nr_actions;
unsigned int no_suspend_depth;
+ unsigned int cond_suspend_depth;
unsigned int force_resume_depth;
#endif
#ifdef CONFIG_PROC_FS
struct kmem_cache;
struct page;
+struct vm_struct;
#ifdef CONFIG_KASAN
void kasan_slab_alloc(struct kmem_cache *s, void *object);
void kasan_slab_free(struct kmem_cache *s, void *object);
-#define MODULE_ALIGN (PAGE_SIZE << KASAN_SHADOW_SCALE_SHIFT)
-
int kasan_module_alloc(void *addr, size_t size);
-void kasan_module_free(void *addr);
+void kasan_free_shadow(const struct vm_struct *vm);
#else /* CONFIG_KASAN */
-#define MODULE_ALIGN 1
-
static inline void kasan_unpoison_shadow(const void *address, size_t size) {}
static inline void kasan_enable_current(void) {}
static inline void kasan_slab_free(struct kmem_cache *s, void *object) {}
static inline int kasan_module_alloc(void *addr, size_t size) { return 0; }
-static inline void kasan_module_free(void *addr) {}
+static inline void kasan_free_shadow(const struct vm_struct *vm) {}
#endif /* CONFIG_KASAN */
#include <linux/compiler.h>
unsigned long lcm(unsigned long a, unsigned long b) __attribute_const__;
+unsigned long lcm_not_zero(unsigned long a, unsigned long b) __attribute_const__;
#endif /* _LCM_H */
* led */
ATA_FLAG_NO_DIPM = (1 << 23), /* host not happy with DIPM */
ATA_FLAG_LOWTAG = (1 << 24), /* host wants lowest available tag */
+ ATA_FLAG_SAS_HOST = (1 << 25), /* SAS host */
/* bits 24:31 of ap->flags are reserved for LLD specific flags */
#define PALMAS_GPADC_TRIM15 0x0E
#define PALMAS_GPADC_TRIM16 0x0F
+/* TPS659038 regen2_ctrl offset iss different from palmas */
+#define TPS659038_REGEN2_CTRL 0x12
+
/* TPS65917 Interrupt registers */
/* Registers for function INTERRUPT */
enum mlx4_update_qp_attr {
MLX4_UPDATE_QP_SMAC = 1 << 0,
- MLX4_UPDATE_QP_VSD = 1 << 2,
+ MLX4_UPDATE_QP_VSD = 1 << 1,
MLX4_UPDATE_QP_SUPPORTED_ATTRS = (1 << 2) - 1
};
unsigned long *ftrace_callsites;
#endif
+#ifdef CONFIG_LIVEPATCH
+ bool klp_alive;
+#endif
+
#ifdef CONFIG_MODULE_UNLOAD
/* What modules depend on me? */
struct list_head source_list;
/* Any cleanup before freeing mod->module_init */
void module_arch_freeing_init(struct module *mod);
+
+#ifdef CONFIG_KASAN
+#include <linux/kasan.h>
+#define MODULE_ALIGN (PAGE_SIZE << KASAN_SHADOW_SCALE_SHIFT)
+#else
+#define MODULE_ALIGN PAGE_SIZE
+#endif
+
#endif
* Used to add FDB entries to dump requests. Implementers should add
* entries to skb and update idx with the number of entries.
*
- * int (*ndo_bridge_setlink)(struct net_device *dev, struct nlmsghdr *nlh)
+ * int (*ndo_bridge_setlink)(struct net_device *dev, struct nlmsghdr *nlh,
+ * u16 flags)
* int (*ndo_bridge_getlink)(struct sk_buff *skb, u32 pid, u32 seq,
* struct net_device *dev, u32 filter_mask)
+ * int (*ndo_bridge_dellink)(struct net_device *dev, struct nlmsghdr *nlh,
+ * u16 flags);
*
* int (*ndo_change_carrier)(struct net_device *dev, bool new_carrier);
* Called to change device carrier. Soft-devices (like dummy, team, etc)
void synchronize_net(void);
int init_dummy_netdev(struct net_device *dev);
+DECLARE_PER_CPU(int, xmit_recursion);
+static inline int dev_recursion_level(void)
+{
+ return this_cpu_read(xmit_recursion);
+}
+
struct net_device *dev_get_by_index(struct net *net, int ifindex);
struct net_device *__dev_get_by_index(struct net *net, int ifindex);
struct net_device *dev_get_by_index_rcu(struct net *net, int ifindex);
static inline void skb_gro_remcsum_init(struct gro_remcsum *grc)
{
+ grc->offset = 0;
grc->delta = 0;
}
extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *);
extern int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr);
extern int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fattr);
+extern int nfs_post_op_update_inode_force_wcc_locked(struct inode *inode, struct nfs_fattr *fattr);
extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
extern void nfs_access_add_cache(struct inode *, struct nfs_access_entry *);
extern void nfs_access_set_mask(struct nfs_access_entry *, u32);
extern int nfs_revalidate_inode_rcu(struct nfs_server *server, struct inode *inode);
extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *);
extern int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping);
+extern int nfs_revalidate_mapping_protected(struct inode *inode, struct address_space *mapping);
extern int nfs_setattr(struct dentry *, struct iattr *);
-extern void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr);
+extern void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr, struct nfs_fattr *);
extern void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr,
struct nfs4_label *label);
extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx);
extern void nfs_put_lock_context(struct nfs_lock_context *l_ctx);
extern u64 nfs_compat_user_ino64(u64 fileid);
extern void nfs_fattr_init(struct nfs_fattr *fattr);
+extern void nfs_fattr_set_barrier(struct nfs_fattr *fattr);
extern unsigned long nfs_inc_attr_generation_counter(void);
extern struct nfs_fattr *nfs_alloc_fattr(void);
static inline void of_platform_depopulate(struct device *parent) { }
#endif
-#ifdef CONFIG_OF_DYNAMIC
+#if defined(CONFIG_OF_DYNAMIC) && defined(CONFIG_OF_ADDRESS)
extern void of_platform_register_reconfig_notifier(void);
#else
static inline void of_platform_register_reconfig_notifier(void) { }
static inline struct pinctrl * __must_check pinctrl_get(struct device *dev)
{
- return ERR_PTR(-ENOSYS);
+ return NULL;
}
static inline void pinctrl_put(struct pinctrl *p)
struct pinctrl *p,
const char *name)
{
- return ERR_PTR(-ENOSYS);
+ return NULL;
}
static inline int pinctrl_select_state(struct pinctrl *p,
static inline struct pinctrl * __must_check devm_pinctrl_get(struct device *dev)
{
- return ERR_PTR(-ENOSYS);
+ return NULL;
}
static inline void devm_pinctrl_put(struct pinctrl *p)
* @driver_data: private regulator data
* @of_node: OpenFirmware node to parse for device tree bindings (may be
* NULL).
- * @regmap: regmap to use for core regmap helpers if dev_get_regulator() is
+ * @regmap: regmap to use for core regmap helpers if dev_get_regmap() is
* insufficient.
* @ena_gpio_initialized: GPIO controlling regulator enable was properly
* initialized, meaning that >= 0 is a valid gpio
* @buckets: size * hash buckets
*/
struct bucket_table {
- size_t size;
- unsigned int locks_mask;
- spinlock_t *locks;
- struct rhash_head __rcu *buckets[];
+ size_t size;
+ unsigned int locks_mask;
+ spinlock_t *locks;
+
+ struct rhash_head __rcu *buckets[] ____cacheline_aligned_in_smp;
};
typedef u32 (*rht_hashfn_t)(const void *data, u32 len, u32 seed);
* @locks_mul: Number of bucket locks to allocate per cpu (default: 128)
* @hashfn: Function to hash key
* @obj_hashfn: Function to hash object
- * @grow_decision: If defined, may return true if table should expand
- * @shrink_decision: If defined, may return true if table should shrink
- *
- * Note: when implementing the grow and shrink decision function, min/max
- * shift must be enforced, otherwise, resizing watermarks they set may be
- * useless.
*/
struct rhashtable_params {
size_t nelem_hint;
size_t locks_mul;
rht_hashfn_t hashfn;
rht_obj_hashfn_t obj_hashfn;
- bool (*grow_decision)(const struct rhashtable *ht,
- size_t new_size);
- bool (*shrink_decision)(const struct rhashtable *ht,
- size_t new_size);
};
/**
void rhashtable_insert(struct rhashtable *ht, struct rhash_head *node);
bool rhashtable_remove(struct rhashtable *ht, struct rhash_head *node);
-bool rht_grow_above_75(const struct rhashtable *ht, size_t new_size);
-bool rht_shrink_below_30(const struct rhashtable *ht, size_t new_size);
-
int rhashtable_expand(struct rhashtable *ht);
int rhashtable_shrink(struct rhashtable *ht);
/*
* numa_faults_locality tracks if faults recorded during the last
- * scan window were remote/local. The task scan period is adapted
- * based on the locality of the faults with different weights
- * depending on whether they were shared or private faults
+ * scan window were remote/local or failed to migrate. The task scan
+ * period is adapted based on the locality of the faults with different
+ * weights depending on whether they were shared or private faults
*/
- unsigned long numa_faults_locality[2];
+ unsigned long numa_faults_locality[3];
unsigned long numa_pages_migrated;
#endif /* CONFIG_NUMA_BALANCING */
#define TNF_NO_GROUP 0x02
#define TNF_SHARED 0x04
#define TNF_FAULT_LOCAL 0x08
+#define TNF_MIGRATE_FAIL 0x10
#ifdef CONFIG_NUMA_BALANCING
extern void task_numa_fault(int last_node, int node, int pages, int flags);
unsigned char iotype; /* io access style */
unsigned char unused1;
-#define UPIO_PORT (0) /* 8b I/O port access */
-#define UPIO_HUB6 (1) /* Hub6 ISA card */
-#define UPIO_MEM (2) /* 8b MMIO access */
-#define UPIO_MEM32 (3) /* 32b little endian */
-#define UPIO_MEM32BE (4) /* 32b big endian */
-#define UPIO_AU (5) /* Au1x00 and RT288x type IO */
-#define UPIO_TSI (6) /* Tsi108/109 type IO */
+#define UPIO_PORT (SERIAL_IO_PORT) /* 8b I/O port access */
+#define UPIO_HUB6 (SERIAL_IO_HUB6) /* Hub6 ISA card */
+#define UPIO_MEM (SERIAL_IO_MEM) /* 8b MMIO access */
+#define UPIO_MEM32 (SERIAL_IO_MEM32) /* 32b little endian */
+#define UPIO_AU (SERIAL_IO_AU) /* Au1x00 and RT288x type IO */
+#define UPIO_TSI (SERIAL_IO_TSI) /* Tsi108/109 type IO */
+#define UPIO_MEM32BE (SERIAL_IO_MEM32BE) /* 32b big endian */
unsigned int read_status_mask; /* driver specific */
unsigned int ignore_status_mask; /* driver specific */
to->l4_hash = from->l4_hash;
};
+static inline void skb_sender_cpu_clear(struct sk_buff *skb)
+{
+#ifdef CONFIG_XPS
+ skb->sender_cpu = 0;
+#endif
+}
+
#ifdef NET_SKBUFF_DATA_USES_OFFSET
static inline unsigned char *skb_end_pointer(const struct sk_buff *skb)
{
* sequence completes. On some systems, many such sequences can execute as
* as single programmed DMA transfer. On all systems, these messages are
* queued, and might complete after transactions to other devices. Messages
- * sent to a given spi_device are alway executed in FIFO order.
+ * sent to a given spi_device are always executed in FIFO order.
*
* The code that submits an spi_message (and its spi_transfers)
* to the lower layers is responsible for managing its memory.
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
void rpc_register_sysctl(void);
void rpc_unregister_sysctl(void);
-int sunrpc_debugfs_init(void);
+void sunrpc_debugfs_init(void);
void sunrpc_debugfs_exit(void);
-int rpc_clnt_debugfs_register(struct rpc_clnt *);
+void rpc_clnt_debugfs_register(struct rpc_clnt *);
void rpc_clnt_debugfs_unregister(struct rpc_clnt *);
-int rpc_xprt_debugfs_register(struct rpc_xprt *);
+void rpc_xprt_debugfs_register(struct rpc_xprt *);
void rpc_xprt_debugfs_unregister(struct rpc_xprt *);
#else
-static inline int
+static inline void
sunrpc_debugfs_init(void)
{
- return 0;
+ return;
}
static inline void
return;
}
-static inline int
+static inline void
rpc_clnt_debugfs_register(struct rpc_clnt *clnt)
{
- return 0;
+ return;
}
static inline void
return;
}
-static inline int
+static inline void
rpc_xprt_debugfs_register(struct rpc_xprt *xprt)
{
- return 0;
+ return;
}
static inline void
}
#endif
+
+#if IS_ENABLED(CONFIG_THERMAL)
struct thermal_zone_device *thermal_zone_device_register(const char *, int, int,
void *, struct thermal_zone_device_ops *,
const struct thermal_zone_params *, int, int);
struct thermal_cooling_device *, int);
void thermal_cdev_update(struct thermal_cooling_device *);
void thermal_notify_framework(struct thermal_zone_device *, int);
-
-#ifdef CONFIG_NET
+#else
+static inline struct thermal_zone_device *thermal_zone_device_register(
+ const char *type, int trips, int mask, void *devdata,
+ struct thermal_zone_device_ops *ops,
+ const struct thermal_zone_params *tzp,
+ int passive_delay, int polling_delay)
+{ return ERR_PTR(-ENODEV); }
+static inline void thermal_zone_device_unregister(
+ struct thermal_zone_device *tz)
+{ }
+static inline int thermal_zone_bind_cooling_device(
+ struct thermal_zone_device *tz, int trip,
+ struct thermal_cooling_device *cdev,
+ unsigned long upper, unsigned long lower)
+{ return -ENODEV; }
+static inline int thermal_zone_unbind_cooling_device(
+ struct thermal_zone_device *tz, int trip,
+ struct thermal_cooling_device *cdev)
+{ return -ENODEV; }
+static inline void thermal_zone_device_update(struct thermal_zone_device *tz)
+{ }
+static inline struct thermal_cooling_device *
+thermal_cooling_device_register(char *type, void *devdata,
+ const struct thermal_cooling_device_ops *ops)
+{ return ERR_PTR(-ENODEV); }
+static inline struct thermal_cooling_device *
+thermal_of_cooling_device_register(struct device_node *np,
+ char *type, void *devdata, const struct thermal_cooling_device_ops *ops)
+{ return ERR_PTR(-ENODEV); }
+static inline void thermal_cooling_device_unregister(
+ struct thermal_cooling_device *cdev)
+{ }
+static inline struct thermal_zone_device *thermal_zone_get_zone_by_name(
+ const char *name)
+{ return ERR_PTR(-ENODEV); }
+static inline int thermal_zone_get_temp(
+ struct thermal_zone_device *tz, unsigned long *temp)
+{ return -ENODEV; }
+static inline int get_tz_trend(struct thermal_zone_device *tz, int trip)
+{ return -ENODEV; }
+static inline struct thermal_instance *
+get_thermal_instance(struct thermal_zone_device *tz,
+ struct thermal_cooling_device *cdev, int trip)
+{ return ERR_PTR(-ENODEV); }
+static inline void thermal_cdev_update(struct thermal_cooling_device *cdev)
+{ }
+static inline void thermal_notify_framework(struct thermal_zone_device *tz,
+ int trip)
+{ }
+#endif /* CONFIG_THERMAL */
+
+#if defined(CONFIG_NET) && IS_ENABLED(CONFIG_THERMAL)
extern int thermal_generate_netlink_event(struct thermal_zone_device *tz,
enum events event);
#else
size_t maxsize, size_t *start);
int iov_iter_npages(const struct iov_iter *i, int maxpages);
+const void *dup_iter(struct iov_iter *new, struct iov_iter *old, gfp_t flags);
+
static inline size_t iov_iter_count(struct iov_iter *i)
{
return i->count;
* @num_ports: the number of different ports this device will have.
* @bulk_in_size: minimum number of bytes to allocate for bulk-in buffer
* (0 = end-point size)
- * @bulk_out_size: minimum number of bytes to allocate for bulk-out buffer
- * (0 = end-point size)
+ * @bulk_out_size: bytes to allocate for bulk-out buffer (0 = end-point size)
* @calc_num_ports: pointer to a function to determine how many ports this
* device has dynamically. It will be called after the probe()
* callback is called, but before attach()
struct urb *urb;
struct usbnet *dev;
enum skb_state state;
- size_t length;
+ long length;
+ unsigned long packets;
};
+/* Drivers that set FLAG_MULTI_PACKET must call this in their
+ * tx_fixup method before returning an skb.
+ */
+static inline void
+usbnet_set_skb_tx_stats(struct sk_buff *skb,
+ unsigned long packets, long bytes_delta)
+{
+ struct skb_data *entry = (struct skb_data *) skb->cb;
+
+ entry->packets = packets;
+ entry->length = bytes_delta;
+}
+
extern int usbnet_open(struct net_device *net);
extern int usbnet_stop(struct net_device *net);
extern netdev_tx_t usbnet_start_xmit(struct sk_buff *skb,
#define VM_VPAGES 0x00000010 /* buffer for pages was vmalloc'ed */
#define VM_UNINITIALIZED 0x00000020 /* vm_struct is not fully initialized */
#define VM_NO_GUARD 0x00000040 /* don't add guard page */
+#define VM_KASAN 0x00000080 /* has allocated kasan shadow memory */
/* bits [20..32] reserved for arch specific ioremap internals */
/*
/* data contains off-queue information when !WORK_STRUCT_PWQ */
WORK_OFFQ_FLAG_BASE = WORK_STRUCT_COLOR_SHIFT,
- WORK_OFFQ_CANCELING = (1 << WORK_OFFQ_FLAG_BASE),
+ __WORK_OFFQ_CANCELING = WORK_OFFQ_FLAG_BASE,
+ WORK_OFFQ_CANCELING = (1 << __WORK_OFFQ_CANCELING),
/*
* When a work item is off queue, its high bits point to the last
extern unsigned long vm_dirty_bytes;
extern unsigned int dirty_writeback_interval;
extern unsigned int dirty_expire_interval;
+extern unsigned int dirtytime_expire_interval;
extern int vm_highmem_is_dirtyable;
extern int block_dump;
extern int laptop_mode;
extern int dirty_bytes_handler(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp,
loff_t *ppos);
+int dirtytime_interval_handler(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos);
struct ctl_table;
int dirty_writeback_centisecs_handler(struct ctl_table *, int,
* @return Checksum of buffer.
*/
-u16 cfpkt_iterate(struct cfpkt *pkt,
+int cfpkt_iterate(struct cfpkt *pkt,
u16 (*iter_func)(u16 chks, void *buf, u16 len),
u16 data);
enum {
XFRM_LOOKUP_ICMP = 1 << 0,
XFRM_LOOKUP_QUEUE = 1 << 1,
+ XFRM_LOOKUP_KEEP_DST_REF = 1 << 2,
};
struct flowi;
#endif
-static inline int sk_mc_loop(struct sock *sk)
-{
- if (!sk)
- return 1;
- switch (sk->sk_family) {
- case AF_INET:
- return inet_sk(sk)->mc_loop;
-#if IS_ENABLED(CONFIG_IPV6)
- case AF_INET6:
- return inet6_sk(sk)->mc_loop;
-#endif
- }
- WARN_ON(1);
- return 1;
-}
-
bool ip_call_ra_chain(struct sk_buff *skb);
/*
static inline int ip6_skb_dst_mtu(struct sk_buff *skb)
{
- struct ipv6_pinfo *np = skb->sk ? inet6_sk(skb->sk) : NULL;
+ struct ipv6_pinfo *np = skb->sk && !dev_recursion_level() ?
+ inet6_sk(skb->sk) : NULL;
return (np && np->pmtudisc >= IPV6_PMTUDISC_PROBE) ?
skb_dst(skb)->dev->mtu : dst_mtu(skb_dst(skb));
const struct nf_loginfo *li,
const char *fmt, ...);
+__printf(8, 9)
+void nf_log_trace(struct net *net,
+ u_int8_t pf,
+ unsigned int hooknum,
+ const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const struct nf_loginfo *li,
+ const char *fmt, ...);
+
struct nf_log_buf;
struct nf_log_buf *nf_log_buf_open(void);
const struct nft_data *data,
enum nft_data_types type);
+
+/**
+ * struct nft_userdata - user defined data associated with an object
+ *
+ * @len: length of the data
+ * @data: content
+ *
+ * The presence of user data is indicated in an object specific fashion,
+ * so a length of zero can't occur and the value "len" indicates data
+ * of length len + 1.
+ */
+struct nft_userdata {
+ u8 len;
+ unsigned char data[0];
+};
+
/**
* struct nft_set_elem - generic representation of set elements
*
* @handle: rule handle
* @genmask: generation mask
* @dlen: length of expression data
- * @ulen: length of user data (used for comments)
+ * @udata: user data is appended to the rule
* @data: expression data
*/
struct nft_rule {
u64 handle:42,
genmask:2,
dlen:12,
- ulen:8;
+ udata:1;
unsigned char data[]
__attribute__((aligned(__alignof__(struct nft_expr))));
};
return (struct nft_expr *)&rule->data[rule->dlen];
}
-static inline void *nft_userdata(const struct nft_rule *rule)
+static inline struct nft_userdata *nft_userdata(const struct nft_rule *rule)
{
return (void *)&rule->data[rule->dlen];
}
struct dst_entry *sk_dst_check(struct sock *sk, u32 cookie);
+bool sk_mc_loop(struct sock *sk);
+
static inline bool sk_can_gso(const struct sock *sk)
{
return net_gso_ok(sk->sk_route_caps, sk->sk_gso_type);
#define VXLAN_N_VID (1u << 24)
#define VXLAN_VID_MASK (VXLAN_N_VID - 1)
+#define VXLAN_VNI_MASK (VXLAN_VID_MASK << 8)
#define VXLAN_HLEN (sizeof(struct udphdr) + sizeof(struct vxlanhdr))
struct vxlan_metadata {
#define AT91_DDRSDRC_UPD_MR (3 << 20) /* Update load mode register and extended mode register */
#define AT91_DDRSDRC_MDR 0x20 /* Memory Device Register */
-#define AT91_DDRSDRC_MD (3 << 0) /* Memory Device Type */
+#define AT91_DDRSDRC_MD (7 << 0) /* Memory Device Type */
#define AT91_DDRSDRC_MD_SDR 0
#define AT91_DDRSDRC_MD_LOW_POWER_SDR 1
#define AT91_DDRSDRC_MD_LOW_POWER_DDR 3
return snd_pcm_format_physical_width(params_format(p));
}
+static inline void
+params_set_format(struct snd_pcm_hw_params *p, snd_pcm_format_t fmt)
+{
+ snd_mask_set(hw_param_mask(p, SNDRV_PCM_HW_PARAM_FORMAT),
+ (__force int)fmt);
+}
+
#endif /* __SOUND_PCM_PARAMS_H */
struct rt5670_platform_data {
int jd_mode;
bool in2_diff;
+ bool dev_gpio;
bool dmic_en;
unsigned int dmic1_data_pin;
int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
struct snd_soc_dapm_widget_list **list);
-struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol);
struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm(
struct snd_kcontrol *kcontrol);
/* DAPM context */
struct snd_soc_dapm_context {
enum snd_soc_bias_level bias_level;
- enum snd_soc_bias_level suspend_bias_level;
- struct delayed_work delayed_work;
unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */
/* Go to BIAS_OFF in suspend if the DAPM context is idle */
unsigned int suspend_bias_off:1;
/* internal use only */
int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute);
-int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd);
+void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd);
int soc_dpcm_runtime_update(struct snd_soc_card *);
int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
struct snd_soc_dai *dai);
/* Jack reporting */
-int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type,
- struct snd_soc_jack *jack);
+int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type,
+ struct snd_soc_jack *jack, struct snd_soc_jack_pin *pins,
+ unsigned int num_pins);
+
void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask);
int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count,
struct snd_soc_jack_pin *pins);
struct snd_soc_jack {
struct mutex mutex;
struct snd_jack *jack;
- struct snd_soc_codec *codec;
+ struct snd_soc_card *card;
struct list_head pins;
int status;
struct blocking_notifier_head notifier;
unsigned int symmetric_channels:1;
unsigned int symmetric_samplebits:1;
+ /* Mark this pcm with non atomic ops */
+ bool nonatomic;
+
/* Do not create a PCM for this DAI link (Backend link) */
unsigned int no_pcm:1;
/*
* Card-specific routes and widgets.
+ * Note: of_dapm_xxx for Device Tree; Otherwise for driver build-in.
*/
const struct snd_soc_dapm_widget *dapm_widgets;
int num_dapm_widgets;
const struct snd_soc_dapm_route *dapm_routes;
int num_dapm_routes;
+ const struct snd_soc_dapm_widget *of_dapm_widgets;
+ int num_of_dapm_widgets;
+ const struct snd_soc_dapm_route *of_dapm_routes;
+ int num_of_dapm_routes;
bool fully_routed;
struct work_struct deferred_resume_work;
return component->dapm_ptr;
}
+/**
+ * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol
+ * @kcontrol: The kcontrol
+ *
+ * This function must only be used on DAPM contexts that are known to be part of
+ * a CODEC (e.g. in a CODEC driver). Otherwise the behavior is undefined.
+ */
+static inline struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(
+ struct snd_kcontrol *kcontrol)
+{
+ return snd_soc_dapm_to_codec(snd_soc_dapm_kcontrol_dapm(kcontrol));
+}
+
/* codec IO */
unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg);
int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg,
void target_core_setup_sub_cits(struct se_subsystem_api *);
/* attribute helpers from target_core_device.c for backend drivers */
+bool se_dev_check_wce(struct se_device *);
int se_dev_set_max_unmap_lba_count(struct se_device *, u32);
int se_dev_set_max_unmap_block_desc_count(struct se_device *, u32);
int se_dev_set_unmap_granularity(struct se_device *, u32);
#include <linux/ktime.h>
#include <linux/tracepoint.h>
-struct device;
-struct regmap;
+#include "../../../drivers/base/regmap/internal.h"
/*
* Log register events
*/
DECLARE_EVENT_CLASS(regmap_reg,
- TP_PROTO(struct device *dev, unsigned int reg,
+ TP_PROTO(struct regmap *map, unsigned int reg,
unsigned int val),
- TP_ARGS(dev, reg, val),
+ TP_ARGS(map, reg, val),
TP_STRUCT__entry(
- __string( name, dev_name(dev) )
- __field( unsigned int, reg )
- __field( unsigned int, val )
+ __string( name, regmap_name(map) )
+ __field( unsigned int, reg )
+ __field( unsigned int, val )
),
TP_fast_assign(
- __assign_str(name, dev_name(dev));
+ __assign_str(name, regmap_name(map));
__entry->reg = reg;
__entry->val = val;
),
DEFINE_EVENT(regmap_reg, regmap_reg_write,
- TP_PROTO(struct device *dev, unsigned int reg,
+ TP_PROTO(struct regmap *map, unsigned int reg,
unsigned int val),
- TP_ARGS(dev, reg, val)
+ TP_ARGS(map, reg, val)
);
DEFINE_EVENT(regmap_reg, regmap_reg_read,
- TP_PROTO(struct device *dev, unsigned int reg,
+ TP_PROTO(struct regmap *map, unsigned int reg,
unsigned int val),
- TP_ARGS(dev, reg, val)
+ TP_ARGS(map, reg, val)
);
DEFINE_EVENT(regmap_reg, regmap_reg_read_cache,
- TP_PROTO(struct device *dev, unsigned int reg,
+ TP_PROTO(struct regmap *map, unsigned int reg,
unsigned int val),
- TP_ARGS(dev, reg, val)
+ TP_ARGS(map, reg, val)
);
DECLARE_EVENT_CLASS(regmap_block,
- TP_PROTO(struct device *dev, unsigned int reg, int count),
+ TP_PROTO(struct regmap *map, unsigned int reg, int count),
- TP_ARGS(dev, reg, count),
+ TP_ARGS(map, reg, count),
TP_STRUCT__entry(
- __string( name, dev_name(dev) )
- __field( unsigned int, reg )
- __field( int, count )
+ __string( name, regmap_name(map) )
+ __field( unsigned int, reg )
+ __field( int, count )
),
TP_fast_assign(
- __assign_str(name, dev_name(dev));
+ __assign_str(name, regmap_name(map));
__entry->reg = reg;
__entry->count = count;
),
DEFINE_EVENT(regmap_block, regmap_hw_read_start,
- TP_PROTO(struct device *dev, unsigned int reg, int count),
+ TP_PROTO(struct regmap *map, unsigned int reg, int count),
- TP_ARGS(dev, reg, count)
+ TP_ARGS(map, reg, count)
);
DEFINE_EVENT(regmap_block, regmap_hw_read_done,
- TP_PROTO(struct device *dev, unsigned int reg, int count),
+ TP_PROTO(struct regmap *map, unsigned int reg, int count),
- TP_ARGS(dev, reg, count)
+ TP_ARGS(map, reg, count)
);
DEFINE_EVENT(regmap_block, regmap_hw_write_start,
- TP_PROTO(struct device *dev, unsigned int reg, int count),
+ TP_PROTO(struct regmap *map, unsigned int reg, int count),
- TP_ARGS(dev, reg, count)
+ TP_ARGS(map, reg, count)
);
DEFINE_EVENT(regmap_block, regmap_hw_write_done,
- TP_PROTO(struct device *dev, unsigned int reg, int count),
+ TP_PROTO(struct regmap *map, unsigned int reg, int count),
- TP_ARGS(dev, reg, count)
+ TP_ARGS(map, reg, count)
);
TRACE_EVENT(regcache_sync,
- TP_PROTO(struct device *dev, const char *type,
+ TP_PROTO(struct regmap *map, const char *type,
const char *status),
- TP_ARGS(dev, type, status),
+ TP_ARGS(map, type, status),
TP_STRUCT__entry(
- __string( name, dev_name(dev) )
- __string( status, status )
- __string( type, type )
- __field( int, type )
+ __string( name, regmap_name(map) )
+ __string( status, status )
+ __string( type, type )
+ __field( int, type )
),
TP_fast_assign(
- __assign_str(name, dev_name(dev));
+ __assign_str(name, regmap_name(map));
__assign_str(status, status);
__assign_str(type, type);
),
DECLARE_EVENT_CLASS(regmap_bool,
- TP_PROTO(struct device *dev, bool flag),
+ TP_PROTO(struct regmap *map, bool flag),
- TP_ARGS(dev, flag),
+ TP_ARGS(map, flag),
TP_STRUCT__entry(
- __string( name, dev_name(dev) )
- __field( int, flag )
+ __string( name, regmap_name(map) )
+ __field( int, flag )
),
TP_fast_assign(
- __assign_str(name, dev_name(dev));
+ __assign_str(name, regmap_name(map));
__entry->flag = flag;
),
DEFINE_EVENT(regmap_bool, regmap_cache_only,
- TP_PROTO(struct device *dev, bool flag),
+ TP_PROTO(struct regmap *map, bool flag),
- TP_ARGS(dev, flag)
+ TP_ARGS(map, flag)
);
DEFINE_EVENT(regmap_bool, regmap_cache_bypass,
- TP_PROTO(struct device *dev, bool flag),
+ TP_PROTO(struct regmap *map, bool flag),
- TP_ARGS(dev, flag)
+ TP_ARGS(map, flag)
);
DECLARE_EVENT_CLASS(regmap_async,
- TP_PROTO(struct device *dev),
+ TP_PROTO(struct regmap *map),
- TP_ARGS(dev),
+ TP_ARGS(map),
TP_STRUCT__entry(
- __string( name, dev_name(dev) )
+ __string( name, regmap_name(map) )
),
TP_fast_assign(
- __assign_str(name, dev_name(dev));
+ __assign_str(name, regmap_name(map));
),
TP_printk("%s", __get_str(name))
DEFINE_EVENT(regmap_block, regmap_async_write_start,
- TP_PROTO(struct device *dev, unsigned int reg, int count),
+ TP_PROTO(struct regmap *map, unsigned int reg, int count),
- TP_ARGS(dev, reg, count)
+ TP_ARGS(map, reg, count)
);
DEFINE_EVENT(regmap_async, regmap_async_io_complete,
- TP_PROTO(struct device *dev),
+ TP_PROTO(struct regmap *map),
- TP_ARGS(dev)
+ TP_ARGS(map)
);
DEFINE_EVENT(regmap_async, regmap_async_complete_start,
- TP_PROTO(struct device *dev),
+ TP_PROTO(struct regmap *map),
- TP_ARGS(dev)
+ TP_ARGS(map)
);
DEFINE_EVENT(regmap_async, regmap_async_complete_done,
- TP_PROTO(struct device *dev),
+ TP_PROTO(struct regmap *map),
- TP_ARGS(dev)
+ TP_ARGS(map)
);
TRACE_EVENT(regcache_drop_region,
- TP_PROTO(struct device *dev, unsigned int from,
+ TP_PROTO(struct regmap *map, unsigned int from,
unsigned int to),
- TP_ARGS(dev, from, to),
+ TP_ARGS(map, from, to),
TP_STRUCT__entry(
- __string( name, dev_name(dev) )
- __field( unsigned int, from )
- __field( unsigned int, to )
+ __string( name, regmap_name(map) )
+ __field( unsigned int, from )
+ __field( unsigned int, to )
),
TP_fast_assign(
- __assign_str(name, dev_name(dev));
+ __assign_str(name, regmap_name(map));
__entry->from = from;
__entry->to = to;
),
*/
#define MT_TOOL_FINGER 0
#define MT_TOOL_PEN 1
-#define MT_TOOL_MAX 1
+#define MT_TOOL_PALM 2
+#define MT_TOOL_MAX 2
/*
* Values describing the status of a force-feedback effect
* exported filesystem.
*/
#define NFSEXP_V4ROOT 0x10000
-#define NFSEXP_NOPNFS 0x20000
+#define NFSEXP_PNFS 0x20000
/* All flags that we claim to support. (Note we don't support NOACL.) */
#define NFSEXP_ALLFLAGS 0x3FE7F
#define SERIAL_IO_PORT 0
#define SERIAL_IO_HUB6 1
#define SERIAL_IO_MEM 2
+#define SERIAL_IO_MEM32 3
+#define SERIAL_IO_AU 4
+#define SERIAL_IO_TSI 5
+#define SERIAL_IO_MEM32BE 6
#define UART_CLEAR_FIFO 0x01
#define UART_USE_FIFO 0x02
header-y += tc_skbedit.h
header-y += tc_vlan.h
header-y += tc_bpf.h
+header-y += tc_connmark.h
__u32 size_max;
/* The maximum number of segments (if VIRTIO_BLK_F_SEG_MAX) */
__u32 seg_max;
- /* geometry the device (if VIRTIO_BLK_F_GEOMETRY) */
+ /* geometry of the device (if VIRTIO_BLK_F_GEOMETRY) */
struct virtio_blk_geometry {
__u16 cylinders;
__u8 heads;
#define VIRTIO_BLK_T_BARRIER 0x80000000
#endif /* !VIRTIO_BLK_NO_LEGACY */
-/* This is the first element of the read scatter-gather list. */
+/*
+ * This comes first in the read scatter-gather list.
+ * For legacy virtio, if VIRTIO_F_ANY_LAYOUT is not negotiated,
+ * this is the first element of the read scatter-gather list.
+ */
struct virtio_blk_outhdr {
/* VIRTIO_BLK_T* */
__virtio32 type;
#include <linux/virtio_types.h>
-#define VIRTIO_SCSI_CDB_SIZE 32
-#define VIRTIO_SCSI_SENSE_SIZE 96
+/* Default values of the CDB and sense data size configuration fields */
+#define VIRTIO_SCSI_CDB_DEFAULT_SIZE 32
+#define VIRTIO_SCSI_SENSE_DEFAULT_SIZE 96
+
+#ifndef VIRTIO_SCSI_CDB_SIZE
+#define VIRTIO_SCSI_CDB_SIZE VIRTIO_SCSI_CDB_DEFAULT_SIZE
+#endif
+#ifndef VIRTIO_SCSI_SENSE_SIZE
+#define VIRTIO_SCSI_SENSE_SIZE VIRTIO_SCSI_SENSE_DEFAULT_SIZE
+#endif
/* SCSI command request, followed by data-out */
struct virtio_scsi_cmd_req {
};
struct omap_dss_device {
+ struct kobject kobj;
struct device *dev;
struct module *owner;
}
#endif
+#ifdef CONFIG_PREEMPT
+
+static inline void xen_preemptible_hcall_begin(void)
+{
+}
+
+static inline void xen_preemptible_hcall_end(void)
+{
+}
+
+#else
+
+DECLARE_PER_CPU(bool, xen_in_preemptible_hcall);
+
+static inline void xen_preemptible_hcall_begin(void)
+{
+ __this_cpu_write(xen_in_preemptible_hcall, true);
+}
+
+static inline void xen_preemptible_hcall_end(void)
+{
+ __this_cpu_write(xen_in_preemptible_hcall, false);
+}
+
+#endif /* CONFIG_PREEMPT */
+
#endif /* INCLUDE_XEN_OPS_H */
const char *mod_name);
#define xenbus_register_frontend(drv) \
- __xenbus_register_frontend(drv, THIS_MODULE, KBUILD_MODNAME);
+ __xenbus_register_frontend(drv, THIS_MODULE, KBUILD_MODNAME)
#define xenbus_register_backend(drv) \
- __xenbus_register_backend(drv, THIS_MODULE, KBUILD_MODNAME);
+ __xenbus_register_backend(drv, THIS_MODULE, KBUILD_MODNAME)
void xenbus_unregister_driver(struct xenbus_driver *drv);
rcu_read_lock();
cpuset_for_each_descendant_pre(cp, pos_css, root_cs) {
- if (cp == root_cs)
- continue;
-
/* skip the whole subtree if @cp doesn't have any CPU */
if (cpumask_empty(cp->cpus_allowed)) {
pos_css = css_rightmost_descendant(pos_css);
* If it becomes empty, inherit the effective mask of the
* parent, which is guaranteed to have some CPUs.
*/
- if (cpumask_empty(new_cpus))
+ if (cgroup_on_dfl(cp->css.cgroup) && cpumask_empty(new_cpus))
cpumask_copy(new_cpus, parent->effective_cpus);
/* Skip the whole subtree if the cpumask remains the same. */
* If it becomes empty, inherit the effective mask of the
* parent, which is guaranteed to have some MEMs.
*/
- if (nodes_empty(*new_mems))
+ if (cgroup_on_dfl(cp->css.cgroup) && nodes_empty(*new_mems))
*new_mems = parent->effective_mems;
/* Skip the whole subtree if the nodemask remains the same. */
spin_lock_irq(&callback_lock);
cs->mems_allowed = parent->mems_allowed;
+ cs->effective_mems = parent->mems_allowed;
cpumask_copy(cs->cpus_allowed, parent->cpus_allowed);
+ cpumask_copy(cs->effective_cpus, parent->cpus_allowed);
spin_unlock_irq(&callback_lock);
out_unlock:
mutex_unlock(&cpuset_mutex);
ctx = perf_event_ctx_lock_nested(event, SINGLE_DEPTH_NESTING);
WARN_ON_ONCE(ctx->parent_ctx);
perf_remove_from_context(event, true);
- mutex_unlock(&ctx->mutex);
+ perf_event_ctx_unlock(event, ctx);
_free_event(event);
}
{
struct perf_event *event = container_of(entry,
struct perf_event, pending);
+ int rctx;
+
+ rctx = perf_swevent_get_recursion_context();
+ /*
+ * If we 'fail' here, that's OK, it means recursion is already disabled
+ * and we won't recurse 'further'.
+ */
if (event->pending_disable) {
event->pending_disable = 0;
event->pending_wakeup = 0;
perf_event_wakeup(event);
}
+
+ if (rctx >= 0)
+ perf_swevent_put_recursion_context(rctx);
}
/*
* otherwise we'll have trouble later trying to figure out
* which interrupt is which (messes up the interrupt freeing
* logic etc).
+ *
+ * Also IRQF_COND_SUSPEND only makes sense for shared interrupts and
+ * it cannot be set along with IRQF_NO_SUSPEND.
*/
- if ((irqflags & IRQF_SHARED) && !dev_id)
+ if (((irqflags & IRQF_SHARED) && !dev_id) ||
+ (!(irqflags & IRQF_SHARED) && (irqflags & IRQF_COND_SUSPEND)) ||
+ ((irqflags & IRQF_NO_SUSPEND) && (irqflags & IRQF_COND_SUSPEND)))
return -EINVAL;
desc = irq_to_desc(irq);
if (action->flags & IRQF_NO_SUSPEND)
desc->no_suspend_depth++;
+ else if (action->flags & IRQF_COND_SUSPEND)
+ desc->cond_suspend_depth++;
WARN_ON_ONCE(desc->no_suspend_depth &&
- desc->no_suspend_depth != desc->nr_actions);
+ (desc->no_suspend_depth +
+ desc->cond_suspend_depth) != desc->nr_actions);
}
/*
if (action->flags & IRQF_NO_SUSPEND)
desc->no_suspend_depth--;
+ else if (action->flags & IRQF_COND_SUSPEND)
+ desc->cond_suspend_depth--;
}
static bool suspend_device_irq(struct irq_desc *desc, int irq)
/* sets obj->mod if object is not vmlinux and module is found */
static void klp_find_object_module(struct klp_object *obj)
{
+ struct module *mod;
+
if (!klp_is_module(obj))
return;
mutex_lock(&module_mutex);
/*
- * We don't need to take a reference on the module here because we have
- * the klp_mutex, which is also taken by the module notifier. This
- * prevents any module from unloading until we release the klp_mutex.
+ * We do not want to block removal of patched modules and therefore
+ * we do not take a reference here. The patches are removed by
+ * a going module handler instead.
+ */
+ mod = find_module(obj->name);
+ /*
+ * Do not mess work of the module coming and going notifiers.
+ * Note that the patch might still be needed before the going handler
+ * is called. Module functions can be called even in the GOING state
+ * until mod->exit() finishes. This is especially important for
+ * patches that modify semantic of the functions.
*/
- obj->mod = find_module(obj->name);
+ if (mod && mod->klp_alive)
+ obj->mod = mod;
+
mutex_unlock(&module_mutex);
}
/* first, check if it's an exported symbol */
preempt_disable();
sym = find_symbol(name, NULL, NULL, true, true);
- preempt_enable();
if (sym) {
*addr = sym->value;
+ preempt_enable();
return 0;
}
+ preempt_enable();
/* otherwise check if it's in another .o within the patch module */
return klp_find_object_symbol(pmod->name, name, addr);
rcu_read_lock();
func = list_first_or_null_rcu(&ops->func_stack, struct klp_func,
stack_node);
- rcu_read_unlock();
-
if (WARN_ON_ONCE(!func))
- return;
+ goto unlock;
klp_arch_set_pc(regs, (unsigned long)func->new_func);
+unlock:
+ rcu_read_unlock();
}
static int klp_disable_func(struct klp_func *func)
func->state = KLP_DISABLED;
return kobject_init_and_add(&func->kobj, &klp_ktype_func,
- obj->kobj, func->old_name);
+ obj->kobj, "%s", func->old_name);
}
/* parts of the initialization that is done only when the object is loaded */
return -EINVAL;
obj->state = KLP_DISABLED;
+ obj->mod = NULL;
klp_find_object_module(obj);
patch->state = KLP_DISABLED;
ret = kobject_init_and_add(&patch->kobj, &klp_ktype_patch,
- klp_root_kobj, patch->mod->name);
+ klp_root_kobj, "%s", patch->mod->name);
if (ret)
goto unlock;
mutex_lock(&klp_mutex);
+ /*
+ * Each module has to know that the notifier has been called.
+ * We never know what module will get patched by a new patch.
+ */
+ if (action == MODULE_STATE_COMING)
+ mod->klp_alive = true;
+ else /* MODULE_STATE_GOING */
+ mod->klp_alive = false;
+
list_for_each_entry(patch, &klp_patches, list) {
for (obj = patch->objs; obj->funcs; obj++) {
if (!klp_is_module(obj) || strcmp(obj->name, mod->name))
if (!new_class->name)
return 0;
- list_for_each_entry(class, &all_lock_classes, lock_entry) {
+ list_for_each_entry_rcu(class, &all_lock_classes, lock_entry) {
if (new_class->key - new_class->subclass == class->key)
return class->name_version;
if (class->name && !strcmp(class->name, new_class->name))
hash_head = classhashentry(key);
/*
- * We can walk the hash lockfree, because the hash only
- * grows, and we are careful when adding entries to the end:
+ * We do an RCU walk of the hash, see lockdep_free_key_range().
*/
- list_for_each_entry(class, hash_head, hash_entry) {
+ if (DEBUG_LOCKS_WARN_ON(!irqs_disabled()))
+ return NULL;
+
+ list_for_each_entry_rcu(class, hash_head, hash_entry) {
if (class->key == key) {
/*
* Huh! same key, different name? Did someone trample
struct lockdep_subclass_key *key;
struct list_head *hash_head;
struct lock_class *class;
- unsigned long flags;
+
+ DEBUG_LOCKS_WARN_ON(!irqs_disabled());
class = look_up_lock_class(lock, subclass);
if (likely(class))
key = lock->key->subkeys + subclass;
hash_head = classhashentry(key);
- raw_local_irq_save(flags);
if (!graph_lock()) {
- raw_local_irq_restore(flags);
return NULL;
}
/*
* We have to do the hash-walk again, to avoid races
* with another CPU:
*/
- list_for_each_entry(class, hash_head, hash_entry)
+ list_for_each_entry_rcu(class, hash_head, hash_entry) {
if (class->key == key)
goto out_unlock_set;
+ }
+
/*
* Allocate a new key from the static array, and add it to
* the hash:
*/
if (nr_lock_classes >= MAX_LOCKDEP_KEYS) {
if (!debug_locks_off_graph_unlock()) {
- raw_local_irq_restore(flags);
return NULL;
}
- raw_local_irq_restore(flags);
print_lockdep_off("BUG: MAX_LOCKDEP_KEYS too low!");
dump_stack();
if (verbose(class)) {
graph_unlock();
- raw_local_irq_restore(flags);
printk("\nnew class %p: %s", class->key, class->name);
if (class->name_version > 1)
printk("\n");
dump_stack();
- raw_local_irq_save(flags);
if (!graph_lock()) {
- raw_local_irq_restore(flags);
return NULL;
}
}
out_unlock_set:
graph_unlock();
- raw_local_irq_restore(flags);
out_set_class_cache:
if (!subclass || force)
entry->distance = distance;
entry->trace = *trace;
/*
- * Since we never remove from the dependency list, the list can
- * be walked lockless by other CPUs, it's only allocation
- * that must be protected by the spinlock. But this also means
- * we must make new entries visible only once writes to the
- * entry become visible - hence the RCU op:
+ * Both allocation and removal are done under the graph lock; but
+ * iteration is under RCU-sched; see look_up_lock_class() and
+ * lockdep_free_key_range().
*/
list_add_tail_rcu(&entry->entry, head);
else
head = &lock->class->locks_before;
- list_for_each_entry(entry, head, entry) {
+ DEBUG_LOCKS_WARN_ON(!irqs_disabled());
+
+ list_for_each_entry_rcu(entry, head, entry) {
if (!lock_accessed(entry)) {
unsigned int cq_depth;
mark_lock_accessed(entry, lock);
* We can walk it lock-free, because entries only get added
* to the hash:
*/
- list_for_each_entry(chain, hash_head, entry) {
+ list_for_each_entry_rcu(chain, hash_head, entry) {
if (chain->chain_key == chain_key) {
cache_hit:
debug_atomic_inc(chain_lookup_hits);
if (unlikely(!debug_locks))
return;
- if (subclass)
+ if (subclass) {
+ unsigned long flags;
+
+ if (DEBUG_LOCKS_WARN_ON(current->lockdep_recursion))
+ return;
+
+ raw_local_irq_save(flags);
+ current->lockdep_recursion = 1;
register_lock_class(lock, subclass, 1);
+ current->lockdep_recursion = 0;
+ raw_local_irq_restore(flags);
+ }
}
EXPORT_SYMBOL_GPL(lockdep_init_map);
return addr >= start && addr < start + size;
}
+/*
+ * Used in module.c to remove lock classes from memory that is going to be
+ * freed; and possibly re-used by other modules.
+ *
+ * We will have had one sync_sched() before getting here, so we're guaranteed
+ * nobody will look up these exact classes -- they're properly dead but still
+ * allocated.
+ */
void lockdep_free_key_range(void *start, unsigned long size)
{
- struct lock_class *class, *next;
+ struct lock_class *class;
struct list_head *head;
unsigned long flags;
int i;
head = classhash_table + i;
if (list_empty(head))
continue;
- list_for_each_entry_safe(class, next, head, hash_entry) {
+ list_for_each_entry_rcu(class, head, hash_entry) {
if (within(class->key, start, size))
zap_class(class);
else if (within(class->name, start, size))
if (locked)
graph_unlock();
raw_local_irq_restore(flags);
+
+ /*
+ * Wait for any possible iterators from look_up_lock_class() to pass
+ * before continuing to free the memory they refer to.
+ *
+ * sync_sched() is sufficient because the read-side is IRQ disable.
+ */
+ synchronize_sched();
+
+ /*
+ * XXX at this point we could return the resources to the pool;
+ * instead we leak them. We would need to change to bitmap allocators
+ * instead of the linear allocators we have now.
+ */
}
void lockdep_reset_lock(struct lockdep_map *lock)
{
- struct lock_class *class, *next;
+ struct lock_class *class;
struct list_head *head;
unsigned long flags;
int i, j;
head = classhash_table + i;
if (list_empty(head))
continue;
- list_for_each_entry_safe(class, next, head, hash_entry) {
+ list_for_each_entry_rcu(class, head, hash_entry) {
int match = 0;
for (j = 0; j < NR_LOCKDEP_CACHING_CLASSES; j++)
ret = __rt_mutex_slowlock(lock, state, timeout, &waiter);
if (unlikely(ret)) {
+ __set_current_state(TASK_RUNNING);
if (rt_mutex_has_waiters(lock))
remove_waiter(lock, &waiter);
rt_mutex_handle_deadlock(ret, chwalk, &waiter);
#include <linux/async.h>
#include <linux/percpu.h>
#include <linux/kmemleak.h>
-#include <linux/kasan.h>
#include <linux/jump_label.h>
#include <linux/pfn.h>
#include <linux/bsearch.h>
void __weak module_memfree(void *module_region)
{
vfree(module_region);
- kasan_module_free(module_region);
}
void __weak module_arch_cleanup(struct module *mod)
kfree(mod->args);
percpu_modfree(mod);
- /* Free lock-classes: */
+ /* Free lock-classes; relies on the preceding sync_rcu(). */
lockdep_free_key_range(mod->module_core, mod->core_size);
/* Finally, free the core (containing the module structure) */
info->symoffs = ALIGN(mod->core_size, symsect->sh_addralign ?: 1);
info->stroffs = mod->core_size = info->symoffs + ndst * sizeof(Elf_Sym);
mod->core_size += strtab_size;
+ mod->core_size = debug_align(mod->core_size);
/* Put string table section at end of init part of module. */
strsect->sh_flags |= SHF_ALLOC;
strsect->sh_entsize = get_offset(mod, &mod->init_size, strsect,
info->index.str) | INIT_OFFSET_MASK;
+ mod->init_size = debug_align(mod->init_size);
pr_debug("\t%s\n", info->secstrings + strsect->sh_name);
}
module_bug_cleanup(mod);
mutex_unlock(&module_mutex);
- /* Free lock-classes: */
- lockdep_free_key_range(mod->module_core, mod->core_size);
-
/* we can't deallocate the module until we clear memory protection */
unset_module_init_ro_nx(mod);
unset_module_core_ro_nx(mod);
synchronize_rcu();
mutex_unlock(&module_mutex);
free_module:
+ /* Free lock-classes; relies on the preceding sync_rcu() */
+ lockdep_free_key_range(mod->module_core, mod->core_size);
+
module_deallocate(mod, info);
free_copy:
free_copy(info);
struct console_cmdline
{
- char name[8]; /* Name of the driver */
+ char name[16]; /* Name of the driver */
int index; /* Minor dev. to use */
char *options; /* Options for the driver */
#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
for (i = 0, c = console_cmdline;
i < MAX_CMDLINECONSOLES && c->name[0];
i++, c++) {
+ BUILD_BUG_ON(sizeof(c->name) != sizeof(newcon->name));
if (strcmp(c->name, newcon->name) != 0)
continue;
if (newcon->index >= 0 &&
} else {
if (dl_prio(oldprio))
p->dl.dl_boosted = 0;
+ if (rt_prio(oldprio))
+ p->rt.timeout = 0;
p->sched_class = &fair_sched_class;
}
/*
* If there were no record hinting faults then either the task is
* completely idle or all activity is areas that are not of interest
- * to automatic numa balancing. Scan slower
+ * to automatic numa balancing. Related to that, if there were failed
+ * migration then it implies we are migrating too quickly or the local
+ * node is overloaded. In either case, scan slower
*/
- if (local + shared == 0) {
+ if (local + shared == 0 || p->numa_faults_locality[2]) {
p->numa_scan_period = min(p->numa_scan_period_max,
p->numa_scan_period << 1);
if (migrated)
p->numa_pages_migrated += pages;
+ if (flags & TNF_MIGRATE_FAIL)
+ p->numa_faults_locality[2] += pages;
p->numa_faults[task_faults_idx(NUMA_MEMBUF, mem_node, priv)] += pages;
p->numa_faults[task_faults_idx(NUMA_CPUBUF, cpu_node, priv)] += pages;
struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);
int next_state, entered_state;
unsigned int broadcast;
+ bool reflect;
/*
* Check if the idle task must be rescheduled. If it is the
*/
rcu_idle_enter();
+ if (cpuidle_not_available(drv, dev))
+ goto use_default;
+
/*
* Suspend-to-idle ("freeze") is a system state in which all user space
* has been frozen, all I/O devices have been suspended and the only
* until a proper wakeup interrupt happens.
*/
if (idle_should_freeze()) {
- cpuidle_enter_freeze();
- local_irq_enable();
- goto exit_idle;
- }
+ entered_state = cpuidle_enter_freeze(drv, dev);
+ if (entered_state >= 0) {
+ local_irq_enable();
+ goto exit_idle;
+ }
- /*
- * Ask the cpuidle framework to choose a convenient idle state.
- * Fall back to the default arch idle method on errors.
- */
- next_state = cpuidle_select(drv, dev);
- if (next_state < 0) {
-use_default:
+ reflect = false;
+ next_state = cpuidle_find_deepest_state(drv, dev);
+ } else {
+ reflect = true;
/*
- * We can't use the cpuidle framework, let's use the default
- * idle routine.
+ * Ask the cpuidle framework to choose a convenient idle state.
*/
- if (current_clr_polling_and_test())
- local_irq_enable();
- else
- arch_cpu_idle();
-
- goto exit_idle;
+ next_state = cpuidle_select(drv, dev);
}
-
+ /* Fall back to the default arch idle method on errors. */
+ if (next_state < 0)
+ goto use_default;
/*
* The idle task must be scheduled, it is pointless to
/*
* Give the governor an opportunity to reflect on the outcome
*/
- cpuidle_reflect(dev, entered_state);
+ if (reflect)
+ cpuidle_reflect(dev, entered_state);
exit_idle:
__current_set_polling();
rcu_idle_exit();
start_critical_timings();
+ return;
+
+use_default:
+ /*
+ * We can't use the cpuidle framework, let's use the default
+ * idle routine.
+ */
+ if (current_clr_polling_and_test())
+ local_irq_enable();
+ else
+ arch_cpu_idle();
+
+ goto exit_idle;
}
/*
/*
* Work around broken programs that cannot handle "Linux 3.0".
* Instead we map 3.x to 2.6.40+x, so e.g. 3.0 would be 2.6.40
+ * And we map 4.x to 2.6.60+x, so 4.0 would be 2.6.60.
*/
static int override_release(char __user *release, size_t len)
{
break;
rest++;
}
- v = ((LINUX_VERSION_CODE >> 8) & 0xff) + 40;
+ v = ((LINUX_VERSION_CODE >> 8) & 0xff) + 60;
copy = clamp_t(size_t, len, 1, sizeof(buf));
copy = scnprintf(buf, copy, "2.6.%u%s", v, rest);
ret = copy_to_user(release, buf, copy + 1);
.proc_handler = proc_dointvec_minmax,
.extra1 = &zero,
},
+ {
+ .procname = "dirtytime_expire_seconds",
+ .data = &dirtytime_expire_interval,
+ .maxlen = sizeof(dirty_expire_interval),
+ .mode = 0644,
+ .proc_handler = dirtytime_interval_handler,
+ .extra1 = &zero,
+ },
{
.procname = "nr_pdflush_threads",
.mode = 0444 /* read-only */,
*/
static int bc_set_next(ktime_t expires, struct clock_event_device *bc)
{
+ int bc_moved;
/*
* We try to cancel the timer first. If the callback is on
* flight on some other cpu then we let it handle it. If we
* restart the timer because we are in the callback, but we
* can set the expiry time and let the callback return
* HRTIMER_RESTART.
+ *
+ * Since we are in the idle loop at this point and because
+ * hrtimer_{start/cancel} functions call into tracing,
+ * calls to these functions must be bound within RCU_NONIDLE.
*/
- if (hrtimer_try_to_cancel(&bctimer) >= 0) {
- hrtimer_start(&bctimer, expires, HRTIMER_MODE_ABS_PINNED);
+ RCU_NONIDLE(bc_moved = (hrtimer_try_to_cancel(&bctimer) >= 0) ?
+ !hrtimer_start(&bctimer, expires, HRTIMER_MODE_ABS_PINNED) :
+ 0);
+ if (bc_moved) {
/* Bind the "device" to the cpu */
bc->bound_on = smp_processor_id();
} else if (bc->bound_on == smp_processor_id()) {
static struct pid * const ftrace_swapper_pid = &init_struct_pid;
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+static int ftrace_graph_active;
+#else
+# define ftrace_graph_active 0
+#endif
+
#ifdef CONFIG_DYNAMIC_FTRACE
static struct ftrace_ops *removed_ops;
if (!ftrace_rec_count(rec))
rec->flags = 0;
else
- /* Just disable the record (keep REGS state) */
- rec->flags &= ~FTRACE_FL_ENABLED;
+ /*
+ * Just disable the record, but keep the ops TRAMP
+ * and REGS states. The _EN flags must be disabled though.
+ */
+ rec->flags &= ~(FTRACE_FL_ENABLED | FTRACE_FL_TRAMP_EN |
+ FTRACE_FL_REGS_EN);
}
return FTRACE_UPDATE_MAKE_NOP;
static void ftrace_startup_sysctl(void)
{
+ int command;
+
if (unlikely(ftrace_disabled))
return;
/* Force update next time */
saved_ftrace_func = NULL;
/* ftrace_start_up is true if we want ftrace running */
- if (ftrace_start_up)
- ftrace_run_update_code(FTRACE_UPDATE_CALLS);
+ if (ftrace_start_up) {
+ command = FTRACE_UPDATE_CALLS;
+ if (ftrace_graph_active)
+ command |= FTRACE_START_FUNC_RET;
+ ftrace_startup_enable(command);
+ }
}
static void ftrace_shutdown_sysctl(void)
{
+ int command;
+
if (unlikely(ftrace_disabled))
return;
/* ftrace_start_up is true if ftrace is running */
- if (ftrace_start_up)
- ftrace_run_update_code(FTRACE_DISABLE_CALLS);
+ if (ftrace_start_up) {
+ command = FTRACE_DISABLE_CALLS;
+ if (ftrace_graph_active)
+ command |= FTRACE_STOP_FUNC_RET;
+ ftrace_run_update_code(command);
+ }
}
static cycle_t ftrace_update_time;
if (ftrace_enabled) {
- ftrace_startup_sysctl();
-
/* we are starting ftrace again */
if (ftrace_ops_list != &ftrace_list_end)
update_ftrace_function();
+ ftrace_startup_sysctl();
+
} else {
/* stopping ftrace calls (just send to ftrace_stub) */
ftrace_trace_function = ftrace_stub;
ASSIGN_OPS_HASH(graph_ops, &global_ops.local_hash)
};
-static int ftrace_graph_active;
-
int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace)
{
return 0;
}
EXPORT_SYMBOL_GPL(flush_work);
+struct cwt_wait {
+ wait_queue_t wait;
+ struct work_struct *work;
+};
+
+static int cwt_wakefn(wait_queue_t *wait, unsigned mode, int sync, void *key)
+{
+ struct cwt_wait *cwait = container_of(wait, struct cwt_wait, wait);
+
+ if (cwait->work != key)
+ return 0;
+ return autoremove_wake_function(wait, mode, sync, key);
+}
+
static bool __cancel_work_timer(struct work_struct *work, bool is_dwork)
{
+ static DECLARE_WAIT_QUEUE_HEAD(cancel_waitq);
unsigned long flags;
int ret;
do {
ret = try_to_grab_pending(work, is_dwork, &flags);
/*
- * If someone else is canceling, wait for the same event it
- * would be waiting for before retrying.
+ * If someone else is already canceling, wait for it to
+ * finish. flush_work() doesn't work for PREEMPT_NONE
+ * because we may get scheduled between @work's completion
+ * and the other canceling task resuming and clearing
+ * CANCELING - flush_work() will return false immediately
+ * as @work is no longer busy, try_to_grab_pending() will
+ * return -ENOENT as @work is still being canceled and the
+ * other canceling task won't be able to clear CANCELING as
+ * we're hogging the CPU.
+ *
+ * Let's wait for completion using a waitqueue. As this
+ * may lead to the thundering herd problem, use a custom
+ * wake function which matches @work along with exclusive
+ * wait and wakeup.
*/
- if (unlikely(ret == -ENOENT))
- flush_work(work);
+ if (unlikely(ret == -ENOENT)) {
+ struct cwt_wait cwait;
+
+ init_wait(&cwait.wait);
+ cwait.wait.func = cwt_wakefn;
+ cwait.work = work;
+
+ prepare_to_wait_exclusive(&cancel_waitq, &cwait.wait,
+ TASK_UNINTERRUPTIBLE);
+ if (work_is_canceling(work))
+ schedule();
+ finish_wait(&cancel_waitq, &cwait.wait);
+ }
} while (unlikely(ret < 0));
/* tell other tasks trying to grab @work to back off */
flush_work(work);
clear_work_data(work);
+
+ /*
+ * Paired with prepare_to_wait() above so that either
+ * waitqueue_active() is visible here or !work_is_canceling() is
+ * visible there.
+ */
+ smp_mb();
+ if (waitqueue_active(&cancel_waitq))
+ __wake_up(&cancel_waitq, TASK_NORMAL, 1, work);
+
return ret;
}
obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
bust_spinlocks.o kasprintf.o bitmap.o scatterlist.o \
- gcd.o lcm.o list_sort.o uuid.o flex_array.o clz_ctz.o \
+ gcd.o lcm.o list_sort.o uuid.o flex_array.o iov_iter.o clz_ctz.o \
bsearch.o find_last_bit.o find_next_bit.o llist.o memweight.o kfifo.o \
percpu-refcount.o percpu_ida.o rhashtable.o reciprocal_div.o
obj-y += string_helpers.o
--- /dev/null
+#include <linux/export.h>
+#include <linux/uio.h>
+#include <linux/pagemap.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <net/checksum.h>
+
+#define iterate_iovec(i, n, __v, __p, skip, STEP) { \
+ size_t left; \
+ size_t wanted = n; \
+ __p = i->iov; \
+ __v.iov_len = min(n, __p->iov_len - skip); \
+ if (likely(__v.iov_len)) { \
+ __v.iov_base = __p->iov_base + skip; \
+ left = (STEP); \
+ __v.iov_len -= left; \
+ skip += __v.iov_len; \
+ n -= __v.iov_len; \
+ } else { \
+ left = 0; \
+ } \
+ while (unlikely(!left && n)) { \
+ __p++; \
+ __v.iov_len = min(n, __p->iov_len); \
+ if (unlikely(!__v.iov_len)) \
+ continue; \
+ __v.iov_base = __p->iov_base; \
+ left = (STEP); \
+ __v.iov_len -= left; \
+ skip = __v.iov_len; \
+ n -= __v.iov_len; \
+ } \
+ n = wanted - n; \
+}
+
+#define iterate_kvec(i, n, __v, __p, skip, STEP) { \
+ size_t wanted = n; \
+ __p = i->kvec; \
+ __v.iov_len = min(n, __p->iov_len - skip); \
+ if (likely(__v.iov_len)) { \
+ __v.iov_base = __p->iov_base + skip; \
+ (void)(STEP); \
+ skip += __v.iov_len; \
+ n -= __v.iov_len; \
+ } \
+ while (unlikely(n)) { \
+ __p++; \
+ __v.iov_len = min(n, __p->iov_len); \
+ if (unlikely(!__v.iov_len)) \
+ continue; \
+ __v.iov_base = __p->iov_base; \
+ (void)(STEP); \
+ skip = __v.iov_len; \
+ n -= __v.iov_len; \
+ } \
+ n = wanted; \
+}
+
+#define iterate_bvec(i, n, __v, __p, skip, STEP) { \
+ size_t wanted = n; \
+ __p = i->bvec; \
+ __v.bv_len = min_t(size_t, n, __p->bv_len - skip); \
+ if (likely(__v.bv_len)) { \
+ __v.bv_page = __p->bv_page; \
+ __v.bv_offset = __p->bv_offset + skip; \
+ (void)(STEP); \
+ skip += __v.bv_len; \
+ n -= __v.bv_len; \
+ } \
+ while (unlikely(n)) { \
+ __p++; \
+ __v.bv_len = min_t(size_t, n, __p->bv_len); \
+ if (unlikely(!__v.bv_len)) \
+ continue; \
+ __v.bv_page = __p->bv_page; \
+ __v.bv_offset = __p->bv_offset; \
+ (void)(STEP); \
+ skip = __v.bv_len; \
+ n -= __v.bv_len; \
+ } \
+ n = wanted; \
+}
+
+#define iterate_all_kinds(i, n, v, I, B, K) { \
+ size_t skip = i->iov_offset; \
+ if (unlikely(i->type & ITER_BVEC)) { \
+ const struct bio_vec *bvec; \
+ struct bio_vec v; \
+ iterate_bvec(i, n, v, bvec, skip, (B)) \
+ } else if (unlikely(i->type & ITER_KVEC)) { \
+ const struct kvec *kvec; \
+ struct kvec v; \
+ iterate_kvec(i, n, v, kvec, skip, (K)) \
+ } else { \
+ const struct iovec *iov; \
+ struct iovec v; \
+ iterate_iovec(i, n, v, iov, skip, (I)) \
+ } \
+}
+
+#define iterate_and_advance(i, n, v, I, B, K) { \
+ size_t skip = i->iov_offset; \
+ if (unlikely(i->type & ITER_BVEC)) { \
+ const struct bio_vec *bvec; \
+ struct bio_vec v; \
+ iterate_bvec(i, n, v, bvec, skip, (B)) \
+ if (skip == bvec->bv_len) { \
+ bvec++; \
+ skip = 0; \
+ } \
+ i->nr_segs -= bvec - i->bvec; \
+ i->bvec = bvec; \
+ } else if (unlikely(i->type & ITER_KVEC)) { \
+ const struct kvec *kvec; \
+ struct kvec v; \
+ iterate_kvec(i, n, v, kvec, skip, (K)) \
+ if (skip == kvec->iov_len) { \
+ kvec++; \
+ skip = 0; \
+ } \
+ i->nr_segs -= kvec - i->kvec; \
+ i->kvec = kvec; \
+ } else { \
+ const struct iovec *iov; \
+ struct iovec v; \
+ iterate_iovec(i, n, v, iov, skip, (I)) \
+ if (skip == iov->iov_len) { \
+ iov++; \
+ skip = 0; \
+ } \
+ i->nr_segs -= iov - i->iov; \
+ i->iov = iov; \
+ } \
+ i->count -= n; \
+ i->iov_offset = skip; \
+}
+
+static size_t copy_page_to_iter_iovec(struct page *page, size_t offset, size_t bytes,
+ struct iov_iter *i)
+{
+ size_t skip, copy, left, wanted;
+ const struct iovec *iov;
+ char __user *buf;
+ void *kaddr, *from;
+
+ if (unlikely(bytes > i->count))
+ bytes = i->count;
+
+ if (unlikely(!bytes))
+ return 0;
+
+ wanted = bytes;
+ iov = i->iov;
+ skip = i->iov_offset;
+ buf = iov->iov_base + skip;
+ copy = min(bytes, iov->iov_len - skip);
+
+ if (!fault_in_pages_writeable(buf, copy)) {
+ kaddr = kmap_atomic(page);
+ from = kaddr + offset;
+
+ /* first chunk, usually the only one */
+ left = __copy_to_user_inatomic(buf, from, copy);
+ copy -= left;
+ skip += copy;
+ from += copy;
+ bytes -= copy;
+
+ while (unlikely(!left && bytes)) {
+ iov++;
+ buf = iov->iov_base;
+ copy = min(bytes, iov->iov_len);
+ left = __copy_to_user_inatomic(buf, from, copy);
+ copy -= left;
+ skip = copy;
+ from += copy;
+ bytes -= copy;
+ }
+ if (likely(!bytes)) {
+ kunmap_atomic(kaddr);
+ goto done;
+ }
+ offset = from - kaddr;
+ buf += copy;
+ kunmap_atomic(kaddr);
+ copy = min(bytes, iov->iov_len - skip);
+ }
+ /* Too bad - revert to non-atomic kmap */
+ kaddr = kmap(page);
+ from = kaddr + offset;
+ left = __copy_to_user(buf, from, copy);
+ copy -= left;
+ skip += copy;
+ from += copy;
+ bytes -= copy;
+ while (unlikely(!left && bytes)) {
+ iov++;
+ buf = iov->iov_base;
+ copy = min(bytes, iov->iov_len);
+ left = __copy_to_user(buf, from, copy);
+ copy -= left;
+ skip = copy;
+ from += copy;
+ bytes -= copy;
+ }
+ kunmap(page);
+done:
+ if (skip == iov->iov_len) {
+ iov++;
+ skip = 0;
+ }
+ i->count -= wanted - bytes;
+ i->nr_segs -= iov - i->iov;
+ i->iov = iov;
+ i->iov_offset = skip;
+ return wanted - bytes;
+}
+
+static size_t copy_page_from_iter_iovec(struct page *page, size_t offset, size_t bytes,
+ struct iov_iter *i)
+{
+ size_t skip, copy, left, wanted;
+ const struct iovec *iov;
+ char __user *buf;
+ void *kaddr, *to;
+
+ if (unlikely(bytes > i->count))
+ bytes = i->count;
+
+ if (unlikely(!bytes))
+ return 0;
+
+ wanted = bytes;
+ iov = i->iov;
+ skip = i->iov_offset;
+ buf = iov->iov_base + skip;
+ copy = min(bytes, iov->iov_len - skip);
+
+ if (!fault_in_pages_readable(buf, copy)) {
+ kaddr = kmap_atomic(page);
+ to = kaddr + offset;
+
+ /* first chunk, usually the only one */
+ left = __copy_from_user_inatomic(to, buf, copy);
+ copy -= left;
+ skip += copy;
+ to += copy;
+ bytes -= copy;
+
+ while (unlikely(!left && bytes)) {
+ iov++;
+ buf = iov->iov_base;
+ copy = min(bytes, iov->iov_len);
+ left = __copy_from_user_inatomic(to, buf, copy);
+ copy -= left;
+ skip = copy;
+ to += copy;
+ bytes -= copy;
+ }
+ if (likely(!bytes)) {
+ kunmap_atomic(kaddr);
+ goto done;
+ }
+ offset = to - kaddr;
+ buf += copy;
+ kunmap_atomic(kaddr);
+ copy = min(bytes, iov->iov_len - skip);
+ }
+ /* Too bad - revert to non-atomic kmap */
+ kaddr = kmap(page);
+ to = kaddr + offset;
+ left = __copy_from_user(to, buf, copy);
+ copy -= left;
+ skip += copy;
+ to += copy;
+ bytes -= copy;
+ while (unlikely(!left && bytes)) {
+ iov++;
+ buf = iov->iov_base;
+ copy = min(bytes, iov->iov_len);
+ left = __copy_from_user(to, buf, copy);
+ copy -= left;
+ skip = copy;
+ to += copy;
+ bytes -= copy;
+ }
+ kunmap(page);
+done:
+ if (skip == iov->iov_len) {
+ iov++;
+ skip = 0;
+ }
+ i->count -= wanted - bytes;
+ i->nr_segs -= iov - i->iov;
+ i->iov = iov;
+ i->iov_offset = skip;
+ return wanted - bytes;
+}
+
+/*
+ * Fault in the first iovec of the given iov_iter, to a maximum length
+ * of bytes. Returns 0 on success, or non-zero if the memory could not be
+ * accessed (ie. because it is an invalid address).
+ *
+ * writev-intensive code may want this to prefault several iovecs -- that
+ * would be possible (callers must not rely on the fact that _only_ the
+ * first iovec will be faulted with the current implementation).
+ */
+int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes)
+{
+ if (!(i->type & (ITER_BVEC|ITER_KVEC))) {
+ char __user *buf = i->iov->iov_base + i->iov_offset;
+ bytes = min(bytes, i->iov->iov_len - i->iov_offset);
+ return fault_in_pages_readable(buf, bytes);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(iov_iter_fault_in_readable);
+
+void iov_iter_init(struct iov_iter *i, int direction,
+ const struct iovec *iov, unsigned long nr_segs,
+ size_t count)
+{
+ /* It will get better. Eventually... */
+ if (segment_eq(get_fs(), KERNEL_DS)) {
+ direction |= ITER_KVEC;
+ i->type = direction;
+ i->kvec = (struct kvec *)iov;
+ } else {
+ i->type = direction;
+ i->iov = iov;
+ }
+ i->nr_segs = nr_segs;
+ i->iov_offset = 0;
+ i->count = count;
+}
+EXPORT_SYMBOL(iov_iter_init);
+
+static void memcpy_from_page(char *to, struct page *page, size_t offset, size_t len)
+{
+ char *from = kmap_atomic(page);
+ memcpy(to, from + offset, len);
+ kunmap_atomic(from);
+}
+
+static void memcpy_to_page(struct page *page, size_t offset, char *from, size_t len)
+{
+ char *to = kmap_atomic(page);
+ memcpy(to + offset, from, len);
+ kunmap_atomic(to);
+}
+
+static void memzero_page(struct page *page, size_t offset, size_t len)
+{
+ char *addr = kmap_atomic(page);
+ memset(addr + offset, 0, len);
+ kunmap_atomic(addr);
+}
+
+size_t copy_to_iter(void *addr, size_t bytes, struct iov_iter *i)
+{
+ char *from = addr;
+ if (unlikely(bytes > i->count))
+ bytes = i->count;
+
+ if (unlikely(!bytes))
+ return 0;
+
+ iterate_and_advance(i, bytes, v,
+ __copy_to_user(v.iov_base, (from += v.iov_len) - v.iov_len,
+ v.iov_len),
+ memcpy_to_page(v.bv_page, v.bv_offset,
+ (from += v.bv_len) - v.bv_len, v.bv_len),
+ memcpy(v.iov_base, (from += v.iov_len) - v.iov_len, v.iov_len)
+ )
+
+ return bytes;
+}
+EXPORT_SYMBOL(copy_to_iter);
+
+size_t copy_from_iter(void *addr, size_t bytes, struct iov_iter *i)
+{
+ char *to = addr;
+ if (unlikely(bytes > i->count))
+ bytes = i->count;
+
+ if (unlikely(!bytes))
+ return 0;
+
+ iterate_and_advance(i, bytes, v,
+ __copy_from_user((to += v.iov_len) - v.iov_len, v.iov_base,
+ v.iov_len),
+ memcpy_from_page((to += v.bv_len) - v.bv_len, v.bv_page,
+ v.bv_offset, v.bv_len),
+ memcpy((to += v.iov_len) - v.iov_len, v.iov_base, v.iov_len)
+ )
+
+ return bytes;
+}
+EXPORT_SYMBOL(copy_from_iter);
+
+size_t copy_from_iter_nocache(void *addr, size_t bytes, struct iov_iter *i)
+{
+ char *to = addr;
+ if (unlikely(bytes > i->count))
+ bytes = i->count;
+
+ if (unlikely(!bytes))
+ return 0;
+
+ iterate_and_advance(i, bytes, v,
+ __copy_from_user_nocache((to += v.iov_len) - v.iov_len,
+ v.iov_base, v.iov_len),
+ memcpy_from_page((to += v.bv_len) - v.bv_len, v.bv_page,
+ v.bv_offset, v.bv_len),
+ memcpy((to += v.iov_len) - v.iov_len, v.iov_base, v.iov_len)
+ )
+
+ return bytes;
+}
+EXPORT_SYMBOL(copy_from_iter_nocache);
+
+size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes,
+ struct iov_iter *i)
+{
+ if (i->type & (ITER_BVEC|ITER_KVEC)) {
+ void *kaddr = kmap_atomic(page);
+ size_t wanted = copy_to_iter(kaddr + offset, bytes, i);
+ kunmap_atomic(kaddr);
+ return wanted;
+ } else
+ return copy_page_to_iter_iovec(page, offset, bytes, i);
+}
+EXPORT_SYMBOL(copy_page_to_iter);
+
+size_t copy_page_from_iter(struct page *page, size_t offset, size_t bytes,
+ struct iov_iter *i)
+{
+ if (i->type & (ITER_BVEC|ITER_KVEC)) {
+ void *kaddr = kmap_atomic(page);
+ size_t wanted = copy_from_iter(kaddr + offset, bytes, i);
+ kunmap_atomic(kaddr);
+ return wanted;
+ } else
+ return copy_page_from_iter_iovec(page, offset, bytes, i);
+}
+EXPORT_SYMBOL(copy_page_from_iter);
+
+size_t iov_iter_zero(size_t bytes, struct iov_iter *i)
+{
+ if (unlikely(bytes > i->count))
+ bytes = i->count;
+
+ if (unlikely(!bytes))
+ return 0;
+
+ iterate_and_advance(i, bytes, v,
+ __clear_user(v.iov_base, v.iov_len),
+ memzero_page(v.bv_page, v.bv_offset, v.bv_len),
+ memset(v.iov_base, 0, v.iov_len)
+ )
+
+ return bytes;
+}
+EXPORT_SYMBOL(iov_iter_zero);
+
+size_t iov_iter_copy_from_user_atomic(struct page *page,
+ struct iov_iter *i, unsigned long offset, size_t bytes)
+{
+ char *kaddr = kmap_atomic(page), *p = kaddr + offset;
+ iterate_all_kinds(i, bytes, v,
+ __copy_from_user_inatomic((p += v.iov_len) - v.iov_len,
+ v.iov_base, v.iov_len),
+ memcpy_from_page((p += v.bv_len) - v.bv_len, v.bv_page,
+ v.bv_offset, v.bv_len),
+ memcpy((p += v.iov_len) - v.iov_len, v.iov_base, v.iov_len)
+ )
+ kunmap_atomic(kaddr);
+ return bytes;
+}
+EXPORT_SYMBOL(iov_iter_copy_from_user_atomic);
+
+void iov_iter_advance(struct iov_iter *i, size_t size)
+{
+ iterate_and_advance(i, size, v, 0, 0, 0)
+}
+EXPORT_SYMBOL(iov_iter_advance);
+
+/*
+ * Return the count of just the current iov_iter segment.
+ */
+size_t iov_iter_single_seg_count(const struct iov_iter *i)
+{
+ if (i->nr_segs == 1)
+ return i->count;
+ else if (i->type & ITER_BVEC)
+ return min(i->count, i->bvec->bv_len - i->iov_offset);
+ else
+ return min(i->count, i->iov->iov_len - i->iov_offset);
+}
+EXPORT_SYMBOL(iov_iter_single_seg_count);
+
+void iov_iter_kvec(struct iov_iter *i, int direction,
+ const struct kvec *kvec, unsigned long nr_segs,
+ size_t count)
+{
+ BUG_ON(!(direction & ITER_KVEC));
+ i->type = direction;
+ i->kvec = kvec;
+ i->nr_segs = nr_segs;
+ i->iov_offset = 0;
+ i->count = count;
+}
+EXPORT_SYMBOL(iov_iter_kvec);
+
+void iov_iter_bvec(struct iov_iter *i, int direction,
+ const struct bio_vec *bvec, unsigned long nr_segs,
+ size_t count)
+{
+ BUG_ON(!(direction & ITER_BVEC));
+ i->type = direction;
+ i->bvec = bvec;
+ i->nr_segs = nr_segs;
+ i->iov_offset = 0;
+ i->count = count;
+}
+EXPORT_SYMBOL(iov_iter_bvec);
+
+unsigned long iov_iter_alignment(const struct iov_iter *i)
+{
+ unsigned long res = 0;
+ size_t size = i->count;
+
+ if (!size)
+ return 0;
+
+ iterate_all_kinds(i, size, v,
+ (res |= (unsigned long)v.iov_base | v.iov_len, 0),
+ res |= v.bv_offset | v.bv_len,
+ res |= (unsigned long)v.iov_base | v.iov_len
+ )
+ return res;
+}
+EXPORT_SYMBOL(iov_iter_alignment);
+
+ssize_t iov_iter_get_pages(struct iov_iter *i,
+ struct page **pages, size_t maxsize, unsigned maxpages,
+ size_t *start)
+{
+ if (maxsize > i->count)
+ maxsize = i->count;
+
+ if (!maxsize)
+ return 0;
+
+ iterate_all_kinds(i, maxsize, v, ({
+ unsigned long addr = (unsigned long)v.iov_base;
+ size_t len = v.iov_len + (*start = addr & (PAGE_SIZE - 1));
+ int n;
+ int res;
+
+ if (len > maxpages * PAGE_SIZE)
+ len = maxpages * PAGE_SIZE;
+ addr &= ~(PAGE_SIZE - 1);
+ n = DIV_ROUND_UP(len, PAGE_SIZE);
+ res = get_user_pages_fast(addr, n, (i->type & WRITE) != WRITE, pages);
+ if (unlikely(res < 0))
+ return res;
+ return (res == n ? len : res * PAGE_SIZE) - *start;
+ 0;}),({
+ /* can't be more than PAGE_SIZE */
+ *start = v.bv_offset;
+ get_page(*pages = v.bv_page);
+ return v.bv_len;
+ }),({
+ return -EFAULT;
+ })
+ )
+ return 0;
+}
+EXPORT_SYMBOL(iov_iter_get_pages);
+
+static struct page **get_pages_array(size_t n)
+{
+ struct page **p = kmalloc(n * sizeof(struct page *), GFP_KERNEL);
+ if (!p)
+ p = vmalloc(n * sizeof(struct page *));
+ return p;
+}
+
+ssize_t iov_iter_get_pages_alloc(struct iov_iter *i,
+ struct page ***pages, size_t maxsize,
+ size_t *start)
+{
+ struct page **p;
+
+ if (maxsize > i->count)
+ maxsize = i->count;
+
+ if (!maxsize)
+ return 0;
+
+ iterate_all_kinds(i, maxsize, v, ({
+ unsigned long addr = (unsigned long)v.iov_base;
+ size_t len = v.iov_len + (*start = addr & (PAGE_SIZE - 1));
+ int n;
+ int res;
+
+ addr &= ~(PAGE_SIZE - 1);
+ n = DIV_ROUND_UP(len, PAGE_SIZE);
+ p = get_pages_array(n);
+ if (!p)
+ return -ENOMEM;
+ res = get_user_pages_fast(addr, n, (i->type & WRITE) != WRITE, p);
+ if (unlikely(res < 0)) {
+ kvfree(p);
+ return res;
+ }
+ *pages = p;
+ return (res == n ? len : res * PAGE_SIZE) - *start;
+ 0;}),({
+ /* can't be more than PAGE_SIZE */
+ *start = v.bv_offset;
+ *pages = p = get_pages_array(1);
+ if (!p)
+ return -ENOMEM;
+ get_page(*p = v.bv_page);
+ return v.bv_len;
+ }),({
+ return -EFAULT;
+ })
+ )
+ return 0;
+}
+EXPORT_SYMBOL(iov_iter_get_pages_alloc);
+
+size_t csum_and_copy_from_iter(void *addr, size_t bytes, __wsum *csum,
+ struct iov_iter *i)
+{
+ char *to = addr;
+ __wsum sum, next;
+ size_t off = 0;
+ if (unlikely(bytes > i->count))
+ bytes = i->count;
+
+ if (unlikely(!bytes))
+ return 0;
+
+ sum = *csum;
+ iterate_and_advance(i, bytes, v, ({
+ int err = 0;
+ next = csum_and_copy_from_user(v.iov_base,
+ (to += v.iov_len) - v.iov_len,
+ v.iov_len, 0, &err);
+ if (!err) {
+ sum = csum_block_add(sum, next, off);
+ off += v.iov_len;
+ }
+ err ? v.iov_len : 0;
+ }), ({
+ char *p = kmap_atomic(v.bv_page);
+ next = csum_partial_copy_nocheck(p + v.bv_offset,
+ (to += v.bv_len) - v.bv_len,
+ v.bv_len, 0);
+ kunmap_atomic(p);
+ sum = csum_block_add(sum, next, off);
+ off += v.bv_len;
+ }),({
+ next = csum_partial_copy_nocheck(v.iov_base,
+ (to += v.iov_len) - v.iov_len,
+ v.iov_len, 0);
+ sum = csum_block_add(sum, next, off);
+ off += v.iov_len;
+ })
+ )
+ *csum = sum;
+ return bytes;
+}
+EXPORT_SYMBOL(csum_and_copy_from_iter);
+
+size_t csum_and_copy_to_iter(void *addr, size_t bytes, __wsum *csum,
+ struct iov_iter *i)
+{
+ char *from = addr;
+ __wsum sum, next;
+ size_t off = 0;
+ if (unlikely(bytes > i->count))
+ bytes = i->count;
+
+ if (unlikely(!bytes))
+ return 0;
+
+ sum = *csum;
+ iterate_and_advance(i, bytes, v, ({
+ int err = 0;
+ next = csum_and_copy_to_user((from += v.iov_len) - v.iov_len,
+ v.iov_base,
+ v.iov_len, 0, &err);
+ if (!err) {
+ sum = csum_block_add(sum, next, off);
+ off += v.iov_len;
+ }
+ err ? v.iov_len : 0;
+ }), ({
+ char *p = kmap_atomic(v.bv_page);
+ next = csum_partial_copy_nocheck((from += v.bv_len) - v.bv_len,
+ p + v.bv_offset,
+ v.bv_len, 0);
+ kunmap_atomic(p);
+ sum = csum_block_add(sum, next, off);
+ off += v.bv_len;
+ }),({
+ next = csum_partial_copy_nocheck((from += v.iov_len) - v.iov_len,
+ v.iov_base,
+ v.iov_len, 0);
+ sum = csum_block_add(sum, next, off);
+ off += v.iov_len;
+ })
+ )
+ *csum = sum;
+ return bytes;
+}
+EXPORT_SYMBOL(csum_and_copy_to_iter);
+
+int iov_iter_npages(const struct iov_iter *i, int maxpages)
+{
+ size_t size = i->count;
+ int npages = 0;
+
+ if (!size)
+ return 0;
+
+ iterate_all_kinds(i, size, v, ({
+ unsigned long p = (unsigned long)v.iov_base;
+ npages += DIV_ROUND_UP(p + v.iov_len, PAGE_SIZE)
+ - p / PAGE_SIZE;
+ if (npages >= maxpages)
+ return maxpages;
+ 0;}),({
+ npages++;
+ if (npages >= maxpages)
+ return maxpages;
+ }),({
+ unsigned long p = (unsigned long)v.iov_base;
+ npages += DIV_ROUND_UP(p + v.iov_len, PAGE_SIZE)
+ - p / PAGE_SIZE;
+ if (npages >= maxpages)
+ return maxpages;
+ })
+ )
+ return npages;
+}
+EXPORT_SYMBOL(iov_iter_npages);
+
+const void *dup_iter(struct iov_iter *new, struct iov_iter *old, gfp_t flags)
+{
+ *new = *old;
+ if (new->type & ITER_BVEC)
+ return new->bvec = kmemdup(new->bvec,
+ new->nr_segs * sizeof(struct bio_vec),
+ flags);
+ else
+ /* iovec and kvec have identical layout */
+ return new->iov = kmemdup(new->iov,
+ new->nr_segs * sizeof(struct iovec),
+ flags);
+}
+EXPORT_SYMBOL(dup_iter);
return 0;
}
EXPORT_SYMBOL_GPL(lcm);
+
+unsigned long lcm_not_zero(unsigned long a, unsigned long b)
+{
+ unsigned long l = lcm(a, b);
+
+ if (l)
+ return l;
+
+ return (b ? : a);
+}
+EXPORT_SYMBOL_GPL(lcm_not_zero);
/* Error: request to write beyond destination buffer */
if (cpy > oend)
goto _output_error;
+ if ((ref + COPYLENGTH) > oend ||
+ (op + COPYLENGTH) > oend)
+ goto _output_error;
LZ4_SECURECOPY(ref, op, (oend - COPYLENGTH));
while (op < cpy)
*op++ = *ref++;
int minlen = min_t(int, count, nla_len(src));
memcpy(dest, nla_data(src), minlen);
+ if (count > minlen)
+ memset(dest + minlen, 0, count - minlen);
return minlen;
}
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/log2.h>
+#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
static struct bucket_table *bucket_table_alloc(struct rhashtable *ht,
size_t nbuckets)
{
- struct bucket_table *tbl;
+ struct bucket_table *tbl = NULL;
size_t size;
int i;
size = sizeof(*tbl) + nbuckets * sizeof(tbl->buckets[0]);
- tbl = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
+ if (size <= (PAGE_SIZE << PAGE_ALLOC_COSTLY_ORDER))
+ tbl = kzalloc(size, GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY);
if (tbl == NULL)
tbl = vzalloc(size);
-
if (tbl == NULL)
return NULL;
* @ht: hash table
* @new_size: new table size
*/
-bool rht_grow_above_75(const struct rhashtable *ht, size_t new_size)
+static bool rht_grow_above_75(const struct rhashtable *ht, size_t new_size)
{
/* Expand table when exceeding 75% load */
return atomic_read(&ht->nelems) > (new_size / 4 * 3) &&
- (ht->p.max_shift && atomic_read(&ht->shift) < ht->p.max_shift);
+ (!ht->p.max_shift || atomic_read(&ht->shift) < ht->p.max_shift);
}
-EXPORT_SYMBOL_GPL(rht_grow_above_75);
/**
* rht_shrink_below_30 - returns true if nelems < 0.3 * table-size
* @ht: hash table
* @new_size: new table size
*/
-bool rht_shrink_below_30(const struct rhashtable *ht, size_t new_size)
+static bool rht_shrink_below_30(const struct rhashtable *ht, size_t new_size)
{
/* Shrink table beneath 30% load */
return atomic_read(&ht->nelems) < (new_size * 3 / 10) &&
(atomic_read(&ht->shift) > ht->p.min_shift);
}
-EXPORT_SYMBOL_GPL(rht_shrink_below_30);
static void lock_buckets(struct bucket_table *new_tbl,
struct bucket_table *old_tbl, unsigned int hash)
}
}
unlock_buckets(new_tbl, old_tbl, new_hash);
+ cond_resched();
}
/* Unzip interleaved hash chains */
complete = false;
unlock_buckets(new_tbl, old_tbl, old_hash);
+ cond_resched();
}
}
tbl->buckets[new_hash + new_tbl->size]);
unlock_buckets(new_tbl, tbl, new_hash);
+ cond_resched();
}
/* Publish the new, valid hash table */
list_for_each_entry(walker, &ht->walkers, list)
walker->resize = true;
- if (ht->p.grow_decision && ht->p.grow_decision(ht, tbl->size))
+ if (rht_grow_above_75(ht, tbl->size))
rhashtable_expand(ht);
- else if (ht->p.shrink_decision && ht->p.shrink_decision(ht, tbl->size))
+ else if (rht_shrink_below_30(ht, tbl->size))
rhashtable_shrink(ht);
-
unlock:
mutex_unlock(&ht->mutex);
}
-static void rhashtable_wakeup_worker(struct rhashtable *ht)
-{
- struct bucket_table *tbl = rht_dereference_rcu(ht->tbl, ht);
- struct bucket_table *new_tbl = rht_dereference_rcu(ht->future_tbl, ht);
- size_t size = tbl->size;
-
- /* Only adjust the table if no resizing is currently in progress. */
- if (tbl == new_tbl &&
- ((ht->p.grow_decision && ht->p.grow_decision(ht, size)) ||
- (ht->p.shrink_decision && ht->p.shrink_decision(ht, size))))
- schedule_work(&ht->run_work);
-}
-
static void __rhashtable_insert(struct rhashtable *ht, struct rhash_head *obj,
- struct bucket_table *tbl, u32 hash)
+ struct bucket_table *tbl,
+ const struct bucket_table *old_tbl, u32 hash)
{
+ bool no_resize_running = tbl == old_tbl;
struct rhash_head *head;
hash = rht_bucket_index(tbl, hash);
rcu_assign_pointer(tbl->buckets[hash], obj);
atomic_inc(&ht->nelems);
-
- rhashtable_wakeup_worker(ht);
+ if (no_resize_running && rht_grow_above_75(ht, tbl->size))
+ schedule_work(&ht->run_work);
}
/**
hash = obj_raw_hashfn(ht, rht_obj(ht, obj));
lock_buckets(tbl, old_tbl, hash);
- __rhashtable_insert(ht, obj, tbl, hash);
+ __rhashtable_insert(ht, obj, tbl, old_tbl, hash);
unlock_buckets(tbl, old_tbl, hash);
rcu_read_unlock();
unlock_buckets(new_tbl, old_tbl, new_hash);
if (ret) {
+ bool no_resize_running = new_tbl == old_tbl;
+
atomic_dec(&ht->nelems);
- rhashtable_wakeup_worker(ht);
+ if (no_resize_running && rht_shrink_below_30(ht, new_tbl->size))
+ schedule_work(&ht->run_work);
}
rcu_read_unlock();
goto exit;
}
- __rhashtable_insert(ht, obj, new_tbl, new_hash);
+ __rhashtable_insert(ht, obj, new_tbl, old_tbl, new_hash);
exit:
unlock_buckets(new_tbl, old_tbl, new_hash);
if (!iter->walker)
return -ENOMEM;
+ INIT_LIST_HEAD(&iter->walker->list);
+ iter->walker->resize = false;
+
mutex_lock(&ht->mutex);
list_add(&iter->walker->list, &ht->walkers);
mutex_unlock(&ht->mutex);
if (!ht->p.hash_rnd)
get_random_bytes(&ht->p.hash_rnd, sizeof(ht->p.hash_rnd));
- if (ht->p.grow_decision || ht->p.shrink_decision)
- INIT_WORK(&ht->run_work, rht_deferred_worker);
+ INIT_WORK(&ht->run_work, rht_deferred_worker);
return 0;
}
{
ht->being_destroyed = true;
- if (ht->p.grow_decision || ht->p.shrink_decision)
- cancel_work_sync(&ht->run_work);
+ cancel_work_sync(&ht->run_work);
mutex_lock(&ht->mutex);
bucket_table_free(rht_dereference(ht->tbl, ht));
if (s->len < s->size) {
len = vsnprintf(s->buffer + s->len, s->size - s->len, fmt, args);
- if (seq_buf_can_fit(s, len)) {
+ if (s->len + len < s->size) {
s->len += len;
return 0;
}
if (s->len < s->size) {
ret = bstr_printf(s->buffer + s->len, len, fmt, binary);
- if (seq_buf_can_fit(s, ret)) {
+ if (s->len + ret < s->size) {
s->len += ret;
return 0;
}
return err;
}
+static struct rhashtable ht;
+
static int __init test_rht_init(void)
{
- struct rhashtable ht;
struct rhashtable_params params = {
.nelem_hint = TEST_HT_SIZE,
.head_offset = offsetof(struct test_obj, node),
.key_offset = offsetof(struct test_obj, value),
.key_len = sizeof(int),
.hashfn = jhash,
+ .max_shift = 1, /* we expand/shrink manually here */
.nulls_base = (3U << RHT_BASE_SHIFT),
- .grow_decision = rht_grow_above_75,
- .shrink_decision = rht_shrink_below_30,
};
int err;
return err;
}
+static void __exit test_rht_exit(void)
+{
+}
+
module_init(test_rht_init);
+module_exit(test_rht_exit);
MODULE_LICENSE("GPL v2");
mm_init.o mmu_context.o percpu.o slab_common.o \
compaction.o vmacache.o \
interval_tree.o list_lru.o workingset.o \
- iov_iter.o debug.o $(mmu-y)
+ debug.o $(mmu-y)
obj-y += init-mm.o
return (1UL << (align_order - cma->order_per_bit)) - 1;
}
+/*
+ * Find a PFN aligned to the specified order and return an offset represented in
+ * order_per_bits.
+ */
static unsigned long cma_bitmap_aligned_offset(struct cma *cma, int align_order)
{
- unsigned int alignment;
-
if (align_order <= cma->order_per_bit)
return 0;
- alignment = 1UL << (align_order - cma->order_per_bit);
- return ALIGN(cma->base_pfn, alignment) -
- (cma->base_pfn >> cma->order_per_bit);
+
+ return (ALIGN(cma->base_pfn, (1UL << align_order))
+ - cma->base_pfn) >> cma->order_per_bit;
}
static unsigned long cma_bitmap_maxno(struct cma *cma)
int target_nid, last_cpupid = -1;
bool page_locked;
bool migrated = false;
+ bool was_writable;
int flags = 0;
/* A PROT_NONE fault should not end up here */
flags |= TNF_FAULT_LOCAL;
}
- /*
- * Avoid grouping on DSO/COW pages in specific and RO pages
- * in general, RO pages shouldn't hurt as much anyway since
- * they can be in shared cache state.
- */
- if (!pmd_write(pmd))
+ /* See similar comment in do_numa_page for explanation */
+ if (!(vma->vm_flags & VM_WRITE))
flags |= TNF_NO_GROUP;
/*
if (migrated) {
flags |= TNF_MIGRATED;
page_nid = target_nid;
- }
+ } else
+ flags |= TNF_MIGRATE_FAIL;
goto out;
clear_pmdnuma:
BUG_ON(!PageLocked(page));
+ was_writable = pmd_write(pmd);
pmd = pmd_modify(pmd, vma->vm_page_prot);
+ pmd = pmd_mkyoung(pmd);
+ if (was_writable)
+ pmd = pmd_mkwrite(pmd);
set_pmd_at(mm, haddr, pmdp, pmd);
update_mmu_cache_pmd(vma, addr, pmdp);
unlock_page(page);
if (__pmd_trans_huge_lock(pmd, vma, &ptl) == 1) {
pmd_t entry;
+ bool preserve_write = prot_numa && pmd_write(*pmd);
+ ret = 1;
/*
* Avoid trapping faults against the zero page. The read-only
*/
if (prot_numa && is_huge_zero_pmd(*pmd)) {
spin_unlock(ptl);
- return 0;
+ return ret;
}
if (!prot_numa || !pmd_protnone(*pmd)) {
- ret = 1;
entry = pmdp_get_and_clear_notify(mm, addr, pmd);
entry = pmd_modify(entry, newprot);
+ if (preserve_write)
+ entry = pmd_mkwrite(entry);
ret = HPAGE_PMD_NR;
set_pmd_at(mm, addr, pmd, entry);
- BUG_ON(pmd_write(entry));
+ BUG_ON(!preserve_write && pmd_write(entry));
}
spin_unlock(ptl);
}
__SetPageHead(page);
__ClearPageReserved(page);
for (i = 1; i < nr_pages; i++, p = mem_map_next(p, page, i)) {
- __SetPageTail(p);
/*
* For gigantic hugepages allocated through bootmem at
* boot, it's safer to be consistent with the not-gigantic
__ClearPageReserved(p);
set_page_count(p, 0);
p->first_page = page;
+ /* Make sure p->first_page is always valid for PageTail() */
+ smp_wmb();
+ __SetPageTail(p);
}
}
+++ /dev/null
-#include <linux/export.h>
-#include <linux/uio.h>
-#include <linux/pagemap.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <net/checksum.h>
-
-#define iterate_iovec(i, n, __v, __p, skip, STEP) { \
- size_t left; \
- size_t wanted = n; \
- __p = i->iov; \
- __v.iov_len = min(n, __p->iov_len - skip); \
- if (likely(__v.iov_len)) { \
- __v.iov_base = __p->iov_base + skip; \
- left = (STEP); \
- __v.iov_len -= left; \
- skip += __v.iov_len; \
- n -= __v.iov_len; \
- } else { \
- left = 0; \
- } \
- while (unlikely(!left && n)) { \
- __p++; \
- __v.iov_len = min(n, __p->iov_len); \
- if (unlikely(!__v.iov_len)) \
- continue; \
- __v.iov_base = __p->iov_base; \
- left = (STEP); \
- __v.iov_len -= left; \
- skip = __v.iov_len; \
- n -= __v.iov_len; \
- } \
- n = wanted - n; \
-}
-
-#define iterate_kvec(i, n, __v, __p, skip, STEP) { \
- size_t wanted = n; \
- __p = i->kvec; \
- __v.iov_len = min(n, __p->iov_len - skip); \
- if (likely(__v.iov_len)) { \
- __v.iov_base = __p->iov_base + skip; \
- (void)(STEP); \
- skip += __v.iov_len; \
- n -= __v.iov_len; \
- } \
- while (unlikely(n)) { \
- __p++; \
- __v.iov_len = min(n, __p->iov_len); \
- if (unlikely(!__v.iov_len)) \
- continue; \
- __v.iov_base = __p->iov_base; \
- (void)(STEP); \
- skip = __v.iov_len; \
- n -= __v.iov_len; \
- } \
- n = wanted; \
-}
-
-#define iterate_bvec(i, n, __v, __p, skip, STEP) { \
- size_t wanted = n; \
- __p = i->bvec; \
- __v.bv_len = min_t(size_t, n, __p->bv_len - skip); \
- if (likely(__v.bv_len)) { \
- __v.bv_page = __p->bv_page; \
- __v.bv_offset = __p->bv_offset + skip; \
- (void)(STEP); \
- skip += __v.bv_len; \
- n -= __v.bv_len; \
- } \
- while (unlikely(n)) { \
- __p++; \
- __v.bv_len = min_t(size_t, n, __p->bv_len); \
- if (unlikely(!__v.bv_len)) \
- continue; \
- __v.bv_page = __p->bv_page; \
- __v.bv_offset = __p->bv_offset; \
- (void)(STEP); \
- skip = __v.bv_len; \
- n -= __v.bv_len; \
- } \
- n = wanted; \
-}
-
-#define iterate_all_kinds(i, n, v, I, B, K) { \
- size_t skip = i->iov_offset; \
- if (unlikely(i->type & ITER_BVEC)) { \
- const struct bio_vec *bvec; \
- struct bio_vec v; \
- iterate_bvec(i, n, v, bvec, skip, (B)) \
- } else if (unlikely(i->type & ITER_KVEC)) { \
- const struct kvec *kvec; \
- struct kvec v; \
- iterate_kvec(i, n, v, kvec, skip, (K)) \
- } else { \
- const struct iovec *iov; \
- struct iovec v; \
- iterate_iovec(i, n, v, iov, skip, (I)) \
- } \
-}
-
-#define iterate_and_advance(i, n, v, I, B, K) { \
- size_t skip = i->iov_offset; \
- if (unlikely(i->type & ITER_BVEC)) { \
- const struct bio_vec *bvec; \
- struct bio_vec v; \
- iterate_bvec(i, n, v, bvec, skip, (B)) \
- if (skip == bvec->bv_len) { \
- bvec++; \
- skip = 0; \
- } \
- i->nr_segs -= bvec - i->bvec; \
- i->bvec = bvec; \
- } else if (unlikely(i->type & ITER_KVEC)) { \
- const struct kvec *kvec; \
- struct kvec v; \
- iterate_kvec(i, n, v, kvec, skip, (K)) \
- if (skip == kvec->iov_len) { \
- kvec++; \
- skip = 0; \
- } \
- i->nr_segs -= kvec - i->kvec; \
- i->kvec = kvec; \
- } else { \
- const struct iovec *iov; \
- struct iovec v; \
- iterate_iovec(i, n, v, iov, skip, (I)) \
- if (skip == iov->iov_len) { \
- iov++; \
- skip = 0; \
- } \
- i->nr_segs -= iov - i->iov; \
- i->iov = iov; \
- } \
- i->count -= n; \
- i->iov_offset = skip; \
-}
-
-static size_t copy_page_to_iter_iovec(struct page *page, size_t offset, size_t bytes,
- struct iov_iter *i)
-{
- size_t skip, copy, left, wanted;
- const struct iovec *iov;
- char __user *buf;
- void *kaddr, *from;
-
- if (unlikely(bytes > i->count))
- bytes = i->count;
-
- if (unlikely(!bytes))
- return 0;
-
- wanted = bytes;
- iov = i->iov;
- skip = i->iov_offset;
- buf = iov->iov_base + skip;
- copy = min(bytes, iov->iov_len - skip);
-
- if (!fault_in_pages_writeable(buf, copy)) {
- kaddr = kmap_atomic(page);
- from = kaddr + offset;
-
- /* first chunk, usually the only one */
- left = __copy_to_user_inatomic(buf, from, copy);
- copy -= left;
- skip += copy;
- from += copy;
- bytes -= copy;
-
- while (unlikely(!left && bytes)) {
- iov++;
- buf = iov->iov_base;
- copy = min(bytes, iov->iov_len);
- left = __copy_to_user_inatomic(buf, from, copy);
- copy -= left;
- skip = copy;
- from += copy;
- bytes -= copy;
- }
- if (likely(!bytes)) {
- kunmap_atomic(kaddr);
- goto done;
- }
- offset = from - kaddr;
- buf += copy;
- kunmap_atomic(kaddr);
- copy = min(bytes, iov->iov_len - skip);
- }
- /* Too bad - revert to non-atomic kmap */
- kaddr = kmap(page);
- from = kaddr + offset;
- left = __copy_to_user(buf, from, copy);
- copy -= left;
- skip += copy;
- from += copy;
- bytes -= copy;
- while (unlikely(!left && bytes)) {
- iov++;
- buf = iov->iov_base;
- copy = min(bytes, iov->iov_len);
- left = __copy_to_user(buf, from, copy);
- copy -= left;
- skip = copy;
- from += copy;
- bytes -= copy;
- }
- kunmap(page);
-done:
- if (skip == iov->iov_len) {
- iov++;
- skip = 0;
- }
- i->count -= wanted - bytes;
- i->nr_segs -= iov - i->iov;
- i->iov = iov;
- i->iov_offset = skip;
- return wanted - bytes;
-}
-
-static size_t copy_page_from_iter_iovec(struct page *page, size_t offset, size_t bytes,
- struct iov_iter *i)
-{
- size_t skip, copy, left, wanted;
- const struct iovec *iov;
- char __user *buf;
- void *kaddr, *to;
-
- if (unlikely(bytes > i->count))
- bytes = i->count;
-
- if (unlikely(!bytes))
- return 0;
-
- wanted = bytes;
- iov = i->iov;
- skip = i->iov_offset;
- buf = iov->iov_base + skip;
- copy = min(bytes, iov->iov_len - skip);
-
- if (!fault_in_pages_readable(buf, copy)) {
- kaddr = kmap_atomic(page);
- to = kaddr + offset;
-
- /* first chunk, usually the only one */
- left = __copy_from_user_inatomic(to, buf, copy);
- copy -= left;
- skip += copy;
- to += copy;
- bytes -= copy;
-
- while (unlikely(!left && bytes)) {
- iov++;
- buf = iov->iov_base;
- copy = min(bytes, iov->iov_len);
- left = __copy_from_user_inatomic(to, buf, copy);
- copy -= left;
- skip = copy;
- to += copy;
- bytes -= copy;
- }
- if (likely(!bytes)) {
- kunmap_atomic(kaddr);
- goto done;
- }
- offset = to - kaddr;
- buf += copy;
- kunmap_atomic(kaddr);
- copy = min(bytes, iov->iov_len - skip);
- }
- /* Too bad - revert to non-atomic kmap */
- kaddr = kmap(page);
- to = kaddr + offset;
- left = __copy_from_user(to, buf, copy);
- copy -= left;
- skip += copy;
- to += copy;
- bytes -= copy;
- while (unlikely(!left && bytes)) {
- iov++;
- buf = iov->iov_base;
- copy = min(bytes, iov->iov_len);
- left = __copy_from_user(to, buf, copy);
- copy -= left;
- skip = copy;
- to += copy;
- bytes -= copy;
- }
- kunmap(page);
-done:
- if (skip == iov->iov_len) {
- iov++;
- skip = 0;
- }
- i->count -= wanted - bytes;
- i->nr_segs -= iov - i->iov;
- i->iov = iov;
- i->iov_offset = skip;
- return wanted - bytes;
-}
-
-/*
- * Fault in the first iovec of the given iov_iter, to a maximum length
- * of bytes. Returns 0 on success, or non-zero if the memory could not be
- * accessed (ie. because it is an invalid address).
- *
- * writev-intensive code may want this to prefault several iovecs -- that
- * would be possible (callers must not rely on the fact that _only_ the
- * first iovec will be faulted with the current implementation).
- */
-int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes)
-{
- if (!(i->type & (ITER_BVEC|ITER_KVEC))) {
- char __user *buf = i->iov->iov_base + i->iov_offset;
- bytes = min(bytes, i->iov->iov_len - i->iov_offset);
- return fault_in_pages_readable(buf, bytes);
- }
- return 0;
-}
-EXPORT_SYMBOL(iov_iter_fault_in_readable);
-
-void iov_iter_init(struct iov_iter *i, int direction,
- const struct iovec *iov, unsigned long nr_segs,
- size_t count)
-{
- /* It will get better. Eventually... */
- if (segment_eq(get_fs(), KERNEL_DS)) {
- direction |= ITER_KVEC;
- i->type = direction;
- i->kvec = (struct kvec *)iov;
- } else {
- i->type = direction;
- i->iov = iov;
- }
- i->nr_segs = nr_segs;
- i->iov_offset = 0;
- i->count = count;
-}
-EXPORT_SYMBOL(iov_iter_init);
-
-static void memcpy_from_page(char *to, struct page *page, size_t offset, size_t len)
-{
- char *from = kmap_atomic(page);
- memcpy(to, from + offset, len);
- kunmap_atomic(from);
-}
-
-static void memcpy_to_page(struct page *page, size_t offset, char *from, size_t len)
-{
- char *to = kmap_atomic(page);
- memcpy(to + offset, from, len);
- kunmap_atomic(to);
-}
-
-static void memzero_page(struct page *page, size_t offset, size_t len)
-{
- char *addr = kmap_atomic(page);
- memset(addr + offset, 0, len);
- kunmap_atomic(addr);
-}
-
-size_t copy_to_iter(void *addr, size_t bytes, struct iov_iter *i)
-{
- char *from = addr;
- if (unlikely(bytes > i->count))
- bytes = i->count;
-
- if (unlikely(!bytes))
- return 0;
-
- iterate_and_advance(i, bytes, v,
- __copy_to_user(v.iov_base, (from += v.iov_len) - v.iov_len,
- v.iov_len),
- memcpy_to_page(v.bv_page, v.bv_offset,
- (from += v.bv_len) - v.bv_len, v.bv_len),
- memcpy(v.iov_base, (from += v.iov_len) - v.iov_len, v.iov_len)
- )
-
- return bytes;
-}
-EXPORT_SYMBOL(copy_to_iter);
-
-size_t copy_from_iter(void *addr, size_t bytes, struct iov_iter *i)
-{
- char *to = addr;
- if (unlikely(bytes > i->count))
- bytes = i->count;
-
- if (unlikely(!bytes))
- return 0;
-
- iterate_and_advance(i, bytes, v,
- __copy_from_user((to += v.iov_len) - v.iov_len, v.iov_base,
- v.iov_len),
- memcpy_from_page((to += v.bv_len) - v.bv_len, v.bv_page,
- v.bv_offset, v.bv_len),
- memcpy((to += v.iov_len) - v.iov_len, v.iov_base, v.iov_len)
- )
-
- return bytes;
-}
-EXPORT_SYMBOL(copy_from_iter);
-
-size_t copy_from_iter_nocache(void *addr, size_t bytes, struct iov_iter *i)
-{
- char *to = addr;
- if (unlikely(bytes > i->count))
- bytes = i->count;
-
- if (unlikely(!bytes))
- return 0;
-
- iterate_and_advance(i, bytes, v,
- __copy_from_user_nocache((to += v.iov_len) - v.iov_len,
- v.iov_base, v.iov_len),
- memcpy_from_page((to += v.bv_len) - v.bv_len, v.bv_page,
- v.bv_offset, v.bv_len),
- memcpy((to += v.iov_len) - v.iov_len, v.iov_base, v.iov_len)
- )
-
- return bytes;
-}
-EXPORT_SYMBOL(copy_from_iter_nocache);
-
-size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes,
- struct iov_iter *i)
-{
- if (i->type & (ITER_BVEC|ITER_KVEC)) {
- void *kaddr = kmap_atomic(page);
- size_t wanted = copy_to_iter(kaddr + offset, bytes, i);
- kunmap_atomic(kaddr);
- return wanted;
- } else
- return copy_page_to_iter_iovec(page, offset, bytes, i);
-}
-EXPORT_SYMBOL(copy_page_to_iter);
-
-size_t copy_page_from_iter(struct page *page, size_t offset, size_t bytes,
- struct iov_iter *i)
-{
- if (i->type & (ITER_BVEC|ITER_KVEC)) {
- void *kaddr = kmap_atomic(page);
- size_t wanted = copy_from_iter(kaddr + offset, bytes, i);
- kunmap_atomic(kaddr);
- return wanted;
- } else
- return copy_page_from_iter_iovec(page, offset, bytes, i);
-}
-EXPORT_SYMBOL(copy_page_from_iter);
-
-size_t iov_iter_zero(size_t bytes, struct iov_iter *i)
-{
- if (unlikely(bytes > i->count))
- bytes = i->count;
-
- if (unlikely(!bytes))
- return 0;
-
- iterate_and_advance(i, bytes, v,
- __clear_user(v.iov_base, v.iov_len),
- memzero_page(v.bv_page, v.bv_offset, v.bv_len),
- memset(v.iov_base, 0, v.iov_len)
- )
-
- return bytes;
-}
-EXPORT_SYMBOL(iov_iter_zero);
-
-size_t iov_iter_copy_from_user_atomic(struct page *page,
- struct iov_iter *i, unsigned long offset, size_t bytes)
-{
- char *kaddr = kmap_atomic(page), *p = kaddr + offset;
- iterate_all_kinds(i, bytes, v,
- __copy_from_user_inatomic((p += v.iov_len) - v.iov_len,
- v.iov_base, v.iov_len),
- memcpy_from_page((p += v.bv_len) - v.bv_len, v.bv_page,
- v.bv_offset, v.bv_len),
- memcpy((p += v.iov_len) - v.iov_len, v.iov_base, v.iov_len)
- )
- kunmap_atomic(kaddr);
- return bytes;
-}
-EXPORT_SYMBOL(iov_iter_copy_from_user_atomic);
-
-void iov_iter_advance(struct iov_iter *i, size_t size)
-{
- iterate_and_advance(i, size, v, 0, 0, 0)
-}
-EXPORT_SYMBOL(iov_iter_advance);
-
-/*
- * Return the count of just the current iov_iter segment.
- */
-size_t iov_iter_single_seg_count(const struct iov_iter *i)
-{
- if (i->nr_segs == 1)
- return i->count;
- else if (i->type & ITER_BVEC)
- return min(i->count, i->bvec->bv_len - i->iov_offset);
- else
- return min(i->count, i->iov->iov_len - i->iov_offset);
-}
-EXPORT_SYMBOL(iov_iter_single_seg_count);
-
-void iov_iter_kvec(struct iov_iter *i, int direction,
- const struct kvec *kvec, unsigned long nr_segs,
- size_t count)
-{
- BUG_ON(!(direction & ITER_KVEC));
- i->type = direction;
- i->kvec = kvec;
- i->nr_segs = nr_segs;
- i->iov_offset = 0;
- i->count = count;
-}
-EXPORT_SYMBOL(iov_iter_kvec);
-
-void iov_iter_bvec(struct iov_iter *i, int direction,
- const struct bio_vec *bvec, unsigned long nr_segs,
- size_t count)
-{
- BUG_ON(!(direction & ITER_BVEC));
- i->type = direction;
- i->bvec = bvec;
- i->nr_segs = nr_segs;
- i->iov_offset = 0;
- i->count = count;
-}
-EXPORT_SYMBOL(iov_iter_bvec);
-
-unsigned long iov_iter_alignment(const struct iov_iter *i)
-{
- unsigned long res = 0;
- size_t size = i->count;
-
- if (!size)
- return 0;
-
- iterate_all_kinds(i, size, v,
- (res |= (unsigned long)v.iov_base | v.iov_len, 0),
- res |= v.bv_offset | v.bv_len,
- res |= (unsigned long)v.iov_base | v.iov_len
- )
- return res;
-}
-EXPORT_SYMBOL(iov_iter_alignment);
-
-ssize_t iov_iter_get_pages(struct iov_iter *i,
- struct page **pages, size_t maxsize, unsigned maxpages,
- size_t *start)
-{
- if (maxsize > i->count)
- maxsize = i->count;
-
- if (!maxsize)
- return 0;
-
- iterate_all_kinds(i, maxsize, v, ({
- unsigned long addr = (unsigned long)v.iov_base;
- size_t len = v.iov_len + (*start = addr & (PAGE_SIZE - 1));
- int n;
- int res;
-
- if (len > maxpages * PAGE_SIZE)
- len = maxpages * PAGE_SIZE;
- addr &= ~(PAGE_SIZE - 1);
- n = DIV_ROUND_UP(len, PAGE_SIZE);
- res = get_user_pages_fast(addr, n, (i->type & WRITE) != WRITE, pages);
- if (unlikely(res < 0))
- return res;
- return (res == n ? len : res * PAGE_SIZE) - *start;
- 0;}),({
- /* can't be more than PAGE_SIZE */
- *start = v.bv_offset;
- get_page(*pages = v.bv_page);
- return v.bv_len;
- }),({
- return -EFAULT;
- })
- )
- return 0;
-}
-EXPORT_SYMBOL(iov_iter_get_pages);
-
-static struct page **get_pages_array(size_t n)
-{
- struct page **p = kmalloc(n * sizeof(struct page *), GFP_KERNEL);
- if (!p)
- p = vmalloc(n * sizeof(struct page *));
- return p;
-}
-
-ssize_t iov_iter_get_pages_alloc(struct iov_iter *i,
- struct page ***pages, size_t maxsize,
- size_t *start)
-{
- struct page **p;
-
- if (maxsize > i->count)
- maxsize = i->count;
-
- if (!maxsize)
- return 0;
-
- iterate_all_kinds(i, maxsize, v, ({
- unsigned long addr = (unsigned long)v.iov_base;
- size_t len = v.iov_len + (*start = addr & (PAGE_SIZE - 1));
- int n;
- int res;
-
- addr &= ~(PAGE_SIZE - 1);
- n = DIV_ROUND_UP(len, PAGE_SIZE);
- p = get_pages_array(n);
- if (!p)
- return -ENOMEM;
- res = get_user_pages_fast(addr, n, (i->type & WRITE) != WRITE, p);
- if (unlikely(res < 0)) {
- kvfree(p);
- return res;
- }
- *pages = p;
- return (res == n ? len : res * PAGE_SIZE) - *start;
- 0;}),({
- /* can't be more than PAGE_SIZE */
- *start = v.bv_offset;
- *pages = p = get_pages_array(1);
- if (!p)
- return -ENOMEM;
- get_page(*p = v.bv_page);
- return v.bv_len;
- }),({
- return -EFAULT;
- })
- )
- return 0;
-}
-EXPORT_SYMBOL(iov_iter_get_pages_alloc);
-
-size_t csum_and_copy_from_iter(void *addr, size_t bytes, __wsum *csum,
- struct iov_iter *i)
-{
- char *to = addr;
- __wsum sum, next;
- size_t off = 0;
- if (unlikely(bytes > i->count))
- bytes = i->count;
-
- if (unlikely(!bytes))
- return 0;
-
- sum = *csum;
- iterate_and_advance(i, bytes, v, ({
- int err = 0;
- next = csum_and_copy_from_user(v.iov_base,
- (to += v.iov_len) - v.iov_len,
- v.iov_len, 0, &err);
- if (!err) {
- sum = csum_block_add(sum, next, off);
- off += v.iov_len;
- }
- err ? v.iov_len : 0;
- }), ({
- char *p = kmap_atomic(v.bv_page);
- next = csum_partial_copy_nocheck(p + v.bv_offset,
- (to += v.bv_len) - v.bv_len,
- v.bv_len, 0);
- kunmap_atomic(p);
- sum = csum_block_add(sum, next, off);
- off += v.bv_len;
- }),({
- next = csum_partial_copy_nocheck(v.iov_base,
- (to += v.iov_len) - v.iov_len,
- v.iov_len, 0);
- sum = csum_block_add(sum, next, off);
- off += v.iov_len;
- })
- )
- *csum = sum;
- return bytes;
-}
-EXPORT_SYMBOL(csum_and_copy_from_iter);
-
-size_t csum_and_copy_to_iter(void *addr, size_t bytes, __wsum *csum,
- struct iov_iter *i)
-{
- char *from = addr;
- __wsum sum, next;
- size_t off = 0;
- if (unlikely(bytes > i->count))
- bytes = i->count;
-
- if (unlikely(!bytes))
- return 0;
-
- sum = *csum;
- iterate_and_advance(i, bytes, v, ({
- int err = 0;
- next = csum_and_copy_to_user((from += v.iov_len) - v.iov_len,
- v.iov_base,
- v.iov_len, 0, &err);
- if (!err) {
- sum = csum_block_add(sum, next, off);
- off += v.iov_len;
- }
- err ? v.iov_len : 0;
- }), ({
- char *p = kmap_atomic(v.bv_page);
- next = csum_partial_copy_nocheck((from += v.bv_len) - v.bv_len,
- p + v.bv_offset,
- v.bv_len, 0);
- kunmap_atomic(p);
- sum = csum_block_add(sum, next, off);
- off += v.bv_len;
- }),({
- next = csum_partial_copy_nocheck((from += v.iov_len) - v.iov_len,
- v.iov_base,
- v.iov_len, 0);
- sum = csum_block_add(sum, next, off);
- off += v.iov_len;
- })
- )
- *csum = sum;
- return bytes;
-}
-EXPORT_SYMBOL(csum_and_copy_to_iter);
-
-int iov_iter_npages(const struct iov_iter *i, int maxpages)
-{
- size_t size = i->count;
- int npages = 0;
-
- if (!size)
- return 0;
-
- iterate_all_kinds(i, size, v, ({
- unsigned long p = (unsigned long)v.iov_base;
- npages += DIV_ROUND_UP(p + v.iov_len, PAGE_SIZE)
- - p / PAGE_SIZE;
- if (npages >= maxpages)
- return maxpages;
- 0;}),({
- npages++;
- if (npages >= maxpages)
- return maxpages;
- }),({
- unsigned long p = (unsigned long)v.iov_base;
- npages += DIV_ROUND_UP(p + v.iov_len, PAGE_SIZE)
- - p / PAGE_SIZE;
- if (npages >= maxpages)
- return maxpages;
- })
- )
- return npages;
-}
-EXPORT_SYMBOL(iov_iter_npages);
#include <linux/stacktrace.h>
#include <linux/string.h>
#include <linux/types.h>
+#include <linux/vmalloc.h>
#include <linux/kasan.h>
#include "kasan.h"
GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO,
PAGE_KERNEL, VM_NO_GUARD, NUMA_NO_NODE,
__builtin_return_address(0));
- return ret ? 0 : -ENOMEM;
+
+ if (ret) {
+ find_vm_area(addr)->flags |= VM_KASAN;
+ return 0;
+ }
+
+ return -ENOMEM;
}
-void kasan_module_free(void *addr)
+void kasan_free_shadow(const struct vm_struct *vm)
{
- vfree(kasan_mem_to_shadow(addr));
+ if (vm->flags & VM_KASAN)
+ vfree(kasan_mem_to_shadow(vm->addr));
}
static void register_global(struct kasan_global *global)
* on for the root memcg is enough.
*/
if (cgroup_on_dfl(root_css->cgroup))
- mem_cgroup_from_css(root_css)->use_hierarchy = true;
+ root_mem_cgroup->use_hierarchy = true;
+ else
+ root_mem_cgroup->use_hierarchy = false;
}
static u64 memory_current_read(struct cgroup_subsys_state *css,
unsigned long low = ACCESS_ONCE(memcg->low);
if (low == PAGE_COUNTER_MAX)
- seq_puts(m, "infinity\n");
+ seq_puts(m, "max\n");
else
seq_printf(m, "%llu\n", (u64)low * PAGE_SIZE);
int err;
buf = strstrip(buf);
- err = page_counter_memparse(buf, "infinity", &low);
+ err = page_counter_memparse(buf, "max", &low);
if (err)
return err;
unsigned long high = ACCESS_ONCE(memcg->high);
if (high == PAGE_COUNTER_MAX)
- seq_puts(m, "infinity\n");
+ seq_puts(m, "max\n");
else
seq_printf(m, "%llu\n", (u64)high * PAGE_SIZE);
int err;
buf = strstrip(buf);
- err = page_counter_memparse(buf, "infinity", &high);
+ err = page_counter_memparse(buf, "max", &high);
if (err)
return err;
unsigned long max = ACCESS_ONCE(memcg->memory.limit);
if (max == PAGE_COUNTER_MAX)
- seq_puts(m, "infinity\n");
+ seq_puts(m, "max\n");
else
seq_printf(m, "%llu\n", (u64)max * PAGE_SIZE);
int err;
buf = strstrip(buf);
- err = page_counter_memparse(buf, "infinity", &max);
+ err = page_counter_memparse(buf, "max", &max);
if (err)
return err;
if (memcg == root_mem_cgroup)
return false;
- if (page_counter_read(&memcg->memory) > memcg->low)
+ if (page_counter_read(&memcg->memory) >= memcg->low)
return false;
while (memcg != root) {
if (memcg == root_mem_cgroup)
break;
- if (page_counter_read(&memcg->memory) > memcg->low)
+ if (page_counter_read(&memcg->memory) >= memcg->low)
return false;
}
return true;
int last_cpupid;
int target_nid;
bool migrated = false;
+ bool was_writable = pte_write(pte);
int flags = 0;
/* A PROT_NONE fault should not end up here */
/* Make it present again */
pte = pte_modify(pte, vma->vm_page_prot);
pte = pte_mkyoung(pte);
+ if (was_writable)
+ pte = pte_mkwrite(pte);
set_pte_at(mm, addr, ptep, pte);
update_mmu_cache(vma, addr, ptep);
}
/*
- * Avoid grouping on DSO/COW pages in specific and RO pages
- * in general, RO pages shouldn't hurt as much anyway since
- * they can be in shared cache state.
+ * Avoid grouping on RO pages in general. RO pages shouldn't hurt as
+ * much anyway since they can be in shared cache state. This misses
+ * the case where a mapping is writable but the process never writes
+ * to it but pte_write gets cleared during protection updates and
+ * pte_dirty has unpredictable behaviour between PTE scan updates,
+ * background writeback, dirty balancing and application behaviour.
*/
- if (!pte_write(pte))
+ if (!(vma->vm_flags & VM_WRITE))
flags |= TNF_NO_GROUP;
/*
if (migrated) {
page_nid = target_nid;
flags |= TNF_MIGRATED;
- }
+ } else
+ flags |= TNF_MIGRATE_FAIL;
out:
if (page_nid != -1)
return NULL;
arch_refresh_nodedata(nid, pgdat);
+ } else {
+ /* Reset the nr_zones and classzone_idx to 0 before reuse */
+ pgdat->nr_zones = 0;
+ pgdat->classzone_idx = 0;
}
/* we can use NODE_DATA(nid) from here */
if (is_vmalloc_addr(zone->wait_table))
vfree(zone->wait_table);
}
-
- /*
- * Since there is no way to guarentee the address of pgdat/zone is not
- * on stack of any kernel threads or used by other kernel objects
- * without reference counting or other symchronizing method, do not
- * reset node_data and free pgdat here. Just reset it to 0 and reuse
- * the memory when the node is online again.
- */
- memset(pgdat, 0, sizeof(*pgdat));
}
EXPORT_SYMBOL(try_offline_node);
int can_do_mlock(void)
{
- if (capable(CAP_IPC_LOCK))
- return 1;
if (rlimit(RLIMIT_MEMLOCK) != 0)
return 1;
+ if (capable(CAP_IPC_LOCK))
+ return 1;
return 0;
}
EXPORT_SYMBOL(can_do_mlock);
importer->anon_vma = exporter->anon_vma;
error = anon_vma_clone(importer, exporter);
- if (error) {
- importer->anon_vma = NULL;
+ if (error)
return error;
- }
}
}
oldpte = *pte;
if (pte_present(oldpte)) {
pte_t ptent;
+ bool preserve_write = prot_numa && pte_write(oldpte);
/*
* Avoid trapping faults against the zero or KSM
ptent = ptep_modify_prot_start(mm, addr, pte);
ptent = pte_modify(ptent, newprot);
+ if (preserve_write)
+ ptent = pte_mkwrite(ptent);
/* Avoid taking write faults for known dirty pages */
if (dirty_accountable && pte_dirty(ptent) &&
EXPORT_SYMBOL(high_memory);
struct page *mem_map;
unsigned long max_mapnr;
+EXPORT_SYMBOL(max_mapnr);
unsigned long highest_memmap_pfn;
struct percpu_counter vm_committed_as;
int sysctl_overcommit_memory = OVERCOMMIT_GUESS; /* heuristic overcommit */
if (sysctl_nr_trim_pages && total - point >= sysctl_nr_trim_pages) {
total = point;
kdebug("try to alloc exact %lu pages", total);
- base = alloc_pages_exact(len, GFP_KERNEL);
- } else {
- base = (void *)__get_free_pages(GFP_KERNEL, order);
}
+ base = alloc_pages_exact(total << PAGE_SHIFT, GFP_KERNEL);
if (!base)
goto enomem;
* bw * elapsed + write_bandwidth * (period - elapsed)
* write_bandwidth = ---------------------------------------------------
* period
+ *
+ * @written may have decreased due to account_page_redirty().
+ * Avoid underflowing @bw calculation.
*/
- bw = written - bdi->written_stamp;
+ bw = written - min(written, bdi->written_stamp);
bw *= HZ;
if (unlikely(elapsed > period)) {
do_div(bw, elapsed);
unsigned long now)
{
static DEFINE_SPINLOCK(dirty_lock);
- static unsigned long update_time;
+ static unsigned long update_time = INITIAL_JIFFIES;
/*
* check locklessly first to optimize away locking for the most time
if (ac->high_zoneidx < ZONE_NORMAL)
goto out;
/* The OOM killer does not compensate for light reclaim */
- if (!(gfp_mask & __GFP_FS))
+ if (!(gfp_mask & __GFP_FS)) {
+ /*
+ * XXX: Page reclaim didn't yield anything,
+ * and the OOM killer can't be invoked, but
+ * keep looping as per should_alloc_retry().
+ */
+ *did_some_progress = 1;
goto out;
+ }
/*
* GFP_THISNODE contains __GFP_NORETRY and we never hit this.
* Sanity check for bare calls of __GFP_THISNODE, not real OOM.
goto out;
}
/* Exhausted what can be done so it's blamo time */
- if (out_of_memory(ac->zonelist, gfp_mask, order, ac->nodemask, false))
+ if (out_of_memory(ac->zonelist, gfp_mask, order, ac->nodemask, false)
+ || WARN_ON_ONCE(gfp_mask & __GFP_NOFAIL))
*did_some_progress = 1;
out:
oom_zonelist_unlock(ac->zonelist, gfp_mask);
if (!is_migrate_isolate_page(buddy)) {
__isolate_free_page(page, order);
+ kernel_map_pages(page, (1 << order), 1);
set_page_refcounted(page);
isolated_page = page;
}
vma = vma->vm_next;
err = walk_page_test(start, next, walk);
- if (err > 0)
+ if (err > 0) {
+ /*
+ * positive return values are purely for
+ * controlling the pagewalk, so should never
+ * be passed to the callers.
+ */
+ err = 0;
continue;
+ }
if (err < 0)
break;
}
return 0;
enomem_failure:
+ /*
+ * dst->anon_vma is dropped here otherwise its degree can be incorrectly
+ * decremented in unlink_anon_vmas().
+ * We can safely do this because callers of anon_vma_clone() don't care
+ * about dst->anon_vma if anon_vma_clone() failed.
+ */
+ dst->anon_vma = NULL;
unlink_anon_vmas(dst);
return -ENOMEM;
}
bool shmem_mapping(struct address_space *mapping)
{
+ if (!mapping->host)
+ return false;
+
return mapping->host->i_sb->s_op == &shmem_ops;
}
do {
tid = this_cpu_read(s->cpu_slab->tid);
c = raw_cpu_ptr(s->cpu_slab);
- } while (IS_ENABLED(CONFIG_PREEMPT) && unlikely(tid != c->tid));
+ } while (IS_ENABLED(CONFIG_PREEMPT) &&
+ unlikely(tid != READ_ONCE(c->tid)));
/*
* Irqless object alloc/free algorithm used here depends on sequence
do {
tid = this_cpu_read(s->cpu_slab->tid);
c = raw_cpu_ptr(s->cpu_slab);
- } while (IS_ENABLED(CONFIG_PREEMPT) && unlikely(tid != c->tid));
+ } while (IS_ENABLED(CONFIG_PREEMPT) &&
+ unlikely(tid != READ_ONCE(c->tid)));
/* Same with comment on barrier() in slab_alloc_node() */
barrier();
spin_unlock(&vmap_area_lock);
vmap_debug_free_range(va->va_start, va->va_end);
+ kasan_free_shadow(vm);
free_unmap_vmap_area(va);
vm->size -= PAGE_SIZE;
static void p9_virtio_remove(struct virtio_device *vdev)
{
struct virtio_chan *chan = vdev->priv;
-
- if (chan->inuse)
- p9_virtio_close(chan->client);
- vdev->config->del_vqs(vdev);
+ unsigned long warning_time;
mutex_lock(&virtio_9p_lock);
+
+ /* Remove self from list so we don't get new users. */
list_del(&chan->chan_list);
+ warning_time = jiffies;
+
+ /* Wait for existing users to close. */
+ while (chan->inuse) {
+ mutex_unlock(&virtio_9p_lock);
+ msleep(250);
+ if (time_after(jiffies, warning_time + 10 * HZ)) {
+ dev_emerg(&vdev->dev,
+ "p9_virtio_remove: waiting for device in use.\n");
+ warning_time = jiffies;
+ }
+ mutex_lock(&virtio_9p_lock);
+ }
+
mutex_unlock(&virtio_9p_lock);
+
+ vdev->config->del_vqs(vdev);
+
sysfs_remove_file(&(vdev->dev.kobj), &dev_attr_mount_tag.attr);
kobject_uevent(&(vdev->dev.kobj), KOBJ_CHANGE);
kfree(chan->tag);
{
int err;
+ BUILD_BUG_ON(sizeof(struct br_input_skb_cb) > FIELD_SIZEOF(struct sk_buff, cb));
+
err = stp_proto_register(&br_stp_proto);
if (err < 0) {
pr_err("bridge: can't register sap for STP\n");
*/
del_nbp(p);
+ dev_set_mtu(br->dev, br_min_mtu(br));
+
spin_lock_bh(&br->lock);
changed_addr = br_stp_recalculate_bridge_id(br);
spin_unlock_bh(&br->lock);
int copylen;
ret = -EOPNOTSUPP;
- if (m->msg_flags&MSG_OOB)
+ if (flags & MSG_OOB)
goto read_error;
skb = skb_recv_datagram(sk, flags, 0 , &ret);
u16 tmp;
u16 len;
u16 hdrchks;
- u16 pktchks;
+ int pktchks;
struct cffrml *this;
this = container_obj(layr);
return skb->len;
}
-inline u16 cfpkt_iterate(struct cfpkt *pkt,
- u16 (*iter_func)(u16, void *, u16),
- u16 data)
+int cfpkt_iterate(struct cfpkt *pkt,
+ u16 (*iter_func)(u16, void *, u16),
+ u16 data)
{
/*
* Don't care about the performance hit of linearizing,
goto inval_skb;
}
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ skb_reset_mac_header(skb);
skb_reset_network_header(skb);
skb_reset_transport_header(skb);
__get_user(kmsg->msg_controllen, &umsg->msg_controllen) ||
__get_user(kmsg->msg_flags, &umsg->msg_flags))
return -EFAULT;
+
+ if (!uaddr)
+ kmsg->msg_namelen = 0;
+
+ if (kmsg->msg_namelen < 0)
+ return -EINVAL;
+
if (kmsg->msg_namelen > sizeof(struct sockaddr_storage))
kmsg->msg_namelen = sizeof(struct sockaddr_storage);
kmsg->msg_control = compat_ptr(tmp3);
COMPAT_SYSCALL_DEFINE3(sendmsg, int, fd, struct compat_msghdr __user *, msg, unsigned int, flags)
{
- if (flags & MSG_CMSG_COMPAT)
- return -EINVAL;
return __sys_sendmsg(fd, (struct user_msghdr __user *)msg, flags | MSG_CMSG_COMPAT);
}
COMPAT_SYSCALL_DEFINE4(sendmmsg, int, fd, struct compat_mmsghdr __user *, mmsg,
unsigned int, vlen, unsigned int, flags)
{
- if (flags & MSG_CMSG_COMPAT)
- return -EINVAL;
return __sys_sendmmsg(fd, (struct mmsghdr __user *)mmsg, vlen,
flags | MSG_CMSG_COMPAT);
}
COMPAT_SYSCALL_DEFINE3(recvmsg, int, fd, struct compat_msghdr __user *, msg, unsigned int, flags)
{
- if (flags & MSG_CMSG_COMPAT)
- return -EINVAL;
return __sys_recvmsg(fd, (struct user_msghdr __user *)msg, flags | MSG_CMSG_COMPAT);
}
int datagrams;
struct timespec ktspec;
- if (flags & MSG_CMSG_COMPAT)
- return -EINVAL;
-
if (timeout == NULL)
return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen,
flags | MSG_CMSG_COMPAT, NULL);
return false;
while (*name) {
- if (*name == '/' || isspace(*name))
+ if (*name == '/' || *name == ':' || isspace(*name))
return false;
name++;
}
#define skb_update_prio(skb)
#endif
-static DEFINE_PER_CPU(int, xmit_recursion);
+DEFINE_PER_CPU(int, xmit_recursion);
+EXPORT_SYMBOL(xmit_recursion);
+
#define RECURSION_LIMIT 10
/**
[NETIF_F_RXALL_BIT] = "rx-all",
[NETIF_F_HW_L2FW_DOFFLOAD_BIT] = "l2-fwd-offload",
[NETIF_F_BUSY_POLL_BIT] = "busy-poll",
+ [NETIF_F_HW_SWITCH_OFFLOAD_BIT] = "hw-switch-offload",
};
static const char
spin_lock(&net->rules_mod_lock);
list_del_rcu(&ops->list);
- fib_rules_cleanup_ops(ops);
spin_unlock(&net->rules_mod_lock);
+ fib_rules_cleanup_ops(ops);
call_rcu(&ops->rcu, fib_rules_put_rcu);
}
EXPORT_SYMBOL_GPL(fib_rules_unregister);
return 0;
nla_put_failure:
+ kfree(d->xstats);
+ d->xstats = NULL;
+ d->xstats_len = 0;
spin_unlock_bh(d->lock);
return -1;
}
gnet_stats_copy_app(struct gnet_dump *d, void *st, int len)
{
if (d->compat_xstats) {
- d->xstats = st;
+ d->xstats = kmemdup(st, len, GFP_ATOMIC);
+ if (!d->xstats)
+ goto err_out;
d->xstats_len = len;
}
return gnet_stats_copy(d, TCA_STATS_APP, st, len);
return 0;
+
+err_out:
+ d->xstats_len = 0;
+ spin_unlock_bh(d->lock);
+ return -1;
}
EXPORT_SYMBOL(gnet_stats_copy_app);
return -1;
}
+ kfree(d->xstats);
+ d->xstats = NULL;
+ d->xstats_len = 0;
spin_unlock_bh(d->lock);
return 0;
}
*/
int peernet2id(struct net *net, struct net *peer)
{
- int id = __peernet2id(net, peer, true);
+ bool alloc = atomic_read(&peer->count) == 0 ? false : true;
+ int id;
+ id = __peernet2id(net, peer, alloc);
return id >= 0 ? id : NETNSA_NSID_NOT_ASSIGNED;
}
EXPORT_SYMBOL(peernet2id);
return len;
i += len;
+ if ((value > 1) &&
+ (!(pkt_dev->odev->priv_flags & IFF_TX_SKB_SHARING)))
+ return -ENOTSUPP;
pkt_dev->burst = value < 1 ? 1 : value;
sprintf(pg_result, "OK: burst=%d", pkt_dev->burst);
return count;
s_h = cb->args[0];
s_idx = cb->args[1];
- rcu_read_lock();
cb->seq = net->dev_base_seq;
/* A hack to preserve kernel<->userspace interface.
for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
idx = 0;
head = &net->dev_index_head[h];
- hlist_for_each_entry_rcu(dev, head, index_hlist) {
+ hlist_for_each_entry(dev, head, index_hlist) {
if (idx < s_idx)
goto cont;
err = rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK,
}
}
out:
- rcu_read_unlock();
cb->args[1] = idx;
cb->args[0] = h;
struct ifinfomsg *ifm,
struct nlattr **tb)
{
- struct net_device *dev;
+ struct net_device *dev, *aux;
int err;
- for_each_netdev(net, dev) {
+ for_each_netdev_safe(net, dev, aux) {
if (dev->group == group) {
err = do_setlink(skb, dev, ifm, tb, NULL, 0);
if (err < 0)
}
if (1) {
- struct nlattr *attr[ops ? ops->maxtype + 1 : 0];
- struct nlattr *slave_attr[m_ops ? m_ops->slave_maxtype + 1 : 0];
+ struct nlattr *attr[ops ? ops->maxtype + 1 : 1];
+ struct nlattr *slave_attr[m_ops ? m_ops->slave_maxtype + 1 : 1];
struct nlattr **data = NULL;
struct nlattr **slave_data = NULL;
struct net *dest_net, *link_net = NULL;
if (IS_ERR(dest_net))
return PTR_ERR(dest_net);
+ err = -EPERM;
+ if (!netlink_ns_capable(skb, dest_net->user_ns, CAP_NET_ADMIN))
+ goto out;
+
if (tb[IFLA_LINK_NETNSID]) {
int id = nla_get_s32(tb[IFLA_LINK_NETNSID]);
err = -EINVAL;
goto out;
}
+ err = -EPERM;
+ if (!netlink_ns_capable(skb, link_net->user_ns, CAP_NET_ADMIN))
+ goto out;
}
dev = rtnl_create_link(link_net ? : dest_net, ifname,
}
}
err = rtnl_configure_link(dev, ifm);
- if (err < 0) {
- if (ops->newlink) {
- LIST_HEAD(list_kill);
-
- ops->dellink(dev, &list_kill);
- unregister_netdevice_many(&list_kill);
- } else {
- unregister_netdevice(dev);
- }
- goto out;
- }
-
+ if (err < 0)
+ goto out_unregister;
if (link_net) {
err = dev_change_net_namespace(dev, dest_net, ifname);
if (err < 0)
- unregister_netdevice(dev);
+ goto out_unregister;
}
out:
if (link_net)
put_net(link_net);
put_net(dest_net);
return err;
+out_unregister:
+ if (ops->newlink) {
+ LIST_HEAD(list_kill);
+
+ ops->dellink(dev, &list_kill);
+ unregister_netdevice_many(&list_kill);
+ } else {
+ unregister_netdevice(dev);
+ }
+ goto out;
}
}
{
struct sk_buff_head *q = &sk->sk_error_queue;
struct sk_buff *skb, *skb_next;
+ unsigned long flags;
int err = 0;
- spin_lock_bh(&q->lock);
+ spin_lock_irqsave(&q->lock, flags);
skb = __skb_dequeue(q);
if (skb && (skb_next = skb_peek(q)))
err = SKB_EXT_ERR(skb_next)->ee.ee_errno;
- spin_unlock_bh(&q->lock);
+ spin_unlock_irqrestore(&q->lock, flags);
sk->sk_err = err;
if (err)
struct sock *sk, int tstype)
{
struct sk_buff *skb;
- bool tsonly = sk->sk_tsflags & SOF_TIMESTAMPING_OPT_TSONLY;
+ bool tsonly;
+
+ if (!sk)
+ return;
- if (!sk || !skb_may_tx_timestamp(sk, tsonly))
+ tsonly = sk->sk_tsflags & SOF_TIMESTAMPING_OPT_TSONLY;
+ if (!skb_may_tx_timestamp(sk, tsonly))
return;
if (tsonly)
skb->ignore_df = 0;
skb_dst_drop(skb);
skb->mark = 0;
- skb->sender_cpu = 0;
+ skb_sender_cpu_clear(skb);
skb_init_secmark(skb);
secpath_reset(skb);
nf_reset(skb);
sock_reset_flag(sk, bit);
}
+bool sk_mc_loop(struct sock *sk)
+{
+ if (dev_recursion_level())
+ return false;
+ if (!sk)
+ return true;
+ switch (sk->sk_family) {
+ case AF_INET:
+ return inet_sk(sk)->mc_loop;
+#if IS_ENABLED(CONFIG_IPV6)
+ case AF_INET6:
+ return inet6_sk(sk)->mc_loop;
+#endif
+ }
+ WARN_ON(1);
+ return true;
+}
+EXPORT_SYMBOL(sk_mc_loop);
+
/*
* This is meant for all protocols to use and covers goings on
* at the socket level. Everything here is generic.
}
EXPORT_SYMBOL(sock_rfree);
+/*
+ * Buffer destructor for skbs that are not used directly in read or write
+ * path, e.g. for error handler skbs. Automatically called from kfree_skb.
+ */
void sock_efree(struct sk_buff *skb)
{
sock_put(skb->sk);
static int zero = 0;
static int one = 1;
static int ushort_max = USHRT_MAX;
+static int min_sndbuf = SOCK_MIN_SNDBUF;
+static int min_rcvbuf = SOCK_MIN_RCVBUF;
static int net_msg_warn; /* Unused, but still a sysctl */
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
- .extra1 = &one,
+ .extra1 = &min_sndbuf,
},
{
.procname = "rmem_max",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
- .extra1 = &one,
+ .extra1 = &min_rcvbuf,
},
{
.procname = "wmem_default",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
- .extra1 = &one,
+ .extra1 = &min_sndbuf,
},
{
.procname = "rmem_default",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
- .extra1 = &one,
+ .extra1 = &min_rcvbuf,
},
{
.procname = "dev_weight",
if (decnet_debug_level & 16)
printk(KERN_DEBUG
"dn_route_output_slow: initial checks complete."
- " dst=%o4x src=%04x oif=%d try_hard=%d\n",
+ " dst=%04x src=%04x oif=%d try_hard=%d\n",
le16_to_cpu(fld.daddr), le16_to_cpu(fld.saddr),
fld.flowidn_oif, try_hard);
void __exit dn_fib_rules_cleanup(void)
{
+ rtnl_lock();
fib_rules_unregister(dn_fib_rules_ops);
+ rtnl_unlock();
rcu_barrier();
}
#ifdef CONFIG_OF
static int dsa_of_setup_routing_table(struct dsa_platform_data *pd,
struct dsa_chip_data *cd,
- int chip_index,
+ int chip_index, int port_index,
struct device_node *link)
{
- int ret;
const __be32 *reg;
- int link_port_addr;
int link_sw_addr;
struct device_node *parent_sw;
int len;
if (!reg || (len != sizeof(*reg) * 2))
return -EINVAL;
+ /*
+ * Get the destination switch number from the second field of its 'reg'
+ * property, i.e. for "reg = <0x19 1>" sw_addr is '1'.
+ */
link_sw_addr = be32_to_cpup(reg + 1);
if (link_sw_addr >= pd->nr_chips)
memset(cd->rtable, -1, pd->nr_chips * sizeof(s8));
}
- reg = of_get_property(link, "reg", NULL);
- if (!reg) {
- ret = -EINVAL;
- goto out;
- }
-
- link_port_addr = be32_to_cpup(reg);
-
- cd->rtable[link_sw_addr] = link_port_addr;
+ cd->rtable[link_sw_addr] = port_index;
return 0;
-out:
- kfree(cd->rtable);
- return ret;
}
static void dsa_of_free_platform_data(struct dsa_platform_data *pd)
if (!strcmp(port_name, "dsa") && link &&
pd->nr_chips > 1) {
ret = dsa_of_setup_routing_table(pd, cd,
- chip_index, link);
+ chip_index, port_index, link);
if (ret)
goto out_free_chip;
}
struct hsr_port *port;
hsr = netdev_priv(hsr_dev);
+
+ rtnl_lock();
hsr_for_each_port(hsr, port)
hsr_del_port(port);
+ rtnl_unlock();
del_timer_sync(&hsr->prune_timer);
del_timer_sync(&hsr->announce_timer);
return NOTIFY_DONE; /* Not an HSR device */
hsr = netdev_priv(dev);
port = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
+ if (port == NULL) {
+ /* Resend of notification concerning removed device? */
+ return NOTIFY_DONE;
+ }
} else {
hsr = port->hsr;
}
list_del_rcu(&port->port_list);
if (port != master) {
- netdev_update_features(master->dev);
- dev_set_mtu(master->dev, hsr_get_max_mtu(hsr));
+ if (master != NULL) {
+ netdev_update_features(master->dev);
+ dev_set_mtu(master->dev, hsr_get_max_mtu(hsr));
+ }
netdev_rx_handler_unregister(port->dev);
dev_set_promiscuity(port->dev, -1);
}
*/
synchronize_rcu();
- dev_put(port->dev);
+
+ if (port != master)
+ dev_put(port->dev);
}
{
unsigned int i;
+ rtnl_lock();
#ifdef CONFIG_IP_MULTIPLE_TABLES
fib4_rules_exit(net);
#endif
-
- rtnl_lock();
for (i = 0; i < FIB_TABLE_HASHSZ; i++) {
struct fib_table *tb;
struct hlist_head *head;
release_sock(sk);
if (reqsk_queue_empty(&icsk->icsk_accept_queue))
timeo = schedule_timeout(timeo);
+ sched_annotate_sleep();
lock_sock(sk);
err = 0;
if (!reqsk_queue_empty(&icsk->icsk_accept_queue))
mutex_unlock(&inet_diag_table_mutex);
}
+static size_t inet_sk_attr_size(void)
+{
+ return nla_total_size(sizeof(struct tcp_info))
+ + nla_total_size(1) /* INET_DIAG_SHUTDOWN */
+ + nla_total_size(1) /* INET_DIAG_TOS */
+ + nla_total_size(1) /* INET_DIAG_TCLASS */
+ + nla_total_size(sizeof(struct inet_diag_meminfo))
+ + nla_total_size(sizeof(struct inet_diag_msg))
+ + nla_total_size(SK_MEMINFO_VARS * sizeof(u32))
+ + nla_total_size(TCP_CA_NAME_MAX)
+ + nla_total_size(sizeof(struct tcpvegas_info))
+ + 64;
+}
+
int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk,
struct sk_buff *skb, struct inet_diag_req_v2 *req,
struct user_namespace *user_ns,
if (err)
goto out;
- rep = nlmsg_new(sizeof(struct inet_diag_msg) +
- sizeof(struct inet_diag_meminfo) +
- sizeof(struct tcp_info) + 64, GFP_KERNEL);
+ rep = nlmsg_new(inet_sk_attr_size(), GFP_KERNEL);
if (!rep) {
err = -ENOMEM;
goto out;
if (unlikely(opt->optlen))
ip_forward_options(skb);
+ skb_sender_cpu_clear(skb);
return dst_output(skb);
}
struct sk_buff *ip_check_defrag(struct sk_buff *skb, u32 user)
{
struct iphdr iph;
+ int netoff;
u32 len;
if (skb->protocol != htons(ETH_P_IP))
return skb;
- if (!skb_copy_bits(skb, 0, &iph, sizeof(iph)))
+ netoff = skb_network_offset(skb);
+
+ if (skb_copy_bits(skb, netoff, &iph, sizeof(iph)) < 0)
return skb;
if (iph.ihl < 5 || iph.version != 4)
return skb;
len = ntohs(iph.tot_len);
- if (skb->len < len || len < (iph.ihl * 4))
+ if (skb->len < netoff + len || len < (iph.ihl * 4))
return skb;
if (ip_is_fragment(&iph)) {
skb = skb_share_check(skb, GFP_ATOMIC);
if (skb) {
- if (!pskb_may_pull(skb, iph.ihl*4))
+ if (!pskb_may_pull(skb, netoff + iph.ihl * 4))
return skb;
- if (pskb_trim_rcsum(skb, len))
+ if (pskb_trim_rcsum(skb, netoff + len))
return skb;
memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
if (ip_defrag(skb, user))
cork->length += length;
if (((length > mtu) || (skb && skb_is_gso(skb))) &&
(sk->sk_protocol == IPPROTO_UDP) &&
- (rt->dst.dev->features & NETIF_F_UFO) && !rt->dst.header_len) {
+ (rt->dst.dev->features & NETIF_F_UFO) && !rt->dst.header_len &&
+ (sk->sk_type == SOCK_DGRAM)) {
err = ip_ufo_append_data(sk, queue, getfrag, from, length,
hh_len, fragheaderlen, transhdrlen,
maxfraglen, flags);
kfree_skb(skb);
}
-static bool ipv4_pktinfo_prepare_errqueue(const struct sock *sk,
- const struct sk_buff *skb,
- int ee_origin)
+/* IPv4 supports cmsg on all imcp errors and some timestamps
+ *
+ * Timestamp code paths do not initialize the fields expected by cmsg:
+ * the PKTINFO fields in skb->cb[]. Fill those in here.
+ */
+static bool ipv4_datagram_support_cmsg(const struct sock *sk,
+ struct sk_buff *skb,
+ int ee_origin)
{
- struct in_pktinfo *info = PKTINFO_SKB_CB(skb);
+ struct in_pktinfo *info;
+
+ if (ee_origin == SO_EE_ORIGIN_ICMP)
+ return true;
- if ((ee_origin != SO_EE_ORIGIN_TIMESTAMPING) ||
- (!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_CMSG)) ||
+ if (ee_origin == SO_EE_ORIGIN_LOCAL)
+ return false;
+
+ /* Support IP_PKTINFO on tstamp packets if requested, to correlate
+ * timestamp with egress dev. Not possible for packets without dev
+ * or without payload (SOF_TIMESTAMPING_OPT_TSONLY).
+ */
+ if ((!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_CMSG)) ||
(!skb->dev))
return false;
+ info = PKTINFO_SKB_CB(skb);
info->ipi_spec_dst.s_addr = ip_hdr(skb)->saddr;
info->ipi_ifindex = skb->dev->ifindex;
return true;
serr = SKB_EXT_ERR(skb);
- if (sin && skb->len) {
+ if (sin && serr->port) {
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = *(__be32 *)(skb_network_header(skb) +
serr->addr_offset);
sin = &errhdr.offender;
memset(sin, 0, sizeof(*sin));
- if (skb->len &&
- (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP ||
- ipv4_pktinfo_prepare_errqueue(sk, skb, serr->ee.ee_origin))) {
+ if (ipv4_datagram_support_cmsg(sk, skb, serr->ee.ee_origin)) {
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
if (inet_sk(sk)->cmsg_flags)
return 0;
err2:
- kfree(mrt);
+ ipmr_free_table(mrt);
err1:
fib_rules_unregister(ops);
return err;
{
struct mr_table *mrt, *next;
+ rtnl_lock();
list_for_each_entry_safe(mrt, next, &net->ipv4.mr_tables, list) {
list_del(&mrt->list);
ipmr_free_table(mrt);
}
fib_rules_unregister(net->ipv4.mr_rules_ops);
+ rtnl_unlock();
}
#else
#define ipmr_for_each_table(mrt, net) \
static void __net_exit ipmr_rules_exit(struct net *net)
{
+ rtnl_lock();
ipmr_free_table(net->ipv4.mrt);
+ net->ipv4.mrt = NULL;
+ rtnl_unlock();
}
#endif
&chainname, &comment, &rulenum) != 0)
break;
- nf_log_packet(net, AF_INET, hook, skb, in, out, &trace_loginfo,
- "TRACE: %s:%s:%s:%u ",
- tablename, chainname, comment, rulenum);
+ nf_log_trace(net, AF_INET, hook, skb, in, out, &trace_loginfo,
+ "TRACE: %s:%s:%s:%u ",
+ tablename, chainname, comment, rulenum);
}
#endif
kgid_t low, high;
int ret = 0;
+ if (sk->sk_family == AF_INET6)
+ sk->sk_ipv6only = 1;
+
inet_get_ping_group_range_net(net, &low, &high);
if (gid_lte(low, group) && gid_lte(group, high))
return 0;
if (addr_len < sizeof(*addr))
return -EINVAL;
+ if (addr->sin_family != AF_INET &&
+ !(addr->sin_family == AF_UNSPEC &&
+ addr->sin_addr.s_addr == htonl(INADDR_ANY)))
+ return -EAFNOSUPPORT;
+
pr_debug("ping_check_bind_addr(sk=%p,addr=%pI4,port=%d)\n",
sk, &addr->sin_addr.s_addr, ntohs(addr->sin_port));
return -EINVAL;
if (addr->sin6_family != AF_INET6)
- return -EINVAL;
+ return -EAFNOSUPPORT;
pr_debug("ping_check_bind_addr(sk=%p,addr=%pI6c,port=%d)\n",
sk, addr->sin6_addr.s6_addr, ntohs(addr->sin6_port));
if (msg->msg_namelen < sizeof(*usin))
return -EINVAL;
if (usin->sin_family != AF_INET)
- return -EINVAL;
+ return -EAFNOSUPPORT;
daddr = usin->sin_addr.s_addr;
/* no remote port */
} else {
int large_allowed)
{
struct tcp_sock *tp = tcp_sk(sk);
- u32 new_size_goal, size_goal, hlen;
+ u32 new_size_goal, size_goal;
if (!large_allowed || !sk_can_gso(sk))
return mss_now;
- /* Maybe we should/could use sk->sk_prot->max_header here ? */
- hlen = inet_csk(sk)->icsk_af_ops->net_header_len +
- inet_csk(sk)->icsk_ext_hdr_len +
- tp->tcp_header_len;
-
- new_size_goal = sk->sk_gso_max_size - 1 - hlen;
+ /* Note : tcp_tso_autosize() will eventually split this later */
+ new_size_goal = sk->sk_gso_max_size - 1 - MAX_TCP_HEADER;
new_size_goal = tcp_bound_to_half_wnd(tp, new_size_goal);
/* We try hard to avoid divides here */
*/
void tcp_cong_avoid_ai(struct tcp_sock *tp, u32 w, u32 acked)
{
+ /* If credits accumulated at a higher w, apply them gently now. */
+ if (tp->snd_cwnd_cnt >= w) {
+ tp->snd_cwnd_cnt = 0;
+ tp->snd_cwnd++;
+ }
+
tp->snd_cwnd_cnt += acked;
if (tp->snd_cwnd_cnt >= w) {
u32 delta = tp->snd_cwnd_cnt / w;
}
}
- if (ca->cnt == 0) /* cannot be zero */
- ca->cnt = 1;
+ /* The maximum rate of cwnd increase CUBIC allows is 1 packet per
+ * 2 packets ACKed, meaning cwnd grows at 1.5x per RTT.
+ */
+ ca->cnt = max(ca->cnt, 2U);
}
static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
if (!first_ackt.v64)
first_ackt = last_ackt;
- if (!(sacked & TCPCB_SACKED_ACKED))
+ if (!(sacked & TCPCB_SACKED_ACKED)) {
reord = min(pkts_acked, reord);
- if (!after(scb->end_seq, tp->high_seq))
- flag |= FLAG_ORIG_SACK_ACKED;
+ if (!after(scb->end_seq, tp->high_seq))
+ flag |= FLAG_ORIG_SACK_ACKED;
+ }
}
if (sacked & TCPCB_SACKED_ACKED)
return false;
/* If we filled the congestion window, do not expand. */
- if (tp->packets_out >= tp->snd_cwnd)
+ if (tcp_packets_in_flight(tp) >= tp->snd_cwnd)
return false;
return true;
skb->sk = sk;
skb->destructor = sock_edemux;
if (sk->sk_state != TCP_TIME_WAIT) {
- struct dst_entry *dst = sk->sk_rx_dst;
+ struct dst_entry *dst = READ_ONCE(sk->sk_rx_dst);
if (dst)
dst = dst_check(dst, 0);
} else {
/* Socket is locked, keep trying until memory is available. */
for (;;) {
- skb = alloc_skb_fclone(MAX_TCP_HEADER,
- sk->sk_allocation);
+ skb = sk_stream_alloc_skb(sk, 0, sk->sk_allocation);
if (skb)
break;
yield();
}
-
- /* Reserve space for headers and prepare control bits. */
- skb_reserve(skb, MAX_TCP_HEADER);
/* FIN eats a sequence byte, write_seq advanced by tcp_queue_skb(). */
tcp_init_nondata_skb(skb, tp->write_seq,
TCPHDR_ACK | TCPHDR_FIN);
return err;
IPCB(skb)->flags |= IPSKB_XFRM_TUNNEL_SIZE;
+ skb->protocol = htons(ETH_P_IP);
return x->outer_mode->output2(x, skb);
}
int xfrm4_output_finish(struct sk_buff *skb)
{
memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
- skb->protocol = htons(ETH_P_IP);
#ifdef CONFIG_NETFILTER
IPCB(skb)->flags |= IPSKB_XFRM_TRANSFORMED;
return ret;
}
+static
+int addrconf_sysctl_mtu(struct ctl_table *ctl, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ struct inet6_dev *idev = ctl->extra1;
+ int min_mtu = IPV6_MIN_MTU;
+ struct ctl_table lctl;
+
+ lctl = *ctl;
+ lctl.extra1 = &min_mtu;
+ lctl.extra2 = idev ? &idev->dev->mtu : NULL;
+
+ return proc_dointvec_minmax(&lctl, write, buffer, lenp, ppos);
+}
+
static void dev_disable_change(struct inet6_dev *idev)
{
struct netdev_notifier_info info;
.data = &ipv6_devconf.mtu6,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = proc_dointvec,
+ .proc_handler = addrconf_sysctl_mtu,
},
{
.procname = "accept_ra",
kfree_skb(skb);
}
-static void ip6_datagram_prepare_pktinfo_errqueue(struct sk_buff *skb)
+/* IPv6 supports cmsg on all origins aside from SO_EE_ORIGIN_LOCAL.
+ *
+ * At one point, excluding local errors was a quick test to identify icmp/icmp6
+ * errors. This is no longer true, but the test remained, so the v6 stack,
+ * unlike v4, also honors cmsg requests on all wifi and timestamp errors.
+ *
+ * Timestamp code paths do not initialize the fields expected by cmsg:
+ * the PKTINFO fields in skb->cb[]. Fill those in here.
+ */
+static bool ip6_datagram_support_cmsg(struct sk_buff *skb,
+ struct sock_exterr_skb *serr)
{
- int ifindex = skb->dev ? skb->dev->ifindex : -1;
+ if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP ||
+ serr->ee.ee_origin == SO_EE_ORIGIN_ICMP6)
+ return true;
+
+ if (serr->ee.ee_origin == SO_EE_ORIGIN_LOCAL)
+ return false;
+
+ if (!skb->dev)
+ return false;
if (skb->protocol == htons(ETH_P_IPV6))
- IP6CB(skb)->iif = ifindex;
+ IP6CB(skb)->iif = skb->dev->ifindex;
else
- PKTINFO_SKB_CB(skb)->ipi_ifindex = ifindex;
+ PKTINFO_SKB_CB(skb)->ipi_ifindex = skb->dev->ifindex;
+
+ return true;
}
/*
serr = SKB_EXT_ERR(skb);
- if (sin && skb->len) {
+ if (sin && serr->port) {
const unsigned char *nh = skb_network_header(skb);
sin->sin6_family = AF_INET6;
sin->sin6_flowinfo = 0;
memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
sin = &errhdr.offender;
memset(sin, 0, sizeof(*sin));
- if (serr->ee.ee_origin != SO_EE_ORIGIN_LOCAL && skb->len) {
+
+ if (ip6_datagram_support_cmsg(skb, serr)) {
sin->sin6_family = AF_INET6;
- if (np->rxopt.all) {
- if (serr->ee.ee_origin != SO_EE_ORIGIN_ICMP &&
- serr->ee.ee_origin != SO_EE_ORIGIN_ICMP6)
- ip6_datagram_prepare_pktinfo_errqueue(skb);
+ if (np->rxopt.all)
ip6_datagram_recv_common_ctl(sk, msg, skb);
- }
if (skb->protocol == htons(ETH_P_IPV6)) {
sin->sin6_addr = ipv6_hdr(skb)->saddr;
if (np->rxopt.all)
goto again;
flp6->saddr = saddr;
}
+ err = rt->dst.error;
goto out;
}
again:
static void __net_exit fib6_rules_net_exit(struct net *net)
{
+ rtnl_lock();
fib_rules_unregister(net->ipv6.fib6_rules_ops);
+ rtnl_unlock();
}
static struct pernet_operations fib6_rules_net_ops = {
static inline int ip6_forward_finish(struct sk_buff *skb)
{
+ skb_sender_cpu_clear(skb);
return dst_output(skb);
}
{
struct sk_buff *frag;
struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
- struct ipv6_pinfo *np = skb->sk ? inet6_sk(skb->sk) : NULL;
+ struct ipv6_pinfo *np = skb->sk && !dev_recursion_level() ?
+ inet6_sk(skb->sk) : NULL;
struct ipv6hdr *tmp_hdr;
struct frag_hdr *fh;
unsigned int mtu, hlen, left, len;
if (((length > mtu) ||
(skb && skb_is_gso(skb))) &&
(sk->sk_protocol == IPPROTO_UDP) &&
- (rt->dst.dev->features & NETIF_F_UFO)) {
+ (rt->dst.dev->features & NETIF_F_UFO) &&
+ (sk->sk_type == SOCK_DGRAM)) {
err = ip6_ufo_append_data(sk, queue, getfrag, from, length,
hh_len, fragheaderlen,
transhdrlen, mtu, flags, rt);
* Create tunnel matching given parameters.
*
* Return:
- * created tunnel or NULL
+ * created tunnel or error pointer
**/
static struct ip6_tnl *ip6_tnl_create(struct net *net, struct __ip6_tnl_parm *p)
struct net_device *dev;
struct ip6_tnl *t;
char name[IFNAMSIZ];
- int err;
+ int err = -ENOMEM;
if (p->name[0])
strlcpy(name, p->name, IFNAMSIZ);
failed_free:
ip6_dev_free(dev);
failed:
- return NULL;
+ return ERR_PTR(err);
}
/**
* tunnel device is created and registered for use.
*
* Return:
- * matching tunnel or NULL
+ * matching tunnel or error pointer
**/
static struct ip6_tnl *ip6_tnl_locate(struct net *net,
if (ipv6_addr_equal(local, &t->parms.laddr) &&
ipv6_addr_equal(remote, &t->parms.raddr)) {
if (create)
- return NULL;
+ return ERR_PTR(-EEXIST);
return t;
}
}
if (!create)
- return NULL;
+ return ERR_PTR(-ENODEV);
return ip6_tnl_create(net, p);
}
}
ip6_tnl_parm_from_user(&p1, &p);
t = ip6_tnl_locate(net, &p1, 0);
- if (t == NULL)
+ if (IS_ERR(t))
t = netdev_priv(dev);
} else {
memset(&p, 0, sizeof(p));
ip6_tnl_parm_from_user(&p1, &p);
t = ip6_tnl_locate(net, &p1, cmd == SIOCADDTUNNEL);
if (cmd == SIOCCHGTUNNEL) {
- if (t != NULL) {
+ if (!IS_ERR(t)) {
if (t->dev != dev) {
err = -EEXIST;
break;
else
err = ip6_tnl_update(t, &p1);
}
- if (t) {
+ if (!IS_ERR(t)) {
err = 0;
ip6_tnl_parm_to_user(&p, &t->parms);
if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
err = -EFAULT;
- } else
- err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT);
+ } else {
+ err = PTR_ERR(t);
+ }
break;
case SIOCDELTUNNEL:
err = -EPERM;
err = -ENOENT;
ip6_tnl_parm_from_user(&p1, &p);
t = ip6_tnl_locate(net, &p1, 0);
- if (t == NULL)
+ if (IS_ERR(t))
break;
err = -EPERM;
if (t->dev == ip6n->fb_tnl_dev)
struct nlattr *tb[], struct nlattr *data[])
{
struct net *net = dev_net(dev);
- struct ip6_tnl *nt;
+ struct ip6_tnl *nt, *t;
nt = netdev_priv(dev);
ip6_tnl_netlink_parms(data, &nt->parms);
- if (ip6_tnl_locate(net, &nt->parms, 0))
+ t = ip6_tnl_locate(net, &nt->parms, 0);
+ if (!IS_ERR(t))
return -EEXIST;
return ip6_tnl_create2(dev);
ip6_tnl_netlink_parms(data, &p);
t = ip6_tnl_locate(net, &p, 0);
-
- if (t) {
+ if (!IS_ERR(t)) {
if (t->dev != dev)
return -EEXIST;
} else
return 0;
err2:
- kfree(mrt);
+ ip6mr_free_table(mrt);
err1:
fib_rules_unregister(ops);
return err;
list_del(&mrt->list);
ip6mr_free_table(mrt);
}
- rtnl_unlock();
fib_rules_unregister(net->ipv6.mr6_rules_ops);
+ rtnl_unlock();
}
#else
#define ip6mr_for_each_table(mrt, net) \
static void ip6mr_free_table(struct mr6_table *mrt)
{
- del_timer(&mrt->ipmr_expire_timer);
+ del_timer_sync(&mrt->ipmr_expire_timer);
mroute_clean_tables(mrt);
kfree(mrt);
}
if (rt)
rt6_set_expires(rt, jiffies + (HZ * lifetime));
if (ra_msg->icmph.icmp6_hop_limit) {
- in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
+ /* Only set hop_limit on the interface if it is higher than
+ * the current hop_limit.
+ */
+ if (in6_dev->cnf.hop_limit < ra_msg->icmph.icmp6_hop_limit) {
+ in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
+ } else {
+ ND_PRINTK(2, warn, "RA: Got route advertisement with lower hop_limit than current\n");
+ }
if (rt)
dst_metric_set(&rt->dst, RTAX_HOPLIMIT,
ra_msg->icmph.icmp6_hop_limit);
&chainname, &comment, &rulenum) != 0)
break;
- nf_log_packet(net, AF_INET6, hook, skb, in, out, &trace_loginfo,
- "TRACE: %s:%s:%s:%u ",
- tablename, chainname, comment, rulenum);
+ nf_log_trace(net, AF_INET6, hook, skb, in, out, &trace_loginfo,
+ "TRACE: %s:%s:%s:%u ",
+ tablename, chainname, comment, rulenum);
}
#endif
if (msg->msg_name) {
DECLARE_SOCKADDR(struct sockaddr_in6 *, u, msg->msg_name);
- if (msg->msg_namelen < sizeof(struct sockaddr_in6) ||
- u->sin6_family != AF_INET6) {
+ if (msg->msg_namelen < sizeof(*u))
return -EINVAL;
+ if (u->sin6_family != AF_INET6) {
+ return -EAFNOSUPPORT;
}
if (sk->sk_bound_dev_if &&
sk->sk_bound_dev_if != u->sin6_scope_id) {
TCP_SKB_CB(skb)->sacked = 0;
}
+static void tcp_v6_restore_cb(struct sk_buff *skb)
+{
+ /* We need to move header back to the beginning if xfrm6_policy_check()
+ * and tcp_v6_fill_cb() are going to be called again.
+ */
+ memmove(IP6CB(skb), &TCP_SKB_CB(skb)->header.h6,
+ sizeof(struct inet6_skb_parm));
+}
+
static int tcp_v6_rcv(struct sk_buff *skb)
{
const struct tcphdr *th;
inet_twsk_deschedule(tw, &tcp_death_row);
inet_twsk_put(tw);
sk = sk2;
+ tcp_v6_restore_cb(skb);
goto process;
}
/* Fall through to ACK */
tcp_v6_timewait_ack(sk, skb);
break;
case TCP_TW_RST:
+ tcp_v6_restore_cb(skb);
goto no_tcp_socket;
case TCP_TW_SUCCESS:
;
skb->sk = sk;
skb->destructor = sock_edemux;
if (sk->sk_state != TCP_TIME_WAIT) {
- struct dst_entry *dst = sk->sk_rx_dst;
+ struct dst_entry *dst = READ_ONCE(sk->sk_rx_dst);
if (dst)
dst = dst_check(dst, inet6_sk(sk)->rx_dst_cookie);
fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen);
fptr->nexthdr = nexthdr;
fptr->reserved = 0;
- if (skb_shinfo(skb)->ip6_frag_id)
- fptr->identification = skb_shinfo(skb)->ip6_frag_id;
- else
- ipv6_select_ident(fptr,
- (struct rt6_info *)skb_dst(skb));
+ if (!skb_shinfo(skb)->ip6_frag_id)
+ ipv6_proxy_select_ident(skb);
+ fptr->identification = skb_shinfo(skb)->ip6_frag_id;
/* Fragment the skb. ipv6 header and the remaining fields of the
* fragment header are updated in ipv6_gso_segment()
return err;
skb->ignore_df = 1;
+ skb->protocol = htons(ETH_P_IPV6);
return x->outer_mode->output2(x, skb);
}
int xfrm6_output_finish(struct sk_buff *skb)
{
memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
- skb->protocol = htons(ETH_P_IPV6);
#ifdef CONFIG_NETFILTER
IP6CB(skb)->flags |= IP6SKB_XFRM_TRANSFORMED;
#if IS_ENABLED(CONFIG_IPV6_MIP6)
case IPPROTO_MH:
+ offset += ipv6_optlen(exthdr);
if (!onlyproto && pskb_may_pull(skb, nh + offset + 3 - skb->data)) {
struct ip6_mh *mh;
orig_jiffies = jiffies;
/* Set poll time to 200 ms */
- poll_time = IRDA_MIN(timeout, msecs_to_jiffies(200));
+ poll_time = msecs_to_jiffies(200);
+ if (timeout)
+ poll_time = min_t(unsigned long, timeout, poll_time);
spin_lock_irqsave(&self->spinlock, flags);
while (self->tx_skb && self->tx_skb->len) {
break;
}
spin_unlock_irqrestore(&self->spinlock, flags);
- current->state = TASK_RUNNING;
+ __set_current_state(TASK_RUNNING);
}
/*
/* Put ourselves on the wait queue to be woken up */
add_wait_queue(&irnet_events.rwait, &wait);
- current->state = TASK_INTERRUPTIBLE;
+ set_current_state(TASK_INTERRUPTIBLE);
for(;;)
{
/* If there is unread events */
/* Yield and wait to be woken up */
schedule();
}
- current->state = TASK_RUNNING;
+ __set_current_state(TASK_RUNNING);
remove_wait_queue(&irnet_events.rwait, &wait);
/* Did we got it ? */
noblock, &err);
else
skb = sock_alloc_send_skb(sk, len, noblock, &err);
- if (!skb) {
- err = -ENOMEM;
+ if (!skb)
goto out;
- }
if (iucv->transport == AF_IUCV_TRANS_HIPER)
skb_reserve(skb, sizeof(struct af_iucv_trans_hdr) + ETH_HLEN);
if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
l2tp_wq = alloc_workqueue("l2tp", WQ_UNBOUND, 0);
if (!l2tp_wq) {
pr_err("alloc_workqueue failed\n");
+ unregister_pernet_device(&l2tp_net_ops);
rc = -ENOMEM;
goto out;
}
container_of(h, struct tid_ampdu_rx, rcu_head);
int i;
- del_timer_sync(&tid_rx->reorder_timer);
-
for (i = 0; i < tid_rx->buf_size; i++)
__skb_queue_purge(&tid_rx->reorder_buf[i]);
kfree(tid_rx->reorder_buf);
del_timer_sync(&tid_rx->session_timer);
+ /* make sure ieee80211_sta_reorder_release() doesn't re-arm the timer */
+ spin_lock_bh(&tid_rx->reorder_lock);
+ tid_rx->removed = true;
+ spin_unlock_bh(&tid_rx->reorder_lock);
+ del_timer_sync(&tid_rx->reorder_timer);
+
call_rcu(&tid_rx->rcu_head, ieee80211_free_tid_rx);
}
if (ieee80211_chanctx_refcount(local, ctx) == 0)
ieee80211_free_chanctx(local, ctx);
+ sdata->radar_required = false;
+
/* Unreserving may ready an in-place reservation. */
if (use_reserved_switch)
ieee80211_vif_use_reserved_switch(local);
ieee80211_recalc_smps_chanctx(local, ctx);
ieee80211_recalc_radar_chanctx(local, ctx);
out:
+ if (ret)
+ sdata->radar_required = false;
+
mutex_unlock(&local->chanctx_mtx);
return ret;
}
#define IEEE80211_UNSET_POWER_LEVEL INT_MIN
/*
- * Some APs experience problems when working with U-APSD. Decrease the
- * probability of that happening by using legacy mode for all ACs but VO.
- * The AP that caused us trouble was a Cisco 4410N. It ignores our
- * setting, and always treats non-VO ACs as legacy.
+ * Some APs experience problems when working with U-APSD. Decreasing the
+ * probability of that happening by using legacy mode for all ACs but VO isn't
+ * enough.
+ *
+ * Cisco 4410N originally forced us to enable VO by default only because it
+ * treated non-VO ACs as legacy.
+ *
+ * However some APs (notably Netgear R7000) silently reclassify packets to
+ * different ACs. Since u-APSD ACs require trigger frames for frame retrieval
+ * clients would never see some frames (e.g. ARP responses) or would fetch them
+ * accidentally after a long time.
+ *
+ * It makes little sense to enable u-APSD queues by default because it needs
+ * userspace applications to be aware of it to actually take advantage of the
+ * possible additional powersavings. Implicitly depending on driver autotrigger
+ * frame support doesn't make much sense.
*/
-#define IEEE80211_DEFAULT_UAPSD_QUEUES \
- IEEE80211_WMM_IE_STA_QOSINFO_AC_VO
+#define IEEE80211_DEFAULT_UAPSD_QUEUES 0
#define IEEE80211_DEFAULT_MAX_SP_LEN \
IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL
unsigned int flags;
bool csa_waiting_bcn;
+ bool csa_ignored_same_chan;
bool beacon_crc_valid;
u32 beacon_crc;
return;
}
+ if (cfg80211_chandef_identical(&csa_ie.chandef,
+ &sdata->vif.bss_conf.chandef)) {
+ if (ifmgd->csa_ignored_same_chan)
+ return;
+ sdata_info(sdata,
+ "AP %pM tries to chanswitch to same channel, ignore\n",
+ ifmgd->associated->bssid);
+ ifmgd->csa_ignored_same_chan = true;
+ return;
+ }
+
mutex_lock(&local->mtx);
mutex_lock(&local->chanctx_mtx);
conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
sdata->vif.csa_active = true;
sdata->csa_chandef = csa_ie.chandef;
sdata->csa_block_tx = csa_ie.mode;
+ ifmgd->csa_ignored_same_chan = false;
if (sdata->csa_block_tx)
ieee80211_stop_vif_queues(local, sdata,
sdata->vif.csa_active = false;
ifmgd->csa_waiting_bcn = false;
+ ifmgd->csa_ignored_same_chan = false;
if (sdata->csa_block_tx) {
ieee80211_wake_vif_queues(local, sdata,
IEEE80211_QUEUE_STOP_REASON_CSA);
(1ULL << WLAN_EID_CHANNEL_SWITCH) |
(1ULL << WLAN_EID_PWR_CONSTRAINT) |
(1ULL << WLAN_EID_HT_CAPABILITY) |
- (1ULL << WLAN_EID_HT_OPERATION);
+ (1ULL << WLAN_EID_HT_OPERATION) |
+ (1ULL << WLAN_EID_EXT_CHANSWITCH_ANN);
static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt, size_t len,
rate++;
mi->sample_deferred++;
} else {
- if (!msr->sample_limit != 0)
+ if (!msr->sample_limit)
return;
mi->sample_packets++;
set_release_timer:
- mod_timer(&tid_agg_rx->reorder_timer,
- tid_agg_rx->reorder_time[j] + 1 +
- HT_RX_REORDER_BUF_TIMEOUT);
+ if (!tid_agg_rx->removed)
+ mod_timer(&tid_agg_rx->reorder_timer,
+ tid_agg_rx->reorder_time[j] + 1 +
+ HT_RX_REORDER_BUF_TIMEOUT);
} else {
del_timer(&tid_agg_rx->reorder_timer);
}
hdr = (struct ieee80211_hdr *) skb->data;
mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);
+ if (ieee80211_drop_unencrypted(rx, hdr->frame_control))
+ return RX_DROP_MONITOR;
+
/* frame is in RMC, don't forward */
if (ieee80211_is_data(hdr->frame_control) &&
is_multicast_ether_addr(hdr->addr1) &&
* @reorder_lock: serializes access to reorder buffer, see below.
* @auto_seq: used for offloaded BA sessions to automatically pick head_seq_and
* and ssn.
+ * @removed: this session is removed (but might have been found due to RCU)
*
* This structure's lifetime is managed by RCU, assignments to
* the array holding it must hold the aggregation mutex.
u16 timeout;
u8 dialog_token;
bool auto_seq;
+ bool removed;
};
/**
if (tx->sdata->control_port_no_encrypt)
info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
info->control.flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO;
+ info->flags |= IEEE80211_TX_CTL_USE_MINRATE;
}
return TX_CONTINUE;
wdev_iter = &sdata_iter->wdev;
if (sdata_iter == sdata ||
- rcu_access_pointer(sdata_iter->vif.chanctx_conf) == NULL ||
+ !ieee80211_sdata_running(sdata_iter) ||
local->hw.wiphy->software_iftypes & BIT(wdev_iter->iftype))
continue;
if (udest.af == 0)
udest.af = svc->af;
- if (udest.af != svc->af) {
+ if (udest.af != svc->af && cmd != IPVS_CMD_DEL_DEST) {
/* The synchronization protocol is incompatible
* with mixed family services
*/
IP_VS_DBG(2, "BACKUP, add new conn. failed\n");
return;
}
+ if (!(flags & IP_VS_CONN_F_TEMPLATE))
+ kfree(param->pe_data);
}
if (opt)
(opt_flags & IPVS_OPT_F_SEQ_DATA ? &opt : NULL)
);
#endif
+ ip_vs_pe_put(param.pe);
return 0;
/* Error exit */
out:
}
EXPORT_SYMBOL(nf_log_packet);
+void nf_log_trace(struct net *net,
+ u_int8_t pf,
+ unsigned int hooknum,
+ const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const struct nf_loginfo *loginfo, const char *fmt, ...)
+{
+ va_list args;
+ char prefix[NF_LOG_PREFIXLEN];
+ const struct nf_logger *logger;
+
+ rcu_read_lock();
+ logger = rcu_dereference(net->nf.nf_loggers[pf]);
+ if (logger) {
+ va_start(args, fmt);
+ vsnprintf(prefix, sizeof(prefix), fmt, args);
+ va_end(args);
+ logger->logfn(net, pf, hooknum, skb, in, out, loginfo, prefix);
+ }
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL(nf_log_trace);
+
#define S_SIZE (1024 - (sizeof(unsigned int) + 1))
struct nf_log_buf {
static inline void nft_rule_clear(struct net *net, struct nft_rule *rule)
{
- rule->genmask = 0;
+ rule->genmask &= ~(1 << gencursor_next(net));
}
static int
if (nla[NFTA_CHAIN_POLICY]) {
if ((chain != NULL &&
- !(chain->flags & NFT_BASE_CHAIN)) ||
+ !(chain->flags & NFT_BASE_CHAIN)))
+ return -EOPNOTSUPP;
+
+ if (chain == NULL &&
nla[NFTA_CHAIN_HOOK] == NULL)
return -EOPNOTSUPP;
}
nla_nest_end(skb, list);
- if (rule->ulen &&
- nla_put(skb, NFTA_RULE_USERDATA, rule->ulen, nft_userdata(rule)))
- goto nla_put_failure;
+ if (rule->udata) {
+ struct nft_userdata *udata = nft_userdata(rule);
+ if (nla_put(skb, NFTA_RULE_USERDATA, udata->len + 1,
+ udata->data) < 0)
+ goto nla_put_failure;
+ }
nlmsg_end(skb, nlh);
return 0;
struct nft_table *table;
struct nft_chain *chain;
struct nft_rule *rule, *old_rule = NULL;
+ struct nft_userdata *udata;
struct nft_trans *trans = NULL;
struct nft_expr *expr;
struct nft_ctx ctx;
struct nlattr *tmp;
- unsigned int size, i, n, ulen = 0;
+ unsigned int size, i, n, ulen = 0, usize = 0;
int err, rem;
bool create;
u64 handle, pos_handle;
n++;
}
}
+ /* Check for overflow of dlen field */
+ err = -EFBIG;
+ if (size >= 1 << 12)
+ goto err1;
- if (nla[NFTA_RULE_USERDATA])
+ if (nla[NFTA_RULE_USERDATA]) {
ulen = nla_len(nla[NFTA_RULE_USERDATA]);
+ if (ulen > 0)
+ usize = sizeof(struct nft_userdata) + ulen;
+ }
err = -ENOMEM;
- rule = kzalloc(sizeof(*rule) + size + ulen, GFP_KERNEL);
+ rule = kzalloc(sizeof(*rule) + size + usize, GFP_KERNEL);
if (rule == NULL)
goto err1;
rule->handle = handle;
rule->dlen = size;
- rule->ulen = ulen;
+ rule->udata = ulen ? 1 : 0;
- if (ulen)
- nla_memcpy(nft_userdata(rule), nla[NFTA_RULE_USERDATA], ulen);
+ if (ulen) {
+ udata = nft_userdata(rule);
+ udata->len = ulen - 1;
+ nla_memcpy(udata->data, nla[NFTA_RULE_USERDATA], ulen);
+ }
expr = nft_expr_first(rule);
for (i = 0; i < n; i++) {
err3:
list_del_rcu(&rule->list);
- if (trans) {
- list_del_rcu(&nft_trans_rule(trans)->list);
- nft_rule_clear(net, nft_trans_rule(trans));
- nft_trans_destroy(trans);
- chain->use++;
- }
err2:
nf_tables_rule_destroy(&ctx, rule);
err1:
&te->elem,
NFT_MSG_DELSETELEM, 0);
te->set->ops->get(te->set, &te->elem);
- te->set->ops->remove(te->set, &te->elem);
nft_data_uninit(&te->elem.key, NFT_DATA_VALUE);
- if (te->elem.flags & NFT_SET_MAP) {
- nft_data_uninit(&te->elem.data,
- te->set->dtype);
- }
+ if (te->set->flags & NFT_SET_MAP &&
+ !(te->elem.flags & NFT_SET_ELEM_INTERVAL_END))
+ nft_data_uninit(&te->elem.data, te->set->dtype);
+ te->set->ops->remove(te->set, &te->elem);
nft_trans_destroy(trans);
break;
}
{
struct net *net = sock_net(skb->sk);
struct nft_trans *trans, *next;
- struct nft_set *set;
+ struct nft_trans_elem *te;
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
switch (trans->msg_type) {
break;
case NFT_MSG_NEWSETELEM:
nft_trans_elem_set(trans)->nelems--;
- set = nft_trans_elem_set(trans);
- set->ops->get(set, &nft_trans_elem(trans));
- set->ops->remove(set, &nft_trans_elem(trans));
+ te = (struct nft_trans_elem *)trans->data;
+ te->set->ops->get(te->set, &te->elem);
+ nft_data_uninit(&te->elem.key, NFT_DATA_VALUE);
+ if (te->set->flags & NFT_SET_MAP &&
+ !(te->elem.flags & NFT_SET_ELEM_INTERVAL_END))
+ nft_data_uninit(&te->elem.data, te->set->dtype);
+ te->set->ops->remove(te->set, &te->elem);
nft_trans_destroy(trans);
break;
case NFT_MSG_DELSETELEM:
{
struct net *net = dev_net(pkt->in ? pkt->in : pkt->out);
- nf_log_packet(net, pkt->xt.family, pkt->ops->hooknum, pkt->skb, pkt->in,
- pkt->out, &trace_loginfo, "TRACE: %s:%s:%s:%u ",
- chain->table->name, chain->name, comments[type],
- rulenum);
+ nf_log_trace(net, pkt->xt.family, pkt->ops->hooknum, pkt->skb, pkt->in,
+ pkt->out, &trace_loginfo, "TRACE: %s:%s:%s:%u ",
+ chain->table->name, chain->name, comments[type],
+ rulenum);
}
unsigned int
if (!tb[NFCTH_TUPLE_L3PROTONUM] || !tb[NFCTH_TUPLE_L4PROTONUM])
return -EINVAL;
+ /* Not all fields are initialized so first zero the tuple */
+ memset(tuple, 0, sizeof(struct nf_conntrack_tuple));
+
tuple->src.l3num = ntohs(nla_get_be16(tb[NFCTH_TUPLE_L3PROTONUM]));
tuple->dst.protonum = nla_get_u8(tb[NFCTH_TUPLE_L4PROTONUM]);
nft_target_set_tgchk_param(struct xt_tgchk_param *par,
const struct nft_ctx *ctx,
struct xt_target *target, void *info,
- union nft_entry *entry, u8 proto, bool inv)
+ union nft_entry *entry, u16 proto, bool inv)
{
par->net = ctx->net;
par->table = ctx->table->name;
entry->e4.ip.invflags = inv ? IPT_INV_PROTO : 0;
break;
case AF_INET6:
+ if (proto)
+ entry->e6.ipv6.flags |= IP6T_F_PROTO;
+
entry->e6.ipv6.proto = proto;
entry->e6.ipv6.invflags = inv ? IP6T_INV_PROTO : 0;
break;
case NFPROTO_BRIDGE:
- entry->ebt.ethproto = proto;
+ entry->ebt.ethproto = (__force __be16)proto;
entry->ebt.invflags = inv ? EBT_IPROTO : 0;
break;
}
[NFTA_RULE_COMPAT_FLAGS] = { .type = NLA_U32 },
};
-static int nft_parse_compat(const struct nlattr *attr, u8 *proto, bool *inv)
+static int nft_parse_compat(const struct nlattr *attr, u16 *proto, bool *inv)
{
struct nlattr *tb[NFTA_RULE_COMPAT_MAX+1];
u32 flags;
struct xt_target *target = expr->ops->data;
struct xt_tgchk_param par;
size_t size = XT_ALIGN(nla_len(tb[NFTA_TARGET_INFO]));
- u8 proto = 0;
+ u16 proto = 0;
bool inv = false;
union nft_entry e = {};
int ret;
static void
nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx,
struct xt_match *match, void *info,
- union nft_entry *entry, u8 proto, bool inv)
+ union nft_entry *entry, u16 proto, bool inv)
{
par->net = ctx->net;
par->table = ctx->table->name;
entry->e4.ip.invflags = inv ? IPT_INV_PROTO : 0;
break;
case AF_INET6:
+ if (proto)
+ entry->e6.ipv6.flags |= IP6T_F_PROTO;
+
entry->e6.ipv6.proto = proto;
entry->e6.ipv6.invflags = inv ? IP6T_INV_PROTO : 0;
break;
case NFPROTO_BRIDGE:
- entry->ebt.ethproto = proto;
+ entry->ebt.ethproto = (__force __be16)proto;
entry->ebt.invflags = inv ? EBT_IPROTO : 0;
break;
}
struct xt_match *match = expr->ops->data;
struct xt_mtchk_param par;
size_t size = XT_ALIGN(nla_len(tb[NFTA_MATCH_INFO]));
- u8 proto = 0;
+ u16 proto = 0;
bool inv = false;
union nft_entry e = {};
int ret;
struct xt_match *match = nft_match->ops.data;
if (strcmp(match->name, mt_name) == 0 &&
- match->revision == rev && match->family == family)
+ match->revision == rev && match->family == family) {
+ if (!try_module_get(match->me))
+ return ERR_PTR(-ENOENT);
+
return &nft_match->ops;
+ }
}
match = xt_request_find_match(family, mt_name, rev);
struct xt_target *target = nft_target->ops.data;
if (strcmp(target->name, tg_name) == 0 &&
- target->revision == rev && target->family == family)
+ target->revision == rev && target->family == family) {
+ if (!try_module_get(target->me))
+ return ERR_PTR(-ENOENT);
+
return &nft_target->ops;
+ }
}
target = xt_request_find_target(family, tg_name, rev);
iter->err = err;
goto out;
}
+
+ continue;
}
if (iter->count < iter->skip)
.key_offset = offsetof(struct nft_hash_elem, key),
.key_len = set->klen,
.hashfn = jhash,
- .grow_decision = rht_grow_above_75,
- .shrink_decision = rht_shrink_below_30,
};
return rhashtable_init(priv, ¶ms);
{
const struct ip6t_ip6 *i = par->entryinfo;
- if ((i->proto == IPPROTO_TCP || i->proto == IPPROTO_UDP)
- && !(i->flags & IP6T_INV_PROTO))
+ if ((i->proto == IPPROTO_TCP || i->proto == IPPROTO_UDP) &&
+ !(i->invflags & IP6T_INV_PROTO))
return 0;
pr_info("Can be used only in combination with "
mutex_lock(&recent_mutex);
t = recent_table_lookup(recent_net, info->name);
if (t != NULL) {
- if (info->hit_count > t->nstamps_max_mask) {
- pr_info("hitcount (%u) is larger than packets to be remembered (%u) for table %s\n",
- info->hit_count, t->nstamps_max_mask + 1,
- info->name);
- ret = -EINVAL;
- goto out;
+ if (nstamp_mask > t->nstamps_max_mask) {
+ spin_lock_bh(&recent_lock);
+ recent_table_flush(t);
+ t->nstamps_max_mask = nstamp_mask;
+ spin_unlock_bh(&recent_lock);
}
t->refcnt++;
extract_icmp6_fields(const struct sk_buff *skb,
unsigned int outside_hdrlen,
int *protocol,
- struct in6_addr **raddr,
- struct in6_addr **laddr,
+ const struct in6_addr **raddr,
+ const struct in6_addr **laddr,
__be16 *rport,
- __be16 *lport)
+ __be16 *lport,
+ struct ipv6hdr *ipv6_var)
{
- struct ipv6hdr *inside_iph, _inside_iph;
+ const struct ipv6hdr *inside_iph;
struct icmp6hdr *icmph, _icmph;
__be16 *ports, _ports[2];
u8 inside_nexthdr;
if (icmph->icmp6_type & ICMPV6_INFOMSG_MASK)
return 1;
- inside_iph = skb_header_pointer(skb, outside_hdrlen + sizeof(_icmph), sizeof(_inside_iph), &_inside_iph);
+ inside_iph = skb_header_pointer(skb, outside_hdrlen + sizeof(_icmph),
+ sizeof(*ipv6_var), ipv6_var);
if (inside_iph == NULL)
return 1;
inside_nexthdr = inside_iph->nexthdr;
- inside_hdrlen = ipv6_skip_exthdr(skb, outside_hdrlen + sizeof(_icmph) + sizeof(_inside_iph),
+ inside_hdrlen = ipv6_skip_exthdr(skb, outside_hdrlen + sizeof(_icmph) +
+ sizeof(*ipv6_var),
&inside_nexthdr, &inside_fragoff);
if (inside_hdrlen < 0)
return 1; /* hjm: Packet has no/incomplete transport layer headers. */
static bool
socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par)
{
- struct ipv6hdr *iph = ipv6_hdr(skb);
+ struct ipv6hdr ipv6_var, *iph = ipv6_hdr(skb);
struct udphdr _hdr, *hp = NULL;
struct sock *sk = skb->sk;
- struct in6_addr *daddr = NULL, *saddr = NULL;
+ const struct in6_addr *daddr = NULL, *saddr = NULL;
__be16 uninitialized_var(dport), uninitialized_var(sport);
int thoff = 0, uninitialized_var(tproto);
const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo;
} else if (tproto == IPPROTO_ICMPV6) {
if (extract_icmp6_fields(skb, thoff, &tproto, &saddr, &daddr,
- &sport, &dport))
+ &sport, &dport, &ipv6_var))
return false;
} else {
return false;
.key_len = sizeof(u32), /* portid */
.hashfn = jhash,
.max_shift = 16, /* 64K */
- .grow_decision = rht_grow_above_75,
- .shrink_decision = rht_shrink_below_30,
};
if (err != 0)
return 0;
}
-static void __net_exit ovs_exit_net(struct net *net)
+static void __net_exit list_vports_from_net(struct net *net, struct net *dnet,
+ struct list_head *head)
{
- struct datapath *dp, *dp_next;
struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
+ struct datapath *dp;
+
+ list_for_each_entry(dp, &ovs_net->dps, list_node) {
+ int i;
+
+ for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) {
+ struct vport *vport;
+
+ hlist_for_each_entry(vport, &dp->ports[i], dp_hash_node) {
+ struct netdev_vport *netdev_vport;
+
+ if (vport->ops->type != OVS_VPORT_TYPE_INTERNAL)
+ continue;
+
+ netdev_vport = netdev_vport_priv(vport);
+ if (dev_net(netdev_vport->dev) == dnet)
+ list_add(&vport->detach_list, head);
+ }
+ }
+ }
+}
+
+static void __net_exit ovs_exit_net(struct net *dnet)
+{
+ struct datapath *dp, *dp_next;
+ struct ovs_net *ovs_net = net_generic(dnet, ovs_net_id);
+ struct vport *vport, *vport_next;
+ struct net *net;
+ LIST_HEAD(head);
ovs_lock();
list_for_each_entry_safe(dp, dp_next, &ovs_net->dps, list_node)
__dp_destroy(dp);
+
+ rtnl_lock();
+ for_each_net(net)
+ list_vports_from_net(net, dnet, &head);
+ rtnl_unlock();
+
+ /* Detach all vports from given namespace. */
+ list_for_each_entry_safe(vport, vport_next, &head, detach_list) {
+ list_del(&vport->detach_list);
+ ovs_dp_detach_port(vport);
+ }
+
ovs_unlock();
cancel_work_sync(&ovs_net->dp_notify_work);
struct sk_buff *skb)
{
const struct nlattr *ovs_key = nla_data(a);
+ struct nlattr *nla;
size_t key_len = nla_len(ovs_key) / 2;
/* Revert the conversion we did from a non-masked set action to
* masked set action.
*/
- if (nla_put(skb, OVS_ACTION_ATTR_SET, nla_len(a) - key_len, ovs_key))
+ nla = nla_nest_start(skb, OVS_ACTION_ATTR_SET);
+ if (!nla)
return -EMSGSIZE;
+ if (nla_put(skb, nla_type(ovs_key), key_len, nla_data(ovs_key)))
+ return -EMSGSIZE;
+
+ nla_nest_end(skb, nla);
return 0;
}
ASSERT_OVSL();
hlist_del_rcu(&vport->hash_node);
-
- vport->ops->destroy(vport);
-
module_put(vport->ops->owner);
+ vport->ops->destroy(vport);
}
/**
* @ops: Class structure.
* @percpu_stats: Points to per-CPU statistics used and maintained by vport
* @err_stats: Points to error statistics used and maintained by vport
+ * @detach_list: list used for detaching vport in net-exit call.
*/
struct vport {
struct rcu_head rcu;
struct pcpu_sw_netstats __percpu *percpu_stats;
struct vport_err_stats err_stats;
+ struct list_head detach_list;
};
/**
if (pkc->last_kactive_blk_num == pkc->kactive_blk_num) {
if (!frozen) {
+ if (!BLOCK_NUM_PKTS(pbd)) {
+ /* An empty block. Just refresh the timer. */
+ goto refresh_timer;
+ }
prb_retire_current_block(pkc, po, TP_STATUS_BLK_TMO);
if (!prb_dispatch_next_block(pkc, po))
goto refresh_timer;
h1->ts_last_pkt.ts_sec = last_pkt->tp_sec;
h1->ts_last_pkt.ts_nsec = last_pkt->tp_nsec;
} else {
- /* Ok, we tmo'd - so get the current time */
+ /* Ok, we tmo'd - so get the current time.
+ *
+ * It shouldn't really happen as we don't close empty
+ * blocks. See prb_retire_rx_blk_timer_expired().
+ */
struct timespec ts;
getnstimeofday(&ts);
h1->ts_last_pkt.ts_sec = ts.tv_sec;
return 0;
}
+ if (fanout_has_flag(f, PACKET_FANOUT_FLAG_DEFRAG)) {
+ skb = ip_check_defrag(skb, IP_DEFRAG_AF_PACKET);
+ if (!skb)
+ return 0;
+ }
switch (f->type) {
case PACKET_FANOUT_HASH:
default:
- if (fanout_has_flag(f, PACKET_FANOUT_FLAG_DEFRAG)) {
- skb = ip_check_defrag(skb, IP_DEFRAG_AF_PACKET);
- if (!skb)
- return 0;
- }
idx = fanout_demux_hash(f, skb, num);
break;
case PACKET_FANOUT_LB:
return 0;
}
-static void packet_dev_mclist(struct net_device *dev, struct packet_mclist *i, int what)
+static void packet_dev_mclist_delete(struct net_device *dev,
+ struct packet_mclist **mlp)
{
- for ( ; i; i = i->next) {
- if (i->ifindex == dev->ifindex)
- packet_dev_mc(dev, i, what);
+ struct packet_mclist *ml;
+
+ while ((ml = *mlp) != NULL) {
+ if (ml->ifindex == dev->ifindex) {
+ packet_dev_mc(dev, ml, -1);
+ *mlp = ml->next;
+ kfree(ml);
+ } else
+ mlp = &ml->next;
}
}
packet_dev_mc(dev, ml, -1);
kfree(ml);
}
- rtnl_unlock();
- return 0;
+ break;
}
}
rtnl_unlock();
- return -EADDRNOTAVAIL;
+ return 0;
}
static void packet_flush_mclist(struct sock *sk)
switch (msg) {
case NETDEV_UNREGISTER:
if (po->mclist)
- packet_dev_mclist(dev, po->mclist, -1);
+ packet_dev_mclist_delete(dev, &po->mclist);
/* fallthrough */
case NETDEV_DOWN:
int *unpinned);
static void rds_iw_destroy_fastreg(struct rds_iw_mr_pool *pool, struct rds_iw_mr *ibmr);
-static int rds_iw_get_device(struct rds_sock *rs, struct rds_iw_device **rds_iwdev, struct rdma_cm_id **cm_id)
+static int rds_iw_get_device(struct sockaddr_in *src, struct sockaddr_in *dst,
+ struct rds_iw_device **rds_iwdev,
+ struct rdma_cm_id **cm_id)
{
struct rds_iw_device *iwdev;
struct rds_iw_cm_id *i_cm_id;
src_addr->sin_port,
dst_addr->sin_addr.s_addr,
dst_addr->sin_port,
- rs->rs_bound_addr,
- rs->rs_bound_port,
- rs->rs_conn_addr,
- rs->rs_conn_port);
+ src->sin_addr.s_addr,
+ src->sin_port,
+ dst->sin_addr.s_addr,
+ dst->sin_port);
#ifdef WORKING_TUPLE_DETECTION
- if (src_addr->sin_addr.s_addr == rs->rs_bound_addr &&
- src_addr->sin_port == rs->rs_bound_port &&
- dst_addr->sin_addr.s_addr == rs->rs_conn_addr &&
- dst_addr->sin_port == rs->rs_conn_port) {
+ if (src_addr->sin_addr.s_addr == src->sin_addr.s_addr &&
+ src_addr->sin_port == src->sin_port &&
+ dst_addr->sin_addr.s_addr == dst->sin_addr.s_addr &&
+ dst_addr->sin_port == dst->sin_port) {
#else
/* FIXME - needs to compare the local and remote
* ipaddr/port tuple, but the ipaddr is the only
* zero'ed. It doesn't appear to be properly populated
* during connection setup...
*/
- if (src_addr->sin_addr.s_addr == rs->rs_bound_addr) {
+ if (src_addr->sin_addr.s_addr == src->sin_addr.s_addr) {
#endif
spin_unlock_irq(&iwdev->spinlock);
*rds_iwdev = iwdev;
{
struct sockaddr_in *src_addr, *dst_addr;
struct rds_iw_device *rds_iwdev_old;
- struct rds_sock rs;
struct rdma_cm_id *pcm_id;
int rc;
src_addr = (struct sockaddr_in *)&cm_id->route.addr.src_addr;
dst_addr = (struct sockaddr_in *)&cm_id->route.addr.dst_addr;
- rs.rs_bound_addr = src_addr->sin_addr.s_addr;
- rs.rs_bound_port = src_addr->sin_port;
- rs.rs_conn_addr = dst_addr->sin_addr.s_addr;
- rs.rs_conn_port = dst_addr->sin_port;
-
- rc = rds_iw_get_device(&rs, &rds_iwdev_old, &pcm_id);
+ rc = rds_iw_get_device(src_addr, dst_addr, &rds_iwdev_old, &pcm_id);
if (rc)
rds_iw_remove_cm_id(rds_iwdev, cm_id);
struct rds_iw_device *rds_iwdev;
struct rds_iw_mr *ibmr = NULL;
struct rdma_cm_id *cm_id;
+ struct sockaddr_in src = {
+ .sin_addr.s_addr = rs->rs_bound_addr,
+ .sin_port = rs->rs_bound_port,
+ };
+ struct sockaddr_in dst = {
+ .sin_addr.s_addr = rs->rs_conn_addr,
+ .sin_port = rs->rs_conn_port,
+ };
int ret;
- ret = rds_iw_get_device(rs, &rds_iwdev, &cm_id);
+ ret = rds_iw_get_device(&src, &dst, &rds_iwdev, &cm_id);
if (ret || !cm_id) {
ret = -ENODEV;
goto out;
struct rxrpc_header *hdr;
struct sk_buff *txb;
unsigned long *p_txb, resend_at;
- int loop, stop;
+ bool stop;
+ int loop;
u8 resend;
_enter("{%d,%d,%d,%d},",
atomic_read(&call->sequence),
CIRC_CNT(call->acks_head, call->acks_tail, call->acks_winsz));
- stop = 0;
+ stop = false;
resend = 0;
resend_at = 0;
_proto("Tx DATA %%%u { #%d }",
ntohl(sp->hdr.serial), ntohl(sp->hdr.seq));
if (rxrpc_send_packet(call->conn->trans, txb) < 0) {
- stop = 0;
+ stop = true;
sp->resend_at = jiffies + 3;
} else {
sp->resend_at =
- jiffies + rxrpc_resend_timeout * HZ;
+ jiffies + rxrpc_resend_timeout;
}
}
_leave("UDP socket errqueue empty");
return;
}
- if (!skb->len) {
+ serr = SKB_EXT_ERR(skb);
+ if (!skb->len && serr->ee.ee_origin == SO_EE_ORIGIN_TIMESTAMPING) {
_leave("UDP empty message");
kfree_skb(skb);
return;
rxrpc_new_skb(skb);
- serr = SKB_EXT_ERR(skb);
addr = *(__be32 *)(skb_network_header(skb) + serr->addr_offset);
port = serr->port;
if (!skb) {
/* nothing remains on the queue */
if (copied &&
- (msg->msg_flags & MSG_PEEK || timeo == 0))
+ (flags & MSG_PEEK || timeo == 0))
goto out;
/* wait for a message to turn up */
struct tcf_result *res)
{
struct tcf_bpf *b = a->priv;
- int action;
- int filter_res;
+ int action, filter_res;
spin_lock(&b->tcf_lock);
+
b->tcf_tm.lastuse = jiffies;
bstats_update(&b->tcf_bstats, skb);
- action = b->tcf_action;
filter_res = BPF_PROG_RUN(b->filter, skb);
- if (filter_res == 0) {
- /* Return code 0 from the BPF program
- * is being interpreted as a drop here.
- */
- action = TC_ACT_SHOT;
+
+ /* A BPF program may overwrite the default action opcode.
+ * Similarly as in cls_bpf, if filter_res == -1 we use the
+ * default action specified from tc.
+ *
+ * In case a different well-known TC_ACT opcode has been
+ * returned, it will overwrite the default one.
+ *
+ * For everything else that is unkown, TC_ACT_UNSPEC is
+ * returned.
+ */
+ switch (filter_res) {
+ case TC_ACT_PIPE:
+ case TC_ACT_RECLASSIFY:
+ case TC_ACT_OK:
+ action = filter_res;
+ break;
+ case TC_ACT_SHOT:
+ action = filter_res;
b->tcf_qstats.drops++;
+ break;
+ case TC_ACT_UNSPEC:
+ action = b->tcf_action;
+ break;
+ default:
+ action = TC_ACT_UNSPEC;
+ break;
}
spin_unlock(&b->tcf_lock);
struct tc_u_common *tp_c;
int refcnt;
unsigned int divisor;
- struct tc_u_knode __rcu *ht[1];
struct rcu_head rcu;
+ /* The 'ht' field MUST be the last field in structure to allow for
+ * more entries allocated at end of structure.
+ */
+ struct tc_u_knode __rcu *ht[1];
};
struct tc_u_common {
* to replay the request.
*/
module_put(em->ops->owner);
+ em->ops = NULL;
err = -EAGAIN;
}
#endif
if (len > INT_MAX)
len = INT_MAX;
+ if (unlikely(!access_ok(VERIFY_READ, buff, len)))
+ return -EFAULT;
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (!sock)
goto out;
if (size > INT_MAX)
size = INT_MAX;
+ if (unlikely(!access_ok(VERIFY_WRITE, ubuf, size)))
+ return -EFAULT;
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (!sock)
goto out;
for (i = 0; i < arg->npages && arg->pages[i]; i++)
__free_page(arg->pages[i]);
+
+ kfree(arg->pages);
}
static int gssp_alloc_receive_pages(struct gssx_arg_accept_sec_context *arg)
/* number of additional gid's */
if (get_int(&mesg, &N))
goto out;
+ if (N < 0 || N > NGROUPS_MAX)
+ goto out;
status = -ENOMEM;
rsci.cred.cr_group_info = groups_alloc(N);
if (rsci.cred.cr_group_info == NULL)
poll_wait(filp, &queue_wait, wait);
/* alway allow write */
- mask = POLL_OUT | POLLWRNORM;
+ mask = POLLOUT | POLLWRNORM;
if (!rp)
return mask;
struct super_block *pipefs_sb;
int err;
- err = rpc_clnt_debugfs_register(clnt);
- if (err)
- return err;
+ rpc_clnt_debugfs_register(clnt);
pipefs_sb = rpc_get_sb_net(net);
if (pipefs_sb) {
.release = tasks_release,
};
-int
+void
rpc_clnt_debugfs_register(struct rpc_clnt *clnt)
{
- int len, err;
+ int len;
char name[24]; /* enough for "../../rpc_xprt/ + 8 hex digits + NULL */
+ struct rpc_xprt *xprt;
/* Already registered? */
- if (clnt->cl_debugfs)
- return 0;
+ if (clnt->cl_debugfs || !rpc_clnt_dir)
+ return;
len = snprintf(name, sizeof(name), "%x", clnt->cl_clid);
if (len >= sizeof(name))
- return -EINVAL;
+ return;
/* make the per-client dir */
clnt->cl_debugfs = debugfs_create_dir(name, rpc_clnt_dir);
if (!clnt->cl_debugfs)
- return -ENOMEM;
+ return;
/* make tasks file */
- err = -ENOMEM;
if (!debugfs_create_file("tasks", S_IFREG | S_IRUSR, clnt->cl_debugfs,
clnt, &tasks_fops))
goto out_err;
- err = -EINVAL;
rcu_read_lock();
+ xprt = rcu_dereference(clnt->cl_xprt);
+ /* no "debugfs" dentry? Don't bother with the symlink. */
+ if (!xprt->debugfs) {
+ rcu_read_unlock();
+ return;
+ }
len = snprintf(name, sizeof(name), "../../rpc_xprt/%s",
- rcu_dereference(clnt->cl_xprt)->debugfs->d_name.name);
+ xprt->debugfs->d_name.name);
rcu_read_unlock();
+
if (len >= sizeof(name))
goto out_err;
- err = -ENOMEM;
if (!debugfs_create_symlink("xprt", clnt->cl_debugfs, name))
goto out_err;
- return 0;
+ return;
out_err:
debugfs_remove_recursive(clnt->cl_debugfs);
clnt->cl_debugfs = NULL;
- return err;
}
void
.release = xprt_info_release,
};
-int
+void
rpc_xprt_debugfs_register(struct rpc_xprt *xprt)
{
int len, id;
static atomic_t cur_id;
char name[9]; /* 8 hex digits + NULL term */
+ if (!rpc_xprt_dir)
+ return;
+
id = (unsigned int)atomic_inc_return(&cur_id);
len = snprintf(name, sizeof(name), "%x", id);
if (len >= sizeof(name))
- return -EINVAL;
+ return;
/* make the per-client dir */
xprt->debugfs = debugfs_create_dir(name, rpc_xprt_dir);
if (!xprt->debugfs)
- return -ENOMEM;
+ return;
/* make tasks file */
if (!debugfs_create_file("info", S_IFREG | S_IRUSR, xprt->debugfs,
xprt, &xprt_info_fops)) {
debugfs_remove_recursive(xprt->debugfs);
xprt->debugfs = NULL;
- return -ENOMEM;
}
-
- return 0;
}
void
sunrpc_debugfs_exit(void)
{
debugfs_remove_recursive(topdir);
+ topdir = NULL;
+ rpc_clnt_dir = NULL;
+ rpc_xprt_dir = NULL;
}
-int __init
+void __init
sunrpc_debugfs_init(void)
{
topdir = debugfs_create_dir("sunrpc", NULL);
if (!topdir)
- goto out;
+ return;
rpc_clnt_dir = debugfs_create_dir("rpc_clnt", topdir);
if (!rpc_clnt_dir)
if (!rpc_xprt_dir)
goto out_remove;
- return 0;
+ return;
out_remove:
debugfs_remove_recursive(topdir);
topdir = NULL;
-out:
- return -ENOMEM;
+ rpc_clnt_dir = NULL;
}
if (err)
goto out4;
- err = sunrpc_debugfs_init();
- if (err)
- goto out5;
-
+ sunrpc_debugfs_init();
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
rpc_register_sysctl();
#endif
init_socket_xprt(); /* clnt sock transport */
return 0;
-out5:
- unregister_rpc_pipefs();
out4:
unregister_pernet_subsys(&sunrpc_net_ops);
out3:
*/
struct rpc_xprt *xprt_create_transport(struct xprt_create *args)
{
- int err;
struct rpc_xprt *xprt;
struct xprt_class *t;
return ERR_PTR(-ENOMEM);
}
- err = rpc_xprt_debugfs_register(xprt);
- if (err) {
- xprt_destroy(xprt);
- return ERR_PTR(err);
- }
+ rpc_xprt_debugfs_register(xprt);
dprintk("RPC: created transport %p with %u slots\n", xprt,
xprt->max_reqs);
struct rpc_xprt *xprt = rep->rr_xprt;
struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(xprt);
__be32 *iptr;
- int credits, rdmalen, status;
+ int rdmalen, status;
unsigned long cwnd;
+ u32 credits;
/* Check status. If bad, signal disconnect and return rep to pool */
if (rep->rr_len == ~0U) {
*/
struct rpcrdma_buffer {
spinlock_t rb_lock; /* protects indexes */
- int rb_max_requests;/* client max requests */
+ u32 rb_max_requests;/* client max requests */
struct list_head rb_mws; /* optional memory windows/fmrs/frmrs */
struct list_head rb_all;
int rb_send_index;
static void __exit tipc_exit(void)
{
tipc_bearer_cleanup();
+ unregister_pernet_subsys(&tipc_net_ops);
tipc_netlink_stop();
tipc_netlink_compat_stop();
tipc_socket_stop();
tipc_unregister_sysctl();
- unregister_pernet_subsys(&tipc_net_ops);
pr_info("Deactivated\n");
}
/* Clean up all queues, except inputq: */
__skb_queue_purge(&l_ptr->outqueue);
__skb_queue_purge(&l_ptr->deferred_queue);
- skb_queue_splice_init(&l_ptr->wakeupq, &l_ptr->inputq);
- if (!skb_queue_empty(&l_ptr->inputq))
+ if (!owner->inputq)
+ owner->inputq = &l_ptr->inputq;
+ skb_queue_splice_init(&l_ptr->wakeupq, owner->inputq);
+ if (!skb_queue_empty(owner->inputq))
owner->action_flags |= TIPC_MSG_EVT;
- owner->inputq = &l_ptr->inputq;
l_ptr->next_out = NULL;
l_ptr->unacked_window = 0;
l_ptr->checkpoint = 1;
.hashfn = jhash,
.max_shift = 20, /* 1M */
.min_shift = 8, /* 256 */
- .grow_decision = rht_grow_above_75,
- .shrink_decision = rht_shrink_below_30,
};
return rhashtable_init(&tn->sk_rht, &rht_params);
regulatory_exit();
out_fail_reg:
debugfs_remove(ieee80211_debugfs_dir);
+ nl80211_exit();
out_fail_nl80211:
unregister_netdevice_notifier(&cfg80211_netdev_notifier);
out_fail_notifier:
return err;
}
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
-
err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
&flags);
!(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR))
return -EOPNOTSUPP;
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
wdev = rdev_add_virtual_intf(rdev,
nla_data(info->attrs[NL80211_ATTR_IFNAME]),
type, err ? NULL : &flags, ¶ms);
if (parse_station_flags(info, dev->ieee80211_ptr->iftype, ¶ms))
return -EINVAL;
+ /* HT/VHT requires QoS, but if we don't have that just ignore HT/VHT
+ * as userspace might just pass through the capabilities from the IEs
+ * directly, rather than enforcing this restriction and returning an
+ * error in this case.
+ */
+ if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME))) {
+ params.ht_capa = NULL;
+ params.vht_capa = NULL;
+ }
+
/* When you run into this, adjust the code below for the new flag */
BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7);
}
for (j = 0; j < match->n_channels; j++) {
- if (nla_put_u32(msg,
- NL80211_ATTR_WIPHY_FREQ,
- match->channels[j])) {
+ if (nla_put_u32(msg, j, match->channels[j])) {
nla_nest_cancel(msg, nl_freqs);
nla_nest_cancel(msg, nl_match);
goto out;
/* We keep a static world regulatory domain in case of the absence of CRDA */
static const struct ieee80211_regdomain world_regdom = {
- .n_reg_rules = 6,
+ .n_reg_rules = 8,
.alpha2 = "00",
.reg_rules = {
/* IEEE 802.11b/g, channels 1..11 */
* have the xfrm_state's. We need to wait for KM to
* negotiate new SA's or bail out with error.*/
if (net->xfrm.sysctl_larval_drop) {
- dst_release(dst);
- xfrm_pols_put(pols, drop_pols);
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES);
-
- return ERR_PTR(-EREMOTE);
+ err = -EREMOTE;
+ goto error;
}
err = -EAGAIN;
error:
dst_release(dst);
dropdst:
- dst_release(dst_orig);
+ if (!(flags & XFRM_LOOKUP_KEEP_DST_REF))
+ dst_release(dst_orig);
xfrm_pols_put(pols, drop_pols);
return ERR_PTR(err);
}
struct sock *sk, int flags)
{
struct dst_entry *dst = xfrm_lookup(net, dst_orig, fl, sk,
- flags | XFRM_LOOKUP_QUEUE);
+ flags | XFRM_LOOKUP_QUEUE |
+ XFRM_LOOKUP_KEEP_DST_REF);
if (IS_ERR(dst) && PTR_ERR(dst) == -EREMOTE)
return make_blackhole(net, dst_orig->ops->family, dst_orig);
--- /dev/null
+# nothing to do for the initialization of this package
goto out;
/* No partial writes. */
- length = EINVAL;
+ length = -EINVAL;
if (*ppos != 0)
goto out;
if (info->count < 1)
return -EINVAL;
+ if (!*info->id.name)
+ return -EINVAL;
+ if (strnlen(info->id.name, sizeof(info->id.name)) >= sizeof(info->id.name))
+ return -EINVAL;
access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
(info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
SNDRV_CTL_ELEM_ACCESS_INACTIVE|
if (! snd_pcm_playback_empty(substream)) {
snd_pcm_do_start(substream, SNDRV_PCM_STATE_DRAINING);
snd_pcm_post_start(substream, SNDRV_PCM_STATE_DRAINING);
+ } else {
+ runtime->status->state = SNDRV_PCM_STATE_SETUP;
}
break;
case SNDRV_PCM_STATE_RUNNING:
int pitchbend = chan->midi_pitchbend;
int segment;
+ if (pitchbend < -0x2000)
+ pitchbend = -0x2000;
if (pitchbend > 0x1FFF)
pitchbend = 0x1FFF;
*/
#define MAX_MIDI_RX_BLOCKS 8
-#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 µs */
+#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 microseconds */
/* isochronous header parameters */
#define ISO_DATA_LENGTH_SHIFT 16
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->unit = unit;
s->direction = dir;
s->flags = flags;
s->context = ERR_PTR(-1);
{
WARN_ON(amdtp_stream_running(s));
mutex_destroy(&s->mutex);
- fw_unit_put(s->unit);
}
EXPORT_SYMBOL(amdtp_stream_destroy);
return err;
}
+/*
+ * This module releases the FireWire unit data after all ALSA character devices
+ * are released by applications. This is for releasing stream data or finishing
+ * transactions safely. Thus at returning from .remove(), this module still keep
+ * references for the unit.
+ */
static void
bebob_card_free(struct snd_card *card)
{
struct snd_bebob *bebob = card->private_data;
+ snd_bebob_stream_destroy_duplex(bebob);
+ fw_unit_put(bebob->unit);
+
+ kfree(bebob->maudio_special_quirk);
+
if (bebob->card_index >= 0) {
mutex_lock(&devices_mutex);
clear_bit(bebob->card_index, devices_used);
card->private_free = bebob_card_free;
bebob->card = card;
- bebob->unit = unit;
+ bebob->unit = fw_unit_get(unit);
bebob->spec = spec;
mutex_init(&bebob->mutex);
spin_lock_init(&bebob->lock);
if (bebob == NULL)
return;
- kfree(bebob->maudio_special_quirk);
+ /* Awake bus-reset waiters. */
+ if (!completion_done(&bebob->bus_reset))
+ complete_all(&bebob->bus_reset);
- snd_bebob_stream_destroy_duplex(bebob);
- snd_card_disconnect(bebob->card);
+ /* No need to wait for releasing card object in this context. */
snd_card_free_when_closed(bebob->card);
}
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);
}
mutex_unlock(&bebob->mutex);
}
+/*
+ * This function should be called before starting streams or after stopping
+ * streams.
+ */
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);
}
/*
return err;
}
+/*
+ * This function should be called before starting streams or after stopping
+ * streams.
+ */
static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream)
{
- amdtp_stream_destroy(stream);
+ struct fw_iso_resources *resources;
if (stream == &dice->tx_stream)
- fw_iso_resources_destroy(&dice->tx_resources);
+ resources = &dice->tx_resources;
else
- fw_iso_resources_destroy(&dice->rx_resources);
+ resources = &dice->rx_resources;
+
+ amdtp_stream_destroy(stream);
+ fw_iso_resources_destroy(resources);
}
int snd_dice_stream_init_duplex(struct snd_dice *dice)
goto end;
err = init_stream(dice, &dice->rx_stream);
+ if (err < 0)
+ destroy_stream(dice, &dice->tx_stream);
end:
return err;
}
{
snd_dice_transaction_clear_enable(dice);
- stop_stream(dice, &dice->tx_stream);
destroy_stream(dice, &dice->tx_stream);
-
- stop_stream(dice, &dice->rx_stream);
destroy_stream(dice, &dice->rx_stream);
dice->substreams_counter = 0;
strcpy(card->mixername, "DICE");
}
+/*
+ * This module releases the FireWire unit data after all ALSA character devices
+ * are released by applications. This is for releasing stream data or finishing
+ * transactions safely. Thus at returning from .remove(), this module still keep
+ * references for the unit.
+ */
static void dice_card_free(struct snd_card *card)
{
struct snd_dice *dice = card->private_data;
+ snd_dice_stream_destroy_duplex(dice);
snd_dice_transaction_destroy(dice);
+ fw_unit_put(dice->unit);
+
mutex_destroy(&dice->mutex);
}
dice = card->private_data;
dice->card = card;
- dice->unit = unit;
+ dice->unit = fw_unit_get(unit);
card->private_free = dice_card_free;
spin_lock_init(&dice->lock);
{
struct snd_dice *dice = dev_get_drvdata(&unit->device);
- snd_card_disconnect(dice->card);
-
- snd_dice_stream_destroy_duplex(dice);
-
+ /* No need to wait for releasing card object in this context. */
snd_card_free_when_closed(dice->card);
}
return err;
}
+/*
+ * This module releases the FireWire unit data after all ALSA character devices
+ * are released by applications. This is for releasing stream data or finishing
+ * transactions safely. Thus at returning from .remove(), this module still keep
+ * references for the unit.
+ */
static void
efw_card_free(struct snd_card *card)
{
struct snd_efw *efw = card->private_data;
+ snd_efw_stream_destroy_duplex(efw);
+ snd_efw_transaction_remove_instance(efw);
+ fw_unit_put(efw->unit);
+
+ kfree(efw->resp_buf);
+
if (efw->card_index >= 0) {
mutex_lock(&devices_mutex);
clear_bit(efw->card_index, devices_used);
}
mutex_destroy(&efw->mutex);
- kfree(efw->resp_buf);
}
static int
card->private_free = efw_card_free;
efw->card = card;
- efw->unit = unit;
+ efw->unit = fw_unit_get(unit);
mutex_init(&efw->mutex);
spin_lock_init(&efw->lock);
init_waitqueue_head(&efw->hwdep_wait);
{
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);
+ /* No need to wait for releasing card object in this context. */
snd_card_free_when_closed(efw->card);
}
return err;
}
+/*
+ * This function should be called before starting the stream or after stopping
+ * the streams.
+ */
static void
destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)
{
- stop_stream(efw, stream);
-
- amdtp_stream_destroy(stream);
+ struct cmp_connection *conn;
if (stream == &efw->tx_stream)
- cmp_connection_destroy(&efw->out_conn);
+ conn = &efw->out_conn;
else
- cmp_connection_destroy(&efw->in_conn);
+ conn = &efw->in_conn;
+
+ amdtp_stream_destroy(stream);
+ cmp_connection_destroy(&efw->out_conn);
}
static int
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)
int fw_iso_resources_init(struct fw_iso_resources *r, struct fw_unit *unit)
{
r->channels_mask = ~0uLL;
- r->unit = fw_unit_get(unit);
+ r->unit = unit;
mutex_init(&r->mutex);
r->allocated = false;
{
WARN_ON(r->allocated);
mutex_destroy(&r->mutex);
- fw_unit_put(r->unit);
}
EXPORT_SYMBOL(fw_iso_resources_destroy);
}
/* Wait first packet */
- err = amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT);
- if (err < 0)
+ if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
stop_stream(oxfw, stream);
+ err = -ETIMEDOUT;
+ }
end:
return err;
}
stop_stream(oxfw, stream);
}
+/*
+ * This function should be called before starting the stream or after stopping
+ * the streams.
+ */
void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw,
struct amdtp_stream *stream)
{
else
conn = &oxfw->in_conn;
- stop_stream(oxfw, stream);
-
amdtp_stream_destroy(stream);
cmp_connection_destroy(conn);
}
return err;
}
+/*
+ * This module releases the FireWire unit data after all ALSA character devices
+ * are released by applications. This is for releasing stream data or finishing
+ * transactions safely. Thus at returning from .remove(), this module still keep
+ * references for the unit.
+ */
static void oxfw_card_free(struct snd_card *card)
{
struct snd_oxfw *oxfw = card->private_data;
unsigned int i;
+ snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
+ if (oxfw->has_output)
+ snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream);
+
+ fw_unit_put(oxfw->unit);
+
for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
kfree(oxfw->tx_stream_formats[i]);
kfree(oxfw->rx_stream_formats[i]);
oxfw = card->private_data;
oxfw->card = card;
mutex_init(&oxfw->mutex);
- oxfw->unit = unit;
+ oxfw->unit = fw_unit_get(unit);
oxfw->device_info = (const struct device_info *)id->driver_data;
spin_lock_init(&oxfw->lock);
init_waitqueue_head(&oxfw->hwdep_wait);
{
struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
- snd_card_disconnect(oxfw->card);
-
- snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
- if (oxfw->has_output)
- snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream);
-
+ /* No need to wait for releasing card object in this context. */
snd_card_free_when_closed(oxfw->card);
}
spin_lock_init(&chip->mixer_lock);
strcpy(card->mixername, "MSND Pinnacle Mixer");
- for (idx = 0; idx < ARRAY_SIZE(snd_msnd_controls); idx++)
+ for (idx = 0; idx < ARRAY_SIZE(snd_msnd_controls); idx++) {
err = snd_ctl_add(card,
snd_ctl_new1(snd_msnd_controls + idx, chip));
if (err < 0)
return err;
+ }
return 0;
}
dev_err(chip->card->dev, "cannot allocate CORB/RIRB\n");
return err;
}
-EXPORT_SYMBOL_GPL(azx_alloc_cmd_io);
static void azx_init_cmd_io(struct azx *chip)
{
azx_writeb(chip, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN);
spin_unlock_irq(&chip->reg_lock);
}
-EXPORT_SYMBOL_GPL(azx_init_cmd_io);
static void azx_free_cmd_io(struct azx *chip)
{
azx_writeb(chip, CORBCTL, 0);
spin_unlock_irq(&chip->reg_lock);
}
-EXPORT_SYMBOL_GPL(azx_free_cmd_io);
static unsigned int azx_command_addr(u32 cmd)
{
}
}
- if (!bus->no_response_fallback)
+ if (bus->no_response_fallback)
return -1;
if (!chip->polling_mode && chip->poll_count < 2) {
else
return azx_corb_send_cmd(bus, val);
}
-EXPORT_SYMBOL_GPL(azx_send_cmd);
/* get a response */
static unsigned int azx_get_response(struct hda_bus *bus,
else
return azx_rirb_get_response(bus, addr);
}
-EXPORT_SYMBOL_GPL(azx_get_response);
#ifdef CONFIG_SND_HDA_DSP_LOADER
/*
return val;
}
+/* is this a stereo widget or a stereo-to-mono mix? */
+static bool is_stereo_amps(struct hda_codec *codec, hda_nid_t nid, int dir)
+{
+ unsigned int wcaps = get_wcaps(codec, nid);
+ hda_nid_t conn;
+
+ if (wcaps & AC_WCAP_STEREO)
+ return true;
+ if (dir != HDA_INPUT || get_wcaps_type(wcaps) != AC_WID_AUD_MIX)
+ return false;
+ if (snd_hda_get_num_conns(codec, nid) != 1)
+ return false;
+ if (snd_hda_get_connections(codec, nid, &conn, 1) < 0)
+ return false;
+ return !!(get_wcaps(codec, conn) & AC_WCAP_STEREO);
+}
+
/* initialize the amp value (only at the first time) */
static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx)
{
unsigned int caps = query_amp_caps(codec, nid, dir);
int val = get_amp_val_to_activate(codec, nid, dir, caps, false);
- snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val);
+
+ if (is_stereo_amps(codec, nid, dir))
+ snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val);
+ else
+ snd_hda_codec_amp_init(codec, nid, 0, dir, idx, 0xff, val);
+}
+
+/* update the amp, doing in stereo or mono depending on NID */
+static int update_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx,
+ unsigned int mask, unsigned int val)
+{
+ if (is_stereo_amps(codec, nid, dir))
+ return snd_hda_codec_amp_stereo(codec, nid, dir, idx,
+ mask, val);
+ else
+ return snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
+ mask, val);
}
/* calculate amp value mask we can modify;
return;
val &= mask;
- snd_hda_codec_amp_stereo(codec, nid, dir, idx, mask, val);
+ update_amp(codec, nid, dir, idx, mask, val);
}
static void activate_amp_out(struct hda_codec *codec, struct nid_path *path,
has_amp = nid_has_mute(codec, mix, HDA_INPUT);
for (i = 0; i < nums; i++) {
if (has_amp)
- snd_hda_codec_amp_stereo(codec, mix,
- HDA_INPUT, i,
- 0xff, HDA_AMP_MUTE);
+ update_amp(codec, mix, HDA_INPUT, i,
+ 0xff, HDA_AMP_MUTE);
else if (nid_has_volume(codec, conn[i], HDA_OUTPUT))
- snd_hda_codec_amp_stereo(codec, conn[i],
- HDA_OUTPUT, 0,
- 0xff, HDA_AMP_MUTE);
+ update_amp(codec, conn[i], HDA_OUTPUT, 0,
+ 0xff, HDA_AMP_MUTE);
}
}
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM },
/* Panther Point */
{ PCI_DEVICE(0x8086, 0x1e20),
- .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM },
/* Lynx Point */
{ PCI_DEVICE(0x8086, 0x8c20),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
/* Sunrise Point */
{ PCI_DEVICE(0x8086, 0xa170),
- .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE },
/* Sunrise Point-LP */
{ PCI_DEVICE(0x8086, 0x9d70),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE },
(caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT);
}
+/* is this a stereo widget or a stereo-to-mono mix? */
+static bool is_stereo_amps(struct hda_codec *codec, hda_nid_t nid,
+ int dir, unsigned int wcaps, int indices)
+{
+ hda_nid_t conn;
+
+ if (wcaps & AC_WCAP_STEREO)
+ return true;
+ /* check for a stereo-to-mono mix; it must be:
+ * only a single connection, only for input, and only a mixer widget
+ */
+ if (indices != 1 || dir != HDA_INPUT ||
+ get_wcaps_type(wcaps) != AC_WID_AUD_MIX)
+ return false;
+
+ if (snd_hda_get_raw_connections(codec, nid, &conn, 1) < 0)
+ return false;
+ /* the connection source is a stereo? */
+ wcaps = snd_hda_param_read(codec, conn, AC_PAR_AUDIO_WIDGET_CAP);
+ return !!(wcaps & AC_WCAP_STEREO);
+}
+
static void print_amp_vals(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid,
- int dir, int stereo, int indices)
+ int dir, unsigned int wcaps, int indices)
{
unsigned int val;
+ bool stereo;
int i;
+ stereo = is_stereo_amps(codec, nid, dir, wcaps, indices);
+
dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
for (i = 0; i < indices; i++) {
snd_iprintf(buffer, " [");
(codec->single_adc_amp &&
wid_type == AC_WID_AUD_IN))
print_amp_vals(buffer, codec, nid, HDA_INPUT,
- wid_caps & AC_WCAP_STEREO,
- 1);
+ wid_caps, 1);
else
print_amp_vals(buffer, codec, nid, HDA_INPUT,
- wid_caps & AC_WCAP_STEREO,
- conn_len);
+ wid_caps, conn_len);
}
if (wid_caps & AC_WCAP_OUT_AMP) {
snd_iprintf(buffer, " Amp-Out caps: ");
if (wid_type == AC_WID_PIN &&
codec->pin_amp_workaround)
print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
- wid_caps & AC_WCAP_STEREO,
- conn_len);
+ wid_caps, conn_len);
else
print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
- wid_caps & AC_WCAP_STEREO, 1);
+ wid_caps, 1);
}
switch (wid_type) {
SND_PCI_QUIRK(0x106b, 0x1c00, "MacBookPro 8,1", CS420X_MBP81),
SND_PCI_QUIRK(0x106b, 0x2000, "iMac 12,2", CS420X_IMAC27_122),
SND_PCI_QUIRK(0x106b, 0x2800, "MacBookPro 10,1", CS420X_MBP101),
+ SND_PCI_QUIRK(0x106b, 0x5600, "MacBookAir 5,2", CS420X_MBP81),
SND_PCI_QUIRK(0x106b, 0x5b00, "MacBookAir 4,2", CS420X_MBA42),
SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS420X_APPLE),
{} /* terminator */
return -ENOMEM;
spec->gen.automute_hook = cs_automute;
+ codec->single_adc_amp = 1;
snd_hda_pick_fixup(codec, cs420x_models, cs420x_fixup_tbl,
cs420x_fixups);
CXT_PINCFG_LENOVO_TP410,
CXT_PINCFG_LEMOTE_A1004,
CXT_PINCFG_LEMOTE_A1205,
+ CXT_PINCFG_COMPAQ_CQ60,
CXT_FIXUP_STEREO_DMIC,
CXT_FIXUP_INC_MIC_BOOST,
CXT_FIXUP_HEADPHONE_MIC_PIN,
.type = HDA_FIXUP_PINS,
.v.pins = cxt_pincfg_lemote,
},
+ [CXT_PINCFG_COMPAQ_CQ60] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* 0x17 was falsely set up as a mic, it should 0x1d */
+ { 0x17, 0x400001f0 },
+ { 0x1d, 0x97a70120 },
+ { }
+ }
+ },
[CXT_FIXUP_STEREO_DMIC] = {
.type = HDA_FIXUP_FUNC,
.v.func = cxt_fixup_stereo_dmic,
};
static const struct snd_pci_quirk cxt5051_fixups[] = {
+ SND_PCI_QUIRK(0x103c, 0x360b, "Compaq CQ60", CXT_PINCFG_COMPAQ_CQ60),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT_PINCFG_LENOVO_X200),
{}
};
{
/* We currently only handle front, HP */
static hda_nid_t pins[] = {
- 0x0f, 0x10, 0x14, 0x15, 0
+ 0x0f, 0x10, 0x14, 0x15, 0x17, 0
};
hda_nid_t *p;
for (p = pins; *p; p++)
SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC283_FIXUP_INT_MIC),
SND_PCI_QUIRK(0x17aa, 0x501e, "Thinkpad L440", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x5026, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x5036, "Thinkpad T450s", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
{0x17, 0x40000000},
{0x1d, 0x40700001},
{0x21, 0x02211040}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC255_STANDARD_PINS,
+ {0x12, 0x90a60170},
+ {0x14, 0x90170140},
+ {0x17, 0x40000000},
+ {0x1d, 0x40700001},
+ {0x21, 0x02211050}),
SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC280_FIXUP_HP_GPIO4,
{0x12, 0x90a60130},
{0x13, 0x40000000},
STAC_ALIENWARE_M17X,
STAC_92HD89XX_HP_FRONT_JACK,
STAC_92HD89XX_HP_Z1_G2_RIGHT_MIC_JACK,
+ STAC_92HD73XX_ASUS_MOBO,
STAC_92HD73XX_MODELS
};
[STAC_92HD89XX_HP_Z1_G2_RIGHT_MIC_JACK] = {
.type = HDA_FIXUP_PINS,
.v.pins = stac92hd89xx_hp_z1_g2_right_mic_jack_pin_configs,
- }
+ },
+ [STAC_92HD73XX_ASUS_MOBO] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* enable 5.1 and SPDIF out */
+ { 0x0c, 0x01014411 },
+ { 0x0d, 0x01014410 },
+ { 0x0e, 0x01014412 },
+ { 0x22, 0x014b1180 },
+ { }
+ }
+ },
};
static const struct hda_model_fixup stac92hd73xx_models[] = {
{ .id = STAC_DELL_M6_BOTH, .name = "dell-m6" },
{ .id = STAC_DELL_EQ, .name = "dell-eq" },
{ .id = STAC_ALIENWARE_M17X, .name = "alienware" },
+ { .id = STAC_92HD73XX_ASUS_MOBO, .name = "asus-mobo" },
{}
};
"HP Z1 G2", STAC_92HD89XX_HP_Z1_G2_RIGHT_MIC_JACK),
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2b17,
"unknown HP", STAC_92HD89XX_HP_FRONT_JACK),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_ASUSTEK, 0x83f8, "ASUS AT4NM10",
+ STAC_92HD73XX_ASUS_MOBO),
{} /* terminator */
};
return IRQ_HANDLED;
}
+/*
+ * When the bit clock is input, limit the maximum rate according to the
+ * Serial Clock Ratio Considerations section from the SSC documentation:
+ *
+ * The Transmitter and the Receiver can be programmed to operate
+ * with the clock signals provided on either the TK or RK pins.
+ * This allows the SSC to support many slave-mode data transfers.
+ * In this case, the maximum clock speed allowed on the RK pin is:
+ * - Peripheral clock divided by 2 if Receiver Frame Synchro is input
+ * - Peripheral clock divided by 3 if Receiver Frame Synchro is output
+ * In addition, the maximum clock speed allowed on the TK pin is:
+ * - Peripheral clock divided by 6 if Transmit Frame Synchro is input
+ * - Peripheral clock divided by 2 if Transmit Frame Synchro is output
+ *
+ * When the bit clock is output, limit the rate according to the
+ * SSC divider restrictions.
+ */
+static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct atmel_ssc_info *ssc_p = rule->private;
+ struct ssc_device *ssc = ssc_p->ssc;
+ struct snd_interval *i = hw_param_interval(params, rule->var);
+ struct snd_interval t;
+ struct snd_ratnum r = {
+ .den_min = 1,
+ .den_max = 4095,
+ .den_step = 1,
+ };
+ unsigned int num = 0, den = 0;
+ int frame_size;
+ int mck_div = 2;
+ int ret;
+
+ frame_size = snd_soc_params_to_frame_size(params);
+ if (frame_size < 0)
+ return frame_size;
+
+ switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFS:
+ if ((ssc_p->dir_mask & SSC_DIR_MASK_CAPTURE)
+ && ssc->clk_from_rk_pin)
+ /* Receiver Frame Synchro (i.e. capture)
+ * is output (format is _CFS) and the RK pin
+ * is used for input (format is _CBM_).
+ */
+ mck_div = 3;
+ break;
+
+ case SND_SOC_DAIFMT_CBM_CFM:
+ if ((ssc_p->dir_mask & SSC_DIR_MASK_PLAYBACK)
+ && !ssc->clk_from_rk_pin)
+ /* Transmit Frame Synchro (i.e. playback)
+ * is input (format is _CFM) and the TK pin
+ * is used for input (format _CBM_ but not
+ * using the RK pin).
+ */
+ mck_div = 6;
+ break;
+ }
+
+ switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ r.num = ssc_p->mck_rate / mck_div / frame_size;
+
+ ret = snd_interval_ratnum(i, 1, &r, &num, &den);
+ if (ret >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) {
+ params->rate_num = num;
+ params->rate_den = den;
+ }
+ break;
+
+ case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBM_CFM:
+ t.min = 8000;
+ t.max = ssc_p->mck_rate / mck_div / frame_size;
+ t.openmin = t.openmax = 0;
+ t.integer = 0;
+ ret = snd_interval_refine(i, &t);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
/*-------------------------------------------------------------------------*\
* DAI functions
struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
struct atmel_pcm_dma_params *dma_params;
int dir, dir_mask;
+ int ret;
pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n",
ssc_readl(ssc_p->ssc->regs, SR));
/* Enable PMC peripheral clock for this SSC */
pr_debug("atmel_ssc_dai: Starting clock\n");
clk_enable(ssc_p->ssc->clk);
+ ssc_p->mck_rate = clk_get_rate(ssc_p->ssc->clk);
/* Reset the SSC to keep it at a clean status */
ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
dir_mask = SSC_DIR_MASK_CAPTURE;
}
+ ret = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ atmel_ssc_hw_rule_rate,
+ ssc_p,
+ SNDRV_PCM_HW_PARAM_FRAME_BITS,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to specify rate rule: %d\n", ret);
+ return ret;
+ }
+
dma_params = &ssc_dma_params[dai->id][dir];
dma_params->ssc = ssc_p->ssc;
dma_params->substream = substream;
# define atmel_ssc_resume NULL
#endif /* CONFIG_PM */
-#define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000)
-
#define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
.playback = {
.channels_min = 1,
.channels_max = 2,
- .rates = ATMEL_SSC_RATES,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 8000,
+ .rate_max = 384000,
.formats = ATMEL_SSC_FORMATS,},
.capture = {
.channels_min = 1,
.channels_max = 2,
- .rates = ATMEL_SSC_RATES,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 8000,
+ .rate_max = 384000,
.formats = ATMEL_SSC_FORMATS,},
.ops = &atmel_ssc_dai_ops,
};
unsigned short rcmr_period;
struct atmel_pcm_dma_params *dma_params[2];
struct atmel_ssc_state ssc_state;
+ unsigned long mck_rate;
};
int atmel_ssc_set_audio(int ssc_id);
#include <sound/pcm_params.h>
#include <sound/soc.h>
-#include <asm/mach-types.h>
-
#include "../codecs/wm8731.h"
#include "atmel-pcm.h"
#include "atmel_ssc_dai.h"
int ret;
if (!np) {
- if (!(machine_is_at91sam9g20ek() ||
- machine_is_at91sam9g20ek_2mmc()))
- return -ENODEV;
+ return -ENODEV;
}
ret = atmel_ssc_set_audio(0);
card->dev = &pdev->dev;
/* Parse device node info */
- if (np) {
- ret = snd_soc_of_parse_card_name(card, "atmel,model");
- if (ret)
- goto err;
-
- ret = snd_soc_of_parse_audio_routing(card,
- "atmel,audio-routing");
- if (ret)
- goto err;
-
- /* Parse codec info */
- at91sam9g20ek_dai.codec_name = NULL;
- codec_np = of_parse_phandle(np, "atmel,audio-codec", 0);
- if (!codec_np) {
- dev_err(&pdev->dev, "codec info missing\n");
- return -EINVAL;
- }
- at91sam9g20ek_dai.codec_of_node = codec_np;
-
- /* Parse dai and platform info */
- at91sam9g20ek_dai.cpu_dai_name = NULL;
- at91sam9g20ek_dai.platform_name = NULL;
- cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0);
- if (!cpu_np) {
- dev_err(&pdev->dev, "dai and pcm info missing\n");
- return -EINVAL;
- }
- at91sam9g20ek_dai.cpu_of_node = cpu_np;
- at91sam9g20ek_dai.platform_of_node = cpu_np;
-
- of_node_put(codec_np);
- of_node_put(cpu_np);
+ ret = snd_soc_of_parse_card_name(card, "atmel,model");
+ if (ret)
+ goto err;
+
+ ret = snd_soc_of_parse_audio_routing(card,
+ "atmel,audio-routing");
+ if (ret)
+ goto err;
+
+ /* Parse codec info */
+ at91sam9g20ek_dai.codec_name = NULL;
+ codec_np = of_parse_phandle(np, "atmel,audio-codec", 0);
+ if (!codec_np) {
+ dev_err(&pdev->dev, "codec info missing\n");
+ return -EINVAL;
+ }
+ at91sam9g20ek_dai.codec_of_node = codec_np;
+
+ /* Parse dai and platform info */
+ at91sam9g20ek_dai.cpu_dai_name = NULL;
+ at91sam9g20ek_dai.platform_name = NULL;
+ cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0);
+ if (!cpu_np) {
+ dev_err(&pdev->dev, "dai and pcm info missing\n");
+ return -EINVAL;
}
+ at91sam9g20ek_dai.cpu_of_node = cpu_np;
+ at91sam9g20ek_dai.platform_of_node = cpu_np;
+
+ of_node_put(codec_np);
+ of_node_put(cpu_np);
ret = snd_soc_register_card(card);
if (ret) {
config SND_EP93XX_SOC_SNAPPERCL15
tristate "SoC Audio support for Bluewater Systems Snapper CL15 module"
- depends on SND_EP93XX_SOC && MACH_SNAPPER_CL15
+ depends on SND_EP93XX_SOC && MACH_SNAPPER_CL15 && I2C
select SND_EP93XX_SOC_I2S
select SND_SOC_TLV320AIC23_I2C
help
select SND_SOC_MAX98088 if I2C
select SND_SOC_MAX98090 if I2C
select SND_SOC_MAX98095 if I2C
- select SND_SOC_MAX98357A
+ select SND_SOC_MAX98357A if GPIOLIB
select SND_SOC_MAX9850 if I2C
select SND_SOC_MAX9768 if I2C
select SND_SOC_MAX9877 if I2C
select SND_SOC_WM8770 if SPI_MASTER
select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8782
- select SND_SOC_WM8804 if SND_SOC_I2C_AND_SPI
+ select SND_SOC_WM8804_I2C if I2C
+ select SND_SOC_WM8804_SPI if SPI_MASTER
select SND_SOC_WM8900 if I2C
select SND_SOC_WM8903 if I2C
select SND_SOC_WM8904 if I2C
tristate
config SND_SOC_WM8804
- tristate "Wolfson Microelectronics WM8804 S/PDIF transceiver"
- depends on SND_SOC_I2C_AND_SPI
+ tristate
+
+config SND_SOC_WM8804_I2C
+ tristate "Wolfson Microelectronics WM8804 S/PDIF transceiver I2C"
+ depends on I2C
+ select SND_SOC_WM8804
+ select REGMAP_I2C
+
+config SND_SOC_WM8804_SPI
+ tristate "Wolfson Microelectronics WM8804 S/PDIF transceiver SPI"
+ depends on SPI_MASTER
+ select SND_SOC_WM8804
+ select REGMAP_SPI
config SND_SOC_WM8900
tristate
snd-soc-wm8776-objs := wm8776.o
snd-soc-wm8782-objs := wm8782.o
snd-soc-wm8804-objs := wm8804.o
+snd-soc-wm8804-i2c-objs := wm8804-i2c.o
+snd-soc-wm8804-spi-objs := wm8804-spi.o
snd-soc-wm8900-objs := wm8900.o
snd-soc-wm8903-objs := wm8903.o
snd-soc-wm8904-objs := wm8904.o
obj-$(CONFIG_SND_SOC_WM8776) += snd-soc-wm8776.o
obj-$(CONFIG_SND_SOC_WM8782) += snd-soc-wm8782.o
obj-$(CONFIG_SND_SOC_WM8804) += snd-soc-wm8804.o
+obj-$(CONFIG_SND_SOC_WM8804_I2C) += snd-soc-wm8804-i2c.o
+obj-$(CONFIG_SND_SOC_WM8804_SPI) += snd-soc-wm8804-spi.o
obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o
obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o
obj-$(CONFIG_SND_SOC_WM8904) += snd-soc-wm8904.o
adau1977->dvdd_reg = NULL;
}
- adau1977->reset_gpio = devm_gpiod_get(dev, "reset");
- if (IS_ERR(adau1977->reset_gpio)) {
- ret = PTR_ERR(adau1977->reset_gpio);
- if (ret != -ENOENT && ret != -ENOSYS)
- return PTR_ERR(adau1977->reset_gpio);
- adau1977->reset_gpio = NULL;
- }
+ adau1977->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(adau1977->reset_gpio))
+ return PTR_ERR(adau1977->reset_gpio);
dev_set_drvdata(dev, adau1977);
- if (adau1977->reset_gpio) {
- ret = gpiod_direction_output(adau1977->reset_gpio, 0);
- if (ret)
- return ret;
+ if (adau1977->reset_gpio)
ndelay(100);
- }
ret = adau1977_power_enable(adau1977);
if (ret)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
- unsigned int deemph = ucontrol->value.enumerated.item[0];
+ unsigned int deemph = ucontrol->value.integer.value[0];
if (deemph > 1)
return -EINVAL;
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
- ucontrol->value.enumerated.item[0] = adav80x->deemph;
+ ucontrol->value.integer.value[0] = adav80x->deemph;
return 0;
};
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
- int deemph = ucontrol->value.enumerated.item[0];
+ int deemph = ucontrol->value.integer.value[0];
if (deemph > 1)
return -EINVAL;
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
- ucontrol->value.enumerated.item[0] = ak4641->deemph;
+ ucontrol->value.integer.value[0] = ak4641->deemph;
return 0;
};
.name = "ak4642-hifi",
.playback = {
.stream_name = "Playback",
- .channels_min = 1,
+ .channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE },
.capture = {
.stream_name = "Capture",
- .channels_min = 1,
+ .channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE },
};
static const struct snd_soc_dapm_route ak4671_intercon[] = {
- {"DAC Left", "NULL", "PMPLL"},
- {"DAC Right", "NULL", "PMPLL"},
- {"ADC Left", "NULL", "PMPLL"},
- {"ADC Right", "NULL", "PMPLL"},
+ {"DAC Left", NULL, "PMPLL"},
+ {"DAC Right", NULL, "PMPLL"},
+ {"ADC Left", NULL, "PMPLL"},
+ {"ADC Right", NULL, "PMPLL"},
/* Outputs */
- {"LOUT1", "NULL", "LOUT1 Mixer"},
- {"ROUT1", "NULL", "ROUT1 Mixer"},
- {"LOUT2", "NULL", "LOUT2 Mix Amp"},
- {"ROUT2", "NULL", "ROUT2 Mix Amp"},
- {"LOUT3", "NULL", "LOUT3 Mixer"},
- {"ROUT3", "NULL", "ROUT3 Mixer"},
+ {"LOUT1", NULL, "LOUT1 Mixer"},
+ {"ROUT1", NULL, "ROUT1 Mixer"},
+ {"LOUT2", NULL, "LOUT2 Mix Amp"},
+ {"ROUT2", NULL, "ROUT2 Mix Amp"},
+ {"LOUT3", NULL, "LOUT3 Mixer"},
+ {"ROUT3", NULL, "ROUT3 Mixer"},
{"LOUT1 Mixer", "DACL", "DAC Left"},
{"ROUT1 Mixer", "DACR", "DAC Right"},
{"LOUT2 Mixer", "DACHL", "DAC Left"},
{"ROUT2 Mixer", "DACHR", "DAC Right"},
- {"LOUT2 Mix Amp", "NULL", "LOUT2 Mixer"},
- {"ROUT2 Mix Amp", "NULL", "ROUT2 Mixer"},
+ {"LOUT2 Mix Amp", NULL, "LOUT2 Mixer"},
+ {"ROUT2 Mix Amp", NULL, "ROUT2 Mixer"},
{"LOUT3 Mixer", "DACSL", "DAC Left"},
{"ROUT3 Mixer", "DACSR", "DAC Right"},
{"LIN2", NULL, "Mic Bias"},
{"RIN2", NULL, "Mic Bias"},
- {"ADC Left", "NULL", "LIN MUX"},
- {"ADC Right", "NULL", "RIN MUX"},
+ {"ADC Left", NULL, "LIN MUX"},
+ {"ADC Right", NULL, "RIN MUX"},
/* Analog Loops */
- {"LIN1 Mixing Circuit", "NULL", "LIN1"},
- {"RIN1 Mixing Circuit", "NULL", "RIN1"},
- {"LIN2 Mixing Circuit", "NULL", "LIN2"},
- {"RIN2 Mixing Circuit", "NULL", "RIN2"},
- {"LIN3 Mixing Circuit", "NULL", "LIN3"},
- {"RIN3 Mixing Circuit", "NULL", "RIN3"},
- {"LIN4 Mixing Circuit", "NULL", "LIN4"},
- {"RIN4 Mixing Circuit", "NULL", "RIN4"},
+ {"LIN1 Mixing Circuit", NULL, "LIN1"},
+ {"RIN1 Mixing Circuit", NULL, "RIN1"},
+ {"LIN2 Mixing Circuit", NULL, "LIN2"},
+ {"RIN2 Mixing Circuit", NULL, "RIN2"},
+ {"LIN3 Mixing Circuit", NULL, "LIN3"},
+ {"RIN3 Mixing Circuit", NULL, "RIN3"},
+ {"LIN4 Mixing Circuit", NULL, "LIN4"},
+ {"RIN4 Mixing Circuit", NULL, "RIN4"},
{"LOUT1 Mixer", "LINL1", "LIN1 Mixing Circuit"},
{"ROUT1 Mixer", "RINR1", "RIN1 Mixing Circuit"},
}
/* Reset the Device */
- cs35l32->reset_gpio = devm_gpiod_get(&i2c_client->dev,
- "reset-gpios");
- if (IS_ERR(cs35l32->reset_gpio)) {
- ret = PTR_ERR(cs35l32->reset_gpio);
- if (ret != -ENOENT && ret != -ENOSYS)
- return ret;
-
- cs35l32->reset_gpio = NULL;
- } else {
- ret = gpiod_direction_output(cs35l32->reset_gpio, 0);
- if (ret)
- return ret;
+ cs35l32->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l32->reset_gpio))
+ return PTR_ERR(cs35l32->reset_gpio);
+
+ if (cs35l32->reset_gpio)
gpiod_set_value_cansleep(cs35l32->reset_gpio, 1);
- }
/* initialize codec */
ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_AB, ®);
return ret;
}
- cs4265->reset_gpio = devm_gpiod_get(&i2c_client->dev,
- "reset-gpios");
- if (IS_ERR(cs4265->reset_gpio)) {
- ret = PTR_ERR(cs4265->reset_gpio);
- if (ret != -ENOENT && ret != -ENOSYS)
- return ret;
-
- cs4265->reset_gpio = NULL;
- } else {
- ret = gpiod_direction_output(cs4265->reset_gpio, 0);
- if (ret)
- return ret;
+ cs4265->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs4265->reset_gpio))
+ return PTR_ERR(cs4265->reset_gpio);
+
+ if (cs4265->reset_gpio) {
mdelay(1);
gpiod_set_value_cansleep(cs4265->reset_gpio, 1);
-
}
i2c_set_clientdata(i2c_client, cs4265);
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
- ucontrol->value.enumerated.item[0] = cs4271->deemph;
+ ucontrol->value.integer.value[0] = cs4271->deemph;
return 0;
}
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
- cs4271->deemph = ucontrol->value.enumerated.item[0];
+ cs4271->deemph = ucontrol->value.integer.value[0];
return cs4271_set_deemph(codec);
}
if (gpio_is_valid(cs4271->gpio_nreset)) {
/* Reset codec */
gpio_direction_output(cs4271->gpio_nreset, 0);
- udelay(1);
+ mdelay(1);
gpio_set_value(cs4271->gpio_nreset, 1);
/* Give the codec time to wake up */
- udelay(1);
+ mdelay(1);
}
ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
static const struct snd_soc_dapm_route da732x_dapm_routes[] = {
/* Inputs */
- {"AUX1L PGA", "NULL", "AUX1L"},
- {"AUX1R PGA", "NULL", "AUX1R"},
+ {"AUX1L PGA", NULL, "AUX1L"},
+ {"AUX1R PGA", NULL, "AUX1R"},
{"MIC1 PGA", NULL, "MIC1"},
- {"MIC2 PGA", "NULL", "MIC2"},
- {"MIC3 PGA", "NULL", "MIC3"},
+ {"MIC2 PGA", NULL, "MIC2"},
+ {"MIC3 PGA", NULL, "MIC3"},
/* Capture Path */
{"ADC1 Left MUX", "MIC1", "MIC1 PGA"},
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
- ucontrol->value.enumerated.item[0] = es8328->deemph;
+ ucontrol->value.integer.value[0] = es8328->deemph;
return 0;
}
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
- int deemph = ucontrol->value.enumerated.item[0];
+ int deemph = ucontrol->value.integer.value[0];
int ret;
if (deemph > 1)
* max98357a.c -- MAX98357A ALSA SoC Codec driver
*/
-#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
#include <sound/soc.h>
-
-#define DRV_NAME "max98357a"
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
static int max98357a_daiops_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
};
static struct snd_soc_dai_driver max98357a_dai_driver = {
- .name = DRV_NAME,
+ .name = "HiFi",
.playback = {
- .stream_name = DRV_NAME "-playback",
+ .stream_name = "HiFi Playback",
.formats = SNDRV_PCM_FMTBIT_S16 |
SNDRV_PCM_FMTBIT_S24 |
SNDRV_PCM_FMTBIT_S32,
#ifdef CONFIG_OF
static const struct of_device_id max98357a_device_id[] = {
- { .compatible = "maxim," DRV_NAME, },
+ { .compatible = "maxim,max98357a" },
{}
};
MODULE_DEVICE_TABLE(of, max98357a_device_id);
static struct platform_driver max98357a_platform_driver = {
.driver = {
- .name = DRV_NAME,
+ .name = "max98357a",
.of_match_table = of_match_ptr(max98357a_device_id),
},
.probe = max98357a_platform_probe,
MODULE_DESCRIPTION("Maxim MAX98357A Codec Driver");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:" DRV_NAME);
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
- ucontrol->value.enumerated.item[0] = priv->deemph;
+ ucontrol->value.integer.value[0] = priv->deemph;
return 0;
}
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
- priv->deemph = ucontrol->value.enumerated.item[0];
+ priv->deemph = ucontrol->value.integer.value[0];
return pcm1681_set_deemph(codec);
}
int pll_d;
int pll_p;
unsigned long real_pll;
+ unsigned long overclock_pll;
+ unsigned long overclock_dac;
+ unsigned long overclock_dsp;
};
/*
}
}
+static int pcm512x_overclock_pll_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = pcm512x->overclock_pll;
+ return 0;
+}
+
+static int pcm512x_overclock_pll_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+
+ switch (codec->dapm.bias_level) {
+ case SND_SOC_BIAS_OFF:
+ case SND_SOC_BIAS_STANDBY:
+ break;
+ default:
+ return -EBUSY;
+ }
+
+ pcm512x->overclock_pll = ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static int pcm512x_overclock_dsp_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = pcm512x->overclock_dsp;
+ return 0;
+}
+
+static int pcm512x_overclock_dsp_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+
+ switch (codec->dapm.bias_level) {
+ case SND_SOC_BIAS_OFF:
+ case SND_SOC_BIAS_STANDBY:
+ break;
+ default:
+ return -EBUSY;
+ }
+
+ pcm512x->overclock_dsp = ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static int pcm512x_overclock_dac_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = pcm512x->overclock_dac;
+ return 0;
+}
+
+static int pcm512x_overclock_dac_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+
+ switch (codec->dapm.bias_level) {
+ case SND_SOC_BIAS_OFF:
+ case SND_SOC_BIAS_STANDBY:
+ break;
+ default:
+ return -EBUSY;
+ }
+
+ pcm512x->overclock_dac = ucontrol->value.integer.value[0];
+ return 0;
+}
+
static const DECLARE_TLV_DB_SCALE(digital_tlv, -10350, 50, 1);
static const DECLARE_TLV_DB_SCALE(analog_tlv, -600, 600, 0);
static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 80, 0);
static const struct snd_kcontrol_new pcm512x_controls[] = {
SOC_DOUBLE_R_TLV("Digital Playback Volume", PCM512x_DIGITAL_VOLUME_2,
PCM512x_DIGITAL_VOLUME_3, 0, 255, 1, digital_tlv),
-SOC_DOUBLE_TLV("Playback Volume", PCM512x_ANALOG_GAIN_CTRL,
+SOC_DOUBLE_TLV("Analogue Playback Volume", PCM512x_ANALOG_GAIN_CTRL,
PCM512x_LAGN_SHIFT, PCM512x_RAGN_SHIFT, 1, 1, analog_tlv),
-SOC_DOUBLE_TLV("Playback Boost Volume", PCM512x_ANALOG_GAIN_BOOST,
+SOC_DOUBLE_TLV("Analogue Playback Boost Volume", PCM512x_ANALOG_GAIN_BOOST,
PCM512x_AGBL_SHIFT, PCM512x_AGBR_SHIFT, 1, 0, boost_tlv),
SOC_DOUBLE("Digital Playback Switch", PCM512x_MUTE, PCM512x_RQML_SHIFT,
PCM512x_RQMR_SHIFT, 1, 1),
SOC_ENUM("Volume Ramp Up Step", pcm512x_vnus),
SOC_ENUM("Volume Ramp Down Emergency Rate", pcm512x_vedf),
SOC_ENUM("Volume Ramp Down Emergency Step", pcm512x_veds),
+
+SOC_SINGLE_EXT("Max Overclock PLL", SND_SOC_NOPM, 0, 20, 0,
+ pcm512x_overclock_pll_get, pcm512x_overclock_pll_put),
+SOC_SINGLE_EXT("Max Overclock DSP", SND_SOC_NOPM, 0, 40, 0,
+ pcm512x_overclock_dsp_get, pcm512x_overclock_dsp_put),
+SOC_SINGLE_EXT("Max Overclock DAC", SND_SOC_NOPM, 0, 40, 0,
+ pcm512x_overclock_dac_get, pcm512x_overclock_dac_put),
};
static const struct snd_soc_dapm_widget pcm512x_dapm_widgets[] = {
{ "OUTR", NULL, "DACR" },
};
+static unsigned long pcm512x_pll_max(struct pcm512x_priv *pcm512x)
+{
+ return 25000000 + 25000000 * pcm512x->overclock_pll / 100;
+}
+
+static unsigned long pcm512x_dsp_max(struct pcm512x_priv *pcm512x)
+{
+ return 50000000 + 50000000 * pcm512x->overclock_dsp / 100;
+}
+
+static unsigned long pcm512x_dac_max(struct pcm512x_priv *pcm512x,
+ unsigned long rate)
+{
+ return rate + rate * pcm512x->overclock_dac / 100;
+}
+
+static unsigned long pcm512x_sck_max(struct pcm512x_priv *pcm512x)
+{
+ if (!pcm512x->pll_out)
+ return 25000000;
+ return pcm512x_pll_max(pcm512x);
+}
+
+static unsigned long pcm512x_ncp_target(struct pcm512x_priv *pcm512x,
+ unsigned long dac_rate)
+{
+ /*
+ * If the DAC is not actually overclocked, use the good old
+ * NCP target rate...
+ */
+ if (dac_rate <= 6144000)
+ return 1536000;
+ /*
+ * ...but if the DAC is in fact overclocked, bump the NCP target
+ * rate to get the recommended dividers even when overclocking.
+ */
+ return pcm512x_dac_max(pcm512x, 1536000);
+}
+
static const u32 pcm512x_dai_rates[] = {
8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000,
88200, 96000, 176400, 192000, 384000,
static int pcm512x_hw_rule_rate(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
+ struct pcm512x_priv *pcm512x = rule->private;
struct snd_interval ranges[2];
int frame_size;
*/
memset(ranges, 0, sizeof(ranges));
ranges[0].min = 8000;
- ranges[0].max = 25000000 / frame_size / 2;
+ ranges[0].max = pcm512x_sck_max(pcm512x) / frame_size / 2;
ranges[1].min = DIV_ROUND_UP(16000000, frame_size);
ranges[1].max = 384000;
break;
return snd_pcm_hw_rule_add(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
pcm512x_hw_rule_rate,
- NULL,
+ pcm512x,
SNDRV_PCM_HW_PARAM_FRAME_BITS,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
unsigned long bclk_rate)
{
struct device *dev = dai->dev;
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
unsigned long sck_rate;
int pow2;
* as many factors of 2 as possible, as that makes it easier
* to find a fast DAC rate
*/
- pow2 = 1 << fls((25000000 - 16000000) / bclk_rate);
+ pow2 = 1 << fls((pcm512x_pll_max(pcm512x) - 16000000) / bclk_rate);
for (; pow2; pow2 >>= 1) {
- sck_rate = rounddown(25000000, bclk_rate * pow2);
+ sck_rate = rounddown(pcm512x_pll_max(pcm512x),
+ bclk_rate * pow2);
if (sck_rate >= 16000000)
break;
}
/* pllin_rate / P (or here, den) cannot be greater than 20 MHz */
if (pllin_rate / den > 20000000 && num < 8) {
- num *= 20000000 / (pllin_rate / den);
- den *= 20000000 / (pllin_rate / den);
+ num *= DIV_ROUND_UP(pllin_rate / den, 20000000);
+ den *= DIV_ROUND_UP(pllin_rate / den, 20000000);
}
dev_dbg(dev, "num / den = %lu / %lu\n", num, den);
return 0; /* futile, quit early */
/* run DAC no faster than 6144000 Hz */
- for (dac_rate = rounddown(6144000, osr_rate);
+ for (dac_rate = rounddown(pcm512x_dac_max(pcm512x, 6144000), osr_rate);
dac_rate;
dac_rate -= osr_rate) {
osr_rate = 16 * sample_rate;
/* run DSP no faster than 50 MHz */
- dsp_div = mck_rate > 50000000 ? 2 : 1;
+ dsp_div = mck_rate > pcm512x_dsp_max(pcm512x) ? 2 : 1;
dac_rate = pcm512x_pllin_dac_rate(dai, osr_rate, pllin_rate);
if (dac_rate) {
dacsrc_rate = pllin_rate;
} else {
/* run DAC no faster than 6144000 Hz */
- unsigned long dac_mul = 6144000 / osr_rate;
+ unsigned long dac_mul = pcm512x_dac_max(pcm512x, 6144000)
+ / osr_rate;
unsigned long sck_mul = sck_rate / osr_rate;
for (; dac_mul; dac_mul--) {
dacsrc_rate = sck_rate;
}
+ osr_div = DIV_ROUND_CLOSEST(dac_rate, osr_rate);
+ if (osr_div > 128) {
+ dev_err(dev, "Failed to find OSR divider\n");
+ return -EINVAL;
+ }
+
dac_div = DIV_ROUND_CLOSEST(dacsrc_rate, dac_rate);
if (dac_div > 128) {
dev_err(dev, "Failed to find DAC divider\n");
return -EINVAL;
}
+ dac_rate = dacsrc_rate / dac_div;
- ncp_div = DIV_ROUND_CLOSEST(dacsrc_rate / dac_div, 1536000);
- if (ncp_div > 128 || dacsrc_rate / dac_div / ncp_div > 2048000) {
+ ncp_div = DIV_ROUND_CLOSEST(dac_rate,
+ pcm512x_ncp_target(pcm512x, dac_rate));
+ if (ncp_div > 128 || dac_rate / ncp_div > 2048000) {
/* run NCP no faster than 2048000 Hz, but why? */
- ncp_div = DIV_ROUND_UP(dacsrc_rate / dac_div, 2048000);
+ ncp_div = DIV_ROUND_UP(dac_rate, 2048000);
if (ncp_div > 128) {
dev_err(dev, "Failed to find NCP divider\n");
return -EINVAL;
}
}
- osr_div = DIV_ROUND_CLOSEST(dac_rate, osr_rate);
- if (osr_div > 128) {
- dev_err(dev, "Failed to find OSR divider\n");
- return -EINVAL;
- }
-
idac = mck_rate / (dsp_div * sample_rate);
ret = regmap_write(pcm512x->regmap, PCM512x_DSP_CLKDIV, dsp_div - 1);
return ret;
}
- if (sample_rate <= 48000)
+ if (sample_rate <= pcm512x_dac_max(pcm512x, 48000))
fssp = PCM512x_FSSP_48KHZ;
- else if (sample_rate <= 96000)
+ else if (sample_rate <= pcm512x_dac_max(pcm512x, 96000))
fssp = PCM512x_FSSP_96KHZ;
- else if (sample_rate <= 192000)
+ else if (sample_rate <= pcm512x_dac_max(pcm512x, 192000))
fssp = PCM512x_FSSP_192KHZ;
else
fssp = PCM512x_FSSP_384KHZ;
ret, pcm512x->pll_out);
return ret;
}
-
- gpio = PCM512x_G1OE << (4 - 1);
- ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_EN,
- gpio, gpio);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to enable gpio %d: %d\n",
- 4, ret);
- return ret;
- }
-
- gpio = PCM512x_GPIO_OUTPUT_1 + 4 - 1;
- ret = regmap_update_bits(pcm512x->regmap, gpio,
- PCM512x_GxSL, PCM512x_GxSL_PLLLK);
- if (ret != 0) {
- dev_err(codec->dev,
- "Failed to output pll lock on %d: %d\n",
- ret, 4);
- return ret;
- }
}
ret = regmap_update_bits(pcm512x->regmap, PCM512x_SYNCHRONIZE,
rt286->jack = jack;
- /* Send an initial empty report */
- snd_soc_jack_report(rt286->jack, 0,
- SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+ if (jack) {
+ /* enable IRQ */
+ if (rt286->jack->status | SND_JACK_HEADPHONE)
+ snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO1");
+ regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x2, 0x2);
+ /* Send an initial empty report */
+ snd_soc_jack_report(rt286->jack, rt286->jack->status,
+ SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+ } else {
+ /* disable IRQ */
+ regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x2, 0x0);
+ snd_soc_dapm_disable_pin(&codec->dapm, "LDO1");
+ }
+ snd_soc_dapm_sync(&codec->dapm);
return 0;
}
.ident = "Dell Dino",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_BOARD_NAME, "0144P8")
+ DMI_MATCH(DMI_PRODUCT_NAME, "XPS 13 9343")
}
},
{ }
case RT5670_ADC_EQ_CTRL1:
case RT5670_EQ_CTRL1:
case RT5670_ALC_CTRL_1:
- case RT5670_IRQ_CTRL1:
case RT5670_IRQ_CTRL2:
case RT5670_INT_IRQ_ST:
case RT5670_IL_CMD:
}
}
+/**
+ * rt5670_headset_detect - Detect headset.
+ * @codec: SoC audio codec device.
+ * @jack_insert: Jack insert or not.
+ *
+ * Detect whether is headset or not when jack inserted.
+ *
+ * Returns detect status.
+ */
+
+static int rt5670_headset_detect(struct snd_soc_codec *codec, int jack_insert)
+{
+ int val;
+ struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+
+ if (jack_insert) {
+ snd_soc_dapm_force_enable_pin(&codec->dapm,
+ "Mic Det Power");
+ snd_soc_dapm_sync(&codec->dapm);
+ snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x0);
+ snd_soc_update_bits(codec, RT5670_CJ_CTRL2,
+ RT5670_CBJ_DET_MODE | RT5670_CBJ_MN_JD,
+ RT5670_CBJ_MN_JD);
+ snd_soc_write(codec, RT5670_GPIO_CTRL2, 0x0004);
+ snd_soc_update_bits(codec, RT5670_GPIO_CTRL1,
+ RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_IRQ);
+ snd_soc_update_bits(codec, RT5670_CJ_CTRL1,
+ RT5670_CBJ_BST1_EN, RT5670_CBJ_BST1_EN);
+ snd_soc_write(codec, RT5670_JD_CTRL3, 0x00f0);
+ snd_soc_update_bits(codec, RT5670_CJ_CTRL2,
+ RT5670_CBJ_MN_JD, RT5670_CBJ_MN_JD);
+ snd_soc_update_bits(codec, RT5670_CJ_CTRL2,
+ RT5670_CBJ_MN_JD, 0);
+ msleep(300);
+ val = snd_soc_read(codec, RT5670_CJ_CTRL3) & 0x7;
+ if (val == 0x1 || val == 0x2) {
+ rt5670->jack_type = SND_JACK_HEADSET;
+ /* for push button */
+ snd_soc_update_bits(codec, RT5670_INT_IRQ_ST, 0x8, 0x8);
+ snd_soc_update_bits(codec, RT5670_IL_CMD, 0x40, 0x40);
+ snd_soc_read(codec, RT5670_IL_CMD);
+ } else {
+ snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4);
+ rt5670->jack_type = SND_JACK_HEADPHONE;
+ snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power");
+ snd_soc_dapm_sync(&codec->dapm);
+ }
+ } else {
+ snd_soc_update_bits(codec, RT5670_INT_IRQ_ST, 0x8, 0x0);
+ snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4);
+ rt5670->jack_type = 0;
+ snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power");
+ snd_soc_dapm_sync(&codec->dapm);
+ }
+
+ return rt5670->jack_type;
+}
+
+void rt5670_jack_suspend(struct snd_soc_codec *codec)
+{
+ struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+
+ rt5670->jack_type_saved = rt5670->jack_type;
+ rt5670_headset_detect(codec, 0);
+}
+EXPORT_SYMBOL_GPL(rt5670_jack_suspend);
+
+void rt5670_jack_resume(struct snd_soc_codec *codec)
+{
+ struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+
+ if (rt5670->jack_type_saved)
+ rt5670_headset_detect(codec, 1);
+}
+EXPORT_SYMBOL_GPL(rt5670_jack_resume);
+
+static int rt5670_button_detect(struct snd_soc_codec *codec)
+{
+ int btn_type, val;
+
+ val = snd_soc_read(codec, RT5670_IL_CMD);
+ btn_type = val & 0xff80;
+ snd_soc_write(codec, RT5670_IL_CMD, val);
+ if (btn_type != 0) {
+ msleep(20);
+ val = snd_soc_read(codec, RT5670_IL_CMD);
+ snd_soc_write(codec, RT5670_IL_CMD, val);
+ }
+
+ return btn_type;
+}
+
+static int rt5670_irq_detection(void *data)
+{
+ struct rt5670_priv *rt5670 = (struct rt5670_priv *)data;
+ struct snd_soc_jack_gpio *gpio = &rt5670->hp_gpio;
+ struct snd_soc_jack *jack = rt5670->jack;
+ int val, btn_type, report = jack->status;
+
+ if (rt5670->pdata.jd_mode == 1) /* 2 port */
+ val = snd_soc_read(rt5670->codec, RT5670_A_JD_CTRL1) & 0x0070;
+ else
+ val = snd_soc_read(rt5670->codec, RT5670_A_JD_CTRL1) & 0x0020;
+
+ switch (val) {
+ /* jack in */
+ case 0x30: /* 2 port */
+ case 0x0: /* 1 port or 2 port */
+ if (rt5670->jack_type == 0) {
+ report = rt5670_headset_detect(rt5670->codec, 1);
+ /* for push button and jack out */
+ gpio->debounce_time = 25;
+ break;
+ }
+ btn_type = 0;
+ if (snd_soc_read(rt5670->codec, RT5670_INT_IRQ_ST) & 0x4) {
+ /* button pressed */
+ report = SND_JACK_HEADSET;
+ btn_type = rt5670_button_detect(rt5670->codec);
+ switch (btn_type) {
+ case 0x2000: /* up */
+ report |= SND_JACK_BTN_1;
+ break;
+ case 0x0400: /* center */
+ report |= SND_JACK_BTN_0;
+ break;
+ case 0x0080: /* down */
+ report |= SND_JACK_BTN_2;
+ break;
+ default:
+ dev_err(rt5670->codec->dev,
+ "Unexpected button code 0x%04x\n",
+ btn_type);
+ break;
+ }
+ }
+ if (btn_type == 0)/* button release */
+ report = rt5670->jack_type;
+
+ break;
+ /* jack out */
+ case 0x70: /* 2 port */
+ case 0x10: /* 2 port */
+ case 0x20: /* 1 port */
+ report = 0;
+ snd_soc_update_bits(rt5670->codec, RT5670_INT_IRQ_ST, 0x1, 0x0);
+ rt5670_headset_detect(rt5670->codec, 0);
+ gpio->debounce_time = 150; /* for jack in */
+ break;
+ default:
+ break;
+ }
+
+ return report;
+}
+
+int rt5670_set_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack)
+{
+ struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ rt5670->jack = jack;
+ rt5670->hp_gpio.gpiod_dev = codec->dev;
+ rt5670->hp_gpio.name = "headphone detect";
+ rt5670->hp_gpio.report = SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2;
+ rt5670->hp_gpio.debounce_time = 150;
+ rt5670->hp_gpio.wake = true;
+ rt5670->hp_gpio.data = (struct rt5670_priv *)rt5670;
+ rt5670->hp_gpio.jack_status_check = rt5670_irq_detection;
+
+ ret = snd_soc_jack_add_gpios(rt5670->jack, 1,
+ &rt5670->hp_gpio);
+ if (ret) {
+ dev_err(codec->dev, "Adding jack GPIO failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5670_set_jack_detect);
+
static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0);
static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
struct snd_soc_dapm_widget *sink)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- unsigned int val;
+ struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
- val = snd_soc_read(codec, RT5670_GLB_CLK);
- val &= RT5670_SCLK_SRC_MASK;
- if (val == RT5670_SCLK_SRC_PLL1)
+ if (rt5670->sysclk_src == RT5670_SCLK_S_PLL1)
return 1;
else
return 0;
struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
unsigned int reg_val = 0;
- if (freq == rt5670->sysclk && clk_id == rt5670->sysclk_src)
- return 0;
-
- if (rt5670->pdata.jd_mode) {
- if (clk_id == RT5670_SCLK_S_PLL1)
- snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL1");
- else
- snd_soc_dapm_disable_pin(&codec->dapm, "PLL1");
- snd_soc_dapm_sync(&codec->dapm);
- }
switch (clk_id) {
case RT5670_SCLK_S_MCLK:
reg_val |= RT5670_SCLK_SRC_MCLK;
snd_soc_update_bits(codec, RT5670_GLB_CLK,
RT5670_SCLK_SRC_MASK, reg_val);
rt5670->sysclk = freq;
- rt5670->sysclk_src = clk_id;
+ if (clk_id != RT5670_SCLK_S_RCCLK)
+ rt5670->sysclk_src = clk_id;
dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
regmap_write(rt5670->regmap, RT5670_RESET, 0);
+ snd_soc_jack_free_gpios(rt5670->jack, 1, &rt5670->hp_gpio);
return 0;
}
if (dmi_check_system(dmi_platform_intel_braswell)) {
rt5670->pdata.dmic_en = true;
rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_IN2P;
+ rt5670->pdata.dev_gpio = true;
rt5670->pdata.jd_mode = 1;
}
regmap_write(rt5670->regmap, RT5670_RESET, 0);
+ regmap_read(rt5670->regmap, RT5670_VENDOR_ID, &val);
+ if (val >= 4)
+ regmap_write(rt5670->regmap, RT5670_GPIO_CTRL3, 0x0980);
+ else
+ regmap_write(rt5670->regmap, RT5670_GPIO_CTRL3, 0x0d00);
+
ret = regmap_register_patch(rt5670->regmap, init_list,
ARRAY_SIZE(init_list));
if (ret != 0)
regmap_update_bits(rt5670->regmap, RT5670_IN2,
RT5670_IN_DF2, RT5670_IN_DF2);
- if (i2c->irq) {
+ if (rt5670->pdata.dev_gpio) {
+ /* for push button */
+ regmap_write(rt5670->regmap, RT5670_IL_CMD, 0x0000);
+ regmap_write(rt5670->regmap, RT5670_IL_CMD2, 0x0010);
+ regmap_write(rt5670->regmap, RT5670_IL_CMD3, 0x0014);
+ /* for irq */
regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1,
RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_IRQ);
regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL2,
RT5670_GP1_PF_MASK, RT5670_GP1_PF_OUT);
-
+ regmap_update_bits(rt5670->regmap, RT5670_DIG_MISC, 0x8, 0x8);
}
if (rt5670->pdata.jd_mode) {
};
enum {
+ RT5670_DMIC1_DISABLED,
RT5670_DMIC_DATA_GPIO6,
RT5670_DMIC_DATA_IN2P,
RT5670_DMIC_DATA_GPIO7,
};
enum {
+ RT5670_DMIC2_DISABLED,
RT5670_DMIC_DATA_GPIO8,
RT5670_DMIC_DATA_IN3N,
};
enum {
+ RT5670_DMIC3_DISABLED,
RT5670_DMIC_DATA_GPIO9,
RT5670_DMIC_DATA_GPIO10,
RT5670_DMIC_DATA_GPIO5,
struct snd_soc_codec *codec;
struct rt5670_platform_data pdata;
struct regmap *regmap;
+ struct snd_soc_jack *jack;
+ struct snd_soc_jack_gpio hp_gpio;
int sysclk;
int sysclk_src;
int dsp_sw; /* expected parameter setting */
int dsp_rate;
int jack_type;
+ int jack_type_saved;
};
+void rt5670_jack_suspend(struct snd_soc_codec *codec);
+void rt5670_jack_resume(struct snd_soc_codec *codec);
+int rt5670_set_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack);
#endif /* __RT5670_H__ */
RT5677_LDO1_SEL_MASK, 0x0);
regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
RT5677_PWR_LDO1, RT5677_PWR_LDO1);
- regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
- RT5677_MCLK_SRC_MASK, RT5677_MCLK2_SRC);
- regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2,
- RT5677_PLL2_PR_SRC_MASK | RT5677_DSP_CLK_SRC_MASK,
- RT5677_PLL2_PR_SRC_MCLK2 | RT5677_DSP_CLK_SRC_BYPASS);
+ switch (rt5677->type) {
+ case RT5677:
+ regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
+ RT5677_MCLK_SRC_MASK, RT5677_MCLK2_SRC);
+ regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2,
+ RT5677_PLL2_PR_SRC_MASK |
+ RT5677_DSP_CLK_SRC_MASK,
+ RT5677_PLL2_PR_SRC_MCLK2 |
+ RT5677_DSP_CLK_SRC_BYPASS);
+ break;
+ case RT5676:
+ regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2,
+ RT5677_DSP_CLK_SRC_MASK,
+ RT5677_DSP_CLK_SRC_BYPASS);
+ break;
+ default:
+ break;
+ }
regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x07ff);
regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x07fd);
rt5677_set_dsp_mode(codec, true);
{ "IB45 Bypass Mux", "Bypass", "IB45 Mux" },
{ "IB45 Bypass Mux", "Pass SRC", "IB45 Mux" },
- { "IB6 Mux", "IF1 DAC 6", "IF1 DAC6" },
- { "IB6 Mux", "IF2 DAC 6", "IF2 DAC6" },
+ { "IB6 Mux", "IF1 DAC 6", "IF1 DAC6 Mux" },
+ { "IB6 Mux", "IF2 DAC 6", "IF2 DAC6 Mux" },
{ "IB6 Mux", "SLB DAC 6", "SLB DAC6" },
{ "IB6 Mux", "STO4 ADC MIX L", "Stereo4 ADC MIXL" },
{ "IB6 Mux", "IF4 DAC L", "IF4 DAC L" },
{ "IB6 Mux", "STO2 ADC MIX L", "Stereo2 ADC MIXL" },
{ "IB6 Mux", "STO3 ADC MIX L", "Stereo3 ADC MIXL" },
- { "IB7 Mux", "IF1 DAC 7", "IF1 DAC7" },
- { "IB7 Mux", "IF2 DAC 7", "IF2 DAC7" },
+ { "IB7 Mux", "IF1 DAC 7", "IF1 DAC7 Mux" },
+ { "IB7 Mux", "IF2 DAC 7", "IF2 DAC7 Mux" },
{ "IB7 Mux", "SLB DAC 7", "SLB DAC7" },
{ "IB7 Mux", "STO4 ADC MIX R", "Stereo4 ADC MIXR" },
{ "IB7 Mux", "IF4 DAC R", "IF4 DAC R" },
{ "DAC1 FS", NULL, "DAC1 MIXL" },
{ "DAC1 FS", NULL, "DAC1 MIXR" },
- { "DAC2 L Mux", "IF1 DAC 2", "IF1 DAC2" },
- { "DAC2 L Mux", "IF2 DAC 2", "IF2 DAC2" },
+ { "DAC2 L Mux", "IF1 DAC 2", "IF1 DAC2 Mux" },
+ { "DAC2 L Mux", "IF2 DAC 2", "IF2 DAC2 Mux" },
{ "DAC2 L Mux", "IF3 DAC L", "IF3 DAC L" },
{ "DAC2 L Mux", "IF4 DAC L", "IF4 DAC L" },
{ "DAC2 L Mux", "SLB DAC 2", "SLB DAC2" },
{ "DAC2 L Mux", "OB 2", "OutBound2" },
- { "DAC2 R Mux", "IF1 DAC 3", "IF1 DAC3" },
- { "DAC2 R Mux", "IF2 DAC 3", "IF2 DAC3" },
+ { "DAC2 R Mux", "IF1 DAC 3", "IF1 DAC3 Mux" },
+ { "DAC2 R Mux", "IF2 DAC 3", "IF2 DAC3 Mux" },
{ "DAC2 R Mux", "IF3 DAC R", "IF3 DAC R" },
{ "DAC2 R Mux", "IF4 DAC R", "IF4 DAC R" },
{ "DAC2 R Mux", "SLB DAC 3", "SLB DAC3" },
{ "DAC2 R Mux", "Haptic Generator", "Haptic Generator" },
{ "DAC2 R Mux", "VAD ADC", "VAD ADC Mux" },
- { "DAC3 L Mux", "IF1 DAC 4", "IF1 DAC4" },
- { "DAC3 L Mux", "IF2 DAC 4", "IF2 DAC4" },
+ { "DAC3 L Mux", "IF1 DAC 4", "IF1 DAC4 Mux" },
+ { "DAC3 L Mux", "IF2 DAC 4", "IF2 DAC4 Mux" },
{ "DAC3 L Mux", "IF3 DAC L", "IF3 DAC L" },
{ "DAC3 L Mux", "IF4 DAC L", "IF4 DAC L" },
{ "DAC3 L Mux", "SLB DAC 4", "SLB DAC4" },
{ "DAC3 L Mux", "OB 4", "OutBound4" },
- { "DAC3 R Mux", "IF1 DAC 5", "IF1 DAC4" },
- { "DAC3 R Mux", "IF2 DAC 5", "IF2 DAC4" },
+ { "DAC3 R Mux", "IF1 DAC 5", "IF1 DAC5 Mux" },
+ { "DAC3 R Mux", "IF2 DAC 5", "IF2 DAC5 Mux" },
{ "DAC3 R Mux", "IF3 DAC R", "IF3 DAC R" },
{ "DAC3 R Mux", "IF4 DAC R", "IF4 DAC R" },
{ "DAC3 R Mux", "SLB DAC 5", "SLB DAC5" },
{ "DAC3 R Mux", "OB 5", "OutBound5" },
- { "DAC4 L Mux", "IF1 DAC 6", "IF1 DAC6" },
- { "DAC4 L Mux", "IF2 DAC 6", "IF2 DAC6" },
+ { "DAC4 L Mux", "IF1 DAC 6", "IF1 DAC6 Mux" },
+ { "DAC4 L Mux", "IF2 DAC 6", "IF2 DAC6 Mux" },
{ "DAC4 L Mux", "IF3 DAC L", "IF3 DAC L" },
{ "DAC4 L Mux", "IF4 DAC L", "IF4 DAC L" },
{ "DAC4 L Mux", "SLB DAC 6", "SLB DAC6" },
{ "DAC4 L Mux", "OB 6", "OutBound6" },
- { "DAC4 R Mux", "IF1 DAC 7", "IF1 DAC7" },
- { "DAC4 R Mux", "IF2 DAC 7", "IF2 DAC7" },
+ { "DAC4 R Mux", "IF1 DAC 7", "IF1 DAC7 Mux" },
+ { "DAC4 R Mux", "IF2 DAC 7", "IF2 DAC7 Mux" },
{ "DAC4 R Mux", "IF3 DAC R", "IF3 DAC R" },
{ "DAC4 R Mux", "IF4 DAC R", "IF4 DAC R" },
{ "DAC4 R Mux", "SLB DAC 7", "SLB DAC7" },
if (!rt5677->dsp_vad_en) {
regcache_cache_only(rt5677->regmap, true);
regcache_mark_dirty(rt5677->regmap);
- }
- if (gpio_is_valid(rt5677->pow_ldo2))
- gpio_set_value_cansleep(rt5677->pow_ldo2, 0);
+ if (gpio_is_valid(rt5677->pow_ldo2))
+ gpio_set_value_cansleep(rt5677->pow_ldo2, 0);
+ }
return 0;
}
{
struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
- if (gpio_is_valid(rt5677->pow_ldo2)) {
- gpio_set_value_cansleep(rt5677->pow_ldo2, 1);
- msleep(10);
- }
-
if (!rt5677->dsp_vad_en) {
+ if (gpio_is_valid(rt5677->pow_ldo2)) {
+ gpio_set_value_cansleep(rt5677->pow_ldo2, 1);
+ msleep(10);
+ }
+
regcache_cache_only(rt5677->regmap, false);
regcache_sync(rt5677->regmap);
}
};
static const struct i2c_device_id rt5677_i2c_id[] = {
- { "rt5677", 0 },
+ { "rt5677", RT5677 },
+ { "rt5676", RT5676 },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id);
i2c_set_clientdata(i2c, rt5677);
+ rt5677->type = id->driver_data;
+
if (pdata)
rt5677->pdata = *pdata;
RT5677_IRQ_JD3,
};
+enum rt5677_type {
+ RT5677,
+ RT5676,
+};
+
struct rt5677_priv {
struct snd_soc_codec *codec;
struct rt5677_platform_data pdata;
int pll_in;
int pll_out;
int pow_ldo2; /* POW_LDO2 pin */
+ enum rt5677_type type;
#ifdef CONFIG_GPIOLIB
struct gpio_chip gpio_chip;
#endif
/* Enable VDDC charge pump */
ana_pwr |= SGTL5000_VDDC_CHRGPMP_POWERUP;
} else if (vddio >= 3100 && vdda >= 3100) {
- /*
- * if vddio and vddd > 3.1v,
- * charge pump should be clean before set ana_pwr
- */
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
- SGTL5000_VDDC_CHRGPMP_POWERUP, 0);
-
+ ana_pwr &= ~SGTL5000_VDDC_CHRGPMP_POWERUP;
/* VDDC use VDDIO rail */
lreg_ctrl |= SGTL5000_VDDC_ASSN_OVRD;
lreg_ctrl |= SGTL5000_VDDC_MAN_ASSN_VDDIO <<
/* speaker map */
{ "IHFOUTL", NULL, "Speaker Rail"},
{ "IHFOUTR", NULL, "Speaker Rail"},
- { "IHFOUTL", "NULL", "Speaker Left Playback"},
- { "IHFOUTR", "NULL", "Speaker Right Playback"},
+ { "IHFOUTL", NULL, "Speaker Left Playback"},
+ { "IHFOUTR", NULL, "Speaker Right Playback"},
{ "Speaker Left Playback", NULL, "Speaker Left Filter"},
{ "Speaker Right Playback", NULL, "Speaker Right Filter"},
{ "Speaker Left Filter", NULL, "IHFDAC Left"},
snd_soc_write(codec, SN95031_BTNCTRL2, 0x01);
}
-static int sn95031_get_headset_state(struct snd_soc_jack *mfld_jack)
+static int sn95031_get_headset_state(struct snd_soc_codec *codec,
+ struct snd_soc_jack *mfld_jack)
{
- int micbias = sn95031_get_mic_bias(mfld_jack->codec);
+ int micbias = sn95031_get_mic_bias(codec);
int jack_type = snd_soc_jack_get_type(mfld_jack, micbias);
pr_debug("jack type detected = %d\n", jack_type);
if (jack_type == SND_JACK_HEADSET)
- sn95031_enable_jack_btn(mfld_jack->codec);
+ sn95031_enable_jack_btn(codec);
return jack_type;
}
-void sn95031_jack_detection(struct mfld_jack_data *jack_data)
+void sn95031_jack_detection(struct snd_soc_codec *codec,
+ struct mfld_jack_data *jack_data)
{
unsigned int status;
unsigned int mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_HEADSET;
status = SND_JACK_HEADSET | SND_JACK_BTN_1;
} else if (jack_data->intr_id & 0x4) {
pr_debug("headset or headphones inserted\n");
- status = sn95031_get_headset_state(jack_data->mfld_jack);
+ status = sn95031_get_headset_state(codec, jack_data->mfld_jack);
} else if (jack_data->intr_id & 0x8) {
pr_debug("headset or headphones removed\n");
status = 0;
- sn95031_disable_jack_btn(jack_data->mfld_jack->codec);
+ sn95031_disable_jack_btn(codec);
} else {
pr_err("unidentified interrupt\n");
return;
struct snd_soc_jack *mfld_jack;
};
-extern void sn95031_jack_detection(struct mfld_jack_data *jack_data);
+extern void sn95031_jack_detection(struct snd_soc_codec *codec,
+ struct mfld_jack_data *jack_data);
#endif
};
static const struct regmap_range sta32x_write_regs_range[] = {
- regmap_reg_range(STA32X_CONFA, STA32X_AUTO2),
- regmap_reg_range(STA32X_C1CFG, STA32X_FDRC2),
+ regmap_reg_range(STA32X_CONFA, STA32X_FDRC2),
};
static const struct regmap_range sta32x_read_regs_range[] = {
- regmap_reg_range(STA32X_CONFA, STA32X_AUTO2),
- regmap_reg_range(STA32X_C1CFG, STA32X_FDRC2),
+ regmap_reg_range(STA32X_CONFA, STA32X_FDRC2),
};
static const struct regmap_range sta32x_volatile_regs_range[] = {
#endif
/* GPIOs */
- sta350->gpiod_nreset = devm_gpiod_get(dev, "reset");
- if (IS_ERR(sta350->gpiod_nreset)) {
- ret = PTR_ERR(sta350->gpiod_nreset);
- if (ret != -ENOENT && ret != -ENOSYS)
- return ret;
-
- sta350->gpiod_nreset = NULL;
- } else {
- gpiod_direction_output(sta350->gpiod_nreset, 0);
- }
-
- sta350->gpiod_power_down = devm_gpiod_get(dev, "power-down");
- if (IS_ERR(sta350->gpiod_power_down)) {
- ret = PTR_ERR(sta350->gpiod_power_down);
- if (ret != -ENOENT && ret != -ENOSYS)
- return ret;
-
- sta350->gpiod_power_down = NULL;
- } else {
- gpiod_direction_output(sta350->gpiod_power_down, 0);
- }
+ sta350->gpiod_nreset = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(sta350->gpiod_nreset))
+ return PTR_ERR(sta350->gpiod_nreset);
+
+ sta350->gpiod_power_down = devm_gpiod_get(dev, "power-down",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(sta350->gpiod_power_down))
+ return PTR_ERR(sta350->gpiod_power_down);
/* regulators */
for (i = 0; i < ARRAY_SIZE(sta350->supplies); i++)
if (data == NULL)
return -ENOMEM;
- data->enable_gpio = devm_gpiod_get(dev, "enable");
- if (IS_ERR(data->enable_gpio)) {
- ret = PTR_ERR(data->enable_gpio);
- if (ret != -ENOENT && ret != -ENOSYS)
- return ret;
-
- data->enable_gpio = NULL;
- } else {
- gpiod_direction_output(data->enable_gpio, 0);
- }
+ data->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(data->enable_gpio))
+ return PTR_ERR(data->enable_gpio);
data->tas2552_client = client;
data->regmap = devm_regmap_init_i2c(client, &tas2552_regmap_config);
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
- ucontrol->value.enumerated.item[0] = priv->deemph;
+ ucontrol->value.integer.value[0] = priv->deemph;
return 0;
}
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
- priv->deemph = ucontrol->value.enumerated.item[0];
+ priv->deemph = ucontrol->value.integer.value[0];
return tas5086_set_deemph(codec);
}
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
- ucontrol->value.enumerated.item[0] = wm2000->anc_active;
+ ucontrol->value.integer.value[0] = wm2000->anc_active;
return 0;
}
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
- int anc_active = ucontrol->value.enumerated.item[0];
+ int anc_active = ucontrol->value.integer.value[0];
int ret;
if (anc_active > 1)
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
- ucontrol->value.enumerated.item[0] = wm2000->spk_ena;
+ ucontrol->value.integer.value[0] = wm2000->spk_ena;
return 0;
}
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
- int val = ucontrol->value.enumerated.item[0];
+ int val = ucontrol->value.integer.value[0];
int ret;
if (val > 1)
struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
int fll_freq_out;
int fll_freq_in;
+ struct delayed_work pga_work;
};
/*
* Ramp OUT1 PGA volume to minimise pops at stream startup and shutdown.
*/
-static inline int wm8350_out1_ramp_step(struct snd_soc_codec *codec)
+static inline int wm8350_out1_ramp_step(struct wm8350_data *wm8350_data)
{
- struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec);
struct wm8350_output *out1 = &wm8350_data->out1;
struct wm8350 *wm8350 = wm8350_data->wm8350;
int left_complete = 0, right_complete = 0;
/*
* Ramp OUT2 PGA volume to minimise pops at stream startup and shutdown.
*/
-static inline int wm8350_out2_ramp_step(struct snd_soc_codec *codec)
+static inline int wm8350_out2_ramp_step(struct wm8350_data *wm8350_data)
{
- struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec);
struct wm8350_output *out2 = &wm8350_data->out2;
struct wm8350 *wm8350 = wm8350_data->wm8350;
int left_complete = 0, right_complete = 0;
*/
static void wm8350_pga_work(struct work_struct *work)
{
- struct snd_soc_dapm_context *dapm =
- container_of(work, struct snd_soc_dapm_context, delayed_work.work);
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
- struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec);
+ struct wm8350_data *wm8350_data =
+ container_of(work, struct wm8350_data, pga_work.work);
struct wm8350_output *out1 = &wm8350_data->out1,
*out2 = &wm8350_data->out2;
int i, out1_complete, out2_complete;
for (i = 0; i <= 63; i++) {
out1_complete = 1, out2_complete = 1;
if (out1->ramp != WM8350_RAMP_NONE)
- out1_complete = wm8350_out1_ramp_step(codec);
+ out1_complete = wm8350_out1_ramp_step(wm8350_data);
if (out2->ramp != WM8350_RAMP_NONE)
- out2_complete = wm8350_out2_ramp_step(codec);
+ out2_complete = wm8350_out2_ramp_step(wm8350_data);
/* ramp finished ? */
if (out1_complete && out2_complete)
out->ramp = WM8350_RAMP_UP;
out->active = 1;
- schedule_delayed_work(&codec->dapm.delayed_work,
+ schedule_delayed_work(&wm8350_data->pga_work,
msecs_to_jiffies(1));
break;
out->ramp = WM8350_RAMP_DOWN;
out->active = 0;
- schedule_delayed_work(&codec->dapm.delayed_work,
+ schedule_delayed_work(&wm8350_data->pga_work,
msecs_to_jiffies(1));
break;
}
/* Put the codec into reset if it wasn't already */
wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
- INIT_DELAYED_WORK(&codec->dapm.delayed_work, wm8350_pga_work);
+ INIT_DELAYED_WORK(&priv->pga_work, wm8350_pga_work);
INIT_DELAYED_WORK(&priv->hpl.work, wm8350_hpl_work);
INIT_DELAYED_WORK(&priv->hpr.work, wm8350_hpr_work);
/* if there was any work waiting then we run it now and
* wait for its completion */
- flush_delayed_work(&codec->dapm.delayed_work);
+ flush_delayed_work(&priv->pga_work);
wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
- ucontrol->value.enumerated.item[0] = wm8731->deemph;
+ ucontrol->value.integer.value[0] = wm8731->deemph;
return 0;
}
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
- int deemph = ucontrol->value.enumerated.item[0];
+ int deemph = ucontrol->value.integer.value[0];
int ret = 0;
if (deemph > 1)
};
static const unsigned int rates_11289[] = {
- 44100, 88235,
+ 44100, 88200,
};
static const struct snd_pcm_hw_constraint_list constraints_11289 = {
};
static const unsigned int rates_16934[] = {
- 44100, 88235,
+ 44100, 88200,
};
static const struct snd_pcm_hw_constraint_list constraints_16934 = {
};
static const unsigned int rates_22579[] = {
- 44100, 88235, 1764000
+ 44100, 88200, 176400
};
static const struct snd_pcm_hw_constraint_list constraints_22579 = {
};
static const unsigned int rates_36864[] = {
- 48000, 96000, 19200
+ 48000, 96000, 192000
};
static const struct snd_pcm_hw_constraint_list constraints_36864 = {
unsigned int hifi_fmt;
int dai_func;
+ struct delayed_work charge_work;
};
#define wm8753_reset(c) snd_soc_write(c, WM8753_RESET, 0)
return 0;
}
+static void wm8753_charge_work(struct work_struct *work)
+{
+ struct wm8753_priv *wm8753 =
+ container_of(work, struct wm8753_priv, charge_work.work);
+
+ /* Set to 500k */
+ regmap_update_bits(wm8753->regmap, WM8753_PWR1, 0x0180, 0x0100);
+}
+
static int wm8753_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
u16 pwr_reg = snd_soc_read(codec, WM8753_PWR1) & 0xfe3e;
switch (level) {
snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x00c0);
break;
case SND_SOC_BIAS_PREPARE:
- /* set vmid to 5k for quick power up */
- snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x01c1);
+ /* Wait until fully charged */
+ flush_delayed_work(&wm8753->charge_work);
break;
case SND_SOC_BIAS_STANDBY:
- /* mute dac and set vmid to 500k, enable VREF */
- snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x0141);
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ /* set vmid to 5k for quick power up */
+ snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x01c1);
+ schedule_delayed_work(&wm8753->charge_work,
+ msecs_to_jiffies(caps_charge));
+ } else {
+ /* mute dac and set vmid to 500k, enable VREF */
+ snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x0141);
+ }
break;
case SND_SOC_BIAS_OFF:
+ cancel_delayed_work_sync(&wm8753->charge_work);
snd_soc_write(codec, WM8753_PWR1, 0x0001);
break;
}
},
};
-static void wm8753_work(struct work_struct *work)
-{
- struct snd_soc_dapm_context *dapm =
- container_of(work, struct snd_soc_dapm_context,
- delayed_work.work);
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
- wm8753_set_bias_level(codec, dapm->bias_level);
-}
-
-static int wm8753_suspend(struct snd_soc_codec *codec)
-{
- wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF);
- return 0;
-}
-
static int wm8753_resume(struct snd_soc_codec *codec)
{
struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
regcache_sync(wm8753->regmap);
- wm8753_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
- /* charge wm8753 caps */
- if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) {
- wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
- codec->dapm.bias_level = SND_SOC_BIAS_ON;
- queue_delayed_work(system_power_efficient_wq,
- &codec->dapm.delayed_work,
- msecs_to_jiffies(caps_charge));
- }
-
return 0;
}
struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
int ret;
- INIT_DELAYED_WORK(&codec->dapm.delayed_work, wm8753_work);
+ INIT_DELAYED_WORK(&wm8753->charge_work, wm8753_charge_work);
ret = wm8753_reset(codec);
if (ret < 0) {
return ret;
}
- wm8753_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
wm8753->dai_func = 0;
- /* charge output caps */
- wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
- schedule_delayed_work(&codec->dapm.delayed_work,
- msecs_to_jiffies(caps_charge));
-
/* set the update bits */
snd_soc_update_bits(codec, WM8753_LDAC, 0x0100, 0x0100);
snd_soc_update_bits(codec, WM8753_RDAC, 0x0100, 0x0100);
return 0;
}
-/* power down chip */
-static int wm8753_remove(struct snd_soc_codec *codec)
-{
- flush_delayed_work(&codec->dapm.delayed_work);
- wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
- return 0;
-}
-
static struct snd_soc_codec_driver soc_codec_dev_wm8753 = {
.probe = wm8753_probe,
- .remove = wm8753_remove,
- .suspend = wm8753_suspend,
.resume = wm8753_resume,
.set_bias_level = wm8753_set_bias_level,
+ .suspend_bias_off = true,
.controls = wm8753_snd_controls,
.num_controls = ARRAY_SIZE(wm8753_snd_controls),
--- /dev/null
+/*
+ * wm8804-i2c.c -- WM8804 S/PDIF transceiver driver - I2C
+ *
+ * Copyright 2015 Cirrus Logic Inc
+ *
+ * Author: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+
+#include "wm8804.h"
+
+static int wm8804_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(i2c, &wm8804_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return wm8804_probe(&i2c->dev, regmap);
+}
+
+static int wm8804_i2c_remove(struct i2c_client *i2c)
+{
+ wm8804_remove(&i2c->dev);
+ return 0;
+}
+
+static const struct i2c_device_id wm8804_i2c_id[] = {
+ { "wm8804", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, wm8804_i2c_id);
+
+static const struct of_device_id wm8804_of_match[] = {
+ { .compatible = "wlf,wm8804", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8804_of_match);
+
+static struct i2c_driver wm8804_i2c_driver = {
+ .driver = {
+ .name = "wm8804",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8804_of_match,
+ },
+ .probe = wm8804_i2c_probe,
+ .remove = wm8804_i2c_remove,
+ .id_table = wm8804_i2c_id
+};
+
+module_i2c_driver(wm8804_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC WM8804 driver - I2C");
+MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * wm8804-spi.c -- WM8804 S/PDIF transceiver driver - SPI
+ *
+ * Copyright 2015 Cirrus Logic Inc
+ *
+ * Author: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include "wm8804.h"
+
+static int wm8804_spi_probe(struct spi_device *spi)
+{
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_spi(spi, &wm8804_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return wm8804_probe(&spi->dev, regmap);
+}
+
+static int wm8804_spi_remove(struct spi_device *spi)
+{
+ wm8804_remove(&spi->dev);
+ return 0;
+}
+
+static const struct of_device_id wm8804_of_match[] = {
+ { .compatible = "wlf,wm8804", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8804_of_match);
+
+static struct spi_driver wm8804_spi_driver = {
+ .driver = {
+ .name = "wm8804",
+ .owner = THIS_MODULE,
+ .of_match_table = wm8804_of_match,
+ },
+ .probe = wm8804_spi_probe,
+ .remove = wm8804_spi_remove
+};
+
+module_spi_driver(wm8804_spi_driver);
+
+MODULE_DESCRIPTION("ASoC WM8804 driver - SPI");
+MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
-#include <linux/i2c.h>
#include <linux/of_device.h>
-#include <linux/spi/spi.h>
-#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <sound/core.h>
}
}
-static int wm8804_reset(struct snd_soc_codec *codec)
+static int wm8804_reset(struct wm8804_priv *wm8804)
{
- return snd_soc_write(codec, WM8804_RST_DEVID1, 0x0);
+ return regmap_write(wm8804->regmap, WM8804_RST_DEVID1, 0x0);
}
static int wm8804_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}
-static int wm8804_remove(struct snd_soc_codec *codec)
-{
- struct wm8804_priv *wm8804;
- int i;
-
- wm8804 = snd_soc_codec_get_drvdata(codec);
-
- for (i = 0; i < ARRAY_SIZE(wm8804->supplies); ++i)
- regulator_unregister_notifier(wm8804->supplies[i].consumer,
- &wm8804->disable_nb[i]);
- return 0;
-}
-
-static int wm8804_probe(struct snd_soc_codec *codec)
-{
- struct wm8804_priv *wm8804;
- int i, id1, id2, ret;
-
- wm8804 = snd_soc_codec_get_drvdata(codec);
-
- for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++)
- wm8804->supplies[i].supply = wm8804_supply_names[i];
-
- ret = devm_regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8804->supplies),
- wm8804->supplies);
- if (ret) {
- dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
- return ret;
- }
-
- wm8804->disable_nb[0].notifier_call = wm8804_regulator_event_0;
- wm8804->disable_nb[1].notifier_call = wm8804_regulator_event_1;
-
- /* This should really be moved into the regulator core */
- for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) {
- ret = regulator_register_notifier(wm8804->supplies[i].consumer,
- &wm8804->disable_nb[i]);
- if (ret != 0) {
- dev_err(codec->dev,
- "Failed to register regulator notifier: %d\n",
- ret);
- }
- }
-
- ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies),
- wm8804->supplies);
- if (ret) {
- dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
- return ret;
- }
-
- id1 = snd_soc_read(codec, WM8804_RST_DEVID1);
- if (id1 < 0) {
- dev_err(codec->dev, "Failed to read device ID: %d\n", id1);
- ret = id1;
- goto err_reg_enable;
- }
-
- id2 = snd_soc_read(codec, WM8804_DEVID2);
- if (id2 < 0) {
- dev_err(codec->dev, "Failed to read device ID: %d\n", id2);
- ret = id2;
- goto err_reg_enable;
- }
-
- id2 = (id2 << 8) | id1;
-
- if (id2 != 0x8805) {
- dev_err(codec->dev, "Invalid device ID: %#x\n", id2);
- ret = -EINVAL;
- goto err_reg_enable;
- }
-
- ret = snd_soc_read(codec, WM8804_DEVREV);
- if (ret < 0) {
- dev_err(codec->dev, "Failed to read device revision: %d\n",
- ret);
- goto err_reg_enable;
- }
- dev_info(codec->dev, "revision %c\n", ret + 'A');
-
- ret = wm8804_reset(codec);
- if (ret < 0) {
- dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
- goto err_reg_enable;
- }
-
- return 0;
-
-err_reg_enable:
- regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), wm8804->supplies);
- return ret;
-}
-
static const struct snd_soc_dai_ops wm8804_dai_ops = {
.hw_params = wm8804_hw_params,
.set_fmt = wm8804_set_fmt,
};
static const struct snd_soc_codec_driver soc_codec_dev_wm8804 = {
- .probe = wm8804_probe,
- .remove = wm8804_remove,
.set_bias_level = wm8804_set_bias_level,
.idle_bias_off = true,
.num_controls = ARRAY_SIZE(wm8804_snd_controls),
};
-static const struct of_device_id wm8804_of_match[] = {
- { .compatible = "wlf,wm8804", },
- { }
-};
-MODULE_DEVICE_TABLE(of, wm8804_of_match);
-
-static const struct regmap_config wm8804_regmap_config = {
+const struct regmap_config wm8804_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.reg_defaults = wm8804_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8804_reg_defaults),
};
+EXPORT_SYMBOL_GPL(wm8804_regmap_config);
-#if defined(CONFIG_SPI_MASTER)
-static int wm8804_spi_probe(struct spi_device *spi)
+int wm8804_probe(struct device *dev, struct regmap *regmap)
{
struct wm8804_priv *wm8804;
- int ret;
+ unsigned int id1, id2;
+ int i, ret;
- wm8804 = devm_kzalloc(&spi->dev, sizeof *wm8804, GFP_KERNEL);
+ wm8804 = devm_kzalloc(dev, sizeof(*wm8804), GFP_KERNEL);
if (!wm8804)
return -ENOMEM;
- wm8804->regmap = devm_regmap_init_spi(spi, &wm8804_regmap_config);
- if (IS_ERR(wm8804->regmap)) {
- ret = PTR_ERR(wm8804->regmap);
+ dev_set_drvdata(dev, wm8804);
+
+ wm8804->regmap = regmap;
+
+ for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++)
+ wm8804->supplies[i].supply = wm8804_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(wm8804->supplies),
+ wm8804->supplies);
+ if (ret) {
+ dev_err(dev, "Failed to request supplies: %d\n", ret);
return ret;
}
- spi_set_drvdata(spi, wm8804);
+ wm8804->disable_nb[0].notifier_call = wm8804_regulator_event_0;
+ wm8804->disable_nb[1].notifier_call = wm8804_regulator_event_1;
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_wm8804, &wm8804_dai, 1);
+ /* This should really be moved into the regulator core */
+ for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) {
+ ret = regulator_register_notifier(wm8804->supplies[i].consumer,
+ &wm8804->disable_nb[i]);
+ if (ret != 0) {
+ dev_err(dev,
+ "Failed to register regulator notifier: %d\n",
+ ret);
+ }
+ }
- return ret;
-}
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies),
+ wm8804->supplies);
+ if (ret) {
+ dev_err(dev, "Failed to enable supplies: %d\n", ret);
+ goto err_reg_enable;
+ }
-static int wm8804_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
+ ret = regmap_read(regmap, WM8804_RST_DEVID1, &id1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read device ID: %d\n", ret);
+ goto err_reg_enable;
+ }
-static struct spi_driver wm8804_spi_driver = {
- .driver = {
- .name = "wm8804",
- .owner = THIS_MODULE,
- .of_match_table = wm8804_of_match,
- },
- .probe = wm8804_spi_probe,
- .remove = wm8804_spi_remove
-};
-#endif
+ ret = regmap_read(regmap, WM8804_DEVID2, &id2);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read device ID: %d\n", ret);
+ goto err_reg_enable;
+ }
-#if IS_ENABLED(CONFIG_I2C)
-static int wm8804_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
-{
- struct wm8804_priv *wm8804;
- int ret;
+ id2 = (id2 << 8) | id1;
- wm8804 = devm_kzalloc(&i2c->dev, sizeof *wm8804, GFP_KERNEL);
- if (!wm8804)
- return -ENOMEM;
+ if (id2 != 0x8805) {
+ dev_err(dev, "Invalid device ID: %#x\n", id2);
+ ret = -EINVAL;
+ goto err_reg_enable;
+ }
- wm8804->regmap = devm_regmap_init_i2c(i2c, &wm8804_regmap_config);
- if (IS_ERR(wm8804->regmap)) {
- ret = PTR_ERR(wm8804->regmap);
- return ret;
+ ret = regmap_read(regmap, WM8804_DEVREV, &id1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read device revision: %d\n",
+ ret);
+ goto err_reg_enable;
}
+ dev_info(dev, "revision %c\n", id1 + 'A');
- i2c_set_clientdata(i2c, wm8804);
+ ret = wm8804_reset(wm8804);
+ if (ret < 0) {
+ dev_err(dev, "Failed to issue reset: %d\n", ret);
+ goto err_reg_enable;
+ }
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_wm8804, &wm8804_dai, 1);
+ return snd_soc_register_codec(dev, &soc_codec_dev_wm8804,
+ &wm8804_dai, 1);
+
+err_reg_enable:
+ regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), wm8804->supplies);
return ret;
}
+EXPORT_SYMBOL_GPL(wm8804_probe);
-static int wm8804_i2c_remove(struct i2c_client *i2c)
+void wm8804_remove(struct device *dev)
{
- snd_soc_unregister_codec(&i2c->dev);
- return 0;
-}
-
-static const struct i2c_device_id wm8804_i2c_id[] = {
- { "wm8804", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, wm8804_i2c_id);
-
-static struct i2c_driver wm8804_i2c_driver = {
- .driver = {
- .name = "wm8804",
- .owner = THIS_MODULE,
- .of_match_table = wm8804_of_match,
- },
- .probe = wm8804_i2c_probe,
- .remove = wm8804_i2c_remove,
- .id_table = wm8804_i2c_id
-};
-#endif
+ struct wm8804_priv *wm8804;
+ int i;
-static int __init wm8804_modinit(void)
-{
- int ret = 0;
+ wm8804 = dev_get_drvdata(dev);
-#if IS_ENABLED(CONFIG_I2C)
- ret = i2c_add_driver(&wm8804_i2c_driver);
- if (ret) {
- printk(KERN_ERR "Failed to register wm8804 I2C driver: %d\n",
- ret);
- }
-#endif
-#if defined(CONFIG_SPI_MASTER)
- ret = spi_register_driver(&wm8804_spi_driver);
- if (ret != 0) {
- printk(KERN_ERR "Failed to register wm8804 SPI driver: %d\n",
- ret);
- }
-#endif
- return ret;
-}
-module_init(wm8804_modinit);
+ for (i = 0; i < ARRAY_SIZE(wm8804->supplies); ++i)
+ regulator_unregister_notifier(wm8804->supplies[i].consumer,
+ &wm8804->disable_nb[i]);
-static void __exit wm8804_exit(void)
-{
-#if IS_ENABLED(CONFIG_I2C)
- i2c_del_driver(&wm8804_i2c_driver);
-#endif
-#if defined(CONFIG_SPI_MASTER)
- spi_unregister_driver(&wm8804_spi_driver);
-#endif
+ snd_soc_unregister_codec(dev);
}
-module_exit(wm8804_exit);
+EXPORT_SYMBOL_GPL(wm8804_remove);
MODULE_DESCRIPTION("ASoC WM8804 driver");
MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>");
#ifndef _WM8804_H
#define _WM8804_H
+#include <linux/regmap.h>
+
/*
* Register values.
*/
#define WM8804_MCLKDIV_256FS 0
#define WM8804_MCLKDIV_128FS 1
+extern const struct regmap_config wm8804_regmap_config;
+
+int wm8804_probe(struct device *dev, struct regmap *regmap);
+void wm8804_remove(struct device *dev);
+
#endif /* _WM8804_H */
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
- ucontrol->value.enumerated.item[0] = wm8903->deemph;
+ ucontrol->value.integer.value[0] = wm8903->deemph;
return 0;
}
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
- int deemph = ucontrol->value.enumerated.item[0];
+ int deemph = ucontrol->value.integer.value[0];
int ret = 0;
if (deemph > 1)
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
- ucontrol->value.enumerated.item[0] = wm8904->deemph;
+ ucontrol->value.integer.value[0] = wm8904->deemph;
return 0;
}
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
- int deemph = ucontrol->value.enumerated.item[0];
+ int deemph = ucontrol->value.integer.value[0];
if (deemph > 1)
return -EINVAL;
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec);
- ucontrol->value.enumerated.item[0] = wm8955->deemph;
+ ucontrol->value.integer.value[0] = wm8955->deemph;
return 0;
}
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec);
- int deemph = ucontrol->value.enumerated.item[0];
+ int deemph = ucontrol->value.integer.value[0];
if (deemph > 1)
return -EINVAL;
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
- ucontrol->value.enumerated.item[0] = wm8960->deemph;
+ ucontrol->value.integer.value[0] = wm8960->deemph;
return 0;
}
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
- int deemph = ucontrol->value.enumerated.item[0];
+ int deemph = ucontrol->value.integer.value[0];
if (deemph > 1)
return -EINVAL;
#define WM8971_REG_COUNT 43
-static struct workqueue_struct *wm8971_workq = NULL;
-
/* codec private data */
struct wm8971_priv {
unsigned int sysclk;
+ struct delayed_work charge_work;
+ struct regmap *regmap;
};
/*
return 0;
}
+static void wm8971_charge_work(struct work_struct *work)
+{
+ struct wm8971_priv *wm8971 =
+ container_of(work, struct wm8971_priv, charge_work.work);
+
+ /* Set to 500k */
+ regmap_update_bits(wm8971->regmap, WM8971_PWR1, 0x0180, 0x0100);
+}
+
static int wm8971_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec);
u16 pwr_reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e;
switch (level) {
snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x00c1);
break;
case SND_SOC_BIAS_PREPARE:
+ /* Wait until fully charged */
+ flush_delayed_work(&wm8971->charge_work);
break;
case SND_SOC_BIAS_STANDBY:
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
snd_soc_cache_sync(codec);
+ /* charge output caps - set vmid to 5k for quick power up */
+ snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x01c0);
+ queue_delayed_work(system_power_efficient_wq,
+ &wm8971->charge_work, msecs_to_jiffies(1000));
+ } else {
+ /* mute dac and set vmid to 500k, enable VREF */
+ snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x0140);
+ }
- /* mute dac and set vmid to 500k, enable VREF */
- snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x0140);
break;
case SND_SOC_BIAS_OFF:
+ cancel_delayed_work_sync(&wm8971->charge_work);
snd_soc_write(codec, WM8971_PWR1, 0x0001);
break;
}
.ops = &wm8971_dai_ops,
};
-static void wm8971_work(struct work_struct *work)
-{
- struct snd_soc_dapm_context *dapm =
- container_of(work, struct snd_soc_dapm_context,
- delayed_work.work);
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
- wm8971_set_bias_level(codec, codec->dapm.bias_level);
-}
-
-static int wm8971_suspend(struct snd_soc_codec *codec)
-{
- wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF);
- return 0;
-}
-
-static int wm8971_resume(struct snd_soc_codec *codec)
-{
- u16 reg;
-
- wm8971_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
- /* charge wm8971 caps */
- if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) {
- reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e;
- snd_soc_write(codec, WM8971_PWR1, reg | 0x01c0);
- codec->dapm.bias_level = SND_SOC_BIAS_ON;
- queue_delayed_work(wm8971_workq, &codec->dapm.delayed_work,
- msecs_to_jiffies(1000));
- }
-
- return 0;
-}
-
static int wm8971_probe(struct snd_soc_codec *codec)
{
- int ret = 0;
- u16 reg;
+ struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec);
- INIT_DELAYED_WORK(&codec->dapm.delayed_work, wm8971_work);
- wm8971_workq = create_workqueue("wm8971");
- if (wm8971_workq == NULL)
- return -ENOMEM;
+ INIT_DELAYED_WORK(&wm8971->charge_work, wm8971_charge_work);
wm8971_reset(codec);
- /* charge output caps - set vmid to 5k for quick power up */
- reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e;
- snd_soc_write(codec, WM8971_PWR1, reg | 0x01c0);
- codec->dapm.bias_level = SND_SOC_BIAS_STANDBY;
- queue_delayed_work(wm8971_workq, &codec->dapm.delayed_work,
- msecs_to_jiffies(1000));
-
/* set the update bits */
snd_soc_update_bits(codec, WM8971_LDAC, 0x0100, 0x0100);
snd_soc_update_bits(codec, WM8971_RDAC, 0x0100, 0x0100);
snd_soc_update_bits(codec, WM8971_LINVOL, 0x0100, 0x0100);
snd_soc_update_bits(codec, WM8971_RINVOL, 0x0100, 0x0100);
- return ret;
-}
-
-
-/* power down chip */
-static int wm8971_remove(struct snd_soc_codec *codec)
-{
- wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
- if (wm8971_workq)
- destroy_workqueue(wm8971_workq);
return 0;
}
static struct snd_soc_codec_driver soc_codec_dev_wm8971 = {
.probe = wm8971_probe,
- .remove = wm8971_remove,
- .suspend = wm8971_suspend,
- .resume = wm8971_resume,
.set_bias_level = wm8971_set_bias_level,
+ .suspend_bias_off = true,
.controls = wm8971_snd_controls,
.num_controls = ARRAY_SIZE(wm8971_snd_controls),
const struct i2c_device_id *id)
{
struct wm8971_priv *wm8971;
- struct regmap *regmap;
int ret;
wm8971 = devm_kzalloc(&i2c->dev, sizeof(struct wm8971_priv),
if (wm8971 == NULL)
return -ENOMEM;
- regmap = devm_regmap_init_i2c(i2c, &wm8971_regmap);
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
+ wm8971->regmap = devm_regmap_init_i2c(i2c, &wm8971_regmap);
+ if (IS_ERR(wm8971->regmap))
+ return PTR_ERR(wm8971->regmap);
i2c_set_clientdata(i2c, wm8971);
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
- unsigned int val = ucontrol->value.enumerated.item[0];
+ unsigned int val = ucontrol->value.integer.value[0];
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int mixer, mask, shift, old;
mutex_lock(&wm9712->lock);
old = wm9712->hp_mixer[mixer];
- if (ucontrol->value.enumerated.item[0])
+ if (ucontrol->value.integer.value[0])
wm9712->hp_mixer[mixer] |= mask;
else
wm9712->hp_mixer[mixer] &= ~mask;
mixer = mc->shift >> 8;
shift = mc->shift & 0xff;
- ucontrol->value.enumerated.item[0] =
+ ucontrol->value.integer.value[0] =
(wm9712->hp_mixer[mixer] >> shift) & 1;
return 0;
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
- unsigned int val = ucontrol->value.enumerated.item[0];
+ unsigned int val = ucontrol->value.integer.value[0];
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int mixer, mask, shift, old;
mutex_lock(&wm9713->lock);
old = wm9713->hp_mixer[mixer];
- if (ucontrol->value.enumerated.item[0])
+ if (ucontrol->value.integer.value[0])
wm9713->hp_mixer[mixer] |= mask;
else
wm9713->hp_mixer[mixer] &= ~mask;
mixer = mc->shift >> 8;
shift = mc->shift & 0xff;
- ucontrol->value.enumerated.item[0] =
+ ucontrol->value.integer.value[0] =
(wm9713->hp_mixer[mixer] >> shift) & 1;
return 0;
memcpy(ctl->cache, p, ctl->len);
- if (!ctl->enabled) {
- ctl->set = 1;
+ ctl->set = 1;
+ if (!ctl->enabled)
return 0;
- }
return wm_coeff_write_control(kcontrol, p, ctl->len);
}
int ret, pos, blocks, type, offset, reg;
char *file;
struct wm_adsp_buf *buf;
- int tmp;
file = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (file == NULL)
}
}
- tmp = le32_to_cpu(blk->len) % 4;
- if (tmp)
- pos += le32_to_cpu(blk->len) + (4 - tmp) + sizeof(*blk);
- else
- pos += le32_to_cpu(blk->len) + sizeof(*blk);
-
+ pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03;
blocks++;
}
config SND_DAVINCI_SOC
- tristate "SoC Audio for TI DAVINCI"
+ tristate
depends on ARCH_DAVINCI
+ select SND_EDMA_SOC
config SND_EDMA_SOC
- tristate "SoC Audio for Texas Instruments chips using eDMA (AM33XX/43XX)"
- depends on SOC_AM33XX || SOC_AM43XX
+ tristate "SoC Audio for Texas Instruments chips using eDMA"
+ depends on SOC_AM33XX || SOC_AM43XX || ARCH_DAVINCI
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M here if you want audio support for TI SoC which uses eDMA.
The following line of SoCs are supported by this platform driver:
+ - daVinci devices
- AM335x
- AM437x/AM438x
config SND_DAVINCI_SOC_MCASP
tristate "Multichannel Audio Serial Port (McASP) support"
- depends on SND_DAVINCI_SOC || SND_OMAP_SOC || SND_EDMA_SOC
+ depends on SND_OMAP_SOC || SND_EDMA_SOC
help
Say Y or M here if you want to have support for McASP IP found in
various Texas Instruments SoCs like:
config SND_DAVINCI_SOC_EVM
tristate "SoC Audio support for DaVinci DM6446, DM355 or DM365 EVM"
- depends on SND_DAVINCI_SOC && I2C
+ depends on SND_EDMA_SOC && I2C
depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM || MACH_DAVINCI_DM365_EVM
select SND_DAVINCI_SOC_GENERIC_EVM
help
config SND_DM6467_SOC_EVM
tristate "SoC Audio support for DaVinci DM6467 EVM"
- depends on SND_DAVINCI_SOC && MACH_DAVINCI_DM6467_EVM && I2C
+ depends on SND_EDMA_SOC && MACH_DAVINCI_DM6467_EVM && I2C
select SND_DAVINCI_SOC_GENERIC_EVM
select SND_SOC_SPDIF
config SND_DA830_SOC_EVM
tristate "SoC Audio support for DA830/OMAP-L137 EVM"
- depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA830_EVM && I2C
+ depends on SND_EDMA_SOC && MACH_DAVINCI_DA830_EVM && I2C
select SND_DAVINCI_SOC_GENERIC_EVM
help
config SND_DA850_SOC_EVM
tristate "SoC Audio support for DA850/OMAP-L138 EVM"
- depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA850_EVM && I2C
+ depends on SND_EDMA_SOC && MACH_DAVINCI_DA850_EVM && I2C
select SND_DAVINCI_SOC_GENERIC_EVM
help
Say Y if you want to add support for SoC audio on TI
# DAVINCI Platform Support
-snd-soc-davinci-objs := davinci-pcm.o
snd-soc-edma-objs := edma-pcm.o
snd-soc-davinci-i2s-objs := davinci-i2s.o
snd-soc-davinci-mcasp-objs:= davinci-mcasp.o
snd-soc-davinci-vcif-objs:= davinci-vcif.o
-obj-$(CONFIG_SND_DAVINCI_SOC) += snd-soc-davinci.o
obj-$(CONFIG_SND_EDMA_SOC) += snd-soc-edma.o
obj-$(CONFIG_SND_DAVINCI_SOC_I2S) += snd-soc-davinci-i2s.o
obj-$(CONFIG_SND_DAVINCI_SOC_MCASP) += snd-soc-davinci-mcasp.o
return ret;
}
-static int davinci_evm_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(card);
-
- return 0;
-}
-
static struct platform_driver davinci_evm_driver = {
.probe = davinci_evm_probe,
- .remove = davinci_evm_remove,
.driver = {
.name = "davinci_evm",
.pm = &snd_soc_pm_ops,
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
-#include "davinci-pcm.h"
+#include "edma-pcm.h"
#include "davinci-i2s.h"
struct davinci_mcbsp_dev {
struct device *dev;
- struct davinci_pcm_dma_params dma_params[2];
+ struct snd_dmaengine_dai_dma_data dma_data[2];
+ int dma_request[2];
void __iomem *base;
#define MOD_DSP_A 0
#define MOD_DSP_B 1
struct snd_soc_dai *dai)
{
struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai);
- struct davinci_pcm_dma_params *dma_params =
- &dev->dma_params[substream->stream];
struct snd_interval *i = NULL;
int mcbsp_word_length, master;
unsigned int rcr, xcr, srgr, clk_div, freq, framesize;
return -EINVAL;
}
}
- dma_params->acnt = dma_params->data_type = data_type[fmt];
- dma_params->fifo_level = 0;
mcbsp_word_length = asp_word_length[fmt];
switch (master) {
return ret;
}
-static int davinci_i2s_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai);
-
- snd_soc_dai_set_dma_data(dai, substream, dev->dma_params);
- return 0;
-}
-
static void davinci_i2s_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
#define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000
static const struct snd_soc_dai_ops davinci_i2s_dai_ops = {
- .startup = davinci_i2s_startup,
.shutdown = davinci_i2s_shutdown,
.prepare = davinci_i2s_prepare,
.trigger = davinci_i2s_trigger,
};
+static int davinci_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+ struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
+ dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE];
+
+ return 0;
+}
+
static struct snd_soc_dai_driver davinci_i2s_dai = {
+ .probe = davinci_i2s_dai_probe,
.playback = {
.channels_min = 2,
.channels_max = 2,
static int davinci_i2s_probe(struct platform_device *pdev)
{
- struct snd_platform_data *pdata = pdev->dev.platform_data;
struct davinci_mcbsp_dev *dev;
struct resource *mem, *ioarea, *res;
- enum dma_event_q asp_chan_q = EVENTQ_0;
- enum dma_event_q ram_chan_q = EVENTQ_1;
+ int *dma;
int ret;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
GFP_KERNEL);
if (!dev)
return -ENOMEM;
- if (pdata) {
- dev->enable_channel_combine = pdata->enable_channel_combine;
- dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].sram_size =
- pdata->sram_size_playback;
- dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].sram_size =
- pdata->sram_size_capture;
- dev->clk_input_pin = pdata->clk_input_pin;
- dev->i2s_accurate_sck = pdata->i2s_accurate_sck;
- asp_chan_q = pdata->asp_chan_q;
- ram_chan_q = pdata->ram_chan_q;
- }
-
- dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].asp_chan_q = asp_chan_q;
- dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].ram_chan_q = ram_chan_q;
- dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].asp_chan_q = asp_chan_q;
- dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].ram_chan_q = ram_chan_q;
dev->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(dev->clk))
goto err_release_clk;
}
- dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].dma_addr =
+ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr =
(dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG);
- dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].dma_addr =
+ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr =
(dma_addr_t)(mem->start + DAVINCI_MCBSP_DRR_REG);
/* first TX, then RX */
ret = -ENXIO;
goto err_release_clk;
}
- dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].channel = res->start;
+ dma = &dev->dma_request[SNDRV_PCM_STREAM_PLAYBACK];
+ *dma = res->start;
+ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data = dma;
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (!res) {
ret = -ENXIO;
goto err_release_clk;
}
- dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start;
- dev->dev = &pdev->dev;
+ dma = &dev->dma_request[SNDRV_PCM_STREAM_CAPTURE];
+ *dma = res->start;
+ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].filter_data = dma;
+ dev->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, dev);
ret = snd_soc_register_component(&pdev->dev, &davinci_i2s_component,
if (ret != 0)
goto err_release_clk;
- ret = davinci_soc_platform_register(&pdev->dev);
+ ret = edma_pcm_platform_register(&pdev->dev);
if (ret) {
dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
goto err_unregister_component;
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_device.h>
+#include <linux/platform_data/davinci_asp.h>
#include <sound/asoundef.h>
#include <sound/core.h>
#include <sound/dmaengine_pcm.h>
#include <sound/omap-pcm.h>
-#include "davinci-pcm.h"
#include "edma-pcm.h"
#include "davinci-mcasp.h"
};
struct davinci_mcasp {
- struct davinci_pcm_dma_params dma_params[2];
struct snd_dmaengine_dai_dma_data dma_data[2];
void __iomem *base;
u32 fifo_base;
u16 bclk_lrclk_ratio;
int streams;
u32 irq_request[2];
+ int dma_request[2];
int sysclk_freq;
bool bclk_master;
mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AFSX | AFSR);
mcasp->bclk_master = 1;
break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ /* codec is clock slave and frame master */
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
+
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
+
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, ACLKX | ACLKR);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AFSX | AFSR);
+ mcasp->bclk_master = 1;
+ break;
case SND_SOC_DAIFMT_CBM_CFS:
/* codec is clock master and frame slave */
mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream,
int period_words, int channels)
{
- struct davinci_pcm_dma_params *dma_params = &mcasp->dma_params[stream];
struct snd_dmaengine_dai_dma_data *dma_data = &mcasp->dma_data[stream];
int i;
u8 tx_ser = 0;
* For example if three serializers are enabled the DMA
* need to transfer three words per DMA request.
*/
- dma_params->fifo_level = active_serializers;
dma_data->maxburst = active_serializers;
} else {
- dma_params->fifo_level = 0;
dma_data->maxburst = 0;
}
return 0;
/* Configure the burst size for platform drivers */
if (numevt == 1)
numevt = 0;
- dma_params->fifo_level = numevt;
dma_data->maxburst = numevt;
return 0;
struct snd_soc_dai *cpu_dai)
{
struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
- struct davinci_pcm_dma_params *dma_params =
- &mcasp->dma_params[substream->stream];
int word_length;
int channels = params_channels(params);
int period_size = params_period_size(params);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_U8:
case SNDRV_PCM_FORMAT_S8:
- dma_params->data_type = 1;
word_length = 8;
break;
case SNDRV_PCM_FORMAT_U16_LE:
case SNDRV_PCM_FORMAT_S16_LE:
- dma_params->data_type = 2;
word_length = 16;
break;
case SNDRV_PCM_FORMAT_U24_3LE:
case SNDRV_PCM_FORMAT_S24_3LE:
- dma_params->data_type = 3;
word_length = 24;
break;
case SNDRV_PCM_FORMAT_U24_LE:
case SNDRV_PCM_FORMAT_S24_LE:
- dma_params->data_type = 4;
word_length = 24;
break;
case SNDRV_PCM_FORMAT_U32_LE:
case SNDRV_PCM_FORMAT_S32_LE:
- dma_params->data_type = 4;
word_length = 32;
break;
return -EINVAL;
}
- if (mcasp->version == MCASP_VERSION_2 && !dma_params->fifo_level)
- dma_params->acnt = 4;
- else
- dma_params->acnt = dma_params->data_type;
-
davinci_config_channel_size(mcasp, word_length);
if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE)
{
struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
- if (mcasp->version >= MCASP_VERSION_3) {
- /* Using dmaengine PCM */
- dai->playback_dma_data =
- &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
- dai->capture_dma_data =
- &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE];
- } else {
- /* Using davinci-pcm */
- dai->playback_dma_data = mcasp->dma_params;
- dai->capture_dma_data = mcasp->dma_params;
- }
+ dai->playback_dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
+ dai->capture_dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE];
return 0;
}
static struct davinci_mcasp_pdata dm646x_mcasp_pdata = {
.tx_dma_offset = 0x400,
.rx_dma_offset = 0x400,
- .asp_chan_q = EVENTQ_0,
.version = MCASP_VERSION_1,
};
static struct davinci_mcasp_pdata da830_mcasp_pdata = {
.tx_dma_offset = 0x2000,
.rx_dma_offset = 0x2000,
- .asp_chan_q = EVENTQ_0,
.version = MCASP_VERSION_2,
};
static struct davinci_mcasp_pdata am33xx_mcasp_pdata = {
.tx_dma_offset = 0,
.rx_dma_offset = 0,
- .asp_chan_q = EVENTQ_0,
.version = MCASP_VERSION_3,
};
static struct davinci_mcasp_pdata dra7_mcasp_pdata = {
.tx_dma_offset = 0x200,
.rx_dma_offset = 0x284,
- .asp_chan_q = EVENTQ_0,
.version = MCASP_VERSION_4,
};
static int davinci_mcasp_probe(struct platform_device *pdev)
{
- struct davinci_pcm_dma_params *dma_params;
struct snd_dmaengine_dai_dma_data *dma_data;
struct resource *mem, *ioarea, *res, *dat;
struct davinci_mcasp_pdata *pdata;
struct davinci_mcasp *mcasp;
char *irq_name;
+ int *dma;
int irq;
int ret;
if (dat)
mcasp->dat_port = true;
- dma_params = &mcasp->dma_params[SNDRV_PCM_STREAM_PLAYBACK];
dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
- dma_params->asp_chan_q = pdata->asp_chan_q;
- dma_params->ram_chan_q = pdata->ram_chan_q;
- dma_params->sram_pool = pdata->sram_pool;
- dma_params->sram_size = pdata->sram_size_playback;
if (dat)
- dma_params->dma_addr = dat->start;
+ dma_data->addr = dat->start;
else
- dma_params->dma_addr = mem->start + pdata->tx_dma_offset;
-
- /* Unconditional dmaengine stuff */
- dma_data->addr = dma_params->dma_addr;
+ dma_data->addr = mem->start + pdata->tx_dma_offset;
+ dma = &mcasp->dma_request[SNDRV_PCM_STREAM_PLAYBACK];
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (res)
- dma_params->channel = res->start;
+ *dma = res->start;
else
- dma_params->channel = pdata->tx_dma_channel;
+ *dma = pdata->tx_dma_channel;
/* dmaengine filter data for DT and non-DT boot */
if (pdev->dev.of_node)
dma_data->filter_data = "tx";
else
- dma_data->filter_data = &dma_params->channel;
+ dma_data->filter_data = dma;
/* RX is not valid in DIT mode */
if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) {
- dma_params = &mcasp->dma_params[SNDRV_PCM_STREAM_CAPTURE];
dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE];
- dma_params->asp_chan_q = pdata->asp_chan_q;
- dma_params->ram_chan_q = pdata->ram_chan_q;
- dma_params->sram_pool = pdata->sram_pool;
- dma_params->sram_size = pdata->sram_size_capture;
if (dat)
- dma_params->dma_addr = dat->start;
+ dma_data->addr = dat->start;
else
- dma_params->dma_addr = mem->start + pdata->rx_dma_offset;
-
- /* Unconditional dmaengine stuff */
- dma_data->addr = dma_params->dma_addr;
+ dma_data->addr = mem->start + pdata->rx_dma_offset;
+ dma = &mcasp->dma_request[SNDRV_PCM_STREAM_CAPTURE];
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (res)
- dma_params->channel = res->start;
+ *dma = res->start;
else
- dma_params->channel = pdata->rx_dma_channel;
+ *dma = pdata->rx_dma_channel;
/* dmaengine filter data for DT and non-DT boot */
if (pdev->dev.of_node)
dma_data->filter_data = "rx";
else
- dma_data->filter_data = &dma_params->channel;
+ dma_data->filter_data = dma;
}
if (mcasp->version < MCASP_VERSION_3) {
goto err;
switch (mcasp->version) {
-#if IS_BUILTIN(CONFIG_SND_DAVINCI_SOC) || \
- (IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \
- IS_MODULE(CONFIG_SND_DAVINCI_SOC))
- case MCASP_VERSION_1:
- case MCASP_VERSION_2:
- ret = davinci_soc_platform_register(&pdev->dev);
- break;
-#endif
#if IS_BUILTIN(CONFIG_SND_EDMA_SOC) || \
(IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \
IS_MODULE(CONFIG_SND_EDMA_SOC))
+ case MCASP_VERSION_1:
+ case MCASP_VERSION_2:
case MCASP_VERSION_3:
ret = edma_pcm_platform_register(&pdev->dev);
break;
+++ /dev/null
-/*
- * ALSA PCM interface for the TI DAVINCI processor
- *
- * Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
- * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
- * added SRAM ping/pong (C) 2008 Troy Kisky <troy.kisky@boundarydevices.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/dma-mapping.h>
-#include <linux/kernel.h>
-#include <linux/genalloc.h>
-#include <linux/platform_data/edma.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include <asm/dma.h>
-
-#include "davinci-pcm.h"
-
-#ifdef DEBUG
-static void print_buf_info(int slot, char *name)
-{
- struct edmacc_param p;
- if (slot < 0)
- return;
- edma_read_slot(slot, &p);
- printk(KERN_DEBUG "%s: 0x%x, opt=%x, src=%x, a_b_cnt=%x dst=%x\n",
- name, slot, p.opt, p.src, p.a_b_cnt, p.dst);
- printk(KERN_DEBUG " src_dst_bidx=%x link_bcntrld=%x src_dst_cidx=%x ccnt=%x\n",
- p.src_dst_bidx, p.link_bcntrld, p.src_dst_cidx, p.ccnt);
-}
-#else
-static void print_buf_info(int slot, char *name)
-{
-}
-#endif
-
-static struct snd_pcm_hardware pcm_hardware_playback = {
- .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME|
- SNDRV_PCM_INFO_BATCH),
- .buffer_bytes_max = 128 * 1024,
- .period_bytes_min = 32,
- .period_bytes_max = 8 * 1024,
- .periods_min = 16,
- .periods_max = 255,
- .fifo_size = 0,
-};
-
-static struct snd_pcm_hardware pcm_hardware_capture = {
- .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_BATCH),
- .buffer_bytes_max = 128 * 1024,
- .period_bytes_min = 32,
- .period_bytes_max = 8 * 1024,
- .periods_min = 16,
- .periods_max = 255,
- .fifo_size = 0,
-};
-
-/*
- * How ping/pong works....
- *
- * Playback:
- * ram_params - copys 2*ping_size from start of SDRAM to iram,
- * links to ram_link2
- * ram_link2 - copys rest of SDRAM to iram in ping_size units,
- * links to ram_link
- * ram_link - copys entire SDRAM to iram in ping_size uints,
- * links to self
- *
- * asp_params - same as asp_link[0]
- * asp_link[0] - copys from lower half of iram to asp port
- * links to asp_link[1], triggers iram copy event on completion
- * asp_link[1] - copys from upper half of iram to asp port
- * links to asp_link[0], triggers iram copy event on completion
- * triggers interrupt only needed to let upper SOC levels update position
- * in stream on completion
- *
- * When playback is started:
- * ram_params started
- * asp_params started
- *
- * Capture:
- * ram_params - same as ram_link,
- * links to ram_link
- * ram_link - same as playback
- * links to self
- *
- * asp_params - same as playback
- * asp_link[0] - same as playback
- * asp_link[1] - same as playback
- *
- * When capture is started:
- * asp_params started
- */
-struct davinci_runtime_data {
- spinlock_t lock;
- int period; /* current DMA period */
- int asp_channel; /* Master DMA channel */
- int asp_link[2]; /* asp parameter link channel, ping/pong */
- struct davinci_pcm_dma_params *params; /* DMA params */
- int ram_channel;
- int ram_link;
- int ram_link2;
- struct edmacc_param asp_params;
- struct edmacc_param ram_params;
-};
-
-static void davinci_pcm_period_elapsed(struct snd_pcm_substream *substream)
-{
- struct davinci_runtime_data *prtd = substream->runtime->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- prtd->period++;
- if (unlikely(prtd->period >= runtime->periods))
- prtd->period = 0;
-}
-
-static void davinci_pcm_period_reset(struct snd_pcm_substream *substream)
-{
- struct davinci_runtime_data *prtd = substream->runtime->private_data;
-
- prtd->period = 0;
-}
-/*
- * Not used with ping/pong
- */
-static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream)
-{
- struct davinci_runtime_data *prtd = substream->runtime->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned int period_size;
- unsigned int dma_offset;
- dma_addr_t dma_pos;
- dma_addr_t src, dst;
- unsigned short src_bidx, dst_bidx;
- unsigned short src_cidx, dst_cidx;
- unsigned int data_type;
- unsigned short acnt;
- unsigned int count;
- unsigned int fifo_level;
-
- period_size = snd_pcm_lib_period_bytes(substream);
- dma_offset = prtd->period * period_size;
- dma_pos = runtime->dma_addr + dma_offset;
- fifo_level = prtd->params->fifo_level;
-
- pr_debug("davinci_pcm: audio_set_dma_params_play channel = %d "
- "dma_ptr = %x period_size=%x\n", prtd->asp_link[0], dma_pos,
- period_size);
-
- data_type = prtd->params->data_type;
- count = period_size / data_type;
- if (fifo_level)
- count /= fifo_level;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- src = dma_pos;
- dst = prtd->params->dma_addr;
- src_bidx = data_type;
- dst_bidx = 4;
- src_cidx = data_type * fifo_level;
- dst_cidx = 0;
- } else {
- src = prtd->params->dma_addr;
- dst = dma_pos;
- src_bidx = 0;
- dst_bidx = data_type;
- src_cidx = 0;
- dst_cidx = data_type * fifo_level;
- }
-
- acnt = prtd->params->acnt;
- edma_set_src(prtd->asp_link[0], src, INCR, W8BIT);
- edma_set_dest(prtd->asp_link[0], dst, INCR, W8BIT);
-
- edma_set_src_index(prtd->asp_link[0], src_bidx, src_cidx);
- edma_set_dest_index(prtd->asp_link[0], dst_bidx, dst_cidx);
-
- if (!fifo_level)
- edma_set_transfer_params(prtd->asp_link[0], acnt, count, 1, 0,
- ASYNC);
- else
- edma_set_transfer_params(prtd->asp_link[0], acnt,
- fifo_level,
- count, fifo_level,
- ABSYNC);
-}
-
-static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data)
-{
- struct snd_pcm_substream *substream = data;
- struct davinci_runtime_data *prtd = substream->runtime->private_data;
-
- print_buf_info(prtd->ram_channel, "i ram_channel");
- pr_debug("davinci_pcm: link=%d, status=0x%x\n", link, ch_status);
-
- if (unlikely(ch_status != EDMA_DMA_COMPLETE))
- return;
-
- if (snd_pcm_running(substream)) {
- spin_lock(&prtd->lock);
- if (prtd->ram_channel < 0) {
- /* No ping/pong must fix up link dma data*/
- davinci_pcm_enqueue_dma(substream);
- }
- davinci_pcm_period_elapsed(substream);
- spin_unlock(&prtd->lock);
- snd_pcm_period_elapsed(substream);
- }
-}
-
-#ifdef CONFIG_GENERIC_ALLOCATOR
-static int allocate_sram(struct snd_pcm_substream *substream,
- struct gen_pool *sram_pool, unsigned size,
- struct snd_pcm_hardware *ppcm)
-{
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- struct snd_dma_buffer *iram_dma = NULL;
- dma_addr_t iram_phys = 0;
- void *iram_virt = NULL;
-
- if (buf->private_data || !size)
- return 0;
-
- ppcm->period_bytes_max = size;
- iram_virt = gen_pool_dma_alloc(sram_pool, size, &iram_phys);
- if (!iram_virt)
- goto exit1;
- iram_dma = kzalloc(sizeof(*iram_dma), GFP_KERNEL);
- if (!iram_dma)
- goto exit2;
- iram_dma->area = iram_virt;
- iram_dma->addr = iram_phys;
- memset(iram_dma->area, 0, size);
- iram_dma->bytes = size;
- buf->private_data = iram_dma;
- return 0;
-exit2:
- if (iram_virt)
- gen_pool_free(sram_pool, (unsigned)iram_virt, size);
-exit1:
- return -ENOMEM;
-}
-
-static void davinci_free_sram(struct snd_pcm_substream *substream,
- struct snd_dma_buffer *iram_dma)
-{
- struct davinci_runtime_data *prtd = substream->runtime->private_data;
- struct gen_pool *sram_pool = prtd->params->sram_pool;
-
- gen_pool_free(sram_pool, (unsigned) iram_dma->area, iram_dma->bytes);
-}
-#else
-static int allocate_sram(struct snd_pcm_substream *substream,
- struct gen_pool *sram_pool, unsigned size,
- struct snd_pcm_hardware *ppcm)
-{
- return 0;
-}
-
-static void davinci_free_sram(struct snd_pcm_substream *substream,
- struct snd_dma_buffer *iram_dma)
-{
-}
-#endif
-
-/*
- * Only used with ping/pong.
- * This is called after runtime->dma_addr, period_bytes and data_type are valid
- */
-static int ping_pong_dma_setup(struct snd_pcm_substream *substream)
-{
- unsigned short ram_src_cidx, ram_dst_cidx;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct davinci_runtime_data *prtd = runtime->private_data;
- struct snd_dma_buffer *iram_dma =
- (struct snd_dma_buffer *)substream->dma_buffer.private_data;
- struct davinci_pcm_dma_params *params = prtd->params;
- unsigned int data_type = params->data_type;
- unsigned int acnt = params->acnt;
- /* divide by 2 for ping/pong */
- unsigned int ping_size = snd_pcm_lib_period_bytes(substream) >> 1;
- unsigned int fifo_level = prtd->params->fifo_level;
- unsigned int count;
- if ((data_type == 0) || (data_type > 4)) {
- printk(KERN_ERR "%s: data_type=%i\n", __func__, data_type);
- return -EINVAL;
- }
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- dma_addr_t asp_src_pong = iram_dma->addr + ping_size;
- ram_src_cidx = ping_size;
- ram_dst_cidx = -ping_size;
- edma_set_src(prtd->asp_link[1], asp_src_pong, INCR, W8BIT);
-
- edma_set_src_index(prtd->asp_link[0], data_type,
- data_type * fifo_level);
- edma_set_src_index(prtd->asp_link[1], data_type,
- data_type * fifo_level);
-
- edma_set_src(prtd->ram_link, runtime->dma_addr, INCR, W32BIT);
- } else {
- dma_addr_t asp_dst_pong = iram_dma->addr + ping_size;
- ram_src_cidx = -ping_size;
- ram_dst_cidx = ping_size;
- edma_set_dest(prtd->asp_link[1], asp_dst_pong, INCR, W8BIT);
-
- edma_set_dest_index(prtd->asp_link[0], data_type,
- data_type * fifo_level);
- edma_set_dest_index(prtd->asp_link[1], data_type,
- data_type * fifo_level);
-
- edma_set_dest(prtd->ram_link, runtime->dma_addr, INCR, W32BIT);
- }
-
- if (!fifo_level) {
- count = ping_size / data_type;
- edma_set_transfer_params(prtd->asp_link[0], acnt, count,
- 1, 0, ASYNC);
- edma_set_transfer_params(prtd->asp_link[1], acnt, count,
- 1, 0, ASYNC);
- } else {
- count = ping_size / (data_type * fifo_level);
- edma_set_transfer_params(prtd->asp_link[0], acnt, fifo_level,
- count, fifo_level, ABSYNC);
- edma_set_transfer_params(prtd->asp_link[1], acnt, fifo_level,
- count, fifo_level, ABSYNC);
- }
-
- edma_set_src_index(prtd->ram_link, ping_size, ram_src_cidx);
- edma_set_dest_index(prtd->ram_link, ping_size, ram_dst_cidx);
- edma_set_transfer_params(prtd->ram_link, ping_size, 2,
- runtime->periods, 2, ASYNC);
-
- /* init master params */
- edma_read_slot(prtd->asp_link[0], &prtd->asp_params);
- edma_read_slot(prtd->ram_link, &prtd->ram_params);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- struct edmacc_param p_ram;
- /* Copy entire iram buffer before playback started */
- prtd->ram_params.a_b_cnt = (1 << 16) | (ping_size << 1);
- /* 0 dst_bidx */
- prtd->ram_params.src_dst_bidx = (ping_size << 1);
- /* 0 dst_cidx */
- prtd->ram_params.src_dst_cidx = (ping_size << 1);
- prtd->ram_params.ccnt = 1;
-
- /* Skip 1st period */
- edma_read_slot(prtd->ram_link, &p_ram);
- p_ram.src += (ping_size << 1);
- p_ram.ccnt -= 1;
- edma_write_slot(prtd->ram_link2, &p_ram);
- /*
- * When 1st started, ram -> iram dma channel will fill the
- * entire iram. Then, whenever a ping/pong asp buffer finishes,
- * 1/2 iram will be filled.
- */
- prtd->ram_params.link_bcntrld =
- EDMA_CHAN_SLOT(prtd->ram_link2) << 5;
- }
- return 0;
-}
-
-/* 1 asp tx or rx channel using 2 parameter channels
- * 1 ram to/from iram channel using 1 parameter channel
- *
- * Playback
- * ram copy channel kicks off first,
- * 1st ram copy of entire iram buffer completion kicks off asp channel
- * asp tcc always kicks off ram copy of 1/2 iram buffer
- *
- * Record
- * asp channel starts, tcc kicks off ram copy
- */
-static int request_ping_pong(struct snd_pcm_substream *substream,
- struct davinci_runtime_data *prtd,
- struct snd_dma_buffer *iram_dma)
-{
- dma_addr_t asp_src_ping;
- dma_addr_t asp_dst_ping;
- int ret;
- struct davinci_pcm_dma_params *params = prtd->params;
-
- /* Request ram master channel */
- ret = prtd->ram_channel = edma_alloc_channel(EDMA_CHANNEL_ANY,
- davinci_pcm_dma_irq, substream,
- prtd->params->ram_chan_q);
- if (ret < 0)
- goto exit1;
-
- /* Request ram link channel */
- ret = prtd->ram_link = edma_alloc_slot(
- EDMA_CTLR(prtd->ram_channel), EDMA_SLOT_ANY);
- if (ret < 0)
- goto exit2;
-
- ret = prtd->asp_link[1] = edma_alloc_slot(
- EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY);
- if (ret < 0)
- goto exit3;
-
- prtd->ram_link2 = -1;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- ret = prtd->ram_link2 = edma_alloc_slot(
- EDMA_CTLR(prtd->ram_channel), EDMA_SLOT_ANY);
- if (ret < 0)
- goto exit4;
- }
- /* circle ping-pong buffers */
- edma_link(prtd->asp_link[0], prtd->asp_link[1]);
- edma_link(prtd->asp_link[1], prtd->asp_link[0]);
- /* circle ram buffers */
- edma_link(prtd->ram_link, prtd->ram_link);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- asp_src_ping = iram_dma->addr;
- asp_dst_ping = params->dma_addr; /* fifo */
- } else {
- asp_src_ping = params->dma_addr; /* fifo */
- asp_dst_ping = iram_dma->addr;
- }
- /* ping */
- edma_set_src(prtd->asp_link[0], asp_src_ping, INCR, W16BIT);
- edma_set_dest(prtd->asp_link[0], asp_dst_ping, INCR, W16BIT);
- edma_set_src_index(prtd->asp_link[0], 0, 0);
- edma_set_dest_index(prtd->asp_link[0], 0, 0);
-
- edma_read_slot(prtd->asp_link[0], &prtd->asp_params);
- prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f) | TCINTEN);
- prtd->asp_params.opt |= TCCHEN |
- EDMA_TCC(prtd->ram_channel & 0x3f);
- edma_write_slot(prtd->asp_link[0], &prtd->asp_params);
-
- /* pong */
- edma_set_src(prtd->asp_link[1], asp_src_ping, INCR, W16BIT);
- edma_set_dest(prtd->asp_link[1], asp_dst_ping, INCR, W16BIT);
- edma_set_src_index(prtd->asp_link[1], 0, 0);
- edma_set_dest_index(prtd->asp_link[1], 0, 0);
-
- edma_read_slot(prtd->asp_link[1], &prtd->asp_params);
- prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f));
- /* interrupt after every pong completion */
- prtd->asp_params.opt |= TCINTEN | TCCHEN |
- EDMA_TCC(prtd->ram_channel & 0x3f);
- edma_write_slot(prtd->asp_link[1], &prtd->asp_params);
-
- /* ram */
- edma_set_src(prtd->ram_link, iram_dma->addr, INCR, W32BIT);
- edma_set_dest(prtd->ram_link, iram_dma->addr, INCR, W32BIT);
- pr_debug("%s: audio dma channels/slots in use for ram:%u %u %u,"
- "for asp:%u %u %u\n", __func__,
- prtd->ram_channel, prtd->ram_link, prtd->ram_link2,
- prtd->asp_channel, prtd->asp_link[0],
- prtd->asp_link[1]);
- return 0;
-exit4:
- edma_free_channel(prtd->asp_link[1]);
- prtd->asp_link[1] = -1;
-exit3:
- edma_free_channel(prtd->ram_link);
- prtd->ram_link = -1;
-exit2:
- edma_free_channel(prtd->ram_channel);
- prtd->ram_channel = -1;
-exit1:
- return ret;
-}
-
-static int davinci_pcm_dma_request(struct snd_pcm_substream *substream)
-{
- struct snd_dma_buffer *iram_dma;
- struct davinci_runtime_data *prtd = substream->runtime->private_data;
- struct davinci_pcm_dma_params *params = prtd->params;
- int ret;
-
- if (!params)
- return -ENODEV;
-
- /* Request asp master DMA channel */
- ret = prtd->asp_channel = edma_alloc_channel(params->channel,
- davinci_pcm_dma_irq, substream,
- prtd->params->asp_chan_q);
- if (ret < 0)
- goto exit1;
-
- /* Request asp link channels */
- ret = prtd->asp_link[0] = edma_alloc_slot(
- EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY);
- if (ret < 0)
- goto exit2;
-
- iram_dma = (struct snd_dma_buffer *)substream->dma_buffer.private_data;
- if (iram_dma) {
- if (request_ping_pong(substream, prtd, iram_dma) == 0)
- return 0;
- printk(KERN_WARNING "%s: dma channel allocation failed,"
- "not using sram\n", __func__);
- }
-
- /* Issue transfer completion IRQ when the channel completes a
- * transfer, then always reload from the same slot (by a kind
- * of loopback link). The completion IRQ handler will update
- * the reload slot with a new buffer.
- *
- * REVISIT save p_ram here after setting up everything except
- * the buffer and its length (ccnt) ... use it as a template
- * so davinci_pcm_enqueue_dma() takes less time in IRQ.
- */
- edma_read_slot(prtd->asp_link[0], &prtd->asp_params);
- prtd->asp_params.opt |= TCINTEN |
- EDMA_TCC(EDMA_CHAN_SLOT(prtd->asp_channel));
- prtd->asp_params.link_bcntrld = EDMA_CHAN_SLOT(prtd->asp_link[0]) << 5;
- edma_write_slot(prtd->asp_link[0], &prtd->asp_params);
- return 0;
-exit2:
- edma_free_channel(prtd->asp_channel);
- prtd->asp_channel = -1;
-exit1:
- return ret;
-}
-
-static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct davinci_runtime_data *prtd = substream->runtime->private_data;
- int ret = 0;
-
- spin_lock(&prtd->lock);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- edma_start(prtd->asp_channel);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
- prtd->ram_channel >= 0) {
- /* copy 1st iram buffer */
- edma_start(prtd->ram_channel);
- }
- break;
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- edma_resume(prtd->asp_channel);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- edma_pause(prtd->asp_channel);
- break;
- default:
- ret = -EINVAL;
- break;
- }
-
- spin_unlock(&prtd->lock);
-
- return ret;
-}
-
-static int davinci_pcm_prepare(struct snd_pcm_substream *substream)
-{
- struct davinci_runtime_data *prtd = substream->runtime->private_data;
-
- davinci_pcm_period_reset(substream);
- if (prtd->ram_channel >= 0) {
- int ret = ping_pong_dma_setup(substream);
- if (ret < 0)
- return ret;
-
- edma_write_slot(prtd->ram_channel, &prtd->ram_params);
- edma_write_slot(prtd->asp_channel, &prtd->asp_params);
-
- print_buf_info(prtd->ram_channel, "ram_channel");
- print_buf_info(prtd->ram_link, "ram_link");
- print_buf_info(prtd->ram_link2, "ram_link2");
- print_buf_info(prtd->asp_channel, "asp_channel");
- print_buf_info(prtd->asp_link[0], "asp_link[0]");
- print_buf_info(prtd->asp_link[1], "asp_link[1]");
-
- /*
- * There is a phase offset of 2 periods between the position
- * used by dma setup and the position reported in the pointer
- * function.
- *
- * The phase offset, when not using ping-pong buffers, is due to
- * the two consecutive calls to davinci_pcm_enqueue_dma() below.
- *
- * Whereas here, with ping-pong buffers, the phase is due to
- * there being an entire buffer transfer complete before the
- * first dma completion event triggers davinci_pcm_dma_irq().
- */
- davinci_pcm_period_elapsed(substream);
- davinci_pcm_period_elapsed(substream);
-
- return 0;
- }
- davinci_pcm_enqueue_dma(substream);
- davinci_pcm_period_elapsed(substream);
-
- /* Copy self-linked parameter RAM entry into master channel */
- edma_read_slot(prtd->asp_link[0], &prtd->asp_params);
- edma_write_slot(prtd->asp_channel, &prtd->asp_params);
- davinci_pcm_enqueue_dma(substream);
- davinci_pcm_period_elapsed(substream);
-
- return 0;
-}
-
-static snd_pcm_uframes_t
-davinci_pcm_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct davinci_runtime_data *prtd = runtime->private_data;
- unsigned int offset;
- int asp_count;
- unsigned int period_size = snd_pcm_lib_period_bytes(substream);
-
- /*
- * There is a phase offset of 2 periods between the position used by dma
- * setup and the position reported in the pointer function. Either +2 in
- * the dma setup or -2 here in the pointer function (with wrapping,
- * both) accounts for this offset -- choose the latter since it makes
- * the first-time setup clearer.
- */
- spin_lock(&prtd->lock);
- asp_count = prtd->period - 2;
- spin_unlock(&prtd->lock);
-
- if (asp_count < 0)
- asp_count += runtime->periods;
- asp_count *= period_size;
-
- offset = bytes_to_frames(runtime, asp_count);
- if (offset >= runtime->buffer_size)
- offset = 0;
-
- return offset;
-}
-
-static int davinci_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct davinci_runtime_data *prtd;
- struct snd_pcm_hardware *ppcm;
- int ret = 0;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct davinci_pcm_dma_params *pa;
- struct davinci_pcm_dma_params *params;
-
- pa = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
- if (!pa)
- return -ENODEV;
- params = &pa[substream->stream];
-
- ppcm = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- &pcm_hardware_playback : &pcm_hardware_capture;
- allocate_sram(substream, params->sram_pool, params->sram_size, ppcm);
- snd_soc_set_runtime_hwparams(substream, ppcm);
- /* ensure that buffer size is a multiple of period size */
- ret = snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS);
- if (ret < 0)
- return ret;
-
- prtd = kzalloc(sizeof(struct davinci_runtime_data), GFP_KERNEL);
- if (prtd == NULL)
- return -ENOMEM;
-
- spin_lock_init(&prtd->lock);
- prtd->params = params;
- prtd->asp_channel = -1;
- prtd->asp_link[0] = prtd->asp_link[1] = -1;
- prtd->ram_channel = -1;
- prtd->ram_link = -1;
- prtd->ram_link2 = -1;
-
- runtime->private_data = prtd;
-
- ret = davinci_pcm_dma_request(substream);
- if (ret) {
- printk(KERN_ERR "davinci_pcm: Failed to get dma channels\n");
- kfree(prtd);
- }
-
- return ret;
-}
-
-static int davinci_pcm_close(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct davinci_runtime_data *prtd = runtime->private_data;
-
- if (prtd->ram_channel >= 0)
- edma_stop(prtd->ram_channel);
- if (prtd->asp_channel >= 0)
- edma_stop(prtd->asp_channel);
- if (prtd->asp_link[0] >= 0)
- edma_unlink(prtd->asp_link[0]);
- if (prtd->asp_link[1] >= 0)
- edma_unlink(prtd->asp_link[1]);
- if (prtd->ram_link >= 0)
- edma_unlink(prtd->ram_link);
-
- if (prtd->asp_link[0] >= 0)
- edma_free_slot(prtd->asp_link[0]);
- if (prtd->asp_link[1] >= 0)
- edma_free_slot(prtd->asp_link[1]);
- if (prtd->asp_channel >= 0)
- edma_free_channel(prtd->asp_channel);
- if (prtd->ram_link >= 0)
- edma_free_slot(prtd->ram_link);
- if (prtd->ram_link2 >= 0)
- edma_free_slot(prtd->ram_link2);
- if (prtd->ram_channel >= 0)
- edma_free_channel(prtd->ram_channel);
-
- kfree(prtd);
-
- return 0;
-}
-
-static int davinci_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- return snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
-}
-
-static int davinci_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
-static int davinci_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- return dma_mmap_writecombine(substream->pcm->card->dev, vma,
- runtime->dma_area,
- runtime->dma_addr,
- runtime->dma_bytes);
-}
-
-static struct snd_pcm_ops davinci_pcm_ops = {
- .open = davinci_pcm_open,
- .close = davinci_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = davinci_pcm_hw_params,
- .hw_free = davinci_pcm_hw_free,
- .prepare = davinci_pcm_prepare,
- .trigger = davinci_pcm_trigger,
- .pointer = davinci_pcm_pointer,
- .mmap = davinci_pcm_mmap,
-};
-
-static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream,
- size_t size)
-{
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
-
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
- buf->area = dma_alloc_writecombine(pcm->card->dev, size,
- &buf->addr, GFP_KERNEL);
-
- pr_debug("davinci_pcm: preallocate_dma_buffer: area=%p, addr=%p, "
- "size=%d\n", (void *) buf->area, (void *) buf->addr, size);
-
- if (!buf->area)
- return -ENOMEM;
-
- buf->bytes = size;
- return 0;
-}
-
-static void davinci_pcm_free(struct snd_pcm *pcm)
-{
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
- int stream;
-
- for (stream = 0; stream < 2; stream++) {
- struct snd_dma_buffer *iram_dma;
- substream = pcm->streams[stream].substream;
- if (!substream)
- continue;
-
- buf = &substream->dma_buffer;
- if (!buf->area)
- continue;
-
- dma_free_writecombine(pcm->card->dev, buf->bytes,
- buf->area, buf->addr);
- buf->area = NULL;
- iram_dma = buf->private_data;
- if (iram_dma) {
- davinci_free_sram(substream, iram_dma);
- kfree(iram_dma);
- }
- }
-}
-
-static int davinci_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_card *card = rtd->card->snd_card;
- struct snd_pcm *pcm = rtd->pcm;
- int ret;
-
- ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
- if (ret)
- return ret;
-
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
- ret = davinci_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK,
- pcm_hardware_playback.buffer_bytes_max);
- if (ret)
- return ret;
- }
-
- if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
- ret = davinci_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE,
- pcm_hardware_capture.buffer_bytes_max);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static struct snd_soc_platform_driver davinci_soc_platform = {
- .ops = &davinci_pcm_ops,
- .pcm_new = davinci_pcm_new,
- .pcm_free = davinci_pcm_free,
-};
-
-int davinci_soc_platform_register(struct device *dev)
-{
- return devm_snd_soc_register_platform(dev, &davinci_soc_platform);
-}
-EXPORT_SYMBOL_GPL(davinci_soc_platform_register);
-
-MODULE_AUTHOR("Vladimir Barinov");
-MODULE_DESCRIPTION("TI DAVINCI PCM DMA module");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * ALSA PCM interface for the TI DAVINCI processor
- *
- * Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
- * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef _DAVINCI_PCM_H
-#define _DAVINCI_PCM_H
-
-#include <linux/genalloc.h>
-#include <linux/platform_data/davinci_asp.h>
-#include <linux/platform_data/edma.h>
-
-struct davinci_pcm_dma_params {
- int channel; /* sync dma channel ID */
- unsigned short acnt;
- dma_addr_t dma_addr; /* device physical address for DMA */
- unsigned sram_size;
- struct gen_pool *sram_pool; /* SRAM gen_pool for ping pong */
- enum dma_event_q asp_chan_q; /* event queue number for ASP channel */
- enum dma_event_q ram_chan_q; /* event queue number for RAM channel */
- unsigned char data_type; /* xfer data type */
- unsigned char convert_mono_stereo;
- unsigned int fifo_level;
-};
-
-#if IS_ENABLED(CONFIG_SND_DAVINCI_SOC)
-int davinci_soc_platform_register(struct device *dev);
-#else
-static inline int davinci_soc_platform_register(struct device *dev)
-{
- return 0;
-}
-#endif /* CONFIG_SND_DAVINCI_SOC */
-
-#endif
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
-#include "davinci-pcm.h"
+#include "edma-pcm.h"
#include "davinci-i2s.h"
#define MOD_REG_BIT(val, mask, set) do { \
struct davinci_vcif_dev {
struct davinci_vc *davinci_vc;
- struct davinci_pcm_dma_params dma_params[2];
+ struct snd_dmaengine_dai_dma_data dma_data[2];
+ int dma_request[2];
};
static void davinci_vcif_start(struct snd_pcm_substream *substream)
{
struct davinci_vcif_dev *davinci_vcif_dev = snd_soc_dai_get_drvdata(dai);
struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc;
- struct davinci_pcm_dma_params *dma_params =
- &davinci_vcif_dev->dma_params[substream->stream];
u32 w;
/* Restart the codec before setup */
/* Determine xfer data type */
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_U8:
- dma_params->data_type = 0;
-
MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 |
DAVINCI_VC_CTRL_RD_UNSIGNED |
DAVINCI_VC_CTRL_WD_BITS_8 |
DAVINCI_VC_CTRL_WD_UNSIGNED, 1);
break;
case SNDRV_PCM_FORMAT_S8:
- dma_params->data_type = 1;
-
MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 |
DAVINCI_VC_CTRL_WD_BITS_8, 1);
DAVINCI_VC_CTRL_WD_UNSIGNED, 0);
break;
case SNDRV_PCM_FORMAT_S16_LE:
- dma_params->data_type = 2;
-
MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 |
DAVINCI_VC_CTRL_RD_UNSIGNED |
DAVINCI_VC_CTRL_WD_BITS_8 |
return -EINVAL;
}
- dma_params->acnt = dma_params->data_type;
-
writel(w, davinci_vc->base + DAVINCI_VC_CTRL);
return 0;
return ret;
}
-static int davinci_vcif_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct davinci_vcif_dev *dev = snd_soc_dai_get_drvdata(dai);
-
- snd_soc_dai_set_dma_data(dai, substream, dev->dma_params);
- return 0;
-}
-
#define DAVINCI_VCIF_RATES SNDRV_PCM_RATE_8000_48000
static const struct snd_soc_dai_ops davinci_vcif_dai_ops = {
- .startup = davinci_vcif_startup,
.trigger = davinci_vcif_trigger,
.hw_params = davinci_vcif_hw_params,
};
+static int davinci_vcif_dai_probe(struct snd_soc_dai *dai)
+{
+ struct davinci_vcif_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
+ dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE];
+
+ return 0;
+}
+
static struct snd_soc_dai_driver davinci_vcif_dai = {
+ .probe = davinci_vcif_dai_probe,
.playback = {
.channels_min = 1,
.channels_max = 2,
/* DMA tx params */
davinci_vcif_dev->davinci_vc = davinci_vc;
- davinci_vcif_dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].channel =
- davinci_vc->davinci_vcif.dma_tx_channel;
- davinci_vcif_dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].dma_addr =
- davinci_vc->davinci_vcif.dma_tx_addr;
+ davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data =
+ &davinci_vc->davinci_vcif.dma_tx_channel;
+ davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr =
+ davinci_vc->davinci_vcif.dma_tx_addr;
/* DMA rx params */
- davinci_vcif_dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel =
- davinci_vc->davinci_vcif.dma_rx_channel;
- davinci_vcif_dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].dma_addr =
- davinci_vc->davinci_vcif.dma_rx_addr;
+ davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].filter_data =
+ &davinci_vc->davinci_vcif.dma_rx_channel;
+ davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr =
+ davinci_vc->davinci_vcif.dma_rx_addr;
dev_set_drvdata(&pdev->dev, davinci_vcif_dev);
return ret;
}
- ret = davinci_soc_platform_register(&pdev->dev);
+ ret = edma_pcm_platform_register(&pdev->dev);
if (ret) {
dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
snd_soc_unregister_component(&pdev->dev);
memcpy(priv->dai_link, fsl_asoc_card_dai,
sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(priv->dai_link));
+ ret = snd_soc_of_parse_audio_routing(&priv->card, "audio-routing");
+ if (ret) {
+ dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);
+ goto asrc_fail;
+ }
+
/* Normal DAI Link */
priv->dai_link[0].cpu_of_node = cpu_np;
priv->dai_link[0].codec_of_node = codec_np;
enum spdif_txrate index, bool round)
{
const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 };
- bool is_sysclk = clk == spdif_priv->sysclk;
+ bool is_sysclk = clk_is_match(clk, spdif_priv->sysclk);
u64 rate_ideal, rate_actual, sub;
u32 sysclk_dfmin, sysclk_dfmax;
u32 txclk_df, sysclk_df, arate;
spdif_priv->txclk_src[index], rate[index]);
dev_dbg(&pdev->dev, "use txclk df %d for %dHz sample rate\n",
spdif_priv->txclk_df[index], rate[index]);
- if (spdif_priv->txclk[index] == spdif_priv->sysclk)
+ if (clk_is_match(spdif_priv->txclk[index], spdif_priv->sysclk))
dev_dbg(&pdev->dev, "use sysclk df %d for %dHz sample rate\n",
spdif_priv->sysclk_df[index], rate[index]);
dev_dbg(&pdev->dev, "the best rate for %dHz sample rate is %dHz\n",
factor = (div2 + 1) * (7 * psr + 1) * 2;
for (i = 0; i < 255; i++) {
- /* The bclk rate must be smaller than 1/5 sysclk rate */
- if (factor * (i + 1) < 5)
- continue;
-
- tmprate = freq * factor * (i + 2);
+ tmprate = freq * factor * (i + 1);
if (baudclk_is_used)
clkrate = clk_get_rate(ssi_private->baudclk);
else
clkrate = clk_round_rate(ssi_private->baudclk, tmprate);
+ /*
+ * Hardware limitation: The bclk rate must be
+ * never greater than 1/5 IPG clock rate
+ */
+ if (clkrate * 5 > clk_get_rate(ssi_private->clk))
+ continue;
+
clkrate /= factor;
afreq = clkrate / (i + 1);
ssi_private->dma_params_tx.addr = ssi_private->ssi_phys + CCSR_SSI_STX0;
ssi_private->dma_params_rx.addr = ssi_private->ssi_phys + CCSR_SSI_SRX0;
- ret = !of_property_read_u32_array(np, "dmas", dmas, 4);
+ ret = of_property_read_u32_array(np, "dmas", dmas, 4);
if (ssi_private->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL) {
ssi_private->use_dual_fifo = true;
/* When using dual fifo mode, we need to keep watermark
/* Headphone jack detection */
if (gpio_is_valid(data->jack_gpio)) {
- ret = snd_soc_jack_new(rtd->codec, "Headphone",
- SND_JACK_HEADPHONE | SND_JACK_BTN_0,
- &headset_jack);
+ ret = snd_soc_card_jack_new(rtd->card, "Headphone",
+ SND_JACK_HEADPHONE | SND_JACK_BTN_0,
+ &headset_jack, NULL, 0);
if (ret)
return ret;
struct snd_soc_dapm_context *dapm = &codec->dapm;
/* Headphone jack detection */
- snd_soc_jack_new(codec, "Headphone", SND_JACK_HEADPHONE, &hp_jack);
- snd_soc_jack_add_pins(&hp_jack, ARRAY_SIZE(hp_jack_pins),
- hp_jack_pins);
+ snd_soc_card_jack_new(rtd->card, "Headphone", SND_JACK_HEADPHONE,
+ &hp_jack, hp_jack_pins, ARRAY_SIZE(hp_jack_pins));
wm8350_hp_jack_detect(codec, WM8350_JDR, &hp_jack, SND_JACK_HEADPHONE);
/* Microphone jack detection */
- snd_soc_jack_new(codec, "Microphone",
- SND_JACK_MICROPHONE | SND_JACK_BTN_0, &mic_jack);
- snd_soc_jack_add_pins(&mic_jack, ARRAY_SIZE(mic_jack_pins),
- mic_jack_pins);
+ snd_soc_card_jack_new(rtd->card, "Microphone",
+ SND_JACK_MICROPHONE | SND_JACK_BTN_0, &mic_jack,
+ mic_jack_pins, ARRAY_SIZE(mic_jack_pins));
wm8350_mic_jack_detect(codec, &mic_jack, SND_JACK_MICROPHONE,
SND_JACK_BTN_0);
return ret;
if (gpio_is_valid(priv->gpio_hp_det)) {
- snd_soc_jack_new(codec->codec, "Headphones", SND_JACK_HEADPHONE,
- &simple_card_hp_jack);
- snd_soc_jack_add_pins(&simple_card_hp_jack,
- ARRAY_SIZE(simple_card_hp_jack_pins),
- simple_card_hp_jack_pins);
+ snd_soc_card_jack_new(rtd->card, "Headphones",
+ SND_JACK_HEADPHONE,
+ &simple_card_hp_jack,
+ simple_card_hp_jack_pins,
+ ARRAY_SIZE(simple_card_hp_jack_pins));
simple_card_hp_jack_gpio.gpio = priv->gpio_hp_det;
simple_card_hp_jack_gpio.invert = priv->gpio_hp_det_invert;
}
if (gpio_is_valid(priv->gpio_mic_det)) {
- snd_soc_jack_new(codec->codec, "Mic Jack", SND_JACK_MICROPHONE,
- &simple_card_mic_jack);
- snd_soc_jack_add_pins(&simple_card_mic_jack,
- ARRAY_SIZE(simple_card_mic_jack_pins),
- simple_card_mic_jack_pins);
+ snd_soc_card_jack_new(rtd->card, "Mic Jack",
+ SND_JACK_MICROPHONE,
+ &simple_card_mic_jack,
+ simple_card_mic_jack_pins,
+ ARRAY_SIZE(simple_card_mic_jack_pins));
simple_card_mic_jack_gpio.gpio = priv->gpio_mic_det;
simple_card_mic_jack_gpio.invert = priv->gpio_mic_det_invert;
snd_soc_jack_add_gpios(&simple_card_mic_jack, 1,
strlen(dai_link->cpu_dai_name) +
strlen(dai_link->codec_dai_name) + 2,
GFP_KERNEL);
+ if (!name) {
+ ret = -ENOMEM;
+ goto dai_link_of_err;
+ }
+
sprintf(name, "%s-%s", dai_link->cpu_dai_name,
dai_link->codec_dai_name);
dai_link->name = dai_link->stream_name = name;
# Core support
-snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o
-snd-soc-sst-acpi-objs := sst-acpi.o
-
-snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o \
- sst-mfld-platform-compress.o sst-atom-controls.o
-snd-soc-mfld-machine-objs := mfld_machine.o
-
-obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o
-obj-$(CONFIG_SND_MFLD_MACHINE) += snd-soc-mfld-machine.o
-
-obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o
-obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o
+obj-$(CONFIG_SND_SOC_INTEL_SST) += common/
# Platform Support
-snd-soc-sst-haswell-pcm-objs := \
- sst-haswell-ipc.o sst-haswell-pcm.o sst-haswell-dsp.o
-snd-soc-sst-baytrail-pcm-objs := \
- sst-baytrail-ipc.o sst-baytrail-pcm.o sst-baytrail-dsp.o
-
-obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += snd-soc-sst-haswell-pcm.o
-obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += snd-soc-sst-baytrail-pcm.o
+obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += haswell/
+obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += baytrail/
+obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += atom/
# Machine support
-snd-soc-sst-haswell-objs := haswell.o
-snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o
-snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o
-snd-soc-sst-broadwell-objs := broadwell.o
-snd-soc-sst-bytcr-dpcm-rt5640-objs := bytcr_dpcm_rt5640.o
-snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
-snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o
-
-obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
-obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o
-obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o
-obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o
-obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-dpcm-rt5640.o
-obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
-obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o
-
-# DSP driver
-obj-$(CONFIG_SND_SST_IPC) += sst/
+obj-$(CONFIG_SND_SOC_INTEL_SST) += boards/
--- /dev/null
+snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o \
+ sst-mfld-platform-compress.o sst-atom-controls.o
+
+obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o
+
+# DSP driver
+obj-$(CONFIG_SND_SST_IPC) += sst/
--- /dev/null
+/*
+ * sst-atom-controls.c - Intel MID Platform driver DPCM ALSA controls for Mrfld
+ *
+ * Copyright (C) 2013-14 Intel Corp
+ * Author: Omair Mohammed Abdullah <omair.m.abdullah@intel.com>
+ * Vinod Koul <vinod.koul@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * In the dpcm driver modelling when a particular FE/BE/Mixer/Pipe is active
+ * we forward the settings and parameters, rest we keep the values in
+ * driver and forward when DAPM enables them
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "sst-mfld-platform.h"
+#include "sst-atom-controls.h"
+
+static int sst_fill_byte_control(struct sst_data *drv,
+ u8 ipc_msg, u8 block,
+ u8 task_id, u8 pipe_id,
+ u16 len, void *cmd_data)
+{
+ struct snd_sst_bytes_v2 *byte_data = drv->byte_stream;
+
+ byte_data->type = SST_CMD_BYTES_SET;
+ byte_data->ipc_msg = ipc_msg;
+ byte_data->block = block;
+ byte_data->task_id = task_id;
+ byte_data->pipe_id = pipe_id;
+
+ if (len > SST_MAX_BIN_BYTES - sizeof(*byte_data)) {
+ dev_err(&drv->pdev->dev, "command length too big (%u)", len);
+ return -EINVAL;
+ }
+ byte_data->len = len;
+ memcpy(byte_data->bytes, cmd_data, len);
+ print_hex_dump_bytes("writing to lpe: ", DUMP_PREFIX_OFFSET,
+ byte_data, len + sizeof(*byte_data));
+ return 0;
+}
+
+static int sst_fill_and_send_cmd_unlocked(struct sst_data *drv,
+ u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id,
+ void *cmd_data, u16 len)
+{
+ int ret = 0;
+
+ ret = sst_fill_byte_control(drv, ipc_msg,
+ block, task_id, pipe_id, len, cmd_data);
+ if (ret < 0)
+ return ret;
+ return sst->ops->send_byte_stream(sst->dev, drv->byte_stream);
+}
+
+/**
+ * sst_fill_and_send_cmd - generate the IPC message and send it to the FW
+ * @ipc_msg: type of IPC (CMD, SET_PARAMS, GET_PARAMS)
+ * @cmd_data: the IPC payload
+ */
+static int sst_fill_and_send_cmd(struct sst_data *drv,
+ u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id,
+ void *cmd_data, u16 len)
+{
+ int ret;
+
+ mutex_lock(&drv->lock);
+ ret = sst_fill_and_send_cmd_unlocked(drv, ipc_msg, block,
+ task_id, pipe_id, cmd_data, len);
+ mutex_unlock(&drv->lock);
+
+ return ret;
+}
+
+/**
+ * tx map value is a bitfield where each bit represents a FW channel
+ *
+ * 3 2 1 0 # 0 = codec0, 1 = codec1
+ * RLRLRLRL # 3, 4 = reserved
+ *
+ * e.g. slot 0 rx map = 00001100b -> data from slot 0 goes into codec_in1 L,R
+ */
+static u8 sst_ssp_tx_map[SST_MAX_TDM_SLOTS] = {
+ 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default rx map */
+};
+
+/**
+ * rx map value is a bitfield where each bit represents a slot
+ *
+ * 76543210 # 0 = slot 0, 1 = slot 1
+ *
+ * e.g. codec1_0 tx map = 00000101b -> data from codec_out1_0 goes into slot 0, 2
+ */
+static u8 sst_ssp_rx_map[SST_MAX_TDM_SLOTS] = {
+ 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default tx map */
+};
+
+/**
+ * NOTE: this is invoked with lock held
+ */
+static int sst_send_slot_map(struct sst_data *drv)
+{
+ struct sst_param_sba_ssp_slot_map cmd;
+
+ SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+ cmd.header.command_id = SBA_SET_SSP_SLOT_MAP;
+ cmd.header.length = sizeof(struct sst_param_sba_ssp_slot_map)
+ - sizeof(struct sst_dsp_header);
+
+ cmd.param_id = SBA_SET_SSP_SLOT_MAP;
+ cmd.param_len = sizeof(cmd.rx_slot_map) + sizeof(cmd.tx_slot_map)
+ + sizeof(cmd.ssp_index);
+ cmd.ssp_index = SSP_CODEC;
+
+ memcpy(cmd.rx_slot_map, &sst_ssp_tx_map[0], sizeof(cmd.rx_slot_map));
+ memcpy(cmd.tx_slot_map, &sst_ssp_rx_map[0], sizeof(cmd.tx_slot_map));
+
+ return sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,
+ SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd,
+ sizeof(cmd.header) + cmd.header.length);
+}
+
+int sst_slot_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct sst_enum *e = (struct sst_enum *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = e->max;
+
+ if (uinfo->value.enumerated.item > e->max - 1)
+ uinfo->value.enumerated.item = e->max - 1;
+ strcpy(uinfo->value.enumerated.name,
+ e->texts[uinfo->value.enumerated.item]);
+
+ return 0;
+}
+
+/**
+ * sst_slot_get - get the status of the interleaver/deinterleaver control
+ *
+ * Searches the map where the control status is stored, and gets the
+ * channel/slot which is currently set for this enumerated control. Since it is
+ * an enumerated control, there is only one possible value.
+ */
+static int sst_slot_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct sst_enum *e = (void *)kcontrol->private_value;
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct sst_data *drv = snd_soc_component_get_drvdata(c);
+ unsigned int ctl_no = e->reg;
+ unsigned int is_tx = e->tx;
+ unsigned int val, mux;
+ u8 *map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map;
+
+ mutex_lock(&drv->lock);
+ val = 1 << ctl_no;
+ /* search which slot/channel has this bit set - there should be only one */
+ for (mux = e->max; mux > 0; mux--)
+ if (map[mux - 1] & val)
+ break;
+
+ ucontrol->value.enumerated.item[0] = mux;
+ mutex_unlock(&drv->lock);
+
+ dev_dbg(c->dev, "%s - %s map = %#x\n",
+ is_tx ? "tx channel" : "rx slot",
+ e->texts[mux], mux ? map[mux - 1] : -1);
+ return 0;
+}
+
+/* sst_check_and_send_slot_map - helper for checking power state and sending
+ * slot map cmd
+ *
+ * called with lock held
+ */
+static int sst_check_and_send_slot_map(struct sst_data *drv, struct snd_kcontrol *kcontrol)
+{
+ struct sst_enum *e = (void *)kcontrol->private_value;
+ int ret = 0;
+
+ if (e->w && e->w->power)
+ ret = sst_send_slot_map(drv);
+ else
+ dev_err(&drv->pdev->dev, "Slot control: %s doesn't have DAPM widget!!!\n",
+ kcontrol->id.name);
+ return ret;
+}
+
+/**
+ * sst_slot_put - set the status of interleaver/deinterleaver control
+ *
+ * (de)interleaver controls are defined in opposite sense to be user-friendly
+ *
+ * Instead of the enum value being the value written to the register, it is the
+ * register address; and the kcontrol number (register num) is the value written
+ * to the register. This is so that there can be only one value for each
+ * slot/channel since there is only one control for each slot/channel.
+ *
+ * This means that whenever an enum is set, we need to clear the bit
+ * for that kcontrol_no for all the interleaver OR deinterleaver registers
+ */
+static int sst_slot_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+ struct sst_data *drv = snd_soc_component_get_drvdata(c);
+ struct sst_enum *e = (void *)kcontrol->private_value;
+ int i, ret = 0;
+ unsigned int ctl_no = e->reg;
+ unsigned int is_tx = e->tx;
+ unsigned int slot_channel_no;
+ unsigned int val, mux;
+ u8 *map;
+
+ map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map;
+
+ val = 1 << ctl_no;
+ mux = ucontrol->value.enumerated.item[0];
+ if (mux > e->max - 1)
+ return -EINVAL;
+
+ mutex_lock(&drv->lock);
+ /* first clear all registers of this bit */
+ for (i = 0; i < e->max; i++)
+ map[i] &= ~val;
+
+ if (mux == 0) {
+ /* kctl set to 'none' and we reset the bits so send IPC */
+ ret = sst_check_and_send_slot_map(drv, kcontrol);
+
+ mutex_unlock(&drv->lock);
+ return ret;
+ }
+
+ /* offset by one to take "None" into account */
+ slot_channel_no = mux - 1;
+ map[slot_channel_no] |= val;
+
+ dev_dbg(c->dev, "%s %s map = %#x\n",
+ is_tx ? "tx channel" : "rx slot",
+ e->texts[mux], map[slot_channel_no]);
+
+ ret = sst_check_and_send_slot_map(drv, kcontrol);
+
+ mutex_unlock(&drv->lock);
+ return ret;
+}
+
+static int sst_send_algo_cmd(struct sst_data *drv,
+ struct sst_algo_control *bc)
+{
+ int len, ret = 0;
+ struct sst_cmd_set_params *cmd;
+
+ /*bc->max includes sizeof algos + length field*/
+ len = sizeof(cmd->dst) + sizeof(cmd->command_id) + bc->max;
+
+ cmd = kzalloc(len, GFP_KERNEL);
+ if (cmd == NULL)
+ return -ENOMEM;
+
+ SST_FILL_DESTINATION(2, cmd->dst, bc->pipe_id, bc->module_id);
+ cmd->command_id = bc->cmd_id;
+ memcpy(cmd->params, bc->params, bc->max);
+
+ ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,
+ SST_FLAG_BLOCKED, bc->task_id, 0, cmd, len);
+ kfree(cmd);
+ return ret;
+}
+
+/**
+ * sst_find_and_send_pipe_algo - send all the algo parameters for a pipe
+ *
+ * The algos which are in each pipeline are sent to the firmware one by one
+ *
+ * Called with lock held
+ */
+static int sst_find_and_send_pipe_algo(struct sst_data *drv,
+ const char *pipe, struct sst_ids *ids)
+{
+ int ret = 0;
+ struct sst_algo_control *bc;
+ struct sst_module *algo = NULL;
+
+ dev_dbg(&drv->pdev->dev, "Enter: widget=%s\n", pipe);
+
+ list_for_each_entry(algo, &ids->algo_list, node) {
+ bc = (void *)algo->kctl->private_value;
+
+ dev_dbg(&drv->pdev->dev, "Found algo control name=%s pipe=%s\n",
+ algo->kctl->id.name, pipe);
+ ret = sst_send_algo_cmd(drv, bc);
+ if (ret)
+ return ret;
+ }
+ return ret;
+}
+
+static int sst_algo_bytes_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct sst_algo_control *bc = (void *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = bc->max;
+
+ return 0;
+}
+
+static int sst_algo_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct sst_algo_control *bc = (void *)kcontrol->private_value;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+
+ switch (bc->type) {
+ case SST_ALGO_PARAMS:
+ memcpy(ucontrol->value.bytes.data, bc->params, bc->max);
+ break;
+ default:
+ dev_err(component->dev, "Invalid Input- algo type:%d\n",
+ bc->type);
+ return -EINVAL;
+
+ }
+ return 0;
+}
+
+static int sst_algo_control_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);
+ struct sst_algo_control *bc = (void *)kcontrol->private_value;
+
+ dev_dbg(cmpnt->dev, "control_name=%s\n", kcontrol->id.name);
+ mutex_lock(&drv->lock);
+ switch (bc->type) {
+ case SST_ALGO_PARAMS:
+ memcpy(bc->params, ucontrol->value.bytes.data, bc->max);
+ break;
+ default:
+ mutex_unlock(&drv->lock);
+ dev_err(cmpnt->dev, "Invalid Input- algo type:%d\n",
+ bc->type);
+ return -EINVAL;
+ }
+ /*if pipe is enabled, need to send the algo params from here*/
+ if (bc->w && bc->w->power)
+ ret = sst_send_algo_cmd(drv, bc);
+ mutex_unlock(&drv->lock);
+
+ return ret;
+}
+
+static int sst_gain_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = mc->stereo ? 2 : 1;
+ uinfo->value.integer.min = mc->min;
+ uinfo->value.integer.max = mc->max;
+
+ return 0;
+}
+
+/**
+ * sst_send_gain_cmd - send the gain algorithm IPC to the FW
+ * @gv: the stored value of gain (also contains rampduration)
+ * @mute: flag that indicates whether this was called from the
+ * digital_mute callback or directly. If called from the
+ * digital_mute callback, module will be muted/unmuted based on this
+ * flag. The flag is always 0 if called directly.
+ *
+ * Called with sst_data.lock held
+ *
+ * The user-set gain value is sent only if the user-controllable 'mute' control
+ * is OFF (indicated by gv->mute). Otherwise, the mute value (MIN value) is
+ * sent.
+ */
+static int sst_send_gain_cmd(struct sst_data *drv, struct sst_gain_value *gv,
+ u16 task_id, u16 loc_id, u16 module_id, int mute)
+{
+ struct sst_cmd_set_gain_dual cmd;
+
+ dev_dbg(&drv->pdev->dev, "Enter\n");
+
+ cmd.header.command_id = MMX_SET_GAIN;
+ SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+ cmd.gain_cell_num = 1;
+
+ if (mute || gv->mute) {
+ cmd.cell_gains[0].cell_gain_left = SST_GAIN_MIN_VALUE;
+ cmd.cell_gains[0].cell_gain_right = SST_GAIN_MIN_VALUE;
+ } else {
+ cmd.cell_gains[0].cell_gain_left = gv->l_gain;
+ cmd.cell_gains[0].cell_gain_right = gv->r_gain;
+ }
+
+ SST_FILL_DESTINATION(2, cmd.cell_gains[0].dest,
+ loc_id, module_id);
+ cmd.cell_gains[0].gain_time_constant = gv->ramp_duration;
+
+ cmd.header.length = sizeof(struct sst_cmd_set_gain_dual)
+ - sizeof(struct sst_dsp_header);
+
+ /* we are with lock held, so call the unlocked api to send */
+ return sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,
+ SST_FLAG_BLOCKED, task_id, 0, &cmd,
+ sizeof(cmd.header) + cmd.header.length);
+}
+
+static int sst_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
+ struct sst_gain_value *gv = mc->gain_val;
+
+ switch (mc->type) {
+ case SST_GAIN_TLV:
+ ucontrol->value.integer.value[0] = gv->l_gain;
+ ucontrol->value.integer.value[1] = gv->r_gain;
+ break;
+
+ case SST_GAIN_MUTE:
+ ucontrol->value.integer.value[0] = gv->mute ? 1 : 0;
+ break;
+
+ case SST_GAIN_RAMP_DURATION:
+ ucontrol->value.integer.value[0] = gv->ramp_duration;
+ break;
+
+ default:
+ dev_err(component->dev, "Invalid Input- gain type:%d\n",
+ mc->type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sst_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);
+ struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
+ struct sst_gain_value *gv = mc->gain_val;
+
+ mutex_lock(&drv->lock);
+
+ switch (mc->type) {
+ case SST_GAIN_TLV:
+ gv->l_gain = ucontrol->value.integer.value[0];
+ gv->r_gain = ucontrol->value.integer.value[1];
+ dev_dbg(cmpnt->dev, "%s: Volume %d, %d\n",
+ mc->pname, gv->l_gain, gv->r_gain);
+ break;
+
+ case SST_GAIN_MUTE:
+ gv->mute = !!ucontrol->value.integer.value[0];
+ dev_dbg(cmpnt->dev, "%s: Mute %d\n", mc->pname, gv->mute);
+ break;
+
+ case SST_GAIN_RAMP_DURATION:
+ gv->ramp_duration = ucontrol->value.integer.value[0];
+ dev_dbg(cmpnt->dev, "%s: Ramp Delay%d\n",
+ mc->pname, gv->ramp_duration);
+ break;
+
+ default:
+ mutex_unlock(&drv->lock);
+ dev_err(cmpnt->dev, "Invalid Input- gain type:%d\n",
+ mc->type);
+ return -EINVAL;
+ }
+
+ if (mc->w && mc->w->power)
+ ret = sst_send_gain_cmd(drv, gv, mc->task_id,
+ mc->pipe_id | mc->instance_id, mc->module_id, 0);
+ mutex_unlock(&drv->lock);
+
+ return ret;
+}
+
+static int sst_set_pipe_gain(struct sst_ids *ids,
+ struct sst_data *drv, int mute);
+
+static int sst_send_pipe_module_params(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol)
+{
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct sst_data *drv = snd_soc_component_get_drvdata(c);
+ struct sst_ids *ids = w->priv;
+
+ mutex_lock(&drv->lock);
+ sst_find_and_send_pipe_algo(drv, w->name, ids);
+ sst_set_pipe_gain(ids, drv, 0);
+ mutex_unlock(&drv->lock);
+
+ return 0;
+}
+
+static int sst_generic_modules_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ return sst_send_pipe_module_params(w, k);
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(sst_gain_tlv_common, SST_GAIN_MIN_VALUE * 10, 10, 0);
+
+/* Look up table to convert MIXER SW bit regs to SWM inputs */
+static const uint swm_mixer_input_ids[SST_SWM_INPUT_COUNT] = {
+ [SST_IP_CODEC0] = SST_SWM_IN_CODEC0,
+ [SST_IP_CODEC1] = SST_SWM_IN_CODEC1,
+ [SST_IP_LOOP0] = SST_SWM_IN_SPROT_LOOP,
+ [SST_IP_LOOP1] = SST_SWM_IN_MEDIA_LOOP1,
+ [SST_IP_LOOP2] = SST_SWM_IN_MEDIA_LOOP2,
+ [SST_IP_PCM0] = SST_SWM_IN_PCM0,
+ [SST_IP_PCM1] = SST_SWM_IN_PCM1,
+ [SST_IP_MEDIA0] = SST_SWM_IN_MEDIA0,
+ [SST_IP_MEDIA1] = SST_SWM_IN_MEDIA1,
+ [SST_IP_MEDIA2] = SST_SWM_IN_MEDIA2,
+ [SST_IP_MEDIA3] = SST_SWM_IN_MEDIA3,
+};
+
+/**
+ * fill_swm_input - fill in the SWM input ids given the register
+ *
+ * The register value is a bit-field inicated which mixer inputs are ON. Use the
+ * lookup table to get the input-id and fill it in the structure.
+ */
+static int fill_swm_input(struct snd_soc_component *cmpnt,
+ struct swm_input_ids *swm_input, unsigned int reg)
+{
+ uint i, is_set, nb_inputs = 0;
+ u16 input_loc_id;
+
+ dev_dbg(cmpnt->dev, "reg: %#x\n", reg);
+ for (i = 0; i < SST_SWM_INPUT_COUNT; i++) {
+ is_set = reg & BIT(i);
+ if (!is_set)
+ continue;
+
+ input_loc_id = swm_mixer_input_ids[i];
+ SST_FILL_DESTINATION(2, swm_input->input_id,
+ input_loc_id, SST_DEFAULT_MODULE_ID);
+ nb_inputs++;
+ swm_input++;
+ dev_dbg(cmpnt->dev, "input id: %#x, nb_inputs: %d\n",
+ input_loc_id, nb_inputs);
+
+ if (nb_inputs == SST_CMD_SWM_MAX_INPUTS) {
+ dev_warn(cmpnt->dev, "SET_SWM cmd max inputs reached");
+ break;
+ }
+ }
+ return nb_inputs;
+}
+
+
+/**
+ * called with lock held
+ */
+static int sst_set_pipe_gain(struct sst_ids *ids,
+ struct sst_data *drv, int mute)
+{
+ int ret = 0;
+ struct sst_gain_mixer_control *mc;
+ struct sst_gain_value *gv;
+ struct sst_module *gain = NULL;
+
+ list_for_each_entry(gain, &ids->gain_list, node) {
+ struct snd_kcontrol *kctl = gain->kctl;
+
+ dev_dbg(&drv->pdev->dev, "control name=%s\n", kctl->id.name);
+ mc = (void *)kctl->private_value;
+ gv = mc->gain_val;
+
+ ret = sst_send_gain_cmd(drv, gv, mc->task_id,
+ mc->pipe_id | mc->instance_id, mc->module_id, mute);
+ if (ret)
+ return ret;
+ }
+ return ret;
+}
+
+static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct sst_cmd_set_swm cmd;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);
+ struct sst_ids *ids = w->priv;
+ bool set_mixer = false;
+ struct soc_mixer_control *mc;
+ int val = 0;
+ int i = 0;
+
+ dev_dbg(cmpnt->dev, "widget = %s\n", w->name);
+ /*
+ * Identify which mixer input is on and send the bitmap of the
+ * inputs as an IPC to the DSP.
+ */
+ for (i = 0; i < w->num_kcontrols; i++) {
+ if (dapm_kcontrol_get_value(w->kcontrols[i])) {
+ mc = (struct soc_mixer_control *)(w->kcontrols[i])->private_value;
+ val |= 1 << mc->shift;
+ }
+ }
+ dev_dbg(cmpnt->dev, "val = %#x\n", val);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ set_mixer = true;
+ break;
+ case SND_SOC_DAPM_POST_REG:
+ if (w->power)
+ set_mixer = true;
+ break;
+ default:
+ set_mixer = false;
+ }
+
+ if (set_mixer == false)
+ return 0;
+
+ if (SND_SOC_DAPM_EVENT_ON(event) ||
+ event == SND_SOC_DAPM_POST_REG)
+ cmd.switch_state = SST_SWM_ON;
+ else
+ cmd.switch_state = SST_SWM_OFF;
+
+ SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+ /* MMX_SET_SWM == SBA_SET_SWM */
+ cmd.header.command_id = SBA_SET_SWM;
+
+ SST_FILL_DESTINATION(2, cmd.output_id,
+ ids->location_id, SST_DEFAULT_MODULE_ID);
+ cmd.nb_inputs = fill_swm_input(cmpnt, &cmd.input[0], val);
+ cmd.header.length = offsetof(struct sst_cmd_set_swm, input)
+ - sizeof(struct sst_dsp_header)
+ + (cmd.nb_inputs * sizeof(cmd.input[0]));
+
+ return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+ ids->task_id, 0, &cmd,
+ sizeof(cmd.header) + cmd.header.length);
+}
+
+/* SBA mixers - 16 inputs */
+#define SST_SBA_DECLARE_MIX_CONTROLS(kctl_name) \
+ static const struct snd_kcontrol_new kctl_name[] = { \
+ SOC_DAPM_SINGLE("codec_in0 Switch", SND_SOC_NOPM, SST_IP_CODEC0, 1, 0), \
+ SOC_DAPM_SINGLE("codec_in1 Switch", SND_SOC_NOPM, SST_IP_CODEC1, 1, 0), \
+ SOC_DAPM_SINGLE("sprot_loop_in Switch", SND_SOC_NOPM, SST_IP_LOOP0, 1, 0), \
+ SOC_DAPM_SINGLE("media_loop1_in Switch", SND_SOC_NOPM, SST_IP_LOOP1, 1, 0), \
+ SOC_DAPM_SINGLE("media_loop2_in Switch", SND_SOC_NOPM, SST_IP_LOOP2, 1, 0), \
+ SOC_DAPM_SINGLE("pcm0_in Switch", SND_SOC_NOPM, SST_IP_PCM0, 1, 0), \
+ SOC_DAPM_SINGLE("pcm1_in Switch", SND_SOC_NOPM, SST_IP_PCM1, 1, 0), \
+ }
+
+#define SST_SBA_MIXER_GRAPH_MAP(mix_name) \
+ { mix_name, "codec_in0 Switch", "codec_in0" }, \
+ { mix_name, "codec_in1 Switch", "codec_in1" }, \
+ { mix_name, "sprot_loop_in Switch", "sprot_loop_in" }, \
+ { mix_name, "media_loop1_in Switch", "media_loop1_in" }, \
+ { mix_name, "media_loop2_in Switch", "media_loop2_in" }, \
+ { mix_name, "pcm0_in Switch", "pcm0_in" }, \
+ { mix_name, "pcm1_in Switch", "pcm1_in" }
+
+#define SST_MMX_DECLARE_MIX_CONTROLS(kctl_name) \
+ static const struct snd_kcontrol_new kctl_name[] = { \
+ SOC_DAPM_SINGLE("media0_in Switch", SND_SOC_NOPM, SST_IP_MEDIA0, 1, 0), \
+ SOC_DAPM_SINGLE("media1_in Switch", SND_SOC_NOPM, SST_IP_MEDIA1, 1, 0), \
+ SOC_DAPM_SINGLE("media2_in Switch", SND_SOC_NOPM, SST_IP_MEDIA2, 1, 0), \
+ SOC_DAPM_SINGLE("media3_in Switch", SND_SOC_NOPM, SST_IP_MEDIA3, 1, 0), \
+ }
+
+SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media0_controls);
+SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media1_controls);
+
+/* 18 SBA mixers */
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm0_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm1_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm2_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_sprot_l0_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l1_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l2_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_voip_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec0_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec1_controls);
+
+/*
+ * sst_handle_vb_timer - Start/Stop the DSP scheduler
+ *
+ * The DSP expects first cmd to be SBA_VB_START, so at first startup send
+ * that.
+ * DSP expects last cmd to be SBA_VB_IDLE, so at last shutdown send that.
+ *
+ * Do refcount internally so that we send command only at first start
+ * and last end. Since SST driver does its own ref count, invoke sst's
+ * power ops always!
+ */
+int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable)
+{
+ int ret = 0;
+ struct sst_cmd_generic cmd;
+ struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
+ static int timer_usage;
+
+ if (enable)
+ cmd.header.command_id = SBA_VB_START;
+ else
+ cmd.header.command_id = SBA_IDLE;
+ dev_dbg(dai->dev, "enable=%u, usage=%d\n", enable, timer_usage);
+
+ SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+ cmd.header.length = 0;
+
+ if (enable) {
+ ret = sst->ops->power(sst->dev, true);
+ if (ret < 0)
+ return ret;
+ }
+
+ mutex_lock(&drv->lock);
+ if (enable)
+ timer_usage++;
+ else
+ timer_usage--;
+
+ /*
+ * Send the command only if this call is the first enable or last
+ * disable
+ */
+ if ((enable && (timer_usage == 1)) ||
+ (!enable && (timer_usage == 0))) {
+ ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD,
+ SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd,
+ sizeof(cmd.header) + cmd.header.length);
+ if (ret && enable) {
+ timer_usage--;
+ enable = false;
+ }
+ }
+ mutex_unlock(&drv->lock);
+
+ if (!enable)
+ sst->ops->power(sst->dev, false);
+ return ret;
+}
+
+/**
+ * sst_ssp_config - contains SSP configuration for media UC
+ */
+static const struct sst_ssp_config sst_ssp_configs = {
+ .ssp_id = SSP_CODEC,
+ .bits_per_slot = 24,
+ .slots = 4,
+ .ssp_mode = SSP_MODE_MASTER,
+ .pcm_mode = SSP_PCM_MODE_NETWORK,
+ .duplex = SSP_DUPLEX,
+ .ssp_protocol = SSP_MODE_PCM,
+ .fs_width = 1,
+ .fs_frequency = SSP_FS_48_KHZ,
+ .active_slot_map = 0xF,
+ .start_delay = 0,
+};
+
+int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable)
+{
+ struct sst_cmd_sba_hw_set_ssp cmd;
+ struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
+ const struct sst_ssp_config *config;
+
+ dev_info(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id);
+
+ SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+ cmd.header.command_id = SBA_HW_SET_SSP;
+ cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp)
+ - sizeof(struct sst_dsp_header);
+
+ config = &sst_ssp_configs;
+ dev_dbg(dai->dev, "ssp_id: %u\n", config->ssp_id);
+
+ if (enable)
+ cmd.switch_state = SST_SWITCH_ON;
+ else
+ cmd.switch_state = SST_SWITCH_OFF;
+
+ cmd.selection = config->ssp_id;
+ cmd.nb_bits_per_slots = config->bits_per_slot;
+ cmd.nb_slots = config->slots;
+ cmd.mode = config->ssp_mode | (config->pcm_mode << 1);
+ cmd.duplex = config->duplex;
+ cmd.active_tx_slot_map = config->active_slot_map;
+ cmd.active_rx_slot_map = config->active_slot_map;
+ cmd.frame_sync_frequency = config->fs_frequency;
+ cmd.frame_sync_polarity = SSP_FS_ACTIVE_HIGH;
+ cmd.data_polarity = 1;
+ cmd.frame_sync_width = config->fs_width;
+ cmd.ssp_protocol = config->ssp_protocol;
+ cmd.start_delay = config->start_delay;
+ cmd.reserved1 = cmd.reserved2 = 0xFF;
+
+ return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+ SST_TASK_SBA, 0, &cmd,
+ sizeof(cmd.header) + cmd.header.length);
+}
+
+static int sst_set_be_modules(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ int ret = 0;
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct sst_data *drv = snd_soc_component_get_drvdata(c);
+
+ dev_dbg(c->dev, "Enter: widget=%s\n", w->name);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ ret = sst_send_slot_map(drv);
+ if (ret)
+ return ret;
+ ret = sst_send_pipe_module_params(w, k);
+ }
+ return ret;
+}
+
+static int sst_set_media_path(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ int ret = 0;
+ struct sst_cmd_set_media_path cmd;
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct sst_data *drv = snd_soc_component_get_drvdata(c);
+ struct sst_ids *ids = w->priv;
+
+ dev_dbg(c->dev, "widget=%s\n", w->name);
+ dev_dbg(c->dev, "task=%u, location=%#x\n",
+ ids->task_id, ids->location_id);
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ cmd.switch_state = SST_PATH_ON;
+ else
+ cmd.switch_state = SST_PATH_OFF;
+
+ SST_FILL_DESTINATION(2, cmd.header.dst,
+ ids->location_id, SST_DEFAULT_MODULE_ID);
+
+ /* MMX_SET_MEDIA_PATH == SBA_SET_MEDIA_PATH */
+ cmd.header.command_id = MMX_SET_MEDIA_PATH;
+ cmd.header.length = sizeof(struct sst_cmd_set_media_path)
+ - sizeof(struct sst_dsp_header);
+
+ ret = sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+ ids->task_id, 0, &cmd,
+ sizeof(cmd.header) + cmd.header.length);
+ if (ret)
+ return ret;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ ret = sst_send_pipe_module_params(w, k);
+ return ret;
+}
+
+static int sst_set_media_loop(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ int ret = 0;
+ struct sst_cmd_sba_set_media_loop_map cmd;
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct sst_data *drv = snd_soc_component_get_drvdata(c);
+ struct sst_ids *ids = w->priv;
+
+ dev_dbg(c->dev, "Enter:widget=%s\n", w->name);
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ cmd.switch_state = SST_SWITCH_ON;
+ else
+ cmd.switch_state = SST_SWITCH_OFF;
+
+ SST_FILL_DESTINATION(2, cmd.header.dst,
+ ids->location_id, SST_DEFAULT_MODULE_ID);
+
+ cmd.header.command_id = SBA_SET_MEDIA_LOOP_MAP;
+ cmd.header.length = sizeof(struct sst_cmd_sba_set_media_loop_map)
+ - sizeof(struct sst_dsp_header);
+ cmd.param.part.cfg.rate = 2; /* 48khz */
+
+ cmd.param.part.cfg.format = ids->format; /* stereo/Mono */
+ cmd.param.part.cfg.s_length = 1; /* 24bit left justified */
+ cmd.map = 0; /* Algo sequence: Gain - DRP - FIR - IIR */
+
+ ret = sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+ SST_TASK_SBA, 0, &cmd,
+ sizeof(cmd.header) + cmd.header.length);
+ if (ret)
+ return ret;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ ret = sst_send_pipe_module_params(w, k);
+ return ret;
+}
+
+static const struct snd_soc_dapm_widget sst_dapm_widgets[] = {
+ SST_AIF_IN("codec_in0", sst_set_be_modules),
+ SST_AIF_IN("codec_in1", sst_set_be_modules),
+ SST_AIF_OUT("codec_out0", sst_set_be_modules),
+ SST_AIF_OUT("codec_out1", sst_set_be_modules),
+
+ /* Media Paths */
+ /* MediaX IN paths are set via ALLOC, so no SET_MEDIA_PATH command */
+ SST_PATH_INPUT("media0_in", SST_TASK_MMX, SST_SWM_IN_MEDIA0, sst_generic_modules_event),
+ SST_PATH_INPUT("media1_in", SST_TASK_MMX, SST_SWM_IN_MEDIA1, NULL),
+ SST_PATH_INPUT("media2_in", SST_TASK_MMX, SST_SWM_IN_MEDIA2, sst_set_media_path),
+ SST_PATH_INPUT("media3_in", SST_TASK_MMX, SST_SWM_IN_MEDIA3, NULL),
+ SST_PATH_OUTPUT("media0_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA0, sst_set_media_path),
+ SST_PATH_OUTPUT("media1_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA1, sst_set_media_path),
+
+ /* SBA PCM Paths */
+ SST_PATH_INPUT("pcm0_in", SST_TASK_SBA, SST_SWM_IN_PCM0, sst_set_media_path),
+ SST_PATH_INPUT("pcm1_in", SST_TASK_SBA, SST_SWM_IN_PCM1, sst_set_media_path),
+ SST_PATH_OUTPUT("pcm0_out", SST_TASK_SBA, SST_SWM_OUT_PCM0, sst_set_media_path),
+ SST_PATH_OUTPUT("pcm1_out", SST_TASK_SBA, SST_SWM_OUT_PCM1, sst_set_media_path),
+ SST_PATH_OUTPUT("pcm2_out", SST_TASK_SBA, SST_SWM_OUT_PCM2, sst_set_media_path),
+
+ /* SBA Loops */
+ SST_PATH_INPUT("sprot_loop_in", SST_TASK_SBA, SST_SWM_IN_SPROT_LOOP, NULL),
+ SST_PATH_INPUT("media_loop1_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP1, NULL),
+ SST_PATH_INPUT("media_loop2_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP2, NULL),
+ SST_PATH_MEDIA_LOOP_OUTPUT("sprot_loop_out", SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, SST_FMT_MONO, sst_set_media_loop),
+ SST_PATH_MEDIA_LOOP_OUTPUT("media_loop1_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, SST_FMT_MONO, sst_set_media_loop),
+ SST_PATH_MEDIA_LOOP_OUTPUT("media_loop2_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, SST_FMT_STEREO, sst_set_media_loop),
+
+ /* Media Mixers */
+ SST_SWM_MIXER("media0_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA0,
+ sst_mix_media0_controls, sst_swm_mixer_event),
+ SST_SWM_MIXER("media1_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA1,
+ sst_mix_media1_controls, sst_swm_mixer_event),
+
+ /* SBA PCM mixers */
+ SST_SWM_MIXER("pcm0_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM0,
+ sst_mix_pcm0_controls, sst_swm_mixer_event),
+ SST_SWM_MIXER("pcm1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM1,
+ sst_mix_pcm1_controls, sst_swm_mixer_event),
+ SST_SWM_MIXER("pcm2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM2,
+ sst_mix_pcm2_controls, sst_swm_mixer_event),
+
+ /* SBA Loop mixers */
+ SST_SWM_MIXER("sprot_loop_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP,
+ sst_mix_sprot_l0_controls, sst_swm_mixer_event),
+ SST_SWM_MIXER("media_loop1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1,
+ sst_mix_media_l1_controls, sst_swm_mixer_event),
+ SST_SWM_MIXER("media_loop2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2,
+ sst_mix_media_l2_controls, sst_swm_mixer_event),
+
+ /* SBA Backend mixers */
+ SST_SWM_MIXER("codec_out0 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC0,
+ sst_mix_codec0_controls, sst_swm_mixer_event),
+ SST_SWM_MIXER("codec_out1 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC1,
+ sst_mix_codec1_controls, sst_swm_mixer_event),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ {"media0_in", NULL, "Compress Playback"},
+ {"media1_in", NULL, "Headset Playback"},
+ {"media2_in", NULL, "pcm0_out"},
+
+ {"media0_out mix 0", "media0_in Switch", "media0_in"},
+ {"media0_out mix 0", "media1_in Switch", "media1_in"},
+ {"media0_out mix 0", "media2_in Switch", "media2_in"},
+ {"media0_out mix 0", "media3_in Switch", "media3_in"},
+ {"media1_out mix 0", "media0_in Switch", "media0_in"},
+ {"media1_out mix 0", "media1_in Switch", "media1_in"},
+ {"media1_out mix 0", "media2_in Switch", "media2_in"},
+ {"media1_out mix 0", "media3_in Switch", "media3_in"},
+
+ {"media0_out", NULL, "media0_out mix 0"},
+ {"media1_out", NULL, "media1_out mix 0"},
+ {"pcm0_in", NULL, "media0_out"},
+ {"pcm1_in", NULL, "media1_out"},
+
+ {"Headset Capture", NULL, "pcm1_out"},
+ {"Headset Capture", NULL, "pcm2_out"},
+ {"pcm0_out", NULL, "pcm0_out mix 0"},
+ SST_SBA_MIXER_GRAPH_MAP("pcm0_out mix 0"),
+ {"pcm1_out", NULL, "pcm1_out mix 0"},
+ SST_SBA_MIXER_GRAPH_MAP("pcm1_out mix 0"),
+ {"pcm2_out", NULL, "pcm2_out mix 0"},
+ SST_SBA_MIXER_GRAPH_MAP("pcm2_out mix 0"),
+
+ {"media_loop1_in", NULL, "media_loop1_out"},
+ {"media_loop1_out", NULL, "media_loop1_out mix 0"},
+ SST_SBA_MIXER_GRAPH_MAP("media_loop1_out mix 0"),
+ {"media_loop2_in", NULL, "media_loop2_out"},
+ {"media_loop2_out", NULL, "media_loop2_out mix 0"},
+ SST_SBA_MIXER_GRAPH_MAP("media_loop2_out mix 0"),
+ {"sprot_loop_in", NULL, "sprot_loop_out"},
+ {"sprot_loop_out", NULL, "sprot_loop_out mix 0"},
+ SST_SBA_MIXER_GRAPH_MAP("sprot_loop_out mix 0"),
+
+ {"codec_out0", NULL, "codec_out0 mix 0"},
+ SST_SBA_MIXER_GRAPH_MAP("codec_out0 mix 0"),
+ {"codec_out1", NULL, "codec_out1 mix 0"},
+ SST_SBA_MIXER_GRAPH_MAP("codec_out1 mix 0"),
+
+};
+static const char * const slot_names[] = {
+ "none",
+ "slot 0", "slot 1", "slot 2", "slot 3",
+ "slot 4", "slot 5", "slot 6", "slot 7", /* not supported by FW */
+};
+
+static const char * const channel_names[] = {
+ "none",
+ "codec_out0_0", "codec_out0_1", "codec_out1_0", "codec_out1_1",
+ "codec_out2_0", "codec_out2_1", "codec_out3_0", "codec_out3_1", /* not supported by FW */
+};
+
+#define SST_INTERLEAVER(xpname, slot_name, slotno) \
+ SST_SSP_SLOT_CTL(xpname, "tx interleaver", slot_name, slotno, true, \
+ channel_names, sst_slot_get, sst_slot_put)
+
+#define SST_DEINTERLEAVER(xpname, channel_name, channel_no) \
+ SST_SSP_SLOT_CTL(xpname, "rx deinterleaver", channel_name, channel_no, false, \
+ slot_names, sst_slot_get, sst_slot_put)
+
+static const struct snd_kcontrol_new sst_slot_controls[] = {
+ SST_INTERLEAVER("codec_out", "slot 0", 0),
+ SST_INTERLEAVER("codec_out", "slot 1", 1),
+ SST_INTERLEAVER("codec_out", "slot 2", 2),
+ SST_INTERLEAVER("codec_out", "slot 3", 3),
+ SST_DEINTERLEAVER("codec_in", "codec_in0_0", 0),
+ SST_DEINTERLEAVER("codec_in", "codec_in0_1", 1),
+ SST_DEINTERLEAVER("codec_in", "codec_in1_0", 2),
+ SST_DEINTERLEAVER("codec_in", "codec_in1_1", 3),
+};
+
+/* Gain helper with min/max set */
+#define SST_GAIN(name, path_id, task_id, instance, gain_var) \
+ SST_GAIN_KCONTROLS(name, "Gain", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE, \
+ SST_GAIN_TC_MIN, SST_GAIN_TC_MAX, \
+ sst_gain_get, sst_gain_put, \
+ SST_MODULE_ID_GAIN_CELL, path_id, instance, task_id, \
+ sst_gain_tlv_common, gain_var)
+
+#define SST_VOLUME(name, path_id, task_id, instance, gain_var) \
+ SST_GAIN_KCONTROLS(name, "Volume", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE, \
+ SST_GAIN_TC_MIN, SST_GAIN_TC_MAX, \
+ sst_gain_get, sst_gain_put, \
+ SST_MODULE_ID_VOLUME, path_id, instance, task_id, \
+ sst_gain_tlv_common, gain_var)
+
+static struct sst_gain_value sst_gains[];
+
+static const struct snd_kcontrol_new sst_gain_controls[] = {
+ SST_GAIN("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[0]),
+ SST_GAIN("media1_in", SST_PATH_INDEX_MEDIA1_IN, SST_TASK_MMX, 0, &sst_gains[1]),
+ SST_GAIN("media2_in", SST_PATH_INDEX_MEDIA2_IN, SST_TASK_MMX, 0, &sst_gains[2]),
+ SST_GAIN("media3_in", SST_PATH_INDEX_MEDIA3_IN, SST_TASK_MMX, 0, &sst_gains[3]),
+
+ SST_GAIN("pcm0_in", SST_PATH_INDEX_PCM0_IN, SST_TASK_SBA, 0, &sst_gains[4]),
+ SST_GAIN("pcm1_in", SST_PATH_INDEX_PCM1_IN, SST_TASK_SBA, 0, &sst_gains[5]),
+ SST_GAIN("pcm1_out", SST_PATH_INDEX_PCM1_OUT, SST_TASK_SBA, 0, &sst_gains[6]),
+ SST_GAIN("pcm2_out", SST_PATH_INDEX_PCM2_OUT, SST_TASK_SBA, 0, &sst_gains[7]),
+
+ SST_GAIN("codec_in0", SST_PATH_INDEX_CODEC_IN0, SST_TASK_SBA, 0, &sst_gains[8]),
+ SST_GAIN("codec_in1", SST_PATH_INDEX_CODEC_IN1, SST_TASK_SBA, 0, &sst_gains[9]),
+ SST_GAIN("codec_out0", SST_PATH_INDEX_CODEC_OUT0, SST_TASK_SBA, 0, &sst_gains[10]),
+ SST_GAIN("codec_out1", SST_PATH_INDEX_CODEC_OUT1, SST_TASK_SBA, 0, &sst_gains[11]),
+ SST_GAIN("media_loop1_out", SST_PATH_INDEX_MEDIA_LOOP1_OUT, SST_TASK_SBA, 0, &sst_gains[12]),
+ SST_GAIN("media_loop2_out", SST_PATH_INDEX_MEDIA_LOOP2_OUT, SST_TASK_SBA, 0, &sst_gains[13]),
+ SST_GAIN("sprot_loop_out", SST_PATH_INDEX_SPROT_LOOP_OUT, SST_TASK_SBA, 0, &sst_gains[14]),
+ SST_VOLUME("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[15]),
+};
+
+#define SST_GAIN_NUM_CONTROLS 3
+/* the SST_GAIN macro above will create three alsa controls for each
+ * instance invoked, gain, mute and ramp duration, which use the same gain
+ * cell sst_gain to keep track of data
+ * To calculate number of gain cell instances we need to device by 3 in
+ * below caulcation for gain cell memory.
+ * This gets rid of static number and issues while adding new controls
+ */
+static struct sst_gain_value sst_gains[ARRAY_SIZE(sst_gain_controls)/SST_GAIN_NUM_CONTROLS];
+
+static const struct snd_kcontrol_new sst_algo_controls[] = {
+ SST_ALGO_KCONTROL_BYTES("media_loop1_out", "fir", 272, SST_MODULE_ID_FIR_24,
+ SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR),
+ SST_ALGO_KCONTROL_BYTES("media_loop1_out", "iir", 300, SST_MODULE_ID_IIR_24,
+ SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_IIR),
+ SST_ALGO_KCONTROL_BYTES("media_loop1_out", "mdrp", 286, SST_MODULE_ID_MDRP,
+ SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_SET_MDRP),
+ SST_ALGO_KCONTROL_BYTES("media_loop2_out", "fir", 272, SST_MODULE_ID_FIR_24,
+ SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR),
+ SST_ALGO_KCONTROL_BYTES("media_loop2_out", "iir", 300, SST_MODULE_ID_IIR_24,
+ SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_VB_SET_IIR),
+ SST_ALGO_KCONTROL_BYTES("media_loop2_out", "mdrp", 286, SST_MODULE_ID_MDRP,
+ SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_SET_MDRP),
+ SST_ALGO_KCONTROL_BYTES("sprot_loop_out", "lpro", 192, SST_MODULE_ID_SPROT,
+ SST_PATH_INDEX_SPROT_LOOP_OUT, 0, SST_TASK_SBA, SBA_VB_LPRO),
+ SST_ALGO_KCONTROL_BYTES("codec_in0", "dcr", 52, SST_MODULE_ID_FILT_DCR,
+ SST_PATH_INDEX_CODEC_IN0, 0, SST_TASK_SBA, SBA_VB_SET_IIR),
+ SST_ALGO_KCONTROL_BYTES("codec_in1", "dcr", 52, SST_MODULE_ID_FILT_DCR,
+ SST_PATH_INDEX_CODEC_IN1, 0, SST_TASK_SBA, SBA_VB_SET_IIR),
+
+};
+
+static int sst_algo_control_init(struct device *dev)
+{
+ int i = 0;
+ struct sst_algo_control *bc;
+ /*allocate space to cache the algo parameters in the driver*/
+ for (i = 0; i < ARRAY_SIZE(sst_algo_controls); i++) {
+ bc = (struct sst_algo_control *)sst_algo_controls[i].private_value;
+ bc->params = devm_kzalloc(dev, bc->max, GFP_KERNEL);
+ if (bc->params == NULL)
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static bool is_sst_dapm_widget(struct snd_soc_dapm_widget *w)
+{
+ switch (w->id) {
+ case snd_soc_dapm_pga:
+ case snd_soc_dapm_aif_in:
+ case snd_soc_dapm_aif_out:
+ case snd_soc_dapm_input:
+ case snd_soc_dapm_output:
+ case snd_soc_dapm_mixer:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * sst_send_pipe_gains - send gains for the front-end DAIs
+ *
+ * The gains in the pipes connected to the front-ends are muted/unmuted
+ * automatically via the digital_mute() DAPM callback. This function sends the
+ * gains for the front-end pipes.
+ */
+int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute)
+{
+ struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_path *p = NULL;
+
+ dev_dbg(dai->dev, "enter, dai-name=%s dir=%d\n", dai->name, stream);
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dev_dbg(dai->dev, "Stream name=%s\n",
+ dai->playback_widget->name);
+ w = dai->playback_widget;
+ list_for_each_entry(p, &w->sinks, list_source) {
+ if (p->connected && !p->connected(w, p->sink))
+ continue;
+
+ if (p->connect && p->sink->power &&
+ is_sst_dapm_widget(p->sink)) {
+ struct sst_ids *ids = p->sink->priv;
+
+ dev_dbg(dai->dev, "send gains for widget=%s\n",
+ p->sink->name);
+ mutex_lock(&drv->lock);
+ sst_set_pipe_gain(ids, drv, mute);
+ mutex_unlock(&drv->lock);
+ }
+ }
+ } else {
+ dev_dbg(dai->dev, "Stream name=%s\n",
+ dai->capture_widget->name);
+ w = dai->capture_widget;
+ list_for_each_entry(p, &w->sources, list_sink) {
+ if (p->connected && !p->connected(w, p->sink))
+ continue;
+
+ if (p->connect && p->source->power &&
+ is_sst_dapm_widget(p->source)) {
+ struct sst_ids *ids = p->source->priv;
+
+ dev_dbg(dai->dev, "send gain for widget=%s\n",
+ p->source->name);
+ mutex_lock(&drv->lock);
+ sst_set_pipe_gain(ids, drv, mute);
+ mutex_unlock(&drv->lock);
+ }
+ }
+ }
+ return 0;
+}
+
+/**
+ * sst_fill_module_list - populate the list of modules/gains for a pipe
+ *
+ *
+ * Fills the widget pointer in the kcontrol private data, and also fills the
+ * kcontrol pointer in the widget private data.
+ *
+ * Widget pointer is used to send the algo/gain in the .put() handler if the
+ * widget is powerd on.
+ *
+ * Kcontrol pointer is used to send the algo/gain in the widget power ON/OFF
+ * event handler. Each widget (pipe) has multiple algos stored in the algo_list.
+ */
+static int sst_fill_module_list(struct snd_kcontrol *kctl,
+ struct snd_soc_dapm_widget *w, int type)
+{
+ struct sst_module *module = NULL;
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct sst_ids *ids = w->priv;
+ int ret = 0;
+
+ module = devm_kzalloc(c->dev, sizeof(*module), GFP_KERNEL);
+ if (!module)
+ return -ENOMEM;
+
+ if (type == SST_MODULE_GAIN) {
+ struct sst_gain_mixer_control *mc = (void *)kctl->private_value;
+
+ mc->w = w;
+ module->kctl = kctl;
+ list_add_tail(&module->node, &ids->gain_list);
+ } else if (type == SST_MODULE_ALGO) {
+ struct sst_algo_control *bc = (void *)kctl->private_value;
+
+ bc->w = w;
+ module->kctl = kctl;
+ list_add_tail(&module->node, &ids->algo_list);
+ } else {
+ dev_err(c->dev, "invoked for unknown type %d module %s",
+ type, kctl->id.name);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/**
+ * sst_fill_widget_module_info - fill list of gains/algos for the pipe
+ * @widget: pipe modelled as a DAPM widget
+ *
+ * Fill the list of gains/algos for the widget by looking at all the card
+ * controls and comparing the name of the widget with the first part of control
+ * name. First part of control name contains the pipe name (widget name).
+ */
+static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w,
+ struct snd_soc_platform *platform)
+{
+ struct snd_kcontrol *kctl;
+ int index, ret = 0;
+ struct snd_card *card = platform->component.card->snd_card;
+ char *idx;
+
+ down_read(&card->controls_rwsem);
+
+ list_for_each_entry(kctl, &card->controls, list) {
+ idx = strstr(kctl->id.name, " ");
+ if (idx == NULL)
+ continue;
+ index = strlen(kctl->id.name) - strlen(idx);
+
+ if (strstr(kctl->id.name, "Volume") &&
+ !strncmp(kctl->id.name, w->name, index))
+ ret = sst_fill_module_list(kctl, w, SST_MODULE_GAIN);
+
+ else if (strstr(kctl->id.name, "params") &&
+ !strncmp(kctl->id.name, w->name, index))
+ ret = sst_fill_module_list(kctl, w, SST_MODULE_ALGO);
+
+ else if (strstr(kctl->id.name, "Switch") &&
+ !strncmp(kctl->id.name, w->name, index) &&
+ strstr(kctl->id.name, "Gain")) {
+ struct sst_gain_mixer_control *mc =
+ (void *)kctl->private_value;
+
+ mc->w = w;
+
+ } else if (strstr(kctl->id.name, "interleaver") &&
+ !strncmp(kctl->id.name, w->name, index)) {
+ struct sst_enum *e = (void *)kctl->private_value;
+
+ e->w = w;
+
+ } else if (strstr(kctl->id.name, "deinterleaver") &&
+ !strncmp(kctl->id.name, w->name, index)) {
+
+ struct sst_enum *e = (void *)kctl->private_value;
+
+ e->w = w;
+ }
+
+ if (ret < 0) {
+ up_read(&card->controls_rwsem);
+ return ret;
+ }
+ }
+
+ up_read(&card->controls_rwsem);
+ return 0;
+}
+
+/**
+ * sst_fill_linked_widgets - fill the parent pointer for the linked widget
+ */
+static void sst_fill_linked_widgets(struct snd_soc_platform *platform,
+ struct sst_ids *ids)
+{
+ struct snd_soc_dapm_widget *w;
+ unsigned int len = strlen(ids->parent_wname);
+
+ list_for_each_entry(w, &platform->component.card->widgets, list) {
+ if (!strncmp(ids->parent_wname, w->name, len)) {
+ ids->parent_w = w;
+ break;
+ }
+ }
+}
+
+/**
+ * sst_map_modules_to_pipe - fill algo/gains list for all pipes
+ */
+static int sst_map_modules_to_pipe(struct snd_soc_platform *platform)
+{
+ struct snd_soc_dapm_widget *w;
+ int ret = 0;
+
+ list_for_each_entry(w, &platform->component.card->widgets, list) {
+ if (is_sst_dapm_widget(w) && (w->priv)) {
+ struct sst_ids *ids = w->priv;
+
+ dev_dbg(platform->dev, "widget type=%d name=%s\n",
+ w->id, w->name);
+ INIT_LIST_HEAD(&ids->algo_list);
+ INIT_LIST_HEAD(&ids->gain_list);
+ ret = sst_fill_widget_module_info(w, platform);
+
+ if (ret < 0)
+ return ret;
+
+ /* fill linked widgets */
+ if (ids->parent_wname != NULL)
+ sst_fill_linked_widgets(platform, ids);
+ }
+ }
+ return 0;
+}
+
+int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform)
+{
+ int i, ret = 0;
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(&platform->component);
+ struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
+ unsigned int gains = ARRAY_SIZE(sst_gain_controls)/3;
+
+ drv->byte_stream = devm_kzalloc(platform->dev,
+ SST_MAX_BIN_BYTES, GFP_KERNEL);
+ if (!drv->byte_stream)
+ return -ENOMEM;
+
+ snd_soc_dapm_new_controls(dapm, sst_dapm_widgets,
+ ARRAY_SIZE(sst_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm, intercon,
+ ARRAY_SIZE(intercon));
+ snd_soc_dapm_new_widgets(dapm->card);
+
+ for (i = 0; i < gains; i++) {
+ sst_gains[i].mute = SST_GAIN_MUTE_DEFAULT;
+ sst_gains[i].l_gain = SST_GAIN_VOLUME_DEFAULT;
+ sst_gains[i].r_gain = SST_GAIN_VOLUME_DEFAULT;
+ sst_gains[i].ramp_duration = SST_GAIN_RAMP_DURATION_DEFAULT;
+ }
+
+ ret = snd_soc_add_platform_controls(platform, sst_gain_controls,
+ ARRAY_SIZE(sst_gain_controls));
+ if (ret)
+ return ret;
+
+ /* Initialize algo control params */
+ ret = sst_algo_control_init(platform->dev);
+ if (ret)
+ return ret;
+ ret = snd_soc_add_platform_controls(platform, sst_algo_controls,
+ ARRAY_SIZE(sst_algo_controls));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_add_platform_controls(platform, sst_slot_controls,
+ ARRAY_SIZE(sst_slot_controls));
+ if (ret)
+ return ret;
+
+ ret = sst_map_modules_to_pipe(platform);
+
+ return ret;
+}
--- /dev/null
+/*
+ * sst-atom-controls.h - Intel MID Platform driver header file
+ *
+ * Copyright (C) 2013-14 Intel Corp
+ * Author: Ramesh Babu <ramesh.babu.koul@intel.com>
+ * Omair M Abdullah <omair.m.abdullah@intel.com>
+ * Samreen Nilofer <samreen.nilofer@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ */
+
+#ifndef __SST_ATOM_CONTROLS_H__
+#define __SST_ATOM_CONTROLS_H__
+
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+enum {
+ MERR_DPCM_AUDIO = 0,
+ MERR_DPCM_COMPR,
+};
+
+/* define a bit for each mixer input */
+#define SST_MIX_IP(x) (x)
+
+#define SST_IP_CODEC0 SST_MIX_IP(2)
+#define SST_IP_CODEC1 SST_MIX_IP(3)
+#define SST_IP_LOOP0 SST_MIX_IP(4)
+#define SST_IP_LOOP1 SST_MIX_IP(5)
+#define SST_IP_LOOP2 SST_MIX_IP(6)
+#define SST_IP_PROBE SST_MIX_IP(7)
+#define SST_IP_VOIP SST_MIX_IP(12)
+#define SST_IP_PCM0 SST_MIX_IP(13)
+#define SST_IP_PCM1 SST_MIX_IP(14)
+#define SST_IP_MEDIA0 SST_MIX_IP(17)
+#define SST_IP_MEDIA1 SST_MIX_IP(18)
+#define SST_IP_MEDIA2 SST_MIX_IP(19)
+#define SST_IP_MEDIA3 SST_MIX_IP(20)
+
+#define SST_IP_LAST SST_IP_MEDIA3
+
+#define SST_SWM_INPUT_COUNT (SST_IP_LAST + 1)
+#define SST_CMD_SWM_MAX_INPUTS 6
+
+#define SST_PATH_ID_SHIFT 8
+#define SST_DEFAULT_LOCATION_ID 0xFFFF
+#define SST_DEFAULT_CELL_NBR 0xFF
+#define SST_DEFAULT_MODULE_ID 0xFFFF
+
+/*
+ * Audio DSP Path Ids. Specified by the audio DSP FW
+ */
+enum sst_path_index {
+ SST_PATH_INDEX_CODEC_OUT0 = (0x02 << SST_PATH_ID_SHIFT),
+ SST_PATH_INDEX_CODEC_OUT1 = (0x03 << SST_PATH_ID_SHIFT),
+
+ SST_PATH_INDEX_SPROT_LOOP_OUT = (0x04 << SST_PATH_ID_SHIFT),
+ SST_PATH_INDEX_MEDIA_LOOP1_OUT = (0x05 << SST_PATH_ID_SHIFT),
+ SST_PATH_INDEX_MEDIA_LOOP2_OUT = (0x06 << SST_PATH_ID_SHIFT),
+
+ SST_PATH_INDEX_VOIP_OUT = (0x0C << SST_PATH_ID_SHIFT),
+ SST_PATH_INDEX_PCM0_OUT = (0x0D << SST_PATH_ID_SHIFT),
+ SST_PATH_INDEX_PCM1_OUT = (0x0E << SST_PATH_ID_SHIFT),
+ SST_PATH_INDEX_PCM2_OUT = (0x0F << SST_PATH_ID_SHIFT),
+
+ SST_PATH_INDEX_MEDIA0_OUT = (0x12 << SST_PATH_ID_SHIFT),
+ SST_PATH_INDEX_MEDIA1_OUT = (0x13 << SST_PATH_ID_SHIFT),
+
+
+ /* Start of input paths */
+ SST_PATH_INDEX_CODEC_IN0 = (0x82 << SST_PATH_ID_SHIFT),
+ SST_PATH_INDEX_CODEC_IN1 = (0x83 << SST_PATH_ID_SHIFT),
+
+ SST_PATH_INDEX_SPROT_LOOP_IN = (0x84 << SST_PATH_ID_SHIFT),
+ SST_PATH_INDEX_MEDIA_LOOP1_IN = (0x85 << SST_PATH_ID_SHIFT),
+ SST_PATH_INDEX_MEDIA_LOOP2_IN = (0x86 << SST_PATH_ID_SHIFT),
+
+ SST_PATH_INDEX_VOIP_IN = (0x8C << SST_PATH_ID_SHIFT),
+
+ SST_PATH_INDEX_PCM0_IN = (0x8D << SST_PATH_ID_SHIFT),
+ SST_PATH_INDEX_PCM1_IN = (0x8E << SST_PATH_ID_SHIFT),
+
+ SST_PATH_INDEX_MEDIA0_IN = (0x8F << SST_PATH_ID_SHIFT),
+ SST_PATH_INDEX_MEDIA1_IN = (0x90 << SST_PATH_ID_SHIFT),
+ SST_PATH_INDEX_MEDIA2_IN = (0x91 << SST_PATH_ID_SHIFT),
+
+ SST_PATH_INDEX_MEDIA3_IN = (0x9C << SST_PATH_ID_SHIFT),
+
+ SST_PATH_INDEX_RESERVED = (0xFF << SST_PATH_ID_SHIFT),
+};
+
+/*
+ * path IDs
+ */
+enum sst_swm_inputs {
+ SST_SWM_IN_CODEC0 = (SST_PATH_INDEX_CODEC_IN0 | SST_DEFAULT_CELL_NBR),
+ SST_SWM_IN_CODEC1 = (SST_PATH_INDEX_CODEC_IN1 | SST_DEFAULT_CELL_NBR),
+ SST_SWM_IN_SPROT_LOOP = (SST_PATH_INDEX_SPROT_LOOP_IN | SST_DEFAULT_CELL_NBR),
+ SST_SWM_IN_MEDIA_LOOP1 = (SST_PATH_INDEX_MEDIA_LOOP1_IN | SST_DEFAULT_CELL_NBR),
+ SST_SWM_IN_MEDIA_LOOP2 = (SST_PATH_INDEX_MEDIA_LOOP2_IN | SST_DEFAULT_CELL_NBR),
+ SST_SWM_IN_VOIP = (SST_PATH_INDEX_VOIP_IN | SST_DEFAULT_CELL_NBR),
+ SST_SWM_IN_PCM0 = (SST_PATH_INDEX_PCM0_IN | SST_DEFAULT_CELL_NBR),
+ SST_SWM_IN_PCM1 = (SST_PATH_INDEX_PCM1_IN | SST_DEFAULT_CELL_NBR),
+ SST_SWM_IN_MEDIA0 = (SST_PATH_INDEX_MEDIA0_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */
+ SST_SWM_IN_MEDIA1 = (SST_PATH_INDEX_MEDIA1_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */
+ SST_SWM_IN_MEDIA2 = (SST_PATH_INDEX_MEDIA2_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */
+ SST_SWM_IN_MEDIA3 = (SST_PATH_INDEX_MEDIA3_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */
+ SST_SWM_IN_END = (SST_PATH_INDEX_RESERVED | SST_DEFAULT_CELL_NBR)
+};
+
+/*
+ * path IDs
+ */
+enum sst_swm_outputs {
+ SST_SWM_OUT_CODEC0 = (SST_PATH_INDEX_CODEC_OUT0 | SST_DEFAULT_CELL_NBR),
+ SST_SWM_OUT_CODEC1 = (SST_PATH_INDEX_CODEC_OUT1 | SST_DEFAULT_CELL_NBR),
+ SST_SWM_OUT_SPROT_LOOP = (SST_PATH_INDEX_SPROT_LOOP_OUT | SST_DEFAULT_CELL_NBR),
+ SST_SWM_OUT_MEDIA_LOOP1 = (SST_PATH_INDEX_MEDIA_LOOP1_OUT | SST_DEFAULT_CELL_NBR),
+ SST_SWM_OUT_MEDIA_LOOP2 = (SST_PATH_INDEX_MEDIA_LOOP2_OUT | SST_DEFAULT_CELL_NBR),
+ SST_SWM_OUT_VOIP = (SST_PATH_INDEX_VOIP_OUT | SST_DEFAULT_CELL_NBR),
+ SST_SWM_OUT_PCM0 = (SST_PATH_INDEX_PCM0_OUT | SST_DEFAULT_CELL_NBR),
+ SST_SWM_OUT_PCM1 = (SST_PATH_INDEX_PCM1_OUT | SST_DEFAULT_CELL_NBR),
+ SST_SWM_OUT_PCM2 = (SST_PATH_INDEX_PCM2_OUT | SST_DEFAULT_CELL_NBR),
+ SST_SWM_OUT_MEDIA0 = (SST_PATH_INDEX_MEDIA0_OUT | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */
+ SST_SWM_OUT_MEDIA1 = (SST_PATH_INDEX_MEDIA1_OUT | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */
+ SST_SWM_OUT_END = (SST_PATH_INDEX_RESERVED | SST_DEFAULT_CELL_NBR),
+};
+
+enum sst_ipc_msg {
+ SST_IPC_IA_CMD = 1,
+ SST_IPC_IA_SET_PARAMS,
+ SST_IPC_IA_GET_PARAMS,
+};
+
+enum sst_cmd_type {
+ SST_CMD_BYTES_SET = 1,
+ SST_CMD_BYTES_GET = 2,
+};
+
+enum sst_task {
+ SST_TASK_SBA = 1,
+ SST_TASK_MMX = 3,
+};
+
+enum sst_type {
+ SST_TYPE_CMD = 1,
+ SST_TYPE_PARAMS,
+};
+
+enum sst_flag {
+ SST_FLAG_BLOCKED = 1,
+ SST_FLAG_NONBLOCK,
+};
+
+/*
+ * Enumeration for indexing the gain cells in VB_SET_GAIN DSP command
+ */
+enum sst_gain_index {
+ /* GAIN IDs for SB task start here */
+ SST_GAIN_INDEX_CODEC_OUT0,
+ SST_GAIN_INDEX_CODEC_OUT1,
+ SST_GAIN_INDEX_CODEC_IN0,
+ SST_GAIN_INDEX_CODEC_IN1,
+
+ SST_GAIN_INDEX_SPROT_LOOP_OUT,
+ SST_GAIN_INDEX_MEDIA_LOOP1_OUT,
+ SST_GAIN_INDEX_MEDIA_LOOP2_OUT,
+
+ SST_GAIN_INDEX_PCM0_IN_LEFT,
+ SST_GAIN_INDEX_PCM0_IN_RIGHT,
+
+ SST_GAIN_INDEX_PCM1_OUT_LEFT,
+ SST_GAIN_INDEX_PCM1_OUT_RIGHT,
+ SST_GAIN_INDEX_PCM1_IN_LEFT,
+ SST_GAIN_INDEX_PCM1_IN_RIGHT,
+ SST_GAIN_INDEX_PCM2_OUT_LEFT,
+
+ SST_GAIN_INDEX_PCM2_OUT_RIGHT,
+ SST_GAIN_INDEX_VOIP_OUT,
+ SST_GAIN_INDEX_VOIP_IN,
+
+ /* Gain IDs for MMX task start here */
+ SST_GAIN_INDEX_MEDIA0_IN_LEFT,
+ SST_GAIN_INDEX_MEDIA0_IN_RIGHT,
+ SST_GAIN_INDEX_MEDIA1_IN_LEFT,
+ SST_GAIN_INDEX_MEDIA1_IN_RIGHT,
+
+ SST_GAIN_INDEX_MEDIA2_IN_LEFT,
+ SST_GAIN_INDEX_MEDIA2_IN_RIGHT,
+
+ SST_GAIN_INDEX_GAIN_END
+};
+
+/*
+ * Audio DSP module IDs specified by FW spec
+ * TODO: Update with all modules
+ */
+enum sst_module_id {
+ SST_MODULE_ID_PCM = 0x0001,
+ SST_MODULE_ID_MP3 = 0x0002,
+ SST_MODULE_ID_MP24 = 0x0003,
+ SST_MODULE_ID_AAC = 0x0004,
+ SST_MODULE_ID_AACP = 0x0005,
+ SST_MODULE_ID_EAACP = 0x0006,
+ SST_MODULE_ID_WMA9 = 0x0007,
+ SST_MODULE_ID_WMA10 = 0x0008,
+ SST_MODULE_ID_WMA10P = 0x0009,
+ SST_MODULE_ID_RA = 0x000A,
+ SST_MODULE_ID_DDAC3 = 0x000B,
+ SST_MODULE_ID_TRUE_HD = 0x000C,
+ SST_MODULE_ID_HD_PLUS = 0x000D,
+
+ SST_MODULE_ID_SRC = 0x0064,
+ SST_MODULE_ID_DOWNMIX = 0x0066,
+ SST_MODULE_ID_GAIN_CELL = 0x0067,
+ SST_MODULE_ID_SPROT = 0x006D,
+ SST_MODULE_ID_BASS_BOOST = 0x006E,
+ SST_MODULE_ID_STEREO_WDNG = 0x006F,
+ SST_MODULE_ID_AV_REMOVAL = 0x0070,
+ SST_MODULE_ID_MIC_EQ = 0x0071,
+ SST_MODULE_ID_SPL = 0x0072,
+ SST_MODULE_ID_ALGO_VTSV = 0x0073,
+ SST_MODULE_ID_NR = 0x0076,
+ SST_MODULE_ID_BWX = 0x0077,
+ SST_MODULE_ID_DRP = 0x0078,
+ SST_MODULE_ID_MDRP = 0x0079,
+
+ SST_MODULE_ID_ANA = 0x007A,
+ SST_MODULE_ID_AEC = 0x007B,
+ SST_MODULE_ID_NR_SNS = 0x007C,
+ SST_MODULE_ID_SER = 0x007D,
+ SST_MODULE_ID_AGC = 0x007E,
+
+ SST_MODULE_ID_CNI = 0x007F,
+ SST_MODULE_ID_CONTEXT_ALGO_AWARE = 0x0080,
+ SST_MODULE_ID_FIR_24 = 0x0081,
+ SST_MODULE_ID_IIR_24 = 0x0082,
+
+ SST_MODULE_ID_ASRC = 0x0083,
+ SST_MODULE_ID_TONE_GEN = 0x0084,
+ SST_MODULE_ID_BMF = 0x0086,
+ SST_MODULE_ID_EDL = 0x0087,
+ SST_MODULE_ID_GLC = 0x0088,
+
+ SST_MODULE_ID_FIR_16 = 0x0089,
+ SST_MODULE_ID_IIR_16 = 0x008A,
+ SST_MODULE_ID_DNR = 0x008B,
+
+ SST_MODULE_ID_VIRTUALIZER = 0x008C,
+ SST_MODULE_ID_VISUALIZATION = 0x008D,
+ SST_MODULE_ID_LOUDNESS_OPTIMIZER = 0x008E,
+ SST_MODULE_ID_REVERBERATION = 0x008F,
+
+ SST_MODULE_ID_CNI_TX = 0x0090,
+ SST_MODULE_ID_REF_LINE = 0x0091,
+ SST_MODULE_ID_VOLUME = 0x0092,
+ SST_MODULE_ID_FILT_DCR = 0x0094,
+ SST_MODULE_ID_SLV = 0x009A,
+ SST_MODULE_ID_NLF = 0x009B,
+ SST_MODULE_ID_TNR = 0x009C,
+ SST_MODULE_ID_WNR = 0x009D,
+
+ SST_MODULE_ID_LOG = 0xFF00,
+
+ SST_MODULE_ID_TASK = 0xFFFF,
+};
+
+enum sst_cmd {
+ SBA_IDLE = 14,
+ SBA_VB_SET_SPEECH_PATH = 26,
+ MMX_SET_GAIN = 33,
+ SBA_VB_SET_GAIN = 33,
+ FBA_VB_RX_CNI = 35,
+ MMX_SET_GAIN_TIMECONST = 36,
+ SBA_VB_SET_TIMECONST = 36,
+ SBA_VB_START = 85,
+ SBA_SET_SWM = 114,
+ SBA_SET_MDRP = 116,
+ SBA_HW_SET_SSP = 117,
+ SBA_SET_MEDIA_LOOP_MAP = 118,
+ SBA_SET_MEDIA_PATH = 119,
+ MMX_SET_MEDIA_PATH = 119,
+ SBA_VB_LPRO = 126,
+ SBA_VB_SET_FIR = 128,
+ SBA_VB_SET_IIR = 129,
+ SBA_SET_SSP_SLOT_MAP = 130,
+};
+
+enum sst_dsp_switch {
+ SST_SWITCH_OFF = 0,
+ SST_SWITCH_ON = 3,
+};
+
+enum sst_path_switch {
+ SST_PATH_OFF = 0,
+ SST_PATH_ON = 1,
+};
+
+enum sst_swm_state {
+ SST_SWM_OFF = 0,
+ SST_SWM_ON = 3,
+};
+
+#define SST_FILL_LOCATION_IDS(dst, cell_idx, pipe_id) do { \
+ dst.location_id.p.cell_nbr_idx = (cell_idx); \
+ dst.location_id.p.path_id = (pipe_id); \
+ } while (0)
+#define SST_FILL_LOCATION_ID(dst, loc_id) (\
+ dst.location_id.f = (loc_id))
+#define SST_FILL_MODULE_ID(dst, mod_id) (\
+ dst.module_id = (mod_id))
+
+#define SST_FILL_DESTINATION1(dst, id) do { \
+ SST_FILL_LOCATION_ID(dst, (id) & 0xFFFF); \
+ SST_FILL_MODULE_ID(dst, ((id) & 0xFFFF0000) >> 16); \
+ } while (0)
+#define SST_FILL_DESTINATION2(dst, loc_id, mod_id) do { \
+ SST_FILL_LOCATION_ID(dst, loc_id); \
+ SST_FILL_MODULE_ID(dst, mod_id); \
+ } while (0)
+#define SST_FILL_DESTINATION3(dst, cell_idx, path_id, mod_id) do { \
+ SST_FILL_LOCATION_IDS(dst, cell_idx, path_id); \
+ SST_FILL_MODULE_ID(dst, mod_id); \
+ } while (0)
+
+#define SST_FILL_DESTINATION(level, dst, ...) \
+ SST_FILL_DESTINATION##level(dst, __VA_ARGS__)
+#define SST_FILL_DEFAULT_DESTINATION(dst) \
+ SST_FILL_DESTINATION(2, dst, SST_DEFAULT_LOCATION_ID, SST_DEFAULT_MODULE_ID)
+
+struct sst_destination_id {
+ union sst_location_id {
+ struct {
+ u8 cell_nbr_idx; /* module index */
+ u8 path_id; /* pipe_id */
+ } __packed p; /* part */
+ u16 f; /* full */
+ } __packed location_id;
+ u16 module_id;
+} __packed;
+struct sst_dsp_header {
+ struct sst_destination_id dst;
+ u16 command_id;
+ u16 length;
+} __packed;
+
+/*
+ *
+ * Common Commands
+ *
+ */
+struct sst_cmd_generic {
+ struct sst_dsp_header header;
+} __packed;
+
+struct swm_input_ids {
+ struct sst_destination_id input_id;
+} __packed;
+
+struct sst_cmd_set_swm {
+ struct sst_dsp_header header;
+ struct sst_destination_id output_id;
+ u16 switch_state;
+ u16 nb_inputs;
+ struct swm_input_ids input[SST_CMD_SWM_MAX_INPUTS];
+} __packed;
+
+struct sst_cmd_set_media_path {
+ struct sst_dsp_header header;
+ u16 switch_state;
+} __packed;
+
+struct pcm_cfg {
+ u8 s_length:2;
+ u8 rate:3;
+ u8 format:3;
+} __packed;
+
+struct sst_cmd_set_speech_path {
+ struct sst_dsp_header header;
+ u16 switch_state;
+ struct {
+ u16 rsvd:8;
+ struct pcm_cfg cfg;
+ } config;
+} __packed;
+
+struct gain_cell {
+ struct sst_destination_id dest;
+ s16 cell_gain_left;
+ s16 cell_gain_right;
+ u16 gain_time_constant;
+} __packed;
+
+#define NUM_GAIN_CELLS 1
+struct sst_cmd_set_gain_dual {
+ struct sst_dsp_header header;
+ u16 gain_cell_num;
+ struct gain_cell cell_gains[NUM_GAIN_CELLS];
+} __packed;
+struct sst_cmd_set_params {
+ struct sst_destination_id dst;
+ u16 command_id;
+ char params[0];
+} __packed;
+
+
+struct sst_cmd_sba_vb_start {
+ struct sst_dsp_header header;
+} __packed;
+
+union sba_media_loop_params {
+ struct {
+ u16 rsvd:8;
+ struct pcm_cfg cfg;
+ } part;
+ u16 full;
+} __packed;
+
+struct sst_cmd_sba_set_media_loop_map {
+ struct sst_dsp_header header;
+ u16 switch_state;
+ union sba_media_loop_params param;
+ u16 map;
+} __packed;
+
+struct sst_cmd_tone_stop {
+ struct sst_dsp_header header;
+ u16 switch_state;
+} __packed;
+
+enum sst_ssp_mode {
+ SSP_MODE_MASTER = 0,
+ SSP_MODE_SLAVE = 1,
+};
+
+enum sst_ssp_pcm_mode {
+ SSP_PCM_MODE_NORMAL = 0,
+ SSP_PCM_MODE_NETWORK = 1,
+};
+
+enum sst_ssp_duplex {
+ SSP_DUPLEX = 0,
+ SSP_RX = 1,
+ SSP_TX = 2,
+};
+
+enum sst_ssp_fs_frequency {
+ SSP_FS_8_KHZ = 0,
+ SSP_FS_16_KHZ = 1,
+ SSP_FS_44_1_KHZ = 2,
+ SSP_FS_48_KHZ = 3,
+};
+
+enum sst_ssp_fs_polarity {
+ SSP_FS_ACTIVE_LOW = 0,
+ SSP_FS_ACTIVE_HIGH = 1,
+};
+
+enum sst_ssp_protocol {
+ SSP_MODE_PCM = 0,
+ SSP_MODE_I2S = 1,
+};
+
+enum sst_ssp_port_id {
+ SSP_MODEM = 0,
+ SSP_BT = 1,
+ SSP_FM = 2,
+ SSP_CODEC = 3,
+};
+
+struct sst_cmd_sba_hw_set_ssp {
+ struct sst_dsp_header header;
+ u16 selection; /* 0:SSP0(def), 1:SSP1, 2:SSP2 */
+
+ u16 switch_state;
+
+ u16 nb_bits_per_slots:6; /* 0-32 bits, 24 (def) */
+ u16 nb_slots:4; /* 0-8: slots per frame */
+ u16 mode:3; /* 0:Master, 1: Slave */
+ u16 duplex:3;
+
+ u16 active_tx_slot_map:8; /* Bit map, 0:off, 1:on */
+ u16 reserved1:8;
+
+ u16 active_rx_slot_map:8; /* Bit map 0: Off, 1:On */
+ u16 reserved2:8;
+
+ u16 frame_sync_frequency;
+
+ u16 frame_sync_polarity:8;
+ u16 data_polarity:8;
+
+ u16 frame_sync_width; /* 1 to N clocks */
+ u16 ssp_protocol:8;
+ u16 start_delay:8; /* Start delay in terms of clock ticks */
+} __packed;
+
+#define SST_MAX_TDM_SLOTS 8
+
+struct sst_param_sba_ssp_slot_map {
+ struct sst_dsp_header header;
+
+ u16 param_id;
+ u16 param_len;
+ u16 ssp_index;
+
+ u8 rx_slot_map[SST_MAX_TDM_SLOTS];
+ u8 tx_slot_map[SST_MAX_TDM_SLOTS];
+} __packed;
+
+enum {
+ SST_PROBE_EXTRACTOR = 0,
+ SST_PROBE_INJECTOR = 1,
+};
+
+/**** widget defines *****/
+
+#define SST_MODULE_GAIN 1
+#define SST_MODULE_ALGO 2
+
+#define SST_FMT_MONO 0
+#define SST_FMT_STEREO 3
+
+/* physical SSP numbers */
+enum {
+ SST_SSP0 = 0,
+ SST_SSP1,
+ SST_SSP2,
+ SST_SSP_LAST = SST_SSP2,
+};
+
+#define SST_NUM_SSPS (SST_SSP_LAST + 1) /* physical SSPs */
+#define SST_MAX_SSP_MUX 2 /* single SSP muxed between pipes */
+#define SST_MAX_SSP_DOMAINS 2 /* domains present in each pipe */
+
+struct sst_module {
+ struct snd_kcontrol *kctl;
+ struct list_head node;
+};
+
+struct sst_ssp_config {
+ u8 ssp_id;
+ u8 bits_per_slot;
+ u8 slots;
+ u8 ssp_mode;
+ u8 pcm_mode;
+ u8 duplex;
+ u8 ssp_protocol;
+ u8 fs_frequency;
+ u8 active_slot_map;
+ u8 start_delay;
+ u16 fs_width;
+};
+
+struct sst_ssp_cfg {
+ const u8 ssp_number;
+ const int *mux_shift;
+ const int (*domain_shift)[SST_MAX_SSP_MUX];
+ const struct sst_ssp_config (*ssp_config)[SST_MAX_SSP_MUX][SST_MAX_SSP_DOMAINS];
+};
+
+struct sst_ids {
+ u16 location_id;
+ u16 module_id;
+ u8 task_id;
+ u8 format;
+ u8 reg;
+ const char *parent_wname;
+ struct snd_soc_dapm_widget *parent_w;
+ struct list_head algo_list;
+ struct list_head gain_list;
+ const struct sst_pcm_format *pcm_fmt;
+};
+
+
+#define SST_AIF_IN(wname, wevent) \
+{ .id = snd_soc_dapm_aif_in, .name = wname, .sname = NULL, \
+ .reg = SND_SOC_NOPM, .shift = 0, \
+ .on_val = 1, .off_val = 0, \
+ .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
+ .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \
+}
+
+#define SST_AIF_OUT(wname, wevent) \
+{ .id = snd_soc_dapm_aif_out, .name = wname, .sname = NULL, \
+ .reg = SND_SOC_NOPM, .shift = 0, \
+ .on_val = 1, .off_val = 0, \
+ .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
+ .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \
+}
+
+#define SST_INPUT(wname, wevent) \
+{ .id = snd_soc_dapm_input, .name = wname, .sname = NULL, \
+ .reg = SND_SOC_NOPM, .shift = 0, \
+ .on_val = 1, .off_val = 0, \
+ .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
+ .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \
+}
+
+#define SST_OUTPUT(wname, wevent) \
+{ .id = snd_soc_dapm_output, .name = wname, .sname = NULL, \
+ .reg = SND_SOC_NOPM, .shift = 0, \
+ .on_val = 1, .off_val = 0, \
+ .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
+ .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \
+}
+
+#define SST_DAPM_OUTPUT(wname, wloc_id, wtask_id, wformat, wevent) \
+{ .id = snd_soc_dapm_output, .name = wname, .sname = NULL, \
+ .reg = SND_SOC_NOPM, .shift = 0, \
+ .on_val = 1, .off_val = 0, \
+ .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
+ .priv = (void *)&(struct sst_ids) { .location_id = wloc_id, .task_id = wtask_id,\
+ .pcm_fmt = wformat, } \
+}
+
+#define SST_PATH(wname, wtask, wloc_id, wevent, wflags) \
+{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \
+ .kcontrol_news = NULL, .num_kcontrols = 0, \
+ .on_val = 1, .off_val = 0, \
+ .event = wevent, .event_flags = wflags, \
+ .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, } \
+}
+
+#define SST_LINKED_PATH(wname, wtask, wloc_id, linked_wname, wevent, wflags) \
+{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \
+ .kcontrol_news = NULL, .num_kcontrols = 0, \
+ .on_val = 1, .off_val = 0, \
+ .event = wevent, .event_flags = wflags, \
+ .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \
+ .parent_wname = linked_wname} \
+}
+
+#define SST_PATH_MEDIA_LOOP(wname, wtask, wloc_id, wformat, wevent, wflags) \
+{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \
+ .kcontrol_news = NULL, .num_kcontrols = 0, \
+ .event = wevent, .event_flags = wflags, \
+ .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \
+ .format = wformat,} \
+}
+
+/* output is triggered before input */
+#define SST_PATH_INPUT(name, task_id, loc_id, event) \
+ SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
+
+#define SST_PATH_LINKED_INPUT(name, task_id, loc_id, linked_wname, event) \
+ SST_LINKED_PATH(name, task_id, loc_id, linked_wname, event, \
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
+
+#define SST_PATH_OUTPUT(name, task_id, loc_id, event) \
+ SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
+
+#define SST_PATH_LINKED_OUTPUT(name, task_id, loc_id, linked_wname, event) \
+ SST_LINKED_PATH(name, task_id, loc_id, linked_wname, event, \
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
+
+#define SST_PATH_MEDIA_LOOP_OUTPUT(name, task_id, loc_id, format, event) \
+ SST_PATH_MEDIA_LOOP(name, task_id, loc_id, format, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
+
+
+#define SST_SWM_MIXER(wname, wreg, wtask, wloc_id, wcontrols, wevent) \
+{ .id = snd_soc_dapm_mixer, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \
+ .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols),\
+ .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD | \
+ SND_SOC_DAPM_POST_REG, \
+ .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \
+ .reg = wreg } \
+}
+
+enum sst_gain_kcontrol_type {
+ SST_GAIN_TLV,
+ SST_GAIN_MUTE,
+ SST_GAIN_RAMP_DURATION,
+};
+
+struct sst_gain_mixer_control {
+ bool stereo;
+ enum sst_gain_kcontrol_type type;
+ struct sst_gain_value *gain_val;
+ int max;
+ int min;
+ u16 instance_id;
+ u16 module_id;
+ u16 pipe_id;
+ u16 task_id;
+ char pname[44];
+ struct snd_soc_dapm_widget *w;
+};
+
+struct sst_gain_value {
+ u16 ramp_duration;
+ s16 l_gain;
+ s16 r_gain;
+ bool mute;
+};
+#define SST_GAIN_VOLUME_DEFAULT (-1440)
+#define SST_GAIN_RAMP_DURATION_DEFAULT 5 /* timeconstant */
+#define SST_GAIN_MUTE_DEFAULT true
+
+#define SST_GAIN_KCONTROL_TLV(xname, xhandler_get, xhandler_put, \
+ xmod, xpipe, xinstance, xtask, tlv_array, xgain_val, \
+ xmin, xmax, xpname) \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (tlv_array), \
+ .info = sst_gain_ctl_info,\
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = (unsigned long)&(struct sst_gain_mixer_control) \
+ { .stereo = true, .max = xmax, .min = xmin, .type = SST_GAIN_TLV, \
+ .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\
+ .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname}
+
+#define SST_GAIN_KCONTROL_INT(xname, xhandler_get, xhandler_put, \
+ xmod, xpipe, xinstance, xtask, xtype, xgain_val, \
+ xmin, xmax, xpname) \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = sst_gain_ctl_info, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = (unsigned long)&(struct sst_gain_mixer_control) \
+ { .stereo = false, .max = xmax, .min = xmin, .type = xtype, \
+ .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\
+ .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname}
+
+#define SST_GAIN_KCONTROL_BOOL(xname, xhandler_get, xhandler_put,\
+ xmod, xpipe, xinstance, xtask, xgain_val, xpname) \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_bool_ext, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = (unsigned long)&(struct sst_gain_mixer_control) \
+ { .stereo = false, .type = SST_GAIN_MUTE, \
+ .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\
+ .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname}
+#define SST_CONTROL_NAME(xpname, xmname, xinstance, xtype) \
+ xpname " " xmname " " #xinstance " " xtype
+
+#define SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, xtype, xsubmodule) \
+ xpname " " xmname " " #xinstance " " xtype " " xsubmodule
+
+/*
+ * 3 Controls for each Gain module
+ * e.g. - pcm0_in Gain 0 Volume
+ * - pcm0_in Gain 0 Ramp Delay
+ * - pcm0_in Gain 0 Switch
+ */
+#define SST_GAIN_KCONTROLS(xpname, xmname, xmin_gain, xmax_gain, xmin_tc, xmax_tc, \
+ xhandler_get, xhandler_put, \
+ xmod, xpipe, xinstance, xtask, tlv_array, xgain_val) \
+ { SST_GAIN_KCONTROL_INT(SST_CONTROL_NAME(xpname, xmname, xinstance, "Ramp Delay"), \
+ xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, SST_GAIN_RAMP_DURATION, \
+ xgain_val, xmin_tc, xmax_tc, xpname) }, \
+ { SST_GAIN_KCONTROL_BOOL(SST_CONTROL_NAME(xpname, xmname, xinstance, "Switch"), \
+ xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, \
+ xgain_val, xpname) } ,\
+ { SST_GAIN_KCONTROL_TLV(SST_CONTROL_NAME(xpname, xmname, xinstance, "Volume"), \
+ xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, tlv_array, \
+ xgain_val, xmin_gain, xmax_gain, xpname) }
+
+#define SST_GAIN_TC_MIN 5
+#define SST_GAIN_TC_MAX 5000
+#define SST_GAIN_MIN_VALUE -1440 /* in 0.1 DB units */
+#define SST_GAIN_MAX_VALUE 360
+
+enum sst_algo_kcontrol_type {
+ SST_ALGO_PARAMS,
+ SST_ALGO_BYPASS,
+};
+
+struct sst_algo_control {
+ enum sst_algo_kcontrol_type type;
+ int max;
+ u16 module_id;
+ u16 pipe_id;
+ u16 task_id;
+ u16 cmd_id;
+ bool bypass;
+ unsigned char *params;
+ struct snd_soc_dapm_widget *w;
+};
+
+/* size of the control = size of params + size of length field */
+#define SST_ALGO_CTL_VALUE(xcount, xtype, xpipe, xmod, xtask, xcmd) \
+ (struct sst_algo_control){ \
+ .max = xcount + sizeof(u16), .type = xtype, .module_id = xmod, \
+ .pipe_id = xpipe, .task_id = xtask, .cmd_id = xcmd, \
+ }
+
+#define SST_ALGO_KCONTROL(xname, xcount, xmod, xpipe, \
+ xtask, xcmd, xtype, xinfo, xget, xput) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .info = xinfo, .get = xget, .put = xput, \
+ .private_value = (unsigned long)& \
+ SST_ALGO_CTL_VALUE(xcount, xtype, xpipe, \
+ xmod, xtask, xcmd), \
+}
+
+#define SST_ALGO_KCONTROL_BYTES(xpname, xmname, xcount, xmod, \
+ xpipe, xinstance, xtask, xcmd) \
+ SST_ALGO_KCONTROL(SST_CONTROL_NAME(xpname, xmname, xinstance, "params"), \
+ xcount, xmod, xpipe, xtask, xcmd, SST_ALGO_PARAMS, \
+ sst_algo_bytes_ctl_info, \
+ sst_algo_control_get, sst_algo_control_set)
+
+#define SST_ALGO_KCONTROL_BOOL(xpname, xmname, xmod, xpipe, xinstance, xtask) \
+ SST_ALGO_KCONTROL(SST_CONTROL_NAME(xpname, xmname, xinstance, "bypass"), \
+ 0, xmod, xpipe, xtask, 0, SST_ALGO_BYPASS, \
+ snd_soc_info_bool_ext, \
+ sst_algo_control_get, sst_algo_control_set)
+
+#define SST_ALGO_BYPASS_PARAMS(xpname, xmname, xcount, xmod, xpipe, \
+ xinstance, xtask, xcmd) \
+ SST_ALGO_KCONTROL_BOOL(xpname, xmname, xmod, xpipe, xinstance, xtask), \
+ SST_ALGO_KCONTROL_BYTES(xpname, xmname, xcount, xmod, xpipe, xinstance, xtask, xcmd)
+
+#define SST_COMBO_ALGO_KCONTROL_BYTES(xpname, xmname, xsubmod, xcount, xmod, \
+ xpipe, xinstance, xtask, xcmd) \
+ SST_ALGO_KCONTROL(SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, "params", \
+ xsubmod), \
+ xcount, xmod, xpipe, xtask, xcmd, SST_ALGO_PARAMS, \
+ sst_algo_bytes_ctl_info, \
+ sst_algo_control_get, sst_algo_control_set)
+
+
+struct sst_enum {
+ bool tx;
+ unsigned short reg;
+ unsigned int max;
+ const char * const *texts;
+ struct snd_soc_dapm_widget *w;
+};
+
+/* only 4 slots/channels supported atm */
+#define SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts) \
+ (struct sst_enum){ .reg = s_ch_no, .tx = is_tx, .max = 4+1, .texts = xtexts, }
+
+#define SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name) \
+ xpname " " xmname " " s_ch_name
+
+#define SST_SSP_SLOT_CTL(xpname, xmname, s_ch_name, s_ch_no, is_tx, xtexts, xget, xput) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name), \
+ .info = sst_slot_enum_info, \
+ .get = xget, .put = xput, \
+ .private_value = (unsigned long)&SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts), \
+}
+
+#define SST_MUX_CTL_NAME(xpname, xinstance) \
+ xpname " " #xinstance
+
+#define SST_SSP_MUX_ENUM(xreg, xshift, xtexts) \
+ (struct soc_enum) SOC_ENUM_DOUBLE(xreg, xshift, xshift, ARRAY_SIZE(xtexts), xtexts)
+
+#define SST_SSP_MUX_CTL(xpname, xinstance, xreg, xshift, xtexts) \
+ SOC_DAPM_ENUM(SST_MUX_CTL_NAME(xpname, xinstance), \
+ SST_SSP_MUX_ENUM(xreg, xshift, xtexts))
+
+#endif
--- /dev/null
+#ifndef __SST_MFLD_DSP_H__
+#define __SST_MFLD_DSP_H__
+/*
+ * sst_mfld_dsp.h - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-14 Intel Corporation
+ * Authors: Vinod Koul <vinod.koul@linux.intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#define SST_MAX_BIN_BYTES 1024
+
+#define MAX_DBG_RW_BYTES 80
+#define MAX_NUM_SCATTER_BUFFERS 8
+#define MAX_LOOP_BACK_DWORDS 8
+/* IPC base address and mailbox, timestamp offsets */
+#define SST_MAILBOX_SIZE 0x0400
+#define SST_MAILBOX_SEND 0x0000
+#define SST_TIME_STAMP 0x1800
+#define SST_TIME_STAMP_MRFLD 0x800
+#define SST_RESERVED_OFFSET 0x1A00
+#define SST_SCU_LPE_MAILBOX 0x1000
+#define SST_LPE_SCU_MAILBOX 0x1400
+#define SST_SCU_LPE_LOG_BUF (SST_SCU_LPE_MAILBOX+16)
+#define PROCESS_MSG 0x80
+
+/* Message ID's for IPC messages */
+/* Bits B7: SST or IA/SC ; B6-B4: Msg Category; B3-B0: Msg Type */
+
+/* I2L Firmware/Codec Download msgs */
+#define IPC_IA_PREP_LIB_DNLD 0x01
+#define IPC_IA_LIB_DNLD_CMPLT 0x02
+#define IPC_IA_GET_FW_VERSION 0x04
+#define IPC_IA_GET_FW_BUILD_INF 0x05
+#define IPC_IA_GET_FW_INFO 0x06
+#define IPC_IA_GET_FW_CTXT 0x07
+#define IPC_IA_SET_FW_CTXT 0x08
+#define IPC_IA_PREPARE_SHUTDOWN 0x31
+/* I2L Codec Config/control msgs */
+#define IPC_PREP_D3 0x10
+#define IPC_IA_SET_CODEC_PARAMS 0x10
+#define IPC_IA_GET_CODEC_PARAMS 0x11
+#define IPC_IA_SET_PPP_PARAMS 0x12
+#define IPC_IA_GET_PPP_PARAMS 0x13
+#define IPC_SST_PERIOD_ELAPSED_MRFLD 0xA
+#define IPC_IA_ALG_PARAMS 0x1A
+#define IPC_IA_TUNING_PARAMS 0x1B
+#define IPC_IA_SET_RUNTIME_PARAMS 0x1C
+#define IPC_IA_SET_PARAMS 0x1
+#define IPC_IA_GET_PARAMS 0x2
+
+#define IPC_EFFECTS_CREATE 0xE
+#define IPC_EFFECTS_DESTROY 0xF
+
+/* I2L Stream config/control msgs */
+#define IPC_IA_ALLOC_STREAM_MRFLD 0x2
+#define IPC_IA_ALLOC_STREAM 0x20 /* Allocate a stream ID */
+#define IPC_IA_FREE_STREAM_MRFLD 0x03
+#define IPC_IA_FREE_STREAM 0x21 /* Free the stream ID */
+#define IPC_IA_SET_STREAM_PARAMS 0x22
+#define IPC_IA_SET_STREAM_PARAMS_MRFLD 0x12
+#define IPC_IA_GET_STREAM_PARAMS 0x23
+#define IPC_IA_PAUSE_STREAM 0x24
+#define IPC_IA_PAUSE_STREAM_MRFLD 0x4
+#define IPC_IA_RESUME_STREAM 0x25
+#define IPC_IA_RESUME_STREAM_MRFLD 0x5
+#define IPC_IA_DROP_STREAM 0x26
+#define IPC_IA_DROP_STREAM_MRFLD 0x07
+#define IPC_IA_DRAIN_STREAM 0x27 /* Short msg with str_id */
+#define IPC_IA_DRAIN_STREAM_MRFLD 0x8
+#define IPC_IA_CONTROL_ROUTING 0x29
+#define IPC_IA_VTSV_UPDATE_MODULES 0x20
+#define IPC_IA_VTSV_DETECTED 0x21
+
+#define IPC_IA_START_STREAM_MRFLD 0X06
+#define IPC_IA_START_STREAM 0x30 /* Short msg with str_id */
+
+#define IPC_IA_SET_GAIN_MRFLD 0x21
+/* Debug msgs */
+#define IPC_IA_DBG_MEM_READ 0x40
+#define IPC_IA_DBG_MEM_WRITE 0x41
+#define IPC_IA_DBG_LOOP_BACK 0x42
+#define IPC_IA_DBG_LOG_ENABLE 0x45
+#define IPC_IA_DBG_SET_PROBE_PARAMS 0x47
+
+/* L2I Firmware/Codec Download msgs */
+#define IPC_IA_FW_INIT_CMPLT 0x81
+#define IPC_IA_FW_INIT_CMPLT_MRFLD 0x01
+#define IPC_IA_FW_ASYNC_ERR_MRFLD 0x11
+
+/* L2I Codec Config/control msgs */
+#define IPC_SST_FRAGMENT_ELPASED 0x90 /* Request IA more data */
+
+#define IPC_SST_BUF_UNDER_RUN 0x92 /* PB Under run and stopped */
+#define IPC_SST_BUF_OVER_RUN 0x93 /* CAP Under run and stopped */
+#define IPC_SST_DRAIN_END 0x94 /* PB Drain complete and stopped */
+#define IPC_SST_CHNGE_SSP_PARAMS 0x95 /* PB SSP parameters changed */
+#define IPC_SST_STREAM_PROCESS_FATAL_ERR 0x96/* error in processing a stream */
+#define IPC_SST_PERIOD_ELAPSED 0x97 /* period elapsed */
+
+#define IPC_SST_ERROR_EVENT 0x99 /* Buffer over run occurred */
+/* L2S messages */
+#define IPC_SC_DDR_LINK_UP 0xC0
+#define IPC_SC_DDR_LINK_DOWN 0xC1
+#define IPC_SC_SET_LPECLK_REQ 0xC2
+#define IPC_SC_SSP_BIT_BANG 0xC3
+
+/* L2I Error reporting msgs */
+#define IPC_IA_MEM_ALLOC_FAIL 0xE0
+#define IPC_IA_PROC_ERR 0xE1 /* error in processing a
+ stream can be used by playback and
+ capture modules */
+
+/* L2I Debug msgs */
+#define IPC_IA_PRINT_STRING 0xF0
+
+/* Buffer under-run */
+#define IPC_IA_BUF_UNDER_RUN_MRFLD 0x0B
+
+/* Mrfld specific defines:
+ * For asynchronous messages(INIT_CMPLT, PERIOD_ELAPSED, ASYNC_ERROR)
+ * received from FW, the format is:
+ * - IPC High: pvt_id is set to zero. Always short message.
+ * - msg_id is in lower 16-bits of IPC low payload.
+ * - pipe_id is in higher 16-bits of IPC low payload for period_elapsed.
+ * - error id is in higher 16-bits of IPC low payload for async errors.
+ */
+#define SST_ASYNC_DRV_ID 0
+
+/* Command Response or Acknowledge message to any IPC message will have
+ * same message ID and stream ID information which is sent.
+ * There is no specific Ack message ID. The data field is used as response
+ * meaning.
+ */
+enum ackData {
+ IPC_ACK_SUCCESS = 0,
+ IPC_ACK_FAILURE,
+};
+
+enum ipc_ia_msg_id {
+ IPC_CMD = 1, /*!< Task Control message ID */
+ IPC_SET_PARAMS = 2,/*!< Task Set param message ID */
+ IPC_GET_PARAMS = 3, /*!< Task Get param message ID */
+ IPC_INVALID = 0xFF, /*!<Task Get param message ID */
+};
+
+enum sst_codec_types {
+ /* AUDIO/MUSIC CODEC Type Definitions */
+ SST_CODEC_TYPE_UNKNOWN = 0,
+ SST_CODEC_TYPE_PCM, /* Pass through Audio codec */
+ SST_CODEC_TYPE_MP3,
+ SST_CODEC_TYPE_MP24,
+ SST_CODEC_TYPE_AAC,
+ SST_CODEC_TYPE_AACP,
+ SST_CODEC_TYPE_eAACP,
+};
+
+enum stream_type {
+ SST_STREAM_TYPE_NONE = 0,
+ SST_STREAM_TYPE_MUSIC = 1,
+};
+
+enum sst_error_codes {
+ /* Error code,response to msgId: Description */
+ /* Common error codes */
+ SST_SUCCESS = 0, /* Success */
+ SST_ERR_INVALID_STREAM_ID = 1,
+ SST_ERR_INVALID_MSG_ID = 2,
+ SST_ERR_INVALID_STREAM_OP = 3,
+ SST_ERR_INVALID_PARAMS = 4,
+ SST_ERR_INVALID_CODEC = 5,
+ SST_ERR_INVALID_MEDIA_TYPE = 6,
+ SST_ERR_STREAM_ERR = 7,
+
+ SST_ERR_STREAM_IN_USE = 15,
+};
+
+struct ipc_dsp_hdr {
+ u16 mod_index_id:8; /*!< DSP Command ID specific to tasks */
+ u16 pipe_id:8; /*!< instance of the module in the pipeline */
+ u16 mod_id; /*!< Pipe_id */
+ u16 cmd_id; /*!< Module ID = lpe_algo_types_t */
+ u16 length; /*!< Length of the payload only */
+} __packed;
+
+union ipc_header_high {
+ struct {
+ u32 msg_id:8; /* Message ID - Max 256 Message Types */
+ u32 task_id:4; /* Task ID associated with this comand */
+ u32 drv_id:4; /* Identifier for the driver to track*/
+ u32 rsvd1:8; /* Reserved */
+ u32 result:4; /* Reserved */
+ u32 res_rqd:1; /* Response rqd */
+ u32 large:1; /* Large Message if large = 1 */
+ u32 done:1; /* bit 30 - Done bit */
+ u32 busy:1; /* bit 31 - busy bit*/
+ } part;
+ u32 full;
+} __packed;
+/* IPC header */
+union ipc_header_mrfld {
+ struct {
+ u32 header_low_payload;
+ union ipc_header_high header_high;
+ } p;
+ u64 full;
+} __packed;
+/* CAUTION NOTE: All IPC message body must be multiple of 32 bits.*/
+
+/* IPC Header */
+union ipc_header {
+ struct {
+ u32 msg_id:8; /* Message ID - Max 256 Message Types */
+ u32 str_id:5;
+ u32 large:1; /* Large Message if large = 1 */
+ u32 reserved:2; /* Reserved for future use */
+ u32 data:14; /* Ack/Info for msg, size of msg in Mailbox */
+ u32 done:1; /* bit 30 */
+ u32 busy:1; /* bit 31 */
+ } part;
+ u32 full;
+} __packed;
+
+/* Firmware build info */
+struct sst_fw_build_info {
+ unsigned char date[16]; /* Firmware build date */
+ unsigned char time[16]; /* Firmware build time */
+} __packed;
+
+/* Firmware Version info */
+struct snd_sst_fw_version {
+ u8 build; /* build number*/
+ u8 minor; /* minor number*/
+ u8 major; /* major number*/
+ u8 type; /* build type */
+};
+
+struct ipc_header_fw_init {
+ struct snd_sst_fw_version fw_version;/* Firmware version details */
+ struct sst_fw_build_info build_info;
+ u16 result; /* Fw init result */
+ u8 module_id; /* Module ID in case of error */
+ u8 debug_info; /* Debug info from Module ID in case of fail */
+} __packed;
+
+struct snd_sst_tstamp {
+ u64 ring_buffer_counter; /* PB/CP: Bytes copied from/to DDR. */
+ u64 hardware_counter; /* PB/CP: Bytes DMAed to/from SSP. */
+ u64 frames_decoded;
+ u64 bytes_decoded;
+ u64 bytes_copied;
+ u32 sampling_frequency;
+ u32 channel_peak[8];
+} __packed;
+
+/* Stream type params struture for Alloc stream */
+struct snd_sst_str_type {
+ u8 codec_type; /* Codec type */
+ u8 str_type; /* 1 = voice 2 = music */
+ u8 operation; /* Playback or Capture */
+ u8 protected_str; /* 0=Non DRM, 1=DRM */
+ u8 time_slots;
+ u8 reserved; /* Reserved */
+ u16 result; /* Result used for acknowledgment */
+} __packed;
+
+/* Library info structure */
+struct module_info {
+ u32 lib_version;
+ u32 lib_type;/*TBD- KLOCKWORK u8 lib_type;*/
+ u32 media_type;
+ u8 lib_name[12];
+ u32 lib_caps;
+ unsigned char b_date[16]; /* Lib build date */
+ unsigned char b_time[16]; /* Lib build time */
+} __packed;
+
+/* Library slot info */
+struct lib_slot_info {
+ u8 slot_num; /* 1 or 2 */
+ u8 reserved1;
+ u16 reserved2;
+ u32 iram_size; /* slot size in IRAM */
+ u32 dram_size; /* slot size in DRAM */
+ u32 iram_offset; /* starting offset of slot in IRAM */
+ u32 dram_offset; /* starting offset of slot in DRAM */
+} __packed;
+
+struct snd_ppp_mixer_params {
+ __u32 type; /*Type of the parameter */
+ __u32 size;
+ __u32 input_stream_bitmap; /*Input stream Bit Map*/
+} __packed;
+
+struct snd_sst_lib_download {
+ struct module_info lib_info; /* library info type, capabilities etc */
+ struct lib_slot_info slot_info; /* slot info to be downloaded */
+ u32 mod_entry_pt;
+};
+
+struct snd_sst_lib_download_info {
+ struct snd_sst_lib_download dload_lib;
+ u16 result; /* Result used for acknowledgment */
+ u8 pvt_id; /* Private ID */
+ u8 reserved; /* for alignment */
+};
+struct snd_pcm_params {
+ u8 num_chan; /* 1=Mono, 2=Stereo */
+ u8 pcm_wd_sz; /* 16/24 - bit*/
+ u8 use_offload_path; /* 0-PCM using period elpased & ALSA interfaces
+ 1-PCM stream via compressed interface */
+ u8 reserved2;
+ u32 sfreq; /* Sampling rate in Hz */
+ u8 channel_map[8];
+} __packed;
+
+/* MP3 Music Parameters Message */
+struct snd_mp3_params {
+ u8 num_chan; /* 1=Mono, 2=Stereo */
+ u8 pcm_wd_sz; /* 16/24 - bit*/
+ u8 crc_check; /* crc_check - disable (0) or enable (1) */
+ u8 reserved1; /* unused*/
+ u16 reserved2; /* Unused */
+} __packed;
+
+#define AAC_BIT_STREAM_ADTS 0
+#define AAC_BIT_STREAM_ADIF 1
+#define AAC_BIT_STREAM_RAW 2
+
+/* AAC Music Parameters Message */
+struct snd_aac_params {
+ u8 num_chan; /* 1=Mono, 2=Stereo*/
+ u8 pcm_wd_sz; /* 16/24 - bit*/
+ u8 bdownsample; /*SBR downsampling 0 - disable 1 -enabled AAC+ only */
+ u8 bs_format; /* input bit stream format adts=0, adif=1, raw=2 */
+ u16 reser2;
+ u32 externalsr; /*sampling rate of basic AAC raw bit stream*/
+ u8 sbr_signalling;/*disable/enable/set automode the SBR tool.AAC+*/
+ u8 reser1;
+ u16 reser3;
+} __packed;
+
+/* WMA Music Parameters Message */
+struct snd_wma_params {
+ u8 num_chan; /* 1=Mono, 2=Stereo */
+ u8 pcm_wd_sz; /* 16/24 - bit*/
+ u16 reserved1;
+ u32 brate; /* Use the hard coded value. */
+ u32 sfreq; /* Sampling freq eg. 8000, 441000, 48000 */
+ u32 channel_mask; /* Channel Mask */
+ u16 format_tag; /* Format Tag */
+ u16 block_align; /* packet size */
+ u16 wma_encode_opt;/* Encoder option */
+ u8 op_align; /* op align 0- 16 bit, 1- MSB, 2 LSB */
+ u8 reserved; /* reserved */
+} __packed;
+
+/* Codec params struture */
+union snd_sst_codec_params {
+ struct snd_pcm_params pcm_params;
+ struct snd_mp3_params mp3_params;
+ struct snd_aac_params aac_params;
+ struct snd_wma_params wma_params;
+} __packed;
+
+/* Address and size info of a frame buffer */
+struct sst_address_info {
+ u32 addr; /* Address at IA */
+ u32 size; /* Size of the buffer */
+};
+
+struct snd_sst_alloc_params_ext {
+ __u16 sg_count;
+ __u16 reserved;
+ __u32 frag_size; /*Number of samples after which period elapsed
+ message is sent valid only if path = 0*/
+ struct sst_address_info ring_buf_info[8];
+};
+
+struct snd_sst_stream_params {
+ union snd_sst_codec_params uc;
+} __packed;
+
+struct snd_sst_params {
+ u32 result;
+ u32 stream_id;
+ u8 codec;
+ u8 ops;
+ u8 stream_type;
+ u8 device_type;
+ u8 task;
+ struct snd_sst_stream_params sparams;
+ struct snd_sst_alloc_params_ext aparams;
+};
+
+struct snd_sst_alloc_mrfld {
+ u16 codec_type;
+ u8 operation;
+ u8 sg_count;
+ struct sst_address_info ring_buf_info[8];
+ u32 frag_size;
+ u32 ts;
+ struct snd_sst_stream_params codec_params;
+} __packed;
+
+/* Alloc stream params structure */
+struct snd_sst_alloc_params {
+ struct snd_sst_str_type str_type;
+ struct snd_sst_stream_params stream_params;
+ struct snd_sst_alloc_params_ext alloc_params;
+} __packed;
+
+/* Alloc stream response message */
+struct snd_sst_alloc_response {
+ struct snd_sst_str_type str_type; /* Stream type for allocation */
+ struct snd_sst_lib_download lib_dnld; /* Valid only for codec dnld */
+};
+
+/* Drop response */
+struct snd_sst_drop_response {
+ u32 result;
+ u32 bytes;
+};
+
+struct snd_sst_async_msg {
+ u32 msg_id; /* Async msg id */
+ u32 payload[0];
+};
+
+struct snd_sst_async_err_msg {
+ u32 fw_resp; /* Firmware Result */
+ u32 lib_resp; /*Library result */
+} __packed;
+
+struct snd_sst_vol {
+ u32 stream_id;
+ s32 volume;
+ u32 ramp_duration;
+ u32 ramp_type; /* Ramp type, default=0 */
+};
+
+/* Gain library parameters for mrfld
+ * based on DSP command spec v0.82
+ */
+struct snd_sst_gain_v2 {
+ u16 gain_cell_num; /* num of gain cells to modify*/
+ u8 cell_nbr_idx; /* instance index*/
+ u8 cell_path_idx; /* pipe-id */
+ u16 module_id; /*module id */
+ u16 left_cell_gain; /* left gain value in dB*/
+ u16 right_cell_gain; /* right gain value in dB*/
+ u16 gain_time_const; /* gain time constant*/
+} __packed;
+
+struct snd_sst_mute {
+ u32 stream_id;
+ u32 mute;
+};
+
+struct snd_sst_runtime_params {
+ u8 type;
+ u8 str_id;
+ u8 size;
+ u8 rsvd;
+ void *addr;
+} __packed;
+
+enum stream_param_type {
+ SST_SET_TIME_SLOT = 0,
+ SST_SET_CHANNEL_INFO = 1,
+ OTHERS = 2, /*reserved for future params*/
+};
+
+/* CSV Voice call routing structure */
+struct snd_sst_control_routing {
+ u8 control; /* 0=start, 1=Stop */
+ u8 reserved[3]; /* Reserved- for 32 bit alignment */
+};
+
+struct ipc_post {
+ struct list_head node;
+ union ipc_header header; /* driver specific */
+ bool is_large;
+ bool is_process_reply;
+ union ipc_header_mrfld mrfld_header;
+ char *mailbox_data;
+};
+
+struct snd_sst_ctxt_params {
+ u32 address; /* Physical Address in DDR where the context is stored */
+ u32 size; /* size of the context */
+};
+
+struct snd_sst_lpe_log_params {
+ u8 dbg_type;
+ u8 module_id;
+ u8 log_level;
+ u8 reserved;
+} __packed;
+
+enum snd_sst_bytes_type {
+ SND_SST_BYTES_SET = 0x1,
+ SND_SST_BYTES_GET = 0x2,
+};
+
+struct snd_sst_bytes_v2 {
+ u8 type;
+ u8 ipc_msg;
+ u8 block;
+ u8 task_id;
+ u8 pipe_id;
+ u8 rsvd;
+ u16 len;
+ char bytes[0];
+};
+
+#define MAX_VTSV_FILES 2
+struct snd_sst_vtsv_info {
+ struct sst_address_info vfiles[MAX_VTSV_FILES];
+} __packed;
+
+#endif /* __SST_MFLD_DSP_H__ */
--- /dev/null
+/*
+ * sst_mfld_platform.c - Intel MID Platform driver
+ *
+ * Copyright (C) 2010-2014 Intel Corp
+ * Author: Vinod Koul <vinod.koul@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include "sst-mfld-platform.h"
+
+/* compress stream operations */
+static void sst_compr_fragment_elapsed(void *arg)
+{
+ struct snd_compr_stream *cstream = (struct snd_compr_stream *)arg;
+
+ pr_debug("fragment elapsed by driver\n");
+ if (cstream)
+ snd_compr_fragment_elapsed(cstream);
+}
+
+static void sst_drain_notify(void *arg)
+{
+ struct snd_compr_stream *cstream = (struct snd_compr_stream *)arg;
+
+ pr_debug("drain notify by driver\n");
+ if (cstream)
+ snd_compr_drain_notify(cstream);
+}
+
+static int sst_platform_compr_open(struct snd_compr_stream *cstream)
+{
+
+ int ret_val = 0;
+ struct snd_compr_runtime *runtime = cstream->runtime;
+ struct sst_runtime_stream *stream;
+
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream)
+ return -ENOMEM;
+
+ spin_lock_init(&stream->status_lock);
+
+ /* get the sst ops */
+ if (!sst || !try_module_get(sst->dev->driver->owner)) {
+ pr_err("no device available to run\n");
+ ret_val = -ENODEV;
+ goto out_ops;
+ }
+ stream->compr_ops = sst->compr_ops;
+ stream->id = 0;
+
+ /* Turn on LPE */
+ sst->compr_ops->power(sst->dev, true);
+
+ sst_set_stream_status(stream, SST_PLATFORM_INIT);
+ runtime->private_data = stream;
+ return 0;
+out_ops:
+ kfree(stream);
+ return ret_val;
+}
+
+static int sst_platform_compr_free(struct snd_compr_stream *cstream)
+{
+ struct sst_runtime_stream *stream;
+ int ret_val = 0, str_id;
+
+ stream = cstream->runtime->private_data;
+ /* Turn off LPE */
+ sst->compr_ops->power(sst->dev, false);
+
+ /*need to check*/
+ str_id = stream->id;
+ if (str_id)
+ ret_val = stream->compr_ops->close(sst->dev, str_id);
+ module_put(sst->dev->driver->owner);
+ kfree(stream);
+ pr_debug("%s: %d\n", __func__, ret_val);
+ return 0;
+}
+
+static int sst_platform_compr_set_params(struct snd_compr_stream *cstream,
+ struct snd_compr_params *params)
+{
+ struct sst_runtime_stream *stream;
+ int retval;
+ struct snd_sst_params str_params;
+ struct sst_compress_cb cb;
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ struct sst_data *ctx = snd_soc_platform_get_drvdata(platform);
+
+ stream = cstream->runtime->private_data;
+ /* construct fw structure for this*/
+ memset(&str_params, 0, sizeof(str_params));
+
+ /* fill the device type and stream id to pass to SST driver */
+ retval = sst_fill_stream_params(cstream, ctx, &str_params, true);
+ pr_debug("compr_set_params: fill stream params ret_val = 0x%x\n", retval);
+ if (retval < 0)
+ return retval;
+
+ switch (params->codec.id) {
+ case SND_AUDIOCODEC_MP3: {
+ str_params.codec = SST_CODEC_TYPE_MP3;
+ str_params.sparams.uc.mp3_params.num_chan = params->codec.ch_in;
+ str_params.sparams.uc.mp3_params.pcm_wd_sz = 16;
+ break;
+ }
+
+ case SND_AUDIOCODEC_AAC: {
+ str_params.codec = SST_CODEC_TYPE_AAC;
+ str_params.sparams.uc.aac_params.num_chan = params->codec.ch_in;
+ str_params.sparams.uc.aac_params.pcm_wd_sz = 16;
+ if (params->codec.format == SND_AUDIOSTREAMFORMAT_MP4ADTS)
+ str_params.sparams.uc.aac_params.bs_format =
+ AAC_BIT_STREAM_ADTS;
+ else if (params->codec.format == SND_AUDIOSTREAMFORMAT_RAW)
+ str_params.sparams.uc.aac_params.bs_format =
+ AAC_BIT_STREAM_RAW;
+ else {
+ pr_err("Undefined format%d\n", params->codec.format);
+ return -EINVAL;
+ }
+ str_params.sparams.uc.aac_params.externalsr =
+ params->codec.sample_rate;
+ break;
+ }
+
+ default:
+ pr_err("codec not supported, id =%d\n", params->codec.id);
+ return -EINVAL;
+ }
+
+ str_params.aparams.ring_buf_info[0].addr =
+ virt_to_phys(cstream->runtime->buffer);
+ str_params.aparams.ring_buf_info[0].size =
+ cstream->runtime->buffer_size;
+ str_params.aparams.sg_count = 1;
+ str_params.aparams.frag_size = cstream->runtime->fragment_size;
+
+ cb.param = cstream;
+ cb.compr_cb = sst_compr_fragment_elapsed;
+ cb.drain_cb_param = cstream;
+ cb.drain_notify = sst_drain_notify;
+
+ retval = stream->compr_ops->open(sst->dev, &str_params, &cb);
+ if (retval < 0) {
+ pr_err("stream allocation failed %d\n", retval);
+ return retval;
+ }
+
+ stream->id = retval;
+ return 0;
+}
+
+static int sst_platform_compr_trigger(struct snd_compr_stream *cstream, int cmd)
+{
+ struct sst_runtime_stream *stream = cstream->runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if (stream->compr_ops->stream_start)
+ return stream->compr_ops->stream_start(sst->dev, stream->id);
+ case SNDRV_PCM_TRIGGER_STOP:
+ if (stream->compr_ops->stream_drop)
+ return stream->compr_ops->stream_drop(sst->dev, stream->id);
+ case SND_COMPR_TRIGGER_DRAIN:
+ if (stream->compr_ops->stream_drain)
+ return stream->compr_ops->stream_drain(sst->dev, stream->id);
+ case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
+ if (stream->compr_ops->stream_partial_drain)
+ return stream->compr_ops->stream_partial_drain(sst->dev, stream->id);
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (stream->compr_ops->stream_pause)
+ return stream->compr_ops->stream_pause(sst->dev, stream->id);
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (stream->compr_ops->stream_pause_release)
+ return stream->compr_ops->stream_pause_release(sst->dev, stream->id);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sst_platform_compr_pointer(struct snd_compr_stream *cstream,
+ struct snd_compr_tstamp *tstamp)
+{
+ struct sst_runtime_stream *stream;
+
+ stream = cstream->runtime->private_data;
+ stream->compr_ops->tstamp(sst->dev, stream->id, tstamp);
+ tstamp->byte_offset = tstamp->copied_total %
+ (u32)cstream->runtime->buffer_size;
+ pr_debug("calc bytes offset/copied bytes as %d\n", tstamp->byte_offset);
+ return 0;
+}
+
+static int sst_platform_compr_ack(struct snd_compr_stream *cstream,
+ size_t bytes)
+{
+ struct sst_runtime_stream *stream;
+
+ stream = cstream->runtime->private_data;
+ stream->compr_ops->ack(sst->dev, stream->id, (unsigned long)bytes);
+ stream->bytes_written += bytes;
+
+ return 0;
+}
+
+static int sst_platform_compr_get_caps(struct snd_compr_stream *cstream,
+ struct snd_compr_caps *caps)
+{
+ struct sst_runtime_stream *stream =
+ cstream->runtime->private_data;
+
+ return stream->compr_ops->get_caps(caps);
+}
+
+static int sst_platform_compr_get_codec_caps(struct snd_compr_stream *cstream,
+ struct snd_compr_codec_caps *codec)
+{
+ struct sst_runtime_stream *stream =
+ cstream->runtime->private_data;
+
+ return stream->compr_ops->get_codec_caps(codec);
+}
+
+static int sst_platform_compr_set_metadata(struct snd_compr_stream *cstream,
+ struct snd_compr_metadata *metadata)
+{
+ struct sst_runtime_stream *stream =
+ cstream->runtime->private_data;
+
+ return stream->compr_ops->set_metadata(sst->dev, stream->id, metadata);
+}
+
+struct snd_compr_ops sst_platform_compr_ops = {
+
+ .open = sst_platform_compr_open,
+ .free = sst_platform_compr_free,
+ .set_params = sst_platform_compr_set_params,
+ .set_metadata = sst_platform_compr_set_metadata,
+ .trigger = sst_platform_compr_trigger,
+ .pointer = sst_platform_compr_pointer,
+ .ack = sst_platform_compr_ack,
+ .get_caps = sst_platform_compr_get_caps,
+ .get_codec_caps = sst_platform_compr_get_codec_caps,
+};
--- /dev/null
+/*
+ * sst_mfld_platform.c - Intel MID Platform driver
+ *
+ * Copyright (C) 2010-2014 Intel Corp
+ * Author: Vinod Koul <vinod.koul@intel.com>
+ * Author: Harsha Priya <priya.harsha@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "sst-mfld-platform.h"
+#include "sst-atom-controls.h"
+
+struct sst_device *sst;
+static DEFINE_MUTEX(sst_lock);
+extern struct snd_compr_ops sst_platform_compr_ops;
+
+int sst_register_dsp(struct sst_device *dev)
+{
+ if (WARN_ON(!dev))
+ return -EINVAL;
+ if (!try_module_get(dev->dev->driver->owner))
+ return -ENODEV;
+ mutex_lock(&sst_lock);
+ if (sst) {
+ dev_err(dev->dev, "we already have a device %s\n", sst->name);
+ module_put(dev->dev->driver->owner);
+ mutex_unlock(&sst_lock);
+ return -EEXIST;
+ }
+ dev_dbg(dev->dev, "registering device %s\n", dev->name);
+ sst = dev;
+ mutex_unlock(&sst_lock);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_register_dsp);
+
+int sst_unregister_dsp(struct sst_device *dev)
+{
+ if (WARN_ON(!dev))
+ return -EINVAL;
+ if (dev != sst)
+ return -EINVAL;
+
+ mutex_lock(&sst_lock);
+
+ if (!sst) {
+ mutex_unlock(&sst_lock);
+ return -EIO;
+ }
+
+ module_put(sst->dev->driver->owner);
+ dev_dbg(dev->dev, "unreg %s\n", sst->name);
+ sst = NULL;
+ mutex_unlock(&sst_lock);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_unregister_dsp);
+
+static struct snd_pcm_hardware sst_platform_pcm_hw = {
+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_DOUBLE |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_MMAP|
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_SYNC_START),
+ .buffer_bytes_max = SST_MAX_BUFFER,
+ .period_bytes_min = SST_MIN_PERIOD_BYTES,
+ .period_bytes_max = SST_MAX_PERIOD_BYTES,
+ .periods_min = SST_MIN_PERIODS,
+ .periods_max = SST_MAX_PERIODS,
+ .fifo_size = SST_FIFO_SIZE,
+};
+
+static struct sst_dev_stream_map dpcm_strm_map[] = {
+ {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, /* Reserved, not in use */
+ {MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA1_IN, SST_TASK_ID_MEDIA, 0},
+ {MERR_DPCM_COMPR, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA0_IN, SST_TASK_ID_MEDIA, 0},
+ {MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_PCM1_OUT, SST_TASK_ID_MEDIA, 0},
+};
+
+static int sst_media_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+
+ return sst_send_pipe_gains(dai, stream, mute);
+}
+
+/* helper functions */
+void sst_set_stream_status(struct sst_runtime_stream *stream,
+ int state)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&stream->status_lock, flags);
+ stream->stream_status = state;
+ spin_unlock_irqrestore(&stream->status_lock, flags);
+}
+
+static inline int sst_get_stream_status(struct sst_runtime_stream *stream)
+{
+ int state;
+ unsigned long flags;
+
+ spin_lock_irqsave(&stream->status_lock, flags);
+ state = stream->stream_status;
+ spin_unlock_irqrestore(&stream->status_lock, flags);
+ return state;
+}
+
+static void sst_fill_alloc_params(struct snd_pcm_substream *substream,
+ struct snd_sst_alloc_params_ext *alloc_param)
+{
+ unsigned int channels;
+ snd_pcm_uframes_t period_size;
+ ssize_t periodbytes;
+ ssize_t buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
+ u32 buffer_addr = virt_to_phys(substream->dma_buffer.area);
+
+ channels = substream->runtime->channels;
+ period_size = substream->runtime->period_size;
+ periodbytes = samples_to_bytes(substream->runtime, period_size);
+ alloc_param->ring_buf_info[0].addr = buffer_addr;
+ alloc_param->ring_buf_info[0].size = buffer_bytes;
+ alloc_param->sg_count = 1;
+ alloc_param->reserved = 0;
+ alloc_param->frag_size = periodbytes * channels;
+
+}
+static void sst_fill_pcm_params(struct snd_pcm_substream *substream,
+ struct snd_sst_stream_params *param)
+{
+ param->uc.pcm_params.num_chan = (u8) substream->runtime->channels;
+ param->uc.pcm_params.pcm_wd_sz = substream->runtime->sample_bits;
+ param->uc.pcm_params.sfreq = substream->runtime->rate;
+
+ /* PCM stream via ALSA interface */
+ param->uc.pcm_params.use_offload_path = 0;
+ param->uc.pcm_params.reserved2 = 0;
+ memset(param->uc.pcm_params.channel_map, 0, sizeof(u8));
+
+}
+
+static int sst_get_stream_mapping(int dev, int sdev, int dir,
+ struct sst_dev_stream_map *map, int size)
+{
+ int i;
+
+ if (map == NULL)
+ return -EINVAL;
+
+
+ /* index 0 is not used in stream map */
+ for (i = 1; i < size; i++) {
+ if ((map[i].dev_num == dev) && (map[i].direction == dir))
+ return i;
+ }
+ return 0;
+}
+
+int sst_fill_stream_params(void *substream,
+ const struct sst_data *ctx, struct snd_sst_params *str_params, bool is_compress)
+{
+ int map_size;
+ int index;
+ struct sst_dev_stream_map *map;
+ struct snd_pcm_substream *pstream = NULL;
+ struct snd_compr_stream *cstream = NULL;
+
+ map = ctx->pdata->pdev_strm_map;
+ map_size = ctx->pdata->strm_map_size;
+
+ if (is_compress == true)
+ cstream = (struct snd_compr_stream *)substream;
+ else
+ pstream = (struct snd_pcm_substream *)substream;
+
+ str_params->stream_type = SST_STREAM_TYPE_MUSIC;
+
+ /* For pcm streams */
+ if (pstream) {
+ index = sst_get_stream_mapping(pstream->pcm->device,
+ pstream->number, pstream->stream,
+ map, map_size);
+ if (index <= 0)
+ return -EINVAL;
+
+ str_params->stream_id = index;
+ str_params->device_type = map[index].device_id;
+ str_params->task = map[index].task_id;
+
+ str_params->ops = (u8)pstream->stream;
+ }
+
+ if (cstream) {
+ index = sst_get_stream_mapping(cstream->device->device,
+ 0, cstream->direction,
+ map, map_size);
+ if (index <= 0)
+ return -EINVAL;
+ str_params->stream_id = index;
+ str_params->device_type = map[index].device_id;
+ str_params->task = map[index].task_id;
+
+ str_params->ops = (u8)cstream->direction;
+ }
+ return 0;
+}
+
+static int sst_platform_alloc_stream(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct sst_runtime_stream *stream =
+ substream->runtime->private_data;
+ struct snd_sst_stream_params param = {{{0,},},};
+ struct snd_sst_params str_params = {0};
+ struct snd_sst_alloc_params_ext alloc_params = {0};
+ int ret_val = 0;
+ struct sst_data *ctx = snd_soc_dai_get_drvdata(dai);
+
+ /* set codec params and inform SST driver the same */
+ sst_fill_pcm_params(substream, ¶m);
+ sst_fill_alloc_params(substream, &alloc_params);
+ substream->runtime->dma_area = substream->dma_buffer.area;
+ str_params.sparams = param;
+ str_params.aparams = alloc_params;
+ str_params.codec = SST_CODEC_TYPE_PCM;
+
+ /* fill the device type and stream id to pass to SST driver */
+ ret_val = sst_fill_stream_params(substream, ctx, &str_params, false);
+ if (ret_val < 0)
+ return ret_val;
+
+ stream->stream_info.str_id = str_params.stream_id;
+
+ ret_val = stream->ops->open(sst->dev, &str_params);
+ if (ret_val <= 0)
+ return ret_val;
+
+
+ return ret_val;
+}
+
+static void sst_period_elapsed(void *arg)
+{
+ struct snd_pcm_substream *substream = arg;
+ struct sst_runtime_stream *stream;
+ int status;
+
+ if (!substream || !substream->runtime)
+ return;
+ stream = substream->runtime->private_data;
+ if (!stream)
+ return;
+ status = sst_get_stream_status(stream);
+ if (status != SST_PLATFORM_RUNNING)
+ return;
+ snd_pcm_period_elapsed(substream);
+}
+
+static int sst_platform_init_stream(struct snd_pcm_substream *substream)
+{
+ struct sst_runtime_stream *stream =
+ substream->runtime->private_data;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int ret_val;
+
+ dev_dbg(rtd->dev, "setting buffer ptr param\n");
+ sst_set_stream_status(stream, SST_PLATFORM_INIT);
+ stream->stream_info.period_elapsed = sst_period_elapsed;
+ stream->stream_info.arg = substream;
+ stream->stream_info.buffer_ptr = 0;
+ stream->stream_info.sfreq = substream->runtime->rate;
+ ret_val = stream->ops->stream_init(sst->dev, &stream->stream_info);
+ if (ret_val)
+ dev_err(rtd->dev, "control_set ret error %d\n", ret_val);
+ return ret_val;
+
+}
+
+static int power_up_sst(struct sst_runtime_stream *stream)
+{
+ return stream->ops->power(sst->dev, true);
+}
+
+static void power_down_sst(struct sst_runtime_stream *stream)
+{
+ stream->ops->power(sst->dev, false);
+}
+
+static int sst_media_open(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int ret_val = 0;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct sst_runtime_stream *stream;
+
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream)
+ return -ENOMEM;
+ spin_lock_init(&stream->status_lock);
+
+ /* get the sst ops */
+ mutex_lock(&sst_lock);
+ if (!sst ||
+ !try_module_get(sst->dev->driver->owner)) {
+ dev_err(dai->dev, "no device available to run\n");
+ ret_val = -ENODEV;
+ goto out_ops;
+ }
+ stream->ops = sst->ops;
+ mutex_unlock(&sst_lock);
+
+ stream->stream_info.str_id = 0;
+
+ stream->stream_info.arg = substream;
+ /* allocate memory for SST API set */
+ runtime->private_data = stream;
+
+ ret_val = power_up_sst(stream);
+ if (ret_val < 0)
+ return ret_val;
+
+ /* Make sure, that the period size is always even */
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIODS, 2);
+
+ return snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+out_ops:
+ kfree(stream);
+ mutex_unlock(&sst_lock);
+ return ret_val;
+}
+
+static void sst_media_close(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct sst_runtime_stream *stream;
+ int ret_val = 0, str_id;
+
+ stream = substream->runtime->private_data;
+ power_down_sst(stream);
+
+ str_id = stream->stream_info.str_id;
+ if (str_id)
+ ret_val = stream->ops->close(sst->dev, str_id);
+ module_put(sst->dev->driver->owner);
+ kfree(stream);
+}
+
+static inline unsigned int get_current_pipe_id(struct snd_soc_dai *dai,
+ struct snd_pcm_substream *substream)
+{
+ struct sst_data *sst = snd_soc_dai_get_drvdata(dai);
+ struct sst_dev_stream_map *map = sst->pdata->pdev_strm_map;
+ struct sst_runtime_stream *stream =
+ substream->runtime->private_data;
+ u32 str_id = stream->stream_info.str_id;
+ unsigned int pipe_id;
+
+ pipe_id = map[str_id].device_id;
+
+ dev_dbg(dai->dev, "got pipe_id = %#x for str_id = %d\n",
+ pipe_id, str_id);
+ return pipe_id;
+}
+
+static int sst_media_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct sst_runtime_stream *stream;
+ int ret_val = 0, str_id;
+
+ stream = substream->runtime->private_data;
+ str_id = stream->stream_info.str_id;
+ if (stream->stream_info.str_id) {
+ ret_val = stream->ops->stream_drop(sst->dev, str_id);
+ return ret_val;
+ }
+
+ ret_val = sst_platform_alloc_stream(substream, dai);
+ if (ret_val <= 0)
+ return ret_val;
+ snprintf(substream->pcm->id, sizeof(substream->pcm->id),
+ "%d", stream->stream_info.str_id);
+
+ ret_val = sst_platform_init_stream(substream);
+ if (ret_val)
+ return ret_val;
+ substream->runtime->hw.info = SNDRV_PCM_INFO_BLOCK_TRANSFER;
+ return ret_val;
+}
+
+static int sst_media_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+ memset(substream->runtime->dma_area, 0, params_buffer_bytes(params));
+ return 0;
+}
+
+static int sst_media_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int sst_enable_ssp(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int ret = 0;
+
+ if (!dai->active) {
+ ret = sst_handle_vb_timer(dai, true);
+ if (ret)
+ return ret;
+ ret = send_ssp_cmd(dai, dai->name, 1);
+ }
+ return ret;
+}
+
+static void sst_disable_ssp(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ if (!dai->active) {
+ send_ssp_cmd(dai, dai->name, 0);
+ sst_handle_vb_timer(dai, false);
+ }
+}
+
+static struct snd_soc_dai_ops sst_media_dai_ops = {
+ .startup = sst_media_open,
+ .shutdown = sst_media_close,
+ .prepare = sst_media_prepare,
+ .hw_params = sst_media_hw_params,
+ .hw_free = sst_media_hw_free,
+ .mute_stream = sst_media_digital_mute,
+};
+
+static struct snd_soc_dai_ops sst_compr_dai_ops = {
+ .mute_stream = sst_media_digital_mute,
+};
+
+static struct snd_soc_dai_ops sst_be_dai_ops = {
+ .startup = sst_enable_ssp,
+ .shutdown = sst_disable_ssp,
+};
+
+static struct snd_soc_dai_driver sst_platform_dai[] = {
+{
+ .name = "media-cpu-dai",
+ .ops = &sst_media_dai_ops,
+ .playback = {
+ .stream_name = "Headset Playback",
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Headset Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
+ .name = "compress-cpu-dai",
+ .compress_dai = 1,
+ .ops = &sst_compr_dai_ops,
+ .playback = {
+ .stream_name = "Compress Playback",
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+/* BE CPU Dais */
+{
+ .name = "ssp0-port",
+ .ops = &sst_be_dai_ops,
+ .playback = {
+ .stream_name = "ssp0 Tx",
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "ssp0 Rx",
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
+ .name = "ssp1-port",
+ .ops = &sst_be_dai_ops,
+ .playback = {
+ .stream_name = "ssp1 Tx",
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "ssp1 Rx",
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
+ .name = "ssp2-port",
+ .ops = &sst_be_dai_ops,
+ .playback = {
+ .stream_name = "ssp2 Tx",
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "ssp2 Rx",
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+};
+
+static int sst_platform_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+
+ if (substream->pcm->internal)
+ return 0;
+
+ runtime = substream->runtime;
+ runtime->hw = sst_platform_pcm_hw;
+ return 0;
+}
+
+static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ int ret_val = 0, str_id;
+ struct sst_runtime_stream *stream;
+ int status;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+ dev_dbg(rtd->dev, "sst_platform_pcm_trigger called\n");
+ if (substream->pcm->internal)
+ return 0;
+ stream = substream->runtime->private_data;
+ str_id = stream->stream_info.str_id;
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ dev_dbg(rtd->dev, "sst: Trigger Start\n");
+ status = SST_PLATFORM_RUNNING;
+ stream->stream_info.arg = substream;
+ ret_val = stream->ops->stream_start(sst->dev, str_id);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ dev_dbg(rtd->dev, "sst: in stop\n");
+ status = SST_PLATFORM_DROPPED;
+ ret_val = stream->ops->stream_drop(sst->dev, str_id);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ dev_dbg(rtd->dev, "sst: in pause\n");
+ status = SST_PLATFORM_PAUSED;
+ ret_val = stream->ops->stream_pause(sst->dev, str_id);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ dev_dbg(rtd->dev, "sst: in pause release\n");
+ status = SST_PLATFORM_RUNNING;
+ ret_val = stream->ops->stream_pause_release(sst->dev, str_id);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!ret_val)
+ sst_set_stream_status(stream, status);
+
+ return ret_val;
+}
+
+
+static snd_pcm_uframes_t sst_platform_pcm_pointer
+ (struct snd_pcm_substream *substream)
+{
+ struct sst_runtime_stream *stream;
+ int ret_val, status;
+ struct pcm_stream_info *str_info;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+ stream = substream->runtime->private_data;
+ status = sst_get_stream_status(stream);
+ if (status == SST_PLATFORM_INIT)
+ return 0;
+ str_info = &stream->stream_info;
+ ret_val = stream->ops->stream_read_tstamp(sst->dev, str_info);
+ if (ret_val) {
+ dev_err(rtd->dev, "sst: error code = %d\n", ret_val);
+ return ret_val;
+ }
+ substream->runtime->delay = str_info->pcm_delay;
+ return str_info->buffer_ptr;
+}
+
+static struct snd_pcm_ops sst_platform_ops = {
+ .open = sst_platform_open,
+ .ioctl = snd_pcm_lib_ioctl,
+ .trigger = sst_platform_pcm_trigger,
+ .pointer = sst_platform_pcm_pointer,
+};
+
+static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct snd_pcm *pcm = rtd->pcm;
+ int retval = 0;
+
+ if (dai->driver->playback.channels_min ||
+ dai->driver->capture.channels_min) {
+ retval = snd_pcm_lib_preallocate_pages_for_all(pcm,
+ SNDRV_DMA_TYPE_CONTINUOUS,
+ snd_dma_continuous_data(GFP_DMA),
+ SST_MIN_BUFFER, SST_MAX_BUFFER);
+ if (retval) {
+ dev_err(rtd->dev, "dma buffer allocationf fail\n");
+ return retval;
+ }
+ }
+ return retval;
+}
+
+static int sst_soc_probe(struct snd_soc_platform *platform)
+{
+ struct sst_data *drv = dev_get_drvdata(platform->dev);
+
+ drv->soc_card = platform->component.card;
+ return sst_dsp_init_v2_dpcm(platform);
+}
+
+static struct snd_soc_platform_driver sst_soc_platform_drv = {
+ .probe = sst_soc_probe,
+ .ops = &sst_platform_ops,
+ .compr_ops = &sst_platform_compr_ops,
+ .pcm_new = sst_pcm_new,
+};
+
+static const struct snd_soc_component_driver sst_component = {
+ .name = "sst",
+};
+
+
+static int sst_platform_probe(struct platform_device *pdev)
+{
+ struct sst_data *drv;
+ int ret;
+ struct sst_platform_data *pdata;
+
+ drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+ if (drv == NULL) {
+ return -ENOMEM;
+ }
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (pdata == NULL) {
+ return -ENOMEM;
+ }
+
+ pdata->pdev_strm_map = dpcm_strm_map;
+ pdata->strm_map_size = ARRAY_SIZE(dpcm_strm_map);
+ drv->pdata = pdata;
+ drv->pdev = pdev;
+ mutex_init(&drv->lock);
+ dev_set_drvdata(&pdev->dev, drv);
+
+ ret = snd_soc_register_platform(&pdev->dev, &sst_soc_platform_drv);
+ if (ret) {
+ dev_err(&pdev->dev, "registering soc platform failed\n");
+ return ret;
+ }
+
+ ret = snd_soc_register_component(&pdev->dev, &sst_component,
+ sst_platform_dai, ARRAY_SIZE(sst_platform_dai));
+ if (ret) {
+ dev_err(&pdev->dev, "registering cpu dais failed\n");
+ snd_soc_unregister_platform(&pdev->dev);
+ }
+ return ret;
+}
+
+static int sst_platform_remove(struct platform_device *pdev)
+{
+
+ snd_soc_unregister_component(&pdev->dev);
+ snd_soc_unregister_platform(&pdev->dev);
+ dev_dbg(&pdev->dev, "sst_platform_remove success\n");
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int sst_soc_prepare(struct device *dev)
+{
+ struct sst_data *drv = dev_get_drvdata(dev);
+ int i;
+
+ /* suspend all pcms first */
+ snd_soc_suspend(drv->soc_card->dev);
+ snd_soc_poweroff(drv->soc_card->dev);
+
+ /* set the SSPs to idle */
+ for (i = 0; i < drv->soc_card->num_rtd; i++) {
+ struct snd_soc_dai *dai = drv->soc_card->rtd[i].cpu_dai;
+
+ if (dai->active) {
+ send_ssp_cmd(dai, dai->name, 0);
+ sst_handle_vb_timer(dai, false);
+ }
+ }
+
+ return 0;
+}
+
+static void sst_soc_complete(struct device *dev)
+{
+ struct sst_data *drv = dev_get_drvdata(dev);
+ int i;
+
+ /* restart SSPs */
+ for (i = 0; i < drv->soc_card->num_rtd; i++) {
+ struct snd_soc_dai *dai = drv->soc_card->rtd[i].cpu_dai;
+
+ if (dai->active) {
+ sst_handle_vb_timer(dai, true);
+ send_ssp_cmd(dai, dai->name, 1);
+ }
+ }
+ snd_soc_resume(drv->soc_card->dev);
+}
+
+#else
+
+#define sst_soc_prepare NULL
+#define sst_soc_complete NULL
+
+#endif
+
+
+static const struct dev_pm_ops sst_platform_pm = {
+ .prepare = sst_soc_prepare,
+ .complete = sst_soc_complete,
+};
+
+static struct platform_driver sst_platform_driver = {
+ .driver = {
+ .name = "sst-mfld-platform",
+ .pm = &sst_platform_pm,
+ },
+ .probe = sst_platform_probe,
+ .remove = sst_platform_remove,
+};
+
+module_platform_driver(sst_platform_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) MID Platform driver");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:sst-mfld-platform");
--- /dev/null
+/*
+ * sst_mfld_platform.h - Intel MID Platform driver header file
+ *
+ * Copyright (C) 2010 Intel Corp
+ * Author: Vinod Koul <vinod.koul@intel.com>
+ * Author: Harsha Priya <priya.harsha@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#ifndef __SST_PLATFORMDRV_H__
+#define __SST_PLATFORMDRV_H__
+
+#include "sst-mfld-dsp.h"
+
+extern struct sst_device *sst;
+
+#define SST_MONO 1
+#define SST_STEREO 2
+#define SST_MAX_CAP 5
+
+#define SST_MAX_BUFFER (800*1024)
+#define SST_MIN_BUFFER (800*1024)
+#define SST_MIN_PERIOD_BYTES 32
+#define SST_MAX_PERIOD_BYTES SST_MAX_BUFFER
+#define SST_MIN_PERIODS 2
+#define SST_MAX_PERIODS (1024*2)
+#define SST_FIFO_SIZE 0
+
+struct pcm_stream_info {
+ int str_id;
+ void *arg;
+ void (*period_elapsed) (void *arg);
+ unsigned long long buffer_ptr;
+ unsigned long long pcm_delay;
+ int sfreq;
+};
+
+enum sst_drv_status {
+ SST_PLATFORM_INIT = 1,
+ SST_PLATFORM_STARTED,
+ SST_PLATFORM_RUNNING,
+ SST_PLATFORM_PAUSED,
+ SST_PLATFORM_DROPPED,
+};
+
+enum sst_stream_ops {
+ STREAM_OPS_PLAYBACK = 0,
+ STREAM_OPS_CAPTURE,
+};
+
+enum sst_audio_device_type {
+ SND_SST_DEVICE_HEADSET = 1,
+ SND_SST_DEVICE_IHF,
+ SND_SST_DEVICE_VIBRA,
+ SND_SST_DEVICE_HAPTIC,
+ SND_SST_DEVICE_CAPTURE,
+ SND_SST_DEVICE_COMPRESS,
+};
+
+/* PCM Parameters */
+struct sst_pcm_params {
+ u16 codec; /* codec type */
+ u8 num_chan; /* 1=Mono, 2=Stereo */
+ u8 pcm_wd_sz; /* 16/24 - bit*/
+ u32 reserved; /* Bitrate in bits per second */
+ u32 sfreq; /* Sampling rate in Hz */
+ u32 ring_buffer_size;
+ u32 period_count; /* period elapsed in samples*/
+ u32 ring_buffer_addr;
+};
+
+struct sst_stream_params {
+ u32 result;
+ u32 stream_id;
+ u8 codec;
+ u8 ops;
+ u8 stream_type;
+ u8 device_type;
+ struct sst_pcm_params sparams;
+};
+
+struct sst_compress_cb {
+ void *param;
+ void (*compr_cb)(void *param);
+ void *drain_cb_param;
+ void (*drain_notify)(void *param);
+};
+
+struct compress_sst_ops {
+ const char *name;
+ int (*open)(struct device *dev,
+ struct snd_sst_params *str_params, struct sst_compress_cb *cb);
+ int (*stream_start)(struct device *dev, unsigned int str_id);
+ int (*stream_drop)(struct device *dev, unsigned int str_id);
+ int (*stream_drain)(struct device *dev, unsigned int str_id);
+ int (*stream_partial_drain)(struct device *dev, unsigned int str_id);
+ int (*stream_pause)(struct device *dev, unsigned int str_id);
+ int (*stream_pause_release)(struct device *dev, unsigned int str_id);
+
+ int (*tstamp)(struct device *dev, unsigned int str_id,
+ struct snd_compr_tstamp *tstamp);
+ int (*ack)(struct device *dev, unsigned int str_id,
+ unsigned long bytes);
+ int (*close)(struct device *dev, unsigned int str_id);
+ int (*get_caps)(struct snd_compr_caps *caps);
+ int (*get_codec_caps)(struct snd_compr_codec_caps *codec);
+ int (*set_metadata)(struct device *dev, unsigned int str_id,
+ struct snd_compr_metadata *mdata);
+ int (*power)(struct device *dev, bool state);
+};
+
+struct sst_ops {
+ int (*open)(struct device *dev, struct snd_sst_params *str_param);
+ int (*stream_init)(struct device *dev, struct pcm_stream_info *str_info);
+ int (*stream_start)(struct device *dev, int str_id);
+ int (*stream_drop)(struct device *dev, int str_id);
+ int (*stream_pause)(struct device *dev, int str_id);
+ int (*stream_pause_release)(struct device *dev, int str_id);
+ int (*stream_read_tstamp)(struct device *dev, struct pcm_stream_info *str_info);
+ int (*send_byte_stream)(struct device *dev, struct snd_sst_bytes_v2 *bytes);
+ int (*close)(struct device *dev, unsigned int str_id);
+ int (*power)(struct device *dev, bool state);
+};
+
+struct sst_runtime_stream {
+ int stream_status;
+ unsigned int id;
+ size_t bytes_written;
+ struct pcm_stream_info stream_info;
+ struct sst_ops *ops;
+ struct compress_sst_ops *compr_ops;
+ spinlock_t status_lock;
+};
+
+struct sst_device {
+ char *name;
+ struct device *dev;
+ struct sst_ops *ops;
+ struct platform_device *pdev;
+ struct compress_sst_ops *compr_ops;
+};
+
+struct sst_data;
+
+int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform);
+int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute);
+int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable);
+int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable);
+
+void sst_set_stream_status(struct sst_runtime_stream *stream, int state);
+int sst_fill_stream_params(void *substream, const struct sst_data *ctx,
+ struct snd_sst_params *str_params, bool is_compress);
+
+struct sst_algo_int_control_v2 {
+ struct soc_mixer_control mc;
+ u16 module_id; /* module identifieer */
+ u16 pipe_id; /* location info: pipe_id + instance_id */
+ u16 instance_id;
+ unsigned int value; /* Value received is stored here */
+};
+struct sst_data {
+ struct platform_device *pdev;
+ struct sst_platform_data *pdata;
+ struct snd_sst_bytes_v2 *byte_stream;
+ struct mutex lock;
+ struct snd_soc_card *soc_card;
+};
+int sst_register_dsp(struct sst_device *sst);
+int sst_unregister_dsp(struct sst_device *sst);
+#endif
--- /dev/null
+snd-intel-sst-core-objs := sst.o sst_ipc.o sst_stream.o sst_drv_interface.o sst_loader.o sst_pvt.o
+snd-intel-sst-pci-objs += sst_pci.o
+snd-intel-sst-acpi-objs += sst_acpi.o
+
+obj-$(CONFIG_SND_SST_IPC) += snd-intel-sst-core.o
+obj-$(CONFIG_SND_SST_IPC_PCI) += snd-intel-sst-pci.o
+obj-$(CONFIG_SND_SST_IPC_ACPI) += snd-intel-sst-acpi.o
--- /dev/null
+/*
+ * sst.c - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-14 Intel Corp
+ * Authors: Vinod Koul <vinod.koul@intel.com>
+ * Harsha Priya <priya.harsha@intel.com>
+ * Dharageswari R <dharageswari.r@intel.com>
+ * KP Jeeja <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/async.h>
+#include <linux/acpi.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../../common/sst-dsp.h"
+
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine Driver");
+MODULE_LICENSE("GPL v2");
+
+static inline bool sst_is_process_reply(u32 msg_id)
+{
+ return ((msg_id & PROCESS_MSG) ? true : false);
+}
+
+static inline bool sst_validate_mailbox_size(unsigned int size)
+{
+ return ((size <= SST_MAILBOX_SIZE) ? true : false);
+}
+
+static irqreturn_t intel_sst_interrupt_mrfld(int irq, void *context)
+{
+ union interrupt_reg_mrfld isr;
+ union ipc_header_mrfld header;
+ union sst_imr_reg_mrfld imr;
+ struct ipc_post *msg = NULL;
+ unsigned int size = 0;
+ struct intel_sst_drv *drv = (struct intel_sst_drv *) context;
+ irqreturn_t retval = IRQ_HANDLED;
+
+ /* Interrupt arrived, check src */
+ isr.full = sst_shim_read64(drv->shim, SST_ISRX);
+
+ if (isr.part.done_interrupt) {
+ /* Clear done bit */
+ spin_lock(&drv->ipc_spin_lock);
+ header.full = sst_shim_read64(drv->shim,
+ drv->ipc_reg.ipcx);
+ header.p.header_high.part.done = 0;
+ sst_shim_write64(drv->shim, drv->ipc_reg.ipcx, header.full);
+
+ /* write 1 to clear status register */;
+ isr.part.done_interrupt = 1;
+ sst_shim_write64(drv->shim, SST_ISRX, isr.full);
+ spin_unlock(&drv->ipc_spin_lock);
+
+ /* we can send more messages to DSP so trigger work */
+ queue_work(drv->post_msg_wq, &drv->ipc_post_msg_wq);
+ retval = IRQ_HANDLED;
+ }
+
+ if (isr.part.busy_interrupt) {
+ /* message from dsp so copy that */
+ spin_lock(&drv->ipc_spin_lock);
+ imr.full = sst_shim_read64(drv->shim, SST_IMRX);
+ imr.part.busy_interrupt = 1;
+ sst_shim_write64(drv->shim, SST_IMRX, imr.full);
+ spin_unlock(&drv->ipc_spin_lock);
+ header.full = sst_shim_read64(drv->shim, drv->ipc_reg.ipcd);
+
+ if (sst_create_ipc_msg(&msg, header.p.header_high.part.large)) {
+ drv->ops->clear_interrupt(drv);
+ return IRQ_HANDLED;
+ }
+
+ if (header.p.header_high.part.large) {
+ size = header.p.header_low_payload;
+ if (sst_validate_mailbox_size(size)) {
+ memcpy_fromio(msg->mailbox_data,
+ drv->mailbox + drv->mailbox_recv_offset, size);
+ } else {
+ dev_err(drv->dev,
+ "Mailbox not copied, payload size is: %u\n", size);
+ header.p.header_low_payload = 0;
+ }
+ }
+
+ msg->mrfld_header = header;
+ msg->is_process_reply =
+ sst_is_process_reply(header.p.header_high.part.msg_id);
+ spin_lock(&drv->rx_msg_lock);
+ list_add_tail(&msg->node, &drv->rx_list);
+ spin_unlock(&drv->rx_msg_lock);
+ drv->ops->clear_interrupt(drv);
+ retval = IRQ_WAKE_THREAD;
+ }
+ return retval;
+}
+
+static irqreturn_t intel_sst_irq_thread_mrfld(int irq, void *context)
+{
+ struct intel_sst_drv *drv = (struct intel_sst_drv *) context;
+ struct ipc_post *__msg, *msg = NULL;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&drv->rx_msg_lock, irq_flags);
+ if (list_empty(&drv->rx_list)) {
+ spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
+ return IRQ_HANDLED;
+ }
+
+ list_for_each_entry_safe(msg, __msg, &drv->rx_list, node) {
+ list_del(&msg->node);
+ spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
+ if (msg->is_process_reply)
+ drv->ops->process_message(msg);
+ else
+ drv->ops->process_reply(drv, msg);
+
+ if (msg->is_large)
+ kfree(msg->mailbox_data);
+ kfree(msg);
+ spin_lock_irqsave(&drv->rx_msg_lock, irq_flags);
+ }
+ spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
+ return IRQ_HANDLED;
+}
+
+static int sst_save_dsp_context_v2(struct intel_sst_drv *sst)
+{
+ int ret = 0;
+
+ ret = sst_prepare_and_post_msg(sst, SST_TASK_ID_MEDIA, IPC_CMD,
+ IPC_PREP_D3, PIPE_RSVD, 0, NULL, NULL,
+ true, true, false, true);
+
+ if (ret < 0) {
+ dev_err(sst->dev, "not suspending FW!!, Err: %d\n", ret);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+static struct intel_sst_ops mrfld_ops = {
+ .interrupt = intel_sst_interrupt_mrfld,
+ .irq_thread = intel_sst_irq_thread_mrfld,
+ .clear_interrupt = intel_sst_clear_intr_mrfld,
+ .start = sst_start_mrfld,
+ .reset = intel_sst_reset_dsp_mrfld,
+ .post_message = sst_post_message_mrfld,
+ .process_reply = sst_process_reply_mrfld,
+ .save_dsp_context = sst_save_dsp_context_v2,
+ .alloc_stream = sst_alloc_stream_mrfld,
+ .post_download = sst_post_download_mrfld,
+};
+
+int sst_driver_ops(struct intel_sst_drv *sst)
+{
+
+ switch (sst->dev_id) {
+ case SST_MRFLD_PCI_ID:
+ case SST_BYT_ACPI_ID:
+ case SST_CHV_ACPI_ID:
+ sst->tstamp = SST_TIME_STAMP_MRFLD;
+ sst->ops = &mrfld_ops;
+ return 0;
+
+ default:
+ dev_err(sst->dev,
+ "SST Driver capablities missing for dev_id: %x", sst->dev_id);
+ return -EINVAL;
+ };
+}
+
+void sst_process_pending_msg(struct work_struct *work)
+{
+ struct intel_sst_drv *ctx = container_of(work,
+ struct intel_sst_drv, ipc_post_msg_wq);
+
+ ctx->ops->post_message(ctx, NULL, false);
+}
+
+static int sst_workqueue_init(struct intel_sst_drv *ctx)
+{
+ INIT_LIST_HEAD(&ctx->memcpy_list);
+ INIT_LIST_HEAD(&ctx->rx_list);
+ INIT_LIST_HEAD(&ctx->ipc_dispatch_list);
+ INIT_LIST_HEAD(&ctx->block_list);
+ INIT_WORK(&ctx->ipc_post_msg_wq, sst_process_pending_msg);
+ init_waitqueue_head(&ctx->wait_queue);
+
+ ctx->post_msg_wq =
+ create_singlethread_workqueue("sst_post_msg_wq");
+ if (!ctx->post_msg_wq)
+ return -EBUSY;
+ return 0;
+}
+
+static void sst_init_locks(struct intel_sst_drv *ctx)
+{
+ mutex_init(&ctx->sst_lock);
+ spin_lock_init(&ctx->rx_msg_lock);
+ spin_lock_init(&ctx->ipc_spin_lock);
+ spin_lock_init(&ctx->block_lock);
+}
+
+int sst_alloc_drv_context(struct intel_sst_drv **ctx,
+ struct device *dev, unsigned int dev_id)
+{
+ *ctx = devm_kzalloc(dev, sizeof(struct intel_sst_drv), GFP_KERNEL);
+ if (!(*ctx))
+ return -ENOMEM;
+
+ (*ctx)->dev = dev;
+ (*ctx)->dev_id = dev_id;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_alloc_drv_context);
+
+int sst_context_init(struct intel_sst_drv *ctx)
+{
+ int ret = 0, i;
+
+ if (!ctx->pdata)
+ return -EINVAL;
+
+ if (!ctx->pdata->probe_data)
+ return -EINVAL;
+
+ memcpy(&ctx->info, ctx->pdata->probe_data, sizeof(ctx->info));
+
+ ret = sst_driver_ops(ctx);
+ if (ret != 0)
+ return -EINVAL;
+
+ sst_init_locks(ctx);
+ sst_set_fw_state_locked(ctx, SST_RESET);
+
+ /* pvt_id 0 reserved for async messages */
+ ctx->pvt_id = 1;
+ ctx->stream_cnt = 0;
+ ctx->fw_in_mem = NULL;
+ /* we use memcpy, so set to 0 */
+ ctx->use_dma = 0;
+ ctx->use_lli = 0;
+
+ if (sst_workqueue_init(ctx))
+ return -EINVAL;
+
+ ctx->mailbox_recv_offset = ctx->pdata->ipc_info->mbox_recv_off;
+ ctx->ipc_reg.ipcx = SST_IPCX + ctx->pdata->ipc_info->ipc_offset;
+ ctx->ipc_reg.ipcd = SST_IPCD + ctx->pdata->ipc_info->ipc_offset;
+
+ dev_info(ctx->dev, "Got drv data max stream %d\n",
+ ctx->info.max_streams);
+
+ for (i = 1; i <= ctx->info.max_streams; i++) {
+ struct stream_info *stream = &ctx->streams[i];
+
+ memset(stream, 0, sizeof(*stream));
+ stream->pipe_id = PIPE_RSVD;
+ mutex_init(&stream->lock);
+ }
+
+ /* Register the ISR */
+ ret = devm_request_threaded_irq(ctx->dev, ctx->irq_num, ctx->ops->interrupt,
+ ctx->ops->irq_thread, 0, SST_DRV_NAME,
+ ctx);
+ if (ret)
+ goto do_free_mem;
+
+ dev_dbg(ctx->dev, "Registered IRQ %#x\n", ctx->irq_num);
+
+ /* default intr are unmasked so set this as masked */
+ sst_shim_write64(ctx->shim, SST_IMRX, 0xFFFF0038);
+
+ ctx->qos = devm_kzalloc(ctx->dev,
+ sizeof(struct pm_qos_request), GFP_KERNEL);
+ if (!ctx->qos) {
+ ret = -ENOMEM;
+ goto do_free_mem;
+ }
+ pm_qos_add_request(ctx->qos, PM_QOS_CPU_DMA_LATENCY,
+ PM_QOS_DEFAULT_VALUE);
+
+ dev_dbg(ctx->dev, "Requesting FW %s now...\n", ctx->firmware_name);
+ ret = request_firmware_nowait(THIS_MODULE, true, ctx->firmware_name,
+ ctx->dev, GFP_KERNEL, ctx, sst_firmware_load_cb);
+ if (ret) {
+ dev_err(ctx->dev, "Firmware download failed:%d\n", ret);
+ goto do_free_mem;
+ }
+ sst_register(ctx->dev);
+ return 0;
+
+do_free_mem:
+ destroy_workqueue(ctx->post_msg_wq);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sst_context_init);
+
+void sst_context_cleanup(struct intel_sst_drv *ctx)
+{
+ pm_runtime_get_noresume(ctx->dev);
+ pm_runtime_disable(ctx->dev);
+ sst_unregister(ctx->dev);
+ sst_set_fw_state_locked(ctx, SST_SHUTDOWN);
+ flush_scheduled_work();
+ destroy_workqueue(ctx->post_msg_wq);
+ pm_qos_remove_request(ctx->qos);
+ kfree(ctx->fw_sg_list.src);
+ kfree(ctx->fw_sg_list.dst);
+ ctx->fw_sg_list.list_len = 0;
+ kfree(ctx->fw_in_mem);
+ ctx->fw_in_mem = NULL;
+ sst_memcpy_free_resources(ctx);
+ ctx = NULL;
+}
+EXPORT_SYMBOL_GPL(sst_context_cleanup);
+
+static inline void sst_save_shim64(struct intel_sst_drv *ctx,
+ void __iomem *shim,
+ struct sst_shim_regs64 *shim_regs)
+{
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags);
+
+ shim_regs->imrx = sst_shim_read64(shim, SST_IMRX);
+ shim_regs->csr = sst_shim_read64(shim, SST_CSR);
+
+
+ spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags);
+}
+
+static inline void sst_restore_shim64(struct intel_sst_drv *ctx,
+ void __iomem *shim,
+ struct sst_shim_regs64 *shim_regs)
+{
+ unsigned long irq_flags;
+
+ /*
+ * we only need to restore IMRX for this case, rest will be
+ * initialize by FW or driver when firmware is loaded
+ */
+ spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags);
+ sst_shim_write64(shim, SST_IMRX, shim_regs->imrx),
+ sst_shim_write64(shim, SST_CSR, shim_regs->csr),
+ spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags);
+}
+
+void sst_configure_runtime_pm(struct intel_sst_drv *ctx)
+{
+ pm_runtime_set_autosuspend_delay(ctx->dev, SST_SUSPEND_DELAY);
+ pm_runtime_use_autosuspend(ctx->dev);
+ /*
+ * For acpi devices, the actual physical device state is
+ * initially active. So change the state to active before
+ * enabling the pm
+ */
+
+ if (!acpi_disabled)
+ pm_runtime_set_active(ctx->dev);
+
+ pm_runtime_enable(ctx->dev);
+
+ if (acpi_disabled)
+ pm_runtime_set_active(ctx->dev);
+ else
+ pm_runtime_put_noidle(ctx->dev);
+
+ sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64);
+}
+EXPORT_SYMBOL_GPL(sst_configure_runtime_pm);
+
+static int intel_sst_runtime_suspend(struct device *dev)
+{
+ int ret = 0;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ if (ctx->sst_state == SST_RESET) {
+ dev_dbg(dev, "LPE is already in RESET state, No action\n");
+ return 0;
+ }
+ /* save fw context */
+ if (ctx->ops->save_dsp_context(ctx))
+ return -EBUSY;
+
+ /* Move the SST state to Reset */
+ sst_set_fw_state_locked(ctx, SST_RESET);
+
+ synchronize_irq(ctx->irq_num);
+ flush_workqueue(ctx->post_msg_wq);
+
+ ctx->ops->reset(ctx);
+ /* save the shim registers because PMC doesn't save state */
+ sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64);
+
+ return ret;
+}
+
+static int intel_sst_suspend(struct device *dev)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+ struct sst_fw_save *fw_save;
+ int i, ret = 0;
+
+ /* check first if we are already in SW reset */
+ if (ctx->sst_state == SST_RESET)
+ return 0;
+
+ /*
+ * check if any stream is active and running
+ * they should already by suspend by soc_suspend
+ */
+ for (i = 1; i <= ctx->info.max_streams; i++) {
+ struct stream_info *stream = &ctx->streams[i];
+
+ if (stream->status == STREAM_RUNNING) {
+ dev_err(dev, "stream %d is running, cant susupend, abort\n", i);
+ return -EBUSY;
+ }
+ }
+ synchronize_irq(ctx->irq_num);
+ flush_workqueue(ctx->post_msg_wq);
+
+ /* Move the SST state to Reset */
+ sst_set_fw_state_locked(ctx, SST_RESET);
+
+ /* tell DSP we are suspending */
+ if (ctx->ops->save_dsp_context(ctx))
+ return -EBUSY;
+
+ /* save the memories */
+ fw_save = kzalloc(sizeof(*fw_save), GFP_KERNEL);
+ if (!fw_save)
+ return -ENOMEM;
+ fw_save->iram = kzalloc(ctx->iram_end - ctx->iram_base, GFP_KERNEL);
+ if (!fw_save->iram) {
+ ret = -ENOMEM;
+ goto iram;
+ }
+ fw_save->dram = kzalloc(ctx->dram_end - ctx->dram_base, GFP_KERNEL);
+ if (!fw_save->dram) {
+ ret = -ENOMEM;
+ goto dram;
+ }
+ fw_save->sram = kzalloc(SST_MAILBOX_SIZE, GFP_KERNEL);
+ if (!fw_save->sram) {
+ ret = -ENOMEM;
+ goto sram;
+ }
+
+ fw_save->ddr = kzalloc(ctx->ddr_end - ctx->ddr_base, GFP_KERNEL);
+ if (!fw_save->ddr) {
+ ret = -ENOMEM;
+ goto ddr;
+ }
+
+ memcpy32_fromio(fw_save->iram, ctx->iram, ctx->iram_end - ctx->iram_base);
+ memcpy32_fromio(fw_save->dram, ctx->dram, ctx->dram_end - ctx->dram_base);
+ memcpy32_fromio(fw_save->sram, ctx->mailbox, SST_MAILBOX_SIZE);
+ memcpy32_fromio(fw_save->ddr, ctx->ddr, ctx->ddr_end - ctx->ddr_base);
+
+ ctx->fw_save = fw_save;
+ ctx->ops->reset(ctx);
+ return 0;
+ddr:
+ kfree(fw_save->sram);
+sram:
+ kfree(fw_save->dram);
+dram:
+ kfree(fw_save->iram);
+iram:
+ kfree(fw_save);
+ return ret;
+}
+
+static int intel_sst_resume(struct device *dev)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+ struct sst_fw_save *fw_save = ctx->fw_save;
+ int ret = 0;
+ struct sst_block *block;
+
+ if (!fw_save)
+ return 0;
+
+ sst_set_fw_state_locked(ctx, SST_FW_LOADING);
+
+ /* we have to restore the memory saved */
+ ctx->ops->reset(ctx);
+
+ ctx->fw_save = NULL;
+
+ memcpy32_toio(ctx->iram, fw_save->iram, ctx->iram_end - ctx->iram_base);
+ memcpy32_toio(ctx->dram, fw_save->dram, ctx->dram_end - ctx->dram_base);
+ memcpy32_toio(ctx->mailbox, fw_save->sram, SST_MAILBOX_SIZE);
+ memcpy32_toio(ctx->ddr, fw_save->ddr, ctx->ddr_end - ctx->ddr_base);
+
+ kfree(fw_save->sram);
+ kfree(fw_save->dram);
+ kfree(fw_save->iram);
+ kfree(fw_save->ddr);
+ kfree(fw_save);
+
+ block = sst_create_block(ctx, 0, FW_DWNL_ID);
+ if (block == NULL)
+ return -ENOMEM;
+
+
+ /* start and wait for ack */
+ ctx->ops->start(ctx);
+ ret = sst_wait_timeout(ctx, block);
+ if (ret) {
+ dev_err(ctx->dev, "fw download failed %d\n", ret);
+ /* FW download failed due to timeout */
+ ret = -EBUSY;
+
+ } else {
+ sst_set_fw_state_locked(ctx, SST_FW_RUNNING);
+ }
+
+ sst_free_block(ctx, block);
+ return ret;
+}
+
+const struct dev_pm_ops intel_sst_pm = {
+ .suspend = intel_sst_suspend,
+ .resume = intel_sst_resume,
+ .runtime_suspend = intel_sst_runtime_suspend,
+};
+EXPORT_SYMBOL_GPL(intel_sst_pm);
--- /dev/null
+/*
+ * sst.h - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-14 Intel Corporation
+ * Authors: Vinod Koul <vinod.koul@intel.com>
+ * Harsha Priya <priya.harsha@intel.com>
+ * Dharageswari R <dharageswari.r@intel.com>
+ * KP Jeeja <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Common private declarations for SST
+ */
+#ifndef __SST_H__
+#define __SST_H__
+
+#include <linux/firmware.h>
+
+/* driver names */
+#define SST_DRV_NAME "intel_sst_driver"
+#define SST_MRFLD_PCI_ID 0x119A
+#define SST_BYT_ACPI_ID 0x80860F28
+#define SST_CHV_ACPI_ID 0x808622A8
+
+#define SST_SUSPEND_DELAY 2000
+#define FW_CONTEXT_MEM (64*1024)
+#define SST_ICCM_BOUNDARY 4
+#define SST_CONFIG_SSP_SIGN 0x7ffe8001
+
+#define MRFLD_FW_VIRTUAL_BASE 0xC0000000
+#define MRFLD_FW_DDR_BASE_OFFSET 0x0
+#define MRFLD_FW_FEATURE_BASE_OFFSET 0x4
+#define MRFLD_FW_BSS_RESET_BIT 0
+
+extern const struct dev_pm_ops intel_sst_pm;
+enum sst_states {
+ SST_FW_LOADING = 1,
+ SST_FW_RUNNING,
+ SST_RESET,
+ SST_SHUTDOWN,
+};
+
+enum sst_algo_ops {
+ SST_SET_ALGO = 0,
+ SST_GET_ALGO = 1,
+};
+
+#define SST_BLOCK_TIMEOUT 1000
+
+#define FW_SIGNATURE_SIZE 4
+#define FW_NAME_SIZE 32
+
+/* stream states */
+enum sst_stream_states {
+ STREAM_UN_INIT = 0, /* Freed/Not used stream */
+ STREAM_RUNNING = 1, /* Running */
+ STREAM_PAUSED = 2, /* Paused stream */
+ STREAM_DECODE = 3, /* stream is in decoding only state */
+ STREAM_INIT = 4, /* stream init, waiting for data */
+ STREAM_RESET = 5, /* force reset on recovery */
+};
+
+enum sst_ram_type {
+ SST_IRAM = 1,
+ SST_DRAM = 2,
+ SST_DDR = 5,
+ SST_CUSTOM_INFO = 7, /* consists of FW binary information */
+};
+
+/* SST shim registers to structure mapping */
+union interrupt_reg {
+ struct {
+ u64 done_interrupt:1;
+ u64 busy_interrupt:1;
+ u64 rsvd:62;
+ } part;
+ u64 full;
+};
+
+union sst_pisr_reg {
+ struct {
+ u32 pssp0:1;
+ u32 pssp1:1;
+ u32 rsvd0:3;
+ u32 dmac:1;
+ u32 rsvd1:26;
+ } part;
+ u32 full;
+};
+
+union sst_pimr_reg {
+ struct {
+ u32 ssp0:1;
+ u32 ssp1:1;
+ u32 rsvd0:3;
+ u32 dmac:1;
+ u32 rsvd1:10;
+ u32 ssp0_sc:1;
+ u32 ssp1_sc:1;
+ u32 rsvd2:3;
+ u32 dmac_sc:1;
+ u32 rsvd3:10;
+ } part;
+ u32 full;
+};
+
+union config_status_reg_mrfld {
+ struct {
+ u64 lpe_reset:1;
+ u64 lpe_reset_vector:1;
+ u64 runstall:1;
+ u64 pwaitmode:1;
+ u64 clk_sel:3;
+ u64 rsvd2:1;
+ u64 sst_clk:3;
+ u64 xt_snoop:1;
+ u64 rsvd3:4;
+ u64 clk_sel1:6;
+ u64 clk_enable:3;
+ u64 rsvd4:6;
+ u64 slim0baseclk:1;
+ u64 rsvd:32;
+ } part;
+ u64 full;
+};
+
+union interrupt_reg_mrfld {
+ struct {
+ u64 done_interrupt:1;
+ u64 busy_interrupt:1;
+ u64 rsvd:62;
+ } part;
+ u64 full;
+};
+
+union sst_imr_reg_mrfld {
+ struct {
+ u64 done_interrupt:1;
+ u64 busy_interrupt:1;
+ u64 rsvd:62;
+ } part;
+ u64 full;
+};
+
+/**
+ * struct sst_block - This structure is used to block a user/fw data call to another
+ * fw/user call
+ *
+ * @condition: condition for blocking check
+ * @ret_code: ret code when block is released
+ * @data: data ptr
+ * @size: size of data
+ * @on: block condition
+ * @msg_id: msg_id = msgid in mfld/ctp, mrfld = NULL
+ * @drv_id: str_id in mfld/ctp, = drv_id in mrfld
+ * @node: list head node
+ */
+struct sst_block {
+ bool condition;
+ int ret_code;
+ void *data;
+ u32 size;
+ bool on;
+ u32 msg_id;
+ u32 drv_id;
+ struct list_head node;
+};
+
+/**
+ * struct stream_info - structure that holds the stream information
+ *
+ * @status : stream current state
+ * @prev : stream prev state
+ * @ops : stream operation pb/cp/drm...
+ * @bufs: stream buffer list
+ * @lock : stream mutex for protecting state
+ * @pcm_substream : PCM substream
+ * @period_elapsed : PCM period elapsed callback
+ * @sfreq : stream sampling freq
+ * @str_type : stream type
+ * @cumm_bytes : cummulative bytes decoded
+ * @str_type : stream type
+ * @src : stream source
+ */
+struct stream_info {
+ unsigned int status;
+ unsigned int prev;
+ unsigned int ops;
+ struct mutex lock;
+
+ void *pcm_substream;
+ void (*period_elapsed)(void *pcm_substream);
+
+ unsigned int sfreq;
+ u32 cumm_bytes;
+
+ void *compr_cb_param;
+ void (*compr_cb)(void *compr_cb_param);
+
+ void *drain_cb_param;
+ void (*drain_notify)(void *drain_cb_param);
+
+ unsigned int num_ch;
+ unsigned int pipe_id;
+ unsigned int str_id;
+ unsigned int task_id;
+};
+
+#define SST_FW_SIGN "$SST"
+#define SST_FW_LIB_SIGN "$LIB"
+
+/**
+ * struct sst_fw_header - FW file headers
+ *
+ * @signature : FW signature
+ * @file_size: size of fw image
+ * @modules : # of modules
+ * @file_format : version of header format
+ * @reserved : reserved fields
+ */
+struct sst_fw_header {
+ unsigned char signature[FW_SIGNATURE_SIZE];
+ u32 file_size;
+ u32 modules;
+ u32 file_format;
+ u32 reserved[4];
+};
+
+/**
+ * struct fw_module_header - module header in FW
+ *
+ * @signature: module signature
+ * @mod_size: size of module
+ * @blocks: block count
+ * @type: block type
+ * @entry_point: module netry point
+ */
+struct fw_module_header {
+ unsigned char signature[FW_SIGNATURE_SIZE];
+ u32 mod_size;
+ u32 blocks;
+ u32 type;
+ u32 entry_point;
+};
+
+/**
+ * struct fw_block_info - block header for FW
+ *
+ * @type: block ram type I/D
+ * @size: size of block
+ * @ram_offset: offset in ram
+ */
+struct fw_block_info {
+ enum sst_ram_type type;
+ u32 size;
+ u32 ram_offset;
+ u32 rsvd;
+};
+
+struct sst_runtime_param {
+ struct snd_sst_runtime_params param;
+};
+
+struct sst_sg_list {
+ struct scatterlist *src;
+ struct scatterlist *dst;
+ int list_len;
+ unsigned int sg_idx;
+};
+
+struct sst_memcpy_list {
+ struct list_head memcpylist;
+ void *dstn;
+ const void *src;
+ u32 size;
+ bool is_io;
+};
+
+/*Firmware Module Information*/
+enum sst_lib_dwnld_status {
+ SST_LIB_NOT_FOUND = 0,
+ SST_LIB_FOUND,
+ SST_LIB_DOWNLOADED,
+};
+
+struct sst_module_info {
+ const char *name; /*Library name*/
+ u32 id; /*Module ID*/
+ u32 entry_pt; /*Module entry point*/
+ u8 status; /*module status*/
+ u8 rsvd1;
+ u16 rsvd2;
+};
+
+/*
+ * Structure for managing the Library Region(1.5MB)
+ * in DDR in Merrifield
+ */
+struct sst_mem_mgr {
+ phys_addr_t current_base;
+ int avail;
+ unsigned int count;
+};
+
+struct sst_ipc_reg {
+ int ipcx;
+ int ipcd;
+};
+
+struct sst_shim_regs64 {
+ u64 csr;
+ u64 pisr;
+ u64 pimr;
+ u64 isrx;
+ u64 isrd;
+ u64 imrx;
+ u64 imrd;
+ u64 ipcx;
+ u64 ipcd;
+ u64 isrsc;
+ u64 isrlpesc;
+ u64 imrsc;
+ u64 imrlpesc;
+ u64 ipcsc;
+ u64 ipclpesc;
+ u64 clkctl;
+ u64 csr2;
+};
+
+struct sst_fw_save {
+ void *iram;
+ void *dram;
+ void *sram;
+ void *ddr;
+};
+
+/**
+ * struct intel_sst_drv - driver ops
+ *
+ * @sst_state : current sst device state
+ * @dev_id : device identifier, pci_id for pci devices and acpi_id for acpi
+ * devices
+ * @shim : SST shim pointer
+ * @mailbox : SST mailbox pointer
+ * @iram : SST IRAM pointer
+ * @dram : SST DRAM pointer
+ * @pdata : SST info passed as a part of pci platform data
+ * @shim_phy_add : SST shim phy addr
+ * @shim_regs64: Struct to save shim registers
+ * @ipc_dispatch_list : ipc messages dispatched
+ * @rx_list : to copy the process_reply/process_msg from DSP
+ * @ipc_post_msg_wq : wq to post IPC messages context
+ * @mad_ops : MAD driver operations registered
+ * @mad_wq : MAD driver wq
+ * @post_msg_wq : wq to post IPC messages
+ * @streams : sst stream contexts
+ * @list_lock : sst driver list lock (deprecated)
+ * @ipc_spin_lock : spin lock to handle audio shim access and ipc queue
+ * @block_lock : spin lock to add block to block_list and assign pvt_id
+ * @rx_msg_lock : spin lock to handle the rx messages from the DSP
+ * @scard_ops : sst card ops
+ * @pci : sst pci device struture
+ * @dev : pointer to current device struct
+ * @sst_lock : sst device lock
+ * @pvt_id : sst private id
+ * @stream_cnt : total sst active stream count
+ * @pb_streams : total active pb streams
+ * @cp_streams : total active cp streams
+ * @audio_start : audio status
+ * @qos : PM Qos struct
+ * firmware_name : Firmware / Library name
+ */
+struct intel_sst_drv {
+ int sst_state;
+ int irq_num;
+ unsigned int dev_id;
+ void __iomem *ddr;
+ void __iomem *shim;
+ void __iomem *mailbox;
+ void __iomem *iram;
+ void __iomem *dram;
+ unsigned int mailbox_add;
+ unsigned int iram_base;
+ unsigned int dram_base;
+ unsigned int shim_phy_add;
+ unsigned int iram_end;
+ unsigned int dram_end;
+ unsigned int ddr_end;
+ unsigned int ddr_base;
+ unsigned int mailbox_recv_offset;
+ struct sst_shim_regs64 *shim_regs64;
+ struct list_head block_list;
+ struct list_head ipc_dispatch_list;
+ struct sst_platform_info *pdata;
+ struct list_head rx_list;
+ struct work_struct ipc_post_msg_wq;
+ wait_queue_head_t wait_queue;
+ struct workqueue_struct *post_msg_wq;
+ unsigned int tstamp;
+ /* str_id 0 is not used */
+ struct stream_info streams[MAX_NUM_STREAMS+1];
+ spinlock_t ipc_spin_lock;
+ spinlock_t block_lock;
+ spinlock_t rx_msg_lock;
+ struct pci_dev *pci;
+ struct device *dev;
+ volatile long unsigned pvt_id;
+ struct mutex sst_lock;
+ unsigned int stream_cnt;
+ unsigned int csr_value;
+ void *fw_in_mem;
+ struct sst_sg_list fw_sg_list, library_list;
+ struct intel_sst_ops *ops;
+ struct sst_info info;
+ struct pm_qos_request *qos;
+ unsigned int use_dma;
+ unsigned int use_lli;
+ atomic_t fw_clear_context;
+ bool lib_dwnld_reqd;
+ struct list_head memcpy_list;
+ struct sst_ipc_reg ipc_reg;
+ struct sst_mem_mgr lib_mem_mgr;
+ /*
+ * Holder for firmware name. Due to async call it needs to be
+ * persistent till worker thread gets called
+ */
+ char firmware_name[FW_NAME_SIZE];
+
+ struct sst_fw_save *fw_save;
+};
+
+/* misc definitions */
+#define FW_DWNL_ID 0x01
+
+struct intel_sst_ops {
+ irqreturn_t (*interrupt)(int, void *);
+ irqreturn_t (*irq_thread)(int, void *);
+ void (*clear_interrupt)(struct intel_sst_drv *ctx);
+ int (*start)(struct intel_sst_drv *ctx);
+ int (*reset)(struct intel_sst_drv *ctx);
+ void (*process_reply)(struct intel_sst_drv *ctx, struct ipc_post *msg);
+ int (*post_message)(struct intel_sst_drv *ctx,
+ struct ipc_post *msg, bool sync);
+ void (*process_message)(struct ipc_post *msg);
+ void (*set_bypass)(bool set);
+ int (*save_dsp_context)(struct intel_sst_drv *sst);
+ void (*restore_dsp_context)(void);
+ int (*alloc_stream)(struct intel_sst_drv *ctx, void *params);
+ void (*post_download)(struct intel_sst_drv *sst);
+};
+
+int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id);
+int sst_send_byte_stream_mrfld(struct intel_sst_drv *ctx,
+ struct snd_sst_bytes_v2 *sbytes);
+int sst_set_stream_param(int str_id, struct snd_sst_params *str_param);
+int sst_set_metadata(int str_id, char *params);
+int sst_get_stream(struct intel_sst_drv *sst_drv_ctx,
+ struct snd_sst_params *str_param);
+int sst_get_stream_allocated(struct intel_sst_drv *ctx,
+ struct snd_sst_params *str_param,
+ struct snd_sst_lib_download **lib_dnld);
+int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx,
+ int str_id, bool partial_drain);
+int sst_post_message_mrfld(struct intel_sst_drv *ctx,
+ struct ipc_post *msg, bool sync);
+void sst_process_reply_mrfld(struct intel_sst_drv *ctx, struct ipc_post *msg);
+int sst_start_mrfld(struct intel_sst_drv *ctx);
+int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *ctx);
+void intel_sst_clear_intr_mrfld(struct intel_sst_drv *ctx);
+
+int sst_load_fw(struct intel_sst_drv *ctx);
+int sst_load_library(struct snd_sst_lib_download *lib, u8 ops);
+void sst_post_download_mrfld(struct intel_sst_drv *ctx);
+int sst_get_block_stream(struct intel_sst_drv *sst_drv_ctx);
+void sst_memcpy_free_resources(struct intel_sst_drv *ctx);
+
+int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx,
+ struct sst_block *block);
+int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx,
+ struct sst_block *block);
+int sst_create_ipc_msg(struct ipc_post **arg, bool large);
+int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id);
+void sst_clean_stream(struct stream_info *stream);
+int intel_sst_register_compress(struct intel_sst_drv *sst);
+int intel_sst_remove_compress(struct intel_sst_drv *sst);
+void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id);
+int sst_send_sync_msg(int ipc, int str_id);
+int sst_get_num_channel(struct snd_sst_params *str_param);
+int sst_get_sfreq(struct snd_sst_params *str_param);
+int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params);
+void sst_restore_fw_context(void);
+struct sst_block *sst_create_block(struct intel_sst_drv *ctx,
+ u32 msg_id, u32 drv_id);
+int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large,
+ struct intel_sst_drv *sst_drv_ctx, struct sst_block **block,
+ u32 msg_id, u32 drv_id);
+int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed);
+int sst_wake_up_block(struct intel_sst_drv *ctx, int result,
+ u32 drv_id, u32 ipc, void *data, u32 size);
+int sst_request_firmware_async(struct intel_sst_drv *ctx);
+int sst_driver_ops(struct intel_sst_drv *sst);
+struct sst_platform_info *sst_get_acpi_driver_data(const char *hid);
+void sst_firmware_load_cb(const struct firmware *fw, void *context);
+int sst_prepare_and_post_msg(struct intel_sst_drv *sst,
+ int task_id, int ipc_msg, int cmd_id, int pipe_id,
+ size_t mbox_data_len, const void *mbox_data, void **data,
+ bool large, bool fill_dsp, bool sync, bool response);
+
+void sst_process_pending_msg(struct work_struct *work);
+int sst_assign_pvt_id(struct intel_sst_drv *sst_drv_ctx);
+void sst_init_stream(struct stream_info *stream,
+ int codec, int sst_id, int ops, u8 slot);
+int sst_validate_strid(struct intel_sst_drv *sst_drv_ctx, int str_id);
+struct stream_info *get_stream_info(struct intel_sst_drv *sst_drv_ctx,
+ int str_id);
+int get_stream_id_mrfld(struct intel_sst_drv *sst_drv_ctx,
+ u32 pipe_id);
+u32 relocate_imr_addr_mrfld(u32 base_addr);
+void sst_add_to_dispatch_list_and_post(struct intel_sst_drv *sst,
+ struct ipc_post *msg);
+int sst_pm_runtime_put(struct intel_sst_drv *sst_drv);
+int sst_shim_write(void __iomem *addr, int offset, int value);
+u32 sst_shim_read(void __iomem *addr, int offset);
+u64 sst_reg_read64(void __iomem *addr, int offset);
+int sst_shim_write64(void __iomem *addr, int offset, u64 value);
+u64 sst_shim_read64(void __iomem *addr, int offset);
+void sst_set_fw_state_locked(
+ struct intel_sst_drv *sst_drv_ctx, int sst_state);
+void sst_fill_header_mrfld(union ipc_header_mrfld *header,
+ int msg, int task_id, int large, int drv_id);
+void sst_fill_header_dsp(struct ipc_dsp_hdr *dsp, int msg,
+ int pipe_id, int len);
+
+int sst_register(struct device *);
+int sst_unregister(struct device *);
+
+int sst_alloc_drv_context(struct intel_sst_drv **ctx,
+ struct device *dev, unsigned int dev_id);
+int sst_context_init(struct intel_sst_drv *ctx);
+void sst_context_cleanup(struct intel_sst_drv *ctx);
+void sst_configure_runtime_pm(struct intel_sst_drv *ctx);
+void memcpy32_toio(void __iomem *dst, const void *src, int count);
+void memcpy32_fromio(void *dst, const void __iomem *src, int count);
+
+#endif
--- /dev/null
+/*
+ * sst_acpi.c - SST (LPE) driver init file for ACPI enumeration.
+ *
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ * Authors: Ramesh Babu K V <Ramesh.Babu@intel.com>
+ * Authors: Omair Mohammed Abdullah <omair.m.abdullah@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/acpi.h>
+#include <asm/platform_sst_audio.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <acpi/acbuffer.h>
+#include <acpi/platform/acenv.h>
+#include <acpi/platform/aclinux.h>
+#include <acpi/actypes.h>
+#include <acpi/acpi_bus.h>
+#include "../sst-mfld-platform.h"
+#include "../../common/sst-dsp.h"
+#include "sst.h"
+
+struct sst_machines {
+ char *codec_id;
+ char board[32];
+ char machine[32];
+ void (*machine_quirk)(void);
+ char firmware[FW_NAME_SIZE];
+ struct sst_platform_info *pdata;
+
+};
+
+/* LPE viewpoint addresses */
+#define SST_BYT_IRAM_PHY_START 0xff2c0000
+#define SST_BYT_IRAM_PHY_END 0xff2d4000
+#define SST_BYT_DRAM_PHY_START 0xff300000
+#define SST_BYT_DRAM_PHY_END 0xff320000
+#define SST_BYT_IMR_VIRT_START 0xc0000000 /* virtual addr in LPE */
+#define SST_BYT_IMR_VIRT_END 0xc01fffff
+#define SST_BYT_SHIM_PHY_ADDR 0xff340000
+#define SST_BYT_MBOX_PHY_ADDR 0xff344000
+#define SST_BYT_DMA0_PHY_ADDR 0xff298000
+#define SST_BYT_DMA1_PHY_ADDR 0xff29c000
+#define SST_BYT_SSP0_PHY_ADDR 0xff2a0000
+#define SST_BYT_SSP2_PHY_ADDR 0xff2a2000
+
+#define BYT_FW_MOD_TABLE_OFFSET 0x80000
+#define BYT_FW_MOD_TABLE_SIZE 0x100
+#define BYT_FW_MOD_OFFSET (BYT_FW_MOD_TABLE_OFFSET + BYT_FW_MOD_TABLE_SIZE)
+
+static const struct sst_info byt_fwparse_info = {
+ .use_elf = false,
+ .max_streams = 25,
+ .iram_start = SST_BYT_IRAM_PHY_START,
+ .iram_end = SST_BYT_IRAM_PHY_END,
+ .iram_use = true,
+ .dram_start = SST_BYT_DRAM_PHY_START,
+ .dram_end = SST_BYT_DRAM_PHY_END,
+ .dram_use = true,
+ .imr_start = SST_BYT_IMR_VIRT_START,
+ .imr_end = SST_BYT_IMR_VIRT_END,
+ .imr_use = true,
+ .mailbox_start = SST_BYT_MBOX_PHY_ADDR,
+ .num_probes = 0,
+ .lpe_viewpt_rqd = true,
+};
+
+static const struct sst_ipc_info byt_ipc_info = {
+ .ipc_offset = 0,
+ .mbox_recv_off = 0x400,
+};
+
+static const struct sst_lib_dnld_info byt_lib_dnld_info = {
+ .mod_base = SST_BYT_IMR_VIRT_START,
+ .mod_end = SST_BYT_IMR_VIRT_END,
+ .mod_table_offset = BYT_FW_MOD_TABLE_OFFSET,
+ .mod_table_size = BYT_FW_MOD_TABLE_SIZE,
+ .mod_ddr_dnld = false,
+};
+
+static const struct sst_res_info byt_rvp_res_info = {
+ .shim_offset = 0x140000,
+ .shim_size = 0x000100,
+ .shim_phy_addr = SST_BYT_SHIM_PHY_ADDR,
+ .ssp0_offset = 0xa0000,
+ .ssp0_size = 0x1000,
+ .dma0_offset = 0x98000,
+ .dma0_size = 0x4000,
+ .dma1_offset = 0x9c000,
+ .dma1_size = 0x4000,
+ .iram_offset = 0x0c0000,
+ .iram_size = 0x14000,
+ .dram_offset = 0x100000,
+ .dram_size = 0x28000,
+ .mbox_offset = 0x144000,
+ .mbox_size = 0x1000,
+ .acpi_lpe_res_index = 0,
+ .acpi_ddr_index = 2,
+ .acpi_ipc_irq_index = 5,
+};
+
+static struct sst_platform_info byt_rvp_platform_data = {
+ .probe_data = &byt_fwparse_info,
+ .ipc_info = &byt_ipc_info,
+ .lib_info = &byt_lib_dnld_info,
+ .res_info = &byt_rvp_res_info,
+ .platform = "sst-mfld-platform",
+};
+
+/* Cherryview (Cherrytrail and Braswell) uses same mrfld dpcm fw as Baytrail,
+ * so pdata is same as Baytrail.
+ */
+static struct sst_platform_info chv_platform_data = {
+ .probe_data = &byt_fwparse_info,
+ .ipc_info = &byt_ipc_info,
+ .lib_info = &byt_lib_dnld_info,
+ .res_info = &byt_rvp_res_info,
+ .platform = "sst-mfld-platform",
+};
+
+static int sst_platform_get_resources(struct intel_sst_drv *ctx)
+{
+ struct resource *rsrc;
+ struct platform_device *pdev = to_platform_device(ctx->dev);
+
+ /* All ACPI resource request here */
+ /* Get Shim addr */
+ rsrc = platform_get_resource(pdev, IORESOURCE_MEM,
+ ctx->pdata->res_info->acpi_lpe_res_index);
+ if (!rsrc) {
+ dev_err(ctx->dev, "Invalid SHIM base from IFWI");
+ return -EIO;
+ }
+ dev_info(ctx->dev, "LPE base: %#x size:%#x", (unsigned int) rsrc->start,
+ (unsigned int)resource_size(rsrc));
+
+ ctx->iram_base = rsrc->start + ctx->pdata->res_info->iram_offset;
+ ctx->iram_end = ctx->iram_base + ctx->pdata->res_info->iram_size - 1;
+ dev_info(ctx->dev, "IRAM base: %#x", ctx->iram_base);
+ ctx->iram = devm_ioremap_nocache(ctx->dev, ctx->iram_base,
+ ctx->pdata->res_info->iram_size);
+ if (!ctx->iram) {
+ dev_err(ctx->dev, "unable to map IRAM");
+ return -EIO;
+ }
+
+ ctx->dram_base = rsrc->start + ctx->pdata->res_info->dram_offset;
+ ctx->dram_end = ctx->dram_base + ctx->pdata->res_info->dram_size - 1;
+ dev_info(ctx->dev, "DRAM base: %#x", ctx->dram_base);
+ ctx->dram = devm_ioremap_nocache(ctx->dev, ctx->dram_base,
+ ctx->pdata->res_info->dram_size);
+ if (!ctx->dram) {
+ dev_err(ctx->dev, "unable to map DRAM");
+ return -EIO;
+ }
+
+ ctx->shim_phy_add = rsrc->start + ctx->pdata->res_info->shim_offset;
+ dev_info(ctx->dev, "SHIM base: %#x", ctx->shim_phy_add);
+ ctx->shim = devm_ioremap_nocache(ctx->dev, ctx->shim_phy_add,
+ ctx->pdata->res_info->shim_size);
+ if (!ctx->shim) {
+ dev_err(ctx->dev, "unable to map SHIM");
+ return -EIO;
+ }
+
+ /* reassign physical address to LPE viewpoint address */
+ ctx->shim_phy_add = ctx->pdata->res_info->shim_phy_addr;
+
+ /* Get mailbox addr */
+ ctx->mailbox_add = rsrc->start + ctx->pdata->res_info->mbox_offset;
+ dev_info(ctx->dev, "Mailbox base: %#x", ctx->mailbox_add);
+ ctx->mailbox = devm_ioremap_nocache(ctx->dev, ctx->mailbox_add,
+ ctx->pdata->res_info->mbox_size);
+ if (!ctx->mailbox) {
+ dev_err(ctx->dev, "unable to map mailbox");
+ return -EIO;
+ }
+
+ /* reassign physical address to LPE viewpoint address */
+ ctx->mailbox_add = ctx->info.mailbox_start;
+
+ rsrc = platform_get_resource(pdev, IORESOURCE_MEM,
+ ctx->pdata->res_info->acpi_ddr_index);
+ if (!rsrc) {
+ dev_err(ctx->dev, "Invalid DDR base from IFWI");
+ return -EIO;
+ }
+ ctx->ddr_base = rsrc->start;
+ ctx->ddr_end = rsrc->end;
+ dev_info(ctx->dev, "DDR base: %#x", ctx->ddr_base);
+ ctx->ddr = devm_ioremap_nocache(ctx->dev, ctx->ddr_base,
+ resource_size(rsrc));
+ if (!ctx->ddr) {
+ dev_err(ctx->dev, "unable to map DDR");
+ return -EIO;
+ }
+
+ /* Find the IRQ */
+ ctx->irq_num = platform_get_irq(pdev,
+ ctx->pdata->res_info->acpi_ipc_irq_index);
+ return 0;
+}
+
+static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level,
+ void *context, void **ret)
+{
+ *(bool *)context = true;
+ return AE_OK;
+}
+
+static struct sst_machines *sst_acpi_find_machine(
+ struct sst_machines *machines)
+{
+ struct sst_machines *mach;
+ bool found = false;
+
+ for (mach = machines; mach->codec_id; mach++)
+ if (ACPI_SUCCESS(acpi_get_devices(mach->codec_id,
+ sst_acpi_mach_match,
+ &found, NULL)) && found)
+ return mach;
+
+ return NULL;
+}
+
+static int sst_acpi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ int ret = 0;
+ struct intel_sst_drv *ctx;
+ const struct acpi_device_id *id;
+ struct sst_machines *mach;
+ struct platform_device *mdev;
+ struct platform_device *plat_dev;
+ unsigned int dev_id;
+
+ id = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!id)
+ return -ENODEV;
+ dev_dbg(dev, "for %s", id->id);
+
+ mach = (struct sst_machines *)id->driver_data;
+ mach = sst_acpi_find_machine(mach);
+ if (mach == NULL) {
+ dev_err(dev, "No matching machine driver found\n");
+ return -ENODEV;
+ }
+
+ ret = kstrtouint(id->id, 16, &dev_id);
+ if (ret < 0) {
+ dev_err(dev, "Unique device id conversion error: %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "ACPI device id: %x\n", dev_id);
+
+ plat_dev = platform_device_register_data(dev, mach->pdata->platform, -1, NULL, 0);
+ if (IS_ERR(plat_dev)) {
+ dev_err(dev, "Failed to create machine device: %s\n", mach->pdata->platform);
+ return PTR_ERR(plat_dev);
+ }
+
+ /* Create platform device for sst machine driver */
+ mdev = platform_device_register_data(dev, mach->machine, -1, NULL, 0);
+ if (IS_ERR(mdev)) {
+ dev_err(dev, "Failed to create machine device: %s\n", mach->machine);
+ return PTR_ERR(mdev);
+ }
+
+ ret = sst_alloc_drv_context(&ctx, dev, dev_id);
+ if (ret < 0)
+ return ret;
+
+ /* Fill sst platform data */
+ ctx->pdata = mach->pdata;
+ strcpy(ctx->firmware_name, mach->firmware);
+
+ ret = sst_platform_get_resources(ctx);
+ if (ret)
+ return ret;
+
+ ret = sst_context_init(ctx);
+ if (ret < 0)
+ return ret;
+
+ /* need to save shim registers in BYT */
+ ctx->shim_regs64 = devm_kzalloc(ctx->dev, sizeof(*ctx->shim_regs64),
+ GFP_KERNEL);
+ if (!ctx->shim_regs64) {
+ ret = -ENOMEM;
+ goto do_sst_cleanup;
+ }
+
+ sst_configure_runtime_pm(ctx);
+ platform_set_drvdata(pdev, ctx);
+ return ret;
+
+do_sst_cleanup:
+ sst_context_cleanup(ctx);
+ platform_set_drvdata(pdev, NULL);
+ dev_err(ctx->dev, "failed with %d\n", ret);
+ return ret;
+}
+
+/**
+* intel_sst_remove - remove function
+*
+* @pdev: platform device structure
+*
+* This function is called by OS when a device is unloaded
+* This frees the interrupt etc
+*/
+static int sst_acpi_remove(struct platform_device *pdev)
+{
+ struct intel_sst_drv *ctx;
+
+ ctx = platform_get_drvdata(pdev);
+ sst_context_cleanup(ctx);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static struct sst_machines sst_acpi_bytcr[] = {
+ {"10EC5640", "T100", "bytt100_rt5640", NULL, "intel/fw_sst_0f28.bin",
+ &byt_rvp_platform_data },
+ {},
+};
+
+/* Cherryview-based platforms: CherryTrail and Braswell */
+static struct sst_machines sst_acpi_chv[] = {
+ {"10EC5670", "cht-bsw", "cht-bsw-rt5672", NULL, "intel/fw_sst_22a8.bin",
+ &chv_platform_data },
+ {"10EC5645", "cht-bsw", "cht-bsw-rt5645", NULL, "intel/fw_sst_22a8.bin",
+ &chv_platform_data },
+ {},
+};
+
+static const struct acpi_device_id sst_acpi_ids[] = {
+ { "80860F28", (unsigned long)&sst_acpi_bytcr},
+ { "808622A8", (unsigned long) &sst_acpi_chv},
+ { },
+};
+
+MODULE_DEVICE_TABLE(acpi, sst_acpi_ids);
+
+static struct platform_driver sst_acpi_driver = {
+ .driver = {
+ .name = "intel_sst_acpi",
+ .acpi_match_table = ACPI_PTR(sst_acpi_ids),
+ .pm = &intel_sst_pm,
+ },
+ .probe = sst_acpi_probe,
+ .remove = sst_acpi_remove,
+};
+
+module_platform_driver(sst_acpi_driver);
+
+MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine ACPI Driver");
+MODULE_AUTHOR("Ramesh Babu K V");
+MODULE_AUTHOR("Omair Mohammed Abdullah");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("sst");
--- /dev/null
+/*
+ * sst_drv_interface.c - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-14 Intel Corp
+ * Authors: Vinod Koul <vinod.koul@intel.com>
+ * Harsha Priya <priya.harsha@intel.com>
+ * Dharageswari R <dharageswari.r@intel.com)
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/math64.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../../common/sst-dsp.h"
+
+
+
+#define NUM_CODEC 2
+#define MIN_FRAGMENT 2
+#define MAX_FRAGMENT 4
+#define MIN_FRAGMENT_SIZE (50 * 1024)
+#define MAX_FRAGMENT_SIZE (1024 * 1024)
+#define SST_GET_BYTES_PER_SAMPLE(pcm_wd_sz) (((pcm_wd_sz + 15) >> 4) << 1)
+
+int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id)
+{
+ struct stream_info *stream;
+ int ret = 0;
+
+ stream = get_stream_info(ctx, str_id);
+ if (stream) {
+ /* str_id is valid, so stream is alloacted */
+ ret = sst_free_stream(ctx, str_id);
+ if (ret)
+ sst_clean_stream(&ctx->streams[str_id]);
+ return ret;
+ } else {
+ dev_err(ctx->dev, "we tried to free stream context %d which was freed!!!\n", str_id);
+ }
+ return ret;
+}
+
+int sst_get_stream_allocated(struct intel_sst_drv *ctx,
+ struct snd_sst_params *str_param,
+ struct snd_sst_lib_download **lib_dnld)
+{
+ int retval;
+
+ retval = ctx->ops->alloc_stream(ctx, str_param);
+ if (retval > 0)
+ dev_dbg(ctx->dev, "Stream allocated %d\n", retval);
+ return retval;
+
+}
+
+/*
+ * sst_get_sfreq - this function returns the frequency of the stream
+ *
+ * @str_param : stream params
+ */
+int sst_get_sfreq(struct snd_sst_params *str_param)
+{
+ switch (str_param->codec) {
+ case SST_CODEC_TYPE_PCM:
+ return str_param->sparams.uc.pcm_params.sfreq;
+ case SST_CODEC_TYPE_AAC:
+ return str_param->sparams.uc.aac_params.externalsr;
+ case SST_CODEC_TYPE_MP3:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * sst_get_num_channel - get number of channels for the stream
+ *
+ * @str_param : stream params
+ */
+int sst_get_num_channel(struct snd_sst_params *str_param)
+{
+ switch (str_param->codec) {
+ case SST_CODEC_TYPE_PCM:
+ return str_param->sparams.uc.pcm_params.num_chan;
+ case SST_CODEC_TYPE_MP3:
+ return str_param->sparams.uc.mp3_params.num_chan;
+ case SST_CODEC_TYPE_AAC:
+ return str_param->sparams.uc.aac_params.num_chan;
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * sst_get_stream - this function prepares for stream allocation
+ *
+ * @str_param : stream param
+ */
+int sst_get_stream(struct intel_sst_drv *ctx,
+ struct snd_sst_params *str_param)
+{
+ int retval;
+ struct stream_info *str_info;
+
+ /* stream is not allocated, we are allocating */
+ retval = ctx->ops->alloc_stream(ctx, str_param);
+ if (retval <= 0) {
+ return -EIO;
+ }
+ /* store sampling freq */
+ str_info = &ctx->streams[retval];
+ str_info->sfreq = sst_get_sfreq(str_param);
+
+ return retval;
+}
+
+static int sst_power_control(struct device *dev, bool state)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+ int ret = 0;
+ int usage_count = 0;
+
+#ifdef CONFIG_PM
+ usage_count = atomic_read(&dev->power.usage_count);
+#else
+ usage_count = 1;
+#endif
+
+ if (state == true) {
+ ret = pm_runtime_get_sync(dev);
+
+ dev_dbg(ctx->dev, "Enable: pm usage count: %d\n", usage_count);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Runtime get failed with err: %d\n", ret);
+ return ret;
+ }
+ if ((ctx->sst_state == SST_RESET) && (usage_count == 1)) {
+ ret = sst_load_fw(ctx);
+ if (ret) {
+ dev_err(dev, "FW download fail %d\n", ret);
+ sst_set_fw_state_locked(ctx, SST_RESET);
+ ret = sst_pm_runtime_put(ctx);
+ }
+ }
+ } else {
+ dev_dbg(ctx->dev, "Disable: pm usage count: %d\n", usage_count);
+ return sst_pm_runtime_put(ctx);
+ }
+ return ret;
+}
+
+/*
+ * sst_open_pcm_stream - Open PCM interface
+ *
+ * @str_param: parameters of pcm stream
+ *
+ * This function is called by MID sound card driver to open
+ * a new pcm interface
+ */
+static int sst_open_pcm_stream(struct device *dev,
+ struct snd_sst_params *str_param)
+{
+ int retval;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ if (!str_param)
+ return -EINVAL;
+
+ retval = sst_get_stream(ctx, str_param);
+ if (retval > 0)
+ ctx->stream_cnt++;
+ else
+ dev_err(ctx->dev, "sst_get_stream returned err %d\n", retval);
+
+ return retval;
+}
+
+static int sst_cdev_open(struct device *dev,
+ struct snd_sst_params *str_params, struct sst_compress_cb *cb)
+{
+ int str_id, retval;
+ struct stream_info *stream;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ retval = pm_runtime_get_sync(ctx->dev);
+ if (retval < 0)
+ return retval;
+
+ str_id = sst_get_stream(ctx, str_params);
+ if (str_id > 0) {
+ dev_dbg(dev, "stream allocated in sst_cdev_open %d\n", str_id);
+ stream = &ctx->streams[str_id];
+ stream->compr_cb = cb->compr_cb;
+ stream->compr_cb_param = cb->param;
+ stream->drain_notify = cb->drain_notify;
+ stream->drain_cb_param = cb->drain_cb_param;
+ } else {
+ dev_err(dev, "stream encountered error during alloc %d\n", str_id);
+ str_id = -EINVAL;
+ sst_pm_runtime_put(ctx);
+ }
+ return str_id;
+}
+
+static int sst_cdev_close(struct device *dev, unsigned int str_id)
+{
+ int retval;
+ struct stream_info *stream;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ stream = get_stream_info(ctx, str_id);
+ if (!stream) {
+ dev_err(dev, "stream info is NULL for str %d!!!\n", str_id);
+ return -EINVAL;
+ }
+
+ if (stream->status == STREAM_RESET) {
+ dev_dbg(dev, "stream in reset state...\n");
+ stream->status = STREAM_UN_INIT;
+
+ retval = 0;
+ goto put;
+ }
+
+ retval = sst_free_stream(ctx, str_id);
+put:
+ stream->compr_cb_param = NULL;
+ stream->compr_cb = NULL;
+
+ if (retval)
+ dev_err(dev, "free stream returned err %d\n", retval);
+
+ dev_dbg(dev, "End\n");
+ return retval;
+
+}
+
+static int sst_cdev_ack(struct device *dev, unsigned int str_id,
+ unsigned long bytes)
+{
+ struct stream_info *stream;
+ struct snd_sst_tstamp fw_tstamp = {0,};
+ int offset;
+ void __iomem *addr;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ stream = get_stream_info(ctx, str_id);
+ if (!stream)
+ return -EINVAL;
+
+ /* update bytes sent */
+ stream->cumm_bytes += bytes;
+ dev_dbg(dev, "bytes copied %d inc by %ld\n", stream->cumm_bytes, bytes);
+
+ memcpy_fromio(&fw_tstamp,
+ ((void *)(ctx->mailbox + ctx->tstamp)
+ +(str_id * sizeof(fw_tstamp))),
+ sizeof(fw_tstamp));
+
+ fw_tstamp.bytes_copied = stream->cumm_bytes;
+ dev_dbg(dev, "bytes sent to fw %llu inc by %ld\n",
+ fw_tstamp.bytes_copied, bytes);
+
+ addr = ((void *)(ctx->mailbox + ctx->tstamp)) +
+ (str_id * sizeof(fw_tstamp));
+ offset = offsetof(struct snd_sst_tstamp, bytes_copied);
+ sst_shim_write(addr, offset, fw_tstamp.bytes_copied);
+ return 0;
+}
+
+static int sst_cdev_set_metadata(struct device *dev,
+ unsigned int str_id, struct snd_compr_metadata *metadata)
+{
+ int retval = 0;
+ struct stream_info *str_info;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "set metadata for stream %d\n", str_id);
+
+ str_info = get_stream_info(ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+
+ dev_dbg(dev, "pipe id = %d\n", str_info->pipe_id);
+ retval = sst_prepare_and_post_msg(ctx, str_info->task_id, IPC_CMD,
+ IPC_IA_SET_STREAM_PARAMS_MRFLD, str_info->pipe_id,
+ sizeof(*metadata), metadata, NULL,
+ true, true, true, false);
+
+ return retval;
+}
+
+static int sst_cdev_stream_pause(struct device *dev, unsigned int str_id)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ return sst_pause_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_pause_release(struct device *dev,
+ unsigned int str_id)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ return sst_resume_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_start(struct device *dev, unsigned int str_id)
+{
+ struct stream_info *str_info;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ str_info = get_stream_info(ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ str_info->prev = str_info->status;
+ str_info->status = STREAM_RUNNING;
+ return sst_start_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_drop(struct device *dev, unsigned int str_id)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ return sst_drop_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_drain(struct device *dev, unsigned int str_id)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ return sst_drain_stream(ctx, str_id, false);
+}
+
+static int sst_cdev_stream_partial_drain(struct device *dev,
+ unsigned int str_id)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ return sst_drain_stream(ctx, str_id, true);
+}
+
+static int sst_cdev_tstamp(struct device *dev, unsigned int str_id,
+ struct snd_compr_tstamp *tstamp)
+{
+ struct snd_sst_tstamp fw_tstamp = {0,};
+ struct stream_info *stream;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ memcpy_fromio(&fw_tstamp,
+ ((void *)(ctx->mailbox + ctx->tstamp)
+ +(str_id * sizeof(fw_tstamp))),
+ sizeof(fw_tstamp));
+
+ stream = get_stream_info(ctx, str_id);
+ if (!stream)
+ return -EINVAL;
+ dev_dbg(dev, "rb_counter %llu in bytes\n", fw_tstamp.ring_buffer_counter);
+
+ tstamp->copied_total = fw_tstamp.ring_buffer_counter;
+ tstamp->pcm_frames = fw_tstamp.frames_decoded;
+ tstamp->pcm_io_frames = div_u64(fw_tstamp.hardware_counter,
+ (u64)stream->num_ch * SST_GET_BYTES_PER_SAMPLE(24));
+ tstamp->sampling_rate = fw_tstamp.sampling_frequency;
+
+ dev_dbg(dev, "PCM = %u\n", tstamp->pcm_io_frames);
+ dev_dbg(dev, "Ptr Query on strid = %d copied_total %d, decodec %d\n",
+ str_id, tstamp->copied_total, tstamp->pcm_frames);
+ dev_dbg(dev, "rendered %d\n", tstamp->pcm_io_frames);
+
+ return 0;
+}
+
+static int sst_cdev_caps(struct snd_compr_caps *caps)
+{
+ caps->num_codecs = NUM_CODEC;
+ caps->min_fragment_size = MIN_FRAGMENT_SIZE; /* 50KB */
+ caps->max_fragment_size = MAX_FRAGMENT_SIZE; /* 1024KB */
+ caps->min_fragments = MIN_FRAGMENT;
+ caps->max_fragments = MAX_FRAGMENT;
+ caps->codecs[0] = SND_AUDIOCODEC_MP3;
+ caps->codecs[1] = SND_AUDIOCODEC_AAC;
+ return 0;
+}
+
+static struct snd_compr_codec_caps caps_mp3 = {
+ .num_descriptors = 1,
+ .descriptor[0].max_ch = 2,
+ .descriptor[0].sample_rates[0] = 48000,
+ .descriptor[0].sample_rates[1] = 44100,
+ .descriptor[0].sample_rates[2] = 32000,
+ .descriptor[0].sample_rates[3] = 16000,
+ .descriptor[0].sample_rates[4] = 8000,
+ .descriptor[0].num_sample_rates = 5,
+ .descriptor[0].bit_rate[0] = 320,
+ .descriptor[0].bit_rate[1] = 192,
+ .descriptor[0].num_bitrates = 2,
+ .descriptor[0].profiles = 0,
+ .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO,
+ .descriptor[0].formats = 0,
+};
+
+static struct snd_compr_codec_caps caps_aac = {
+ .num_descriptors = 2,
+ .descriptor[1].max_ch = 2,
+ .descriptor[0].sample_rates[0] = 48000,
+ .descriptor[0].sample_rates[1] = 44100,
+ .descriptor[0].sample_rates[2] = 32000,
+ .descriptor[0].sample_rates[3] = 16000,
+ .descriptor[0].sample_rates[4] = 8000,
+ .descriptor[0].num_sample_rates = 5,
+ .descriptor[1].bit_rate[0] = 320,
+ .descriptor[1].bit_rate[1] = 192,
+ .descriptor[1].num_bitrates = 2,
+ .descriptor[1].profiles = 0,
+ .descriptor[1].modes = 0,
+ .descriptor[1].formats =
+ (SND_AUDIOSTREAMFORMAT_MP4ADTS |
+ SND_AUDIOSTREAMFORMAT_RAW),
+};
+
+static int sst_cdev_codec_caps(struct snd_compr_codec_caps *codec)
+{
+ if (codec->codec == SND_AUDIOCODEC_MP3)
+ *codec = caps_mp3;
+ else if (codec->codec == SND_AUDIOCODEC_AAC)
+ *codec = caps_aac;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id)
+{
+ struct stream_info *stream;
+
+ dev_dbg(ctx->dev, "fragment elapsed from firmware for str_id %d\n",
+ str_id);
+ stream = &ctx->streams[str_id];
+ if (stream->compr_cb)
+ stream->compr_cb(stream->compr_cb_param);
+}
+
+/*
+ * sst_close_pcm_stream - Close PCM interface
+ *
+ * @str_id: stream id to be closed
+ *
+ * This function is called by MID sound card driver to close
+ * an existing pcm interface
+ */
+static int sst_close_pcm_stream(struct device *dev, unsigned int str_id)
+{
+ struct stream_info *stream;
+ int retval = 0;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ stream = get_stream_info(ctx, str_id);
+ if (!stream) {
+ dev_err(ctx->dev, "stream info is NULL for str %d!!!\n", str_id);
+ return -EINVAL;
+ }
+
+ if (stream->status == STREAM_RESET) {
+ /* silently fail here as we have cleaned the stream earlier */
+ dev_dbg(ctx->dev, "stream in reset state...\n");
+
+ retval = 0;
+ goto put;
+ }
+
+ retval = free_stream_context(ctx, str_id);
+put:
+ stream->pcm_substream = NULL;
+ stream->status = STREAM_UN_INIT;
+ stream->period_elapsed = NULL;
+ ctx->stream_cnt--;
+
+ if (retval)
+ dev_err(ctx->dev, "free stream returned err %d\n", retval);
+
+ dev_dbg(ctx->dev, "Exit\n");
+ return 0;
+}
+
+static inline int sst_calc_tstamp(struct intel_sst_drv *ctx,
+ struct pcm_stream_info *info,
+ struct snd_pcm_substream *substream,
+ struct snd_sst_tstamp *fw_tstamp)
+{
+ size_t delay_bytes, delay_frames;
+ size_t buffer_sz;
+ u32 pointer_bytes, pointer_samples;
+
+ dev_dbg(ctx->dev, "mrfld ring_buffer_counter %llu in bytes\n",
+ fw_tstamp->ring_buffer_counter);
+ dev_dbg(ctx->dev, "mrfld hardware_counter %llu in bytes\n",
+ fw_tstamp->hardware_counter);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ delay_bytes = (size_t) (fw_tstamp->ring_buffer_counter -
+ fw_tstamp->hardware_counter);
+ else
+ delay_bytes = (size_t) (fw_tstamp->hardware_counter -
+ fw_tstamp->ring_buffer_counter);
+ delay_frames = bytes_to_frames(substream->runtime, delay_bytes);
+ buffer_sz = snd_pcm_lib_buffer_bytes(substream);
+ div_u64_rem(fw_tstamp->ring_buffer_counter, buffer_sz, &pointer_bytes);
+ pointer_samples = bytes_to_samples(substream->runtime, pointer_bytes);
+
+ dev_dbg(ctx->dev, "pcm delay %zu in bytes\n", delay_bytes);
+
+ info->buffer_ptr = pointer_samples / substream->runtime->channels;
+
+ info->pcm_delay = delay_frames / substream->runtime->channels;
+ dev_dbg(ctx->dev, "buffer ptr %llu pcm_delay rep: %llu\n",
+ info->buffer_ptr, info->pcm_delay);
+ return 0;
+}
+
+static int sst_read_timestamp(struct device *dev, struct pcm_stream_info *info)
+{
+ struct stream_info *stream;
+ struct snd_pcm_substream *substream;
+ struct snd_sst_tstamp fw_tstamp;
+ unsigned int str_id;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ str_id = info->str_id;
+ stream = get_stream_info(ctx, str_id);
+ if (!stream)
+ return -EINVAL;
+
+ if (!stream->pcm_substream)
+ return -EINVAL;
+ substream = stream->pcm_substream;
+
+ memcpy_fromio(&fw_tstamp,
+ ((void *)(ctx->mailbox + ctx->tstamp)
+ + (str_id * sizeof(fw_tstamp))),
+ sizeof(fw_tstamp));
+ return sst_calc_tstamp(ctx, info, substream, &fw_tstamp);
+}
+
+static int sst_stream_start(struct device *dev, int str_id)
+{
+ struct stream_info *str_info;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ if (ctx->sst_state != SST_FW_RUNNING)
+ return 0;
+ str_info = get_stream_info(ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ str_info->prev = str_info->status;
+ str_info->status = STREAM_RUNNING;
+ sst_start_stream(ctx, str_id);
+
+ return 0;
+}
+
+static int sst_stream_drop(struct device *dev, int str_id)
+{
+ struct stream_info *str_info;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ if (ctx->sst_state != SST_FW_RUNNING)
+ return 0;
+
+ str_info = get_stream_info(ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ str_info->prev = STREAM_UN_INIT;
+ str_info->status = STREAM_INIT;
+ return sst_drop_stream(ctx, str_id);
+}
+
+static int sst_stream_pause(struct device *dev, int str_id)
+{
+ struct stream_info *str_info;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ if (ctx->sst_state != SST_FW_RUNNING)
+ return 0;
+
+ str_info = get_stream_info(ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+
+ return sst_pause_stream(ctx, str_id);
+}
+
+static int sst_stream_resume(struct device *dev, int str_id)
+{
+ struct stream_info *str_info;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ if (ctx->sst_state != SST_FW_RUNNING)
+ return 0;
+
+ str_info = get_stream_info(ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ return sst_resume_stream(ctx, str_id);
+}
+
+static int sst_stream_init(struct device *dev, struct pcm_stream_info *str_info)
+{
+ int str_id = 0;
+ struct stream_info *stream;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ str_id = str_info->str_id;
+
+ if (ctx->sst_state != SST_FW_RUNNING)
+ return 0;
+
+ stream = get_stream_info(ctx, str_id);
+ if (!stream)
+ return -EINVAL;
+
+ dev_dbg(ctx->dev, "setting the period ptrs\n");
+ stream->pcm_substream = str_info->arg;
+ stream->period_elapsed = str_info->period_elapsed;
+ stream->sfreq = str_info->sfreq;
+ stream->prev = stream->status;
+ stream->status = STREAM_INIT;
+ dev_dbg(ctx->dev,
+ "pcm_substream %p, period_elapsed %p, sfreq %d, status %d\n",
+ stream->pcm_substream, stream->period_elapsed,
+ stream->sfreq, stream->status);
+
+ return 0;
+}
+
+/*
+ * sst_set_byte_stream - Set generic params
+ *
+ * @cmd: control cmd to be set
+ * @arg: command argument
+ *
+ * This function is called by MID sound card driver to configure
+ * SST runtime params.
+ */
+static int sst_send_byte_stream(struct device *dev,
+ struct snd_sst_bytes_v2 *bytes)
+{
+ int ret_val = 0;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ if (NULL == bytes)
+ return -EINVAL;
+ ret_val = pm_runtime_get_sync(ctx->dev);
+ if (ret_val < 0)
+ return ret_val;
+
+ ret_val = sst_send_byte_stream_mrfld(ctx, bytes);
+ sst_pm_runtime_put(ctx);
+
+ return ret_val;
+}
+
+static struct sst_ops pcm_ops = {
+ .open = sst_open_pcm_stream,
+ .stream_init = sst_stream_init,
+ .stream_start = sst_stream_start,
+ .stream_drop = sst_stream_drop,
+ .stream_pause = sst_stream_pause,
+ .stream_pause_release = sst_stream_resume,
+ .stream_read_tstamp = sst_read_timestamp,
+ .send_byte_stream = sst_send_byte_stream,
+ .close = sst_close_pcm_stream,
+ .power = sst_power_control,
+};
+
+static struct compress_sst_ops compr_ops = {
+ .open = sst_cdev_open,
+ .close = sst_cdev_close,
+ .stream_pause = sst_cdev_stream_pause,
+ .stream_pause_release = sst_cdev_stream_pause_release,
+ .stream_start = sst_cdev_stream_start,
+ .stream_drop = sst_cdev_stream_drop,
+ .stream_drain = sst_cdev_stream_drain,
+ .stream_partial_drain = sst_cdev_stream_partial_drain,
+ .tstamp = sst_cdev_tstamp,
+ .ack = sst_cdev_ack,
+ .get_caps = sst_cdev_caps,
+ .get_codec_caps = sst_cdev_codec_caps,
+ .set_metadata = sst_cdev_set_metadata,
+ .power = sst_power_control,
+};
+
+static struct sst_device sst_dsp_device = {
+ .name = "Intel(R) SST LPE",
+ .dev = NULL,
+ .ops = &pcm_ops,
+ .compr_ops = &compr_ops,
+};
+
+/*
+ * sst_register - function to register DSP
+ *
+ * This functions registers DSP with the platform driver
+ */
+int sst_register(struct device *dev)
+{
+ int ret_val;
+
+ sst_dsp_device.dev = dev;
+ ret_val = sst_register_dsp(&sst_dsp_device);
+ if (ret_val)
+ dev_err(dev, "Unable to register DSP with platform driver\n");
+
+ return ret_val;
+}
+
+int sst_unregister(struct device *dev)
+{
+ return sst_unregister_dsp(&sst_dsp_device);
+}
--- /dev/null
+/*
+ * sst_ipc.c - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-14 Intel Corporation
+ * Authors: Vinod Koul <vinod.koul@intel.com>
+ * Harsha Priya <priya.harsha@intel.com>
+ * Dharageswari R <dharageswari.r@intel.com>
+ * KP Jeeja <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/pci.h>
+#include <linux/firmware.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/intel-mid.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../../common/sst-dsp.h"
+
+struct sst_block *sst_create_block(struct intel_sst_drv *ctx,
+ u32 msg_id, u32 drv_id)
+{
+ struct sst_block *msg = NULL;
+
+ dev_dbg(ctx->dev, "Enter\n");
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (!msg)
+ return NULL;
+ msg->condition = false;
+ msg->on = true;
+ msg->msg_id = msg_id;
+ msg->drv_id = drv_id;
+ spin_lock_bh(&ctx->block_lock);
+ list_add_tail(&msg->node, &ctx->block_list);
+ spin_unlock_bh(&ctx->block_lock);
+
+ return msg;
+}
+
+/*
+ * while handling the interrupts, we need to check for message status and
+ * then if we are blocking for a message
+ *
+ * here we are unblocking the blocked ones, this is based on id we have
+ * passed and search that for block threads.
+ * We will not find block in two cases
+ * a) when its small message and block in not there, so silently ignore
+ * them
+ * b) when we are actually not able to find the block (bug perhaps)
+ *
+ * Since we have bit of small messages we can spam kernel log with err
+ * print on above so need to keep as debug prints which should be enabled
+ * via dynamic debug while debugging IPC issues
+ */
+int sst_wake_up_block(struct intel_sst_drv *ctx, int result,
+ u32 drv_id, u32 ipc, void *data, u32 size)
+{
+ struct sst_block *block = NULL;
+
+ dev_dbg(ctx->dev, "Enter\n");
+
+ spin_lock_bh(&ctx->block_lock);
+ list_for_each_entry(block, &ctx->block_list, node) {
+ dev_dbg(ctx->dev, "Block ipc %d, drv_id %d\n", block->msg_id,
+ block->drv_id);
+ if (block->msg_id == ipc && block->drv_id == drv_id) {
+ dev_dbg(ctx->dev, "free up the block\n");
+ block->ret_code = result;
+ block->data = data;
+ block->size = size;
+ block->condition = true;
+ spin_unlock_bh(&ctx->block_lock);
+ wake_up(&ctx->wait_queue);
+ return 0;
+ }
+ }
+ spin_unlock_bh(&ctx->block_lock);
+ dev_dbg(ctx->dev,
+ "Block not found or a response received for a short msg for ipc %d, drv_id %d\n",
+ ipc, drv_id);
+ return -EINVAL;
+}
+
+int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed)
+{
+ struct sst_block *block = NULL, *__block;
+
+ dev_dbg(ctx->dev, "Enter\n");
+ spin_lock_bh(&ctx->block_lock);
+ list_for_each_entry_safe(block, __block, &ctx->block_list, node) {
+ if (block == freed) {
+ pr_debug("pvt_id freed --> %d\n", freed->drv_id);
+ /* toggle the index position of pvt_id */
+ list_del(&freed->node);
+ spin_unlock_bh(&ctx->block_lock);
+ kfree(freed->data);
+ freed->data = NULL;
+ kfree(freed);
+ return 0;
+ }
+ }
+ spin_unlock_bh(&ctx->block_lock);
+ dev_err(ctx->dev, "block is already freed!!!\n");
+ return -EINVAL;
+}
+
+int sst_post_message_mrfld(struct intel_sst_drv *sst_drv_ctx,
+ struct ipc_post *ipc_msg, bool sync)
+{
+ struct ipc_post *msg = ipc_msg;
+ union ipc_header_mrfld header;
+ unsigned int loop_count = 0;
+ int retval = 0;
+ unsigned long irq_flags;
+
+ dev_dbg(sst_drv_ctx->dev, "Enter: sync: %d\n", sync);
+ spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+ header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX);
+ if (sync) {
+ while (header.p.header_high.part.busy) {
+ if (loop_count > 25) {
+ dev_err(sst_drv_ctx->dev,
+ "sst: Busy wait failed, cant send this msg\n");
+ retval = -EBUSY;
+ goto out;
+ }
+ cpu_relax();
+ loop_count++;
+ header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX);
+ }
+ } else {
+ if (list_empty(&sst_drv_ctx->ipc_dispatch_list)) {
+ /* queue is empty, nothing to send */
+ spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+ dev_dbg(sst_drv_ctx->dev,
+ "Empty msg queue... NO Action\n");
+ return 0;
+ }
+
+ if (header.p.header_high.part.busy) {
+ spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+ dev_dbg(sst_drv_ctx->dev, "Busy not free... post later\n");
+ return 0;
+ }
+
+ /* copy msg from list */
+ msg = list_entry(sst_drv_ctx->ipc_dispatch_list.next,
+ struct ipc_post, node);
+ list_del(&msg->node);
+ }
+ dev_dbg(sst_drv_ctx->dev, "sst: Post message: header = %x\n",
+ msg->mrfld_header.p.header_high.full);
+ dev_dbg(sst_drv_ctx->dev, "sst: size = 0x%x\n",
+ msg->mrfld_header.p.header_low_payload);
+
+ if (msg->mrfld_header.p.header_high.part.large)
+ memcpy_toio(sst_drv_ctx->mailbox + SST_MAILBOX_SEND,
+ msg->mailbox_data,
+ msg->mrfld_header.p.header_low_payload);
+
+ sst_shim_write64(sst_drv_ctx->shim, SST_IPCX, msg->mrfld_header.full);
+
+out:
+ spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+ kfree(msg->mailbox_data);
+ kfree(msg);
+ return retval;
+}
+
+void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx)
+{
+ union interrupt_reg_mrfld isr;
+ union interrupt_reg_mrfld imr;
+ union ipc_header_mrfld clear_ipc;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+ imr.full = sst_shim_read64(sst_drv_ctx->shim, SST_IMRX);
+ isr.full = sst_shim_read64(sst_drv_ctx->shim, SST_ISRX);
+
+ /* write 1 to clear*/
+ isr.part.busy_interrupt = 1;
+ sst_shim_write64(sst_drv_ctx->shim, SST_ISRX, isr.full);
+
+ /* Set IA done bit */
+ clear_ipc.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCD);
+
+ clear_ipc.p.header_high.part.busy = 0;
+ clear_ipc.p.header_high.part.done = 1;
+ clear_ipc.p.header_low_payload = IPC_ACK_SUCCESS;
+ sst_shim_write64(sst_drv_ctx->shim, SST_IPCD, clear_ipc.full);
+ /* un mask busy interrupt */
+ imr.part.busy_interrupt = 0;
+ sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, imr.full);
+ spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+}
+
+
+/*
+ * process_fw_init - process the FW init msg
+ *
+ * @msg: IPC message mailbox data from FW
+ *
+ * This function processes the FW init msg from FW
+ * marks FW state and prints debug info of loaded FW
+ */
+static void process_fw_init(struct intel_sst_drv *sst_drv_ctx,
+ void *msg)
+{
+ struct ipc_header_fw_init *init =
+ (struct ipc_header_fw_init *)msg;
+ int retval = 0;
+
+ dev_dbg(sst_drv_ctx->dev, "*** FW Init msg came***\n");
+ if (init->result) {
+ sst_set_fw_state_locked(sst_drv_ctx, SST_RESET);
+ dev_err(sst_drv_ctx->dev, "FW Init failed, Error %x\n",
+ init->result);
+ retval = init->result;
+ goto ret;
+ }
+
+ret:
+ sst_wake_up_block(sst_drv_ctx, retval, FW_DWNL_ID, 0 , NULL, 0);
+}
+
+static void process_fw_async_msg(struct intel_sst_drv *sst_drv_ctx,
+ struct ipc_post *msg)
+{
+ u32 msg_id;
+ int str_id;
+ u32 data_size, i;
+ void *data_offset;
+ struct stream_info *stream;
+ union ipc_header_high msg_high;
+ u32 msg_low, pipe_id;
+
+ msg_high = msg->mrfld_header.p.header_high;
+ msg_low = msg->mrfld_header.p.header_low_payload;
+ msg_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->cmd_id;
+ data_offset = (msg->mailbox_data + sizeof(struct ipc_dsp_hdr));
+ data_size = msg_low - (sizeof(struct ipc_dsp_hdr));
+
+ switch (msg_id) {
+ case IPC_SST_PERIOD_ELAPSED_MRFLD:
+ pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
+ str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
+ if (str_id > 0) {
+ dev_dbg(sst_drv_ctx->dev,
+ "Period elapsed rcvd for pipe id 0x%x\n",
+ pipe_id);
+ stream = &sst_drv_ctx->streams[str_id];
+ if (stream->period_elapsed)
+ stream->period_elapsed(stream->pcm_substream);
+ if (stream->compr_cb)
+ stream->compr_cb(stream->compr_cb_param);
+ }
+ break;
+
+ case IPC_IA_DRAIN_STREAM_MRFLD:
+ pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
+ str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
+ if (str_id > 0) {
+ stream = &sst_drv_ctx->streams[str_id];
+ if (stream->drain_notify)
+ stream->drain_notify(stream->drain_cb_param);
+ }
+ break;
+
+ case IPC_IA_FW_ASYNC_ERR_MRFLD:
+ dev_err(sst_drv_ctx->dev, "FW sent async error msg:\n");
+ for (i = 0; i < (data_size/4); i++)
+ print_hex_dump(KERN_DEBUG, NULL, DUMP_PREFIX_NONE,
+ 16, 4, data_offset, data_size, false);
+ break;
+
+ case IPC_IA_FW_INIT_CMPLT_MRFLD:
+ process_fw_init(sst_drv_ctx, data_offset);
+ break;
+
+ case IPC_IA_BUF_UNDER_RUN_MRFLD:
+ pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
+ str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
+ if (str_id > 0)
+ dev_err(sst_drv_ctx->dev,
+ "Buffer under-run for pipe:%#x str_id:%d\n",
+ pipe_id, str_id);
+ break;
+
+ default:
+ dev_err(sst_drv_ctx->dev,
+ "Unrecognized async msg from FW msg_id %#x\n", msg_id);
+ }
+}
+
+void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx,
+ struct ipc_post *msg)
+{
+ unsigned int drv_id;
+ void *data;
+ union ipc_header_high msg_high;
+ u32 msg_low;
+ struct ipc_dsp_hdr *dsp_hdr;
+ unsigned int cmd_id;
+
+ msg_high = msg->mrfld_header.p.header_high;
+ msg_low = msg->mrfld_header.p.header_low_payload;
+
+ dev_dbg(sst_drv_ctx->dev, "IPC process message header %x payload %x\n",
+ msg->mrfld_header.p.header_high.full,
+ msg->mrfld_header.p.header_low_payload);
+
+ drv_id = msg_high.part.drv_id;
+
+ /* Check for async messages first */
+ if (drv_id == SST_ASYNC_DRV_ID) {
+ /*FW sent async large message*/
+ process_fw_async_msg(sst_drv_ctx, msg);
+ return;
+ }
+
+ /* FW sent short error response for an IPC */
+ if (msg_high.part.result && drv_id && !msg_high.part.large) {
+ /* 32-bit FW error code in msg_low */
+ dev_err(sst_drv_ctx->dev, "FW sent error response 0x%x", msg_low);
+ sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
+ msg_high.part.drv_id,
+ msg_high.part.msg_id, NULL, 0);
+ return;
+ }
+
+ /*
+ * Process all valid responses
+ * if it is a large message, the payload contains the size to
+ * copy from mailbox
+ **/
+ if (msg_high.part.large) {
+ data = kzalloc(msg_low, GFP_KERNEL);
+ if (!data)
+ return;
+ memcpy(data, (void *) msg->mailbox_data, msg_low);
+ /* Copy command id so that we can use to put sst to reset */
+ dsp_hdr = (struct ipc_dsp_hdr *)data;
+ cmd_id = dsp_hdr->cmd_id;
+ dev_dbg(sst_drv_ctx->dev, "cmd_id %d\n", dsp_hdr->cmd_id);
+ if (sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
+ msg_high.part.drv_id,
+ msg_high.part.msg_id, data, msg_low))
+ kfree(data);
+ } else {
+ sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
+ msg_high.part.drv_id,
+ msg_high.part.msg_id, NULL, 0);
+ }
+
+}
--- /dev/null
+/*
+ * sst_dsp.c - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-14 Intel Corp
+ * Authors: Vinod Koul <vinod.koul@intel.com>
+ * Harsha Priya <priya.harsha@intel.com>
+ * Dharageswari R <dharageswari.r@intel.com>
+ * KP Jeeja <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This file contains all dsp controlling functions like firmware download,
+ * setting/resetting dsp cores, etc
+ */
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+#include <linux/dmaengine.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../../common/sst-dsp.h"
+
+void memcpy32_toio(void __iomem *dst, const void *src, int count)
+{
+ /* __iowrite32_copy uses 32-bit count values so divide by 4 for
+ * right count in words
+ */
+ __iowrite32_copy(dst, src, count/4);
+}
+
+void memcpy32_fromio(void *dst, const void __iomem *src, int count)
+{
+ /* __iowrite32_copy uses 32-bit count values so divide by 4 for
+ * right count in words
+ */
+ __iowrite32_copy(dst, src, count/4);
+}
+
+/**
+ * intel_sst_reset_dsp_mrfld - Resetting SST DSP
+ *
+ * This resets DSP in case of MRFLD platfroms
+ */
+int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *sst_drv_ctx)
+{
+ union config_status_reg_mrfld csr;
+
+ dev_dbg(sst_drv_ctx->dev, "sst: Resetting the DSP in mrfld\n");
+ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+
+ dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+ csr.full |= 0x7;
+ sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+
+ dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+ csr.full &= ~(0x1);
+ sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+
+ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+ dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+ return 0;
+}
+
+/**
+ * sst_start_merrifield - Start the SST DSP processor
+ *
+ * This starts the DSP in MERRIFIELD platfroms
+ */
+int sst_start_mrfld(struct intel_sst_drv *sst_drv_ctx)
+{
+ union config_status_reg_mrfld csr;
+
+ dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP in mrfld LALALALA\n");
+ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+ dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+ csr.full |= 0x7;
+ sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+
+ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+ dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+ csr.part.xt_snoop = 1;
+ csr.full &= ~(0x5);
+ sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+
+ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+ dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP_merrifield:%llx\n",
+ csr.full);
+ return 0;
+}
+
+static int sst_validate_fw_image(struct intel_sst_drv *ctx, unsigned long size,
+ struct fw_module_header **module, u32 *num_modules)
+{
+ struct sst_fw_header *header;
+ const void *sst_fw_in_mem = ctx->fw_in_mem;
+
+ dev_dbg(ctx->dev, "Enter\n");
+
+ /* Read the header information from the data pointer */
+ header = (struct sst_fw_header *)sst_fw_in_mem;
+ dev_dbg(ctx->dev,
+ "header sign=%s size=%x modules=%x fmt=%x size=%zx\n",
+ header->signature, header->file_size, header->modules,
+ header->file_format, sizeof(*header));
+
+ /* verify FW */
+ if ((strncmp(header->signature, SST_FW_SIGN, 4) != 0) ||
+ (size != header->file_size + sizeof(*header))) {
+ /* Invalid FW signature */
+ dev_err(ctx->dev, "InvalidFW sign/filesize mismatch\n");
+ return -EINVAL;
+ }
+ *num_modules = header->modules;
+ *module = (void *)sst_fw_in_mem + sizeof(*header);
+
+ return 0;
+}
+
+/*
+ * sst_fill_memcpy_list - Fill the memcpy list
+ *
+ * @memcpy_list: List to be filled
+ * @destn: Destination addr to be filled in the list
+ * @src: Source addr to be filled in the list
+ * @size: Size to be filled in the list
+ *
+ * Adds the node to the list after required fields
+ * are populated in the node
+ */
+static int sst_fill_memcpy_list(struct list_head *memcpy_list,
+ void *destn, const void *src, u32 size, bool is_io)
+{
+ struct sst_memcpy_list *listnode;
+
+ listnode = kzalloc(sizeof(*listnode), GFP_KERNEL);
+ if (listnode == NULL)
+ return -ENOMEM;
+ listnode->dstn = destn;
+ listnode->src = src;
+ listnode->size = size;
+ listnode->is_io = is_io;
+ list_add_tail(&listnode->memcpylist, memcpy_list);
+
+ return 0;
+}
+
+/**
+ * sst_parse_module_memcpy - Parse audio FW modules and populate the memcpy list
+ *
+ * @sst_drv_ctx : driver context
+ * @module : FW module header
+ * @memcpy_list : Pointer to the list to be populated
+ * Create the memcpy list as the number of block to be copied
+ * returns error or 0 if module sizes are proper
+ */
+static int sst_parse_module_memcpy(struct intel_sst_drv *sst_drv_ctx,
+ struct fw_module_header *module, struct list_head *memcpy_list)
+{
+ struct fw_block_info *block;
+ u32 count;
+ int ret_val = 0;
+ void __iomem *ram_iomem;
+
+ dev_dbg(sst_drv_ctx->dev, "module sign %s size %x blocks %x type %x\n",
+ module->signature, module->mod_size,
+ module->blocks, module->type);
+ dev_dbg(sst_drv_ctx->dev, "module entrypoint 0x%x\n", module->entry_point);
+
+ block = (void *)module + sizeof(*module);
+
+ for (count = 0; count < module->blocks; count++) {
+ if (block->size <= 0) {
+ dev_err(sst_drv_ctx->dev, "block size invalid\n");
+ return -EINVAL;
+ }
+ switch (block->type) {
+ case SST_IRAM:
+ ram_iomem = sst_drv_ctx->iram;
+ break;
+ case SST_DRAM:
+ ram_iomem = sst_drv_ctx->dram;
+ break;
+ case SST_DDR:
+ ram_iomem = sst_drv_ctx->ddr;
+ break;
+ case SST_CUSTOM_INFO:
+ block = (void *)block + sizeof(*block) + block->size;
+ continue;
+ default:
+ dev_err(sst_drv_ctx->dev, "wrong ram type0x%x in block0x%x\n",
+ block->type, count);
+ return -EINVAL;
+ }
+
+ ret_val = sst_fill_memcpy_list(memcpy_list,
+ ram_iomem + block->ram_offset,
+ (void *)block + sizeof(*block), block->size, 1);
+ if (ret_val)
+ return ret_val;
+
+ block = (void *)block + sizeof(*block) + block->size;
+ }
+ return 0;
+}
+
+/**
+ * sst_parse_fw_memcpy - parse the firmware image & populate the list for memcpy
+ *
+ * @ctx : pointer to drv context
+ * @size : size of the firmware
+ * @fw_list : pointer to list_head to be populated
+ * This function parses the FW image and saves the parsed image in the list
+ * for memcpy
+ */
+static int sst_parse_fw_memcpy(struct intel_sst_drv *ctx, unsigned long size,
+ struct list_head *fw_list)
+{
+ struct fw_module_header *module;
+ u32 count, num_modules;
+ int ret_val;
+
+ ret_val = sst_validate_fw_image(ctx, size, &module, &num_modules);
+ if (ret_val)
+ return ret_val;
+
+ for (count = 0; count < num_modules; count++) {
+ ret_val = sst_parse_module_memcpy(ctx, module, fw_list);
+ if (ret_val)
+ return ret_val;
+ module = (void *)module + sizeof(*module) + module->mod_size;
+ }
+
+ return 0;
+}
+
+/**
+ * sst_do_memcpy - function initiates the memcpy
+ *
+ * @memcpy_list: Pter to memcpy list on which the memcpy needs to be initiated
+ *
+ * Triggers the memcpy
+ */
+static void sst_do_memcpy(struct list_head *memcpy_list)
+{
+ struct sst_memcpy_list *listnode;
+
+ list_for_each_entry(listnode, memcpy_list, memcpylist) {
+ if (listnode->is_io == true)
+ memcpy32_toio((void __iomem *)listnode->dstn,
+ listnode->src, listnode->size);
+ else
+ memcpy(listnode->dstn, listnode->src, listnode->size);
+ }
+}
+
+void sst_memcpy_free_resources(struct intel_sst_drv *sst_drv_ctx)
+{
+ struct sst_memcpy_list *listnode, *tmplistnode;
+
+ /* Free the list */
+ if (!list_empty(&sst_drv_ctx->memcpy_list)) {
+ list_for_each_entry_safe(listnode, tmplistnode,
+ &sst_drv_ctx->memcpy_list, memcpylist) {
+ list_del(&listnode->memcpylist);
+ kfree(listnode);
+ }
+ }
+}
+
+static int sst_cache_and_parse_fw(struct intel_sst_drv *sst,
+ const struct firmware *fw)
+{
+ int retval = 0;
+
+ sst->fw_in_mem = kzalloc(fw->size, GFP_KERNEL);
+ if (!sst->fw_in_mem) {
+ retval = -ENOMEM;
+ goto end_release;
+ }
+ dev_dbg(sst->dev, "copied fw to %p", sst->fw_in_mem);
+ dev_dbg(sst->dev, "phys: %lx", (unsigned long)virt_to_phys(sst->fw_in_mem));
+ memcpy(sst->fw_in_mem, fw->data, fw->size);
+ retval = sst_parse_fw_memcpy(sst, fw->size, &sst->memcpy_list);
+ if (retval) {
+ dev_err(sst->dev, "Failed to parse fw\n");
+ kfree(sst->fw_in_mem);
+ sst->fw_in_mem = NULL;
+ }
+
+end_release:
+ release_firmware(fw);
+ return retval;
+
+}
+
+void sst_firmware_load_cb(const struct firmware *fw, void *context)
+{
+ struct intel_sst_drv *ctx = context;
+
+ dev_dbg(ctx->dev, "Enter\n");
+
+ if (fw == NULL) {
+ dev_err(ctx->dev, "request fw failed\n");
+ return;
+ }
+
+ mutex_lock(&ctx->sst_lock);
+
+ if (ctx->sst_state != SST_RESET ||
+ ctx->fw_in_mem != NULL) {
+ release_firmware(fw);
+ mutex_unlock(&ctx->sst_lock);
+ return;
+ }
+
+ dev_dbg(ctx->dev, "Request Fw completed\n");
+ sst_cache_and_parse_fw(ctx, fw);
+ mutex_unlock(&ctx->sst_lock);
+}
+
+/*
+ * sst_request_fw - requests audio fw from kernel and saves a copy
+ *
+ * This function requests the SST FW from the kernel, parses it and
+ * saves a copy in the driver context
+ */
+static int sst_request_fw(struct intel_sst_drv *sst)
+{
+ int retval = 0;
+ const struct firmware *fw;
+
+ retval = request_firmware(&fw, sst->firmware_name, sst->dev);
+ if (fw == NULL) {
+ dev_err(sst->dev, "fw is returning as null\n");
+ return -EINVAL;
+ }
+ if (retval) {
+ dev_err(sst->dev, "request fw failed %d\n", retval);
+ return retval;
+ }
+ mutex_lock(&sst->sst_lock);
+ retval = sst_cache_and_parse_fw(sst, fw);
+ mutex_unlock(&sst->sst_lock);
+
+ return retval;
+}
+
+/*
+ * Writing the DDR physical base to DCCM offset
+ * so that FW can use it to setup TLB
+ */
+static void sst_dccm_config_write(void __iomem *dram_base,
+ unsigned int ddr_base)
+{
+ void __iomem *addr;
+ u32 bss_reset = 0;
+
+ addr = (void __iomem *)(dram_base + MRFLD_FW_DDR_BASE_OFFSET);
+ memcpy32_toio(addr, (void *)&ddr_base, sizeof(u32));
+ bss_reset |= (1 << MRFLD_FW_BSS_RESET_BIT);
+ addr = (void __iomem *)(dram_base + MRFLD_FW_FEATURE_BASE_OFFSET);
+ memcpy32_toio(addr, &bss_reset, sizeof(u32));
+
+}
+
+void sst_post_download_mrfld(struct intel_sst_drv *ctx)
+{
+ sst_dccm_config_write(ctx->dram, ctx->ddr_base);
+ dev_dbg(ctx->dev, "config written to DCCM\n");
+}
+
+/**
+ * sst_load_fw - function to load FW into DSP
+ * Transfers the FW to DSP using dma/memcpy
+ */
+int sst_load_fw(struct intel_sst_drv *sst_drv_ctx)
+{
+ int ret_val = 0;
+ struct sst_block *block;
+
+ dev_dbg(sst_drv_ctx->dev, "sst_load_fw\n");
+
+ if (sst_drv_ctx->sst_state != SST_RESET ||
+ sst_drv_ctx->sst_state == SST_SHUTDOWN)
+ return -EAGAIN;
+
+ if (!sst_drv_ctx->fw_in_mem) {
+ dev_dbg(sst_drv_ctx->dev, "sst: FW not in memory retry to download\n");
+ ret_val = sst_request_fw(sst_drv_ctx);
+ if (ret_val)
+ return ret_val;
+ }
+
+ BUG_ON(!sst_drv_ctx->fw_in_mem);
+ block = sst_create_block(sst_drv_ctx, 0, FW_DWNL_ID);
+ if (block == NULL)
+ return -ENOMEM;
+
+ /* Prevent C-states beyond C6 */
+ pm_qos_update_request(sst_drv_ctx->qos, 0);
+
+ sst_drv_ctx->sst_state = SST_FW_LOADING;
+
+ ret_val = sst_drv_ctx->ops->reset(sst_drv_ctx);
+ if (ret_val)
+ goto restore;
+
+ sst_do_memcpy(&sst_drv_ctx->memcpy_list);
+
+ /* Write the DRAM/DCCM config before enabling FW */
+ if (sst_drv_ctx->ops->post_download)
+ sst_drv_ctx->ops->post_download(sst_drv_ctx);
+
+ /* bring sst out of reset */
+ ret_val = sst_drv_ctx->ops->start(sst_drv_ctx);
+ if (ret_val)
+ goto restore;
+
+ ret_val = sst_wait_timeout(sst_drv_ctx, block);
+ if (ret_val) {
+ dev_err(sst_drv_ctx->dev, "fw download failed %d\n" , ret_val);
+ /* FW download failed due to timeout */
+ ret_val = -EBUSY;
+
+ }
+
+
+restore:
+ /* Re-enable Deeper C-states beyond C6 */
+ pm_qos_update_request(sst_drv_ctx->qos, PM_QOS_DEFAULT_VALUE);
+ sst_free_block(sst_drv_ctx, block);
+ dev_dbg(sst_drv_ctx->dev, "fw load successful!!!\n");
+
+ if (sst_drv_ctx->ops->restore_dsp_context)
+ sst_drv_ctx->ops->restore_dsp_context();
+ sst_drv_ctx->sst_state = SST_FW_RUNNING;
+ return ret_val;
+}
+
--- /dev/null
+/*
+ * sst_pci.c - SST (LPE) driver init file for pci enumeration.
+ *
+ * Copyright (C) 2008-14 Intel Corp
+ * Authors: Vinod Koul <vinod.koul@intel.com>
+ * Harsha Priya <priya.harsha@intel.com>
+ * Dharageswari R <dharageswari.r@intel.com>
+ * KP Jeeja <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+
+static int sst_platform_get_resources(struct intel_sst_drv *ctx)
+{
+ int ddr_base, ret = 0;
+ struct pci_dev *pci = ctx->pci;
+
+ ret = pci_request_regions(pci, SST_DRV_NAME);
+ if (ret)
+ return ret;
+
+ /* map registers */
+ /* DDR base */
+ if (ctx->dev_id == SST_MRFLD_PCI_ID) {
+ ctx->ddr_base = pci_resource_start(pci, 0);
+ /* check that the relocated IMR base matches with FW Binary */
+ ddr_base = relocate_imr_addr_mrfld(ctx->ddr_base);
+ if (!ctx->pdata->lib_info) {
+ dev_err(ctx->dev, "lib_info pointer NULL\n");
+ ret = -EINVAL;
+ goto do_release_regions;
+ }
+ if (ddr_base != ctx->pdata->lib_info->mod_base) {
+ dev_err(ctx->dev,
+ "FW LSP DDR BASE does not match with IFWI\n");
+ ret = -EINVAL;
+ goto do_release_regions;
+ }
+ ctx->ddr_end = pci_resource_end(pci, 0);
+
+ ctx->ddr = pcim_iomap(pci, 0,
+ pci_resource_len(pci, 0));
+ if (!ctx->ddr) {
+ ret = -EINVAL;
+ goto do_release_regions;
+ }
+ dev_dbg(ctx->dev, "sst: DDR Ptr %p\n", ctx->ddr);
+ } else {
+ ctx->ddr = NULL;
+ }
+ /* SHIM */
+ ctx->shim_phy_add = pci_resource_start(pci, 1);
+ ctx->shim = pcim_iomap(pci, 1, pci_resource_len(pci, 1));
+ if (!ctx->shim) {
+ ret = -EINVAL;
+ goto do_release_regions;
+ }
+ dev_dbg(ctx->dev, "SST Shim Ptr %p\n", ctx->shim);
+
+ /* Shared SRAM */
+ ctx->mailbox_add = pci_resource_start(pci, 2);
+ ctx->mailbox = pcim_iomap(pci, 2, pci_resource_len(pci, 2));
+ if (!ctx->mailbox) {
+ ret = -EINVAL;
+ goto do_release_regions;
+ }
+ dev_dbg(ctx->dev, "SRAM Ptr %p\n", ctx->mailbox);
+
+ /* IRAM */
+ ctx->iram_end = pci_resource_end(pci, 3);
+ ctx->iram_base = pci_resource_start(pci, 3);
+ ctx->iram = pcim_iomap(pci, 3, pci_resource_len(pci, 3));
+ if (!ctx->iram) {
+ ret = -EINVAL;
+ goto do_release_regions;
+ }
+ dev_dbg(ctx->dev, "IRAM Ptr %p\n", ctx->iram);
+
+ /* DRAM */
+ ctx->dram_end = pci_resource_end(pci, 4);
+ ctx->dram_base = pci_resource_start(pci, 4);
+ ctx->dram = pcim_iomap(pci, 4, pci_resource_len(pci, 4));
+ if (!ctx->dram) {
+ ret = -EINVAL;
+ goto do_release_regions;
+ }
+ dev_dbg(ctx->dev, "DRAM Ptr %p\n", ctx->dram);
+do_release_regions:
+ pci_release_regions(pci);
+ return 0;
+}
+
+/*
+ * intel_sst_probe - PCI probe function
+ *
+ * @pci: PCI device structure
+ * @pci_id: PCI device ID structure
+ *
+ */
+static int intel_sst_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ int ret = 0;
+ struct intel_sst_drv *sst_drv_ctx;
+ struct sst_platform_info *sst_pdata = pci->dev.platform_data;
+
+ dev_dbg(&pci->dev, "Probe for DID %x\n", pci->device);
+ ret = sst_alloc_drv_context(&sst_drv_ctx, &pci->dev, pci->device);
+ if (ret < 0)
+ return ret;
+
+ sst_drv_ctx->pdata = sst_pdata;
+ sst_drv_ctx->irq_num = pci->irq;
+ snprintf(sst_drv_ctx->firmware_name, sizeof(sst_drv_ctx->firmware_name),
+ "%s%04x%s", "fw_sst_",
+ sst_drv_ctx->dev_id, ".bin");
+
+ ret = sst_context_init(sst_drv_ctx);
+ if (ret < 0)
+ return ret;
+
+ /* Init the device */
+ ret = pcim_enable_device(pci);
+ if (ret) {
+ dev_err(sst_drv_ctx->dev,
+ "device can't be enabled. Returned err: %d\n", ret);
+ goto do_free_drv_ctx;
+ }
+ sst_drv_ctx->pci = pci_dev_get(pci);
+ ret = sst_platform_get_resources(sst_drv_ctx);
+ if (ret < 0)
+ goto do_free_drv_ctx;
+
+ pci_set_drvdata(pci, sst_drv_ctx);
+ sst_configure_runtime_pm(sst_drv_ctx);
+
+ return ret;
+
+do_free_drv_ctx:
+ sst_context_cleanup(sst_drv_ctx);
+ dev_err(sst_drv_ctx->dev, "Probe failed with %d\n", ret);
+ return ret;
+}
+
+/**
+ * intel_sst_remove - PCI remove function
+ *
+ * @pci: PCI device structure
+ *
+ * This function is called by OS when a device is unloaded
+ * This frees the interrupt etc
+ */
+static void intel_sst_remove(struct pci_dev *pci)
+{
+ struct intel_sst_drv *sst_drv_ctx = pci_get_drvdata(pci);
+
+ sst_context_cleanup(sst_drv_ctx);
+ pci_dev_put(sst_drv_ctx->pci);
+ pci_release_regions(pci);
+ pci_set_drvdata(pci, NULL);
+}
+
+/* PCI Routines */
+static struct pci_device_id intel_sst_ids[] = {
+ { PCI_VDEVICE(INTEL, SST_MRFLD_PCI_ID), 0},
+ { 0, }
+};
+
+static struct pci_driver sst_driver = {
+ .name = SST_DRV_NAME,
+ .id_table = intel_sst_ids,
+ .probe = intel_sst_probe,
+ .remove = intel_sst_remove,
+#ifdef CONFIG_PM
+ .driver = {
+ .pm = &intel_sst_pm,
+ },
+#endif
+};
+
+module_pci_driver(sst_driver);
+
+MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine PCI Driver");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_AUTHOR("Dharageswari R <dharageswari.r@intel.com>");
+MODULE_AUTHOR("KP Jeeja <jeeja.kp@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("sst");
--- /dev/null
+/*
+ * sst_pvt.c - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-14 Intel Corp
+ * Authors: Vinod Koul <vinod.koul@intel.com>
+ * Harsha Priya <priya.harsha@intel.com>
+ * Dharageswari R <dharageswari.r@intel.com>
+ * KP Jeeja <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/kobject.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <sound/asound.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../../common/sst-dsp.h"
+
+int sst_shim_write(void __iomem *addr, int offset, int value)
+{
+ writel(value, addr + offset);
+ return 0;
+}
+
+u32 sst_shim_read(void __iomem *addr, int offset)
+{
+ return readl(addr + offset);
+}
+
+u64 sst_reg_read64(void __iomem *addr, int offset)
+{
+ u64 val = 0;
+
+ memcpy_fromio(&val, addr + offset, sizeof(val));
+
+ return val;
+}
+
+int sst_shim_write64(void __iomem *addr, int offset, u64 value)
+{
+ memcpy_toio(addr + offset, &value, sizeof(value));
+ return 0;
+}
+
+u64 sst_shim_read64(void __iomem *addr, int offset)
+{
+ u64 val = 0;
+
+ memcpy_fromio(&val, addr + offset, sizeof(val));
+ return val;
+}
+
+void sst_set_fw_state_locked(
+ struct intel_sst_drv *sst_drv_ctx, int sst_state)
+{
+ mutex_lock(&sst_drv_ctx->sst_lock);
+ sst_drv_ctx->sst_state = sst_state;
+ mutex_unlock(&sst_drv_ctx->sst_lock);
+}
+
+/*
+ * sst_wait_interruptible - wait on event
+ *
+ * @sst_drv_ctx: Driver context
+ * @block: Driver block to wait on
+ *
+ * This function waits without a timeout (and is interruptable) for a
+ * given block event
+ */
+int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx,
+ struct sst_block *block)
+{
+ int retval = 0;
+
+ if (!wait_event_interruptible(sst_drv_ctx->wait_queue,
+ block->condition)) {
+ /* event wake */
+ if (block->ret_code < 0) {
+ dev_err(sst_drv_ctx->dev,
+ "stream failed %d\n", block->ret_code);
+ retval = -EBUSY;
+ } else {
+ dev_dbg(sst_drv_ctx->dev, "event up\n");
+ retval = 0;
+ }
+ } else {
+ dev_err(sst_drv_ctx->dev, "signal interrupted\n");
+ retval = -EINTR;
+ }
+ return retval;
+
+}
+
+/*
+ * sst_wait_timeout - wait on event for timeout
+ *
+ * @sst_drv_ctx: Driver context
+ * @block: Driver block to wait on
+ *
+ * This function waits with a timeout value (and is not interruptible) on a
+ * given block event
+ */
+int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx, struct sst_block *block)
+{
+ int retval = 0;
+
+ /*
+ * NOTE:
+ * Observed that FW processes the alloc msg and replies even
+ * before the alloc thread has finished execution
+ */
+ dev_dbg(sst_drv_ctx->dev,
+ "waiting for condition %x ipc %d drv_id %d\n",
+ block->condition, block->msg_id, block->drv_id);
+ if (wait_event_timeout(sst_drv_ctx->wait_queue,
+ block->condition,
+ msecs_to_jiffies(SST_BLOCK_TIMEOUT))) {
+ /* event wake */
+ dev_dbg(sst_drv_ctx->dev, "Event wake %x\n",
+ block->condition);
+ dev_dbg(sst_drv_ctx->dev, "message ret: %d\n",
+ block->ret_code);
+ retval = -block->ret_code;
+ } else {
+ block->on = false;
+ dev_err(sst_drv_ctx->dev,
+ "Wait timed-out condition:%#x, msg_id:%#x fw_state %#x\n",
+ block->condition, block->msg_id, sst_drv_ctx->sst_state);
+ sst_drv_ctx->sst_state = SST_RESET;
+
+ retval = -EBUSY;
+ }
+ return retval;
+}
+
+/*
+ * sst_create_ipc_msg - create a IPC message
+ *
+ * @arg: ipc message
+ * @large: large or short message
+ *
+ * this function allocates structures to send a large or short
+ * message to the firmware
+ */
+int sst_create_ipc_msg(struct ipc_post **arg, bool large)
+{
+ struct ipc_post *msg;
+
+ msg = kzalloc(sizeof(struct ipc_post), GFP_ATOMIC);
+ if (!msg)
+ return -ENOMEM;
+ if (large) {
+ msg->mailbox_data = kzalloc(SST_MAILBOX_SIZE, GFP_ATOMIC);
+ if (!msg->mailbox_data) {
+ kfree(msg);
+ return -ENOMEM;
+ }
+ } else {
+ msg->mailbox_data = NULL;
+ }
+ msg->is_large = large;
+ *arg = msg;
+ return 0;
+}
+
+/*
+ * sst_create_block_and_ipc_msg - Creates IPC message and sst block
+ * @arg: passed to sst_create_ipc_message API
+ * @large: large or short message
+ * @sst_drv_ctx: sst driver context
+ * @block: return block allocated
+ * @msg_id: IPC
+ * @drv_id: stream id or private id
+ */
+int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large,
+ struct intel_sst_drv *sst_drv_ctx, struct sst_block **block,
+ u32 msg_id, u32 drv_id)
+{
+ int retval = 0;
+
+ retval = sst_create_ipc_msg(arg, large);
+ if (retval)
+ return retval;
+ *block = sst_create_block(sst_drv_ctx, msg_id, drv_id);
+ if (*block == NULL) {
+ kfree(*arg);
+ return -ENOMEM;
+ }
+ return retval;
+}
+
+/*
+ * sst_clean_stream - clean the stream context
+ *
+ * @stream: stream structure
+ *
+ * this function resets the stream contexts
+ * should be called in free
+ */
+void sst_clean_stream(struct stream_info *stream)
+{
+ stream->status = STREAM_UN_INIT;
+ stream->prev = STREAM_UN_INIT;
+ mutex_lock(&stream->lock);
+ stream->cumm_bytes = 0;
+ mutex_unlock(&stream->lock);
+}
+
+int sst_prepare_and_post_msg(struct intel_sst_drv *sst,
+ int task_id, int ipc_msg, int cmd_id, int pipe_id,
+ size_t mbox_data_len, const void *mbox_data, void **data,
+ bool large, bool fill_dsp, bool sync, bool response)
+{
+ struct ipc_post *msg = NULL;
+ struct ipc_dsp_hdr dsp_hdr;
+ struct sst_block *block;
+ int ret = 0, pvt_id;
+
+ pvt_id = sst_assign_pvt_id(sst);
+ if (pvt_id < 0)
+ return pvt_id;
+
+ if (response)
+ ret = sst_create_block_and_ipc_msg(
+ &msg, large, sst, &block, ipc_msg, pvt_id);
+ else
+ ret = sst_create_ipc_msg(&msg, large);
+
+ if (ret < 0) {
+ test_and_clear_bit(pvt_id, &sst->pvt_id);
+ return -ENOMEM;
+ }
+
+ dev_dbg(sst->dev, "pvt_id = %d, pipe id = %d, task = %d ipc_msg: %d\n",
+ pvt_id, pipe_id, task_id, ipc_msg);
+ sst_fill_header_mrfld(&msg->mrfld_header, ipc_msg,
+ task_id, large, pvt_id);
+ msg->mrfld_header.p.header_low_payload = sizeof(dsp_hdr) + mbox_data_len;
+ msg->mrfld_header.p.header_high.part.res_rqd = !sync;
+ dev_dbg(sst->dev, "header:%x\n",
+ msg->mrfld_header.p.header_high.full);
+ dev_dbg(sst->dev, "response rqd: %x",
+ msg->mrfld_header.p.header_high.part.res_rqd);
+ dev_dbg(sst->dev, "msg->mrfld_header.p.header_low_payload:%d",
+ msg->mrfld_header.p.header_low_payload);
+ if (fill_dsp) {
+ sst_fill_header_dsp(&dsp_hdr, cmd_id, pipe_id, mbox_data_len);
+ memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr));
+ if (mbox_data_len) {
+ memcpy(msg->mailbox_data + sizeof(dsp_hdr),
+ mbox_data, mbox_data_len);
+ }
+ }
+
+ if (sync)
+ sst->ops->post_message(sst, msg, true);
+ else
+ sst_add_to_dispatch_list_and_post(sst, msg);
+
+ if (response) {
+ ret = sst_wait_timeout(sst, block);
+ if (ret < 0) {
+ goto out;
+ } else if(block->data) {
+ if (!data)
+ goto out;
+ *data = kzalloc(block->size, GFP_KERNEL);
+ if (!(*data)) {
+ ret = -ENOMEM;
+ goto out;
+ } else
+ memcpy(data, (void *) block->data, block->size);
+ }
+ }
+out:
+ if (response)
+ sst_free_block(sst, block);
+ test_and_clear_bit(pvt_id, &sst->pvt_id);
+ return ret;
+}
+
+int sst_pm_runtime_put(struct intel_sst_drv *sst_drv)
+{
+ int ret;
+
+ pm_runtime_mark_last_busy(sst_drv->dev);
+ ret = pm_runtime_put_autosuspend(sst_drv->dev);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+void sst_fill_header_mrfld(union ipc_header_mrfld *header,
+ int msg, int task_id, int large, int drv_id)
+{
+ header->full = 0;
+ header->p.header_high.part.msg_id = msg;
+ header->p.header_high.part.task_id = task_id;
+ header->p.header_high.part.large = large;
+ header->p.header_high.part.drv_id = drv_id;
+ header->p.header_high.part.done = 0;
+ header->p.header_high.part.busy = 1;
+ header->p.header_high.part.res_rqd = 1;
+}
+
+void sst_fill_header_dsp(struct ipc_dsp_hdr *dsp, int msg,
+ int pipe_id, int len)
+{
+ dsp->cmd_id = msg;
+ dsp->mod_index_id = 0xff;
+ dsp->pipe_id = pipe_id;
+ dsp->length = len;
+ dsp->mod_id = 0;
+}
+
+#define SST_MAX_BLOCKS 15
+/*
+ * sst_assign_pvt_id - assign a pvt id for stream
+ *
+ * @sst_drv_ctx : driver context
+ *
+ * this function assigns a private id for calls that dont have stream
+ * context yet, should be called with lock held
+ * uses bits for the id, and finds first free bits and assigns that
+ */
+int sst_assign_pvt_id(struct intel_sst_drv *drv)
+{
+ int local;
+
+ spin_lock(&drv->block_lock);
+ /* find first zero index from lsb */
+ local = ffz(drv->pvt_id);
+ dev_dbg(drv->dev, "pvt_id assigned --> %d\n", local);
+ if (local >= SST_MAX_BLOCKS){
+ spin_unlock(&drv->block_lock);
+ dev_err(drv->dev, "PVT _ID error: no free id blocks ");
+ return -EINVAL;
+ }
+ /* toggle the index */
+ change_bit(local, &drv->pvt_id);
+ spin_unlock(&drv->block_lock);
+ return local;
+}
+
+void sst_init_stream(struct stream_info *stream,
+ int codec, int sst_id, int ops, u8 slot)
+{
+ stream->status = STREAM_INIT;
+ stream->prev = STREAM_UN_INIT;
+ stream->ops = ops;
+}
+
+int sst_validate_strid(
+ struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+ if (str_id <= 0 || str_id > sst_drv_ctx->info.max_streams) {
+ dev_err(sst_drv_ctx->dev,
+ "SST ERR: invalid stream id : %d, max %d\n",
+ str_id, sst_drv_ctx->info.max_streams);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct stream_info *get_stream_info(
+ struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+ if (sst_validate_strid(sst_drv_ctx, str_id))
+ return NULL;
+ return &sst_drv_ctx->streams[str_id];
+}
+
+int get_stream_id_mrfld(struct intel_sst_drv *sst_drv_ctx,
+ u32 pipe_id)
+{
+ int i;
+
+ for (i = 1; i <= sst_drv_ctx->info.max_streams; i++)
+ if (pipe_id == sst_drv_ctx->streams[i].pipe_id)
+ return i;
+
+ dev_dbg(sst_drv_ctx->dev, "no such pipe_id(%u)", pipe_id);
+ return -1;
+}
+
+u32 relocate_imr_addr_mrfld(u32 base_addr)
+{
+ /* Get the difference from 512MB aligned base addr */
+ /* relocate the base */
+ base_addr = MRFLD_FW_VIRTUAL_BASE + (base_addr % (512 * 1024 * 1024));
+ return base_addr;
+}
+EXPORT_SYMBOL_GPL(relocate_imr_addr_mrfld);
+
+void sst_add_to_dispatch_list_and_post(struct intel_sst_drv *sst,
+ struct ipc_post *msg)
+{
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&sst->ipc_spin_lock, irq_flags);
+ list_add_tail(&msg->node, &sst->ipc_dispatch_list);
+ spin_unlock_irqrestore(&sst->ipc_spin_lock, irq_flags);
+ sst->ops->post_message(sst, NULL, false);
+}
--- /dev/null
+/*
+ * sst_stream.c - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-14 Intel Corp
+ * Authors: Vinod Koul <vinod.koul@intel.com>
+ * Harsha Priya <priya.harsha@intel.com>
+ * Dharageswari R <dharageswari.r@intel.com>
+ * KP Jeeja <jeeja.kp@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/pci.h>
+#include <linux/firmware.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../../common/sst-dsp.h"
+
+int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params)
+{
+ struct snd_sst_alloc_mrfld alloc_param;
+ struct snd_sst_params *str_params;
+ struct snd_sst_tstamp fw_tstamp;
+ struct stream_info *str_info;
+ struct snd_sst_alloc_response *response;
+ unsigned int str_id, pipe_id, task_id;
+ int i, num_ch, ret = 0;
+ void *data = NULL;
+
+ dev_dbg(sst_drv_ctx->dev, "Enter\n");
+ BUG_ON(!params);
+
+ str_params = (struct snd_sst_params *)params;
+ memset(&alloc_param, 0, sizeof(alloc_param));
+ alloc_param.operation = str_params->ops;
+ alloc_param.codec_type = str_params->codec;
+ alloc_param.sg_count = str_params->aparams.sg_count;
+ alloc_param.ring_buf_info[0].addr =
+ str_params->aparams.ring_buf_info[0].addr;
+ alloc_param.ring_buf_info[0].size =
+ str_params->aparams.ring_buf_info[0].size;
+ alloc_param.frag_size = str_params->aparams.frag_size;
+
+ memcpy(&alloc_param.codec_params, &str_params->sparams,
+ sizeof(struct snd_sst_stream_params));
+
+ /*
+ * fill channel map params for multichannel support.
+ * Ideally channel map should be received from upper layers
+ * for multichannel support.
+ * Currently hardcoding as per FW reqm.
+ */
+ num_ch = sst_get_num_channel(str_params);
+ for (i = 0; i < 8; i++) {
+ if (i < num_ch)
+ alloc_param.codec_params.uc.pcm_params.channel_map[i] = i;
+ else
+ alloc_param.codec_params.uc.pcm_params.channel_map[i] = 0xFF;
+ }
+
+ str_id = str_params->stream_id;
+ str_info = get_stream_info(sst_drv_ctx, str_id);
+ if (str_info == NULL) {
+ dev_err(sst_drv_ctx->dev, "get stream info returned null\n");
+ return -EINVAL;
+ }
+
+ pipe_id = str_params->device_type;
+ task_id = str_params->task;
+ sst_drv_ctx->streams[str_id].pipe_id = pipe_id;
+ sst_drv_ctx->streams[str_id].task_id = task_id;
+ sst_drv_ctx->streams[str_id].num_ch = num_ch;
+
+ if (sst_drv_ctx->info.lpe_viewpt_rqd)
+ alloc_param.ts = sst_drv_ctx->info.mailbox_start +
+ sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp));
+ else
+ alloc_param.ts = sst_drv_ctx->mailbox_add +
+ sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp));
+
+ dev_dbg(sst_drv_ctx->dev, "alloc tstamp location = 0x%x\n",
+ alloc_param.ts);
+ dev_dbg(sst_drv_ctx->dev, "assigned pipe id 0x%x to task %d\n",
+ pipe_id, task_id);
+
+ /* allocate device type context */
+ sst_init_stream(&sst_drv_ctx->streams[str_id], alloc_param.codec_type,
+ str_id, alloc_param.operation, 0);
+
+ dev_info(sst_drv_ctx->dev, "Alloc for str %d pipe %#x\n",
+ str_id, pipe_id);
+ ret = sst_prepare_and_post_msg(sst_drv_ctx, task_id, IPC_CMD,
+ IPC_IA_ALLOC_STREAM_MRFLD, pipe_id, sizeof(alloc_param),
+ &alloc_param, data, true, true, false, true);
+
+ if (ret < 0) {
+ dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);
+ /* alloc failed, so reset the state to uninit */
+ str_info->status = STREAM_UN_INIT;
+ str_id = ret;
+ } else if (data) {
+ response = (struct snd_sst_alloc_response *)data;
+ ret = response->str_type.result;
+ if (!ret)
+ goto out;
+ dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);
+ if (ret == SST_ERR_STREAM_IN_USE) {
+ dev_err(sst_drv_ctx->dev,
+ "FW not in clean state, send free for:%d\n", str_id);
+ sst_free_stream(sst_drv_ctx, str_id);
+ }
+ str_id = -ret;
+ }
+out:
+ kfree(data);
+ return str_id;
+}
+
+/**
+* sst_start_stream - Send msg for a starting stream
+* @str_id: stream ID
+*
+* This function is called by any function which wants to start
+* a stream.
+*/
+int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+ int retval = 0;
+ struct stream_info *str_info;
+ u16 data = 0;
+
+ dev_dbg(sst_drv_ctx->dev, "sst_start_stream for %d\n", str_id);
+ str_info = get_stream_info(sst_drv_ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ if (str_info->status != STREAM_RUNNING)
+ return -EBADRQC;
+
+ retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
+ IPC_CMD, IPC_IA_START_STREAM_MRFLD, str_info->pipe_id,
+ sizeof(u16), &data, NULL, true, true, true, false);
+
+ return retval;
+}
+
+int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx,
+ struct snd_sst_bytes_v2 *bytes)
+{ struct ipc_post *msg = NULL;
+ u32 length;
+ int pvt_id, ret = 0;
+ struct sst_block *block = NULL;
+
+ dev_dbg(sst_drv_ctx->dev,
+ "type:%u ipc_msg:%u block:%u task_id:%u pipe: %#x length:%#x\n",
+ bytes->type, bytes->ipc_msg, bytes->block, bytes->task_id,
+ bytes->pipe_id, bytes->len);
+
+ if (sst_create_ipc_msg(&msg, true))
+ return -ENOMEM;
+
+ pvt_id = sst_assign_pvt_id(sst_drv_ctx);
+ sst_fill_header_mrfld(&msg->mrfld_header, bytes->ipc_msg,
+ bytes->task_id, 1, pvt_id);
+ msg->mrfld_header.p.header_high.part.res_rqd = bytes->block;
+ length = bytes->len;
+ msg->mrfld_header.p.header_low_payload = length;
+ dev_dbg(sst_drv_ctx->dev, "length is %d\n", length);
+ memcpy(msg->mailbox_data, &bytes->bytes, bytes->len);
+ if (bytes->block) {
+ block = sst_create_block(sst_drv_ctx, bytes->ipc_msg, pvt_id);
+ if (block == NULL) {
+ kfree(msg);
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
+
+ sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg);
+ dev_dbg(sst_drv_ctx->dev, "msg->mrfld_header.p.header_low_payload:%d",
+ msg->mrfld_header.p.header_low_payload);
+
+ if (bytes->block) {
+ ret = sst_wait_timeout(sst_drv_ctx, block);
+ if (ret) {
+ dev_err(sst_drv_ctx->dev, "fw returned err %d\n", ret);
+ sst_free_block(sst_drv_ctx, block);
+ goto out;
+ }
+ }
+ if (bytes->type == SND_SST_BYTES_GET) {
+ /*
+ * copy the reply and send back
+ * we need to update only sz and payload
+ */
+ if (bytes->block) {
+ unsigned char *r = block->data;
+
+ dev_dbg(sst_drv_ctx->dev, "read back %d bytes",
+ bytes->len);
+ memcpy(bytes->bytes, r, bytes->len);
+ }
+ }
+ if (bytes->block)
+ sst_free_block(sst_drv_ctx, block);
+out:
+ test_and_clear_bit(pvt_id, &sst_drv_ctx->pvt_id);
+ return 0;
+}
+
+/*
+ * sst_pause_stream - Send msg for a pausing stream
+ * @str_id: stream ID
+ *
+ * This function is called by any function which wants to pause
+ * an already running stream.
+ */
+int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+ int retval = 0;
+ struct stream_info *str_info;
+
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_pause_stream for %d\n", str_id);
+ str_info = get_stream_info(sst_drv_ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ if (str_info->status == STREAM_PAUSED)
+ return 0;
+ if (str_info->status == STREAM_RUNNING ||
+ str_info->status == STREAM_INIT) {
+ if (str_info->prev == STREAM_UN_INIT)
+ return -EBADRQC;
+
+ retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
+ IPC_IA_PAUSE_STREAM_MRFLD, str_info->pipe_id,
+ 0, NULL, NULL, true, true, false, true);
+
+ if (retval == 0) {
+ str_info->prev = str_info->status;
+ str_info->status = STREAM_PAUSED;
+ } else if (retval == SST_ERR_INVALID_STREAM_ID) {
+ retval = -EINVAL;
+ mutex_lock(&sst_drv_ctx->sst_lock);
+ sst_clean_stream(str_info);
+ mutex_unlock(&sst_drv_ctx->sst_lock);
+ }
+ } else {
+ retval = -EBADRQC;
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:BADRQC for stream\n ");
+ }
+
+ return retval;
+}
+
+/**
+ * sst_resume_stream - Send msg for resuming stream
+ * @str_id: stream ID
+ *
+ * This function is called by any function which wants to resume
+ * an already paused stream.
+ */
+int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+ int retval = 0;
+ struct stream_info *str_info;
+
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_resume_stream for %d\n", str_id);
+ str_info = get_stream_info(sst_drv_ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ if (str_info->status == STREAM_RUNNING)
+ return 0;
+ if (str_info->status == STREAM_PAUSED) {
+ retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
+ IPC_CMD, IPC_IA_RESUME_STREAM_MRFLD,
+ str_info->pipe_id, 0, NULL, NULL,
+ true, true, false, true);
+
+ if (!retval) {
+ if (str_info->prev == STREAM_RUNNING)
+ str_info->status = STREAM_RUNNING;
+ else
+ str_info->status = STREAM_INIT;
+ str_info->prev = STREAM_PAUSED;
+ } else if (retval == -SST_ERR_INVALID_STREAM_ID) {
+ retval = -EINVAL;
+ mutex_lock(&sst_drv_ctx->sst_lock);
+ sst_clean_stream(str_info);
+ mutex_unlock(&sst_drv_ctx->sst_lock);
+ }
+ } else {
+ retval = -EBADRQC;
+ dev_err(sst_drv_ctx->dev, "SST ERR: BADQRC for stream\n");
+ }
+
+ return retval;
+}
+
+
+/**
+ * sst_drop_stream - Send msg for stopping stream
+ * @str_id: stream ID
+ *
+ * This function is called by any function which wants to stop
+ * a stream.
+ */
+int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+ int retval = 0;
+ struct stream_info *str_info;
+
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_drop_stream for %d\n", str_id);
+ str_info = get_stream_info(sst_drv_ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+
+ if (str_info->status != STREAM_UN_INIT) {
+ str_info->prev = STREAM_UN_INIT;
+ str_info->status = STREAM_INIT;
+ str_info->cumm_bytes = 0;
+ retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
+ IPC_CMD, IPC_IA_DROP_STREAM_MRFLD,
+ str_info->pipe_id, 0, NULL, NULL,
+ true, true, true, false);
+ } else {
+ retval = -EBADRQC;
+ dev_dbg(sst_drv_ctx->dev, "BADQRC for stream, state %x\n",
+ str_info->status);
+ }
+ return retval;
+}
+
+/**
+* sst_drain_stream - Send msg for draining stream
+* @str_id: stream ID
+*
+* This function is called by any function which wants to drain
+* a stream.
+*/
+int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx,
+ int str_id, bool partial_drain)
+{
+ int retval = 0;
+ struct stream_info *str_info;
+
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_drain_stream for %d\n", str_id);
+ str_info = get_stream_info(sst_drv_ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ if (str_info->status != STREAM_RUNNING &&
+ str_info->status != STREAM_INIT &&
+ str_info->status != STREAM_PAUSED) {
+ dev_err(sst_drv_ctx->dev, "SST ERR: BADQRC for stream = %d\n",
+ str_info->status);
+ return -EBADRQC;
+ }
+
+ retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
+ IPC_IA_DRAIN_STREAM_MRFLD, str_info->pipe_id,
+ sizeof(u8), &partial_drain, NULL, true, true, false, false);
+ /*
+ * with new non blocked drain implementation in core we dont need to
+ * wait for respsonse, and need to only invoke callback for drain
+ * complete
+ */
+
+ return retval;
+}
+
+/**
+ * sst_free_stream - Frees a stream
+ * @str_id: stream ID
+ *
+ * This function is called by any function which wants to free
+ * a stream.
+ */
+int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+ int retval = 0;
+ struct stream_info *str_info;
+ struct intel_sst_ops *ops;
+
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_free_stream for %d\n", str_id);
+
+ mutex_lock(&sst_drv_ctx->sst_lock);
+ if (sst_drv_ctx->sst_state == SST_RESET) {
+ mutex_unlock(&sst_drv_ctx->sst_lock);
+ return -ENODEV;
+ }
+ mutex_unlock(&sst_drv_ctx->sst_lock);
+ str_info = get_stream_info(sst_drv_ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ ops = sst_drv_ctx->ops;
+
+ mutex_lock(&str_info->lock);
+ if (str_info->status != STREAM_UN_INIT) {
+ str_info->prev = str_info->status;
+ str_info->status = STREAM_UN_INIT;
+ mutex_unlock(&str_info->lock);
+
+ dev_info(sst_drv_ctx->dev, "Free for str %d pipe %#x\n",
+ str_id, str_info->pipe_id);
+ retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
+ IPC_IA_FREE_STREAM_MRFLD, str_info->pipe_id, 0,
+ NULL, NULL, true, true, false, true);
+
+ dev_dbg(sst_drv_ctx->dev, "sst: wait for free returned %d\n",
+ retval);
+ mutex_lock(&sst_drv_ctx->sst_lock);
+ sst_clean_stream(str_info);
+ mutex_unlock(&sst_drv_ctx->sst_lock);
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:Stream freed\n");
+ } else {
+ mutex_unlock(&str_info->lock);
+ retval = -EBADRQC;
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:BADQRC for stream\n");
+ }
+
+ return retval;
+}
--- /dev/null
+snd-soc-sst-baytrail-pcm-objs := \
+ sst-baytrail-ipc.o sst-baytrail-pcm.o sst-baytrail-dsp.o
+
+obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += snd-soc-sst-baytrail-pcm.o
--- /dev/null
+/*
+ * Intel Baytrail SST DSP driver
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+
+#include "../common/sst-dsp.h"
+#include "../common/sst-dsp-priv.h"
+#include "sst-baytrail-ipc.h"
+
+#define SST_BYT_FW_SIGNATURE_SIZE 4
+#define SST_BYT_FW_SIGN "$SST"
+
+#define SST_BYT_IRAM_OFFSET 0xC0000
+#define SST_BYT_DRAM_OFFSET 0x100000
+#define SST_BYT_SHIM_OFFSET 0x140000
+
+enum sst_ram_type {
+ SST_BYT_IRAM = 1,
+ SST_BYT_DRAM = 2,
+ SST_BYT_CACHE = 3,
+};
+
+struct dma_block_info {
+ enum sst_ram_type type; /* IRAM/DRAM */
+ u32 size; /* Bytes */
+ u32 ram_offset; /* Offset in I/DRAM */
+ u32 rsvd; /* Reserved field */
+};
+
+struct fw_header {
+ unsigned char signature[SST_BYT_FW_SIGNATURE_SIZE];
+ u32 file_size; /* size of fw minus this header */
+ u32 modules; /* # of modules */
+ u32 file_format; /* version of header format */
+ u32 reserved[4];
+};
+
+struct sst_byt_fw_module_header {
+ unsigned char signature[SST_BYT_FW_SIGNATURE_SIZE];
+ u32 mod_size; /* size of module */
+ u32 blocks; /* # of blocks */
+ u32 type; /* codec type, pp lib */
+ u32 entry_point;
+};
+
+static int sst_byt_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
+ struct sst_byt_fw_module_header *module)
+{
+ struct dma_block_info *block;
+ struct sst_module *mod;
+ struct sst_module_template template;
+ int count;
+
+ memset(&template, 0, sizeof(template));
+ template.id = module->type;
+ template.entry = module->entry_point;
+
+ mod = sst_module_new(fw, &template, NULL);
+ if (mod == NULL)
+ return -ENOMEM;
+
+ block = (void *)module + sizeof(*module);
+
+ for (count = 0; count < module->blocks; count++) {
+
+ if (block->size <= 0) {
+ dev_err(dsp->dev, "block %d size invalid\n", count);
+ return -EINVAL;
+ }
+
+ switch (block->type) {
+ case SST_BYT_IRAM:
+ mod->offset = block->ram_offset +
+ dsp->addr.iram_offset;
+ mod->type = SST_MEM_IRAM;
+ break;
+ case SST_BYT_DRAM:
+ mod->offset = block->ram_offset +
+ dsp->addr.dram_offset;
+ mod->type = SST_MEM_DRAM;
+ break;
+ case SST_BYT_CACHE:
+ mod->offset = block->ram_offset +
+ (dsp->addr.fw_ext - dsp->addr.lpe);
+ mod->type = SST_MEM_CACHE;
+ break;
+ default:
+ dev_err(dsp->dev, "wrong ram type 0x%x in block0x%x\n",
+ block->type, count);
+ return -EINVAL;
+ }
+
+ mod->size = block->size;
+ mod->data = (void *)block + sizeof(*block);
+
+ sst_module_alloc_blocks(mod);
+
+ block = (void *)block + sizeof(*block) + block->size;
+ }
+ return 0;
+}
+
+static int sst_byt_parse_fw_image(struct sst_fw *sst_fw)
+{
+ struct fw_header *header;
+ struct sst_byt_fw_module_header *module;
+ struct sst_dsp *dsp = sst_fw->dsp;
+ int ret, count;
+
+ /* Read the header information from the data pointer */
+ header = (struct fw_header *)sst_fw->dma_buf;
+
+ /* verify FW */
+ if ((strncmp(header->signature, SST_BYT_FW_SIGN, 4) != 0) ||
+ (sst_fw->size != header->file_size + sizeof(*header))) {
+ /* Invalid FW signature */
+ dev_err(dsp->dev, "Invalid FW sign/filesize mismatch\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dsp->dev,
+ "header sign=%4s size=0x%x modules=0x%x fmt=0x%x size=%zu\n",
+ header->signature, header->file_size, header->modules,
+ header->file_format, sizeof(*header));
+
+ module = (void *)sst_fw->dma_buf + sizeof(*header);
+ for (count = 0; count < header->modules; count++) {
+ /* module */
+ ret = sst_byt_parse_module(dsp, sst_fw, module);
+ if (ret < 0) {
+ dev_err(dsp->dev, "invalid module %d\n", count);
+ return ret;
+ }
+ module = (void *)module + sizeof(*module) + module->mod_size;
+ }
+
+ return 0;
+}
+
+static void sst_byt_dump_shim(struct sst_dsp *sst)
+{
+ int i;
+ u64 reg;
+
+ for (i = 0; i <= 0xF0; i += 8) {
+ reg = sst_dsp_shim_read64_unlocked(sst, i);
+ if (reg)
+ dev_dbg(sst->dev, "shim 0x%2.2x value 0x%16.16llx\n",
+ i, reg);
+ }
+
+ for (i = 0x00; i <= 0xff; i += 4) {
+ reg = readl(sst->addr.pci_cfg + i);
+ if (reg)
+ dev_dbg(sst->dev, "pci 0x%2.2x value 0x%8.8x\n",
+ i, (u32)reg);
+ }
+}
+
+static irqreturn_t sst_byt_irq(int irq, void *context)
+{
+ struct sst_dsp *sst = (struct sst_dsp *) context;
+ u64 isrx;
+ irqreturn_t ret = IRQ_NONE;
+
+ spin_lock(&sst->spinlock);
+
+ isrx = sst_dsp_shim_read64_unlocked(sst, SST_ISRX);
+ if (isrx & SST_ISRX_DONE) {
+ /* ADSP has processed the message request from IA */
+ sst_dsp_shim_update_bits64_unlocked(sst, SST_IPCX,
+ SST_BYT_IPCX_DONE, 0);
+ ret = IRQ_WAKE_THREAD;
+ }
+ if (isrx & SST_BYT_ISRX_REQUEST) {
+ /* mask message request from ADSP and do processing later */
+ sst_dsp_shim_update_bits64_unlocked(sst, SST_IMRX,
+ SST_BYT_IMRX_REQUEST,
+ SST_BYT_IMRX_REQUEST);
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ spin_unlock(&sst->spinlock);
+
+ return ret;
+}
+
+static void sst_byt_boot(struct sst_dsp *sst)
+{
+ int tries = 10;
+
+ /*
+ * save the physical address of extended firmware block in the first
+ * 4 bytes of the mailbox
+ */
+ memcpy_toio(sst->addr.lpe + SST_BYT_MAILBOX_OFFSET,
+ &sst->pdata->fw_base, sizeof(u32));
+
+ /* release stall and wait to unstall */
+ sst_dsp_shim_update_bits64(sst, SST_CSR, SST_BYT_CSR_STALL, 0x0);
+ while (tries--) {
+ if (!(sst_dsp_shim_read64(sst, SST_CSR) &
+ SST_BYT_CSR_PWAITMODE))
+ break;
+ msleep(100);
+ }
+ if (tries < 0) {
+ dev_err(sst->dev, "unable to start DSP\n");
+ sst_byt_dump_shim(sst);
+ }
+}
+
+static void sst_byt_reset(struct sst_dsp *sst)
+{
+ /* put DSP into reset, set reset vector and stall */
+ sst_dsp_shim_update_bits64(sst, SST_CSR,
+ SST_BYT_CSR_RST | SST_BYT_CSR_VECTOR_SEL | SST_BYT_CSR_STALL,
+ SST_BYT_CSR_RST | SST_BYT_CSR_VECTOR_SEL | SST_BYT_CSR_STALL);
+
+ udelay(10);
+
+ /* take DSP out of reset and keep stalled for FW loading */
+ sst_dsp_shim_update_bits64(sst, SST_CSR, SST_BYT_CSR_RST, 0);
+}
+
+struct sst_adsp_memregion {
+ u32 start;
+ u32 end;
+ int blocks;
+ enum sst_mem_type type;
+};
+
+/* BYT test stuff */
+static const struct sst_adsp_memregion byt_region[] = {
+ {0xC0000, 0x100000, 8, SST_MEM_IRAM}, /* I-SRAM - 8 * 32kB */
+ {0x100000, 0x140000, 8, SST_MEM_DRAM}, /* D-SRAM0 - 8 * 32kB */
+};
+
+static int sst_byt_resource_map(struct sst_dsp *sst, struct sst_pdata *pdata)
+{
+ sst->addr.lpe_base = pdata->lpe_base;
+ sst->addr.lpe = ioremap(pdata->lpe_base, pdata->lpe_size);
+ if (!sst->addr.lpe)
+ return -ENODEV;
+
+ /* ADSP PCI MMIO config space */
+ sst->addr.pci_cfg = ioremap(pdata->pcicfg_base, pdata->pcicfg_size);
+ if (!sst->addr.pci_cfg) {
+ iounmap(sst->addr.lpe);
+ return -ENODEV;
+ }
+
+ /* SST Extended FW allocation */
+ sst->addr.fw_ext = ioremap(pdata->fw_base, pdata->fw_size);
+ if (!sst->addr.fw_ext) {
+ iounmap(sst->addr.pci_cfg);
+ iounmap(sst->addr.lpe);
+ return -ENODEV;
+ }
+
+ /* SST Shim */
+ sst->addr.shim = sst->addr.lpe + sst->addr.shim_offset;
+
+ sst_dsp_mailbox_init(sst, SST_BYT_MAILBOX_OFFSET + 0x204,
+ SST_BYT_IPC_MAX_PAYLOAD_SIZE,
+ SST_BYT_MAILBOX_OFFSET,
+ SST_BYT_IPC_MAX_PAYLOAD_SIZE);
+
+ sst->irq = pdata->irq;
+
+ return 0;
+}
+
+static int sst_byt_init(struct sst_dsp *sst, struct sst_pdata *pdata)
+{
+ const struct sst_adsp_memregion *region;
+ struct device *dev;
+ int ret = -ENODEV, i, j, region_count;
+ u32 offset, size;
+
+ dev = sst->dev;
+
+ switch (sst->id) {
+ case SST_DEV_ID_BYT:
+ region = byt_region;
+ region_count = ARRAY_SIZE(byt_region);
+ sst->addr.iram_offset = SST_BYT_IRAM_OFFSET;
+ sst->addr.dram_offset = SST_BYT_DRAM_OFFSET;
+ sst->addr.shim_offset = SST_BYT_SHIM_OFFSET;
+ break;
+ default:
+ dev_err(dev, "failed to get mem resources\n");
+ return ret;
+ }
+
+ ret = sst_byt_resource_map(sst, pdata);
+ if (ret < 0) {
+ dev_err(dev, "failed to map resources\n");
+ return ret;
+ }
+
+ ret = dma_coerce_mask_and_coherent(sst->dma_dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ /* enable Interrupt from both sides */
+ sst_dsp_shim_update_bits64(sst, SST_IMRX, 0x3, 0x0);
+ sst_dsp_shim_update_bits64(sst, SST_IMRD, 0x3, 0x0);
+
+ /* register DSP memory blocks - ideally we should get this from ACPI */
+ for (i = 0; i < region_count; i++) {
+ offset = region[i].start;
+ size = (region[i].end - region[i].start) / region[i].blocks;
+
+ /* register individual memory blocks */
+ for (j = 0; j < region[i].blocks; j++) {
+ sst_mem_block_register(sst, offset, size,
+ region[i].type, NULL, j, sst);
+ offset += size;
+ }
+ }
+
+ return 0;
+}
+
+static void sst_byt_free(struct sst_dsp *sst)
+{
+ sst_mem_block_unregister_all(sst);
+ iounmap(sst->addr.lpe);
+ iounmap(sst->addr.pci_cfg);
+ iounmap(sst->addr.fw_ext);
+}
+
+struct sst_ops sst_byt_ops = {
+ .reset = sst_byt_reset,
+ .boot = sst_byt_boot,
+ .write = sst_shim32_write,
+ .read = sst_shim32_read,
+ .write64 = sst_shim32_write64,
+ .read64 = sst_shim32_read64,
+ .ram_read = sst_memcpy_fromio_32,
+ .ram_write = sst_memcpy_toio_32,
+ .irq_handler = sst_byt_irq,
+ .init = sst_byt_init,
+ .free = sst_byt_free,
+ .parse_fw = sst_byt_parse_fw_image,
+};
--- /dev/null
+/*
+ * Intel Baytrail SST IPC Support
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/kthread.h>
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <asm/div64.h>
+
+#include "sst-baytrail-ipc.h"
+#include "../common/sst-dsp.h"
+#include "../common/sst-dsp-priv.h"
+#include "../common/sst-ipc.h"
+
+/* IPC message timeout */
+#define IPC_TIMEOUT_MSECS 300
+#define IPC_BOOT_MSECS 200
+
+#define IPC_EMPTY_LIST_SIZE 8
+
+/* IPC header bits */
+#define IPC_HEADER_MSG_ID_MASK 0xff
+#define IPC_HEADER_MSG_ID(x) ((x) & IPC_HEADER_MSG_ID_MASK)
+#define IPC_HEADER_STR_ID_SHIFT 8
+#define IPC_HEADER_STR_ID_MASK 0x1f
+#define IPC_HEADER_STR_ID(x) (((x) & 0x1f) << IPC_HEADER_STR_ID_SHIFT)
+#define IPC_HEADER_LARGE_SHIFT 13
+#define IPC_HEADER_LARGE(x) (((x) & 0x1) << IPC_HEADER_LARGE_SHIFT)
+#define IPC_HEADER_DATA_SHIFT 16
+#define IPC_HEADER_DATA_MASK 0x3fff
+#define IPC_HEADER_DATA(x) (((x) & 0x3fff) << IPC_HEADER_DATA_SHIFT)
+
+/* mask for differentiating between notification and reply message */
+#define IPC_NOTIFICATION (0x1 << 7)
+
+/* I2L Stream config/control msgs */
+#define IPC_IA_ALLOC_STREAM 0x20
+#define IPC_IA_FREE_STREAM 0x21
+#define IPC_IA_PAUSE_STREAM 0x24
+#define IPC_IA_RESUME_STREAM 0x25
+#define IPC_IA_DROP_STREAM 0x26
+#define IPC_IA_START_STREAM 0x30
+
+/* notification messages */
+#define IPC_IA_FW_INIT_CMPLT 0x81
+#define IPC_SST_PERIOD_ELAPSED 0x97
+
+/* IPC messages between host and ADSP */
+struct sst_byt_address_info {
+ u32 addr;
+ u32 size;
+} __packed;
+
+struct sst_byt_str_type {
+ u8 codec_type;
+ u8 str_type;
+ u8 operation;
+ u8 protected_str;
+ u8 time_slots;
+ u8 reserved;
+ u16 result;
+} __packed;
+
+struct sst_byt_pcm_params {
+ u8 num_chan;
+ u8 pcm_wd_sz;
+ u8 use_offload_path;
+ u8 reserved;
+ u32 sfreq;
+ u8 channel_map[8];
+} __packed;
+
+struct sst_byt_frames_info {
+ u16 num_entries;
+ u16 rsrvd;
+ u32 frag_size;
+ struct sst_byt_address_info ring_buf_info[8];
+} __packed;
+
+struct sst_byt_alloc_params {
+ struct sst_byt_str_type str_type;
+ struct sst_byt_pcm_params pcm_params;
+ struct sst_byt_frames_info frame_info;
+} __packed;
+
+struct sst_byt_alloc_response {
+ struct sst_byt_str_type str_type;
+ u8 reserved[88];
+} __packed;
+
+struct sst_byt_start_stream_params {
+ u32 byte_offset;
+} __packed;
+
+struct sst_byt_tstamp {
+ u64 ring_buffer_counter;
+ u64 hardware_counter;
+ u64 frames_decoded;
+ u64 bytes_decoded;
+ u64 bytes_copied;
+ u32 sampling_frequency;
+ u32 channel_peak[8];
+} __packed;
+
+struct sst_byt_fw_version {
+ u8 build;
+ u8 minor;
+ u8 major;
+ u8 type;
+} __packed;
+
+struct sst_byt_fw_build_info {
+ u8 date[16];
+ u8 time[16];
+} __packed;
+
+struct sst_byt_fw_init {
+ struct sst_byt_fw_version fw_version;
+ struct sst_byt_fw_build_info build_info;
+ u16 result;
+ u8 module_id;
+ u8 debug_info;
+} __packed;
+
+struct sst_byt_stream;
+struct sst_byt;
+
+/* stream infomation */
+struct sst_byt_stream {
+ struct list_head node;
+
+ /* configuration */
+ struct sst_byt_alloc_params request;
+ struct sst_byt_alloc_response reply;
+
+ /* runtime info */
+ struct sst_byt *byt;
+ int str_id;
+ bool commited;
+ bool running;
+
+ /* driver callback */
+ u32 (*notify_position)(struct sst_byt_stream *stream, void *data);
+ void *pdata;
+};
+
+/* SST Baytrail IPC data */
+struct sst_byt {
+ struct device *dev;
+ struct sst_dsp *dsp;
+
+ /* stream */
+ struct list_head stream_list;
+
+ /* boot */
+ wait_queue_head_t boot_wait;
+ bool boot_complete;
+ struct sst_fw *fw;
+
+ /* IPC messaging */
+ struct sst_generic_ipc ipc;
+};
+
+static inline u64 sst_byt_header(int msg_id, int data, bool large, int str_id)
+{
+ u64 header;
+
+ header = IPC_HEADER_MSG_ID(msg_id) |
+ IPC_HEADER_STR_ID(str_id) |
+ IPC_HEADER_LARGE(large) |
+ IPC_HEADER_DATA(data) |
+ SST_BYT_IPCX_BUSY;
+
+ return header;
+}
+
+static inline u16 sst_byt_header_msg_id(u64 header)
+{
+ return header & IPC_HEADER_MSG_ID_MASK;
+}
+
+static inline u8 sst_byt_header_str_id(u64 header)
+{
+ return (header >> IPC_HEADER_STR_ID_SHIFT) & IPC_HEADER_STR_ID_MASK;
+}
+
+static inline u16 sst_byt_header_data(u64 header)
+{
+ return (header >> IPC_HEADER_DATA_SHIFT) & IPC_HEADER_DATA_MASK;
+}
+
+static struct sst_byt_stream *sst_byt_get_stream(struct sst_byt *byt,
+ int stream_id)
+{
+ struct sst_byt_stream *stream;
+
+ list_for_each_entry(stream, &byt->stream_list, node) {
+ if (stream->str_id == stream_id)
+ return stream;
+ }
+
+ return NULL;
+}
+
+static void sst_byt_stream_update(struct sst_byt *byt, struct ipc_message *msg)
+{
+ struct sst_byt_stream *stream;
+ u64 header = msg->header;
+ u8 stream_id = sst_byt_header_str_id(header);
+ u8 stream_msg = sst_byt_header_msg_id(header);
+
+ stream = sst_byt_get_stream(byt, stream_id);
+ if (stream == NULL)
+ return;
+
+ switch (stream_msg) {
+ case IPC_IA_DROP_STREAM:
+ case IPC_IA_PAUSE_STREAM:
+ case IPC_IA_FREE_STREAM:
+ stream->running = false;
+ break;
+ case IPC_IA_START_STREAM:
+ case IPC_IA_RESUME_STREAM:
+ stream->running = true;
+ break;
+ }
+}
+
+static int sst_byt_process_reply(struct sst_byt *byt, u64 header)
+{
+ struct ipc_message *msg;
+
+ msg = sst_ipc_reply_find_msg(&byt->ipc, header);
+ if (msg == NULL)
+ return 1;
+
+ if (header & IPC_HEADER_LARGE(true)) {
+ msg->rx_size = sst_byt_header_data(header);
+ sst_dsp_inbox_read(byt->dsp, msg->rx_data, msg->rx_size);
+ }
+
+ /* update any stream states */
+ sst_byt_stream_update(byt, msg);
+
+ list_del(&msg->list);
+ /* wake up */
+ sst_ipc_tx_msg_reply_complete(&byt->ipc, msg);
+
+ return 1;
+}
+
+static void sst_byt_fw_ready(struct sst_byt *byt, u64 header)
+{
+ dev_dbg(byt->dev, "ipc: DSP is ready 0x%llX\n", header);
+
+ byt->boot_complete = true;
+ wake_up(&byt->boot_wait);
+}
+
+static int sst_byt_process_notification(struct sst_byt *byt,
+ unsigned long *flags)
+{
+ struct sst_dsp *sst = byt->dsp;
+ struct sst_byt_stream *stream;
+ u64 header;
+ u8 msg_id, stream_id;
+ int handled = 1;
+
+ header = sst_dsp_shim_read64_unlocked(sst, SST_IPCD);
+ msg_id = sst_byt_header_msg_id(header);
+
+ switch (msg_id) {
+ case IPC_SST_PERIOD_ELAPSED:
+ stream_id = sst_byt_header_str_id(header);
+ stream = sst_byt_get_stream(byt, stream_id);
+ if (stream && stream->running && stream->notify_position) {
+ spin_unlock_irqrestore(&sst->spinlock, *flags);
+ stream->notify_position(stream, stream->pdata);
+ spin_lock_irqsave(&sst->spinlock, *flags);
+ }
+ break;
+ case IPC_IA_FW_INIT_CMPLT:
+ sst_byt_fw_ready(byt, header);
+ break;
+ }
+
+ return handled;
+}
+
+static irqreturn_t sst_byt_irq_thread(int irq, void *context)
+{
+ struct sst_dsp *sst = (struct sst_dsp *) context;
+ struct sst_byt *byt = sst_dsp_get_thread_context(sst);
+ struct sst_generic_ipc *ipc = &byt->ipc;
+ u64 header;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sst->spinlock, flags);
+
+ header = sst_dsp_shim_read64_unlocked(sst, SST_IPCD);
+ if (header & SST_BYT_IPCD_BUSY) {
+ if (header & IPC_NOTIFICATION) {
+ /* message from ADSP */
+ sst_byt_process_notification(byt, &flags);
+ } else {
+ /* reply from ADSP */
+ sst_byt_process_reply(byt, header);
+ }
+ /*
+ * clear IPCD BUSY bit and set DONE bit. Tell DSP we have
+ * processed the message and can accept new. Clear data part
+ * of the header
+ */
+ sst_dsp_shim_update_bits64_unlocked(sst, SST_IPCD,
+ SST_BYT_IPCD_DONE | SST_BYT_IPCD_BUSY |
+ IPC_HEADER_DATA(IPC_HEADER_DATA_MASK),
+ SST_BYT_IPCD_DONE);
+ /* unmask message request interrupts */
+ sst_dsp_shim_update_bits64_unlocked(sst, SST_IMRX,
+ SST_BYT_IMRX_REQUEST, 0);
+ }
+
+ spin_unlock_irqrestore(&sst->spinlock, flags);
+
+ /* continue to send any remaining messages... */
+ queue_kthread_work(&ipc->kworker, &ipc->kwork);
+
+ return IRQ_HANDLED;
+}
+
+/* stream API */
+struct sst_byt_stream *sst_byt_stream_new(struct sst_byt *byt, int id,
+ u32 (*notify_position)(struct sst_byt_stream *stream, void *data),
+ void *data)
+{
+ struct sst_byt_stream *stream;
+ struct sst_dsp *sst = byt->dsp;
+ unsigned long flags;
+
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (stream == NULL)
+ return NULL;
+
+ spin_lock_irqsave(&sst->spinlock, flags);
+ list_add(&stream->node, &byt->stream_list);
+ stream->notify_position = notify_position;
+ stream->pdata = data;
+ stream->byt = byt;
+ stream->str_id = id;
+ spin_unlock_irqrestore(&sst->spinlock, flags);
+
+ return stream;
+}
+
+int sst_byt_stream_set_bits(struct sst_byt *byt, struct sst_byt_stream *stream,
+ int bits)
+{
+ stream->request.pcm_params.pcm_wd_sz = bits;
+ return 0;
+}
+
+int sst_byt_stream_set_channels(struct sst_byt *byt,
+ struct sst_byt_stream *stream, u8 channels)
+{
+ stream->request.pcm_params.num_chan = channels;
+ return 0;
+}
+
+int sst_byt_stream_set_rate(struct sst_byt *byt, struct sst_byt_stream *stream,
+ unsigned int rate)
+{
+ stream->request.pcm_params.sfreq = rate;
+ return 0;
+}
+
+/* stream sonfiguration */
+int sst_byt_stream_type(struct sst_byt *byt, struct sst_byt_stream *stream,
+ int codec_type, int stream_type, int operation)
+{
+ stream->request.str_type.codec_type = codec_type;
+ stream->request.str_type.str_type = stream_type;
+ stream->request.str_type.operation = operation;
+ stream->request.str_type.time_slots = 0xc;
+
+ return 0;
+}
+
+int sst_byt_stream_buffer(struct sst_byt *byt, struct sst_byt_stream *stream,
+ uint32_t buffer_addr, uint32_t buffer_size)
+{
+ stream->request.frame_info.num_entries = 1;
+ stream->request.frame_info.ring_buf_info[0].addr = buffer_addr;
+ stream->request.frame_info.ring_buf_info[0].size = buffer_size;
+ /* calculate bytes per 4 ms fragment */
+ stream->request.frame_info.frag_size =
+ stream->request.pcm_params.sfreq *
+ stream->request.pcm_params.num_chan *
+ stream->request.pcm_params.pcm_wd_sz / 8 *
+ 4 / 1000;
+ return 0;
+}
+
+int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream)
+{
+ struct sst_byt_alloc_params *str_req = &stream->request;
+ struct sst_byt_alloc_response *reply = &stream->reply;
+ u64 header;
+ int ret;
+
+ header = sst_byt_header(IPC_IA_ALLOC_STREAM,
+ sizeof(*str_req) + sizeof(u32),
+ true, stream->str_id);
+ ret = sst_ipc_tx_message_wait(&byt->ipc, header, str_req,
+ sizeof(*str_req),
+ reply, sizeof(*reply));
+ if (ret < 0) {
+ dev_err(byt->dev, "ipc: error stream commit failed\n");
+ return ret;
+ }
+
+ stream->commited = true;
+
+ return 0;
+}
+
+int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream)
+{
+ u64 header;
+ int ret = 0;
+ struct sst_dsp *sst = byt->dsp;
+ unsigned long flags;
+
+ if (!stream->commited)
+ goto out;
+
+ header = sst_byt_header(IPC_IA_FREE_STREAM, 0, false, stream->str_id);
+ ret = sst_ipc_tx_message_wait(&byt->ipc, header, NULL, 0, NULL, 0);
+ if (ret < 0) {
+ dev_err(byt->dev, "ipc: free stream %d failed\n",
+ stream->str_id);
+ return -EAGAIN;
+ }
+
+ stream->commited = false;
+out:
+ spin_lock_irqsave(&sst->spinlock, flags);
+ list_del(&stream->node);
+ kfree(stream);
+ spin_unlock_irqrestore(&sst->spinlock, flags);
+
+ return ret;
+}
+
+static int sst_byt_stream_operations(struct sst_byt *byt, int type,
+ int stream_id, int wait)
+{
+ u64 header;
+
+ header = sst_byt_header(type, 0, false, stream_id);
+ if (wait)
+ return sst_ipc_tx_message_wait(&byt->ipc, header, NULL,
+ 0, NULL, 0);
+ else
+ return sst_ipc_tx_message_nowait(&byt->ipc, header,
+ NULL, 0);
+}
+
+/* stream ALSA trigger operations */
+int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream,
+ u32 start_offset)
+{
+ struct sst_byt_start_stream_params start_stream;
+ void *tx_msg;
+ size_t size;
+ u64 header;
+ int ret;
+
+ start_stream.byte_offset = start_offset;
+ header = sst_byt_header(IPC_IA_START_STREAM,
+ sizeof(start_stream) + sizeof(u32),
+ true, stream->str_id);
+ tx_msg = &start_stream;
+ size = sizeof(start_stream);
+
+ ret = sst_ipc_tx_message_nowait(&byt->ipc, header, tx_msg, size);
+ if (ret < 0)
+ dev_err(byt->dev, "ipc: error failed to start stream %d\n",
+ stream->str_id);
+
+ return ret;
+}
+
+int sst_byt_stream_stop(struct sst_byt *byt, struct sst_byt_stream *stream)
+{
+ int ret;
+
+ /* don't stop streams that are not commited */
+ if (!stream->commited)
+ return 0;
+
+ ret = sst_byt_stream_operations(byt, IPC_IA_DROP_STREAM,
+ stream->str_id, 0);
+ if (ret < 0)
+ dev_err(byt->dev, "ipc: error failed to stop stream %d\n",
+ stream->str_id);
+ return ret;
+}
+
+int sst_byt_stream_pause(struct sst_byt *byt, struct sst_byt_stream *stream)
+{
+ int ret;
+
+ ret = sst_byt_stream_operations(byt, IPC_IA_PAUSE_STREAM,
+ stream->str_id, 0);
+ if (ret < 0)
+ dev_err(byt->dev, "ipc: error failed to pause stream %d\n",
+ stream->str_id);
+
+ return ret;
+}
+
+int sst_byt_stream_resume(struct sst_byt *byt, struct sst_byt_stream *stream)
+{
+ int ret;
+
+ ret = sst_byt_stream_operations(byt, IPC_IA_RESUME_STREAM,
+ stream->str_id, 0);
+ if (ret < 0)
+ dev_err(byt->dev, "ipc: error failed to resume stream %d\n",
+ stream->str_id);
+
+ return ret;
+}
+
+int sst_byt_get_dsp_position(struct sst_byt *byt,
+ struct sst_byt_stream *stream, int buffer_size)
+{
+ struct sst_dsp *sst = byt->dsp;
+ struct sst_byt_tstamp fw_tstamp;
+ u8 str_id = stream->str_id;
+ u32 tstamp_offset;
+
+ tstamp_offset = SST_BYT_TIMESTAMP_OFFSET + str_id * sizeof(fw_tstamp);
+ memcpy_fromio(&fw_tstamp,
+ sst->addr.lpe + tstamp_offset, sizeof(fw_tstamp));
+
+ return do_div(fw_tstamp.ring_buffer_counter, buffer_size);
+}
+
+struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt)
+{
+ return byt->dsp;
+}
+
+static struct sst_dsp_device byt_dev = {
+ .thread = sst_byt_irq_thread,
+ .ops = &sst_byt_ops,
+};
+
+int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata)
+{
+ struct sst_byt *byt = pdata->dsp;
+
+ dev_dbg(byt->dev, "dsp reset\n");
+ sst_dsp_reset(byt->dsp);
+ sst_ipc_drop_all(&byt->ipc);
+ dev_dbg(byt->dev, "dsp in reset\n");
+
+ dev_dbg(byt->dev, "free all blocks and unload fw\n");
+ sst_fw_unload(byt->fw);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_byt_dsp_suspend_late);
+
+int sst_byt_dsp_boot(struct device *dev, struct sst_pdata *pdata)
+{
+ struct sst_byt *byt = pdata->dsp;
+ int ret;
+
+ dev_dbg(byt->dev, "reload dsp fw\n");
+
+ sst_dsp_reset(byt->dsp);
+
+ ret = sst_fw_reload(byt->fw);
+ if (ret < 0) {
+ dev_err(dev, "error: failed to reload firmware\n");
+ return ret;
+ }
+
+ /* wait for DSP boot completion */
+ byt->boot_complete = false;
+ sst_dsp_boot(byt->dsp);
+ dev_dbg(byt->dev, "dsp booting...\n");
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_byt_dsp_boot);
+
+int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata)
+{
+ struct sst_byt *byt = pdata->dsp;
+ int err;
+
+ dev_dbg(byt->dev, "wait for dsp reboot\n");
+
+ err = wait_event_timeout(byt->boot_wait, byt->boot_complete,
+ msecs_to_jiffies(IPC_BOOT_MSECS));
+ if (err == 0) {
+ dev_err(byt->dev, "ipc: error DSP boot timeout\n");
+ return -EIO;
+ }
+
+ dev_dbg(byt->dev, "dsp rebooted\n");
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_byt_dsp_wait_for_ready);
+
+static void byt_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg)
+{
+ if (msg->header & IPC_HEADER_LARGE(true))
+ sst_dsp_outbox_write(ipc->dsp, msg->tx_data, msg->tx_size);
+
+ sst_dsp_shim_write64_unlocked(ipc->dsp, SST_IPCX, msg->header);
+}
+
+static void byt_shim_dbg(struct sst_generic_ipc *ipc, const char *text)
+{
+ struct sst_dsp *sst = ipc->dsp;
+ u64 isr, ipcd, imrx, ipcx;
+
+ ipcx = sst_dsp_shim_read64_unlocked(sst, SST_IPCX);
+ isr = sst_dsp_shim_read64_unlocked(sst, SST_ISRX);
+ ipcd = sst_dsp_shim_read64_unlocked(sst, SST_IPCD);
+ imrx = sst_dsp_shim_read64_unlocked(sst, SST_IMRX);
+
+ dev_err(ipc->dev,
+ "ipc: --%s-- ipcx 0x%llx isr 0x%llx ipcd 0x%llx imrx 0x%llx\n",
+ text, ipcx, isr, ipcd, imrx);
+}
+
+static void byt_tx_data_copy(struct ipc_message *msg, char *tx_data,
+ size_t tx_size)
+{
+ /* msg content = lower 32-bit of the header + data */
+ *(u32 *)msg->tx_data = (u32)(msg->header & (u32)-1);
+ memcpy(msg->tx_data + sizeof(u32), tx_data, tx_size);
+ msg->tx_size += sizeof(u32);
+}
+
+static u64 byt_reply_msg_match(u64 header, u64 *mask)
+{
+ /* match reply to message sent based on msg and stream IDs */
+ *mask = IPC_HEADER_MSG_ID_MASK |
+ IPC_HEADER_STR_ID_MASK << IPC_HEADER_STR_ID_SHIFT;
+ header &= *mask;
+
+ return header;
+}
+
+int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
+{
+ struct sst_byt *byt;
+ struct sst_generic_ipc *ipc;
+ struct sst_fw *byt_sst_fw;
+ struct sst_byt_fw_init init;
+ int err;
+
+ dev_dbg(dev, "initialising Byt DSP IPC\n");
+
+ byt = devm_kzalloc(dev, sizeof(*byt), GFP_KERNEL);
+ if (byt == NULL)
+ return -ENOMEM;
+
+ ipc = &byt->ipc;
+ ipc->dev = dev;
+ ipc->ops.tx_msg = byt_tx_msg;
+ ipc->ops.shim_dbg = byt_shim_dbg;
+ ipc->ops.tx_data_copy = byt_tx_data_copy;
+ ipc->ops.reply_msg_match = byt_reply_msg_match;
+
+ err = sst_ipc_init(ipc);
+ if (err != 0)
+ goto ipc_init_err;
+
+ INIT_LIST_HEAD(&byt->stream_list);
+ init_waitqueue_head(&byt->boot_wait);
+ byt_dev.thread_context = byt;
+
+ /* init SST shim */
+ byt->dsp = sst_dsp_new(dev, &byt_dev, pdata);
+ if (byt->dsp == NULL) {
+ err = -ENODEV;
+ goto dsp_new_err;
+ }
+
+ ipc->dsp = byt->dsp;
+
+ /* keep the DSP in reset state for base FW loading */
+ sst_dsp_reset(byt->dsp);
+
+ byt_sst_fw = sst_fw_new(byt->dsp, pdata->fw, byt);
+ if (byt_sst_fw == NULL) {
+ err = -ENODEV;
+ dev_err(dev, "error: failed to load firmware\n");
+ goto fw_err;
+ }
+
+ /* wait for DSP boot completion */
+ sst_dsp_boot(byt->dsp);
+ err = wait_event_timeout(byt->boot_wait, byt->boot_complete,
+ msecs_to_jiffies(IPC_BOOT_MSECS));
+ if (err == 0) {
+ err = -EIO;
+ dev_err(byt->dev, "ipc: error DSP boot timeout\n");
+ goto boot_err;
+ }
+
+ /* show firmware information */
+ sst_dsp_inbox_read(byt->dsp, &init, sizeof(init));
+ dev_info(byt->dev, "FW version: %02x.%02x.%02x.%02x\n",
+ init.fw_version.major, init.fw_version.minor,
+ init.fw_version.build, init.fw_version.type);
+ dev_info(byt->dev, "Build type: %x\n", init.fw_version.type);
+ dev_info(byt->dev, "Build date: %s %s\n",
+ init.build_info.date, init.build_info.time);
+
+ pdata->dsp = byt;
+ byt->fw = byt_sst_fw;
+
+ return 0;
+
+boot_err:
+ sst_dsp_reset(byt->dsp);
+ sst_fw_free(byt_sst_fw);
+fw_err:
+ sst_dsp_free(byt->dsp);
+dsp_new_err:
+ sst_ipc_fini(ipc);
+ipc_init_err:
+ kfree(byt);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(sst_byt_dsp_init);
+
+void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata)
+{
+ struct sst_byt *byt = pdata->dsp;
+
+ sst_dsp_reset(byt->dsp);
+ sst_fw_free_all(byt->dsp);
+ sst_dsp_free(byt->dsp);
+ sst_ipc_fini(&byt->ipc);
+}
+EXPORT_SYMBOL_GPL(sst_byt_dsp_free);
--- /dev/null
+/*
+ * Intel Baytrail SST IPC Support
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef __SST_BYT_IPC_H
+#define __SST_BYT_IPC_H
+
+#include <linux/types.h>
+
+struct sst_byt;
+struct sst_byt_stream;
+struct sst_pdata;
+extern struct sst_ops sst_byt_ops;
+
+
+#define SST_BYT_MAILBOX_OFFSET 0x144000
+#define SST_BYT_TIMESTAMP_OFFSET (SST_BYT_MAILBOX_OFFSET + 0x800)
+
+/**
+ * Upfront defined maximum message size that is
+ * expected by the in/out communication pipes in FW.
+ */
+#define SST_BYT_IPC_MAX_PAYLOAD_SIZE 200
+
+/* stream API */
+struct sst_byt_stream *sst_byt_stream_new(struct sst_byt *byt, int id,
+ uint32_t (*get_write_position)(struct sst_byt_stream *stream,
+ void *data),
+ void *data);
+
+/* stream configuration */
+int sst_byt_stream_set_bits(struct sst_byt *byt, struct sst_byt_stream *stream,
+ int bits);
+int sst_byt_stream_set_channels(struct sst_byt *byt,
+ struct sst_byt_stream *stream, u8 channels);
+int sst_byt_stream_set_rate(struct sst_byt *byt, struct sst_byt_stream *stream,
+ unsigned int rate);
+int sst_byt_stream_type(struct sst_byt *byt, struct sst_byt_stream *stream,
+ int codec_type, int stream_type, int operation);
+int sst_byt_stream_buffer(struct sst_byt *byt, struct sst_byt_stream *stream,
+ uint32_t buffer_addr, uint32_t buffer_size);
+int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream);
+int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream);
+
+/* stream ALSA trigger operations */
+int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream,
+ u32 start_offset);
+int sst_byt_stream_stop(struct sst_byt *byt, struct sst_byt_stream *stream);
+int sst_byt_stream_pause(struct sst_byt *byt, struct sst_byt_stream *stream);
+int sst_byt_stream_resume(struct sst_byt *byt, struct sst_byt_stream *stream);
+
+int sst_byt_get_dsp_position(struct sst_byt *byt,
+ struct sst_byt_stream *stream, int buffer_size);
+
+/* init */
+int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata);
+void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata);
+struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt);
+int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata);
+int sst_byt_dsp_boot(struct device *dev, struct sst_pdata *pdata);
+int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata);
+
+#endif
--- /dev/null
+/*
+ * Intel Baytrail SST PCM Support
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "sst-baytrail-ipc.h"
+#include "../common/sst-dsp-priv.h"
+#include "../common/sst-dsp.h"
+
+#define BYT_PCM_COUNT 2
+
+static const struct snd_pcm_hardware sst_byt_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE,
+ .period_bytes_min = 384,
+ .period_bytes_max = 48000,
+ .periods_min = 2,
+ .periods_max = 250,
+ .buffer_bytes_max = 96000,
+};
+
+/* private data for each PCM DSP stream */
+struct sst_byt_pcm_data {
+ struct sst_byt_stream *stream;
+ struct snd_pcm_substream *substream;
+ struct mutex mutex;
+
+ /* latest DSP DMA hw pointer */
+ u32 hw_ptr;
+
+ struct work_struct work;
+};
+
+/* private data for the driver */
+struct sst_byt_priv_data {
+ /* runtime DSP */
+ struct sst_byt *byt;
+
+ /* DAI data */
+ struct sst_byt_pcm_data pcm[BYT_PCM_COUNT];
+
+ /* flag indicating is stream context restore needed after suspend */
+ bool restore_stream;
+};
+
+/* this may get called several times by oss emulation */
+static int sst_byt_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sst_byt_priv_data *pdata =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
+ struct sst_byt *byt = pdata->byt;
+ u32 rate, bits;
+ u8 channels;
+ int ret, playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+
+ dev_dbg(rtd->dev, "PCM: hw_params, pcm_data %p\n", pcm_data);
+
+ ret = sst_byt_stream_type(byt, pcm_data->stream,
+ 1, 1, !playback);
+ if (ret < 0) {
+ dev_err(rtd->dev, "failed to set stream format %d\n", ret);
+ return ret;
+ }
+
+ rate = params_rate(params);
+ ret = sst_byt_stream_set_rate(byt, pcm_data->stream, rate);
+ if (ret < 0) {
+ dev_err(rtd->dev, "could not set rate %d\n", rate);
+ return ret;
+ }
+
+ bits = snd_pcm_format_width(params_format(params));
+ ret = sst_byt_stream_set_bits(byt, pcm_data->stream, bits);
+ if (ret < 0) {
+ dev_err(rtd->dev, "could not set formats %d\n",
+ params_rate(params));
+ return ret;
+ }
+
+ channels = (u8)(params_channels(params) & 0xF);
+ ret = sst_byt_stream_set_channels(byt, pcm_data->stream, channels);
+ if (ret < 0) {
+ dev_err(rtd->dev, "could not set channels %d\n",
+ params_rate(params));
+ return ret;
+ }
+
+ snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+
+ ret = sst_byt_stream_buffer(byt, pcm_data->stream,
+ substream->dma_buffer.addr,
+ params_buffer_bytes(params));
+ if (ret < 0) {
+ dev_err(rtd->dev, "PCM: failed to set DMA buffer %d\n", ret);
+ return ret;
+ }
+
+ ret = sst_byt_stream_commit(byt, pcm_data->stream);
+ if (ret < 0) {
+ dev_err(rtd->dev, "PCM: failed stream commit %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sst_byt_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+ dev_dbg(rtd->dev, "PCM: hw_free\n");
+ snd_pcm_lib_free_pages(substream);
+
+ return 0;
+}
+
+static int sst_byt_pcm_restore_stream_context(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sst_byt_priv_data *pdata =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
+ struct sst_byt *byt = pdata->byt;
+ int ret;
+
+ /* commit stream using existing stream params */
+ ret = sst_byt_stream_commit(byt, pcm_data->stream);
+ if (ret < 0) {
+ dev_err(rtd->dev, "PCM: failed stream commit %d\n", ret);
+ return ret;
+ }
+
+ sst_byt_stream_start(byt, pcm_data->stream, pcm_data->hw_ptr);
+
+ dev_dbg(rtd->dev, "stream context restored at offset %d\n",
+ pcm_data->hw_ptr);
+
+ return 0;
+}
+
+static void sst_byt_pcm_work(struct work_struct *work)
+{
+ struct sst_byt_pcm_data *pcm_data =
+ container_of(work, struct sst_byt_pcm_data, work);
+
+ if (snd_pcm_running(pcm_data->substream))
+ sst_byt_pcm_restore_stream_context(pcm_data->substream);
+}
+
+static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sst_byt_priv_data *pdata =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
+ struct sst_byt *byt = pdata->byt;
+
+ dev_dbg(rtd->dev, "PCM: trigger %d\n", cmd);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ pcm_data->hw_ptr = 0;
+ sst_byt_stream_start(byt, pcm_data->stream, 0);
+ break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (pdata->restore_stream == true)
+ schedule_work(&pcm_data->work);
+ else
+ sst_byt_stream_resume(byt, pcm_data->stream);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ sst_byt_stream_resume(byt, pcm_data->stream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ sst_byt_stream_stop(byt, pcm_data->stream);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ pdata->restore_stream = false;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ sst_byt_stream_pause(byt, pcm_data->stream);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static u32 byt_notify_pointer(struct sst_byt_stream *stream, void *data)
+{
+ struct sst_byt_pcm_data *pcm_data = data;
+ struct snd_pcm_substream *substream = pcm_data->substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sst_byt_priv_data *pdata =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ struct sst_byt *byt = pdata->byt;
+ u32 pos, hw_pos;
+
+ hw_pos = sst_byt_get_dsp_position(byt, pcm_data->stream,
+ snd_pcm_lib_buffer_bytes(substream));
+ pcm_data->hw_ptr = hw_pos;
+ pos = frames_to_bytes(runtime,
+ (runtime->control->appl_ptr %
+ runtime->buffer_size));
+
+ dev_dbg(rtd->dev, "PCM: App/DMA pointer %u/%u bytes\n", pos, hw_pos);
+
+ snd_pcm_period_elapsed(substream);
+ return pos;
+}
+
+static snd_pcm_uframes_t sst_byt_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct sst_byt_priv_data *pdata =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
+
+ dev_dbg(rtd->dev, "PCM: DMA pointer %u bytes\n", pcm_data->hw_ptr);
+
+ return bytes_to_frames(runtime, pcm_data->hw_ptr);
+}
+
+static int sst_byt_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sst_byt_priv_data *pdata =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
+ struct sst_byt *byt = pdata->byt;
+
+ dev_dbg(rtd->dev, "PCM: open\n");
+
+ mutex_lock(&pcm_data->mutex);
+
+ pcm_data->substream = substream;
+
+ snd_soc_set_runtime_hwparams(substream, &sst_byt_pcm_hardware);
+
+ pcm_data->stream = sst_byt_stream_new(byt, substream->stream + 1,
+ byt_notify_pointer, pcm_data);
+ if (pcm_data->stream == NULL) {
+ dev_err(rtd->dev, "failed to create stream\n");
+ mutex_unlock(&pcm_data->mutex);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&pcm_data->mutex);
+ return 0;
+}
+
+static int sst_byt_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct sst_byt_priv_data *pdata =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
+ struct sst_byt *byt = pdata->byt;
+ int ret;
+
+ dev_dbg(rtd->dev, "PCM: close\n");
+
+ cancel_work_sync(&pcm_data->work);
+ mutex_lock(&pcm_data->mutex);
+ ret = sst_byt_stream_free(byt, pcm_data->stream);
+ if (ret < 0) {
+ dev_dbg(rtd->dev, "Free stream fail\n");
+ goto out;
+ }
+ pcm_data->stream = NULL;
+
+out:
+ mutex_unlock(&pcm_data->mutex);
+ return ret;
+}
+
+static int sst_byt_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+ dev_dbg(rtd->dev, "PCM: mmap\n");
+ return snd_pcm_lib_default_mmap(substream, vma);
+}
+
+static struct snd_pcm_ops sst_byt_pcm_ops = {
+ .open = sst_byt_pcm_open,
+ .close = sst_byt_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = sst_byt_pcm_hw_params,
+ .hw_free = sst_byt_pcm_hw_free,
+ .trigger = sst_byt_pcm_trigger,
+ .pointer = sst_byt_pcm_pointer,
+ .mmap = sst_byt_pcm_mmap,
+};
+
+static int sst_byt_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ size_t size;
+ struct snd_soc_platform *platform = rtd->platform;
+ struct sst_pdata *pdata = dev_get_platdata(platform->dev);
+ int ret = 0;
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream ||
+ pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+ size = sst_byt_pcm_hardware.buffer_bytes_max;
+ ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
+ SNDRV_DMA_TYPE_DEV,
+ pdata->dma_dev,
+ size, size);
+ if (ret) {
+ dev_err(rtd->dev, "dma buffer allocation failed %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static struct snd_soc_dai_driver byt_dais[] = {
+ {
+ .name = "Baytrail PCM",
+ .playback = {
+ .stream_name = "System Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Analog Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ },
+};
+
+static int sst_byt_pcm_probe(struct snd_soc_platform *platform)
+{
+ struct sst_pdata *plat_data = dev_get_platdata(platform->dev);
+ struct sst_byt_priv_data *priv_data;
+ int i;
+
+ if (!plat_data)
+ return -ENODEV;
+
+ priv_data = devm_kzalloc(platform->dev, sizeof(*priv_data),
+ GFP_KERNEL);
+ priv_data->byt = plat_data->dsp;
+ snd_soc_platform_set_drvdata(platform, priv_data);
+
+ for (i = 0; i < BYT_PCM_COUNT; i++) {
+ mutex_init(&priv_data->pcm[i].mutex);
+ INIT_WORK(&priv_data->pcm[i].work, sst_byt_pcm_work);
+ }
+
+ return 0;
+}
+
+static int sst_byt_pcm_remove(struct snd_soc_platform *platform)
+{
+ return 0;
+}
+
+static struct snd_soc_platform_driver byt_soc_platform = {
+ .probe = sst_byt_pcm_probe,
+ .remove = sst_byt_pcm_remove,
+ .ops = &sst_byt_pcm_ops,
+ .pcm_new = sst_byt_pcm_new,
+};
+
+static const struct snd_soc_component_driver byt_dai_component = {
+ .name = "byt-dai",
+};
+
+#ifdef CONFIG_PM
+static int sst_byt_pcm_dev_suspend_late(struct device *dev)
+{
+ struct sst_pdata *sst_pdata = dev_get_platdata(dev);
+ struct sst_byt_priv_data *priv_data = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(dev, "suspending late\n");
+
+ ret = sst_byt_dsp_suspend_late(dev, sst_pdata);
+ if (ret < 0) {
+ dev_err(dev, "failed to suspend %d\n", ret);
+ return ret;
+ }
+
+ priv_data->restore_stream = true;
+
+ return ret;
+}
+
+static int sst_byt_pcm_dev_resume_early(struct device *dev)
+{
+ struct sst_pdata *sst_pdata = dev_get_platdata(dev);
+ int ret;
+
+ dev_dbg(dev, "resume early\n");
+
+ /* load fw and boot DSP */
+ ret = sst_byt_dsp_boot(dev, sst_pdata);
+ if (ret)
+ return ret;
+
+ /* wait for FW to finish booting */
+ return sst_byt_dsp_wait_for_ready(dev, sst_pdata);
+}
+
+static const struct dev_pm_ops sst_byt_pm_ops = {
+ .suspend_late = sst_byt_pcm_dev_suspend_late,
+ .resume_early = sst_byt_pcm_dev_resume_early,
+};
+
+#define SST_BYT_PM_OPS (&sst_byt_pm_ops)
+#else
+#define SST_BYT_PM_OPS NULL
+#endif
+
+static int sst_byt_pcm_dev_probe(struct platform_device *pdev)
+{
+ struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
+ int ret;
+
+ ret = sst_byt_dsp_init(&pdev->dev, sst_pdata);
+ if (ret < 0)
+ return -ENODEV;
+
+ ret = snd_soc_register_platform(&pdev->dev, &byt_soc_platform);
+ if (ret < 0)
+ goto err_plat;
+
+ ret = snd_soc_register_component(&pdev->dev, &byt_dai_component,
+ byt_dais, ARRAY_SIZE(byt_dais));
+ if (ret < 0)
+ goto err_comp;
+
+ return 0;
+
+err_comp:
+ snd_soc_unregister_platform(&pdev->dev);
+err_plat:
+ sst_byt_dsp_free(&pdev->dev, sst_pdata);
+ return ret;
+}
+
+static int sst_byt_pcm_dev_remove(struct platform_device *pdev)
+{
+ struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
+
+ snd_soc_unregister_platform(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
+ sst_byt_dsp_free(&pdev->dev, sst_pdata);
+
+ return 0;
+}
+
+static struct platform_driver sst_byt_pcm_driver = {
+ .driver = {
+ .name = "baytrail-pcm-audio",
+ .pm = SST_BYT_PM_OPS,
+ },
+
+ .probe = sst_byt_pcm_dev_probe,
+ .remove = sst_byt_pcm_dev_remove,
+};
+module_platform_driver(sst_byt_pcm_driver);
+
+MODULE_AUTHOR("Jarkko Nikula");
+MODULE_DESCRIPTION("Baytrail PCM");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:baytrail-pcm-audio");
--- /dev/null
+snd-soc-sst-haswell-objs := haswell.o
+snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o
+snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o
+snd-soc-sst-broadwell-objs := broadwell.o
+snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o
+snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
+snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o
+
+obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
+obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o
+obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o
+obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o
+obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o
+obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
+obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o
--- /dev/null
+/*
+ * Intel Broadwell Wildcatpoint SST Audio
+ *
+ * Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+
+#include "../common/sst-dsp.h"
+#include "../haswell/sst-haswell-ipc.h"
+
+#include "../../codecs/rt286.h"
+
+static struct snd_soc_jack broadwell_headset;
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin broadwell_headset_pins[] = {
+ {
+ .pin = "Mic Jack",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+
+static const struct snd_kcontrol_new broadwell_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+};
+
+static const struct snd_soc_dapm_widget broadwell_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_MIC("DMIC1", NULL),
+ SND_SOC_DAPM_MIC("DMIC2", NULL),
+ SND_SOC_DAPM_LINE("Line Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route broadwell_rt286_map[] = {
+
+ /* speaker */
+ {"Speaker", NULL, "SPOR"},
+ {"Speaker", NULL, "SPOL"},
+
+ /* HP jack connectors - unknown if we have jack deteck */
+ {"Headphone Jack", NULL, "HPO Pin"},
+
+ /* other jacks */
+ {"MIC1", NULL, "Mic Jack"},
+ {"LINE1", NULL, "Line Jack"},
+
+ /* digital mics */
+ {"DMIC1 Pin", NULL, "DMIC1"},
+ {"DMIC2 Pin", NULL, "DMIC2"},
+
+ /* CODEC BE connections */
+ {"SSP0 CODEC IN", NULL, "AIF1 Capture"},
+ {"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
+};
+
+static int broadwell_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_codec *codec = rtd->codec;
+ int ret = 0;
+ ret = snd_soc_card_jack_new(rtd->card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0, &broadwell_headset,
+ broadwell_headset_pins, ARRAY_SIZE(broadwell_headset_pins));
+ if (ret)
+ return ret;
+
+ rt286_mic_detect(codec, &broadwell_headset);
+ return 0;
+}
+
+
+static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ /* The ADSP will covert the FE rate to 48k, stereo */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSP0 to 16 bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
+ return 0;
+}
+
+static int broadwell_rt286_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT286_SCLK_S_PLL, 24000000,
+ SND_SOC_CLOCK_IN);
+
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec sysclk configuration\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static struct snd_soc_ops broadwell_rt286_ops = {
+ .hw_params = broadwell_rt286_hw_params,
+};
+
+static int broadwell_rtd_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct sst_pdata *pdata = dev_get_platdata(rtd->platform->dev);
+ struct sst_hsw *broadwell = pdata->dsp;
+ int ret;
+
+ /* Set ADSP SSP port settings */
+ ret = sst_hsw_device_set_config(broadwell, SST_HSW_DEVICE_SSP_0,
+ SST_HSW_DEVICE_MCLK_FREQ_24_MHZ,
+ SST_HSW_DEVICE_CLOCK_MASTER, 9);
+ if (ret < 0) {
+ dev_err(rtd->dev, "error: failed to set device config\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/* broadwell digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link broadwell_rt286_dais[] = {
+ /* Front End DAI links */
+ {
+ .name = "System PCM",
+ .stream_name = "System Playback/Capture",
+ .cpu_dai_name = "System Pin",
+ .platform_name = "haswell-pcm-audio",
+ .dynamic = 1,
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .init = broadwell_rtd_init,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ },
+ {
+ .name = "Offload0",
+ .stream_name = "Offload0 Playback",
+ .cpu_dai_name = "Offload0 Pin",
+ .platform_name = "haswell-pcm-audio",
+ .dynamic = 1,
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dpcm_playback = 1,
+ },
+ {
+ .name = "Offload1",
+ .stream_name = "Offload1 Playback",
+ .cpu_dai_name = "Offload1 Pin",
+ .platform_name = "haswell-pcm-audio",
+ .dynamic = 1,
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dpcm_playback = 1,
+ },
+ {
+ .name = "Loopback PCM",
+ .stream_name = "Loopback",
+ .cpu_dai_name = "Loopback Pin",
+ .platform_name = "haswell-pcm-audio",
+ .dynamic = 0,
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dpcm_capture = 1,
+ },
+ /* Back End DAI links */
+ {
+ /* SSP0 - Codec */
+ .name = "Codec",
+ .be_id = 0,
+ .cpu_dai_name = "snd-soc-dummy-dai",
+ .platform_name = "snd-soc-dummy",
+ .no_pcm = 1,
+ .codec_name = "i2c-INT343A:00",
+ .codec_dai_name = "rt286-aif1",
+ .init = broadwell_rt286_codec_init,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .be_hw_params_fixup = broadwell_ssp0_fixup,
+ .ops = &broadwell_rt286_ops,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ },
+};
+
+static int broadwell_suspend(struct snd_soc_card *card){
+ struct snd_soc_codec *codec;
+
+ list_for_each_entry(codec, &card->codec_dev_list, card_list) {
+ if (!strcmp(codec->component.name, "i2c-INT343A:00")) {
+ dev_dbg(codec->dev, "disabling jack detect before going to suspend.\n");
+ rt286_mic_detect(codec, NULL);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int broadwell_resume(struct snd_soc_card *card){
+ struct snd_soc_codec *codec;
+
+ list_for_each_entry(codec, &card->codec_dev_list, card_list) {
+ if (!strcmp(codec->component.name, "i2c-INT343A:00")) {
+ dev_dbg(codec->dev, "enabling jack detect for resume.\n");
+ rt286_mic_detect(codec, &broadwell_headset);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* broadwell audio machine driver for WPT + RT286S */
+static struct snd_soc_card broadwell_rt286 = {
+ .name = "broadwell-rt286",
+ .owner = THIS_MODULE,
+ .dai_link = broadwell_rt286_dais,
+ .num_links = ARRAY_SIZE(broadwell_rt286_dais),
+ .controls = broadwell_controls,
+ .num_controls = ARRAY_SIZE(broadwell_controls),
+ .dapm_widgets = broadwell_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(broadwell_widgets),
+ .dapm_routes = broadwell_rt286_map,
+ .num_dapm_routes = ARRAY_SIZE(broadwell_rt286_map),
+ .fully_routed = true,
+ .suspend_pre = broadwell_suspend,
+ .resume_post = broadwell_resume,
+};
+
+static int broadwell_audio_probe(struct platform_device *pdev)
+{
+ broadwell_rt286.dev = &pdev->dev;
+
+ return snd_soc_register_card(&broadwell_rt286);
+}
+
+static int broadwell_audio_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_card(&broadwell_rt286);
+ return 0;
+}
+
+static struct platform_driver broadwell_audio = {
+ .probe = broadwell_audio_probe,
+ .remove = broadwell_audio_remove,
+ .driver = {
+ .name = "broadwell-audio",
+ },
+};
+
+module_platform_driver(broadwell_audio)
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood, Xingchao Wang");
+MODULE_DESCRIPTION("Intel SST Audio for WPT/Broadwell");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:broadwell-audio");
--- /dev/null
+/*
+ * Intel Baytrail SST MAX98090 machine driver
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include "../../codecs/max98090.h"
+
+struct byt_max98090_private {
+ struct snd_soc_jack jack;
+};
+
+static const struct snd_soc_dapm_widget byt_max98090_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route byt_max98090_audio_map[] = {
+ {"IN34", NULL, "Headset Mic"},
+ {"Headset Mic", NULL, "MICBIAS"},
+ {"DMICL", NULL, "Int Mic"},
+ {"Headphone", NULL, "HPL"},
+ {"Headphone", NULL, "HPR"},
+ {"Ext Spk", NULL, "SPKL"},
+ {"Ext Spk", NULL, "SPKR"},
+};
+
+static const struct snd_kcontrol_new byt_max98090_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+ SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static struct snd_soc_jack_pin hs_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static struct snd_soc_jack_gpio hs_jack_gpios[] = {
+ {
+ .name = "hp-gpio",
+ .idx = 0,
+ .report = SND_JACK_HEADPHONE | SND_JACK_LINEOUT,
+ .debounce_time = 200,
+ },
+ {
+ .name = "mic-gpio",
+ .idx = 1,
+ .invert = 1,
+ .report = SND_JACK_MICROPHONE,
+ .debounce_time = 200,
+ },
+};
+
+static int byt_max98090_init(struct snd_soc_pcm_runtime *runtime)
+{
+ int ret;
+ struct snd_soc_card *card = runtime->card;
+ struct byt_max98090_private *drv = snd_soc_card_get_drvdata(card);
+ struct snd_soc_jack *jack = &drv->jack;
+
+ card->dapm.idle_bias_off = true;
+
+ ret = snd_soc_dai_set_sysclk(runtime->codec_dai,
+ M98090_REG_SYSTEM_CLOCK,
+ 25000000, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "Can't set codec clock %d\n", ret);
+ return ret;
+ }
+
+ /* Enable jack detection */
+ ret = snd_soc_card_jack_new(runtime->card, "Headset",
+ SND_JACK_LINEOUT | SND_JACK_HEADSET, jack,
+ hs_jack_pins, ARRAY_SIZE(hs_jack_pins));
+ if (ret)
+ return ret;
+
+ return snd_soc_jack_add_gpiods(card->dev->parent, jack,
+ ARRAY_SIZE(hs_jack_gpios),
+ hs_jack_gpios);
+}
+
+static struct snd_soc_dai_link byt_max98090_dais[] = {
+ {
+ .name = "Baytrail Audio",
+ .stream_name = "Audio",
+ .cpu_dai_name = "baytrail-pcm-audio",
+ .codec_dai_name = "HiFi",
+ .codec_name = "i2c-193C9890:00",
+ .platform_name = "baytrail-pcm-audio",
+ .init = byt_max98090_init,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ },
+};
+
+static struct snd_soc_card byt_max98090_card = {
+ .name = "byt-max98090",
+ .dai_link = byt_max98090_dais,
+ .num_links = ARRAY_SIZE(byt_max98090_dais),
+ .dapm_widgets = byt_max98090_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(byt_max98090_widgets),
+ .dapm_routes = byt_max98090_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(byt_max98090_audio_map),
+ .controls = byt_max98090_controls,
+ .num_controls = ARRAY_SIZE(byt_max98090_controls),
+ .fully_routed = true,
+};
+
+static int byt_max98090_probe(struct platform_device *pdev)
+{
+ int ret_val = 0;
+ struct byt_max98090_private *priv;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC);
+ if (!priv) {
+ dev_err(&pdev->dev, "allocation failed\n");
+ return -ENOMEM;
+ }
+
+ byt_max98090_card.dev = &pdev->dev;
+ snd_soc_card_set_drvdata(&byt_max98090_card, priv);
+ ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_max98090_card);
+ if (ret_val) {
+ dev_err(&pdev->dev,
+ "snd_soc_register_card failed %d\n", ret_val);
+ return ret_val;
+ }
+
+ return ret_val;
+}
+
+static int byt_max98090_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct byt_max98090_private *priv = snd_soc_card_get_drvdata(card);
+
+ snd_soc_jack_free_gpios(&priv->jack, ARRAY_SIZE(hs_jack_gpios),
+ hs_jack_gpios);
+
+ return 0;
+}
+
+static struct platform_driver byt_max98090_driver = {
+ .probe = byt_max98090_probe,
+ .remove = byt_max98090_remove,
+ .driver = {
+ .name = "byt-max98090",
+ .pm = &snd_soc_pm_ops,
+ },
+};
+module_platform_driver(byt_max98090_driver)
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail Machine driver");
+MODULE_AUTHOR("Omair Md Abdullah, Jarkko Nikula");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:byt-max98090");
--- /dev/null
+/*
+ * Intel Baytrail SST RT5640 machine driver
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/dmi.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include "../../codecs/rt5640.h"
+
+#include "../common/sst-dsp.h"
+
+static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Internal Mic", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = {
+ {"Headset Mic", NULL, "MICBIAS1"},
+ {"IN2P", NULL, "Headset Mic"},
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+ {"Speaker", NULL, "SPOLP"},
+ {"Speaker", NULL, "SPOLN"},
+ {"Speaker", NULL, "SPORP"},
+ {"Speaker", NULL, "SPORN"},
+};
+
+static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic1_map[] = {
+ {"DMIC1", NULL, "Internal Mic"},
+};
+
+static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic2_map[] = {
+ {"DMIC2", NULL, "Internal Mic"},
+};
+
+static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = {
+ {"Internal Mic", NULL, "MICBIAS1"},
+ {"IN1P", NULL, "Internal Mic"},
+};
+
+enum {
+ BYT_RT5640_DMIC1_MAP,
+ BYT_RT5640_DMIC2_MAP,
+ BYT_RT5640_IN1_MAP,
+};
+
+#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff)
+#define BYT_RT5640_DMIC_EN BIT(16)
+
+static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP |
+ BYT_RT5640_DMIC_EN;
+
+static const struct snd_kcontrol_new byt_rt5640_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Internal Mic"),
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+};
+
+static int byt_rt5640_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1,
+ params_rate(params) * 256,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set codec clock %d\n", ret);
+ return ret;
+ }
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1,
+ params_rate(params) * 64,
+ params_rate(params) * 256);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "can't set codec pll: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int byt_rt5640_quirk_cb(const struct dmi_system_id *id)
+{
+ byt_rt5640_quirk = (unsigned long)id->driver_data;
+ return 1;
+}
+
+static const struct dmi_system_id byt_rt5640_quirk_table[] = {
+ {
+ .callback = byt_rt5640_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"),
+ },
+ .driver_data = (unsigned long *)BYT_RT5640_IN1_MAP,
+ },
+ {
+ .callback = byt_rt5640_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "DellInc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"),
+ },
+ .driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP |
+ BYT_RT5640_DMIC_EN),
+ },
+ {}
+};
+
+static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
+{
+ int ret;
+ struct snd_soc_codec *codec = runtime->codec;
+ struct snd_soc_card *card = runtime->card;
+ const struct snd_soc_dapm_route *custom_map;
+ int num_routes;
+
+ card->dapm.idle_bias_off = true;
+
+ ret = snd_soc_add_card_controls(card, byt_rt5640_controls,
+ ARRAY_SIZE(byt_rt5640_controls));
+ if (ret) {
+ dev_err(card->dev, "unable to add card controls\n");
+ return ret;
+ }
+
+ dmi_check_system(byt_rt5640_quirk_table);
+ switch (BYT_RT5640_MAP(byt_rt5640_quirk)) {
+ case BYT_RT5640_IN1_MAP:
+ custom_map = byt_rt5640_intmic_in1_map;
+ num_routes = ARRAY_SIZE(byt_rt5640_intmic_in1_map);
+ break;
+ case BYT_RT5640_DMIC2_MAP:
+ custom_map = byt_rt5640_intmic_dmic2_map;
+ num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic2_map);
+ break;
+ default:
+ custom_map = byt_rt5640_intmic_dmic1_map;
+ num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic1_map);
+ }
+
+ ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes);
+ if (ret)
+ return ret;
+
+ if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) {
+ ret = rt5640_dmic_enable(codec, 0, 0);
+ if (ret)
+ return ret;
+ }
+
+ snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
+ snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker");
+
+ return ret;
+}
+
+static struct snd_soc_ops byt_rt5640_ops = {
+ .hw_params = byt_rt5640_hw_params,
+};
+
+static struct snd_soc_dai_link byt_rt5640_dais[] = {
+ {
+ .name = "Baytrail Audio",
+ .stream_name = "Audio",
+ .cpu_dai_name = "baytrail-pcm-audio",
+ .codec_dai_name = "rt5640-aif1",
+ .codec_name = "i2c-10EC5640:00",
+ .platform_name = "baytrail-pcm-audio",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .init = byt_rt5640_init,
+ .ops = &byt_rt5640_ops,
+ },
+};
+
+static struct snd_soc_card byt_rt5640_card = {
+ .name = "byt-rt5640",
+ .dai_link = byt_rt5640_dais,
+ .num_links = ARRAY_SIZE(byt_rt5640_dais),
+ .dapm_widgets = byt_rt5640_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(byt_rt5640_widgets),
+ .dapm_routes = byt_rt5640_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(byt_rt5640_audio_map),
+ .fully_routed = true,
+};
+
+static int byt_rt5640_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &byt_rt5640_card;
+
+ card->dev = &pdev->dev;
+ return devm_snd_soc_register_card(&pdev->dev, card);
+}
+
+static struct platform_driver byt_rt5640_audio = {
+ .probe = byt_rt5640_probe,
+ .driver = {
+ .name = "byt-rt5640",
+ .pm = &snd_soc_pm_ops,
+ },
+};
+module_platform_driver(byt_rt5640_audio)
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail Machine driver");
+MODULE_AUTHOR("Omair Md Abdullah, Jarkko Nikula");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:byt-rt5640");
--- /dev/null
+/*
+ * byt_cr_dpcm_rt5640.c - ASoc Machine driver for Intel Byt CR platform
+ *
+ * Copyright (C) 2014 Intel Corp
+ * Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../../codecs/rt5640.h"
+#include "../atom/sst-atom-controls.h"
+
+static const struct snd_soc_dapm_widget byt_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route byt_audio_map[] = {
+ {"IN2P", NULL, "Headset Mic"},
+ {"IN2N", NULL, "Headset Mic"},
+ {"Headset Mic", NULL, "MICBIAS1"},
+ {"IN1P", NULL, "MICBIAS1"},
+ {"LDO2", NULL, "Int Mic"},
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+ {"Ext Spk", NULL, "SPOLP"},
+ {"Ext Spk", NULL, "SPOLN"},
+ {"Ext Spk", NULL, "SPORP"},
+ {"Ext Spk", NULL, "SPORN"},
+
+ {"AIF1 Playback", NULL, "ssp2 Tx"},
+ {"ssp2 Tx", NULL, "codec_out0"},
+ {"ssp2 Tx", NULL, "codec_out1"},
+ {"codec_in0", NULL, "ssp2 Rx"},
+ {"codec_in1", NULL, "ssp2 Rx"},
+ {"ssp2 Rx", NULL, "AIF1 Capture"},
+};
+
+static const struct snd_kcontrol_new byt_mc_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+ SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static int byt_aif1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret;
+
+ snd_soc_dai_set_bclk_ratio(codec_dai, 50);
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1,
+ params_rate(params) * 512,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec clock %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1,
+ params_rate(params) * 50,
+ params_rate(params) * 512);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_pcm_stream byt_dai_params = {
+ .formats = SNDRV_PCM_FMTBIT_S24_LE,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+};
+
+static int byt_codec_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ /* The DSP will covert the FE rate to 48k, stereo, 24bits */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSP2 to 24-bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+ return 0;
+}
+
+static unsigned int rates_48000[] = {
+ 48000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_48000 = {
+ .count = ARRAY_SIZE(rates_48000),
+ .list = rates_48000,
+};
+
+static int byt_aif1_startup(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_48000);
+}
+
+static struct snd_soc_ops byt_aif1_ops = {
+ .startup = byt_aif1_startup,
+};
+
+static struct snd_soc_ops byt_be_ssp2_ops = {
+ .hw_params = byt_aif1_hw_params,
+};
+
+static struct snd_soc_dai_link byt_dailink[] = {
+ [MERR_DPCM_AUDIO] = {
+ .name = "Baytrail Audio Port",
+ .stream_name = "Baytrail Audio",
+ .cpu_dai_name = "media-cpu-dai",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .platform_name = "sst-mfld-platform",
+ .ignore_suspend = 1,
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ops = &byt_aif1_ops,
+ },
+ [MERR_DPCM_COMPR] = {
+ .name = "Baytrail Compressed Port",
+ .stream_name = "Baytrail Compress",
+ .cpu_dai_name = "compress-cpu-dai",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .platform_name = "sst-mfld-platform",
+ },
+ /* back ends */
+ {
+ .name = "SSP2-Codec",
+ .be_id = 1,
+ .cpu_dai_name = "ssp2-port",
+ .platform_name = "sst-mfld-platform",
+ .no_pcm = 1,
+ .codec_dai_name = "rt5640-aif1",
+ .codec_name = "i2c-10EC5640:00",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBS_CFS,
+ .be_hw_params_fixup = byt_codec_fixup,
+ .ignore_suspend = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ops = &byt_be_ssp2_ops,
+ },
+};
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_byt = {
+ .name = "baytrailcraudio",
+ .dai_link = byt_dailink,
+ .num_links = ARRAY_SIZE(byt_dailink),
+ .dapm_widgets = byt_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(byt_dapm_widgets),
+ .dapm_routes = byt_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(byt_audio_map),
+ .controls = byt_mc_controls,
+ .num_controls = ARRAY_SIZE(byt_mc_controls),
+};
+
+static int snd_byt_mc_probe(struct platform_device *pdev)
+{
+ int ret_val = 0;
+
+ /* register the soc card */
+ snd_soc_card_byt.dev = &pdev->dev;
+
+ ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_byt);
+ if (ret_val) {
+ dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n", ret_val);
+ return ret_val;
+ }
+ platform_set_drvdata(pdev, &snd_soc_card_byt);
+ return ret_val;
+}
+
+static struct platform_driver snd_byt_mc_driver = {
+ .driver = {
+ .name = "bytt100_rt5640",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = snd_byt_mc_probe,
+};
+
+module_platform_driver(snd_byt_mc_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
+MODULE_AUTHOR("Subhransu S. Prusty <subhransu.s.prusty@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bytt100_rt5640");
--- /dev/null
+/*
+ * cht-bsw-rt5645.c - ASoc Machine driver for Intel Cherryview-based platforms
+ * Cherrytrail and Braswell, with RT5645 codec.
+ *
+ * Copyright (C) 2015 Intel Corp
+ * Author: Fang, Yang A <yang.a.fang@intel.com>
+ * N,Harshapriya <harshapriya.n@intel.com>
+ * This file is modified from cht_bsw_rt5672.c
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include "../../codecs/rt5645.h"
+#include "../atom/sst-atom-controls.h"
+
+#define CHT_PLAT_CLK_3_HZ 19200000
+#define CHT_CODEC_DAI "rt5645-aif1"
+
+struct cht_mc_private {
+ struct snd_soc_jack hp_jack;
+ struct snd_soc_jack mic_jack;
+};
+
+static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
+{
+ int i;
+
+ for (i = 0; i < card->num_rtd; i++) {
+ struct snd_soc_pcm_runtime *rtd;
+
+ rtd = card->rtd + i;
+ if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
+ strlen(CHT_CODEC_DAI)))
+ return rtd->codec_dai;
+ }
+ return NULL;
+}
+
+static int platform_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_card *card = dapm->card;
+ struct snd_soc_dai *codec_dai;
+ int ret;
+
+ codec_dai = cht_get_codec_dai(card);
+ if (!codec_dai) {
+ dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");
+ return -EIO;
+ }
+
+ if (!SND_SOC_DAPM_EVENT_OFF(event))
+ return 0;
+
+ /* Set codec sysclk source to its internal clock because codec PLL will
+ * be off when idle and MCLK will also be off by ACPI when codec is
+ * runtime suspended. Codec needs clock for jack detection and button
+ * press.
+ */
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_RCCLK,
+ 0, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+ SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+ platform_clock_control, SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route cht_audio_map[] = {
+ {"IN1P", NULL, "Headset Mic"},
+ {"IN1N", NULL, "Headset Mic"},
+ {"DMIC L1", NULL, "Int Mic"},
+ {"DMIC R1", NULL, "Int Mic"},
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+ {"Ext Spk", NULL, "SPOL"},
+ {"Ext Spk", NULL, "SPOR"},
+ {"AIF1 Playback", NULL, "ssp2 Tx"},
+ {"ssp2 Tx", NULL, "codec_out0"},
+ {"ssp2 Tx", NULL, "codec_out1"},
+ {"codec_in0", NULL, "ssp2 Rx" },
+ {"codec_in1", NULL, "ssp2 Rx" },
+ {"ssp2 Rx", NULL, "AIF1 Capture"},
+ {"Headphone", NULL, "Platform Clock"},
+ {"Headset Mic", NULL, "Platform Clock"},
+ {"Int Mic", NULL, "Platform Clock"},
+ {"Ext Spk", NULL, "Platform Clock"},
+};
+
+static const struct snd_kcontrol_new cht_mc_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+ SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret;
+
+ /* set codec PLL source to the 19.2MHz platform clock (MCLK) */
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT5645_PLL1_S_MCLK,
+ CHT_PLAT_CLK_3_HZ, params_rate(params) * 512);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_PLL1,
+ params_rate(params) * 512, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+ int ret;
+ struct snd_soc_codec *codec = runtime->codec;
+ struct snd_soc_dai *codec_dai = runtime->codec_dai;
+ struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card);
+
+ /* Select clk_i2s1_asrc as ASRC clock source */
+ rt5645_sel_asrc_clk_src(codec,
+ RT5645_DA_STEREO_FILTER |
+ RT5645_DA_MONO_L_FILTER |
+ RT5645_DA_MONO_R_FILTER |
+ RT5645_AD_STEREO_FILTER,
+ RT5645_CLK_SEL_I2S1_ASRC);
+
+ /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24);
+ if (ret < 0) {
+ dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new(runtime->card, "Headphone Jack",
+ SND_JACK_HEADPHONE, &ctx->hp_jack,
+ NULL, 0);
+ if (ret) {
+ dev_err(runtime->dev, "HP jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_card_jack_new(runtime->card, "Mic Jack",
+ SND_JACK_MICROPHONE, &ctx->mic_jack,
+ NULL, 0);
+ if (ret) {
+ dev_err(runtime->dev, "Mic jack creation failed %d\n", ret);
+ return ret;
+ }
+
+ rt5645_set_jack_detect(codec, &ctx->hp_jack, &ctx->mic_jack);
+
+ return ret;
+}
+
+static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ /* The DSP will covert the FE rate to 48k, stereo, 24bits */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSP2 to 24-bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+ return 0;
+}
+
+static unsigned int rates_48000[] = {
+ 48000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_48000 = {
+ .count = ARRAY_SIZE(rates_48000),
+ .list = rates_48000,
+};
+
+static int cht_aif1_startup(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_48000);
+}
+
+static struct snd_soc_ops cht_aif1_ops = {
+ .startup = cht_aif1_startup,
+};
+
+static struct snd_soc_ops cht_be_ssp2_ops = {
+ .hw_params = cht_aif1_hw_params,
+};
+
+static struct snd_soc_dai_link cht_dailink[] = {
+ [MERR_DPCM_AUDIO] = {
+ .name = "Audio Port",
+ .stream_name = "Audio",
+ .cpu_dai_name = "media-cpu-dai",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .platform_name = "sst-mfld-platform",
+ .ignore_suspend = 1,
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ops = &cht_aif1_ops,
+ },
+ [MERR_DPCM_COMPR] = {
+ .name = "Compressed Port",
+ .stream_name = "Compress",
+ .cpu_dai_name = "compress-cpu-dai",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .platform_name = "sst-mfld-platform",
+ },
+ /* CODEC<->CODEC link */
+ /* back ends */
+ {
+ .name = "SSP2-Codec",
+ .be_id = 1,
+ .cpu_dai_name = "ssp2-port",
+ .platform_name = "sst-mfld-platform",
+ .no_pcm = 1,
+ .codec_dai_name = "rt5645-aif1",
+ .codec_name = "i2c-10EC5645:00",
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF
+ | SND_SOC_DAIFMT_CBS_CFS,
+ .init = cht_codec_init,
+ .be_hw_params_fixup = cht_codec_fixup,
+ .ignore_suspend = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ops = &cht_be_ssp2_ops,
+ },
+};
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_cht = {
+ .name = "chtrt5645",
+ .dai_link = cht_dailink,
+ .num_links = ARRAY_SIZE(cht_dailink),
+ .dapm_widgets = cht_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
+ .dapm_routes = cht_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cht_audio_map),
+ .controls = cht_mc_controls,
+ .num_controls = ARRAY_SIZE(cht_mc_controls),
+};
+
+static int snd_cht_mc_probe(struct platform_device *pdev)
+{
+ int ret_val = 0;
+ struct cht_mc_private *drv;
+
+ drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
+ if (!drv)
+ return -ENOMEM;
+
+ snd_soc_card_cht.dev = &pdev->dev;
+ snd_soc_card_set_drvdata(&snd_soc_card_cht, drv);
+ ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
+ if (ret_val) {
+ dev_err(&pdev->dev,
+ "snd_soc_register_card failed %d\n", ret_val);
+ return ret_val;
+ }
+ platform_set_drvdata(pdev, &snd_soc_card_cht);
+ return ret_val;
+}
+
+static struct platform_driver snd_cht_mc_driver = {
+ .driver = {
+ .name = "cht-bsw-rt5645",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = snd_cht_mc_probe,
+};
+
+module_platform_driver(snd_cht_mc_driver)
+
+MODULE_DESCRIPTION("ASoC Intel(R) Braswell Machine driver");
+MODULE_AUTHOR("Fang, Yang A,N,Harshapriya");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cht-bsw-rt5645");
--- /dev/null
+/*
+ * cht_bsw_rt5672.c - ASoc Machine driver for Intel Cherryview-based platforms
+ * Cherrytrail and Braswell, with RT5672 codec.
+ *
+ * Copyright (C) 2014 Intel Corp
+ * Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
+ * Mengdong Lin <mengdong.lin@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include "../../codecs/rt5670.h"
+#include "../atom/sst-atom-controls.h"
+
+/* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */
+#define CHT_PLAT_CLK_3_HZ 19200000
+#define CHT_CODEC_DAI "rt5670-aif1"
+
+static struct snd_soc_jack cht_bsw_headset;
+
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin cht_bsw_headset_pins[] = {
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+
+static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
+{
+ int i;
+
+ for (i = 0; i < card->num_rtd; i++) {
+ struct snd_soc_pcm_runtime *rtd;
+
+ rtd = card->rtd + i;
+ if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
+ strlen(CHT_CODEC_DAI)))
+ return rtd->codec_dai;
+ }
+ return NULL;
+}
+
+static int platform_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_card *card = dapm->card;
+ struct snd_soc_dai *codec_dai;
+ int ret;
+
+ codec_dai = cht_get_codec_dai(card);
+ if (!codec_dai) {
+ dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");
+ return -EIO;
+ }
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* set codec PLL source to the 19.2MHz platform clock (MCLK) */
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK,
+ CHT_PLAT_CLK_3_HZ, 48000 * 512);
+ if (ret < 0) {
+ dev_err(card->dev, "can't set codec pll: %d\n", ret);
+ return ret;
+ }
+
+ /* set codec sysclk source to PLL */
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_PLL1,
+ 48000 * 512, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
+ return ret;
+ }
+ } else {
+ /* Set codec sysclk source to its internal clock because codec
+ * PLL will be off when idle and MCLK will also be off by ACPI
+ * when codec is runtime suspended. Codec needs clock for jack
+ * detection and button press.
+ */
+ snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK,
+ 48000 * 512, SND_SOC_CLOCK_IN);
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+ SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+ platform_clock_control, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route cht_audio_map[] = {
+ {"IN1P", NULL, "Headset Mic"},
+ {"IN1N", NULL, "Headset Mic"},
+ {"DMIC L1", NULL, "Int Mic"},
+ {"DMIC R1", NULL, "Int Mic"},
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+ {"Ext Spk", NULL, "SPOLP"},
+ {"Ext Spk", NULL, "SPOLN"},
+ {"Ext Spk", NULL, "SPORP"},
+ {"Ext Spk", NULL, "SPORN"},
+ {"AIF1 Playback", NULL, "ssp2 Tx"},
+ {"ssp2 Tx", NULL, "codec_out0"},
+ {"ssp2 Tx", NULL, "codec_out1"},
+ {"codec_in0", NULL, "ssp2 Rx"},
+ {"codec_in1", NULL, "ssp2 Rx"},
+ {"ssp2 Rx", NULL, "AIF1 Capture"},
+ {"Headphone", NULL, "Platform Clock"},
+ {"Headset Mic", NULL, "Platform Clock"},
+ {"Int Mic", NULL, "Platform Clock"},
+ {"Ext Spk", NULL, "Platform Clock"},
+};
+
+static const struct snd_kcontrol_new cht_mc_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+ SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret;
+
+ /* set codec PLL source to the 19.2MHz platform clock (MCLK) */
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK,
+ CHT_PLAT_CLK_3_HZ, params_rate(params) * 512);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+ return ret;
+ }
+
+ /* set codec sysclk source to PLL */
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_PLL1,
+ params_rate(params) * 512,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+ int ret;
+ struct snd_soc_dai *codec_dai = runtime->codec_dai;
+ struct snd_soc_codec *codec = codec_dai->codec;
+
+ /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24);
+ if (ret < 0) {
+ dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret);
+ return ret;
+ }
+
+ /* Select codec ASRC clock source to track I2S1 clock, because codec
+ * is in slave mode and 100fs I2S format (BCLK = 100 * LRCLK) cannot
+ * be supported by RT5672. Otherwise, ASRC will be disabled and cause
+ * noise.
+ */
+ rt5670_sel_asrc_clk_src(codec,
+ RT5670_DA_STEREO_FILTER
+ | RT5670_DA_MONO_L_FILTER
+ | RT5670_DA_MONO_R_FILTER
+ | RT5670_AD_STEREO_FILTER
+ | RT5670_AD_MONO_L_FILTER
+ | RT5670_AD_MONO_R_FILTER,
+ RT5670_CLK_SEL_I2S1_ASRC);
+
+ ret = snd_soc_card_jack_new(runtime->card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2, &cht_bsw_headset,
+ cht_bsw_headset_pins, ARRAY_SIZE(cht_bsw_headset_pins));
+ if (ret)
+ return ret;
+
+ rt5670_set_jack_detect(codec, &cht_bsw_headset);
+ return 0;
+}
+
+static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ /* The DSP will covert the FE rate to 48k, stereo, 24bits */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSP2 to 24-bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+ return 0;
+}
+
+static unsigned int rates_48000[] = {
+ 48000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_48000 = {
+ .count = ARRAY_SIZE(rates_48000),
+ .list = rates_48000,
+};
+
+static int cht_aif1_startup(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_48000);
+}
+
+static struct snd_soc_ops cht_aif1_ops = {
+ .startup = cht_aif1_startup,
+};
+
+static struct snd_soc_ops cht_be_ssp2_ops = {
+ .hw_params = cht_aif1_hw_params,
+};
+
+static struct snd_soc_dai_link cht_dailink[] = {
+ /* Front End DAI links */
+ [MERR_DPCM_AUDIO] = {
+ .name = "Audio Port",
+ .stream_name = "Audio",
+ .cpu_dai_name = "media-cpu-dai",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .platform_name = "sst-mfld-platform",
+ .nonatomic = true,
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ops = &cht_aif1_ops,
+ },
+ [MERR_DPCM_COMPR] = {
+ .name = "Compressed Port",
+ .stream_name = "Compress",
+ .cpu_dai_name = "compress-cpu-dai",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .platform_name = "sst-mfld-platform",
+ },
+
+ /* Back End DAI links */
+ {
+ /* SSP2 - Codec */
+ .name = "SSP2-Codec",
+ .be_id = 1,
+ .cpu_dai_name = "ssp2-port",
+ .platform_name = "sst-mfld-platform",
+ .no_pcm = 1,
+ .nonatomic = true,
+ .codec_dai_name = "rt5670-aif1",
+ .codec_name = "i2c-10EC5670:00",
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF
+ | SND_SOC_DAIFMT_CBS_CFS,
+ .init = cht_codec_init,
+ .be_hw_params_fixup = cht_codec_fixup,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ops = &cht_be_ssp2_ops,
+ },
+};
+
+static int cht_suspend_pre(struct snd_soc_card *card)
+{
+ struct snd_soc_codec *codec;
+
+ list_for_each_entry(codec, &card->codec_dev_list, card_list) {
+ if (!strcmp(codec->component.name, "i2c-10EC5670:00")) {
+ dev_dbg(codec->dev, "disabling jack detect before going to suspend.\n");
+ rt5670_jack_suspend(codec);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int cht_resume_post(struct snd_soc_card *card)
+{
+ struct snd_soc_codec *codec;
+
+ list_for_each_entry(codec, &card->codec_dev_list, card_list) {
+ if (!strcmp(codec->component.name, "i2c-10EC5670:00")) {
+ dev_dbg(codec->dev, "enabling jack detect for resume.\n");
+ rt5670_jack_resume(codec);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_cht = {
+ .name = "cherrytrailcraudio",
+ .dai_link = cht_dailink,
+ .num_links = ARRAY_SIZE(cht_dailink),
+ .dapm_widgets = cht_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
+ .dapm_routes = cht_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cht_audio_map),
+ .controls = cht_mc_controls,
+ .num_controls = ARRAY_SIZE(cht_mc_controls),
+ .suspend_pre = cht_suspend_pre,
+ .resume_post = cht_resume_post,
+};
+
+static int snd_cht_mc_probe(struct platform_device *pdev)
+{
+ int ret_val = 0;
+
+ /* register the soc card */
+ snd_soc_card_cht.dev = &pdev->dev;
+ ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
+ if (ret_val) {
+ dev_err(&pdev->dev,
+ "snd_soc_register_card failed %d\n", ret_val);
+ return ret_val;
+ }
+ platform_set_drvdata(pdev, &snd_soc_card_cht);
+ return ret_val;
+}
+
+static struct platform_driver snd_cht_mc_driver = {
+ .driver = {
+ .name = "cht-bsw-rt5672",
+ },
+ .probe = snd_cht_mc_probe,
+};
+
+module_platform_driver(snd_cht_mc_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
+MODULE_AUTHOR("Subhransu S. Prusty, Mengdong Lin");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cht-bsw-rt5672");
--- /dev/null
+/*
+ * Intel Haswell Lynxpoint SST Audio
+ *
+ * Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "../common/sst-dsp.h"
+#include "../haswell/sst-haswell-ipc.h"
+
+#include "../../codecs/rt5640.h"
+
+/* Haswell ULT platforms have a Headphone and Mic jack */
+static const struct snd_soc_dapm_widget haswell_widgets[] = {
+ SND_SOC_DAPM_HP("Headphones", NULL),
+ SND_SOC_DAPM_MIC("Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route haswell_rt5640_map[] = {
+
+ {"Headphones", NULL, "HPOR"},
+ {"Headphones", NULL, "HPOL"},
+ {"IN2P", NULL, "Mic"},
+
+ /* CODEC BE connections */
+ {"SSP0 CODEC IN", NULL, "AIF1 Capture"},
+ {"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
+};
+
+static int haswell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ /* The ADSP will covert the FE rate to 48k, stereo */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSP0 to 16 bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
+ return 0;
+}
+
+static int haswell_rt5640_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_MCLK, 12288000,
+ SND_SOC_CLOCK_IN);
+
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec sysclk configuration\n");
+ return ret;
+ }
+
+ /* set correct codec filter for DAI format and clock config */
+ snd_soc_update_bits(rtd->codec, 0x83, 0xffff, 0x8000);
+
+ return ret;
+}
+
+static struct snd_soc_ops haswell_rt5640_ops = {
+ .hw_params = haswell_rt5640_hw_params,
+};
+
+static int haswell_rtd_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct sst_pdata *pdata = dev_get_platdata(rtd->platform->dev);
+ struct sst_hsw *haswell = pdata->dsp;
+ int ret;
+
+ /* Set ADSP SSP port settings */
+ ret = sst_hsw_device_set_config(haswell, SST_HSW_DEVICE_SSP_0,
+ SST_HSW_DEVICE_MCLK_FREQ_24_MHZ,
+ SST_HSW_DEVICE_CLOCK_MASTER, 9);
+ if (ret < 0) {
+ dev_err(rtd->dev, "failed to set device config\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_link haswell_rt5640_dais[] = {
+ /* Front End DAI links */
+ {
+ .name = "System",
+ .stream_name = "System Playback/Capture",
+ .cpu_dai_name = "System Pin",
+ .platform_name = "haswell-pcm-audio",
+ .dynamic = 1,
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .init = haswell_rtd_init,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ },
+ {
+ .name = "Offload0",
+ .stream_name = "Offload0 Playback",
+ .cpu_dai_name = "Offload0 Pin",
+ .platform_name = "haswell-pcm-audio",
+ .dynamic = 1,
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dpcm_playback = 1,
+ },
+ {
+ .name = "Offload1",
+ .stream_name = "Offload1 Playback",
+ .cpu_dai_name = "Offload1 Pin",
+ .platform_name = "haswell-pcm-audio",
+ .dynamic = 1,
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dpcm_playback = 1,
+ },
+ {
+ .name = "Loopback",
+ .stream_name = "Loopback",
+ .cpu_dai_name = "Loopback Pin",
+ .platform_name = "haswell-pcm-audio",
+ .dynamic = 0,
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dpcm_capture = 1,
+ },
+
+ /* Back End DAI links */
+ {
+ /* SSP0 - Codec */
+ .name = "Codec",
+ .be_id = 0,
+ .cpu_dai_name = "snd-soc-dummy-dai",
+ .platform_name = "snd-soc-dummy",
+ .no_pcm = 1,
+ .codec_name = "i2c-INT33CA:00",
+ .codec_dai_name = "rt5640-aif1",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .be_hw_params_fixup = haswell_ssp0_fixup,
+ .ops = &haswell_rt5640_ops,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ },
+};
+
+/* audio machine driver for Haswell Lynxpoint DSP + RT5640 */
+static struct snd_soc_card haswell_rt5640 = {
+ .name = "haswell-rt5640",
+ .owner = THIS_MODULE,
+ .dai_link = haswell_rt5640_dais,
+ .num_links = ARRAY_SIZE(haswell_rt5640_dais),
+ .dapm_widgets = haswell_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(haswell_widgets),
+ .dapm_routes = haswell_rt5640_map,
+ .num_dapm_routes = ARRAY_SIZE(haswell_rt5640_map),
+ .fully_routed = true,
+};
+
+static int haswell_audio_probe(struct platform_device *pdev)
+{
+ haswell_rt5640.dev = &pdev->dev;
+
+ return devm_snd_soc_register_card(&pdev->dev, &haswell_rt5640);
+}
+
+static struct platform_driver haswell_audio = {
+ .probe = haswell_audio_probe,
+ .driver = {
+ .name = "haswell-audio",
+ },
+};
+
+module_platform_driver(haswell_audio)
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood, Xingchao Wang");
+MODULE_DESCRIPTION("Intel SST Audio for Haswell Lynxpoint");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:haswell-audio");
--- /dev/null
+/*
+ * mfld_machine.c - ASoc Machine driver for Intel Medfield MID platform
+ *
+ * Copyright (C) 2010 Intel Corp
+ * Author: Vinod Koul <vinod.koul@intel.com>
+ * Author: Harsha Priya <priya.harsha@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include "../codecs/sn95031.h"
+
+#define MID_MONO 1
+#define MID_STEREO 2
+#define MID_MAX_CAP 5
+#define MFLD_JACK_INSERT 0x04
+
+enum soc_mic_bias_zones {
+ MFLD_MV_START = 0,
+ /* mic bias volutage range for Headphones*/
+ MFLD_MV_HP = 400,
+ /* mic bias volutage range for American Headset*/
+ MFLD_MV_AM_HS = 650,
+ /* mic bias volutage range for Headset*/
+ MFLD_MV_HS = 2000,
+ MFLD_MV_UNDEFINED,
+};
+
+static unsigned int hs_switch;
+static unsigned int lo_dac;
+static struct snd_soc_codec *mfld_codec;
+
+struct mfld_mc_private {
+ void __iomem *int_base;
+ u8 interrupt_status;
+};
+
+struct snd_soc_jack mfld_jack;
+
+/*Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin mfld_jack_pins[] = {
+ {
+ .pin = "Headphones",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "AMIC1",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+/* jack detection voltage zones */
+static struct snd_soc_jack_zone mfld_zones[] = {
+ {MFLD_MV_START, MFLD_MV_AM_HS, SND_JACK_HEADPHONE},
+ {MFLD_MV_AM_HS, MFLD_MV_HS, SND_JACK_HEADSET},
+};
+
+/* sound card controls */
+static const char *headset_switch_text[] = {"Earpiece", "Headset"};
+
+static const char *lo_text[] = {"Vibra", "Headset", "IHF", "None"};
+
+static const struct soc_enum headset_enum =
+ SOC_ENUM_SINGLE_EXT(2, headset_switch_text);
+
+static const struct soc_enum lo_enum =
+ SOC_ENUM_SINGLE_EXT(4, lo_text);
+
+static int headset_get_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = hs_switch;
+ return 0;
+}
+
+static int headset_set_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = &card->dapm;
+
+ if (ucontrol->value.integer.value[0] == hs_switch)
+ return 0;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ if (ucontrol->value.integer.value[0]) {
+ pr_debug("hs_set HS path\n");
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Headphones");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT");
+ } else {
+ pr_debug("hs_set EP path\n");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones");
+ snd_soc_dapm_enable_pin_unlocked(dapm, "EPOUT");
+ }
+
+ snd_soc_dapm_sync_unlocked(dapm);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ hs_switch = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static void lo_enable_out_pins(struct snd_soc_dapm_context *dapm)
+{
+ snd_soc_dapm_enable_pin_unlocked(dapm, "IHFOUTL");
+ snd_soc_dapm_enable_pin_unlocked(dapm, "IHFOUTR");
+ snd_soc_dapm_enable_pin_unlocked(dapm, "LINEOUTL");
+ snd_soc_dapm_enable_pin_unlocked(dapm, "LINEOUTR");
+ snd_soc_dapm_enable_pin_unlocked(dapm, "VIB1OUT");
+ snd_soc_dapm_enable_pin_unlocked(dapm, "VIB2OUT");
+ if (hs_switch) {
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Headphones");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT");
+ } else {
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones");
+ snd_soc_dapm_enable_pin_unlocked(dapm, "EPOUT");
+ }
+}
+
+static int lo_get_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = lo_dac;
+ return 0;
+}
+
+static int lo_set_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = &card->dapm;
+
+ if (ucontrol->value.integer.value[0] == lo_dac)
+ return 0;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ /* we dont want to work with last state of lineout so just enable all
+ * pins and then disable pins not required
+ */
+ lo_enable_out_pins(dapm);
+
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ pr_debug("set vibra path\n");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "VIB1OUT");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "VIB2OUT");
+ snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0);
+ break;
+
+ case 1:
+ pr_debug("set hs path\n");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT");
+ snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0x22);
+ break;
+
+ case 2:
+ pr_debug("set spkr path\n");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "IHFOUTL");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "IHFOUTR");
+ snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0x44);
+ break;
+
+ case 3:
+ pr_debug("set null path\n");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "LINEOUTL");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "LINEOUTR");
+ snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0x66);
+ break;
+ }
+
+ snd_soc_dapm_sync_unlocked(dapm);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ lo_dac = ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static const struct snd_kcontrol_new mfld_snd_controls[] = {
+ SOC_ENUM_EXT("Playback Switch", headset_enum,
+ headset_get_switch, headset_set_switch),
+ SOC_ENUM_EXT("Lineout Mux", lo_enum,
+ lo_get_switch, lo_set_switch),
+};
+
+static const struct snd_soc_dapm_widget mfld_widgets[] = {
+ SND_SOC_DAPM_HP("Headphones", NULL),
+ SND_SOC_DAPM_MIC("Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route mfld_map[] = {
+ {"Headphones", NULL, "HPOUTR"},
+ {"Headphones", NULL, "HPOUTL"},
+ {"Mic", NULL, "AMIC1"},
+};
+
+static void mfld_jack_check(unsigned int intr_status)
+{
+ struct mfld_jack_data jack_data;
+
+ if (!mfld_codec)
+ return;
+
+ jack_data.mfld_jack = &mfld_jack;
+ jack_data.intr_id = intr_status;
+
+ sn95031_jack_detection(mfld_codec, &jack_data);
+ /* TODO: add american headset detection post gpiolib support */
+}
+
+static int mfld_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_dapm_context *dapm = &runtime->card->dapm;
+ int ret_val;
+
+ /* default is earpiece pin, userspace sets it explcitly */
+ snd_soc_dapm_disable_pin(dapm, "Headphones");
+ /* default is lineout NC, userspace sets it explcitly */
+ snd_soc_dapm_disable_pin(dapm, "LINEOUTL");
+ snd_soc_dapm_disable_pin(dapm, "LINEOUTR");
+ lo_dac = 3;
+ hs_switch = 0;
+ /* we dont use linein in this so set to NC */
+ snd_soc_dapm_disable_pin(dapm, "LINEINL");
+ snd_soc_dapm_disable_pin(dapm, "LINEINR");
+
+ /* Headset and button jack detection */
+ ret_val = snd_soc_card_jack_new(runtime->card,
+ "Intel(R) MID Audio Jack", SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1, &mfld_jack,
+ mfld_jack_pins, ARRAY_SIZE(mfld_jack_pins));
+ if (ret_val) {
+ pr_err("jack creation failed\n");
+ return ret_val;
+ }
+
+ ret_val = snd_soc_jack_add_zones(&mfld_jack,
+ ARRAY_SIZE(mfld_zones), mfld_zones);
+ if (ret_val) {
+ pr_err("adding jack zones failed\n");
+ return ret_val;
+ }
+
+ mfld_codec = runtime->codec;
+
+ /* we want to check if anything is inserted at boot,
+ * so send a fake event to codec and it will read adc
+ * to find if anything is there or not */
+ mfld_jack_check(MFLD_JACK_INSERT);
+ return ret_val;
+}
+
+static struct snd_soc_dai_link mfld_msic_dailink[] = {
+ {
+ .name = "Medfield Headset",
+ .stream_name = "Headset",
+ .cpu_dai_name = "Headset-cpu-dai",
+ .codec_dai_name = "SN95031 Headset",
+ .codec_name = "sn95031",
+ .platform_name = "sst-platform",
+ .init = mfld_init,
+ },
+ {
+ .name = "Medfield Speaker",
+ .stream_name = "Speaker",
+ .cpu_dai_name = "Speaker-cpu-dai",
+ .codec_dai_name = "SN95031 Speaker",
+ .codec_name = "sn95031",
+ .platform_name = "sst-platform",
+ .init = NULL,
+ },
+ {
+ .name = "Medfield Vibra",
+ .stream_name = "Vibra1",
+ .cpu_dai_name = "Vibra1-cpu-dai",
+ .codec_dai_name = "SN95031 Vibra1",
+ .codec_name = "sn95031",
+ .platform_name = "sst-platform",
+ .init = NULL,
+ },
+ {
+ .name = "Medfield Haptics",
+ .stream_name = "Vibra2",
+ .cpu_dai_name = "Vibra2-cpu-dai",
+ .codec_dai_name = "SN95031 Vibra2",
+ .codec_name = "sn95031",
+ .platform_name = "sst-platform",
+ .init = NULL,
+ },
+ {
+ .name = "Medfield Compress",
+ .stream_name = "Speaker",
+ .cpu_dai_name = "Compress-cpu-dai",
+ .codec_dai_name = "SN95031 Speaker",
+ .codec_name = "sn95031",
+ .platform_name = "sst-platform",
+ .init = NULL,
+ },
+};
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_mfld = {
+ .name = "medfield_audio",
+ .owner = THIS_MODULE,
+ .dai_link = mfld_msic_dailink,
+ .num_links = ARRAY_SIZE(mfld_msic_dailink),
+
+ .controls = mfld_snd_controls,
+ .num_controls = ARRAY_SIZE(mfld_snd_controls),
+ .dapm_widgets = mfld_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mfld_widgets),
+ .dapm_routes = mfld_map,
+ .num_dapm_routes = ARRAY_SIZE(mfld_map),
+};
+
+static irqreturn_t snd_mfld_jack_intr_handler(int irq, void *dev)
+{
+ struct mfld_mc_private *mc_private = (struct mfld_mc_private *) dev;
+
+ memcpy_fromio(&mc_private->interrupt_status,
+ ((void *)(mc_private->int_base)),
+ sizeof(u8));
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t snd_mfld_jack_detection(int irq, void *data)
+{
+ struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data;
+
+ mfld_jack_check(mc_drv_ctx->interrupt_status);
+
+ return IRQ_HANDLED;
+}
+
+static int snd_mfld_mc_probe(struct platform_device *pdev)
+{
+ int ret_val = 0, irq;
+ struct mfld_mc_private *mc_drv_ctx;
+ struct resource *irq_mem;
+
+ pr_debug("snd_mfld_mc_probe called\n");
+
+ /* retrive the irq number */
+ irq = platform_get_irq(pdev, 0);
+
+ /* audio interrupt base of SRAM location where
+ * interrupts are stored by System FW */
+ mc_drv_ctx = devm_kzalloc(&pdev->dev, sizeof(*mc_drv_ctx), GFP_ATOMIC);
+ if (!mc_drv_ctx) {
+ pr_err("allocation failed\n");
+ return -ENOMEM;
+ }
+
+ irq_mem = platform_get_resource_byname(
+ pdev, IORESOURCE_MEM, "IRQ_BASE");
+ if (!irq_mem) {
+ pr_err("no mem resource given\n");
+ return -ENODEV;
+ }
+ mc_drv_ctx->int_base = devm_ioremap_nocache(&pdev->dev, irq_mem->start,
+ resource_size(irq_mem));
+ if (!mc_drv_ctx->int_base) {
+ pr_err("Mapping of cache failed\n");
+ return -ENOMEM;
+ }
+ /* register for interrupt */
+ ret_val = devm_request_threaded_irq(&pdev->dev, irq,
+ snd_mfld_jack_intr_handler,
+ snd_mfld_jack_detection,
+ IRQF_SHARED, pdev->dev.driver->name, mc_drv_ctx);
+ if (ret_val) {
+ pr_err("cannot register IRQ\n");
+ return ret_val;
+ }
+ /* register the soc card */
+ snd_soc_card_mfld.dev = &pdev->dev;
+ ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_mfld);
+ if (ret_val) {
+ pr_debug("snd_soc_register_card failed %d\n", ret_val);
+ return ret_val;
+ }
+ platform_set_drvdata(pdev, mc_drv_ctx);
+ pr_debug("successfully exited probe\n");
+ return 0;
+}
+
+static struct platform_driver snd_mfld_mc_driver = {
+ .driver = {
+ .name = "msic_audio",
+ },
+ .probe = snd_mfld_mc_probe,
+};
+
+module_platform_driver(snd_mfld_mc_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) MID Machine driver");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:msic-audio");
+++ /dev/null
-/*
- * Intel Broadwell Wildcatpoint SST Audio
- *
- * Copyright (C) 2013, Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/jack.h>
-#include <sound/pcm_params.h>
-
-#include "sst-dsp.h"
-#include "sst-haswell-ipc.h"
-
-#include "../codecs/rt286.h"
-
-static struct snd_soc_jack broadwell_headset;
-/* Headset jack detection DAPM pins */
-static struct snd_soc_jack_pin broadwell_headset_pins[] = {
- {
- .pin = "Mic Jack",
- .mask = SND_JACK_MICROPHONE,
- },
- {
- .pin = "Headphone Jack",
- .mask = SND_JACK_HEADPHONE,
- },
-};
-
-static const struct snd_kcontrol_new broadwell_controls[] = {
- SOC_DAPM_PIN_SWITCH("Speaker"),
- SOC_DAPM_PIN_SWITCH("Headphone Jack"),
-};
-
-static const struct snd_soc_dapm_widget broadwell_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_SPK("Speaker", NULL),
- SND_SOC_DAPM_MIC("Mic Jack", NULL),
- SND_SOC_DAPM_MIC("DMIC1", NULL),
- SND_SOC_DAPM_MIC("DMIC2", NULL),
- SND_SOC_DAPM_LINE("Line Jack", NULL),
-};
-
-static const struct snd_soc_dapm_route broadwell_rt286_map[] = {
-
- /* speaker */
- {"Speaker", NULL, "SPOR"},
- {"Speaker", NULL, "SPOL"},
-
- /* HP jack connectors - unknown if we have jack deteck */
- {"Headphone Jack", NULL, "HPO Pin"},
-
- /* other jacks */
- {"MIC1", NULL, "Mic Jack"},
- {"LINE1", NULL, "Line Jack"},
-
- /* digital mics */
- {"DMIC1 Pin", NULL, "DMIC1"},
- {"DMIC2 Pin", NULL, "DMIC2"},
-
- /* CODEC BE connections */
- {"SSP0 CODEC IN", NULL, "AIF1 Capture"},
- {"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
-};
-
-static int broadwell_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_codec *codec = rtd->codec;
- int ret = 0;
- ret = snd_soc_jack_new(codec, "Headset",
- SND_JACK_HEADSET | SND_JACK_BTN_0, &broadwell_headset);
-
- if (ret)
- return ret;
-
- ret = snd_soc_jack_add_pins(&broadwell_headset,
- ARRAY_SIZE(broadwell_headset_pins),
- broadwell_headset_pins);
- if (ret)
- return ret;
-
- rt286_mic_detect(codec, &broadwell_headset);
- return 0;
-}
-
-
-static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_hw_params *params)
-{
- struct snd_interval *rate = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_RATE);
- struct snd_interval *channels = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_CHANNELS);
-
- /* The ADSP will covert the FE rate to 48k, stereo */
- rate->min = rate->max = 48000;
- channels->min = channels->max = 2;
-
- /* set SSP0 to 16 bit */
- snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT -
- SNDRV_PCM_HW_PARAM_FIRST_MASK],
- SNDRV_PCM_FORMAT_S16_LE);
- return 0;
-}
-
-static int broadwell_rt286_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, RT286_SCLK_S_PLL, 24000000,
- SND_SOC_CLOCK_IN);
-
- if (ret < 0) {
- dev_err(rtd->dev, "can't set codec sysclk configuration\n");
- return ret;
- }
-
- return ret;
-}
-
-static struct snd_soc_ops broadwell_rt286_ops = {
- .hw_params = broadwell_rt286_hw_params,
-};
-
-static int broadwell_rtd_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct sst_pdata *pdata = dev_get_platdata(rtd->platform->dev);
- struct sst_hsw *broadwell = pdata->dsp;
- int ret;
-
- /* Set ADSP SSP port settings */
- ret = sst_hsw_device_set_config(broadwell, SST_HSW_DEVICE_SSP_0,
- SST_HSW_DEVICE_MCLK_FREQ_24_MHZ,
- SST_HSW_DEVICE_CLOCK_MASTER, 9);
- if (ret < 0) {
- dev_err(rtd->dev, "error: failed to set device config\n");
- return ret;
- }
-
- return 0;
-}
-
-/* broadwell digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link broadwell_rt286_dais[] = {
- /* Front End DAI links */
- {
- .name = "System PCM",
- .stream_name = "System Playback/Capture",
- .cpu_dai_name = "System Pin",
- .platform_name = "haswell-pcm-audio",
- .dynamic = 1,
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .init = broadwell_rtd_init,
- .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
- .dpcm_playback = 1,
- .dpcm_capture = 1,
- },
- {
- .name = "Offload0",
- .stream_name = "Offload0 Playback",
- .cpu_dai_name = "Offload0 Pin",
- .platform_name = "haswell-pcm-audio",
- .dynamic = 1,
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
- .dpcm_playback = 1,
- },
- {
- .name = "Offload1",
- .stream_name = "Offload1 Playback",
- .cpu_dai_name = "Offload1 Pin",
- .platform_name = "haswell-pcm-audio",
- .dynamic = 1,
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
- .dpcm_playback = 1,
- },
- {
- .name = "Loopback PCM",
- .stream_name = "Loopback",
- .cpu_dai_name = "Loopback Pin",
- .platform_name = "haswell-pcm-audio",
- .dynamic = 0,
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
- .dpcm_capture = 1,
- },
- /* Back End DAI links */
- {
- /* SSP0 - Codec */
- .name = "Codec",
- .be_id = 0,
- .cpu_dai_name = "snd-soc-dummy-dai",
- .platform_name = "snd-soc-dummy",
- .no_pcm = 1,
- .codec_name = "i2c-INT343A:00",
- .codec_dai_name = "rt286-aif1",
- .init = broadwell_rt286_codec_init,
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ignore_suspend = 1,
- .ignore_pmdown_time = 1,
- .be_hw_params_fixup = broadwell_ssp0_fixup,
- .ops = &broadwell_rt286_ops,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
- },
-};
-
-/* broadwell audio machine driver for WPT + RT286S */
-static struct snd_soc_card broadwell_rt286 = {
- .name = "broadwell-rt286",
- .owner = THIS_MODULE,
- .dai_link = broadwell_rt286_dais,
- .num_links = ARRAY_SIZE(broadwell_rt286_dais),
- .controls = broadwell_controls,
- .num_controls = ARRAY_SIZE(broadwell_controls),
- .dapm_widgets = broadwell_widgets,
- .num_dapm_widgets = ARRAY_SIZE(broadwell_widgets),
- .dapm_routes = broadwell_rt286_map,
- .num_dapm_routes = ARRAY_SIZE(broadwell_rt286_map),
- .fully_routed = true,
-};
-
-static int broadwell_audio_probe(struct platform_device *pdev)
-{
- broadwell_rt286.dev = &pdev->dev;
-
- return snd_soc_register_card(&broadwell_rt286);
-}
-
-static int broadwell_audio_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_card(&broadwell_rt286);
- return 0;
-}
-
-static struct platform_driver broadwell_audio = {
- .probe = broadwell_audio_probe,
- .remove = broadwell_audio_remove,
- .driver = {
- .name = "broadwell-audio",
- },
-};
-
-module_platform_driver(broadwell_audio)
-
-/* Module information */
-MODULE_AUTHOR("Liam Girdwood, Xingchao Wang");
-MODULE_DESCRIPTION("Intel SST Audio for WPT/Broadwell");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:broadwell-audio");
+++ /dev/null
-/*
- * Intel Baytrail SST MAX98090 machine driver
- * Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/acpi.h>
-#include <linux/device.h>
-#include <linux/gpio.h>
-#include <linux/gpio/consumer.h>
-#include <linux/slab.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/jack.h>
-#include "../codecs/max98090.h"
-
-struct byt_max98090_private {
- struct snd_soc_jack jack;
-};
-
-static const struct snd_soc_dapm_widget byt_max98090_widgets[] = {
- SND_SOC_DAPM_HP("Headphone", NULL),
- SND_SOC_DAPM_MIC("Headset Mic", NULL),
- SND_SOC_DAPM_MIC("Int Mic", NULL),
- SND_SOC_DAPM_SPK("Ext Spk", NULL),
-};
-
-static const struct snd_soc_dapm_route byt_max98090_audio_map[] = {
- {"IN34", NULL, "Headset Mic"},
- {"Headset Mic", NULL, "MICBIAS"},
- {"DMICL", NULL, "Int Mic"},
- {"Headphone", NULL, "HPL"},
- {"Headphone", NULL, "HPR"},
- {"Ext Spk", NULL, "SPKL"},
- {"Ext Spk", NULL, "SPKR"},
-};
-
-static const struct snd_kcontrol_new byt_max98090_controls[] = {
- SOC_DAPM_PIN_SWITCH("Headphone"),
- SOC_DAPM_PIN_SWITCH("Headset Mic"),
- SOC_DAPM_PIN_SWITCH("Int Mic"),
- SOC_DAPM_PIN_SWITCH("Ext Spk"),
-};
-
-static struct snd_soc_jack_pin hs_jack_pins[] = {
- {
- .pin = "Headphone",
- .mask = SND_JACK_HEADPHONE,
- },
- {
- .pin = "Headset Mic",
- .mask = SND_JACK_MICROPHONE,
- },
-};
-
-static struct snd_soc_jack_gpio hs_jack_gpios[] = {
- {
- .name = "hp-gpio",
- .idx = 0,
- .report = SND_JACK_HEADPHONE | SND_JACK_LINEOUT,
- .debounce_time = 200,
- },
- {
- .name = "mic-gpio",
- .idx = 1,
- .invert = 1,
- .report = SND_JACK_MICROPHONE,
- .debounce_time = 200,
- },
-};
-
-static int byt_max98090_init(struct snd_soc_pcm_runtime *runtime)
-{
- int ret;
- struct snd_soc_codec *codec = runtime->codec;
- struct snd_soc_card *card = runtime->card;
- struct byt_max98090_private *drv = snd_soc_card_get_drvdata(card);
- struct snd_soc_jack *jack = &drv->jack;
-
- card->dapm.idle_bias_off = true;
-
- ret = snd_soc_dai_set_sysclk(runtime->codec_dai,
- M98090_REG_SYSTEM_CLOCK,
- 25000000, SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(card->dev, "Can't set codec clock %d\n", ret);
- return ret;
- }
-
- /* Enable jack detection */
- ret = snd_soc_jack_new(codec, "Headset",
- SND_JACK_LINEOUT | SND_JACK_HEADSET, jack);
- if (ret)
- return ret;
-
- ret = snd_soc_jack_add_pins(jack, ARRAY_SIZE(hs_jack_pins),
- hs_jack_pins);
- if (ret)
- return ret;
-
- return snd_soc_jack_add_gpiods(card->dev->parent, jack,
- ARRAY_SIZE(hs_jack_gpios),
- hs_jack_gpios);
-}
-
-static struct snd_soc_dai_link byt_max98090_dais[] = {
- {
- .name = "Baytrail Audio",
- .stream_name = "Audio",
- .cpu_dai_name = "baytrail-pcm-audio",
- .codec_dai_name = "HiFi",
- .codec_name = "i2c-193C9890:00",
- .platform_name = "baytrail-pcm-audio",
- .init = byt_max98090_init,
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- },
-};
-
-static struct snd_soc_card byt_max98090_card = {
- .name = "byt-max98090",
- .dai_link = byt_max98090_dais,
- .num_links = ARRAY_SIZE(byt_max98090_dais),
- .dapm_widgets = byt_max98090_widgets,
- .num_dapm_widgets = ARRAY_SIZE(byt_max98090_widgets),
- .dapm_routes = byt_max98090_audio_map,
- .num_dapm_routes = ARRAY_SIZE(byt_max98090_audio_map),
- .controls = byt_max98090_controls,
- .num_controls = ARRAY_SIZE(byt_max98090_controls),
- .fully_routed = true,
-};
-
-static int byt_max98090_probe(struct platform_device *pdev)
-{
- int ret_val = 0;
- struct byt_max98090_private *priv;
-
- priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC);
- if (!priv) {
- dev_err(&pdev->dev, "allocation failed\n");
- return -ENOMEM;
- }
-
- byt_max98090_card.dev = &pdev->dev;
- snd_soc_card_set_drvdata(&byt_max98090_card, priv);
- ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_max98090_card);
- if (ret_val) {
- dev_err(&pdev->dev,
- "snd_soc_register_card failed %d\n", ret_val);
- return ret_val;
- }
-
- return ret_val;
-}
-
-static int byt_max98090_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
- struct byt_max98090_private *priv = snd_soc_card_get_drvdata(card);
-
- snd_soc_jack_free_gpios(&priv->jack, ARRAY_SIZE(hs_jack_gpios),
- hs_jack_gpios);
-
- return 0;
-}
-
-static struct platform_driver byt_max98090_driver = {
- .probe = byt_max98090_probe,
- .remove = byt_max98090_remove,
- .driver = {
- .name = "byt-max98090",
- .pm = &snd_soc_pm_ops,
- },
-};
-module_platform_driver(byt_max98090_driver)
-
-MODULE_DESCRIPTION("ASoC Intel(R) Baytrail Machine driver");
-MODULE_AUTHOR("Omair Md Abdullah, Jarkko Nikula");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:byt-max98090");
+++ /dev/null
-/*
- * Intel Baytrail SST RT5640 machine driver
- * Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/acpi.h>
-#include <linux/device.h>
-#include <linux/dmi.h>
-#include <linux/slab.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/jack.h>
-#include "../codecs/rt5640.h"
-
-#include "sst-dsp.h"
-
-static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = {
- SND_SOC_DAPM_HP("Headphone", NULL),
- SND_SOC_DAPM_MIC("Headset Mic", NULL),
- SND_SOC_DAPM_MIC("Internal Mic", NULL),
- SND_SOC_DAPM_SPK("Speaker", NULL),
-};
-
-static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = {
- {"Headset Mic", NULL, "MICBIAS1"},
- {"IN2P", NULL, "Headset Mic"},
- {"Headphone", NULL, "HPOL"},
- {"Headphone", NULL, "HPOR"},
- {"Speaker", NULL, "SPOLP"},
- {"Speaker", NULL, "SPOLN"},
- {"Speaker", NULL, "SPORP"},
- {"Speaker", NULL, "SPORN"},
-};
-
-static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic1_map[] = {
- {"DMIC1", NULL, "Internal Mic"},
-};
-
-static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic2_map[] = {
- {"DMIC2", NULL, "Internal Mic"},
-};
-
-static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = {
- {"Internal Mic", NULL, "MICBIAS1"},
- {"IN1P", NULL, "Internal Mic"},
-};
-
-enum {
- BYT_RT5640_DMIC1_MAP,
- BYT_RT5640_DMIC2_MAP,
- BYT_RT5640_IN1_MAP,
-};
-
-#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff)
-#define BYT_RT5640_DMIC_EN BIT(16)
-
-static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP |
- BYT_RT5640_DMIC_EN;
-
-static const struct snd_kcontrol_new byt_rt5640_controls[] = {
- SOC_DAPM_PIN_SWITCH("Headphone"),
- SOC_DAPM_PIN_SWITCH("Headset Mic"),
- SOC_DAPM_PIN_SWITCH("Internal Mic"),
- SOC_DAPM_PIN_SWITCH("Speaker"),
-};
-
-static int byt_rt5640_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1,
- params_rate(params) * 256,
- SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(codec_dai->dev, "can't set codec clock %d\n", ret);
- return ret;
- }
- ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1,
- params_rate(params) * 64,
- params_rate(params) * 256);
- if (ret < 0) {
- dev_err(codec_dai->dev, "can't set codec pll: %d\n", ret);
- return ret;
- }
- return 0;
-}
-
-static int byt_rt5640_quirk_cb(const struct dmi_system_id *id)
-{
- byt_rt5640_quirk = (unsigned long)id->driver_data;
- return 1;
-}
-
-static const struct dmi_system_id byt_rt5640_quirk_table[] = {
- {
- .callback = byt_rt5640_quirk_cb,
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
- DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"),
- },
- .driver_data = (unsigned long *)BYT_RT5640_IN1_MAP,
- },
- {
- .callback = byt_rt5640_quirk_cb,
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "DellInc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"),
- },
- .driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP |
- BYT_RT5640_DMIC_EN),
- },
- {}
-};
-
-static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
-{
- int ret;
- struct snd_soc_codec *codec = runtime->codec;
- struct snd_soc_card *card = runtime->card;
- const struct snd_soc_dapm_route *custom_map;
- int num_routes;
-
- card->dapm.idle_bias_off = true;
-
- ret = snd_soc_add_card_controls(card, byt_rt5640_controls,
- ARRAY_SIZE(byt_rt5640_controls));
- if (ret) {
- dev_err(card->dev, "unable to add card controls\n");
- return ret;
- }
-
- dmi_check_system(byt_rt5640_quirk_table);
- switch (BYT_RT5640_MAP(byt_rt5640_quirk)) {
- case BYT_RT5640_IN1_MAP:
- custom_map = byt_rt5640_intmic_in1_map;
- num_routes = ARRAY_SIZE(byt_rt5640_intmic_in1_map);
- break;
- case BYT_RT5640_DMIC2_MAP:
- custom_map = byt_rt5640_intmic_dmic2_map;
- num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic2_map);
- break;
- default:
- custom_map = byt_rt5640_intmic_dmic1_map;
- num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic1_map);
- }
-
- ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes);
- if (ret)
- return ret;
-
- if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) {
- ret = rt5640_dmic_enable(codec, 0, 0);
- if (ret)
- return ret;
- }
-
- snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
- snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker");
-
- return ret;
-}
-
-static struct snd_soc_ops byt_rt5640_ops = {
- .hw_params = byt_rt5640_hw_params,
-};
-
-static struct snd_soc_dai_link byt_rt5640_dais[] = {
- {
- .name = "Baytrail Audio",
- .stream_name = "Audio",
- .cpu_dai_name = "baytrail-pcm-audio",
- .codec_dai_name = "rt5640-aif1",
- .codec_name = "i2c-10EC5640:00",
- .platform_name = "baytrail-pcm-audio",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .init = byt_rt5640_init,
- .ops = &byt_rt5640_ops,
- },
-};
-
-static struct snd_soc_card byt_rt5640_card = {
- .name = "byt-rt5640",
- .dai_link = byt_rt5640_dais,
- .num_links = ARRAY_SIZE(byt_rt5640_dais),
- .dapm_widgets = byt_rt5640_widgets,
- .num_dapm_widgets = ARRAY_SIZE(byt_rt5640_widgets),
- .dapm_routes = byt_rt5640_audio_map,
- .num_dapm_routes = ARRAY_SIZE(byt_rt5640_audio_map),
- .fully_routed = true,
-};
-
-static int byt_rt5640_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &byt_rt5640_card;
-
- card->dev = &pdev->dev;
- return devm_snd_soc_register_card(&pdev->dev, card);
-}
-
-static struct platform_driver byt_rt5640_audio = {
- .probe = byt_rt5640_probe,
- .driver = {
- .name = "byt-rt5640",
- .pm = &snd_soc_pm_ops,
- },
-};
-module_platform_driver(byt_rt5640_audio)
-
-MODULE_DESCRIPTION("ASoC Intel(R) Baytrail Machine driver");
-MODULE_AUTHOR("Omair Md Abdullah, Jarkko Nikula");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:byt-rt5640");
+++ /dev/null
-/*
- * byt_cr_dpcm_rt5640.c - ASoc Machine driver for Intel Byt CR platform
- *
- * Copyright (C) 2014 Intel Corp
- * Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/device.h>
-#include <linux/slab.h>
-#include <linux/input.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include "../codecs/rt5640.h"
-#include "sst-atom-controls.h"
-
-static const struct snd_soc_dapm_widget byt_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone", NULL),
- SND_SOC_DAPM_MIC("Headset Mic", NULL),
- SND_SOC_DAPM_MIC("Int Mic", NULL),
- SND_SOC_DAPM_SPK("Ext Spk", NULL),
-};
-
-static const struct snd_soc_dapm_route byt_audio_map[] = {
- {"IN2P", NULL, "Headset Mic"},
- {"IN2N", NULL, "Headset Mic"},
- {"Headset Mic", NULL, "MICBIAS1"},
- {"IN1P", NULL, "MICBIAS1"},
- {"LDO2", NULL, "Int Mic"},
- {"Headphone", NULL, "HPOL"},
- {"Headphone", NULL, "HPOR"},
- {"Ext Spk", NULL, "SPOLP"},
- {"Ext Spk", NULL, "SPOLN"},
- {"Ext Spk", NULL, "SPORP"},
- {"Ext Spk", NULL, "SPORN"},
-
- {"AIF1 Playback", NULL, "ssp2 Tx"},
- {"ssp2 Tx", NULL, "codec_out0"},
- {"ssp2 Tx", NULL, "codec_out1"},
- {"codec_in0", NULL, "ssp2 Rx"},
- {"codec_in1", NULL, "ssp2 Rx"},
- {"ssp2 Rx", NULL, "AIF1 Capture"},
-};
-
-static const struct snd_kcontrol_new byt_mc_controls[] = {
- SOC_DAPM_PIN_SWITCH("Headphone"),
- SOC_DAPM_PIN_SWITCH("Headset Mic"),
- SOC_DAPM_PIN_SWITCH("Int Mic"),
- SOC_DAPM_PIN_SWITCH("Ext Spk"),
-};
-
-static int byt_aif1_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
-
- snd_soc_dai_set_bclk_ratio(codec_dai, 50);
-
- ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1,
- params_rate(params) * 512,
- SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(rtd->dev, "can't set codec clock %d\n", ret);
- return ret;
- }
-
- ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1,
- params_rate(params) * 50,
- params_rate(params) * 512);
- if (ret < 0) {
- dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
- return ret;
- }
-
- return 0;
-}
-
-static const struct snd_soc_pcm_stream byt_dai_params = {
- .formats = SNDRV_PCM_FMTBIT_S24_LE,
- .rate_min = 48000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
-};
-
-static int byt_codec_fixup(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_hw_params *params)
-{
- struct snd_interval *rate = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_RATE);
- struct snd_interval *channels = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_CHANNELS);
-
- /* The DSP will covert the FE rate to 48k, stereo, 24bits */
- rate->min = rate->max = 48000;
- channels->min = channels->max = 2;
-
- /* set SSP2 to 24-bit */
- snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT -
- SNDRV_PCM_HW_PARAM_FIRST_MASK],
- SNDRV_PCM_FORMAT_S24_LE);
- return 0;
-}
-
-static unsigned int rates_48000[] = {
- 48000,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_48000 = {
- .count = ARRAY_SIZE(rates_48000),
- .list = rates_48000,
-};
-
-static int byt_aif1_startup(struct snd_pcm_substream *substream)
-{
- return snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &constraints_48000);
-}
-
-static struct snd_soc_ops byt_aif1_ops = {
- .startup = byt_aif1_startup,
-};
-
-static struct snd_soc_ops byt_be_ssp2_ops = {
- .hw_params = byt_aif1_hw_params,
-};
-
-static struct snd_soc_dai_link byt_dailink[] = {
- [MERR_DPCM_AUDIO] = {
- .name = "Baytrail Audio Port",
- .stream_name = "Baytrail Audio",
- .cpu_dai_name = "media-cpu-dai",
- .codec_dai_name = "snd-soc-dummy-dai",
- .codec_name = "snd-soc-dummy",
- .platform_name = "sst-mfld-platform",
- .ignore_suspend = 1,
- .dynamic = 1,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
- .ops = &byt_aif1_ops,
- },
- [MERR_DPCM_COMPR] = {
- .name = "Baytrail Compressed Port",
- .stream_name = "Baytrail Compress",
- .cpu_dai_name = "compress-cpu-dai",
- .codec_dai_name = "snd-soc-dummy-dai",
- .codec_name = "snd-soc-dummy",
- .platform_name = "sst-mfld-platform",
- },
- /* back ends */
- {
- .name = "SSP2-Codec",
- .be_id = 1,
- .cpu_dai_name = "ssp2-port",
- .platform_name = "sst-mfld-platform",
- .no_pcm = 1,
- .codec_dai_name = "rt5640-aif1",
- .codec_name = "i2c-10EC5640:00",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
- .be_hw_params_fixup = byt_codec_fixup,
- .ignore_suspend = 1,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
- .ops = &byt_be_ssp2_ops,
- },
-};
-
-/* SoC card */
-static struct snd_soc_card snd_soc_card_byt = {
- .name = "baytrailcraudio",
- .dai_link = byt_dailink,
- .num_links = ARRAY_SIZE(byt_dailink),
- .dapm_widgets = byt_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(byt_dapm_widgets),
- .dapm_routes = byt_audio_map,
- .num_dapm_routes = ARRAY_SIZE(byt_audio_map),
- .controls = byt_mc_controls,
- .num_controls = ARRAY_SIZE(byt_mc_controls),
-};
-
-static int snd_byt_mc_probe(struct platform_device *pdev)
-{
- int ret_val = 0;
-
- /* register the soc card */
- snd_soc_card_byt.dev = &pdev->dev;
-
- ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_byt);
- if (ret_val) {
- dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n", ret_val);
- return ret_val;
- }
- platform_set_drvdata(pdev, &snd_soc_card_byt);
- return ret_val;
-}
-
-static struct platform_driver snd_byt_mc_driver = {
- .driver = {
- .name = "bytt100_rt5640",
- .pm = &snd_soc_pm_ops,
- },
- .probe = snd_byt_mc_probe,
-};
-
-module_platform_driver(snd_byt_mc_driver);
-
-MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
-MODULE_AUTHOR("Subhransu S. Prusty <subhransu.s.prusty@intel.com>");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:bytt100_rt5640");
+++ /dev/null
-/*
- * cht-bsw-rt5645.c - ASoc Machine driver for Intel Cherryview-based platforms
- * Cherrytrail and Braswell, with RT5645 codec.
- *
- * Copyright (C) 2015 Intel Corp
- * Author: Fang, Yang A <yang.a.fang@intel.com>
- * N,Harshapriya <harshapriya.n@intel.com>
- * This file is modified from cht_bsw_rt5672.c
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/jack.h>
-#include "../codecs/rt5645.h"
-#include "sst-atom-controls.h"
-
-#define CHT_PLAT_CLK_3_HZ 19200000
-#define CHT_CODEC_DAI "rt5645-aif1"
-
-struct cht_mc_private {
- struct snd_soc_jack hp_jack;
- struct snd_soc_jack mic_jack;
-};
-
-static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
-{
- int i;
-
- for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_pcm_runtime *rtd;
-
- rtd = card->rtd + i;
- if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
- strlen(CHT_CODEC_DAI)))
- return rtd->codec_dai;
- }
- return NULL;
-}
-
-static int platform_clock_control(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- struct snd_soc_dapm_context *dapm = w->dapm;
- struct snd_soc_card *card = dapm->card;
- struct snd_soc_dai *codec_dai;
- int ret;
-
- codec_dai = cht_get_codec_dai(card);
- if (!codec_dai) {
- dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");
- return -EIO;
- }
-
- if (!SND_SOC_DAPM_EVENT_OFF(event))
- return 0;
-
- /* Set codec sysclk source to its internal clock because codec PLL will
- * be off when idle and MCLK will also be off by ACPI when codec is
- * runtime suspended. Codec needs clock for jack detection and button
- * press.
- */
- ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_RCCLK,
- 0, SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
- return ret;
- }
-
- return 0;
-}
-
-static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone", NULL),
- SND_SOC_DAPM_MIC("Headset Mic", NULL),
- SND_SOC_DAPM_MIC("Int Mic", NULL),
- SND_SOC_DAPM_SPK("Ext Spk", NULL),
- SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
- platform_clock_control, SND_SOC_DAPM_POST_PMD),
-};
-
-static const struct snd_soc_dapm_route cht_audio_map[] = {
- {"IN1P", NULL, "Headset Mic"},
- {"IN1N", NULL, "Headset Mic"},
- {"DMIC L1", NULL, "Int Mic"},
- {"DMIC R1", NULL, "Int Mic"},
- {"Headphone", NULL, "HPOL"},
- {"Headphone", NULL, "HPOR"},
- {"Ext Spk", NULL, "SPOL"},
- {"Ext Spk", NULL, "SPOR"},
- {"AIF1 Playback", NULL, "ssp2 Tx"},
- {"ssp2 Tx", NULL, "codec_out0"},
- {"ssp2 Tx", NULL, "codec_out1"},
- {"codec_in0", NULL, "ssp2 Rx" },
- {"codec_in1", NULL, "ssp2 Rx" },
- {"ssp2 Rx", NULL, "AIF1 Capture"},
- {"Headphone", NULL, "Platform Clock"},
- {"Headset Mic", NULL, "Platform Clock"},
- {"Int Mic", NULL, "Platform Clock"},
- {"Ext Spk", NULL, "Platform Clock"},
-};
-
-static const struct snd_kcontrol_new cht_mc_controls[] = {
- SOC_DAPM_PIN_SWITCH("Headphone"),
- SOC_DAPM_PIN_SWITCH("Headset Mic"),
- SOC_DAPM_PIN_SWITCH("Int Mic"),
- SOC_DAPM_PIN_SWITCH("Ext Spk"),
-};
-
-static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
-
- /* set codec PLL source to the 19.2MHz platform clock (MCLK) */
- ret = snd_soc_dai_set_pll(codec_dai, 0, RT5645_PLL1_S_MCLK,
- CHT_PLAT_CLK_3_HZ, params_rate(params) * 512);
- if (ret < 0) {
- dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
- return ret;
- }
-
- ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_PLL1,
- params_rate(params) * 512, SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
- return ret;
- }
-
- return 0;
-}
-
-static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
-{
- int ret;
- struct snd_soc_codec *codec = runtime->codec;
- struct snd_soc_dai *codec_dai = runtime->codec_dai;
- struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card);
-
- /* Select clk_i2s1_asrc as ASRC clock source */
- rt5645_sel_asrc_clk_src(codec,
- RT5645_DA_STEREO_FILTER |
- RT5645_DA_MONO_L_FILTER |
- RT5645_DA_MONO_R_FILTER |
- RT5645_AD_STEREO_FILTER,
- RT5645_CLK_SEL_I2S1_ASRC);
-
- /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
- ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24);
- if (ret < 0) {
- dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret);
- return ret;
- }
-
- ret = snd_soc_jack_new(codec, "Headphone Jack",
- SND_JACK_HEADPHONE,
- &ctx->hp_jack);
- if (ret) {
- dev_err(runtime->dev, "HP jack creation failed %d\n", ret);
- return ret;
- }
-
- ret = snd_soc_jack_new(codec, "Mic Jack",
- SND_JACK_MICROPHONE,
- &ctx->mic_jack);
- if (ret) {
- dev_err(runtime->dev, "Mic jack creation failed %d\n", ret);
- return ret;
- }
-
- rt5645_set_jack_detect(codec, &ctx->hp_jack, &ctx->mic_jack);
-
- return ret;
-}
-
-static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_hw_params *params)
-{
- struct snd_interval *rate = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_RATE);
- struct snd_interval *channels = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_CHANNELS);
-
- /* The DSP will covert the FE rate to 48k, stereo, 24bits */
- rate->min = rate->max = 48000;
- channels->min = channels->max = 2;
-
- /* set SSP2 to 24-bit */
- snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT -
- SNDRV_PCM_HW_PARAM_FIRST_MASK],
- SNDRV_PCM_FORMAT_S24_LE);
- return 0;
-}
-
-static unsigned int rates_48000[] = {
- 48000,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_48000 = {
- .count = ARRAY_SIZE(rates_48000),
- .list = rates_48000,
-};
-
-static int cht_aif1_startup(struct snd_pcm_substream *substream)
-{
- return snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &constraints_48000);
-}
-
-static struct snd_soc_ops cht_aif1_ops = {
- .startup = cht_aif1_startup,
-};
-
-static struct snd_soc_ops cht_be_ssp2_ops = {
- .hw_params = cht_aif1_hw_params,
-};
-
-static struct snd_soc_dai_link cht_dailink[] = {
- [MERR_DPCM_AUDIO] = {
- .name = "Audio Port",
- .stream_name = "Audio",
- .cpu_dai_name = "media-cpu-dai",
- .codec_dai_name = "snd-soc-dummy-dai",
- .codec_name = "snd-soc-dummy",
- .platform_name = "sst-mfld-platform",
- .ignore_suspend = 1,
- .dynamic = 1,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
- .ops = &cht_aif1_ops,
- },
- [MERR_DPCM_COMPR] = {
- .name = "Compressed Port",
- .stream_name = "Compress",
- .cpu_dai_name = "compress-cpu-dai",
- .codec_dai_name = "snd-soc-dummy-dai",
- .codec_name = "snd-soc-dummy",
- .platform_name = "sst-mfld-platform",
- },
- /* CODEC<->CODEC link */
- /* back ends */
- {
- .name = "SSP2-Codec",
- .be_id = 1,
- .cpu_dai_name = "ssp2-port",
- .platform_name = "sst-mfld-platform",
- .no_pcm = 1,
- .codec_dai_name = "rt5645-aif1",
- .codec_name = "i2c-10EC5645:00",
- .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
- .init = cht_codec_init,
- .be_hw_params_fixup = cht_codec_fixup,
- .ignore_suspend = 1,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
- .ops = &cht_be_ssp2_ops,
- },
-};
-
-/* SoC card */
-static struct snd_soc_card snd_soc_card_cht = {
- .name = "chtrt5645",
- .dai_link = cht_dailink,
- .num_links = ARRAY_SIZE(cht_dailink),
- .dapm_widgets = cht_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
- .dapm_routes = cht_audio_map,
- .num_dapm_routes = ARRAY_SIZE(cht_audio_map),
- .controls = cht_mc_controls,
- .num_controls = ARRAY_SIZE(cht_mc_controls),
-};
-
-static int snd_cht_mc_probe(struct platform_device *pdev)
-{
- int ret_val = 0;
- struct cht_mc_private *drv;
-
- drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
- if (!drv)
- return -ENOMEM;
-
- snd_soc_card_cht.dev = &pdev->dev;
- snd_soc_card_set_drvdata(&snd_soc_card_cht, drv);
- ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
- if (ret_val) {
- dev_err(&pdev->dev,
- "snd_soc_register_card failed %d\n", ret_val);
- return ret_val;
- }
- platform_set_drvdata(pdev, &snd_soc_card_cht);
- return ret_val;
-}
-
-static struct platform_driver snd_cht_mc_driver = {
- .driver = {
- .name = "cht-bsw-rt5645",
- .pm = &snd_soc_pm_ops,
- },
- .probe = snd_cht_mc_probe,
-};
-
-module_platform_driver(snd_cht_mc_driver)
-
-MODULE_DESCRIPTION("ASoC Intel(R) Braswell Machine driver");
-MODULE_AUTHOR("Fang, Yang A,N,Harshapriya");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:cht-bsw-rt5645");
+++ /dev/null
-/*
- * cht_bsw_rt5672.c - ASoc Machine driver for Intel Cherryview-based platforms
- * Cherrytrail and Braswell, with RT5672 codec.
- *
- * Copyright (C) 2014 Intel Corp
- * Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
- * Mengdong Lin <mengdong.lin@intel.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include "../codecs/rt5670.h"
-#include "sst-atom-controls.h"
-
-/* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */
-#define CHT_PLAT_CLK_3_HZ 19200000
-#define CHT_CODEC_DAI "rt5670-aif1"
-
-static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
-{
- int i;
-
- for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_pcm_runtime *rtd;
-
- rtd = card->rtd + i;
- if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
- strlen(CHT_CODEC_DAI)))
- return rtd->codec_dai;
- }
- return NULL;
-}
-
-static int platform_clock_control(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- struct snd_soc_dapm_context *dapm = w->dapm;
- struct snd_soc_card *card = dapm->card;
- struct snd_soc_dai *codec_dai;
-
- codec_dai = cht_get_codec_dai(card);
- if (!codec_dai) {
- dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");
- return -EIO;
- }
-
- if (!SND_SOC_DAPM_EVENT_OFF(event))
- return 0;
-
- /* Set codec sysclk source to its internal clock because codec PLL will
- * be off when idle and MCLK will also be off by ACPI when codec is
- * runtime suspended. Codec needs clock for jack detection and button
- * press.
- */
- snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK,
- 0, SND_SOC_CLOCK_IN);
-
- return 0;
-}
-
-static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone", NULL),
- SND_SOC_DAPM_MIC("Headset Mic", NULL),
- SND_SOC_DAPM_MIC("Int Mic", NULL),
- SND_SOC_DAPM_SPK("Ext Spk", NULL),
- SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
- platform_clock_control, SND_SOC_DAPM_POST_PMD),
-};
-
-static const struct snd_soc_dapm_route cht_audio_map[] = {
- {"IN1P", NULL, "Headset Mic"},
- {"IN1N", NULL, "Headset Mic"},
- {"DMIC L1", NULL, "Int Mic"},
- {"DMIC R1", NULL, "Int Mic"},
- {"Headphone", NULL, "HPOL"},
- {"Headphone", NULL, "HPOR"},
- {"Ext Spk", NULL, "SPOLP"},
- {"Ext Spk", NULL, "SPOLN"},
- {"Ext Spk", NULL, "SPORP"},
- {"Ext Spk", NULL, "SPORN"},
- {"AIF1 Playback", NULL, "ssp2 Tx"},
- {"ssp2 Tx", NULL, "codec_out0"},
- {"ssp2 Tx", NULL, "codec_out1"},
- {"codec_in0", NULL, "ssp2 Rx"},
- {"codec_in1", NULL, "ssp2 Rx"},
- {"ssp2 Rx", NULL, "AIF1 Capture"},
- {"Headphone", NULL, "Platform Clock"},
- {"Headset Mic", NULL, "Platform Clock"},
- {"Int Mic", NULL, "Platform Clock"},
- {"Ext Spk", NULL, "Platform Clock"},
-};
-
-static const struct snd_kcontrol_new cht_mc_controls[] = {
- SOC_DAPM_PIN_SWITCH("Headphone"),
- SOC_DAPM_PIN_SWITCH("Headset Mic"),
- SOC_DAPM_PIN_SWITCH("Int Mic"),
- SOC_DAPM_PIN_SWITCH("Ext Spk"),
-};
-
-static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
-
- /* set codec PLL source to the 19.2MHz platform clock (MCLK) */
- ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK,
- CHT_PLAT_CLK_3_HZ, params_rate(params) * 512);
- if (ret < 0) {
- dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
- return ret;
- }
-
- /* set codec sysclk source to PLL */
- ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_PLL1,
- params_rate(params) * 512,
- SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
- return ret;
- }
- return 0;
-}
-
-static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
-{
- int ret;
- struct snd_soc_dai *codec_dai = runtime->codec_dai;
- struct snd_soc_codec *codec = codec_dai->codec;
-
- /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
- ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24);
- if (ret < 0) {
- dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret);
- return ret;
- }
-
- /* Select codec ASRC clock source to track I2S1 clock, because codec
- * is in slave mode and 100fs I2S format (BCLK = 100 * LRCLK) cannot
- * be supported by RT5672. Otherwise, ASRC will be disabled and cause
- * noise.
- */
- rt5670_sel_asrc_clk_src(codec,
- RT5670_DA_STEREO_FILTER
- | RT5670_DA_MONO_L_FILTER
- | RT5670_DA_MONO_R_FILTER
- | RT5670_AD_STEREO_FILTER
- | RT5670_AD_MONO_L_FILTER
- | RT5670_AD_MONO_R_FILTER,
- RT5670_CLK_SEL_I2S1_ASRC);
- return 0;
-}
-
-static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_hw_params *params)
-{
- struct snd_interval *rate = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_RATE);
- struct snd_interval *channels = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_CHANNELS);
-
- /* The DSP will covert the FE rate to 48k, stereo, 24bits */
- rate->min = rate->max = 48000;
- channels->min = channels->max = 2;
-
- /* set SSP2 to 24-bit */
- snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT -
- SNDRV_PCM_HW_PARAM_FIRST_MASK],
- SNDRV_PCM_FORMAT_S24_LE);
- return 0;
-}
-
-static unsigned int rates_48000[] = {
- 48000,
-};
-
-static struct snd_pcm_hw_constraint_list constraints_48000 = {
- .count = ARRAY_SIZE(rates_48000),
- .list = rates_48000,
-};
-
-static int cht_aif1_startup(struct snd_pcm_substream *substream)
-{
- return snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &constraints_48000);
-}
-
-static struct snd_soc_ops cht_aif1_ops = {
- .startup = cht_aif1_startup,
-};
-
-static struct snd_soc_ops cht_be_ssp2_ops = {
- .hw_params = cht_aif1_hw_params,
-};
-
-static struct snd_soc_dai_link cht_dailink[] = {
- /* Front End DAI links */
- [MERR_DPCM_AUDIO] = {
- .name = "Audio Port",
- .stream_name = "Audio",
- .cpu_dai_name = "media-cpu-dai",
- .codec_dai_name = "snd-soc-dummy-dai",
- .codec_name = "snd-soc-dummy",
- .platform_name = "sst-mfld-platform",
- .ignore_suspend = 1,
- .dynamic = 1,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
- .ops = &cht_aif1_ops,
- },
- [MERR_DPCM_COMPR] = {
- .name = "Compressed Port",
- .stream_name = "Compress",
- .cpu_dai_name = "compress-cpu-dai",
- .codec_dai_name = "snd-soc-dummy-dai",
- .codec_name = "snd-soc-dummy",
- .platform_name = "sst-mfld-platform",
- },
-
- /* Back End DAI links */
- {
- /* SSP2 - Codec */
- .name = "SSP2-Codec",
- .be_id = 1,
- .cpu_dai_name = "ssp2-port",
- .platform_name = "sst-mfld-platform",
- .no_pcm = 1,
- .codec_dai_name = "rt5670-aif1",
- .codec_name = "i2c-10EC5670:00",
- .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF
- | SND_SOC_DAIFMT_CBS_CFS,
- .init = cht_codec_init,
- .be_hw_params_fixup = cht_codec_fixup,
- .ignore_suspend = 1,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
- .ops = &cht_be_ssp2_ops,
- },
-};
-
-/* SoC card */
-static struct snd_soc_card snd_soc_card_cht = {
- .name = "cherrytrailcraudio",
- .dai_link = cht_dailink,
- .num_links = ARRAY_SIZE(cht_dailink),
- .dapm_widgets = cht_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
- .dapm_routes = cht_audio_map,
- .num_dapm_routes = ARRAY_SIZE(cht_audio_map),
- .controls = cht_mc_controls,
- .num_controls = ARRAY_SIZE(cht_mc_controls),
-};
-
-static int snd_cht_mc_probe(struct platform_device *pdev)
-{
- int ret_val = 0;
-
- /* register the soc card */
- snd_soc_card_cht.dev = &pdev->dev;
- ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
- if (ret_val) {
- dev_err(&pdev->dev,
- "snd_soc_register_card failed %d\n", ret_val);
- return ret_val;
- }
- platform_set_drvdata(pdev, &snd_soc_card_cht);
- return ret_val;
-}
-
-static struct platform_driver snd_cht_mc_driver = {
- .driver = {
- .name = "cht-bsw-rt5672",
- .pm = &snd_soc_pm_ops,
- },
- .probe = snd_cht_mc_probe,
-};
-
-module_platform_driver(snd_cht_mc_driver);
-
-MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
-MODULE_AUTHOR("Subhransu S. Prusty, Mengdong Lin");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:cht-bsw-rt5672");
--- /dev/null
+snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o
+snd-soc-sst-acpi-objs := sst-acpi.o
+snd-soc-sst-ipc-objs := sst-ipc.o
+
+obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o
+obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o
+
--- /dev/null
+/*
+ * Intel SST loader on ACPI systems
+ *
+ * Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "sst-dsp.h"
+
+#define SST_LPT_DSP_DMA_ADDR_OFFSET 0x0F0000
+#define SST_WPT_DSP_DMA_ADDR_OFFSET 0x0FE000
+#define SST_LPT_DSP_DMA_SIZE (1024 - 1)
+
+/* Descriptor for SST ASoC machine driver */
+struct sst_acpi_mach {
+ /* ACPI ID for the matching machine driver. Audio codec for instance */
+ const u8 id[ACPI_ID_LEN];
+ /* machine driver name */
+ const char *drv_name;
+ /* firmware file name */
+ const char *fw_filename;
+};
+
+/* Descriptor for setting up SST platform data */
+struct sst_acpi_desc {
+ const char *drv_name;
+ struct sst_acpi_mach *machines;
+ /* Platform resource indexes. Must set to -1 if not used */
+ int resindex_lpe_base;
+ int resindex_pcicfg_base;
+ int resindex_fw_base;
+ int irqindex_host_ipc;
+ int resindex_dma_base;
+ /* Unique number identifying the SST core on platform */
+ int sst_id;
+ /* DMA only valid when resindex_dma_base != -1*/
+ int dma_engine;
+ int dma_size;
+};
+
+struct sst_acpi_priv {
+ struct platform_device *pdev_mach;
+ struct platform_device *pdev_pcm;
+ struct sst_pdata sst_pdata;
+ struct sst_acpi_desc *desc;
+ struct sst_acpi_mach *mach;
+};
+
+static void sst_acpi_fw_cb(const struct firmware *fw, void *context)
+{
+ struct platform_device *pdev = context;
+ struct device *dev = &pdev->dev;
+ struct sst_acpi_priv *sst_acpi = platform_get_drvdata(pdev);
+ struct sst_pdata *sst_pdata = &sst_acpi->sst_pdata;
+ struct sst_acpi_desc *desc = sst_acpi->desc;
+ struct sst_acpi_mach *mach = sst_acpi->mach;
+
+ sst_pdata->fw = fw;
+ if (!fw) {
+ dev_err(dev, "Cannot load firmware %s\n", mach->fw_filename);
+ return;
+ }
+
+ /* register PCM and DAI driver */
+ sst_acpi->pdev_pcm =
+ platform_device_register_data(dev, desc->drv_name, -1,
+ sst_pdata, sizeof(*sst_pdata));
+ if (IS_ERR(sst_acpi->pdev_pcm)) {
+ dev_err(dev, "Cannot register device %s. Error %d\n",
+ desc->drv_name, (int)PTR_ERR(sst_acpi->pdev_pcm));
+ }
+
+ return;
+}
+
+static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level,
+ void *context, void **ret)
+{
+ *(bool *)context = true;
+ return AE_OK;
+}
+
+static struct sst_acpi_mach *sst_acpi_find_machine(
+ struct sst_acpi_mach *machines)
+{
+ struct sst_acpi_mach *mach;
+ bool found = false;
+
+ for (mach = machines; mach->id[0]; mach++)
+ if (ACPI_SUCCESS(acpi_get_devices(mach->id,
+ sst_acpi_mach_match,
+ &found, NULL)) && found)
+ return mach;
+
+ return NULL;
+}
+
+static int sst_acpi_probe(struct platform_device *pdev)
+{
+ const struct acpi_device_id *id;
+ struct device *dev = &pdev->dev;
+ struct sst_acpi_priv *sst_acpi;
+ struct sst_pdata *sst_pdata;
+ struct sst_acpi_mach *mach;
+ struct sst_acpi_desc *desc;
+ struct resource *mmio;
+ int ret = 0;
+
+ sst_acpi = devm_kzalloc(dev, sizeof(*sst_acpi), GFP_KERNEL);
+ if (sst_acpi == NULL)
+ return -ENOMEM;
+
+ id = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!id)
+ return -ENODEV;
+
+ desc = (struct sst_acpi_desc *)id->driver_data;
+ mach = sst_acpi_find_machine(desc->machines);
+ if (mach == NULL) {
+ dev_err(dev, "No matching ASoC machine driver found\n");
+ return -ENODEV;
+ }
+
+ sst_pdata = &sst_acpi->sst_pdata;
+ sst_pdata->id = desc->sst_id;
+ sst_pdata->dma_dev = dev;
+ sst_acpi->desc = desc;
+ sst_acpi->mach = mach;
+
+ sst_pdata->resindex_dma_base = desc->resindex_dma_base;
+ if (desc->resindex_dma_base >= 0) {
+ sst_pdata->dma_engine = desc->dma_engine;
+ sst_pdata->dma_base = desc->resindex_dma_base;
+ sst_pdata->dma_size = desc->dma_size;
+ }
+
+ if (desc->irqindex_host_ipc >= 0)
+ sst_pdata->irq = platform_get_irq(pdev, desc->irqindex_host_ipc);
+
+ if (desc->resindex_lpe_base >= 0) {
+ mmio = platform_get_resource(pdev, IORESOURCE_MEM,
+ desc->resindex_lpe_base);
+ if (mmio) {
+ sst_pdata->lpe_base = mmio->start;
+ sst_pdata->lpe_size = resource_size(mmio);
+ }
+ }
+
+ if (desc->resindex_pcicfg_base >= 0) {
+ mmio = platform_get_resource(pdev, IORESOURCE_MEM,
+ desc->resindex_pcicfg_base);
+ if (mmio) {
+ sst_pdata->pcicfg_base = mmio->start;
+ sst_pdata->pcicfg_size = resource_size(mmio);
+ }
+ }
+
+ if (desc->resindex_fw_base >= 0) {
+ mmio = platform_get_resource(pdev, IORESOURCE_MEM,
+ desc->resindex_fw_base);
+ if (mmio) {
+ sst_pdata->fw_base = mmio->start;
+ sst_pdata->fw_size = resource_size(mmio);
+ }
+ }
+
+ platform_set_drvdata(pdev, sst_acpi);
+
+ /* register machine driver */
+ sst_acpi->pdev_mach =
+ platform_device_register_data(dev, mach->drv_name, -1,
+ sst_pdata, sizeof(*sst_pdata));
+ if (IS_ERR(sst_acpi->pdev_mach))
+ return PTR_ERR(sst_acpi->pdev_mach);
+
+ /* continue SST probing after firmware is loaded */
+ ret = request_firmware_nowait(THIS_MODULE, true, mach->fw_filename,
+ dev, GFP_KERNEL, pdev, sst_acpi_fw_cb);
+ if (ret)
+ platform_device_unregister(sst_acpi->pdev_mach);
+
+ return ret;
+}
+
+static int sst_acpi_remove(struct platform_device *pdev)
+{
+ struct sst_acpi_priv *sst_acpi = platform_get_drvdata(pdev);
+ struct sst_pdata *sst_pdata = &sst_acpi->sst_pdata;
+
+ platform_device_unregister(sst_acpi->pdev_mach);
+ if (!IS_ERR_OR_NULL(sst_acpi->pdev_pcm))
+ platform_device_unregister(sst_acpi->pdev_pcm);
+ release_firmware(sst_pdata->fw);
+
+ return 0;
+}
+
+static struct sst_acpi_mach haswell_machines[] = {
+ { "INT33CA", "haswell-audio", "intel/IntcSST1.bin" },
+ {}
+};
+
+static struct sst_acpi_desc sst_acpi_haswell_desc = {
+ .drv_name = "haswell-pcm-audio",
+ .machines = haswell_machines,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = 1,
+ .resindex_fw_base = -1,
+ .irqindex_host_ipc = 0,
+ .sst_id = SST_DEV_ID_LYNX_POINT,
+ .dma_engine = SST_DMA_TYPE_DW,
+ .resindex_dma_base = SST_LPT_DSP_DMA_ADDR_OFFSET,
+ .dma_size = SST_LPT_DSP_DMA_SIZE,
+};
+
+static struct sst_acpi_mach broadwell_machines[] = {
+ { "INT343A", "broadwell-audio", "intel/IntcSST2.bin" },
+ {}
+};
+
+static struct sst_acpi_desc sst_acpi_broadwell_desc = {
+ .drv_name = "haswell-pcm-audio",
+ .machines = broadwell_machines,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = 1,
+ .resindex_fw_base = -1,
+ .irqindex_host_ipc = 0,
+ .sst_id = SST_DEV_ID_WILDCAT_POINT,
+ .dma_engine = SST_DMA_TYPE_DW,
+ .resindex_dma_base = SST_WPT_DSP_DMA_ADDR_OFFSET,
+ .dma_size = SST_LPT_DSP_DMA_SIZE,
+};
+
+static struct sst_acpi_mach baytrail_machines[] = {
+ { "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-48kHz_i2s_master" },
+ { "193C9890", "byt-max98090", "intel/fw_sst_0f28.bin-48kHz_i2s_master" },
+ {}
+};
+
+static struct sst_acpi_desc sst_acpi_baytrail_desc = {
+ .drv_name = "baytrail-pcm-audio",
+ .machines = baytrail_machines,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = 1,
+ .resindex_fw_base = 2,
+ .irqindex_host_ipc = 5,
+ .sst_id = SST_DEV_ID_BYT,
+ .resindex_dma_base = -1,
+};
+
+static struct acpi_device_id sst_acpi_match[] = {
+ { "INT33C8", (unsigned long)&sst_acpi_haswell_desc },
+ { "INT3438", (unsigned long)&sst_acpi_broadwell_desc },
+ { "80860F28", (unsigned long)&sst_acpi_baytrail_desc },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, sst_acpi_match);
+
+static struct platform_driver sst_acpi_driver = {
+ .probe = sst_acpi_probe,
+ .remove = sst_acpi_remove,
+ .driver = {
+ .name = "sst-acpi",
+ .acpi_match_table = ACPI_PTR(sst_acpi_match),
+ },
+};
+module_platform_driver(sst_acpi_driver);
+
+MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>");
+MODULE_DESCRIPTION("Intel SST loader on ACPI systems");
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/*
+ * Intel Smart Sound Technology
+ *
+ * Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __SOUND_SOC_SST_DSP_PRIV_H
+#define __SOUND_SOC_SST_DSP_PRIV_H
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/firmware.h>
+
+struct sst_mem_block;
+struct sst_module;
+struct sst_fw;
+
+/* do we need to remove or keep */
+#define DSP_DRAM_ADDR_OFFSET 0x400000
+
+/*
+ * DSP Operations exported by platform Audio DSP driver.
+ */
+struct sst_ops {
+ /* DSP core boot / reset */
+ void (*boot)(struct sst_dsp *);
+ void (*reset)(struct sst_dsp *);
+ int (*wake)(struct sst_dsp *);
+ void (*sleep)(struct sst_dsp *);
+ void (*stall)(struct sst_dsp *);
+
+ /* Shim IO */
+ void (*write)(void __iomem *addr, u32 offset, u32 value);
+ u32 (*read)(void __iomem *addr, u32 offset);
+ void (*write64)(void __iomem *addr, u32 offset, u64 value);
+ u64 (*read64)(void __iomem *addr, u32 offset);
+
+ /* DSP I/DRAM IO */
+ void (*ram_read)(struct sst_dsp *sst, void *dest, void __iomem *src,
+ size_t bytes);
+ void (*ram_write)(struct sst_dsp *sst, void __iomem *dest, void *src,
+ size_t bytes);
+
+ void (*dump)(struct sst_dsp *);
+
+ /* IRQ handlers */
+ irqreturn_t (*irq_handler)(int irq, void *context);
+
+ /* SST init and free */
+ int (*init)(struct sst_dsp *sst, struct sst_pdata *pdata);
+ void (*free)(struct sst_dsp *sst);
+
+ /* FW module parser/loader */
+ int (*parse_fw)(struct sst_fw *sst_fw);
+};
+
+/*
+ * Audio DSP memory offsets and addresses.
+ */
+struct sst_addr {
+ u32 lpe_base;
+ u32 shim_offset;
+ u32 iram_offset;
+ u32 dram_offset;
+ u32 dsp_iram_offset;
+ u32 dsp_dram_offset;
+ void __iomem *lpe;
+ void __iomem *shim;
+ void __iomem *pci_cfg;
+ void __iomem *fw_ext;
+};
+
+/*
+ * Audio DSP Mailbox configuration.
+ */
+struct sst_mailbox {
+ void __iomem *in_base;
+ void __iomem *out_base;
+ size_t in_size;
+ size_t out_size;
+};
+
+/*
+ * Audio DSP memory block types.
+ */
+enum sst_mem_type {
+ SST_MEM_IRAM = 0,
+ SST_MEM_DRAM = 1,
+ SST_MEM_ANY = 2,
+ SST_MEM_CACHE= 3,
+};
+
+/*
+ * Audio DSP Generic Firmware File.
+ *
+ * SST Firmware files can consist of 1..N modules. This generic structure is
+ * used to manage each firmware file and it's modules regardless of SST firmware
+ * type. A SST driver may load multiple FW files.
+ */
+struct sst_fw {
+ struct sst_dsp *dsp;
+
+ /* base addresses of FW file data */
+ dma_addr_t dmable_fw_paddr; /* physical address of fw data */
+ void *dma_buf; /* virtual address of fw data */
+ u32 size; /* size of fw data */
+
+ /* lists */
+ struct list_head list; /* DSP list of FW */
+ struct list_head module_list; /* FW list of modules */
+
+ void *private; /* core doesn't touch this */
+};
+
+/*
+ * Audio DSP Generic Module Template.
+ *
+ * Used to define and register a new FW module. This data is extracted from
+ * FW module header information.
+ */
+struct sst_module_template {
+ u32 id;
+ u32 entry; /* entry point */
+ u32 scratch_size;
+ u32 persistent_size;
+};
+
+/*
+ * Block Allocator - Used to allocate blocks of DSP memory.
+ */
+struct sst_block_allocator {
+ u32 id;
+ u32 offset;
+ int size;
+ enum sst_mem_type type;
+};
+
+/*
+ * Runtime Module Instance - A module object can be instanciated multiple
+ * times within the DSP FW.
+ */
+struct sst_module_runtime {
+ struct sst_dsp *dsp;
+ int id;
+ struct sst_module *module; /* parent module we belong too */
+
+ u32 persistent_offset; /* private memory offset */
+ void *private;
+
+ struct list_head list;
+ struct list_head block_list; /* list of blocks used */
+};
+
+/*
+ * Runtime Module Context - The runtime context must be manually stored by the
+ * driver prior to enter S3 and restored after leaving S3. This should really be
+ * part of the memory context saved by the enter D3 message IPC ???
+ */
+struct sst_module_runtime_context {
+ dma_addr_t dma_buffer;
+ u32 *buffer;
+};
+
+/*
+ * Audio DSP Module State
+ */
+enum sst_module_state {
+ SST_MODULE_STATE_UNLOADED = 0, /* default state */
+ SST_MODULE_STATE_LOADED,
+ SST_MODULE_STATE_INITIALIZED, /* and inactive */
+ SST_MODULE_STATE_ACTIVE,
+};
+
+/*
+ * Audio DSP Generic Module.
+ *
+ * Each Firmware file can consist of 1..N modules. A module can span multiple
+ * ADSP memory blocks. The simplest FW will be a file with 1 module. A module
+ * can be instanciated multiple times in the DSP.
+ */
+struct sst_module {
+ struct sst_dsp *dsp;
+ struct sst_fw *sst_fw; /* parent FW we belong too */
+
+ /* module configuration */
+ u32 id;
+ u32 entry; /* module entry point */
+ s32 offset; /* module offset in firmware file */
+ u32 size; /* module size */
+ u32 scratch_size; /* global scratch memory required */
+ u32 persistent_size; /* private memory required */
+ enum sst_mem_type type; /* destination memory type */
+ u32 data_offset; /* offset in ADSP memory space */
+ void *data; /* module data */
+
+ /* runtime */
+ u32 usage_count; /* can be unloaded if count == 0 */
+ void *private; /* core doesn't touch this */
+
+ /* lists */
+ struct list_head block_list; /* Module list of blocks in use */
+ struct list_head list; /* DSP list of modules */
+ struct list_head list_fw; /* FW list of modules */
+ struct list_head runtime_list; /* list of runtime module objects*/
+
+ /* state */
+ enum sst_module_state state;
+};
+
+/*
+ * SST Memory Block operations.
+ */
+struct sst_block_ops {
+ int (*enable)(struct sst_mem_block *block);
+ int (*disable)(struct sst_mem_block *block);
+};
+
+/*
+ * SST Generic Memory Block.
+ *
+ * SST ADP memory has multiple IRAM and DRAM blocks. Some ADSP blocks can be
+ * power gated.
+ */
+struct sst_mem_block {
+ struct sst_dsp *dsp;
+ struct sst_module *module; /* module that uses this block */
+
+ /* block config */
+ u32 offset; /* offset from base */
+ u32 size; /* block size */
+ u32 index; /* block index 0..N */
+ enum sst_mem_type type; /* block memory type IRAM/DRAM */
+ struct sst_block_ops *ops; /* block operations, if any */
+
+ /* block status */
+ u32 bytes_used; /* bytes in use by modules */
+ void *private; /* generic core does not touch this */
+ int users; /* number of modules using this block */
+
+ /* block lists */
+ struct list_head module_list; /* Module list of blocks */
+ struct list_head list; /* Map list of free/used blocks */
+};
+
+/*
+ * Generic SST Shim Interface.
+ */
+struct sst_dsp {
+
+ /* runtime */
+ struct sst_dsp_device *sst_dev;
+ spinlock_t spinlock; /* IPC locking */
+ struct mutex mutex; /* DSP FW lock */
+ struct device *dev;
+ struct device *dma_dev;
+ void *thread_context;
+ int irq;
+ u32 id;
+
+ /* list of free and used ADSP memory blocks */
+ struct list_head used_block_list;
+ struct list_head free_block_list;
+
+ /* operations */
+ struct sst_ops *ops;
+
+ /* debug FS */
+ struct dentry *debugfs_root;
+
+ /* base addresses */
+ struct sst_addr addr;
+
+ /* mailbox */
+ struct sst_mailbox mailbox;
+
+ /* SST FW files loaded and their modules */
+ struct list_head module_list;
+ struct list_head fw_list;
+
+ /* scratch buffer */
+ struct list_head scratch_block_list;
+ u32 scratch_offset;
+ u32 scratch_size;
+
+ /* platform data */
+ struct sst_pdata *pdata;
+
+ /* DMA FW loading */
+ struct sst_dma *dma;
+ bool fw_use_dma;
+};
+
+/* Size optimised DRAM/IRAM memcpy */
+static inline void sst_dsp_write(struct sst_dsp *sst, void *src,
+ u32 dest_offset, size_t bytes)
+{
+ sst->ops->ram_write(sst, sst->addr.lpe + dest_offset, src, bytes);
+}
+
+static inline void sst_dsp_read(struct sst_dsp *sst, void *dest,
+ u32 src_offset, size_t bytes)
+{
+ sst->ops->ram_read(sst, dest, sst->addr.lpe + src_offset, bytes);
+}
+
+static inline void *sst_dsp_get_thread_context(struct sst_dsp *sst)
+{
+ return sst->thread_context;
+}
+
+/* Create/Free FW files - can contain multiple modules */
+struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
+ const struct firmware *fw, void *private);
+void sst_fw_free(struct sst_fw *sst_fw);
+void sst_fw_free_all(struct sst_dsp *dsp);
+int sst_fw_reload(struct sst_fw *sst_fw);
+void sst_fw_unload(struct sst_fw *sst_fw);
+
+/* Create/Free firmware modules */
+struct sst_module *sst_module_new(struct sst_fw *sst_fw,
+ struct sst_module_template *template, void *private);
+void sst_module_free(struct sst_module *module);
+struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id);
+int sst_module_alloc_blocks(struct sst_module *module);
+int sst_module_free_blocks(struct sst_module *module);
+
+/* Create/Free firmware module runtime instances */
+struct sst_module_runtime *sst_module_runtime_new(struct sst_module *module,
+ int id, void *private);
+void sst_module_runtime_free(struct sst_module_runtime *runtime);
+struct sst_module_runtime *sst_module_runtime_get_from_id(
+ struct sst_module *module, u32 id);
+int sst_module_runtime_alloc_blocks(struct sst_module_runtime *runtime,
+ int offset);
+int sst_module_runtime_free_blocks(struct sst_module_runtime *runtime);
+int sst_module_runtime_save(struct sst_module_runtime *runtime,
+ struct sst_module_runtime_context *context);
+int sst_module_runtime_restore(struct sst_module_runtime *runtime,
+ struct sst_module_runtime_context *context);
+
+/* generic block allocation */
+int sst_alloc_blocks(struct sst_dsp *dsp, struct sst_block_allocator *ba,
+ struct list_head *block_list);
+int sst_free_blocks(struct sst_dsp *dsp, struct list_head *block_list);
+
+/* scratch allocation */
+int sst_block_alloc_scratch(struct sst_dsp *dsp);
+void sst_block_free_scratch(struct sst_dsp *dsp);
+
+/* Register the DSPs memory blocks - would be nice to read from ACPI */
+struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
+ u32 size, enum sst_mem_type type, struct sst_block_ops *ops, u32 index,
+ void *private);
+void sst_mem_block_unregister_all(struct sst_dsp *dsp);
+
+/* Create/Free DMA resources */
+int sst_dma_new(struct sst_dsp *sst);
+void sst_dma_free(struct sst_dma *dma);
+
+u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset,
+ enum sst_mem_type type);
+#endif
--- /dev/null
+/*
+ * Intel Smart Sound Technology (SST) DSP Core Driver
+ *
+ * Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#include "sst-dsp.h"
+#include "sst-dsp-priv.h"
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/intel-sst.h>
+
+/* Internal generic low-level SST IO functions - can be overidden */
+void sst_shim32_write(void __iomem *addr, u32 offset, u32 value)
+{
+ writel(value, addr + offset);
+}
+EXPORT_SYMBOL_GPL(sst_shim32_write);
+
+u32 sst_shim32_read(void __iomem *addr, u32 offset)
+{
+ return readl(addr + offset);
+}
+EXPORT_SYMBOL_GPL(sst_shim32_read);
+
+void sst_shim32_write64(void __iomem *addr, u32 offset, u64 value)
+{
+ memcpy_toio(addr + offset, &value, sizeof(value));
+}
+EXPORT_SYMBOL_GPL(sst_shim32_write64);
+
+u64 sst_shim32_read64(void __iomem *addr, u32 offset)
+{
+ u64 val;
+
+ memcpy_fromio(&val, addr + offset, sizeof(val));
+ return val;
+}
+EXPORT_SYMBOL_GPL(sst_shim32_read64);
+
+static inline void _sst_memcpy_toio_32(volatile u32 __iomem *dest,
+ u32 *src, size_t bytes)
+{
+ int i, words = bytes >> 2;
+
+ for (i = 0; i < words; i++)
+ writel(src[i], dest + i);
+}
+
+static inline void _sst_memcpy_fromio_32(u32 *dest,
+ const volatile __iomem u32 *src, size_t bytes)
+{
+ int i, words = bytes >> 2;
+
+ for (i = 0; i < words; i++)
+ dest[i] = readl(src + i);
+}
+
+void sst_memcpy_toio_32(struct sst_dsp *sst,
+ void __iomem *dest, void *src, size_t bytes)
+{
+ _sst_memcpy_toio_32(dest, src, bytes);
+}
+EXPORT_SYMBOL_GPL(sst_memcpy_toio_32);
+
+void sst_memcpy_fromio_32(struct sst_dsp *sst, void *dest,
+ void __iomem *src, size_t bytes)
+{
+ _sst_memcpy_fromio_32(dest, src, bytes);
+}
+EXPORT_SYMBOL_GPL(sst_memcpy_fromio_32);
+
+/* Public API */
+void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sst->spinlock, flags);
+ sst->ops->write(sst->addr.shim, offset, value);
+ spin_unlock_irqrestore(&sst->spinlock, flags);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_write);
+
+u32 sst_dsp_shim_read(struct sst_dsp *sst, u32 offset)
+{
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&sst->spinlock, flags);
+ val = sst->ops->read(sst->addr.shim, offset);
+ spin_unlock_irqrestore(&sst->spinlock, flags);
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_read);
+
+void sst_dsp_shim_write64(struct sst_dsp *sst, u32 offset, u64 value)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sst->spinlock, flags);
+ sst->ops->write64(sst->addr.shim, offset, value);
+ spin_unlock_irqrestore(&sst->spinlock, flags);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_write64);
+
+u64 sst_dsp_shim_read64(struct sst_dsp *sst, u32 offset)
+{
+ unsigned long flags;
+ u64 val;
+
+ spin_lock_irqsave(&sst->spinlock, flags);
+ val = sst->ops->read64(sst->addr.shim, offset);
+ spin_unlock_irqrestore(&sst->spinlock, flags);
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_read64);
+
+void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value)
+{
+ sst->ops->write(sst->addr.shim, offset, value);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_write_unlocked);
+
+u32 sst_dsp_shim_read_unlocked(struct sst_dsp *sst, u32 offset)
+{
+ return sst->ops->read(sst->addr.shim, offset);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_read_unlocked);
+
+void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value)
+{
+ sst->ops->write64(sst->addr.shim, offset, value);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_write64_unlocked);
+
+u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset)
+{
+ return sst->ops->read64(sst->addr.shim, offset);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_read64_unlocked);
+
+int sst_dsp_shim_update_bits_unlocked(struct sst_dsp *sst, u32 offset,
+ u32 mask, u32 value)
+{
+ bool change;
+ unsigned int old, new;
+ u32 ret;
+
+ ret = sst_dsp_shim_read_unlocked(sst, offset);
+
+ old = ret;
+ new = (old & (~mask)) | (value & mask);
+
+ change = (old != new);
+ if (change)
+ sst_dsp_shim_write_unlocked(sst, offset, new);
+
+ return change;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_unlocked);
+
+int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset,
+ u64 mask, u64 value)
+{
+ bool change;
+ u64 old, new;
+
+ old = sst_dsp_shim_read64_unlocked(sst, offset);
+
+ new = (old & (~mask)) | (value & mask);
+
+ change = (old != new);
+ if (change)
+ sst_dsp_shim_write64_unlocked(sst, offset, new);
+
+ return change;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64_unlocked);
+
+int sst_dsp_shim_update_bits(struct sst_dsp *sst, u32 offset,
+ u32 mask, u32 value)
+{
+ unsigned long flags;
+ bool change;
+
+ spin_lock_irqsave(&sst->spinlock, flags);
+ change = sst_dsp_shim_update_bits_unlocked(sst, offset, mask, value);
+ spin_unlock_irqrestore(&sst->spinlock, flags);
+ return change;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits);
+
+int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset,
+ u64 mask, u64 value)
+{
+ unsigned long flags;
+ bool change;
+
+ spin_lock_irqsave(&sst->spinlock, flags);
+ change = sst_dsp_shim_update_bits64_unlocked(sst, offset, mask, value);
+ spin_unlock_irqrestore(&sst->spinlock, flags);
+ return change;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64);
+
+void sst_dsp_dump(struct sst_dsp *sst)
+{
+ if (sst->ops->dump)
+ sst->ops->dump(sst);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_dump);
+
+void sst_dsp_reset(struct sst_dsp *sst)
+{
+ if (sst->ops->reset)
+ sst->ops->reset(sst);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_reset);
+
+int sst_dsp_boot(struct sst_dsp *sst)
+{
+ if (sst->ops->boot)
+ sst->ops->boot(sst);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_boot);
+
+int sst_dsp_wake(struct sst_dsp *sst)
+{
+ if (sst->ops->wake)
+ return sst->ops->wake(sst);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_wake);
+
+void sst_dsp_sleep(struct sst_dsp *sst)
+{
+ if (sst->ops->sleep)
+ sst->ops->sleep(sst);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_sleep);
+
+void sst_dsp_stall(struct sst_dsp *sst)
+{
+ if (sst->ops->stall)
+ sst->ops->stall(sst);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_stall);
+
+void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg)
+{
+ sst_dsp_shim_write_unlocked(dsp, SST_IPCX, msg | SST_IPCX_BUSY);
+ trace_sst_ipc_msg_tx(msg);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_tx);
+
+u32 sst_dsp_ipc_msg_rx(struct sst_dsp *dsp)
+{
+ u32 msg;
+
+ msg = sst_dsp_shim_read_unlocked(dsp, SST_IPCX);
+ trace_sst_ipc_msg_rx(msg);
+
+ return msg;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_rx);
+
+int sst_dsp_mailbox_init(struct sst_dsp *sst, u32 inbox_offset, size_t inbox_size,
+ u32 outbox_offset, size_t outbox_size)
+{
+ sst->mailbox.in_base = sst->addr.lpe + inbox_offset;
+ sst->mailbox.out_base = sst->addr.lpe + outbox_offset;
+ sst->mailbox.in_size = inbox_size;
+ sst->mailbox.out_size = outbox_size;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_mailbox_init);
+
+void sst_dsp_outbox_write(struct sst_dsp *sst, void *message, size_t bytes)
+{
+ u32 i;
+
+ trace_sst_ipc_outbox_write(bytes);
+
+ memcpy_toio(sst->mailbox.out_base, message, bytes);
+
+ for (i = 0; i < bytes; i += 4)
+ trace_sst_ipc_outbox_wdata(i, *(u32 *)(message + i));
+}
+EXPORT_SYMBOL_GPL(sst_dsp_outbox_write);
+
+void sst_dsp_outbox_read(struct sst_dsp *sst, void *message, size_t bytes)
+{
+ u32 i;
+
+ trace_sst_ipc_outbox_read(bytes);
+
+ memcpy_fromio(message, sst->mailbox.out_base, bytes);
+
+ for (i = 0; i < bytes; i += 4)
+ trace_sst_ipc_outbox_rdata(i, *(u32 *)(message + i));
+}
+EXPORT_SYMBOL_GPL(sst_dsp_outbox_read);
+
+void sst_dsp_inbox_write(struct sst_dsp *sst, void *message, size_t bytes)
+{
+ u32 i;
+
+ trace_sst_ipc_inbox_write(bytes);
+
+ memcpy_toio(sst->mailbox.in_base, message, bytes);
+
+ for (i = 0; i < bytes; i += 4)
+ trace_sst_ipc_inbox_wdata(i, *(u32 *)(message + i));
+}
+EXPORT_SYMBOL_GPL(sst_dsp_inbox_write);
+
+void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes)
+{
+ u32 i;
+
+ trace_sst_ipc_inbox_read(bytes);
+
+ memcpy_fromio(message, sst->mailbox.in_base, bytes);
+
+ for (i = 0; i < bytes; i += 4)
+ trace_sst_ipc_inbox_rdata(i, *(u32 *)(message + i));
+}
+EXPORT_SYMBOL_GPL(sst_dsp_inbox_read);
+
+struct sst_dsp *sst_dsp_new(struct device *dev,
+ struct sst_dsp_device *sst_dev, struct sst_pdata *pdata)
+{
+ struct sst_dsp *sst;
+ int err;
+
+ dev_dbg(dev, "initialising audio DSP id 0x%x\n", pdata->id);
+
+ sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL);
+ if (sst == NULL)
+ return NULL;
+
+ spin_lock_init(&sst->spinlock);
+ mutex_init(&sst->mutex);
+ sst->dev = dev;
+ sst->dma_dev = pdata->dma_dev;
+ sst->thread_context = sst_dev->thread_context;
+ sst->sst_dev = sst_dev;
+ sst->id = pdata->id;
+ sst->irq = pdata->irq;
+ sst->ops = sst_dev->ops;
+ sst->pdata = pdata;
+ INIT_LIST_HEAD(&sst->used_block_list);
+ INIT_LIST_HEAD(&sst->free_block_list);
+ INIT_LIST_HEAD(&sst->module_list);
+ INIT_LIST_HEAD(&sst->fw_list);
+ INIT_LIST_HEAD(&sst->scratch_block_list);
+
+ /* Initialise SST Audio DSP */
+ if (sst->ops->init) {
+ err = sst->ops->init(sst, pdata);
+ if (err < 0)
+ return NULL;
+ }
+
+ /* Register the ISR */
+ err = request_threaded_irq(sst->irq, sst->ops->irq_handler,
+ sst_dev->thread, IRQF_SHARED, "AudioDSP", sst);
+ if (err)
+ goto irq_err;
+
+ err = sst_dma_new(sst);
+ if (err)
+ dev_warn(dev, "sst_dma_new failed %d\n", err);
+
+ return sst;
+
+irq_err:
+ if (sst->ops->free)
+ sst->ops->free(sst);
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_new);
+
+void sst_dsp_free(struct sst_dsp *sst)
+{
+ free_irq(sst->irq, sst);
+ if (sst->ops->free)
+ sst->ops->free(sst);
+
+ sst_dma_free(sst->dma);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_free);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_DESCRIPTION("Intel SST Core");
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/*
+ * Intel Smart Sound Technology (SST) Core
+ *
+ * Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __SOUND_SOC_SST_DSP_H
+#define __SOUND_SOC_SST_DSP_H
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+
+/* SST Device IDs */
+#define SST_DEV_ID_LYNX_POINT 0x33C8
+#define SST_DEV_ID_WILDCAT_POINT 0x3438
+#define SST_DEV_ID_BYT 0x0F28
+
+/* Supported SST DMA Devices */
+#define SST_DMA_TYPE_DW 1
+
+/* autosuspend delay 5s*/
+#define SST_RUNTIME_SUSPEND_DELAY (5 * 1000)
+
+/* SST Shim register map
+ * The register naming can differ between products. Some products also
+ * contain extra functionality.
+ */
+#define SST_CSR 0x00
+#define SST_PISR 0x08
+#define SST_PIMR 0x10
+#define SST_ISRX 0x18
+#define SST_ISRD 0x20
+#define SST_IMRX 0x28
+#define SST_IMRD 0x30
+#define SST_IPCX 0x38 /* IPC IA -> SST */
+#define SST_IPCD 0x40 /* IPC SST -> IA */
+#define SST_ISRSC 0x48
+#define SST_ISRLPESC 0x50
+#define SST_IMRSC 0x58
+#define SST_IMRLPESC 0x60
+#define SST_IPCSC 0x68
+#define SST_IPCLPESC 0x70
+#define SST_CLKCTL 0x78
+#define SST_CSR2 0x80
+#define SST_LTRC 0xE0
+#define SST_HMDC 0xE8
+
+#define SST_SHIM_BEGIN SST_CSR
+#define SST_SHIM_END SST_HDMC
+
+#define SST_DBGO 0xF0
+
+#define SST_SHIM_SIZE 0x100
+#define SST_PWMCTRL 0x1000
+
+/* SST Shim Register bits
+ * The register bit naming can differ between products. Some products also
+ * contain extra functionality.
+ */
+
+/* CSR / CS */
+#define SST_CSR_RST (0x1 << 1)
+#define SST_CSR_SBCS0 (0x1 << 2)
+#define SST_CSR_SBCS1 (0x1 << 3)
+#define SST_CSR_DCS(x) (x << 4)
+#define SST_CSR_DCS_MASK (0x7 << 4)
+#define SST_CSR_STALL (0x1 << 10)
+#define SST_CSR_S0IOCS (0x1 << 21)
+#define SST_CSR_S1IOCS (0x1 << 23)
+#define SST_CSR_LPCS (0x1 << 31)
+#define SST_CSR_24MHZ_LPCS (SST_CSR_SBCS0 | SST_CSR_SBCS1 | SST_CSR_LPCS)
+#define SST_CSR_24MHZ_NO_LPCS (SST_CSR_SBCS0 | SST_CSR_SBCS1)
+#define SST_BYT_CSR_RST (0x1 << 0)
+#define SST_BYT_CSR_VECTOR_SEL (0x1 << 1)
+#define SST_BYT_CSR_STALL (0x1 << 2)
+#define SST_BYT_CSR_PWAITMODE (0x1 << 3)
+
+/* ISRX / ISC */
+#define SST_ISRX_BUSY (0x1 << 1)
+#define SST_ISRX_DONE (0x1 << 0)
+#define SST_BYT_ISRX_REQUEST (0x1 << 1)
+
+/* ISRD / ISD */
+#define SST_ISRD_BUSY (0x1 << 1)
+#define SST_ISRD_DONE (0x1 << 0)
+
+/* IMRX / IMC */
+#define SST_IMRX_BUSY (0x1 << 1)
+#define SST_IMRX_DONE (0x1 << 0)
+#define SST_BYT_IMRX_REQUEST (0x1 << 1)
+
+/* IMRD / IMD */
+#define SST_IMRD_DONE (0x1 << 0)
+#define SST_IMRD_BUSY (0x1 << 1)
+#define SST_IMRD_SSP0 (0x1 << 16)
+#define SST_IMRD_DMAC0 (0x1 << 21)
+#define SST_IMRD_DMAC1 (0x1 << 22)
+#define SST_IMRD_DMAC (SST_IMRD_DMAC0 | SST_IMRD_DMAC1)
+
+/* IPCX / IPCC */
+#define SST_IPCX_DONE (0x1 << 30)
+#define SST_IPCX_BUSY (0x1 << 31)
+#define SST_BYT_IPCX_DONE ((u64)0x1 << 62)
+#define SST_BYT_IPCX_BUSY ((u64)0x1 << 63)
+
+/* IPCD */
+#define SST_IPCD_DONE (0x1 << 30)
+#define SST_IPCD_BUSY (0x1 << 31)
+#define SST_BYT_IPCD_DONE ((u64)0x1 << 62)
+#define SST_BYT_IPCD_BUSY ((u64)0x1 << 63)
+
+/* CLKCTL */
+#define SST_CLKCTL_SMOS(x) (x << 24)
+#define SST_CLKCTL_MASK (3 << 24)
+#define SST_CLKCTL_DCPLCG (1 << 18)
+#define SST_CLKCTL_SCOE1 (1 << 17)
+#define SST_CLKCTL_SCOE0 (1 << 16)
+
+/* CSR2 / CS2 */
+#define SST_CSR2_SDFD_SSP0 (1 << 1)
+#define SST_CSR2_SDFD_SSP1 (1 << 2)
+
+/* LTRC */
+#define SST_LTRC_VAL(x) (x << 0)
+
+/* HMDC */
+#define SST_HMDC_HDDA0(x) (x << 0)
+#define SST_HMDC_HDDA1(x) (x << 7)
+#define SST_HMDC_HDDA_E0_CH0 1
+#define SST_HMDC_HDDA_E0_CH1 2
+#define SST_HMDC_HDDA_E0_CH2 4
+#define SST_HMDC_HDDA_E0_CH3 8
+#define SST_HMDC_HDDA_E1_CH0 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH0)
+#define SST_HMDC_HDDA_E1_CH1 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH1)
+#define SST_HMDC_HDDA_E1_CH2 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH2)
+#define SST_HMDC_HDDA_E1_CH3 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH3)
+#define SST_HMDC_HDDA_E0_ALLCH (SST_HMDC_HDDA_E0_CH0 | SST_HMDC_HDDA_E0_CH1 | \
+ SST_HMDC_HDDA_E0_CH2 | SST_HMDC_HDDA_E0_CH3)
+#define SST_HMDC_HDDA_E1_ALLCH (SST_HMDC_HDDA_E1_CH0 | SST_HMDC_HDDA_E1_CH1 | \
+ SST_HMDC_HDDA_E1_CH2 | SST_HMDC_HDDA_E1_CH3)
+
+
+/* SST Vendor Defined Registers and bits */
+#define SST_VDRTCTL0 0xa0
+#define SST_VDRTCTL1 0xa4
+#define SST_VDRTCTL2 0xa8
+#define SST_VDRTCTL3 0xaC
+
+/* VDRTCTL0 */
+#define SST_VDRTCL0_D3PGD (1 << 0)
+#define SST_VDRTCL0_D3SRAMPGD (1 << 1)
+#define SST_VDRTCL0_DSRAMPGE_SHIFT 12
+#define SST_VDRTCL0_DSRAMPGE_MASK (0xfffff << SST_VDRTCL0_DSRAMPGE_SHIFT)
+#define SST_VDRTCL0_ISRAMPGE_SHIFT 2
+#define SST_VDRTCL0_ISRAMPGE_MASK (0x3ff << SST_VDRTCL0_ISRAMPGE_SHIFT)
+
+/* VDRTCTL2 */
+#define SST_VDRTCL2_DCLCGE (1 << 1)
+#define SST_VDRTCL2_DTCGE (1 << 10)
+#define SST_VDRTCL2_APLLSE_MASK (1 << 31)
+
+/* PMCS */
+#define SST_PMCS 0x84
+#define SST_PMCS_PS_MASK 0x3
+
+struct sst_dsp;
+
+/*
+ * SST Device.
+ *
+ * This structure is populated by the SST core driver.
+ */
+struct sst_dsp_device {
+ /* Mandatory fields */
+ struct sst_ops *ops;
+ irqreturn_t (*thread)(int irq, void *context);
+ void *thread_context;
+};
+
+/*
+ * SST Platform Data.
+ */
+struct sst_pdata {
+ /* ACPI data */
+ u32 lpe_base;
+ u32 lpe_size;
+ u32 pcicfg_base;
+ u32 pcicfg_size;
+ u32 fw_base;
+ u32 fw_size;
+ int irq;
+
+ /* Firmware */
+ const struct firmware *fw;
+
+ /* DMA */
+ int resindex_dma_base; /* other fields invalid if equals to -1 */
+ u32 dma_base;
+ u32 dma_size;
+ int dma_engine;
+ struct device *dma_dev;
+
+ /* DSP */
+ u32 id;
+ void *dsp;
+};
+
+/* Initialization */
+struct sst_dsp *sst_dsp_new(struct device *dev,
+ struct sst_dsp_device *sst_dev, struct sst_pdata *pdata);
+void sst_dsp_free(struct sst_dsp *sst);
+
+/* SHIM Read / Write */
+void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value);
+u32 sst_dsp_shim_read(struct sst_dsp *sst, u32 offset);
+int sst_dsp_shim_update_bits(struct sst_dsp *sst, u32 offset,
+ u32 mask, u32 value);
+void sst_dsp_shim_write64(struct sst_dsp *sst, u32 offset, u64 value);
+u64 sst_dsp_shim_read64(struct sst_dsp *sst, u32 offset);
+int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset,
+ u64 mask, u64 value);
+
+/* SHIM Read / Write Unlocked for callers already holding sst lock */
+void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value);
+u32 sst_dsp_shim_read_unlocked(struct sst_dsp *sst, u32 offset);
+int sst_dsp_shim_update_bits_unlocked(struct sst_dsp *sst, u32 offset,
+ u32 mask, u32 value);
+void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value);
+u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset);
+int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset,
+ u64 mask, u64 value);
+
+/* Internal generic low-level SST IO functions - can be overidden */
+void sst_shim32_write(void __iomem *addr, u32 offset, u32 value);
+u32 sst_shim32_read(void __iomem *addr, u32 offset);
+void sst_shim32_write64(void __iomem *addr, u32 offset, u64 value);
+u64 sst_shim32_read64(void __iomem *addr, u32 offset);
+void sst_memcpy_toio_32(struct sst_dsp *sst,
+ void __iomem *dest, void *src, size_t bytes);
+void sst_memcpy_fromio_32(struct sst_dsp *sst,
+ void *dest, void __iomem *src, size_t bytes);
+
+/* DSP reset & boot */
+void sst_dsp_reset(struct sst_dsp *sst);
+int sst_dsp_boot(struct sst_dsp *sst);
+int sst_dsp_wake(struct sst_dsp *sst);
+void sst_dsp_sleep(struct sst_dsp *sst);
+void sst_dsp_stall(struct sst_dsp *sst);
+
+/* DMA */
+int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id);
+void sst_dsp_dma_put_channel(struct sst_dsp *dsp);
+int sst_dsp_dma_copyfrom(struct sst_dsp *sst, dma_addr_t dest_addr,
+ dma_addr_t src_addr, size_t size);
+int sst_dsp_dma_copyto(struct sst_dsp *sst, dma_addr_t dest_addr,
+ dma_addr_t src_addr, size_t size);
+
+/* Msg IO */
+void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg);
+u32 sst_dsp_ipc_msg_rx(struct sst_dsp *dsp);
+
+/* Mailbox management */
+int sst_dsp_mailbox_init(struct sst_dsp *dsp, u32 inbox_offset,
+ size_t inbox_size, u32 outbox_offset, size_t outbox_size);
+void sst_dsp_inbox_write(struct sst_dsp *dsp, void *message, size_t bytes);
+void sst_dsp_inbox_read(struct sst_dsp *dsp, void *message, size_t bytes);
+void sst_dsp_outbox_write(struct sst_dsp *dsp, void *message, size_t bytes);
+void sst_dsp_outbox_read(struct sst_dsp *dsp, void *message, size_t bytes);
+void sst_dsp_mailbox_dump(struct sst_dsp *dsp, size_t bytes);
+
+/* Debug */
+void sst_dsp_dump(struct sst_dsp *sst);
+
+#endif
--- /dev/null
+/*
+ * Intel SST Firmware Loader
+ *
+ * Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+#include <linux/export.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/pci.h>
+#include <linux/acpi.h>
+
+/* supported DMA engine drivers */
+#include <linux/platform_data/dma-dw.h>
+#include <linux/dma/dw.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
+#include "sst-dsp.h"
+#include "sst-dsp-priv.h"
+
+#define SST_DMA_RESOURCES 2
+#define SST_DSP_DMA_MAX_BURST 0x3
+#define SST_HSW_BLOCK_ANY 0xffffffff
+
+#define SST_HSW_MASK_DMA_ADDR_DSP 0xfff00000
+
+struct sst_dma {
+ struct sst_dsp *sst;
+
+ struct dw_dma_chip *chip;
+
+ struct dma_async_tx_descriptor *desc;
+ struct dma_chan *ch;
+};
+
+static inline void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes)
+{
+ /* __iowrite32_copy use 32bit size values so divide by 4 */
+ __iowrite32_copy((void *)dest, src, bytes/4);
+}
+
+static void sst_dma_transfer_complete(void *arg)
+{
+ struct sst_dsp *sst = (struct sst_dsp *)arg;
+
+ dev_dbg(sst->dev, "DMA: callback\n");
+}
+
+static int sst_dsp_dma_copy(struct sst_dsp *sst, dma_addr_t dest_addr,
+ dma_addr_t src_addr, size_t size)
+{
+ struct dma_async_tx_descriptor *desc;
+ struct sst_dma *dma = sst->dma;
+
+ if (dma->ch == NULL) {
+ dev_err(sst->dev, "error: no DMA channel\n");
+ return -ENODEV;
+ }
+
+ dev_dbg(sst->dev, "DMA: src: 0x%lx dest 0x%lx size %zu\n",
+ (unsigned long)src_addr, (unsigned long)dest_addr, size);
+
+ desc = dma->ch->device->device_prep_dma_memcpy(dma->ch, dest_addr,
+ src_addr, size, DMA_CTRL_ACK);
+ if (!desc){
+ dev_err(sst->dev, "error: dma prep memcpy failed\n");
+ return -EINVAL;
+ }
+
+ desc->callback = sst_dma_transfer_complete;
+ desc->callback_param = sst;
+
+ desc->tx_submit(desc);
+ dma_wait_for_async_tx(desc);
+
+ return 0;
+}
+
+/* copy to DSP */
+int sst_dsp_dma_copyto(struct sst_dsp *sst, dma_addr_t dest_addr,
+ dma_addr_t src_addr, size_t size)
+{
+ return sst_dsp_dma_copy(sst, dest_addr | SST_HSW_MASK_DMA_ADDR_DSP,
+ src_addr, size);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_dma_copyto);
+
+/* copy from DSP */
+int sst_dsp_dma_copyfrom(struct sst_dsp *sst, dma_addr_t dest_addr,
+ dma_addr_t src_addr, size_t size)
+{
+ return sst_dsp_dma_copy(sst, dest_addr,
+ src_addr | SST_HSW_MASK_DMA_ADDR_DSP, size);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_dma_copyfrom);
+
+/* remove module from memory - callers hold locks */
+static void block_list_remove(struct sst_dsp *dsp,
+ struct list_head *block_list)
+{
+ struct sst_mem_block *block, *tmp;
+ int err;
+
+ /* disable each block */
+ list_for_each_entry(block, block_list, module_list) {
+
+ if (block->ops && block->ops->disable) {
+ err = block->ops->disable(block);
+ if (err < 0)
+ dev_err(dsp->dev,
+ "error: cant disable block %d:%d\n",
+ block->type, block->index);
+ }
+ }
+
+ /* mark each block as free */
+ list_for_each_entry_safe(block, tmp, block_list, module_list) {
+ list_del(&block->module_list);
+ list_move(&block->list, &dsp->free_block_list);
+ dev_dbg(dsp->dev, "block freed %d:%d at offset 0x%x\n",
+ block->type, block->index, block->offset);
+ }
+}
+
+/* prepare the memory block to receive data from host - callers hold locks */
+static int block_list_prepare(struct sst_dsp *dsp,
+ struct list_head *block_list)
+{
+ struct sst_mem_block *block;
+ int ret = 0;
+
+ /* enable each block so that's it'e ready for data */
+ list_for_each_entry(block, block_list, module_list) {
+
+ if (block->ops && block->ops->enable && !block->users) {
+ ret = block->ops->enable(block);
+ if (ret < 0) {
+ dev_err(dsp->dev,
+ "error: cant disable block %d:%d\n",
+ block->type, block->index);
+ goto err;
+ }
+ }
+ }
+ return ret;
+
+err:
+ list_for_each_entry(block, block_list, module_list) {
+ if (block->ops && block->ops->disable)
+ block->ops->disable(block);
+ }
+ return ret;
+}
+
+static struct dw_dma_platform_data dw_pdata = {
+ .is_private = 1,
+ .chan_allocation_order = CHAN_ALLOCATION_ASCENDING,
+ .chan_priority = CHAN_PRIORITY_ASCENDING,
+};
+
+static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem,
+ int irq)
+{
+ struct dw_dma_chip *chip;
+ int err;
+
+ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return ERR_PTR(-ENOMEM);
+
+ chip->irq = irq;
+ chip->regs = devm_ioremap_resource(dev, mem);
+ if (IS_ERR(chip->regs))
+ return ERR_CAST(chip->regs);
+
+ err = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(31));
+ if (err)
+ return ERR_PTR(err);
+
+ chip->dev = dev;
+ err = dw_dma_probe(chip, &dw_pdata);
+ if (err)
+ return ERR_PTR(err);
+
+ return chip;
+}
+
+static void dw_remove(struct dw_dma_chip *chip)
+{
+ dw_dma_remove(chip);
+}
+
+static bool dma_chan_filter(struct dma_chan *chan, void *param)
+{
+ struct sst_dsp *dsp = (struct sst_dsp *)param;
+
+ return chan->device->dev == dsp->dma_dev;
+}
+
+int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id)
+{
+ struct sst_dma *dma = dsp->dma;
+ struct dma_slave_config slave;
+ dma_cap_mask_t mask;
+ int ret;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+
+ dma->ch = dma_request_channel(mask, dma_chan_filter, dsp);
+ if (dma->ch == NULL) {
+ dev_err(dsp->dev, "error: DMA request channel failed\n");
+ return -EIO;
+ }
+
+ memset(&slave, 0, sizeof(slave));
+ slave.direction = DMA_MEM_TO_DEV;
+ slave.src_addr_width =
+ slave.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ slave.src_maxburst = slave.dst_maxburst = SST_DSP_DMA_MAX_BURST;
+
+ ret = dmaengine_slave_config(dma->ch, &slave);
+ if (ret) {
+ dev_err(dsp->dev, "error: unable to set DMA slave config %d\n",
+ ret);
+ dma_release_channel(dma->ch);
+ dma->ch = NULL;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_dma_get_channel);
+
+void sst_dsp_dma_put_channel(struct sst_dsp *dsp)
+{
+ struct sst_dma *dma = dsp->dma;
+
+ if (!dma->ch)
+ return;
+
+ dma_release_channel(dma->ch);
+ dma->ch = NULL;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_dma_put_channel);
+
+int sst_dma_new(struct sst_dsp *sst)
+{
+ struct sst_pdata *sst_pdata = sst->pdata;
+ struct sst_dma *dma;
+ struct resource mem;
+ const char *dma_dev_name;
+ int ret = 0;
+
+ if (sst->pdata->resindex_dma_base == -1)
+ /* DMA is not used, return and squelsh error messages */
+ return 0;
+
+ /* configure the correct platform data for whatever DMA engine
+ * is attached to the ADSP IP. */
+ switch (sst->pdata->dma_engine) {
+ case SST_DMA_TYPE_DW:
+ dma_dev_name = "dw_dmac";
+ break;
+ default:
+ dev_err(sst->dev, "error: invalid DMA engine %d\n",
+ sst->pdata->dma_engine);
+ return -EINVAL;
+ }
+
+ dma = devm_kzalloc(sst->dev, sizeof(struct sst_dma), GFP_KERNEL);
+ if (!dma)
+ return -ENOMEM;
+
+ dma->sst = sst;
+
+ memset(&mem, 0, sizeof(mem));
+
+ mem.start = sst->addr.lpe_base + sst_pdata->dma_base;
+ mem.end = sst->addr.lpe_base + sst_pdata->dma_base + sst_pdata->dma_size - 1;
+ mem.flags = IORESOURCE_MEM;
+
+ /* now register DMA engine device */
+ dma->chip = dw_probe(sst->dma_dev, &mem, sst_pdata->irq);
+ if (IS_ERR(dma->chip)) {
+ dev_err(sst->dev, "error: DMA device register failed\n");
+ ret = PTR_ERR(dma->chip);
+ goto err_dma_dev;
+ }
+
+ sst->dma = dma;
+ sst->fw_use_dma = true;
+ return 0;
+
+err_dma_dev:
+ devm_kfree(sst->dev, dma);
+ return ret;
+}
+EXPORT_SYMBOL(sst_dma_new);
+
+void sst_dma_free(struct sst_dma *dma)
+{
+
+ if (dma == NULL)
+ return;
+
+ if (dma->ch)
+ dma_release_channel(dma->ch);
+
+ if (dma->chip)
+ dw_remove(dma->chip);
+
+}
+EXPORT_SYMBOL(sst_dma_free);
+
+/* create new generic firmware object */
+struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
+ const struct firmware *fw, void *private)
+{
+ struct sst_fw *sst_fw;
+ int err;
+
+ if (!dsp->ops->parse_fw)
+ return NULL;
+
+ sst_fw = kzalloc(sizeof(*sst_fw), GFP_KERNEL);
+ if (sst_fw == NULL)
+ return NULL;
+
+ sst_fw->dsp = dsp;
+ sst_fw->private = private;
+ sst_fw->size = fw->size;
+
+ /* allocate DMA buffer to store FW data */
+ sst_fw->dma_buf = dma_alloc_coherent(dsp->dma_dev, sst_fw->size,
+ &sst_fw->dmable_fw_paddr, GFP_DMA | GFP_KERNEL);
+ if (!sst_fw->dma_buf) {
+ dev_err(dsp->dev, "error: DMA alloc failed\n");
+ kfree(sst_fw);
+ return NULL;
+ }
+
+ /* copy FW data to DMA-able memory */
+ memcpy((void *)sst_fw->dma_buf, (void *)fw->data, fw->size);
+
+ if (dsp->fw_use_dma) {
+ err = sst_dsp_dma_get_channel(dsp, 0);
+ if (err < 0)
+ goto chan_err;
+ }
+
+ /* call core specific FW paser to load FW data into DSP */
+ err = dsp->ops->parse_fw(sst_fw);
+ if (err < 0) {
+ dev_err(dsp->dev, "error: parse fw failed %d\n", err);
+ goto parse_err;
+ }
+
+ if (dsp->fw_use_dma)
+ sst_dsp_dma_put_channel(dsp);
+
+ mutex_lock(&dsp->mutex);
+ list_add(&sst_fw->list, &dsp->fw_list);
+ mutex_unlock(&dsp->mutex);
+
+ return sst_fw;
+
+parse_err:
+ if (dsp->fw_use_dma)
+ sst_dsp_dma_put_channel(dsp);
+chan_err:
+ dma_free_coherent(dsp->dma_dev, sst_fw->size,
+ sst_fw->dma_buf,
+ sst_fw->dmable_fw_paddr);
+ sst_fw->dma_buf = NULL;
+ kfree(sst_fw);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(sst_fw_new);
+
+int sst_fw_reload(struct sst_fw *sst_fw)
+{
+ struct sst_dsp *dsp = sst_fw->dsp;
+ int ret;
+
+ dev_dbg(dsp->dev, "reloading firmware\n");
+
+ /* call core specific FW paser to load FW data into DSP */
+ ret = dsp->ops->parse_fw(sst_fw);
+ if (ret < 0)
+ dev_err(dsp->dev, "error: parse fw failed %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sst_fw_reload);
+
+void sst_fw_unload(struct sst_fw *sst_fw)
+{
+ struct sst_dsp *dsp = sst_fw->dsp;
+ struct sst_module *module, *mtmp;
+ struct sst_module_runtime *runtime, *rtmp;
+
+ dev_dbg(dsp->dev, "unloading firmware\n");
+
+ mutex_lock(&dsp->mutex);
+
+ /* check module by module */
+ list_for_each_entry_safe(module, mtmp, &dsp->module_list, list) {
+ if (module->sst_fw == sst_fw) {
+
+ /* remove runtime modules */
+ list_for_each_entry_safe(runtime, rtmp, &module->runtime_list, list) {
+
+ block_list_remove(dsp, &runtime->block_list);
+ list_del(&runtime->list);
+ kfree(runtime);
+ }
+
+ /* now remove the module */
+ block_list_remove(dsp, &module->block_list);
+ list_del(&module->list);
+ kfree(module);
+ }
+ }
+
+ /* remove all scratch blocks */
+ block_list_remove(dsp, &dsp->scratch_block_list);
+
+ mutex_unlock(&dsp->mutex);
+}
+EXPORT_SYMBOL_GPL(sst_fw_unload);
+
+/* free single firmware object */
+void sst_fw_free(struct sst_fw *sst_fw)
+{
+ struct sst_dsp *dsp = sst_fw->dsp;
+
+ mutex_lock(&dsp->mutex);
+ list_del(&sst_fw->list);
+ mutex_unlock(&dsp->mutex);
+
+ if (sst_fw->dma_buf)
+ dma_free_coherent(dsp->dma_dev, sst_fw->size, sst_fw->dma_buf,
+ sst_fw->dmable_fw_paddr);
+ kfree(sst_fw);
+}
+EXPORT_SYMBOL_GPL(sst_fw_free);
+
+/* free all firmware objects */
+void sst_fw_free_all(struct sst_dsp *dsp)
+{
+ struct sst_fw *sst_fw, *t;
+
+ mutex_lock(&dsp->mutex);
+ list_for_each_entry_safe(sst_fw, t, &dsp->fw_list, list) {
+
+ list_del(&sst_fw->list);
+ dma_free_coherent(dsp->dev, sst_fw->size, sst_fw->dma_buf,
+ sst_fw->dmable_fw_paddr);
+ kfree(sst_fw);
+ }
+ mutex_unlock(&dsp->mutex);
+}
+EXPORT_SYMBOL_GPL(sst_fw_free_all);
+
+/* create a new SST generic module from FW template */
+struct sst_module *sst_module_new(struct sst_fw *sst_fw,
+ struct sst_module_template *template, void *private)
+{
+ struct sst_dsp *dsp = sst_fw->dsp;
+ struct sst_module *sst_module;
+
+ sst_module = kzalloc(sizeof(*sst_module), GFP_KERNEL);
+ if (sst_module == NULL)
+ return NULL;
+
+ sst_module->id = template->id;
+ sst_module->dsp = dsp;
+ sst_module->sst_fw = sst_fw;
+ sst_module->scratch_size = template->scratch_size;
+ sst_module->persistent_size = template->persistent_size;
+ sst_module->entry = template->entry;
+ sst_module->state = SST_MODULE_STATE_UNLOADED;
+
+ INIT_LIST_HEAD(&sst_module->block_list);
+ INIT_LIST_HEAD(&sst_module->runtime_list);
+
+ mutex_lock(&dsp->mutex);
+ list_add(&sst_module->list, &dsp->module_list);
+ mutex_unlock(&dsp->mutex);
+
+ return sst_module;
+}
+EXPORT_SYMBOL_GPL(sst_module_new);
+
+/* free firmware module and remove from available list */
+void sst_module_free(struct sst_module *sst_module)
+{
+ struct sst_dsp *dsp = sst_module->dsp;
+
+ mutex_lock(&dsp->mutex);
+ list_del(&sst_module->list);
+ mutex_unlock(&dsp->mutex);
+
+ kfree(sst_module);
+}
+EXPORT_SYMBOL_GPL(sst_module_free);
+
+struct sst_module_runtime *sst_module_runtime_new(struct sst_module *module,
+ int id, void *private)
+{
+ struct sst_dsp *dsp = module->dsp;
+ struct sst_module_runtime *runtime;
+
+ runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
+ if (runtime == NULL)
+ return NULL;
+
+ runtime->id = id;
+ runtime->dsp = dsp;
+ runtime->module = module;
+ INIT_LIST_HEAD(&runtime->block_list);
+
+ mutex_lock(&dsp->mutex);
+ list_add(&runtime->list, &module->runtime_list);
+ mutex_unlock(&dsp->mutex);
+
+ return runtime;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_new);
+
+void sst_module_runtime_free(struct sst_module_runtime *runtime)
+{
+ struct sst_dsp *dsp = runtime->dsp;
+
+ mutex_lock(&dsp->mutex);
+ list_del(&runtime->list);
+ mutex_unlock(&dsp->mutex);
+
+ kfree(runtime);
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_free);
+
+static struct sst_mem_block *find_block(struct sst_dsp *dsp,
+ struct sst_block_allocator *ba)
+{
+ struct sst_mem_block *block;
+
+ list_for_each_entry(block, &dsp->free_block_list, list) {
+ if (block->type == ba->type && block->offset == ba->offset)
+ return block;
+ }
+
+ return NULL;
+}
+
+/* Block allocator must be on block boundary */
+static int block_alloc_contiguous(struct sst_dsp *dsp,
+ struct sst_block_allocator *ba, struct list_head *block_list)
+{
+ struct list_head tmp = LIST_HEAD_INIT(tmp);
+ struct sst_mem_block *block;
+ u32 block_start = SST_HSW_BLOCK_ANY;
+ int size = ba->size, offset = ba->offset;
+
+ while (ba->size > 0) {
+
+ block = find_block(dsp, ba);
+ if (!block) {
+ list_splice(&tmp, &dsp->free_block_list);
+
+ ba->size = size;
+ ba->offset = offset;
+ return -ENOMEM;
+ }
+
+ list_move_tail(&block->list, &tmp);
+ ba->offset += block->size;
+ ba->size -= block->size;
+ }
+ ba->size = size;
+ ba->offset = offset;
+
+ list_for_each_entry(block, &tmp, list) {
+
+ if (block->offset < block_start)
+ block_start = block->offset;
+
+ list_add(&block->module_list, block_list);
+
+ dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n",
+ block->type, block->index, block->offset);
+ }
+
+ list_splice(&tmp, &dsp->used_block_list);
+ return 0;
+}
+
+/* allocate first free DSP blocks for data - callers hold locks */
+static int block_alloc(struct sst_dsp *dsp, struct sst_block_allocator *ba,
+ struct list_head *block_list)
+{
+ struct sst_mem_block *block, *tmp;
+ int ret = 0;
+
+ if (ba->size == 0)
+ return 0;
+
+ /* find first free whole blocks that can hold module */
+ list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
+
+ /* ignore blocks with wrong type */
+ if (block->type != ba->type)
+ continue;
+
+ if (ba->size > block->size)
+ continue;
+
+ ba->offset = block->offset;
+ block->bytes_used = ba->size % block->size;
+ list_add(&block->module_list, block_list);
+ list_move(&block->list, &dsp->used_block_list);
+ dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n",
+ block->type, block->index, block->offset);
+ return 0;
+ }
+
+ /* then find free multiple blocks that can hold module */
+ list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
+
+ /* ignore blocks with wrong type */
+ if (block->type != ba->type)
+ continue;
+
+ /* do we span > 1 blocks */
+ if (ba->size > block->size) {
+
+ /* align ba to block boundary */
+ ba->offset = block->offset;
+
+ ret = block_alloc_contiguous(dsp, ba, block_list);
+ if (ret == 0)
+ return ret;
+
+ }
+ }
+
+ /* not enough free block space */
+ return -ENOMEM;
+}
+
+int sst_alloc_blocks(struct sst_dsp *dsp, struct sst_block_allocator *ba,
+ struct list_head *block_list)
+{
+ int ret;
+
+ dev_dbg(dsp->dev, "block request 0x%x bytes at offset 0x%x type %d\n",
+ ba->size, ba->offset, ba->type);
+
+ mutex_lock(&dsp->mutex);
+
+ ret = block_alloc(dsp, ba, block_list);
+ if (ret < 0) {
+ dev_err(dsp->dev, "error: can't alloc blocks %d\n", ret);
+ goto out;
+ }
+
+ /* prepare DSP blocks for module usage */
+ ret = block_list_prepare(dsp, block_list);
+ if (ret < 0)
+ dev_err(dsp->dev, "error: prepare failed\n");
+
+out:
+ mutex_unlock(&dsp->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sst_alloc_blocks);
+
+int sst_free_blocks(struct sst_dsp *dsp, struct list_head *block_list)
+{
+ mutex_lock(&dsp->mutex);
+ block_list_remove(dsp, block_list);
+ mutex_unlock(&dsp->mutex);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_free_blocks);
+
+/* allocate memory blocks for static module addresses - callers hold locks */
+static int block_alloc_fixed(struct sst_dsp *dsp, struct sst_block_allocator *ba,
+ struct list_head *block_list)
+{
+ struct sst_mem_block *block, *tmp;
+ struct sst_block_allocator ba_tmp = *ba;
+ u32 end = ba->offset + ba->size, block_end;
+ int err;
+
+ /* only IRAM/DRAM blocks are managed */
+ if (ba->type != SST_MEM_IRAM && ba->type != SST_MEM_DRAM)
+ return 0;
+
+ /* are blocks already attached to this module */
+ list_for_each_entry_safe(block, tmp, block_list, module_list) {
+
+ /* ignore blocks with wrong type */
+ if (block->type != ba->type)
+ continue;
+
+ block_end = block->offset + block->size;
+
+ /* find block that holds section */
+ if (ba->offset >= block->offset && end <= block_end)
+ return 0;
+
+ /* does block span more than 1 section */
+ if (ba->offset >= block->offset && ba->offset < block_end) {
+
+ /* align ba to block boundary */
+ ba_tmp.size -= block_end - ba->offset;
+ ba_tmp.offset = block_end;
+ err = block_alloc_contiguous(dsp, &ba_tmp, block_list);
+ if (err < 0)
+ return -ENOMEM;
+
+ /* module already owns blocks */
+ return 0;
+ }
+ }
+
+ /* find first free blocks that can hold section in free list */
+ list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
+ block_end = block->offset + block->size;
+
+ /* ignore blocks with wrong type */
+ if (block->type != ba->type)
+ continue;
+
+ /* find block that holds section */
+ if (ba->offset >= block->offset && end <= block_end) {
+
+ /* add block */
+ list_move(&block->list, &dsp->used_block_list);
+ list_add(&block->module_list, block_list);
+ dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n",
+ block->type, block->index, block->offset);
+ return 0;
+ }
+
+ /* does block span more than 1 section */
+ if (ba->offset >= block->offset && ba->offset < block_end) {
+
+ /* add block */
+ list_move(&block->list, &dsp->used_block_list);
+ list_add(&block->module_list, block_list);
+ /* align ba to block boundary */
+ ba_tmp.size -= block_end - ba->offset;
+ ba_tmp.offset = block_end;
+
+ err = block_alloc_contiguous(dsp, &ba_tmp, block_list);
+ if (err < 0)
+ return -ENOMEM;
+
+ return 0;
+ }
+ }
+
+ return -ENOMEM;
+}
+
+/* Load fixed module data into DSP memory blocks */
+int sst_module_alloc_blocks(struct sst_module *module)
+{
+ struct sst_dsp *dsp = module->dsp;
+ struct sst_fw *sst_fw = module->sst_fw;
+ struct sst_block_allocator ba;
+ int ret;
+
+ memset(&ba, 0, sizeof(ba));
+ ba.size = module->size;
+ ba.type = module->type;
+ ba.offset = module->offset;
+
+ dev_dbg(dsp->dev, "block request 0x%x bytes at offset 0x%x type %d\n",
+ ba.size, ba.offset, ba.type);
+
+ mutex_lock(&dsp->mutex);
+
+ /* alloc blocks that includes this section */
+ ret = block_alloc_fixed(dsp, &ba, &module->block_list);
+ if (ret < 0) {
+ dev_err(dsp->dev,
+ "error: no free blocks for section at offset 0x%x size 0x%x\n",
+ module->offset, module->size);
+ mutex_unlock(&dsp->mutex);
+ return -ENOMEM;
+ }
+
+ /* prepare DSP blocks for module copy */
+ ret = block_list_prepare(dsp, &module->block_list);
+ if (ret < 0) {
+ dev_err(dsp->dev, "error: fw module prepare failed\n");
+ goto err;
+ }
+
+ /* copy partial module data to blocks */
+ if (dsp->fw_use_dma) {
+ ret = sst_dsp_dma_copyto(dsp,
+ dsp->addr.lpe_base + module->offset,
+ sst_fw->dmable_fw_paddr + module->data_offset,
+ module->size);
+ if (ret < 0) {
+ dev_err(dsp->dev, "error: module copy failed\n");
+ goto err;
+ }
+ } else
+ sst_memcpy32(dsp->addr.lpe + module->offset, module->data,
+ module->size);
+
+ mutex_unlock(&dsp->mutex);
+ return ret;
+
+err:
+ block_list_remove(dsp, &module->block_list);
+ mutex_unlock(&dsp->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sst_module_alloc_blocks);
+
+/* Unload entire module from DSP memory */
+int sst_module_free_blocks(struct sst_module *module)
+{
+ struct sst_dsp *dsp = module->dsp;
+
+ mutex_lock(&dsp->mutex);
+ block_list_remove(dsp, &module->block_list);
+ mutex_unlock(&dsp->mutex);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_module_free_blocks);
+
+int sst_module_runtime_alloc_blocks(struct sst_module_runtime *runtime,
+ int offset)
+{
+ struct sst_dsp *dsp = runtime->dsp;
+ struct sst_module *module = runtime->module;
+ struct sst_block_allocator ba;
+ int ret;
+
+ if (module->persistent_size == 0)
+ return 0;
+
+ memset(&ba, 0, sizeof(ba));
+ ba.size = module->persistent_size;
+ ba.type = SST_MEM_DRAM;
+
+ mutex_lock(&dsp->mutex);
+
+ /* do we need to allocate at a fixed address ? */
+ if (offset != 0) {
+
+ ba.offset = offset;
+
+ dev_dbg(dsp->dev, "persistent fixed block request 0x%x bytes type %d offset 0x%x\n",
+ ba.size, ba.type, ba.offset);
+
+ /* alloc blocks that includes this section */
+ ret = block_alloc_fixed(dsp, &ba, &runtime->block_list);
+
+ } else {
+ dev_dbg(dsp->dev, "persistent block request 0x%x bytes type %d\n",
+ ba.size, ba.type);
+
+ /* alloc blocks that includes this section */
+ ret = block_alloc(dsp, &ba, &runtime->block_list);
+ }
+ if (ret < 0) {
+ dev_err(dsp->dev,
+ "error: no free blocks for runtime module size 0x%x\n",
+ module->persistent_size);
+ mutex_unlock(&dsp->mutex);
+ return -ENOMEM;
+ }
+ runtime->persistent_offset = ba.offset;
+
+ /* prepare DSP blocks for module copy */
+ ret = block_list_prepare(dsp, &runtime->block_list);
+ if (ret < 0) {
+ dev_err(dsp->dev, "error: runtime block prepare failed\n");
+ goto err;
+ }
+
+ mutex_unlock(&dsp->mutex);
+ return ret;
+
+err:
+ block_list_remove(dsp, &module->block_list);
+ mutex_unlock(&dsp->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_alloc_blocks);
+
+int sst_module_runtime_free_blocks(struct sst_module_runtime *runtime)
+{
+ struct sst_dsp *dsp = runtime->dsp;
+
+ mutex_lock(&dsp->mutex);
+ block_list_remove(dsp, &runtime->block_list);
+ mutex_unlock(&dsp->mutex);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_free_blocks);
+
+int sst_module_runtime_save(struct sst_module_runtime *runtime,
+ struct sst_module_runtime_context *context)
+{
+ struct sst_dsp *dsp = runtime->dsp;
+ struct sst_module *module = runtime->module;
+ int ret = 0;
+
+ dev_dbg(dsp->dev, "saving runtime %d memory at 0x%x size 0x%x\n",
+ runtime->id, runtime->persistent_offset,
+ module->persistent_size);
+
+ context->buffer = dma_alloc_coherent(dsp->dma_dev,
+ module->persistent_size,
+ &context->dma_buffer, GFP_DMA | GFP_KERNEL);
+ if (!context->buffer) {
+ dev_err(dsp->dev, "error: DMA context alloc failed\n");
+ return -ENOMEM;
+ }
+
+ mutex_lock(&dsp->mutex);
+
+ if (dsp->fw_use_dma) {
+
+ ret = sst_dsp_dma_get_channel(dsp, 0);
+ if (ret < 0)
+ goto err;
+
+ ret = sst_dsp_dma_copyfrom(dsp, context->dma_buffer,
+ dsp->addr.lpe_base + runtime->persistent_offset,
+ module->persistent_size);
+ sst_dsp_dma_put_channel(dsp);
+ if (ret < 0) {
+ dev_err(dsp->dev, "error: context copy failed\n");
+ goto err;
+ }
+ } else
+ sst_memcpy32(context->buffer, dsp->addr.lpe +
+ runtime->persistent_offset,
+ module->persistent_size);
+
+err:
+ mutex_unlock(&dsp->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_save);
+
+int sst_module_runtime_restore(struct sst_module_runtime *runtime,
+ struct sst_module_runtime_context *context)
+{
+ struct sst_dsp *dsp = runtime->dsp;
+ struct sst_module *module = runtime->module;
+ int ret = 0;
+
+ dev_dbg(dsp->dev, "restoring runtime %d memory at 0x%x size 0x%x\n",
+ runtime->id, runtime->persistent_offset,
+ module->persistent_size);
+
+ mutex_lock(&dsp->mutex);
+
+ if (!context->buffer) {
+ dev_info(dsp->dev, "no context buffer need to restore!\n");
+ goto err;
+ }
+
+ if (dsp->fw_use_dma) {
+
+ ret = sst_dsp_dma_get_channel(dsp, 0);
+ if (ret < 0)
+ goto err;
+
+ ret = sst_dsp_dma_copyto(dsp,
+ dsp->addr.lpe_base + runtime->persistent_offset,
+ context->dma_buffer, module->persistent_size);
+ sst_dsp_dma_put_channel(dsp);
+ if (ret < 0) {
+ dev_err(dsp->dev, "error: module copy failed\n");
+ goto err;
+ }
+ } else
+ sst_memcpy32(dsp->addr.lpe + runtime->persistent_offset,
+ context->buffer, module->persistent_size);
+
+ dma_free_coherent(dsp->dma_dev, module->persistent_size,
+ context->buffer, context->dma_buffer);
+ context->buffer = NULL;
+
+err:
+ mutex_unlock(&dsp->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_restore);
+
+/* register a DSP memory block for use with FW based modules */
+struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
+ u32 size, enum sst_mem_type type, struct sst_block_ops *ops, u32 index,
+ void *private)
+{
+ struct sst_mem_block *block;
+
+ block = kzalloc(sizeof(*block), GFP_KERNEL);
+ if (block == NULL)
+ return NULL;
+
+ block->offset = offset;
+ block->size = size;
+ block->index = index;
+ block->type = type;
+ block->dsp = dsp;
+ block->private = private;
+ block->ops = ops;
+
+ mutex_lock(&dsp->mutex);
+ list_add(&block->list, &dsp->free_block_list);
+ mutex_unlock(&dsp->mutex);
+
+ return block;
+}
+EXPORT_SYMBOL_GPL(sst_mem_block_register);
+
+/* unregister all DSP memory blocks */
+void sst_mem_block_unregister_all(struct sst_dsp *dsp)
+{
+ struct sst_mem_block *block, *tmp;
+
+ mutex_lock(&dsp->mutex);
+
+ /* unregister used blocks */
+ list_for_each_entry_safe(block, tmp, &dsp->used_block_list, list) {
+ list_del(&block->list);
+ kfree(block);
+ }
+
+ /* unregister free blocks */
+ list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
+ list_del(&block->list);
+ kfree(block);
+ }
+
+ mutex_unlock(&dsp->mutex);
+}
+EXPORT_SYMBOL_GPL(sst_mem_block_unregister_all);
+
+/* allocate scratch buffer blocks */
+int sst_block_alloc_scratch(struct sst_dsp *dsp)
+{
+ struct sst_module *module;
+ struct sst_block_allocator ba;
+ int ret;
+
+ mutex_lock(&dsp->mutex);
+
+ /* calculate required scratch size */
+ dsp->scratch_size = 0;
+ list_for_each_entry(module, &dsp->module_list, list) {
+ dev_dbg(dsp->dev, "module %d scratch req 0x%x bytes\n",
+ module->id, module->scratch_size);
+ if (dsp->scratch_size < module->scratch_size)
+ dsp->scratch_size = module->scratch_size;
+ }
+
+ dev_dbg(dsp->dev, "scratch buffer required is 0x%x bytes\n",
+ dsp->scratch_size);
+
+ if (dsp->scratch_size == 0) {
+ dev_info(dsp->dev, "no modules need scratch buffer\n");
+ mutex_unlock(&dsp->mutex);
+ return 0;
+ }
+
+ /* allocate blocks for module scratch buffers */
+ dev_dbg(dsp->dev, "allocating scratch blocks\n");
+
+ ba.size = dsp->scratch_size;
+ ba.type = SST_MEM_DRAM;
+
+ /* do we need to allocate at fixed offset */
+ if (dsp->scratch_offset != 0) {
+
+ dev_dbg(dsp->dev, "block request 0x%x bytes type %d at 0x%x\n",
+ ba.size, ba.type, ba.offset);
+
+ ba.offset = dsp->scratch_offset;
+
+ /* alloc blocks that includes this section */
+ ret = block_alloc_fixed(dsp, &ba, &dsp->scratch_block_list);
+
+ } else {
+ dev_dbg(dsp->dev, "block request 0x%x bytes type %d\n",
+ ba.size, ba.type);
+
+ ba.offset = 0;
+ ret = block_alloc(dsp, &ba, &dsp->scratch_block_list);
+ }
+ if (ret < 0) {
+ dev_err(dsp->dev, "error: can't alloc scratch blocks\n");
+ mutex_unlock(&dsp->mutex);
+ return ret;
+ }
+
+ ret = block_list_prepare(dsp, &dsp->scratch_block_list);
+ if (ret < 0) {
+ dev_err(dsp->dev, "error: scratch block prepare failed\n");
+ mutex_unlock(&dsp->mutex);
+ return ret;
+ }
+
+ /* assign the same offset of scratch to each module */
+ dsp->scratch_offset = ba.offset;
+ mutex_unlock(&dsp->mutex);
+ return dsp->scratch_size;
+}
+EXPORT_SYMBOL_GPL(sst_block_alloc_scratch);
+
+/* free all scratch blocks */
+void sst_block_free_scratch(struct sst_dsp *dsp)
+{
+ mutex_lock(&dsp->mutex);
+ block_list_remove(dsp, &dsp->scratch_block_list);
+ mutex_unlock(&dsp->mutex);
+}
+EXPORT_SYMBOL_GPL(sst_block_free_scratch);
+
+/* get a module from it's unique ID */
+struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id)
+{
+ struct sst_module *module;
+
+ mutex_lock(&dsp->mutex);
+
+ list_for_each_entry(module, &dsp->module_list, list) {
+ if (module->id == id) {
+ mutex_unlock(&dsp->mutex);
+ return module;
+ }
+ }
+
+ mutex_unlock(&dsp->mutex);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(sst_module_get_from_id);
+
+struct sst_module_runtime *sst_module_runtime_get_from_id(
+ struct sst_module *module, u32 id)
+{
+ struct sst_module_runtime *runtime;
+ struct sst_dsp *dsp = module->dsp;
+
+ mutex_lock(&dsp->mutex);
+
+ list_for_each_entry(runtime, &module->runtime_list, list) {
+ if (runtime->id == id) {
+ mutex_unlock(&dsp->mutex);
+ return runtime;
+ }
+ }
+
+ mutex_unlock(&dsp->mutex);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_get_from_id);
+
+/* returns block address in DSP address space */
+u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset,
+ enum sst_mem_type type)
+{
+ switch (type) {
+ case SST_MEM_IRAM:
+ return offset - dsp->addr.iram_offset +
+ dsp->addr.dsp_iram_offset;
+ case SST_MEM_DRAM:
+ return offset - dsp->addr.dram_offset +
+ dsp->addr.dsp_dram_offset;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(sst_dsp_get_offset);
--- /dev/null
+/*
+ * Intel SST generic IPC Support
+ *
+ * Copyright (C) 2015, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/kthread.h>
+#include <sound/asound.h>
+
+#include "sst-dsp.h"
+#include "sst-dsp-priv.h"
+#include "sst-ipc.h"
+
+/* IPC message timeout (msecs) */
+#define IPC_TIMEOUT_MSECS 300
+
+#define IPC_EMPTY_LIST_SIZE 8
+
+/* locks held by caller */
+static struct ipc_message *msg_get_empty(struct sst_generic_ipc *ipc)
+{
+ struct ipc_message *msg = NULL;
+
+ if (!list_empty(&ipc->empty_list)) {
+ msg = list_first_entry(&ipc->empty_list, struct ipc_message,
+ list);
+ list_del(&msg->list);
+ }
+
+ return msg;
+}
+
+static int tx_wait_done(struct sst_generic_ipc *ipc,
+ struct ipc_message *msg, void *rx_data)
+{
+ unsigned long flags;
+ int ret;
+
+ /* wait for DSP completion (in all cases atm inc pending) */
+ ret = wait_event_timeout(msg->waitq, msg->complete,
+ msecs_to_jiffies(IPC_TIMEOUT_MSECS));
+
+ spin_lock_irqsave(&ipc->dsp->spinlock, flags);
+ if (ret == 0) {
+ if (ipc->ops.shim_dbg != NULL)
+ ipc->ops.shim_dbg(ipc, "message timeout");
+
+ list_del(&msg->list);
+ ret = -ETIMEDOUT;
+ } else {
+
+ /* copy the data returned from DSP */
+ if (msg->rx_size)
+ memcpy(rx_data, msg->rx_data, msg->rx_size);
+ ret = msg->errno;
+ }
+
+ list_add_tail(&msg->list, &ipc->empty_list);
+ spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
+ return ret;
+}
+
+static int ipc_tx_message(struct sst_generic_ipc *ipc, u64 header,
+ void *tx_data, size_t tx_bytes, void *rx_data,
+ size_t rx_bytes, int wait)
+{
+ struct ipc_message *msg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ipc->dsp->spinlock, flags);
+
+ msg = msg_get_empty(ipc);
+ if (msg == NULL) {
+ spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
+ return -EBUSY;
+ }
+
+ msg->header = header;
+ msg->tx_size = tx_bytes;
+ msg->rx_size = rx_bytes;
+ msg->wait = wait;
+ msg->errno = 0;
+ msg->pending = false;
+ msg->complete = false;
+
+ if ((tx_bytes) && (ipc->ops.tx_data_copy != NULL))
+ ipc->ops.tx_data_copy(msg, tx_data, tx_bytes);
+
+ list_add_tail(&msg->list, &ipc->tx_list);
+ spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
+
+ queue_kthread_work(&ipc->kworker, &ipc->kwork);
+
+ if (wait)
+ return tx_wait_done(ipc, msg, rx_data);
+ else
+ return 0;
+}
+
+static int msg_empty_list_init(struct sst_generic_ipc *ipc)
+{
+ int i;
+
+ ipc->msg = kzalloc(sizeof(struct ipc_message) *
+ IPC_EMPTY_LIST_SIZE, GFP_KERNEL);
+ if (ipc->msg == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
+ init_waitqueue_head(&ipc->msg[i].waitq);
+ list_add(&ipc->msg[i].list, &ipc->empty_list);
+ }
+
+ return 0;
+}
+
+static void ipc_tx_msgs(struct kthread_work *work)
+{
+ struct sst_generic_ipc *ipc =
+ container_of(work, struct sst_generic_ipc, kwork);
+ struct ipc_message *msg;
+ unsigned long flags;
+ u64 ipcx;
+
+ spin_lock_irqsave(&ipc->dsp->spinlock, flags);
+
+ if (list_empty(&ipc->tx_list) || ipc->pending) {
+ spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
+ return;
+ }
+
+ /* if the DSP is busy, we will TX messages after IRQ.
+ * also postpone if we are in the middle of procesing completion irq*/
+ ipcx = sst_dsp_shim_read_unlocked(ipc->dsp, SST_IPCX);
+ if (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE)) {
+ spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
+ return;
+ }
+
+ msg = list_first_entry(&ipc->tx_list, struct ipc_message, list);
+ list_move(&msg->list, &ipc->rx_list);
+
+ if (ipc->ops.tx_msg != NULL)
+ ipc->ops.tx_msg(ipc, msg);
+
+ spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
+}
+
+int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header,
+ void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes)
+{
+ return ipc_tx_message(ipc, header, tx_data, tx_bytes,
+ rx_data, rx_bytes, 1);
+}
+EXPORT_SYMBOL_GPL(sst_ipc_tx_message_wait);
+
+int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header,
+ void *tx_data, size_t tx_bytes)
+{
+ return ipc_tx_message(ipc, header, tx_data, tx_bytes,
+ NULL, 0, 0);
+}
+EXPORT_SYMBOL_GPL(sst_ipc_tx_message_nowait);
+
+struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc,
+ u64 header)
+{
+ struct ipc_message *msg;
+ u64 mask;
+
+ if (ipc->ops.reply_msg_match != NULL)
+ header = ipc->ops.reply_msg_match(header, &mask);
+
+ if (list_empty(&ipc->rx_list)) {
+ dev_err(ipc->dev, "error: rx list empty but received 0x%llx\n",
+ header);
+ return NULL;
+ }
+
+ list_for_each_entry(msg, &ipc->rx_list, list) {
+ if ((msg->header & mask) == header)
+ return msg;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(sst_ipc_reply_find_msg);
+
+/* locks held by caller */
+void sst_ipc_tx_msg_reply_complete(struct sst_generic_ipc *ipc,
+ struct ipc_message *msg)
+{
+ msg->complete = true;
+
+ if (!msg->wait)
+ list_add_tail(&msg->list, &ipc->empty_list);
+ else
+ wake_up(&msg->waitq);
+}
+EXPORT_SYMBOL_GPL(sst_ipc_tx_msg_reply_complete);
+
+void sst_ipc_drop_all(struct sst_generic_ipc *ipc)
+{
+ struct ipc_message *msg, *tmp;
+ unsigned long flags;
+ int tx_drop_cnt = 0, rx_drop_cnt = 0;
+
+ /* drop all TX and Rx messages before we stall + reset DSP */
+ spin_lock_irqsave(&ipc->dsp->spinlock, flags);
+
+ list_for_each_entry_safe(msg, tmp, &ipc->tx_list, list) {
+ list_move(&msg->list, &ipc->empty_list);
+ tx_drop_cnt++;
+ }
+
+ list_for_each_entry_safe(msg, tmp, &ipc->rx_list, list) {
+ list_move(&msg->list, &ipc->empty_list);
+ rx_drop_cnt++;
+ }
+
+ spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
+
+ if (tx_drop_cnt || rx_drop_cnt)
+ dev_err(ipc->dev, "dropped IPC msg RX=%d, TX=%d\n",
+ tx_drop_cnt, rx_drop_cnt);
+}
+EXPORT_SYMBOL_GPL(sst_ipc_drop_all);
+
+int sst_ipc_init(struct sst_generic_ipc *ipc)
+{
+ int ret;
+
+ INIT_LIST_HEAD(&ipc->tx_list);
+ INIT_LIST_HEAD(&ipc->rx_list);
+ INIT_LIST_HEAD(&ipc->empty_list);
+ init_waitqueue_head(&ipc->wait_txq);
+
+ ret = msg_empty_list_init(ipc);
+ if (ret < 0)
+ return -ENOMEM;
+
+ /* start the IPC message thread */
+ init_kthread_worker(&ipc->kworker);
+ ipc->tx_thread = kthread_run(kthread_worker_fn,
+ &ipc->kworker, "%s",
+ dev_name(ipc->dev));
+ if (IS_ERR(ipc->tx_thread)) {
+ dev_err(ipc->dev, "error: failed to create message TX task\n");
+ ret = PTR_ERR(ipc->tx_thread);
+ kfree(ipc->msg);
+ return ret;
+ }
+
+ init_kthread_work(&ipc->kwork, ipc_tx_msgs);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_ipc_init);
+
+void sst_ipc_fini(struct sst_generic_ipc *ipc)
+{
+ if (ipc->tx_thread)
+ kthread_stop(ipc->tx_thread);
+
+ if (ipc->msg)
+ kfree(ipc->msg);
+}
+EXPORT_SYMBOL_GPL(sst_ipc_fini);
+
+/* Module information */
+MODULE_AUTHOR("Jin Yao");
+MODULE_DESCRIPTION("Intel SST IPC generic");
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/*
+ * Intel SST generic IPC Support
+ *
+ * Copyright (C) 2015, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __SST_GENERIC_IPC_H
+#define __SST_GENERIC_IPC_H
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/workqueue.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+
+#define IPC_MAX_MAILBOX_BYTES 256
+
+struct ipc_message {
+ struct list_head list;
+ u64 header;
+
+ /* direction wrt host CPU */
+ char tx_data[IPC_MAX_MAILBOX_BYTES];
+ size_t tx_size;
+ char rx_data[IPC_MAX_MAILBOX_BYTES];
+ size_t rx_size;
+
+ wait_queue_head_t waitq;
+ bool pending;
+ bool complete;
+ bool wait;
+ int errno;
+};
+
+struct sst_generic_ipc;
+
+struct sst_plat_ipc_ops {
+ void (*tx_msg)(struct sst_generic_ipc *, struct ipc_message *);
+ void (*shim_dbg)(struct sst_generic_ipc *, const char *);
+ void (*tx_data_copy)(struct ipc_message *, char *, size_t);
+ u64 (*reply_msg_match)(u64 header, u64 *mask);
+};
+
+/* SST generic IPC data */
+struct sst_generic_ipc {
+ struct device *dev;
+ struct sst_dsp *dsp;
+
+ /* IPC messaging */
+ struct list_head tx_list;
+ struct list_head rx_list;
+ struct list_head empty_list;
+ wait_queue_head_t wait_txq;
+ struct task_struct *tx_thread;
+ struct kthread_worker kworker;
+ struct kthread_work kwork;
+ bool pending;
+ struct ipc_message *msg;
+
+ struct sst_plat_ipc_ops ops;
+};
+
+int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header,
+ void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes);
+
+int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header,
+ void *tx_data, size_t tx_bytes);
+
+struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc,
+ u64 header);
+
+void sst_ipc_tx_msg_reply_complete(struct sst_generic_ipc *ipc,
+ struct ipc_message *msg);
+
+void sst_ipc_drop_all(struct sst_generic_ipc *ipc);
+int sst_ipc_init(struct sst_generic_ipc *ipc);
+void sst_ipc_fini(struct sst_generic_ipc *ipc);
+
+#endif
+++ /dev/null
-/*
- * Intel Haswell Lynxpoint SST Audio
- *
- * Copyright (C) 2013, Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-
-#include "sst-dsp.h"
-#include "sst-haswell-ipc.h"
-
-#include "../codecs/rt5640.h"
-
-/* Haswell ULT platforms have a Headphone and Mic jack */
-static const struct snd_soc_dapm_widget haswell_widgets[] = {
- SND_SOC_DAPM_HP("Headphones", NULL),
- SND_SOC_DAPM_MIC("Mic", NULL),
-};
-
-static const struct snd_soc_dapm_route haswell_rt5640_map[] = {
-
- {"Headphones", NULL, "HPOR"},
- {"Headphones", NULL, "HPOL"},
- {"IN2P", NULL, "Mic"},
-
- /* CODEC BE connections */
- {"SSP0 CODEC IN", NULL, "AIF1 Capture"},
- {"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
-};
-
-static int haswell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_hw_params *params)
-{
- struct snd_interval *rate = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_RATE);
- struct snd_interval *channels = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_CHANNELS);
-
- /* The ADSP will covert the FE rate to 48k, stereo */
- rate->min = rate->max = 48000;
- channels->min = channels->max = 2;
-
- /* set SSP0 to 16 bit */
- snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT -
- SNDRV_PCM_HW_PARAM_FIRST_MASK],
- SNDRV_PCM_FORMAT_S16_LE);
- return 0;
-}
-
-static int haswell_rt5640_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
-
- ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_MCLK, 12288000,
- SND_SOC_CLOCK_IN);
-
- if (ret < 0) {
- dev_err(rtd->dev, "can't set codec sysclk configuration\n");
- return ret;
- }
-
- /* set correct codec filter for DAI format and clock config */
- snd_soc_update_bits(rtd->codec, 0x83, 0xffff, 0x8000);
-
- return ret;
-}
-
-static struct snd_soc_ops haswell_rt5640_ops = {
- .hw_params = haswell_rt5640_hw_params,
-};
-
-static int haswell_rtd_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct sst_pdata *pdata = dev_get_platdata(rtd->platform->dev);
- struct sst_hsw *haswell = pdata->dsp;
- int ret;
-
- /* Set ADSP SSP port settings */
- ret = sst_hsw_device_set_config(haswell, SST_HSW_DEVICE_SSP_0,
- SST_HSW_DEVICE_MCLK_FREQ_24_MHZ,
- SST_HSW_DEVICE_CLOCK_MASTER, 9);
- if (ret < 0) {
- dev_err(rtd->dev, "failed to set device config\n");
- return ret;
- }
-
- return 0;
-}
-
-static struct snd_soc_dai_link haswell_rt5640_dais[] = {
- /* Front End DAI links */
- {
- .name = "System",
- .stream_name = "System Playback/Capture",
- .cpu_dai_name = "System Pin",
- .platform_name = "haswell-pcm-audio",
- .dynamic = 1,
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .init = haswell_rtd_init,
- .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
- .dpcm_playback = 1,
- .dpcm_capture = 1,
- },
- {
- .name = "Offload0",
- .stream_name = "Offload0 Playback",
- .cpu_dai_name = "Offload0 Pin",
- .platform_name = "haswell-pcm-audio",
- .dynamic = 1,
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
- .dpcm_playback = 1,
- },
- {
- .name = "Offload1",
- .stream_name = "Offload1 Playback",
- .cpu_dai_name = "Offload1 Pin",
- .platform_name = "haswell-pcm-audio",
- .dynamic = 1,
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
- .dpcm_playback = 1,
- },
- {
- .name = "Loopback",
- .stream_name = "Loopback",
- .cpu_dai_name = "Loopback Pin",
- .platform_name = "haswell-pcm-audio",
- .dynamic = 0,
- .codec_name = "snd-soc-dummy",
- .codec_dai_name = "snd-soc-dummy-dai",
- .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
- .dpcm_capture = 1,
- },
-
- /* Back End DAI links */
- {
- /* SSP0 - Codec */
- .name = "Codec",
- .be_id = 0,
- .cpu_dai_name = "snd-soc-dummy-dai",
- .platform_name = "snd-soc-dummy",
- .no_pcm = 1,
- .codec_name = "i2c-INT33CA:00",
- .codec_dai_name = "rt5640-aif1",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ignore_suspend = 1,
- .ignore_pmdown_time = 1,
- .be_hw_params_fixup = haswell_ssp0_fixup,
- .ops = &haswell_rt5640_ops,
- .dpcm_playback = 1,
- .dpcm_capture = 1,
- },
-};
-
-/* audio machine driver for Haswell Lynxpoint DSP + RT5640 */
-static struct snd_soc_card haswell_rt5640 = {
- .name = "haswell-rt5640",
- .owner = THIS_MODULE,
- .dai_link = haswell_rt5640_dais,
- .num_links = ARRAY_SIZE(haswell_rt5640_dais),
- .dapm_widgets = haswell_widgets,
- .num_dapm_widgets = ARRAY_SIZE(haswell_widgets),
- .dapm_routes = haswell_rt5640_map,
- .num_dapm_routes = ARRAY_SIZE(haswell_rt5640_map),
- .fully_routed = true,
-};
-
-static int haswell_audio_probe(struct platform_device *pdev)
-{
- haswell_rt5640.dev = &pdev->dev;
-
- return devm_snd_soc_register_card(&pdev->dev, &haswell_rt5640);
-}
-
-static struct platform_driver haswell_audio = {
- .probe = haswell_audio_probe,
- .driver = {
- .name = "haswell-audio",
- },
-};
-
-module_platform_driver(haswell_audio)
-
-/* Module information */
-MODULE_AUTHOR("Liam Girdwood, Xingchao Wang");
-MODULE_DESCRIPTION("Intel SST Audio for Haswell Lynxpoint");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:haswell-audio");
--- /dev/null
+snd-soc-sst-haswell-pcm-objs := \
+ sst-haswell-ipc.o sst-haswell-pcm.o sst-haswell-dsp.o
+
+obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += snd-soc-sst-haswell-pcm.o
--- /dev/null
+/*
+ * Intel Haswell SST DSP driver
+ *
+ * Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/export.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/pci.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+
+#include "../common/sst-dsp.h"
+#include "../common/sst-dsp-priv.h"
+#include "../haswell/sst-haswell-ipc.h"
+
+#include <trace/events/hswadsp.h>
+
+#define SST_HSW_FW_SIGNATURE_SIZE 4
+#define SST_HSW_FW_SIGN "$SST"
+#define SST_HSW_FW_LIB_SIGN "$LIB"
+
+#define SST_WPT_SHIM_OFFSET 0xFB000
+#define SST_LP_SHIM_OFFSET 0xE7000
+#define SST_WPT_IRAM_OFFSET 0xA0000
+#define SST_LP_IRAM_OFFSET 0x80000
+#define SST_WPT_DSP_DRAM_OFFSET 0x400000
+#define SST_WPT_DSP_IRAM_OFFSET 0x00000
+#define SST_LPT_DSP_DRAM_OFFSET 0x400000
+#define SST_LPT_DSP_IRAM_OFFSET 0x00000
+
+#define SST_SHIM_PM_REG 0x84
+
+#define SST_HSW_IRAM 1
+#define SST_HSW_DRAM 2
+#define SST_HSW_REGS 3
+
+struct dma_block_info {
+ __le32 type; /* IRAM/DRAM */
+ __le32 size; /* Bytes */
+ __le32 ram_offset; /* Offset in I/DRAM */
+ __le32 rsvd; /* Reserved field */
+} __attribute__((packed));
+
+struct fw_module_info {
+ __le32 persistent_size;
+ __le32 scratch_size;
+} __attribute__((packed));
+
+struct fw_header {
+ unsigned char signature[SST_HSW_FW_SIGNATURE_SIZE]; /* FW signature */
+ __le32 file_size; /* size of fw minus this header */
+ __le32 modules; /* # of modules */
+ __le32 file_format; /* version of header format */
+ __le32 reserved[4];
+} __attribute__((packed));
+
+struct fw_module_header {
+ unsigned char signature[SST_HSW_FW_SIGNATURE_SIZE]; /* module signature */
+ __le32 mod_size; /* size of module */
+ __le32 blocks; /* # of blocks */
+ __le16 padding;
+ __le16 type; /* codec type, pp lib */
+ __le32 entry_point;
+ struct fw_module_info info;
+} __attribute__((packed));
+
+static void hsw_free(struct sst_dsp *sst);
+
+static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
+ struct fw_module_header *module)
+{
+ struct dma_block_info *block;
+ struct sst_module *mod;
+ struct sst_module_template template;
+ int count, ret;
+ void __iomem *ram;
+
+ /* TODO: allowed module types need to be configurable */
+ if (module->type != SST_HSW_MODULE_BASE_FW
+ && module->type != SST_HSW_MODULE_PCM_SYSTEM
+ && module->type != SST_HSW_MODULE_PCM
+ && module->type != SST_HSW_MODULE_PCM_REFERENCE
+ && module->type != SST_HSW_MODULE_PCM_CAPTURE
+ && module->type != SST_HSW_MODULE_WAVES
+ && module->type != SST_HSW_MODULE_LPAL)
+ return 0;
+
+ dev_dbg(dsp->dev, "new module sign 0x%s size 0x%x blocks 0x%x type 0x%x\n",
+ module->signature, module->mod_size,
+ module->blocks, module->type);
+ dev_dbg(dsp->dev, " entrypoint 0x%x\n", module->entry_point);
+ dev_dbg(dsp->dev, " persistent 0x%x scratch 0x%x\n",
+ module->info.persistent_size, module->info.scratch_size);
+
+ memset(&template, 0, sizeof(template));
+ template.id = module->type;
+ template.entry = module->entry_point - 4;
+ template.persistent_size = module->info.persistent_size;
+ template.scratch_size = module->info.scratch_size;
+
+ mod = sst_module_new(fw, &template, NULL);
+ if (mod == NULL)
+ return -ENOMEM;
+
+ block = (void *)module + sizeof(*module);
+
+ for (count = 0; count < module->blocks; count++) {
+
+ if (block->size <= 0) {
+ dev_err(dsp->dev,
+ "error: block %d size invalid\n", count);
+ sst_module_free(mod);
+ return -EINVAL;
+ }
+
+ switch (block->type) {
+ case SST_HSW_IRAM:
+ ram = dsp->addr.lpe;
+ mod->offset =
+ block->ram_offset + dsp->addr.iram_offset;
+ mod->type = SST_MEM_IRAM;
+ break;
+ case SST_HSW_DRAM:
+ case SST_HSW_REGS:
+ ram = dsp->addr.lpe;
+ mod->offset = block->ram_offset;
+ mod->type = SST_MEM_DRAM;
+ break;
+ default:
+ dev_err(dsp->dev, "error: bad type 0x%x for block 0x%x\n",
+ block->type, count);
+ sst_module_free(mod);
+ return -EINVAL;
+ }
+
+ mod->size = block->size;
+ mod->data = (void *)block + sizeof(*block);
+ mod->data_offset = mod->data - fw->dma_buf;
+
+ dev_dbg(dsp->dev, "module block %d type 0x%x "
+ "size 0x%x ==> ram %p offset 0x%x\n",
+ count, mod->type, block->size, ram,
+ block->ram_offset);
+
+ ret = sst_module_alloc_blocks(mod);
+ if (ret < 0) {
+ dev_err(dsp->dev, "error: could not allocate blocks for module %d\n",
+ count);
+ sst_module_free(mod);
+ return ret;
+ }
+
+ block = (void *)block + sizeof(*block) + block->size;
+ }
+ mod->state = SST_MODULE_STATE_LOADED;
+
+ return 0;
+}
+
+static int hsw_parse_fw_image(struct sst_fw *sst_fw)
+{
+ struct fw_header *header;
+ struct fw_module_header *module;
+ struct sst_dsp *dsp = sst_fw->dsp;
+ int ret, count;
+
+ /* Read the header information from the data pointer */
+ header = (struct fw_header *)sst_fw->dma_buf;
+
+ /* verify FW */
+ if ((strncmp(header->signature, SST_HSW_FW_SIGN, 4) != 0) ||
+ (sst_fw->size != header->file_size + sizeof(*header))) {
+ dev_err(dsp->dev, "error: invalid fw sign/filesize mismatch\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dsp->dev, "header size=0x%x modules=0x%x fmt=0x%x size=%zu\n",
+ header->file_size, header->modules,
+ header->file_format, sizeof(*header));
+
+ /* parse each module */
+ module = (void *)sst_fw->dma_buf + sizeof(*header);
+ for (count = 0; count < header->modules; count++) {
+
+ /* module */
+ ret = hsw_parse_module(dsp, sst_fw, module);
+ if (ret < 0) {
+ dev_err(dsp->dev, "error: invalid module %d\n", count);
+ return ret;
+ }
+ module = (void *)module + sizeof(*module) + module->mod_size;
+ }
+
+ return 0;
+}
+
+static irqreturn_t hsw_irq(int irq, void *context)
+{
+ struct sst_dsp *sst = (struct sst_dsp *) context;
+ u32 isr;
+ int ret = IRQ_NONE;
+
+ spin_lock(&sst->spinlock);
+
+ /* Interrupt arrived, check src */
+ isr = sst_dsp_shim_read_unlocked(sst, SST_ISRX);
+ if (isr & SST_ISRX_DONE) {
+ trace_sst_irq_done(isr,
+ sst_dsp_shim_read_unlocked(sst, SST_IMRX));
+
+ /* Mask Done interrupt before return */
+ sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
+ SST_IMRX_DONE, SST_IMRX_DONE);
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ if (isr & SST_ISRX_BUSY) {
+ trace_sst_irq_busy(isr,
+ sst_dsp_shim_read_unlocked(sst, SST_IMRX));
+
+ /* Mask Busy interrupt before return */
+ sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
+ SST_IMRX_BUSY, SST_IMRX_BUSY);
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ spin_unlock(&sst->spinlock);
+ return ret;
+}
+
+static void hsw_set_dsp_D3(struct sst_dsp *sst)
+{
+ u32 val;
+ u32 reg;
+
+ /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+ reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+ reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE);
+ writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+ /* enable power gating and switch off DRAM & IRAM blocks */
+ val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+ val |= SST_VDRTCL0_DSRAMPGE_MASK |
+ SST_VDRTCL0_ISRAMPGE_MASK;
+ val &= ~(SST_VDRTCL0_D3PGD | SST_VDRTCL0_D3SRAMPGD);
+ writel(val, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+ /* switch off audio PLL */
+ val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+ val |= SST_VDRTCL2_APLLSE_MASK;
+ writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+ /* disable MCLK(clkctl.smos = 0) */
+ sst_dsp_shim_update_bits_unlocked(sst, SST_CLKCTL,
+ SST_CLKCTL_MASK, 0);
+
+ /* Set D3 state, delay 50 us */
+ val = readl(sst->addr.pci_cfg + SST_PMCS);
+ val |= SST_PMCS_PS_MASK;
+ writel(val, sst->addr.pci_cfg + SST_PMCS);
+ udelay(50);
+
+ /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+ reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+ reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE;
+ writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+ udelay(50);
+
+}
+
+static void hsw_reset(struct sst_dsp *sst)
+{
+ /* put DSP into reset and stall */
+ sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
+ SST_CSR_RST | SST_CSR_STALL,
+ SST_CSR_RST | SST_CSR_STALL);
+
+ /* keep in reset for 10ms */
+ mdelay(10);
+
+ /* take DSP out of reset and keep stalled for FW loading */
+ sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
+ SST_CSR_RST | SST_CSR_STALL, SST_CSR_STALL);
+}
+
+static int hsw_set_dsp_D0(struct sst_dsp *sst)
+{
+ int tries = 10;
+ u32 reg, fw_dump_bit;
+
+ /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+ reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+ reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE);
+ writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+ /* Disable D3PG (VDRTCTL0.D3PGD = 1) */
+ reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+ reg |= SST_VDRTCL0_D3PGD;
+ writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+ /* Set D0 state */
+ reg = readl(sst->addr.pci_cfg + SST_PMCS);
+ reg &= ~SST_PMCS_PS_MASK;
+ writel(reg, sst->addr.pci_cfg + SST_PMCS);
+
+ /* check that ADSP shim is enabled */
+ while (tries--) {
+ reg = readl(sst->addr.pci_cfg + SST_PMCS) & SST_PMCS_PS_MASK;
+ if (reg == 0)
+ goto finish;
+
+ msleep(1);
+ }
+
+ return -ENODEV;
+
+finish:
+ /* select SSP1 19.2MHz base clock, SSP clock 0, turn off Low Power Clock */
+ sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
+ SST_CSR_S1IOCS | SST_CSR_SBCS1 | SST_CSR_LPCS, 0x0);
+
+ /* stall DSP core, set clk to 192/96Mhz */
+ sst_dsp_shim_update_bits_unlocked(sst,
+ SST_CSR, SST_CSR_STALL | SST_CSR_DCS_MASK,
+ SST_CSR_STALL | SST_CSR_DCS(4));
+
+ /* Set 24MHz MCLK, prevent local clock gating, enable SSP0 clock */
+ sst_dsp_shim_update_bits_unlocked(sst, SST_CLKCTL,
+ SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0,
+ SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0);
+
+ /* Stall and reset core, set CSR */
+ hsw_reset(sst);
+
+ /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+ reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+ reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE;
+ writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+ udelay(50);
+
+ /* switch on audio PLL */
+ reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+ reg &= ~SST_VDRTCL2_APLLSE_MASK;
+ writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+ /* set default power gating control, enable power gating control for all blocks. that is,
+ can't be accessed, please enable each block before accessing. */
+ reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+ reg |= SST_VDRTCL0_DSRAMPGE_MASK | SST_VDRTCL0_ISRAMPGE_MASK;
+ /* for D0, always enable the block(DSRAM[0]) used for FW dump */
+ fw_dump_bit = 1 << SST_VDRTCL0_DSRAMPGE_SHIFT;
+ writel(reg & ~fw_dump_bit, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+
+ /* disable DMA finish function for SSP0 & SSP1 */
+ sst_dsp_shim_update_bits_unlocked(sst, SST_CSR2, SST_CSR2_SDFD_SSP1,
+ SST_CSR2_SDFD_SSP1);
+
+ /* set on-demond mode on engine 0,1 for all channels */
+ sst_dsp_shim_update_bits(sst, SST_HMDC,
+ SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH,
+ SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH);
+
+ /* Enable Interrupt from both sides */
+ sst_dsp_shim_update_bits(sst, SST_IMRX, (SST_IMRX_BUSY | SST_IMRX_DONE),
+ 0x0);
+ sst_dsp_shim_update_bits(sst, SST_IMRD, (SST_IMRD_DONE | SST_IMRD_BUSY |
+ SST_IMRD_SSP0 | SST_IMRD_DMAC), 0x0);
+
+ /* clear IPC registers */
+ sst_dsp_shim_write(sst, SST_IPCX, 0x0);
+ sst_dsp_shim_write(sst, SST_IPCD, 0x0);
+ sst_dsp_shim_write(sst, 0x80, 0x6);
+ sst_dsp_shim_write(sst, 0xe0, 0x300a);
+
+ return 0;
+}
+
+static void hsw_boot(struct sst_dsp *sst)
+{
+ /* set oportunistic mode on engine 0,1 for all channels */
+ sst_dsp_shim_update_bits(sst, SST_HMDC,
+ SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH, 0);
+
+ /* set DSP to RUN */
+ sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, SST_CSR_STALL, 0x0);
+}
+
+static void hsw_stall(struct sst_dsp *sst)
+{
+ /* stall DSP */
+ sst_dsp_shim_update_bits(sst, SST_CSR,
+ SST_CSR_24MHZ_LPCS | SST_CSR_STALL,
+ SST_CSR_STALL | SST_CSR_24MHZ_LPCS);
+}
+
+static void hsw_sleep(struct sst_dsp *sst)
+{
+ dev_dbg(sst->dev, "HSW_PM dsp runtime suspend\n");
+
+ /* put DSP into reset and stall */
+ sst_dsp_shim_update_bits(sst, SST_CSR,
+ SST_CSR_24MHZ_LPCS | SST_CSR_RST | SST_CSR_STALL,
+ SST_CSR_RST | SST_CSR_STALL | SST_CSR_24MHZ_LPCS);
+
+ hsw_set_dsp_D3(sst);
+ dev_dbg(sst->dev, "HSW_PM dsp runtime suspend exit\n");
+}
+
+static int hsw_wake(struct sst_dsp *sst)
+{
+ int ret;
+
+ dev_dbg(sst->dev, "HSW_PM dsp runtime resume\n");
+
+ ret = hsw_set_dsp_D0(sst);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(sst->dev, "HSW_PM dsp runtime resume exit\n");
+
+ return 0;
+}
+
+struct sst_adsp_memregion {
+ u32 start;
+ u32 end;
+ int blocks;
+ enum sst_mem_type type;
+};
+
+/* lynx point ADSP mem regions */
+static const struct sst_adsp_memregion lp_region[] = {
+ {0x00000, 0x40000, 8, SST_MEM_DRAM}, /* D-SRAM0 - 8 * 32kB */
+ {0x40000, 0x80000, 8, SST_MEM_DRAM}, /* D-SRAM1 - 8 * 32kB */
+ {0x80000, 0xE0000, 12, SST_MEM_IRAM}, /* I-SRAM - 12 * 32kB */
+};
+
+/* wild cat point ADSP mem regions */
+static const struct sst_adsp_memregion wpt_region[] = {
+ {0x00000, 0xA0000, 20, SST_MEM_DRAM}, /* D-SRAM0,D-SRAM1,D-SRAM2 - 20 * 32kB */
+ {0xA0000, 0xF0000, 10, SST_MEM_IRAM}, /* I-SRAM - 10 * 32kB */
+};
+
+static int hsw_acpi_resource_map(struct sst_dsp *sst, struct sst_pdata *pdata)
+{
+ /* ADSP DRAM & IRAM */
+ sst->addr.lpe_base = pdata->lpe_base;
+ sst->addr.lpe = ioremap(pdata->lpe_base, pdata->lpe_size);
+ if (!sst->addr.lpe)
+ return -ENODEV;
+
+ /* ADSP PCI MMIO config space */
+ sst->addr.pci_cfg = ioremap(pdata->pcicfg_base, pdata->pcicfg_size);
+ if (!sst->addr.pci_cfg) {
+ iounmap(sst->addr.lpe);
+ return -ENODEV;
+ }
+
+ /* SST Shim */
+ sst->addr.shim = sst->addr.lpe + sst->addr.shim_offset;
+ return 0;
+}
+
+struct sst_sram_shift {
+ u32 dev_id; /* SST Device IDs */
+ u32 iram_shift;
+ u32 dram_shift;
+};
+
+static const struct sst_sram_shift sram_shift[] = {
+ {SST_DEV_ID_LYNX_POINT, 6, 16}, /* lp */
+ {SST_DEV_ID_WILDCAT_POINT, 2, 12}, /* wpt */
+};
+
+static u32 hsw_block_get_bit(struct sst_mem_block *block)
+{
+ u32 bit = 0, shift = 0, index;
+ struct sst_dsp *sst = block->dsp;
+
+ for (index = 0; index < ARRAY_SIZE(sram_shift); index++) {
+ if (sram_shift[index].dev_id == sst->id)
+ break;
+ }
+
+ if (index < ARRAY_SIZE(sram_shift)) {
+ switch (block->type) {
+ case SST_MEM_DRAM:
+ shift = sram_shift[index].dram_shift;
+ break;
+ case SST_MEM_IRAM:
+ shift = sram_shift[index].iram_shift;
+ break;
+ default:
+ shift = 0;
+ }
+ } else
+ shift = 0;
+
+ bit = 1 << (block->index + shift);
+
+ return bit;
+}
+
+/*dummy read a SRAM block.*/
+static void sst_mem_block_dummy_read(struct sst_mem_block *block)
+{
+ u32 size;
+ u8 tmp_buf[4];
+ struct sst_dsp *sst = block->dsp;
+
+ size = block->size > 4 ? 4 : block->size;
+ memcpy_fromio(tmp_buf, sst->addr.lpe + block->offset, size);
+}
+
+/* enable 32kB memory block - locks held by caller */
+static int hsw_block_enable(struct sst_mem_block *block)
+{
+ struct sst_dsp *sst = block->dsp;
+ u32 bit, val;
+
+ if (block->users++ > 0)
+ return 0;
+
+ dev_dbg(block->dsp->dev, " enabled block %d:%d at offset 0x%x\n",
+ block->type, block->index, block->offset);
+
+ /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+ val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+ val &= ~SST_VDRTCL2_DCLCGE;
+ writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+ val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+ bit = hsw_block_get_bit(block);
+ writel(val & ~bit, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+ /* wait 18 DSP clock ticks */
+ udelay(10);
+
+ /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+ val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+ val |= SST_VDRTCL2_DCLCGE;
+ writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+ udelay(50);
+
+ /*add a dummy read before the SRAM block is written, otherwise the writing may miss bytes sometimes.*/
+ sst_mem_block_dummy_read(block);
+ return 0;
+}
+
+/* disable 32kB memory block - locks held by caller */
+static int hsw_block_disable(struct sst_mem_block *block)
+{
+ struct sst_dsp *sst = block->dsp;
+ u32 bit, val;
+
+ if (--block->users > 0)
+ return 0;
+
+ dev_dbg(block->dsp->dev, " disabled block %d:%d at offset 0x%x\n",
+ block->type, block->index, block->offset);
+
+ /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+ val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+ val &= ~SST_VDRTCL2_DCLCGE;
+ writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+
+ val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+ bit = hsw_block_get_bit(block);
+ /* don't disable DSRAM[0], keep it always enable for FW dump*/
+ if (bit != (1 << SST_VDRTCL0_DSRAMPGE_SHIFT))
+ writel(val | bit, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+ /* wait 18 DSP clock ticks */
+ udelay(10);
+
+ /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+ val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+ val |= SST_VDRTCL2_DCLCGE;
+ writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+ udelay(50);
+
+ return 0;
+}
+
+static struct sst_block_ops sst_hsw_ops = {
+ .enable = hsw_block_enable,
+ .disable = hsw_block_disable,
+};
+
+static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
+{
+ const struct sst_adsp_memregion *region;
+ struct device *dev;
+ int ret = -ENODEV, i, j, region_count;
+ u32 offset, size, fw_dump_bit;
+
+ dev = sst->dma_dev;
+
+ switch (sst->id) {
+ case SST_DEV_ID_LYNX_POINT:
+ region = lp_region;
+ region_count = ARRAY_SIZE(lp_region);
+ sst->addr.iram_offset = SST_LP_IRAM_OFFSET;
+ sst->addr.dsp_iram_offset = SST_LPT_DSP_IRAM_OFFSET;
+ sst->addr.dsp_dram_offset = SST_LPT_DSP_DRAM_OFFSET;
+ sst->addr.shim_offset = SST_LP_SHIM_OFFSET;
+ break;
+ case SST_DEV_ID_WILDCAT_POINT:
+ region = wpt_region;
+ region_count = ARRAY_SIZE(wpt_region);
+ sst->addr.iram_offset = SST_WPT_IRAM_OFFSET;
+ sst->addr.dsp_iram_offset = SST_WPT_DSP_IRAM_OFFSET;
+ sst->addr.dsp_dram_offset = SST_WPT_DSP_DRAM_OFFSET;
+ sst->addr.shim_offset = SST_WPT_SHIM_OFFSET;
+ break;
+ default:
+ dev_err(dev, "error: failed to get mem resources\n");
+ return ret;
+ }
+
+ ret = hsw_acpi_resource_map(sst, pdata);
+ if (ret < 0) {
+ dev_err(dev, "error: failed to map resources\n");
+ return ret;
+ }
+
+ /* enable the DSP SHIM */
+ ret = hsw_set_dsp_D0(sst);
+ if (ret < 0) {
+ dev_err(dev, "error: failed to set DSP D0 and reset SHIM\n");
+ return ret;
+ }
+
+ ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(31));
+ if (ret)
+ return ret;
+
+
+ /* register DSP memory blocks - ideally we should get this from ACPI */
+ for (i = 0; i < region_count; i++) {
+ offset = region[i].start;
+ size = (region[i].end - region[i].start) / region[i].blocks;
+
+ /* register individual memory blocks */
+ for (j = 0; j < region[i].blocks; j++) {
+ sst_mem_block_register(sst, offset, size,
+ region[i].type, &sst_hsw_ops, j, sst);
+ offset += size;
+ }
+ }
+
+ /* always enable the block(DSRAM[0]) used for FW dump */
+ fw_dump_bit = 1 << SST_VDRTCL0_DSRAMPGE_SHIFT;
+ /* set default power gating control, enable power gating control for all blocks. that is,
+ can't be accessed, please enable each block before accessing. */
+ writel(0xffffffff & ~fw_dump_bit, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+ return 0;
+}
+
+static void hsw_free(struct sst_dsp *sst)
+{
+ sst_mem_block_unregister_all(sst);
+ iounmap(sst->addr.lpe);
+ iounmap(sst->addr.pci_cfg);
+}
+
+struct sst_ops haswell_ops = {
+ .reset = hsw_reset,
+ .boot = hsw_boot,
+ .stall = hsw_stall,
+ .wake = hsw_wake,
+ .sleep = hsw_sleep,
+ .write = sst_shim32_write,
+ .read = sst_shim32_read,
+ .write64 = sst_shim32_write64,
+ .read64 = sst_shim32_read64,
+ .ram_read = sst_memcpy_fromio_32,
+ .ram_write = sst_memcpy_toio_32,
+ .irq_handler = hsw_irq,
+ .init = hsw_init,
+ .free = hsw_free,
+ .parse_fw = hsw_parse_fw_image,
+};
--- /dev/null
+/*
+ * Intel SST Haswell/Broadwell IPC Support
+ *
+ * Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/platform_device.h>
+#include <linux/kthread.h>
+#include <linux/firmware.h>
+#include <linux/dma-mapping.h>
+#include <linux/debugfs.h>
+#include <linux/pm_runtime.h>
+#include <sound/asound.h>
+
+#include "sst-haswell-ipc.h"
+#include "../common/sst-dsp.h"
+#include "../common/sst-dsp-priv.h"
+#include "../common/sst-ipc.h"
+
+/* Global Message - Generic */
+#define IPC_GLB_TYPE_SHIFT 24
+#define IPC_GLB_TYPE_MASK (0x1f << IPC_GLB_TYPE_SHIFT)
+#define IPC_GLB_TYPE(x) (x << IPC_GLB_TYPE_SHIFT)
+
+/* Global Message - Reply */
+#define IPC_GLB_REPLY_SHIFT 0
+#define IPC_GLB_REPLY_MASK (0x1f << IPC_GLB_REPLY_SHIFT)
+#define IPC_GLB_REPLY_TYPE(x) (x << IPC_GLB_REPLY_TYPE_SHIFT)
+
+/* Stream Message - Generic */
+#define IPC_STR_TYPE_SHIFT 20
+#define IPC_STR_TYPE_MASK (0xf << IPC_STR_TYPE_SHIFT)
+#define IPC_STR_TYPE(x) (x << IPC_STR_TYPE_SHIFT)
+#define IPC_STR_ID_SHIFT 16
+#define IPC_STR_ID_MASK (0xf << IPC_STR_ID_SHIFT)
+#define IPC_STR_ID(x) (x << IPC_STR_ID_SHIFT)
+
+/* Stream Message - Reply */
+#define IPC_STR_REPLY_SHIFT 0
+#define IPC_STR_REPLY_MASK (0x1f << IPC_STR_REPLY_SHIFT)
+
+/* Stream Stage Message - Generic */
+#define IPC_STG_TYPE_SHIFT 12
+#define IPC_STG_TYPE_MASK (0xf << IPC_STG_TYPE_SHIFT)
+#define IPC_STG_TYPE(x) (x << IPC_STG_TYPE_SHIFT)
+#define IPC_STG_ID_SHIFT 10
+#define IPC_STG_ID_MASK (0x3 << IPC_STG_ID_SHIFT)
+#define IPC_STG_ID(x) (x << IPC_STG_ID_SHIFT)
+
+/* Stream Stage Message - Reply */
+#define IPC_STG_REPLY_SHIFT 0
+#define IPC_STG_REPLY_MASK (0x1f << IPC_STG_REPLY_SHIFT)
+
+/* Debug Log Message - Generic */
+#define IPC_LOG_OP_SHIFT 20
+#define IPC_LOG_OP_MASK (0xf << IPC_LOG_OP_SHIFT)
+#define IPC_LOG_OP_TYPE(x) (x << IPC_LOG_OP_SHIFT)
+#define IPC_LOG_ID_SHIFT 16
+#define IPC_LOG_ID_MASK (0xf << IPC_LOG_ID_SHIFT)
+#define IPC_LOG_ID(x) (x << IPC_LOG_ID_SHIFT)
+
+/* Module Message */
+#define IPC_MODULE_OPERATION_SHIFT 20
+#define IPC_MODULE_OPERATION_MASK (0xf << IPC_MODULE_OPERATION_SHIFT)
+#define IPC_MODULE_OPERATION(x) (x << IPC_MODULE_OPERATION_SHIFT)
+
+#define IPC_MODULE_ID_SHIFT 16
+#define IPC_MODULE_ID_MASK (0xf << IPC_MODULE_ID_SHIFT)
+#define IPC_MODULE_ID(x) (x << IPC_MODULE_ID_SHIFT)
+
+/* IPC message timeout (msecs) */
+#define IPC_TIMEOUT_MSECS 300
+#define IPC_BOOT_MSECS 200
+#define IPC_MSG_WAIT 0
+#define IPC_MSG_NOWAIT 1
+
+/* Firmware Ready Message */
+#define IPC_FW_READY (0x1 << 29)
+#define IPC_STATUS_MASK (0x3 << 30)
+
+#define IPC_EMPTY_LIST_SIZE 8
+#define IPC_MAX_STREAMS 4
+
+/* Mailbox */
+#define IPC_MAX_MAILBOX_BYTES 256
+
+#define INVALID_STREAM_HW_ID 0xffffffff
+
+/* Global Message - Types and Replies */
+enum ipc_glb_type {
+ IPC_GLB_GET_FW_VERSION = 0, /* Retrieves firmware version */
+ IPC_GLB_PERFORMANCE_MONITOR = 1, /* Performance monitoring actions */
+ IPC_GLB_ALLOCATE_STREAM = 3, /* Request to allocate new stream */
+ IPC_GLB_FREE_STREAM = 4, /* Request to free stream */
+ IPC_GLB_GET_FW_CAPABILITIES = 5, /* Retrieves firmware capabilities */
+ IPC_GLB_STREAM_MESSAGE = 6, /* Message directed to stream or its stages */
+ /* Request to store firmware context during D0->D3 transition */
+ IPC_GLB_REQUEST_DUMP = 7,
+ /* Request to restore firmware context during D3->D0 transition */
+ IPC_GLB_RESTORE_CONTEXT = 8,
+ IPC_GLB_GET_DEVICE_FORMATS = 9, /* Set device format */
+ IPC_GLB_SET_DEVICE_FORMATS = 10, /* Get device format */
+ IPC_GLB_SHORT_REPLY = 11,
+ IPC_GLB_ENTER_DX_STATE = 12,
+ IPC_GLB_GET_MIXER_STREAM_INFO = 13, /* Request mixer stream params */
+ IPC_GLB_DEBUG_LOG_MESSAGE = 14, /* Message to or from the debug logger. */
+ IPC_GLB_MODULE_OPERATION = 15, /* Message to loadable fw module */
+ IPC_GLB_REQUEST_TRANSFER = 16, /* < Request Transfer for host */
+ IPC_GLB_MAX_IPC_MESSAGE_TYPE = 17, /* Maximum message number */
+};
+
+enum ipc_glb_reply {
+ IPC_GLB_REPLY_SUCCESS = 0, /* The operation was successful. */
+ IPC_GLB_REPLY_ERROR_INVALID_PARAM = 1, /* Invalid parameter was passed. */
+ IPC_GLB_REPLY_UNKNOWN_MESSAGE_TYPE = 2, /* Uknown message type was resceived. */
+ IPC_GLB_REPLY_OUT_OF_RESOURCES = 3, /* No resources to satisfy the request. */
+ IPC_GLB_REPLY_BUSY = 4, /* The system or resource is busy. */
+ IPC_GLB_REPLY_PENDING = 5, /* The action was scheduled for processing. */
+ IPC_GLB_REPLY_FAILURE = 6, /* Critical error happened. */
+ IPC_GLB_REPLY_INVALID_REQUEST = 7, /* Request can not be completed. */
+ IPC_GLB_REPLY_STAGE_UNINITIALIZED = 8, /* Processing stage was uninitialized. */
+ IPC_GLB_REPLY_NOT_FOUND = 9, /* Required resource can not be found. */
+ IPC_GLB_REPLY_SOURCE_NOT_STARTED = 10, /* Source was not started. */
+};
+
+enum ipc_module_operation {
+ IPC_MODULE_NOTIFICATION = 0,
+ IPC_MODULE_ENABLE = 1,
+ IPC_MODULE_DISABLE = 2,
+ IPC_MODULE_GET_PARAMETER = 3,
+ IPC_MODULE_SET_PARAMETER = 4,
+ IPC_MODULE_GET_INFO = 5,
+ IPC_MODULE_MAX_MESSAGE
+};
+
+/* Stream Message - Types */
+enum ipc_str_operation {
+ IPC_STR_RESET = 0,
+ IPC_STR_PAUSE = 1,
+ IPC_STR_RESUME = 2,
+ IPC_STR_STAGE_MESSAGE = 3,
+ IPC_STR_NOTIFICATION = 4,
+ IPC_STR_MAX_MESSAGE
+};
+
+/* Stream Stage Message Types */
+enum ipc_stg_operation {
+ IPC_STG_GET_VOLUME = 0,
+ IPC_STG_SET_VOLUME,
+ IPC_STG_SET_WRITE_POSITION,
+ IPC_STG_SET_FX_ENABLE,
+ IPC_STG_SET_FX_DISABLE,
+ IPC_STG_SET_FX_GET_PARAM,
+ IPC_STG_SET_FX_SET_PARAM,
+ IPC_STG_SET_FX_GET_INFO,
+ IPC_STG_MUTE_LOOPBACK,
+ IPC_STG_MAX_MESSAGE
+};
+
+/* Stream Stage Message Types For Notification*/
+enum ipc_stg_operation_notify {
+ IPC_POSITION_CHANGED = 0,
+ IPC_STG_GLITCH,
+ IPC_STG_MAX_NOTIFY
+};
+
+enum ipc_glitch_type {
+ IPC_GLITCH_UNDERRUN = 1,
+ IPC_GLITCH_DECODER_ERROR,
+ IPC_GLITCH_DOUBLED_WRITE_POS,
+ IPC_GLITCH_MAX
+};
+
+/* Debug Control */
+enum ipc_debug_operation {
+ IPC_DEBUG_ENABLE_LOG = 0,
+ IPC_DEBUG_DISABLE_LOG = 1,
+ IPC_DEBUG_REQUEST_LOG_DUMP = 2,
+ IPC_DEBUG_NOTIFY_LOG_DUMP = 3,
+ IPC_DEBUG_MAX_DEBUG_LOG
+};
+
+/* Firmware Ready */
+struct sst_hsw_ipc_fw_ready {
+ u32 inbox_offset;
+ u32 outbox_offset;
+ u32 inbox_size;
+ u32 outbox_size;
+ u32 fw_info_size;
+ u8 fw_info[IPC_MAX_MAILBOX_BYTES - 5 * sizeof(u32)];
+} __attribute__((packed));
+
+struct sst_hsw_stream;
+struct sst_hsw;
+
+/* Stream infomation */
+struct sst_hsw_stream {
+ /* configuration */
+ struct sst_hsw_ipc_stream_alloc_req request;
+ struct sst_hsw_ipc_stream_alloc_reply reply;
+ struct sst_hsw_ipc_stream_free_req free_req;
+
+ /* Mixer info */
+ u32 mute_volume[SST_HSW_NO_CHANNELS];
+ u32 mute[SST_HSW_NO_CHANNELS];
+
+ /* runtime info */
+ struct sst_hsw *hsw;
+ int host_id;
+ bool commited;
+ bool running;
+
+ /* Notification work */
+ struct work_struct notify_work;
+ u32 header;
+
+ /* Position info from DSP */
+ struct sst_hsw_ipc_stream_set_position wpos;
+ struct sst_hsw_ipc_stream_get_position rpos;
+ struct sst_hsw_ipc_stream_glitch_position glitch;
+
+ /* Volume info */
+ struct sst_hsw_ipc_volume_req vol_req;
+
+ /* driver callback */
+ u32 (*notify_position)(struct sst_hsw_stream *stream, void *data);
+ void *pdata;
+
+ /* record the fw read position when playback */
+ snd_pcm_uframes_t old_position;
+ bool play_silence;
+ struct list_head node;
+};
+
+/* FW log ring information */
+struct sst_hsw_log_stream {
+ dma_addr_t dma_addr;
+ unsigned char *dma_area;
+ unsigned char *ring_descr;
+ int pages;
+ int size;
+
+ /* Notification work */
+ struct work_struct notify_work;
+ wait_queue_head_t readers_wait_q;
+ struct mutex rw_mutex;
+
+ u32 last_pos;
+ u32 curr_pos;
+ u32 reader_pos;
+
+ /* fw log config */
+ u32 config[SST_HSW_FW_LOG_CONFIG_DWORDS];
+
+ struct sst_hsw *hsw;
+};
+
+/* SST Haswell IPC data */
+struct sst_hsw {
+ struct device *dev;
+ struct sst_dsp *dsp;
+ struct platform_device *pdev_pcm;
+
+ /* FW config */
+ struct sst_hsw_ipc_fw_ready fw_ready;
+ struct sst_hsw_ipc_fw_version version;
+ bool fw_done;
+ struct sst_fw *sst_fw;
+
+ /* stream */
+ struct list_head stream_list;
+
+ /* global mixer */
+ struct sst_hsw_ipc_stream_info_reply mixer_info;
+ enum sst_hsw_volume_curve curve_type;
+ u32 curve_duration;
+ u32 mute[SST_HSW_NO_CHANNELS];
+ u32 mute_volume[SST_HSW_NO_CHANNELS];
+
+ /* DX */
+ struct sst_hsw_ipc_dx_reply dx;
+ void *dx_context;
+ dma_addr_t dx_context_paddr;
+
+ /* boot */
+ wait_queue_head_t boot_wait;
+ bool boot_complete;
+ bool shutdown;
+
+ /* IPC messaging */
+ struct sst_generic_ipc ipc;
+
+ /* FW log stream */
+ struct sst_hsw_log_stream log_stream;
+
+ /* flags bit field to track module state when resume from RTD3,
+ * each bit represent state (enabled/disabled) of single module */
+ u32 enabled_modules_rtd3;
+
+ /* buffer to store parameter lines */
+ u32 param_idx_w; /* write index */
+ u32 param_idx_r; /* read index */
+ u8 param_buf[WAVES_PARAM_LINES][WAVES_PARAM_COUNT];
+};
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/hswadsp.h>
+
+static inline u32 msg_get_global_type(u32 msg)
+{
+ return (msg & IPC_GLB_TYPE_MASK) >> IPC_GLB_TYPE_SHIFT;
+}
+
+static inline u32 msg_get_global_reply(u32 msg)
+{
+ return (msg & IPC_GLB_REPLY_MASK) >> IPC_GLB_REPLY_SHIFT;
+}
+
+static inline u32 msg_get_stream_type(u32 msg)
+{
+ return (msg & IPC_STR_TYPE_MASK) >> IPC_STR_TYPE_SHIFT;
+}
+
+static inline u32 msg_get_stage_type(u32 msg)
+{
+ return (msg & IPC_STG_TYPE_MASK) >> IPC_STG_TYPE_SHIFT;
+}
+
+static inline u32 msg_get_stream_id(u32 msg)
+{
+ return (msg & IPC_STR_ID_MASK) >> IPC_STR_ID_SHIFT;
+}
+
+static inline u32 msg_get_notify_reason(u32 msg)
+{
+ return (msg & IPC_STG_TYPE_MASK) >> IPC_STG_TYPE_SHIFT;
+}
+
+static inline u32 msg_get_module_operation(u32 msg)
+{
+ return (msg & IPC_MODULE_OPERATION_MASK) >> IPC_MODULE_OPERATION_SHIFT;
+}
+
+static inline u32 msg_get_module_id(u32 msg)
+{
+ return (msg & IPC_MODULE_ID_MASK) >> IPC_MODULE_ID_SHIFT;
+}
+
+u32 create_channel_map(enum sst_hsw_channel_config config)
+{
+ switch (config) {
+ case SST_HSW_CHANNEL_CONFIG_MONO:
+ return (0xFFFFFFF0 | SST_HSW_CHANNEL_CENTER);
+ case SST_HSW_CHANNEL_CONFIG_STEREO:
+ return (0xFFFFFF00 | SST_HSW_CHANNEL_LEFT
+ | (SST_HSW_CHANNEL_RIGHT << 4));
+ case SST_HSW_CHANNEL_CONFIG_2_POINT_1:
+ return (0xFFFFF000 | SST_HSW_CHANNEL_LEFT
+ | (SST_HSW_CHANNEL_RIGHT << 4)
+ | (SST_HSW_CHANNEL_LFE << 8 ));
+ case SST_HSW_CHANNEL_CONFIG_3_POINT_0:
+ return (0xFFFFF000 | SST_HSW_CHANNEL_LEFT
+ | (SST_HSW_CHANNEL_CENTER << 4)
+ | (SST_HSW_CHANNEL_RIGHT << 8));
+ case SST_HSW_CHANNEL_CONFIG_3_POINT_1:
+ return (0xFFFF0000 | SST_HSW_CHANNEL_LEFT
+ | (SST_HSW_CHANNEL_CENTER << 4)
+ | (SST_HSW_CHANNEL_RIGHT << 8)
+ | (SST_HSW_CHANNEL_LFE << 12));
+ case SST_HSW_CHANNEL_CONFIG_QUATRO:
+ return (0xFFFF0000 | SST_HSW_CHANNEL_LEFT
+ | (SST_HSW_CHANNEL_RIGHT << 4)
+ | (SST_HSW_CHANNEL_LEFT_SURROUND << 8)
+ | (SST_HSW_CHANNEL_RIGHT_SURROUND << 12));
+ case SST_HSW_CHANNEL_CONFIG_4_POINT_0:
+ return (0xFFFF0000 | SST_HSW_CHANNEL_LEFT
+ | (SST_HSW_CHANNEL_CENTER << 4)
+ | (SST_HSW_CHANNEL_RIGHT << 8)
+ | (SST_HSW_CHANNEL_CENTER_SURROUND << 12));
+ case SST_HSW_CHANNEL_CONFIG_5_POINT_0:
+ return (0xFFF00000 | SST_HSW_CHANNEL_LEFT
+ | (SST_HSW_CHANNEL_CENTER << 4)
+ | (SST_HSW_CHANNEL_RIGHT << 8)
+ | (SST_HSW_CHANNEL_LEFT_SURROUND << 12)
+ | (SST_HSW_CHANNEL_RIGHT_SURROUND << 16));
+ case SST_HSW_CHANNEL_CONFIG_5_POINT_1:
+ return (0xFF000000 | SST_HSW_CHANNEL_CENTER
+ | (SST_HSW_CHANNEL_LEFT << 4)
+ | (SST_HSW_CHANNEL_RIGHT << 8)
+ | (SST_HSW_CHANNEL_LEFT_SURROUND << 12)
+ | (SST_HSW_CHANNEL_RIGHT_SURROUND << 16)
+ | (SST_HSW_CHANNEL_LFE << 20));
+ case SST_HSW_CHANNEL_CONFIG_DUAL_MONO:
+ return (0xFFFFFF00 | SST_HSW_CHANNEL_LEFT
+ | (SST_HSW_CHANNEL_LEFT << 4));
+ default:
+ return 0xFFFFFFFF;
+ }
+}
+
+static struct sst_hsw_stream *get_stream_by_id(struct sst_hsw *hsw,
+ int stream_id)
+{
+ struct sst_hsw_stream *stream;
+
+ list_for_each_entry(stream, &hsw->stream_list, node) {
+ if (stream->reply.stream_hw_id == stream_id)
+ return stream;
+ }
+
+ return NULL;
+}
+
+static void hsw_fw_ready(struct sst_hsw *hsw, u32 header)
+{
+ struct sst_hsw_ipc_fw_ready fw_ready;
+ u32 offset;
+ u8 fw_info[IPC_MAX_MAILBOX_BYTES - 5 * sizeof(u32)];
+ char *tmp[5], *pinfo;
+ int i = 0;
+
+ offset = (header & 0x1FFFFFFF) << 3;
+
+ dev_dbg(hsw->dev, "ipc: DSP is ready 0x%8.8x offset %d\n",
+ header, offset);
+
+ /* copy data from the DSP FW ready offset */
+ sst_dsp_read(hsw->dsp, &fw_ready, offset, sizeof(fw_ready));
+
+ sst_dsp_mailbox_init(hsw->dsp, fw_ready.inbox_offset,
+ fw_ready.inbox_size, fw_ready.outbox_offset,
+ fw_ready.outbox_size);
+
+ hsw->boot_complete = true;
+ wake_up(&hsw->boot_wait);
+
+ dev_dbg(hsw->dev, " mailbox upstream 0x%x - size 0x%x\n",
+ fw_ready.inbox_offset, fw_ready.inbox_size);
+ dev_dbg(hsw->dev, " mailbox downstream 0x%x - size 0x%x\n",
+ fw_ready.outbox_offset, fw_ready.outbox_size);
+ if (fw_ready.fw_info_size < sizeof(fw_ready.fw_info)) {
+ fw_ready.fw_info[fw_ready.fw_info_size] = 0;
+ dev_dbg(hsw->dev, " Firmware info: %s \n", fw_ready.fw_info);
+
+ /* log the FW version info got from the mailbox here. */
+ memcpy(fw_info, fw_ready.fw_info, fw_ready.fw_info_size);
+ pinfo = &fw_info[0];
+ for (i = 0; i < ARRAY_SIZE(tmp); i++)
+ tmp[i] = strsep(&pinfo, " ");
+ dev_info(hsw->dev, "FW loaded, mailbox readback FW info: type %s, - "
+ "version: %s.%s, build %s, source commit id: %s\n",
+ tmp[0], tmp[1], tmp[2], tmp[3], tmp[4]);
+ }
+}
+
+static void hsw_notification_work(struct work_struct *work)
+{
+ struct sst_hsw_stream *stream = container_of(work,
+ struct sst_hsw_stream, notify_work);
+ struct sst_hsw_ipc_stream_glitch_position *glitch = &stream->glitch;
+ struct sst_hsw_ipc_stream_get_position *pos = &stream->rpos;
+ struct sst_hsw *hsw = stream->hsw;
+ u32 reason;
+
+ reason = msg_get_notify_reason(stream->header);
+
+ switch (reason) {
+ case IPC_STG_GLITCH:
+ trace_ipc_notification("DSP stream under/overrun",
+ stream->reply.stream_hw_id);
+ sst_dsp_inbox_read(hsw->dsp, glitch, sizeof(*glitch));
+
+ dev_err(hsw->dev, "glitch %d pos 0x%x write pos 0x%x\n",
+ glitch->glitch_type, glitch->present_pos,
+ glitch->write_pos);
+ break;
+
+ case IPC_POSITION_CHANGED:
+ trace_ipc_notification("DSP stream position changed for",
+ stream->reply.stream_hw_id);
+ sst_dsp_inbox_read(hsw->dsp, pos, sizeof(*pos));
+
+ if (stream->notify_position)
+ stream->notify_position(stream, stream->pdata);
+
+ break;
+ default:
+ dev_err(hsw->dev, "error: unknown notification 0x%x\n",
+ stream->header);
+ break;
+ }
+
+ /* tell DSP that notification has been handled */
+ sst_dsp_shim_update_bits(hsw->dsp, SST_IPCD,
+ SST_IPCD_BUSY | SST_IPCD_DONE, SST_IPCD_DONE);
+
+ /* unmask busy interrupt */
+ sst_dsp_shim_update_bits(hsw->dsp, SST_IMRX, SST_IMRX_BUSY, 0);
+}
+
+static void hsw_stream_update(struct sst_hsw *hsw, struct ipc_message *msg)
+{
+ struct sst_hsw_stream *stream;
+ u32 header = msg->header & ~(IPC_STATUS_MASK | IPC_GLB_REPLY_MASK);
+ u32 stream_id = msg_get_stream_id(header);
+ u32 stream_msg = msg_get_stream_type(header);
+
+ stream = get_stream_by_id(hsw, stream_id);
+ if (stream == NULL)
+ return;
+
+ switch (stream_msg) {
+ case IPC_STR_STAGE_MESSAGE:
+ case IPC_STR_NOTIFICATION:
+ break;
+ case IPC_STR_RESET:
+ trace_ipc_notification("stream reset", stream->reply.stream_hw_id);
+ break;
+ case IPC_STR_PAUSE:
+ stream->running = false;
+ trace_ipc_notification("stream paused",
+ stream->reply.stream_hw_id);
+ break;
+ case IPC_STR_RESUME:
+ stream->running = true;
+ trace_ipc_notification("stream running",
+ stream->reply.stream_hw_id);
+ break;
+ }
+}
+
+static int hsw_process_reply(struct sst_hsw *hsw, u32 header)
+{
+ struct ipc_message *msg;
+ u32 reply = msg_get_global_reply(header);
+
+ trace_ipc_reply("processing -->", header);
+
+ msg = sst_ipc_reply_find_msg(&hsw->ipc, header);
+ if (msg == NULL) {
+ trace_ipc_error("error: can't find message header", header);
+ return -EIO;
+ }
+
+ /* first process the header */
+ switch (reply) {
+ case IPC_GLB_REPLY_PENDING:
+ trace_ipc_pending_reply("received", header);
+ msg->pending = true;
+ hsw->ipc.pending = true;
+ return 1;
+ case IPC_GLB_REPLY_SUCCESS:
+ if (msg->pending) {
+ trace_ipc_pending_reply("completed", header);
+ sst_dsp_inbox_read(hsw->dsp, msg->rx_data,
+ msg->rx_size);
+ hsw->ipc.pending = false;
+ } else {
+ /* copy data from the DSP */
+ sst_dsp_outbox_read(hsw->dsp, msg->rx_data,
+ msg->rx_size);
+ }
+ break;
+ /* these will be rare - but useful for debug */
+ case IPC_GLB_REPLY_UNKNOWN_MESSAGE_TYPE:
+ trace_ipc_error("error: unknown message type", header);
+ msg->errno = -EBADMSG;
+ break;
+ case IPC_GLB_REPLY_OUT_OF_RESOURCES:
+ trace_ipc_error("error: out of resources", header);
+ msg->errno = -ENOMEM;
+ break;
+ case IPC_GLB_REPLY_BUSY:
+ trace_ipc_error("error: reply busy", header);
+ msg->errno = -EBUSY;
+ break;
+ case IPC_GLB_REPLY_FAILURE:
+ trace_ipc_error("error: reply failure", header);
+ msg->errno = -EINVAL;
+ break;
+ case IPC_GLB_REPLY_STAGE_UNINITIALIZED:
+ trace_ipc_error("error: stage uninitialized", header);
+ msg->errno = -EINVAL;
+ break;
+ case IPC_GLB_REPLY_NOT_FOUND:
+ trace_ipc_error("error: reply not found", header);
+ msg->errno = -EINVAL;
+ break;
+ case IPC_GLB_REPLY_SOURCE_NOT_STARTED:
+ trace_ipc_error("error: source not started", header);
+ msg->errno = -EINVAL;
+ break;
+ case IPC_GLB_REPLY_INVALID_REQUEST:
+ trace_ipc_error("error: invalid request", header);
+ msg->errno = -EINVAL;
+ break;
+ case IPC_GLB_REPLY_ERROR_INVALID_PARAM:
+ trace_ipc_error("error: invalid parameter", header);
+ msg->errno = -EINVAL;
+ break;
+ default:
+ trace_ipc_error("error: unknown reply", header);
+ msg->errno = -EINVAL;
+ break;
+ }
+
+ /* update any stream states */
+ if (msg_get_global_type(header) == IPC_GLB_STREAM_MESSAGE)
+ hsw_stream_update(hsw, msg);
+
+ /* wake up and return the error if we have waiters on this message ? */
+ list_del(&msg->list);
+ sst_ipc_tx_msg_reply_complete(&hsw->ipc, msg);
+
+ return 1;
+}
+
+static int hsw_module_message(struct sst_hsw *hsw, u32 header)
+{
+ u32 operation, module_id;
+ int handled = 0;
+
+ operation = msg_get_module_operation(header);
+ module_id = msg_get_module_id(header);
+ dev_dbg(hsw->dev, "received module message header: 0x%8.8x\n",
+ header);
+ dev_dbg(hsw->dev, "operation: 0x%8.8x module_id: 0x%8.8x\n",
+ operation, module_id);
+
+ switch (operation) {
+ case IPC_MODULE_NOTIFICATION:
+ dev_dbg(hsw->dev, "module notification received");
+ handled = 1;
+ break;
+ default:
+ handled = hsw_process_reply(hsw, header);
+ break;
+ }
+
+ return handled;
+}
+
+static int hsw_stream_message(struct sst_hsw *hsw, u32 header)
+{
+ u32 stream_msg, stream_id, stage_type;
+ struct sst_hsw_stream *stream;
+ int handled = 0;
+
+ stream_msg = msg_get_stream_type(header);
+ stream_id = msg_get_stream_id(header);
+ stage_type = msg_get_stage_type(header);
+
+ stream = get_stream_by_id(hsw, stream_id);
+ if (stream == NULL)
+ return handled;
+
+ stream->header = header;
+
+ switch (stream_msg) {
+ case IPC_STR_STAGE_MESSAGE:
+ dev_err(hsw->dev, "error: stage msg not implemented 0x%8.8x\n",
+ header);
+ break;
+ case IPC_STR_NOTIFICATION:
+ schedule_work(&stream->notify_work);
+ break;
+ default:
+ /* handle pending message complete request */
+ handled = hsw_process_reply(hsw, header);
+ break;
+ }
+
+ return handled;
+}
+
+static int hsw_log_message(struct sst_hsw *hsw, u32 header)
+{
+ u32 operation = (header & IPC_LOG_OP_MASK) >> IPC_LOG_OP_SHIFT;
+ struct sst_hsw_log_stream *stream = &hsw->log_stream;
+ int ret = 1;
+
+ if (operation != IPC_DEBUG_REQUEST_LOG_DUMP) {
+ dev_err(hsw->dev,
+ "error: log msg not implemented 0x%8.8x\n", header);
+ return 0;
+ }
+
+ mutex_lock(&stream->rw_mutex);
+ stream->last_pos = stream->curr_pos;
+ sst_dsp_inbox_read(
+ hsw->dsp, &stream->curr_pos, sizeof(stream->curr_pos));
+ mutex_unlock(&stream->rw_mutex);
+
+ schedule_work(&stream->notify_work);
+
+ return ret;
+}
+
+static int hsw_process_notification(struct sst_hsw *hsw)
+{
+ struct sst_dsp *sst = hsw->dsp;
+ u32 type, header;
+ int handled = 1;
+
+ header = sst_dsp_shim_read_unlocked(sst, SST_IPCD);
+ type = msg_get_global_type(header);
+
+ trace_ipc_request("processing -->", header);
+
+ /* FW Ready is a special case */
+ if (!hsw->boot_complete && header & IPC_FW_READY) {
+ hsw_fw_ready(hsw, header);
+ return handled;
+ }
+
+ switch (type) {
+ case IPC_GLB_GET_FW_VERSION:
+ case IPC_GLB_ALLOCATE_STREAM:
+ case IPC_GLB_FREE_STREAM:
+ case IPC_GLB_GET_FW_CAPABILITIES:
+ case IPC_GLB_REQUEST_DUMP:
+ case IPC_GLB_GET_DEVICE_FORMATS:
+ case IPC_GLB_SET_DEVICE_FORMATS:
+ case IPC_GLB_ENTER_DX_STATE:
+ case IPC_GLB_GET_MIXER_STREAM_INFO:
+ case IPC_GLB_MAX_IPC_MESSAGE_TYPE:
+ case IPC_GLB_RESTORE_CONTEXT:
+ case IPC_GLB_SHORT_REPLY:
+ dev_err(hsw->dev, "error: message type %d header 0x%x\n",
+ type, header);
+ break;
+ case IPC_GLB_STREAM_MESSAGE:
+ handled = hsw_stream_message(hsw, header);
+ break;
+ case IPC_GLB_DEBUG_LOG_MESSAGE:
+ handled = hsw_log_message(hsw, header);
+ break;
+ case IPC_GLB_MODULE_OPERATION:
+ handled = hsw_module_message(hsw, header);
+ break;
+ default:
+ dev_err(hsw->dev, "error: unexpected type %d hdr 0x%8.8x\n",
+ type, header);
+ break;
+ }
+
+ return handled;
+}
+
+static irqreturn_t hsw_irq_thread(int irq, void *context)
+{
+ struct sst_dsp *sst = (struct sst_dsp *) context;
+ struct sst_hsw *hsw = sst_dsp_get_thread_context(sst);
+ struct sst_generic_ipc *ipc = &hsw->ipc;
+ u32 ipcx, ipcd;
+ int handled;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sst->spinlock, flags);
+
+ ipcx = sst_dsp_ipc_msg_rx(hsw->dsp);
+ ipcd = sst_dsp_shim_read_unlocked(sst, SST_IPCD);
+
+ /* reply message from DSP */
+ if (ipcx & SST_IPCX_DONE) {
+
+ /* Handle Immediate reply from DSP Core */
+ handled = hsw_process_reply(hsw, ipcx);
+
+ if (handled > 0) {
+ /* clear DONE bit - tell DSP we have completed */
+ sst_dsp_shim_update_bits_unlocked(sst, SST_IPCX,
+ SST_IPCX_DONE, 0);
+
+ /* unmask Done interrupt */
+ sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
+ SST_IMRX_DONE, 0);
+ }
+ }
+
+ /* new message from DSP */
+ if (ipcd & SST_IPCD_BUSY) {
+
+ /* Handle Notification and Delayed reply from DSP Core */
+ handled = hsw_process_notification(hsw);
+
+ /* clear BUSY bit and set DONE bit - accept new messages */
+ if (handled > 0) {
+ sst_dsp_shim_update_bits_unlocked(sst, SST_IPCD,
+ SST_IPCD_BUSY | SST_IPCD_DONE, SST_IPCD_DONE);
+
+ /* unmask busy interrupt */
+ sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
+ SST_IMRX_BUSY, 0);
+ }
+ }
+
+ spin_unlock_irqrestore(&sst->spinlock, flags);
+
+ /* continue to send any remaining messages... */
+ queue_kthread_work(&ipc->kworker, &ipc->kwork);
+
+ return IRQ_HANDLED;
+}
+
+int sst_hsw_fw_get_version(struct sst_hsw *hsw,
+ struct sst_hsw_ipc_fw_version *version)
+{
+ int ret;
+
+ ret = sst_ipc_tx_message_wait(&hsw->ipc,
+ IPC_GLB_TYPE(IPC_GLB_GET_FW_VERSION),
+ NULL, 0, version, sizeof(*version));
+ if (ret < 0)
+ dev_err(hsw->dev, "error: get version failed\n");
+
+ return ret;
+}
+
+/* Mixer Controls */
+int sst_hsw_stream_get_volume(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+ u32 stage_id, u32 channel, u32 *volume)
+{
+ if (channel > 1)
+ return -EINVAL;
+
+ sst_dsp_read(hsw->dsp, volume,
+ stream->reply.volume_register_address[channel],
+ sizeof(*volume));
+
+ return 0;
+}
+
+/* stream volume */
+int sst_hsw_stream_set_volume(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 volume)
+{
+ struct sst_hsw_ipc_volume_req *req;
+ u32 header;
+ int ret;
+
+ trace_ipc_request("set stream volume", stream->reply.stream_hw_id);
+
+ if (channel >= 2 && channel != SST_HSW_CHANNELS_ALL)
+ return -EINVAL;
+
+ header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) |
+ IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE);
+ header |= (stream->reply.stream_hw_id << IPC_STR_ID_SHIFT);
+ header |= (IPC_STG_SET_VOLUME << IPC_STG_TYPE_SHIFT);
+ header |= (stage_id << IPC_STG_ID_SHIFT);
+
+ req = &stream->vol_req;
+ req->target_volume = volume;
+
+ /* set both at same time ? */
+ if (channel == SST_HSW_CHANNELS_ALL) {
+ if (hsw->mute[0] && hsw->mute[1]) {
+ hsw->mute_volume[0] = hsw->mute_volume[1] = volume;
+ return 0;
+ } else if (hsw->mute[0])
+ req->channel = 1;
+ else if (hsw->mute[1])
+ req->channel = 0;
+ else
+ req->channel = SST_HSW_CHANNELS_ALL;
+ } else {
+ /* set only 1 channel */
+ if (hsw->mute[channel]) {
+ hsw->mute_volume[channel] = volume;
+ return 0;
+ }
+ req->channel = channel;
+ }
+
+ ret = sst_ipc_tx_message_wait(&hsw->ipc, header, req,
+ sizeof(*req), NULL, 0);
+ if (ret < 0) {
+ dev_err(hsw->dev, "error: set stream volume failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+int sst_hsw_mixer_get_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
+ u32 *volume)
+{
+ if (channel > 1)
+ return -EINVAL;
+
+ sst_dsp_read(hsw->dsp, volume,
+ hsw->mixer_info.volume_register_address[channel],
+ sizeof(*volume));
+
+ return 0;
+}
+
+/* global mixer volume */
+int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
+ u32 volume)
+{
+ struct sst_hsw_ipc_volume_req req;
+ u32 header;
+ int ret;
+
+ trace_ipc_request("set mixer volume", volume);
+
+ if (channel >= 2 && channel != SST_HSW_CHANNELS_ALL)
+ return -EINVAL;
+
+ /* set both at same time ? */
+ if (channel == SST_HSW_CHANNELS_ALL) {
+ if (hsw->mute[0] && hsw->mute[1]) {
+ hsw->mute_volume[0] = hsw->mute_volume[1] = volume;
+ return 0;
+ } else if (hsw->mute[0])
+ req.channel = 1;
+ else if (hsw->mute[1])
+ req.channel = 0;
+ else
+ req.channel = SST_HSW_CHANNELS_ALL;
+ } else {
+ /* set only 1 channel */
+ if (hsw->mute[channel]) {
+ hsw->mute_volume[channel] = volume;
+ return 0;
+ }
+ req.channel = channel;
+ }
+
+ header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) |
+ IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE);
+ header |= (hsw->mixer_info.mixer_hw_id << IPC_STR_ID_SHIFT);
+ header |= (IPC_STG_SET_VOLUME << IPC_STG_TYPE_SHIFT);
+ header |= (stage_id << IPC_STG_ID_SHIFT);
+
+ req.curve_duration = hsw->curve_duration;
+ req.curve_type = hsw->curve_type;
+ req.target_volume = volume;
+
+ ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &req,
+ sizeof(req), NULL, 0);
+ if (ret < 0) {
+ dev_err(hsw->dev, "error: set mixer volume failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Stream API */
+struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id,
+ u32 (*notify_position)(struct sst_hsw_stream *stream, void *data),
+ void *data)
+{
+ struct sst_hsw_stream *stream;
+ struct sst_dsp *sst = hsw->dsp;
+ unsigned long flags;
+
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (stream == NULL)
+ return NULL;
+
+ spin_lock_irqsave(&sst->spinlock, flags);
+ stream->reply.stream_hw_id = INVALID_STREAM_HW_ID;
+ list_add(&stream->node, &hsw->stream_list);
+ stream->notify_position = notify_position;
+ stream->pdata = data;
+ stream->hsw = hsw;
+ stream->host_id = id;
+
+ /* work to process notification messages */
+ INIT_WORK(&stream->notify_work, hsw_notification_work);
+ spin_unlock_irqrestore(&sst->spinlock, flags);
+
+ return stream;
+}
+
+int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
+{
+ u32 header;
+ int ret = 0;
+ struct sst_dsp *sst = hsw->dsp;
+ unsigned long flags;
+
+ if (!stream) {
+ dev_warn(hsw->dev, "warning: stream is NULL, no stream to free, ignore it.\n");
+ return 0;
+ }
+
+ /* dont free DSP streams that are not commited */
+ if (!stream->commited)
+ goto out;
+
+ trace_ipc_request("stream free", stream->host_id);
+
+ stream->free_req.stream_id = stream->reply.stream_hw_id;
+ header = IPC_GLB_TYPE(IPC_GLB_FREE_STREAM);
+
+ ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &stream->free_req,
+ sizeof(stream->free_req), NULL, 0);
+ if (ret < 0) {
+ dev_err(hsw->dev, "error: free stream %d failed\n",
+ stream->free_req.stream_id);
+ return -EAGAIN;
+ }
+
+ trace_hsw_stream_free_req(stream, &stream->free_req);
+
+out:
+ cancel_work_sync(&stream->notify_work);
+ spin_lock_irqsave(&sst->spinlock, flags);
+ list_del(&stream->node);
+ kfree(stream);
+ spin_unlock_irqrestore(&sst->spinlock, flags);
+
+ return ret;
+}
+
+int sst_hsw_stream_set_bits(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream, enum sst_hsw_bitdepth bits)
+{
+ if (stream->commited) {
+ dev_err(hsw->dev, "error: stream committed for set bits\n");
+ return -EINVAL;
+ }
+
+ stream->request.format.bitdepth = bits;
+ return 0;
+}
+
+int sst_hsw_stream_set_channels(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream, int channels)
+{
+ if (stream->commited) {
+ dev_err(hsw->dev, "error: stream committed for set channels\n");
+ return -EINVAL;
+ }
+
+ stream->request.format.ch_num = channels;
+ return 0;
+}
+
+int sst_hsw_stream_set_rate(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream, int rate)
+{
+ if (stream->commited) {
+ dev_err(hsw->dev, "error: stream committed for set rate\n");
+ return -EINVAL;
+ }
+
+ stream->request.format.frequency = rate;
+ return 0;
+}
+
+int sst_hsw_stream_set_map_config(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream, u32 map,
+ enum sst_hsw_channel_config config)
+{
+ if (stream->commited) {
+ dev_err(hsw->dev, "error: stream committed for set map\n");
+ return -EINVAL;
+ }
+
+ stream->request.format.map = map;
+ stream->request.format.config = config;
+ return 0;
+}
+
+int sst_hsw_stream_set_style(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream, enum sst_hsw_interleaving style)
+{
+ if (stream->commited) {
+ dev_err(hsw->dev, "error: stream committed for set style\n");
+ return -EINVAL;
+ }
+
+ stream->request.format.style = style;
+ return 0;
+}
+
+int sst_hsw_stream_set_valid(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream, u32 bits)
+{
+ if (stream->commited) {
+ dev_err(hsw->dev, "error: stream committed for set valid bits\n");
+ return -EINVAL;
+ }
+
+ stream->request.format.valid_bit = bits;
+ return 0;
+}
+
+/* Stream Configuration */
+int sst_hsw_stream_format(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+ enum sst_hsw_stream_path_id path_id,
+ enum sst_hsw_stream_type stream_type,
+ enum sst_hsw_stream_format format_id)
+{
+ if (stream->commited) {
+ dev_err(hsw->dev, "error: stream committed for set format\n");
+ return -EINVAL;
+ }
+
+ stream->request.path_id = path_id;
+ stream->request.stream_type = stream_type;
+ stream->request.format_id = format_id;
+
+ trace_hsw_stream_alloc_request(stream, &stream->request);
+
+ return 0;
+}
+
+int sst_hsw_stream_buffer(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+ u32 ring_pt_address, u32 num_pages,
+ u32 ring_size, u32 ring_offset, u32 ring_first_pfn)
+{
+ if (stream->commited) {
+ dev_err(hsw->dev, "error: stream committed for buffer\n");
+ return -EINVAL;
+ }
+
+ stream->request.ringinfo.ring_pt_address = ring_pt_address;
+ stream->request.ringinfo.num_pages = num_pages;
+ stream->request.ringinfo.ring_size = ring_size;
+ stream->request.ringinfo.ring_offset = ring_offset;
+ stream->request.ringinfo.ring_first_pfn = ring_first_pfn;
+
+ trace_hsw_stream_buffer(stream);
+
+ return 0;
+}
+
+int sst_hsw_stream_set_module_info(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream, struct sst_module_runtime *runtime)
+{
+ struct sst_hsw_module_map *map = &stream->request.map;
+ struct sst_dsp *dsp = sst_hsw_get_dsp(hsw);
+ struct sst_module *module = runtime->module;
+
+ if (stream->commited) {
+ dev_err(hsw->dev, "error: stream committed for set module\n");
+ return -EINVAL;
+ }
+
+ /* only support initial module atm */
+ map->module_entries_count = 1;
+ map->module_entries[0].module_id = module->id;
+ map->module_entries[0].entry_point = module->entry;
+
+ stream->request.persistent_mem.offset =
+ sst_dsp_get_offset(dsp, runtime->persistent_offset, SST_MEM_DRAM);
+ stream->request.persistent_mem.size = module->persistent_size;
+
+ stream->request.scratch_mem.offset =
+ sst_dsp_get_offset(dsp, dsp->scratch_offset, SST_MEM_DRAM);
+ stream->request.scratch_mem.size = dsp->scratch_size;
+
+ dev_dbg(hsw->dev, "module %d runtime %d using:\n", module->id,
+ runtime->id);
+ dev_dbg(hsw->dev, " persistent offset 0x%x bytes 0x%x\n",
+ stream->request.persistent_mem.offset,
+ stream->request.persistent_mem.size);
+ dev_dbg(hsw->dev, " scratch offset 0x%x bytes 0x%x\n",
+ stream->request.scratch_mem.offset,
+ stream->request.scratch_mem.size);
+
+ return 0;
+}
+
+int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
+{
+ struct sst_hsw_ipc_stream_alloc_req *str_req = &stream->request;
+ struct sst_hsw_ipc_stream_alloc_reply *reply = &stream->reply;
+ u32 header;
+ int ret;
+
+ if (!stream) {
+ dev_warn(hsw->dev, "warning: stream is NULL, no stream to commit, ignore it.\n");
+ return 0;
+ }
+
+ if (stream->commited) {
+ dev_warn(hsw->dev, "warning: stream is already committed, ignore it.\n");
+ return 0;
+ }
+
+ trace_ipc_request("stream alloc", stream->host_id);
+
+ header = IPC_GLB_TYPE(IPC_GLB_ALLOCATE_STREAM);
+
+ ret = sst_ipc_tx_message_wait(&hsw->ipc, header, str_req,
+ sizeof(*str_req), reply, sizeof(*reply));
+ if (ret < 0) {
+ dev_err(hsw->dev, "error: stream commit failed\n");
+ return ret;
+ }
+
+ stream->commited = 1;
+ trace_hsw_stream_alloc_reply(stream);
+
+ return 0;
+}
+
+snd_pcm_uframes_t sst_hsw_stream_get_old_position(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream)
+{
+ return stream->old_position;
+}
+
+void sst_hsw_stream_set_old_position(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream, snd_pcm_uframes_t val)
+{
+ stream->old_position = val;
+}
+
+bool sst_hsw_stream_get_silence_start(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream)
+{
+ return stream->play_silence;
+}
+
+void sst_hsw_stream_set_silence_start(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream, bool val)
+{
+ stream->play_silence = val;
+}
+
+/* Stream Information - these calls could be inline but we want the IPC
+ ABI to be opaque to client PCM drivers to cope with any future ABI changes */
+int sst_hsw_mixer_get_info(struct sst_hsw *hsw)
+{
+ struct sst_hsw_ipc_stream_info_reply *reply;
+ u32 header;
+ int ret;
+
+ reply = &hsw->mixer_info;
+ header = IPC_GLB_TYPE(IPC_GLB_GET_MIXER_STREAM_INFO);
+
+ trace_ipc_request("get global mixer info", 0);
+
+ ret = sst_ipc_tx_message_wait(&hsw->ipc, header, NULL, 0,
+ reply, sizeof(*reply));
+ if (ret < 0) {
+ dev_err(hsw->dev, "error: get stream info failed\n");
+ return ret;
+ }
+
+ trace_hsw_mixer_info_reply(reply);
+
+ return 0;
+}
+
+/* Send stream command */
+static int sst_hsw_stream_operations(struct sst_hsw *hsw, int type,
+ int stream_id, int wait)
+{
+ u32 header;
+
+ header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) | IPC_STR_TYPE(type);
+ header |= (stream_id << IPC_STR_ID_SHIFT);
+
+ if (wait)
+ return sst_ipc_tx_message_wait(&hsw->ipc, header,
+ NULL, 0, NULL, 0);
+ else
+ return sst_ipc_tx_message_nowait(&hsw->ipc, header, NULL, 0);
+}
+
+/* Stream ALSA trigger operations */
+int sst_hsw_stream_pause(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+ int wait)
+{
+ int ret;
+
+ if (!stream) {
+ dev_warn(hsw->dev, "warning: stream is NULL, no stream to pause, ignore it.\n");
+ return 0;
+ }
+
+ trace_ipc_request("stream pause", stream->reply.stream_hw_id);
+
+ ret = sst_hsw_stream_operations(hsw, IPC_STR_PAUSE,
+ stream->reply.stream_hw_id, wait);
+ if (ret < 0)
+ dev_err(hsw->dev, "error: failed to pause stream %d\n",
+ stream->reply.stream_hw_id);
+
+ return ret;
+}
+
+int sst_hsw_stream_resume(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+ int wait)
+{
+ int ret;
+
+ if (!stream) {
+ dev_warn(hsw->dev, "warning: stream is NULL, no stream to resume, ignore it.\n");
+ return 0;
+ }
+
+ trace_ipc_request("stream resume", stream->reply.stream_hw_id);
+
+ ret = sst_hsw_stream_operations(hsw, IPC_STR_RESUME,
+ stream->reply.stream_hw_id, wait);
+ if (ret < 0)
+ dev_err(hsw->dev, "error: failed to resume stream %d\n",
+ stream->reply.stream_hw_id);
+
+ return ret;
+}
+
+int sst_hsw_stream_reset(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
+{
+ int ret, tries = 10;
+
+ if (!stream) {
+ dev_warn(hsw->dev, "warning: stream is NULL, no stream to reset, ignore it.\n");
+ return 0;
+ }
+
+ /* dont reset streams that are not commited */
+ if (!stream->commited)
+ return 0;
+
+ /* wait for pause to complete before we reset the stream */
+ while (stream->running && tries--)
+ msleep(1);
+ if (!tries) {
+ dev_err(hsw->dev, "error: reset stream %d still running\n",
+ stream->reply.stream_hw_id);
+ return -EINVAL;
+ }
+
+ trace_ipc_request("stream reset", stream->reply.stream_hw_id);
+
+ ret = sst_hsw_stream_operations(hsw, IPC_STR_RESET,
+ stream->reply.stream_hw_id, 1);
+ if (ret < 0)
+ dev_err(hsw->dev, "error: failed to reset stream %d\n",
+ stream->reply.stream_hw_id);
+ return ret;
+}
+
+/* Stream pointer positions */
+u32 sst_hsw_get_dsp_position(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream)
+{
+ u32 rpos;
+
+ sst_dsp_read(hsw->dsp, &rpos,
+ stream->reply.read_position_register_address, sizeof(rpos));
+
+ return rpos;
+}
+
+/* Stream presentation (monotonic) positions */
+u64 sst_hsw_get_dsp_presentation_position(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream)
+{
+ u64 ppos;
+
+ sst_dsp_read(hsw->dsp, &ppos,
+ stream->reply.presentation_position_register_address,
+ sizeof(ppos));
+
+ return ppos;
+}
+
+/* physical BE config */
+int sst_hsw_device_set_config(struct sst_hsw *hsw,
+ enum sst_hsw_device_id dev, enum sst_hsw_device_mclk mclk,
+ enum sst_hsw_device_mode mode, u32 clock_divider)
+{
+ struct sst_hsw_ipc_device_config_req config;
+ u32 header;
+ int ret;
+
+ trace_ipc_request("set device config", dev);
+
+ config.ssp_interface = dev;
+ config.clock_frequency = mclk;
+ config.mode = mode;
+ config.clock_divider = clock_divider;
+ if (mode == SST_HSW_DEVICE_TDM_CLOCK_MASTER)
+ config.channels = 4;
+ else
+ config.channels = 2;
+
+ trace_hsw_device_config_req(&config);
+
+ header = IPC_GLB_TYPE(IPC_GLB_SET_DEVICE_FORMATS);
+
+ ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &config,
+ sizeof(config), NULL, 0);
+ if (ret < 0)
+ dev_err(hsw->dev, "error: set device formats failed\n");
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sst_hsw_device_set_config);
+
+/* DX Config */
+int sst_hsw_dx_set_state(struct sst_hsw *hsw,
+ enum sst_hsw_dx_state state, struct sst_hsw_ipc_dx_reply *dx)
+{
+ u32 header, state_;
+ int ret, item;
+
+ header = IPC_GLB_TYPE(IPC_GLB_ENTER_DX_STATE);
+ state_ = state;
+
+ trace_ipc_request("PM enter Dx state", state);
+
+ ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &state_,
+ sizeof(state_), dx, sizeof(*dx));
+ if (ret < 0) {
+ dev_err(hsw->dev, "ipc: error set dx state %d failed\n", state);
+ return ret;
+ }
+
+ for (item = 0; item < dx->entries_no; item++) {
+ dev_dbg(hsw->dev,
+ "Item[%d] offset[%x] - size[%x] - source[%x]\n",
+ item, dx->mem_info[item].offset,
+ dx->mem_info[item].size,
+ dx->mem_info[item].source);
+ }
+ dev_dbg(hsw->dev, "ipc: got %d entry numbers for state %d\n",
+ dx->entries_no, state);
+
+ return ret;
+}
+
+struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw,
+ int mod_id, int offset)
+{
+ struct sst_dsp *dsp = hsw->dsp;
+ struct sst_module *module;
+ struct sst_module_runtime *runtime;
+ int err;
+
+ module = sst_module_get_from_id(dsp, mod_id);
+ if (module == NULL) {
+ dev_err(dsp->dev, "error: failed to get module %d for pcm\n",
+ mod_id);
+ return NULL;
+ }
+
+ runtime = sst_module_runtime_new(module, mod_id, NULL);
+ if (runtime == NULL) {
+ dev_err(dsp->dev, "error: failed to create module %d runtime\n",
+ mod_id);
+ return NULL;
+ }
+
+ err = sst_module_runtime_alloc_blocks(runtime, offset);
+ if (err < 0) {
+ dev_err(dsp->dev, "error: failed to alloc blocks for module %d runtime\n",
+ mod_id);
+ sst_module_runtime_free(runtime);
+ return NULL;
+ }
+
+ dev_dbg(dsp->dev, "runtime id %d created for module %d\n", runtime->id,
+ mod_id);
+ return runtime;
+}
+
+void sst_hsw_runtime_module_free(struct sst_module_runtime *runtime)
+{
+ sst_module_runtime_free_blocks(runtime);
+ sst_module_runtime_free(runtime);
+}
+
+#ifdef CONFIG_PM
+static int sst_hsw_dx_state_dump(struct sst_hsw *hsw)
+{
+ struct sst_dsp *sst = hsw->dsp;
+ u32 item, offset, size;
+ int ret = 0;
+
+ trace_ipc_request("PM state dump. Items #", SST_HSW_MAX_DX_REGIONS);
+
+ if (hsw->dx.entries_no > SST_HSW_MAX_DX_REGIONS) {
+ dev_err(hsw->dev,
+ "error: number of FW context regions greater than %d\n",
+ SST_HSW_MAX_DX_REGIONS);
+ memset(&hsw->dx, 0, sizeof(hsw->dx));
+ return -EINVAL;
+ }
+
+ ret = sst_dsp_dma_get_channel(sst, 0);
+ if (ret < 0) {
+ dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
+ return ret;
+ }
+
+ /* set on-demond mode on engine 0 channel 3 */
+ sst_dsp_shim_update_bits(sst, SST_HMDC,
+ SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH,
+ SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH);
+
+ for (item = 0; item < hsw->dx.entries_no; item++) {
+ if (hsw->dx.mem_info[item].source == SST_HSW_DX_TYPE_MEMORY_DUMP
+ && hsw->dx.mem_info[item].offset > DSP_DRAM_ADDR_OFFSET
+ && hsw->dx.mem_info[item].offset <
+ DSP_DRAM_ADDR_OFFSET + SST_HSW_DX_CONTEXT_SIZE) {
+
+ offset = hsw->dx.mem_info[item].offset
+ - DSP_DRAM_ADDR_OFFSET;
+ size = (hsw->dx.mem_info[item].size + 3) & (~3);
+
+ ret = sst_dsp_dma_copyfrom(sst, hsw->dx_context_paddr + offset,
+ sst->addr.lpe_base + offset, size);
+ if (ret < 0) {
+ dev_err(hsw->dev,
+ "error: FW context dump failed\n");
+ memset(&hsw->dx, 0, sizeof(hsw->dx));
+ goto out;
+ }
+ }
+ }
+
+out:
+ sst_dsp_dma_put_channel(sst);
+ return ret;
+}
+
+static int sst_hsw_dx_state_restore(struct sst_hsw *hsw)
+{
+ struct sst_dsp *sst = hsw->dsp;
+ u32 item, offset, size;
+ int ret;
+
+ for (item = 0; item < hsw->dx.entries_no; item++) {
+ if (hsw->dx.mem_info[item].source == SST_HSW_DX_TYPE_MEMORY_DUMP
+ && hsw->dx.mem_info[item].offset > DSP_DRAM_ADDR_OFFSET
+ && hsw->dx.mem_info[item].offset <
+ DSP_DRAM_ADDR_OFFSET + SST_HSW_DX_CONTEXT_SIZE) {
+
+ offset = hsw->dx.mem_info[item].offset
+ - DSP_DRAM_ADDR_OFFSET;
+ size = (hsw->dx.mem_info[item].size + 3) & (~3);
+
+ ret = sst_dsp_dma_copyto(sst, sst->addr.lpe_base + offset,
+ hsw->dx_context_paddr + offset, size);
+ if (ret < 0) {
+ dev_err(hsw->dev,
+ "error: FW context restore failed\n");
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int sst_hsw_dsp_load(struct sst_hsw *hsw)
+{
+ struct sst_dsp *dsp = hsw->dsp;
+ struct sst_fw *sst_fw, *t;
+ int ret;
+
+ dev_dbg(hsw->dev, "loading audio DSP....");
+
+ ret = sst_dsp_wake(dsp);
+ if (ret < 0) {
+ dev_err(hsw->dev, "error: failed to wake audio DSP\n");
+ return -ENODEV;
+ }
+
+ ret = sst_dsp_dma_get_channel(dsp, 0);
+ if (ret < 0) {
+ dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
+ return ret;
+ }
+
+ list_for_each_entry_safe_reverse(sst_fw, t, &dsp->fw_list, list) {
+ ret = sst_fw_reload(sst_fw);
+ if (ret < 0) {
+ dev_err(hsw->dev, "error: SST FW reload failed\n");
+ sst_dsp_dma_put_channel(dsp);
+ return -ENOMEM;
+ }
+ }
+ ret = sst_block_alloc_scratch(hsw->dsp);
+ if (ret < 0)
+ return -EINVAL;
+
+ sst_dsp_dma_put_channel(dsp);
+ return 0;
+}
+
+static int sst_hsw_dsp_restore(struct sst_hsw *hsw)
+{
+ struct sst_dsp *dsp = hsw->dsp;
+ int ret;
+
+ dev_dbg(hsw->dev, "restoring audio DSP....");
+
+ ret = sst_dsp_dma_get_channel(dsp, 0);
+ if (ret < 0) {
+ dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
+ return ret;
+ }
+
+ ret = sst_hsw_dx_state_restore(hsw);
+ if (ret < 0) {
+ dev_err(hsw->dev, "error: SST FW context restore failed\n");
+ sst_dsp_dma_put_channel(dsp);
+ return -ENOMEM;
+ }
+ sst_dsp_dma_put_channel(dsp);
+
+ /* wait for DSP boot completion */
+ sst_dsp_boot(dsp);
+
+ return ret;
+}
+
+int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw)
+{
+ int ret;
+
+ dev_dbg(hsw->dev, "audio dsp runtime suspend\n");
+
+ ret = sst_hsw_dx_set_state(hsw, SST_HSW_DX_STATE_D3, &hsw->dx);
+ if (ret < 0)
+ return ret;
+
+ sst_dsp_stall(hsw->dsp);
+
+ ret = sst_hsw_dx_state_dump(hsw);
+ if (ret < 0)
+ return ret;
+
+ sst_ipc_drop_all(&hsw->ipc);
+
+ return 0;
+}
+
+int sst_hsw_dsp_runtime_sleep(struct sst_hsw *hsw)
+{
+ struct sst_fw *sst_fw, *t;
+ struct sst_dsp *dsp = hsw->dsp;
+
+ list_for_each_entry_safe(sst_fw, t, &dsp->fw_list, list) {
+ sst_fw_unload(sst_fw);
+ }
+ sst_block_free_scratch(dsp);
+
+ hsw->boot_complete = false;
+
+ sst_dsp_sleep(dsp);
+
+ return 0;
+}
+
+int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw)
+{
+ struct device *dev = hsw->dev;
+ int ret;
+
+ dev_dbg(dev, "audio dsp runtime resume\n");
+
+ if (hsw->boot_complete)
+ return 1; /* tell caller no action is required */
+
+ ret = sst_hsw_dsp_restore(hsw);
+ if (ret < 0)
+ dev_err(dev, "error: audio DSP boot failure\n");
+
+ sst_hsw_init_module_state(hsw);
+
+ ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete,
+ msecs_to_jiffies(IPC_BOOT_MSECS));
+ if (ret == 0) {
+ dev_err(hsw->dev, "error: audio DSP boot timeout IPCD 0x%x IPCX 0x%x\n",
+ sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCD),
+ sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX));
+ return -EIO;
+ }
+
+ /* Set ADSP SSP port settings */
+ ret = sst_hsw_device_set_config(hsw, SST_HSW_DEVICE_SSP_0,
+ SST_HSW_DEVICE_MCLK_FREQ_24_MHZ,
+ SST_HSW_DEVICE_CLOCK_MASTER, 9);
+ if (ret < 0)
+ dev_err(dev, "error: SSP re-initialization failed\n");
+
+ return ret;
+}
+#endif
+
+struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw)
+{
+ return hsw->dsp;
+}
+
+void sst_hsw_init_module_state(struct sst_hsw *hsw)
+{
+ struct sst_module *module;
+ enum sst_hsw_module_id id;
+
+ /* the base fw contains several modules */
+ for (id = SST_HSW_MODULE_BASE_FW; id < SST_HSW_MAX_MODULE_ID; id++) {
+ module = sst_module_get_from_id(hsw->dsp, id);
+ if (module) {
+ /* module waves is active only after being enabled */
+ if (id == SST_HSW_MODULE_WAVES)
+ module->state = SST_MODULE_STATE_INITIALIZED;
+ else
+ module->state = SST_MODULE_STATE_ACTIVE;
+ }
+ }
+}
+
+bool sst_hsw_is_module_loaded(struct sst_hsw *hsw, u32 module_id)
+{
+ struct sst_module *module;
+
+ module = sst_module_get_from_id(hsw->dsp, module_id);
+ if (module == NULL || module->state == SST_MODULE_STATE_UNLOADED)
+ return false;
+ else
+ return true;
+}
+
+bool sst_hsw_is_module_active(struct sst_hsw *hsw, u32 module_id)
+{
+ struct sst_module *module;
+
+ module = sst_module_get_from_id(hsw->dsp, module_id);
+ if (module != NULL && module->state == SST_MODULE_STATE_ACTIVE)
+ return true;
+ else
+ return false;
+}
+
+void sst_hsw_set_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id)
+{
+ hsw->enabled_modules_rtd3 |= (1 << module_id);
+}
+
+void sst_hsw_set_module_disabled_rtd3(struct sst_hsw *hsw, u32 module_id)
+{
+ hsw->enabled_modules_rtd3 &= ~(1 << module_id);
+}
+
+bool sst_hsw_is_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id)
+{
+ return hsw->enabled_modules_rtd3 & (1 << module_id);
+}
+
+void sst_hsw_reset_param_buf(struct sst_hsw *hsw)
+{
+ hsw->param_idx_w = 0;
+ hsw->param_idx_r = 0;
+ memset((void *)hsw->param_buf, 0, sizeof(hsw->param_buf));
+}
+
+int sst_hsw_store_param_line(struct sst_hsw *hsw, u8 *buf)
+{
+ /* save line to the first available position of param buffer */
+ if (hsw->param_idx_w > WAVES_PARAM_LINES - 1) {
+ dev_warn(hsw->dev, "warning: param buffer overflow!\n");
+ return -EPERM;
+ }
+ memcpy(hsw->param_buf[hsw->param_idx_w], buf, WAVES_PARAM_COUNT);
+ hsw->param_idx_w++;
+ return 0;
+}
+
+int sst_hsw_load_param_line(struct sst_hsw *hsw, u8 *buf)
+{
+ u8 id = 0;
+
+ /* read the first matching line from param buffer */
+ while (hsw->param_idx_r < WAVES_PARAM_LINES) {
+ id = hsw->param_buf[hsw->param_idx_r][0];
+ hsw->param_idx_r++;
+ if (buf[0] == id) {
+ memcpy(buf, hsw->param_buf[hsw->param_idx_r],
+ WAVES_PARAM_COUNT);
+ break;
+ }
+ }
+ if (hsw->param_idx_r > WAVES_PARAM_LINES - 1) {
+ dev_dbg(hsw->dev, "end of buffer, roll to the beginning\n");
+ hsw->param_idx_r = 0;
+ return 0;
+ }
+ return 0;
+}
+
+int sst_hsw_launch_param_buf(struct sst_hsw *hsw)
+{
+ int ret, idx;
+
+ if (!sst_hsw_is_module_active(hsw, SST_HSW_MODULE_WAVES)) {
+ dev_dbg(hsw->dev, "module waves is not active\n");
+ return 0;
+ }
+
+ /* put all param lines to DSP through ipc */
+ for (idx = 0; idx < hsw->param_idx_w; idx++) {
+ ret = sst_hsw_module_set_param(hsw,
+ SST_HSW_MODULE_WAVES, 0, hsw->param_buf[idx][0],
+ WAVES_PARAM_COUNT, hsw->param_buf[idx]);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+int sst_hsw_module_load(struct sst_hsw *hsw,
+ u32 module_id, u32 instance_id, char *name)
+{
+ int ret = 0;
+ const struct firmware *fw = NULL;
+ struct sst_fw *hsw_sst_fw;
+ struct sst_module *module;
+ struct device *dev = hsw->dev;
+ struct sst_dsp *dsp = hsw->dsp;
+
+ dev_dbg(dev, "sst_hsw_module_load id=%d, name='%s'", module_id, name);
+
+ module = sst_module_get_from_id(dsp, module_id);
+ if (module == NULL) {
+ /* loading for the first time */
+ if (module_id == SST_HSW_MODULE_BASE_FW) {
+ /* for base module: use fw requested in acpi probe */
+ fw = dsp->pdata->fw;
+ if (!fw) {
+ dev_err(dev, "request Base fw failed\n");
+ return -ENODEV;
+ }
+ } else {
+ /* try and load any other optional modules if they are
+ * available. Use dev_info instead of dev_err in case
+ * request firmware failed */
+ ret = request_firmware(&fw, name, dev);
+ if (ret) {
+ dev_info(dev, "fw image %s not available(%d)\n",
+ name, ret);
+ return ret;
+ }
+ }
+ hsw_sst_fw = sst_fw_new(dsp, fw, hsw);
+ if (hsw_sst_fw == NULL) {
+ dev_err(dev, "error: failed to load firmware\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ module = sst_module_get_from_id(dsp, module_id);
+ if (module == NULL) {
+ dev_err(dev, "error: no module %d in firmware %s\n",
+ module_id, name);
+ }
+ } else
+ dev_info(dev, "module %d (%s) already loaded\n",
+ module_id, name);
+out:
+ /* release fw, but base fw should be released by acpi driver */
+ if (fw && module_id != SST_HSW_MODULE_BASE_FW)
+ release_firmware(fw);
+
+ return ret;
+}
+
+int sst_hsw_module_enable(struct sst_hsw *hsw,
+ u32 module_id, u32 instance_id)
+{
+ int ret;
+ u32 header = 0;
+ struct sst_hsw_ipc_module_config config;
+ struct sst_module *module;
+ struct sst_module_runtime *runtime;
+ struct device *dev = hsw->dev;
+ struct sst_dsp *dsp = hsw->dsp;
+
+ if (!sst_hsw_is_module_loaded(hsw, module_id)) {
+ dev_dbg(dev, "module %d not loaded\n", module_id);
+ return 0;
+ }
+
+ if (sst_hsw_is_module_active(hsw, module_id)) {
+ dev_info(dev, "module %d already enabled\n", module_id);
+ return 0;
+ }
+
+ module = sst_module_get_from_id(dsp, module_id);
+ if (module == NULL) {
+ dev_err(dev, "module %d not valid\n", module_id);
+ return -ENXIO;
+ }
+
+ runtime = sst_module_runtime_get_from_id(module, module_id);
+ if (runtime == NULL) {
+ dev_err(dev, "runtime %d not valid", module_id);
+ return -ENXIO;
+ }
+
+ header = IPC_GLB_TYPE(IPC_GLB_MODULE_OPERATION) |
+ IPC_MODULE_OPERATION(IPC_MODULE_ENABLE) |
+ IPC_MODULE_ID(module_id);
+ dev_dbg(dev, "module enable header: %x\n", header);
+
+ config.map.module_entries_count = 1;
+ config.map.module_entries[0].module_id = module->id;
+ config.map.module_entries[0].entry_point = module->entry;
+
+ config.persistent_mem.offset =
+ sst_dsp_get_offset(dsp,
+ runtime->persistent_offset, SST_MEM_DRAM);
+ config.persistent_mem.size = module->persistent_size;
+
+ config.scratch_mem.offset =
+ sst_dsp_get_offset(dsp,
+ dsp->scratch_offset, SST_MEM_DRAM);
+ config.scratch_mem.size = module->scratch_size;
+ dev_dbg(dev, "mod %d enable p:%d @ %x, s:%d @ %x, ep: %x",
+ config.map.module_entries[0].module_id,
+ config.persistent_mem.size,
+ config.persistent_mem.offset,
+ config.scratch_mem.size, config.scratch_mem.offset,
+ config.map.module_entries[0].entry_point);
+
+ ret = sst_ipc_tx_message_wait(&hsw->ipc, header,
+ &config, sizeof(config), NULL, 0);
+ if (ret < 0)
+ dev_err(dev, "ipc: module enable failed - %d\n", ret);
+ else
+ module->state = SST_MODULE_STATE_ACTIVE;
+
+ return ret;
+}
+
+int sst_hsw_module_disable(struct sst_hsw *hsw,
+ u32 module_id, u32 instance_id)
+{
+ int ret;
+ u32 header;
+ struct sst_module *module;
+ struct device *dev = hsw->dev;
+ struct sst_dsp *dsp = hsw->dsp;
+
+ if (!sst_hsw_is_module_loaded(hsw, module_id)) {
+ dev_dbg(dev, "module %d not loaded\n", module_id);
+ return 0;
+ }
+
+ if (!sst_hsw_is_module_active(hsw, module_id)) {
+ dev_info(dev, "module %d already disabled\n", module_id);
+ return 0;
+ }
+
+ module = sst_module_get_from_id(dsp, module_id);
+ if (module == NULL) {
+ dev_err(dev, "module %d not valid\n", module_id);
+ return -ENXIO;
+ }
+
+ header = IPC_GLB_TYPE(IPC_GLB_MODULE_OPERATION) |
+ IPC_MODULE_OPERATION(IPC_MODULE_DISABLE) |
+ IPC_MODULE_ID(module_id);
+
+ ret = sst_ipc_tx_message_wait(&hsw->ipc, header, NULL, 0, NULL, 0);
+ if (ret < 0)
+ dev_err(dev, "module disable failed - %d\n", ret);
+ else
+ module->state = SST_MODULE_STATE_INITIALIZED;
+
+ return ret;
+}
+
+int sst_hsw_module_set_param(struct sst_hsw *hsw,
+ u32 module_id, u32 instance_id, u32 parameter_id,
+ u32 param_size, char *param)
+{
+ int ret;
+ unsigned char *data = NULL;
+ u32 header = 0;
+ u32 payload_size = 0, transfer_parameter_size = 0;
+ dma_addr_t dma_addr = 0;
+ struct sst_hsw_transfer_parameter *parameter;
+ struct device *dev = hsw->dev;
+
+ header = IPC_GLB_TYPE(IPC_GLB_MODULE_OPERATION) |
+ IPC_MODULE_OPERATION(IPC_MODULE_SET_PARAMETER) |
+ IPC_MODULE_ID(module_id);
+ dev_dbg(dev, "sst_hsw_module_set_param header=%x\n", header);
+
+ payload_size = param_size +
+ sizeof(struct sst_hsw_transfer_parameter) -
+ sizeof(struct sst_hsw_transfer_list);
+ dev_dbg(dev, "parameter size : %d\n", param_size);
+ dev_dbg(dev, "payload size : %d\n", payload_size);
+
+ if (payload_size <= SST_HSW_IPC_MAX_SHORT_PARAMETER_SIZE) {
+ /* short parameter, mailbox can contain data */
+ dev_dbg(dev, "transfer parameter size : %d\n",
+ transfer_parameter_size);
+
+ transfer_parameter_size = ALIGN(payload_size, 4);
+ dev_dbg(dev, "transfer parameter aligned size : %d\n",
+ transfer_parameter_size);
+
+ parameter = kzalloc(transfer_parameter_size, GFP_KERNEL);
+ if (parameter == NULL)
+ return -ENOMEM;
+
+ memcpy(parameter->data, param, param_size);
+ } else {
+ dev_warn(dev, "transfer parameter size too large!");
+ return 0;
+ }
+
+ parameter->parameter_id = parameter_id;
+ parameter->data_size = param_size;
+
+ ret = sst_ipc_tx_message_wait(&hsw->ipc, header,
+ parameter, transfer_parameter_size , NULL, 0);
+ if (ret < 0)
+ dev_err(dev, "ipc: module set parameter failed - %d\n", ret);
+
+ kfree(parameter);
+
+ if (data)
+ dma_free_coherent(hsw->dsp->dma_dev,
+ param_size, (void *)data, dma_addr);
+
+ return ret;
+}
+
+static struct sst_dsp_device hsw_dev = {
+ .thread = hsw_irq_thread,
+ .ops = &haswell_ops,
+};
+
+static void hsw_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg)
+{
+ /* send the message */
+ sst_dsp_outbox_write(ipc->dsp, msg->tx_data, msg->tx_size);
+ sst_dsp_ipc_msg_tx(ipc->dsp, msg->header);
+}
+
+static void hsw_shim_dbg(struct sst_generic_ipc *ipc, const char *text)
+{
+ struct sst_dsp *sst = ipc->dsp;
+ u32 isr, ipcd, imrx, ipcx;
+
+ ipcx = sst_dsp_shim_read_unlocked(sst, SST_IPCX);
+ isr = sst_dsp_shim_read_unlocked(sst, SST_ISRX);
+ ipcd = sst_dsp_shim_read_unlocked(sst, SST_IPCD);
+ imrx = sst_dsp_shim_read_unlocked(sst, SST_IMRX);
+
+ dev_err(ipc->dev,
+ "ipc: --%s-- ipcx 0x%8.8x isr 0x%8.8x ipcd 0x%8.8x imrx 0x%8.8x\n",
+ text, ipcx, isr, ipcd, imrx);
+}
+
+static void hsw_tx_data_copy(struct ipc_message *msg, char *tx_data,
+ size_t tx_size)
+{
+ memcpy(msg->tx_data, tx_data, tx_size);
+}
+
+static u64 hsw_reply_msg_match(u64 header, u64 *mask)
+{
+ /* clear reply bits & status bits */
+ header &= ~(IPC_STATUS_MASK | IPC_GLB_REPLY_MASK);
+ *mask = (u64)-1;
+
+ return header;
+}
+
+int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
+{
+ struct sst_hsw_ipc_fw_version version;
+ struct sst_hsw *hsw;
+ struct sst_generic_ipc *ipc;
+ int ret;
+
+ dev_dbg(dev, "initialising Audio DSP IPC\n");
+
+ hsw = devm_kzalloc(dev, sizeof(*hsw), GFP_KERNEL);
+ if (hsw == NULL)
+ return -ENOMEM;
+
+ ipc = &hsw->ipc;
+ ipc->dev = dev;
+ ipc->ops.tx_msg = hsw_tx_msg;
+ ipc->ops.shim_dbg = hsw_shim_dbg;
+ ipc->ops.tx_data_copy = hsw_tx_data_copy;
+ ipc->ops.reply_msg_match = hsw_reply_msg_match;
+
+ ret = sst_ipc_init(ipc);
+ if (ret != 0)
+ goto ipc_init_err;
+
+ INIT_LIST_HEAD(&hsw->stream_list);
+ init_waitqueue_head(&hsw->boot_wait);
+ hsw_dev.thread_context = hsw;
+
+ /* init SST shim */
+ hsw->dsp = sst_dsp_new(dev, &hsw_dev, pdata);
+ if (hsw->dsp == NULL) {
+ ret = -ENODEV;
+ goto dsp_new_err;
+ }
+
+ ipc->dsp = hsw->dsp;
+
+ /* allocate DMA buffer for context storage */
+ hsw->dx_context = dma_alloc_coherent(hsw->dsp->dma_dev,
+ SST_HSW_DX_CONTEXT_SIZE, &hsw->dx_context_paddr, GFP_KERNEL);
+ if (hsw->dx_context == NULL) {
+ ret = -ENOMEM;
+ goto dma_err;
+ }
+
+ /* keep the DSP in reset state for base FW loading */
+ sst_dsp_reset(hsw->dsp);
+
+ /* load base module and other modules in base firmware image */
+ ret = sst_hsw_module_load(hsw, SST_HSW_MODULE_BASE_FW, 0, "Base");
+ if (ret < 0)
+ goto fw_err;
+
+ /* try to load module waves */
+ sst_hsw_module_load(hsw, SST_HSW_MODULE_WAVES, 0, "intel/IntcPP01.bin");
+
+ /* allocate scratch mem regions */
+ ret = sst_block_alloc_scratch(hsw->dsp);
+ if (ret < 0)
+ goto boot_err;
+
+ /* init param buffer */
+ sst_hsw_reset_param_buf(hsw);
+
+ /* wait for DSP boot completion */
+ sst_dsp_boot(hsw->dsp);
+ ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete,
+ msecs_to_jiffies(IPC_BOOT_MSECS));
+ if (ret == 0) {
+ ret = -EIO;
+ dev_err(hsw->dev, "error: audio DSP boot timeout IPCD 0x%x IPCX 0x%x\n",
+ sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCD),
+ sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX));
+ goto boot_err;
+ }
+
+ /* init module state after boot */
+ sst_hsw_init_module_state(hsw);
+
+ /* get the FW version */
+ sst_hsw_fw_get_version(hsw, &version);
+
+ /* get the globalmixer */
+ ret = sst_hsw_mixer_get_info(hsw);
+ if (ret < 0) {
+ dev_err(hsw->dev, "error: failed to get stream info\n");
+ goto boot_err;
+ }
+
+ pdata->dsp = hsw;
+ return 0;
+
+boot_err:
+ sst_dsp_reset(hsw->dsp);
+ sst_fw_free_all(hsw->dsp);
+fw_err:
+ dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE,
+ hsw->dx_context, hsw->dx_context_paddr);
+dma_err:
+ sst_dsp_free(hsw->dsp);
+dsp_new_err:
+ sst_ipc_fini(ipc);
+ipc_init_err:
+ kfree(hsw);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sst_hsw_dsp_init);
+
+void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata)
+{
+ struct sst_hsw *hsw = pdata->dsp;
+
+ sst_dsp_reset(hsw->dsp);
+ sst_fw_free_all(hsw->dsp);
+ dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE,
+ hsw->dx_context, hsw->dx_context_paddr);
+ sst_dsp_free(hsw->dsp);
+ sst_ipc_fini(&hsw->ipc);
+}
+EXPORT_SYMBOL_GPL(sst_hsw_dsp_free);
--- /dev/null
+/*
+ * Intel SST Haswell/Broadwell IPC Support
+ *
+ * Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __SST_HASWELL_IPC_H
+#define __SST_HASWELL_IPC_H
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <sound/asound.h>
+
+#define SST_HSW_NO_CHANNELS 4
+#define SST_HSW_MAX_DX_REGIONS 14
+#define SST_HSW_DX_CONTEXT_SIZE (640 * 1024)
+#define SST_HSW_CHANNELS_ALL 0xffffffff
+
+#define SST_HSW_FW_LOG_CONFIG_DWORDS 12
+#define SST_HSW_GLOBAL_LOG 15
+
+/**
+ * Upfront defined maximum message size that is
+ * expected by the in/out communication pipes in FW.
+ */
+#define SST_HSW_IPC_MAX_PAYLOAD_SIZE 400
+#define SST_HSW_MAX_INFO_SIZE 64
+#define SST_HSW_BUILD_HASH_LENGTH 40
+#define SST_HSW_IPC_MAX_SHORT_PARAMETER_SIZE 500
+#define WAVES_PARAM_COUNT 128
+#define WAVES_PARAM_LINES 160
+
+struct sst_hsw;
+struct sst_hsw_stream;
+struct sst_hsw_log_stream;
+struct sst_pdata;
+struct sst_module;
+struct sst_module_runtime;
+extern struct sst_ops haswell_ops;
+
+/* Stream Allocate Path ID */
+enum sst_hsw_stream_path_id {
+ SST_HSW_STREAM_PATH_SSP0_OUT = 0,
+ SST_HSW_STREAM_PATH_SSP0_IN = 1,
+ SST_HSW_STREAM_PATH_MAX_PATH_ID = 2,
+};
+
+/* Stream Allocate Stream Type */
+enum sst_hsw_stream_type {
+ SST_HSW_STREAM_TYPE_RENDER = 0,
+ SST_HSW_STREAM_TYPE_SYSTEM = 1,
+ SST_HSW_STREAM_TYPE_CAPTURE = 2,
+ SST_HSW_STREAM_TYPE_LOOPBACK = 3,
+ SST_HSW_STREAM_TYPE_MAX_STREAM_TYPE = 4,
+};
+
+/* Stream Allocate Stream Format */
+enum sst_hsw_stream_format {
+ SST_HSW_STREAM_FORMAT_PCM_FORMAT = 0,
+ SST_HSW_STREAM_FORMAT_MP3_FORMAT = 1,
+ SST_HSW_STREAM_FORMAT_AAC_FORMAT = 2,
+ SST_HSW_STREAM_FORMAT_MAX_FORMAT_ID = 3,
+};
+
+/* Device ID */
+enum sst_hsw_device_id {
+ SST_HSW_DEVICE_SSP_0 = 0,
+ SST_HSW_DEVICE_SSP_1 = 1,
+};
+
+/* Device Master Clock Frequency */
+enum sst_hsw_device_mclk {
+ SST_HSW_DEVICE_MCLK_OFF = 0,
+ SST_HSW_DEVICE_MCLK_FREQ_6_MHZ = 1,
+ SST_HSW_DEVICE_MCLK_FREQ_12_MHZ = 2,
+ SST_HSW_DEVICE_MCLK_FREQ_24_MHZ = 3,
+};
+
+/* Device Clock Master */
+enum sst_hsw_device_mode {
+ SST_HSW_DEVICE_CLOCK_SLAVE = 0,
+ SST_HSW_DEVICE_CLOCK_MASTER = 1,
+ SST_HSW_DEVICE_TDM_CLOCK_MASTER = 2,
+};
+
+/* DX Power State */
+enum sst_hsw_dx_state {
+ SST_HSW_DX_STATE_D0 = 0,
+ SST_HSW_DX_STATE_D1 = 1,
+ SST_HSW_DX_STATE_D3 = 3,
+ SST_HSW_DX_STATE_MAX = 3,
+};
+
+/* Audio stream stage IDs */
+enum sst_hsw_fx_stage_id {
+ SST_HSW_STAGE_ID_WAVES = 0,
+ SST_HSW_STAGE_ID_DTS = 1,
+ SST_HSW_STAGE_ID_DOLBY = 2,
+ SST_HSW_STAGE_ID_BOOST = 3,
+ SST_HSW_STAGE_ID_MAX_FX_ID
+};
+
+/* DX State Type */
+enum sst_hsw_dx_type {
+ SST_HSW_DX_TYPE_FW_IMAGE = 0,
+ SST_HSW_DX_TYPE_MEMORY_DUMP = 1
+};
+
+/* Volume Curve Type*/
+enum sst_hsw_volume_curve {
+ SST_HSW_VOLUME_CURVE_NONE = 0,
+ SST_HSW_VOLUME_CURVE_FADE = 1
+};
+
+/* Sample ordering */
+enum sst_hsw_interleaving {
+ SST_HSW_INTERLEAVING_PER_CHANNEL = 0,
+ SST_HSW_INTERLEAVING_PER_SAMPLE = 1,
+};
+
+/* Channel indices */
+enum sst_hsw_channel_index {
+ SST_HSW_CHANNEL_LEFT = 0,
+ SST_HSW_CHANNEL_CENTER = 1,
+ SST_HSW_CHANNEL_RIGHT = 2,
+ SST_HSW_CHANNEL_LEFT_SURROUND = 3,
+ SST_HSW_CHANNEL_CENTER_SURROUND = 3,
+ SST_HSW_CHANNEL_RIGHT_SURROUND = 4,
+ SST_HSW_CHANNEL_LFE = 7,
+ SST_HSW_CHANNEL_INVALID = 0xF,
+};
+
+/* List of supported channel maps. */
+enum sst_hsw_channel_config {
+ SST_HSW_CHANNEL_CONFIG_MONO = 0, /* mono only. */
+ SST_HSW_CHANNEL_CONFIG_STEREO = 1, /* L & R. */
+ SST_HSW_CHANNEL_CONFIG_2_POINT_1 = 2, /* L, R & LFE; PCM only. */
+ SST_HSW_CHANNEL_CONFIG_3_POINT_0 = 3, /* L, C & R; MP3 & AAC only. */
+ SST_HSW_CHANNEL_CONFIG_3_POINT_1 = 4, /* L, C, R & LFE; PCM only. */
+ SST_HSW_CHANNEL_CONFIG_QUATRO = 5, /* L, R, Ls & Rs; PCM only. */
+ SST_HSW_CHANNEL_CONFIG_4_POINT_0 = 6, /* L, C, R & Cs; MP3 & AAC only. */
+ SST_HSW_CHANNEL_CONFIG_5_POINT_0 = 7, /* L, C, R, Ls & Rs. */
+ SST_HSW_CHANNEL_CONFIG_5_POINT_1 = 8, /* L, C, R, Ls, Rs & LFE. */
+ SST_HSW_CHANNEL_CONFIG_DUAL_MONO = 9, /* One channel replicated in two. */
+ SST_HSW_CHANNEL_CONFIG_INVALID,
+};
+
+/* List of supported bit depths. */
+enum sst_hsw_bitdepth {
+ SST_HSW_DEPTH_8BIT = 8,
+ SST_HSW_DEPTH_16BIT = 16,
+ SST_HSW_DEPTH_24BIT = 24, /* Default. */
+ SST_HSW_DEPTH_32BIT = 32,
+ SST_HSW_DEPTH_INVALID = 33,
+};
+
+enum sst_hsw_module_id {
+ SST_HSW_MODULE_BASE_FW = 0x0,
+ SST_HSW_MODULE_MP3 = 0x1,
+ SST_HSW_MODULE_AAC_5_1 = 0x2,
+ SST_HSW_MODULE_AAC_2_0 = 0x3,
+ SST_HSW_MODULE_SRC = 0x4,
+ SST_HSW_MODULE_WAVES = 0x5,
+ SST_HSW_MODULE_DOLBY = 0x6,
+ SST_HSW_MODULE_BOOST = 0x7,
+ SST_HSW_MODULE_LPAL = 0x8,
+ SST_HSW_MODULE_DTS = 0x9,
+ SST_HSW_MODULE_PCM_CAPTURE = 0xA,
+ SST_HSW_MODULE_PCM_SYSTEM = 0xB,
+ SST_HSW_MODULE_PCM_REFERENCE = 0xC,
+ SST_HSW_MODULE_PCM = 0xD,
+ SST_HSW_MODULE_BLUETOOTH_RENDER_MODULE = 0xE,
+ SST_HSW_MODULE_BLUETOOTH_CAPTURE_MODULE = 0xF,
+ SST_HSW_MAX_MODULE_ID,
+};
+
+enum sst_hsw_performance_action {
+ SST_HSW_PERF_START = 0,
+ SST_HSW_PERF_STOP = 1,
+};
+
+struct sst_hsw_transfer_info {
+ uint32_t destination; /* destination address */
+ uint32_t reverse:1; /* if 1 data flows from destination */
+ uint32_t size:31; /* transfer size in bytes.*/
+ uint16_t first_page_offset; /* offset to data in the first page. */
+ uint8_t packed_pages; /* page addresses. Each occupies 20 bits */
+} __attribute__((packed));
+
+struct sst_hsw_transfer_list {
+ uint32_t transfers_count;
+ struct sst_hsw_transfer_info transfers;
+} __attribute__((packed));
+
+struct sst_hsw_transfer_parameter {
+ uint32_t parameter_id;
+ uint32_t data_size;
+ union {
+ uint8_t data[1];
+ struct sst_hsw_transfer_list transfer_list;
+ };
+} __attribute__((packed));
+
+/* SST firmware module info */
+struct sst_hsw_module_info {
+ u8 name[SST_HSW_MAX_INFO_SIZE];
+ u8 version[SST_HSW_MAX_INFO_SIZE];
+} __attribute__((packed));
+
+/* Module entry point */
+struct sst_hsw_module_entry {
+ enum sst_hsw_module_id module_id;
+ u32 entry_point;
+} __attribute__((packed));
+
+/* Module map - alignement matches DSP */
+struct sst_hsw_module_map {
+ u8 module_entries_count;
+ struct sst_hsw_module_entry module_entries[1];
+} __attribute__((packed));
+
+struct sst_hsw_memory_info {
+ u32 offset;
+ u32 size;
+} __attribute__((packed));
+
+struct sst_hsw_fx_enable {
+ struct sst_hsw_module_map module_map;
+ struct sst_hsw_memory_info persistent_mem;
+} __attribute__((packed));
+
+struct sst_hsw_ipc_module_config {
+ struct sst_hsw_module_map map;
+ struct sst_hsw_memory_info persistent_mem;
+ struct sst_hsw_memory_info scratch_mem;
+} __attribute__((packed));
+
+struct sst_hsw_get_fx_param {
+ u32 parameter_id;
+ u32 param_size;
+} __attribute__((packed));
+
+struct sst_hsw_perf_action {
+ u32 action;
+} __attribute__((packed));
+
+struct sst_hsw_perf_data {
+ u64 timestamp;
+ u64 cycles;
+ u64 datatime;
+} __attribute__((packed));
+
+/* FW version */
+struct sst_hsw_ipc_fw_version {
+ u8 build;
+ u8 minor;
+ u8 major;
+ u8 type;
+ u8 fw_build_hash[SST_HSW_BUILD_HASH_LENGTH];
+ u32 fw_log_providers_hash;
+} __attribute__((packed));
+
+/* Stream ring info */
+struct sst_hsw_ipc_stream_ring {
+ u32 ring_pt_address;
+ u32 num_pages;
+ u32 ring_size;
+ u32 ring_offset;
+ u32 ring_first_pfn;
+} __attribute__((packed));
+
+/* Debug Dump Log Enable Request */
+struct sst_hsw_ipc_debug_log_enable_req {
+ struct sst_hsw_ipc_stream_ring ringinfo;
+ u32 config[SST_HSW_FW_LOG_CONFIG_DWORDS];
+} __attribute__((packed));
+
+/* Debug Dump Log Reply */
+struct sst_hsw_ipc_debug_log_reply {
+ u32 log_buffer_begining;
+ u32 log_buffer_size;
+} __attribute__((packed));
+
+/* Stream glitch position */
+struct sst_hsw_ipc_stream_glitch_position {
+ u32 glitch_type;
+ u32 present_pos;
+ u32 write_pos;
+} __attribute__((packed));
+
+/* Stream get position */
+struct sst_hsw_ipc_stream_get_position {
+ u32 position;
+ u32 fw_cycle_count;
+} __attribute__((packed));
+
+/* Stream set position */
+struct sst_hsw_ipc_stream_set_position {
+ u32 position;
+ u32 end_of_buffer;
+} __attribute__((packed));
+
+/* Stream Free Request */
+struct sst_hsw_ipc_stream_free_req {
+ u8 stream_id;
+ u8 reserved[3];
+} __attribute__((packed));
+
+/* Set Volume Request */
+struct sst_hsw_ipc_volume_req {
+ u32 channel;
+ u32 target_volume;
+ u64 curve_duration;
+ u32 curve_type;
+} __attribute__((packed));
+
+/* Device Configuration Request */
+struct sst_hsw_ipc_device_config_req {
+ u32 ssp_interface;
+ u32 clock_frequency;
+ u32 mode;
+ u16 clock_divider;
+ u8 channels;
+ u8 reserved;
+} __attribute__((packed));
+
+/* Audio Data formats */
+struct sst_hsw_audio_data_format_ipc {
+ u32 frequency;
+ u32 bitdepth;
+ u32 map;
+ u32 config;
+ u32 style;
+ u8 ch_num;
+ u8 valid_bit;
+ u8 reserved[2];
+} __attribute__((packed));
+
+/* Stream Allocate Request */
+struct sst_hsw_ipc_stream_alloc_req {
+ u8 path_id;
+ u8 stream_type;
+ u8 format_id;
+ u8 reserved;
+ struct sst_hsw_audio_data_format_ipc format;
+ struct sst_hsw_ipc_stream_ring ringinfo;
+ struct sst_hsw_module_map map;
+ struct sst_hsw_memory_info persistent_mem;
+ struct sst_hsw_memory_info scratch_mem;
+ u32 number_of_notifications;
+} __attribute__((packed));
+
+/* Stream Allocate Reply */
+struct sst_hsw_ipc_stream_alloc_reply {
+ u32 stream_hw_id;
+ u32 mixer_hw_id; // returns rate ????
+ u32 read_position_register_address;
+ u32 presentation_position_register_address;
+ u32 peak_meter_register_address[SST_HSW_NO_CHANNELS];
+ u32 volume_register_address[SST_HSW_NO_CHANNELS];
+} __attribute__((packed));
+
+/* Get Mixer Stream Info */
+struct sst_hsw_ipc_stream_info_reply {
+ u32 mixer_hw_id;
+ u32 peak_meter_register_address[SST_HSW_NO_CHANNELS];
+ u32 volume_register_address[SST_HSW_NO_CHANNELS];
+} __attribute__((packed));
+
+/* DX State Request */
+struct sst_hsw_ipc_dx_req {
+ u8 state;
+ u8 reserved[3];
+} __attribute__((packed));
+
+/* DX State Reply Memory Info Item */
+struct sst_hsw_ipc_dx_memory_item {
+ u32 offset;
+ u32 size;
+ u32 source;
+} __attribute__((packed));
+
+/* DX State Reply */
+struct sst_hsw_ipc_dx_reply {
+ u32 entries_no;
+ struct sst_hsw_ipc_dx_memory_item mem_info[SST_HSW_MAX_DX_REGIONS];
+} __attribute__((packed));
+
+struct sst_hsw_ipc_fw_version;
+
+/* SST Init & Free */
+struct sst_hsw *sst_hsw_new(struct device *dev, const u8 *fw, size_t fw_length,
+ u32 fw_offset);
+void sst_hsw_free(struct sst_hsw *hsw);
+int sst_hsw_fw_get_version(struct sst_hsw *hsw,
+ struct sst_hsw_ipc_fw_version *version);
+u32 create_channel_map(enum sst_hsw_channel_config config);
+
+/* Stream Mixer Controls - */
+int sst_hsw_stream_set_volume(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 volume);
+int sst_hsw_stream_get_volume(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 *volume);
+
+/* Global Mixer Controls - */
+int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
+ u32 volume);
+int sst_hsw_mixer_get_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
+ u32 *volume);
+
+/* Stream API */
+struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id,
+ u32 (*get_write_position)(struct sst_hsw_stream *stream, void *data),
+ void *data);
+
+int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream);
+
+/* Stream Configuration */
+int sst_hsw_stream_format(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+ enum sst_hsw_stream_path_id path_id,
+ enum sst_hsw_stream_type stream_type,
+ enum sst_hsw_stream_format format_id);
+
+int sst_hsw_stream_buffer(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+ u32 ring_pt_address, u32 num_pages,
+ u32 ring_size, u32 ring_offset, u32 ring_first_pfn);
+
+int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream);
+
+int sst_hsw_stream_set_valid(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+ u32 bits);
+int sst_hsw_stream_set_rate(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+ int rate);
+int sst_hsw_stream_set_bits(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+ enum sst_hsw_bitdepth bits);
+int sst_hsw_stream_set_channels(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream, int channels);
+int sst_hsw_stream_set_map_config(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream, u32 map,
+ enum sst_hsw_channel_config config);
+int sst_hsw_stream_set_style(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+ enum sst_hsw_interleaving style);
+int sst_hsw_stream_set_module_info(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream, struct sst_module_runtime *runtime);
+int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream, u32 offset, u32 size);
+int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream, u32 offset, u32 size);
+snd_pcm_uframes_t sst_hsw_stream_get_old_position(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream);
+void sst_hsw_stream_set_old_position(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream, snd_pcm_uframes_t val);
+bool sst_hsw_stream_get_silence_start(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream);
+void sst_hsw_stream_set_silence_start(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream, bool val);
+int sst_hsw_mixer_get_info(struct sst_hsw *hsw);
+
+/* Stream ALSA trigger operations */
+int sst_hsw_stream_pause(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+ int wait);
+int sst_hsw_stream_resume(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
+ int wait);
+int sst_hsw_stream_reset(struct sst_hsw *hsw, struct sst_hsw_stream *stream);
+
+/* Stream pointer positions */
+int sst_hsw_stream_get_read_pos(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream, u32 *position);
+int sst_hsw_stream_get_write_pos(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream, u32 *position);
+u32 sst_hsw_get_dsp_position(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream);
+u64 sst_hsw_get_dsp_presentation_position(struct sst_hsw *hsw,
+ struct sst_hsw_stream *stream);
+
+/* HW port config */
+int sst_hsw_device_set_config(struct sst_hsw *hsw,
+ enum sst_hsw_device_id dev, enum sst_hsw_device_mclk mclk,
+ enum sst_hsw_device_mode mode, u32 clock_divider);
+
+/* DX Config */
+int sst_hsw_dx_set_state(struct sst_hsw *hsw,
+ enum sst_hsw_dx_state state, struct sst_hsw_ipc_dx_reply *dx);
+
+/* init */
+int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata);
+void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata);
+struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw);
+
+/* fw module function */
+void sst_hsw_init_module_state(struct sst_hsw *hsw);
+bool sst_hsw_is_module_loaded(struct sst_hsw *hsw, u32 module_id);
+bool sst_hsw_is_module_active(struct sst_hsw *hsw, u32 module_id);
+void sst_hsw_set_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id);
+void sst_hsw_set_module_disabled_rtd3(struct sst_hsw *hsw, u32 module_id);
+bool sst_hsw_is_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id);
+void sst_hsw_reset_param_buf(struct sst_hsw *hsw);
+int sst_hsw_store_param_line(struct sst_hsw *hsw, u8 *buf);
+int sst_hsw_load_param_line(struct sst_hsw *hsw, u8 *buf);
+int sst_hsw_launch_param_buf(struct sst_hsw *hsw);
+
+int sst_hsw_module_load(struct sst_hsw *hsw,
+ u32 module_id, u32 instance_id, char *name);
+int sst_hsw_module_enable(struct sst_hsw *hsw,
+ u32 module_id, u32 instance_id);
+int sst_hsw_module_disable(struct sst_hsw *hsw,
+ u32 module_id, u32 instance_id);
+int sst_hsw_module_set_param(struct sst_hsw *hsw,
+ u32 module_id, u32 instance_id, u32 parameter_id,
+ u32 param_size, char *param);
+
+/* runtime module management */
+struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw,
+ int mod_id, int offset);
+void sst_hsw_runtime_module_free(struct sst_module_runtime *runtime);
+
+/* PM */
+int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw);
+int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw);
+int sst_hsw_dsp_load(struct sst_hsw *hsw);
+int sst_hsw_dsp_runtime_sleep(struct sst_hsw *hsw);
+
+#endif
--- /dev/null
+/*
+ * Intel SST Haswell/Broadwell PCM Support
+ *
+ * Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/compress_driver.h>
+
+#include "../haswell/sst-haswell-ipc.h"
+#include "../common/sst-dsp-priv.h"
+#include "../common/sst-dsp.h"
+
+#define HSW_PCM_COUNT 6
+#define HSW_VOLUME_MAX 0x7FFFFFFF /* 0dB */
+
+#define SST_OLD_POSITION(d, r, o) ((d) + \
+ frames_to_bytes(r, o))
+#define SST_SAMPLES(r, x) (bytes_to_samples(r, \
+ frames_to_bytes(r, (x))))
+
+/* simple volume table */
+static const u32 volume_map[] = {
+ HSW_VOLUME_MAX >> 30,
+ HSW_VOLUME_MAX >> 29,
+ HSW_VOLUME_MAX >> 28,
+ HSW_VOLUME_MAX >> 27,
+ HSW_VOLUME_MAX >> 26,
+ HSW_VOLUME_MAX >> 25,
+ HSW_VOLUME_MAX >> 24,
+ HSW_VOLUME_MAX >> 23,
+ HSW_VOLUME_MAX >> 22,
+ HSW_VOLUME_MAX >> 21,
+ HSW_VOLUME_MAX >> 20,
+ HSW_VOLUME_MAX >> 19,
+ HSW_VOLUME_MAX >> 18,
+ HSW_VOLUME_MAX >> 17,
+ HSW_VOLUME_MAX >> 16,
+ HSW_VOLUME_MAX >> 15,
+ HSW_VOLUME_MAX >> 14,
+ HSW_VOLUME_MAX >> 13,
+ HSW_VOLUME_MAX >> 12,
+ HSW_VOLUME_MAX >> 11,
+ HSW_VOLUME_MAX >> 10,
+ HSW_VOLUME_MAX >> 9,
+ HSW_VOLUME_MAX >> 8,
+ HSW_VOLUME_MAX >> 7,
+ HSW_VOLUME_MAX >> 6,
+ HSW_VOLUME_MAX >> 5,
+ HSW_VOLUME_MAX >> 4,
+ HSW_VOLUME_MAX >> 3,
+ HSW_VOLUME_MAX >> 2,
+ HSW_VOLUME_MAX >> 1,
+ HSW_VOLUME_MAX >> 0,
+};
+
+#define HSW_PCM_PERIODS_MAX 64
+#define HSW_PCM_PERIODS_MIN 2
+
+#define HSW_PCM_DAI_ID_SYSTEM 0
+#define HSW_PCM_DAI_ID_OFFLOAD0 1
+#define HSW_PCM_DAI_ID_OFFLOAD1 2
+#define HSW_PCM_DAI_ID_LOOPBACK 3
+
+
+static const struct snd_pcm_hardware hsw_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP |
+ SNDRV_PCM_INFO_DRAIN_TRIGGER,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .period_bytes_min = PAGE_SIZE,
+ .period_bytes_max = (HSW_PCM_PERIODS_MAX / HSW_PCM_PERIODS_MIN) * PAGE_SIZE,
+ .periods_min = HSW_PCM_PERIODS_MIN,
+ .periods_max = HSW_PCM_PERIODS_MAX,
+ .buffer_bytes_max = HSW_PCM_PERIODS_MAX * PAGE_SIZE,
+};
+
+struct hsw_pcm_module_map {
+ int dai_id;
+ int stream;
+ enum sst_hsw_module_id mod_id;
+};
+
+/* private data for each PCM DSP stream */
+struct hsw_pcm_data {
+ int dai_id;
+ struct sst_hsw_stream *stream;
+ struct sst_module_runtime *runtime;
+ struct sst_module_runtime_context context;
+ struct snd_pcm *hsw_pcm;
+ u32 volume[2];
+ struct snd_pcm_substream *substream;
+ struct snd_compr_stream *cstream;
+ unsigned int wpos;
+ struct mutex mutex;
+ bool allocated;
+ int persistent_offset;
+};
+
+enum hsw_pm_state {
+ HSW_PM_STATE_D0 = 0,
+ HSW_PM_STATE_RTD3 = 1,
+ HSW_PM_STATE_D3 = 2,
+};
+
+/* private data for the driver */
+struct hsw_priv_data {
+ /* runtime DSP */
+ struct sst_hsw *hsw;
+ struct device *dev;
+ enum hsw_pm_state pm_state;
+ struct snd_soc_card *soc_card;
+ struct sst_module_runtime *runtime_waves; /* sound effect module */
+
+ /* page tables */
+ struct snd_dma_buffer dmab[HSW_PCM_COUNT][2];
+
+ /* DAI data */
+ struct hsw_pcm_data pcm[HSW_PCM_COUNT][2];
+};
+
+
+/* static mappings between PCMs and modules - may be dynamic in future */
+static struct hsw_pcm_module_map mod_map[] = {
+ {HSW_PCM_DAI_ID_SYSTEM, 0, SST_HSW_MODULE_PCM_SYSTEM},
+ {HSW_PCM_DAI_ID_OFFLOAD0, 0, SST_HSW_MODULE_PCM},
+ {HSW_PCM_DAI_ID_OFFLOAD1, 0, SST_HSW_MODULE_PCM},
+ {HSW_PCM_DAI_ID_LOOPBACK, 1, SST_HSW_MODULE_PCM_REFERENCE},
+ {HSW_PCM_DAI_ID_SYSTEM, 1, SST_HSW_MODULE_PCM_CAPTURE},
+};
+
+static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data);
+
+static inline u32 hsw_mixer_to_ipc(unsigned int value)
+{
+ if (value >= ARRAY_SIZE(volume_map))
+ return volume_map[0];
+ else
+ return volume_map[value];
+}
+
+static inline unsigned int hsw_ipc_to_mixer(u32 value)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(volume_map); i++) {
+ if (volume_map[i] >= value)
+ return i;
+ }
+
+ return i - 1;
+}
+
+static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct hsw_priv_data *pdata =
+ snd_soc_platform_get_drvdata(platform);
+ struct hsw_pcm_data *pcm_data;
+ struct sst_hsw *hsw = pdata->hsw;
+ u32 volume;
+ int dai, stream;
+
+ dai = mod_map[mc->reg].dai_id;
+ stream = mod_map[mc->reg].stream;
+ pcm_data = &pdata->pcm[dai][stream];
+
+ mutex_lock(&pcm_data->mutex);
+ pm_runtime_get_sync(pdata->dev);
+
+ if (!pcm_data->stream) {
+ pcm_data->volume[0] =
+ hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
+ pcm_data->volume[1] =
+ hsw_mixer_to_ipc(ucontrol->value.integer.value[1]);
+ pm_runtime_mark_last_busy(pdata->dev);
+ pm_runtime_put_autosuspend(pdata->dev);
+ mutex_unlock(&pcm_data->mutex);
+ return 0;
+ }
+
+ if (ucontrol->value.integer.value[0] ==
+ ucontrol->value.integer.value[1]) {
+ volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
+ /* apply volume value to all channels */
+ sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, SST_HSW_CHANNELS_ALL, volume);
+ } else {
+ volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
+ sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 0, volume);
+ volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[1]);
+ sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 1, volume);
+ }
+
+ pm_runtime_mark_last_busy(pdata->dev);
+ pm_runtime_put_autosuspend(pdata->dev);
+ mutex_unlock(&pcm_data->mutex);
+ return 0;
+}
+
+static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct hsw_priv_data *pdata =
+ snd_soc_platform_get_drvdata(platform);
+ struct hsw_pcm_data *pcm_data;
+ struct sst_hsw *hsw = pdata->hsw;
+ u32 volume;
+ int dai, stream;
+
+ dai = mod_map[mc->reg].dai_id;
+ stream = mod_map[mc->reg].stream;
+ pcm_data = &pdata->pcm[dai][stream];
+
+ mutex_lock(&pcm_data->mutex);
+ pm_runtime_get_sync(pdata->dev);
+
+ if (!pcm_data->stream) {
+ ucontrol->value.integer.value[0] =
+ hsw_ipc_to_mixer(pcm_data->volume[0]);
+ ucontrol->value.integer.value[1] =
+ hsw_ipc_to_mixer(pcm_data->volume[1]);
+ pm_runtime_mark_last_busy(pdata->dev);
+ pm_runtime_put_autosuspend(pdata->dev);
+ mutex_unlock(&pcm_data->mutex);
+ return 0;
+ }
+
+ sst_hsw_stream_get_volume(hsw, pcm_data->stream, 0, 0, &volume);
+ ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume);
+ sst_hsw_stream_get_volume(hsw, pcm_data->stream, 0, 1, &volume);
+ ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume);
+
+ pm_runtime_mark_last_busy(pdata->dev);
+ pm_runtime_put_autosuspend(pdata->dev);
+ mutex_unlock(&pcm_data->mutex);
+
+ return 0;
+}
+
+static int hsw_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+ struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
+ struct sst_hsw *hsw = pdata->hsw;
+ u32 volume;
+
+ pm_runtime_get_sync(pdata->dev);
+
+ if (ucontrol->value.integer.value[0] ==
+ ucontrol->value.integer.value[1]) {
+
+ volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
+ sst_hsw_mixer_set_volume(hsw, 0, SST_HSW_CHANNELS_ALL, volume);
+
+ } else {
+ volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
+ sst_hsw_mixer_set_volume(hsw, 0, 0, volume);
+
+ volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[1]);
+ sst_hsw_mixer_set_volume(hsw, 0, 1, volume);
+ }
+
+ pm_runtime_mark_last_busy(pdata->dev);
+ pm_runtime_put_autosuspend(pdata->dev);
+ return 0;
+}
+
+static int hsw_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+ struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
+ struct sst_hsw *hsw = pdata->hsw;
+ unsigned int volume = 0;
+
+ pm_runtime_get_sync(pdata->dev);
+ sst_hsw_mixer_get_volume(hsw, 0, 0, &volume);
+ ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume);
+
+ sst_hsw_mixer_get_volume(hsw, 0, 1, &volume);
+ ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume);
+
+ pm_runtime_mark_last_busy(pdata->dev);
+ pm_runtime_put_autosuspend(pdata->dev);
+ return 0;
+}
+
+static int hsw_waves_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+ struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
+ struct sst_hsw *hsw = pdata->hsw;
+ enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES;
+
+ ucontrol->value.integer.value[0] =
+ (sst_hsw_is_module_active(hsw, id) ||
+ sst_hsw_is_module_enabled_rtd3(hsw, id));
+ return 0;
+}
+
+static int hsw_waves_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+ struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
+ struct sst_hsw *hsw = pdata->hsw;
+ int ret = 0;
+ enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES;
+ bool switch_on = (bool)ucontrol->value.integer.value[0];
+
+ /* if module is in RAM on the DSP, apply user settings to module through
+ * ipc. If module is not in RAM on the DSP, store user setting for
+ * track */
+ if (sst_hsw_is_module_loaded(hsw, id)) {
+ if (switch_on == sst_hsw_is_module_active(hsw, id))
+ return 0;
+
+ if (switch_on)
+ ret = sst_hsw_module_enable(hsw, id, 0);
+ else
+ ret = sst_hsw_module_disable(hsw, id, 0);
+ } else {
+ if (switch_on == sst_hsw_is_module_enabled_rtd3(hsw, id))
+ return 0;
+
+ if (switch_on)
+ sst_hsw_set_module_enabled_rtd3(hsw, id);
+ else
+ sst_hsw_set_module_disabled_rtd3(hsw, id);
+ }
+
+ return ret;
+}
+
+static int hsw_waves_param_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+ struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
+ struct sst_hsw *hsw = pdata->hsw;
+
+ /* return a matching line from param buffer */
+ return sst_hsw_load_param_line(hsw, ucontrol->value.bytes.data);
+}
+
+static int hsw_waves_param_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+ struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
+ struct sst_hsw *hsw = pdata->hsw;
+ int ret;
+ enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES;
+ int param_id = ucontrol->value.bytes.data[0];
+ int param_size = WAVES_PARAM_COUNT;
+
+ /* clear param buffer and reset buffer index */
+ if (param_id == 0xFF) {
+ sst_hsw_reset_param_buf(hsw);
+ return 0;
+ }
+
+ /* store params into buffer */
+ ret = sst_hsw_store_param_line(hsw, ucontrol->value.bytes.data);
+ if (ret < 0)
+ return ret;
+
+ if (sst_hsw_is_module_active(hsw, id))
+ ret = sst_hsw_module_set_param(hsw, id, 0, param_id,
+ param_size, ucontrol->value.bytes.data);
+ return ret;
+}
+
+/* TLV used by both global and stream volumes */
+static const DECLARE_TLV_DB_SCALE(hsw_vol_tlv, -9000, 300, 1);
+
+/* System Pin has no volume control */
+static const struct snd_kcontrol_new hsw_volume_controls[] = {
+ /* Global DSP volume */
+ SOC_DOUBLE_EXT_TLV("Master Playback Volume", 0, 0, 8,
+ ARRAY_SIZE(volume_map) - 1, 0,
+ hsw_volume_get, hsw_volume_put, hsw_vol_tlv),
+ /* Offload 0 volume */
+ SOC_DOUBLE_EXT_TLV("Media0 Playback Volume", 1, 0, 8,
+ ARRAY_SIZE(volume_map) - 1, 0,
+ hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
+ /* Offload 1 volume */
+ SOC_DOUBLE_EXT_TLV("Media1 Playback Volume", 2, 0, 8,
+ ARRAY_SIZE(volume_map) - 1, 0,
+ hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
+ /* Mic Capture volume */
+ SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8,
+ ARRAY_SIZE(volume_map) - 1, 0,
+ hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
+ /* enable/disable module waves */
+ SOC_SINGLE_BOOL_EXT("Waves Switch", 0,
+ hsw_waves_switch_get, hsw_waves_switch_put),
+ /* set parameters to module waves */
+ SND_SOC_BYTES_EXT("Waves Set Param", WAVES_PARAM_COUNT,
+ hsw_waves_param_get, hsw_waves_param_put),
+};
+
+/* Create DMA buffer page table for DSP */
+static int create_adsp_page_table(struct snd_pcm_substream *substream,
+ struct hsw_priv_data *pdata, struct snd_soc_pcm_runtime *rtd,
+ unsigned char *dma_area, size_t size, int pcm)
+{
+ struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream);
+ int i, pages, stream = substream->stream;
+
+ pages = snd_sgbuf_aligned_pages(size);
+
+ dev_dbg(rtd->dev, "generating page table for %p size 0x%zu pages %d\n",
+ dma_area, size, pages);
+
+ for (i = 0; i < pages; i++) {
+ u32 idx = (((i << 2) + i)) >> 1;
+ u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT;
+ u32 *pg_table;
+
+ dev_dbg(rtd->dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn);
+
+ pg_table = (u32 *)(pdata->dmab[pcm][stream].area + idx);
+
+ if (i & 1)
+ *pg_table |= (pfn << 4);
+ else
+ *pg_table |= pfn;
+ }
+
+ return 0;
+}
+
+/* this may get called several times by oss emulation */
+static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct hsw_priv_data *pdata =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ struct hsw_pcm_data *pcm_data;
+ struct sst_hsw *hsw = pdata->hsw;
+ struct sst_module *module_data;
+ struct sst_dsp *dsp;
+ struct snd_dma_buffer *dmab;
+ enum sst_hsw_stream_type stream_type;
+ enum sst_hsw_stream_path_id path_id;
+ u32 rate, bits, map, pages, module_id;
+ u8 channels;
+ int ret, dai;
+
+ dai = mod_map[rtd->cpu_dai->id].dai_id;
+ pcm_data = &pdata->pcm[dai][substream->stream];
+
+ /* check if we are being called a subsequent time */
+ if (pcm_data->allocated) {
+ ret = sst_hsw_stream_reset(hsw, pcm_data->stream);
+ if (ret < 0)
+ dev_dbg(rtd->dev, "error: reset stream failed %d\n",
+ ret);
+
+ ret = sst_hsw_stream_free(hsw, pcm_data->stream);
+ if (ret < 0) {
+ dev_dbg(rtd->dev, "error: free stream failed %d\n",
+ ret);
+ return ret;
+ }
+ pcm_data->allocated = false;
+
+ pcm_data->stream = sst_hsw_stream_new(hsw, rtd->cpu_dai->id,
+ hsw_notify_pointer, pcm_data);
+ if (pcm_data->stream == NULL) {
+ dev_err(rtd->dev, "error: failed to create stream\n");
+ return -EINVAL;
+ }
+ }
+
+ /* stream direction */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ path_id = SST_HSW_STREAM_PATH_SSP0_OUT;
+ else
+ path_id = SST_HSW_STREAM_PATH_SSP0_IN;
+
+ /* DSP stream type depends on DAI ID */
+ switch (rtd->cpu_dai->id) {
+ case 0:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ stream_type = SST_HSW_STREAM_TYPE_SYSTEM;
+ module_id = SST_HSW_MODULE_PCM_SYSTEM;
+ }
+ else {
+ stream_type = SST_HSW_STREAM_TYPE_CAPTURE;
+ module_id = SST_HSW_MODULE_PCM_CAPTURE;
+ }
+ break;
+ case 1:
+ case 2:
+ stream_type = SST_HSW_STREAM_TYPE_RENDER;
+ module_id = SST_HSW_MODULE_PCM;
+ break;
+ case 3:
+ /* path ID needs to be OUT for loopback */
+ stream_type = SST_HSW_STREAM_TYPE_LOOPBACK;
+ path_id = SST_HSW_STREAM_PATH_SSP0_OUT;
+ module_id = SST_HSW_MODULE_PCM_REFERENCE;
+ break;
+ default:
+ dev_err(rtd->dev, "error: invalid DAI ID %d\n",
+ rtd->cpu_dai->id);
+ return -EINVAL;
+ };
+
+ ret = sst_hsw_stream_format(hsw, pcm_data->stream,
+ path_id, stream_type, SST_HSW_STREAM_FORMAT_PCM_FORMAT);
+ if (ret < 0) {
+ dev_err(rtd->dev, "error: failed to set format %d\n", ret);
+ return ret;
+ }
+
+ rate = params_rate(params);
+ ret = sst_hsw_stream_set_rate(hsw, pcm_data->stream, rate);
+ if (ret < 0) {
+ dev_err(rtd->dev, "error: could not set rate %d\n", rate);
+ return ret;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bits = SST_HSW_DEPTH_16BIT;
+ sst_hsw_stream_set_valid(hsw, pcm_data->stream, 16);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bits = SST_HSW_DEPTH_32BIT;
+ sst_hsw_stream_set_valid(hsw, pcm_data->stream, 24);
+ break;
+ case SNDRV_PCM_FORMAT_S8:
+ bits = SST_HSW_DEPTH_8BIT;
+ sst_hsw_stream_set_valid(hsw, pcm_data->stream, 8);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ bits = SST_HSW_DEPTH_32BIT;
+ sst_hsw_stream_set_valid(hsw, pcm_data->stream, 32);
+ break;
+ default:
+ dev_err(rtd->dev, "error: invalid format %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ ret = sst_hsw_stream_set_bits(hsw, pcm_data->stream, bits);
+ if (ret < 0) {
+ dev_err(rtd->dev, "error: could not set bits %d\n", bits);
+ return ret;
+ }
+
+ channels = params_channels(params);
+ map = create_channel_map(SST_HSW_CHANNEL_CONFIG_STEREO);
+ sst_hsw_stream_set_map_config(hsw, pcm_data->stream,
+ map, SST_HSW_CHANNEL_CONFIG_STEREO);
+
+ ret = sst_hsw_stream_set_channels(hsw, pcm_data->stream, channels);
+ if (ret < 0) {
+ dev_err(rtd->dev, "error: could not set channels %d\n",
+ channels);
+ return ret;
+ }
+
+ ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+ if (ret < 0) {
+ dev_err(rtd->dev, "error: could not allocate %d bytes for PCM %d\n",
+ params_buffer_bytes(params), ret);
+ return ret;
+ }
+
+ dmab = snd_pcm_get_dma_buf(substream);
+
+ ret = create_adsp_page_table(substream, pdata, rtd, runtime->dma_area,
+ runtime->dma_bytes, rtd->cpu_dai->id);
+ if (ret < 0)
+ return ret;
+
+ sst_hsw_stream_set_style(hsw, pcm_data->stream,
+ SST_HSW_INTERLEAVING_PER_CHANNEL);
+
+ if (runtime->dma_bytes % PAGE_SIZE)
+ pages = (runtime->dma_bytes / PAGE_SIZE) + 1;
+ else
+ pages = runtime->dma_bytes / PAGE_SIZE;
+
+ ret = sst_hsw_stream_buffer(hsw, pcm_data->stream,
+ pdata->dmab[rtd->cpu_dai->id][substream->stream].addr,
+ pages, runtime->dma_bytes, 0,
+ snd_sgbuf_get_addr(dmab, 0) >> PAGE_SHIFT);
+ if (ret < 0) {
+ dev_err(rtd->dev, "error: failed to set DMA buffer %d\n", ret);
+ return ret;
+ }
+
+ dsp = sst_hsw_get_dsp(hsw);
+
+ module_data = sst_module_get_from_id(dsp, module_id);
+ if (module_data == NULL) {
+ dev_err(rtd->dev, "error: failed to get module config\n");
+ return -EINVAL;
+ }
+
+ sst_hsw_stream_set_module_info(hsw, pcm_data->stream,
+ pcm_data->runtime);
+
+ ret = sst_hsw_stream_commit(hsw, pcm_data->stream);
+ if (ret < 0) {
+ dev_err(rtd->dev, "error: failed to commit stream %d\n", ret);
+ return ret;
+ }
+
+ if (!pcm_data->allocated) {
+ /* Set previous saved volume */
+ sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
+ 0, pcm_data->volume[0]);
+ sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
+ 1, pcm_data->volume[1]);
+ pcm_data->allocated = true;
+ }
+
+ ret = sst_hsw_stream_pause(hsw, pcm_data->stream, 1);
+ if (ret < 0)
+ dev_err(rtd->dev, "error: failed to pause %d\n", ret);
+
+ return 0;
+}
+
+static int hsw_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ snd_pcm_lib_free_pages(substream);
+ return 0;
+}
+
+static int hsw_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct hsw_priv_data *pdata =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ struct hsw_pcm_data *pcm_data;
+ struct sst_hsw_stream *sst_stream;
+ struct sst_hsw *hsw = pdata->hsw;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ snd_pcm_uframes_t pos;
+ int dai;
+
+ dai = mod_map[rtd->cpu_dai->id].dai_id;
+ pcm_data = &pdata->pcm[dai][substream->stream];
+ sst_stream = pcm_data->stream;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ sst_hsw_stream_set_silence_start(hsw, sst_stream, false);
+ sst_hsw_stream_resume(hsw, pcm_data->stream, 0);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ sst_hsw_stream_set_silence_start(hsw, sst_stream, false);
+ sst_hsw_stream_pause(hsw, pcm_data->stream, 0);
+ break;
+ case SNDRV_PCM_TRIGGER_DRAIN:
+ pos = runtime->control->appl_ptr % runtime->buffer_size;
+ sst_hsw_stream_set_old_position(hsw, pcm_data->stream, pos);
+ sst_hsw_stream_set_silence_start(hsw, sst_stream, true);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data)
+{
+ struct hsw_pcm_data *pcm_data = data;
+ struct snd_pcm_substream *substream = pcm_data->substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct hsw_priv_data *pdata =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ struct sst_hsw *hsw = pdata->hsw;
+ u32 pos;
+ snd_pcm_uframes_t position = bytes_to_frames(runtime,
+ sst_hsw_get_dsp_position(hsw, pcm_data->stream));
+ unsigned char *dma_area = runtime->dma_area;
+ snd_pcm_uframes_t dma_frames =
+ bytes_to_frames(runtime, runtime->dma_bytes);
+ snd_pcm_uframes_t old_position;
+ ssize_t samples;
+
+ pos = frames_to_bytes(runtime,
+ (runtime->control->appl_ptr % runtime->buffer_size));
+
+ dev_vdbg(rtd->dev, "PCM: App pointer %d bytes\n", pos);
+
+ /* SST fw don't know where to stop dma
+ * So, SST driver need to clean the data which has been consumed
+ */
+ if (dma_area == NULL || dma_frames <= 0
+ || (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ || !sst_hsw_stream_get_silence_start(hsw, stream)) {
+ snd_pcm_period_elapsed(substream);
+ return pos;
+ }
+
+ old_position = sst_hsw_stream_get_old_position(hsw, stream);
+ if (position > old_position) {
+ if (position < dma_frames) {
+ samples = SST_SAMPLES(runtime, position - old_position);
+ snd_pcm_format_set_silence(runtime->format,
+ SST_OLD_POSITION(dma_area,
+ runtime, old_position),
+ samples);
+ } else
+ dev_err(rtd->dev, "PCM: position is wrong\n");
+ } else {
+ if (old_position < dma_frames) {
+ samples = SST_SAMPLES(runtime,
+ dma_frames - old_position);
+ snd_pcm_format_set_silence(runtime->format,
+ SST_OLD_POSITION(dma_area,
+ runtime, old_position),
+ samples);
+ } else
+ dev_err(rtd->dev, "PCM: dma_bytes is wrong\n");
+ if (position < dma_frames) {
+ samples = SST_SAMPLES(runtime, position);
+ snd_pcm_format_set_silence(runtime->format,
+ dma_area, samples);
+ } else
+ dev_err(rtd->dev, "PCM: position is wrong\n");
+ }
+ sst_hsw_stream_set_old_position(hsw, stream, position);
+
+ /* let alsa know we have play a period */
+ snd_pcm_period_elapsed(substream);
+ return pos;
+}
+
+static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct hsw_priv_data *pdata =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ struct hsw_pcm_data *pcm_data;
+ struct sst_hsw *hsw = pdata->hsw;
+ snd_pcm_uframes_t offset;
+ uint64_t ppos;
+ u32 position;
+ int dai;
+
+ dai = mod_map[rtd->cpu_dai->id].dai_id;
+ pcm_data = &pdata->pcm[dai][substream->stream];
+ position = sst_hsw_get_dsp_position(hsw, pcm_data->stream);
+
+ offset = bytes_to_frames(runtime, position);
+ ppos = sst_hsw_get_dsp_presentation_position(hsw, pcm_data->stream);
+
+ dev_vdbg(rtd->dev, "PCM: DMA pointer %du bytes, pos %llu\n",
+ position, ppos);
+ return offset;
+}
+
+static int hsw_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct hsw_priv_data *pdata =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ struct hsw_pcm_data *pcm_data;
+ struct sst_hsw *hsw = pdata->hsw;
+ int dai;
+
+ dai = mod_map[rtd->cpu_dai->id].dai_id;
+ pcm_data = &pdata->pcm[dai][substream->stream];
+
+ mutex_lock(&pcm_data->mutex);
+ pm_runtime_get_sync(pdata->dev);
+
+ snd_soc_pcm_set_drvdata(rtd, pcm_data);
+ pcm_data->substream = substream;
+
+ snd_soc_set_runtime_hwparams(substream, &hsw_pcm_hardware);
+
+ pcm_data->stream = sst_hsw_stream_new(hsw, rtd->cpu_dai->id,
+ hsw_notify_pointer, pcm_data);
+ if (pcm_data->stream == NULL) {
+ dev_err(rtd->dev, "error: failed to create stream\n");
+ pm_runtime_mark_last_busy(pdata->dev);
+ pm_runtime_put_autosuspend(pdata->dev);
+ mutex_unlock(&pcm_data->mutex);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&pcm_data->mutex);
+ return 0;
+}
+
+static int hsw_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct hsw_priv_data *pdata =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ struct hsw_pcm_data *pcm_data;
+ struct sst_hsw *hsw = pdata->hsw;
+ int ret, dai;
+
+ dai = mod_map[rtd->cpu_dai->id].dai_id;
+ pcm_data = &pdata->pcm[dai][substream->stream];
+
+ mutex_lock(&pcm_data->mutex);
+ ret = sst_hsw_stream_reset(hsw, pcm_data->stream);
+ if (ret < 0) {
+ dev_dbg(rtd->dev, "error: reset stream failed %d\n", ret);
+ goto out;
+ }
+
+ ret = sst_hsw_stream_free(hsw, pcm_data->stream);
+ if (ret < 0) {
+ dev_dbg(rtd->dev, "error: free stream failed %d\n", ret);
+ goto out;
+ }
+ pcm_data->allocated = 0;
+ pcm_data->stream = NULL;
+
+out:
+ pm_runtime_mark_last_busy(pdata->dev);
+ pm_runtime_put_autosuspend(pdata->dev);
+ mutex_unlock(&pcm_data->mutex);
+ return ret;
+}
+
+static struct snd_pcm_ops hsw_pcm_ops = {
+ .open = hsw_pcm_open,
+ .close = hsw_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = hsw_pcm_hw_params,
+ .hw_free = hsw_pcm_hw_free,
+ .trigger = hsw_pcm_trigger,
+ .pointer = hsw_pcm_pointer,
+ .page = snd_pcm_sgbuf_ops_page,
+};
+
+static int hsw_pcm_create_modules(struct hsw_priv_data *pdata)
+{
+ struct sst_hsw *hsw = pdata->hsw;
+ struct hsw_pcm_data *pcm_data;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
+ pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
+
+ /* create new runtime module, use same offset if recreated */
+ pcm_data->runtime = sst_hsw_runtime_module_create(hsw,
+ mod_map[i].mod_id, pcm_data->persistent_offset);
+ if (pcm_data->runtime == NULL)
+ goto err;
+ pcm_data->persistent_offset =
+ pcm_data->runtime->persistent_offset;
+ }
+
+ /* create runtime blocks for module waves */
+ if (sst_hsw_is_module_loaded(hsw, SST_HSW_MODULE_WAVES)) {
+ pdata->runtime_waves = sst_hsw_runtime_module_create(hsw,
+ SST_HSW_MODULE_WAVES, 0);
+ if (pdata->runtime_waves == NULL)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ for (--i; i >= 0; i--) {
+ pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
+ sst_hsw_runtime_module_free(pcm_data->runtime);
+ }
+
+ return -ENODEV;
+}
+
+static void hsw_pcm_free_modules(struct hsw_priv_data *pdata)
+{
+ struct sst_hsw *hsw = pdata->hsw;
+ struct hsw_pcm_data *pcm_data;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
+ pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
+ sst_hsw_runtime_module_free(pcm_data->runtime);
+ }
+ if (sst_hsw_is_module_loaded(hsw, SST_HSW_MODULE_WAVES)) {
+ sst_hsw_runtime_module_free(pdata->runtime_waves);
+ }
+}
+
+static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_pcm *pcm = rtd->pcm;
+ struct snd_soc_platform *platform = rtd->platform;
+ struct sst_pdata *pdata = dev_get_platdata(platform->dev);
+ struct hsw_priv_data *priv_data = dev_get_drvdata(platform->dev);
+ struct device *dev = pdata->dma_dev;
+ int ret = 0;
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream ||
+ pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+ ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
+ SNDRV_DMA_TYPE_DEV_SG,
+ dev,
+ hsw_pcm_hardware.buffer_bytes_max,
+ hsw_pcm_hardware.buffer_bytes_max);
+ if (ret) {
+ dev_err(rtd->dev, "dma buffer allocation failed %d\n",
+ ret);
+ return ret;
+ }
+ }
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
+ priv_data->pcm[rtd->cpu_dai->id][SNDRV_PCM_STREAM_PLAYBACK].hsw_pcm = pcm;
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream)
+ priv_data->pcm[rtd->cpu_dai->id][SNDRV_PCM_STREAM_CAPTURE].hsw_pcm = pcm;
+
+ return ret;
+}
+
+#define HSW_FORMATS \
+ (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
+
+static struct snd_soc_dai_driver hsw_dais[] = {
+ {
+ .name = "System Pin",
+ .id = HSW_PCM_DAI_ID_SYSTEM,
+ .playback = {
+ .stream_name = "System Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Analog Capture",
+ .channels_min = 2,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ },
+ {
+ /* PCM */
+ .name = "Offload0 Pin",
+ .id = HSW_PCM_DAI_ID_OFFLOAD0,
+ .playback = {
+ .stream_name = "Offload0 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = HSW_FORMATS,
+ },
+ },
+ {
+ /* PCM */
+ .name = "Offload1 Pin",
+ .id = HSW_PCM_DAI_ID_OFFLOAD1,
+ .playback = {
+ .stream_name = "Offload1 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = HSW_FORMATS,
+ },
+ },
+ {
+ .name = "Loopback Pin",
+ .id = HSW_PCM_DAI_ID_LOOPBACK,
+ .capture = {
+ .stream_name = "Loopback Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ },
+};
+
+static const struct snd_soc_dapm_widget widgets[] = {
+
+ /* Backend DAIs */
+ SND_SOC_DAPM_AIF_IN("SSP0 CODEC IN", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SSP0 CODEC OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SSP1 BT IN", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SSP1 BT OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
+
+ /* Global Playback Mixer */
+ SND_SOC_DAPM_MIXER("Playback VMixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route graph[] = {
+
+ /* Playback Mixer */
+ {"Playback VMixer", NULL, "System Playback"},
+ {"Playback VMixer", NULL, "Offload0 Playback"},
+ {"Playback VMixer", NULL, "Offload1 Playback"},
+
+ {"SSP0 CODEC OUT", NULL, "Playback VMixer"},
+
+ {"Analog Capture", NULL, "SSP0 CODEC IN"},
+};
+
+static int hsw_pcm_probe(struct snd_soc_platform *platform)
+{
+ struct hsw_priv_data *priv_data = snd_soc_platform_get_drvdata(platform);
+ struct sst_pdata *pdata = dev_get_platdata(platform->dev);
+ struct device *dma_dev, *dev;
+ int i, ret = 0;
+
+ if (!pdata)
+ return -ENODEV;
+
+ dev = platform->dev;
+ dma_dev = pdata->dma_dev;
+
+ priv_data->hsw = pdata->dsp;
+ priv_data->dev = platform->dev;
+ priv_data->pm_state = HSW_PM_STATE_D0;
+ priv_data->soc_card = platform->component.card;
+
+ /* allocate DSP buffer page tables */
+ for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
+
+ /* playback */
+ if (hsw_dais[i].playback.channels_min) {
+ mutex_init(&priv_data->pcm[i][SNDRV_PCM_STREAM_PLAYBACK].mutex);
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dma_dev,
+ PAGE_SIZE, &priv_data->dmab[i][0]);
+ if (ret < 0)
+ goto err;
+ }
+
+ /* capture */
+ if (hsw_dais[i].capture.channels_min) {
+ mutex_init(&priv_data->pcm[i][SNDRV_PCM_STREAM_CAPTURE].mutex);
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dma_dev,
+ PAGE_SIZE, &priv_data->dmab[i][1]);
+ if (ret < 0)
+ goto err;
+ }
+ }
+
+ /* allocate runtime modules */
+ ret = hsw_pcm_create_modules(priv_data);
+ if (ret < 0)
+ goto err;
+
+ /* enable runtime PM with auto suspend */
+ pm_runtime_set_autosuspend_delay(platform->dev,
+ SST_RUNTIME_SUSPEND_DELAY);
+ pm_runtime_use_autosuspend(platform->dev);
+ pm_runtime_enable(platform->dev);
+ pm_runtime_idle(platform->dev);
+
+ return 0;
+
+err:
+ for (--i; i >= 0; i--) {
+ if (hsw_dais[i].playback.channels_min)
+ snd_dma_free_pages(&priv_data->dmab[i][0]);
+ if (hsw_dais[i].capture.channels_min)
+ snd_dma_free_pages(&priv_data->dmab[i][1]);
+ }
+ return ret;
+}
+
+static int hsw_pcm_remove(struct snd_soc_platform *platform)
+{
+ struct hsw_priv_data *priv_data =
+ snd_soc_platform_get_drvdata(platform);
+ int i;
+
+ pm_runtime_disable(platform->dev);
+ hsw_pcm_free_modules(priv_data);
+
+ for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
+ if (hsw_dais[i].playback.channels_min)
+ snd_dma_free_pages(&priv_data->dmab[i][0]);
+ if (hsw_dais[i].capture.channels_min)
+ snd_dma_free_pages(&priv_data->dmab[i][1]);
+ }
+
+ return 0;
+}
+
+static struct snd_soc_platform_driver hsw_soc_platform = {
+ .probe = hsw_pcm_probe,
+ .remove = hsw_pcm_remove,
+ .ops = &hsw_pcm_ops,
+ .pcm_new = hsw_pcm_new,
+};
+
+static const struct snd_soc_component_driver hsw_dai_component = {
+ .name = "haswell-dai",
+ .controls = hsw_volume_controls,
+ .num_controls = ARRAY_SIZE(hsw_volume_controls),
+ .dapm_widgets = widgets,
+ .num_dapm_widgets = ARRAY_SIZE(widgets),
+ .dapm_routes = graph,
+ .num_dapm_routes = ARRAY_SIZE(graph),
+};
+
+static int hsw_pcm_dev_probe(struct platform_device *pdev)
+{
+ struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
+ struct hsw_priv_data *priv_data;
+ int ret;
+
+ if (!sst_pdata)
+ return -EINVAL;
+
+ priv_data = devm_kzalloc(&pdev->dev, sizeof(*priv_data), GFP_KERNEL);
+ if (!priv_data)
+ return -ENOMEM;
+
+ ret = sst_hsw_dsp_init(&pdev->dev, sst_pdata);
+ if (ret < 0)
+ return -ENODEV;
+
+ priv_data->hsw = sst_pdata->dsp;
+ platform_set_drvdata(pdev, priv_data);
+
+ ret = snd_soc_register_platform(&pdev->dev, &hsw_soc_platform);
+ if (ret < 0)
+ goto err_plat;
+
+ ret = snd_soc_register_component(&pdev->dev, &hsw_dai_component,
+ hsw_dais, ARRAY_SIZE(hsw_dais));
+ if (ret < 0)
+ goto err_comp;
+
+ return 0;
+
+err_comp:
+ snd_soc_unregister_platform(&pdev->dev);
+err_plat:
+ sst_hsw_dsp_free(&pdev->dev, sst_pdata);
+ return 0;
+}
+
+static int hsw_pcm_dev_remove(struct platform_device *pdev)
+{
+ struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
+
+ snd_soc_unregister_platform(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
+ sst_hsw_dsp_free(&pdev->dev, sst_pdata);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int hsw_pcm_runtime_idle(struct device *dev)
+{
+ return 0;
+}
+
+static int hsw_pcm_runtime_suspend(struct device *dev)
+{
+ struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+ struct sst_hsw *hsw = pdata->hsw;
+ int ret;
+
+ if (pdata->pm_state >= HSW_PM_STATE_RTD3)
+ return 0;
+
+ /* fw modules will be unloaded on RTD3, set flag to track */
+ if (sst_hsw_is_module_active(hsw, SST_HSW_MODULE_WAVES)) {
+ ret = sst_hsw_module_disable(hsw, SST_HSW_MODULE_WAVES, 0);
+ if (ret < 0)
+ return ret;
+ sst_hsw_set_module_enabled_rtd3(hsw, SST_HSW_MODULE_WAVES);
+ }
+ sst_hsw_dsp_runtime_suspend(hsw);
+ sst_hsw_dsp_runtime_sleep(hsw);
+ pdata->pm_state = HSW_PM_STATE_RTD3;
+
+ return 0;
+}
+
+static int hsw_pcm_runtime_resume(struct device *dev)
+{
+ struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+ struct sst_hsw *hsw = pdata->hsw;
+ int ret;
+
+ if (pdata->pm_state != HSW_PM_STATE_RTD3)
+ return 0;
+
+ ret = sst_hsw_dsp_load(hsw);
+ if (ret < 0) {
+ dev_err(dev, "failed to reload %d\n", ret);
+ return ret;
+ }
+
+ ret = hsw_pcm_create_modules(pdata);
+ if (ret < 0) {
+ dev_err(dev, "failed to create modules %d\n", ret);
+ return ret;
+ }
+
+ ret = sst_hsw_dsp_runtime_resume(hsw);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1) /* no action required */
+ return 0;
+
+ /* check flag when resume */
+ if (sst_hsw_is_module_enabled_rtd3(hsw, SST_HSW_MODULE_WAVES)) {
+ ret = sst_hsw_module_enable(hsw, SST_HSW_MODULE_WAVES, 0);
+ if (ret < 0)
+ return ret;
+ /* put parameters from buffer to dsp */
+ ret = sst_hsw_launch_param_buf(hsw);
+ if (ret < 0)
+ return ret;
+ /* unset flag */
+ sst_hsw_set_module_disabled_rtd3(hsw, SST_HSW_MODULE_WAVES);
+ }
+
+ pdata->pm_state = HSW_PM_STATE_D0;
+ return ret;
+}
+
+#else
+#define hsw_pcm_runtime_idle NULL
+#define hsw_pcm_runtime_suspend NULL
+#define hsw_pcm_runtime_resume NULL
+#endif
+
+#ifdef CONFIG_PM
+
+static void hsw_pcm_complete(struct device *dev)
+{
+ struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+ struct sst_hsw *hsw = pdata->hsw;
+ struct hsw_pcm_data *pcm_data;
+ int i, err;
+
+ if (pdata->pm_state != HSW_PM_STATE_D3)
+ return;
+
+ err = sst_hsw_dsp_load(hsw);
+ if (err < 0) {
+ dev_err(dev, "failed to reload %d\n", err);
+ return;
+ }
+
+ err = hsw_pcm_create_modules(pdata);
+ if (err < 0) {
+ dev_err(dev, "failed to create modules %d\n", err);
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
+ pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
+
+ if (!pcm_data->substream)
+ continue;
+
+ err = sst_module_runtime_restore(pcm_data->runtime,
+ &pcm_data->context);
+ if (err < 0)
+ dev_err(dev, "failed to restore context for PCM %d\n", i);
+ }
+
+ snd_soc_resume(pdata->soc_card->dev);
+
+ err = sst_hsw_dsp_runtime_resume(hsw);
+ if (err < 0)
+ return;
+ else if (err == 1) /* no action required */
+ return;
+
+ pdata->pm_state = HSW_PM_STATE_D0;
+ return;
+}
+
+static int hsw_pcm_prepare(struct device *dev)
+{
+ struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+ struct sst_hsw *hsw = pdata->hsw;
+ struct hsw_pcm_data *pcm_data;
+ int i, err;
+
+ if (pdata->pm_state == HSW_PM_STATE_D3)
+ return 0;
+ else if (pdata->pm_state == HSW_PM_STATE_D0) {
+ /* suspend all active streams */
+ for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
+ pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
+
+ if (!pcm_data->substream)
+ continue;
+ dev_dbg(dev, "suspending pcm %d\n", i);
+ snd_pcm_suspend_all(pcm_data->hsw_pcm);
+
+ /* We need to wait until the DSP FW stops the streams */
+ msleep(2);
+ }
+
+ /* preserve persistent memory */
+ for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
+ pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
+
+ if (!pcm_data->substream)
+ continue;
+
+ dev_dbg(dev, "saving context pcm %d\n", i);
+ err = sst_module_runtime_save(pcm_data->runtime,
+ &pcm_data->context);
+ if (err < 0)
+ dev_err(dev, "failed to save context for PCM %d\n", i);
+ }
+ /* enter D3 state and stall */
+ sst_hsw_dsp_runtime_suspend(hsw);
+ /* put the DSP to sleep */
+ sst_hsw_dsp_runtime_sleep(hsw);
+ }
+
+ snd_soc_suspend(pdata->soc_card->dev);
+ snd_soc_poweroff(pdata->soc_card->dev);
+
+ pdata->pm_state = HSW_PM_STATE_D3;
+
+ return 0;
+}
+
+#else
+#define hsw_pcm_prepare NULL
+#define hsw_pcm_complete NULL
+#endif
+
+static const struct dev_pm_ops hsw_pcm_pm = {
+ .runtime_idle = hsw_pcm_runtime_idle,
+ .runtime_suspend = hsw_pcm_runtime_suspend,
+ .runtime_resume = hsw_pcm_runtime_resume,
+ .prepare = hsw_pcm_prepare,
+ .complete = hsw_pcm_complete,
+};
+
+static struct platform_driver hsw_pcm_driver = {
+ .driver = {
+ .name = "haswell-pcm-audio",
+ .pm = &hsw_pcm_pm,
+ },
+
+ .probe = hsw_pcm_dev_probe,
+ .remove = hsw_pcm_dev_remove,
+};
+module_platform_driver(hsw_pcm_driver);
+
+MODULE_AUTHOR("Liam Girdwood, Xingchao Wang");
+MODULE_DESCRIPTION("Haswell/Lynxpoint + Broadwell/Wildcatpoint PCM");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:haswell-pcm-audio");
+++ /dev/null
-/*
- * mfld_machine.c - ASoc Machine driver for Intel Medfield MID platform
- *
- * Copyright (C) 2010 Intel Corp
- * Author: Vinod Koul <vinod.koul@intel.com>
- * Author: Harsha Priya <priya.harsha@intel.com>
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/jack.h>
-#include "../codecs/sn95031.h"
-
-#define MID_MONO 1
-#define MID_STEREO 2
-#define MID_MAX_CAP 5
-#define MFLD_JACK_INSERT 0x04
-
-enum soc_mic_bias_zones {
- MFLD_MV_START = 0,
- /* mic bias volutage range for Headphones*/
- MFLD_MV_HP = 400,
- /* mic bias volutage range for American Headset*/
- MFLD_MV_AM_HS = 650,
- /* mic bias volutage range for Headset*/
- MFLD_MV_HS = 2000,
- MFLD_MV_UNDEFINED,
-};
-
-static unsigned int hs_switch;
-static unsigned int lo_dac;
-static struct snd_soc_codec *mfld_codec;
-
-struct mfld_mc_private {
- void __iomem *int_base;
- u8 interrupt_status;
-};
-
-struct snd_soc_jack mfld_jack;
-
-/*Headset jack detection DAPM pins */
-static struct snd_soc_jack_pin mfld_jack_pins[] = {
- {
- .pin = "Headphones",
- .mask = SND_JACK_HEADPHONE,
- },
- {
- .pin = "AMIC1",
- .mask = SND_JACK_MICROPHONE,
- },
-};
-
-/* jack detection voltage zones */
-static struct snd_soc_jack_zone mfld_zones[] = {
- {MFLD_MV_START, MFLD_MV_AM_HS, SND_JACK_HEADPHONE},
- {MFLD_MV_AM_HS, MFLD_MV_HS, SND_JACK_HEADSET},
-};
-
-/* sound card controls */
-static const char *headset_switch_text[] = {"Earpiece", "Headset"};
-
-static const char *lo_text[] = {"Vibra", "Headset", "IHF", "None"};
-
-static const struct soc_enum headset_enum =
- SOC_ENUM_SINGLE_EXT(2, headset_switch_text);
-
-static const struct soc_enum lo_enum =
- SOC_ENUM_SINGLE_EXT(4, lo_text);
-
-static int headset_get_switch(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] = hs_switch;
- return 0;
-}
-
-static int headset_set_switch(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_context *dapm = &card->dapm;
-
- if (ucontrol->value.integer.value[0] == hs_switch)
- return 0;
-
- snd_soc_dapm_mutex_lock(dapm);
-
- if (ucontrol->value.integer.value[0]) {
- pr_debug("hs_set HS path\n");
- snd_soc_dapm_enable_pin_unlocked(dapm, "Headphones");
- snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT");
- } else {
- pr_debug("hs_set EP path\n");
- snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones");
- snd_soc_dapm_enable_pin_unlocked(dapm, "EPOUT");
- }
-
- snd_soc_dapm_sync_unlocked(dapm);
-
- snd_soc_dapm_mutex_unlock(dapm);
-
- hs_switch = ucontrol->value.integer.value[0];
-
- return 0;
-}
-
-static void lo_enable_out_pins(struct snd_soc_dapm_context *dapm)
-{
- snd_soc_dapm_enable_pin_unlocked(dapm, "IHFOUTL");
- snd_soc_dapm_enable_pin_unlocked(dapm, "IHFOUTR");
- snd_soc_dapm_enable_pin_unlocked(dapm, "LINEOUTL");
- snd_soc_dapm_enable_pin_unlocked(dapm, "LINEOUTR");
- snd_soc_dapm_enable_pin_unlocked(dapm, "VIB1OUT");
- snd_soc_dapm_enable_pin_unlocked(dapm, "VIB2OUT");
- if (hs_switch) {
- snd_soc_dapm_enable_pin_unlocked(dapm, "Headphones");
- snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT");
- } else {
- snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones");
- snd_soc_dapm_enable_pin_unlocked(dapm, "EPOUT");
- }
-}
-
-static int lo_get_switch(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.integer.value[0] = lo_dac;
- return 0;
-}
-
-static int lo_set_switch(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_context *dapm = &card->dapm;
-
- if (ucontrol->value.integer.value[0] == lo_dac)
- return 0;
-
- snd_soc_dapm_mutex_lock(dapm);
-
- /* we dont want to work with last state of lineout so just enable all
- * pins and then disable pins not required
- */
- lo_enable_out_pins(dapm);
-
- switch (ucontrol->value.integer.value[0]) {
- case 0:
- pr_debug("set vibra path\n");
- snd_soc_dapm_disable_pin_unlocked(dapm, "VIB1OUT");
- snd_soc_dapm_disable_pin_unlocked(dapm, "VIB2OUT");
- snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0);
- break;
-
- case 1:
- pr_debug("set hs path\n");
- snd_soc_dapm_disable_pin_unlocked(dapm, "Headphones");
- snd_soc_dapm_disable_pin_unlocked(dapm, "EPOUT");
- snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0x22);
- break;
-
- case 2:
- pr_debug("set spkr path\n");
- snd_soc_dapm_disable_pin_unlocked(dapm, "IHFOUTL");
- snd_soc_dapm_disable_pin_unlocked(dapm, "IHFOUTR");
- snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0x44);
- break;
-
- case 3:
- pr_debug("set null path\n");
- snd_soc_dapm_disable_pin_unlocked(dapm, "LINEOUTL");
- snd_soc_dapm_disable_pin_unlocked(dapm, "LINEOUTR");
- snd_soc_update_bits(mfld_codec, SN95031_LOCTL, 0x66, 0x66);
- break;
- }
-
- snd_soc_dapm_sync_unlocked(dapm);
-
- snd_soc_dapm_mutex_unlock(dapm);
-
- lo_dac = ucontrol->value.integer.value[0];
- return 0;
-}
-
-static const struct snd_kcontrol_new mfld_snd_controls[] = {
- SOC_ENUM_EXT("Playback Switch", headset_enum,
- headset_get_switch, headset_set_switch),
- SOC_ENUM_EXT("Lineout Mux", lo_enum,
- lo_get_switch, lo_set_switch),
-};
-
-static const struct snd_soc_dapm_widget mfld_widgets[] = {
- SND_SOC_DAPM_HP("Headphones", NULL),
- SND_SOC_DAPM_MIC("Mic", NULL),
-};
-
-static const struct snd_soc_dapm_route mfld_map[] = {
- {"Headphones", NULL, "HPOUTR"},
- {"Headphones", NULL, "HPOUTL"},
- {"Mic", NULL, "AMIC1"},
-};
-
-static void mfld_jack_check(unsigned int intr_status)
-{
- struct mfld_jack_data jack_data;
-
- jack_data.mfld_jack = &mfld_jack;
- jack_data.intr_id = intr_status;
-
- sn95031_jack_detection(&jack_data);
- /* TODO: add american headset detection post gpiolib support */
-}
-
-static int mfld_init(struct snd_soc_pcm_runtime *runtime)
-{
- struct snd_soc_dapm_context *dapm = &runtime->card->dapm;
- int ret_val;
-
- mfld_codec = runtime->codec;
-
- /* default is earpiece pin, userspace sets it explcitly */
- snd_soc_dapm_disable_pin(dapm, "Headphones");
- /* default is lineout NC, userspace sets it explcitly */
- snd_soc_dapm_disable_pin(dapm, "LINEOUTL");
- snd_soc_dapm_disable_pin(dapm, "LINEOUTR");
- lo_dac = 3;
- hs_switch = 0;
- /* we dont use linein in this so set to NC */
- snd_soc_dapm_disable_pin(dapm, "LINEINL");
- snd_soc_dapm_disable_pin(dapm, "LINEINR");
-
- /* Headset and button jack detection */
- ret_val = snd_soc_jack_new(mfld_codec, "Intel(R) MID Audio Jack",
- SND_JACK_HEADSET | SND_JACK_BTN_0 |
- SND_JACK_BTN_1, &mfld_jack);
- if (ret_val) {
- pr_err("jack creation failed\n");
- return ret_val;
- }
-
- ret_val = snd_soc_jack_add_pins(&mfld_jack,
- ARRAY_SIZE(mfld_jack_pins), mfld_jack_pins);
- if (ret_val) {
- pr_err("adding jack pins failed\n");
- return ret_val;
- }
- ret_val = snd_soc_jack_add_zones(&mfld_jack,
- ARRAY_SIZE(mfld_zones), mfld_zones);
- if (ret_val) {
- pr_err("adding jack zones failed\n");
- return ret_val;
- }
-
- /* we want to check if anything is inserted at boot,
- * so send a fake event to codec and it will read adc
- * to find if anything is there or not */
- mfld_jack_check(MFLD_JACK_INSERT);
- return ret_val;
-}
-
-static struct snd_soc_dai_link mfld_msic_dailink[] = {
- {
- .name = "Medfield Headset",
- .stream_name = "Headset",
- .cpu_dai_name = "Headset-cpu-dai",
- .codec_dai_name = "SN95031 Headset",
- .codec_name = "sn95031",
- .platform_name = "sst-platform",
- .init = mfld_init,
- },
- {
- .name = "Medfield Speaker",
- .stream_name = "Speaker",
- .cpu_dai_name = "Speaker-cpu-dai",
- .codec_dai_name = "SN95031 Speaker",
- .codec_name = "sn95031",
- .platform_name = "sst-platform",
- .init = NULL,
- },
- {
- .name = "Medfield Vibra",
- .stream_name = "Vibra1",
- .cpu_dai_name = "Vibra1-cpu-dai",
- .codec_dai_name = "SN95031 Vibra1",
- .codec_name = "sn95031",
- .platform_name = "sst-platform",
- .init = NULL,
- },
- {
- .name = "Medfield Haptics",
- .stream_name = "Vibra2",
- .cpu_dai_name = "Vibra2-cpu-dai",
- .codec_dai_name = "SN95031 Vibra2",
- .codec_name = "sn95031",
- .platform_name = "sst-platform",
- .init = NULL,
- },
- {
- .name = "Medfield Compress",
- .stream_name = "Speaker",
- .cpu_dai_name = "Compress-cpu-dai",
- .codec_dai_name = "SN95031 Speaker",
- .codec_name = "sn95031",
- .platform_name = "sst-platform",
- .init = NULL,
- },
-};
-
-/* SoC card */
-static struct snd_soc_card snd_soc_card_mfld = {
- .name = "medfield_audio",
- .owner = THIS_MODULE,
- .dai_link = mfld_msic_dailink,
- .num_links = ARRAY_SIZE(mfld_msic_dailink),
-
- .controls = mfld_snd_controls,
- .num_controls = ARRAY_SIZE(mfld_snd_controls),
- .dapm_widgets = mfld_widgets,
- .num_dapm_widgets = ARRAY_SIZE(mfld_widgets),
- .dapm_routes = mfld_map,
- .num_dapm_routes = ARRAY_SIZE(mfld_map),
-};
-
-static irqreturn_t snd_mfld_jack_intr_handler(int irq, void *dev)
-{
- struct mfld_mc_private *mc_private = (struct mfld_mc_private *) dev;
-
- memcpy_fromio(&mc_private->interrupt_status,
- ((void *)(mc_private->int_base)),
- sizeof(u8));
- return IRQ_WAKE_THREAD;
-}
-
-static irqreturn_t snd_mfld_jack_detection(int irq, void *data)
-{
- struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data;
-
- if (mfld_jack.codec == NULL)
- return IRQ_HANDLED;
- mfld_jack_check(mc_drv_ctx->interrupt_status);
-
- return IRQ_HANDLED;
-}
-
-static int snd_mfld_mc_probe(struct platform_device *pdev)
-{
- int ret_val = 0, irq;
- struct mfld_mc_private *mc_drv_ctx;
- struct resource *irq_mem;
-
- pr_debug("snd_mfld_mc_probe called\n");
-
- /* retrive the irq number */
- irq = platform_get_irq(pdev, 0);
-
- /* audio interrupt base of SRAM location where
- * interrupts are stored by System FW */
- mc_drv_ctx = devm_kzalloc(&pdev->dev, sizeof(*mc_drv_ctx), GFP_ATOMIC);
- if (!mc_drv_ctx) {
- pr_err("allocation failed\n");
- return -ENOMEM;
- }
-
- irq_mem = platform_get_resource_byname(
- pdev, IORESOURCE_MEM, "IRQ_BASE");
- if (!irq_mem) {
- pr_err("no mem resource given\n");
- return -ENODEV;
- }
- mc_drv_ctx->int_base = devm_ioremap_nocache(&pdev->dev, irq_mem->start,
- resource_size(irq_mem));
- if (!mc_drv_ctx->int_base) {
- pr_err("Mapping of cache failed\n");
- return -ENOMEM;
- }
- /* register for interrupt */
- ret_val = devm_request_threaded_irq(&pdev->dev, irq,
- snd_mfld_jack_intr_handler,
- snd_mfld_jack_detection,
- IRQF_SHARED, pdev->dev.driver->name, mc_drv_ctx);
- if (ret_val) {
- pr_err("cannot register IRQ\n");
- return ret_val;
- }
- /* register the soc card */
- snd_soc_card_mfld.dev = &pdev->dev;
- ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_mfld);
- if (ret_val) {
- pr_debug("snd_soc_register_card failed %d\n", ret_val);
- return ret_val;
- }
- platform_set_drvdata(pdev, mc_drv_ctx);
- pr_debug("successfully exited probe\n");
- return 0;
-}
-
-static struct platform_driver snd_mfld_mc_driver = {
- .driver = {
- .name = "msic_audio",
- },
- .probe = snd_mfld_mc_probe,
-};
-
-module_platform_driver(snd_mfld_mc_driver);
-
-MODULE_DESCRIPTION("ASoC Intel(R) MID Machine driver");
-MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
-MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:msic-audio");
+++ /dev/null
-/*
- * Intel SST loader on ACPI systems
- *
- * Copyright (C) 2013, Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/acpi.h>
-#include <linux/device.h>
-#include <linux/firmware.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-
-#include "sst-dsp.h"
-
-#define SST_LPT_DSP_DMA_ADDR_OFFSET 0x0F0000
-#define SST_WPT_DSP_DMA_ADDR_OFFSET 0x0FE000
-#define SST_LPT_DSP_DMA_SIZE (1024 - 1)
-
-/* Descriptor for SST ASoC machine driver */
-struct sst_acpi_mach {
- /* ACPI ID for the matching machine driver. Audio codec for instance */
- const u8 id[ACPI_ID_LEN];
- /* machine driver name */
- const char *drv_name;
- /* firmware file name */
- const char *fw_filename;
-};
-
-/* Descriptor for setting up SST platform data */
-struct sst_acpi_desc {
- const char *drv_name;
- struct sst_acpi_mach *machines;
- /* Platform resource indexes. Must set to -1 if not used */
- int resindex_lpe_base;
- int resindex_pcicfg_base;
- int resindex_fw_base;
- int irqindex_host_ipc;
- int resindex_dma_base;
- /* Unique number identifying the SST core on platform */
- int sst_id;
- /* DMA only valid when resindex_dma_base != -1*/
- int dma_engine;
- int dma_size;
-};
-
-struct sst_acpi_priv {
- struct platform_device *pdev_mach;
- struct platform_device *pdev_pcm;
- struct sst_pdata sst_pdata;
- struct sst_acpi_desc *desc;
- struct sst_acpi_mach *mach;
-};
-
-static void sst_acpi_fw_cb(const struct firmware *fw, void *context)
-{
- struct platform_device *pdev = context;
- struct device *dev = &pdev->dev;
- struct sst_acpi_priv *sst_acpi = platform_get_drvdata(pdev);
- struct sst_pdata *sst_pdata = &sst_acpi->sst_pdata;
- struct sst_acpi_desc *desc = sst_acpi->desc;
- struct sst_acpi_mach *mach = sst_acpi->mach;
-
- sst_pdata->fw = fw;
- if (!fw) {
- dev_err(dev, "Cannot load firmware %s\n", mach->fw_filename);
- return;
- }
-
- /* register PCM and DAI driver */
- sst_acpi->pdev_pcm =
- platform_device_register_data(dev, desc->drv_name, -1,
- sst_pdata, sizeof(*sst_pdata));
- if (IS_ERR(sst_acpi->pdev_pcm)) {
- dev_err(dev, "Cannot register device %s. Error %d\n",
- desc->drv_name, (int)PTR_ERR(sst_acpi->pdev_pcm));
- }
-
- return;
-}
-
-static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level,
- void *context, void **ret)
-{
- *(bool *)context = true;
- return AE_OK;
-}
-
-static struct sst_acpi_mach *sst_acpi_find_machine(
- struct sst_acpi_mach *machines)
-{
- struct sst_acpi_mach *mach;
- bool found = false;
-
- for (mach = machines; mach->id[0]; mach++)
- if (ACPI_SUCCESS(acpi_get_devices(mach->id,
- sst_acpi_mach_match,
- &found, NULL)) && found)
- return mach;
-
- return NULL;
-}
-
-static int sst_acpi_probe(struct platform_device *pdev)
-{
- const struct acpi_device_id *id;
- struct device *dev = &pdev->dev;
- struct sst_acpi_priv *sst_acpi;
- struct sst_pdata *sst_pdata;
- struct sst_acpi_mach *mach;
- struct sst_acpi_desc *desc;
- struct resource *mmio;
- int ret = 0;
-
- sst_acpi = devm_kzalloc(dev, sizeof(*sst_acpi), GFP_KERNEL);
- if (sst_acpi == NULL)
- return -ENOMEM;
-
- id = acpi_match_device(dev->driver->acpi_match_table, dev);
- if (!id)
- return -ENODEV;
-
- desc = (struct sst_acpi_desc *)id->driver_data;
- mach = sst_acpi_find_machine(desc->machines);
- if (mach == NULL) {
- dev_err(dev, "No matching ASoC machine driver found\n");
- return -ENODEV;
- }
-
- sst_pdata = &sst_acpi->sst_pdata;
- sst_pdata->id = desc->sst_id;
- sst_pdata->dma_dev = dev;
- sst_acpi->desc = desc;
- sst_acpi->mach = mach;
-
- if (desc->resindex_dma_base >= 0) {
- sst_pdata->dma_engine = desc->dma_engine;
- sst_pdata->dma_base = desc->resindex_dma_base;
- sst_pdata->dma_size = desc->dma_size;
- }
-
- if (desc->irqindex_host_ipc >= 0)
- sst_pdata->irq = platform_get_irq(pdev, desc->irqindex_host_ipc);
-
- if (desc->resindex_lpe_base >= 0) {
- mmio = platform_get_resource(pdev, IORESOURCE_MEM,
- desc->resindex_lpe_base);
- if (mmio) {
- sst_pdata->lpe_base = mmio->start;
- sst_pdata->lpe_size = resource_size(mmio);
- }
- }
-
- if (desc->resindex_pcicfg_base >= 0) {
- mmio = platform_get_resource(pdev, IORESOURCE_MEM,
- desc->resindex_pcicfg_base);
- if (mmio) {
- sst_pdata->pcicfg_base = mmio->start;
- sst_pdata->pcicfg_size = resource_size(mmio);
- }
- }
-
- if (desc->resindex_fw_base >= 0) {
- mmio = platform_get_resource(pdev, IORESOURCE_MEM,
- desc->resindex_fw_base);
- if (mmio) {
- sst_pdata->fw_base = mmio->start;
- sst_pdata->fw_size = resource_size(mmio);
- }
- }
-
- platform_set_drvdata(pdev, sst_acpi);
-
- /* register machine driver */
- sst_acpi->pdev_mach =
- platform_device_register_data(dev, mach->drv_name, -1,
- sst_pdata, sizeof(*sst_pdata));
- if (IS_ERR(sst_acpi->pdev_mach))
- return PTR_ERR(sst_acpi->pdev_mach);
-
- /* continue SST probing after firmware is loaded */
- ret = request_firmware_nowait(THIS_MODULE, true, mach->fw_filename,
- dev, GFP_KERNEL, pdev, sst_acpi_fw_cb);
- if (ret)
- platform_device_unregister(sst_acpi->pdev_mach);
-
- return ret;
-}
-
-static int sst_acpi_remove(struct platform_device *pdev)
-{
- struct sst_acpi_priv *sst_acpi = platform_get_drvdata(pdev);
- struct sst_pdata *sst_pdata = &sst_acpi->sst_pdata;
-
- platform_device_unregister(sst_acpi->pdev_mach);
- if (!IS_ERR_OR_NULL(sst_acpi->pdev_pcm))
- platform_device_unregister(sst_acpi->pdev_pcm);
- release_firmware(sst_pdata->fw);
-
- return 0;
-}
-
-static struct sst_acpi_mach haswell_machines[] = {
- { "INT33CA", "haswell-audio", "intel/IntcSST1.bin" },
- {}
-};
-
-static struct sst_acpi_desc sst_acpi_haswell_desc = {
- .drv_name = "haswell-pcm-audio",
- .machines = haswell_machines,
- .resindex_lpe_base = 0,
- .resindex_pcicfg_base = 1,
- .resindex_fw_base = -1,
- .irqindex_host_ipc = 0,
- .sst_id = SST_DEV_ID_LYNX_POINT,
- .dma_engine = SST_DMA_TYPE_DW,
- .resindex_dma_base = SST_LPT_DSP_DMA_ADDR_OFFSET,
- .dma_size = SST_LPT_DSP_DMA_SIZE,
-};
-
-static struct sst_acpi_mach broadwell_machines[] = {
- { "INT343A", "broadwell-audio", "intel/IntcSST2.bin" },
- {}
-};
-
-static struct sst_acpi_desc sst_acpi_broadwell_desc = {
- .drv_name = "haswell-pcm-audio",
- .machines = broadwell_machines,
- .resindex_lpe_base = 0,
- .resindex_pcicfg_base = 1,
- .resindex_fw_base = -1,
- .irqindex_host_ipc = 0,
- .sst_id = SST_DEV_ID_WILDCAT_POINT,
- .dma_engine = SST_DMA_TYPE_DW,
- .resindex_dma_base = SST_WPT_DSP_DMA_ADDR_OFFSET,
- .dma_size = SST_LPT_DSP_DMA_SIZE,
-};
-
-static struct sst_acpi_mach baytrail_machines[] = {
- { "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-48kHz_i2s_master" },
- { "193C9890", "byt-max98090", "intel/fw_sst_0f28.bin-48kHz_i2s_master" },
- {}
-};
-
-static struct sst_acpi_desc sst_acpi_baytrail_desc = {
- .drv_name = "baytrail-pcm-audio",
- .machines = baytrail_machines,
- .resindex_lpe_base = 0,
- .resindex_pcicfg_base = 1,
- .resindex_fw_base = 2,
- .irqindex_host_ipc = 5,
- .sst_id = SST_DEV_ID_BYT,
- .resindex_dma_base = -1,
-};
-
-static struct acpi_device_id sst_acpi_match[] = {
- { "INT33C8", (unsigned long)&sst_acpi_haswell_desc },
- { "INT3438", (unsigned long)&sst_acpi_broadwell_desc },
- { "80860F28", (unsigned long)&sst_acpi_baytrail_desc },
- { }
-};
-MODULE_DEVICE_TABLE(acpi, sst_acpi_match);
-
-static struct platform_driver sst_acpi_driver = {
- .probe = sst_acpi_probe,
- .remove = sst_acpi_remove,
- .driver = {
- .name = "sst-acpi",
- .acpi_match_table = ACPI_PTR(sst_acpi_match),
- },
-};
-module_platform_driver(sst_acpi_driver);
-
-MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@linux.intel.com>");
-MODULE_DESCRIPTION("Intel SST loader on ACPI systems");
-MODULE_LICENSE("GPL v2");
+++ /dev/null
-/*
- * sst-atom-controls.c - Intel MID Platform driver DPCM ALSA controls for Mrfld
- *
- * Copyright (C) 2013-14 Intel Corp
- * Author: Omair Mohammed Abdullah <omair.m.abdullah@intel.com>
- * Vinod Koul <vinod.koul@intel.com>
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * In the dpcm driver modelling when a particular FE/BE/Mixer/Pipe is active
- * we forward the settings and parameters, rest we keep the values in
- * driver and forward when DAPM enables them
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/slab.h>
-#include <sound/soc.h>
-#include <sound/tlv.h>
-#include "sst-mfld-platform.h"
-#include "sst-atom-controls.h"
-
-static int sst_fill_byte_control(struct sst_data *drv,
- u8 ipc_msg, u8 block,
- u8 task_id, u8 pipe_id,
- u16 len, void *cmd_data)
-{
- struct snd_sst_bytes_v2 *byte_data = drv->byte_stream;
-
- byte_data->type = SST_CMD_BYTES_SET;
- byte_data->ipc_msg = ipc_msg;
- byte_data->block = block;
- byte_data->task_id = task_id;
- byte_data->pipe_id = pipe_id;
-
- if (len > SST_MAX_BIN_BYTES - sizeof(*byte_data)) {
- dev_err(&drv->pdev->dev, "command length too big (%u)", len);
- return -EINVAL;
- }
- byte_data->len = len;
- memcpy(byte_data->bytes, cmd_data, len);
- print_hex_dump_bytes("writing to lpe: ", DUMP_PREFIX_OFFSET,
- byte_data, len + sizeof(*byte_data));
- return 0;
-}
-
-static int sst_fill_and_send_cmd_unlocked(struct sst_data *drv,
- u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id,
- void *cmd_data, u16 len)
-{
- int ret = 0;
-
- ret = sst_fill_byte_control(drv, ipc_msg,
- block, task_id, pipe_id, len, cmd_data);
- if (ret < 0)
- return ret;
- return sst->ops->send_byte_stream(sst->dev, drv->byte_stream);
-}
-
-/**
- * sst_fill_and_send_cmd - generate the IPC message and send it to the FW
- * @ipc_msg: type of IPC (CMD, SET_PARAMS, GET_PARAMS)
- * @cmd_data: the IPC payload
- */
-static int sst_fill_and_send_cmd(struct sst_data *drv,
- u8 ipc_msg, u8 block, u8 task_id, u8 pipe_id,
- void *cmd_data, u16 len)
-{
- int ret;
-
- mutex_lock(&drv->lock);
- ret = sst_fill_and_send_cmd_unlocked(drv, ipc_msg, block,
- task_id, pipe_id, cmd_data, len);
- mutex_unlock(&drv->lock);
-
- return ret;
-}
-
-/**
- * tx map value is a bitfield where each bit represents a FW channel
- *
- * 3 2 1 0 # 0 = codec0, 1 = codec1
- * RLRLRLRL # 3, 4 = reserved
- *
- * e.g. slot 0 rx map = 00001100b -> data from slot 0 goes into codec_in1 L,R
- */
-static u8 sst_ssp_tx_map[SST_MAX_TDM_SLOTS] = {
- 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default rx map */
-};
-
-/**
- * rx map value is a bitfield where each bit represents a slot
- *
- * 76543210 # 0 = slot 0, 1 = slot 1
- *
- * e.g. codec1_0 tx map = 00000101b -> data from codec_out1_0 goes into slot 0, 2
- */
-static u8 sst_ssp_rx_map[SST_MAX_TDM_SLOTS] = {
- 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default tx map */
-};
-
-/**
- * NOTE: this is invoked with lock held
- */
-static int sst_send_slot_map(struct sst_data *drv)
-{
- struct sst_param_sba_ssp_slot_map cmd;
-
- SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
- cmd.header.command_id = SBA_SET_SSP_SLOT_MAP;
- cmd.header.length = sizeof(struct sst_param_sba_ssp_slot_map)
- - sizeof(struct sst_dsp_header);
-
- cmd.param_id = SBA_SET_SSP_SLOT_MAP;
- cmd.param_len = sizeof(cmd.rx_slot_map) + sizeof(cmd.tx_slot_map)
- + sizeof(cmd.ssp_index);
- cmd.ssp_index = SSP_CODEC;
-
- memcpy(cmd.rx_slot_map, &sst_ssp_tx_map[0], sizeof(cmd.rx_slot_map));
- memcpy(cmd.tx_slot_map, &sst_ssp_rx_map[0], sizeof(cmd.tx_slot_map));
-
- return sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,
- SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd,
- sizeof(cmd.header) + cmd.header.length);
-}
-
-int sst_slot_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct sst_enum *e = (struct sst_enum *)kcontrol->private_value;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = e->max;
-
- if (uinfo->value.enumerated.item > e->max - 1)
- uinfo->value.enumerated.item = e->max - 1;
- strcpy(uinfo->value.enumerated.name,
- e->texts[uinfo->value.enumerated.item]);
-
- return 0;
-}
-
-/**
- * sst_slot_get - get the status of the interleaver/deinterleaver control
- *
- * Searches the map where the control status is stored, and gets the
- * channel/slot which is currently set for this enumerated control. Since it is
- * an enumerated control, there is only one possible value.
- */
-static int sst_slot_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct sst_enum *e = (void *)kcontrol->private_value;
- struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
- struct sst_data *drv = snd_soc_component_get_drvdata(c);
- unsigned int ctl_no = e->reg;
- unsigned int is_tx = e->tx;
- unsigned int val, mux;
- u8 *map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map;
-
- mutex_lock(&drv->lock);
- val = 1 << ctl_no;
- /* search which slot/channel has this bit set - there should be only one */
- for (mux = e->max; mux > 0; mux--)
- if (map[mux - 1] & val)
- break;
-
- ucontrol->value.enumerated.item[0] = mux;
- mutex_unlock(&drv->lock);
-
- dev_dbg(c->dev, "%s - %s map = %#x\n",
- is_tx ? "tx channel" : "rx slot",
- e->texts[mux], mux ? map[mux - 1] : -1);
- return 0;
-}
-
-/* sst_check_and_send_slot_map - helper for checking power state and sending
- * slot map cmd
- *
- * called with lock held
- */
-static int sst_check_and_send_slot_map(struct sst_data *drv, struct snd_kcontrol *kcontrol)
-{
- struct sst_enum *e = (void *)kcontrol->private_value;
- int ret = 0;
-
- if (e->w && e->w->power)
- ret = sst_send_slot_map(drv);
- else
- dev_err(&drv->pdev->dev, "Slot control: %s doesn't have DAPM widget!!!\n",
- kcontrol->id.name);
- return ret;
-}
-
-/**
- * sst_slot_put - set the status of interleaver/deinterleaver control
- *
- * (de)interleaver controls are defined in opposite sense to be user-friendly
- *
- * Instead of the enum value being the value written to the register, it is the
- * register address; and the kcontrol number (register num) is the value written
- * to the register. This is so that there can be only one value for each
- * slot/channel since there is only one control for each slot/channel.
- *
- * This means that whenever an enum is set, we need to clear the bit
- * for that kcontrol_no for all the interleaver OR deinterleaver registers
- */
-static int sst_slot_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
- struct sst_data *drv = snd_soc_component_get_drvdata(c);
- struct sst_enum *e = (void *)kcontrol->private_value;
- int i, ret = 0;
- unsigned int ctl_no = e->reg;
- unsigned int is_tx = e->tx;
- unsigned int slot_channel_no;
- unsigned int val, mux;
- u8 *map;
-
- map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map;
-
- val = 1 << ctl_no;
- mux = ucontrol->value.enumerated.item[0];
- if (mux > e->max - 1)
- return -EINVAL;
-
- mutex_lock(&drv->lock);
- /* first clear all registers of this bit */
- for (i = 0; i < e->max; i++)
- map[i] &= ~val;
-
- if (mux == 0) {
- /* kctl set to 'none' and we reset the bits so send IPC */
- ret = sst_check_and_send_slot_map(drv, kcontrol);
-
- mutex_unlock(&drv->lock);
- return ret;
- }
-
- /* offset by one to take "None" into account */
- slot_channel_no = mux - 1;
- map[slot_channel_no] |= val;
-
- dev_dbg(c->dev, "%s %s map = %#x\n",
- is_tx ? "tx channel" : "rx slot",
- e->texts[mux], map[slot_channel_no]);
-
- ret = sst_check_and_send_slot_map(drv, kcontrol);
-
- mutex_unlock(&drv->lock);
- return ret;
-}
-
-static int sst_send_algo_cmd(struct sst_data *drv,
- struct sst_algo_control *bc)
-{
- int len, ret = 0;
- struct sst_cmd_set_params *cmd;
-
- /*bc->max includes sizeof algos + length field*/
- len = sizeof(cmd->dst) + sizeof(cmd->command_id) + bc->max;
-
- cmd = kzalloc(len, GFP_KERNEL);
- if (cmd == NULL)
- return -ENOMEM;
-
- SST_FILL_DESTINATION(2, cmd->dst, bc->pipe_id, bc->module_id);
- cmd->command_id = bc->cmd_id;
- memcpy(cmd->params, bc->params, bc->max);
-
- ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,
- SST_FLAG_BLOCKED, bc->task_id, 0, cmd, len);
- kfree(cmd);
- return ret;
-}
-
-/**
- * sst_find_and_send_pipe_algo - send all the algo parameters for a pipe
- *
- * The algos which are in each pipeline are sent to the firmware one by one
- *
- * Called with lock held
- */
-static int sst_find_and_send_pipe_algo(struct sst_data *drv,
- const char *pipe, struct sst_ids *ids)
-{
- int ret = 0;
- struct sst_algo_control *bc;
- struct sst_module *algo = NULL;
-
- dev_dbg(&drv->pdev->dev, "Enter: widget=%s\n", pipe);
-
- list_for_each_entry(algo, &ids->algo_list, node) {
- bc = (void *)algo->kctl->private_value;
-
- dev_dbg(&drv->pdev->dev, "Found algo control name=%s pipe=%s\n",
- algo->kctl->id.name, pipe);
- ret = sst_send_algo_cmd(drv, bc);
- if (ret)
- return ret;
- }
- return ret;
-}
-
-static int sst_algo_bytes_ctl_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct sst_algo_control *bc = (void *)kcontrol->private_value;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
- uinfo->count = bc->max;
-
- return 0;
-}
-
-static int sst_algo_control_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct sst_algo_control *bc = (void *)kcontrol->private_value;
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-
- switch (bc->type) {
- case SST_ALGO_PARAMS:
- memcpy(ucontrol->value.bytes.data, bc->params, bc->max);
- break;
- default:
- dev_err(component->dev, "Invalid Input- algo type:%d\n",
- bc->type);
- return -EINVAL;
-
- }
- return 0;
-}
-
-static int sst_algo_control_set(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- int ret = 0;
- struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
- struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);
- struct sst_algo_control *bc = (void *)kcontrol->private_value;
-
- dev_dbg(cmpnt->dev, "control_name=%s\n", kcontrol->id.name);
- mutex_lock(&drv->lock);
- switch (bc->type) {
- case SST_ALGO_PARAMS:
- memcpy(bc->params, ucontrol->value.bytes.data, bc->max);
- break;
- default:
- mutex_unlock(&drv->lock);
- dev_err(cmpnt->dev, "Invalid Input- algo type:%d\n",
- bc->type);
- return -EINVAL;
- }
- /*if pipe is enabled, need to send the algo params from here*/
- if (bc->w && bc->w->power)
- ret = sst_send_algo_cmd(drv, bc);
- mutex_unlock(&drv->lock);
-
- return ret;
-}
-
-static int sst_gain_ctl_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = mc->stereo ? 2 : 1;
- uinfo->value.integer.min = mc->min;
- uinfo->value.integer.max = mc->max;
-
- return 0;
-}
-
-/**
- * sst_send_gain_cmd - send the gain algorithm IPC to the FW
- * @gv: the stored value of gain (also contains rampduration)
- * @mute: flag that indicates whether this was called from the
- * digital_mute callback or directly. If called from the
- * digital_mute callback, module will be muted/unmuted based on this
- * flag. The flag is always 0 if called directly.
- *
- * Called with sst_data.lock held
- *
- * The user-set gain value is sent only if the user-controllable 'mute' control
- * is OFF (indicated by gv->mute). Otherwise, the mute value (MIN value) is
- * sent.
- */
-static int sst_send_gain_cmd(struct sst_data *drv, struct sst_gain_value *gv,
- u16 task_id, u16 loc_id, u16 module_id, int mute)
-{
- struct sst_cmd_set_gain_dual cmd;
-
- dev_dbg(&drv->pdev->dev, "Enter\n");
-
- cmd.header.command_id = MMX_SET_GAIN;
- SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
- cmd.gain_cell_num = 1;
-
- if (mute || gv->mute) {
- cmd.cell_gains[0].cell_gain_left = SST_GAIN_MIN_VALUE;
- cmd.cell_gains[0].cell_gain_right = SST_GAIN_MIN_VALUE;
- } else {
- cmd.cell_gains[0].cell_gain_left = gv->l_gain;
- cmd.cell_gains[0].cell_gain_right = gv->r_gain;
- }
-
- SST_FILL_DESTINATION(2, cmd.cell_gains[0].dest,
- loc_id, module_id);
- cmd.cell_gains[0].gain_time_constant = gv->ramp_duration;
-
- cmd.header.length = sizeof(struct sst_cmd_set_gain_dual)
- - sizeof(struct sst_dsp_header);
-
- /* we are with lock held, so call the unlocked api to send */
- return sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,
- SST_FLAG_BLOCKED, task_id, 0, &cmd,
- sizeof(cmd.header) + cmd.header.length);
-}
-
-static int sst_gain_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
- struct sst_gain_value *gv = mc->gain_val;
-
- switch (mc->type) {
- case SST_GAIN_TLV:
- ucontrol->value.integer.value[0] = gv->l_gain;
- ucontrol->value.integer.value[1] = gv->r_gain;
- break;
-
- case SST_GAIN_MUTE:
- ucontrol->value.integer.value[0] = gv->mute ? 1 : 0;
- break;
-
- case SST_GAIN_RAMP_DURATION:
- ucontrol->value.integer.value[0] = gv->ramp_duration;
- break;
-
- default:
- dev_err(component->dev, "Invalid Input- gain type:%d\n",
- mc->type);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int sst_gain_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- int ret = 0;
- struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
- struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);
- struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
- struct sst_gain_value *gv = mc->gain_val;
-
- mutex_lock(&drv->lock);
-
- switch (mc->type) {
- case SST_GAIN_TLV:
- gv->l_gain = ucontrol->value.integer.value[0];
- gv->r_gain = ucontrol->value.integer.value[1];
- dev_dbg(cmpnt->dev, "%s: Volume %d, %d\n",
- mc->pname, gv->l_gain, gv->r_gain);
- break;
-
- case SST_GAIN_MUTE:
- gv->mute = !!ucontrol->value.integer.value[0];
- dev_dbg(cmpnt->dev, "%s: Mute %d\n", mc->pname, gv->mute);
- break;
-
- case SST_GAIN_RAMP_DURATION:
- gv->ramp_duration = ucontrol->value.integer.value[0];
- dev_dbg(cmpnt->dev, "%s: Ramp Delay%d\n",
- mc->pname, gv->ramp_duration);
- break;
-
- default:
- mutex_unlock(&drv->lock);
- dev_err(cmpnt->dev, "Invalid Input- gain type:%d\n",
- mc->type);
- return -EINVAL;
- }
-
- if (mc->w && mc->w->power)
- ret = sst_send_gain_cmd(drv, gv, mc->task_id,
- mc->pipe_id | mc->instance_id, mc->module_id, 0);
- mutex_unlock(&drv->lock);
-
- return ret;
-}
-
-static int sst_set_pipe_gain(struct sst_ids *ids,
- struct sst_data *drv, int mute);
-
-static int sst_send_pipe_module_params(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol)
-{
- struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
- struct sst_data *drv = snd_soc_component_get_drvdata(c);
- struct sst_ids *ids = w->priv;
-
- mutex_lock(&drv->lock);
- sst_find_and_send_pipe_algo(drv, w->name, ids);
- sst_set_pipe_gain(ids, drv, 0);
- mutex_unlock(&drv->lock);
-
- return 0;
-}
-
-static int sst_generic_modules_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- if (SND_SOC_DAPM_EVENT_ON(event))
- return sst_send_pipe_module_params(w, k);
- return 0;
-}
-
-static const DECLARE_TLV_DB_SCALE(sst_gain_tlv_common, SST_GAIN_MIN_VALUE * 10, 10, 0);
-
-/* Look up table to convert MIXER SW bit regs to SWM inputs */
-static const uint swm_mixer_input_ids[SST_SWM_INPUT_COUNT] = {
- [SST_IP_CODEC0] = SST_SWM_IN_CODEC0,
- [SST_IP_CODEC1] = SST_SWM_IN_CODEC1,
- [SST_IP_LOOP0] = SST_SWM_IN_SPROT_LOOP,
- [SST_IP_LOOP1] = SST_SWM_IN_MEDIA_LOOP1,
- [SST_IP_LOOP2] = SST_SWM_IN_MEDIA_LOOP2,
- [SST_IP_PCM0] = SST_SWM_IN_PCM0,
- [SST_IP_PCM1] = SST_SWM_IN_PCM1,
- [SST_IP_MEDIA0] = SST_SWM_IN_MEDIA0,
- [SST_IP_MEDIA1] = SST_SWM_IN_MEDIA1,
- [SST_IP_MEDIA2] = SST_SWM_IN_MEDIA2,
- [SST_IP_MEDIA3] = SST_SWM_IN_MEDIA3,
-};
-
-/**
- * fill_swm_input - fill in the SWM input ids given the register
- *
- * The register value is a bit-field inicated which mixer inputs are ON. Use the
- * lookup table to get the input-id and fill it in the structure.
- */
-static int fill_swm_input(struct snd_soc_component *cmpnt,
- struct swm_input_ids *swm_input, unsigned int reg)
-{
- uint i, is_set, nb_inputs = 0;
- u16 input_loc_id;
-
- dev_dbg(cmpnt->dev, "reg: %#x\n", reg);
- for (i = 0; i < SST_SWM_INPUT_COUNT; i++) {
- is_set = reg & BIT(i);
- if (!is_set)
- continue;
-
- input_loc_id = swm_mixer_input_ids[i];
- SST_FILL_DESTINATION(2, swm_input->input_id,
- input_loc_id, SST_DEFAULT_MODULE_ID);
- nb_inputs++;
- swm_input++;
- dev_dbg(cmpnt->dev, "input id: %#x, nb_inputs: %d\n",
- input_loc_id, nb_inputs);
-
- if (nb_inputs == SST_CMD_SWM_MAX_INPUTS) {
- dev_warn(cmpnt->dev, "SET_SWM cmd max inputs reached");
- break;
- }
- }
- return nb_inputs;
-}
-
-
-/**
- * called with lock held
- */
-static int sst_set_pipe_gain(struct sst_ids *ids,
- struct sst_data *drv, int mute)
-{
- int ret = 0;
- struct sst_gain_mixer_control *mc;
- struct sst_gain_value *gv;
- struct sst_module *gain = NULL;
-
- list_for_each_entry(gain, &ids->gain_list, node) {
- struct snd_kcontrol *kctl = gain->kctl;
-
- dev_dbg(&drv->pdev->dev, "control name=%s\n", kctl->id.name);
- mc = (void *)kctl->private_value;
- gv = mc->gain_val;
-
- ret = sst_send_gain_cmd(drv, gv, mc->task_id,
- mc->pipe_id | mc->instance_id, mc->module_id, mute);
- if (ret)
- return ret;
- }
- return ret;
-}
-
-static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- struct sst_cmd_set_swm cmd;
- struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
- struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);
- struct sst_ids *ids = w->priv;
- bool set_mixer = false;
- struct soc_mixer_control *mc;
- int val = 0;
- int i = 0;
-
- dev_dbg(cmpnt->dev, "widget = %s\n", w->name);
- /*
- * Identify which mixer input is on and send the bitmap of the
- * inputs as an IPC to the DSP.
- */
- for (i = 0; i < w->num_kcontrols; i++) {
- if (dapm_kcontrol_get_value(w->kcontrols[i])) {
- mc = (struct soc_mixer_control *)(w->kcontrols[i])->private_value;
- val |= 1 << mc->shift;
- }
- }
- dev_dbg(cmpnt->dev, "val = %#x\n", val);
-
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- case SND_SOC_DAPM_POST_PMD:
- set_mixer = true;
- break;
- case SND_SOC_DAPM_POST_REG:
- if (w->power)
- set_mixer = true;
- break;
- default:
- set_mixer = false;
- }
-
- if (set_mixer == false)
- return 0;
-
- if (SND_SOC_DAPM_EVENT_ON(event) ||
- event == SND_SOC_DAPM_POST_REG)
- cmd.switch_state = SST_SWM_ON;
- else
- cmd.switch_state = SST_SWM_OFF;
-
- SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
- /* MMX_SET_SWM == SBA_SET_SWM */
- cmd.header.command_id = SBA_SET_SWM;
-
- SST_FILL_DESTINATION(2, cmd.output_id,
- ids->location_id, SST_DEFAULT_MODULE_ID);
- cmd.nb_inputs = fill_swm_input(cmpnt, &cmd.input[0], val);
- cmd.header.length = offsetof(struct sst_cmd_set_swm, input)
- - sizeof(struct sst_dsp_header)
- + (cmd.nb_inputs * sizeof(cmd.input[0]));
-
- return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
- ids->task_id, 0, &cmd,
- sizeof(cmd.header) + cmd.header.length);
-}
-
-/* SBA mixers - 16 inputs */
-#define SST_SBA_DECLARE_MIX_CONTROLS(kctl_name) \
- static const struct snd_kcontrol_new kctl_name[] = { \
- SOC_DAPM_SINGLE("codec_in0 Switch", SND_SOC_NOPM, SST_IP_CODEC0, 1, 0), \
- SOC_DAPM_SINGLE("codec_in1 Switch", SND_SOC_NOPM, SST_IP_CODEC1, 1, 0), \
- SOC_DAPM_SINGLE("sprot_loop_in Switch", SND_SOC_NOPM, SST_IP_LOOP0, 1, 0), \
- SOC_DAPM_SINGLE("media_loop1_in Switch", SND_SOC_NOPM, SST_IP_LOOP1, 1, 0), \
- SOC_DAPM_SINGLE("media_loop2_in Switch", SND_SOC_NOPM, SST_IP_LOOP2, 1, 0), \
- SOC_DAPM_SINGLE("pcm0_in Switch", SND_SOC_NOPM, SST_IP_PCM0, 1, 0), \
- SOC_DAPM_SINGLE("pcm1_in Switch", SND_SOC_NOPM, SST_IP_PCM1, 1, 0), \
- }
-
-#define SST_SBA_MIXER_GRAPH_MAP(mix_name) \
- { mix_name, "codec_in0 Switch", "codec_in0" }, \
- { mix_name, "codec_in1 Switch", "codec_in1" }, \
- { mix_name, "sprot_loop_in Switch", "sprot_loop_in" }, \
- { mix_name, "media_loop1_in Switch", "media_loop1_in" }, \
- { mix_name, "media_loop2_in Switch", "media_loop2_in" }, \
- { mix_name, "pcm0_in Switch", "pcm0_in" }, \
- { mix_name, "pcm1_in Switch", "pcm1_in" }
-
-#define SST_MMX_DECLARE_MIX_CONTROLS(kctl_name) \
- static const struct snd_kcontrol_new kctl_name[] = { \
- SOC_DAPM_SINGLE("media0_in Switch", SND_SOC_NOPM, SST_IP_MEDIA0, 1, 0), \
- SOC_DAPM_SINGLE("media1_in Switch", SND_SOC_NOPM, SST_IP_MEDIA1, 1, 0), \
- SOC_DAPM_SINGLE("media2_in Switch", SND_SOC_NOPM, SST_IP_MEDIA2, 1, 0), \
- SOC_DAPM_SINGLE("media3_in Switch", SND_SOC_NOPM, SST_IP_MEDIA3, 1, 0), \
- }
-
-SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media0_controls);
-SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media1_controls);
-
-/* 18 SBA mixers */
-SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm0_controls);
-SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm1_controls);
-SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm2_controls);
-SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_sprot_l0_controls);
-SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l1_controls);
-SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l2_controls);
-SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_voip_controls);
-SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec0_controls);
-SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec1_controls);
-
-/*
- * sst_handle_vb_timer - Start/Stop the DSP scheduler
- *
- * The DSP expects first cmd to be SBA_VB_START, so at first startup send
- * that.
- * DSP expects last cmd to be SBA_VB_IDLE, so at last shutdown send that.
- *
- * Do refcount internally so that we send command only at first start
- * and last end. Since SST driver does its own ref count, invoke sst's
- * power ops always!
- */
-int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable)
-{
- int ret = 0;
- struct sst_cmd_generic cmd;
- struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
- static int timer_usage;
-
- if (enable)
- cmd.header.command_id = SBA_VB_START;
- else
- cmd.header.command_id = SBA_IDLE;
- dev_dbg(dai->dev, "enable=%u, usage=%d\n", enable, timer_usage);
-
- SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
- cmd.header.length = 0;
-
- if (enable) {
- ret = sst->ops->power(sst->dev, true);
- if (ret < 0)
- return ret;
- }
-
- mutex_lock(&drv->lock);
- if (enable)
- timer_usage++;
- else
- timer_usage--;
-
- /*
- * Send the command only if this call is the first enable or last
- * disable
- */
- if ((enable && (timer_usage == 1)) ||
- (!enable && (timer_usage == 0))) {
- ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD,
- SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd,
- sizeof(cmd.header) + cmd.header.length);
- if (ret && enable) {
- timer_usage--;
- enable = false;
- }
- }
- mutex_unlock(&drv->lock);
-
- if (!enable)
- sst->ops->power(sst->dev, false);
- return ret;
-}
-
-/**
- * sst_ssp_config - contains SSP configuration for media UC
- */
-static const struct sst_ssp_config sst_ssp_configs = {
- .ssp_id = SSP_CODEC,
- .bits_per_slot = 24,
- .slots = 4,
- .ssp_mode = SSP_MODE_MASTER,
- .pcm_mode = SSP_PCM_MODE_NETWORK,
- .duplex = SSP_DUPLEX,
- .ssp_protocol = SSP_MODE_PCM,
- .fs_width = 1,
- .fs_frequency = SSP_FS_48_KHZ,
- .active_slot_map = 0xF,
- .start_delay = 0,
-};
-
-int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable)
-{
- struct sst_cmd_sba_hw_set_ssp cmd;
- struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
- const struct sst_ssp_config *config;
-
- dev_info(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id);
-
- SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
- cmd.header.command_id = SBA_HW_SET_SSP;
- cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp)
- - sizeof(struct sst_dsp_header);
-
- config = &sst_ssp_configs;
- dev_dbg(dai->dev, "ssp_id: %u\n", config->ssp_id);
-
- if (enable)
- cmd.switch_state = SST_SWITCH_ON;
- else
- cmd.switch_state = SST_SWITCH_OFF;
-
- cmd.selection = config->ssp_id;
- cmd.nb_bits_per_slots = config->bits_per_slot;
- cmd.nb_slots = config->slots;
- cmd.mode = config->ssp_mode | (config->pcm_mode << 1);
- cmd.duplex = config->duplex;
- cmd.active_tx_slot_map = config->active_slot_map;
- cmd.active_rx_slot_map = config->active_slot_map;
- cmd.frame_sync_frequency = config->fs_frequency;
- cmd.frame_sync_polarity = SSP_FS_ACTIVE_HIGH;
- cmd.data_polarity = 1;
- cmd.frame_sync_width = config->fs_width;
- cmd.ssp_protocol = config->ssp_protocol;
- cmd.start_delay = config->start_delay;
- cmd.reserved1 = cmd.reserved2 = 0xFF;
-
- return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
- SST_TASK_SBA, 0, &cmd,
- sizeof(cmd.header) + cmd.header.length);
-}
-
-static int sst_set_be_modules(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- int ret = 0;
- struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
- struct sst_data *drv = snd_soc_component_get_drvdata(c);
-
- dev_dbg(c->dev, "Enter: widget=%s\n", w->name);
-
- if (SND_SOC_DAPM_EVENT_ON(event)) {
- ret = sst_send_slot_map(drv);
- if (ret)
- return ret;
- ret = sst_send_pipe_module_params(w, k);
- }
- return ret;
-}
-
-static int sst_set_media_path(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- int ret = 0;
- struct sst_cmd_set_media_path cmd;
- struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
- struct sst_data *drv = snd_soc_component_get_drvdata(c);
- struct sst_ids *ids = w->priv;
-
- dev_dbg(c->dev, "widget=%s\n", w->name);
- dev_dbg(c->dev, "task=%u, location=%#x\n",
- ids->task_id, ids->location_id);
-
- if (SND_SOC_DAPM_EVENT_ON(event))
- cmd.switch_state = SST_PATH_ON;
- else
- cmd.switch_state = SST_PATH_OFF;
-
- SST_FILL_DESTINATION(2, cmd.header.dst,
- ids->location_id, SST_DEFAULT_MODULE_ID);
-
- /* MMX_SET_MEDIA_PATH == SBA_SET_MEDIA_PATH */
- cmd.header.command_id = MMX_SET_MEDIA_PATH;
- cmd.header.length = sizeof(struct sst_cmd_set_media_path)
- - sizeof(struct sst_dsp_header);
-
- ret = sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
- ids->task_id, 0, &cmd,
- sizeof(cmd.header) + cmd.header.length);
- if (ret)
- return ret;
-
- if (SND_SOC_DAPM_EVENT_ON(event))
- ret = sst_send_pipe_module_params(w, k);
- return ret;
-}
-
-static int sst_set_media_loop(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- int ret = 0;
- struct sst_cmd_sba_set_media_loop_map cmd;
- struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
- struct sst_data *drv = snd_soc_component_get_drvdata(c);
- struct sst_ids *ids = w->priv;
-
- dev_dbg(c->dev, "Enter:widget=%s\n", w->name);
- if (SND_SOC_DAPM_EVENT_ON(event))
- cmd.switch_state = SST_SWITCH_ON;
- else
- cmd.switch_state = SST_SWITCH_OFF;
-
- SST_FILL_DESTINATION(2, cmd.header.dst,
- ids->location_id, SST_DEFAULT_MODULE_ID);
-
- cmd.header.command_id = SBA_SET_MEDIA_LOOP_MAP;
- cmd.header.length = sizeof(struct sst_cmd_sba_set_media_loop_map)
- - sizeof(struct sst_dsp_header);
- cmd.param.part.cfg.rate = 2; /* 48khz */
-
- cmd.param.part.cfg.format = ids->format; /* stereo/Mono */
- cmd.param.part.cfg.s_length = 1; /* 24bit left justified */
- cmd.map = 0; /* Algo sequence: Gain - DRP - FIR - IIR */
-
- ret = sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
- SST_TASK_SBA, 0, &cmd,
- sizeof(cmd.header) + cmd.header.length);
- if (ret)
- return ret;
-
- if (SND_SOC_DAPM_EVENT_ON(event))
- ret = sst_send_pipe_module_params(w, k);
- return ret;
-}
-
-static const struct snd_soc_dapm_widget sst_dapm_widgets[] = {
- SST_AIF_IN("codec_in0", sst_set_be_modules),
- SST_AIF_IN("codec_in1", sst_set_be_modules),
- SST_AIF_OUT("codec_out0", sst_set_be_modules),
- SST_AIF_OUT("codec_out1", sst_set_be_modules),
-
- /* Media Paths */
- /* MediaX IN paths are set via ALLOC, so no SET_MEDIA_PATH command */
- SST_PATH_INPUT("media0_in", SST_TASK_MMX, SST_SWM_IN_MEDIA0, sst_generic_modules_event),
- SST_PATH_INPUT("media1_in", SST_TASK_MMX, SST_SWM_IN_MEDIA1, NULL),
- SST_PATH_INPUT("media2_in", SST_TASK_MMX, SST_SWM_IN_MEDIA2, sst_set_media_path),
- SST_PATH_INPUT("media3_in", SST_TASK_MMX, SST_SWM_IN_MEDIA3, NULL),
- SST_PATH_OUTPUT("media0_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA0, sst_set_media_path),
- SST_PATH_OUTPUT("media1_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA1, sst_set_media_path),
-
- /* SBA PCM Paths */
- SST_PATH_INPUT("pcm0_in", SST_TASK_SBA, SST_SWM_IN_PCM0, sst_set_media_path),
- SST_PATH_INPUT("pcm1_in", SST_TASK_SBA, SST_SWM_IN_PCM1, sst_set_media_path),
- SST_PATH_OUTPUT("pcm0_out", SST_TASK_SBA, SST_SWM_OUT_PCM0, sst_set_media_path),
- SST_PATH_OUTPUT("pcm1_out", SST_TASK_SBA, SST_SWM_OUT_PCM1, sst_set_media_path),
- SST_PATH_OUTPUT("pcm2_out", SST_TASK_SBA, SST_SWM_OUT_PCM2, sst_set_media_path),
-
- /* SBA Loops */
- SST_PATH_INPUT("sprot_loop_in", SST_TASK_SBA, SST_SWM_IN_SPROT_LOOP, NULL),
- SST_PATH_INPUT("media_loop1_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP1, NULL),
- SST_PATH_INPUT("media_loop2_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP2, NULL),
- SST_PATH_MEDIA_LOOP_OUTPUT("sprot_loop_out", SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, SST_FMT_MONO, sst_set_media_loop),
- SST_PATH_MEDIA_LOOP_OUTPUT("media_loop1_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, SST_FMT_MONO, sst_set_media_loop),
- SST_PATH_MEDIA_LOOP_OUTPUT("media_loop2_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, SST_FMT_STEREO, sst_set_media_loop),
-
- /* Media Mixers */
- SST_SWM_MIXER("media0_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA0,
- sst_mix_media0_controls, sst_swm_mixer_event),
- SST_SWM_MIXER("media1_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA1,
- sst_mix_media1_controls, sst_swm_mixer_event),
-
- /* SBA PCM mixers */
- SST_SWM_MIXER("pcm0_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM0,
- sst_mix_pcm0_controls, sst_swm_mixer_event),
- SST_SWM_MIXER("pcm1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM1,
- sst_mix_pcm1_controls, sst_swm_mixer_event),
- SST_SWM_MIXER("pcm2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM2,
- sst_mix_pcm2_controls, sst_swm_mixer_event),
-
- /* SBA Loop mixers */
- SST_SWM_MIXER("sprot_loop_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP,
- sst_mix_sprot_l0_controls, sst_swm_mixer_event),
- SST_SWM_MIXER("media_loop1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1,
- sst_mix_media_l1_controls, sst_swm_mixer_event),
- SST_SWM_MIXER("media_loop2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2,
- sst_mix_media_l2_controls, sst_swm_mixer_event),
-
- /* SBA Backend mixers */
- SST_SWM_MIXER("codec_out0 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC0,
- sst_mix_codec0_controls, sst_swm_mixer_event),
- SST_SWM_MIXER("codec_out1 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC1,
- sst_mix_codec1_controls, sst_swm_mixer_event),
-};
-
-static const struct snd_soc_dapm_route intercon[] = {
- {"media0_in", NULL, "Compress Playback"},
- {"media1_in", NULL, "Headset Playback"},
- {"media2_in", NULL, "pcm0_out"},
-
- {"media0_out mix 0", "media0_in Switch", "media0_in"},
- {"media0_out mix 0", "media1_in Switch", "media1_in"},
- {"media0_out mix 0", "media2_in Switch", "media2_in"},
- {"media0_out mix 0", "media3_in Switch", "media3_in"},
- {"media1_out mix 0", "media0_in Switch", "media0_in"},
- {"media1_out mix 0", "media1_in Switch", "media1_in"},
- {"media1_out mix 0", "media2_in Switch", "media2_in"},
- {"media1_out mix 0", "media3_in Switch", "media3_in"},
-
- {"media0_out", NULL, "media0_out mix 0"},
- {"media1_out", NULL, "media1_out mix 0"},
- {"pcm0_in", NULL, "media0_out"},
- {"pcm1_in", NULL, "media1_out"},
-
- {"Headset Capture", NULL, "pcm1_out"},
- {"Headset Capture", NULL, "pcm2_out"},
- {"pcm0_out", NULL, "pcm0_out mix 0"},
- SST_SBA_MIXER_GRAPH_MAP("pcm0_out mix 0"),
- {"pcm1_out", NULL, "pcm1_out mix 0"},
- SST_SBA_MIXER_GRAPH_MAP("pcm1_out mix 0"),
- {"pcm2_out", NULL, "pcm2_out mix 0"},
- SST_SBA_MIXER_GRAPH_MAP("pcm2_out mix 0"),
-
- {"media_loop1_in", NULL, "media_loop1_out"},
- {"media_loop1_out", NULL, "media_loop1_out mix 0"},
- SST_SBA_MIXER_GRAPH_MAP("media_loop1_out mix 0"),
- {"media_loop2_in", NULL, "media_loop2_out"},
- {"media_loop2_out", NULL, "media_loop2_out mix 0"},
- SST_SBA_MIXER_GRAPH_MAP("media_loop2_out mix 0"),
- {"sprot_loop_in", NULL, "sprot_loop_out"},
- {"sprot_loop_out", NULL, "sprot_loop_out mix 0"},
- SST_SBA_MIXER_GRAPH_MAP("sprot_loop_out mix 0"),
-
- {"codec_out0", NULL, "codec_out0 mix 0"},
- SST_SBA_MIXER_GRAPH_MAP("codec_out0 mix 0"),
- {"codec_out1", NULL, "codec_out1 mix 0"},
- SST_SBA_MIXER_GRAPH_MAP("codec_out1 mix 0"),
-
-};
-static const char * const slot_names[] = {
- "none",
- "slot 0", "slot 1", "slot 2", "slot 3",
- "slot 4", "slot 5", "slot 6", "slot 7", /* not supported by FW */
-};
-
-static const char * const channel_names[] = {
- "none",
- "codec_out0_0", "codec_out0_1", "codec_out1_0", "codec_out1_1",
- "codec_out2_0", "codec_out2_1", "codec_out3_0", "codec_out3_1", /* not supported by FW */
-};
-
-#define SST_INTERLEAVER(xpname, slot_name, slotno) \
- SST_SSP_SLOT_CTL(xpname, "tx interleaver", slot_name, slotno, true, \
- channel_names, sst_slot_get, sst_slot_put)
-
-#define SST_DEINTERLEAVER(xpname, channel_name, channel_no) \
- SST_SSP_SLOT_CTL(xpname, "rx deinterleaver", channel_name, channel_no, false, \
- slot_names, sst_slot_get, sst_slot_put)
-
-static const struct snd_kcontrol_new sst_slot_controls[] = {
- SST_INTERLEAVER("codec_out", "slot 0", 0),
- SST_INTERLEAVER("codec_out", "slot 1", 1),
- SST_INTERLEAVER("codec_out", "slot 2", 2),
- SST_INTERLEAVER("codec_out", "slot 3", 3),
- SST_DEINTERLEAVER("codec_in", "codec_in0_0", 0),
- SST_DEINTERLEAVER("codec_in", "codec_in0_1", 1),
- SST_DEINTERLEAVER("codec_in", "codec_in1_0", 2),
- SST_DEINTERLEAVER("codec_in", "codec_in1_1", 3),
-};
-
-/* Gain helper with min/max set */
-#define SST_GAIN(name, path_id, task_id, instance, gain_var) \
- SST_GAIN_KCONTROLS(name, "Gain", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE, \
- SST_GAIN_TC_MIN, SST_GAIN_TC_MAX, \
- sst_gain_get, sst_gain_put, \
- SST_MODULE_ID_GAIN_CELL, path_id, instance, task_id, \
- sst_gain_tlv_common, gain_var)
-
-#define SST_VOLUME(name, path_id, task_id, instance, gain_var) \
- SST_GAIN_KCONTROLS(name, "Volume", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE, \
- SST_GAIN_TC_MIN, SST_GAIN_TC_MAX, \
- sst_gain_get, sst_gain_put, \
- SST_MODULE_ID_VOLUME, path_id, instance, task_id, \
- sst_gain_tlv_common, gain_var)
-
-static struct sst_gain_value sst_gains[];
-
-static const struct snd_kcontrol_new sst_gain_controls[] = {
- SST_GAIN("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[0]),
- SST_GAIN("media1_in", SST_PATH_INDEX_MEDIA1_IN, SST_TASK_MMX, 0, &sst_gains[1]),
- SST_GAIN("media2_in", SST_PATH_INDEX_MEDIA2_IN, SST_TASK_MMX, 0, &sst_gains[2]),
- SST_GAIN("media3_in", SST_PATH_INDEX_MEDIA3_IN, SST_TASK_MMX, 0, &sst_gains[3]),
-
- SST_GAIN("pcm0_in", SST_PATH_INDEX_PCM0_IN, SST_TASK_SBA, 0, &sst_gains[4]),
- SST_GAIN("pcm1_in", SST_PATH_INDEX_PCM1_IN, SST_TASK_SBA, 0, &sst_gains[5]),
- SST_GAIN("pcm1_out", SST_PATH_INDEX_PCM1_OUT, SST_TASK_SBA, 0, &sst_gains[6]),
- SST_GAIN("pcm2_out", SST_PATH_INDEX_PCM2_OUT, SST_TASK_SBA, 0, &sst_gains[7]),
-
- SST_GAIN("codec_in0", SST_PATH_INDEX_CODEC_IN0, SST_TASK_SBA, 0, &sst_gains[8]),
- SST_GAIN("codec_in1", SST_PATH_INDEX_CODEC_IN1, SST_TASK_SBA, 0, &sst_gains[9]),
- SST_GAIN("codec_out0", SST_PATH_INDEX_CODEC_OUT0, SST_TASK_SBA, 0, &sst_gains[10]),
- SST_GAIN("codec_out1", SST_PATH_INDEX_CODEC_OUT1, SST_TASK_SBA, 0, &sst_gains[11]),
- SST_GAIN("media_loop1_out", SST_PATH_INDEX_MEDIA_LOOP1_OUT, SST_TASK_SBA, 0, &sst_gains[12]),
- SST_GAIN("media_loop2_out", SST_PATH_INDEX_MEDIA_LOOP2_OUT, SST_TASK_SBA, 0, &sst_gains[13]),
- SST_GAIN("sprot_loop_out", SST_PATH_INDEX_SPROT_LOOP_OUT, SST_TASK_SBA, 0, &sst_gains[14]),
- SST_VOLUME("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[15]),
-};
-
-#define SST_GAIN_NUM_CONTROLS 3
-/* the SST_GAIN macro above will create three alsa controls for each
- * instance invoked, gain, mute and ramp duration, which use the same gain
- * cell sst_gain to keep track of data
- * To calculate number of gain cell instances we need to device by 3 in
- * below caulcation for gain cell memory.
- * This gets rid of static number and issues while adding new controls
- */
-static struct sst_gain_value sst_gains[ARRAY_SIZE(sst_gain_controls)/SST_GAIN_NUM_CONTROLS];
-
-static const struct snd_kcontrol_new sst_algo_controls[] = {
- SST_ALGO_KCONTROL_BYTES("media_loop1_out", "fir", 272, SST_MODULE_ID_FIR_24,
- SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR),
- SST_ALGO_KCONTROL_BYTES("media_loop1_out", "iir", 300, SST_MODULE_ID_IIR_24,
- SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_IIR),
- SST_ALGO_KCONTROL_BYTES("media_loop1_out", "mdrp", 286, SST_MODULE_ID_MDRP,
- SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_SET_MDRP),
- SST_ALGO_KCONTROL_BYTES("media_loop2_out", "fir", 272, SST_MODULE_ID_FIR_24,
- SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR),
- SST_ALGO_KCONTROL_BYTES("media_loop2_out", "iir", 300, SST_MODULE_ID_IIR_24,
- SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_VB_SET_IIR),
- SST_ALGO_KCONTROL_BYTES("media_loop2_out", "mdrp", 286, SST_MODULE_ID_MDRP,
- SST_PATH_INDEX_MEDIA_LOOP2_OUT, 0, SST_TASK_SBA, SBA_SET_MDRP),
- SST_ALGO_KCONTROL_BYTES("sprot_loop_out", "lpro", 192, SST_MODULE_ID_SPROT,
- SST_PATH_INDEX_SPROT_LOOP_OUT, 0, SST_TASK_SBA, SBA_VB_LPRO),
- SST_ALGO_KCONTROL_BYTES("codec_in0", "dcr", 52, SST_MODULE_ID_FILT_DCR,
- SST_PATH_INDEX_CODEC_IN0, 0, SST_TASK_SBA, SBA_VB_SET_IIR),
- SST_ALGO_KCONTROL_BYTES("codec_in1", "dcr", 52, SST_MODULE_ID_FILT_DCR,
- SST_PATH_INDEX_CODEC_IN1, 0, SST_TASK_SBA, SBA_VB_SET_IIR),
-
-};
-
-static int sst_algo_control_init(struct device *dev)
-{
- int i = 0;
- struct sst_algo_control *bc;
- /*allocate space to cache the algo parameters in the driver*/
- for (i = 0; i < ARRAY_SIZE(sst_algo_controls); i++) {
- bc = (struct sst_algo_control *)sst_algo_controls[i].private_value;
- bc->params = devm_kzalloc(dev, bc->max, GFP_KERNEL);
- if (bc->params == NULL)
- return -ENOMEM;
- }
- return 0;
-}
-
-static bool is_sst_dapm_widget(struct snd_soc_dapm_widget *w)
-{
- switch (w->id) {
- case snd_soc_dapm_pga:
- case snd_soc_dapm_aif_in:
- case snd_soc_dapm_aif_out:
- case snd_soc_dapm_input:
- case snd_soc_dapm_output:
- case snd_soc_dapm_mixer:
- return true;
- default:
- return false;
- }
-}
-
-/**
- * sst_send_pipe_gains - send gains for the front-end DAIs
- *
- * The gains in the pipes connected to the front-ends are muted/unmuted
- * automatically via the digital_mute() DAPM callback. This function sends the
- * gains for the front-end pipes.
- */
-int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute)
-{
- struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
- struct snd_soc_dapm_widget *w;
- struct snd_soc_dapm_path *p = NULL;
-
- dev_dbg(dai->dev, "enter, dai-name=%s dir=%d\n", dai->name, stream);
-
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- dev_dbg(dai->dev, "Stream name=%s\n",
- dai->playback_widget->name);
- w = dai->playback_widget;
- list_for_each_entry(p, &w->sinks, list_source) {
- if (p->connected && !p->connected(w, p->sink))
- continue;
-
- if (p->connect && p->sink->power &&
- is_sst_dapm_widget(p->sink)) {
- struct sst_ids *ids = p->sink->priv;
-
- dev_dbg(dai->dev, "send gains for widget=%s\n",
- p->sink->name);
- mutex_lock(&drv->lock);
- sst_set_pipe_gain(ids, drv, mute);
- mutex_unlock(&drv->lock);
- }
- }
- } else {
- dev_dbg(dai->dev, "Stream name=%s\n",
- dai->capture_widget->name);
- w = dai->capture_widget;
- list_for_each_entry(p, &w->sources, list_sink) {
- if (p->connected && !p->connected(w, p->sink))
- continue;
-
- if (p->connect && p->source->power &&
- is_sst_dapm_widget(p->source)) {
- struct sst_ids *ids = p->source->priv;
-
- dev_dbg(dai->dev, "send gain for widget=%s\n",
- p->source->name);
- mutex_lock(&drv->lock);
- sst_set_pipe_gain(ids, drv, mute);
- mutex_unlock(&drv->lock);
- }
- }
- }
- return 0;
-}
-
-/**
- * sst_fill_module_list - populate the list of modules/gains for a pipe
- *
- *
- * Fills the widget pointer in the kcontrol private data, and also fills the
- * kcontrol pointer in the widget private data.
- *
- * Widget pointer is used to send the algo/gain in the .put() handler if the
- * widget is powerd on.
- *
- * Kcontrol pointer is used to send the algo/gain in the widget power ON/OFF
- * event handler. Each widget (pipe) has multiple algos stored in the algo_list.
- */
-static int sst_fill_module_list(struct snd_kcontrol *kctl,
- struct snd_soc_dapm_widget *w, int type)
-{
- struct sst_module *module = NULL;
- struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
- struct sst_ids *ids = w->priv;
- int ret = 0;
-
- module = devm_kzalloc(c->dev, sizeof(*module), GFP_KERNEL);
- if (!module)
- return -ENOMEM;
-
- if (type == SST_MODULE_GAIN) {
- struct sst_gain_mixer_control *mc = (void *)kctl->private_value;
-
- mc->w = w;
- module->kctl = kctl;
- list_add_tail(&module->node, &ids->gain_list);
- } else if (type == SST_MODULE_ALGO) {
- struct sst_algo_control *bc = (void *)kctl->private_value;
-
- bc->w = w;
- module->kctl = kctl;
- list_add_tail(&module->node, &ids->algo_list);
- } else {
- dev_err(c->dev, "invoked for unknown type %d module %s",
- type, kctl->id.name);
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-/**
- * sst_fill_widget_module_info - fill list of gains/algos for the pipe
- * @widget: pipe modelled as a DAPM widget
- *
- * Fill the list of gains/algos for the widget by looking at all the card
- * controls and comparing the name of the widget with the first part of control
- * name. First part of control name contains the pipe name (widget name).
- */
-static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w,
- struct snd_soc_platform *platform)
-{
- struct snd_kcontrol *kctl;
- int index, ret = 0;
- struct snd_card *card = platform->component.card->snd_card;
- char *idx;
-
- down_read(&card->controls_rwsem);
-
- list_for_each_entry(kctl, &card->controls, list) {
- idx = strstr(kctl->id.name, " ");
- if (idx == NULL)
- continue;
- index = strlen(kctl->id.name) - strlen(idx);
-
- if (strstr(kctl->id.name, "Volume") &&
- !strncmp(kctl->id.name, w->name, index))
- ret = sst_fill_module_list(kctl, w, SST_MODULE_GAIN);
-
- else if (strstr(kctl->id.name, "params") &&
- !strncmp(kctl->id.name, w->name, index))
- ret = sst_fill_module_list(kctl, w, SST_MODULE_ALGO);
-
- else if (strstr(kctl->id.name, "Switch") &&
- !strncmp(kctl->id.name, w->name, index) &&
- strstr(kctl->id.name, "Gain")) {
- struct sst_gain_mixer_control *mc =
- (void *)kctl->private_value;
-
- mc->w = w;
-
- } else if (strstr(kctl->id.name, "interleaver") &&
- !strncmp(kctl->id.name, w->name, index)) {
- struct sst_enum *e = (void *)kctl->private_value;
-
- e->w = w;
-
- } else if (strstr(kctl->id.name, "deinterleaver") &&
- !strncmp(kctl->id.name, w->name, index)) {
-
- struct sst_enum *e = (void *)kctl->private_value;
-
- e->w = w;
- }
-
- if (ret < 0) {
- up_read(&card->controls_rwsem);
- return ret;
- }
- }
-
- up_read(&card->controls_rwsem);
- return 0;
-}
-
-/**
- * sst_fill_linked_widgets - fill the parent pointer for the linked widget
- */
-static void sst_fill_linked_widgets(struct snd_soc_platform *platform,
- struct sst_ids *ids)
-{
- struct snd_soc_dapm_widget *w;
- unsigned int len = strlen(ids->parent_wname);
-
- list_for_each_entry(w, &platform->component.card->widgets, list) {
- if (!strncmp(ids->parent_wname, w->name, len)) {
- ids->parent_w = w;
- break;
- }
- }
-}
-
-/**
- * sst_map_modules_to_pipe - fill algo/gains list for all pipes
- */
-static int sst_map_modules_to_pipe(struct snd_soc_platform *platform)
-{
- struct snd_soc_dapm_widget *w;
- int ret = 0;
-
- list_for_each_entry(w, &platform->component.card->widgets, list) {
- if (is_sst_dapm_widget(w) && (w->priv)) {
- struct sst_ids *ids = w->priv;
-
- dev_dbg(platform->dev, "widget type=%d name=%s\n",
- w->id, w->name);
- INIT_LIST_HEAD(&ids->algo_list);
- INIT_LIST_HEAD(&ids->gain_list);
- ret = sst_fill_widget_module_info(w, platform);
-
- if (ret < 0)
- return ret;
-
- /* fill linked widgets */
- if (ids->parent_wname != NULL)
- sst_fill_linked_widgets(platform, ids);
- }
- }
- return 0;
-}
-
-int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform)
-{
- int i, ret = 0;
- struct snd_soc_dapm_context *dapm =
- snd_soc_component_get_dapm(&platform->component);
- struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
- unsigned int gains = ARRAY_SIZE(sst_gain_controls)/3;
-
- drv->byte_stream = devm_kzalloc(platform->dev,
- SST_MAX_BIN_BYTES, GFP_KERNEL);
- if (!drv->byte_stream)
- return -ENOMEM;
-
- snd_soc_dapm_new_controls(dapm, sst_dapm_widgets,
- ARRAY_SIZE(sst_dapm_widgets));
- snd_soc_dapm_add_routes(dapm, intercon,
- ARRAY_SIZE(intercon));
- snd_soc_dapm_new_widgets(dapm->card);
-
- for (i = 0; i < gains; i++) {
- sst_gains[i].mute = SST_GAIN_MUTE_DEFAULT;
- sst_gains[i].l_gain = SST_GAIN_VOLUME_DEFAULT;
- sst_gains[i].r_gain = SST_GAIN_VOLUME_DEFAULT;
- sst_gains[i].ramp_duration = SST_GAIN_RAMP_DURATION_DEFAULT;
- }
-
- ret = snd_soc_add_platform_controls(platform, sst_gain_controls,
- ARRAY_SIZE(sst_gain_controls));
- if (ret)
- return ret;
-
- /* Initialize algo control params */
- ret = sst_algo_control_init(platform->dev);
- if (ret)
- return ret;
- ret = snd_soc_add_platform_controls(platform, sst_algo_controls,
- ARRAY_SIZE(sst_algo_controls));
- if (ret)
- return ret;
-
- ret = snd_soc_add_platform_controls(platform, sst_slot_controls,
- ARRAY_SIZE(sst_slot_controls));
- if (ret)
- return ret;
-
- ret = sst_map_modules_to_pipe(platform);
-
- return ret;
-}
+++ /dev/null
-/*
- * sst-atom-controls.h - Intel MID Platform driver header file
- *
- * Copyright (C) 2013-14 Intel Corp
- * Author: Ramesh Babu <ramesh.babu.koul@intel.com>
- * Omair M Abdullah <omair.m.abdullah@intel.com>
- * Samreen Nilofer <samreen.nilofer@intel.com>
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- */
-
-#ifndef __SST_ATOM_CONTROLS_H__
-#define __SST_ATOM_CONTROLS_H__
-
-#include <sound/soc.h>
-#include <sound/tlv.h>
-
-enum {
- MERR_DPCM_AUDIO = 0,
- MERR_DPCM_COMPR,
-};
-
-/* define a bit for each mixer input */
-#define SST_MIX_IP(x) (x)
-
-#define SST_IP_CODEC0 SST_MIX_IP(2)
-#define SST_IP_CODEC1 SST_MIX_IP(3)
-#define SST_IP_LOOP0 SST_MIX_IP(4)
-#define SST_IP_LOOP1 SST_MIX_IP(5)
-#define SST_IP_LOOP2 SST_MIX_IP(6)
-#define SST_IP_PROBE SST_MIX_IP(7)
-#define SST_IP_VOIP SST_MIX_IP(12)
-#define SST_IP_PCM0 SST_MIX_IP(13)
-#define SST_IP_PCM1 SST_MIX_IP(14)
-#define SST_IP_MEDIA0 SST_MIX_IP(17)
-#define SST_IP_MEDIA1 SST_MIX_IP(18)
-#define SST_IP_MEDIA2 SST_MIX_IP(19)
-#define SST_IP_MEDIA3 SST_MIX_IP(20)
-
-#define SST_IP_LAST SST_IP_MEDIA3
-
-#define SST_SWM_INPUT_COUNT (SST_IP_LAST + 1)
-#define SST_CMD_SWM_MAX_INPUTS 6
-
-#define SST_PATH_ID_SHIFT 8
-#define SST_DEFAULT_LOCATION_ID 0xFFFF
-#define SST_DEFAULT_CELL_NBR 0xFF
-#define SST_DEFAULT_MODULE_ID 0xFFFF
-
-/*
- * Audio DSP Path Ids. Specified by the audio DSP FW
- */
-enum sst_path_index {
- SST_PATH_INDEX_CODEC_OUT0 = (0x02 << SST_PATH_ID_SHIFT),
- SST_PATH_INDEX_CODEC_OUT1 = (0x03 << SST_PATH_ID_SHIFT),
-
- SST_PATH_INDEX_SPROT_LOOP_OUT = (0x04 << SST_PATH_ID_SHIFT),
- SST_PATH_INDEX_MEDIA_LOOP1_OUT = (0x05 << SST_PATH_ID_SHIFT),
- SST_PATH_INDEX_MEDIA_LOOP2_OUT = (0x06 << SST_PATH_ID_SHIFT),
-
- SST_PATH_INDEX_VOIP_OUT = (0x0C << SST_PATH_ID_SHIFT),
- SST_PATH_INDEX_PCM0_OUT = (0x0D << SST_PATH_ID_SHIFT),
- SST_PATH_INDEX_PCM1_OUT = (0x0E << SST_PATH_ID_SHIFT),
- SST_PATH_INDEX_PCM2_OUT = (0x0F << SST_PATH_ID_SHIFT),
-
- SST_PATH_INDEX_MEDIA0_OUT = (0x12 << SST_PATH_ID_SHIFT),
- SST_PATH_INDEX_MEDIA1_OUT = (0x13 << SST_PATH_ID_SHIFT),
-
-
- /* Start of input paths */
- SST_PATH_INDEX_CODEC_IN0 = (0x82 << SST_PATH_ID_SHIFT),
- SST_PATH_INDEX_CODEC_IN1 = (0x83 << SST_PATH_ID_SHIFT),
-
- SST_PATH_INDEX_SPROT_LOOP_IN = (0x84 << SST_PATH_ID_SHIFT),
- SST_PATH_INDEX_MEDIA_LOOP1_IN = (0x85 << SST_PATH_ID_SHIFT),
- SST_PATH_INDEX_MEDIA_LOOP2_IN = (0x86 << SST_PATH_ID_SHIFT),
-
- SST_PATH_INDEX_VOIP_IN = (0x8C << SST_PATH_ID_SHIFT),
-
- SST_PATH_INDEX_PCM0_IN = (0x8D << SST_PATH_ID_SHIFT),
- SST_PATH_INDEX_PCM1_IN = (0x8E << SST_PATH_ID_SHIFT),
-
- SST_PATH_INDEX_MEDIA0_IN = (0x8F << SST_PATH_ID_SHIFT),
- SST_PATH_INDEX_MEDIA1_IN = (0x90 << SST_PATH_ID_SHIFT),
- SST_PATH_INDEX_MEDIA2_IN = (0x91 << SST_PATH_ID_SHIFT),
-
- SST_PATH_INDEX_MEDIA3_IN = (0x9C << SST_PATH_ID_SHIFT),
-
- SST_PATH_INDEX_RESERVED = (0xFF << SST_PATH_ID_SHIFT),
-};
-
-/*
- * path IDs
- */
-enum sst_swm_inputs {
- SST_SWM_IN_CODEC0 = (SST_PATH_INDEX_CODEC_IN0 | SST_DEFAULT_CELL_NBR),
- SST_SWM_IN_CODEC1 = (SST_PATH_INDEX_CODEC_IN1 | SST_DEFAULT_CELL_NBR),
- SST_SWM_IN_SPROT_LOOP = (SST_PATH_INDEX_SPROT_LOOP_IN | SST_DEFAULT_CELL_NBR),
- SST_SWM_IN_MEDIA_LOOP1 = (SST_PATH_INDEX_MEDIA_LOOP1_IN | SST_DEFAULT_CELL_NBR),
- SST_SWM_IN_MEDIA_LOOP2 = (SST_PATH_INDEX_MEDIA_LOOP2_IN | SST_DEFAULT_CELL_NBR),
- SST_SWM_IN_VOIP = (SST_PATH_INDEX_VOIP_IN | SST_DEFAULT_CELL_NBR),
- SST_SWM_IN_PCM0 = (SST_PATH_INDEX_PCM0_IN | SST_DEFAULT_CELL_NBR),
- SST_SWM_IN_PCM1 = (SST_PATH_INDEX_PCM1_IN | SST_DEFAULT_CELL_NBR),
- SST_SWM_IN_MEDIA0 = (SST_PATH_INDEX_MEDIA0_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */
- SST_SWM_IN_MEDIA1 = (SST_PATH_INDEX_MEDIA1_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */
- SST_SWM_IN_MEDIA2 = (SST_PATH_INDEX_MEDIA2_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */
- SST_SWM_IN_MEDIA3 = (SST_PATH_INDEX_MEDIA3_IN | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */
- SST_SWM_IN_END = (SST_PATH_INDEX_RESERVED | SST_DEFAULT_CELL_NBR)
-};
-
-/*
- * path IDs
- */
-enum sst_swm_outputs {
- SST_SWM_OUT_CODEC0 = (SST_PATH_INDEX_CODEC_OUT0 | SST_DEFAULT_CELL_NBR),
- SST_SWM_OUT_CODEC1 = (SST_PATH_INDEX_CODEC_OUT1 | SST_DEFAULT_CELL_NBR),
- SST_SWM_OUT_SPROT_LOOP = (SST_PATH_INDEX_SPROT_LOOP_OUT | SST_DEFAULT_CELL_NBR),
- SST_SWM_OUT_MEDIA_LOOP1 = (SST_PATH_INDEX_MEDIA_LOOP1_OUT | SST_DEFAULT_CELL_NBR),
- SST_SWM_OUT_MEDIA_LOOP2 = (SST_PATH_INDEX_MEDIA_LOOP2_OUT | SST_DEFAULT_CELL_NBR),
- SST_SWM_OUT_VOIP = (SST_PATH_INDEX_VOIP_OUT | SST_DEFAULT_CELL_NBR),
- SST_SWM_OUT_PCM0 = (SST_PATH_INDEX_PCM0_OUT | SST_DEFAULT_CELL_NBR),
- SST_SWM_OUT_PCM1 = (SST_PATH_INDEX_PCM1_OUT | SST_DEFAULT_CELL_NBR),
- SST_SWM_OUT_PCM2 = (SST_PATH_INDEX_PCM2_OUT | SST_DEFAULT_CELL_NBR),
- SST_SWM_OUT_MEDIA0 = (SST_PATH_INDEX_MEDIA0_OUT | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */
- SST_SWM_OUT_MEDIA1 = (SST_PATH_INDEX_MEDIA1_OUT | SST_DEFAULT_CELL_NBR), /* Part of Media Mixer */
- SST_SWM_OUT_END = (SST_PATH_INDEX_RESERVED | SST_DEFAULT_CELL_NBR),
-};
-
-enum sst_ipc_msg {
- SST_IPC_IA_CMD = 1,
- SST_IPC_IA_SET_PARAMS,
- SST_IPC_IA_GET_PARAMS,
-};
-
-enum sst_cmd_type {
- SST_CMD_BYTES_SET = 1,
- SST_CMD_BYTES_GET = 2,
-};
-
-enum sst_task {
- SST_TASK_SBA = 1,
- SST_TASK_MMX,
-};
-
-enum sst_type {
- SST_TYPE_CMD = 1,
- SST_TYPE_PARAMS,
-};
-
-enum sst_flag {
- SST_FLAG_BLOCKED = 1,
- SST_FLAG_NONBLOCK,
-};
-
-/*
- * Enumeration for indexing the gain cells in VB_SET_GAIN DSP command
- */
-enum sst_gain_index {
- /* GAIN IDs for SB task start here */
- SST_GAIN_INDEX_CODEC_OUT0,
- SST_GAIN_INDEX_CODEC_OUT1,
- SST_GAIN_INDEX_CODEC_IN0,
- SST_GAIN_INDEX_CODEC_IN1,
-
- SST_GAIN_INDEX_SPROT_LOOP_OUT,
- SST_GAIN_INDEX_MEDIA_LOOP1_OUT,
- SST_GAIN_INDEX_MEDIA_LOOP2_OUT,
-
- SST_GAIN_INDEX_PCM0_IN_LEFT,
- SST_GAIN_INDEX_PCM0_IN_RIGHT,
-
- SST_GAIN_INDEX_PCM1_OUT_LEFT,
- SST_GAIN_INDEX_PCM1_OUT_RIGHT,
- SST_GAIN_INDEX_PCM1_IN_LEFT,
- SST_GAIN_INDEX_PCM1_IN_RIGHT,
- SST_GAIN_INDEX_PCM2_OUT_LEFT,
-
- SST_GAIN_INDEX_PCM2_OUT_RIGHT,
- SST_GAIN_INDEX_VOIP_OUT,
- SST_GAIN_INDEX_VOIP_IN,
-
- /* Gain IDs for MMX task start here */
- SST_GAIN_INDEX_MEDIA0_IN_LEFT,
- SST_GAIN_INDEX_MEDIA0_IN_RIGHT,
- SST_GAIN_INDEX_MEDIA1_IN_LEFT,
- SST_GAIN_INDEX_MEDIA1_IN_RIGHT,
-
- SST_GAIN_INDEX_MEDIA2_IN_LEFT,
- SST_GAIN_INDEX_MEDIA2_IN_RIGHT,
-
- SST_GAIN_INDEX_GAIN_END
-};
-
-/*
- * Audio DSP module IDs specified by FW spec
- * TODO: Update with all modules
- */
-enum sst_module_id {
- SST_MODULE_ID_PCM = 0x0001,
- SST_MODULE_ID_MP3 = 0x0002,
- SST_MODULE_ID_MP24 = 0x0003,
- SST_MODULE_ID_AAC = 0x0004,
- SST_MODULE_ID_AACP = 0x0005,
- SST_MODULE_ID_EAACP = 0x0006,
- SST_MODULE_ID_WMA9 = 0x0007,
- SST_MODULE_ID_WMA10 = 0x0008,
- SST_MODULE_ID_WMA10P = 0x0009,
- SST_MODULE_ID_RA = 0x000A,
- SST_MODULE_ID_DDAC3 = 0x000B,
- SST_MODULE_ID_TRUE_HD = 0x000C,
- SST_MODULE_ID_HD_PLUS = 0x000D,
-
- SST_MODULE_ID_SRC = 0x0064,
- SST_MODULE_ID_DOWNMIX = 0x0066,
- SST_MODULE_ID_GAIN_CELL = 0x0067,
- SST_MODULE_ID_SPROT = 0x006D,
- SST_MODULE_ID_BASS_BOOST = 0x006E,
- SST_MODULE_ID_STEREO_WDNG = 0x006F,
- SST_MODULE_ID_AV_REMOVAL = 0x0070,
- SST_MODULE_ID_MIC_EQ = 0x0071,
- SST_MODULE_ID_SPL = 0x0072,
- SST_MODULE_ID_ALGO_VTSV = 0x0073,
- SST_MODULE_ID_NR = 0x0076,
- SST_MODULE_ID_BWX = 0x0077,
- SST_MODULE_ID_DRP = 0x0078,
- SST_MODULE_ID_MDRP = 0x0079,
-
- SST_MODULE_ID_ANA = 0x007A,
- SST_MODULE_ID_AEC = 0x007B,
- SST_MODULE_ID_NR_SNS = 0x007C,
- SST_MODULE_ID_SER = 0x007D,
- SST_MODULE_ID_AGC = 0x007E,
-
- SST_MODULE_ID_CNI = 0x007F,
- SST_MODULE_ID_CONTEXT_ALGO_AWARE = 0x0080,
- SST_MODULE_ID_FIR_24 = 0x0081,
- SST_MODULE_ID_IIR_24 = 0x0082,
-
- SST_MODULE_ID_ASRC = 0x0083,
- SST_MODULE_ID_TONE_GEN = 0x0084,
- SST_MODULE_ID_BMF = 0x0086,
- SST_MODULE_ID_EDL = 0x0087,
- SST_MODULE_ID_GLC = 0x0088,
-
- SST_MODULE_ID_FIR_16 = 0x0089,
- SST_MODULE_ID_IIR_16 = 0x008A,
- SST_MODULE_ID_DNR = 0x008B,
-
- SST_MODULE_ID_VIRTUALIZER = 0x008C,
- SST_MODULE_ID_VISUALIZATION = 0x008D,
- SST_MODULE_ID_LOUDNESS_OPTIMIZER = 0x008E,
- SST_MODULE_ID_REVERBERATION = 0x008F,
-
- SST_MODULE_ID_CNI_TX = 0x0090,
- SST_MODULE_ID_REF_LINE = 0x0091,
- SST_MODULE_ID_VOLUME = 0x0092,
- SST_MODULE_ID_FILT_DCR = 0x0094,
- SST_MODULE_ID_SLV = 0x009A,
- SST_MODULE_ID_NLF = 0x009B,
- SST_MODULE_ID_TNR = 0x009C,
- SST_MODULE_ID_WNR = 0x009D,
-
- SST_MODULE_ID_LOG = 0xFF00,
-
- SST_MODULE_ID_TASK = 0xFFFF,
-};
-
-enum sst_cmd {
- SBA_IDLE = 14,
- SBA_VB_SET_SPEECH_PATH = 26,
- MMX_SET_GAIN = 33,
- SBA_VB_SET_GAIN = 33,
- FBA_VB_RX_CNI = 35,
- MMX_SET_GAIN_TIMECONST = 36,
- SBA_VB_SET_TIMECONST = 36,
- SBA_VB_START = 85,
- SBA_SET_SWM = 114,
- SBA_SET_MDRP = 116,
- SBA_HW_SET_SSP = 117,
- SBA_SET_MEDIA_LOOP_MAP = 118,
- SBA_SET_MEDIA_PATH = 119,
- MMX_SET_MEDIA_PATH = 119,
- SBA_VB_LPRO = 126,
- SBA_VB_SET_FIR = 128,
- SBA_VB_SET_IIR = 129,
- SBA_SET_SSP_SLOT_MAP = 130,
-};
-
-enum sst_dsp_switch {
- SST_SWITCH_OFF = 0,
- SST_SWITCH_ON = 3,
-};
-
-enum sst_path_switch {
- SST_PATH_OFF = 0,
- SST_PATH_ON = 1,
-};
-
-enum sst_swm_state {
- SST_SWM_OFF = 0,
- SST_SWM_ON = 3,
-};
-
-#define SST_FILL_LOCATION_IDS(dst, cell_idx, pipe_id) do { \
- dst.location_id.p.cell_nbr_idx = (cell_idx); \
- dst.location_id.p.path_id = (pipe_id); \
- } while (0)
-#define SST_FILL_LOCATION_ID(dst, loc_id) (\
- dst.location_id.f = (loc_id))
-#define SST_FILL_MODULE_ID(dst, mod_id) (\
- dst.module_id = (mod_id))
-
-#define SST_FILL_DESTINATION1(dst, id) do { \
- SST_FILL_LOCATION_ID(dst, (id) & 0xFFFF); \
- SST_FILL_MODULE_ID(dst, ((id) & 0xFFFF0000) >> 16); \
- } while (0)
-#define SST_FILL_DESTINATION2(dst, loc_id, mod_id) do { \
- SST_FILL_LOCATION_ID(dst, loc_id); \
- SST_FILL_MODULE_ID(dst, mod_id); \
- } while (0)
-#define SST_FILL_DESTINATION3(dst, cell_idx, path_id, mod_id) do { \
- SST_FILL_LOCATION_IDS(dst, cell_idx, path_id); \
- SST_FILL_MODULE_ID(dst, mod_id); \
- } while (0)
-
-#define SST_FILL_DESTINATION(level, dst, ...) \
- SST_FILL_DESTINATION##level(dst, __VA_ARGS__)
-#define SST_FILL_DEFAULT_DESTINATION(dst) \
- SST_FILL_DESTINATION(2, dst, SST_DEFAULT_LOCATION_ID, SST_DEFAULT_MODULE_ID)
-
-struct sst_destination_id {
- union sst_location_id {
- struct {
- u8 cell_nbr_idx; /* module index */
- u8 path_id; /* pipe_id */
- } __packed p; /* part */
- u16 f; /* full */
- } __packed location_id;
- u16 module_id;
-} __packed;
-struct sst_dsp_header {
- struct sst_destination_id dst;
- u16 command_id;
- u16 length;
-} __packed;
-
-/*
- *
- * Common Commands
- *
- */
-struct sst_cmd_generic {
- struct sst_dsp_header header;
-} __packed;
-
-struct swm_input_ids {
- struct sst_destination_id input_id;
-} __packed;
-
-struct sst_cmd_set_swm {
- struct sst_dsp_header header;
- struct sst_destination_id output_id;
- u16 switch_state;
- u16 nb_inputs;
- struct swm_input_ids input[SST_CMD_SWM_MAX_INPUTS];
-} __packed;
-
-struct sst_cmd_set_media_path {
- struct sst_dsp_header header;
- u16 switch_state;
-} __packed;
-
-struct pcm_cfg {
- u8 s_length:2;
- u8 rate:3;
- u8 format:3;
-} __packed;
-
-struct sst_cmd_set_speech_path {
- struct sst_dsp_header header;
- u16 switch_state;
- struct {
- u16 rsvd:8;
- struct pcm_cfg cfg;
- } config;
-} __packed;
-
-struct gain_cell {
- struct sst_destination_id dest;
- s16 cell_gain_left;
- s16 cell_gain_right;
- u16 gain_time_constant;
-} __packed;
-
-#define NUM_GAIN_CELLS 1
-struct sst_cmd_set_gain_dual {
- struct sst_dsp_header header;
- u16 gain_cell_num;
- struct gain_cell cell_gains[NUM_GAIN_CELLS];
-} __packed;
-struct sst_cmd_set_params {
- struct sst_destination_id dst;
- u16 command_id;
- char params[0];
-} __packed;
-
-
-struct sst_cmd_sba_vb_start {
- struct sst_dsp_header header;
-} __packed;
-
-union sba_media_loop_params {
- struct {
- u16 rsvd:8;
- struct pcm_cfg cfg;
- } part;
- u16 full;
-} __packed;
-
-struct sst_cmd_sba_set_media_loop_map {
- struct sst_dsp_header header;
- u16 switch_state;
- union sba_media_loop_params param;
- u16 map;
-} __packed;
-
-struct sst_cmd_tone_stop {
- struct sst_dsp_header header;
- u16 switch_state;
-} __packed;
-
-enum sst_ssp_mode {
- SSP_MODE_MASTER = 0,
- SSP_MODE_SLAVE = 1,
-};
-
-enum sst_ssp_pcm_mode {
- SSP_PCM_MODE_NORMAL = 0,
- SSP_PCM_MODE_NETWORK = 1,
-};
-
-enum sst_ssp_duplex {
- SSP_DUPLEX = 0,
- SSP_RX = 1,
- SSP_TX = 2,
-};
-
-enum sst_ssp_fs_frequency {
- SSP_FS_8_KHZ = 0,
- SSP_FS_16_KHZ = 1,
- SSP_FS_44_1_KHZ = 2,
- SSP_FS_48_KHZ = 3,
-};
-
-enum sst_ssp_fs_polarity {
- SSP_FS_ACTIVE_LOW = 0,
- SSP_FS_ACTIVE_HIGH = 1,
-};
-
-enum sst_ssp_protocol {
- SSP_MODE_PCM = 0,
- SSP_MODE_I2S = 1,
-};
-
-enum sst_ssp_port_id {
- SSP_MODEM = 0,
- SSP_BT = 1,
- SSP_FM = 2,
- SSP_CODEC = 3,
-};
-
-struct sst_cmd_sba_hw_set_ssp {
- struct sst_dsp_header header;
- u16 selection; /* 0:SSP0(def), 1:SSP1, 2:SSP2 */
-
- u16 switch_state;
-
- u16 nb_bits_per_slots:6; /* 0-32 bits, 24 (def) */
- u16 nb_slots:4; /* 0-8: slots per frame */
- u16 mode:3; /* 0:Master, 1: Slave */
- u16 duplex:3;
-
- u16 active_tx_slot_map:8; /* Bit map, 0:off, 1:on */
- u16 reserved1:8;
-
- u16 active_rx_slot_map:8; /* Bit map 0: Off, 1:On */
- u16 reserved2:8;
-
- u16 frame_sync_frequency;
-
- u16 frame_sync_polarity:8;
- u16 data_polarity:8;
-
- u16 frame_sync_width; /* 1 to N clocks */
- u16 ssp_protocol:8;
- u16 start_delay:8; /* Start delay in terms of clock ticks */
-} __packed;
-
-#define SST_MAX_TDM_SLOTS 8
-
-struct sst_param_sba_ssp_slot_map {
- struct sst_dsp_header header;
-
- u16 param_id;
- u16 param_len;
- u16 ssp_index;
-
- u8 rx_slot_map[SST_MAX_TDM_SLOTS];
- u8 tx_slot_map[SST_MAX_TDM_SLOTS];
-} __packed;
-
-enum {
- SST_PROBE_EXTRACTOR = 0,
- SST_PROBE_INJECTOR = 1,
-};
-
-/**** widget defines *****/
-
-#define SST_MODULE_GAIN 1
-#define SST_MODULE_ALGO 2
-
-#define SST_FMT_MONO 0
-#define SST_FMT_STEREO 3
-
-/* physical SSP numbers */
-enum {
- SST_SSP0 = 0,
- SST_SSP1,
- SST_SSP2,
- SST_SSP_LAST = SST_SSP2,
-};
-
-#define SST_NUM_SSPS (SST_SSP_LAST + 1) /* physical SSPs */
-#define SST_MAX_SSP_MUX 2 /* single SSP muxed between pipes */
-#define SST_MAX_SSP_DOMAINS 2 /* domains present in each pipe */
-
-struct sst_module {
- struct snd_kcontrol *kctl;
- struct list_head node;
-};
-
-struct sst_ssp_config {
- u8 ssp_id;
- u8 bits_per_slot;
- u8 slots;
- u8 ssp_mode;
- u8 pcm_mode;
- u8 duplex;
- u8 ssp_protocol;
- u8 fs_frequency;
- u8 active_slot_map;
- u8 start_delay;
- u16 fs_width;
-};
-
-struct sst_ssp_cfg {
- const u8 ssp_number;
- const int *mux_shift;
- const int (*domain_shift)[SST_MAX_SSP_MUX];
- const struct sst_ssp_config (*ssp_config)[SST_MAX_SSP_MUX][SST_MAX_SSP_DOMAINS];
-};
-
-struct sst_ids {
- u16 location_id;
- u16 module_id;
- u8 task_id;
- u8 format;
- u8 reg;
- const char *parent_wname;
- struct snd_soc_dapm_widget *parent_w;
- struct list_head algo_list;
- struct list_head gain_list;
- const struct sst_pcm_format *pcm_fmt;
-};
-
-
-#define SST_AIF_IN(wname, wevent) \
-{ .id = snd_soc_dapm_aif_in, .name = wname, .sname = NULL, \
- .reg = SND_SOC_NOPM, .shift = 0, \
- .on_val = 1, .off_val = 0, \
- .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
- .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \
-}
-
-#define SST_AIF_OUT(wname, wevent) \
-{ .id = snd_soc_dapm_aif_out, .name = wname, .sname = NULL, \
- .reg = SND_SOC_NOPM, .shift = 0, \
- .on_val = 1, .off_val = 0, \
- .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
- .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \
-}
-
-#define SST_INPUT(wname, wevent) \
-{ .id = snd_soc_dapm_input, .name = wname, .sname = NULL, \
- .reg = SND_SOC_NOPM, .shift = 0, \
- .on_val = 1, .off_val = 0, \
- .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
- .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \
-}
-
-#define SST_OUTPUT(wname, wevent) \
-{ .id = snd_soc_dapm_output, .name = wname, .sname = NULL, \
- .reg = SND_SOC_NOPM, .shift = 0, \
- .on_val = 1, .off_val = 0, \
- .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
- .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \
-}
-
-#define SST_DAPM_OUTPUT(wname, wloc_id, wtask_id, wformat, wevent) \
-{ .id = snd_soc_dapm_output, .name = wname, .sname = NULL, \
- .reg = SND_SOC_NOPM, .shift = 0, \
- .on_val = 1, .off_val = 0, \
- .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
- .priv = (void *)&(struct sst_ids) { .location_id = wloc_id, .task_id = wtask_id,\
- .pcm_fmt = wformat, } \
-}
-
-#define SST_PATH(wname, wtask, wloc_id, wevent, wflags) \
-{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \
- .kcontrol_news = NULL, .num_kcontrols = 0, \
- .on_val = 1, .off_val = 0, \
- .event = wevent, .event_flags = wflags, \
- .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, } \
-}
-
-#define SST_LINKED_PATH(wname, wtask, wloc_id, linked_wname, wevent, wflags) \
-{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \
- .kcontrol_news = NULL, .num_kcontrols = 0, \
- .on_val = 1, .off_val = 0, \
- .event = wevent, .event_flags = wflags, \
- .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \
- .parent_wname = linked_wname} \
-}
-
-#define SST_PATH_MEDIA_LOOP(wname, wtask, wloc_id, wformat, wevent, wflags) \
-{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \
- .kcontrol_news = NULL, .num_kcontrols = 0, \
- .event = wevent, .event_flags = wflags, \
- .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \
- .format = wformat,} \
-}
-
-/* output is triggered before input */
-#define SST_PATH_INPUT(name, task_id, loc_id, event) \
- SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
-
-#define SST_PATH_LINKED_INPUT(name, task_id, loc_id, linked_wname, event) \
- SST_LINKED_PATH(name, task_id, loc_id, linked_wname, event, \
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
-
-#define SST_PATH_OUTPUT(name, task_id, loc_id, event) \
- SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
-
-#define SST_PATH_LINKED_OUTPUT(name, task_id, loc_id, linked_wname, event) \
- SST_LINKED_PATH(name, task_id, loc_id, linked_wname, event, \
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
-
-#define SST_PATH_MEDIA_LOOP_OUTPUT(name, task_id, loc_id, format, event) \
- SST_PATH_MEDIA_LOOP(name, task_id, loc_id, format, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
-
-
-#define SST_SWM_MIXER(wname, wreg, wtask, wloc_id, wcontrols, wevent) \
-{ .id = snd_soc_dapm_mixer, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \
- .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols),\
- .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD | \
- SND_SOC_DAPM_POST_REG, \
- .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \
- .reg = wreg } \
-}
-
-enum sst_gain_kcontrol_type {
- SST_GAIN_TLV,
- SST_GAIN_MUTE,
- SST_GAIN_RAMP_DURATION,
-};
-
-struct sst_gain_mixer_control {
- bool stereo;
- enum sst_gain_kcontrol_type type;
- struct sst_gain_value *gain_val;
- int max;
- int min;
- u16 instance_id;
- u16 module_id;
- u16 pipe_id;
- u16 task_id;
- char pname[44];
- struct snd_soc_dapm_widget *w;
-};
-
-struct sst_gain_value {
- u16 ramp_duration;
- s16 l_gain;
- s16 r_gain;
- bool mute;
-};
-#define SST_GAIN_VOLUME_DEFAULT (-1440)
-#define SST_GAIN_RAMP_DURATION_DEFAULT 5 /* timeconstant */
-#define SST_GAIN_MUTE_DEFAULT true
-
-#define SST_GAIN_KCONTROL_TLV(xname, xhandler_get, xhandler_put, \
- xmod, xpipe, xinstance, xtask, tlv_array, xgain_val, \
- xmin, xmax, xpname) \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
- SNDRV_CTL_ELEM_ACCESS_READWRITE, \
- .tlv.p = (tlv_array), \
- .info = sst_gain_ctl_info,\
- .get = xhandler_get, .put = xhandler_put, \
- .private_value = (unsigned long)&(struct sst_gain_mixer_control) \
- { .stereo = true, .max = xmax, .min = xmin, .type = SST_GAIN_TLV, \
- .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\
- .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname}
-
-#define SST_GAIN_KCONTROL_INT(xname, xhandler_get, xhandler_put, \
- xmod, xpipe, xinstance, xtask, xtype, xgain_val, \
- xmin, xmax, xpname) \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .info = sst_gain_ctl_info, \
- .get = xhandler_get, .put = xhandler_put, \
- .private_value = (unsigned long)&(struct sst_gain_mixer_control) \
- { .stereo = false, .max = xmax, .min = xmin, .type = xtype, \
- .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\
- .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname}
-
-#define SST_GAIN_KCONTROL_BOOL(xname, xhandler_get, xhandler_put,\
- xmod, xpipe, xinstance, xtask, xgain_val, xpname) \
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .info = snd_soc_info_bool_ext, \
- .get = xhandler_get, .put = xhandler_put, \
- .private_value = (unsigned long)&(struct sst_gain_mixer_control) \
- { .stereo = false, .type = SST_GAIN_MUTE, \
- .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\
- .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname}
-#define SST_CONTROL_NAME(xpname, xmname, xinstance, xtype) \
- xpname " " xmname " " #xinstance " " xtype
-
-#define SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, xtype, xsubmodule) \
- xpname " " xmname " " #xinstance " " xtype " " xsubmodule
-
-/*
- * 3 Controls for each Gain module
- * e.g. - pcm0_in Gain 0 Volume
- * - pcm0_in Gain 0 Ramp Delay
- * - pcm0_in Gain 0 Switch
- */
-#define SST_GAIN_KCONTROLS(xpname, xmname, xmin_gain, xmax_gain, xmin_tc, xmax_tc, \
- xhandler_get, xhandler_put, \
- xmod, xpipe, xinstance, xtask, tlv_array, xgain_val) \
- { SST_GAIN_KCONTROL_INT(SST_CONTROL_NAME(xpname, xmname, xinstance, "Ramp Delay"), \
- xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, SST_GAIN_RAMP_DURATION, \
- xgain_val, xmin_tc, xmax_tc, xpname) }, \
- { SST_GAIN_KCONTROL_BOOL(SST_CONTROL_NAME(xpname, xmname, xinstance, "Switch"), \
- xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, \
- xgain_val, xpname) } ,\
- { SST_GAIN_KCONTROL_TLV(SST_CONTROL_NAME(xpname, xmname, xinstance, "Volume"), \
- xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, tlv_array, \
- xgain_val, xmin_gain, xmax_gain, xpname) }
-
-#define SST_GAIN_TC_MIN 5
-#define SST_GAIN_TC_MAX 5000
-#define SST_GAIN_MIN_VALUE -1440 /* in 0.1 DB units */
-#define SST_GAIN_MAX_VALUE 360
-
-enum sst_algo_kcontrol_type {
- SST_ALGO_PARAMS,
- SST_ALGO_BYPASS,
-};
-
-struct sst_algo_control {
- enum sst_algo_kcontrol_type type;
- int max;
- u16 module_id;
- u16 pipe_id;
- u16 task_id;
- u16 cmd_id;
- bool bypass;
- unsigned char *params;
- struct snd_soc_dapm_widget *w;
-};
-
-/* size of the control = size of params + size of length field */
-#define SST_ALGO_CTL_VALUE(xcount, xtype, xpipe, xmod, xtask, xcmd) \
- (struct sst_algo_control){ \
- .max = xcount + sizeof(u16), .type = xtype, .module_id = xmod, \
- .pipe_id = xpipe, .task_id = xtask, .cmd_id = xcmd, \
- }
-
-#define SST_ALGO_KCONTROL(xname, xcount, xmod, xpipe, \
- xtask, xcmd, xtype, xinfo, xget, xput) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = xname, \
- .info = xinfo, .get = xget, .put = xput, \
- .private_value = (unsigned long)& \
- SST_ALGO_CTL_VALUE(xcount, xtype, xpipe, \
- xmod, xtask, xcmd), \
-}
-
-#define SST_ALGO_KCONTROL_BYTES(xpname, xmname, xcount, xmod, \
- xpipe, xinstance, xtask, xcmd) \
- SST_ALGO_KCONTROL(SST_CONTROL_NAME(xpname, xmname, xinstance, "params"), \
- xcount, xmod, xpipe, xtask, xcmd, SST_ALGO_PARAMS, \
- sst_algo_bytes_ctl_info, \
- sst_algo_control_get, sst_algo_control_set)
-
-#define SST_ALGO_KCONTROL_BOOL(xpname, xmname, xmod, xpipe, xinstance, xtask) \
- SST_ALGO_KCONTROL(SST_CONTROL_NAME(xpname, xmname, xinstance, "bypass"), \
- 0, xmod, xpipe, xtask, 0, SST_ALGO_BYPASS, \
- snd_soc_info_bool_ext, \
- sst_algo_control_get, sst_algo_control_set)
-
-#define SST_ALGO_BYPASS_PARAMS(xpname, xmname, xcount, xmod, xpipe, \
- xinstance, xtask, xcmd) \
- SST_ALGO_KCONTROL_BOOL(xpname, xmname, xmod, xpipe, xinstance, xtask), \
- SST_ALGO_KCONTROL_BYTES(xpname, xmname, xcount, xmod, xpipe, xinstance, xtask, xcmd)
-
-#define SST_COMBO_ALGO_KCONTROL_BYTES(xpname, xmname, xsubmod, xcount, xmod, \
- xpipe, xinstance, xtask, xcmd) \
- SST_ALGO_KCONTROL(SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, "params", \
- xsubmod), \
- xcount, xmod, xpipe, xtask, xcmd, SST_ALGO_PARAMS, \
- sst_algo_bytes_ctl_info, \
- sst_algo_control_get, sst_algo_control_set)
-
-
-struct sst_enum {
- bool tx;
- unsigned short reg;
- unsigned int max;
- const char * const *texts;
- struct snd_soc_dapm_widget *w;
-};
-
-/* only 4 slots/channels supported atm */
-#define SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts) \
- (struct sst_enum){ .reg = s_ch_no, .tx = is_tx, .max = 4+1, .texts = xtexts, }
-
-#define SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name) \
- xpname " " xmname " " s_ch_name
-
-#define SST_SSP_SLOT_CTL(xpname, xmname, s_ch_name, s_ch_no, is_tx, xtexts, xget, xput) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name), \
- .info = sst_slot_enum_info, \
- .get = xget, .put = xput, \
- .private_value = (unsigned long)&SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts), \
-}
-
-#define SST_MUX_CTL_NAME(xpname, xinstance) \
- xpname " " #xinstance
-
-#define SST_SSP_MUX_ENUM(xreg, xshift, xtexts) \
- (struct soc_enum) SOC_ENUM_DOUBLE(xreg, xshift, xshift, ARRAY_SIZE(xtexts), xtexts)
-
-#define SST_SSP_MUX_CTL(xpname, xinstance, xreg, xshift, xtexts) \
- SOC_DAPM_ENUM(SST_MUX_CTL_NAME(xpname, xinstance), \
- SST_SSP_MUX_ENUM(xreg, xshift, xtexts))
-
-#endif
+++ /dev/null
-/*
- * Intel Baytrail SST DSP driver
- * Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- */
-
-#include <linux/delay.h>
-#include <linux/fs.h>
-#include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/dma-mapping.h>
-#include <linux/platform_device.h>
-#include <linux/firmware.h>
-
-#include "sst-dsp.h"
-#include "sst-dsp-priv.h"
-#include "sst-baytrail-ipc.h"
-
-#define SST_BYT_FW_SIGNATURE_SIZE 4
-#define SST_BYT_FW_SIGN "$SST"
-
-#define SST_BYT_IRAM_OFFSET 0xC0000
-#define SST_BYT_DRAM_OFFSET 0x100000
-#define SST_BYT_SHIM_OFFSET 0x140000
-
-enum sst_ram_type {
- SST_BYT_IRAM = 1,
- SST_BYT_DRAM = 2,
- SST_BYT_CACHE = 3,
-};
-
-struct dma_block_info {
- enum sst_ram_type type; /* IRAM/DRAM */
- u32 size; /* Bytes */
- u32 ram_offset; /* Offset in I/DRAM */
- u32 rsvd; /* Reserved field */
-};
-
-struct fw_header {
- unsigned char signature[SST_BYT_FW_SIGNATURE_SIZE];
- u32 file_size; /* size of fw minus this header */
- u32 modules; /* # of modules */
- u32 file_format; /* version of header format */
- u32 reserved[4];
-};
-
-struct sst_byt_fw_module_header {
- unsigned char signature[SST_BYT_FW_SIGNATURE_SIZE];
- u32 mod_size; /* size of module */
- u32 blocks; /* # of blocks */
- u32 type; /* codec type, pp lib */
- u32 entry_point;
-};
-
-static int sst_byt_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
- struct sst_byt_fw_module_header *module)
-{
- struct dma_block_info *block;
- struct sst_module *mod;
- struct sst_module_template template;
- int count;
-
- memset(&template, 0, sizeof(template));
- template.id = module->type;
- template.entry = module->entry_point;
-
- mod = sst_module_new(fw, &template, NULL);
- if (mod == NULL)
- return -ENOMEM;
-
- block = (void *)module + sizeof(*module);
-
- for (count = 0; count < module->blocks; count++) {
-
- if (block->size <= 0) {
- dev_err(dsp->dev, "block %d size invalid\n", count);
- return -EINVAL;
- }
-
- switch (block->type) {
- case SST_BYT_IRAM:
- mod->offset = block->ram_offset +
- dsp->addr.iram_offset;
- mod->type = SST_MEM_IRAM;
- break;
- case SST_BYT_DRAM:
- mod->offset = block->ram_offset +
- dsp->addr.dram_offset;
- mod->type = SST_MEM_DRAM;
- break;
- case SST_BYT_CACHE:
- mod->offset = block->ram_offset +
- (dsp->addr.fw_ext - dsp->addr.lpe);
- mod->type = SST_MEM_CACHE;
- break;
- default:
- dev_err(dsp->dev, "wrong ram type 0x%x in block0x%x\n",
- block->type, count);
- return -EINVAL;
- }
-
- mod->size = block->size;
- mod->data = (void *)block + sizeof(*block);
-
- sst_module_alloc_blocks(mod);
-
- block = (void *)block + sizeof(*block) + block->size;
- }
- return 0;
-}
-
-static int sst_byt_parse_fw_image(struct sst_fw *sst_fw)
-{
- struct fw_header *header;
- struct sst_byt_fw_module_header *module;
- struct sst_dsp *dsp = sst_fw->dsp;
- int ret, count;
-
- /* Read the header information from the data pointer */
- header = (struct fw_header *)sst_fw->dma_buf;
-
- /* verify FW */
- if ((strncmp(header->signature, SST_BYT_FW_SIGN, 4) != 0) ||
- (sst_fw->size != header->file_size + sizeof(*header))) {
- /* Invalid FW signature */
- dev_err(dsp->dev, "Invalid FW sign/filesize mismatch\n");
- return -EINVAL;
- }
-
- dev_dbg(dsp->dev,
- "header sign=%4s size=0x%x modules=0x%x fmt=0x%x size=%zu\n",
- header->signature, header->file_size, header->modules,
- header->file_format, sizeof(*header));
-
- module = (void *)sst_fw->dma_buf + sizeof(*header);
- for (count = 0; count < header->modules; count++) {
- /* module */
- ret = sst_byt_parse_module(dsp, sst_fw, module);
- if (ret < 0) {
- dev_err(dsp->dev, "invalid module %d\n", count);
- return ret;
- }
- module = (void *)module + sizeof(*module) + module->mod_size;
- }
-
- return 0;
-}
-
-static void sst_byt_dump_shim(struct sst_dsp *sst)
-{
- int i;
- u64 reg;
-
- for (i = 0; i <= 0xF0; i += 8) {
- reg = sst_dsp_shim_read64_unlocked(sst, i);
- if (reg)
- dev_dbg(sst->dev, "shim 0x%2.2x value 0x%16.16llx\n",
- i, reg);
- }
-
- for (i = 0x00; i <= 0xff; i += 4) {
- reg = readl(sst->addr.pci_cfg + i);
- if (reg)
- dev_dbg(sst->dev, "pci 0x%2.2x value 0x%8.8x\n",
- i, (u32)reg);
- }
-}
-
-static irqreturn_t sst_byt_irq(int irq, void *context)
-{
- struct sst_dsp *sst = (struct sst_dsp *) context;
- u64 isrx;
- irqreturn_t ret = IRQ_NONE;
-
- spin_lock(&sst->spinlock);
-
- isrx = sst_dsp_shim_read64_unlocked(sst, SST_ISRX);
- if (isrx & SST_ISRX_DONE) {
- /* ADSP has processed the message request from IA */
- sst_dsp_shim_update_bits64_unlocked(sst, SST_IPCX,
- SST_BYT_IPCX_DONE, 0);
- ret = IRQ_WAKE_THREAD;
- }
- if (isrx & SST_BYT_ISRX_REQUEST) {
- /* mask message request from ADSP and do processing later */
- sst_dsp_shim_update_bits64_unlocked(sst, SST_IMRX,
- SST_BYT_IMRX_REQUEST,
- SST_BYT_IMRX_REQUEST);
- ret = IRQ_WAKE_THREAD;
- }
-
- spin_unlock(&sst->spinlock);
-
- return ret;
-}
-
-static void sst_byt_boot(struct sst_dsp *sst)
-{
- int tries = 10;
-
- /*
- * save the physical address of extended firmware block in the first
- * 4 bytes of the mailbox
- */
- memcpy_toio(sst->addr.lpe + SST_BYT_MAILBOX_OFFSET,
- &sst->pdata->fw_base, sizeof(u32));
-
- /* release stall and wait to unstall */
- sst_dsp_shim_update_bits64(sst, SST_CSR, SST_BYT_CSR_STALL, 0x0);
- while (tries--) {
- if (!(sst_dsp_shim_read64(sst, SST_CSR) &
- SST_BYT_CSR_PWAITMODE))
- break;
- msleep(100);
- }
- if (tries < 0) {
- dev_err(sst->dev, "unable to start DSP\n");
- sst_byt_dump_shim(sst);
- }
-}
-
-static void sst_byt_reset(struct sst_dsp *sst)
-{
- /* put DSP into reset, set reset vector and stall */
- sst_dsp_shim_update_bits64(sst, SST_CSR,
- SST_BYT_CSR_RST | SST_BYT_CSR_VECTOR_SEL | SST_BYT_CSR_STALL,
- SST_BYT_CSR_RST | SST_BYT_CSR_VECTOR_SEL | SST_BYT_CSR_STALL);
-
- udelay(10);
-
- /* take DSP out of reset and keep stalled for FW loading */
- sst_dsp_shim_update_bits64(sst, SST_CSR, SST_BYT_CSR_RST, 0);
-}
-
-struct sst_adsp_memregion {
- u32 start;
- u32 end;
- int blocks;
- enum sst_mem_type type;
-};
-
-/* BYT test stuff */
-static const struct sst_adsp_memregion byt_region[] = {
- {0xC0000, 0x100000, 8, SST_MEM_IRAM}, /* I-SRAM - 8 * 32kB */
- {0x100000, 0x140000, 8, SST_MEM_DRAM}, /* D-SRAM0 - 8 * 32kB */
-};
-
-static int sst_byt_resource_map(struct sst_dsp *sst, struct sst_pdata *pdata)
-{
- sst->addr.lpe_base = pdata->lpe_base;
- sst->addr.lpe = ioremap(pdata->lpe_base, pdata->lpe_size);
- if (!sst->addr.lpe)
- return -ENODEV;
-
- /* ADSP PCI MMIO config space */
- sst->addr.pci_cfg = ioremap(pdata->pcicfg_base, pdata->pcicfg_size);
- if (!sst->addr.pci_cfg) {
- iounmap(sst->addr.lpe);
- return -ENODEV;
- }
-
- /* SST Extended FW allocation */
- sst->addr.fw_ext = ioremap(pdata->fw_base, pdata->fw_size);
- if (!sst->addr.fw_ext) {
- iounmap(sst->addr.pci_cfg);
- iounmap(sst->addr.lpe);
- return -ENODEV;
- }
-
- /* SST Shim */
- sst->addr.shim = sst->addr.lpe + sst->addr.shim_offset;
-
- sst_dsp_mailbox_init(sst, SST_BYT_MAILBOX_OFFSET + 0x204,
- SST_BYT_IPC_MAX_PAYLOAD_SIZE,
- SST_BYT_MAILBOX_OFFSET,
- SST_BYT_IPC_MAX_PAYLOAD_SIZE);
-
- sst->irq = pdata->irq;
-
- return 0;
-}
-
-static int sst_byt_init(struct sst_dsp *sst, struct sst_pdata *pdata)
-{
- const struct sst_adsp_memregion *region;
- struct device *dev;
- int ret = -ENODEV, i, j, region_count;
- u32 offset, size;
-
- dev = sst->dev;
-
- switch (sst->id) {
- case SST_DEV_ID_BYT:
- region = byt_region;
- region_count = ARRAY_SIZE(byt_region);
- sst->addr.iram_offset = SST_BYT_IRAM_OFFSET;
- sst->addr.dram_offset = SST_BYT_DRAM_OFFSET;
- sst->addr.shim_offset = SST_BYT_SHIM_OFFSET;
- break;
- default:
- dev_err(dev, "failed to get mem resources\n");
- return ret;
- }
-
- ret = sst_byt_resource_map(sst, pdata);
- if (ret < 0) {
- dev_err(dev, "failed to map resources\n");
- return ret;
- }
-
- ret = dma_coerce_mask_and_coherent(sst->dma_dev, DMA_BIT_MASK(32));
- if (ret)
- return ret;
-
- /* enable Interrupt from both sides */
- sst_dsp_shim_update_bits64(sst, SST_IMRX, 0x3, 0x0);
- sst_dsp_shim_update_bits64(sst, SST_IMRD, 0x3, 0x0);
-
- /* register DSP memory blocks - ideally we should get this from ACPI */
- for (i = 0; i < region_count; i++) {
- offset = region[i].start;
- size = (region[i].end - region[i].start) / region[i].blocks;
-
- /* register individual memory blocks */
- for (j = 0; j < region[i].blocks; j++) {
- sst_mem_block_register(sst, offset, size,
- region[i].type, NULL, j, sst);
- offset += size;
- }
- }
-
- return 0;
-}
-
-static void sst_byt_free(struct sst_dsp *sst)
-{
- sst_mem_block_unregister_all(sst);
- iounmap(sst->addr.lpe);
- iounmap(sst->addr.pci_cfg);
- iounmap(sst->addr.fw_ext);
-}
-
-struct sst_ops sst_byt_ops = {
- .reset = sst_byt_reset,
- .boot = sst_byt_boot,
- .write = sst_shim32_write,
- .read = sst_shim32_read,
- .write64 = sst_shim32_write64,
- .read64 = sst_shim32_read64,
- .ram_read = sst_memcpy_fromio_32,
- .ram_write = sst_memcpy_toio_32,
- .irq_handler = sst_byt_irq,
- .init = sst_byt_init,
- .free = sst_byt_free,
- .parse_fw = sst_byt_parse_fw_image,
-};
+++ /dev/null
-/*
- * Intel Baytrail SST IPC Support
- * Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- */
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/device.h>
-#include <linux/wait.h>
-#include <linux/spinlock.h>
-#include <linux/workqueue.h>
-#include <linux/export.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/kthread.h>
-#include <linux/firmware.h>
-#include <linux/io.h>
-#include <asm/div64.h>
-
-#include "sst-baytrail-ipc.h"
-#include "sst-dsp.h"
-#include "sst-dsp-priv.h"
-
-/* IPC message timeout */
-#define IPC_TIMEOUT_MSECS 300
-#define IPC_BOOT_MSECS 200
-
-#define IPC_EMPTY_LIST_SIZE 8
-
-/* IPC header bits */
-#define IPC_HEADER_MSG_ID_MASK 0xff
-#define IPC_HEADER_MSG_ID(x) ((x) & IPC_HEADER_MSG_ID_MASK)
-#define IPC_HEADER_STR_ID_SHIFT 8
-#define IPC_HEADER_STR_ID_MASK 0x1f
-#define IPC_HEADER_STR_ID(x) (((x) & 0x1f) << IPC_HEADER_STR_ID_SHIFT)
-#define IPC_HEADER_LARGE_SHIFT 13
-#define IPC_HEADER_LARGE(x) (((x) & 0x1) << IPC_HEADER_LARGE_SHIFT)
-#define IPC_HEADER_DATA_SHIFT 16
-#define IPC_HEADER_DATA_MASK 0x3fff
-#define IPC_HEADER_DATA(x) (((x) & 0x3fff) << IPC_HEADER_DATA_SHIFT)
-
-/* mask for differentiating between notification and reply message */
-#define IPC_NOTIFICATION (0x1 << 7)
-
-/* I2L Stream config/control msgs */
-#define IPC_IA_ALLOC_STREAM 0x20
-#define IPC_IA_FREE_STREAM 0x21
-#define IPC_IA_PAUSE_STREAM 0x24
-#define IPC_IA_RESUME_STREAM 0x25
-#define IPC_IA_DROP_STREAM 0x26
-#define IPC_IA_START_STREAM 0x30
-
-/* notification messages */
-#define IPC_IA_FW_INIT_CMPLT 0x81
-#define IPC_SST_PERIOD_ELAPSED 0x97
-
-/* IPC messages between host and ADSP */
-struct sst_byt_address_info {
- u32 addr;
- u32 size;
-} __packed;
-
-struct sst_byt_str_type {
- u8 codec_type;
- u8 str_type;
- u8 operation;
- u8 protected_str;
- u8 time_slots;
- u8 reserved;
- u16 result;
-} __packed;
-
-struct sst_byt_pcm_params {
- u8 num_chan;
- u8 pcm_wd_sz;
- u8 use_offload_path;
- u8 reserved;
- u32 sfreq;
- u8 channel_map[8];
-} __packed;
-
-struct sst_byt_frames_info {
- u16 num_entries;
- u16 rsrvd;
- u32 frag_size;
- struct sst_byt_address_info ring_buf_info[8];
-} __packed;
-
-struct sst_byt_alloc_params {
- struct sst_byt_str_type str_type;
- struct sst_byt_pcm_params pcm_params;
- struct sst_byt_frames_info frame_info;
-} __packed;
-
-struct sst_byt_alloc_response {
- struct sst_byt_str_type str_type;
- u8 reserved[88];
-} __packed;
-
-struct sst_byt_start_stream_params {
- u32 byte_offset;
-} __packed;
-
-struct sst_byt_tstamp {
- u64 ring_buffer_counter;
- u64 hardware_counter;
- u64 frames_decoded;
- u64 bytes_decoded;
- u64 bytes_copied;
- u32 sampling_frequency;
- u32 channel_peak[8];
-} __packed;
-
-struct sst_byt_fw_version {
- u8 build;
- u8 minor;
- u8 major;
- u8 type;
-} __packed;
-
-struct sst_byt_fw_build_info {
- u8 date[16];
- u8 time[16];
-} __packed;
-
-struct sst_byt_fw_init {
- struct sst_byt_fw_version fw_version;
- struct sst_byt_fw_build_info build_info;
- u16 result;
- u8 module_id;
- u8 debug_info;
-} __packed;
-
-/* driver internal IPC message structure */
-struct ipc_message {
- struct list_head list;
- u64 header;
-
- /* direction wrt host CPU */
- char tx_data[SST_BYT_IPC_MAX_PAYLOAD_SIZE];
- size_t tx_size;
- char rx_data[SST_BYT_IPC_MAX_PAYLOAD_SIZE];
- size_t rx_size;
-
- wait_queue_head_t waitq;
- bool complete;
- bool wait;
- int errno;
-};
-
-struct sst_byt_stream;
-struct sst_byt;
-
-/* stream infomation */
-struct sst_byt_stream {
- struct list_head node;
-
- /* configuration */
- struct sst_byt_alloc_params request;
- struct sst_byt_alloc_response reply;
-
- /* runtime info */
- struct sst_byt *byt;
- int str_id;
- bool commited;
- bool running;
-
- /* driver callback */
- u32 (*notify_position)(struct sst_byt_stream *stream, void *data);
- void *pdata;
-};
-
-/* SST Baytrail IPC data */
-struct sst_byt {
- struct device *dev;
- struct sst_dsp *dsp;
-
- /* stream */
- struct list_head stream_list;
-
- /* boot */
- wait_queue_head_t boot_wait;
- bool boot_complete;
- struct sst_fw *fw;
-
- /* IPC messaging */
- struct list_head tx_list;
- struct list_head rx_list;
- struct list_head empty_list;
- wait_queue_head_t wait_txq;
- struct task_struct *tx_thread;
- struct kthread_worker kworker;
- struct kthread_work kwork;
- struct ipc_message *msg;
-};
-
-static inline u64 sst_byt_header(int msg_id, int data, bool large, int str_id)
-{
- u64 header;
-
- header = IPC_HEADER_MSG_ID(msg_id) |
- IPC_HEADER_STR_ID(str_id) |
- IPC_HEADER_LARGE(large) |
- IPC_HEADER_DATA(data) |
- SST_BYT_IPCX_BUSY;
-
- return header;
-}
-
-static inline u16 sst_byt_header_msg_id(u64 header)
-{
- return header & IPC_HEADER_MSG_ID_MASK;
-}
-
-static inline u8 sst_byt_header_str_id(u64 header)
-{
- return (header >> IPC_HEADER_STR_ID_SHIFT) & IPC_HEADER_STR_ID_MASK;
-}
-
-static inline u16 sst_byt_header_data(u64 header)
-{
- return (header >> IPC_HEADER_DATA_SHIFT) & IPC_HEADER_DATA_MASK;
-}
-
-static struct sst_byt_stream *sst_byt_get_stream(struct sst_byt *byt,
- int stream_id)
-{
- struct sst_byt_stream *stream;
-
- list_for_each_entry(stream, &byt->stream_list, node) {
- if (stream->str_id == stream_id)
- return stream;
- }
-
- return NULL;
-}
-
-static void sst_byt_ipc_shim_dbg(struct sst_byt *byt, const char *text)
-{
- struct sst_dsp *sst = byt->dsp;
- u64 isr, ipcd, imrx, ipcx;
-
- ipcx = sst_dsp_shim_read64_unlocked(sst, SST_IPCX);
- isr = sst_dsp_shim_read64_unlocked(sst, SST_ISRX);
- ipcd = sst_dsp_shim_read64_unlocked(sst, SST_IPCD);
- imrx = sst_dsp_shim_read64_unlocked(sst, SST_IMRX);
-
- dev_err(byt->dev,
- "ipc: --%s-- ipcx 0x%llx isr 0x%llx ipcd 0x%llx imrx 0x%llx\n",
- text, ipcx, isr, ipcd, imrx);
-}
-
-/* locks held by caller */
-static struct ipc_message *sst_byt_msg_get_empty(struct sst_byt *byt)
-{
- struct ipc_message *msg = NULL;
-
- if (!list_empty(&byt->empty_list)) {
- msg = list_first_entry(&byt->empty_list,
- struct ipc_message, list);
- list_del(&msg->list);
- }
-
- return msg;
-}
-
-static void sst_byt_ipc_tx_msgs(struct kthread_work *work)
-{
- struct sst_byt *byt =
- container_of(work, struct sst_byt, kwork);
- struct ipc_message *msg;
- u64 ipcx;
- unsigned long flags;
-
- spin_lock_irqsave(&byt->dsp->spinlock, flags);
- if (list_empty(&byt->tx_list)) {
- spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
- return;
- }
-
- /* if the DSP is busy we will TX messages after IRQ */
- ipcx = sst_dsp_shim_read64_unlocked(byt->dsp, SST_IPCX);
- if (ipcx & SST_BYT_IPCX_BUSY) {
- spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
- return;
- }
-
- msg = list_first_entry(&byt->tx_list, struct ipc_message, list);
-
- list_move(&msg->list, &byt->rx_list);
-
- /* send the message */
- if (msg->header & IPC_HEADER_LARGE(true))
- sst_dsp_outbox_write(byt->dsp, msg->tx_data, msg->tx_size);
- sst_dsp_shim_write64_unlocked(byt->dsp, SST_IPCX, msg->header);
-
- spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
-}
-
-static inline void sst_byt_tx_msg_reply_complete(struct sst_byt *byt,
- struct ipc_message *msg)
-{
- msg->complete = true;
-
- if (!msg->wait)
- list_add_tail(&msg->list, &byt->empty_list);
- else
- wake_up(&msg->waitq);
-}
-
-static void sst_byt_drop_all(struct sst_byt *byt)
-{
- struct ipc_message *msg, *tmp;
- unsigned long flags;
-
- /* drop all TX and Rx messages before we stall + reset DSP */
- spin_lock_irqsave(&byt->dsp->spinlock, flags);
- list_for_each_entry_safe(msg, tmp, &byt->tx_list, list) {
- list_move(&msg->list, &byt->empty_list);
- }
-
- list_for_each_entry_safe(msg, tmp, &byt->rx_list, list) {
- list_move(&msg->list, &byt->empty_list);
- }
-
- spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
-}
-
-static int sst_byt_tx_wait_done(struct sst_byt *byt, struct ipc_message *msg,
- void *rx_data)
-{
- unsigned long flags;
- int ret;
-
- /* wait for DSP completion */
- ret = wait_event_timeout(msg->waitq, msg->complete,
- msecs_to_jiffies(IPC_TIMEOUT_MSECS));
-
- spin_lock_irqsave(&byt->dsp->spinlock, flags);
- if (ret == 0) {
- list_del(&msg->list);
- sst_byt_ipc_shim_dbg(byt, "message timeout");
-
- ret = -ETIMEDOUT;
- } else {
-
- /* copy the data returned from DSP */
- if (msg->rx_size)
- memcpy(rx_data, msg->rx_data, msg->rx_size);
- ret = msg->errno;
- }
-
- list_add_tail(&msg->list, &byt->empty_list);
- spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
- return ret;
-}
-
-static int sst_byt_ipc_tx_message(struct sst_byt *byt, u64 header,
- void *tx_data, size_t tx_bytes,
- void *rx_data, size_t rx_bytes, int wait)
-{
- unsigned long flags;
- struct ipc_message *msg;
-
- spin_lock_irqsave(&byt->dsp->spinlock, flags);
-
- msg = sst_byt_msg_get_empty(byt);
- if (msg == NULL) {
- spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
- return -EBUSY;
- }
-
- msg->header = header;
- msg->tx_size = tx_bytes;
- msg->rx_size = rx_bytes;
- msg->wait = wait;
- msg->errno = 0;
- msg->complete = false;
-
- if (tx_bytes) {
- /* msg content = lower 32-bit of the header + data */
- *(u32 *)msg->tx_data = (u32)(header & (u32)-1);
- memcpy(msg->tx_data + sizeof(u32), tx_data, tx_bytes);
- msg->tx_size += sizeof(u32);
- }
-
- list_add_tail(&msg->list, &byt->tx_list);
- spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
-
- queue_kthread_work(&byt->kworker, &byt->kwork);
-
- if (wait)
- return sst_byt_tx_wait_done(byt, msg, rx_data);
- else
- return 0;
-}
-
-static inline int sst_byt_ipc_tx_msg_wait(struct sst_byt *byt, u64 header,
- void *tx_data, size_t tx_bytes,
- void *rx_data, size_t rx_bytes)
-{
- return sst_byt_ipc_tx_message(byt, header, tx_data, tx_bytes,
- rx_data, rx_bytes, 1);
-}
-
-static inline int sst_byt_ipc_tx_msg_nowait(struct sst_byt *byt, u64 header,
- void *tx_data, size_t tx_bytes)
-{
- return sst_byt_ipc_tx_message(byt, header, tx_data, tx_bytes,
- NULL, 0, 0);
-}
-
-static struct ipc_message *sst_byt_reply_find_msg(struct sst_byt *byt,
- u64 header)
-{
- struct ipc_message *msg = NULL, *_msg;
- u64 mask;
-
- /* match reply to message sent based on msg and stream IDs */
- mask = IPC_HEADER_MSG_ID_MASK |
- IPC_HEADER_STR_ID_MASK << IPC_HEADER_STR_ID_SHIFT;
- header &= mask;
-
- if (list_empty(&byt->rx_list)) {
- dev_err(byt->dev,
- "ipc: rx list is empty but received 0x%llx\n", header);
- goto out;
- }
-
- list_for_each_entry(_msg, &byt->rx_list, list) {
- if ((_msg->header & mask) == header) {
- msg = _msg;
- break;
- }
- }
-
-out:
- return msg;
-}
-
-static void sst_byt_stream_update(struct sst_byt *byt, struct ipc_message *msg)
-{
- struct sst_byt_stream *stream;
- u64 header = msg->header;
- u8 stream_id = sst_byt_header_str_id(header);
- u8 stream_msg = sst_byt_header_msg_id(header);
-
- stream = sst_byt_get_stream(byt, stream_id);
- if (stream == NULL)
- return;
-
- switch (stream_msg) {
- case IPC_IA_DROP_STREAM:
- case IPC_IA_PAUSE_STREAM:
- case IPC_IA_FREE_STREAM:
- stream->running = false;
- break;
- case IPC_IA_START_STREAM:
- case IPC_IA_RESUME_STREAM:
- stream->running = true;
- break;
- }
-}
-
-static int sst_byt_process_reply(struct sst_byt *byt, u64 header)
-{
- struct ipc_message *msg;
-
- msg = sst_byt_reply_find_msg(byt, header);
- if (msg == NULL)
- return 1;
-
- if (header & IPC_HEADER_LARGE(true)) {
- msg->rx_size = sst_byt_header_data(header);
- sst_dsp_inbox_read(byt->dsp, msg->rx_data, msg->rx_size);
- }
-
- /* update any stream states */
- sst_byt_stream_update(byt, msg);
-
- list_del(&msg->list);
- /* wake up */
- sst_byt_tx_msg_reply_complete(byt, msg);
-
- return 1;
-}
-
-static void sst_byt_fw_ready(struct sst_byt *byt, u64 header)
-{
- dev_dbg(byt->dev, "ipc: DSP is ready 0x%llX\n", header);
-
- byt->boot_complete = true;
- wake_up(&byt->boot_wait);
-}
-
-static int sst_byt_process_notification(struct sst_byt *byt,
- unsigned long *flags)
-{
- struct sst_dsp *sst = byt->dsp;
- struct sst_byt_stream *stream;
- u64 header;
- u8 msg_id, stream_id;
- int handled = 1;
-
- header = sst_dsp_shim_read64_unlocked(sst, SST_IPCD);
- msg_id = sst_byt_header_msg_id(header);
-
- switch (msg_id) {
- case IPC_SST_PERIOD_ELAPSED:
- stream_id = sst_byt_header_str_id(header);
- stream = sst_byt_get_stream(byt, stream_id);
- if (stream && stream->running && stream->notify_position) {
- spin_unlock_irqrestore(&sst->spinlock, *flags);
- stream->notify_position(stream, stream->pdata);
- spin_lock_irqsave(&sst->spinlock, *flags);
- }
- break;
- case IPC_IA_FW_INIT_CMPLT:
- sst_byt_fw_ready(byt, header);
- break;
- }
-
- return handled;
-}
-
-static irqreturn_t sst_byt_irq_thread(int irq, void *context)
-{
- struct sst_dsp *sst = (struct sst_dsp *) context;
- struct sst_byt *byt = sst_dsp_get_thread_context(sst);
- u64 header;
- unsigned long flags;
-
- spin_lock_irqsave(&sst->spinlock, flags);
-
- header = sst_dsp_shim_read64_unlocked(sst, SST_IPCD);
- if (header & SST_BYT_IPCD_BUSY) {
- if (header & IPC_NOTIFICATION) {
- /* message from ADSP */
- sst_byt_process_notification(byt, &flags);
- } else {
- /* reply from ADSP */
- sst_byt_process_reply(byt, header);
- }
- /*
- * clear IPCD BUSY bit and set DONE bit. Tell DSP we have
- * processed the message and can accept new. Clear data part
- * of the header
- */
- sst_dsp_shim_update_bits64_unlocked(sst, SST_IPCD,
- SST_BYT_IPCD_DONE | SST_BYT_IPCD_BUSY |
- IPC_HEADER_DATA(IPC_HEADER_DATA_MASK),
- SST_BYT_IPCD_DONE);
- /* unmask message request interrupts */
- sst_dsp_shim_update_bits64_unlocked(sst, SST_IMRX,
- SST_BYT_IMRX_REQUEST, 0);
- }
-
- spin_unlock_irqrestore(&sst->spinlock, flags);
-
- /* continue to send any remaining messages... */
- queue_kthread_work(&byt->kworker, &byt->kwork);
-
- return IRQ_HANDLED;
-}
-
-/* stream API */
-struct sst_byt_stream *sst_byt_stream_new(struct sst_byt *byt, int id,
- u32 (*notify_position)(struct sst_byt_stream *stream, void *data),
- void *data)
-{
- struct sst_byt_stream *stream;
- struct sst_dsp *sst = byt->dsp;
- unsigned long flags;
-
- stream = kzalloc(sizeof(*stream), GFP_KERNEL);
- if (stream == NULL)
- return NULL;
-
- spin_lock_irqsave(&sst->spinlock, flags);
- list_add(&stream->node, &byt->stream_list);
- stream->notify_position = notify_position;
- stream->pdata = data;
- stream->byt = byt;
- stream->str_id = id;
- spin_unlock_irqrestore(&sst->spinlock, flags);
-
- return stream;
-}
-
-int sst_byt_stream_set_bits(struct sst_byt *byt, struct sst_byt_stream *stream,
- int bits)
-{
- stream->request.pcm_params.pcm_wd_sz = bits;
- return 0;
-}
-
-int sst_byt_stream_set_channels(struct sst_byt *byt,
- struct sst_byt_stream *stream, u8 channels)
-{
- stream->request.pcm_params.num_chan = channels;
- return 0;
-}
-
-int sst_byt_stream_set_rate(struct sst_byt *byt, struct sst_byt_stream *stream,
- unsigned int rate)
-{
- stream->request.pcm_params.sfreq = rate;
- return 0;
-}
-
-/* stream sonfiguration */
-int sst_byt_stream_type(struct sst_byt *byt, struct sst_byt_stream *stream,
- int codec_type, int stream_type, int operation)
-{
- stream->request.str_type.codec_type = codec_type;
- stream->request.str_type.str_type = stream_type;
- stream->request.str_type.operation = operation;
- stream->request.str_type.time_slots = 0xc;
-
- return 0;
-}
-
-int sst_byt_stream_buffer(struct sst_byt *byt, struct sst_byt_stream *stream,
- uint32_t buffer_addr, uint32_t buffer_size)
-{
- stream->request.frame_info.num_entries = 1;
- stream->request.frame_info.ring_buf_info[0].addr = buffer_addr;
- stream->request.frame_info.ring_buf_info[0].size = buffer_size;
- /* calculate bytes per 4 ms fragment */
- stream->request.frame_info.frag_size =
- stream->request.pcm_params.sfreq *
- stream->request.pcm_params.num_chan *
- stream->request.pcm_params.pcm_wd_sz / 8 *
- 4 / 1000;
- return 0;
-}
-
-int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream)
-{
- struct sst_byt_alloc_params *str_req = &stream->request;
- struct sst_byt_alloc_response *reply = &stream->reply;
- u64 header;
- int ret;
-
- header = sst_byt_header(IPC_IA_ALLOC_STREAM,
- sizeof(*str_req) + sizeof(u32),
- true, stream->str_id);
- ret = sst_byt_ipc_tx_msg_wait(byt, header, str_req, sizeof(*str_req),
- reply, sizeof(*reply));
- if (ret < 0) {
- dev_err(byt->dev, "ipc: error stream commit failed\n");
- return ret;
- }
-
- stream->commited = true;
-
- return 0;
-}
-
-int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream)
-{
- u64 header;
- int ret = 0;
- struct sst_dsp *sst = byt->dsp;
- unsigned long flags;
-
- if (!stream->commited)
- goto out;
-
- header = sst_byt_header(IPC_IA_FREE_STREAM, 0, false, stream->str_id);
- ret = sst_byt_ipc_tx_msg_wait(byt, header, NULL, 0, NULL, 0);
- if (ret < 0) {
- dev_err(byt->dev, "ipc: free stream %d failed\n",
- stream->str_id);
- return -EAGAIN;
- }
-
- stream->commited = false;
-out:
- spin_lock_irqsave(&sst->spinlock, flags);
- list_del(&stream->node);
- kfree(stream);
- spin_unlock_irqrestore(&sst->spinlock, flags);
-
- return ret;
-}
-
-static int sst_byt_stream_operations(struct sst_byt *byt, int type,
- int stream_id, int wait)
-{
- u64 header;
-
- header = sst_byt_header(type, 0, false, stream_id);
- if (wait)
- return sst_byt_ipc_tx_msg_wait(byt, header, NULL, 0, NULL, 0);
- else
- return sst_byt_ipc_tx_msg_nowait(byt, header, NULL, 0);
-}
-
-/* stream ALSA trigger operations */
-int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream,
- u32 start_offset)
-{
- struct sst_byt_start_stream_params start_stream;
- void *tx_msg;
- size_t size;
- u64 header;
- int ret;
-
- start_stream.byte_offset = start_offset;
- header = sst_byt_header(IPC_IA_START_STREAM,
- sizeof(start_stream) + sizeof(u32),
- true, stream->str_id);
- tx_msg = &start_stream;
- size = sizeof(start_stream);
-
- ret = sst_byt_ipc_tx_msg_nowait(byt, header, tx_msg, size);
- if (ret < 0)
- dev_err(byt->dev, "ipc: error failed to start stream %d\n",
- stream->str_id);
-
- return ret;
-}
-
-int sst_byt_stream_stop(struct sst_byt *byt, struct sst_byt_stream *stream)
-{
- int ret;
-
- /* don't stop streams that are not commited */
- if (!stream->commited)
- return 0;
-
- ret = sst_byt_stream_operations(byt, IPC_IA_DROP_STREAM,
- stream->str_id, 0);
- if (ret < 0)
- dev_err(byt->dev, "ipc: error failed to stop stream %d\n",
- stream->str_id);
- return ret;
-}
-
-int sst_byt_stream_pause(struct sst_byt *byt, struct sst_byt_stream *stream)
-{
- int ret;
-
- ret = sst_byt_stream_operations(byt, IPC_IA_PAUSE_STREAM,
- stream->str_id, 0);
- if (ret < 0)
- dev_err(byt->dev, "ipc: error failed to pause stream %d\n",
- stream->str_id);
-
- return ret;
-}
-
-int sst_byt_stream_resume(struct sst_byt *byt, struct sst_byt_stream *stream)
-{
- int ret;
-
- ret = sst_byt_stream_operations(byt, IPC_IA_RESUME_STREAM,
- stream->str_id, 0);
- if (ret < 0)
- dev_err(byt->dev, "ipc: error failed to resume stream %d\n",
- stream->str_id);
-
- return ret;
-}
-
-int sst_byt_get_dsp_position(struct sst_byt *byt,
- struct sst_byt_stream *stream, int buffer_size)
-{
- struct sst_dsp *sst = byt->dsp;
- struct sst_byt_tstamp fw_tstamp;
- u8 str_id = stream->str_id;
- u32 tstamp_offset;
-
- tstamp_offset = SST_BYT_TIMESTAMP_OFFSET + str_id * sizeof(fw_tstamp);
- memcpy_fromio(&fw_tstamp,
- sst->addr.lpe + tstamp_offset, sizeof(fw_tstamp));
-
- return do_div(fw_tstamp.ring_buffer_counter, buffer_size);
-}
-
-static int msg_empty_list_init(struct sst_byt *byt)
-{
- struct ipc_message *msg;
- int i;
-
- byt->msg = kzalloc(sizeof(*msg) * IPC_EMPTY_LIST_SIZE, GFP_KERNEL);
- if (byt->msg == NULL)
- return -ENOMEM;
-
- for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
- init_waitqueue_head(&byt->msg[i].waitq);
- list_add(&byt->msg[i].list, &byt->empty_list);
- }
-
- return 0;
-}
-
-struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt)
-{
- return byt->dsp;
-}
-
-static struct sst_dsp_device byt_dev = {
- .thread = sst_byt_irq_thread,
- .ops = &sst_byt_ops,
-};
-
-int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata)
-{
- struct sst_byt *byt = pdata->dsp;
-
- dev_dbg(byt->dev, "dsp reset\n");
- sst_dsp_reset(byt->dsp);
- sst_byt_drop_all(byt);
- dev_dbg(byt->dev, "dsp in reset\n");
-
- dev_dbg(byt->dev, "free all blocks and unload fw\n");
- sst_fw_unload(byt->fw);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(sst_byt_dsp_suspend_late);
-
-int sst_byt_dsp_boot(struct device *dev, struct sst_pdata *pdata)
-{
- struct sst_byt *byt = pdata->dsp;
- int ret;
-
- dev_dbg(byt->dev, "reload dsp fw\n");
-
- sst_dsp_reset(byt->dsp);
-
- ret = sst_fw_reload(byt->fw);
- if (ret < 0) {
- dev_err(dev, "error: failed to reload firmware\n");
- return ret;
- }
-
- /* wait for DSP boot completion */
- byt->boot_complete = false;
- sst_dsp_boot(byt->dsp);
- dev_dbg(byt->dev, "dsp booting...\n");
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(sst_byt_dsp_boot);
-
-int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata)
-{
- struct sst_byt *byt = pdata->dsp;
- int err;
-
- dev_dbg(byt->dev, "wait for dsp reboot\n");
-
- err = wait_event_timeout(byt->boot_wait, byt->boot_complete,
- msecs_to_jiffies(IPC_BOOT_MSECS));
- if (err == 0) {
- dev_err(byt->dev, "ipc: error DSP boot timeout\n");
- return -EIO;
- }
-
- dev_dbg(byt->dev, "dsp rebooted\n");
- return 0;
-}
-EXPORT_SYMBOL_GPL(sst_byt_dsp_wait_for_ready);
-
-int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
-{
- struct sst_byt *byt;
- struct sst_fw *byt_sst_fw;
- struct sst_byt_fw_init init;
- int err;
-
- dev_dbg(dev, "initialising Byt DSP IPC\n");
-
- byt = devm_kzalloc(dev, sizeof(*byt), GFP_KERNEL);
- if (byt == NULL)
- return -ENOMEM;
-
- byt->dev = dev;
- INIT_LIST_HEAD(&byt->stream_list);
- INIT_LIST_HEAD(&byt->tx_list);
- INIT_LIST_HEAD(&byt->rx_list);
- INIT_LIST_HEAD(&byt->empty_list);
- init_waitqueue_head(&byt->boot_wait);
- init_waitqueue_head(&byt->wait_txq);
-
- err = msg_empty_list_init(byt);
- if (err < 0)
- return -ENOMEM;
-
- /* start the IPC message thread */
- init_kthread_worker(&byt->kworker);
- byt->tx_thread = kthread_run(kthread_worker_fn,
- &byt->kworker, "%s",
- dev_name(byt->dev));
- if (IS_ERR(byt->tx_thread)) {
- err = PTR_ERR(byt->tx_thread);
- dev_err(byt->dev, "error failed to create message TX task\n");
- goto err_free_msg;
- }
- init_kthread_work(&byt->kwork, sst_byt_ipc_tx_msgs);
-
- byt_dev.thread_context = byt;
-
- /* init SST shim */
- byt->dsp = sst_dsp_new(dev, &byt_dev, pdata);
- if (byt->dsp == NULL) {
- err = -ENODEV;
- goto dsp_err;
- }
-
- /* keep the DSP in reset state for base FW loading */
- sst_dsp_reset(byt->dsp);
-
- byt_sst_fw = sst_fw_new(byt->dsp, pdata->fw, byt);
- if (byt_sst_fw == NULL) {
- err = -ENODEV;
- dev_err(dev, "error: failed to load firmware\n");
- goto fw_err;
- }
-
- /* wait for DSP boot completion */
- sst_dsp_boot(byt->dsp);
- err = wait_event_timeout(byt->boot_wait, byt->boot_complete,
- msecs_to_jiffies(IPC_BOOT_MSECS));
- if (err == 0) {
- err = -EIO;
- dev_err(byt->dev, "ipc: error DSP boot timeout\n");
- goto boot_err;
- }
-
- /* show firmware information */
- sst_dsp_inbox_read(byt->dsp, &init, sizeof(init));
- dev_info(byt->dev, "FW version: %02x.%02x.%02x.%02x\n",
- init.fw_version.major, init.fw_version.minor,
- init.fw_version.build, init.fw_version.type);
- dev_info(byt->dev, "Build type: %x\n", init.fw_version.type);
- dev_info(byt->dev, "Build date: %s %s\n",
- init.build_info.date, init.build_info.time);
-
- pdata->dsp = byt;
- byt->fw = byt_sst_fw;
-
- return 0;
-
-boot_err:
- sst_dsp_reset(byt->dsp);
- sst_fw_free(byt_sst_fw);
-fw_err:
- sst_dsp_free(byt->dsp);
-dsp_err:
- kthread_stop(byt->tx_thread);
-err_free_msg:
- kfree(byt->msg);
-
- return err;
-}
-EXPORT_SYMBOL_GPL(sst_byt_dsp_init);
-
-void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata)
-{
- struct sst_byt *byt = pdata->dsp;
-
- sst_dsp_reset(byt->dsp);
- sst_fw_free_all(byt->dsp);
- sst_dsp_free(byt->dsp);
- kthread_stop(byt->tx_thread);
- kfree(byt->msg);
-}
-EXPORT_SYMBOL_GPL(sst_byt_dsp_free);
+++ /dev/null
-/*
- * Intel Baytrail SST IPC Support
- * Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- */
-
-#ifndef __SST_BYT_IPC_H
-#define __SST_BYT_IPC_H
-
-#include <linux/types.h>
-
-struct sst_byt;
-struct sst_byt_stream;
-struct sst_pdata;
-extern struct sst_ops sst_byt_ops;
-
-
-#define SST_BYT_MAILBOX_OFFSET 0x144000
-#define SST_BYT_TIMESTAMP_OFFSET (SST_BYT_MAILBOX_OFFSET + 0x800)
-
-/**
- * Upfront defined maximum message size that is
- * expected by the in/out communication pipes in FW.
- */
-#define SST_BYT_IPC_MAX_PAYLOAD_SIZE 200
-
-/* stream API */
-struct sst_byt_stream *sst_byt_stream_new(struct sst_byt *byt, int id,
- uint32_t (*get_write_position)(struct sst_byt_stream *stream,
- void *data),
- void *data);
-
-/* stream configuration */
-int sst_byt_stream_set_bits(struct sst_byt *byt, struct sst_byt_stream *stream,
- int bits);
-int sst_byt_stream_set_channels(struct sst_byt *byt,
- struct sst_byt_stream *stream, u8 channels);
-int sst_byt_stream_set_rate(struct sst_byt *byt, struct sst_byt_stream *stream,
- unsigned int rate);
-int sst_byt_stream_type(struct sst_byt *byt, struct sst_byt_stream *stream,
- int codec_type, int stream_type, int operation);
-int sst_byt_stream_buffer(struct sst_byt *byt, struct sst_byt_stream *stream,
- uint32_t buffer_addr, uint32_t buffer_size);
-int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream);
-int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream);
-
-/* stream ALSA trigger operations */
-int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream,
- u32 start_offset);
-int sst_byt_stream_stop(struct sst_byt *byt, struct sst_byt_stream *stream);
-int sst_byt_stream_pause(struct sst_byt *byt, struct sst_byt_stream *stream);
-int sst_byt_stream_resume(struct sst_byt *byt, struct sst_byt_stream *stream);
-
-int sst_byt_get_dsp_position(struct sst_byt *byt,
- struct sst_byt_stream *stream, int buffer_size);
-
-/* init */
-int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata);
-void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata);
-struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt);
-int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata);
-int sst_byt_dsp_boot(struct device *dev, struct sst_pdata *pdata);
-int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata);
-
-#endif
+++ /dev/null
-/*
- * Intel Baytrail SST PCM Support
- * Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- */
-
-#include <linux/module.h>
-#include <linux/dma-mapping.h>
-#include <linux/slab.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include "sst-baytrail-ipc.h"
-#include "sst-dsp-priv.h"
-#include "sst-dsp.h"
-
-#define BYT_PCM_COUNT 2
-
-static const struct snd_pcm_hardware sst_byt_pcm_hardware = {
- .info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_RESUME,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- .period_bytes_min = 384,
- .period_bytes_max = 48000,
- .periods_min = 2,
- .periods_max = 250,
- .buffer_bytes_max = 96000,
-};
-
-/* private data for each PCM DSP stream */
-struct sst_byt_pcm_data {
- struct sst_byt_stream *stream;
- struct snd_pcm_substream *substream;
- struct mutex mutex;
-
- /* latest DSP DMA hw pointer */
- u32 hw_ptr;
-
- struct work_struct work;
-};
-
-/* private data for the driver */
-struct sst_byt_priv_data {
- /* runtime DSP */
- struct sst_byt *byt;
-
- /* DAI data */
- struct sst_byt_pcm_data pcm[BYT_PCM_COUNT];
-
- /* flag indicating is stream context restore needed after suspend */
- bool restore_stream;
-};
-
-/* this may get called several times by oss emulation */
-static int sst_byt_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct sst_byt_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
- struct sst_byt *byt = pdata->byt;
- u32 rate, bits;
- u8 channels;
- int ret, playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
-
- dev_dbg(rtd->dev, "PCM: hw_params, pcm_data %p\n", pcm_data);
-
- ret = sst_byt_stream_type(byt, pcm_data->stream,
- 1, 1, !playback);
- if (ret < 0) {
- dev_err(rtd->dev, "failed to set stream format %d\n", ret);
- return ret;
- }
-
- rate = params_rate(params);
- ret = sst_byt_stream_set_rate(byt, pcm_data->stream, rate);
- if (ret < 0) {
- dev_err(rtd->dev, "could not set rate %d\n", rate);
- return ret;
- }
-
- bits = snd_pcm_format_width(params_format(params));
- ret = sst_byt_stream_set_bits(byt, pcm_data->stream, bits);
- if (ret < 0) {
- dev_err(rtd->dev, "could not set formats %d\n",
- params_rate(params));
- return ret;
- }
-
- channels = (u8)(params_channels(params) & 0xF);
- ret = sst_byt_stream_set_channels(byt, pcm_data->stream, channels);
- if (ret < 0) {
- dev_err(rtd->dev, "could not set channels %d\n",
- params_rate(params));
- return ret;
- }
-
- snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
-
- ret = sst_byt_stream_buffer(byt, pcm_data->stream,
- substream->dma_buffer.addr,
- params_buffer_bytes(params));
- if (ret < 0) {
- dev_err(rtd->dev, "PCM: failed to set DMA buffer %d\n", ret);
- return ret;
- }
-
- ret = sst_byt_stream_commit(byt, pcm_data->stream);
- if (ret < 0) {
- dev_err(rtd->dev, "PCM: failed stream commit %d\n", ret);
- return ret;
- }
-
- return 0;
-}
-
-static int sst_byt_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
-
- dev_dbg(rtd->dev, "PCM: hw_free\n");
- snd_pcm_lib_free_pages(substream);
-
- return 0;
-}
-
-static int sst_byt_pcm_restore_stream_context(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct sst_byt_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
- struct sst_byt *byt = pdata->byt;
- int ret;
-
- /* commit stream using existing stream params */
- ret = sst_byt_stream_commit(byt, pcm_data->stream);
- if (ret < 0) {
- dev_err(rtd->dev, "PCM: failed stream commit %d\n", ret);
- return ret;
- }
-
- sst_byt_stream_start(byt, pcm_data->stream, pcm_data->hw_ptr);
-
- dev_dbg(rtd->dev, "stream context restored at offset %d\n",
- pcm_data->hw_ptr);
-
- return 0;
-}
-
-static void sst_byt_pcm_work(struct work_struct *work)
-{
- struct sst_byt_pcm_data *pcm_data =
- container_of(work, struct sst_byt_pcm_data, work);
-
- if (snd_pcm_running(pcm_data->substream))
- sst_byt_pcm_restore_stream_context(pcm_data->substream);
-}
-
-static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct sst_byt_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
- struct sst_byt *byt = pdata->byt;
-
- dev_dbg(rtd->dev, "PCM: trigger %d\n", cmd);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- pcm_data->hw_ptr = 0;
- sst_byt_stream_start(byt, pcm_data->stream, 0);
- break;
- case SNDRV_PCM_TRIGGER_RESUME:
- if (pdata->restore_stream == true)
- schedule_work(&pcm_data->work);
- else
- sst_byt_stream_resume(byt, pcm_data->stream);
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- sst_byt_stream_resume(byt, pcm_data->stream);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- sst_byt_stream_stop(byt, pcm_data->stream);
- break;
- case SNDRV_PCM_TRIGGER_SUSPEND:
- pdata->restore_stream = false;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- sst_byt_stream_pause(byt, pcm_data->stream);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static u32 byt_notify_pointer(struct sst_byt_stream *stream, void *data)
-{
- struct sst_byt_pcm_data *pcm_data = data;
- struct snd_pcm_substream *substream = pcm_data->substream;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct sst_byt_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct sst_byt *byt = pdata->byt;
- u32 pos, hw_pos;
-
- hw_pos = sst_byt_get_dsp_position(byt, pcm_data->stream,
- snd_pcm_lib_buffer_bytes(substream));
- pcm_data->hw_ptr = hw_pos;
- pos = frames_to_bytes(runtime,
- (runtime->control->appl_ptr %
- runtime->buffer_size));
-
- dev_dbg(rtd->dev, "PCM: App/DMA pointer %u/%u bytes\n", pos, hw_pos);
-
- snd_pcm_period_elapsed(substream);
- return pos;
-}
-
-static snd_pcm_uframes_t sst_byt_pcm_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct sst_byt_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
-
- dev_dbg(rtd->dev, "PCM: DMA pointer %u bytes\n", pcm_data->hw_ptr);
-
- return bytes_to_frames(runtime, pcm_data->hw_ptr);
-}
-
-static int sst_byt_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct sst_byt_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
- struct sst_byt *byt = pdata->byt;
-
- dev_dbg(rtd->dev, "PCM: open\n");
-
- mutex_lock(&pcm_data->mutex);
-
- pcm_data->substream = substream;
-
- snd_soc_set_runtime_hwparams(substream, &sst_byt_pcm_hardware);
-
- pcm_data->stream = sst_byt_stream_new(byt, substream->stream + 1,
- byt_notify_pointer, pcm_data);
- if (pcm_data->stream == NULL) {
- dev_err(rtd->dev, "failed to create stream\n");
- mutex_unlock(&pcm_data->mutex);
- return -EINVAL;
- }
-
- mutex_unlock(&pcm_data->mutex);
- return 0;
-}
-
-static int sst_byt_pcm_close(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct sst_byt_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
- struct sst_byt *byt = pdata->byt;
- int ret;
-
- dev_dbg(rtd->dev, "PCM: close\n");
-
- cancel_work_sync(&pcm_data->work);
- mutex_lock(&pcm_data->mutex);
- ret = sst_byt_stream_free(byt, pcm_data->stream);
- if (ret < 0) {
- dev_dbg(rtd->dev, "Free stream fail\n");
- goto out;
- }
- pcm_data->stream = NULL;
-
-out:
- mutex_unlock(&pcm_data->mutex);
- return ret;
-}
-
-static int sst_byt_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
-
- dev_dbg(rtd->dev, "PCM: mmap\n");
- return snd_pcm_lib_default_mmap(substream, vma);
-}
-
-static struct snd_pcm_ops sst_byt_pcm_ops = {
- .open = sst_byt_pcm_open,
- .close = sst_byt_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = sst_byt_pcm_hw_params,
- .hw_free = sst_byt_pcm_hw_free,
- .trigger = sst_byt_pcm_trigger,
- .pointer = sst_byt_pcm_pointer,
- .mmap = sst_byt_pcm_mmap,
-};
-
-static int sst_byt_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_pcm *pcm = rtd->pcm;
- size_t size;
- struct snd_soc_platform *platform = rtd->platform;
- struct sst_pdata *pdata = dev_get_platdata(platform->dev);
- int ret = 0;
-
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream ||
- pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
- size = sst_byt_pcm_hardware.buffer_bytes_max;
- ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
- SNDRV_DMA_TYPE_DEV,
- pdata->dma_dev,
- size, size);
- if (ret) {
- dev_err(rtd->dev, "dma buffer allocation failed %d\n",
- ret);
- return ret;
- }
- }
-
- return ret;
-}
-
-static struct snd_soc_dai_driver byt_dais[] = {
- {
- .name = "Baytrail PCM",
- .playback = {
- .stream_name = "System Playback",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S24_3LE |
- SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .stream_name = "Analog Capture",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- },
-};
-
-static int sst_byt_pcm_probe(struct snd_soc_platform *platform)
-{
- struct sst_pdata *plat_data = dev_get_platdata(platform->dev);
- struct sst_byt_priv_data *priv_data;
- int i;
-
- if (!plat_data)
- return -ENODEV;
-
- priv_data = devm_kzalloc(platform->dev, sizeof(*priv_data),
- GFP_KERNEL);
- priv_data->byt = plat_data->dsp;
- snd_soc_platform_set_drvdata(platform, priv_data);
-
- for (i = 0; i < BYT_PCM_COUNT; i++) {
- mutex_init(&priv_data->pcm[i].mutex);
- INIT_WORK(&priv_data->pcm[i].work, sst_byt_pcm_work);
- }
-
- return 0;
-}
-
-static int sst_byt_pcm_remove(struct snd_soc_platform *platform)
-{
- return 0;
-}
-
-static struct snd_soc_platform_driver byt_soc_platform = {
- .probe = sst_byt_pcm_probe,
- .remove = sst_byt_pcm_remove,
- .ops = &sst_byt_pcm_ops,
- .pcm_new = sst_byt_pcm_new,
-};
-
-static const struct snd_soc_component_driver byt_dai_component = {
- .name = "byt-dai",
-};
-
-#ifdef CONFIG_PM
-static int sst_byt_pcm_dev_suspend_late(struct device *dev)
-{
- struct sst_pdata *sst_pdata = dev_get_platdata(dev);
- struct sst_byt_priv_data *priv_data = dev_get_drvdata(dev);
- int ret;
-
- dev_dbg(dev, "suspending late\n");
-
- ret = sst_byt_dsp_suspend_late(dev, sst_pdata);
- if (ret < 0) {
- dev_err(dev, "failed to suspend %d\n", ret);
- return ret;
- }
-
- priv_data->restore_stream = true;
-
- return ret;
-}
-
-static int sst_byt_pcm_dev_resume_early(struct device *dev)
-{
- struct sst_pdata *sst_pdata = dev_get_platdata(dev);
- int ret;
-
- dev_dbg(dev, "resume early\n");
-
- /* load fw and boot DSP */
- ret = sst_byt_dsp_boot(dev, sst_pdata);
- if (ret)
- return ret;
-
- /* wait for FW to finish booting */
- return sst_byt_dsp_wait_for_ready(dev, sst_pdata);
-}
-
-static const struct dev_pm_ops sst_byt_pm_ops = {
- .suspend_late = sst_byt_pcm_dev_suspend_late,
- .resume_early = sst_byt_pcm_dev_resume_early,
-};
-
-#define SST_BYT_PM_OPS (&sst_byt_pm_ops)
-#else
-#define SST_BYT_PM_OPS NULL
-#endif
-
-static int sst_byt_pcm_dev_probe(struct platform_device *pdev)
-{
- struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
- int ret;
-
- ret = sst_byt_dsp_init(&pdev->dev, sst_pdata);
- if (ret < 0)
- return -ENODEV;
-
- ret = snd_soc_register_platform(&pdev->dev, &byt_soc_platform);
- if (ret < 0)
- goto err_plat;
-
- ret = snd_soc_register_component(&pdev->dev, &byt_dai_component,
- byt_dais, ARRAY_SIZE(byt_dais));
- if (ret < 0)
- goto err_comp;
-
- return 0;
-
-err_comp:
- snd_soc_unregister_platform(&pdev->dev);
-err_plat:
- sst_byt_dsp_free(&pdev->dev, sst_pdata);
- return ret;
-}
-
-static int sst_byt_pcm_dev_remove(struct platform_device *pdev)
-{
- struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
-
- snd_soc_unregister_platform(&pdev->dev);
- snd_soc_unregister_component(&pdev->dev);
- sst_byt_dsp_free(&pdev->dev, sst_pdata);
-
- return 0;
-}
-
-static struct platform_driver sst_byt_pcm_driver = {
- .driver = {
- .name = "baytrail-pcm-audio",
- .pm = SST_BYT_PM_OPS,
- },
-
- .probe = sst_byt_pcm_dev_probe,
- .remove = sst_byt_pcm_dev_remove,
-};
-module_platform_driver(sst_byt_pcm_driver);
-
-MODULE_AUTHOR("Jarkko Nikula");
-MODULE_DESCRIPTION("Baytrail PCM");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:baytrail-pcm-audio");
+++ /dev/null
-/*
- * Intel Smart Sound Technology
- *
- * Copyright (C) 2013, Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#ifndef __SOUND_SOC_SST_DSP_PRIV_H
-#define __SOUND_SOC_SST_DSP_PRIV_H
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/interrupt.h>
-#include <linux/firmware.h>
-
-struct sst_mem_block;
-struct sst_module;
-struct sst_fw;
-
-/* do we need to remove or keep */
-#define DSP_DRAM_ADDR_OFFSET 0x400000
-
-/*
- * DSP Operations exported by platform Audio DSP driver.
- */
-struct sst_ops {
- /* DSP core boot / reset */
- void (*boot)(struct sst_dsp *);
- void (*reset)(struct sst_dsp *);
- int (*wake)(struct sst_dsp *);
- void (*sleep)(struct sst_dsp *);
- void (*stall)(struct sst_dsp *);
-
- /* Shim IO */
- void (*write)(void __iomem *addr, u32 offset, u32 value);
- u32 (*read)(void __iomem *addr, u32 offset);
- void (*write64)(void __iomem *addr, u32 offset, u64 value);
- u64 (*read64)(void __iomem *addr, u32 offset);
-
- /* DSP I/DRAM IO */
- void (*ram_read)(struct sst_dsp *sst, void *dest, void __iomem *src,
- size_t bytes);
- void (*ram_write)(struct sst_dsp *sst, void __iomem *dest, void *src,
- size_t bytes);
-
- void (*dump)(struct sst_dsp *);
-
- /* IRQ handlers */
- irqreturn_t (*irq_handler)(int irq, void *context);
-
- /* SST init and free */
- int (*init)(struct sst_dsp *sst, struct sst_pdata *pdata);
- void (*free)(struct sst_dsp *sst);
-
- /* FW module parser/loader */
- int (*parse_fw)(struct sst_fw *sst_fw);
-};
-
-/*
- * Audio DSP memory offsets and addresses.
- */
-struct sst_addr {
- u32 lpe_base;
- u32 shim_offset;
- u32 iram_offset;
- u32 dram_offset;
- u32 dsp_iram_offset;
- u32 dsp_dram_offset;
- void __iomem *lpe;
- void __iomem *shim;
- void __iomem *pci_cfg;
- void __iomem *fw_ext;
-};
-
-/*
- * Audio DSP Mailbox configuration.
- */
-struct sst_mailbox {
- void __iomem *in_base;
- void __iomem *out_base;
- size_t in_size;
- size_t out_size;
-};
-
-/*
- * Audio DSP memory block types.
- */
-enum sst_mem_type {
- SST_MEM_IRAM = 0,
- SST_MEM_DRAM = 1,
- SST_MEM_ANY = 2,
- SST_MEM_CACHE= 3,
-};
-
-/*
- * Audio DSP Generic Firmware File.
- *
- * SST Firmware files can consist of 1..N modules. This generic structure is
- * used to manage each firmware file and it's modules regardless of SST firmware
- * type. A SST driver may load multiple FW files.
- */
-struct sst_fw {
- struct sst_dsp *dsp;
-
- /* base addresses of FW file data */
- dma_addr_t dmable_fw_paddr; /* physical address of fw data */
- void *dma_buf; /* virtual address of fw data */
- u32 size; /* size of fw data */
-
- /* lists */
- struct list_head list; /* DSP list of FW */
- struct list_head module_list; /* FW list of modules */
-
- void *private; /* core doesn't touch this */
-};
-
-/*
- * Audio DSP Generic Module Template.
- *
- * Used to define and register a new FW module. This data is extracted from
- * FW module header information.
- */
-struct sst_module_template {
- u32 id;
- u32 entry; /* entry point */
- u32 scratch_size;
- u32 persistent_size;
-};
-
-/*
- * Block Allocator - Used to allocate blocks of DSP memory.
- */
-struct sst_block_allocator {
- u32 id;
- u32 offset;
- int size;
- enum sst_mem_type type;
-};
-
-/*
- * Runtime Module Instance - A module object can be instanciated multiple
- * times within the DSP FW.
- */
-struct sst_module_runtime {
- struct sst_dsp *dsp;
- int id;
- struct sst_module *module; /* parent module we belong too */
-
- u32 persistent_offset; /* private memory offset */
- void *private;
-
- struct list_head list;
- struct list_head block_list; /* list of blocks used */
-};
-
-/*
- * Runtime Module Context - The runtime context must be manually stored by the
- * driver prior to enter S3 and restored after leaving S3. This should really be
- * part of the memory context saved by the enter D3 message IPC ???
- */
-struct sst_module_runtime_context {
- dma_addr_t dma_buffer;
- u32 *buffer;
-};
-
-/*
- * Audio DSP Generic Module.
- *
- * Each Firmware file can consist of 1..N modules. A module can span multiple
- * ADSP memory blocks. The simplest FW will be a file with 1 module. A module
- * can be instanciated multiple times in the DSP.
- */
-struct sst_module {
- struct sst_dsp *dsp;
- struct sst_fw *sst_fw; /* parent FW we belong too */
-
- /* module configuration */
- u32 id;
- u32 entry; /* module entry point */
- s32 offset; /* module offset in firmware file */
- u32 size; /* module size */
- u32 scratch_size; /* global scratch memory required */
- u32 persistent_size; /* private memory required */
- enum sst_mem_type type; /* destination memory type */
- u32 data_offset; /* offset in ADSP memory space */
- void *data; /* module data */
-
- /* runtime */
- u32 usage_count; /* can be unloaded if count == 0 */
- void *private; /* core doesn't touch this */
-
- /* lists */
- struct list_head block_list; /* Module list of blocks in use */
- struct list_head list; /* DSP list of modules */
- struct list_head list_fw; /* FW list of modules */
- struct list_head runtime_list; /* list of runtime module objects*/
-};
-
-/*
- * SST Memory Block operations.
- */
-struct sst_block_ops {
- int (*enable)(struct sst_mem_block *block);
- int (*disable)(struct sst_mem_block *block);
-};
-
-/*
- * SST Generic Memory Block.
- *
- * SST ADP memory has multiple IRAM and DRAM blocks. Some ADSP blocks can be
- * power gated.
- */
-struct sst_mem_block {
- struct sst_dsp *dsp;
- struct sst_module *module; /* module that uses this block */
-
- /* block config */
- u32 offset; /* offset from base */
- u32 size; /* block size */
- u32 index; /* block index 0..N */
- enum sst_mem_type type; /* block memory type IRAM/DRAM */
- struct sst_block_ops *ops; /* block operations, if any */
-
- /* block status */
- u32 bytes_used; /* bytes in use by modules */
- void *private; /* generic core does not touch this */
- int users; /* number of modules using this block */
-
- /* block lists */
- struct list_head module_list; /* Module list of blocks */
- struct list_head list; /* Map list of free/used blocks */
-};
-
-/*
- * Generic SST Shim Interface.
- */
-struct sst_dsp {
-
- /* runtime */
- struct sst_dsp_device *sst_dev;
- spinlock_t spinlock; /* IPC locking */
- struct mutex mutex; /* DSP FW lock */
- struct device *dev;
- struct device *dma_dev;
- void *thread_context;
- int irq;
- u32 id;
-
- /* list of free and used ADSP memory blocks */
- struct list_head used_block_list;
- struct list_head free_block_list;
-
- /* operations */
- struct sst_ops *ops;
-
- /* debug FS */
- struct dentry *debugfs_root;
-
- /* base addresses */
- struct sst_addr addr;
-
- /* mailbox */
- struct sst_mailbox mailbox;
-
- /* SST FW files loaded and their modules */
- struct list_head module_list;
- struct list_head fw_list;
-
- /* scratch buffer */
- struct list_head scratch_block_list;
- u32 scratch_offset;
- u32 scratch_size;
-
- /* platform data */
- struct sst_pdata *pdata;
-
- /* DMA FW loading */
- struct sst_dma *dma;
- bool fw_use_dma;
-};
-
-/* Size optimised DRAM/IRAM memcpy */
-static inline void sst_dsp_write(struct sst_dsp *sst, void *src,
- u32 dest_offset, size_t bytes)
-{
- sst->ops->ram_write(sst, sst->addr.lpe + dest_offset, src, bytes);
-}
-
-static inline void sst_dsp_read(struct sst_dsp *sst, void *dest,
- u32 src_offset, size_t bytes)
-{
- sst->ops->ram_read(sst, dest, sst->addr.lpe + src_offset, bytes);
-}
-
-static inline void *sst_dsp_get_thread_context(struct sst_dsp *sst)
-{
- return sst->thread_context;
-}
-
-/* Create/Free FW files - can contain multiple modules */
-struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
- const struct firmware *fw, void *private);
-void sst_fw_free(struct sst_fw *sst_fw);
-void sst_fw_free_all(struct sst_dsp *dsp);
-int sst_fw_reload(struct sst_fw *sst_fw);
-void sst_fw_unload(struct sst_fw *sst_fw);
-
-/* Create/Free firmware modules */
-struct sst_module *sst_module_new(struct sst_fw *sst_fw,
- struct sst_module_template *template, void *private);
-void sst_module_free(struct sst_module *module);
-struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id);
-int sst_module_alloc_blocks(struct sst_module *module);
-int sst_module_free_blocks(struct sst_module *module);
-
-/* Create/Free firmware module runtime instances */
-struct sst_module_runtime *sst_module_runtime_new(struct sst_module *module,
- int id, void *private);
-void sst_module_runtime_free(struct sst_module_runtime *runtime);
-struct sst_module_runtime *sst_module_runtime_get_from_id(
- struct sst_module *module, u32 id);
-int sst_module_runtime_alloc_blocks(struct sst_module_runtime *runtime,
- int offset);
-int sst_module_runtime_free_blocks(struct sst_module_runtime *runtime);
-int sst_module_runtime_save(struct sst_module_runtime *runtime,
- struct sst_module_runtime_context *context);
-int sst_module_runtime_restore(struct sst_module_runtime *runtime,
- struct sst_module_runtime_context *context);
-
-/* generic block allocation */
-int sst_alloc_blocks(struct sst_dsp *dsp, struct sst_block_allocator *ba,
- struct list_head *block_list);
-int sst_free_blocks(struct sst_dsp *dsp, struct list_head *block_list);
-
-/* scratch allocation */
-int sst_block_alloc_scratch(struct sst_dsp *dsp);
-void sst_block_free_scratch(struct sst_dsp *dsp);
-
-/* Register the DSPs memory blocks - would be nice to read from ACPI */
-struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
- u32 size, enum sst_mem_type type, struct sst_block_ops *ops, u32 index,
- void *private);
-void sst_mem_block_unregister_all(struct sst_dsp *dsp);
-
-/* Create/Free DMA resources */
-int sst_dma_new(struct sst_dsp *sst);
-void sst_dma_free(struct sst_dma *dma);
-
-u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset,
- enum sst_mem_type type);
-#endif
+++ /dev/null
-/*
- * Intel Smart Sound Technology (SST) DSP Core Driver
- *
- * Copyright (C) 2013, Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/slab.h>
-#include <linux/export.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-
-#include "sst-dsp.h"
-#include "sst-dsp-priv.h"
-
-#define CREATE_TRACE_POINTS
-#include <trace/events/intel-sst.h>
-
-/* Internal generic low-level SST IO functions - can be overidden */
-void sst_shim32_write(void __iomem *addr, u32 offset, u32 value)
-{
- writel(value, addr + offset);
-}
-EXPORT_SYMBOL_GPL(sst_shim32_write);
-
-u32 sst_shim32_read(void __iomem *addr, u32 offset)
-{
- return readl(addr + offset);
-}
-EXPORT_SYMBOL_GPL(sst_shim32_read);
-
-void sst_shim32_write64(void __iomem *addr, u32 offset, u64 value)
-{
- memcpy_toio(addr + offset, &value, sizeof(value));
-}
-EXPORT_SYMBOL_GPL(sst_shim32_write64);
-
-u64 sst_shim32_read64(void __iomem *addr, u32 offset)
-{
- u64 val;
-
- memcpy_fromio(&val, addr + offset, sizeof(val));
- return val;
-}
-EXPORT_SYMBOL_GPL(sst_shim32_read64);
-
-static inline void _sst_memcpy_toio_32(volatile u32 __iomem *dest,
- u32 *src, size_t bytes)
-{
- int i, words = bytes >> 2;
-
- for (i = 0; i < words; i++)
- writel(src[i], dest + i);
-}
-
-static inline void _sst_memcpy_fromio_32(u32 *dest,
- const volatile __iomem u32 *src, size_t bytes)
-{
- int i, words = bytes >> 2;
-
- for (i = 0; i < words; i++)
- dest[i] = readl(src + i);
-}
-
-void sst_memcpy_toio_32(struct sst_dsp *sst,
- void __iomem *dest, void *src, size_t bytes)
-{
- _sst_memcpy_toio_32(dest, src, bytes);
-}
-EXPORT_SYMBOL_GPL(sst_memcpy_toio_32);
-
-void sst_memcpy_fromio_32(struct sst_dsp *sst, void *dest,
- void __iomem *src, size_t bytes)
-{
- _sst_memcpy_fromio_32(dest, src, bytes);
-}
-EXPORT_SYMBOL_GPL(sst_memcpy_fromio_32);
-
-/* Public API */
-void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&sst->spinlock, flags);
- sst->ops->write(sst->addr.shim, offset, value);
- spin_unlock_irqrestore(&sst->spinlock, flags);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_write);
-
-u32 sst_dsp_shim_read(struct sst_dsp *sst, u32 offset)
-{
- unsigned long flags;
- u32 val;
-
- spin_lock_irqsave(&sst->spinlock, flags);
- val = sst->ops->read(sst->addr.shim, offset);
- spin_unlock_irqrestore(&sst->spinlock, flags);
-
- return val;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_read);
-
-void sst_dsp_shim_write64(struct sst_dsp *sst, u32 offset, u64 value)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&sst->spinlock, flags);
- sst->ops->write64(sst->addr.shim, offset, value);
- spin_unlock_irqrestore(&sst->spinlock, flags);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_write64);
-
-u64 sst_dsp_shim_read64(struct sst_dsp *sst, u32 offset)
-{
- unsigned long flags;
- u64 val;
-
- spin_lock_irqsave(&sst->spinlock, flags);
- val = sst->ops->read64(sst->addr.shim, offset);
- spin_unlock_irqrestore(&sst->spinlock, flags);
-
- return val;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_read64);
-
-void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value)
-{
- sst->ops->write(sst->addr.shim, offset, value);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_write_unlocked);
-
-u32 sst_dsp_shim_read_unlocked(struct sst_dsp *sst, u32 offset)
-{
- return sst->ops->read(sst->addr.shim, offset);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_read_unlocked);
-
-void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value)
-{
- sst->ops->write64(sst->addr.shim, offset, value);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_write64_unlocked);
-
-u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset)
-{
- return sst->ops->read64(sst->addr.shim, offset);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_read64_unlocked);
-
-int sst_dsp_shim_update_bits_unlocked(struct sst_dsp *sst, u32 offset,
- u32 mask, u32 value)
-{
- bool change;
- unsigned int old, new;
- u32 ret;
-
- ret = sst_dsp_shim_read_unlocked(sst, offset);
-
- old = ret;
- new = (old & (~mask)) | (value & mask);
-
- change = (old != new);
- if (change)
- sst_dsp_shim_write_unlocked(sst, offset, new);
-
- return change;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_unlocked);
-
-int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset,
- u64 mask, u64 value)
-{
- bool change;
- u64 old, new;
-
- old = sst_dsp_shim_read64_unlocked(sst, offset);
-
- new = (old & (~mask)) | (value & mask);
-
- change = (old != new);
- if (change)
- sst_dsp_shim_write64_unlocked(sst, offset, new);
-
- return change;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64_unlocked);
-
-int sst_dsp_shim_update_bits(struct sst_dsp *sst, u32 offset,
- u32 mask, u32 value)
-{
- unsigned long flags;
- bool change;
-
- spin_lock_irqsave(&sst->spinlock, flags);
- change = sst_dsp_shim_update_bits_unlocked(sst, offset, mask, value);
- spin_unlock_irqrestore(&sst->spinlock, flags);
- return change;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits);
-
-int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset,
- u64 mask, u64 value)
-{
- unsigned long flags;
- bool change;
-
- spin_lock_irqsave(&sst->spinlock, flags);
- change = sst_dsp_shim_update_bits64_unlocked(sst, offset, mask, value);
- spin_unlock_irqrestore(&sst->spinlock, flags);
- return change;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64);
-
-void sst_dsp_dump(struct sst_dsp *sst)
-{
- if (sst->ops->dump)
- sst->ops->dump(sst);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_dump);
-
-void sst_dsp_reset(struct sst_dsp *sst)
-{
- if (sst->ops->reset)
- sst->ops->reset(sst);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_reset);
-
-int sst_dsp_boot(struct sst_dsp *sst)
-{
- if (sst->ops->boot)
- sst->ops->boot(sst);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_boot);
-
-int sst_dsp_wake(struct sst_dsp *sst)
-{
- if (sst->ops->wake)
- return sst->ops->wake(sst);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_wake);
-
-void sst_dsp_sleep(struct sst_dsp *sst)
-{
- if (sst->ops->sleep)
- sst->ops->sleep(sst);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_sleep);
-
-void sst_dsp_stall(struct sst_dsp *sst)
-{
- if (sst->ops->stall)
- sst->ops->stall(sst);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_stall);
-
-void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg)
-{
- sst_dsp_shim_write_unlocked(dsp, SST_IPCX, msg | SST_IPCX_BUSY);
- trace_sst_ipc_msg_tx(msg);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_tx);
-
-u32 sst_dsp_ipc_msg_rx(struct sst_dsp *dsp)
-{
- u32 msg;
-
- msg = sst_dsp_shim_read_unlocked(dsp, SST_IPCX);
- trace_sst_ipc_msg_rx(msg);
-
- return msg;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_rx);
-
-int sst_dsp_mailbox_init(struct sst_dsp *sst, u32 inbox_offset, size_t inbox_size,
- u32 outbox_offset, size_t outbox_size)
-{
- sst->mailbox.in_base = sst->addr.lpe + inbox_offset;
- sst->mailbox.out_base = sst->addr.lpe + outbox_offset;
- sst->mailbox.in_size = inbox_size;
- sst->mailbox.out_size = outbox_size;
- return 0;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_mailbox_init);
-
-void sst_dsp_outbox_write(struct sst_dsp *sst, void *message, size_t bytes)
-{
- u32 i;
-
- trace_sst_ipc_outbox_write(bytes);
-
- memcpy_toio(sst->mailbox.out_base, message, bytes);
-
- for (i = 0; i < bytes; i += 4)
- trace_sst_ipc_outbox_wdata(i, *(u32 *)(message + i));
-}
-EXPORT_SYMBOL_GPL(sst_dsp_outbox_write);
-
-void sst_dsp_outbox_read(struct sst_dsp *sst, void *message, size_t bytes)
-{
- u32 i;
-
- trace_sst_ipc_outbox_read(bytes);
-
- memcpy_fromio(message, sst->mailbox.out_base, bytes);
-
- for (i = 0; i < bytes; i += 4)
- trace_sst_ipc_outbox_rdata(i, *(u32 *)(message + i));
-}
-EXPORT_SYMBOL_GPL(sst_dsp_outbox_read);
-
-void sst_dsp_inbox_write(struct sst_dsp *sst, void *message, size_t bytes)
-{
- u32 i;
-
- trace_sst_ipc_inbox_write(bytes);
-
- memcpy_toio(sst->mailbox.in_base, message, bytes);
-
- for (i = 0; i < bytes; i += 4)
- trace_sst_ipc_inbox_wdata(i, *(u32 *)(message + i));
-}
-EXPORT_SYMBOL_GPL(sst_dsp_inbox_write);
-
-void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes)
-{
- u32 i;
-
- trace_sst_ipc_inbox_read(bytes);
-
- memcpy_fromio(message, sst->mailbox.in_base, bytes);
-
- for (i = 0; i < bytes; i += 4)
- trace_sst_ipc_inbox_rdata(i, *(u32 *)(message + i));
-}
-EXPORT_SYMBOL_GPL(sst_dsp_inbox_read);
-
-struct sst_dsp *sst_dsp_new(struct device *dev,
- struct sst_dsp_device *sst_dev, struct sst_pdata *pdata)
-{
- struct sst_dsp *sst;
- int err;
-
- dev_dbg(dev, "initialising audio DSP id 0x%x\n", pdata->id);
-
- sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL);
- if (sst == NULL)
- return NULL;
-
- spin_lock_init(&sst->spinlock);
- mutex_init(&sst->mutex);
- sst->dev = dev;
- sst->dma_dev = pdata->dma_dev;
- sst->thread_context = sst_dev->thread_context;
- sst->sst_dev = sst_dev;
- sst->id = pdata->id;
- sst->irq = pdata->irq;
- sst->ops = sst_dev->ops;
- sst->pdata = pdata;
- INIT_LIST_HEAD(&sst->used_block_list);
- INIT_LIST_HEAD(&sst->free_block_list);
- INIT_LIST_HEAD(&sst->module_list);
- INIT_LIST_HEAD(&sst->fw_list);
- INIT_LIST_HEAD(&sst->scratch_block_list);
-
- /* Initialise SST Audio DSP */
- if (sst->ops->init) {
- err = sst->ops->init(sst, pdata);
- if (err < 0)
- return NULL;
- }
-
- /* Register the ISR */
- err = request_threaded_irq(sst->irq, sst->ops->irq_handler,
- sst_dev->thread, IRQF_SHARED, "AudioDSP", sst);
- if (err)
- goto irq_err;
-
- err = sst_dma_new(sst);
- if (err)
- dev_warn(dev, "sst_dma_new failed %d\n", err);
-
- return sst;
-
-irq_err:
- if (sst->ops->free)
- sst->ops->free(sst);
-
- return NULL;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_new);
-
-void sst_dsp_free(struct sst_dsp *sst)
-{
- free_irq(sst->irq, sst);
- if (sst->ops->free)
- sst->ops->free(sst);
-
- sst_dma_free(sst->dma);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_free);
-
-/* Module information */
-MODULE_AUTHOR("Liam Girdwood");
-MODULE_DESCRIPTION("Intel SST Core");
-MODULE_LICENSE("GPL v2");
+++ /dev/null
-/*
- * Intel Smart Sound Technology (SST) Core
- *
- * Copyright (C) 2013, Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#ifndef __SOUND_SOC_SST_DSP_H
-#define __SOUND_SOC_SST_DSP_H
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/interrupt.h>
-
-/* SST Device IDs */
-#define SST_DEV_ID_LYNX_POINT 0x33C8
-#define SST_DEV_ID_WILDCAT_POINT 0x3438
-#define SST_DEV_ID_BYT 0x0F28
-
-/* Supported SST DMA Devices */
-#define SST_DMA_TYPE_DW 1
-#define SST_DMA_TYPE_MID 2
-
-/* autosuspend delay 5s*/
-#define SST_RUNTIME_SUSPEND_DELAY (5 * 1000)
-
-/* SST Shim register map
- * The register naming can differ between products. Some products also
- * contain extra functionality.
- */
-#define SST_CSR 0x00
-#define SST_PISR 0x08
-#define SST_PIMR 0x10
-#define SST_ISRX 0x18
-#define SST_ISRD 0x20
-#define SST_IMRX 0x28
-#define SST_IMRD 0x30
-#define SST_IPCX 0x38 /* IPC IA -> SST */
-#define SST_IPCD 0x40 /* IPC SST -> IA */
-#define SST_ISRSC 0x48
-#define SST_ISRLPESC 0x50
-#define SST_IMRSC 0x58
-#define SST_IMRLPESC 0x60
-#define SST_IPCSC 0x68
-#define SST_IPCLPESC 0x70
-#define SST_CLKCTL 0x78
-#define SST_CSR2 0x80
-#define SST_LTRC 0xE0
-#define SST_HMDC 0xE8
-
-#define SST_SHIM_BEGIN SST_CSR
-#define SST_SHIM_END SST_HDMC
-
-#define SST_DBGO 0xF0
-
-#define SST_SHIM_SIZE 0x100
-#define SST_PWMCTRL 0x1000
-
-/* SST Shim Register bits
- * The register bit naming can differ between products. Some products also
- * contain extra functionality.
- */
-
-/* CSR / CS */
-#define SST_CSR_RST (0x1 << 1)
-#define SST_CSR_SBCS0 (0x1 << 2)
-#define SST_CSR_SBCS1 (0x1 << 3)
-#define SST_CSR_DCS(x) (x << 4)
-#define SST_CSR_DCS_MASK (0x7 << 4)
-#define SST_CSR_STALL (0x1 << 10)
-#define SST_CSR_S0IOCS (0x1 << 21)
-#define SST_CSR_S1IOCS (0x1 << 23)
-#define SST_CSR_LPCS (0x1 << 31)
-#define SST_CSR_24MHZ_LPCS (SST_CSR_SBCS0 | SST_CSR_SBCS1 | SST_CSR_LPCS)
-#define SST_CSR_24MHZ_NO_LPCS (SST_CSR_SBCS0 | SST_CSR_SBCS1)
-#define SST_BYT_CSR_RST (0x1 << 0)
-#define SST_BYT_CSR_VECTOR_SEL (0x1 << 1)
-#define SST_BYT_CSR_STALL (0x1 << 2)
-#define SST_BYT_CSR_PWAITMODE (0x1 << 3)
-
-/* ISRX / ISC */
-#define SST_ISRX_BUSY (0x1 << 1)
-#define SST_ISRX_DONE (0x1 << 0)
-#define SST_BYT_ISRX_REQUEST (0x1 << 1)
-
-/* ISRD / ISD */
-#define SST_ISRD_BUSY (0x1 << 1)
-#define SST_ISRD_DONE (0x1 << 0)
-
-/* IMRX / IMC */
-#define SST_IMRX_BUSY (0x1 << 1)
-#define SST_IMRX_DONE (0x1 << 0)
-#define SST_BYT_IMRX_REQUEST (0x1 << 1)
-
-/* IMRD / IMD */
-#define SST_IMRD_DONE (0x1 << 0)
-#define SST_IMRD_BUSY (0x1 << 1)
-#define SST_IMRD_SSP0 (0x1 << 16)
-#define SST_IMRD_DMAC0 (0x1 << 21)
-#define SST_IMRD_DMAC1 (0x1 << 22)
-#define SST_IMRD_DMAC (SST_IMRD_DMAC0 | SST_IMRD_DMAC1)
-
-/* IPCX / IPCC */
-#define SST_IPCX_DONE (0x1 << 30)
-#define SST_IPCX_BUSY (0x1 << 31)
-#define SST_BYT_IPCX_DONE ((u64)0x1 << 62)
-#define SST_BYT_IPCX_BUSY ((u64)0x1 << 63)
-
-/* IPCD */
-#define SST_IPCD_DONE (0x1 << 30)
-#define SST_IPCD_BUSY (0x1 << 31)
-#define SST_BYT_IPCD_DONE ((u64)0x1 << 62)
-#define SST_BYT_IPCD_BUSY ((u64)0x1 << 63)
-
-/* CLKCTL */
-#define SST_CLKCTL_SMOS(x) (x << 24)
-#define SST_CLKCTL_MASK (3 << 24)
-#define SST_CLKCTL_DCPLCG (1 << 18)
-#define SST_CLKCTL_SCOE1 (1 << 17)
-#define SST_CLKCTL_SCOE0 (1 << 16)
-
-/* CSR2 / CS2 */
-#define SST_CSR2_SDFD_SSP0 (1 << 1)
-#define SST_CSR2_SDFD_SSP1 (1 << 2)
-
-/* LTRC */
-#define SST_LTRC_VAL(x) (x << 0)
-
-/* HMDC */
-#define SST_HMDC_HDDA0(x) (x << 0)
-#define SST_HMDC_HDDA1(x) (x << 7)
-#define SST_HMDC_HDDA_E0_CH0 1
-#define SST_HMDC_HDDA_E0_CH1 2
-#define SST_HMDC_HDDA_E0_CH2 4
-#define SST_HMDC_HDDA_E0_CH3 8
-#define SST_HMDC_HDDA_E1_CH0 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH0)
-#define SST_HMDC_HDDA_E1_CH1 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH1)
-#define SST_HMDC_HDDA_E1_CH2 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH2)
-#define SST_HMDC_HDDA_E1_CH3 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH3)
-#define SST_HMDC_HDDA_E0_ALLCH (SST_HMDC_HDDA_E0_CH0 | SST_HMDC_HDDA_E0_CH1 | \
- SST_HMDC_HDDA_E0_CH2 | SST_HMDC_HDDA_E0_CH3)
-#define SST_HMDC_HDDA_E1_ALLCH (SST_HMDC_HDDA_E1_CH0 | SST_HMDC_HDDA_E1_CH1 | \
- SST_HMDC_HDDA_E1_CH2 | SST_HMDC_HDDA_E1_CH3)
-
-
-/* SST Vendor Defined Registers and bits */
-#define SST_VDRTCTL0 0xa0
-#define SST_VDRTCTL1 0xa4
-#define SST_VDRTCTL2 0xa8
-#define SST_VDRTCTL3 0xaC
-
-/* VDRTCTL0 */
-#define SST_VDRTCL0_D3PGD (1 << 0)
-#define SST_VDRTCL0_D3SRAMPGD (1 << 1)
-#define SST_VDRTCL0_DSRAMPGE_SHIFT 12
-#define SST_VDRTCL0_DSRAMPGE_MASK (0xfffff << SST_VDRTCL0_DSRAMPGE_SHIFT)
-#define SST_VDRTCL0_ISRAMPGE_SHIFT 2
-#define SST_VDRTCL0_ISRAMPGE_MASK (0x3ff << SST_VDRTCL0_ISRAMPGE_SHIFT)
-
-/* VDRTCTL2 */
-#define SST_VDRTCL2_DCLCGE (1 << 1)
-#define SST_VDRTCL2_DTCGE (1 << 10)
-#define SST_VDRTCL2_APLLSE_MASK (1 << 31)
-
-/* PMCS */
-#define SST_PMCS 0x84
-#define SST_PMCS_PS_MASK 0x3
-
-struct sst_dsp;
-
-/*
- * SST Device.
- *
- * This structure is populated by the SST core driver.
- */
-struct sst_dsp_device {
- /* Mandatory fields */
- struct sst_ops *ops;
- irqreturn_t (*thread)(int irq, void *context);
- void *thread_context;
-};
-
-/*
- * SST Platform Data.
- */
-struct sst_pdata {
- /* ACPI data */
- u32 lpe_base;
- u32 lpe_size;
- u32 pcicfg_base;
- u32 pcicfg_size;
- u32 fw_base;
- u32 fw_size;
- int irq;
-
- /* Firmware */
- const struct firmware *fw;
-
- /* DMA */
- u32 dma_base;
- u32 dma_size;
- int dma_engine;
- struct device *dma_dev;
-
- /* DSP */
- u32 id;
- void *dsp;
-};
-
-/* Initialization */
-struct sst_dsp *sst_dsp_new(struct device *dev,
- struct sst_dsp_device *sst_dev, struct sst_pdata *pdata);
-void sst_dsp_free(struct sst_dsp *sst);
-
-/* SHIM Read / Write */
-void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value);
-u32 sst_dsp_shim_read(struct sst_dsp *sst, u32 offset);
-int sst_dsp_shim_update_bits(struct sst_dsp *sst, u32 offset,
- u32 mask, u32 value);
-void sst_dsp_shim_write64(struct sst_dsp *sst, u32 offset, u64 value);
-u64 sst_dsp_shim_read64(struct sst_dsp *sst, u32 offset);
-int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset,
- u64 mask, u64 value);
-
-/* SHIM Read / Write Unlocked for callers already holding sst lock */
-void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value);
-u32 sst_dsp_shim_read_unlocked(struct sst_dsp *sst, u32 offset);
-int sst_dsp_shim_update_bits_unlocked(struct sst_dsp *sst, u32 offset,
- u32 mask, u32 value);
-void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value);
-u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset);
-int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset,
- u64 mask, u64 value);
-
-/* Internal generic low-level SST IO functions - can be overidden */
-void sst_shim32_write(void __iomem *addr, u32 offset, u32 value);
-u32 sst_shim32_read(void __iomem *addr, u32 offset);
-void sst_shim32_write64(void __iomem *addr, u32 offset, u64 value);
-u64 sst_shim32_read64(void __iomem *addr, u32 offset);
-void sst_memcpy_toio_32(struct sst_dsp *sst,
- void __iomem *dest, void *src, size_t bytes);
-void sst_memcpy_fromio_32(struct sst_dsp *sst,
- void *dest, void __iomem *src, size_t bytes);
-
-/* DSP reset & boot */
-void sst_dsp_reset(struct sst_dsp *sst);
-int sst_dsp_boot(struct sst_dsp *sst);
-int sst_dsp_wake(struct sst_dsp *sst);
-void sst_dsp_sleep(struct sst_dsp *sst);
-void sst_dsp_stall(struct sst_dsp *sst);
-
-/* DMA */
-int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id);
-void sst_dsp_dma_put_channel(struct sst_dsp *dsp);
-int sst_dsp_dma_copyfrom(struct sst_dsp *sst, dma_addr_t dest_addr,
- dma_addr_t src_addr, size_t size);
-int sst_dsp_dma_copyto(struct sst_dsp *sst, dma_addr_t dest_addr,
- dma_addr_t src_addr, size_t size);
-
-/* Msg IO */
-void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg);
-u32 sst_dsp_ipc_msg_rx(struct sst_dsp *dsp);
-
-/* Mailbox management */
-int sst_dsp_mailbox_init(struct sst_dsp *dsp, u32 inbox_offset,
- size_t inbox_size, u32 outbox_offset, size_t outbox_size);
-void sst_dsp_inbox_write(struct sst_dsp *dsp, void *message, size_t bytes);
-void sst_dsp_inbox_read(struct sst_dsp *dsp, void *message, size_t bytes);
-void sst_dsp_outbox_write(struct sst_dsp *dsp, void *message, size_t bytes);
-void sst_dsp_outbox_read(struct sst_dsp *dsp, void *message, size_t bytes);
-void sst_dsp_mailbox_dump(struct sst_dsp *dsp, size_t bytes);
-
-/* Debug */
-void sst_dsp_dump(struct sst_dsp *sst);
-
-#endif
+++ /dev/null
-/*
- * Intel SST Firmware Loader
- *
- * Copyright (C) 2013, Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/firmware.h>
-#include <linux/export.h>
-#include <linux/platform_device.h>
-#include <linux/dma-mapping.h>
-#include <linux/dmaengine.h>
-#include <linux/pci.h>
-#include <linux/acpi.h>
-
-/* supported DMA engine drivers */
-#include <linux/platform_data/dma-dw.h>
-#include <linux/dma/dw.h>
-
-#include <asm/page.h>
-#include <asm/pgtable.h>
-
-#include "sst-dsp.h"
-#include "sst-dsp-priv.h"
-
-#define SST_DMA_RESOURCES 2
-#define SST_DSP_DMA_MAX_BURST 0x3
-#define SST_HSW_BLOCK_ANY 0xffffffff
-
-#define SST_HSW_MASK_DMA_ADDR_DSP 0xfff00000
-
-struct sst_dma {
- struct sst_dsp *sst;
-
- struct dw_dma_chip *chip;
-
- struct dma_async_tx_descriptor *desc;
- struct dma_chan *ch;
-};
-
-static inline void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes)
-{
- /* __iowrite32_copy use 32bit size values so divide by 4 */
- __iowrite32_copy((void *)dest, src, bytes/4);
-}
-
-static void sst_dma_transfer_complete(void *arg)
-{
- struct sst_dsp *sst = (struct sst_dsp *)arg;
-
- dev_dbg(sst->dev, "DMA: callback\n");
-}
-
-static int sst_dsp_dma_copy(struct sst_dsp *sst, dma_addr_t dest_addr,
- dma_addr_t src_addr, size_t size)
-{
- struct dma_async_tx_descriptor *desc;
- struct sst_dma *dma = sst->dma;
-
- if (dma->ch == NULL) {
- dev_err(sst->dev, "error: no DMA channel\n");
- return -ENODEV;
- }
-
- dev_dbg(sst->dev, "DMA: src: 0x%lx dest 0x%lx size %zu\n",
- (unsigned long)src_addr, (unsigned long)dest_addr, size);
-
- desc = dma->ch->device->device_prep_dma_memcpy(dma->ch, dest_addr,
- src_addr, size, DMA_CTRL_ACK);
- if (!desc){
- dev_err(sst->dev, "error: dma prep memcpy failed\n");
- return -EINVAL;
- }
-
- desc->callback = sst_dma_transfer_complete;
- desc->callback_param = sst;
-
- desc->tx_submit(desc);
- dma_wait_for_async_tx(desc);
-
- return 0;
-}
-
-/* copy to DSP */
-int sst_dsp_dma_copyto(struct sst_dsp *sst, dma_addr_t dest_addr,
- dma_addr_t src_addr, size_t size)
-{
- return sst_dsp_dma_copy(sst, dest_addr | SST_HSW_MASK_DMA_ADDR_DSP,
- src_addr, size);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_dma_copyto);
-
-/* copy from DSP */
-int sst_dsp_dma_copyfrom(struct sst_dsp *sst, dma_addr_t dest_addr,
- dma_addr_t src_addr, size_t size)
-{
- return sst_dsp_dma_copy(sst, dest_addr,
- src_addr | SST_HSW_MASK_DMA_ADDR_DSP, size);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_dma_copyfrom);
-
-/* remove module from memory - callers hold locks */
-static void block_list_remove(struct sst_dsp *dsp,
- struct list_head *block_list)
-{
- struct sst_mem_block *block, *tmp;
- int err;
-
- /* disable each block */
- list_for_each_entry(block, block_list, module_list) {
-
- if (block->ops && block->ops->disable) {
- err = block->ops->disable(block);
- if (err < 0)
- dev_err(dsp->dev,
- "error: cant disable block %d:%d\n",
- block->type, block->index);
- }
- }
-
- /* mark each block as free */
- list_for_each_entry_safe(block, tmp, block_list, module_list) {
- list_del(&block->module_list);
- list_move(&block->list, &dsp->free_block_list);
- dev_dbg(dsp->dev, "block freed %d:%d at offset 0x%x\n",
- block->type, block->index, block->offset);
- }
-}
-
-/* prepare the memory block to receive data from host - callers hold locks */
-static int block_list_prepare(struct sst_dsp *dsp,
- struct list_head *block_list)
-{
- struct sst_mem_block *block;
- int ret = 0;
-
- /* enable each block so that's it'e ready for data */
- list_for_each_entry(block, block_list, module_list) {
-
- if (block->ops && block->ops->enable && !block->users) {
- ret = block->ops->enable(block);
- if (ret < 0) {
- dev_err(dsp->dev,
- "error: cant disable block %d:%d\n",
- block->type, block->index);
- goto err;
- }
- }
- }
- return ret;
-
-err:
- list_for_each_entry(block, block_list, module_list) {
- if (block->ops && block->ops->disable)
- block->ops->disable(block);
- }
- return ret;
-}
-
-static struct dw_dma_platform_data dw_pdata = {
- .is_private = 1,
- .chan_allocation_order = CHAN_ALLOCATION_ASCENDING,
- .chan_priority = CHAN_PRIORITY_ASCENDING,
-};
-
-static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem,
- int irq)
-{
- struct dw_dma_chip *chip;
- int err;
-
- chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
- if (!chip)
- return ERR_PTR(-ENOMEM);
-
- chip->irq = irq;
- chip->regs = devm_ioremap_resource(dev, mem);
- if (IS_ERR(chip->regs))
- return ERR_CAST(chip->regs);
-
- err = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(31));
- if (err)
- return ERR_PTR(err);
-
- chip->dev = dev;
- err = dw_dma_probe(chip, &dw_pdata);
- if (err)
- return ERR_PTR(err);
-
- return chip;
-}
-
-static void dw_remove(struct dw_dma_chip *chip)
-{
- dw_dma_remove(chip);
-}
-
-static bool dma_chan_filter(struct dma_chan *chan, void *param)
-{
- struct sst_dsp *dsp = (struct sst_dsp *)param;
-
- return chan->device->dev == dsp->dma_dev;
-}
-
-int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id)
-{
- struct sst_dma *dma = dsp->dma;
- struct dma_slave_config slave;
- dma_cap_mask_t mask;
- int ret;
-
- /* The Intel MID DMA engine driver needs the slave config set but
- * Synopsis DMA engine driver safely ignores the slave config */
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
- dma_cap_set(DMA_MEMCPY, mask);
-
- dma->ch = dma_request_channel(mask, dma_chan_filter, dsp);
- if (dma->ch == NULL) {
- dev_err(dsp->dev, "error: DMA request channel failed\n");
- return -EIO;
- }
-
- memset(&slave, 0, sizeof(slave));
- slave.direction = DMA_MEM_TO_DEV;
- slave.src_addr_width =
- slave.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- slave.src_maxburst = slave.dst_maxburst = SST_DSP_DMA_MAX_BURST;
-
- ret = dmaengine_slave_config(dma->ch, &slave);
- if (ret) {
- dev_err(dsp->dev, "error: unable to set DMA slave config %d\n",
- ret);
- dma_release_channel(dma->ch);
- dma->ch = NULL;
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_dma_get_channel);
-
-void sst_dsp_dma_put_channel(struct sst_dsp *dsp)
-{
- struct sst_dma *dma = dsp->dma;
-
- if (!dma->ch)
- return;
-
- dma_release_channel(dma->ch);
- dma->ch = NULL;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_dma_put_channel);
-
-int sst_dma_new(struct sst_dsp *sst)
-{
- struct sst_pdata *sst_pdata = sst->pdata;
- struct sst_dma *dma;
- struct resource mem;
- const char *dma_dev_name;
- int ret = 0;
-
- /* configure the correct platform data for whatever DMA engine
- * is attached to the ADSP IP. */
- switch (sst->pdata->dma_engine) {
- case SST_DMA_TYPE_DW:
- dma_dev_name = "dw_dmac";
- break;
- case SST_DMA_TYPE_MID:
- dma_dev_name = "Intel MID DMA";
- break;
- default:
- dev_err(sst->dev, "error: invalid DMA engine %d\n",
- sst->pdata->dma_engine);
- return -EINVAL;
- }
-
- dma = devm_kzalloc(sst->dev, sizeof(struct sst_dma), GFP_KERNEL);
- if (!dma)
- return -ENOMEM;
-
- dma->sst = sst;
-
- memset(&mem, 0, sizeof(mem));
-
- mem.start = sst->addr.lpe_base + sst_pdata->dma_base;
- mem.end = sst->addr.lpe_base + sst_pdata->dma_base + sst_pdata->dma_size - 1;
- mem.flags = IORESOURCE_MEM;
-
- /* now register DMA engine device */
- dma->chip = dw_probe(sst->dma_dev, &mem, sst_pdata->irq);
- if (IS_ERR(dma->chip)) {
- dev_err(sst->dev, "error: DMA device register failed\n");
- ret = PTR_ERR(dma->chip);
- goto err_dma_dev;
- }
-
- sst->dma = dma;
- sst->fw_use_dma = true;
- return 0;
-
-err_dma_dev:
- devm_kfree(sst->dev, dma);
- return ret;
-}
-EXPORT_SYMBOL(sst_dma_new);
-
-void sst_dma_free(struct sst_dma *dma)
-{
-
- if (dma == NULL)
- return;
-
- if (dma->ch)
- dma_release_channel(dma->ch);
-
- if (dma->chip)
- dw_remove(dma->chip);
-
-}
-EXPORT_SYMBOL(sst_dma_free);
-
-/* create new generic firmware object */
-struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
- const struct firmware *fw, void *private)
-{
- struct sst_fw *sst_fw;
- int err;
-
- if (!dsp->ops->parse_fw)
- return NULL;
-
- sst_fw = kzalloc(sizeof(*sst_fw), GFP_KERNEL);
- if (sst_fw == NULL)
- return NULL;
-
- sst_fw->dsp = dsp;
- sst_fw->private = private;
- sst_fw->size = fw->size;
-
- /* allocate DMA buffer to store FW data */
- sst_fw->dma_buf = dma_alloc_coherent(dsp->dma_dev, sst_fw->size,
- &sst_fw->dmable_fw_paddr, GFP_DMA | GFP_KERNEL);
- if (!sst_fw->dma_buf) {
- dev_err(dsp->dev, "error: DMA alloc failed\n");
- kfree(sst_fw);
- return NULL;
- }
-
- /* copy FW data to DMA-able memory */
- memcpy((void *)sst_fw->dma_buf, (void *)fw->data, fw->size);
-
- if (dsp->fw_use_dma) {
- err = sst_dsp_dma_get_channel(dsp, 0);
- if (err < 0)
- goto chan_err;
- }
-
- /* call core specific FW paser to load FW data into DSP */
- err = dsp->ops->parse_fw(sst_fw);
- if (err < 0) {
- dev_err(dsp->dev, "error: parse fw failed %d\n", err);
- goto parse_err;
- }
-
- if (dsp->fw_use_dma)
- sst_dsp_dma_put_channel(dsp);
-
- mutex_lock(&dsp->mutex);
- list_add(&sst_fw->list, &dsp->fw_list);
- mutex_unlock(&dsp->mutex);
-
- return sst_fw;
-
-parse_err:
- if (dsp->fw_use_dma)
- sst_dsp_dma_put_channel(dsp);
-chan_err:
- dma_free_coherent(dsp->dma_dev, sst_fw->size,
- sst_fw->dma_buf,
- sst_fw->dmable_fw_paddr);
- sst_fw->dma_buf = NULL;
- kfree(sst_fw);
- return NULL;
-}
-EXPORT_SYMBOL_GPL(sst_fw_new);
-
-int sst_fw_reload(struct sst_fw *sst_fw)
-{
- struct sst_dsp *dsp = sst_fw->dsp;
- int ret;
-
- dev_dbg(dsp->dev, "reloading firmware\n");
-
- /* call core specific FW paser to load FW data into DSP */
- ret = dsp->ops->parse_fw(sst_fw);
- if (ret < 0)
- dev_err(dsp->dev, "error: parse fw failed %d\n", ret);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(sst_fw_reload);
-
-void sst_fw_unload(struct sst_fw *sst_fw)
-{
- struct sst_dsp *dsp = sst_fw->dsp;
- struct sst_module *module, *mtmp;
- struct sst_module_runtime *runtime, *rtmp;
-
- dev_dbg(dsp->dev, "unloading firmware\n");
-
- mutex_lock(&dsp->mutex);
-
- /* check module by module */
- list_for_each_entry_safe(module, mtmp, &dsp->module_list, list) {
- if (module->sst_fw == sst_fw) {
-
- /* remove runtime modules */
- list_for_each_entry_safe(runtime, rtmp, &module->runtime_list, list) {
-
- block_list_remove(dsp, &runtime->block_list);
- list_del(&runtime->list);
- kfree(runtime);
- }
-
- /* now remove the module */
- block_list_remove(dsp, &module->block_list);
- list_del(&module->list);
- kfree(module);
- }
- }
-
- /* remove all scratch blocks */
- block_list_remove(dsp, &dsp->scratch_block_list);
-
- mutex_unlock(&dsp->mutex);
-}
-EXPORT_SYMBOL_GPL(sst_fw_unload);
-
-/* free single firmware object */
-void sst_fw_free(struct sst_fw *sst_fw)
-{
- struct sst_dsp *dsp = sst_fw->dsp;
-
- mutex_lock(&dsp->mutex);
- list_del(&sst_fw->list);
- mutex_unlock(&dsp->mutex);
-
- if (sst_fw->dma_buf)
- dma_free_coherent(dsp->dma_dev, sst_fw->size, sst_fw->dma_buf,
- sst_fw->dmable_fw_paddr);
- kfree(sst_fw);
-}
-EXPORT_SYMBOL_GPL(sst_fw_free);
-
-/* free all firmware objects */
-void sst_fw_free_all(struct sst_dsp *dsp)
-{
- struct sst_fw *sst_fw, *t;
-
- mutex_lock(&dsp->mutex);
- list_for_each_entry_safe(sst_fw, t, &dsp->fw_list, list) {
-
- list_del(&sst_fw->list);
- dma_free_coherent(dsp->dev, sst_fw->size, sst_fw->dma_buf,
- sst_fw->dmable_fw_paddr);
- kfree(sst_fw);
- }
- mutex_unlock(&dsp->mutex);
-}
-EXPORT_SYMBOL_GPL(sst_fw_free_all);
-
-/* create a new SST generic module from FW template */
-struct sst_module *sst_module_new(struct sst_fw *sst_fw,
- struct sst_module_template *template, void *private)
-{
- struct sst_dsp *dsp = sst_fw->dsp;
- struct sst_module *sst_module;
-
- sst_module = kzalloc(sizeof(*sst_module), GFP_KERNEL);
- if (sst_module == NULL)
- return NULL;
-
- sst_module->id = template->id;
- sst_module->dsp = dsp;
- sst_module->sst_fw = sst_fw;
- sst_module->scratch_size = template->scratch_size;
- sst_module->persistent_size = template->persistent_size;
- sst_module->entry = template->entry;
-
- INIT_LIST_HEAD(&sst_module->block_list);
- INIT_LIST_HEAD(&sst_module->runtime_list);
-
- mutex_lock(&dsp->mutex);
- list_add(&sst_module->list, &dsp->module_list);
- mutex_unlock(&dsp->mutex);
-
- return sst_module;
-}
-EXPORT_SYMBOL_GPL(sst_module_new);
-
-/* free firmware module and remove from available list */
-void sst_module_free(struct sst_module *sst_module)
-{
- struct sst_dsp *dsp = sst_module->dsp;
-
- mutex_lock(&dsp->mutex);
- list_del(&sst_module->list);
- mutex_unlock(&dsp->mutex);
-
- kfree(sst_module);
-}
-EXPORT_SYMBOL_GPL(sst_module_free);
-
-struct sst_module_runtime *sst_module_runtime_new(struct sst_module *module,
- int id, void *private)
-{
- struct sst_dsp *dsp = module->dsp;
- struct sst_module_runtime *runtime;
-
- runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
- if (runtime == NULL)
- return NULL;
-
- runtime->id = id;
- runtime->dsp = dsp;
- runtime->module = module;
- INIT_LIST_HEAD(&runtime->block_list);
-
- mutex_lock(&dsp->mutex);
- list_add(&runtime->list, &module->runtime_list);
- mutex_unlock(&dsp->mutex);
-
- return runtime;
-}
-EXPORT_SYMBOL_GPL(sst_module_runtime_new);
-
-void sst_module_runtime_free(struct sst_module_runtime *runtime)
-{
- struct sst_dsp *dsp = runtime->dsp;
-
- mutex_lock(&dsp->mutex);
- list_del(&runtime->list);
- mutex_unlock(&dsp->mutex);
-
- kfree(runtime);
-}
-EXPORT_SYMBOL_GPL(sst_module_runtime_free);
-
-static struct sst_mem_block *find_block(struct sst_dsp *dsp,
- struct sst_block_allocator *ba)
-{
- struct sst_mem_block *block;
-
- list_for_each_entry(block, &dsp->free_block_list, list) {
- if (block->type == ba->type && block->offset == ba->offset)
- return block;
- }
-
- return NULL;
-}
-
-/* Block allocator must be on block boundary */
-static int block_alloc_contiguous(struct sst_dsp *dsp,
- struct sst_block_allocator *ba, struct list_head *block_list)
-{
- struct list_head tmp = LIST_HEAD_INIT(tmp);
- struct sst_mem_block *block;
- u32 block_start = SST_HSW_BLOCK_ANY;
- int size = ba->size, offset = ba->offset;
-
- while (ba->size > 0) {
-
- block = find_block(dsp, ba);
- if (!block) {
- list_splice(&tmp, &dsp->free_block_list);
-
- ba->size = size;
- ba->offset = offset;
- return -ENOMEM;
- }
-
- list_move_tail(&block->list, &tmp);
- ba->offset += block->size;
- ba->size -= block->size;
- }
- ba->size = size;
- ba->offset = offset;
-
- list_for_each_entry(block, &tmp, list) {
-
- if (block->offset < block_start)
- block_start = block->offset;
-
- list_add(&block->module_list, block_list);
-
- dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n",
- block->type, block->index, block->offset);
- }
-
- list_splice(&tmp, &dsp->used_block_list);
- return 0;
-}
-
-/* allocate first free DSP blocks for data - callers hold locks */
-static int block_alloc(struct sst_dsp *dsp, struct sst_block_allocator *ba,
- struct list_head *block_list)
-{
- struct sst_mem_block *block, *tmp;
- int ret = 0;
-
- if (ba->size == 0)
- return 0;
-
- /* find first free whole blocks that can hold module */
- list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
-
- /* ignore blocks with wrong type */
- if (block->type != ba->type)
- continue;
-
- if (ba->size > block->size)
- continue;
-
- ba->offset = block->offset;
- block->bytes_used = ba->size % block->size;
- list_add(&block->module_list, block_list);
- list_move(&block->list, &dsp->used_block_list);
- dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n",
- block->type, block->index, block->offset);
- return 0;
- }
-
- /* then find free multiple blocks that can hold module */
- list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
-
- /* ignore blocks with wrong type */
- if (block->type != ba->type)
- continue;
-
- /* do we span > 1 blocks */
- if (ba->size > block->size) {
-
- /* align ba to block boundary */
- ba->offset = block->offset;
-
- ret = block_alloc_contiguous(dsp, ba, block_list);
- if (ret == 0)
- return ret;
-
- }
- }
-
- /* not enough free block space */
- return -ENOMEM;
-}
-
-int sst_alloc_blocks(struct sst_dsp *dsp, struct sst_block_allocator *ba,
- struct list_head *block_list)
-{
- int ret;
-
- dev_dbg(dsp->dev, "block request 0x%x bytes at offset 0x%x type %d\n",
- ba->size, ba->offset, ba->type);
-
- mutex_lock(&dsp->mutex);
-
- ret = block_alloc(dsp, ba, block_list);
- if (ret < 0) {
- dev_err(dsp->dev, "error: can't alloc blocks %d\n", ret);
- goto out;
- }
-
- /* prepare DSP blocks for module usage */
- ret = block_list_prepare(dsp, block_list);
- if (ret < 0)
- dev_err(dsp->dev, "error: prepare failed\n");
-
-out:
- mutex_unlock(&dsp->mutex);
- return ret;
-}
-EXPORT_SYMBOL_GPL(sst_alloc_blocks);
-
-int sst_free_blocks(struct sst_dsp *dsp, struct list_head *block_list)
-{
- mutex_lock(&dsp->mutex);
- block_list_remove(dsp, block_list);
- mutex_unlock(&dsp->mutex);
- return 0;
-}
-EXPORT_SYMBOL_GPL(sst_free_blocks);
-
-/* allocate memory blocks for static module addresses - callers hold locks */
-static int block_alloc_fixed(struct sst_dsp *dsp, struct sst_block_allocator *ba,
- struct list_head *block_list)
-{
- struct sst_mem_block *block, *tmp;
- struct sst_block_allocator ba_tmp = *ba;
- u32 end = ba->offset + ba->size, block_end;
- int err;
-
- /* only IRAM/DRAM blocks are managed */
- if (ba->type != SST_MEM_IRAM && ba->type != SST_MEM_DRAM)
- return 0;
-
- /* are blocks already attached to this module */
- list_for_each_entry_safe(block, tmp, block_list, module_list) {
-
- /* ignore blocks with wrong type */
- if (block->type != ba->type)
- continue;
-
- block_end = block->offset + block->size;
-
- /* find block that holds section */
- if (ba->offset >= block->offset && end <= block_end)
- return 0;
-
- /* does block span more than 1 section */
- if (ba->offset >= block->offset && ba->offset < block_end) {
-
- /* align ba to block boundary */
- ba_tmp.size -= block_end - ba->offset;
- ba_tmp.offset = block_end;
- err = block_alloc_contiguous(dsp, &ba_tmp, block_list);
- if (err < 0)
- return -ENOMEM;
-
- /* module already owns blocks */
- return 0;
- }
- }
-
- /* find first free blocks that can hold section in free list */
- list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
- block_end = block->offset + block->size;
-
- /* ignore blocks with wrong type */
- if (block->type != ba->type)
- continue;
-
- /* find block that holds section */
- if (ba->offset >= block->offset && end <= block_end) {
-
- /* add block */
- list_move(&block->list, &dsp->used_block_list);
- list_add(&block->module_list, block_list);
- dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n",
- block->type, block->index, block->offset);
- return 0;
- }
-
- /* does block span more than 1 section */
- if (ba->offset >= block->offset && ba->offset < block_end) {
-
- /* add block */
- list_move(&block->list, &dsp->used_block_list);
- list_add(&block->module_list, block_list);
- /* align ba to block boundary */
- ba_tmp.size -= block_end - ba->offset;
- ba_tmp.offset = block_end;
-
- err = block_alloc_contiguous(dsp, &ba_tmp, block_list);
- if (err < 0)
- return -ENOMEM;
-
- return 0;
- }
- }
-
- return -ENOMEM;
-}
-
-/* Load fixed module data into DSP memory blocks */
-int sst_module_alloc_blocks(struct sst_module *module)
-{
- struct sst_dsp *dsp = module->dsp;
- struct sst_fw *sst_fw = module->sst_fw;
- struct sst_block_allocator ba;
- int ret;
-
- memset(&ba, 0, sizeof(ba));
- ba.size = module->size;
- ba.type = module->type;
- ba.offset = module->offset;
-
- dev_dbg(dsp->dev, "block request 0x%x bytes at offset 0x%x type %d\n",
- ba.size, ba.offset, ba.type);
-
- mutex_lock(&dsp->mutex);
-
- /* alloc blocks that includes this section */
- ret = block_alloc_fixed(dsp, &ba, &module->block_list);
- if (ret < 0) {
- dev_err(dsp->dev,
- "error: no free blocks for section at offset 0x%x size 0x%x\n",
- module->offset, module->size);
- mutex_unlock(&dsp->mutex);
- return -ENOMEM;
- }
-
- /* prepare DSP blocks for module copy */
- ret = block_list_prepare(dsp, &module->block_list);
- if (ret < 0) {
- dev_err(dsp->dev, "error: fw module prepare failed\n");
- goto err;
- }
-
- /* copy partial module data to blocks */
- if (dsp->fw_use_dma) {
- ret = sst_dsp_dma_copyto(dsp,
- dsp->addr.lpe_base + module->offset,
- sst_fw->dmable_fw_paddr + module->data_offset,
- module->size);
- if (ret < 0) {
- dev_err(dsp->dev, "error: module copy failed\n");
- goto err;
- }
- } else
- sst_memcpy32(dsp->addr.lpe + module->offset, module->data,
- module->size);
-
- mutex_unlock(&dsp->mutex);
- return ret;
-
-err:
- block_list_remove(dsp, &module->block_list);
- mutex_unlock(&dsp->mutex);
- return ret;
-}
-EXPORT_SYMBOL_GPL(sst_module_alloc_blocks);
-
-/* Unload entire module from DSP memory */
-int sst_module_free_blocks(struct sst_module *module)
-{
- struct sst_dsp *dsp = module->dsp;
-
- mutex_lock(&dsp->mutex);
- block_list_remove(dsp, &module->block_list);
- mutex_unlock(&dsp->mutex);
- return 0;
-}
-EXPORT_SYMBOL_GPL(sst_module_free_blocks);
-
-int sst_module_runtime_alloc_blocks(struct sst_module_runtime *runtime,
- int offset)
-{
- struct sst_dsp *dsp = runtime->dsp;
- struct sst_module *module = runtime->module;
- struct sst_block_allocator ba;
- int ret;
-
- if (module->persistent_size == 0)
- return 0;
-
- memset(&ba, 0, sizeof(ba));
- ba.size = module->persistent_size;
- ba.type = SST_MEM_DRAM;
-
- mutex_lock(&dsp->mutex);
-
- /* do we need to allocate at a fixed address ? */
- if (offset != 0) {
-
- ba.offset = offset;
-
- dev_dbg(dsp->dev, "persistent fixed block request 0x%x bytes type %d offset 0x%x\n",
- ba.size, ba.type, ba.offset);
-
- /* alloc blocks that includes this section */
- ret = block_alloc_fixed(dsp, &ba, &runtime->block_list);
-
- } else {
- dev_dbg(dsp->dev, "persistent block request 0x%x bytes type %d\n",
- ba.size, ba.type);
-
- /* alloc blocks that includes this section */
- ret = block_alloc(dsp, &ba, &runtime->block_list);
- }
- if (ret < 0) {
- dev_err(dsp->dev,
- "error: no free blocks for runtime module size 0x%x\n",
- module->persistent_size);
- mutex_unlock(&dsp->mutex);
- return -ENOMEM;
- }
- runtime->persistent_offset = ba.offset;
-
- /* prepare DSP blocks for module copy */
- ret = block_list_prepare(dsp, &runtime->block_list);
- if (ret < 0) {
- dev_err(dsp->dev, "error: runtime block prepare failed\n");
- goto err;
- }
-
- mutex_unlock(&dsp->mutex);
- return ret;
-
-err:
- block_list_remove(dsp, &module->block_list);
- mutex_unlock(&dsp->mutex);
- return ret;
-}
-EXPORT_SYMBOL_GPL(sst_module_runtime_alloc_blocks);
-
-int sst_module_runtime_free_blocks(struct sst_module_runtime *runtime)
-{
- struct sst_dsp *dsp = runtime->dsp;
-
- mutex_lock(&dsp->mutex);
- block_list_remove(dsp, &runtime->block_list);
- mutex_unlock(&dsp->mutex);
- return 0;
-}
-EXPORT_SYMBOL_GPL(sst_module_runtime_free_blocks);
-
-int sst_module_runtime_save(struct sst_module_runtime *runtime,
- struct sst_module_runtime_context *context)
-{
- struct sst_dsp *dsp = runtime->dsp;
- struct sst_module *module = runtime->module;
- int ret = 0;
-
- dev_dbg(dsp->dev, "saving runtime %d memory at 0x%x size 0x%x\n",
- runtime->id, runtime->persistent_offset,
- module->persistent_size);
-
- context->buffer = dma_alloc_coherent(dsp->dma_dev,
- module->persistent_size,
- &context->dma_buffer, GFP_DMA | GFP_KERNEL);
- if (!context->buffer) {
- dev_err(dsp->dev, "error: DMA context alloc failed\n");
- return -ENOMEM;
- }
-
- mutex_lock(&dsp->mutex);
-
- if (dsp->fw_use_dma) {
-
- ret = sst_dsp_dma_get_channel(dsp, 0);
- if (ret < 0)
- goto err;
-
- ret = sst_dsp_dma_copyfrom(dsp, context->dma_buffer,
- dsp->addr.lpe_base + runtime->persistent_offset,
- module->persistent_size);
- sst_dsp_dma_put_channel(dsp);
- if (ret < 0) {
- dev_err(dsp->dev, "error: context copy failed\n");
- goto err;
- }
- } else
- sst_memcpy32(context->buffer, dsp->addr.lpe +
- runtime->persistent_offset,
- module->persistent_size);
-
-err:
- mutex_unlock(&dsp->mutex);
- return ret;
-}
-EXPORT_SYMBOL_GPL(sst_module_runtime_save);
-
-int sst_module_runtime_restore(struct sst_module_runtime *runtime,
- struct sst_module_runtime_context *context)
-{
- struct sst_dsp *dsp = runtime->dsp;
- struct sst_module *module = runtime->module;
- int ret = 0;
-
- dev_dbg(dsp->dev, "restoring runtime %d memory at 0x%x size 0x%x\n",
- runtime->id, runtime->persistent_offset,
- module->persistent_size);
-
- mutex_lock(&dsp->mutex);
-
- if (!context->buffer) {
- dev_info(dsp->dev, "no context buffer need to restore!\n");
- goto err;
- }
-
- if (dsp->fw_use_dma) {
-
- ret = sst_dsp_dma_get_channel(dsp, 0);
- if (ret < 0)
- goto err;
-
- ret = sst_dsp_dma_copyto(dsp,
- dsp->addr.lpe_base + runtime->persistent_offset,
- context->dma_buffer, module->persistent_size);
- sst_dsp_dma_put_channel(dsp);
- if (ret < 0) {
- dev_err(dsp->dev, "error: module copy failed\n");
- goto err;
- }
- } else
- sst_memcpy32(dsp->addr.lpe + runtime->persistent_offset,
- context->buffer, module->persistent_size);
-
- dma_free_coherent(dsp->dma_dev, module->persistent_size,
- context->buffer, context->dma_buffer);
- context->buffer = NULL;
-
-err:
- mutex_unlock(&dsp->mutex);
- return ret;
-}
-EXPORT_SYMBOL_GPL(sst_module_runtime_restore);
-
-/* register a DSP memory block for use with FW based modules */
-struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
- u32 size, enum sst_mem_type type, struct sst_block_ops *ops, u32 index,
- void *private)
-{
- struct sst_mem_block *block;
-
- block = kzalloc(sizeof(*block), GFP_KERNEL);
- if (block == NULL)
- return NULL;
-
- block->offset = offset;
- block->size = size;
- block->index = index;
- block->type = type;
- block->dsp = dsp;
- block->private = private;
- block->ops = ops;
-
- mutex_lock(&dsp->mutex);
- list_add(&block->list, &dsp->free_block_list);
- mutex_unlock(&dsp->mutex);
-
- return block;
-}
-EXPORT_SYMBOL_GPL(sst_mem_block_register);
-
-/* unregister all DSP memory blocks */
-void sst_mem_block_unregister_all(struct sst_dsp *dsp)
-{
- struct sst_mem_block *block, *tmp;
-
- mutex_lock(&dsp->mutex);
-
- /* unregister used blocks */
- list_for_each_entry_safe(block, tmp, &dsp->used_block_list, list) {
- list_del(&block->list);
- kfree(block);
- }
-
- /* unregister free blocks */
- list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
- list_del(&block->list);
- kfree(block);
- }
-
- mutex_unlock(&dsp->mutex);
-}
-EXPORT_SYMBOL_GPL(sst_mem_block_unregister_all);
-
-/* allocate scratch buffer blocks */
-int sst_block_alloc_scratch(struct sst_dsp *dsp)
-{
- struct sst_module *module;
- struct sst_block_allocator ba;
- int ret;
-
- mutex_lock(&dsp->mutex);
-
- /* calculate required scratch size */
- dsp->scratch_size = 0;
- list_for_each_entry(module, &dsp->module_list, list) {
- dev_dbg(dsp->dev, "module %d scratch req 0x%x bytes\n",
- module->id, module->scratch_size);
- if (dsp->scratch_size < module->scratch_size)
- dsp->scratch_size = module->scratch_size;
- }
-
- dev_dbg(dsp->dev, "scratch buffer required is 0x%x bytes\n",
- dsp->scratch_size);
-
- if (dsp->scratch_size == 0) {
- dev_info(dsp->dev, "no modules need scratch buffer\n");
- mutex_unlock(&dsp->mutex);
- return 0;
- }
-
- /* allocate blocks for module scratch buffers */
- dev_dbg(dsp->dev, "allocating scratch blocks\n");
-
- ba.size = dsp->scratch_size;
- ba.type = SST_MEM_DRAM;
-
- /* do we need to allocate at fixed offset */
- if (dsp->scratch_offset != 0) {
-
- dev_dbg(dsp->dev, "block request 0x%x bytes type %d at 0x%x\n",
- ba.size, ba.type, ba.offset);
-
- ba.offset = dsp->scratch_offset;
-
- /* alloc blocks that includes this section */
- ret = block_alloc_fixed(dsp, &ba, &dsp->scratch_block_list);
-
- } else {
- dev_dbg(dsp->dev, "block request 0x%x bytes type %d\n",
- ba.size, ba.type);
-
- ba.offset = 0;
- ret = block_alloc(dsp, &ba, &dsp->scratch_block_list);
- }
- if (ret < 0) {
- dev_err(dsp->dev, "error: can't alloc scratch blocks\n");
- mutex_unlock(&dsp->mutex);
- return ret;
- }
-
- ret = block_list_prepare(dsp, &dsp->scratch_block_list);
- if (ret < 0) {
- dev_err(dsp->dev, "error: scratch block prepare failed\n");
- mutex_unlock(&dsp->mutex);
- return ret;
- }
-
- /* assign the same offset of scratch to each module */
- dsp->scratch_offset = ba.offset;
- mutex_unlock(&dsp->mutex);
- return dsp->scratch_size;
-}
-EXPORT_SYMBOL_GPL(sst_block_alloc_scratch);
-
-/* free all scratch blocks */
-void sst_block_free_scratch(struct sst_dsp *dsp)
-{
- mutex_lock(&dsp->mutex);
- block_list_remove(dsp, &dsp->scratch_block_list);
- mutex_unlock(&dsp->mutex);
-}
-EXPORT_SYMBOL_GPL(sst_block_free_scratch);
-
-/* get a module from it's unique ID */
-struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id)
-{
- struct sst_module *module;
-
- mutex_lock(&dsp->mutex);
-
- list_for_each_entry(module, &dsp->module_list, list) {
- if (module->id == id) {
- mutex_unlock(&dsp->mutex);
- return module;
- }
- }
-
- mutex_unlock(&dsp->mutex);
- return NULL;
-}
-EXPORT_SYMBOL_GPL(sst_module_get_from_id);
-
-struct sst_module_runtime *sst_module_runtime_get_from_id(
- struct sst_module *module, u32 id)
-{
- struct sst_module_runtime *runtime;
- struct sst_dsp *dsp = module->dsp;
-
- mutex_lock(&dsp->mutex);
-
- list_for_each_entry(runtime, &module->runtime_list, list) {
- if (runtime->id == id) {
- mutex_unlock(&dsp->mutex);
- return runtime;
- }
- }
-
- mutex_unlock(&dsp->mutex);
- return NULL;
-}
-EXPORT_SYMBOL_GPL(sst_module_runtime_get_from_id);
-
-/* returns block address in DSP address space */
-u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset,
- enum sst_mem_type type)
-{
- switch (type) {
- case SST_MEM_IRAM:
- return offset - dsp->addr.iram_offset +
- dsp->addr.dsp_iram_offset;
- case SST_MEM_DRAM:
- return offset - dsp->addr.dram_offset +
- dsp->addr.dsp_dram_offset;
- default:
- return 0;
- }
-}
-EXPORT_SYMBOL_GPL(sst_dsp_get_offset);
+++ /dev/null
-/*
- * Intel Haswell SST DSP driver
- *
- * Copyright (C) 2013, Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/delay.h>
-#include <linux/fs.h>
-#include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/sched.h>
-#include <linux/export.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/dma-mapping.h>
-#include <linux/platform_device.h>
-#include <linux/pci.h>
-#include <linux/firmware.h>
-#include <linux/pm_runtime.h>
-
-#include "sst-dsp.h"
-#include "sst-dsp-priv.h"
-#include "sst-haswell-ipc.h"
-
-#include <trace/events/hswadsp.h>
-
-#define SST_HSW_FW_SIGNATURE_SIZE 4
-#define SST_HSW_FW_SIGN "$SST"
-#define SST_HSW_FW_LIB_SIGN "$LIB"
-
-#define SST_WPT_SHIM_OFFSET 0xFB000
-#define SST_LP_SHIM_OFFSET 0xE7000
-#define SST_WPT_IRAM_OFFSET 0xA0000
-#define SST_LP_IRAM_OFFSET 0x80000
-#define SST_WPT_DSP_DRAM_OFFSET 0x400000
-#define SST_WPT_DSP_IRAM_OFFSET 0x00000
-#define SST_LPT_DSP_DRAM_OFFSET 0x400000
-#define SST_LPT_DSP_IRAM_OFFSET 0x00000
-
-#define SST_SHIM_PM_REG 0x84
-
-#define SST_HSW_IRAM 1
-#define SST_HSW_DRAM 2
-#define SST_HSW_REGS 3
-
-struct dma_block_info {
- __le32 type; /* IRAM/DRAM */
- __le32 size; /* Bytes */
- __le32 ram_offset; /* Offset in I/DRAM */
- __le32 rsvd; /* Reserved field */
-} __attribute__((packed));
-
-struct fw_module_info {
- __le32 persistent_size;
- __le32 scratch_size;
-} __attribute__((packed));
-
-struct fw_header {
- unsigned char signature[SST_HSW_FW_SIGNATURE_SIZE]; /* FW signature */
- __le32 file_size; /* size of fw minus this header */
- __le32 modules; /* # of modules */
- __le32 file_format; /* version of header format */
- __le32 reserved[4];
-} __attribute__((packed));
-
-struct fw_module_header {
- unsigned char signature[SST_HSW_FW_SIGNATURE_SIZE]; /* module signature */
- __le32 mod_size; /* size of module */
- __le32 blocks; /* # of blocks */
- __le16 padding;
- __le16 type; /* codec type, pp lib */
- __le32 entry_point;
- struct fw_module_info info;
-} __attribute__((packed));
-
-static void hsw_free(struct sst_dsp *sst);
-
-static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
- struct fw_module_header *module)
-{
- struct dma_block_info *block;
- struct sst_module *mod;
- struct sst_module_template template;
- int count, ret;
- void __iomem *ram;
-
- /* TODO: allowed module types need to be configurable */
- if (module->type != SST_HSW_MODULE_BASE_FW
- && module->type != SST_HSW_MODULE_PCM_SYSTEM
- && module->type != SST_HSW_MODULE_PCM
- && module->type != SST_HSW_MODULE_PCM_REFERENCE
- && module->type != SST_HSW_MODULE_PCM_CAPTURE
- && module->type != SST_HSW_MODULE_LPAL)
- return 0;
-
- dev_dbg(dsp->dev, "new module sign 0x%s size 0x%x blocks 0x%x type 0x%x\n",
- module->signature, module->mod_size,
- module->blocks, module->type);
- dev_dbg(dsp->dev, " entrypoint 0x%x\n", module->entry_point);
- dev_dbg(dsp->dev, " persistent 0x%x scratch 0x%x\n",
- module->info.persistent_size, module->info.scratch_size);
-
- memset(&template, 0, sizeof(template));
- template.id = module->type;
- template.entry = module->entry_point - 4;
- template.persistent_size = module->info.persistent_size;
- template.scratch_size = module->info.scratch_size;
-
- mod = sst_module_new(fw, &template, NULL);
- if (mod == NULL)
- return -ENOMEM;
-
- block = (void *)module + sizeof(*module);
-
- for (count = 0; count < module->blocks; count++) {
-
- if (block->size <= 0) {
- dev_err(dsp->dev,
- "error: block %d size invalid\n", count);
- sst_module_free(mod);
- return -EINVAL;
- }
-
- switch (block->type) {
- case SST_HSW_IRAM:
- ram = dsp->addr.lpe;
- mod->offset =
- block->ram_offset + dsp->addr.iram_offset;
- mod->type = SST_MEM_IRAM;
- break;
- case SST_HSW_DRAM:
- ram = dsp->addr.lpe;
- mod->offset = block->ram_offset;
- mod->type = SST_MEM_DRAM;
- break;
- default:
- dev_err(dsp->dev, "error: bad type 0x%x for block 0x%x\n",
- block->type, count);
- sst_module_free(mod);
- return -EINVAL;
- }
-
- mod->size = block->size;
- mod->data = (void *)block + sizeof(*block);
- mod->data_offset = mod->data - fw->dma_buf;
-
- dev_dbg(dsp->dev, "module block %d type 0x%x "
- "size 0x%x ==> ram %p offset 0x%x\n",
- count, mod->type, block->size, ram,
- block->ram_offset);
-
- ret = sst_module_alloc_blocks(mod);
- if (ret < 0) {
- dev_err(dsp->dev, "error: could not allocate blocks for module %d\n",
- count);
- sst_module_free(mod);
- return ret;
- }
-
- block = (void *)block + sizeof(*block) + block->size;
- }
-
- return 0;
-}
-
-static int hsw_parse_fw_image(struct sst_fw *sst_fw)
-{
- struct fw_header *header;
- struct fw_module_header *module;
- struct sst_dsp *dsp = sst_fw->dsp;
- int ret, count;
-
- /* Read the header information from the data pointer */
- header = (struct fw_header *)sst_fw->dma_buf;
-
- /* verify FW */
- if ((strncmp(header->signature, SST_HSW_FW_SIGN, 4) != 0) ||
- (sst_fw->size != header->file_size + sizeof(*header))) {
- dev_err(dsp->dev, "error: invalid fw sign/filesize mismatch\n");
- return -EINVAL;
- }
-
- dev_dbg(dsp->dev, "header size=0x%x modules=0x%x fmt=0x%x size=%zu\n",
- header->file_size, header->modules,
- header->file_format, sizeof(*header));
-
- /* parse each module */
- module = (void *)sst_fw->dma_buf + sizeof(*header);
- for (count = 0; count < header->modules; count++) {
-
- /* module */
- ret = hsw_parse_module(dsp, sst_fw, module);
- if (ret < 0) {
- dev_err(dsp->dev, "error: invalid module %d\n", count);
- return ret;
- }
- module = (void *)module + sizeof(*module) + module->mod_size;
- }
-
- /* allocate scratch mem regions */
- sst_block_alloc_scratch(dsp);
-
- return 0;
-}
-
-static irqreturn_t hsw_irq(int irq, void *context)
-{
- struct sst_dsp *sst = (struct sst_dsp *) context;
- u32 isr;
- int ret = IRQ_NONE;
-
- spin_lock(&sst->spinlock);
-
- /* Interrupt arrived, check src */
- isr = sst_dsp_shim_read_unlocked(sst, SST_ISRX);
- if (isr & SST_ISRX_DONE) {
- trace_sst_irq_done(isr,
- sst_dsp_shim_read_unlocked(sst, SST_IMRX));
-
- /* Mask Done interrupt before return */
- sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
- SST_IMRX_DONE, SST_IMRX_DONE);
- ret = IRQ_WAKE_THREAD;
- }
-
- if (isr & SST_ISRX_BUSY) {
- trace_sst_irq_busy(isr,
- sst_dsp_shim_read_unlocked(sst, SST_IMRX));
-
- /* Mask Busy interrupt before return */
- sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
- SST_IMRX_BUSY, SST_IMRX_BUSY);
- ret = IRQ_WAKE_THREAD;
- }
-
- spin_unlock(&sst->spinlock);
- return ret;
-}
-
-static void hsw_set_dsp_D3(struct sst_dsp *sst)
-{
- u32 val;
- u32 reg;
-
- /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
- reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
- reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE);
- writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
-
- /* enable power gating and switch off DRAM & IRAM blocks */
- val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
- val |= SST_VDRTCL0_DSRAMPGE_MASK |
- SST_VDRTCL0_ISRAMPGE_MASK;
- val &= ~(SST_VDRTCL0_D3PGD | SST_VDRTCL0_D3SRAMPGD);
- writel(val, sst->addr.pci_cfg + SST_VDRTCTL0);
-
- /* switch off audio PLL */
- val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
- val |= SST_VDRTCL2_APLLSE_MASK;
- writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
-
- /* disable MCLK(clkctl.smos = 0) */
- sst_dsp_shim_update_bits_unlocked(sst, SST_CLKCTL,
- SST_CLKCTL_MASK, 0);
-
- /* Set D3 state, delay 50 us */
- val = readl(sst->addr.pci_cfg + SST_PMCS);
- val |= SST_PMCS_PS_MASK;
- writel(val, sst->addr.pci_cfg + SST_PMCS);
- udelay(50);
-
- /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
- reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
- reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE;
- writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
-
- udelay(50);
-
-}
-
-static void hsw_reset(struct sst_dsp *sst)
-{
- /* put DSP into reset and stall */
- sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
- SST_CSR_RST | SST_CSR_STALL,
- SST_CSR_RST | SST_CSR_STALL);
-
- /* keep in reset for 10ms */
- mdelay(10);
-
- /* take DSP out of reset and keep stalled for FW loading */
- sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
- SST_CSR_RST | SST_CSR_STALL, SST_CSR_STALL);
-}
-
-static int hsw_set_dsp_D0(struct sst_dsp *sst)
-{
- int tries = 10;
- u32 reg, fw_dump_bit;
-
- /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
- reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
- reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE);
- writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
-
- /* Disable D3PG (VDRTCTL0.D3PGD = 1) */
- reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
- reg |= SST_VDRTCL0_D3PGD;
- writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0);
-
- /* Set D0 state */
- reg = readl(sst->addr.pci_cfg + SST_PMCS);
- reg &= ~SST_PMCS_PS_MASK;
- writel(reg, sst->addr.pci_cfg + SST_PMCS);
-
- /* check that ADSP shim is enabled */
- while (tries--) {
- reg = readl(sst->addr.pci_cfg + SST_PMCS) & SST_PMCS_PS_MASK;
- if (reg == 0)
- goto finish;
-
- msleep(1);
- }
-
- return -ENODEV;
-
-finish:
- /* select SSP1 19.2MHz base clock, SSP clock 0, turn off Low Power Clock */
- sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
- SST_CSR_S1IOCS | SST_CSR_SBCS1 | SST_CSR_LPCS, 0x0);
-
- /* stall DSP core, set clk to 192/96Mhz */
- sst_dsp_shim_update_bits_unlocked(sst,
- SST_CSR, SST_CSR_STALL | SST_CSR_DCS_MASK,
- SST_CSR_STALL | SST_CSR_DCS(4));
-
- /* Set 24MHz MCLK, prevent local clock gating, enable SSP0 clock */
- sst_dsp_shim_update_bits_unlocked(sst, SST_CLKCTL,
- SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0,
- SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0);
-
- /* Stall and reset core, set CSR */
- hsw_reset(sst);
-
- /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
- reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
- reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE;
- writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
-
- udelay(50);
-
- /* switch on audio PLL */
- reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
- reg &= ~SST_VDRTCL2_APLLSE_MASK;
- writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
-
- /* set default power gating control, enable power gating control for all blocks. that is,
- can't be accessed, please enable each block before accessing. */
- reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
- reg |= SST_VDRTCL0_DSRAMPGE_MASK | SST_VDRTCL0_ISRAMPGE_MASK;
- /* for D0, always enable the block(DSRAM[0]) used for FW dump */
- fw_dump_bit = 1 << SST_VDRTCL0_DSRAMPGE_SHIFT;
- writel(reg & ~fw_dump_bit, sst->addr.pci_cfg + SST_VDRTCTL0);
-
-
- /* disable DMA finish function for SSP0 & SSP1 */
- sst_dsp_shim_update_bits_unlocked(sst, SST_CSR2, SST_CSR2_SDFD_SSP1,
- SST_CSR2_SDFD_SSP1);
-
- /* set on-demond mode on engine 0,1 for all channels */
- sst_dsp_shim_update_bits(sst, SST_HMDC,
- SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH,
- SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH);
-
- /* Enable Interrupt from both sides */
- sst_dsp_shim_update_bits(sst, SST_IMRX, (SST_IMRX_BUSY | SST_IMRX_DONE),
- 0x0);
- sst_dsp_shim_update_bits(sst, SST_IMRD, (SST_IMRD_DONE | SST_IMRD_BUSY |
- SST_IMRD_SSP0 | SST_IMRD_DMAC), 0x0);
-
- /* clear IPC registers */
- sst_dsp_shim_write(sst, SST_IPCX, 0x0);
- sst_dsp_shim_write(sst, SST_IPCD, 0x0);
- sst_dsp_shim_write(sst, 0x80, 0x6);
- sst_dsp_shim_write(sst, 0xe0, 0x300a);
-
- return 0;
-}
-
-static void hsw_boot(struct sst_dsp *sst)
-{
- /* set oportunistic mode on engine 0,1 for all channels */
- sst_dsp_shim_update_bits(sst, SST_HMDC,
- SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH, 0);
-
- /* set DSP to RUN */
- sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, SST_CSR_STALL, 0x0);
-}
-
-static void hsw_stall(struct sst_dsp *sst)
-{
- /* stall DSP */
- sst_dsp_shim_update_bits(sst, SST_CSR,
- SST_CSR_24MHZ_LPCS | SST_CSR_STALL,
- SST_CSR_STALL | SST_CSR_24MHZ_LPCS);
-}
-
-static void hsw_sleep(struct sst_dsp *sst)
-{
- dev_dbg(sst->dev, "HSW_PM dsp runtime suspend\n");
-
- /* put DSP into reset and stall */
- sst_dsp_shim_update_bits(sst, SST_CSR,
- SST_CSR_24MHZ_LPCS | SST_CSR_RST | SST_CSR_STALL,
- SST_CSR_RST | SST_CSR_STALL | SST_CSR_24MHZ_LPCS);
-
- hsw_set_dsp_D3(sst);
- dev_dbg(sst->dev, "HSW_PM dsp runtime suspend exit\n");
-}
-
-static int hsw_wake(struct sst_dsp *sst)
-{
- int ret;
-
- dev_dbg(sst->dev, "HSW_PM dsp runtime resume\n");
-
- ret = hsw_set_dsp_D0(sst);
- if (ret < 0)
- return ret;
-
- dev_dbg(sst->dev, "HSW_PM dsp runtime resume exit\n");
-
- return 0;
-}
-
-struct sst_adsp_memregion {
- u32 start;
- u32 end;
- int blocks;
- enum sst_mem_type type;
-};
-
-/* lynx point ADSP mem regions */
-static const struct sst_adsp_memregion lp_region[] = {
- {0x00000, 0x40000, 8, SST_MEM_DRAM}, /* D-SRAM0 - 8 * 32kB */
- {0x40000, 0x80000, 8, SST_MEM_DRAM}, /* D-SRAM1 - 8 * 32kB */
- {0x80000, 0xE0000, 12, SST_MEM_IRAM}, /* I-SRAM - 12 * 32kB */
-};
-
-/* wild cat point ADSP mem regions */
-static const struct sst_adsp_memregion wpt_region[] = {
- {0x00000, 0xA0000, 20, SST_MEM_DRAM}, /* D-SRAM0,D-SRAM1,D-SRAM2 - 20 * 32kB */
- {0xA0000, 0xF0000, 10, SST_MEM_IRAM}, /* I-SRAM - 10 * 32kB */
-};
-
-static int hsw_acpi_resource_map(struct sst_dsp *sst, struct sst_pdata *pdata)
-{
- /* ADSP DRAM & IRAM */
- sst->addr.lpe_base = pdata->lpe_base;
- sst->addr.lpe = ioremap(pdata->lpe_base, pdata->lpe_size);
- if (!sst->addr.lpe)
- return -ENODEV;
-
- /* ADSP PCI MMIO config space */
- sst->addr.pci_cfg = ioremap(pdata->pcicfg_base, pdata->pcicfg_size);
- if (!sst->addr.pci_cfg) {
- iounmap(sst->addr.lpe);
- return -ENODEV;
- }
-
- /* SST Shim */
- sst->addr.shim = sst->addr.lpe + sst->addr.shim_offset;
- return 0;
-}
-
-struct sst_sram_shift {
- u32 dev_id; /* SST Device IDs */
- u32 iram_shift;
- u32 dram_shift;
-};
-
-static const struct sst_sram_shift sram_shift[] = {
- {SST_DEV_ID_LYNX_POINT, 6, 16}, /* lp */
- {SST_DEV_ID_WILDCAT_POINT, 2, 12}, /* wpt */
-};
-
-static u32 hsw_block_get_bit(struct sst_mem_block *block)
-{
- u32 bit = 0, shift = 0, index;
- struct sst_dsp *sst = block->dsp;
-
- for (index = 0; index < ARRAY_SIZE(sram_shift); index++) {
- if (sram_shift[index].dev_id == sst->id)
- break;
- }
-
- if (index < ARRAY_SIZE(sram_shift)) {
- switch (block->type) {
- case SST_MEM_DRAM:
- shift = sram_shift[index].dram_shift;
- break;
- case SST_MEM_IRAM:
- shift = sram_shift[index].iram_shift;
- break;
- default:
- shift = 0;
- }
- } else
- shift = 0;
-
- bit = 1 << (block->index + shift);
-
- return bit;
-}
-
-/*dummy read a SRAM block.*/
-static void sst_mem_block_dummy_read(struct sst_mem_block *block)
-{
- u32 size;
- u8 tmp_buf[4];
- struct sst_dsp *sst = block->dsp;
-
- size = block->size > 4 ? 4 : block->size;
- memcpy_fromio(tmp_buf, sst->addr.lpe + block->offset, size);
-}
-
-/* enable 32kB memory block - locks held by caller */
-static int hsw_block_enable(struct sst_mem_block *block)
-{
- struct sst_dsp *sst = block->dsp;
- u32 bit, val;
-
- if (block->users++ > 0)
- return 0;
-
- dev_dbg(block->dsp->dev, " enabled block %d:%d at offset 0x%x\n",
- block->type, block->index, block->offset);
-
- /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
- val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
- val &= ~SST_VDRTCL2_DCLCGE;
- writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
-
- val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
- bit = hsw_block_get_bit(block);
- writel(val & ~bit, sst->addr.pci_cfg + SST_VDRTCTL0);
-
- /* wait 18 DSP clock ticks */
- udelay(10);
-
- /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
- val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
- val |= SST_VDRTCL2_DCLCGE;
- writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
-
- udelay(50);
-
- /*add a dummy read before the SRAM block is written, otherwise the writing may miss bytes sometimes.*/
- sst_mem_block_dummy_read(block);
- return 0;
-}
-
-/* disable 32kB memory block - locks held by caller */
-static int hsw_block_disable(struct sst_mem_block *block)
-{
- struct sst_dsp *sst = block->dsp;
- u32 bit, val;
-
- if (--block->users > 0)
- return 0;
-
- dev_dbg(block->dsp->dev, " disabled block %d:%d at offset 0x%x\n",
- block->type, block->index, block->offset);
-
- /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
- val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
- val &= ~SST_VDRTCL2_DCLCGE;
- writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
-
-
- val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
- bit = hsw_block_get_bit(block);
- /* don't disable DSRAM[0], keep it always enable for FW dump*/
- if (bit != (1 << SST_VDRTCL0_DSRAMPGE_SHIFT))
- writel(val | bit, sst->addr.pci_cfg + SST_VDRTCTL0);
-
- /* wait 18 DSP clock ticks */
- udelay(10);
-
- /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
- val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
- val |= SST_VDRTCL2_DCLCGE;
- writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
-
- udelay(50);
-
- return 0;
-}
-
-static struct sst_block_ops sst_hsw_ops = {
- .enable = hsw_block_enable,
- .disable = hsw_block_disable,
-};
-
-static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
-{
- const struct sst_adsp_memregion *region;
- struct device *dev;
- int ret = -ENODEV, i, j, region_count;
- u32 offset, size, fw_dump_bit;
-
- dev = sst->dma_dev;
-
- switch (sst->id) {
- case SST_DEV_ID_LYNX_POINT:
- region = lp_region;
- region_count = ARRAY_SIZE(lp_region);
- sst->addr.iram_offset = SST_LP_IRAM_OFFSET;
- sst->addr.dsp_iram_offset = SST_LPT_DSP_IRAM_OFFSET;
- sst->addr.dsp_dram_offset = SST_LPT_DSP_DRAM_OFFSET;
- sst->addr.shim_offset = SST_LP_SHIM_OFFSET;
- break;
- case SST_DEV_ID_WILDCAT_POINT:
- region = wpt_region;
- region_count = ARRAY_SIZE(wpt_region);
- sst->addr.iram_offset = SST_WPT_IRAM_OFFSET;
- sst->addr.dsp_iram_offset = SST_WPT_DSP_IRAM_OFFSET;
- sst->addr.dsp_dram_offset = SST_WPT_DSP_DRAM_OFFSET;
- sst->addr.shim_offset = SST_WPT_SHIM_OFFSET;
- break;
- default:
- dev_err(dev, "error: failed to get mem resources\n");
- return ret;
- }
-
- ret = hsw_acpi_resource_map(sst, pdata);
- if (ret < 0) {
- dev_err(dev, "error: failed to map resources\n");
- return ret;
- }
-
- /* enable the DSP SHIM */
- ret = hsw_set_dsp_D0(sst);
- if (ret < 0) {
- dev_err(dev, "error: failed to set DSP D0 and reset SHIM\n");
- return ret;
- }
-
- ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(31));
- if (ret)
- return ret;
-
-
- /* register DSP memory blocks - ideally we should get this from ACPI */
- for (i = 0; i < region_count; i++) {
- offset = region[i].start;
- size = (region[i].end - region[i].start) / region[i].blocks;
-
- /* register individual memory blocks */
- for (j = 0; j < region[i].blocks; j++) {
- sst_mem_block_register(sst, offset, size,
- region[i].type, &sst_hsw_ops, j, sst);
- offset += size;
- }
- }
-
- /* always enable the block(DSRAM[0]) used for FW dump */
- fw_dump_bit = 1 << SST_VDRTCL0_DSRAMPGE_SHIFT;
- /* set default power gating control, enable power gating control for all blocks. that is,
- can't be accessed, please enable each block before accessing. */
- writel(0xffffffff & ~fw_dump_bit, sst->addr.pci_cfg + SST_VDRTCTL0);
-
- return 0;
-}
-
-static void hsw_free(struct sst_dsp *sst)
-{
- sst_mem_block_unregister_all(sst);
- iounmap(sst->addr.lpe);
- iounmap(sst->addr.pci_cfg);
-}
-
-struct sst_ops haswell_ops = {
- .reset = hsw_reset,
- .boot = hsw_boot,
- .stall = hsw_stall,
- .wake = hsw_wake,
- .sleep = hsw_sleep,
- .write = sst_shim32_write,
- .read = sst_shim32_read,
- .write64 = sst_shim32_write64,
- .read64 = sst_shim32_read64,
- .ram_read = sst_memcpy_fromio_32,
- .ram_write = sst_memcpy_toio_32,
- .irq_handler = hsw_irq,
- .init = hsw_init,
- .free = hsw_free,
- .parse_fw = hsw_parse_fw_image,
-};
+++ /dev/null
-/*
- * Intel SST Haswell/Broadwell IPC Support
- *
- * Copyright (C) 2013, Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/device.h>
-#include <linux/wait.h>
-#include <linux/spinlock.h>
-#include <linux/workqueue.h>
-#include <linux/export.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/sched.h>
-#include <linux/platform_device.h>
-#include <linux/kthread.h>
-#include <linux/firmware.h>
-#include <linux/dma-mapping.h>
-#include <linux/debugfs.h>
-#include <linux/pm_runtime.h>
-#include <sound/asound.h>
-
-#include "sst-haswell-ipc.h"
-#include "sst-dsp.h"
-#include "sst-dsp-priv.h"
-
-/* Global Message - Generic */
-#define IPC_GLB_TYPE_SHIFT 24
-#define IPC_GLB_TYPE_MASK (0x1f << IPC_GLB_TYPE_SHIFT)
-#define IPC_GLB_TYPE(x) (x << IPC_GLB_TYPE_SHIFT)
-
-/* Global Message - Reply */
-#define IPC_GLB_REPLY_SHIFT 0
-#define IPC_GLB_REPLY_MASK (0x1f << IPC_GLB_REPLY_SHIFT)
-#define IPC_GLB_REPLY_TYPE(x) (x << IPC_GLB_REPLY_TYPE_SHIFT)
-
-/* Stream Message - Generic */
-#define IPC_STR_TYPE_SHIFT 20
-#define IPC_STR_TYPE_MASK (0xf << IPC_STR_TYPE_SHIFT)
-#define IPC_STR_TYPE(x) (x << IPC_STR_TYPE_SHIFT)
-#define IPC_STR_ID_SHIFT 16
-#define IPC_STR_ID_MASK (0xf << IPC_STR_ID_SHIFT)
-#define IPC_STR_ID(x) (x << IPC_STR_ID_SHIFT)
-
-/* Stream Message - Reply */
-#define IPC_STR_REPLY_SHIFT 0
-#define IPC_STR_REPLY_MASK (0x1f << IPC_STR_REPLY_SHIFT)
-
-/* Stream Stage Message - Generic */
-#define IPC_STG_TYPE_SHIFT 12
-#define IPC_STG_TYPE_MASK (0xf << IPC_STG_TYPE_SHIFT)
-#define IPC_STG_TYPE(x) (x << IPC_STG_TYPE_SHIFT)
-#define IPC_STG_ID_SHIFT 10
-#define IPC_STG_ID_MASK (0x3 << IPC_STG_ID_SHIFT)
-#define IPC_STG_ID(x) (x << IPC_STG_ID_SHIFT)
-
-/* Stream Stage Message - Reply */
-#define IPC_STG_REPLY_SHIFT 0
-#define IPC_STG_REPLY_MASK (0x1f << IPC_STG_REPLY_SHIFT)
-
-/* Debug Log Message - Generic */
-#define IPC_LOG_OP_SHIFT 20
-#define IPC_LOG_OP_MASK (0xf << IPC_LOG_OP_SHIFT)
-#define IPC_LOG_OP_TYPE(x) (x << IPC_LOG_OP_SHIFT)
-#define IPC_LOG_ID_SHIFT 16
-#define IPC_LOG_ID_MASK (0xf << IPC_LOG_ID_SHIFT)
-#define IPC_LOG_ID(x) (x << IPC_LOG_ID_SHIFT)
-
-/* IPC message timeout (msecs) */
-#define IPC_TIMEOUT_MSECS 300
-#define IPC_BOOT_MSECS 200
-#define IPC_MSG_WAIT 0
-#define IPC_MSG_NOWAIT 1
-
-/* Firmware Ready Message */
-#define IPC_FW_READY (0x1 << 29)
-#define IPC_STATUS_MASK (0x3 << 30)
-
-#define IPC_EMPTY_LIST_SIZE 8
-#define IPC_MAX_STREAMS 4
-
-/* Mailbox */
-#define IPC_MAX_MAILBOX_BYTES 256
-
-#define INVALID_STREAM_HW_ID 0xffffffff
-
-/* Global Message - Types and Replies */
-enum ipc_glb_type {
- IPC_GLB_GET_FW_VERSION = 0, /* Retrieves firmware version */
- IPC_GLB_PERFORMANCE_MONITOR = 1, /* Performance monitoring actions */
- IPC_GLB_ALLOCATE_STREAM = 3, /* Request to allocate new stream */
- IPC_GLB_FREE_STREAM = 4, /* Request to free stream */
- IPC_GLB_GET_FW_CAPABILITIES = 5, /* Retrieves firmware capabilities */
- IPC_GLB_STREAM_MESSAGE = 6, /* Message directed to stream or its stages */
- /* Request to store firmware context during D0->D3 transition */
- IPC_GLB_REQUEST_DUMP = 7,
- /* Request to restore firmware context during D3->D0 transition */
- IPC_GLB_RESTORE_CONTEXT = 8,
- IPC_GLB_GET_DEVICE_FORMATS = 9, /* Set device format */
- IPC_GLB_SET_DEVICE_FORMATS = 10, /* Get device format */
- IPC_GLB_SHORT_REPLY = 11,
- IPC_GLB_ENTER_DX_STATE = 12,
- IPC_GLB_GET_MIXER_STREAM_INFO = 13, /* Request mixer stream params */
- IPC_GLB_DEBUG_LOG_MESSAGE = 14, /* Message to or from the debug logger. */
- IPC_GLB_REQUEST_TRANSFER = 16, /* < Request Transfer for host */
- IPC_GLB_MAX_IPC_MESSAGE_TYPE = 17, /* Maximum message number */
-};
-
-enum ipc_glb_reply {
- IPC_GLB_REPLY_SUCCESS = 0, /* The operation was successful. */
- IPC_GLB_REPLY_ERROR_INVALID_PARAM = 1, /* Invalid parameter was passed. */
- IPC_GLB_REPLY_UNKNOWN_MESSAGE_TYPE = 2, /* Uknown message type was resceived. */
- IPC_GLB_REPLY_OUT_OF_RESOURCES = 3, /* No resources to satisfy the request. */
- IPC_GLB_REPLY_BUSY = 4, /* The system or resource is busy. */
- IPC_GLB_REPLY_PENDING = 5, /* The action was scheduled for processing. */
- IPC_GLB_REPLY_FAILURE = 6, /* Critical error happened. */
- IPC_GLB_REPLY_INVALID_REQUEST = 7, /* Request can not be completed. */
- IPC_GLB_REPLY_STAGE_UNINITIALIZED = 8, /* Processing stage was uninitialized. */
- IPC_GLB_REPLY_NOT_FOUND = 9, /* Required resource can not be found. */
- IPC_GLB_REPLY_SOURCE_NOT_STARTED = 10, /* Source was not started. */
-};
-
-/* Stream Message - Types */
-enum ipc_str_operation {
- IPC_STR_RESET = 0,
- IPC_STR_PAUSE = 1,
- IPC_STR_RESUME = 2,
- IPC_STR_STAGE_MESSAGE = 3,
- IPC_STR_NOTIFICATION = 4,
- IPC_STR_MAX_MESSAGE
-};
-
-/* Stream Stage Message Types */
-enum ipc_stg_operation {
- IPC_STG_GET_VOLUME = 0,
- IPC_STG_SET_VOLUME,
- IPC_STG_SET_WRITE_POSITION,
- IPC_STG_SET_FX_ENABLE,
- IPC_STG_SET_FX_DISABLE,
- IPC_STG_SET_FX_GET_PARAM,
- IPC_STG_SET_FX_SET_PARAM,
- IPC_STG_SET_FX_GET_INFO,
- IPC_STG_MUTE_LOOPBACK,
- IPC_STG_MAX_MESSAGE
-};
-
-/* Stream Stage Message Types For Notification*/
-enum ipc_stg_operation_notify {
- IPC_POSITION_CHANGED = 0,
- IPC_STG_GLITCH,
- IPC_STG_MAX_NOTIFY
-};
-
-enum ipc_glitch_type {
- IPC_GLITCH_UNDERRUN = 1,
- IPC_GLITCH_DECODER_ERROR,
- IPC_GLITCH_DOUBLED_WRITE_POS,
- IPC_GLITCH_MAX
-};
-
-/* Debug Control */
-enum ipc_debug_operation {
- IPC_DEBUG_ENABLE_LOG = 0,
- IPC_DEBUG_DISABLE_LOG = 1,
- IPC_DEBUG_REQUEST_LOG_DUMP = 2,
- IPC_DEBUG_NOTIFY_LOG_DUMP = 3,
- IPC_DEBUG_MAX_DEBUG_LOG
-};
-
-/* Firmware Ready */
-struct sst_hsw_ipc_fw_ready {
- u32 inbox_offset;
- u32 outbox_offset;
- u32 inbox_size;
- u32 outbox_size;
- u32 fw_info_size;
- u8 fw_info[IPC_MAX_MAILBOX_BYTES - 5 * sizeof(u32)];
-} __attribute__((packed));
-
-struct ipc_message {
- struct list_head list;
- u32 header;
-
- /* direction wrt host CPU */
- char tx_data[IPC_MAX_MAILBOX_BYTES];
- size_t tx_size;
- char rx_data[IPC_MAX_MAILBOX_BYTES];
- size_t rx_size;
-
- wait_queue_head_t waitq;
- bool pending;
- bool complete;
- bool wait;
- int errno;
-};
-
-struct sst_hsw_stream;
-struct sst_hsw;
-
-/* Stream infomation */
-struct sst_hsw_stream {
- /* configuration */
- struct sst_hsw_ipc_stream_alloc_req request;
- struct sst_hsw_ipc_stream_alloc_reply reply;
- struct sst_hsw_ipc_stream_free_req free_req;
-
- /* Mixer info */
- u32 mute_volume[SST_HSW_NO_CHANNELS];
- u32 mute[SST_HSW_NO_CHANNELS];
-
- /* runtime info */
- struct sst_hsw *hsw;
- int host_id;
- bool commited;
- bool running;
-
- /* Notification work */
- struct work_struct notify_work;
- u32 header;
-
- /* Position info from DSP */
- struct sst_hsw_ipc_stream_set_position wpos;
- struct sst_hsw_ipc_stream_get_position rpos;
- struct sst_hsw_ipc_stream_glitch_position glitch;
-
- /* Volume info */
- struct sst_hsw_ipc_volume_req vol_req;
-
- /* driver callback */
- u32 (*notify_position)(struct sst_hsw_stream *stream, void *data);
- void *pdata;
-
- /* record the fw read position when playback */
- snd_pcm_uframes_t old_position;
- bool play_silence;
- struct list_head node;
-};
-
-/* FW log ring information */
-struct sst_hsw_log_stream {
- dma_addr_t dma_addr;
- unsigned char *dma_area;
- unsigned char *ring_descr;
- int pages;
- int size;
-
- /* Notification work */
- struct work_struct notify_work;
- wait_queue_head_t readers_wait_q;
- struct mutex rw_mutex;
-
- u32 last_pos;
- u32 curr_pos;
- u32 reader_pos;
-
- /* fw log config */
- u32 config[SST_HSW_FW_LOG_CONFIG_DWORDS];
-
- struct sst_hsw *hsw;
-};
-
-/* SST Haswell IPC data */
-struct sst_hsw {
- struct device *dev;
- struct sst_dsp *dsp;
- struct platform_device *pdev_pcm;
-
- /* FW config */
- struct sst_hsw_ipc_fw_ready fw_ready;
- struct sst_hsw_ipc_fw_version version;
- bool fw_done;
- struct sst_fw *sst_fw;
-
- /* stream */
- struct list_head stream_list;
-
- /* global mixer */
- struct sst_hsw_ipc_stream_info_reply mixer_info;
- enum sst_hsw_volume_curve curve_type;
- u32 curve_duration;
- u32 mute[SST_HSW_NO_CHANNELS];
- u32 mute_volume[SST_HSW_NO_CHANNELS];
-
- /* DX */
- struct sst_hsw_ipc_dx_reply dx;
- void *dx_context;
- dma_addr_t dx_context_paddr;
-
- /* boot */
- wait_queue_head_t boot_wait;
- bool boot_complete;
- bool shutdown;
-
- /* IPC messaging */
- struct list_head tx_list;
- struct list_head rx_list;
- struct list_head empty_list;
- wait_queue_head_t wait_txq;
- struct task_struct *tx_thread;
- struct kthread_worker kworker;
- struct kthread_work kwork;
- bool pending;
- struct ipc_message *msg;
-
- /* FW log stream */
- struct sst_hsw_log_stream log_stream;
-};
-
-#define CREATE_TRACE_POINTS
-#include <trace/events/hswadsp.h>
-
-static inline u32 msg_get_global_type(u32 msg)
-{
- return (msg & IPC_GLB_TYPE_MASK) >> IPC_GLB_TYPE_SHIFT;
-}
-
-static inline u32 msg_get_global_reply(u32 msg)
-{
- return (msg & IPC_GLB_REPLY_MASK) >> IPC_GLB_REPLY_SHIFT;
-}
-
-static inline u32 msg_get_stream_type(u32 msg)
-{
- return (msg & IPC_STR_TYPE_MASK) >> IPC_STR_TYPE_SHIFT;
-}
-
-static inline u32 msg_get_stage_type(u32 msg)
-{
- return (msg & IPC_STG_TYPE_MASK) >> IPC_STG_TYPE_SHIFT;
-}
-
-static inline u32 msg_get_stream_id(u32 msg)
-{
- return (msg & IPC_STR_ID_MASK) >> IPC_STR_ID_SHIFT;
-}
-
-static inline u32 msg_get_notify_reason(u32 msg)
-{
- return (msg & IPC_STG_TYPE_MASK) >> IPC_STG_TYPE_SHIFT;
-}
-
-u32 create_channel_map(enum sst_hsw_channel_config config)
-{
- switch (config) {
- case SST_HSW_CHANNEL_CONFIG_MONO:
- return (0xFFFFFFF0 | SST_HSW_CHANNEL_CENTER);
- case SST_HSW_CHANNEL_CONFIG_STEREO:
- return (0xFFFFFF00 | SST_HSW_CHANNEL_LEFT
- | (SST_HSW_CHANNEL_RIGHT << 4));
- case SST_HSW_CHANNEL_CONFIG_2_POINT_1:
- return (0xFFFFF000 | SST_HSW_CHANNEL_LEFT
- | (SST_HSW_CHANNEL_RIGHT << 4)
- | (SST_HSW_CHANNEL_LFE << 8 ));
- case SST_HSW_CHANNEL_CONFIG_3_POINT_0:
- return (0xFFFFF000 | SST_HSW_CHANNEL_LEFT
- | (SST_HSW_CHANNEL_CENTER << 4)
- | (SST_HSW_CHANNEL_RIGHT << 8));
- case SST_HSW_CHANNEL_CONFIG_3_POINT_1:
- return (0xFFFF0000 | SST_HSW_CHANNEL_LEFT
- | (SST_HSW_CHANNEL_CENTER << 4)
- | (SST_HSW_CHANNEL_RIGHT << 8)
- | (SST_HSW_CHANNEL_LFE << 12));
- case SST_HSW_CHANNEL_CONFIG_QUATRO:
- return (0xFFFF0000 | SST_HSW_CHANNEL_LEFT
- | (SST_HSW_CHANNEL_RIGHT << 4)
- | (SST_HSW_CHANNEL_LEFT_SURROUND << 8)
- | (SST_HSW_CHANNEL_RIGHT_SURROUND << 12));
- case SST_HSW_CHANNEL_CONFIG_4_POINT_0:
- return (0xFFFF0000 | SST_HSW_CHANNEL_LEFT
- | (SST_HSW_CHANNEL_CENTER << 4)
- | (SST_HSW_CHANNEL_RIGHT << 8)
- | (SST_HSW_CHANNEL_CENTER_SURROUND << 12));
- case SST_HSW_CHANNEL_CONFIG_5_POINT_0:
- return (0xFFF00000 | SST_HSW_CHANNEL_LEFT
- | (SST_HSW_CHANNEL_CENTER << 4)
- | (SST_HSW_CHANNEL_RIGHT << 8)
- | (SST_HSW_CHANNEL_LEFT_SURROUND << 12)
- | (SST_HSW_CHANNEL_RIGHT_SURROUND << 16));
- case SST_HSW_CHANNEL_CONFIG_5_POINT_1:
- return (0xFF000000 | SST_HSW_CHANNEL_CENTER
- | (SST_HSW_CHANNEL_LEFT << 4)
- | (SST_HSW_CHANNEL_RIGHT << 8)
- | (SST_HSW_CHANNEL_LEFT_SURROUND << 12)
- | (SST_HSW_CHANNEL_RIGHT_SURROUND << 16)
- | (SST_HSW_CHANNEL_LFE << 20));
- case SST_HSW_CHANNEL_CONFIG_DUAL_MONO:
- return (0xFFFFFF00 | SST_HSW_CHANNEL_LEFT
- | (SST_HSW_CHANNEL_LEFT << 4));
- default:
- return 0xFFFFFFFF;
- }
-}
-
-static struct sst_hsw_stream *get_stream_by_id(struct sst_hsw *hsw,
- int stream_id)
-{
- struct sst_hsw_stream *stream;
-
- list_for_each_entry(stream, &hsw->stream_list, node) {
- if (stream->reply.stream_hw_id == stream_id)
- return stream;
- }
-
- return NULL;
-}
-
-static void ipc_shim_dbg(struct sst_hsw *hsw, const char *text)
-{
- struct sst_dsp *sst = hsw->dsp;
- u32 isr, ipcd, imrx, ipcx;
-
- ipcx = sst_dsp_shim_read_unlocked(sst, SST_IPCX);
- isr = sst_dsp_shim_read_unlocked(sst, SST_ISRX);
- ipcd = sst_dsp_shim_read_unlocked(sst, SST_IPCD);
- imrx = sst_dsp_shim_read_unlocked(sst, SST_IMRX);
-
- dev_err(hsw->dev, "ipc: --%s-- ipcx 0x%8.8x isr 0x%8.8x ipcd 0x%8.8x imrx 0x%8.8x\n",
- text, ipcx, isr, ipcd, imrx);
-}
-
-/* locks held by caller */
-static struct ipc_message *msg_get_empty(struct sst_hsw *hsw)
-{
- struct ipc_message *msg = NULL;
-
- if (!list_empty(&hsw->empty_list)) {
- msg = list_first_entry(&hsw->empty_list, struct ipc_message,
- list);
- list_del(&msg->list);
- }
-
- return msg;
-}
-
-static void ipc_tx_msgs(struct kthread_work *work)
-{
- struct sst_hsw *hsw =
- container_of(work, struct sst_hsw, kwork);
- struct ipc_message *msg;
- unsigned long flags;
- u32 ipcx;
-
- spin_lock_irqsave(&hsw->dsp->spinlock, flags);
-
- if (list_empty(&hsw->tx_list) || hsw->pending) {
- spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
- return;
- }
-
- /* if the DSP is busy, we will TX messages after IRQ.
- * also postpone if we are in the middle of procesing completion irq*/
- ipcx = sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX);
- if (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE)) {
- spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
- return;
- }
-
- msg = list_first_entry(&hsw->tx_list, struct ipc_message, list);
-
- list_move(&msg->list, &hsw->rx_list);
-
- /* send the message */
- sst_dsp_outbox_write(hsw->dsp, msg->tx_data, msg->tx_size);
- sst_dsp_ipc_msg_tx(hsw->dsp, msg->header | SST_IPCX_BUSY);
-
- spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
-}
-
-/* locks held by caller */
-static void tx_msg_reply_complete(struct sst_hsw *hsw, struct ipc_message *msg)
-{
- msg->complete = true;
- trace_ipc_reply("completed", msg->header);
-
- if (!msg->wait)
- list_add_tail(&msg->list, &hsw->empty_list);
- else
- wake_up(&msg->waitq);
-}
-
-static int tx_wait_done(struct sst_hsw *hsw, struct ipc_message *msg,
- void *rx_data)
-{
- unsigned long flags;
- int ret;
-
- /* wait for DSP completion (in all cases atm inc pending) */
- ret = wait_event_timeout(msg->waitq, msg->complete,
- msecs_to_jiffies(IPC_TIMEOUT_MSECS));
-
- spin_lock_irqsave(&hsw->dsp->spinlock, flags);
- if (ret == 0) {
- ipc_shim_dbg(hsw, "message timeout");
-
- trace_ipc_error("error message timeout for", msg->header);
- list_del(&msg->list);
- ret = -ETIMEDOUT;
- } else {
-
- /* copy the data returned from DSP */
- if (msg->rx_size)
- memcpy(rx_data, msg->rx_data, msg->rx_size);
- ret = msg->errno;
- }
-
- list_add_tail(&msg->list, &hsw->empty_list);
- spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
- return ret;
-}
-
-static int ipc_tx_message(struct sst_hsw *hsw, u32 header, void *tx_data,
- size_t tx_bytes, void *rx_data, size_t rx_bytes, int wait)
-{
- struct ipc_message *msg;
- unsigned long flags;
-
- spin_lock_irqsave(&hsw->dsp->spinlock, flags);
-
- msg = msg_get_empty(hsw);
- if (msg == NULL) {
- spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
- return -EBUSY;
- }
-
- if (tx_bytes)
- memcpy(msg->tx_data, tx_data, tx_bytes);
-
- msg->header = header;
- msg->tx_size = tx_bytes;
- msg->rx_size = rx_bytes;
- msg->wait = wait;
- msg->errno = 0;
- msg->pending = false;
- msg->complete = false;
-
- list_add_tail(&msg->list, &hsw->tx_list);
- spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
-
- queue_kthread_work(&hsw->kworker, &hsw->kwork);
-
- if (wait)
- return tx_wait_done(hsw, msg, rx_data);
- else
- return 0;
-}
-
-static inline int ipc_tx_message_wait(struct sst_hsw *hsw, u32 header,
- void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes)
-{
- return ipc_tx_message(hsw, header, tx_data, tx_bytes, rx_data,
- rx_bytes, 1);
-}
-
-static inline int ipc_tx_message_nowait(struct sst_hsw *hsw, u32 header,
- void *tx_data, size_t tx_bytes)
-{
- return ipc_tx_message(hsw, header, tx_data, tx_bytes, NULL, 0, 0);
-}
-
-static void hsw_fw_ready(struct sst_hsw *hsw, u32 header)
-{
- struct sst_hsw_ipc_fw_ready fw_ready;
- u32 offset;
- u8 fw_info[IPC_MAX_MAILBOX_BYTES - 5 * sizeof(u32)];
- char *tmp[5], *pinfo;
- int i = 0;
-
- offset = (header & 0x1FFFFFFF) << 3;
-
- dev_dbg(hsw->dev, "ipc: DSP is ready 0x%8.8x offset %d\n",
- header, offset);
-
- /* copy data from the DSP FW ready offset */
- sst_dsp_read(hsw->dsp, &fw_ready, offset, sizeof(fw_ready));
-
- sst_dsp_mailbox_init(hsw->dsp, fw_ready.inbox_offset,
- fw_ready.inbox_size, fw_ready.outbox_offset,
- fw_ready.outbox_size);
-
- hsw->boot_complete = true;
- wake_up(&hsw->boot_wait);
-
- dev_dbg(hsw->dev, " mailbox upstream 0x%x - size 0x%x\n",
- fw_ready.inbox_offset, fw_ready.inbox_size);
- dev_dbg(hsw->dev, " mailbox downstream 0x%x - size 0x%x\n",
- fw_ready.outbox_offset, fw_ready.outbox_size);
- if (fw_ready.fw_info_size < sizeof(fw_ready.fw_info)) {
- fw_ready.fw_info[fw_ready.fw_info_size] = 0;
- dev_dbg(hsw->dev, " Firmware info: %s \n", fw_ready.fw_info);
-
- /* log the FW version info got from the mailbox here. */
- memcpy(fw_info, fw_ready.fw_info, fw_ready.fw_info_size);
- pinfo = &fw_info[0];
- for (i = 0; i < sizeof(tmp) / sizeof(char *); i++)
- tmp[i] = strsep(&pinfo, " ");
- dev_info(hsw->dev, "FW loaded, mailbox readback FW info: type %s, - "
- "version: %s.%s, build %s, source commit id: %s\n",
- tmp[0], tmp[1], tmp[2], tmp[3], tmp[4]);
- }
-}
-
-static void hsw_notification_work(struct work_struct *work)
-{
- struct sst_hsw_stream *stream = container_of(work,
- struct sst_hsw_stream, notify_work);
- struct sst_hsw_ipc_stream_glitch_position *glitch = &stream->glitch;
- struct sst_hsw_ipc_stream_get_position *pos = &stream->rpos;
- struct sst_hsw *hsw = stream->hsw;
- u32 reason;
-
- reason = msg_get_notify_reason(stream->header);
-
- switch (reason) {
- case IPC_STG_GLITCH:
- trace_ipc_notification("DSP stream under/overrun",
- stream->reply.stream_hw_id);
- sst_dsp_inbox_read(hsw->dsp, glitch, sizeof(*glitch));
-
- dev_err(hsw->dev, "glitch %d pos 0x%x write pos 0x%x\n",
- glitch->glitch_type, glitch->present_pos,
- glitch->write_pos);
- break;
-
- case IPC_POSITION_CHANGED:
- trace_ipc_notification("DSP stream position changed for",
- stream->reply.stream_hw_id);
- sst_dsp_inbox_read(hsw->dsp, pos, sizeof(*pos));
-
- if (stream->notify_position)
- stream->notify_position(stream, stream->pdata);
-
- break;
- default:
- dev_err(hsw->dev, "error: unknown notification 0x%x\n",
- stream->header);
- break;
- }
-
- /* tell DSP that notification has been handled */
- sst_dsp_shim_update_bits(hsw->dsp, SST_IPCD,
- SST_IPCD_BUSY | SST_IPCD_DONE, SST_IPCD_DONE);
-
- /* unmask busy interrupt */
- sst_dsp_shim_update_bits(hsw->dsp, SST_IMRX, SST_IMRX_BUSY, 0);
-}
-
-static struct ipc_message *reply_find_msg(struct sst_hsw *hsw, u32 header)
-{
- struct ipc_message *msg;
-
- /* clear reply bits & status bits */
- header &= ~(IPC_STATUS_MASK | IPC_GLB_REPLY_MASK);
-
- if (list_empty(&hsw->rx_list)) {
- dev_err(hsw->dev, "error: rx list empty but received 0x%x\n",
- header);
- return NULL;
- }
-
- list_for_each_entry(msg, &hsw->rx_list, list) {
- if (msg->header == header)
- return msg;
- }
-
- return NULL;
-}
-
-static void hsw_stream_update(struct sst_hsw *hsw, struct ipc_message *msg)
-{
- struct sst_hsw_stream *stream;
- u32 header = msg->header & ~(IPC_STATUS_MASK | IPC_GLB_REPLY_MASK);
- u32 stream_id = msg_get_stream_id(header);
- u32 stream_msg = msg_get_stream_type(header);
-
- stream = get_stream_by_id(hsw, stream_id);
- if (stream == NULL)
- return;
-
- switch (stream_msg) {
- case IPC_STR_STAGE_MESSAGE:
- case IPC_STR_NOTIFICATION:
- break;
- case IPC_STR_RESET:
- trace_ipc_notification("stream reset", stream->reply.stream_hw_id);
- break;
- case IPC_STR_PAUSE:
- stream->running = false;
- trace_ipc_notification("stream paused",
- stream->reply.stream_hw_id);
- break;
- case IPC_STR_RESUME:
- stream->running = true;
- trace_ipc_notification("stream running",
- stream->reply.stream_hw_id);
- break;
- }
-}
-
-static int hsw_process_reply(struct sst_hsw *hsw, u32 header)
-{
- struct ipc_message *msg;
- u32 reply = msg_get_global_reply(header);
-
- trace_ipc_reply("processing -->", header);
-
- msg = reply_find_msg(hsw, header);
- if (msg == NULL) {
- trace_ipc_error("error: can't find message header", header);
- return -EIO;
- }
-
- /* first process the header */
- switch (reply) {
- case IPC_GLB_REPLY_PENDING:
- trace_ipc_pending_reply("received", header);
- msg->pending = true;
- hsw->pending = true;
- return 1;
- case IPC_GLB_REPLY_SUCCESS:
- if (msg->pending) {
- trace_ipc_pending_reply("completed", header);
- sst_dsp_inbox_read(hsw->dsp, msg->rx_data,
- msg->rx_size);
- hsw->pending = false;
- } else {
- /* copy data from the DSP */
- sst_dsp_outbox_read(hsw->dsp, msg->rx_data,
- msg->rx_size);
- }
- break;
- /* these will be rare - but useful for debug */
- case IPC_GLB_REPLY_UNKNOWN_MESSAGE_TYPE:
- trace_ipc_error("error: unknown message type", header);
- msg->errno = -EBADMSG;
- break;
- case IPC_GLB_REPLY_OUT_OF_RESOURCES:
- trace_ipc_error("error: out of resources", header);
- msg->errno = -ENOMEM;
- break;
- case IPC_GLB_REPLY_BUSY:
- trace_ipc_error("error: reply busy", header);
- msg->errno = -EBUSY;
- break;
- case IPC_GLB_REPLY_FAILURE:
- trace_ipc_error("error: reply failure", header);
- msg->errno = -EINVAL;
- break;
- case IPC_GLB_REPLY_STAGE_UNINITIALIZED:
- trace_ipc_error("error: stage uninitialized", header);
- msg->errno = -EINVAL;
- break;
- case IPC_GLB_REPLY_NOT_FOUND:
- trace_ipc_error("error: reply not found", header);
- msg->errno = -EINVAL;
- break;
- case IPC_GLB_REPLY_SOURCE_NOT_STARTED:
- trace_ipc_error("error: source not started", header);
- msg->errno = -EINVAL;
- break;
- case IPC_GLB_REPLY_INVALID_REQUEST:
- trace_ipc_error("error: invalid request", header);
- msg->errno = -EINVAL;
- break;
- case IPC_GLB_REPLY_ERROR_INVALID_PARAM:
- trace_ipc_error("error: invalid parameter", header);
- msg->errno = -EINVAL;
- break;
- default:
- trace_ipc_error("error: unknown reply", header);
- msg->errno = -EINVAL;
- break;
- }
-
- /* update any stream states */
- if (msg_get_global_type(header) == IPC_GLB_STREAM_MESSAGE)
- hsw_stream_update(hsw, msg);
-
- /* wake up and return the error if we have waiters on this message ? */
- list_del(&msg->list);
- tx_msg_reply_complete(hsw, msg);
-
- return 1;
-}
-
-static int hsw_stream_message(struct sst_hsw *hsw, u32 header)
-{
- u32 stream_msg, stream_id, stage_type;
- struct sst_hsw_stream *stream;
- int handled = 0;
-
- stream_msg = msg_get_stream_type(header);
- stream_id = msg_get_stream_id(header);
- stage_type = msg_get_stage_type(header);
-
- stream = get_stream_by_id(hsw, stream_id);
- if (stream == NULL)
- return handled;
-
- stream->header = header;
-
- switch (stream_msg) {
- case IPC_STR_STAGE_MESSAGE:
- dev_err(hsw->dev, "error: stage msg not implemented 0x%8.8x\n",
- header);
- break;
- case IPC_STR_NOTIFICATION:
- schedule_work(&stream->notify_work);
- break;
- default:
- /* handle pending message complete request */
- handled = hsw_process_reply(hsw, header);
- break;
- }
-
- return handled;
-}
-
-static int hsw_log_message(struct sst_hsw *hsw, u32 header)
-{
- u32 operation = (header & IPC_LOG_OP_MASK) >> IPC_LOG_OP_SHIFT;
- struct sst_hsw_log_stream *stream = &hsw->log_stream;
- int ret = 1;
-
- if (operation != IPC_DEBUG_REQUEST_LOG_DUMP) {
- dev_err(hsw->dev,
- "error: log msg not implemented 0x%8.8x\n", header);
- return 0;
- }
-
- mutex_lock(&stream->rw_mutex);
- stream->last_pos = stream->curr_pos;
- sst_dsp_inbox_read(
- hsw->dsp, &stream->curr_pos, sizeof(stream->curr_pos));
- mutex_unlock(&stream->rw_mutex);
-
- schedule_work(&stream->notify_work);
-
- return ret;
-}
-
-static int hsw_process_notification(struct sst_hsw *hsw)
-{
- struct sst_dsp *sst = hsw->dsp;
- u32 type, header;
- int handled = 1;
-
- header = sst_dsp_shim_read_unlocked(sst, SST_IPCD);
- type = msg_get_global_type(header);
-
- trace_ipc_request("processing -->", header);
-
- /* FW Ready is a special case */
- if (!hsw->boot_complete && header & IPC_FW_READY) {
- hsw_fw_ready(hsw, header);
- return handled;
- }
-
- switch (type) {
- case IPC_GLB_GET_FW_VERSION:
- case IPC_GLB_ALLOCATE_STREAM:
- case IPC_GLB_FREE_STREAM:
- case IPC_GLB_GET_FW_CAPABILITIES:
- case IPC_GLB_REQUEST_DUMP:
- case IPC_GLB_GET_DEVICE_FORMATS:
- case IPC_GLB_SET_DEVICE_FORMATS:
- case IPC_GLB_ENTER_DX_STATE:
- case IPC_GLB_GET_MIXER_STREAM_INFO:
- case IPC_GLB_MAX_IPC_MESSAGE_TYPE:
- case IPC_GLB_RESTORE_CONTEXT:
- case IPC_GLB_SHORT_REPLY:
- dev_err(hsw->dev, "error: message type %d header 0x%x\n",
- type, header);
- break;
- case IPC_GLB_STREAM_MESSAGE:
- handled = hsw_stream_message(hsw, header);
- break;
- case IPC_GLB_DEBUG_LOG_MESSAGE:
- handled = hsw_log_message(hsw, header);
- break;
- default:
- dev_err(hsw->dev, "error: unexpected type %d hdr 0x%8.8x\n",
- type, header);
- break;
- }
-
- return handled;
-}
-
-static irqreturn_t hsw_irq_thread(int irq, void *context)
-{
- struct sst_dsp *sst = (struct sst_dsp *) context;
- struct sst_hsw *hsw = sst_dsp_get_thread_context(sst);
- u32 ipcx, ipcd;
- int handled;
- unsigned long flags;
-
- spin_lock_irqsave(&sst->spinlock, flags);
-
- ipcx = sst_dsp_ipc_msg_rx(hsw->dsp);
- ipcd = sst_dsp_shim_read_unlocked(sst, SST_IPCD);
-
- /* reply message from DSP */
- if (ipcx & SST_IPCX_DONE) {
-
- /* Handle Immediate reply from DSP Core */
- handled = hsw_process_reply(hsw, ipcx);
-
- if (handled > 0) {
- /* clear DONE bit - tell DSP we have completed */
- sst_dsp_shim_update_bits_unlocked(sst, SST_IPCX,
- SST_IPCX_DONE, 0);
-
- /* unmask Done interrupt */
- sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
- SST_IMRX_DONE, 0);
- }
- }
-
- /* new message from DSP */
- if (ipcd & SST_IPCD_BUSY) {
-
- /* Handle Notification and Delayed reply from DSP Core */
- handled = hsw_process_notification(hsw);
-
- /* clear BUSY bit and set DONE bit - accept new messages */
- if (handled > 0) {
- sst_dsp_shim_update_bits_unlocked(sst, SST_IPCD,
- SST_IPCD_BUSY | SST_IPCD_DONE, SST_IPCD_DONE);
-
- /* unmask busy interrupt */
- sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX,
- SST_IMRX_BUSY, 0);
- }
- }
-
- spin_unlock_irqrestore(&sst->spinlock, flags);
-
- /* continue to send any remaining messages... */
- queue_kthread_work(&hsw->kworker, &hsw->kwork);
-
- return IRQ_HANDLED;
-}
-
-int sst_hsw_fw_get_version(struct sst_hsw *hsw,
- struct sst_hsw_ipc_fw_version *version)
-{
- int ret;
-
- ret = ipc_tx_message_wait(hsw, IPC_GLB_TYPE(IPC_GLB_GET_FW_VERSION),
- NULL, 0, version, sizeof(*version));
- if (ret < 0)
- dev_err(hsw->dev, "error: get version failed\n");
-
- return ret;
-}
-
-/* Mixer Controls */
-int sst_hsw_stream_get_volume(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- u32 stage_id, u32 channel, u32 *volume)
-{
- if (channel > 1)
- return -EINVAL;
-
- sst_dsp_read(hsw->dsp, volume,
- stream->reply.volume_register_address[channel],
- sizeof(*volume));
-
- return 0;
-}
-
-/* stream volume */
-int sst_hsw_stream_set_volume(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 volume)
-{
- struct sst_hsw_ipc_volume_req *req;
- u32 header;
- int ret;
-
- trace_ipc_request("set stream volume", stream->reply.stream_hw_id);
-
- if (channel >= 2 && channel != SST_HSW_CHANNELS_ALL)
- return -EINVAL;
-
- header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) |
- IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE);
- header |= (stream->reply.stream_hw_id << IPC_STR_ID_SHIFT);
- header |= (IPC_STG_SET_VOLUME << IPC_STG_TYPE_SHIFT);
- header |= (stage_id << IPC_STG_ID_SHIFT);
-
- req = &stream->vol_req;
- req->target_volume = volume;
-
- /* set both at same time ? */
- if (channel == SST_HSW_CHANNELS_ALL) {
- if (hsw->mute[0] && hsw->mute[1]) {
- hsw->mute_volume[0] = hsw->mute_volume[1] = volume;
- return 0;
- } else if (hsw->mute[0])
- req->channel = 1;
- else if (hsw->mute[1])
- req->channel = 0;
- else
- req->channel = SST_HSW_CHANNELS_ALL;
- } else {
- /* set only 1 channel */
- if (hsw->mute[channel]) {
- hsw->mute_volume[channel] = volume;
- return 0;
- }
- req->channel = channel;
- }
-
- ret = ipc_tx_message_wait(hsw, header, req, sizeof(*req), NULL, 0);
- if (ret < 0) {
- dev_err(hsw->dev, "error: set stream volume failed\n");
- return ret;
- }
-
- return 0;
-}
-
-int sst_hsw_mixer_get_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
- u32 *volume)
-{
- if (channel > 1)
- return -EINVAL;
-
- sst_dsp_read(hsw->dsp, volume,
- hsw->mixer_info.volume_register_address[channel],
- sizeof(*volume));
-
- return 0;
-}
-
-/* global mixer volume */
-int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
- u32 volume)
-{
- struct sst_hsw_ipc_volume_req req;
- u32 header;
- int ret;
-
- trace_ipc_request("set mixer volume", volume);
-
- if (channel >= 2 && channel != SST_HSW_CHANNELS_ALL)
- return -EINVAL;
-
- /* set both at same time ? */
- if (channel == SST_HSW_CHANNELS_ALL) {
- if (hsw->mute[0] && hsw->mute[1]) {
- hsw->mute_volume[0] = hsw->mute_volume[1] = volume;
- return 0;
- } else if (hsw->mute[0])
- req.channel = 1;
- else if (hsw->mute[1])
- req.channel = 0;
- else
- req.channel = SST_HSW_CHANNELS_ALL;
- } else {
- /* set only 1 channel */
- if (hsw->mute[channel]) {
- hsw->mute_volume[channel] = volume;
- return 0;
- }
- req.channel = channel;
- }
-
- header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) |
- IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE);
- header |= (hsw->mixer_info.mixer_hw_id << IPC_STR_ID_SHIFT);
- header |= (IPC_STG_SET_VOLUME << IPC_STG_TYPE_SHIFT);
- header |= (stage_id << IPC_STG_ID_SHIFT);
-
- req.curve_duration = hsw->curve_duration;
- req.curve_type = hsw->curve_type;
- req.target_volume = volume;
-
- ret = ipc_tx_message_wait(hsw, header, &req, sizeof(req), NULL, 0);
- if (ret < 0) {
- dev_err(hsw->dev, "error: set mixer volume failed\n");
- return ret;
- }
-
- return 0;
-}
-
-/* Stream API */
-struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id,
- u32 (*notify_position)(struct sst_hsw_stream *stream, void *data),
- void *data)
-{
- struct sst_hsw_stream *stream;
- struct sst_dsp *sst = hsw->dsp;
- unsigned long flags;
-
- stream = kzalloc(sizeof(*stream), GFP_KERNEL);
- if (stream == NULL)
- return NULL;
-
- spin_lock_irqsave(&sst->spinlock, flags);
- stream->reply.stream_hw_id = INVALID_STREAM_HW_ID;
- list_add(&stream->node, &hsw->stream_list);
- stream->notify_position = notify_position;
- stream->pdata = data;
- stream->hsw = hsw;
- stream->host_id = id;
-
- /* work to process notification messages */
- INIT_WORK(&stream->notify_work, hsw_notification_work);
- spin_unlock_irqrestore(&sst->spinlock, flags);
-
- return stream;
-}
-
-int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
-{
- u32 header;
- int ret = 0;
- struct sst_dsp *sst = hsw->dsp;
- unsigned long flags;
-
- if (!stream) {
- dev_warn(hsw->dev, "warning: stream is NULL, no stream to free, ignore it.\n");
- return 0;
- }
-
- /* dont free DSP streams that are not commited */
- if (!stream->commited)
- goto out;
-
- trace_ipc_request("stream free", stream->host_id);
-
- stream->free_req.stream_id = stream->reply.stream_hw_id;
- header = IPC_GLB_TYPE(IPC_GLB_FREE_STREAM);
-
- ret = ipc_tx_message_wait(hsw, header, &stream->free_req,
- sizeof(stream->free_req), NULL, 0);
- if (ret < 0) {
- dev_err(hsw->dev, "error: free stream %d failed\n",
- stream->free_req.stream_id);
- return -EAGAIN;
- }
-
- trace_hsw_stream_free_req(stream, &stream->free_req);
-
-out:
- cancel_work_sync(&stream->notify_work);
- spin_lock_irqsave(&sst->spinlock, flags);
- list_del(&stream->node);
- kfree(stream);
- spin_unlock_irqrestore(&sst->spinlock, flags);
-
- return ret;
-}
-
-int sst_hsw_stream_set_bits(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, enum sst_hsw_bitdepth bits)
-{
- if (stream->commited) {
- dev_err(hsw->dev, "error: stream committed for set bits\n");
- return -EINVAL;
- }
-
- stream->request.format.bitdepth = bits;
- return 0;
-}
-
-int sst_hsw_stream_set_channels(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, int channels)
-{
- if (stream->commited) {
- dev_err(hsw->dev, "error: stream committed for set channels\n");
- return -EINVAL;
- }
-
- stream->request.format.ch_num = channels;
- return 0;
-}
-
-int sst_hsw_stream_set_rate(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, int rate)
-{
- if (stream->commited) {
- dev_err(hsw->dev, "error: stream committed for set rate\n");
- return -EINVAL;
- }
-
- stream->request.format.frequency = rate;
- return 0;
-}
-
-int sst_hsw_stream_set_map_config(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, u32 map,
- enum sst_hsw_channel_config config)
-{
- if (stream->commited) {
- dev_err(hsw->dev, "error: stream committed for set map\n");
- return -EINVAL;
- }
-
- stream->request.format.map = map;
- stream->request.format.config = config;
- return 0;
-}
-
-int sst_hsw_stream_set_style(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, enum sst_hsw_interleaving style)
-{
- if (stream->commited) {
- dev_err(hsw->dev, "error: stream committed for set style\n");
- return -EINVAL;
- }
-
- stream->request.format.style = style;
- return 0;
-}
-
-int sst_hsw_stream_set_valid(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, u32 bits)
-{
- if (stream->commited) {
- dev_err(hsw->dev, "error: stream committed for set valid bits\n");
- return -EINVAL;
- }
-
- stream->request.format.valid_bit = bits;
- return 0;
-}
-
-/* Stream Configuration */
-int sst_hsw_stream_format(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- enum sst_hsw_stream_path_id path_id,
- enum sst_hsw_stream_type stream_type,
- enum sst_hsw_stream_format format_id)
-{
- if (stream->commited) {
- dev_err(hsw->dev, "error: stream committed for set format\n");
- return -EINVAL;
- }
-
- stream->request.path_id = path_id;
- stream->request.stream_type = stream_type;
- stream->request.format_id = format_id;
-
- trace_hsw_stream_alloc_request(stream, &stream->request);
-
- return 0;
-}
-
-int sst_hsw_stream_buffer(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- u32 ring_pt_address, u32 num_pages,
- u32 ring_size, u32 ring_offset, u32 ring_first_pfn)
-{
- if (stream->commited) {
- dev_err(hsw->dev, "error: stream committed for buffer\n");
- return -EINVAL;
- }
-
- stream->request.ringinfo.ring_pt_address = ring_pt_address;
- stream->request.ringinfo.num_pages = num_pages;
- stream->request.ringinfo.ring_size = ring_size;
- stream->request.ringinfo.ring_offset = ring_offset;
- stream->request.ringinfo.ring_first_pfn = ring_first_pfn;
-
- trace_hsw_stream_buffer(stream);
-
- return 0;
-}
-
-int sst_hsw_stream_set_module_info(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, struct sst_module_runtime *runtime)
-{
- struct sst_hsw_module_map *map = &stream->request.map;
- struct sst_dsp *dsp = sst_hsw_get_dsp(hsw);
- struct sst_module *module = runtime->module;
-
- if (stream->commited) {
- dev_err(hsw->dev, "error: stream committed for set module\n");
- return -EINVAL;
- }
-
- /* only support initial module atm */
- map->module_entries_count = 1;
- map->module_entries[0].module_id = module->id;
- map->module_entries[0].entry_point = module->entry;
-
- stream->request.persistent_mem.offset =
- sst_dsp_get_offset(dsp, runtime->persistent_offset, SST_MEM_DRAM);
- stream->request.persistent_mem.size = module->persistent_size;
-
- stream->request.scratch_mem.offset =
- sst_dsp_get_offset(dsp, dsp->scratch_offset, SST_MEM_DRAM);
- stream->request.scratch_mem.size = dsp->scratch_size;
-
- dev_dbg(hsw->dev, "module %d runtime %d using:\n", module->id,
- runtime->id);
- dev_dbg(hsw->dev, " persistent offset 0x%x bytes 0x%x\n",
- stream->request.persistent_mem.offset,
- stream->request.persistent_mem.size);
- dev_dbg(hsw->dev, " scratch offset 0x%x bytes 0x%x\n",
- stream->request.scratch_mem.offset,
- stream->request.scratch_mem.size);
-
- return 0;
-}
-
-int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
-{
- struct sst_hsw_ipc_stream_alloc_req *str_req = &stream->request;
- struct sst_hsw_ipc_stream_alloc_reply *reply = &stream->reply;
- u32 header;
- int ret;
-
- if (!stream) {
- dev_warn(hsw->dev, "warning: stream is NULL, no stream to commit, ignore it.\n");
- return 0;
- }
-
- if (stream->commited) {
- dev_warn(hsw->dev, "warning: stream is already committed, ignore it.\n");
- return 0;
- }
-
- trace_ipc_request("stream alloc", stream->host_id);
-
- header = IPC_GLB_TYPE(IPC_GLB_ALLOCATE_STREAM);
-
- ret = ipc_tx_message_wait(hsw, header, str_req, sizeof(*str_req),
- reply, sizeof(*reply));
- if (ret < 0) {
- dev_err(hsw->dev, "error: stream commit failed\n");
- return ret;
- }
-
- stream->commited = 1;
- trace_hsw_stream_alloc_reply(stream);
-
- return 0;
-}
-
-snd_pcm_uframes_t sst_hsw_stream_get_old_position(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream)
-{
- return stream->old_position;
-}
-
-void sst_hsw_stream_set_old_position(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, snd_pcm_uframes_t val)
-{
- stream->old_position = val;
-}
-
-bool sst_hsw_stream_get_silence_start(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream)
-{
- return stream->play_silence;
-}
-
-void sst_hsw_stream_set_silence_start(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, bool val)
-{
- stream->play_silence = val;
-}
-
-/* Stream Information - these calls could be inline but we want the IPC
- ABI to be opaque to client PCM drivers to cope with any future ABI changes */
-int sst_hsw_mixer_get_info(struct sst_hsw *hsw)
-{
- struct sst_hsw_ipc_stream_info_reply *reply;
- u32 header;
- int ret;
-
- reply = &hsw->mixer_info;
- header = IPC_GLB_TYPE(IPC_GLB_GET_MIXER_STREAM_INFO);
-
- trace_ipc_request("get global mixer info", 0);
-
- ret = ipc_tx_message_wait(hsw, header, NULL, 0, reply, sizeof(*reply));
- if (ret < 0) {
- dev_err(hsw->dev, "error: get stream info failed\n");
- return ret;
- }
-
- trace_hsw_mixer_info_reply(reply);
-
- return 0;
-}
-
-/* Send stream command */
-static int sst_hsw_stream_operations(struct sst_hsw *hsw, int type,
- int stream_id, int wait)
-{
- u32 header;
-
- header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) | IPC_STR_TYPE(type);
- header |= (stream_id << IPC_STR_ID_SHIFT);
-
- if (wait)
- return ipc_tx_message_wait(hsw, header, NULL, 0, NULL, 0);
- else
- return ipc_tx_message_nowait(hsw, header, NULL, 0);
-}
-
-/* Stream ALSA trigger operations */
-int sst_hsw_stream_pause(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- int wait)
-{
- int ret;
-
- if (!stream) {
- dev_warn(hsw->dev, "warning: stream is NULL, no stream to pause, ignore it.\n");
- return 0;
- }
-
- trace_ipc_request("stream pause", stream->reply.stream_hw_id);
-
- ret = sst_hsw_stream_operations(hsw, IPC_STR_PAUSE,
- stream->reply.stream_hw_id, wait);
- if (ret < 0)
- dev_err(hsw->dev, "error: failed to pause stream %d\n",
- stream->reply.stream_hw_id);
-
- return ret;
-}
-
-int sst_hsw_stream_resume(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- int wait)
-{
- int ret;
-
- if (!stream) {
- dev_warn(hsw->dev, "warning: stream is NULL, no stream to resume, ignore it.\n");
- return 0;
- }
-
- trace_ipc_request("stream resume", stream->reply.stream_hw_id);
-
- ret = sst_hsw_stream_operations(hsw, IPC_STR_RESUME,
- stream->reply.stream_hw_id, wait);
- if (ret < 0)
- dev_err(hsw->dev, "error: failed to resume stream %d\n",
- stream->reply.stream_hw_id);
-
- return ret;
-}
-
-int sst_hsw_stream_reset(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
-{
- int ret, tries = 10;
-
- if (!stream) {
- dev_warn(hsw->dev, "warning: stream is NULL, no stream to reset, ignore it.\n");
- return 0;
- }
-
- /* dont reset streams that are not commited */
- if (!stream->commited)
- return 0;
-
- /* wait for pause to complete before we reset the stream */
- while (stream->running && tries--)
- msleep(1);
- if (!tries) {
- dev_err(hsw->dev, "error: reset stream %d still running\n",
- stream->reply.stream_hw_id);
- return -EINVAL;
- }
-
- trace_ipc_request("stream reset", stream->reply.stream_hw_id);
-
- ret = sst_hsw_stream_operations(hsw, IPC_STR_RESET,
- stream->reply.stream_hw_id, 1);
- if (ret < 0)
- dev_err(hsw->dev, "error: failed to reset stream %d\n",
- stream->reply.stream_hw_id);
- return ret;
-}
-
-/* Stream pointer positions */
-u32 sst_hsw_get_dsp_position(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream)
-{
- u32 rpos;
-
- sst_dsp_read(hsw->dsp, &rpos,
- stream->reply.read_position_register_address, sizeof(rpos));
-
- return rpos;
-}
-
-/* Stream presentation (monotonic) positions */
-u64 sst_hsw_get_dsp_presentation_position(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream)
-{
- u64 ppos;
-
- sst_dsp_read(hsw->dsp, &ppos,
- stream->reply.presentation_position_register_address,
- sizeof(ppos));
-
- return ppos;
-}
-
-/* physical BE config */
-int sst_hsw_device_set_config(struct sst_hsw *hsw,
- enum sst_hsw_device_id dev, enum sst_hsw_device_mclk mclk,
- enum sst_hsw_device_mode mode, u32 clock_divider)
-{
- struct sst_hsw_ipc_device_config_req config;
- u32 header;
- int ret;
-
- trace_ipc_request("set device config", dev);
-
- config.ssp_interface = dev;
- config.clock_frequency = mclk;
- config.mode = mode;
- config.clock_divider = clock_divider;
- if (mode == SST_HSW_DEVICE_TDM_CLOCK_MASTER)
- config.channels = 4;
- else
- config.channels = 2;
-
- trace_hsw_device_config_req(&config);
-
- header = IPC_GLB_TYPE(IPC_GLB_SET_DEVICE_FORMATS);
-
- ret = ipc_tx_message_wait(hsw, header, &config, sizeof(config),
- NULL, 0);
- if (ret < 0)
- dev_err(hsw->dev, "error: set device formats failed\n");
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(sst_hsw_device_set_config);
-
-/* DX Config */
-int sst_hsw_dx_set_state(struct sst_hsw *hsw,
- enum sst_hsw_dx_state state, struct sst_hsw_ipc_dx_reply *dx)
-{
- u32 header, state_;
- int ret, item;
-
- header = IPC_GLB_TYPE(IPC_GLB_ENTER_DX_STATE);
- state_ = state;
-
- trace_ipc_request("PM enter Dx state", state);
-
- ret = ipc_tx_message_wait(hsw, header, &state_, sizeof(state_),
- dx, sizeof(*dx));
- if (ret < 0) {
- dev_err(hsw->dev, "ipc: error set dx state %d failed\n", state);
- return ret;
- }
-
- for (item = 0; item < dx->entries_no; item++) {
- dev_dbg(hsw->dev,
- "Item[%d] offset[%x] - size[%x] - source[%x]\n",
- item, dx->mem_info[item].offset,
- dx->mem_info[item].size,
- dx->mem_info[item].source);
- }
- dev_dbg(hsw->dev, "ipc: got %d entry numbers for state %d\n",
- dx->entries_no, state);
-
- return ret;
-}
-
-struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw,
- int mod_id, int offset)
-{
- struct sst_dsp *dsp = hsw->dsp;
- struct sst_module *module;
- struct sst_module_runtime *runtime;
- int err;
-
- module = sst_module_get_from_id(dsp, mod_id);
- if (module == NULL) {
- dev_err(dsp->dev, "error: failed to get module %d for pcm\n",
- mod_id);
- return NULL;
- }
-
- runtime = sst_module_runtime_new(module, mod_id, NULL);
- if (runtime == NULL) {
- dev_err(dsp->dev, "error: failed to create module %d runtime\n",
- mod_id);
- return NULL;
- }
-
- err = sst_module_runtime_alloc_blocks(runtime, offset);
- if (err < 0) {
- dev_err(dsp->dev, "error: failed to alloc blocks for module %d runtime\n",
- mod_id);
- sst_module_runtime_free(runtime);
- return NULL;
- }
-
- dev_dbg(dsp->dev, "runtime id %d created for module %d\n", runtime->id,
- mod_id);
- return runtime;
-}
-
-void sst_hsw_runtime_module_free(struct sst_module_runtime *runtime)
-{
- sst_module_runtime_free_blocks(runtime);
- sst_module_runtime_free(runtime);
-}
-
-#ifdef CONFIG_PM
-static int sst_hsw_dx_state_dump(struct sst_hsw *hsw)
-{
- struct sst_dsp *sst = hsw->dsp;
- u32 item, offset, size;
- int ret = 0;
-
- trace_ipc_request("PM state dump. Items #", SST_HSW_MAX_DX_REGIONS);
-
- if (hsw->dx.entries_no > SST_HSW_MAX_DX_REGIONS) {
- dev_err(hsw->dev,
- "error: number of FW context regions greater than %d\n",
- SST_HSW_MAX_DX_REGIONS);
- memset(&hsw->dx, 0, sizeof(hsw->dx));
- return -EINVAL;
- }
-
- ret = sst_dsp_dma_get_channel(sst, 0);
- if (ret < 0) {
- dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
- return ret;
- }
-
- /* set on-demond mode on engine 0 channel 3 */
- sst_dsp_shim_update_bits(sst, SST_HMDC,
- SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH,
- SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH);
-
- for (item = 0; item < hsw->dx.entries_no; item++) {
- if (hsw->dx.mem_info[item].source == SST_HSW_DX_TYPE_MEMORY_DUMP
- && hsw->dx.mem_info[item].offset > DSP_DRAM_ADDR_OFFSET
- && hsw->dx.mem_info[item].offset <
- DSP_DRAM_ADDR_OFFSET + SST_HSW_DX_CONTEXT_SIZE) {
-
- offset = hsw->dx.mem_info[item].offset
- - DSP_DRAM_ADDR_OFFSET;
- size = (hsw->dx.mem_info[item].size + 3) & (~3);
-
- ret = sst_dsp_dma_copyfrom(sst, hsw->dx_context_paddr + offset,
- sst->addr.lpe_base + offset, size);
- if (ret < 0) {
- dev_err(hsw->dev,
- "error: FW context dump failed\n");
- memset(&hsw->dx, 0, sizeof(hsw->dx));
- goto out;
- }
- }
- }
-
-out:
- sst_dsp_dma_put_channel(sst);
- return ret;
-}
-
-static int sst_hsw_dx_state_restore(struct sst_hsw *hsw)
-{
- struct sst_dsp *sst = hsw->dsp;
- u32 item, offset, size;
- int ret;
-
- for (item = 0; item < hsw->dx.entries_no; item++) {
- if (hsw->dx.mem_info[item].source == SST_HSW_DX_TYPE_MEMORY_DUMP
- && hsw->dx.mem_info[item].offset > DSP_DRAM_ADDR_OFFSET
- && hsw->dx.mem_info[item].offset <
- DSP_DRAM_ADDR_OFFSET + SST_HSW_DX_CONTEXT_SIZE) {
-
- offset = hsw->dx.mem_info[item].offset
- - DSP_DRAM_ADDR_OFFSET;
- size = (hsw->dx.mem_info[item].size + 3) & (~3);
-
- ret = sst_dsp_dma_copyto(sst, sst->addr.lpe_base + offset,
- hsw->dx_context_paddr + offset, size);
- if (ret < 0) {
- dev_err(hsw->dev,
- "error: FW context restore failed\n");
- return ret;
- }
- }
- }
-
- return 0;
-}
-
-static void sst_hsw_drop_all(struct sst_hsw *hsw)
-{
- struct ipc_message *msg, *tmp;
- unsigned long flags;
- int tx_drop_cnt = 0, rx_drop_cnt = 0;
-
- /* drop all TX and Rx messages before we stall + reset DSP */
- spin_lock_irqsave(&hsw->dsp->spinlock, flags);
-
- list_for_each_entry_safe(msg, tmp, &hsw->tx_list, list) {
- list_move(&msg->list, &hsw->empty_list);
- tx_drop_cnt++;
- }
-
- list_for_each_entry_safe(msg, tmp, &hsw->rx_list, list) {
- list_move(&msg->list, &hsw->empty_list);
- rx_drop_cnt++;
- }
-
- spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
-
- if (tx_drop_cnt || rx_drop_cnt)
- dev_err(hsw->dev, "dropped IPC msg RX=%d, TX=%d\n",
- tx_drop_cnt, rx_drop_cnt);
-}
-
-int sst_hsw_dsp_load(struct sst_hsw *hsw)
-{
- struct sst_dsp *dsp = hsw->dsp;
- int ret;
-
- dev_dbg(hsw->dev, "loading audio DSP....");
-
- ret = sst_dsp_wake(dsp);
- if (ret < 0) {
- dev_err(hsw->dev, "error: failed to wake audio DSP\n");
- return -ENODEV;
- }
-
- ret = sst_dsp_dma_get_channel(dsp, 0);
- if (ret < 0) {
- dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
- return ret;
- }
-
- ret = sst_fw_reload(hsw->sst_fw);
- if (ret < 0) {
- dev_err(hsw->dev, "error: SST FW reload failed\n");
- sst_dsp_dma_put_channel(dsp);
- return -ENOMEM;
- }
-
- sst_dsp_dma_put_channel(dsp);
- return 0;
-}
-
-static int sst_hsw_dsp_restore(struct sst_hsw *hsw)
-{
- struct sst_dsp *dsp = hsw->dsp;
- int ret;
-
- dev_dbg(hsw->dev, "restoring audio DSP....");
-
- ret = sst_dsp_dma_get_channel(dsp, 0);
- if (ret < 0) {
- dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
- return ret;
- }
-
- ret = sst_hsw_dx_state_restore(hsw);
- if (ret < 0) {
- dev_err(hsw->dev, "error: SST FW context restore failed\n");
- sst_dsp_dma_put_channel(dsp);
- return -ENOMEM;
- }
- sst_dsp_dma_put_channel(dsp);
-
- /* wait for DSP boot completion */
- sst_dsp_boot(dsp);
-
- return ret;
-}
-
-int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw)
-{
- int ret;
-
- dev_dbg(hsw->dev, "audio dsp runtime suspend\n");
-
- ret = sst_hsw_dx_set_state(hsw, SST_HSW_DX_STATE_D3, &hsw->dx);
- if (ret < 0)
- return ret;
-
- sst_dsp_stall(hsw->dsp);
-
- ret = sst_hsw_dx_state_dump(hsw);
- if (ret < 0)
- return ret;
-
- sst_hsw_drop_all(hsw);
-
- return 0;
-}
-
-int sst_hsw_dsp_runtime_sleep(struct sst_hsw *hsw)
-{
- sst_fw_unload(hsw->sst_fw);
- sst_block_free_scratch(hsw->dsp);
-
- hsw->boot_complete = false;
-
- sst_dsp_sleep(hsw->dsp);
-
- return 0;
-}
-
-int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw)
-{
- struct device *dev = hsw->dev;
- int ret;
-
- dev_dbg(dev, "audio dsp runtime resume\n");
-
- if (hsw->boot_complete)
- return 1; /* tell caller no action is required */
-
- ret = sst_hsw_dsp_restore(hsw);
- if (ret < 0)
- dev_err(dev, "error: audio DSP boot failure\n");
-
- ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete,
- msecs_to_jiffies(IPC_BOOT_MSECS));
- if (ret == 0) {
- dev_err(hsw->dev, "error: audio DSP boot timeout IPCD 0x%x IPCX 0x%x\n",
- sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCD),
- sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX));
- return -EIO;
- }
-
- /* Set ADSP SSP port settings */
- ret = sst_hsw_device_set_config(hsw, SST_HSW_DEVICE_SSP_0,
- SST_HSW_DEVICE_MCLK_FREQ_24_MHZ,
- SST_HSW_DEVICE_CLOCK_MASTER, 9);
- if (ret < 0)
- dev_err(dev, "error: SSP re-initialization failed\n");
-
- return ret;
-}
-#endif
-
-static int msg_empty_list_init(struct sst_hsw *hsw)
-{
- int i;
-
- hsw->msg = kzalloc(sizeof(struct ipc_message) *
- IPC_EMPTY_LIST_SIZE, GFP_KERNEL);
- if (hsw->msg == NULL)
- return -ENOMEM;
-
- for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
- init_waitqueue_head(&hsw->msg[i].waitq);
- list_add(&hsw->msg[i].list, &hsw->empty_list);
- }
-
- return 0;
-}
-
-struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw)
-{
- return hsw->dsp;
-}
-
-static struct sst_dsp_device hsw_dev = {
- .thread = hsw_irq_thread,
- .ops = &haswell_ops,
-};
-
-int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
-{
- struct sst_hsw_ipc_fw_version version;
- struct sst_hsw *hsw;
- int ret;
-
- dev_dbg(dev, "initialising Audio DSP IPC\n");
-
- hsw = devm_kzalloc(dev, sizeof(*hsw), GFP_KERNEL);
- if (hsw == NULL)
- return -ENOMEM;
-
- hsw->dev = dev;
- INIT_LIST_HEAD(&hsw->stream_list);
- INIT_LIST_HEAD(&hsw->tx_list);
- INIT_LIST_HEAD(&hsw->rx_list);
- INIT_LIST_HEAD(&hsw->empty_list);
- init_waitqueue_head(&hsw->boot_wait);
- init_waitqueue_head(&hsw->wait_txq);
-
- ret = msg_empty_list_init(hsw);
- if (ret < 0)
- return -ENOMEM;
-
- /* start the IPC message thread */
- init_kthread_worker(&hsw->kworker);
- hsw->tx_thread = kthread_run(kthread_worker_fn,
- &hsw->kworker, "%s",
- dev_name(hsw->dev));
- if (IS_ERR(hsw->tx_thread)) {
- ret = PTR_ERR(hsw->tx_thread);
- dev_err(hsw->dev, "error: failed to create message TX task\n");
- goto err_free_msg;
- }
- init_kthread_work(&hsw->kwork, ipc_tx_msgs);
-
- hsw_dev.thread_context = hsw;
-
- /* init SST shim */
- hsw->dsp = sst_dsp_new(dev, &hsw_dev, pdata);
- if (hsw->dsp == NULL) {
- ret = -ENODEV;
- goto dsp_err;
- }
-
- /* allocate DMA buffer for context storage */
- hsw->dx_context = dma_alloc_coherent(hsw->dsp->dma_dev,
- SST_HSW_DX_CONTEXT_SIZE, &hsw->dx_context_paddr, GFP_KERNEL);
- if (hsw->dx_context == NULL) {
- ret = -ENOMEM;
- goto dma_err;
- }
-
- /* keep the DSP in reset state for base FW loading */
- sst_dsp_reset(hsw->dsp);
-
- hsw->sst_fw = sst_fw_new(hsw->dsp, pdata->fw, hsw);
- if (hsw->sst_fw == NULL) {
- ret = -ENODEV;
- dev_err(dev, "error: failed to load firmware\n");
- goto fw_err;
- }
-
- /* wait for DSP boot completion */
- sst_dsp_boot(hsw->dsp);
- ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete,
- msecs_to_jiffies(IPC_BOOT_MSECS));
- if (ret == 0) {
- ret = -EIO;
- dev_err(hsw->dev, "error: audio DSP boot timeout IPCD 0x%x IPCX 0x%x\n",
- sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCD),
- sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX));
- goto boot_err;
- }
-
- /* get the FW version */
- sst_hsw_fw_get_version(hsw, &version);
-
- /* get the globalmixer */
- ret = sst_hsw_mixer_get_info(hsw);
- if (ret < 0) {
- dev_err(hsw->dev, "error: failed to get stream info\n");
- goto boot_err;
- }
-
- pdata->dsp = hsw;
- return 0;
-
-boot_err:
- sst_dsp_reset(hsw->dsp);
- sst_fw_free(hsw->sst_fw);
-fw_err:
- dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE,
- hsw->dx_context, hsw->dx_context_paddr);
-dma_err:
- sst_dsp_free(hsw->dsp);
-dsp_err:
- kthread_stop(hsw->tx_thread);
-err_free_msg:
- kfree(hsw->msg);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(sst_hsw_dsp_init);
-
-void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata)
-{
- struct sst_hsw *hsw = pdata->dsp;
-
- sst_dsp_reset(hsw->dsp);
- sst_fw_free_all(hsw->dsp);
- dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE,
- hsw->dx_context, hsw->dx_context_paddr);
- sst_dsp_free(hsw->dsp);
- kthread_stop(hsw->tx_thread);
- kfree(hsw->msg);
-}
-EXPORT_SYMBOL_GPL(sst_hsw_dsp_free);
+++ /dev/null
-/*
- * Intel SST Haswell/Broadwell IPC Support
- *
- * Copyright (C) 2013, Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#ifndef __SST_HASWELL_IPC_H
-#define __SST_HASWELL_IPC_H
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <sound/asound.h>
-
-#define SST_HSW_NO_CHANNELS 4
-#define SST_HSW_MAX_DX_REGIONS 14
-#define SST_HSW_DX_CONTEXT_SIZE (640 * 1024)
-#define SST_HSW_CHANNELS_ALL 0xffffffff
-
-#define SST_HSW_FW_LOG_CONFIG_DWORDS 12
-#define SST_HSW_GLOBAL_LOG 15
-
-/**
- * Upfront defined maximum message size that is
- * expected by the in/out communication pipes in FW.
- */
-#define SST_HSW_IPC_MAX_PAYLOAD_SIZE 400
-#define SST_HSW_MAX_INFO_SIZE 64
-#define SST_HSW_BUILD_HASH_LENGTH 40
-
-struct sst_hsw;
-struct sst_hsw_stream;
-struct sst_hsw_log_stream;
-struct sst_pdata;
-struct sst_module;
-struct sst_module_runtime;
-extern struct sst_ops haswell_ops;
-
-/* Stream Allocate Path ID */
-enum sst_hsw_stream_path_id {
- SST_HSW_STREAM_PATH_SSP0_OUT = 0,
- SST_HSW_STREAM_PATH_SSP0_IN = 1,
- SST_HSW_STREAM_PATH_MAX_PATH_ID = 2,
-};
-
-/* Stream Allocate Stream Type */
-enum sst_hsw_stream_type {
- SST_HSW_STREAM_TYPE_RENDER = 0,
- SST_HSW_STREAM_TYPE_SYSTEM = 1,
- SST_HSW_STREAM_TYPE_CAPTURE = 2,
- SST_HSW_STREAM_TYPE_LOOPBACK = 3,
- SST_HSW_STREAM_TYPE_MAX_STREAM_TYPE = 4,
-};
-
-/* Stream Allocate Stream Format */
-enum sst_hsw_stream_format {
- SST_HSW_STREAM_FORMAT_PCM_FORMAT = 0,
- SST_HSW_STREAM_FORMAT_MP3_FORMAT = 1,
- SST_HSW_STREAM_FORMAT_AAC_FORMAT = 2,
- SST_HSW_STREAM_FORMAT_MAX_FORMAT_ID = 3,
-};
-
-/* Device ID */
-enum sst_hsw_device_id {
- SST_HSW_DEVICE_SSP_0 = 0,
- SST_HSW_DEVICE_SSP_1 = 1,
-};
-
-/* Device Master Clock Frequency */
-enum sst_hsw_device_mclk {
- SST_HSW_DEVICE_MCLK_OFF = 0,
- SST_HSW_DEVICE_MCLK_FREQ_6_MHZ = 1,
- SST_HSW_DEVICE_MCLK_FREQ_12_MHZ = 2,
- SST_HSW_DEVICE_MCLK_FREQ_24_MHZ = 3,
-};
-
-/* Device Clock Master */
-enum sst_hsw_device_mode {
- SST_HSW_DEVICE_CLOCK_SLAVE = 0,
- SST_HSW_DEVICE_CLOCK_MASTER = 1,
- SST_HSW_DEVICE_TDM_CLOCK_MASTER = 2,
-};
-
-/* DX Power State */
-enum sst_hsw_dx_state {
- SST_HSW_DX_STATE_D0 = 0,
- SST_HSW_DX_STATE_D1 = 1,
- SST_HSW_DX_STATE_D3 = 3,
- SST_HSW_DX_STATE_MAX = 3,
-};
-
-/* Audio stream stage IDs */
-enum sst_hsw_fx_stage_id {
- SST_HSW_STAGE_ID_WAVES = 0,
- SST_HSW_STAGE_ID_DTS = 1,
- SST_HSW_STAGE_ID_DOLBY = 2,
- SST_HSW_STAGE_ID_BOOST = 3,
- SST_HSW_STAGE_ID_MAX_FX_ID
-};
-
-/* DX State Type */
-enum sst_hsw_dx_type {
- SST_HSW_DX_TYPE_FW_IMAGE = 0,
- SST_HSW_DX_TYPE_MEMORY_DUMP = 1
-};
-
-/* Volume Curve Type*/
-enum sst_hsw_volume_curve {
- SST_HSW_VOLUME_CURVE_NONE = 0,
- SST_HSW_VOLUME_CURVE_FADE = 1
-};
-
-/* Sample ordering */
-enum sst_hsw_interleaving {
- SST_HSW_INTERLEAVING_PER_CHANNEL = 0,
- SST_HSW_INTERLEAVING_PER_SAMPLE = 1,
-};
-
-/* Channel indices */
-enum sst_hsw_channel_index {
- SST_HSW_CHANNEL_LEFT = 0,
- SST_HSW_CHANNEL_CENTER = 1,
- SST_HSW_CHANNEL_RIGHT = 2,
- SST_HSW_CHANNEL_LEFT_SURROUND = 3,
- SST_HSW_CHANNEL_CENTER_SURROUND = 3,
- SST_HSW_CHANNEL_RIGHT_SURROUND = 4,
- SST_HSW_CHANNEL_LFE = 7,
- SST_HSW_CHANNEL_INVALID = 0xF,
-};
-
-/* List of supported channel maps. */
-enum sst_hsw_channel_config {
- SST_HSW_CHANNEL_CONFIG_MONO = 0, /* mono only. */
- SST_HSW_CHANNEL_CONFIG_STEREO = 1, /* L & R. */
- SST_HSW_CHANNEL_CONFIG_2_POINT_1 = 2, /* L, R & LFE; PCM only. */
- SST_HSW_CHANNEL_CONFIG_3_POINT_0 = 3, /* L, C & R; MP3 & AAC only. */
- SST_HSW_CHANNEL_CONFIG_3_POINT_1 = 4, /* L, C, R & LFE; PCM only. */
- SST_HSW_CHANNEL_CONFIG_QUATRO = 5, /* L, R, Ls & Rs; PCM only. */
- SST_HSW_CHANNEL_CONFIG_4_POINT_0 = 6, /* L, C, R & Cs; MP3 & AAC only. */
- SST_HSW_CHANNEL_CONFIG_5_POINT_0 = 7, /* L, C, R, Ls & Rs. */
- SST_HSW_CHANNEL_CONFIG_5_POINT_1 = 8, /* L, C, R, Ls, Rs & LFE. */
- SST_HSW_CHANNEL_CONFIG_DUAL_MONO = 9, /* One channel replicated in two. */
- SST_HSW_CHANNEL_CONFIG_INVALID,
-};
-
-/* List of supported bit depths. */
-enum sst_hsw_bitdepth {
- SST_HSW_DEPTH_8BIT = 8,
- SST_HSW_DEPTH_16BIT = 16,
- SST_HSW_DEPTH_24BIT = 24, /* Default. */
- SST_HSW_DEPTH_32BIT = 32,
- SST_HSW_DEPTH_INVALID = 33,
-};
-
-enum sst_hsw_module_id {
- SST_HSW_MODULE_BASE_FW = 0x0,
- SST_HSW_MODULE_MP3 = 0x1,
- SST_HSW_MODULE_AAC_5_1 = 0x2,
- SST_HSW_MODULE_AAC_2_0 = 0x3,
- SST_HSW_MODULE_SRC = 0x4,
- SST_HSW_MODULE_WAVES = 0x5,
- SST_HSW_MODULE_DOLBY = 0x6,
- SST_HSW_MODULE_BOOST = 0x7,
- SST_HSW_MODULE_LPAL = 0x8,
- SST_HSW_MODULE_DTS = 0x9,
- SST_HSW_MODULE_PCM_CAPTURE = 0xA,
- SST_HSW_MODULE_PCM_SYSTEM = 0xB,
- SST_HSW_MODULE_PCM_REFERENCE = 0xC,
- SST_HSW_MODULE_PCM = 0xD,
- SST_HSW_MODULE_BLUETOOTH_RENDER_MODULE = 0xE,
- SST_HSW_MODULE_BLUETOOTH_CAPTURE_MODULE = 0xF,
- SST_HSW_MAX_MODULE_ID,
-};
-
-enum sst_hsw_performance_action {
- SST_HSW_PERF_START = 0,
- SST_HSW_PERF_STOP = 1,
-};
-
-/* SST firmware module info */
-struct sst_hsw_module_info {
- u8 name[SST_HSW_MAX_INFO_SIZE];
- u8 version[SST_HSW_MAX_INFO_SIZE];
-} __attribute__((packed));
-
-/* Module entry point */
-struct sst_hsw_module_entry {
- enum sst_hsw_module_id module_id;
- u32 entry_point;
-} __attribute__((packed));
-
-/* Module map - alignement matches DSP */
-struct sst_hsw_module_map {
- u8 module_entries_count;
- struct sst_hsw_module_entry module_entries[1];
-} __attribute__((packed));
-
-struct sst_hsw_memory_info {
- u32 offset;
- u32 size;
-} __attribute__((packed));
-
-struct sst_hsw_fx_enable {
- struct sst_hsw_module_map module_map;
- struct sst_hsw_memory_info persistent_mem;
-} __attribute__((packed));
-
-struct sst_hsw_get_fx_param {
- u32 parameter_id;
- u32 param_size;
-} __attribute__((packed));
-
-struct sst_hsw_perf_action {
- u32 action;
-} __attribute__((packed));
-
-struct sst_hsw_perf_data {
- u64 timestamp;
- u64 cycles;
- u64 datatime;
-} __attribute__((packed));
-
-/* FW version */
-struct sst_hsw_ipc_fw_version {
- u8 build;
- u8 minor;
- u8 major;
- u8 type;
- u8 fw_build_hash[SST_HSW_BUILD_HASH_LENGTH];
- u32 fw_log_providers_hash;
-} __attribute__((packed));
-
-/* Stream ring info */
-struct sst_hsw_ipc_stream_ring {
- u32 ring_pt_address;
- u32 num_pages;
- u32 ring_size;
- u32 ring_offset;
- u32 ring_first_pfn;
-} __attribute__((packed));
-
-/* Debug Dump Log Enable Request */
-struct sst_hsw_ipc_debug_log_enable_req {
- struct sst_hsw_ipc_stream_ring ringinfo;
- u32 config[SST_HSW_FW_LOG_CONFIG_DWORDS];
-} __attribute__((packed));
-
-/* Debug Dump Log Reply */
-struct sst_hsw_ipc_debug_log_reply {
- u32 log_buffer_begining;
- u32 log_buffer_size;
-} __attribute__((packed));
-
-/* Stream glitch position */
-struct sst_hsw_ipc_stream_glitch_position {
- u32 glitch_type;
- u32 present_pos;
- u32 write_pos;
-} __attribute__((packed));
-
-/* Stream get position */
-struct sst_hsw_ipc_stream_get_position {
- u32 position;
- u32 fw_cycle_count;
-} __attribute__((packed));
-
-/* Stream set position */
-struct sst_hsw_ipc_stream_set_position {
- u32 position;
- u32 end_of_buffer;
-} __attribute__((packed));
-
-/* Stream Free Request */
-struct sst_hsw_ipc_stream_free_req {
- u8 stream_id;
- u8 reserved[3];
-} __attribute__((packed));
-
-/* Set Volume Request */
-struct sst_hsw_ipc_volume_req {
- u32 channel;
- u32 target_volume;
- u64 curve_duration;
- u32 curve_type;
-} __attribute__((packed));
-
-/* Device Configuration Request */
-struct sst_hsw_ipc_device_config_req {
- u32 ssp_interface;
- u32 clock_frequency;
- u32 mode;
- u16 clock_divider;
- u8 channels;
- u8 reserved;
-} __attribute__((packed));
-
-/* Audio Data formats */
-struct sst_hsw_audio_data_format_ipc {
- u32 frequency;
- u32 bitdepth;
- u32 map;
- u32 config;
- u32 style;
- u8 ch_num;
- u8 valid_bit;
- u8 reserved[2];
-} __attribute__((packed));
-
-/* Stream Allocate Request */
-struct sst_hsw_ipc_stream_alloc_req {
- u8 path_id;
- u8 stream_type;
- u8 format_id;
- u8 reserved;
- struct sst_hsw_audio_data_format_ipc format;
- struct sst_hsw_ipc_stream_ring ringinfo;
- struct sst_hsw_module_map map;
- struct sst_hsw_memory_info persistent_mem;
- struct sst_hsw_memory_info scratch_mem;
- u32 number_of_notifications;
-} __attribute__((packed));
-
-/* Stream Allocate Reply */
-struct sst_hsw_ipc_stream_alloc_reply {
- u32 stream_hw_id;
- u32 mixer_hw_id; // returns rate ????
- u32 read_position_register_address;
- u32 presentation_position_register_address;
- u32 peak_meter_register_address[SST_HSW_NO_CHANNELS];
- u32 volume_register_address[SST_HSW_NO_CHANNELS];
-} __attribute__((packed));
-
-/* Get Mixer Stream Info */
-struct sst_hsw_ipc_stream_info_reply {
- u32 mixer_hw_id;
- u32 peak_meter_register_address[SST_HSW_NO_CHANNELS];
- u32 volume_register_address[SST_HSW_NO_CHANNELS];
-} __attribute__((packed));
-
-/* DX State Request */
-struct sst_hsw_ipc_dx_req {
- u8 state;
- u8 reserved[3];
-} __attribute__((packed));
-
-/* DX State Reply Memory Info Item */
-struct sst_hsw_ipc_dx_memory_item {
- u32 offset;
- u32 size;
- u32 source;
-} __attribute__((packed));
-
-/* DX State Reply */
-struct sst_hsw_ipc_dx_reply {
- u32 entries_no;
- struct sst_hsw_ipc_dx_memory_item mem_info[SST_HSW_MAX_DX_REGIONS];
-} __attribute__((packed));
-
-struct sst_hsw_ipc_fw_version;
-
-/* SST Init & Free */
-struct sst_hsw *sst_hsw_new(struct device *dev, const u8 *fw, size_t fw_length,
- u32 fw_offset);
-void sst_hsw_free(struct sst_hsw *hsw);
-int sst_hsw_fw_get_version(struct sst_hsw *hsw,
- struct sst_hsw_ipc_fw_version *version);
-u32 create_channel_map(enum sst_hsw_channel_config config);
-
-/* Stream Mixer Controls - */
-int sst_hsw_stream_set_volume(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 volume);
-int sst_hsw_stream_get_volume(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 *volume);
-
-/* Global Mixer Controls - */
-int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
- u32 volume);
-int sst_hsw_mixer_get_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
- u32 *volume);
-
-/* Stream API */
-struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id,
- u32 (*get_write_position)(struct sst_hsw_stream *stream, void *data),
- void *data);
-
-int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream);
-
-/* Stream Configuration */
-int sst_hsw_stream_format(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- enum sst_hsw_stream_path_id path_id,
- enum sst_hsw_stream_type stream_type,
- enum sst_hsw_stream_format format_id);
-
-int sst_hsw_stream_buffer(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- u32 ring_pt_address, u32 num_pages,
- u32 ring_size, u32 ring_offset, u32 ring_first_pfn);
-
-int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream);
-
-int sst_hsw_stream_set_valid(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- u32 bits);
-int sst_hsw_stream_set_rate(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- int rate);
-int sst_hsw_stream_set_bits(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- enum sst_hsw_bitdepth bits);
-int sst_hsw_stream_set_channels(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, int channels);
-int sst_hsw_stream_set_map_config(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, u32 map,
- enum sst_hsw_channel_config config);
-int sst_hsw_stream_set_style(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- enum sst_hsw_interleaving style);
-int sst_hsw_stream_set_module_info(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, struct sst_module_runtime *runtime);
-int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, u32 offset, u32 size);
-int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, u32 offset, u32 size);
-snd_pcm_uframes_t sst_hsw_stream_get_old_position(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream);
-void sst_hsw_stream_set_old_position(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, snd_pcm_uframes_t val);
-bool sst_hsw_stream_get_silence_start(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream);
-void sst_hsw_stream_set_silence_start(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, bool val);
-int sst_hsw_mixer_get_info(struct sst_hsw *hsw);
-
-/* Stream ALSA trigger operations */
-int sst_hsw_stream_pause(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- int wait);
-int sst_hsw_stream_resume(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
- int wait);
-int sst_hsw_stream_reset(struct sst_hsw *hsw, struct sst_hsw_stream *stream);
-
-/* Stream pointer positions */
-int sst_hsw_stream_get_read_pos(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, u32 *position);
-int sst_hsw_stream_get_write_pos(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, u32 *position);
-u32 sst_hsw_get_dsp_position(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream);
-u64 sst_hsw_get_dsp_presentation_position(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream);
-
-/* HW port config */
-int sst_hsw_device_set_config(struct sst_hsw *hsw,
- enum sst_hsw_device_id dev, enum sst_hsw_device_mclk mclk,
- enum sst_hsw_device_mode mode, u32 clock_divider);
-
-/* DX Config */
-int sst_hsw_dx_set_state(struct sst_hsw *hsw,
- enum sst_hsw_dx_state state, struct sst_hsw_ipc_dx_reply *dx);
-
-/* init */
-int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata);
-void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata);
-struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw);
-
-/* runtime module management */
-struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw,
- int mod_id, int offset);
-void sst_hsw_runtime_module_free(struct sst_module_runtime *runtime);
-
-/* PM */
-int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw);
-int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw);
-int sst_hsw_dsp_load(struct sst_hsw *hsw);
-int sst_hsw_dsp_runtime_sleep(struct sst_hsw *hsw);
-
-#endif
+++ /dev/null
-/*
- * Intel SST Haswell/Broadwell PCM Support
- *
- * Copyright (C) 2013, Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/module.h>
-#include <linux/dma-mapping.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/pm_runtime.h>
-#include <asm/page.h>
-#include <asm/pgtable.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/dmaengine_pcm.h>
-#include <sound/soc.h>
-#include <sound/tlv.h>
-#include <sound/compress_driver.h>
-
-#include "sst-haswell-ipc.h"
-#include "sst-dsp-priv.h"
-#include "sst-dsp.h"
-
-#define HSW_PCM_COUNT 6
-#define HSW_VOLUME_MAX 0x7FFFFFFF /* 0dB */
-
-#define SST_OLD_POSITION(d, r, o) ((d) + \
- frames_to_bytes(r, o))
-#define SST_SAMPLES(r, x) (bytes_to_samples(r, \
- frames_to_bytes(r, (x))))
-
-/* simple volume table */
-static const u32 volume_map[] = {
- HSW_VOLUME_MAX >> 30,
- HSW_VOLUME_MAX >> 29,
- HSW_VOLUME_MAX >> 28,
- HSW_VOLUME_MAX >> 27,
- HSW_VOLUME_MAX >> 26,
- HSW_VOLUME_MAX >> 25,
- HSW_VOLUME_MAX >> 24,
- HSW_VOLUME_MAX >> 23,
- HSW_VOLUME_MAX >> 22,
- HSW_VOLUME_MAX >> 21,
- HSW_VOLUME_MAX >> 20,
- HSW_VOLUME_MAX >> 19,
- HSW_VOLUME_MAX >> 18,
- HSW_VOLUME_MAX >> 17,
- HSW_VOLUME_MAX >> 16,
- HSW_VOLUME_MAX >> 15,
- HSW_VOLUME_MAX >> 14,
- HSW_VOLUME_MAX >> 13,
- HSW_VOLUME_MAX >> 12,
- HSW_VOLUME_MAX >> 11,
- HSW_VOLUME_MAX >> 10,
- HSW_VOLUME_MAX >> 9,
- HSW_VOLUME_MAX >> 8,
- HSW_VOLUME_MAX >> 7,
- HSW_VOLUME_MAX >> 6,
- HSW_VOLUME_MAX >> 5,
- HSW_VOLUME_MAX >> 4,
- HSW_VOLUME_MAX >> 3,
- HSW_VOLUME_MAX >> 2,
- HSW_VOLUME_MAX >> 1,
- HSW_VOLUME_MAX >> 0,
-};
-
-#define HSW_PCM_PERIODS_MAX 64
-#define HSW_PCM_PERIODS_MIN 2
-
-#define HSW_PCM_DAI_ID_SYSTEM 0
-#define HSW_PCM_DAI_ID_OFFLOAD0 1
-#define HSW_PCM_DAI_ID_OFFLOAD1 2
-#define HSW_PCM_DAI_ID_LOOPBACK 3
-
-
-static const struct snd_pcm_hardware hsw_pcm_hardware = {
- .info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_RESUME |
- SNDRV_PCM_INFO_NO_PERIOD_WAKEUP |
- SNDRV_PCM_INFO_DRAIN_TRIGGER,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S32_LE,
- .period_bytes_min = PAGE_SIZE,
- .period_bytes_max = (HSW_PCM_PERIODS_MAX / HSW_PCM_PERIODS_MIN) * PAGE_SIZE,
- .periods_min = HSW_PCM_PERIODS_MIN,
- .periods_max = HSW_PCM_PERIODS_MAX,
- .buffer_bytes_max = HSW_PCM_PERIODS_MAX * PAGE_SIZE,
-};
-
-struct hsw_pcm_module_map {
- int dai_id;
- int stream;
- enum sst_hsw_module_id mod_id;
-};
-
-/* private data for each PCM DSP stream */
-struct hsw_pcm_data {
- int dai_id;
- struct sst_hsw_stream *stream;
- struct sst_module_runtime *runtime;
- struct sst_module_runtime_context context;
- struct snd_pcm *hsw_pcm;
- u32 volume[2];
- struct snd_pcm_substream *substream;
- struct snd_compr_stream *cstream;
- unsigned int wpos;
- struct mutex mutex;
- bool allocated;
- int persistent_offset;
-};
-
-enum hsw_pm_state {
- HSW_PM_STATE_D0 = 0,
- HSW_PM_STATE_RTD3 = 1,
- HSW_PM_STATE_D3 = 2,
-};
-
-/* private data for the driver */
-struct hsw_priv_data {
- /* runtime DSP */
- struct sst_hsw *hsw;
- struct device *dev;
- enum hsw_pm_state pm_state;
- struct snd_soc_card *soc_card;
-
- /* page tables */
- struct snd_dma_buffer dmab[HSW_PCM_COUNT][2];
-
- /* DAI data */
- struct hsw_pcm_data pcm[HSW_PCM_COUNT][2];
-};
-
-
-/* static mappings between PCMs and modules - may be dynamic in future */
-static struct hsw_pcm_module_map mod_map[] = {
- {HSW_PCM_DAI_ID_SYSTEM, 0, SST_HSW_MODULE_PCM_SYSTEM},
- {HSW_PCM_DAI_ID_OFFLOAD0, 0, SST_HSW_MODULE_PCM},
- {HSW_PCM_DAI_ID_OFFLOAD1, 0, SST_HSW_MODULE_PCM},
- {HSW_PCM_DAI_ID_LOOPBACK, 1, SST_HSW_MODULE_PCM_REFERENCE},
- {HSW_PCM_DAI_ID_SYSTEM, 1, SST_HSW_MODULE_PCM_CAPTURE},
-};
-
-static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data);
-
-static inline u32 hsw_mixer_to_ipc(unsigned int value)
-{
- if (value >= ARRAY_SIZE(volume_map))
- return volume_map[0];
- else
- return volume_map[value];
-}
-
-static inline unsigned int hsw_ipc_to_mixer(u32 value)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(volume_map); i++) {
- if (volume_map[i] >= value)
- return i;
- }
-
- return i - 1;
-}
-
-static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- struct hsw_priv_data *pdata =
- snd_soc_platform_get_drvdata(platform);
- struct hsw_pcm_data *pcm_data;
- struct sst_hsw *hsw = pdata->hsw;
- u32 volume;
- int dai, stream;
-
- dai = mod_map[mc->reg].dai_id;
- stream = mod_map[mc->reg].stream;
- pcm_data = &pdata->pcm[dai][stream];
-
- mutex_lock(&pcm_data->mutex);
- pm_runtime_get_sync(pdata->dev);
-
- if (!pcm_data->stream) {
- pcm_data->volume[0] =
- hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
- pcm_data->volume[1] =
- hsw_mixer_to_ipc(ucontrol->value.integer.value[1]);
- pm_runtime_mark_last_busy(pdata->dev);
- pm_runtime_put_autosuspend(pdata->dev);
- mutex_unlock(&pcm_data->mutex);
- return 0;
- }
-
- if (ucontrol->value.integer.value[0] ==
- ucontrol->value.integer.value[1]) {
- volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
- /* apply volume value to all channels */
- sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, SST_HSW_CHANNELS_ALL, volume);
- } else {
- volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
- sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 0, volume);
- volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[1]);
- sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 1, volume);
- }
-
- pm_runtime_mark_last_busy(pdata->dev);
- pm_runtime_put_autosuspend(pdata->dev);
- mutex_unlock(&pcm_data->mutex);
- return 0;
-}
-
-static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- struct hsw_priv_data *pdata =
- snd_soc_platform_get_drvdata(platform);
- struct hsw_pcm_data *pcm_data;
- struct sst_hsw *hsw = pdata->hsw;
- u32 volume;
- int dai, stream;
-
- dai = mod_map[mc->reg].dai_id;
- stream = mod_map[mc->reg].stream;
- pcm_data = &pdata->pcm[dai][stream];
-
- mutex_lock(&pcm_data->mutex);
- pm_runtime_get_sync(pdata->dev);
-
- if (!pcm_data->stream) {
- ucontrol->value.integer.value[0] =
- hsw_ipc_to_mixer(pcm_data->volume[0]);
- ucontrol->value.integer.value[1] =
- hsw_ipc_to_mixer(pcm_data->volume[1]);
- pm_runtime_mark_last_busy(pdata->dev);
- pm_runtime_put_autosuspend(pdata->dev);
- mutex_unlock(&pcm_data->mutex);
- return 0;
- }
-
- sst_hsw_stream_get_volume(hsw, pcm_data->stream, 0, 0, &volume);
- ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume);
- sst_hsw_stream_get_volume(hsw, pcm_data->stream, 0, 1, &volume);
- ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume);
-
- pm_runtime_mark_last_busy(pdata->dev);
- pm_runtime_put_autosuspend(pdata->dev);
- mutex_unlock(&pcm_data->mutex);
-
- return 0;
-}
-
-static int hsw_volume_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
- struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
- struct sst_hsw *hsw = pdata->hsw;
- u32 volume;
-
- pm_runtime_get_sync(pdata->dev);
-
- if (ucontrol->value.integer.value[0] ==
- ucontrol->value.integer.value[1]) {
-
- volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
- sst_hsw_mixer_set_volume(hsw, 0, SST_HSW_CHANNELS_ALL, volume);
-
- } else {
- volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
- sst_hsw_mixer_set_volume(hsw, 0, 0, volume);
-
- volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[1]);
- sst_hsw_mixer_set_volume(hsw, 0, 1, volume);
- }
-
- pm_runtime_mark_last_busy(pdata->dev);
- pm_runtime_put_autosuspend(pdata->dev);
- return 0;
-}
-
-static int hsw_volume_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
- struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
- struct sst_hsw *hsw = pdata->hsw;
- unsigned int volume = 0;
-
- pm_runtime_get_sync(pdata->dev);
- sst_hsw_mixer_get_volume(hsw, 0, 0, &volume);
- ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume);
-
- sst_hsw_mixer_get_volume(hsw, 0, 1, &volume);
- ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume);
-
- pm_runtime_mark_last_busy(pdata->dev);
- pm_runtime_put_autosuspend(pdata->dev);
- return 0;
-}
-
-/* TLV used by both global and stream volumes */
-static const DECLARE_TLV_DB_SCALE(hsw_vol_tlv, -9000, 300, 1);
-
-/* System Pin has no volume control */
-static const struct snd_kcontrol_new hsw_volume_controls[] = {
- /* Global DSP volume */
- SOC_DOUBLE_EXT_TLV("Master Playback Volume", 0, 0, 8,
- ARRAY_SIZE(volume_map) - 1, 0,
- hsw_volume_get, hsw_volume_put, hsw_vol_tlv),
- /* Offload 0 volume */
- SOC_DOUBLE_EXT_TLV("Media0 Playback Volume", 1, 0, 8,
- ARRAY_SIZE(volume_map) - 1, 0,
- hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
- /* Offload 1 volume */
- SOC_DOUBLE_EXT_TLV("Media1 Playback Volume", 2, 0, 8,
- ARRAY_SIZE(volume_map) - 1, 0,
- hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
- /* Mic Capture volume */
- SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8,
- ARRAY_SIZE(volume_map) - 1, 0,
- hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
-};
-
-/* Create DMA buffer page table for DSP */
-static int create_adsp_page_table(struct snd_pcm_substream *substream,
- struct hsw_priv_data *pdata, struct snd_soc_pcm_runtime *rtd,
- unsigned char *dma_area, size_t size, int pcm)
-{
- struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream);
- int i, pages, stream = substream->stream;
-
- pages = snd_sgbuf_aligned_pages(size);
-
- dev_dbg(rtd->dev, "generating page table for %p size 0x%zu pages %d\n",
- dma_area, size, pages);
-
- for (i = 0; i < pages; i++) {
- u32 idx = (((i << 2) + i)) >> 1;
- u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT;
- u32 *pg_table;
-
- dev_dbg(rtd->dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn);
-
- pg_table = (u32 *)(pdata->dmab[pcm][stream].area + idx);
-
- if (i & 1)
- *pg_table |= (pfn << 4);
- else
- *pg_table |= pfn;
- }
-
- return 0;
-}
-
-/* this may get called several times by oss emulation */
-static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct hsw_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct hsw_pcm_data *pcm_data;
- struct sst_hsw *hsw = pdata->hsw;
- struct sst_module *module_data;
- struct sst_dsp *dsp;
- struct snd_dma_buffer *dmab;
- enum sst_hsw_stream_type stream_type;
- enum sst_hsw_stream_path_id path_id;
- u32 rate, bits, map, pages, module_id;
- u8 channels;
- int ret, dai;
-
- dai = mod_map[rtd->cpu_dai->id].dai_id;
- pcm_data = &pdata->pcm[dai][substream->stream];
-
- /* check if we are being called a subsequent time */
- if (pcm_data->allocated) {
- ret = sst_hsw_stream_reset(hsw, pcm_data->stream);
- if (ret < 0)
- dev_dbg(rtd->dev, "error: reset stream failed %d\n",
- ret);
-
- ret = sst_hsw_stream_free(hsw, pcm_data->stream);
- if (ret < 0) {
- dev_dbg(rtd->dev, "error: free stream failed %d\n",
- ret);
- return ret;
- }
- pcm_data->allocated = false;
-
- pcm_data->stream = sst_hsw_stream_new(hsw, rtd->cpu_dai->id,
- hsw_notify_pointer, pcm_data);
- if (pcm_data->stream == NULL) {
- dev_err(rtd->dev, "error: failed to create stream\n");
- return -EINVAL;
- }
- }
-
- /* stream direction */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- path_id = SST_HSW_STREAM_PATH_SSP0_OUT;
- else
- path_id = SST_HSW_STREAM_PATH_SSP0_IN;
-
- /* DSP stream type depends on DAI ID */
- switch (rtd->cpu_dai->id) {
- case 0:
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- stream_type = SST_HSW_STREAM_TYPE_SYSTEM;
- module_id = SST_HSW_MODULE_PCM_SYSTEM;
- }
- else {
- stream_type = SST_HSW_STREAM_TYPE_CAPTURE;
- module_id = SST_HSW_MODULE_PCM_CAPTURE;
- }
- break;
- case 1:
- case 2:
- stream_type = SST_HSW_STREAM_TYPE_RENDER;
- module_id = SST_HSW_MODULE_PCM;
- break;
- case 3:
- /* path ID needs to be OUT for loopback */
- stream_type = SST_HSW_STREAM_TYPE_LOOPBACK;
- path_id = SST_HSW_STREAM_PATH_SSP0_OUT;
- module_id = SST_HSW_MODULE_PCM_REFERENCE;
- break;
- default:
- dev_err(rtd->dev, "error: invalid DAI ID %d\n",
- rtd->cpu_dai->id);
- return -EINVAL;
- };
-
- ret = sst_hsw_stream_format(hsw, pcm_data->stream,
- path_id, stream_type, SST_HSW_STREAM_FORMAT_PCM_FORMAT);
- if (ret < 0) {
- dev_err(rtd->dev, "error: failed to set format %d\n", ret);
- return ret;
- }
-
- rate = params_rate(params);
- ret = sst_hsw_stream_set_rate(hsw, pcm_data->stream, rate);
- if (ret < 0) {
- dev_err(rtd->dev, "error: could not set rate %d\n", rate);
- return ret;
- }
-
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- bits = SST_HSW_DEPTH_16BIT;
- sst_hsw_stream_set_valid(hsw, pcm_data->stream, 16);
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- bits = SST_HSW_DEPTH_32BIT;
- sst_hsw_stream_set_valid(hsw, pcm_data->stream, 24);
- break;
- case SNDRV_PCM_FORMAT_S8:
- bits = SST_HSW_DEPTH_8BIT;
- sst_hsw_stream_set_valid(hsw, pcm_data->stream, 8);
- break;
- case SNDRV_PCM_FORMAT_S32_LE:
- bits = SST_HSW_DEPTH_32BIT;
- sst_hsw_stream_set_valid(hsw, pcm_data->stream, 32);
- break;
- default:
- dev_err(rtd->dev, "error: invalid format %d\n",
- params_format(params));
- return -EINVAL;
- }
-
- ret = sst_hsw_stream_set_bits(hsw, pcm_data->stream, bits);
- if (ret < 0) {
- dev_err(rtd->dev, "error: could not set bits %d\n", bits);
- return ret;
- }
-
- channels = params_channels(params);
- map = create_channel_map(SST_HSW_CHANNEL_CONFIG_STEREO);
- sst_hsw_stream_set_map_config(hsw, pcm_data->stream,
- map, SST_HSW_CHANNEL_CONFIG_STEREO);
-
- ret = sst_hsw_stream_set_channels(hsw, pcm_data->stream, channels);
- if (ret < 0) {
- dev_err(rtd->dev, "error: could not set channels %d\n",
- channels);
- return ret;
- }
-
- ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
- if (ret < 0) {
- dev_err(rtd->dev, "error: could not allocate %d bytes for PCM %d\n",
- params_buffer_bytes(params), ret);
- return ret;
- }
-
- dmab = snd_pcm_get_dma_buf(substream);
-
- ret = create_adsp_page_table(substream, pdata, rtd, runtime->dma_area,
- runtime->dma_bytes, rtd->cpu_dai->id);
- if (ret < 0)
- return ret;
-
- sst_hsw_stream_set_style(hsw, pcm_data->stream,
- SST_HSW_INTERLEAVING_PER_CHANNEL);
-
- if (runtime->dma_bytes % PAGE_SIZE)
- pages = (runtime->dma_bytes / PAGE_SIZE) + 1;
- else
- pages = runtime->dma_bytes / PAGE_SIZE;
-
- ret = sst_hsw_stream_buffer(hsw, pcm_data->stream,
- pdata->dmab[rtd->cpu_dai->id][substream->stream].addr,
- pages, runtime->dma_bytes, 0,
- snd_sgbuf_get_addr(dmab, 0) >> PAGE_SHIFT);
- if (ret < 0) {
- dev_err(rtd->dev, "error: failed to set DMA buffer %d\n", ret);
- return ret;
- }
-
- dsp = sst_hsw_get_dsp(hsw);
-
- module_data = sst_module_get_from_id(dsp, module_id);
- if (module_data == NULL) {
- dev_err(rtd->dev, "error: failed to get module config\n");
- return -EINVAL;
- }
-
- sst_hsw_stream_set_module_info(hsw, pcm_data->stream,
- pcm_data->runtime);
-
- ret = sst_hsw_stream_commit(hsw, pcm_data->stream);
- if (ret < 0) {
- dev_err(rtd->dev, "error: failed to commit stream %d\n", ret);
- return ret;
- }
-
- if (!pcm_data->allocated) {
- /* Set previous saved volume */
- sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
- 0, pcm_data->volume[0]);
- sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
- 1, pcm_data->volume[1]);
- pcm_data->allocated = true;
- }
-
- ret = sst_hsw_stream_pause(hsw, pcm_data->stream, 1);
- if (ret < 0)
- dev_err(rtd->dev, "error: failed to pause %d\n", ret);
-
- return 0;
-}
-
-static int hsw_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- snd_pcm_lib_free_pages(substream);
- return 0;
-}
-
-static int hsw_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct hsw_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct hsw_pcm_data *pcm_data;
- struct sst_hsw_stream *sst_stream;
- struct sst_hsw *hsw = pdata->hsw;
- struct snd_pcm_runtime *runtime = substream->runtime;
- snd_pcm_uframes_t pos;
- int dai;
-
- dai = mod_map[rtd->cpu_dai->id].dai_id;
- pcm_data = &pdata->pcm[dai][substream->stream];
- sst_stream = pcm_data->stream;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- sst_hsw_stream_set_silence_start(hsw, sst_stream, false);
- sst_hsw_stream_resume(hsw, pcm_data->stream, 0);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- sst_hsw_stream_set_silence_start(hsw, sst_stream, false);
- sst_hsw_stream_pause(hsw, pcm_data->stream, 0);
- break;
- case SNDRV_PCM_TRIGGER_DRAIN:
- pos = runtime->control->appl_ptr % runtime->buffer_size;
- sst_hsw_stream_set_old_position(hsw, pcm_data->stream, pos);
- sst_hsw_stream_set_silence_start(hsw, sst_stream, true);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data)
-{
- struct hsw_pcm_data *pcm_data = data;
- struct snd_pcm_substream *substream = pcm_data->substream;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct hsw_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct sst_hsw *hsw = pdata->hsw;
- u32 pos;
- snd_pcm_uframes_t position = bytes_to_frames(runtime,
- sst_hsw_get_dsp_position(hsw, pcm_data->stream));
- unsigned char *dma_area = runtime->dma_area;
- snd_pcm_uframes_t dma_frames =
- bytes_to_frames(runtime, runtime->dma_bytes);
- snd_pcm_uframes_t old_position;
- ssize_t samples;
-
- pos = frames_to_bytes(runtime,
- (runtime->control->appl_ptr % runtime->buffer_size));
-
- dev_vdbg(rtd->dev, "PCM: App pointer %d bytes\n", pos);
-
- /* SST fw don't know where to stop dma
- * So, SST driver need to clean the data which has been consumed
- */
- if (dma_area == NULL || dma_frames <= 0
- || (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- || !sst_hsw_stream_get_silence_start(hsw, stream)) {
- snd_pcm_period_elapsed(substream);
- return pos;
- }
-
- old_position = sst_hsw_stream_get_old_position(hsw, stream);
- if (position > old_position) {
- if (position < dma_frames) {
- samples = SST_SAMPLES(runtime, position - old_position);
- snd_pcm_format_set_silence(runtime->format,
- SST_OLD_POSITION(dma_area,
- runtime, old_position),
- samples);
- } else
- dev_err(rtd->dev, "PCM: position is wrong\n");
- } else {
- if (old_position < dma_frames) {
- samples = SST_SAMPLES(runtime,
- dma_frames - old_position);
- snd_pcm_format_set_silence(runtime->format,
- SST_OLD_POSITION(dma_area,
- runtime, old_position),
- samples);
- } else
- dev_err(rtd->dev, "PCM: dma_bytes is wrong\n");
- if (position < dma_frames) {
- samples = SST_SAMPLES(runtime, position);
- snd_pcm_format_set_silence(runtime->format,
- dma_area, samples);
- } else
- dev_err(rtd->dev, "PCM: position is wrong\n");
- }
- sst_hsw_stream_set_old_position(hsw, stream, position);
-
- /* let alsa know we have play a period */
- snd_pcm_period_elapsed(substream);
- return pos;
-}
-
-static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct hsw_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct hsw_pcm_data *pcm_data;
- struct sst_hsw *hsw = pdata->hsw;
- snd_pcm_uframes_t offset;
- uint64_t ppos;
- u32 position;
- int dai;
-
- dai = mod_map[rtd->cpu_dai->id].dai_id;
- pcm_data = &pdata->pcm[dai][substream->stream];
- position = sst_hsw_get_dsp_position(hsw, pcm_data->stream);
-
- offset = bytes_to_frames(runtime, position);
- ppos = sst_hsw_get_dsp_presentation_position(hsw, pcm_data->stream);
-
- dev_vdbg(rtd->dev, "PCM: DMA pointer %du bytes, pos %llu\n",
- position, ppos);
- return offset;
-}
-
-static int hsw_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct hsw_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct hsw_pcm_data *pcm_data;
- struct sst_hsw *hsw = pdata->hsw;
- int dai;
-
- dai = mod_map[rtd->cpu_dai->id].dai_id;
- pcm_data = &pdata->pcm[dai][substream->stream];
-
- mutex_lock(&pcm_data->mutex);
- pm_runtime_get_sync(pdata->dev);
-
- snd_soc_pcm_set_drvdata(rtd, pcm_data);
- pcm_data->substream = substream;
-
- snd_soc_set_runtime_hwparams(substream, &hsw_pcm_hardware);
-
- pcm_data->stream = sst_hsw_stream_new(hsw, rtd->cpu_dai->id,
- hsw_notify_pointer, pcm_data);
- if (pcm_data->stream == NULL) {
- dev_err(rtd->dev, "error: failed to create stream\n");
- pm_runtime_mark_last_busy(pdata->dev);
- pm_runtime_put_autosuspend(pdata->dev);
- mutex_unlock(&pcm_data->mutex);
- return -EINVAL;
- }
-
- mutex_unlock(&pcm_data->mutex);
- return 0;
-}
-
-static int hsw_pcm_close(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct hsw_priv_data *pdata =
- snd_soc_platform_get_drvdata(rtd->platform);
- struct hsw_pcm_data *pcm_data;
- struct sst_hsw *hsw = pdata->hsw;
- int ret, dai;
-
- dai = mod_map[rtd->cpu_dai->id].dai_id;
- pcm_data = &pdata->pcm[dai][substream->stream];
-
- mutex_lock(&pcm_data->mutex);
- ret = sst_hsw_stream_reset(hsw, pcm_data->stream);
- if (ret < 0) {
- dev_dbg(rtd->dev, "error: reset stream failed %d\n", ret);
- goto out;
- }
-
- ret = sst_hsw_stream_free(hsw, pcm_data->stream);
- if (ret < 0) {
- dev_dbg(rtd->dev, "error: free stream failed %d\n", ret);
- goto out;
- }
- pcm_data->allocated = 0;
- pcm_data->stream = NULL;
-
-out:
- pm_runtime_mark_last_busy(pdata->dev);
- pm_runtime_put_autosuspend(pdata->dev);
- mutex_unlock(&pcm_data->mutex);
- return ret;
-}
-
-static struct snd_pcm_ops hsw_pcm_ops = {
- .open = hsw_pcm_open,
- .close = hsw_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = hsw_pcm_hw_params,
- .hw_free = hsw_pcm_hw_free,
- .trigger = hsw_pcm_trigger,
- .pointer = hsw_pcm_pointer,
- .page = snd_pcm_sgbuf_ops_page,
-};
-
-static int hsw_pcm_create_modules(struct hsw_priv_data *pdata)
-{
- struct sst_hsw *hsw = pdata->hsw;
- struct hsw_pcm_data *pcm_data;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
- pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
-
- /* create new runtime module, use same offset if recreated */
- pcm_data->runtime = sst_hsw_runtime_module_create(hsw,
- mod_map[i].mod_id, pcm_data->persistent_offset);
- if (pcm_data->runtime == NULL)
- goto err;
- pcm_data->persistent_offset =
- pcm_data->runtime->persistent_offset;
- }
-
- return 0;
-
-err:
- for (--i; i >= 0; i--) {
- pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
- sst_hsw_runtime_module_free(pcm_data->runtime);
- }
-
- return -ENODEV;
-}
-
-static void hsw_pcm_free_modules(struct hsw_priv_data *pdata)
-{
- struct hsw_pcm_data *pcm_data;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
- pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
-
- sst_hsw_runtime_module_free(pcm_data->runtime);
- }
-}
-
-static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_pcm *pcm = rtd->pcm;
- struct snd_soc_platform *platform = rtd->platform;
- struct sst_pdata *pdata = dev_get_platdata(platform->dev);
- struct hsw_priv_data *priv_data = dev_get_drvdata(platform->dev);
- struct device *dev = pdata->dma_dev;
- int ret = 0;
-
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream ||
- pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
- ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
- SNDRV_DMA_TYPE_DEV_SG,
- dev,
- hsw_pcm_hardware.buffer_bytes_max,
- hsw_pcm_hardware.buffer_bytes_max);
- if (ret) {
- dev_err(rtd->dev, "dma buffer allocation failed %d\n",
- ret);
- return ret;
- }
- }
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
- priv_data->pcm[rtd->cpu_dai->id][SNDRV_PCM_STREAM_PLAYBACK].hsw_pcm = pcm;
- if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream)
- priv_data->pcm[rtd->cpu_dai->id][SNDRV_PCM_STREAM_CAPTURE].hsw_pcm = pcm;
-
- return ret;
-}
-
-#define HSW_FORMATS \
- (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
-
-static struct snd_soc_dai_driver hsw_dais[] = {
- {
- .name = "System Pin",
- .id = HSW_PCM_DAI_ID_SYSTEM,
- .playback = {
- .stream_name = "System Playback",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .stream_name = "Analog Capture",
- .channels_min = 2,
- .channels_max = 4,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
- },
- },
- {
- /* PCM */
- .name = "Offload0 Pin",
- .id = HSW_PCM_DAI_ID_OFFLOAD0,
- .playback = {
- .stream_name = "Offload0 Playback",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
- .formats = HSW_FORMATS,
- },
- },
- {
- /* PCM */
- .name = "Offload1 Pin",
- .id = HSW_PCM_DAI_ID_OFFLOAD1,
- .playback = {
- .stream_name = "Offload1 Playback",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
- .formats = HSW_FORMATS,
- },
- },
- {
- .name = "Loopback Pin",
- .id = HSW_PCM_DAI_ID_LOOPBACK,
- .capture = {
- .stream_name = "Loopback Capture",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
- },
- },
-};
-
-static const struct snd_soc_dapm_widget widgets[] = {
-
- /* Backend DAIs */
- SND_SOC_DAPM_AIF_IN("SSP0 CODEC IN", NULL, 0, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_AIF_OUT("SSP0 CODEC OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_AIF_IN("SSP1 BT IN", NULL, 0, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_AIF_OUT("SSP1 BT OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
-
- /* Global Playback Mixer */
- SND_SOC_DAPM_MIXER("Playback VMixer", SND_SOC_NOPM, 0, 0, NULL, 0),
-};
-
-static const struct snd_soc_dapm_route graph[] = {
-
- /* Playback Mixer */
- {"Playback VMixer", NULL, "System Playback"},
- {"Playback VMixer", NULL, "Offload0 Playback"},
- {"Playback VMixer", NULL, "Offload1 Playback"},
-
- {"SSP0 CODEC OUT", NULL, "Playback VMixer"},
-
- {"Analog Capture", NULL, "SSP0 CODEC IN"},
-};
-
-static int hsw_pcm_probe(struct snd_soc_platform *platform)
-{
- struct hsw_priv_data *priv_data = snd_soc_platform_get_drvdata(platform);
- struct sst_pdata *pdata = dev_get_platdata(platform->dev);
- struct device *dma_dev, *dev;
- int i, ret = 0;
-
- if (!pdata)
- return -ENODEV;
-
- dev = platform->dev;
- dma_dev = pdata->dma_dev;
-
- priv_data->hsw = pdata->dsp;
- priv_data->dev = platform->dev;
- priv_data->pm_state = HSW_PM_STATE_D0;
- priv_data->soc_card = platform->component.card;
-
- /* allocate DSP buffer page tables */
- for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
-
- /* playback */
- if (hsw_dais[i].playback.channels_min) {
- mutex_init(&priv_data->pcm[i][SNDRV_PCM_STREAM_PLAYBACK].mutex);
- ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dma_dev,
- PAGE_SIZE, &priv_data->dmab[i][0]);
- if (ret < 0)
- goto err;
- }
-
- /* capture */
- if (hsw_dais[i].capture.channels_min) {
- mutex_init(&priv_data->pcm[i][SNDRV_PCM_STREAM_CAPTURE].mutex);
- ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dma_dev,
- PAGE_SIZE, &priv_data->dmab[i][1]);
- if (ret < 0)
- goto err;
- }
- }
-
- /* allocate runtime modules */
- hsw_pcm_create_modules(priv_data);
-
- /* enable runtime PM with auto suspend */
- pm_runtime_set_autosuspend_delay(platform->dev,
- SST_RUNTIME_SUSPEND_DELAY);
- pm_runtime_use_autosuspend(platform->dev);
- pm_runtime_enable(platform->dev);
- pm_runtime_idle(platform->dev);
-
- return 0;
-
-err:
- for (;i >= 0; i--) {
- if (hsw_dais[i].playback.channels_min)
- snd_dma_free_pages(&priv_data->dmab[i][0]);
- if (hsw_dais[i].capture.channels_min)
- snd_dma_free_pages(&priv_data->dmab[i][1]);
- }
- return ret;
-}
-
-static int hsw_pcm_remove(struct snd_soc_platform *platform)
-{
- struct hsw_priv_data *priv_data =
- snd_soc_platform_get_drvdata(platform);
- int i;
-
- pm_runtime_disable(platform->dev);
- hsw_pcm_free_modules(priv_data);
-
- for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
- if (hsw_dais[i].playback.channels_min)
- snd_dma_free_pages(&priv_data->dmab[i][0]);
- if (hsw_dais[i].capture.channels_min)
- snd_dma_free_pages(&priv_data->dmab[i][1]);
- }
-
- return 0;
-}
-
-static struct snd_soc_platform_driver hsw_soc_platform = {
- .probe = hsw_pcm_probe,
- .remove = hsw_pcm_remove,
- .ops = &hsw_pcm_ops,
- .pcm_new = hsw_pcm_new,
-};
-
-static const struct snd_soc_component_driver hsw_dai_component = {
- .name = "haswell-dai",
- .controls = hsw_volume_controls,
- .num_controls = ARRAY_SIZE(hsw_volume_controls),
- .dapm_widgets = widgets,
- .num_dapm_widgets = ARRAY_SIZE(widgets),
- .dapm_routes = graph,
- .num_dapm_routes = ARRAY_SIZE(graph),
-};
-
-static int hsw_pcm_dev_probe(struct platform_device *pdev)
-{
- struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
- struct hsw_priv_data *priv_data;
- int ret;
-
- if (!sst_pdata)
- return -EINVAL;
-
- priv_data = devm_kzalloc(&pdev->dev, sizeof(*priv_data), GFP_KERNEL);
- if (!priv_data)
- return -ENOMEM;
-
- ret = sst_hsw_dsp_init(&pdev->dev, sst_pdata);
- if (ret < 0)
- return -ENODEV;
-
- priv_data->hsw = sst_pdata->dsp;
- platform_set_drvdata(pdev, priv_data);
-
- ret = snd_soc_register_platform(&pdev->dev, &hsw_soc_platform);
- if (ret < 0)
- goto err_plat;
-
- ret = snd_soc_register_component(&pdev->dev, &hsw_dai_component,
- hsw_dais, ARRAY_SIZE(hsw_dais));
- if (ret < 0)
- goto err_comp;
-
- return 0;
-
-err_comp:
- snd_soc_unregister_platform(&pdev->dev);
-err_plat:
- sst_hsw_dsp_free(&pdev->dev, sst_pdata);
- return 0;
-}
-
-static int hsw_pcm_dev_remove(struct platform_device *pdev)
-{
- struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
-
- snd_soc_unregister_platform(&pdev->dev);
- snd_soc_unregister_component(&pdev->dev);
- sst_hsw_dsp_free(&pdev->dev, sst_pdata);
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-
-static int hsw_pcm_runtime_idle(struct device *dev)
-{
- return 0;
-}
-
-static int hsw_pcm_runtime_suspend(struct device *dev)
-{
- struct hsw_priv_data *pdata = dev_get_drvdata(dev);
- struct sst_hsw *hsw = pdata->hsw;
-
- if (pdata->pm_state >= HSW_PM_STATE_RTD3)
- return 0;
-
- sst_hsw_dsp_runtime_suspend(hsw);
- sst_hsw_dsp_runtime_sleep(hsw);
- pdata->pm_state = HSW_PM_STATE_RTD3;
-
- return 0;
-}
-
-static int hsw_pcm_runtime_resume(struct device *dev)
-{
- struct hsw_priv_data *pdata = dev_get_drvdata(dev);
- struct sst_hsw *hsw = pdata->hsw;
- int ret;
-
- if (pdata->pm_state != HSW_PM_STATE_RTD3)
- return 0;
-
- ret = sst_hsw_dsp_load(hsw);
- if (ret < 0) {
- dev_err(dev, "failed to reload %d\n", ret);
- return ret;
- }
-
- ret = hsw_pcm_create_modules(pdata);
- if (ret < 0) {
- dev_err(dev, "failed to create modules %d\n", ret);
- return ret;
- }
-
- ret = sst_hsw_dsp_runtime_resume(hsw);
- if (ret < 0)
- return ret;
- else if (ret == 1) /* no action required */
- return 0;
-
- pdata->pm_state = HSW_PM_STATE_D0;
- return ret;
-}
-
-#else
-#define hsw_pcm_runtime_idle NULL
-#define hsw_pcm_runtime_suspend NULL
-#define hsw_pcm_runtime_resume NULL
-#endif
-
-#ifdef CONFIG_PM
-
-static void hsw_pcm_complete(struct device *dev)
-{
- struct hsw_priv_data *pdata = dev_get_drvdata(dev);
- struct sst_hsw *hsw = pdata->hsw;
- struct hsw_pcm_data *pcm_data;
- int i, err;
-
- if (pdata->pm_state != HSW_PM_STATE_D3)
- return;
-
- err = sst_hsw_dsp_load(hsw);
- if (err < 0) {
- dev_err(dev, "failed to reload %d\n", err);
- return;
- }
-
- err = hsw_pcm_create_modules(pdata);
- if (err < 0) {
- dev_err(dev, "failed to create modules %d\n", err);
- return;
- }
-
- for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
- pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
-
- if (!pcm_data->substream)
- continue;
-
- err = sst_module_runtime_restore(pcm_data->runtime,
- &pcm_data->context);
- if (err < 0)
- dev_err(dev, "failed to restore context for PCM %d\n", i);
- }
-
- snd_soc_resume(pdata->soc_card->dev);
-
- err = sst_hsw_dsp_runtime_resume(hsw);
- if (err < 0)
- return;
- else if (err == 1) /* no action required */
- return;
-
- pdata->pm_state = HSW_PM_STATE_D0;
- return;
-}
-
-static int hsw_pcm_prepare(struct device *dev)
-{
- struct hsw_priv_data *pdata = dev_get_drvdata(dev);
- struct sst_hsw *hsw = pdata->hsw;
- struct hsw_pcm_data *pcm_data;
- int i, err;
-
- if (pdata->pm_state == HSW_PM_STATE_D3)
- return 0;
- else if (pdata->pm_state == HSW_PM_STATE_D0) {
- /* suspend all active streams */
- for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
- pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
-
- if (!pcm_data->substream)
- continue;
- dev_dbg(dev, "suspending pcm %d\n", i);
- snd_pcm_suspend_all(pcm_data->hsw_pcm);
-
- /* We need to wait until the DSP FW stops the streams */
- msleep(2);
- }
-
- /* preserve persistent memory */
- for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
- pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
-
- if (!pcm_data->substream)
- continue;
-
- dev_dbg(dev, "saving context pcm %d\n", i);
- err = sst_module_runtime_save(pcm_data->runtime,
- &pcm_data->context);
- if (err < 0)
- dev_err(dev, "failed to save context for PCM %d\n", i);
- }
- /* enter D3 state and stall */
- sst_hsw_dsp_runtime_suspend(hsw);
- /* put the DSP to sleep */
- sst_hsw_dsp_runtime_sleep(hsw);
- }
-
- snd_soc_suspend(pdata->soc_card->dev);
- snd_soc_poweroff(pdata->soc_card->dev);
-
- pdata->pm_state = HSW_PM_STATE_D3;
-
- return 0;
-}
-
-#else
-#define hsw_pcm_prepare NULL
-#define hsw_pcm_complete NULL
-#endif
-
-static const struct dev_pm_ops hsw_pcm_pm = {
- .runtime_idle = hsw_pcm_runtime_idle,
- .runtime_suspend = hsw_pcm_runtime_suspend,
- .runtime_resume = hsw_pcm_runtime_resume,
- .prepare = hsw_pcm_prepare,
- .complete = hsw_pcm_complete,
-};
-
-static struct platform_driver hsw_pcm_driver = {
- .driver = {
- .name = "haswell-pcm-audio",
- .pm = &hsw_pcm_pm,
- },
-
- .probe = hsw_pcm_dev_probe,
- .remove = hsw_pcm_dev_remove,
-};
-module_platform_driver(hsw_pcm_driver);
-
-MODULE_AUTHOR("Liam Girdwood, Xingchao Wang");
-MODULE_DESCRIPTION("Haswell/Lynxpoint + Broadwell/Wildcatpoint PCM");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:haswell-pcm-audio");
+++ /dev/null
-#ifndef __SST_MFLD_DSP_H__
-#define __SST_MFLD_DSP_H__
-/*
- * sst_mfld_dsp.h - Intel SST Driver for audio engine
- *
- * Copyright (C) 2008-14 Intel Corporation
- * Authors: Vinod Koul <vinod.koul@linux.intel.com>
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-
-#define SST_MAX_BIN_BYTES 1024
-
-#define MAX_DBG_RW_BYTES 80
-#define MAX_NUM_SCATTER_BUFFERS 8
-#define MAX_LOOP_BACK_DWORDS 8
-/* IPC base address and mailbox, timestamp offsets */
-#define SST_MAILBOX_SIZE 0x0400
-#define SST_MAILBOX_SEND 0x0000
-#define SST_TIME_STAMP 0x1800
-#define SST_TIME_STAMP_MRFLD 0x800
-#define SST_RESERVED_OFFSET 0x1A00
-#define SST_SCU_LPE_MAILBOX 0x1000
-#define SST_LPE_SCU_MAILBOX 0x1400
-#define SST_SCU_LPE_LOG_BUF (SST_SCU_LPE_MAILBOX+16)
-#define PROCESS_MSG 0x80
-
-/* Message ID's for IPC messages */
-/* Bits B7: SST or IA/SC ; B6-B4: Msg Category; B3-B0: Msg Type */
-
-/* I2L Firmware/Codec Download msgs */
-#define IPC_IA_PREP_LIB_DNLD 0x01
-#define IPC_IA_LIB_DNLD_CMPLT 0x02
-#define IPC_IA_GET_FW_VERSION 0x04
-#define IPC_IA_GET_FW_BUILD_INF 0x05
-#define IPC_IA_GET_FW_INFO 0x06
-#define IPC_IA_GET_FW_CTXT 0x07
-#define IPC_IA_SET_FW_CTXT 0x08
-#define IPC_IA_PREPARE_SHUTDOWN 0x31
-/* I2L Codec Config/control msgs */
-#define IPC_PREP_D3 0x10
-#define IPC_IA_SET_CODEC_PARAMS 0x10
-#define IPC_IA_GET_CODEC_PARAMS 0x11
-#define IPC_IA_SET_PPP_PARAMS 0x12
-#define IPC_IA_GET_PPP_PARAMS 0x13
-#define IPC_SST_PERIOD_ELAPSED_MRFLD 0xA
-#define IPC_IA_ALG_PARAMS 0x1A
-#define IPC_IA_TUNING_PARAMS 0x1B
-#define IPC_IA_SET_RUNTIME_PARAMS 0x1C
-#define IPC_IA_SET_PARAMS 0x1
-#define IPC_IA_GET_PARAMS 0x2
-
-#define IPC_EFFECTS_CREATE 0xE
-#define IPC_EFFECTS_DESTROY 0xF
-
-/* I2L Stream config/control msgs */
-#define IPC_IA_ALLOC_STREAM_MRFLD 0x2
-#define IPC_IA_ALLOC_STREAM 0x20 /* Allocate a stream ID */
-#define IPC_IA_FREE_STREAM_MRFLD 0x03
-#define IPC_IA_FREE_STREAM 0x21 /* Free the stream ID */
-#define IPC_IA_SET_STREAM_PARAMS 0x22
-#define IPC_IA_SET_STREAM_PARAMS_MRFLD 0x12
-#define IPC_IA_GET_STREAM_PARAMS 0x23
-#define IPC_IA_PAUSE_STREAM 0x24
-#define IPC_IA_PAUSE_STREAM_MRFLD 0x4
-#define IPC_IA_RESUME_STREAM 0x25
-#define IPC_IA_RESUME_STREAM_MRFLD 0x5
-#define IPC_IA_DROP_STREAM 0x26
-#define IPC_IA_DROP_STREAM_MRFLD 0x07
-#define IPC_IA_DRAIN_STREAM 0x27 /* Short msg with str_id */
-#define IPC_IA_DRAIN_STREAM_MRFLD 0x8
-#define IPC_IA_CONTROL_ROUTING 0x29
-#define IPC_IA_VTSV_UPDATE_MODULES 0x20
-#define IPC_IA_VTSV_DETECTED 0x21
-
-#define IPC_IA_START_STREAM_MRFLD 0X06
-#define IPC_IA_START_STREAM 0x30 /* Short msg with str_id */
-
-#define IPC_IA_SET_GAIN_MRFLD 0x21
-/* Debug msgs */
-#define IPC_IA_DBG_MEM_READ 0x40
-#define IPC_IA_DBG_MEM_WRITE 0x41
-#define IPC_IA_DBG_LOOP_BACK 0x42
-#define IPC_IA_DBG_LOG_ENABLE 0x45
-#define IPC_IA_DBG_SET_PROBE_PARAMS 0x47
-
-/* L2I Firmware/Codec Download msgs */
-#define IPC_IA_FW_INIT_CMPLT 0x81
-#define IPC_IA_FW_INIT_CMPLT_MRFLD 0x01
-#define IPC_IA_FW_ASYNC_ERR_MRFLD 0x11
-
-/* L2I Codec Config/control msgs */
-#define IPC_SST_FRAGMENT_ELPASED 0x90 /* Request IA more data */
-
-#define IPC_SST_BUF_UNDER_RUN 0x92 /* PB Under run and stopped */
-#define IPC_SST_BUF_OVER_RUN 0x93 /* CAP Under run and stopped */
-#define IPC_SST_DRAIN_END 0x94 /* PB Drain complete and stopped */
-#define IPC_SST_CHNGE_SSP_PARAMS 0x95 /* PB SSP parameters changed */
-#define IPC_SST_STREAM_PROCESS_FATAL_ERR 0x96/* error in processing a stream */
-#define IPC_SST_PERIOD_ELAPSED 0x97 /* period elapsed */
-
-#define IPC_SST_ERROR_EVENT 0x99 /* Buffer over run occurred */
-/* L2S messages */
-#define IPC_SC_DDR_LINK_UP 0xC0
-#define IPC_SC_DDR_LINK_DOWN 0xC1
-#define IPC_SC_SET_LPECLK_REQ 0xC2
-#define IPC_SC_SSP_BIT_BANG 0xC3
-
-/* L2I Error reporting msgs */
-#define IPC_IA_MEM_ALLOC_FAIL 0xE0
-#define IPC_IA_PROC_ERR 0xE1 /* error in processing a
- stream can be used by playback and
- capture modules */
-
-/* L2I Debug msgs */
-#define IPC_IA_PRINT_STRING 0xF0
-
-/* Buffer under-run */
-#define IPC_IA_BUF_UNDER_RUN_MRFLD 0x0B
-
-/* Mrfld specific defines:
- * For asynchronous messages(INIT_CMPLT, PERIOD_ELAPSED, ASYNC_ERROR)
- * received from FW, the format is:
- * - IPC High: pvt_id is set to zero. Always short message.
- * - msg_id is in lower 16-bits of IPC low payload.
- * - pipe_id is in higher 16-bits of IPC low payload for period_elapsed.
- * - error id is in higher 16-bits of IPC low payload for async errors.
- */
-#define SST_ASYNC_DRV_ID 0
-
-/* Command Response or Acknowledge message to any IPC message will have
- * same message ID and stream ID information which is sent.
- * There is no specific Ack message ID. The data field is used as response
- * meaning.
- */
-enum ackData {
- IPC_ACK_SUCCESS = 0,
- IPC_ACK_FAILURE,
-};
-
-enum ipc_ia_msg_id {
- IPC_CMD = 1, /*!< Task Control message ID */
- IPC_SET_PARAMS = 2,/*!< Task Set param message ID */
- IPC_GET_PARAMS = 3, /*!< Task Get param message ID */
- IPC_INVALID = 0xFF, /*!<Task Get param message ID */
-};
-
-enum sst_codec_types {
- /* AUDIO/MUSIC CODEC Type Definitions */
- SST_CODEC_TYPE_UNKNOWN = 0,
- SST_CODEC_TYPE_PCM, /* Pass through Audio codec */
- SST_CODEC_TYPE_MP3,
- SST_CODEC_TYPE_MP24,
- SST_CODEC_TYPE_AAC,
- SST_CODEC_TYPE_AACP,
- SST_CODEC_TYPE_eAACP,
-};
-
-enum stream_type {
- SST_STREAM_TYPE_NONE = 0,
- SST_STREAM_TYPE_MUSIC = 1,
-};
-
-enum sst_error_codes {
- /* Error code,response to msgId: Description */
- /* Common error codes */
- SST_SUCCESS = 0, /* Success */
- SST_ERR_INVALID_STREAM_ID = 1,
- SST_ERR_INVALID_MSG_ID = 2,
- SST_ERR_INVALID_STREAM_OP = 3,
- SST_ERR_INVALID_PARAMS = 4,
- SST_ERR_INVALID_CODEC = 5,
- SST_ERR_INVALID_MEDIA_TYPE = 6,
- SST_ERR_STREAM_ERR = 7,
-
- SST_ERR_STREAM_IN_USE = 15,
-};
-
-struct ipc_dsp_hdr {
- u16 mod_index_id:8; /*!< DSP Command ID specific to tasks */
- u16 pipe_id:8; /*!< instance of the module in the pipeline */
- u16 mod_id; /*!< Pipe_id */
- u16 cmd_id; /*!< Module ID = lpe_algo_types_t */
- u16 length; /*!< Length of the payload only */
-} __packed;
-
-union ipc_header_high {
- struct {
- u32 msg_id:8; /* Message ID - Max 256 Message Types */
- u32 task_id:4; /* Task ID associated with this comand */
- u32 drv_id:4; /* Identifier for the driver to track*/
- u32 rsvd1:8; /* Reserved */
- u32 result:4; /* Reserved */
- u32 res_rqd:1; /* Response rqd */
- u32 large:1; /* Large Message if large = 1 */
- u32 done:1; /* bit 30 - Done bit */
- u32 busy:1; /* bit 31 - busy bit*/
- } part;
- u32 full;
-} __packed;
-/* IPC header */
-union ipc_header_mrfld {
- struct {
- u32 header_low_payload;
- union ipc_header_high header_high;
- } p;
- u64 full;
-} __packed;
-/* CAUTION NOTE: All IPC message body must be multiple of 32 bits.*/
-
-/* IPC Header */
-union ipc_header {
- struct {
- u32 msg_id:8; /* Message ID - Max 256 Message Types */
- u32 str_id:5;
- u32 large:1; /* Large Message if large = 1 */
- u32 reserved:2; /* Reserved for future use */
- u32 data:14; /* Ack/Info for msg, size of msg in Mailbox */
- u32 done:1; /* bit 30 */
- u32 busy:1; /* bit 31 */
- } part;
- u32 full;
-} __packed;
-
-/* Firmware build info */
-struct sst_fw_build_info {
- unsigned char date[16]; /* Firmware build date */
- unsigned char time[16]; /* Firmware build time */
-} __packed;
-
-/* Firmware Version info */
-struct snd_sst_fw_version {
- u8 build; /* build number*/
- u8 minor; /* minor number*/
- u8 major; /* major number*/
- u8 type; /* build type */
-};
-
-struct ipc_header_fw_init {
- struct snd_sst_fw_version fw_version;/* Firmware version details */
- struct sst_fw_build_info build_info;
- u16 result; /* Fw init result */
- u8 module_id; /* Module ID in case of error */
- u8 debug_info; /* Debug info from Module ID in case of fail */
-} __packed;
-
-struct snd_sst_tstamp {
- u64 ring_buffer_counter; /* PB/CP: Bytes copied from/to DDR. */
- u64 hardware_counter; /* PB/CP: Bytes DMAed to/from SSP. */
- u64 frames_decoded;
- u64 bytes_decoded;
- u64 bytes_copied;
- u32 sampling_frequency;
- u32 channel_peak[8];
-} __packed;
-
-/* Stream type params struture for Alloc stream */
-struct snd_sst_str_type {
- u8 codec_type; /* Codec type */
- u8 str_type; /* 1 = voice 2 = music */
- u8 operation; /* Playback or Capture */
- u8 protected_str; /* 0=Non DRM, 1=DRM */
- u8 time_slots;
- u8 reserved; /* Reserved */
- u16 result; /* Result used for acknowledgment */
-} __packed;
-
-/* Library info structure */
-struct module_info {
- u32 lib_version;
- u32 lib_type;/*TBD- KLOCKWORK u8 lib_type;*/
- u32 media_type;
- u8 lib_name[12];
- u32 lib_caps;
- unsigned char b_date[16]; /* Lib build date */
- unsigned char b_time[16]; /* Lib build time */
-} __packed;
-
-/* Library slot info */
-struct lib_slot_info {
- u8 slot_num; /* 1 or 2 */
- u8 reserved1;
- u16 reserved2;
- u32 iram_size; /* slot size in IRAM */
- u32 dram_size; /* slot size in DRAM */
- u32 iram_offset; /* starting offset of slot in IRAM */
- u32 dram_offset; /* starting offset of slot in DRAM */
-} __packed;
-
-struct snd_ppp_mixer_params {
- __u32 type; /*Type of the parameter */
- __u32 size;
- __u32 input_stream_bitmap; /*Input stream Bit Map*/
-} __packed;
-
-struct snd_sst_lib_download {
- struct module_info lib_info; /* library info type, capabilities etc */
- struct lib_slot_info slot_info; /* slot info to be downloaded */
- u32 mod_entry_pt;
-};
-
-struct snd_sst_lib_download_info {
- struct snd_sst_lib_download dload_lib;
- u16 result; /* Result used for acknowledgment */
- u8 pvt_id; /* Private ID */
- u8 reserved; /* for alignment */
-};
-struct snd_pcm_params {
- u8 num_chan; /* 1=Mono, 2=Stereo */
- u8 pcm_wd_sz; /* 16/24 - bit*/
- u8 use_offload_path; /* 0-PCM using period elpased & ALSA interfaces
- 1-PCM stream via compressed interface */
- u8 reserved2;
- u32 sfreq; /* Sampling rate in Hz */
- u8 channel_map[8];
-} __packed;
-
-/* MP3 Music Parameters Message */
-struct snd_mp3_params {
- u8 num_chan; /* 1=Mono, 2=Stereo */
- u8 pcm_wd_sz; /* 16/24 - bit*/
- u8 crc_check; /* crc_check - disable (0) or enable (1) */
- u8 reserved1; /* unused*/
- u16 reserved2; /* Unused */
-} __packed;
-
-#define AAC_BIT_STREAM_ADTS 0
-#define AAC_BIT_STREAM_ADIF 1
-#define AAC_BIT_STREAM_RAW 2
-
-/* AAC Music Parameters Message */
-struct snd_aac_params {
- u8 num_chan; /* 1=Mono, 2=Stereo*/
- u8 pcm_wd_sz; /* 16/24 - bit*/
- u8 bdownsample; /*SBR downsampling 0 - disable 1 -enabled AAC+ only */
- u8 bs_format; /* input bit stream format adts=0, adif=1, raw=2 */
- u16 reser2;
- u32 externalsr; /*sampling rate of basic AAC raw bit stream*/
- u8 sbr_signalling;/*disable/enable/set automode the SBR tool.AAC+*/
- u8 reser1;
- u16 reser3;
-} __packed;
-
-/* WMA Music Parameters Message */
-struct snd_wma_params {
- u8 num_chan; /* 1=Mono, 2=Stereo */
- u8 pcm_wd_sz; /* 16/24 - bit*/
- u16 reserved1;
- u32 brate; /* Use the hard coded value. */
- u32 sfreq; /* Sampling freq eg. 8000, 441000, 48000 */
- u32 channel_mask; /* Channel Mask */
- u16 format_tag; /* Format Tag */
- u16 block_align; /* packet size */
- u16 wma_encode_opt;/* Encoder option */
- u8 op_align; /* op align 0- 16 bit, 1- MSB, 2 LSB */
- u8 reserved; /* reserved */
-} __packed;
-
-/* Codec params struture */
-union snd_sst_codec_params {
- struct snd_pcm_params pcm_params;
- struct snd_mp3_params mp3_params;
- struct snd_aac_params aac_params;
- struct snd_wma_params wma_params;
-} __packed;
-
-/* Address and size info of a frame buffer */
-struct sst_address_info {
- u32 addr; /* Address at IA */
- u32 size; /* Size of the buffer */
-};
-
-struct snd_sst_alloc_params_ext {
- __u16 sg_count;
- __u16 reserved;
- __u32 frag_size; /*Number of samples after which period elapsed
- message is sent valid only if path = 0*/
- struct sst_address_info ring_buf_info[8];
-};
-
-struct snd_sst_stream_params {
- union snd_sst_codec_params uc;
-} __packed;
-
-struct snd_sst_params {
- u32 result;
- u32 stream_id;
- u8 codec;
- u8 ops;
- u8 stream_type;
- u8 device_type;
- u8 task;
- struct snd_sst_stream_params sparams;
- struct snd_sst_alloc_params_ext aparams;
-};
-
-struct snd_sst_alloc_mrfld {
- u16 codec_type;
- u8 operation;
- u8 sg_count;
- struct sst_address_info ring_buf_info[8];
- u32 frag_size;
- u32 ts;
- struct snd_sst_stream_params codec_params;
-} __packed;
-
-/* Alloc stream params structure */
-struct snd_sst_alloc_params {
- struct snd_sst_str_type str_type;
- struct snd_sst_stream_params stream_params;
- struct snd_sst_alloc_params_ext alloc_params;
-} __packed;
-
-/* Alloc stream response message */
-struct snd_sst_alloc_response {
- struct snd_sst_str_type str_type; /* Stream type for allocation */
- struct snd_sst_lib_download lib_dnld; /* Valid only for codec dnld */
-};
-
-/* Drop response */
-struct snd_sst_drop_response {
- u32 result;
- u32 bytes;
-};
-
-struct snd_sst_async_msg {
- u32 msg_id; /* Async msg id */
- u32 payload[0];
-};
-
-struct snd_sst_async_err_msg {
- u32 fw_resp; /* Firmware Result */
- u32 lib_resp; /*Library result */
-} __packed;
-
-struct snd_sst_vol {
- u32 stream_id;
- s32 volume;
- u32 ramp_duration;
- u32 ramp_type; /* Ramp type, default=0 */
-};
-
-/* Gain library parameters for mrfld
- * based on DSP command spec v0.82
- */
-struct snd_sst_gain_v2 {
- u16 gain_cell_num; /* num of gain cells to modify*/
- u8 cell_nbr_idx; /* instance index*/
- u8 cell_path_idx; /* pipe-id */
- u16 module_id; /*module id */
- u16 left_cell_gain; /* left gain value in dB*/
- u16 right_cell_gain; /* right gain value in dB*/
- u16 gain_time_const; /* gain time constant*/
-} __packed;
-
-struct snd_sst_mute {
- u32 stream_id;
- u32 mute;
-};
-
-struct snd_sst_runtime_params {
- u8 type;
- u8 str_id;
- u8 size;
- u8 rsvd;
- void *addr;
-} __packed;
-
-enum stream_param_type {
- SST_SET_TIME_SLOT = 0,
- SST_SET_CHANNEL_INFO = 1,
- OTHERS = 2, /*reserved for future params*/
-};
-
-/* CSV Voice call routing structure */
-struct snd_sst_control_routing {
- u8 control; /* 0=start, 1=Stop */
- u8 reserved[3]; /* Reserved- for 32 bit alignment */
-};
-
-struct ipc_post {
- struct list_head node;
- union ipc_header header; /* driver specific */
- bool is_large;
- bool is_process_reply;
- union ipc_header_mrfld mrfld_header;
- char *mailbox_data;
-};
-
-struct snd_sst_ctxt_params {
- u32 address; /* Physical Address in DDR where the context is stored */
- u32 size; /* size of the context */
-};
-
-struct snd_sst_lpe_log_params {
- u8 dbg_type;
- u8 module_id;
- u8 log_level;
- u8 reserved;
-} __packed;
-
-enum snd_sst_bytes_type {
- SND_SST_BYTES_SET = 0x1,
- SND_SST_BYTES_GET = 0x2,
-};
-
-struct snd_sst_bytes_v2 {
- u8 type;
- u8 ipc_msg;
- u8 block;
- u8 task_id;
- u8 pipe_id;
- u8 rsvd;
- u16 len;
- char bytes[0];
-};
-
-#define MAX_VTSV_FILES 2
-struct snd_sst_vtsv_info {
- struct sst_address_info vfiles[MAX_VTSV_FILES];
-} __packed;
-
-#endif /* __SST_MFLD_DSP_H__ */
+++ /dev/null
-/*
- * sst_mfld_platform.c - Intel MID Platform driver
- *
- * Copyright (C) 2010-2014 Intel Corp
- * Author: Vinod Koul <vinod.koul@intel.com>
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/compress_driver.h>
-#include "sst-mfld-platform.h"
-
-/* compress stream operations */
-static void sst_compr_fragment_elapsed(void *arg)
-{
- struct snd_compr_stream *cstream = (struct snd_compr_stream *)arg;
-
- pr_debug("fragment elapsed by driver\n");
- if (cstream)
- snd_compr_fragment_elapsed(cstream);
-}
-
-static void sst_drain_notify(void *arg)
-{
- struct snd_compr_stream *cstream = (struct snd_compr_stream *)arg;
-
- pr_debug("drain notify by driver\n");
- if (cstream)
- snd_compr_drain_notify(cstream);
-}
-
-static int sst_platform_compr_open(struct snd_compr_stream *cstream)
-{
-
- int ret_val = 0;
- struct snd_compr_runtime *runtime = cstream->runtime;
- struct sst_runtime_stream *stream;
-
- stream = kzalloc(sizeof(*stream), GFP_KERNEL);
- if (!stream)
- return -ENOMEM;
-
- spin_lock_init(&stream->status_lock);
-
- /* get the sst ops */
- if (!sst || !try_module_get(sst->dev->driver->owner)) {
- pr_err("no device available to run\n");
- ret_val = -ENODEV;
- goto out_ops;
- }
- stream->compr_ops = sst->compr_ops;
- stream->id = 0;
-
- /* Turn on LPE */
- sst->compr_ops->power(sst->dev, true);
-
- sst_set_stream_status(stream, SST_PLATFORM_INIT);
- runtime->private_data = stream;
- return 0;
-out_ops:
- kfree(stream);
- return ret_val;
-}
-
-static int sst_platform_compr_free(struct snd_compr_stream *cstream)
-{
- struct sst_runtime_stream *stream;
- int ret_val = 0, str_id;
-
- stream = cstream->runtime->private_data;
- /* Turn off LPE */
- sst->compr_ops->power(sst->dev, false);
-
- /*need to check*/
- str_id = stream->id;
- if (str_id)
- ret_val = stream->compr_ops->close(sst->dev, str_id);
- module_put(sst->dev->driver->owner);
- kfree(stream);
- pr_debug("%s: %d\n", __func__, ret_val);
- return 0;
-}
-
-static int sst_platform_compr_set_params(struct snd_compr_stream *cstream,
- struct snd_compr_params *params)
-{
- struct sst_runtime_stream *stream;
- int retval;
- struct snd_sst_params str_params;
- struct sst_compress_cb cb;
- struct snd_soc_pcm_runtime *rtd = cstream->private_data;
- struct snd_soc_platform *platform = rtd->platform;
- struct sst_data *ctx = snd_soc_platform_get_drvdata(platform);
-
- stream = cstream->runtime->private_data;
- /* construct fw structure for this*/
- memset(&str_params, 0, sizeof(str_params));
-
- /* fill the device type and stream id to pass to SST driver */
- retval = sst_fill_stream_params(cstream, ctx, &str_params, true);
- pr_debug("compr_set_params: fill stream params ret_val = 0x%x\n", retval);
- if (retval < 0)
- return retval;
-
- switch (params->codec.id) {
- case SND_AUDIOCODEC_MP3: {
- str_params.codec = SST_CODEC_TYPE_MP3;
- str_params.sparams.uc.mp3_params.num_chan = params->codec.ch_in;
- str_params.sparams.uc.mp3_params.pcm_wd_sz = 16;
- break;
- }
-
- case SND_AUDIOCODEC_AAC: {
- str_params.codec = SST_CODEC_TYPE_AAC;
- str_params.sparams.uc.aac_params.num_chan = params->codec.ch_in;
- str_params.sparams.uc.aac_params.pcm_wd_sz = 16;
- if (params->codec.format == SND_AUDIOSTREAMFORMAT_MP4ADTS)
- str_params.sparams.uc.aac_params.bs_format =
- AAC_BIT_STREAM_ADTS;
- else if (params->codec.format == SND_AUDIOSTREAMFORMAT_RAW)
- str_params.sparams.uc.aac_params.bs_format =
- AAC_BIT_STREAM_RAW;
- else {
- pr_err("Undefined format%d\n", params->codec.format);
- return -EINVAL;
- }
- str_params.sparams.uc.aac_params.externalsr =
- params->codec.sample_rate;
- break;
- }
-
- default:
- pr_err("codec not supported, id =%d\n", params->codec.id);
- return -EINVAL;
- }
-
- str_params.aparams.ring_buf_info[0].addr =
- virt_to_phys(cstream->runtime->buffer);
- str_params.aparams.ring_buf_info[0].size =
- cstream->runtime->buffer_size;
- str_params.aparams.sg_count = 1;
- str_params.aparams.frag_size = cstream->runtime->fragment_size;
-
- cb.param = cstream;
- cb.compr_cb = sst_compr_fragment_elapsed;
- cb.drain_cb_param = cstream;
- cb.drain_notify = sst_drain_notify;
-
- retval = stream->compr_ops->open(sst->dev, &str_params, &cb);
- if (retval < 0) {
- pr_err("stream allocation failed %d\n", retval);
- return retval;
- }
-
- stream->id = retval;
- return 0;
-}
-
-static int sst_platform_compr_trigger(struct snd_compr_stream *cstream, int cmd)
-{
- struct sst_runtime_stream *stream = cstream->runtime->private_data;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- if (stream->compr_ops->stream_start)
- return stream->compr_ops->stream_start(sst->dev, stream->id);
- case SNDRV_PCM_TRIGGER_STOP:
- if (stream->compr_ops->stream_drop)
- return stream->compr_ops->stream_drop(sst->dev, stream->id);
- case SND_COMPR_TRIGGER_DRAIN:
- if (stream->compr_ops->stream_drain)
- return stream->compr_ops->stream_drain(sst->dev, stream->id);
- case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
- if (stream->compr_ops->stream_partial_drain)
- return stream->compr_ops->stream_partial_drain(sst->dev, stream->id);
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (stream->compr_ops->stream_pause)
- return stream->compr_ops->stream_pause(sst->dev, stream->id);
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (stream->compr_ops->stream_pause_release)
- return stream->compr_ops->stream_pause_release(sst->dev, stream->id);
- default:
- return -EINVAL;
- }
-}
-
-static int sst_platform_compr_pointer(struct snd_compr_stream *cstream,
- struct snd_compr_tstamp *tstamp)
-{
- struct sst_runtime_stream *stream;
-
- stream = cstream->runtime->private_data;
- stream->compr_ops->tstamp(sst->dev, stream->id, tstamp);
- tstamp->byte_offset = tstamp->copied_total %
- (u32)cstream->runtime->buffer_size;
- pr_debug("calc bytes offset/copied bytes as %d\n", tstamp->byte_offset);
- return 0;
-}
-
-static int sst_platform_compr_ack(struct snd_compr_stream *cstream,
- size_t bytes)
-{
- struct sst_runtime_stream *stream;
-
- stream = cstream->runtime->private_data;
- stream->compr_ops->ack(sst->dev, stream->id, (unsigned long)bytes);
- stream->bytes_written += bytes;
-
- return 0;
-}
-
-static int sst_platform_compr_get_caps(struct snd_compr_stream *cstream,
- struct snd_compr_caps *caps)
-{
- struct sst_runtime_stream *stream =
- cstream->runtime->private_data;
-
- return stream->compr_ops->get_caps(caps);
-}
-
-static int sst_platform_compr_get_codec_caps(struct snd_compr_stream *cstream,
- struct snd_compr_codec_caps *codec)
-{
- struct sst_runtime_stream *stream =
- cstream->runtime->private_data;
-
- return stream->compr_ops->get_codec_caps(codec);
-}
-
-static int sst_platform_compr_set_metadata(struct snd_compr_stream *cstream,
- struct snd_compr_metadata *metadata)
-{
- struct sst_runtime_stream *stream =
- cstream->runtime->private_data;
-
- return stream->compr_ops->set_metadata(sst->dev, stream->id, metadata);
-}
-
-struct snd_compr_ops sst_platform_compr_ops = {
-
- .open = sst_platform_compr_open,
- .free = sst_platform_compr_free,
- .set_params = sst_platform_compr_set_params,
- .set_metadata = sst_platform_compr_set_metadata,
- .trigger = sst_platform_compr_trigger,
- .pointer = sst_platform_compr_pointer,
- .ack = sst_platform_compr_ack,
- .get_caps = sst_platform_compr_get_caps,
- .get_codec_caps = sst_platform_compr_get_codec_caps,
-};
+++ /dev/null
-/*
- * sst_mfld_platform.c - Intel MID Platform driver
- *
- * Copyright (C) 2010-2014 Intel Corp
- * Author: Vinod Koul <vinod.koul@intel.com>
- * Author: Harsha Priya <priya.harsha@intel.com>
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/compress_driver.h>
-#include <asm/platform_sst_audio.h>
-#include "sst-mfld-platform.h"
-#include "sst-atom-controls.h"
-
-struct sst_device *sst;
-static DEFINE_MUTEX(sst_lock);
-extern struct snd_compr_ops sst_platform_compr_ops;
-
-int sst_register_dsp(struct sst_device *dev)
-{
- if (WARN_ON(!dev))
- return -EINVAL;
- if (!try_module_get(dev->dev->driver->owner))
- return -ENODEV;
- mutex_lock(&sst_lock);
- if (sst) {
- dev_err(dev->dev, "we already have a device %s\n", sst->name);
- module_put(dev->dev->driver->owner);
- mutex_unlock(&sst_lock);
- return -EEXIST;
- }
- dev_dbg(dev->dev, "registering device %s\n", dev->name);
- sst = dev;
- mutex_unlock(&sst_lock);
- return 0;
-}
-EXPORT_SYMBOL_GPL(sst_register_dsp);
-
-int sst_unregister_dsp(struct sst_device *dev)
-{
- if (WARN_ON(!dev))
- return -EINVAL;
- if (dev != sst)
- return -EINVAL;
-
- mutex_lock(&sst_lock);
-
- if (!sst) {
- mutex_unlock(&sst_lock);
- return -EIO;
- }
-
- module_put(sst->dev->driver->owner);
- dev_dbg(dev->dev, "unreg %s\n", sst->name);
- sst = NULL;
- mutex_unlock(&sst_lock);
- return 0;
-}
-EXPORT_SYMBOL_GPL(sst_unregister_dsp);
-
-static struct snd_pcm_hardware sst_platform_pcm_hw = {
- .info = (SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_DOUBLE |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_RESUME |
- SNDRV_PCM_INFO_MMAP|
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_SYNC_START),
- .buffer_bytes_max = SST_MAX_BUFFER,
- .period_bytes_min = SST_MIN_PERIOD_BYTES,
- .period_bytes_max = SST_MAX_PERIOD_BYTES,
- .periods_min = SST_MIN_PERIODS,
- .periods_max = SST_MAX_PERIODS,
- .fifo_size = SST_FIFO_SIZE,
-};
-
-static struct sst_dev_stream_map dpcm_strm_map[] = {
- {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, /* Reserved, not in use */
- {MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA1_IN, SST_TASK_ID_MEDIA, 0},
- {MERR_DPCM_COMPR, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA0_IN, SST_TASK_ID_MEDIA, 0},
- {MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_PCM1_OUT, SST_TASK_ID_MEDIA, 0},
-};
-
-static int sst_media_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
-{
-
- return sst_send_pipe_gains(dai, stream, mute);
-}
-
-/* helper functions */
-void sst_set_stream_status(struct sst_runtime_stream *stream,
- int state)
-{
- unsigned long flags;
- spin_lock_irqsave(&stream->status_lock, flags);
- stream->stream_status = state;
- spin_unlock_irqrestore(&stream->status_lock, flags);
-}
-
-static inline int sst_get_stream_status(struct sst_runtime_stream *stream)
-{
- int state;
- unsigned long flags;
-
- spin_lock_irqsave(&stream->status_lock, flags);
- state = stream->stream_status;
- spin_unlock_irqrestore(&stream->status_lock, flags);
- return state;
-}
-
-static void sst_fill_alloc_params(struct snd_pcm_substream *substream,
- struct snd_sst_alloc_params_ext *alloc_param)
-{
- unsigned int channels;
- snd_pcm_uframes_t period_size;
- ssize_t periodbytes;
- ssize_t buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
- u32 buffer_addr = virt_to_phys(substream->dma_buffer.area);
-
- channels = substream->runtime->channels;
- period_size = substream->runtime->period_size;
- periodbytes = samples_to_bytes(substream->runtime, period_size);
- alloc_param->ring_buf_info[0].addr = buffer_addr;
- alloc_param->ring_buf_info[0].size = buffer_bytes;
- alloc_param->sg_count = 1;
- alloc_param->reserved = 0;
- alloc_param->frag_size = periodbytes * channels;
-
-}
-static void sst_fill_pcm_params(struct snd_pcm_substream *substream,
- struct snd_sst_stream_params *param)
-{
- param->uc.pcm_params.num_chan = (u8) substream->runtime->channels;
- param->uc.pcm_params.pcm_wd_sz = substream->runtime->sample_bits;
- param->uc.pcm_params.sfreq = substream->runtime->rate;
-
- /* PCM stream via ALSA interface */
- param->uc.pcm_params.use_offload_path = 0;
- param->uc.pcm_params.reserved2 = 0;
- memset(param->uc.pcm_params.channel_map, 0, sizeof(u8));
-
-}
-
-static int sst_get_stream_mapping(int dev, int sdev, int dir,
- struct sst_dev_stream_map *map, int size)
-{
- int i;
-
- if (map == NULL)
- return -EINVAL;
-
-
- /* index 0 is not used in stream map */
- for (i = 1; i < size; i++) {
- if ((map[i].dev_num == dev) && (map[i].direction == dir))
- return i;
- }
- return 0;
-}
-
-int sst_fill_stream_params(void *substream,
- const struct sst_data *ctx, struct snd_sst_params *str_params, bool is_compress)
-{
- int map_size;
- int index;
- struct sst_dev_stream_map *map;
- struct snd_pcm_substream *pstream = NULL;
- struct snd_compr_stream *cstream = NULL;
-
- map = ctx->pdata->pdev_strm_map;
- map_size = ctx->pdata->strm_map_size;
-
- if (is_compress == true)
- cstream = (struct snd_compr_stream *)substream;
- else
- pstream = (struct snd_pcm_substream *)substream;
-
- str_params->stream_type = SST_STREAM_TYPE_MUSIC;
-
- /* For pcm streams */
- if (pstream) {
- index = sst_get_stream_mapping(pstream->pcm->device,
- pstream->number, pstream->stream,
- map, map_size);
- if (index <= 0)
- return -EINVAL;
-
- str_params->stream_id = index;
- str_params->device_type = map[index].device_id;
- str_params->task = map[index].task_id;
-
- str_params->ops = (u8)pstream->stream;
- }
-
- if (cstream) {
- index = sst_get_stream_mapping(cstream->device->device,
- 0, cstream->direction,
- map, map_size);
- if (index <= 0)
- return -EINVAL;
- str_params->stream_id = index;
- str_params->device_type = map[index].device_id;
- str_params->task = map[index].task_id;
-
- str_params->ops = (u8)cstream->direction;
- }
- return 0;
-}
-
-static int sst_platform_alloc_stream(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct sst_runtime_stream *stream =
- substream->runtime->private_data;
- struct snd_sst_stream_params param = {{{0,},},};
- struct snd_sst_params str_params = {0};
- struct snd_sst_alloc_params_ext alloc_params = {0};
- int ret_val = 0;
- struct sst_data *ctx = snd_soc_dai_get_drvdata(dai);
-
- /* set codec params and inform SST driver the same */
- sst_fill_pcm_params(substream, ¶m);
- sst_fill_alloc_params(substream, &alloc_params);
- substream->runtime->dma_area = substream->dma_buffer.area;
- str_params.sparams = param;
- str_params.aparams = alloc_params;
- str_params.codec = SST_CODEC_TYPE_PCM;
-
- /* fill the device type and stream id to pass to SST driver */
- ret_val = sst_fill_stream_params(substream, ctx, &str_params, false);
- if (ret_val < 0)
- return ret_val;
-
- stream->stream_info.str_id = str_params.stream_id;
-
- ret_val = stream->ops->open(sst->dev, &str_params);
- if (ret_val <= 0)
- return ret_val;
-
-
- return ret_val;
-}
-
-static void sst_period_elapsed(void *arg)
-{
- struct snd_pcm_substream *substream = arg;
- struct sst_runtime_stream *stream;
- int status;
-
- if (!substream || !substream->runtime)
- return;
- stream = substream->runtime->private_data;
- if (!stream)
- return;
- status = sst_get_stream_status(stream);
- if (status != SST_PLATFORM_RUNNING)
- return;
- snd_pcm_period_elapsed(substream);
-}
-
-static int sst_platform_init_stream(struct snd_pcm_substream *substream)
-{
- struct sst_runtime_stream *stream =
- substream->runtime->private_data;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- int ret_val;
-
- dev_dbg(rtd->dev, "setting buffer ptr param\n");
- sst_set_stream_status(stream, SST_PLATFORM_INIT);
- stream->stream_info.period_elapsed = sst_period_elapsed;
- stream->stream_info.arg = substream;
- stream->stream_info.buffer_ptr = 0;
- stream->stream_info.sfreq = substream->runtime->rate;
- ret_val = stream->ops->stream_init(sst->dev, &stream->stream_info);
- if (ret_val)
- dev_err(rtd->dev, "control_set ret error %d\n", ret_val);
- return ret_val;
-
-}
-
-static int power_up_sst(struct sst_runtime_stream *stream)
-{
- return stream->ops->power(sst->dev, true);
-}
-
-static void power_down_sst(struct sst_runtime_stream *stream)
-{
- stream->ops->power(sst->dev, false);
-}
-
-static int sst_media_open(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- int ret_val = 0;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct sst_runtime_stream *stream;
-
- stream = kzalloc(sizeof(*stream), GFP_KERNEL);
- if (!stream)
- return -ENOMEM;
- spin_lock_init(&stream->status_lock);
-
- /* get the sst ops */
- mutex_lock(&sst_lock);
- if (!sst ||
- !try_module_get(sst->dev->driver->owner)) {
- dev_err(dai->dev, "no device available to run\n");
- ret_val = -ENODEV;
- goto out_ops;
- }
- stream->ops = sst->ops;
- mutex_unlock(&sst_lock);
-
- stream->stream_info.str_id = 0;
-
- stream->stream_info.arg = substream;
- /* allocate memory for SST API set */
- runtime->private_data = stream;
-
- ret_val = power_up_sst(stream);
- if (ret_val < 0)
- return ret_val;
-
- /* Make sure, that the period size is always even */
- snd_pcm_hw_constraint_step(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_PERIODS, 2);
-
- return snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS);
-out_ops:
- kfree(stream);
- mutex_unlock(&sst_lock);
- return ret_val;
-}
-
-static void sst_media_close(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct sst_runtime_stream *stream;
- int ret_val = 0, str_id;
-
- stream = substream->runtime->private_data;
- power_down_sst(stream);
-
- str_id = stream->stream_info.str_id;
- if (str_id)
- ret_val = stream->ops->close(sst->dev, str_id);
- module_put(sst->dev->driver->owner);
- kfree(stream);
-}
-
-static inline unsigned int get_current_pipe_id(struct snd_soc_dai *dai,
- struct snd_pcm_substream *substream)
-{
- struct sst_data *sst = snd_soc_dai_get_drvdata(dai);
- struct sst_dev_stream_map *map = sst->pdata->pdev_strm_map;
- struct sst_runtime_stream *stream =
- substream->runtime->private_data;
- u32 str_id = stream->stream_info.str_id;
- unsigned int pipe_id;
-
- pipe_id = map[str_id].device_id;
-
- dev_dbg(dai->dev, "got pipe_id = %#x for str_id = %d\n",
- pipe_id, str_id);
- return pipe_id;
-}
-
-static int sst_media_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct sst_runtime_stream *stream;
- int ret_val = 0, str_id;
-
- stream = substream->runtime->private_data;
- str_id = stream->stream_info.str_id;
- if (stream->stream_info.str_id) {
- ret_val = stream->ops->stream_drop(sst->dev, str_id);
- return ret_val;
- }
-
- ret_val = sst_platform_alloc_stream(substream, dai);
- if (ret_val <= 0)
- return ret_val;
- snprintf(substream->pcm->id, sizeof(substream->pcm->id),
- "%d", stream->stream_info.str_id);
-
- ret_val = sst_platform_init_stream(substream);
- if (ret_val)
- return ret_val;
- substream->runtime->hw.info = SNDRV_PCM_INFO_BLOCK_TRANSFER;
- return ret_val;
-}
-
-static int sst_media_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
- memset(substream->runtime->dma_area, 0, params_buffer_bytes(params));
- return 0;
-}
-
-static int sst_media_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- return snd_pcm_lib_free_pages(substream);
-}
-
-static int sst_enable_ssp(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- int ret = 0;
-
- if (!dai->active) {
- ret = sst_handle_vb_timer(dai, true);
- if (ret)
- return ret;
- ret = send_ssp_cmd(dai, dai->name, 1);
- }
- return ret;
-}
-
-static void sst_disable_ssp(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- if (!dai->active) {
- send_ssp_cmd(dai, dai->name, 0);
- sst_handle_vb_timer(dai, false);
- }
-}
-
-static struct snd_soc_dai_ops sst_media_dai_ops = {
- .startup = sst_media_open,
- .shutdown = sst_media_close,
- .prepare = sst_media_prepare,
- .hw_params = sst_media_hw_params,
- .hw_free = sst_media_hw_free,
- .mute_stream = sst_media_digital_mute,
-};
-
-static struct snd_soc_dai_ops sst_compr_dai_ops = {
- .mute_stream = sst_media_digital_mute,
-};
-
-static struct snd_soc_dai_ops sst_be_dai_ops = {
- .startup = sst_enable_ssp,
- .shutdown = sst_disable_ssp,
-};
-
-static struct snd_soc_dai_driver sst_platform_dai[] = {
-{
- .name = "media-cpu-dai",
- .ops = &sst_media_dai_ops,
- .playback = {
- .stream_name = "Headset Playback",
- .channels_min = SST_STEREO,
- .channels_max = SST_STEREO,
- .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .stream_name = "Headset Capture",
- .channels_min = 1,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
-},
-{
- .name = "compress-cpu-dai",
- .compress_dai = 1,
- .ops = &sst_compr_dai_ops,
- .playback = {
- .stream_name = "Compress Playback",
- .channels_min = SST_STEREO,
- .channels_max = SST_STEREO,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
-},
-/* BE CPU Dais */
-{
- .name = "ssp0-port",
- .ops = &sst_be_dai_ops,
- .playback = {
- .stream_name = "ssp0 Tx",
- .channels_min = SST_STEREO,
- .channels_max = SST_STEREO,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .stream_name = "ssp0 Rx",
- .channels_min = SST_STEREO,
- .channels_max = SST_STEREO,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
-},
-{
- .name = "ssp1-port",
- .ops = &sst_be_dai_ops,
- .playback = {
- .stream_name = "ssp1 Tx",
- .channels_min = SST_STEREO,
- .channels_max = SST_STEREO,
- .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .stream_name = "ssp1 Rx",
- .channels_min = SST_STEREO,
- .channels_max = SST_STEREO,
- .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
-},
-{
- .name = "ssp2-port",
- .ops = &sst_be_dai_ops,
- .playback = {
- .stream_name = "ssp2 Tx",
- .channels_min = SST_STEREO,
- .channels_max = SST_STEREO,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .stream_name = "ssp2 Rx",
- .channels_min = SST_STEREO,
- .channels_max = SST_STEREO,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
-},
-};
-
-static int sst_platform_open(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime;
-
- if (substream->pcm->internal)
- return 0;
-
- runtime = substream->runtime;
- runtime->hw = sst_platform_pcm_hw;
- return 0;
-}
-
-static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream,
- int cmd)
-{
- int ret_val = 0, str_id;
- struct sst_runtime_stream *stream;
- int status;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
-
- dev_dbg(rtd->dev, "sst_platform_pcm_trigger called\n");
- if (substream->pcm->internal)
- return 0;
- stream = substream->runtime->private_data;
- str_id = stream->stream_info.str_id;
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- dev_dbg(rtd->dev, "sst: Trigger Start\n");
- status = SST_PLATFORM_RUNNING;
- stream->stream_info.arg = substream;
- ret_val = stream->ops->stream_start(sst->dev, str_id);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- dev_dbg(rtd->dev, "sst: in stop\n");
- status = SST_PLATFORM_DROPPED;
- ret_val = stream->ops->stream_drop(sst->dev, str_id);
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- dev_dbg(rtd->dev, "sst: in pause\n");
- status = SST_PLATFORM_PAUSED;
- ret_val = stream->ops->stream_pause(sst->dev, str_id);
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- dev_dbg(rtd->dev, "sst: in pause release\n");
- status = SST_PLATFORM_RUNNING;
- ret_val = stream->ops->stream_pause_release(sst->dev, str_id);
- break;
- default:
- return -EINVAL;
- }
-
- if (!ret_val)
- sst_set_stream_status(stream, status);
-
- return ret_val;
-}
-
-
-static snd_pcm_uframes_t sst_platform_pcm_pointer
- (struct snd_pcm_substream *substream)
-{
- struct sst_runtime_stream *stream;
- int ret_val, status;
- struct pcm_stream_info *str_info;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
-
- stream = substream->runtime->private_data;
- status = sst_get_stream_status(stream);
- if (status == SST_PLATFORM_INIT)
- return 0;
- str_info = &stream->stream_info;
- ret_val = stream->ops->stream_read_tstamp(sst->dev, str_info);
- if (ret_val) {
- dev_err(rtd->dev, "sst: error code = %d\n", ret_val);
- return ret_val;
- }
- substream->runtime->delay = str_info->pcm_delay;
- return str_info->buffer_ptr;
-}
-
-static struct snd_pcm_ops sst_platform_ops = {
- .open = sst_platform_open,
- .ioctl = snd_pcm_lib_ioctl,
- .trigger = sst_platform_pcm_trigger,
- .pointer = sst_platform_pcm_pointer,
-};
-
-static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_dai *dai = rtd->cpu_dai;
- struct snd_pcm *pcm = rtd->pcm;
- int retval = 0;
-
- if (dai->driver->playback.channels_min ||
- dai->driver->capture.channels_min) {
- retval = snd_pcm_lib_preallocate_pages_for_all(pcm,
- SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_DMA),
- SST_MIN_BUFFER, SST_MAX_BUFFER);
- if (retval) {
- dev_err(rtd->dev, "dma buffer allocationf fail\n");
- return retval;
- }
- }
- return retval;
-}
-
-static int sst_soc_probe(struct snd_soc_platform *platform)
-{
- return sst_dsp_init_v2_dpcm(platform);
-}
-
-static struct snd_soc_platform_driver sst_soc_platform_drv = {
- .probe = sst_soc_probe,
- .ops = &sst_platform_ops,
- .compr_ops = &sst_platform_compr_ops,
- .pcm_new = sst_pcm_new,
-};
-
-static const struct snd_soc_component_driver sst_component = {
- .name = "sst",
-};
-
-
-static int sst_platform_probe(struct platform_device *pdev)
-{
- struct sst_data *drv;
- int ret;
- struct sst_platform_data *pdata;
-
- drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
- if (drv == NULL) {
- return -ENOMEM;
- }
-
- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (pdata == NULL) {
- return -ENOMEM;
- }
-
- pdata->pdev_strm_map = dpcm_strm_map;
- pdata->strm_map_size = ARRAY_SIZE(dpcm_strm_map);
- drv->pdata = pdata;
- drv->pdev = pdev;
- mutex_init(&drv->lock);
- dev_set_drvdata(&pdev->dev, drv);
-
- ret = snd_soc_register_platform(&pdev->dev, &sst_soc_platform_drv);
- if (ret) {
- dev_err(&pdev->dev, "registering soc platform failed\n");
- return ret;
- }
-
- ret = snd_soc_register_component(&pdev->dev, &sst_component,
- sst_platform_dai, ARRAY_SIZE(sst_platform_dai));
- if (ret) {
- dev_err(&pdev->dev, "registering cpu dais failed\n");
- snd_soc_unregister_platform(&pdev->dev);
- }
- return ret;
-}
-
-static int sst_platform_remove(struct platform_device *pdev)
-{
-
- snd_soc_unregister_component(&pdev->dev);
- snd_soc_unregister_platform(&pdev->dev);
- dev_dbg(&pdev->dev, "sst_platform_remove success\n");
- return 0;
-}
-
-static struct platform_driver sst_platform_driver = {
- .driver = {
- .name = "sst-mfld-platform",
- },
- .probe = sst_platform_probe,
- .remove = sst_platform_remove,
-};
-
-module_platform_driver(sst_platform_driver);
-
-MODULE_DESCRIPTION("ASoC Intel(R) MID Platform driver");
-MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
-MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:sst-mfld-platform");
+++ /dev/null
-/*
- * sst_mfld_platform.h - Intel MID Platform driver header file
- *
- * Copyright (C) 2010 Intel Corp
- * Author: Vinod Koul <vinod.koul@intel.com>
- * Author: Harsha Priya <priya.harsha@intel.com>
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-
-#ifndef __SST_PLATFORMDRV_H__
-#define __SST_PLATFORMDRV_H__
-
-#include "sst-mfld-dsp.h"
-
-extern struct sst_device *sst;
-
-#define SST_MONO 1
-#define SST_STEREO 2
-#define SST_MAX_CAP 5
-
-#define SST_MAX_BUFFER (800*1024)
-#define SST_MIN_BUFFER (800*1024)
-#define SST_MIN_PERIOD_BYTES 32
-#define SST_MAX_PERIOD_BYTES SST_MAX_BUFFER
-#define SST_MIN_PERIODS 2
-#define SST_MAX_PERIODS (1024*2)
-#define SST_FIFO_SIZE 0
-
-struct pcm_stream_info {
- int str_id;
- void *arg;
- void (*period_elapsed) (void *arg);
- unsigned long long buffer_ptr;
- unsigned long long pcm_delay;
- int sfreq;
-};
-
-enum sst_drv_status {
- SST_PLATFORM_INIT = 1,
- SST_PLATFORM_STARTED,
- SST_PLATFORM_RUNNING,
- SST_PLATFORM_PAUSED,
- SST_PLATFORM_DROPPED,
-};
-
-enum sst_stream_ops {
- STREAM_OPS_PLAYBACK = 0,
- STREAM_OPS_CAPTURE,
-};
-
-enum sst_audio_device_type {
- SND_SST_DEVICE_HEADSET = 1,
- SND_SST_DEVICE_IHF,
- SND_SST_DEVICE_VIBRA,
- SND_SST_DEVICE_HAPTIC,
- SND_SST_DEVICE_CAPTURE,
- SND_SST_DEVICE_COMPRESS,
-};
-
-/* PCM Parameters */
-struct sst_pcm_params {
- u16 codec; /* codec type */
- u8 num_chan; /* 1=Mono, 2=Stereo */
- u8 pcm_wd_sz; /* 16/24 - bit*/
- u32 reserved; /* Bitrate in bits per second */
- u32 sfreq; /* Sampling rate in Hz */
- u32 ring_buffer_size;
- u32 period_count; /* period elapsed in samples*/
- u32 ring_buffer_addr;
-};
-
-struct sst_stream_params {
- u32 result;
- u32 stream_id;
- u8 codec;
- u8 ops;
- u8 stream_type;
- u8 device_type;
- struct sst_pcm_params sparams;
-};
-
-struct sst_compress_cb {
- void *param;
- void (*compr_cb)(void *param);
- void *drain_cb_param;
- void (*drain_notify)(void *param);
-};
-
-struct compress_sst_ops {
- const char *name;
- int (*open)(struct device *dev,
- struct snd_sst_params *str_params, struct sst_compress_cb *cb);
- int (*stream_start)(struct device *dev, unsigned int str_id);
- int (*stream_drop)(struct device *dev, unsigned int str_id);
- int (*stream_drain)(struct device *dev, unsigned int str_id);
- int (*stream_partial_drain)(struct device *dev, unsigned int str_id);
- int (*stream_pause)(struct device *dev, unsigned int str_id);
- int (*stream_pause_release)(struct device *dev, unsigned int str_id);
-
- int (*tstamp)(struct device *dev, unsigned int str_id,
- struct snd_compr_tstamp *tstamp);
- int (*ack)(struct device *dev, unsigned int str_id,
- unsigned long bytes);
- int (*close)(struct device *dev, unsigned int str_id);
- int (*get_caps)(struct snd_compr_caps *caps);
- int (*get_codec_caps)(struct snd_compr_codec_caps *codec);
- int (*set_metadata)(struct device *dev, unsigned int str_id,
- struct snd_compr_metadata *mdata);
- int (*power)(struct device *dev, bool state);
-};
-
-struct sst_ops {
- int (*open)(struct device *dev, struct snd_sst_params *str_param);
- int (*stream_init)(struct device *dev, struct pcm_stream_info *str_info);
- int (*stream_start)(struct device *dev, int str_id);
- int (*stream_drop)(struct device *dev, int str_id);
- int (*stream_pause)(struct device *dev, int str_id);
- int (*stream_pause_release)(struct device *dev, int str_id);
- int (*stream_read_tstamp)(struct device *dev, struct pcm_stream_info *str_info);
- int (*send_byte_stream)(struct device *dev, struct snd_sst_bytes_v2 *bytes);
- int (*close)(struct device *dev, unsigned int str_id);
- int (*power)(struct device *dev, bool state);
-};
-
-struct sst_runtime_stream {
- int stream_status;
- unsigned int id;
- size_t bytes_written;
- struct pcm_stream_info stream_info;
- struct sst_ops *ops;
- struct compress_sst_ops *compr_ops;
- spinlock_t status_lock;
-};
-
-struct sst_device {
- char *name;
- struct device *dev;
- struct sst_ops *ops;
- struct platform_device *pdev;
- struct compress_sst_ops *compr_ops;
-};
-
-struct sst_data;
-
-int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform);
-int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute);
-int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable);
-int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable);
-
-void sst_set_stream_status(struct sst_runtime_stream *stream, int state);
-int sst_fill_stream_params(void *substream, const struct sst_data *ctx,
- struct snd_sst_params *str_params, bool is_compress);
-
-struct sst_algo_int_control_v2 {
- struct soc_mixer_control mc;
- u16 module_id; /* module identifieer */
- u16 pipe_id; /* location info: pipe_id + instance_id */
- u16 instance_id;
- unsigned int value; /* Value received is stored here */
-};
-struct sst_data {
- struct platform_device *pdev;
- struct sst_platform_data *pdata;
- struct snd_sst_bytes_v2 *byte_stream;
- struct mutex lock;
-};
-int sst_register_dsp(struct sst_device *sst);
-int sst_unregister_dsp(struct sst_device *sst);
-#endif
+++ /dev/null
-snd-intel-sst-core-objs := sst.o sst_ipc.o sst_stream.o sst_drv_interface.o sst_loader.o sst_pvt.o
-snd-intel-sst-pci-objs += sst_pci.o
-snd-intel-sst-acpi-objs += sst_acpi.o
-
-obj-$(CONFIG_SND_SST_IPC) += snd-intel-sst-core.o
-obj-$(CONFIG_SND_SST_IPC_PCI) += snd-intel-sst-pci.o
-obj-$(CONFIG_SND_SST_IPC_ACPI) += snd-intel-sst-acpi.o
+++ /dev/null
-/*
- * sst.c - Intel SST Driver for audio engine
- *
- * Copyright (C) 2008-14 Intel Corp
- * Authors: Vinod Koul <vinod.koul@intel.com>
- * Harsha Priya <priya.harsha@intel.com>
- * Dharageswari R <dharageswari.r@intel.com>
- * KP Jeeja <jeeja.kp@intel.com>
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-#include <linux/module.h>
-#include <linux/fs.h>
-#include <linux/interrupt.h>
-#include <linux/firmware.h>
-#include <linux/pm_runtime.h>
-#include <linux/pm_qos.h>
-#include <linux/async.h>
-#include <linux/acpi.h>
-#include <sound/core.h>
-#include <sound/soc.h>
-#include <asm/platform_sst_audio.h>
-#include "../sst-mfld-platform.h"
-#include "sst.h"
-#include "../sst-dsp.h"
-
-MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
-MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
-MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine Driver");
-MODULE_LICENSE("GPL v2");
-
-static inline bool sst_is_process_reply(u32 msg_id)
-{
- return ((msg_id & PROCESS_MSG) ? true : false);
-}
-
-static inline bool sst_validate_mailbox_size(unsigned int size)
-{
- return ((size <= SST_MAILBOX_SIZE) ? true : false);
-}
-
-static irqreturn_t intel_sst_interrupt_mrfld(int irq, void *context)
-{
- union interrupt_reg_mrfld isr;
- union ipc_header_mrfld header;
- union sst_imr_reg_mrfld imr;
- struct ipc_post *msg = NULL;
- unsigned int size = 0;
- struct intel_sst_drv *drv = (struct intel_sst_drv *) context;
- irqreturn_t retval = IRQ_HANDLED;
-
- /* Interrupt arrived, check src */
- isr.full = sst_shim_read64(drv->shim, SST_ISRX);
-
- if (isr.part.done_interrupt) {
- /* Clear done bit */
- spin_lock(&drv->ipc_spin_lock);
- header.full = sst_shim_read64(drv->shim,
- drv->ipc_reg.ipcx);
- header.p.header_high.part.done = 0;
- sst_shim_write64(drv->shim, drv->ipc_reg.ipcx, header.full);
-
- /* write 1 to clear status register */;
- isr.part.done_interrupt = 1;
- sst_shim_write64(drv->shim, SST_ISRX, isr.full);
- spin_unlock(&drv->ipc_spin_lock);
-
- /* we can send more messages to DSP so trigger work */
- queue_work(drv->post_msg_wq, &drv->ipc_post_msg_wq);
- retval = IRQ_HANDLED;
- }
-
- if (isr.part.busy_interrupt) {
- /* message from dsp so copy that */
- spin_lock(&drv->ipc_spin_lock);
- imr.full = sst_shim_read64(drv->shim, SST_IMRX);
- imr.part.busy_interrupt = 1;
- sst_shim_write64(drv->shim, SST_IMRX, imr.full);
- spin_unlock(&drv->ipc_spin_lock);
- header.full = sst_shim_read64(drv->shim, drv->ipc_reg.ipcd);
-
- if (sst_create_ipc_msg(&msg, header.p.header_high.part.large)) {
- drv->ops->clear_interrupt(drv);
- return IRQ_HANDLED;
- }
-
- if (header.p.header_high.part.large) {
- size = header.p.header_low_payload;
- if (sst_validate_mailbox_size(size)) {
- memcpy_fromio(msg->mailbox_data,
- drv->mailbox + drv->mailbox_recv_offset, size);
- } else {
- dev_err(drv->dev,
- "Mailbox not copied, payload size is: %u\n", size);
- header.p.header_low_payload = 0;
- }
- }
-
- msg->mrfld_header = header;
- msg->is_process_reply =
- sst_is_process_reply(header.p.header_high.part.msg_id);
- spin_lock(&drv->rx_msg_lock);
- list_add_tail(&msg->node, &drv->rx_list);
- spin_unlock(&drv->rx_msg_lock);
- drv->ops->clear_interrupt(drv);
- retval = IRQ_WAKE_THREAD;
- }
- return retval;
-}
-
-static irqreturn_t intel_sst_irq_thread_mrfld(int irq, void *context)
-{
- struct intel_sst_drv *drv = (struct intel_sst_drv *) context;
- struct ipc_post *__msg, *msg = NULL;
- unsigned long irq_flags;
-
- spin_lock_irqsave(&drv->rx_msg_lock, irq_flags);
- if (list_empty(&drv->rx_list)) {
- spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
- return IRQ_HANDLED;
- }
-
- list_for_each_entry_safe(msg, __msg, &drv->rx_list, node) {
- list_del(&msg->node);
- spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
- if (msg->is_process_reply)
- drv->ops->process_message(msg);
- else
- drv->ops->process_reply(drv, msg);
-
- if (msg->is_large)
- kfree(msg->mailbox_data);
- kfree(msg);
- spin_lock_irqsave(&drv->rx_msg_lock, irq_flags);
- }
- spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
- return IRQ_HANDLED;
-}
-
-static int sst_save_dsp_context_v2(struct intel_sst_drv *sst)
-{
- int ret = 0;
-
- ret = sst_prepare_and_post_msg(sst, SST_TASK_ID_MEDIA, IPC_CMD,
- IPC_PREP_D3, PIPE_RSVD, 0, NULL, NULL,
- true, true, false, true);
-
- if (ret < 0) {
- dev_err(sst->dev, "not suspending FW!!, Err: %d\n", ret);
- return -EIO;
- }
-
- return 0;
-}
-
-
-static struct intel_sst_ops mrfld_ops = {
- .interrupt = intel_sst_interrupt_mrfld,
- .irq_thread = intel_sst_irq_thread_mrfld,
- .clear_interrupt = intel_sst_clear_intr_mrfld,
- .start = sst_start_mrfld,
- .reset = intel_sst_reset_dsp_mrfld,
- .post_message = sst_post_message_mrfld,
- .process_reply = sst_process_reply_mrfld,
- .save_dsp_context = sst_save_dsp_context_v2,
- .alloc_stream = sst_alloc_stream_mrfld,
- .post_download = sst_post_download_mrfld,
-};
-
-int sst_driver_ops(struct intel_sst_drv *sst)
-{
-
- switch (sst->dev_id) {
- case SST_MRFLD_PCI_ID:
- case SST_BYT_ACPI_ID:
- case SST_CHV_ACPI_ID:
- sst->tstamp = SST_TIME_STAMP_MRFLD;
- sst->ops = &mrfld_ops;
- return 0;
-
- default:
- dev_err(sst->dev,
- "SST Driver capablities missing for dev_id: %x", sst->dev_id);
- return -EINVAL;
- };
-}
-
-void sst_process_pending_msg(struct work_struct *work)
-{
- struct intel_sst_drv *ctx = container_of(work,
- struct intel_sst_drv, ipc_post_msg_wq);
-
- ctx->ops->post_message(ctx, NULL, false);
-}
-
-static int sst_workqueue_init(struct intel_sst_drv *ctx)
-{
- INIT_LIST_HEAD(&ctx->memcpy_list);
- INIT_LIST_HEAD(&ctx->rx_list);
- INIT_LIST_HEAD(&ctx->ipc_dispatch_list);
- INIT_LIST_HEAD(&ctx->block_list);
- INIT_WORK(&ctx->ipc_post_msg_wq, sst_process_pending_msg);
- init_waitqueue_head(&ctx->wait_queue);
-
- ctx->post_msg_wq =
- create_singlethread_workqueue("sst_post_msg_wq");
- if (!ctx->post_msg_wq)
- return -EBUSY;
- return 0;
-}
-
-static void sst_init_locks(struct intel_sst_drv *ctx)
-{
- mutex_init(&ctx->sst_lock);
- spin_lock_init(&ctx->rx_msg_lock);
- spin_lock_init(&ctx->ipc_spin_lock);
- spin_lock_init(&ctx->block_lock);
-}
-
-int sst_alloc_drv_context(struct intel_sst_drv **ctx,
- struct device *dev, unsigned int dev_id)
-{
- *ctx = devm_kzalloc(dev, sizeof(struct intel_sst_drv), GFP_KERNEL);
- if (!(*ctx))
- return -ENOMEM;
-
- (*ctx)->dev = dev;
- (*ctx)->dev_id = dev_id;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(sst_alloc_drv_context);
-
-int sst_context_init(struct intel_sst_drv *ctx)
-{
- int ret = 0, i;
-
- if (!ctx->pdata)
- return -EINVAL;
-
- if (!ctx->pdata->probe_data)
- return -EINVAL;
-
- memcpy(&ctx->info, ctx->pdata->probe_data, sizeof(ctx->info));
-
- ret = sst_driver_ops(ctx);
- if (ret != 0)
- return -EINVAL;
-
- sst_init_locks(ctx);
- sst_set_fw_state_locked(ctx, SST_RESET);
-
- /* pvt_id 0 reserved for async messages */
- ctx->pvt_id = 1;
- ctx->stream_cnt = 0;
- ctx->fw_in_mem = NULL;
- /* we use memcpy, so set to 0 */
- ctx->use_dma = 0;
- ctx->use_lli = 0;
-
- if (sst_workqueue_init(ctx))
- return -EINVAL;
-
- ctx->mailbox_recv_offset = ctx->pdata->ipc_info->mbox_recv_off;
- ctx->ipc_reg.ipcx = SST_IPCX + ctx->pdata->ipc_info->ipc_offset;
- ctx->ipc_reg.ipcd = SST_IPCD + ctx->pdata->ipc_info->ipc_offset;
-
- dev_info(ctx->dev, "Got drv data max stream %d\n",
- ctx->info.max_streams);
-
- for (i = 1; i <= ctx->info.max_streams; i++) {
- struct stream_info *stream = &ctx->streams[i];
-
- memset(stream, 0, sizeof(*stream));
- stream->pipe_id = PIPE_RSVD;
- mutex_init(&stream->lock);
- }
-
- /* Register the ISR */
- ret = devm_request_threaded_irq(ctx->dev, ctx->irq_num, ctx->ops->interrupt,
- ctx->ops->irq_thread, 0, SST_DRV_NAME,
- ctx);
- if (ret)
- goto do_free_mem;
-
- dev_dbg(ctx->dev, "Registered IRQ %#x\n", ctx->irq_num);
-
- /* default intr are unmasked so set this as masked */
- sst_shim_write64(ctx->shim, SST_IMRX, 0xFFFF0038);
-
- ctx->qos = devm_kzalloc(ctx->dev,
- sizeof(struct pm_qos_request), GFP_KERNEL);
- if (!ctx->qos) {
- ret = -ENOMEM;
- goto do_free_mem;
- }
- pm_qos_add_request(ctx->qos, PM_QOS_CPU_DMA_LATENCY,
- PM_QOS_DEFAULT_VALUE);
-
- dev_dbg(ctx->dev, "Requesting FW %s now...\n", ctx->firmware_name);
- ret = request_firmware_nowait(THIS_MODULE, true, ctx->firmware_name,
- ctx->dev, GFP_KERNEL, ctx, sst_firmware_load_cb);
- if (ret) {
- dev_err(ctx->dev, "Firmware download failed:%d\n", ret);
- goto do_free_mem;
- }
- sst_register(ctx->dev);
- return 0;
-
-do_free_mem:
- destroy_workqueue(ctx->post_msg_wq);
- return ret;
-}
-EXPORT_SYMBOL_GPL(sst_context_init);
-
-void sst_context_cleanup(struct intel_sst_drv *ctx)
-{
- pm_runtime_get_noresume(ctx->dev);
- pm_runtime_disable(ctx->dev);
- sst_unregister(ctx->dev);
- sst_set_fw_state_locked(ctx, SST_SHUTDOWN);
- flush_scheduled_work();
- destroy_workqueue(ctx->post_msg_wq);
- pm_qos_remove_request(ctx->qos);
- kfree(ctx->fw_sg_list.src);
- kfree(ctx->fw_sg_list.dst);
- ctx->fw_sg_list.list_len = 0;
- kfree(ctx->fw_in_mem);
- ctx->fw_in_mem = NULL;
- sst_memcpy_free_resources(ctx);
- ctx = NULL;
-}
-EXPORT_SYMBOL_GPL(sst_context_cleanup);
-
-static inline void sst_save_shim64(struct intel_sst_drv *ctx,
- void __iomem *shim,
- struct sst_shim_regs64 *shim_regs)
-{
- unsigned long irq_flags;
-
- spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags);
-
- shim_regs->imrx = sst_shim_read64(shim, SST_IMRX),
-
- spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags);
-}
-
-static inline void sst_restore_shim64(struct intel_sst_drv *ctx,
- void __iomem *shim,
- struct sst_shim_regs64 *shim_regs)
-{
- unsigned long irq_flags;
-
- /*
- * we only need to restore IMRX for this case, rest will be
- * initialize by FW or driver when firmware is loaded
- */
- spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags);
- sst_shim_write64(shim, SST_IMRX, shim_regs->imrx),
- spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags);
-}
-
-void sst_configure_runtime_pm(struct intel_sst_drv *ctx)
-{
- pm_runtime_set_autosuspend_delay(ctx->dev, SST_SUSPEND_DELAY);
- pm_runtime_use_autosuspend(ctx->dev);
- /*
- * For acpi devices, the actual physical device state is
- * initially active. So change the state to active before
- * enabling the pm
- */
- pm_runtime_enable(ctx->dev);
-
- if (acpi_disabled)
- pm_runtime_set_active(ctx->dev);
- else
- pm_runtime_put_noidle(ctx->dev);
-
- sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64);
-}
-EXPORT_SYMBOL_GPL(sst_configure_runtime_pm);
-
-static int intel_sst_runtime_suspend(struct device *dev)
-{
- int ret = 0;
- struct intel_sst_drv *ctx = dev_get_drvdata(dev);
-
- if (ctx->sst_state == SST_RESET) {
- dev_dbg(dev, "LPE is already in RESET state, No action\n");
- return 0;
- }
- /* save fw context */
- if (ctx->ops->save_dsp_context(ctx))
- return -EBUSY;
-
- /* Move the SST state to Reset */
- sst_set_fw_state_locked(ctx, SST_RESET);
-
- synchronize_irq(ctx->irq_num);
- flush_workqueue(ctx->post_msg_wq);
-
- /* save the shim registers because PMC doesn't save state */
- sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64);
-
- return ret;
-}
-
-static int intel_sst_runtime_resume(struct device *dev)
-{
- int ret = 0;
- struct intel_sst_drv *ctx = dev_get_drvdata(dev);
-
- if (ctx->sst_state == SST_RESET) {
- ret = sst_load_fw(ctx);
- if (ret) {
- dev_err(dev, "FW download fail %d\n", ret);
- sst_set_fw_state_locked(ctx, SST_RESET);
- }
- }
- return ret;
-}
-
-const struct dev_pm_ops intel_sst_pm = {
- .runtime_suspend = intel_sst_runtime_suspend,
- .runtime_resume = intel_sst_runtime_resume,
-};
-EXPORT_SYMBOL_GPL(intel_sst_pm);
+++ /dev/null
-/*
- * sst.h - Intel SST Driver for audio engine
- *
- * Copyright (C) 2008-14 Intel Corporation
- * Authors: Vinod Koul <vinod.koul@intel.com>
- * Harsha Priya <priya.harsha@intel.com>
- * Dharageswari R <dharageswari.r@intel.com>
- * KP Jeeja <jeeja.kp@intel.com>
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * Common private declarations for SST
- */
-#ifndef __SST_H__
-#define __SST_H__
-
-#include <linux/firmware.h>
-
-/* driver names */
-#define SST_DRV_NAME "intel_sst_driver"
-#define SST_MRFLD_PCI_ID 0x119A
-#define SST_BYT_ACPI_ID 0x80860F28
-#define SST_CHV_ACPI_ID 0x808622A8
-
-#define SST_SUSPEND_DELAY 2000
-#define FW_CONTEXT_MEM (64*1024)
-#define SST_ICCM_BOUNDARY 4
-#define SST_CONFIG_SSP_SIGN 0x7ffe8001
-
-#define MRFLD_FW_VIRTUAL_BASE 0xC0000000
-#define MRFLD_FW_DDR_BASE_OFFSET 0x0
-#define MRFLD_FW_FEATURE_BASE_OFFSET 0x4
-#define MRFLD_FW_BSS_RESET_BIT 0
-
-extern const struct dev_pm_ops intel_sst_pm;
-enum sst_states {
- SST_FW_LOADING = 1,
- SST_FW_RUNNING,
- SST_RESET,
- SST_SHUTDOWN,
-};
-
-enum sst_algo_ops {
- SST_SET_ALGO = 0,
- SST_GET_ALGO = 1,
-};
-
-#define SST_BLOCK_TIMEOUT 1000
-
-#define FW_SIGNATURE_SIZE 4
-#define FW_NAME_SIZE 32
-
-/* stream states */
-enum sst_stream_states {
- STREAM_UN_INIT = 0, /* Freed/Not used stream */
- STREAM_RUNNING = 1, /* Running */
- STREAM_PAUSED = 2, /* Paused stream */
- STREAM_DECODE = 3, /* stream is in decoding only state */
- STREAM_INIT = 4, /* stream init, waiting for data */
- STREAM_RESET = 5, /* force reset on recovery */
-};
-
-enum sst_ram_type {
- SST_IRAM = 1,
- SST_DRAM = 2,
- SST_DDR = 5,
- SST_CUSTOM_INFO = 7, /* consists of FW binary information */
-};
-
-/* SST shim registers to structure mapping */
-union interrupt_reg {
- struct {
- u64 done_interrupt:1;
- u64 busy_interrupt:1;
- u64 rsvd:62;
- } part;
- u64 full;
-};
-
-union sst_pisr_reg {
- struct {
- u32 pssp0:1;
- u32 pssp1:1;
- u32 rsvd0:3;
- u32 dmac:1;
- u32 rsvd1:26;
- } part;
- u32 full;
-};
-
-union sst_pimr_reg {
- struct {
- u32 ssp0:1;
- u32 ssp1:1;
- u32 rsvd0:3;
- u32 dmac:1;
- u32 rsvd1:10;
- u32 ssp0_sc:1;
- u32 ssp1_sc:1;
- u32 rsvd2:3;
- u32 dmac_sc:1;
- u32 rsvd3:10;
- } part;
- u32 full;
-};
-
-union config_status_reg_mrfld {
- struct {
- u64 lpe_reset:1;
- u64 lpe_reset_vector:1;
- u64 runstall:1;
- u64 pwaitmode:1;
- u64 clk_sel:3;
- u64 rsvd2:1;
- u64 sst_clk:3;
- u64 xt_snoop:1;
- u64 rsvd3:4;
- u64 clk_sel1:6;
- u64 clk_enable:3;
- u64 rsvd4:6;
- u64 slim0baseclk:1;
- u64 rsvd:32;
- } part;
- u64 full;
-};
-
-union interrupt_reg_mrfld {
- struct {
- u64 done_interrupt:1;
- u64 busy_interrupt:1;
- u64 rsvd:62;
- } part;
- u64 full;
-};
-
-union sst_imr_reg_mrfld {
- struct {
- u64 done_interrupt:1;
- u64 busy_interrupt:1;
- u64 rsvd:62;
- } part;
- u64 full;
-};
-
-/**
- * struct sst_block - This structure is used to block a user/fw data call to another
- * fw/user call
- *
- * @condition: condition for blocking check
- * @ret_code: ret code when block is released
- * @data: data ptr
- * @size: size of data
- * @on: block condition
- * @msg_id: msg_id = msgid in mfld/ctp, mrfld = NULL
- * @drv_id: str_id in mfld/ctp, = drv_id in mrfld
- * @node: list head node
- */
-struct sst_block {
- bool condition;
- int ret_code;
- void *data;
- u32 size;
- bool on;
- u32 msg_id;
- u32 drv_id;
- struct list_head node;
-};
-
-/**
- * struct stream_info - structure that holds the stream information
- *
- * @status : stream current state
- * @prev : stream prev state
- * @ops : stream operation pb/cp/drm...
- * @bufs: stream buffer list
- * @lock : stream mutex for protecting state
- * @pcm_substream : PCM substream
- * @period_elapsed : PCM period elapsed callback
- * @sfreq : stream sampling freq
- * @str_type : stream type
- * @cumm_bytes : cummulative bytes decoded
- * @str_type : stream type
- * @src : stream source
- */
-struct stream_info {
- unsigned int status;
- unsigned int prev;
- unsigned int ops;
- struct mutex lock;
-
- void *pcm_substream;
- void (*period_elapsed)(void *pcm_substream);
-
- unsigned int sfreq;
- u32 cumm_bytes;
-
- void *compr_cb_param;
- void (*compr_cb)(void *compr_cb_param);
-
- void *drain_cb_param;
- void (*drain_notify)(void *drain_cb_param);
-
- unsigned int num_ch;
- unsigned int pipe_id;
- unsigned int str_id;
- unsigned int task_id;
-};
-
-#define SST_FW_SIGN "$SST"
-#define SST_FW_LIB_SIGN "$LIB"
-
-/**
- * struct sst_fw_header - FW file headers
- *
- * @signature : FW signature
- * @file_size: size of fw image
- * @modules : # of modules
- * @file_format : version of header format
- * @reserved : reserved fields
- */
-struct sst_fw_header {
- unsigned char signature[FW_SIGNATURE_SIZE];
- u32 file_size;
- u32 modules;
- u32 file_format;
- u32 reserved[4];
-};
-
-/**
- * struct fw_module_header - module header in FW
- *
- * @signature: module signature
- * @mod_size: size of module
- * @blocks: block count
- * @type: block type
- * @entry_point: module netry point
- */
-struct fw_module_header {
- unsigned char signature[FW_SIGNATURE_SIZE];
- u32 mod_size;
- u32 blocks;
- u32 type;
- u32 entry_point;
-};
-
-/**
- * struct fw_block_info - block header for FW
- *
- * @type: block ram type I/D
- * @size: size of block
- * @ram_offset: offset in ram
- */
-struct fw_block_info {
- enum sst_ram_type type;
- u32 size;
- u32 ram_offset;
- u32 rsvd;
-};
-
-struct sst_runtime_param {
- struct snd_sst_runtime_params param;
-};
-
-struct sst_sg_list {
- struct scatterlist *src;
- struct scatterlist *dst;
- int list_len;
- unsigned int sg_idx;
-};
-
-struct sst_memcpy_list {
- struct list_head memcpylist;
- void *dstn;
- const void *src;
- u32 size;
- bool is_io;
-};
-
-/*Firmware Module Information*/
-enum sst_lib_dwnld_status {
- SST_LIB_NOT_FOUND = 0,
- SST_LIB_FOUND,
- SST_LIB_DOWNLOADED,
-};
-
-struct sst_module_info {
- const char *name; /*Library name*/
- u32 id; /*Module ID*/
- u32 entry_pt; /*Module entry point*/
- u8 status; /*module status*/
- u8 rsvd1;
- u16 rsvd2;
-};
-
-/*
- * Structure for managing the Library Region(1.5MB)
- * in DDR in Merrifield
- */
-struct sst_mem_mgr {
- phys_addr_t current_base;
- int avail;
- unsigned int count;
-};
-
-struct sst_ipc_reg {
- int ipcx;
- int ipcd;
-};
-
-struct sst_shim_regs64 {
- u64 csr;
- u64 pisr;
- u64 pimr;
- u64 isrx;
- u64 isrd;
- u64 imrx;
- u64 imrd;
- u64 ipcx;
- u64 ipcd;
- u64 isrsc;
- u64 isrlpesc;
- u64 imrsc;
- u64 imrlpesc;
- u64 ipcsc;
- u64 ipclpesc;
- u64 clkctl;
- u64 csr2;
-};
-
-/**
- * struct intel_sst_drv - driver ops
- *
- * @sst_state : current sst device state
- * @dev_id : device identifier, pci_id for pci devices and acpi_id for acpi
- * devices
- * @shim : SST shim pointer
- * @mailbox : SST mailbox pointer
- * @iram : SST IRAM pointer
- * @dram : SST DRAM pointer
- * @pdata : SST info passed as a part of pci platform data
- * @shim_phy_add : SST shim phy addr
- * @shim_regs64: Struct to save shim registers
- * @ipc_dispatch_list : ipc messages dispatched
- * @rx_list : to copy the process_reply/process_msg from DSP
- * @ipc_post_msg_wq : wq to post IPC messages context
- * @mad_ops : MAD driver operations registered
- * @mad_wq : MAD driver wq
- * @post_msg_wq : wq to post IPC messages
- * @streams : sst stream contexts
- * @list_lock : sst driver list lock (deprecated)
- * @ipc_spin_lock : spin lock to handle audio shim access and ipc queue
- * @block_lock : spin lock to add block to block_list and assign pvt_id
- * @rx_msg_lock : spin lock to handle the rx messages from the DSP
- * @scard_ops : sst card ops
- * @pci : sst pci device struture
- * @dev : pointer to current device struct
- * @sst_lock : sst device lock
- * @pvt_id : sst private id
- * @stream_cnt : total sst active stream count
- * @pb_streams : total active pb streams
- * @cp_streams : total active cp streams
- * @audio_start : audio status
- * @qos : PM Qos struct
- * firmware_name : Firmware / Library name
- */
-struct intel_sst_drv {
- int sst_state;
- int irq_num;
- unsigned int dev_id;
- void __iomem *ddr;
- void __iomem *shim;
- void __iomem *mailbox;
- void __iomem *iram;
- void __iomem *dram;
- unsigned int mailbox_add;
- unsigned int iram_base;
- unsigned int dram_base;
- unsigned int shim_phy_add;
- unsigned int iram_end;
- unsigned int dram_end;
- unsigned int ddr_end;
- unsigned int ddr_base;
- unsigned int mailbox_recv_offset;
- struct sst_shim_regs64 *shim_regs64;
- struct list_head block_list;
- struct list_head ipc_dispatch_list;
- struct sst_platform_info *pdata;
- struct list_head rx_list;
- struct work_struct ipc_post_msg_wq;
- wait_queue_head_t wait_queue;
- struct workqueue_struct *post_msg_wq;
- unsigned int tstamp;
- /* str_id 0 is not used */
- struct stream_info streams[MAX_NUM_STREAMS+1];
- spinlock_t ipc_spin_lock;
- spinlock_t block_lock;
- spinlock_t rx_msg_lock;
- struct pci_dev *pci;
- struct device *dev;
- volatile long unsigned pvt_id;
- struct mutex sst_lock;
- unsigned int stream_cnt;
- unsigned int csr_value;
- void *fw_in_mem;
- struct sst_sg_list fw_sg_list, library_list;
- struct intel_sst_ops *ops;
- struct sst_info info;
- struct pm_qos_request *qos;
- unsigned int use_dma;
- unsigned int use_lli;
- atomic_t fw_clear_context;
- bool lib_dwnld_reqd;
- struct list_head memcpy_list;
- struct sst_ipc_reg ipc_reg;
- struct sst_mem_mgr lib_mem_mgr;
- /*
- * Holder for firmware name. Due to async call it needs to be
- * persistent till worker thread gets called
- */
- char firmware_name[FW_NAME_SIZE];
-};
-
-/* misc definitions */
-#define FW_DWNL_ID 0x01
-
-struct intel_sst_ops {
- irqreturn_t (*interrupt)(int, void *);
- irqreturn_t (*irq_thread)(int, void *);
- void (*clear_interrupt)(struct intel_sst_drv *ctx);
- int (*start)(struct intel_sst_drv *ctx);
- int (*reset)(struct intel_sst_drv *ctx);
- void (*process_reply)(struct intel_sst_drv *ctx, struct ipc_post *msg);
- int (*post_message)(struct intel_sst_drv *ctx,
- struct ipc_post *msg, bool sync);
- void (*process_message)(struct ipc_post *msg);
- void (*set_bypass)(bool set);
- int (*save_dsp_context)(struct intel_sst_drv *sst);
- void (*restore_dsp_context)(void);
- int (*alloc_stream)(struct intel_sst_drv *ctx, void *params);
- void (*post_download)(struct intel_sst_drv *sst);
-};
-
-int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int id);
-int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int id);
-int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int id);
-int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int id);
-int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id);
-int sst_send_byte_stream_mrfld(struct intel_sst_drv *ctx,
- struct snd_sst_bytes_v2 *sbytes);
-int sst_set_stream_param(int str_id, struct snd_sst_params *str_param);
-int sst_set_metadata(int str_id, char *params);
-int sst_get_stream(struct intel_sst_drv *sst_drv_ctx,
- struct snd_sst_params *str_param);
-int sst_get_stream_allocated(struct intel_sst_drv *ctx,
- struct snd_sst_params *str_param,
- struct snd_sst_lib_download **lib_dnld);
-int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx,
- int str_id, bool partial_drain);
-int sst_post_message_mrfld(struct intel_sst_drv *ctx,
- struct ipc_post *msg, bool sync);
-void sst_process_reply_mrfld(struct intel_sst_drv *ctx, struct ipc_post *msg);
-int sst_start_mrfld(struct intel_sst_drv *ctx);
-int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *ctx);
-void intel_sst_clear_intr_mrfld(struct intel_sst_drv *ctx);
-
-int sst_load_fw(struct intel_sst_drv *ctx);
-int sst_load_library(struct snd_sst_lib_download *lib, u8 ops);
-void sst_post_download_mrfld(struct intel_sst_drv *ctx);
-int sst_get_block_stream(struct intel_sst_drv *sst_drv_ctx);
-void sst_memcpy_free_resources(struct intel_sst_drv *ctx);
-
-int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx,
- struct sst_block *block);
-int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx,
- struct sst_block *block);
-int sst_create_ipc_msg(struct ipc_post **arg, bool large);
-int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id);
-void sst_clean_stream(struct stream_info *stream);
-int intel_sst_register_compress(struct intel_sst_drv *sst);
-int intel_sst_remove_compress(struct intel_sst_drv *sst);
-void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id);
-int sst_send_sync_msg(int ipc, int str_id);
-int sst_get_num_channel(struct snd_sst_params *str_param);
-int sst_get_sfreq(struct snd_sst_params *str_param);
-int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params);
-void sst_restore_fw_context(void);
-struct sst_block *sst_create_block(struct intel_sst_drv *ctx,
- u32 msg_id, u32 drv_id);
-int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large,
- struct intel_sst_drv *sst_drv_ctx, struct sst_block **block,
- u32 msg_id, u32 drv_id);
-int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed);
-int sst_wake_up_block(struct intel_sst_drv *ctx, int result,
- u32 drv_id, u32 ipc, void *data, u32 size);
-int sst_request_firmware_async(struct intel_sst_drv *ctx);
-int sst_driver_ops(struct intel_sst_drv *sst);
-struct sst_platform_info *sst_get_acpi_driver_data(const char *hid);
-void sst_firmware_load_cb(const struct firmware *fw, void *context);
-int sst_prepare_and_post_msg(struct intel_sst_drv *sst,
- int task_id, int ipc_msg, int cmd_id, int pipe_id,
- size_t mbox_data_len, const void *mbox_data, void **data,
- bool large, bool fill_dsp, bool sync, bool response);
-
-void sst_process_pending_msg(struct work_struct *work);
-int sst_assign_pvt_id(struct intel_sst_drv *sst_drv_ctx);
-void sst_init_stream(struct stream_info *stream,
- int codec, int sst_id, int ops, u8 slot);
-int sst_validate_strid(struct intel_sst_drv *sst_drv_ctx, int str_id);
-struct stream_info *get_stream_info(struct intel_sst_drv *sst_drv_ctx,
- int str_id);
-int get_stream_id_mrfld(struct intel_sst_drv *sst_drv_ctx,
- u32 pipe_id);
-u32 relocate_imr_addr_mrfld(u32 base_addr);
-void sst_add_to_dispatch_list_and_post(struct intel_sst_drv *sst,
- struct ipc_post *msg);
-int sst_pm_runtime_put(struct intel_sst_drv *sst_drv);
-int sst_shim_write(void __iomem *addr, int offset, int value);
-u32 sst_shim_read(void __iomem *addr, int offset);
-u64 sst_reg_read64(void __iomem *addr, int offset);
-int sst_shim_write64(void __iomem *addr, int offset, u64 value);
-u64 sst_shim_read64(void __iomem *addr, int offset);
-void sst_set_fw_state_locked(
- struct intel_sst_drv *sst_drv_ctx, int sst_state);
-void sst_fill_header_mrfld(union ipc_header_mrfld *header,
- int msg, int task_id, int large, int drv_id);
-void sst_fill_header_dsp(struct ipc_dsp_hdr *dsp, int msg,
- int pipe_id, int len);
-
-int sst_register(struct device *);
-int sst_unregister(struct device *);
-
-int sst_alloc_drv_context(struct intel_sst_drv **ctx,
- struct device *dev, unsigned int dev_id);
-int sst_context_init(struct intel_sst_drv *ctx);
-void sst_context_cleanup(struct intel_sst_drv *ctx);
-void sst_configure_runtime_pm(struct intel_sst_drv *ctx);
-#endif
+++ /dev/null
-/*
- * sst_acpi.c - SST (LPE) driver init file for ACPI enumeration.
- *
- * Copyright (c) 2013, Intel Corporation.
- *
- * Authors: Ramesh Babu K V <Ramesh.Babu@intel.com>
- * Authors: Omair Mohammed Abdullah <omair.m.abdullah@intel.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- *
- */
-
-#include <linux/module.h>
-#include <linux/fs.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <linux/miscdevice.h>
-#include <linux/platform_device.h>
-#include <linux/firmware.h>
-#include <linux/pm_runtime.h>
-#include <linux/pm_qos.h>
-#include <linux/acpi.h>
-#include <asm/platform_sst_audio.h>
-#include <sound/core.h>
-#include <sound/soc.h>
-#include <sound/compress_driver.h>
-#include <acpi/acbuffer.h>
-#include <acpi/platform/acenv.h>
-#include <acpi/platform/aclinux.h>
-#include <acpi/actypes.h>
-#include <acpi/acpi_bus.h>
-#include "../sst-mfld-platform.h"
-#include "../sst-dsp.h"
-#include "sst.h"
-
-struct sst_machines {
- char *codec_id;
- char board[32];
- char machine[32];
- void (*machine_quirk)(void);
- char firmware[FW_NAME_SIZE];
- struct sst_platform_info *pdata;
-
-};
-
-/* LPE viewpoint addresses */
-#define SST_BYT_IRAM_PHY_START 0xff2c0000
-#define SST_BYT_IRAM_PHY_END 0xff2d4000
-#define SST_BYT_DRAM_PHY_START 0xff300000
-#define SST_BYT_DRAM_PHY_END 0xff320000
-#define SST_BYT_IMR_VIRT_START 0xc0000000 /* virtual addr in LPE */
-#define SST_BYT_IMR_VIRT_END 0xc01fffff
-#define SST_BYT_SHIM_PHY_ADDR 0xff340000
-#define SST_BYT_MBOX_PHY_ADDR 0xff344000
-#define SST_BYT_DMA0_PHY_ADDR 0xff298000
-#define SST_BYT_DMA1_PHY_ADDR 0xff29c000
-#define SST_BYT_SSP0_PHY_ADDR 0xff2a0000
-#define SST_BYT_SSP2_PHY_ADDR 0xff2a2000
-
-#define BYT_FW_MOD_TABLE_OFFSET 0x80000
-#define BYT_FW_MOD_TABLE_SIZE 0x100
-#define BYT_FW_MOD_OFFSET (BYT_FW_MOD_TABLE_OFFSET + BYT_FW_MOD_TABLE_SIZE)
-
-static const struct sst_info byt_fwparse_info = {
- .use_elf = false,
- .max_streams = 25,
- .iram_start = SST_BYT_IRAM_PHY_START,
- .iram_end = SST_BYT_IRAM_PHY_END,
- .iram_use = true,
- .dram_start = SST_BYT_DRAM_PHY_START,
- .dram_end = SST_BYT_DRAM_PHY_END,
- .dram_use = true,
- .imr_start = SST_BYT_IMR_VIRT_START,
- .imr_end = SST_BYT_IMR_VIRT_END,
- .imr_use = true,
- .mailbox_start = SST_BYT_MBOX_PHY_ADDR,
- .num_probes = 0,
- .lpe_viewpt_rqd = true,
-};
-
-static const struct sst_ipc_info byt_ipc_info = {
- .ipc_offset = 0,
- .mbox_recv_off = 0x400,
-};
-
-static const struct sst_lib_dnld_info byt_lib_dnld_info = {
- .mod_base = SST_BYT_IMR_VIRT_START,
- .mod_end = SST_BYT_IMR_VIRT_END,
- .mod_table_offset = BYT_FW_MOD_TABLE_OFFSET,
- .mod_table_size = BYT_FW_MOD_TABLE_SIZE,
- .mod_ddr_dnld = false,
-};
-
-static const struct sst_res_info byt_rvp_res_info = {
- .shim_offset = 0x140000,
- .shim_size = 0x000100,
- .shim_phy_addr = SST_BYT_SHIM_PHY_ADDR,
- .ssp0_offset = 0xa0000,
- .ssp0_size = 0x1000,
- .dma0_offset = 0x98000,
- .dma0_size = 0x4000,
- .dma1_offset = 0x9c000,
- .dma1_size = 0x4000,
- .iram_offset = 0x0c0000,
- .iram_size = 0x14000,
- .dram_offset = 0x100000,
- .dram_size = 0x28000,
- .mbox_offset = 0x144000,
- .mbox_size = 0x1000,
- .acpi_lpe_res_index = 0,
- .acpi_ddr_index = 2,
- .acpi_ipc_irq_index = 5,
-};
-
-static struct sst_platform_info byt_rvp_platform_data = {
- .probe_data = &byt_fwparse_info,
- .ipc_info = &byt_ipc_info,
- .lib_info = &byt_lib_dnld_info,
- .res_info = &byt_rvp_res_info,
- .platform = "sst-mfld-platform",
-};
-
-/* Cherryview (Cherrytrail and Braswell) uses same mrfld dpcm fw as Baytrail,
- * so pdata is same as Baytrail.
- */
-static struct sst_platform_info chv_platform_data = {
- .probe_data = &byt_fwparse_info,
- .ipc_info = &byt_ipc_info,
- .lib_info = &byt_lib_dnld_info,
- .res_info = &byt_rvp_res_info,
- .platform = "sst-mfld-platform",
-};
-
-static int sst_platform_get_resources(struct intel_sst_drv *ctx)
-{
- struct resource *rsrc;
- struct platform_device *pdev = to_platform_device(ctx->dev);
-
- /* All ACPI resource request here */
- /* Get Shim addr */
- rsrc = platform_get_resource(pdev, IORESOURCE_MEM,
- ctx->pdata->res_info->acpi_lpe_res_index);
- if (!rsrc) {
- dev_err(ctx->dev, "Invalid SHIM base from IFWI");
- return -EIO;
- }
- dev_info(ctx->dev, "LPE base: %#x size:%#x", (unsigned int) rsrc->start,
- (unsigned int)resource_size(rsrc));
-
- ctx->iram_base = rsrc->start + ctx->pdata->res_info->iram_offset;
- ctx->iram_end = ctx->iram_base + ctx->pdata->res_info->iram_size - 1;
- dev_info(ctx->dev, "IRAM base: %#x", ctx->iram_base);
- ctx->iram = devm_ioremap_nocache(ctx->dev, ctx->iram_base,
- ctx->pdata->res_info->iram_size);
- if (!ctx->iram) {
- dev_err(ctx->dev, "unable to map IRAM");
- return -EIO;
- }
-
- ctx->dram_base = rsrc->start + ctx->pdata->res_info->dram_offset;
- ctx->dram_end = ctx->dram_base + ctx->pdata->res_info->dram_size - 1;
- dev_info(ctx->dev, "DRAM base: %#x", ctx->dram_base);
- ctx->dram = devm_ioremap_nocache(ctx->dev, ctx->dram_base,
- ctx->pdata->res_info->dram_size);
- if (!ctx->dram) {
- dev_err(ctx->dev, "unable to map DRAM");
- return -EIO;
- }
-
- ctx->shim_phy_add = rsrc->start + ctx->pdata->res_info->shim_offset;
- dev_info(ctx->dev, "SHIM base: %#x", ctx->shim_phy_add);
- ctx->shim = devm_ioremap_nocache(ctx->dev, ctx->shim_phy_add,
- ctx->pdata->res_info->shim_size);
- if (!ctx->shim) {
- dev_err(ctx->dev, "unable to map SHIM");
- return -EIO;
- }
-
- /* reassign physical address to LPE viewpoint address */
- ctx->shim_phy_add = ctx->pdata->res_info->shim_phy_addr;
-
- /* Get mailbox addr */
- ctx->mailbox_add = rsrc->start + ctx->pdata->res_info->mbox_offset;
- dev_info(ctx->dev, "Mailbox base: %#x", ctx->mailbox_add);
- ctx->mailbox = devm_ioremap_nocache(ctx->dev, ctx->mailbox_add,
- ctx->pdata->res_info->mbox_size);
- if (!ctx->mailbox) {
- dev_err(ctx->dev, "unable to map mailbox");
- return -EIO;
- }
-
- /* reassign physical address to LPE viewpoint address */
- ctx->mailbox_add = ctx->info.mailbox_start;
-
- rsrc = platform_get_resource(pdev, IORESOURCE_MEM,
- ctx->pdata->res_info->acpi_ddr_index);
- if (!rsrc) {
- dev_err(ctx->dev, "Invalid DDR base from IFWI");
- return -EIO;
- }
- ctx->ddr_base = rsrc->start;
- ctx->ddr_end = rsrc->end;
- dev_info(ctx->dev, "DDR base: %#x", ctx->ddr_base);
- ctx->ddr = devm_ioremap_nocache(ctx->dev, ctx->ddr_base,
- resource_size(rsrc));
- if (!ctx->ddr) {
- dev_err(ctx->dev, "unable to map DDR");
- return -EIO;
- }
-
- /* Find the IRQ */
- ctx->irq_num = platform_get_irq(pdev,
- ctx->pdata->res_info->acpi_ipc_irq_index);
- return 0;
-}
-
-static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level,
- void *context, void **ret)
-{
- *(bool *)context = true;
- return AE_OK;
-}
-
-static struct sst_machines *sst_acpi_find_machine(
- struct sst_machines *machines)
-{
- struct sst_machines *mach;
- bool found = false;
-
- for (mach = machines; mach->codec_id; mach++)
- if (ACPI_SUCCESS(acpi_get_devices(mach->codec_id,
- sst_acpi_mach_match,
- &found, NULL)) && found)
- return mach;
-
- return NULL;
-}
-
-static int sst_acpi_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- int ret = 0;
- struct intel_sst_drv *ctx;
- const struct acpi_device_id *id;
- struct sst_machines *mach;
- struct platform_device *mdev;
- struct platform_device *plat_dev;
- unsigned int dev_id;
-
- id = acpi_match_device(dev->driver->acpi_match_table, dev);
- if (!id)
- return -ENODEV;
- dev_dbg(dev, "for %s", id->id);
-
- mach = (struct sst_machines *)id->driver_data;
- mach = sst_acpi_find_machine(mach);
- if (mach == NULL) {
- dev_err(dev, "No matching machine driver found\n");
- return -ENODEV;
- }
-
- ret = kstrtouint(id->id, 16, &dev_id);
- if (ret < 0) {
- dev_err(dev, "Unique device id conversion error: %d\n", ret);
- return ret;
- }
-
- dev_dbg(dev, "ACPI device id: %x\n", dev_id);
-
- plat_dev = platform_device_register_data(dev, mach->pdata->platform, -1, NULL, 0);
- if (IS_ERR(plat_dev)) {
- dev_err(dev, "Failed to create machine device: %s\n", mach->pdata->platform);
- return PTR_ERR(plat_dev);
- }
-
- /* Create platform device for sst machine driver */
- mdev = platform_device_register_data(dev, mach->machine, -1, NULL, 0);
- if (IS_ERR(mdev)) {
- dev_err(dev, "Failed to create machine device: %s\n", mach->machine);
- return PTR_ERR(mdev);
- }
-
- ret = sst_alloc_drv_context(&ctx, dev, dev_id);
- if (ret < 0)
- return ret;
-
- /* Fill sst platform data */
- ctx->pdata = mach->pdata;
- strcpy(ctx->firmware_name, mach->firmware);
-
- ret = sst_platform_get_resources(ctx);
- if (ret)
- return ret;
-
- ret = sst_context_init(ctx);
- if (ret < 0)
- return ret;
-
- /* need to save shim registers in BYT */
- ctx->shim_regs64 = devm_kzalloc(ctx->dev, sizeof(*ctx->shim_regs64),
- GFP_KERNEL);
- if (!ctx->shim_regs64) {
- return -ENOMEM;
- goto do_sst_cleanup;
- }
-
- sst_configure_runtime_pm(ctx);
- platform_set_drvdata(pdev, ctx);
- return ret;
-
-do_sst_cleanup:
- sst_context_cleanup(ctx);
- platform_set_drvdata(pdev, NULL);
- dev_err(ctx->dev, "failed with %d\n", ret);
- return ret;
-}
-
-/**
-* intel_sst_remove - remove function
-*
-* @pdev: platform device structure
-*
-* This function is called by OS when a device is unloaded
-* This frees the interrupt etc
-*/
-static int sst_acpi_remove(struct platform_device *pdev)
-{
- struct intel_sst_drv *ctx;
-
- ctx = platform_get_drvdata(pdev);
- sst_context_cleanup(ctx);
- platform_set_drvdata(pdev, NULL);
- return 0;
-}
-
-static struct sst_machines sst_acpi_bytcr[] = {
- {"10EC5640", "T100", "bytt100_rt5640", NULL, "intel/fw_sst_0f28.bin",
- &byt_rvp_platform_data },
- {},
-};
-
-/* Cherryview-based platforms: CherryTrail and Braswell */
-static struct sst_machines sst_acpi_chv[] = {
- {"10EC5670", "cht-bsw", "cht-bsw-rt5672", NULL, "intel/fw_sst_22a8.bin",
- &chv_platform_data },
- {"10EC5645", "cht-bsw", "cht-bsw-rt5645", NULL, "intel/fw_sst_22a8.bin",
- &chv_platform_data },
- {},
-};
-
-static const struct acpi_device_id sst_acpi_ids[] = {
- { "80860F28", (unsigned long)&sst_acpi_bytcr},
- { "808622A8", (unsigned long) &sst_acpi_chv},
- { },
-};
-
-MODULE_DEVICE_TABLE(acpi, sst_acpi_ids);
-
-static struct platform_driver sst_acpi_driver = {
- .driver = {
- .name = "intel_sst_acpi",
- .acpi_match_table = ACPI_PTR(sst_acpi_ids),
- .pm = &intel_sst_pm,
- },
- .probe = sst_acpi_probe,
- .remove = sst_acpi_remove,
-};
-
-module_platform_driver(sst_acpi_driver);
-
-MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine ACPI Driver");
-MODULE_AUTHOR("Ramesh Babu K V");
-MODULE_AUTHOR("Omair Mohammed Abdullah");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("sst");
+++ /dev/null
-/*
- * sst_drv_interface.c - Intel SST Driver for audio engine
- *
- * Copyright (C) 2008-14 Intel Corp
- * Authors: Vinod Koul <vinod.koul@intel.com>
- * Harsha Priya <priya.harsha@intel.com>
- * Dharageswari R <dharageswari.r@intel.com)
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-#include <linux/delay.h>
-#include <linux/pci.h>
-#include <linux/fs.h>
-#include <linux/firmware.h>
-#include <linux/pm_runtime.h>
-#include <linux/pm_qos.h>
-#include <linux/math64.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/compress_driver.h>
-#include <asm/platform_sst_audio.h>
-#include "../sst-mfld-platform.h"
-#include "sst.h"
-#include "../sst-dsp.h"
-
-
-
-#define NUM_CODEC 2
-#define MIN_FRAGMENT 2
-#define MAX_FRAGMENT 4
-#define MIN_FRAGMENT_SIZE (50 * 1024)
-#define MAX_FRAGMENT_SIZE (1024 * 1024)
-#define SST_GET_BYTES_PER_SAMPLE(pcm_wd_sz) (((pcm_wd_sz + 15) >> 4) << 1)
-
-int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id)
-{
- struct stream_info *stream;
- int ret = 0;
-
- stream = get_stream_info(ctx, str_id);
- if (stream) {
- /* str_id is valid, so stream is alloacted */
- ret = sst_free_stream(ctx, str_id);
- if (ret)
- sst_clean_stream(&ctx->streams[str_id]);
- return ret;
- } else {
- dev_err(ctx->dev, "we tried to free stream context %d which was freed!!!\n", str_id);
- }
- return ret;
-}
-
-int sst_get_stream_allocated(struct intel_sst_drv *ctx,
- struct snd_sst_params *str_param,
- struct snd_sst_lib_download **lib_dnld)
-{
- int retval;
-
- retval = ctx->ops->alloc_stream(ctx, str_param);
- if (retval > 0)
- dev_dbg(ctx->dev, "Stream allocated %d\n", retval);
- return retval;
-
-}
-
-/*
- * sst_get_sfreq - this function returns the frequency of the stream
- *
- * @str_param : stream params
- */
-int sst_get_sfreq(struct snd_sst_params *str_param)
-{
- switch (str_param->codec) {
- case SST_CODEC_TYPE_PCM:
- return str_param->sparams.uc.pcm_params.sfreq;
- case SST_CODEC_TYPE_AAC:
- return str_param->sparams.uc.aac_params.externalsr;
- case SST_CODEC_TYPE_MP3:
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-/*
- * sst_get_num_channel - get number of channels for the stream
- *
- * @str_param : stream params
- */
-int sst_get_num_channel(struct snd_sst_params *str_param)
-{
- switch (str_param->codec) {
- case SST_CODEC_TYPE_PCM:
- return str_param->sparams.uc.pcm_params.num_chan;
- case SST_CODEC_TYPE_MP3:
- return str_param->sparams.uc.mp3_params.num_chan;
- case SST_CODEC_TYPE_AAC:
- return str_param->sparams.uc.aac_params.num_chan;
- default:
- return -EINVAL;
- }
-}
-
-/*
- * sst_get_stream - this function prepares for stream allocation
- *
- * @str_param : stream param
- */
-int sst_get_stream(struct intel_sst_drv *ctx,
- struct snd_sst_params *str_param)
-{
- int retval;
- struct stream_info *str_info;
-
- /* stream is not allocated, we are allocating */
- retval = ctx->ops->alloc_stream(ctx, str_param);
- if (retval <= 0) {
- return -EIO;
- }
- /* store sampling freq */
- str_info = &ctx->streams[retval];
- str_info->sfreq = sst_get_sfreq(str_param);
-
- return retval;
-}
-
-static int sst_power_control(struct device *dev, bool state)
-{
- struct intel_sst_drv *ctx = dev_get_drvdata(dev);
-
- dev_dbg(ctx->dev, "state:%d", state);
- if (state == true)
- return pm_runtime_get_sync(dev);
- else
- return sst_pm_runtime_put(ctx);
-}
-
-/*
- * sst_open_pcm_stream - Open PCM interface
- *
- * @str_param: parameters of pcm stream
- *
- * This function is called by MID sound card driver to open
- * a new pcm interface
- */
-static int sst_open_pcm_stream(struct device *dev,
- struct snd_sst_params *str_param)
-{
- int retval;
- struct intel_sst_drv *ctx = dev_get_drvdata(dev);
-
- if (!str_param)
- return -EINVAL;
-
- retval = sst_get_stream(ctx, str_param);
- if (retval > 0)
- ctx->stream_cnt++;
- else
- dev_err(ctx->dev, "sst_get_stream returned err %d\n", retval);
-
- return retval;
-}
-
-static int sst_cdev_open(struct device *dev,
- struct snd_sst_params *str_params, struct sst_compress_cb *cb)
-{
- int str_id, retval;
- struct stream_info *stream;
- struct intel_sst_drv *ctx = dev_get_drvdata(dev);
-
- retval = pm_runtime_get_sync(ctx->dev);
- if (retval < 0)
- return retval;
-
- str_id = sst_get_stream(ctx, str_params);
- if (str_id > 0) {
- dev_dbg(dev, "stream allocated in sst_cdev_open %d\n", str_id);
- stream = &ctx->streams[str_id];
- stream->compr_cb = cb->compr_cb;
- stream->compr_cb_param = cb->param;
- stream->drain_notify = cb->drain_notify;
- stream->drain_cb_param = cb->drain_cb_param;
- } else {
- dev_err(dev, "stream encountered error during alloc %d\n", str_id);
- str_id = -EINVAL;
- sst_pm_runtime_put(ctx);
- }
- return str_id;
-}
-
-static int sst_cdev_close(struct device *dev, unsigned int str_id)
-{
- int retval;
- struct stream_info *stream;
- struct intel_sst_drv *ctx = dev_get_drvdata(dev);
-
- stream = get_stream_info(ctx, str_id);
- if (!stream) {
- dev_err(dev, "stream info is NULL for str %d!!!\n", str_id);
- return -EINVAL;
- }
-
- if (stream->status == STREAM_RESET) {
- dev_dbg(dev, "stream in reset state...\n");
- stream->status = STREAM_UN_INIT;
-
- retval = 0;
- goto put;
- }
-
- retval = sst_free_stream(ctx, str_id);
-put:
- stream->compr_cb_param = NULL;
- stream->compr_cb = NULL;
-
- if (retval)
- dev_err(dev, "free stream returned err %d\n", retval);
-
- dev_dbg(dev, "End\n");
- return retval;
-
-}
-
-static int sst_cdev_ack(struct device *dev, unsigned int str_id,
- unsigned long bytes)
-{
- struct stream_info *stream;
- struct snd_sst_tstamp fw_tstamp = {0,};
- int offset;
- void __iomem *addr;
- struct intel_sst_drv *ctx = dev_get_drvdata(dev);
-
- stream = get_stream_info(ctx, str_id);
- if (!stream)
- return -EINVAL;
-
- /* update bytes sent */
- stream->cumm_bytes += bytes;
- dev_dbg(dev, "bytes copied %d inc by %ld\n", stream->cumm_bytes, bytes);
-
- memcpy_fromio(&fw_tstamp,
- ((void *)(ctx->mailbox + ctx->tstamp)
- +(str_id * sizeof(fw_tstamp))),
- sizeof(fw_tstamp));
-
- fw_tstamp.bytes_copied = stream->cumm_bytes;
- dev_dbg(dev, "bytes sent to fw %llu inc by %ld\n",
- fw_tstamp.bytes_copied, bytes);
-
- addr = ((void *)(ctx->mailbox + ctx->tstamp)) +
- (str_id * sizeof(fw_tstamp));
- offset = offsetof(struct snd_sst_tstamp, bytes_copied);
- sst_shim_write(addr, offset, fw_tstamp.bytes_copied);
- return 0;
-}
-
-static int sst_cdev_set_metadata(struct device *dev,
- unsigned int str_id, struct snd_compr_metadata *metadata)
-{
- int retval = 0;
- struct stream_info *str_info;
- struct intel_sst_drv *ctx = dev_get_drvdata(dev);
-
- dev_dbg(dev, "set metadata for stream %d\n", str_id);
-
- str_info = get_stream_info(ctx, str_id);
- if (!str_info)
- return -EINVAL;
-
- dev_dbg(dev, "pipe id = %d\n", str_info->pipe_id);
- retval = sst_prepare_and_post_msg(ctx, str_info->task_id, IPC_CMD,
- IPC_IA_SET_STREAM_PARAMS_MRFLD, str_info->pipe_id,
- sizeof(*metadata), metadata, NULL,
- true, true, true, false);
-
- return retval;
-}
-
-static int sst_cdev_stream_pause(struct device *dev, unsigned int str_id)
-{
- struct intel_sst_drv *ctx = dev_get_drvdata(dev);
-
- return sst_pause_stream(ctx, str_id);
-}
-
-static int sst_cdev_stream_pause_release(struct device *dev,
- unsigned int str_id)
-{
- struct intel_sst_drv *ctx = dev_get_drvdata(dev);
-
- return sst_resume_stream(ctx, str_id);
-}
-
-static int sst_cdev_stream_start(struct device *dev, unsigned int str_id)
-{
- struct stream_info *str_info;
- struct intel_sst_drv *ctx = dev_get_drvdata(dev);
-
- str_info = get_stream_info(ctx, str_id);
- if (!str_info)
- return -EINVAL;
- str_info->prev = str_info->status;
- str_info->status = STREAM_RUNNING;
- return sst_start_stream(ctx, str_id);
-}
-
-static int sst_cdev_stream_drop(struct device *dev, unsigned int str_id)
-{
- struct intel_sst_drv *ctx = dev_get_drvdata(dev);
-
- return sst_drop_stream(ctx, str_id);
-}
-
-static int sst_cdev_stream_drain(struct device *dev, unsigned int str_id)
-{
- struct intel_sst_drv *ctx = dev_get_drvdata(dev);
-
- return sst_drain_stream(ctx, str_id, false);
-}
-
-static int sst_cdev_stream_partial_drain(struct device *dev,
- unsigned int str_id)
-{
- struct intel_sst_drv *ctx = dev_get_drvdata(dev);
-
- return sst_drain_stream(ctx, str_id, true);
-}
-
-static int sst_cdev_tstamp(struct device *dev, unsigned int str_id,
- struct snd_compr_tstamp *tstamp)
-{
- struct snd_sst_tstamp fw_tstamp = {0,};
- struct stream_info *stream;
- struct intel_sst_drv *ctx = dev_get_drvdata(dev);
-
- memcpy_fromio(&fw_tstamp,
- ((void *)(ctx->mailbox + ctx->tstamp)
- +(str_id * sizeof(fw_tstamp))),
- sizeof(fw_tstamp));
-
- stream = get_stream_info(ctx, str_id);
- if (!stream)
- return -EINVAL;
- dev_dbg(dev, "rb_counter %llu in bytes\n", fw_tstamp.ring_buffer_counter);
-
- tstamp->copied_total = fw_tstamp.ring_buffer_counter;
- tstamp->pcm_frames = fw_tstamp.frames_decoded;
- tstamp->pcm_io_frames = div_u64(fw_tstamp.hardware_counter,
- (u64)((stream->num_ch) * SST_GET_BYTES_PER_SAMPLE(24)));
- tstamp->sampling_rate = fw_tstamp.sampling_frequency;
-
- dev_dbg(dev, "PCM = %u\n", tstamp->pcm_io_frames);
- dev_dbg(dev, "Ptr Query on strid = %d copied_total %d, decodec %d\n",
- str_id, tstamp->copied_total, tstamp->pcm_frames);
- dev_dbg(dev, "rendered %d\n", tstamp->pcm_io_frames);
-
- return 0;
-}
-
-static int sst_cdev_caps(struct snd_compr_caps *caps)
-{
- caps->num_codecs = NUM_CODEC;
- caps->min_fragment_size = MIN_FRAGMENT_SIZE; /* 50KB */
- caps->max_fragment_size = MAX_FRAGMENT_SIZE; /* 1024KB */
- caps->min_fragments = MIN_FRAGMENT;
- caps->max_fragments = MAX_FRAGMENT;
- caps->codecs[0] = SND_AUDIOCODEC_MP3;
- caps->codecs[1] = SND_AUDIOCODEC_AAC;
- return 0;
-}
-
-static struct snd_compr_codec_caps caps_mp3 = {
- .num_descriptors = 1,
- .descriptor[0].max_ch = 2,
- .descriptor[0].sample_rates[0] = 48000,
- .descriptor[0].sample_rates[1] = 44100,
- .descriptor[0].sample_rates[2] = 32000,
- .descriptor[0].sample_rates[3] = 16000,
- .descriptor[0].sample_rates[4] = 8000,
- .descriptor[0].num_sample_rates = 5,
- .descriptor[0].bit_rate[0] = 320,
- .descriptor[0].bit_rate[1] = 192,
- .descriptor[0].num_bitrates = 2,
- .descriptor[0].profiles = 0,
- .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO,
- .descriptor[0].formats = 0,
-};
-
-static struct snd_compr_codec_caps caps_aac = {
- .num_descriptors = 2,
- .descriptor[1].max_ch = 2,
- .descriptor[0].sample_rates[0] = 48000,
- .descriptor[0].sample_rates[1] = 44100,
- .descriptor[0].sample_rates[2] = 32000,
- .descriptor[0].sample_rates[3] = 16000,
- .descriptor[0].sample_rates[4] = 8000,
- .descriptor[0].num_sample_rates = 5,
- .descriptor[1].bit_rate[0] = 320,
- .descriptor[1].bit_rate[1] = 192,
- .descriptor[1].num_bitrates = 2,
- .descriptor[1].profiles = 0,
- .descriptor[1].modes = 0,
- .descriptor[1].formats =
- (SND_AUDIOSTREAMFORMAT_MP4ADTS |
- SND_AUDIOSTREAMFORMAT_RAW),
-};
-
-static int sst_cdev_codec_caps(struct snd_compr_codec_caps *codec)
-{
- if (codec->codec == SND_AUDIOCODEC_MP3)
- *codec = caps_mp3;
- else if (codec->codec == SND_AUDIOCODEC_AAC)
- *codec = caps_aac;
- else
- return -EINVAL;
-
- return 0;
-}
-
-void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id)
-{
- struct stream_info *stream;
-
- dev_dbg(ctx->dev, "fragment elapsed from firmware for str_id %d\n",
- str_id);
- stream = &ctx->streams[str_id];
- if (stream->compr_cb)
- stream->compr_cb(stream->compr_cb_param);
-}
-
-/*
- * sst_close_pcm_stream - Close PCM interface
- *
- * @str_id: stream id to be closed
- *
- * This function is called by MID sound card driver to close
- * an existing pcm interface
- */
-static int sst_close_pcm_stream(struct device *dev, unsigned int str_id)
-{
- struct stream_info *stream;
- int retval = 0;
- struct intel_sst_drv *ctx = dev_get_drvdata(dev);
-
- stream = get_stream_info(ctx, str_id);
- if (!stream) {
- dev_err(ctx->dev, "stream info is NULL for str %d!!!\n", str_id);
- return -EINVAL;
- }
-
- if (stream->status == STREAM_RESET) {
- /* silently fail here as we have cleaned the stream earlier */
- dev_dbg(ctx->dev, "stream in reset state...\n");
-
- retval = 0;
- goto put;
- }
-
- retval = free_stream_context(ctx, str_id);
-put:
- stream->pcm_substream = NULL;
- stream->status = STREAM_UN_INIT;
- stream->period_elapsed = NULL;
- ctx->stream_cnt--;
-
- if (retval)
- dev_err(ctx->dev, "free stream returned err %d\n", retval);
-
- dev_dbg(ctx->dev, "Exit\n");
- return 0;
-}
-
-static inline int sst_calc_tstamp(struct intel_sst_drv *ctx,
- struct pcm_stream_info *info,
- struct snd_pcm_substream *substream,
- struct snd_sst_tstamp *fw_tstamp)
-{
- size_t delay_bytes, delay_frames;
- size_t buffer_sz;
- u32 pointer_bytes, pointer_samples;
-
- dev_dbg(ctx->dev, "mrfld ring_buffer_counter %llu in bytes\n",
- fw_tstamp->ring_buffer_counter);
- dev_dbg(ctx->dev, "mrfld hardware_counter %llu in bytes\n",
- fw_tstamp->hardware_counter);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- delay_bytes = (size_t) (fw_tstamp->ring_buffer_counter -
- fw_tstamp->hardware_counter);
- else
- delay_bytes = (size_t) (fw_tstamp->hardware_counter -
- fw_tstamp->ring_buffer_counter);
- delay_frames = bytes_to_frames(substream->runtime, delay_bytes);
- buffer_sz = snd_pcm_lib_buffer_bytes(substream);
- div_u64_rem(fw_tstamp->ring_buffer_counter, buffer_sz, &pointer_bytes);
- pointer_samples = bytes_to_samples(substream->runtime, pointer_bytes);
-
- dev_dbg(ctx->dev, "pcm delay %zu in bytes\n", delay_bytes);
-
- info->buffer_ptr = pointer_samples / substream->runtime->channels;
-
- info->pcm_delay = delay_frames / substream->runtime->channels;
- dev_dbg(ctx->dev, "buffer ptr %llu pcm_delay rep: %llu\n",
- info->buffer_ptr, info->pcm_delay);
- return 0;
-}
-
-static int sst_read_timestamp(struct device *dev, struct pcm_stream_info *info)
-{
- struct stream_info *stream;
- struct snd_pcm_substream *substream;
- struct snd_sst_tstamp fw_tstamp;
- unsigned int str_id;
- struct intel_sst_drv *ctx = dev_get_drvdata(dev);
-
- str_id = info->str_id;
- stream = get_stream_info(ctx, str_id);
- if (!stream)
- return -EINVAL;
-
- if (!stream->pcm_substream)
- return -EINVAL;
- substream = stream->pcm_substream;
-
- memcpy_fromio(&fw_tstamp,
- ((void *)(ctx->mailbox + ctx->tstamp)
- + (str_id * sizeof(fw_tstamp))),
- sizeof(fw_tstamp));
- return sst_calc_tstamp(ctx, info, substream, &fw_tstamp);
-}
-
-static int sst_stream_start(struct device *dev, int str_id)
-{
- struct stream_info *str_info;
- struct intel_sst_drv *ctx = dev_get_drvdata(dev);
-
- if (ctx->sst_state != SST_FW_RUNNING)
- return 0;
- str_info = get_stream_info(ctx, str_id);
- if (!str_info)
- return -EINVAL;
- str_info->prev = str_info->status;
- str_info->status = STREAM_RUNNING;
- sst_start_stream(ctx, str_id);
-
- return 0;
-}
-
-static int sst_stream_drop(struct device *dev, int str_id)
-{
- struct stream_info *str_info;
- struct intel_sst_drv *ctx = dev_get_drvdata(dev);
-
- if (ctx->sst_state != SST_FW_RUNNING)
- return 0;
-
- str_info = get_stream_info(ctx, str_id);
- if (!str_info)
- return -EINVAL;
- str_info->prev = STREAM_UN_INIT;
- str_info->status = STREAM_INIT;
- return sst_drop_stream(ctx, str_id);
-}
-
-static int sst_stream_init(struct device *dev, struct pcm_stream_info *str_info)
-{
- int str_id = 0;
- struct stream_info *stream;
- struct intel_sst_drv *ctx = dev_get_drvdata(dev);
-
- str_id = str_info->str_id;
-
- if (ctx->sst_state != SST_FW_RUNNING)
- return 0;
-
- stream = get_stream_info(ctx, str_id);
- if (!stream)
- return -EINVAL;
-
- dev_dbg(ctx->dev, "setting the period ptrs\n");
- stream->pcm_substream = str_info->arg;
- stream->period_elapsed = str_info->period_elapsed;
- stream->sfreq = str_info->sfreq;
- stream->prev = stream->status;
- stream->status = STREAM_INIT;
- dev_dbg(ctx->dev,
- "pcm_substream %p, period_elapsed %p, sfreq %d, status %d\n",
- stream->pcm_substream, stream->period_elapsed,
- stream->sfreq, stream->status);
-
- return 0;
-}
-
-/*
- * sst_set_byte_stream - Set generic params
- *
- * @cmd: control cmd to be set
- * @arg: command argument
- *
- * This function is called by MID sound card driver to configure
- * SST runtime params.
- */
-static int sst_send_byte_stream(struct device *dev,
- struct snd_sst_bytes_v2 *bytes)
-{
- int ret_val = 0;
- struct intel_sst_drv *ctx = dev_get_drvdata(dev);
-
- if (NULL == bytes)
- return -EINVAL;
- ret_val = pm_runtime_get_sync(ctx->dev);
- if (ret_val < 0)
- return ret_val;
-
- ret_val = sst_send_byte_stream_mrfld(ctx, bytes);
- sst_pm_runtime_put(ctx);
-
- return ret_val;
-}
-
-static struct sst_ops pcm_ops = {
- .open = sst_open_pcm_stream,
- .stream_init = sst_stream_init,
- .stream_start = sst_stream_start,
- .stream_drop = sst_stream_drop,
- .stream_read_tstamp = sst_read_timestamp,
- .send_byte_stream = sst_send_byte_stream,
- .close = sst_close_pcm_stream,
- .power = sst_power_control,
-};
-
-static struct compress_sst_ops compr_ops = {
- .open = sst_cdev_open,
- .close = sst_cdev_close,
- .stream_pause = sst_cdev_stream_pause,
- .stream_pause_release = sst_cdev_stream_pause_release,
- .stream_start = sst_cdev_stream_start,
- .stream_drop = sst_cdev_stream_drop,
- .stream_drain = sst_cdev_stream_drain,
- .stream_partial_drain = sst_cdev_stream_partial_drain,
- .tstamp = sst_cdev_tstamp,
- .ack = sst_cdev_ack,
- .get_caps = sst_cdev_caps,
- .get_codec_caps = sst_cdev_codec_caps,
- .set_metadata = sst_cdev_set_metadata,
- .power = sst_power_control,
-};
-
-static struct sst_device sst_dsp_device = {
- .name = "Intel(R) SST LPE",
- .dev = NULL,
- .ops = &pcm_ops,
- .compr_ops = &compr_ops,
-};
-
-/*
- * sst_register - function to register DSP
- *
- * This functions registers DSP with the platform driver
- */
-int sst_register(struct device *dev)
-{
- int ret_val;
-
- sst_dsp_device.dev = dev;
- ret_val = sst_register_dsp(&sst_dsp_device);
- if (ret_val)
- dev_err(dev, "Unable to register DSP with platform driver\n");
-
- return ret_val;
-}
-
-int sst_unregister(struct device *dev)
-{
- return sst_unregister_dsp(&sst_dsp_device);
-}
+++ /dev/null
-/*
- * sst_ipc.c - Intel SST Driver for audio engine
- *
- * Copyright (C) 2008-14 Intel Corporation
- * Authors: Vinod Koul <vinod.koul@intel.com>
- * Harsha Priya <priya.harsha@intel.com>
- * Dharageswari R <dharageswari.r@intel.com>
- * KP Jeeja <jeeja.kp@intel.com>
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-#include <linux/pci.h>
-#include <linux/firmware.h>
-#include <linux/sched.h>
-#include <linux/delay.h>
-#include <linux/pm_runtime.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/compress_driver.h>
-#include <asm/intel-mid.h>
-#include <asm/platform_sst_audio.h>
-#include "../sst-mfld-platform.h"
-#include "sst.h"
-#include "../sst-dsp.h"
-
-struct sst_block *sst_create_block(struct intel_sst_drv *ctx,
- u32 msg_id, u32 drv_id)
-{
- struct sst_block *msg = NULL;
-
- dev_dbg(ctx->dev, "Enter\n");
- msg = kzalloc(sizeof(*msg), GFP_KERNEL);
- if (!msg)
- return NULL;
- msg->condition = false;
- msg->on = true;
- msg->msg_id = msg_id;
- msg->drv_id = drv_id;
- spin_lock_bh(&ctx->block_lock);
- list_add_tail(&msg->node, &ctx->block_list);
- spin_unlock_bh(&ctx->block_lock);
-
- return msg;
-}
-
-/*
- * while handling the interrupts, we need to check for message status and
- * then if we are blocking for a message
- *
- * here we are unblocking the blocked ones, this is based on id we have
- * passed and search that for block threads.
- * We will not find block in two cases
- * a) when its small message and block in not there, so silently ignore
- * them
- * b) when we are actually not able to find the block (bug perhaps)
- *
- * Since we have bit of small messages we can spam kernel log with err
- * print on above so need to keep as debug prints which should be enabled
- * via dynamic debug while debugging IPC issues
- */
-int sst_wake_up_block(struct intel_sst_drv *ctx, int result,
- u32 drv_id, u32 ipc, void *data, u32 size)
-{
- struct sst_block *block = NULL;
-
- dev_dbg(ctx->dev, "Enter\n");
-
- spin_lock_bh(&ctx->block_lock);
- list_for_each_entry(block, &ctx->block_list, node) {
- dev_dbg(ctx->dev, "Block ipc %d, drv_id %d\n", block->msg_id,
- block->drv_id);
- if (block->msg_id == ipc && block->drv_id == drv_id) {
- dev_dbg(ctx->dev, "free up the block\n");
- block->ret_code = result;
- block->data = data;
- block->size = size;
- block->condition = true;
- spin_unlock_bh(&ctx->block_lock);
- wake_up(&ctx->wait_queue);
- return 0;
- }
- }
- spin_unlock_bh(&ctx->block_lock);
- dev_dbg(ctx->dev,
- "Block not found or a response received for a short msg for ipc %d, drv_id %d\n",
- ipc, drv_id);
- return -EINVAL;
-}
-
-int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed)
-{
- struct sst_block *block = NULL, *__block;
-
- dev_dbg(ctx->dev, "Enter\n");
- spin_lock_bh(&ctx->block_lock);
- list_for_each_entry_safe(block, __block, &ctx->block_list, node) {
- if (block == freed) {
- pr_debug("pvt_id freed --> %d\n", freed->drv_id);
- /* toggle the index position of pvt_id */
- list_del(&freed->node);
- spin_unlock_bh(&ctx->block_lock);
- kfree(freed->data);
- freed->data = NULL;
- kfree(freed);
- return 0;
- }
- }
- spin_unlock_bh(&ctx->block_lock);
- dev_err(ctx->dev, "block is already freed!!!\n");
- return -EINVAL;
-}
-
-int sst_post_message_mrfld(struct intel_sst_drv *sst_drv_ctx,
- struct ipc_post *ipc_msg, bool sync)
-{
- struct ipc_post *msg = ipc_msg;
- union ipc_header_mrfld header;
- unsigned int loop_count = 0;
- int retval = 0;
- unsigned long irq_flags;
-
- dev_dbg(sst_drv_ctx->dev, "Enter: sync: %d\n", sync);
- spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags);
- header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX);
- if (sync) {
- while (header.p.header_high.part.busy) {
- if (loop_count > 25) {
- dev_err(sst_drv_ctx->dev,
- "sst: Busy wait failed, cant send this msg\n");
- retval = -EBUSY;
- goto out;
- }
- cpu_relax();
- loop_count++;
- header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX);
- }
- } else {
- if (list_empty(&sst_drv_ctx->ipc_dispatch_list)) {
- /* queue is empty, nothing to send */
- spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
- dev_dbg(sst_drv_ctx->dev,
- "Empty msg queue... NO Action\n");
- return 0;
- }
-
- if (header.p.header_high.part.busy) {
- spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
- dev_dbg(sst_drv_ctx->dev, "Busy not free... post later\n");
- return 0;
- }
-
- /* copy msg from list */
- msg = list_entry(sst_drv_ctx->ipc_dispatch_list.next,
- struct ipc_post, node);
- list_del(&msg->node);
- }
- dev_dbg(sst_drv_ctx->dev, "sst: Post message: header = %x\n",
- msg->mrfld_header.p.header_high.full);
- dev_dbg(sst_drv_ctx->dev, "sst: size = 0x%x\n",
- msg->mrfld_header.p.header_low_payload);
-
- if (msg->mrfld_header.p.header_high.part.large)
- memcpy_toio(sst_drv_ctx->mailbox + SST_MAILBOX_SEND,
- msg->mailbox_data,
- msg->mrfld_header.p.header_low_payload);
-
- sst_shim_write64(sst_drv_ctx->shim, SST_IPCX, msg->mrfld_header.full);
-
-out:
- spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
- kfree(msg->mailbox_data);
- kfree(msg);
- return retval;
-}
-
-void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx)
-{
- union interrupt_reg_mrfld isr;
- union interrupt_reg_mrfld imr;
- union ipc_header_mrfld clear_ipc;
- unsigned long irq_flags;
-
- spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags);
- imr.full = sst_shim_read64(sst_drv_ctx->shim, SST_IMRX);
- isr.full = sst_shim_read64(sst_drv_ctx->shim, SST_ISRX);
-
- /* write 1 to clear*/
- isr.part.busy_interrupt = 1;
- sst_shim_write64(sst_drv_ctx->shim, SST_ISRX, isr.full);
-
- /* Set IA done bit */
- clear_ipc.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCD);
-
- clear_ipc.p.header_high.part.busy = 0;
- clear_ipc.p.header_high.part.done = 1;
- clear_ipc.p.header_low_payload = IPC_ACK_SUCCESS;
- sst_shim_write64(sst_drv_ctx->shim, SST_IPCD, clear_ipc.full);
- /* un mask busy interrupt */
- imr.part.busy_interrupt = 0;
- sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, imr.full);
- spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
-}
-
-
-/*
- * process_fw_init - process the FW init msg
- *
- * @msg: IPC message mailbox data from FW
- *
- * This function processes the FW init msg from FW
- * marks FW state and prints debug info of loaded FW
- */
-static void process_fw_init(struct intel_sst_drv *sst_drv_ctx,
- void *msg)
-{
- struct ipc_header_fw_init *init =
- (struct ipc_header_fw_init *)msg;
- int retval = 0;
-
- dev_dbg(sst_drv_ctx->dev, "*** FW Init msg came***\n");
- if (init->result) {
- sst_set_fw_state_locked(sst_drv_ctx, SST_RESET);
- dev_err(sst_drv_ctx->dev, "FW Init failed, Error %x\n",
- init->result);
- retval = init->result;
- goto ret;
- }
-
-ret:
- sst_wake_up_block(sst_drv_ctx, retval, FW_DWNL_ID, 0 , NULL, 0);
-}
-
-static void process_fw_async_msg(struct intel_sst_drv *sst_drv_ctx,
- struct ipc_post *msg)
-{
- u32 msg_id;
- int str_id;
- u32 data_size, i;
- void *data_offset;
- struct stream_info *stream;
- union ipc_header_high msg_high;
- u32 msg_low, pipe_id;
-
- msg_high = msg->mrfld_header.p.header_high;
- msg_low = msg->mrfld_header.p.header_low_payload;
- msg_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->cmd_id;
- data_offset = (msg->mailbox_data + sizeof(struct ipc_dsp_hdr));
- data_size = msg_low - (sizeof(struct ipc_dsp_hdr));
-
- switch (msg_id) {
- case IPC_SST_PERIOD_ELAPSED_MRFLD:
- pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
- str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
- if (str_id > 0) {
- dev_dbg(sst_drv_ctx->dev,
- "Period elapsed rcvd for pipe id 0x%x\n",
- pipe_id);
- stream = &sst_drv_ctx->streams[str_id];
- if (stream->period_elapsed)
- stream->period_elapsed(stream->pcm_substream);
- if (stream->compr_cb)
- stream->compr_cb(stream->compr_cb_param);
- }
- break;
-
- case IPC_IA_DRAIN_STREAM_MRFLD:
- pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
- str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
- if (str_id > 0) {
- stream = &sst_drv_ctx->streams[str_id];
- if (stream->drain_notify)
- stream->drain_notify(stream->drain_cb_param);
- }
- break;
-
- case IPC_IA_FW_ASYNC_ERR_MRFLD:
- dev_err(sst_drv_ctx->dev, "FW sent async error msg:\n");
- for (i = 0; i < (data_size/4); i++)
- print_hex_dump(KERN_DEBUG, NULL, DUMP_PREFIX_NONE,
- 16, 4, data_offset, data_size, false);
- break;
-
- case IPC_IA_FW_INIT_CMPLT_MRFLD:
- process_fw_init(sst_drv_ctx, data_offset);
- break;
-
- case IPC_IA_BUF_UNDER_RUN_MRFLD:
- pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
- str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
- if (str_id > 0)
- dev_err(sst_drv_ctx->dev,
- "Buffer under-run for pipe:%#x str_id:%d\n",
- pipe_id, str_id);
- break;
-
- default:
- dev_err(sst_drv_ctx->dev,
- "Unrecognized async msg from FW msg_id %#x\n", msg_id);
- }
-}
-
-void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx,
- struct ipc_post *msg)
-{
- unsigned int drv_id;
- void *data;
- union ipc_header_high msg_high;
- u32 msg_low;
- struct ipc_dsp_hdr *dsp_hdr;
- unsigned int cmd_id;
-
- msg_high = msg->mrfld_header.p.header_high;
- msg_low = msg->mrfld_header.p.header_low_payload;
-
- dev_dbg(sst_drv_ctx->dev, "IPC process message header %x payload %x\n",
- msg->mrfld_header.p.header_high.full,
- msg->mrfld_header.p.header_low_payload);
-
- drv_id = msg_high.part.drv_id;
-
- /* Check for async messages first */
- if (drv_id == SST_ASYNC_DRV_ID) {
- /*FW sent async large message*/
- process_fw_async_msg(sst_drv_ctx, msg);
- return;
- }
-
- /* FW sent short error response for an IPC */
- if (msg_high.part.result && drv_id && !msg_high.part.large) {
- /* 32-bit FW error code in msg_low */
- dev_err(sst_drv_ctx->dev, "FW sent error response 0x%x", msg_low);
- sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
- msg_high.part.drv_id,
- msg_high.part.msg_id, NULL, 0);
- return;
- }
-
- /*
- * Process all valid responses
- * if it is a large message, the payload contains the size to
- * copy from mailbox
- **/
- if (msg_high.part.large) {
- data = kzalloc(msg_low, GFP_KERNEL);
- if (!data)
- return;
- memcpy(data, (void *) msg->mailbox_data, msg_low);
- /* Copy command id so that we can use to put sst to reset */
- dsp_hdr = (struct ipc_dsp_hdr *)data;
- cmd_id = dsp_hdr->cmd_id;
- dev_dbg(sst_drv_ctx->dev, "cmd_id %d\n", dsp_hdr->cmd_id);
- if (sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
- msg_high.part.drv_id,
- msg_high.part.msg_id, data, msg_low))
- kfree(data);
- } else {
- sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
- msg_high.part.drv_id,
- msg_high.part.msg_id, NULL, 0);
- }
-
-}
+++ /dev/null
-/*
- * sst_dsp.c - Intel SST Driver for audio engine
- *
- * Copyright (C) 2008-14 Intel Corp
- * Authors: Vinod Koul <vinod.koul@intel.com>
- * Harsha Priya <priya.harsha@intel.com>
- * Dharageswari R <dharageswari.r@intel.com>
- * KP Jeeja <jeeja.kp@intel.com>
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This file contains all dsp controlling functions like firmware download,
- * setting/resetting dsp cores, etc
- */
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/fs.h>
-#include <linux/sched.h>
-#include <linux/firmware.h>
-#include <linux/dmaengine.h>
-#include <linux/pm_runtime.h>
-#include <linux/pm_qos.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/compress_driver.h>
-#include <asm/platform_sst_audio.h>
-#include "../sst-mfld-platform.h"
-#include "sst.h"
-#include "../sst-dsp.h"
-
-static inline void memcpy32_toio(void __iomem *dst, const void *src, int count)
-{
- /* __iowrite32_copy uses 32-bit count values so divide by 4 for
- * right count in words
- */
- __iowrite32_copy(dst, src, count/4);
-}
-
-/**
- * intel_sst_reset_dsp_mrfld - Resetting SST DSP
- *
- * This resets DSP in case of MRFLD platfroms
- */
-int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *sst_drv_ctx)
-{
- union config_status_reg_mrfld csr;
-
- dev_dbg(sst_drv_ctx->dev, "sst: Resetting the DSP in mrfld\n");
- csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
-
- dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
-
- csr.full |= 0x7;
- sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
- csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
-
- dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
-
- csr.full &= ~(0x1);
- sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
-
- csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
- dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
- return 0;
-}
-
-/**
- * sst_start_merrifield - Start the SST DSP processor
- *
- * This starts the DSP in MERRIFIELD platfroms
- */
-int sst_start_mrfld(struct intel_sst_drv *sst_drv_ctx)
-{
- union config_status_reg_mrfld csr;
-
- dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP in mrfld LALALALA\n");
- csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
- dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
-
- csr.full |= 0x7;
- sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
-
- csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
- dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
-
- csr.part.xt_snoop = 1;
- csr.full &= ~(0x5);
- sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
-
- csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
- dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP_merrifield:%llx\n",
- csr.full);
- return 0;
-}
-
-static int sst_validate_fw_image(struct intel_sst_drv *ctx, unsigned long size,
- struct fw_module_header **module, u32 *num_modules)
-{
- struct sst_fw_header *header;
- const void *sst_fw_in_mem = ctx->fw_in_mem;
-
- dev_dbg(ctx->dev, "Enter\n");
-
- /* Read the header information from the data pointer */
- header = (struct sst_fw_header *)sst_fw_in_mem;
- dev_dbg(ctx->dev,
- "header sign=%s size=%x modules=%x fmt=%x size=%zx\n",
- header->signature, header->file_size, header->modules,
- header->file_format, sizeof(*header));
-
- /* verify FW */
- if ((strncmp(header->signature, SST_FW_SIGN, 4) != 0) ||
- (size != header->file_size + sizeof(*header))) {
- /* Invalid FW signature */
- dev_err(ctx->dev, "InvalidFW sign/filesize mismatch\n");
- return -EINVAL;
- }
- *num_modules = header->modules;
- *module = (void *)sst_fw_in_mem + sizeof(*header);
-
- return 0;
-}
-
-/*
- * sst_fill_memcpy_list - Fill the memcpy list
- *
- * @memcpy_list: List to be filled
- * @destn: Destination addr to be filled in the list
- * @src: Source addr to be filled in the list
- * @size: Size to be filled in the list
- *
- * Adds the node to the list after required fields
- * are populated in the node
- */
-static int sst_fill_memcpy_list(struct list_head *memcpy_list,
- void *destn, const void *src, u32 size, bool is_io)
-{
- struct sst_memcpy_list *listnode;
-
- listnode = kzalloc(sizeof(*listnode), GFP_KERNEL);
- if (listnode == NULL)
- return -ENOMEM;
- listnode->dstn = destn;
- listnode->src = src;
- listnode->size = size;
- listnode->is_io = is_io;
- list_add_tail(&listnode->memcpylist, memcpy_list);
-
- return 0;
-}
-
-/**
- * sst_parse_module_memcpy - Parse audio FW modules and populate the memcpy list
- *
- * @sst_drv_ctx : driver context
- * @module : FW module header
- * @memcpy_list : Pointer to the list to be populated
- * Create the memcpy list as the number of block to be copied
- * returns error or 0 if module sizes are proper
- */
-static int sst_parse_module_memcpy(struct intel_sst_drv *sst_drv_ctx,
- struct fw_module_header *module, struct list_head *memcpy_list)
-{
- struct fw_block_info *block;
- u32 count;
- int ret_val = 0;
- void __iomem *ram_iomem;
-
- dev_dbg(sst_drv_ctx->dev, "module sign %s size %x blocks %x type %x\n",
- module->signature, module->mod_size,
- module->blocks, module->type);
- dev_dbg(sst_drv_ctx->dev, "module entrypoint 0x%x\n", module->entry_point);
-
- block = (void *)module + sizeof(*module);
-
- for (count = 0; count < module->blocks; count++) {
- if (block->size <= 0) {
- dev_err(sst_drv_ctx->dev, "block size invalid\n");
- return -EINVAL;
- }
- switch (block->type) {
- case SST_IRAM:
- ram_iomem = sst_drv_ctx->iram;
- break;
- case SST_DRAM:
- ram_iomem = sst_drv_ctx->dram;
- break;
- case SST_DDR:
- ram_iomem = sst_drv_ctx->ddr;
- break;
- case SST_CUSTOM_INFO:
- block = (void *)block + sizeof(*block) + block->size;
- continue;
- default:
- dev_err(sst_drv_ctx->dev, "wrong ram type0x%x in block0x%x\n",
- block->type, count);
- return -EINVAL;
- }
-
- ret_val = sst_fill_memcpy_list(memcpy_list,
- ram_iomem + block->ram_offset,
- (void *)block + sizeof(*block), block->size, 1);
- if (ret_val)
- return ret_val;
-
- block = (void *)block + sizeof(*block) + block->size;
- }
- return 0;
-}
-
-/**
- * sst_parse_fw_memcpy - parse the firmware image & populate the list for memcpy
- *
- * @ctx : pointer to drv context
- * @size : size of the firmware
- * @fw_list : pointer to list_head to be populated
- * This function parses the FW image and saves the parsed image in the list
- * for memcpy
- */
-static int sst_parse_fw_memcpy(struct intel_sst_drv *ctx, unsigned long size,
- struct list_head *fw_list)
-{
- struct fw_module_header *module;
- u32 count, num_modules;
- int ret_val;
-
- ret_val = sst_validate_fw_image(ctx, size, &module, &num_modules);
- if (ret_val)
- return ret_val;
-
- for (count = 0; count < num_modules; count++) {
- ret_val = sst_parse_module_memcpy(ctx, module, fw_list);
- if (ret_val)
- return ret_val;
- module = (void *)module + sizeof(*module) + module->mod_size;
- }
-
- return 0;
-}
-
-/**
- * sst_do_memcpy - function initiates the memcpy
- *
- * @memcpy_list: Pter to memcpy list on which the memcpy needs to be initiated
- *
- * Triggers the memcpy
- */
-static void sst_do_memcpy(struct list_head *memcpy_list)
-{
- struct sst_memcpy_list *listnode;
-
- list_for_each_entry(listnode, memcpy_list, memcpylist) {
- if (listnode->is_io == true)
- memcpy32_toio((void __iomem *)listnode->dstn,
- listnode->src, listnode->size);
- else
- memcpy(listnode->dstn, listnode->src, listnode->size);
- }
-}
-
-void sst_memcpy_free_resources(struct intel_sst_drv *sst_drv_ctx)
-{
- struct sst_memcpy_list *listnode, *tmplistnode;
-
- /* Free the list */
- if (!list_empty(&sst_drv_ctx->memcpy_list)) {
- list_for_each_entry_safe(listnode, tmplistnode,
- &sst_drv_ctx->memcpy_list, memcpylist) {
- list_del(&listnode->memcpylist);
- kfree(listnode);
- }
- }
-}
-
-static int sst_cache_and_parse_fw(struct intel_sst_drv *sst,
- const struct firmware *fw)
-{
- int retval = 0;
-
- sst->fw_in_mem = kzalloc(fw->size, GFP_KERNEL);
- if (!sst->fw_in_mem) {
- retval = -ENOMEM;
- goto end_release;
- }
- dev_dbg(sst->dev, "copied fw to %p", sst->fw_in_mem);
- dev_dbg(sst->dev, "phys: %lx", (unsigned long)virt_to_phys(sst->fw_in_mem));
- memcpy(sst->fw_in_mem, fw->data, fw->size);
- retval = sst_parse_fw_memcpy(sst, fw->size, &sst->memcpy_list);
- if (retval) {
- dev_err(sst->dev, "Failed to parse fw\n");
- kfree(sst->fw_in_mem);
- sst->fw_in_mem = NULL;
- }
-
-end_release:
- release_firmware(fw);
- return retval;
-
-}
-
-void sst_firmware_load_cb(const struct firmware *fw, void *context)
-{
- struct intel_sst_drv *ctx = context;
-
- dev_dbg(ctx->dev, "Enter\n");
-
- if (fw == NULL) {
- dev_err(ctx->dev, "request fw failed\n");
- return;
- }
-
- mutex_lock(&ctx->sst_lock);
-
- if (ctx->sst_state != SST_RESET ||
- ctx->fw_in_mem != NULL) {
- release_firmware(fw);
- mutex_unlock(&ctx->sst_lock);
- return;
- }
-
- dev_dbg(ctx->dev, "Request Fw completed\n");
- sst_cache_and_parse_fw(ctx, fw);
- mutex_unlock(&ctx->sst_lock);
-}
-
-/*
- * sst_request_fw - requests audio fw from kernel and saves a copy
- *
- * This function requests the SST FW from the kernel, parses it and
- * saves a copy in the driver context
- */
-static int sst_request_fw(struct intel_sst_drv *sst)
-{
- int retval = 0;
- const struct firmware *fw;
-
- retval = request_firmware(&fw, sst->firmware_name, sst->dev);
- if (fw == NULL) {
- dev_err(sst->dev, "fw is returning as null\n");
- return -EINVAL;
- }
- if (retval) {
- dev_err(sst->dev, "request fw failed %d\n", retval);
- return retval;
- }
- mutex_lock(&sst->sst_lock);
- retval = sst_cache_and_parse_fw(sst, fw);
- mutex_unlock(&sst->sst_lock);
-
- return retval;
-}
-
-/*
- * Writing the DDR physical base to DCCM offset
- * so that FW can use it to setup TLB
- */
-static void sst_dccm_config_write(void __iomem *dram_base,
- unsigned int ddr_base)
-{
- void __iomem *addr;
- u32 bss_reset = 0;
-
- addr = (void __iomem *)(dram_base + MRFLD_FW_DDR_BASE_OFFSET);
- memcpy32_toio(addr, (void *)&ddr_base, sizeof(u32));
- bss_reset |= (1 << MRFLD_FW_BSS_RESET_BIT);
- addr = (void __iomem *)(dram_base + MRFLD_FW_FEATURE_BASE_OFFSET);
- memcpy32_toio(addr, &bss_reset, sizeof(u32));
-
-}
-
-void sst_post_download_mrfld(struct intel_sst_drv *ctx)
-{
- sst_dccm_config_write(ctx->dram, ctx->ddr_base);
- dev_dbg(ctx->dev, "config written to DCCM\n");
-}
-
-/**
- * sst_load_fw - function to load FW into DSP
- * Transfers the FW to DSP using dma/memcpy
- */
-int sst_load_fw(struct intel_sst_drv *sst_drv_ctx)
-{
- int ret_val = 0;
- struct sst_block *block;
-
- dev_dbg(sst_drv_ctx->dev, "sst_load_fw\n");
-
- if (sst_drv_ctx->sst_state != SST_RESET ||
- sst_drv_ctx->sst_state == SST_SHUTDOWN)
- return -EAGAIN;
-
- if (!sst_drv_ctx->fw_in_mem) {
- dev_dbg(sst_drv_ctx->dev, "sst: FW not in memory retry to download\n");
- ret_val = sst_request_fw(sst_drv_ctx);
- if (ret_val)
- return ret_val;
- }
-
- BUG_ON(!sst_drv_ctx->fw_in_mem);
- block = sst_create_block(sst_drv_ctx, 0, FW_DWNL_ID);
- if (block == NULL)
- return -ENOMEM;
-
- /* Prevent C-states beyond C6 */
- pm_qos_update_request(sst_drv_ctx->qos, 0);
-
- sst_drv_ctx->sst_state = SST_FW_LOADING;
-
- ret_val = sst_drv_ctx->ops->reset(sst_drv_ctx);
- if (ret_val)
- goto restore;
-
- sst_do_memcpy(&sst_drv_ctx->memcpy_list);
-
- /* Write the DRAM/DCCM config before enabling FW */
- if (sst_drv_ctx->ops->post_download)
- sst_drv_ctx->ops->post_download(sst_drv_ctx);
-
- /* bring sst out of reset */
- ret_val = sst_drv_ctx->ops->start(sst_drv_ctx);
- if (ret_val)
- goto restore;
-
- ret_val = sst_wait_timeout(sst_drv_ctx, block);
- if (ret_val) {
- dev_err(sst_drv_ctx->dev, "fw download failed %d\n" , ret_val);
- /* FW download failed due to timeout */
- ret_val = -EBUSY;
-
- }
-
-
-restore:
- /* Re-enable Deeper C-states beyond C6 */
- pm_qos_update_request(sst_drv_ctx->qos, PM_QOS_DEFAULT_VALUE);
- sst_free_block(sst_drv_ctx, block);
- dev_dbg(sst_drv_ctx->dev, "fw load successful!!!\n");
-
- if (sst_drv_ctx->ops->restore_dsp_context)
- sst_drv_ctx->ops->restore_dsp_context();
- sst_drv_ctx->sst_state = SST_FW_RUNNING;
- return ret_val;
-}
-
+++ /dev/null
-/*
- * sst_pci.c - SST (LPE) driver init file for pci enumeration.
- *
- * Copyright (C) 2008-14 Intel Corp
- * Authors: Vinod Koul <vinod.koul@intel.com>
- * Harsha Priya <priya.harsha@intel.com>
- * Dharageswari R <dharageswari.r@intel.com>
- * KP Jeeja <jeeja.kp@intel.com>
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/fs.h>
-#include <linux/firmware.h>
-#include <linux/pm_runtime.h>
-#include <sound/core.h>
-#include <sound/soc.h>
-#include <asm/platform_sst_audio.h>
-#include "../sst-mfld-platform.h"
-#include "sst.h"
-
-static int sst_platform_get_resources(struct intel_sst_drv *ctx)
-{
- int ddr_base, ret = 0;
- struct pci_dev *pci = ctx->pci;
-
- ret = pci_request_regions(pci, SST_DRV_NAME);
- if (ret)
- return ret;
-
- /* map registers */
- /* DDR base */
- if (ctx->dev_id == SST_MRFLD_PCI_ID) {
- ctx->ddr_base = pci_resource_start(pci, 0);
- /* check that the relocated IMR base matches with FW Binary */
- ddr_base = relocate_imr_addr_mrfld(ctx->ddr_base);
- if (!ctx->pdata->lib_info) {
- dev_err(ctx->dev, "lib_info pointer NULL\n");
- ret = -EINVAL;
- goto do_release_regions;
- }
- if (ddr_base != ctx->pdata->lib_info->mod_base) {
- dev_err(ctx->dev,
- "FW LSP DDR BASE does not match with IFWI\n");
- ret = -EINVAL;
- goto do_release_regions;
- }
- ctx->ddr_end = pci_resource_end(pci, 0);
-
- ctx->ddr = pcim_iomap(pci, 0,
- pci_resource_len(pci, 0));
- if (!ctx->ddr) {
- ret = -EINVAL;
- goto do_release_regions;
- }
- dev_dbg(ctx->dev, "sst: DDR Ptr %p\n", ctx->ddr);
- } else {
- ctx->ddr = NULL;
- }
- /* SHIM */
- ctx->shim_phy_add = pci_resource_start(pci, 1);
- ctx->shim = pcim_iomap(pci, 1, pci_resource_len(pci, 1));
- if (!ctx->shim) {
- ret = -EINVAL;
- goto do_release_regions;
- }
- dev_dbg(ctx->dev, "SST Shim Ptr %p\n", ctx->shim);
-
- /* Shared SRAM */
- ctx->mailbox_add = pci_resource_start(pci, 2);
- ctx->mailbox = pcim_iomap(pci, 2, pci_resource_len(pci, 2));
- if (!ctx->mailbox) {
- ret = -EINVAL;
- goto do_release_regions;
- }
- dev_dbg(ctx->dev, "SRAM Ptr %p\n", ctx->mailbox);
-
- /* IRAM */
- ctx->iram_end = pci_resource_end(pci, 3);
- ctx->iram_base = pci_resource_start(pci, 3);
- ctx->iram = pcim_iomap(pci, 3, pci_resource_len(pci, 3));
- if (!ctx->iram) {
- ret = -EINVAL;
- goto do_release_regions;
- }
- dev_dbg(ctx->dev, "IRAM Ptr %p\n", ctx->iram);
-
- /* DRAM */
- ctx->dram_end = pci_resource_end(pci, 4);
- ctx->dram_base = pci_resource_start(pci, 4);
- ctx->dram = pcim_iomap(pci, 4, pci_resource_len(pci, 4));
- if (!ctx->dram) {
- ret = -EINVAL;
- goto do_release_regions;
- }
- dev_dbg(ctx->dev, "DRAM Ptr %p\n", ctx->dram);
-do_release_regions:
- pci_release_regions(pci);
- return 0;
-}
-
-/*
- * intel_sst_probe - PCI probe function
- *
- * @pci: PCI device structure
- * @pci_id: PCI device ID structure
- *
- */
-static int intel_sst_probe(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
-{
- int ret = 0;
- struct intel_sst_drv *sst_drv_ctx;
- struct sst_platform_info *sst_pdata = pci->dev.platform_data;
-
- dev_dbg(&pci->dev, "Probe for DID %x\n", pci->device);
- ret = sst_alloc_drv_context(&sst_drv_ctx, &pci->dev, pci->device);
- if (ret < 0)
- return ret;
-
- sst_drv_ctx->pdata = sst_pdata;
- sst_drv_ctx->irq_num = pci->irq;
- snprintf(sst_drv_ctx->firmware_name, sizeof(sst_drv_ctx->firmware_name),
- "%s%04x%s", "fw_sst_",
- sst_drv_ctx->dev_id, ".bin");
-
- ret = sst_context_init(sst_drv_ctx);
- if (ret < 0)
- return ret;
-
- /* Init the device */
- ret = pcim_enable_device(pci);
- if (ret) {
- dev_err(sst_drv_ctx->dev,
- "device can't be enabled. Returned err: %d\n", ret);
- goto do_free_drv_ctx;
- }
- sst_drv_ctx->pci = pci_dev_get(pci);
- ret = sst_platform_get_resources(sst_drv_ctx);
- if (ret < 0)
- goto do_free_drv_ctx;
-
- pci_set_drvdata(pci, sst_drv_ctx);
- sst_configure_runtime_pm(sst_drv_ctx);
-
- return ret;
-
-do_free_drv_ctx:
- sst_context_cleanup(sst_drv_ctx);
- dev_err(sst_drv_ctx->dev, "Probe failed with %d\n", ret);
- return ret;
-}
-
-/**
- * intel_sst_remove - PCI remove function
- *
- * @pci: PCI device structure
- *
- * This function is called by OS when a device is unloaded
- * This frees the interrupt etc
- */
-static void intel_sst_remove(struct pci_dev *pci)
-{
- struct intel_sst_drv *sst_drv_ctx = pci_get_drvdata(pci);
-
- sst_context_cleanup(sst_drv_ctx);
- pci_dev_put(sst_drv_ctx->pci);
- pci_release_regions(pci);
- pci_set_drvdata(pci, NULL);
-}
-
-/* PCI Routines */
-static struct pci_device_id intel_sst_ids[] = {
- { PCI_VDEVICE(INTEL, SST_MRFLD_PCI_ID), 0},
- { 0, }
-};
-
-static struct pci_driver sst_driver = {
- .name = SST_DRV_NAME,
- .id_table = intel_sst_ids,
- .probe = intel_sst_probe,
- .remove = intel_sst_remove,
-#ifdef CONFIG_PM
- .driver = {
- .pm = &intel_sst_pm,
- },
-#endif
-};
-
-module_pci_driver(sst_driver);
-
-MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine PCI Driver");
-MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
-MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
-MODULE_AUTHOR("Dharageswari R <dharageswari.r@intel.com>");
-MODULE_AUTHOR("KP Jeeja <jeeja.kp@intel.com>");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("sst");
+++ /dev/null
-/*
- * sst_pvt.c - Intel SST Driver for audio engine
- *
- * Copyright (C) 2008-14 Intel Corp
- * Authors: Vinod Koul <vinod.koul@intel.com>
- * Harsha Priya <priya.harsha@intel.com>
- * Dharageswari R <dharageswari.r@intel.com>
- * KP Jeeja <jeeja.kp@intel.com>
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-#include <linux/kobject.h>
-#include <linux/pci.h>
-#include <linux/fs.h>
-#include <linux/firmware.h>
-#include <linux/pm_runtime.h>
-#include <linux/sched.h>
-#include <linux/delay.h>
-#include <sound/asound.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/compress_driver.h>
-#include <asm/platform_sst_audio.h>
-#include "../sst-mfld-platform.h"
-#include "sst.h"
-#include "../sst-dsp.h"
-
-int sst_shim_write(void __iomem *addr, int offset, int value)
-{
- writel(value, addr + offset);
- return 0;
-}
-
-u32 sst_shim_read(void __iomem *addr, int offset)
-{
- return readl(addr + offset);
-}
-
-u64 sst_reg_read64(void __iomem *addr, int offset)
-{
- u64 val = 0;
-
- memcpy_fromio(&val, addr + offset, sizeof(val));
-
- return val;
-}
-
-int sst_shim_write64(void __iomem *addr, int offset, u64 value)
-{
- memcpy_toio(addr + offset, &value, sizeof(value));
- return 0;
-}
-
-u64 sst_shim_read64(void __iomem *addr, int offset)
-{
- u64 val = 0;
-
- memcpy_fromio(&val, addr + offset, sizeof(val));
- return val;
-}
-
-void sst_set_fw_state_locked(
- struct intel_sst_drv *sst_drv_ctx, int sst_state)
-{
- mutex_lock(&sst_drv_ctx->sst_lock);
- sst_drv_ctx->sst_state = sst_state;
- mutex_unlock(&sst_drv_ctx->sst_lock);
-}
-
-/*
- * sst_wait_interruptible - wait on event
- *
- * @sst_drv_ctx: Driver context
- * @block: Driver block to wait on
- *
- * This function waits without a timeout (and is interruptable) for a
- * given block event
- */
-int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx,
- struct sst_block *block)
-{
- int retval = 0;
-
- if (!wait_event_interruptible(sst_drv_ctx->wait_queue,
- block->condition)) {
- /* event wake */
- if (block->ret_code < 0) {
- dev_err(sst_drv_ctx->dev,
- "stream failed %d\n", block->ret_code);
- retval = -EBUSY;
- } else {
- dev_dbg(sst_drv_ctx->dev, "event up\n");
- retval = 0;
- }
- } else {
- dev_err(sst_drv_ctx->dev, "signal interrupted\n");
- retval = -EINTR;
- }
- return retval;
-
-}
-
-unsigned long long read_shim_data(struct intel_sst_drv *sst, int addr)
-{
- unsigned long long val = 0;
-
- switch (sst->dev_id) {
- case SST_MRFLD_PCI_ID:
- case SST_BYT_ACPI_ID:
- val = sst_shim_read64(sst->shim, addr);
- break;
- }
- return val;
-}
-
-void write_shim_data(struct intel_sst_drv *sst, int addr,
- unsigned long long data)
-{
- switch (sst->dev_id) {
- case SST_MRFLD_PCI_ID:
- case SST_BYT_ACPI_ID:
- sst_shim_write64(sst->shim, addr, (u64) data);
- break;
- }
-}
-
-/*
- * sst_wait_timeout - wait on event for timeout
- *
- * @sst_drv_ctx: Driver context
- * @block: Driver block to wait on
- *
- * This function waits with a timeout value (and is not interruptible) on a
- * given block event
- */
-int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx, struct sst_block *block)
-{
- int retval = 0;
-
- /*
- * NOTE:
- * Observed that FW processes the alloc msg and replies even
- * before the alloc thread has finished execution
- */
- dev_dbg(sst_drv_ctx->dev,
- "waiting for condition %x ipc %d drv_id %d\n",
- block->condition, block->msg_id, block->drv_id);
- if (wait_event_timeout(sst_drv_ctx->wait_queue,
- block->condition,
- msecs_to_jiffies(SST_BLOCK_TIMEOUT))) {
- /* event wake */
- dev_dbg(sst_drv_ctx->dev, "Event wake %x\n",
- block->condition);
- dev_dbg(sst_drv_ctx->dev, "message ret: %d\n",
- block->ret_code);
- retval = -block->ret_code;
- } else {
- block->on = false;
- dev_err(sst_drv_ctx->dev,
- "Wait timed-out condition:%#x, msg_id:%#x fw_state %#x\n",
- block->condition, block->msg_id, sst_drv_ctx->sst_state);
- sst_drv_ctx->sst_state = SST_RESET;
-
- retval = -EBUSY;
- }
- return retval;
-}
-
-/*
- * sst_create_ipc_msg - create a IPC message
- *
- * @arg: ipc message
- * @large: large or short message
- *
- * this function allocates structures to send a large or short
- * message to the firmware
- */
-int sst_create_ipc_msg(struct ipc_post **arg, bool large)
-{
- struct ipc_post *msg;
-
- msg = kzalloc(sizeof(struct ipc_post), GFP_ATOMIC);
- if (!msg)
- return -ENOMEM;
- if (large) {
- msg->mailbox_data = kzalloc(SST_MAILBOX_SIZE, GFP_ATOMIC);
- if (!msg->mailbox_data) {
- kfree(msg);
- return -ENOMEM;
- }
- } else {
- msg->mailbox_data = NULL;
- }
- msg->is_large = large;
- *arg = msg;
- return 0;
-}
-
-/*
- * sst_create_block_and_ipc_msg - Creates IPC message and sst block
- * @arg: passed to sst_create_ipc_message API
- * @large: large or short message
- * @sst_drv_ctx: sst driver context
- * @block: return block allocated
- * @msg_id: IPC
- * @drv_id: stream id or private id
- */
-int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large,
- struct intel_sst_drv *sst_drv_ctx, struct sst_block **block,
- u32 msg_id, u32 drv_id)
-{
- int retval = 0;
-
- retval = sst_create_ipc_msg(arg, large);
- if (retval)
- return retval;
- *block = sst_create_block(sst_drv_ctx, msg_id, drv_id);
- if (*block == NULL) {
- kfree(*arg);
- return -ENOMEM;
- }
- return retval;
-}
-
-/*
- * sst_clean_stream - clean the stream context
- *
- * @stream: stream structure
- *
- * this function resets the stream contexts
- * should be called in free
- */
-void sst_clean_stream(struct stream_info *stream)
-{
- stream->status = STREAM_UN_INIT;
- stream->prev = STREAM_UN_INIT;
- mutex_lock(&stream->lock);
- stream->cumm_bytes = 0;
- mutex_unlock(&stream->lock);
-}
-
-int sst_prepare_and_post_msg(struct intel_sst_drv *sst,
- int task_id, int ipc_msg, int cmd_id, int pipe_id,
- size_t mbox_data_len, const void *mbox_data, void **data,
- bool large, bool fill_dsp, bool sync, bool response)
-{
- struct ipc_post *msg = NULL;
- struct ipc_dsp_hdr dsp_hdr;
- struct sst_block *block;
- int ret = 0, pvt_id;
-
- pvt_id = sst_assign_pvt_id(sst);
- if (pvt_id < 0)
- return pvt_id;
-
- if (response)
- ret = sst_create_block_and_ipc_msg(
- &msg, large, sst, &block, ipc_msg, pvt_id);
- else
- ret = sst_create_ipc_msg(&msg, large);
-
- if (ret < 0) {
- test_and_clear_bit(pvt_id, &sst->pvt_id);
- return -ENOMEM;
- }
-
- dev_dbg(sst->dev, "pvt_id = %d, pipe id = %d, task = %d ipc_msg: %d\n",
- pvt_id, pipe_id, task_id, ipc_msg);
- sst_fill_header_mrfld(&msg->mrfld_header, ipc_msg,
- task_id, large, pvt_id);
- msg->mrfld_header.p.header_low_payload = sizeof(dsp_hdr) + mbox_data_len;
- msg->mrfld_header.p.header_high.part.res_rqd = !sync;
- dev_dbg(sst->dev, "header:%x\n",
- msg->mrfld_header.p.header_high.full);
- dev_dbg(sst->dev, "response rqd: %x",
- msg->mrfld_header.p.header_high.part.res_rqd);
- dev_dbg(sst->dev, "msg->mrfld_header.p.header_low_payload:%d",
- msg->mrfld_header.p.header_low_payload);
- if (fill_dsp) {
- sst_fill_header_dsp(&dsp_hdr, cmd_id, pipe_id, mbox_data_len);
- memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr));
- if (mbox_data_len) {
- memcpy(msg->mailbox_data + sizeof(dsp_hdr),
- mbox_data, mbox_data_len);
- }
- }
-
- if (sync)
- sst->ops->post_message(sst, msg, true);
- else
- sst_add_to_dispatch_list_and_post(sst, msg);
-
- if (response) {
- ret = sst_wait_timeout(sst, block);
- if (ret < 0) {
- goto out;
- } else if(block->data) {
- if (!data)
- goto out;
- *data = kzalloc(block->size, GFP_KERNEL);
- if (!(*data)) {
- ret = -ENOMEM;
- goto out;
- } else
- memcpy(data, (void *) block->data, block->size);
- }
- }
-out:
- if (response)
- sst_free_block(sst, block);
- test_and_clear_bit(pvt_id, &sst->pvt_id);
- return ret;
-}
-
-int sst_pm_runtime_put(struct intel_sst_drv *sst_drv)
-{
- int ret;
-
- pm_runtime_mark_last_busy(sst_drv->dev);
- ret = pm_runtime_put_autosuspend(sst_drv->dev);
- if (ret < 0)
- return ret;
- return 0;
-}
-
-void sst_fill_header_mrfld(union ipc_header_mrfld *header,
- int msg, int task_id, int large, int drv_id)
-{
- header->full = 0;
- header->p.header_high.part.msg_id = msg;
- header->p.header_high.part.task_id = task_id;
- header->p.header_high.part.large = large;
- header->p.header_high.part.drv_id = drv_id;
- header->p.header_high.part.done = 0;
- header->p.header_high.part.busy = 1;
- header->p.header_high.part.res_rqd = 1;
-}
-
-void sst_fill_header_dsp(struct ipc_dsp_hdr *dsp, int msg,
- int pipe_id, int len)
-{
- dsp->cmd_id = msg;
- dsp->mod_index_id = 0xff;
- dsp->pipe_id = pipe_id;
- dsp->length = len;
- dsp->mod_id = 0;
-}
-
-#define SST_MAX_BLOCKS 15
-/*
- * sst_assign_pvt_id - assign a pvt id for stream
- *
- * @sst_drv_ctx : driver context
- *
- * this function assigns a private id for calls that dont have stream
- * context yet, should be called with lock held
- * uses bits for the id, and finds first free bits and assigns that
- */
-int sst_assign_pvt_id(struct intel_sst_drv *drv)
-{
- int local;
-
- spin_lock(&drv->block_lock);
- /* find first zero index from lsb */
- local = ffz(drv->pvt_id);
- dev_dbg(drv->dev, "pvt_id assigned --> %d\n", local);
- if (local >= SST_MAX_BLOCKS){
- spin_unlock(&drv->block_lock);
- dev_err(drv->dev, "PVT _ID error: no free id blocks ");
- return -EINVAL;
- }
- /* toggle the index */
- change_bit(local, &drv->pvt_id);
- spin_unlock(&drv->block_lock);
- return local;
-}
-
-void sst_init_stream(struct stream_info *stream,
- int codec, int sst_id, int ops, u8 slot)
-{
- stream->status = STREAM_INIT;
- stream->prev = STREAM_UN_INIT;
- stream->ops = ops;
-}
-
-int sst_validate_strid(
- struct intel_sst_drv *sst_drv_ctx, int str_id)
-{
- if (str_id <= 0 || str_id > sst_drv_ctx->info.max_streams) {
- dev_err(sst_drv_ctx->dev,
- "SST ERR: invalid stream id : %d, max %d\n",
- str_id, sst_drv_ctx->info.max_streams);
- return -EINVAL;
- }
-
- return 0;
-}
-
-struct stream_info *get_stream_info(
- struct intel_sst_drv *sst_drv_ctx, int str_id)
-{
- if (sst_validate_strid(sst_drv_ctx, str_id))
- return NULL;
- return &sst_drv_ctx->streams[str_id];
-}
-
-int get_stream_id_mrfld(struct intel_sst_drv *sst_drv_ctx,
- u32 pipe_id)
-{
- int i;
-
- for (i = 1; i <= sst_drv_ctx->info.max_streams; i++)
- if (pipe_id == sst_drv_ctx->streams[i].pipe_id)
- return i;
-
- dev_dbg(sst_drv_ctx->dev, "no such pipe_id(%u)", pipe_id);
- return -1;
-}
-
-u32 relocate_imr_addr_mrfld(u32 base_addr)
-{
- /* Get the difference from 512MB aligned base addr */
- /* relocate the base */
- base_addr = MRFLD_FW_VIRTUAL_BASE + (base_addr % (512 * 1024 * 1024));
- return base_addr;
-}
-EXPORT_SYMBOL_GPL(relocate_imr_addr_mrfld);
-
-void sst_add_to_dispatch_list_and_post(struct intel_sst_drv *sst,
- struct ipc_post *msg)
-{
- unsigned long irq_flags;
-
- spin_lock_irqsave(&sst->ipc_spin_lock, irq_flags);
- list_add_tail(&msg->node, &sst->ipc_dispatch_list);
- spin_unlock_irqrestore(&sst->ipc_spin_lock, irq_flags);
- sst->ops->post_message(sst, NULL, false);
-}
+++ /dev/null
-/*
- * sst_stream.c - Intel SST Driver for audio engine
- *
- * Copyright (C) 2008-14 Intel Corp
- * Authors: Vinod Koul <vinod.koul@intel.com>
- * Harsha Priya <priya.harsha@intel.com>
- * Dharageswari R <dharageswari.r@intel.com>
- * KP Jeeja <jeeja.kp@intel.com>
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-#include <linux/pci.h>
-#include <linux/firmware.h>
-#include <linux/sched.h>
-#include <linux/delay.h>
-#include <linux/pm_runtime.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/compress_driver.h>
-#include <asm/platform_sst_audio.h>
-#include "../sst-mfld-platform.h"
-#include "sst.h"
-#include "../sst-dsp.h"
-
-int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params)
-{
- struct snd_sst_alloc_mrfld alloc_param;
- struct snd_sst_params *str_params;
- struct snd_sst_tstamp fw_tstamp;
- struct stream_info *str_info;
- struct snd_sst_alloc_response *response;
- unsigned int str_id, pipe_id, task_id;
- int i, num_ch, ret = 0;
- void *data = NULL;
-
- dev_dbg(sst_drv_ctx->dev, "Enter\n");
- BUG_ON(!params);
-
- str_params = (struct snd_sst_params *)params;
- memset(&alloc_param, 0, sizeof(alloc_param));
- alloc_param.operation = str_params->ops;
- alloc_param.codec_type = str_params->codec;
- alloc_param.sg_count = str_params->aparams.sg_count;
- alloc_param.ring_buf_info[0].addr =
- str_params->aparams.ring_buf_info[0].addr;
- alloc_param.ring_buf_info[0].size =
- str_params->aparams.ring_buf_info[0].size;
- alloc_param.frag_size = str_params->aparams.frag_size;
-
- memcpy(&alloc_param.codec_params, &str_params->sparams,
- sizeof(struct snd_sst_stream_params));
-
- /*
- * fill channel map params for multichannel support.
- * Ideally channel map should be received from upper layers
- * for multichannel support.
- * Currently hardcoding as per FW reqm.
- */
- num_ch = sst_get_num_channel(str_params);
- for (i = 0; i < 8; i++) {
- if (i < num_ch)
- alloc_param.codec_params.uc.pcm_params.channel_map[i] = i;
- else
- alloc_param.codec_params.uc.pcm_params.channel_map[i] = 0xFF;
- }
-
- str_id = str_params->stream_id;
- str_info = get_stream_info(sst_drv_ctx, str_id);
- if (str_info == NULL) {
- dev_err(sst_drv_ctx->dev, "get stream info returned null\n");
- return -EINVAL;
- }
-
- pipe_id = str_params->device_type;
- task_id = str_params->task;
- sst_drv_ctx->streams[str_id].pipe_id = pipe_id;
- sst_drv_ctx->streams[str_id].task_id = task_id;
- sst_drv_ctx->streams[str_id].num_ch = num_ch;
-
- if (sst_drv_ctx->info.lpe_viewpt_rqd)
- alloc_param.ts = sst_drv_ctx->info.mailbox_start +
- sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp));
- else
- alloc_param.ts = sst_drv_ctx->mailbox_add +
- sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp));
-
- dev_dbg(sst_drv_ctx->dev, "alloc tstamp location = 0x%x\n",
- alloc_param.ts);
- dev_dbg(sst_drv_ctx->dev, "assigned pipe id 0x%x to task %d\n",
- pipe_id, task_id);
-
- /* allocate device type context */
- sst_init_stream(&sst_drv_ctx->streams[str_id], alloc_param.codec_type,
- str_id, alloc_param.operation, 0);
-
- dev_info(sst_drv_ctx->dev, "Alloc for str %d pipe %#x\n",
- str_id, pipe_id);
- ret = sst_prepare_and_post_msg(sst_drv_ctx, task_id, IPC_CMD,
- IPC_IA_ALLOC_STREAM_MRFLD, pipe_id, sizeof(alloc_param),
- &alloc_param, data, true, true, false, true);
-
- if (ret < 0) {
- dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);
- /* alloc failed, so reset the state to uninit */
- str_info->status = STREAM_UN_INIT;
- str_id = ret;
- } else if (data) {
- response = (struct snd_sst_alloc_response *)data;
- ret = response->str_type.result;
- if (!ret)
- goto out;
- dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);
- if (ret == SST_ERR_STREAM_IN_USE) {
- dev_err(sst_drv_ctx->dev,
- "FW not in clean state, send free for:%d\n", str_id);
- sst_free_stream(sst_drv_ctx, str_id);
- }
- str_id = -ret;
- }
-out:
- kfree(data);
- return str_id;
-}
-
-/**
-* sst_start_stream - Send msg for a starting stream
-* @str_id: stream ID
-*
-* This function is called by any function which wants to start
-* a stream.
-*/
-int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
-{
- int retval = 0;
- struct stream_info *str_info;
- u16 data = 0;
-
- dev_dbg(sst_drv_ctx->dev, "sst_start_stream for %d\n", str_id);
- str_info = get_stream_info(sst_drv_ctx, str_id);
- if (!str_info)
- return -EINVAL;
- if (str_info->status != STREAM_RUNNING)
- return -EBADRQC;
-
- retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
- IPC_CMD, IPC_IA_START_STREAM_MRFLD, str_info->pipe_id,
- sizeof(u16), &data, NULL, true, true, true, false);
-
- return retval;
-}
-
-int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx,
- struct snd_sst_bytes_v2 *bytes)
-{ struct ipc_post *msg = NULL;
- u32 length;
- int pvt_id, ret = 0;
- struct sst_block *block = NULL;
-
- dev_dbg(sst_drv_ctx->dev,
- "type:%u ipc_msg:%u block:%u task_id:%u pipe: %#x length:%#x\n",
- bytes->type, bytes->ipc_msg, bytes->block, bytes->task_id,
- bytes->pipe_id, bytes->len);
-
- if (sst_create_ipc_msg(&msg, true))
- return -ENOMEM;
-
- pvt_id = sst_assign_pvt_id(sst_drv_ctx);
- sst_fill_header_mrfld(&msg->mrfld_header, bytes->ipc_msg,
- bytes->task_id, 1, pvt_id);
- msg->mrfld_header.p.header_high.part.res_rqd = bytes->block;
- length = bytes->len;
- msg->mrfld_header.p.header_low_payload = length;
- dev_dbg(sst_drv_ctx->dev, "length is %d\n", length);
- memcpy(msg->mailbox_data, &bytes->bytes, bytes->len);
- if (bytes->block) {
- block = sst_create_block(sst_drv_ctx, bytes->ipc_msg, pvt_id);
- if (block == NULL) {
- kfree(msg);
- ret = -ENOMEM;
- goto out;
- }
- }
-
- sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg);
- dev_dbg(sst_drv_ctx->dev, "msg->mrfld_header.p.header_low_payload:%d",
- msg->mrfld_header.p.header_low_payload);
-
- if (bytes->block) {
- ret = sst_wait_timeout(sst_drv_ctx, block);
- if (ret) {
- dev_err(sst_drv_ctx->dev, "fw returned err %d\n", ret);
- sst_free_block(sst_drv_ctx, block);
- goto out;
- }
- }
- if (bytes->type == SND_SST_BYTES_GET) {
- /*
- * copy the reply and send back
- * we need to update only sz and payload
- */
- if (bytes->block) {
- unsigned char *r = block->data;
-
- dev_dbg(sst_drv_ctx->dev, "read back %d bytes",
- bytes->len);
- memcpy(bytes->bytes, r, bytes->len);
- }
- }
- if (bytes->block)
- sst_free_block(sst_drv_ctx, block);
-out:
- test_and_clear_bit(pvt_id, &sst_drv_ctx->pvt_id);
- return 0;
-}
-
-/*
- * sst_pause_stream - Send msg for a pausing stream
- * @str_id: stream ID
- *
- * This function is called by any function which wants to pause
- * an already running stream.
- */
-int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
-{
- int retval = 0;
- struct stream_info *str_info;
-
- dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_pause_stream for %d\n", str_id);
- str_info = get_stream_info(sst_drv_ctx, str_id);
- if (!str_info)
- return -EINVAL;
- if (str_info->status == STREAM_PAUSED)
- return 0;
- if (str_info->status == STREAM_RUNNING ||
- str_info->status == STREAM_INIT) {
- if (str_info->prev == STREAM_UN_INIT)
- return -EBADRQC;
-
- retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
- IPC_IA_PAUSE_STREAM_MRFLD, str_info->pipe_id,
- 0, NULL, NULL, true, true, false, true);
-
- if (retval == 0) {
- str_info->prev = str_info->status;
- str_info->status = STREAM_PAUSED;
- } else if (retval == SST_ERR_INVALID_STREAM_ID) {
- retval = -EINVAL;
- mutex_lock(&sst_drv_ctx->sst_lock);
- sst_clean_stream(str_info);
- mutex_unlock(&sst_drv_ctx->sst_lock);
- }
- } else {
- retval = -EBADRQC;
- dev_dbg(sst_drv_ctx->dev, "SST DBG:BADRQC for stream\n ");
- }
-
- return retval;
-}
-
-/**
- * sst_resume_stream - Send msg for resuming stream
- * @str_id: stream ID
- *
- * This function is called by any function which wants to resume
- * an already paused stream.
- */
-int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
-{
- int retval = 0;
- struct stream_info *str_info;
-
- dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_resume_stream for %d\n", str_id);
- str_info = get_stream_info(sst_drv_ctx, str_id);
- if (!str_info)
- return -EINVAL;
- if (str_info->status == STREAM_RUNNING)
- return 0;
- if (str_info->status == STREAM_PAUSED) {
- retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
- IPC_CMD, IPC_IA_RESUME_STREAM_MRFLD,
- str_info->pipe_id, 0, NULL, NULL,
- true, true, false, true);
-
- if (!retval) {
- if (str_info->prev == STREAM_RUNNING)
- str_info->status = STREAM_RUNNING;
- else
- str_info->status = STREAM_INIT;
- str_info->prev = STREAM_PAUSED;
- } else if (retval == -SST_ERR_INVALID_STREAM_ID) {
- retval = -EINVAL;
- mutex_lock(&sst_drv_ctx->sst_lock);
- sst_clean_stream(str_info);
- mutex_unlock(&sst_drv_ctx->sst_lock);
- }
- } else {
- retval = -EBADRQC;
- dev_err(sst_drv_ctx->dev, "SST ERR: BADQRC for stream\n");
- }
-
- return retval;
-}
-
-
-/**
- * sst_drop_stream - Send msg for stopping stream
- * @str_id: stream ID
- *
- * This function is called by any function which wants to stop
- * a stream.
- */
-int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
-{
- int retval = 0;
- struct stream_info *str_info;
-
- dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_drop_stream for %d\n", str_id);
- str_info = get_stream_info(sst_drv_ctx, str_id);
- if (!str_info)
- return -EINVAL;
-
- if (str_info->status != STREAM_UN_INIT) {
- str_info->prev = STREAM_UN_INIT;
- str_info->status = STREAM_INIT;
- str_info->cumm_bytes = 0;
- retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
- IPC_CMD, IPC_IA_DROP_STREAM_MRFLD,
- str_info->pipe_id, 0, NULL, NULL,
- true, true, true, false);
- } else {
- retval = -EBADRQC;
- dev_dbg(sst_drv_ctx->dev, "BADQRC for stream, state %x\n",
- str_info->status);
- }
- return retval;
-}
-
-/**
-* sst_drain_stream - Send msg for draining stream
-* @str_id: stream ID
-*
-* This function is called by any function which wants to drain
-* a stream.
-*/
-int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx,
- int str_id, bool partial_drain)
-{
- int retval = 0;
- struct stream_info *str_info;
-
- dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_drain_stream for %d\n", str_id);
- str_info = get_stream_info(sst_drv_ctx, str_id);
- if (!str_info)
- return -EINVAL;
- if (str_info->status != STREAM_RUNNING &&
- str_info->status != STREAM_INIT &&
- str_info->status != STREAM_PAUSED) {
- dev_err(sst_drv_ctx->dev, "SST ERR: BADQRC for stream = %d\n",
- str_info->status);
- return -EBADRQC;
- }
-
- retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
- IPC_IA_DRAIN_STREAM_MRFLD, str_info->pipe_id,
- sizeof(u8), &partial_drain, NULL, true, true, false, false);
- /*
- * with new non blocked drain implementation in core we dont need to
- * wait for respsonse, and need to only invoke callback for drain
- * complete
- */
-
- return retval;
-}
-
-/**
- * sst_free_stream - Frees a stream
- * @str_id: stream ID
- *
- * This function is called by any function which wants to free
- * a stream.
- */
-int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
-{
- int retval = 0;
- struct stream_info *str_info;
- struct intel_sst_ops *ops;
-
- dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_free_stream for %d\n", str_id);
-
- mutex_lock(&sst_drv_ctx->sst_lock);
- if (sst_drv_ctx->sst_state == SST_RESET) {
- mutex_unlock(&sst_drv_ctx->sst_lock);
- return -ENODEV;
- }
- mutex_unlock(&sst_drv_ctx->sst_lock);
- str_info = get_stream_info(sst_drv_ctx, str_id);
- if (!str_info)
- return -EINVAL;
- ops = sst_drv_ctx->ops;
-
- mutex_lock(&str_info->lock);
- if (str_info->status != STREAM_UN_INIT) {
- str_info->prev = str_info->status;
- str_info->status = STREAM_UN_INIT;
- mutex_unlock(&str_info->lock);
-
- dev_info(sst_drv_ctx->dev, "Free for str %d pipe %#x\n",
- str_id, str_info->pipe_id);
- retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
- IPC_IA_FREE_STREAM_MRFLD, str_info->pipe_id, 0,
- NULL, NULL, true, true, false, true);
-
- dev_dbg(sst_drv_ctx->dev, "sst: wait for free returned %d\n",
- retval);
- mutex_lock(&sst_drv_ctx->sst_lock);
- sst_clean_stream(str_info);
- mutex_unlock(&sst_drv_ctx->sst_lock);
- dev_dbg(sst_drv_ctx->dev, "SST DBG:Stream freed\n");
- } else {
- mutex_unlock(&str_info->lock);
- retval = -EBADRQC;
- dev_dbg(sst_drv_ctx->dev, "SST DBG:BADQRC for stream\n");
- }
-
- return retval;
-}
if (PTR_ERR(priv->extclk) == -EPROBE_DEFER)
return -EPROBE_DEFER;
} else {
- if (priv->extclk == priv->clk) {
+ if (clk_is_match(priv->extclk, priv->clk)) {
devm_clk_put(&pdev->dev, priv->extclk);
priv->extclk = ERR_PTR(-EINVAL);
} else {
config SND_OMAP_SOC_OMAP_ABE_TWL6040
tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec"
- depends on TWL6040_CORE && SND_OMAP_SOC && (ARCH_OMAP4 || COMPILE_TEST)
+ depends on TWL6040_CORE && SND_OMAP_SOC && (ARCH_OMAP4 || SOC_OMAP5 || COMPILE_TEST)
select SND_OMAP_SOC_DMIC
select SND_OMAP_SOC_MCPDM
select SND_SOC_TWL6040
select SND_SOC_DMIC
+ select COMMON_CLK_PALMAS if SOC_OMAP5
help
Say Y if you want to add support for SoC audio on OMAP boards using
ABE and twl6040 codec. This driver currently supports:
- SDP4430/Blaze boards
- PandaBoard (4430)
- PandaBoardES (4460)
+ - omap5-uevm (5432)
config SND_OMAP_SOC_OMAP3_PANDORA
tristate "SoC Audio support for OMAP3 Pandora"
/* Add hook switch - can be used to control the codec from userspace
* even if line discipline fails */
- ret = snd_soc_jack_new(rtd->codec, "hook_switch",
- SND_JACK_HEADSET, &ams_delta_hook_switch);
+ ret = snd_soc_card_jack_new(card, "hook_switch", SND_JACK_HEADSET,
+ &ams_delta_hook_switch, NULL, 0);
if (ret)
dev_warn(card->dev,
"Failed to allocate resources for hook switch, "
/* Headset jack detection only if it is supported */
if (priv->jack_detection) {
- ret = snd_soc_jack_new(codec, "Headset Jack",
- SND_JACK_HEADSET, &hs_jack);
+ ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET, &hs_jack,
+ hs_jack_pins,
+ ARRAY_SIZE(hs_jack_pins));
if (ret)
return ret;
- ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins),
- hs_jack_pins);
twl6040_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADSET);
}
- return ret;
+ return 0;
}
static const struct snd_soc_dapm_route dmic_audio_map[] = {
return ret;
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
card->name = devm_kasprintf(dev, GFP_KERNEL,
"HDMI %s", dev_name(ad->dssdev));
card->owner = THIS_MODULE;
case OMAP_MCBSP_SYSCLK_CLKX_EXT:
regs->srgr2 |= CLKSM;
+ regs->pcr0 |= SCLKME;
+ /*
+ * If McBSP is master but yet the CLKX/CLKR pin drives the SRG,
+ * disable output on those pins. This enables to inject the
+ * reference clock through CLKX/CLKR. For this to work
+ * set_dai_sysclk() _needs_ to be called after set_dai_fmt().
+ */
+ regs->pcr0 &= ~CLKXM;
+ break;
case OMAP_MCBSP_SYSCLK_CLKR_EXT:
regs->pcr0 |= SCLKME;
+ /* Disable ouput on CLKR pin in master mode */
+ regs->pcr0 &= ~CLKRM;
break;
default:
err = -ENODEV;
#define pcm_omap1510() 0
#endif
-static const struct snd_pcm_hardware omap_pcm_hardware = {
+static struct snd_pcm_hardware omap_pcm_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
.buffer_bytes_max = 128 * 1024,
};
+/* sDMA supports only 1, 2, and 4 byte transfer elements. */
+static void omap_pcm_limit_supported_formats(void)
+{
+ int i;
+
+ for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) {
+ switch (snd_pcm_format_physical_width(i)) {
+ case 8:
+ case 16:
+ case 32:
+ omap_pcm_hardware.formats |= (1LL << i);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
/* this may get called several times by oss emulation */
static int omap_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
struct snd_pcm *pcm = rtd->pcm;
int ret;
- ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(64));
+ ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
if (ret)
return ret;
int omap_pcm_platform_register(struct device *dev)
{
+ omap_pcm_limit_supported_formats();
return devm_snd_soc_register_platform(dev, &omap_soc_platform);
}
EXPORT_SYMBOL_GPL(omap_pcm_platform_register);
if (priv->jack_detect > 0) {
hs_jack_gpios[0].gpio = priv->jack_detect;
- ret = snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET,
- &priv->hs_jack);
- if (ret)
- return ret;
-
- ret = snd_soc_jack_add_pins(&priv->hs_jack,
- ARRAY_SIZE(hs_jack_pins),
- hs_jack_pins);
+ ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET, &priv->hs_jack,
+ hs_jack_pins,
+ ARRAY_SIZE(hs_jack_pins));
if (ret)
return ret;
}
/* AV jack detection */
- err = snd_soc_jack_new(codec, "AV Jack",
- SND_JACK_HEADSET | SND_JACK_VIDEOOUT,
- &rx51_av_jack);
+ err = snd_soc_card_jack_new(rtd->card, "AV Jack",
+ SND_JACK_HEADSET | SND_JACK_VIDEOOUT,
+ &rx51_av_jack, NULL, 0);
if (err) {
dev_err(card->dev, "Failed to add AV Jack\n");
return err;
*/
static int hx4700_ak4641_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_codec *codec = rtd->codec;
int err;
/* Jack detection API stuff */
- err = snd_soc_jack_new(codec, "Headphone Jack",
- SND_JACK_HEADPHONE, &hs_jack);
- if (err)
- return err;
-
- err = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pin),
- hs_jack_pin);
+ err = snd_soc_card_jack_new(rtd->card, "Headphone Jack",
+ SND_JACK_HEADPHONE, &hs_jack, hs_jack_pin,
+ ARRAY_SIZE(hs_jack_pin));
if (err)
return err;
static int palm27x_ac97_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_codec *codec = rtd->codec;
int err;
/* Jack detection API stuff */
- err = snd_soc_jack_new(codec, "Headphone Jack",
- SND_JACK_HEADPHONE, &hs_jack);
- if (err)
- return err;
-
- err = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins),
- hs_jack_pins);
+ err = snd_soc_card_jack_new(rtd->card, "Headphone Jack",
+ SND_JACK_HEADPHONE, &hs_jack, hs_jack_pins,
+ ARRAY_SIZE(hs_jack_pins));
if (err)
return err;
struct snd_soc_codec *codec = rtd->codec;
/* Headset jack detection */
- snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE
- | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2,
- &hs_jack);
- snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins),
- hs_jack_pins);
- snd_soc_jack_new(codec, "Microphone Jack", SND_JACK_MICROPHONE,
- &mic_jack);
- snd_soc_jack_add_pins(&mic_jack, ARRAY_SIZE(mic_jack_pins),
- mic_jack_pins);
+ snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2,
+ &hs_jack, hs_jack_pins, ARRAY_SIZE(hs_jack_pins));
+ snd_soc_card_jack_new(rtd->card, "Microphone Jack", SND_JACK_MICROPHONE,
+ &mic_jack, mic_jack_pins,
+ ARRAY_SIZE(mic_jack_pins));
/* headphone, microphone detection & headset short detection */
pm860x_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADPHONE,
snd_soc_dapm_disable_pin(dapm, "MONO1");
/* Jack detection API stuff */
- ret = snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET,
- &hs_jack);
- if (ret)
- goto err;
-
- ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins),
- hs_jack_pins);
+ ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET,
+ &hs_jack, hs_jack_pins,
+ ARRAY_SIZE(hs_jack_pins));
if (ret)
goto err;
config SND_SOC_SPEYSIDE
tristate "Audio support for Wolfson Speyside"
- depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410
+ depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 && I2C && SPI_MASTER
select SND_SAMSUNG_I2S
select SND_SOC_WM8996
select SND_SOC_WM9081
config SND_SOC_BELLS
tristate "Audio support for Wolfson Bells"
- depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 && MFD_ARIZONA
+ depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 && MFD_ARIZONA && I2C && SPI_MASTER
select SND_SAMSUNG_I2S
select SND_SOC_WM5102
select SND_SOC_WM5110
config SND_SOC_LITTLEMILL
tristate "Audio support for Wolfson Littlemill"
- depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410
+ depends on SND_SOC_SAMSUNG && MACH_WLF_CRAGG_6410 && I2C
select SND_SAMSUNG_I2S
select MFD_WM8994
select SND_SOC_WM8994
config SND_SOC_ODROIDX2
tristate "Audio support for Odroid-X2 and Odroid-U3"
- depends on SND_SOC_SAMSUNG
+ depends on SND_SOC_SAMSUNG && I2C
select SND_SOC_MAX98090
select SND_SAMSUNG_I2S
help
config SND_SOC_ARNDALE_RT5631_ALC5631
tristate "Audio support for RT5631(ALC5631) on Arndale Board"
- depends on SND_SOC_SAMSUNG
+ depends on SND_SOC_SAMSUNG && I2C
select SND_SAMSUNG_I2S
select SND_SOC_RT5631
static int h1940_uda1380_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_codec *codec = rtd->codec;
-
- snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
- &hp_jack);
-
- snd_soc_jack_add_pins(&hp_jack, ARRAY_SIZE(hp_jack_pins),
- hp_jack_pins);
+ snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE,
+ &hp_jack, hp_jack_pins, ARRAY_SIZE(hp_jack_pins));
snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
hp_jack_gpios);
if (ret < 0)
return ret;
- ret = snd_soc_jack_new(codec, "Headset",
- SND_JACK_HEADSET | SND_JACK_MECHANICAL |
- SND_JACK_BTN_0 | SND_JACK_BTN_1 |
- SND_JACK_BTN_2 | SND_JACK_BTN_3 |
- SND_JACK_BTN_4 | SND_JACK_BTN_5,
- &littlemill_headset);
+ ret = snd_soc_card_jack_new(card, "Headset",
+ SND_JACK_HEADSET | SND_JACK_MECHANICAL |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 |
+ SND_JACK_BTN_4 | SND_JACK_BTN_5,
+ &littlemill_headset, NULL, 0);
if (ret)
return ret;
return ret;
}
- ret = snd_soc_jack_new(codec, "Headset",
- SND_JACK_LINEOUT | SND_JACK_HEADSET |
- SND_JACK_BTN_0,
- &lowland_headset);
- if (ret)
- return ret;
-
- ret = snd_soc_jack_add_pins(&lowland_headset,
- ARRAY_SIZE(lowland_headset_pins),
- lowland_headset_pins);
+ ret = snd_soc_card_jack_new(rtd->card, "Headset", SND_JACK_LINEOUT |
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &lowland_headset, lowland_headset_pins,
+ ARRAY_SIZE(lowland_headset_pins));
if (ret)
return ret;
static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_codec *codec = rtd->codec;
-
- snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
- &hp_jack);
-
- snd_soc_jack_add_pins(&hp_jack, ARRAY_SIZE(hp_jack_pins),
- hp_jack_pins);
+ snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE,
+ &hp_jack, hp_jack_pins, ARRAY_SIZE(hp_jack_pins));
snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios),
hp_jack_gpios);
snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
/* Headphone jack detection */
- err = snd_soc_jack_new(codec, "Headphone Jack",
- SND_JACK_HEADPHONE, &smartq_jack);
- if (err)
- return err;
-
- err = snd_soc_jack_add_pins(&smartq_jack, ARRAY_SIZE(smartq_jack_pins),
- smartq_jack_pins);
+ err = snd_soc_card_jack_new(rtd->card, "Headphone Jack",
+ SND_JACK_HEADPHONE, &smartq_jack,
+ smartq_jack_pins,
+ ARRAY_SIZE(smartq_jack_pins));
if (err)
return err;
pr_err("Failed to request HP_SEL GPIO: %d\n", ret);
gpio_direction_output(WM8996_HPSEL_GPIO, speyside_jack_polarity);
- ret = snd_soc_jack_new(codec, "Headset",
- SND_JACK_LINEOUT | SND_JACK_HEADSET |
- SND_JACK_BTN_0,
- &speyside_headset);
- if (ret)
- return ret;
-
- ret = snd_soc_jack_add_pins(&speyside_headset,
- ARRAY_SIZE(speyside_headset_pins),
- speyside_headset_pins);
+ ret = snd_soc_card_jack_new(rtd->card, "Headset", SND_JACK_LINEOUT |
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &speyside_headset, speyside_headset_pins,
+ ARRAY_SIZE(speyside_headset_pins));
if (ret)
return ret;
if (ret < 0)
return ret;
- ret = snd_soc_jack_new(codec, "Headset",
- SND_JACK_HEADSET | SND_JACK_BTN_0,
- &tobermory_headset);
- if (ret)
- return ret;
-
- ret = snd_soc_jack_add_pins(&tobermory_headset,
- ARRAY_SIZE(tobermory_headset_pins),
- tobermory_headset_pins);
+ ret = snd_soc_card_jack_new(card, "Headset", SND_JACK_HEADSET |
+ SND_JACK_BTN_0, &tobermory_headset,
+ tobermory_headset_pins,
+ ARRAY_SIZE(tobermory_headset_pins));
if (ret)
return ret;
return mod->ops->dma_name(mod);
}
-void rsnd_mod_init(struct rsnd_mod *mod,
+int rsnd_mod_init(struct rsnd_mod *mod,
struct rsnd_mod_ops *ops,
struct clk *clk,
enum rsnd_mod_type type,
int id)
{
+ int ret = clk_prepare(clk);
+
+ if (ret)
+ return ret;
+
mod->id = id;
mod->ops = ops;
mod->type = type;
mod->clk = clk;
+
+ return ret;
+}
+
+void rsnd_mod_quit(struct rsnd_mod *mod)
+{
+ if (mod->clk)
+ clk_unprepare(mod->clk);
}
/*
goto exit_snd_probe;
}
+ dev_set_drvdata(dev, priv);
+
/*
* asoc register
*/
goto exit_snd_soc;
}
- dev_set_drvdata(dev, priv);
-
pm_runtime_enable(dev);
dev_info(dev, "probed\n");
{
struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev);
struct rsnd_dai *rdai;
+ void (*remove_func[])(struct platform_device *pdev,
+ struct rsnd_priv *priv) = {
+ rsnd_ssi_remove,
+ rsnd_src_remove,
+ rsnd_dvc_remove,
+ };
int ret = 0, i;
pm_runtime_disable(&pdev->dev);
ret |= rsnd_dai_call(remove, &rdai->capture, priv);
}
+ for (i = 0; i < ARRAY_SIZE(remove_func); i++)
+ remove_func[i](pdev, priv);
+
snd_soc_unregister_component(&pdev->dev);
snd_soc_unregister_platform(&pdev->dev);
struct rsnd_dvc *dvc;
struct clk *clk;
char name[RSND_DVC_NAME_SIZE];
- int i, nr;
+ int i, nr, ret;
rsnd_of_parse_dvc(pdev, of_data, priv);
dvc->info = &info->dvc_info[i];
- rsnd_mod_init(&dvc->mod, &rsnd_dvc_ops,
+ ret = rsnd_mod_init(&dvc->mod, &rsnd_dvc_ops,
clk, RSND_MOD_DVC, i);
+ if (ret)
+ return ret;
dev_dbg(dev, "CMD%d probed\n", i);
}
return 0;
}
+
+void rsnd_dvc_remove(struct platform_device *pdev,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_dvc *dvc;
+ int i;
+
+ for_each_rsnd_dvc(dvc, priv, i) {
+ rsnd_mod_quit(&dvc->mod);
+ }
+}
#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma)
#define rsnd_mod_to_io(mod) ((mod)->io)
#define rsnd_mod_id(mod) ((mod)->id)
-#define rsnd_mod_hw_start(mod) clk_prepare_enable((mod)->clk)
-#define rsnd_mod_hw_stop(mod) clk_disable_unprepare((mod)->clk)
+#define rsnd_mod_hw_start(mod) clk_enable((mod)->clk)
+#define rsnd_mod_hw_stop(mod) clk_disable((mod)->clk)
-void rsnd_mod_init(struct rsnd_mod *mod,
+int rsnd_mod_init(struct rsnd_mod *mod,
struct rsnd_mod_ops *ops,
struct clk *clk,
enum rsnd_mod_type type,
int id);
+void rsnd_mod_quit(struct rsnd_mod *mod);
char *rsnd_mod_name(struct rsnd_mod *mod);
char *rsnd_mod_dma_name(struct rsnd_mod *mod);
int rsnd_src_probe(struct platform_device *pdev,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv);
+void rsnd_src_remove(struct platform_device *pdev,
+ struct rsnd_priv *priv);
struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id);
unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
struct rsnd_dai_stream *io,
int rsnd_ssi_probe(struct platform_device *pdev,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv);
+void rsnd_ssi_remove(struct platform_device *pdev,
+ struct rsnd_priv *priv);
struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
struct rsnd_mod_ops *ops;
struct clk *clk;
char name[RSND_SRC_NAME_SIZE];
- int i, nr;
+ int i, nr, ret;
ops = NULL;
if (rsnd_is_gen1(priv))
src->info = &info->src_info[i];
- rsnd_mod_init(&src->mod, ops, clk, RSND_MOD_SRC, i);
+ ret = rsnd_mod_init(&src->mod, ops, clk, RSND_MOD_SRC, i);
+ if (ret)
+ return ret;
dev_dbg(dev, "SRC%d probed\n", i);
}
return 0;
}
+
+void rsnd_src_remove(struct platform_device *pdev,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_src *src;
+ int i;
+
+ for_each_rsnd_src(src, priv, i) {
+ rsnd_mod_quit(&src->mod);
+ }
+}
struct clk *clk;
struct rsnd_ssi *ssi;
char name[RSND_SSI_NAME_SIZE];
- int i, nr;
+ int i, nr, ret;
rsnd_of_parse_ssi(pdev, of_data, priv);
else if (rsnd_ssi_pio_available(ssi))
ops = &rsnd_ssi_pio_ops;
- rsnd_mod_init(&ssi->mod, ops, clk, RSND_MOD_SSI, i);
+ ret = rsnd_mod_init(&ssi->mod, ops, clk, RSND_MOD_SSI, i);
+ if (ret)
+ return ret;
rsnd_ssi_parent_clk_setup(priv, ssi);
}
return 0;
}
+
+void rsnd_ssi_remove(struct platform_device *pdev,
+ struct rsnd_priv *priv)
+{
+ struct rsnd_ssi *ssi;
+ int i;
+
+ for_each_rsnd_ssi(ssi, priv, i) {
+ rsnd_mod_quit(&ssi->mod);
+ }
+}
static void soc_init_component_debugfs(struct snd_soc_component *component)
{
+ if (!component->card->debugfs_card_root)
+ return;
+
if (component->debugfs_prefix) {
char *name;
if (!buf)
return -ENOMEM;
+ mutex_lock(&client_mutex);
+
list_for_each_entry(codec, &codec_list, list) {
len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n",
codec->component.name);
}
}
+ mutex_unlock(&client_mutex);
+
if (ret >= 0)
ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
if (!buf)
return -ENOMEM;
+ mutex_lock(&client_mutex);
+
list_for_each_entry(component, &component_list, list) {
list_for_each_entry(dai, &component->dai_list, list) {
len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n",
}
}
+ mutex_unlock(&client_mutex);
+
ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
kfree(buf);
if (!buf)
return -ENOMEM;
+ mutex_lock(&client_mutex);
+
list_for_each_entry(platform, &platform_list, list) {
len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n",
platform->component.name);
}
}
+ mutex_unlock(&client_mutex);
+
ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
kfree(buf);
static void soc_init_card_debugfs(struct snd_soc_card *card)
{
+ if (!snd_soc_debugfs_root)
+ return;
+
card->debugfs_card_root = debugfs_create_dir(card->name,
snd_soc_debugfs_root);
if (!card->debugfs_card_root) {
debugfs_remove_recursive(card->debugfs_card_root);
}
+
+static void snd_soc_debugfs_init(void)
+{
+ snd_soc_debugfs_root = debugfs_create_dir("asoc", NULL);
+ if (IS_ERR(snd_soc_debugfs_root) || !snd_soc_debugfs_root) {
+ pr_warn("ASoC: Failed to create debugfs directory\n");
+ snd_soc_debugfs_root = NULL;
+ return;
+ }
+
+ if (!debugfs_create_file("codecs", 0444, snd_soc_debugfs_root, NULL,
+ &codec_list_fops))
+ pr_warn("ASoC: Failed to create CODEC list debugfs file\n");
+
+ if (!debugfs_create_file("dais", 0444, snd_soc_debugfs_root, NULL,
+ &dai_list_fops))
+ pr_warn("ASoC: Failed to create DAI list debugfs file\n");
+
+ if (!debugfs_create_file("platforms", 0444, snd_soc_debugfs_root, NULL,
+ &platform_list_fops))
+ pr_warn("ASoC: Failed to create platform list debugfs file\n");
+}
+
+static void snd_soc_debugfs_exit(void)
+{
+ debugfs_remove_recursive(snd_soc_debugfs_root);
+}
+
#else
#define soc_init_codec_debugfs NULL
static inline void soc_cleanup_card_debugfs(struct snd_soc_card *card)
{
}
+
+static inline void snd_soc_debugfs_init(void)
+{
+}
+
+static inline void snd_soc_debugfs_exit(void)
+{
+}
+
#endif
struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card,
cpu_dai->driver->suspend(cpu_dai);
}
- /* close any waiting streams and save state */
- for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_dai **codec_dais = card->rtd[i].codec_dais;
+ /* close any waiting streams */
+ for (i = 0; i < card->num_rtd; i++)
flush_delayed_work(&card->rtd[i].delayed_work);
- for (j = 0; j < card->rtd[i].num_codecs; j++) {
- codec_dais[j]->codec->dapm.suspend_bias_level =
- codec_dais[j]->codec->dapm.bias_level;
- }
- }
for (i = 0; i < card->num_rtd; i++) {
{
struct snd_soc_component *component;
+ lockdep_assert_held(&client_mutex);
+
list_for_each_entry(component, &component_list, list) {
if (of_node) {
if (component->dev->of_node == of_node)
struct snd_soc_component *component;
struct snd_soc_dai *dai;
+ lockdep_assert_held(&client_mutex);
+
/* Find CPU DAI from registered DAIs*/
list_for_each_entry(component, &component_list, list) {
if (dlc->of_node && component->dev->of_node != dlc->of_node)
}
}
+ if (dai_link->dai_fmt)
+ snd_soc_runtime_set_dai_fmt(rtd, dai_link->dai_fmt);
+
ret = soc_post_component_init(rtd, dai_link->name);
if (ret)
return ret;
#ifdef CONFIG_DEBUG_FS
/* add DPCM sysfs entries */
- if (dai_link->dynamic) {
- ret = soc_dpcm_debugfs_add(rtd);
- if (ret < 0) {
- dev_err(rtd->dev,
- "ASoC: failed to add dpcm sysfs entries: %d\n",
- ret);
- return ret;
- }
- }
+ if (dai_link->dynamic)
+ soc_dpcm_debugfs_add(rtd);
#endif
if (cpu_dai->driver->compress_dai) {
/* unregister the rtd device */
if (rtd->dev_registered) {
- device_remove_file(rtd->dev, &dev_attr_codec_reg);
device_unregister(rtd->dev);
rtd->dev_registered = 0;
}
struct snd_soc_codec *codec;
int ret, i, order;
+ mutex_lock(&client_mutex);
mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_INIT);
/* bind DAIs */
goto base_error;
}
+ soc_init_card_debugfs(card);
+
card->dapm.bias_level = SND_SOC_BIAS_OFF;
card->dapm.dev = card->dev;
card->dapm.card = card;
snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,
card->num_dapm_widgets);
+ if (card->of_dapm_widgets)
+ snd_soc_dapm_new_controls(&card->dapm, card->of_dapm_widgets,
+ card->num_of_dapm_widgets);
+
/* initialise the sound card only once */
if (card->probe) {
ret = card->probe(card);
snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,
card->num_dapm_routes);
- for (i = 0; i < card->num_links; i++) {
- if (card->dai_link[i].dai_fmt)
- snd_soc_runtime_set_dai_fmt(&card->rtd[i],
- card->dai_link[i].dai_fmt);
- }
+ if (card->of_dapm_routes)
+ snd_soc_dapm_add_routes(&card->dapm, card->of_dapm_routes,
+ card->num_of_dapm_routes);
snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname),
"%s", card->name);
card->instantiated = 1;
snd_soc_dapm_sync(&card->dapm);
mutex_unlock(&card->mutex);
+ mutex_unlock(&client_mutex);
return 0;
if (card->remove)
card->remove(card);
+ soc_cleanup_card_debugfs(card);
snd_card_free(card->snd_card);
base_error:
mutex_unlock(&card->mutex);
+ mutex_unlock(&client_mutex);
return ret;
}
snd_soc_initialize_card_lists(card);
- soc_init_card_debugfs(card);
-
card->rtd = devm_kzalloc(card->dev,
sizeof(struct snd_soc_pcm_runtime) *
(card->num_links + card->num_aux_devs),
ret = snd_soc_instantiate_card(card);
if (ret != 0)
- soc_cleanup_card_debugfs(card);
+ return ret;
/* deactivate pins to sleep state */
for (i = 0; i < card->num_rtd; i++) {
list_del(&component->list);
}
-static void snd_soc_component_del(struct snd_soc_component *component)
-{
- mutex_lock(&client_mutex);
- snd_soc_component_del_unlocked(component);
- mutex_unlock(&client_mutex);
-}
-
int snd_soc_register_component(struct device *dev,
const struct snd_soc_component_driver *cmpnt_drv,
struct snd_soc_dai_driver *dai_drv,
{
struct snd_soc_component *cmpnt;
+ mutex_lock(&client_mutex);
list_for_each_entry(cmpnt, &component_list, list) {
if (dev == cmpnt->dev && cmpnt->registered_as_component)
goto found;
}
+ mutex_unlock(&client_mutex);
return;
found:
- snd_soc_component_del(cmpnt);
+ snd_soc_component_del_unlocked(cmpnt);
+ mutex_unlock(&client_mutex);
snd_soc_component_cleanup(cmpnt);
kfree(cmpnt);
}
{
struct snd_soc_platform *platform;
+ mutex_lock(&client_mutex);
list_for_each_entry(platform, &platform_list, list) {
- if (dev == platform->dev)
+ if (dev == platform->dev) {
+ mutex_unlock(&client_mutex);
return platform;
+ }
}
+ mutex_unlock(&client_mutex);
return NULL;
}
{
struct snd_soc_codec *codec;
+ mutex_lock(&client_mutex);
list_for_each_entry(codec, &codec_list, list) {
if (dev == codec->dev)
goto found;
}
+ mutex_unlock(&client_mutex);
return;
found:
-
- mutex_lock(&client_mutex);
list_del(&codec->list);
snd_soc_component_del_unlocked(&codec->component);
mutex_unlock(&client_mutex);
widgets[i].name = wname;
}
- card->dapm_widgets = widgets;
- card->num_dapm_widgets = num_widgets;
+ card->of_dapm_widgets = widgets;
+ card->num_of_dapm_widgets = num_widgets;
return 0;
}
}
}
- card->num_dapm_routes = num_routes;
- card->dapm_routes = routes;
+ card->num_of_dapm_routes = num_routes;
+ card->of_dapm_routes = routes;
return 0;
}
static int __init snd_soc_init(void)
{
-#ifdef CONFIG_DEBUG_FS
- snd_soc_debugfs_root = debugfs_create_dir("asoc", NULL);
- if (IS_ERR(snd_soc_debugfs_root) || !snd_soc_debugfs_root) {
- pr_warn("ASoC: Failed to create debugfs directory\n");
- snd_soc_debugfs_root = NULL;
- }
-
- if (!debugfs_create_file("codecs", 0444, snd_soc_debugfs_root, NULL,
- &codec_list_fops))
- pr_warn("ASoC: Failed to create CODEC list debugfs file\n");
-
- if (!debugfs_create_file("dais", 0444, snd_soc_debugfs_root, NULL,
- &dai_list_fops))
- pr_warn("ASoC: Failed to create DAI list debugfs file\n");
-
- if (!debugfs_create_file("platforms", 0444, snd_soc_debugfs_root, NULL,
- &platform_list_fops))
- pr_warn("ASoC: Failed to create platform list debugfs file\n");
-#endif
-
+ snd_soc_debugfs_init();
snd_soc_util_init();
return platform_driver_register(&soc_driver);
static void __exit snd_soc_exit(void)
{
snd_soc_util_exit();
+ snd_soc_debugfs_exit();
#ifdef CONFIG_DEBUG_FS
- debugfs_remove_recursive(snd_soc_debugfs_root);
#endif
platform_driver_unregister(&soc_driver);
}
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_dapm);
-/**
- * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol
- * @kcontrol: The kcontrol
- */
-struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol)
-{
- return snd_soc_dapm_to_codec(snd_soc_dapm_kcontrol_dapm(kcontrol));
-}
-EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_codec);
-
static void dapm_reset(struct snd_soc_card *card)
{
struct snd_soc_dapm_widget *w;
{
struct dentry *d;
+ if (!parent)
+ return;
+
dapm->debugfs_dapm = debugfs_create_dir("dapm", parent);
if (!dapm->debugfs_dapm) {
#include <trace/events/asoc.h>
/**
- * snd_soc_jack_new - Create a new jack
- * @codec: ASoC codec
+ * snd_soc_card_jack_new - Create a new jack
+ * @card: ASoC card
* @id: an identifying string for this jack
* @type: a bitmask of enum snd_jack_type values that can be detected by
* this jack
* @jack: structure to use for the jack
+ * @pins: Array of jack pins to be added to the jack or NULL
+ * @num_pins: Number of elements in the @pins array
*
* Creates a new jack object.
*
* Returns zero if successful, or a negative error code on failure.
* On success jack will be initialised.
*/
-int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type,
- struct snd_soc_jack *jack)
+int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type,
+ struct snd_soc_jack *jack, struct snd_soc_jack_pin *pins,
+ unsigned int num_pins)
{
+ int ret;
+
mutex_init(&jack->mutex);
- jack->codec = codec;
+ jack->card = card;
INIT_LIST_HEAD(&jack->pins);
INIT_LIST_HEAD(&jack->jack_zones);
BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier);
- return snd_jack_new(codec->component.card->snd_card, id, type, &jack->jack);
+ ret = snd_jack_new(card->snd_card, id, type, &jack->jack);
+ if (ret)
+ return ret;
+
+ if (num_pins)
+ return snd_soc_jack_add_pins(jack, num_pins, pins);
+
+ return 0;
}
-EXPORT_SYMBOL_GPL(snd_soc_jack_new);
+EXPORT_SYMBOL_GPL(snd_soc_card_jack_new);
/**
* snd_soc_jack_report - Report the current status for a jack
*/
void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask)
{
- struct snd_soc_codec *codec;
struct snd_soc_dapm_context *dapm;
struct snd_soc_jack_pin *pin;
unsigned int sync = 0;
if (!jack)
return;
- codec = jack->codec;
- dapm = &codec->dapm;
+ dapm = &jack->card->dapm;
mutex_lock(&jack->mutex);
for (i = 0; i < count; i++) {
if (!pins[i].pin) {
- dev_err(jack->codec->dev, "ASoC: No name for pin %d\n",
+ dev_err(jack->card->dev, "ASoC: No name for pin %d\n",
i);
return -EINVAL;
}
if (!pins[i].mask) {
- dev_err(jack->codec->dev, "ASoC: No mask for pin %d"
+ dev_err(jack->card->dev, "ASoC: No mask for pin %d"
" (%s)\n", i, pins[i].pin);
return -EINVAL;
}
static irqreturn_t gpio_handler(int irq, void *data)
{
struct snd_soc_jack_gpio *gpio = data;
- struct device *dev = gpio->jack->codec->component.card->dev;
+ struct device *dev = gpio->jack->card->dev;
trace_snd_soc_jack_irq(gpio->name);
for (i = 0; i < count; i++) {
if (!gpios[i].name) {
- dev_err(jack->codec->dev,
+ dev_err(jack->card->dev,
"ASoC: No name for gpio at index %d\n", i);
ret = -EINVAL;
goto undo;
} else {
/* legacy GPIO number */
if (!gpio_is_valid(gpios[i].gpio)) {
- dev_err(jack->codec->dev,
+ dev_err(jack->card->dev,
"ASoC: Invalid gpio %d\n",
gpios[i].gpio);
ret = -EINVAL;
if (gpios[i].wake) {
ret = irq_set_irq_wake(gpiod_to_irq(gpios[i].desc), 1);
if (ret != 0)
- dev_err(jack->codec->dev,
+ dev_err(jack->card->dev,
"ASoC: Failed to mark GPIO at index %d as wake source: %d\n",
i, ret);
}
stream ? "<-" : "->", be->dai_link->name);
#ifdef CONFIG_DEBUG_FS
- dpcm->debugfs_state = debugfs_create_u32(be->dai_link->name, 0644,
- fe->debugfs_dpcm_root, &dpcm->state);
+ if (fe->debugfs_dpcm_root)
+ dpcm->debugfs_state = debugfs_create_u32(be->dai_link->name, 0644,
+ fe->debugfs_dpcm_root, &dpcm->state);
#endif
return 1;
}
/* DAPM dai link stream work */
INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
+ pcm->nonatomic = rtd->dai_link->nonatomic;
rtd->pcm = pcm;
pcm->private_data = rtd;
.llseek = default_llseek,
};
-int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd)
+void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd)
{
if (!rtd->dai_link)
- return 0;
+ return;
+
+ if (!rtd->card->debugfs_card_root)
+ return;
rtd->debugfs_dpcm_root = debugfs_create_dir(rtd->dai_link->name,
rtd->card->debugfs_card_root);
dev_dbg(rtd->dev,
"ASoC: Failed to create dpcm debugfs directory %s\n",
rtd->dai_link->name);
- return -EINVAL;
+ return;
}
rtd->debugfs_dpcm_state = debugfs_create_file("state", 0444,
rtd->debugfs_dpcm_root,
rtd, &dpcm_state_fops);
-
- return 0;
}
#endif
struct snd_soc_dapm_context *dapm = &codec->dapm;
struct tegra_alc5632 *machine = snd_soc_card_get_drvdata(rtd->card);
- snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET,
- &tegra_alc5632_hs_jack);
- snd_soc_jack_add_pins(&tegra_alc5632_hs_jack,
- ARRAY_SIZE(tegra_alc5632_hs_jack_pins),
- tegra_alc5632_hs_jack_pins);
+ snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET,
+ &tegra_alc5632_hs_jack,
+ tegra_alc5632_hs_jack_pins,
+ ARRAY_SIZE(tegra_alc5632_hs_jack_pins));
if (gpio_is_valid(machine->gpio_hp_det)) {
tegra_alc5632_hp_jack_gpio.gpio = machine->gpio_hp_det;
SND_SOC_DAPM_HP("Headphones", NULL),
SND_SOC_DAPM_SPK("Speakers", NULL),
SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
};
static const struct snd_kcontrol_new tegra_max98090_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphones"),
SOC_DAPM_PIN_SWITCH("Speakers"),
+ SOC_DAPM_PIN_SWITCH("Mic Jack"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
};
static int tegra_max98090_asoc_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_codec *codec = codec_dai->codec;
struct tegra_max98090 *machine = snd_soc_card_get_drvdata(rtd->card);
if (gpio_is_valid(machine->gpio_hp_det)) {
- snd_soc_jack_new(codec, "Headphones", SND_JACK_HEADPHONE,
- &tegra_max98090_hp_jack);
- snd_soc_jack_add_pins(&tegra_max98090_hp_jack,
- ARRAY_SIZE(tegra_max98090_hp_jack_pins),
- tegra_max98090_hp_jack_pins);
+ snd_soc_card_jack_new(rtd->card, "Headphones",
+ SND_JACK_HEADPHONE,
+ &tegra_max98090_hp_jack,
+ tegra_max98090_hp_jack_pins,
+ ARRAY_SIZE(tegra_max98090_hp_jack_pins));
tegra_max98090_hp_jack_gpio.gpio = machine->gpio_hp_det;
snd_soc_jack_add_gpios(&tegra_max98090_hp_jack,
}
if (gpio_is_valid(machine->gpio_mic_det)) {
- snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE,
- &tegra_max98090_mic_jack);
- snd_soc_jack_add_pins(&tegra_max98090_mic_jack,
- ARRAY_SIZE(tegra_max98090_mic_jack_pins),
- tegra_max98090_mic_jack_pins);
+ snd_soc_card_jack_new(rtd->card, "Mic Jack",
+ SND_JACK_MICROPHONE,
+ &tegra_max98090_mic_jack,
+ tegra_max98090_mic_jack_pins,
+ ARRAY_SIZE(tegra_max98090_mic_jack_pins));
tegra_max98090_mic_jack_gpio.gpio = machine->gpio_mic_det;
snd_soc_jack_add_gpios(&tegra_max98090_mic_jack,
static int tegra_rt5640_asoc_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_codec *codec = codec_dai->codec;
struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(rtd->card);
- snd_soc_jack_new(codec, "Headphones", SND_JACK_HEADPHONE,
- &tegra_rt5640_hp_jack);
- snd_soc_jack_add_pins(&tegra_rt5640_hp_jack,
- ARRAY_SIZE(tegra_rt5640_hp_jack_pins),
- tegra_rt5640_hp_jack_pins);
+ snd_soc_card_jack_new(rtd->card, "Headphones", SND_JACK_HEADPHONE,
+ &tegra_rt5640_hp_jack, tegra_rt5640_hp_jack_pins,
+ ARRAY_SIZE(tegra_rt5640_hp_jack_pins));
if (gpio_is_valid(machine->gpio_hp_det)) {
tegra_rt5640_hp_jack_gpio.gpio = machine->gpio_hp_det;
struct snd_soc_dapm_context *dapm = &codec->dapm;
struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(rtd->card);
- snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
- &tegra_rt5677_hp_jack);
- snd_soc_jack_add_pins(&tegra_rt5677_hp_jack, 1,
- &tegra_rt5677_hp_jack_pins);
+ snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE,
+ &tegra_rt5677_hp_jack,
+ &tegra_rt5677_hp_jack_pins, 1);
if (gpio_is_valid(machine->gpio_hp_det)) {
tegra_rt5677_hp_jack_gpio.gpio = machine->gpio_hp_det;
}
- snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE,
- &tegra_rt5677_mic_jack);
- snd_soc_jack_add_pins(&tegra_rt5677_mic_jack, 1,
- &tegra_rt5677_mic_jack_pins);
+ snd_soc_card_jack_new(rtd->card, "Mic Jack", SND_JACK_MICROPHONE,
+ &tegra_rt5677_mic_jack,
+ &tegra_rt5677_mic_jack_pins, 1);
if (gpio_is_valid(machine->gpio_mic_present)) {
tegra_rt5677_mic_jack_gpio.gpio = machine->gpio_mic_present;
if (gpio_is_valid(machine->gpio_hp_det)) {
tegra_wm8903_hp_jack_gpio.gpio = machine->gpio_hp_det;
- snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE,
- &tegra_wm8903_hp_jack);
- snd_soc_jack_add_pins(&tegra_wm8903_hp_jack,
- ARRAY_SIZE(tegra_wm8903_hp_jack_pins),
- tegra_wm8903_hp_jack_pins);
+ snd_soc_card_jack_new(rtd->card, "Headphone Jack",
+ SND_JACK_HEADPHONE, &tegra_wm8903_hp_jack,
+ tegra_wm8903_hp_jack_pins,
+ ARRAY_SIZE(tegra_wm8903_hp_jack_pins));
snd_soc_jack_add_gpios(&tegra_wm8903_hp_jack,
1,
&tegra_wm8903_hp_jack_gpio);
}
- snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE,
- &tegra_wm8903_mic_jack);
- snd_soc_jack_add_pins(&tegra_wm8903_mic_jack,
- ARRAY_SIZE(tegra_wm8903_mic_jack_pins),
- tegra_wm8903_mic_jack_pins);
+ snd_soc_card_jack_new(rtd->card, "Mic Jack", SND_JACK_MICROPHONE,
+ &tegra_wm8903_mic_jack,
+ tegra_wm8903_mic_jack_pins,
+ ARRAY_SIZE(tegra_wm8903_mic_jack_pins));
wm8903_mic_detect(codec, &tegra_wm8903_mic_jack, SND_JACK_MICROPHONE,
0);
for (; p < buf_end; ++p) {
short pv = le16_to_cpu(*p);
int val = (pv * volume[chn & 1]) >> 8;
- pv = clamp(val, 0x7fff, -0x8000);
+ pv = clamp(val, -0x8000, 0x7fff);
*p = cpu_to_le16(pv);
++chn;
}
val = p[0] + (p[1] << 8) + ((signed char)p[2] << 16);
val = (val * volume[chn & 1]) >> 8;
- val = clamp(val, 0x7fffff, -0x800000);
+ val = clamp(val, -0x800000, 0x7fffff);
p[0] = val;
p[1] = val >> 8;
p[2] = val >> 16;
short pov = le16_to_cpu(*po);
short piv = le16_to_cpu(*pi);
int val = pov + ((piv * volume) >> 8);
- pov = clamp(val, 0x7fff, -0x8000);
+ pov = clamp(val, -0x8000, 0x7fff);
*po = cpu_to_le16(pov);
}
}
}
}
},
+{
+ USB_DEVICE(0x0582, 0x0159),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ /* .vendor_name = "Roland", */
+ /* .product_name = "UA-22", */
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 0,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 2,
+ .type = QUIRK_MIDI_FIXED_ENDPOINT,
+ .data = & (const struct snd_usb_midi_endpoint_info) {
+ .out_cables = 0x0001,
+ .in_cables = 0x0001
+ }
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
/* this catches most recent vendor-specific Roland devices */
{
.match_flags = USB_DEVICE_ID_MATCH_VENDOR |
memcpy_t fn = r->fn.memcpy;
int i;
- memcpy_alloc_mem(&src, &dst, len);
+ memcpy_alloc_mem(&dst, &src, len);
if (prefault)
fn(dst, src, len);
void *src = NULL, *dst = NULL;
int i;
- memcpy_alloc_mem(&src, &dst, len);
+ memcpy_alloc_mem(&dst, &src, len);
if (prefault)
fn(dst, src, len);
endif
endif
+ifeq ($(RAW_ARCH),sparc64)
+ ARCH ?= sparc
+endif
+
ARCH ?= $(RAW_ARCH)
LP64 := $(shell echo __LP64__ | ${CC} ${CFLAGS} -E -x c - | tail -n 1)
$(BUILD)
test-pthread-attr-setaffinity-np.bin:
- $(BUILD) -Werror -lpthread
+ $(BUILD) -D_GNU_SOURCE -Werror -lpthread
test-stackprotector-all.bin:
$(BUILD) -Werror -fstack-protector-all
{
int ret = 0;
pthread_attr_t thread_attr;
+ cpu_set_t cs;
pthread_attr_init(&thread_attr);
/* don't care abt exact args, just the API itself in libpthread */
- ret = pthread_attr_setaffinity_np(&thread_attr, 0, NULL);
+ ret = pthread_attr_setaffinity_np(&thread_attr, sizeof(cs), &cs);
return ret;
}
static void ins__delete(struct ins_operands *ops)
{
+ if (ops == NULL)
+ return;
zfree(&ops->source.raw);
zfree(&ops->source.name);
zfree(&ops->target.raw);
if (cpu < 0)
cpu = 0;
+ /*
+ * Using -1 for the pid is a workaround to avoid gratuitous jump label
+ * changes.
+ */
while (1) {
/* check cloexec flag */
fd = sys_perf_event_open(&attr, pid, cpu, -1,
err, strerror_r(err, sbuf, sizeof(sbuf)));
/* not supported, confirm error related to PERF_FLAG_FD_CLOEXEC */
- fd = sys_perf_event_open(&attr, pid, cpu, -1, 0);
+ while (1) {
+ fd = sys_perf_event_open(&attr, pid, cpu, -1, 0);
+ if (fd < 0 && pid == -1 && errno == EACCES) {
+ pid = 0;
+ continue;
+ }
+ break;
+ }
err = errno;
+ if (fd >= 0)
+ close(fd);
+
if (WARN_ONCE(fd < 0 && err != EBUSY,
"perf_event_open(..., 0) failed unexpectedly with error %d (%s)\n",
err, strerror_r(err, sbuf, sizeof(sbuf))))
return -1;
- close(fd);
-
return 0;
}
int mask;
int refcnt;
unsigned int prev;
- char event_copy[PERF_SAMPLE_MAX_SIZE];
+ char event_copy[PERF_SAMPLE_MAX_SIZE] __attribute__((aligned(8)));
};
struct perf_evlist {
#include <symbol/kallsyms.h>
#include "debug.h"
+#ifndef EM_AARCH64
+#define EM_AARCH64 183 /* ARM 64 bit */
+#endif
+
+
#ifdef HAVE_CPLUS_DEMANGLE_SUPPORT
extern char *cplus_demangle(const char *, int);
$(OUTPUT)cpupower: $(UTIL_OBJS) $(OUTPUT)libcpupower.so.$(LIB_MAJ)
$(ECHO) " CC " $@
- $(QUIET) $(CC) $(CFLAGS) $(LDFLAGS) $(UTIL_OBJS) -lcpupower -Wl,-rpath=./ -lrt -lpci -L$(OUTPUT) -o $@
+ $(QUIET) $(CC) $(CFLAGS) $(LDFLAGS) $(UTIL_OBJS) -lcpupower -lrt -lpci -L$(OUTPUT) -o $@
$(QUIET) $(STRIPCMD) $@
$(OUTPUT)po/$(PACKAGE).pot: $(UTIL_SRC)
TARGETS_HOTPLUG = cpu-hotplug
TARGETS_HOTPLUG += memory-hotplug
+# Clear LDFLAGS and MAKEFLAGS if called from main
+# Makefile to avoid test build failures when test
+# Makefile doesn't have explicit build rules.
+ifeq (1,$(MAKELEVEL))
+undefine LDFLAGS
+override MAKEFLAGS =
+endif
+
all:
for TARGET in $(TARGETS); do \
make -C $$TARGET; \
#ifdef __NR_execveat
return syscall(__NR_execveat, fd, path, argv, envp, flags);
#else
- errno = -ENOSYS;
+ errno = ENOSYS;
return -1;
#endif
}
int fd_cloexec = open_or_die("execveat", O_RDONLY|O_CLOEXEC);
int fd_script_cloexec = open_or_die("script", O_RDONLY|O_CLOEXEC);
+ /* Check if we have execveat at all, and bail early if not */
+ errno = 0;
+ execveat_(-1, NULL, NULL, NULL, 0);
+ if (errno == ENOSYS) {
+ printf("[FAIL] ENOSYS calling execveat - no kernel support?\n");
+ return 1;
+ }
+
/* Change file position to confirm it doesn't affect anything */
lseek(fd, 10, SEEK_SET);
BINDIR=usr/bin
WARNFLAGS=-Wall -Wshadow -W -Wformat -Wimplicit-function-declaration -Wimplicit-int
-CFLAGS= -O1 ${WARNFLAGS} -fstack-protector
-CC=gcc
+CFLAGS+= -O1 ${WARNFLAGS} -fstack-protector
+CC=$(CROSS_COMPILE)gcc
CFLAGS+=-D VERSION=\"$(VERSION)\"
LDFLAGS+=
CONFIG_FILE=
CONFIG_PATH=
+# Static builds might require -ltinfo, for instance
+ifneq ($(findstring -static, $(LDFLAGS)),)
+STATIC := --static
+endif
+
+TMON_LIBS=-lm -lpthread
+TMON_LIBS += $(shell pkg-config --libs $(STATIC) panelw ncursesw 2> /dev/null || \
+ pkg-config --libs $(STATIC) panel ncurses 2> /dev/null || \
+ echo -lpanel -lncurses)
OBJS = tmon.o tui.o sysfs.o pid.o
OBJS +=
tmon: $(OBJS) Makefile tmon.h
- $(CC) ${CFLAGS} $(LDFLAGS) $(OBJS) -o $(TARGET) -lm -lpanel -lncursesw -ltinfo -lpthread
+ $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) -o $(TARGET) $(TMON_LIBS)
valgrind: tmon
sudo valgrind -v --track-origins=yes --tool=memcheck --leak-check=yes --show-reachable=yes --num-callers=20 --track-fds=yes ./$(TARGET) 1> /dev/null
.PP
The \fB-t --time-interval\fP option sets the polling interval in seconds
.PP
+The \fB-T --target-temp\fP option sets the initial target temperature
+.PP
The \fB-v --version\fP option shows the version of \fBtmon \fP
.PP
The \fB-z --zone\fP option sets the target therma zone instance to be controlled
printf(" -h, --help show this help message\n");
printf(" -l, --log log data to /var/tmp/tmon.log\n");
printf(" -t, --time-interval sampling time interval, > 1 sec.\n");
+ printf(" -T, --target-temp initial target temperature\n");
printf(" -v, --version show version\n");
printf(" -z, --zone target thermal zone id\n");
{ "control", 1, NULL, 'c' },
{ "daemon", 0, NULL, 'd' },
{ "time-interval", 1, NULL, 't' },
+ { "target-temp", 1, NULL, 'T' },
{ "log", 0, NULL, 'l' },
{ "help", 0, NULL, 'h' },
{ "version", 0, NULL, 'v' },
{
int err = 0;
int id2 = 0, c;
- double yk = 0.0; /* controller output */
+ double yk = 0.0, temp; /* controller output */
int target_tz_index;
if (geteuid() != 0) {
exit(EXIT_FAILURE);
}
- while ((c = getopt_long(argc, argv, "c:dlht:vgz:", opts, &id2)) != -1) {
+ while ((c = getopt_long(argc, argv, "c:dlht:T:vgz:", opts, &id2)) != -1) {
switch (c) {
case 'c':
no_control = 0;
if (ticktime < 1)
ticktime = 1;
break;
+ case 'T':
+ temp = strtod(optarg, NULL);
+ if (temp < 0) {
+ fprintf(stderr, "error: temperature must be positive\n");
+ return 1;
+ }
+ target_temp_user = temp;
+ break;
case 'l':
printf("Logging data to /var/tmp/tmon.log\n");
logging = 1;
#include "tmon.h"
+#define min(x, y) ({ \
+ typeof(x) _min1 = (x); \
+ typeof(y) _min2 = (y); \
+ (void) (&_min1 == &_min2); \
+ _min1 < _min2 ? _min1 : _min2; })
+
+#define max(x, y) ({ \
+ typeof(x) _max1 = (x); \
+ typeof(y) _max2 = (y); \
+ (void) (&_max1 == &_max2); \
+ _max1 > _max2 ? _max1 : _max2; })
+
static PANEL *data_panel;
static PANEL *dialogue_panel;
static PANEL *top;
wrefresh(status_bar_window);
}
+/* wrap at 5 */
+#define DIAG_DEV_ROWS 5
+/*
+ * list cooling devices + "set temp" entry; wraps after 5 rows, if they fit
+ */
+static int diag_dev_rows(void)
+{
+ int entries = ptdata.nr_cooling_dev + 1;
+ int rows = max(DIAG_DEV_ROWS, (entries + 1) / 2);
+ return min(rows, entries);
+}
+
void setup_windows(void)
{
int y_begin = 1;
* dialogue window is a pop-up, when needed it lays on top of cdev win
*/
- dialogue_window = subwin(stdscr, ptdata.nr_cooling_dev+5, maxx-50,
+ dialogue_window = subwin(stdscr, diag_dev_rows() + 5, maxx-50,
DIAG_Y, DIAG_X);
thermal_data_window = subwin(stdscr, ptdata.nr_tz_sensor *
}
const char DIAG_TITLE[] = "[ TUNABLES ]";
-#define DIAG_DEV_ROWS 5
void show_dialogue(void)
{
int j, x = 0, y = 0;
+ int rows, cols;
WINDOW *w = dialogue_window;
if (tui_disabled || !w)
return;
+ getmaxyx(w, rows, cols);
+
+ /* Silence compiler 'unused' warnings */
+ (void)cols;
+
werase(w);
box(w, 0, 0);
mvwprintw(w, 0, maxx/4, DIAG_TITLE);
/* list all the available tunables */
for (j = 0; j <= ptdata.nr_cooling_dev; j++) {
- y = j % DIAG_DEV_ROWS;
+ y = j % diag_dev_rows();
if (y == 0 && j != 0)
x += 20;
if (j == ptdata.nr_cooling_dev)
ptdata.cdi[j].type, ptdata.cdi[j].instance);
}
wattron(w, A_BOLD);
- mvwprintw(w, DIAG_DEV_ROWS+1, 1, "Enter Choice [A-Z]?");
+ mvwprintw(w, diag_dev_rows()+1, 1, "Enter Choice [A-Z]?");
wattroff(w, A_BOLD);
- /* y size of dialogue win is nr cdev + 5, so print legend
- * at the bottom line
- */
- mvwprintw(w, ptdata.nr_cooling_dev+3, 1,
+ /* print legend at the bottom line */
+ mvwprintw(w, rows - 2, 1,
"Legend: A=Active, P=Passive, C=Critical");
wrefresh(dialogue_window);
snprintf(buf, sizeof(buf), "New Value for %.10s-%2d: ",
ptdata.cdi[cdev_id].type,
ptdata.cdi[cdev_id].instance);
- write_dialogue_win(buf, DIAG_DEV_ROWS+2, 2);
+ write_dialogue_win(buf, diag_dev_rows() + 2, 2);
handle_input_val(cdev_id);
} else {
snprintf(buf, sizeof(buf), "Invalid selection %d", ch);
{
if (!(lr_desc.state & LR_STATE_MASK))
vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr |= (1ULL << lr);
+ else
+ vcpu->arch.vgic_cpu.vgic_v2.vgic_elrsr &= ~(1ULL << lr);
}
static u64 vgic_v2_get_elrsr(const struct kvm_vcpu *vcpu)
return vcpu->arch.vgic_cpu.vgic_v2.vgic_eisr;
}
+static void vgic_v2_clear_eisr(struct kvm_vcpu *vcpu)
+{
+ vcpu->arch.vgic_cpu.vgic_v2.vgic_eisr = 0;
+}
+
static u32 vgic_v2_get_interrupt_status(const struct kvm_vcpu *vcpu)
{
u32 misr = vcpu->arch.vgic_cpu.vgic_v2.vgic_misr;
.sync_lr_elrsr = vgic_v2_sync_lr_elrsr,
.get_elrsr = vgic_v2_get_elrsr,
.get_eisr = vgic_v2_get_eisr,
+ .clear_eisr = vgic_v2_clear_eisr,
.get_interrupt_status = vgic_v2_get_interrupt_status,
.enable_underflow = vgic_v2_enable_underflow,
.disable_underflow = vgic_v2_disable_underflow,
{
if (!(lr_desc.state & LR_STATE_MASK))
vcpu->arch.vgic_cpu.vgic_v3.vgic_elrsr |= (1U << lr);
+ else
+ vcpu->arch.vgic_cpu.vgic_v3.vgic_elrsr &= ~(1U << lr);
}
static u64 vgic_v3_get_elrsr(const struct kvm_vcpu *vcpu)
return vcpu->arch.vgic_cpu.vgic_v3.vgic_eisr;
}
+static void vgic_v3_clear_eisr(struct kvm_vcpu *vcpu)
+{
+ vcpu->arch.vgic_cpu.vgic_v3.vgic_eisr = 0;
+}
+
static u32 vgic_v3_get_interrupt_status(const struct kvm_vcpu *vcpu)
{
u32 misr = vcpu->arch.vgic_cpu.vgic_v3.vgic_misr;
.sync_lr_elrsr = vgic_v3_sync_lr_elrsr,
.get_elrsr = vgic_v3_get_elrsr,
.get_eisr = vgic_v3_get_eisr,
+ .clear_eisr = vgic_v3_clear_eisr,
.get_interrupt_status = vgic_v3_get_interrupt_status,
.enable_underflow = vgic_v3_enable_underflow,
.disable_underflow = vgic_v3_disable_underflow,
return vgic_ops->get_eisr(vcpu);
}
+static inline void vgic_clear_eisr(struct kvm_vcpu *vcpu)
+{
+ vgic_ops->clear_eisr(vcpu);
+}
+
static inline u32 vgic_get_interrupt_status(struct kvm_vcpu *vcpu)
{
return vgic_ops->get_interrupt_status(vcpu);
vgic_set_lr(vcpu, lr_nr, vlr);
clear_bit(lr_nr, vgic_cpu->lr_used);
vgic_cpu->vgic_irq_lr_map[irq] = LR_EMPTY;
+ vgic_sync_lr_elrsr(vcpu, lr_nr, vlr);
}
/*
BUG_ON(!test_bit(lr, vgic_cpu->lr_used));
vlr.state |= LR_STATE_PENDING;
vgic_set_lr(vcpu, lr, vlr);
+ vgic_sync_lr_elrsr(vcpu, lr, vlr);
return true;
}
}
vlr.state |= LR_EOI_INT;
vgic_set_lr(vcpu, lr, vlr);
+ vgic_sync_lr_elrsr(vcpu, lr, vlr);
return true;
}
if (status & INT_STATUS_UNDERFLOW)
vgic_disable_underflow(vcpu);
+ /*
+ * In the next iterations of the vcpu loop, if we sync the vgic state
+ * after flushing it, but before entering the guest (this happens for
+ * pending signals and vmid rollovers), then make sure we don't pick
+ * up any old maintenance interrupts here.
+ */
+ vgic_clear_eisr(vcpu);
+
return level_pending;
}
* emulation. So check this here again. KVM_CREATE_DEVICE does
* the proper checks already.
*/
- if (type == KVM_DEV_TYPE_ARM_VGIC_V2 && !vgic->can_emulate_gicv2)
- return -ENODEV;
+ if (type == KVM_DEV_TYPE_ARM_VGIC_V2 && !vgic->can_emulate_gicv2) {
+ ret = -ENODEV;
+ goto out;
+ }
/*
* Any time a vcpu is run, vcpu_load is called which tries to grab the
BUILD_BUG_ON(KVM_MEM_SLOTS_NUM > SHRT_MAX);
r = -ENOMEM;
- kvm->memslots = kzalloc(sizeof(struct kvm_memslots), GFP_KERNEL);
+ kvm->memslots = kvm_kvzalloc(sizeof(struct kvm_memslots));
if (!kvm->memslots)
goto out_err_no_srcu;
out_err_no_disable:
for (i = 0; i < KVM_NR_BUSES; i++)
kfree(kvm->buses[i]);
- kfree(kvm->memslots);
+ kvfree(kvm->memslots);
kvm_arch_free_vm(kvm);
return ERR_PTR(r);
}
kvm_for_each_memslot(memslot, slots)
kvm_free_physmem_slot(kvm, memslot, NULL);
- kfree(kvm->memslots);
+ kvfree(kvm->memslots);
}
static void kvm_destroy_devices(struct kvm *kvm)
goto out_free;
}
- slots = kmemdup(kvm->memslots, sizeof(struct kvm_memslots),
- GFP_KERNEL);
+ slots = kvm_kvzalloc(sizeof(struct kvm_memslots));
if (!slots)
goto out_free;
+ memcpy(slots, kvm->memslots, sizeof(struct kvm_memslots));
if ((change == KVM_MR_DELETE) || (change == KVM_MR_MOVE)) {
slot = id_to_memslot(slots, mem->slot);
kvm_arch_commit_memory_region(kvm, mem, &old, change);
kvm_free_physmem_slot(kvm, &old, &new);
- kfree(old_memslots);
+ kvfree(old_memslots);
/*
* IOMMU mapping: New slots need to be mapped. Old slots need to be
return 0;
out_slots:
- kfree(slots);
+ kvfree(slots);
out_free:
kvm_free_physmem_slot(kvm, &new, &old);
out:
case KVM_CAP_SIGNAL_MSI:
#endif
#ifdef CONFIG_HAVE_KVM_IRQFD
+ case KVM_CAP_IRQFD:
case KVM_CAP_IRQFD_RESAMPLE:
#endif
case KVM_CAP_CHECK_EXTENSION_VM: