Merge "msm: ipa: Add MBIM tethering 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-coresight.dtsi b/arch/arm/boot/dts/msm8226-coresight.dtsi
index 30d79df..b891d3d 100644
--- a/arch/arm/boot/dts/msm8226-coresight.dtsi
+++ b/arch/arm/boot/dts/msm8226-coresight.dtsi
@@ -23,6 +23,7 @@
coresight-id = <0>;
coresight-name = "coresight-tmc-etr";
coresight-nr-inports = <1>;
+ coresight-ctis = <&cti0 &cti8>;
};
tpiu: tpiu@fc318000 {
@@ -60,6 +61,7 @@
coresight-child-list = <&replicator>;
coresight-child-ports = <0>;
coresight-default-sink;
+ coresight-ctis = <&cti0 &cti8>;
};
funnel_merg: funnel@fc31b000 {
@@ -213,4 +215,144 @@
qcom,blk-size = <3>;
};
+
+ cti0: cti@fc308000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc308000 0x1000>;
+ reg-names = "cti0-base";
+
+ coresight-id = <15>;
+ coresight-name = "coresight-cti0";
+ coresight-nr-inports = <0>;
+ };
+
+ cti1: cti@fc309000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc309000 0x1000>;
+ reg-names = "cti1-base";
+
+ coresight-id = <16>;
+ coresight-name = "coresight-cti1";
+ coresight-nr-inports = <0>;
+ };
+
+ cti2: cti@fc30a000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc30a000 0x1000>;
+ reg-names = "cti2-base";
+
+ coresight-id = <17>;
+ coresight-name = "coresight-cti2";
+ coresight-nr-inports = <0>;
+ };
+
+ cti3: cti@fc30b000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc30b000 0x1000>;
+ reg-names = "cti3-base";
+
+ coresight-id = <18>;
+ coresight-name = "coresight-cti3";
+ coresight-nr-inports = <0>;
+ };
+
+ cti4: cti@fc30c000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc30c000 0x1000>;
+ reg-names = "cti4-base";
+
+ coresight-id = <19>;
+ coresight-name = "coresight-cti4";
+ coresight-nr-inports = <0>;
+ };
+
+ cti5: cti@fc30d000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc30d000 0x1000>;
+ reg-names = "cti5-base";
+
+ coresight-id = <20>;
+ coresight-name = "coresight-cti5";
+ coresight-nr-inports = <0>;
+ };
+
+ cti6: cti@fc30e000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc30e000 0x1000>;
+ reg-names = "cti6-base";
+
+ coresight-id = <21>;
+ coresight-name = "coresight-cti6";
+ coresight-nr-inports = <0>;
+ };
+
+ cti7: cti@fc30f000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc30f000 0x1000>;
+ reg-names = "cti7-base";
+
+ coresight-id = <22>;
+ coresight-name = "coresight-cti7";
+ coresight-nr-inports = <0>;
+ };
+
+ cti8: cti@fc310000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc310000 0x1000>;
+ reg-names = "cti8-base";
+
+ coresight-id = <23>;
+ coresight-name = "coresight-cti8";
+ coresight-nr-inports = <0>;
+ };
+
+ cti_l2: cti@fc340000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc340000 0x1000>;
+ reg-names = "cti-l2-base";
+
+ coresight-id = <24>;
+ coresight-name = "coresight-cti-l2";
+ coresight-nr-inports = <0>;
+ };
+
+ cti_cpu0: cti@fc341000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc341000 0x1000>;
+ reg-names = "cti-cpu0-base";
+
+ coresight-id = <25>;
+ coresight-name = "coresight-cti-cpu0";
+ coresight-nr-inports = <0>;
+ };
+
+ cti_cpu1: cti@fc342000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc342000 0x1000>;
+ reg-names = "cti-cpu1-base";
+
+ coresight-id = <26>;
+ coresight-name = "coresight-cti-cpu1";
+ coresight-nr-inports = <0>;
+ };
+
+ cti_cpu2: cti@fc343000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc343000 0x1000>;
+ reg-names = "cti-cpu2-base";
+
+ coresight-id = <27>;
+ coresight-name = "coresight-cti-cpu2";
+ coresight-nr-inports = <0>;
+ };
+
+ cti_cpu3: cti@fc344000 {
+ compatible = "arm,coresight-cti";
+ reg = <0xfc344000 0x1000>;
+ reg-names = "cti-cpu3-base";
+
+ coresight-id = <28>;
+ coresight-name = "coresight-cti-cpu3";
+ coresight-nr-inports = <0>;
+ };
};
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-ion.dtsi b/arch/arm/boot/dts/msm8226-ion.dtsi
index b06ad42..9a35507 100644
--- a/arch/arm/boot/dts/msm8226-ion.dtsi
+++ b/arch/arm/boot/dts/msm8226-ion.dtsi
@@ -24,8 +24,7 @@
compatible = "qcom,msm-ion-reserve";
reg = <8>;
qcom,heap-align = <0x1000>;
- qcom,memory-reservation-type = "EBI1"; /* reserve EBI memory */
- qcom,memory-reservation-size = <0x3800000>;
+ linux,contiguous-region = <&secure_mem>;
};
qcom,ion-heap@25 { /* IOMMU HEAP */
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..0bc8efd 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";
@@ -50,12 +50,57 @@
spi0 = &spi_0;
};
+ memory {
+ secure_mem: secure_region {
+ linux,contiguous-region;
+ reg = <0 0x3800000>;
+ label = "secure_mem";
+ };
+ };
+
timer {
compatible = "arm,armv7-timer";
interrupts = <1 2 0 1 3 0>;
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 +467,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 +509,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 +695,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/boot/dts/msmzinc.dtsi b/arch/arm/boot/dts/msmzinc.dtsi
index 8905962..d981909 100644
--- a/arch/arm/boot/dts/msmzinc.dtsi
+++ b/arch/arm/boot/dts/msmzinc.dtsi
@@ -71,6 +71,11 @@
rpm-standalone;
};
+ qcom,msm-imem@fe805000 {
+ compatible = "qcom,msm-imem";
+ reg = <0xfe805000 0x1000>; /* Address and size of IMEM */
+ };
+
qcom,ion {
compatible = "qcom,msm-ion";
#address-cells = <1>;
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/configs/msm9625-perf_defconfig b/arch/arm/configs/msm9625-perf_defconfig
index 6e8fe3e..c2fb1487 100644
--- a/arch/arm/configs/msm9625-perf_defconfig
+++ b/arch/arm/configs/msm9625-perf_defconfig
@@ -161,6 +161,8 @@
CONFIG_MTD_BLOCK=y
# CONFIG_MTD_MSM_NAND is not set
CONFIG_MTD_MSM_QPIC_NAND=y
+CONFIG_SYNC=y
+CONFIG_SW_SYNC=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
# CONFIG_ANDROID_PMEM is not set
@@ -218,6 +220,10 @@
CONFIG_REGULATOR_QPNP=y
CONFIG_ION=y
CONFIG_ION_MSM=y
+CONFIG_FB=y
+CONFIG_FB_MSM=y
+CONFIG_FB_MSM_QPIC=y
+CONFIG_FB_MSM_QPIC_PANEL_DETECT=y
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_SOC=y
diff --git a/arch/arm/configs/msm9625_defconfig b/arch/arm/configs/msm9625_defconfig
index 9a32239..828eaa9 100644
--- a/arch/arm/configs/msm9625_defconfig
+++ b/arch/arm/configs/msm9625_defconfig
@@ -161,6 +161,8 @@
CONFIG_MTD_BLOCK=y
# CONFIG_MTD_MSM_NAND is not set
CONFIG_MTD_MSM_QPIC_NAND=y
+CONFIG_SYNC=y
+CONFIG_SW_SYNC=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
# CONFIG_ANDROID_PMEM is not set
@@ -219,6 +221,10 @@
CONFIG_REGULATOR_QPNP=y
CONFIG_ION=y
CONFIG_ION_MSM=y
+CONFIG_FB=y
+CONFIG_FB_MSM=y
+CONFIG_FB_MSM_QPIC=y
+CONFIG_FB_MSM_QPIC_PANEL_DETECT=y
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_SOC=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-8226.c b/arch/arm/mach-msm/clock-8226.c
index 2899d93..c823c39 100644
--- a/arch/arm/mach-msm/clock-8226.c
+++ b/arch/arm/mach-msm/clock-8226.c
@@ -3098,6 +3098,20 @@
CLK_LOOKUP("core_clk", qdss_clk.c, "fc33d000.jtagmm"),
CLK_LOOKUP("core_clk", qdss_clk.c, "fc33e000.jtagmm"),
CLK_LOOKUP("core_clk", qdss_clk.c, "fc33f000.jtagmm"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc308000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc309000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc30a000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc30b000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc30c000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc30d000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc30e000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc30f000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc310000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc340000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc341000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc342000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc343000.cti"),
+ CLK_LOOKUP("core_clk", qdss_clk.c, "fc344000.cti"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc322000.tmc"),
CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc318000.tpiu"),
@@ -3117,6 +3131,20 @@
CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc33d000.jtagmm"),
CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc33e000.jtagmm"),
CLK_LOOKUP("core_a_clk", qdss_clk.c, "fc33f000.jtagmm"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc308000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc309000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc30a000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc30b000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc30c000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc30d000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc30e000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc30f000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc310000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc340000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc341000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc342000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc343000.cti"),
+ CLK_LOOKUP("core_a_clk", qdss_a_clk.c, "fc344000.cti"),
/* HSUSB-OTG Clocks */
CLK_LOOKUP("xo", xo.c, "f9a55000.usb"),
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/msm_iomap-zinc.h b/arch/arm/mach-msm/include/mach/msm_iomap-zinc.h
index 8456445..8283622 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-zinc.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-zinc.h
@@ -34,9 +34,6 @@
#define MSMZINC_TLMM_PHYS 0xFD510000
#define MSMZINC_TLMM_SIZE SZ_16K
-#define MSMZINC_IMEM_PHYS 0xFC42B000
-#define MSMZINC_IMEM_SIZE SZ_4K
-
#ifdef CONFIG_DEBUG_MSMZINC_UART
#define MSM_DEBUG_UART_BASE IOMEM(0xFA71E000)
#define MSM_DEBUG_UART_PHYS 0xF991E000
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/io.c b/arch/arm/mach-msm/io.c
index c7b47a5..c71a79a 100644
--- a/arch/arm/mach-msm/io.c
+++ b/arch/arm/mach-msm/io.c
@@ -328,7 +328,6 @@
MSM_CHIP_DEVICE(QGIC_DIST, MSMZINC),
MSM_CHIP_DEVICE(QGIC_CPU, MSMZINC),
MSM_CHIP_DEVICE(TLMM, MSMZINC),
- MSM_CHIP_DEVICE(IMEM, MSMZINC),
{
.virtual = (unsigned long) MSM_SHARED_RAM_BASE,
.length = MSM_SHARED_RAM_SIZE,
@@ -343,6 +342,7 @@
{
msm_shared_ram_phys = MSMZINC_SHARED_RAM_PHYS;
msm_map_io(msm_zinc_io_desc, ARRAY_SIZE(msm_zinc_io_desc));
+ of_scan_flat_dt(msm_scan_dt_map_imem, NULL);
}
#endif /* CONFIG_ARCH_MSMZINC */
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/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/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c
index dffae70..02ada38 100644
--- a/drivers/gpu/msm/kgsl_pwrscale.c
+++ b/drivers/gpu/msm/kgsl_pwrscale.c
@@ -237,16 +237,14 @@
void kgsl_pwrscale_busy(struct kgsl_device *device)
{
if (PWRSCALE_ACTIVE(device) && device->pwrscale.policy->busy)
- if (device->requested_state != KGSL_STATE_SLUMBER)
- device->pwrscale.policy->busy(device,
- &device->pwrscale);
+ device->pwrscale.policy->busy(device,
+ &device->pwrscale);
}
void kgsl_pwrscale_idle(struct kgsl_device *device)
{
if (PWRSCALE_ACTIVE(device) && device->pwrscale.policy->idle)
- if (device->requested_state != KGSL_STATE_SLUMBER &&
- device->requested_state != KGSL_STATE_SLEEP)
+ if (device->state == KGSL_STATE_ACTIVE)
device->pwrscale.policy->idle(device,
&device->pwrscale);
}
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
index b136125..a786750 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c
@@ -120,7 +120,6 @@
{"camss_csi_vfe_clk", -1},
{"iface_clk", -1},
{"bus_clk", -1},
- {"alt_bus_clk", -1},
};
static void msm_vfe40_init_qos_parms(struct vfe_device *vfe_dev)
diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
index 4f8c257..663cc40 100644
--- a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
@@ -1006,6 +1006,13 @@
goto err_mem_alloc;
}
for (i = 0; i < num_bus_pdata; i++) {
+ if (!res->has_ocmem &&
+ (!strcmp(bus_pdata_config_vector[i].name,
+ "qcom,enc-ocmem-ab-ib")
+ || !strcmp(bus_pdata_config_vector[i].name,
+ "qcom,dec-ocmem-ab-ib"))) {
+ continue;
+ }
res->bus_pdata[i].num_usecases = get_u32_array_num_elements(
pdev, bus_pdata_config_vector[i].name);
if (res->bus_pdata[i].num_usecases == 0) {
@@ -1201,6 +1208,9 @@
kres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
res->irq = kres ? kres->start : -1;
+ res->has_ocmem = of_property_read_bool(pdev->dev.of_node,
+ "qcom,has-ocmem");
+
rc = msm_vidc_load_freq_table(res);
if (rc) {
dprintk(VIDC_ERR, "Failed to load freq table: %d\n", rc);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index b71a816..ba3e393 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -949,7 +949,7 @@
dprintk(VIDC_WARN,
"Failed to scale DDR bus. Performance might be impacted\n");
}
- if (call_hfi_op(hdev, is_ocmem_present, hdev->hfi_device_data)) {
+ if (core->resources.has_ocmem) {
if (msm_comm_scale_bus(core, inst->session_type,
OCMEM_MEM))
dprintk(VIDC_WARN,
@@ -1112,9 +1112,11 @@
}
msm_comm_scale_clocks_and_bus(inst);
if (list_empty(&core->instances)) {
- if (inst->state != MSM_VIDC_CORE_INVALID)
- msm_comm_unset_ocmem(core);
- call_hfi_op(hdev, free_ocmem, hdev->hfi_device_data);
+ if (core->resources.has_ocmem) {
+ if (inst->state != MSM_VIDC_CORE_INVALID)
+ msm_comm_unset_ocmem(core);
+ call_hfi_op(hdev, free_ocmem, hdev->hfi_device_data);
+ }
dprintk(VIDC_DBG, "Calling vidc_hal_core_release\n");
rc = call_hfi_op(hdev, core_release, hdev->hfi_device_data);
if (rc) {
@@ -1126,7 +1128,10 @@
core->state = VIDC_CORE_UNINIT;
mutex_unlock(&core->lock);
call_hfi_op(hdev, unload_fw, hdev->hfi_device_data);
- msm_comm_unvote_buses(core, DDR_MEM|OCMEM_MEM);
+ if (core->resources.has_ocmem)
+ msm_comm_unvote_buses(core, DDR_MEM|OCMEM_MEM);
+ else
+ msm_comm_unvote_buses(core, DDR_MEM);
}
core_already_uninited:
change_inst_state(inst, MSM_VIDC_CORE_UNINIT);
@@ -1273,21 +1278,26 @@
inst, inst->state);
goto exit;
}
- ocmem_sz = get_ocmem_requirement(inst->prop.height, inst->prop.width);
- rc = msm_comm_scale_bus(inst->core, inst->session_type, OCMEM_MEM);
- if (!rc) {
- mutex_lock(&inst->core->sync_lock);
- rc = call_hfi_op(hdev, alloc_ocmem, hdev->hfi_device_data,
- ocmem_sz);
- mutex_unlock(&inst->core->sync_lock);
- if (rc) {
+ if (inst->core->resources.has_ocmem) {
+ ocmem_sz = get_ocmem_requirement(inst->prop.height,
+ inst->prop.width);
+ rc = msm_comm_scale_bus(inst->core, inst->session_type,
+ OCMEM_MEM);
+ if (!rc) {
+ mutex_lock(&inst->core->sync_lock);
+ rc = call_hfi_op(hdev, alloc_ocmem,
+ hdev->hfi_device_data,
+ ocmem_sz);
+ mutex_unlock(&inst->core->sync_lock);
+ if (rc) {
+ dprintk(VIDC_WARN,
+ "Failed to allocate OCMEM. Performance will be impacted\n");
+ msm_comm_unvote_buses(inst->core, OCMEM_MEM);
+ }
+ } else {
dprintk(VIDC_WARN,
- "Failed to allocate OCMEM. Performance will be impacted\n");
- msm_comm_unvote_buses(inst->core, OCMEM_MEM);
+ "Failed to vote for OCMEM BW. Performance will be impacted\n");
}
- } else {
- dprintk(VIDC_WARN,
- "Failed to vote for OCMEM BW. Performance will be impacted\n");
}
rc = call_hfi_op(hdev, session_load_res, (void *) inst->session);
if (rc) {
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_resources.h b/drivers/media/platform/msm/vidc/msm_vidc_resources.h
index 54c0878..86b824b 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_resources.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_resources.h
@@ -73,6 +73,7 @@
struct msm_bus_scale_pdata *bus_pdata;
struct iommu_set iommu_group_set;
struct buffer_usage_set buffer_usage_set;
+ bool has_ocmem;
struct platform_device *pdev;
};
diff --git a/drivers/media/platform/msm/vidc/q6_hfi.c b/drivers/media/platform/msm/vidc/q6_hfi.c
index d1948d1..f0ca050 100644
--- a/drivers/media/platform/msm/vidc/q6_hfi.c
+++ b/drivers/media/platform/msm/vidc/q6_hfi.c
@@ -1105,14 +1105,6 @@
return 0;
}
-static int q6_hfi_is_ocmem_present(void *dev)
-{
- (void)dev;
-
- /* Q6 does not support ocmem */
- return 0;
-}
-
static int q6_hfi_iommu_get_domain_partition(void *dev, u32 flags,
u32 buffer_type, int *domain, int *partition)
{
@@ -1225,7 +1217,6 @@
hdev->unset_ocmem = q6_hfi_unset_ocmem;
hdev->alloc_ocmem = q6_hfi_alloc_ocmem;
hdev->free_ocmem = q6_hfi_free_ocmem;
- hdev->is_ocmem_present = q6_hfi_is_ocmem_present;
hdev->iommu_get_domain_partition = q6_hfi_iommu_get_domain_partition;
hdev->load_fw = q6_hfi_load_fw;
hdev->unload_fw = q6_hfi_unload_fw;
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index 424af64..232ad90 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -1941,8 +1941,11 @@
sizeof(clock[VCODEC_AHB_CLK].name));
strlcpy(clock[VCODEC_AXI_CLK].name, "bus_clk",
sizeof(clock[VCODEC_AXI_CLK].name));
- strlcpy(clock[VCODEC_OCMEM_CLK].name, "mem_clk",
- sizeof(clock[VCODEC_OCMEM_CLK].name));
+
+ if (res->has_ocmem) {
+ strlcpy(clock[VCODEC_OCMEM_CLK].name, "mem_clk",
+ sizeof(clock[VCODEC_OCMEM_CLK].name));
+ }
clock[VCODEC_CLK].count = res->load_freq_tbl_size;
memcpy((void *)clock[VCODEC_CLK].load_freq_tbl, res->load_freq_tbl,
@@ -1962,6 +1965,8 @@
}
for (i = 0; i < VCODEC_MAX_CLKS; i++) {
+ if (i == VCODEC_OCMEM_CLK && !res->has_ocmem)
+ continue;
cl = &device->resources.clock[i];
if (!cl->clk) {
cl->clk = devm_clk_get(&res->pdev->dev, cl->name);
@@ -1976,6 +1981,8 @@
if (i < VCODEC_MAX_CLKS) {
for (--i; i >= 0; i--) {
+ if (i == VCODEC_OCMEM_CLK && !res->has_ocmem)
+ continue;
cl = &device->resources.clock[i];
clk_put(cl->clk);
}
@@ -1991,8 +1998,12 @@
dprintk(VIDC_ERR, "Invalid args\n");
return;
}
- for (i = 0; i < VCODEC_MAX_CLKS; i++)
+
+ for (i = 0; i < VCODEC_MAX_CLKS; i++) {
+ if (i == VCODEC_OCMEM_CLK && !device->res->has_ocmem)
+ continue;
clk_put(device->resources.clock[i].clk);
+ }
}
static inline void venus_hfi_disable_clks(struct venus_hfi_device *device)
{
@@ -2003,7 +2014,9 @@
return;
}
- for (i = 0; i < VCODEC_MAX_CLKS; i++) {
+ for (i = VCODEC_CLK; i < VCODEC_MAX_CLKS; i++) {
+ if (i == VCODEC_OCMEM_CLK && !device->res->has_ocmem)
+ continue;
cl = &device->resources.clock[i];
clk_disable_unprepare(cl->clk);
}
@@ -2018,7 +2031,10 @@
dprintk(VIDC_ERR, "Invalid params: %p\n", device);
return -EINVAL;
}
- for (i = 0; i < VCODEC_MAX_CLKS; i++) {
+
+ for (i = VCODEC_CLK; i < VCODEC_MAX_CLKS; i++) {
+ if (i == VCODEC_OCMEM_CLK && !device->res->has_ocmem)
+ continue;
cl = &device->resources.clock[i];
rc = clk_prepare_enable(cl->clk);
if (rc) {
@@ -2151,18 +2167,27 @@
dprintk(VIDC_ERR, "Failed to register bus scale client\n");
goto err_init_bus;
}
- bus_info->ocmem_handle[MSM_VIDC_ENCODER] =
- msm_bus_scale_register_client(&device->res->bus_pdata[0]);
- if (!bus_info->ocmem_handle[MSM_VIDC_ENCODER]) {
- dprintk(VIDC_ERR, "Failed to register bus scale client\n");
- goto err_init_bus;
+
+ if (device->res->has_ocmem) {
+ bus_info->ocmem_handle[MSM_VIDC_ENCODER] =
+ msm_bus_scale_register_client(
+ &device->res->bus_pdata[0]);
+ if (!bus_info->ocmem_handle[MSM_VIDC_ENCODER]) {
+ dprintk(VIDC_ERR,
+ "Failed to register bus scale client\n");
+ goto err_init_bus;
+ }
+
+ bus_info->ocmem_handle[MSM_VIDC_DECODER] =
+ msm_bus_scale_register_client(
+ &device->res->bus_pdata[1]);
+ if (!bus_info->ocmem_handle[MSM_VIDC_DECODER]) {
+ dprintk(VIDC_ERR,
+ "Failed to register bus scale client\n");
+ goto err_init_bus;
+ }
}
- bus_info->ocmem_handle[MSM_VIDC_DECODER] =
- msm_bus_scale_register_client(&device->res->bus_pdata[1]);
- if (!bus_info->ocmem_handle[MSM_VIDC_DECODER]) {
- dprintk(VIDC_ERR, "Failed to register bus scale client\n");
- goto err_init_bus;
- }
+
return rc;
err_init_bus:
venus_hfi_deinit_bus(device);
@@ -2406,18 +2431,6 @@
return rc;
}
-static int venus_hfi_is_ocmem_present(void *dev)
-{
- struct venus_hfi_device *device = dev;
- if (!device) {
- dprintk(VIDC_ERR, "%s invalid device handle %p",
- __func__, device);
- return -EINVAL;
- }
-
- return device->resources.ocmem.buf ? 1 : 0;
-}
-
static void venus_hfi_deinit_ocmem(struct venus_hfi_device *device)
{
if (device->resources.ocmem.handle)
@@ -2451,7 +2464,9 @@
goto err_register_iommu_domain;
}
- venus_hfi_ocmem_init(device);
+ if (res->has_ocmem)
+ venus_hfi_ocmem_init(device);
+
return rc;
err_register_iommu_domain:
@@ -2464,7 +2479,8 @@
static void venus_hfi_deinit_resources(struct venus_hfi_device *device)
{
- venus_hfi_deinit_ocmem(device);
+ if (device->res->has_ocmem)
+ venus_hfi_deinit_ocmem(device);
venus_hfi_deregister_iommu_domains(device);
venus_hfi_deinit_bus(device);
venus_hfi_deinit_clocks(device);
@@ -2658,8 +2674,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;
}
}
@@ -2839,7 +2855,6 @@
hdev->unset_ocmem = venus_hfi_unset_ocmem;
hdev->alloc_ocmem = venus_hfi_alloc_ocmem;
hdev->free_ocmem = venus_hfi_free_ocmem;
- hdev->is_ocmem_present = venus_hfi_is_ocmem_present;
hdev->iommu_get_domain_partition = venus_hfi_iommu_get_domain_partition;
hdev->load_fw = venus_hfi_load_fw;
hdev->unload_fw = venus_hfi_unload_fw;
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index 0c88866..5ad0bb5 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -1047,7 +1047,6 @@
int (*unset_ocmem)(void *dev);
int (*alloc_ocmem)(void *dev, unsigned long size);
int (*free_ocmem)(void *dev);
- int (*is_ocmem_present)(void *dev);
int (*iommu_get_domain_partition)(void *dev, u32 flags, u32 buffer_type,
int *domain_num, int *partition_num);
int (*load_fw)(void *dev);
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/Kconfig b/drivers/video/msm/Kconfig
index 1c59a68..88f8be5 100644
--- a/drivers/video/msm/Kconfig
+++ b/drivers/video/msm/Kconfig
@@ -41,6 +41,9 @@
bool
default n
+config FB_MSM_MDSS_COMMON
+ bool
+
choice
prompt "MDP HW version"
default FB_MSM_MDP22
@@ -88,6 +91,7 @@
config FB_MSM_MDSS
bool "MDSS HW"
+ select FB_MSM_MDSS_COMMON
---help---
The Mobile Display Sub System (MDSS) driver supports devices which
contain MDSS hardware block.
@@ -103,6 +107,10 @@
endchoice
+config FB_MSM_QPIC
+ bool
+ select FB_MSM_MDSS_COMMON
+
config FB_MSM_EBI2
bool
default n
@@ -950,6 +958,21 @@
---help---
Support for EBI2 panel auto detect
+config FB_MSM_QPIC_ILI_QVGA_PANEL
+ bool "Qpic MIPI ILI QVGA Panel"
+ select FB_MSM_QPIC
+ ---help---
+ Support for MIPI ILI QVGA (240x320) panel
+ ILI TECHNOLOGY 9341
+ with on-chip full display RAM
+ use parallel interface
+
+config FB_MSM_QPIC_PANEL_DETECT
+ bool "Qpic Panel Detect"
+ select FB_MSM_QPIC_ILI_QVGA_PANEL
+ ---help---
+ Support for Qpic panel auto detect
+
if FB_MSM_MDSS
source "drivers/video/msm/mdss/Kconfig"
endif
diff --git a/drivers/video/msm/Makefile b/drivers/video/msm/Makefile
index e49e2ba..67c6b48 100644
--- a/drivers/video/msm/Makefile
+++ b/drivers/video/msm/Makefile
@@ -1,4 +1,4 @@
-ifeq ($(CONFIG_FB_MSM_MDSS),y)
+ifeq ($(CONFIG_FB_MSM_MDSS_COMMON),y)
obj-y += mdss/
else
obj-y := msm_fb.o
diff --git a/drivers/video/msm/mdss/Makefile b/drivers/video/msm/mdss/Makefile
index 17987d4..a710fef 100644
--- a/drivers/video/msm/mdss/Makefile
+++ b/drivers/video/msm/mdss/Makefile
@@ -7,7 +7,10 @@
mdss-mdp-objs += mdss_mdp_wb.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss-mdp.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss_fb.o
+
+ifeq ($(CONFIG_FB_MSM_MDSS),y)
obj-$(CONFIG_DEBUG_FS) += mdss_debug.o
+endif
mdss-dsi-objs := mdss_dsi.o mdss_dsi_host.o
mdss-dsi-objs += mdss_dsi_panel.o
@@ -23,3 +26,7 @@
obj-$(CONFIG_FB_MSM_MDSS_HDMI_PANEL) += mdss_hdmi_hdcp.o
obj-$(CONFIG_FB_MSM_MDSS_WRITEBACK) += mdss_wb.o
+
+mdss-qpic-objs := mdss_qpic.o mdss_fb.o mdss_qpic_panel.o
+obj-$(CONFIG_FB_MSM_QPIC) += mdss-qpic.o
+obj-$(CONFIG_FB_MSM_QPIC_ILI_QVGA_PANEL) += qpic_panel_ili_qvga.o
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c
index 3d632c7..94261cc 100644
--- a/drivers/video/msm/mdss/mdss_fb.c
+++ b/drivers/video/msm/mdss/mdss_fb.c
@@ -730,19 +730,6 @@
.fb_mmap = mdss_fb_mmap,
};
-static u32 mdss_fb_line_length(u32 fb_index, u32 xres, int bpp)
-{
- /* The adreno GPU hardware requires that the pitch be aligned to
- 32 pixels for color buffers, so for the cases where the GPU
- is writing directly to fb0, the framebuffer pitch
- also needs to be 32 pixel aligned */
-
- if (fb_index == 0)
- return ALIGN(xres, 32) * bpp;
- else
- return xres * bpp;
-}
-
static int mdss_fb_alloc_fbmem(struct msm_fb_data_type *mfd)
{
void *virt = NULL;
@@ -753,16 +740,8 @@
size = PAGE_ALIGN(mfd->fbi->fix.line_length * yres);
if (mfd->index == 0) {
- int dom;
- virt = allocate_contiguous_memory(size, MEMTYPE_EBI1, SZ_1M, 0);
- if (!virt) {
- pr_err("unable to alloc fbmem size=%u\n", size);
- return -ENOMEM;
- }
- phys = memory_pool_node_paddr(virt);
- dom = mdss_get_iommu_domain(MDSS_IOMMU_DOMAIN_UNSECURE);
- msm_iommu_map_contig_buffer(phys, dom, 0, size, SZ_4K,
- 0, &(mfd->iova));
+ if (mdss_mdp_alloc_fb_mem(mfd, size, (u32 *)&phys, &virt))
+ return -ENOMEM;
pr_info("allocating %u bytes at %p (%lx phys) for fb %d\n",
size, virt, phys, mfd->index);
} else {
@@ -921,7 +900,7 @@
var->xres *= 2;
fix->type = panel_info->is_3d_panel;
- fix->line_length = mdss_fb_line_length(mfd->index, var->xres, bpp);
+ fix->line_length = mdss_mdp_fb_stride(mfd->index, var->xres, bpp);
var->yres = panel_info->yres;
var->xres_virtual = var->xres;
@@ -1425,7 +1404,7 @@
return -EINVAL;
}
- mfd->fbi->fix.line_length = mdss_fb_line_length(mfd->index, var->xres,
+ mfd->fbi->fix.line_length = mdss_mdp_fb_stride(mfd->index, var->xres,
var->bits_per_pixel / 8);
if (mfd->panel_reconfig || (mfd->fb_imgType != old_imgType)) {
diff --git a/drivers/video/msm/mdss/mdss_mdp.c b/drivers/video/msm/mdss/mdss_mdp.c
index 977fc63..f9b05fc 100644
--- a/drivers/video/msm/mdss/mdss_mdp.c
+++ b/drivers/video/msm/mdss/mdss_mdp.c
@@ -46,6 +46,8 @@
#include <mach/msm_bus_board.h>
#include <mach/iommu.h>
#include <mach/iommu_domains.h>
+#include <mach/memory.h>
+#include <mach/msm_memtypes.h>
#include "mdss.h"
#include "mdss_fb.h"
@@ -126,6 +128,39 @@
char *prop_name);
static int mdss_mdp_parse_dt_smp(struct platform_device *pdev);
+int mdss_mdp_alloc_fb_mem(struct msm_fb_data_type *mfd,
+ u32 size, u32 *phys, void **virt)
+{
+ int dom;
+ void *fb_virt;
+ u32 fb_phys;
+ fb_virt = allocate_contiguous_memory(size, MEMTYPE_EBI1, SZ_1M, 0);
+ if (!fb_virt) {
+ pr_err("unable to alloc fbmem size=%u\n", size);
+ return -ENOMEM;
+ }
+ fb_phys = memory_pool_node_paddr(fb_virt);
+ dom = mdss_get_iommu_domain(MDSS_IOMMU_DOMAIN_UNSECURE);
+ msm_iommu_map_contig_buffer(fb_phys, dom, 0, size, SZ_4K,
+ 0, &(mfd->iova));
+ *phys = fb_phys;
+ *virt = fb_virt;
+ return 0;
+}
+
+u32 mdss_mdp_fb_stride(u32 fb_index, u32 xres, int bpp)
+{
+ /* The adreno GPU hardware requires that the pitch be aligned to
+ 32 pixels for color buffers, so for the cases where the GPU
+ is writing directly to fb0, the framebuffer pitch
+ also needs to be 32 pixel aligned */
+
+ if (fb_index == 0)
+ return ALIGN(xres, 32) * bpp;
+ else
+ return xres * bpp;
+}
+
static inline int mdss_irq_dispatch(u32 hw_ndx, int irq, void *ptr)
{
struct mdss_hw *hw;
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index efd93c0..43456ca 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -420,4 +420,7 @@
int mdss_mdp_wb_ioctl_handler(struct msm_fb_data_type *mfd, u32 cmd, void *arg);
int mdss_mdp_get_ctl_mixers(u32 fb_num, u32 *mixer_id);
+int mdss_mdp_alloc_fb_mem(struct msm_fb_data_type *mfd,
+ u32 size, u32 *phys, void **virt);
+u32 mdss_mdp_fb_stride(u32 fb_index, u32 xres, int bpp);
#endif /* MDSS_MDP_H */
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/drivers/video/msm/mdss/mdss_qpic.c b/drivers/video/msm/mdss/mdss_qpic.c
new file mode 100644
index 0000000..647504f
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_qpic.c
@@ -0,0 +1,708 @@
+/* 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/hrtimer.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/regulator/consumer.h>
+#include <linux/semaphore.h>
+#include <linux/uaccess.h>
+#include <linux/bootmem.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <mach/sps.h>
+#include <mach/clk.h>
+#include <mach/hardware.h>
+
+#include "mdss_fb.h"
+#include "mdss_qpic.h"
+
+static int mdss_qpic_probe(struct platform_device *pdev);
+static int mdss_qpic_remove(struct platform_device *pdev);
+
+struct qpic_data_type *qpic_res;
+
+/* for tuning */
+static u32 use_bam = true;
+static u32 use_irq;
+static u32 use_vsync;
+
+static const struct of_device_id mdss_qpic_dt_match[] = {
+ { .compatible = "qcom,mdss_qpic",},
+ {}
+};
+MODULE_DEVICE_TABLE(of, mdss_qpic_dt_match);
+
+static struct platform_driver mdss_qpic_driver = {
+ .probe = mdss_qpic_probe,
+ .remove = mdss_qpic_remove,
+ .shutdown = NULL,
+ .driver = {
+ /*
+ * Simulate mdp hw
+ */
+ .name = "mdp",
+ .of_match_table = mdss_qpic_dt_match,
+ },
+};
+
+int qpic_on(struct msm_fb_data_type *mfd)
+{
+ int ret;
+ ret = mdss_qpic_panel_on(qpic_res->panel_data);
+ return ret;
+}
+
+int qpic_off(struct msm_fb_data_type *mfd)
+{
+ int ret;
+ ret = mdss_qpic_panel_off(qpic_res->panel_data);
+ return ret;
+}
+
+static void mdss_qpic_pan_display(struct msm_fb_data_type *mfd)
+{
+
+ struct fb_info *fbi;
+ u32 offset, fb_offset, size;
+ int bpp;
+
+ if (!mfd) {
+ pr_err("%s: mfd is NULL!", __func__);
+ return;
+ }
+
+ fbi = mfd->fbi;
+
+ bpp = fbi->var.bits_per_pixel / 8;
+ offset = fbi->var.xoffset * bpp +
+ fbi->var.yoffset * fbi->fix.line_length;
+
+ if (offset > fbi->fix.smem_len) {
+ pr_err("invalid fb offset=%u total length=%u\n",
+ offset, fbi->fix.smem_len);
+ return;
+ }
+ fb_offset = (u32)fbi->fix.smem_start + offset;
+
+ mdss_qpic_panel_on(qpic_res->panel_data);
+ size = fbi->var.xres * fbi->var.yres * bpp;
+
+ qpic_send_frame(0, 0, fbi->var.xres, fbi->var.yres,
+ (u32 *)fb_offset, size);
+}
+
+int mdss_mdp_alloc_fb_mem(struct msm_fb_data_type *mfd,
+ u32 size, u32 *phys, void **virt)
+{
+ if (!qpic_res->res_init)
+ return -EINVAL;
+ if (!qpic_res->fb_virt) {
+ qpic_res->fb_virt = (void *)dmam_alloc_coherent(
+ &qpic_res->pdev->dev,
+ size + QPIC_MAX_CMD_BUF_SIZE,
+ &qpic_res->fb_phys,
+ GFP_KERNEL);
+ pr_err("%s size=%d vir_addr=%x phys_addr=%x",
+ __func__, size, (int)qpic_res->fb_virt,
+ (int)qpic_res->fb_phys);
+ if (!qpic_res->fb_virt)
+ return -ENOMEM;
+ qpic_res->cmd_buf_virt = qpic_res->fb_virt + size;
+ qpic_res->cmd_buf_phys = qpic_res->fb_phys + size;
+ }
+ *phys = qpic_res->fb_phys;
+ *virt = qpic_res->fb_virt;
+ return 0;
+}
+
+u32 mdss_mdp_fb_stride(u32 fb_index, u32 xres, int bpp)
+{
+ return xres * bpp;
+}
+
+int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd)
+{
+ mfd->on_fnc = qpic_on;
+ mfd->off_fnc = qpic_off;
+ mfd->dma_fnc = mdss_qpic_pan_display;
+ return 0;
+}
+
+u32 mdss_get_panel_framerate(struct msm_fb_data_type *mfd)
+{
+ return qpic_panel_get_framerate();
+}
+
+int qpic_register_panel(struct mdss_panel_data *pdata)
+{
+ struct platform_device *mdss_fb_dev = NULL;
+ int rc;
+
+ mdss_fb_dev = platform_device_alloc("mdss_fb", pdata->panel_info.pdest);
+ if (!mdss_fb_dev) {
+ pr_err("unable to allocate mdss_fb device\n");
+ return -ENOMEM;
+ }
+
+ mdss_fb_dev->dev.platform_data = pdata;
+
+ rc = platform_device_add(mdss_fb_dev);
+ if (rc) {
+ platform_device_put(mdss_fb_dev);
+ pr_err("unable to probe mdss_fb device (%d)\n", rc);
+ return rc;
+ }
+
+ qpic_res->panel_data = pdata;
+
+ return rc;
+}
+
+int qpic_init_sps(struct platform_device *pdev,
+ struct qpic_sps_endpt *end_point)
+{
+ int rc = 0;
+ struct sps_pipe *pipe_handle;
+ struct sps_connect *sps_config = &end_point->config;
+ struct sps_register_event *sps_event = &end_point->bam_event;
+ struct sps_bam_props bam = {0};
+ u32 bam_handle = 0;
+
+ if (qpic_res->sps_init)
+ return 0;
+ bam.phys_addr = qpic_res->qpic_phys + 0x4000;
+ bam.virt_addr = qpic_res->qpic_base + 0x4000;
+ bam.irq = qpic_res->irq - 4;
+ bam.manage = SPS_BAM_MGR_DEVICE_REMOTE | SPS_BAM_MGR_MULTI_EE;
+
+ rc = sps_phy2h(bam.phys_addr, &bam_handle);
+ if (rc)
+ rc = sps_register_bam_device(&bam, &bam_handle);
+ if (rc) {
+ pr_err("%s bam_handle is NULL", __func__);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ pipe_handle = sps_alloc_endpoint();
+ if (!pipe_handle) {
+ pr_err("sps_alloc_endpoint() failed\n");
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = sps_get_config(pipe_handle, sps_config);
+ if (rc) {
+ pr_err("sps_get_config() failed %d\n", rc);
+ goto free_endpoint;
+ }
+
+ /* WRITE CASE: source - system memory; destination - BAM */
+ sps_config->source = SPS_DEV_HANDLE_MEM;
+ sps_config->destination = bam_handle;
+ sps_config->mode = SPS_MODE_DEST;
+ sps_config->dest_pipe_index = 6;
+
+ sps_config->options = SPS_O_AUTO_ENABLE | SPS_O_EOT;
+ sps_config->lock_group = 0;
+ /*
+ * Descriptor FIFO is a cyclic FIFO. If 64 descriptors
+ * are allowed to be submitted before we get any ack for any of them,
+ * the descriptor FIFO size should be: (SPS_MAX_DESC_NUM + 1) *
+ * sizeof(struct sps_iovec).
+ */
+ sps_config->desc.size = (64) *
+ sizeof(struct sps_iovec);
+ sps_config->desc.base = dmam_alloc_coherent(&pdev->dev,
+ sps_config->desc.size,
+ &sps_config->desc.phys_base,
+ GFP_KERNEL);
+ if (!sps_config->desc.base) {
+ pr_err("dmam_alloc_coherent() failed for size %x\n",
+ sps_config->desc.size);
+ rc = -ENOMEM;
+ goto free_endpoint;
+ }
+ memset(sps_config->desc.base, 0x00, sps_config->desc.size);
+
+ rc = sps_connect(pipe_handle, sps_config);
+ if (rc) {
+ pr_err("sps_connect() failed %d\n", rc);
+ goto free_endpoint;
+ }
+
+ init_completion(&end_point->completion);
+ sps_event->mode = SPS_TRIGGER_WAIT;
+ sps_event->options = SPS_O_EOT;
+ sps_event->xfer_done = &end_point->completion;
+ sps_event->user = (void *)qpic_res;
+
+ rc = sps_register_event(pipe_handle, sps_event);
+ if (rc) {
+ pr_err("sps_register_event() failed %d\n", rc);
+ goto sps_disconnect;
+ }
+
+ end_point->handle = pipe_handle;
+ qpic_res->sps_init = true;
+ goto out;
+sps_disconnect:
+ sps_disconnect(pipe_handle);
+free_endpoint:
+ sps_free_endpoint(pipe_handle);
+out:
+ return rc;
+}
+
+void mdss_qpic_reset(void)
+{
+ u32 time_end;
+
+ QPIC_OUTP(QPIC_REG_QPIC_LCDC_RESET, 1 << 0);
+ /* wait 100 us after reset as suggested by hw */
+ usleep(100);
+ time_end = (u32)ktime_to_ms(ktime_get()) + QPIC_MAX_VSYNC_WAIT_TIME;
+ while (((QPIC_INP(QPIC_REG_QPIC_LCDC_STTS) & (1 << 8)) == 0)) {
+ if ((u32)ktime_to_ms(ktime_get()) > time_end) {
+ pr_err("%s reset not finished", __func__);
+ break;
+ }
+ /* yield 100 us for next polling by experiment*/
+ usleep(100);
+ }
+}
+
+void qpic_interrupt_en(u32 en)
+{
+ QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_CLR, 0xff);
+ if (en) {
+ if (!qpic_res->irq_ena) {
+ qpic_res->irq_ena = true;
+ enable_irq(qpic_res->irq);
+ QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_EN,
+ (1 << 0) | (1 << 2));
+ }
+ } else {
+ QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_EN, 0);
+ disable_irq(qpic_res->irq);
+ qpic_res->irq_ena = false;
+ }
+}
+
+static irqreturn_t qpic_irq_handler(int irq, void *ptr)
+{
+ u32 data;
+ data = QPIC_INP(QPIC_REG_QPIC_LCDC_IRQ_STTS);
+ QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_CLR, 0xff);
+ return 0;
+}
+
+int qpic_flush_buffer_bam(u32 cmd, u32 len, u32 *param, u32 is_cmd)
+{
+ int ret = 0;
+ u32 phys_addr, cfg2, block_len , flags;
+ if (is_cmd) {
+ memcpy((u8 *)qpic_res->cmd_buf_virt, param, len);
+ invalidate_caches((unsigned long)qpic_res->cmd_buf_virt,
+ len,
+ (unsigned long)qpic_res->cmd_buf_phys);
+ phys_addr = qpic_res->cmd_buf_phys;
+ } else {
+ phys_addr = (u32)param;
+ }
+
+ cfg2 = QPIC_INP(QPIC_REG_QPIC_LCDC_CFG2);
+ cfg2 &= ~0xFF;
+ cfg2 |= cmd;
+ QPIC_OUTP(QPIC_REG_QPIC_LCDC_CFG2, cfg2);
+ block_len = 0x7FF0;
+ while (len > 0) {
+ if (len <= 0x7FF0) {
+ flags = SPS_IOVEC_FLAG_EOT;
+ block_len = len;
+ } else {
+ flags = 0;
+ }
+ ret = sps_transfer_one(qpic_res->qpic_endpt.handle,
+ phys_addr, block_len, NULL, flags);
+ if (ret)
+ pr_err("failed to submit command %x ret %d\n",
+ cmd, ret);
+ phys_addr += block_len;
+ len -= block_len;
+ }
+ ret = wait_for_completion_interruptible_timeout(
+ &qpic_res->qpic_endpt.completion,
+ msecs_to_jiffies(100 * 4));
+ if (ret <= 0)
+ pr_err("%s timeout %x", __func__, ret);
+ else
+ ret = 0;
+ return ret;
+}
+
+int qpic_flush_buffer_sw(u32 cmd, u32 len, u32 *param, u32 is_cmd)
+{
+ u32 bytes_left, space, data, cfg2, time_end;
+ int i, ret = 0;
+ if ((len <= (sizeof(u32) * 4)) && (is_cmd)) {
+ len >>= 2;/* len in dwords */
+ data = 0;
+ for (i = 0; i < len; i++)
+ data |= param[i] << (8 * i);
+ QPIC_OUTP(QPIC_REG_QPIC_LCDC_CMD_DATA_CYCLE_CNT, len);
+ QPIC_OUTP(QPIC_REG_LCD_DEVICE_CMD0 + (4 * cmd), data);
+ return 0;
+ }
+ if ((len & 0x1) != 0) {
+ pr_err("%s: number of bytes needs be even", __func__);
+ len = (len + 1) & (~0x1);
+ }
+ QPIC_OUTP(QPIC_REG_QPIC_LCDC_IRQ_CLR, 0xff);
+ cfg2 = QPIC_INP(QPIC_REG_QPIC_LCDC_CFG2);
+ cfg2 |= (1 << 24); /* transparent mode */
+ cfg2 &= ~0xFF;
+ cfg2 |= cmd;
+ QPIC_OUTP(QPIC_REG_QPIC_LCDC_CFG2, cfg2);
+ QPIC_OUTP(QPIC_REG_QPIC_LCDC_FIFO_SOF, 0x0);
+ bytes_left = len;
+ while (bytes_left > 0) {
+ time_end = (u32)ktime_to_ms(ktime_get()) +
+ QPIC_MAX_VSYNC_WAIT_TIME;
+ while (1) {
+ data = QPIC_INP(QPIC_REG_QPIC_LCDC_STTS);
+ data &= 0x3F;
+ if (data == 0)
+ break;
+ /* yield 10 us for next polling by experiment*/
+ usleep(10);
+ if (ktime_to_ms(ktime_get()) > time_end) {
+ pr_err("%s time out", __func__);
+ ret = -EBUSY;
+ goto exit_send_cmd_sw;
+ }
+ }
+ space = (16 - data);
+
+ while ((space > 0) && (bytes_left > 0)) {
+ /* write to fifo */
+ if (bytes_left >= 4) {
+ QPIC_OUTP(QPIC_REG_QPIC_LCDC_FIFO_DATA_PORT0,
+ param[0]);
+ param++;
+ bytes_left -= 4;
+ space++;
+ } else if (bytes_left == 2) {
+ QPIC_OUTPW(QPIC_REG_QPIC_LCDC_FIFO_DATA_PORT0,
+ *(u16 *)param);
+ bytes_left -= 2;
+ }
+ }
+ }
+ /* finished */
+ QPIC_OUTP(QPIC_REG_QPIC_LCDC_FIFO_EOF, 0x0);
+
+ time_end = (u32)ktime_to_ms(ktime_get()) + QPIC_MAX_VSYNC_WAIT_TIME;
+ while (1) {
+ data = QPIC_INP(QPIC_REG_QPIC_LCDC_IRQ_STTS);
+ if (data & (1 << 2))
+ break;
+ /* yield 10 us for next polling by experiment*/
+ usleep(10);
+ if (ktime_to_ms(ktime_get()) > time_end) {
+ pr_err("%s wait for eof time out", __func__);
+ ret = -EBUSY;
+ goto exit_send_cmd_sw;
+ }
+ }
+exit_send_cmd_sw:
+ cfg2 &= ~(1 << 24);
+ QPIC_OUTP(QPIC_REG_QPIC_LCDC_CFG2, cfg2);
+ return ret;
+}
+
+int qpic_flush_buffer(u32 cmd, u32 len, u32 *param, u32 is_cmd)
+{
+ if (use_bam) {
+ if (is_cmd)
+ return qpic_flush_buffer_sw(cmd, len, param, is_cmd);
+ else
+ return qpic_flush_buffer_bam(cmd, len, param, is_cmd);
+ } else {
+ return qpic_flush_buffer_sw(cmd, len, param, is_cmd);
+ }
+}
+
+int mdss_qpic_init(void)
+{
+ int ret = 0;
+ u32 data;
+ mdss_qpic_reset();
+
+ pr_info("%s version=%x", __func__, QPIC_INP(QPIC_REG_LCDC_VERSION));
+ data = QPIC_INP(QPIC_REG_QPIC_LCDC_CTRL);
+ /* clear vsync wait , bam mode = 0*/
+ data &= ~(3 << 0);
+ data &= ~(0x1f << 3);
+ data |= (1 << 3); /* threshold */
+ data |= (1 << 8); /* lcd_en */
+ data &= ~(0x1f << 9);
+ data |= (1 << 9); /* threshold */
+ QPIC_OUTP(QPIC_REG_QPIC_LCDC_CTRL, data);
+
+ if (use_irq && qpic_res->irq_requested) {
+ ret = devm_request_irq(&qpic_res->pdev->dev,
+ qpic_res->irq, qpic_irq_handler,
+ IRQF_DISABLED, "QPIC", qpic_res);
+ if (ret) {
+ pr_err("qpic request_irq() failed!\n");
+ use_irq = false;
+ }
+ qpic_res->irq_requested = true;
+ }
+
+ qpic_interrupt_en(use_irq);
+ QPIC_OUTP(QPIC_REG_QPIC_LCDC_CFG0, 0x02108501);
+ data = QPIC_INP(QPIC_REG_QPIC_LCDC_CFG2);
+ data &= ~(0xFFF);
+ data |= 0x200; /* XRGB */
+ data |= 0x2C;
+ QPIC_OUTP(QPIC_REG_QPIC_LCDC_CFG2, data);
+
+ if (use_bam) {
+ qpic_init_sps(qpic_res->pdev , &qpic_res->qpic_endpt);
+ data = QPIC_INP(QPIC_REG_QPIC_LCDC_CTRL);
+ data |= (1 << 1);
+ QPIC_OUTP(QPIC_REG_QPIC_LCDC_CTRL, data);
+ }
+ /* TE enable */
+ if (use_vsync) {
+ data = QPIC_INP(QPIC_REG_QPIC_LCDC_CTRL);
+ data |= (1 << 0);
+ QPIC_OUTP(QPIC_REG_QPIC_LCDC_CTRL, data);
+ }
+
+ return ret;
+}
+
+static int mdss_qpic_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int rc = 0;
+
+ if (!pdev->dev.of_node) {
+ pr_err("qpic driver only supports device tree probe\n");
+ return -ENOTSUPP;
+ }
+
+ if (!qpic_res)
+ qpic_res = devm_kzalloc(&pdev->dev,
+ sizeof(*qpic_res), GFP_KERNEL);
+
+ if (!qpic_res)
+ return -ENOMEM;
+
+ if (qpic_res->res_init) {
+ pr_err("qpic already initialized\n");
+ return -EINVAL;
+ }
+
+ pdev->id = 0;
+
+ qpic_res->pdev = pdev;
+ platform_set_drvdata(pdev, qpic_res);
+
+ res = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, "qpic_base");
+ if (!res) {
+ pr_err("unable to get QPIC reg base address\n");
+ rc = -ENOMEM;
+ goto probe_done;
+ }
+
+ qpic_res->qpic_reg_size = resource_size(res);
+ qpic_res->qpic_base = devm_ioremap(&pdev->dev, res->start,
+ qpic_res->qpic_reg_size);
+ if (unlikely(!qpic_res->qpic_base)) {
+ pr_err("unable to map MDSS QPIC base\n");
+ rc = -ENOMEM;
+ goto probe_done;
+ }
+ qpic_res->qpic_phys = res->start;
+ pr_info("MDSS QPIC HW Base phy_Address=0x%x virt=0x%x\n",
+ (int) res->start,
+ (int) qpic_res->qpic_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ pr_err("unable to get QPIC irq\n");
+ rc = -ENOMEM;
+ goto probe_done;
+ }
+
+ qpic_res->irq = res->start;
+ qpic_res->res_init = true;
+probe_done:
+ return rc;
+}
+
+static int mdss_qpic_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static int __init mdss_qpic_driver_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&mdss_qpic_driver);
+ if (ret)
+ pr_err("mdss_qpic_register_driver() failed!\n");
+ return ret;
+}
+
+module_init(mdss_qpic_driver_init);
+
+int mdss_mdp_pa_config(struct mdss_mdp_ctl *ctl,
+ struct mdp_pa_cfg_data *config, u32 *copyback)
+{
+ return 0;
+}
+
+int mdss_mdp_pcc_config(struct mdss_mdp_ctl *ctl,
+ struct mdp_pcc_cfg_data *cfg_ptr, u32 *copyback)
+{
+ return 0;
+}
+
+int mdss_mdp_igc_lut_config(struct mdss_mdp_ctl *ctl,
+ struct mdp_igc_lut_data *config, u32 *copyback)
+{
+ return 0;
+}
+
+int mdss_mdp_argc_config(struct mdss_mdp_ctl *ctl,
+ struct mdp_pgc_lut_data *config, u32 *copyback)
+{
+ return 0;
+}
+
+int mdss_mdp_hist_lut_config(struct mdss_mdp_ctl *ctl,
+ struct mdp_hist_lut_data *config, u32 *copyback)
+{
+ return 0;
+}
+
+int mdss_mdp_dither_config(struct mdss_mdp_ctl *ctl,
+ struct mdp_dither_cfg_data *config, u32 *copyback)
+{
+ return 0;
+}
+
+int mdss_mdp_gamut_config(struct mdss_mdp_ctl *ctl,
+ struct mdp_gamut_cfg_data *config, u32 *copyback)
+{
+ return 0;
+}
+
+int mdss_mdp_histogram_start(struct mdss_mdp_ctl *ctl,
+ struct mdp_histogram_start_req *req)
+{
+ return 0;
+}
+
+int mdss_mdp_histogram_stop(struct mdss_mdp_ctl *ctl, u32 block)
+{
+ return 0;
+}
+
+int mdss_mdp_hist_collect(struct mdss_mdp_ctl *ctl,
+ struct mdp_histogram_data *hist,
+ u32 *hist_data_addr)
+{
+ return 0;
+}
+
+int mdss_mdp_overlay_kickoff(struct mdss_mdp_ctl *ctl)
+{
+ return 0;
+}
+
+void mdss_mdp_clk_ctrl(int enable, int isr)
+{
+}
+
+int mdss_mdp_copy_splash_screen(struct mdss_panel_data *pdata)
+{
+ return 0;
+}
+
+void mdss_mdp_footswitch_ctrl_splash(int on)
+{
+}
+
+int msm_fb_writeback_init(struct fb_info *info)
+{
+ return 0;
+}
+EXPORT_SYMBOL(msm_fb_writeback_init);
+
+int msm_fb_writeback_start(struct fb_info *info)
+{
+ return 0;
+}
+EXPORT_SYMBOL(msm_fb_writeback_start);
+
+int msm_fb_writeback_queue_buffer(struct fb_info *info,
+ struct msmfb_data *data)
+{
+ return 0;
+}
+EXPORT_SYMBOL(msm_fb_writeback_queue_buffer);
+
+int msm_fb_writeback_dequeue_buffer(struct fb_info *info,
+ struct msmfb_data *data)
+{
+ return 0;
+}
+EXPORT_SYMBOL(msm_fb_writeback_dequeue_buffer);
+
+int msm_fb_writeback_stop(struct fb_info *info)
+{
+ return 0;
+}
+EXPORT_SYMBOL(msm_fb_writeback_stop);
+
+int msm_fb_writeback_terminate(struct fb_info *info)
+{
+ return 0;
+}
+EXPORT_SYMBOL(msm_fb_writeback_terminate);
+
+int msm_fb_get_iommu_domain(struct fb_info *info, int domain)
+{
+ return 0;
+}
+EXPORT_SYMBOL(msm_fb_get_iommu_domain);
diff --git a/drivers/video/msm/mdss/mdss_qpic.h b/drivers/video/msm/mdss/mdss_qpic.h
new file mode 100644
index 0000000..086e8c8
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_qpic.h
@@ -0,0 +1,95 @@
+/* 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.
+ *
+ */
+
+#ifndef MDSS_QPIC_H
+#define MDSS_QPIC_H
+
+#include <linux/list.h>
+#include <mach/scm-io.h>
+#include <mach/sps.h>
+
+#include "mdss_panel.h"
+
+#define QPIC_REG_QPIC_LCDC_CTRL 0x22000
+#define QPIC_REG_LCDC_VERSION 0x22004
+#define QPIC_REG_QPIC_LCDC_IRQ_EN 0x22008
+#define QPIC_REG_QPIC_LCDC_IRQ_STTS 0x2200C
+#define QPIC_REG_QPIC_LCDC_IRQ_CLR 0x22010
+#define QPIC_REG_QPIC_LCDC_STTS 0x22014
+#define QPIC_REG_QPIC_LCDC_CMD_DATA_CYCLE_CNT 0x22018
+#define QPIC_REG_QPIC_LCDC_CFG0 0x22020
+#define QPIC_REG_QPIC_LCDC_CFG1 0x22024
+#define QPIC_REG_QPIC_LCDC_CFG2 0x22028
+#define QPIC_REG_QPIC_LCDC_RESET 0x2202C
+#define QPIC_REG_QPIC_LCDC_FIFO_SOF 0x22100
+#define QPIC_REG_LCD_DEVICE_CMD0 0x23000
+#define QPIC_REG_QPIC_LCDC_FIFO_DATA_PORT0 0x22140
+#define QPIC_REG_QPIC_LCDC_FIFO_EOF 0x22180
+
+#define QPIC_OUTP(off, data) \
+ writel_relaxed((data), qpic_res->qpic_base + (off))
+#define QPIC_OUTPW(off, data) \
+ writew_relaxed((data), qpic_res->qpic_base + (off))
+#define QPIC_INP(off) \
+ readl_relaxed(qpic_res->qpic_base + (off))
+
+#define QPIC_MAX_VSYNC_WAIT_TIME 500
+#define QPIC_MAX_CMD_BUF_SIZE 512
+
+int mdss_qpic_init(void);
+int qpic_flush_buffer(u32 cmd, u32 len, u32 *param, u32 is_cmd);
+
+u32 msm_qpic_get_bam_hdl(struct sps_bam_props *bam);
+int mdss_qpic_panel_on(struct mdss_panel_data *pdata);
+int mdss_qpic_panel_off(struct mdss_panel_data *pdata);
+int qpic_register_panel(struct mdss_panel_data *pdata);
+int ili9341_on(void);
+
+/* Structure that defines an SPS end point for a BAM pipe. */
+struct qpic_sps_endpt {
+ struct sps_pipe *handle;
+ struct sps_connect config;
+ struct sps_register_event bam_event;
+ struct completion completion;
+};
+
+struct qpic_data_type {
+ u32 rev;
+ struct platform_device *pdev;
+ size_t qpic_reg_size;
+ u32 qpic_phys;
+ char __iomem *qpic_base;
+ u32 irq;
+ u32 irq_ena;
+ u32 res_init;
+ void *fb_virt;
+ u32 fb_phys;
+ void *cmd_buf_virt;
+ u32 cmd_buf_phys;
+ struct qpic_sps_endpt qpic_endpt;
+ u32 sps_init;
+ u32 irq_requested;
+ struct mdss_panel_data *panel_data;
+};
+
+u32 qpic_send_frame(
+ u32 x_start,
+ u32 y_start,
+ u32 x_end,
+ u32 y_end,
+ u32 *data,
+ u32 total_bytes);
+
+u32 qpic_panel_get_framerate(void);
+
+#endif /* MDSS_QPIC_H */
diff --git a/drivers/video/msm/mdss/mdss_qpic_panel.c b/drivers/video/msm/mdss/mdss_qpic_panel.c
new file mode 100644
index 0000000..9825abc
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_qpic_panel.c
@@ -0,0 +1,246 @@
+/* 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.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/qpnp/pin.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/leds.h>
+#include <linux/regulator/consumer.h>
+#include <linux/dma-mapping.h>
+#include <linux/uaccess.h>
+
+#include <mach/sps.h>
+
+#include "mdss.h"
+#include "mdss_panel.h"
+#include "mdss_qpic.h"
+#include "mdss_qpic_panel.h"
+
+static u32 panel_is_on;
+static u32 panel_refresh_rate;
+static int (*qpic_panel_on)(void);
+static void (*qpic_panel_off)(void);
+static int (*qpic_panel_init)(struct platform_device *pdev,
+ struct device_node *np);
+
+u32 qpic_panel_get_framerate(void)
+{
+ return panel_refresh_rate;
+}
+
+u32 qpic_send_panel_cmd(u32 cmd, u32 *val, u32 length)
+{
+ u32 ret;
+ u32 cmd_index;
+ u32 size;
+ u32 buffer[LCDC_INTERNAL_BUFFER_SIZE];
+ u32 i;
+
+ cmd_index = LCDC_EXTRACT_OP_CMD(cmd);
+ size = LCDC_EXTRACT_OP_SIZE(cmd);
+ if (size == INV_SIZE)
+ size = length;
+ /* short or pixel data commands need not conversion */
+ if ((cmd == OP_WRITE_MEMORY_CONTINUE) ||
+ (cmd == OP_WRITE_MEMORY_START)) {
+ ret = qpic_flush_buffer(cmd_index, size, val, false);
+ } else {
+ if (size > LCDC_INTERNAL_BUFFER_SIZE)
+ size = LCDC_INTERNAL_BUFFER_SIZE;
+ /* correcting for encoding issues */
+ for (i = 0; i < size; i += sizeof(u32)) {
+ buffer[i] = (val[(i>>2)] >> 0) & 0xff;
+ buffer[i+1] = (val[(i>>2)] >> 8) & 0xff;
+ buffer[i+2] = (val[(i>>2)] >> 16) & 0xff;
+ buffer[i+3] = (val[(i>>2)] >> 24) & 0xff;
+ }
+ ret = qpic_flush_buffer(cmd_index,
+ size * sizeof(u32), buffer, true);
+ }
+ return ret;
+}
+
+u32 qpic_panel_set_cmd_only(u32 command)
+{
+ u32 param;
+ return qpic_send_panel_cmd(command, ¶m, 0);
+}
+
+/* write a frame of pixels to a MIPI screen */
+u32 qpic_send_frame(u32 x_start,
+ u32 y_start,
+ u32 x_end,
+ u32 y_end,
+ u32 *data,
+ u32 total_bytes)
+{
+ u32 param;
+ u32 status;
+ u32 start_0_7;
+ u32 end_0_7;
+ u32 start_8_15;
+ u32 end_8_15;
+
+ /* convert to 16 bit representation */
+ x_start = x_start & 0xffff;
+ y_start = y_start & 0xffff;
+ x_end = x_end & 0xffff;
+ y_end = y_end & 0xffff;
+
+ /* set column/page */
+ start_0_7 = x_start & 0xff;
+ end_0_7 = x_end & 0xff;
+ start_8_15 = (x_start >> 8) & 0xff;
+ end_8_15 = (x_end >> 8) & 0xff;
+ param = (start_8_15 << 0) | (start_0_7 << 8) |
+ (end_8_15 << 16) | (end_0_7 << 24U);
+ status = qpic_send_panel_cmd(OP_SET_COLUMN_ADDRESS, ¶m, 0);
+ if (status) {
+ pr_err("Failed to set column address");
+ return status;
+ }
+
+ start_0_7 = y_start & 0xff;
+ end_0_7 = y_end & 0xff;
+ start_8_15 = (y_start >> 8) & 0xff;
+ end_8_15 = (y_end >> 8) & 0xff;
+ param = (start_8_15 << 0) | (start_0_7 << 8) |
+ (end_8_15 << 16) | (end_0_7 << 24U);
+ status = qpic_send_panel_cmd(OP_SET_PAGE_ADDRESS, ¶m, 0);
+ if (status) {
+ pr_err("Failed to set page address");
+ return status;
+ }
+
+ status = qpic_send_panel_cmd(OP_WRITE_MEMORY_START,
+ &(data[0]), total_bytes);
+ if (status) {
+ pr_err("Failed to start memory write");
+ return status;
+ }
+ return 0;
+}
+
+int mdss_qpic_panel_on(struct mdss_panel_data *pdata)
+{
+ int rc = 0;
+
+ if (panel_is_on)
+ return 0;
+ mdss_qpic_init();
+
+ if (qpic_panel_on)
+ rc = qpic_panel_on();
+ if (rc)
+ return rc;
+ panel_is_on = true;
+ return 0;
+}
+
+int mdss_qpic_panel_off(struct mdss_panel_data *pdata)
+{
+ if (qpic_panel_off)
+ qpic_panel_off();
+ panel_is_on = false;
+ return 0;
+}
+
+static int mdss_panel_parse_dt(struct platform_device *pdev,
+ struct mdss_panel_data *panel_data)
+{
+ struct device_node *np = pdev->dev.of_node;
+ u32 res[6], tmp;
+ int rc;
+
+ rc = of_property_read_u32_array(np, "qcom,mdss-pan-res", res, 2);
+ if (rc) {
+ pr_err("%s:%d, panel resolution not specified\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ panel_data->panel_info.xres = (!rc ? res[0] : 240);
+ panel_data->panel_info.yres = (!rc ? res[1] : 320);
+ rc = of_property_read_u32(np, "qcom,mdss-pan-bpp", &tmp);
+ if (rc) {
+ pr_err("%s:%d, panel bpp not specified\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ panel_data->panel_info.bpp = (!rc ? tmp : 24);
+ of_property_read_u32(np, "qcom,refresh_rate", &panel_refresh_rate);
+
+ panel_data->panel_info.type = EBI2_PANEL;
+ panel_data->panel_info.pdest = DISPLAY_1;
+
+ if (qpic_panel_init)
+ rc = qpic_panel_init(pdev, np);
+
+ return rc;
+}
+
+static int __devinit mdss_qpic_panel_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ static struct mdss_panel_data vendor_pdata;
+ static const char *panel_name;
+
+ pr_debug("%s:%d, debug info id=%d", __func__, __LINE__, pdev->id);
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ panel_name = of_get_property(pdev->dev.of_node, "label", NULL);
+ if (!panel_name)
+ pr_info("%s:%d, panel name not specified\n",
+ __func__, __LINE__);
+ else
+ pr_info("%s: Panel Name = %s\n", __func__, panel_name);
+
+ /* select panel according to label */
+ qpic_panel_init = ili9341_init;
+ qpic_panel_on = ili9341_on;
+ qpic_panel_off = ili9341_off;
+
+ rc = mdss_panel_parse_dt(pdev, &vendor_pdata);
+ if (rc)
+ return rc;
+
+ rc = qpic_register_panel(&vendor_pdata);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static const struct of_device_id mdss_qpic_panel_match[] = {
+ {.compatible = "qcom,mdss-qpic-panel"},
+ {}
+};
+
+static struct platform_driver this_driver = {
+ .probe = mdss_qpic_panel_probe,
+ .driver = {
+ .name = "qpic_panel",
+ .of_match_table = mdss_qpic_panel_match,
+ },
+};
+
+static int __init mdss_qpic_panel_init(void)
+{
+ return platform_driver_register(&this_driver);
+}
+MODULE_DEVICE_TABLE(of, mdss_qpic_panel_match);
+module_init(mdss_qpic_panel_init);
diff --git a/drivers/video/msm/mdss/mdss_qpic_panel.h b/drivers/video/msm/mdss/mdss_qpic_panel.h
new file mode 100644
index 0000000..76f7dc2
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_qpic_panel.h
@@ -0,0 +1,114 @@
+/* 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.
+ *
+ */
+
+#ifndef MDSS_QPIC_PANEL_H
+#define MDSS_QPIC_PANEL_H
+
+#include <linux/list.h>
+#include <mach/sps.h>
+
+#include "mdss_panel.h"
+
+#define LCDC_INTERNAL_BUFFER_SIZE 30
+
+/**
+ Macros for coding MIPI commands
+*/
+#define INV_SIZE 0xFFFF
+/* Size of argument to MIPI command is variable */
+#define OP_SIZE_PAIR(op, size) ((op<<16) | size)
+/* MIPI {command, argument size} tuple */
+#define LCDC_EXTRACT_OP_SIZE(op_identifier) ((op_identifier&0xFFFF))
+/* extract size from command identifier */
+#define LCDC_EXTRACT_OP_CMD(op_identifier) (((op_identifier>>16)&0xFFFF))
+/* extract command id from command identifier */
+
+
+/* MIPI standard efinitions */
+#define LCDC_ADDRESS_MODE_ORDER_BOTTOM_TO_TOP 0x80
+#define LCDC_ADDRESS_MODE_ORDER_RIGHT_TO_LEFT 0x40
+#define LCDC_ADDRESS_MODE_ORDER_REVERSE 0x20
+#define LCDC_ADDRESS_MODE_ORDER_REFRESH_BOTTOM_TO_TOP 0x10
+#define LCDC_ADDRESS_MODE_ORDER_BGER_RGB 0x08
+#define LCDC_ADDRESS_MODE_ORDER_REFERESH_RIGHT_TO_LEFT 0x04
+#define LCDC_ADDRESS_MODE_FLIP_HORIZONTAL 0x02
+#define LCDC_ADDRESS_MODE_FLIP_VERTICAL 0x01
+
+#define LCDC_PIXEL_FORMAT_3_BITS_PER_PIXEL 0x1
+#define LCDC_PIXEL_FORMAT_8_BITS_PER_PIXEL 0x2
+#define LCDC_PIXEL_FORMAT_12_BITS_PER_PIXEL 0x3
+#define LCDC_PIXEL_FORMAT_16_BITS_PER_PIXEL 0x5
+#define LCDC_PIXEL_FORMAT_18_BITS_PER_PIXEL 0x6
+#define LCDC_PIXEL_FORMAT_24_BITS_PER_PIXEL 0x7
+
+#define LCDC_CREATE_PIXEL_FORMAT(dpi_format, dbi_format) \
+ (dpi_format | (dpi_format<<4))
+
+#define POWER_MODE_IDLE_ON 0x40
+#define POWER_MODE_PARTIAL_ON 0x20
+#define POWER_MODE_SLEEP_ON 0x10
+#define POWER_MODE_NORMAL_ON 0x08
+#define POWER_MODE_DISPLAY_ON 0x04
+
+#define LCDC_DISPLAY_MODE_SCROLLING_ON 0x80
+#define LCDC_DISPLAY_MODE_INVERSION_ON 0x20
+#define LCDC_DISPLAY_MODE_GAMMA_MASK 0x07
+
+/**
+ * LDCc MIPI Type B supported commands
+ */
+enum {
+ OP_ENTER_IDLE_MODE = OP_SIZE_PAIR(0x39, 0),
+ OP_ENTER_INVERT_MODE = OP_SIZE_PAIR(0x21, 0),
+ OP_ENTER_NORMAL_MODE = OP_SIZE_PAIR(0x13, 0),
+ OP_ENTER_PARTIAL_MODE = OP_SIZE_PAIR(0x12, 0),
+ OP_ENTER_SLEEP_MODE = OP_SIZE_PAIR(0x10, 0),
+ OP_EXIT_INVERT_MODE = OP_SIZE_PAIR(0x20, 0),
+ OP_EXIT_SLEEP_MODE = OP_SIZE_PAIR(0x11, 0),
+ OP_EXIT_IDLE_MODE = OP_SIZE_PAIR(0x38, 0),
+ OP_GET_ADDRESS_MODE = OP_SIZE_PAIR(0x0B, 1),
+ OP_GET_BLUE_CHANNEL = OP_SIZE_PAIR(0x08, 1),
+ OP_GET_DIAGNOSTIC_RESULT = OP_SIZE_PAIR(0x0F, 2),
+ OP_GET_DISPLAY_MODE = OP_SIZE_PAIR(0x0D, 1),
+ OP_GET_GREEN_CHANNEL = OP_SIZE_PAIR(0x07, 1),
+ OP_GET_PIXEL_FORMAT = OP_SIZE_PAIR(0x0C, 1),
+ OP_GET_POWER_MODE = OP_SIZE_PAIR(0x0A, 1),
+ OP_GET_RED_CHANNEL = OP_SIZE_PAIR(0x06, 1),
+ OP_GET_SCANLINE = OP_SIZE_PAIR(0x45, 2),
+ OP_GET_SIGNAL_MODE = OP_SIZE_PAIR(0x0E, 1),
+ OP_NOP = OP_SIZE_PAIR(0x00, 0),
+ OP_READ_DDB_CONTINUE = OP_SIZE_PAIR(0xA8, INV_SIZE),
+ OP_READ_DDB_START = OP_SIZE_PAIR(0xA1, INV_SIZE),
+ OP_READ_MEMORY_CONTINUE = OP_SIZE_PAIR(0x3E, INV_SIZE),
+ OP_READ_MEMORY_START = OP_SIZE_PAIR(0x2E, INV_SIZE),
+ OP_SET_ADDRESS_MODE = OP_SIZE_PAIR(0x36, 1),
+ OP_SET_COLUMN_ADDRESS = OP_SIZE_PAIR(0x2A, 4),
+ OP_SET_DISPLAY_OFF = OP_SIZE_PAIR(0x28, 0),
+ OP_SET_DISPLAY_ON = OP_SIZE_PAIR(0x29, 0),
+ OP_SET_GAMMA_CURVE = OP_SIZE_PAIR(0x26, 1),
+ OP_SET_PAGE_ADDRESS = OP_SIZE_PAIR(0x2B, 4),
+ OP_SET_PARTIAL_COLUMNS = OP_SIZE_PAIR(0x31, 4),
+ OP_SET_PARTIAL_ROWS = OP_SIZE_PAIR(0x30, 4),
+ OP_SET_PIXEL_FORMAT = OP_SIZE_PAIR(0x3A, 1),
+ OP_SOFT_RESET = OP_SIZE_PAIR(0x01, 0),
+ OP_WRITE_MEMORY_CONTINUE = OP_SIZE_PAIR(0x3C, INV_SIZE),
+ OP_WRITE_MEMORY_START = OP_SIZE_PAIR(0x2C, INV_SIZE),
+};
+
+u32 qpic_panel_set_cmd_only(u32 command);
+u32 qpic_send_panel_cmd(u32 cmd, u32 *val, u32 length);
+int ili9341_on(void);
+void ili9341_off(void);
+int ili9341_init(struct platform_device *pdev,
+ struct device_node *np);
+#endif /* MDSS_QPIC_PANEL_H */
diff --git a/drivers/video/msm/mdss/qpic_panel_ili_qvga.c b/drivers/video/msm/mdss/qpic_panel_ili_qvga.c
new file mode 100644
index 0000000..4459c6b
--- /dev/null
+++ b/drivers/video/msm/mdss/qpic_panel_ili_qvga.c
@@ -0,0 +1,226 @@
+/* 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.
+ *
+ */
+
+#include <linux/memory.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+
+#include "mdss_qpic.h"
+#include "mdss_qpic_panel.h"
+
+enum {
+ OP_ILI9341_TEARING_EFFECT_LINE_ON = OP_SIZE_PAIR(0x35, 1),
+ OP_ILI9341_INTERFACE_CONTROL = OP_SIZE_PAIR(0xf6, 3),
+ OP_ILI9341_WRITE_CTRL_DISPLAY = OP_SIZE_PAIR(0x53, 1),
+ OP_ILI9341_POWER_CONTROL_A = OP_SIZE_PAIR(0xcb, 5),
+ OP_ILI9341_POWER_CONTROL_B = OP_SIZE_PAIR(0xcf, 3),
+ OP_ILI9341_DRIVER_TIMING_CONTROL_A = OP_SIZE_PAIR(0xe8, 3),
+ OP_ILI9341_DRIVER_TIMING_CONTROL_B = OP_SIZE_PAIR(0xea, 3),
+ OP_ILI9341_POWER_ON_SEQUENCE_CONTROL = OP_SIZE_PAIR(0xed, 4),
+ OP_ILI9341_PUMP_RATIO_CONTROL = OP_SIZE_PAIR(0xf7, 1),
+ OP_ILI9341_POWER_CONTROL_1 = OP_SIZE_PAIR(0xc0, 1),
+ OP_ILI9341_POWER_CONTROL_2 = OP_SIZE_PAIR(0xc1, 1),
+ OP_ILI9341_VCOM_CONTROL_1 = OP_SIZE_PAIR(0xc5, 2),
+ OP_ILI9341_VCOM_CONTROL_2 = OP_SIZE_PAIR(0xc7, 1),
+ OP_ILI9341_MEMORY_ACCESS_CONTROL = OP_SIZE_PAIR(0x36, 1),
+ OP_ILI9341_FRAME_RATE_CONTROL = OP_SIZE_PAIR(0xb1, 2),
+ OP_ILI9341_DISPLAY_FUNCTION_CONTROL = OP_SIZE_PAIR(0xb6, 4),
+ OP_ILI9341_ENABLE_3G = OP_SIZE_PAIR(0xf2, 1),
+ OP_ILI9341_COLMOD_PIXEL_FORMAT_SET = OP_SIZE_PAIR(0x3a, 1),
+ OP_ILI9341_GAMMA_SET = OP_SIZE_PAIR(0x26, 1),
+ OP_ILI9341_POSITIVE_GAMMA_CORRECTION = OP_SIZE_PAIR(0xe0, 15),
+ OP_ILI9341_NEGATIVE_GAMMA_CORRECTION = OP_SIZE_PAIR(0xe1, 15),
+ OP_ILI9341_READ_DISPLAY_ID = OP_SIZE_PAIR(0x04, 4),
+ OP_ILI9341_READ_DISPLAY_POWER_MODE = OP_SIZE_PAIR(0x0a, 2),
+ OP_ILI9341_READ_DISPLAY_MADCTL = OP_SIZE_PAIR(0x0b, 2),
+};
+
+static int rst_gpio;
+static int cs_gpio;
+static int ad8_gpio;
+static int te_gpio;
+struct regulator *vdd_vreg;
+struct regulator *avdd_vreg;
+
+int ili9341_init(struct platform_device *pdev,
+ struct device_node *np)
+{
+ int rc = 0;
+ rst_gpio = of_get_named_gpio(np, "qcom,rst-gpio", 0);
+ cs_gpio = of_get_named_gpio(np, "qcom,cs-gpio", 0);
+ ad8_gpio = of_get_named_gpio(np, "qcom,ad8-gpio", 0);
+ te_gpio = of_get_named_gpio(np, "qcom,te-gpio", 0);
+ if (!gpio_is_valid(rst_gpio)) {
+ pr_err("%s: reset gpio not specified\n" , __func__);
+ return -EINVAL;
+ }
+ if (!gpio_is_valid(cs_gpio)) {
+ pr_err("%s: cs gpio not specified\n", __func__);
+ return -EINVAL;
+ }
+ if (!gpio_is_valid(ad8_gpio)) {
+ pr_err("%s: ad8 gpio not specified\n", __func__);
+ return -EINVAL;
+ }
+ if (!gpio_is_valid(te_gpio)) {
+ pr_err("%s: te gpio not specified\n", __func__);
+ return -EINVAL;
+ }
+ vdd_vreg = devm_regulator_get(&pdev->dev, "vdd");
+ if (IS_ERR(vdd_vreg)) {
+ pr_err("%s could not get vdd,", __func__);
+ return -ENODEV;
+ }
+ avdd_vreg = devm_regulator_get(&pdev->dev, "avdd");
+ if (IS_ERR(avdd_vreg)) {
+ pr_err("%s could not get avdd,", __func__);
+ return -ENODEV;
+ }
+ rc = regulator_set_voltage(vdd_vreg, 1800000, 1800000);
+ if (rc) {
+ pr_err("vdd_vreg->set_voltage failed, rc=%d\n", rc);
+ return -EINVAL;
+ }
+ rc = regulator_set_voltage(avdd_vreg, 2700000, 2700000);
+ if (rc) {
+ pr_err("vdd_vreg->set_voltage failed, rc=%d\n", rc);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ili9341_panel_power_on(void)
+{
+ int rc;
+ rc = regulator_enable(vdd_vreg);
+ if (rc) {
+ pr_err("enable vdd failed, rc=%d\n", rc);
+ return -ENODEV;
+ }
+ rc = regulator_enable(avdd_vreg);
+ if (rc) {
+ pr_err("enable avdd failed, rc=%d\n", rc);
+ return -ENODEV;
+ }
+
+ if (gpio_request(rst_gpio, "disp_rst_n")) {
+ pr_err("%s request reset gpio failed\n", __func__);
+ return -EINVAL;
+ }
+ if (gpio_request(cs_gpio, "disp_cs_n")) {
+ gpio_free(rst_gpio);
+ pr_err("%s request cs gpio failed\n", __func__);
+ return -EINVAL;
+ }
+
+ if (gpio_request(ad8_gpio, "disp_ad8_n")) {
+ gpio_free(cs_gpio);
+ gpio_free(rst_gpio);
+ pr_err("%s request ad8 gpio failed\n", __func__);
+ return -EINVAL;
+ }
+ if (gpio_request(te_gpio, "disp_te_n")) {
+ gpio_free(ad8_gpio);
+ gpio_free(cs_gpio);
+ gpio_free(rst_gpio);
+ pr_err("%s request te gpio failed\n", __func__);
+ return -EINVAL;
+ }
+ /* wait for 20 ms after enable gpio as suggested by hw */
+ msleep(20);
+ return 0;
+}
+
+static void ili9341_panel_power_off(void)
+{
+ gpio_free(ad8_gpio);
+ gpio_free(cs_gpio);
+ gpio_free(rst_gpio);
+ gpio_free(te_gpio);
+ regulator_disable(vdd_vreg);
+ regulator_disable(avdd_vreg);
+}
+
+int ili9341_on(void)
+{
+ u32 param[20];
+ int ret;
+ ret = ili9341_panel_power_on();
+ if (ret)
+ return ret;
+ qpic_panel_set_cmd_only(OP_SOFT_RESET);
+ /* wait for 120 ms after reset as panel spec suggests */
+ msleep(120);
+ qpic_panel_set_cmd_only(OP_SET_DISPLAY_OFF);
+ /* wait for 20 ms after disply off */
+ msleep(20);
+
+ /* set memory access control */
+ param[0] = ((0x48)<<0) | ((0x00)<<8) | ((0x00)<<16) | ((0x00U)<<24U);
+ qpic_send_panel_cmd(OP_ILI9341_MEMORY_ACCESS_CONTROL, param, 0);
+ /* wait for 20 ms after command sent as panel spec suggests */
+ msleep(20);
+
+ /* set COLMOD: Pixel Format Set */
+ param[0] = ((0x66)<<0) | ((0x00)<<8) | ((0x00)<<16) | ((0x00U)<<24U);
+ qpic_send_panel_cmd(OP_ILI9341_COLMOD_PIXEL_FORMAT_SET, param, 0);
+ /* wait for 20 ms after command sent as panel spec suggests */
+ msleep(20);
+
+ /* set interface */
+ param[0] = ((0x01)<<0) | ((0x00)<<8) | ((0x00)<<16) | ((0x00U)<<24U);
+ qpic_send_panel_cmd(OP_ILI9341_INTERFACE_CONTROL, ¶m[0], 0);
+ /* wait for 20 ms after command sent */
+ msleep(20);
+
+ /* exit sleep mode */
+ qpic_panel_set_cmd_only(OP_EXIT_SLEEP_MODE);
+ /* wait for 20 ms after command sent as panel spec suggests */
+ msleep(20);
+
+ /* normal mode */
+ qpic_panel_set_cmd_only(OP_ENTER_NORMAL_MODE);
+ /* wait for 20 ms after command sent as panel spec suggests */
+ msleep(20);
+
+ /* display on */
+ qpic_panel_set_cmd_only(OP_SET_DISPLAY_ON);
+ /* wait for 20 ms after command sent as panel spec suggests */
+ msleep(20);
+
+ /* tearing effect */
+ param[0] = ((0x00)<<0) | ((0x00)<<8) | ((0x00)<<16) | ((0x00U)<<24U);
+ qpic_send_panel_cmd(OP_ILI9341_TEARING_EFFECT_LINE_ON, param, 0);
+ /* wait for 20 ms after command sent as panel spec suggests */
+ msleep(20);
+
+ return 0;
+}
+
+void ili9341_off(void)
+{
+ ili9341_panel_power_off();
+}
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);