Merge "defconfig: msm9625: enable display support"
diff --git a/Documentation/arm/msm/n_smux.txt b/Documentation/arm/msm/n_smux.txt
new file mode 100644
index 0000000..c9123ad
--- /dev/null
+++ b/Documentation/arm/msm/n_smux.txt
@@ -0,0 +1,630 @@
+Introduction
+============
+
+The Serial Mux (SMUX) is a TTY Line Discipline that multiplexes
+multiple logical channels onto a single TTY serial channel. The
+logical channels are exposed through a kernel API.
+
+Companion adaptation drivers use the kernel API to expose logical
+channels as character devices (SMUX CTL - smux_ctl.c) to the user-space
+and as net devices (SMUX RMNET - msm_rmnet_smux.c) to the TCP/IP stack.
+
+Power control calls are supported to the physical serial driver to
+optimize power usage.
+
+
+Software description
+====================
+
+The Serial Mux driver will be similar in design to the SDIO DMUX and
+BAM DMUX drivers and will use the same multiplexing protocol with
+additional commands to support inactivity timeouts along with
+power-down and wake-up handshaking. Companion adaptation drivers will
+support data-plane traffic through TCP/IP (SMUX_RMNET) and control
+plane traffic through SMUX_CTL.
+
+
+ ttyHS0 RMNET[0..N] smuxctl[0..M]
+ | | |
+ | | |
+ | ------------------ -------------------
+ | | IP Framework | | VFS Framework |
+ | ------------------ -------------------
+ | | | | |
+ | | | | |
+ | ------- ------- ------- -------
+ | | Rmnet | | Rmnet | | CDEV | | CDEV |
+ | | Dev 0 |...| Dev N | | Dev 0 |...| Dev M |
+ | --------------------- --------------------- -------------
+ | | | | | | Other |
+ | | msm_rmnet_smux | | smux_ctl | | Kernel-only |
+ | | | | | | Clients |
+ | --------------------- --------------------- -------------
+ | | | | | |
+ | | | | | |
+ | | | | | |
+ | | | | | |
+ | ---------------------------------------------------
+ | |
+ | |
+ | |
+ | |
+ --------------- --------------------
+ | | | |
+ | TTY Framework | <- Line Discipline -> | SMUX |
+ | | | |
+ --------------- --------------------
+ | |
+ | |
+ --------------- |
+ | | |
+ | HS UART |<---------- Power API -----------
+ | |
+ ---------------
+ |
+ V
+ To Remote System
+
+
+Each logical channel will contain package management structures
+including a watermark to ensure fair usage of the physical layer by
+competing logical channels. All data for logical channels will be
+processed in FIFO order.
+
+Once data has been queued with SMUX, processing, copying of data, and
+notification to clients will be done using a combination of the TTY
+framework notification context and a workqueue. The lifetime of all
+buffers is controlled by the clients with SMUX taking temporary
+ownership of the buffers for read and write operations.
+
+The physical transport is assumed to be perfect and all errors will be
+handled by notifying the client of the failure. Watermark support and
+round-robin scheduling ensure that individual logical channels do not
+starve other logical channels.
+
+Data stalls caused by failure of the remote system are handled by
+Subsystem Restart. The restart logic will notify clients of the
+failure and all read and write buffers will be returned to the client
+with an error notification.
+
+Design
+======
+
+The goals for SMUX are to:
+ 1) multiplex multiple logical channels into a single physical
+ channel
+ 2) support a kernel API
+ 3) provide power control of the physical layer
+
+In addition, the companion adapter modules have the goals:
+ 1) support userspace character-device clients (smux_ctl)
+ 2) support net devices through the TCP/IP stack (msm_rmnet_smux)
+
+Alternate designs consider including 3GPP 27.010 MUX protocol
+implementations and existing SDIO CMUX/DMUX implementations.
+
+The 3GPP 27.010 MUX protocol as implemented in both n_gsm.c and OpenEZX
+looked promising at first glance. However, upon further inspection,
+the implementations did not fully implement the power-control portions
+of the 27.010 MUX protocol. They also did not support kernel clients
+as they were designed to work only with userspace through TTY devices.
+The code was reviewed to determine the effort to add power-control
+signaling to the physical transport driver and to add a kernel API, but
+it was deemed that adding the API to support both of these behaviors
+would be difficult to do in a generic way such that it would be
+accepted by the upstream community.
+
+The SDIO CMUX/DMUX drivers do not have power control in them and the
+CMUX and DMUX drivers both require a separate physical channel. To use
+them, we would need to create an additional mux layer that would sit
+between CMUX/DMUX and the HS UART driver which would add another MUX
+header.
+
+Design - MUX Protocol
+=====================
+The MUX packet consists of a header (detailed below) and a payload.
+All values are in little-endian format and all reserved fields are set
+to zero unless otherwise mentioned in the individual command
+descriptions.
+
+Invalid commands and malformed commands will be logged to the kernel log
+as an error and ignored.
+
+ -----------------------------------------
+ |31 24| 16| 8| 0|
+ |----------|---------|----------|---------|
+ | Magic Number | Flags | CMD |
+ |----------|---------|----------|---------|
+ | Pad Len | LCID | Packet Length (N) |
+ |-----------------------------------------|
+ | Data Payload (0..N bytes) |
+ |-----------------------------------------|
+ | Pad Data (0..Pad Len bytes) |
+ -----------------------------------------
+
+Field definitions:
+ * Magic Number - always 0x33FC
+ * Flags - flags for individual commands
+ * CMD - SMUX command
+ * Pad Len - Padding in bytes at the end of the payload
+ * LCID - Logical channel ID
+ * Packet Length - Length of the data payload in bytes
+
+Commands
+ 0x0 - Data
+ 0x1 - Open Logical Channel
+ 0x2 - Close Logical Channel
+ 0x3 - Status
+ 0x4 - Power Control
+
+Data Command
+------------
+The Data command sends data on an already fully-opened logical channel.
+
+Flags:
+ * Bits 0:7 - Reserved
+
+Open Logical Channel Command
+----------------------------
+The Open command is a request to open a logical channel. Each channel
+will have a local and remote open flag. The remote open flag will be
+set to open when receiving an open command and responding with an open
+ACK. The local open flag is set to open when sending an open command
+and receiving an ACK.
+
+ Remote Side | Local Side
+ |
+ SMUX Client SMUX SMUX SMUX Client
+ | | | |
+ | Open | | |
+ |--------->| | |
+ | | Open Logical Channel | |
+ | |---------------------->| |
+ | | |--- |
+ | | | | Set Remote Open |
+ | | |<-- |
+ | | Open ACK | |
+ | |<----------------------| |
+ | |--- | |
+ | | | Set Local Open | |
+ | |<-- | |
+ | ... ... ...
+ | | | msm_smux_open() |
+ | | |<-----------------------|
+ | | Open Logical Channel | |
+ | |<----------------------| |
+ | | | |
+ | |--- | |
+ | | | Set Remote Open | |
+ | |<-- | |
+ | | Open ACK | |
+ | |---------------------->| |
+ | | |--- |
+ | | | | Set Local Open |
+ | | |<-- |
+ | | | notify(SMUX_CONNECTED) |
+ | | |----------------------->|
+
+
+ Logical channel is now fully open and can receive
+ and transmit data.
+
+No data shall be transmitted over the physical link for the logical
+channel unless the channel is open.
+
+Flags:
+ * Bit 0 - 1 = ACK
+ * Bit 1 - Power Collapse Enable
+ * Bit 2 - Remote Loopback Enable
+ * Bits 3:7 - Reserved
+
+Power Collapse Enable (bit 1) enables power-collapse handshaking when
+processing an open command. The first logical channel open command
+received from the remote side will set the global power control state
+and all subsequent open commands should use the same value of the Power
+Collapse bit. The value of this bit can be changed during runtime by
+closing all logical channels and then re-opening them with the new
+global state.
+
+If the protocol stack does not support power collapse and it receives
+an open command with the Power Collapse Enable bit set, then it
+shall respond with an open command with the Power Collapse Enable bit
+cleared.
+
+If Power Collapse is disabled, then Power Control Commands should not
+be sent.
+
+Remote Loopback Enable (bit 2) enables loopback support when data is
+received from the remote side. In this case, SMUX should echo the
+received data packet back to the sender.
+
+Close Logical Channel Command
+-----------------------------
+The Close command closes the logical channel and updates the internal
+open state flags. The remote open flag will be set to closed when
+receiving a close command and responding with an close ACK. The local
+open flag is set to closed when sending a close command and receiving an
+ACK.
+
+No data shall be transmitted over the physical link for the logical
+channel after receiving a close command and responding with the close
+ACK.
+
+Flags:
+ * Bit 0 - ACK (when set to 1)
+ * Bits 1:7 - Reserved
+
+
+Status Command
+--------------
+The Status Command updates the channel status signals which include four
+ITU v.24 status bits in the lower nibble of the flags field along with a
+logical channel flow-control signal. The v.24 signals are pass-through
+and do not affect the state of SMUX.
+
+The Logical Channel Flow Control bit will disable TX on the logical
+channel when set and send a flow-control notification to the logical
+channel client. Any further attempts to transmit will result in an
+error return code.
+
+Flags:
+ * Bit 0 - RTC (DTR/DSR)
+ * Bit 1 - RTR (RTS/CTS)
+ * Bit 2 - RI
+ * Bit 3 - DCD
+ * Bit 4 - Logical Channel Flow Control
+ * Bits 5:7 - Reserved
+
+
+Power Control Command
+---------------------
+The physical layer requires a variable amount of time to wakeup from
+power collapse, reconfigure the hardware, and start processing data.
+Data may be lost until the wakeup has been completed. Because of this,
+a character-based wakeup method will be used to ensure that the remote
+side is active and ready before sending SMUX commands.
+
+If the remote side has previously requested power-down (boot-up state),
+then a wakeup request character is sent at periodic intervals (1 ms or
+8 character-widths, whichever is larger) until a wakeup-acknowledge character
+has been received. Normal transmit operations can then be performed. Once an
+activity timeout occurs, then a sleep vote should be sent to the remote side to
+let it know that the channel is no longer needed. The remote side should
+respond with an ACK.
+
+The following state diagram shows the full sequence of power state transitions.
+This state machine is identical on both the local and remote sides. The states
+marked "(internal)" are transitional states used in this driver that are not
+part of the power states tracked by the remote side. The edges are labeled
+using the format CONDITION:ACTION where condition is the guard condition that
+must be true for the edge to be taken and ACTION is the action that will be
+taken.
+
+ +--------------+ RX Sleep ACK || RX Sleep Request
+ :Flush and power-down | Powering |<---------+
+ UART +---------+ Down Flush | |
+ | | (internal) | |
+ | +--------------+ |
+ | ^ |
+ | | +-------+------+
+ v | | | |
+ +--------------+ | | Powering |
+ | | | | Down |<-----+
+Init --->| OFF | | | | |
+ | |------+ | +--------------+ |
+ | | | | |
+ +------+-------+ | | TX Sleep Request
+ | | | Complete
+ | | | |
+ Data ready to send | | |
+ :TX Wakeup Request | |RX Sleep Request |
+ | | |:TX Sleep ACK |
+ | | +------------+ +-------+------+
+ | | | | Turning Off^|
+ | | | | Flush |
+ | | | | (internal) |
+ | |RX Wakeup Request | +--------------+
+ | |:TX Wakeup ACK | ^
+ | | | |
+ | | | Inactivity Timeout
+ | +--------------+ | :TX Sleep Request
+ | | | |
+ v v | |
+ +--------------+ RX Wakeup ACK +-------+------+ |
+ | +------------------>| UP |+-----+
+ | Powering | | |
+ | Up +------------------>| Packet TX/RX |
+ +----->| | RX Wakeup Request | is now active|<------+
+ | +--+-----------+ :TX Wakeup ACK +----------+---+ |
+ | | | |
+ +---------+ +-----------+
+ Wakeup Request Timeout RX Wakeup Request
+ :TX Wakeup Request :TX Wakeup ACK
+
+
+In-band wakeup bytes:
+ * 0xfd - Wakeup Request
+ * 0xfe - Wakeup Acknowledge
+
+Flags:
+ * Bit 0 - ACK (when set to 1)
+ * Bit 1 - 1 = Sleep Request
+ * Bits 2:7 - Reserved
+
+Initial SMUX State
+------------------
+The boot-up state of SMUX is in power-disabled mode and all logical
+channels closed. Before sending any commands to open logical channels,
+the remote SMUX must be woken up.
+
+Power Management
+================
+
+Power management will consist of wakeup and shutdown control of the
+physical layer based upon an activity timeout. Wakelocks will be
+utilized to prevent the system from going to sleep while the transport
+is active. The activity timeout is anticipated to be 500 ms, but this
+is subject to tuning to meet power and performance specifications.
+
+SMP/multi-core
+==============
+
+Locking and synchronization will be done using mutexes or spinlocks
+where appropriate. The software will be structured such that locking
+can be kept to a minimum by only locking when items are added and
+removed from lists.
+
+Security
+========
+
+No new security issues are anticipated as communication with userspace
+is done through the existing TCP/IP and CDEV frameworks.
+
+Performance
+===========
+
+The throughput requirements for this design are on the order of 250Kbps
+so throughput concerns are not expected to be an issue. However, in
+the hope that this driver can be leveraged in the future instead of
+writing yet another multiplexing layer, performance will be considered
+when making design decisions.
+
+Interface
+=========
+
+The kernel API consists of commands to read and write data, vote for
+power, and verify the state of the logical channels.
+
+
+Open
+----
+int msm_smux_open(uint32_t lcid, void *priv,
+ void (*notify)(void *priv, int event_type, void *metadata),
+ int (*get_rx_buffer)(void *priv, void **pkt_priv,
+ void **buffer, int size))
+
+Open a logical channel. The channel will be first opened locally and
+an open command will be sent to the remote processor. Once the remote
+processor sends an open command, then the port will be fully open and
+ready for data operations -- the client will be notified with an
+SMUX_CONNECTED notification.
+
+For receive notifications, the driver will read the SMUX header into
+temporary storage and then when the logical channel and size are both
+known, the driver will call the get_rx_buffer() function to request a
+buffer for the data. The client should return 0 upon success or < 0
+using standard Linux error codes if an error occurred. If the error
+code is EAGAIN, then the call to get_rx_buffer() will be retried once,
+otherwise the data will be discarded by the driver and a kernel error
+message logged.
+
+Once the receive data has been processed, the notify() function will be
+called with metadata pointing to an instance of struct smux_meta_read
+and the event type will either be SMUX_READ_DONE for successful cases or
+SMUX_READ_FAIL for failure cases.
+
+/*
+ * Notification events that are passed to the notify() function.
+ *
+ * If the @metadata argument in the notifier is non-null, then it will
+ * point to the associated struct smux_meta_* structure.
+ */
+enum {
+ SMUX_CONNECTED, /* @metadata is null */
+ SMUX_DISCONNECTED,
+ SMUX_READ_DONE,
+ SMUX_READ_FAIL,
+ SMUX_WRITE_DONE,
+ SMUX_WRITE_FAIL,
+ SMUX_TIOCM_UPDATE,
+ SMUX_LOW_WM_HIT, /* @metadata is NULL */
+ SMUX_HIGH_WM_HIT, /* @metadata is NULL */
+};
+
+/*
+ * Metadata for SMUX_READ_DONE/SMUX_READ_FAIL notification
+ *
+ * @pkt_priv: Packet-specific private data
+ * @buffer: Buffer pointer passed into msm_smux_write
+ * @len: Buffer length passed into msm_smux_write
+ */
+struct smux_meta_read {
+ void *pkt_priv;
+ void *buffer;
+ int len;
+};
+
+Close
+-----
+int msm_smux_close(uint32_t lcid)
+
+Closes a logical channel locally and sends a close command to the
+remote host.
+
+If there is pending transmit or receive data, then SMUX_WRITE_FAIL and
+SMUX_READ_FAIL notifications will be made to return ownership of the
+buffers to the client.
+
+Once the remote side of the port has been closed, the notify function
+will be called with the event SMUX_DISCONNECTED and metadata pointing
+to a struct smux_meta_disconnected structure. After this point, no
+further notifications will be performed.
+
+/*
+ * Metadata for SMUX_DISCONNECTED notification
+ *
+ * @is_ssr: Disconnect caused by subsystem restart
+ */
+struct smux_meta_disconnected {
+ int is_ssr;
+};
+
+
+Write
+-----
+int msm_smux_write(uint32_t lcid, void *pkt_priv, void *data, int len)
+
+Queues data for transmit. Once the data has been transmitted, the
+SMUX_WRITE_DONE or SMUX_WRITE_FAIL notifications will be sent with
+metadata pointing to an instance of struct smux_meta_write.
+
+If the high watermark has been exceeded, then further writes will
+return -EAGAIN.
+
+Data may be written as soon as the local side of the port has been
+opened, but the data will not be transmitted until the channel has been
+fully opened and the SMUX_CONNECTED event has been sent.
+
+/*
+ * Metadata for SMUX_WRITE_DONE/SMUX_WRITE_FAIL notification
+ *
+ * @pkt_priv: Packet-specific private data
+ * @buffer: Buffer pointer returned by get_rx_buffer()
+ * @len: Buffer length returned by get_rx_buffer()
+ */
+struct smux_meta_write {
+ void *pkt_priv;
+ void *buffer;
+ int len;
+};
+
+Watermark
+---------
+ int msm_smux_is_ch_full(uint32_t lcid)
+ int msm_smux_is_ch_low(uint32_t lcid)
+
+A channel watermark is used to keep individual clients from using
+excessive internal resources. The client may call
+msm_smux_is_ch_full() after every msm_smux_write() operation and if the
+watermark is high, it should not queue any more packets for
+transmission. As an alternative, the client may base this decision
+upon receiving an SMUX_HIGH_WM_HIT notification.
+
+Likewise, the client may call msm_smux_is_ch_low() after every
+SMUX_WRITE_DONE or SMUX_WRITE_FAIL notification and if the watermark is
+low, then new transmit operations can be started. As an alternative,
+the client may base this decision upon receiving an SMUX_LOW_WM_HIT
+notification.
+
+Control Signals
+---------------
+ long msm_smux_tiocm_get(uint32_t lcid)
+ long msm_smux_tiocm_set(uint32_t lcid, uint32_t set, uint32_t clear)
+
+The TIOCM bits do not affect the SMUX internal state as they are
+pass-through for the clients. The client can receive notifications of
+state changes through the SMUX_TIOCM_UPDATE command.
+
+See the "Status Command" section for details on the TIOCM bits.
+
+/*
+ * Metadata for SMUX_TIOCM_UPDATE notification
+ *
+ * @previous: Previous TIOCM state
+ * @current: Current TIOCM state
+ *
+ */
+struct smux_meta_tiocm {
+ uint32_t previous;
+ uint32_t current;
+};
+
+Subsystem Restart
+-----------------
+Subsystem restart is handled by sending a disconnect notification
+followed by sending read and write fail notifications to each client.
+This returns ownership of the read and write buffers to the clients for
+client-appropriate handling.
+
+The sequence of notifications shall be:
+ 1) SMUX_DISCONNECTED notification with @metadata->is_ssr == 1
+ 2) SMUX_WRITE_FAIL for each packet in TX queue
+ 3) SMUX_READ_FAIL for any RX packet in progress
+
+After the completion of the sequence, the client should call msm_smux_close()
+followed by a call to msm_smux_open() to re-open the port.
+
+
+Debug / Testing
+---------------
+
+Several debugfs nodes will be exported under the n_gsm directory for
+testing and debugging.
+ * tbl - prints table of logical channels
+ * stats - prints transfer statistics
+ * enable_local_loopback - echo LCID to enable loopback
+ * disable_local_loopback - echo LCID to enable loopback
+ * enable_remote_loopback - echo LCID to enable loopback
+ * disable_remote_loopback - echo LCID to enable loopback
+
+Driver parameters
+=================
+
+A module parameter called debug_mask will be exported to allow a user
+to set the log level for debugging.
+
+Config options
+==============
+
+No configuration options are planned.
+
+Dependencies
+============
+
+The msm_rmnet_smux and smux_ctl drivers are part of this project and
+are used to interface with the Linux TCP/IP framework and user space.
+
+The physical transport is the HS UART driver which will be extended to
+add a kernel API (the current interface is a TTY interface).
+
+Initialization of dependency drivers is handled using the platform
+device framework. When SMUX is loaded as a line discipline of the TTY
+Framework, it will register separate device drivers with the following
+device names:
+ * SMUX_CTL
+ * SMUX_RMNET
+ * SMUX_DUN_DATA_HSUART
+ * SMUX_RMNET_DATA_HSUART
+ * SMUX_RMNET_CTL_HSUART
+ * SMUX_DIAG
+
+The drivers are removed when SMUX is unloaded from the line discipline.
+
+
+User space utilities
+====================
+
+No userspace utilities are planned aside from testing and example
+applications.
+
+Known issues
+============
+
+None.
+
+To do
+=====
+Once completed, benchmark to determine if FIFO packet scheduling is
+sufficient or if a different scheduling algorithm such as deficit
+round-robin or deficit FIFO scheduling is needed to fairly handle
+variable-sized packets.
diff --git a/Documentation/devicetree/bindings/arm/msm/core_sleep_status.txt b/Documentation/devicetree/bindings/arm/msm/core_sleep_status.txt
new file mode 100644
index 0000000..6ac80f1
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/core_sleep_status.txt
@@ -0,0 +1,29 @@
+* MSM Sleep status
+
+MSM Sleep status device is used to check the power collapsed status of a
+offlined core. The core that initiates the hotplug would wait on the
+sleep status device before CPU_DEAD notifications are sent out. Some hardware
+devices require that the offlined core is power collapsed before turning off
+the resources that are used by the offlined core.
+
+This device is dependent on the pm-8x60 device, which configures the low power
+mode of respective cores. The sleep status is only valid when the core enters
+a low power mode. The device is a child node to pm-8x60 node which is documented
+in Documentation/devicetree/bindings/arm/msm/pm-8x60.txt
+
+The required properties of sleep status device are:
+
+- compatible: qcom,cpu-sleep-status
+- reg: physical address of the sleep status register for Core 0
+- qcom,cpu-alias-addr - On MSM chipset, the each cores registers are at a
+ fixed offset each other.
+- qcom,cpu-sleep-status-mask - The bit mask within the status register that
+ indicates the Core's sleep state.
+
+Example:
+ qcom,cpu-sleep-status@f9088008 {
+ compatible = "qcom,cpu-sleep-status";
+ reg = <0xf9088008 0x4>;
+ qcom,cpu-alias-addr = <0x10000>;
+ qcom,sleep-status-mask= <0x80000>;
+ };
diff --git a/Documentation/devicetree/bindings/media/video/msm-vidc.txt b/Documentation/devicetree/bindings/media/video/msm-vidc.txt
index f97e063..bebdb5a 100644
--- a/Documentation/devicetree/bindings/media/video/msm-vidc.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-vidc.txt
@@ -39,6 +39,7 @@
- qcom,buffer-type-tz-usage-table : a key-value pair, mapping a buffer type
(enum hal_buffer) to its corresponding TZ usage. The TZ usages are defined
as "enum cp_mem_usage" in include/linux/msm_ion.h
+- qcom,has-ocmem: indicate the target has ocmem if this property exists
Example:
@@ -55,6 +56,7 @@
<243000 133330000>,
<108000 100000000>,
<36000 50000000>;
+ qcom,has-ocmem;
qcom,hfi = "venus";
qcom,reg-presets = <0x80004 0x1>,
<0x80178 0x00001FFF>;
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index 4cd9f99..a4c05d4 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -96,6 +96,12 @@
is 8, which indicates the rx path used for audio playback
on HDMI device.
+* msm-lsm-client
+
+Required properties:
+
+ - compatible : "qcom,msm-lsm-client"
+
* msm-dai-q6
[First Level Nodes]
diff --git a/arch/arm/boot/dts/msm8226-iommu-domains.dtsi b/arch/arm/boot/dts/msm8226-iommu-domains.dtsi
new file mode 100644
index 0000000..6ea5b9e
--- /dev/null
+++ b/arch/arm/boot/dts/msm8226-iommu-domains.dtsi
@@ -0,0 +1,31 @@
+/* Copyright (c) 2013, The Linux Foundation. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/ {
+ qcom,iommu-domains {
+ compatible = "qcom,iommu-domains";
+
+ venus_domain_ns: qcom,iommu-domain1 {
+ label = "venus_ns";
+ qcom,iommu-contexts = <&venus_ns>;
+ qcom,virtual-addr-pool = <0x40000000 0x3f000000
+ 0x7f000000 0x1000000>;
+ };
+
+ venus_domain_cp: qcom,iommu-domain2 {
+ label = "venus_cp";
+ qcom,iommu-contexts = <&venus_cp>;
+ qcom,virtual-addr-pool = <0x1000000 0x3f000000>;
+ qcom,secure-domain;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/msm8226-pm.dtsi b/arch/arm/boot/dts/msm8226-pm.dtsi
index 2351d9c..34283e8 100644
--- a/arch/arm/boot/dts/msm8226-pm.dtsi
+++ b/arch/arm/boot/dts/msm8226-pm.dtsi
@@ -114,8 +114,8 @@
qcom,lpm-resources@0 {
reg = <0x0>;
qcom,name = "vdd-dig";
- qcom,type = <0x62706d73>; /* "smpb" */
- qcom,id = <0x02>;
+ qcom,type = <0x61706d73>; /* "smpa" */
+ qcom,id = <0x01>;
qcom,key = <0x6e726f63>; /* "corn" */
qcom,init-value = <5>; /* Super Turbo */
};
@@ -123,8 +123,8 @@
qcom,lpm-resources@1 {
reg = <0x1>;
qcom,name = "vdd-mem";
- qcom,type = <0x62706d73>; /* "smpb" */
- qcom,id = <0x01>;
+ qcom,type = <0x616F646C>; /* "ldoa" */
+ qcom,id = <0x03>;
qcom,key = <0x7675>; /* "uv" */
qcom,init-value = <1050000>; /* Super Turbo */
};
diff --git a/arch/arm/boot/dts/msm8226.dtsi b/arch/arm/boot/dts/msm8226.dtsi
index 1f51a37..7a27977 100644
--- a/arch/arm/boot/dts/msm8226.dtsi
+++ b/arch/arm/boot/dts/msm8226.dtsi
@@ -20,7 +20,7 @@
/include/ "msm8226-bus.dtsi"
/include/ "msm8226-mdss.dtsi"
/include/ "msm8226-coresight.dtsi"
-
+/include/ "msm8226-iommu-domains.dtsi"
/ {
model = "Qualcomm MSM 8226";
compatible = "qcom,msm8226";
@@ -56,6 +56,43 @@
clock-frequency = <19200000>;
};
+ qcom,vidc@fdc00000 {
+ compatible = "qcom,msm-vidc";
+ reg = <0xfdc00000 0xff000>;
+ interrupts = <0 44 0>;
+ qcom,load-freq-tbl = <352800 160000000>,
+ <244800 133330000>,
+ <108000 66700000>;
+ qcom,hfi = "venus";
+ qcom,bus-ports = <1>;
+ qcom,reg-presets = <0xE0024 0x0>,
+ <0x80124 0x3>,
+ <0xE0020 0x5555556>,
+ <0x800B0 0x10101001>,
+ <0x800B4 0x00101010>,
+ <0x800C0 0x1010100f>,
+ <0x800C4 0x00101010>,
+ <0x800D0 0x00000010>,
+ <0x800D4 0x00000010>,
+ <0x800D8 0x00000707>;
+ qcom,enc-ddr-ab-ib = <0 0>,
+ <129000 142000>,
+ <384000 422000>,
+ <866000 953000>;
+ qcom,dec-ddr-ab-ib = <0 0>,
+ <103000 134000>,
+ <268000 348000>,
+ <505000 657000>;
+ qcom,iommu-groups = <&venus_domain_ns &venus_domain_cp>;
+ qcom,iommu-group-buffer-types = <0xfff 0x1ff>;
+ qcom,buffer-type-tz-usage-table = <0x1 0x1>,
+ <0x1fe 0x2>;
+ };
+
+ qcom,wfd {
+ compatible = "qcom,msm-wfd";
+ };
+
serial@f991f000 {
compatible = "qcom,msm-lsuart-v14";
reg = <0xf991f000 0x1000>;
@@ -422,7 +459,6 @@
compatible = "qcom,rpm-smd";
rpm-channel-name = "rpm_requests";
rpm-channel-type = <15>; /* SMD_APPS_RPM */
- rpm-standalone;
};
sdcc1: qcom,sdcc@f9824000 {
@@ -465,7 +501,6 @@
/* 190,ee0_krait_hlos_spmi_periph_irq */
/* 187,channel_0_krait_hlos_trans_done_irq */
interrupts = <0 190 0>, <0 187 0>;
- qcom,not-wakeup;
qcom,pmic-arb-ee = <0>;
qcom,pmic-arb-channel = <0>;
};
@@ -652,6 +687,34 @@
compatible = "qcom,tz-log";
reg = <0x0fc5b82c 0x1000>;
};
+
+ jtag_mm0: jtagmm@fc33c000 {
+ compatible = "qcom,jtag-mm";
+ reg = <0xfc33c000 0x1000>,
+ <0xfc330000 0x1000>;
+ reg-names = "etm-base","debug-base";
+ };
+
+ jtag_mm1: jtagmm@fc33d000 {
+ compatible = "qcom,jtag-mm";
+ reg = <0xfc33d000 0x1000>,
+ <0xfc332000 0x1000>;
+ reg-names = "etm-base","debug-base";
+ };
+
+ jtag_mm2: jtagmm@fc33e000 {
+ compatible = "qcom,jtag-mm";
+ reg = <0xfc33e000 0x1000>,
+ <0xfc334000 0x1000>;
+ reg-names = "etm-base","debug-base";
+ };
+
+ jtag_mm3: jtagmm@fc33f000 {
+ compatible = "qcom,jtag-mm";
+ reg = <0xfc33f000 0x1000>,
+ <0xfc336000 0x1000>;
+ reg-names = "etm-base","debug-base";
+ };
};
&gdsc_venus {
diff --git a/arch/arm/boot/dts/msm8610.dtsi b/arch/arm/boot/dts/msm8610.dtsi
index b258506..b78c3af 100644
--- a/arch/arm/boot/dts/msm8610.dtsi
+++ b/arch/arm/boot/dts/msm8610.dtsi
@@ -236,7 +236,6 @@
compatible = "qcom,rpm-smd";
rpm-channel-name = "rpm_requests";
rpm-channel-type = <15>; /* SMD_APPS_RPM */
- rpm-standalone;
};
qcom,msm-mem-hole {
diff --git a/arch/arm/boot/dts/msm8974-cdp.dtsi b/arch/arm/boot/dts/msm8974-cdp.dtsi
index 45cbc89..2c513e8 100644
--- a/arch/arm/boot/dts/msm8974-cdp.dtsi
+++ b/arch/arm/boot/dts/msm8974-cdp.dtsi
@@ -22,6 +22,7 @@
qcom,mdss_dsi_toshiba_720p_video {
status = "ok";
+ qcom,cont-splash-enabled;
};
qcom,mdss_dsi_orise_720p_video {
@@ -358,9 +359,6 @@
&pm8941_chg {
status = "ok";
- qcom,chg-charging-disabled;
- qcom,chg-use-default-batt-values;
-
qcom,chg-chgr@1000 {
status = "ok";
};
@@ -369,10 +367,6 @@
status = "ok";
};
- qcom,chg-bat-if@1200 {
- status = "ok";
- };
-
qcom,chg-usb-chgpth@1300 {
status = "ok";
};
diff --git a/arch/arm/boot/dts/msm8974-fluid.dtsi b/arch/arm/boot/dts/msm8974-fluid.dtsi
index d22c746..b18bf88 100644
--- a/arch/arm/boot/dts/msm8974-fluid.dtsi
+++ b/arch/arm/boot/dts/msm8974-fluid.dtsi
@@ -21,6 +21,7 @@
qcom,mdss_dsi_toshiba_720p_video {
status = "ok";
+ qcom,cont-splash-enabled;
};
qcom,hdmi_tx@fd922100 {
diff --git a/arch/arm/boot/dts/msm8974-liquid.dtsi b/arch/arm/boot/dts/msm8974-liquid.dtsi
index 985b307..a35b9d2 100644
--- a/arch/arm/boot/dts/msm8974-liquid.dtsi
+++ b/arch/arm/boot/dts/msm8974-liquid.dtsi
@@ -370,6 +370,14 @@
hsic,ignore-cal-pad-config;
hsic,strobe-pad-offset = <0x2050>;
hsic,data-pad-offset = <0x2054>;
+
+ qcom,msm-bus,name = "hsic";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,active-only = <0>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <85 512 0 0>,
+ <85 512 40000 160000>;
};
};
};
diff --git a/arch/arm/boot/dts/msm8974-mtp.dtsi b/arch/arm/boot/dts/msm8974-mtp.dtsi
index 380ec20..4f0469c 100644
--- a/arch/arm/boot/dts/msm8974-mtp.dtsi
+++ b/arch/arm/boot/dts/msm8974-mtp.dtsi
@@ -21,6 +21,7 @@
qcom,mdss_dsi_toshiba_720p_video {
status = "ok";
+ qcom,cont-splash-enabled;
};
qcom,hdmi_tx@fd922100 {
diff --git a/arch/arm/boot/dts/msm8974-v1-cdp.dts b/arch/arm/boot/dts/msm8974-v1-cdp.dts
index 8db99b2..33bd1fb 100644
--- a/arch/arm/boot/dts/msm8974-v1-cdp.dts
+++ b/arch/arm/boot/dts/msm8974-v1-cdp.dts
@@ -19,10 +19,6 @@
model = "Qualcomm MSM 8974 CDP";
compatible = "qcom,msm8974-cdp", "qcom,msm8974";
qcom,msm-id = <126 1 0>;
-
- qcom,mdss_dsi_toshiba_720p_video {
- qcom,cont-splash-enabled;
- };
};
&ehci {
diff --git a/arch/arm/boot/dts/msm8974-v1-fluid.dts b/arch/arm/boot/dts/msm8974-v1-fluid.dts
index 683fe18..9fb287c 100644
--- a/arch/arm/boot/dts/msm8974-v1-fluid.dts
+++ b/arch/arm/boot/dts/msm8974-v1-fluid.dts
@@ -20,9 +20,6 @@
compatible = "qcom,msm8974-fluid", "qcom,msm8974";
qcom,msm-id = <126 3 0>;
- qcom,mdss_dsi_toshiba_720p_video {
- qcom,cont-splash-enabled;
- };
};
&pm8941_chg {
diff --git a/arch/arm/boot/dts/msm8974-v1-mtp.dts b/arch/arm/boot/dts/msm8974-v1-mtp.dts
index 0e5d2ae..205ee24 100644
--- a/arch/arm/boot/dts/msm8974-v1-mtp.dts
+++ b/arch/arm/boot/dts/msm8974-v1-mtp.dts
@@ -19,10 +19,6 @@
model = "Qualcomm MSM 8974 MTP";
compatible = "qcom,msm8974-mtp", "qcom,msm8974";
qcom,msm-id = <126 8 0>;
-
- qcom,mdss_dsi_toshiba_720p_video {
- qcom,cont-splash-enabled;
- };
};
&pm8941_chg {
diff --git a/arch/arm/boot/dts/msm8974-v1-pm.dtsi b/arch/arm/boot/dts/msm8974-v1-pm.dtsi
index d8a1444..a0b9be6 100644
--- a/arch/arm/boot/dts/msm8974-v1-pm.dtsi
+++ b/arch/arm/boot/dts/msm8974-v1-pm.dtsi
@@ -420,9 +420,19 @@
qcom,pm-8x60@fe805664 {
compatible = "qcom,pm-8x60";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
reg = <0xfe805664 0x40>;
qcom,pc-mode = "tz_l2_int";
qcom,use-sync-timer;
+
+ qcom,cpu-sleep-status@f9088008 {
+ compatible = "qcom,cpu-sleep-status";
+ reg = <0xf9088008 0x4>;
+ qcom,cpu-alias-addr = <0x10000>;
+ qcom,sleep-status-mask= <0x80000>;
+ };
};
qcom,rpm-log@fc19dc00 {
diff --git a/arch/arm/boot/dts/msm8974-v2-pm.dtsi b/arch/arm/boot/dts/msm8974-v2-pm.dtsi
index e020fa4..2cfb192 100644
--- a/arch/arm/boot/dts/msm8974-v2-pm.dtsi
+++ b/arch/arm/boot/dts/msm8974-v2-pm.dtsi
@@ -417,10 +417,20 @@
qcom,pm-8x60@fe805664 {
compatible = "qcom,pm-8x60";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
reg = <0xfe805664 0x40>;
qcom,pc-mode = "tz_l2_int";
qcom,use-sync-timer;
qcom,saw-turns-off-pll;
+
+ qcom,cpu-sleep-status@f9088008{
+ compatible = "qcom,cpu-sleep-status";
+ reg = <0xf9088008 0x100>;
+ qcom,cpu-alias-addr = <0x10000>;
+ qcom,sleep-status-mask= <0x80000>;
+ };
};
qcom,rpm-log@fc19dc00 {
diff --git a/arch/arm/boot/dts/msm8974.dtsi b/arch/arm/boot/dts/msm8974.dtsi
index ee79827..aa3742f 100644
--- a/arch/arm/boot/dts/msm8974.dtsi
+++ b/arch/arm/boot/dts/msm8974.dtsi
@@ -95,6 +95,7 @@
reg = <0xfdc00000 0xff000>;
interrupts = <0 44 0>;
qcom,hfi = "venus";
+ qcom,has-ocmem;
};
qcom,wfd {
@@ -590,6 +591,7 @@
qcom,audio-routing =
"RX_BIAS", "MCLK",
"LDO_H", "MCLK",
+ "AIF4 MAD", "MCLK",
"AMIC1", "MIC BIAS1 Internal1",
"MIC BIAS1 Internal1", "Handset Mic",
"AMIC2", "MIC BIAS2 External",
@@ -800,6 +802,10 @@
qcom,msm-dai-q6-dev-id = <8>;
};
+ qcom,msm-lsm-client {
+ compatible = "qcom,msm-lsm-client";
+ };
+
qcom,msm-dai-q6 {
compatible = "qcom,msm-dai-q6";
qcom,msm-dai-q6-sb-0-rx {
@@ -852,6 +858,11 @@
qcom,msm-dai-q6-dev-id = <16393>;
};
+ qcom,msm-dai-q6-sb-5-tx {
+ compatible = "qcom,msm-dai-q6-dev";
+ qcom,msm-dai-q6-dev-id = <16395>;
+ };
+
qcom,msm-dai-q6-bt-sco-rx {
compatible = "qcom,msm-dai-q6-dev";
qcom,msm-dai-q6-dev-id = <12288>;
diff --git a/arch/arm/boot/dts/msm9625-v2-cdp.dts b/arch/arm/boot/dts/msm9625-v2-cdp.dts
index 244556d..09a89ab 100644
--- a/arch/arm/boot/dts/msm9625-v2-cdp.dts
+++ b/arch/arm/boot/dts/msm9625-v2-cdp.dts
@@ -42,7 +42,7 @@
wlan0: qca,wlan {
cell-index = <0>;
- compatible = "qca,ar6004-sdio";
+ compatible = "qca,ar6004-hsic";
qca,chip-pwd-l-gpios = <&msmgpio 62 0>;
qca,pm-enable-gpios = <&pm8019_gpios 3 0x0>;
qca,ar6004-vdd-io-supply = <&pm8019_l11>;
diff --git a/arch/arm/boot/dts/msm9625-v2-mtp.dts b/arch/arm/boot/dts/msm9625-v2-mtp.dts
index bf0f539..7949080 100644
--- a/arch/arm/boot/dts/msm9625-v2-mtp.dts
+++ b/arch/arm/boot/dts/msm9625-v2-mtp.dts
@@ -42,7 +42,7 @@
wlan0: qca,wlan {
cell-index = <0>;
- compatible = "qca,ar6004-sdio";
+ compatible = "qca,ar6004-hsic";
qca,chip-pwd-l-gpios = <&msmgpio 62 0>;
qca,pm-enable-gpios = <&pm8019_gpios 3 0x0>;
qca,ar6004-vdd-io-supply = <&pm8019_l11>;
diff --git a/arch/arm/boot/dts/msm9625.dtsi b/arch/arm/boot/dts/msm9625.dtsi
index a11398b..9172029 100644
--- a/arch/arm/boot/dts/msm9625.dtsi
+++ b/arch/arm/boot/dts/msm9625.dtsi
@@ -123,14 +123,17 @@
qcom,msm-bus,vectors-KBps =
<85 512 0 0>,
<85 512 40000 640000>;
+ qcom,pool-64-bit-align;
+ qcom,enable-hbm;
};
qcom,usbbam@f9a44000 {
compatible = "qcom,usb-bam-msm";
- reg = <0xf9a44000 0x11000>;
- reg-names = "hsusb";
- interrupts = <0 135 0>;
- interrupt-names = "hsusb";
+ reg = <0xf9a44000 0x11000>,
+ <0xf9a04000 0x11000>;
+ reg-names = "hsusb", "hsic";
+ interrupts = <0 135 0 0 255 0>;
+ interrupt-names = "hsusb", "hsic";
qcom,usb-bam-num-pipes = <16>;
qcom,ignore-core-reset-ack;
qcom,disable-clk-gating;
@@ -179,6 +182,66 @@
qcom,descriptor-fifo-offset = <0x4000>;
qcom,descriptor-fifo-size = <0x400>;
};
+ qcom,pipe3 {
+ label = "hsic-ipa-in-0";
+ qcom,usb-bam-mem-type = <2>;
+ qcom,bam-type = <2>;
+ qcom,dir = <1>;
+ qcom,pipe-num = <0>;
+ qcom,peer-bam = <2>;
+ qcom,dst-bam-physical-address = <0xf9a04000>;
+ qcom,dst-bam-pipe-index = <3>;
+ qcom,data-fifo-size = <0xD480>;
+ qcom,descriptor-fifo-size = <0x1A80>;
+ };
+ qcom,pipe4 {
+ label = "hsic-ipa-in-1";
+ qcom,bam-type = <2>;
+ qcom,dir = <1>;
+ qcom,pipe-num = <1>;
+ qcom,peer-bam = <2>;
+ qcom,usb-bam-mem-type = <2>;
+ qcom,dst-bam-physical-address = <0xf9a04000>;
+ qcom,dst-bam-pipe-index = <4>;
+ qcom,data-fifo-size = <0xD480>;
+ qcom,descriptor-fifo-size = <0x1A80>;
+ };
+ qcom,pipe5 {
+ label = "hsic-ipa-in-2";
+ qcom,usb-bam-mem-type = <2>;
+ qcom,bam-type = <2>;
+ qcom,dir = <1>;
+ qcom,pipe-num = <2>;
+ qcom,peer-bam = <2>;
+ qcom,dst-bam-physical-address = <0xf9a04000>;
+ qcom,dst-bam-pipe-index = <5>;
+ qcom,data-fifo-size = <0xD480>;
+ qcom,descriptor-fifo-size = <0x1A80>;
+ };
+ qcom,pipe6 {
+ label = "hsic-ipa-in-3";
+ qcom,usb-bam-mem-type = <2>;
+ qcom,bam-type = <2>;
+ qcom,dir = <1>;
+ qcom,pipe-num = <3>;
+ qcom,peer-bam = <2>;
+ qcom,dst-bam-physical-address = <0xf9a04000>;
+ qcom,dst-bam-pipe-index = <6>;
+ qcom,data-fifo-size = <0xD480>;
+ qcom,descriptor-fifo-size = <0x1A80>;
+ };
+ qcom,pipe7 {
+ label = "hsic-ipa-out-0";
+ qcom,usb-bam-mem-type = <2>;
+ qcom,bam-type = <2>;
+ qcom,dir = <0>;
+ qcom,pipe-num = <0>;
+ qcom,peer-bam = <2>;
+ qcom,src-bam-physical-address = <0xf9a04000>;
+ qcom,src-bam-pipe-index = <7>;
+ qcom,data-fifo-size = <0xD480>;
+ qcom,descriptor-fifo-size = <0x1A80>;
+ };
};
qcom,nand@f9ac0000 {
diff --git a/arch/arm/configs/msm8610_defconfig b/arch/arm/configs/msm8610_defconfig
index d4abef2..cfdff6f 100644
--- a/arch/arm/configs/msm8610_defconfig
+++ b/arch/arm/configs/msm8610_defconfig
@@ -182,6 +182,7 @@
CONFIG_NET_CLS_FW=y
CONFIG_SYNC=y
CONFIG_SW_SYNC=y
+CONFIG_CMA=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_MD=y
@@ -333,6 +334,7 @@
CONFIG_CORESIGHT_FUNNEL=y
CONFIG_CORESIGHT_REPLICATOR=y
CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_ETM=y
CONFIG_CORESIGHT_EVENT=m
CONFIG_ENABLE_DEFAULT_TRACERS=y
CONFIG_PM_WAKELOCKS=y
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index b65016a..f7d993f 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -463,6 +463,7 @@
select ARM_HAS_SG_CHAIN
select REGULATOR
select MSM_RPM_REGULATOR_SMD
+ select MSM_JTAG_MM if CORESIGHT_ETM
endmenu
choice
diff --git a/arch/arm/mach-msm/clock-pll.c b/arch/arm/mach-msm/clock-pll.c
index c82058b..d2be1f9 100644
--- a/arch/arm/mach-msm/clock-pll.c
+++ b/arch/arm/mach-msm/clock-pll.c
@@ -529,6 +529,18 @@
BUG();
}
+ if (!pll_clk_is_enabled(c))
+ return HANDOFF_DISABLED_CLK;
+
+ /*
+ * Do not call pll_clk_enable() since that function can assume
+ * the PLL is not in use when it's called.
+ */
+ remote_spin_lock(&pll_lock);
+ pll_control->pll[PLL_BASE + pll->id].votes |= BIT(1);
+ pll_control->pll[PLL_BASE + pll->id].on = 1;
+ remote_spin_unlock(&pll_lock);
+
return HANDOFF_ENABLED_CLK;
}
@@ -572,10 +584,22 @@
spin_unlock_irqrestore(&soft_vote_lock, flags);
}
+static enum handoff pll_acpu_vote_clk_handoff(struct clk *c)
+{
+ if (pll_vote_clk_handoff(c) == HANDOFF_DISABLED_CLK)
+ return HANDOFF_DISABLED_CLK;
+
+ if (pll_acpu_vote_clk_enable(c))
+ return HANDOFF_DISABLED_CLK;
+
+ return HANDOFF_ENABLED_CLK;
+}
+
struct clk_ops clk_ops_pll_acpu_vote = {
.enable = pll_acpu_vote_clk_enable,
.disable = pll_acpu_vote_clk_disable,
.is_enabled = pll_vote_clk_is_enabled,
+ .handoff = pll_acpu_vote_clk_handoff,
};
static void __init __set_fsm_mode(void __iomem *mode_reg,
diff --git a/arch/arm/mach-msm/hotplug.c b/arch/arm/mach-msm/hotplug.c
index 5cd5057..0e97c27 100644
--- a/arch/arm/mach-msm/hotplug.c
+++ b/arch/arm/mach-msm/hotplug.c
@@ -70,6 +70,11 @@
int platform_cpu_kill(unsigned int cpu)
{
+ int ret;
+
+ ret = msm_pm_wait_cpu_shutdown(cpu);
+ if (ret)
+ return 0;
return 1;
}
diff --git a/arch/arm/mach-msm/include/mach/qdsp6v2/apr.h b/arch/arm/mach-msm/include/mach/qdsp6v2/apr.h
index 59908e6..c21f6e5 100644
--- a/arch/arm/mach-msm/include/mach/qdsp6v2/apr.h
+++ b/arch/arm/mach-msm/include/mach/qdsp6v2/apr.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2013, The Linux Foundation. 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 and
@@ -82,6 +82,7 @@
#define APR_SVC_ADSP_CVS 0x0A
#define APR_SVC_ADSP_CVP 0x0B
#define APR_SVC_USM 0x0C
+#define APR_SVC_LSM 0x0D
#define APR_SVC_VIDC 0x16
#define APR_SVC_MAX 0x17
diff --git a/arch/arm/mach-msm/jtag-mm.c b/arch/arm/mach-msm/jtag-mm.c
index af05995..8a67614 100644
--- a/arch/arm/mach-msm/jtag-mm.c
+++ b/arch/arm/mach-msm/jtag-mm.c
@@ -124,6 +124,8 @@
#define DBGBCRn(n) (0x140 + (n * 4))
#define DBGWVRn(n) (0x180 + (n * 4))
#define DBGWCRn(n) (0x1C0 + (n * 4))
+#define DBGOSLAR (0x300)
+#define DBGOSLSR (0x304)
#define DBGPRCR (0x310)
#define DBGITMISCOUT (0xEF8)
#define DBGITMISCIN (0xEFC)
@@ -131,6 +133,7 @@
#define DBGCLAIMCLR (0xFA4)
#define DBGDSCR_MASK (0x6C30FC3C)
+#define OSLOCK_MAGIC (0xC5ACCE55)
#define MAX_DBG_STATE_SIZE (90)
#define MAX_ETM_STATE_SIZE (78)
@@ -140,6 +143,7 @@
#define ARCH_V3_5 (0x25)
#define ARM_DEBUG_ARCH_V7B (0x3)
+#define ARM_DEBUG_ARCH_V7p1 (0x5)
#define etm_write(etm, val, off) \
__raw_writel(val, etm->base + off)
@@ -221,12 +225,40 @@
uint8_t nr_ctxid_cmp;
struct etm_cpu_ctx *cpu_ctx[NR_CPUS];
bool save_restore_enabled[NR_CPUS];
+ bool os_lock_present;
};
static struct etm_ctx etm;
static struct clk *clock[NR_CPUS];
+static void etm_os_lock(struct etm_cpu_ctx *etmdata)
+{
+ uint32_t count;
+
+ if (etm.os_lock_present) {
+ etm_write(etmdata, OSLOCK_MAGIC, ETMOSLAR);
+ /* Ensure OS lock is set before proceeding */
+ mb();
+ for (count = TIMEOUT_US; BVAL(etm_read(etmdata, ETMSR), 1) != 1
+ && count > 0; count--)
+ udelay(1);
+ if (count == 0)
+ pr_err_ratelimited(
+ "timeout while setting prog bit, ETMSR: %#x\n",
+ etm_read(etmdata, ETMSR));
+ }
+}
+
+static void etm_os_unlock(struct etm_cpu_ctx *etmdata)
+{
+ if (etm.os_lock_present) {
+ /* Ensure all writes are complete before clearing OS lock */
+ mb();
+ etm_write(etmdata, 0x0, ETMOSLAR);
+ }
+}
+
static void etm_set_pwrdwn(struct etm_cpu_ctx *etmdata)
{
uint32_t etmcr;
@@ -273,6 +305,8 @@
switch (etm.arch) {
case ETM_ARCH_V3_5:
+ etm_os_lock(etmdata);
+
etmdata->state[i++] = etm_read(etmdata, ETMTRIGGER);
etmdata->state[i++] = etm_read(etmdata, ETMASICCTLR);
etmdata->state[i++] = etm_read(etmdata, ETMSR);
@@ -346,6 +380,8 @@
switch (etm.arch) {
case ETM_ARCH_V3_5:
+ etm_os_lock(etmdata);
+
etm_clr_pwrdwn(etmdata);
etm_write(etmdata, etmdata->state[i++], ETMTRIGGER);
etm_write(etmdata, etmdata->state[i++], ETMASICCTLR);
@@ -406,6 +442,8 @@
* bit
*/
etm_write(etmdata, etmdata->state[i++], ETMCR);
+
+ etm_os_unlock(etmdata);
break;
default:
pr_err_ratelimited("unsupported etm arch %d in %s\n", etm.arch,
@@ -422,22 +460,46 @@
i = 0;
DBG_UNLOCK(dbgdata);
- dbgdata->state[i++] = dbg_read(dbgdata, DBGWFAR);
- dbgdata->state[i++] = dbg_read(dbgdata, DBGVCR);
- for (j = 0; j < dbg.nr_bp; j++) {
- dbgdata->state[i++] = dbg_read(dbgdata, DBGBVRn(j));
- dbgdata->state[i++] = dbg_read(dbgdata, DBGBCRn(j));
+ switch (dbg.arch) {
+ case ARM_DEBUG_ARCH_V7B:
+ dbgdata->state[i++] = dbg_read(dbgdata, DBGWFAR);
+ dbgdata->state[i++] = dbg_read(dbgdata, DBGVCR);
+ for (j = 0; j < dbg.nr_bp; j++) {
+ dbgdata->state[i++] = dbg_read(dbgdata, DBGBVRn(j));
+ dbgdata->state[i++] = dbg_read(dbgdata, DBGBCRn(j));
+ }
+ for (j = 0; j < dbg.nr_wp; j++) {
+ dbgdata->state[i++] = dbg_read(dbgdata, DBGWVRn(j));
+ dbgdata->state[i++] = dbg_read(dbgdata, DBGWCRn(j));
+ }
+ dbgdata->state[i++] = dbg_read(dbgdata, DBGPRCR);
+ dbgdata->state[i++] = dbg_read(dbgdata, DBGDTRTXext);
+ dbgdata->state[i++] = dbg_read(dbgdata, DBGDTRRXext);
+ dbgdata->state[i++] = dbg_read(dbgdata, DBGDSCRext);
+ break;
+ case ARM_DEBUG_ARCH_V7p1:
+ /* Set OS Lock */
+ dbg_write(dbgdata, OSLOCK_MAGIC, DBGOSLAR);
+ /* Ensure OS lock is set before proceeding */
+ mb();
+
+ dbgdata->state[i++] = dbg_read(dbgdata, DBGWFAR);
+ dbgdata->state[i++] = dbg_read(dbgdata, DBGVCR);
+ for (j = 0; j < dbg.nr_bp; j++) {
+ dbgdata->state[i++] = dbg_read(dbgdata, DBGBVRn(j));
+ dbgdata->state[i++] = dbg_read(dbgdata, DBGBCRn(j));
+ }
+ for (j = 0; j < dbg.nr_wp; j++) {
+ dbgdata->state[i++] = dbg_read(dbgdata, DBGWVRn(j));
+ dbgdata->state[i++] = dbg_read(dbgdata, DBGWCRn(j));
+ }
+ dbgdata->state[i++] = dbg_read(dbgdata, DBGPRCR);
+ dbgdata->state[i++] = dbg_read(dbgdata, DBGCLAIMCLR);
+ dbgdata->state[i++] = dbg_read(dbgdata, DBGDTRTXext);
+ dbgdata->state[i++] = dbg_read(dbgdata, DBGDTRRXext);
+ dbgdata->state[i++] = dbg_read(dbgdata, DBGDSCRext);
+ break;
}
- for (j = 0; j < dbg.nr_wp; j++) {
- dbgdata->state[i++] = dbg_read(dbgdata, DBGWVRn(j));
- dbgdata->state[i++] = dbg_read(dbgdata, DBGWCRn(j));
- }
- dbgdata->state[i++] = dbg_read(dbgdata, DBGPRCR);
- dbgdata->state[i++] = dbg_read(dbgdata, DBGCLAIMSET);
- dbgdata->state[i++] = dbg_read(dbgdata, DBGCLAIMCLR);
- dbgdata->state[i++] = dbg_read(dbgdata, DBGDTRTXext);
- dbgdata->state[i++] = dbg_read(dbgdata, DBGDTRRXext);
- dbgdata->state[i++] = dbg_read(dbgdata, DBGDSCRext);
DBG_LOCK(dbgdata);
}
@@ -449,23 +511,55 @@
i = 0;
DBG_UNLOCK(dbgdata);
- dbg_write(dbgdata, dbgdata->state[i++], DBGWFAR);
- dbg_write(dbgdata, dbgdata->state[i++], DBGVCR);
- for (j = 0; j < dbg.nr_bp; j++) {
- dbg_write(dbgdata, dbgdata->state[i++], DBGBVRn(j));
- dbg_write(dbgdata, dbgdata->state[i++], DBGBCRn(j));
- }
- for (j = 0; j < dbg.nr_wp; j++) {
- dbg_write(dbgdata, dbgdata->state[i++], DBGWVRn(j));
- dbg_write(dbgdata, dbgdata->state[i++], DBGWCRn(j));
- }
- dbg_write(dbgdata, dbgdata->state[i++], DBGPRCR);
- dbg_write(dbgdata, dbgdata->state[i++], DBGCLAIMSET);
- dbg_write(dbgdata, dbgdata->state[i++], DBGCLAIMCLR);
- dbg_write(dbgdata, dbgdata->state[i++], DBGDTRTXext);
- dbg_write(dbgdata, dbgdata->state[i++], DBGDTRRXext);
- dbg_write(dbgdata, dbgdata->state[i++] & DBGDSCR_MASK,
+ switch (dbg.arch) {
+ case ARM_DEBUG_ARCH_V7B:
+ dbg_write(dbgdata, dbgdata->state[i++], DBGWFAR);
+ dbg_write(dbgdata, dbgdata->state[i++], DBGVCR);
+ for (j = 0; j < dbg.nr_bp; j++) {
+ dbg_write(dbgdata, dbgdata->state[i++], DBGBVRn(j));
+ dbg_write(dbgdata, dbgdata->state[i++], DBGBCRn(j));
+ }
+ for (j = 0; j < dbg.nr_wp; j++) {
+ dbg_write(dbgdata, dbgdata->state[i++], DBGWVRn(j));
+ dbg_write(dbgdata, dbgdata->state[i++], DBGWCRn(j));
+ }
+ dbg_write(dbgdata, dbgdata->state[i++], DBGPRCR);
+ dbg_write(dbgdata, dbgdata->state[i++], DBGDTRTXext);
+ dbg_write(dbgdata, dbgdata->state[i++], DBGDTRRXext);
+ dbg_write(dbgdata, dbgdata->state[i++] & DBGDSCR_MASK,
DBGDSCRext);
+ break;
+ case ARM_DEBUG_ARCH_V7p1:
+ /* Set OS lock. Lock will already be set after power collapse
+ * but this write is included to ensure it is set.
+ */
+ dbg_write(dbgdata, OSLOCK_MAGIC, DBGOSLAR);
+ /* Ensure OS lock is set before proceeding */
+ mb();
+
+ dbg_write(dbgdata, dbgdata->state[i++], DBGWFAR);
+ dbg_write(dbgdata, dbgdata->state[i++], DBGVCR);
+ for (j = 0; j < dbg.nr_bp; j++) {
+ dbg_write(dbgdata, dbgdata->state[i++], DBGBVRn(j));
+ dbg_write(dbgdata, dbgdata->state[i++], DBGBCRn(j));
+ }
+ for (j = 0; j < dbg.nr_wp; j++) {
+ dbg_write(dbgdata, dbgdata->state[i++], DBGWVRn(j));
+ dbg_write(dbgdata, dbgdata->state[i++], DBGWCRn(j));
+ }
+ dbg_write(dbgdata, dbgdata->state[i++], DBGPRCR);
+ dbg_write(dbgdata, dbgdata->state[i++], DBGCLAIMSET);
+ dbg_write(dbgdata, dbgdata->state[i++], DBGDTRTXext);
+ dbg_write(dbgdata, dbgdata->state[i++], DBGDTRRXext);
+ dbg_write(dbgdata, dbgdata->state[i++] & DBGDSCR_MASK,
+ DBGDSCRext);
+ /* Ensure all writes are completing before clearing OS lock */
+ mb();
+ dbg_write(dbgdata, 0x0, DBGOSLAR);
+ /* Ensure OS lock is cleared before proceeding */
+ mb();
+ break;
+ }
DBG_LOCK(dbgdata);
}
@@ -529,6 +623,17 @@
return true;
}
+static void __devinit etm_os_lock_init(struct etm_cpu_ctx *etmdata)
+{
+ uint32_t etmoslsr;
+
+ etmoslsr = etm_read(etmdata, ETMOSLSR);
+ if (!BVAL(etmoslsr, 0) && !BVAL(etmoslsr, 3))
+ etm.os_lock_present = false;
+ else
+ etm.os_lock_present = true;
+}
+
static void __devinit etm_init_arch_data(void *info)
{
uint32_t etmidr;
@@ -541,6 +646,8 @@
*/
ETM_UNLOCK(etmdata);
+ etm_os_lock_init(etmdata);
+
etm_clr_pwrdwn(etmdata);
/* Set prog bit. It will be set from reset but this is included to
* ensure it is set
@@ -578,7 +685,7 @@
etm.cpu_ctx[cpu] = etmdata;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "etm-base");
etmdata->base = devm_ioremap(dev, res->start, resource_size(res));
if (!etmdata->base)
@@ -590,8 +697,11 @@
if (!etmdata->state)
return -ENOMEM;
- smp_call_function_single(0, etm_init_arch_data, etmdata, 1);
-
+ if (cpu == 0) {
+ if (smp_call_function_single(cpu, etm_init_arch_data,
+ etmdata, 1))
+ dev_err(dev, "Jtagmm: ETM arch init failed\n");
+ }
if (etm_arch_supported(etm.arch)) {
if (scm_get_feat_version(TZ_DBG_ETM_FEAT_ID) < TZ_DBG_ETM_VER)
etm.save_restore_enabled[cpu] = true;
@@ -606,6 +716,7 @@
{
switch (arch) {
case ARM_DEBUG_ARCH_V7B:
+ case ARM_DEBUG_ARCH_V7p1:
break;
default:
return false;
@@ -642,7 +753,7 @@
dbg.cpu_ctx[cpu] = dbgdata;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "debug-base");
dbgdata->base = devm_ioremap(dev, res->start, resource_size(res));
if (!dbgdata->base)
@@ -654,8 +765,11 @@
if (!dbgdata->state)
return -ENOMEM;
- smp_call_function_single(0, dbg_init_arch_data, dbgdata, 1);
-
+ if (cpu == 0) {
+ if (smp_call_function_single(cpu, dbg_init_arch_data,
+ dbgdata, 1))
+ dev_err(dev, "Jtagmm: Dbg arch init failed\n");
+ }
if (dbg_arch_supported(dbg.arch)) {
if (scm_get_feat_version(TZ_DBG_ETM_FEAT_ID) < TZ_DBG_ETM_VER)
dbg.save_restore_enabled[cpu] = true;
diff --git a/arch/arm/mach-msm/msm_rtb.c b/arch/arm/mach-msm/msm_rtb.c
index e797e2e..fdf39be 100644
--- a/arch/arm/mach-msm/msm_rtb.c
+++ b/arch/arm/mach-msm/msm_rtb.c
@@ -55,7 +55,7 @@
struct msm_rtb_state {
struct msm_rtb_layout *rtb;
- unsigned long phys;
+ phys_addr_t phys;
int nentries;
int size;
int enabled;
diff --git a/arch/arm/mach-msm/pm-8x60.c b/arch/arm/mach-msm/pm-8x60.c
index a22d664..717c057 100644
--- a/arch/arm/mach-msm/pm-8x60.c
+++ b/arch/arm/mach-msm/pm-8x60.c
@@ -25,7 +25,9 @@
#include <linux/smp.h>
#include <linux/suspend.h>
#include <linux/tick.h>
+#include <linux/delay.h>
#include <linux/platform_device.h>
+#include <linux/of_platform.h>
#include <linux/regulator/krait-regulator.h>
#include <mach/msm_iomap.h>
#include <mach/socinfo.h>
@@ -127,6 +129,7 @@
static bool msm_pm_retention_calls_tz;
static uint32_t msm_pm_max_sleep_time;
static bool msm_no_ramp_down_pc;
+static struct msm_pm_sleep_status_data *msm_pm_slp_sts;
static int msm_pm_get_pc_mode(struct device_node *node,
const char *key, uint32_t *pc_mode_val)
@@ -953,6 +956,32 @@
return sleep_mode;
}
+int msm_pm_wait_cpu_shutdown(unsigned int cpu)
+{
+ int timeout = 10;
+
+ if (!msm_pm_slp_sts)
+ return 0;
+ if (!msm_pm_slp_sts[cpu].base_addr)
+ return 0;
+ while (timeout--) {
+ /*
+ * Check for the SPM of the core being hotplugged to set
+ * its sleep state.The SPM sleep state indicates that the
+ * core has been power collapsed.
+ */
+ int acc_sts = __raw_readl(msm_pm_slp_sts[cpu].base_addr);
+
+ if (acc_sts & msm_pm_slp_sts[cpu].mask)
+ return 0;
+ usleep(100);
+ }
+
+ pr_info("%s(): Timed out waiting for CPU %u SPM to enter sleep state",
+ __func__, cpu);
+ return -EBUSY;
+}
+
void msm_pm_cpu_enter_lowpower(unsigned int cpu)
{
int i;
@@ -1109,6 +1138,93 @@
.enter = msm_pm_enter,
.valid = suspend_valid_only_mem,
};
+static int __devinit msm_cpu_status_probe(struct platform_device *pdev)
+{
+ struct msm_pm_sleep_status_data *pdata;
+ char *key;
+ u32 cpu;
+
+ if (!pdev)
+ return -EFAULT;
+
+ msm_pm_slp_sts =
+ kzalloc(sizeof(*msm_pm_slp_sts) * num_possible_cpus(),
+ GFP_KERNEL);
+
+ if (!msm_pm_slp_sts)
+ return -ENOMEM;
+
+ if (pdev->dev.of_node) {
+ struct resource *res;
+ u32 offset;
+ int rc;
+ u32 mask;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ goto fail_free_mem;
+
+ key = "qcom,cpu-alias-addr";
+ rc = of_property_read_u32(pdev->dev.of_node, key, &offset);
+
+ if (rc)
+ goto fail_free_mem;
+
+ key = "qcom,sleep-status-mask";
+ rc = of_property_read_u32(pdev->dev.of_node, key,
+ &mask);
+ if (rc)
+ goto fail_free_mem;
+
+ for_each_possible_cpu(cpu) {
+ msm_pm_slp_sts[cpu].base_addr =
+ ioremap(res->start + cpu * offset,
+ resource_size(res));
+ msm_pm_slp_sts[cpu].mask = mask;
+
+ if (!msm_pm_slp_sts[cpu].base_addr)
+ goto failed_of_node;
+ }
+
+ } else {
+ pdata = pdev->dev.platform_data;
+ if (!pdev->dev.platform_data)
+ goto fail_free_mem;
+
+ for_each_possible_cpu(cpu) {
+ msm_pm_slp_sts[cpu].base_addr =
+ pdata->base_addr + cpu * pdata->cpu_offset;
+ msm_pm_slp_sts[cpu].mask = pdata->mask;
+ }
+ }
+
+ return 0;
+
+failed_of_node:
+ pr_info("%s(): Failed to key=%s\n", __func__, key);
+ for_each_possible_cpu(cpu) {
+ if (msm_pm_slp_sts[cpu].base_addr)
+ iounmap(msm_pm_slp_sts[cpu].base_addr);
+ }
+fail_free_mem:
+ kfree(msm_pm_slp_sts);
+ return -EINVAL;
+
+};
+
+static struct of_device_id msm_slp_sts_match_tbl[] = {
+ {.compatible = "qcom,cpu-sleep-status"},
+ {},
+};
+
+static struct platform_driver msm_cpu_status_driver = {
+ .probe = msm_cpu_status_probe,
+ .driver = {
+ .name = "cpu_slp_status",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_slp_sts_match_tbl,
+ },
+};
static int __devinit msm_pm_init(void)
{
@@ -1378,6 +1494,9 @@
pm_8x60_probe_done:
msm_pm_init();
+ if (pdev->dev.of_node)
+ of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+
return ret;
}
@@ -1397,6 +1516,16 @@
static int __init msm_pm_8x60_init(void)
{
+ int rc;
+
+ rc = platform_driver_register(&msm_cpu_status_driver);
+
+ if (rc) {
+ pr_err("%s(): failed to register driver %s\n", __func__,
+ msm_cpu_status_driver.driver.name);
+ return rc;
+ }
+
return platform_driver_register(&msm_pm_8x60_driver);
}
device_initcall(msm_pm_8x60_init);
diff --git a/arch/arm/mach-msm/pm.h b/arch/arm/mach-msm/pm.h
index 793b778..8a043d8 100644
--- a/arch/arm/mach-msm/pm.h
+++ b/arch/arm/mach-msm/pm.h
@@ -135,9 +135,11 @@
#ifdef CONFIG_MSM_PM8X60
void msm_pm_set_rpm_wakeup_irq(unsigned int irq);
void msm_pm_set_sleep_ops(struct msm_pm_sleep_ops *ops);
+int msm_pm_wait_cpu_shutdown(unsigned int cpu);
#else
static inline void msm_pm_set_rpm_wakeup_irq(unsigned int irq) {}
static inline void msm_pm_set_sleep_ops(struct msm_pm_sleep_ops *ops) {}
+static inline int msm_pm_wait_cpu_shutdown(unsigned int cpu) { return 0; }
#endif
#ifdef CONFIG_HOTPLUG_CPU
int msm_platform_secondary_init(unsigned int cpu);
diff --git a/arch/arm/mach-msm/qdsp6v2/apr.c b/arch/arm/mach-msm/qdsp6v2/apr.c
index 4c106c5..6e60db1 100644
--- a/arch/arm/mach-msm/qdsp6v2/apr.c
+++ b/arch/arm/mach-msm/qdsp6v2/apr.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2013, The Linux Foundation. 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 and
@@ -26,6 +26,7 @@
#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/slab.h>
+#include <sound/apr_audio-v2.h>
#include <asm/mach-types.h>
#include <mach/subsystem_restart.h>
#include <mach/msm_smd.h>
@@ -114,6 +115,11 @@
.name = "VIDC",
.idx = 9,
.id = APR_SVC_VIDC,
+ },
+ {
+ .name = "LSM",
+ .idx = 9,
+ .id = APR_SVC_LSM,
.client_id = APR_CLIENT_AUDIO,
},
};
@@ -280,7 +286,6 @@
return -ENETRESET;
}
-
spin_lock_irqsave(&svc->w_lock, flags);
dest_id = svc->dest_id;
client_id = svc->client_id;
@@ -391,7 +396,8 @@
svc == APR_SVC_ADM || svc == APR_SVC_ADSP_CORE ||
svc == APR_SVC_USM ||
svc == APR_SVC_TEST_CLIENT || svc == APR_SVC_ADSP_MVM ||
- svc == APR_SVC_ADSP_CVS || svc == APR_SVC_ADSP_CVP)
+ svc == APR_SVC_ADSP_CVS || svc == APR_SVC_ADSP_CVP ||
+ svc == APR_SVC_LSM)
clnt = APR_CLIENT_AUDIO;
else if (svc == APR_SVC_VIDC)
clnt = APR_CLIENT_AUDIO;
diff --git a/arch/arm/mach-msm/ramdump.h b/arch/arm/mach-msm/ramdump.h
index e43ca12..3a960a1 100644
--- a/arch/arm/mach-msm/ramdump.h
+++ b/arch/arm/mach-msm/ramdump.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. 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 and
@@ -20,6 +20,7 @@
unsigned long size;
};
+#ifdef CONFIG_MSM_SUBSYSTEM_RESTART
void *create_ramdump_device(const char *dev_name, struct device *parent);
void destroy_ramdump_device(void *dev);
int do_ramdump(void *handle, struct ramdump_segment *segments,
@@ -27,4 +28,28 @@
int do_elf_ramdump(void *handle, struct ramdump_segment *segments,
int nsegments);
+#else
+static inline void *create_ramdump_device(const char *dev_name,
+ struct device *parent)
+{
+ return NULL;
+}
+
+static inline void destroy_ramdump_device(void *dev)
+{
+}
+
+static inline int do_ramdump(void *handle, struct ramdump_segment *segments,
+ int nsegments)
+{
+ return -ENODEV;
+}
+
+static inline int do_elf_ramdump(void *handle, struct ramdump_segment *segments,
+ int nsegments)
+{
+ return -ENODEV;
+}
+#endif /* CONFIG_MSM_SUBSYSTEM_RESTART */
+
#endif
diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c
index cffb211..40ef20e 100644
--- a/arch/arm/mach-msm/smd.c
+++ b/arch/arm/mach-msm/smd.c
@@ -49,6 +49,7 @@
#include "smd_private.h"
#include "modem_notifier.h"
+#include "ramdump.h"
#if defined(CONFIG_ARCH_QSD8X50) || defined(CONFIG_ARCH_MSM8X60) \
|| defined(CONFIG_ARCH_MSM8960) || defined(CONFIG_ARCH_FSM9XXX) \
@@ -178,7 +179,10 @@
};
static uint32_t num_smem_areas;
static struct smem_area *smem_areas;
+static struct ramdump_segment *smem_ramdump_segments;
+static void *smem_ramdump_dev;
static void *smem_range_check(phys_addr_t base, unsigned offset);
+static void *smd_dev;
struct interrupt_stat interrupt_stats[NUM_SMD_SUBSYSTEMS];
@@ -977,10 +981,6 @@
SMx_POWER_INFO("%s: starting reset\n", __func__);
- /* release any held spinlocks */
- remote_spin_release(&remote_spinlock, restart_pid);
- remote_spin_release_all(restart_pid);
-
shared = smem_find(ID_CH_ALLOC_TBL, sizeof(*shared) * 64);
if (!shared) {
pr_err("%s: allocation table not initialized\n", __func__);
@@ -3720,6 +3720,7 @@
struct device_node *node;
int ret;
const char *compatible;
+ struct ramdump_segment *ramdump_segments_tmp;
int subnode_num = 0;
resource_size_t irq_out_size;
@@ -3757,13 +3758,33 @@
}
}
+ /* initialize SSR ramdump regions */
+ key = "smem";
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, key);
+ if (!r) {
+ pr_err("%s: missing '%s'\n", __func__, key);
+ return -ENODEV;
+ }
+ ramdump_segments_tmp = kmalloc_array(num_smem_areas + 1,
+ sizeof(struct ramdump_segment), GFP_KERNEL);
+
+ if (!ramdump_segments_tmp) {
+ pr_err("%s: ramdump segment kmalloc failed\n", __func__);
+ ret = -ENOMEM;
+ goto free_smem_areas;
+ }
+ ramdump_segments_tmp[0].address = r->start;
+ ramdump_segments_tmp[0].size = resource_size(r);
+
if (num_smem_areas) {
+
smem_areas = kmalloc(sizeof(struct smem_area) * num_smem_areas,
GFP_KERNEL);
+
if (!smem_areas) {
pr_err("%s: smem areas kmalloc failed\n", __func__);
- num_smem_areas = 0;
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto free_smem_areas;
}
count = 1;
while (1) {
@@ -3775,6 +3796,16 @@
break;
aux_mem_base = r->start;
aux_mem_size = resource_size(r);
+
+ /*
+ * Add to ram-dumps segments.
+ * ramdump_segments_tmp[0] is the main SMEM region,
+ * so auxiliary segments are indexed by count
+ * instead of count - 1.
+ */
+ ramdump_segments_tmp[count].address = aux_mem_base;
+ ramdump_segments_tmp[count].size = aux_mem_size;
+
SMD_DBG("%s: %s = %pa %pa", __func__, temp_string,
&aux_mem_base, &aux_mem_size);
smem_areas[count - 1].phys_addr = aux_mem_base;
@@ -3822,6 +3853,7 @@
++subnode_num;
}
+ smem_ramdump_segments = ramdump_segments_tmp;
return 0;
rollback_subnodes:
@@ -3838,6 +3870,7 @@
}
free_smem_areas:
num_smem_areas = 0;
+ kfree(ramdump_segments_tmp);
kfree(smem_areas);
smem_areas = NULL;
return ret;
@@ -3869,6 +3902,7 @@
__func__);
return ret;
}
+ smd_dev = &pdev->dev;
} else if (pdev->dev.platform_data) {
ret = smd_core_platform_init(pdev);
if (ret) {
@@ -3928,6 +3962,26 @@
__func__, notifier->processor,
notifier->name);
+ remote_spin_release(&remote_spinlock, notifier->processor);
+ remote_spin_release_all(notifier->processor);
+
+ if (smem_ramdump_dev) {
+ int ret;
+
+ SMD_INFO("%s: saving ramdump\n", __func__);
+ /*
+ * XPU protection does not currently allow the
+ * auxiliary memory regions to be dumped. If this
+ * changes, then num_smem_areas + 1 should be passed
+ * into do_elf_ramdump() to dump all regions.
+ */
+ ret = do_elf_ramdump(smem_ramdump_dev,
+ smem_ramdump_segments, 1);
+ if (ret < 0)
+ pr_err("%s: unable to dump smem %d\n", __func__,
+ ret);
+ }
+
smd_channel_reset(notifier->processor);
}
@@ -3940,12 +3994,20 @@
void *handle;
struct restart_notifier_block *nb;
+ smem_ramdump_dev = create_ramdump_device("smem-smd", smd_dev);
+ if (IS_ERR_OR_NULL(smem_ramdump_dev)) {
+ pr_err("%s: Unable to create smem ramdump device.\n",
+ __func__);
+ smem_ramdump_dev = NULL;
+ }
+
for (i = 0; i < ARRAY_SIZE(restart_notifiers); i++) {
nb = &restart_notifiers[i];
handle = subsys_notif_register_notifier(nb->name, &nb->nb);
SMD_DBG("%s: registering notif for '%s', handle=%p\n",
__func__, nb->name, handle);
}
+
return 0;
}
late_initcall(modem_restart_late_init);
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 73fe5d6..0f4d613 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -89,7 +89,7 @@
int n = -1, err = 0;
VERIFY(err, 0 != access_ok(access ? VERIFY_WRITE : VERIFY_READ,
- (void __user *)start, len));
+ (void __user *)start, len));
if (err)
goto bail;
VERIFY(err, 0 != (vma = find_vma(current->mm, start)));
@@ -611,27 +611,28 @@
static int get_dev(struct fastrpc_apps *me, struct fastrpc_device **rdev)
{
struct hlist_head *head;
- struct fastrpc_device *dev = 0;
- struct hlist_node *n;
+ struct fastrpc_device *dev = 0, *devfree = 0;
+ struct hlist_node *pos, *n;
uint32_t h = hash_32(current->tgid, RPC_HASH_BITS);
int err = 0;
spin_lock(&me->hlock);
head = &me->htbl[h];
- hlist_for_each_entry(dev, n, head, hn) {
+ hlist_for_each_entry_safe(dev, pos, n, head, hn) {
if (dev->tgid == current->tgid) {
hlist_del(&dev->hn);
+ devfree = dev;
break;
}
}
spin_unlock(&me->hlock);
- VERIFY(err, dev != 0);
+ VERIFY(err, devfree != 0);
if (err)
goto bail;
- *rdev = dev;
+ *rdev = devfree;
bail:
if (err) {
- free_dev(dev);
+ free_dev(devfree);
err = alloc_dev(rdev);
}
return err;
@@ -756,22 +757,23 @@
struct fastrpc_apps *me = &gfa;
uint32_t h = hash_32(current->tgid, RPC_HASH_BITS);
struct hlist_head *head;
- struct hlist_node *pos;
- struct fastrpc_device *dev;
+ struct hlist_node *pos, *n;
+ struct fastrpc_device *dev, *devfree;
rnext:
- dev = 0;
+ devfree = dev = 0;
spin_lock(&me->hlock);
head = &me->htbl[h];
- hlist_for_each_entry(dev, pos, head, hn) {
+ hlist_for_each_entry_safe(dev, pos, n, head, hn) {
if (dev->tgid == current->tgid) {
hlist_del(&dev->hn);
+ devfree = dev;
break;
}
}
spin_unlock(&me->hlock);
- if (dev) {
- free_dev(dev);
+ if (devfree) {
+ free_dev(devfree);
goto rnext;
}
return;
diff --git a/drivers/devfreq/governor_performance.c b/drivers/devfreq/governor_performance.c
index 574a06b..af75ddd 100644
--- a/drivers/devfreq/governor_performance.c
+++ b/drivers/devfreq/governor_performance.c
@@ -10,6 +10,7 @@
*/
#include <linux/devfreq.h>
+#include "governor.h"
static int devfreq_performance_func(struct devfreq *df,
unsigned long *freq)
@@ -25,8 +26,14 @@
return 0;
}
+static int performance_init(struct devfreq *devfreq)
+{
+ return update_devfreq(devfreq);
+}
+
const struct devfreq_governor devfreq_performance = {
.name = "performance",
+ .init = performance_init,
.get_target_freq = devfreq_performance_func,
.no_central_polling = true,
};
diff --git a/drivers/devfreq/governor_powersave.c b/drivers/devfreq/governor_powersave.c
index d742d4a..fec0cdb 100644
--- a/drivers/devfreq/governor_powersave.c
+++ b/drivers/devfreq/governor_powersave.c
@@ -10,6 +10,7 @@
*/
#include <linux/devfreq.h>
+#include "governor.h"
static int devfreq_powersave_func(struct devfreq *df,
unsigned long *freq)
@@ -22,8 +23,14 @@
return 0;
}
+static int powersave_init(struct devfreq *devfreq)
+{
+ return update_devfreq(devfreq);
+}
+
const struct devfreq_governor devfreq_powersave = {
.name = "powersave",
+ .init = powersave_init,
.get_target_freq = devfreq_powersave_func,
.no_central_polling = true,
};
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index 93b7e5d..cc01d37 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -722,17 +722,23 @@
{
struct kgsl_iommu *iommu = mmu->priv;
struct kgsl_iommu_unit *iommu_unit = &iommu->iommu_units[unit_id];
- int i;
+ int i, j;
+ int found_ctx;
- if (data->iommu_ctx_count > KGSL_IOMMU_MAX_DEVS_PER_UNIT) {
- KGSL_CORE_ERR("Too many iommu devices defined for an "
- "IOMMU unit\n");
- return -EINVAL;
- }
-
- for (i = 0; i < data->iommu_ctx_count; i++) {
- if (!data->iommu_ctxs[i].iommu_ctx_name)
- continue;
+ for (j = 0; j < KGSL_IOMMU_MAX_DEVS_PER_UNIT; j++) {
+ found_ctx = 0;
+ for (i = 0; i < data->iommu_ctx_count; i++) {
+ if (j == data->iommu_ctxs[i].ctx_id) {
+ found_ctx = 1;
+ break;
+ }
+ }
+ if (!found_ctx)
+ break;
+ if (!data->iommu_ctxs[i].iommu_ctx_name) {
+ KGSL_CORE_ERR("Context name invalid\n");
+ return -EINVAL;
+ }
iommu_unit->dev[iommu_unit->dev_count].dev =
msm_iommu_get_ctx(data->iommu_ctxs[i].iommu_ctx_name);
@@ -741,12 +747,6 @@
"device %s\n", data->iommu_ctxs[i].iommu_ctx_name);
return -EINVAL;
}
- if (KGSL_IOMMU_CONTEXT_USER != data->iommu_ctxs[i].ctx_id &&
- KGSL_IOMMU_CONTEXT_PRIV != data->iommu_ctxs[i].ctx_id) {
- KGSL_CORE_ERR("Invalid context ID defined: %d\n",
- data->iommu_ctxs[i].ctx_id);
- return -EINVAL;
- }
iommu_unit->dev[iommu_unit->dev_count].ctx_id =
data->iommu_ctxs[i].ctx_id;
iommu_unit->dev[iommu_unit->dev_count].kgsldev = mmu->device;
@@ -758,6 +758,10 @@
iommu_unit->dev_count++;
}
+ if (!j) {
+ KGSL_CORE_ERR("No ctxts initialized, user ctxt absent\n ");
+ return -EINVAL;
+ }
return 0;
}
diff --git a/drivers/media/platform/msm/dvb/video/mpq_dvb_video.c b/drivers/media/platform/msm/dvb/video/mpq_dvb_video.c
index 46002c3..b9fdc5e 100644
--- a/drivers/media/platform/msm/dvb/video/mpq_dvb_video.c
+++ b/drivers/media/platform/msm/dvb/video/mpq_dvb_video.c
@@ -28,7 +28,7 @@
#include <linux/uaccess.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
-#include <linux/android_pmem.h>
+
#include <linux/clk.h>
#include <linux/timer.h>
#include <mach/msm_subsystem_map.h>
@@ -43,8 +43,6 @@
#define ERR(x...) pr_err(x)
#define MPQ_VID_DEC_NAME "mpq_vidc_dec"
-static unsigned int vidc_mmu_subsystem[] = {
- MSM_SUBSYSTEM_VIDEO};
static char vid_thread_names[DVB_MPQ_NUM_VIDEO_DEVICES][10] = {
"dvb-vid-0",
@@ -1060,10 +1058,7 @@
{
struct vcd_property_hdr vcd_property_hdr;
struct vcd_property_h264_mv_buffer *vcd_h264_mv_buffer = NULL;
- struct msm_mapped_buffer *mapped_buffer = NULL;
u32 vcd_status = VCD_ERR_FAIL;
- u32 len = 0, flags = 0;
- struct file *file;
int rc = 0;
unsigned long ionflag = 0;
unsigned long buffer_size = 0;
@@ -1084,28 +1079,8 @@
vcd_h264_mv_buffer->offset = mv_data->offset;
if (!vcd_get_ion_status()) {
- if (get_pmem_file(vcd_h264_mv_buffer->pmem_fd,
- (unsigned long *) (&(vcd_h264_mv_buffer->
- physical_addr)),
- (unsigned long *) (&vcd_h264_mv_buffer->
- kernel_virtual_addr),
- (unsigned long *) (&len), &file)) {
- ERR("%s(): get_pmem_file failed\n", __func__);
- return -EIO;
- }
- put_pmem_file(file);
- flags = MSM_SUBSYSTEM_MAP_IOVA;
- mapped_buffer = msm_subsystem_map_buffer(
- (unsigned long)vcd_h264_mv_buffer->physical_addr, len,
- flags, vidc_mmu_subsystem,
- sizeof(vidc_mmu_subsystem)/
- sizeof(unsigned int));
- if (IS_ERR(mapped_buffer)) {
- ERR("buffer map failed");
- return PTR_ERR(mapped_buffer);
- }
- vcd_h264_mv_buffer->client_data = (void *) mapped_buffer;
- vcd_h264_mv_buffer->dev_addr = (u8 *)mapped_buffer->iova[0];
+ pr_err("PMEM not available");
+ return -EINVAL;
} else {
client_ctx->h264_mv_ion_handle = ion_import_dma_buf(
client_ctx->user_ion_client,
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
index 65542bc..62158b0 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_debug.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
@@ -18,7 +18,7 @@
int msm_vidc_debug = 0x3;
int msm_fw_debug = 0x18;
int msm_fw_debug_mode = 0x1;
-int msm_fw_low_power_mode = 0x1;
+int msm_fw_low_power_mode = 0x0;
struct debug_buffer {
char ptr[MAX_DBG_BUF_SIZE];
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index 1b7ecf0..fb27b23 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -2002,12 +2002,8 @@
dprintk(VIDC_ERR, "Invalid params: %p\n", device);
return;
}
- if (device->clocks_enabled) {
- cl = &device->resources.clock[VCODEC_CLK];
- clk_disable_unprepare(cl->clk);
- }
- for (i = VCODEC_CLK; i < VCODEC_MAX_CLKS; i++) {
+ for (i = 0; i < VCODEC_MAX_CLKS; i++) {
cl = &device->resources.clock[i];
clk_disable_unprepare(cl->clk);
}
@@ -2022,17 +2018,7 @@
dprintk(VIDC_ERR, "Invalid params: %p\n", device);
return -EINVAL;
}
- if (!device->clocks_enabled) {
- cl = &device->resources.clock[VCODEC_CLK];
- rc = clk_prepare_enable(cl->clk);
- if (rc) {
- dprintk(VIDC_ERR, "Failed to enable clocks\n");
- goto fail_clk_enable;
- } else {
- dprintk(VIDC_DBG, "Clock: %s enabled\n", cl->name);
- }
- }
- for (i = VCODEC_CLK; i < VCODEC_MAX_CLKS; i++) {
+ for (i = 0; i < VCODEC_MAX_CLKS; i++) {
cl = &device->resources.clock[i];
rc = clk_prepare_enable(cl->clk);
if (rc) {
@@ -2672,8 +2658,8 @@
}
if (device->resources.fw.cookie) {
venus_hfi_disable_clks(device);
- subsystem_put(device->resources.fw.cookie);
venus_hfi_iommu_detach(device);
+ subsystem_put(device->resources.fw.cookie);
device->resources.fw.cookie = NULL;
}
}
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
index ef21b7b..f9ae3c2 100644
--- a/drivers/platform/msm/usb_bam.c
+++ b/drivers/platform/msm/usb_bam.c
@@ -416,14 +416,16 @@
if (pipe_connect->mem_type == SYSTEM_MEM) {
pr_debug("%s: Freeing system memory used by PIPE\n", __func__);
- dma_free_coherent(&ctx.usb_bam_pdev->dev,
- sps_connection->data.size,
- sps_connection->data.base,
- sps_connection->data.phys_base);
- dma_free_coherent(&ctx.usb_bam_pdev->dev,
- sps_connection->desc.size,
- sps_connection->desc.base,
- sps_connection->desc.phys_base);
+ if (sps_connection->data.phys_base)
+ dma_free_coherent(&ctx.usb_bam_pdev->dev,
+ sps_connection->data.size,
+ sps_connection->data.base,
+ sps_connection->data.phys_base);
+ if (sps_connection->desc.phys_base)
+ dma_free_coherent(&ctx.usb_bam_pdev->dev,
+ sps_connection->desc.size,
+ sps_connection->desc.base,
+ sps_connection->desc.phys_base);
} else if (pipe_connect->mem_type == USB_PRIVATE_MEM) {
pr_debug("Freeing USB private memory used by BAM PIPE\n");
writel_relaxed(0x0, ctx.qscratch_ram1_reg);
@@ -806,10 +808,11 @@
int ret;
u8 idx;
struct usb_bam_pipe_connect *pipe_connect;
+ struct sps_connect *sps_connection;
if (ipa_params->prod_clnt_hdl) {
/* close USB -> IPA pipe */
- idx = ipa_params->src_idx;
+ idx = ipa_params->dst_idx;
ret = ipa_disconnect(ipa_params->prod_clnt_hdl);
if (ret) {
pr_err("%s: dst pipe disconnection failure\n",
@@ -817,11 +820,20 @@
return ret;
}
pipe_connect = &usb_bam_connections[idx];
- pipe_connect->enabled = 0;
+ sps_connection = &ctx.usb_bam_sps.sps_connections[idx];
+ sps_connection->data.phys_base = 0;
+ sps_connection->desc.phys_base = 0;
+
+ ret = usb_bam_disconnect_pipe(idx);
+ if (ret) {
+ pr_err("%s: failure to disconnect pipe %d\n",
+ __func__, idx);
+ return ret;
+ }
}
if (ipa_params->cons_clnt_hdl) {
/* close IPA -> USB pipe */
- idx = ipa_params->dst_idx;
+ idx = ipa_params->src_idx;
ret = ipa_disconnect(ipa_params->cons_clnt_hdl);
if (ret) {
pr_err("%s: src pipe disconnection failure\n",
@@ -829,7 +841,16 @@
return ret;
}
pipe_connect = &usb_bam_connections[idx];
- pipe_connect->enabled = 0;
+ sps_connection = &ctx.usb_bam_sps.sps_connections[idx];
+ sps_connection->data.phys_base = 0;
+ sps_connection->desc.phys_base = 0;
+
+ ret = usb_bam_disconnect_pipe(idx);
+ if (ret) {
+ pr_err("%s: failure to disconnect pipe %d\n",
+ __func__, idx);
+ return ret;
+ }
}
ipa_rm_release_resource(IPA_CLIENT_USB_PROD);
diff --git a/drivers/power/qpnp-charger.c b/drivers/power/qpnp-charger.c
index eb0d6d4..180ca0d 100644
--- a/drivers/power/qpnp-charger.c
+++ b/drivers/power/qpnp-charger.c
@@ -525,6 +525,8 @@
if (chip->usb_present ^ usb_present) {
chip->usb_present = usb_present;
+ if (!usb_present)
+ qpnp_chg_iusbmax_set(chip, QPNP_CHG_I_MAX_MIN_100);
power_supply_set_present(chip->usb_psy,
chip->usb_present);
}
@@ -1015,17 +1017,15 @@
chip->usb_psy->get_property(chip->usb_psy,
POWER_SUPPLY_PROP_ONLINE, &ret);
- if (ret.intval && qpnp_chg_is_usb_chg_plugged_in(chip)) {
- chip->usb_psy->get_property(chip->usb_psy,
- POWER_SUPPLY_PROP_CURRENT_MAX, &ret);
- qpnp_chg_iusbmax_set(chip, ret.intval / 1000);
- if ((ret.intval / 1000) <= QPNP_CHG_I_MAX_MIN_MA)
+ /* Only honour requests while USB is present */
+ if (qpnp_chg_is_usb_chg_plugged_in(chip)) {
+ if (ret.intval <= 2) {
qpnp_chg_usb_suspend_enable(chip, 1);
- else
+ qpnp_chg_iusbmax_set(chip, QPNP_CHG_I_MAX_MIN_100);
+ } else {
+ qpnp_chg_iusbmax_set(chip, ret.intval / 1000);
qpnp_chg_usb_suspend_enable(chip, 0);
- } else {
- qpnp_chg_iusbmax_set(chip, QPNP_CHG_I_MAX_MIN_100);
- qpnp_chg_usb_suspend_enable(chip, 0);
+ }
}
pr_debug("end of power supply changed\n");
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index 98a8202..c6b3472 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -564,6 +564,8 @@
struct ion_client *iclient = mdss_get_ionclient();
struct mdss_panel_data *pdata;
int ret = 0, off;
+ int mdss_mdp_rev = MDSS_MDP_REG_READ(MDSS_MDP_REG_HW_VERSION);
+ int mdss_v2_intf_off = 0;
off = 0;
@@ -576,9 +578,13 @@
mdss_mdp_ctl_write(ctl, 0, MDSS_MDP_LM_BORDER_COLOR);
off = MDSS_MDP_REG_INTF_OFFSET(ctl->intf_num);
+ if (mdss_mdp_rev == MDSS_MDP_HW_REV_102)
+ mdss_v2_intf_off = 0xEC00;
+
/* wait for 1 VSYNC for the pipe to be unstaged */
msleep(20);
- MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 0);
+ MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_INTF_TIMING_ENGINE_EN -
+ mdss_v2_intf_off, 0);
ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CONT_SPLASH_FINISH,
NULL);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
diff --git a/include/sound/Kbuild b/include/sound/Kbuild
index ba75dca..edadaa9 100644
--- a/include/sound/Kbuild
+++ b/include/sound/Kbuild
@@ -11,3 +11,4 @@
header-y += tlv.h
header-y += compress_params.h
header-y += compress_offload.h
+header-y += lsm_params.h
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h
index bc68c24..f88c817 100644
--- a/include/sound/apr_audio-v2.h
+++ b/include/sound/apr_audio-v2.h
@@ -658,6 +658,11 @@
#define SLIMBUS_3_TX 0x4007
#define SLIMBUS_4_RX 0x4008
#define SLIMBUS_4_TX 0x4009 /* index = 24 */
+#define SLIMBUS_5_RX 0x400a
+#define SLIMBUS_5_TX 0x400b
+#define SLIMBUS_6_RX 0x400c
+#define SLIMBUS_6_TX 0x400d
+#define SLIMBUS_PORT_LAST SLIMBUS_6_TX
#define INT_BT_SCO_RX 0x3000 /* index = 25 */
#define INT_BT_SCO_TX 0x3001 /* index = 26 */
#define INT_BT_A2DP_RX 0x3002 /* index = 27 */
@@ -798,6 +803,15 @@
#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_RX 0x4008
/* SLIMbus Tx port on channel 4. */
#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX 0x4009
+/* SLIMbus Rx port on channel 5. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_RX 0x400a
+/* SLIMbus Tx port on channel 5. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX 0x400b
+/* SLIMbus Rx port on channel 6. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX 0x400c
+/* SLIMbus Tx port on channel 6. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_TX 0x400d
+
/* Generic pseudoport 1. */
#define AFE_PORT_ID_PSEUDOPORT_01 0x8001
/* Generic pseudoport 2. */
@@ -1972,7 +1986,6 @@
*/
} __packed;
-
union afe_port_config {
struct afe_param_id_pcm_cfg pcm;
struct afe_param_id_i2s_cfg i2s;
@@ -6442,6 +6455,56 @@
} __packed;
/* SRS TruMedia end */
+/* LSM Specific */
+#define VW_FEAT_DIM (39)
+
+#define APRV2_IDS_SERVICE_ID_ADSP_LSM_V (0xD)
+#define APRV2_IDS_DOMAIN_ID_ADSP_V (0x4)
+#define APRV2_IDS_DOMAIN_ID_APPS_V (0x5)
+
+#define LSM_SESSION_CMD_SHARED_MEM_MAP_REGIONS (0x00012A7F)
+#define LSM_SESSION_CMDRSP_SHARED_MEM_MAP_REGIONS (0x00012A80)
+#define LSM_SESSION_CMD_SHARED_MEM_UNMAP_REGIONS (0x00012A81)
+#define LSM_SESSION_CMD_OPEN_TX (0x00012A82)
+#define LSM_SESSION_CMD_CLOSE_TX (0x00012A88)
+#define LSM_SESSION_CMD_SET_PARAMS (0x00012A83)
+#define LSM_SESSION_CMD_REGISTER_SOUND_MODEL (0x00012A84)
+#define LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL (0x00012A85)
+#define LSM_SESSION_CMD_START (0x00012A86)
+#define LSM_SESSION_CMD_STOP (0x00012A87)
+
+#define LSM_SESSION_EVENT_DETECTION_STATUS (0x00012B00)
+
+#define LSM_MODULE_ID_VOICE_WAKEUP (0x00012C00)
+#define LSM_PARAM_ID_ENDPOINT_DETECT_THRESHOLD (0x00012C01)
+#define LSM_PARAM_ID_OPERATION_MODE (0x00012C02)
+#define LSM_PARAM_ID_GAIN (0x00012C03)
+#define LSM_PARAM_ID_CONNECT_TO_PORT (0x00012C04)
+#define LSM_PARAM_ID_KEYWORD_DETECT_SENSITIVITY (0x00012C05)
+#define LSM_PARAM_ID_USER_DETECT_SENSITIVITY (0x00012C06)
+#define LSM_PARAM_ID_FEATURE_COMPENSATION_DATA (0x00012C07)
+
+
+/* HW MAD specific */
+#define AFE_MODULE_HW_MAD (0x00010230)
+#define AFE_PARAM_ID_HW_MAD_CFG (0x00010231)
+#define AFE_PARAM_ID_HW_MAD_CTRL (0x00010232)
+#define AFE_PARAM_ID_SLIMBUS_SLAVE_PORT_CFG (0x00010233)
+
+/* SW MAD specific */
+#define AFE_MODULE_SW_MAD (0x0001022D)
+#define AFE_PARAM_ID_SW_MAD_CFG (0x0001022E)
+#define AFE_PARAM_ID_SVM_MODEL (0x0001022F)
+
+/* Commands/Params to pass the codec/slimbus data to DSP */
+#define AFE_SVC_CMD_SET_PARAM (0x000100f3)
+#define AFE_MODULE_CDC_DEV_CFG (0x00010234)
+#define AFE_PARAM_ID_CDC_SLIMBUS_SLAVE_CFG (0x00010235)
+#define AFE_PARAM_ID_CDC_REG_CFG (0x00010236)
+#define AFE_PARAM_ID_CDC_REG_CFG_INIT (0x00010237)
+
+#define AFE_MAX_CDC_REGISTERS_TO_CONFIG (20)
+
/* ERROR CODES */
/* Success. The operation completed with no errors. */
#define ADSP_EOK 0x00000000
@@ -6669,4 +6732,104 @@
uint16_t reserved;
} __packed;
+enum afe_config_type {
+ AFE_SLIMBUS_SLAVE_PORT_CONFIG,
+ AFE_SLIMBUS_SLAVE_CONFIG,
+ AFE_CDC_REGISTERS_CONFIG,
+ AFE_MAX_CONFIG_TYPES,
+};
+
+struct afe_param_slimbus_slave_port_cfg {
+ uint32_t minor_version;
+ uint16_t slimbus_dev_id;
+ uint16_t slave_dev_pgd_la;
+ uint16_t slave_dev_intfdev_la;
+ uint16_t bit_width;
+ uint16_t data_format;
+ uint16_t num_channels;
+ uint16_t slave_port_mapping[AFE_PORT_MAX_AUDIO_CHAN_CNT];
+} __packed;
+
+struct afe_param_cdc_slimbus_slave_cfg {
+ uint32_t minor_version;
+ uint32_t device_enum_addr_lsw;
+ uint32_t device_enum_addr_msw;
+ uint16_t tx_slave_port_offset;
+ uint16_t rx_slave_port_offset;
+} __packed;
+
+struct afe_param_cdc_reg_cfg {
+ uint32_t minor_version;
+ uint32_t reg_logical_addr;
+ uint32_t reg_field_type;
+ uint32_t reg_field_bit_mask;
+ uint16_t reg_bit_width;
+ uint16_t reg_offset_scale;
+} __packed;
+
+struct afe_param_cdc_reg_cfg_data {
+ uint32_t num_registers;
+ struct afe_param_cdc_reg_cfg *reg_data;
+} __packed;
+
+struct afe_svc_cmd_set_param {
+ uint32_t payload_size;
+ uint32_t payload_address_lsw;
+ uint32_t payload_address_msw;
+ uint32_t mem_map_handle;
+} __packed;
+
+struct afe_param_hw_mad_ctrl {
+ uint32_t minor_version;
+ uint16_t mad_type;
+ uint16_t mad_enable;
+} __packed;
+
+struct afe_cmd_hw_mad_ctrl {
+ struct apr_hdr hdr;
+ struct afe_port_cmd_set_param_v2 param;
+ struct afe_port_param_data_v2 pdata;
+ struct afe_param_hw_mad_ctrl payload;
+} __packed;
+
+struct afe_cmd_hw_mad_slimbus_slave_port_cfg {
+ struct apr_hdr hdr;
+ struct afe_port_cmd_set_param_v2 param;
+ struct afe_port_param_data_v2 pdata;
+ struct afe_param_slimbus_slave_port_cfg sb_port_cfg;
+} __packed;
+
+struct afe_cmd_sw_mad_enable {
+ struct apr_hdr hdr;
+ struct afe_port_cmd_set_param_v2 param;
+ struct afe_port_param_data_v2 pdata;
+} __packed;
+
+struct afe_param_cdc_reg_cfg_payload {
+ struct afe_port_param_data_v2 common;
+ struct afe_param_cdc_reg_cfg reg_cfg;
+} __packed;
+
+/*
+ * reg_data's size can be up to AFE_MAX_CDC_REGISTERS_TO_CONFIG
+ */
+struct afe_svc_cmd_cdc_reg_cfg {
+ struct apr_hdr hdr;
+ struct afe_svc_cmd_set_param param;
+ struct afe_param_cdc_reg_cfg_payload reg_data[0];
+} __packed;
+
+struct afe_svc_cmd_init_cdc_reg_cfg {
+ struct apr_hdr hdr;
+ struct afe_svc_cmd_set_param param;
+ struct afe_port_param_data_v2 init;
+} __packed;
+
+struct afe_svc_cmd_sb_slave_cfg {
+ struct apr_hdr hdr;
+ struct afe_svc_cmd_set_param param;
+ struct afe_port_param_data_v2 pdata;
+ struct afe_param_cdc_slimbus_slave_cfg sb_slave_cfg;
+} __packed;
+
#endif /*_APR_AUDIO_V2_H_ */
diff --git a/include/sound/lsm_params.h b/include/sound/lsm_params.h
new file mode 100644
index 0000000..0edd8b5
--- /dev/null
+++ b/include/sound/lsm_params.h
@@ -0,0 +1,36 @@
+#ifndef __LSM_PARAMS_H__
+#define __LSM_PARAMS_H__
+
+#include <linux/types.h>
+#include <sound/asound.h>
+
+#define SNDRV_LSM_VERSION SNDRV_PROTOCOL_VERSION(0, 1, 0)
+
+enum lsm_detection_mode {
+ LSM_MODE_KEYWORD_ONLY_DETECTION = 1,
+ LSM_MODE_USER_KEYWORD_DETECTION
+};
+
+struct snd_lsm_sound_model {
+ __u8 *data;
+ __u32 data_size;
+ enum lsm_detection_mode detection_mode;
+ __u16 min_keyw_confidence;
+ __u16 min_user_confidence;
+ bool detect_failure;
+};
+
+struct snd_lsm_event_status {
+ __u16 status;
+ __u16 payload_size;
+ __u8 payload[0];
+};
+
+#define SNDRV_LSM_REG_SND_MODEL _IOW('U', 0x00, struct snd_lsm_sound_model)
+#define SNDRV_LSM_DEREG_SND_MODEL _IOW('U', 0x01, int)
+#define SNDRV_LSM_EVENT_STATUS _IOW('U', 0x02, struct snd_lsm_event_status)
+#define SNDRV_LSM_ABORT_EVENT _IOW('U', 0x03, int)
+#define SNDRV_LSM_START _IOW('U', 0x04, int)
+#define SNDRV_LSM_STOP _IOW('U', 0x05, int)
+
+#endif
diff --git a/include/sound/q6afe-v2.h b/include/sound/q6afe-v2.h
index feac314..22ddbbc 100644
--- a/include/sound/q6afe-v2.h
+++ b/include/sound/q6afe-v2.h
@@ -64,24 +64,34 @@
IDX_SLIMBUS_3_TX = 22,
IDX_SLIMBUS_4_RX = 23,
IDX_SLIMBUS_4_TX = 24,
- IDX_INT_BT_SCO_RX = 25,
- IDX_INT_BT_SCO_TX = 26,
- IDX_INT_BT_A2DP_RX = 27,
- IDX_INT_FM_RX = 28,
- IDX_INT_FM_TX = 29,
- IDX_RT_PROXY_PORT_001_RX = 30,
- IDX_RT_PROXY_PORT_001_TX = 31,
- IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX = 32,
- IDX_AFE_PORT_ID_QUATERNARY_MI2S_TX = 33,
- IDX_AFE_PORT_ID_SECONDARY_MI2S_RX = 34,
- IDX_AFE_PORT_ID_SECONDARY_MI2S_TX = 35,
- IDX_AFE_PORT_ID_TERTIARY_MI2S_RX = 36,
- IDX_AFE_PORT_ID_TERTIARY_MI2S_TX = 37,
- IDX_AFE_PORT_ID_PRIMARY_MI2S_RX = 38,
- IDX_AFE_PORT_ID_PRIMARY_MI2S_TX = 39,
+ IDX_SLIMBUS_5_RX = 25,
+ IDX_SLIMBUS_5_TX = 26,
+ IDX_INT_BT_SCO_RX = 27,
+ IDX_INT_BT_SCO_TX = 28,
+ IDX_INT_BT_A2DP_RX = 29,
+ IDX_INT_FM_RX = 30,
+ IDX_INT_FM_TX = 31,
+ IDX_RT_PROXY_PORT_001_RX = 32,
+ IDX_RT_PROXY_PORT_001_TX = 33,
+ IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX = 34,
+ IDX_AFE_PORT_ID_QUATERNARY_MI2S_TX = 35,
+ IDX_AFE_PORT_ID_SECONDARY_MI2S_RX = 36,
+ IDX_AFE_PORT_ID_SECONDARY_MI2S_TX = 37,
+ IDX_AFE_PORT_ID_TERTIARY_MI2S_RX = 38,
+ IDX_AFE_PORT_ID_TERTIARY_MI2S_TX = 39,
+ IDX_AFE_PORT_ID_PRIMARY_MI2S_RX = 40,
+ IDX_AFE_PORT_ID_PRIMARY_MI2S_TX = 41,
+ IDX_GLOBAL_CFG,
AFE_MAX_PORTS
};
+enum afe_mad_type {
+ MAD_HW_NONE = 0x00,
+ MAD_HW_AUDIO = 0x01,
+ MAD_HW_BEACON = 0x02,
+ MAD_HW_ULTRASOUND = 0x04
+};
+
struct afe_audio_buffer {
dma_addr_t phys;
void *data;
@@ -172,4 +182,10 @@
struct afe_digital_clk_cfg *cfg);
int q6afe_check_osr_clk_freq(u32 freq);
+int afe_turn_onoff_hw_mad(u16 mad_type, u16 mad_enable);
+int afe_port_set_mad_type(u16 port_id, enum afe_mad_type mad_type);
+enum afe_mad_type afe_port_get_mad_type(u16 port_id);
+int afe_set_config(enum afe_config_type config_type, void *config_data,
+ int arg);
+
#endif /* __Q6AFE_V2_H__ */
diff --git a/include/sound/q6lsm.h b/include/sound/q6lsm.h
new file mode 100644
index 0000000..5c9d4b9
--- /dev/null
+++ b/include/sound/q6lsm.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2013, Linux Foundation. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __Q6LSM_H__
+#define __Q6LSM_H__
+
+#include <linux/list.h>
+#include <linux/msm_ion.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/lsm_params.h>
+#include <mach/qdsp6v2/apr.h>
+#include <mach/msm_subsystem_map.h>
+
+typedef void (*app_cb)(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *priv);
+
+struct lsm_sound_model {
+ dma_addr_t phys;
+ void *data;
+ uint32_t size; /* size of buffer */
+ uint32_t actual_size; /* actual number of bytes read by DSP */
+ struct ion_handle *handle;
+ struct ion_client *client;
+ uint32_t mem_map_handle;
+};
+
+struct lsm_client {
+ int session;
+ app_cb cb;
+ atomic_t cmd_state;
+ void *priv;
+ struct apr_svc *apr;
+ struct apr_svc *mmap_apr;
+ struct mutex cmd_lock;
+ struct lsm_sound_model sound_model;
+ wait_queue_head_t cmd_wait;
+ uint16_t mode;
+ uint16_t connect_to_port;
+ uint16_t user_sensitivity;
+ uint16_t kw_sensitivity;
+ bool started;
+};
+
+struct lsm_stream_cmd_open_tx {
+ struct apr_hdr hdr;
+ uint16_t app_id;
+ uint16_t reserved;
+ uint32_t sampling_rate;
+} __packed;
+
+struct lsm_param_payload_common {
+ uint32_t module_id;
+ uint32_t param_id;
+ uint16_t param_size;
+ uint16_t reserved;
+} __packed;
+
+struct lsm_param_op_mode {
+ struct lsm_param_payload_common common;
+ uint32_t minor_version;
+ uint16_t mode;
+ uint16_t reserved;
+} __packed;
+
+struct lsm_param_connect_to_port {
+ struct lsm_param_payload_common common;
+ uint32_t minor_version;
+ /* AFE port id that receives voice wake up data */
+ uint16_t port_id;
+ uint16_t reserved;
+} __packed;
+
+struct lsm_param_kw_detect_sensitivity {
+ struct lsm_param_payload_common common;
+ uint32_t minor_version;
+ /* scale factor to change the keyword confidence thresholds */
+ uint16_t keyword_sensitivity;
+ uint16_t reserved;
+} __packed;
+
+struct lsm_param_user_detect_sensitivity {
+ struct lsm_param_payload_common common;
+ uint32_t minor_version;
+ /* scale factor to change the user confidence thresholds */
+ uint16_t user_sensitivity;
+ uint16_t reserved;
+} __packed;
+
+struct lsm_params_payload {
+ struct lsm_param_connect_to_port connect_to_port;
+ struct lsm_param_op_mode op_mode;
+ struct lsm_param_kw_detect_sensitivity kwds;
+ struct lsm_param_user_detect_sensitivity uds;
+} __packed;
+
+struct lsm_cmd_set_params {
+ struct apr_hdr hdr;
+ uint32_t data_payload_size;
+ uint32_t data_payload_addr_lsw;
+ uint32_t data_payload_addr_msw;
+ uint32_t mem_map_handle;
+ struct lsm_params_payload payload;
+} __packed;
+
+struct lsm_cmd_reg_snd_model {
+ struct apr_hdr hdr;
+ uint32_t model_size;
+ uint32_t model_addr_lsw;
+ uint32_t model_addr_msw;
+ uint32_t mem_map_handle;
+} __packed;
+
+struct lsm_client *q6lsm_client_alloc(app_cb cb, void *priv);
+void q6lsm_client_free(struct lsm_client *client);
+int q6lsm_open(struct lsm_client *client);
+int q6lsm_start(struct lsm_client *client, bool wait);
+int q6lsm_stop(struct lsm_client *client, bool wait);
+int q6lsm_snd_model_buf_alloc(struct lsm_client *client, uint32_t len);
+int q6lsm_close(struct lsm_client *client);
+int q6lsm_register_sound_model(struct lsm_client *client,
+ enum lsm_detection_mode mode, u16 minkeyword,
+ u16 minuser, bool detectfailure);
+int q6lsm_deregister_sound_model(struct lsm_client *client);
+
+#endif /* __Q6LSM_H__ */
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 4636247..7403b40 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -1534,6 +1534,20 @@
err = substream->ops->ioctl(substream, cmd, arg);
return err;
}
+
+static int snd_user_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void __user *arg)
+{
+ struct snd_pcm_runtime *runtime;
+ int err = 0;
+
+ if (PCM_RUNTIME_CHECK(substream))
+ return -ENXIO;
+ runtime = substream->runtime;
+ err = substream->ops->ioctl(substream, cmd, arg);
+ return err;
+}
+
/*
* drop ioctl
*
@@ -2605,6 +2619,9 @@
case SNDRV_COMPRESS_TSTAMP:
case SNDRV_COMPRESS_DRAIN:
return snd_compressed_ioctl(substream, cmd, arg);
+ default:
+ if (((cmd >> 8) & 0xff) == 'U')
+ return snd_user_ioctl(substream, cmd, arg);
}
snd_printd("unknown ioctl = 0x%x\n", cmd);
return -ENOTTY;
@@ -2788,10 +2805,12 @@
unsigned long arg)
{
struct snd_pcm_file *pcm_file;
+ unsigned char ioctl_magic;
pcm_file = file->private_data;
+ ioctl_magic = ((cmd >> 8) & 0xff);
- if ((((cmd >> 8) & 0xff) != 'A') && (((cmd >> 8) & 0xff) != 'C'))
+ if (ioctl_magic != 'A' && ioctl_magic != 'C' && ioctl_magic != 'U')
return -ENOTTY;
return snd_pcm_capture_ioctl1(file, pcm_file->substream, cmd,
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index 0c10548..78d1749 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -38,6 +38,9 @@
#include "wcd9xxx-resmgr.h"
#include "wcd9xxx-common.h"
+#define TAIKO_MAD_SLIMBUS_TX_PORT 12
+#define TAIKO_MAD_AUDIO_FIRMWARE_PATH "wcd9320/wcd9320_mad_audio.bin"
+
static atomic_t kp_taiko_priv;
static int spkr_drv_wrnd_param_set(const char *val,
const struct kernel_param *kp);
@@ -47,6 +50,121 @@
.set = spkr_drv_wrnd_param_set,
.get = param_get_int,
};
+
+static struct afe_param_slimbus_slave_port_cfg taiko_slimbus_slave_port_cfg = {
+ .minor_version = 1,
+ .slimbus_dev_id = AFE_SLIMBUS_DEVICE_1,
+ .slave_dev_pgd_la = 0,
+ .slave_dev_intfdev_la = 0,
+ .bit_width = 16,
+ .data_format = 0,
+ .num_channels = 1
+};
+
+enum {
+ RESERVED = 0,
+ AANC_LPF_FF_FB = 1,
+ AANC_LPF_COEFF_MSB,
+ AANC_LPF_COEFF_LSB,
+ HW_MAD_AUDIO_ENABLE,
+ HW_MAD_ULTR_ENABLE,
+ HW_MAD_BEACON_ENABLE,
+ HW_MAD_AUDIO_SLEEP_TIME,
+ HW_MAD_ULTR_SLEEP_TIME,
+ HW_MAD_BEACON_SLEEP_TIME,
+ HW_MAD_TX_AUDIO_SWITCH_OFF,
+ HW_MAD_TX_ULTR_SWITCH_OFF,
+ HW_MAD_TX_BEACON_SWITCH_OFF,
+ MAD_AUDIO_INT_DEST_SELECT_REG,
+ MAD_ULT_INT_DEST_SELECT_REG,
+ MAD_BEACON_INT_DEST_SELECT_REG,
+ MAD_CLIP_INT_DEST_SELECT_REG,
+ MAD_VBAT_INT_DEST_SELECT_REG,
+ MAD_AUDIO_INT_MASK_REG,
+ MAD_ULT_INT_MASK_REG,
+ MAD_BEACON_INT_MASK_REG,
+ MAD_CLIP_INT_MASK_REG,
+ MAD_VBAT_INT_MASK_REG,
+ MAD_AUDIO_INT_STATUS_REG,
+ MAD_ULT_INT_STATUS_REG,
+ MAD_BEACON_INT_STATUS_REG,
+ MAD_CLIP_INT_STATUS_REG,
+ MAD_VBAT_INT_STATUS_REG,
+ MAD_AUDIO_INT_CLEAR_REG,
+ MAD_ULT_INT_CLEAR_REG,
+ MAD_BEACON_INT_CLEAR_REG,
+ MAD_CLIP_INT_CLEAR_REG,
+ MAD_VBAT_INT_CLEAR_REG,
+ SB_PGD_PORT_TX_WATERMARK_n,
+ SB_PGD_PORT_TX_ENABLE_n,
+ SB_PGD_PORT_RX_WATERMARK_n,
+ SB_PGD_PORT_RX_ENABLE_n,
+ MAX_CFG_REGISTERS,
+};
+
+static struct afe_param_cdc_reg_cfg mad_audio_reg_cfg[] = {
+ {
+ 1,
+ (TAIKO_REGISTER_START_OFFSET + TAIKO_A_CDC_MAD_MAIN_CTL_1),
+ HW_MAD_AUDIO_ENABLE, 0x1, 8, 0
+ },
+ {
+ 1,
+ (TAIKO_REGISTER_START_OFFSET + TAIKO_A_CDC_MAD_AUDIO_CTL_3),
+ HW_MAD_AUDIO_SLEEP_TIME, 0xF, 8, 0
+ },
+ {
+ 1,
+ (TAIKO_REGISTER_START_OFFSET + TAIKO_A_CDC_MAD_AUDIO_CTL_4),
+ HW_MAD_TX_AUDIO_SWITCH_OFF, 0x1, 8, 0
+ },
+ {
+ 1,
+ (TAIKO_REGISTER_START_OFFSET + TAIKO_A_INTR_DESTN3),
+ MAD_AUDIO_INT_DEST_SELECT_REG, 0x1, 8, 0
+ },
+ {
+ 1,
+ (TAIKO_REGISTER_START_OFFSET + TAIKO_A_INTR_MASK3),
+ MAD_AUDIO_INT_MASK_REG, 0x1, 8, 0
+ },
+ {
+ 1,
+ (TAIKO_REGISTER_START_OFFSET + TAIKO_A_INTR_STATUS3),
+ MAD_AUDIO_INT_STATUS_REG, 0x1, 8, 0
+ },
+ {
+ 1,
+ (TAIKO_REGISTER_START_OFFSET + TAIKO_A_INTR_CLEAR3),
+ MAD_AUDIO_INT_CLEAR_REG, 0x1, 8, 0
+ },
+ {
+ 1,
+ (TAIKO_REGISTER_START_OFFSET + TAIKO_SB_PGD_PORT_TX_BASE),
+ SB_PGD_PORT_TX_WATERMARK_n, 0x1E, 8, 0x1
+ },
+ {
+ 1,
+ (TAIKO_REGISTER_START_OFFSET + TAIKO_SB_PGD_PORT_TX_BASE),
+ SB_PGD_PORT_TX_ENABLE_n, 0x1, 8, 0x1
+ },
+ {
+ 1,
+ (TAIKO_REGISTER_START_OFFSET + TAIKO_SB_PGD_PORT_RX_BASE),
+ SB_PGD_PORT_RX_WATERMARK_n, 0x1E, 8, 0x1
+ },
+ {
+ 1,
+ (TAIKO_REGISTER_START_OFFSET + TAIKO_SB_PGD_PORT_RX_BASE),
+ SB_PGD_PORT_RX_ENABLE_n, 0x1, 8, 0x1
+ }
+};
+
+static struct afe_param_cdc_reg_cfg_data taiko_mad_audio_reg_cfg = {
+ .num_registers = ARRAY_SIZE(mad_audio_reg_cfg),
+ .reg_data = mad_audio_reg_cfg,
+};
+
module_param_cb(spkr_drv_wrnd, &spkr_drv_wrnd_param_ops, &spkr_drv_wrnd, 0644);
MODULE_PARM_DESC(spkr_drv_wrnd,
"Run software workaround to avoid leakage on the speaker drive");
@@ -84,6 +202,7 @@
AIF3_PB,
AIF3_CAP,
AIF4_VIFEED,
+ AIF4_MAD_TX,
NUM_CODEC_DAIS,
};
@@ -237,6 +356,8 @@
bool spkr_pa_widget_on;
+ struct afe_param_cdc_slimbus_slave_cfg slimbus_slave_cfg;
+
/* resmgr module */
struct wcd9xxx_resmgr resmgr;
/* mbhc module */
@@ -1555,6 +1676,9 @@
static const struct snd_kcontrol_new lineout4_ground_switch =
SOC_DAPM_SINGLE("Switch", TAIKO_A_RX_LINE_4_DAC_CTL, 6, 1, 0);
+static const struct snd_kcontrol_new aif4_mad_switch =
+ SOC_DAPM_SINGLE("Switch", TAIKO_A_CDC_CLK_OTHR_CTL, 4, 1, 0);
+
/* virtual port entries */
static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -2155,6 +2279,108 @@
return 0;
}
+static int taiko_codec_config_mad(struct snd_soc_codec *codec)
+{
+ int ret;
+ const struct firmware *fw;
+ struct mad_audio_cal *mad_cal;
+ const char *filename = TAIKO_MAD_AUDIO_FIRMWARE_PATH;
+
+ pr_debug("%s: enter\n", __func__);
+ ret = request_firmware(&fw, filename, codec->dev);
+ if (ret != 0) {
+ pr_err("Failed to acquire MAD firwmare data %s: %d\n", filename,
+ ret);
+ return -ENODEV;
+ }
+
+ if (fw->size < sizeof(struct mad_audio_cal)) {
+ pr_err("%s: incorrect firmware size %u\n", __func__, fw->size);
+ release_firmware(fw);
+ return -ENOMEM;
+ }
+
+ mad_cal = (struct mad_audio_cal *)(fw->data);
+ if (!mad_cal) {
+ pr_err("%s: Invalid calibration data\n", __func__);
+ release_firmware(fw);
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, TAIKO_A_CDC_CONN_MAD,
+ 0x0F, mad_cal->microphone_info.input_microphone);
+ snd_soc_write(codec, TAIKO_A_CDC_MAD_MAIN_CTL_2,
+ mad_cal->microphone_info.cycle_time);
+ snd_soc_update_bits(codec, TAIKO_A_CDC_MAD_MAIN_CTL_1, 0xFF << 3,
+ ((uint16_t)mad_cal->microphone_info.settle_time)
+ << 3);
+
+ /* Audio */
+ snd_soc_write(codec, TAIKO_A_CDC_MAD_AUDIO_CTL_8,
+ mad_cal->audio_info.rms_omit_samples);
+ snd_soc_update_bits(codec, TAIKO_A_CDC_MAD_AUDIO_CTL_1,
+ 0x07 << 4, mad_cal->audio_info.rms_comp_time << 4);
+ snd_soc_update_bits(codec, TAIKO_A_CDC_MAD_AUDIO_CTL_2, 0x03 << 2,
+ mad_cal->audio_info.detection_mechanism << 2);
+ snd_soc_write(codec, TAIKO_A_CDC_MAD_AUDIO_CTL_7,
+ mad_cal->audio_info.rms_diff_threshold & 0x3F);
+ snd_soc_write(codec, TAIKO_A_CDC_MAD_AUDIO_CTL_5,
+ mad_cal->audio_info.rms_threshold_lsb);
+ snd_soc_write(codec, TAIKO_A_CDC_MAD_AUDIO_CTL_6,
+ mad_cal->audio_info.rms_threshold_msb);
+
+
+ /* Beacon */
+ snd_soc_write(codec, TAIKO_A_CDC_MAD_BEACON_CTL_8,
+ mad_cal->beacon_info.rms_omit_samples);
+ snd_soc_update_bits(codec, TAIKO_A_CDC_MAD_BEACON_CTL_1,
+ 0x07 << 4, mad_cal->beacon_info.rms_comp_time);
+ snd_soc_update_bits(codec, TAIKO_A_CDC_MAD_BEACON_CTL_2, 0x03 << 2,
+ mad_cal->beacon_info.detection_mechanism << 2);
+ snd_soc_write(codec, TAIKO_A_CDC_MAD_BEACON_CTL_7,
+ mad_cal->beacon_info.rms_diff_threshold & 0x1F);
+ snd_soc_write(codec, TAIKO_A_CDC_MAD_BEACON_CTL_5,
+ mad_cal->beacon_info.rms_threshold_lsb);
+ snd_soc_write(codec, TAIKO_A_CDC_MAD_BEACON_CTL_6,
+ mad_cal->beacon_info.rms_threshold_msb);
+
+ /* Ultrasound */
+ snd_soc_update_bits(codec, TAIKO_A_CDC_MAD_BEACON_CTL_1,
+ 0x07 << 4, mad_cal->beacon_info.rms_comp_time);
+ snd_soc_update_bits(codec, TAIKO_A_CDC_MAD_ULTR_CTL_2, 0x03 << 2,
+ mad_cal->ultrasound_info.detection_mechanism);
+ snd_soc_write(codec, TAIKO_A_CDC_MAD_ULTR_CTL_7,
+ mad_cal->ultrasound_info.rms_diff_threshold & 0x1F);
+ snd_soc_write(codec, TAIKO_A_CDC_MAD_ULTR_CTL_5,
+ mad_cal->ultrasound_info.rms_threshold_lsb);
+ snd_soc_write(codec, TAIKO_A_CDC_MAD_ULTR_CTL_6,
+ mad_cal->ultrasound_info.rms_threshold_msb);
+
+ release_firmware(fw);
+ pr_debug("%s: leave ret %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int taiko_codec_enable_mad(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ int ret = 0;
+
+ pr_debug("%s %d\n", __func__, event);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = taiko_codec_config_mad(codec);
+ if (ret) {
+ pr_err("%s: Failed to config MAD\n", __func__);
+ break;
+ }
+ break;
+ }
+ return ret;
+}
+
static int taiko_codec_enable_micbias(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -2674,6 +2900,11 @@
{"AIF3 CAP", NULL, "AIF3_CAP Mixer"},
{"AIF4 VI", NULL, "SPK_OUT"},
+ /* MAD */
+ {"AIF4 MAD", NULL, "CDC_CONN"},
+ {"MADONOFF", "Switch", "MADINPUT"},
+ {"AIF4 MAD", NULL, "MADONOFF"},
+
/* SLIM_MIXER("AIF1_CAP Mixer"),*/
{"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
{"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
@@ -3184,6 +3415,8 @@
static int taiko_volatile(struct snd_soc_codec *ssc, unsigned int reg)
{
+ int i;
+
/* Registers lower than 0x100 are top level registers which can be
* written by the Taiko core driver.
*/
@@ -3222,6 +3455,11 @@
return 1;
}
+ for (i = 0; i < ARRAY_SIZE(mad_audio_reg_cfg); i++)
+ if (mad_audio_reg_cfg[i].reg_logical_addr -
+ TAIKO_REGISTER_START_OFFSET == reg)
+ return 1;
+
return 0;
}
@@ -3428,6 +3666,7 @@
case AIF2_CAP:
case AIF3_CAP:
case AIF4_VIFEED:
+ case AIF4_MAD_TX:
if (!tx_slot || !tx_num) {
pr_err("%s: Invalid tx_slot %d or tx_num %d\n",
__func__, (u32) tx_slot, (u32) tx_num);
@@ -3858,6 +4097,20 @@
},
.ops = &taiko_dai_ops,
},
+ {
+ .name = "taiko_mad1",
+ .id = AIF4_MAD_TX,
+ .capture = {
+ .stream_name = "AIF4 MAD TX",
+ .rates = SNDRV_PCM_RATE_16000,
+ .formats = TAIKO_FORMATS,
+ .rate_min = 16000,
+ .rate_max = 16000,
+ .channels_min = 1,
+ .channels_max = 1,
+ },
+ .ops = &taiko_dai_ops,
+ },
};
static struct snd_soc_dai_driver taiko_i2s_dai[] = {
@@ -4556,6 +4809,12 @@
SND_SOC_DAPM_AIF_OUT_E("AIF4 VI", "VIfeed", 0, SND_SOC_NOPM,
AIF4_VIFEED, 0, taiko_codec_enable_slimvi_feedback,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT_E("AIF4 MAD", "AIF4 MAD TX", 0,
+ SND_SOC_NOPM, 0, 0,
+ taiko_codec_enable_mad, SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SWITCH("MADONOFF", SND_SOC_NOPM, 0, 0,
+ &aif4_mad_switch),
+ SND_SOC_DAPM_INPUT("MADINPUT"),
SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0,
aif_cap_mixer, ARRAY_SIZE(aif_cap_mixer)),
@@ -4884,6 +5143,26 @@
TAIKO_REG_VAL(TAIKO_A_CDC_RX5_B6_CTL, 0x80),
TAIKO_REG_VAL(TAIKO_A_CDC_RX6_B6_CTL, 0x80),
TAIKO_REG_VAL(TAIKO_A_CDC_RX7_B6_CTL, 0x80),
+
+ /* MAD registers */
+ TAIKO_REG_VAL(TAIKO_A_MAD_ANA_CTRL, 0xF1),
+ TAIKO_REG_VAL(TAIKO_A_CDC_MAD_MAIN_CTL_1, 0x00),
+ TAIKO_REG_VAL(TAIKO_A_CDC_MAD_MAIN_CTL_2, 0x00),
+ TAIKO_REG_VAL(TAIKO_A_CDC_MAD_AUDIO_CTL_1, 0x00),
+ /* Set SAMPLE_TX_EN bit */
+ TAIKO_REG_VAL(TAIKO_A_CDC_MAD_AUDIO_CTL_2, 0x03),
+ TAIKO_REG_VAL(TAIKO_A_CDC_MAD_AUDIO_CTL_3, 0x00),
+ TAIKO_REG_VAL(TAIKO_A_CDC_MAD_AUDIO_CTL_4, 0x00),
+ TAIKO_REG_VAL(TAIKO_A_CDC_MAD_AUDIO_CTL_5, 0x00),
+ TAIKO_REG_VAL(TAIKO_A_CDC_MAD_AUDIO_CTL_6, 0x00),
+ TAIKO_REG_VAL(TAIKO_A_CDC_MAD_AUDIO_CTL_7, 0x00),
+ TAIKO_REG_VAL(TAIKO_A_CDC_MAD_AUDIO_CTL_8, 0x00),
+ TAIKO_REG_VAL(TAIKO_A_CDC_MAD_AUDIO_IIR_CTL_PTR, 0x00),
+ TAIKO_REG_VAL(TAIKO_A_CDC_MAD_AUDIO_IIR_CTL_VAL, 0x40),
+ TAIKO_REG_VAL(TAIKO_A_CDC_DEBUG_B7_CTL, 0x00),
+ TAIKO_REG_VAL(TAIKO_A_CDC_CLK_OTHR_RESET_B1_CTL, 0x00),
+ TAIKO_REG_VAL(TAIKO_A_CDC_CLK_OTHR_CTL, 0x00),
+ TAIKO_REG_VAL(TAIKO_A_CDC_CONN_MAD, 0x01),
};
static const struct wcd9xxx_reg_mask_val taiko_1_0_reg_defaults[] = {
@@ -5121,6 +5400,26 @@
}
EXPORT_SYMBOL_GPL(taiko_hs_detect);
+static void taiko_init_slim_slave_cfg(struct snd_soc_codec *codec)
+{
+ struct taiko_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct afe_param_cdc_slimbus_slave_cfg *cfg;
+ struct wcd9xxx *wcd9xxx = codec->control_data;
+ uint64_t eaddr = 0;
+
+ cfg = &priv->slimbus_slave_cfg;
+ cfg->minor_version = 1;
+ cfg->tx_slave_port_offset = 0;
+ cfg->rx_slave_port_offset = 16;
+
+ memcpy(&eaddr, &wcd9xxx->slim->e_addr, sizeof(wcd9xxx->slim->e_addr));
+ WARN_ON(sizeof(wcd9xxx->slim->e_addr) != 6);
+ cfg->device_enum_addr_lsw = eaddr & 0xFFFFFFFF;
+ cfg->device_enum_addr_msw = eaddr >> 32;
+
+ pr_debug("%s: slimbus logical address 0x%llx\n", __func__, eaddr);
+}
+
static int taiko_post_reset_cb(struct wcd9xxx *wcd9xxx)
{
int ret = 0;
@@ -5150,6 +5449,8 @@
if (IS_ERR_VALUE(ret))
pr_err("%s: bad pdata\n", __func__);
+ taiko_init_slim_slave_cfg(codec);
+
wcd9xxx_mbhc_deinit(&taiko->mbhc);
ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec);
if (ret)
@@ -5160,6 +5461,23 @@
return ret;
}
+void *taiko_get_afe_config(struct snd_soc_codec *codec,
+ enum afe_config_type config_type)
+{
+ struct taiko_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (config_type) {
+ case AFE_SLIMBUS_SLAVE_CONFIG:
+ return &priv->slimbus_slave_cfg;
+ case AFE_CDC_REGISTERS_CONFIG:
+ return &taiko_mad_audio_reg_cfg;
+ case AFE_SLIMBUS_SLAVE_PORT_CONFIG:
+ return &taiko_slimbus_slave_port_cfg;
+ default:
+ pr_err("%s: Unknown config_type 0x%x\n", __func__, config_type);
+ return NULL;
+ }
+}
static struct wcd9xxx_reg_address taiko_reg_address = {
.micb_4_mbhc = TAIKO_A_MICB_4_MBHC,
@@ -5353,6 +5671,14 @@
INIT_LIST_HEAD(&taiko->dai[i].wcd9xxx_ch_list);
init_waitqueue_head(&taiko->dai[i].dai_wait);
}
+ taiko_slimbus_slave_port_cfg.slave_dev_intfdev_la =
+ control->slim_slave->laddr;
+ taiko_slimbus_slave_port_cfg.slave_dev_pgd_la =
+ control->slim->laddr;
+ taiko_slimbus_slave_port_cfg.slave_port_mapping[0] =
+ TAIKO_MAD_SLIMBUS_TX_PORT;
+
+ taiko_init_slim_slave_cfg(codec);
}
if (TAIKO_IS_1_0(control->version))
diff --git a/sound/soc/codecs/wcd9320.h b/sound/soc/codecs/wcd9320.h
index 89a0b9f..36310e5 100644
--- a/sound/soc/codecs/wcd9320.h
+++ b/sound/soc/codecs/wcd9320.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. 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 and
@@ -14,6 +14,7 @@
#include <sound/soc.h>
#include <sound/jack.h>
+#include <sound/apr_audio-v2.h>
#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
#include "wcd9xxx-mbhc.h"
#include "wcd9xxx-resmgr.h"
@@ -25,6 +26,10 @@
#define TAIKO_REG_VAL(reg, val) {reg, 0, val}
#define TAIKO_MCLK_ID 0
+#define TAIKO_REGISTER_START_OFFSET 0x800
+#define TAIKO_SB_PGD_PORT_RX_BASE 0x40
+#define TAIKO_SB_PGD_PORT_TX_BASE 0x50
+
extern const u8 taiko_reg_readable[TAIKO_CACHE_SIZE];
extern const u8 taiko_reset_reg_defaults[TAIKO_CACHE_SIZE];
struct taiko_codec_dai_data {
@@ -90,9 +95,62 @@
u32 num_anc_slots;
};
+struct mad_audio_header {
+ u32 reserved[3];
+ u32 num_reg_cfg;
+};
+
+struct mad_microphone_info {
+ uint8_t input_microphone;
+ uint8_t cycle_time;
+ uint8_t settle_time;
+ uint8_t padding;
+} __packed;
+
+struct mad_micbias_info {
+ uint8_t micbias;
+ uint8_t k_factor;
+ uint8_t external_bypass_capacitor;
+ uint8_t internal_biasing;
+ uint8_t cfilter;
+ uint8_t padding[3];
+} __packed;
+
+struct mad_rms_audio_beacon_info {
+ uint8_t rms_omit_samples;
+ uint8_t rms_comp_time;
+ uint8_t detection_mechanism;
+ uint8_t rms_diff_threshold;
+ uint8_t rms_threshold_lsb;
+ uint8_t rms_threshold_msb;
+ uint8_t padding[2];
+ uint8_t iir_coefficients[36];
+} __packed;
+
+struct mad_rms_ultrasound_info {
+ uint8_t rms_comp_time;
+ uint8_t detection_mechanism;
+ uint8_t rms_diff_threshold;
+ uint8_t rms_threshold_lsb;
+ uint8_t rms_threshold_msb;
+ uint8_t padding[3];
+ uint8_t iir_coefficients[36];
+} __packed;
+
+struct mad_audio_cal {
+ uint32_t version;
+ struct mad_microphone_info microphone_info;
+ struct mad_micbias_info micbias_info;
+ struct mad_rms_audio_beacon_info audio_info;
+ struct mad_rms_audio_beacon_info beacon_info;
+ struct mad_rms_ultrasound_info ultrasound_info;
+} __packed;
+
extern int taiko_mclk_enable(struct snd_soc_codec *codec, int mclk_enable,
bool dapm);
extern int taiko_hs_detect(struct snd_soc_codec *codec,
struct wcd9xxx_mbhc_config *mbhc_cfg);
+extern void *taiko_get_afe_config(struct snd_soc_codec *codec,
+ enum afe_config_type config_type);
#endif
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
index 96709be..e00e409 100644
--- a/sound/soc/msm/msm-dai-fe.c
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -612,6 +612,20 @@
.ops = &msm_fe_dai_ops,
.name = "DTMF_RX_HOSTLESS",
},
+ {
+ .capture = {
+ .stream_name = "Listen Audio Service Capture",
+ .aif_name = "LSM_UL_HL",
+ .rates = SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 1,
+ .rate_min = 16000,
+ .rate_max = 16000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "LSM",
+ },
};
static __devinit int msm_fe_dai_dev_probe(struct platform_device *pdev)
diff --git a/sound/soc/msm/msm8226.c b/sound/soc/msm/msm8226.c
index 235b527..328e287 100644
--- a/sound/soc/msm/msm8226.c
+++ b/sound/soc/msm/msm8226.c
@@ -982,6 +982,45 @@
.ops = &msm8226_be_ops,
.ignore_suspend = 1,
},
+ /* Incall Record Uplink BACK END DAI Link */
+ {
+ .name = LPASS_BE_INCALL_RECORD_TX,
+ .stream_name = "Voice Uplink Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.32772",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Record Downlink BACK END DAI Link */
+ {
+ .name = LPASS_BE_INCALL_RECORD_RX,
+ .stream_name = "Voice Downlink Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.32771",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
+ /* Incall Music BACK END DAI Link */
+ {
+ .name = LPASS_BE_VOICE_PLAYBACK_TX,
+ .stream_name = "Voice Farend Playback",
+ .cpu_dai_name = "msm-dai-q6-dev.32773",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ .ignore_suspend = 1,
+ },
};
struct snd_soc_card snd_soc_card_msm8226 = {
diff --git a/sound/soc/msm/msm8974.c b/sound/soc/msm/msm8974.c
index 747e027..0dbe3f7 100644
--- a/sound/soc/msm/msm8974.c
+++ b/sound/soc/msm/msm8974.c
@@ -24,6 +24,7 @@
#include <sound/soc-dapm.h>
#include <sound/pcm.h>
#include <sound/jack.h>
+#include <sound/q6afe-v2.h>
#include <asm/mach-types.h>
#include <mach/socinfo.h>
#include <sound/pcm_params.h>
@@ -1003,6 +1004,33 @@
return 0;
}
+static int msm_slim_5_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ int rc;
+ void *config;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_interval *rate =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s enter\n", __func__);
+ rate->min = rate->max = 16000;
+ channels->min = channels->max = 1;
+
+ config = taiko_get_afe_config(codec, AFE_SLIMBUS_SLAVE_PORT_CONFIG);
+ rc = afe_set_config(AFE_SLIMBUS_SLAVE_PORT_CONFIG, config,
+ SLIMBUS_5_TX);
+ if (rc) {
+ pr_err("%s: Failed to set slimbus slave port config %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ return 0;
+}
+
static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
@@ -1054,6 +1082,7 @@
static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
{
int err;
+ void *config_data;
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
@@ -1108,6 +1137,23 @@
snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
+
+ config_data = taiko_get_afe_config(codec, AFE_CDC_REGISTERS_CONFIG);
+ err = afe_set_config(AFE_CDC_REGISTERS_CONFIG, config_data, 0);
+ if (err) {
+ pr_err("%s: Failed to set codec registers config %d\n",
+ __func__, err);
+ return err;
+ }
+
+ config_data = taiko_get_afe_config(codec, AFE_SLIMBUS_SLAVE_CONFIG);
+ err = afe_set_config(AFE_SLIMBUS_SLAVE_CONFIG, config_data, 0);
+ if (err) {
+ pr_err("%s: Failed to set slimbus slave config %d\n", __func__,
+ err);
+ return err;
+ }
+
/* start mbhc */
mbhc_cfg.calibration = def_taiko_mbhc_cal();
if (mbhc_cfg.calibration)
@@ -1574,6 +1620,22 @@
.ignore_pmdown_time = 1,
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA5,
},
+ /* LSM FE */
+ {
+ .name = "Listen Audio Service",
+ .stream_name = "Listen Audio Service",
+ .cpu_dai_name = "LSM",
+ .platform_name = "msm-lsm-client",
+ .dynamic = 1,
+ .trigger = { SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST },
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_LSM1,
+ },
/* Backend BT/FM DAI Links */
{
.name = LPASS_BE_INT_BT_SCO_RX,
@@ -1837,6 +1899,19 @@
.be_hw_params_fixup = msm_be_hw_params_fixup,
.ignore_suspend = 1,
},
+ /* MAD BE */
+ {
+ .name = LPASS_BE_SLIMBUS_5_TX,
+ .stream_name = "Slimbus5 Capture",
+ .cpu_dai_name = "msm-dai-q6-dev.16395",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "taiko_codec",
+ .codec_dai_name = "taiko_mad1",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_5_TX,
+ .be_hw_params_fixup = msm_slim_5_tx_be_hw_params_fixup,
+ .ops = &msm8974_be_ops,
+ },
/* Incall Music BACK END DAI Link */
{
.name = LPASS_BE_VOICE_PLAYBACK_TX,
diff --git a/sound/soc/msm/qdsp6v2/Makefile b/sound/soc/msm/qdsp6v2/Makefile
index 69c0976..391b3da 100644
--- a/sound/soc/msm/qdsp6v2/Makefile
+++ b/sound/soc/msm/qdsp6v2/Makefile
@@ -1,6 +1,11 @@
-snd-soc-qdsp6v2-objs += msm-dai-q6-v2.o msm-pcm-q6-v2.o msm-pcm-routing-v2.o msm-compr-q6-v2.o msm-multi-ch-pcm-q6-v2.o
-snd-soc-qdsp6v2-objs += msm-pcm-lpa-v2.o msm-pcm-afe-v2.o msm-pcm-voip-v2.o msm-pcm-voice-v2.o msm-dai-q6-hdmi-v2.o
-obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o msm-pcm-dtmf-v2.o msm-dai-stub-v2.o
-obj-y += q6adm.o q6afe.o q6asm.o q6audio-v2.o q6voice.o q6core.o audio_acdb.o rtac.o
+snd-soc-qdsp6v2-objs += msm-dai-q6-v2.o msm-pcm-q6-v2.o msm-pcm-routing-v2.o \
+ msm-compr-q6-v2.o msm-multi-ch-pcm-q6-v2.o \
+ msm-pcm-lpa-v2.o msm-pcm-afe-v2.o msm-pcm-voip-v2.o \
+ msm-pcm-voice-v2.o msm-dai-q6-hdmi-v2.o \
+ msm-lsm-client.o
+obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o msm-pcm-dtmf-v2.o \
+ msm-dai-stub-v2.o
+obj-y += q6adm.o q6afe.o q6asm.o q6audio-v2.o q6voice.o q6core.o audio_acdb.o \
+ rtac.o q6lsm.o
ocmem-audio-objs += audio_ocmem.o
obj-$(CONFIG_AUDIO_OCMEM) += ocmem-audio.o
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
index 557ec65..916be0b 100644
--- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -701,6 +701,7 @@
case SLIMBUS_2_TX:
case SLIMBUS_3_TX:
case SLIMBUS_4_TX:
+ case SLIMBUS_5_TX:
rc = msm_dai_q6_slim_bus_hw_params(params, dai,
substream->stream);
break;
@@ -831,6 +832,7 @@
case SLIMBUS_2_TX:
case SLIMBUS_3_TX:
case SLIMBUS_4_TX:
+ case SLIMBUS_5_TX:
/*
* channel number to be between 128 and 255.
* For TX port use channel numbers
@@ -1980,6 +1982,7 @@
break;
case SLIMBUS_0_TX:
case SLIMBUS_2_TX:
+ case SLIMBUS_5_TX:
rc = snd_soc_register_dai(&pdev->dev,
&msm_dai_q6_slimbus_tx_dai);
break;
diff --git a/sound/soc/msm/qdsp6v2/msm-lsm-client.c b/sound/soc/msm/qdsp6v2/msm-lsm-client.c
new file mode 100644
index 0000000..ea6f390
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-lsm-client.c
@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 2013, Linux Foundation. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/android_pmem.h>
+#include <linux/of.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/timer.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/q6lsm.h>
+#include <sound/lsm_params.h>
+#include "msm-pcm-routing-v2.h"
+
+struct lsm_priv {
+ struct snd_pcm_substream *substream;
+ struct lsm_client *lsm_client;
+
+ struct snd_lsm_event_status *event_status;
+ spinlock_t event_lock;
+ wait_queue_head_t event_wait;
+ unsigned long event_avail;
+ atomic_t event_wait_stop;
+};
+
+static void lsm_event_handler(uint32_t opcode, uint32_t token,
+ void *payload, void *priv)
+{
+ unsigned long flags;
+ struct snd_lsm_event_status *event_status;
+ struct lsm_priv *prtd = priv;
+ struct snd_pcm_substream *substream = prtd->substream;
+
+ pr_debug("%s: enter opcode 0x%x\n", __func__, opcode);
+ switch (opcode) {
+ case LSM_SESSION_EVENT_DETECTION_STATUS:
+ event_status = payload;
+
+ spin_lock_irqsave(&prtd->event_lock, flags);
+ prtd->event_status = krealloc(prtd->event_status,
+ sizeof(*event_status) +
+ event_status->payload_size,
+ GFP_ATOMIC);
+ if (likely(prtd->event_status)) {
+ memcpy(prtd->event_status, event_status,
+ sizeof(*event_status) +
+ event_status->payload_size);
+ prtd->event_avail = 1;
+ spin_unlock_irqrestore(&prtd->event_lock, flags);
+ wake_up(&prtd->event_wait);
+ } else {
+ spin_unlock_irqrestore(&prtd->event_lock, flags);
+ pr_err("%s: Couldn't allocate %d bytes of memory\n",
+ __func__, event_status->payload_size);
+ }
+ if (substream->timer_running)
+ snd_timer_interrupt(substream->timer, 1);
+ break;
+ default:
+ pr_debug("%s: Unsupported Event opcode 0x%x\n", __func__,
+ opcode);
+ break;
+ }
+}
+
+static int msm_lsm_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ unsigned long flags;
+ int ret;
+ struct snd_lsm_sound_model snd_model;
+ int rc = 0;
+ int xchg = 0;
+ int size = 0;
+ struct snd_lsm_event_status *event_status = NULL;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct lsm_priv *prtd = runtime->private_data;
+ struct snd_lsm_event_status *user = arg;
+
+ pr_debug("%s: enter cmd %x\n", __func__, cmd);
+ switch (cmd) {
+ case SNDRV_LSM_REG_SND_MODEL:
+ pr_debug("%s: Registering sound model\n", __func__);
+ if (copy_from_user(&snd_model, (void *)arg,
+ sizeof(struct snd_lsm_sound_model))) {
+ rc = -EFAULT;
+ pr_err("%s: copy from user failed, size %d\n", __func__,
+ sizeof(struct snd_lsm_sound_model));
+ break;
+ }
+
+ rc = q6lsm_snd_model_buf_alloc(prtd->lsm_client,
+ snd_model.data_size);
+ if (rc) {
+ pr_err("%s: q6lsm buffer alloc failed, size %d\n",
+ __func__, snd_model.data_size);
+ break;
+ }
+
+ if (copy_from_user(prtd->lsm_client->sound_model.data,
+ snd_model.data, snd_model.data_size)) {
+ pr_err("%s: copy from user data failed size %d\n",
+ __func__, snd_model.data_size);
+ rc = -EFAULT;
+ break;
+ }
+
+ rc = q6lsm_register_sound_model(prtd->lsm_client,
+ snd_model.detection_mode,
+ snd_model.min_keyw_confidence,
+ snd_model.min_user_confidence,
+ snd_model.detect_failure);
+ if (rc < 0)
+ pr_err("%s: q6lsm_register_sound_model failed =%d\n",
+ __func__, rc);
+ break;
+
+ case SNDRV_LSM_DEREG_SND_MODEL:
+ pr_debug("%s: Deregistering sound model\n", __func__);
+ rc = q6lsm_deregister_sound_model(prtd->lsm_client);
+ break;
+
+ case SNDRV_LSM_EVENT_STATUS:
+ pr_debug("%s: Get event status\n", __func__);
+ atomic_set(&prtd->event_wait_stop, 0);
+ rc = wait_event_interruptible(prtd->event_wait,
+ (cmpxchg(&prtd->event_avail, 1, 0) ||
+ (xchg = atomic_cmpxchg(&prtd->event_wait_stop,
+ 1, 0))));
+ pr_debug("%s: wait_event_interruptible %d event_wait_stop %d\n",
+ __func__, rc, xchg);
+ if (!rc && !xchg) {
+ pr_debug("%s: New event available %ld\n", __func__,
+ prtd->event_avail);
+ spin_lock_irqsave(&prtd->event_lock, flags);
+ if (prtd->event_status) {
+ size = sizeof(*event_status) +
+ prtd->event_status->payload_size;
+ event_status = kmemdup(prtd->event_status, size,
+ GFP_ATOMIC);
+ }
+ spin_unlock_irqrestore(&prtd->event_lock, flags);
+ if (!event_status) {
+ pr_err("%s: Couldn't allocate %d bytes\n",
+ __func__, size);
+ /*
+ * Don't use -ENOMEM as userspace will check
+ * it for increasing buffer
+ */
+ rc = -EFAULT;
+ } else {
+ if (user->payload_size <
+ event_status->payload_size) {
+ pr_debug("%s: provided %dbytes isn't enough, needs %dbytes\n",
+ __func__, user->payload_size,
+ size);
+ rc = -ENOMEM;
+ } else if (!access_ok(VERIFY_WRITE, arg,
+ size)) {
+ rc = -EFAULT;
+ } else {
+ rc = copy_to_user(arg, event_status,
+ size);
+ if (rc)
+ pr_err("%s: copy to user failed %d\n",
+ __func__, rc);
+ }
+ kfree(event_status);
+ }
+ } else if (xchg) {
+ pr_debug("%s: Wait aborted\n", __func__);
+ rc = 0;
+ }
+ break;
+
+ case SNDRV_LSM_ABORT_EVENT:
+ pr_debug("%s: Aborting event status wait\n", __func__);
+ atomic_set(&prtd->event_wait_stop, 1);
+ wake_up(&prtd->event_wait);
+ break;
+
+ case SNDRV_LSM_START:
+ pr_debug("%s: Starting LSM client session\n", __func__);
+ if (!prtd->lsm_client->started) {
+ ret = q6lsm_start(prtd->lsm_client, true);
+ if (!ret) {
+ prtd->lsm_client->started = true;
+ pr_debug("%s: LSM client session started\n",
+ __func__);
+ }
+ }
+ break;
+
+ case SNDRV_LSM_STOP:
+ pr_debug("%s: Stopping LSM client session\n", __func__);
+ if (prtd->lsm_client->started) {
+ ret = q6lsm_stop(prtd->lsm_client, true);
+ if (!ret) {
+ prtd->lsm_client->started = false;
+ pr_debug("%s: LSM client session stopped\n",
+ __func__);
+ }
+ }
+ break;
+
+ default:
+ pr_debug("%s: Falling into default snd_lib_ioctl cmd 0x%x\n",
+ __func__, cmd);
+ rc = snd_pcm_lib_ioctl(substream, cmd, arg);
+ break;
+ }
+
+ if (!rc)
+ pr_debug("%s: leave (%d)\n", __func__, rc);
+ else
+ pr_err("%s: cmd 0x%x failed %d\n", __func__, cmd, rc);
+
+ return rc;
+}
+
+static int msm_lsm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct lsm_priv *prtd;
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+ prtd = kzalloc(sizeof(struct lsm_priv), GFP_KERNEL);
+ if (!prtd) {
+ pr_err("%s: Failed to allocate memory for lsm_priv\n",
+ __func__);
+ return -ENOMEM;
+ }
+ prtd->substream = substream;
+ prtd->lsm_client = q6lsm_client_alloc((app_cb)lsm_event_handler, prtd);
+ if (!prtd->lsm_client) {
+ pr_err("%s: Could not allocate memory\n", __func__);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+ ret = q6lsm_open(prtd->lsm_client);
+ if (ret < 0) {
+ pr_err("%s: lsm out open failed\n", __func__);
+ q6lsm_client_free(prtd->lsm_client);
+ kfree(prtd);
+ return -ENOMEM;
+ }
+
+ pr_debug("%s: Session ID %d\n", __func__, prtd->lsm_client->session);
+ prtd->lsm_client->started = false;
+ spin_lock_init(&prtd->event_lock);
+ init_waitqueue_head(&prtd->event_wait);
+ runtime->private_data = prtd;
+
+ return 0;
+}
+
+static int msm_lsm_close(struct snd_pcm_substream *substream)
+{
+ unsigned long flags;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct lsm_priv *prtd = runtime->private_data;
+
+ pr_debug("%s\n", __func__);
+
+ q6lsm_close(prtd->lsm_client);
+ q6lsm_client_free(prtd->lsm_client);
+
+ spin_lock_irqsave(&prtd->event_lock, flags);
+ kfree(prtd->event_status);
+ prtd->event_status = NULL;
+ spin_unlock_irqrestore(&prtd->event_lock, flags);
+ kfree(prtd);
+
+ return 0;
+}
+
+static struct snd_pcm_ops msm_lsm_ops = {
+ .open = msm_lsm_open,
+ .close = msm_lsm_close,
+ .ioctl = msm_lsm_ioctl,
+};
+
+static int msm_asoc_lsm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ return 0;
+}
+
+static int msm_asoc_lsm_probe(struct snd_soc_platform *platform)
+{
+ pr_debug("enter %s\n", __func__);
+
+ return 0;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+ .ops = &msm_lsm_ops,
+ .pcm_new = msm_asoc_lsm_new,
+ .probe = msm_asoc_lsm_probe,
+};
+
+static __devinit int msm_lsm_probe(struct platform_device *pdev)
+{
+ if (pdev->dev.of_node)
+ dev_set_name(&pdev->dev, "%s", "msm-lsm-client");
+
+ return snd_soc_register_platform(&pdev->dev, &msm_soc_platform);
+}
+
+static int msm_lsm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+
+ return 0;
+}
+
+static const struct of_device_id msm_lsm_client_dt_match[] = {
+ {.compatible = "qcom,msm-lsm-client" },
+ { }
+};
+
+static struct platform_driver msm_lsm_driver = {
+ .driver = {
+ .name = "msm-lsm-client",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(msm_lsm_client_dt_match),
+ },
+ .probe = msm_lsm_probe,
+ .remove = __devexit_p(msm_lsm_remove),
+};
+
+static int __init msm_soc_platform_init(void)
+{
+ return platform_driver_register(&msm_lsm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+ platform_driver_unregister(&msm_lsm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("LSM client platform driver");
+MODULE_DEVICE_TABLE(of, msm_lsm_client_dt_match);
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
index 9fe438c..819512d 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -55,6 +55,27 @@
static int fm_switch_enable;
static int fm_pcmrx_switch_enable;
static int srs_alsa_ctrl_ever_called;
+static int lsm_mux_slim_port;
+
+enum {
+ MADNONE,
+ MADAUDIO,
+ MADBEACON,
+ MADULTRASOUND
+};
+
+#define SLIMBUS_0_TX_TEXT "SLIMBUS_0_TX"
+#define SLIMBUS_1_TX_TEXT "SLIMBUS_1_TX"
+#define SLIMBUS_2_TX_TEXT "SLIMBUS_2_TX"
+#define SLIMBUS_3_TX_TEXT "SLIMBUS_3_TX"
+#define SLIMBUS_4_TX_TEXT "SLIMBUS_4_TX"
+#define SLIMBUS_5_TX_TEXT "SLIMBUS_5_TX"
+#define LSM_FUNCTION_TEXT "LSM Function"
+static const char * const mad_audio_mux_text[] = {
+ "None",
+ SLIMBUS_0_TX_TEXT, SLIMBUS_1_TX_TEXT, SLIMBUS_2_TX_TEXT,
+ SLIMBUS_3_TX_TEXT, SLIMBUS_4_TX_TEXT, SLIMBUS_5_TX_TEXT
+};
#define INT_RX_VOL_MAX_STEPS 0x2000
#define INT_RX_VOL_GAIN 0x2000
@@ -190,6 +211,7 @@
{ SLIMBUS_4_TX, 0, 0, 0, 0, 0},
{ SLIMBUS_3_RX, 0, 0, 0, 0, 0},
{ SLIMBUS_3_TX, 0, 0, 0, 0, 0},
+ { SLIMBUS_5_TX, 0, 0, 0, 0, 0 },
{ SLIMBUS_EXTPROC_RX, 0, 0, 0, 0, 0},
{ SLIMBUS_EXTPROC_RX, 0, 0, 0, 0, 0},
{ SLIMBUS_EXTPROC_RX, 0, 0, 0, 0, 0},
@@ -731,6 +753,118 @@
return 1;
}
+static int msm_routing_lsm_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = lsm_mux_slim_port;
+ return 0;
+}
+
+static int msm_routing_lsm_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int mux = ucontrol->value.enumerated.item[0];
+
+ pr_debug("%s: LSM enable %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ if (ucontrol->value.integer.value[0]) {
+ lsm_mux_slim_port = ucontrol->value.integer.value[0];
+ snd_soc_dapm_mux_update_power(widget, kcontrol, 1, mux, e);
+ } else {
+ snd_soc_dapm_mux_update_power(widget, kcontrol, 1, mux, e);
+ lsm_mux_slim_port = ucontrol->value.integer.value[0];
+ }
+
+ return 0;
+}
+
+static int msm_routing_lsm_func_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i;
+ u16 port_id;
+ enum afe_mad_type mad_type;
+
+ pr_debug("%s: enter\n", __func__);
+ for (i = 0; i < ARRAY_SIZE(mad_audio_mux_text); i++)
+ if (!strncmp(kcontrol->id.name, mad_audio_mux_text[i],
+ strlen(mad_audio_mux_text[i])))
+ break;
+
+ if (i-- == ARRAY_SIZE(mad_audio_mux_text)) {
+ WARN(1, "Invalid id name %s\n", kcontrol->id.name);
+ return -EINVAL;
+ }
+
+ port_id = i * 2 + 1 + SLIMBUS_0_RX;
+ mad_type = afe_port_get_mad_type(port_id);
+ pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id,
+ mad_type);
+ switch (mad_type) {
+ case MAD_HW_NONE:
+ ucontrol->value.integer.value[0] = MADNONE;
+ break;
+ case MAD_HW_AUDIO:
+ ucontrol->value.integer.value[0] = MADAUDIO;
+ break;
+ case MAD_HW_BEACON:
+ ucontrol->value.integer.value[0] = MADBEACON;
+ break;
+ case MAD_HW_ULTRASOUND:
+ ucontrol->value.integer.value[0] = MADULTRASOUND;
+ break;
+ default:
+ WARN(1, "Unknown\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int msm_routing_lsm_func_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int i;
+ u16 port_id;
+ enum afe_mad_type mad_type;
+
+ pr_debug("%s: enter\n", __func__);
+ for (i = 0; i < ARRAY_SIZE(mad_audio_mux_text); i++)
+ if (!strncmp(kcontrol->id.name, mad_audio_mux_text[i],
+ strlen(mad_audio_mux_text[i])))
+ break;
+
+ if (i-- == ARRAY_SIZE(mad_audio_mux_text)) {
+ WARN(1, "Invalid id name %s\n", kcontrol->id.name);
+ return -EINVAL;
+ }
+
+ port_id = i * 2 + 1 + SLIMBUS_0_RX;
+ switch (ucontrol->value.integer.value[0]) {
+ case MADNONE:
+ mad_type = MAD_HW_NONE;
+ break;
+ case MADAUDIO:
+ mad_type = MAD_HW_AUDIO;
+ break;
+ case MADBEACON:
+ mad_type = MAD_HW_BEACON;
+ break;
+ case MADULTRASOUND:
+ mad_type = MAD_HW_ULTRASOUND;
+ break;
+ default:
+ WARN(1, "Unknown\n");
+ return -EINVAL;
+ }
+
+ pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id,
+ mad_type);
+ return afe_port_set_mad_type(port_id, mad_type);
+}
+
static int msm_routing_get_port_mixer(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1734,6 +1868,33 @@
0, 1, 0, msm_routing_get_fm_pcmrx_switch_mixer,
msm_routing_put_fm_pcmrx_switch_mixer);
+static const struct soc_enum lsm_mux_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mad_audio_mux_text), mad_audio_mux_text);
+static const struct snd_kcontrol_new lsm_mux =
+ SOC_DAPM_ENUM_EXT("LSM1 MUX", lsm_mux_enum,
+ msm_routing_lsm_mux_get,
+ msm_routing_lsm_mux_put);
+
+static const char * const lsm_func_text[] = {
+ "None", "AUDIO", "BEACON", "ULTRASOUND"
+};
+static const struct soc_enum lsm_func_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lsm_func_text), lsm_func_text);
+static const struct snd_kcontrol_new lsm_function[] = {
+ SOC_ENUM_EXT(SLIMBUS_0_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+ msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+ SOC_ENUM_EXT(SLIMBUS_1_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+ msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+ SOC_ENUM_EXT(SLIMBUS_2_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+ msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+ SOC_ENUM_EXT(SLIMBUS_3_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+ msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+ SOC_ENUM_EXT(SLIMBUS_4_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+ msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+ SOC_ENUM_EXT(SLIMBUS_5_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+ msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+};
+
static const struct snd_kcontrol_new int_fm_vol_mixer_controls[] = {
SOC_SINGLE_EXT_TLV("Internal FM RX Volume", SND_SOC_NOPM, 0,
INT_RX_VOL_GAIN, 0, msm_routing_get_fm_vol_mixer,
@@ -2111,6 +2272,9 @@
0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("DTMF_DL_HL", "DTMF_RX_HOSTLESS Playback",
0, 0, 0, 0),
+ /* LSM */
+ SND_SOC_DAPM_AIF_OUT("LSM_UL_HL", "Listen Audio Service Capture",
+ 0, 0, 0, 0),
/* Backend AIF */
/* Stream name equals to backend dai link stream name
*/
@@ -2156,6 +2320,7 @@
0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("SLIMBUS_4_TX", "Slimbus4 Capture",
0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SLIMBUS_5_TX", "Slimbus5 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("AUX_PCM_RX", "AUX PCM Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("AUX_PCM_TX", "AUX PCM Capture", 0, 0, 0, 0),
@@ -2180,6 +2345,10 @@
&fm_switch_mixer_controls),
SND_SOC_DAPM_SWITCH("PCM_RX_DL_HL", SND_SOC_NOPM, 0, 0,
&pcm_rx_switch_mixer_controls),
+
+ /* Mux Definitions */
+ SND_SOC_DAPM_MUX("LSM1 MUX", SND_SOC_NOPM, 0, 0, &lsm_mux),
+
/* Mixer definitions */
SND_SOC_DAPM_MIXER("PRI_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
pri_i2s_rx_mixer_controls, ARRAY_SIZE(pri_i2s_rx_mixer_controls)),
@@ -2514,6 +2683,14 @@
{"SLIM1_UL_HL", NULL, "SLIMBUS_1_TX"},
{"SLIM3_UL_HL", NULL, "SLIMBUS_3_TX"},
{"SLIM4_UL_HL", NULL, "SLIMBUS_4_TX"},
+
+ {"LSM1 MUX", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
+ {"LSM1 MUX", "SLIMBUS_1_TX", "SLIMBUS_1_TX"},
+ {"LSM1 MUX", "SLIMBUS_3_TX", "SLIMBUS_3_TX"},
+ {"LSM1 MUX", "SLIMBUS_4_TX", "SLIMBUS_4_TX"},
+ {"LSM1 MUX", "SLIMBUS_5_TX", "SLIMBUS_5_TX"},
+ {"LSM_UL_HL", NULL, "LSM1 MUX"},
+
{"INT_FM_RX", NULL, "INTFM_DL_HL"},
{"INTFM_UL_HL", NULL, "INT_FM_TX"},
{"AUX_PCM_RX", NULL, "AUXPCM_DL_HL"},
@@ -2600,6 +2777,7 @@
{"SLIMBUS_1_TX", NULL, "BE_IN" },
{"SLIMBUS_3_TX", NULL, "BE_IN" },
{"SLIMBUS_4_TX", NULL, "BE_IN" },
+ {"SLIMBUS_5_TX", NULL, "BE_IN" },
{"BE_OUT", NULL, "INT_BT_SCO_RX"},
{"INT_BT_SCO_TX", NULL, "BE_IN"},
{"BE_OUT", NULL, "INT_FM_RX"},
@@ -2821,6 +2999,9 @@
snd_soc_add_platform_controls(platform,
multi_ch_channel_map_mixer_controls,
ARRAY_SIZE(multi_ch_channel_map_mixer_controls));
+
+ snd_soc_add_platform_controls(platform, lsm_function,
+ ARRAY_SIZE(lsm_function));
return 0;
}
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
index 7ecdff3..798f676 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
@@ -51,6 +51,7 @@
#define LPASS_BE_SLIMBUS_3_TX "SLIMBUS_3_TX"
#define LPASS_BE_SLIMBUS_4_RX "SLIMBUS_4_RX"
#define LPASS_BE_SLIMBUS_4_TX "SLIMBUS_4_TX"
+#define LPASS_BE_SLIMBUS_5_TX "SLIMBUS_5_TX"
/* For multimedia front-ends, asm session is allocated dynamically.
* Hence, asm session/multimedia front-end mapping has to be maintained.
@@ -71,6 +72,7 @@
MSM_FRONTEND_DAI_VOICE_STUB,
MSM_FRONTEND_DAI_VOLTE,
MSM_FRONTEND_DAI_DTMF_RX,
+ MSM_FRONTEND_DAI_LSM1,
MSM_FRONTEND_DAI_MAX,
};
@@ -103,6 +105,7 @@
MSM_BACKEND_DAI_SLIMBUS_4_TX,
MSM_BACKEND_DAI_SLIMBUS_3_RX,
MSM_BACKEND_DAI_SLIMBUS_3_TX,
+ MSM_BACKEND_DAI_SLIMBUS_5_TX,
MSM_BACKEND_DAI_EXTPROC_RX,
MSM_BACKEND_DAI_EXTPROC_TX,
MSM_BACKEND_DAI_EXTPROC_EC_TX,
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.h
index d0b119c..a25fb2a 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.h
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2013, The Linux Foundation. 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 and
@@ -11,7 +11,7 @@
*/
#ifndef _MSM_PCM_VOICE_H
#define _MSM_PCM_VOICE_H
-#include <sound/apr_audio.h>
+#include <sound/apr_audio-v2.h>
enum {
VOICE_SESSION_INDEX,
diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c
index 5e1da59..97cd3fc 100644
--- a/sound/soc/msm/qdsp6v2/q6afe.c
+++ b/sound/soc/msm/qdsp6v2/q6afe.c
@@ -9,7 +9,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
-
#include <linux/slab.h>
#include <linux/debugfs.h>
#include <linux/kernel.h>
@@ -22,6 +21,7 @@
#include <sound/apr_audio-v2.h>
#include <sound/q6afe-v2.h>
#include <sound/q6audio-v2.h>
+#include "msm-pcm-routing-v2.h"
#include "audio_acdb.h"
@@ -47,6 +47,8 @@
int vi_tx_port;
};
+static atomic_t afe_ports_mad_type[SLIMBUS_PORT_LAST - SLIMBUS_0_RX];
+
static struct afe_ctl this_afe;
#define TIMEOUT_MS 1000
@@ -121,6 +123,7 @@
case AFE_SERVICE_CMD_SHARED_MEM_UNMAP_REGIONS:
case AFE_SERVICE_CMD_UNREGISTER_RT_PORT_DRIVER:
case AFE_PORTS_CMD_DTMF_CTL:
+ case AFE_SVC_CMD_SET_PARAM:
atomic_set(&this_afe.state, 0);
wake_up(&this_afe.wait[data->token]);
break;
@@ -218,6 +221,7 @@
case SLIMBUS_2_TX:
case SLIMBUS_3_TX:
case SLIMBUS_4_TX:
+ case SLIMBUS_5_TX:
case INT_FM_TX:
case VOICE_RECORD_RX:
case INT_BT_SCO_TX:
@@ -298,6 +302,37 @@
return ret;
}
+/*
+ * afe_apr_send_pkt : returns 0 on success, negative otherwise.
+ */
+static int afe_apr_send_pkt(void *data, wait_queue_head_t *wait)
+{
+ int ret;
+
+ atomic_set(&this_afe.state, 1);
+ atomic_set(&this_afe.status, 0);
+ ret = apr_send_pkt(this_afe.apr, data);
+ if (ret > 0) {
+ if (wait) {
+ ret = wait_event_timeout(*wait,
+ (atomic_read(&this_afe.state) == 0),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (ret)
+ ret = 0;
+ else
+ ret = -ETIMEDOUT;
+ } else {
+ ret = 0;
+ }
+ } else if (ret == 0) {
+ /* apr_send_pkt can return 0 when nothing is transmitted */
+ ret = -EINVAL;
+ }
+
+ pr_debug("%s: leave %d\n", __func__, ret);
+ return ret;
+}
+
static void afe_send_cal_block(int32_t path, u16 port_id)
{
int result = 0;
@@ -350,22 +385,13 @@
__func__, port_id, path,
cal_block.cal_size, cal_block.cal_paddr);
- atomic_set(&this_afe.state, 1);
- result = apr_send_pkt(this_afe.apr, (uint32_t *) &afe_cal);
- if (result < 0) {
- pr_err("%s: AFE cal for port %d failed\n",
- __func__, port_id);
- }
-
- result = wait_event_timeout(this_afe.wait[index],
- (atomic_read(&this_afe.state) == 0),
- msecs_to_jiffies(TIMEOUT_MS));
- if (!result) {
- pr_err("%s: wait_event timeout SET AFE CAL\n", __func__);
- goto done;
- }
-
- pr_debug("%s: AFE cal sent for path %d device!\n", __func__, path);
+ result = afe_apr_send_pkt(&afe_cal, &this_afe.wait[index]);
+ if (result)
+ pr_err("%s: AFE cal for port %d failed %d\n",
+ __func__, port_id, result);
+ else
+ pr_debug("%s: AFE cal sent for path %d device!\n", __func__,
+ path);
done:
return;
}
@@ -506,14 +532,319 @@
}
}
+int afe_turn_onoff_hw_mad(u16 mad_type, u16 enable)
+{
+ int ret;
+ struct afe_cmd_hw_mad_ctrl config;
+
+ pr_debug("%s: enter\n", __func__);
+ memset(&config, 0, sizeof(config));
+ config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ config.hdr.pkt_size = sizeof(config);
+ config.hdr.src_port = 0;
+ config.hdr.dest_port = 0;
+ config.hdr.token = IDX_GLOBAL_CFG;
+ config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+ config.param.port_id = SLIMBUS_5_TX;
+ config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) -
+ sizeof(config.param);
+ config.param.payload_address_lsw = 0x00;
+ config.param.payload_address_msw = 0x00;
+ config.param.mem_map_handle = 0x00;
+ config.pdata.module_id = AFE_MODULE_HW_MAD;
+ config.pdata.param_id = AFE_PARAM_ID_HW_MAD_CTRL;
+ config.pdata.param_size = sizeof(config.payload);
+ config.payload.minor_version = 1;
+ config.payload.mad_type = mad_type;
+ config.payload.mad_enable = enable;
+
+ ret = afe_apr_send_pkt(&config, &this_afe.wait[IDX_GLOBAL_CFG]);
+ if (ret)
+ pr_err("%s: AFE_PARAM_ID_HW_MAD_CTRL failed %d\n", __func__,
+ ret);
+ return ret;
+}
+
+static int afe_send_slimbus_slave_cfg(
+ struct afe_param_cdc_slimbus_slave_cfg *sb_slave_cfg)
+{
+ int ret;
+ struct afe_svc_cmd_sb_slave_cfg config;
+
+ pr_debug("%s: enter\n", __func__);
+
+ config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ config.hdr.pkt_size = sizeof(config);
+ config.hdr.src_port = 0;
+ config.hdr.dest_port = 0;
+ config.hdr.token = IDX_GLOBAL_CFG;
+ config.hdr.opcode = AFE_SVC_CMD_SET_PARAM;
+ config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) -
+ sizeof(config.param);
+ config.param.payload_address_lsw = 0x00;
+ config.param.payload_address_msw = 0x00;
+ config.param.mem_map_handle = 0x00;
+ config.pdata.module_id = AFE_MODULE_CDC_DEV_CFG;
+ config.pdata.param_id = AFE_PARAM_ID_CDC_SLIMBUS_SLAVE_CFG;
+ config.pdata.param_size =
+ sizeof(struct afe_param_cdc_slimbus_slave_cfg);
+ config.sb_slave_cfg = *sb_slave_cfg;
+
+ ret = afe_apr_send_pkt(&config, &this_afe.wait[IDX_GLOBAL_CFG]);
+ if (ret)
+ pr_err("%s: AFE_PARAM_ID_CDC_SLIMBUS_SLAVE_CFG failed %d\n",
+ __func__, ret);
+
+ pr_debug("%s: leave %d\n", __func__, ret);
+ return ret;
+}
+
+static int afe_send_codec_reg_config(
+ struct afe_param_cdc_reg_cfg_data *cdc_reg_cfg)
+{
+ int i, ret;
+ int pkt_size, payload_size;
+ struct afe_svc_cmd_cdc_reg_cfg *config;
+ struct afe_svc_cmd_set_param *param;
+
+ pr_debug("%s: enter\n", __func__);
+ payload_size = sizeof(struct afe_param_cdc_reg_cfg_payload) *
+ cdc_reg_cfg->num_registers;
+ pkt_size = sizeof(*config) + payload_size;
+
+ pr_debug("%s: pkt_size %d, payload %d\n", __func__, pkt_size,
+ payload_size);
+ config = kzalloc(pkt_size, GFP_KERNEL);
+ if (!config) {
+ pr_warn("%s: Not enought memory, pkt_size %d\n", __func__,
+ pkt_size);
+ return -ENOMEM;
+ }
+
+ config->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ config->hdr.pkt_size = pkt_size;
+ config->hdr.src_port = 0;
+ config->hdr.dest_port = 0;
+ config->hdr.token = IDX_GLOBAL_CFG;
+ config->hdr.opcode = AFE_SVC_CMD_SET_PARAM;
+
+ param = &config->param;
+ param->payload_size = payload_size;
+ param->payload_address_lsw = 0x00;
+ param->payload_address_msw = 0x00;
+ param->mem_map_handle = 0x00;
+
+ for (i = 0; i < cdc_reg_cfg->num_registers; i++) {
+ config->reg_data[i].common.module_id = AFE_MODULE_CDC_DEV_CFG;
+ config->reg_data[i].common.param_id = AFE_PARAM_ID_CDC_REG_CFG;
+ config->reg_data[i].common.param_size =
+ sizeof(config->reg_data[i].reg_cfg);
+ config->reg_data[i].reg_cfg = cdc_reg_cfg->reg_data[i];
+ }
+
+ ret = afe_apr_send_pkt(config, &this_afe.wait[IDX_GLOBAL_CFG]);
+ if (ret)
+ pr_err("%s: AFE_PARAM_ID_CDC_REG_CFG failed %d\n", __func__,
+ ret);
+
+ kfree(config);
+ pr_debug("%s: leave ret %d\n", __func__, ret);
+ return ret;
+}
+
+static int afe_init_cdc_reg_config(void)
+{
+ int ret;
+ struct afe_svc_cmd_init_cdc_reg_cfg config;
+
+ pr_debug("%s: enter\n", __func__);
+ config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ config.hdr.pkt_size = sizeof(config);
+ config.hdr.src_port = 0;
+ config.hdr.dest_port = 0;
+ config.hdr.token = IDX_GLOBAL_CFG;
+ config.hdr.opcode = AFE_SVC_CMD_SET_PARAM;
+
+ config.param.payload_size = sizeof(struct afe_port_param_data_v2);
+ config.param.payload_address_lsw = 0x00;
+ config.param.payload_address_msw = 0x00;
+ config.param.mem_map_handle = 0x00;
+
+ config.init.module_id = AFE_MODULE_CDC_DEV_CFG;
+ config.init.param_id = AFE_PARAM_ID_CDC_REG_CFG_INIT;
+ config.init.param_size = 0;
+
+ ret = afe_apr_send_pkt(&config, &this_afe.wait[IDX_GLOBAL_CFG]);
+ if (ret) {
+ pr_err("%s: AFE_PARAM_ID_CDC_INIT_REG_CFG failed %d\n",
+ __func__, ret);
+ } else if (atomic_read(&this_afe.status) != 0) {
+ pr_err("%s: config cmd failed\n", __func__);
+ ret = -EINVAL;
+ }
+
+ pr_debug("%s: leave ret %d\n", __func__, 0);
+ return ret;
+}
+
+static int afe_send_slimbus_slave_port_cfg(
+ struct afe_param_slimbus_slave_port_cfg *port_config, u16 port_id)
+{
+ int ret, index;
+ struct afe_cmd_hw_mad_slimbus_slave_port_cfg config;
+
+ pr_debug("%s: enter, port_id %u\n", __func__, port_id);
+ index = q6audio_get_port_index(port_id);
+ if (q6audio_validate_port(port_id) < 0) {
+ pr_err("%s: port id: %#x\n", __func__, port_id);
+ return -EINVAL;
+ }
+
+ config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ config.hdr.pkt_size = sizeof(config);
+ config.hdr.src_port = 0;
+ config.hdr.dest_port = 0;
+ config.hdr.token = index;
+ config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+ config.param.port_id = port_id;
+ config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) -
+ sizeof(config.param);
+ config.param.payload_address_lsw = 0x00;
+ config.param.payload_address_msw = 0x00;
+ config.param.mem_map_handle = 0x00;
+ config.pdata.module_id = AFE_MODULE_HW_MAD;
+ config.pdata.param_id = AFE_PARAM_ID_SLIMBUS_SLAVE_PORT_CFG;
+ config.pdata.param_size = sizeof(*port_config);
+ config.sb_port_cfg = *port_config;
+
+ ret = afe_apr_send_pkt(&config, &this_afe.wait[index]);
+ if (ret) {
+ pr_err("%s: AFE_PARAM_ID_SLIMBUS_SLAVE_PORT_CFG failed %d\n",
+ __func__, ret);
+ } else if (atomic_read(&this_afe.status) != 0) {
+ pr_err("%s: config cmd failed\n", __func__);
+ ret = -EINVAL;
+ }
+ pr_debug("%s: leave %d\n", __func__, ret);
+ return ret;
+}
+
+int afe_port_set_mad_type(u16 port_id, enum afe_mad_type mad_type)
+{
+ int i;
+
+ i = port_id - SLIMBUS_0_RX;
+ if (i < 0 || i > ARRAY_SIZE(afe_ports_mad_type)) {
+ pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id);
+ return -EINVAL;
+ }
+ atomic_set(&afe_ports_mad_type[i], mad_type);
+ return 0;
+}
+
+enum afe_mad_type afe_port_get_mad_type(u16 port_id)
+{
+ int i;
+
+ i = port_id - SLIMBUS_0_RX;
+ if (i < 0 || i > ARRAY_SIZE(afe_ports_mad_type)) {
+ pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id);
+ return MAD_HW_NONE;
+ }
+ return (enum afe_mad_type) atomic_read(&afe_ports_mad_type[i]);
+}
+
+int afe_set_config(enum afe_config_type config_type, void *config_data, int arg)
+{
+ int ret;
+
+ pr_debug("%s: enter config_type %d\n", __func__, config_type);
+ ret = afe_q6_interface_prepare();
+ if (ret) {
+ pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ switch (config_type) {
+ case AFE_SLIMBUS_SLAVE_CONFIG:
+ ret = afe_send_slimbus_slave_cfg(config_data);
+ if (!ret)
+ ret = afe_init_cdc_reg_config();
+ else
+ pr_err("%s: Sending slimbus slave config failed %d\n",
+ __func__, ret);
+ break;
+ case AFE_CDC_REGISTERS_CONFIG:
+ ret = afe_send_codec_reg_config(config_data);
+ break;
+ case AFE_SLIMBUS_SLAVE_PORT_CONFIG:
+ ret = afe_send_slimbus_slave_port_cfg(config_data, arg);
+ break;
+ default:
+ pr_err("%s: unknown configuration type", __func__);
+ ret = -EINVAL;
+ }
+
+ pr_debug("%s: leave ret %d\n", __func__, ret);
+ return ret;
+}
+
+static int afe_send_cmd_port_start(u16 port_id)
+{
+ struct afe_port_cmd_device_start start;
+ int ret, index;
+
+ pr_debug("%s: enter\n", __func__);
+ index = q6audio_get_port_index(port_id);
+ if (q6audio_validate_port(port_id) < 0) {
+ pr_err("%s: port id: %#x\n", __func__, port_id);
+ return -EINVAL;
+ }
+
+ start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ start.hdr.pkt_size = sizeof(start);
+ start.hdr.src_port = 0;
+ start.hdr.dest_port = 0;
+ start.hdr.token = index;
+ start.hdr.opcode = AFE_PORT_CMD_DEVICE_START;
+ start.port_id = q6audio_get_port_id(port_id);
+ pr_debug("%s: cmd device start opcode[0x%x] port id[0x%x]\n",
+ __func__, start.hdr.opcode, start.port_id);
+
+ ret = afe_apr_send_pkt(&start, &this_afe.wait[index]);
+ if (ret) {
+ pr_err("%s: AFE enable for port %#x failed %d\n", __func__,
+ port_id, ret);
+ } else if (this_afe.task != current) {
+ this_afe.task = current;
+ pr_debug("task_name = %s pid = %d\n",
+ this_afe.task->comm, this_afe.task->pid);
+ }
+
+ pr_debug("%s: leave %d\n", __func__, ret);
+ return ret;
+}
+
int afe_port_start(u16 port_id, union afe_port_config *afe_config,
u32 rate) /* This function is no blocking */
{
- struct afe_port_cmd_device_start start;
struct afe_audioif_config_command config;
- int ret;
+ int ret = 0;
int cfg_type;
int index = 0;
+ enum afe_mad_type mad_type;
if (!afe_config) {
pr_err("%s: Error, no configuration data\n", __func__);
@@ -529,6 +860,7 @@
port_id = VIRTUAL_ID_TO_PORTID(port_id);
pr_debug("%s: port id: %#x\n", __func__, port_id);
+
index = q6audio_get_port_index(port_id);
if (q6audio_validate_port(port_id) < 0) {
pr_err("%s: port id: %#x\n", __func__, port_id);
@@ -539,11 +871,19 @@
if (IS_ERR_VALUE(ret))
return ret;
- if (q6audio_validate_port(port_id) < 0) {
- pr_err("%s: Failed : Invalid Port id = %#x\n", __func__,
- port_id);
- ret = -EINVAL;
- goto fail_cmd;
+ afe_send_cal(port_id);
+
+ /* Start SW MAD module */
+ mad_type = afe_port_get_mad_type(port_id);
+ pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id,
+ mad_type);
+ if (mad_type != MAD_HW_NONE) {
+ ret = afe_turn_onoff_hw_mad(mad_type, true);
+ if (ret) {
+ pr_err("%s: afe_turn_onoff_hw_mad failed %d\n",
+ __func__, ret);
+ return ret;
+ }
}
config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
@@ -593,6 +933,7 @@
case SLIMBUS_3_TX:
case SLIMBUS_4_RX:
case SLIMBUS_4_TX:
+ case SLIMBUS_5_TX:
cfg_type = AFE_PARAM_ID_SLIMBUS_CONFIG;
break;
case RT_PROXY_PORT_001_RX:
@@ -623,69 +964,17 @@
config.port = *afe_config;
- atomic_set(&this_afe.state, 1);
- atomic_set(&this_afe.status, 0);
- ret = apr_send_pkt(this_afe.apr, (uint32_t *) &config);
- if (ret < 0) {
+ ret = afe_apr_send_pkt(&config, &this_afe.wait[index]);
+ if (ret) {
pr_err("%s: AFE enable for port %#x failed\n", __func__,
port_id);
- ret = -EINVAL;
goto fail_cmd;
- }
- ret = wait_event_timeout(this_afe.wait[index],
- (atomic_read(&this_afe.state) == 0),
- msecs_to_jiffies(TIMEOUT_MS));
-
- if (!ret) {
- pr_err("%s: wait_event timeout IF CONFIG\n", __func__);
- ret = -EINVAL;
- goto fail_cmd;
- }
- if (atomic_read(&this_afe.status) != 0) {
+ } else if (atomic_read(&this_afe.status) != 0) {
pr_err("%s: config cmd failed\n", __func__);
ret = -EINVAL;
goto fail_cmd;
}
- /* send AFE cal */
- afe_send_cal(port_id);
-
- start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
- APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
- start.hdr.pkt_size = sizeof(start);
- start.hdr.src_port = 0;
- start.hdr.dest_port = 0;
- start.hdr.token = index;
- start.hdr.opcode = AFE_PORT_CMD_DEVICE_START;
- start.port_id = q6audio_get_port_id(port_id);
- pr_debug("%s: cmd device start opcode[0x%x] port id[0x%x]\n",
- __func__, start.hdr.opcode, start.port_id);
-
- atomic_set(&this_afe.state, 1);
- ret = apr_send_pkt(this_afe.apr, (uint32_t *) &start);
-
- if (IS_ERR_VALUE(ret)) {
- pr_err("%s: AFE enable for port %#x failed\n", __func__,
- port_id);
- ret = -EINVAL;
- goto fail_cmd;
- }
-
- ret = wait_event_timeout(this_afe.wait[index],
- (atomic_read(&this_afe.state) == 0),
- msecs_to_jiffies(TIMEOUT_MS));
-
- if (!ret) {
- pr_err("%s: wait_event timeout PORT START\n", __func__);
- ret = -EINVAL;
- goto fail_cmd;
- }
- if (this_afe.task != current)
- this_afe.task = current;
-
- pr_debug("task_name = %s pid = %d\n",
- this_afe.task->comm, this_afe.task->pid);
-
- return 0;
+ return afe_send_cmd_port_start(port_id);
fail_cmd:
return ret;
@@ -725,6 +1014,7 @@
case RT_PROXY_PORT_001_TX: return IDX_RT_PROXY_PORT_001_TX;
case SLIMBUS_4_RX: return IDX_SLIMBUS_4_RX;
case SLIMBUS_4_TX: return IDX_SLIMBUS_4_TX;
+ case SLIMBUS_5_TX: return IDX_SLIMBUS_5_TX;
case AFE_PORT_ID_PRIMARY_MI2S_RX:
return IDX_AFE_PORT_ID_PRIMARY_MI2S_RX;
case AFE_PORT_ID_QUATERNARY_MI2S_RX:
@@ -837,25 +1127,13 @@
__func__, config.param.payload_size, config.pdata.param_size,
sizeof(config), sizeof(config.param), sizeof(config.port),
sizeof(struct apr_hdr), config.pdata.param_id);
- atomic_set(&this_afe.state, 1);
- atomic_set(&this_afe.status, 0);
- ret = apr_send_pkt(this_afe.apr, (uint32_t *) &config);
- if (ret < 0) {
+
+ ret = afe_apr_send_pkt(&config, &this_afe.wait[index]);
+ if (ret) {
pr_err("%s: AFE enable for port %d opcode[0x%x]failed\n",
__func__, port_id, cfg_type);
- ret = -EINVAL;
goto fail_cmd;
- }
-
- ret = wait_event_timeout(this_afe.wait[index],
- (atomic_read(&this_afe.state) == 0),
- msecs_to_jiffies(TIMEOUT_MS));
- if (!ret) {
- pr_err("%s: wait_event timeout\n", __func__);
- ret = -EINVAL;
- goto fail_cmd;
- }
- if (atomic_read(&this_afe.status) != 0) {
+ } else if (atomic_read(&this_afe.status) != 0) {
pr_err("%s: config cmd failed\n", __func__);
ret = -EINVAL;
goto fail_cmd;
@@ -870,24 +1148,14 @@
start.port_id = q6audio_get_port_id(port_id);
pr_debug("%s: cmd device start opcode[0x%x] port id[0x%x]\n",
__func__, start.hdr.opcode, start.port_id);
- atomic_set(&this_afe.state, 1);
- ret = apr_send_pkt(this_afe.apr, (uint32_t *) &start);
- if (ret < 0) {
+
+ ret = afe_apr_send_pkt(&start, &this_afe.wait[index]);
+ if (ret) {
pr_err("%s: AFE enable for port %d failed\n", __func__,
port_id);
- ret = -EINVAL;
- goto fail_cmd;
- }
- ret = wait_event_timeout(this_afe.wait[index],
- (atomic_read(&this_afe.state) == 0),
- msecs_to_jiffies(TIMEOUT_MS));
- if (!ret) {
- pr_err("%s: wait_event timeout\n", __func__);
- ret = -EINVAL;
goto fail_cmd;
}
- return 0;
fail_cmd:
return ret;
}
@@ -934,23 +1202,10 @@
lb_cmd.routing_mode = LB_MODE_DEFAULT;
lb_cmd.enable = (enable ? 1 : 0);
lb_cmd.loopback_cfg_minor_version = AFE_API_VERSION_LOOPBACK_CONFIG;
- atomic_set(&this_afe.state, 1);
- ret = apr_send_pkt(this_afe.apr, (uint32_t *) &lb_cmd);
- if (ret < 0) {
- pr_err("%s: AFE loopback failed\n", __func__);
- ret = -EINVAL;
- goto done;
- }
- pr_debug("%s: waiting for this_afe.wait[%d]\n", __func__, index);
- ret = wait_event_timeout(this_afe.wait[index],
- (atomic_read(&this_afe.state) == 0),
- msecs_to_jiffies(TIMEOUT_MS));
- if (!ret) {
- pr_err("%s: wait_event timeout\n", __func__);
- ret = -EINVAL;
- }
-done:
+ ret = afe_apr_send_pkt(&lb_cmd, &this_afe.wait[index]);
+ if (ret)
+ pr_err("%s: AFE loopback failed %d\n", __func__, ret);
return ret;
}
@@ -1016,24 +1271,13 @@
set_param.rx_port_id = port_id;
set_param.gain = volume;
- atomic_set(&this_afe.state, 1);
- ret = apr_send_pkt(this_afe.apr, (uint32_t *) &set_param);
- if (ret < 0) {
+ ret = afe_apr_send_pkt(&set_param, &this_afe.wait[index]);
+ if (ret) {
pr_err("%s: AFE param set failed for port %d\n",
__func__, port_id);
- ret = -EINVAL;
goto fail_cmd;
}
- ret = wait_event_timeout(this_afe.wait[index],
- (atomic_read(&this_afe.state) == 0),
- msecs_to_jiffies(TIMEOUT_MS));
- if (ret < 0) {
- pr_err("%s: wait_event timeout\n", __func__);
- ret = -EINVAL;
- goto fail_cmd;
- }
- return 0;
fail_cmd:
return ret;
}
@@ -1060,9 +1304,8 @@
start.port_id = port_id;
start.timing = 1;
- atomic_set(&this_afe.state, 1);
- ret = apr_send_pkt(this_afe.apr, (uint32_t *) &start);
- if (ret < 0) {
+ ret = afe_apr_send_pkt(&start, NULL);
+ if (ret) {
pr_err("%s: AFE enable for port %d failed %d\n",
__func__, port_id, ret);
return -EINVAL;
@@ -1095,25 +1338,13 @@
start.hdr.opcode = AFE_PSEUDOPORT_CMD_START;
start.port_id = port_id;
start.timing = 1;
-
start.hdr.token = index;
- atomic_set(&this_afe.state, 1);
- ret = apr_send_pkt(this_afe.apr, (uint32_t *) &start);
- if (ret < 0) {
+
+ ret = afe_apr_send_pkt(&start, &this_afe.wait[index]);
+ if (ret)
pr_err("%s: AFE enable for port %d failed %d\n",
__func__, port_id, ret);
- return -EINVAL;
- }
-
- ret = wait_event_timeout(this_afe.wait[index],
- (atomic_read(&this_afe.state) == 0),
- msecs_to_jiffies(TIMEOUT_MS));
- if (!ret) {
- pr_err("%s: wait_event timeout\n", __func__);
- return -EINVAL;
- }
-
- return 0;
+ return ret;
}
int afe_pseudo_port_stop_nowait(u16 port_id)
@@ -1141,17 +1372,13 @@
stop.hdr.opcode = AFE_PSEUDOPORT_CMD_STOP;
stop.port_id = port_id;
stop.reserved = 0;
-
stop.hdr.token = index;
- atomic_set(&this_afe.state, 1);
- ret = apr_send_pkt(this_afe.apr, (uint32_t *) &stop);
- if (ret < 0) {
+
+ ret = afe_apr_send_pkt(&stop, NULL);
+ if (ret)
pr_err("%s: AFE close failed %d\n", __func__, ret);
- return -EINVAL;
- }
- return 0;
-
+ return ret;
}
int afe_stop_pseudo_port(u16 port_id)
@@ -1180,24 +1407,13 @@
stop.hdr.opcode = AFE_PSEUDOPORT_CMD_STOP;
stop.port_id = port_id;
stop.reserved = 0;
-
stop.hdr.token = index;
- atomic_set(&this_afe.state, 1);
- ret = apr_send_pkt(this_afe.apr, (uint32_t *) &stop);
- if (ret < 0) {
+
+ ret = afe_apr_send_pkt(&stop, &this_afe.wait[index]);
+ if (ret)
pr_err("%s: AFE close failed %d\n", __func__, ret);
- return -EINVAL;
- }
- ret = wait_event_timeout(this_afe.wait[index],
- (atomic_read(&this_afe.state) == 0),
- msecs_to_jiffies(TIMEOUT_MS));
- if (!ret) {
- pr_err("%s: wait_event timeout\n", __func__);
- return -EINVAL;
- }
-
- return 0;
+ return ret;
}
uint32_t afe_req_mmap_handle(struct afe_audio_client *ac)
@@ -1480,15 +1696,11 @@
mregion_pl->shm_addr_msw = 0x00;
mregion_pl->mem_size_bytes = dma_buf_sz;
- atomic_set(&this_afe.state, 1);
- ret = apr_send_pkt(this_afe.apr, (uint32_t *) mmap_region_cmd);
- if (ret < 0) {
+ ret = afe_apr_send_pkt(mmap_region_cmd, NULL);
+ if (ret)
pr_err("%s: AFE memory map cmd failed %d\n",
__func__, ret);
- ret = -EINVAL;
- return ret;
- }
- return 0;
+ return ret;
}
int q6afe_audio_client_buf_free_contiguous(unsigned int dir,
struct afe_audio_client *ac)
@@ -1576,24 +1788,11 @@
/* Todo */
index = mregion.hdr.token = IDX_RSVD_2;
- atomic_set(&this_afe.state, 1);
- ret = apr_send_pkt(this_afe.apr, (uint32_t *) &mregion);
- if (ret < 0) {
+ ret = afe_apr_send_pkt(&mregion, &this_afe.wait[index]);
+ if (ret)
pr_err("%s: AFE memory unmap cmd failed %d\n",
__func__, ret);
- ret = -EINVAL;
- return ret;
- }
-
- ret = wait_event_timeout(this_afe.wait[index],
- (atomic_read(&this_afe.state) == 0),
- msecs_to_jiffies(TIMEOUT_MS));
- if (!ret) {
- pr_err("%s: wait_event timeout\n", __func__);
- ret = -EINVAL;
- return ret;
- }
- return 0;
+ return ret;
}
int afe_cmd_memory_unmap_nowait(u32 mem_map_handle)
@@ -1623,13 +1822,11 @@
mregion.hdr.opcode = AFE_SERVICE_CMD_SHARED_MEM_UNMAP_REGIONS;
mregion.mem_map_handle = mem_map_handle;
- ret = apr_send_pkt(this_afe.apr, (uint32_t *) &mregion);
- if (ret < 0) {
+ ret = afe_apr_send_pkt(&mregion, NULL);
+ if (ret)
pr_err("%s: AFE memory unmap cmd failed %d\n",
__func__, ret);
- ret = -EINVAL;
- }
- return 0;
+ return ret;
}
int afe_register_get_events(u16 port_id,
@@ -1675,14 +1872,11 @@
rtproxy.port_id = port_id;
rtproxy.reserved = 0;
- ret = apr_send_pkt(this_afe.apr, (uint32_t *) &rtproxy);
- if (ret < 0) {
+ ret = afe_apr_send_pkt(&rtproxy, NULL);
+ if (ret)
pr_err("%s: AFE reg. rtproxy_event failed %d\n",
__func__, ret);
- ret = -EINVAL;
- return ret;
- }
- return 0;
+ return ret;
}
int afe_unregister_get_events(u16 port_id)
@@ -1734,24 +1928,11 @@
this_afe.rx_private_data = NULL;
}
- atomic_set(&this_afe.state, 1);
- ret = apr_send_pkt(this_afe.apr, (uint32_t *) &rtproxy);
- if (ret < 0) {
+ ret = afe_apr_send_pkt(&rtproxy, &this_afe.wait[index]);
+ if (ret)
pr_err("%s: AFE enable Unreg. rtproxy_event failed %d\n",
__func__, ret);
- ret = -EINVAL;
- return ret;
- }
-
- ret = wait_event_timeout(this_afe.wait[index],
- (atomic_read(&this_afe.state) == 0),
- msecs_to_jiffies(TIMEOUT_MS));
- if (!ret) {
- pr_err("%s: wait_event timeout\n", __func__);
- ret = -EINVAL;
- return ret;
- }
- return 0;
+ return ret;
}
int afe_rt_proxy_port_write(u32 buf_addr_p, u32 mem_map_handle, int bytes)
@@ -1781,14 +1962,11 @@
afecmd_wr.available_bytes = bytes;
afecmd_wr.reserved = 0;
- ret = apr_send_pkt(this_afe.apr, (uint32_t *) &afecmd_wr);
- if (ret < 0) {
+ ret = afe_apr_send_pkt(&afecmd_wr, NULL);
+ if (ret)
pr_err("%s: AFE rtproxy write to port 0x%x failed %d\n",
__func__, afecmd_wr.port_id, ret);
- ret = -EINVAL;
- return ret;
- }
- return 0;
+ return ret;
}
@@ -1818,14 +1996,11 @@
afecmd_rd.available_bytes = bytes;
afecmd_rd.mem_map_handle = mem_map_handle;
- ret = apr_send_pkt(this_afe.apr, (uint32_t *) &afecmd_rd);
- if (ret < 0) {
+ ret = afe_apr_send_pkt(&afecmd_rd, NULL);
+ if (ret)
pr_err("%s: AFE rtproxy read cmd to port 0x%x failed %d\n",
__func__, afecmd_rd.port_id, ret);
- ret = -EINVAL;
- return ret;
- }
- return 0;
+ return ret;
}
#ifdef CONFIG_DEBUG_FS
@@ -2102,25 +2277,10 @@
cmd_sidetone.routing_mode = LB_MODE_SIDETONE;
cmd_sidetone.enable = enable;
- atomic_set(&this_afe.state, 1);
- ret = apr_send_pkt(this_afe.apr, (uint32_t *) &cmd_sidetone);
- if (ret < 0) {
+ ret = afe_apr_send_pkt(&cmd_sidetone, &this_afe.wait[index]);
+ if (ret)
pr_err("%s: AFE sidetone failed for tx_port:%d rx_port:%d\n",
__func__, tx_port_id, rx_port_id);
- ret = -EINVAL;
- goto fail_cmd;
- }
-
- ret = wait_event_timeout(this_afe.wait[index],
- (atomic_read(&this_afe.state) == 0),
- msecs_to_jiffies(TIMEOUT_MS));
- if (ret < 0) {
- pr_err("%s: wait_event timeout\n", __func__);
- ret = -EINVAL;
- goto fail_cmd;
- }
- return 0;
-fail_cmd:
return ret;
}
@@ -2217,12 +2377,9 @@
stop.port_id = port_id;
stop.reserved = 0;
- ret = apr_send_pkt(this_afe.apr, (uint32_t *) &stop);
-
- if (IS_ERR_VALUE(ret)) {
+ ret = afe_apr_send_pkt(&stop, NULL);
+ if (ret)
pr_err("%s: AFE close failed\n", __func__);
- ret = -EINVAL;
- }
fail_cmd:
return ret;
@@ -2232,10 +2389,10 @@
int afe_close(int port_id)
{
struct afe_port_cmd_device_stop stop;
+ enum afe_mad_type mad_type;
int ret = 0;
int index = 0;
-
if (this_afe.apr == NULL) {
pr_err("AFE is already closed\n");
ret = -EINVAL;
@@ -2245,9 +2402,25 @@
port_id = q6audio_convert_virtual_to_portid(port_id);
index = q6audio_get_port_index(port_id);
- if (q6audio_validate_port(port_id) < 0)
+ if (q6audio_validate_port(port_id) < 0) {
+ pr_warn("%s: Not a valid port id 0x0%x\n", __func__, port_id);
return -EINVAL;
+ }
+ mad_type = afe_port_get_mad_type(port_id);
+ pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id,
+ mad_type);
+ if (mad_type != MAD_HW_NONE) {
+ pr_debug("%s: Turn off MAD\n", __func__);
+ ret = afe_turn_onoff_hw_mad(mad_type, false);
+ if (ret) {
+ pr_err("%s: afe_turn_onoff_hw_mad failed %d\n",
+ __func__, ret);
+ return ret;
+ }
+ } else {
+ pr_debug("%s: Not a MAD port\n", __func__);
+ }
stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
@@ -2259,23 +2432,10 @@
stop.port_id = q6audio_get_port_id(port_id);
stop.reserved = 0;
- atomic_set(&this_afe.state, 1);
- ret = apr_send_pkt(this_afe.apr, (uint32_t *) &stop);
+ ret = afe_apr_send_pkt(&stop, &this_afe.wait[index]);
+ if (ret)
+ pr_err("%s: AFE close failed %d\n", __func__, ret);
- if (ret < 0) {
- pr_err("%s: AFE close failed\n", __func__);
- ret = -EINVAL;
- goto fail_cmd;
- }
-
- ret = wait_event_timeout(this_afe.wait[index],
- (atomic_read(&this_afe.state) == 0),
- msecs_to_jiffies(TIMEOUT_MS));
- if (!ret) {
- pr_err("%s: wait_event timeout\n", __func__);
- ret = -EINVAL;
- goto fail_cmd;
- }
fail_cmd:
return ret;
}
diff --git a/sound/soc/msm/qdsp6v2/q6audio-v2.c b/sound/soc/msm/qdsp6v2/q6audio-v2.c
index 4ed0fb7..d3d335d 100644
--- a/sound/soc/msm/qdsp6v2/q6audio-v2.c
+++ b/sound/soc/msm/qdsp6v2/q6audio-v2.c
@@ -44,6 +44,7 @@
case SLIMBUS_2_RX: return IDX_SLIMBUS_2_RX;
case SLIMBUS_2_TX: return IDX_SLIMBUS_2_TX;
case SLIMBUS_4_TX: return IDX_SLIMBUS_4_TX;
+ case SLIMBUS_5_TX: return IDX_SLIMBUS_5_TX;
case INT_BT_SCO_RX: return IDX_INT_BT_SCO_RX;
case INT_BT_SCO_TX: return IDX_INT_BT_SCO_TX;
case INT_BT_A2DP_RX: return IDX_INT_BT_A2DP_RX;
@@ -91,6 +92,7 @@
case SLIMBUS_2_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_RX;
case SLIMBUS_2_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_TX;
case SLIMBUS_4_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX;
+ case SLIMBUS_5_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX;
case INT_BT_SCO_RX: return AFE_PORT_ID_INTERNAL_BT_SCO_RX;
case INT_BT_SCO_TX: return AFE_PORT_ID_INTERNAL_BT_SCO_TX;
case INT_BT_A2DP_RX: return AFE_PORT_ID_INTERNAL_BT_A2DP_RX;
@@ -109,7 +111,9 @@
case AFE_PORT_ID_SECONDARY_MI2S_TX:
return AFE_PORT_ID_SECONDARY_MI2S_TX;
- default: return -EINVAL;
+ default:
+ pr_warn("%s: Invalid port_id %d\n", __func__, port_id);
+ return -EINVAL;
}
}
int q6audio_convert_virtual_to_portid(u16 port_id)
@@ -185,6 +189,7 @@
case SLIMBUS_2_RX:
case SLIMBUS_2_TX:
case SLIMBUS_4_TX:
+ case SLIMBUS_5_TX:
case INT_BT_SCO_RX:
case INT_BT_SCO_TX:
case INT_BT_A2DP_RX:
diff --git a/sound/soc/msm/qdsp6v2/q6lsm.c b/sound/soc/msm/qdsp6v2/q6lsm.c
new file mode 100644
index 0000000..f2b531a
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/q6lsm.c
@@ -0,0 +1,813 @@
+/*
+ * Copyright (c) 2013, Linux Foundation. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/fs.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/memory_alloc.h>
+#include <linux/debugfs.h>
+#include <linux/time.h>
+#include <linux/atomic.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/lsm_params.h>
+#include <sound/q6lsm.h>
+#include <asm/ioctls.h>
+#include <mach/memory.h>
+#include <mach/debug_mm.h>
+#include <mach/msm_subsystem_map.h>
+#include "audio_acdb.h"
+
+#define APR_TIMEOUT (5 * HZ)
+#define LSM_CAL_SIZE 4096
+
+enum {
+ CMD_STATE_CLEARED = 0,
+ CMD_STATE_WAIT_RESP = 1,
+};
+
+enum {
+ LSM_INVALID_SESSION_ID = 0,
+ LSM_MIN_SESSION_ID = 1,
+ LSM_MAX_SESSION_ID = 8,
+};
+
+struct lsm_common {
+ void *apr;
+ atomic_t apr_users;
+ uint32_t lsm_cal_addr;
+ uint32_t lsm_cal_size;
+ uint32_t mmap_handle_for_cal;
+ struct mutex apr_lock;
+};
+
+static struct lsm_common lsm_common;
+
+/*
+ * mmap_handle_p can point either client->sound_model.mem_map_handle or
+ * lsm_common.mmap_handle_for_cal.
+ * mmap_lock must be held while accessing this.
+ */
+static spinlock_t mmap_lock;
+static uint32_t *mmap_handle_p;
+
+static spinlock_t lsm_session_lock;
+static struct lsm_client *lsm_session[LSM_MAX_SESSION_ID + 1];
+
+static int q6lsm_mmapcallback(struct apr_client_data *data, void *priv);
+static int q6lsm_send_cal(struct lsm_client *client);
+static int q6lsm_snd_model_buf_free(struct lsm_client *client);
+
+static int q6lsm_callback(struct apr_client_data *data, void *priv)
+{
+ struct lsm_client *client = (struct lsm_client *)priv;
+ uint32_t token;
+ uint32_t *payload;
+
+ if (!client || !data) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ payload = data->payload;
+ pr_debug("%s: Session %d opcode 0x%x token 0x%x payload size %d\n",
+ __func__, client->session,
+ data->opcode, data->token, data->payload_size);
+
+ if (data->opcode == APR_BASIC_RSP_RESULT) {
+ token = data->token;
+ switch (payload[0]) {
+ case LSM_SESSION_CMD_START:
+ case LSM_SESSION_CMD_STOP:
+ case LSM_SESSION_CMD_SET_PARAMS:
+ case LSM_SESSION_CMD_OPEN_TX:
+ case LSM_SESSION_CMD_CLOSE_TX:
+ case LSM_SESSION_CMD_REGISTER_SOUND_MODEL:
+ case LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL:
+ case LSM_SESSION_CMD_SHARED_MEM_UNMAP_REGIONS:
+ if (token != client->session &&
+ payload[0] !=
+ LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL) {
+ pr_err("%s: Invalid session %d receivced expected %d\n",
+ __func__, token, client->session);
+ return -EINVAL;
+ }
+ if (atomic_cmpxchg(&client->cmd_state,
+ CMD_STATE_WAIT_RESP,
+ CMD_STATE_CLEARED) ==
+ CMD_STATE_WAIT_RESP)
+ wake_up(&client->cmd_wait);
+ break;
+ default:
+ pr_debug("%s: Unknown command 0x%x\n",
+ __func__, payload[0]);
+ break;
+ }
+ return 0;
+ }
+
+ if (client->cb)
+ client->cb(data->opcode, data->token, data->payload,
+ client->priv);
+
+ return 0;
+}
+
+static int q6lsm_session_alloc(struct lsm_client *client)
+{
+ unsigned long flags;
+ int n, ret = -ENOMEM;
+
+ spin_lock_irqsave(&lsm_session_lock, flags);
+ for (n = LSM_MIN_SESSION_ID; n <= LSM_MAX_SESSION_ID; n++) {
+ if (!lsm_session[n]) {
+ lsm_session[n] = client;
+ ret = n;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&lsm_session_lock, flags);
+ return ret;
+}
+
+static void q6lsm_session_free(struct lsm_client *client)
+{
+ unsigned long flags;
+
+ pr_debug("%s: Freeing session ID %d\n", __func__, client->session);
+ spin_lock_irqsave(&lsm_session_lock, flags);
+ lsm_session[client->session] = LSM_INVALID_SESSION_ID;
+ spin_unlock_irqrestore(&lsm_session_lock, flags);
+ client->session = LSM_INVALID_SESSION_ID;
+}
+
+static void *q6lsm_mmap_apr_reg(void)
+{
+ if (atomic_inc_return(&lsm_common.apr_users) == 1) {
+ lsm_common.apr =
+ apr_register("ADSP", "LSM", q6lsm_mmapcallback,
+ 0x0FFFFFFFF, &lsm_common);
+ if (!lsm_common.apr) {
+ pr_debug("%s Unable to register APR LSM common port\n",
+ __func__);
+ atomic_dec(&lsm_common.apr_users);
+ }
+ }
+
+ return lsm_common.apr;
+}
+
+static int q6lsm_mmap_apr_dereg(void)
+{
+ if (atomic_read(&lsm_common.apr_users) <= 0) {
+ WARN("%s: APR common port already closed\n", __func__);
+ } else {
+ if (atomic_dec_return(&lsm_common.apr_users) == 0) {
+ apr_deregister(lsm_common.apr);
+ pr_debug("%s:APR De-Register common port\n", __func__);
+ }
+ }
+ return 0;
+}
+
+struct lsm_client *q6lsm_client_alloc(app_cb cb, void *priv)
+{
+ struct lsm_client *client;
+ int n;
+
+ pr_debug("%s: enter\n", __func__);
+ client = kzalloc(sizeof(struct lsm_client), GFP_KERNEL);
+ if (!client)
+ return NULL;
+ n = q6lsm_session_alloc(client);
+ if (n <= 0) {
+ kfree(client);
+ return NULL;
+ }
+
+ pr_debug("%s: New client session %d\n", __func__, client->session);
+ client->session = n;
+ client->cb = cb;
+ client->priv = priv;
+ client->apr = apr_register("ADSP", "LSM", q6lsm_callback,
+ ((client->session) << 8 | 0x0001), client);
+
+ if (client->apr == NULL) {
+ pr_err("%s: Registration with APR failed\n", __func__);
+ goto fail;
+ }
+
+ pr_debug("%s Registering the common port with APR\n", __func__);
+ client->mmap_apr = q6lsm_mmap_apr_reg();
+ if (!client->mmap_apr) {
+ pr_err("%s: APR registration failed\n", __func__);
+ goto fail;
+ }
+
+ init_waitqueue_head(&client->cmd_wait);
+ mutex_init(&client->cmd_lock);
+ atomic_set(&client->cmd_state, CMD_STATE_CLEARED);
+ pr_debug("%s: New client allocated\n", __func__);
+ return client;
+fail:
+ q6lsm_client_free(client);
+ return NULL;
+}
+
+void q6lsm_client_free(struct lsm_client *client)
+{
+ if (!client || !client->session)
+ return;
+
+ apr_deregister(client->apr);
+ client->mmap_apr = NULL;
+ q6lsm_session_free(client);
+ q6lsm_mmap_apr_dereg();
+ mutex_destroy(&client->cmd_lock);
+ kfree(client);
+}
+
+/*
+ * q6lsm_apr_send_pkt : If wait == true, hold mutex to prevent from preempting
+ * other thread's wait.
+ * If mmap_handle_p != NULL, disable irq and spin lock to
+ * protect mmap_handle_p
+ */
+static int q6lsm_apr_send_pkt(struct lsm_client *client, void *handle,
+ void *data, bool wait, uint32_t *mmap_p)
+{
+ int ret;
+ unsigned long flags = 0;
+
+ pr_debug("%s: enter wait %d\n", __func__, wait);
+ if (wait)
+ mutex_lock(&lsm_common.apr_lock);
+ if (mmap_p) {
+ WARN_ON(!wait);
+ spin_lock_irqsave(&mmap_lock, flags);
+ mmap_handle_p = mmap_p;
+ }
+ atomic_set(&client->cmd_state, CMD_STATE_WAIT_RESP);
+ ret = apr_send_pkt(client->apr, data);
+ if (mmap_p)
+ spin_unlock_irqrestore(&mmap_lock, flags);
+
+ if (ret < 0) {
+ pr_err("%s: apr_send_pkt failed %d\n", __func__, ret);
+ } else if (wait) {
+ ret = wait_event_timeout(client->cmd_wait,
+ (atomic_read(&client->cmd_state) ==
+ CMD_STATE_CLEARED),
+ APR_TIMEOUT);
+ if (likely(ret))
+ ret = 0;
+ else
+ pr_err("%s: wait timedout\n", __func__);
+ } else {
+ ret = 0;
+ }
+ if (wait)
+ mutex_unlock(&lsm_common.apr_lock);
+
+ pr_debug("%s: leave ret %d\n", __func__, ret);
+ return ret;
+}
+
+static void q6lsm_add_hdr(struct lsm_client *client, struct apr_hdr *hdr,
+ uint32_t pkt_size, bool cmd_flg)
+{
+ pr_debug("%s: pkt_size %d cmd_flg %d session %d\n", __func__,
+ pkt_size, cmd_flg, client->session);
+ hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(sizeof(struct apr_hdr)),
+ APR_PKT_VER);
+ hdr->src_svc = APR_SVC_LSM;
+ hdr->src_domain = APR_DOMAIN_APPS;
+ hdr->dest_svc = APR_SVC_LSM;
+ hdr->dest_domain = APR_DOMAIN_ADSP;
+ hdr->src_port = ((client->session << 8) & 0xFF00) | 0x01;
+ hdr->dest_port = ((client->session << 8) & 0xFF00) | 0x01;
+ hdr->pkt_size = pkt_size;
+ if (cmd_flg)
+ hdr->token = client->session;
+}
+
+int q6lsm_open(struct lsm_client *client)
+{
+ int rc;
+ struct lsm_stream_cmd_open_tx open;
+
+ memset(&open, 0, sizeof(open));
+ q6lsm_add_hdr(client, &open.hdr, sizeof(open), true);
+
+ open.hdr.opcode = LSM_SESSION_CMD_OPEN_TX;
+ open.app_id = 1;
+ open.sampling_rate = 16000;
+
+ rc = q6lsm_apr_send_pkt(client, client->apr, &open, true, NULL);
+ if (rc)
+ pr_err("%s: Open failed opcode 0x%x, rc %d\n",
+ __func__, open.hdr.opcode, rc);
+
+ pr_debug("%s: leave %d\n", __func__, rc);
+ return rc;
+}
+
+static int q6lsm_set_params(struct lsm_client *client)
+{
+ int rc;
+ struct lsm_cmd_set_params params;
+ struct lsm_params_payload *payload = ¶ms.payload;
+
+ pr_debug("%s: enter\n", __func__);
+ q6lsm_add_hdr(client, ¶ms.hdr, sizeof(params), true);
+
+ params.hdr.opcode = LSM_SESSION_CMD_SET_PARAMS;
+ params.data_payload_addr_lsw = 0;
+ params.data_payload_addr_msw = 0;
+ params.mem_map_handle = 0;
+ params.data_payload_size = sizeof(struct lsm_params_payload);
+
+ payload->op_mode.common.module_id = LSM_MODULE_ID_VOICE_WAKEUP;
+ payload->op_mode.common.param_id = LSM_PARAM_ID_OPERATION_MODE;
+ payload->op_mode.common.param_size =
+ sizeof(struct lsm_param_op_mode) - sizeof(payload->op_mode.common);
+ payload->op_mode.common.reserved = 0;
+ payload->op_mode.minor_version = 1;
+ payload->op_mode.mode = client->mode;
+ payload->op_mode.reserved = 0;
+
+ payload->connect_to_port.common.module_id = LSM_MODULE_ID_VOICE_WAKEUP;
+ payload->connect_to_port.common.param_id = LSM_PARAM_ID_CONNECT_TO_PORT;
+ payload->connect_to_port.common.param_size =
+ sizeof(payload->connect_to_port) - sizeof(payload->op_mode.common);
+ payload->connect_to_port.common.reserved = 0;
+ payload->connect_to_port.minor_version = 1;
+ payload->connect_to_port.port_id = client->connect_to_port;
+ payload->connect_to_port.reserved = 0;
+
+ payload->kwds.common.module_id = LSM_MODULE_ID_VOICE_WAKEUP;
+ payload->kwds.common.param_id = LSM_PARAM_ID_KEYWORD_DETECT_SENSITIVITY;
+ payload->kwds.common.param_size =
+ sizeof(payload->kwds) - sizeof(payload->op_mode.common);
+ payload->kwds.common.reserved = 0;
+ payload->kwds.minor_version = 1;
+ payload->kwds.keyword_sensitivity = client->kw_sensitivity;
+ payload->kwds.reserved = 0;
+
+ payload->uds.common.module_id = LSM_MODULE_ID_VOICE_WAKEUP;
+ payload->uds.common.param_id = LSM_PARAM_ID_USER_DETECT_SENSITIVITY;
+ payload->uds.common.param_size =
+ sizeof(payload->uds) - sizeof(payload->op_mode.common);
+ payload->uds.common.reserved = 0;
+ payload->uds.minor_version = 1;
+ payload->uds.user_sensitivity = client->user_sensitivity;
+ payload->uds.reserved = 0;
+
+ rc = q6lsm_apr_send_pkt(client, client->apr, ¶ms, true, NULL);
+ if (rc)
+ pr_err("%s: Failed set_params opcode 0x%x, rc %d\n",
+ __func__, params.hdr.opcode, rc);
+
+ pr_debug("%s: leave %d\n", __func__, rc);
+ return rc;
+}
+
+int q6lsm_register_sound_model(struct lsm_client *client,
+ enum lsm_detection_mode mode, u16 minkeyword,
+ u16 minuser, bool detectfailure)
+{
+ int rc;
+ struct lsm_cmd_reg_snd_model cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ if (mode == LSM_MODE_KEYWORD_ONLY_DETECTION) {
+ client->mode = 0x01;
+ } else if (mode == LSM_MODE_USER_KEYWORD_DETECTION) {
+ client->mode = 0x03;
+ } else {
+ pr_err("%s: Incorrect detection mode %d\n", __func__, mode);
+ return -EINVAL;
+ }
+ client->mode |= detectfailure << 2;
+ client->kw_sensitivity = minkeyword;
+ client->user_sensitivity = minuser;
+ client->connect_to_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX;
+
+ rc = q6lsm_set_params(client);
+ if (rc < 0) {
+ pr_err("%s: Failed to set lsm config params\n", __func__);
+ return rc;
+ }
+ rc = q6lsm_send_cal(client);
+ if (rc < 0) {
+ pr_err("%s: Failed to send calibration data\n", __func__);
+ return rc;
+ }
+
+ q6lsm_add_hdr(client, &cmd.hdr, sizeof(cmd), true);
+ cmd.hdr.opcode = LSM_SESSION_CMD_REGISTER_SOUND_MODEL;
+ cmd.model_addr_lsw = client->sound_model.phys;
+ cmd.model_addr_msw = 0;
+ cmd.model_size = client->sound_model.size;
+ /* read updated mem_map_handle by q6lsm_mmapcallback */
+ rmb();
+ cmd.mem_map_handle = client->sound_model.mem_map_handle;
+
+ pr_debug("%s: lsw %x, size %d, handle %x\n", __func__,
+ cmd.model_addr_lsw, cmd.model_size, cmd.mem_map_handle);
+ rc = q6lsm_apr_send_pkt(client, client->apr, &cmd, true, NULL);
+ if (rc)
+ pr_err("%s: Failed cmd op[0x%x]rc[%d]\n", __func__,
+ cmd.hdr.opcode, rc);
+ else
+ pr_debug("%s: Register sound model succeeded\n", __func__);
+
+ return rc;
+}
+
+int q6lsm_deregister_sound_model(struct lsm_client *client)
+{
+ int rc;
+ struct lsm_cmd_reg_snd_model cmd;
+
+ if (!client || !client->apr) {
+ pr_err("APR handle NULL\n");
+ return -EINVAL;
+ }
+ pr_debug("%s: session[%d]", __func__, client->session);
+
+ memset(&cmd, 0, sizeof(cmd));
+ q6lsm_add_hdr(client, &cmd.hdr, sizeof(cmd.hdr), false);
+ cmd.hdr.opcode = LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL;
+
+ rc = q6lsm_apr_send_pkt(client, client->apr, &cmd.hdr, true, NULL);
+ if (rc < 0) {
+ pr_err("%s: Failed cmd opcode 0x%x, rc %d\n", __func__,
+ cmd.hdr.opcode, rc);
+ } else {
+ pr_debug("%s: Deregister sound model succeeded\n", __func__);
+ q6lsm_snd_model_buf_free(client);
+ }
+
+ return rc;
+}
+
+static void q6lsm_add_mmaphdr(struct lsm_client *client, struct apr_hdr *hdr,
+ u32 pkt_size, u32 cmd_flg, u32 token)
+{
+ pr_debug("%s:pkt size=%d cmd_flg=%d session=%d\n", __func__, pkt_size,
+ cmd_flg, client->session);
+ hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ hdr->src_port = 0x00;
+ hdr->dest_port = client->session;
+ if (cmd_flg)
+ hdr->token = token;
+ hdr->pkt_size = pkt_size;
+ return;
+}
+
+static int q6lsm_memory_map_regions(struct lsm_client *client,
+ uint32_t dma_addr_p, uint32_t dma_buf_sz,
+ uint32_t *mmap_p)
+{
+ struct avs_cmd_shared_mem_map_regions *mmap_regions = NULL;
+ struct avs_shared_map_region_payload *mregions = NULL;
+ void *mmap_region_cmd = NULL;
+ void *payload = NULL;
+ int rc;
+ int cmd_size = 0;
+
+ pr_debug("%s: dma_addr_p 0x%x, dma_buf_sz %d, session %d\n",
+ __func__, dma_addr_p, dma_buf_sz, client->session);
+
+ cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions) +
+ sizeof(struct avs_shared_map_region_payload);
+
+ mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+ if (!mmap_region_cmd)
+ return -ENOMEM;
+
+ mmap_regions = (struct avs_cmd_shared_mem_map_regions *)mmap_region_cmd;
+ q6lsm_add_mmaphdr(client, &mmap_regions->hdr, cmd_size, true,
+ (client->session << 8));
+
+ mmap_regions->hdr.opcode = LSM_SESSION_CMD_SHARED_MEM_MAP_REGIONS;
+ mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
+ mmap_regions->num_regions = 1;
+ mmap_regions->property_flag = 0x00;
+ payload = ((u8 *)mmap_region_cmd +
+ sizeof(struct avs_cmd_shared_mem_map_regions));
+ mregions = (struct avs_shared_map_region_payload *)payload;
+
+ mregions->shm_addr_lsw = dma_addr_p;
+ mregions->shm_addr_msw = 0;
+ mregions->mem_size_bytes = dma_buf_sz;
+
+ rc = q6lsm_apr_send_pkt(client, client->mmap_apr, mmap_region_cmd,
+ true, mmap_p);
+ if (rc)
+ pr_err("%s: Failed mmap_regions opcode 0x%x, rc %d\n",
+ __func__, mmap_regions->hdr.opcode, rc);
+
+ pr_debug("%s: leave %d\n", __func__, rc);
+ kfree(mmap_region_cmd);
+ return rc;
+}
+
+static int q6lsm_send_cal(struct lsm_client *client)
+{
+ int rc;
+
+ struct lsm_cmd_set_params params;
+ struct acdb_cal_block lsm_cal;
+
+ pr_debug("%s: enter\n", __func__);
+
+ memset(&lsm_cal, 0, sizeof(lsm_cal));
+ get_lsm_cal(&lsm_cal);
+ if (!lsm_cal.cal_size) {
+ pr_err("%s: Could not get LSM calibration data\n", __func__);
+ rc = -EINVAL;
+ goto bail;
+ }
+
+ /* Cache mmap address, only map once or if new addr */
+ if ((lsm_common.lsm_cal_addr != lsm_cal.cal_paddr) ||
+ (lsm_cal.cal_size > lsm_common.lsm_cal_size)) {
+ if (lsm_common.lsm_cal_addr != 0)
+ afe_cmd_memory_unmap(lsm_cal.cal_paddr);
+
+ rc = q6lsm_memory_map_regions(client, lsm_cal.cal_paddr,
+ LSM_CAL_SIZE,
+ &lsm_common.mmap_handle_for_cal);
+ if (rc < 0) {
+ pr_err("%s: Calibration data memory map failed\n",
+ __func__);
+ goto bail;
+ }
+ lsm_common.lsm_cal_addr = lsm_cal.cal_paddr;
+ lsm_common.lsm_cal_size = LSM_CAL_SIZE;
+ }
+
+ q6lsm_add_hdr(client, ¶ms.hdr, sizeof(params), true);
+ params.hdr.opcode = LSM_SESSION_CMD_SET_PARAMS;
+ params.data_payload_addr_lsw = lsm_cal.cal_paddr;
+ params.data_payload_addr_msw = 0;
+ /* read updated mem_map_handle by q6lsm_mmapcallback */
+ rmb();
+ params.mem_map_handle = lsm_common.mmap_handle_for_cal;
+ params.data_payload_size = lsm_cal.cal_size;
+
+ rc = q6lsm_apr_send_pkt(client, client->apr, ¶ms, true, NULL);
+ if (rc)
+ pr_err("%s: Failed set_params opcode 0x%x, rc %d\n",
+ __func__, params.hdr.opcode, rc);
+bail:
+ return rc;
+}
+
+static int q6lsm_memory_unmap_regions(struct lsm_client *client)
+{
+ struct avs_cmd_shared_mem_unmap_regions unmap;
+ int rc = 0;
+ int cmd_size = 0;
+
+ cmd_size = sizeof(struct avs_cmd_shared_mem_unmap_regions);
+ q6lsm_add_mmaphdr(client, &unmap.hdr, cmd_size,
+ true, (client->session << 8));
+ unmap.hdr.opcode = LSM_SESSION_CMD_SHARED_MEM_UNMAP_REGIONS;
+ unmap.mem_map_handle = client->sound_model.mem_map_handle;
+
+ pr_debug("%s: unmap handle 0x%x\n", __func__, unmap.mem_map_handle);
+ rc = q6lsm_apr_send_pkt(client, client->mmap_apr, &unmap, true,
+ NULL);
+ if (rc)
+ pr_err("%s: Failed mmap_regions opcode 0x%x rc %d\n",
+ __func__, unmap.hdr.opcode, rc);
+
+ return rc;
+}
+
+static int q6lsm_snd_model_buf_free(struct lsm_client *client)
+{
+ int rc;
+
+ pr_debug("%s: Session id %d\n", __func__, client->session);
+ mutex_lock(&client->cmd_lock);
+ rc = q6lsm_memory_unmap_regions(client);
+ if (rc < 0) {
+ pr_err("%s CMD Memory_unmap_regions failed\n", __func__);
+ } else if (client->sound_model.data) {
+ ion_unmap_kernel(client->sound_model.client,
+ client->sound_model.handle);
+ ion_free(client->sound_model.client,
+ client->sound_model.handle);
+ ion_client_destroy(client->sound_model.client);
+ client->sound_model.data = NULL;
+ client->sound_model.phys = 0;
+ }
+ mutex_unlock(&client->cmd_lock);
+ return rc;
+}
+
+static struct lsm_client *q6lsm_get_lsm_client(int session_id)
+{
+ unsigned long flags;
+ struct lsm_client *client = NULL;
+
+ spin_lock_irqsave(&lsm_session_lock, flags);
+ if (session_id < LSM_MIN_SESSION_ID || session_id > LSM_MAX_SESSION_ID)
+ pr_err("%s: Invalid session %d\n", __func__, session_id);
+ else if (!lsm_session[session_id])
+ pr_err("%s: Not an active session %d\n", __func__, session_id);
+ else
+ client = lsm_session[session_id];
+ spin_unlock_irqrestore(&lsm_session_lock, flags);
+
+ return client;
+}
+
+/*
+ * q6lsm_mmapcallback : atomic context
+ */
+static int q6lsm_mmapcallback(struct apr_client_data *data, void *priv)
+{
+ unsigned long flags;
+ uint32_t sid = 0;
+ const uint32_t *payload = data->payload;
+ const uint32_t command = payload[0];
+ const uint32_t retcode = payload[1];
+ struct lsm_client *client = NULL;
+
+ pr_debug("%s: opcode 0x%x command 0x%x return code 0x%x\n", __func__,
+ data->opcode, command, retcode);
+
+ sid = (data->token >> 8) & 0x0F;
+ client = q6lsm_get_lsm_client(sid);
+ if (!client) {
+ pr_debug("%s: Session %d already freed\n", __func__, sid);
+ return 0;
+ }
+
+ switch (data->opcode) {
+ case LSM_SESSION_CMDRSP_SHARED_MEM_MAP_REGIONS:
+ if (atomic_read(&client->cmd_state) == CMD_STATE_WAIT_RESP) {
+ spin_lock_irqsave(&mmap_lock, flags);
+ *mmap_handle_p = command;
+ /* spin_unlock_irqrestore implies barrier */
+ spin_unlock_irqrestore(&mmap_lock, flags);
+ atomic_set(&client->cmd_state, CMD_STATE_CLEARED);
+ wake_up(&client->cmd_wait);
+ }
+ break;
+ case APR_BASIC_RSP_RESULT:
+ if (command == LSM_SESSION_CMD_SHARED_MEM_UNMAP_REGIONS) {
+ atomic_set(&client->cmd_state, CMD_STATE_CLEARED);
+ wake_up(&client->cmd_wait);
+ } else {
+ pr_warn("%s: Unexpected command 0x%x\n", __func__,
+ command);
+ }
+ break;
+ default:
+ pr_debug("%s: command 0x%x return code 0x%x\n",
+ __func__, command, retcode);
+ break;
+ }
+ if (client->cb)
+ client->cb(data->opcode, data->token,
+ data->payload, client->priv);
+ return 0;
+}
+
+int q6lsm_snd_model_buf_alloc(struct lsm_client *client, uint32_t len)
+{
+ int rc = -EINVAL;
+
+ if (!client)
+ goto fail;
+
+ mutex_lock(&client->cmd_lock);
+ if (!client->sound_model.data) {
+ client->sound_model.client =
+ msm_ion_client_create(UINT_MAX, "lsm_client");
+ if (IS_ERR_OR_NULL(client->sound_model.client)) {
+ pr_err("%s: ION create client for AUDIO failed\n",
+ __func__);
+ goto fail;
+ }
+ client->sound_model.handle =
+ ion_alloc(client->sound_model.client,
+ len, SZ_4K, (0x1 << ION_AUDIO_HEAP_ID), 0);
+ if (IS_ERR_OR_NULL(client->sound_model.handle)) {
+ pr_err("%s: ION memory allocation for AUDIO failed\n",
+ __func__);
+ goto fail;
+ }
+
+ rc = ion_phys(client->sound_model.client,
+ client->sound_model.handle,
+ (ion_phys_addr_t *)&client->sound_model.phys,
+ (size_t *)&len);
+ if (rc) {
+ pr_err("%s: ION get physical mem failed, rc%d\n",
+ __func__, rc);
+ goto fail;
+ }
+
+ client->sound_model.data =
+ ion_map_kernel(client->sound_model.client,
+ client->sound_model.handle);
+ if (IS_ERR_OR_NULL(client->sound_model.data)) {
+ pr_err("%s: ION memory mapping failed\n", __func__);
+ goto fail;
+ }
+ memset(client->sound_model.data, 0, len);
+ client->sound_model.size = len;
+ } else {
+ rc = -EBUSY;
+ goto fail;
+ }
+ mutex_unlock(&client->cmd_lock);
+
+ rc = q6lsm_memory_map_regions(client, client->sound_model.phys,
+ client->sound_model.size,
+ &client->sound_model.mem_map_handle);
+ if (rc < 0) {
+ pr_err("%s:CMD Memory_map_regions failed\n", __func__);
+ goto fail;
+ }
+
+ return 0;
+fail:
+ q6lsm_snd_model_buf_free(client);
+ return rc;
+}
+
+static int q6lsm_cmd(struct lsm_client *client, int opcode, bool wait)
+{
+ struct apr_hdr hdr;
+ int rc;
+
+ pr_debug("%s: enter opcode %d wait %d\n", __func__, opcode, wait);
+ q6lsm_add_hdr(client, &hdr, sizeof(hdr), true);
+ switch (opcode) {
+ case LSM_SESSION_CMD_START:
+ case LSM_SESSION_CMD_STOP:
+ case LSM_SESSION_CMD_CLOSE_TX:
+ hdr.opcode = opcode;
+ break;
+ default:
+ pr_err("%s: Invalid opcode %d\n", __func__, opcode);
+ return -EINVAL;
+ }
+ rc = q6lsm_apr_send_pkt(client, client->apr, &hdr, wait, NULL);
+ if (rc)
+ pr_err("%s: Failed commmand 0x%x\n", __func__, hdr.opcode);
+
+ pr_debug("%s: leave %d\n", __func__, rc);
+ return rc;
+}
+
+int q6lsm_start(struct lsm_client *client, bool wait)
+{
+ return q6lsm_cmd(client, LSM_SESSION_CMD_START, wait);
+}
+
+int q6lsm_stop(struct lsm_client *client, bool wait)
+{
+ return q6lsm_cmd(client, LSM_SESSION_CMD_STOP, wait);
+}
+
+int q6lsm_close(struct lsm_client *client)
+{
+ return q6lsm_cmd(client, LSM_SESSION_CMD_CLOSE_TX, true);
+}
+
+static int __init q6lsm_init(void)
+{
+ pr_debug("%s\n", __func__);
+ spin_lock_init(&lsm_session_lock);
+ spin_lock_init(&mmap_lock);
+ mutex_init(&lsm_common.apr_lock);
+ return 0;
+}
+
+device_initcall(q6lsm_init);